/* * main.c: the daemon itself */ #include #include #include #include #include #include #include #include #include "config.h" #include "trie.h" #include "task.h" #include "daemonize.h" #include "async.h" enum resp { SUCCESS, FAIL }; void reg_sig_handlers(void); bool process(int fd); int sockfd; //async.h has to be able to use it, and I would hate to write a getter for that int main (void) { openlog(cfg_log_ident, cfg_log_opt, cfg_log_facility); //I will probably close its fd a moment later, FIXME syslog(cfg_log_facility | LOG_INFO, "Starting"); // No arguments, if you want something, go change source, according to documentation. // daemonize(0); // Load saved state -- variable values and tasks async_load(0); // Set basic signal handlers reg_sig_handlers(); // Setup a socket to listen on sockfd = socket(AF_UNIX, SOCK_STREAM, 0); struct sockaddr_un addr; socklen_t addrsize = sizeof(struct sockaddr_un); addr.sun_family = AF_UNIX; memcpy(&addr.sun_path, cfg_socket, strlen(cfg_socket)+1); if(bind(sockfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1) { syslog(cfg_log_facility | LOG_ERR, "daemon: bind failed: %m"); } if(listen(sockfd, cfg_sock_maxclients) == -1) { syslog(cfg_log_facility | LOG_ERR, "daemon: listen failed: %m"); } // Everything prepared syslog(cfg_log_facility | LOG_INFO, "Started"); // Main loop: (everything else is handled by process() and signal handlers). while (true) { int fd = accept(sockfd, (struct sockaddr *) &addr, &addrsize); if (fd == -1) { syslog(cfg_log_facility | LOG_ERR, "daemon: accept failed: %m"); while(1); break; } else { syslog(cfg_log_facility | LOG_INFO, "daemon: accepted new connection"); process(fd); } } syslog(cfg_log_facility | LOG_CRIT, "Escaped infinite loop!"); return 5; } uint64_t read_uint64(int fd) { char buf[8]; read(fd, buf, 8); uint64_t num = *(uint64_t *)buf; return num; } bool write_uint64(uint64_t num, int fd) { char *buf = # write(fd, buf, 8); return true; //TODO: error handling } char *read_string(int fd) { char *buf; struct grow *tmp = grow_init(true); do { buf = malloc(1); if(buf == NULL) { syslog(cfg_log_facility | LOG_ERR, "cannot malloc memory for a letter: %m"); grow_drop(tmp); return NULL; } read(fd, buf, 1); grow_push(buf, tmp); } while (buf[0] != '\0'); char *str = malloc(tmp->elems * sizeof(char)); if (str == NULL) { syslog(cfg_log_facility | LOG_ERR, "cannot malloc memory for string: %m"); grow_drop(tmp); return NULL; } for (uint64_t j = 0; j < tmp -> elems; j++) { str[j] = *(char *)(tmp->arr[j]); } grow_drop(tmp); syslog(cfg_log_facility | LOG_DEBUG, "daemon: read_string: returning '%s'", str); return str; } bool write_string (char *str, int fd) { int len = strlen(str)+1; write(fd, str, len); return true; // TODO: error handling } void response(enum resp r, int fd) { if (r==SUCCESS) { char str[] = "OK"; write(fd, str, 2); } else { char str[] = "KO"; write(fd, str, 2); } return; } bool process(int fd) { char cmd[5]; read(fd, cmd, 4); cmd[4] = '\0'; if (0){ //alignment return true; } else if (strcmp(cmd, "TADD") == 0) { struct task *t = task_read(fd); if (t == NULL) { // We do not handle errors here, they are already logged response(FAIL,fd); return false; } if(task_add(*t)) { response(SUCCESS,fd); return true; } else { response(FAIL,fd); return false; } } else if (strcmp(cmd, "TDEL") == 0) { uint64_t id = read_uint64(fd); if(task_delete(id)) { response(SUCCESS,fd); return true; } else { response(FAIL,fd); return false; } } else if (strcmp(cmd, "TENA") == 0) { uint64_t id = read_uint64(fd); if(task_enable(id)) { response(SUCCESS,fd); return true; } else { response(FAIL,fd); return false; } } else if (strcmp(cmd, "TDIS") == 0) { uint64_t id = read_uint64(fd); if(task_disable(id)) { response(SUCCESS,fd); return true; } else { response(FAIL,fd); return false; } } else if (strcmp(cmd, "TDET") == 0) { uint64_t id = read_uint64(fd); struct task t = task_details(id); response(SUCCESS, fd); return task_write(t, fd); } else if (strcmp(cmd, "TLST") == 0) { bool retval = true; struct grow *lst = task_list(); if (lst == NULL) { response(SUCCESS, fd); retval &= write_uint64(0, fd); } else { response(SUCCESS,fd); retval &= write_uint64(lst->active_elems, fd); for (uint64_t i = 0; i < lst->elems; i++) { if (lst->arr[i] == NULL) { continue; } else { struct task t = *(struct task *)lst->arr[i]; retval &= task_write(t, fd); } } } return retval; } else if (strcmp(cmd, "VADD") == 0) { int64_t val = (int64_t) read_uint64(fd); char *str = read_string(fd); if(trie_set(str, val)) { response(SUCCESS, fd); return true; } else { response(FAIL, fd); return false; } } else if (strcmp(cmd, "VSET") == 0) { int64_t val = (int64_t) read_uint64(fd); char *str = read_string(fd); if(trie_set(str, val)) { response(SUCCESS, fd); return true; } else { response(FAIL, fd); return false; } } else if (strcmp(cmd, "VDEL") == 0) { char *str = read_string(fd); if(trie_unset(str)) { response(SUCCESS, fd); return true; } else { response(FAIL, fd); return false; } } else if (strcmp(cmd, "VLST") == 0) { bool retval = true; struct grow *lst = trie_list(); if (lst == NULL) { response(SUCCESS, fd); retval &= write_uint64(0, fd); return retval; } response(SUCCESS, fd); retval &= write_uint64(lst->elems, fd); for (uint64_t i = 0; i < lst->elems; i++) { struct trie_list item = *(struct trie_list *)lst->arr[i]; retval &= write_uint64(item.val, fd); retval &= write_string(item.name, fd); } return retval; } else if (strcmp(cmd, "VDET") == 0) { char *name = read_string(fd); if (name == NULL) { response(FAIL, fd); return false; } struct trie_retval var = trie_lookup(name); response(SUCCESS, fd); write(fd, &var, sizeof(struct trie_retval)); if (var.tasks == NULL) { write_uint64(0, fd); return true; } write_uint64(var.tasks->active_elems, fd); for(uint64_t i = 0; i< var.tasks->elems; i++) { if(var.tasks->arr[i] == NULL) { continue; } else { uint64_t task = *(uint64_t *)var.tasks->arr[i]; write_uint64(task, fd); } } return true; // TODO: error handling!! } else if (strcmp(cmd, "QUIT") == 0) { response(SUCCESS, fd); async_shutdown(0); return true; } else if (strcmp(cmd, "REST") == 0) { response(SUCCESS, fd); async_restart(0); return true; } else if (strcmp(cmd, "SAVE") == 0) { response(SUCCESS, fd); async_save(0); return true; } else if (strcmp(cmd, "FLSH") == 0) { char *safe = calloc(1+strlen(cfg_safe_string), sizeof(char)); read(fd, safe, strlen(cfg_safe_string)); if (strcmp(safe, cfg_safe_string) == 0) { response(SUCCESS, fd); async_flush(0); return true; } else { response(FAIL, fd); return false; } } else if (strcmp(cmd, "LOAD") == 0) { char *safe = calloc(1+strlen(cfg_safe_string), sizeof(char)); read(fd, safe, strlen(cfg_safe_string)); if (strcmp(safe, cfg_safe_string) == 0) { response(SUCCESS, fd); async_load(0); return true; } else { response(FAIL, fd); return false; } } else if (strcmp(cmd, "KILL") == 0) { char *safe = calloc(1+strlen(cfg_safe_string), sizeof(char)); read(fd, safe, strlen(cfg_safe_string)); if (strcmp(safe, cfg_safe_string) == 0) { response(SUCCESS, fd); async_kill(0); return true; } else { response(FAIL, fd); return false; } } else { syslog(cfg_log_facility | LOG_NOTICE, "Unknown command: %s (0x%02X%02X%02X%02X)", cmd, cmd[0], cmd[1], cmd[2], cmd[3]); char str[] = "KO"; write(fd, str, 2); response(FAIL,fd); return false; } } void reg_sig_handlers(void) { sigset_t empty; sigemptyset(&empty); // Shall not fail //SIGTERM struct sigaction term_sigh; term_sigh.sa_handler = &async_shutdown; term_sigh.sa_mask = empty; term_sigh.sa_flags = 0; sigaction(SIGTERM, &term_sigh, NULL); //SIGCHLD struct sigaction chld_sigh; chld_sigh.sa_handler = SIG_IGN; // We don't care for children chld_sigh.sa_mask = empty; chld_sigh.sa_flags = SA_NOCLDWAIT; sigaction(SIGCHLD, &chld_sigh, NULL); //SIGHUP struct sigaction hup_sigh; hup_sigh.sa_handler = &async_restart; hup_sigh.sa_mask = empty; hup_sigh.sa_flags = 0; sigaction(SIGHUP, &hup_sigh, NULL); //SIGINT struct sigaction int_sigh; int_sigh.sa_handler = &async_save; int_sigh.sa_mask = empty; int_sigh.sa_flags = 0; sigaction(SIGINT, &int_sigh, NULL); //SIGALRM struct sigaction alrm_sigh; alrm_sigh.sa_sigaction = &async_run; alrm_sigh.sa_mask = empty; alrm_sigh.sa_flags = SA_SIGINFO; sigaction(SIGALRM, &alrm_sigh, NULL); }