From e6f1e4b041da78716a26c7b622b407c3df1d877a Mon Sep 17 00:00:00 2001 From: thib Date: Sat, 2 Mar 2002 17:23:55 +0000 Subject: [PATCH] made a usable version ... --- fcrondyn.c | 523 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 512 insertions(+), 11 deletions(-) diff --git a/fcrondyn.c b/fcrondyn.c index 41d9d2f..7eea203 100644 --- a/fcrondyn.c +++ b/fcrondyn.c @@ -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 @@ -33,21 +33,33 @@ #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; } -- 2.40.0