/* Task Spooler - a task queue system for the unix user Copyright (C) 2007 LluĂ­s Batlle i Rossell Please find the license in the provided COPYING file. */ #include #include #include #include #include #include "main.h" extern char *optarg; extern int optind, opterr, optopt; /* Globals */ struct Command_line command_line; int server_socket; /* Globals for the environment of getopt */ static char getopt_env[] = "POSIXLY_CORRECT=YES"; static char *old_getopt_env; static char version[] = "Task Spooler v0.4.1 - a task queue system for the unix user.\n" "Copyright (C) 2007 Lluis Batlle i Rossell"; static void default_command_line() { command_line.request = c_LIST; command_line.need_server = 0; command_line.store_output = 1; command_line.should_go_background = 1; command_line.should_keep_finished = 1; command_line.gzip = 0; command_line.send_output_by_mail = 0; } void get_command(int index, int argc, char **argv) { command_line.command.array = &(argv[index]); command_line.command.num = argc - index; } static int get_two_jobs(const char *str, int *j1, int *j2) { char tmp[50]; char *tmp2; if(strlen(str) >= 50) return 0; strcpy(tmp, str); tmp2 = strchr(tmp , '-'); if (tmp2 == NULL) return 0; /* We change the '-' to '\0', so we have a delimiter, * and we can access the two strings for the ids */ *tmp2 = '\0'; /* Skip the '\0', and point tmp2 to the second id */ ++tmp2; *j1 = atoi(tmp); *j2 = atoi(tmp2); return 1; } void parse_opts(int argc, char **argv) { int c; int res; /* Parse options */ while(1) { c = getopt(argc, argv, ":VhKgClnfmr:t:c:o:p:w:u:s:U:"); if (c == -1) break; switch(c) { case 'K': command_line.request = c_KILL_SERVER; command_line.should_go_background = 0; break; case 'l': command_line.request = c_LIST; break; case 'h': command_line.request = c_SHOW_HELP; break; case 'V': command_line.request = c_SHOW_VERSION; break; case 'C': command_line.request = c_CLEAR_FINISHED; break; case 'c': command_line.request = c_CAT; command_line.jobid = atoi(optarg); break; case 'o': command_line.request = c_SHOW_OUTPUT_FILE; command_line.jobid = atoi(optarg); break; case 'n': command_line.store_output = 0; break; case 'g': command_line.gzip = 1; break; case 'f': command_line.should_go_background = 0; break; case 'm': command_line.send_output_by_mail = 1; break; case 't': command_line.request = c_TAIL; command_line.jobid = atoi(optarg); break; case 'p': command_line.request = c_SHOW_PID; command_line.jobid = atoi(optarg); break; case 'r': command_line.request = c_REMOVEJOB; command_line.jobid = atoi(optarg); break; case 'w': command_line.request = c_WAITJOB; command_line.jobid = atoi(optarg); break; case 'u': command_line.request = c_URGENT; command_line.jobid = atoi(optarg); break; case 's': command_line.request = c_GET_STATE; command_line.jobid = atoi(optarg); break; case 'U': command_line.request = c_SWAP_JOBS; res = get_two_jobs(optarg, &command_line.jobid, &command_line.jobid2); if (!res) { fprintf(stderr, "Wrong for -U.\n"); exit(-1); } if (command_line.jobid == command_line.jobid2) { fprintf(stderr, "Wrong for -U. " "Use different ids.\n"); exit(-1); } break; case ':': switch(optopt) { case 't': command_line.request = c_TAIL; command_line.jobid = -1; /* This means the 'last' job */ break; case 'c': command_line.request = c_CAT; command_line.jobid = -1; /* This means the 'last' job */ break; case 'o': command_line.request = c_SHOW_OUTPUT_FILE; command_line.jobid = -1; /* This means the 'last' job */ break; case 'p': command_line.request = c_SHOW_PID; command_line.jobid = -1; /* This means the 'last' job */ break; case 'r': command_line.request = c_REMOVEJOB; command_line.jobid = -1; /* This means the 'last' added job */ break; case 'w': command_line.request = c_WAITJOB; command_line.jobid = -1; /* This means the 'last' added job */ break; case 'u': command_line.request = c_URGENT; command_line.jobid = -1; /* This means the 'last' added job */ break; case 's': command_line.request = c_GET_STATE; command_line.jobid = -1; /* This means the 'last' added job */ break; default: fprintf(stderr, "Option %c missing argument: %s\n", optopt, optarg); exit(-1); } break; case '?': fprintf(stderr, "Wrong option %c.\n", optopt); exit(-1); } } command_line.command.num = 0; /* if the request is still the default option... * (the default values should be centralized) */ if (optind < argc && command_line.request == c_LIST) { command_line.request = c_QUEUE; get_command(optind, argc, argv); } if (command_line.request != c_SHOW_HELP && command_line.request != c_SHOW_VERSION) command_line.need_server = 1; if ( ! command_line.store_output && ! command_line.should_go_background ) command_line.should_keep_finished = 0; if ( command_line.send_output_by_mail && ((! command_line.store_output) || command_line.gzip) ) { fprintf(stderr, "For e-mail, you should store the output (not through gzip)\n"); exit(-1); } } static void fill_first_3_handles() { int tmp_pipe1[2]; int tmp_pipe2[2]; /* This will fill handles 0 and 1 */ pipe(tmp_pipe1); /* This will fill handles 2 and 3 */ pipe(tmp_pipe2); close(tmp_pipe2[1]); } static void go_background() { int pid; pid = fork(); switch(pid) { case -1: error("fork failed"); break; case 0: close(0); close(1); close(2); /* This is a weird thing. But we will later want to * allocate special files to the 0, 1 or 2 fds. It's * almost impossible, if other important things got * allocated here. */ fill_first_3_handles(); setsid(); break; default: exit(0); } } static void print_help(const char *cmd) { printf("usage: %s [action] [-ngfm] [cmd...]\n", cmd); printf("Env vars:\n"); printf(" TS_SOCKET the path to the unix socket used by the ts command\n"); printf(" TS_MAILTO where to mail the result (on -m). Local user by default\n"); printf(" TS_MAXFINISHED maximum finished jobs in the queue\n"); printf(" TS_ONFINISH binary called on job end (passing jobid, error, outfile, command)\n"); printf("Actions:\n"); printf(" -K kill the task spooler server\n"); printf(" -C clear the list of finished jobs\n"); printf(" -l show the job list (default action)\n"); printf(" -t [id] tail -f the output of the job. Last run if not specified.\n"); printf(" -c [id] cat the output of the job. Last run if not specified.\n"); printf(" -p [id] show the pid of the job. Last run if not specified.\n"); printf(" -o [id] show the output file. Of last job run, if not specified.\n"); printf(" -s [id] show the job state. Of the last added, if not specified.\n"); printf(" -r [id] remove a job. The last added, if not specified.\n"); printf(" -w [id] wait for a job. The last added, if not specified.\n"); printf(" -u [id] put that job first. The last added, if not specified.\n"); printf(" -U swap two jobs in the queue.\n"); printf(" -h show this help\n"); printf(" -V show the program version\n"); printf("Options adding jobs:\n"); printf(" -n don't store the output of the command.\n"); printf(" -g gzip the stored output (if not -n).\n"); printf(" -f don't fork into background.\n"); printf(" -m send the output by e-mail (uses sendmail).\n"); } static void print_version() { puts(version); } static void set_getopt_env() { old_getopt_env = getenv("POSIXLY_CORRECT"); putenv(getopt_env); } static void unset_getopt_env() { if (old_getopt_env == NULL) { /* Wipe the string from the environment */ putenv("POSIXLY_CORRECT"); } else sprintf(getopt_env, "POSIXLY_CORRECT=%s", old_getopt_env); } int main(int argc, char **argv) { int errorlevel = 0; process_type = CLIENT; set_getopt_env(); /* This is needed in a gnu system, so getopt works well */ default_command_line(); parse_opts(argc, argv); unset_getopt_env(); /* This will be inherited by the server, if it's run */ ignore_sigpipe(); if (command_line.need_server) ensure_server_up(); switch(command_line.request) { case c_SHOW_VERSION: print_version(argv[0]); break; case c_SHOW_HELP: print_help(argv[0]); break; case c_QUEUE: if (command_line.command.num <= 0) error("Tried to queue a void command. parameters: %i", command_line.command.num); if (!command_line.need_server) error("The command %i needs the server", command_line.request); c_new_job(); command_line.jobid = c_wait_newjob_ok(); if (command_line.store_output) { printf("%i\n", command_line.jobid); fflush(stdout); } if (command_line.should_go_background) { go_background(); c_wait_server_commands(); } else { errorlevel = c_wait_server_commands(); } break; case c_LIST: if (!command_line.need_server) error("The command %i needs the server", command_line.request); c_list_jobs(); c_wait_server_lines(); break; case c_KILL_SERVER: if (!command_line.need_server) error("The command %i needs the server", command_line.request); c_shutdown_server(); break; case c_CLEAR_FINISHED: if (!command_line.need_server) error("The command %i needs the server", command_line.request); c_clear_finished(); break; case c_TAIL: if (!command_line.need_server) error("The command %i needs the server", command_line.request); c_tail(); /* This will not return! */ break; case c_CAT: if (!command_line.need_server) error("The command %i needs the server", command_line.request); c_cat(); /* This will not return! */ break; case c_SHOW_OUTPUT_FILE: if (!command_line.need_server) error("The command %i needs the server", command_line.request); c_show_output_file(); break; case c_SHOW_PID: if (!command_line.need_server) error("The command %i needs the server", command_line.request); c_show_pid(); break; case c_REMOVEJOB: if (!command_line.need_server) error("The command %i needs the server", command_line.request); c_remove_job(); break; case c_WAITJOB: if (!command_line.need_server) error("The command %i needs the server", command_line.request); errorlevel = c_wait_job(); break; case c_URGENT: if (!command_line.need_server) error("The command %i needs the server", command_line.request); c_move_urgent(); break; case c_SWAP_JOBS: if (!command_line.need_server) error("The command %i needs the server", command_line.request); c_swap_jobs(); break; case c_GET_STATE: if (!command_line.need_server) error("The command %i needs the server", command_line.request); /* This will also print the state into stdout */ c_get_state(); break; } if (command_line.need_server) { close(server_socket); } return errorlevel; }