More WIP. Ready for 1.0.0 release.

This commit is contained in:
Laurence Withers 2006-10-10 16:58:06 +01:00
parent b2739c367a
commit 15257a911d
11 changed files with 192 additions and 11 deletions

29
README
View File

@ -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.

View File

@ -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>

View File

@ -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

View File

@ -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);
} }

39
src/mini-at/socket.c Normal file
View File

@ -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
*/

58
src/mini-at/stdin.c Normal file
View File

@ -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
*/

View File

@ -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) {

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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

View File

@ -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=""