/* * Copyright (c) 1999, 2000 Motoyuki Kasahara. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "pccard_low.h" /* * Socket file name that pccardd binds. */ #define SERVER_SOCKET_NAME "/var/tmp/.pccardd" /* * File name of my socket. */ #define CLIENT_SOCKET_NAME "/tmp/.pccard" /* * Lock file name for my socket. */ #define LOCK_FILE_NAME "/tmp/.pccard.lock" /* * Unexported function. */ static void pccard_parse_response(const char *, int *, char *, char *, char *, int *); /* * File descriptor of my socket. */ static int socket_file = -1; /* * File descriptor of my socket. */ static int lock_file = -1; /* * Address of my socket. */ static struct sockaddr_un client_address; /* * Address of pccardd's socket. */ static struct sockaddr_un server_address; /* * Message handlers. */ extern void (*pccard_output_error_message)(const char *, ...); extern void (*pccard_output_warning_message)(const char *, ...); extern void (*pccard_output_debug_message)(const char *, ...); /* * Debug flag. */ extern int pccard_debug_flag; /* * Open a socket for talking to pccardd. * If an error occurs, -1 is returned. Otherwise 0 is returned. */ int pccard_open_socket(void) { struct flock lock; struct stat st; if (socket_file >= 0) pccard_close_socket(); /* * Open the lock file. */ lock_file = open(LOCK_FILE_NAME, O_RDWR | O_CREAT, 0600); if (lock_file == -1) { pccard_output_error_message("open() failed, %s: %s", strerror(errno), LOCK_FILE_NAME); pccard_close_socket(); return -1; } if (pccard_debug_flag) pccard_output_debug_message("open the lock file: %s", LOCK_FILE_NAME); /* * Lock the file. */ lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 1; lock.l_pid = 0; if (fcntl(lock_file, F_SETLK, &lock) == -1) { pccard_output_error_message("fnctl(F_SETLK) failed, %s: %s", strerror(errno), LOCK_FILE_NAME); pccard_output_error_message("another client might be running"); pccard_close_socket(); return -1; } if (pccard_debug_flag) pccard_output_debug_message("lock the file: %s", LOCK_FILE_NAME); /* * Remove the old socket, if exists. */ if (stat(CLIENT_SOCKET_NAME, &st) == 0) { if (unlink(CLIENT_SOCKET_NAME) == -1) { pccard_output_error_message("unlink() failed, %s: %s", strerror(errno), CLIENT_SOCKET_NAME); pccard_close_socket(); return -1; } if (pccard_debug_flag) { pccard_output_debug_message("remove the old socket file: %s", CLIENT_SOCKET_NAME); } } /* * Create an UNIX domain socket for me (i.e. client). */ socket_file = socket(AF_UNIX, SOCK_DGRAM, 0); if (socket_file == -1) { pccard_output_error_message("socket() failed, %s", strerror(errno)); pccard_close_socket(); return -1; } /* * Set address for the socket. */ memset(&client_address, 0, sizeof(client_address)); client_address.sun_family = AF_UNIX; strcpy(client_address.sun_path, CLIENT_SOCKET_NAME); /* * Bind the address to the socket. */ if (bind(socket_file, (struct sockaddr *)&client_address, SUN_LEN(&client_address)) == -1) { pccard_output_error_message("bind() failed, %s: %s", strerror(errno), client_address.sun_path); pccard_close_socket(); return -1; } /* * Set address of the pccardd's socket. */ memset(&server_address, 0, sizeof(server_address)); server_address.sun_family = AF_UNIX; strcpy(server_address.sun_path, SERVER_SOCKET_NAME); if (pccard_debug_flag) { pccard_output_debug_message("open the socket file: %s", CLIENT_SOCKET_NAME); } return 0; } /* * Close the lock file and the socket used to talk to pccardd, and remove * them. We ignore an error at removal of the socket file. */ void pccard_close_socket(void) { struct flock lock; int is_locked; pid_t pid; /* * Close the lock file. * Also remove the file if the process locks the file. */ if (lock_file != -1) { pid = getpid(); lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 1; lock.l_pid = pid; if (fcntl(lock_file, F_GETLK, &lock) == 0 && lock.l_pid == pid) is_locked = 1; else is_locked = 0; close(lock_file); if (pccard_debug_flag) { pccard_output_debug_message("close the lock file: %s", LOCK_FILE_NAME); } if (is_locked) { if (unlink(LOCK_FILE_NAME) == -1) { pccard_output_warning_message("unlink() failed, %s: %s", strerror(errno), LOCK_FILE_NAME); } else if (pccard_debug_flag) { pccard_output_debug_message("remove the lock file: %s", LOCK_FILE_NAME); } } lock_file = -1; } /* * Close and remove the socket file. */ if (socket_file != -1) { close(socket_file); if (pccard_debug_flag) { pccard_output_debug_message("close the socket file: %s", CLIENT_SOCKET_NAME); } if (unlink(CLIENT_SOCKET_NAME) == -1) { pccard_output_warning_message("unlink() failed, %s: %s", strerror(errno), CLIENT_SOCKET_NAME); } else if (pccard_debug_flag) { pccard_output_debug_message("remove the socket file: %s", CLIENT_SOCKET_NAME); } socket_file = -1; } } /* * Send a request command to pccardd. * If an error occurs, -1 is returned. Otherwise 0 is returned. */ int pccard_send_request(const char *command) { size_t command_length; int saved_errno; if (socket_file == -1) { pccard_output_error_message("socket file is not opened"); return -1; } /* * Send a request. */ command_length = strlen(command); errno = 0; if (sendto(socket_file, command, command_length, 0, (struct sockaddr *)&server_address, SUN_LEN(&server_address)) != command_length) { saved_errno = errno; pccard_output_error_message("sendto() failed, %s: %s", strerror(errno), SERVER_SOCKET_NAME); errno = saved_errno; /* * If no such file exist, or connection is refused, we guess * that pccardd is not running. */ if (errno == ENOENT || errno == ECONNREFUSED) pccard_output_error_message("pccardd doesn't seem to be running"); return -1; } if (pccard_debug_flag) pccard_output_debug_message("send the sring to pccardd: %s", command); return 0; } /* * Wait until the socket is ready for reading. * If an error occurs, -1 is returned. Otherwise 0 is returned. */ int pccard_wait_response(void) { fd_set fdset; if (socket_file == -1) { pccard_output_error_message("socket file is not opened"); return -1; } for (;;) { FD_ZERO(&fdset); FD_SET(socket_file, &fdset); if (select(socket_file + 1, &fdset, NULL, NULL, NULL) != -1) break; else if (errno != EINTR) { pccard_output_error_message("select() failed, %s", strerror(errno)); return -1; } } return 0; } /* * Receive a response from pccardd. * If an error occurs, -1 is returned. * Otherwise the response message is set to `buffer' and 0 is returned. */ int pccard_receive_response(char *buffer) { int from_length; ssize_t receive_length; struct sockaddr_un from_address; if (socket_file == -1) { pccard_output_error_message("socket file is not opened"); return -1; } receive_length = recvfrom(socket_file, buffer, PCCARD_MAX_RESPONSE_LENGTH, 0, (struct sockaddr *)&from_address, &from_length); if (receive_length == -1) { pccard_output_error_message("recvfrom() failed, %s", strerror(errno)); return -1; } *(buffer + receive_length) = '\0'; if (pccard_debug_flag) { pccard_output_debug_message("receive the string from pccardd: %s", buffer); } return 0; } /* * Query how many slots the system has. * The number of slots is returned, if the query is succeeded. * Otherwise -1 is returned. */ int pccard_query_number_of_slots(void) { char buffer[PCCARD_MAX_RESPONSE_LENGTH + 1]; int slots; if (pccard_send_request("S") == -1) return -1; if (pccard_wait_response() == -1) return -1; if (pccard_receive_response(buffer) == -1) return -1; slots = atoi(buffer); if (pccard_debug_flag) pccard_output_debug_message("the number of slots: nslots=%d", slots); return slots; } /* * Query information of the slot `slot'. * If the query is succeeded, `manufacturer', `version', `driver' and * `status' of the slot are set by this function, and 0 is returned. * Otherwise -1 is returned. */ int pccard_query_slot_information(int slot, char *manufacturer, char *version, char *driver, int *status) { char request_buffer[PCCARD_MAX_REQUEST_LENGTH + 1]; char response_buffer[PCCARD_MAX_RESPONSE_LENGTH + 1]; int response_slot; /* * Send a request and receive a response. */ sprintf(request_buffer, "N%d", slot); if (pccard_send_request(request_buffer) == -1) return -1; if (pccard_wait_response() == -1) return -1; if (pccard_receive_response(response_buffer) == -1) return -1; /* * Parse the response. * (`response_slot' is dummy argument). */ pccard_parse_response(response_buffer, &response_slot, manufacturer, version, driver, status); if (pccard_debug_flag) { pccard_output_debug_message("slot status: " "slot=%d, manufacturer=%s, version=%s, driver=%s, status=%d", slot, manufacturer, version, driver, *status); } return 0; } /* * Send insertion request of the slot `slot'. * If the request is succeeded, 0 is returned. * Otherwise -1 is returned. */ int pccard_query_insertion(int slot) { char request_buffer[PCCARD_MAX_REQUEST_LENGTH + 1]; char response_buffer[PCCARD_MAX_RESPONSE_LENGTH + 1]; sprintf(request_buffer, "P%d", slot); if (pccard_send_request(request_buffer) == -1) return -1; if (pccard_wait_response() == -1) return -1; if (pccard_receive_response(response_buffer) == -1) return -1; if (pccard_debug_flag) pccard_output_debug_message("activate slot: slot=%d", slot); return 0; } /* * Send removal request of the slot `slot'. * If the request is succeeded, 0 is returned. * Otherwise -1 is returned. */ int pccard_query_removal(int slot) { char request_buffer[PCCARD_MAX_REQUEST_LENGTH + 1]; char response_buffer[PCCARD_MAX_RESPONSE_LENGTH + 1]; sprintf(request_buffer, "Q%d", slot); if (pccard_send_request(request_buffer) == -1) return -1; if (pccard_wait_response() == -1) return -1; if (pccard_receive_response(response_buffer) == -1) return -1; if (pccard_debug_flag) pccard_output_debug_message("inactivate slot: slot=%d", slot); return 0; } /* * Receive information that pccardd tells me that status of a slot has * been changed. * Upon return, `slot', `manufacturer', `version', `driver' and `status' * of the slot are set by this function. */ int pccard_status_changed(int *slot, char *manufacturer, char *version, char *driver, int *status) { char response_buffer[PCCARD_MAX_RESPONSE_LENGTH + 1]; /* * Receive information. */ if (pccard_receive_response(response_buffer) == -1) return -1; pccard_parse_response(response_buffer, slot, manufacturer, version, driver, status); if (pccard_debug_flag) { pccard_output_debug_message("status changed: " "slot=%d, manufacturer=%s, version=%s, driver=%s, status=%d", *slot, manufacturer, version, driver, *status); } return 0; } /* * Parse `response' sent from pccardd. * Get a slot number, manufacturer, version, driver and status * in the repsonse, put them into the arguments with the corresponding * names. */ static void pccard_parse_response(const char *response, int *slot, char *manufacturer, char *version, char *driver, int *status) { const char *token_start; const char *token_end; const char *p; /* * Initialize parameters. */ *manufacturer = '\0'; *version = '\0'; *driver = '\0'; *status = PCCARD_STATUS_UNKNOWN; /* * Get a slot number in the response. */ p = response; *slot = atoi(p); while (*p != '~' && *p != '\0') p++; if (*p == '\0') return; p++; /* * Get a manufacturer in the response. */ while (*p == ' ' || *p == '\t') p++; token_start = p; token_end = p; while (*p != '~' && *p != '\0') { if (*p != ' ' && *p != '\t') token_end = p + 1; p++; } memcpy(manufacturer, token_start, (token_end - token_start)); *(manufacturer + (token_end - token_start)) = '\0'; if (*p == '\0') return; p++; /* * Get a version in the response. */ while (*p == ' ' || *p == '\t') p++; token_start = p; token_end = p; while (*p != '~' && *p != '\0') { if (*p != ' ' && *p != '\t') token_end = p + 1; p++; } memcpy(version, token_start, (token_end - token_start)); *(version + (token_end - token_start)) = '\0'; if (*p == '\0') return; p++; /* * Get a driver in the response. */ while (*p == ' ' || *p == '\t') p++; token_start = p; token_end = p; while (*p != '~' && *p != '\0') { if (*p != ' ' && *p != '\t') token_end = p + 1; p++; } memcpy(driver, token_start, (token_end - token_start)); *(driver + (token_end - token_start)) = '\0'; if (*p == '\0') return; p++; /* * Get status. */ *status = atoi(p); } /* * Return the file descriptor of my socket. * * Note: This function is useful when an application program handles * an event by itself, especially handles I/O event of some files, * or gets both X and file events (like XtAppAddInput()). * But, don't read, write, open, nor close the socket file in the * application. */ int pccard_socket_file(void) { return socket_file; }