Exemple #1
0
void exchange_katcl(struct katcl_line *l, int fd)
{
  sane_line_katcl(l);

  /* WARNING: exchanging fds forces discarding of pending io */

  if(l->l_fd >= 0){
    close(l->l_fd);
  }
  l->l_fd = fd;

  if(l->l_ready){
    destroy_parse_katcl(l->l_ready);
    l->l_ready = NULL;
  }

  if(l->l_next){
    l->l_next = reuse_parse_katcl(l->l_next);
  }

  if(l->l_stage){
    destroy_parse_katcl(l->l_stage);
    l->l_stage = NULL;
  }

  l->l_pending = 0;
  l->l_arg = 0;
  l->l_offset = 0;

  clear_queue_katcl(l->l_queue);

  l->l_error = 0;
  l->l_sendable = 1;
}
Exemple #2
0
int monitor_event_variable_katcp(struct katcp_dispatch *d, struct katcp_vrbl *vx, struct katcp_flat *fx)
{
  struct katcp_subscribe *sub;
  struct katcl_parse *px;

  px = make_sensor_katcp(d, NULL, vx, KATCP_SENSOR_STATUS_INFORM);
  if(px == NULL){
    return -1;
  }

  sub = locate_subscribe_katcp(d, vx, fx);
  if(sub == NULL){
    sub = attach_variable_katcp(d, vx, fx);
    if(sub == NULL){
      destroy_parse_katcl(px);
      return -1;
    }
  }

  sub->s_strategy = KATCP_STRATEGY_EVENT;

  append_parse_katcp(d, px);
  destroy_parse_katcl(px);

  return 0;
}
Exemple #3
0
struct katcl_parse *make_sensor_katcp(struct katcp_dispatch *d, char *name, struct katcp_vrbl *vx, char *prefix)
{
  struct katcl_parse *px;

#ifdef DEBUG
  fprintf(stderr, "sensor: triggering sensor change logic on sensor %s\n", name);
#endif

  px = create_referenced_parse_katcl();
  if(px == NULL){
    return NULL;
  }

  if(add_string_parse_katcl(px, KATCP_FLAG_FIRST | KATCP_FLAG_STRING, prefix) < 0){
    destroy_parse_katcl(px);
    return NULL;
  }

  if(add_partial_sensor_katcp(d, px, name, KATCP_FLAG_LAST, vx) < 0){
    destroy_parse_katcl(px);
    return NULL;
  }

  return px;
}
Exemple #4
0
int change_sensor_katcp(struct katcp_dispatch *d, void *state, char *name, struct katcp_vrbl *vx)
{
  struct katcp_wit *w;
  struct katcl_parse *px;

  w = state;
  sane_wit(w);

#ifdef DEBUG
  fprintf(stderr, "sensor: triggering sensor change logic on sensor %s\n", name);
#endif

  if(vx->v_flags & KATCP_VRF_HID){
    return 0;
  }

  px = make_sensor_katcp(d, name, vx, KATCP_SENSOR_STATUS_INFORM);
  if(px == NULL){
    return -1;
  }

  broadcast_subscribe_katcp(d, w, px);

  destroy_parse_katcl(px);

  return 0;
}
Exemple #5
0
void void_destroy_parse_katcl(void *v)
{
  struct katcl_parse *px;

  px = v;

  destroy_parse_katcl(px);
}
Exemple #6
0
void destroy_katcl(struct katcl_line *l, int end)
{
  if(l == NULL){
    return;
  }

  /* in */

  if(l->l_ready){
    destroy_parse_katcl(l->l_ready);
    l->l_ready = NULL;
  }
  if(l->l_next){
    destroy_parse_katcl(l->l_next);
    l->l_next = NULL;
  }

  /* out */

  if(l->l_stage){
    destroy_parse_katcl(l->l_stage);
    l->l_stage = NULL;
  }

  l->l_pending = 0;
  l->l_arg = 0;
  l->l_offset = 0;

  if(l->l_queue){
    destroy_queue_katcl(l->l_queue);
    l->l_queue = NULL;
  }

  if(end){
    if(l->l_fd >= 0){
      close(l->l_fd);
      l->l_fd = (-1);
    }
  }

  l->l_error = EINVAL;
  l->l_sendable = 0;

  free(l);
}
Exemple #7
0
static int after_append_katcl(struct katcl_line *l, int flags, int result)
{
  if(result < 0){ /* things went wrong, throw away the entire line */
#ifdef DEBUG
    fprintf(stderr, "append failed, discarding stage\n");
#endif
    if(l->l_stage){
      destroy_parse_katcl(l->l_stage);
      l->l_stage = NULL;
    }
    return result;
  }

  if(!(flags & KATCP_FLAG_LAST)){
#if DEBUG > 1
    fprintf(stderr, "after append: flag not last, not doing anything\n");
#endif
    return result;
  }

  /* probably redundant, before_append_katcl needs to check for this too */
  if(l->l_stage == NULL){
#ifdef DEBUG
    fprintf(stderr, "after append: no stage variable\n");
#endif
    return -1;
  }

  add_tail_queue_katcl(l->l_queue, l->l_stage);
  
  destroy_parse_katcl(l->l_stage);
  l->l_stage = NULL;

#ifdef KATCP_FLUSH_THRESHOLD
  if(size_queue_katcl(l->l_queue) > KATCP_FLUSH_THRESHOLD){
    /* WARNING: ignores return code */
    write_katcl(l);
  }
#endif

  return result;
}
int perform_sensor_update_katcp(struct katcp_dispatch *d, void *data)
{
  struct katcp_shared *s;
  unsigned int i, j, count;
  struct katcp_flat *fx;
  struct katcp_group *gx;
  struct katcl_parse *px;

  s = d->d_shared;
  if(s == NULL){
    return -1;
  }

  if(s->s_changes <= 0){
    log_message_katcp(d, KATCP_LEVEL_WARN, NULL, "logic problem: scheduled device update, but nothing requires updating");
    return -1;
  }

  px = create_referenced_parse_katcl();
  if(px == NULL){
    return -1;
  }

  add_string_parse_katcl(px, KATCP_FLAG_FIRST | KATCP_FLAG_STRING, KATCP_DEVICE_CHANGED_INFORM);
  add_string_parse_katcl(px, KATCP_FLAG_LAST | KATCP_FLAG_STRING, "sensor-list");

  count = 0;

  for(j = 0; j < s->s_members; j++){
    gx = s->s_groups[j];
    for(i = 0; i < gx->g_count; i++){
      fx = is_ready_flat_katcp(d, gx->g_flats[i]);
      if(fx){
        if((fx->f_stale & KATCP_STALE_MASK_SENSOR) == KATCP_STALE_SENSOR_STALE){
          fx->f_stale = KATCP_STALE_SENSOR_NAIVE;

          if((fx->f_flags & KATCP_FLAT_TOCLIENT) && (fx->f_flags & KATCP_FLAT_SEESKATCP)){
            /* TODO: shouldn't we use the fancy queue infrastructure ? */
            append_parse_katcl(fx->f_line, px);
            count++;
          }
        }
      }
    }
  }

  log_message_katcp(d, KATCP_LEVEL_DEBUG, NULL, "notified %u clients of %u sensor %s", count, s->s_changes, (s->s_changes == 1) ? "change" : "changes");

  destroy_parse_katcl(px);

  s->s_changes = 0;

  return 0;
}
Exemple #9
0
void clear_katcl(struct katcl_line *l) /* discard a full line */
{
  if(l->l_ready == NULL){
#ifdef DEBUG
    fprintf(stderr, "unusual: clearing something which is empty\n");
#endif
    return;
  }

  destroy_parse_katcl(l->l_ready);
  l->l_ready = NULL;
}
Exemple #10
0
void destroy_remote(struct remote *rx)
{
  unsigned int i;

  if(rx == NULL){
    return;
  }

  if(rx->r_name){
    free(rx->r_name);
    rx->r_name = NULL;
  }

  if(rx->r_line){
    destroy_katcl(rx->r_line, 1);
    rx->r_line = NULL;
  }

  rx->r_index = 0;
  rx->r_state = RX_BAD;

  rx->r_match = NULL;

  if(rx->r_vector){
    for(i = 0; i < rx->r_count; i++){
      if(rx->r_vector[i]){
        destroy_parse_katcl(rx->r_vector[i]);
        rx->r_vector[i] = NULL;
      }
    }
    free(rx->r_vector);
    rx->r_vector = NULL;
  }
  rx->r_count = 0;

  free(rx);
}
Exemple #11
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;
}
Exemple #12
0
int write_katcl(struct katcl_line *l)
{
  int wr;
  int state;
  unsigned int space, want, can, actual;
  struct katcl_parse *p;
  struct katcl_larg *la;
#define TMP_MARGIN 32

  p = NULL;

  for(state = WRITE_STATE_FILL; state != WRITE_STATE_DONE; ){
#if DEBUG > 1
    fprintf(stderr, "write: state=%d, parse=%p, arg=%u, offset=%u, pending=%u\n", state, p, l->l_arg, l->l_offset, l->l_pending);
#endif
    switch(state){

      case WRITE_STATE_FILL : 

        p = get_head_queue_katcl(l->l_queue);
        if(p == NULL){
          state = (l->l_pending > 0) ? WRITE_STATE_SEND : WRITE_STATE_DONE;
          continue; /* WARNING */
        } 

#ifdef KATCP_CONSISTENCY_CHECKS
        if(p->p_magic != KATCL_PARSE_MAGIC){
          fprintf(stderr, "write: bad magic returned from get_head (%x, expected %x)\n", p->p_magic, KATCL_PARSE_MAGIC);
          abort();
        }
#endif

        if((l->l_pending + TMP_MARGIN) > KATCL_IO_SIZE){
          state = WRITE_STATE_SEND; /* drain if no margin */
          continue; /* WARNING */
        }

#ifdef KATCP_CONSISTENCY_CHECKS
        if(l->l_arg >= p->p_got){
          fprintf(stderr, "write: logic problem: arg=%u >= got=%u\n", l->l_arg, p->p_got);
          abort();
        }
#endif

        la = &(p->p_args[l->l_arg]);

#ifdef KATCP_CONSISTENCY_CHECKS
        if((la->a_begin + l->l_offset) > la->a_end){
          fprintf(stderr, "write: logic problem: offset=%u extends beyond argument %u (%u-%u)\n", l->l_offset, l->l_arg, la->a_begin, la->a_end);
          abort();
        }
#endif

        if((la->a_begin + l->l_offset) >= la->a_end){ /* done ? */
          if(l->l_offset == 0){ /* special case - null arg */
#ifdef KATCP_CONSISTENCY_CHECKS
            if(l->l_arg == 0){
              fprintf(stderr, "write: problem - arg0 is null\n");
              abort();
            }
#endif
            strcpy(l->l_buffer + l->l_pending, "\\@");
            l->l_pending += 2;
          }
          if(la->a_escape <= 1){ /* mark things which were thought to need escaping, but did not appropriately */
            la->a_escape = 0;
          }

          l->l_arg++;
          l->l_offset = 0;
          state = WRITE_STATE_NEXT;

          continue;
        }

        want = la->a_end - (la->a_begin + l->l_offset);
        space = KATCL_IO_SIZE - (l->l_pending + 1);

#if DEBUG>1
        fprintf(stderr, "write: arg[%u] has %u more, space is %u\n", l->l_arg, want, space);
#endif

        if(la->a_escape){
          can = ((space / 2) >= want) ? want : space / 2;
          actual = escape_copy_katcl(l->l_buffer + l->l_pending, p->p_buffer + la->a_begin + l->l_offset, can);
          if(actual > can){
            la->a_escape = 2; /* record that we needed to escape */
          }
          l->l_pending += actual;
        } else {
          can = (space >= want) ? want : space;
          memcpy(l->l_buffer + l->l_pending, p->p_buffer + la->a_begin + l->l_offset, can);
          l->l_pending += can;
        }
        l->l_offset += can;

        break;

      case WRITE_STATE_NEXT : /* move onto next */
        /* gets a proposed position, updates to next parse or end if needed, adding separators and tags */

        p = get_head_queue_katcl(l->l_queue);
        if(p == NULL){
          l->l_offset = 0;
          l->l_arg = 0;
          state = WRITE_STATE_SEND;
          continue;
        }

#ifdef KATCP_CONSISTENCY_CHECKS
        if(p->p_magic != KATCL_PARSE_MAGIC){
          fprintf(stderr, "write: bad magic returned from get_head (%x, expected %x)\n", p->p_magic, KATCL_PARSE_MAGIC);
          abort();
        }
#endif

        if(l->l_pending >= KATCL_IO_SIZE){
          fprintf(stderr, "write: logic problem: pending=%u, no space left\n", l->l_pending);
        }
      
        if((p->p_tag >= 0) && (l->l_arg == 1)){
          /* !#$ : TODO: enter tag printing state */
        }

        if(l->l_arg < p->p_got){ /* more args */
          state = WRITE_STATE_FILL;
          l->l_buffer[l->l_pending++] = ' ';
          continue;
        }

        l->l_arg = 0;
        l->l_buffer[l->l_pending++] = '\n';

#if DEBUG > 1
        fprintf(stderr, "write: wrote out parse %p (refs %d)\n", p, p->p_refs);
#endif

        p = remove_head_queue_katcl(l->l_queue);
        destroy_parse_katcl(p);
        state = p ? WRITE_STATE_FILL : WRITE_STATE_SEND;

        break;

      case WRITE_STATE_SEND : /* do io */

        p = get_head_queue_katcl(l->l_queue);
        if(l->l_pending <= 0){ /* nothing more to write ? */
          state = p ? WRITE_STATE_FILL : WRITE_STATE_DONE;
          continue; /* WARNING: added later, attempt to remove write(,,0) cases */
        }

        if(l->l_sendable){
#ifdef MSG_NOSIGNAL
          wr = send(l->l_fd, l->l_buffer, l->l_pending, MSG_DONTWAIT | MSG_NOSIGNAL);
#else
          wr = send(l->l_fd, l->l_buffer, l->l_pending, MSG_DONTWAIT);
#endif
        } else {
          wr = write(l->l_fd, l->l_buffer, l->l_pending);
        }

        if(wr < 0){
          switch(errno){
            case EAGAIN :
            case EINTR  :
              return 0; /* returns zero if still more to do */
            case ENOTSOCK :
              if(l->l_sendable > 0){
                l->l_sendable = 0; /* try again, this time with write() not send() */
                continue; /* WARNING, restart for();  */
              }
              /* WARNING: drop through */
            default :
              l->l_error = errno;
              return -1;
          }
        }

        if(wr > 0){
          if(wr < l->l_pending){ /* a tad expensive, but we expect this to be rare */
            memmove(l->l_buffer, l->l_buffer + wr, l->l_pending - wr);
            l->l_pending -= wr;
          } else {
            l->l_pending = 0;
          }
        }

        break;
    }
  }

  /* done everything */
  return 1;
#undef TMP_MARGIN
}
Exemple #13
0
int main()
{
  struct katcl_line *l;
  struct katcl_parse *p;
  int count, seed, i, k, fds[2], result, al, bl;
  char alpha[MAX_ARG_LEN], beta[MAX_ARG_LEN];
  pid_t pid;

  seed = getpid();
  printf("line test: seed is %d\n", seed);
  srand(seed);

  if(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0){
    fprintf(stderr, "line: unable to create socketpair\n");
    return 1;
  }

  pid = fork();
  if(pid < 0){
    fprintf(stderr, "line: unable to fork\n");
    return 1;
  }

  if(pid == 0){
    close(fds[0]);
    echobuffer(fds[1]);
    return 0;
  }

  close(fds[1]);

  l = create_katcl(fds[0]);
  if(l == NULL){
    fprintf(stderr, "main: unable to create katcl\n");
    return 1;
  }

  for(i = 0; i < TEST_RUNS; i++){

#ifdef DEBUG
    fprintf(stderr, "line test: iteration %d\n", i);
#endif

    p = create_referenced_parse_katcl();
    if(p == NULL){
      fprintf(stderr, "unable to create parse instance %d\n", i);
      return 1;
    }

#ifdef DEBUG
    fprintf(stderr, "test: ref before submission %d\n", p->p_refs);
#endif

    fill_random_test(p);
    dump_parse_katcl(p, "random", stderr);

    if(append_parse_katcl(l, p) < 0){ 
      fprintf(stderr, "unable to add parse %d\n", i);
      return 1;
    }

#ifdef DEBUG
    fprintf(stderr, "test: ref after submission %d\n", p->p_refs);
#endif

    while((result = write_katcl(l)) == 0);

#ifdef DEBUG
    fprintf(stderr, "test: ref after write %d\n", p->p_refs);
#endif

    if(result < 0){
      fprintf(stderr, "unable to write data\n");
      return 1;
    }

    do{
      result = read_katcl(l);
      if(result){
        fprintf(stderr, "read result is %d\n", result);
        return 1;
      }
    } while(have_katcl(l) == 0);

    count = arg_count_katcl(l);

    for(k = 0; k < count; k++){
      al = arg_buffer_katcl(l, k, alpha, MAX_ARG_LEN);
      bl = get_buffer_parse_katcl(p, k, beta, MAX_ARG_LEN);

      if((bl < 0) || (al < 0)){
        fprintf(stderr, "al=%d, bl=%d\n", al, bl);
        return 1;
      }

      if(al != bl){
        fprintf(stderr, "al=%d != bl=%d\n", al, bl);
        return 1;
      }

      if(memcmp(alpha, beta, al)){
        fprintf(stderr, "mismatch: round=%d, arg=%d\n", i, k); 
        return 1;
      }
    }

    fprintf(stderr, "parsed a line with %d words\n", count);

    destroy_parse_katcl(p);

  }

  destroy_katcl(l, 1);

  printf("line test: ok\n");

  return 0;
}