int sleepkick_main(int argc UNUSED_PARAM, char **argv) { unsigned duration; unsigned watchdog; ++argv; if (!*argv) bb_show_usage(); duration = xatou(*argv); ++argv; if(*argv) { int fd; /* File handler for watchdog */ int i = 0; int iMax = 0; watchdog = xatou(*argv); iMax = (int) (duration / watchdog); int modDuration = 0; int wReminder = 0; if( duration >= watchdog) { modDuration = duration % watchdog; } else { modDuration = duration; } fd = open(WATCHDOGDEV, O_RDWR); for( i = 0; i < iMax; i++) { write(fd, "1", 1); sleep(watchdog); } if(modDuration) { write(fd, "1", 1); sleep(modDuration); write(fd, "1", 1); } close(fd); } else { sleep(duration); } return EXIT_SUCCESS; }
int xatoi_positive(const char *numstr) { unsigned r = xatou(numstr); if (r > (unsigned)INT_MAX) error_msg_and_die("invalid number '%s'", numstr); return r; }
/* Process the 'config threshold: memcap #bytes, option2-name option2-value, ...' config threshold: memcap #bytes */ void ProcessThresholdOptions(char *options) { int i = 0; char ** args; int nargs; char ** oargs; int noargs; if( !s_enabled ) return ; args = mSplit(options,",",10,&nargs,0); /* get rule option pairs */ for(i=0;i<nargs;i++) { oargs = mSplit(options," ",2,&noargs,0); /* get rule option pairs */ if( strcmp(oargs[0],"memcap") == 0 ) { s_memcap = xatou(oargs[1],"config threshold: memcap"); } else { FatalError("Threshold-RuleOptionParse: unknown argument\n"); } } mSplitFree(&args, nargs); mSplitFree(&oargs, noargs); }
static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode) { static const uint8_t tcflag_offsets[] ALIGN1 = { offsetof(struct termios, c_cflag), /* control */ offsetof(struct termios, c_iflag), /* input */ offsetof(struct termios, c_oflag), /* output */ offsetof(struct termios, c_lflag) /* local */ }; if (type <= local) { return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]); } return NULL; } static void set_speed_or_die(enum speed_setting type, const char *arg, struct termios *mode) { speed_t baud; baud = tty_value_to_baud(xatou(arg)); if (type != output_speed) { /* either input or both */ cfsetispeed(mode, baud); } if (type != input_speed) { /* either output or both */ cfsetospeed(mode, baud); } }
int halt_main(int argc, char **argv) { static const int magic[] = { #ifdef RB_HALT_SYSTEM RB_HALT_SYSTEM, #elif defined RB_HALT RB_HALT, #endif #ifdef RB_POWER_OFF RB_POWER_OFF, #elif defined RB_POWERDOWN RB_POWERDOWN, #endif RB_AUTOBOOT }; static const int signals[] = { SIGUSR1, SIGUSR2, SIGTERM }; char *delay; int which, flags, rc = 1; /* Figure out which applet we're running */ for (which = 0; "hpr"[which] != *applet_name; which++); /* Parse and handle arguments */ flags = getopt32(argv, "d:nf", &delay); if (flags & 1) sleep(xatou(delay)); if (!(flags & 2)) sync(); /* start of y42304 20080419: 解决96358芯片软复位问题:*/ #if defined(SUPPORT_BCM96358) || defined(SUPPORT_BCM96368) BSP_SYS_SoftReboot(); return 0; #endif /* end of y42304 20080419: 解决96358芯片软复位问题:*/ /* Perform action. */ if (ENABLE_INIT && !(flags & 4)) { if (ENABLE_FEATURE_INITRD) { pid_t *pidlist = find_pid_by_name("linuxrc"); if (pidlist[0] > 0) rc = kill(pidlist[0], signals[which]); if (ENABLE_FEATURE_CLEAN_UP) free(pidlist); } if (rc) rc = kill(1, signals[which]); } else { rc = reboot(magic[which]); } if (rc) bb_error_msg("no"); return rc; }
int vconfig_main(int argc, char **argv) { struct vlan_ioctl_args ifr; const char *p; int fd; if (argc < 3) { bb_show_usage(); } /* Don't bother closing the filedes. It will be closed on cleanup. */ /* Will die if 802.1q is not present */ xopen(conf_file_name, O_RDONLY); memset(&ifr, 0, sizeof(struct vlan_ioctl_args)); ++argv; p = xfind_str(cmds+2, *argv); ifr.cmd = *p; if (argc != p[-1]) { bb_show_usage(); } if (ifr.cmd == SET_VLAN_NAME_TYPE_CMD) { /* set_name_type */ ifr.u.name_type = *xfind_str(name_types+1, argv[1]); } else { if (strlen(argv[1]) >= IF_NAMESIZE) { bb_error_msg_and_die("if_name >= %d chars", IF_NAMESIZE); } strcpy(ifr.device1, argv[1]); p = argv[2]; /* I suppose one could try to combine some of the function calls below, * since ifr.u.flag, ifr.u.VID, and ifr.u.skb_priority are all same-sized * (unsigned) int members of a unions. But because of the range checking, * doing so wouldn't save that much space and would also make maintainence * more of a pain. */ if (ifr.cmd == SET_VLAN_FLAG_CMD) { /* set_flag */ ifr.u.flag = xatoul_range(p, 0, 1); /* DM: in order to set reorder header, qos must be set */ ifr.vlan_qos = xatoul_range(argv[3], 0, 7); } else if (ifr.cmd == ADD_VLAN_CMD) { /* add */ ifr.u.VID = xatoul_range(p, 0, VLAN_GROUP_ARRAY_LEN-1); } else if (ifr.cmd != DEL_VLAN_CMD) { /* set_{egress|ingress}_map */ ifr.u.skb_priority = xatou(p); ifr.vlan_qos = xatoul_range(argv[3], 0, 7); } } fd = xsocket(AF_INET, SOCK_STREAM, 0); if (ioctl(fd, SIOCSIFVLAN, &ifr) < 0) { bb_perror_msg_and_die("ioctl error for %s", *argv); } return 0; }
int usleep_main(int argc UNUSED_PARAM, char **argv) { if (!argv[1]) { bb_show_usage(); } usleep(xatou(argv[1])); return EXIT_SUCCESS; }
int watch_main(int argc, char **argv) { unsigned opt; unsigned period = 2; unsigned cmdlen; char *header = NULL; char *cmd; char *tmp; char **p; opt_complementary = "-1"; // at least one param please opt = getopt32(argv, "+dtn:", &tmp); //if (opt & 0x1) // -d (ignore) //if (opt & 0x2) // -t if (opt & 0x4) period = xatou(tmp); argv += optind; p = argv; cmdlen = 1; // 1 for terminal NUL while (*p) cmdlen += strlen(*p++) + 1; tmp = cmd = xmalloc(cmdlen); while (*argv) { tmp += sprintf(tmp, " %s", *argv); argv++; } cmd++; // skip initial space while (1) { printf("\033[H\033[J"); if (!(opt & 0x2)) { // no -t int width, len; char *thyme; time_t t; get_terminal_width_height(STDIN_FILENO, &width, 0); header = xrealloc(header, width--); // '%-*s' pads header with spaces to the full width snprintf(header, width, "Every %ds: %-*s", period, width, cmd); time(&t); thyme = ctime(&t); len = strlen(thyme); if (len < width) strcpy(header + width - len, thyme); puts(header); } fflush(stdout); // TODO: 'real' watch pipes cmd's output to itself // and does not allow it to overflow the screen // (taking into account linewrap!) system(cmd); sleep(period); } return 0; // gcc thinks we can reach this :) }
int xatoi(const char *numstr) { unsigned r; if (*numstr != '-') return xatoi_positive(numstr); r = xatou(numstr + 1); if (r > (unsigned)INT_MAX + 1) error_msg_and_die("invalid number '%s'", numstr); return - (int)r; }
int usleep_main(int argc UNUSED_PARAM, char **argv) { if (!argv[1]) { bb_show_usage(); } if (usleep(xatou(argv[1]))) { bb_perror_nomsg_and_die(); } return EXIT_SUCCESS; }
static void set_speed_or_die(enum speed_setting type, const char * const arg, struct termios * const mode) { speed_t baud; baud = tty_value_to_baud(xatou(arg)); if (type != output_speed) { /* either input or both */ cfsetispeed(mode, baud); } if (type != input_speed) { /* either output or both */ cfsetospeed(mode, baud); } }
/* Parse basic CIDR block - [!]a.b.c.d/bits */ static void parseCIDR( THDX_STRUCT * thdx, char * s ) { #ifdef SUP_IP6 sfip_pton(s, &thdx->ip_address); #else char **args; int nargs; if (*s == '!') { thdx->not_flag = 1; s++; while( (*s <= ' ') && (*s > 0) ) s++; /* skip whitespace */ } args = mSplit( s , "/", 2, &nargs, 0 ); /* get rule option pairs */ if( !nargs || nargs > 2 ) { FatalError("%s(%d) => Suppress-Parse: argument pairing error\n", file_name, file_line); } /* * Keep IP in network order */ thdx->ip_address = inet_addr( args[0] ); if( nargs == 2 ) { int i; int nbits; int mask; nbits = xatou( args[1],"suppress: cidr mask bits" ); mask = 1 << 31; for( i=0; i<nbits; i++ ) { thdx->ip_mask |= mask; mask >>= 1; } /* Put mask in network order */ thdx->ip_mask = htonl(thdx->ip_mask); }
int usleep_main(int argc UNUSED_PARAM, char **argv) { if (!argv[1]) { bb_show_usage(); } /* Safe wrt NOFORK? (noforks are not allowed to run for * a long time). Try "usleep 99999999" + ^C + "echo $?" * in hush with FEATURE_SH_NOFORK=y. * At least on uclibc, usleep() thanslates to nanosleep() * which returns early on any signal (even caught one), * and uclibc does not loop back on EINTR. */ usleep(xatou(argv[1])); return EXIT_SUCCESS; }
int sleep_main(int argc UNUSED_PARAM, char **argv) { duration_t duration; ++argv; if (!*argv) bb_show_usage(); #if ENABLE_FEATURE_FANCY_SLEEP # if ENABLE_FLOAT_DURATION /* undo busybox.c setlocale */ setlocale(LC_NUMERIC, "C"); # endif duration = 0; do { duration += parse_duration_str(*argv); } while (*++argv); sleep_for_duration(duration); #else /* simple */ duration = xatou(*argv); sleep(duration); #endif return EXIT_SUCCESS; }
int stty_main(int argc UNUSED_PARAM, char **argv) { struct termios mode; void (*output_func)(const struct termios *, int); const char *file_name = NULL; int display_all = 0; int stty_state; int k; INIT_G(); stty_state = STTY_noargs; output_func = do_display; /* First pass: only parse/verify command line params */ k = 0; while (argv[++k]) { const struct mode_info *mp; const struct control_info *cp; const char *arg = argv[k]; const char *argnext = argv[k+1]; int param; if (arg[0] == '-') { int i; mp = find_mode(arg+1); if (mp) { if (!(mp->flags & REV)) goto invalid_argument; stty_state &= ~STTY_noargs; continue; } /* It is an option - parse it */ i = 0; while (arg[++i]) { switch (arg[i]) { case 'a': stty_state |= STTY_verbose_output; output_func = do_display; display_all = 1; break; case 'g': stty_state |= STTY_recoverable_output; output_func = display_recoverable; break; case 'F': if (file_name) bb_error_msg_and_die("only one device may be specified"); file_name = &arg[i+1]; /* "-Fdevice" ? */ if (!file_name[0]) { /* nope, "-F device" */ int p = k+1; /* argv[p] is argnext */ file_name = argnext; if (!file_name) bb_error_msg_and_die(bb_msg_requires_arg, "-F"); /* remove -F param from arg[vc] */ while (argv[p]) { argv[p] = argv[p+1]; ++p; } } goto end_option; default: goto invalid_argument; } } end_option: continue; } mp = find_mode(arg); if (mp) { stty_state &= ~STTY_noargs; continue; } cp = find_control(arg); if (cp) { if (!argnext) bb_error_msg_and_die(bb_msg_requires_arg, arg); /* called for the side effect of xfunc death only */ set_control_char_or_die(cp, argnext, &mode); stty_state &= ~STTY_noargs; ++k; continue; } param = find_param(arg); if (param & param_need_arg) { if (!argnext) bb_error_msg_and_die(bb_msg_requires_arg, arg); ++k; } switch (param) { #ifdef __linux__ case param_line: # ifndef TIOCGWINSZ xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); break; # endif /* else fall-through */ #endif #ifdef TIOCGWINSZ case param_rows: case param_cols: case param_columns: xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); break; case param_size: #endif case param_speed: break; case param_ispeed: /* called for the side effect of xfunc death only */ set_speed_or_die(input_speed, argnext, &mode); break; case param_ospeed: /* called for the side effect of xfunc death only */ set_speed_or_die(output_speed, argnext, &mode); break; default: if (recover_mode(arg, &mode) == 1) break; if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break; invalid_argument: bb_error_msg_and_die("invalid argument '%s'", arg); } stty_state &= ~STTY_noargs; } /* Specifying both -a and -g is an error */ if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) == (STTY_verbose_output | STTY_recoverable_output) ) { bb_error_msg_and_die("-a and -g are mutually exclusive"); } /* Specifying -a or -g with non-options is an error */ if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) && !(stty_state & STTY_noargs) ) { bb_error_msg_and_die("modes may not be set when -a or -g is used"); } /* Now it is safe to start doing things */ if (file_name) { G.device_name = file_name; xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO); ndelay_off(STDIN_FILENO); } /* Initialize to all zeroes so there is no risk memcmp will report a spurious difference in an uninitialized portion of the structure */ memset(&mode, 0, sizeof(mode)); if (tcgetattr(STDIN_FILENO, &mode)) perror_on_device_and_die("%s"); if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) { G.max_col = get_terminal_width(STDOUT_FILENO); output_func(&mode, display_all); return EXIT_SUCCESS; } /* Second pass: perform actions */ k = 0; while (argv[++k]) { const struct mode_info *mp; const struct control_info *cp; const char *arg = argv[k]; const char *argnext = argv[k+1]; int param; if (arg[0] == '-') { mp = find_mode(arg+1); if (mp) { set_mode(mp, 1 /* reversed */, &mode); stty_state |= STTY_require_set_attr; } /* It is an option - already parsed. Skip it */ continue; } mp = find_mode(arg); if (mp) { set_mode(mp, 0 /* non-reversed */, &mode); stty_state |= STTY_require_set_attr; continue; } cp = find_control(arg); if (cp) { ++k; set_control_char_or_die(cp, argnext, &mode); stty_state |= STTY_require_set_attr; continue; } param = find_param(arg); if (param & param_need_arg) { ++k; } switch (param) { #ifdef __linux__ case param_line: mode.c_line = xatoul_sfx(argnext, stty_suffixes); stty_state |= STTY_require_set_attr; break; #endif #ifdef TIOCGWINSZ case param_cols: case param_columns: set_window_size(-1, xatoul_sfx(argnext, stty_suffixes)); break; case param_size: display_window_size(0); break; case param_rows: set_window_size(xatoul_sfx(argnext, stty_suffixes), -1); break; #endif case param_speed: display_speed(&mode, 0); break; case param_ispeed: set_speed_or_die(input_speed, argnext, &mode); stty_state |= (STTY_require_set_attr | STTY_speed_was_set); break; case param_ospeed: set_speed_or_die(output_speed, argnext, &mode); stty_state |= (STTY_require_set_attr | STTY_speed_was_set); break; default: if (recover_mode(arg, &mode) == 1) stty_state |= STTY_require_set_attr; else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{ set_speed_or_die(both_speeds, arg, &mode); stty_state |= (STTY_require_set_attr | STTY_speed_was_set); } /* else - impossible (caught in the first pass): bb_error_msg_and_die("invalid argument '%s'", arg); */ } } if (stty_state & STTY_require_set_attr) { struct termios new_mode; if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode)) perror_on_device_and_die("%s"); /* POSIX (according to Zlotnick's book) tcsetattr returns zero if it performs *any* of the requested operations. This means it can report 'success' when it has actually failed to perform some proper subset of the requested operations. To detect this partial failure, get the current terminal attributes and compare them to the requested ones */ /* Initialize to all zeroes so there is no risk memcmp will report a spurious difference in an uninitialized portion of the structure */ memset(&new_mode, 0, sizeof(new_mode)); if (tcgetattr(STDIN_FILENO, &new_mode)) perror_on_device_and_die("%s"); if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) { /* * I think the below chunk is not necessary on Linux. * If you are deleting it, also delete STTY_speed_was_set bit - * it is only ever checked here. */ #if 0 /* was "if CIBAUD" */ /* SunOS 4.1.3 (at least) has the problem that after this sequence, tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2); sometimes (m1 != m2). The only difference is in the four bits of the c_cflag field corresponding to the baud rate. To save Sun users a little confusion, don't report an error if this happens. But suppress the error only if we haven't tried to set the baud rate explicitly -- otherwise we'd never give an error for a true failure to set the baud rate */ new_mode.c_cflag &= (~CIBAUD); if ((stty_state & STTY_speed_was_set) || memcmp(&mode, &new_mode, sizeof(mode)) != 0) #endif perror_on_device_and_die("%s: cannot perform all requested operations"); } } return EXIT_SUCCESS; }
int sleep_main(int argc UNUSED_PARAM, char **argv) { #if ENABLE_FEATURE_FLOAT_SLEEP double duration; struct timespec ts; #else unsigned duration; #endif ++argv; if (!*argv) bb_show_usage(); #if ENABLE_FEATURE_FLOAT_SLEEP duration = 0; do { char *arg = *argv; if (strchr(arg, '.')) { double d; char *pp; int len = strspn(arg, "0123456789."); char sv = arg[len]; arg[len] = '\0'; errno = 0; d = strtod(arg, &pp); if (errno || *pp) bb_show_usage(); arg[len] = sv; len--; sv = arg[len]; arg[len] = '1'; duration += d * xatoul_sfx(&arg[len], sfx); arg[len] = sv; } else duration += xatoul_sfx(arg, sfx); } while (*++argv); ts.tv_sec = MAXINT(typeof(ts.tv_sec)); ts.tv_nsec = 0; if (duration >= 0 && duration < ts.tv_sec) { ts.tv_sec = duration; ts.tv_nsec = (duration - ts.tv_sec) * 1000000000; } do { errno = 0; nanosleep(&ts, &ts); } while (errno == EINTR); #elif ENABLE_FEATURE_FANCY_SLEEP duration = 0; do { duration += xatou_range_sfx(*argv, 0, UINT_MAX - duration, sfx); } while (*++argv); sleep(duration); #else /* simple */ duration = xatou(*argv); sleep(duration); // Off. If it's really needed, provide example why //if (sleep(duration)) { // bb_perror_nomsg_and_die(); //} #endif return EXIT_SUCCESS; }
int rtcwake_main(int argc UNUSED_PARAM, char **argv) { unsigned opt; const char *rtcname = NULL; const char *suspend = "standby"; const char *opt_seconds; const char *opt_time; time_t rtc_time; time_t sys_time; time_t alarm_time = alarm_time; unsigned seconds = seconds; /* for compiler */ int utc = -1; int fd; #if ENABLE_LONG_OPTS static const char rtcwake_longopts[] ALIGN1 = "auto\0" No_argument "a" "local\0" No_argument "l" "utc\0" No_argument "u" "device\0" Required_argument "d" "mode\0" Required_argument "m" "seconds\0" Required_argument "s" "time\0" Required_argument "t" ; #endif opt = getopt32long(argv, /* Must have -s or -t, exclusive */ "^alud:m:s:t:" "\0" "s:t:s--t:t--s", rtcwake_longopts, &rtcname, &suspend, &opt_seconds, &opt_time); /* this is the default if (opt & RTCWAKE_OPT_AUTO) utc = -1; */ if (opt & (RTCWAKE_OPT_UTC | RTCWAKE_OPT_LOCAL)) utc = opt & RTCWAKE_OPT_UTC; if (opt & RTCWAKE_OPT_SECONDS) { /* alarm time, seconds-to-sleep (relative) */ seconds = xatou(opt_seconds); } else { /* RTCWAKE_OPT_TIME */ /* alarm time, time_t (absolute, seconds since 1/1 1970 UTC) */ if (sizeof(alarm_time) <= sizeof(long)) alarm_time = xatol(opt_time); else alarm_time = xatoll(opt_time); } if (utc == -1) utc = rtc_adjtime_is_utc(); /* the rtcname is relative to /dev */ xchdir("/dev"); /* this RTC must exist and (if we'll sleep) be wakeup-enabled */ fd = rtc_xopen(&rtcname, O_RDONLY); if (strcmp(suspend, "on") != 0) if (!may_wakeup(rtcname)) bb_error_msg_and_die("%s not enabled for wakeup events", rtcname); /* relative or absolute alarm time, normalized to time_t */ sys_time = time(NULL); { struct tm tm_time; rtc_read_tm(&tm_time, fd); rtc_time = rtc_tm2time(&tm_time, utc); } if (opt & RTCWAKE_OPT_TIME) { /* Correct for RTC<->system clock difference */ alarm_time += rtc_time - sys_time; if (alarm_time < rtc_time) /* * Compat message text. * I'd say "RTC time is already ahead of ..." instead. */ bb_error_msg_and_die("time doesn't go backward to %s", ctime(&alarm_time)); } else alarm_time = rtc_time + seconds + 1; setup_alarm(fd, &alarm_time, rtc_time); sync(); #if 0 /*debug*/ printf("sys_time: %s", ctime(&sys_time)); printf("rtc_time: %s", ctime(&rtc_time)); #endif printf("wakeup from \"%s\" at %s", suspend, ctime(&alarm_time)); fflush_all(); usleep(10 * 1000); if (strcmp(suspend, "on") != 0) xopen_xwrite_close(SYS_POWER_PATH, suspend); else { /* "fake" suspend ... we'll do the delay ourselves */ unsigned long data; do { ssize_t ret = safe_read(fd, &data, sizeof(data)); if (ret < 0) { bb_perror_msg("rtc read"); break; } } while (!(data & RTC_AF)); } xioctl(fd, RTC_AIE_OFF, 0); if (ENABLE_FEATURE_CLEAN_UP) close(fd); return EXIT_SUCCESS; }
int main(int argc, char **argv) { abrt_init(argv); /* I18n */ setlocale(LC_ALL, ""); #if ENABLE_NLS bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif const char *program_usage_string = _( "\n& [-vf] [-c CONFFILE]... [-F FMTFILE] [-A FMTFILE2] -d DIR" "\nor:" "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] FILE..." "\nor:" "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] -w" "\nor:" "\n& [-v] [-c CONFFILE]... -h DUPHASH" "\n" "\nReports problem to MantisBT." "\n" "\nThe tool reads DIR. Then it tries to find an issue" "\nwith the same abrt_hash in custom field 'abrt_hash'." "\n" "\nIf such issue is not found, then a new issue is created. Elements of DIR" "\nare stored in the issue as part of issue description or as attachments," "\ndepending on their type and size." "\n" "\nOtherwise, if such issue is found and it is marked as CLOSED DUPLICATE," "\nthe tool follows the chain of duplicates until it finds a non-DUPLICATE issue." "\nThe tool adds a new comment to found issue." "\n" "\nThe URL to new or modified issue is printed to stdout and recorded in" "\n'reported_to' element." "\n" "\nOption -t uploads FILEs to the already created issue on MantisBT site." "\nThe issue ID is retrieved from directory specified by -d DIR." "\nIf problem data in DIR was never reported to MantisBT, upload will fail." "\n" "\nOption -tID uploads FILEs to the issue with specified ID on MantisBT site." "\n-d DIR is ignored." "\n" "\nOption -r sets the last url from reporter_to element which is prefixed with" "\nTRACKER_NAME to URL field. This option is applied only when a new issue is to be" "\nfiled. The default value is 'ABRT Server'" "\n" "\nIf not specified, CONFFILE defaults to "CONF_DIR"/plugins/mantisbt.conf" "\nand user's local ~"USER_HOME_CONFIG_PATH"/mantisbt.conf." "\nIts lines should have 'PARAM = VALUE' format." "\nRecognized string parameters: MantisbtURL, Login, Password, Project, ProjectVersion." "\nRecognized boolean parameter (VALUE should be 1/0, yes/no): SSLVerify, CreatePrivate." "\nUser's local configuration overrides the system wide configuration." "\nParameters can be overridden via $Mantisbt_PARAM environment variables." "\n" "\nFMTFILE default to "CONF_DIR"/plugins/mantisbt_format.conf." "\nFMTFILE2 default to "CONF_DIR"/plugins/mantisbt_formatdup.conf." ); enum { OPT_v = 1 << 0, OPT_d = 1 << 1, OPT_c = 1 << 2, OPT_F = 1 << 3, OPT_A = 1 << 4, OPT_t = 1 << 5, OPT_f = 1 << 6, OPT_h = 1 << 7, OPT_r = 1 << 8, OPT_D = 1 << 9, }; const char *dump_dir_name = "."; GList *conf_file = NULL; const char *fmt_file = CONF_DIR"/plugins/mantisbt_format.conf"; const char *fmt_file2 = CONF_DIR"/plugins/mantisbt_formatdup.conf"; char *abrt_hash = NULL; char *ticket_no = NULL; const char *tracker_str = "ABRT Server"; char *debug_str = NULL; mantisbt_settings_t mbt_settings = { 0 }; /* Keep enum above and order of options below in sync! */ struct options program_options[] = { OPT__VERBOSE(&g_verbose), OPT_STRING( 'd', NULL, &dump_dir_name , "DIR" , _("Problem directory")), OPT_LIST( 'c', NULL, &conf_file , "FILE" , _("Configuration file (may be given many times)")), OPT_STRING( 'F', NULL, &fmt_file , "FILE" , _("Formatting file for initial comment")), OPT_STRING( 'A', NULL, &fmt_file2 , "FILE" , _("Formatting file for duplicates")), OPT_OPTSTRING('t', "ticket", &ticket_no , "ID" , _("Attach FILEs [to issue with this ID]")), OPT_BOOL( 'f', NULL, NULL, _("Force reporting even if this problem is already reported")), OPT_STRING( 'h', "duphash", &abrt_hash, "DUPHASH", _("Print BUG_ID which has given DUPHASH")), OPT_STRING( 'r', "tracker", &tracker_str, "TRACKER_NAME", _("A name of bug tracker for an additional URL from 'reported_to'")), OPT_OPTSTRING('D', "debug", &debug_str , "STR" , _("Debug")), OPT_END() }; unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); argv += optind; export_abrt_envvars(0); map_string_t *settings = new_map_string(); { char *local_conf = NULL; if (!conf_file) { conf_file = g_list_append(conf_file, (char*) CONF_DIR"/plugins/mantisbt.conf"); local_conf = xasprintf("%s"USER_HOME_CONFIG_PATH"/mantisbt.conf", getenv("HOME")); conf_file = g_list_append(conf_file, local_conf); } while (conf_file) { char *fn = (char *)conf_file->data; log_notice("Loading settings from '%s'", fn); load_conf_file(fn, settings, /*skip key w/o values:*/ false); log_debug("Loaded '%s'", fn); conf_file = g_list_delete_link(conf_file, conf_file); } free(local_conf); struct dump_dir *dd = NULL; if (abrt_hash == NULL) { dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) error_msg_and_die(_("Can't open problem dir '%s'."), dump_dir_name); } set_settings(&mbt_settings, settings, dd); dd_close(dd); /* WRONG! set_settings() does not copy the strings, it merely sets up pointers * to settings[] dictionary: */ /*free_map_string(settings);*/ } /* No connection is opened between client and server. Users authentication * is performed on every SOAP method call. In the first step we verify the * credentials by calling 'mc_login' method. In the case the credentials are * correctly applies the reporter uses them in the next requests. It is not * necessary to call 'mc_login' method because the method provides only * verification of credentials. */ verify_credentials(&mbt_settings); if (abrt_hash) { log_warning(_("Looking for similar problems in MantisBT")); GList *ids = mantisbt_search_by_abrt_hash(&mbt_settings, abrt_hash); mantisbt_settings_free(&mbt_settings); if (ids == NULL) return EXIT_FAILURE; puts(ids->data); response_values_free(ids); return EXIT_SUCCESS; } mantisbt_get_project_id_from_name(&mbt_settings); if (opts & OPT_t) { if (!argv[0]) show_usage_and_die(program_usage_string, program_options); if (!ticket_no) { struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) xfunc_die(); report_result_t *reported_to = find_in_reported_to(dd, "MantisBT"); dd_close(dd); if (!reported_to || !reported_to->url) error_msg_and_die(_("Can't get MantisBT ID because this problem has not yet been reported to MantisBT.")); char *url = reported_to->url; reported_to->url = NULL; free_report_result(reported_to); if (prefixcmp(url, mbt_settings.m_mantisbt_url) != 0) error_msg_and_die(_("This problem has been reported to MantisBT '%s' which differs from the configured MantisBT '%s'."), url, mbt_settings.m_mantisbt_url); ticket_no = strrchr(url, '='); if (!ticket_no) error_msg_and_die(_("Malformed url to MantisBT '%s'."), url); /* won't ever call free on it - it simplifies the code a lot */ ticket_no = xstrdup(ticket_no + 1); log_warning(_("Using MantisBT ID '%s'"), ticket_no); } /* Attach files to existing MantisBT issues */ while (*argv) { const char *path = *argv++; char *filename = basename(path); log_warning(_("Attaching file '%s' to issue %s"), filename, ticket_no); mantisbt_attach_file(&mbt_settings, ticket_no, filename, path); } return 0; } /* Create new issue in MantisBT */ if (!(opts & OPT_f)) { struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) xfunc_die(); report_result_t *reported_to = find_in_reported_to(dd, "MantisBT"); dd_close(dd); if (reported_to && reported_to->url) { char *msg = xasprintf(_("This problem was already reported to MantisBT (see '%s')." " Do you still want to create a new issue?"), reported_to->url); int yes = ask_yes_no(msg); free(msg); if (!yes) return 0; } free_report_result(reported_to); } problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name); if (!problem_data) xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */ const char *category = problem_data_get_content_or_die(problem_data, FILENAME_COMPONENT); const char *duphash = problem_data_get_content_or_die(problem_data, FILENAME_DUPHASH); if (opts & OPT_D) { problem_formatter_t *pf = problem_formatter_new(); problem_formatter_add_section(pf, PR_SEC_ADDITIONAL_INFO, /* optional section */ 0); if (problem_formatter_load_file(pf, fmt_file)) error_msg_and_die("Invalid format file: %s", fmt_file); problem_report_t *pr = NULL; if (problem_formatter_generate_report(pf, problem_data, &pr)) error_msg_and_die("Failed to format issue report from problem data"); printf("summary: %s\n" "\n" "Description:\n%s\n" "Additional info:\n%s\n" , problem_report_get_summary(pr) , problem_report_get_description(pr) , problem_report_get_section(pr, PR_SEC_ADDITIONAL_INFO) ); puts("attachments:"); for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a)) printf(" %s\n", (const char *)a->data); problem_report_free(pr); problem_formatter_free(pf); exit(0); } unsigned long bug_id = 0; /* If REMOTE_RESULT contains "DUPLICATE 12345", we consider it a dup of 12345 * and won't search on MantisBT server. */ char *remote_result; remote_result = problem_data_get_content_or_NULL(problem_data, FILENAME_REMOTE_RESULT); if (remote_result) { char *cmd = strtok(remote_result, " \n"); char *id = strtok(NULL, " \n"); if (!prefixcmp(cmd, "DUPLICATE")) { errno = 0; char *e; bug_id = strtoul(id, &e, 10); if (errno || id == e || *e != '\0' || bug_id > INT_MAX) { /* error / no digits / illegal trailing chars / too big a number */ bug_id = 0; } } } mantisbt_issue_info_t *ii; if (!bug_id) { log_warning(_("Checking for duplicates")); int existing_id = -1; int crossver_id = -1; { /* Figure out whether we want to match category * when doing dup search. */ const char *category_substitute = is_in_comma_separated_list(category, mbt_settings.m_DontMatchComponents) ? NULL : category; /* We don't do dup detection across versions (see below why), * but we do add a note if cross-version potential dup exists. * For that, we search for cross version dups first: */ // SOAP API searching method is not in the final version, it's possible the project will be string GList *crossver_bugs_ids = mantisbt_search_duplicate_issues(&mbt_settings, category_substitute, /*version*/ NULL, duphash); unsigned crossver_bugs_count = g_list_length(crossver_bugs_ids); log_debug("MantisBT has %i reports with duphash '%s' including cross-version ones", crossver_bugs_count, duphash); if (crossver_bugs_count > 0) crossver_id = atoi(g_list_first(crossver_bugs_ids)->data); if (crossver_bugs_count > 0) { // SOAP API searching method is not in the final version, it's possible the project will be string GList *dup_bugs_ids = mantisbt_search_duplicate_issues(&mbt_settings, category_substitute, mbt_settings.m_project_version, duphash); unsigned dup_bugs_count = g_list_length(dup_bugs_ids); log_debug("MantisBT has %i reports with duphash '%s'", dup_bugs_count, duphash); if (dup_bugs_count > 0) existing_id = atoi(g_list_first(dup_bugs_ids)->data); } } if (existing_id < 0) { /* Create new issue */ log_warning(_("Creating a new issue")); problem_formatter_t *pf = problem_formatter_new(); problem_formatter_add_section(pf, PR_SEC_ADDITIONAL_INFO, 0); if (problem_formatter_load_file(pf, fmt_file)) error_msg_and_die(_("Invalid format file: %s"), fmt_file); problem_report_t *pr = NULL; if (problem_formatter_generate_report(pf, problem_data, &pr)) error_msg_and_die(_("Failed to format problem data")); if (crossver_id >= 0) problem_report_buffer_printf( problem_report_get_buffer(pr, PR_SEC_DESCRIPTION), "\nPotential duplicate: issue %u\n", crossver_id); problem_formatter_free(pf); /* get tracker URL if exists */ struct dump_dir *dd = dd_opendir(dump_dir_name, 0); char *tracker_url = NULL; if (dd) { report_result_t *reported_to = find_in_reported_to(dd, tracker_str); dd_close(dd); if (reported_to && reported_to->url) { log_warning(_("Adding External URL to issue")); tracker_url = xstrdup(reported_to->url); free_report_result(reported_to); } } int new_id = mantisbt_create_new_issue(&mbt_settings, problem_data, pr, tracker_url); free(tracker_url); if (new_id == -1) return EXIT_FAILURE; log_warning(_("Adding attachments to issue %i"), new_id); char *new_id_str = xasprintf("%u", new_id); for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a)) { const char *item_name = (const char *)a->data; struct problem_item *item = problem_data_get_item_or_NULL(problem_data, item_name); if (!item) continue; else if (item->flags & CD_FLAG_TXT) mantisbt_attach_data(&mbt_settings, new_id_str, item_name, item->content, strlen(item->content)); else if (item->flags & CD_FLAG_BIN) mantisbt_attach_file(&mbt_settings, new_id_str, item_name, item->content); } free(new_id_str); problem_report_free(pr); ii = mantisbt_issue_info_new(); ii->mii_id = new_id; ii->mii_status = xstrdup("new"); goto finish; } bug_id = existing_id; } ii = mantisbt_get_issue_info(&mbt_settings, bug_id); log_warning(_("Bug is already reported: %i"), ii->mii_id); /* Follow duplicates */ if ((strcmp(ii->mii_status, "closed") == 0) && (strcmp(ii->mii_resolution, "duplicate") == 0) ) { mantisbt_issue_info_t *origin = mantisbt_find_origin_bug_closed_duplicate(&mbt_settings, ii); if (origin) { mantisbt_issue_info_free(ii); ii = origin; } } /* TODO CC list * Is no MantisBT SOAP API method which allows adding users to CC list * without updating issue. */ /* Add comment and bt */ const char *comment = problem_data_get_content_or_NULL(problem_data, FILENAME_COMMENT); if (comment && comment[0]) { problem_formatter_t *pf = problem_formatter_new(); if (problem_formatter_load_file(pf, fmt_file2)) error_msg_and_die(_("Invalid duplicate format file: '%s"), fmt_file2); problem_report_t *pr; if (problem_formatter_generate_report(pf, problem_data, &pr)) error_msg_and_die(_("Failed to format duplicate comment from problem data")); const char *mbtcomment = problem_report_get_description(pr); int dup_comment = is_comment_dup(ii->mii_notes, mbtcomment); if (!dup_comment) { log_warning(_("Adding new comment to issue %d"), ii->mii_id); mantisbt_add_issue_note(&mbt_settings, ii->mii_id, mbtcomment); const char *bt = problem_data_get_content_or_NULL(problem_data, FILENAME_BACKTRACE); unsigned rating = 0; const char *rating_str = problem_data_get_content_or_NULL(problem_data, FILENAME_RATING); /* python doesn't have rating file */ if (rating_str) rating = xatou(rating_str); if (bt && rating > ii->mii_best_bt_rating) { char *bug_id_str = xasprintf("%i", ii->mii_id); log_warning(_("Attaching better backtrace")); // find unique filename of attachment char *name = NULL; for (int i = 0;; ++i) { if (i == 0) name = xasprintf("%s", FILENAME_BACKTRACE); else name = xasprintf("%s%d", FILENAME_BACKTRACE, i); if (g_list_find_custom(ii->mii_attachments, name, (GCompareFunc) strcmp) == NULL) break; free(name); } mantisbt_attach_data(&mbt_settings, bug_id_str, name, bt, strlen(bt)); free(name); free(bug_id_str); } } else log_warning(_("Found the same comment in the issue history, not adding a new one")); problem_report_free(pr); problem_formatter_free(pf); } finish: log_warning(_("Status: %s%s%s %s/view.php?id=%u"), ii->mii_status, ii->mii_resolution ? " " : "", ii->mii_resolution ? ii->mii_resolution : "", mbt_settings.m_mantisbt_url, ii->mii_id); struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (dd) { report_result_t rr = { .label = (char *)"MantisBT" }; rr.url = xasprintf("%s/view.php?id=%u", mbt_settings.m_mantisbt_url, ii->mii_id); add_reported_to_entry(dd, &rr); free(rr.url); dd_close(dd); } mantisbt_settings_free(&mbt_settings); return 0; }
int main(int argc, char **argv) { abrt_init(argv); /* I18n */ setlocale(LC_ALL, ""); #if ENABLE_NLS bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( "\n& [-vbf] [-g GROUP-NAME]... [-c CONFFILE]... [-F FMTFILE] [-A FMTFILE2] -d DIR" "\nor:" "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] FILE..." "\nor:" "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] -w" "\nor:" "\n& [-v] [-c CONFFILE]... -h DUPHASH" "\n" "\nReports problem to Bugzilla." "\n" "\nThe tool reads DIR. Then it logs in to Bugzilla and tries to find a bug" "\nwith the same abrt_hash:HEXSTRING in 'Whiteboard'." "\n" "\nIf such bug is not found, then a new bug is created. Elements of DIR" "\nare stored in the bug as part of bug description or as attachments," "\ndepending on their type and size." "\n" "\nOtherwise, if such bug is found and it is marked as CLOSED DUPLICATE," "\nthe tool follows the chain of duplicates until it finds a non-DUPLICATE bug." "\nThe tool adds a new comment to found bug." "\n" "\nThe URL to new or modified bug is printed to stdout and recorded in" "\n'reported_to' element." "\n" "\nOption -t uploads FILEs to the already created bug on Bugzilla site." "\nThe bug ID is retrieved from directory specified by -d DIR." "\nIf problem data in DIR was never reported to Bugzilla, upload will fail." "\n" "\nOption -tID uploads FILEs to the bug with specified ID on Bugzilla site." "\n-d DIR is ignored." "\n" "\nOption -w adds bugzilla user to bug's CC list." "\n" "\nIf not specified, CONFFILE defaults to "CONF_DIR"/plugins/bugzilla.conf" "\nIts lines should have 'PARAM = VALUE' format." "\nRecognized string parameters: BugzillaURL, Login, Password, OSRelease." "\nRecognized boolean parameter (VALUE should be 1/0, yes/no): SSLVerify." "\nParameters can be overridden via $Bugzilla_PARAM environment variables." "\n" "\nFMTFILE and FMTFILE2 default to "CONF_DIR"/plugins/bugzilla_format.conf" ); enum { OPT_v = 1 << 0, OPT_d = 1 << 1, OPT_c = 1 << 2, OPT_F = 1 << 3, OPT_A = 1 << 4, OPT_t = 1 << 5, OPT_b = 1 << 6, OPT_f = 1 << 7, OPT_w = 1 << 8, OPT_h = 1 << 9, OPT_g = 1 << 10, OPT_D = 1 << 11, }; const char *dump_dir_name = "."; GList *conf_file = NULL; const char *fmt_file = CONF_DIR"/plugins/bugzilla_format.conf"; const char *fmt_file2 = fmt_file; char *abrt_hash = NULL; char *ticket_no = NULL; char *debug_str = NULL; GList *group = NULL; /* Keep enum above and order of options below in sync! */ struct options program_options[] = { OPT__VERBOSE(&g_verbose), OPT_STRING( 'd', NULL, &dump_dir_name , "DIR" , _("Problem directory")), OPT_LIST( 'c', NULL, &conf_file , "FILE" , _("Configuration file (may be given many times)")), OPT_STRING( 'F', NULL, &fmt_file , "FILE" , _("Formatting file for initial comment")), OPT_STRING( 'A', NULL, &fmt_file2 , "FILE" , _("Formatting file for duplicates")), OPT_OPTSTRING('t', "ticket", &ticket_no , "ID" , _("Attach FILEs [to bug with this ID]")), OPT_BOOL( 'b', NULL, NULL, _("When creating bug, attach binary files too")), OPT_BOOL( 'f', NULL, NULL, _("Force reporting even if this problem is already reported")), OPT_BOOL( 'w', NULL, NULL, _("Add bugzilla user to CC list [of bug with this ID]")), OPT_STRING( 'h', "duphash", &abrt_hash, "DUPHASH", _("Print BUG_ID which has given DUPHASH")), OPT_LIST( 'g', "group", &group , "GROUP" , _("Restrict access to this group only")), OPT_OPTSTRING('D', "debug", &debug_str , "STR" , _("Debug")), OPT_END() }; unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); argv += optind; export_abrt_envvars(0); map_string_t *settings = new_map_string(); struct bugzilla_struct rhbz = { 0 }; { if (!conf_file) conf_file = g_list_append(conf_file, (char*) CONF_DIR"/plugins/bugzilla.conf"); while (conf_file) { char *fn = (char *)conf_file->data; VERB1 log("Loading settings from '%s'", fn); load_conf_file(fn, settings, /*skip key w/o values:*/ false); VERB3 log("Loaded '%s'", fn); conf_file = g_list_delete_link(conf_file, conf_file); } set_settings(&rhbz, settings); /* WRONG! set_settings() does not copy the strings, it merely sets up pointers * to settings[] dictionary: */ /*free_map_string(settings);*/ } VERB1 log("Initializing XML-RPC library"); xmlrpc_env env; xmlrpc_env_init(&env); xmlrpc_client_setup_global_const(&env); if (env.fault_occurred) abrt_xmlrpc_die(&env); xmlrpc_env_clean(&env); struct abrt_xmlrpc *client; client = abrt_xmlrpc_new_client(rhbz.b_bugzilla_xmlrpc, rhbz.b_ssl_verify); unsigned rhbz_ver = rhbz_version(client); if (abrt_hash) { log(_("Looking for similar problems in bugzilla")); char *hash; if (prefixcmp(abrt_hash, "abrt_hash:")) hash = xasprintf("abrt_hash:%s", abrt_hash); else hash = xstrdup(abrt_hash); /* it's Fedora specific */ xmlrpc_value *all_bugs = rhbz_search_duphash(client, /*product:*/ "Fedora", /*version:*/ NULL, /*component:*/ NULL, hash); free(hash); unsigned all_bugs_size = rhbz_array_size(all_bugs); if (all_bugs_size > 0) { int bug_id = rhbz_get_bug_id_from_array0(all_bugs, rhbz_ver); printf("%i\n", bug_id); } return EXIT_SUCCESS; } if (rhbz.b_login[0] == '\0') { free(rhbz.b_login); rhbz.b_login = ask_bz_login(_("Login is not provided by configuration. Please enter your BZ login:"******"Password is not provided by configuration. Please enter the password for '%s':"), rhbz.b_login); rhbz.b_password = ask_bz_password(question); free(question); } if (opts & OPT_t) { if ((!argv[0] && !(opts & OPT_w)) || (argv[0] && (opts & OPT_w))) show_usage_and_die(program_usage_string, program_options); if (!ticket_no) { struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) xfunc_die(); report_result_t *reported_to = find_in_reported_to(dd, "Bugzilla:"); dd_close(dd); if (!reported_to || !reported_to->url) error_msg_and_die(_("Can't get Bugzilla ID because this problem has not yet been reported to Bugzilla.")); char *url = reported_to->url; reported_to->url = NULL; free_report_result(reported_to); if (prefixcmp(url, rhbz.b_bugzilla_url) != 0) error_msg_and_die(_("This problem has been reported to Bugzilla '%s' which differs from the configured Bugzilla '%s'."), url, rhbz.b_bugzilla_url); ticket_no = strrchr(url, '='); if (!ticket_no) error_msg_and_die(_("Malformed url to Bugzilla '%s'."), url); /* won't ever call free on it - it simplifies the code a lot */ ticket_no = xstrdup(ticket_no + 1); log(_("Using Bugzilla ID '%s'"), ticket_no); } login(client, &rhbz); if (opts & OPT_w) rhbz_mail_to_cc(client, xatoi_positive(ticket_no), rhbz.b_login, /* require mail notify */ 0); else { /* Attach files to existing BZ */ while (*argv) { const char *filename = *argv++; VERB1 log("Attaching file '%s' to bug %s", filename, ticket_no); int fd = open(filename, O_RDONLY); if (fd < 0) { perror_msg("Can't open '%s'", filename); continue; } struct stat st; if (fstat(fd, &st) != 0 || !S_ISREG(st.st_mode)) { error_msg("'%s': not a regular file", filename); close(fd); continue; } rhbz_attach_fd(client, ticket_no, filename, fd, /*flags*/ 0); close(fd); } } log(_("Logging out")); rhbz_logout(client); #if 0 /* enable if you search for leaks (valgrind etc) */ abrt_xmlrpc_free_client(client); #endif return 0; } /* Create new bug in Bugzilla */ if (!(opts & OPT_f)) { struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) xfunc_die(); report_result_t *reported_to = find_in_reported_to(dd, "Bugzilla:"); dd_close(dd); if (reported_to && reported_to->url) { char *msg = xasprintf("This problem was already reported to Bugzilla (see '%s')." " Do you still want to create a new bug?", reported_to->url); int yes = ask_yes_no(msg); free(msg); if (!yes) return 0; } free_report_result(reported_to); } problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name); if (!problem_data) xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */ const char *component = problem_data_get_content_or_die(problem_data, FILENAME_COMPONENT); const char *duphash = problem_data_get_content_or_NULL(problem_data, FILENAME_DUPHASH); //COMPAT, remove after 2.1 release if (!duphash) duphash = problem_data_get_content_or_die(problem_data, "global_uuid"); if (!rhbz.b_product || !*rhbz.b_product) /* if not overridden or empty... */ { free(rhbz.b_product); free(rhbz.b_product_version); map_string_t *osinfo = new_map_string(); problem_data_get_osinfo(problem_data, osinfo); parse_osinfo_for_bz(osinfo, &rhbz.b_product, &rhbz.b_product_version); free_map_string(osinfo); if (!rhbz.b_product) error_msg_and_die(_("Can't determine Bugzilla Product from problem data.")); } if (opts & OPT_D) { GList *comment_fmt_spec = load_bzrep_conf_file(fmt_file); struct strbuf *bzcomment_buf = strbuf_new(); generate_bz_comment(bzcomment_buf, problem_data, comment_fmt_spec); char *bzcomment = strbuf_free_nobuf(bzcomment_buf); char *summary = create_summary_string(problem_data, comment_fmt_spec); printf("summary: %s\n" "\n" "%s" , summary, bzcomment ); free(bzcomment); free(summary); exit(0); } login(client, &rhbz); int bug_id = 0; /* If REMOTE_RESULT contains "DUPLICATE 12345", we consider it a dup of 12345 * and won't search on bz server. */ char *remote_result; remote_result = problem_data_get_content_or_NULL(problem_data, FILENAME_REMOTE_RESULT); if (remote_result) { char *cmd = strtok(remote_result, " \n"); char *id = strtok(NULL, " \n"); if (!prefixcmp(cmd, "DUPLICATE")) { errno = 0; char *e; bug_id = strtoul(id, &e, 10); if (errno || id == e || *e != '\0' || bug_id > INT_MAX) { /* error / no digits / illegal trailing chars / too big a number */ bug_id = 0; } } } struct bug_info *bz = NULL; if (!bug_id) { log(_("Checking for duplicates")); int existing_id = -1; int crossver_id = -1; { /* Figure out whether we want to match component * when doing dup search. */ const char *component_substitute = is_in_comma_separated_list(component, rhbz.b_DontMatchComponents) ? NULL : component; /* We don't do dup detection across versions (see below why), * but we do add a note if cross-version potential dup exists. * For that, we search for cross version dups first: */ xmlrpc_value *crossver_bugs = rhbz_search_duphash(client, rhbz.b_product, /*version:*/ NULL, component_substitute, duphash); unsigned crossver_bugs_count = rhbz_array_size(crossver_bugs); VERB3 log("Bugzilla has %i reports with duphash '%s' including cross-version ones", crossver_bugs_count, duphash); if (crossver_bugs_count > 0) crossver_id = rhbz_get_bug_id_from_array0(crossver_bugs, rhbz_ver); xmlrpc_DECREF(crossver_bugs); if (crossver_bugs_count > 0) { /* In dup detection we require match in product *and version*. * Otherwise we sometimes have bugs in e.g. Fedora 17 * considered to be dups of Fedora 16 bugs. * Imagine that F16 is "end-of-lifed" - allowing cross-version * match will make all newly detected crashes DUPed * to a bug in a dead release. */ xmlrpc_value *dup_bugs = rhbz_search_duphash(client, rhbz.b_product, rhbz.b_product_version, component_substitute, duphash); unsigned dup_bugs_count = rhbz_array_size(dup_bugs); VERB3 log("Bugzilla has %i reports with duphash '%s'", dup_bugs_count, duphash); if (dup_bugs_count > 0) existing_id = rhbz_get_bug_id_from_array0(dup_bugs, rhbz_ver); xmlrpc_DECREF(dup_bugs); } } if (existing_id < 0) { /* Create new bug */ log(_("Creating a new bug")); GList *comment_fmt_spec = load_bzrep_conf_file(fmt_file); struct strbuf *bzcomment_buf = strbuf_new(); generate_bz_comment(bzcomment_buf, problem_data, comment_fmt_spec); if (crossver_id >= 0) strbuf_append_strf(bzcomment_buf, "\nPotential duplicate: bug %u\n", crossver_id); char *bzcomment = strbuf_free_nobuf(bzcomment_buf); char *summary = create_summary_string(problem_data, comment_fmt_spec); int new_id = rhbz_new_bug(client, problem_data, rhbz.b_product, rhbz.b_product_version, summary, bzcomment, group ); free(bzcomment); free(summary); if (new_id == -1) { error_msg_and_die(_("Failed to create a new bug.")); } log(_("Adding attachments to bug %i"), new_id); char new_id_str[sizeof(int)*3 + 2]; sprintf(new_id_str, "%i", new_id); attach_files(client, new_id_str, problem_data, comment_fmt_spec); //TODO: free_comment_fmt_spec(comment_fmt_spec); bz = new_bug_info(); bz->bi_status = xstrdup("NEW"); bz->bi_id = new_id; goto log_out; } bug_id = existing_id; } bz = rhbz_bug_info(client, bug_id); log(_("Bug is already reported: %i"), bz->bi_id); /* Follow duplicates */ if ((strcmp(bz->bi_status, "CLOSED") == 0) && (strcmp(bz->bi_resolution, "DUPLICATE") == 0) ) { struct bug_info *origin; origin = rhbz_find_origin_bug_closed_duplicate(client, bz); if (origin) { free_bug_info(bz); bz = origin; } } if (strcmp(bz->bi_status, "CLOSED") != 0) { /* Add user's login to CC if not there already */ if (strcmp(bz->bi_reporter, rhbz.b_login) != 0 && !g_list_find_custom(bz->bi_cc_list, rhbz.b_login, (GCompareFunc)g_strcmp0) ) { log(_("Adding %s to CC list"), rhbz.b_login); rhbz_mail_to_cc(client, bz->bi_id, rhbz.b_login, RHBZ_NOMAIL_NOTIFY); } /* Add comment and bt */ const char *comment = problem_data_get_content_or_NULL(problem_data, FILENAME_COMMENT); if (comment && comment[0]) { GList *comment_fmt_spec = load_bzrep_conf_file(fmt_file2); struct strbuf *bzcomment_buf = strbuf_new(); generate_bz_comment(bzcomment_buf, problem_data, comment_fmt_spec); char *bzcomment = strbuf_free_nobuf(bzcomment_buf); //TODO: free_comment_fmt_spec(comment_fmt_spec); int dup_comment = is_comment_dup(bz->bi_comments, bzcomment); if (!dup_comment) { log(_("Adding new comment to bug %d"), bz->bi_id); rhbz_add_comment(client, bz->bi_id, bzcomment, 0); free(bzcomment); const char *bt = problem_data_get_content_or_NULL(problem_data, FILENAME_BACKTRACE); unsigned rating = 0; const char *rating_str = problem_data_get_content_or_NULL(problem_data, FILENAME_RATING); /* python doesn't have rating file */ if (rating_str) rating = xatou(rating_str); if (bt && rating > bz->bi_best_bt_rating) { char bug_id_str[sizeof(int)*3 + 2]; sprintf(bug_id_str, "%i", bz->bi_id); log(_("Attaching better backtrace")); rhbz_attach_blob(client, bug_id_str, FILENAME_BACKTRACE, bt, strlen(bt), RHBZ_NOMAIL_NOTIFY); } } else { free(bzcomment); log(_("Found the same comment in the bug history, not adding a new one")); } } } log_out: log(_("Logging out")); rhbz_logout(client); log(_("Status: %s%s%s %s/show_bug.cgi?id=%u"), bz->bi_status, bz->bi_resolution ? " " : "", bz->bi_resolution ? bz->bi_resolution : "", rhbz.b_bugzilla_url, bz->bi_id); struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (dd) { char *msg = xasprintf("Bugzilla: URL=%s/show_bug.cgi?id=%u", rhbz.b_bugzilla_url, bz->bi_id); add_reported_to(dd, msg); free(msg); dd_close(dd); } #if 0 /* enable if you search for leaks (valgrind etc) */ free(rhbz.b_product); free(rhbz.b_product_version); problem_data_free(problem_data); free_bug_info(bz); abrt_xmlrpc_free_client(client); #endif return 0; }
int microcom_main(int argc, char **argv) { struct pollfd pfd[2]; #define sfd (pfd[1].fd) char *device_lock_file = NULL; const char *s; const char *opt_s = "9600"; unsigned speed; int len; int exitcode = 1; struct termios tio0, tiosfd, tio; getopt32(argv, "s:", &opt_s); argc -= optind; argv += optind; if (!argv[0]) bb_show_usage(); speed = xatou(opt_s); // try to create lock file in /var/lock s = bb_basename(argv[0]); if (!s[0]) { errno = ENODEV; bb_perror_msg_and_die("can't lock device"); } device_lock_file = xasprintf("/var/lock/LCK..%s", s); sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644); if (sfd < 0) { if (ENABLE_FEATURE_CLEAN_UP) free(device_lock_file); device_lock_file = NULL; if (errno == EEXIST) bb_perror_msg_and_die("can't lock device"); // We don't abort on other errors: /var/lock can be // non-writable or non-existent } else { // %4d to make mgetty happy. It treats 4-bytes lock files as binary, // not text, PID. Making 5+ char file. Brrr... s = xasprintf("%4d\n", getpid()); write(sfd, s, strlen(s)); if (ENABLE_FEATURE_CLEAN_UP) free((char*)s); close(sfd); } // open device sfd = open(argv[0], O_RDWR); if (sfd < 0) { bb_perror_msg("can't open device"); goto unlock_and_exit; } // put stdin to "raw mode", handle one character at a time tcgetattr(STDIN_FILENO, &tio0); tio = tio0; tio.c_lflag &= ~(ICANON|ECHO); tio.c_iflag &= ~(IXON|ICRNL); tio.c_oflag &= ~(ONLCR); tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; if (tcsetattr(STDIN_FILENO, TCSANOW, &tio)) { bb_perror_msg("can't tcsetattr for %s", "stdin"); goto unlock_and_exit; } /* same thing for modem (plus: set baud rate) - TODO: make CLI option */ tcgetattr(sfd, &tiosfd); tio = tiosfd; tio.c_lflag &= ~(ICANON|ECHO); tio.c_iflag &= ~(IXON|ICRNL); tio.c_oflag &= ~(ONLCR); tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; cfsetispeed(&tio, tty_value_to_baud(speed)); cfsetospeed(&tio, tty_value_to_baud(speed)); if (tcsetattr(sfd, TCSANOW, &tio)) { bb_perror_msg("can't tcsetattr for %s", "device"); goto unlock_and_exit; } // disable SIGINT signal(SIGINT, SIG_IGN); // drain stdin tcflush(STDIN_FILENO, TCIFLUSH); printf("connected to '%s' (%d bps), exit with ctrl-X...\r\n", argv[0], speed); // main loop: check with poll(), then read/write bytes across pfd[0].fd = STDIN_FILENO; pfd[0].events = POLLIN; /*pfd[1].fd = sfd;*/ pfd[1].events = POLLIN; while (1) { int i; safe_poll(pfd, 2, -1); for (i = 0; i < 2; ++i) { if (pfd[i].revents & POLLIN) { len = read(pfd[i].fd, bb_common_bufsiz1, COMMON_BUFSIZE); if (len > 0) { if (!i && 24 == bb_common_bufsiz1[0]) goto done; // ^X exits write(pfd[1-i].fd, bb_common_bufsiz1, len); } } } } done: tcsetattr(sfd, TCSANOW, &tiosfd); tcsetattr(STDIN_FILENO, TCSANOW, &tio0); tcflush(STDIN_FILENO, TCIFLUSH); if (ENABLE_FEATURE_CLEAN_UP) close(sfd); exitcode = 0; unlock_and_exit: // delete lock file if (device_lock_file) { unlink(device_lock_file); if (ENABLE_FEATURE_CLEAN_UP) free(device_lock_file); } return exitcode; }
static tcflag_t *get_ptr_to_tcflag(unsigned type, const struct termios *mode) { static const uint8_t tcflag_offsets[] ALIGN1 = { offsetof(struct termios, c_cflag), /* control */ offsetof(struct termios, c_iflag), /* input */ offsetof(struct termios, c_oflag), /* output */ offsetof(struct termios, c_lflag) /* local */ }; if (type <= local) { return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]); } return NULL; } /* Flags for 'struct mode_info' */ #define SANE_SET 1 /* Set in 'sane' mode */ #define SANE_UNSET 2 /* Unset in 'sane' mode */ #define REV 4 /* Can be turned off by prepending '-' */ #define OMIT 8 /* Don't display value */ /* Each mode. * This structure should be kept as small as humanly possible. */ struct mode_info { const uint8_t type; /* Which structure element to change */ const uint8_t flags; /* Setting and display options */ /* only these values are ever used, so... */ #if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100 const uint8_t mask; #elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000 const uint16_t mask; #else const tcflag_t mask; /* Other bits to turn off for this mode */ #endif /* was using short here, but ppc32 was unhappy */ const tcflag_t bits; /* Bits to set for this mode */ }; enum { /* Must match mode_name[] and mode_info[] order! */ IDX_evenp = 0, IDX_parity, IDX_oddp, IDX_nl, IDX_ek, IDX_sane, IDX_cooked, IDX_raw, IDX_pass8, IDX_litout, IDX_cbreak, IDX_crt, IDX_dec, #if IXANY IDX_decctlq, #endif #if TABDLY || OXTABS IDX_tabs, #endif #if XCASE && IUCLC && OLCUC IDX_lcase, IDX_LCASE, #endif }; #define MI_ENTRY(N,T,F,B,M) N "\0" /* Mode names given on command line */ static const char mode_name[] = MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) MI_ENTRY("ek", combination, OMIT, 0, 0 ) MI_ENTRY("sane", combination, OMIT, 0, 0 ) MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) MI_ENTRY("crt", combination, OMIT, 0, 0 ) MI_ENTRY("dec", combination, OMIT, 0, 0 ) #if IXANY MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ) #endif #if TABDLY || OXTABS MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ) #endif #if XCASE && IUCLC && OLCUC MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ) MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ) #endif MI_ENTRY("parenb", control, REV, PARENB, 0 ) MI_ENTRY("parodd", control, REV, PARODD, 0 ) MI_ENTRY("cs5", control, 0, CS5, CSIZE) MI_ENTRY("cs6", control, 0, CS6, CSIZE) MI_ENTRY("cs7", control, 0, CS7, CSIZE) MI_ENTRY("cs8", control, 0, CS8, CSIZE) MI_ENTRY("hupcl", control, REV, HUPCL, 0 ) MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ) MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ) MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ) MI_ENTRY("clocal", control, REV, CLOCAL, 0 ) #if CRTSCTS MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ) #endif MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ) MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ) MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ) MI_ENTRY("parmrk", input, REV, PARMRK, 0 ) MI_ENTRY("inpck", input, REV, INPCK, 0 ) MI_ENTRY("istrip", input, REV, ISTRIP, 0 ) MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ) MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ) MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) MI_ENTRY("ixon", input, REV, IXON, 0 ) MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 ) #if IUCLC MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) #endif #if IXANY MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ) #endif #if IMAXBEL MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ) #endif #if IUTF8 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 ) #endif MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ) #if OLCUC MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ) #endif #if OCRNL MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ) #endif #if ONLCR MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ) #endif #if ONOCR MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ) #endif #if ONLRET MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ) #endif #if OFILL MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ) #endif #if OFDEL MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ) #endif #if NLDLY MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY) MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY) #endif #if CRDLY MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY) MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY) MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY) MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY) #endif #if TABDLY MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY) # if TAB2 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY) # endif # if TAB1 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY) # endif MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY) #else # if OXTABS MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ) # endif #endif #if BSDLY MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY) MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY) #endif #if VTDLY MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY) MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY) #endif #if FFDLY MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY) MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY) #endif MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ) MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ) #if IEXTEN MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) #endif MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) #if XCASE MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ) #endif #if TOSTOP MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ) #endif #if ECHOPRT MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 ) #endif #if ECHOCTL MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 ) #endif #if ECHOKE MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 ) #endif ; #undef MI_ENTRY #define MI_ENTRY(N,T,F,B,M) { T, F, M, B }, static const struct mode_info mode_info[] = { /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */ MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) MI_ENTRY("ek", combination, OMIT, 0, 0 ) MI_ENTRY("sane", combination, OMIT, 0, 0 ) MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) MI_ENTRY("crt", combination, OMIT, 0, 0 ) MI_ENTRY("dec", combination, OMIT, 0, 0 ) #if IXANY MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ) #endif #if TABDLY || OXTABS MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ) #endif #if XCASE && IUCLC && OLCUC MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ) MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ) #endif MI_ENTRY("parenb", control, REV, PARENB, 0 ) MI_ENTRY("parodd", control, REV, PARODD, 0 ) MI_ENTRY("cs5", control, 0, CS5, CSIZE) MI_ENTRY("cs6", control, 0, CS6, CSIZE) MI_ENTRY("cs7", control, 0, CS7, CSIZE) MI_ENTRY("cs8", control, 0, CS8, CSIZE) MI_ENTRY("hupcl", control, REV, HUPCL, 0 ) MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ) MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ) MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ) MI_ENTRY("clocal", control, REV, CLOCAL, 0 ) #if CRTSCTS MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ) #endif MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ) MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ) MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ) MI_ENTRY("parmrk", input, REV, PARMRK, 0 ) MI_ENTRY("inpck", input, REV, INPCK, 0 ) MI_ENTRY("istrip", input, REV, ISTRIP, 0 ) MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ) MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ) MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) MI_ENTRY("ixon", input, REV, IXON, 0 ) MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 ) #if IUCLC MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) #endif #if IXANY MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ) #endif #if IMAXBEL MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ) #endif #if IUTF8 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 ) #endif MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ) #if OLCUC MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ) #endif #if OCRNL MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ) #endif #if ONLCR MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ) #endif #if ONOCR MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ) #endif #if ONLRET MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ) #endif #if OFILL MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ) #endif #if OFDEL MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ) #endif #if NLDLY MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY) MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY) #endif #if CRDLY MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY) MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY) MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY) MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY) #endif #if TABDLY MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY) # if TAB2 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY) # endif # if TAB1 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY) # endif MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY) #else # if OXTABS MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ) # endif #endif #if BSDLY MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY) MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY) #endif #if VTDLY MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY) MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY) #endif #if FFDLY MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY) MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY) #endif MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ) MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ) #if IEXTEN MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) #endif MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) #if XCASE MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ) #endif #if TOSTOP MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ) #endif #if ECHOPRT MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 ) #endif #if ECHOCTL MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 ) #endif #if ECHOKE MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 ) #endif }; enum { NUM_mode_info = ARRAY_SIZE(mode_info) }; /* Control characters */ struct control_info { const uint8_t saneval; /* Value to set for 'stty sane' */ const uint8_t offset; /* Offset in c_cc */ }; enum { /* Must match control_name[] and control_info[] order! */ CIDX_intr = 0, CIDX_quit, CIDX_erase, CIDX_kill, CIDX_eof, CIDX_eol, #if VEOL2 CIDX_eol2, #endif #if VSWTCH CIDX_swtch, #endif CIDX_start, CIDX_stop, CIDX_susp, #if VDSUSP CIDX_dsusp, #endif #if VREPRINT CIDX_rprnt, #endif #if VWERASE CIDX_werase, #endif #if VLNEXT CIDX_lnext, #endif #if VFLUSHO CIDX_flush, #endif #if VSTATUS CIDX_status, #endif CIDX_min, CIDX_time, }; #define CI_ENTRY(n,s,o) n "\0" /* Name given on command line */ static const char control_name[] = CI_ENTRY("intr", CINTR, VINTR ) CI_ENTRY("quit", CQUIT, VQUIT ) CI_ENTRY("erase", CERASE, VERASE ) CI_ENTRY("kill", CKILL, VKILL ) CI_ENTRY("eof", CEOF, VEOF ) CI_ENTRY("eol", CEOL, VEOL ) #if VEOL2 CI_ENTRY("eol2", CEOL2, VEOL2 ) #endif #if VSWTCH CI_ENTRY("swtch", CSWTCH, VSWTCH ) #endif CI_ENTRY("start", CSTART, VSTART ) CI_ENTRY("stop", CSTOP, VSTOP ) CI_ENTRY("susp", CSUSP, VSUSP ) #if VDSUSP CI_ENTRY("dsusp", CDSUSP, VDSUSP ) #endif #if VREPRINT CI_ENTRY("rprnt", CRPRNT, VREPRINT) #endif #if VWERASE CI_ENTRY("werase", CWERASE, VWERASE ) #endif #if VLNEXT CI_ENTRY("lnext", CLNEXT, VLNEXT ) #endif #if VFLUSHO CI_ENTRY("flush", CFLUSHO, VFLUSHO ) #endif #if VSTATUS CI_ENTRY("status", CSTATUS, VSTATUS ) #endif /* These must be last because of the display routines */ CI_ENTRY("min", 1, VMIN ) CI_ENTRY("time", 0, VTIME ) ; #undef CI_ENTRY #define CI_ENTRY(n,s,o) { s, o }, static const struct control_info control_info[] = { /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */ CI_ENTRY("intr", CINTR, VINTR ) CI_ENTRY("quit", CQUIT, VQUIT ) CI_ENTRY("erase", CERASE, VERASE ) CI_ENTRY("kill", CKILL, VKILL ) CI_ENTRY("eof", CEOF, VEOF ) CI_ENTRY("eol", CEOL, VEOL ) #if VEOL2 CI_ENTRY("eol2", CEOL2, VEOL2 ) #endif #if VSWTCH CI_ENTRY("swtch", CSWTCH, VSWTCH ) #endif CI_ENTRY("start", CSTART, VSTART ) CI_ENTRY("stop", CSTOP, VSTOP ) CI_ENTRY("susp", CSUSP, VSUSP ) #if VDSUSP CI_ENTRY("dsusp", CDSUSP, VDSUSP ) #endif #if VREPRINT CI_ENTRY("rprnt", CRPRNT, VREPRINT) #endif #if VWERASE CI_ENTRY("werase", CWERASE, VWERASE ) #endif #if VLNEXT CI_ENTRY("lnext", CLNEXT, VLNEXT ) #endif #if VFLUSHO CI_ENTRY("flush", CFLUSHO, VFLUSHO ) #endif #if VSTATUS CI_ENTRY("status", CSTATUS, VSTATUS ) #endif /* These must be last because of the display routines */ CI_ENTRY("min", 1, VMIN ) CI_ENTRY("time", 0, VTIME ) }; enum { NUM_control_info = ARRAY_SIZE(control_info) }; struct globals { const char *device_name; /* The width of the screen, for output wrapping */ unsigned max_col; /* Current position, to know when to wrap */ unsigned current_col; char buf[10]; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { \ G.device_name = bb_msg_standard_input; \ G.max_col = 80; \ } while (0) static void set_speed_or_die(enum speed_setting type, const char *arg, struct termios *mode) { speed_t baud; baud = tty_value_to_baud(xatou(arg)); if (type != output_speed) { /* either input or both */ cfsetispeed(mode, baud); } if (type != input_speed) { /* either output or both */ cfsetospeed(mode, baud); } }
int arping_main(int argc, char **argv) { const char *device = "eth0"; int ifindex; char *source = NULL; char *target; s = xsocket(PF_PACKET, SOCK_DGRAM, 0); // Drop suid root privileges xsetuid(getuid()); { unsigned opt; char *_count, *_timeout; /* Dad also sets quit_on_reply. * Advert also sets unsolicited. */ opt_complementary = "Df:AU"; opt = getopt32(argc, argv, "DUAqfbc:w:i:s:", &_count, &_timeout, &device, &source); cfg |= opt & 0x3f; /* set respective flags */ if (opt & 0x40) /* -c: count */ count = xatou(_count); if (opt & 0x80) /* -w: timeout */ timeout = xatoul_range(_timeout, 0, INT_MAX/2000); //if (opt & 0x100) /* -i: interface */ if (strlen(device) > IF_NAMESIZE) { bb_error_msg_and_die("interface name '%s' is too long", device); } //if (opt & 0x200) /* -s: source */ } argc -= optind; argv += optind; if (argc != 1) bb_show_usage(); target = *argv; xfunc_error_retval = 2; { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, device, IFNAMSIZ - 1); if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { bb_error_msg_and_die("interface %s not found", device); } ifindex = ifr.ifr_ifindex; if (ioctl(s, SIOCGIFFLAGS, (char *) &ifr)) { bb_error_msg_and_die("SIOCGIFFLAGS"); } if (!(ifr.ifr_flags & IFF_UP)) { bb_error_msg_and_die("interface %s is down", device); } if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) { bb_error_msg("interface %s is not ARPable", device); return (cfg & dad ? 0 : 2); } } if (!inet_aton(target, &dst)) { len_and_sockaddr *lsa; lsa = xhost_and_af2sockaddr(target, 0, AF_INET); memcpy(&dst, &lsa->sin.sin_addr.s_addr, 4); if (ENABLE_FEATURE_CLEAN_UP) free(lsa); } if (source && !inet_aton(source, &src)) { bb_error_msg_and_die("invalid source address %s", source); } if (!(cfg & dad) && (cfg & unsolicited) && src.s_addr == 0) src = dst; if (!(cfg & dad) || src.s_addr) { struct sockaddr_in saddr; int probe_fd = xsocket(AF_INET, SOCK_DGRAM, 0); if (device) { if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) + 1) == -1) bb_error_msg("warning: interface %s is ignored", device); } memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; if (src.s_addr) { saddr.sin_addr = src; xbind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)); } else if (!(cfg & dad)) { socklen_t alen = sizeof(saddr); saddr.sin_port = htons(1025); saddr.sin_addr = dst; if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, &const_int_1, sizeof(const_int_1)) == -1) bb_perror_msg("warning: setsockopt(SO_DONTROUTE)"); xconnect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)); if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) == -1) { bb_error_msg_and_die("getsockname"); } src = saddr.sin_addr; } close(probe_fd); } me.sll_family = AF_PACKET; me.sll_ifindex = ifindex; me.sll_protocol = htons(ETH_P_ARP); xbind(s, (struct sockaddr *) &me, sizeof(me)); { socklen_t alen = sizeof(me); if (getsockname(s, (struct sockaddr *) &me, &alen) == -1) { bb_error_msg_and_die("getsockname"); } } if (me.sll_halen == 0) { bb_error_msg("interface \"%s\" is not ARPable (no ll address)", device); return (cfg & dad ? 0 : 2); } he = me; memset(he.sll_addr, -1, he.sll_halen); if (!(cfg & quiet)) { printf("ARPING to %s from %s via %s\n", inet_ntoa(dst), inet_ntoa(src), device ? device : "unknown"); } if (!src.s_addr && !(cfg & dad)) { bb_error_msg_and_die("no src address in the non-DAD mode"); } { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_RESTART; sa.sa_handler = (void (*)(int)) finish; sigaction(SIGINT, &sa, NULL); sa.sa_handler = (void (*)(int)) catcher; sigaction(SIGALRM, &sa, NULL); } catcher(); while (1) { sigset_t sset, osset; RESERVE_CONFIG_UBUFFER(packet, 4096); struct sockaddr_ll from; socklen_t alen = sizeof(from); int cc; cc = recvfrom(s, packet, 4096, 0, (struct sockaddr *) &from, &alen); if (cc < 0) { bb_perror_msg("recvfrom"); continue; } sigemptyset(&sset); sigaddset(&sset, SIGALRM); sigaddset(&sset, SIGINT); sigprocmask(SIG_BLOCK, &sset, &osset); recv_pack(packet, cc, &from); sigprocmask(SIG_SETMASK, &osset, NULL); RELEASE_CONFIG_BUFFER(packet); } }
/* Parse Threshold Rule option parameters for each RULE 'threshold: type limit|threshold|both, track by_src|by_dst, count #, seconds #;' */ void ParseThreshold2( THDX_STRUCT * thdx, char * s ) { int i = 0; char * argv[100], * t; int argc; int count_flag=0; int seconds_flag=0; int type_flag=0; int tracking_flag=0; if( !s_enabled ) return ; memset( thdx, 0, sizeof(THDX_STRUCT) ); thdx->priority = -1; /* Make this lower than standalone threshold command defaults ??? */ /* Parse all of the args - they come in pairs */ for( argc=0, t = strtok(s," ,\n"); argc < 100 && t != 0 ; argc++, t = strtok(0," ,\n") ) { argv[ argc ] = t; } /* Parameter Check - enough args ?*/ if( argc != 8 ) { /* Fatal incorrect argument count */ FatalError("Threshold-RuleOptionParse: incorrect argument count, should be 4 pairs\n"); } for(i=0;i<argc;i++) { if( strcmp(argv[i],"count") == 0 ) { i++; thdx->count = xatou(argv[i],"threshold: count"); count_flag++; } else if( strcmp(argv[i],"seconds") == 0 ) { i++; thdx->seconds = xatou(argv[i],"threshold: seconds"); seconds_flag++; } else if( strcmp(argv[i],"type") == 0 ) { i++; if( strcmp(argv[i],"limit") == 0 ) { thdx->type = THD_TYPE_LIMIT; } else if( strcmp(argv[i],"threshold") == 0 ) { thdx->type = THD_TYPE_THRESHOLD; } else if( strcmp(argv[i],"both") == 0 ) { thdx->type = THD_TYPE_BOTH; } else { /* Fatal incorrect threshold type */ FatalError("Threshold-RuleOptionParse: incorrect 'type' argument \n"); } type_flag++; } else if( strcmp(argv[i],"track") == 0 ) { i++; if( strcmp(argv[i],"by_src") == 0 ) { thdx->tracking = THD_TRK_SRC; } else if( strcmp(argv[i],"by_dst") == 0 ) { thdx->tracking = THD_TRK_DST; } else { /* Fatal incorrect threshold type */ FatalError("Threshold-RuleOptionParse: incorrect tracking type\n"); } tracking_flag++; } else { /* Fatal Out Here - Unknow Option */ FatalError("Threshold-RuleOptionParse: unknown argument \n"); } } if( (count_flag + tracking_flag + type_flag + seconds_flag ) != 4 ) { /* Fatal - incorrect argument count */ FatalError("Threshold-RuleOptionParse: incorrect argument count\n"); } }
/* threshold gen_id #, sig_id #, type limit|threshold|both, track by_src|by_dst, count #, seconds # 8/25/03 - added support for a global threshold, uses sid = 0, and is applied after all other thresholding so a sid specific threshold or suppress command has precedence... */ void ParseSFThreshold( FILE * fp, char * rule ) { char **args, **oargs; int nargs, noargs; THDX_STRUCT thdx; int count_flag=0; int seconds_flag=0; int type_flag=0; int tracking_flag=0; /* int priority_flag=0; */ int genid_flag=0; int sigid_flag=0; int i; memset( &thdx, 0, sizeof(THDX_STRUCT) ); while( (*rule <= ' ') && (*rule > 0) ) rule++; /* skip whitespace */ while( (*rule > ' ') ) rule++; /* skip 'threshold' */ args = mSplit(rule,",",15,&nargs,0); /* get rule option pairs */ for( i=0; i<nargs; i++ ) { oargs = mSplit(args[i]," ",2,&noargs,0); /* get rule option pairs */ if( noargs != 2 ) { FatalError("Threshold Parse: argument pairing error\n"); } if( strcmp(oargs[0],"type")==0 ) { if( strcmp(oargs[1],"limit") == 0 ) { thdx.type = THD_TYPE_LIMIT; } else if( strcmp(oargs[1],"threshold") == 0 ) { thdx.type = THD_TYPE_THRESHOLD; } else if( strcmp(oargs[1],"both") == 0 ) { thdx.type = THD_TYPE_BOTH; } else { /* Fatal incorrect threshold type */ FatalError("Threshold-Parse: incorrect 'type' argument \n"); } type_flag++; } else if( strcmp(oargs[0],"track")==0 ) { if( strcmp(oargs[1],"by_src") == 0 ) { thdx.tracking = THD_TRK_SRC; } else if( strcmp(oargs[1],"by_dst") == 0 ) { thdx.tracking = THD_TRK_DST; } else { /* Fatal incorrect threshold type */ FatalError("Threshold-Parse: incorrect tracking type\n"); } tracking_flag++; } else if( strcmp(oargs[0],"count")==0 ) { thdx.count = xatou(oargs[1],"threshold: count"); count_flag++; } else if( strcmp(oargs[0],"seconds")==0 ) { thdx.seconds = xatou(oargs[1],"threshold: seconds"); seconds_flag++; } else if( strcmp(oargs[0],"gen_id")==0 ) { thdx.gen_id = xatou(oargs[1],"threshold: gen_id"); genid_flag++; if( oargs[1][0]== '-' ) FatalError("Threshold-Parse: gen_id < 0 not supported '%s %s'\n",oargs[0],oargs[1]); } else if( strcmp(oargs[0],"sig_id")==0 ) { thdx.sig_id = xatou(oargs[1],"threshold: sig_id"); sigid_flag++; if( oargs[1][0]== '-' ) FatalError("Threshold-Parse: sig_id < 0 not supported '%s %s'\n",oargs[0],oargs[1]); } else { /* Fatal incorrect threshold type */ FatalError("Threshold-Parse: unsupported option : %s %s\n",oargs[0],oargs[1]); } } if( (count_flag + tracking_flag + type_flag + seconds_flag + genid_flag + sigid_flag) != 6 ) { /* Fatal - incorrect argument count */ FatalError("Threshold-Parse: incorrect argument count\n"); } if( sfthreshold_create( &thdx ) ) { if( thdx.sig_id == 0 ) { FatalError("Global Threshold-Parse: could not create a threshold object -- only one per gen_id=%u!\n",thdx.gen_id); } else { if( thdx.gen_id == 0 ) { FatalError("Global Threshold-Parse: could not create a threshold object -- a gen_id < 0 requires a sig_id < 0, sig_id=%u !\n",thdx.sig_id); } else { FatalError("Threshold-Parse: could not create a threshold object -- only one per sig_id=%u!\n",thdx.sig_id); } } } mSplitFree(&args, nargs); mSplitFree(&oargs, noargs); }
unsigned type FAST_FUNC xatou()(const char *numstr) { return xatou(_sfx)(numstr, NULL); }
int beep_main(int argc, char **argv) { int speaker = get_console_fd_or_die(); unsigned tickrate_div_freq = tickrate_div_freq; /* for compiler */ unsigned length = length; unsigned delay = delay; unsigned rep = rep; int c; c = 'n'; while (c != -1) { if (c == 'n') { tickrate_div_freq = CLOCK_TICK_RATE / FREQ; length = LENGTH; delay = DELAY; rep = REPETITIONS; } c = getopt(argc, argv, "f:l:d:r:n"); /* TODO: -s, -c: * pipe stdin to stdout, but also beep after each line (-s) or char (-c) */ switch (c) { case 'f': /* TODO: what "-f 0" should do? */ tickrate_div_freq = (unsigned)CLOCK_TICK_RATE / xatou(optarg); continue; case 'l': length = xatou(optarg); continue; case 'd': /* TODO: * -d N, -D N * specify a delay of N milliseconds between repetitions. * -d specifies that this delay should only occur between beeps, * that is, it should not occur after the last repetition. * -D indicates that the delay should occur after every repetition */ delay = xatou(optarg); continue; case 'r': rep = xatou(optarg); continue; case 'n': case -1: break; default: bb_show_usage(); } while (rep) { //bb_info_msg("rep[%d] freq=%d, length=%d, delay=%d", rep, freq, length, delay); xioctl(speaker, KIOCSOUND, (void*)(long)tickrate_div_freq); usleep(1000 * length); ioctl(speaker, KIOCSOUND, (void*)0); if (--rep) usleep(delay); } } if (ENABLE_FEATURE_CLEAN_UP) close(speaker); return EXIT_SUCCESS; }
int split_main(int argc, char **argv) { unsigned suffix_len = 2; char *pfx; char *count_p; const char *sfx; off_t cnt = 1000; off_t remaining = 0; unsigned opt; ssize_t bytes_read, to_write; char *src; opt_complementary = "?2"; opt = getopt32(argv, "l:b:a:", &count_p, &count_p, &sfx); if (opt & SPLIT_OPT_l) cnt = xatoul(count_p); if (opt & SPLIT_OPT_b) cnt = xatoul_sfx(count_p, split_suffices); if (opt & SPLIT_OPT_a) suffix_len = xatou(sfx); sfx = "x"; argv += optind; if (argv[0]) { if (argv[1]) sfx = argv[1]; xmove_fd(xopen(argv[0], O_RDONLY), 0); } else { argv[0] = (char *) bb_msg_standard_input; } if (NAME_MAX < strlen(sfx) + suffix_len) bb_error_msg_and_die("suffix too long"); { char *char_p = xzalloc(suffix_len + 1); memset(char_p, 'a', suffix_len); pfx = xasprintf("%s%s", sfx, char_p); if (ENABLE_FEATURE_CLEAN_UP) free(char_p); } while (1) { bytes_read = safe_read(0, read_buffer, READ_BUFFER_SIZE); if (!bytes_read) break; if (bytes_read < 0) bb_simple_perror_msg_and_die(argv[0]); src = read_buffer; do { if (!remaining) { if (!pfx) bb_error_msg_and_die("suffixes exhausted"); xmove_fd(xopen(pfx, O_WRONLY | O_CREAT | O_TRUNC), 1); pfx = next_file(pfx, suffix_len); remaining = cnt; } if (opt & SPLIT_OPT_b) { /* split by bytes */ to_write = (bytes_read < remaining) ? bytes_read : remaining; remaining -= to_write; } else { /* split by lines */ /* can be sped up by using _memrchr_ * and writing many lines at once... */ char *end = memchr(src, '\n', bytes_read); if (end) { --remaining; to_write = end - src + 1; } else { to_write = bytes_read; } } xwrite(1, src, to_write); bytes_read -= to_write; src += to_write; } while (bytes_read); } return EXIT_SUCCESS; }
int tail_main(int argc, char **argv) { unsigned count = 10; unsigned sleep_period = 1; bool from_top; int header_threshhold = 1; const char *str_c, *str_n, *str_s; char *tailbuf; size_t tailbufsize; int taillen = 0; int newline = 0; int nfiles, nread, nwrite, seen, i, opt; int *fds; char *s, *buf; const char *fmt; #if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_TAIL /* Allow legacy syntax of an initial numeric option without -n. */ if (argc >= 2 && (argv[1][0] == '+' || argv[1][0] == '-') && isdigit(argv[1][1]) ) { argv[0] = (char*)"-n"; argv--; argc++; } #endif opt = getopt32(argc, argv, "fc:n:" USE_FEATURE_FANCY_TAIL("qs:v"), &str_c, &str_n, &str_s); #define FOLLOW (opt & 0x1) #define COUNT_BYTES (opt & 0x2) //if (opt & 0x1) // -f if (opt & 0x2) count = eat_num(str_c); // -c if (opt & 0x4) count = eat_num(str_n); // -n #if ENABLE_FEATURE_FANCY_TAIL if (opt & 0x8) header_threshhold = INT_MAX; // -q if (opt & 0x10) sleep_period = xatou(str_s); // -s if (opt & 0x20) header_threshhold = 0; // -v #endif argc -= optind; argv += optind; from_top = status; /* open all the files */ fds = xmalloc(sizeof(int) * (argc + 1)); status = nfiles = i = 0; if (argc == 0) { struct stat statbuf; if (!fstat(STDIN_FILENO, &statbuf) && S_ISFIFO(statbuf.st_mode)) { opt &= ~1; /* clear FOLLOW */ } *argv = (char *) bb_msg_standard_input; goto DO_STDIN; } do { if (NOT_LONE_DASH(argv[i])) { fds[nfiles] = open(argv[i], O_RDONLY); if (fds[nfiles] < 0) { bb_perror_msg("%s", argv[i]); status = EXIT_FAILURE; continue; } } else { DO_STDIN: /* "-" */ fds[nfiles] = STDIN_FILENO; } argv[nfiles] = argv[i]; ++nfiles; } while (++i < argc); if (!nfiles) bb_error_msg_and_die("no files"); tailbufsize = BUFSIZ; /* tail the files */ if (!from_top && COUNT_BYTES) { if (tailbufsize < count) { tailbufsize = count + BUFSIZ; } } buf = tailbuf = xmalloc(tailbufsize); fmt = header_fmt + 1; /* Skip header leading newline on first output. */ i = 0; do { /* Be careful. It would be possible to optimize the count-bytes * case if the file is seekable. If you do though, remember that * starting file position may not be the beginning of the file. * Beware of backing up too far. See example in wc.c. */ if (!(count | from_top) && lseek(fds[i], 0, SEEK_END) >= 0) { continue; } if (nfiles > header_threshhold) { tail_xprint_header(fmt, argv[i]); fmt = header_fmt; } buf = tailbuf; taillen = 0; seen = 1; newline = 0; while ((nread = tail_read(fds[i], buf, tailbufsize-taillen)) > 0) { if (from_top) { nwrite = nread; if (seen < count) { if (COUNT_BYTES) { nwrite -= (count - seen); seen = count; } else { s = buf; do { --nwrite; if (*s++ == '\n' && ++seen == count) { break; } } while (nwrite); } } xwrite(STDOUT_FILENO, buf + nread - nwrite, nwrite); } else if (count) { if (COUNT_BYTES) { taillen += nread; if (taillen > count) { memmove(tailbuf, tailbuf + taillen - count, count); taillen = count; } } else { int k = nread; int nbuf = 0; while (k) { --k; if (buf[k] == '\n') { ++nbuf; } } if (newline + nbuf < count) { newline += nbuf; taillen += nread; } else { int extra = 0; if (buf[nread-1] != '\n') { extra = 1; } k = newline + nbuf + extra - count; s = tailbuf; while (k) { if (*s == '\n') { --k; } ++s; } taillen += nread - (s - tailbuf); memmove(tailbuf, s, taillen); newline = count - extra; } if (tailbufsize < taillen + BUFSIZ) { tailbufsize = taillen + BUFSIZ; tailbuf = xrealloc(tailbuf, tailbufsize); } } buf = tailbuf + taillen; } } if (!from_top) { xwrite(STDOUT_FILENO, tailbuf, taillen); } taillen = 0; } while (++i < nfiles); buf = xrealloc(tailbuf, BUFSIZ); fmt = NULL; if (FOLLOW) while (1) { sleep(sleep_period); i = 0; do { if (nfiles > header_threshhold) { fmt = header_fmt; } while ((nread = tail_read(fds[i], buf, sizeof(buf))) > 0) { if (fmt) { tail_xprint_header(fmt, argv[i]); fmt = NULL; } xwrite(STDOUT_FILENO, buf, nread); } } while (++i < nfiles); } return status; }