More WIP. Ready for 1.0.0 release.
This commit is contained in:
		
							parent
							
								
									b2739c367a
								
							
						
					
					
						commit
						15257a911d
					
				
							
								
								
									
										29
									
								
								README
								
								
								
								
							
							
						
						
									
										29
									
								
								README
								
								
								
								
							|  | @ -23,3 +23,32 @@ Why this and not the standard `at' suite? | ||||||
|     deleted. |     deleted. | ||||||
|  4) Flexible user control through use of Uinx sockets. |  4) Flexible user control through use of Uinx sockets. | ||||||
| 
 | 
 | ||||||
|  | Instructions | ||||||
|  | ------------ | ||||||
|  | 
 | ||||||
|  | Run the programs with `--help' for a summary. | ||||||
|  | 
 | ||||||
|  | The daemon, mini-atd, must be run in the background. It is responsible for keeping track of commands | ||||||
|  | to run and for running them at the appropriate time. The client, mini-at, uses a named socket to | ||||||
|  | communicate with the daemon. By default, the daemon creates this socket at ``~/.mini-atd''. | ||||||
|  | 
 | ||||||
|  | The daemon can either run in the foreground (default) or in the background, with the `--daemon' or  | ||||||
|  | `-d' option. You can change the name of the socket it uses, and the permissions on the socket. By | ||||||
|  | default, the permissions are 0600. This means that only the user who ran the daemon (or root) can | ||||||
|  | cause commands to be run. Commands are always run as the user who ran the daemon, so it is important | ||||||
|  | to ensure that only trusted users have access to the socket. | ||||||
|  | 
 | ||||||
|  | The client uses a time specification in ISO8601. If you don't fully specify the time, it uses the | ||||||
|  | earliest possible interpretation (so `2006' would give `2006-01-01T00:00:00'). If no time zone is | ||||||
|  | specified, UTC is assumed, but it is best to explicitly indicate this with a Z. | ||||||
|  | 
 | ||||||
|  | If you wish to provide stdin other than /dev/null for a process, then you can specify a file on the | ||||||
|  | client commandline using `-f' or `--file'. Note that this file will be deleted once the process is | ||||||
|  | run. Alternatively, you can use `-f -' and pipe the content you want to the stdin of the client, | ||||||
|  | which will store a copy in a temporary file. | ||||||
|  | 
 | ||||||
|  | If you wish to run one program but pretend that you've run it as something else, you can specify the | ||||||
|  | argv0 option in the client. By default, this will just be a copy of the command name. | ||||||
|  | 
 | ||||||
|  | The client will copy its commandline arguments from the shell, and performs no processing on them. | ||||||
|  | This means that it does not expand words or perform quoting. | ||||||
|  |  | ||||||
|  | @ -14,6 +14,10 @@ | ||||||
| #include <getopt.h> | #include <getopt.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <sys/un.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/socket.h> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ MONOLITHIC_TESTS="src/mini-at/build.app src/mini-at/build.monolithic" | ||||||
| if [ -z "${mini_at_MONOLITHIC}" ] | if [ -z "${mini_at_MONOLITHIC}" ] | ||||||
| then | then | ||||||
|     MONOLITHIC_SOURCE="src/common/mini-at.h |     MONOLITHIC_SOURCE="src/common/mini-at.h | ||||||
|         $(echo src/mini-at/{TopSource,time,main}.c)" |         $(echo src/mini-at/{TopSource,time,stdin,socket,main}.c)" | ||||||
|     make_monolithic ${SRC} C || return 1 |     make_monolithic ${SRC} C || return 1 | ||||||
| 
 | 
 | ||||||
|     mini_at_MONOLITHIC=1 |     mini_at_MONOLITHIC=1 | ||||||
|  |  | ||||||
|  | @ -37,6 +37,7 @@ void usage(void) | ||||||
|         "-V, --version      Display version.\n" |         "-V, --version      Display version.\n" | ||||||
|         "-f, --file <x>     Specify filename to read stdin from, - to copy stdin.\n" |         "-f, --file <x>     Specify filename to read stdin from, - to copy stdin.\n" | ||||||
|         "-0, --argv0 <x>    Change argv[0] from `cmd' to `x'.\n" |         "-0, --argv0 <x>    Change argv[0] from `cmd' to `x'.\n" | ||||||
|  |         "-s, --socket <x>   Name of mini-atd's socket (default: ~/.mini-atd).\n" | ||||||
|         "\n" |         "\n" | ||||||
|         "'time' is an ISO8601 time. If you specify an incomplete time, e.g. '2010', we simply\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" |         "take the earliest time it could represent (i.e. 2010-01-01T00:00:00).\n" | ||||||
|  | @ -58,14 +59,14 @@ struct option options[] = { | ||||||
| 
 | 
 | ||||||
| int main(int argc, char* argv[]) | int main(int argc, char* argv[]) | ||||||
| { | { | ||||||
|     const char* stdin_file = 0, * argv0 = 0; |     const char* stdin_file = 0, * argv0 = 0, * socket = 0; | ||||||
|     struct at_msg a; |     struct at_msg a; | ||||||
|     char msg[AT_MSG_SIZE]; |     char msg[AT_MSG_SIZE], sockbuf[sizeof(struct sockaddr_un) - sizeof(int)]; | ||||||
|     int size, i, x; |     int size, i, x; | ||||||
|     time_t t; |     time_t t; | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|         switch(getopt_long(argc, argv, "hVf:0:", options, 0)) { |         switch(getopt_long(argc, argv, "hVf:0:s:", options, 0)) { | ||||||
|         case -1: |         case -1: | ||||||
|             goto done; |             goto done; | ||||||
| 
 | 
 | ||||||
|  | @ -92,6 +93,14 @@ int main(int argc, char* argv[]) | ||||||
|             } |             } | ||||||
|             argv0 = optarg; |             argv0 = optarg; | ||||||
|             break; |             break; | ||||||
|  | 
 | ||||||
|  |         case 's': | ||||||
|  |             if(socket) { | ||||||
|  |                 fprintf(stderr, "Socket can only be specified once.\n"); | ||||||
|  |                 return 1; | ||||||
|  |             } | ||||||
|  |             socket = optarg; | ||||||
|  |             break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -122,6 +131,7 @@ done: | ||||||
|     a.cmd_len = strlen(argv[0]); |     a.cmd_len = strlen(argv[0]); | ||||||
|     if(stdin_file) { |     if(stdin_file) { | ||||||
|         stdin_file = do_stdin(stdin_file); |         stdin_file = do_stdin(stdin_file); | ||||||
|  |         if(!stdin_file) return 1; | ||||||
|         a.in_fn_len = strlen(stdin_file); |         a.in_fn_len = strlen(stdin_file); | ||||||
|     } |     } | ||||||
|     a.num_args = argc; |     a.num_args = argc; | ||||||
|  | @ -143,10 +153,21 @@ done: | ||||||
|         msg_add(msg, &size, argv[i], x); |         msg_add(msg, &size, argv[i], x); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // send message
 |     a.magic = ~a.magic; | ||||||
|     // TODO
 |     msg_add(msg, &size, (char*)&a.magic, sizeof(a.magic)); | ||||||
| 
 | 
 | ||||||
|     return 0; |     // send message
 | ||||||
|  |     if(!socket) { | ||||||
|  |         socket = getenv("HOME"); | ||||||
|  |         if(!socket) { | ||||||
|  |             fprintf(stderr, "$HOME not set, cannot find socket\n"); | ||||||
|  |             return 1; | ||||||
|  |         } | ||||||
|  |         snprintf(sockbuf, sizeof(sockbuf) - 1, "%s/.mini-atd", socket); | ||||||
|  |         sockbuf[sizeof(sockbuf) - 1] = 0; | ||||||
|  |         socket = sockbuf; | ||||||
|  |     } | ||||||
|  |     return send_message(socket, msg, size); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,39 @@ | ||||||
|  | /* mini-at/src/mini-at/TopSource.c
 | ||||||
|  |  * | ||||||
|  |  *  (c)2006, Laurence Withers, <l@lwithers.me.uk>. | ||||||
|  |  *  Released under the GNU GPLv2. See file COPYING or | ||||||
|  |  *  http://www.gnu.org/copyleft/gpl.html for details.
 | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* send_message()
 | ||||||
|  |  * | ||||||
|  |  *  Sends the message `msg' (of `size' bytes) to the unix socket `sockname'. Returns 0 on success. | ||||||
|  |  */ | ||||||
|  | int send_message(const char* sockname, const char* msg, int size) | ||||||
|  | { | ||||||
|  |     int fd; | ||||||
|  |     ssize_t wr; | ||||||
|  |     struct sockaddr_un addr; | ||||||
|  | 
 | ||||||
|  |     fd = socket(PF_UNIX, SOCK_DGRAM, 0); | ||||||
|  |     memset(&addr, 0, sizeof(addr)); | ||||||
|  |     addr.sun_family = AF_UNIX; | ||||||
|  |     strcpy(addr.sun_path, sockname); | ||||||
|  | 
 | ||||||
|  |     wr = sendto(fd, msg, size, 0, (struct sockaddr*)&addr, sizeof(addr)); | ||||||
|  |     if(wr == -1) { | ||||||
|  |         perror("sendto"); | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* options for text editors
 | ||||||
|  | kate: replace-trailing-space-save true; space-indent true; tab-width 4; | ||||||
|  | vim: expandtab:ts=4:sw=4 | ||||||
|  | */ | ||||||
|  | @ -0,0 +1,58 @@ | ||||||
|  | /* mini-at/src/mini-at/stdin.c
 | ||||||
|  |  * | ||||||
|  |  *  (c)2006, Laurence Withers, <l@lwithers.me.uk>. | ||||||
|  |  *  Released under the GNU GPLv2. See file COPYING or | ||||||
|  |  *  http://www.gnu.org/copyleft/gpl.html for details.
 | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* do_stdin()
 | ||||||
|  |  * | ||||||
|  |  *  Copies data from the file specified in `filename' (or from stdin if `-'). At present, we don't  | ||||||
|  |  *  create a copy of an existing file; we merely point at it. Perhaps an option could be added to | ||||||
|  |  *  do the copy, but `cat' could do the job just as easily. | ||||||
|  |  */ | ||||||
|  | const char* do_stdin(const char* filename) | ||||||
|  | { | ||||||
|  |     ssize_t rd; | ||||||
|  |     char buf[1024]; | ||||||
|  |     static char fn[] = "/tmp/mini-at-stdin.XXXXXX"; | ||||||
|  |     int tfd; | ||||||
|  | 
 | ||||||
|  |     // just point at existing file
 | ||||||
|  |     if(strcmp(filename, "-")) return filename; | ||||||
|  | 
 | ||||||
|  |     // open temporary file (relies on libc opening it mode 0600)
 | ||||||
|  |     tfd = mkstemp(fn); | ||||||
|  |     if(tfd == -1) { | ||||||
|  |         perror("mkstemp"); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // copy stdin to a temporary file
 | ||||||
|  |     while(1) { | ||||||
|  |         rd = read(0, buf, sizeof(buf)); | ||||||
|  |         switch(rd) { | ||||||
|  |         case -1: | ||||||
|  |             perror("read"); | ||||||
|  |             return 0; | ||||||
|  | 
 | ||||||
|  |         case 0:     // EOF
 | ||||||
|  |             return fn; | ||||||
|  | 
 | ||||||
|  |         default: | ||||||
|  |             if(write(tfd, buf, rd) != rd) { | ||||||
|  |                 perror("write"); | ||||||
|  |                 return 0; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* options for text editors
 | ||||||
|  | kate: replace-trailing-space-save true; space-indent true; tab-width 4; | ||||||
|  | vim: expandtab:ts=4:sw=4 | ||||||
|  | */ | ||||||
|  | @ -123,6 +123,7 @@ done: | ||||||
|     fd = setup_socket(fn_buf, perms); |     fd = setup_socket(fn_buf, perms); | ||||||
|     if(fd == -1) return 1; |     if(fd == -1) return 1; | ||||||
|     memset(&tdb, 0, sizeof(tdb)); |     memset(&tdb, 0, sizeof(tdb)); | ||||||
|  |     _debug_time_db = &tdb; | ||||||
| 
 | 
 | ||||||
|     // process
 |     // process
 | ||||||
|     while(!Quit) { |     while(!Quit) { | ||||||
|  |  | ||||||
|  | @ -165,10 +165,13 @@ void signal_handler(int sig) | ||||||
|     signal(sig, SIG_DFL); |     signal(sig, SIG_DFL); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void debug_handler(int sig); | ||||||
|  | 
 | ||||||
| void setup_signals(void) | void setup_signals(void) | ||||||
| { | { | ||||||
|     signal(SIGINT, signal_handler); |     signal(SIGINT, signal_handler); | ||||||
|     signal(SIGTERM, signal_handler); |     signal(SIGTERM, signal_handler); | ||||||
|  |     signal(SIGUSR1, debug_handler); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,7 +17,9 @@ void process_message(const char* buf, int size, struct time_db* tdb) | ||||||
|     struct at_msg a; |     struct at_msg a; | ||||||
|     struct time_db_entry t; |     struct time_db_entry t; | ||||||
|     struct tm* tm; |     struct tm* tm; | ||||||
|     int i, j; |     int i, j, realsize; | ||||||
|  | 
 | ||||||
|  |     realsize = size; | ||||||
| 
 | 
 | ||||||
|     memset(&t, 0, sizeof(t)); |     memset(&t, 0, sizeof(t)); | ||||||
| 
 | 
 | ||||||
|  | @ -91,7 +93,7 @@ void process_message(const char* buf, int size, struct time_db* tdb) | ||||||
|     return; |     return; | ||||||
| 
 | 
 | ||||||
| bad: | bad: | ||||||
|     LOG(LOG_WARNING, "bad message of %d bytes received through socket.", size); |     LOG(LOG_WARNING, "bad message of %d bytes received through socket.", realsize); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -151,7 +151,7 @@ void process_time_db(struct time_db* tdb) | ||||||
|     time(&now); |     time(&now); | ||||||
| 
 | 
 | ||||||
|     iter = tdb->head; |     iter = tdb->head; | ||||||
|     while(iter && now <= iter->when) { |     while(iter && now >= iter->when) { | ||||||
|         // run this command
 |         // run this command
 | ||||||
|         pid = process_time_db_entry(iter); |         pid = process_time_db_entry(iter); | ||||||
|         if(pid != -1) { |         if(pid != -1) { | ||||||
|  | @ -225,6 +225,30 @@ void add_time_db_entry(struct time_db* tdb, const struct time_db_entry* t) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | struct time_db* _debug_time_db = 0; | ||||||
|  | void dump_time_db(struct time_db* tdb) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |     struct tm tm; | ||||||
|  |     struct time_db_entry* iter; | ||||||
|  | 
 | ||||||
|  |     for(i = 0; i < tdb->num_pids; ++i) LOG(LOG_DEBUG, "waiting on PID %lu", (long)tdb->pids[i]); | ||||||
|  |     for(iter = tdb->head; iter; iter = iter->next) { | ||||||
|  |         gmtime_r(&iter->when, &tm); | ||||||
|  |         LOG(LOG_DEBUG, "time database has entry %s for %04d%02d%02dT%02d%02d%02d", | ||||||
|  |             iter->path, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void debug_handler(int sig) | ||||||
|  | { | ||||||
|  |     signal(sig, debug_handler); | ||||||
|  |     if(_debug_time_db) dump_time_db(_debug_time_db); | ||||||
|  |     else LOG(LOG_DEBUG, "no time database"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /* options for text editors
 | /* options for text editors
 | ||||||
| kate: replace-trailing-space-save true; space-indent true; tab-width 4; | kate: replace-trailing-space-save true; space-indent true; tab-width 4; | ||||||
| vim: expandtab:ts=4:sw=4 | vim: expandtab:ts=4:sw=4 | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								version
								
								
								
								
							
							
						
						
									
										2
									
								
								version
								
								
								
								
							|  | @ -10,7 +10,7 @@ | ||||||
| # VERSION contains the full version number of the library, which is  | # VERSION contains the full version number of the library, which is  | ||||||
| # expected to be in 'major.minor.micro' format. It can optionally be  | # expected to be in 'major.minor.micro' format. It can optionally be  | ||||||
| # suffixed with a string. | # suffixed with a string. | ||||||
| VERMAJOR=0 | VERMAJOR=1 | ||||||
| VERMINOR=0 | VERMINOR=0 | ||||||
| VERMICRO=0 | VERMICRO=0 | ||||||
| VEREXTRA="" | VEREXTRA="" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Laurence Withers
						Laurence Withers