static void start_one_job(const char *user, CronLine *line) { const char *shell; struct passwd *pas; pid_t pid; pas = getpwnam(user); if (!pas) { bb_error_msg("can't get uid for %s", user); goto err; } /* Prepare things before vfork */ shell = line->cl_shell ? line->cl_shell : DEFAULT_SHELL; set_env_vars(pas, shell); /* Fork as the user in question and run program */ pid = vfork(); if (pid == 0) { /* CHILD */ /* initgroups, setgid, setuid, and chdir to home or CRON_DIR */ change_user(pas); log5("child running %s", shell); /* crond 3.0pl1-100 puts tasks in separate process groups */ bb_setpgrp(); execl(shell, shell, "-c", line->cl_cmd, (char *) NULL); bb_error_msg_and_die("can't execute '%s' for user %s", shell, user); } if (pid < 0) { bb_perror_msg("vfork"); err: pid = 0; } line->cl_pid = pid; }
/* * Determine which jobs need to be run. Under normal conditions, the * period is about a minute (one scan). Worst case it will be one * hour (60 scans). */ static void flag_starting_jobs(time_t t1, time_t t2) { time_t t; /* Find jobs > t1 and <= t2 */ for (t = t1 - t1 % 60; t <= t2; t += 60) { struct tm *ptm; CronFile *file; CronLine *line; if (t <= t1) continue; ptm = localtime(&t); for (file = G.cron_files; file; file = file->cf_next) { log5("file %s:", file->cf_username); if (file->cf_deleted) continue; for (line = file->cf_lines; line; line = line->cl_next) { log5(" line %s", line->cl_cmd); if (line->cl_Mins[ptm->tm_min] && line->cl_Hrs[ptm->tm_hour] && (line->cl_Days[ptm->tm_mday] || line->cl_Dow[ptm->tm_wday]) && line->cl_Mons[ptm->tm_mon] ) { log5(" job: %d %s", (int)line->cl_pid, line->cl_cmd); if (line->cl_pid > 0) { log8("user %s: process already running: %s", file->cf_username, line->cl_cmd); } else if (line->cl_pid == 0) { line->cl_pid = -1; file->cf_wants_starting = 1; } } } } } }
// MyINFO example: // $MyINFO $ALL <nick> <interest>$ $<speed\x01>$<e-mail>$<sharesize>$ // $MyINFO $ALL nick <++ V:0.668,M:P,H:39/0/0,S:1>$ $DSL\x01$$74894830123$ // at this point the tag has already been validated by the hub so we do only the simplest tests here int cpiPython::SplitMyINFO(const char *msg, const char **nick, const char **desc, const char **tag, const char **speed, const char **mail, const char **size) { const char *begin = "$MyINFO $ALL "; int dollars[5] = { -1, -1, -1, -1, -1 }; int spacepos=0, validtag=0, tagstart=0, tagend=0; // spacepos = position of space after nick int len = strlen(msg); int start = strlen(begin); if (len < 21 ) return 0; if (strncmp(msg, begin, start)) return 0; for (int pos = start, dollar = 0; pos < len && dollar < 5; pos++) { switch (msg[pos]) { case '$': dollars[dollar] = pos; dollar++; break; case ' ': if (!spacepos and !dollar) spacepos = pos; break; case '<': if (!dollar) tagstart = pos; break; case '>': if (!dollar) tagend = pos; break; } } if (dollars[4] != len-1 || !spacepos) return 0; if (tagstart && tagend && msg[tagend+1] == '$') validtag = 1; string s = msg; string _nick = s.substr( start, spacepos - start ); string _desc = (validtag) ? s.substr( spacepos + 1, tagstart - spacepos - 1 ) : s.substr( spacepos + 1, dollars[0] - spacepos - 1 ); string _tag = (validtag) ? s.substr( tagstart, dollars[0] - tagstart ) : ""; string _speed = s.substr( dollars[1] + 1, dollars[2] - dollars[1] - 1 ); string _mail = s.substr( dollars[2] + 1, dollars[3] - dollars[2] - 1 ); string _size = s.substr( dollars[3] + 1, dollars[4] - dollars[3] - 1 ); (*nick) = strdup( _nick.c_str()); (*desc) = strdup( _desc.c_str()); (*tag) = strdup( _tag.c_str()); (*speed) = strdup( _speed.c_str()); (*mail) = strdup( _mail.c_str()); (*size) = strdup( _size.c_str()); log5("PY: SplitMyINFO: [%s] \n dollars:%d,%d,%d,%d,%d / tag start:%d,end:%d / space:%d\n nick:%s/desc:%s/tag:%s/speed:%s/mail:%s/size:%s\n", s.c_str(), dollars[0], dollars[1], dollars[2], dollars[3], dollars[4], tagstart, tagend, spacepos, *nick, *desc, *tag, *speed, *mail, *size); return 1; }
TEST(LoggerTest, LevelTest) { std::ostringstream ss; Logger log1(ss, Logger::TRACE_); Logger log2(ss, Logger::VERBOSE_); Logger log3(ss, Logger::DEBUG_); Logger log4(ss, Logger::INFO_); Logger log5(ss, Logger::WARN_); Logger log6(ss, Logger::ERROR_); ASSERT_TRUE(log1.t()); ASSERT_TRUE(log1.v()); ASSERT_TRUE(log1.d()); ASSERT_TRUE(log1.i()); ASSERT_FALSE(log2.t()); ASSERT_TRUE(log2.v()); ASSERT_TRUE(log2.d()); ASSERT_TRUE(log2.i()); ASSERT_FALSE(log3.t()); ASSERT_FALSE(log3.v()); ASSERT_TRUE(log3.d()); ASSERT_TRUE(log3.i()); ASSERT_FALSE(log4.t()); ASSERT_FALSE(log4.v()); ASSERT_FALSE(log4.d()); ASSERT_TRUE(log4.i()); ASSERT_FALSE(log5.t()); ASSERT_FALSE(log5.v()); ASSERT_FALSE(log5.d()); ASSERT_FALSE(log5.i()); ASSERT_FALSE(log6.t()); ASSERT_FALSE(log6.v()); ASSERT_FALSE(log6.d()); ASSERT_FALSE(log6.i()); }
int crond_main(int argc UNUSED_PARAM, char **argv) { time_t t2; unsigned rescan; unsigned sleep_time; unsigned opts; INIT_G(); /* "-b after -f is ignored", and so on for every pair a-b */ opt_complementary = "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l") /* -l and -d have numeric param */ ":l+" IF_FEATURE_CROND_D(":d+"); opts = getopt32(argv, "l:L:fbSc:" IF_FEATURE_CROND_D("d:"), &G.log_level, &G.log_filename, &G.crontab_dir_name IF_FEATURE_CROND_D(,&G.log_level)); /* both -d N and -l N set the same variable: G.log_level */ if (!(opts & OPT_f)) { /* close stdin, stdout, stderr. * close unused descriptors - don't need them. */ bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); } if (!(opts & OPT_d) && G.log_filename == NULL) { /* logging to syslog */ openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON); logmode = LOGMODE_SYSLOG; } //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ reopen_logfile_to_stderr(); xchdir(G.crontab_dir_name); log8("crond (busybox "BB_VER") started, log level %d", G.log_level); rescan_crontab_dir(); write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid"); /* Main loop */ t2 = time(NULL); rescan = 60; sleep_time = 60; for (;;) { struct stat sbuf; time_t t1; long dt; /* Synchronize to 1 minute, minimum 1 second */ t1 = t2; sleep(sleep_time - (time(NULL) % sleep_time)); t2 = time(NULL); dt = (long)t2 - (long)t1; reopen_logfile_to_stderr(); /* * The file 'cron.update' is checked to determine new cron * jobs. The directory is rescanned once an hour to deal * with any screwups. * * Check for time jump. Disparities over an hour either way * result in resynchronization. A negative disparity * less than an hour causes us to effectively sleep until we * match the original time (i.e. no re-execution of jobs that * have just been run). A positive disparity less than * an hour causes intermediate jobs to be run, but only once * in the worst case. * * When running jobs, the inequality used is greater but not * equal to t1, and less then or equal to t2. */ if (stat(G.crontab_dir_name, &sbuf) != 0) sbuf.st_mtime = 0; /* force update (once) if dir was deleted */ if (G.crontab_dir_mtime != sbuf.st_mtime) { G.crontab_dir_mtime = sbuf.st_mtime; rescan = 1; } if (--rescan == 0) { rescan = 60; rescan_crontab_dir(); } process_cron_update_file(); log5("wakeup dt=%ld", dt); if (dt < -60 * 60 || dt > 60 * 60) { bb_error_msg("time disparity of %ld minutes detected", dt / 60); /* and we do not run any jobs in this case */ } else if (dt > 0) { /* Usual case: time advances forward, as expected */ flag_starting_jobs(t1, t2); start_jobs(); sleep_time = 60; if (check_completions() > 0) { /* some jobs are still running */ sleep_time = 10; } } /* else: time jumped back, do not run any jobs */ } /* for (;;) */ return 0; /* not reached */ }
static pid_t fork_job(const char *user, int mailFd, CronLine *line, bool run_sendmail) { struct passwd *pas; const char *shell, *prog; smallint sv_logmode; pid_t pid; /* prepare things before vfork */ pas = getpwnam(user); if (!pas) { bb_error_msg("can't get uid for %s", user); goto err; } shell = line->cl_shell ? line->cl_shell : DEFAULT_SHELL; prog = run_sendmail ? SENDMAIL : shell; set_env_vars(pas, shell); sv_logmode = logmode; pid = vfork(); if (pid == 0) { /* CHILD */ /* initgroups, setgid, setuid, and chdir to home or CRON_DIR */ change_user(pas); log5("child running %s", prog); if (mailFd >= 0) { xmove_fd(mailFd, run_sendmail ? 0 : 1); dup2(1, 2); } /* crond 3.0pl1-100 puts tasks in separate process groups */ bb_setpgrp(); if (!run_sendmail) execlp(prog, prog, "-c", line->cl_cmd, (char *) NULL); else execlp(prog, prog, SENDMAIL_ARGS, (char *) NULL); /* * I want this error message on stderr too, * even if other messages go only to syslog: */ logmode |= LOGMODE_STDIO; bb_error_msg_and_die("can't execute '%s' for user %s", prog, user); } logmode = sv_logmode; if (pid < 0) { bb_perror_msg("vfork"); err: pid = 0; } /* else: PARENT, FORK SUCCESS */ /* * Close the mail file descriptor.. we can't just leave it open in * a structure, closing it later, because we might run out of descriptors */ if (mailFd >= 0) { close(mailFd); } return pid; }
static void load_crontab(const char *fileName) { struct parser_t *parser; struct stat sbuf; int maxLines; char *tokens[6]; #if ENABLE_FEATURE_CROND_CALL_SENDMAIL char *mailTo = NULL; #endif char *shell = NULL; delete_cronfile(fileName); if (!getpwnam(fileName)) { log7("ignoring file '%s' (no such user)", fileName); return; } parser = config_open(fileName); if (!parser) return; maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES; if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DAEMON_UID) { CronFile *file = xzalloc(sizeof(CronFile)); CronLine **pline; int n; file->cf_username = xstrdup(fileName); pline = &file->cf_lines; while (1) { CronLine *line; if (!--maxLines) { bb_error_msg("user %s: too many lines", fileName); break; } n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY); if (!n) break; log5("user:%s entry:%s", fileName, parser->data); /* check if line is setting MAILTO= */ if (is_prefixed_with(tokens[0], "MAILTO=")) { #if ENABLE_FEATURE_CROND_CALL_SENDMAIL free(mailTo); mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL; #endif /* otherwise just ignore such lines */ continue; } if (is_prefixed_with(tokens[0], "SHELL=")) { free(shell); shell = xstrdup(&tokens[0][6]); continue; } //TODO: handle HOME= too? "man crontab" says: //name = value // //where the spaces around the equal-sign (=) are optional, and any subsequent //non-leading spaces in value will be part of the value assigned to name. //The value string may be placed in quotes (single or double, but matching) //to preserve leading or trailing blanks. // //Several environment variables are set up automatically by the cron(8) daemon. //SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd //line of the crontab's owner. HOME and SHELL may be overridden by settings //in the crontab; LOGNAME may not. /* check if a minimum of tokens is specified */ if (n < 6) continue; *pline = line = xzalloc(sizeof(*line)); /* parse date ranges */ ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]); ParseField(file->cf_username, line->cl_Hrs, 24, 0, NULL, tokens[1]); ParseField(file->cf_username, line->cl_Days, 32, 0, NULL, tokens[2]); ParseField(file->cf_username, line->cl_Mons, 12, -1, MonAry, tokens[3]); ParseField(file->cf_username, line->cl_Dow, 7, 0, DowAry, tokens[4]); /* * fix days and dow - if one is not "*" and the other * is "*", the other is set to 0, and vise-versa */ FixDayDow(line); #if ENABLE_FEATURE_CROND_CALL_SENDMAIL /* copy mailto (can be NULL) */ line->cl_mailto = xstrdup(mailTo); #endif line->cl_shell = xstrdup(shell); /* copy command */ line->cl_cmd = xstrdup(tokens[5]); pline = &line->cl_next; //bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]); } *pline = NULL; file->cf_next = G.cron_files; G.cron_files = file; } config_close(parser); #if ENABLE_FEATURE_CROND_CALL_SENDMAIL free(mailTo); #endif free(shell); }
static void load_crontab(const char *fileName) { struct parser_t *parser; struct stat sbuf; int maxLines; char *tokens[6]; #if ENABLE_FEATURE_CROND_CALL_SENDMAIL char *mailTo = NULL; #endif char *shell = NULL; delete_cronfile(fileName); if (!getpwnam(fileName)) { log7("ignoring file '%s' (no such user)", fileName); return; } parser = config_open(fileName); if (!parser) return; maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES; if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DAEMON_UID) { CronFile *file = xzalloc(sizeof(CronFile)); CronLine **pline; int n; file->cf_username = xstrdup(fileName); pline = &file->cf_lines; while (1) { CronLine *line; if (!--maxLines) { bb_error_msg("user %s: too many lines", fileName); break; } n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY); if (!n) break; log5("user:%s entry:%s", fileName, parser->data); /* check if line is setting MAILTO= */ if (is_prefixed_with(tokens[0], "MAILTO=")) { #if ENABLE_FEATURE_CROND_CALL_SENDMAIL free(mailTo); mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL; #endif /* otherwise just ignore such lines */ continue; } if (is_prefixed_with(tokens[0], "SHELL=")) { free(shell); shell = xstrdup(&tokens[0][6]); continue; } //TODO: handle HOME= too? "man crontab" says: //name = value // //where the spaces around the equal-sign (=) are optional, and any subsequent //non-leading spaces in value will be part of the value assigned to name. //The value string may be placed in quotes (single or double, but matching) //to preserve leading or trailing blanks. // //Several environment variables are set up automatically by the cron(8) daemon. //SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd //line of the crontab's owner. HOME and SHELL may be overridden by settings //in the crontab; LOGNAME may not. #if ENABLE_FEATURE_CROND_SPECIAL_TIMES if (tokens[0][0] == '@') { /* * "@daily /a/script/to/run PARAM1 PARAM2..." */ typedef struct SpecialEntry { const char *name; const char tokens[8]; } SpecialEntry; static const SpecialEntry SpecAry[] = { /* hour day month weekday */ { "yearly", "0\0" "1\0" "1\0" "*" }, { "annually", "0\0" "1\0" "1\0" "*" }, { "monthly", "0\0" "1\0" "*\0" "*" }, { "weekly", "0\0" "*\0" "*\0" "0" }, { "daily", "0\0" "*\0" "*\0" "*" }, { "midnight", "0\0" "*\0" "*\0" "*" }, { "hourly", "*\0" "*\0" "*\0" "*" }, { "reboot", "" }, }; const SpecialEntry *e = SpecAry; if (n < 2) continue; for (;;) { if (strcmp(e->name, tokens[0] + 1) == 0) { /* * tokens[1] is only the first word of command, * can'r use it. * find the entire command in unmodified string: */ tokens[5] = skip_whitespace( skip_non_whitespace( skip_whitespace(parser->data))); if (e->tokens[0]) { char *et = (char*)e->tokens; /* minute is "0" for all specials */ tokens[0] = (char*)"0"; tokens[1] = et; tokens[2] = et + 2; tokens[3] = et + 4; tokens[4] = et + 6; } goto got_it; } if (!e->tokens[0]) break; e++; } continue; /* bad line (unrecognized '@foo') */ } #endif /* check if a minimum of tokens is specified */ if (n < 6) continue; IF_FEATURE_CROND_SPECIAL_TIMES( got_it: ) *pline = line = xzalloc(sizeof(*line)); #if ENABLE_FEATURE_CROND_SPECIAL_TIMES if (tokens[0][0] == '@') { /* "@reboot" line */ file->cf_wants_starting = 1; line->cl_pid = START_ME_REBOOT; /* wants to start */ /* line->cl_Mins/Hrs/etc stay zero: never match any time */ } else #endif { /* parse date ranges */ ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]); ParseField(file->cf_username, line->cl_Hrs, 24, 0, NULL, tokens[1]); ParseField(file->cf_username, line->cl_Days, 32, 0, NULL, tokens[2]); ParseField(file->cf_username, line->cl_Mons, 12, -1, MonAry, tokens[3]); ParseField(file->cf_username, line->cl_Dow, 7, 0, DowAry, tokens[4]); /* * fix days and dow - if one is not "*" and the other * is "*", the other is set to 0, and vise-versa */ FixDayDow(line); } #if ENABLE_FEATURE_CROND_CALL_SENDMAIL /* copy mailto (can be NULL) */ line->cl_mailto = xstrdup(mailTo); #endif line->cl_shell = xstrdup(shell); /* copy command */ line->cl_cmd = xstrdup(tokens[5]); pline = &line->cl_next; //bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]); } *pline = NULL; file->cf_next = G.cron_files; G.cron_files = file; }
/** * This is the main message reading loop. Messages are read, validated, * decrypted if necessary, then passed to the appropriate routine for handling. */ void mainloop(void) { struct uftp_h *header; struct group_list_t *group; unsigned char *buf, *decrypted, *message; char rxname[INET6_ADDRSTRLEN]; unsigned int decryptlen, meslen; int packetlen, rval, i, ecn; uint8_t version, *func, tos; uint16_t txseq; union sockaddr_u src; struct timeval *tv, rxtime; double new_grtt; log2(0, 0, 0, "%s", VERSIONSTR); for (i = 0; i < key_count; i++) { if (privkey_type[i] == KEYBLOB_RSA) { log2(0, 0, 0, "Loaded %d bit RSA key with fingerprint %s", RSA_keylen(privkey[i].rsa) * 8, print_key_fingerprint(privkey[i], KEYBLOB_RSA)); } else { log2(0, 0, 0, "Loaded ECDSA key with curve %s and fingerprint %s", curve_name(get_EC_curve(privkey[i].ec)), print_key_fingerprint(privkey[i], KEYBLOB_EC)); } } buf = safe_calloc(MAXMTU, 1); decrypted = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; while (1) { tv = getrecenttimeout(); if (tv) { log5(0, 0, 0, "read timeout: %d.%06d", tv->tv_sec, tv->tv_usec); } if (read_packet(listener, &src, buf, &packetlen, MAXMTU, tv, &tos) <= 0) { continue; } gettimeofday(&rxtime, NULL); if ((rval = getnameinfo((struct sockaddr *)&src, family_len(src), rxname, sizeof(rxname), NULL, 0, NI_NUMERICHOST)) != 0) { log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval)); } if (header->version == UFTP_VER_NUM) { version = header->version; group = find_group(ntohl(header->group_id), header->group_inst); } else { log1(0, 0, 0, "Invalid message from %s: not uftp packet " "or invalid version", rxname); continue; } if (packetlen < sizeof(struct uftp_h) + 4) { log1(0, 0, 0, "Invalid packet size from %s: %d", rxname, packetlen); continue; } txseq = htons(header->seq); // A KEY_INFO or ABORT could come from a proxy, so don't check the seq // TODO: need to account for these in the loss history if ((group != NULL) && (header->func != KEYINFO) && (header->func != ABORT)) { if ((int16_t)(group->max_txseq - txseq) > MAXMISORDER) { glog3(group, "seq out of range, dropping"); continue; } if (group->cc_type != CC_NONE) { ecn = ((tos & 0x3) == 3); update_loss_history(group, txseq, packetlen, ecn); } else if ((int16_t)(txseq - group->max_txseq) > 0) { group->max_txseq = txseq; } } if ((header->func == ENCRYPTED) && (group != NULL) && (group->keytype != KEY_NONE)) { if (group->phase == PHASE_REGISTERED) { glog1(group, "Got encrypted packet from %s " "but keys not established", rxname); } if (!validate_and_decrypt(buf, packetlen, &decrypted, &decryptlen, group->keytype, group->groupkey, group->groupsalt, group->ivlen, group->hashtype, group->grouphmackey, group->hmaclen, group->sigtype, group->keyextype, group->server_pubkey, group->server_pubkeylen)) { glog1(group, "Rejecting message from %s: " "decrypt/validate failed", rxname); continue; } func = (uint8_t *)decrypted; message = decrypted; meslen = decryptlen; } else { if ((group != NULL) && (group->keytype != KEY_NONE) && ((header->func == FILEINFO) || (header->func == FILESEG) || (header->func == DONE) || (header->func == DONE_CONF) || ((header->func == ABORT) && (group->phase != PHASE_REGISTERED)))) { glog1(group, "Rejecting %s message from %s: not encrypted", func_name(header->func), rxname); continue; } func = (uint8_t *)&header->func; message = buf + sizeof(struct uftp_h); meslen = packetlen - sizeof(struct uftp_h); } if (group != NULL) { new_grtt = unquantize_grtt(header->grtt); if (fabs(new_grtt - group->grtt) > 0.001) { group->grtt = new_grtt; set_timeout(group, 1); } group->gsize = unquantize_gsize(header->gsize); glog5(group, "grtt: %.3f", group->grtt); } if (header->func == PROXY_KEY) { handle_proxy_key(&src, message, meslen); continue; } if (header->func == HB_RESP) { handle_hb_response(listener, &src, message, meslen, hb_hosts, hbhost_count, privkey[0], privkey_type[0], uid); continue; } if (header->func == ANNOUNCE) { // Ignore any ANNOUNCE for a group we're already handling if (group == NULL) { handle_announce(&src, buf, packetlen, rxtime); } else if (group->phase == PHASE_MIDGROUP) { // Make sure we don't time out while waiting for other // clients to register with the server. set_timeout(group, 0); } } else { if (group == NULL) { // group / file ID not in list continue; } if (group->version != version) { glog1(group, "Version mismatch"); continue; } if (group->src_id != header->src_id) { glog1(group, "Source ID mismatch"); continue; } if (*func == ABORT) { handle_abort(group, message, meslen); continue; } switch (group->phase) { case PHASE_REGISTERED: if (group->keytype != KEY_NONE) { if (*func == KEYINFO) { handle_keyinfo(group, message, meslen, header->src_id); } else { glog1(group, "Expected KEYINFO, got %s", func_name(*func)); } } else if (group->keytype == KEY_NONE) { if (*func == REG_CONF) { handle_regconf(group, message, meslen); } else if (*func == FILEINFO) { handle_fileinfo(group, message, meslen, rxtime); } else { glog1(group, "Expected REG_CONF, got %s", func_name(*func)); } } break; case PHASE_MIDGROUP: if (*func == FILEINFO) { handle_fileinfo(group, message, meslen, rxtime); } else if (*func == KEYINFO) { handle_keyinfo(group, message, meslen, header->src_id); } else if (*func == DONE) { handle_done(group, message, meslen); } else { // Other clients may be still getting earlier files or // setting up, so silently ignore anything unexpected // and reset the timeout. set_timeout(group, 0); } break; case PHASE_RECEIVING: if (*func == FILEINFO) { handle_fileinfo(group, message, meslen, rxtime); } else if (*func == FILESEG) { handle_fileseg(group, message, meslen, txseq); } else if (*func == DONE) { handle_done(group, message, meslen); } else if (*func == CONG_CTRL) { handle_cong_ctrl(group, message, meslen, rxtime); } break; case PHASE_COMPLETE: if (*func == DONE_CONF) { handle_done_conf(group, message, meslen); } break; } } } }