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