int main(int argc, char **argv) { #define BUFFER 64 char buffer[BUFFER]; char *level, *app, *server, *output; int run, fd, i, j, c, verbose, attempts, detach, result, truncate, flags; struct katcl_parse *p; struct katcl_line *ls, *lo; struct sigaction sa; time_t now; struct tm *local; i = j = 1; app = argv[0]; verbose = 0; attempts = 2; detach = 0; truncate = 0; server = getenv("KATCP_SERVER"); if(server == NULL){ server = "localhost"; } output = NULL; level = NULL; flags = 0; /* placate -Wall */ while (i < argc) { if (argv[i][0] == '-') { c = argv[i][j]; switch (c) { case 'h' : usage(app); return EX_OK; case 'v' : verbose++; j++; break; case 'd' : detach = 1; j++; break; case 'f' : detach = 0; j++; break; case 't' : truncate = 1; j++; break; case 'q' : verbose = 0; j++; break; case 'l' : case 'o' : case 'a' : case 's' : j++; if (argv[i][j] == '\0') { j = 0; i++; } if (i >= argc) { fprintf(stderr, "%s: usage: argument needs a parameter\n", app); return EX_USAGE; } switch(c){ case 'l' : level = argv[i] + j; break; case 'o' : output = argv[i] + j; break; case 'a' : attempts = atoi(argv[i] + j); break; case 's' : server = argv[i] + j; break; } i++; j = 1; break; case '-' : j++; break; case '\0': j = 1; i++; break; default: fprintf(stderr, "%s: usage: unknown option -%c\n", app, argv[i][j]); return EX_USAGE; } } else { if(output){ fprintf(stderr, "%s: usage: unexpected extra argument %s (can only save to one file)\n", app, argv[i]); return EX_USAGE; } output = argv[i]; i++; } } if(detach){ if(fork_parent() < 0){ fprintf(stderr, "%s: unable to detach process\n", app); return EX_OSERR; } } sa.sa_handler = handle_signal; #if 0 sa.sa_flags = SA_RESTART; #endif sa.sa_flags = 0; sigemptyset(&(sa.sa_mask)); sigaction(SIGHUP, &sa, NULL); sigaction(SIGUSR1, &sa, NULL); sigaction(SIGUSR2, &sa, NULL); if(server == NULL){ server = "localhost:7147"; } if(level){ log_changed = 1; log_level = log_to_code_katcl(level); if(log_level < 0){ fprintf(stderr, "%s: usage: invalid initial log priority %s\n", app, level); return EX_USAGE; } } if(output == NULL){ if(detach == 1){ fprintf(stderr, "%s: usage: need a filename as target\n", app); return EX_USAGE; } fd = STDOUT_FILENO; } else { flags = O_CREAT | O_WRONLY; if(truncate == 0){ flags |= O_APPEND; } fd = open(output, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); if(fd < 0){ fprintf(stderr, "%s: unable to open file %s: %s\n", app, output, strerror(errno)); return EX_OSERR; } } lo = create_katcl(fd); if(lo == NULL){ fprintf(stderr, "%s: unable to allocate log state\n", app); return EX_OSERR; } /**********************/ while((attempts-- > 0) && ((fd = net_connect(server, 0, 0)) < 0)){ sleep(1); } if(attempts <= 0){ sync_message_katcl(lo, KATCP_LEVEL_FATAL, NAME, "unable to connect to %s", server); return EX_UNAVAILABLE; } ls = create_katcl(fd); if(ls == NULL){ sync_message_katcl(lo, KATCP_LEVEL_FATAL, NAME, "unable to allocate parser state"); return EX_OSERR; } if(detach){ fclose(stderr); } time(&now); local = localtime(&now); strftime(buffer, BUFFER - 1, "%Y-%m-%dT%H:%M:%S", local); sync_message_katcl(lo, KATCP_LEVEL_INFO, NAME, "monitor start for %s at %s", server, buffer); for(run = 1; run > 0;){ if(log_reload > 0){ if(output){ fd = open(output, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); if(fd >= 0){ exchange_katcl(lo, fd); } } log_reload = 0; } /* WARNING: will only run after the next message - may have to interrupt syscall to get past this */ if(log_changed > 0){ level = log_to_string_katcl(log_level); if(level){ p = create_parse_katcl(); if(p){ add_string_parse_katcl(p, KATCP_FLAG_STRING | KATCP_FLAG_FIRST, "?log-level"); add_string_parse_katcl(p, KATCP_FLAG_STRING | KATCP_FLAG_LAST, level); append_parse_katcl(lo, p); /* dodgy refcount dealings: p is created with refcount = 0, so can only do write at end, otherwise may end up being deallocated */ append_parse_katcl(ls, p); write_katcl(ls); } } else { sync_message_katcl(lo, KATCP_LEVEL_ERROR, NAME, "invalid log priority number %d", level); } log_changed = 0; } result = read_katcl(ls); if(result < 0){ sync_message_katcl(lo, KATCP_LEVEL_FATAL, NAME, "read from network failed: %s", strerror(errno)); return EX_OSERR; } if(result == 1){ run = 0; } while(have_katcl(ls)){ p = ready_katcl(ls); if(p){ append_parse_katcl(lo, p); } } write_katcl(lo); } return EX_OK; #undef BUFFER }
int main(int argc, char **argv) { char *app, *server, *match, *parm, *tmp, *cmd, *extra, *label; int i, j, c, fd; int verbose, result, status, base, run, info, reply, display, max, prefix, timeout, fmt, pos, flags, munge, show, firm; struct katcl_line *l, *k; fd_set fsr, fsw; struct timeval tv, when, now; label = getenv("KATCP_LABEL"); if(label == NULL){ label = KCPCMD_NAME; } server = getenv("KATCP_SERVER"); if(server == NULL){ server = "localhost"; } firm = 0; info = 1; reply = 1; verbose = 1; i = j = 1; app = argv[0]; base = (-1); timeout = 5; fmt = FMT_TEXT; pos = (-1); k = NULL; munge = 0; show = 1; parm = NULL; while (i < argc) { if (argv[i][0] == '-') { c = argv[i][j]; switch (c) { case 'h' : usage(app); return 0; case 'V' : #ifdef VERSION printf("%s version %s\n", app, VERSION); #else printf("%s version is unknown\n", app); #endif #ifdef __DATE__ printf("%s compiled on %s\n", app, __DATE__); #endif #ifdef KATCP_PROTOCOL_MAJOR_VERSION #ifdef KATCP_PROTOCOL_MINOR_VERSION printf("%s built for katcp %d.%d\n", app, KATCP_PROTOCOL_MAJOR_VERSION, KATCP_PROTOCOL_MINOR_VERSION); #endif #endif return 0; case 'v' : verbose++; j++; break; case 'q' : verbose = 0; info = 0; reply = 0; j++; break; case 'i' : info = 1 - info; j++; break; case 'r' : reply = 1 - reply; j++; break; case 'f' : firm = 1 - firm; j++; break; case 'n' : show = 0; j++; break; case 'k' : k = create_katcl(STDOUT_FILENO); if(k == NULL){ fprintf(stderr, "%s: unable to create katcp message logic\n", app); return 2; } j++; break; case 'a' : fmt = FMT_AUTO; j++; break; case 'x' : fmt = FMT_HEX; j++; break; case 'b' : fmt = FMT_BIN; j++; break; case 'm' : munge = 1; j++; break; case 'l' : case 's' : case 't' : case 'p' : j++; if (argv[i][j] == '\0') { j = 0; i++; } if (i >= argc) { fprintf(stderr, "%s: argument needs a parameter\n", app); return 2; } switch(c){ case 'l' : label = argv[i] + j; break; case 's' : server = argv[i] + j; break; case 't' : timeout = atoi(argv[i] + j); break; case 'p' : pos = atoi(argv[i] + j); if(pos < 0){ fprintf(stderr, "%s: position needs to be nonnegative, not %d\n", app, pos); return 2; } break; } i++; j = 1; break; case '-' : j++; break; case '\0': j = 1; i++; break; default: fprintf(stderr, "%s: unknown option -%c\n", app, argv[i][j]); return 2; } } else { base = i; i = argc; } } if(munge){ if(k){ reply = 1; } } if(base < 0){ if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "no command given"); } fprintf(stderr, "%s: need a command to send (use -h for help)\n", app); return 2; } status = 1; flags = 0; if(verbose > 0){ flags = NETC_VERBOSE_ERRORS; if(verbose > 1){ flags = NETC_VERBOSE_STATS; } } fd = net_connect(server, 0, flags); if(fd < 0){ if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to connect to %s", server); } return 2; } l = create_katcl(fd); if(l == NULL){ if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to create katcp parser"); } fprintf(stderr, "%s: unable to create katcp parser\n", app); return 2; } i = base; match = NULL; flags = ((i + 1) < argc) ? KATCP_FLAG_FIRST : (KATCP_FLAG_FIRST | KATCP_FLAG_LAST); switch(argv[i][0]){ case KATCP_REQUEST : match = argv[i] + 1; /* FALL */ case KATCP_INFORM : case KATCP_REPLY : append_string_katcl(l, flags, argv[i]); break; default : match = argv[i]; append_args_katcl(l, flags, "%c%s", KATCP_REQUEST, argv[i]); break; } i++; while(i < argc){ tmp = argv[i]; i++; flags = (i < argc) ? 0 : KATCP_FLAG_LAST; if(load_arg(l, tmp, fmt, flags) < 0){ if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to load argument %d", i); } fprintf(stderr, "%s: unable to load argument %d\n", app, i); return 2; } } if(match){ for(prefix = 0; (match[prefix] != '\0') && (match[prefix] != ' '); prefix++); #ifdef DEBUG fprintf(stderr, "debug: checking prefix %d of %s\n", prefix, match); #endif } else { prefix = 0; /* pacify -Wall, prefix only used if match is set */ } /* WARNING: logic a bit intricate */ tv.tv_sec = timeout; tv.tv_usec = 0; if(firm){ gettimeofday(&now, NULL); add_time_katcp(&when, &now, &tv); } for(run = 1; run;){ FD_ZERO(&fsr); FD_ZERO(&fsw); if(match){ /* only look for data if we need it */ FD_SET(fd, &fsr); } if(flushing_katcl(l)){ /* only write data if we have some */ FD_SET(fd, &fsw); } if(k){ if(flushing_katcl(k)){ /* only write data if we have some */ FD_SET(STDOUT_FILENO, &fsw); } } result = select(fd + 1, &fsr, &fsw, NULL, &tv); switch(result){ case -1 : switch(errno){ case EAGAIN : case EINTR : continue; /* WARNING */ default : if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "select failed: %s", strerror(errno)); } return 2; } break; case 0 : if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "request %s timed out after %d seconds", argv[base], timeout); } if(verbose){ fprintf(stderr, "%s: no %s within %d seconds\n", app, firm ? "response" : "io activity", timeout); } /* could terminate cleanly here, but ... */ return 2; } if(firm){ gettimeofday(&now, NULL); sub_time_katcp(&tv, &when, &now); } else{ tv.tv_sec = timeout; tv.tv_usec = 0; } if(FD_ISSET(fd, &fsw)){ result = write_katcl(l); if(result < 0){ if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "write failed: %s", strerror(errno)); } fprintf(stderr, "%s: write failed: %s\n", app, strerror(error_katcl(l))); return 2; } if((result > 0) && (match == NULL)){ /* if we finished writing and don't expect a match then quit */ run = 0; } } if(k){ if(FD_ISSET(STDOUT_FILENO, &fsw)){ write_katcl(k); } } if(FD_ISSET(fd, &fsr)){ result = read_katcl(l); if(result){ if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "read failed: %s", (result < 0) ? strerror(error_katcl(l)) : "connection terminated"); } fprintf(stderr, "%s: read failed: %s\n", app, (result < 0) ? strerror(error_katcl(l)) : "connection terminated"); return 2; } } while(have_katcl(l) > 0){ cmd = arg_string_katcl(l, 0); if(cmd){ display = 0; switch(cmd[0]){ case KATCP_INFORM : display = info; if(show == 0){ if(!strcmp(KATCP_VERSION_CONNECT_INFORM, cmd)){ display = 0; } if(!strcmp(KATCP_VERSION_INFORM, cmd)){ display = 0; } if(!strcmp(KATCP_BUILD_STATE_INFORM, cmd)){ display = 0; } } break; case KATCP_REPLY : display = reply; parm = arg_string_katcl(l, 1); if(match){ if(strncmp(match, cmd + 1, prefix) || ((cmd[prefix + 1] != '\0') && (cmd[prefix + 1] != ' '))){ if(k){ sync_message_katcl(k, KATCP_LEVEL_WARN, label, "encountered unexpected reply %s", cmd); } fprintf(stderr, "%s: warning: encountered unexpected reply <%s>\n", app, cmd); } else { if(parm && !strcmp(parm, KATCP_OK)){ status = 0; } run = 0; } } break; case KATCP_REQUEST : if(k){ sync_message_katcl(k, KATCP_LEVEL_WARN, label, "encountered unanswerable request %s", cmd); } fprintf(stderr, "%s: warning: encountered an unanswerable request <%s>\n", app, cmd); break; default : if(k){ sync_message_katcl(k, KATCP_LEVEL_WARN, label, "read malformed message %s", cmd); } fprintf(stderr, "%s: read malformed message <%s>\n", app, cmd); break; } if(display){ #ifdef DEBUG fprintf(stderr, "need to display\n"); #endif if(k){ if(munge && parm && (cmd[0] == KATCP_REPLY)){ if(!strcmp(parm, KATCP_OK)){ sync_message_katcl(k, KATCP_LEVEL_DEBUG, cmd + 1, KATCP_OK); } else { extra = arg_string_katcl(l, 2); sync_message_katcl(k, KATCP_LEVEL_WARN, cmd + 1, "%s (%s)", parm, extra ? extra : "no extra information"); } } else { relay_katcl(l, k); } } else { max = arg_count_katcl(l); if(pos < 0){ for(i = 0; i < max; i++){ if(print_arg(l, i, fmt) < 0){ fprintf(stderr, "%s: failed to print argument %d\n", app, i); return 2; } fputc(((i + 1) == max) ? '\n' : ' ' , stdout); } } else { if(pos < max){ i = pos; if(print_arg(l, i, fmt) < 0){ fprintf(stderr, "%s: failed to print argument %d\n", app, i); return 2; } } } } } } } } destroy_katcl(l, 1); if(k){ while(write_katcl(k) == 0); destroy_katcl(k, 0); } return status; }
int main(int argc, char **argv) { struct state *ss; struct child *cx; fd_set fsr, fsw; char *cmd; int i, j, c, mfd, fd, verbose, result, status; sigset_t mask_current, mask_previous; struct sigaction action_current, action_previous; pid_t pid; ss = create_state(STDOUT_FILENO); if(ss == NULL){ return 4; } #if 0 char *app, *parm, *cmd, *copy, *ptr, *servers; int verbose, result, status, base, info, reply, timeout, pos, flags, show; int xmit, code; unsigned int len; info = 1; reply = 1; i = j = 1; app = argv[0]; base = (-1); timeout = 5; pos = (-1); k = NULL; show = 1; parm = NULL; #endif verbose = 1; i = j = 1; while (i < argc) { if (argv[i][0] == '-') { c = argv[i][j]; switch (c) { case 'h' : usage(argv[0]); return 0; case 'v' : verbose++; j++; break; case 'q' : verbose = 0; j++; break; case '-' : j++; break; case '\0': j = 1; i++; break; default: sync_message_katcl(ss->s_up, KATCP_LEVEL_ERROR, KCPCON_NAME, "unknown option -%c", argv[i][j]); return 2; } } else { #ifdef DEBUG fprintf(stderr, "about to start <%s>\n", argv[i]); #endif if(string_launch_child(ss, argv[i]) < 0){ sync_message_katcl(ss->s_up, KATCP_LEVEL_ERROR, KCPCON_NAME, "unable to start <%s>", argv[i]); return 4; } i++; } } sigprocmask(SIG_SETMASK, NULL, &mask_current); sigaddset(&mask_current, SIGCHLD); action_current.sa_handler = &handle_child; action_current.sa_flags = SA_NOCLDSTOP; sigfillset(&(action_current.sa_mask)); sigaction(SIGCHLD, &action_current, &action_previous); sigprocmask(SIG_SETMASK, &mask_current, &mask_previous); sigemptyset(&mask_current); #if 0 sigaddset(&mask_current, SIGTERM); #endif for(ss->s_finished = 0; ss->s_finished < ss->s_count;){ mfd = 0; FD_ZERO(&fsr); FD_ZERO(&fsw); if(flushing_katcl(ss->s_up)){ mfd = fileno_katcl(ss->s_up); FD_SET(mfd, &fsw); } for(i = 0; i < ss->s_count; i++){ cx = ss->s_vector[i]; if(cx->c_line){ fd = fileno_katcl(cx->c_line); if(fd > mfd){ mfd = fd; } FD_SET(fd, &fsr); } } result = pselect(mfd + 1, &fsr, &fsw, NULL, NULL, &mask_current); #ifdef DEBUG fprintf(stderr, "select returns %d\n", result); #endif switch(result){ case -1 : switch(errno){ case EAGAIN : case EINTR : continue; /* WARNING */ default : sync_message_katcl(ss->s_up, KATCP_LEVEL_ERROR, KCPCON_NAME, "select failed: %s", strerror(errno)); return 4; } break; case 0 : sync_message_katcl(ss->s_up, KATCP_LEVEL_ERROR, KCPCON_NAME, "requests timed out despite having no timeout"); /* could terminate cleanly here, but ... */ return 4; } fd = fileno_katcl(ss->s_up); if(FD_ISSET(fd, &fsw)){ result = write_katcl(ss->s_up); } for(i = 0; i < ss->s_count; i++){ cx = ss->s_vector[i]; if(cx->c_line){ fd = fileno_katcl(cx->c_line); if(FD_ISSET(fd, &fsr)){ /* get things */ result = read_katcl(cx->c_line); if(result){ if(result < 0){ sync_message_katcl(ss->s_up, KATCP_LEVEL_ERROR, KCPCON_NAME, "read failed: %s", strerror(error_katcl(cx->c_line))); } else { if(cx->c_pid > 0){ log_message_katcl(ss->s_up, KATCP_LEVEL_DEBUG, KCPCON_NAME, "subordinate job %u ended", cx->c_pid); } } destroy_katcl(cx->c_line, 1); cx->c_line = NULL; ss->s_finished++; continue; /* WARNING */ } } while(have_katcl(cx->c_line) > 0){ /* compute */ cmd = arg_string_katcl(cx->c_line, 0); if(cmd){ #ifdef DEBUG fprintf(stderr, "reading message <%s ...>\n", cmd); #endif switch(cmd[0]){ case KATCP_INFORM : #if 0 if(!strcmp(KATCP_VERSION_CONNECT_INFORM, cmd)){ } if(!strcmp(KATCP_VERSION_INFORM, cmd)){ } if(!strcmp(KATCP_BUILD_STATE_INFORM, cmd)){ } #endif relay_katcl(cx->c_line, ss->s_up); break; } } } } } /* the position of this logic is rather intricate */ if(got_child_signal){ got_child_signal = 0; while((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0){ for(i = 0; i < ss->s_count; i++){ cx = ss->s_vector[i]; if(cx->c_pid == pid){ if (WIFEXITED(status)) { result = WEXITSTATUS(status); log_message_katcl(ss->s_up, KATCP_LEVEL_DEBUG, KCPCON_NAME, "subordinate job[%u] %u exited with code %d", i, cx->c_pid, result); cx->c_status = (result > 4) ? 4 : result; } else if (WIFSIGNALED(status)) { result = WTERMSIG(status); log_message_katcl(ss->s_up, KATCP_LEVEL_WARN, KCPCON_NAME, "subordinate job[%u] %u killed by signal %d", i, cx->c_pid, result); cx->c_status = 4; } else { log_message_katcl(ss->s_up, KATCP_LEVEL_WARN, KCPCON_NAME, "subordinate job[%u] %u return unexpected status %d", i, cx->c_pid, status); cx->c_status = 4; } if(cx->c_status > ss->s_code){ ss->s_code = cx->c_status; } cx->c_pid = (-1); } } } } } /* force drain */ while(write_katcl(ss->s_up) == 0); result = ss->s_code; destroy_state(ss); /* WARNING: pointlessly fussy */ sigaction(SIGCHLD, &action_previous, NULL); sigprocmask(SIG_BLOCK, &mask_previous, NULL); return result; }