]> granicus.if.org Git - fcron/commitdiff
made a usable version ...
authorthib <thib>
Sat, 2 Mar 2002 17:23:55 +0000 (17:23 +0000)
committerthib <thib>
Sat, 2 Mar 2002 17:23:55 +0000 (17:23 +0000)
fcrondyn.c

index 41d9d2f5faef1af08c46e9a75d42b627fd2f315f..7eea203c4af63d0478dd98394e7f472f66fdb24f 100644 (file)
@@ -22,7 +22,7 @@
  *  `LICENSE' that comes with the fcron source distribution.
  */
 
- /* $Id: fcrondyn.c,v 1.1 2002-02-25 18:38:42 thib Exp $ */
+ /* $Id: fcrondyn.c,v 1.2 2002-03-02 17:23:55 thib Exp $ */
 
 /* fcrondyn : interact dynamically with running fcron process :
  *     - list jobs, with their status, next time of execution, etc
 
 #include "fcrondyn.h"
 #include "allow.h"
+#include "read_string.h"
 
-char rcs_info[] = "$Id: fcrondyn.c,v 1.1 2002-02-25 18:38:42 thib Exp $";
+char rcs_info[] = "$Id: fcrondyn.c,v 1.2 2002-03-02 17:23:55 thib Exp $";
 
 void info(void);
 void usage(void);
+void xexit(int exit_val);
+RETSIGTYPE sigpipe_handler(int x);
+int interactive_mode(int fd);
+/* returned by parse_cmd() and/or talk_fcron() */
+#define QUIT_CMD 1
+#define HELP_CMD 2
+#define ZEROLEN_CMD 3
+#define CMD_NOT_FOUND 4
+#define INVALID_ARG 5
+int talk_fcron(char *cmd_str, int fd);
+int parse_cmd(char *cmd_str, long int **cmd, int *cmd_len);
+int connect_fcron(void);
+int authenticate_user(int fd);
 
+/* command line options */
 #ifdef DEBUG
 char debug_opt = 1;       /* set to 1 if we are in debug mode */
 #else
 char debug_opt = 0;       /* set to 1 if we are in debug mode */
 #endif
-
-char *user = NULL;
-uid_t uid = 0;
-uid_t asuid = 0;
+char *cmd_str = NULL;
 
 /* needed by log part : */
 char *prog_name = NULL;
@@ -55,6 +67,34 @@ char foreground = 1;
 char dosyslog = 1;
 pid_t daemon_pid = 0;
 
+/* misc */
+char *user_str;
+uid_t user_uid;
+
+struct cmd_list_ent cmd_list[NUM_CMD] = {
+    /* name, desc, num opt, cmd code, cmd opts, cmd defaults */
+    {"ls_job", "List all jobs of user", 1, CMD_LIST_JOBS,
+     {USER}, {CUR_USER} },
+    {"ls_lavgq", "List jobs of user which are in lavg queue", 1, CMD_LIST_LAVGQ,
+     {USER}, {CUR_USER}},
+    {"ls_serialq", "List jobs of user which are in serial queue", 1, CMD_LIST_SERIALQ,
+     {USER}, {CUR_USER}},
+    {"ls_exeq", "List running jobs of user", 1, CMD_LIST_EXEQ,
+     {USER}, {CUR_USER}},
+    {"detail", "Print details on job", 1, CMD_DETAILS, 
+     {JOBID}, {ARG_REQUIRED}},
+/*    {"reschedule", "Reschedule next execution of job", 2, CMD_RESCHEDULE,
+      {TIME_AND_DATE, JOBID}, {ARG_REQUIRED, ARG_REQUIRED}}, */
+    {"runnow", "Advance next execution of job to now", 1, CMD_RUNNOW,
+     {JOBID}, {ARG_REQUIRED}},
+    {"run", "Run job now (without changing its current schedule)", 1, CMD_RUN,
+     {JOBID}, {ARG_REQUIRED}},
+    {"send_signal", "Send signal to running job", 2, CMD_SEND_SIGNAL,
+     {SIGNAL, JOBID}, {ARG_REQUIRED, ARG_REQUIRED}},
+    {"renice", "Renice running job", 2, CMD_RENICE,
+     {NICE_VALUE, JOBID}, {ARG_REQUIRED, ARG_REQUIRED}}
+};
+
 
 void
 info(void)
@@ -78,23 +118,484 @@ usage(void)
   /*  print a help message about command line options and exit */
 {
     fprintf(stderr, 
-           "fcrondyn [-n] file [user|-u user]\n"
-           "fcrondyn { -l | -r | -e | -z } [-n] [user|-u user]\n"
+           "fcrondyn [-i]\n"
+           "fcrondyn -x 'command'\n"
            "fcrondyn -h\n"
-           "  -u user    specify user name.\n"
-           "  -l         list user's current fcrontab.\n"
-           "  -r         remove user's current fcrontab.\n"
            "  -c f       make fcrontab use config file f.\n"
            "  -d         set up debug mode.\n"
            "  -h         display this help message.\n"
+           "  -V         display version & infos about fcrondyn.\n"
            "\n"
        );
     
     exit(EXIT_ERR);
 }
 
+RETSIGTYPE
+sigpipe_handler(int x)
+    /* handle broken pipes ... */
+{
+    fprintf(stderr, "Broken pipe : check if fcron is still running.\n");
+    fprintf(stderr, "Exiting ...\n");
+
+    xexit(EXIT_ERR);
+}
+
+void
+xexit(int exit_val)
+    /* clean & exit */
+{
+    Flush(cmd_str);
+
+    exit(exit_val);
+}
+
+
+/* used in parse_cmd : */
+#define Write_cmd(data) \
+         memcpy(buf + *cmd_len, (long int *) &data, sizeof(long int)); \
+         *cmd_len += 1;
+
+#define Strncmp(str1, str2, str1_size) \
+          strncmp(str1, str2, (str1_size < strlen(str2)) ? strlen(str2) : str1_size)
+
+int
+parse_cmd(char *cmd_str, long int **cmd, int *cmd_len)
+    /* read a command string, check if it is valid and translate it */
+{
+    long int buf[SOCKET_MSG_LEN];
+    int word_size = 0;
+    int i = 0, rank = -1;
+    long int int_buf = 0;
+    struct passwd *pass = NULL;
+
+    bzero(buf, sizeof(buf));
+    *cmd_len = 0;
+    remove_blanks(cmd_str); /* at the end of the string */
+    
+    if ( (word_size = get_word(&cmd_str)) == 0 ) {
+       fprintf(stderr, "Warning : Zero-length command name : line ignored.\n");
+       return ZEROLEN_CMD;
+    }
+
+    if (Strncmp(cmd_str, "q", word_size) == 0 || Strncmp(cmd_str, "quit", word_size) == 0
+       || Strncmp(cmd_str, "exit", word_size) == 0) {
+       if ( debug_opt ) 
+           fprintf(stderr, "quit command\n");
+       return QUIT_CMD;
+    }
+
+    if( Strncmp(cmd_str, "h", word_size)==0 || Strncmp(cmd_str, "help", word_size)==0 ) {
+       if ( debug_opt ) 
+           fprintf(stderr, "help command\n");
+       return HELP_CMD;
+    }
+
+    for (i = 0; i < NUM_CMD; i++) {
+       if ( Strncmp(cmd_str, cmd_list[i].cmd_name, word_size) == 0 ) {
+           rank = i;
+           break;
+       }
+    }
+    if ( rank == (-1) ) {
+       fprintf(stderr, "Error : Command not found.\n");
+       return CMD_NOT_FOUND;
+    }
+    Write_cmd(cmd_list[rank].cmd_code);
+
+    if ( debug_opt )
+       fprintf(stderr, "command : %s\n", cmd_list[i].cmd_name);
+
+    cmd_str += word_size;
+    for (i = 0 ; i < cmd_list[rank].cmd_numopt; i++) {
+
+       if ( (word_size = get_word(&cmd_str)) == 0 ) {
+
+           if (cmd_list[rank].cmd_default[i] == ARG_REQUIRED) {
+               fprintf(stderr, "Error : arg required !\n");
+               return INVALID_ARG;
+           }
+
+           /* use default value : currently, works only with CUR_USER */
+           if ( user_uid == ROOTUID ) {
+               /* default for root = all */
+               int_buf = ALL;
+               Write_cmd( int_buf );
+               if ( debug_opt )
+                   fprintf(stderr, "  uid = ALL\n");
+           }
+           else {
+               Write_cmd( user_uid );
+               if ( debug_opt )
+                   fprintf(stderr, "  uid = %d\n", user_uid);
+           }
+
+       }
+
+       else {
+           
+           /* get value from line ... */
+           switch (cmd_list[rank].cmd_opt[i]) {
+
+           case USER:
+               int_buf = (long int) *(cmd_str + word_size);
+               *(cmd_str + word_size) = '\0';
+               if ( (pass = getpwnam(cmd_str) ) == NULL ) {
+                   fprintf(stderr, "Error : '%s' is not a valid user name.\n", cmd_str);
+                   return INVALID_ARG;
+               }
+               *(cmd_str + word_size) = (char) int_buf;
+               cmd_str += word_size;
+               Write_cmd(pass->pw_uid);
+               if ( debug_opt )
+                   fprintf(stderr, "  uid = %d\n", pass->pw_uid);
+               break;
+
+           case JOBID:
+               /* after strtol(), cmd_str will be updated (first non-number char) */
+               if ( (int_buf = strtol(cmd_str, &cmd_str, 10)) < 0 || int_buf >= LONG_MAX
+                    || (! isspace( (int) *cmd_str) && *cmd_str != '\0') ) {
+                   fprintf(stderr, "Error : invalid jobid.\n");
+                   return INVALID_ARG;
+               }
+               Write_cmd(int_buf);
+               if ( debug_opt )
+                   fprintf(stderr, "  jobid = %ld\n", int_buf);
+               break;
+
+           case TIME_AND_DATE:
+               /* argghh !!! no standard function ! */
+               break;
+
+           case NICE_VALUE:
+               /* after strtol(), cmd_str will be updated (first non-number char) */
+               if ( (int_buf = strtol(cmd_str, &cmd_str, 10)) > 20 
+                    || (int_buf < 0 && getuid() != ROOTUID) || int_buf < -20
+                    || (! isspace( (int) *cmd_str) && *cmd_str != '\0') ) {
+                   fprintf(stderr, "Error : invalid nice value.\n");
+                   return INVALID_ARG;
+               }
+               Write_cmd(int_buf);
+               if ( debug_opt )
+                   fprintf(stderr, "  nicevalue = %ld\n", int_buf);
+               break;
+
+           case SIGNAL:
+               if ( isalpha( (int) *cmd_str ) ) {
+                   if ( Strncmp(cmd_str, "HUP", word_size) == 0 ) int_buf = SIGHUP;
+                   else if (Strncmp(cmd_str, "INT", word_size) == 0) int_buf = SIGINT;
+                   else if (Strncmp(cmd_str, "QUIT", word_size) == 0) int_buf = SIGQUIT;
+                   else if (Strncmp(cmd_str, "KILL", word_size) == 0) int_buf = SIGKILL;
+                   else if (Strncmp(cmd_str, "ALRM", word_size) == 0) int_buf = SIGALRM;
+                   else if (Strncmp(cmd_str, "TERM", word_size) == 0) int_buf = SIGTERM;
+                   else if (Strncmp(cmd_str, "USR1", word_size) == 0) int_buf = SIGUSR1;
+                   else if (Strncmp(cmd_str, "USR2", word_size) == 0) int_buf = SIGUSR2;
+                   else if (Strncmp(cmd_str, "CONT", word_size) == 0) int_buf = SIGCONT;
+                   else if (Strncmp(cmd_str, "STOP", word_size) == 0) int_buf = SIGSTOP;
+                   else if (Strncmp(cmd_str, "TSTP", word_size) == 0) int_buf = SIGTSTP;
+                   else {
+                       fprintf(stderr, "Error : unknow signal (try integer value)\n");
+                       return INVALID_ARG;
+                   }
+                   cmd_str += word_size;
+               }
+               /* after strtol(), cmd_str will be updated (first non-number char) */
+               else if((int_buf=strtol(cmd_str, &cmd_str, 10)) < 0 || int_buf>=LONG_MAX
+                    || (! isspace( (int) *cmd_str) && *cmd_str != '\0') ) {
+                   fprintf(stderr, "Error : invalid signal value.\n");
+                   return INVALID_ARG;
+               }
+               Write_cmd(int_buf);
+               if ( debug_opt )
+                   fprintf(stderr, "  signal = %ld\n", int_buf);
+               break;
+
+           default:
+               fprintf(stderr, "Error : Unknown arg !");
+               return INVALID_ARG;
+           }
+       }
+    }
+
+    Skip_blanks(cmd_str);
+    if ( *cmd_str != '\0' )
+       fprintf(stderr, "Warning : too much arguments : '%s' ignored.\n", cmd_str);
+
+    /* This is a valid command ... */
+    if ( (*cmd = calloc(*cmd_len, sizeof(long int))) == NULL )
+       die_e("Could not calloc.");
+
+    memcpy(*cmd, buf, *cmd_len * sizeof(long int));
+
+    return OK;
+}
+
+int
+authenticate_user(int fd)
+    /* authenticate user */
+{
+    char *password = NULL;
+    char buf[USER_NAME_LEN + 16];
+    int len = 0;
+
+    snprintf(buf, sizeof(buf), "password for %s :", user_str);
+    if ( (password = read_string(CONV_ECHO_OFF, buf)) == NULL )
+       return ERR;
+
+    len = snprintf(buf, sizeof(buf), "%s", user_str) + 1;
+    len += snprintf(buf + len, sizeof(buf) - len, "%s", password) + 1;
+    send(fd, buf, len, 0);
+    Overwrite(buf);
+    Overwrite(password);
+    free(password);
+    recv(fd, buf, sizeof(buf), 0);
+    if ( strncmp(buf, "1", sizeof("1")) != 0 )
+       return ERR;
+
+    return OK;
+}
+
+
+int
+connect_fcron(void)
+    /* connect to fcron through a socket, and identify user */
+{
+    int fd = -1;
+    struct sockaddr_un addr;
+    int len = 0;
+
+    if ( (fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1 )
+       die_e("could not create socket");
+
+    addr.sun_family = AF_UNIX;
+    if ( (len = strlen(fifofile)) > sizeof(addr.sun_path) )
+       die("Error : fifo file path too long (max is %d)", sizeof(addr.sun_path));
+    strncpy(addr.sun_path, fifofile, sizeof(addr.sun_path));
+
+    if ( connect(fd, (struct sockaddr *) &addr, sizeof(addr.sun_family) + len) < 0 )
+       die_e("Cannot connect() to fcron (check if fcron is running)");
+
+    /* */
+    //if ( fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) == -1 )
+    //error_e("Could not set fd attribute O_NONBLOCK");
+    /* */
+
+    if ( authenticate_user(fd) == ERR )
+       die("Unable to authenticate user");
+
+    return fd;
+
+}
+
+int
+talk_fcron(char *cmd_str, int fd)
+    /* read a string command, check if it is valid and translate it,
+     * send it to fcron, and print its answer */
+{
+    long int *cmd = NULL;
+    int cmd_len = 0;
+    char buf[LINE_LEN];
+    size_t read_len = 0;
+    char existing_connection = (fd < 0) ? 0 : 1;
+
+    switch ( parse_cmd(cmd_str, &cmd, &cmd_len) ) {
+    case OK:
+       break;
+    case HELP_CMD:
+    {
+       int i, j;
+       printf("Command recognized by fcrondyn :\n");
+       printf("------------------------------\n");
+       for (i = 0; i < NUM_CMD; i++) {
+           printf("%s ", cmd_list[i].cmd_name);
+
+           /* print args : */
+           for (j = 0; j < cmd_list[i].cmd_numopt; j++) {
+               if ( cmd_list[i].cmd_default[j] != ARG_REQUIRED )
+                   printf("[");
+               switch ( cmd_list[i].cmd_opt[j] ) {
+               case USER: printf("user"); break;
+               case JOBID: printf("jobid"); break;
+               case TIME_AND_DATE: printf("time"); break;
+               case NICE_VALUE: printf("niceval"); break;
+               case SIGNAL: printf("sig"); break;
+               case BOOLEAN: printf("bool"); break;
+               default: printf("unknown_arg!");
+               }
+               if ( cmd_list[i].cmd_default[j] != ARG_REQUIRED )
+                   printf("]");
+               printf(" ");
+           }
+
+           if (cmd_list[i].cmd_numopt + (strlen(cmd_list[i].cmd_name)/8) <= 1.5)
+               printf("\t\t%s\n", cmd_list[i].cmd_desc);
+           else
+               printf("\t%s\n", cmd_list[i].cmd_desc);
+       }
+    }
+       return HELP_CMD;
+    case QUIT_CMD:
+       return QUIT_CMD;
+    case CMD_NOT_FOUND:
+       return CMD_NOT_FOUND;
+    case INVALID_ARG:
+       return INVALID_ARG;
+    case ZEROLEN_CMD:
+       return ZEROLEN_CMD;
+    default:
+       return ERR;
+    }
+
+    /* This is a valid command (so we'll have to free() it) ... */
+
+    if ( ! existing_connection && (fd = connect_fcron()) == ERR )
+       return ERR;
+
+    send(fd, cmd, cmd_len * sizeof(long int), 0);
+    Flush(cmd);
+    cmd_len = 0;
+
+    while ( (read_len = recv(fd, buf, sizeof(buf), 0)) > 0 && read_len < sizeof(buf) ) {
+       write(STDOUT_FILENO, buf, read_len);
+       if ( read_len > sizeof(END_STR) &&
+            strncmp( &buf[read_len - sizeof(END_STR)], END_STR, sizeof(END_STR)) == 0 )
+           break;
+    }
+       
+
+    if ( ! existing_connection )
+       close(fd);
+
+    return OK;
+}
+
+
+int
+interactive_mode(int fd)
+    /* provide user a prompt, read command, send it to fcron, print its answer,
+     * then give another prompt, etc, until user type an exit command */
+{
+    char existing_connection = (fd < 0) ? 0 : 1;
+    int return_code = 0;
+    char buf[LINE_LEN];
+
+    if ( ! existing_connection && (fd = connect_fcron()) == ERR )
+       return ERR;
+
+    while (fprintf(stderr, "fcrondyn> ") && fgets(buf, sizeof(buf), stdin) != NULL 
+          && (return_code = talk_fcron(buf, fd)) != QUIT_CMD) ;
+
+    if ( ! existing_connection )
+       close(fd);
+
+    return OK;
+}
+
+
+void
+parseopt(int argc, char *argv[])
+  /* set options */
+{
+
+    int c, i;
+    extern char *optarg;
+    extern int optind, opterr, optopt;
+
+    /* constants and variables defined by command line */
+
+    while(1) {
+       c = getopt(argc, argv, "hVdc:ix:");
+       if (c == EOF) break;
+       switch (c) {
+
+       case 'V':
+           info(); break;
+
+       case 'h':
+           usage(); break;
+
+       case 'd':
+           debug_opt = 1; break;
+
+       case 'c':
+           Set(fcronconf, optarg);
+           break;
+
+       case 'i':
+           Flush(cmd_str);
+           break;
+
+       case 'x':
+           Set(cmd_str, optarg);
+           break;
+
+       case ':':
+           fprintf(stderr, "(setopt) Missing parameter.\n");
+           usage();
+
+       case '?':
+           usage();
+
+       default:
+           fprintf(stderr, "(setopt) Warning: getopt returned %c.\n", c);
+       }
+
+    }
+
+    if (optind < argc) {
+       for (i = optind; i <= argc; i++)
+           fprintf(stderr, "Unknown argument \"%s\"", argv[i]);
+       usage();
+    }
+}
+
+
 int
 main(int argc, char **argv)
 {
+    int return_code = 0;
+    int fd = (-1);
+    struct passwd *pass = NULL;
+
+    if ( strrchr(argv[0], '/') == NULL) prog_name = argv[0];
+    else prog_name = strrchr(argv[0], '/') + 1;
+
+    /* interpret command line options */
+    parseopt(argc, argv);
+
+/*      if (debug_opt) */
+/*     fprintf(stderr, "uid : %d euid : %d\n", getuid(), geteuid()); */
+
+    /* read fcron.conf and update global parameters */
+    read_conf();
+
+    user_uid = getuid();
+    if ( (pass = getpwuid(user_uid)) == NULL )
+       die("user \"%s\" is not in passwd file. Aborting.", USERNAME);
+    user_str = strdup2(pass->pw_name);
+
+    /* we don't need anymore special rights : drop them */
+    seteuid(user_uid);
+    setuid(user_uid);
+/*      if (debug_opt) */
+/*     fprintf(stderr, "uid : %d euid : %d\n", getuid(), geteuid()); */
+
+    if ( ! is_allowed(user_str) ) {
+       die("User \"%s\" is not allowed to use %s. Aborting.",
+           user_str, prog_name);           
+    }
+
+    /* check for broken pipes ... */
+    signal(SIGPIPE, sigpipe_handler);
+
+    if ( cmd_str == NULL )
+       return_code = interactive_mode(fd);
+    else
+       return_code = talk_fcron(cmd_str, fd);
+
+    xexit( (return_code == OK ) ? EXIT_OK : EXIT_ERR );
+
+    /* never reached */
+    return EXIT_OK;
 
 }