int write_word_wops(struct wops_state *w, char *name, uint32_t value) { int result[4], status, i; int expect[4] = { 7, 0, 2, 5 }; char *ptr; uint32_t tmp; if(maintain_wops(w) < 0){ return -1; } tmp = htonl(value); result[0] = append_string_katcl(w->w_line, KATCP_FLAG_FIRST | KATCP_FLAG_STRING, "?write"); result[1] = append_string_katcl(w->w_line, KATCP_FLAG_STRING, name); result[2] = append_unsigned_long_katcl(w->w_line, KATCP_FLAG_ULONG, 0); result[3] = append_buffer_katcl(w->w_line, KATCP_FLAG_LAST | KATCP_FLAG_BUFFER, &tmp, 4); expect[1] = strlen(name) + 1; for(i = 0; i < 4; i++){ if(result[i] != expect[i]){ #ifdef DEBUG fprintf(stderr, "write: result[%d]=%d != %d\n", i, result[i], expect[i]); #endif return -1; } } while((status = complete_rpc_katcl(w->w_line, 0, &(w->w_when))) == 0); if(status < 0){ if(w->w_line){ destroy_rpc_katcl(w->w_line); w->w_line = NULL; } #ifdef DEBUG fprintf(stderr, "write: complete call failed\n"); #endif return -1; } ptr = arg_string_katcl(w->w_line, 1); if(ptr == NULL){ #ifdef DEBUG fprintf(stderr, "write: unable to acquire first parameter\n"); #endif return -1; } if(strcmp(ptr, KATCP_OK)){ #ifdef DEBUG fprintf(stderr, "write: problematic return code %s\n", ptr); #endif return 1; } return 0; }
int read_word_wops(struct wops_state *w, char *name, uint32_t *value) { int result[4], status, i; int expect[4] = { 6, 0, 2, 2 }; char *ptr; uint32_t tmp; if(maintain_wops(w) < 0){ return -1; } result[0] = append_string_katcl(w->w_line, KATCP_FLAG_FIRST | KATCP_FLAG_STRING, "?read"); result[1] = append_string_katcl(w->w_line, KATCP_FLAG_STRING, name); result[2] = append_unsigned_long_katcl(w->w_line, KATCP_FLAG_ULONG, 0); result[3] = append_unsigned_long_katcl(w->w_line, KATCP_FLAG_LAST | KATCP_FLAG_ULONG, 4); expect[1] = strlen(name) + 1; for(i = 0; i < 4; i++){ if(result[i] != expect[i]){ #ifdef DEBUG fprintf(stderr, "read: append[%d]=%d != %d\n", i, result[i], expect[i]); #endif return -1; } } while((status = complete_rpc_katcl(w->w_line, 0, &(w->w_when))) == 0); #ifdef DEBUG fprintf(stderr, "read: status is %d\n", status); #endif if(status < 0){ if(w->w_line){ destroy_rpc_katcl(w->w_line); w->w_line = NULL; } return -1; } ptr = arg_string_katcl(w->w_line, 1); if(ptr == NULL){ return -1; } if(strcmp(ptr, KATCP_OK)){ return 1; } status = arg_buffer_katcl(w->w_line, 2, &tmp, 4); if(status != 4){ return -1; } *value = ntohl(tmp); return 0; }
int main(int argc, char **argv) { struct katcl_line *l; int result, total, i; char *ptr; if(argc <= 2){ fprintf(stderr, "usage: %s server-ip[:server-port] command\n", argv[0]); fprintf(stderr, "example: %s localhost:7147 ?watchdog\n", argv[0]); return 1; } /* connect to a remote machine, arg is "server:port" where ":port is optional" */ l = create_name_rpc_katcl(argv[1]); if(l == NULL){ fprintf(stderr, "unable to create client connection to server %s: %s\n", argv[1], strerror(errno)); return 1; } /* send the request, with 5000ms timeout. Here we don't pass any parameters */ result = send_rpc_katcl(l, 5000, KATCP_FLAG_FIRST | KATCP_FLAG_LAST | KATCP_FLAG_STRING, argv[2], NULL); /* result is 0 if the reply returns "ok", 1 if it failed and -1 if things went wrong doing IO or otherwise */ printf("result of %s request is %d\n", argv[2], result); /* you can examine the content of the reply with the following functions */ total = arg_count_katcl(l); printf("have %d arguments in reply\n", total); for(i = 0; i < total; i++){ /* for binary data use the arg_buffer_katcl, string will stop at the first occurrence of a \0 */ ptr = arg_string_katcl(l, i); printf("reply[%d] is <%s>\n", i, ptr); } destroy_rpc_katcl(l); return 0; }
int main(int argc, char **argv) { struct set *ss; struct remote *rx; struct katcl_parse *px; struct katcl_line *k; struct timeval delta, start, stop; fd_set fsr, fsw; char *app, *parm, *cmd, *copy, *ptr, *servers, *extra, *label; int i, j, c, fd, mfd, count; int verbose, result, status, info, timeout, flags, show, munge, once; int xmit, code; unsigned int len; servers = getenv("KATCP_SERVER"); if(servers == NULL){ servers = "localhost:7147"; } once = 1; munge = 0; info = 1; verbose = 1; i = j = 1; app = argv[0]; timeout = 0; k = NULL; show = 1; parm = NULL; extra = NULL; label = KCPPAR_NAME; count = 0; px = NULL; k = create_katcl(STDOUT_FILENO); if(k == NULL){ fprintf(stderr, "%s: unable to create katcp message logic\n", app); return 4; } ss = create_set(); if(ss == NULL){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to set up command set data structures"); return 4; } xmit = (-1); while (i < argc) { if (argv[i][0] == '-') { c = argv[i][j]; switch (c) { case 'h' : usage(app); return 0; case 'i' : info = 1 - info; j++; break; case 'm' : munge = 1; j++; break; case 'n' : show = 0; j++; break; case 'q' : verbose = 0; j++; break; case 'x' : xmit = 0; j++; break; case 'l' : case 's' : case 't' : j++; if (argv[i][j] == '\0') { j = 0; i++; } if (i >= argc) { sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "argument needs a parameter"); return 2; } switch(c){ case 'l' : label = argv[i] + j; break; case 's' : servers = argv[i] + j; break; case 't' : timeout = atoi(argv[i] + j); break; } i++; j = 1; break; case 'v' : verbose++; j++; break; case '-' : j++; break; case '\0': j = 1; i++; break; default: sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unknown option -%c", argv[i][j]); return 2; } } else { copy = NULL; if(xmit < 0){ /* WARNING: this could make error detection worse */ xmit = 0; } if(xmit == 0){ px = create_referenced_parse_katcl(); if(px == NULL){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to create parse instance for <%s ...>", argv[i]); return 4; } switch(argv[i][0]){ case KATCP_REQUEST : case KATCP_REPLY : case KATCP_INFORM : ptr = argv[i]; break; default : copy = malloc(strlen(argv[i]) + 1); if(copy == NULL){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to allocate temporary storage for %s", argv[i]); return 4; } copy[0] = KATCP_REQUEST; strcpy(copy + 1, argv[i]); ptr = copy; break; } flags = KATCP_FLAG_FIRST; } else { ptr = argv[i]; flags = 0; } i++; j = 1; if((i >= argc) || (argv[i][0] == '-')){ flags |= KATCP_FLAG_LAST; } if(add_string_parse_katcl(px, flags, ptr) < 0){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to add parameter %s", ptr); return 4; } if(flags & KATCP_FLAG_LAST){ #ifdef DEBUG fprintf(stderr, "par: loading command for servers %s\n", servers); #endif if(load_parse_set(ss, servers, px) < 0){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to load command into server set %s", servers); return 4; } destroy_parse_katcl(px); px = NULL; } if(copy){ free(copy); copy = NULL; } xmit++; } } if(timeout == 0){ timeout = 5000 * ss->s_count; } gettimeofday(&start, NULL); delta.tv_sec = timeout / 1000; delta.tv_usec = (timeout % 1000) * 1000; add_time_katcp(&stop, &start, &delta); if(activate_remotes(ss, k) < 0){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to initiate connections to remote servers"); return 3; } for(ss->s_finished = 0; ss->s_finished < ss->s_count;){ mfd = 0; FD_ZERO(&fsr); FD_ZERO(&fsw); if(k){ if(flushing_katcl(k)){ mfd = fileno_katcl(k); FD_SET(mfd, &fsw); } } for(i = 0; i < ss->s_count; i++){ rx = ss->s_vector[i]; if(rx->r_line){ fd = fileno_katcl(rx->r_line); if(fd > mfd){ mfd = fd; } } else { fd = (-1); /* WARNING: live dangerously */ } switch(rx->r_state){ case RX_SETUP : FD_SET(fd, &fsw); break; case RX_UP : if(flushing_katcl(rx->r_line)){ /* only write data if we have some */ FD_SET(fd, &fsw); } FD_SET(fd, &fsr); break; /* case RX_OK : */ /* case RX_FAIL : */ /* case RX_BAD : */ default : break; } } gettimeofday(&start, NULL); sub_time_katcp(&delta, &stop, &start); result = select(mfd + 1, &fsr, &fsw, NULL, &delta); switch(result){ case -1 : switch(errno){ case EAGAIN : case EINTR : continue; /* WARNING */ default : sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "select failed: %s", strerror(errno)); return 4; } break; case 0 : sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "requests timed out after %dms", timeout); /* could terminate cleanly here, but ... */ return 3; } if(k){ fd = fileno_katcl(k); if(FD_ISSET(fd, &fsw)){ write_katcl(k); /* WARNING: ignores write failures - unable to do much about it */ } } for(i = 0; i < ss->s_count; i++){ rx = ss->s_vector[i]; if(rx->r_line){ fd = fileno_katcl(rx->r_line); } else { fd = (-1); /* WARNING: live dangerously, will cause select to core dump if logic is incorrect */ } switch(rx->r_state){ case RX_SETUP : if(FD_ISSET(fd, &fsw)){ len = sizeof(int); result = getsockopt(fd, SOL_SOCKET, SO_ERROR, &code, &len); if(result == 0){ switch(code){ case 0 : if(verbose){ log_message_katcl(k, KATCP_LEVEL_DEBUG, label, "async connect to %s succeeded", rx->r_name); } if(next_request(rx) < 0){ log_message_katcl(k, KATCP_LEVEL_ERROR, label, "failed to load request for destination %s", rx->r_name); update_state(ss, rx, RX_BAD); } else { update_state(ss, rx, RX_UP); } break; case EINPROGRESS : log_message_katcl(k, KATCP_LEVEL_WARN, label, "saw an in progress despite write set being ready on job %s", rx->r_name); break; default : log_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to connect to %s: %s", rx->r_name, strerror(code)); update_state(ss, rx, RX_BAD); break; } } } break; case RX_UP : if(FD_ISSET(fd, &fsw)){ /* flushing things */ result = write_katcl(rx->r_line); if(result < 0){ log_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to write to %s: %s", rx->r_name, strerror(error_katcl(rx->r_line))); update_state(ss, rx, RX_BAD); } } if(FD_ISSET(fd, &fsr)){ /* get things */ result = read_katcl(rx->r_line); if(result){ if(result < 0){ log_message_katcl(k, KATCP_LEVEL_ERROR, label, "read from %s failed: %s", rx->r_name, strerror(error_katcl(rx->r_line))); } else { log_message_katcl(k, KATCP_LEVEL_WARN, label, "%s disconnected", rx->r_name); } } } while(have_katcl(rx->r_line) > 0){ /* compute */ cmd = arg_string_katcl(rx->r_line, 0); if(cmd){ #ifdef DEBUG fprintf(stderr, "reading message <%s ...>\n", cmd); #endif switch(cmd[0]){ case KATCP_INFORM : if(info){ if(show == 0){ if(!strcmp(KATCP_VERSION_CONNECT_INFORM, cmd)){ break; } if(!strcmp(KATCP_VERSION_INFORM, cmd)){ break; } if(!strcmp(KATCP_BUILD_STATE_INFORM, cmd)){ break; } } relay_katcl(rx->r_line, k); } break; case KATCP_REPLY : switch(cmd[1]){ case ' ' : case '\n' : case '\r' : case '\t' : case '\\' : case '\0' : log_message_katcl(k, KATCP_LEVEL_ERROR, label, "unreasonable response message from %s", rx->r_name); update_state(ss, rx, RX_BAD); break; default : ptr = cmd + 1; if(strcmp(ptr, rx->r_match)){ log_message_katcl(k, KATCP_LEVEL_ERROR, label, "downstream %s returned response %s which was never requested", rx->r_name, ptr); update_state(ss, rx, RX_BAD); } else { parm = arg_string_katcl(rx->r_line, 1); if(parm){ if(strcmp(parm, KATCP_OK) == 0){ count++; if(munge){ log_message_katcl(k, KATCP_LEVEL_INFO, label, "%s %s ok", rx->r_name, ptr); } if(verbose > 1){ log_message_katcl(k, KATCP_LEVEL_TRACE, label, "request %s to %s returned ok", ptr, rx->r_name); } result = next_request(rx); if(result){ if(result < 0){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to queue request %s to %s", ptr, rx->r_name); update_state(ss, rx, RX_BAD); } else { update_state(ss, rx, RX_OK); } } } else { if(munge){ extra = arg_string_katcl(rx->r_line, 2); log_message_katcl(k, KATCP_LEVEL_ERROR, label, "%s %s %s (%s)", rx->r_name, ptr, parm, extra ? extra : "no extra information"); } if(verbose > 0){ log_message_katcl(k, KATCP_LEVEL_ERROR, label, "downstream %s unable to process %s with status %s (%s)", rx->r_name, cmd, parm, extra ? extra : "no extra information"); } update_state(ss, rx, RX_FAIL); } } else { log_message_katcl(k, KATCP_LEVEL_ERROR, label, "response %s without status from %s", cmd, rx->r_name); update_state(ss, rx, RX_FAIL); } } break; } break; case KATCP_REQUEST : log_message_katcl(k, KATCP_LEVEL_WARN, label, "encountered unanswerable request %s", cmd); update_state(ss, rx, RX_BAD); break; default : if(once){ log_message_katcl(k, KATCP_LEVEL_WARN, label, "read malformed message %s from %s", cmd, rx->r_name); once = 1; } break; } } } break; /* case RX_OK : */ /* case RX_FAIL : */ /* case RX_BAD : */ default : break; } } } status = ss->s_status; destroy_set(ss); if(verbose){ if(status > 0){ log_message_katcl(k, KATCP_LEVEL_WARN, label, "command sequence failed after operation %d", count); } else { if(count > 0){ log_message_katcl(k, KATCP_LEVEL_INFO, label, "%d operations ok", count); } else { log_message_katcl(k, KATCP_LEVEL_INFO, label, "did nothing successfully"); } } } /* flush, allows us to get away with deferring writes to stdout */ while(write_katcl(k) == 0); destroy_katcl(k, 0); return status; }
int print_arg(struct katcl_line *l, int index, int fmt) { char *ptr, *tmp; unsigned int want, have, safe, i, dfmt; switch(fmt){ case FMT_TEXT : ptr = arg_string_katcl(l, index); if(ptr == NULL){ #ifdef DEBUG fprintf(stderr, "print: unable to acquire argument %d\n", index); #endif return -1; } fputs(ptr, stdout); return 0; case FMT_HEX : case FMT_BIN : case FMT_AUTO : ptr = NULL; want = BUFFER; have = 0; safe = TIMEOUT; do{ tmp = realloc(ptr, want); if(tmp == NULL){ free(ptr); #ifdef DEBUG fprintf(stderr, "print: unable to reallocate %p to %d\n", ptr, want); #endif return -1; } have = want; ptr = tmp; want = arg_buffer_katcl(l, index, ptr, have); safe--; if(!safe){ #ifdef DEBUG fprintf(stderr, "print: safety threshold exceeded, wanted %d\n", want); #endif free(ptr); return -1; } } while(want > have); if(want <= 0){ free(ptr); #ifdef DEBUG fprintf(stderr, "print: argument %d length is %d\n", index, want); #endif return (want < 0) ? -1 : 0; } if(fmt == FMT_AUTO){ dfmt = FMT_TEXT; for(i = 0; i < want; i++){ if(!isprint(ptr[i])){ dfmt = FMT_HEX; } } } else { dfmt = fmt; } switch(dfmt){ case FMT_BIN : case FMT_TEXT : fwrite(ptr, 1, want, stdout); break; case FMT_HEX : fprintf(stdout, "0x"); for(i = 0; i < want; i++){ fprintf(stdout, "%02x", ((unsigned char *)ptr)[i]); } } free(ptr); return 0; default : #ifdef DEBUG fprintf(stderr, "print: unknown format %d\n", fmt); #endif return -1; } }
int main(int argc, char **argv) { char *app, *server, *match, *parm, *tmp, *cmd, *extra; int i, j, c, fd; int verbose, result, status, base, run, info, reply, display, max, prefix, timeout, fmt, pos, flags, munge, show; struct katcl_line *l, *k; fd_set fsr, fsw; struct timeval tv; server = getenv("KATCP_SERVER"); if(server == NULL){ server = "localhost"; } 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; while (i < argc) { if (argv[i][0] == '-') { c = argv[i][j]; switch (c) { case 'h' : usage(app); 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 '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 '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 '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, KCPCMD_NAME, "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, KCPCMD_NAME, "unable to connect to %s", server); } return 2; } l = create_katcl(fd); if(l == NULL){ if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, KCPCMD_NAME, "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, KCPCMD_NAME, "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 */ 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); } tv.tv_sec = timeout; tv.tv_usec = 0; 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, KCPCMD_NAME, "select failed: %s", strerror(errno)); } return 2; } break; case 0 : if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, KCPCMD_NAME, "request timed out after %d seconds", timeout); } if(verbose){ fprintf(stderr, "%s: no io activity within %d seconds\n", app, timeout); } /* could terminate cleanly here, but ... */ return 2; } if(FD_ISSET(fd, &fsw)){ result = write_katcl(l); if(result < 0){ if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, KCPCMD_NAME, "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(FD_ISSET(fd, &fsr)){ result = read_katcl(l); if(result){ if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, KCPCMD_NAME, "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; } } 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, KCPCMD_NAME, "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, KCPCMD_NAME, "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, KCPCMD_NAME, "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; }