/*
* 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1