/* FIXME bother removing _GNU_SOURCE? It only gives struct ucred, which * is linux-only anyway */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "alarmd_lock.h" #include "alarmd_proto.h" struct alarm { UT_hash_handle hh; uuid_t uuid; char *name; bool is_raised; pid_t owner; time_t last_change; }; static struct alarm *alarms; int8_t read_string(int sock, unsigned char (*buffer)[128]) { uint8_t length = 0; if (recv(sock, &length, sizeof(uint8_t), 0) < sizeof(uint8_t)) { return -1; } if (length >= 128) { return -1; } if (recv(sock, buffer, length, 0) < 0) { return -1; } (*buffer)[length] = '\0'; return length; } int handle_register(int c_sock, pid_t owner) { uuid_t uuid; unsigned char buffer[128]; struct alarm *new_alarm = NULL; if (read_string(c_sock, &buffer) < 0) { return 1; } uuid_generate(uuid); if (send(c_sock, uuid, sizeof(uuid_t), 0) != sizeof(uuid_t)) { perror("send"); return 1; } if ((new_alarm = malloc(sizeof(*new_alarm))) == NULL) { perror("malloc"); return 1; } new_alarm->name = strdup((const char*)buffer); memcpy(new_alarm->uuid, uuid, sizeof(new_alarm->uuid)); new_alarm->is_raised = 0; time(&(new_alarm->last_change)); new_alarm->owner = owner; alarmd_lock(); HASH_ADD(hh, alarms, uuid, sizeof(uuid), new_alarm); alarmd_unlock(); return 0; } int handle_deregister(int c_sock) { uuid_t uuid; struct alarm *target = NULL; if (recv(c_sock, &uuid, sizeof(uuid_t), 0) < sizeof(uuid_t)) { return 1; } alarmd_lock(); HASH_FIND(hh, alarms, uuid, sizeof(uuid_t), target); if (target != NULL) { HASH_DEL(alarms, target); free(target); } alarmd_unlock(); return 0; } int handle_set_raised_generic(int c_sock, int shall_raise) { uuid_t uuid; size_t nread = 0; struct alarm *target = NULL; if ((nread = recv(c_sock, &uuid, sizeof(uuid), 0)) < sizeof(uuid)) { fprintf(stderr, "Received UUID too short\n"); return 1; } alarmd_lock(); HASH_FIND(hh, alarms, uuid, sizeof(uuid_t), target); if (target != NULL) { if (target->is_raised != shall_raise) { time(&(target->last_change)); target->is_raised = shall_raise; } } alarmd_unlock(); return 0; } int handle_raise(int c_sock) { return handle_set_raised_generic(c_sock, 1); } int handle_clear(int c_sock) { return handle_set_raised_generic(c_sock, 0); } int handle_query(int c_sock) { uint32_t count = 0; uint8_t length = 0; struct alarm *a = NULL; struct alarm *tmp = NULL; alarmd_lock(); count = HASH_COUNT(alarms); if (send(c_sock, &count, sizeof(count), 0) < sizeof(count)) { perror("send"); } HASH_ITER(hh, alarms, a, tmp) { /* FIXME factor out, idiot. also bounds check on strlen */ length = strlen(a->name); if (send(c_sock, &length, sizeof(length), 0) < sizeof(length)) { perror("send"); } if (send(c_sock, a->name, strlen(a->name), 0) < strlen(a->name)) { perror("send"); } if (send(c_sock, &(a->is_raised), sizeof(a->is_raised), 0) < sizeof(a->is_raised)) { perror("send"); } if (send(c_sock, &(a->last_change), sizeof(a->last_change), 0) < sizeof(a->last_change)) { perror("send"); } if (send(c_sock, &(a->owner), sizeof(a->owner), 0) < sizeof(a->owner)) { perror("send"); } } alarmd_unlock(); return 0; } void *handle_client(void *c_sock_) { int c_sock = *((int*)c_sock_); uint32_t packet_type = 0; ssize_t nread = 0; unsigned int cred_len = 0; struct ucred cred; cred_len = sizeof(struct ucred); if (getsockopt(c_sock, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) < 0) { perror("getsockopt"); close(c_sock); } while (1) { /* read packet type */ nread = recv(c_sock, &packet_type, sizeof(packet_type), 0); if (nread < 0) { perror("recv"); return NULL; } else if (nread == 0) { break; } else if (nread < sizeof(packet_type)) { fprintf(stderr, "Packet type too short\n"); return NULL; } switch(packet_type) { case ALARMD_PACKET_TYPE_REGISTER: if (handle_register(c_sock, cred.pid)) {; close(c_sock); } break; case ALARMD_PACKET_TYPE_DEREGISTER: if (handle_deregister(c_sock)) { close(c_sock); } break; case ALARMD_PACKET_TYPE_RAISE: if (handle_raise(c_sock)) { close(c_sock); } break; case ALARMD_PACKET_TYPE_CLEAR: if (handle_clear(c_sock)) { close(c_sock); } break; case ALARMD_PACKET_TYPE_QUERY: if (handle_query(c_sock)) { close(c_sock); } break; default: fprintf(stderr, "Unknown packet type %d\n", packet_type); break; } } close(c_sock); return NULL; } int main(int argc, char **argv) { int sock = 0; int c_sock = 0; socklen_t c_addr_l = 0; struct sockaddr_un local, c_addr; pthread_t thread; if (argc != 2) { fprintf(stderr, "Syntax: %s socket_name\n", argv[0]); return 1; } if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { perror("socket"); return 1; } local.sun_family = AF_UNIX; strcpy(local.sun_path, argv[1]); unlink(local.sun_path); if (bind(sock, (struct sockaddr *)&local, strlen(local.sun_path) + sizeof(local.sun_family)) < 0) { perror("bind"); return 1; } if (listen(sock, 128) < 0) { perror("listen"); return 1; } c_addr_l = sizeof(c_addr); while (1) { if ((c_sock = accept(sock, (struct sockaddr *)&c_addr, &c_addr_l)) < 0) { perror("accept"); return 1; } pthread_create(&thread, NULL, handle_client, &c_sock); pthread_detach(thread); } fprintf(stderr, "Shutting down\n"); close(sock); }