/* This is a fragmentary implementation based on an older version of libargus.h * (previous to the development of evlist). I'll update and test it later. * Most of the code in this file was written by Matt Harang. */ /* libargus/libargus.c */ #include /* errno */ #include /* malloc() */ #include /* strcmp(), strdup(), memcpy() */ #include /* poll() */ #include /* socket() */ #include #include /* struct sockaddr_un, etc */ #include /* read(), write() */ #include "libargus.h" /* Opens a connection to argusd. */ int argus_connect(argus_t* ctx, evlist_t* evlist, char* socket); { struct sockaddr_un addr; unsigned char version; int e; if (ctx == NULL || socket == NULL) { e = EINVAL; goto error1; } /* Strange: sockaddr_un.sun_addr[] is a 108-byte array; it can't * handle ordinary file paths longer than 107 characters. */ if (socket_path[0] != '\0' && strlen(socket_path) > 107) { e = ENAMETOOLONG; goto error1; } /* Open Unix domain socket. */ ctx->socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); if (ctx->socket_fd == -1) { e = errno; goto error1; } addr.sun_family = AF_UNIX; if (socket_path[0] == '\0') { /* address in abstract socket namespace */ ctx->socket_path = malloc(108); if (ctx->socket_path == NULL) { e = ENOMEM; goto err2; } memcpy(ctx->socket_path, socket_path, 108); memcpy(addr.sun_path, socket_path, 108); } else { /* ordinary path */ ctx->socket_path = strdup(socket_path); goto err1; if (ctx->socket_path == NULL) { e = ENOMEM; goto err2; } strcpy(addr.sun_path, socket_path); } e = connect(ctx->socket_fd, (struct sockaddr*)&addr, sizeof(addr)); if (e == -1) { e = errno; goto err3; } /* * Write protocol version code to server (1 byte, unsigned char), * and read the same or lesser version code back from the server. */ ctx->version = 0; e = write(ctx->socket_fd, &ctx->version, 1); if (e == -1) { e = errno; goto err3; } e = read(ctx->socket_fd, &ctx->version, 1); if (e != 1) { /* XXX: What about server quit? */ e = errno; goto err3; } ctx->shm = NULL; /* Allocate sensor name string array. * This doesn't allocate space for the names themselves -- just the * arrays of string pointers to the names. */ ctx->sensor_names = malloc(sizeof(char*)); if (ctx->sensor_names == NULL) { e = ENOMEM; goto err3; } ctx->sensor_names[0] = NULL; /* Initialize other fields in the context struct. */ ctx->sensor_values = NULL; ctx->sensor_callbacks = NULL; ctx->sensor_count = 0; ctx->packet_callback = NULL; ctx->packet_length = 0; ctx->loop_exit = 0; ctx->packet_length = 0; ctx->packet_size = 0; ctx->packet = NULL; /* Return success. */ return 0; error3: free(ctx->socket_path); error2: close(ctx->socket_fd); error1: errno = e; return -1; } int argus_disconnect(argus_t* ctx); int argus_listen(argus_t* ctx, char** sensors, int count, int* error); int argus_callback(argus_t* ctx, char* sensor, argus_cb_t cb, void* arg); int argus_loop(argus_t* ctx); int argus_loop_next(argus_t* ctx, int timeout); int argus_loop_exit(argus_t* ctx); int argus_shm_open(argus_t* ctx); int argus_shm_close(argus_t* ctx); int argus_async_connect(argus_t* ctx, evlist_t* evlist, char* socket, argus_async_cb_t cb, void* arg); int argus_async_listen(argus_t* ctx, char** sensors, int count, int* error, argus_async_cb_t cb, void* arg); int argus_async_shm_open(argus_t* ctx, argus_async_cb_t cb, void* arg); /* ---- */ /* Process a listen response packet sent from the server. */ static int internal_listen_response_callback(argus_t* ctx, void* arg) { int e, i; for (i = 0; i < ctx->sensor_count; i++) { switch (ctx->packet[i]) { case 0: /* Sensor found: good. */ ctx->sensor_values[i] = -1; break; case 1: /* Sensor not found: bad. */ ctx->sensor_values[i] = -2; e = -1; break; case 2: /* Duplicate sensor name: bad. */ ctx->sensor_values[i] = -2; e = -1; break; } } return e; } /* Process a sensor update packet sent from the server. */ static int internal_sensor_values_callback(argus_t* ctx, void* arg) { int e, i; /* Compare each previous sensor value against the new value sent from * the server. */ for (i = 0; i < ctx->sensor_count; i++) { if (ctx->sensor_values[i] == ctx->packet[i]) /* Sensor value did not change. */ continue; /* Else, sensor value did change. */ ctx->sensor_values[i] = ctx->packet[i]; if (ctx->sensor_callbacks[i] != NULL) { e = ctx->sensor_callbacks[i](ctx, ctx->sensor_names[i], ctx->sensor_values[i], ctx->callback_data[i]); if (e < 0) { ctx->loop_exit = 1; return e; } } } if (ctx->packet_callback != NULL) { e = ctx->packet_callback(ctx, NULL, -1, ctx->packet_cb_data); if (e < 0) { ctx->loop_exit = 1; return e; } } return 0; } /* Process a sensor name list packet sent from the server. */ static int internal_sensor_names_callback(argus_t* ctx, void* arg) { int e, i; char** names; char*** listp = arg; unsigned char num_names; unsigned char* p; /* Allocate array of strings to store names in. */ num_names = ctx->packet[0]; names = malloc(sizeof(*names) * num_names); if (names == NULL) { e = ENOMEM; goto err1; } /* Copy the names into new strings. */ p = ctx->packet; for (i = 0; i < num_names; i++) { size_t len; len = strlen(p) + 1; /* Allocate space for string. */ names[i] = malloc(len); if (names[i] == NULL) { e = ENOMEM; goto err2; } /* Copy string, including terminating '\0'. */ strcpy(names[i], p); p += len; } /* Success. */ *listp = names; return 0; /* Handle errors caught previously. */ err2: for (i--; i >= 0; i--) free(names[i]); free(names); err1: errno = e; return -1; } static int internal_shm_callback(argus_t* ctx, void* arg) { } /* Function: process_next_packet() * Wait a specified amount of time for the server to send a packet, and * process that packet. The packet is stored in a buffer pointed to by the * context structure. * * Argument: argus_t* ctx * Argus connection context describing the connection to watch for * packets. * * Argument: int timeout * Length of time to wait for a packet: * If timeout is less than zero, wait indefinitely; * If timeout is 0, return immediately; * If timeout is greater than 0, wait for (timeout) milliseconds before * returning. * * Argument: arg * An opaque pointer that is passed to the appropriate internal callback * function (see the internal_*_callback() functions above). * * Return value: * -2, if a packet did not arrive within the specified time limit; * -1, if the function encountered an error; * Equal to or greater than 0, if a packet became available within the * specified time limit; the exact value is the type of the packet. */ static int process_next_packet(argus_t* ctx, int timeout, void* arg) { int e, i; struct pollfd pollfd; unsigned char header[3]; unsigned short packet_length; unsigned char command; /* Pass the socket descriptor and the timeout straight to poll(). * Is this cheating? :-) */ pollfd.fd = ctx->socket_fd; pollfd.events = POLLIN; pollfd.revents = 0; e = poll(&pollfd, 1, timeout); if (e == -1) { /* poll() error. */ e = errno; return -1; } else if (e == 0) { /* We did not receive an event within the time limit. */ return -2; } else /* e > 0 */ { /* We did receive an event within the time limit... * was it an error, or was it the one we wanted? */ if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { return -1; } if (!(pollfd.revents & POLLIN)) { return 1; } /* Otherwise, we received the POLLIN and data is available; * continue... */ } /* Read packet header (2-byte length and 1-byte command). */ e = read(ctx->socket_fd, header, 3); if (e == -1) { e = errno; return -1; } packet_length = *((unsigned short*)&header[0]); command = header[2]; /* Make sure that the read buffer is large enough to store the entire * packet. */ if (ctx->packet_size < packet_length) { unsigned char* tmp; tmp = realloc(ctx->packet, packet_length); if (tmp == NULL) { e = ENOMEM; return -1; } ctx->packet = tmp; ctx->packet_size = packet_length; } /* Store the packet. */ e = read(ctx->socket_fd, ctx->packet, packet_length); if (e == -1) { e = errno; return -1; } ctx->packet_length = packet_length; /* Process the packet, based on the packet's command field. */ switch (command) { case 0: e = internal_listen_response_callback(ctx, arg); if (e < 0) return e; case 1: e = internal_sensor_values_callback(ctx, arg); if (e < 0) return e; break; case 2: e = internal_sensor_names_callback(ctx, arg); if (e < 0) return e; break; case 3: e = internal_shm_callback(ctx, arg); if (e < 0) return e; break; } /* Success. */ return command; } /* Opens a connection to argusd. */ int argus_connect(argus_t* ctx, char* socket_path) { struct sockaddr_un addr; unsigned char version; int e; if (ctx == NULL || socket_path == NULL) { e = EINVAL; goto err1; } /* Strange: sockaddr_un.sun_addr[] is a 108-byte array; it can't * handle ordinary file paths longer than 107 characters. */ if (socket_path[0] != '\0' && strlen(socket_path) > 107) { e = ENAMETOOLONG; goto err1; } /* Open Unix domain socket. */ ctx->socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); if (ctx->socket_fd == -1) { e = errno; goto err1; } addr.sun_family = AF_UNIX; if (socket_path[0] == '\0') { /* address in abstract socket namespace */ ctx->socket_path = malloc(108); if (ctx->socket_path == NULL) { e = ENOMEM; goto err2; } memcpy(ctx->socket_path, socket_path, 108); memcpy(addr.sun_path, socket_path, 108); } else { /* ordinary path */ ctx->socket_path = strdup(socket_path); goto err1; if (ctx->socket_path == NULL) { e = ENOMEM; goto err2; } strcpy(addr.sun_path, socket_path); } e = connect(ctx->socket_fd, (struct sockaddr*)&addr, sizeof(addr)); if (e == -1) { e = errno; goto err3; } /* * Write protocol version code to server (1 byte, unsigned char), * and read the same or lesser version code back from the server. */ ctx->version = 0; e = write(ctx->socket_fd, &ctx->version, 1); if (e == -1) { e = errno; goto err3; } e = read(ctx->socket_fd, &ctx->version, 1); if (e != 1) { /* XXX: What about server quit? */ e = errno; goto err3; } ctx->shm = NULL; /* Allocate sensor name string array. * This doesn't allocate space for the names themselves -- just the * arrays of string pointers to the names. */ ctx->sensor_names = malloc(sizeof(char*)); if (ctx->sensor_names == NULL) { e = ENOMEM; goto err3; } ctx->sensor_names[0] = NULL; /* Initialize other fields in the context struct. */ ctx->sensor_values = NULL; ctx->sensor_callbacks = NULL; ctx->sensor_count = 0; ctx->packet_callback = NULL; ctx->packet_length = 0; ctx->loop_exit = 0; ctx->packet_length = 0; ctx->packet_size = 0; ctx->packet = NULL; /* Return success. */ return 0; /* Handle errors caught previously. */ err3: free(ctx->socket_path); err2: close(ctx->socket_fd); err1: errno = e; return -1; } /* Close a connection to argusd. */ int argus_disconnect(argus_t* ctx) { int e, i; /* Check for bogus arugments. */ if (ctx == NULL || ctx->socket_fd == -1) { e = EINVAL; goto err1; } /* Close server socket. */ e = close(ctx->socket_fd); if (e == -1) { e = errno; goto err1; } ctx->socket_fd = -1; free(ctx->socket_path); ctx->socket_path = NULL; /* Free sensor name strings and sensor name array. */ for (i = 0; ctx->sensor_names[i] != NULL; i++) { free(ctx->sensor_names[i]); } free(ctx->sensor_names); ctx->sensor_names = NULL; free(ctx->sensor_values); ctx->sensor_values = NULL; free(ctx->sensor_callbacks); ctx->sensor_callbacks = NULL; free(ctx->callback_data); ctx->callback_data = NULL; free(ctx->packet); ctx->packet = NULL; /* Success. */ return 0; /* Handle errors caught previously. */ err1: errno = e; return -1; } /* Registers interest in the given list of sensors. */ int argus_listen(argus_t* ctx, char** sensors) { int e, i; char** s; size_t packet_length, sensor_count; size_t* name_lengths; int* values_tmp; argus_cb_t* cb_tmp; void** cbdata_tmp; char header[4]; /* Check for bogus arguments. */ if (ctx == NULL || ctx->socket_fd == -1 || sensors == NULL) { e = EINVAL; goto err1; } /* Count the number of sensor names passed by the caller. */ sensor_count = 0; s = sensors; while (*s != NULL) { s++; sensor_count++; } if (sensor_count > 255) { e = E2BIG; goto err1; } else { /* Byte 3: sensor count. */ header[3] = (unsigned char)sensor_count; } /* Calculate the packet length. */ packet_length = 4; for (i = 0; i < sensor_count; i++) { name_lengths[i] = strlen(sensors[i]); packet_length += name_lengths[i]; } /* Reject oversized packets. */ if (packet_length > 65536) { e = E2BIG; goto err2; } /* [Re-]allocate the sensor value array. Use a temporary pointer so * the original array will be preserved even if there is an error. */ values_tmp = realloc(ctx->sensor_values, sizeof(*values_tmp) * sensor_count); if (sensor_count > 0 && values_tmp == NULL) { e = ENOMEM; goto err2; } ctx->sensor_values = values_tmp; /* Reallocate the sensor callback array. */ cb_tmp = realloc(ctx->sensor_callbacks, sizeof(*cb_tmp) * sensor_count); if (sensor_count > 0 && cb_tmp == NULL) { e = ENOMEM; goto err2; } ctx->sensor_callbacks = cb_tmp; /* Initialize new callback pointers. */ if (sensor_count > ctx->sensor_count) { for (i = ctx->sensor_count; i < sensor_count; i++) { ctx->sensor_callbacks[i] = NULL; } } /* Reallocate the callback data array. */ cbdata_tmp = realloc(ctx->callback_data, sizeof(*cbdata_tmp) * sensor_count); if (sensor_count > 0 && cbdata_tmp == NULL) { e = ENOMEM; goto err2; } ctx->callback_data = cbdata_tmp; /* Initialize new callback data pointers. */ if (sensor_count > ctx->sensor_count) { for (i = ctx->sensor_count; i < sensor_count; i++) { ctx->callback_data[i] = NULL; } } /* Assemble the packet and send it to the server. */ e = write(ctx->socket_fd, header, 4); if (e == -1) { e = errno; goto err2; } for (i = 0; i < sensor_count; i++) { e = write(ctx->socket_fd, sensors[i], name_lengths[i] + 1); if (e == -1) { e = errno; goto err2; } } free(name_lengths); /* Wait for the server to respond. */ while (1) { e = process_next_packet(ctx, -1, NULL); if (e < 0) goto err1; if (e == 0) break; } /* Success. */ return 0; /* Handle errors caught previously. */ err2: free(name_lengths); err1: errno = e; return -1; } /* Registers a callback function for a given named sensor. */ int argus_callback(argus_t* ctx, char* sensor, void* data, argus_cb_t cb) { int e, i; /* Check for bogus arguments. */ if (ctx == NULL || ctx->socket_fd == -1 || sensor == NULL) { e = EINVAL; goto err1; } /* Find named sensor in context's sensor list. */ for (i = 0; i < ctx->sensor_count; i++) { if (strcmp(sensor, ctx->sensor_names[i]) == 0) { goto found_sensor; } } /* Sensor not found in list. */ e = ESRCH; /* XXX: Is this the right error code? */ goto err1; found_sensor: ctx->sensor_callbacks[i] = cb; ctx->callback_data[i] = data; /* Success. */ return 0; /* Handle errors caught previously. */ err1: errno = e; return -1; } /* XXX: Do we need a function to set the packet callback function? */ /* Enter an event loop, waiting for incoming data from the server, and * processing the data when received. */ int argus_loop(argus_t* ctx) { int e; /* Check for bogus arguments. */ if (ctx == NULL || ctx->socket_fd == -1) { e = EINVAL; return -1; } ctx->loop_exit = 0; while (!ctx->loop_exit) { e = process_next_packet(ctx, -1, NULL); if (e < 0) return -1; } /* Loop exited successfully. */ return 0; } /* When called from a sensor callback, this function causes the enclosing * argus_loop() to exit successfully, returning control to its caller. */ int argus_loop_exit(argus_t* ctx) { if (ctx == NULL || ctx->socket_fd == -1) { errno = EINVAL; return -1; } ctx->loop_exit = 1; return 0; } /* Halts program execution until new data is available from the server. * This function does not return until data becomes available, the timeout * expires, or an error occurs (such as a disconnection from the server). */ int argus_data_wait(argus_t* ctx, int timeout) { if (ctx == NULL || ctx->socket_fd == -1) { errno = EINVAL; return -1; } return process_next_packet(ctx, timeout, NULL); } /* Read and process a packet of data from the server, executing callbacks as * necessary. After this function returns with success, new data is * available. */ int argus_data_read(argus_t* ctx) { if (ctx == NULL || ctx->socket_fd == -1) { errno = EINVAL; return -1; } return process_next_packet(ctx, -1, NULL); } /* Requests a list of names of all available sensors from the server. */ int argus_sensors(argus_t* ctx, char*** sensors) { int e; unsigned char buf[2] = { 2, 2 }; /* Check for bogus arguments. */ if (ctx == NULL || ctx->socket_fd == -1 || sensors == NULL) { e = EINVAL; goto err1; } /* Send sensor name request to server. */ e = write(ctx->socket_fd, buf, 2); if (e == -1) { e = errno; goto err1; } /* Wait for the server to respond. */ while (1) { e = process_next_packet(ctx, -1, sensors); if (e < 0) goto err1; if (e == 2) break; } /* Success. */ return ctx->packet[0]; /* Handle errors caught previously. */ err1: e = errno; return -1; } int argus_open_shm(argus_t* ctx) { } int argus_close_shm(argus_t* ctx) { } /* vim: set ts=4 sts=4 sw=4 tw=80 et: */