int load_arg(struct katcl_line *l, char *arg, int fmt, int flags) { unsigned char *tmp; int len, i, j, v, result; switch(fmt){ case FMT_BIN : case FMT_TEXT : return append_string_katcl(l, flags, arg); case FMT_AUTO : case FMT_HEX : if(strncmp(arg, "0x", 2)){ return append_string_katcl(l, flags, arg); } else { len = strlen(arg + 2); if(len % 2){ fprintf(stderr, "warning: argument %s contains an odd number of hex characters, ignoring the trailing one\n", arg); len--; } len = len / 2; if(len == 0){ /* NULL parameters are really bad form */ return append_buffer_katcl(l, flags, NULL, 0); } tmp = malloc(len); if(tmp == NULL){ fprintf(stderr, "warning: argument %s contains an odd number of hex characters, ignoring the trailing one\n", arg); return -1; } j = 2; for(i = 0; i < len; i++){ v = h2n(arg[j++]); if(v < 0){ free(tmp); return -1; } tmp[i] = 0xf0 & (v << 4); v = h2n(arg[j++]); if(v < 0){ free(tmp); return -1; } tmp[i] |= 0x0f & v; } result = append_buffer_katcl(l, flags, tmp, len); free(tmp); return result; } break; default : fprintf(stderr, "error: unhandled format %d\n", fmt); return -1; } }
int vextra_response_katcl(struct katcl_line *cl, int code, char *fmt, va_list args) { int result[3]; char *name, *status; if(code > KATCP_RESULT_OK){ return -1; } name = arg_copy_string_katcl(cl, 0); if(name == NULL){ return -1; } name[0] = KATCP_REPLY; status = code_to_name_katcm(code); if(status == NULL){ return -1; } result[0] = append_string_katcl(cl, KATCP_FLAG_FIRST, name); result[1] = append_string_katcl(cl, 0, status); result[2] = append_vargs_katcl(cl, KATCP_FLAG_LAST, fmt, args); return vector_sum(result, 3); }
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 basic_inform_katcl(struct katcl_line *cl, char *name, char *arg) { int result[2]; if(arg){ result[0] = append_string_katcl(cl, KATCP_FLAG_FIRST, name); result[1] = append_string_katcl(cl, KATCP_FLAG_LAST, arg); return vector_sum(result, 2); } else { return append_string_katcl(cl, KATCP_FLAG_FIRST | KATCP_FLAG_LAST, name); } }
int reg_req_wordread(struct katcl_line *l, char *reg) { int ret = 0; if (l == NULL) { fprintf(stderr, "NULL katcl_line pointer"); return -1; } ret += append_string_katcl(l, KATCP_FLAG_FIRST | KATCP_FLAG_STRING, REG_KATCP_WORDREAD); ret += append_string_katcl(l, KATCP_FLAG_LAST | KATCP_FLAG_STRING, reg); return ret; }
int vlog_message_katcl(struct katcl_line *cl, int level, char *name, char *fmt, va_list args) { int result[5]; struct timeval now; unsigned int milli; char *subsystem, *logstring; #ifdef DEBUG if(level >= KATCP_LEVEL_OFF){ fprintf(stderr, "log: bad form to a message of level off or worse\n"); return -1; } #endif logstring = log_to_string_katcl(level); if(logstring == NULL){ #ifdef DEBUG fprintf(stderr, "log: bad log level\n"); abort(); #endif return -1; } subsystem = name ? name : "unknown" ; gettimeofday(&now, NULL); milli = now.tv_usec / 1000; result[0] = append_string_katcl(cl, KATCP_FLAG_FIRST, KATCP_LOG_INFORM); result[1] = append_string_katcl(cl, 0, logstring); result[2] = append_args_katcl(cl, 0, "%lu%03d", now.tv_sec, milli); result[3] = append_string_katcl(cl, 0, subsystem); #if DEBUG > 1 fprintf(stderr, "log: my fmt string is <%s>, milli=%u\n", fmt, milli); #endif result[4] = append_vargs_katcl(cl, KATCP_FLAG_LAST, fmt, args); /* do the right thing, collect error codes */ return vector_sum(result, 5); }
int upload_tbs(struct katcl_line *l, void *data) { struct tbs_port_data *pd; int run, lfd, nfd, rr, wr, have; unsigned char buf[MTU]; if (data == NULL) return -1; pd = data; if (pd == NULL) return -1; lfd = net_listen(NULL, pd->t_port, NETC_VERBOSE_ERRORS | NETC_VERBOSE_STATS); if (lfd < 0){ return -1; } signal(SIGALRM, SIG_DFL); alarm(UPLOAD_TIMEOUT); nfd = accept(lfd, NULL, 0); close(lfd); if (nfd < 0){ return -1; } for (run = 1; run > 0; ){ rr = read(nfd, buf, MTU); if (rr == 0){ run = rr; break; } else if (rr < 0){ sync_message_katcl(l, KATCP_LEVEL_INFO, NULL, "%s: read failed while receiving bof file: %s", __func__, strerror(errno)); append_string_katcl(l, KATCP_FLAG_FIRST | KATCP_FLAG_STRING | KATCP_FLAG_LAST, UPLOADFAIL); while(write_katcl(l) == 0); close(nfd); return -1; } have = 0; do { wr = write(pd->t_fd, buf+have, rr-have); switch(wr){ case -1: switch(errno){ case EAGAIN: case EINTR: break; default: sync_message_katcl(l, KATCP_LEVEL_INFO, NULL, "%s: WRITE FAILED %s", __func__, strerror(errno)); append_string_katcl(l, KATCP_FLAG_FIRST | KATCP_FLAG_STRING | KATCP_FLAG_LAST, UPLOADFAIL); while(write_katcl(l) == 0); close(nfd); return-1; } case 0: sync_message_katcl(l, KATCP_LEVEL_INFO, NULL, "%s: WRITE FAILED %s", __func__, strerror(errno)); append_string_katcl(l, KATCP_FLAG_FIRST | KATCP_FLAG_STRING | KATCP_FLAG_LAST, UPLOADFAIL); while(write_katcl(l) == 0); close(nfd); return-1; default: have += wr; sync_message_katcl(l, KATCP_LEVEL_DEBUG, NULL, "%s: wrote %d bytes to parent", __func__, wr); break; } } while(have < rr); pd->t_rsize += rr; sync_message_katcl(l, KATCP_LEVEL_INFO, NULL, "uploaded %d bytes", pd->t_rsize); alarm(UPLOAD_TIMEOUT); } append_string_katcl(l, KATCP_FLAG_FIRST | KATCP_FLAG_STRING | KATCP_FLAG_LAST, UPLOADCOMPLETE); while(write_katcl(l) == 0); close(nfd); alarm(0); return 0; }
int vsend_katcl(struct katcl_line *l, va_list args) { int flags, result, check; char *string; void *buffer; unsigned long value; int len; #ifdef KATCP_USE_FLOATS double dvalue; #endif check = KATCP_FLAG_FIRST; do{ flags = va_arg(args, int); if((check & flags) != check){ /* WARNING: tests first arg for FLAG_FIRST */ #ifdef DEBUG fprintf(stderr, "vsend: flag check failed 0x%x\n", flags); #endif return -1; } check = 0; switch(flags & KATCP_TYPE_FLAGS){ case KATCP_FLAG_STRING : string = va_arg(args, char *); result = append_string_katcl(l, flags & KATCP_ORDER_FLAGS, string); break; case KATCP_FLAG_SLONG : value = va_arg(args, unsigned long); result = append_signed_long_katcl(l, flags & KATCP_ORDER_FLAGS, value); break; case KATCP_FLAG_ULONG : value = va_arg(args, unsigned long); result = append_unsigned_long_katcl(l, flags & KATCP_ORDER_FLAGS, value); break; case KATCP_FLAG_XLONG : value = va_arg(args, unsigned long); result = append_hex_long_katcl(l, flags & KATCP_ORDER_FLAGS, value); break; case KATCP_FLAG_BUFFER : buffer = va_arg(args, void *); len = va_arg(args, int); result = append_buffer_katcl(l, flags & KATCP_ORDER_FLAGS, buffer, len); break; #ifdef KATCP_USE_FLOATS case KATCP_FLAG_DOUBLE : dvalue = va_arg(args, double); result = append_double_katcl(l, flags & KATCP_ORDER_FLAGS, dvalue); break; #endif default : #ifdef DEBUG fprintf(stderr, "vsend: bad type flag 0x%x\n", flags); #endif result = (-1); break; } #if DEBUG > 1 fprintf(stderr, "vsend: appended: flags=0x%02x, result=%d\n", flags, result); #endif if(result <= 0){ #ifdef DEBUG fprintf(stderr, "vsend: bad result for type 0x%x\n", flags); #endif return -1; } } while(!(flags & KATCP_FLAG_LAST)); return 0; }
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; }