diff --git a/src/common/mini-at.h b/src/common/mini-at.h new file mode 100644 index 0000000..4fea2c3 --- /dev/null +++ b/src/common/mini-at.h @@ -0,0 +1,48 @@ +/* mini-at/src/mini-atd/time.c + * + * (c)2006, Laurence Withers, . + * Released under the GNU GPLv2. See file COPYING or + * http://www.gnu.org/copyleft/gpl.html for details. +*/ + + + +/* AT_MAGIC + * + * This number, "MNAT" in ASCII, is prepended to the message (and its complement concatenated). + * This stops random writes to the socket from being interpreted. + */ +#define AT_MAGIC 0x4D4E4154 + + + +/* AT_MSG_SIZE + * + * The biggest packet we read/write from the socket. This limits the length of the commandline, + * so we ideally want it to be as large as possible. + */ +#define AT_MSG_SIZE 0x8000 + + + +/* struct at_msg + * + * The header of any message written to the socket. After the header, we find the path and input + * filename path data, followed by num_args * [int arg_len, arg]. The structure ends with + * ~AT_MAGIC. + */ +struct at_msg { + int magic; // must be AT_MAGIC + int version; // (major << 20) | (minor << 10) | micro + int when; // time_t + int cmd_len; // length of path, in bytes, excluding \0 + int in_fn_len; // for stdin filename, 0 if no redirection + int num_args; // number of commandline arguments, always > 0 +}; + + + +/* options for text editors +kate: replace-trailing-space-save true; space-indent true; tab-width 4; +vim: expandtab:ts=4:sw=4 +*/ diff --git a/src/mini-at/TopSource.c b/src/mini-at/TopSource.c index 53f66d9..50502d4 100644 --- a/src/mini-at/TopSource.c +++ b/src/mini-at/TopSource.c @@ -5,13 +5,13 @@ * http://www.gnu.org/copyleft/gpl.html for details. */ + + // Below are all the includes used throughout the application. +#include +#include -int main(void) -{ - return 0; -} /* options for text editors kate: replace-trailing-space-save true; space-indent true; tab-width 4; diff --git a/src/mini-at/main.c b/src/mini-at/main.c new file mode 100644 index 0000000..712a685 --- /dev/null +++ b/src/mini-at/main.c @@ -0,0 +1,138 @@ +/* mini-at/src/mini-at/main.c + * + * (c)2006, Laurence Withers, . + * Released under the GNU GPLv2. See file COPYING or + * http://www.gnu.org/copyleft/gpl.html for details. +*/ + + + +void version(void) +{ + printf("mini-at " VERSION "\n" + "(c)2006, Laurence Withers.\n"); +} + + + +void usage(void) +{ + printf("Usage:\n\n" + " mini-at [options] time cmd [args ...]\n\n" + "Options:\n" + "-h, --help Display this screen.\n" + "-V, --version Display version.\n" + "-f, --file Specify filename to read stdin from, - to copy stdin.\n", + "-0, --argv0 Change argv[0] from `cmd' to `x'.\n" + "\n" + "'time' is an ISO8601 time. If you specify an incomplete time, e.g. '2010', we simply\n" + "take the earliest time it could represent (i.e. 2010-01-01T00:00:00).\n" + "\n"); + version(); +} + + + +struct option options[] = { + { "help", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'V' }, + { "file", required_argument, 0, 'f' }, + { "argv0", required_argument, 0, '0' }, + { 0, 0, 0, 0 } +} + + + +int main(int argc, char* argv[]) +{ + const char* stdin_file = 0, * argv0 = 0; + struct at_msg a; + char msg[AT_MSG_SIZE]; + int size, i, x; + + while(1) { + switch(getopt_long(argc, argv, "hVf:0:", options, 0)) { + case -1: + goto done; + + case 'h': + usage(); + return 0; + + case 'V': + version(); + return 0; + + case 'f': + if(stdin_file) { + fprintf(stderr, "Input file can only be specified once.\n"); + return 1; + } + stdin_file = optarg; + break; + + case '0': + if(argv0) { + fprintf(stderr, "argv[0] can only be specified once.\n"); + return 1; + } + argv0 = optarg; + break; + } + } + +done: + argv += optind; + argc -= optind; + if(argc < 2) { + usage(); + return 1; + } + + // parse time + t = parse_time(argv[0]); + if(t == (time_t)-1) return 1; + ++argv; + --argc; + + // set up message header + memset(&a, 0, sizeof(a)); + a.magic = AT_MAGIC; + a.version = (VERMAJOR << 20) | (VERMINOR << 10) | VERMICRO; + a.when = t; + a.cmd_len = strlen(argv[0]); + if(stdin_file) { + stdin_file = do_stdin(stdin_file); + a.in_fn_len = strlen(stdin_file); + } + a.num_args = argc; + + // set up message + size = 0; + msg_add(msg, &size, &a, sizeof(a)); + msg_add(msg, &size, argv[0], a.cmd_len); + if(stdin_file) msg_add(msg, &size, stdin_file, a.in_fn_len); + + if(!argv0) argv0 = argv[0]; + x = strlen(argv0); + msg_add(msg, &size, &x, sizeof(x)); + msg_add(msg, &size, argv0, x); + + for(i = 1; i < argc; ++i) { + x = strlen(argv[i]); + msg_add(msg, &size, &x, sizeof(x)); + msg_add(msg, &size, argv[i], x); + } + + // send message + // TODO + + return 0; +} + + + +/* options for text editors +kate: replace-trailing-space-save true; space-indent true; tab-width 4; +vim: expandtab:ts=4:sw=4 +*/ diff --git a/src/mini-atd/build.monolithic b/src/mini-atd/build.monolithic index 859b8e8..ef8d047 100644 --- a/src/mini-atd/build.monolithic +++ b/src/mini-atd/build.monolithic @@ -7,7 +7,8 @@ MONOLITHIC_TESTS="src/mini-atd/build.app src/mini-atd/build.monolithic" if [ -z "${mini_atd_MONOLITHIC}" ] then - MONOLITHIC_SOURCE="$(echo src/mini-atd/{TopSource,setup,util,time,main}.c)" + MONOLITHIC_SOURCE="src/common/mini-at.h + $(echo src/mini-atd/{TopSource,setup,util,time,socket,main}.c)" make_monolithic ${SRC} C || return 1 mini_atd_MONOLITHIC=1 diff --git a/src/mini-atd/main.c b/src/mini-atd/main.c index a479e0b..8edce04 100644 --- a/src/mini-atd/main.c +++ b/src/mini-atd/main.c @@ -47,6 +47,7 @@ int main(int argc, char* argv[]) char* endp; int perms = -1, daemon = 0, fd = -1; char fn_buf[sizeof(struct sockaddr_un) - sizeof(int)]; // not perfect, but solves build errors + struct time_db tdb; // process commandline options while(1) { @@ -121,10 +122,12 @@ done: setup_signals(); fd = setup_socket(fn_buf, perms); if(fd == -1) return 1; + memset(&tdb, 0, sizeof(tdb)); // process while(!Quit) { -// if(process_socket(fd)) return 1; + if(process_socket(fd, &tdb)) return 1; + process_time_db(&tdb); sleep(1); } diff --git a/src/mini-atd/socket.c b/src/mini-atd/socket.c new file mode 100644 index 0000000..3d21645 --- /dev/null +++ b/src/mini-atd/socket.c @@ -0,0 +1,128 @@ +/* mini-at/src/mini-atd/socket.c + * + * (c)2006, Laurence Withers, . + * Released under the GNU GPLv2. See file COPYING or + * http://www.gnu.org/copyleft/gpl.html for details. +*/ + + + +/* process_message() + * + * This is called whenever we receive a single message. It should decode the message and add an + * entry to the time database `tdb'. + */ +void process_message(const char* buf, int size, struct time_db* tdb) +{ + struct at_msg a; + struct time_db_entry t; + struct tm* tm; + int i, j; + + memset(&t, 0, sizeof(t)); + + // sanity checking + if(size < (int)sizeof(struct at_msg)) goto bad; + memcpy(&a, buf, sizeof(a)); + + if(a.magic != AT_MAGIC) { + LOG(LOG_WARNING, "bad message received through socket (magic was 0x%X, should be 0x%X).", + a.magic, AT_MAGIC); + return; + } + + if(a.version != ((VERMAJOR << 20) | (VERMINOR << 10) | VERMICRO)) { + LOG(LOG_WARNING, "version mismatch (got %d.%d.%d, expected " VERSION ").", + a.version >> 20, (a.version >> 10) & 0x3FF, a.version & 0x3FF); + return; + } + + t.when = a.when; + t.num_args = a.num_args; + buf += sizeof(struct at_msg); + size -= sizeof(struct at_msg); + + // sanity check + if(a.cmd_len < 1 || a.in_fn_len < 0 || a.num_args < 1 + || size < a.cmd_len + a.in_fn_len) goto bad; + + // read in the command path + t.path = safe_malloc(a.cmd_len + 1); + memcpy(t.path, buf, a.cmd_len); + buf += a.cmd_len; + size -= a.cmd_len; + + // read in (optional) input filename + if(a.in_fn_len) { + t.in = safe_malloc(a.in_fn_len + 1); + memcpy(t.in, buf, a.in_fn_len); + buf += a.in_fn_len; + size -= a.in_fn_len; + } + + // now read in arguments + t.args = safe_malloc(sizeof(char*) * (t.num_args + 1)); // extra +1 for NULL for execve + for(i = 0; i < t.num_args; ++i) { + if(size < (int)sizeof(int)) goto bad; + memcpy(&j, buf, sizeof(int)); + if(j < 0) goto bad; + buf += sizeof(int); + size -= sizeof(int); + + if(size < j) goto bad; + t.args[i] = safe_malloc(j + 1); + if(j) { + memcpy(t.args[i], buf, j); + buf += j; + size -= j; + } + } + + // check for end + if(size != sizeof(int)) goto bad; + memcpy(&j, buf, sizeof(int)); + if(j != ~AT_MAGIC) goto bad; + + // done -- insert our new entry + tm = gmtime(&t.when); + LOG(LOG_INFO, "Command ``%s'' scheduled for %04d-%02d-%02dT%02d:%02d:%02d", + t.path, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + add_time_db_entry(tdb, &t); + return; + +bad: + LOG(LOG_WARNING, "bad message of %d bytes received through socket.", size); +} + + + +/* process_socket() + * + * Checks the socket for input; if some is available, processes it. + */ +int process_socket(int fd, struct time_db* tdb) +{ + ssize_t rd; + char buf[AT_MSG_SIZE]; + + while(1) { + rd = read(fd, buf, sizeof(buf)); + if(rd == -1) { + if(errno == EINTR) continue; + if(errno == EAGAIN) return 0; + + LOG(LOG_CRIT, "read() on socket fd %d failed - %s (%d).", + fd, strerror(errno), errno); + return -1; + } + + process_message(buf, rd, tdb); + } +} + + + +/* options for text editors +kate: replace-trailing-space-save true; space-indent true; tab-width 4; +vim: expandtab:ts=4:sw=4 +*/ diff --git a/src/mini-atd/time.c b/src/mini-atd/time.c index 6b60cd3..84cba56 100644 --- a/src/mini-atd/time.c +++ b/src/mini-atd/time.c @@ -201,11 +201,14 @@ void free_time_db(struct time_db* tdb) /* add_time_db_entry() * - * Adds an entry `t' into the time database `tdb'. Does a sorted list insert. + * Adds an entry `t' into the time database `tdb'. Bitwise copies `t'. Does a sorted list insert. */ -void add_time_db_entry(struct time_db* tdb, struct time_db_entry* t) +void add_time_db_entry(struct time_db* tdb, const struct time_db_entry* t) { - struct time_db_entry* iter, * prev; + struct time_db_entry* iter, * prev, * new; + + new = safe_malloc(sizeof(struct time_db_entry)); + memcpy(new, t, sizeof(struct time_db_entry)); prev = 0; iter = tdb->head; @@ -214,10 +217,10 @@ void add_time_db_entry(struct time_db* tdb, struct time_db_entry* t) prev = iter; iter = iter->next; } - t->next = iter; + new->next = iter; - if(prev) prev->next = t; - else tdb->head = t; + if(prev) prev->next = new; + else tdb->head = new; }