aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2015-05-15 10:23:51 +0200
committerBjørn Mork <bjorn@mork.no>2015-05-15 10:23:51 +0200
commit02013228914a1d17e8df15d4e2b7950469395a5c (patch)
tree48d2fbe2f5a5adb60cbeabc26fadaec8e0fa82ed
parent9b3dbb454e8f8a463d5fe4541ee2001585527bc6 (diff)
ripe-atlas-fw: imported version 45204520
Signed-off-by: Bjørn Mork <bjorn@mork.no>
-rw-r--r--.config888
-rw-r--r--.gitignore54
-rw-r--r--AUTHORS6
-rw-r--r--Config.in1
-rw-r--r--INSTALL14
-rw-r--r--Makefile1
-rw-r--r--Makefile.flags5
-rw-r--r--coreutils/Config.in20
-rw-r--r--coreutils/Kbuild2
-rw-r--r--coreutils/buddyinfo.c72
-rw-r--r--coreutils/condmv.c100
-rw-r--r--coreutils/date.c40
-rw-r--r--coreutils/dfrm.c123
-rw-r--r--coreutils/findpid.c33
-rw-r--r--coreutils/sleepkick.c74
-rw-r--r--coreutils/test.c7
-rw-r--r--eperd/Config.in56
-rw-r--r--eperd/Kbuild8
-rw-r--r--eperd/condmv.c166
-rw-r--r--eperd/eooqd.c805
-rw-r--r--eperd/eperd.c1163
-rw-r--r--eperd/eperd.h51
-rw-r--r--eperd/evhttpget.c57
-rw-r--r--eperd/evping.c58
-rw-r--r--eperd/evtdig.c2131
-rw-r--r--eperd/evtraceroute.c56
-rw-r--r--eperd/httpget.c1760
-rw-r--r--eperd/ping.c1288
-rw-r--r--eperd/readresolv.c56
-rw-r--r--eperd/readresolv.h5
-rw-r--r--eperd/tcputil.c249
-rw-r--r--eperd/tcputil.h42
-rw-r--r--eperd/traceroute.c2866
-rw-r--r--examples/cron.root.74
-rw-r--r--include/applets.h15
-rw-r--r--include/cmdtable.h23
-rw-r--r--include/libbb.h27
-rw-r--r--include/usage.h172
-rw-r--r--libbb/Kbuild5
-rw-r--r--libbb/atlas_bb64.c126
-rw-r--r--libbb/atlas_bb64.h18
-rw-r--r--libbb/atlas_probe.c35
-rw-r--r--libbb/atlas_probe.h6
-rw-r--r--libbb/find_pid_by_name.c2
-rw-r--r--libbb/getopt32.c22
-rw-r--r--libbb/strlcat.c46
-rw-r--r--libbb/strlcpy.c64
-rw-r--r--libbb/validate_filename.c33
-rw-r--r--libbb/xconnect.c38
-rw-r--r--libbb/xfuncs_printf.c56
-rwxr-xr-xlibevent-2.0.20-stable/configure2
-rw-r--r--libevent-2.0.20-stable/evdns.c5
-rw-r--r--loginutils/passwd.c93
-rw-r--r--miscutils/Config.in15
-rw-r--r--miscutils/Kbuild1
-rw-r--r--miscutils/perd.c1483
-rw-r--r--networking/Config.in48
-rw-r--r--networking/Kbuild5
-rw-r--r--networking/atlasinit.h33
-rw-r--r--networking/httpget.c1458
-rw-r--r--networking/httppost.c1283
-rw-r--r--networking/rptra6.c353
-rw-r--r--networking/rxtxrpt.c368
-rw-r--r--networking/sslgetcert.c762
-rw-r--r--networking/telnetd.c805
-rw-r--r--networking/tftp.c2
-rw-r--r--scripts/kconfig/lex.zconf.c2325
-rw-r--r--scripts/kconfig/zconf.hash.c231
-rw-r--r--scripts/kconfig/zconf.tab.c2173
-rw-r--r--shell/ash.c20
-rw-r--r--shell/hush.c384
-rw-r--r--util-linux/Config.in4
72 files changed, 24671 insertions, 131 deletions
diff --git a/.config b/.config
new file mode 100644
index 0000000..283a61b
--- /dev/null
+++ b/.config
@@ -0,0 +1,888 @@
+#
+# Automatically generated make config: don't edit
+# Busybox version: 1.13.3
+# Mon May 6 12:33:49 2013
+#
+CONFIG_HAVE_DOT_CONFIG=y
+
+#
+# Busybox Settings
+#
+
+#
+# General Configuration
+#
+# CONFIG_DESKTOP is not set
+# CONFIG_EXTRA_COMPAT is not set
+# CONFIG_FEATURE_ASSUME_UNICODE is not set
+CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
+# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
+# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
+CONFIG_SHOW_USAGE=y
+CONFIG_FEATURE_VERBOSE_USAGE=y
+CONFIG_FEATURE_COMPRESS_USAGE=y
+CONFIG_FEATURE_INSTALLER=y
+CONFIG_LOCALE_SUPPORT=y
+CONFIG_GETOPT_LONG=y
+CONFIG_FEATURE_DEVPTS=y
+# CONFIG_FEATURE_CLEAN_UP is not set
+CONFIG_FEATURE_PIDFILE=y
+CONFIG_FEATURE_SUID=y
+CONFIG_FEATURE_SUID_CONFIG=y
+CONFIG_FEATURE_SUID_CONFIG_QUIET=y
+# CONFIG_SELINUX is not set
+# CONFIG_FEATURE_PREFER_APPLETS is not set
+CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
+CONFIG_FEATURE_SYSLOG=y
+# CONFIG_FEATURE_HAVE_RPC is not set
+
+#
+# Build Options
+#
+# CONFIG_STATIC is not set
+# CONFIG_PIE is not set
+# CONFIG_NOMMU is not set
+# CONFIG_BUILD_LIBBUSYBOX is not set
+# CONFIG_FEATURE_INDIVIDUAL is not set
+# CONFIG_FEATURE_SHARED_BUSYBOX is not set
+CONFIG_LFS=y
+CONFIG_CROSS_COMPILER_PREFIX=""
+
+#
+# Debugging Options
+#
+CONFIG_DEBUG=y
+CONFIG_DEBUG_PESSIMIZE=y
+# CONFIG_WERROR is not set
+CONFIG_NO_DEBUG_LIB=y
+# CONFIG_DMALLOC is not set
+# CONFIG_EFENCE is not set
+CONFIG_INCLUDE_SUSv2=y
+
+#
+# Installation Options
+#
+# CONFIG_INSTALL_NO_USR is not set
+CONFIG_INSTALL_APPLET_SYMLINKS=y
+# CONFIG_INSTALL_APPLET_HARDLINKS is not set
+# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
+# CONFIG_INSTALL_APPLET_DONT is not set
+# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
+# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
+# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
+CONFIG_PREFIX="/usr/local/atlas/bb-13.3"
+
+#
+# Busybox Library Tuning
+#
+CONFIG_PASSWORD_MINLEN=6
+CONFIG_MD5_SIZE_VS_SPEED=2
+CONFIG_FEATURE_FAST_TOP=y
+# CONFIG_FEATURE_ETC_NETWORKS is not set
+CONFIG_FEATURE_EDITING=y
+CONFIG_FEATURE_EDITING_MAX_LEN=1024
+# CONFIG_FEATURE_EDITING_VI is not set
+CONFIG_FEATURE_EDITING_HISTORY=15
+# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set
+CONFIG_FEATURE_TAB_COMPLETION=y
+# CONFIG_FEATURE_USERNAME_COMPLETION is not set
+# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set
+# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
+CONFIG_FEATURE_COPYBUF_KB=4
+# CONFIG_MONOTONIC_SYSCALL is not set
+CONFIG_IOCTL_HEX2STR_ERROR=y
+CONFIG_FEATURE_HWIB=y
+
+#
+# Applets
+#
+
+#
+# Archival Utilities
+#
+# CONFIG_FEATURE_SEAMLESS_LZMA is not set
+# CONFIG_FEATURE_SEAMLESS_BZ2 is not set
+# CONFIG_FEATURE_SEAMLESS_GZ is not set
+# CONFIG_FEATURE_SEAMLESS_Z is not set
+# CONFIG_AR is not set
+# CONFIG_FEATURE_AR_LONG_FILENAMES is not set
+# CONFIG_BUNZIP2 is not set
+# CONFIG_BZIP2 is not set
+# CONFIG_CPIO is not set
+# CONFIG_FEATURE_CPIO_O is not set
+# CONFIG_DPKG is not set
+# CONFIG_DPKG_DEB is not set
+# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
+# CONFIG_GUNZIP is not set
+# CONFIG_GZIP is not set
+# CONFIG_RPM2CPIO is not set
+# CONFIG_RPM is not set
+# CONFIG_TAR is not set
+# CONFIG_FEATURE_TAR_CREATE is not set
+# CONFIG_FEATURE_TAR_AUTODETECT is not set
+# CONFIG_FEATURE_TAR_FROM is not set
+# CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY is not set
+# CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set
+# CONFIG_FEATURE_TAR_GNU_EXTENSIONS is not set
+# CONFIG_FEATURE_TAR_LONG_OPTIONS is not set
+# CONFIG_FEATURE_TAR_UNAME_GNAME is not set
+# CONFIG_UNCOMPRESS is not set
+# CONFIG_UNLZMA is not set
+# CONFIG_FEATURE_LZMA_FAST is not set
+# CONFIG_UNZIP is not set
+
+#
+# Coreutils
+#
+CONFIG_BASENAME=y
+CONFIG_CAL=y
+CONFIG_CAT=y
+CONFIG_CATV=y
+CONFIG_CHGRP=y
+CONFIG_CHMOD=y
+CONFIG_CHOWN=y
+CONFIG_CHROOT=y
+CONFIG_CKSUM=y
+CONFIG_COMM=y
+CONFIG_CONDMV=y
+CONFIG_CP=y
+CONFIG_CUT=y
+CONFIG_DATE=y
+CONFIG_FEATURE_DATE_ISOFMT=y
+CONFIG_DD=y
+CONFIG_FEATURE_DD_SIGNAL_HANDLING=y
+CONFIG_FEATURE_DD_IBS_OBS=y
+CONFIG_DF=y
+CONFIG_DFRM=y
+CONFIG_FEATURE_DF_FANCY=y
+CONFIG_DIRNAME=y
+CONFIG_DOS2UNIX=y
+CONFIG_UNIX2DOS=y
+CONFIG_DU=y
+CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y
+CONFIG_ECHO=y
+CONFIG_FEATURE_FANCY_ECHO=y
+CONFIG_ENV=y
+CONFIG_FEATURE_ENV_LONG_OPTIONS=y
+CONFIG_EXPAND=y
+CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y
+CONFIG_EXPR=y
+CONFIG_EXPR_MATH_SUPPORT_64=y
+CONFIG_FALSE=y
+CONFIG_FOLD=y
+CONFIG_HEAD=y
+CONFIG_FEATURE_FANCY_HEAD=y
+CONFIG_HOSTID=y
+CONFIG_ID=y
+CONFIG_INSTALL=y
+CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y
+CONFIG_LENGTH=y
+CONFIG_LN=y
+CONFIG_LOGNAME=y
+CONFIG_LS=y
+CONFIG_FEATURE_LS_FILETYPES=y
+CONFIG_FEATURE_LS_FOLLOWLINKS=y
+CONFIG_FEATURE_LS_RECURSIVE=y
+CONFIG_FEATURE_LS_SORTFILES=y
+CONFIG_FEATURE_LS_TIMESTAMPS=y
+CONFIG_FEATURE_LS_USERNAME=y
+CONFIG_FEATURE_LS_COLOR=y
+CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y
+CONFIG_MD5SUM=y
+CONFIG_MKDIR=y
+CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y
+CONFIG_MKFIFO=y
+CONFIG_MKNOD=y
+CONFIG_MV=y
+CONFIG_FEATURE_MV_LONG_OPTIONS=y
+CONFIG_NICE=y
+CONFIG_NOHUP=y
+CONFIG_OD=y
+CONFIG_PRINTENV=y
+CONFIG_PRINTF=y
+CONFIG_PWD=y
+CONFIG_READLINK=y
+CONFIG_FEATURE_READLINK_FOLLOW=y
+CONFIG_REALPATH=y
+CONFIG_RM=y
+CONFIG_RMDIR=y
+CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y
+CONFIG_SEQ=y
+CONFIG_SHA1SUM=y
+CONFIG_SLEEP=y
+CONFIG_FEATURE_FANCY_SLEEP=y
+CONFIG_FEATURE_FLOAT_SLEEP=y
+CONFIG_SLEEPKICK=y
+CONFIG_SORT=y
+CONFIG_FEATURE_SORT_BIG=y
+CONFIG_SPLIT=y
+CONFIG_FEATURE_SPLIT_FANCY=y
+CONFIG_STAT=y
+CONFIG_FEATURE_STAT_FORMAT=y
+CONFIG_STTY=y
+CONFIG_SUM=y
+CONFIG_SYNC=y
+CONFIG_TAC=y
+CONFIG_TAIL=y
+CONFIG_FEATURE_FANCY_TAIL=y
+CONFIG_TEE=y
+CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
+CONFIG_TEST=y
+CONFIG_FEATURE_TEST_64=y
+CONFIG_TOUCH=y
+CONFIG_TR=y
+CONFIG_FEATURE_TR_CLASSES=y
+CONFIG_FEATURE_TR_EQUIV=y
+CONFIG_TRUE=y
+CONFIG_TTY=y
+CONFIG_UNAME=y
+CONFIG_UNEXPAND=y
+CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y
+CONFIG_UNIQ=y
+CONFIG_USLEEP=y
+CONFIG_UUDECODE=y
+CONFIG_UUENCODE=y
+CONFIG_WC=y
+CONFIG_FEATURE_WC_LARGE=y
+CONFIG_WHO=y
+CONFIG_WHOAMI=y
+CONFIG_YES=y
+
+#
+# Common options for cp and mv
+#
+CONFIG_FEATURE_PRESERVE_HARDLINKS=y
+
+#
+# Common options for ls, more and telnet
+#
+CONFIG_FEATURE_AUTOWIDTH=y
+
+#
+# Common options for df, du, ls
+#
+CONFIG_FEATURE_HUMAN_READABLE=y
+
+#
+# Common options for md5sum, sha1sum
+#
+CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
+
+#
+# Console Utilities
+#
+CONFIG_CHVT=y
+CONFIG_CLEAR=y
+CONFIG_DEALLOCVT=y
+CONFIG_DUMPKMAP=y
+CONFIG_KBD_MODE=y
+CONFIG_LOADFONT=y
+CONFIG_LOADKMAP=y
+CONFIG_OPENVT=y
+CONFIG_RESET=y
+CONFIG_RESIZE=y
+CONFIG_FEATURE_RESIZE_PRINT=y
+CONFIG_SETCONSOLE=y
+CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS=y
+CONFIG_SETFONT=y
+CONFIG_FEATURE_SETFONT_TEXTUAL_MAP=y
+CONFIG_DEFAULT_SETFONT_DIR=""
+CONFIG_SETKEYCODES=y
+CONFIG_SETLOGCONS=y
+CONFIG_SHOWKEY=y
+
+#
+# Debian Utilities
+#
+# CONFIG_MKTEMP is not set
+# CONFIG_PIPE_PROGRESS is not set
+# CONFIG_RUN_PARTS is not set
+# CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set
+# CONFIG_FEATURE_RUN_PARTS_FANCY is not set
+# CONFIG_START_STOP_DAEMON is not set
+# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set
+# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set
+# CONFIG_WHICH is not set
+
+#
+# Editors
+#
+# CONFIG_AWK is not set
+# CONFIG_FEATURE_AWK_LIBM is not set
+# CONFIG_CMP is not set
+# CONFIG_DIFF is not set
+# CONFIG_FEATURE_DIFF_BINARY is not set
+# CONFIG_FEATURE_DIFF_DIR is not set
+# CONFIG_FEATURE_DIFF_MINIMAL is not set
+# CONFIG_ED is not set
+# CONFIG_PATCH is not set
+CONFIG_SED=y
+CONFIG_VI=y
+CONFIG_FEATURE_VI_MAX_LEN=4096
+# CONFIG_FEATURE_VI_8BIT is not set
+CONFIG_FEATURE_VI_COLON=y
+CONFIG_FEATURE_VI_YANKMARK=y
+CONFIG_FEATURE_VI_SEARCH=y
+CONFIG_FEATURE_VI_USE_SIGNALS=y
+CONFIG_FEATURE_VI_DOT_CMD=y
+CONFIG_FEATURE_VI_READONLY=y
+CONFIG_FEATURE_VI_SETOPTS=y
+CONFIG_FEATURE_VI_SET=y
+CONFIG_FEATURE_VI_WIN_RESIZE=y
+CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y
+CONFIG_FEATURE_ALLOW_EXEC=y
+
+#
+# Eperd
+#
+CONFIG_EOOQD=y
+CONFIG_EPERD=y
+CONFIG_EVHTTPGET=y
+CONFIG_EVPING=y
+CONFIG_EVTDIG=y
+# CONFIG_FEATURE_EVTDIG_DEBUG is not set
+CONFIG_EVTRACEROUTE=y
+
+#
+# Finding Utilities
+#
+# CONFIG_FIND is not set
+# CONFIG_FEATURE_FIND_PRINT0 is not set
+# CONFIG_FEATURE_FIND_MTIME is not set
+# CONFIG_FEATURE_FIND_MMIN is not set
+# CONFIG_FEATURE_FIND_PERM is not set
+# CONFIG_FEATURE_FIND_TYPE is not set
+# CONFIG_FEATURE_FIND_XDEV is not set
+# CONFIG_FEATURE_FIND_MAXDEPTH is not set
+# CONFIG_FEATURE_FIND_NEWER is not set
+# CONFIG_FEATURE_FIND_INUM is not set
+# CONFIG_FEATURE_FIND_EXEC is not set
+# CONFIG_FEATURE_FIND_USER is not set
+# CONFIG_FEATURE_FIND_GROUP is not set
+# CONFIG_FEATURE_FIND_NOT is not set
+# CONFIG_FEATURE_FIND_DEPTH is not set
+# CONFIG_FEATURE_FIND_PAREN is not set
+# CONFIG_FEATURE_FIND_SIZE is not set
+# CONFIG_FEATURE_FIND_PRUNE is not set
+# CONFIG_FEATURE_FIND_DELETE is not set
+# CONFIG_FEATURE_FIND_PATH is not set
+# CONFIG_FEATURE_FIND_REGEX is not set
+# CONFIG_FEATURE_FIND_CONTEXT is not set
+CONFIG_GREP=y
+CONFIG_FEATURE_GREP_EGREP_ALIAS=y
+CONFIG_FEATURE_GREP_FGREP_ALIAS=y
+CONFIG_FEATURE_GREP_CONTEXT=y
+CONFIG_XARGS=y
+CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
+CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
+CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
+CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
+
+#
+# Init Utilities
+#
+CONFIG_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+CONFIG_FEATURE_KILL_REMOVED=y
+CONFIG_FEATURE_KILL_DELAY=0
+CONFIG_FEATURE_INIT_SCTTY=y
+CONFIG_FEATURE_INIT_SYSLOG=y
+CONFIG_FEATURE_EXTRA_QUIET=y
+CONFIG_FEATURE_INIT_COREDUMPS=y
+CONFIG_FEATURE_INITRD=y
+CONFIG_HALT=y
+CONFIG_MESG=y
+
+#
+# Login/Password Management Utilities
+#
+CONFIG_FEATURE_SHADOWPASSWDS=y
+CONFIG_USE_BB_PWD_GRP=y
+CONFIG_USE_BB_SHADOW=y
+CONFIG_USE_BB_CRYPT=y
+CONFIG_ADDGROUP=y
+CONFIG_FEATURE_ADDUSER_TO_GROUP=y
+CONFIG_DELGROUP=y
+CONFIG_FEATURE_DEL_USER_FROM_GROUP=y
+# CONFIG_FEATURE_CHECK_NAMES is not set
+CONFIG_ADDUSER=y
+CONFIG_FEATURE_ADDUSER_LONG_OPTIONS=y
+CONFIG_DELUSER=y
+CONFIG_GETTY=y
+CONFIG_FEATURE_UTMP=y
+CONFIG_FEATURE_WTMP=y
+CONFIG_LOGIN=y
+# CONFIG_PAM is not set
+CONFIG_LOGIN_SCRIPTS=y
+CONFIG_FEATURE_NOLOGIN=y
+CONFIG_FEATURE_SECURETTY=y
+CONFIG_PASSWD=y
+CONFIG_FEATURE_PASSWD_WEAK_CHECK=y
+CONFIG_CRYPTPW=y
+CONFIG_CHPASSWD=y
+CONFIG_SU=y
+CONFIG_FEATURE_SU_SYSLOG=y
+CONFIG_FEATURE_SU_CHECKS_SHELLS=y
+CONFIG_SULOGIN=y
+CONFIG_VLOCK=y
+
+#
+# Linux Ext2 FS Progs
+#
+# CONFIG_CHATTR is not set
+# CONFIG_FSCK is not set
+# CONFIG_LSATTR is not set
+
+#
+# Linux Module Utilities
+#
+CONFIG_DEFAULT_MODULES_DIR="/lib/modules"
+CONFIG_DEFAULT_DEPMOD_FILE="modules.dep"
+CONFIG_MODPROBE_SMALL=y
+CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE=y
+CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y
+# CONFIG_INSMOD is not set
+# CONFIG_RMMOD is not set
+# CONFIG_LSMOD is not set
+# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set
+# CONFIG_MODPROBE is not set
+# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set
+# CONFIG_DEPMOD is not set
+
+#
+# Options common to multiple modutils
+#
+# CONFIG_FEATURE_2_4_MODULES is not set
+# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
+# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
+# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
+# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
+# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set
+# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
+# CONFIG_FEATURE_MODUTILS_ALIAS is not set
+# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set
+
+#
+# Linux System Utilities
+#
+# CONFIG_BLKID is not set
+CONFIG_DMESG=y
+CONFIG_FEATURE_DMESG_PRETTY=y
+CONFIG_FBSET=y
+CONFIG_FEATURE_FBSET_FANCY=y
+CONFIG_FEATURE_FBSET_READMODE=y
+CONFIG_FDFLUSH=y
+CONFIG_FDFORMAT=y
+# CONFIG_FDISK is not set
+CONFIG_FDISK_SUPPORT_LARGE_DISKS=y
+# CONFIG_FEATURE_FDISK_WRITABLE is not set
+# CONFIG_FEATURE_AIX_LABEL is not set
+# CONFIG_FEATURE_SGI_LABEL is not set
+# CONFIG_FEATURE_SUN_LABEL is not set
+# CONFIG_FEATURE_OSF_LABEL is not set
+# CONFIG_FEATURE_FDISK_ADVANCED is not set
+CONFIG_FINDFS=y
+CONFIG_FREERAMDISK=y
+CONFIG_FSCK_MINIX=y
+CONFIG_MKFS_MINIX=y
+
+#
+# Minix filesystem support
+#
+CONFIG_FEATURE_MINIX2=y
+CONFIG_GETOPT=y
+CONFIG_HEXDUMP=y
+CONFIG_FEATURE_HEXDUMP_REVERSE=y
+CONFIG_HD=y
+CONFIG_HWCLOCK=y
+CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS=y
+CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS=y
+CONFIG_IPCRM=y
+CONFIG_IPCS=y
+CONFIG_LOSETUP=y
+CONFIG_MDEV=y
+CONFIG_FEATURE_MDEV_CONF=y
+CONFIG_FEATURE_MDEV_RENAME=y
+CONFIG_FEATURE_MDEV_RENAME_REGEXP=y
+CONFIG_FEATURE_MDEV_EXEC=y
+CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y
+CONFIG_MKSWAP=y
+CONFIG_FEATURE_MKSWAP_V0=y
+CONFIG_MORE=y
+CONFIG_FEATURE_USE_TERMIOS=y
+CONFIG_VOLUMEID=y
+CONFIG_FEATURE_VOLUMEID_EXT=y
+# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
+CONFIG_FEATURE_VOLUMEID_FAT=y
+# CONFIG_FEATURE_VOLUMEID_HFS is not set
+CONFIG_FEATURE_VOLUMEID_JFS=y
+# CONFIG_FEATURE_VOLUMEID_XFS is not set
+# CONFIG_FEATURE_VOLUMEID_NTFS is not set
+# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set
+# CONFIG_FEATURE_VOLUMEID_UDF is not set
+# CONFIG_FEATURE_VOLUMEID_LUKS is not set
+# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set
+# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set
+# CONFIG_FEATURE_VOLUMEID_ROMFS is not set
+# CONFIG_FEATURE_VOLUMEID_SYSV is not set
+# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set
+# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set
+# CONFIG_MOUNT is not set
+# CONFIG_FEATURE_MOUNT_FAKE is not set
+# CONFIG_FEATURE_MOUNT_VERBOSE is not set
+# CONFIG_FEATURE_MOUNT_HELPERS is not set
+# CONFIG_FEATURE_MOUNT_LABEL is not set
+# CONFIG_FEATURE_MOUNT_NFS is not set
+# CONFIG_FEATURE_MOUNT_CIFS is not set
+# CONFIG_FEATURE_MOUNT_FLAGS is not set
+# CONFIG_FEATURE_MOUNT_FSTAB is not set
+# CONFIG_PIVOT_ROOT is not set
+CONFIG_RDATE=y
+CONFIG_RDEV=y
+CONFIG_READPROFILE=y
+CONFIG_RTCWAKE=y
+CONFIG_SCRIPT=y
+CONFIG_SETARCH=y
+# CONFIG_SWAPONOFF is not set
+# CONFIG_FEATURE_SWAPON_PRI is not set
+# CONFIG_SWITCH_ROOT is not set
+# CONFIG_UMOUNT is not set
+# CONFIG_FEATURE_UMOUNT_ALL is not set
+# CONFIG_FEATURE_MOUNT_LOOP is not set
+# CONFIG_FEATURE_MTAB_SUPPORT is not set
+
+#
+# Miscellaneous Utilities
+#
+CONFIG_ADJTIMEX=y
+# CONFIG_BBCONFIG is not set
+CONFIG_CHAT=y
+CONFIG_FEATURE_CHAT_NOFAIL=y
+# CONFIG_FEATURE_CHAT_TTY_HIFI is not set
+CONFIG_FEATURE_CHAT_IMPLICIT_CR=y
+CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y
+CONFIG_FEATURE_CHAT_SEND_ESCAPES=y
+CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y
+CONFIG_FEATURE_CHAT_CLR_ABORT=y
+CONFIG_CHRT=y
+# CONFIG_CROND is not set
+# CONFIG_FEATURE_CROND_D is not set
+# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
+CONFIG_CRONTAB=y
+CONFIG_DC=y
+CONFIG_FEATURE_DC_LIBM=y
+# CONFIG_DEVFSD is not set
+# CONFIG_DEVFSD_MODLOAD is not set
+# CONFIG_DEVFSD_FG_NP is not set
+# CONFIG_DEVFSD_VERBOSE is not set
+# CONFIG_FEATURE_DEVFS is not set
+CONFIG_DEVMEM=y
+CONFIG_EJECT=y
+CONFIG_FEATURE_EJECT_SCSI=y
+CONFIG_FBSPLASH=y
+# CONFIG_INOTIFYD is not set
+CONFIG_LAST=y
+# CONFIG_FEATURE_LAST_SMALL is not set
+CONFIG_FEATURE_LAST_FANCY=y
+CONFIG_LESS=y
+CONFIG_FEATURE_LESS_MAXLINES=9999999
+CONFIG_FEATURE_LESS_BRACKETS=y
+CONFIG_FEATURE_LESS_FLAGS=y
+CONFIG_FEATURE_LESS_DASHCMD=y
+CONFIG_FEATURE_LESS_MARKS=y
+CONFIG_FEATURE_LESS_REGEXP=y
+CONFIG_FEATURE_LESS_LINENUMS=y
+CONFIG_FEATURE_LESS_WINCH=y
+CONFIG_HDPARM=y
+CONFIG_FEATURE_HDPARM_GET_IDENTITY=y
+CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y
+CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y
+CONFIG_MAKEDEVS=y
+# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
+CONFIG_FEATURE_MAKEDEVS_TABLE=y
+CONFIG_MAN=y
+CONFIG_MICROCOM=y
+CONFIG_MOUNTPOINT=y
+CONFIG_MT=y
+CONFIG_PERD=y
+CONFIG_FEATURE_PERD_D=y
+CONFIG_RAIDAUTORUN=y
+CONFIG_READAHEAD=y
+CONFIG_RUNLEVEL=y
+CONFIG_RX=y
+CONFIG_SETSID=y
+CONFIG_STRINGS=y
+# CONFIG_TASKSET is not set
+# CONFIG_FEATURE_TASKSET_FANCY is not set
+CONFIG_TIME=y
+CONFIG_TTYSIZE=y
+CONFIG_WATCHDOG=y
+
+#
+# Networking Utilities
+#
+CONFIG_FEATURE_IPV6=y
+CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
+# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
+CONFIG_ARP=y
+CONFIG_ARPING=y
+# CONFIG_AUTOSSH is not set
+CONFIG_BRCTL=y
+CONFIG_FEATURE_BRCTL_FANCY=y
+CONFIG_FEATURE_BRCTL_SHOW=y
+CONFIG_DNSD=y
+CONFIG_ETHER_WAKE=y
+CONFIG_FAKEIDENTD=y
+CONFIG_FTPGET=y
+CONFIG_FTPPUT=y
+CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
+CONFIG_HOSTNAME=y
+# CONFIG_HTTPD is not set
+# CONFIG_FEATURE_HTTPD_RANGES is not set
+# CONFIG_FEATURE_HTTPD_USE_SENDFILE is not set
+# CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP is not set
+# CONFIG_FEATURE_HTTPD_SETUID is not set
+# CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set
+# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
+# CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES is not set
+# CONFIG_FEATURE_HTTPD_CGI is not set
+# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
+# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
+# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set
+# CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set
+# CONFIG_FEATURE_HTTPD_PROXY is not set
+CONFIG_HTTPGET=y
+CONFIG_HTTPPOST=y
+CONFIG_IFCONFIG=y
+CONFIG_FEATURE_IFCONFIG_STATUS=y
+CONFIG_FEATURE_IFCONFIG_SLIP=y
+CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y
+CONFIG_FEATURE_IFCONFIG_HW=y
+CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y
+CONFIG_IFENSLAVE=y
+CONFIG_IFUPDOWN=y
+CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate"
+CONFIG_FEATURE_IFUPDOWN_IP=y
+CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y
+# CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set
+CONFIG_FEATURE_IFUPDOWN_IPV4=y
+CONFIG_FEATURE_IFUPDOWN_IPV6=y
+CONFIG_FEATURE_IFUPDOWN_MAPPING=y
+# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set
+# CONFIG_INETD is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set
+# CONFIG_FEATURE_INETD_RPC is not set
+CONFIG_IP=y
+CONFIG_FEATURE_IP_ADDRESS=y
+CONFIG_FEATURE_IP_LINK=y
+CONFIG_FEATURE_IP_ROUTE=y
+CONFIG_FEATURE_IP_TUNNEL=y
+CONFIG_FEATURE_IP_RULE=y
+CONFIG_FEATURE_IP_SHORT_FORMS=y
+# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
+CONFIG_IPADDR=y
+CONFIG_IPLINK=y
+CONFIG_IPROUTE=y
+CONFIG_IPTUNNEL=y
+CONFIG_IPRULE=y
+CONFIG_IPCALC=y
+CONFIG_FEATURE_IPCALC_FANCY=y
+CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y
+CONFIG_NAMEIF=y
+CONFIG_FEATURE_NAMEIF_EXTENDED=y
+CONFIG_NC=y
+CONFIG_NC_SERVER=y
+CONFIG_NC_EXTRA=y
+CONFIG_NETSTAT=y
+CONFIG_FEATURE_NETSTAT_WIDE=y
+CONFIG_FEATURE_NETSTAT_PRG=y
+CONFIG_NSLOOKUP=y
+CONFIG_PING=y
+CONFIG_PING6=y
+CONFIG_FEATURE_FANCY_PING=y
+CONFIG_PSCAN=y
+CONFIG_ROUTE=y
+CONFIG_RPTRA6=y
+CONFIG_RXTXRPT=y
+CONFIG_SLATTACH=y
+CONFIG_SSLGETCERT=y
+CONFIG_TELNET=y
+CONFIG_FEATURE_TELNET_TTYPE=y
+CONFIG_FEATURE_TELNET_AUTOLOGIN=y
+CONFIG_TELNETD=y
+CONFIG_FEATURE_TELNETD_STANDALONE=y
+CONFIG_TFTP=y
+CONFIG_TFTPD=y
+CONFIG_FEATURE_TFTP_GET=y
+CONFIG_FEATURE_TFTP_PUT=y
+CONFIG_FEATURE_TFTP_BLOCKSIZE=y
+# CONFIG_TFTP_DEBUG is not set
+CONFIG_TRACEROUTE=y
+CONFIG_FEATURE_TRACEROUTE_IPV6=y
+CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
+# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
+# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
+# CONFIG_APP_UDHCPD is not set
+# CONFIG_APP_DHCPRELAY is not set
+# CONFIG_APP_DUMPLEASES is not set
+# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
+CONFIG_DHCPD_LEASES_FILE=""
+# CONFIG_APP_UDHCPC is not set
+# CONFIG_FEATURE_UDHCPC_ARPING is not set
+# CONFIG_FEATURE_UDHCP_PORT is not set
+# CONFIG_UDHCP_DEBUG is not set
+# CONFIG_FEATURE_UDHCP_RFC3397 is not set
+CONFIG_UDHCPC_DEFAULT_SCRIPT=""
+CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=
+CONFIG_VCONFIG=y
+CONFIG_WGET=y
+CONFIG_FEATURE_WGET_STATUSBAR=y
+CONFIG_FEATURE_WGET_AUTHENTICATION=y
+CONFIG_FEATURE_WGET_LONG_OPTIONS=y
+CONFIG_ZCIP=y
+# CONFIG_TCPSVD is not set
+# CONFIG_UDPSVD is not set
+
+#
+# Print Utilities
+#
+CONFIG_LPD=y
+CONFIG_LPR=y
+CONFIG_LPQ=y
+
+#
+# Mail Utilities
+#
+# CONFIG_MAKEMIME is not set
+CONFIG_FEATURE_MIME_CHARSET=""
+# CONFIG_POPMAILDIR is not set
+# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set
+# CONFIG_REFORMIME is not set
+# CONFIG_FEATURE_REFORMIME_COMPAT is not set
+# CONFIG_SENDMAIL is not set
+# CONFIG_FEATURE_SENDMAIL_MAILX is not set
+# CONFIG_FEATURE_SENDMAIL_MAILXX is not set
+
+#
+# Process Utilities
+#
+CONFIG_FREE=y
+CONFIG_FUSER=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+CONFIG_KILLALL5=y
+CONFIG_NMETER=y
+CONFIG_PGREP=y
+CONFIG_PIDOF=y
+CONFIG_FEATURE_PIDOF_SINGLE=y
+CONFIG_FEATURE_PIDOF_OMIT=y
+CONFIG_PKILL=y
+CONFIG_PS=y
+CONFIG_FEATURE_PS_WIDE=y
+# CONFIG_FEATURE_PS_TIME is not set
+# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
+CONFIG_RENICE=y
+CONFIG_BB_SYSCTL=y
+CONFIG_TOP=y
+CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y
+CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y
+CONFIG_FEATURE_TOP_SMP_CPU=y
+CONFIG_FEATURE_TOP_DECIMALS=y
+CONFIG_FEATURE_TOP_SMP_PROCESS=y
+CONFIG_FEATURE_TOPMEM=y
+CONFIG_UPTIME=y
+CONFIG_WATCH=y
+
+#
+# Runit Utilities
+#
+# CONFIG_RUNSV is not set
+# CONFIG_RUNSVDIR is not set
+# CONFIG_FEATURE_RUNSVDIR_LOG is not set
+CONFIG_SV=y
+CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service"
+# CONFIG_SVLOGD is not set
+# CONFIG_CHPST is not set
+# CONFIG_SETUIDGID is not set
+# CONFIG_ENVUIDGID is not set
+# CONFIG_ENVDIR is not set
+# CONFIG_SOFTLIMIT is not set
+# CONFIG_CHCON is not set
+# CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set
+# CONFIG_GETENFORCE is not set
+# CONFIG_GETSEBOOL is not set
+# CONFIG_LOAD_POLICY is not set
+# CONFIG_MATCHPATHCON is not set
+# CONFIG_RESTORECON is not set
+# CONFIG_RUNCON is not set
+# CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set
+# CONFIG_SELINUXENABLED is not set
+# CONFIG_SETENFORCE is not set
+# CONFIG_SETFILES is not set
+# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set
+# CONFIG_SETSEBOOL is not set
+# CONFIG_SESTATUS is not set
+
+#
+# Shells
+#
+# CONFIG_FEATURE_SH_IS_ASH is not set
+CONFIG_FEATURE_SH_IS_HUSH=y
+# CONFIG_FEATURE_SH_IS_MSH is not set
+# CONFIG_FEATURE_SH_IS_NONE is not set
+# CONFIG_ASH is not set
+# CONFIG_ASH_BASH_COMPAT is not set
+# CONFIG_ASH_JOB_CONTROL is not set
+# CONFIG_ASH_READ_NCHARS is not set
+# CONFIG_ASH_READ_TIMEOUT is not set
+# CONFIG_ASH_ALIAS is not set
+# CONFIG_ASH_MATH_SUPPORT is not set
+# CONFIG_ASH_MATH_SUPPORT_64 is not set
+# CONFIG_ASH_GETOPTS is not set
+# CONFIG_ASH_BUILTIN_ECHO is not set
+# CONFIG_ASH_BUILTIN_PRINTF is not set
+# CONFIG_ASH_BUILTIN_TEST is not set
+# CONFIG_ASH_CMDCMD is not set
+# CONFIG_ASH_MAIL is not set
+# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set
+# CONFIG_ASH_RANDOM_SUPPORT is not set
+# CONFIG_ASH_EXPAND_PRMT is not set
+CONFIG_HUSH=y
+CONFIG_HUSH_HELP=y
+CONFIG_HUSH_INTERACTIVE=y
+CONFIG_HUSH_JOB=y
+CONFIG_HUSH_TICK=y
+CONFIG_HUSH_IF=y
+CONFIG_HUSH_LOOPS=y
+CONFIG_HUSH_CASE=y
+# CONFIG_LASH is not set
+CONFIG_MSH=y
+
+#
+# Bourne Shell Options
+#
+CONFIG_FEATURE_SH_EXTRA_QUIET=y
+# CONFIG_FEATURE_SH_STANDALONE is not set
+# CONFIG_FEATURE_SH_NOFORK is not set
+CONFIG_CTTYHACK=y
+
+#
+# System Logging Utilities
+#
+# CONFIG_SYSLOGD is not set
+# CONFIG_FEATURE_ROTATE_LOGFILE is not set
+# CONFIG_FEATURE_REMOTE_LOG is not set
+# CONFIG_FEATURE_SYSLOGD_DUP is not set
+# CONFIG_FEATURE_IPC_SYSLOG is not set
+CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=
+# CONFIG_LOGREAD is not set
+# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
+# CONFIG_KLOGD is not set
+# CONFIG_LOGGER is not set
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..056b3eb
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,54 @@
+*~
+*.a
+*.a.cmd
+*.bak
+*.o
+.OBJ.*
+*.o.cmd
+*.rej
+*.cmd
+*.swp
+applets/applet_tables
+applets/.applet_tables.cmd
+applets/usage
+applets/.usage.cmd
+busybox
+busybox.links
+busybox_unstripped*
+.busybox_unstripped.cmd
+.config.old
+cscope.out
+*/cscope.out
+docs/BusyBox.1
+docs/BusyBox.html
+docs/busybox.net/BusyBox.html
+docs/busybox.pod
+docs/BusyBox.txt
+include/applet_tables.h
+include/autoconf.h
+include/bbconfigopts.h
+include/config
+include/usage_compressed.h
+.indent.pro
+.kconfig.d
+.kernelrelease
+scripts/basic/docproc
+scripts/basic/fixdep
+scripts/basic/split-include
+scripts/kconfig/conf
+libevent-2.0.20-stable/sample/.libs/*
+libevent-2.0.20-stable/test
+libevent-2.0.20-stable/*.lo
+libevent-2.0.20-stable/*.pc
+libevent-2.0.20-stable/*.la
+libevent-2.0.20-stable/stamp-h1
+libevent-2.0.20-stable/sample
+libevent-2.0.20-stable/libtool
+libevent-2.0.20-stable/include/event2/event-config.h
+libevent-2.0.20-stable/include/Makefile
+libevent-2.0.20-stable/config.status
+libevent-2.0.20-stable/config.log
+libevent-2.0.20-stable/config.h
+libevent-2.0.20-stable/Makefile
+libevent-2.0.20-stable/.libs
+libevent-2.0.20-stable/.deps
diff --git a/AUTHORS b/AUTHORS
index 378c332..bea1d4b 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -171,3 +171,9 @@ Bernhard Reutner-Fischer <rep.dot.nop@gmail.com>
Mike Frysinger <vapier@gentoo.org>
initial e2fsprogs, printenv, setarch, sum, misc
+
+Philip Homburg <philip.homburg@ripe.net>
+ eperd evtraceroute evping perd condmv httpget httppost
+
+Antony Antony <antony@ripe.net>
+ evtdig buddyinfo findpid
diff --git a/Config.in b/Config.in
index 4fd9d11..bf04919 100644
--- a/Config.in
+++ b/Config.in
@@ -587,6 +587,7 @@ source coreutils/Config.in
source console-tools/Config.in
source debianutils/Config.in
source editors/Config.in
+source eperd/Config.in
source findutils/Config.in
source init/Config.in
source loginutils/Config.in
diff --git a/INSTALL b/INSTALL
index a7902ab..095137b 100644
--- a/INSTALL
+++ b/INSTALL
@@ -3,9 +3,17 @@ Building:
The BusyBox build process is similar to the Linux kernel build:
- make menuconfig # This creates a file called ".config"
- make # This creates the "busybox" executable
- make install # or make CONFIG_PREFIX=/path/from/root install
+ cd libevent-2.0.20-stable # first build libevent
+ ./configure --prefix=/usr/local/atlas
+ make install
+ cd ../
+ make # This creates the "busybox" executable
+ make install # installs to /usr/local/atlas/bb-13.3/
+ mkdir -p /home/atlas/crons/7 /home/atlas/data/new
+ cp examples/cron.root.7 /home/atlas/crons/7/root
+ /usr/local/atlas/bb-13.3/usr/sbin/eperd -c /home/atlas/crons/7 -A 19807 -P /home/atlas/status/perd-7.pid.vol -O /home/atlas/data/new/7 # it is ok to see output "eperd: in my_exit (exit was called!) Aborted
+ # your output is appended to file /home/atlas/data/new/7.
+ tail -f /home/atlas/data/new/7
The full list of configuration and install options is available by typing:
diff --git a/Makefile b/Makefile
index 4bd5ebc..566c48a 100644
--- a/Makefile
+++ b/Makefile
@@ -451,6 +451,7 @@ libs-y := \
debianutils/ \
e2fsprogs/ \
editors/ \
+ eperd/ \
findutils/ \
init/ \
libbb/ \
diff --git a/Makefile.flags b/Makefile.flags
index e314802..6fa7e75 100644
--- a/Makefile.flags
+++ b/Makefile.flags
@@ -11,6 +11,7 @@ CPPFLAGS += $(call cc-option,-std=gnu99,)
CPPFLAGS += \
-Iinclude -Ilibbb \
+ -Ilibevent-2.0.20-stable/include \
$(if $(KBUILD_SRC),-Iinclude2 -I$(srctree)/include -I$(srctree)/libbb) \
-include include/autoconf.h \
-D_GNU_SOURCE -DNDEBUG \
@@ -99,6 +100,10 @@ ifeq ($(CONFIG_DMALLOC),y)
LDLIBS += dmalloc
endif
+LDLIBS += event
+
+LDFLAGS += -Llibevent-2.0.20-stable/.libs -Wl,-rpath,/usr/local/atlas/lib -Wl,-rpath,/home/atlas/busybox-1.13.3/libevent-2.0.20-stable/.libs
+
# If a flat binary should be built, CFLAGS_busybox="-Wl,-elf2flt"
# env var should be set for make invocation.
# Here we check whether CFLAGS_busybox indeed contains that flag.
diff --git a/coreutils/Config.in b/coreutils/Config.in
index 8cbc92f..bb81994 100644
--- a/coreutils/Config.in
+++ b/coreutils/Config.in
@@ -72,6 +72,12 @@ config COMM
comm is used to compare two files line by line and return
a three-column output.
+config CONDMV
+ bool "condmv"
+ default n
+ help
+ condmv is used to rename a file if the destination does not exist
+
config CP
bool "cp"
default n
@@ -136,6 +142,13 @@ config DF
df reports the amount of disk space used and available
on filesystems.
+config DFRM
+ bool "dfrm"
+ default n
+ help
+ dfrm deletes files from directories when the amount of free space is
+ too low
+
config FEATURE_DF_FANCY
bool "Enable -a, -i, -B"
default n
@@ -537,6 +550,13 @@ config FEATURE_FLOAT_SLEEP
help
Allow for fractional numeric parameters.
+config SLEEPKICK
+ bool "sleepkick"
+ default n
+ help
+ sleep is used to pause for a specified number of seconds.
+ And kick the /dev/watchdog every x seconds. RIPE ATLAS. Antony
+
config SORT
bool "sort"
default n
diff --git a/coreutils/Kbuild b/coreutils/Kbuild
index a5a2d4c..4cb1736 100644
--- a/coreutils/Kbuild
+++ b/coreutils/Kbuild
@@ -20,11 +20,13 @@ lib-$(CONFIG_CHOWN) += chown.o
lib-$(CONFIG_CHROOT) += chroot.o
lib-$(CONFIG_CKSUM) += cksum.o
lib-$(CONFIG_COMM) += comm.o
+lib-$(CONFIG_CONDMV) += condmv.o
lib-$(CONFIG_CP) += cp.o
lib-$(CONFIG_CUT) += cut.o
lib-$(CONFIG_DATE) += date.o
lib-$(CONFIG_DD) += dd.o
lib-$(CONFIG_DF) += df.o
+lib-$(CONFIG_DFRM) += dfrm.o
lib-$(CONFIG_DIRNAME) += dirname.o
lib-$(CONFIG_DOS2UNIX) += dos2unix.o
lib-$(CONFIG_DU) += du.o
diff --git a/coreutils/buddyinfo.c b/coreutils/buddyinfo.c
new file mode 100644
index 0000000..6f41eb1
--- /dev/null
+++ b/coreutils/buddyinfo.c
@@ -0,0 +1,72 @@
+/* vi: set sw=2 ts=2:
+ *
+ * 2010-2013 Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * read /cat/proc/buddyinfo and print out.
+ * if env variable LOWMEM_REBOOT is set KBytes same as buddyinfo reboot
+ *
+ */
+
+#include "libbb.h"
+
+/* This is a NOFORK applet. Be very careful! */
+
+int buddyinfo_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int buddyinfo_main(int argc UNUSED_PARAM, char **argv)
+{
+ char *lowmemChar;
+ unsigned lowmem = 0;
+ lowmemChar = getenv("LOW_MEM_T");
+ if(lowmemChar)
+ lowmem = xatou(lowmemChar);
+
+ FILE *fp = xfopen_for_read("/proc/buddyinfo");
+ char aa[10];
+ fscanf(fp, "%s", aa);
+ fscanf(fp, "%s", aa);
+ fscanf(fp, "%s", aa);
+ fscanf(fp, "%s", aa);
+
+ char *my_mac ;
+ my_mac = getenv("ETHER_SCANNED");
+
+ int i = 0;
+ int j = 0;
+ int memBlock = 4;
+ int fReboot = 1; // don't reboot
+ if (lowmem >= 4 )
+ {
+ fReboot = 0; // env variable is set sow we check for low thershhold
+ }
+ printf ("RESULT 9001.0 ongoing %d ", (int)time(0));
+ if (my_mac != NULL)
+ printf("%s ", my_mac);
+ for (j=0; j< 11; j++)
+ {
+ fscanf(fp, "%d", &i);
+ printf("%-3d ", i);
+ if ( lowmem >= 4)
+ {
+ if( memBlock >= lowmem)
+ {
+ if(fReboot == 0)
+ {
+ if (i > 0 )
+ {
+ fReboot = 1;
+
+ }
+ }
+ }
+ }
+ memBlock *= 2;
+ }
+ printf ("\n");
+ fclose(fp);
+ if(fReboot == 0 )
+ {
+ fprintf(stderr, "buddy info returned 1 for block %d\n", lowmem);
+ return (EXIT_FAILURE);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/coreutils/condmv.c b/coreutils/condmv.c
new file mode 100644
index 0000000..0139779
--- /dev/null
+++ b/coreutils/condmv.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * condmv.c -- move a file only if the destination doesn't exist
+ */
+
+#include "libbb.h"
+
+#define SAFE_PREFIX_FROM ATLAS_DATA_NEW
+#define SAFE_PREFIX_TO ATLAS_DATA_OUT
+
+#define A_FLAG (1 << 0)
+#define F_FLAG (1 << 1)
+
+int condmv_main(int argc, char *argv[])
+{
+ char *opt_add, *from, *to;
+ uint32_t opt;
+ struct stat sb;
+ FILE *file;
+ time_t mytime;
+
+ opt_add= NULL;
+ opt_complementary= NULL; /* For when we are called by crond */
+ opt= getopt32(argv, "!A:f", &opt_add);
+
+ if (opt == (uint32_t)-1)
+ {
+ fprintf(stderr, "condmv: bad options\n");
+ return 1;
+ }
+
+ if (argc != optind + 2)
+ {
+ fprintf(stderr, "condmv: two arguments expected\n");
+ return 1;
+ }
+
+ from= argv[optind];
+ to= argv[optind+1];
+
+ if (!validate_filename(from, SAFE_PREFIX_FROM))
+ {
+ fprintf(stderr, "insecure from file '%s'\n", from);
+ return 1;
+ }
+ if (!validate_filename(to, SAFE_PREFIX_TO) &&
+ !validate_filename(to, SAFE_PREFIX_FROM))
+ {
+ fprintf(stderr, "insecure to file '%s'\n", to);
+ return 1;
+ }
+
+ if (stat(to, &sb) == 0 && !(opt & F_FLAG))
+ {
+ /* Destination exists */
+ fprintf(stderr, "condmv: not moving, destination '%s' exists\n",
+ to);
+ return 1;
+ }
+
+ if (opt_add)
+ {
+ mytime = time(NULL);
+ /* We have to add something to the existing file before moving
+ * to.
+ */
+ file= fopen(from, "a");
+ if (file == NULL)
+ {
+ fprintf(stderr,
+ "condmv: unable to append to '%s': %s\n",
+ from, strerror(errno));
+ return 1;
+ }
+ if (fprintf(file, "%s %lu %s\n", opt_add, mytime, from) < 0)
+ {
+ fprintf(stderr,
+ "condmv: unable to append to '%s': %s\n",
+ from, strerror(errno));
+ fclose(file);
+ return 1;
+ }
+ if (fclose(file) != 0)
+ {
+ fprintf(stderr,
+ "condmv: unable to close '%s': %s\n",
+ from, strerror(errno));
+ return 1;
+ }
+ }
+ if (rename(from, to) != 0)
+ {
+ fprintf(stderr, "condmv: unable to rename '%s' to '%s': %s\n",
+ from, to, strerror(errno));
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/coreutils/date.c b/coreutils/date.c
index 177b7d0..57ec4e7 100644
--- a/coreutils/date.c
+++ b/coreutils/date.c
@@ -29,8 +29,9 @@
#define DATE_OPT_UTC 0x04
#define DATE_OPT_DATE 0x08
#define DATE_OPT_REFERENCE 0x10
-#define DATE_OPT_TIMESPEC 0x20
-#define DATE_OPT_HINT 0x40
+#define DATE_OPT_UNIXSECS 0x20
+#define DATE_OPT_TIMESPEC 0x40
+#define DATE_OPT_HINT 0x80
static void maybe_set_utc(int opt)
{
@@ -50,10 +51,11 @@ int date_main(int argc UNUSED_PARAM, char **argv)
char *fmt_str2dt;
char *filename;
char *isofmt_arg = NULL;
+ char *check;
opt_complementary = "d--s:s--d"
USE_FEATURE_DATE_ISOFMT(":R--I:I--R");
- opt = getopt32(argv, "Rs:ud:r:"
+ opt = getopt32(argv, "Rs:ud:r:S"
USE_FEATURE_DATE_ISOFMT("I::D:"),
&date_str, &date_str, &filename
USE_FEATURE_DATE_ISOFMT(, &isofmt_arg, &fmt_str2dt));
@@ -104,7 +106,19 @@ int date_main(int argc UNUSED_PARAM, char **argv)
tm_time.tm_hour = 0;
/* Process any date input to UNIX time since 1 Jan 1970 */
- if (ENABLE_FEATURE_DATE_ISOFMT && (opt & DATE_OPT_HINT)) {
+ if (opt & DATE_OPT_UNIXSECS)
+ {
+ tm= strtoul(date_str, &check, 10);
+ if (check[0] != '\0')
+ {
+ bb_error_msg_and_die(bb_msg_invalid_date,
+ date_str);
+ }
+
+ /* Fill in tm_time */
+ tm_time= *localtime(&tm);
+ }
+ else if (ENABLE_FEATURE_DATE_ISOFMT && (opt & DATE_OPT_HINT)) {
if (strptime(date_str, fmt_str2dt, &tm_time) == NULL)
bb_error_msg_and_die(bb_msg_invalid_date, date_str);
} else {
@@ -168,13 +182,19 @@ int date_main(int argc UNUSED_PARAM, char **argv)
bb_error_msg_and_die(bb_msg_invalid_date, date_str);
}
}
- /* Correct any day of week and day of year etc. fields */
- tm_time.tm_isdst = -1; /* Be sure to recheck dst. */
- tm = mktime(&tm_time);
- if (tm < 0) {
- bb_error_msg_and_die(bb_msg_invalid_date, date_str);
+ if (!(opt & DATE_OPT_UNIXSECS))
+ {
+ /* Correct any day of week and day of year etc.
+ * fields
+ */
+ tm_time.tm_isdst = -1; /* Be sure to recheck dst. */
+ tm = mktime(&tm_time);
+ if (tm < 0) {
+ bb_error_msg_and_die(bb_msg_invalid_date,
+ date_str);
+ }
+ maybe_set_utc(opt);
}
- maybe_set_utc(opt);
/* if setting time, set it */
if ((opt & DATE_OPT_SET) && stime(&tm) < 0) {
diff --git a/coreutils/dfrm.c b/coreutils/dfrm.c
new file mode 100644
index 0000000..75e3a5f
--- /dev/null
+++ b/coreutils/dfrm.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * dfrm.c
+ * Remove the contents of directories if the amount of free space gets too low
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/vfs.h>
+
+#include "libbb.h"
+
+#define DBQ(str) "\"" #str "\""
+
+int dfrm_main(int argc, char *argv[])
+{
+ int i;
+ size_t len;
+ uint32_t opt;
+ unsigned long limit, avail;
+ char *opt_atlas;
+ char *dev, *limit_str, *dir_str, *check, *path;
+ DIR *dir;
+ struct dirent *de;
+ struct statfs sb;
+
+ opt_atlas= NULL;
+ opt_complementary= NULL; /* Just in case */
+ opt= getopt32(argv, "A:", &opt_atlas);
+
+ if (argc < optind+3)
+ {
+ printf("not enough arguments\n");
+ return 1;
+ }
+ dev= argv[optind];
+ limit_str= argv[optind+1];
+
+ if (statfs(dev, &sb) != 0)
+ {
+ fprintf(stderr, "statfs on %s failed: %s\n",
+ dev, strerror(errno));
+ return 1;
+ }
+
+ printf("RESULT { ");
+ if (opt_atlas)
+ {
+ printf(
+ DBQ(id) ":" DBQ(%s) ", " DBQ(fw) ": %d, " DBQ(time) ": %ld, ",
+ opt_atlas, get_atlas_fw_version(), (long)time(NULL));
+ }
+ printf(DBQ(bsize) ": %ld, " DBQ(blocks) ": %ld, "
+ DBQ(bfree) ": %ld, " DBQ(free) ": %ld",
+ (long)sb.f_bsize, (long)sb.f_blocks, (long)sb.f_bfree,
+ (long)sb.f_bfree*(sb.f_bsize/1024));
+ printf(" }\n");
+
+ avail= sb.f_bavail*(sb.f_bsize/1024);
+
+ limit= strtoul(limit_str, &check, 10);
+ if (check[0] != '\0')
+ {
+ fprintf(stderr, "unable to parse limit '%s'\n", limit_str);
+ return 1;
+ }
+ if (avail > limit)
+ {
+ fprintf(stderr, "enough space free, no need to do anything\n");
+ return 1;
+ }
+
+ for (i= optind+2; i < argc; i++)
+ {
+ dir_str= argv[i];
+
+ dir= opendir(dir_str);
+ if (!dir)
+ {
+ fprintf(stderr, "opendir failed for '%s'\n", dir_str);
+ continue;
+ }
+
+ path= NULL;
+ while (de= readdir(dir), de != NULL)
+ {
+ if (strcmp(de->d_name, ".") == 0 ||
+ strcmp(de->d_name, "..") == 0)
+ {
+ continue;
+ }
+ len= strlen(dir_str) + 1 + strlen(de->d_name) + 1;
+ path= realloc(path, len); /* Avoid leaks */
+ if (path == NULL)
+ {
+ fprintf(stderr,
+ "unable to allocate %ld bytes\n",
+ (long)len);
+ continue;
+ }
+ strlcpy(path, dir_str, len);
+ strlcat(path, "/", len);
+ strlcat(path, de->d_name, len);
+
+ if (unlink(path) != 0)
+ {
+ fprintf(stderr, "unable to unlink '%s': %s\n",
+ path, strerror(errno));
+ continue;
+ }
+ fprintf(stderr, "rm %s\n", path);
+ }
+ closedir(dir);
+ free(path); path= NULL;
+
+ }
+
+ return 0;
+}
diff --git a/coreutils/findpid.c b/coreutils/findpid.c
new file mode 100644
index 0000000..fa1d572
--- /dev/null
+++ b/coreutils/findpid.c
@@ -0,0 +1,33 @@
+/* vi: set sw=4 ts=4:
+ *
+ * Copyright (c) 2010-2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * simple find_pid_name. return 0 if a name is found
+ */
+
+#include "libbb.h"
+
+/* This is a NOFORK applet. Be very careful! */
+
+int findpid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int findpid_main(int argc UNUSED_PARAM, char **argv)
+{
+ pid_t* pidList;
+ procps_status_t* p = NULL;
+
+ if (argc > 1)
+ {
+ while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_COMM|PSSCAN_ARGVN)))
+ {
+ if (comm_match(p, argv[1])
+ /* or we require argv0 to match (essential for matching reexeced
+ /proc/self/exe)*/
+ || (p->argv0 && strcmp(bb_basename(p->argv0), argv) == 0)
+ /* TODO: we can also try /proc/NUM/exe link, do we want that? */
+ ) {
+ return EXIT_SUCCESS;
+ }
+ }
+ }
+ return EXIT_FAILURE;
+}
diff --git a/coreutils/sleepkick.c b/coreutils/sleepkick.c
new file mode 100644
index 0000000..cd06867
--- /dev/null
+++ b/coreutils/sleepkick.c
@@ -0,0 +1,74 @@
+/* vi: set sw=4 ts=4:
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ * Copyright (c) 2010-2013 RIPE NCC, Antony <antony@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * sleep implementation for busybox with watchdog petting.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* BB_AUDIT GNU issues -- fancy version matches except args must be ints. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/sleep.html */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Rewritten to do proper arg and error checking.
+ * Also, added a 'fancy' configuration to accept multiple args with
+ * time suffixes for seconds, minutes, hours, and days.
+ */
+
+#include "libbb.h"
+#define WATCHDOGDEV "/dev/watchdog"
+
+/* This is a NOFORK applet. Be very careful! */
+
+int sleepkick_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+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;
+}
diff --git a/coreutils/test.c b/coreutils/test.c
index ae40192..1b7a51b 100644
--- a/coreutils/test.c
+++ b/coreutils/test.c
@@ -42,7 +42,7 @@
unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
- binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
+ binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-ad"|"-le"|"-lt"|
"-nt"|"-ot"|"-ef";
operand ::= <any legal UNIX file name>
*/
@@ -82,6 +82,7 @@ enum token {
INTNE,
INTGE,
INTGT,
+ INTAD,
INTLE,
INTLT,
UNOT,
@@ -154,6 +155,7 @@ static const char *const TOKSTR[] = {
"INTNE",
"INTGE",
"INTGT",
+ "INTAD'
"INTLE",
"INTLT",
"UNOT",
@@ -216,6 +218,7 @@ static const struct operator_t ops[] = {
{ "-ge", INTGE , BINOP },
{ "-gt", INTGT , BINOP },
{ "-le", INTLE , BINOP },
+ { "-ad", INTAD , BINOP },
{ "-lt", INTLT , BINOP },
{ "-nt", FILNT , BINOP },
{ "-ot", FILOT , BINOP },
@@ -383,6 +386,8 @@ static int binop(void)
return val1 >= val2;
if (op->op_num == INTGT)
return val1 > val2;
+ if (op->op_num == INTAD)
+ return 7;
if (op->op_num == INTLE)
return val1 <= val2;
if (op->op_num == INTLT)
diff --git a/eperd/Config.in b/eperd/Config.in
new file mode 100644
index 0000000..f547875
--- /dev/null
+++ b/eperd/Config.in
@@ -0,0 +1,56 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Eperd"
+
+config EOOQD
+ bool "Eooqd"
+ default n
+ select FEATURE_SUID
+ select FEATURE_SYSLOG
+ help
+ Eooqd runs Atlas measurements just once.
+
+config EPERD
+ bool "Eperd"
+ default n
+ select FEATURE_SUID
+ select FEATURE_SYSLOG
+ help
+ Eperd periodically runs Atlas measurements. It is based on crond.
+
+config EVHTTPGET
+ bool "evhttpget"
+ default n
+ help
+ standalone version of event-driven httpget
+
+config EVPING
+ bool "evping"
+ default n
+ help
+ standalone version of event-driven ping
+
+config EVTDIG
+ bool "evtdig"
+ default n
+ depends on EPERD
+ help
+ tiny dig event driven version. support only limited queries id.sever
+ txt chaos. RIPE NCC 2011
+
+config FEATURE_EVTDIG_DEBUG
+ bool "Enable debug support in evtdig"
+ default n
+ depends on EVTDIG
+ help
+ extra debug info. Also may cause segfault or/and memory leak. Add at your own risk.
+
+config EVTRACEROUTE
+ bool "evtraceroute"
+ default n
+ help
+ standalone version of event-driven traceroute
+endmenu
diff --git a/eperd/Kbuild b/eperd/Kbuild
new file mode 100644
index 0000000..24d257a
--- /dev/null
+++ b/eperd/Kbuild
@@ -0,0 +1,8 @@
+# Makefile for busybox
+#
+# Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_EPERD) += eooqd.o eperd.o condmv.o httpget.o ping.o traceroute.o evhttpget.o evping.o evtdig.o evtraceroute.o tcputil.o readresolv.o
diff --git a/eperd/condmv.c b/eperd/condmv.c
new file mode 100644
index 0000000..78bc9be
--- /dev/null
+++ b/eperd/condmv.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * condmv.c -- move a file only if the destination doesn't exist
+ */
+
+#include "libbb.h"
+#include "eperd.h"
+
+#define SAFE_PREFIX_FROM ATLAS_DATA_NEW
+#define SAFE_PREFIX_TO ATLAS_DATA_OUT
+
+#define A_FLAG (1 << 0)
+#define F_FLAG (1 << 1)
+
+#define DEFAULT_INTERVAL 60
+
+struct condmvstate
+{
+ char *from;
+ char *to;
+ char *atlas;
+ int force;
+ int interval;
+};
+
+static void *condmv_init(int argc, char *argv[],
+ void (*done)(void *state) UNUSED_PARAM)
+{
+ char *opt_add, *opt_interval, *from, *to, *check;
+ int interval;
+ uint32_t opt;
+ struct condmvstate *state;
+
+ opt_add= NULL;
+ opt_interval= NULL;
+ opt_complementary= NULL; /* For when we are called by crond */
+ opt= getopt32(argv, "!A:fi:", &opt_add, &opt_interval);
+ if (opt == (uint32_t)-1)
+ return NULL;
+
+ if (argc != optind + 2)
+ {
+ crondlog(LVL8 "too many or too few arguments (required 2)");
+ return NULL;
+ }
+
+ if (opt_interval)
+ {
+ interval= strtoul(opt_interval, &check, 0);
+ if (interval <= 0)
+ {
+ crondlog(LVL8 "unable to parse interval '%s'",
+ opt_interval);
+ return NULL;
+ }
+ }
+ else
+ interval= DEFAULT_INTERVAL;
+
+ from= argv[optind];
+ to= argv[optind+1];
+
+ if (!validate_filename(from, SAFE_PREFIX_FROM))
+ {
+ fprintf(stderr, "insecure from file '%s'\n", from);
+ return NULL;
+ }
+ if (!validate_filename(to, SAFE_PREFIX_TO))
+ {
+ fprintf(stderr, "insecure to file '%s'\n", to);
+ return NULL;
+ }
+
+ state= malloc(sizeof(*state));
+ state->from= strdup(from);
+ state->to= strdup(to);
+ state->atlas= opt_add ? strdup(opt_add) : NULL;
+ state->force= !!(opt & F_FLAG);
+ state->interval= interval;
+
+ return state;
+}
+
+static void condmv_start(void *state)
+{
+ size_t len;
+ time_t mytime;
+ char *to;
+ FILE *file;
+ struct condmvstate *condmvstate;
+ struct stat sb;
+
+ condmvstate= state;
+
+ len= strlen(condmvstate->to) + 20;
+ to= malloc(len);
+ snprintf(to, len, "%s.%ld", condmvstate->to,
+ (long)time(NULL)/condmvstate->interval);
+
+ crondlog(LVL7 "condmv_start: destination '%s'\n", to);
+
+ if (stat(to, &sb) == 0 && !condmvstate->force)
+ {
+ free(to);
+ return;
+ }
+
+ if (condmvstate->atlas)
+ {
+ mytime = time(NULL);
+ /* We have to add something to the existing file before moving
+ * to.
+ */
+ file= fopen(condmvstate->from, "a");
+ if (file == NULL)
+ {
+ crondlog(LVL9 "condmv: unable to append to '%s': %s\n",
+ condmvstate->from, strerror(errno));
+ free(to);
+ return;
+ }
+ if (fprintf(file, "%s %lu %s\n", condmvstate->atlas, mytime,
+ condmvstate->from) < 0)
+ {
+ crondlog(LVL9 "condmv: unable to append to '%s': %s\n",
+ condmvstate->from, strerror(errno));
+ fclose(file);
+ free(to);
+ return;
+ }
+ if (fclose(file) != 0)
+ {
+ crondlog(LVL9 "condmv: unable to close '%s': %s\n",
+ condmvstate->from, strerror(errno));
+ free(to);
+ return;
+ }
+ }
+ if (rename(condmvstate->from, to) != 0)
+ {
+ crondlog(LVL9 "condmv: unable to rename '%s' to '%s': %s\n",
+ condmvstate->from, to, strerror(errno));
+ }
+ free(to);
+}
+
+static int condmv_delete(void *state)
+{
+ struct condmvstate *condmvstate;
+
+ condmvstate= state;
+ free(condmvstate->from);
+ condmvstate->from= NULL;
+ free(condmvstate->to);
+ condmvstate->to= NULL;
+ free(condmvstate->atlas);
+ condmvstate->atlas= NULL;
+
+ free(condmvstate);
+
+ return 1;
+}
+
+struct testops condmv_ops = { condmv_init, condmv_start, condmv_delete };
+
diff --git a/eperd/eooqd.c b/eperd/eooqd.c
new file mode 100644
index 0000000..298e63c
--- /dev/null
+++ b/eperd/eooqd.c
@@ -0,0 +1,805 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * eooqd.c Libevent-based One-off queue daemon
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <libbb.h>
+#include <event2/event.h>
+#include <event2/event_struct.h>
+#include <event2/dns.h>
+
+#include "eperd.h"
+
+#define SUFFIX ".curr"
+#define OOQD_NEW_PREFIX "/home/atlas/data/new/ooq"
+#define OOQD_OUT "/home/atlas/data/ooq.out/ooq"
+
+#define ATLAS_NARGS 64 /* Max arguments to a built-in command */
+#define ATLAS_ARGSIZE 512 /* Max size of the command line */
+
+#define SAFE_PREFIX ATLAS_DATA_NEW
+
+#define DBQ(str) "\"" #str "\""
+
+struct slot
+{
+ void *cmdstate;
+};
+
+static struct
+{
+ char *queue_file;
+ char *atlas_id;
+ char curr_qfile[256];
+ FILE *curr_file;
+ int max_busy;
+ int curr_busy;
+ int curr_index;
+ struct slot *slots;
+} *state;
+
+static struct builtin
+{
+ const char *cmd;
+ struct testops *testops;
+} builtin_cmds[]=
+{
+ { "evhttpget", &httpget_ops },
+ { "evping", &ping_ops },
+ { "evtdig", &tdig_ops },
+ { "evtraceroute", &traceroute_ops },
+ { NULL, NULL }
+};
+
+
+static void process(FILE *file);
+static void report(const char *fmt, ...);
+static void report_err(const char *fmt, ...);
+
+static void checkQueue(evutil_socket_t fd, short what, void *arg);
+static void add_line(void);
+static void cmddone(void *cmdstate);
+static void re_post(evutil_socket_t fd, short what, void *arg);
+static void post_results(void);
+static void skip_space(char *cp, char **ncpp);
+static void skip_nonspace(char *cp, char **ncpp);
+static void find_eos(char *cp, char **ncpp);
+
+int eooqd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int eooqd_main(int argc, char *argv[])
+{
+ int r;
+ uint32_t opt;
+ char *atlas_id, *pid_file_name;
+ struct event *checkQueueEvent, *rePostEvent;
+ struct timeval tv;
+
+ atlas_id= NULL;
+ pid_file_name= NULL;
+
+ opt = getopt32(argv, "A:P:", &atlas_id, &pid_file_name);
+
+ if (argc != optind+1)
+ {
+ bb_show_usage();
+ return 1;
+ }
+
+ if(pid_file_name)
+ {
+ write_pidfile(pid_file_name);
+ }
+
+ state = xzalloc(sizeof(*state));
+
+ state->atlas_id= atlas_id;
+ state->queue_file= argv[optind];
+
+ state->max_busy= 10;
+
+ state->slots= xzalloc(sizeof(*state->slots) * state->max_busy);
+
+ if (strlen(state->queue_file) + strlen(SUFFIX) + 1 >
+ sizeof(state->curr_qfile))
+ {
+ report("filename too long ('%s')", state->queue_file);
+ return 1;
+ }
+
+ strlcpy(state->curr_qfile, state->queue_file,
+ sizeof(state->curr_qfile));
+ strlcat(state->curr_qfile, SUFFIX, sizeof(state->curr_qfile));
+
+ /* Create libevent event base */
+ EventBase= event_base_new();
+ if (!EventBase)
+ {
+ crondlog(DIE9 "event_base_new failed"); /* exits */
+ }
+ DnsBase= evdns_base_new(EventBase, 1 /*initialize*/);
+ if (!DnsBase)
+ {
+ crondlog(DIE9 "evdns_base_new failed"); /* exits */
+ }
+
+ DnsBase = evdns_base_new(EventBase, 1);
+ if(!DnsBase) {
+ event_base_free(EventBase);
+ crondlog(DIE9 "evdns_base_new failed"); /* exits */
+ }
+
+ checkQueueEvent= event_new(EventBase, -1, EV_TIMEOUT|EV_PERSIST,
+ checkQueue, NULL);
+ if (!checkQueueEvent)
+ crondlog(DIE9 "event_new failed"); /* exits */
+ tv.tv_sec= 1;
+ tv.tv_usec= 0;
+ event_add(checkQueueEvent, &tv);
+
+ rePostEvent= event_new(EventBase, -1, EV_TIMEOUT|EV_PERSIST,
+ re_post, NULL);
+ if (!rePostEvent)
+ crondlog(DIE9 "event_new failed"); /* exits */
+ tv.tv_sec= 60;
+ tv.tv_usec= 0;
+ event_add(rePostEvent, &tv);
+#if 0
+ for(;;)
+ {
+ /* Try to move queue_file to curr_qfile. This provide at most
+ * once behavior and allows producers to create a new
+ * queue_file while we process the old one.
+ */
+ if (rename(queue_file, curr_qfile) == -1)
+ {
+ if (errno == ENOENT)
+ {
+ sleep(WAIT_TIME);
+ continue;
+ }
+ report_err("rename failed");
+ return 1;
+ }
+
+ file= fopen(curr_qfile, "r");
+ if (file == NULL)
+ {
+ report_err("open '%s' failed", curr_qfile);
+ continue;
+ }
+
+ process(file);
+
+ fclose(file);
+
+ /* No need to delete curr_qfile */
+ }
+#endif
+ r= event_base_loop(EventBase, 0);
+ if (r != 0)
+ crondlog(LVL9 "event_base_loop failed");
+ return 0;
+}
+
+static void checkQueue(evutil_socket_t fd UNUSED_PARAM,
+ short what UNUSED_PARAM, void *arg UNUSED_PARAM)
+{
+ if (!state->curr_file)
+ {
+ /* Try to move queue_file to curr_qfile. This provide at most
+ * once behavior and allows producers to create a new
+ * queue_file while we process the old one.
+ */
+ if (rename(state->queue_file, state->curr_qfile) == -1)
+ {
+ if (errno == ENOENT)
+ {
+ return;
+ }
+ report_err("rename failed");
+ return;
+ }
+
+ state->curr_file= fopen(state->curr_qfile, "r");
+ if (state->curr_file == NULL)
+ {
+ report_err("open '%s' failed", state->curr_qfile);
+ return;
+ }
+ }
+
+ while (state->curr_file && state->curr_busy < state->max_busy)
+ {
+ add_line();
+ }
+}
+
+static void add_line(void)
+{
+ char c;
+ int i, argc, skip, slot;
+ size_t len;
+ char *cp, *ncp;
+ struct builtin *bp;
+ char *p;
+ const char *reason;
+ void *cmdstate;
+ FILE *fn;
+ const char *argv[ATLAS_NARGS];
+ char args[ATLAS_ARGSIZE];
+ char cmdline[256];
+ char filename[80];
+ struct stat sb;
+
+ if (fgets(cmdline, sizeof(cmdline), state->curr_file) == NULL)
+ {
+ if (ferror(state->curr_file))
+ report_err("error reading queue file");
+ fclose(state->curr_file);
+ state->curr_file= NULL;
+ return;
+ }
+
+ cp= strchr(cmdline, '\n');
+ if (cp)
+ *cp= '\0';
+
+ crondlog(LVL7 "atlas_run: looking for '%s'", cmdline);
+
+ cmdstate= NULL;
+ reason= NULL;
+ for (bp= builtin_cmds; bp->cmd != NULL; bp++)
+ {
+ len= strlen(bp->cmd);
+ if (strncmp(cmdline, bp->cmd, len) != 0)
+ continue;
+ if (cmdline[len] != ' ')
+ continue;
+ break;
+ }
+ if (bp->cmd == NULL)
+ {
+ reason="command not found";
+ goto error;
+ }
+
+ crondlog(LVL7 "found cmd '%s' for '%s'", bp->cmd, cmdline);
+
+ len= strlen(cmdline);
+ if (len+1 > ATLAS_ARGSIZE)
+ {
+ crondlog(LVL8 "atlas_run: command line too big: '%s'", cmdline);
+ reason="command line too big";
+ goto error;
+ }
+ strcpy(args, cmdline);
+
+ /* Split the command line */
+ cp= args;
+ argc= 0;
+ argv[argc]= cp;
+ skip_nonspace(cp, &ncp);
+ cp= ncp;
+
+ for(;;)
+ {
+ /* End of list */
+ if (cp[0] == '\0')
+ {
+ argc++;
+ break;
+ }
+
+ /* Find start of next argument */
+ skip_space(cp, &ncp);
+
+ /* Terminate current one */
+ cp[0]= '\0';
+ argc++;
+
+ if (argc >= ATLAS_NARGS-1)
+ {
+ crondlog(
+ LVL8 "atlas_run: command line '%s', too many arguments",
+ cmdline);
+ reason="too many arguments";
+ goto error;
+ }
+
+ cp= ncp;
+ argv[argc]= cp;
+ if (cp[0] == '"')
+ {
+ /* Special code for strings */
+ find_eos(cp+1, &ncp);
+ if (ncp[0] != '"')
+ {
+ crondlog(
+ LVL8 "atlas_run: command line '%s', end of string not found",
+ cmdline);
+ reason="end of string not found";
+ goto error;
+ }
+ argv[argc]= cp+1;
+ cp= ncp;
+ cp[0]= '\0';
+ cp++;
+ }
+ else
+ {
+ skip_nonspace(cp, &ncp);
+ cp= ncp;
+ }
+ }
+
+ if (argc >= ATLAS_NARGS-2)
+ {
+ crondlog(
+ LVL8 "atlas_run: command line '%s', too many arguments",
+ cmdline);
+ reason="too many arguments";
+ goto error;
+ }
+
+ /* find a slot for this command */
+ for (skip= 1; skip <= state->max_busy; skip++)
+ {
+ slot= (state->curr_index+skip) % state->max_busy;
+ if (state->slots[slot].cmdstate == NULL)
+ break;
+ }
+ if (state->slots[slot].cmdstate != NULL)
+ crondlog(DIE9 "no empty slot?");
+ argv[argc++]= "-O";
+ snprintf(filename, sizeof(filename), OOQD_NEW_PREFIX ".%d", slot);
+ argv[argc++]= filename;
+
+ argv[argc]= NULL;
+
+ for (i= 0; i<argc; i++)
+ crondlog(LVL7 "atlas_run: argv[%d] = '%s'", i, argv[i]);
+
+ cmdstate= bp->testops->init(argc, argv, cmddone);
+ crondlog(LVL7 "init returned %p for '%s'", cmdstate, cmdline);
+
+ if (cmdstate != NULL)
+ {
+ state->slots[slot].cmdstate= cmdstate;
+ state->curr_index= slot;
+ state->curr_busy++;
+
+ bp->testops->start(cmdstate);
+ }
+
+error:
+ if (cmdstate == NULL)
+ {
+ fn= fopen(OOQD_NEW_PREFIX, "a");
+ if (!fn)
+ {
+ crondlog(DIE9 "unable to append to '%s'",
+ OOQD_NEW_PREFIX);
+ }
+ fprintf(fn, "RESULT { ");
+ if (state->atlas_id)
+ fprintf(fn, DBQ(id) ":" DBQ(%s) ", ", state->atlas_id);
+ fprintf(fn, DBQ(fw) ":" DBQ(%d) ", " DBQ(time) ":%ld, ",
+ get_atlas_fw_version(), (long)time(NULL));
+ if (reason)
+ fprintf(fn, DBQ(reason) ":" DBQ(%s) ", ", reason);
+ fprintf(fn, DBQ(cmd) ": \"");
+ for (p= cmdline; *p; p++)
+ {
+ c= *p;
+ if (c == '"' || c == '\\')
+ fprintf(fn, "\\%c", c);
+ else if (isprint((unsigned char)c))
+ fputc(c, fn);
+ else
+ fprintf(fn, "\\u%04x", (unsigned char)c);
+ }
+ fprintf(fn, "\"");
+ fprintf(fn, " }\n");
+ fclose(fn);
+
+ if (stat(OOQD_OUT, &sb) == -1 &&
+ stat(OOQD_NEW_PREFIX, &sb) == 0)
+ {
+ if (rename(OOQD_NEW_PREFIX, OOQD_OUT) == -1)
+ {
+ report_err("move '%s' to '%s' failed",
+ OOQD_NEW_PREFIX, OOQD_OUT);
+ }
+ }
+ post_results();
+ }
+}
+
+static void cmddone(void *cmdstate)
+{
+ int i;
+ char from_filename[80];
+ char to_filename[80];
+ struct stat sb;
+
+ report("command is done for cmdstate %p", cmdstate);
+
+ /* Find command */
+ for (i= 0; i<state->max_busy; i++)
+ {
+ if (state->slots[i].cmdstate == cmdstate)
+ break;
+ }
+ if (i >= state->max_busy)
+ {
+ report("cmddone: state state %p", cmdstate);
+ return;
+ }
+ state->slots[i].cmdstate= NULL;
+ state->curr_busy--;
+
+ snprintf(from_filename, sizeof(from_filename),
+ "/home/atlas/data/new/ooq.%d", i);
+ snprintf(to_filename, sizeof(to_filename),
+ "/home/atlas/data/ooq.out/%d", i);
+ if (stat(to_filename, &sb) == 0)
+ {
+ report("output file '%s' is busy", to_filename);
+
+ /* continue, we may have to post */
+ }
+ else if (rename(from_filename, to_filename) == -1)
+ {
+ report_err("move '%s' to '%s' failed",
+ from_filename, to_filename);
+ }
+
+ if (state->curr_busy == 0)
+ {
+ post_results();
+ }
+}
+
+static void re_post(evutil_socket_t fd UNUSED_PARAM, short what UNUSED_PARAM,
+ void *arg UNUSED_PARAM)
+{
+ /* Just call post_results every once in awhile in case some results
+ * were left behind.
+ */
+ post_results();
+}
+
+static void post_results(void)
+{
+ int i, j, r, need_post;
+ const char *argv[20];
+ char from_filename[80];
+ char to_filename[80];
+ struct stat sb;
+
+ for (j= 0; j<5; j++)
+ {
+ /* Grab results and see if something need to be done. */
+ need_post= 0;
+
+ if (stat(OOQD_OUT, &sb) == 0)
+ {
+ /* There is more to post */
+ need_post= 1;
+ } else if (stat(OOQD_NEW_PREFIX, &sb) == 0)
+ {
+ if (rename(OOQD_NEW_PREFIX, OOQD_OUT) == 0)
+ need_post= 1;
+ else
+ {
+ report_err("move '%s' to '%s' failed",
+ OOQD_NEW_PREFIX, OOQD_OUT);
+ }
+ }
+ for (i= 0; i<state->max_busy; i++)
+ {
+ snprintf(from_filename, sizeof(from_filename),
+ "/home/atlas/data/new/ooq.%d", i);
+ snprintf(to_filename, sizeof(to_filename),
+ "/home/atlas/data/ooq.out/%d", i);
+ if (stat(to_filename, &sb) == 0)
+ {
+ /* There is more to post */
+ need_post= 1;
+ continue;
+ }
+ if (stat(from_filename, &sb) == -1)
+ {
+ /* Nothing to do */
+ continue;
+ }
+
+ need_post= 1;
+ if (rename(from_filename, to_filename) == -1)
+ {
+ report_err("move '%s' to '%s' failed",
+ from_filename, to_filename);
+ }
+ }
+
+ if (!need_post)
+ break;
+
+ i= 0;
+ argv[i++]= "httppost";
+ argv[i++]= "-A";
+ argv[i++]= "9015";
+ argv[i++]= "--delete-file";
+ argv[i++]= "--post-header";
+ argv[i++]= "/home/atlas/status/p_to_c_report_header";
+ argv[i++]= "--post-dir";
+ argv[i++]= "/home/atlas/data/ooq.out";
+ argv[i++]= "--post-footer";
+ argv[i++]= "/home/atlas/status/con_session_id.txt";
+ argv[i++]= "-O";
+ argv[i++]= "/home/atlas/data/new/ooq_sent.vol";
+ argv[i++]= "http://127.0.0.1:8080/";
+ argv[i]= NULL;
+ r= httppost_main(i, argv);
+ if (r != 0)
+ {
+ report("httppost failed with %d", r);
+ return;
+ }
+
+ }
+}
+
+static void skip_space(char *cp, char **ncpp)
+{
+ while (cp[0] != '\0' && isspace(*(unsigned char *)cp))
+ cp++;
+ *ncpp= cp;
+}
+
+static void skip_nonspace(char *cp, char **ncpp)
+{
+ while (cp[0] != '\0' && !isspace(*(unsigned char *)cp))
+ cp++;
+ *ncpp= cp;
+}
+
+static void find_eos(char *cp, char **ncpp)
+{
+ while (cp[0] != '\0' && cp[0] != '"')
+ cp++;
+ *ncpp= cp;
+}
+
+#if 0
+static void process(FILE *file)
+{
+ int i, argc, do_append, saved_fd, out_fd, flags;
+ size_t len;
+ char *cp, *ncp, *outfile;
+ struct builtin *bp;
+ char line[256];
+ char *argv[NARGS];
+
+printf("in process\n");
+ while (cp= fgets(line, sizeof(line), file), cp != NULL)
+ {
+printf("got cp %p, line %p, '%s'\n", cp, line, cp);
+ if (strchr(line, '\n') == NULL)
+ {
+ report("line '%s' too long", line);
+ return;
+ }
+
+ /* Skip leading white space */
+ cp= line;
+ while (cp[0] != '\0' && isspace((unsigned char)cp[0]))
+ cp++;
+
+ if (cp[0] == '\0' || cp[0] == '#')
+ continue; /* Empty or comment line */
+
+ for (bp= builtin_cmds; bp->cmd != NULL; bp++)
+ {
+ len= strlen(bp->cmd);
+ if (strncmp(cp, bp->cmd, len) != 0)
+ continue;
+ if (cp[len] != ' ')
+ continue;
+ break;
+ }
+ if (bp->cmd == NULL)
+ {
+ report("nothing found for '%s'", cp);
+ return; /* Nothing found */
+ }
+
+ /* Remove trailing white space */
+ len= strlen(cp);
+ while (len > 0 && isspace((unsigned char)cp[len-1]))
+ {
+ cp[len-1]= '\0';
+ len--;
+ }
+
+ outfile= NULL;
+ do_append= 0;
+
+ /* Split the command line */
+ argc= 0;
+ argv[argc]= cp;
+ skip_nonspace(cp, &ncp);
+ cp= ncp;
+
+ for(;;)
+ {
+ /* End of list */
+ if (cp[0] == '\0')
+ {
+ argc++;
+ break;
+ }
+
+ /* Find start of next argument */
+ skip_space(cp, &ncp);
+
+ /* Terminate current one */
+ cp[0]= '\0';
+
+ /* Special case for '>' */
+ if (argv[argc][0] == '>')
+ {
+ cp= argv[argc]+1;
+ if (cp[0] == '>')
+ {
+ /* Append */
+ do_append= 1;
+ cp++;
+ }
+ if (cp[0] != '\0')
+ {
+ /* Filename immediately follows '>' */
+ outfile= cp;
+
+ /* And move on with the next option */
+ }
+ else
+ {
+ /* Get the next argument */
+ outfile= ncp;
+ cp= ncp;
+ skip_nonspace(cp, &ncp);
+ cp= ncp;
+
+ if (cp[0] == '\0')
+ break;
+
+ /* Find start of next argument */
+ skip_space(cp, &ncp);
+ *cp= '\0';
+
+ if (ncp[0] == '\0')
+ break; /* No more arguments */
+ }
+ }
+ else
+ {
+ argc++;
+ }
+
+ if (argc >= NARGS-1)
+ {
+ report("command line '%s', too arguments",
+ line);
+ continue; /* Just skip it */
+ }
+
+ cp= ncp;
+ argv[argc]= cp;
+ if (cp[0] == '"')
+ {
+ /* Special code for strings */
+ find_eos(cp+1, &ncp);
+ if (ncp[0] != '"')
+ {
+ report(
+ "command line '%s', end of string not found",
+ line);
+ continue; /* Just skip it */
+ }
+ argv[argc]= cp+1;
+ cp= ncp;
+ cp[0]= '\0';
+ cp++;
+ }
+ else
+ {
+ skip_nonspace(cp, &ncp);
+ cp= ncp;
+ }
+ }
+
+ if (argc >= NARGS)
+ {
+ report("command line '%s', too many arguments", line);
+ return;
+ }
+ argv[argc]= NULL;
+
+ for (i= 0; i<argc; i++)
+ report("argv[%d] = '%s'", i, argv[i]);
+
+ saved_fd= -1; /* lint */
+ if (outfile)
+ {
+ /* Redirect I/O */
+ report("sending output to '%s'", outfile);
+ if (!validate_filename(outfile, SAFE_PREFIX))
+ {
+ report("insecure output file '%s'", outfile);
+ return;
+ }
+ flags= O_CREAT | O_WRONLY;
+ if (do_append)
+ flags |= O_APPEND;
+ out_fd= open(outfile, flags, 0644);
+ if (out_fd == -1)
+ {
+ report_err("unable to create output file '%s'",
+ outfile);
+ return;
+ }
+ fflush(stdout);
+ saved_fd= dup(1);
+ if (saved_fd == -1)
+ {
+ report("unable to dub stdout");
+ close(out_fd);
+ return;
+ }
+ dup2(out_fd, 1);
+ close(out_fd);
+ }
+
+ bp->func(argc, argv);
+
+ if (outfile)
+ {
+ fflush(stdout);
+ dup2(saved_fd, 1);
+ close(saved_fd);
+ }
+ }
+}
+#endif
+
+static void report(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "ooqd: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+
+ va_end(ap);
+}
+
+static void report_err(const char *fmt, ...)
+{
+ int terrno;
+ va_list ap;
+
+ terrno= errno;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "ooqd: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": %s\n", strerror(terrno));
+
+ va_end(ap);
+}
diff --git a/eperd/eperd.c b/eperd/eperd.c
new file mode 100644
index 0000000..48a1ef0
--- /dev/null
+++ b/eperd/eperd.c
@@ -0,0 +1,1163 @@
+/* vi: set sw=4 ts=4:
+ * eperd formerly crond but now heavily hacked for Atlas
+ *
+ * crond -d[#] -c <crondir> -f -b
+ *
+ * run as root, but NOT setuid root
+ *
+ * Copyright(c) 2013 RIPE NCC <atlas@ripe.net>
+ * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
+ * (version 2.3.2)
+ * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "libbb.h"
+#include <syslog.h>
+#include <event2/event.h>
+#include <event2/event_struct.h>
+#include <event2/dns.h>
+
+#include "eperd.h"
+
+/* glibc frees previous setenv'ed value when we do next setenv()
+ * of the same variable. uclibc does not do this! */
+#if (defined(__GLIBC__) && !defined(__UCLIBC__)) /* || OTHER_SAFE_LIBC... */
+#define SETENV_LEAKS 0
+#else
+#define SETENV_LEAKS 1
+#endif
+
+#define DBQ(str) "\"" #str "\""
+
+#ifndef CRONTABS
+#define CRONTABS "/var/spool/cron/crontabs"
+#endif
+#ifndef TMPDIR
+#define TMPDIR "/var/spool/cron"
+#endif
+#ifndef SENDMAIL
+#define SENDMAIL "sendmail"
+#endif
+#ifndef SENDMAIL_ARGS
+#define SENDMAIL_ARGS "-ti", "oem"
+#endif
+#ifndef CRONUPDATE
+#define CRONUPDATE "cron.update"
+#endif
+#ifndef MAXLINES
+#define MAXLINES 256 /* max lines in non-root crontabs */
+#endif
+
+#define URANDOM_DEV "/dev/urandom"
+#define ATLAS_FW_VERSION "/home/atlas/state/FIRMWARE_APPS_VERSION"
+
+struct CronLine {
+ struct CronLine *cl_Next;
+ char *cl_Shell; /* shell command */
+ pid_t cl_Pid; /* running pid, 0, or armed (-1) */
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+ int cl_MailPos; /* 'empty file' size */
+ smallint cl_MailFlag; /* running pid is for mail */
+ char *cl_MailTo; /* whom to mail results */
+#endif
+ unsigned interval;
+ time_t nextcycle;
+ time_t start_time;
+ time_t end_time;
+ enum distribution { DISTR_NONE, DISTR_UNIFORM } distribution;
+ int distr_param; /* Parameter for distribution, if any */
+ int distr_offset; /* Current offset to randomize the interval */
+ struct event event;
+ struct testops *testops;
+ void *teststate;
+
+ /* For cleanup */
+ char needs_delete;
+
+ /* For debugging */
+ time_t lasttime;
+};
+
+
+#define DaemonUid 0
+
+
+enum {
+ OPT_l = (1 << 0),
+ OPT_L = (1 << 1),
+ OPT_f = (1 << 2),
+ OPT_b = (1 << 3),
+ OPT_S = (1 << 4),
+ OPT_c = (1 << 5),
+ OPT_A = (1 << 6),
+ OPT_D = (1 << 7),
+ OPT_d = (1 << 8) * ENABLE_FEATURE_CROND_D,
+};
+#if ENABLE_FEATURE_CROND_D
+#define DebugOpt (option_mask32 & OPT_d)
+#else
+#define DebugOpt 0
+#endif
+
+
+struct globals G;
+#define INIT_G() do { \
+ LogLevel = 8; \
+ CDir = CRONTABS; \
+} while (0)
+
+static int do_kick_watchdog;
+static char *out_filename= NULL;
+static char *atlas_id= NULL;
+
+static void CheckUpdates(evutil_socket_t fd, short what, void *arg);
+static void CheckUpdatesHour(evutil_socket_t fd, short what, void *arg);
+static void SynchronizeDir(void);
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+static void EndJob(const char *user, CronLine *line);
+#else
+#define EndJob(user, line) ((line)->cl_Pid = 0)
+#endif
+static void DeleteFile(void);
+static int Insert(CronLine *line);
+static void Start(CronLine *line);
+static void atlas_init(CronLine *line);
+static void RunJob(evutil_socket_t fd, short what, void *arg);
+
+void crondlog(const char *ctl, ...)
+{
+ va_list va;
+ int level = (ctl[0] & 0x1f);
+
+ va_start(va, ctl);
+ if (level >= (int)LogLevel) {
+ /* Debug mode: all to (non-redirected) stderr, */
+ /* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */
+ if (!DebugOpt && LogFile) {
+ /* Otherwise (log to file): we reopen log file at every write: */
+ int logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600);
+ if (logfd >= 0)
+ xmove_fd(logfd, STDERR_FILENO);
+ }
+// TODO: ERR -> error, WARN -> warning, LVL -> info
+ bb_verror_msg(ctl + 1, va, /* strerr: */ NULL);
+ }
+ va_end(va);
+ if (ctl[0] & 0x80)
+ exit(20);
+}
+
+int get_atlas_fw_version(void)
+{
+ static int fw_version= -1;
+
+ int r, fw;
+ FILE *file;
+
+ if (fw_version != -1)
+ return fw_version;
+
+ file= fopen(ATLAS_FW_VERSION, "r");
+ if (file == NULL)
+ {
+ crondlog(LVL9 "get_atlas_fw_version: unable to open '%s': %s",
+ ATLAS_FW_VERSION, strerror(errno));
+ return -1;
+ }
+ r= fscanf(file, "%d", &fw);
+ fclose(file);
+ if (r == -1)
+ {
+ crondlog(LVL9 "get_atlas_fw_version: unable to read from '%s'",
+ ATLAS_FW_VERSION);
+ return -1;
+ }
+
+ fw_version= fw;
+ return fw;
+}
+
+static void my_exit(void)
+{
+ crondlog(LVL8 "in my_exit (exit was called!)");
+ abort();
+}
+
+static void kick_watchdog(void)
+{
+ if(do_kick_watchdog)
+ {
+ int fdwatchdog = open("/dev/watchdog", O_RDWR);
+ write(fdwatchdog, "1", 1);
+ close(fdwatchdog);
+ }
+}
+
+#if 0
+static void FAST_FUNC Xbb_daemonize_or_rexec(int flags, char **argv)
+{
+ int fd;
+
+ if (flags & DAEMON_CHDIR_ROOT)
+ xchdir("/");
+
+ if (flags & DAEMON_DEVNULL_STDIO) {
+ close(0);
+ close(1);
+ close(2);
+ }
+
+ fd = open(bb_dev_null, O_RDWR);
+ if (fd < 0) {
+ /* NB: we can be called as bb_sanitize_stdio() from init
+ * or mdev, and there /dev/null may legitimately not (yet) exist!
+ * Do not use xopen above, but obtain _ANY_ open descriptor,
+ * even bogus one as below. */
+ fd = xopen("/", O_RDONLY); /* don't believe this can fail */
+ }
+
+ while ((unsigned)fd < 2)
+ fd = dup(fd); /* have 0,1,2 open at least to /dev/null */
+
+ if (!(flags & DAEMON_ONLY_SANITIZE)) {
+ //forkexit_or_rexec(argv);
+ /* if daemonizing, make sure we detach from stdio & ctty */
+ setsid();
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ }
+ while (fd > 2) {
+ close(fd--);
+ if (!(flags & DAEMON_CLOSE_EXTRA_FDS))
+ return;
+ /* else close everything after fd#2 */
+ }
+}
+#endif
+
+int eperd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int eperd_main(int argc UNUSED_PARAM, char **argv)
+{
+ unsigned opt;
+ int r, fd;
+ unsigned seed;
+ struct event *updateEventMin, *updateEventHour;
+ struct timeval tv;
+
+ const char *PidFileName = NULL;
+
+ atexit(my_exit);
+
+ 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" USE_FEATURE_PERD_D(":d-l")
+ ":l+:d+"; /* -l and -d have numeric param */
+ opt = getopt32(argv, "l:L:fbSc:A:DP:" USE_FEATURE_PERD_D("d:") "O:",
+ &LogLevel, &LogFile, &CDir, &atlas_id, &PidFileName
+ USE_FEATURE_PERD_D(,&LogLevel), &out_filename);
+ /* both -d N and -l N set the same variable: LogLevel */
+
+ if (!(opt & OPT_f)) {
+ /* close stdin, stdout, stderr.
+ * close unused descriptors - don't need them. */
+ bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
+ }
+
+ if (!DebugOpt && LogFile == NULL) {
+ /* logging to syslog */
+ openlog(applet_name, LOG_CONS | LOG_PID, LOG_LOCAL6);
+ logmode = LOGMODE_SYSLOG;
+ }
+
+ do_kick_watchdog= !!(opt & OPT_D);
+
+ xchdir(CDir);
+ //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */
+ xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */
+ crondlog(LVL9 "crond (busybox "BB_VER") started, log level %d", LogLevel);
+
+ /* Create libevent event base */
+ EventBase= event_base_new();
+ if (!EventBase)
+ {
+ crondlog(DIE9 "event_base_new failed"); /* exits */
+ }
+ DnsBase= evdns_base_new(EventBase, 1 /*initialize*/);
+ if (!DnsBase)
+ {
+ crondlog(DIE9 "evdns_base_new failed"); /* exits */
+ }
+
+ fd= open(URANDOM_DEV, O_RDONLY);
+
+ /* Best effort, just ignore errors */
+ if (fd != -1)
+ {
+ read(fd, &seed, sizeof(seed));
+ close(fd);
+ }
+ crondlog(LVL7 "using seed '%u'", seed);
+ srandom(seed);
+
+ SynchronizeDir();
+
+ updateEventMin= event_new(EventBase, -1, EV_TIMEOUT|EV_PERSIST,
+ CheckUpdates, NULL);
+ if (!updateEventMin)
+ crondlog(DIE9 "event_new failed"); /* exits */
+ tv.tv_sec= 60;
+ tv.tv_usec= 0;
+ event_add(updateEventMin, &tv);
+
+ updateEventHour= event_new(EventBase, -1, EV_TIMEOUT|EV_PERSIST,
+ CheckUpdatesHour, NULL);
+ if (!updateEventHour)
+ crondlog(DIE9 "event_new failed"); /* exits */
+ tv.tv_sec= 3600;
+ tv.tv_usec= 0;
+ event_add(updateEventHour, &tv);
+
+ if(PidFileName)
+ {
+ write_pidfile(PidFileName);
+ }
+ else
+ {
+ write_pidfile("/var/run/crond.pid");
+ }
+#if 0
+ /* main loop - synchronize to 1 second after the minute, minimum sleep
+ * of 1 second. */
+ {
+ time_t t1 = time(NULL);
+ time_t next;
+ time_t last_minutely= 0;
+ time_t last_hourly= 0;
+ int sleep_time = 10; /* AA previously 60 */
+ for (;;) {
+ kick_watchdog();
+ sleep(sleep_time);
+
+ kick_watchdog();
+
+ if (t1 >= last_minutely + 60)
+ {
+ last_minutely= t1;
+ CheckUpdates();
+ }
+ if (t1 >= last_hourly + 3600)
+ {
+ last_hourly= t1;
+ SynchronizeDir();
+ }
+ {
+ sleep_time= 60;
+ if (do_kick_watchdog)
+ sleep_time= 10;
+ TestJobs(&next);
+ crondlog(LVL7 "got next %d, now %d",
+ next, time(NULL));
+ if (!next)
+ {
+ crondlog(LVL7 "calling RunJobs at %d",
+ time(NULL));
+ RunJobs();
+ crondlog(LVL7 "RunJobs ended at %d",
+ time(NULL));
+ sleep_time= 1;
+ } else if (next > t1 && next < t1+sleep_time)
+ sleep_time= next-t1;
+ if (CheckJobs() > 0) {
+ sleep_time = 10;
+ }
+ crondlog(
+ LVL7 "t1 = %d, next = %d, sleep_time = %d",
+ t1, next, sleep_time);
+ }
+ t1= time(NULL);
+ }
+ }
+#endif
+ r= event_base_loop(EventBase, 0);
+ if (r != 0)
+ crondlog(LVL9 "event_base_loop failed");
+ return 0; /* not reached */
+}
+
+#if SETENV_LEAKS
+/* We set environment *before* vfork (because we want to use vfork),
+ * so we cannot use setenv() - repeated calls to setenv() may leak memory!
+ * Using putenv(), and freeing memory after unsetenv() won't leak */
+static void safe_setenv4(char **pvar_val, const char *var, const char *val /*, int len*/)
+{
+ const int len = 4; /* both var names are 4 char long */
+ char *var_val = *pvar_val;
+
+ if (var_val) {
+ var_val[len] = '\0'; /* nuke '=' */
+ unsetenv(var_val);
+ free(var_val);
+ }
+ *pvar_val = xasprintf("%s=%s", var, val);
+ putenv(*pvar_val);
+}
+#endif
+
+static void do_distr(CronLine *line)
+{
+ long n, r, modulus, max;
+
+ line->distr_offset= 0; /* Safe default */
+ if (line->distribution == DISTR_UNIFORM)
+ {
+ /* Generate a random number in the range [0..distr_param] */
+ modulus= line->distr_param+1;
+ n= LONG_MAX/modulus;
+ max= n*modulus;
+ do
+ {
+ r= random();
+ } while (r >= max);
+ r %= modulus;
+ line->distr_offset= r - line->distr_param/2;
+ }
+ crondlog(LVL7 "do_distr: using %d", line->distr_offset);
+}
+
+static void SynchronizeFile(const char *fileName)
+{
+ struct parser_t *parser;
+ struct stat sbuf;
+ int r, maxLines;
+ char *tokens[6];
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+ char *mailTo = NULL;
+#endif
+ char *check0, *check1, *check2;
+ CronLine *line;
+
+ if (!fileName)
+ return;
+
+ for (line= LineBase; line; line= line->cl_Next)
+ line->needs_delete= 1;
+
+ parser = config_open(fileName);
+ if (!parser)
+ {
+ /* We have to get rid of the old entries if the file is not
+ * there. Assume a non-existant file is the only reason for
+ * failure.
+ */
+ DeleteFile();
+ return;
+ }
+
+ maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES;
+
+ if (fstat(fileno(parser->fp), &sbuf) == 0 /* && sbuf.st_uid == DaemonUid */) {
+ int n;
+
+ while (1) {
+ if (!--maxLines)
+ break;
+ n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY);
+ if (!n)
+ break;
+
+ if (DebugOpt)
+ crondlog(LVL5 "user:%s entry:%s", fileName, parser->data);
+
+ /* check if line is setting MAILTO= */
+ if (0 == strncmp(tokens[0], "MAILTO=", 7)) {
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+ free(mailTo);
+ mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL;
+#endif /* otherwise just ignore such lines */
+ continue;
+ }
+ /* check if a minimum of tokens is specified */
+ if (n < 6)
+ continue;
+ line = xzalloc(sizeof(*line));
+ line->interval= strtoul(tokens[0], &check0, 10);
+ line->start_time= strtoul(tokens[1], &check1, 10);
+ line->end_time= strtoul(tokens[2], &check2, 10);
+
+ if (line->interval <= 0 || check0[0] != '\0' ||
+ check1[0] != '\0' ||
+ check2[0] != '\0')
+ {
+ crondlog(LVL9 "bad crontab line");
+ free(line);
+ continue;
+ }
+
+ if (strcmp(tokens[3], "NONE") == 0)
+ {
+ line->distribution= DISTR_NONE;
+ }
+ else if (strcmp(tokens[3], "UNIFORM") == 0)
+ {
+ line->distribution= DISTR_UNIFORM;
+ line->distr_param=
+ strtoul(tokens[4], &check0, 10);
+ if (check0[0] != '\0')
+ {
+ crondlog(LVL9 "bad crontab line");
+ free(line);
+ continue;
+ }
+ if (line->distr_param == 0 ||
+ LONG_MAX/line->distr_param == 0)
+ {
+ line->distribution= DISTR_NONE;
+ }
+ }
+
+ line->lasttime= 0;
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+ /* copy mailto (can be NULL) */
+ line->cl_MailTo = xstrdup(mailTo);
+#endif
+ /* copy command */
+ line->cl_Shell = xstrdup(tokens[5]);
+ if (DebugOpt) {
+ crondlog(LVL5 " command:%s", tokens[5]);
+ }
+//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]);
+
+ evtimer_assign(&line->event, EventBase, RunJob, line);
+
+ r= Insert(line);
+ if (!r)
+ {
+ /* Existing line. Delete new one */
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+ free(line->cl_MailTo);
+#endif
+ free(line->cl_Shell);
+ free(line);
+ continue;
+ }
+
+ /* New line, should schedule start event */
+ Start(line);
+
+ kick_watchdog();
+ }
+
+ if (maxLines == 0) {
+ crondlog(WARN9 "user %s: too many lines", fileName);
+ }
+ }
+ config_close(parser);
+
+ DeleteFile();
+}
+
+static void CheckUpdates(evutil_socket_t __attribute__ ((unused)) fd,
+ short __attribute__ ((unused)) what,
+ void __attribute__ ((unused)) *arg)
+{
+ FILE *fi;
+ char buf[256];
+
+ fi = fopen_for_read(CRONUPDATE);
+ if (fi != NULL) {
+ unlink(CRONUPDATE);
+ while (fgets(buf, sizeof(buf), fi) != NULL) {
+ /* use first word only */
+ SynchronizeFile(strtok(buf, " \t\r\n"));
+ }
+ fclose(fi);
+ }
+}
+
+static void CheckUpdatesHour(evutil_socket_t __attribute__ ((unused)) fd,
+ short __attribute__ ((unused)) what,
+ void __attribute__ ((unused)) *arg)
+{
+ SynchronizeDir();
+}
+
+static void SynchronizeDir(void)
+{
+ /*
+ * Remove cron update file
+ *
+ * Re-chdir, in case directory was renamed & deleted, or otherwise
+ * screwed up.
+ *
+ * Only load th crontab for 'root'
+ */
+ unlink(CRONUPDATE);
+ if (chdir(CDir) < 0) {
+ crondlog(DIE9 "can't chdir(%s)", CDir);
+ }
+
+ SynchronizeFile("root");
+ DeleteFile();
+}
+
+/*
+ * Insert - insert if not already there
+ */
+static int Insert(CronLine *line)
+{
+ CronLine *last;
+ struct timeval tv;
+ time_t now;
+
+ if (oldLine)
+ {
+ /* Try to match line expected to be next */
+ if (oldLine->interval == line->interval &&
+ oldLine->start_time == line->start_time &&
+ strcmp(oldLine->cl_Shell, line->cl_Shell) == 0)
+ {
+ crondlog(LVL9 "next line matches");
+ ; /* okay */
+ }
+ else
+ oldLine= NULL;
+ }
+
+ if (!oldLine)
+ {
+ /* Try to find one */
+ for (last= NULL, oldLine= LineBase; oldLine;
+ last= oldLine, oldLine= oldLine->cl_Next)
+ {
+ if (oldLine->interval == line->interval &&
+ oldLine->start_time == line->start_time &&
+ strcmp(oldLine->cl_Shell, line->cl_Shell) == 0)
+ {
+ break;
+ }
+ }
+ }
+
+ if (oldLine)
+ {
+ crondlog(LVL7 "Insert: found match for line '%s'",
+ line->cl_Shell);
+ crondlog(LVL7 "Insert: setting end time to %d", line->end_time);
+ oldLine->end_time= line->end_time;
+ oldLine->needs_delete= 0;
+
+ /* Reschedule event */
+ now= time(NULL);
+ tv.tv_sec= oldLine->nextcycle*oldLine->interval +
+ oldLine->start_time +
+ oldLine->distr_offset - now;
+ if (tv.tv_sec < 0)
+ tv.tv_sec= 0;
+ tv.tv_usec= 0;
+ crondlog(LVL7 "Insert: nextcycle %d, interval %d, start_time %d, distr_offset %d, now %d, tv_sec %d",
+ oldLine->nextcycle, oldLine->interval,
+ oldLine->start_time, oldLine->distr_offset, now,
+ tv.tv_sec);
+ event_add(&oldLine->event, &tv);
+
+ return 0;
+ }
+
+ crondlog(LVL7 "found no match for line '%s'", line->cl_Shell);
+ line->cl_Next= NULL;
+ if (last)
+ last->cl_Next= line;
+ else
+ LineBase= line;
+ return 1;
+}
+
+static void Start(CronLine *line)
+{
+ time_t now;
+ struct timeval tv;
+
+ line->testops= NULL;
+
+ /* Parse command line and init test */
+ atlas_init(line);
+ if (!line->testops)
+ return; /* Test failed to initialize */
+
+ now= time(NULL);
+ if (now > line->end_time)
+ return; /* This job has expired */
+
+ if (now < line->start_time)
+ line->nextcycle= 0;
+ else
+ line->nextcycle= (now-line->start_time)/line->interval + 1;
+ do_distr(line);
+
+ tv.tv_sec= line->nextcycle*line->interval + line->start_time +
+ line->distr_offset - now;
+ if (tv.tv_sec < 0)
+ tv.tv_sec= 0;
+ tv.tv_usec= 0;
+ crondlog(LVL7 "Start: nextcycle %d, interval %d, start_time %d, distr_offset %d, now %d, tv_sec %d",
+ line->nextcycle, line->interval,
+ line->start_time, line->distr_offset, now,
+ tv.tv_sec);
+ event_add(&line->event, &tv);
+}
+
+/*
+ * DeleteFile() - delete user database
+ *
+ * Note: multiple entries for same user may exist if we were unable to
+ * completely delete a database due to running processes.
+ */
+static void DeleteFile(void)
+{
+ int r;
+ CronLine **pline = &LineBase;
+ CronLine *line;
+
+ oldLine= NULL;
+
+ while ((line = *pline) != NULL) {
+ if (!line->needs_delete)
+ {
+ pline= &line->cl_Next;
+ continue;
+ }
+ kick_watchdog();
+ if (!line->teststate)
+ {
+ crondlog(LVL8 "DeleteFile: no state to delete for '%s'",
+ line->cl_Shell);
+ }
+ if (line->testops && line->teststate)
+ {
+ r= line->testops->delete(line->teststate);
+ if (r != 1)
+ {
+ crondlog(LVL9 "DeleteFile: line is busy");
+ pline= &line->cl_Next;
+ continue;
+ }
+ line->testops= NULL;
+ line->teststate= NULL;
+ }
+ event_del(&line->event);
+ free(line->cl_Shell);
+ line->cl_Shell= NULL;
+
+ *pline= line->cl_Next;
+ free(line);
+ }
+}
+
+static void skip_space(char *cp, char **ncpp)
+{
+ while (cp[0] != '\0' && isspace(*(unsigned char *)cp))
+ cp++;
+ *ncpp= cp;
+}
+
+static void skip_nonspace(char *cp, char **ncpp)
+{
+ while (cp[0] != '\0' && !isspace(*(unsigned char *)cp))
+ cp++;
+ *ncpp= cp;
+}
+
+static void find_eos(char *cp, char **ncpp)
+{
+ while (cp[0] != '\0' && cp[0] != '"')
+ cp++;
+ *ncpp= cp;
+}
+
+static struct builtin
+{
+ const char *cmd;
+ struct testops *testops;
+} builtin_cmds[]=
+{
+ { "evhttpget", &httpget_ops },
+ { "evping", &ping_ops },
+ { "evtdig", &tdig_ops },
+ { "evtraceroute", &traceroute_ops },
+ { "condmv", &condmv_ops },
+ { NULL, NULL }
+};
+
+
+#define ATLAS_NARGS 64 /* Max arguments to a built-in command */
+#define ATLAS_ARGSIZE 512 /* Max size of the command line */
+
+static void atlas_init(CronLine *line)
+{
+ char c;
+ int i, argc;
+ size_t len;
+ char *cp, *ncp;
+ struct builtin *bp;
+ char *cmdline, *p;
+ const char *reason;
+ void *state;
+ FILE *fn;
+ char *argv[ATLAS_NARGS];
+ char args[ATLAS_ARGSIZE];
+
+ cmdline= line->cl_Shell;
+ crondlog(LVL7 "atlas_run: looking for %p '%s'", cmdline, cmdline);
+
+ state= NULL;
+ reason= NULL;
+ for (bp= builtin_cmds; bp->cmd != NULL; bp++)
+ {
+ len= strlen(bp->cmd);
+ if (strncmp(cmdline, bp->cmd, len) != 0)
+ continue;
+ if (cmdline[len] != ' ')
+ continue;
+ break;
+ }
+ if (bp->cmd == NULL)
+ {
+ reason="command not found";
+ goto error;
+ }
+
+ crondlog(LVL7 "found cmd '%s' for '%s'", bp->cmd, cmdline);
+
+ len= strlen(cmdline);
+ if (len+1 > ATLAS_ARGSIZE)
+ {
+ crondlog(LVL8 "atlas_run: command line too big: '%s'", cmdline);
+ reason="command line too big";
+ goto error;
+ }
+ strcpy(args, cmdline);
+
+ /* Split the command line */
+ cp= args;
+ argc= 0;
+ argv[argc]= cp;
+ skip_nonspace(cp, &ncp);
+ cp= ncp;
+
+ for(;;)
+ {
+ /* End of list */
+ if (cp[0] == '\0')
+ {
+ argc++;
+ break;
+ }
+
+ /* Find start of next argument */
+ skip_space(cp, &ncp);
+
+ /* Terminate current one */
+ cp[0]= '\0';
+ argc++;
+
+ if (argc >= ATLAS_NARGS-1)
+ {
+ crondlog(
+ LVL8 "atlas_run: command line '%s', too many arguments",
+ cmdline);
+ reason="too many arguments";
+ goto error;
+ }
+
+ cp= ncp;
+ argv[argc]= cp;
+ if (cp[0] == '"')
+ {
+ /* Special code for strings */
+ find_eos(cp+1, &ncp);
+ if (ncp[0] != '"')
+ {
+ crondlog(
+ LVL8 "atlas_run: command line '%s', end of string not found",
+ cmdline);
+ reason="end of string not found";
+ goto error;
+ }
+ argv[argc]= cp+1;
+ cp= ncp;
+ cp[0]= '\0';
+ cp++;
+ }
+ else
+ {
+ skip_nonspace(cp, &ncp);
+ cp= ncp;
+ }
+ }
+
+ if (argc >= ATLAS_NARGS)
+ {
+ crondlog(
+ LVL8 "atlas_run: command line '%s', too many arguments",
+ cmdline);
+ reason="too many arguments";
+ goto error;
+ }
+ argv[argc]= NULL;
+
+ for (i= 0; i<argc; i++)
+ crondlog(LVL7 "atlas_run: argv[%d] = '%s'", i, argv[i]);
+
+ state= bp->testops->init(argc, argv, 0);
+ crondlog(LVL7 "init returned %p for '%s'", state, line->cl_Shell);
+ line->teststate= state;
+ line->testops= bp->testops;
+
+error:
+ if (state == NULL && out_filename)
+ {
+ fn= fopen(out_filename, "a");
+ if (!fn)
+ crondlog(DIE9 "unable to append to '%s'", out_filename);
+ fprintf(fn, "RESULT { ");
+ if (atlas_id)
+ fprintf(fn, DBQ(id) ":" DBQ(%s) ", ", atlas_id);
+ fprintf(fn, DBQ(fw) ":" DBQ(%d) ", " DBQ(time) ":%ld, ",
+ get_atlas_fw_version(), (long)time(NULL));
+ if (reason)
+ fprintf(fn, DBQ(reason) ":" DBQ(%s) ", ", reason);
+ fprintf(fn, DBQ(cmd) ": \"");
+ for (p= line->cl_Shell; *p; p++)
+ {
+ c= *p;
+ if (c == '"' || c == '\\')
+ fprintf(fn, "\\%c", c);
+ else if (isprint((unsigned char)c))
+ fputc(c, fn);
+ else
+ fprintf(fn, "\\u%04x", (unsigned char)c);
+ }
+ fprintf(fn, "\"");
+ fprintf(fn, " }\n");
+ fclose(fn);
+ }
+}
+
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+
+// TODO: sendmail should be _run-time_ option, not compile-time!
+
+static void
+ForkJob(const char *user, CronLine *line, int mailFd,
+ const char *prog, const char *cmd, const char *arg,
+ const char *mail_filename)
+{
+ struct passwd *pas;
+ pid_t pid;
+
+ /* prepare things before vfork */
+ pas = getpwnam(user);
+ if (!pas) {
+ crondlog(LVL9 "can't get uid for %s", user);
+ goto err;
+ }
+ SetEnv(pas);
+
+ pid = vfork();
+ if (pid == 0) {
+ /* CHILD */
+ /* change running state to the user in question */
+ ChangeUser(pas);
+ if (DebugOpt) {
+ crondlog(LVL5 "child running %s", prog);
+ }
+ if (mailFd >= 0) {
+ xmove_fd(mailFd, mail_filename ? 1 : 0);
+ dup2(1, 2);
+ }
+ /* crond 3.0pl1-100 puts tasks in separate process groups */
+ bb_setpgrp();
+ execlp(prog, prog, cmd, arg, NULL);
+ crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user, prog, cmd, arg);
+ if (mail_filename) {
+ fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);
+ }
+ _exit(EXIT_SUCCESS);
+ }
+
+ line->cl_Pid = pid;
+ if (pid < 0) {
+ /* FORK FAILED */
+ crondlog(ERR20 "can't vfork");
+ err:
+ line->cl_Pid = 0;
+ if (mail_filename) {
+ unlink(mail_filename);
+ }
+ } else if (mail_filename) {
+ /* PARENT, FORK SUCCESS
+ * rename mail-file based on pid of process
+ */
+ char mailFile2[128];
+
+ snprintf(mailFile2, sizeof(mailFile2), "%s/cron.%s.%d", TMPDIR, user, pid);
+ rename(mail_filename, mailFile2); // TODO: xrename?
+ }
+
+ /*
+ * 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);
+ }
+}
+
+static void RunJob(const char *user, CronLine *line)
+{
+ char mailFile[128];
+ int mailFd = -1;
+
+ line->cl_Pid = 0;
+ line->cl_MailFlag = 0;
+
+ if (line->cl_MailTo) {
+ /* open mail file - owner root so nobody can screw with it. */
+ snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, getpid());
+ mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600);
+
+ if (mailFd >= 0) {
+ line->cl_MailFlag = 1;
+ fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", line->cl_MailTo,
+ line->cl_Shell);
+ line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR);
+ } else {
+ crondlog(ERR20 "cannot create mail file %s for user %s, "
+ "discarding output", mailFile, user);
+ }
+ }
+
+
+ if (atlas_outfile && atlas_run(line->cl_Shell))
+ {
+ /* Internal command */
+ return;
+ }
+
+ ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile);
+}
+
+/*
+ * EndJob - called when job terminates and when mail terminates
+ */
+static void EndJob(const char *user, CronLine *line)
+{
+ int mailFd;
+ char mailFile[128];
+ struct stat sbuf;
+
+ /* No job */
+ if (line->cl_Pid <= 0) {
+ line->cl_Pid = 0;
+ return;
+ }
+
+ /*
+ * End of job and no mail file
+ * End of sendmail job
+ */
+ snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, line->cl_Pid);
+ line->cl_Pid = 0;
+
+ if (line->cl_MailFlag == 0) {
+ return;
+ }
+ line->cl_MailFlag = 0;
+
+ /*
+ * End of primary job - check for mail file. If size has increased and
+ * the file is still valid, we sendmail it.
+ */
+ mailFd = open(mailFile, O_RDONLY);
+ unlink(mailFile);
+ if (mailFd < 0) {
+ return;
+ }
+
+ if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid
+ || sbuf.st_nlink != 0 || sbuf.st_size == line->cl_MailPos
+ || !S_ISREG(sbuf.st_mode)
+ ) {
+ close(mailFd);
+ return;
+ }
+ if (line->cl_MailTo)
+ ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
+}
+
+#else /* crond without sendmail */
+
+static void RunJob(evutil_socket_t __attribute__ ((unused)) fd,
+ short __attribute__ ((unused)) what, void *arg)
+{
+ time_t now;
+ CronLine *line;
+ struct timeval tv;
+
+ line= arg;
+
+ now= time(NULL);
+
+ crondlog(LVL7 "RunJob for %p, '%s'\n", arg, line->cl_Shell);
+ crondlog(LVL7 "RubJob, now %d, end_time %d\n", now, line->end_time);
+
+ if (now > line->end_time)
+ {
+ crondlog(LVL7 "RunJob: expired\n");
+ return; /* This job has expired */
+ }
+
+ if (line->needs_delete)
+ {
+ crondlog(LVL7 "RunJob: needs delete\n");
+ return; /* Line is to be deleted */
+ }
+
+ if (!line->teststate)
+ {
+ crondlog(LVL8 "not starting cmd '%s' (not init)\n",
+ line->cl_Shell);
+ return;
+ }
+
+ // crondlog(LVL8 "starting cmd '%s'\n", line->cl_Shell);
+
+ line->testops->start(line->teststate);
+
+ // crondlog(LVL8 "after cmd '%s'\n", line->cl_Shell);
+
+ line->nextcycle++;
+ if (line->start_time + line->nextcycle*line->interval < now)
+ {
+ crondlog(LVL7 "recomputing nextcycle");
+ line->nextcycle= (now-line->start_time)/line->interval + 1;
+ }
+
+ do_distr(line);
+ tv.tv_sec= line->nextcycle*line->interval + line->start_time +
+ line->distr_offset - now;
+ if (tv.tv_sec < 0)
+ tv.tv_sec= 0;
+ tv.tv_usec= 0;
+ crondlog(LVL7 "RunJob: nextcycle %d, interval %d, start_time %d, distr_offset %d, now %d, tv_sec %d",
+ line->nextcycle, line->interval,
+ line->start_time, line->distr_offset, now,
+ tv.tv_sec);
+ event_add(&line->event, &tv);
+}
+
+#endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */
diff --git a/eperd/eperd.h b/eperd/eperd.h
new file mode 100644
index 0000000..ecdab64
--- /dev/null
+++ b/eperd/eperd.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * eperd.h
+ */
+
+typedef struct CronLine CronLine;
+
+struct globals {
+ unsigned LogLevel; /* = 8; */
+ const char *LogFile;
+ const char *CDir; /* = CRONTABS; */
+ CronLine *LineBase;
+ CronLine *oldLine;
+ struct event_base *EventBase;
+ struct evdns_base *DnsBase;
+};
+extern struct globals G;
+#define LogLevel (G.LogLevel )
+#define LogFile (G.LogFile )
+#define CDir (G.CDir )
+#define LineBase (G.LineBase )
+#define FileBase (G.FileBase )
+#define oldLine (G.oldLine )
+#define EventBase (G.EventBase )
+#define DnsBase (G.DnsBase )
+
+#define LVL5 "\x05"
+#define LVL7 "\x07"
+#define LVL8 "\x08"
+#define LVL9 "\x09"
+#define WARN9 "\x49"
+#define DIE9 "\xc9"
+/* level >= 20 is "error" */
+#define ERR20 "\x14"
+
+struct testops
+{
+ void *(*init)(int argc, char *argv[], void (*done)(void *teststate));
+ void (*start)(void *teststate);
+ int (*delete)(void *teststate);
+};
+
+extern struct testops condmv_ops;
+extern struct testops httpget_ops;
+extern struct testops ping_ops;
+extern struct testops tdig_ops;
+extern struct testops traceroute_ops;
+
+void crondlog(const char *ctl, ...);
+int get_atlas_fw_version(void);
diff --git a/eperd/evhttpget.c b/eperd/evhttpget.c
new file mode 100644
index 0000000..91e26ab
--- /dev/null
+++ b/eperd/evhttpget.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Standalone version of the event-based httpget.
+ */
+
+#include "libbb.h"
+#include <syslog.h>
+#include <event2/event.h>
+#include <event2/event_struct.h>
+#include <event2/dns.h>
+
+#include "eperd.h"
+
+static void done(void *state UNUSED_PARAM)
+{
+ fprintf(stderr, "And we are done\n");
+ exit(0);
+}
+
+int evhttpget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int evhttpget_main(int argc UNUSED_PARAM, char **argv)
+{
+ int r;
+ void *state;
+
+ /* Create libevent event base */
+ EventBase= event_base_new();
+ if (!EventBase)
+ {
+ fprintf(stderr, "evhttpget_base_new failed\n");
+ exit(1);
+ }
+ DnsBase= evdns_base_new(EventBase, 1 /*initialize*/);
+ if (!DnsBase)
+ {
+ fprintf(stderr, "evdns_base_new failed\n");
+ exit(1);
+ }
+
+ state= httpget_ops.init(argc, argv, done);
+ if (!state)
+ {
+ fprintf(stderr, "evhttpget: traceroute_ops.init failed\n");
+ exit(1);
+ }
+ httpget_ops.start(state);
+
+ r= event_base_loop(EventBase, 0);
+ if (r != 0)
+ {
+ fprintf(stderr, "evhttpget: event_base_loop failed\n");
+ exit(1);
+ }
+ return 0; /* not reached */
+}
+
diff --git a/eperd/evping.c b/eperd/evping.c
new file mode 100644
index 0000000..a11d51d
--- /dev/null
+++ b/eperd/evping.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Standalone version of the event-based ping.
+ */
+
+#include "libbb.h"
+#include <syslog.h>
+#include <event2/dns.h>
+#include <event2/event.h>
+#include <event2/event_struct.h>
+
+#include "eperd.h"
+
+static void done(void *state UNUSED_PARAM)
+{
+ fprintf(stderr, "And we are done\n");
+ exit(0);
+}
+
+int evping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int evping_main(int argc UNUSED_PARAM, char **argv)
+{
+ int r;
+ void *state;
+
+ /* Create libevent event base */
+ EventBase= event_base_new();
+ if (!EventBase)
+ {
+ fprintf(stderr, "evping_base_new failed\n");
+ exit(1);
+ }
+ DnsBase= evdns_base_new(EventBase, 1 /*initialize*/);
+ if (!DnsBase)
+ {
+ fprintf(stderr, "evdns_base_new failed\n");
+ exit(1);
+ }
+
+
+ state= ping_ops.init(argc, argv, done);
+ if (!state)
+ {
+ fprintf(stderr, "evping_ops.init failed\n");
+ exit(1);
+ }
+ ping_ops.start(state);
+
+ r= event_base_loop(EventBase, 0);
+ if (r != 0)
+ {
+ fprintf(stderr, "evping_base_loop failed\n");
+ exit(1);
+ }
+ return 0; /* not reached */
+}
+
diff --git a/eperd/evtdig.c b/eperd/evtdig.c
new file mode 100644
index 0000000..ee6139b
--- /dev/null
+++ b/eperd/evtdig.c
@@ -0,0 +1,2131 @@
+/*
+ * Copyright (c) 2011-2013 RIPE NCC <atlas@ripe.net>
+ * Copyright (c) 2009 Rocco Carbone <ro...@tecsiel.it>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "atlas_bb64.h"
+#include "atlas_probe.h"
+#include <netdb.h>
+#include <getopt.h>
+#include <netinet/in.h>
+#include <netinet/ip_icmp.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <math.h>
+#include <assert.h>
+
+#include "eperd.h"
+#include "resolv.h"
+#include "readresolv.h"
+#include "tcputil.h"
+
+#include <event2/event.h>
+#include <event2/event_struct.h>
+#include <event2/dns.h>
+#include <event2/bufferevent.h>
+#include <event2/buffer.h>
+#include <event2/util.h>
+#define DQ(str) "\"" #str "\""
+#define DQC(str) "\"" #str "\" : "
+#define JS(key, val) fprintf(fh, "\"" #key"\" : \"%s\" , ", val);
+#define JS_NC(key, val) fprintf(fh, "\"" #key"\" : \"%s\" ", val);
+#define JSDOT(key, val) fprintf(fh, "\"" #key"\" : \"%s.\" , ", val);
+#define JS1(key, fmt, val) fprintf(fh, "\"" #key"\" : "#fmt" , ", val);
+#define JD(key, val) fprintf(fh, "\"" #key"\" : %d , ", val);
+#define JD_NC(key, val) fprintf(fh, "\"" #key"\" : %d ", val);
+#define JU(key, val) fprintf(fh, "\"" #key"\" : %u , ", val);
+#define JU_NC(key, val) fprintf(fh, "\"" #key"\" : %u", val);
+#define JC fprintf(fh, ",");
+
+#define SAFE_PREFIX ATLAS_DATA_NEW
+
+#define BLURT crondlog (LVL5 "%s:%d %s()", __FILE__, __LINE__, __func__);crondlog
+#define IAMHERE crondlog (LVL5 "%s:%d %s()", __FILE__, __LINE__, __func__);
+
+#undef MIN /* just in case */
+#undef MAX /* also, just in case */
+#define Q_RESOLV_CONF -1
+#define O_RESOLV_CONF 1003
+#define O_PREPEND_PROBE_ID 1004
+#define O_EVDNS 1005
+
+#define DNS_FLAG_RD 0x0100
+
+#define MIN(a, b) (a < b ? a : b)
+#define MAX(a, b) (a > b ? a : b)
+
+#define ENV2QRY(env) \
+ ((struct query_state *)((char *)env - offsetof(struct query_state, tu_env)))
+
+#define MAX_DNS_BUF_SIZE 5120
+#define MAX_DNS_OUT_BUF_SIZE 512
+
+/* Intervals and timeouts (all are in milliseconds unless otherwise specified) */
+#define DEFAULT_NOREPLY_TIMEOUT 5000 /* 1000 msec - 0 is illegal */
+#define DEFAULT_LINE_LENGTH 80
+#define DEFAULT_STATS_REPORT_INTERVEL 180 /* in seconds */
+#define CONN_TO 5 /* TCP connection time out in seconds */
+
+/* state of the dns query */
+#define STATUS_DNS_RESOLV 1001
+#define STATUS_TCP_CONNECTING 1002
+#define STATUS_TCP_CONNECTED 1003
+#define STATUS_TCP_WRITE 1004
+#define STATUS_NEXT_QUERY 1005
+#define STATUS_FREE 0
+
+// seems T_DNSKEY is not defined header files of lenny and sdk
+#ifndef ns_t_dnskey
+#define ns_t_dnskey 48
+#endif
+
+#ifndef T_DNSKEY
+#define T_DNSKEY ns_t_dnskey
+#endif
+
+#ifndef ns_t_rrsig
+#define ns_t_rrsig 46
+#endif
+
+#ifndef T_RRSIG
+#define T_RRSIG ns_t_rrsig
+#endif
+
+#ifndef ns_t_nsec
+#define ns_t_nsec 47
+#endif
+
+#ifndef T_NSEC
+#define T_NSEC ns_t_nsec
+#endif
+
+#ifndef T_NSEC3
+#define T_NSEC3 ns_t_nsec3
+#endif
+
+#ifndef ns_t_nsec3
+#define ns_t_nsec3 50
+#endif
+
+
+#ifndef ns_t_ds
+#define ns_t_ds 43
+#endif
+
+#ifndef T_DS
+#define T_DS ns_t_ds
+#endif
+
+
+/* Definition for various types of counters */
+typedef uint32_t counter_t;
+
+/* How to keep track of a DNS query session */
+struct tdig_base {
+ struct event_base *event_base;
+
+ evutil_socket_t rawfd_v4; /* Raw socket used to nsm hosts */
+ evutil_socket_t rawfd_v6; /* Raw socket used to nsm hosts */
+
+ struct timeval tv_noreply; /* DNS query Reply timeout */
+
+ /* A circular list of user queries */
+ struct query_state *qry_head;
+
+ struct event event4; /* Used to detect read events on raw socket */
+ struct event event6; /* Used to detect read events on raw socket */
+ struct event statsReportEvent;
+ int resolv_max;
+ char nslist[MAXNS][INET6_ADDRSTRLEN * 2];
+
+ counter_t sendfail; /* # of failed sendto() */
+ counter_t sentok; /* # of successful sendto() */
+ counter_t recvfail; /* # of failed recvfrom() */
+ counter_t recvok; /* # of successful recvfrom() */
+ counter_t martian; /* # of DNS replies we are not looking for */
+ counter_t shortpkt; /* # of DNS payload with size < sizeof(struct DNS_HEADER) == 12 bytes */
+ counter_t sentbytes;
+ counter_t recvbytes;
+ counter_t timeout;
+ counter_t queries;
+ counter_t activeqry;
+
+ u_char packet [MAX_DNS_BUF_SIZE] ;
+ /* used only for the stand alone version */
+ void (*done)(void *state);
+};
+
+static struct tdig_base *tdig_base;
+
+/* How to keep track of each user query to send dns query */
+struct query_state {
+
+ struct tdig_base *base;
+ char * name; /* Host identifier as given by the user */
+ char * fqname; /* Full qualified hostname */
+ char * ipname; /* Remote address in dot notation */
+ u_int16_t qryid; /* query id 16 bit */
+ int tcp_fd;
+ FILE *tcp_file;
+ int wire_size;
+
+ struct bufferevent *bev_tcp;
+ struct tu_env tu_env;
+
+ int opt_v4_only ;
+ int opt_v6_only ;
+ int opt_AF;
+ int opt_proto;
+ int opt_edns0;
+ int opt_dnssec;
+ int opt_nsid;
+ int opt_qbuf;
+ int opt_abuf;
+ int opt_resolv_conf;
+ int opt_rd;
+ int opt_prepend_probe_id;
+ int opt_evdns;
+
+ char * str_Atlas;
+ u_int16_t qtype;
+ u_int16_t qclass;
+
+ char *lookupname;
+ char * server_name;
+ char *out_filename ;
+
+ uint32_t pktsize; /* Packet size in bytes */
+ struct addrinfo *res, *ressave, *ressent;
+
+ struct sockaddr_in remote; /* store the reply packet src address */
+
+
+ struct event noreply_timer; /* Timer to handle timeout */
+ struct event nsm_timer; /* Timer to send UDP */
+ struct event next_qry_timer; /* Timer event to start next query */
+
+ struct timeval xmit_time;
+ double triptime;
+
+ //tdig_callback_type user_callback;
+ void *user_callback;
+ void *user_pointer; /* the pointer given to us for this qry */
+
+ /* these objects are kept in a circular list */
+ struct query_state *next, *prev;
+
+ struct buf err;
+ struct buf qbuf;
+ struct buf packet;
+ int qst ;
+ char dst_addr_str[(INET6_ADDRSTRLEN+1)];
+ char loc_addr_str[(INET6_ADDRSTRLEN+1)];
+ unsigned short dst_ai_family ;
+ unsigned short loc_ai_family ;
+ struct sockaddr_in6 loc_sin6;
+ socklen_t loc_socklen;
+
+
+ u_char *outbuff;
+};
+//DNS header structure
+struct DNS_HEADER
+{
+ u_int16_t id; // identification number
+
+ u_int16_t flags;
+/*
+ u_int16_t rd :1, // recursion desired
+ tc :1, // truncated message
+ aa :1, // authoritive answer
+ opcode :4, // purpose of message
+ qr :1, // query/response flag
+ rcode :4, // response code
+ cd :1, // checking disabled
+ ad :1, // authenticated data
+ z :1, // its z! reserved
+ ra :1; // recursion available
+
+*/
+ u_int16_t q_count; // number of question entries
+ u_int16_t ans_count; // number of answer entries
+ u_int16_t ns_count; // number of authority entries
+ u_int16_t add_count; // number of resource entries
+};
+
+// EDNS OPT pseudo-RR : EDNS0
+struct EDNS0_HEADER
+{
+ /** EDNS0 available buffer size, see RFC2671 */
+ u_int16_t otype;
+ uint16_t _edns_udp_size;
+ u_int8_t _edns_x; // combined rcode and edns version both zeros.
+ u_int8_t _edns_y; // combined rcode and edns version both zeros.
+ u_int16_t Z ; // first bit is the D0 bit.
+};
+
+// EDNS OPT pseudo-RR : eg NSID RFC 5001
+struct EDNS_NSID
+{
+ uint16_t len;
+ u_int16_t otype;
+ u_int16_t odata;
+};
+
+
+//Constant sized fields of query structure
+struct QUESTION
+{
+ u_int16_t qtype;
+ u_int16_t qclass;
+};
+
+//Constant sized fields of the resource record structure
+#pragma pack(push, 1)
+struct R_DATA
+{
+ u_int16_t type;
+ u_int16_t _class;
+ u_int32_t ttl;
+ u_int16_t data_len;
+};
+#pragma pack(pop)
+
+//Pointers to resource record contents
+struct RES_RECORD
+{
+ unsigned char *name;
+ struct R_DATA *resource;
+ unsigned char *rdata;
+};
+
+static struct option longopts[]=
+{
+ // class IN
+ { "a", required_argument, NULL, (100000 + T_A) },
+ { "ns", required_argument, NULL, (100000 + T_NS) },
+ { "cname", required_argument, NULL, (100000 + T_CNAME) },
+ { "ptr", required_argument, NULL, (100000 + T_PTR ) },
+ { "mx", required_argument, NULL, (100000 + T_MX ) },
+ { "txt", required_argument, NULL, (100000 + T_TXT ) },
+ { "aaaa", required_argument, NULL, (100000 + T_AAAA) },
+ { "axfr", required_argument, NULL, (100000 + T_AXFR ) }, //yet to be tested.
+ { "any", required_argument, NULL, (100000 + T_ANY) },
+ { "dnskey", required_argument, NULL, (100000 + T_DNSKEY) },
+ { "nsec", required_argument, NULL, (100000 + T_NSEC) },
+ { "nsec3", required_argument, NULL, (100000 + T_NSEC3) },
+ { "ds", required_argument, NULL, (100000 + T_DS) },
+ { "rrsig", required_argument, NULL, (100000 + T_RRSIG) },
+ { "soa", required_argument, NULL, 's' },
+
+ // clas CHAOS
+ { "hostname.bind", no_argument, NULL, 'h' },
+ { "id.server", no_argument, NULL, 'i' },
+ { "version.bind", no_argument, NULL, 'b' },
+ { "version.server", no_argument, NULL, 'r' },
+
+ // flags
+ { "edns0", required_argument, NULL, 'e' },
+ { "nsid", no_argument, NULL, 'n' },
+ { "d0", no_argument, NULL, 'd' },
+
+ { "resolv", no_argument, NULL, O_RESOLV_CONF },
+ { "qbuf", no_argument, NULL, 1001 },
+ { "noabuf", no_argument, NULL, 1002 },
+
+ { "evdns", no_argument, NULL, O_EVDNS },
+ { "out-file", required_argument, NULL, 'O' },
+ { "p_probe_id", no_argument, NULL, O_PREPEND_PROBE_ID },
+ { NULL, }
+};
+static char line[DEFAULT_LINE_LENGTH];
+
+static void tdig_stats(int unused UNUSED_PARAM, const short event UNUSED_PARAM, void *h);
+static int tdig_delete(void *state);
+static void ChangetoDnsNameFormat(u_char *dns, char * qry) ;
+struct tdig_base *tdig_base_new(struct event_base *event_base);
+void tdig_start (struct query_state *qry);
+void printReply(struct query_state *qry, int wire_size, unsigned char *result);
+void printErrorQuick (struct query_state *qry);
+static void local_exit(void *state);
+static void *tdig_init(int argc, char *argv[], void (*done)(void *state));
+static void process_reply(void * arg, int nrecv, struct timeval now, int af, void *remote);
+static void mk_dns_buff(struct query_state *qry, u_char *packet);
+int ip_addr_cmp (u_int16_t af_a, void *a, u_int16_t af_b, void *b);
+static void udp_dns_cb(int err, struct evutil_addrinfo *ev_res, struct query_state *qry);
+
+/* move the next functions from tdig.c */
+u_int32_t get32b (char *p);
+void ldns_write_uint16(void *dst, uint16_t data);
+uint16_t ldns_read_uint16(const void *src);
+unsigned char* ReadName(unsigned char *base, size_t size, size_t offset,
+ int* count);
+/* from tdig.c */
+
+void print_txt_json(unsigned char *rdata, int txt_len, FILE *fh);
+
+int evtdig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int evtdig_main(int argc, char **argv)
+{
+ struct query_state *qry;
+
+ EventBase=event_base_new();
+ if (!EventBase)
+ {
+ crondlog(LVL9 "event_base_new failed"); /* exits */
+ }
+
+ qry = tdig_init(argc, argv, NULL);
+ if(!qry) {
+ crondlog(DIE9 "evdns_base_new failed"); /* exits */
+ event_base_free (EventBase);
+ return 1;
+ }
+
+ DnsBase = evdns_base_new(EventBase, 1);
+ if (!DnsBase) {
+ crondlog(DIE9 "evdns_base_new failed"); /* exits */
+ event_base_free (EventBase);
+ return 1;
+ }
+
+ tdig_start(qry);
+ printf ("starting query\n");
+
+ event_base_dispatch (EventBase);
+ event_base_loopbreak (EventBase);
+ if(EventBase)
+ event_base_free(EventBase);
+ return 0;
+}
+
+void print_txt_json(unsigned char *rdata, int txt_len, FILE *fh)
+{
+ int i;
+
+ fprintf(fh, ", \"RDATA\" : \"");
+ for(i = 0; i < txt_len; i++) {
+ if( (*rdata == 34 ) || (*rdata == 92 )) {
+ fprintf(fh, "\\%c", *(char *)rdata );
+ }
+ // Space - DEL
+ else if ((*rdata > 31 ) && (*rdata < 128)) {
+ fprintf(fh, "%c", *(char *)rdata );
+ }
+ else {
+ fprintf(fh, "\\u00%02X", *rdata );
+ }
+ rdata++;
+ }
+
+ fprintf(fh, "\"");
+}
+
+static void local_exit(void *state UNUSED_PARAM)
+{
+ //fprintf(stderr, "And we are done\n");
+ exit(0);
+}
+
+
+/* Initialize a struct timeval by converting milliseconds */
+static void msecstotv(time_t msecs, struct timeval *tv)
+{
+ tv->tv_sec = msecs / 1000;
+ tv->tv_usec = msecs % 1000 * 1000;
+}
+
+int ip_addr_cmp (u_int16_t af_a, void *a, u_int16_t af_b, void *b)
+{
+ struct sockaddr_in *a4;
+ struct sockaddr_in *b4;
+ struct sockaddr_in6 *a6;
+ struct sockaddr_in6 *b6;
+ char buf[INET6_ADDRSTRLEN];
+
+ if(af_a != af_b) {
+ crondlog(LVL5 "address family mismatch in %d ", __LINE__);
+ return -1;
+ }
+
+ if(af_a == AF_INET ) {
+ a4 = (struct sockaddr_in *) a;
+ b4 = (struct sockaddr_in *) b;
+ if( memcmp ( &(a4->sin_addr), &(b4->sin_addr), sizeof(struct in_addr)) == 0) {
+ return 0;
+ }
+ else
+ return 1;
+ }
+ else if(af_a == AF_INET6 ) {
+ a6 = (struct sockaddr_in6 *) a;
+ b6 = (struct sockaddr_in6 *) b;
+ if( memcmp ( &(a6->sin6_addr), &(b6->sin6_addr), sizeof(struct in6_addr)) == 0) {
+ inet_ntop(AF_INET6, &(a6->sin6_addr), buf, sizeof(buf));
+ crondlog(LVL5 "address6 match A %s", buf);
+ inet_ntop(AF_INET6, &(b6->sin6_addr), buf, sizeof(buf));
+ crondlog(LVL5 "address6 match B %s", buf);
+
+ return 0;
+ }
+ else {
+ inet_ntop(AF_INET6, &(a6->sin6_addr), buf, sizeof(buf));
+ crondlog(LVL5 "address6 mismatch A %s", buf);
+ inet_ntop(AF_INET6, &(b6->sin6_addr), buf, sizeof(buf));
+ crondlog(LVL5 "address mismatch B %s", buf);
+
+
+ return 1;
+ }
+ }
+ return 1;
+}
+
+/* Lookup for a query by its index */
+static struct query_state* tdig_lookup_query( struct tdig_base * base, int idx, int af, void * remote)
+{
+ int i = 0;
+ struct query_state *qry;
+
+ qry = base->qry_head;
+ if (!qry)
+ return NULL;
+ do {
+ i++;
+ if (qry->qryid == idx)
+ {
+ //AA chnage to LVL5
+ crondlog(LVL7 "found matching query id %d", idx);
+ if( qry->ressent && ip_addr_cmp (af, remote, qry->ressent->ai_family, qry->ressent->ai_addr) == 0) {
+ crondlog(LVL7 "matching id and address id %d", idx);
+ return qry;
+ }
+ else {
+ crondlog(LVL7 "matching id and address mismatch id %d", idx);
+ }
+ }
+ qry = qry->next;
+ if (i > (2*base->activeqry) ) {
+ crondlog(LVL7 "i am looping %d AA", idx);
+ return NULL;
+ }
+
+ } while (qry != base->qry_head);
+
+ return NULL;
+}
+
+static void mk_dns_buff(struct query_state *qry, u_char *packet)
+{
+ struct DNS_HEADER *dns = NULL;
+ u_char *qname;
+ struct QUESTION *qinfo = NULL;
+ struct EDNS0_HEADER *e;
+ struct EDNS_NSID *n;
+ int r;
+ struct buf pbuf;
+ char *lookup_prepend;
+ int probe_id;
+
+ dns = (struct DNS_HEADER *)packet;
+ r = random();
+ r %= 65535;
+ qry->qryid = (uint16_t) r; // host is storing int host byte order
+ crondlog(LVL5 "%s %s() : %d base address %p",__FILE__, __func__, __LINE__, qry->base);
+ BLURT(LVL5 "dns qyery id %d", qry->qryid);
+ dns->id = (uint16_t) htons(r);
+ /*
+ dns->qr = 0; //This is a query
+ dns->opcode = 0; //This is a standard query
+ dns->aa = 0; //Not Authoritative
+ dns->tc = 0; //This message is not truncated
+ dns->rd = 0; //Recursion not Desired
+ dns->ra = 1; //Recursion not available! hey we dont have it (lol)
+ dns->z = 0;
+ dns->ad = 0;
+ dns->cd = 0;
+ dns->rcode = 0;
+*/
+ dns->q_count = htons(1); //we have only 1 question
+ dns->ans_count = 0;
+ dns->ns_count = 0;
+ dns->add_count = htons(0);
+
+ if (( qry->opt_resolv_conf > Q_RESOLV_CONF ) || (qry->opt_rd )){
+ // if you need more falgs do a bitwise and here.
+ dns->flags = htons(DNS_FLAG_RD);
+ }
+
+ //point to the query portion
+ qname =(u_char *)&packet[sizeof(struct DNS_HEADER)];
+
+ // should it be limited to clas C_IN ?
+ if(qry->opt_prepend_probe_id ) {
+ probe_id = get_probe_id();
+ probe_id = MAX(probe_id, 0);
+
+
+ lookup_prepend = xzalloc(DEFAULT_LINE_LENGTH + sizeof(qry->lookupname));
+ snprintf(lookup_prepend, (sizeof(qry->lookupname) + DEFAULT_LINE_LENGTH - 1), "%d.%lu.%s", probe_id, qry->xmit_time.tv_sec, qry->lookupname);
+
+ ChangetoDnsNameFormat(qname, lookup_prepend); // fill the query portion.
+
+ free(lookup_prepend);
+ }
+ else {
+ ChangetoDnsNameFormat(qname, qry->lookupname); // fill the query portion.
+ }
+ qinfo =(struct QUESTION*)&packet[sizeof(struct DNS_HEADER) + (strlen((const char*)qname) + 1)];
+
+ qinfo->qtype = htons(qry->qtype);
+ qinfo->qclass = htons(qry->qclass);
+
+ qry->pktsize = (strlen((const char*)qname) + 1) + sizeof(struct DNS_HEADER) + sizeof(struct QUESTION) ;
+ if(qry->opt_nsid || qry->opt_dnssec || (qry->opt_edns0 > 512)) {
+ e=(struct EDNS0_HEADER*)&packet[ qry->pktsize + 1 ];
+ e->otype = htons(ns_t_opt);
+ e->_edns_udp_size = htons(qry->opt_edns0);
+ if(qry->opt_dnssec) {
+ e->Z = htons(0x8000);
+ }
+ else {
+ e->Z = 0x0;
+ }
+ crondlog(LVL5 "opt header in hex | %02X %02X %02X %02X %02X %02X %02X %02X %02X | %02X",
+ packet[qry->pktsize],
+ packet[qry->pktsize + 1],
+ packet[qry->pktsize + 2],
+ packet[qry->pktsize + 3],
+ packet[qry->pktsize + 4],
+ packet[qry->pktsize + 5],
+ packet[qry->pktsize + 6],
+ packet[qry->pktsize + 7],
+ packet[qry->pktsize + 8],
+ packet[qry->pktsize + 9]);
+
+ qry->pktsize += sizeof(struct EDNS0_HEADER) ;
+
+ if(qry->opt_nsid ) {
+ dns->add_count = htons(1);
+ n=(struct EDNS_NSID*)&packet[ qry->pktsize + 1 ];
+ n->len = htons(4);
+ n->otype = htons(3);
+ }
+ qry->pktsize += sizeof(struct EDNS_NSID) + 1;
+ dns->add_count = htons(1);
+ /* Transmit the request over the network */
+ }
+ buf_init(&pbuf, -1);
+
+ if(qry->pktsize) {
+ snprintf(line, DEFAULT_LINE_LENGTH, "%0d bytes ", qry->pktsize);
+ buf_add(&pbuf, line, strlen(line));
+
+ line[0] = '"';
+ buf_add(&pbuf, line, 1);
+ for(int x = 0; x < qry->pktsize; x++) {
+ snprintf(line, DEFAULT_LINE_LENGTH, "%02X ", packet[x]);
+ buf_add(&pbuf, line, 3);
+ }
+ line[0] = '"';
+ line[1] = '\0';
+ buf_add(&pbuf, line, 2 );
+ crondlog(LVL5 "payload : %s", pbuf.buf);
+ buf_cleanup(&pbuf);
+ }
+}
+
+
+
+/* Attempt to transmit a UDP DNS Request to a serveri. TCP is else where */
+static void tdig_send_query_callback(int unused UNUSED_PARAM, const short event UNUSED_PARAM, void *h)
+{
+ struct query_state *qry = h;
+ struct tdig_base *base = qry->base;
+ uint32_t nsent = 0;
+ u_char *outbuff;
+ int err = 0;
+ int sockfd;
+
+ /* Clean the no reply timer (if any was previously set) */
+ evtimer_del(&qry->noreply_timer);
+
+ outbuff = xzalloc(MAX_DNS_OUT_BUF_SIZE);
+ bzero(outbuff, MAX_DNS_OUT_BUF_SIZE);
+ //AA delete qry->outbuff = outbuff;
+ gettimeofday(&qry->xmit_time, NULL);
+ mk_dns_buff(qry, outbuff);
+ do {
+ switch (qry->res->ai_family) {
+ case AF_INET:
+ nsent = sendto(base->rawfd_v4, outbuff,qry->pktsize, MSG_DONTWAIT, qry->res->ai_addr, qry->res->ai_addrlen);
+ break;
+ case AF_INET6:
+ nsent = sendto(base->rawfd_v6, outbuff,qry->pktsize, MSG_DONTWAIT, qry->res->ai_addr, qry->res->ai_addrlen);
+ break;
+ }
+ qry->ressent = qry->res;
+
+ if (nsent == qry->pktsize) {
+ // the packet is send. Now lets try to the source address we would have used.
+ // create another sock with same dest, connect and get the source address
+ // delete that socket and hope the the source address is the right one.
+ if ((sockfd = socket(qry->res->ai_family, SOCK_DGRAM, 0 ) ) < 0 ) {
+ snprintf(line, DEFAULT_LINE_LENGTH, "%s \"socket\" : \"temp socket to get src address failed %s\"", qry->err.size ? ", " : "", strerror(errno));
+ buf_add(&qry->err, line, strlen(line));
+ return;
+ }
+ else {
+ qry->loc_socklen = sizeof(qry->loc_sin6);
+ connect(sockfd, qry->res->ai_addr, qry->res->ai_addrlen);
+ if (getsockname(sockfd,(struct sockaddr *)&qry->loc_sin6, &qry->loc_socklen) == -1) {
+ snprintf(line, DEFAULT_LINE_LENGTH, "%s \"getscokname\" : \"%s\"", qry->err.size ? ", " : "", strerror(errno));
+ buf_add(&qry->err, line, strlen(line));
+ }
+ close(sockfd);
+ }
+
+ /* One more DNS Query is sent */
+ base->sentok++;
+ base->sentbytes += nsent;
+ err = 0;
+ /* Add the timer to handle no reply condition in the given timeout */
+ evtimer_add(&qry->noreply_timer, &base->tv_noreply);
+ if(qry->opt_qbuf) {
+ buf_init(&qry->qbuf, -1);
+ buf_add_b64(&qry->qbuf, outbuff, qry->pktsize, 0);
+ }
+
+ }
+ else {
+ err = 1;
+ base->sendfail++;
+ snprintf(line, DEFAULT_LINE_LENGTH, "%s \"senderror\" : \"AF %s, %s\"", qry->err.size ? ", " : ""
+ , strerror(errno) , qry->res->ai_family == AF_INET ? "AF_INET" :"NOT AF_INET");
+ buf_add(&qry->err, line, strlen(line));
+ }
+ } while ((qry->res = qry->res->ai_next) != NULL);
+ free (outbuff);
+ outbuff = NULL;
+ if(err) {
+ printReply (qry, 0, NULL);
+ return;
+ }
+}
+
+static void next_qry_cb(int unused UNUSED_PARAM, const short event UNUSED_PARAM, void *h) {
+ struct query_state *qry = h;
+ BLURT(LVL5 "next query for %s", qry->server_name);
+ tdig_start(qry);
+}
+
+/* The callback to handle timeouts due to destination host unreachable condition */
+static void noreply_callback(int unused UNUSED_PARAM, const short event UNUSED_PARAM, void *h)
+{
+ struct query_state *qry = h;
+ qry->base->timeout++;
+ snprintf(line, DEFAULT_LINE_LENGTH, "%s \"timeout\" : %d", qry->err.size ? ", " : "", DEFAULT_NOREPLY_TIMEOUT);
+ buf_add(&qry->err, line, strlen(line));
+
+ BLURT(LVL5 "AAA timeout for %s ", qry->server_name);
+ printReply (qry, 0, NULL);
+ return;
+}
+
+static void tcp_timeout_callback (int __attribute((unused)) unused,
+ const short __attribute((unused)) event, void *s)
+{
+ struct query_state * qry;
+ qry = ENV2QRY(s);
+ noreply_callback(0, 0, qry);
+}
+
+static void tcp_reporterr(struct tu_env *env, enum tu_err cause,
+ const char *str)
+{
+ struct query_state * qry;
+ qry = ENV2QRY(env);
+
+ // if (env != &state->tu_env) abort(); // Why do i need this? AA
+
+ switch(cause)
+ {
+ case TU_DNS_ERR:
+
+ snprintf(line, DEFAULT_LINE_LENGTH, "%s \"TUDNS\" : \"%s\"", qry->err.size ? ", " : "", str );
+ buf_add(&qry->err, line, strlen(line));
+ break;
+
+ case TU_READ_ERR:
+ // need more than this reporting for this case AA
+ snprintf(line, DEFAULT_LINE_LENGTH, "%s \"TU_READ_ERR\" : \"%s\"", qry->err.size ? ", " : "", str );
+ buf_add(&qry->err, line, strlen(line));
+ break;
+
+ case TU_CONNECT_ERR:
+ snprintf(line, DEFAULT_LINE_LENGTH, "%s \"TUCONNECT\" : \"%s\"", qry->err.size ? ", " : "", str );
+ buf_add(&qry->err, line, strlen(line));
+ //reconnect next one AA
+ break;
+
+ case TU_OUT_OF_ADDRS:
+ snprintf(line, DEFAULT_LINE_LENGTH, "%s \"TU_OUT_OF_ADDRESS\" : \"%s\"", qry->err.size ? ", " : "", str );
+ buf_add(&qry->err, line, strlen(line));
+ break;
+
+ default:
+ snprintf(line, DEFAULT_LINE_LENGTH, "%s \"TU_UNKNOWN\" : \"%d %s\"", qry->err.size ? ", " : "", cause, str );
+ crondlog(DIE9 "reporterr: bad cause %d", cause);
+ break;
+ }
+ printReply (qry, 0, NULL);
+}
+
+static void tcp_dnscount(struct tu_env *env, int count)
+{
+ struct query_state * qry;
+ qry = ENV2QRY(env);
+ BLURT(LVL5 "dns count for %s : %d", qry->server_name , count);
+}
+
+static void tcp_beforeconnect(struct tu_env *env,
+ struct sockaddr *addr, socklen_t addrlen)
+{
+ struct query_state * qry;
+ qry = ENV2QRY(env);
+ gettimeofday(&qry->xmit_time, NULL);
+ qry->dst_ai_family = addr->sa_family;
+ BLURT(LVL5 "time : %d", qry->xmit_time.tv_sec);
+ getnameinfo(addr, addrlen, qry->dst_addr_str, INET6_ADDRSTRLEN , NULL, 0, NI_NUMERICHOST);
+}
+
+static void tcp_connected(struct tu_env *env, struct bufferevent *bev)
+{
+ uint16_t payload_len ;
+ u_char *outbuff;
+ u_char *wire;
+ struct query_state * qry;
+ qry = ENV2QRY(env);
+
+ qry->loc_socklen= sizeof(qry->loc_sin6);
+ getsockname(bufferevent_getfd(bev), &qry->loc_sin6, &qry->loc_socklen);
+
+ qry->bev_tcp = bev;
+ outbuff = xzalloc(MAX_DNS_BUF_SIZE);
+ bzero(outbuff, MAX_DNS_OUT_BUF_SIZE);
+ mk_dns_buff(qry, outbuff);
+ payload_len = (uint16_t) qry->pktsize;
+ wire = xzalloc (payload_len + 4);
+ ldns_write_uint16(wire, qry->pktsize);
+ memcpy(wire + 2, outbuff, qry->pktsize);
+ evbuffer_add(bufferevent_get_output(qry->bev_tcp), wire, (qry->pktsize +2));
+ qry->base->sentok++;
+ qry->base->sentbytes+= (qry->pktsize +2);
+ BLURT(LVL5 "send %u bytes", payload_len );
+
+ if(qry->opt_qbuf) {
+ buf_init(&qry->qbuf, -1);
+ buf_add_b64(&qry->qbuf, outbuff, qry->pktsize, 0);
+ }
+ free(outbuff);
+ free(wire);
+}
+
+static void tcp_readcb(struct bufferevent *bev UNUSED_PARAM, void *ptr)
+{
+
+ struct query_state *qry = ptr;
+ int n;
+ u_char b2[2];
+ struct timeval rectime;
+ struct evbuffer *input ;
+ struct DNS_HEADER *dnsR = NULL;
+
+ qry = ENV2QRY(ptr);
+ BLURT(LVL5 "TCP readcb %s", qry->server_name );
+
+ if( qry->packet.size && (qry->packet.size >= qry->wire_size)) {
+ snprintf(line, DEFAULT_LINE_LENGTH, "%s \"TCPREADSIZE\" : "
+ " \"red more bytes than expected %d, got %zu\""
+ , qry->err.size ? ", " : ""
+ , qry->wire_size, qry->packet.size);
+ buf_add(&qry->err, line, strlen(line));
+ printReply (qry, 0, NULL);
+ return;
+ }
+
+ gettimeofday(&rectime, NULL);
+ bzero(qry->base->packet, MAX_DNS_BUF_SIZE);
+
+ input = bufferevent_get_input(bev);
+ if(qry->wire_size == 0) {
+ n = evbuffer_remove(input, b2, 2 );
+ if(n == 2){
+ qry->wire_size = ldns_read_uint16(b2);
+ buf_init(&qry->packet, -1);
+ }
+ else {
+
+ snprintf(line, DEFAULT_LINE_LENGTH, "%s \"TCPREAD\" : \"expected 2 bytes and got %d\"", qry->err.size ? ", " : "", n );
+ buf_add(&qry->err, line, strlen(line));
+ }
+ }
+ while ((n = evbuffer_remove(input,line , DEFAULT_LINE_LENGTH )) > 0) {
+ buf_add(&qry->packet, line, n);
+ if(qry->wire_size == qry->packet.size) {
+ crondlog(LVL5 "in readcb %s %s red %d bytes ", qry->str_Atlas, qry->server_name, qry->wire_size);
+ crondlog(LVL5 "qry pointer address readcb %p qry.id, %d", qry->qryid);
+ crondlog(LVL5 "DBG: base pointer address readcb %p", qry->base );
+ dnsR = (struct DNS_HEADER*) qry->packet.buf;
+ if ( ntohs(dnsR->id) == qry->qryid ) {
+ qry->triptime = (rectime.tv_sec - qry->xmit_time.tv_sec)*1000 + (rectime.tv_usec-qry->xmit_time.tv_usec)/1e3;
+ printReply (qry, qry->packet.size, qry->packet.buf);
+ }
+ else {
+ bzero(line, DEFAULT_LINE_LENGTH);
+ snprintf(line, DEFAULT_LINE_LENGTH, " %s \"idmismatch\" : \"mismatch id from tcp fd %d\"", qry->err.size ? ", " : "", n);
+ buf_add(&qry->err, line, strlen(line));
+ printReply (qry, 0, NULL);
+ }
+ return;
+ }
+ }
+}
+
+static void tcp_writecb(struct bufferevent *bev, void *ptr)
+{
+ /*
+ struct query_state * qry;
+ qry = ENV2QRY(ptr);
+ */
+ BLURT(LVL5 "TCP writecb");
+}
+
+
+
+/*
+ * Called by libevent when the kernel says that the raw socket is ready for reading.
+ *
+ * It reads a packet from the wire and attempt to decode and relate DNS Request/Reply.
+ *
+ * To be legal the packet received must be:
+ * o of enough size (> DNS Header size)
+ * o the one we are looking for (matching the same identifier of all the packets the program is able to send)
+ */
+
+static void process_reply(void * arg, int nrecv, struct timeval now, int af, void *remote )
+{
+ struct tdig_base *base = arg;
+
+ struct DNS_HEADER *dnsR = NULL;
+
+ struct query_state * qry;
+
+ if (nrecv < sizeof (struct DNS_HEADER)) {
+ base->shortpkt++;
+ return;
+ }
+
+ dnsR = (struct DNS_HEADER*) base->packet;
+ base->recvok++;
+
+
+ crondlog(LVL7 "DBG: base address process reply %p, nrec %d", base, nrecv);
+ /* Get the pointer to the qry descriptor in our internal table */
+ qry = tdig_lookup_query(base, ntohs(dnsR->id), af, remote);
+
+ if ( ! qry) {
+ base->martian++;
+ crondlog(LVL7 "DBG: no match found for qry id i %d",\
+ntohs(dnsR->id));
+ return;
+ }
+
+ qry->base->recvbytes += nrecv;
+ gettimeofday(&now, NULL); // lave this till fix now from ready_callback6 corruption; ghoost
+ qry->triptime = (now.tv_sec-qry->xmit_time.tv_sec)*1000 + (now.tv_usec-qry->xmit_time.tv_usec)/1e3;
+
+ /* Clean the noreply timer */
+ evtimer_del(&qry->noreply_timer);
+ printReply (qry, nrecv, base->packet);
+ return;
+}
+
+static void ready_callback4 (int unused UNUSED_PARAM, const short event UNUSED_PARAM, void * arg)
+{
+ struct tdig_base *base = arg;
+ int nrecv;
+ struct sockaddr_in remote4; /* responding internet address */
+ socklen_t slen;
+ struct timeval rectime;
+
+ slen = sizeof(struct sockaddr);
+ bzero(base->packet, MAX_DNS_BUF_SIZE);
+ /* Time the packet has been received */
+
+ gettimeofday(&rectime, NULL);
+ /* Receive data from the network */
+ nrecv = recvfrom(base->rawfd_v4, base->packet, sizeof(base->packet), MSG_DONTWAIT, &remote4, &slen);
+ if (nrecv < 0) {
+ /* One more failure */
+ base->recvfail++;
+ return ;
+ }
+ process_reply(arg, nrecv, rectime, remote4.sin_family, &remote4);
+ return;
+}
+
+static void ready_callback6 (int unused UNUSED_PARAM, const short event UNUSED_PARAM, void * arg)
+{
+ struct tdig_base *base = arg;
+ int nrecv;
+ struct timeval rectime;
+ struct msghdr msg;
+ struct iovec iov[1];
+ //char buf[INET6_ADDRSTRLEN];
+ struct sockaddr_in6 remote6;
+ char cmsgbuf[256];
+
+ /* Time the packet has been received */
+ gettimeofday(&rectime, NULL);
+
+ iov[0].iov_base= base->packet;
+ iov[0].iov_len= sizeof(base->packet);
+
+ msg.msg_name= &remote6;
+ msg.msg_namelen= sizeof( struct sockaddr_in6);
+ msg.msg_iov= iov;
+ msg.msg_iovlen= 1;
+ msg.msg_control= cmsgbuf;
+ msg.msg_controllen= sizeof(cmsgbuf);
+ msg.msg_flags= 0; /* Not really needed */
+
+ nrecv= recvmsg(base->rawfd_v6, &msg, MSG_DONTWAIT);
+ if (nrecv == -1) {
+ /* Strange, read error */
+ printf("ready_callback6: read error '%s'\n", strerror(errno));
+ return;
+ }
+ process_reply(arg, nrecv, rectime, remote6.sin6_family, &remote6);
+
+ return;
+}
+
+/* this called for each query/line in eperd */
+static void *tdig_init(int argc, char *argv[], void (*done)(void *state))
+{
+ char *check;
+ struct query_state *qry;
+ int c;
+
+ if(!tdig_base)
+ tdig_base = tdig_base_new(EventBase);
+
+ if(!tdig_base)
+ crondlog(DIE9 "tdig_base_new failed");
+
+ tdig_base->done = done;
+
+ qry=xzalloc(sizeof(*qry));
+
+ // initialize per query state variables;
+ qry->qtype = T_TXT; /* TEXT */
+ qry->qclass = C_CHAOS;
+ qry->opt_v4_only = 0;
+ qry->opt_v6_only = 0;
+ qry->str_Atlas = NULL;
+ qry->out_filename = NULL;
+ qry->opt_proto = 17;
+ qry->tcp_file = NULL;
+ qry->tcp_fd = -1;
+ qry->server_name = NULL;
+ qry->str_Atlas = NULL;
+ tdig_base->activeqry++;
+ qry->qst = 0;
+ qry->wire_size = 0;
+ qry->triptime = 0;
+ qry->opt_edns0 = 512;
+ qry->opt_dnssec = 0;
+ qry->opt_nsid = 0;
+ qry->opt_qbuf = 0;
+ qry->opt_abuf = 1;
+ qry->opt_rd = 0;
+ qry->opt_evdns = 0;
+ qry->opt_prepend_probe_id = 0;
+ qry->ressave = NULL;
+ qry->ressent = NULL;
+ buf_init(&qry->err, -1);
+ buf_init(&qry->packet, -1);
+ qry->opt_resolv_conf = (Q_RESOLV_CONF - 1);
+ qry->lookupname = NULL;
+ qry->dst_ai_family = 0;
+ qry->loc_ai_family = 0;
+ qry->loc_sin6.sin6_family = 0;
+
+ /* initialize callbacks : */
+ /* sendpacket called by UDP send */
+ evtimer_assign(&qry->nsm_timer, tdig_base->event_base,
+ tdig_send_query_callback, qry);
+ /* no reply timeout for udp queries */
+ evtimer_assign(&qry->noreply_timer, tdig_base->event_base,
+ noreply_callback, qry);
+
+ /* callback/timer used for restarting query by --resove */
+ evtimer_assign(&qry->next_qry_timer, tdig_base->event_base, next_qry_cb
+ ,qry);
+
+ optind = 0;
+ while (c= getopt_long(argc, argv, "46adD:e:tbhinqO:Rrs:A:?", longopts, NULL), c != -1) {
+ switch(c) {
+ case '4':
+ qry->opt_v4_only = 1;
+ qry->opt_AF = AF_INET;
+ break;
+ case '6':
+ qry->opt_v6_only = 1;
+ qry->opt_AF = AF_INET6;
+ break;
+
+ case 'a':
+ qry->opt_v6_only = 1;
+ qry->opt_v4_only = 1;
+ break;
+
+ case 'A':
+ qry->str_Atlas = strdup(optarg);
+ break;
+ case 'b':
+ qry->lookupname = strdup ("version.bind.");
+ break;
+
+ case 'd':
+ qry->opt_dnssec = 1;
+ break;
+
+ case 'e':
+ qry->opt_edns0= strtoul(optarg, &check, 10);
+ break;
+
+ case 'h':
+ qry->lookupname = strdup("hostname.bind.");
+ break;
+
+ case 'i':
+ qry->lookupname = strdup("id.server.");
+ break;
+
+ case 'n':
+ qry->opt_nsid = 1;
+ break;
+
+ case 'O':
+ qry->out_filename = strdup(optarg);
+ break;
+
+ case 'r':
+ qry->lookupname = strdup("version.server.");
+ break;
+
+ case 'R':
+ qry->opt_rd = 1;
+ break;
+
+ case 's':
+ qry->qtype = T_SOA;
+ qry->qclass = C_IN;
+ qry->lookupname = strdup(optarg);
+ break;
+
+ case 't':
+ qry->opt_proto = 6;
+ break;
+
+ case 1001:
+ qry->opt_qbuf = 1;
+ break;
+
+ case 1002:
+ qry->opt_abuf = 0;
+ break;
+
+ case O_RESOLV_CONF :
+ qry->opt_resolv_conf = Q_RESOLV_CONF ;
+ qry->opt_v6_only = 1;
+ qry->opt_v4_only = 1;
+ break;
+
+ case O_PREPEND_PROBE_ID:
+ qry->opt_prepend_probe_id = 1;
+ break;
+
+ case O_EVDNS:
+ qry->opt_evdns = 1;
+ break;
+
+ case (100000 + T_A):
+ qry->qtype = T_A;
+ qry->qclass = C_IN;
+ qry->lookupname = strdup(optarg);
+ break;
+
+ case (100000 + T_NS):
+ qry->qtype = T_NS;
+ qry->qclass = C_IN;
+ qry->lookupname = strdup(optarg);
+ break;
+
+ case (100000 + T_CNAME):
+ qry->qtype = T_CNAME;
+ qry->qclass = C_IN;
+ qry->lookupname = strdup(optarg);
+ break;
+
+ case (100000 + T_PTR):
+ qry->qtype = T_PTR;
+ qry->qclass = C_IN;
+ qry->lookupname = strdup(optarg);
+ break;
+
+ case (100000 + T_MX):
+ qry->qtype = T_MX;
+ qry->qclass = C_IN;
+ qry->lookupname = strdup(optarg);
+ break;
+
+ case (100000 + T_TXT):
+ qry->qtype = T_TXT;
+ qry->qclass = C_IN;
+ qry->lookupname = strdup(optarg);
+ break;
+
+ case (100000 + T_AAAA ):
+ qry->qtype = T_AAAA ;
+ qry->qclass = C_IN;
+ qry->lookupname = strdup(optarg);
+ break;
+
+ case (100000 + T_AXFR ):
+ qry->qtype = T_AXFR ;
+ qry->qclass = C_IN;
+ qry->lookupname = strdup(optarg);
+ break;
+
+ case (100000 + T_ANY):
+ qry->qtype = T_ANY ;
+ qry->qclass = C_IN;
+ qry->lookupname = strdup(optarg);
+ break;
+
+ case (100000 + T_DS):
+ qry->qtype = T_DS;
+ qry->qclass = C_IN;
+ qry->lookupname = strdup(optarg);
+ break;
+
+ case (100000 + T_NSEC):
+ qry->qtype = T_NSEC;
+ qry->qclass = C_IN;
+ qry->lookupname = strdup(optarg);
+ break;
+
+ case (100000 + T_NSEC3):
+ qry->qtype = T_NSEC3;
+ qry->qclass = C_IN;
+ qry->lookupname = strdup(optarg);
+ break;
+
+ case (100000 + T_DNSKEY):
+ qry->qtype = T_DNSKEY;
+ qry->qclass = C_IN;
+ qry->lookupname = strdup(optarg);
+ break;
+
+ case (100000 + T_RRSIG):
+ qry->qtype = T_RRSIG;
+ qry->qclass = C_IN;
+ qry->lookupname = strdup(optarg);
+ break;break;
+
+ default:
+ fprintf(stderr, "ERROR unknown option %d ??\n", c);
+ tdig_delete(qry);
+ return (0);
+ break;
+ }
+ }
+ if( qry->opt_resolv_conf == Q_RESOLV_CONF ) {
+ if(tdig_base->resolv_max ) {
+ qry->opt_resolv_conf = 1;
+ qry->server_name = strdup(tdig_base->nslist[0]);
+ }
+ else {
+ // may be the /etc/resolv.conf is yet to red.
+ // try once then use it || give up
+ tdig_base->resolv_max = get_local_resolvers (tdig_base->nslist);
+ if(tdig_base->resolv_max ){
+ qry->opt_resolv_conf = 1;
+ qry->server_name = strdup(tdig_base->nslist[0]);
+ }
+ else {
+ tdig_delete(qry);
+ return NULL;
+ }
+ }
+ }
+ else if (optind != argc-1) {
+ crondlog(LVL9 "ERROR no server IP address in input");
+ tdig_delete(qry);
+ return NULL;
+ }
+ else
+ qry->server_name = strdup(argv[optind]);
+
+ if(qry->lookupname == NULL) {
+ crondlog(LVL9 "ERROR no query in command line");
+ tdig_delete(qry);
+ return NULL;
+ }
+
+ if (qry->out_filename &&
+ !validate_filename(qry->out_filename, SAFE_PREFIX))
+ {
+ crondlog(LVL8 "insecure file '%s'", qry->out_filename);
+ tdig_delete(qry);
+ return NULL;
+ }
+
+
+ if(qry->opt_v6_only == 0)
+ {
+ qry->opt_v4_only = 1;
+ qry->opt_AF = AF_INET;
+ }
+ qry->base = tdig_base;
+
+ /* insert this qry into the list of queries */
+ if (!tdig_base->qry_head) {
+ qry->next = qry->prev = qry;
+ tdig_base->qry_head = qry;
+ tdig_stats( 0, 0, tdig_base); // call this first time to initial values.
+ crondlog(LVL7 "new head qry %s qry->prev %s qry->next %s", qry->str_Atlas, qry->prev->str_Atlas, qry->next->str_Atlas);
+ }
+ else {
+ crondlog(LVL7 "old head hea %s hea->prev %s hea->next %s", tdig_base->qry_head->str_Atlas, tdig_base->qry_head->prev->str_Atlas, tdig_base->qry_head->next->str_Atlas);
+ if (tdig_base->qry_head->prev == tdig_base->qry_head) {
+ tdig_base->qry_head->prev = qry;
+ crondlog(LVL7 "head->prev == head quereis %d AA", tdig_base->activeqry);
+ }
+ qry->next = tdig_base->qry_head->next;
+ qry->prev = tdig_base->qry_head;
+ tdig_base->qry_head->next->prev = qry;
+ tdig_base->qry_head->next = qry;
+ crondlog(LVL7 " qry %s qry->prev %s qry->next %s", qry->str_Atlas, qry->prev->str_Atlas, qry->next->str_Atlas);
+ crondlog(LVL7 "new head hea %s hea->prev %s hea->next %s", tdig_base->qry_head->str_Atlas, tdig_base->qry_head->prev->str_Atlas, tdig_base->qry_head->next->str_Atlas);
+ }
+ return qry;
+}
+
+/* called only once. Initialize tdig_base variables here */
+struct tdig_base * tdig_base_new(struct event_base *event_base)
+{
+ evutil_socket_t fd6;
+ evutil_socket_t fd4;
+ struct addrinfo hints;
+ int on = 1;
+ struct timeval tv;
+
+ bzero(&hints,sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_flags = 0;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = 0;
+
+ /* Create an endpoint for communication using raw socket for ICMP calls */
+ if ((fd4 = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol) ) < 0 )
+ {
+ return NULL;
+ }
+
+ hints.ai_family = AF_INET6;
+ if ((fd6 = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol) ) < 0 )
+ {
+ close(fd4);
+ return NULL;
+ }
+
+ tdig_base= xzalloc(sizeof( struct tdig_base));
+ if (tdig_base == NULL)
+ {
+ close(fd4);
+ close(fd6);
+ return (NULL);
+ }
+
+ tdig_base->qry_head = NULL;
+ tdig_base->sendfail = 0;
+ tdig_base->sentok = 0;
+ tdig_base->recvfail = 0;
+ tdig_base->recvok = 0;
+ tdig_base->martian = 0;
+ tdig_base->shortpkt = 0;
+ tdig_base->sentbytes = 0;
+ tdig_base->recvbytes = 0;
+ tdig_base->timeout = 0;
+ tdig_base->activeqry = 0;
+ tdig_base->resolv_max = 0;
+
+ memset(tdig_base, 0, sizeof(struct tdig_base));
+ tdig_base->event_base = event_base;
+
+ tdig_base->rawfd_v4 = fd4;
+ tdig_base->rawfd_v6 = fd6;
+
+ setsockopt(fd6, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
+
+ on = 1;
+ setsockopt(fd6, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on));
+
+ //memset(&tdig_base-->loc_sin6, '\0', sizeof(tdig_base-->loc_sin6));
+ //tdig_base-->loc_socklen= 0;
+
+ evutil_make_socket_nonblocking(tdig_base->rawfd_v4);
+
+ msecstotv(DEFAULT_NOREPLY_TIMEOUT, &tdig_base->tv_noreply);
+
+ // Define the callback to handle UDP Reply
+ // add the raw file descriptor to those monitored for read events
+
+ event_assign(&tdig_base->event4, tdig_base->event_base, tdig_base->rawfd_v4,
+ EV_READ | EV_PERSIST, ready_callback4, tdig_base);
+ event_add(&tdig_base->event4, NULL);
+
+ event_assign(&tdig_base->event6, tdig_base->event_base, tdig_base->rawfd_v6,
+ EV_READ | EV_PERSIST, ready_callback6, tdig_base);
+ event_add(&tdig_base->event6, NULL);
+
+ evtimer_assign(&tdig_base->statsReportEvent, tdig_base->event_base, tdig_stats, tdig_base);
+ tv.tv_sec = DEFAULT_STATS_REPORT_INTERVEL;
+ tv.tv_usec = 0;
+ event_add(&tdig_base->statsReportEvent, &tv);
+
+ return tdig_base;
+}
+
+static void udp_dns_cb(int err, struct evutil_addrinfo *ev_res, struct query_state *qry) {
+
+ if (err) {
+ qry->qst = STATUS_FREE;
+ snprintf(line, DEFAULT_LINE_LENGTH, "\"evdns_getaddrinfo\": \"%s\"", evutil_gai_strerror(err));
+ buf_add(&qry->err, line, strlen(line));
+ printReply (qry, 0, NULL);
+ return ;
+
+ }
+ else {
+ qry->res = ev_res;
+ qry->ressave = ev_res;
+ tdig_send_query_callback(0, 0, qry);
+ }
+}
+
+void tdig_start (struct query_state *qry)
+{
+ struct timeval asap = { 0, 0 };
+ struct timeval interval;
+
+ int err_num;
+ struct addrinfo hints, *res;
+ char port[] = "domain";
+ char port_as_char[] = "53";
+
+ switch(qry->qst)
+ {
+ case STATUS_NEXT_QUERY :
+ case STATUS_FREE :
+ break;
+ default:
+ printErrorQuick(qry);
+ return ;
+ }
+
+ if(qry->opt_resolv_conf > tdig_base->resolv_max) {
+ qry->opt_resolv_conf = 0;
+ free (qry->server_name);
+ qry->server_name = strdup(tdig_base->nslist[qry->opt_resolv_conf]);
+ qry->opt_resolv_conf++;
+ }
+
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = 0;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = 0;
+
+ gettimeofday(&qry->xmit_time, NULL);
+ qry->qst = STATUS_DNS_RESOLV;
+
+ if(qry->opt_v6_only == 1)
+ {
+ hints.ai_family = AF_INET6;
+ }
+ else if(qry->opt_v4_only == 1)
+ {
+ hints.ai_family = AF_INET;
+ }
+
+ if( (qry->opt_v4_only == 1 ) && (qry->opt_v6_only == 1) )
+ {
+ hints.ai_family = AF_UNSPEC;
+ }
+
+ if(qry->opt_proto == 17) { //UDP
+ if(qry->opt_evdns ) {
+ // use EVDNS asynchronous call
+ evdns_getaddrinfo(DnsBase, qry->server_name, port_as_char , &hints, udp_dns_cb, qry);
+ }
+ else {
+ // using getaddrinfo; blocking call
+ if ( ( err_num = getaddrinfo(qry->server_name, port , &hints, &res)))
+ {
+ snprintf(line, DEFAULT_LINE_LENGTH, "%s \"getaddrinfo\": \"port %s, AF %d %s\"", qry->err.size ? ", " : "", port, hints.ai_family, gai_strerror(err_num));
+ buf_add(&qry->err, line, strlen(line));
+
+ printReply (qry, 0, NULL);
+ qry->qst = STATUS_FREE;
+ return ;
+ }
+
+ qry->res = res;
+ qry->ressave = res;
+
+ evtimer_add(&qry->nsm_timer, &asap);
+ }
+ }
+ else { // TCP Query
+
+ qry->wire_size = 0;
+ crondlog(LVL5 "TCP QUERY %s", qry->server_name);
+ interval.tv_sec = CONN_TO;
+ interval.tv_usec= 0;
+ tu_connect_to_name (&qry->tu_env, qry->server_name, port_as_char,
+ &interval, &hints, tcp_timeout_callback, tcp_reporterr,
+ tcp_dnscount, tcp_beforeconnect,
+ tcp_connected, tcp_readcb, tcp_writecb);
+
+ }
+ return ;
+}
+
+#if 0
+int tdig_base_count_queries(struct tdig_base *base)
+{
+ const struct query_state *qry;
+ int n = 0;
+
+ qry = base->qry_head;
+ if (!qry)
+ return 0;
+ do {
+ ++n;
+ qry = qry->next;
+ } while (qry != base->qry_head);
+
+ return n;
+}
+
+#endif
+
+static void tdig_stats(int unusg_statsed UNUSED_PARAM, const short event UNUSED_PARAM, void *h)
+{
+ struct timeval now;
+ FILE *fh;
+ struct tdig_base *base;
+ struct query_state *qry;
+
+ base = h;
+ if(!base->qry_head )
+ return;
+
+ qry = base->qry_head;
+
+ if(! base->sentok )
+ return;
+
+ if (qry->out_filename) {
+ fh= fopen(qry->out_filename, "a");
+ if (!fh)
+ crondlog(DIE9 "unable to append to '%s'", qry->out_filename);
+ }
+ else
+ fh = stdout;
+
+ fprintf(fh, "RESULT { ");
+ JS(id, "9201" );
+ gettimeofday(&now, NULL);
+ JS1(time, %ld, now.tv_sec);
+ JU(sok , base->sentok);
+ JU(rok , base->recvok);
+ JU(sent , base->sentbytes);
+ JU(recv , base->recvbytes);
+ JU(serr , base->sendfail);
+ JU(rerr , base->recvfail);
+ JU(timeout , base->timeout);
+ JU(short , base->shortpkt);
+ JU(martian, base->martian);
+ JU_NC(q, base->activeqry);
+
+ fprintf(fh, " }\n");
+ if (qry->out_filename)
+ fclose (fh);
+ // reuse timeval now
+ now.tv_sec = DEFAULT_STATS_REPORT_INTERVEL;
+ now.tv_usec = 0;
+ event_add(&tdig_base->statsReportEvent, &now);
+}
+
+
+static void ChangetoDnsNameFormat(u_char * dns, char* qry)
+{
+ int lock = 0, i;
+
+ for(i = 0 ; i < (int)strlen((char*)qry) ; i++)
+ {
+ //printf ("%c", qry[i] );
+ if(qry[i]=='.')
+ {
+ *dns++=i-lock;
+ for(;lock<i;lock++) {
+ *dns++=qry[lock];
+ }
+ lock++; //or lock=i+1;
+ }
+ }
+ *dns++=0;
+}
+
+
+static void free_qry_inst(struct query_state *qry)
+{
+ struct timeval asap = { 0, 0 };
+ BLURT(LVL5 "freeing instance of %s ", qry->server_name);
+
+ if(qry->err.size)
+ {
+ buf_cleanup(&qry->err);
+ }
+ if(qry->qbuf.size)
+ buf_cleanup(&qry->qbuf);
+
+ if(qry->ressave && qry->opt_evdns) {
+ evutil_freeaddrinfo(qry->ressave);
+ qry->ressave = NULL;
+ qry->ressent = NULL;
+ }
+ else if (qry->ressave )
+ {
+ freeaddrinfo(qry->ressave);
+ qry->ressave = NULL;
+ qry->ressent = NULL;
+ }
+ qry->qst = STATUS_FREE;
+ qry->wire_size = 0;
+
+ if(qry->packet.size)
+ {
+ buf_cleanup(&qry->packet);
+ }
+
+ if(qry->opt_proto == 6)
+ tu_cleanup(&qry->tu_env);
+
+ if ( qry->opt_resolv_conf > Q_RESOLV_CONF ) {
+ // this loop goes over servers in /etc/resolv.conf
+ // select the next server and restart
+ if(qry->opt_resolv_conf < tdig_base->resolv_max) {
+ free (qry->server_name);
+ qry->server_name = strdup(tdig_base->nslist[qry->opt_resolv_conf]);
+ qry->opt_resolv_conf++;
+ qry->qst = STATUS_NEXT_QUERY;
+ evtimer_add(&qry->next_qry_timer, &asap);
+ return;
+ }
+ else
+ qry->opt_resolv_conf++;
+ }
+
+ if(qry->base->done)
+ {
+ qry->base->done(qry);
+ /*
+ void (*terminator)(void *state);
+ struct event_base *event_base;
+ struct tdig_base *tbase;
+ terminator = qry->base->done;
+ event_base = qry->base->event_base;
+ if(DnsBase) {
+ evdns_base_free(DnsBase, 0);
+ DnsBase = NULL;
+ }
+ tbase = qry->base;
+ tdig_delete(qry);
+ free(tbase);
+ event_base_loopbreak(event_base);
+ event_base_free(event_base);
+ terminator(qry);
+ */
+ }
+
+}
+
+
+static int tdig_delete(void *state)
+{
+ struct query_state *qry;
+
+ qry = state;
+
+ if (qry->qst )
+ return 0;
+
+ if(qry->out_filename)
+ {
+ free(qry->out_filename);
+ qry->out_filename = NULL ;
+ }
+ if(qry->lookupname)
+ {
+ free(qry->lookupname);
+ qry->lookupname = NULL;
+ }
+
+ /* Delete timers */
+ evtimer_del(&qry->noreply_timer);
+ evtimer_del(&qry->nsm_timer);
+
+ if((qry->next == qry->prev) && (qry->next == qry)) {
+ qry->base->qry_head = NULL;
+ crondlog(LVL7 "deleted last query qry %s", qry->str_Atlas);
+ }
+ else {
+#if ENABLE_FEATURE_EVTDIG_DEBUG
+ crondlog(LVL7 "deleted qry %s qry->prev %s qry->next %s qry_head %s", qry->str_Atlas, qry->prev->str_Atlas, qry->next->str_Atlas, qry->base->qry_head->str_Atlas);
+ crondlog(LVL7 "old qry->next->prev %s qry->prev->next %s", qry->next->prev->str_Atlas, qry->prev->next->str_Atlas);
+#endif
+ if(qry->next)
+ qry->next->prev = qry->prev;
+ if(qry->prev)
+ qry->prev->next = qry->next;
+ if(qry->base && qry->base->qry_head == qry)
+ qry->base->qry_head = qry->next;
+
+#if ENABLE_FEATURE_EVTDIG_DEBUG
+ crondlog(LVL7 "new qry->next->prev %s qry->prev->next %s", qry->next->prev->str_Atlas, qry->prev->next->str_Atlas);
+#endif
+ }
+ if( qry->str_Atlas)
+ {
+ free( qry->str_Atlas);
+ qry->str_Atlas = NULL;
+ }
+ if(qry->server_name)
+ {
+ free(qry->server_name);
+ qry->server_name = NULL;
+ }
+ if(qry->base)
+ qry->base->activeqry--;
+ free(qry);
+ qry = NULL;
+ return 1;
+}
+
+void printErrorQuick (struct query_state *qry)
+{
+ FILE *fh;
+ struct timeval now;
+ if (qry->out_filename)
+ {
+ fh= fopen(qry->out_filename, "a");
+ if (!fh)
+ crondlog(DIE9 "unable to append to '%s'",
+ qry->out_filename);
+ }
+ else
+ fh = stdout;
+
+ fprintf(fh, "RESULT { ");
+ if(qry->str_Atlas)
+ {
+ JS(id, qry->str_Atlas);
+ }
+ gettimeofday(&now, NULL);
+ JS1(time, %ld, now.tv_sec);
+
+ snprintf(line, DEFAULT_LINE_LENGTH, "\"query busy\": \"too frequent. previous one is not done yet\"");
+ fprintf(fh, "\"error\" : { %s }" , line);
+
+ fprintf(fh, " }");
+ fprintf(fh, "\n");
+ if (qry->out_filename)
+ fclose(fh);
+}
+
+
+void printReply(struct query_state *qry, int wire_size, unsigned char *result )
+{
+ int i, stop=0;
+ unsigned char *qname, *reader;
+ struct DNS_HEADER *dnsR = NULL;
+ struct RES_RECORD answers[20]; //the replies from the DNS server
+ void *ptr = NULL;
+ char addrstr[100];
+ FILE *fh;
+ //char buf[INET6_ADDRSTRLEN];
+ u_int32_t serial;
+ struct buf tmpbuf;
+ char str[4];
+ int iMax ;
+ int flagAnswer = 1;
+ int data_len;
+
+ if (qry->out_filename)
+ {
+ fh= fopen(qry->out_filename, "a");
+ if (!fh)
+ crondlog(DIE9 "unable to append to '%s'",
+ qry->out_filename);
+ }
+ else
+ fh = stdout;
+
+ fprintf(fh, "RESULT { ");
+ if(qry->str_Atlas)
+ {
+ JS(id, qry->str_Atlas);
+ }
+ JS1(time, %ld, qry->xmit_time.tv_sec);
+ if ( qry->opt_resolv_conf > Q_RESOLV_CONF ) {
+ JD (subid, qry->opt_resolv_conf);
+ JD (submax, qry->base->resolv_max);
+ }
+ if( qry->ressent)
+ { // started to send query
+ // historic resaons only works with UDP
+ switch (qry->ressent->ai_family)
+ {
+ case AF_INET:
+ ptr = &((struct sockaddr_in *) qry->ressent->ai_addr)->sin_addr;
+ break;
+ case AF_INET6:
+ ptr = &((struct sockaddr_in6 *) qry->ressent->ai_addr)->sin6_addr;
+ break;
+ }
+ inet_ntop (qry->ressent->ai_family, ptr, addrstr, 100);
+ if(strcmp(addrstr, qry->server_name)) {
+ JS(name, qry->server_name);
+ }
+ JS(dst_addr, addrstr);
+ JD(af, qry->ressent->ai_family == PF_INET6 ? 6 : 4);
+ }
+ else if(qry->dst_ai_family)
+ {
+ if(strcmp(qry->dst_addr_str, qry->server_name)) {
+ JS(dst_name, qry->server_name);
+ }
+ JS(dst_addr , qry->dst_addr_str);
+ JD(af, qry->dst_ai_family == PF_INET6 ? 6 : 4);
+ }
+ else {
+ JS(dst_name, qry->server_name);
+ }
+ if(qry->loc_sin6.sin6_family) {
+ line[0] = '\0';
+ getnameinfo((struct sockaddr *)&qry->loc_sin6,
+ qry->loc_socklen, line, sizeof(line),
+ NULL, 0, NI_NUMERICHOST);
+ if(strlen(line))
+ JS(src_addr, line);
+ }
+
+ JS_NC(proto, qry->opt_proto == 6 ? "TCP" : "UDP" );
+ if(qry->opt_qbuf && qry->qbuf.size) {
+ str[0] = '\0';
+ buf_add(&qry->qbuf, str, 1);
+ JC;
+ JS_NC(qbuf, qry->qbuf.buf );
+ }
+
+
+ if(result)
+ {
+ dnsR = (struct DNS_HEADER*) result;
+
+ //point to the query portion
+ qname =(unsigned char*)&result[sizeof(struct DNS_HEADER)];
+
+ //move ahead of the dns header and the query field
+ reader = &result[sizeof(struct DNS_HEADER) + (strlen((const char*)qname)+1) + sizeof(struct QUESTION)];
+
+ fprintf (fh, ", \"result\" : { ");
+ fprintf (fh, " \"rt\" : %.3f", qry->triptime);
+ fprintf (fh, " , \"size\" : %d", wire_size);
+ fprintf (fh, " , \"ID\" : %d", ntohs(dnsR->id));
+ /*
+ fprintf (fh, " , \"RCODE\" : %d", dnsR->rcode);
+ fprintf (fh, " , \"AA\" : %d", dnsR->aa);
+ fprintf (fh, " , \"TC\" : %d", dnsR->tc);
+ */
+ fprintf (fh, " , \"ANCOUNT\" : %d ", ntohs(dnsR->ans_count ));
+ fprintf (fh, " , \"QDCOUNT\" : %u ",ntohs(dnsR->q_count));
+ fprintf (fh, " , \"NSCOUNT\" : %d" , ntohs(dnsR->ns_count));
+ fprintf (fh, " , \"ARCOUNT\" : %d ",ntohs(dnsR->add_count));
+
+ str[0] = '\0';
+ if(qry->opt_abuf) {
+ JC;
+ buf_init(&tmpbuf, -1);
+ buf_add_b64(&tmpbuf, result, wire_size, 0);
+ buf_add(&tmpbuf, str, 1);
+ JS_NC(abuf, tmpbuf.buf );
+ buf_cleanup(&tmpbuf);
+ }
+
+ stop=0;
+ iMax = 0;
+
+ if (dnsR->ans_count > 0)
+ {
+ iMax = MIN(2, ntohs(dnsR->ans_count));
+
+ for(i=0;i<iMax;i++)
+ {
+ answers[i].name=ReadName(result,wire_size,
+ reader-result,&stop);
+ reader = reader + stop;
+
+ answers[i].resource = (struct R_DATA*)(reader);
+ reader = reader + sizeof(struct R_DATA);
+
+ answers[i].rdata = NULL;
+
+
+ if(ntohs(answers[i].resource->type)==T_TXT) //txt
+ {
+ answers[i].rdata = NULL;
+ data_len = ntohs(answers[i].resource->data_len) - 1;
+
+ if(flagAnswer) {
+ fprintf (fh, ", \"answers\" : [ ");
+ flagAnswer = 0;
+ }
+ if (flagAnswer == 0) {
+ if(i > 0)
+ fprintf(fh, ", ");
+ fprintf(fh, " { ");
+ }
+ fprintf(fh, " \"TYPE\" : \"TXT\"");
+ fprintf(fh, " , \"NAME\" : \"%s.\" ",answers[i].name);
+ print_txt_json(&result[reader-result+1], data_len, fh);
+ reader = reader + ntohs(answers[i].resource->data_len);
+ if(flagAnswer == 0)
+ fprintf(fh, " } ");
+
+ }
+ else if (ntohs(answers[i].resource->type)== T_SOA)
+ {
+ if(flagAnswer) {
+ fprintf (fh, ", \"answers\" : [ ");
+ flagAnswer = 0;
+ }
+ if (flagAnswer == 0) {
+ if(i > 0)
+ fprintf(fh, ", ");
+ fprintf(fh, " { ");
+ }
+
+
+ JS(TYPE, "SOA");
+ JSDOT(NAME, answers[i].name);
+ JU(TTL, ntohl(answers[i].resource->ttl));
+ answers[i].rdata = ReadName(
+ result,wire_size,
+ reader-result,&stop);
+ JSDOT( MNAME, answers[i].rdata);
+ reader = reader + stop;
+ free(answers[i].rdata);
+ answers[i].rdata = ReadName(
+ result,wire_size,
+ reader-result,&stop);
+ JSDOT( RNAME, answers[i].rdata);
+ reader = reader + stop;
+ serial = get32b(reader);
+ JU_NC(SERIAL, serial);
+ reader = reader + 4;
+ reader = reader + 16; // skip REFRESH, RETRY, EXIPIRE, and MINIMUM
+ if(flagAnswer == 0)
+ fprintf(fh, " } ");
+ }
+ else
+ {
+ // JU(TYPE, ntohs(answers[i].resource->type));
+ // JU_NC(RDLENGTH, ntohs(answers[i].resource->data_len))
+ reader = reader + ntohs(answers[i].resource->data_len);
+ }
+
+ fflush(fh);
+ // free mem
+ if(answers[i].rdata != NULL)
+ free (answers[i].rdata);
+ }
+ if(flagAnswer == 0)
+ fprintf (fh, " ]");
+ }
+
+ for(i=0;i<iMax;i++)
+ {
+ free(answers[i].name);
+ }
+
+ fprintf (fh , " }"); //result
+ }
+ if(qry->err.size)
+ {
+ line[0] = '\0';
+ buf_add(&qry->err, line, 1 );
+ fprintf(fh, ", \"error\" : { %s }" , qry->err.buf);
+ }
+ fprintf(fh, " }");
+ fprintf(fh, "\n");
+ if (qry->out_filename)
+ fclose(fh);
+ free_qry_inst(qry);
+}
+
+unsigned char* ReadName(unsigned char *base, size_t size, size_t offset,
+ int* count)
+{
+ unsigned char *name;
+ unsigned int p=0,jumped=0, len;
+
+ *count = 0;
+ name = (unsigned char*)malloc(256);
+
+ name[0]= '\0';
+
+ //read the names in 3www6google3com format
+ while(len= base[offset], len !=0)
+ {
+ if (len & 0xc0)
+ {
+ if ((len & 0xc0) != 0xc0)
+ {
+ /* Bad format */
+ strcpy(name, "format-error");
+ printf("format-error: len = %d\n",
+ len);
+ abort();
+ return name;
+ }
+
+ offset= ((len & ~0xc0) << 8) | base[offset+1];
+ if (offset >= size)
+ {
+ strcpy(name, "offset-error");
+ printf("offset-error\n");
+ abort();
+ return name;
+ }
+ if(jumped==0)
+ {
+ /* if we havent jumped to another location
+ * then we can count up
+ */
+ *count += 2;
+ }
+ jumped= 1;
+ continue;
+ }
+ if (offset+len+1 > size)
+ {
+ strcpy(name, "buf-bounds-error");
+ printf("buf-bounds-error\n");
+ abort();
+ return name;
+ }
+
+ if (p+len+1 > 255)
+ {
+ strcpy(name, "name-length-error");
+ printf("name-length-error\n");
+ abort();
+ return name;
+ }
+ memcpy(name+p, base+offset+1, len);
+ name[p+len]= '.';
+ p += len+1;
+ offset += len+1;
+
+ if(jumped==0)
+ {
+ /* if we havent jumped to another location then we
+ * can count up
+ */
+ *count += len+1;
+ }
+ }
+
+ if (!jumped)
+ (*count)++; /* Terminating zero length */
+
+ name[p]= '\0'; //string complete
+
+ if(p > 0)
+ name[p-1]= '\0'; //remove the last dot
+ return name;
+}
+
+/* get 4 bytes from memory
+ * eg. used to extract serial number from soa packet
+ */
+ u_int32_t
+get32b (char *p)
+{
+ u_int32_t var;
+
+ var = (0x000000ff & *(p)) << 24;
+ var |= (0x000000ff & *(p+1)) << 16;
+ var |= (0x000000ff & *(p+2)) << 8;
+ var |= (0x000000ff & *(p+3));
+
+ return (var);
+}
+
+/*
+ * Copy data allowing for unaligned accesses in network byte order
+ * (big endian).
+ */
+void ldns_write_uint16(void *dst, uint16_t data)
+{
+#ifdef ALLOW_UNALIGNED_ACCESSES
+ * (uint16_t *) dst = htons(data);
+#else
+ uint8_t *p = (uint8_t *) dst;
+ p[0] = (uint8_t) ((data >> 8) & 0xff);
+ p[1] = (uint8_t) (data & 0xff);
+#endif
+}
+
+uint16_t
+ldns_read_uint16(const void *src)
+{
+#ifdef ALLOW_UNALIGNED_ACCESSES
+ return ntohs(*(uint16_t *) src);
+#else
+ uint8_t *p = (uint8_t *) src;
+ return ((uint16_t) p[0] << 8) | (uint16_t) p[1];
+#endif
+}
+
+struct testops tdig_ops = { tdig_init, tdig_start, tdig_delete };
diff --git a/eperd/evtraceroute.c b/eperd/evtraceroute.c
new file mode 100644
index 0000000..d5e0d36
--- /dev/null
+++ b/eperd/evtraceroute.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Standalone version of the event-based traceroute.
+ */
+
+#include "libbb.h"
+#include <syslog.h>
+#include <event2/event.h>
+#include <event2/event_struct.h>
+
+#include "eperd.h"
+
+static void done(void *state UNUSED_PARAM)
+{
+ fprintf(stderr, "And we are done\n");
+ exit(0);
+}
+
+int evtraceroute_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int evtraceroute_main(int argc UNUSED_PARAM, char **argv)
+{
+ int r;
+ void *state;
+
+ /* Create libevent event base */
+ EventBase= event_base_new();
+ if (!EventBase)
+ {
+ fprintf(stderr, "evtraceroute: event_base_new failed\n");
+ exit(1);
+ }
+ DnsBase= evdns_base_new(EventBase, 1 /*initialize*/);
+ if (!DnsBase)
+ {
+ fprintf(stderr, "evdns_base_new failed\n");
+ exit(1);
+ }
+
+ state= traceroute_ops.init(argc, argv, done);
+ if (!state)
+ {
+ fprintf(stderr, "evtraceroute: traceroute_ops.init failed\n");
+ exit(1);
+ }
+ traceroute_ops.start(state);
+
+ r= event_base_loop(EventBase, 0);
+ if (r != 0)
+ {
+ fprintf(stderr, "evtraceroute: event_base_loop failed\n");
+ exit(1);
+ }
+ return 0; /* not reached */
+}
+
diff --git a/eperd/httpget.c b/eperd/httpget.c
new file mode 100644
index 0000000..c380f18
--- /dev/null
+++ b/eperd/httpget.c
@@ -0,0 +1,1760 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * httpget.c -- libevent-based version of httpget
+ */
+
+#include "libbb.h"
+#include <assert.h>
+#include <getopt.h>
+#include <event2/buffer.h>
+#include <event2/bufferevent.h>
+#include <event2/dns.h>
+#include <event2/event.h>
+#include <event2/event_struct.h>
+
+#include "eperd.h"
+#include "tcputil.h"
+
+#define SAFE_PREFIX_IN ATLAS_DATA_OUT
+#define SAFE_PREFIX_OUT ATLAS_DATA_NEW
+
+#define CONN_TO 5
+
+#define ENV2STATE(env) \
+ ((struct hgstate *)((char *)env - offsetof(struct hgstate, tu_env)))
+
+#define DBQ(str) "\"" #str "\""
+
+#define MAX_LINE_LEN 2048 /* We don't deal with lines longer than this */
+#define POST_BUF_SIZE 2048 /* Big enough to be efficient? */
+
+static struct option longopts[]=
+{
+ { "all", no_argument, NULL, 'a' },
+ { "combine", no_argument, NULL, 'c' },
+ { "get", no_argument, NULL, 'g' },
+ { "head", no_argument, NULL, 'E' },
+ { "post", no_argument, NULL, 'P' },
+ { "post-file", required_argument, NULL, 'p' },
+ { "post-header", required_argument, NULL, 'h' },
+ { "post-footer", required_argument, NULL, 'f' },
+ { "store-headers", required_argument, NULL, 'H' },
+ { "store-body", required_argument, NULL, 'B' },
+ { "user-agent", required_argument, NULL, 'u' },
+ { NULL, }
+};
+
+enum readstate { READ_STATUS, READ_HEADER, READ_BODY, READ_SIMPLE,
+ READ_CHUNKED, READ_CHUNK_BODY, READ_CHUNK_END, READ_CHUNKED_TRAILER,
+ READ_DONE };
+enum writestate { WRITE_HEADER, WRITE_POST_HEADER, WRITE_POST_FILE,
+ WRITE_POST_FOOTER, WRITE_DONE };
+
+struct hgbase
+{
+ struct event_base *event_base;
+
+ struct hgstate **table;
+ int tabsiz;
+
+ /* For standalone httpget. Called when a httpget instance is
+ * done. Just one pointer for all instances. It is up to the caller
+ * to keep it consistent.
+ */
+ void (*done)(void *state);
+};
+
+struct hgstate
+{
+ /* Parameters */
+ char *output_file;
+ char *atlas;
+ char do_all;
+ char do_combine;
+ char only_v4;
+ char only_v6;
+ char do_get;
+ char do_head;
+ char do_post;
+ char do_http10;
+ char *user_agent;
+ char *post_header;
+ char *post_file;
+ char *post_footer;
+ int max_headers;
+ int max_body;
+
+ /* State */
+ char busy;
+ struct tu_env tu_env;
+ char dnserr;
+ char connecting;
+ char *host;
+ char *port;
+ char *hostport;
+ char *path;
+ struct bufferevent *bev;
+ enum readstate readstate;
+ enum writestate writestate;
+ int http_result;
+ char res_major;
+ char res_minor;
+ int headers_size;
+ int tot_headers;
+ int chunked;
+ int tot_chunked;
+ int content_length;
+ int content_offset;
+ int subid;
+ int submax;
+ time_t gstart;
+ struct timeval start;
+ double resptime;
+ FILE *post_fh;
+ char *post_buf;
+
+ char *line;
+ size_t linemax; /* Allocated size of line */
+ size_t linelen; /* Current amount of data in line */
+ size_t lineoffset; /* Offset in line where to start processing */
+
+ /* Base and index in table */
+ struct hgbase *base;
+ int index;
+
+ struct sockaddr_in6 sin6;
+ socklen_t socklen;
+ struct sockaddr_in6 loc_sin6;
+ socklen_t loc_socklen;
+
+ char *result;
+ size_t reslen;
+ size_t resmax;
+};
+
+static struct hgbase *hg_base;
+
+static void report(struct hgstate *state);
+static void add_str(struct hgstate *state, const char *str);
+static void add_str_quoted(struct hgstate *state, char *str);
+
+static struct hgbase *httpget_base_new(struct event_base *event_base)
+{
+ struct hgbase *base;
+
+ base= xzalloc(sizeof(*base));
+
+ base->event_base= event_base;
+
+ base->tabsiz= 10;
+ base->table= xzalloc(base->tabsiz * sizeof(*base->table));
+
+ return base;
+}
+
+static int parse_url(char *url, char **hostp, char **portp, char **hostportp,
+ char **pathp)
+{
+ char *item;
+ const char *cp, *np, *prefix;
+ size_t len;
+
+ *hostp= NULL;
+ *portp= NULL;
+ *hostportp= NULL;
+ *pathp= NULL;
+
+ /* the url must start with 'http://' */
+ prefix= "http://";
+ len= strlen(prefix);
+ if (strncasecmp(prefix, url, len) != 0)
+ {
+ crondlog(LVL8 "bad prefix in url '%s'", url);
+ goto fail;
+ }
+
+ cp= url+len;
+
+ /* Get hostport part */
+ np= strchr(cp, '/');
+ if (np != NULL)
+ len= np-cp;
+ else
+ {
+ len= strlen(cp);
+ np= cp+len;
+ }
+ if (len == 0)
+ {
+ crondlog(LVL8 "missing host part in url '%s'", url);
+ return 0;
+ }
+ item= xmalloc(len+1);
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ *hostportp= item;
+
+ /* The remainder is the path */
+ cp= np;
+ if (cp[0] == '\0')
+ cp= "/";
+ len= strlen(cp);
+ item= xmalloc(len+1);
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ *pathp= item;
+
+ /* Extract the host name from hostport */
+ cp= *hostportp;
+ np= cp;
+ if (cp[0] == '[')
+ {
+ /* IPv6 address literal */
+ np= strchr(cp, ']');
+ if (np == NULL || np == cp+1)
+ {
+ crondlog(LVL8
+ "malformed IPv6 address literal in url '%s'",
+ url);
+ goto fail;
+ }
+ }
+
+ np= strchr(np, ':');
+ if (np != NULL)
+ len= np-cp;
+ else
+ {
+ len= strlen(cp);
+ np= cp+len;
+ }
+ if (len == 0)
+ {
+ crondlog(LVL8 "missing host part in url '%s'", url);
+ goto fail;
+ }
+ item= xmalloc(len+1);
+ if (cp[0] == '[')
+ {
+ /* Leave out the square brackets */
+ memcpy(item, cp+1, len-2);
+ item[len-2]= '\0';
+ }
+ else
+ {
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ }
+ *hostp= item;
+
+ /* Port */
+ cp= np;
+ if (cp[0] == '\0')
+ cp= "80";
+ else
+ cp++;
+ len= strlen(cp);
+ item= xmalloc(len+1);
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ *portp= item;
+
+ return 1;
+
+fail:
+ if (*hostp)
+ {
+ free(*hostp);
+ *hostp= NULL;
+ }
+ if (*portp)
+ {
+ free(*portp);
+ *portp= NULL;
+ }
+ if (*hostportp)
+ {
+ free(*hostportp);
+ *hostportp= NULL;
+ }
+ if (*pathp)
+ {
+ free(*pathp);
+ *pathp= NULL;
+ }
+ return 0;
+}
+
+static void timeout_callback(int __attribute((unused)) unused,
+ const short __attribute((unused)) event, void *s)
+{
+ struct hgstate *state;
+
+ state= ENV2STATE(s);
+
+ if (state->connecting)
+ {
+ add_str(state, DBQ(err) ":" DBQ(connect: timeout) ", ");
+ if (state->do_all)
+ report(state);
+ else
+ tu_restart_connect(&state->tu_env);
+ return;
+ }
+ switch(state->readstate)
+ {
+ case READ_STATUS:
+ add_str(state, DBQ(err) ":" DBQ(timeout reading status) ", ");
+ report(state);
+ break;
+ case READ_HEADER:
+ if (state->max_headers)
+ add_str(s, " ], ");
+ add_str(state, ", " DBQ(err) ":" DBQ(timeout reading headers));
+ report(state);
+ break;
+ case READ_SIMPLE:
+#if 0 /* Enable when adding storing bodies */
+ if (state->max_body)
+ add_str(s, " ]");
+#endif
+ add_str(state, DBQ(err) ":" DBQ(timeout reading body) ", ");
+ report(state);
+ break;
+ case READ_CHUNKED:
+ case READ_CHUNK_BODY:
+#if 0 /* Enable when adding storing bodies */
+ if (state->max_body)
+ add_str(s, " ]");
+#endif
+ add_str(state, DBQ(err) ":" DBQ(timeout reading chunk) ", ");
+ report(state);
+ break;
+ default:
+ printf("in timeout_callback, unhandled cased: %d\n",
+ state->readstate);
+ }
+}
+
+static void *httpget_init(int __attribute((unused)) argc, char *argv[],
+ void (*done)(void *state))
+{
+ int c, i, do_combine, do_get, do_head, do_post,
+ max_headers, max_body, only_v4, only_v6,
+ do_all, do_http10;
+ size_t newsiz;
+ char *url, *check;
+ char *post_file, *output_file, *post_footer, *post_header,
+ *A_arg, *store_headers, *store_body;
+ const char *user_agent;
+ char *host, *port, *hostport, *path;
+ struct hgstate *state;
+ FILE *fh;
+
+ /* Arguments */
+ do_http10= 0;
+ do_all= 0;
+ do_combine= 0;
+ do_get= 1;
+ do_head= 0;
+ do_post= 0;
+ post_file= NULL;
+ post_footer=NULL;
+ post_header=NULL;
+ output_file= NULL;
+ store_headers= NULL;
+ store_body= NULL;
+ A_arg= NULL;
+ only_v4= 0;
+ only_v6= 0;
+ user_agent= "httpget for atlas.ripe.net";
+
+ if (!hg_base)
+ {
+ hg_base= httpget_base_new(EventBase);
+ if (!hg_base)
+ crondlog(DIE9 "httpget_base_new failed");
+ }
+
+
+ /* Allow us to be called directly by another program in busybox */
+ optind= 0;
+ while (c= getopt_long(argc, argv, "01aA:cO:46", longopts, NULL), c != -1)
+ {
+ switch(c)
+ {
+ case '0':
+ do_http10= 1;
+ break;
+ case '1':
+ do_http10= 0;
+ break;
+ case 'a': /* --all */
+ do_all= 1;
+ break;
+ case 'A':
+ A_arg= optarg;
+ break;
+ case 'c': /* --combine */
+ do_combine= 1;
+ break;
+ case 'O':
+ output_file= optarg;
+ break;
+ case 'g': /* --get */
+ do_get = 1;
+ do_head = 0;
+ do_post = 0;
+ break;
+ case 'E': /* --head */
+ do_get = 0;
+ do_head = 1;
+ do_post = 0;
+ break;
+ case 'P': /* --post */
+ do_get = 0;
+ do_head = 0;
+ do_post = 1;
+ break;
+ case 'h': /* --post-header */
+ post_header= optarg;
+ break;
+ case 'f': /* --post-footer */
+ post_footer= optarg;
+ break;
+ case 'p': /* --post-file */
+ post_file= optarg;
+ break;
+ case 'H': /* --store-headers */
+ store_headers= optarg;
+ break;
+ case 'B': /* --store-body */
+ store_body= optarg;
+ break;
+ case '4':
+ only_v4= 1;
+ only_v6= 0;
+ break;
+ case '6':
+ only_v6= 1;
+ only_v4= 0;
+ break;
+ case 'u': /* --user-agent */
+ user_agent= optarg;
+ break;
+ default:
+ crondlog(LVL8 "bad option '%c'", c);
+ return NULL;
+ }
+ }
+
+ if (optind != argc-1)
+ {
+ crondlog(LVL8 "exactly one url expected");
+ return NULL;
+ }
+ url= argv[optind];
+
+ if (output_file)
+ {
+ if (!validate_filename(output_file, SAFE_PREFIX_OUT))
+ {
+ crondlog(LVL8 "insecure file '%s'", output_file);
+ return NULL;
+ }
+ fh= fopen(output_file, "a");
+ if (!fh)
+ {
+ crondlog(LVL8 "unable to append to '%s'",
+ output_file);
+ return NULL;
+ }
+ fclose(fh);
+ }
+ if (post_header && !validate_filename(post_header, SAFE_PREFIX_IN))
+ {
+ crondlog(LVL8 "insecure file '%s'", post_header);
+ return NULL;
+ }
+ if (post_file && !validate_filename(post_file, SAFE_PREFIX_IN))
+ {
+ crondlog(LVL8 "insecure file '%s'", post_file);
+ return NULL;
+ }
+ if (post_footer && !validate_filename(post_footer, SAFE_PREFIX_IN))
+ {
+ crondlog(LVL8 "insecure file '%s'", post_footer);
+ return NULL;
+ }
+
+ max_headers= 0;
+ max_body= UINT_MAX; /* default is to write out the entire body */
+
+ if (store_headers)
+ {
+ max_headers= strtoul(store_headers, &check, 10);
+ if (check[0] != '\0')
+ {
+ crondlog(LVL8
+ "unable to parse argument (--store-headers) '%s'",
+ store_headers);
+ return NULL;
+ }
+ }
+
+ if (store_body)
+ {
+ max_body= strtoul(store_body, &check, 10);
+ if (check[0] != '\0')
+ {
+ crondlog(LVL8
+ "unable to parse argument (--store-body) '%s'",
+ store_body);
+ return NULL;
+ }
+ }
+
+ if (!parse_url(url, &host, &port, &hostport, &path))
+ {
+ /* Do we need to report an error? */
+ return NULL;
+ }
+
+ //printf("host: %s\n", host);
+ //printf("port: %s\n", port);
+ //printf("hostport: %s\n", hostport);
+ //printf("path: %s\n", path);
+
+ state= xzalloc(sizeof(*state));
+ state->base= hg_base;
+ state->atlas= A_arg ? strdup(A_arg) : NULL;
+ state->output_file= output_file ? strdup(output_file) : NULL;
+ state->host= host;
+ state->port= port;
+ state->hostport= hostport;
+ state->path= path;
+ state->do_all= do_all;
+ state->do_combine= !!do_combine;
+ state->do_get= do_get;
+ state->do_head= do_head;
+ state->do_post= do_post;
+ state->post_header= post_header ? strdup(post_header) : NULL;
+ state->post_file= post_file ? strdup(post_file) : NULL;
+ state->post_footer= post_footer ? strdup(post_footer) : NULL;
+ state->do_http10= do_http10;
+ state->user_agent= user_agent ? strdup(user_agent) : NULL;
+ state->max_headers= max_headers;
+ state->max_body= max_body;
+
+ state->only_v4= 2;
+
+ state->only_v4= !!only_v4; /* Gcc bug? */
+ state->only_v6= !!only_v6;
+
+ //evtimer_assign(&state->timer, state->base->event_base,
+ // timeout_callback, state);
+
+ state->line= NULL;
+ state->linemax= 0;
+ state->linelen= 0;
+ state->lineoffset= 0;
+
+ for (i= 0; i<hg_base->tabsiz; i++)
+ {
+ if (hg_base->table[i] == NULL)
+ break;
+ }
+ if (i >= hg_base->tabsiz)
+ {
+ newsiz= 2*hg_base->tabsiz;
+ hg_base->table= xrealloc(hg_base->table,
+ newsiz*sizeof(*hg_base->table));
+ for (i= hg_base->tabsiz; i<newsiz; i++)
+ hg_base->table[i]= NULL;
+ i= hg_base->tabsiz;
+ hg_base->tabsiz= newsiz;
+ }
+ state->index= i;
+ hg_base->table[i]= state;
+ hg_base->done= done;
+
+ return state;
+}
+
+static void report(struct hgstate *state)
+{
+ int done, do_output;
+ FILE *fh;
+ char namebuf[NI_MAXHOST];
+ char line[160];
+
+ //event_del(&state->timer);
+
+ state->subid++;
+
+ do_output= 1;
+ if (state->do_all && state->do_combine && state->subid<state->submax)
+ {
+ do_output= 0;
+ }
+
+ fh= NULL;
+ if (do_output)
+ {
+ if (state->output_file)
+ {
+ fh= fopen(state->output_file, "a");
+ if (!fh)
+ crondlog(DIE9 "unable to append to '%s'",
+ state->output_file);
+ }
+ else
+ fh= stdout;
+
+ fprintf(fh, "RESULT { ");
+ if (state->atlas)
+ {
+ fprintf(fh, DBQ(id) ":" DBQ(%s) ", "
+ DBQ(fw) ":%d, "
+ DBQ(time) ":%ld, ",
+ state->atlas, get_atlas_fw_version(),
+ state->gstart);
+ }
+ fprintf(fh, DBQ(result) ":[ ");
+ }
+
+ if (state->do_all && !state->dnserr)
+ {
+ if (state->do_combine)
+ {
+ snprintf(line, sizeof(line), DBQ(time) ":%ld, ",
+ state->start.tv_sec);
+ }
+ else
+ {
+ snprintf(line, sizeof(line), DBQ(subid) ":%d, "
+ DBQ(submax) ":%d, ",
+ state->subid, state->submax);
+ }
+ add_str(state, line);
+ }
+
+ if (!state->dnserr)
+ {
+ snprintf(line, sizeof(line),
+ DBQ(method) ":" DBQ(%s) ", " DBQ(af) ": %d",
+ state->do_get ? "GET" : state->do_head ? "HEAD" :
+ "POST",
+ state->sin6.sin6_family == AF_INET6 ? 6 : 4);
+ add_str(state, line);
+
+ getnameinfo((struct sockaddr *)&state->sin6, state->socklen,
+ namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST);
+
+ snprintf(line, sizeof(line), ", " DBQ(dst_addr) ":" DBQ(%s),
+ namebuf);
+ add_str(state, line);
+ }
+
+ if (!state->connecting && !state->dnserr)
+ {
+ namebuf[0]= '\0';
+ getnameinfo((struct sockaddr *)&state->loc_sin6,
+ state->loc_socklen, namebuf, sizeof(namebuf),
+ NULL, 0, NI_NUMERICHOST);
+
+ snprintf(line, sizeof(line), ", " DBQ(src_addr) ":" DBQ(%s),
+ namebuf);
+ add_str(state, line);
+ }
+
+ done= (state->readstate == READ_DONE);
+ if (done)
+ {
+ snprintf(line, sizeof(line),
+ ", " DBQ(rt) ":%f"
+ ", " DBQ(res) ":%d"
+ ", " DBQ(ver) ":" DBQ(%d.%d)
+ ", " DBQ(hsize) ":%d"
+ ", " DBQ(bsize) ":%d",
+ state->resptime,
+ state->http_result,
+ state->res_major, state->res_minor,
+ state->headers_size,
+ state->content_offset);
+ add_str(state, line);
+ }
+
+ if (!state->dnserr)
+ {
+ add_str(state, " }");
+ }
+ if (!do_output)
+ add_str(state, ", ");
+ else
+ add_str(state, " ]");
+
+ if (do_output)
+ {
+ fprintf(fh, "%s }\n", state->result);
+ free(state->result);
+ state->result= NULL;
+ state->resmax= 0;
+ state->reslen= 0;
+
+ if (state->output_file)
+ fclose(fh);
+ }
+
+ free(state->post_buf);
+ state->post_buf= NULL;
+
+ if (state->do_all && state->subid < state->submax)
+ {
+ tu_restart_connect(&state->tu_env);
+ return;
+ }
+ if (state->linemax)
+ {
+ state->linemax= 0;
+ free(state->line);
+ state->line= NULL;
+ }
+
+ state->bev= NULL;
+
+ tu_cleanup(&state->tu_env);
+
+ if (state->base->done)
+ state->base->done(state);
+ state->busy= 0;
+}
+
+static int get_input(struct hgstate *state)
+{
+ int n;
+
+ /* Assume that we always end up with a full buffer anyway */
+ if (state->linemax == 0)
+ {
+ if (state->line)
+ crondlog(DIE9 "line is not empty");
+
+ state->linemax= MAX_LINE_LEN;
+ state->line= xmalloc(state->linemax);
+ }
+
+ if (state->lineoffset)
+ {
+ if (state->linelen > state->lineoffset)
+ {
+ memmove(state->line, &state->line[state->lineoffset],
+ state->linelen-state->lineoffset);
+ state->linelen -= state->lineoffset;
+ }
+ else
+ {
+ state->linelen= 0;
+ }
+ state->lineoffset= 0;
+ }
+ if (state->linelen >= state->linemax)
+ {
+ return -1; /* We cannot get more data */
+ }
+
+ n= bufferevent_read(state->bev,
+ &state->line[state->linelen],
+ state->linemax-state->linelen);
+ if (n < 0)
+ return -1;
+ state->linelen += n;
+ return 0;
+}
+
+static void skip_spaces(const char *cp, char **ncp)
+{
+ const unsigned char *ucp;
+
+ ucp= (const unsigned char *)cp;
+ while (ucp[0] != '\0' && isspace(ucp[0]))
+ ucp++;
+ *ncp= (char *)ucp;
+}
+
+static void add_str(struct hgstate *state, const char *str)
+{
+ size_t len;
+
+ len= strlen(str);
+ if (state->reslen + len+1 > state->resmax)
+ {
+ state->resmax= state->reslen + len+1 + 80;
+ state->result= xrealloc(state->result, state->resmax);
+ }
+ memcpy(state->result+state->reslen, str, len+1);
+ state->reslen += len;
+ //printf("add_str: result = '%s'\n", state->result);
+}
+
+static void add_str_quoted(struct hgstate *state, char *str)
+{
+ char c;
+ char *p;
+ char buf[20];
+
+ for (p= str; *p; p++)
+ {
+ c= *p;
+ if (c == '"' || c == '\\')
+ snprintf(buf, sizeof(buf), "\\%c", c);
+ else if (isprint((unsigned char)c))
+ {
+ buf[0]= c;
+ buf[1]= '\0';
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf), "\\u%04x",
+ (unsigned char)c);
+ }
+ add_str(state, buf);
+ }
+}
+
+static void err_status(struct hgstate *state, const char *reason)
+{
+ char line[80];
+ snprintf(line, sizeof(line),
+ DBQ(err) ":" DBQ(bad status line: %s) ", ",
+ reason);
+ add_str(state, line);
+ report(state);
+}
+
+static void err_header(struct hgstate *state, const char *reason)
+{
+ char line[80];
+ if (state->max_headers != 0)
+ add_str(state, " ], ");
+ snprintf(line, sizeof(line),
+ DBQ(err) ":" DBQ(bad header line: %s) ", ", reason);
+ add_str(state, line);
+ report(state);
+}
+
+static void err_chunked(struct hgstate *state, const char *reason)
+{
+ char line[80];
+ snprintf(line, sizeof(line), DBQ(err) ":" DBQ(bad chunk line: %s) ", ",
+ reason);
+ add_str(state, line);
+ report(state);
+}
+
+static void readcb(struct bufferevent *bev UNUSED_PARAM, void *ptr)
+{
+ int r, major, minor, need_line, no_body;
+ size_t len;
+ char *cp, *ncp, *check, *line;
+ const char *prefix, *kw;
+ struct hgstate *state;
+ struct timeval endtime;
+
+ state= ENV2STATE(ptr);
+
+ for (;;)
+ {
+ switch(state->readstate)
+ {
+ case READ_STATUS:
+ case READ_HEADER:
+ case READ_CHUNKED:
+ case READ_CHUNK_END:
+ case READ_CHUNKED_TRAILER:
+ need_line= 1;
+ break;
+ default:
+ need_line= 0;
+ break;
+ }
+
+ if (need_line)
+ {
+ /* Wait for a complete line */
+ if (state->linemax == 0 ||
+ memchr(&state->line[state->lineoffset], '\n',
+ state->linelen-state->lineoffset) == NULL)
+ {
+ r= get_input(state);
+ if (r == -1)
+ {
+ printf(
+ "readcb: get_input failed, should do something\n");
+ return;
+ }
+
+ /* Did we get what we want? */
+ if (memchr(&state->line[state->lineoffset],
+ '\n', state->linelen-state->lineoffset)
+ == NULL)
+ {
+ /* No */
+ if (state->linelen-state->lineoffset >=
+ MAX_LINE_LEN)
+ {
+ add_str(state, DBQ(err) ":"
+ DBQ(line too long)
+ ", ");
+ report(state);
+ }
+ return;
+ }
+ }
+ }
+
+ switch(state->readstate)
+ {
+ case READ_STATUS:
+ line= &state->line[state->lineoffset];
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ /* Contains nul */
+ err_status(state, "contains nul");
+ return;
+ }
+
+ state->lineoffset += (cp-line+1);
+
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ /* Check http version */
+ prefix= "http/";
+ len= strlen(prefix);
+ if (strncasecmp(prefix, line, len) != 0)
+ {
+ err_status(state, "bad prefix");
+ return;
+ }
+ cp= line+len;
+
+ major= strtoul(cp, &check, 10);
+ if (check == cp || check[0] != '.')
+ {
+ err_status(state, "bad major");
+ return;
+ }
+
+ cp= check+1;
+ minor= strtoul(cp, &check, 10);
+ if (check == cp || check[0] == '\0' ||
+ !isspace(*(unsigned char *)check))
+ {
+ err_status(state, "bad minor");
+ return;
+ }
+
+ skip_spaces(check, &cp);
+
+ if (!isdigit(*(unsigned char *)cp))
+ {
+ err_status(state, "bad status code");
+ return;
+ }
+ state->http_result= strtoul(cp, NULL, 10);
+ state->res_major= major;
+ state->res_minor= minor;
+
+ state->readstate= READ_HEADER;
+ state->content_length= -1;
+
+ if (state->max_headers)
+ {
+ add_str(state, DBQ(header) ": [");
+ }
+
+ continue;
+
+ case READ_HEADER:
+ line= &state->line[state->lineoffset];
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ err_header(state, "contains nul");
+ return;
+ }
+
+ len= (cp-line+1);
+ state->lineoffset += len;
+
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ if (line[0] == '\0')
+ {
+ if (state->tot_headers <= state->max_headers &&
+ state->max_headers != 0)
+ {
+ if (state->tot_headers != 0)
+ add_str(state, ",");
+ add_str(state, " \"\"");
+ }
+ if (state->max_headers)
+ add_str(state, " ], ");
+ state->readstate= READ_BODY;
+ continue;
+ }
+
+ state->headers_size += len;
+
+ len= strlen(line);
+ if (state->tot_headers+len+1 <= state->max_headers)
+ {
+ if (state->tot_headers != 0)
+ add_str(state, ",");
+ add_str(state, " \"");
+ add_str_quoted(state, line);
+ add_str(state, "\"");
+ state->tot_headers += len;
+ } else if (state->tot_headers <= state->max_headers &&
+ state->max_headers != 0)
+ {
+ /* Fill up remaining space and report
+ * truncation */
+ if (state->tot_headers != 0)
+ add_str(state, ",");
+ add_str(state, " \"");
+ if (state->tot_headers < state->max_headers)
+ {
+ line[state->max_headers-
+ state->tot_headers]= '\0';
+ add_str_quoted(state, line);
+ }
+ add_str(state, "[...]\"");
+
+ state->tot_headers += len+1;
+ }
+
+ cp= line;
+ skip_spaces(cp, &ncp);
+ if (ncp != line)
+ continue; /* Continuation line */
+
+ cp= ncp;
+ while (ncp[0] != '\0' && ncp[0] != ':' &&
+ !isspace((unsigned char)ncp[0]))
+ {
+ ncp++;
+ }
+
+ kw= "Transfer-Encoding";
+ len= strlen(kw);
+ if (strncasecmp(cp, kw, len) == 0)
+ {
+ /* Skip optional white space */
+ cp= ncp;
+ skip_spaces(cp, &cp);
+
+ if (cp[0] != ':')
+ {
+ err_header(state,
+ "malformed transfer-encoding");
+ return;
+ }
+ cp++;
+
+ /* Skip more white space */
+ skip_spaces(cp, &cp);
+
+ /* Should have the value by now */
+ kw= "chunked";
+ len= strlen(kw);
+ if (strncasecmp(cp, kw, len) != 0)
+ continue;
+ /* make sure we have end of line or white
+ * space */
+ if (cp[len] != '\0' &&
+ isspace((unsigned char)cp[len]))
+ {
+ continue;
+ }
+ state->chunked= 1;
+ continue;
+ }
+
+ kw= "Content-length";
+ len= strlen(kw);
+ if (strncasecmp(cp, kw, len) != 0)
+ continue;
+
+ /* Skip optional white space */
+ cp= ncp;
+ skip_spaces(cp, &cp);
+
+ if (cp[0] != ':')
+ {
+ err_header(state,
+ "malformed content-length");
+ return;
+ }
+ cp++;
+
+ /* Skip more white space */
+ skip_spaces(cp, &cp);
+
+ /* Should have the value by now */
+ state->content_length= strtoul(cp, &check, 10);
+ if (check == cp)
+ {
+ err_header(state,
+ "malformed content-length");
+ return;
+ }
+
+ /* And after that we should have just white space */
+ cp= check;
+ skip_spaces(cp, &cp);
+
+ if (cp[0] != '\0')
+ {
+ err_header(state,
+ "malformed content-length");
+ return;
+ }
+ continue;
+
+ case READ_BODY:
+ no_body= (state->do_head || state->http_result == 204 ||
+ state->http_result == 304 ||
+ state->http_result/100 == 1);
+
+ if (no_body)
+ {
+ /* This reply will not have a body even if
+ * there is a content-length line.
+ */
+ state->readstate= READ_DONE;
+ }
+ else if (state->chunked)
+ state->readstate= READ_CHUNKED;
+ else
+ {
+ state->readstate= READ_SIMPLE;
+ state->content_offset= 0;
+ }
+
+ continue;
+
+ case READ_CHUNKED:
+ line= &state->line[state->lineoffset];
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ err_chunked(state, "contains nul");
+ return;
+ }
+
+ len= (cp-line+1);
+ state->lineoffset += len;
+
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ len= strtoul(line, &check, 16);
+ if (check == line || (check[0] != '\0' &&
+ !isspace(*(unsigned char *)check)))
+ {
+ err_chunked(state, "not a number");
+ return;
+ }
+
+ if (!len)
+ {
+ state->readstate= READ_CHUNKED_TRAILER;
+ continue;
+ }
+
+ state->tot_chunked += len;
+ state->readstate= READ_CHUNK_BODY;
+ continue;
+
+ case READ_CHUNK_BODY:
+ if (state->content_offset >= state->tot_chunked)
+ {
+ state->readstate= READ_CHUNK_END;
+ continue;
+ }
+
+ /* Do we need more input? */
+ if (state->linemax == 0 ||
+ state->lineoffset >= state->linelen)
+ {
+ r= get_input(state);
+ if (r == -1)
+ {
+ printf(
+ "readcb: get_input failed, should do something\n");
+ return;
+ }
+
+ /* Did we get what we want? */
+ if (state->lineoffset >= state->linelen)
+ {
+ /* No */
+ return;
+ }
+ }
+
+ len= state->linelen-state->lineoffset;
+ if (state->content_offset+len > state->tot_chunked)
+ len= state->tot_chunked-state->content_offset;
+
+ if (state->content_offset+len <= state->max_body)
+ {
+#if 0
+ printf(
+ "readcb: should report %ld bytes worth of content\n",
+ len);
+#endif
+ }
+ else if (state->content_offset <= state->max_body &&
+ state->max_body != 0)
+ {
+ /* Fill up remaining space and report
+ * truncation */
+ if (state->content_offset < state->max_body)
+ {
+ len= state->max_body -
+ state->content_offset;
+#if 0
+ printf(
+ "readcb: should report %ld bytes worth of content\n",
+ len);
+#endif
+
+ }
+ printf(
+ "readcb: should add truncation indicator\n");
+ }
+
+ state->content_offset += len;
+ state->lineoffset += len;
+
+ continue;
+
+ case READ_CHUNK_END:
+ line= &state->line[state->lineoffset];
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ err_chunked(state, "contains nul");
+ return;
+ }
+
+ len= (cp-line+1);
+ state->lineoffset += len;
+
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ if (strlen(line) != 0)
+ {
+ err_chunked(state,
+ "garbage at the end of chunk");
+ return;
+ }
+
+ state->readstate= READ_CHUNKED;
+ continue;
+
+ case READ_CHUNKED_TRAILER:
+ line= &state->line[state->lineoffset];
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ err_chunked(state, "contains nul");
+ return;
+ }
+
+ len= (cp-line+1);
+ state->lineoffset += len;
+
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ if (line[0] == '\0')
+ {
+ state->readstate= READ_DONE;
+ continue;
+ }
+ continue;
+
+ case READ_SIMPLE:
+ if (state->content_length >= 0 &&
+ state->content_offset >= state->content_length)
+ {
+ state->readstate= READ_DONE;
+ continue;
+ }
+
+ /* Do we need more input? */
+ if (state->linemax == 0 ||
+ state->lineoffset >= state->linelen)
+ {
+ r= get_input(state);
+ if (r == -1)
+ {
+ printf(
+ "readcb: get_input failed, should do something\n");
+ return;
+ }
+
+ /* Did we get what we want? */
+ if (state->lineoffset >= state->linelen)
+ {
+ /* No */
+ return;
+ }
+ }
+
+ len= state->linelen-state->lineoffset;
+ if (state->content_offset+len <= state->max_body)
+ {
+#if 0
+ printf(
+ "readcb: should report %ld bytes worth of content\n",
+ len);
+#endif
+ }
+ else if (state->content_offset <= state->max_body &&
+ state->max_body != 0)
+ {
+ /* Fill up remaining space and report
+ * truncation */
+ if (state->content_offset < state->max_body)
+ {
+ len= state->max_body -
+ state->content_offset;
+#if 0
+ printf(
+ "readcb: should report %ld bytes worth of content\n",
+ len);
+#endif
+
+ }
+ printf(
+ "readcb: should add truncation indicator\n");
+ }
+
+ state->content_offset += len;
+ state->lineoffset += len;
+
+ continue;
+
+ case READ_DONE:
+ if (state->bev)
+ {
+ state->bev= NULL;
+ gettimeofday(&endtime, NULL);
+ state->resptime=
+ (endtime.tv_sec-
+ state->start.tv_sec)*1e3 +
+ (endtime.tv_usec-
+ state->start.tv_usec)/1e3;
+ report(state);
+ }
+ return;
+ default:
+ printf("readcb: readstate = %d\n", state->readstate);
+ return;
+ }
+ }
+}
+
+static int post_file(struct hgstate *state, const char *filename)
+{
+ int r;
+ FILE *fh;
+
+ if (!state->post_fh)
+ {
+ fh= fopen(filename, "r");
+ if (fh == NULL)
+ {
+ printf("post_file: unable to open '%s': %s\n",
+ filename, strerror(errno));
+ return -1;
+ }
+ state->post_fh= fh;
+ }
+ if (!state->post_buf)
+ state->post_buf= xmalloc(POST_BUF_SIZE);
+ r= fread(state->post_buf, 1, POST_BUF_SIZE, state->post_fh);
+ if (r == -1)
+ {
+ printf("post_file: error reading from '%s': %s\n",
+ filename, strerror(errno));
+ return -1;
+ }
+ if (r == 0)
+ {
+ fclose(state->post_fh);
+ state->post_fh= NULL;
+ return 1;
+ }
+ r= bufferevent_write(state->bev, state->post_buf, r);
+ if (r == -1)
+ {
+ printf("post_file: bufferevent_write failed\n");
+ }
+ return r;
+}
+
+static void writecb(struct bufferevent *bev, void *ptr)
+{
+ int r;
+ struct hgstate *state;
+ struct evbuffer *output;
+ off_t cLength;
+ struct stat sb;
+
+ state= ENV2STATE(ptr);
+
+ for(;;)
+ {
+ switch(state->writestate)
+ {
+ case WRITE_HEADER:
+ output= bufferevent_get_output(bev);
+ evbuffer_add_printf(output, "%s %s HTTP/1.%c\r\n",
+ state->do_get ? "GET" :
+ state->do_head ? "HEAD" : "POST", state->path,
+ state->do_http10 ? '0' : '1');
+ evbuffer_add_printf(output, "Host: %s\r\n",
+ state->host);
+ evbuffer_add_printf(output, "Connection: close\r\n");
+ evbuffer_add_printf(output, "User-Agent: %s\r\n",
+ state->user_agent);
+ if (state->do_post)
+ {
+ evbuffer_add_printf(output,
+ "Content-Type: application/x-www-form-urlencoded\r\n");
+ }
+
+ cLength= 0;
+ if (state->do_post)
+ {
+ if (state->post_header)
+ {
+ if (stat(state->post_header, &sb) == 0)
+ cLength += sb.st_size;
+ }
+ if (state->post_file)
+ {
+ if (stat(state->post_file, &sb) == 0)
+ cLength += sb.st_size;
+ }
+ if (state->post_footer)
+ {
+ if (stat(state->post_footer, &sb) == 0)
+ cLength += sb.st_size;
+ }
+ evbuffer_add_printf(output,
+ "Content-Length: %lu\r\n",
+ (unsigned long)cLength);
+ }
+
+ evbuffer_add_printf(output, "\r\n");
+ if (state->do_post)
+ state->writestate = WRITE_POST_HEADER;
+ else
+ state->writestate = WRITE_DONE;
+ return;
+ case WRITE_POST_HEADER:
+ if (!state->post_header)
+ {
+ state->writestate= WRITE_POST_FILE;
+ continue;
+ }
+ r= post_file(state, state->post_header);
+ if (r != 1)
+ return;
+
+ /* Done */
+ state->writestate= WRITE_POST_FILE;
+ continue;
+
+ case WRITE_POST_FILE:
+ if (!state->post_file)
+ {
+ state->writestate= WRITE_POST_FOOTER;
+ continue;
+ }
+ r= post_file(state, state->post_file);
+ if (r != 1)
+ return;
+
+ /* Done */
+ state->writestate= WRITE_POST_FOOTER;
+ continue;
+ case WRITE_POST_FOOTER:
+ if (!state->post_footer)
+ {
+ state->writestate= WRITE_DONE;
+ continue;
+ }
+ r= post_file(state, state->post_footer);
+ if (r != 1)
+ return;
+
+ /* Done */
+ state->writestate= WRITE_DONE;
+ continue;
+ case WRITE_DONE:
+ return;
+ default:
+ printf("writecb: unknown write state: %d\n",
+ state->writestate);
+ return;
+ }
+ }
+
+}
+
+static void err_reading(struct hgstate *state)
+{
+ struct timeval endtime;
+
+ switch(state->readstate)
+ {
+ case READ_STATUS:
+ add_str(state, ", " DBQ(err) ":" DBQ(error reading status));
+ report(state);
+ break;
+ case READ_HEADER:
+ if (state->max_headers)
+ add_str(state, " ], ");
+ add_str(state, DBQ(err) ":" DBQ(error reading headers) ", ");
+ report(state);
+ break;
+ case READ_SIMPLE:
+#if 0
+ if (state->max_body)
+ add_str(state, " ]");
+#endif
+ if (state->content_length == -1)
+ {
+ /* EOF is normal */
+ state->readstate= READ_DONE;
+ }
+ else
+ {
+ add_str(state, DBQ(err) ":" DBQ(error reading body)
+ ", ");
+ }
+ gettimeofday(&endtime, NULL);
+ state->resptime= (endtime.tv_sec-state->start.tv_sec)*1e3 +
+ (endtime.tv_usec-state->start.tv_usec)/1e3;
+ report(state);
+ break;
+ default:
+ printf("in err_reading, unhandled case\n");
+ }
+}
+
+static void dnscount(struct tu_env *env, int count)
+{
+ struct hgstate *state;
+
+ state= ENV2STATE(env);
+ state->subid= 0;
+ state->submax= count;
+}
+
+static void beforeconnect(struct tu_env *env,
+ struct sockaddr *addr, socklen_t addrlen)
+{
+ struct hgstate *state;
+
+ state= ENV2STATE(env);
+
+ state->socklen= addrlen;
+ memcpy(&state->sin6, addr, state->socklen);
+
+ state->connecting= 1;
+ state->readstate= READ_STATUS;
+ state->writestate= WRITE_HEADER;
+
+ state->linelen= 0;
+ state->lineoffset= 0;
+ state->headers_size= 0;
+ state->tot_headers= 0;
+
+ /* Clear result */
+ if (!state->do_all || !state->do_combine)
+ state->reslen= 0;
+
+ add_str(state, "{ ");
+
+ gettimeofday(&state->start, NULL);
+}
+
+
+static void reporterr(struct tu_env *env, enum tu_err cause,
+ const char *str)
+{
+ struct hgstate *state;
+ char line[80];
+
+ state= ENV2STATE(env);
+
+ if (env != &state->tu_env) abort();
+
+ switch(cause)
+ {
+ case TU_DNS_ERR:
+ snprintf(line, sizeof(line),
+ "{ " DBQ(dnserr) ":" DBQ(%s) " }", str);
+ add_str(state, line);
+ state->dnserr= 1;
+ report(state);
+ break;
+
+ case TU_READ_ERR:
+ err_reading(state);
+ break;
+
+ case TU_CONNECT_ERR:
+ snprintf(line, sizeof(line),
+ DBQ(err) ":" DBQ(connect: %s) ", ", str);
+ add_str(state, line);
+
+ if (state->do_all)
+ report(state);
+ else
+ tu_restart_connect(&state->tu_env);
+ break;
+
+ case TU_OUT_OF_ADDRS:
+ report(state);
+ break;
+
+ default:
+ crondlog(DIE9 "reporterr: bad cause %d", cause);
+ }
+}
+
+static void connected(struct tu_env *env, struct bufferevent *bev)
+{
+ struct hgstate *state;
+
+ state= ENV2STATE(env);
+
+ if (env != &state->tu_env) abort();
+
+ state->connecting= 0;
+ state->bev= bev;
+
+ state->loc_socklen= sizeof(state->loc_sin6);
+ getsockname(bufferevent_getfd(bev),
+ &state->loc_sin6, &state->loc_socklen);
+}
+
+static void httpget_start(void *state)
+{
+ struct hgstate *hgstate;
+ struct evutil_addrinfo hints;
+ struct timeval interval;
+
+ hgstate= state;
+
+ if (hgstate->busy)
+ {
+ printf("httget_start: busy\n");
+ return;
+ }
+ hgstate->busy= 1;
+
+ hgstate->dnserr= 0;
+ hgstate->connecting= 0;
+ hgstate->readstate= READ_STATUS;
+ hgstate->writestate= WRITE_HEADER;
+ hgstate->gstart= time(NULL);
+
+ memset(&hints, '\0', sizeof(hints));
+ hints.ai_socktype= SOCK_STREAM;
+ if (hgstate->only_v4)
+ hints.ai_family= AF_INET;
+ else if (hgstate->only_v6)
+ hints.ai_family= AF_INET6;
+ interval.tv_sec= CONN_TO;
+ interval.tv_usec= 0;
+ tu_connect_to_name(&hgstate->tu_env, hgstate->host, hgstate->port,
+ &interval, &hints, timeout_callback,
+ reporterr, dnscount, beforeconnect,
+ connected, readcb, writecb);
+}
+
+static int httpget_delete(void *state)
+{
+ int ind;
+ struct hgstate *hgstate;
+ struct hgbase *base;
+
+ hgstate= state;
+
+ printf("httpget_delete: state %p, index %d, busy %d\n",
+ state, hgstate->index, hgstate->busy);
+
+ if (hgstate->busy)
+ return 0;
+
+ if (hgstate->line)
+ crondlog(DIE9 "line is not empty");
+
+ base= hgstate->base;
+ ind= hgstate->index;
+
+ if (base->table[ind] != hgstate)
+ crondlog(DIE9 "strange, state not in table");
+ base->table[ind]= NULL;
+
+ //event_del(&hgstate->timer);
+
+ free(hgstate->atlas);
+ hgstate->atlas= NULL;
+ free(hgstate->output_file);
+ hgstate->output_file= NULL;
+ free(hgstate->host);
+ hgstate->host= NULL;
+ free(hgstate->hostport);
+ hgstate->hostport= NULL;
+ free(hgstate->port);
+ hgstate->port= NULL;
+ free(hgstate->path);
+ hgstate->path= NULL;
+ free(hgstate->user_agent);
+ hgstate->user_agent= NULL;
+ free(hgstate->post_header);
+ hgstate->post_header= NULL;
+ free(hgstate->post_file);
+ hgstate->post_file= NULL;
+ free(hgstate->post_footer);
+ hgstate->post_footer= NULL;
+
+ free(hgstate);
+
+ return 1;
+}
+
+struct testops httpget_ops = { httpget_init, httpget_start,
+ httpget_delete };
+
diff --git a/eperd/ping.c b/eperd/ping.c
new file mode 100644
index 0000000..639ce9c
--- /dev/null
+++ b/eperd/ping.c
@@ -0,0 +1,1288 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Copyright (c) 2009 Rocco Carbone
+ * This includes code Copyright (c) 2009 Rocco Carbone
+ * taken from the libevent-based ping.
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * ping.c
+ */
+
+#include "libbb.h"
+#include <event2/dns.h>
+#include <event2/event.h>
+#include <event2/event_struct.h>
+
+#include <netinet/in.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+
+#include "eperd.h"
+
+#define SAFE_PREFIX ATLAS_DATA_NEW
+
+#define DBQ(str) "\"" #str "\""
+
+#define PING_OPT_STRING ("!46rc:s:A:O:")
+
+enum
+{
+ opt_4 = (1 << 0),
+ opt_6 = (1 << 1),
+ opt_r = (1 << 2),
+};
+
+/* Intervals and timeouts (all are in milliseconds unless otherwise specified)
+ */
+#define DEFAULT_PING_INTERVAL 1000 /* 1 sec - 0 means flood mode */
+
+/* Max IP packet size is 65536 while fixed IP header size is 20;
+ * the traditional ping program transmits 56 bytes of data, so the
+ * default data size is calculated as to be like the original
+ */
+#define IPHDR 20
+#define MAX_DATA_SIZE (4096 - IPHDR - ICMP_MINLEN)
+
+/* Error codes */
+#define PING_ERR_NONE 0
+#define PING_ERR_TIMEOUT 1 /* Communication with the host timed out */
+#define PING_ERR_DUP 2 /* Duplicate packet */
+#define PING_ERR_DONE 3 /* Max number of packets to send has been
+ * reached.
+ */
+#define PING_ERR_SENDTO 4 /* Sendto system call failed */
+#define PING_ERR_DNS 5 /* DNS error */
+#define PING_ERR_DNS_NO_ADDR 6 /* DNS no suitable addresses */
+#define PING_ERR_SHUTDOWN 10 /* The request was canceled because the PING subsystem was shut down */
+#define PING_ERR_CANCEL 12 /* The request was canceled via a call to evping_cancel_request */
+#define PING_ERR_UNKNOWN 16 /* An unknown error occurred */
+
+
+/* Definition for various types of counters */
+typedef uint64_t counter_t;
+
+/* How to keep track of a PING session */
+struct pingbase
+{
+ struct event_base *event_base;
+
+ evutil_socket_t rawfd4; /* Raw socket used to ping hosts (IPv4)
+ */
+ evutil_socket_t rawfd6; /* Raw socket used to ping hosts (IPv6)
+ */
+
+ pid_t pid; /* Identifier to send with each ICMP
+ * Request */
+
+ struct timeval tv_interval; /* Ping interval between two subsequent
+ * pings */
+
+ /* A list of hosts to ping. */
+ struct pingstate **table;
+ int tabsiz;
+
+ struct event event4; /* Used to detect read events on raw
+ * socket */
+ struct event event6; /* Used to detect read events on raw
+ * socket */
+ void (*done)(void *state); /* Called when a ping is done */
+
+ u_char packet [MAX_DATA_SIZE];
+};
+
+struct pingstate
+{
+ /* Parameters */
+ char *atlas;
+ char *hostname;
+ int pingcount;
+ char *out_filename;
+ char delay_name_res;
+
+ /* State */
+ struct sockaddr_in6 sin6;
+ socklen_t socklen;
+ struct sockaddr_in6 loc_sin6;
+ socklen_t loc_socklen;
+ int busy;
+ char got_reply;
+ char first;
+ char no_dst;
+ unsigned char ttl;
+ unsigned size;
+
+ char *result;
+ size_t reslen;
+ size_t resmax;
+
+ struct pingbase *base;
+
+ sa_family_t af; /* Desired address family */
+ struct evutil_addrinfo *dns_res;
+ struct evutil_addrinfo *dns_curr;
+
+ size_t maxsize;
+
+ int maxpkts; /* Number of packets to send */
+
+ int index; /* Index into the array of hosts */
+ u_int8_t seq; /* ICMP sequence (modulo 256) for next
+ * run
+ */
+ u_int8_t rcvd_ttl; /* TTL in (last) reply packet */
+ char dnsip;
+ char send_error;
+
+ struct event ping_timer; /* Timer to ping host at given
+ * intervals
+ */
+
+ /* Packets Counters */
+ size_t cursize;
+ counter_t sentpkts; /* Total # of ICMP Echo Requests sent */
+};
+
+/* User Data added to the ICMP header
+ *
+ * The 'ts' is the time the request is sent on the wire
+ * and it is used to compute the network round-trip value.
+ *
+ * The 'index' parameter is an index value in the array of hosts to ping
+ * and it is used to relate each response with the corresponding request
+ */
+struct evdata {
+ struct timeval ts;
+ uint32_t index;
+};
+
+
+
+/* Initialize a struct timeval by converting milliseconds */
+static void
+msecstotv(time_t msecs, struct timeval *tv)
+{
+ tv->tv_sec = msecs / 1000;
+ tv->tv_usec = msecs % 1000 * 1000;
+}
+
+/* The time since 'tv' in microseconds */
+static time_t
+tvtousecs (struct timeval *tv)
+{
+ return tv->tv_sec * 1000000.0 + tv->tv_usec;
+}
+
+static void add_str(struct pingstate *state, const char *str)
+{
+ size_t len;
+
+ len= strlen(str);
+ if (state->reslen + len+1 > state->resmax)
+ {
+ state->resmax= state->reslen + len+1 + 80;
+ state->result= xrealloc(state->result, state->resmax);
+ }
+ memcpy(state->result+state->reslen, str, len+1);
+ state->reslen += len;
+ //printf("add_str: result = '%s'\n", state->result);
+}
+
+static int get_timesync(void)
+{
+ FILE *fh;
+ int lastsync;
+
+ fh= fopen(ATLAS_TIMESYNC_FILE, "r");
+ if (!fh)
+ return -1;
+ fscanf(fh, "%d", &lastsync);
+ fclose(fh);
+ return time(NULL)-lastsync;
+}
+
+static void report(struct pingstate *state)
+{
+ FILE *fh;
+ char namebuf[NI_MAXHOST];
+
+ if (state->out_filename)
+ {
+ fh= fopen(state->out_filename, "a");
+ if (!fh)
+ crondlog(DIE9 "unable to append to '%s'",
+ state->out_filename);
+ }
+ else
+ fh= stdout;
+
+ fprintf(fh, "RESULT { ");
+ if (state->atlas)
+ {
+ fprintf(fh, DBQ(id) ":" DBQ(%s)
+ ", " DBQ(fw) ":%d"
+ ", " DBQ(lts) ":%d"
+ ", " DBQ(time) ":%ld, ",
+ state->atlas, get_atlas_fw_version(), get_timesync(),
+ (long)time(NULL));
+ }
+
+ fprintf(fh, DBQ(dst_name) ":" DBQ(%s),
+ state->hostname);
+
+ if (!state->no_dst)
+ {
+ getnameinfo((struct sockaddr *)&state->sin6, state->socklen,
+ namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST);
+
+ fprintf(fh, ", " DBQ(dst_addr) ":" DBQ(%s) ", " DBQ(af) ":%d",
+ namebuf, state->sin6.sin6_family == AF_INET6 ? 6 : 4);
+ }
+
+ if (state->got_reply)
+ {
+ namebuf[0]= '\0';
+ getnameinfo((struct sockaddr *)&state->loc_sin6,
+ state->loc_socklen, namebuf, sizeof(namebuf),
+ NULL, 0, NI_NUMERICHOST);
+
+ fprintf(fh, ", \"src_addr\":\"%s\"", namebuf);
+ }
+
+ fprintf(fh, ", " DBQ(proto) ":" DBQ(ICMP));
+
+ if (state->got_reply)
+ fprintf(fh, ", " DBQ(ttl) ":%d", state->ttl);
+
+ fprintf(fh, ", " DBQ(size) ":%d", state->size);
+
+ fprintf(fh, ", \"result\": [ %s ] }\n", state->result);
+ free(state->result);
+ state->result= NULL;
+ state->busy= 0;
+
+ if (state->out_filename)
+ fclose(fh);
+}
+
+static void ping_cb(int result, int bytes,
+ struct sockaddr *sa, socklen_t socklen,
+ struct sockaddr *loc_sa, socklen_t loc_socklen,
+ int seq, int ttl,
+ struct timeval * elapsed, void * arg)
+{
+ struct pingstate *pingstate;
+ unsigned long usecs;
+ char namebuf[NI_MAXHOST];
+ char line[256];
+
+ pingstate= arg;
+
+#if 0
+ crondlog(LVL7 "in ping_cb: result %d, bytes %d, seq %d, ttl %d",
+ result, bytes, seq, ttl);
+#endif
+
+ if (!pingstate->busy)
+ {
+ crondlog(LVL8 "ping_cb: not busy for state %p, '%s'",
+ pingstate, pingstate->hostname);
+ return;
+ }
+
+ if (pingstate->first)
+ {
+ pingstate->size= bytes;
+ pingstate->ttl= ttl;
+ }
+
+ if (result == PING_ERR_NONE || result == PING_ERR_DUP)
+ {
+ /* Got a ping reply */
+ usecs= (elapsed->tv_sec * 1000000 + elapsed->tv_usec);
+
+ snprintf(line, sizeof(line),
+ "%s{ ", pingstate->first ? "" : ", ");
+ add_str(pingstate, line);
+ pingstate->first= 0;
+ pingstate->no_dst= 0;
+ if (result == PING_ERR_DUP)
+ {
+ add_str(pingstate, DBQ(dup) ":1, ");
+ }
+
+ snprintf(line, sizeof(line),
+ DBQ(rtt) ":%f",
+ usecs/1000.);
+ add_str(pingstate, line);
+
+ if (!pingstate->got_reply)
+ {
+ memcpy(&pingstate->loc_sin6, loc_sa, loc_socklen);
+ pingstate->loc_socklen= loc_socklen;
+
+ pingstate->got_reply= 1;
+ }
+
+ if (pingstate->size != bytes)
+ {
+ snprintf(line, sizeof(line),
+ ", " DBQ(size) ":%d", bytes);
+ add_str(pingstate, line);
+ pingstate->size= bytes;
+ }
+ if (pingstate->ttl != ttl)
+ {
+ snprintf(line, sizeof(line),
+ ", " DBQ(ttl) ":%d", ttl);
+ add_str(pingstate, line);
+ pingstate->ttl= ttl;
+ }
+ if (memcmp(&pingstate->loc_sin6, loc_sa, loc_socklen) != 0)
+ {
+ namebuf[0]= '\0';
+ getnameinfo(loc_sa, loc_socklen, namebuf,
+ sizeof(namebuf), NULL, 0, NI_NUMERICHOST);
+
+ snprintf(line, sizeof(line),
+ ", " DBQ(srcaddr) ":" DBQ(%s), namebuf);
+ add_str(pingstate, line);
+ }
+
+ add_str(pingstate, " }");
+ }
+ if (result == PING_ERR_TIMEOUT)
+ {
+ /* No ping reply */
+
+ snprintf(line, sizeof(line),
+ "%s{ " DBQ(x) ":" DBQ(*),
+ pingstate->first ? "" : ", ");
+ add_str(pingstate, line);
+ pingstate->no_dst= 0;
+ }
+ if (result == PING_ERR_SENDTO)
+ {
+ snprintf(line, sizeof(line),
+ "%s{ " DBQ(error) ":" DBQ(sendto failed: %s),
+ pingstate->first ? "" : ", ", strerror(seq));
+ add_str(pingstate, line);
+ pingstate->no_dst= 0;
+ }
+ if (result == PING_ERR_TIMEOUT || result == PING_ERR_SENDTO)
+ {
+ if (pingstate->first && pingstate->loc_socklen != 0)
+ {
+ namebuf[0]= '\0';
+ getnameinfo((struct sockaddr *)&pingstate->loc_sin6,
+ pingstate->loc_socklen,
+ namebuf, sizeof(namebuf),
+ NULL, 0, NI_NUMERICHOST);
+
+ snprintf(line, sizeof(line),
+ ", " DBQ(srcaddr) ":" DBQ(%s), namebuf);
+ add_str(pingstate, line);
+ }
+ add_str(pingstate, " }");
+ pingstate->first= 0;
+ }
+ if (result == PING_ERR_DNS)
+ {
+ pingstate->size= bytes;
+ snprintf(line, sizeof(line),
+ "%s{ " DBQ(error) ":" DBQ(dns resolution failed: %s) " }",
+ pingstate->first ? "" : ", ", (char *)sa);
+ add_str(pingstate, line);
+ report(pingstate);
+ }
+ if (result == PING_ERR_DONE)
+ {
+ report(pingstate);
+ }
+}
+
+/*
+ * Checksum routine for Internet Protocol family headers (C Version).
+ * From ping examples in W. Richard Stevens "Unix Network Programming" book.
+ */
+static int mkcksum(u_short *p, int n)
+{
+ u_short answer;
+ long sum = 0;
+ u_short odd_byte = 0;
+
+ while (n > 1)
+ {
+ sum += *p++;
+ n -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (n == 1)
+ {
+ * (u_char *) &odd_byte = * (u_char *) p;
+ sum += odd_byte;
+ }
+
+ sum = (sum >> 16) + (sum & 0xffff); /* add high 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* ones-complement, truncate */
+
+ return answer;
+}
+
+
+/*
+ * Format an ICMP Echo Request packet to be sent over the wire.
+ *
+ * o the IP packet will be added on by the kernel
+ * o the ID field is the Unix process ID
+ * o the sequence number is an ascending integer
+ *
+ * The first 8 bytes of the data portion are used
+ * to hold a Unix "timeval" struct in VAX byte-order,
+ * to compute the network round-trip value.
+ *
+ * The second 8 bytes of the data portion are used
+ * to keep an unique integer used as index in the array
+ * ho hosts being monitored
+ */
+static void fmticmp4(u_char *buffer, size_t *sizep, u_int8_t seq,
+ uint32_t idx, pid_t pid)
+{
+ size_t minlen;
+ struct icmp *icmp = (struct icmp *) buffer;
+ struct evdata *data = (struct evdata *) (buffer + ICMP_MINLEN);
+
+ struct timeval now;
+
+ minlen= ICMP_MINLEN + sizeof(*data);
+ if (*sizep < minlen)
+ *sizep= minlen;
+ if (*sizep > MAX_DATA_SIZE)
+ *sizep= MAX_DATA_SIZE;
+
+ if (*sizep > minlen)
+ memset(buffer+minlen, '\0', *sizep-minlen);
+
+ /* The ICMP header (no checksum here until user data has been filled in) */
+ icmp->icmp_type = ICMP_ECHO; /* type of message */
+ icmp->icmp_code = 0; /* type sub code */
+ icmp->icmp_id = 0xffff & pid; /* unique process identifier */
+ icmp->icmp_seq = htons(seq); /* message identifier */
+
+ /* User data */
+ gettimeofday(&now, NULL);
+ data->ts = now; /* current time */
+ data->index = idx; /* index into an array */
+
+ /* Last, compute ICMP checksum */
+ icmp->icmp_cksum = 0;
+ icmp->icmp_cksum = mkcksum((u_short *) icmp, *sizep); /* ones complement checksum of struct */
+}
+
+
+/*
+ * Format an ICMPv6 Echo Request packet to be sent over the wire.
+ *
+ * o the IP packet will be added on by the kernel
+ * o the ID field is the Unix process ID
+ * o the sequence number is an ascending integer
+ *
+ * The first 8 bytes of the data portion are used
+ * to hold a Unix "timeval" struct in VAX byte-order,
+ * to compute the network round-trip value.
+ *
+ * The second 8 bytes of the data portion are used
+ * to keep an unique integer used as index in the array
+ * ho hosts being monitored
+ */
+static void fmticmp6(u_char *buffer, size_t *sizep,
+ u_int8_t seq, uint32_t idx, pid_t pid)
+{
+ size_t minlen;
+ struct icmp6_hdr *icmp = (struct icmp6_hdr *) buffer;
+ struct evdata *data = (struct evdata *) (buffer + offsetof(struct icmp6_hdr, icmp6_data16[2]));
+
+ struct timeval now;
+
+ minlen= offsetof(struct icmp6_hdr, icmp6_data16[2]) + sizeof(*data);
+ if (*sizep < minlen)
+ *sizep= minlen;
+ if (*sizep > MAX_DATA_SIZE)
+ *sizep= MAX_DATA_SIZE;
+
+ if (*sizep > minlen)
+ memset(buffer+minlen, '\0', *sizep-minlen);
+
+ /* The ICMP header (no checksum here until user data has been filled in) */
+ icmp->icmp6_type = ICMP6_ECHO_REQUEST; /* type of message */
+ icmp->icmp6_code = 0; /* type sub code */
+ icmp->icmp6_id = 0xffff & pid; /* unique process identifier */
+ icmp->icmp6_seq = htons(seq); /* message identifier */
+
+ /* User data */
+ gettimeofday(&now, NULL);
+ data->ts = now; /* current time */
+ data->index = idx; /* index into an array */
+
+ icmp->icmp6_cksum = 0;
+}
+
+
+/* Attempt to transmit an ICMP Echo Request to a given host */
+static void ping_xmit(struct pingstate *host)
+{
+ struct pingbase *base = host->base;
+
+ int nsent, fd4, fd6, t_errno, r;
+
+ host->send_error= 0;
+ if (host->sentpkts >= host->maxpkts)
+ {
+ /* Done. */
+ ping_cb(PING_ERR_DONE, host->cursize,
+ (struct sockaddr *)&host->sin6, host->socklen,
+ (struct sockaddr *)&host->loc_sin6, host->loc_socklen,
+ 0, host->rcvd_ttl, NULL,
+ host);
+ if (host->base->done)
+ host->base->done(host);
+
+ /* Fake packet sent to kill timer */
+ host->sentpkts++;
+
+ return;
+ }
+
+ /* Transmit the request over the network */
+ if (host->sin6.sin6_family == AF_INET6)
+ {
+ /* Format the ICMP Echo Reply packet to send */
+ fmticmp6(base->packet, &host->cursize, host->seq, host->index,
+ base->pid);
+
+ fd6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+ if (fd6 != -1)
+ {
+ r= connect(fd6, (struct sockaddr *)&host->sin6,
+ host->socklen);
+ if (r == 0)
+ {
+ host->loc_socklen=
+ sizeof(host->loc_sin6);
+ getsockname(fd6, &host->loc_sin6,
+ &host->loc_socklen);
+ }
+ }
+
+ nsent = sendto(fd6, base->packet, host->cursize,
+ MSG_DONTWAIT, (struct sockaddr *)&host->sin6,
+ host->socklen);
+
+ t_errno= errno;
+ close(fd6);
+ errno= t_errno;
+ }
+ else
+ {
+ /* Format the ICMP Echo Reply packet to send */
+ fmticmp4(base->packet, &host->cursize, host->seq, host->index,
+ base->pid);
+
+ fd4 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (fd4 != -1)
+ {
+ r= connect(fd4, (struct sockaddr *)&host->sin6,
+ host->socklen);
+ if (r == 0)
+ {
+ host->loc_socklen=
+ sizeof(host->loc_sin6);
+ getsockname(fd4, &host->loc_sin6,
+ &host->loc_socklen);
+ }
+ }
+
+
+ nsent = sendto(fd4, base->packet, host->cursize,
+ MSG_DONTWAIT, (struct sockaddr *)&host->sin6,
+ host->socklen);
+
+ t_errno= errno;
+ close(fd4);
+ errno= t_errno;
+ }
+
+ if (nsent > 0)
+ {
+ /* Update timestamps and counters */
+ host->sentpkts++;
+
+ }
+ else
+ {
+ host->sentpkts++;
+ host->send_error= 1;
+
+ /* Report the failure and stop */
+ ping_cb(PING_ERR_SENDTO, host->cursize,
+ (struct sockaddr *)&host->sin6, host->socklen,
+ (struct sockaddr *)&host->loc_sin6, host->loc_socklen,
+ errno, 0, NULL,
+ host);
+ }
+}
+
+
+/* The callback to handle timeouts due to destination host unreachable condition */
+static void noreply_callback(int __attribute((unused)) unused, const short __attribute((unused)) event, void *h)
+{
+ struct pingstate *host = h;
+
+ if (!host->got_reply && !host->send_error)
+ {
+ ping_cb(PING_ERR_TIMEOUT, host->cursize,
+ (struct sockaddr *)&host->sin6, host->socklen,
+ NULL, 0,
+ host->seq, -1, &host->base->tv_interval,
+ host);
+
+ /* Update the sequence number for the next run */
+ host->seq = (host->seq + 1) % 256;
+ }
+
+ ping_xmit(host);
+
+ if (host->sentpkts <= host->maxpkts)
+ {
+ evtimer_add(&host->ping_timer, &host->base->tv_interval);
+ }
+}
+
+/*
+ * Called by libevent when the kernel says that the raw socket is ready for reading.
+ *
+ * It reads a packet from the wire and attempt to decode and relate ICMP Echo Request/Reply.
+ *
+ * To be legal the packet received must be:
+ * o of enough size (> IPHDR + ICMP_MINLEN)
+ * o of ICMP Protocol
+ * o of type ICMP_ECHOREPLY
+ * o the one we are looking for (matching the same identifier of all the packets the program is able to send)
+ */
+static void ready_callback4 (int __attribute((unused)) unused,
+ const short __attribute((unused)) event, void * arg)
+{
+ struct pingbase *base = arg;
+
+ int nrecv, isDup;
+ struct sockaddr_in remote; /* responding internet address */
+ socklen_t slen = sizeof(struct sockaddr);
+ struct sockaddr_in *sin4p;
+ struct sockaddr_in loc_sin4;
+
+ /* Pointer to relevant portions of the packet (IP, ICMP and user data) */
+ struct ip * ip = (struct ip *) base->packet;
+ struct icmphdr * icmp;
+ struct evdata * data = (struct evdata *) (base->packet + IPHDR + ICMP_MINLEN);
+ int hlen = 0;
+
+ struct timeval now;
+ struct pingstate * host;
+
+ /* Time the packet has been received */
+ gettimeofday(&now, NULL);
+
+// printf("ready_callback4: before recvfrom\n");
+ /* Receive data from the network */
+ nrecv = recvfrom(base->rawfd4, base->packet, sizeof(base->packet), MSG_DONTWAIT, (struct sockaddr *) &remote, &slen);
+ if (nrecv < 0)
+ {
+ goto done;
+ }
+
+#if 0
+ { int i;
+ printf("received:");
+ for (i= 0; i<nrecv; i++)
+ printf(" %02x", base->packet[i]);
+ printf("\n");
+ }
+#endif
+
+ /* Calculate the IP header length */
+ hlen = ip->ip_hl * 4;
+
+ /* Check the IP header */
+ if (nrecv < hlen + ICMP_MINLEN || ip->ip_hl < 5)
+ {
+ /* One more too short packet */
+printf("ready_callback4: too short\n");
+ goto done;
+ }
+
+ /* The ICMP portion */
+ icmp = (struct icmphdr *) (base->packet + hlen);
+
+ /* Check the ICMP header to drop unexpected packets due to unrecognized id */
+ if (icmp->un.echo.id != base->pid)
+ {
+#if 0
+ printf("ready_callback4: bad pid: got %d, expect %d\n",
+ icmp->un.echo.id, base->pid);
+#endif
+ goto done;
+ }
+
+ /* Check the ICMP payload for legal values of the 'index' portion */
+ if (data->index >= base->tabsiz || base->table[data->index] == NULL)
+ {
+ goto done;
+ }
+
+ /* Get the pointer to the host descriptor in our internal table */
+ host= base->table[data->index];
+
+ /* Check for Destination Host Unreachable */
+ if (icmp->type == ICMP_ECHO)
+ {
+ /* Completely ignore ECHO requests */
+ }
+ else if (icmp->type == ICMP_ECHOREPLY)
+ {
+ /* Use the User Data to relate Echo Request/Reply and evaluate the Round Trip Time */
+ struct timeval elapsed; /* response time */
+ time_t usecs;
+
+ /* Compute time difference to calculate the round trip */
+ evutil_timersub (&now, &data->ts, &elapsed);
+
+ /* Update counters */
+ usecs = tvtousecs(&elapsed);
+
+ /* Set destination address of packet as local address */
+ sin4p= &loc_sin4;
+ memset(sin4p, '\0', sizeof(*sin4p));
+ sin4p->sin_family= AF_INET;
+ sin4p->sin_addr= ip->ip_dst;
+ host->rcvd_ttl= ip->ip_ttl;
+
+ /* Report everything with the wrong sequence number as a dup.
+ * This is not quite right, it could be a late packet. Do we
+ * care?
+ */
+ isDup= (ntohs(icmp->un.echo.sequence) != host->seq);
+ ping_cb(isDup ? PING_ERR_DUP : PING_ERR_NONE,
+ nrecv - IPHDR,
+ (struct sockaddr *)&host->sin6, host->socklen,
+ (struct sockaddr *)&loc_sin4, sizeof(loc_sin4),
+ ntohs(icmp->un.echo.sequence), ip->ip_ttl, &elapsed,
+ host);
+
+ /* Update the sequence number for the next run */
+ host->seq = (host->seq + 1) % 256;
+
+ if (!isDup)
+ host->got_reply= 1;
+ }
+ else
+ {
+printf("ready_callback4: not an echo reply\n");
+ /* Handle this condition exactly as the request has expired */
+ noreply_callback (-1, -1, host);
+ }
+
+done:
+ ;
+}
+
+/*
+ * Called by libevent when the kernel says that the raw socket is ready for reading.
+ *
+ * It reads a packet from the wire and attempt to decode and relate ICMP Echo Request/Reply.
+ *
+ * To be legal the packet received must be:
+ * o of enough size (> IPHDR + ICMP_MINLEN)
+ * o of ICMP Protocol
+ * o of type ICMP_ECHOREPLY
+ * o the one we are looking for (matching the same identifier of all the packets the program is able to send)
+ */
+static void ready_callback6 (int __attribute((unused)) unused,
+ const short __attribute((unused)) event, void * arg)
+{
+ struct pingbase *base = arg;
+
+ int nrecv, isDup;
+ struct sockaddr_in remote; /* responding internet address */
+
+ /* Pointer to relevant portions of the packet (IP, ICMP and user data) */
+ struct icmp6_hdr * icmp = (struct icmp6_hdr *) base->packet;
+ struct evdata * data = (struct evdata *) (base->packet +
+ offsetof(struct icmp6_hdr, icmp6_data16[2]));
+
+ struct timeval now;
+ struct pingstate * host;
+ struct cmsghdr *cmsgptr;
+ struct sockaddr_in6 *sin6p;
+ struct msghdr msg;
+ struct sockaddr_in6 loc_sin6;
+ struct iovec iov[1];
+ char cmsgbuf[256];
+
+ /* Time the packet has been received */
+ gettimeofday(&now, NULL);
+
+ iov[0].iov_base= base->packet;
+ iov[0].iov_len= sizeof(base->packet);
+ msg.msg_name= &remote;
+ msg.msg_namelen= sizeof(remote);
+ msg.msg_iov= iov;
+ msg.msg_iovlen= 1;
+ msg.msg_control= cmsgbuf;
+ msg.msg_controllen= sizeof(cmsgbuf);
+ msg.msg_flags= 0; /* Not really needed */
+
+ /* Receive data from the network */
+ nrecv= recvmsg(base->rawfd6, &msg, MSG_DONTWAIT);
+ if (nrecv < 0)
+ {
+ goto done;
+ }
+
+ /* Check the ICMP header to drop unexpected packets due to
+ * unrecognized id
+ */
+ if (icmp->icmp6_id != base->pid)
+ {
+ goto done;
+ }
+
+ /* Check the ICMP payload for legal values of the 'index' portion */
+ if (data->index >= base->tabsiz || base->table[data->index] == NULL)
+ {
+ goto done;
+ }
+
+ /* Get the pointer to the host descriptor in our internal table */
+ host= base->table[data->index];
+
+ /* Check for Destination Host Unreachable */
+ if (icmp->icmp6_type == ICMP6_ECHO_REPLY)
+ {
+ /* Use the User Data to relate Echo Request/Reply and evaluate the Round Trip Time */
+ struct timeval elapsed; /* response time */
+ time_t usecs;
+
+ /* Compute time difference to calculate the round trip */
+ evutil_timersub (&now, &data->ts, &elapsed);
+
+ /* Update counters */
+ usecs = tvtousecs(&elapsed);
+
+ /* Set destination address of packet as local address */
+ memset(&loc_sin6, '\0', sizeof(loc_sin6));
+ for (cmsgptr= CMSG_FIRSTHDR(&msg); cmsgptr;
+ cmsgptr= CMSG_NXTHDR(&msg, cmsgptr))
+ {
+ if (cmsgptr->cmsg_len == 0)
+ break; /* Can this happen? */
+ if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
+ cmsgptr->cmsg_type == IPV6_PKTINFO)
+ {
+ sin6p= &loc_sin6;
+ sin6p->sin6_family= AF_INET6;
+ sin6p->sin6_addr= ((struct in6_pktinfo *)
+ CMSG_DATA(cmsgptr))->ipi6_addr;
+ }
+ if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
+ cmsgptr->cmsg_type == IPV6_HOPLIMIT)
+ {
+ host->rcvd_ttl= *(int *)CMSG_DATA(cmsgptr);
+ }
+ }
+
+ /* Report everything with the wrong sequence number as a dup.
+ * This is not quite right, it could be a late packet. Do we
+ * care?
+ */
+ isDup= (ntohs(icmp->icmp6_seq) != host->seq);
+ ping_cb(isDup ? PING_ERR_DUP : PING_ERR_NONE,
+ nrecv - IPHDR,\
+ (struct sockaddr *)&host->sin6, host->socklen,
+ (struct sockaddr *)&loc_sin6, sizeof(loc_sin6),
+ ntohs(icmp->icmp6_seq), host->rcvd_ttl, &elapsed,
+ host);
+
+ /* Update the sequence number for the next run */
+ host->seq = (host->seq + 1) % 256;
+
+ if (!isDup)
+ host->got_reply= 1;
+ }
+ else
+ /* Handle this condition exactly as the request has expired */
+ noreply_callback (-1, -1, host);
+
+done:
+ ;
+}
+
+
+static void *ping_init(int __attribute((unused)) argc, char *argv[],
+ void (*done)(void *state))
+{
+ static struct pingbase *ping_base;
+
+ int i, newsiz, delay_name_res;
+ uint32_t opt;
+ unsigned pingcount; /* must be int-sized */
+ unsigned size;
+ sa_family_t af;
+ const char *hostname;
+ char *str_Atlas;
+ char *out_filename;
+ struct pingstate *state;
+ len_and_sockaddr *lsa;
+ FILE *fh;
+
+ if (!ping_base)
+ {
+ int p_proto, on;
+ struct protoent *protop;
+ evutil_socket_t fd4, fd6;
+
+ /* Check if the ICMP protocol is available on this system */
+ protop = getprotobyname("icmp");
+ if (protop)
+ p_proto= protop->p_proto;
+ else
+ p_proto= IPPROTO_ICMP;
+
+ /* Create an endpoint for communication using raw socket for ICMP calls */
+ if ((fd4 = socket(AF_INET, SOCK_RAW, p_proto)) == -1) {
+ return NULL;
+ }
+
+ /* Check if the ICMP6 protocol is available on this system */
+ protop = getprotobyname("icmp6");
+ if (protop)
+ p_proto= protop->p_proto;
+ else
+ p_proto= IPPROTO_ICMPV6;
+
+ if ((fd6 = socket(AF_INET6, SOCK_RAW, p_proto)) == -1) {
+ close(fd4);
+ return NULL;
+ }
+
+ on = 1;
+ setsockopt(fd6, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on));
+
+ on = 1;
+ setsockopt(fd6, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
+ sizeof(on));
+
+ ping_base = malloc(sizeof(*ping_base));
+ if (ping_base == NULL)
+ return (NULL);
+ memset(ping_base, 0, sizeof(*ping_base));
+
+ ping_base->event_base = EventBase;
+
+ ping_base->tabsiz= 10;
+ ping_base->table= xzalloc(ping_base->tabsiz *
+ sizeof(*ping_base->table));
+
+ ping_base->rawfd4 = fd4;
+ ping_base->rawfd6 = fd6;
+ evutil_make_socket_nonblocking(ping_base->rawfd4);
+ evutil_make_socket_nonblocking(ping_base->rawfd6);
+
+ /* Set default values */
+ ping_base->pid = getpid();
+
+ msecstotv(DEFAULT_PING_INTERVAL, &ping_base->tv_interval);
+
+ /* Define the callback to handle ICMP Echo Reply and add the
+ * raw file descriptor to those monitored for read events */
+ event_assign(&ping_base->event4, ping_base->event_base,
+ ping_base->rawfd4, EV_READ | EV_PERSIST,
+ ready_callback4, ping_base);
+ event_assign(&ping_base->event6, ping_base->event_base,
+ ping_base->rawfd6, EV_READ | EV_PERSIST,
+ ready_callback6, ping_base);
+ event_add(&ping_base->event4, NULL);
+ event_add(&ping_base->event6, NULL);
+
+ ping_base->done= 0;
+ }
+
+ /* Parse arguments */
+ pingcount= 3;
+ size= 0;
+ str_Atlas= NULL;
+ out_filename= NULL;
+ /* exactly one argument needed; -c NUM */
+ opt_complementary = "=1:c+:s+";
+ opt = getopt32(argv, PING_OPT_STRING, &pingcount, &size,
+ &str_Atlas, &out_filename);
+ hostname = argv[optind];
+
+ if (opt == 0xffffffff)
+ {
+ crondlog(LVL8 "bad options");
+ return NULL;
+ }
+
+ if (out_filename)
+ {
+ if (!validate_filename(out_filename, SAFE_PREFIX))
+ {
+ crondlog(LVL8 "insecure file '%s'", out_filename);
+ return NULL;
+ }
+ fh= fopen(out_filename, "a");
+ if (!fh)
+ {
+ crondlog(LVL8 "unable to append to '%s'",
+ out_filename);
+ return NULL;
+ }
+ fclose(fh);
+ }
+
+ af= AF_UNSPEC;
+ if (opt & opt_4)
+ af= AF_INET;
+ if (opt & opt_6)
+ af= AF_INET6;
+ delay_name_res= !!(opt & opt_r);
+
+ if (!delay_name_res)
+ {
+ /* Attempt to resolv 'name' */
+ lsa= host_and_af2sockaddr(hostname, 0, af);
+ if (!lsa)
+ return NULL;
+
+ if (lsa->len > sizeof(state->sin6))
+ {
+ free(lsa);
+ return NULL;
+ }
+ }
+
+ state= xzalloc(sizeof(*state));
+
+ memset(&state->loc_sin6, '\0', sizeof(state->loc_sin6));
+ state->loc_socklen= 0;
+ if (!delay_name_res)
+ {
+ state->socklen= lsa->len;
+ memcpy(&state->sin6, &lsa->u.sa, state->socklen);
+ free(lsa); lsa= NULL;
+ }
+
+ state->base = ping_base;
+ state->af= af;
+ state->delay_name_res= delay_name_res;
+
+ state->seq = 1;
+
+ /* Define here the callbacks to ping the host and to handle no reply
+ * timeouts
+ */
+ evtimer_assign(&state->ping_timer, state->base->event_base,
+ noreply_callback, state);
+
+ for (i= 0; i<ping_base->tabsiz; i++)
+ {
+ if (ping_base->table[i] == NULL)
+ break;
+ }
+ if (i >= ping_base->tabsiz)
+ {
+ newsiz= 2*ping_base->tabsiz;
+ ping_base->table= xrealloc(ping_base->table,
+ newsiz*sizeof(*ping_base->table));
+ for (i= ping_base->tabsiz; i<newsiz; i++)
+ ping_base->table[i]= NULL;
+ i= ping_base->tabsiz;
+ ping_base->tabsiz= newsiz;
+ }
+ state->index= i;
+ ping_base->table[i]= state;
+
+ state->pingcount= pingcount;
+ state->atlas= str_Atlas ? strdup(str_Atlas) : NULL;
+ state->hostname= strdup(hostname);
+ state->out_filename= out_filename ? strdup(out_filename) : NULL;
+
+ state->result= NULL;
+ state->reslen= 0;
+ state->resmax= 0;
+
+ state->maxsize = size;
+ state->base->done= done;
+
+ return state;
+}
+
+static void ping_start2(void *state)
+{
+ struct pingstate *pingstate;
+
+ pingstate= state;
+
+ pingstate->sentpkts= 0;
+ pingstate->cursize= pingstate->maxsize;
+
+ ping_xmit(pingstate);
+
+ /* Add the timer to handle no reply condition in the given timeout */
+ evtimer_add(&pingstate->ping_timer, &pingstate->base->tv_interval);
+}
+
+static void dns_cb(int result, struct evutil_addrinfo *res, void *ctx)
+{
+ int count;
+ struct pingstate *env;
+ struct evutil_addrinfo *cur;
+
+ env= ctx;
+
+ if (!env->dnsip)
+ {
+ crondlog(LVL7
+ "dns_cb: in dns_cb but not doing dns at this time");
+ if (res)
+ evutil_freeaddrinfo(res);
+ return;
+ }
+
+ env->dnsip= 0;
+
+ if (result != 0)
+ {
+ ping_cb(PING_ERR_DNS, env->maxsize,
+ (struct sockaddr *)evutil_gai_strerror(result), 0,
+ (struct sockaddr *)NULL, 0,
+ 0, 0, NULL,
+ env);
+ ping_cb(PING_ERR_DONE, env->maxsize,
+ (struct sockaddr *)NULL, 0,
+ (struct sockaddr *)NULL, 0,
+ 0, 0, NULL,
+ env);
+ if (env->base->done)
+ env->base->done(env);
+ return;
+ }
+
+ env->dns_res= res;
+ env->dns_curr= res;
+
+ count= 0;
+ for (cur= res; cur; cur= cur->ai_next)
+ count++;
+
+ // env->reportcount(env, count);
+
+ while (env->dns_curr)
+ {
+ env->socklen= env->dns_curr->ai_addrlen;
+ if (env->socklen > sizeof(env->sin6))
+ continue; /* Weird */
+ memcpy(&env->sin6, env->dns_curr->ai_addr,
+ env->socklen);
+
+ ping_start2(env);
+
+ return;
+ }
+
+ /* Something went wrong */
+ evutil_freeaddrinfo(env->dns_res);
+ env->dns_res= NULL;
+ env->dns_curr= NULL;
+ ping_cb(PING_ERR_DNS_NO_ADDR, env->cursize,
+ (struct sockaddr *)NULL, 0,
+ (struct sockaddr *)NULL, 0,
+ 0, 0, NULL,
+ env);
+ if (env->base->done)
+ env->base->done(env);
+}
+
+static void ping_start(void *state)
+{
+ struct pingstate *pingstate;
+ struct evdns_getaddrinfo_request *evdns_req;
+ struct evutil_addrinfo hints;
+
+ pingstate= state;
+
+ if (pingstate->result) free(pingstate->result);
+ pingstate->resmax= 80;
+ pingstate->result= xmalloc(pingstate->resmax);
+ pingstate->reslen= 0;
+
+ pingstate->first= 1;
+ pingstate->got_reply= 0;
+ pingstate->no_dst= 1;
+ pingstate->busy= 1;
+
+ pingstate->maxpkts= pingstate->pingcount;
+
+ if (!pingstate->delay_name_res)
+ {
+ ping_start2(state);
+ return;
+ }
+
+ pingstate->dnsip= 1;
+
+ memset(&hints, '\0', sizeof(hints));
+ hints.ai_socktype= SOCK_DGRAM;
+ hints.ai_family= pingstate->af;
+ printf("hostname '%s', family %d\n",
+ pingstate->hostname, hints.ai_family);
+ evdns_req= evdns_getaddrinfo(DnsBase, pingstate->hostname,
+ NULL, &hints, dns_cb, pingstate);
+}
+
+static int ping_delete(void *state)
+{
+ struct pingstate *pingstate;
+ struct pingbase *base;
+
+ pingstate= state;
+
+ if (pingstate->busy)
+ {
+ crondlog(LVL8
+ "ping_delete: not deleting, busy for state %p, '%s'",
+ pingstate, pingstate->hostname);
+ return 0;
+ }
+
+ base= pingstate->base;
+
+ evtimer_del(&pingstate->ping_timer);
+
+ base->table[pingstate->index]= NULL;
+
+ free(pingstate->atlas);
+ pingstate->atlas= NULL;
+ free(pingstate->hostname);
+ pingstate->hostname= NULL;
+ free(pingstate->out_filename);
+ pingstate->out_filename= NULL;
+
+ free(pingstate);
+
+ return 1;
+}
+
+struct testops ping_ops = { ping_init, ping_start, ping_delete };
+
diff --git a/eperd/readresolv.c b/eperd/readresolv.c
new file mode 100644
index 0000000..9d3b8c9
--- /dev/null
+++ b/eperd/readresolv.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#define LINEL (INET6_ADDRSTRLEN * 2)
+#include "libbb.h"
+#include "resolv.h"
+
+static void nameserver_ip_add (char *nsentry, char *ip_as_string)
+{
+
+ strncpy (nsentry, ip_as_string, LINEL);
+ // printf("AA added nameserver %s\n", ip_as_string);
+ // printf("AA added nameserver to ns %s\n", nsentry);
+ return;
+}
+
+static int resolv_conf_parse_line (char *nsentry, char *line)
+{
+
+#define NEXT_TOKEN strtok_r(NULL, delims, &strtok_state)
+ char *strtok_state;
+ static const char *const delims = " \t";
+ char *const first_token = strtok_r(line, delims, &strtok_state);
+
+ if (!first_token) return 0;
+
+ if (!strcmp(first_token, "nameserver")) {
+ char *const nameserver = NEXT_TOKEN;
+ if (nameserver) {
+ if(nameserver[(strlen(nameserver) - 1)] == '\n')
+ nameserver[(strlen(nameserver) - 1)] = NULL;
+ nameserver_ip_add(nsentry, nameserver);
+ //printf("AA added nameserver %s\n", nsentry);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int get_local_resolvers(char nslist[MAXNS][INET6_ADDRSTRLEN * 2])
+{
+ char buf[LINEL];
+ int i = 0;
+
+ FILE *R = fopen ("/etc/resolv.conf", "r");
+ if (R != NULL) {
+ while ( (fgets (buf, LINEL, R)) && (i < MAXNS)) {
+ if(resolv_conf_parse_line(nslist[i], buf) )
+ i++;
+ }
+ fclose (R);
+ }
+ return i;
+}
diff --git a/eperd/readresolv.h b/eperd/readresolv.h
new file mode 100644
index 0000000..a195aaa
--- /dev/null
+++ b/eperd/readresolv.h
@@ -0,0 +1,5 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for detai
+ */
+int get_local_resolvers(char nslist[MAXNS][INET6_ADDRSTRLEN * 2]);
diff --git a/eperd/tcputil.c b/eperd/tcputil.c
new file mode 100644
index 0000000..0e5d221
--- /dev/null
+++ b/eperd/tcputil.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * tcputil.c
+ */
+
+#include "libbb.h"
+#include "eperd.h"
+#include <event2/bufferevent.h>
+#include <event2/dns.h>
+#include <event2/event.h>
+
+#include "tcputil.h"
+
+static void dns_cb(int result, struct evutil_addrinfo *res, void *ctx);
+static void create_bev(struct tu_env *env);
+static void eventcb(struct bufferevent *bev, short events, void *ptr);
+
+void tu_connect_to_name(struct tu_env *env, char *host, char *port,
+ struct timeval *interval,
+ struct evutil_addrinfo *hints,
+ void (*timeout_callback)(int unused, const short event, void *s),
+ void (*reporterr)(struct tu_env *env, enum tu_err cause,
+ const char *err),
+ void (*reportcount)(struct tu_env *env, int count),
+ void (*beforeconnect)(struct tu_env *env,
+ struct sockaddr *addr, socklen_t addrlen),
+ void (*connected)(struct tu_env *env, struct bufferevent *bev),
+ void (*readcb)(struct bufferevent *bev, void *ptr),
+ void (*writecb)(struct bufferevent *bev, void *ptr))
+{
+ struct evdns_getaddrinfo_request *evdns_req;
+
+ env->interval= *interval;
+ env->reporterr= reporterr;
+ env->reportcount= reportcount;
+ env->beforeconnect= beforeconnect;
+ env->connected= connected;
+ env->readcb= readcb;
+ env->writecb= writecb;
+ env->dns_res= NULL;
+ env->bev= NULL;
+
+ evtimer_assign(&env->timer, EventBase,
+ timeout_callback, env);
+
+ env->dnsip= 1;
+ env->connecting= 0;
+ evdns_req= evdns_getaddrinfo(DnsBase, host, port,
+ hints, dns_cb, env);
+}
+
+void tu_restart_connect(struct tu_env *env)
+{
+ struct bufferevent *bev;
+
+ /* Delete old bev */
+ if (env->bev)
+ {
+ bufferevent_free(env->bev);
+ env->bev= NULL;
+ }
+
+ /* And create a new one */
+ create_bev(env);
+ bev= env->bev;
+
+ /* Connect failed, try next address */
+ if (env->dns_curr)
+ /* Just to be on the safe side */
+ {
+ env->dns_curr= env->dns_curr->ai_next;
+ }
+ while (env->dns_curr)
+ {
+ evtimer_add(&env->timer, &env->interval);
+
+ env->beforeconnect(env,
+ env->dns_curr->ai_addr, env->dns_curr->ai_addrlen);
+ if (bufferevent_socket_connect(bev,
+ env->dns_curr->ai_addr,
+ env->dns_curr->ai_addrlen) == 0)
+ {
+ /* Connecting, wait for callback */
+ return;
+ }
+
+ /* Immediate error? */
+ printf("connect error\n");
+ env->dns_curr= env->dns_curr->ai_next;
+ }
+
+ /* Something went wrong */
+ bufferevent_free(env->bev);
+ env->bev= NULL;
+ if (env->dns_res)
+ {
+ evutil_freeaddrinfo(env->dns_res);
+ env->dns_res= NULL;
+ env->dns_curr= NULL;
+ }
+ env->reporterr(env, TU_OUT_OF_ADDRS, "");
+}
+
+void tu_cleanup(struct tu_env *env)
+{
+ if (env->dns_res)
+ {
+ evutil_freeaddrinfo(env->dns_res);
+ env->dns_res= NULL;
+ env->dns_curr= NULL;
+ }
+ if (env->bev)
+ {
+ bufferevent_free(env->bev);
+ env->bev= NULL;
+ }
+
+ event_del(&env->timer);
+}
+
+static void dns_cb(int result, struct evutil_addrinfo *res, void *ctx)
+{
+ int count;
+ struct tu_env *env;
+ struct bufferevent *bev;
+ struct evutil_addrinfo *cur;
+
+ env= ctx;
+
+ if (!env->dnsip)
+ {
+ crondlog(LVL7
+ "dns_cb: in dns_cb but not doing dns at this time");
+ if (res)
+ evutil_freeaddrinfo(res);
+ return;
+ }
+
+ env->dnsip= 0;
+
+ if (result != 0)
+ {
+ env->reporterr(env, TU_DNS_ERR, evutil_gai_strerror(result));
+ return;
+ }
+
+ env->dns_res= res;
+ env->dns_curr= res;
+
+ count= 0;
+ for (cur= res; cur; cur= cur->ai_next)
+ count++;
+
+ env->reportcount(env, count);
+
+ create_bev(env);
+
+ while (env->dns_curr)
+ {
+ evtimer_add(&env->timer, &env->interval);
+
+ env->beforeconnect(env,
+ env->dns_curr->ai_addr, env->dns_curr->ai_addrlen);
+ bev= env->bev;
+ if (bufferevent_socket_connect(bev,
+ env->dns_curr->ai_addr,
+ env->dns_curr->ai_addrlen) == 0)
+ {
+ /* Connecting, wait for callback */
+ return;
+ }
+
+ /* Immediate error? */
+ printf("dns_cb: connect error\n");
+
+ /* It is possible that the callback already freed dns_curr. */
+ if (!env->dns_curr)
+ {
+ printf("dns_cb: callback ate dns_curr\n");
+ if (env->dns_res)
+ crondlog(DIE9 "dns_cb: dns_res not null");
+ return;
+ }
+ env->dns_curr= env->dns_curr->ai_next;
+ }
+
+ /* Something went wrong */
+ printf("dns_cb: Connect failed\n");
+ bufferevent_free(env->bev);
+ env->bev= NULL;
+ evutil_freeaddrinfo(env->dns_res);
+ env->dns_res= NULL;
+ env->dns_curr= NULL;
+ env->reporterr(env, TU_OUT_OF_ADDRS, "");
+}
+
+static void create_bev(struct tu_env *env)
+{
+ struct bufferevent *bev;
+
+ bev= bufferevent_socket_new(EventBase, -1,
+ BEV_OPT_CLOSE_ON_FREE);
+ if (!bev)
+ {
+ crondlog(DIE9 "bufferevent_socket_new failed");
+ }
+ bufferevent_setcb(bev, env->readcb, env->writecb, eventcb, env);
+ bufferevent_enable(bev, EV_WRITE);
+ env->bev= bev;
+ env->connecting= 1;
+
+}
+
+static void eventcb(struct bufferevent *bev, short events, void *ptr)
+{
+ struct tu_env *env;
+
+ env= ptr;
+
+ if (events & BEV_EVENT_READING)
+ {
+ env->reporterr(env, TU_READ_ERR, "");
+ events &= ~BEV_EVENT_READING;
+ return;
+ }
+ if (events & BEV_EVENT_ERROR)
+ {
+ if (env->connecting)
+ {
+ env->reporterr(env, TU_CONNECT_ERR,
+ strerror(errno));
+ return;
+ }
+ events &= ~BEV_EVENT_ERROR;
+ }
+ if (events & BEV_EVENT_CONNECTED)
+ {
+ events &= ~BEV_EVENT_CONNECTED;
+ env->connecting= 0;
+ bufferevent_enable(bev, EV_READ);
+
+ env->connected(env, bev);
+ env->writecb(bev, ptr);
+ }
+ if (events)
+ printf("events = 0x%x\n", events);
+}
+
diff --git a/eperd/tcputil.h b/eperd/tcputil.h
new file mode 100644
index 0000000..d2b11ec
--- /dev/null
+++ b/eperd/tcputil.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * tcputil.h
+ */
+
+#include <event2/event_struct.h>
+
+enum tu_err { TU_DNS_ERR, TU_READ_ERR, TU_CONNECT_ERR, TU_OUT_OF_ADDRS };
+struct tu_env
+{
+ char dnsip;
+ char connecting;
+ struct evutil_addrinfo *dns_res;
+ struct evutil_addrinfo *dns_curr;
+ struct bufferevent *bev;
+ struct timeval interval;
+ struct event timer;
+ void (*reporterr)(struct tu_env *env, enum tu_err cause,
+ const char *str);
+ void (*reportcount)(struct tu_env *env, int count);
+ void (*beforeconnect)(struct tu_env *env,
+ struct sockaddr *addr, socklen_t addrlen);
+ void (*connected)(struct tu_env *env, struct bufferevent *bev);
+ void (*readcb)(struct bufferevent *bev, void *env);
+ void (*writecb)(struct bufferevent *bev, void *env);
+};
+
+void tu_connect_to_name(struct tu_env *env, char *host, char *port,
+ struct timeval *timeout,
+ struct evutil_addrinfo *hints,
+ void (*timeout_callback)(int unused, const short event, void *env),
+ void (*reporterr)(struct tu_env *env, enum tu_err cause,
+ const char *err),
+ void (*reportcount)(struct tu_env *env, int count),
+ void (*beforeconnect)(struct tu_env *env,
+ struct sockaddr *addr, socklen_t addrlen),
+ void (*connected)(struct tu_env *env, struct bufferevent *bev),
+ void (*readcb)(struct bufferevent *bev, void *env),
+ void (*writecb)(struct bufferevent *bev, void *env));
+void tu_restart_connect(struct tu_env *env);
+void tu_cleanup(struct tu_env *env);
diff --git a/eperd/traceroute.c b/eperd/traceroute.c
new file mode 100644
index 0000000..014849b
--- /dev/null
+++ b/eperd/traceroute.c
@@ -0,0 +1,2866 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * traceroute.c
+ */
+
+#include "libbb.h"
+#include <event2/dns.h>
+#include <event2/event.h>
+#include <event2/event_struct.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet/udp.h>
+
+#include "eperd.h"
+
+#define SAFE_PREFIX ATLAS_DATA_NEW
+
+#define DBQ(str) "\"" #str "\""
+
+#ifndef STANDALONE_BUSYBOX
+#define uh_sport source
+#define uh_dport dest
+#define uh_ulen len
+#define uh_sum check
+#endif
+
+#define TRACEROUTE_OPT_STRING ("!46IUFra:c:f:g:m:w:z:A:O:S:")
+
+#define OPT_4 (1 << 0)
+#define OPT_6 (1 << 1)
+#define OPT_I (1 << 2)
+#define OPT_U (1 << 3)
+#define OPT_F (1 << 4)
+#define OPT_r (1 << 5)
+
+#define BASE_PORT (0x8000 + 666)
+#define SRC_BASE_PORT (20480)
+#define MAX_DATA_SIZE (4096)
+
+#define DBQ(str) "\"" #str "\""
+
+#define ICMPEXT_VERSION_SHIFT 4
+
+#define ICMPEXT_MPLS 1
+#define ICMPEXT_MPLS_IN 1
+
+#define MPLS_LABEL_SHIFT 12
+#define MPLS_EXT_SHIFT 9
+#define MPLS_EXT_MASK 0x7
+#define MPLS_S_BIT 0x100
+#define MPLS_TTL_MASK 0xff
+
+struct trtbase
+{
+ struct event_base *event_base;
+
+ int v4icmp_rcv;
+ int v6icmp_rcv;
+ int v4icmp_snd;
+ int v6icmp_snd;
+ int v4udp_snd;
+
+ int my_pid;
+
+ struct event event4;
+ struct event event6;
+
+ struct trtstate **table;
+ int tabsiz;
+
+ /* For standalone traceroute. Called when a traceroute instance is
+ * done. Just one pointer for all instances. It is up to the caller
+ * to keep it consistent.
+ */
+ void (*done)(void *state);
+
+ u_char packet[MAX_DATA_SIZE];
+};
+
+struct trtstate
+{
+ /* Parameters */
+ char *atlas;
+ char *hostname;
+ char *out_filename;
+ char do_icmp;
+ char do_v6;
+ char dont_fragment;
+ char delay_name_res;
+ char trtcount;
+ unsigned short maxpacksize;
+ unsigned char firsthop;
+ unsigned char maxhops;
+ unsigned char gaplimit;
+ unsigned char parismod;
+ unsigned duptimeout;
+ unsigned timeout;
+
+ /* Base and index in table */
+ struct trtbase *base;
+ int index;
+
+ struct sockaddr_in6 sin6;
+ socklen_t socklen;
+ struct sockaddr_in6 loc_sin6;
+ socklen_t loc_socklen;
+
+ int sent;
+ uint8_t hop;
+ uint16_t paris;
+ uint16_t seq;
+ unsigned short curpacksize;
+
+ uint8_t last_response_hop; /* Hop at which we last got something
+ * back.
+ */
+ unsigned done:1; /* We got something from the target
+ * host or a destination unreachable.
+ */
+ unsigned not_done:1; /* Not got something else */
+ unsigned lastditch:1; /* In last-ditch hop */
+ unsigned busy:1; /* Busy, do not start another one */
+ unsigned gotresp:1; /* Got a response to the last packet
+ * we sent. For dup detection.
+ */
+ unsigned dnsip:1; /* Busy with dns name resolution */
+ struct evutil_addrinfo *dns_res;
+ struct evutil_addrinfo *dns_curr;
+
+ time_t starttime;
+ struct timeval xmit_time;
+
+ struct event timer;
+
+ unsigned long min;
+ unsigned long max;
+ unsigned long sum;
+ int sentpkts;
+ int rcvdpkts;
+ int duppkts;
+
+ char *result;
+ size_t reslen;
+ size_t resmax;
+};
+
+static struct trtbase *trt_base;
+
+struct udp_ph
+{
+ struct in_addr src;
+ struct in_addr dst;
+ uint8_t zero;
+ uint8_t proto;
+ uint16_t len;
+};
+
+struct v6_ph
+{
+ struct in6_addr src;
+ struct in6_addr dst;
+ uint32_t len;
+ uint8_t zero[3];
+ uint8_t nxt;
+};
+
+struct v6info
+{
+ uint16_t fuzz;
+ uint32_t pid;
+ uint32_t id;
+ uint32_t seq;
+ struct timeval tv;
+};
+
+static int in_cksum(unsigned short *buf, int sz)
+{
+ int nleft = sz;
+ int sum = 0;
+ unsigned short *w = buf;
+ unsigned short ans = 0;
+
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ if (nleft == 1) {
+ *(unsigned char *) (&ans) = *(unsigned char *) w;
+ sum += ans;
+ }
+
+ sum = (sum >> 16) + (sum & 0xFFFF);
+ sum += (sum >> 16);
+ ans = ~sum;
+ return ans;
+}
+
+static int in_cksum_udp(struct udp_ph *udp_ph, struct udphdr *udp,
+ unsigned short *buf, int sz)
+{
+ int nleft = sz;
+ int sum = 0;
+ unsigned short *w = buf;
+ unsigned short ans = 0;
+
+ nleft= sizeof(*udp_ph);
+ w= (unsigned short *)udp_ph;
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ nleft= sizeof(*udp);
+ w= (unsigned short *)udp;
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ nleft= sz;
+ w= buf;
+
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ if (nleft == 1) {
+ *(unsigned char *) (&ans) = *(unsigned char *) w;
+ sum += ans;
+ }
+
+ sum = (sum >> 16) + (sum & 0xFFFF);
+ sum += (sum >> 16);
+ ans = ~sum;
+ return ans;
+}
+
+static int in_cksum_icmp6(struct v6_ph *v6_ph, unsigned short *buf, int sz)
+{
+ int nleft = sz;
+ int sum = 0;
+ unsigned short *w = buf;
+ unsigned short ans = 0;
+
+ nleft= sizeof(*v6_ph);
+ w= (unsigned short *)v6_ph;
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ nleft= sz;
+ w= buf;
+
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ if (nleft == 1) {
+ *(unsigned char *) (&ans) = *(unsigned char *) w;
+ sum += ans;
+ }
+
+ sum = (sum >> 16) + (sum & 0xFFFF);
+ sum += (sum >> 16);
+ ans = ~sum;
+ return ans;
+}
+
+static void add_str(struct trtstate *state, const char *str)
+{
+ size_t len;
+
+ len= strlen(str);
+ if (state->reslen + len+1 > state->resmax)
+ {
+ state->resmax= state->reslen + len+1 + 80;
+ state->result= xrealloc(state->result, state->resmax);
+ }
+ memcpy(state->result+state->reslen, str, len+1);
+ state->reslen += len;
+ //printf("add_str: result = '%s'\n", state->result);
+}
+
+static void report(struct trtstate *state)
+{
+ FILE *fh;
+ char namebuf[NI_MAXHOST];
+
+ event_del(&state->timer);
+
+ if (state->out_filename)
+ {
+ fh= fopen(state->out_filename, "a");
+ if (!fh)
+ crondlog(DIE9 "unable to append to '%s'",
+ state->out_filename);
+ }
+ else
+ fh= stdout;
+
+ fprintf(fh, "RESULT { ");
+ if (state->atlas)
+ {
+ fprintf(fh, DBQ(id) ":" DBQ(%s)
+ ", " DBQ(fw) ":%d"
+ ", " DBQ(time) ":%ld"
+ ", " DBQ(endtime) ":%ld, ",
+ state->atlas, get_atlas_fw_version(),
+ state->starttime,
+ (long)time(NULL));
+ }
+
+ fprintf(fh, DBQ(dst_name) ":" DBQ(%s),
+ state->hostname);
+
+ if (!state->dnsip)
+ {
+ getnameinfo((struct sockaddr *)&state->sin6, state->socklen,
+ namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST);
+
+ fprintf(fh, ", " DBQ(dst_addr) ":" DBQ(%s), namebuf);
+
+ namebuf[0]= '\0';
+ getnameinfo((struct sockaddr *)&state->loc_sin6,
+ state->loc_socklen,
+ namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST);
+
+ fprintf(fh, ", " DBQ(src_addr) ":" DBQ(%s), namebuf);
+ }
+
+ fprintf(fh, ", " DBQ(proto) ":" DBQ(%s) ", " DBQ(af) ": %d",
+ state->do_icmp ? "ICMP" : "UDP",
+ state->dnsip ? (state->do_v6 ? 6 : 4) :
+ (state->sin6.sin6_family == AF_INET6 ? 6 : 4));
+
+ fprintf(fh, ", \"size\":%d", state->maxpacksize);
+ if (state->parismod)
+ {
+ fprintf(fh, ", \"paris_id\":%d",
+ state->paris % state->parismod);
+ }
+ fprintf(fh, ", \"result\": [ %s ] }\n", state->result);
+ free(state->result);
+ state->result= NULL;
+ state->busy= 0;
+
+ if (state->out_filename)
+ fclose(fh);
+
+ if (state->base->done)
+ state->base->done(state);
+}
+
+static void send_pkt(struct trtstate *state)
+{
+ int r, hop, len, on, sock, serrno;
+ uint16_t sum, val;
+ unsigned usum;
+ struct trtbase *base;
+ struct icmp *icmp_hdr;
+ struct icmp6_hdr *icmp6_hdr;
+ struct v6info *v6info;
+ struct udp_ph udp_ph;
+ struct v6_ph v6_ph;
+ struct udphdr udp;
+ struct timeval interval;
+ char line[80];
+ char id[]= "http://atlas.ripe.net Atlas says Hi!";
+
+ state->gotresp= 0;
+
+ base= state->base;
+
+ if (state->sent >= state->trtcount)
+ {
+ add_str(state, " } ] }");
+ if (state->hop >= state->maxhops ||
+ (state->done && !state->not_done))
+ {
+ /* We are done */
+ report(state);
+ return;
+ }
+
+ state->hop++;
+ state->sent= 0;
+ state->done= 0;
+ state->not_done= 0;
+
+ if (state->hop - state->last_response_hop >
+ state->gaplimit)
+ {
+#if 0
+ printf("gaplimit reached: %d > %d + %d\n",
+ state->hop, state->last_response_hop,
+ state->gaplimit);
+#endif
+ if (state->lastditch)
+ {
+ /* Also done with last-ditch probe. */
+ report(state);
+ return;
+ }
+ state->lastditch= 1;
+ state->hop= 255;
+ }
+
+ snprintf(line, sizeof(line),
+ ", { \"hop\":%d, \"result\": [ ", state->hop);
+ add_str(state, line);
+ }
+ state->seq++;
+
+ gettimeofday(&state->xmit_time, NULL);
+
+ if (state->sin6.sin6_family == AF_INET6)
+ {
+ hop= state->hop;
+
+ if (state->do_icmp)
+ {
+ /* Set hop count */
+ setsockopt(base->v6icmp_snd, SOL_IPV6,
+ IPV6_UNICAST_HOPS, &hop, sizeof(hop));
+
+ /* Set/clear don't fragment */
+ on= (state->dont_fragment ? IPV6_PMTUDISC_DO :
+ IPV6_PMTUDISC_DONT);
+ setsockopt(base->v6icmp_snd, IPPROTO_IPV6,
+ IPV6_MTU_DISCOVER, &on, sizeof(on));
+
+ icmp6_hdr= (struct icmp6_hdr *)base->packet;
+ icmp6_hdr->icmp6_type= ICMP6_ECHO_REQUEST;
+ icmp6_hdr->icmp6_code= 0;
+ icmp6_hdr->icmp6_cksum= 0;
+ icmp6_hdr->icmp6_id= htons(base->my_pid);
+ icmp6_hdr->icmp6_seq= htons(state->seq);
+
+ v6info= (struct v6info *)&icmp6_hdr[1];
+ v6info->fuzz= 0;
+ v6info->pid= htonl(base->my_pid);
+ v6info->id= htonl(state->index);
+ v6info->seq= htonl(state->seq);
+ v6info->tv= state->xmit_time;
+
+ len= sizeof(*icmp6_hdr)+sizeof(*v6info);
+
+ if (state->curpacksize < len)
+ state->curpacksize= len;
+ if (state->curpacksize > len)
+ {
+ memset(&base->packet[len], '\0',
+ state->curpacksize-len);
+ strcpy((char *)&base->packet[len], id);
+ len= state->curpacksize;
+ }
+
+ if (state->parismod)
+ {
+ memset(&v6_ph, '\0', sizeof(v6_ph));
+ v6_ph.src= state->loc_sin6.sin6_addr;
+ v6_ph.dst= state->sin6.sin6_addr;
+ v6_ph.len= htonl(len);
+ v6_ph.nxt= IPPROTO_ICMPV6;
+
+ sum= in_cksum_icmp6(&v6_ph,
+ (unsigned short *)base->packet, len);
+
+ /* Avoid 0 */
+ val= state->paris % state->parismod + 1;
+
+ sum= ntohs(sum);
+ usum= sum + (0xffff - val);
+ sum= usum + (usum >> 16);
+
+ v6info->fuzz= htons(sum);
+
+ sum= in_cksum_icmp6(&v6_ph,
+ (unsigned short *)base->packet, len);
+
+#if 0
+ printf(
+ "send_pkt: seq %d, paris %d, cksum= htons(0x%x)\n",
+ state->seq, state->paris,
+ ntohs(sum));
+#endif
+ }
+
+ r= sendto(base->v6icmp_snd, base->packet, len, 0,
+ (struct sockaddr *)&state->sin6,
+ state->socklen);
+
+#if 0
+ { static int doit=1; if (doit && r != -1)
+ { errno= ENOSYS; r= -1; } doit= !doit; }
+#endif
+
+ if (r == -1)
+ {
+ if (errno != EMSGSIZE)
+ {
+ serrno= errno;
+
+ snprintf(line, sizeof(line),
+ "%s{ " DBQ(error) ":" DBQ(sendto failed: %s) " } ] }",
+ state->sent ? " }, " : "",
+ strerror(serrno));
+ add_str(state, line);
+ report(state);
+ return;
+ }
+ }
+ }
+ else
+ {
+ sock= socket(AF_INET6, SOCK_DGRAM, 0);
+ if (sock == -1)
+ {
+ crondlog(DIE9 "socket failed");
+ }
+
+ on= 1;
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on,
+ sizeof(on));
+
+ /* Bind to source addr/port */
+ r= bind(sock,
+ (struct sockaddr *)&state->loc_sin6,
+ state->loc_socklen);
+ if (r == -1)
+ {
+ serrno= errno;
+
+ snprintf(line, sizeof(line),
+ "%s{ " DBQ(error) ":" DBQ(bind failed: %s) " } ] }",
+ state->sent ? " }, " : "",
+ strerror(serrno));
+ add_str(state, line);
+ report(state);
+ close(sock);
+ return;
+ }
+
+ /* Set port */
+ if (state->parismod)
+ {
+ state->sin6.sin6_port= htons(BASE_PORT +
+ (state->paris % state->parismod));
+ }
+ else
+ {
+ state->sin6.sin6_port= htons(BASE_PORT +
+ state->seq);
+ }
+
+ /* Set hop count */
+ setsockopt(sock, SOL_IPV6, IPV6_UNICAST_HOPS,
+ &hop, sizeof(hop));
+
+ /* Set/clear don't fragment */
+ on= (state->dont_fragment ? IPV6_PMTUDISC_DO :
+ IPV6_PMTUDISC_DONT);
+ setsockopt(sock, IPPROTO_IPV6,
+ IPV6_MTU_DISCOVER, &on, sizeof(on));
+
+ v6info= (struct v6info *)base->packet;
+ v6info->fuzz= 0;
+ v6info->pid= htonl(base->my_pid);
+ v6info->id= htonl(state->index);
+ v6info->seq= htonl(state->seq);
+ v6info->tv= state->xmit_time;
+
+#if 0
+ printf(
+"send_pkt: IPv6 UDP: pid = htonl(%d), id = htonl(%d), seq = htonl(%d)\n",
+ ntohl(v6info->pid),
+ ntohl(v6info->id),
+ ntohl(v6info->seq));
+#endif
+
+ len= sizeof(*v6info);
+
+ if (state->curpacksize < len)
+ state->curpacksize= len;
+ if (state->curpacksize > len)
+ {
+ memset(&base->packet[len], '\0',
+ state->curpacksize-len);
+ strcpy((char *)&base->packet[len], id);
+ len= state->curpacksize;
+ }
+
+ r= sendto(sock, base->packet, len, 0,
+ (struct sockaddr *)&state->sin6,
+ state->socklen);
+
+#if 0
+ { static int doit=1; if (doit && r != -1)
+ { errno= ENOSYS; r= -1; } doit= !doit; }
+#endif
+ serrno= errno;
+ close(sock);
+
+ if (r == -1)
+ {
+ if (serrno != EACCES &&
+ serrno != ECONNREFUSED &&
+ serrno != EMSGSIZE)
+ {
+ snprintf(line, sizeof(line),
+ "%s{ " DBQ(error) ":" DBQ(sendto failed: %s) " } ] }",
+ state->sent ? " }, " : "",
+ strerror(serrno));
+ add_str(state, line);
+ report(state);
+ return;
+ }
+ }
+ }
+ }
+ else
+ {
+#if 0
+ printf(
+"send_pkt: sending IPv4 packet, do_icmp %d, parismod %d, index %d, state %p\n",
+ state->do_icmp, state->parismod, state->index, state);
+#endif
+
+ if (state->do_icmp)
+ {
+ hop= state->hop;
+
+ icmp_hdr= (struct icmp *)base->packet;
+ icmp_hdr->icmp_type= ICMP_ECHO;
+ icmp_hdr->icmp_code= 0;
+ icmp_hdr->icmp_cksum= 0;
+ icmp_hdr->icmp_id= htons(state->index);
+ icmp_hdr->icmp_seq= htons(state->seq);
+ icmp_hdr->icmp_data[0]= '\0';
+ icmp_hdr->icmp_data[1]= '\0';
+
+ len= offsetof(struct icmp, icmp_data[2]);
+
+ if (state->curpacksize < len)
+ state->curpacksize= len;
+ if (state->curpacksize > len)
+ {
+ memset(&base->packet[len], '\0',
+ state->curpacksize-len);
+ strcpy((char *)&base->packet[len], id);
+ len= state->curpacksize;
+ }
+
+ if (state->parismod)
+ {
+ sum= in_cksum((unsigned short *)icmp_hdr, len);
+
+ sum= ntohs(sum);
+ usum= sum + (0xffff - state->paris);
+ sum= usum + (usum >> 16);
+
+ icmp_hdr->icmp_data[0]= sum >> 8;
+ icmp_hdr->icmp_data[1]= sum;
+ }
+
+ icmp_hdr->icmp_cksum=
+ in_cksum((unsigned short *)icmp_hdr, len);
+
+#if 0
+ printf(
+ "send_pkt: seq %d, paris %d, icmp_cksum= htons(%d)\n",
+ state->seq, state->paris,
+ ntohs(icmp_hdr->icmp_cksum));
+#endif
+
+ /* Set hop count */
+ setsockopt(base->v4icmp_snd, IPPROTO_IP, IP_TTL,
+ &hop, sizeof(hop));
+
+ /* Set/clear don't fragment */
+ on= (state->dont_fragment ? IP_PMTUDISC_DO :
+ IP_PMTUDISC_DONT);
+ setsockopt(base->v4icmp_snd, IPPROTO_IP,
+ IP_MTU_DISCOVER, &on, sizeof(on));
+
+ r= sendto(base->v4icmp_snd, base->packet, len, 0,
+ (struct sockaddr *)&state->sin6,
+ state->socklen);
+
+#if 0
+ { static int doit=1; if (doit && r != -1)
+ { errno= ENOSYS; r= -1; } doit= !doit; }
+#endif
+
+ if (r == -1)
+ {
+ if (errno != EMSGSIZE)
+ {
+ serrno= errno;
+
+ snprintf(line, sizeof(line),
+ "%s{ " DBQ(error) ":" DBQ(sendto failed: %s) " } ] }",
+ state->sent ? " }, " : "",
+ strerror(serrno));
+ add_str(state, line);
+ report(state);
+ return;
+ }
+ }
+ }
+ else
+ {
+ sock= socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1)
+ {
+ crondlog(DIE9 "socket failed");
+ }
+
+ on= 1;
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on,
+ sizeof(on));
+
+ /* Bind to source addr/port */
+ r= bind(sock,
+ (struct sockaddr *)&state->loc_sin6,
+ state->loc_socklen);
+#if 0
+ { static int doit=1; if (doit && r != -1)
+ { errno= ENOSYS; r= -1; } doit= !doit; }
+#endif
+ if (r == -1)
+ {
+ serrno= errno;
+
+ snprintf(line, sizeof(line),
+ "%s{ " DBQ(error) ":" DBQ(bind failed: %s) " } ] }",
+ state->sent ? " }, " : "",
+ strerror(serrno));
+ add_str(state, line);
+ report(state);
+ close(sock);
+ return;
+ }
+
+ hop= state->hop;
+
+ /* Set port */
+ if (state->parismod)
+ {
+ ((struct sockaddr_in *)&state->sin6)->sin_port=
+ htons(BASE_PORT +
+ (state->paris % state->parismod));
+ }
+ else
+ {
+ ((struct sockaddr_in *)&state->sin6)->sin_port=
+ htons(BASE_PORT + state->seq);
+ }
+
+ base->packet[0]= '\0';
+ base->packet[1]= '\0';
+ len= 2; /* We need to fudge checksum */
+
+ if (state->curpacksize < len)
+ state->curpacksize= len;
+ if (state->curpacksize > len)
+ {
+ memset(&base->packet[len], '\0',
+ state->curpacksize-len);
+ strcpy((char *)&base->packet[len], id);
+ len= state->curpacksize;
+ }
+
+ udp_ph.src= ((struct sockaddr_in *)&state->loc_sin6)->
+ sin_addr;
+ udp_ph.dst= ((struct sockaddr_in *)&state->sin6)->
+ sin_addr;
+ udp_ph.zero= 0;
+ udp_ph.proto= IPPROTO_UDP;
+ udp_ph.len= htons(sizeof(udp)+len);
+ udp.uh_sport=
+ ((struct sockaddr_in *)&state->loc_sin6)->
+ sin_port;
+ udp.uh_dport= ((struct sockaddr_in *)&state->sin6)->
+ sin_port;
+ udp.uh_ulen= udp_ph.len;
+ udp.uh_sum= 0;
+
+ sum= in_cksum_udp(&udp_ph, &udp,
+ (unsigned short *)base->packet, len);
+
+ if (state->parismod)
+ {
+ /* Make sure that the sequence number ends
+ * up in the checksum field. We can't store
+ * 0. So we add 1.
+ */
+ if (state->seq == 0)
+ state->seq++;
+ val= state->seq;
+ }
+ else
+ {
+ /* Use id+1 */
+ val= state->index+1;
+ }
+
+ sum= ntohs(sum);
+ usum= sum + (0xffff - val);
+ sum= usum + (usum >> 16);
+
+ base->packet[0]= sum >> 8;
+ base->packet[1]= sum;
+
+ sum= in_cksum_udp(&udp_ph, &udp,
+ (unsigned short *)base->packet, len);
+
+ /* Set hop count */
+ setsockopt(sock, IPPROTO_IP, IP_TTL,
+ &hop, sizeof(hop));
+
+ /* Set/clear don't fragment */
+ on= (state->dont_fragment ? IP_PMTUDISC_DO :
+ IP_PMTUDISC_DONT);
+ setsockopt(sock, IPPROTO_IP,
+ IP_MTU_DISCOVER, &on, sizeof(on));
+
+ r= sendto(sock, base->packet, len, 0,
+ (struct sockaddr *)&state->sin6,
+ state->socklen);
+
+#if 0
+ { static int doit=0; if (doit && r != -1)
+ { errno= ENOSYS; r= -1; } doit= !doit; }
+#endif
+
+ serrno= errno;
+ close(sock);
+ if (r == -1)
+ {
+ if (serrno != EMSGSIZE)
+ {
+ serrno= errno;
+
+ snprintf(line, sizeof(line),
+ "%s{ " DBQ(error) ":" DBQ(sendto failed: %s) " } ] }",
+ state->sent ? " }, " : "",
+ strerror(serrno));
+ add_str(state, line);
+ report(state);
+ return;
+ }
+ }
+ }
+ }
+
+ if (state->sent)
+ add_str(state, " }, ");
+ add_str(state, "{ ");
+
+ /* Increment packets sent */
+ state->sent++;
+
+ /* Set timer */
+ interval.tv_sec= state->timeout/1000000;
+ interval.tv_usec= state->timeout % 1000000;
+ evtimer_add(&state->timer, &interval);
+
+}
+
+static void do_mpls(struct trtstate *state, unsigned char *packet,
+ size_t size)
+{
+ int o, exp, s, ttl;
+ uint32_t v, label;
+ char line[256];
+
+ add_str(state, ", " DBQ(mpls) ": [");
+
+ for (o= 0; o+4 <= size; o += 4)
+ {
+ v= (ntohl(*(uint32_t *)&packet[o]));
+ label= (v >> MPLS_LABEL_SHIFT);
+ exp= ((v >> MPLS_EXT_SHIFT) & MPLS_EXT_MASK);
+ s= !!(v & MPLS_S_BIT);
+ ttl= (v & MPLS_TTL_MASK);
+
+ snprintf(line, sizeof(line), "%s { " DBQ(label) ":%d, "
+ DBQ(exp) ":%d, " DBQ(s) ":%d, " DBQ(ttl) ":%d }",
+ o == 0 ? "" : ",",
+ label, exp, s, ttl);
+ add_str(state, line);
+ }
+
+ add_str(state, " ]");
+}
+
+static void do_icmp_multi(struct trtstate *state,
+ unsigned char *packet, size_t size, int pre_rfc4884)
+{
+ int o, len;
+ uint16_t cksum;
+ uint8_t class, ctype, version;
+ char line[256];
+
+ if (size < 4)
+ {
+ printf("do_icmp_multi: not enough for ICMP extension header\n");
+ return;
+ }
+ cksum= in_cksum((unsigned short *)packet, size);
+ if (cksum != 0)
+ {
+ /* There is also anoption for a zero checksum. */
+ if (!pre_rfc4884)
+ printf("do_icmp_multi: bad checksum\n");
+ return;
+ }
+
+ version= (*(uint8_t *)packet >> ICMPEXT_VERSION_SHIFT);
+
+ snprintf(line, sizeof(line), ", " DBQ(icmpext) ": { "
+ DBQ(version) ":%d" ", " DBQ(rfc4884) ":%d",
+ version, !pre_rfc4884);
+ add_str(state, line);
+
+ add_str(state, ", " DBQ(obj) ": [");
+
+ o= 4;
+ while (o+4 < size)
+ {
+ len= ntohs(*(uint16_t *)&packet[o]);
+ class= packet[o+2];
+ ctype= packet[o+3];
+
+ snprintf(line, sizeof(line), "%s { " DBQ(class) ":%d, "
+ DBQ(type) ":%d",
+ o == 4 ? "" : ",", class, ctype);
+ add_str(state, line);
+
+ if (len < 4 || o+len > size)
+ {
+ add_str(state, " }");
+ printf("do_icmp_multi: bad len %d\n", len);
+ break;
+ }
+ if (class == ICMPEXT_MPLS && ctype == ICMPEXT_MPLS_IN)
+ do_mpls(state, packet+o+4, len-4);
+ o += len;
+
+ add_str(state, " }");
+ }
+
+ add_str(state, " ] }");
+}
+
+static void ready_callback4(int __attribute((unused)) unused,
+ const short __attribute((unused)) event, void *s)
+{
+ struct trtbase *base;
+ struct trtstate *state;
+ int hlen, ehlen, ind, nextmtu, late, isDup, icmp_prefixlen, offset;
+ unsigned seq;
+ ssize_t nrecv;
+ socklen_t slen;
+ struct ip *ip, *eip;
+ struct icmp *icmp, *eicmp;
+ struct udphdr *eudp;
+ double ms;
+ struct timeval now, interval;
+ struct sockaddr_in remote;
+ char line[80];
+
+ gettimeofday(&now, NULL);
+
+ base= s;
+
+ slen= sizeof(remote);
+ nrecv= recvfrom(base->v4icmp_rcv, base->packet, sizeof(base->packet),
+ MSG_DONTWAIT, (struct sockaddr *)&remote, &slen);
+ if (nrecv == -1)
+ {
+ /* Strange, read error */
+ printf("ready_callback4: read error '%s'\n", strerror(errno));
+ return;
+ }
+ // printf("ready_callback4: got packet\n");
+
+ ip= (struct ip *)base->packet;
+ hlen= ip->ip_hl*4;
+
+ if (nrecv < hlen + ICMP_MINLEN || ip->ip_hl < 5)
+ {
+ /* Short packet */
+ printf("ready_callback4: too short %d\n", (int)nrecv);
+ return;
+ }
+
+ icmp= (struct icmp *)(base->packet+hlen);
+
+ if (icmp->icmp_type == ICMP_TIME_EXCEEDED ||
+ icmp->icmp_type == ICMP_DEST_UNREACH)
+ {
+ eip= &icmp->icmp_ip;
+ ehlen= eip->ip_hl*4;
+
+ /* Make sure the packet we have is big enough */
+ if (nrecv < hlen + ICMP_MINLEN + ehlen || eip->ip_hl < 5)
+ {
+ printf("ready_callback4: too short %d\n", (int)nrecv);
+ return;
+ }
+
+ if (eip->ip_p == IPPROTO_UDP)
+ {
+ /* Now check if there is also a UDP header in the
+ * packet
+ */
+ if (nrecv < hlen + ICMP_MINLEN + ehlen + sizeof(*eudp))
+ {
+ printf("ready_callback4: too short %d\n",
+ (int)nrecv);
+ return;
+ }
+
+ eudp= (struct udphdr *)((char *)eip+ehlen);
+
+ /* We store the id in the source port.
+ */
+ ind= ntohs(eudp->uh_sport) - SRC_BASE_PORT;
+
+ state= NULL;
+ if (ind >= 0 && ind < base->tabsiz)
+ state= base->table[ind];
+ if (state && state->sin6.sin6_family != AF_INET)
+ state= NULL;
+ if (state && state->do_icmp)
+ state= NULL;
+
+ if (!state)
+ {
+ /* Nothing here */
+ // printf("ready_callback4: no state\n");
+ return;
+ }
+
+#if 0
+ printf("ready_callback4: from %s",
+ inet_ntoa(remote.sin_addr));
+ printf(" for %s hop %d\n",
+ inet_ntoa(((struct sockaddr_in *)
+ &state->sin6)->sin_addr), state->hop);
+#endif
+
+ if (!state->busy)
+ {
+#if 0
+ printf(
+ "ready_callback4: index (%d) is not busy\n",
+ ind);
+#endif
+ return;
+ }
+
+ late= 0;
+ isDup= 0;
+ if (state->parismod)
+ {
+ /* Sequence number is in checksum field */
+ seq= ntohs(eudp->uh_sum);
+
+ /* Unfortunately, cheap home routers may
+ * forget to restore the checksum field
+ * when they are doing NAT. Ignore the
+ * sequence number if it seems wrong.
+ */
+ if (seq > state->seq)
+ seq= state->seq;
+ }
+ else
+ {
+ /* Sequence number is in destination field */
+ seq= ntohs(eudp->uh_dport)-BASE_PORT;
+ }
+
+ if (seq != state->seq)
+ {
+ if (seq > state->seq)
+ {
+#if 0
+ printf(
+ "ready_callback4: mismatch for seq, got 0x%x, expected 0x%x (for %s)\n",
+ seq, state->seq,
+ state->hostname);
+#endif
+ return;
+ }
+ late= 1;
+
+ snprintf(line, sizeof(line), "\"late\":%d",
+ state->seq-seq);
+ add_str(state, line);
+ }
+ else if (state->gotresp)
+ {
+ isDup= 1;
+ add_str(state, " }, { \"dup\":true");
+ }
+
+ if (!late && !isDup)
+ state->last_response_hop= state->hop;
+
+ ms= (now.tv_sec-state->xmit_time.tv_sec)*1000 +
+ (now.tv_usec-state->xmit_time.tv_usec)/1e3;
+
+ snprintf(line, sizeof(line), "%s\"from\":\"%s\"",
+ (late || isDup) ? ", " : "",
+ inet_ntoa(remote.sin_addr));
+ add_str(state, line);
+ snprintf(line, sizeof(line),
+ ", \"ttl\":%d, \"size\":%d",
+ ip->ip_ttl, (int)nrecv);
+ add_str(state, line);
+ if (!late)
+ {
+ snprintf(line, sizeof(line), ", \"rtt\":%.3f",
+ ms);
+ add_str(state, line);
+ }
+ if (eip->ip_ttl != 1)
+ {
+ snprintf(line, sizeof(line), ", \"ittl\":%d",
+ eip->ip_ttl);
+ add_str(state, line);
+ }
+
+ if (memcmp(&eip->ip_src,
+ &((struct sockaddr_in *)&state->loc_sin6)->
+ sin_addr, sizeof(eip->ip_src)) != 0)
+ {
+ printf("ready_callback4: changed source %s\n",
+ inet_ntoa(eip->ip_src));
+ }
+ if (memcmp(&eip->ip_dst,
+ &((struct sockaddr_in *)&state->sin6)->
+ sin_addr, sizeof(eip->ip_dst)) != 0)
+ {
+ snprintf(line, sizeof(line),
+ ", \"edst\":\"%s\"",
+ inet_ntoa(eip->ip_dst));
+ add_str(state, line);
+ }
+ if (memcmp(&ip->ip_dst,
+ &((struct sockaddr_in *)&state->loc_sin6)->
+ sin_addr, sizeof(eip->ip_src)) != 0)
+ {
+ printf("ready_callback4: weird destination %s\n",
+ inet_ntoa(ip->ip_dst));
+ }
+
+#if 0
+ printf("ready_callback4: from %s, ttl %d",
+ inet_ntoa(remote.sin_addr), ip->ip_ttl);
+ printf(" for %s hop %d\n",
+ inet_ntoa(((struct sockaddr_in *)
+ &state->sin6)->sin_addr), state->hop);
+#endif
+
+ if (icmp->icmp_type == ICMP_TIME_EXCEEDED)
+ {
+ if (!late)
+ state->not_done= 1;
+ }
+ else if (icmp->icmp_type == ICMP_DEST_UNREACH)
+ {
+ if (!late)
+ state->done= 1;
+ switch(icmp->icmp_code)
+ {
+ case ICMP_UNREACH_NET:
+ add_str(state, ", \"err\":\"N\"");
+ break;
+ case ICMP_UNREACH_HOST:
+ add_str(state, ", \"err\":\"H\"");
+ break;
+ case ICMP_UNREACH_PROTOCOL:
+ add_str(state, ", \"err\":\"P\"");
+ break;
+ case ICMP_UNREACH_PORT:
+ break;
+ case ICMP_UNREACH_NEEDFRAG:
+ nextmtu= ntohs(icmp->icmp_nextmtu);
+ snprintf(line, sizeof(line),
+ ", \"mtu\":%d",
+ nextmtu);
+ add_str(state, line);
+ if (!late && nextmtu >= sizeof(*ip)+
+ sizeof(*eudp))
+ {
+ nextmtu -= sizeof(*ip)+
+ sizeof(*eudp);
+ if (nextmtu <
+ state->curpacksize)
+ {
+ state->curpacksize=
+ nextmtu;
+ }
+ }
+printf("curpacksize: %d\n", state->curpacksize);
+ if (!late)
+ state->not_done= 1;
+ break;
+ case ICMP_UNREACH_FILTER_PROHIB:
+ add_str(state, ", \"err\":\"A\"");
+ break;
+ default:
+ snprintf(line, sizeof(line),
+ ", \"err\":%d",
+ icmp->icmp_code);
+ add_str(state, line);
+ break;
+ }
+ }
+ }
+ else if (eip->ip_p == IPPROTO_ICMP)
+ {
+ /* Now check if there is also an ICMP header in the
+ * packet
+ */
+ if (nrecv < hlen + ICMP_MINLEN + ehlen +
+ offsetof(struct icmp, icmp_data[0]))
+ {
+ printf("ready_callback4: too short %d\n",
+ (int)nrecv);
+ return;
+ }
+
+ eicmp= (struct icmp *)((char *)eip+ehlen);
+
+ if (eicmp->icmp_type != ICMP_ECHO ||
+ eicmp->icmp_code != 0)
+ {
+ printf("ready_callback4: not ECHO\n");
+ return;
+ }
+
+ ind= ntohs(eicmp->icmp_id);
+
+ if (ind >= base->tabsiz)
+ {
+ /* Out of range */
+#if 0
+ printf(
+ "ready_callback4: index out of range (%d)\n",
+ ind);
+#endif
+ return;
+ }
+
+ state= base->table[ind];
+ if (!state)
+ {
+ /* Nothing here */
+ printf(
+ "ready_callback4: nothing at index (%d)\n",
+ ind);
+ return;
+ }
+
+ if (state->sin6.sin6_family != AF_INET)
+ {
+ // printf("ready_callback4: bad family\n");
+ return;
+ }
+
+ if (!state->do_icmp)
+ {
+ printf(
+ "ready_callback4: index (%d) is not doing ICMP\n",
+ ind);
+ return;
+ }
+ if (!state->busy)
+ {
+printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family);
+ printf(
+ "ready_callback4: index (%d) is not busy\n",
+ ind);
+ return;
+ }
+
+ if (state->parismod &&
+ ntohs(eicmp->icmp_cksum) != state->paris)
+ {
+ printf(
+ "ready_callback4: mismatch for paris, got 0x%x, expected 0x%x (%s)\n",
+ ntohs(eicmp->icmp_cksum), state->paris,
+ state->hostname);
+ }
+
+ late= 0;
+ isDup= 0;
+ seq= ntohs(eicmp->icmp_seq);
+ if (seq != state->seq)
+ {
+ if (seq > state->seq)
+ {
+#if 0
+ printf(
+ "ready_callback4: mismatch for seq, got 0x%x, expected 0x%x (for %s)\n",
+ seq, state->seq,
+ state->hostname);
+#endif
+ return;
+ }
+ late= 1;
+
+ snprintf(line, sizeof(line), "\"late\":%d",
+ state->seq-seq);
+ add_str(state, line);
+ }
+ else if (state->gotresp)
+ {
+ isDup= 1;
+ add_str(state, " }, { \"dup\":true");
+ }
+
+ if (!late && !isDup)
+ state->last_response_hop= state->hop;
+
+ ms= (now.tv_sec-state->xmit_time.tv_sec)*1000 +
+ (now.tv_usec-state->xmit_time.tv_usec)/1e3;
+
+ snprintf(line, sizeof(line), "%s\"from\":\"%s\"",
+ (late || isDup) ? ", " : "",
+ inet_ntoa(remote.sin_addr));
+ add_str(state, line);
+ snprintf(line, sizeof(line),
+ ", \"ttl\":%d, \"size\":%d",
+ ip->ip_ttl, (int)nrecv);
+ add_str(state, line);
+ if (!late)
+ {
+ snprintf(line, sizeof(line), ", \"rtt\":%.3f",
+ ms);
+ add_str(state, line);
+ }
+
+ if (eip->ip_ttl != 1)
+ {
+ snprintf(line, sizeof(line), ", \"ittl\":%d",
+ eip->ip_ttl);
+ add_str(state, line);
+ }
+
+ if (memcmp(&eip->ip_src,
+ &((struct sockaddr_in *)&state->loc_sin6)->
+ sin_addr, sizeof(eip->ip_src)) != 0)
+ {
+ printf("ready_callback4: changed source %s\n",
+ inet_ntoa(eip->ip_src));
+ }
+ if (memcmp(&eip->ip_dst,
+ &((struct sockaddr_in *)&state->sin6)->
+ sin_addr, sizeof(eip->ip_dst)) != 0)
+ {
+ snprintf(line, sizeof(line),
+ ", \"edst\":\"%s\"",
+ inet_ntoa(eip->ip_dst));
+ add_str(state, line);
+ }
+ if (memcmp(&ip->ip_dst,
+ &((struct sockaddr_in *)&state->loc_sin6)->
+ sin_addr, sizeof(eip->ip_src)) != 0)
+ {
+ printf("ready_callback4: weird destination %s\n",
+ inet_ntoa(ip->ip_dst));
+ }
+
+#if 0
+ printf("ready_callback4: from %s, ttl %d",
+ inet_ntoa(remote.sin_addr), ip->ip_ttl);
+ printf(" for %s hop %d\n",
+ inet_ntoa(((struct sockaddr_in *)
+ &state->sin6)->sin_addr), state->hop);
+#endif
+
+ if (icmp->icmp_type == ICMP_TIME_EXCEEDED)
+ {
+ if (!late && !isDup)
+ state->not_done= 1;
+ }
+ else if (icmp->icmp_type == ICMP_DEST_UNREACH)
+ {
+ if (!late)
+ state->done= 1;
+ switch(icmp->icmp_code)
+ {
+ case ICMP_UNREACH_NET:
+ add_str(state, ", \"err\":\"N\"");
+ break;
+ case ICMP_UNREACH_HOST:
+ add_str(state, ", \"err\":\"H\"");
+ break;
+ case ICMP_UNREACH_PROTOCOL:
+ add_str(state, ", \"err\":\"P\"");
+ break;
+ case ICMP_UNREACH_PORT:
+ add_str(state, ", \"err\":\"p\"");
+ break;
+ case ICMP_UNREACH_NEEDFRAG:
+ nextmtu= ntohs(icmp->icmp_nextmtu);
+ snprintf(line, sizeof(line),
+ ", \"mtu\":%d",
+ nextmtu);
+ add_str(state, line);
+ if (!late && nextmtu >= sizeof(*ip))
+ {
+ nextmtu -= sizeof(*ip);
+ if (nextmtu <
+ state->curpacksize)
+ {
+ state->curpacksize=
+ nextmtu;
+ }
+ }
+ if (!late)
+ state->not_done= 1;
+ break;
+ case ICMP_UNREACH_FILTER_PROHIB:
+ add_str(state, ", \"err\":\"A\"");
+ break;
+ default:
+ snprintf(line, sizeof(line),
+ ", \"err\":%d",
+ icmp->icmp_code);
+ add_str(state, line);
+ break;
+ }
+ }
+ else
+ {
+ printf("imcp type %d\n", icmp->icmp_type);
+ }
+ }
+ else
+ {
+ printf("ready_callback4: not UDP or ICMP (%d\n",
+ eip->ip_p);
+ return;
+ }
+
+ /* RFC-4884, Multi-Part ICMP messages */
+ icmp_prefixlen= (ntohs(icmp->icmp_pmvoid) & 0xff) * 4;
+ if (icmp_prefixlen != 0)
+ {
+
+ printf("icmp_pmvoid: 0x%x for %s\n", icmp->icmp_pmvoid, state->hostname);
+ printf("icmp_prefixlen: 0x%x for %s\n", icmp_prefixlen, inet_ntoa(remote.sin_addr));
+ offset= hlen + ICMP_MINLEN + icmp_prefixlen;
+ if (nrecv > offset)
+ {
+ do_icmp_multi(state, base->packet+offset,
+ nrecv-offset, 0 /*!pre_rfc4884*/);
+ }
+ else
+ {
+ printf(
+ "ready_callback4: too short %d (Multi-Part ICMP)\n",
+ (int)nrecv);
+ }
+ }
+ else if (nrecv > hlen + ICMP_MINLEN + 128)
+ {
+ /* Try old style extensions */
+ icmp_prefixlen= 128;
+ offset= hlen + ICMP_MINLEN + icmp_prefixlen;
+ if (nrecv > offset)
+ {
+ do_icmp_multi(state, base->packet+offset,
+ nrecv-offset, 1 /*pre_rfc4884*/);
+ }
+ else
+ {
+ printf(
+ "ready_callback4: too short %d (Multi-Part ICMP)\n",
+ (int)nrecv);
+ }
+ }
+
+ if (late)
+ add_str(state, " }, { ");
+
+ if (!late && !isDup)
+ {
+ if (state->duptimeout)
+ {
+ state->gotresp= 1;
+ interval.tv_sec= state->duptimeout/1000000;
+ interval.tv_usec= state->duptimeout % 1000000;
+ evtimer_add(&state->timer, &interval);
+ }
+ else
+ send_pkt(state);
+ }
+ }
+ else if (icmp->icmp_type == ICMP_ECHOREPLY)
+ {
+ if (icmp->icmp_code != 0)
+ {
+ printf("ready_callback4: not proper ECHO REPLY\n");
+ return;
+ }
+
+ ind= ntohs(icmp->icmp_id);
+
+ if (ind >= base->tabsiz)
+ {
+ /* Out of range */
+#if 0
+ printf(
+ "ready_callback4: index out of range (%d)\n",
+ ind);
+#endif
+ return;
+ }
+
+ state= base->table[ind];
+ if (!state)
+ {
+ /* Nothing here */
+ printf(
+ "ready_callback4: nothing at index (%d)\n",
+ ind);
+ return;
+ }
+
+ if (state->sin6.sin6_family != AF_INET)
+ {
+ // printf("ready_callback4: bad family\n");
+ return;
+ }
+
+ if (!state->busy)
+ {
+printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family);
+ printf(
+ "ready_callback4: index (%d) is not busy\n",
+ ind);
+ return;
+ }
+
+ late= 0;
+ isDup= 0;
+ seq= ntohs(icmp->icmp_seq);
+ if (seq != state->seq)
+ {
+ if (seq > state->seq)
+ {
+#if 0
+ printf(
+"ready_callback4: mismatch for seq, got 0x%x, expected 0x%x, for %s\n",
+ seq, state->seq, state->hostname);
+#endif
+ return;
+ }
+ late= 1;
+
+ snprintf(line, sizeof(line), "\"late\":%d",
+ state->seq-seq);
+ add_str(state, line);
+ }
+ else if (state->gotresp)
+ {
+ isDup= 1;
+ add_str(state, " }, { \"dup\":true");
+ }
+
+ if (memcmp(&ip->ip_dst,
+ &((struct sockaddr_in *)&state->loc_sin6)->
+ sin_addr, sizeof(eip->ip_src)) != 0)
+ {
+ printf("ready_callback4: weird destination %s\n",
+ inet_ntoa(ip->ip_dst));
+ }
+
+ ms= (now.tv_sec-state->xmit_time.tv_sec)*1000 +
+ (now.tv_usec-state->xmit_time.tv_usec)/1e3;
+
+ snprintf(line, sizeof(line), "%s\"from\":\"%s\"",
+ (late || isDup) ? ", " : "",
+ inet_ntoa(remote.sin_addr));
+ add_str(state, line);
+ snprintf(line, sizeof(line), ", \"ttl\":%d, \"size\":%d",
+ ip->ip_ttl, (int)nrecv);
+ add_str(state, line);
+ if (!late)
+ {
+ snprintf(line, sizeof(line), ", \"rtt\":%.3f", ms);
+ add_str(state, line);
+ }
+
+#if 0
+ printf("ready_callback4: from %s, ttl %d",
+ inet_ntoa(remote.sin_addr), ip->ip_ttl);
+ printf(" for %s hop %d\n",
+ inet_ntoa(((struct sockaddr_in *)
+ &state->sin6)->sin_addr), state->hop);
+#endif
+
+ /* Done */
+ state->done= 1;
+
+ if (late)
+ add_str(state, " }, { ");
+
+ if (!late && !isDup)
+ {
+ if (state->duptimeout)
+ {
+ state->gotresp= 1;
+ interval.tv_sec= state->duptimeout/1000000;
+ interval.tv_usec= state->duptimeout % 1000000;
+ evtimer_add(&state->timer, &interval);
+ }
+ else
+ send_pkt(state);
+ }
+
+ return;
+ }
+ else if (icmp->icmp_type == ICMP_ECHO ||
+ icmp->icmp_type == ICMP_ROUTERADVERT)
+ {
+ /* No need to do anything */
+ }
+ else
+ {
+ printf("ready_callback4: got type %d\n", icmp->icmp_type);
+ return;
+ }
+}
+
+static void ready_callback6(int __attribute((unused)) unused,
+ const short __attribute((unused)) event, void *s)
+{
+ ssize_t nrecv;
+ int ind, rcvdttl, late, isDup, nxt, icmp_prefixlen, offset;
+ unsigned nextmtu, seq;
+ size_t ehdrsiz, siz;
+ struct trtbase *base;
+ struct trtstate *state;
+ struct ip6_hdr *eip;
+ struct ip6_frag *frag;
+ struct icmp6_hdr *icmp, *eicmp;
+ struct udphdr *eudp;
+ struct v6info *v6info;
+ struct cmsghdr *cmsgptr;
+ void *ptr;
+ double ms;
+ struct timeval now;
+ struct sockaddr_in6 remote;
+ struct in6_addr dstaddr;
+ struct msghdr msg;
+ struct iovec iov[1];
+ struct timeval interval;
+ char buf[INET6_ADDRSTRLEN];
+ char line[80];
+ char cmsgbuf[256];
+
+ gettimeofday(&now, NULL);
+
+ base= s;
+
+ iov[0].iov_base= base->packet;
+ iov[0].iov_len= sizeof(base->packet);
+ msg.msg_name= &remote;
+ msg.msg_namelen= sizeof(remote);
+ msg.msg_iov= iov;
+ msg.msg_iovlen= 1;
+ msg.msg_control= cmsgbuf;
+ msg.msg_controllen= sizeof(cmsgbuf);
+ msg.msg_flags= 0; /* Not really needed */
+
+ nrecv= recvmsg(base->v6icmp_rcv, &msg, MSG_DONTWAIT);
+ if (nrecv == -1)
+ {
+ /* Strange, read error */
+ printf("ready_callback6: read error '%s'\n", strerror(errno));
+ return;
+ }
+
+ rcvdttl= -42; /* To spot problems */
+ memset(&dstaddr, '\0', sizeof(dstaddr));
+ for (cmsgptr= CMSG_FIRSTHDR(&msg); cmsgptr;
+ cmsgptr= CMSG_NXTHDR(&msg, cmsgptr))
+ {
+ if (cmsgptr->cmsg_len == 0)
+ break; /* Can this happen? */
+ if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
+ cmsgptr->cmsg_type == IPV6_HOPLIMIT)
+ {
+ rcvdttl= *(int *)CMSG_DATA(cmsgptr);
+ }
+ if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
+ cmsgptr->cmsg_type == IPV6_PKTINFO)
+ {
+ dstaddr= ((struct in6_pktinfo *)
+ CMSG_DATA(cmsgptr))->ipi6_addr;
+ }
+ }
+
+ if (nrecv < sizeof(*icmp))
+ {
+ /* Short packet */
+#if 0
+ printf("ready_callback6: too short %d (icmp)\n", (int)nrecv);
+#endif
+ return;
+ }
+
+ icmp= (struct icmp6_hdr *)&base->packet;
+
+ if (icmp->icmp6_type == ICMP6_DST_UNREACH ||
+ icmp->icmp6_type == ICMP6_PACKET_TOO_BIG ||
+ icmp->icmp6_type == ICMP6_TIME_EXCEEDED)
+ {
+ eip= (struct ip6_hdr *)&icmp[1];
+
+ /* Make sure the packet we have is big enough */
+ if (nrecv < sizeof(*icmp) + sizeof(*eip))
+ {
+#if 0
+ printf("ready_callback6: too short %d (icmp_ip)\n",
+ (int)nrecv);
+#endif
+ return;
+ }
+
+ /* Make sure we have UDP or ICMP or a fragment header */
+ if (eip->ip6_nxt == IPPROTO_FRAGMENT ||
+ eip->ip6_nxt == IPPROTO_UDP ||
+ eip->ip6_nxt == IPPROTO_ICMPV6)
+ {
+ ehdrsiz= 0;
+ frag= NULL;
+ nxt= eip->ip6_nxt;
+ if (nxt == IPPROTO_FRAGMENT)
+ {
+ /* Make sure the fragment header is completely
+ * there.
+ */
+ if (nrecv < sizeof(*icmp) + sizeof(*eip)
+ + sizeof(*frag))
+ {
+#if 0
+ printf(
+ "ready_callback6: too short %d (icmp+ip+frag)\n",
+ (int)nrecv);
+#endif
+ return;
+ }
+ frag= (struct ip6_frag *)&eip[1];
+ if ((ntohs(frag->ip6f_offlg) & ~3) != 0)
+ {
+ /* Not first fragment, just ignore
+ * it.
+ */
+ return;
+ }
+ ehdrsiz= sizeof(*frag);
+ nxt= frag->ip6f_nxt;
+ }
+
+ if (nxt == IPPROTO_UDP)
+ ehdrsiz += sizeof(*eudp);
+ else
+ ehdrsiz += sizeof(*eicmp);
+
+ /* Now check if there is also a header in the
+ * packet.
+ */
+ if (nrecv < sizeof(*icmp) + sizeof(*eip)
+ + ehdrsiz + sizeof(*v6info))
+ {
+#if 0
+ printf(
+ "ready_callback6: too short %d (all) from %s\n",
+ (int)nrecv, inet_ntop(AF_INET6,
+ &remote.sin6_addr, buf, sizeof(buf)));
+#endif
+ return;
+ }
+
+ eudp= NULL;
+ eicmp= NULL;
+ ptr= (frag ? (void *)&frag[1] : (void *)&eip[1]);
+ if (nxt == IPPROTO_UDP)
+ {
+ eudp= (struct udphdr *)ptr;
+ v6info= (struct v6info *)&eudp[1];
+ }
+ else
+ {
+ eicmp= (struct icmp6_hdr *)ptr;
+ v6info= (struct v6info *)&eicmp[1];
+ }
+
+#if 0
+ printf(
+"ready_callback6: pid = htonl(%d), id = htonl(%d), seq = htonl(%d)\n",
+ ntohl(v6info->pid),
+ ntohl(v6info->id),
+ ntohl(v6info->seq));
+#endif
+
+ if (ntohl(v6info->pid) != base->my_pid)
+ {
+ /* From a different process */
+ return;
+ }
+
+ ind= ntohl(v6info->id);
+
+ state= NULL;
+ if (ind >= 0 && ind < base->tabsiz)
+ state= base->table[ind];
+
+ if (state && state->sin6.sin6_family != AF_INET6)
+ state= NULL;
+
+ if (state)
+ {
+ if ((eudp && state->do_icmp) ||
+ (eicmp && !state->do_icmp))
+ {
+ state= NULL;
+ }
+ }
+
+ if (!state)
+ {
+ /* Nothing here */
+ return;
+ }
+
+#if 0
+ printf("ready_callback6: from %s",
+ inet_ntop(AF_INET6, &remote.sin6_addr,
+ buf, sizeof(buf)));
+ printf(" for %s hop %d\n",
+ inet_ntop(AF_INET6, &state->sin6.sin6_addr,
+ buf, sizeof(buf)), state->hop);
+#endif
+
+ if (!state->busy)
+ {
+printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family);
+ printf(
+ "ready_callback6: index (%d) is not busy\n",
+ ind);
+ return;
+ }
+
+ late= 0;
+ isDup= 0;
+ seq= ntohl(v6info->seq);
+ if (seq != state->seq)
+ {
+ if (seq > state->seq)
+ {
+ printf(
+ "ready_callback6: mismatch for seq, got 0x%x, expected 0x%x\n",
+ ntohl(v6info->seq),
+ state->seq);
+ return;
+ }
+ late= 1;
+
+ snprintf(line, sizeof(line), "\"late\":%d",
+ state->seq-seq);
+ add_str(state, line);
+ } else if (state->gotresp)
+ {
+ isDup= 1;
+ add_str(state, " }, { \"dup\":true");
+ }
+
+ if (!late && !isDup)
+ state->last_response_hop= state->hop;
+
+ if (memcmp(&eip->ip6_src,
+ &state->loc_sin6.sin6_addr,
+ sizeof(eip->ip6_src)) != 0)
+ {
+ printf("ready_callback6: changed source %s\n",
+ inet_ntop(AF_INET6, &eip->ip6_src,
+ buf, sizeof(buf)));
+ }
+ if (memcmp(&eip->ip6_dst,
+ &state->sin6.sin6_addr,
+ sizeof(eip->ip6_dst)) != 0)
+ {
+ printf(
+ "ready_callback6: changed destination %s for %s\n",
+ inet_ntop(AF_INET6, &eip->ip6_dst,
+ buf, sizeof(buf)),
+ state->hostname);
+ }
+ if (memcmp(&dstaddr,
+ &state->loc_sin6.sin6_addr,
+ sizeof(dstaddr)) != 0)
+ {
+ printf("ready_callback6: weird destination %s\n",
+ inet_ntop(AF_INET6, &dstaddr,
+ buf, sizeof(buf)));
+ }
+
+ if (eicmp && state->parismod &&
+ ntohs(eicmp->icmp6_cksum) !=
+ state->paris % state->parismod + 1)
+ {
+ printf(
+ "ready_callback6: got checksum 0x%x, expected 0x%x\n",
+ ntohs(eicmp->icmp6_cksum),
+ state->paris % state->parismod + 1);
+ }
+
+ if (!late)
+ {
+ ms= (now.tv_sec-state->xmit_time.tv_sec)*1000 +
+ (now.tv_usec-state->xmit_time.tv_usec)/
+ 1e3;
+ }
+ else
+ {
+ ms= (now.tv_sec-v6info->tv.tv_sec)*1000 +
+ (now.tv_usec-v6info->tv.tv_usec)/
+ 1e3;
+ }
+
+ snprintf(line, sizeof(line), "%s\"from\":\"%s\"",
+ (late || isDup) ? ", " : "",
+ inet_ntop(AF_INET6, &remote.sin6_addr,
+ buf, sizeof(buf)));
+ add_str(state, line);
+ snprintf(line, sizeof(line),
+ ", \"ttl\":%d, \"rtt\":%.3f, \"size\":%d",
+ rcvdttl, ms, (int)nrecv);
+ add_str(state, line);
+ if (eip->ip6_hops != 1)
+ {
+ snprintf(line, sizeof(line), ", \"ittl\":%d",
+ eip->ip6_hops);
+ add_str(state, line);
+ }
+
+#if 0
+ printf("ready_callback6: from %s, ttl %d",
+ inet_ntop(AF_INET6, &remote.sin6_addr, buf,
+ sizeof(buf)), rcvdttl);
+ printf(" for %s hop %d\n",
+ inet_ntop(AF_INET6, &state->sin6.sin6_addr, buf,
+ sizeof(buf)), state->hop);
+#endif
+
+ if (icmp->icmp6_type == ICMP6_TIME_EXCEEDED)
+ {
+ if (!late && !isDup)
+ state->not_done= 1;
+ }
+ else if (icmp->icmp6_type == ICMP6_PACKET_TOO_BIG)
+ {
+ nextmtu= ntohl(icmp->icmp6_mtu);
+ snprintf(line, sizeof(line), ", \"mtu\":%d",
+ nextmtu);
+ add_str(state, line);
+ siz= sizeof(*eip);
+ if (eudp)
+ siz += sizeof(*eudp);
+ if (!late && nextmtu >= siz)
+ {
+ nextmtu -= siz;
+ if (nextmtu < state->curpacksize)
+ state->curpacksize= nextmtu;
+ }
+ if (!late)
+ state->not_done= 1;
+ }
+ else if (icmp->icmp6_type == ICMP6_DST_UNREACH)
+ {
+ if (!late)
+ state->done= 1;
+ switch(icmp->icmp6_code)
+ {
+ case ICMP6_DST_UNREACH_NOROUTE:
+ add_str(state, ", \"err\":\"N\"");
+ break;
+ case ICMP6_DST_UNREACH_ADDR:
+ add_str(state, ", \"err\":\"H\"");
+ break;
+ case ICMP6_DST_UNREACH_NOPORT:
+ break;
+ case ICMP6_DST_UNREACH_ADMIN:
+ add_str(state, ", \"err\":\"A\"");
+ break;
+ default:
+ snprintf(line, sizeof(line),
+ ", \"err\":%d",
+ icmp->icmp6_code);
+ add_str(state, line);
+ break;
+ }
+ }
+ }
+ else
+ {
+ printf(
+ "ready_callback6: not UDP or ICMP (ip6_nxt = %d)\n",
+ eip->ip6_nxt);
+ return;
+ }
+
+ /* RFC-4884, Multi-Part ICMP messages */
+ icmp_prefixlen= icmp->icmp6_data8[0] * 8;
+ if (icmp_prefixlen != 0)
+ {
+
+ printf("icmp6_data8[0]: 0x%x for %s\n", icmp->icmp6_data8[0], state->hostname);
+ printf("icmp_prefixlen: 0x%x for %s\n", icmp_prefixlen, inet_ntop(AF_INET6, &state->sin6.sin6_addr, buf, sizeof(buf)));
+ offset= sizeof(*icmp) + icmp_prefixlen;
+ if (nrecv > offset)
+ {
+ do_icmp_multi(state, base->packet+offset,
+ nrecv-offset, 0 /*!pre_rfc4884*/);
+ }
+ else
+ {
+#if 0
+ printf(
+ "ready_callback6: too short %d (Multi-Part ICMP)\n",
+ (int)nrecv);
+#endif
+ }
+ }
+ else if (nrecv > 128)
+ {
+ /* Try old style extensions */
+ icmp_prefixlen= 128;
+ offset= sizeof(*icmp) + icmp_prefixlen;
+ if (nrecv > offset)
+ {
+ do_icmp_multi(state, base->packet+offset,
+ nrecv-offset, 1 /*pre_rfc4884*/);
+ }
+ else
+ {
+ printf(
+ "ready_callback6: too short %d (Multi-Part ICMP)\n",
+ (int)nrecv);
+ }
+ }
+
+ if (late)
+ add_str(state, " }, { ");
+
+ if (!late && !isDup)
+ {
+ if (state->duptimeout)
+ {
+ state->gotresp= 1;
+ interval.tv_sec= state->duptimeout/1000000;
+ interval.tv_usec= state->duptimeout % 1000000;
+ evtimer_add(&state->timer, &interval);
+ }
+ else
+ send_pkt(state);
+ }
+ }
+ else if (icmp->icmp6_type == ICMP6_ECHO_REPLY)
+ {
+ eip= NULL;
+
+ /* Now check if there is also a header in the packet */
+ if (nrecv < sizeof(*icmp) + sizeof(*v6info))
+ {
+#if 0
+ printf("ready_callback6: too short %d (echo reply)\n",
+ (int)nrecv);
+#endif
+ return;
+ }
+
+ eudp= NULL;
+ eicmp= NULL;
+
+ v6info= (struct v6info *)&icmp[1];
+
+ if (ntohl(v6info->pid) != base->my_pid)
+ {
+ /* From a different process */
+ return;
+ }
+
+ ind= ntohl(v6info->id);
+
+ state= NULL;
+ if (ind >= 0 && ind < base->tabsiz)
+ state= base->table[ind];
+
+ if (state && state->sin6.sin6_family != AF_INET6)
+ state= NULL;
+
+ if (state && !state->do_icmp)
+ {
+ state= NULL;
+ }
+
+ if (!state)
+ {
+ /* Nothing here */
+ return;
+ }
+
+#if 0
+ printf("ready_callback6: from %s",
+ inet_ntop(AF_INET6, &remote.sin6_addr,
+ buf, sizeof(buf)));
+ printf(" for %s hop %d\n",
+ inet_ntop(AF_INET6, &state->sin6.sin6_addr,
+ buf, sizeof(buf)), state->hop);
+#endif
+
+ if (!state->busy)
+ {
+printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family);
+ printf(
+ "ready_callback6: index (%d) is not busy\n",
+ ind);
+ return;
+ }
+
+ late= 0;
+ isDup= 0;
+ seq= ntohl(v6info->seq);
+ if (seq != state->seq)
+ {
+ if (seq > state->seq)
+ {
+ printf(
+"ready_callback6: mismatch for seq, got 0x%x, expected 0x%x\n",
+ ntohl(v6info->seq),
+ state->seq);
+ return;
+ }
+ late= 1;
+
+ snprintf(line, sizeof(line), "\"late\":%d",
+ state->seq-seq);
+ add_str(state, line);
+ }
+ else if (state->gotresp)
+ {
+ isDup= 1;
+ add_str(state, " }, { \"dup\":true");
+ }
+
+ if (!late && !isDup)
+ {
+ state->last_response_hop= state->hop;
+ state->done= 1;
+ }
+
+ if (memcmp(&dstaddr, &state->loc_sin6.sin6_addr,
+ sizeof(dstaddr)) != 0)
+ {
+ printf("ready_callback6: weird destination %s\n",
+ inet_ntop(AF_INET6, &dstaddr,
+ buf, sizeof(buf)));
+ }
+
+ if (!late)
+ {
+ ms= (now.tv_sec-state->xmit_time.tv_sec)*1000 +
+ (now.tv_usec-state->xmit_time.tv_usec)/
+ 1e3;
+ }
+ else
+ {
+ ms= (now.tv_sec-v6info->tv.tv_sec)*1000 +
+ (now.tv_usec-v6info->tv.tv_usec)/
+ 1e3;
+ }
+
+ snprintf(line, sizeof(line), "%s\"from\":\"%s\"",
+ (late || isDup) ? ", " : "",
+ inet_ntop(AF_INET6, &remote.sin6_addr,
+ buf, sizeof(buf)));
+ add_str(state, line);
+ snprintf(line, sizeof(line),
+ ", \"ttl\":%d, \"rtt\":%.3f, \"size\":%d",
+ rcvdttl, ms, (int)nrecv);
+ add_str(state, line);
+
+#if 0
+ printf("ready_callback6: from %s, ttl %d",
+ inet_ntop(AF_INET6, &remote.sin6_addr, buf,
+ sizeof(buf)), rcvdttl);
+ printf(" for %s hop %d\n",
+ inet_ntop(AF_INET6, &state->sin6.sin6_addr, buf,
+ sizeof(buf)), state->hop);
+#endif
+
+ if (late)
+ add_str(state, " }, { ");
+
+ if (!late && !isDup)
+ {
+ if (state->duptimeout)
+ {
+ state->gotresp= 1;
+ interval.tv_sec= state->duptimeout/1000000;
+ interval.tv_usec= state->duptimeout % 1000000;
+ evtimer_add(&state->timer, &interval);
+ }
+ else
+ send_pkt(state);
+ }
+ }
+ else if (icmp->icmp6_type == ICMP6_ECHO_REQUEST /* 128 */ ||
+ icmp->icmp6_type == MLD_LISTENER_QUERY /* 130 */ ||
+ icmp->icmp6_type == MLD_LISTENER_REPORT /* 131 */ ||
+ icmp->icmp6_type == ND_ROUTER_ADVERT /* 134 */ ||
+ icmp->icmp6_type == ND_NEIGHBOR_SOLICIT /* 135 */ ||
+ icmp->icmp6_type == ND_NEIGHBOR_ADVERT /* 136 */ ||
+ icmp->icmp6_type == ND_REDIRECT /* 137 */)
+ {
+ /* No need to do anything */
+ }
+ else
+ {
+ printf("ready_callback6: got type %d\n", icmp->icmp6_type);
+ return;
+ }
+}
+
+static struct trtbase *traceroute_base_new(struct event_base
+ *event_base)
+{
+ int on;
+ struct trtbase *base;
+
+ base= xzalloc(sizeof(*base));
+
+ base->event_base= event_base;
+
+ base->tabsiz= 10;
+ base->table= xzalloc(base->tabsiz * sizeof(*base->table));
+
+ base->v4icmp_rcv= xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ base->v6icmp_rcv= xsocket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+ base->v4icmp_snd= xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ base->v6icmp_snd= xsocket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+ base->v4udp_snd= xsocket(AF_INET, SOCK_DGRAM, 0);
+
+ base->my_pid= getpid();
+
+ on = 1;
+ setsockopt(base->v6icmp_rcv, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ &on, sizeof(on));
+
+ on = 1;
+ setsockopt(base->v6icmp_rcv, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+ &on, sizeof(on));
+
+ event_assign(&base->event4, base->event_base, base->v4icmp_rcv,
+ EV_READ | EV_PERSIST, ready_callback4, base);
+ event_assign(&base->event6, base->event_base, base->v6icmp_rcv,
+ EV_READ | EV_PERSIST, ready_callback6, base);
+ event_add(&base->event4, NULL);
+ event_add(&base->event6, NULL);
+
+ return base;
+}
+
+static void noreply_callback(int __attribute((unused)) unused,
+ const short __attribute((unused)) event, void *s)
+{
+ struct trtstate *state;
+
+ state= s;
+
+#if 0
+ printf("noreply_callback: gotresp = %d\n",
+ state->gotresp);
+#endif
+
+ if (!state->gotresp)
+ add_str(state, "\"x\":\"*\"");
+
+ send_pkt(state);
+}
+
+static void *traceroute_init(int __attribute((unused)) argc, char *argv[],
+ void (*done)(void *state))
+{
+ uint32_t opt;
+ int i, do_icmp, do_v6, dont_fragment, delay_name_res;
+ unsigned count, duptimeout, firsthop, gaplimit, maxhops, maxpacksize,
+ parismod, timeout; /* must be int-sized */
+ size_t newsiz;
+ char *str_Atlas;
+ const char *hostname;
+ char *out_filename;
+ struct trtstate *state;
+ sa_family_t af;
+ len_and_sockaddr *lsa;
+ FILE *fh;
+
+ if (!trt_base)
+ {
+ trt_base= traceroute_base_new(EventBase);
+ if (!trt_base)
+ crondlog(DIE9 "traceroute_base_new failed");
+ }
+
+ /* Parse arguments */
+ count= 3;
+ firsthop= 1;
+ gaplimit= 5;
+ maxhops= 32;
+ maxpacksize= 40;
+ duptimeout= 10;
+ timeout= 1000;
+ parismod= 16;
+ str_Atlas= NULL;
+ out_filename= NULL;
+ opt_complementary = "=1:4--6:i--u:a+:c+:f+:g+:m+:w+:z+:S+";
+ opt = getopt32(argv, TRACEROUTE_OPT_STRING, &parismod, &count,
+ &firsthop, &gaplimit, &maxhops, &timeout, &duptimeout,
+ &str_Atlas, &out_filename, &maxpacksize);
+ hostname = argv[optind];
+
+ if (opt == 0xffffffff)
+ {
+ crondlog(LVL8 "bad options");
+ return NULL;
+ }
+
+ do_icmp= !!(opt & OPT_I);
+ do_v6= !!(opt & OPT_6);
+ dont_fragment= !!(opt & OPT_F);
+ delay_name_res= !!(opt & OPT_r);
+ if (maxpacksize > sizeof(trt_base->packet))
+ maxpacksize= sizeof(trt_base->packet);
+
+ if (out_filename)
+ {
+ if (!validate_filename(out_filename, SAFE_PREFIX))
+ {
+ crondlog(LVL8 "insecure file '%s'", out_filename);
+ return NULL;
+ }
+ fh= fopen(out_filename, "a");
+ if (!fh)
+ {
+ crondlog(LVL8 "unable to append to '%s'",
+ out_filename);
+ return NULL;
+ }
+ fclose(fh);
+ }
+
+ if (!delay_name_res)
+ {
+ /* Attempt to resolve 'name' */
+ af= do_v6 ? AF_INET6 : AF_INET;
+ lsa= host_and_af2sockaddr(hostname, 0, af);
+ if (!lsa)
+ return NULL;
+
+ if (lsa->len > sizeof(state->sin6))
+ {
+ free(lsa);
+ return NULL;
+ }
+ }
+ else
+ {
+ /* lint */
+ lsa= NULL;
+ af= -1;
+ }
+
+ state= xzalloc(sizeof(*state));
+ state->parismod= parismod;
+ state->trtcount= count;
+ state->firsthop= firsthop;
+ state->maxpacksize= maxpacksize;
+ state->maxhops= maxhops;
+ state->gaplimit= gaplimit;
+ state->duptimeout= duptimeout*1000;
+ state->timeout= timeout*1000;
+ state->atlas= str_Atlas ? strdup(str_Atlas) : NULL;
+ state->hostname= strdup(hostname);
+ state->do_icmp= do_icmp;
+ state->do_v6= do_v6;
+ state->dont_fragment= dont_fragment;
+ state->delay_name_res= delay_name_res;
+ state->out_filename= out_filename ? strdup(out_filename) : NULL;
+ state->base= trt_base;
+ state->busy= 0;
+ state->result= NULL;
+ state->reslen= 0;
+ state->resmax= 0;
+
+ for (i= 0; i<trt_base->tabsiz; i++)
+ {
+ if (trt_base->table[i] == NULL)
+ break;
+ }
+ if (i >= trt_base->tabsiz)
+ {
+ newsiz= 2*trt_base->tabsiz;
+ trt_base->table= xrealloc(trt_base->table,
+ newsiz*sizeof(*trt_base->table));
+ for (i= trt_base->tabsiz; i<newsiz; i++)
+ trt_base->table[i]= NULL;
+ i= trt_base->tabsiz;
+ trt_base->tabsiz= newsiz;
+ }
+ state->index= i;
+ trt_base->table[i]= state;
+ trt_base->done= done;
+
+ printf("traceroute_init: state %p, index %d\n",
+ state, state->index);
+
+ memset(&state->loc_sin6, '\0', sizeof(state->loc_sin6));
+ state->loc_socklen= 0;
+
+ if (!delay_name_res)
+ {
+ memcpy(&state->sin6, &lsa->u.sa, lsa->len);
+ state->socklen= lsa->len;
+ free(lsa); lsa= NULL;
+ if (af == AF_INET6)
+ {
+ char buf[INET6_ADDRSTRLEN];
+ printf("traceroute_init: %s, len %d for %s\n",
+ inet_ntop(AF_INET6, &state->sin6.sin6_addr,
+ buf, sizeof(buf)), state->socklen,
+ state->hostname);
+ }
+ }
+
+ evtimer_assign(&state->timer, state->base->event_base,
+ noreply_callback, state);
+
+ return state;
+}
+
+static void traceroute_start2(void *state)
+{
+ int r, serrno;
+ struct trtstate *trtstate;
+ struct trtbase *trtbase;
+ struct sockaddr_in loc_sa4;
+ struct sockaddr_in6 loc_sa6;
+ char line[80];
+
+ trtstate= state;
+ trtbase= trtstate->base;
+
+ if (trtstate->busy)
+ {
+ printf("traceroute_start: busy, can't start\n");
+ return;
+ }
+ trtstate->busy= 1;
+
+ trtstate->min= ULONG_MAX;
+ trtstate->max= 0;
+ trtstate->sum= 0;
+ trtstate->sentpkts= 0;
+ trtstate->rcvdpkts= 0;
+ trtstate->duppkts= 0;
+
+ trtstate->hop= trtstate->firsthop;
+ trtstate->sent= 0;
+ trtstate->seq= 0;
+ trtstate->paris++;
+ trtstate->last_response_hop= 0; /* Should be starting hop */
+ trtstate->done= 0;
+ trtstate->not_done= 0;
+ trtstate->lastditch= 0;
+ trtstate->curpacksize= trtstate->maxpacksize;
+
+ if (trtstate->result) free(trtstate->result);
+ trtstate->resmax= 80;
+ trtstate->result= xmalloc(trtstate->resmax);
+ trtstate->reslen= 0;
+ trtstate->starttime= time(NULL);
+
+ snprintf(line, sizeof(line), "{ \"hop\":%d", trtstate->hop);
+ add_str(trtstate, line);
+
+ if (trtstate->do_icmp)
+ {
+ if (trtstate->do_v6)
+ {
+ memset(&loc_sa6, '\0', sizeof(loc_sa6));
+ loc_sa6.sin6_family= AF_INET;
+
+ r= connect(trtbase->v6icmp_snd,
+ (struct sockaddr *)&trtstate->sin6,
+ trtstate->socklen);
+#if 0
+ { errno= ENOSYS; r= -1; }
+#endif
+ if (r == -1)
+ {
+ serrno= errno;
+
+ snprintf(line, sizeof(line),
+ ", " DBQ(error) ":" DBQ(connect failed: %s) " }",
+ strerror(serrno));
+ add_str(trtstate, line);
+ report(trtstate);
+ return;
+ }
+ trtstate->loc_socklen= sizeof(trtstate->loc_sin6);
+ if (getsockname(trtbase->v6icmp_snd,
+ &trtstate->loc_sin6,
+ &trtstate->loc_socklen) == -1)
+ {
+ crondlog(DIE9 "getsockname failed");
+ }
+#if 0
+ printf("Got localname: %s\n",
+ inet_ntop(AF_INET6,
+ &trtstate->loc_sin6.sin6_addr,
+ buf, sizeof(buf)));
+#endif
+ }
+ else
+ {
+ memset(&loc_sa4, '\0', sizeof(loc_sa4));
+ loc_sa4.sin_family= AF_INET;
+ ((struct sockaddr_in *)&trtstate->sin6)->sin_port=
+ htons(0x8000);
+
+ r= connect(trtbase->v4icmp_snd,
+ (struct sockaddr *)&trtstate->sin6,
+ trtstate->socklen);
+#if 0
+ { errno= ENOSYS; r= -1; }
+#endif
+ if (r == -1)
+ {
+ serrno= errno;
+
+ snprintf(line, sizeof(line),
+ ", " DBQ(error) ":" DBQ(connect failed: %s) " }",
+ strerror(serrno));
+ add_str(trtstate, line);
+ report(trtstate);
+ return;
+ }
+ trtstate->loc_socklen= sizeof(trtstate->loc_sin6);
+ if (getsockname(trtbase->v4icmp_snd,
+ &trtstate->loc_sin6,
+ &trtstate->loc_socklen) == -1)
+ {
+ crondlog(DIE9 "getsockname failed");
+ }
+#if 0
+ printf("Got localname: %s\n",
+ inet_ntoa(((struct sockaddr_in *)
+ &trtstate->loc_sin6)->sin_addr));
+#endif
+ }
+ }
+ else
+ {
+ if (trtstate->do_v6)
+ {
+ int sock;
+
+ memset(&loc_sa6, '\0', sizeof(loc_sa6));
+ loc_sa6.sin6_family= AF_INET6;
+ loc_sa6.sin6_port= htons(SRC_BASE_PORT +
+ trtstate->index);;
+
+ sock= socket(AF_INET6, SOCK_DGRAM, 0);
+ if (sock == -1)
+ {
+ crondlog(DIE9 "socket failed");
+ }
+printf("traceroute_start2: before bind\n");
+ r= bind(sock, (struct sockaddr *)&loc_sa6,
+ sizeof(loc_sa6));
+ if (r == -1)
+ {
+ serrno= errno;
+
+ snprintf(line, sizeof(line),
+ ", " DBQ(error) ":" DBQ(bind failed: %s) " }",
+ strerror(serrno));
+ add_str(trtstate, line);
+ report(trtstate);
+ close(sock);
+ return;
+ }
+
+ r= connect(sock, (struct sockaddr *)&trtstate->sin6,
+ trtstate->socklen);
+#if 0
+ { errno= ENOSYS; r= -1; }
+#endif
+ if (r == -1)
+ {
+ serrno= errno;
+
+ snprintf(line, sizeof(line),
+ ", " DBQ(error) ":" DBQ(connect failed: %s) " }",
+ strerror(serrno));
+ add_str(trtstate, line);
+ report(trtstate);
+ return;
+ }
+
+ trtstate->loc_socklen= sizeof(trtstate->loc_sin6);
+ if (getsockname(sock,
+ &trtstate->loc_sin6,
+ &trtstate->loc_socklen) == -1)
+ {
+ crondlog(DIE9 "getsockname failed");
+ }
+
+ close(sock);
+#if 0
+ printf("Got localname: %s:%d\n",
+ inet_ntop(AF_INET6,
+ &trtstate->loc_sin6.sin6_addr,
+ buf, sizeof(buf)),
+ ntohs(((struct sockaddr_in *)&trtstate->
+ loc_sin6)->sin_port));
+#endif
+ }
+ else
+ {
+ int sock;
+
+ memset(&loc_sa4, '\0', sizeof(loc_sa4));
+ loc_sa4.sin_family= AF_INET;
+
+ loc_sa4.sin_port= htons(SRC_BASE_PORT +
+ trtstate->index);;
+
+ /* Also set destination port */
+ ((struct sockaddr_in *)&trtstate->sin6)->
+ sin_port= htons(BASE_PORT);
+
+ sock= socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1)
+ {
+ crondlog(DIE9 "socket failed");
+ }
+ r= bind(sock, (struct sockaddr *)&loc_sa4,
+ sizeof(loc_sa4));
+#if 0
+ { errno= ENOSYS; r= -1; }
+#endif
+ if (r == -1)
+ {
+ serrno= errno;
+
+ snprintf(line, sizeof(line),
+ ", " DBQ(error) ":" DBQ(bind failed: %s) " }",
+ strerror(serrno));
+ add_str(trtstate, line);
+ report(trtstate);
+ close(sock);
+ return;
+ }
+
+ r= connect(sock, (struct sockaddr *) &trtstate->sin6,
+ trtstate->socklen);
+#if 0
+ { errno= ENOSYS; r= -1; }
+#endif
+ if (r == -1)
+ {
+ serrno= errno;
+
+ snprintf(line, sizeof(line),
+ ", " DBQ(error) ":" DBQ(connect failed: %s) " }",
+ strerror(serrno));
+ add_str(trtstate, line);
+ report(trtstate);
+ close(sock);
+ return;
+ }
+ trtstate->loc_socklen= sizeof(trtstate->loc_sin6);
+ if (getsockname(sock,
+ &trtstate->loc_sin6,
+ &trtstate->loc_socklen) == -1)
+ {
+ crondlog(DIE9 "getsockname failed");
+ }
+ close(sock);
+#if 0
+ printf("Got localname: %s:%d\n",
+ inet_ntoa(((struct sockaddr_in *)
+ &trtstate->loc_sin6)->sin_addr),
+ ntohs(((struct sockaddr_in *)&trtstate->
+ loc_sin6)->sin_port));
+#endif
+ }
+ }
+
+ add_str(trtstate, ", \"result\": [ ");
+
+ send_pkt(trtstate);
+}
+
+static void dns_cb(int result, struct evutil_addrinfo *res, void *ctx)
+{
+ int count;
+ struct trtstate *env;
+ struct evutil_addrinfo *cur;
+ char line[160];
+
+ env= ctx;
+
+ if (!env->dnsip)
+ {
+ crondlog(LVL7
+ "dns_cb: in dns_cb but not doing dns at this time");
+ if (res)
+ evutil_freeaddrinfo(res);
+ return;
+ }
+
+ if (result != 0)
+ {
+ /* Hmm, great. Where do we put this init code */
+ if (env->result) free(env->result);
+ env->resmax= 80;
+ env->result= xmalloc(env->resmax);
+ env->reslen= 0;
+
+ env->starttime= time(NULL);
+ snprintf(line, sizeof(line),
+ "{ " DBQ(error) ":" DBQ(name resolution failed: %s) " }",
+ evutil_gai_strerror(result));
+ add_str(env, line);
+ report(env);
+ return;
+ }
+
+ env->dnsip= 0;
+
+ env->dns_res= res;
+ env->dns_curr= res;
+
+ count= 0;
+ for (cur= res; cur; cur= cur->ai_next)
+ count++;
+
+ // env->reportcount(env, count);
+
+ while (env->dns_curr)
+ {
+ env->socklen= env->dns_curr->ai_addrlen;
+ if (env->socklen > sizeof(env->sin6))
+ continue; /* Weird */
+ memcpy(&env->sin6, env->dns_curr->ai_addr,
+ env->socklen);
+
+ traceroute_start2(env);
+
+ evutil_freeaddrinfo(env->dns_res);
+ env->dns_res= NULL;
+ env->dns_curr= NULL;
+ return;
+ }
+
+ /* Something went wrong */
+ evutil_freeaddrinfo(env->dns_res);
+ env->dns_res= NULL;
+ env->dns_curr= NULL;
+ snprintf(line, sizeof(line),
+"%s{ " DBQ(error) ":" DBQ(name resolution failed: out of addresses) " } ] }",
+ env->sent ? " }, " : "");
+ add_str(env, line);
+ report(env);
+}
+
+static void traceroute_start(void *state)
+{
+ struct trtstate *trtstate;
+ struct evutil_addrinfo hints;
+
+ trtstate= state;
+
+ if (!trtstate->delay_name_res)
+ {
+ traceroute_start2(state);
+ return;
+ }
+
+ memset(&hints, '\0', sizeof(hints));
+ hints.ai_socktype= SOCK_DGRAM;
+ hints.ai_family= trtstate->do_v6 ? AF_INET6 : AF_INET;
+ trtstate->dnsip= 1;
+ (void) evdns_getaddrinfo(DnsBase, trtstate->hostname, NULL,
+ &hints, dns_cb, trtstate);
+}
+
+static int traceroute_delete(void *state)
+{
+ int ind;
+ struct trtstate *trtstate;
+ struct trtbase *base;
+
+ trtstate= state;
+
+ printf("traceroute_delete: state %p, index %d, busy %d\n",
+ state, trtstate->index, trtstate->busy);
+
+ if (trtstate->busy)
+ return 0;
+
+ base= trtstate->base;
+ ind= trtstate->index;
+
+ if (base->table[ind] != trtstate)
+ crondlog(DIE9 "strange, state not in table");
+ base->table[ind]= NULL;
+
+ event_del(&trtstate->timer);
+
+ free(trtstate->atlas);
+ trtstate->atlas= NULL;
+ free(trtstate->hostname);
+ trtstate->hostname= NULL;
+ free(trtstate->out_filename);
+ trtstate->out_filename= NULL;
+
+ free(trtstate);
+
+ return 1;
+}
+
+struct testops traceroute_ops = { traceroute_init, traceroute_start,
+ traceroute_delete };
+
diff --git a/examples/cron.root.7 b/examples/cron.root.7
new file mode 100644
index 0000000..0347e80
--- /dev/null
+++ b/examples/cron.root.7
@@ -0,0 +1,4 @@
+60 1363873067 2147483647 UNIFORM 14 evtdig -4 -h --evdns -A "1015529" -O /home/atlas/data/new/7 193.0.14.129
+60 1363873067 2147483647 UNIFORM 14 evping -4 -c 3 -A "1001" -O /home/atlas/data/new/7 193.0.14.129
+60 1363873067 2147483647 UNIFORM 14 evtraceroute -4 -c 3 -U -w 1000 -A "5001" -O /home/atlas/data/new/7 193.0.14.129
+60 1363873067 2147483647 UNIFORM 14 evhttpget -4 -1 -A "12023" -O /home/atlas/data/new/7 http://www.ripe.net/favicon.ico
diff --git a/include/applets.h b/include/applets.h
index 0e4cbd5..eb1495e 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -95,6 +95,7 @@ USE_CHGRP(APPLET_NOEXEC(chgrp, chgrp, _BB_DIR_BIN, _BB_SUID_NEVER, chgrp))
USE_CHMOD(APPLET_NOEXEC(chmod, chmod, _BB_DIR_BIN, _BB_SUID_NEVER, chmod))
USE_CHOWN(APPLET_NOEXEC(chown, chown, _BB_DIR_BIN, _BB_SUID_NEVER, chown))
USE_CHPASSWD(APPLET(chpasswd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_CHPASSWD(APPLET(chpasswd, _BB_DIR_BIN, _BB_SUID_NEVER))
USE_CHPST(APPLET(chpst, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_CHROOT(APPLET(chroot, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
USE_CHRT(APPLET(chrt, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
@@ -103,6 +104,7 @@ USE_CKSUM(APPLET(cksum, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_CLEAR(APPLET(clear, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_CMP(APPLET(cmp, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_COMM(APPLET(comm, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_CONDMV(APPLET_NOEXEC(condmv, condmv, _BB_DIR_BIN, _BB_SUID_NEVER, condmv))
USE_CP(APPLET_NOEXEC(cp, cp, _BB_DIR_BIN, _BB_SUID_NEVER, cp))
USE_CPIO(APPLET(cpio, _BB_DIR_BIN, _BB_SUID_NEVER))
USE_CROND(APPLET(crond, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
@@ -121,6 +123,7 @@ USE_MODPROBE_SMALL(APPLET_ODDNAME(depmod, modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER
USE_DEVFSD(APPLET(devfsd, _BB_DIR_SBIN, _BB_SUID_NEVER))
USE_DEVMEM(APPLET(devmem, _BB_DIR_SBIN, _BB_SUID_NEVER))
USE_DF(APPLET(df, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_DFRM(APPLET(dfrm, _BB_DIR_BIN, _BB_SUID_NEVER))
USE_APP_DHCPRELAY(APPLET(dhcprelay, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
USE_DIFF(APPLET(diff, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_DIRNAME(APPLET_NOFORK(dirname, dirname, _BB_DIR_USR_BIN, _BB_SUID_NEVER, dirname))
@@ -141,7 +144,13 @@ USE_EJECT(APPLET(eject, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_ENV(APPLET_NOEXEC(env, env, _BB_DIR_USR_BIN, _BB_SUID_NEVER, env))
USE_ENVDIR(APPLET_ODDNAME(envdir, chpst, _BB_DIR_USR_BIN, _BB_SUID_NEVER, envdir))
USE_ENVUIDGID(APPLET_ODDNAME(envuidgid, chpst, _BB_DIR_USR_BIN, _BB_SUID_NEVER, envuidgid))
+USE_EOOQD(APPLET(eooqd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_EPERD(APPLET(eperd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
USE_ETHER_WAKE(APPLET_ODDNAME(ether-wake, ether_wake, _BB_DIR_USR_BIN, _BB_SUID_NEVER, ether_wake))
+USE_EVHTTPGET(APPLET(evhttpget, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_EVPING(APPLET(evping, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_EVTDIG(APPLET(evtdig, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_EVTRACEROUTE(APPLET(evtraceroute, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_EXPAND(APPLET(expand, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_EXPR(APPLET(expr, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_FAKEIDENTD(APPLET(fakeidentd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
@@ -179,6 +188,8 @@ USE_HEXDUMP(APPLET_NOEXEC(hexdump, hexdump, _BB_DIR_USR_BIN, _BB_SUID_NEVER, hex
USE_HOSTID(APPLET_NOFORK(hostid, hostid, _BB_DIR_USR_BIN, _BB_SUID_NEVER, hostid))
USE_HOSTNAME(APPLET(hostname, _BB_DIR_BIN, _BB_SUID_NEVER))
USE_HTTPD(APPLET(httpd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_HTTPGET(APPLET_NOFORK(httpget, httpget, _BB_DIR_BIN, _BB_SUID_NEVER, httpget))
+USE_HTTPPOST(APPLET_NOFORK(httppost, httppost, _BB_DIR_BIN, _BB_SUID_NEVER, httppost))
USE_HUSH(APPLET(hush, _BB_DIR_BIN, _BB_SUID_NEVER))
USE_HWCLOCK(APPLET(hwclock, _BB_DIR_SBIN, _BB_SUID_NEVER))
USE_ID(APPLET(id, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
@@ -273,6 +284,7 @@ USE_OPENVT(APPLET(openvt, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
//USE_PARSE(APPLET(parse, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_PASSWD(APPLET(passwd, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS))
USE_PATCH(APPLET(patch, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_PERD(APPLET(perd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
USE_PGREP(APPLET(pgrep, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_PIDOF(APPLET(pidof, _BB_DIR_BIN, _BB_SUID_NEVER))
USE_PING(APPLET(ping, _BB_DIR_BIN, _BB_SUID_MAYBE))
@@ -306,6 +318,7 @@ USE_RMMOD(APPLET(rmmod, _BB_DIR_SBIN, _BB_SUID_NEVER))
USE_MODPROBE_SMALL(APPLET_ODDNAME(rmmod, modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER, modprobe))
USE_ROUTE(APPLET(route, _BB_DIR_SBIN, _BB_SUID_NEVER))
USE_RPM(APPLET(rpm, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_RPTRA6(APPLET(rptra6, _BB_DIR_BIN, _BB_SUID_NEVER))
USE_RPM2CPIO(APPLET(rpm2cpio, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_RTCWAKE(APPLET(rtcwake, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_RUN_PARTS(APPLET_ODDNAME(run-parts, run_parts, _BB_DIR_BIN, _BB_SUID_NEVER, run_parts))
@@ -314,6 +327,7 @@ USE_RUNLEVEL(APPLET(runlevel, _BB_DIR_SBIN, _BB_SUID_NEVER))
USE_RUNSV(APPLET(runsv, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_RUNSVDIR(APPLET(runsvdir, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_RX(APPLET(rx, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_RXTXRPT(APPLET(rxtxrpt, _BB_DIR_BIN, _BB_SUID_NEVER))
USE_SCRIPT(APPLET(script, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_SED(APPLET(sed, _BB_DIR_BIN, _BB_SUID_NEVER))
USE_SELINUXENABLED(APPLET(selinuxenabled, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
@@ -340,6 +354,7 @@ USE_SLEEP(APPLET_NOFORK(sleep, sleep, _BB_DIR_BIN, _BB_SUID_NEVER, sleep))
USE_SOFTLIMIT(APPLET_ODDNAME(softlimit, chpst, _BB_DIR_USR_BIN, _BB_SUID_NEVER, softlimit))
USE_SORT(APPLET_NOEXEC(sort, sort, _BB_DIR_USR_BIN, _BB_SUID_NEVER, sort))
USE_SPLIT(APPLET(split, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_SSLGETCERT(APPLET(sslgetcert, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_START_STOP_DAEMON(APPLET_ODDNAME(start-stop-daemon, start_stop_daemon, _BB_DIR_SBIN, _BB_SUID_NEVER, start_stop_daemon))
USE_STAT(APPLET(stat, _BB_DIR_BIN, _BB_SUID_NEVER))
USE_STRINGS(APPLET(strings, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
diff --git a/include/cmdtable.h b/include/cmdtable.h
new file mode 100644
index 0000000..b50d12e
--- /dev/null
+++ b/include/cmdtable.h
@@ -0,0 +1,23 @@
+/*
+cmdtable.h
+
+Commands for perd and ooqd
+*/
+
+int condmv_main(int argc, char *argv[]);
+int httpget_main(int argc, char *argv[]);
+int httppost_main(int argc, char *argv[]);
+int sslgetcert_main(int argc, char *argv[]);
+
+static struct builtin
+{
+ const char *cmd;
+ int (*func)(int argc, char *argv[]);
+} builtin_cmds[]=
+{
+ { "condmv", condmv_main },
+ { "httppost", httppost_main },
+ { "sslgetcert", sslgetcert_main },
+ { NULL, 0 }
+};
+
diff --git a/include/libbb.h b/include/libbb.h
index 08fed90..1231b31 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -302,6 +302,17 @@ extern char *bb_get_last_path_component_strip(char *path) FAST_FUNC;
/* "abc/def/" -> "" and it never modifies 'path' */
extern char *bb_get_last_path_component_nostrip(const char *path) FAST_FUNC;
+/* What's the best place for this? */
+#define ATLAS_HOME "/home/atlas"
+#define ATLAS_CRONS ATLAS_HOME "/crons"
+#define ATLAS_STATUS ATLAS_HOME "/status"
+#define ATLAS_DATA_OUT ATLAS_HOME "/data/out"
+#define ATLAS_DATA_OOQ_OUT ATLAS_HOME "/data/ooq.out"
+#define ATLAS_DATA_NEW ATLAS_HOME "/data/new"
+#define ATLAS_TIMESYNC_FILE ATLAS_STATUS "/timesync.vol"
+
+extern int validate_filename(const char *path, const char *prefix);
+
int ndelay_on(int fd) FAST_FUNC;
int ndelay_off(int fd) FAST_FUNC;
int close_on_exec_on(int fd) FAST_FUNC;
@@ -319,6 +330,10 @@ char *xrealloc_getcwd_or_warn(char *cwd) FAST_FUNC;
char *xmalloc_follow_symlinks(const char *path) FAST_FUNC;
+extern size_t strlcat(char *__restrict dst, const char *__restrict src,
+ size_t n) __THROW __nonnull ((1, 2));
+extern size_t strlcpy(char *__restrict dst, const char *__restrict src,
+ size_t n) __THROW __nonnull ((1, 2));
enum {
/* bb_signals(BB_FATAL_SIGS, handler) catches all signals which
@@ -422,13 +437,20 @@ struct BUG_too_small {
) <= 127 ? 1 : -1];
};
-
int xsocket(int domain, int type, int protocol) FAST_FUNC;
void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) FAST_FUNC;
+void xrbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen,
+ void (*reportf)(int err)) FAST_FUNC;
void xlisten(int s, int backlog) FAST_FUNC;
void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) FAST_FUNC;
+void xrconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen,
+ void (*reportf)(int err)) FAST_FUNC;
ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to,
socklen_t tolen) FAST_FUNC;
+ssize_t xrsendto(int s, const void *buf, size_t len, const struct sockaddr *to,
+ socklen_t tolen, void (*reportf)(int err)) FAST_FUNC;
+ssize_t rsendto(int s, const void *buf, size_t len, const struct sockaddr *to,
+ socklen_t tolen, void (*reportf)(int err)) FAST_FUNC;
/* SO_REUSEADDR allows a server to rebind to an address that is already
* "in use" by old connections to e.g. previous server instance which is
* killed or crashed. Without it bind will fail until all such connections
@@ -526,6 +548,8 @@ struct hostent *xgethostbyname(const char *name) FAST_FUNC;
// Also mount.c and inetd.c are using gethostbyname(),
// + inet_common.c has additional IPv4-only stuff
+len_and_sockaddr* get_sock_lsa(int fd) FAST_FUNC;
+
void socket_want_pktinfo(int fd) FAST_FUNC;
ssize_t send_to_from(int fd, void *buf, size_t len, int flags,
@@ -1302,6 +1326,7 @@ procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC;
/* Puts [comm] if cmdline is empty (-> process is a kernel thread) */
void read_cmdline(char *buf, int col, unsigned pid, const char *comm) FAST_FUNC;
pid_t *find_pid_by_name(const char* procName) FAST_FUNC;
+int comm_match(procps_status_t *p, const char *procName);
pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC;
diff --git a/include/usage.h b/include/usage.h
index 63aff31..2fd5953 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -470,6 +470,14 @@
"\n -2 Suppress lines unique to FILE2" \
"\n -3 Suppress lines common to both files" \
+#define condmv_trivial_usage \
+ "[-A <string to append>][-f] FILE1 FILE2"
+#define condmv_full_usage "\n\n" \
+ "Rename FILE1 to FILE2 if FILE2 does not exist\n" \
+ "\nOptions:" \
+ "\n -A <string> Append <string> before renaming FILE1" \
+ "\n -f Force. Move even if FILE2 does exist" \
+
#define bbconfig_trivial_usage \
""
#define bbconfig_full_usage "\n\n" \
@@ -602,6 +610,7 @@
"\n -d TIME Display TIME, not 'now'" \
"\n -r FILE Display last modification time of FILE" \
"\n [-s] TIME Set time to TIME" \
+ "\n -S Interpret TIME as Unix seconds since epoch" \
USE_FEATURE_DATE_ISOFMT( \
"\n -D FMT Use FMT for str->date conversion" \
) \
@@ -749,6 +758,12 @@
"Filesystem 512-blocks Used Available Capacity Mounted on\n" \
"/dev/sda3 17381728 17107080 274648 98% /\n"
+#define dfrm_trivial_usage \
+ "<fs> <min-size> <dir>..."
+
+#define dfrm_full_usage \
+ "<fs> <min-size> <dir>..."
+
#define dhcprelay_trivial_usage \
"[client1,client2,...] [server_device]"
#define dhcprelay_full_usage "\n\n" \
@@ -965,6 +980,37 @@
"\n -, -i Start with an empty environment" \
"\n -u Remove variable from the environment" \
+#define eooqd_trivial_usage \
+ "<queue-file>"
+#define eooqd_full_usage \
+
+#define eperd_trivial_usage \
+ "-fbSAD -P pidfile -l N " USE_FEATURE_CROND_D("-d N ") "-L LOGFILE -c DIR"
+#define eperd_full_usage "\n\n" \
+ " -f Foreground" \
+ "\n -b Background (default)" \
+ "\n -S Log to syslog (default)" \
+ "\n -l Set log level. 0 is the most verbose, default 8" \
+ USE_FEATURE_CROND_D( \
+ "\n -d Set log level, log to stderr" \
+ ) \
+ "\n -L Log to file" \
+ "\n -c Working dir" \
+ "\n -A Atlas specific processing" \
+ "\n -D Periodically kick watchdog" \
+ "\n -P pidfile to use" \
+
+#define evtdig_trivial_usage \
+ "[-h|-i|-b|-s] ... <server IP address>"
+#define evtdig_full_usage "\n\n" \
+ "evtdig: a tiny implemention dns queries which supports 4 queries\n" \
+ "\n not implemented: recursion" \
+ "\n -h | --hostname-bind hostname.bind txt chaos " \
+ "\n -i | id-server id.server txt chaos " \
+ "\n -b | version-bind version-bind txt chaos " \
+ "\n -s | soa <zone> to be implmented " \
+ "\n RIPE NCC 2011 " \
+
#define ether_wake_trivial_usage \
"[-b] [-i iface] [-p aa:bb:cc:dd[:ee:ff]] MAC"
#define ether_wake_full_usage "\n\n" \
@@ -976,6 +1022,44 @@
"\n -i iface Interface to use (default eth0)" \
"\n -p pass Append four or six byte password PW to the packet" \
+#define evhttpget_trivial_usage \
+ "[-ac0146] [--all [--combine]] [--get|--head|--post] [--post-file <file>] [--post-header <file>] [--post-footer <file>] [--store-headers <bytes>] [--user-agent <string>] [-A <atlas id>] [-O <file>]"
+#define evhttpget_full_usage "\n\n" \
+ "\nOptions:" \
+ "\n -a --all Report on all addresses" \
+ "\n -c --combine Combine the reports for all address in one JSON" \
+ "\n --get GET method" \
+ "\n --head HEAD method" \
+ "\n --post POST mehod" \
+ "\n --post-file <filename> File to post" \
+ "\n --post-header <fn> File to post (comes first)" \
+ "\n --post-footer <fn> File to post (comes last)" \
+ "\n --store-headers <bytes> Number of bytes of the header to store"\
+ "\n --user-agent <string> User agent header" \
+ "\n -0 HTTP/1.0" \
+ "\n -1 HTTP/1.1" \
+ "\n -A <atlas id> Atlas ID" \
+ "\n -O <filename> Output file" \
+ "\n -4 Only IPv4 addresses" \
+ "\n -6 Only IPv6 addresses" \
+
+#define evping_trivial_usage \
+ "todo"
+#define evping_full_usage "\n\n" \
+ "\nOptions:" \
+ "\n -c <count> Number of packets" \
+ "\n -s <size> Size" \
+ "\n -A <id> Atlas measurement ID" \
+ "\n -O <out file> Output file name" \
+ "\n -4 IPv4" \
+ "\n -6 IPv6" \
+ "todo"
+
+#define evtraceroute_trivial_usage \
+ "todo"
+#define evtraceroute_full_usage "\n\n" \
+ "todo"
+
#define expand_trivial_usage \
"[-i] [-t NUM] [FILE|-]"
#define expand_full_usage "\n\n" \
@@ -1577,6 +1661,55 @@
"\n -e STRING HTML encode STRING" \
"\n -d STRING URL decode STRING" \
+#define httpget_trivial_usage \
+ "[--append] [--delete-file] [--get|--head|--post] " \
+ "[--post-file FILE] [--post-dir DIR] " \
+ "[--post-header FILE] " \
+ "[--post-footer FILE] [--set-time bound] " \
+ "[--store-headers SIZE] [--store-body SIZE] [--summary] " \
+ "[--user-agent STRING] [-0|-1] [-4|-6] [-A STRING] [-O FILE] URL"
+#define httpget_full_usage "\n\n" \
+"Interact with a HTTP server using GET/HEAD/POST commands\n" \
+"\nOptions:" \
+"\n --append Append data to output file" \
+"\n --defile-file Delete files after they have been posted" \
+"\n --get HTTP GET command (default)" \
+"\n --head HTTP HEAD command" \
+"\n --post HTTP POST command" \
+"\n --post-file FILE Post this file" \
+"\n --post-dir DIR Post all files in this directory" \
+"\n --post-header FILE First post this file and do not delete it" \
+"\n --post-footer FILE Post this file last and do not delete it" \
+"\n --set-time bound Parse the time in the HTTP reply and set the" \
+"\n system time if it exceeds bound (in seconds)" \
+"\n --store-headers SIZE Write this amount of headers to the output" \
+"\n --store-body SIZE Write this amount of body to the output" \
+"\n --summary Write the summary output" \
+"\n --user-agent STRING Set the user-agent header to STRING" \
+"\n -0 Send the request using http/1.0" \
+"\n -1 Send the request using http/1.1 (default)" \
+"\n -4 Connect only over IPv4" \
+"\n -6 Connect only over IPv6" \
+"\n -A STRING Format the output for Atlas using STRING" \
+"\n -O FILE Write the body of the HTTP reply to FILE"
+
+#define httppost_trivial_usage \
+ "[--delete-file] [--post-file FILE] [--post-dir DIR] " \
+ "[--post-header FILE] " \
+ "[--post-footer FILE] [--set-time bound] " \
+ "[-O FILE] URL"
+#define httppost_full_usage "\n\n" \
+"Post file using the HTTP POST command\n" \
+"\nOptions:" \
+"\n --defile-file Delete files after they have been posted" \
+"\n --post-file FILE Post this file" \
+"\n --post-dir DIR Post all files in this directory" \
+"\n --post-header FILE First post this file and do not delete it" \
+"\n --post-footer FILE Post this file last and do not delete it" \
+"\n --set-time bound Parse the time in the HTTP reply and set the" \
+"\n system time if it exceeds bound (in seconds)" \
+"\n -O FILE Write the body of the HTTP reply to FILE"
+
#define hwclock_trivial_usage \
USE_FEATURE_HWCLOCK_LONG_OPTIONS( \
"[-r|--show] [-s|--hctosys] [-w|--systohc]" \
@@ -2983,6 +3116,23 @@
"$ patch -p1 < example.diff\n" \
"$ patch -p0 -i example.diff"
+#define perd_trivial_usage \
+ "-fbSAD -P pidfile -l N " USE_FEATURE_CROND_D("-d N ") "-L LOGFILE -c DIR"
+#define perd_full_usage "\n\n" \
+ " -f Foreground" \
+ "\n -b Background (default)" \
+ "\n -S Log to syslog (default)" \
+ "\n -l Set log level. 0 is the most verbose, default 8" \
+ USE_FEATURE_CROND_D( \
+ "\n -d Set log level, log to stderr" \
+ ) \
+ "\n -L Log to file" \
+ "\n -c Working dir" \
+ "\n -A Atlas specific processing" \
+ "\n -D Periodically kick watchdog" \
+ "\n -P pidfile to use" \
+
+
#define pgrep_trivial_usage \
"[-flnovx] pattern"
#define pgrep_full_usage "\n\n" \
@@ -3394,6 +3544,10 @@
#define rpm2cpio_full_usage "\n\n" \
"Output a cpio archive of the rpm file"
+#define rptra6_trivial_usage \
+
+#define rptra6_full_usage "\n\n" \
+
#define rtcwake_trivial_usage \
"[-a | -l | -u] [-d DEV] [-m MODE] [-s SECS | -t TIME]"
#define rtcwake_full_usage "\n\n" \
@@ -3493,6 +3647,13 @@
#define rx_example_usage \
"$ rx /tmp/foo\n"
+#define rxtxrpt_trivial_usage \
+ "[-A STRING] [ipv6-info-cache]"
+
+#define rxtxrpt_full_usage "\n\n" \
+ "Display RX and TX statistics as well as IPv6 info\n" \
+ "\n -A STRING Use Atlas format with STRING" \
+
#define script_trivial_usage \
"[-afq] [-c COMMAND] [OUTFILE]"
#define script_full_usage "\n\n" \
@@ -3768,6 +3929,15 @@
"$ split TODO foo\n" \
"$ cat TODO | split -a 2 -l 2 TODO_\n"
+#define sslgetcert_trivial_usage \
+ "-[46] [-A <id>] [-p <port>] <hostname>"
+#define sslgetcert_full_usage "\n\n" \
+ "\nOptions:" \
+ "\n -A <id> Atlas measurement ID" \
+ "\n -p <port> Port (default https)" \
+ "\n -4 IPv4" \
+ "\n -6 IPv6" \
+
#define start_stop_daemon_trivial_usage \
"[OPTIONS] [-S|-K] ... [-- arguments...]"
#define start_stop_daemon_full_usage "\n\n" \
@@ -4322,7 +4492,7 @@
"hello world\n"
#define traceroute_trivial_usage \
- "[-FIldnrv] [-f 1st_ttl] [-m max_ttl] [-p port#] [-q nqueries]\n" \
+ "[-FIldnrv46] [-f 1st_ttl] [-m max_ttl] [-p port#] [-q nqueries]\n" \
" [-s src_addr] [-t tos] [-w wait] [-g gateway] [-i iface]\n" \
" [-z pausemsecs] HOST [data size]"
#define traceroute_full_usage "\n\n" \
diff --git a/libbb/Kbuild b/libbb/Kbuild
index 786cbee..dead4bf 100644
--- a/libbb/Kbuild
+++ b/libbb/Kbuild
@@ -8,6 +8,8 @@ lib-y:=
lib-y += appletlib.o
lib-y += ask_confirmation.o
+lib-y += atlas_bb64.o
+lib-y += atlas_probe.o
lib-y += bb_askpass.o
lib-y += bb_basename.o
lib-y += bb_do_delay.o
@@ -97,6 +99,7 @@ lib-y += trim.o
lib-y += u_signal_names.o
lib-y += udp_io.o
lib-y += uuencode.o
+lib-y += validate_filename.o
lib-y += vdprintf.o
lib-y += verror_msg.o
lib-y += vfork_daemon_rexec.o
@@ -113,6 +116,8 @@ lib-y += xgetcwd.o
lib-y += xgethostbyname.o
lib-y += xreadlink.o
lib-y += xrealloc_vector.o
+lib-y += strlcat.o
+lib-y += strlcpy.o
# conditionally compiled objects:
lib-$(CONFIG_FEATURE_MOUNT_LOOP) += loop.o
diff --git a/libbb/atlas_bb64.c b/libbb/atlas_bb64.c
new file mode 100644
index 0000000..bcd073b
--- /dev/null
+++ b/libbb/atlas_bb64.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#define BUF_CHUNK 256
+
+struct buf
+{
+ size_t offset;
+ size_t size;
+ size_t maxsize;
+ unsigned char *buf;
+ int fd;
+};
+
+void buf_init(struct buf *buf, int fd)
+{
+ buf->maxsize= 0;
+ buf->size= 0;
+ buf->offset= 0;
+ buf->buf= NULL;
+ buf->fd= fd;
+}
+
+int buf_add(struct buf *buf, const void *data, size_t len )
+{
+ size_t maxsize;
+ void *newbuf;
+
+ if (buf->size+len <= buf->maxsize)
+ {
+ /* Easy case, just add data */
+ memcpy(buf->buf+buf->size, data, len);
+ buf->size += len;
+ return 0;
+ }
+
+ /* Just get a new buffer */
+ maxsize= buf->size-buf->offset + len + BUF_CHUNK;
+
+ newbuf= malloc(maxsize);
+ if (!newbuf)
+ {
+ fprintf(stderr, "unable to allocate %ld bytes\n", maxsize);
+ return (1);
+ }
+
+ if (buf->offset < buf->size)
+ {
+ /* Copy existing data */
+ memcpy(newbuf, buf->buf+buf->offset, buf->size-buf->offset);
+ buf->size -= buf->offset;
+ buf->offset= 0;
+ }
+ else
+ {
+ buf->size= buf->offset= 0;
+ }
+ buf->maxsize= maxsize;
+ free(buf->buf);
+ buf->buf= newbuf;
+
+ memcpy(buf->buf+buf->size, data, len);
+ buf->size += len;
+ return 0;
+}
+
+int buf_add_b64(struct buf *buf, void *data, size_t len, int mime_nl)
+{
+ char b64[]=
+ "ABCDEFGHIJKLMNOP"
+ "QRSTUVWXYZabcdef"
+ "ghijklmnopqrstuv"
+ "wxyz0123456789+/";
+ int i;
+ uint8_t *p;
+ uint32_t v;
+ char str[4];
+
+ p= data;
+
+ for (i= 0; i+3 <= len; i += 3, p += 3)
+ {
+ v= (p[0] << 16) + (p[1] << 8) + p[2];
+ str[0]= b64[(v >> 18) & 63];
+ str[1]= b64[(v >> 12) & 63];
+ str[2]= b64[(v >> 6) & 63];
+ str[3]= b64[(v >> 0) & 63];
+ buf_add(buf, str, 4);
+ if(mime_nl)
+ if (i % 48 == 45)
+ buf_add(buf, "\n", 1);
+ }
+ switch(len-i)
+ {
+ case 0: break; /* Nothing to do */
+ case 1:
+ v= (p[0] << 16);
+ str[0]= b64[(v >> 18) & 63];
+ str[1]= b64[(v >> 12) & 63];
+ str[2]= '=';
+ str[3]= '=';
+ buf_add(buf, str, 4);
+ break;
+ case 2:
+ v= (p[0] << 16) + (p[1] << 8);
+ str[0]= b64[(v >> 18) & 63];
+ str[1]= b64[(v >> 12) & 63];
+ str[2]= b64[(v >> 6) & 63];
+ str[3]= '=';
+ buf_add(buf, str, 4);
+ break;
+ default:
+ fprintf(stderr, "bad state in buf_add_b64");
+ }
+}
+
+void buf_cleanup(struct buf *buf)
+{
+ if(buf->maxsize)
+ free(buf->buf);
+ buf->buf = NULL;
+ buf->offset= buf->size= buf->maxsize= 0;
+}
diff --git a/libbb/atlas_bb64.h b/libbb/atlas_bb64.h
new file mode 100644
index 0000000..d745a9b
--- /dev/null
+++ b/libbb/atlas_bb64.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+struct buf
+{
+ size_t offset;
+ size_t size;
+ size_t maxsize;
+ char *buf;
+ int fd;
+};
+
+void buf_init(struct buf *buf, int fd);
+int buf_add(struct buf *buf, const void *data, size_t len );
+int buf_add_b64(struct buf *buf, void *data, size_t len, int mime_nl);
+void buf_cleanup(struct buf *buf);
diff --git a/libbb/atlas_probe.c b/libbb/atlas_probe.c
new file mode 100644
index 0000000..a3264fb
--- /dev/null
+++ b/libbb/atlas_probe.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+int get_probe_id(void)
+{
+ int probe_id;
+ size_t len;
+ char *check;
+ const char *key;
+ FILE *fp;
+ char buf[80];
+
+ fp= fopen("/home/atlas/status/reg_init_reply.txt", "r");
+ if (!fp)
+ return -1;
+
+ probe_id= -1;
+ while (fgets(buf, sizeof(buf), fp) != NULL)
+ {
+ if (strchr(buf, '\n') == NULL)
+ continue;
+ key= "PROBE_ID ";
+ len= strlen(key);
+
+ if (strncmp(buf, key, len) != 0 || strlen(buf) <= len)
+ continue;
+ probe_id= strtol(buf+len, &check, 10);
+ break;
+ }
+ fclose(fp);
+ return probe_id;
+}
diff --git a/libbb/atlas_probe.h b/libbb/atlas_probe.h
new file mode 100644
index 0000000..2787985
--- /dev/null
+++ b/libbb/atlas_probe.h
@@ -0,0 +1,6 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+int get_probe_id(void);
diff --git a/libbb/find_pid_by_name.c b/libbb/find_pid_by_name.c
index 92d6d02..90851eb 100644
--- a/libbb/find_pid_by_name.c
+++ b/libbb/find_pid_by_name.c
@@ -38,7 +38,7 @@ execXXX("/proc/self/exe", applet_name, params....)
and therefore comm field contains "exe".
*/
-static int comm_match(procps_status_t *p, const char *procName)
+int comm_match(procps_status_t *p, const char *procName)
{
int argv1idx;
diff --git a/libbb/getopt32.c b/libbb/getopt32.c
index 17babcd..198149d 100644
--- a/libbb/getopt32.c
+++ b/libbb/getopt32.c
@@ -3,6 +3,7 @@
* universal getopt32 implementation for busybox
*
* Copyright (C) 2003-2005 Vladimir Oleynik <dzo@simtreas.ru>
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
@@ -327,6 +328,7 @@ getopt32(char **argv, const char *applet_opts, ...)
unsigned flags = 0;
unsigned requires = 0;
t_complementary complementary[33]; /* last stays zero-filled */
+ char first_char;
int c;
const unsigned char *s;
t_complementary *on_off;
@@ -357,6 +359,11 @@ getopt32(char **argv, const char *applet_opts, ...)
on_off = complementary;
memset(on_off, 0, sizeof(complementary));
+ /* skip bbox extension */
+ first_char = applet_opts[0];
+ if (first_char == '!')
+ applet_opts++;
+
/* skip GNU extension */
s = (const unsigned char *)applet_opts;
if (*s == '+' || *s == '-')
@@ -549,11 +556,11 @@ getopt32(char **argv, const char *applet_opts, ...)
* is always NULL (see above) */
if (on_off->opt_char == '\0' /* && c != '\0' */) {
/* c is probably '?' - "bad option" */
- bb_show_usage();
+ goto error;
}
}
if (flags & on_off->incongruously)
- bb_show_usage();
+ goto error;
trigger = on_off->switch_on & on_off->switch_off;
flags &= ~(on_off->switch_off ^ trigger);
flags |= on_off->switch_on ^ trigger;
@@ -579,14 +586,19 @@ getopt32(char **argv, const char *applet_opts, ...)
for (on_off = complementary; on_off->opt_char; on_off++) {
if (on_off->requires && (flags & on_off->switch_on) &&
(flags & on_off->requires) == 0)
- bb_show_usage();
+ goto error;
}
if (requires && (flags & requires) == 0)
- bb_show_usage();
+ goto error;
argc -= optind;
if (argc < min_arg || (max_arg >= 0 && argc > max_arg))
- bb_show_usage();
+ goto error;
option_mask32 = flags;
return flags;
+
+ error:
+ if (first_char != '!')
+ bb_show_usage();
+ return (int32_t)-1;
}
diff --git a/libbb/strlcat.c b/libbb/strlcat.c
new file mode 100644
index 0000000..6dd7747
--- /dev/null
+++ b/libbb/strlcat.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2002 Manuel Novoa III
+ * Copyright (C) 2000-2005 Erik Andersen <andersen@uclibc.org>
+ *
+ * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+ */
+
+/* OpenBSD function:
+ * Append at most n-1-strlen(dst) chars from src to dst and nul-terminate dst.
+ * Returns strlen(src) + strlen({original} dst), so truncation occurred if the
+ * return val is >= n.
+ * Note: If dst doesn't contain a nul in the first n chars, strlen(dst) is
+ * taken as n. */
+
+#include "libbb.h"
+
+size_t strlcat(register char *__restrict dst,
+ register const char *__restrict src,
+ size_t n)
+{
+ size_t len;
+ char dummy[1];
+
+ len = 0;
+
+ while (1) {
+ if (len >= n) {
+ dst = dummy;
+ break;
+ }
+ if (!*dst) {
+ break;
+ }
+ ++dst;
+ ++len;
+ }
+
+ while ((*dst = *src) != 0) {
+ if (++len < n) {
+ ++dst;
+ }
+ ++src;
+ }
+
+ return len;
+}
diff --git a/libbb/strlcpy.c b/libbb/strlcpy.c
new file mode 100644
index 0000000..02cd55a
--- /dev/null
+++ b/libbb/strlcpy.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2002 Manuel Novoa III
+ * Copyright (C) 2000-2005 Erik Andersen <andersen@uclibc.org>
+ *
+ * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+ */
+
+#include "libbb.h"
+
+#ifdef WANT_WIDE
+# define Wstrlcpy __wcslcpy
+# define Wstrxfrm wcsxfrm
+#else
+// libc_hidden_proto(strlcpy)
+# define Wstrlcpy strlcpy
+# define Wstrxfrm strxfrm
+# define Wchar char
+#endif
+
+
+/* OpenBSD function:
+ * Copy at most n-1 chars from src to dst and nul-terminate dst.
+ * Returns strlen(src), so truncation occurred if the return value is >= n. */
+
+#ifdef WANT_WIDE
+size_t Wstrlcpy(register Wchar *__restrict dst,
+ register const Wchar *__restrict src,
+ size_t n) attribute_hidden;
+#endif
+size_t Wstrlcpy(register Wchar *__restrict dst,
+ register const Wchar *__restrict src,
+ size_t n)
+{
+ const Wchar *src0 = src;
+ Wchar dummy[1];
+
+ if (!n) {
+ dst = dummy;
+ } else {
+ --n;
+ }
+
+ while ((*dst = *src) != 0) {
+ if (n) {
+ --n;
+ ++dst;
+ }
+ ++src;
+ }
+
+ return src - src0;
+}
+#ifndef WANT_WIDE
+//libc_hidden_def(strlcpy)
+#ifndef __UCLIBC_HAS_LOCALE__
+//libc_hidden_proto(strxfrm)
+//strong_alias(strlcpy,strxfrm)
+//libc_hidden_def(strxfrm)
+#endif
+#else
+#ifndef __UCLIBC_HAS_LOCALE__
+strong_alias(__wcslcpy,wcsxfrm)
+#endif
+#endif
diff --git a/libbb/validate_filename.c b/libbb/validate_filename.c
new file mode 100644
index 0000000..2b9b80a
--- /dev/null
+++ b/libbb/validate_filename.c
@@ -0,0 +1,33 @@
+#include "libbb.h"
+
+int validate_filename(const char *path, const char *prefix)
+{
+ size_t path_len, prefix_len;
+
+ /* Check for the following properties:
+ * 1) path start with prefix
+ * 2) the next character after prefix is a '/'
+ * 3) path does not contain '/../'
+ * 4) path does not end in '/..'
+ * return 0 if any of the properties does not hold
+ * return 1 if all properties hold
+ */
+ path_len= strlen(path);
+ prefix_len= strlen(prefix);
+ if (path_len < prefix_len)
+ return 0;
+
+ if (memcmp(path, prefix, prefix_len) != 0)
+ return 0; /* property 1 */
+
+ if (path[prefix_len] != '/')
+ return 0; /* property 2 */
+
+ if (strstr(path, "/../") != NULL)
+ return 0; /* property 3 */
+
+ if (path_len >= 3 && strcmp(&path[path_len-3], "/..") == 0)
+ return 0; /* property 4 */
+
+ return 1;
+}
diff --git a/libbb/xconnect.c b/libbb/xconnect.c
index 27c7424..f313655 100644
--- a/libbb/xconnect.c
+++ b/libbb/xconnect.c
@@ -32,6 +32,24 @@ int FAST_FUNC setsockopt_bindtodevice(int fd, const char *iface)
return r;
}
+len_and_sockaddr* FAST_FUNC get_sock_lsa(int fd)
+{
+ len_and_sockaddr lsa;
+ len_and_sockaddr *lsa_ptr;
+
+ lsa.len = LSA_SIZEOF_SA;
+ if (getsockname(fd, &lsa.u.sa, &lsa.len) != 0)
+ return NULL;
+
+ lsa_ptr = xzalloc(LSA_LEN_SIZE + lsa.len);
+ if (lsa.len > LSA_SIZEOF_SA) { /* rarely (if ever) happens */
+ lsa_ptr->len = lsa.len;
+ getsockname(fd, &lsa_ptr->u.sa, &lsa_ptr->len);
+ } else {
+ memcpy(lsa_ptr, &lsa, LSA_LEN_SIZE + lsa.len);
+ }
+ return lsa_ptr;
+}
void FAST_FUNC xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen)
{
@@ -46,6 +64,26 @@ void FAST_FUNC xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen)
}
}
+void FAST_FUNC xrconnect(int s,
+ const struct sockaddr *s_addr, socklen_t addrlen,
+ void (*reportf)(int err))
+{
+ if (connect(s, s_addr, addrlen) < 0) {
+ if (reportf) {
+ int t_errno= errno;
+ reportf(t_errno);
+ errno= t_errno;
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(s);
+ if (s_addr->sa_family == AF_INET)
+ bb_perror_msg_and_die("%s (%s)",
+ "cannot connect to remote host",
+ inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr));
+ bb_perror_msg_and_die("cannot connect to remote host");
+ }
+}
+
/* Return port number for a service.
* If "port" is a number use it as the port.
* If "port" is a name it is looked up in /etc/services, if it isnt found return
diff --git a/libbb/xfuncs_printf.c b/libbb/xfuncs_printf.c
index 108e140..0bee40a 100644
--- a/libbb/xfuncs_printf.c
+++ b/libbb/xfuncs_printf.c
@@ -409,6 +409,21 @@ void FAST_FUNC xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen)
if (bind(sockfd, my_addr, addrlen)) bb_perror_msg_and_die("bind");
}
+// Call a user supplied reporting function and die with an error message if we
+// can't bind a socket to an address.
+void FAST_FUNC xrbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen,
+ void (*reportf)(int err))
+{
+ if (bind(sockfd, my_addr, addrlen)) {
+ if (reportf) {
+ int t_errno= errno;
+ reportf(t_errno);
+ errno= t_errno;
+ }
+ bb_perror_msg_and_die("bind");
+ }
+}
+
// Die with an error message if we can't listen for connections on a socket.
void FAST_FUNC xlisten(int s, int backlog)
{
@@ -429,6 +444,47 @@ ssize_t FAST_FUNC xsendto(int s, const void *buf, size_t len, const struct sock
return ret;
}
+/* Call a user supplied function and die with an error message if sendto failed.
+ * Return bytes sent otherwise */
+ssize_t FAST_FUNC xrsendto(int s, const void *buf, size_t len,
+ const struct sockaddr *to, socklen_t tolen,
+ void (*reportf)(int err))
+{
+ ssize_t ret = sendto(s, buf, len, 0, to, tolen);
+ if (ret < 0) {
+ if (reportf) {
+ int t_errno= errno;
+ reportf(t_errno);
+ t_errno= errno;
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(s);
+ bb_perror_msg_and_die("sendto");
+ }
+ return ret;
+}
+
+/* Call a user supplied function with an error message if sendto failed.
+ * Return bytes sent otherwise */
+ssize_t FAST_FUNC rsendto(int s, const void *buf, size_t len,
+ const struct sockaddr *to, socklen_t tolen,
+ void (*reportf)(int err))
+{
+ ssize_t ret = sendto(s, buf, len, 0, to, tolen);
+ if (ret < 0) {
+ int t_errno= errno;
+ if (reportf) {
+ reportf(t_errno);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(s);
+ errno= t_errno;
+ bb_perror_msg("sendto");
+ errno= t_errno;
+ }
+ return ret;
+}
+
// xstat() - a stat() which dies on failure with meaningful error message
void FAST_FUNC xstat(const char *name, struct stat *stat_buf)
{
diff --git a/libevent-2.0.20-stable/configure b/libevent-2.0.20-stable/configure
index d2bd3cb..af5ad31 100755
--- a/libevent-2.0.20-stable/configure
+++ b/libevent-2.0.20-stable/configure
@@ -555,7 +555,7 @@ ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
#
# Initializations.
#
-ac_default_prefix=/usr/local
+ac_default_prefix=/usr/local/atlas
ac_clean_files=
ac_config_libobj_dir=.
LIBOBJS=
diff --git a/libevent-2.0.20-stable/evdns.c b/libevent-2.0.20-stable/evdns.c
index e9cea45..8beee70 100644
--- a/libevent-2.0.20-stable/evdns.c
+++ b/libevent-2.0.20-stable/evdns.c
@@ -408,6 +408,7 @@ static int evdns_base_resolv_conf_parse_impl(struct evdns_base *base, int flags,
static int evdns_base_set_option_impl(struct evdns_base *base,
const char *option, const char *val, int flags);
static void evdns_base_free_and_unlock(struct evdns_base *base, int fail_requests);
+static void evdns_request_timeout_callback(evutil_socket_t fd, short events, void *arg);
static int strtoint(const char *const str);
@@ -894,7 +895,9 @@ reply_handle(struct request *const req, u16 flags, u32 ttl, struct reply *reply)
evutil_format_sockaddr_port(
(struct sockaddr *)&req->ns->address,
addrbuf, sizeof(addrbuf)));
- break;
+ /* Call the timneout function */
+ evdns_request_timeout_callback(0, 0, req);
+ return;
default:
/* we got a good reply from the nameserver: it is up. */
if (req->handle == req->ns->probe_request) {
diff --git a/loginutils/passwd.c b/loginutils/passwd.c
index 99fb76e..851936a 100644
--- a/loginutils/passwd.c
+++ b/loginutils/passwd.c
@@ -6,67 +6,21 @@
#include "libbb.h"
#include <syslog.h>
-static void nuke_str(char *str)
-{
- if (str) memset(str, 0, strlen(str));
-}
-static char* new_password(const struct passwd *pw, uid_t myuid, int algo)
+static char* new_password( int algo, char *pass)
{
char salt[sizeof("$N$XXXXXXXX")]; /* "$N$XXXXXXXX" or "XX" */
- char *orig = (char*)"";
- char *newp = NULL;
- char *cp = NULL;
char *ret = NULL; /* failure so far */
- if (myuid && pw->pw_passwd[0]) {
- char *encrypted;
-
- orig = bb_askpass(0, "Old password:"); /* returns ptr to static */
- if (!orig)
- goto err_ret;
- encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */
- if (strcmp(encrypted, pw->pw_passwd) != 0) {
- syslog(LOG_WARNING, "incorrect password for %s",
- pw->pw_name);
- bb_do_delay(FAIL_DELAY);
- puts("Incorrect password");
- goto err_ret;
- }
- if (ENABLE_FEATURE_CLEAN_UP) free(encrypted);
- }
- orig = xstrdup(orig); /* or else bb_askpass() will destroy it */
- newp = bb_askpass(0, "New password:"); /* returns ptr to static */
- if (!newp)
- goto err_ret;
- newp = xstrdup(newp); /* we are going to bb_askpass() again, so save it */
- if (ENABLE_FEATURE_PASSWD_WEAK_CHECK
- && obscure(orig, newp, pw) && myuid)
- goto err_ret; /* non-root is not allowed to have weak passwd */
-
- cp = bb_askpass(0, "Retype password:");
- if (!cp)
- goto err_ret;
- if (strcmp(cp, newp)) {
- puts("Passwords don't match");
- goto err_ret;
- }
-
crypt_make_salt(salt, 1, 0); /* des */
if (algo) { /* MD5 */
strcpy(salt, "$1$");
crypt_make_salt(salt + 3, 4, 0);
}
/* pw_encrypt returns malloced str */
- ret = pw_encrypt(newp, salt, 1);
+ ret = pw_encrypt(pass, salt, 1);
/* whee, success! */
- err_ret:
- nuke_str(orig);
- if (ENABLE_FEATURE_CLEAN_UP) free(orig);
- nuke_str(newp);
- if (ENABLE_FEATURE_CLEAN_UP) free(newp);
- nuke_str(cp);
return ret;
}
@@ -89,10 +43,10 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
char *myname;
char *name;
char *newp;
+ char *pass;
struct passwd *pw;
uid_t myuid;
struct rlimit rlimit_fsize;
- char c;
#if ENABLE_FEATURE_SHADOWPASSWDS
/* Using _r function to avoid pulling in static buffers */
struct spwd spw;
@@ -116,8 +70,13 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
/* Will complain and die if username not found */
myname = xstrdup(bb_getpwuid(NULL, -1, myuid));
- name = argv[0] ? argv[0] : myname;
+ if( argc<3 ) {
+ bb_error_msg_and_die("You should supply a name ans a password");
+ }
+
+ name = argv[0];
+ pass = argv[1];
pw = getpwnam(name);
if (!pw)
bb_error_msg_and_die("unknown user %s", name);
@@ -126,6 +85,8 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
bb_error_msg_and_die("%s can't change password for %s", myname, name);
}
+ newp = new_password( opt & STATE_ALGO_md5, pass);
+
#if ENABLE_FEATURE_SHADOWPASSWDS
{
/* getspnam_r may return 0 yet set result to NULL.
@@ -143,34 +104,6 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
}
#endif
- /* Decide what the new password will be */
- newp = NULL;
- c = pw->pw_passwd[0] - '!';
- if (!(opt & OPT_lud)) {
- if (myuid && !c) { /* passwd starts with '!' */
- /* LOGMODE_BOTH */
- bb_error_msg_and_die("cannot change "
- "locked password for %s", name);
- }
- printf("Changing password for %s\n", name);
- newp = new_password(pw, myuid, opt & STATE_ALGO_md5);
- if (!newp) {
- logmode = LOGMODE_STDIO;
- bb_error_msg_and_die("password for %s is unchanged", name);
- }
- } else if (opt & OPT_lock) {
- if (!c) goto skip; /* passwd starts with '!' */
- newp = xasprintf("!%s", pw->pw_passwd);
- } else if (opt & OPT_unlock) {
- if (c) goto skip; /* not '!' */
- /* pw->pw_passwd points to static storage,
- * strdup'ing to avoid nasty surprizes */
- newp = xstrdup(&pw->pw_passwd[1]);
- } else if (opt & OPT_delete) {
- //newp = xstrdup("");
- newp = (char*)"";
- }
-
rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000;
setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
bb_signals(0
@@ -183,7 +116,7 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
#if ENABLE_FEATURE_SHADOWPASSWDS
filename = bb_path_shadow_file;
- rc = update_passwd(bb_path_shadow_file, name, newp);
+ rc = update_passwd(bb_path_shadow_file, name, newp );
if (rc == 0) /* no lines updated, no errors detected */
#endif
{
@@ -197,7 +130,7 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
bb_info_msg("Password for %s changed by %s", name, myname);
//if (ENABLE_FEATURE_CLEAN_UP) free(newp);
- skip:
+
if (!newp) {
bb_error_msg_and_die("password for %s is already %slocked",
name, (opt & OPT_unlock) ? "un" : "");
diff --git a/miscutils/Config.in b/miscutils/Config.in
index 60b87c1..8dd4187 100644
--- a/miscutils/Config.in
+++ b/miscutils/Config.in
@@ -453,6 +453,21 @@ config MT
to advance or rewind a tape past a specified number of archive
files on the tape.
+config PERD
+ bool "perd"
+ default n
+ select FEATURE_SUID
+ select FEATURE_SYSLOG
+ help
+ Perd periodically runs Atlas measurements. It is based on crond.
+
+config FEATURE_PERD_D
+ bool "Support option -d to redirect output to stderr"
+ depends on PERD
+ default n
+ help
+ -d sets loglevel to 0 (most verbose) and directs all output to stderr.
+
config RAIDAUTORUN
bool "raidautorun"
default n
diff --git a/miscutils/Kbuild b/miscutils/Kbuild
index 13791ef..537999b 100644
--- a/miscutils/Kbuild
+++ b/miscutils/Kbuild
@@ -26,6 +26,7 @@ lib-$(CONFIG_MAN) += man.o
lib-$(CONFIG_MICROCOM) += microcom.o
lib-$(CONFIG_MOUNTPOINT) += mountpoint.o
lib-$(CONFIG_MT) += mt.o
+lib-$(CONFIG_PERD) += perd.o
lib-$(CONFIG_RAIDAUTORUN) += raidautorun.o
lib-$(CONFIG_READAHEAD) += readahead.o
lib-$(CONFIG_RUNLEVEL) += runlevel.o
diff --git a/miscutils/perd.c b/miscutils/perd.c
new file mode 100644
index 0000000..1058dea
--- /dev/null
+++ b/miscutils/perd.c
@@ -0,0 +1,1483 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * perd formerly crond but now heavily hacked for Atlas
+ *
+ * crond -d[#] -c <crondir> -f -b
+ *
+ * run as root, but NOT setuid root
+ *
+ * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
+ * (version 2.3.2)
+ * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "libbb.h"
+#include <syslog.h>
+
+#define ATLAS 1
+#define ATLAS_NEW_FORMAT 1
+
+#define DBQ(str) "\"" #str "\""
+
+/* glibc frees previous setenv'ed value when we do next setenv()
+ * of the same variable. uclibc does not do this! */
+#if (defined(__GLIBC__) && !defined(__UCLIBC__)) /* || OTHER_SAFE_LIBC... */
+#define SETENV_LEAKS 0
+#else
+#define SETENV_LEAKS 1
+#endif
+
+
+#ifndef CRONTABS
+#define CRONTABS "/var/spool/cron/crontabs"
+#endif
+#ifndef TMPDIR
+#define TMPDIR "/var/spool/cron"
+#endif
+#ifndef CRONUPDATE
+#define CRONUPDATE "cron.update"
+#endif
+#ifndef MAXLINES
+#define MAXLINES 256 /* max lines in non-root crontabs */
+#endif
+
+#ifdef ATLAS
+#include <cmdtable.h>
+
+#define SAFE_PREFIX ATLAS_DATA_NEW
+#endif
+
+#if ATLAS_NEW_FORMAT
+#define URANDOM_DEV "/dev/urandom"
+#endif
+
+
+typedef struct CronFile {
+ struct CronFile *cf_Next;
+ struct CronLine *cf_LineBase;
+ char *cf_User; /* username */
+ smallint cf_Ready; /* bool: one or more jobs ready */
+ smallint cf_Running; /* bool: one or more jobs running */
+ smallint cf_ToBeDeleted; /* marked for deletion, ignore */
+ smallint cf_Deleted; /* deleted but some entries are
+ * still busy
+ */
+} CronFile;
+
+typedef struct CronLine {
+ struct CronLine *cl_Next;
+ char *cl_Shell; /* shell command */
+ pid_t cl_Pid; /* running pid, 0, or armed (-1) */
+#if ATLAS_NEW_FORMAT
+ unsigned interval;
+ time_t nextcycle;
+ time_t start_time;
+ time_t end_time;
+ enum distribution { DISTR_NONE, DISTR_UNIFORM } distribution;
+ int distr_param; /* Parameter for distribution, if any */
+ int distr_offset; /* Current offset to randomize the interval */
+
+ /* For debugging */
+ time_t lasttime;
+#else
+ /* ordered by size, not in natural order. makes code smaller: */
+ char cl_Dow[7]; /* 0-6, beginning sunday */
+ char cl_Mons[12]; /* 0-11 */
+ char cl_Hrs[24]; /* 0-23 */
+ char cl_Days[32]; /* 1-31 */
+ char cl_Mins[60]; /* 0-59 */
+#endif /* ATLAS_NEW_FORMAT */
+} CronLine;
+
+
+#define DaemonUid 0
+
+
+enum {
+ OPT_l = (1 << 0),
+ OPT_L = (1 << 1),
+ OPT_f = (1 << 2),
+ OPT_b = (1 << 3),
+ OPT_S = (1 << 4),
+ OPT_c = (1 << 5),
+ OPT_A = (1 << 6),
+ OPT_D = (1 << 7),
+ OPT_d = (1 << 8) * ENABLE_FEATURE_CROND_D,
+};
+#if ENABLE_FEATURE_CROND_D
+#define DebugOpt (option_mask32 & OPT_d)
+#else
+#define DebugOpt 0
+#endif
+
+
+struct globals {
+ unsigned LogLevel; /* = 8; */
+ const char *LogFile;
+ const char *CDir; /* = CRONTABS; */
+ CronFile *FileBase;
+ CronFile *oldFile;
+ CronLine *oldLine;
+#if SETENV_LEAKS
+ char *env_var_user;
+ char *env_var_home;
+#endif
+};
+#ifdef ATLAS
+static struct globals G;
+#else
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#endif
+#define LogLevel (G.LogLevel )
+#define LogFile (G.LogFile )
+#define CDir (G.CDir )
+#define FileBase (G.FileBase )
+#define oldFile (G.oldFile )
+#define oldLine (G.oldLine )
+#define env_var_user (G.env_var_user )
+#define env_var_home (G.env_var_home )
+#define INIT_G() do { \
+ LogLevel = 8; \
+ CDir = CRONTABS; \
+} while (0)
+
+#ifdef ATLAS
+static int do_kick_watchdog;
+static char *atlas_id= NULL;
+static char *out_filename= NULL;
+
+static int atlas_run(char *cmdline);
+#endif
+
+static void CheckUpdates(void);
+static void SynchronizeDir(void);
+#if ATLAS_NEW_FORMAT
+static int TestJobs(time_t *nextp);
+#else
+static int TestJobs(time_t t1, time_t t2);
+#endif
+static void RunJobs(void);
+static int CheckJobs(void);
+static void RunJob(const char *user, CronLine *line);
+#define EndJob(user, line) ((line)->cl_Pid = 0)
+static void DeleteFile(CronFile *tfile);
+static void SetOld(const char *userName);
+static void CopyFromOld(CronLine *line);
+
+
+#define LVL5 "\x05"
+#define LVL7 "\x07"
+#define LVL8 "\x08"
+#define LVL9 "\x09"
+#define WARN9 "\x49"
+#define DIE9 "\xc9"
+/* level >= 20 is "error" */
+#define ERR20 "\x14"
+
+static void crondlog(const char *ctl, ...)
+{
+ va_list va;
+ int level = (ctl[0] & 0x1f);
+
+ va_start(va, ctl);
+ if (level >= (int)LogLevel) {
+ /* Debug mode: all to (non-redirected) stderr, */
+ /* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */
+ if (!DebugOpt && LogFile) {
+ /* Otherwise (log to file): we reopen log file at every write: */
+ int logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600);
+ if (logfd >= 0)
+ xmove_fd(logfd, STDERR_FILENO);
+ }
+// TODO: ERR -> error, WARN -> warning, LVL -> info
+ bb_verror_msg(ctl + 1, va, /* strerr: */ NULL);
+ }
+ va_end(va);
+ if (ctl[0] & 0x80)
+ exit(20);
+}
+
+static void my_exit(void)
+{
+ crondlog(LVL8 "in my_exit (exit was called!)");
+ abort();
+}
+
+static void kick_watchdog(void)
+{
+ if(do_kick_watchdog)
+ {
+ int fdwatchdog = open("/dev/watchdog", O_RDWR);
+ write(fdwatchdog, "1", 1);
+ close(fdwatchdog);
+ }
+}
+
+int perd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int perd_main(int argc UNUSED_PARAM, char **argv)
+{
+ unsigned opt;
+#if ATLAS_NEW_FORMAT
+ int fd;
+ unsigned seed;
+#endif
+
+ const char *PidFileName = NULL;
+ atexit(my_exit);
+
+ 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" USE_FEATURE_PERD_D(":d-l")
+ ":l+:d+"; /* -l and -d have numeric param */
+ opt = getopt32(argv, "l:L:fbSc:A:DP:" USE_FEATURE_PERD_D("d:") "O:",
+ &LogLevel, &LogFile, &CDir, &atlas_id, &PidFileName
+ USE_FEATURE_PERD_D(,&LogLevel), &out_filename);
+ /* both -d N and -l N set the same variable: LogLevel */
+
+ if (!(opt & OPT_f)) {
+ /* close stdin, stdout, stderr.
+ * close unused descriptors - don't need them. */
+ bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
+ }
+
+ if (!DebugOpt && LogFile == NULL) {
+ /* logging to syslog */
+ openlog(applet_name, LOG_CONS | LOG_PID, LOG_LOCAL6);
+ logmode = LOGMODE_SYSLOG;
+ }
+
+ do_kick_watchdog= !!(opt & OPT_D);
+
+ xchdir(CDir);
+ //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */
+ xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */
+ crondlog(LVL9 "crond (busybox "BB_VER") started, log level %d", LogLevel);
+
+#if ATLAS_NEW_FORMAT
+ fd= open(URANDOM_DEV, O_RDONLY);
+
+ /* Best effort, just ignore errors */
+ if (fd != -1)
+ {
+ read(fd, &seed, sizeof(seed));
+ close(fd);
+ }
+ crondlog(LVL7 "using seed '%u'", seed);
+ srandom(seed);
+#endif
+
+ SynchronizeDir();
+
+ /* main loop - synchronize to 1 second after the minute, minimum sleep
+ * of 1 second. */
+ {
+ time_t t1 = time(NULL);
+#if ATLAS_NEW_FORMAT
+ time_t next;
+ time_t last_minutely= 0;
+ time_t last_hourly= 0;
+#else
+ time_t t2;
+ long dt;
+ int rescan = 60;
+#endif
+ int sleep_time = 10; /* AA previously 60 */
+ if(PidFileName)
+ {
+ write_pidfile(PidFileName);
+ }
+ else
+ {
+ write_pidfile("/var/run/crond.pid");
+ }
+ for (;;) {
+ kick_watchdog();
+#if ATLAS_NEW_FORMAT
+ sleep(sleep_time);
+#else
+ sleep((sleep_time + 1) - (time(NULL) % sleep_time));
+#endif
+
+ kick_watchdog();
+
+#if ATLAS_NEW_FORMAT
+ if (t1 >= last_minutely + 60)
+ {
+ last_minutely= t1;
+ CheckUpdates();
+ }
+ if (t1 >= last_hourly + 3600)
+ {
+ last_hourly= t1;
+ SynchronizeDir();
+ }
+#else
+ t2 = time(NULL);
+ dt = (long)t2 - (long)t1;
+
+ /*
+ * 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 disparity. Disparities over an hour either way
+ * result in resynchronization. A reverse-indexed disparity
+ * less then 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 forward-indexed disparity less then
+ * 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 (--rescan == 0) {
+ rescan = 60;
+ SynchronizeDir();
+ }
+ CheckUpdates();
+ if (DebugOpt)
+ crondlog(LVL5 "wakeup dt=%ld", dt);
+ if (dt < -60 * 60 || dt > 60 * 60) {
+ crondlog(WARN9 "time disparity of %d minutes detected", dt / 60);
+ } else if (dt > 0)
+#endif /* ATLAS_NEW_FORMAT */
+ {
+#if ATLAS_NEW_FORMAT
+ sleep_time= 60;
+ if (do_kick_watchdog)
+ sleep_time= 10;
+ TestJobs(&next);
+ crondlog(LVL7 "got next %d, now %d",
+ next, time(NULL));
+ if (!next)
+ {
+ crondlog(LVL7 "calling RunJobs at %d",
+ time(NULL));
+ RunJobs();
+ crondlog(LVL7 "RunJobs ended at %d",
+ time(NULL));
+ sleep_time= 1;
+ } else if (next > t1 && next < t1+sleep_time)
+ sleep_time= next-t1;
+ if (CheckJobs() > 0) {
+ sleep_time = 10;
+ }
+ crondlog(
+ LVL7 "t1 = %d, next = %d, sleep_time = %d",
+ t1, next, sleep_time);
+#else
+ TestJobs(t1, t2);
+ RunJobs();
+ sleep(4);
+ if (CheckJobs() > 0) {
+ sleep_time = 10;
+ } else {
+ sleep_time = 10; /* AA previously 60 */
+ }
+#endif
+ }
+#if ATLAS_NEW_FORMAT
+ t1= time(NULL);
+#else
+ t1 = t2;
+#endif
+ }
+ }
+ return 0; /* not reached */
+}
+
+#if SETENV_LEAKS
+/* We set environment *before* vfork (because we want to use vfork),
+ * so we cannot use setenv() - repeated calls to setenv() may leak memory!
+ * Using putenv(), and freeing memory after unsetenv() won't leak */
+static void safe_setenv4(char **pvar_val, const char *var, const char *val /*, int len*/)
+{
+ const int len = 4; /* both var names are 4 char long */
+ char *var_val = *pvar_val;
+
+ if (var_val) {
+ var_val[len] = '\0'; /* nuke '=' */
+ unsetenv(var_val);
+ free(var_val);
+ }
+ *pvar_val = xasprintf("%s=%s", var, val);
+ putenv(*pvar_val);
+}
+#endif
+
+static void SetEnv(struct passwd *pas)
+{
+#if SETENV_LEAKS
+ safe_setenv4(&env_var_user, "USER", pas->pw_name);
+ safe_setenv4(&env_var_home, "HOME", pas->pw_dir);
+ /* if we want to set user's shell instead: */
+ /*safe_setenv(env_var_user, "SHELL", pas->pw_shell, 5);*/
+#else
+ xsetenv("USER", pas->pw_name);
+ xsetenv("HOME", pas->pw_dir);
+#endif
+ /* currently, we use constant one: */
+ /*setenv("SHELL", DEFAULT_SHELL, 1); - done earlier */
+}
+
+static void ChangeUser(struct passwd *pas)
+{
+ /* careful: we're after vfork! */
+ change_identity(pas); /* - initgroups, setgid, setuid */
+ if (chdir(pas->pw_dir) < 0) {
+ crondlog(LVL9 "can't chdir(%s)", pas->pw_dir);
+ if (chdir(TMPDIR) < 0) {
+ crondlog(DIE9 "can't chdir(%s)", TMPDIR); /* exits */
+ }
+ }
+}
+
+static const char DowAry[] ALIGN1 =
+ "sun""mon""tue""wed""thu""fri""sat"
+ /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */
+;
+
+static const char MonAry[] ALIGN1 =
+ "jan""feb""mar""apr""may""jun""jul""aug""sep""oct""nov""dec"
+ /* "Jan""Feb""Mar""Apr""May""Jun""Jul""Aug""Sep""Oct""Nov""Dec" */
+;
+
+#if !ATLAS_NEW_FORMAT
+static void ParseField(char *user, char *ary, int modvalue, int off,
+ const char *names, char *ptr)
+/* 'names' is a pointer to a set of 3-char abbreviations */
+{
+ char *base = ptr;
+ int n1 = -1;
+ int n2 = -1;
+
+ // this can't happen due to config_read()
+ /*if (base == NULL)
+ return;*/
+
+ while (1) {
+ int skip = 0;
+
+ /* Handle numeric digit or symbol or '*' */
+ if (*ptr == '*') {
+ n1 = 0; /* everything will be filled */
+ n2 = modvalue - 1;
+ skip = 1;
+ ++ptr;
+ } else if (isdigit(*ptr)) {
+ if (n1 < 0) {
+ n1 = strtol(ptr, &ptr, 10) + off;
+ } else {
+ n2 = strtol(ptr, &ptr, 10) + off;
+ }
+ skip = 1;
+ } else if (names) {
+ int i;
+
+ for (i = 0; names[i]; i += 3) {
+ /* was using strncmp before... */
+ if (strncasecmp(ptr, &names[i], 3) == 0) {
+ ptr += 3;
+ if (n1 < 0) {
+ n1 = i / 3;
+ } else {
+ n2 = i / 3;
+ }
+ skip = 1;
+ break;
+ }
+ }
+ }
+
+ /* handle optional range '-' */
+ if (skip == 0) {
+ goto err;
+ }
+ if (*ptr == '-' && n2 < 0) {
+ ++ptr;
+ continue;
+ }
+
+ /*
+ * collapse single-value ranges, handle skipmark, and fill
+ * in the character array appropriately.
+ */
+ if (n2 < 0) {
+ n2 = n1;
+ }
+ if (*ptr == '/') {
+ skip = strtol(ptr + 1, &ptr, 10);
+ }
+
+ /*
+ * fill array, using a failsafe is the easiest way to prevent
+ * an endless loop
+ */
+ {
+ int s0 = 1;
+ int failsafe = 1024;
+
+ --n1;
+ do {
+ n1 = (n1 + 1) % modvalue;
+
+ if (--s0 == 0) {
+ ary[n1 % modvalue] = 1;
+ s0 = skip;
+ }
+ if (--failsafe == 0) {
+ goto err;
+ }
+ } while (n1 != n2);
+
+ }
+ if (*ptr != ',') {
+ break;
+ }
+ ++ptr;
+ n1 = -1;
+ n2 = -1;
+ }
+
+ if (*ptr) {
+ err:
+ crondlog(WARN9 "user %s: parse error at %s", user, base);
+ return;
+ }
+
+ if (DebugOpt && (LogLevel <= 5)) { /* like LVL5 */
+ /* can't use crondlog, it inserts '\n' */
+ int i;
+ for (i = 0; i < modvalue; ++i)
+ fprintf(stderr, "%d", (unsigned char)ary[i]);
+ fputc('\n', stderr);
+ }
+}
+#endif /* !ATLAS_NEW_FORMAT */
+
+#if !ATLAS_NEW_FORMAT
+static void FixDayDow(CronLine *line)
+{
+ unsigned i;
+ int weekUsed = 0;
+ int daysUsed = 0;
+
+ for (i = 0; i < ARRAY_SIZE(line->cl_Dow); ++i) {
+ if (line->cl_Dow[i] == 0) {
+ weekUsed = 1;
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(line->cl_Days); ++i) {
+ if (line->cl_Days[i] == 0) {
+ daysUsed = 1;
+ break;
+ }
+ }
+ if (weekUsed != daysUsed) {
+ if (weekUsed)
+ memset(line->cl_Days, 0, sizeof(line->cl_Days));
+ else /* daysUsed */
+ memset(line->cl_Dow, 0, sizeof(line->cl_Dow));
+ }
+}
+#endif /* !ATLAS_NEW_FORMAT */
+
+static void do_distr(CronLine *line)
+{
+ long n, r, modulus, max;
+
+ line->distr_offset= 0; /* Safe default */
+ if (line->distribution == DISTR_UNIFORM)
+ {
+ /* Generate a random number in the range [0..distr_param] */
+ modulus= line->distr_param+1;
+ n= LONG_MAX/modulus;
+ max= n*modulus;
+ do
+ {
+ r= random();
+ } while (r >= max);
+ r %= modulus;
+ line->distr_offset= r - line->distr_param/2;
+ }
+ crondlog(LVL7 "do_distr: using %d", line->distr_offset);
+}
+
+static void SynchronizeFile(const char *fileName)
+{
+ struct parser_t *parser;
+ struct stat sbuf;
+ int maxLines;
+ char *tokens[6];
+#if ATLAS_NEW_FORMAT
+ char *check0, *check1, *check2;
+ time_t now;
+#endif
+
+ if (!fileName)
+ return;
+
+ SetOld(fileName);
+
+ parser = config_open(fileName);
+ if (!parser)
+ {
+ /* We have to get rid of the old entries if the file is not
+ * there. Assume a non-existant file is the only reason for
+ * failure.
+ */
+ DeleteFile(oldFile);
+ return;
+ }
+
+ maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES;
+
+#if ATLAS_NEW_FORMAT
+ now= time(NULL);
+#endif
+
+ if (fstat(fileno(parser->fp), &sbuf) == 0 /* && sbuf.st_uid == DaemonUid */ ) {
+ CronFile *file = xzalloc(sizeof(CronFile));
+ CronLine **pline;
+ int n;
+
+ file->cf_User = xstrdup(fileName);
+ pline = &file->cf_LineBase;
+
+ while (1) {
+ CronLine *line;
+
+ if (!--maxLines)
+ break;
+ n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY);
+ if (!n)
+ break;
+
+ if (DebugOpt)
+ crondlog(LVL5 "user:%s entry:%s", fileName, parser->data);
+
+ /* check if line is setting MAILTO= */
+ if (0 == strncmp(tokens[0], "MAILTO=", 7)) {
+ continue;
+ }
+ /* check if a minimum of tokens is specified */
+ if (n < 6)
+ continue;
+ *pline = line = xzalloc(sizeof(*line));
+#if ATLAS_NEW_FORMAT
+ line->interval= strtoul(tokens[0], &check0, 10);
+ line->start_time= strtoul(tokens[1], &check1, 10);
+ line->end_time= strtoul(tokens[2], &check2, 10);
+
+ line->nextcycle= (now-line->start_time)/
+ line->interval + 1;
+
+ if (check0[0] != '\0' ||
+ check1[0] != '\0' ||
+ check2[0] != '\0')
+ {
+ crondlog(LVL9 "bad crontab line");
+ free(line);
+ continue;
+ }
+
+ if (strcmp(tokens[3], "NONE") == 0)
+ {
+ line->distribution= DISTR_NONE;
+ }
+ else if (strcmp(tokens[3], "UNIFORM") == 0)
+ {
+ line->distribution= DISTR_UNIFORM;
+ line->distr_param=
+ strtoul(tokens[4], &check0, 10);
+ if (check0[0] != '\0')
+ {
+ crondlog(LVL9 "bad crontab line");
+ free(line);
+ continue;
+ }
+ if (line->distr_param == 0 ||
+ LONG_MAX/line->distr_param == 0)
+ {
+ line->distribution= DISTR_NONE;
+ }
+ }
+ do_distr(line);
+
+ line->lasttime= 0;
+#else
+ /* parse date ranges */
+ ParseField(file->cf_User, line->cl_Mins, 60, 0, NULL, tokens[0]);
+ ParseField(file->cf_User, line->cl_Hrs, 24, 0, NULL, tokens[1]);
+ ParseField(file->cf_User, line->cl_Days, 32, 0, NULL, tokens[2]);
+ ParseField(file->cf_User, line->cl_Mons, 12, -1, MonAry, tokens[3]);
+ ParseField(file->cf_User, 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);
+#endif /* ATLAS_NEW_FORMAT */
+ /* copy command */
+ line->cl_Shell = xstrdup(tokens[5]);
+ if (DebugOpt) {
+ crondlog(LVL5 " command:%s", 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]);
+
+ CopyFromOld(line);
+
+ kick_watchdog();
+ }
+ *pline = NULL;
+
+ file->cf_Next = FileBase;
+ FileBase = file;
+
+ if (maxLines == 0) {
+ crondlog(WARN9 "user %s: too many lines", fileName);
+ }
+ }
+ config_close(parser);
+
+ DeleteFile(oldFile);
+}
+
+static void CheckUpdates(void)
+{
+ FILE *fi;
+ char buf[256];
+
+ fi = fopen_for_read(CRONUPDATE);
+ if (fi != NULL) {
+ unlink(CRONUPDATE);
+ while (fgets(buf, sizeof(buf), fi) != NULL) {
+ /* use first word only */
+ SynchronizeFile(strtok(buf, " \t\r\n"));
+ }
+ fclose(fi);
+ }
+}
+
+static void SynchronizeDir(void)
+{
+ CronFile *file;
+
+ /* Mark all file in the current database for deletion */
+ for (file = FileBase; file; file = file->cf_Next) {
+ file->cf_ToBeDeleted= 1;
+ }
+
+ /*
+ * Remove cron update file
+ *
+ * Re-chdir, in case directory was renamed & deleted, or otherwise
+ * screwed up.
+ *
+ * scan directory and add associated users
+ */
+ unlink(CRONUPDATE);
+ if (chdir(CDir) < 0) {
+ crondlog(DIE9 "can't chdir(%s)", CDir);
+ }
+ {
+ DIR *dir = opendir(".");
+ struct dirent *den;
+
+ if (!dir)
+ crondlog(DIE9 "can't chdir(%s)", "."); /* exits */
+ while ((den = readdir(dir)) != NULL) {
+ if (strchr(den->d_name, '.') != NULL) {
+ continue;
+ }
+ if (getpwnam(den->d_name)) {
+ SynchronizeFile(den->d_name);
+ } else {
+ crondlog(LVL7 "ignoring %s", den->d_name);
+ }
+ }
+ closedir(dir);
+ }
+
+ /* Clear the cf_Deleted flags on all file and try to delete everything
+ * marked as cf_ToBeDeleted.
+ */
+ for (file = FileBase; file; file = file->cf_Next) {
+ file->cf_Deleted= 0;
+ }
+
+ again:
+ for (file = FileBase; file; file = file->cf_Next) {
+ if (file->cf_ToBeDeleted && !file->cf_Deleted) {
+ DeleteFile(file);
+ goto again;
+ }
+ }
+
+}
+
+/*
+ * SetOld() - find a user database that is not marked for deletion set.
+ */
+static void SetOld(const char *userName)
+{
+ CronFile *file;
+
+ oldFile= NULL;
+ oldLine= NULL;
+ for (file = FileBase; file; file = file->cf_Next) {
+ if (file->cf_ToBeDeleted)
+ continue;
+ if (strcmp(file->cf_User, userName) != 0)
+ continue;
+ file->cf_ToBeDeleted= 1;
+ oldFile= file;
+ break;
+ }
+}
+
+/*
+ * CopyFromOld - copy nextcycle from old entry
+ */
+static void CopyFromOld(CronLine *line)
+{
+ if (!oldFile)
+ return; /* Nothing to do */
+
+ if (oldLine)
+ {
+ /* Try to match line expected to be next */
+ if (oldLine->interval == line->interval &&
+ oldLine->start_time == line->start_time &&
+ strcmp(oldLine->cl_Shell, line->cl_Shell) == 0)
+ {
+ crondlog(LVL7 "next line matches");
+ ; /* okay */
+ }
+ else
+ oldLine= NULL;
+ }
+
+ if (!oldLine)
+ {
+ /* Try to find one */
+ for (oldLine= oldFile->cf_LineBase; oldLine;
+ oldLine= oldLine->cl_Next)
+ {
+ if (oldLine->interval == line->interval &&
+ oldLine->start_time == line->start_time &&
+ strcmp(oldLine->cl_Shell, line->cl_Shell) == 0)
+ {
+ crondlog(LVL7 "found matching line");
+ break;
+ }
+ }
+ }
+
+ if (!oldLine)
+ {
+ crondlog(LVL7 "found no match for line '%s'",
+ line->cl_Shell);
+ return;
+ }
+
+ crondlog(LVL7 "found old line for '%s'", oldLine->cl_Shell);
+ if (line->nextcycle != oldLine->nextcycle)
+ {
+ crondlog(LVL9 "nextcycle %d -> %d for '%s'",
+ line->nextcycle, oldLine->nextcycle,
+ oldLine->cl_Shell);
+ }
+ line->nextcycle= oldLine->nextcycle;
+
+ if (oldLine->distribution == line->distribution &&
+ oldLine->distr_param == line->distr_param)
+ {
+ line->distr_offset= oldLine->distr_offset;
+ }
+
+ oldLine= oldLine->cl_Next;
+}
+
+/*
+ * DeleteFile() - delete user database
+ *
+ * Note: multiple entries for same user may exist if we were unable to
+ * completely delete a database due to running processes.
+ */
+static void DeleteFile(CronFile *tfile)
+{
+ CronFile **pfile = &FileBase;
+ CronFile *file;
+
+ while ((file = *pfile) != NULL) {
+ if (file == tfile) {
+ CronLine **pline = &file->cf_LineBase;
+ CronLine *line;
+
+ file->cf_Running = 0;
+ file->cf_Deleted = 1;
+
+ while ((line = *pline) != NULL) {
+ if (line->cl_Pid > 0) {
+ file->cf_Running = 1;
+ pline = &line->cl_Next;
+ } else {
+ *pline = line->cl_Next;
+ free(line->cl_Shell);
+ free(line);
+ }
+ kick_watchdog();
+ }
+ if (file->cf_Running == 0) {
+ *pfile = file->cf_Next;
+ free(file->cf_User);
+ free(file);
+ } else {
+ pfile = &file->cf_Next;
+ }
+ } else {
+ pfile = &file->cf_Next;
+ }
+ }
+}
+
+/*
+ * TestJobs()
+ *
+ * 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).
+ */
+#if ATLAS_NEW_FORMAT
+static int TestJobs(time_t *nextp)
+#else
+static int TestJobs(time_t t1, time_t t2)
+#endif
+{
+ int nJobs = 0;
+#if ATLAS_NEW_FORMAT
+ time_t now;
+#else
+ time_t t;
+#endif
+
+#if ATLAS_NEW_FORMAT
+ now= time(NULL);
+ *nextp= now+3600; /* Enough */
+
+ {
+ CronFile *file;
+ CronLine *line;
+#else
+ /* Find jobs > t1 and <= t2 */
+
+ for (t = t1 - t1 % 60; t <= t2; t += 60) {
+ struct tm *tp;
+ CronFile *file;
+ CronLine *line;
+
+ if (t <= t1)
+ continue;
+
+ tp = localtime(&t);
+#endif /* ATLAS_NEW_FORMAT */
+ for (file = FileBase; file; file = file->cf_Next) {
+ if (DebugOpt)
+ crondlog(LVL5 "file %s:", file->cf_User);
+ if (file->cf_Deleted)
+ continue;
+ for (line = file->cf_LineBase; line; line = line->cl_Next) {
+ if (DebugOpt)
+ crondlog(LVL5 " line %s", line->cl_Shell);
+#if ATLAS_NEW_FORMAT
+ if (line->lasttime != 0)
+ {
+ if (now > line->lasttime+
+ line->interval+
+ line->distr_param)
+ {
+ crondlog(
+LVL7 "(TestJobs) job is late. Now %d, lasttime %d, max %d, should %d: %s",
+ now, line->lasttime,
+ line->lasttime+
+ line->interval+
+ line->distr_param,
+ line->start_time +
+ line->nextcycle*
+ line->interval+
+ line->distr_offset,
+ line->cl_Shell);
+ }
+ }
+
+ if (now >= line->start_time +
+ line->nextcycle*line->interval +
+ line->distr_offset &&
+ now >= line->start_time &&
+ now <= line->end_time
+ )
+#else
+ if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour]
+ && (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday])
+ && line->cl_Mons[tp->tm_mon]
+ )
+#endif
+ {
+ if (DebugOpt) {
+ crondlog(LVL5 " job: %d %s",
+ (int)line->cl_Pid, line->cl_Shell);
+ }
+ if (line->cl_Pid > 0) {
+ crondlog(LVL8 "user %s: process already running: %s",
+ file->cf_User, line->cl_Shell);
+ } else if (line->cl_Pid == 0) {
+ line->cl_Pid = -1;
+ file->cf_Ready = 1;
+ ++nJobs;
+#if ATLAS_NEW_FORMAT
+ *nextp= 0;
+ line->nextcycle++;
+ if (line->start_time +
+ line->nextcycle*
+ line->interval <= now)
+ {
+ line->nextcycle=
+ (now-line->start_time)/
+ line->interval + 1;
+ }
+ do_distr(line);
+#endif
+ }
+ }
+#if ATLAS_NEW_FORMAT
+ else if (now >= line->start_time &&
+ now <= line->end_time)
+ {
+ /* Compute next time */
+ time_t next;
+
+ next= line->start_time +
+ line->nextcycle*line->interval +
+ line->distr_offset;
+ if (next < *nextp)
+ *nextp= next;
+ }
+#endif /* ATLAS_NEW_FORMAT */
+ }
+ }
+ }
+ return nJobs;
+}
+
+static void RunJobs(void)
+{
+ CronFile *file;
+ CronLine *line;
+
+ for (file = FileBase; file; file = file->cf_Next) {
+ if (!file->cf_Ready)
+ continue;
+
+ file->cf_Ready = 0;
+ for (line = file->cf_LineBase; line; line = line->cl_Next) {
+ if (line->cl_Pid >= 0)
+ continue;
+
+ kick_watchdog();
+
+ RunJob(file->cf_User, line);
+ crondlog(LVL8 "USER %s pid %3d cmd %s",
+ file->cf_User, (int)line->cl_Pid, line->cl_Shell);
+ if (line->cl_Pid < 0) {
+ file->cf_Ready = 1;
+ } else if (line->cl_Pid > 0) {
+ file->cf_Running = 1;
+ }
+ // AA make it wait till the job is finished
+ while (CheckJobs() > 0)
+ {
+ // crondlog(LVL9 "waiting for job %s ", line->cl_Shell);
+ sleep(5);
+ }
+
+ }
+ }
+}
+
+/*
+ * CheckJobs() - check for job completion
+ *
+ * Check for job completion, return number of jobs still running after
+ * all done.
+ */
+static int CheckJobs(void)
+{
+ CronFile *file;
+ CronLine *line;
+ int nStillRunning = 0;
+
+ for (file = FileBase; file; file = file->cf_Next) {
+ if (file->cf_Running) {
+ file->cf_Running = 0;
+
+ for (line = file->cf_LineBase; line; line = line->cl_Next) {
+ int status, r;
+ if (line->cl_Pid <= 0)
+ continue;
+
+ r = waitpid(line->cl_Pid, &status, WNOHANG);
+ if (r < 0 || r == line->cl_Pid) {
+ EndJob(file->cf_User, line);
+ if (line->cl_Pid) {
+ file->cf_Running = 1;
+ }
+ } else if (r == 0) {
+ file->cf_Running = 1;
+ }
+ }
+ }
+ nStillRunning += file->cf_Running;
+ }
+ return nStillRunning;
+}
+
+static void skip_space(char *cp, char **ncpp)
+{
+ while (cp[0] != '\0' && isspace(*(unsigned char *)cp))
+ cp++;
+ *ncpp= cp;
+}
+
+static void skip_nonspace(char *cp, char **ncpp)
+{
+ while (cp[0] != '\0' && !isspace(*(unsigned char *)cp))
+ cp++;
+ *ncpp= cp;
+}
+
+static void find_eos(char *cp, char **ncpp)
+{
+ while (cp[0] != '\0' && cp[0] != '"')
+ cp++;
+ *ncpp= cp;
+}
+
+
+#define ATLAS_NARGS 20 /* Max arguments to a built-in command */
+#define ATLAS_ARGSIZE 512 /* Max size of the command line */
+
+static int atlas_run(char *cmdline)
+{
+ char c;
+ int i, r, argc, atlas_fd, saved_fd, do_append, flags;
+ size_t len;
+ char *cp, *ncp;
+ struct builtin *bp;
+ char *outfile;
+ FILE *fn;
+ char *reason;
+ char *argv[ATLAS_NARGS];
+ char args[ATLAS_ARGSIZE];
+
+ crondlog(LVL7 "atlas_run: looking for %p '%s'", cmdline, cmdline);
+
+ reason= NULL;
+ for (bp= builtin_cmds; bp->cmd != NULL; bp++)
+ {
+ len= strlen(bp->cmd);
+ if (strncmp(cmdline, bp->cmd, len) != 0)
+ continue;
+ if (cmdline[len] != ' ')
+ continue;
+ break;
+ }
+ if (bp->cmd == NULL)
+ {
+ crondlog(LVL8 "cmd not found '%s'", cmdline);
+ r= -1;
+ reason="cmd not found";
+ goto error;
+ }
+
+ crondlog(LVL7 "found cmd '%s' for '%s'", bp->cmd, cmdline);
+
+ outfile= NULL;
+ do_append= 0;
+
+ len= strlen(cmdline);
+ if (len+1 > ATLAS_ARGSIZE)
+ {
+ crondlog(LVL8 "atlas_run: command line too big: '%s'", cmdline);
+ r= -1;
+ reason="command line too big";
+ goto error;
+ }
+ strcpy(args, cmdline);
+
+ /* Split the command line */
+ cp= args;
+ argc= 0;
+ argv[argc]= cp;
+ skip_nonspace(cp, &ncp);
+ cp= ncp;
+
+ for(;;)
+ {
+ /* End of list */
+ if (cp[0] == '\0')
+ {
+ argc++;
+ break;
+ }
+
+ /* Find start of next argument */
+ skip_space(cp, &ncp);
+
+ /* Terminate current one */
+ cp[0]= '\0';
+
+ /* Special case for '>' */
+ if (argv[argc][0] == '>')
+ {
+ cp= argv[argc]+1;
+ if (cp[0] == '>')
+ {
+ /* Append */
+ do_append= 1;
+ cp++;
+ }
+ if (cp[0] != '\0')
+ {
+ /* Filename immediately follows '>' */
+ outfile= cp;
+
+ /* And move on with the next option */
+ }
+ else
+ {
+ /* Get the next argument */
+ outfile= ncp;
+ cp= ncp;
+ skip_nonspace(cp, &ncp);
+ cp= ncp;
+
+ if (cp[0] == '\0')
+ break;
+
+ /* Find start of next argument */
+ skip_space(cp, &ncp);
+ *cp= '\0';
+ }
+ }
+ else
+ {
+ argc++;
+ }
+
+ if (argc >= ATLAS_NARGS-1)
+ {
+ crondlog(
+ LVL8 "atlas_run: command line '%s', too many arguments",
+ cmdline);
+ r= -1;
+ reason="too many arguments";
+ goto error;
+ }
+
+ cp= ncp;
+ argv[argc]= cp;
+ if (cp[0] == '"')
+ {
+ /* Special code for strings */
+ find_eos(cp+1, &ncp);
+ if (ncp[0] != '"')
+ {
+ crondlog(
+ LVL8 "atlas_run: command line '%s', end of string not found",
+ cmdline);
+ r= -1;
+ reason="end of string not found";
+ goto error;
+ }
+ argv[argc]= cp+1;
+ cp= ncp;
+ cp[0]= '\0';
+ cp++;
+ }
+ else
+ {
+ skip_nonspace(cp, &ncp);
+ cp= ncp;
+ }
+ }
+
+ if (argc >= ATLAS_NARGS)
+ {
+ crondlog(
+ LVL8 "atlas_run: command line '%s', too many arguments",
+ cmdline);
+ r= -1;
+ reason="too many arguments";
+ goto error;
+ }
+ argv[argc]= NULL;
+
+ for (i= 0; i<argc; i++)
+ crondlog(LVL7 "atlas_run: argv[%d] = '%s'", i, argv[i]);
+
+ saved_fd= -1; /* lint */
+ if (outfile)
+ {
+ /* Redirect I/O */
+ crondlog(LVL7 "sending output to '%s'", outfile);
+ if (!validate_filename(outfile, SAFE_PREFIX))
+ {
+ crondlog(
+ LVL8 "atlas_run: insecure output file '%s'",
+ outfile);
+ r= -1;
+ reason="insecure output file";
+ goto error;
+ }
+ flags= O_CREAT | O_WRONLY;
+ if (do_append)
+ flags |= O_APPEND;
+ atlas_fd= open(outfile, flags, 0644);
+ if (atlas_fd == -1)
+ {
+ crondlog(
+ LVL8 "atlas_run: unable to create output file '%s'",
+ outfile);
+ r= -1;
+ reason="unable to create output file";
+ goto error;
+ }
+ fflush(stdout);
+ saved_fd= dup(1);
+ if (saved_fd == -1)
+ {
+ crondlog(LVL8 "atlas_run: unable to dub stdout");
+ close(atlas_fd);
+ r= -1;
+ reason="unable to dub stdout";
+ goto error;
+ }
+ dup2(atlas_fd, 1);
+ close(atlas_fd);
+ }
+
+ r= bp->func(argc, argv);
+
+ alarm(0);
+
+ if (outfile)
+ {
+ fflush(stdout);
+ dup2(saved_fd, 1);
+ close(saved_fd);
+ }
+
+error:
+ if (r != 0 && out_filename)
+ {
+ fn= fopen(out_filename, "a");
+ if (!fn)
+ crondlog(DIE9 "unable to append to '%s'", out_filename);
+ fprintf(fn, "RESULT { ");
+ if (atlas_id)
+ fprintf(fn, DBQ(id) ":" DBQ(%s) ", ", atlas_id);
+ fprintf(fn, DBQ(fw) ":" DBQ(%d) ", " DBQ(time) ":%d, ",
+ get_atlas_fw_version(), time(NULL));
+ if (reason != NULL)
+ fprintf(fn, DBQ(reason) ":" DBQ(%s) ", ", reason);
+ fprintf(fn, DBQ(err) ":%d, " DBQ(cmd) ": \"", r);
+ for (cp= cmdline; *cp; cp++)
+ {
+ c= *cp;
+ if (c == '"' || c == '\\')
+ fprintf(fn, "\\%c", c);
+ else if (isprint((unsigned char)c))
+ fputc(c, fn);
+ else
+ fprintf(fn, "\\u%04x", (unsigned char)c);
+ }
+ fprintf(fn, "\"");
+ fprintf(fn, " }\n");
+ fclose(fn);
+ }
+
+ return 1;
+}
+
+static void RunJob(const char *user, CronLine *line)
+{
+ struct passwd *pas;
+ pid_t pid;
+
+ if (line->lasttime != 0)
+ {
+ time_t now= time(NULL);
+ if (now > line->lasttime+line->interval+line->distr_param)
+ {
+ crondlog(LVL7 "job is late. Now %d, lasttime %d, max %d, should %d: %s",
+ now, line->lasttime,
+ line->lasttime+line->interval+line->distr_param,
+ line->start_time +
+ line->nextcycle*line->interval+
+ line->distr_offset,
+ line->cl_Shell);
+ }
+ }
+ line->lasttime= time(NULL);
+
+ if (atlas_run(line->cl_Shell))
+ {
+ /* Internal command */
+ line->cl_Pid = 0;
+ return;
+ }
+
+ /* Don't run external commands */
+ line->cl_Pid = 0;
+#if 0
+ /* prepare things before vfork */
+ pas = getpwnam(user);
+ if (!pas) {
+ crondlog(LVL9 "can't get uid for %s", user);
+ goto err;
+ }
+ SetEnv(pas);
+
+ /* fork as the user in question and run program */
+ pid = vfork();
+ if (pid == 0) {
+ /* CHILD */
+ /* change running state to the user in question */
+ ChangeUser(pas);
+ if (DebugOpt) {
+ crondlog(LVL5 "child running %s", DEFAULT_SHELL);
+ }
+ /* crond 3.0pl1-100 puts tasks in separate process groups */
+ bb_setpgrp();
+ /* Disable execl for securty reasons */
+ //execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL);
+ crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user,
+ DEFAULT_SHELL, "-c", line->cl_Shell);
+ _exit(EXIT_SUCCESS);
+ }
+ if (pid < 0) {
+ /* FORK FAILED */
+ crondlog(ERR20 "can't vfork");
+ err:
+ pid = 0;
+ }
+ line->cl_Pid = pid;
+#endif
+}
diff --git a/networking/Config.in b/networking/Config.in
index 95f8942..7c72b6d 100644
--- a/networking/Config.in
+++ b/networking/Config.in
@@ -47,6 +47,13 @@ config ARPING
help
Ping hosts by ARP packets.
+config AUTOSSH
+ bool "autossh"
+ default n
+ help
+ Monitor the ssh port. Added by AA Antony. 2010 Oct
+
+
config BRCTL
bool "brctl"
default n
@@ -199,8 +206,7 @@ config FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
This option enables support for running scripts through an
interpreter. Turn this on if you want PHP scripts to work
properly. You need to supply an additional line in your httpd
- config file:
- *.php:/path/to/your/php
+ config file: *.php:/path/to/your/php
config FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
bool "Set REMOTE_PORT environment variable for CGI"
@@ -245,6 +251,18 @@ config FEATURE_HTTPD_PROXY
Then a request to /url/myfile will be forwarded to
http://hostname[:port]/new/path/myfile.
+config HTTPGET
+ bool "httpget"
+ default y
+ help
+ simple http GET. RIPE NCC 2011
+
+config HTTPPOST
+ bool "httppost"
+ default y
+ help
+ simple httppost --post-file with IPv4. RIPE NCC 2011
+
config IFCONFIG
bool "ifconfig"
default n
@@ -681,6 +699,19 @@ config ROUTE
help
Route displays or manipulates the kernel's IP routing tables.
+config RPTRA6
+ bool "rptra6"
+ default n
+ help
+ Report received IPv6 router advertisements
+
+config RXTXRPT
+ bool "rxtxrpt"
+ default n
+ help
+ rxtxrpt report RX and TX statistics as well as IPv6 addresses and
+ routes
+
config SLATTACH
bool "slattach"
default n
@@ -688,6 +719,12 @@ config SLATTACH
slattach is a small utility to attach network interfaces to serial
lines.
+config SSLGETCERT
+ bool "sslgetcert"
+ default n
+ help
+ simple simple program to get certificates from an ssl server
+
#config TC
# bool "tc"
# default n
@@ -833,6 +870,13 @@ config TRACEROUTE
help
Utility to trace the route of IP packets
+config FEATURE_TRACEROUTE_IPV6
+ bool "Enable IPv6 Support"
+ default n
+ depends on FEATURE_IPV6 && TRACEROUTE
+ help
+ Add IPv6 support for the traceroute
+
config FEATURE_TRACEROUTE_VERBOSE
bool "Enable verbose output"
default n
diff --git a/networking/Kbuild b/networking/Kbuild
index 63d0745..ff7dfb3 100644
--- a/networking/Kbuild
+++ b/networking/Kbuild
@@ -15,6 +15,8 @@ lib-$(CONFIG_FTPGET) += ftpgetput.o
lib-$(CONFIG_FTPPUT) += ftpgetput.o
lib-$(CONFIG_HOSTNAME) += hostname.o
lib-$(CONFIG_HTTPD) += httpd.o
+lib-$(CONFIG_HTTPGET) += httpget.o
+lib-$(CONFIG_HTTPPOST) += httppost.o
lib-$(CONFIG_IFCONFIG) += ifconfig.o interface.o
lib-$(CONFIG_IFENSLAVE) += ifenslave.o interface.o
lib-$(CONFIG_IFUPDOWN) += ifupdown.o
@@ -29,7 +31,10 @@ lib-$(CONFIG_PING) += ping.o
lib-$(CONFIG_PING6) += ping.o
lib-$(CONFIG_PSCAN) += pscan.o
lib-$(CONFIG_ROUTE) += route.o
+lib-$(CONFIG_RPTRA6) += rptra6.o
+lib-$(CONFIG_RXTXRPT) += rxtxrpt.o
lib-$(CONFIG_SLATTACH) += slattach.o
+lib-$(CONFIG_SSLGETCERT) += sslgetcert.o
lib-$(CONFIG_TC) += tc.o
lib-$(CONFIG_TELNET) += telnet.o
lib-$(CONFIG_TELNETD) += telnetd.o
diff --git a/networking/atlasinit.h b/networking/atlasinit.h
new file mode 100644
index 0000000..f7362a6
--- /dev/null
+++ b/networking/atlasinit.h
@@ -0,0 +1,33 @@
+/* RIPEAtlas
+ * All the configurable variables - and some non configurables too
+ * $Id: $
+ */
+
+#ifndef _ATLASINIT_H
+#define _ATLASINIT_H
+
+#define ATLAS_BUF_SIZE 1024
+#define MAX_READ ATLAS_BUF_SIZE-2 /* should be enough to read controller keys */
+
+/*********************************************************************
+ * Set these constants to your liking
+ */
+
+extern const char atlas_log_file[];
+extern const int atlas_log_level;
+
+extern const char atlas_contr_known_hosts[];
+extern const char atlas_rereg_timestamp[];
+
+extern const int max_lines; /* maximum lines we'll process */
+extern const int min_rereg_time; /* 12h */
+extern const int max_rereg_time; /* 28d */
+extern const int default_rereg_time; /* 7d */
+
+/*********************************************************************/
+
+enum { ALL, DEBUG, INFO, WARN, ERROR } error_level;
+
+void atlas_log( int level, const char *msg, ... );
+
+#endif
diff --git a/networking/httpget.c b/networking/httpget.c
new file mode 100644
index 0000000..387a33f
--- /dev/null
+++ b/networking/httpget.c
@@ -0,0 +1,1458 @@
+/*
+ * Copyright (c) 2011-2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * httpget.c -- Simple program that uses the HTTP GET command
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include "libbb.h"
+
+#define SAFE_PREFIX_OUT ATLAS_DATA_OUT
+#define SAFE_PREFIX_NEW ATLAS_DATA_NEW
+
+#define debug 0
+
+static struct option longopts[]=
+{
+ { "append", no_argument, NULL, 'a' },
+ { "get", no_argument, NULL, 'g' },
+ { "head", no_argument, NULL, 'E' },
+ { "post", no_argument, NULL, 'P' },
+ { "post-file", required_argument, NULL, 'p' },
+ { "post-dir", required_argument, NULL, 'D' },
+ { "post-header", required_argument, NULL, 'h' },
+ { "post-footer", required_argument, NULL, 'f' },
+ { "set-time", required_argument, NULL, 's' },
+ { "store-headers", required_argument, NULL, 'H' },
+ { "store-body", required_argument, NULL, 'B' },
+ { "summary", no_argument, NULL, 'S' },
+ { "user-agent", required_argument, NULL, 'u' },
+ { NULL, }
+};
+
+static char *time_tolerance;
+static char buffer[1024];
+static char host_addr[INET6_ADDRSTRLEN];
+static sa_family_t family;
+static const char *user_agent= "httpget for atlas.ripe.net";
+static int tcp_fd= -1;
+
+static int parse_url(char *url, char **hostp, char **portp, char **hostportp,
+ char **pathp);
+static int check_result(FILE *tcp_file, int *result);
+static int eat_headers(FILE *tcp_file, int *chunked, int *content_length,
+ int *headers_size, FILE *out_file, int max_headers);
+static int connect_to_name(char *host, char *port, int only_v4, int only_v6,
+ struct timeval *start_time, int *gerr);
+static char *do_dir(char *dir_name, off_t *lenp);
+static int copy_chunked(FILE *in_file, FILE *out_file, int *length,
+ int max_body);
+static int copy_bytes(FILE *in_file, FILE *out_file, int *length,
+ int max_body);
+static void got_alarm(int sig);
+static void fatal(const char *fmt, ...);
+static void fatal_err(const char *fmt, ...);
+static void report(const char *fmt, ...);
+static void report_err(const char *fmt, ...);
+static int write_to_tcp_fd (int fd, FILE *tcp_file);
+static void skip_spaces(const char *cp, char **ncp);
+
+int httpget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int httpget_main(int argc, char *argv[])
+{
+ int c, i, r, fd, fdF, fdH, fdS, chunked, content_length,
+ result, http_result, do_get, do_head, do_post,
+ max_headers, max_body, do_multiline, only_v4, only_v6,
+ do_summary, headers_size, no_body, do_append, do_http10, gerr,
+ out_file_needs_closing;
+ char *url, *host, *port, *hostport, *path, *filelist, *p, *check;
+ char *post_dir, *post_file, *output_file, *post_footer, *post_header,
+ *A_arg, *store_headers, *store_body;
+ FILE *tcp_file, *out_file;
+ struct timeval tv_start, tv_end;
+ struct stat sbF, sbH, sbS;
+ off_t cLength, dir_length;
+ struct sigaction sa;
+ char rndbuf[16];
+
+ /* Arguments */
+ do_http10= 0;
+ do_append= 0;
+ do_get= 1;
+ do_head= 0;
+ do_post= 0;
+ post_dir= NULL;
+ post_file= NULL;
+ post_footer=NULL;
+ post_header=NULL;
+ output_file= NULL;
+ time_tolerance= NULL;
+ store_headers= NULL;
+ store_body= NULL;
+ A_arg= NULL;
+ only_v4= 0;
+ only_v6= 0;
+ do_summary= 0;
+
+ /* Used in cleanup */
+ fd= -1;
+ fdH= -1;
+ fdF= -1;
+ fdS= -1;
+ tcp_fd= -1;
+ tcp_file= NULL;
+ out_file= NULL;
+ out_file_needs_closing= 0;
+ host= NULL;
+ port= NULL;
+ hostport= NULL;
+ path= NULL;
+ filelist= NULL;
+
+ /* Others */
+ do_multiline= 0;
+ http_result= -1;
+ dir_length= 0;
+ headers_size= 0;
+
+ /* Allow us to be called directly by another program in busybox */
+ optind= 0;
+ while (c= getopt_long(argc, argv, "01A:O:46?", longopts, NULL), c != -1)
+ {
+ switch(c)
+ {
+ case '0':
+ do_http10= 1;
+ break;
+ case '1':
+ do_http10= 0;
+ break;
+ case 'a': /* --append */
+ do_append= 1;
+ break;
+ case 'A':
+ A_arg= optarg;
+ break;
+ case 'O':
+ output_file= optarg;
+ break;
+ case 'g': /* --get */
+ do_get = 1;
+ do_head = 0;
+ do_post = 0;
+ break;
+ case 'E': /* --head */
+ do_get = 0;
+ do_head = 1;
+ do_post = 0;
+ break;
+ case 'P': /* --post */
+ do_get = 0;
+ do_head = 0;
+ do_post = 1;
+ break;
+ case 'D':
+ post_dir = optarg; /* --post-dir */
+ break;
+ case 'h': /* --post-header */
+ post_header= optarg;
+ break;
+ case 'f': /* --post-footer */
+ post_footer= optarg;
+ break;
+
+ case 'p': /* --post-file */
+ post_file= optarg;
+ break;
+ case 's': /* --set-time */
+ time_tolerance= optarg;
+ break;
+ case 'H': /* --store-headers */
+ store_headers= optarg;
+ break;
+ case 'B': /* --store-body */
+ store_body= optarg;
+ break;
+ case 'S': /* --summary */
+ do_summary= 1;
+ break;
+ case '4':
+ only_v4= 1;
+ only_v6= 0;
+ break;
+ case '6':
+ only_v6= 1;
+ only_v4= 0;
+ break;
+ case 'u': /* --user-agent */
+ user_agent= optarg;
+ break;
+ case '?':
+ bb_show_usage();
+ return 1;
+ default:
+ fatal("bad option '%c'", c);
+ }
+ }
+
+ if (optind != argc-1)
+ fatal("exactly one url expected");
+ url= argv[optind];
+
+ max_headers= 0;
+ max_body= UINT_MAX; /* default is to write out the entire body */
+ if (do_summary)
+ max_body= 0; /* default to no body if we want a summary */
+
+ if (store_headers)
+ {
+ max_headers= strtoul(store_headers, &check, 10);
+ if (check[0] != '\0')
+ {
+ report("unable to parse argument '%s'", store_headers);
+ return 1;
+ }
+ }
+
+ if (store_body)
+ {
+ max_body= strtoul(store_body, &check, 10);
+ if (check[0] != '\0')
+ {
+ report("unable to parse argument '%s'", store_body);
+ return 1;
+ }
+ }
+
+ if (!parse_url(url, &host, &port, &hostport, &path))
+ {
+ goto err;
+ }
+
+ //printf("host: %s\n", host);
+ //printf("port: %s\n", port);
+ //printf("hostport: %s\n", hostport);
+ //printf("path: %s\n", path);
+
+ if (post_dir)
+ {
+ filelist= do_dir(post_dir, &dir_length);
+ if (!filelist)
+ {
+ /* Something went wrong. */
+ goto err;
+ }
+ if (debug)
+ {
+ fprintf(stderr, "total size in dir: %ld\n",
+ (long)dir_length);
+ }
+ }
+
+ if(post_header != NULL )
+ {
+ if (!validate_filename(post_header, SAFE_PREFIX_OUT))
+ {
+ report("insecure file '%s'", post_header);
+ goto err;
+ }
+ fdH = open(post_header, O_RDONLY);
+ if(fdH == -1 )
+ {
+ report_err("unable to open header '%s'", post_header);
+ goto err;
+ }
+ if (fstat(fdH, &sbH) == -1)
+ {
+ report_err("fstat failed on header file '%s'",
+ post_header);
+ goto err;
+ }
+ if (!S_ISREG(sbH.st_mode))
+ {
+ report("'%s' header is not a regular file",
+ post_header);
+ goto err;
+ }
+ }
+
+ if(post_footer != NULL )
+ {
+ if (!validate_filename(post_footer, SAFE_PREFIX_OUT))
+ {
+ report("insecure file '%s'", post_footer);
+ goto err;
+ }
+ fdF = open(post_footer, O_RDONLY);
+ if(fdF == -1 )
+ {
+ report_err("unable to open footer '%s'", post_footer);
+ goto err;
+ }
+ if (fstat(fdF, &sbF) == -1)
+ {
+ report_err("fstat failed on footer file '%s'",
+ post_footer);
+ goto err;
+ }
+ if (!S_ISREG(sbF.st_mode))
+ {
+ report("'%s' footer is not a regular file",
+ post_footer);
+ goto err;
+ }
+ }
+
+ /* Try to open the file before trying to connect */
+ if (post_file != NULL)
+ {
+ if (!validate_filename(post_file, SAFE_PREFIX_OUT))
+ {
+ report("insecure file '%s'", post_file);
+ goto err;
+ }
+ fdS= open(post_file, O_RDONLY);
+ if (fdS == -1)
+ {
+ report_err("unable to open '%s'", post_file);
+ goto err;
+ }
+ if (fstat(fdS, &sbS) == -1)
+ {
+ report_err("fstat failed");
+ goto err;
+ }
+ if (!S_ISREG(sbS.st_mode))
+ {
+ report("'%s' is not a regular file", post_file);
+ goto err;
+ }
+ }
+
+ sa.sa_flags= 0;
+ sa.sa_handler= got_alarm;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGALRM, &sa, NULL);
+ if (debug) fprintf(stderr, "setting alarm\n");
+ alarm(10);
+ signal(SIGPIPE, SIG_IGN);
+
+ if (output_file)
+ {
+ if (!validate_filename(output_file, SAFE_PREFIX_NEW))
+ {
+ report("insecure output file '%s'", output_file);
+ goto err;
+ }
+ out_file= fopen(output_file, do_append ? "a" : "w");
+ if (!out_file)
+ {
+ report_err("unable to create '%s'", output_file);
+ goto err;
+ }
+ out_file_needs_closing= 1;
+ }
+ else
+ out_file= stdout;
+
+
+ tcp_fd= connect_to_name(host, port, only_v4, only_v6, &tv_start,
+ &gerr);
+ if (tcp_fd == -1)
+ {
+ int s_errno= errno;
+
+ if (A_arg && do_summary)
+ {
+ fprintf(out_file, "%s %ld ",
+ A_arg, (long)time(NULL));
+ if (gerr != 0)
+ {
+ fprintf(out_file, "bad-hostname %s\n",
+ gai_strerror(gerr));
+ }
+ else
+ {
+ fprintf(out_file, "connect error %d\n",
+ s_errno);
+ }
+ }
+ report("unable to connect to '%s'", host);
+ goto err;
+ }
+
+ /* Stdio makes life easy */
+ tcp_file= fdopen(tcp_fd, "r+");
+ if (tcp_file == NULL)
+ {
+ report("fdopen failed");
+ goto err;
+ }
+ tcp_fd= -1;
+
+ if (debug) fprintf(stderr, "httpget: sending request\n");
+ fprintf(tcp_file, "%s %s HTTP/1.%c\r\n",
+ do_get ? "GET" : do_head ? "HEAD" : "POST", path,
+ do_http10 ? '0' : '1');
+ fprintf(tcp_file, "Host: %s\r\n", host);
+ fprintf(tcp_file, "Connection: close\r\n");
+ fprintf(tcp_file, "User-Agent: %s\r\n", user_agent);
+ if (do_post)
+ {
+ fprintf(tcp_file,
+ "Content-Type: application/x-www-form-urlencoded\r\n");
+ }
+
+ cLength= 0;
+ if( post_header != NULL )
+ cLength += sbH.st_size;
+
+ if (post_file)
+ cLength += sbS.st_size;
+
+ if (post_dir)
+ cLength += dir_length;
+
+ if( post_footer != NULL )
+ cLength += sbF.st_size;
+
+ fprintf(tcp_file, "Content-Length: %lu\r\n", (unsigned long)cLength);
+ fprintf(tcp_file, "\r\n");
+
+ if( post_header != NULL )
+ {
+ if (!write_to_tcp_fd(fdH, tcp_file))
+ {
+ printf("write_to_tcp_fd failed\n");
+ goto fail;
+ }
+ }
+
+ if (post_file != NULL)
+ {
+ if (!write_to_tcp_fd(fdS, tcp_file))
+ {
+ printf("write_to_tcp_fd failed\n");
+ goto fail;
+ }
+ }
+
+ if (post_dir)
+ {
+ for (p= filelist; p[0] != 0; p += strlen(p)+1)
+ {
+ if (debug) fprintf(stderr, "posting file '%s'\n", p);
+ if (!validate_filename(p, SAFE_PREFIX_OUT))
+ {
+ report("insecure file '%s'", p);
+ goto err;
+ }
+ fd= open(p, O_RDONLY);
+ if (fd == -1)
+ {
+ report_err("unable to open '%s'", p);
+ goto err;
+ }
+ r= write_to_tcp_fd(fd, tcp_file);
+ close(fd);
+ fd= -1;
+ if (!r)
+ {
+ printf("write_to_tcp_fd failed\n");
+ goto fail;
+ }
+ }
+ }
+
+ if( post_footer != NULL)
+ {
+ if (!write_to_tcp_fd(fdF, tcp_file))
+ {
+ printf("write_to_tcp_fd failed\n");
+ goto fail;
+ }
+ }
+
+ if (debug) fprintf(stderr, "httpget: writing output\n");
+ do_multiline= (A_arg && (max_headers != 0 || max_body != 0));
+ if (do_multiline)
+ {
+ fd= open("/dev/urandom", O_RDONLY);
+ read(fd, rndbuf, sizeof(rndbuf));
+ close(fd);
+ fprintf(out_file, "BEGINRESULT ");
+ for (i= 0; i<sizeof(rndbuf); i++)
+ fprintf(out_file, "%02x", (unsigned char)rndbuf[i]);
+ fprintf(out_file, " %s %ld\n", A_arg, (long)time(NULL));
+ }
+
+ if (debug) fprintf(stderr, "httpget: getting result\n");
+ if (!check_result(tcp_file, &http_result))
+ {
+ printf("check_result failed\n");
+ goto fail;
+ }
+ if (debug) fprintf(stderr, "httpget: getting reply headers \n");
+ if (!eat_headers(tcp_file, &chunked, &content_length, &headers_size,
+ out_file, max_headers))
+ {
+ printf("eat_headers failed\n");
+ goto fail;
+ }
+
+ no_body= (do_head || http_result == 204 || http_result == 304 ||
+ http_result/100 == 1);
+
+ if (max_headers != 0 && max_body != 0)
+ fprintf(out_file, "\n"); /* separate headers from body */
+
+ if (no_body)
+ {
+ /* This reply will not have a body even if there is a
+ * content-length line.
+ */
+ }
+ else if (chunked)
+ {
+ if (!copy_chunked(tcp_file, out_file, &content_length,
+ max_body))
+ {
+ printf("copy_chunked failed\n");
+ goto fail;
+ }
+ }
+ else
+ {
+ if (!copy_bytes(tcp_file, out_file, &content_length, max_body))
+ {
+ printf("copy_bytes failed\n");
+ goto fail;
+ }
+ }
+
+fail:
+ gettimeofday(&tv_end, NULL);
+
+ tv_end.tv_sec -= tv_start.tv_sec;
+ tv_end.tv_usec -= tv_start.tv_usec;
+ if (tv_end.tv_usec < 0)
+ {
+ tv_end.tv_usec += 1000000;
+ tv_end.tv_sec--;
+ }
+
+ if (do_multiline)
+ {
+ fprintf(out_file, "ENDRESULT ");
+ for (i= 0; i<sizeof(rndbuf); i++)
+ fprintf(out_file, "%02x", (unsigned char)rndbuf[i]);
+ fprintf(out_file, "\n");
+ }
+
+ if (A_arg && do_summary)
+ {
+ fprintf(out_file, "%s %ld ",
+ A_arg, (long)time(NULL));
+ }
+ if (do_summary)
+ {
+ const char *v, *cmd;
+
+ if (do_get)
+ cmd= "GET";
+ else if (do_head)
+ cmd= "HEAD";
+ else
+ cmd= "POST";
+ if (family == AF_INET)
+ v= "4";
+ else if (family == AF_INET6)
+ v= "6";
+ else
+ v= "?";
+
+ fprintf(out_file, "%s%s %s %d.%06d %03u %d %d\n",
+ cmd, v,
+ host_addr, (int)tv_end.tv_sec, (int)tv_end.tv_usec,
+ http_result, headers_size, content_length);
+ }
+
+ if (debug) fprintf(stderr, "httpget: done\n");
+
+ result= 0;
+
+leave:
+ if (fdH != -1) close(fdH);
+ if (fdF != -1) close(fdF);
+ if (fdS != -1) close(fdS);
+ if (fd != -1) close(fd);
+ if (tcp_file) fclose(tcp_file);
+ if (tcp_fd != -1) close(tcp_fd);
+ if (out_file && out_file_needs_closing) fclose(out_file);
+ if (host) free(host);
+ if (port) free(port);
+ if (hostport) free(hostport);
+ if (path) free(path);
+ if (filelist) free(filelist);
+
+ printf("clearing alarm\n");
+ alarm(0);
+ signal(SIGPIPE, SIG_DFL);
+
+ return result;
+
+err:
+ result= 1;
+ goto leave;
+}
+
+static int write_to_tcp_fd (int fd, FILE *tcp_file)
+{
+ int r;
+ /* Copy file */
+ while(r= read(fd, buffer, sizeof(buffer)), r > 0)
+ {
+ if (fwrite(buffer, r, 1, tcp_file) != 1)
+ {
+ report_err("error writing to tcp connection");
+ return 0;
+ }
+ }
+ if (r == -1)
+ fatal_err("error reading from file");
+ return 1;
+}
+
+
+static int parse_url(char *url, char **hostp, char **portp, char **hostportp,
+ char **pathp)
+{
+ char *item;
+ const char *cp, *np, *prefix;
+ size_t len;
+
+ *hostp= NULL;
+ *portp= NULL;
+ *hostportp= NULL;
+ *pathp= NULL;
+
+ /* the url must start with 'http://' */
+ prefix= "http://";
+ len= strlen(prefix);
+ if (strncasecmp(prefix, url, len) != 0)
+ {
+ report("bad prefix in url '%s'", url);
+ goto fail;
+ }
+
+ cp= url+len;
+
+ /* Get hostport part */
+ np= strchr(cp, '/');
+ if (np != NULL)
+ len= np-cp;
+ else
+ {
+ len= strlen(cp);
+ np= cp+len;
+ }
+ if (len == 0)
+ {
+ report("missing host part in url '%s'", url);
+ return 0;
+ }
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ *hostportp= item;
+
+ /* The remainder is the path */
+ cp= np;
+ if (cp[0] == '\0')
+ cp= "/";
+ len= strlen(cp);
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ *pathp= item;
+
+ /* Extract the host name from hostport */
+ cp= *hostportp;
+ np= cp;
+ if (cp[0] == '[')
+ {
+ /* IPv6 address literal */
+ np= strchr(cp, ']');
+ if (np == NULL || np == cp+1)
+ {
+ report("malformed IPv6 address literal in url '%s'",
+ url);
+ goto fail;
+ }
+ }
+
+ np= strchr(np, ':');
+ if (np != NULL)
+ len= np-cp;
+ else
+ {
+ len= strlen(cp);
+ np= cp+len;
+ }
+ if (len == 0)
+ {
+ report("missing host part in url '%s'", url);
+ goto fail;
+ }
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ if (cp[0] == '[')
+ {
+ /* Leave out the square brackets */
+ memcpy(item, cp+1, len-2);
+ item[len-2]= '\0';
+ }
+ else
+ {
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ }
+ *hostp= item;
+
+ /* Port */
+ cp= np;
+ if (cp[0] == '\0')
+ cp= "80";
+ else
+ cp++;
+ len= strlen(cp);
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ *portp= item;
+
+ return 1;
+
+fail:
+ if (*hostp)
+ {
+ free(*hostp);
+ *hostp= NULL;
+ }
+ if (*portp)
+ {
+ free(*portp);
+ *portp= NULL;
+ }
+ if (*hostportp)
+ {
+ free(*hostportp);
+ *hostportp= NULL;
+ }
+ if (*pathp)
+ {
+ free(*pathp);
+ *pathp= NULL;
+ }
+ return 0;
+}
+
+static int check_result(FILE *tcp_file, int *result)
+{
+ int major, minor;
+ size_t len;
+ char *cp, *check, *line;
+ const char *prefix;
+
+ *result= -1; /* Signal error actually getting a result */
+
+ if (fgets(buffer, sizeof(buffer), tcp_file) == NULL)
+ {
+ if (feof(tcp_file))
+ report("got unexpected EOF from server");
+ else
+ report_err("error reading from server");
+ return 0;
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ report("line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ /* Check http version */
+ prefix= "http/";
+ len= strlen(prefix);
+ if (strncasecmp(prefix, line, len) != 0)
+ {
+ report("bad prefix in response '%s'", line);
+ return 0;
+ }
+ cp= line+len;
+ major= strtoul(cp, &check, 10);
+ if (check == cp || check[0] != '.')
+ {
+ report("bad major version in response '%s'", line);
+ return 0;
+ }
+ cp= check+1;
+ minor= strtoul(cp, &check, 10);
+ if (check == cp || check[0] == '\0' ||
+ !isspace(*(unsigned char *)check))
+ {
+ report("bad major version in response '%s'", line);
+ return 0;
+ }
+
+ skip_spaces(check, &cp);
+
+ if (!isdigit(*(unsigned char *)cp))
+ {
+ report("bad status code in response '%s'", line);
+ return 0;
+ }
+ *result= strtoul(cp, NULL, 10);
+
+ return 1;
+}
+
+static int eat_headers(FILE *tcp_file, int *chunked, int *content_length,
+ int *headers_size, FILE *out_file, int max_headers)
+{
+ int tot_headers;
+ char *line, *cp, *ncp, *check;
+ size_t len;
+ const char *kw;
+
+ *chunked= 0;
+ *content_length= -1;
+ tot_headers= 0;
+ *headers_size= 0;
+ while (fgets(buffer, sizeof(buffer), tcp_file) != NULL)
+ {
+ line= buffer;
+ len=strlen(line);
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ report("line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ if (line[0] == '\0')
+ return 1; /* End of headers */
+
+ *headers_size += len;
+
+ if (debug) fprintf(stderr, "httpget: got line '%s'\n", line);
+
+ len= strlen(line);
+ if (tot_headers+len+1 <= max_headers)
+ {
+ fprintf(out_file, "%s\n", line);
+ tot_headers += len+1;
+ } else if (tot_headers <= max_headers && max_headers != 0)
+ {
+ /* Fill up remaining space and report truncation */
+ if (tot_headers < max_headers)
+ {
+ fprintf(out_file, "%.*s\n", max_headers-tot_headers,
+ line);
+ }
+ fprintf(out_file, "[...]\n");
+
+ tot_headers += len+1;
+ }
+
+ if (time_tolerance && strncmp(line, "Date: ", 6) == 0)
+ {
+ /* Try to set time from server */
+ time_t now, tim, tolerance;
+ struct tm tm;
+
+ tolerance= strtoul(time_tolerance, &cp, 10);
+ if (cp[0] != '\0')
+ {
+ fatal("unable to parse tolerance '%s'",
+ time_tolerance);
+ }
+ cp= strptime(line+6, "%a, %d %b %Y %H:%M:%S ", &tm);
+ if (!cp || strcmp(cp, "GMT") != 0)
+ {
+ if (debug)
+ {
+ fprintf(stderr,
+ "unable to parse time '%s'\n",
+ line+6);
+ }
+ }
+ tim= timegm(&tm);
+ now= time(NULL);
+ if (now < tim-tolerance || now > tim+tolerance)
+ {
+ if (debug)
+ { fprintf(stderr,
+ "setting time, time difference is %d\n",
+ (int)(tim-now));
+ }
+ stime(&tim);
+ }
+ }
+
+
+ cp= line;
+ skip_spaces(cp, &ncp);
+ if (ncp != line)
+ continue; /* Continuation line */
+
+ cp= ncp;
+ while (ncp[0] != '\0' && ncp[0] != ':' &&
+ !isspace((unsigned char)ncp[0]))
+ {
+ ncp++;
+ }
+
+ kw= "Transfer-Encoding";
+ len= strlen(kw);
+ if (strncasecmp(cp, kw, len) == 0)
+ {
+ /* Skip optional white space */
+ cp= ncp;
+ skip_spaces(cp, &cp);
+
+ if (cp[0] != ':')
+ {
+ report("malformed content-length header", line);
+ return 0;
+ }
+ cp++;
+
+ /* Skip more white space */
+ skip_spaces(cp, &cp);
+
+ /* Should have the value by now */
+ kw= "chunked";
+ len= strlen(kw);
+ if (strncasecmp(cp, kw, len) != 0)
+ continue;
+ /* make sure we have end of line or white space */
+ if (cp[len] != '\0' && isspace((unsigned char)cp[len]))
+ continue;
+ *chunked= 1;
+ continue;
+ }
+
+ kw= "Content-length";
+ len= strlen(kw);
+ if (strncasecmp(cp, kw, len) != 0)
+ continue;
+
+ /* Skip optional white space */
+ cp= ncp;
+ skip_spaces(cp, &cp);
+
+ if (cp[0] != ':')
+ {
+ report("malformed content-length header", line);
+ return 0;
+ }
+ cp++;
+
+ /* Skip more white space */
+ skip_spaces(cp, &cp);
+
+ /* Should have the value by now */
+ *content_length= strtoul(cp, &check, 10);
+ if (check == cp)
+ {
+ report("malformed content-length header", line);
+ return 0;
+ }
+
+ /* And after that we should have just white space */
+ cp= check;
+ skip_spaces(cp, &cp);
+
+ if (cp[0] != '\0')
+ {
+ report("malformed content-length header", line);
+ return 0;
+ }
+ }
+ if (feof(tcp_file))
+ report("got unexpected EOF from server");
+ else
+ report_err("error reading from server");
+ return 0;
+}
+
+static int connect_to_name(char *host, char *port, int only_v4, int only_v6,
+ struct timeval *start_time, int *gerr)
+{
+ int r, s, s_errno;
+ struct addrinfo *res, *aip;
+ struct addrinfo hints;
+
+ if (debug) fprintf(stderr, "httpget: before getaddrinfo\n");
+ memset(&hints, '\0', sizeof(hints));
+ hints.ai_socktype= SOCK_STREAM;
+ if (only_v4)
+ hints.ai_family= AF_INET;
+ if (only_v6)
+ hints.ai_family= AF_INET6;
+ r= getaddrinfo(host, port, &hints, &res);
+ *gerr= r;
+ if (r != 0)
+ {
+ report("unable to resolve '%s': %s", host, gai_strerror(r));
+ return -1;
+ }
+
+ s_errno= 0;
+ s= -1;
+ for (aip= res; aip != NULL; aip= aip->ai_next)
+ {
+ family= res->ai_family;
+ getnameinfo(res->ai_addr, res->ai_addrlen, host_addr,
+ sizeof(host_addr), NULL, 0, NI_NUMERICHOST);
+
+ gettimeofday(start_time, NULL);
+ s= socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s == -1)
+ {
+ s_errno= errno;
+ continue;
+ }
+
+ if (debug) fprintf(stderr, "httpget: before connect\n");
+ if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
+ break;
+
+ s_errno= errno;
+ close(s);
+ s= -1;
+ }
+
+ freeaddrinfo(res);
+ if (s == -1)
+ errno= s_errno;
+ return s;
+}
+
+char *do_dir(char *dir_name, off_t *lenp)
+{
+ size_t currsize, allocsize, dirlen, len;
+ char *list, *tmplist, *path;
+ DIR *dir;
+ struct dirent *de;
+ struct stat sb;
+
+ /* Scan a directory for files. Return the filenames asa list of
+ * strings. An empty string terminates the list. Also compute the
+ * total size of the files
+ */
+ *lenp= 0;
+ currsize= 0;
+ allocsize= 0;
+ list= NULL;
+ dir= opendir(dir_name);
+ if (dir == NULL)
+ {
+ report_err("opendir failed for '%s'", dir_name);
+ return NULL;
+ }
+
+ dirlen= strlen(dir_name);
+ while (de= readdir(dir), de != NULL)
+ {
+ /* Concat dir and entry */
+ len= dirlen + 1 + strlen(de->d_name) + 1;
+ if (currsize+len > allocsize)
+ {
+ allocsize += 4096;
+ tmplist= realloc(list, allocsize);
+ if (!tmplist)
+ {
+ free(list);
+ report("realloc failed for %d bytes",
+ allocsize);
+ closedir(dir);
+ return NULL;
+ }
+ list= tmplist;
+ }
+ path= list+currsize;
+
+ strlcpy(path, dir_name, allocsize-currsize);
+ strlcat(path, "/", allocsize-currsize);
+ strlcat(path, de->d_name, allocsize-currsize);
+
+ if (stat(path, &sb) != 0)
+ {
+ report_err("stat '%s' failed", path);
+ free(list);
+ closedir(dir);
+ return NULL;
+ }
+
+ if (!S_ISREG(sb.st_mode))
+ continue; /* Just skip entry */
+
+ currsize += len;
+ *lenp += sb.st_size;
+ }
+ closedir(dir);
+
+ /* Add empty string to terminate the list */
+ len= 1;
+ if (currsize+len > allocsize)
+ {
+ allocsize += 4096;
+ tmplist= realloc(list, allocsize);
+ if (!tmplist)
+ {
+ free(list);
+ report("realloc failed for %d bytes", allocsize);
+ return NULL;
+ }
+ list= tmplist;
+ }
+ path= list+currsize;
+
+ *path= '\0';
+
+ return list;
+}
+
+static int copy_chunked(FILE *in_file, FILE *out_file, int *length, int max_body)
+{
+ int need_nl;
+ size_t len, offset, size, tot_body;
+ char *cp, *line, *check;
+
+ *length= 0;
+ need_nl= 0;
+ tot_body= 0;
+ for (;;)
+ {
+ /* Get a chunk size */
+ if (fgets(buffer, sizeof(buffer), in_file) == NULL)
+ {
+ report("error reading input");
+ return 0;
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ report("line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ if (debug)
+ {
+ fprintf(stderr, "httpget: got chunk line '%s'\n", line);
+ }
+ len= strtoul(line, &check, 16);
+ if (check[0] != '\0' && !isspace(*(unsigned char *)check))
+ {
+ report("bad chunk line '%s'", line);
+ return 0;
+ }
+ if (!len)
+ break;
+
+ *length += len;
+
+ offset= 0;
+
+ while (offset < len)
+ {
+ size= len-offset;
+ if (size > sizeof(buffer))
+ size= sizeof(buffer);
+ if (fread(buffer, size, 1, in_file) != 1)
+ {
+ report("error reading input");
+ return 0;
+ }
+
+ if (tot_body+size <= max_body)
+ {
+ if (fwrite(buffer, size, 1, out_file) != 1)
+ fatal_err("error writing output");
+ need_nl= (buffer[size-1] != '\n');
+ tot_body += len;
+ } else if (tot_body <= max_body && max_body != 0)
+ {
+ /* Fill up remaining space and report truncation */
+ if (tot_body < max_body)
+ {
+ if (fwrite(buffer, max_body-tot_body, 1,
+ out_file) != 1)
+ {
+ fatal_err(
+ "error writing output");
+ }
+ }
+ fprintf(out_file, "\n[...]\n");
+ need_nl= 0;
+ tot_body += len;
+ }
+
+ offset += size;
+ }
+
+ /* Expect empty line after data */
+ if (fgets(buffer, sizeof(buffer), in_file) == NULL)
+ {
+ report("error reading input");
+ return 0;
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ report("line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+ if (line[0] != '\0')
+ {
+ report("Garbage after chunk data");
+ return 0;
+ }
+ }
+
+ if (max_body && need_nl)
+ fprintf(out_file, "\n");
+
+ for (;;)
+ {
+ /* Get an end-of-chunk line */
+ if (fgets(buffer, sizeof(buffer), in_file) == NULL)
+ {
+ report("error reading input");
+ return 0;
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ report("line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+ if (line[0] == '\0')
+ break;
+
+ if (debug)
+ {
+ fprintf(stderr,
+ "httpget: got end-of-chunk line '%s'\n", line);
+ }
+ }
+ return 1;
+}
+
+static int copy_bytes(FILE *in_file, FILE *out_file, int *length,
+ int max_body)
+{
+ int len, need_nl;
+ size_t offset, size;
+
+ offset= 0;
+
+ need_nl= 0;
+ len= *length;
+ while (len == -1 || offset < len)
+ {
+ if (len == -1)
+ {
+ size= sizeof(buffer);
+
+ size= fread(buffer, 1, sizeof(buffer), in_file);
+ if (size == 0)
+ {
+ if (feof(in_file))
+ break; /* Got EOF */
+ report_err("error reading input");
+
+ if (max_body && need_nl)
+ fprintf(out_file, "\n");
+ return 0;
+ }
+ }
+ else
+ {
+ size= len-offset;
+ if (size > sizeof(buffer))
+ size= sizeof(buffer);
+
+ if (fread(buffer, size, 1, in_file) != 1)
+ {
+ report_err("error reading input");
+
+ if (max_body && need_nl)
+ fprintf(out_file, "\n");
+ return 0;
+ }
+ }
+
+ if (offset+size <= max_body)
+ {
+ if (fwrite(buffer, size, 1, out_file) != 1)
+ fatal_err("error writing output");
+ need_nl= (buffer[size-1] != '\n');
+ }
+ else if (offset <= max_body && max_body != 0)
+ {
+ /* Fill up remaining space and report truncation */
+ if (offset < max_body)
+ {
+ if (fwrite(buffer, max_body-offset, 1,
+ out_file) != 1)
+ {
+ fatal_err("error writing output");
+ }
+ }
+ fprintf(out_file, "\n[...]\n");
+ need_nl= 0;
+ }
+
+ offset += size;
+ }
+
+ if (max_body && need_nl)
+ fprintf(out_file, "\n");
+ if (len == -1)
+ *length= offset;
+ return 1;
+}
+
+static void skip_spaces(const char *cp, char **ncp)
+{
+ const unsigned char *ucp;
+
+ ucp= (const unsigned char *)cp;
+ while (ucp[0] != '\0' && isspace(ucp[0]))
+ ucp++;
+ *ncp= (char *)ucp;
+}
+
+static void got_alarm(int sig __attribute__((unused)) )
+{
+ // printf("got alarm\n");
+ // printf("switching tcp_fd to nonblocking\n");
+ if (tcp_fd != -1)
+ fcntl(tcp_fd, F_SETFL, fcntl(tcp_fd, F_GETFL) | O_NONBLOCK);
+ //printf("setting alarm again\n");
+ alarm(1);
+}
+
+static void usage(void)
+{
+ fprintf(stderr,
+"Usage: httpget\n");
+ fprintf(stderr,
+" [--post-header <file-to-post>] [--post-file <file-to-post>]\n");
+ fprintf(stderr,
+" [--post-footer <file-to-post>] \n");
+
+ fprintf(stderr,
+" [--post-footer <file-to-post>] [-O <output-file>] <url>\n");
+ exit(1);
+}
+
+static void fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httpget: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+
+ va_end(ap);
+
+ exit(1);
+}
+
+static void fatal_err(const char *fmt, ...)
+{
+ int s_errno;
+ va_list ap;
+
+ s_errno= errno;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httpget: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": %s\n", strerror(s_errno));
+
+ va_end(ap);
+
+ exit(1);
+}
+
+static void report(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httpget: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+
+ va_end(ap);
+}
+
+static void report_err(const char *fmt, ...)
+{
+ int s_errno;
+ va_list ap;
+
+ s_errno= errno;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httpget: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": %s\n", strerror(s_errno));
+
+ va_end(ap);
+}
diff --git a/networking/httppost.c b/networking/httppost.c
new file mode 100644
index 0000000..26934ec
--- /dev/null
+++ b/networking/httppost.c
@@ -0,0 +1,1283 @@
+/*
+ * Copyright (c) 2011-2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * httppost.c -- Simple program that uses the HTTP POST command
+*/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include "libbb.h"
+
+#define SAFE_PREFIX_DATA_OUT ATLAS_DATA_OUT
+#define SAFE_PREFIX_DATA_OOQ_OUT ATLAS_DATA_OOQ_OUT
+#define SAFE_PREFIX_DATA_NEW ATLAS_DATA_NEW
+#define SAFE_PREFIX_STATUS ATLAS_STATUS
+
+struct option longopts[]=
+{
+ { "delete-file", no_argument, NULL, 'd' },
+ { "maxpostsize", required_argument, NULL, 'm' },
+ { "post-file", required_argument, NULL, 'p' },
+ { "post-dir", required_argument, NULL, 'D' },
+ { "post-header", required_argument, NULL, 'h' },
+ { "post-footer", required_argument, NULL, 'f' },
+ { "set-time", required_argument, NULL, 's' },
+ { "timeout", required_argument, NULL, 't' },
+ { NULL, }
+};
+
+static int tcp_fd;
+static struct timeval start_time;
+static time_t timeout = 300;
+
+/* Result sent by controller when input is acceptable. */
+#define OK_STR "OK\n"
+
+static int parse_url(char *url, char **hostp, char **portp, char **hostportp,
+ char **pathp);
+static int check_result(FILE *tcp_file);
+static int eat_headers(FILE *tcp_file, int *chunked, int *content_length, time_t *timep);
+static int connect_to_name(char *host, char *port);
+char *do_dir(char *dir_name, off_t curr_size, off_t max_size, off_t *lenp);
+static int copy_chunked(FILE *in_file, FILE *out_file, int *found_okp);
+static int copy_bytes(FILE *in_file, FILE *out_file, size_t len,
+ int *found_okp);
+static void fatal(const char *fmt, ...);
+// static void fatal_err(const char *fmt, ...);
+static void report(const char *fmt, ...);
+static void report_err(const char *fmt, ...);
+static int write_to_tcp_fd (int fd, FILE *tcp_file);
+static void skip_spaces(const char *cp, char **ncp);
+static void got_alarm(int sig);
+static void kick_watchdog(void);
+
+int httppost_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int httppost_main(int argc, char *argv[])
+{
+ int c, r, fd, fdF, fdH, fdS, chunked, content_length, result;
+ int opt_delete_file, found_ok;
+ char *url, *host, *port, *hostport, *path, *filelist, *p, *check;
+ char *post_dir, *post_file, *atlas_id, *output_file,
+ *post_footer, *post_header, *maxpostsizestr, *timeoutstr;
+ char *time_tolerance;
+ FILE *tcp_file, *out_file, *fh;
+ time_t server_time, tolerance;
+ struct stat sbF, sbH, sbS;
+ off_t cLength, dir_length, maxpostsize;
+ struct sigaction sa;
+
+ post_dir= NULL;
+ post_file= NULL;
+ post_footer=NULL;
+ post_header=NULL;
+ atlas_id= NULL;
+ output_file= NULL;
+ opt_delete_file = 0;
+ time_tolerance = NULL;
+ maxpostsizestr= NULL;
+ timeoutstr= NULL;
+
+ fd= -1;
+ fdH= -1;
+ fdF= -1;
+ fdS= -1;
+ tcp_fd= -1;
+ tcp_file= NULL;
+ out_file= NULL;
+ host= NULL;
+ port= NULL;
+ hostport= NULL;
+ path= NULL;
+ filelist= NULL;
+ maxpostsize= 1000000;
+
+ /* Allow us to be called directly by another program in busybox */
+ optind= 0;
+ while (c= getopt_long(argc, argv, "A:O:?", longopts, NULL), c != -1)
+ {
+ switch(c)
+ {
+ case 'A':
+ atlas_id= optarg;
+ break;
+ case 'O':
+ output_file= optarg;
+ break;
+ case 'd':
+ opt_delete_file = 1;
+ break;
+ case 'D':
+ post_dir = optarg; /* --post-dir */
+ break;
+ case 'h': /* --post-header */
+ post_header= optarg;
+ break;
+ case 'f': /* --post-footer */
+ post_footer= optarg;
+ break;
+ case 'm': /* --maxpostsize */
+ maxpostsizestr= optarg;
+ break;
+ case 'p': /* --post-file */
+ post_file= optarg;
+ break;
+ case 's': /* --set-time */
+ time_tolerance= optarg;
+ break;
+ case 't': /* --timeout */
+ timeoutstr= optarg;
+ break;
+ case '?':
+ fprintf(stderr, "bad option\n");
+ return 1;
+ default:
+ fatal("bad option '%c'", c);
+ }
+ }
+
+ if (optind != argc-1)
+ {
+ fprintf(stderr, "exactly one url expected\n");
+ return 1;
+ }
+ url= argv[optind];
+
+ if (maxpostsizestr)
+ {
+ maxpostsize= strtoul(maxpostsizestr, &check, 0);
+ if (check[0] != 0)
+ {
+ report("unable to parse maxpostsize '%s'",
+ maxpostsizestr);
+ goto err;
+ }
+ }
+
+ if (timeoutstr)
+ {
+ timeout= strtoul(timeoutstr, &check, 0);
+ if (check[0] != 0)
+ {
+ report("unable to parse timeout '%s'",
+ timeoutstr);
+ goto err;
+ }
+ }
+
+ tolerance= 0;
+ if (time_tolerance)
+ {
+ tolerance= strtoul(time_tolerance, &p, 10);
+ if (p[0] != '\0')
+ {
+ fprintf(stderr, "unable to parse tolerance '%s'\n",
+ time_tolerance);
+ return 1;
+ }
+ }
+
+ if (parse_url(url, &host, &port, &hostport, &path) == -1)
+ return 1;
+
+ //printf("host: %s\n", host);
+ //printf("port: %s\n", port);
+ //printf("hostport: %s\n", hostport);
+ //printf("path: %s\n", path);
+
+ cLength= 0;
+
+ if(post_header != NULL )
+ {
+ if (!validate_filename(post_header, SAFE_PREFIX_DATA_OUT) &&
+ !validate_filename(post_header, SAFE_PREFIX_STATUS))
+ {
+ report("protected file (for header) '%s'", post_header);
+ goto err;
+ }
+ fdH = open(post_header, O_RDONLY);
+ if(fdH == -1 )
+ {
+ report_err("unable to open header '%s'", post_header);
+ goto err;
+ }
+ if (fstat(fdH, &sbH) == -1)
+ {
+ report_err("fstat failed on header file '%s'",
+ post_header);
+ goto err;
+ }
+ if (!S_ISREG(sbH.st_mode))
+ {
+ report("'%s' header is not a regular file",
+ post_header);
+ goto err;
+ }
+ cLength += sbH.st_size;
+ }
+
+ if(post_footer != NULL )
+ {
+ if (!validate_filename(post_footer, SAFE_PREFIX_DATA_OUT) &&
+ !validate_filename(post_footer, SAFE_PREFIX_STATUS))
+ {
+ report("pretected file (for footer) '%s'", post_footer);
+ goto err;
+ }
+ fdF = open(post_footer, O_RDONLY);
+ if(fdF == -1 )
+ {
+ report_err("unable to open footer '%s'", post_footer);
+ goto err;
+ }
+ if (fstat(fdF, &sbF) == -1)
+ {
+ report_err("fstat failed on footer file '%s'",
+ post_footer);
+ goto err;
+ }
+ if (!S_ISREG(sbF.st_mode))
+ {
+ report("'%s' footer is not a regular file",
+ post_footer);
+ goto err;
+ }
+ cLength += sbF.st_size;
+ }
+
+ /* Try to open the file before trying to connect */
+ if (post_file != NULL)
+ {
+ if (!validate_filename(post_file, SAFE_PREFIX_DATA_OUT) &&
+ !validate_filename(post_file, SAFE_PREFIX_STATUS))
+ {
+ report("protected file (post) '%s'", post_file);
+ goto err;
+ }
+ fdS= open(post_file, O_RDONLY);
+ if (fdS == -1)
+ {
+ report_err("unable to open '%s'", post_file);
+ goto err;
+ }
+ if (fstat(fdS, &sbS) == -1)
+ {
+ report_err("fstat failed");
+ goto err;
+ }
+ if (!S_ISREG(sbS.st_mode))
+ {
+ report("'%s' is not a regular file", post_file);
+ goto err;
+ }
+ cLength += sbS.st_size;
+ }
+
+ if (post_dir)
+ {
+ filelist= do_dir(post_dir, cLength, maxpostsize, &dir_length);
+ if (!filelist)
+ {
+ /* Something went wrong. */
+ goto err;
+ }
+ fprintf(stderr, "total size in dir: %ld\n", (long)dir_length);
+ cLength += dir_length;
+ }
+
+ gettimeofday(&start_time, NULL);
+
+ sa.sa_flags= 0;
+ sa.sa_handler= got_alarm;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGALRM, &sa, NULL);
+ alarm(10);
+ signal(SIGPIPE, SIG_IGN);
+
+ tcp_fd= connect_to_name(host, port);
+ if (tcp_fd == -1)
+ {
+ report_err("unable to connect to '%s'", host);
+ goto err;
+ }
+
+ /* Stdio makes life easy */
+ tcp_file= fdopen(tcp_fd, "r+");
+ if (tcp_file == NULL)
+ {
+ report("fdopen failed");
+ goto err;
+ }
+
+ fprintf(stderr, "httppost: sending request\n");
+ fprintf(tcp_file, "POST %s HTTP/1.1\r\n", path);
+ //fprintf(tcp_file, "GET %s HTTP/1.1\r\n", path);
+ fprintf(tcp_file, "Host: %s\r\n", host);
+ fprintf(tcp_file, "Connection: close\r\n");
+ fprintf(tcp_file, "User-Agent: httppost for atlas.ripe.net\r\n");
+ fprintf(tcp_file,
+ "Content-Type: application/x-www-form-urlencoded\r\n");
+
+ cLength= 0;
+ if( post_header != NULL )
+ cLength += sbH.st_size;
+
+ if (post_file)
+ cLength += sbS.st_size;
+
+ if (post_dir)
+ cLength += dir_length;
+
+ if( post_footer != NULL )
+ cLength += sbF.st_size;
+
+ fprintf(tcp_file, "Content-Length: %lu\r\n", (unsigned long)cLength);
+ fprintf(tcp_file, "\r\n");
+
+ if( post_header != NULL )
+ {
+ if (!write_to_tcp_fd(fdH, tcp_file))
+ goto err;
+ }
+
+ if (post_file != NULL)
+ {
+ if (!write_to_tcp_fd(fdS, tcp_file))
+ goto err;
+ }
+
+ if (post_dir)
+ {
+ for (p= filelist; p[0] != 0; p += strlen(p)+1)
+ {
+ fprintf(stderr, "posting file '%s'\n", p);
+ if (!validate_filename(p, SAFE_PREFIX_DATA_OUT) &&
+ !validate_filename(p, SAFE_PREFIX_DATA_OOQ_OUT))
+ {
+ report("protected file (post dir) '%s'", p);
+ goto err;
+ }
+ fd= open(p, O_RDONLY);
+ if (fd == -1)
+ {
+ report_err("unable to open '%s'", p);
+ goto err;
+ }
+ r= write_to_tcp_fd(fd, tcp_file);
+ close(fd);
+ fd= -1;
+ if (!r)
+ goto err;
+ }
+ }
+
+ if( post_footer != NULL)
+ {
+ if (!write_to_tcp_fd(fdF, tcp_file))
+ goto err;
+ }
+
+ fprintf(stderr, "httppost: getting result\n");
+ if (!check_result(tcp_file))
+ goto err;
+ fprintf(stderr, "httppost: getting reply headers \n");
+ server_time= 0;
+ if (!eat_headers(tcp_file, &chunked, &content_length, &server_time))
+ goto err;
+
+ if (tolerance && server_time > 0)
+ {
+ /* Try to set time from server */
+ struct timeval now;
+ double rtt;
+
+ gettimeofday(&now, NULL);
+ rtt= now.tv_sec-start_time.tv_sec;
+ rtt += (now.tv_usec-start_time.tv_usec)/1e6;
+ if (rtt < 0) rtt= 0;
+ if (now.tv_sec < server_time-tolerance-rtt ||
+ now.tv_sec > server_time+tolerance+rtt)
+ {
+ fprintf(stderr,
+ "setting time, time difference is %ld\n",
+ (long)server_time-now.tv_sec);
+ stime(&server_time);
+ if (atlas_id)
+ {
+ printf(
+ "RESULT %s ongoing %ld httppost setting time, local %ld, remote %ld\n",
+ atlas_id, (long)time(NULL),
+ (long)now.tv_sec,
+ (long)server_time);
+ }
+ }
+ else if (rtt <= 1)
+ {
+ /* Time and network are fine. Record this fact */
+ fh= fopen(ATLAS_TIMESYNC_FILE ".new", "wt");
+ if (fh)
+ {
+ fprintf(fh, "%ld\n", (long)now.tv_sec);
+ fclose(fh);
+ rename(ATLAS_TIMESYNC_FILE ".new",
+ ATLAS_TIMESYNC_FILE);
+ }
+ }
+ else if (atlas_id)
+ {
+ printf("RESULT %s ongoing %ld httppost rtt %g ms\n",
+ atlas_id, (long)time(NULL), rtt*1000);
+ }
+ }
+
+ fprintf(stderr, "httppost: writing output\n");
+ if (output_file)
+ {
+ if (!validate_filename(output_file, SAFE_PREFIX_DATA_NEW))
+ {
+ report("protected file (output) '%s'", output_file);
+ goto err;
+ }
+ out_file= fopen(output_file, "w");
+ if (!out_file)
+ {
+ report_err("unable to create '%s'", output_file);
+ goto err;
+ }
+ }
+ else
+ out_file= stdout;
+
+ if (chunked)
+ {
+ if (!copy_chunked(tcp_file, out_file, &found_ok))
+ goto err;
+ }
+ else if (content_length)
+ {
+ if (!copy_bytes(tcp_file, out_file, content_length, &found_ok))
+ goto err;
+ }
+ if (!found_ok)
+ fprintf(stderr, "httppost: reply text was not equal to OK\n");
+ if ( opt_delete_file == 1 && found_ok)
+ {
+ fprintf(stderr, "httppost: deleting files\n");
+ if (post_file)
+ {
+ if (!validate_filename(post_file, SAFE_PREFIX_DATA_OUT))
+ {
+ report("trying to delete protected file '%s'",
+ post_file);
+ goto err;
+ }
+ unlink (post_file);
+ }
+ if (post_dir)
+ {
+ for (p= filelist; p[0] != 0; p += strlen(p)+1)
+ {
+ fprintf(stderr, "unlinking file '%s'\n", p);
+ if (unlink(p) != 0)
+ report_err("unable to unlink '%s'", p);
+ }
+ }
+ }
+ fprintf(stderr, "httppost: done\n");
+
+ result= 0;
+
+leave:
+ if (fdH != -1) close(fdH);
+ if (fdF != -1) close(fdF);
+ if (fdS != -1) close(fdS);
+ if (fd != -1) close(fd);
+ if (tcp_file)
+ {
+ fclose(tcp_file);
+ tcp_fd= -1;
+ }
+ if (tcp_fd != -1) close(tcp_fd);
+ if (out_file) fclose(out_file);
+ if (host) free(host);
+ if (port) free(port);
+ if (hostport) free(hostport);
+ if (path) free(path);
+ if (filelist) free(filelist);
+
+ alarm(0);
+ signal(SIGPIPE, SIG_DFL);
+
+ return result;
+
+err:
+ fprintf(stderr, "httppost: leaving with error\n");
+ result= 1;
+ goto leave;
+}
+
+static int write_to_tcp_fd (int fd, FILE *tcp_file)
+{
+ int r;
+ char buffer[1024];
+
+ /* Copy file */
+ while(r= read(fd, buffer, sizeof(buffer)), r > 0)
+ {
+ if (fwrite(buffer, r, 1, tcp_file) != 1)
+ {
+ report_err("error writing to tcp connection");
+ return 0;
+ }
+ alarm(10);
+ }
+ if (r == -1)
+ {
+ report_err("error reading from file");
+ return 0;
+ }
+ return 1;
+}
+
+
+static int parse_url(char *url, char **hostp, char **portp, char **hostportp,
+ char **pathp)
+{
+ char *item;
+ const char *cp, *np, *prefix;
+ size_t len;
+
+ *hostportp= NULL;
+ *pathp= NULL;
+ *hostp= NULL;
+ *portp= NULL;
+
+ /* the url must start with 'http://' */
+ prefix= "http://";
+ len= strlen(prefix);
+ if (strncasecmp(prefix, url, len) != 0)
+ {
+ fprintf(stderr, "bad prefix in url '%s'\n", url);
+ return -1;
+ }
+
+ cp= url+len;
+
+ /* Get hostport part */
+ np= strchr(cp, '/');
+ if (np != NULL)
+ len= np-cp;
+ else
+ {
+ len= strlen(cp);
+ np= cp+len;
+ }
+ if (len == 0)
+ {
+ fprintf(stderr, "missing host part in url '%s'\n", url);
+ return -1;
+ }
+
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ *hostportp= item;
+
+ /* The remainder is the path */
+ cp= np;
+ if (cp[0] == '\0')
+ cp= "/";
+ len= strlen(cp);
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ *pathp= item;
+
+ /* Extract the host name from hostport */
+ cp= *hostportp;
+ np= cp;
+ if (cp[0] == '[')
+ {
+ /* IPv6 address literal */
+ np= strchr(cp, ']');
+ if (np == NULL || np == cp+1)
+ {
+ fprintf(stderr,
+ "malformed IPv6 address literal in url '%s'\n",
+ url);
+ goto error;
+ }
+ }
+ np= strchr(np, ':');
+ if (np != NULL)
+ len= np-cp;
+ else
+ {
+ len= strlen(cp);
+ np= cp+len;
+ }
+ if (len == 0)
+ {
+ fprintf(stderr, "missing host part in url '%s'\n", url);
+ goto error;
+ }
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ if (cp[0] == '[')
+ {
+ /* Leave out the square brackets */
+ memcpy(item, cp+1, len-2);
+ item[len-2]= '\0';
+ }
+ else
+ {
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ }
+ *hostp= item;
+
+ /* Port */
+ cp= np;
+ if (cp[0] == '\0')
+ cp= "80";
+ else
+ cp++;
+ len= strlen(cp);
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ *portp= item;
+
+ return 0;
+error:
+ free(*hostportp); *hostportp= NULL;
+ free(*pathp); *pathp= NULL;
+ free(*hostp); *hostp= NULL;
+ free(*portp); *portp= NULL;
+
+ return -1;
+}
+
+static int check_result(FILE *tcp_file)
+{
+ int major, minor;
+ size_t len;
+ char *cp, *check, *line;
+ const char *prefix;
+ char buffer[1024];
+
+ while (fgets(buffer, sizeof(buffer), tcp_file) == NULL)
+ {
+ if (feof(tcp_file))
+ {
+ report("got unexpected EOF from server");
+ return 0;
+ }
+ if (errno == EINTR)
+ {
+ report("timeout");
+ sleep(10);
+ }
+ else
+ {
+ report_err("error reading from server");
+ return 0;
+ }
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ fprintf(stderr, "line too long\n");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ /* Check http version */
+ prefix= "http/";
+ len= strlen(prefix);
+ if (strncasecmp(prefix, line, len) != 0)
+ {
+ fprintf(stderr, "bad prefix in response '%s'\n", line);
+ return 0;
+ }
+ cp= line+len;
+ major= strtoul(cp, &check, 10);
+ if (check == cp || check[0] != '.')
+ {
+ fprintf(stderr, "bad major version in response '%s'\n", line);
+ return 0;
+ }
+ cp= check+1;
+ minor= strtoul(cp, &check, 10);
+ if (check == cp || check[0] == '\0' ||
+ !isspace(*(unsigned char *)check))
+ {
+ fprintf(stderr, "bad major version in response '%s'\n", line);
+ return 0;
+ }
+
+ skip_spaces(check, &cp);
+
+ if (!isdigit(*(unsigned char *)cp))
+ {
+ fprintf(stderr, "bad status code in response '%s'\n", line);
+ return 0;
+ }
+
+ if (cp[0] != '2')
+ {
+ report("POST command failed: '%s'", cp);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int eat_headers(FILE *tcp_file, int *chunked, int *content_length, time_t *timep)
+{
+ char *line, *cp, *ncp, *check;
+ size_t len;
+ const char *kw;
+ char buffer[1024];
+
+ *chunked= 0;
+ *content_length= 0;
+ while (fgets(buffer, sizeof(buffer), tcp_file) != NULL)
+ {
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ fprintf(stderr, "line too long\n");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ if (line[0] == '\0')
+ return 1; /* End of headers */
+
+ fprintf(stderr, "httppost: got line '%s'\n", line);
+
+ if (strncmp(line, "Date: ", 6) == 0)
+ {
+ /* Parse date header */
+ struct tm tm;
+
+ cp= strptime(line+6, "%a, %d %b %Y %H:%M:%S ", &tm);
+ if (!cp || strcmp(cp, "GMT") != 0)
+ {
+ fprintf(stderr, "unable to parse time '%s'\n",
+ line+6);
+ }
+ *timep= timegm(&tm);
+ }
+
+ cp= line;
+ skip_spaces(cp, &ncp);
+ if (ncp != line)
+ continue; /* Continuation line */
+
+ cp= ncp;
+ while (ncp[0] != '\0' && ncp[0] != ':' &&
+ !isspace((unsigned char)ncp[0]))
+ {
+ ncp++;
+ }
+
+ kw= "Transfer-Encoding";
+ len= strlen(kw);
+ if (strncasecmp(cp, kw, len) == 0)
+ {
+ /* Skip optional white space */
+ cp= ncp;
+ skip_spaces(cp, &cp);
+
+ if (cp[0] != ':')
+ {
+ fprintf(stderr,
+ "malformed transfer-encoding header");
+ return 0;
+ }
+ cp++;
+
+ /* Skip more white space */
+ skip_spaces(cp, &cp);
+
+ /* Should have the value by now */
+ kw= "chunked";
+ len= strlen(kw);
+ if (strncasecmp(cp, kw, len) != 0)
+ continue;
+ /* make sure we have end of line or white space */
+ if (cp[len] != '\0' && isspace((unsigned char)cp[len]))
+ continue;
+ *chunked= 1;
+ continue;
+ }
+
+ kw= "Content-length";
+ len= strlen(kw);
+ if (strncasecmp(cp, kw, len) != 0)
+ continue;
+
+ /* Skip optional white space */
+ cp= ncp;
+ skip_spaces(cp, &cp);
+
+ if (cp[0] != ':')
+ {
+ fprintf(stderr, "malformed content-length header");
+ return 0;
+ }
+ cp++;
+
+ /* Skip more white space */
+ skip_spaces(cp, &cp);
+
+ /* Should have the value by now */
+ *content_length= strtoul(cp, &check, 10);
+ if (check == cp)
+ {
+ fprintf(stderr, "malformed content-length header\n");
+ return 0;
+ }
+
+ /* And after that we should have just white space */
+ cp= check;
+ skip_spaces(cp, &cp);
+
+ if (cp[0] != '\0')
+ {
+ fprintf(stderr, "malformed content-length header\n");
+ return 0;
+ }
+ }
+ if (feof(tcp_file))
+ report("got unexpected EOF from server");
+ else
+ report_err("error reading from server");
+ return 0;
+}
+
+static int connect_to_name(char *host, char *port)
+{
+ int r, s, s_errno;
+ struct addrinfo *res, *aip;
+ struct addrinfo hints;
+
+ fprintf(stderr, "httppost: before getaddrinfo\n");
+ memset(&hints, '\0', sizeof(hints));
+ hints.ai_socktype= SOCK_STREAM;
+ r= getaddrinfo(host, port, &hints, &res);
+ if (r != 0)
+ {
+ fprintf(stderr, "unable to resolve '%s': %s\n",
+ host, gai_strerror(r));
+ errno= ENOENT; /* Need something */
+ return -1;
+ }
+
+ s_errno= 0;
+ s= -1;
+ for (aip= res; aip != NULL; aip= aip->ai_next)
+ {
+ s= socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s == -1)
+ {
+ s_errno= errno;
+ continue;
+ }
+
+ fprintf(stderr, "httppost: before connect\n");
+ if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
+ break;
+
+ s_errno= errno;
+ close(s);
+ s= -1;
+ }
+
+ freeaddrinfo(res);
+ if (s == -1)
+ errno= s_errno;
+ return s;
+}
+
+char *do_dir(char *dir_name, off_t curr_tot_size, off_t max_size, off_t *lenp)
+{
+ size_t currsize, allocsize, dirlen, len;
+ char *list, *tmplist, *path;
+ DIR *dir;
+ struct dirent *de;
+ struct stat sb;
+
+ /* Scan a directory for files. Return the filenames asa list of
+ * strings. An empty string terminates the list. Also compute the
+ * total size of the files
+ */
+ *lenp= 0;
+ currsize= 0;
+ allocsize= 0;
+ list= NULL;
+ dir= opendir(dir_name);
+ if (dir == NULL)
+ {
+ report_err("opendir failed for '%s'", dir_name);
+ return NULL;
+ }
+
+ dirlen= strlen(dir_name);
+ while (de= readdir(dir), de != NULL)
+ {
+ /* Concat dir and entry */
+ len= dirlen + 1 + strlen(de->d_name) + 1;
+ if (currsize+len > allocsize)
+ {
+ allocsize += 4096;
+ tmplist= realloc(list, allocsize);
+ if (!tmplist)
+ {
+ free(list);
+ report("realloc failed for %d bytes",
+ allocsize);
+ closedir(dir);
+ return NULL;
+ }
+ list= tmplist;
+ }
+ path= list+currsize;
+
+ strlcpy(path, dir_name, allocsize-currsize);
+ strlcat(path, "/", allocsize-currsize);
+ strlcat(path, de->d_name, allocsize-currsize);
+
+ if (stat(path, &sb) != 0)
+ {
+ report_err("stat '%s' failed", path);
+ free(list);
+ closedir(dir);
+ return NULL;
+ }
+
+ if (!S_ISREG(sb.st_mode))
+ continue; /* Just skip entry */
+
+ if (curr_tot_size + sb.st_size > max_size)
+ {
+ /* File is too big to fit this time. */
+ if (sb.st_size > max_size/2)
+ {
+ /* File just too big in general */
+ report("deleting file '%s', size %d",
+ path, sb.st_size);
+ unlink(path);
+ }
+ continue;
+ }
+
+ currsize += len;
+ curr_tot_size += sb.st_size;
+ *lenp += sb.st_size;
+ }
+ closedir(dir);
+
+ /* Add empty string to terminate the list */
+ len= 1;
+ if (currsize+len > allocsize)
+ {
+ allocsize += 4096;
+ tmplist= realloc(list, allocsize);
+ if (!tmplist)
+ {
+ free(list);
+ report("realloc failed for %d bytes", allocsize);
+ return NULL;
+ }
+ list= tmplist;
+ }
+ path= list+currsize;
+
+ *path= '\0';
+
+ return list;
+}
+
+static int copy_chunked(FILE *in_file, FILE *out_file, int *found_okp)
+{
+ int i;
+ size_t len, offset, size;
+ char *cp, *line, *check;
+ const char *okp;
+ char buffer[1024];
+
+ okp= OK_STR;
+
+ for (;;)
+ {
+ /* Get a chunk size */
+ if (fgets(buffer, sizeof(buffer), in_file) == NULL)
+ {
+ report("error reading input");
+ return 0;
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ fprintf(stderr, "line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ fprintf(stderr, "httppost: got chunk line '%s'\n", line);
+ len= strtoul(line, &check, 16);
+ if (check[0] != '\0' && !isspace(*(unsigned char *)check))
+ {
+ fprintf(stderr, "bad chunk line '%s'", line);
+ return 0;
+ }
+ if (!len)
+ break;
+
+ offset= 0;
+
+ while (offset < len)
+ {
+ size= len-offset;
+ if (size > sizeof(buffer))
+ size= sizeof(buffer);
+ if (fread(buffer, size, 1, in_file) != 1)
+ {
+ report_err("error reading input");
+ return 0;
+ }
+ if (fwrite(buffer, size, 1, out_file) != 1)
+ {
+ fprintf(stderr, "error writing output");
+ return 0;
+ }
+ offset += size;
+
+ for (i= 0; i<size; i++)
+ {
+ if (!okp)
+ break;
+ if (*okp != buffer[i] || *okp == '\0')
+ {
+ okp= NULL;
+ break;
+ }
+ okp++;
+ }
+ }
+
+ /* Expect empty line after data */
+ if (fgets(buffer, sizeof(buffer), in_file) == NULL)
+ {
+ report("error reading input");
+ return 0;
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ fprintf(stderr, "line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+ if (line[0] != '\0')
+ {
+ fprintf(stderr, "Garbage after chunk data");
+ return 0;
+ }
+ }
+
+ for (;;)
+ {
+ /* Get an end-of-chunk line */
+ if (fgets(buffer, sizeof(buffer), in_file) == NULL)
+ {
+ report("error reading input");
+ return 0;
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ fprintf(stderr, "line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+ if (line[0] == '\0')
+ break;
+
+ fprintf(stderr, "httppost: got end-of-chunk line '%s'\n", line);
+ }
+ *found_okp= (okp != NULL && *okp == '\0');
+ return 1;
+}
+
+static int copy_bytes(FILE *in_file, FILE *out_file, size_t len, int *found_okp)
+{
+ int i;
+ size_t offset, size;
+ const char *okp;
+ char buffer[1024];
+
+ okp= OK_STR;
+
+ offset= 0;
+
+ while (offset < len)
+ {
+ size= len-offset;
+ if (size > sizeof(buffer))
+ size= sizeof(buffer);
+ if (fread(buffer, size, 1, in_file) != 1)
+ {
+ report_err("error reading input");
+ return 0;
+ }
+ if (fwrite(buffer, size, 1, out_file) != 1)
+ {
+ report_err("error writing output");
+ return 0;
+ }
+ offset += size;
+
+ for (i= 0; i<size; i++)
+ {
+ if (!okp)
+ break;
+ if (*okp != buffer[i] || *okp == '\0')
+ {
+ okp= NULL;
+ break;
+ }
+ okp++;
+ }
+ }
+ *found_okp= (okp != NULL && *okp == '\0');
+ return 1;
+}
+
+static void skip_spaces(const char *cp, char **ncp)
+{
+ const unsigned char *ucp;
+
+ ucp= (const unsigned char *)cp;
+ while (ucp[0] != '\0' && isspace(ucp[0]))
+ ucp++;
+ *ncp= (char *)ucp;
+}
+
+static void got_alarm(int sig __attribute__((unused)) )
+{
+ if (tcp_fd != -1 && time(NULL) > start_time.tv_sec+timeout)
+ {
+ report("setting tcp_fd to nonblock");
+ fcntl(tcp_fd, F_SETFL, fcntl(tcp_fd, F_GETFL) | O_NONBLOCK);
+ }
+ kick_watchdog();
+ report("got alarm, setting alarm again");
+ alarm(1);
+}
+
+static void kick_watchdog(void)
+{
+ int fdwatchdog = open("/dev/watchdog", O_RDWR);
+ if (fdwatchdog != -1)
+ {
+ write(fdwatchdog, "1", 1);
+ close(fdwatchdog);
+ }
+}
+
+static void fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httppost: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+
+ va_end(ap);
+
+ exit(1);
+}
+
+#if 0
+static void fatal_err(const char *fmt, ...)
+{
+ int s_errno;
+ va_list ap;
+
+ s_errno= errno;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httppost: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": %s\n", strerror(s_errno));
+
+ va_end(ap);
+
+ exit(1);
+}
+#endif
+
+static void report(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httppost: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+
+ va_end(ap);
+}
+
+static void report_err(const char *fmt, ...)
+{
+ int s_errno;
+ va_list ap;
+
+ s_errno= errno;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httppost: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": %s\n", strerror(s_errno));
+
+ va_end(ap);
+}
diff --git a/networking/rptra6.c b/networking/rptra6.c
new file mode 100644
index 0000000..b481252
--- /dev/null
+++ b/networking/rptra6.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+#include "libbb.h"
+
+#include <netinet/in.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+
+#define OPT_STRING "P:"
+
+#define DBQ(str) "\"" #str "\""
+
+#define IN6ADDR_ALL_NODES_INIT { { { 0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }
+struct in6_addr in6addr_all_nodes = IN6ADDR_ALL_NODES_INIT; /* ff02::1 */
+
+#define OPT_RDNSS 25
+
+#define RA_PREF_MASK 0x18
+#define RA_PREF_HIGH 0x08
+#define RA_PREF_LOW 0x18
+
+struct opt_rdnss /* RDNSS option */
+{
+ uint8_t nd_opt_rdnss_type;
+ uint8_t nd_opt_rdnss_len;
+ uint16_t nd_opt_rdnss_reserved;
+ uint32_t nd_opt_rdnss_lifetime;
+};
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage: rptra6 <new> <out>\n");
+ exit(1);
+}
+
+int rptra6_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE;
+int rptra6_main(int argc, char *argv[])
+{
+ int i, r, first, sock, on, nrecv, rcvd_ttl, olen;
+ uint8_t flags_reserved;
+ size_t o;
+ char *new_name, *out_name;
+ struct nd_router_advert *ra;
+ struct nd_opt_hdr *oh;
+ struct nd_opt_prefix_info *pi;
+ struct nd_opt_mtu *mtup;
+ struct opt_rdnss *rdnssp;
+ struct icmp6_hdr * icmp;
+ struct cmsghdr *cmsgptr;
+ struct sockaddr_in6 *sin6p;
+ FILE *of;
+ char *str_pidfile;
+ struct stat sb;
+ struct sockaddr_in6 remote; /* responding internet address */
+ struct sockaddr_in6 loc_sin6;
+ struct msghdr msg;
+ struct iovec iov[1];
+ char namebuf[NI_MAXHOST];
+ char cmsgbuf[256];
+ char packet[4096];
+
+ str_pidfile= NULL;
+ (void) getopt32(argv, OPT_STRING, &str_pidfile);
+
+ if (argc != optind+2)
+ usage();
+
+ new_name= argv[optind];
+ out_name= argv[optind+1];
+
+ if (str_pidfile)
+ {
+ of= fopen(str_pidfile, "w");
+ if (of)
+ {
+ fprintf(of, "%d\n", getpid());
+ fclose(of);
+ }
+ }
+
+ of= NULL;
+
+ sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+ if (sock == -1)
+ {
+ printf("socket failed: %s\n", strerror(errno));
+ return 1;
+ }
+
+ on = 1;
+ setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
+
+ on = 1;
+ setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on));
+
+
+ icmp = (struct icmp6_hdr *) packet;
+
+ for(;;)
+ {
+ iov[0].iov_base= packet;
+ iov[0].iov_len= sizeof(packet);
+ msg.msg_name= &remote;
+ msg.msg_namelen= sizeof(remote);
+ msg.msg_iov= iov;
+ msg.msg_iovlen= 1;
+ msg.msg_control= cmsgbuf;
+ msg.msg_controllen= sizeof(cmsgbuf);
+ msg.msg_flags= 0; /* Not really needed */
+
+ /* Receive data from the network */
+ nrecv= recvmsg(sock, &msg, 0);
+ if (nrecv < 0)
+ {
+ printf("recvmsg failed: %s\n", strerror(errno));
+ break;
+ }
+
+ /* Check for Destination Host Unreachable */
+ if (icmp->icmp6_type != ND_ROUTER_ADVERT)
+ {
+ switch(icmp->icmp6_type)
+ {
+ case ICMP6_DST_UNREACH: /* 1 */
+ case ICMP6_PACKET_TOO_BIG: /* 2 */
+ case ICMP6_TIME_EXCEEDED: /* 3 */
+ case ICMP6_ECHO_REQUEST: /* 128 */
+ case ICMP6_ECHO_REPLY: /* 129 */
+ case ND_NEIGHBOR_SOLICIT: /* 135 */
+ case ND_NEIGHBOR_ADVERT: /* 136 */
+ case ND_REDIRECT: /* 137 */
+ break; /* Ignore */
+ default:
+ printf("icmp6_type %d\n", icmp->icmp6_type);
+ break;
+ }
+ continue;
+ }
+
+ of= fopen(new_name, "a");
+ if (of == NULL)
+ {
+ fprintf(stderr, "unable to open '%s': %s\n", new_name, strerror(errno));
+ exit(1);
+ }
+
+ fprintf(of, "RESULT { " DBQ(id) ": " DBQ(9019) ", " DBQ(time) ": %ld",
+ (long)time(NULL));
+ getnameinfo((struct sockaddr *)&remote, msg.msg_namelen,
+ namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST);
+ fprintf(of, ", " DBQ(src) ": " DBQ(%s), namebuf);
+
+ /* Set destination address of packet as local address */
+ memset(&loc_sin6, '\0', sizeof(loc_sin6));
+ for (cmsgptr= CMSG_FIRSTHDR(&msg); cmsgptr;
+ cmsgptr= CMSG_NXTHDR(&msg, cmsgptr))
+ {
+ if (cmsgptr->cmsg_len == 0)
+ break; /* Can this happen? */
+ if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
+ cmsgptr->cmsg_type == IPV6_PKTINFO)
+ {
+ sin6p= &loc_sin6;
+ sin6p->sin6_family= AF_INET6;
+ sin6p->sin6_addr= ((struct in6_pktinfo *)
+ CMSG_DATA(cmsgptr))->ipi6_addr;
+ }
+ if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
+ cmsgptr->cmsg_type == IPV6_HOPLIMIT)
+ {
+ rcvd_ttl= *(int *)CMSG_DATA(cmsgptr);
+ }
+ }
+
+ if (memcmp(&loc_sin6.sin6_addr, &in6addr_all_nodes,
+ sizeof(loc_sin6.sin6_addr)) != 0)
+ {
+ getnameinfo((struct sockaddr *)&loc_sin6, sizeof(loc_sin6),
+ namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST);
+ fprintf(of, ", " DBQ(dst) ": " DBQ(%s), namebuf);
+ }
+ if (rcvd_ttl != 255)
+ fprintf(of, ", " DBQ(ttl) ": %d", rcvd_ttl);
+
+ ra= (struct nd_router_advert *)packet;
+ fprintf(of, ", " DBQ(hop_limit) ": %d", ra->nd_ra_curhoplimit);
+ flags_reserved= ra->nd_ra_flags_reserved;
+ if (flags_reserved & ND_RA_FLAG_OTHER)
+ {
+ fprintf(of, ", " DBQ(other_conf) ": true");
+ flags_reserved &= ~ND_RA_FLAG_OTHER;
+ }
+ switch(flags_reserved & RA_PREF_MASK)
+ {
+ case RA_PREF_HIGH:
+ fprintf(of, ", " DBQ(preference) ": " DBQ(high));
+ flags_reserved &= ~RA_PREF_MASK;
+ break;
+ case RA_PREF_LOW:
+ fprintf(of, ", " DBQ(preference) ": " DBQ(low));
+ flags_reserved &= ~RA_PREF_MASK;
+ break;
+ }
+ if (flags_reserved)
+ fprintf(of, ", " DBQ(reserved) ": 0x%x", flags_reserved);
+ fprintf(of, ", " DBQ(lifetime) ": %d", ntohs(ra->nd_ra_router_lifetime));
+ if (ra->nd_ra_reachable)
+ fprintf(of, ", " DBQ(reachable_time) ": %d", ntohl(ra->nd_ra_reachable));
+ if (ra->nd_ra_retransmit)
+ fprintf(of, ", " DBQ(retransmit_time) ": %d", ntohl(ra->nd_ra_retransmit));
+
+ fprintf(of, ", " DBQ(options) ": [ ");
+ first= 1;
+ for (o= sizeof(*ra); o<nrecv;)
+ {
+ if (!first)
+ fprintf(of, ", ");
+ else
+ first= 0;
+
+ if (o+sizeof(*oh) > nrecv)
+ {
+ printf("partial option\n");
+ break;
+ }
+
+ oh= (struct nd_opt_hdr *)&packet[o];
+ if (oh->nd_opt_len == 0)
+ {
+ printf("bad option length (0) at %ld\n",
+ (long)o);
+ break;
+ }
+ olen= oh->nd_opt_len * 8;
+
+ switch(oh->nd_opt_type)
+ {
+ case ND_OPT_SOURCE_LINKADDR: /* 1 */
+ fprintf(of, "{ " DBQ(type) ": " DBQ(link layer address) ", "
+ DBQ(addr) ": \"");
+ for (i= 2; i<olen; i++)
+ {
+ fprintf(of, "%s%02x", i == 2 ? "" : ":",
+ ((uint8_t *)oh)[i]);
+ }
+ fprintf(of, "\" }");
+ break;
+ case ND_OPT_PREFIX_INFORMATION: /* 3 */
+ if (olen < sizeof(*pi))
+ {
+ printf(
+ "bad option length (%d) for prefix info\n",
+ oh->nd_opt_len);
+ break;
+ }
+ pi= (struct nd_opt_prefix_info *)oh;
+ fprintf(of, "{ " DBQ(prefix_len) ": %d",
+ pi->nd_opt_pi_prefix_len);
+ flags_reserved= pi->nd_opt_pi_flags_reserved;
+ if (flags_reserved & ND_OPT_PI_FLAG_ONLINK)
+ {
+ fprintf(of, ", " DBQ(onlink) ": true");
+ flags_reserved &= ~ND_OPT_PI_FLAG_ONLINK;
+ }
+ if (flags_reserved & ND_OPT_PI_FLAG_AUTO)
+ {
+ fprintf(of, ", " DBQ(auto) ": true");
+ flags_reserved &= ~ND_OPT_PI_FLAG_AUTO;
+ }
+
+ if (flags_reserved)
+ {
+ fprintf(of, ", " DBQ(reserved1) ": 0x%x", flags_reserved);
+ }
+ fprintf(of, ", " DBQ(valid_time) ": %d",
+ ntohl(pi-> nd_opt_pi_valid_time));
+ fprintf(of, ", " DBQ(preferred_time) ": %d",
+ ntohl(pi-> nd_opt_pi_preferred_time));
+ if (pi-> nd_opt_pi_reserved2)
+ {
+ fprintf(of, ", " DBQ(reserved2) ": %d",
+ ntohl(pi-> nd_opt_pi_reserved2));
+ }
+
+ fprintf(of, ", " DBQ(prefix) ": " DBQ(%s) " }",
+ inet_ntop(AF_INET6, &pi->nd_opt_pi_prefix,
+ namebuf, sizeof(namebuf)));
+ break;
+
+ case ND_OPT_MTU: /* 5 */
+ fprintf(of, "{ " DBQ(type) ": " DBQ(mtu));
+ mtup= (struct nd_opt_mtu *)oh;
+ if (mtup->nd_opt_mtu_reserved)
+ {
+ fprintf(of, ", " DBQ(reserved) ": 0x%x",
+ ntohs(mtup->nd_opt_mtu_reserved));
+ }
+ fprintf(of, ", " DBQ(mtu) ": %d }",
+ ntohl(mtup->nd_opt_mtu_mtu));
+ break;
+
+ case OPT_RDNSS: /* 25 */
+ fprintf(of, "{ " DBQ(type) ": " DBQ(rdnss));
+ rdnssp= (struct opt_rdnss *)oh;
+ if (rdnssp->nd_opt_rdnss_reserved)
+ {
+ fprintf(of, ", " DBQ(reserved) ": %d",
+ ntohs(rdnssp->nd_opt_rdnss_reserved));
+ }
+ fprintf(of, ", " DBQ(lifetime) ": %d",
+ ntohl(rdnssp->nd_opt_rdnss_lifetime));
+
+ fprintf(of, ", " DBQ(addrs) ": [ ");
+ for (i= 8; i+16 <= olen; i+= 16)
+ {
+ inet_ntop(AF_INET6, ((char *)oh)+i,
+ namebuf, sizeof(namebuf));
+ fprintf(of, "%s" DBQ(%s),
+ i == 8 ? "" : ", ",
+ namebuf);
+ }
+ fprintf(of, " ] }");
+ break;
+
+ default:
+ fprintf(of, "{ " DBQ(type_no) ": %d }", oh->nd_opt_type);
+ break;
+ }
+
+
+ o += olen;
+ }
+ fprintf(of, " ] }\n");
+
+ fclose(of);
+
+ r= stat(out_name, &sb);
+ if (r == 0)
+ continue;
+ if (errno == ENOENT)
+ {
+ rename(new_name, out_name);
+ continue;
+ }
+ fprintf(stderr, "stat '%s' failed: %s\n", out_name, strerror(errno));
+ exit(1);
+ }
+
+ fprintf(stderr, "end of main\n");
+ exit(1);
+}
diff --git a/networking/rxtxrpt.c b/networking/rxtxrpt.c
new file mode 100644
index 0000000..609cedb
--- /dev/null
+++ b/networking/rxtxrpt.c
@@ -0,0 +1,368 @@
+/*
+rxtxrpt.c
+
+Report RX and TX statistics. Also report IPv6 address and the IPv6 routing
+table if it has changed.
+*/
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libbb.h"
+
+#define DEV_FILE "/proc/net/dev"
+#define IF_INET6_FILE "/proc/net/if_inet6"
+#define IPV6_ROUTE_FILE "/proc/net/ipv6_route"
+#define SUFFIX ".new"
+
+int do_atlas= 0;
+
+static int rpt_rxtx(void);
+static int setup_ipv6_rpt(char *cache_name, int *need_report);
+static int rpt_ipv6(char *cache_name);
+static void report(const char *fmt, ...);
+static void report_err(const char *fmt, ...);
+
+int rxtxrpt_main(int argc, char *argv[])
+{
+ int r, need_report;
+ unsigned opt;
+ char *opt_atlas, *cache_name;
+
+ opt_atlas= NULL;
+ opt_complementary= NULL;
+ opt= getopt32(argv, "A:", &opt_atlas);
+
+ do_atlas= (opt_atlas != NULL);
+
+ if (argc > optind+1)
+ bb_show_usage();
+
+ cache_name= NULL;
+ if (argc == optind+1)
+ cache_name= argv[optind];
+
+ if (do_atlas)
+ {
+ printf("%s %lu ", opt_atlas, time(NULL));
+ }
+
+ r= rpt_rxtx();
+ if (r != 0)
+ return r;
+
+ if (cache_name)
+ {
+ r= setup_ipv6_rpt(cache_name, &need_report);
+ if (r != 0)
+ return r;
+ if (need_report)
+ {
+ r= rpt_ipv6(cache_name);
+ if (r != 0)
+ return r;
+ }
+ }
+
+ if (do_atlas)
+ printf("\n");
+
+ return 0;
+}
+
+static int rpt_rxtx(void)
+{
+ int i;
+ char *cp;
+ FILE *file;
+ char buf[256];
+
+ file= fopen(DEV_FILE, "r");
+ if (!file)
+ {
+ report_err("unable to open '%s'", DEV_FILE);
+ return 1;
+ }
+
+ /* Skip two lines */
+ if (fgets(buf, sizeof(buf), file) == NULL ||
+ fgets(buf, sizeof(buf), file) == NULL)
+ {
+ report_err("unable to read from '%s'", DEV_FILE);
+ fclose(file);
+ return 1;
+ }
+
+ /* Copy two line */
+ for (i= 0; i<2; i++)
+ {
+ if (fgets(buf, sizeof(buf), file) == NULL)
+ {
+ report_err("unable to read from '%s'", DEV_FILE);
+ fclose(file);
+ return 1;
+ }
+
+ if (do_atlas)
+ {
+ /* Get rid of newline */
+ cp= strchr(buf, '\n');
+ if (cp) *cp= '\0';
+
+ if (i != 0)
+ printf(" NEWLINE ");
+ }
+ fputs(buf, stdout);
+ }
+ fclose(file);
+
+ return 0;
+}
+
+static int setup_ipv6_rpt(char *cache_name, int *need_report)
+{
+ int i, r;
+ char *cp, *cp1;
+ char filename[80];
+ char buf1[1024];
+ char buf2[1024];
+ FILE *in_file, *out_file, *cache_file;
+
+ *need_report= 0;
+
+ if (strlen(cache_name) + strlen(SUFFIX) + 1 > sizeof(filename))
+ {
+ report("cache name '%s' too long", cache_name);
+ return 1;
+ }
+
+ strlcpy(filename, cache_name, sizeof(filename));
+ strlcat(filename, SUFFIX, sizeof(filename));
+
+ out_file= fopen(filename, "w");
+ if (out_file == NULL)
+ {
+ report_err("unable to create '%s'", filename);
+ return 1;
+ }
+
+ /* Copy IF_INET6_FILE */
+ in_file= fopen(IF_INET6_FILE, "r");
+ if (in_file == NULL)
+ {
+ report_err("unable to open '%s'", IF_INET6_FILE);
+ fclose(out_file);
+ return 1;
+ }
+
+ while (r= fread(buf1, 1, sizeof(buf1), in_file), r > 0)
+ {
+ if (fwrite(buf1, 1, r, out_file) != r)
+ {
+ report_err("error writing to '%s'", filename);
+ fclose(in_file);
+ fclose(out_file);
+ return 1;
+ }
+ }
+ if (ferror(in_file))
+ {
+ report_err("error reading from '%s'", IF_INET6_FILE);
+ fclose(in_file);
+ fclose(out_file);
+ return 1;
+ }
+ fclose(in_file);
+
+ /* Copy IPV6_ROUTE_FILE */
+ in_file= fopen(IPV6_ROUTE_FILE, "r");
+ if (in_file == NULL)
+ {
+ report_err("unable to open '%s'", IPV6_ROUTE_FILE);
+ fclose(out_file);
+ return 1;
+ }
+
+ while (fgets(buf1, sizeof(buf1), in_file) != NULL)
+ {
+ /* Cut out Ref and Use fields */
+ cp= buf1;
+ for (i= 0; i<6; i++)
+ {
+ if (cp && cp[0] != '\0')
+ cp= strchr(cp+1, ' ');
+ }
+ if (!cp && cp[0] == '\0')
+ {
+ report("bad data in '%s'", IPV6_ROUTE_FILE);
+ fclose(in_file);
+ fclose(out_file);
+ return 1;
+ }
+ cp++;
+ /* Find the end of the two fields */
+ cp1= cp;
+ for (i= 0; i<2; i++)
+ {
+ if (cp1 && cp1[0] != '\0')
+ cp1= strchr(cp1+1, ' ');
+ }
+ if (!cp1 && cp1[0] == '\0')
+ {
+ report("bad data in '%s'", IPV6_ROUTE_FILE);
+ fclose(in_file);
+ fclose(out_file);
+ return 1;
+ }
+ cp1++;
+ /* And delete the two fields */
+ memmove(cp, cp1, strlen(cp1)+1);
+
+ if (fputs(buf1, out_file) == -1)
+ {
+ report_err("error writing to '%s'", filename);
+ fclose(in_file);
+ fclose(out_file);
+ return 1;
+ }
+ }
+ if (ferror(in_file))
+ {
+ report_err("error reading from '%s'", IPV6_ROUTE_FILE);
+ fclose(in_file);
+ fclose(out_file);
+ return 1;
+ }
+ fclose(in_file);
+
+ /* Now check if the new file is different from the cache one */
+ fclose(out_file);
+ cache_file= fopen(cache_name, "r");
+ if (cache_file == NULL)
+ {
+ /* Assume that any kind of error here calls for reporting */
+ *need_report= 1;
+ }
+
+ if (cache_file)
+ {
+ in_file= fopen(filename, "r");
+ if (in_file == NULL)
+ {
+ report_err("unable to open '%s'", filename);
+ fclose(cache_file);
+ return 1;
+ }
+
+ /* Compare them */
+ while (r= fread(buf1, 1, sizeof(buf1), cache_file), r > 0)
+ {
+ if (fread(buf2, 1, sizeof(buf2), in_file) != r)
+ {
+ /* Ignore errors, just report */
+ *need_report= 1;
+ break;
+ }
+
+ if (memcmp(buf1, buf2, r) != 0)
+ {
+ /* Something changed, report */
+ *need_report= 1;
+ break;
+ }
+ }
+
+ /* Maybe something got added */
+ if (!*need_report)
+ {
+ if (fread(buf2, 1, sizeof(buf2), in_file) != 0)
+ {
+ *need_report= 1;
+ }
+ }
+ fclose(cache_file);
+ fclose(in_file);
+ }
+
+ if (*need_report)
+ {
+ if (rename(filename, cache_name) == -1)
+ {
+ report_err("renaming '%s' to '%s' failed",
+ filename, cache_name);
+ return 1;
+ }
+ }
+ else
+ {
+ if (unlink(filename) == -1)
+ {
+ report_err("unlinking '%s' failed",
+ filename);
+ }
+ }
+
+ return 0;
+}
+
+static int rpt_ipv6(char *cache_name)
+{
+ FILE *file;
+ char *cp;
+ char buf[256];
+
+ file= fopen(cache_name, "r");
+ if (!file)
+ {
+ report_err("unable to open '%s'", cache_name);
+ return 1;
+ }
+
+ /* Copy all lines */
+ while (fgets(buf, sizeof(buf), file) != NULL)
+ {
+ if (do_atlas)
+ {
+ /* Get rid of newline */
+ cp= strchr(buf, '\n');
+ if (cp) *cp= '\0';
+
+ printf(" NEWLINE ");
+ }
+ fputs(buf, stdout);
+ }
+ fclose(file);
+
+ return 0;
+}
+
+static void report(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "rxtxrpt: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+
+ va_end(ap);
+}
+
+static void report_err(const char *fmt, ...)
+{
+ int t_errno;
+ va_list ap;
+
+ t_errno= errno;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "rxtxrpt: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": %s\n", strerror(t_errno));
+
+ va_end(ap);
+}
diff --git a/networking/sslgetcert.c b/networking/sslgetcert.c
new file mode 100644
index 0000000..a0fbf3c
--- /dev/null
+++ b/networking/sslgetcert.c
@@ -0,0 +1,762 @@
+/* Simple SSL client to get server certificates */
+
+#include "libbb.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+#define OPT_4 (1 << 0)
+#define OPT_6 (1 << 1)
+
+#define MSG_HANDSHAKE 22
+#define HS_CLIENT_HELLO 1
+#define HS_SERVER_HELLO 2
+#define HS_CERTIFICATE 11
+
+struct buf
+{
+ size_t offset;
+ size_t size;
+ size_t maxsize;
+ char *buf;
+ int fd;
+};
+
+#define BUF_CHUNK 4096
+
+struct msgbuf
+{
+ struct buf *inbuf;
+ struct buf *outbuf;
+
+ struct buf buffer;
+};
+
+struct hsbuf
+{
+ struct buf buffer;
+};
+
+#define URANDOM_DEV "/dev/urandom"
+
+#define DBQ(str) "\"" #str "\""
+
+static int tcp_fd= -1;
+
+static void fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "ssltestc30: ");
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+static void got_alarm(int sig __attribute__((unused)) )
+{
+ if (tcp_fd != -1)
+ fcntl(tcp_fd, F_SETFL, fcntl(tcp_fd, F_GETFL) | O_NONBLOCK);
+ alarm(1);
+}
+
+static void buf_init(struct buf *buf, int fd)
+{
+ buf->maxsize= 0;
+ buf->size= 0;
+ buf->offset= 0;
+ buf->buf= NULL;
+ buf->fd= fd;
+}
+
+static void buf_add(struct buf *buf, const void *data, size_t len)
+{
+ size_t maxsize;
+ void *newbuf;
+
+ if (buf->size+len <= buf->maxsize)
+ {
+ /* Easy case, just add data */
+ memcpy(buf->buf+buf->size, data, len);
+ buf->size += len;
+ return;
+ }
+
+ /* Just get a new buffer */
+ maxsize= buf->size-buf->offset + len + BUF_CHUNK;
+ newbuf= malloc(maxsize);
+ if (!newbuf)
+ {
+ fprintf(stderr, "unable to allocate %ld bytes\n", maxsize);
+ exit(1);
+ }
+
+ if (buf->offset < buf->size)
+ {
+ /* Copy existing data */
+ memcpy(newbuf, buf->buf+buf->offset, buf->size-buf->offset);
+ buf->size -= buf->offset;
+ buf->offset= 0;
+ }
+ else
+ {
+ buf->size= buf->offset= 0;
+ }
+ buf->maxsize= maxsize;
+ free(buf->buf);
+ buf->buf= newbuf;
+
+ memcpy(buf->buf+buf->size, data, len);
+ buf->size += len;
+}
+
+static void buf_add_b64(struct buf *buf, void *data, size_t len)
+{
+ char b64[]=
+ "ABCDEFGHIJKLMNOP"
+ "QRSTUVWXYZabcdef"
+ "ghijklmnopqrstuv"
+ "wxyz0123456789+/";
+ int i;
+ uint8_t *p;
+ uint32_t v;
+ char str[4];
+
+ p= data;
+
+ for (i= 0; i+3 <= len; i += 3, p += 3)
+ {
+ v= (p[0] << 16) + (p[1] << 8) + p[2];
+ str[0]= b64[(v >> 18) & 63];
+ str[1]= b64[(v >> 12) & 63];
+ str[2]= b64[(v >> 6) & 63];
+ str[3]= b64[(v >> 0) & 63];
+ buf_add(buf, str, 4);
+ if (i % 48 == 45)
+ buf_add(buf, "\n", 1);
+ }
+ switch(len-i)
+ {
+ case 0: break; /* Nothing to do */
+ case 1:
+ v= (p[0] << 16);
+ str[0]= b64[(v >> 18) & 63];
+ str[1]= b64[(v >> 12) & 63];
+ str[2]= '=';
+ str[3]= '=';
+ buf_add(buf, str, 4);
+ break;
+ case 2:
+ v= (p[0] << 16) + (p[1] << 8);
+ str[0]= b64[(v >> 18) & 63];
+ str[1]= b64[(v >> 12) & 63];
+ str[2]= b64[(v >> 6) & 63];
+ str[3]= '=';
+ buf_add(buf, str, 4);
+ break;
+ default:
+ fatal("bad state in buf_add_b64");
+ }
+}
+
+static int buf_read(struct buf *buf)
+{
+ int r;
+ size_t maxsize;
+ void *newbuf;
+
+ if (buf->size >= buf->maxsize)
+ {
+ if (buf->size-buf->offset + BUF_CHUNK <= buf->maxsize)
+ {
+ /* The buffer is big enough, just need to compact */
+ fatal("buf_read: should compact");
+ }
+ else
+ {
+ maxsize= buf->size-buf->offset + BUF_CHUNK;
+ newbuf= malloc(maxsize);
+ if (!newbuf)
+ fatal("unable to allocate %d bytes", maxsize);
+ buf->maxsize= maxsize;
+
+ if (buf->size > buf->offset)
+ {
+ memcpy(newbuf, buf->buf+buf->offset,
+ buf->size-buf->offset);
+ buf->size -= buf->offset;
+ buf->offset= 0;
+ }
+ else
+ {
+ buf->size= buf->offset= 0;
+ }
+ free(buf->buf);
+ buf->buf= newbuf;
+ }
+ }
+
+ for (;;)
+ {
+ r= read(buf->fd, buf->buf+buf->size, buf->maxsize-buf->size);
+ if (r > 0)
+ {
+ buf->size += r;
+ break;
+ }
+ fprintf(stderr, "read error on fd %d: %s",
+ buf->fd, r == 0 ? "eof" : strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int buf_write(struct buf *buf)
+{
+ int r;
+ size_t len;
+
+ while (buf->offset < buf->size)
+ {
+ len= buf->size - buf->offset;
+ r= write(buf->fd, buf->buf+buf->offset, len);
+ if (len > 0)
+ {
+ buf->offset += len;
+ continue;
+ }
+ fprintf(stderr, "write to %d failed: %s\n",
+ buf->fd, r == 0 ? "eof" : strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static void buf_cleanup(struct buf *buf)
+{
+ free(buf->buf);
+ buf->offset= buf->size= buf->maxsize= 0;
+}
+
+static void msgbuf_init(struct msgbuf *msgbuf,
+ struct buf *inbuf, struct buf *outbuf)
+{
+ buf_init(&msgbuf->buffer, -1);
+ msgbuf->inbuf= inbuf;
+ msgbuf->outbuf= outbuf;
+}
+
+static void msgbuf_add(struct msgbuf *msgbuf, void *buf, size_t size)
+{
+ buf_add(&msgbuf->buffer, buf, size);
+}
+
+static int Xmsgbuf_read(struct msgbuf *msgbuf, int type)
+{
+ int r;
+ size_t len;
+ uint8_t *p;
+
+ for(;;)
+ {
+ while (msgbuf->inbuf->size - msgbuf->inbuf->offset < 5)
+ {
+ r= buf_read(msgbuf->inbuf);
+ if (r < 0)
+ {
+ fprintf(stderr,
+ "msgbuf_read: buf_read failed\n");
+ return -1;
+ }
+ continue;
+ }
+ p= (uint8_t *)msgbuf->inbuf->buf+msgbuf->inbuf->offset;
+ type= p[0];
+ if (p[0] != type)
+ {
+ fprintf(stderr, "msgbuf_read: got type %d\n", p[0]);
+ return -1;
+ }
+ if (p[1] != 3 || p[2] != 0)
+ {
+ fprintf(stderr,
+ "msgbuf_read: got bad major/minor %d.%d\n",
+ p[1], p[2]);
+ return -1;
+ }
+ len= (p[3] << 8) + p[4];
+ if (msgbuf->inbuf->size - msgbuf->inbuf->offset < 5 + len)
+ {
+ r= buf_read(msgbuf->inbuf);
+ if (r < 0)
+ {
+ fprintf(stderr,
+ "msgbuf_read: buf_read failed\n");
+ return -1;
+ }
+ continue;
+ }
+
+ /* Move the data to the msg buffer */
+ msgbuf_add(msgbuf, msgbuf->inbuf->buf+msgbuf->inbuf->offset+5,
+ len);
+ msgbuf->inbuf->offset += 5+len;
+ break;
+ }
+ return 0;
+}
+
+static void msgbuf_final(struct msgbuf *msgbuf, int type)
+{
+ uint8_t c;
+ size_t len;
+
+ while (msgbuf->buffer.offset < msgbuf->buffer.size)
+ {
+ len= msgbuf->buffer.size-msgbuf->buffer.offset;
+ if (len > 0x4000)
+ len= 0x4000;
+
+ c= type;
+ buf_add(msgbuf->outbuf, &c, 1);
+
+ c= 3;
+ buf_add(msgbuf->outbuf, &c, 1);
+
+ c= 0;
+ buf_add(msgbuf->outbuf, &c, 1);
+
+ c= len >> 8;
+ buf_add(msgbuf->outbuf, &c, 1);
+
+ c= len;
+ buf_add(msgbuf->outbuf, &c, 1);
+
+ buf_add(msgbuf->outbuf,
+ msgbuf->buffer.buf + msgbuf->buffer.offset, len);
+
+ msgbuf->buffer.offset += len;
+ }
+}
+
+static void msgbuf_cleanup(struct msgbuf *msgbuf)
+{
+ buf_cleanup(&msgbuf->buffer);
+}
+
+static void hsbuf_init(struct hsbuf *hsbuf)
+{
+ buf_init(&hsbuf->buffer, -1);
+}
+
+static void hsbuf_add(struct hsbuf *hsbuf, const void *buf, size_t len)
+{
+ buf_add(&hsbuf->buffer, buf, len);
+}
+
+static void hsbuf_cleanup(struct hsbuf *hsbuf)
+{
+ buf_cleanup(&hsbuf->buffer);
+}
+
+static void hsbuf_final(struct hsbuf *hsbuf, int type, struct msgbuf *msgbuf)
+{
+ uint8_t c;
+ size_t len;
+
+ len= hsbuf->buffer.size - hsbuf->buffer.offset;
+
+ c= type;
+ msgbuf_add(msgbuf, &c, 1);
+
+ c= (len >> 16);
+ msgbuf_add(msgbuf, &c, 1);
+
+ c= (len >> 8);
+ msgbuf_add(msgbuf, &c, 1);
+
+ c= len;
+ msgbuf_add(msgbuf, &c, 1);
+
+ msgbuf_add(msgbuf, hsbuf->buffer.buf + hsbuf->buffer.offset, len);
+ hsbuf->buffer.offset += len;
+}
+
+static int connect_to_name(int af, const char *host, const char *port)
+{
+ int r, s, s_errno;
+ struct addrinfo *res, *aip;
+ struct addrinfo hints;
+
+ memset(&hints, '\0', sizeof(hints));
+ hints.ai_family= af;
+ hints.ai_socktype= SOCK_STREAM;
+ r= getaddrinfo(host, port, &hints, &res);
+ if (r != 0)
+ {
+ fprintf(stderr,
+ "unable to resolve '%s': %s", host, gai_strerror(r));
+ return -1;
+ }
+
+ s_errno= 0;
+ s= -1;
+ for (aip= res; aip != NULL; aip= aip->ai_next)
+ {
+ s= socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s == -1)
+ {
+ s_errno= errno;
+ continue;
+ }
+
+ if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
+ break;
+
+ s_errno= errno;
+ close(s);
+ s= -1;
+ }
+
+ freeaddrinfo(res);
+ if (s == -1)
+ errno= s_errno;
+ return s;
+}
+
+static void add_random(struct hsbuf *hsbuf)
+{
+ int fd;
+ time_t t;
+ uint8_t buf[32];
+
+ t= time(NULL);
+ buf[0]= t >> 24;
+ buf[1]= t >> 16;
+ buf[2]= t >> 8;
+ buf[3]= t;
+
+ fd= open(URANDOM_DEV, O_RDONLY);
+
+ /* Best effort, just ignore errors */
+ if (fd != -1)
+ {
+ read(fd, buf+4, sizeof(buf)-4);
+ close(fd);
+ }
+ hsbuf_add(hsbuf, buf, sizeof(buf));
+}
+
+static void add_sessionid(struct hsbuf *hsbuf)
+{
+ uint8_t c;
+
+ c= 0;
+ hsbuf_add(hsbuf, &c, 1);
+}
+
+static void add_ciphers(struct hsbuf *hsbuf)
+{
+ uint8_t c;
+ size_t len;
+ uint8_t ciphers[]= { 0x0,0xff, 0x0,0x88, 0x0,0x87, 0x0,0x39, 0x0,0x38,
+ 0x0,0x84, 0x0,0x35, 0x0,0x45, 0x0,0x44, 0x0,0x33, 0x0,0x32,
+ 0x0,0x96, 0x0,0x41, 0x0,0x4, 0x0,0x5, 0x0,0x2f, 0x0,0x16,
+ 0x0,0x13, 0xfe,0xff, 0x0,0xa };
+
+ len= sizeof(ciphers);
+
+ c= len >> 8;
+ hsbuf_add(hsbuf, &c, 1);
+ c= len;
+ hsbuf_add(hsbuf, &c, 1);
+
+ hsbuf_add(hsbuf, ciphers, len);
+}
+
+static void add_compression(struct hsbuf *hsbuf)
+{
+ uint8_t c;
+ size_t len;
+ uint8_t compression[]= { 0x1, 0x0 };
+
+ len= sizeof(compression);
+
+ c= len;
+ hsbuf_add(hsbuf, &c, 1);
+
+ hsbuf_add(hsbuf, compression, len);
+}
+
+static int Xeat_server_hello(struct msgbuf *msgbuf)
+{
+ int r;
+ size_t len;
+ uint8_t *p;
+
+ for (;;)
+ {
+ if (msgbuf->buffer.size - msgbuf->buffer.offset < 4)
+ {
+ r= Xmsgbuf_read(msgbuf, MSG_HANDSHAKE);
+ if (r < 0)
+ {
+ fprintf(stderr,
+ "eat_server_hello: msgbuf_read failed\n");
+ return -1;
+
+ }
+ continue;
+ }
+ p= (uint8_t *)msgbuf->buffer.buf+msgbuf->buffer.offset;
+ if (p[0] != HS_SERVER_HELLO)
+ {
+ fprintf(stderr, "eat_server_hello: got type %d\n",
+ p[0]);
+ return -1;
+ }
+ len= (p[1] << 16) + (p[2] << 8) + p[3];
+ if (msgbuf->buffer.size - msgbuf->buffer.offset < 4+len)
+ {
+ r= Xmsgbuf_read(msgbuf, MSG_HANDSHAKE);
+ if (r < 0)
+ {
+ fprintf(stderr,
+ "eat_server_hello: msgbuf_read failed\n");
+ return -1;
+ }
+ continue;
+ }
+ msgbuf->buffer.offset += 4+len;
+ break;
+ }
+ return 0;
+}
+
+static int Xeat_certificate(struct msgbuf *msgbuf)
+{
+ int i, n, r, first, slen;
+ size_t o, len;
+ uint8_t *p;
+ struct buf tmpbuf;
+
+ for (;;)
+ {
+ if (msgbuf->buffer.size - msgbuf->buffer.offset < 4)
+ {
+ r= Xmsgbuf_read(msgbuf, MSG_HANDSHAKE);
+ if (r < 0)
+ {
+ fprintf(stderr,
+ "eat_certificate: msgbuf_read failed\n");
+ return -1;
+ }
+ continue;
+ }
+ p= (uint8_t *)msgbuf->buffer.buf+msgbuf->buffer.offset;
+ if (p[0] != HS_CERTIFICATE)
+ {
+ fprintf(stderr, "eat_certificate: got type %d\n", p[0]);
+ return -1;
+ }
+ len= (p[1] << 16) + (p[2] << 8) + p[3];
+ if (msgbuf->buffer.size - msgbuf->buffer.offset < 4+len)
+ {
+ r= Xmsgbuf_read(msgbuf, MSG_HANDSHAKE);
+ if (r < 0)
+ {
+ fprintf(stderr,
+ "eat_certificate: msgbuf_read failed\n");
+ return -1;
+ }
+ continue;
+ }
+ p += 4;
+ n= (p[0] << 16) + (p[1] << 8) + p[2];
+ o= 3;
+ first= 1;
+ printf(", " DBQ(cert) ":[ ");
+
+ buf_init(&tmpbuf, -1);
+ while (o < 3+n)
+ {
+ slen= (p[o] << 16) + (p[o+1] << 8) + p[o+2];
+ buf_add_b64(&tmpbuf, p+o+3, slen);
+ printf("%s\"-----BEGIN CERTIFICATE-----\\n",
+ !first ? ", " : "");
+ for (i= tmpbuf.offset; i<tmpbuf.size; i++)
+ {
+ if (tmpbuf.buf[i] == '\n')
+ fputs("\\n", stdout);
+ else
+ putchar(tmpbuf.buf[i]);
+ }
+ printf("\\n-----END CERTIFICATE-----\"");
+ tmpbuf.size= tmpbuf.offset;
+ o += 3+slen;
+ first= 0;
+ }
+ buf_cleanup(&tmpbuf);
+ printf(" ]");
+ if (o != 3+n)
+ {
+ fprintf(stderr,
+ "do_certificate: bad amount of cert data\n");
+ return -1;
+ }
+ if (o != len)
+ {
+ fprintf(stderr,
+ "do_certificate: bad amount of cert data\n");
+ return -1;
+ }
+ msgbuf->buffer.offset += 4+len;
+ break;
+ }
+
+ return 0;
+}
+
+extern int get_atlas_fw_version(void); /* In eperd */
+
+int sslgetcert_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int sslgetcert_main(int argc UNUSED_PARAM, char **argv)
+{
+ int r, af;
+ uint32_t opt;
+ socklen_t salen;
+ char *str_Atlas;
+ const char *str_port;
+ char *hostname;
+ struct buf inbuf, outbuf;
+ struct msgbuf msginbuf, msgoutbuf;
+ struct hsbuf hsbuf;
+ struct sockaddr_storage sa;
+ struct sigaction sia;
+ char hostbuf[NI_MAXHOST];
+
+ str_Atlas= NULL;
+ str_port= "https";
+
+ opt_complementary = "=1";
+ opt = getopt32(argv, "!46A:p:", &str_Atlas, &str_port);
+ if (opt == (uint32_t)-1)
+ return 1;
+ hostname = argv[optind];
+
+ af= AF_UNSPEC;
+ if (opt & OPT_4)
+ af= AF_INET;
+ if (opt & OPT_6)
+ af= AF_INET6;
+
+ sia.sa_flags= 0;
+ sia.sa_handler= got_alarm;
+ sigemptyset(&sia.sa_mask);
+ sigaction(SIGALRM, &sia, NULL);
+ alarm(10);
+ signal(SIGPIPE, SIG_IGN);
+
+ tcp_fd= connect_to_name(af, hostname, str_port);
+
+ printf("RESULT { ");
+ if (str_Atlas)
+ {
+ printf(DBQ(id) ":" DBQ(%s)
+ ", " DBQ(fw) ":%d",
+ str_Atlas, get_atlas_fw_version());
+ }
+
+ printf("%s" DBQ(time) ":%ld", str_Atlas ? ", " : "", time(NULL));
+ printf(", " DBQ(dst_name) ":" DBQ(%s) ", " DBQ(dst_port) ":" DBQ(%s),
+ hostname, str_port);
+
+ printf(", " DBQ(method) ":" DBQ(SSL) ", " DBQ(ver) ":" DBQ(3.0));
+ if (af != AF_UNSPEC)
+ printf(", " DBQ(af) ": %d", af == AF_INET6 ? 6 : 4);
+
+ if (tcp_fd == -1)
+ {
+ printf(", " DBQ(err) ":" DBQ(unable to connect) " }\n");
+ return 0;
+ }
+
+ salen= sizeof(sa);
+ if (getpeername(tcp_fd, (struct sockaddr *)&sa, &salen) != -1)
+ {
+ getnameinfo((struct sockaddr *)&sa, salen,
+ hostbuf, sizeof(hostbuf), NULL, 0,
+ NI_NUMERICHOST);
+ printf(", " DBQ(dst_addr) ":" DBQ(%s), hostbuf);
+ if (af == AF_UNSPEC)
+ {
+ printf(", " DBQ(af) ": %d",
+ sa.ss_family == AF_INET6 ? 6 : 4);
+ }
+ }
+ salen= sizeof(sa);
+ if (getsockname(tcp_fd, (struct sockaddr *)&sa, &salen) != -1)
+ {
+ getnameinfo((struct sockaddr *)&sa, salen,
+ hostbuf, sizeof(hostbuf), NULL, 0,
+ NI_NUMERICHOST);
+ printf(", " DBQ(src_addr) ":" DBQ(%s), hostbuf);
+ }
+
+ buf_init(&outbuf, tcp_fd);
+ msgbuf_init(&msgoutbuf, NULL, &outbuf);
+ hsbuf_init(&hsbuf);
+
+ /* Major/minor */
+ hsbuf_add(&hsbuf, "\3", 1);
+ hsbuf_add(&hsbuf, "\0", 1);
+ add_random(&hsbuf);
+ add_sessionid(&hsbuf);
+ add_ciphers(&hsbuf);
+ add_compression(&hsbuf);
+
+ hsbuf_final(&hsbuf, HS_CLIENT_HELLO, &msgoutbuf);
+ msgbuf_final(&msgoutbuf, MSG_HANDSHAKE);
+ r= buf_write(&outbuf);
+ if (r == -1)
+ goto fail;
+
+ buf_init(&inbuf, tcp_fd);
+ msgbuf_init(&msginbuf, &inbuf, NULL);
+
+ if (Xeat_server_hello(&msginbuf) < 0)
+ goto fail;
+
+ if (Xeat_certificate(&msginbuf) < 0)
+ goto fail;
+
+fail:
+ printf(" }\n");
+
+ close(tcp_fd);
+ tcp_fd= -1;
+
+ hsbuf_cleanup(&hsbuf);
+ msgbuf_cleanup(&msginbuf);
+ msgbuf_cleanup(&msgoutbuf);
+ buf_cleanup(&inbuf);
+ buf_cleanup(&outbuf);
+
+ alarm(0);
+ signal(SIGPIPE, SIG_DFL);
+
+ return 0;
+}
diff --git a/networking/telnetd.c b/networking/telnetd.c
index 46dfb31..1855f1b 100644
--- a/networking/telnetd.c
+++ b/networking/telnetd.c
@@ -32,12 +32,65 @@
#endif
#include <arpa/telnet.h>
+#define ATLAS 1
+
+#ifdef ATLAS
+#include <string.h>
+#include <unistd.h>
+#include <linux/reboot.h>
+
+#define LOGIN_PREFIX "Atlas probe, see http://atlas.ripe.net/\r\n\r\n"
+#define LOGIN_PROMPT " login: "
+#define PASSWORD_PROMPT "\r\nPassword: "
+
+#define ATLAS_LOGIN "C_TO_P_TEST_V1"
+#define ATLAS_SESSION_FILE "/home/atlas/status/con_session_id.txt"
+#define SESSION_ID_PREFIX "SESSION_ID "
+
+#define CMD_CRONTAB "CRONTAB "
+#define CMD_CRONLINE "CRONLINE "
+#define CMD_ONEOFF "ONEOFF "
+#define CMD_REBOOT "REBOOT"
+
+#define CRLF "\r\n"
+#define RESULT_OK "OK" CRLF CRLF
+#define BAD_PASSWORD "BAD_PASSWORD" CRLF CRLF
+#define BAD_COMMAND "BAD_COMMAND" CRLF CRLF
+#define NAME_TOO_LONG "NAME_TOO_LONG" CRLF CRLF
+#define CRONTAB_BUSY "CRONTAB_BUSY" CRLF CRLF
+#define CREATE_FAILED "UNABLE_TO_CREATE_NEW_CRONTAB" CRLF CRLF
+#define IO_ERROR "IO_ERROR" CRLF CRLF
+#define BAD_PATH "BAD_PATH" CRLF CRLF
+
+#define CRONUSER "root"
+#define CRONTAB_NEW_SUF "/" CRONUSER ".new"
+#define CRONTAB_SUFFIX "/" CRONUSER
+#define CRONUPDATE "/cron.update"
+#define UPDATELINE CRONUSER "\n"
+#define ONEOFF_SUFFIX ".new"
+#define SAFE_PREFIX ATLAS_CRONS
+
+enum state
+{
+ DO_TRADITIONAL,
+ GET_LOGINNAME,
+ GET_PASSWORD,
+ GET_CMD,
+ DO_CRONTAB,
+ EOM_SEEN
+};
+#endif
+
/* Structure that describes a session */
struct tsession {
struct tsession *next;
int sockfd_read, sockfd_write, ptyfd;
int shell_pid;
+#ifdef ATLAS
+ enum state state;
+#endif
+
/* two circular buffers */
/*char *buf1, *buf2;*/
/*#define TS_BUF1 ts->buf1*/
@@ -52,6 +105,19 @@ struct tsession {
* Make whole thing fit in 4k */
enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 };
+#ifdef ATLAS
+static int equal_sessionid(char *passwd);
+static void add_2sock(struct tsession *ts, const char *str);
+static void pack_4sock(void);
+static char *getline_2pty(struct tsession *ts);
+static void pack_2pty(struct tsession *ts);
+static int start_crontab(struct tsession *ts, char *line);
+static void add_to_crontab(struct tsession *ts, char *line);
+static void end_crontab(struct tsession *ts);
+static void do_oneoff(struct tsession *ts, char *line);
+static int get_probe_id(void);
+int validate_filename(const char *path, const char *prefix);
+#endif
/* Globals */
static int maxfd;
@@ -59,6 +125,16 @@ static struct tsession *sessions;
static const char *loginpath = "/bin/login";
static const char *issuefile = "/etc/issue.net";
+#ifdef ATLAS
+/* Place to store the file handle and directory name for a new crontab */
+static FILE *atlas_crontab;
+static char atlas_dirname[256];
+static struct tsession *atlas_ts; /* Allow only one 'atlas' connection
+ * at a time. The old one
+ * self-destructs when a new one is
+ * started.
+ */
+#endif
/*
Remove all IAC's from buf1 (received IACs are ignored and must be removed
@@ -165,21 +241,32 @@ make_new_session(
USE_FEATURE_TELNETD_STANDALONE(int sock)
SKIP_FEATURE_TELNETD_STANDALONE(void)
) {
+#ifndef ATLAS
const char *login_argv[2];
struct termios termbuf;
int fd, pid;
char tty_name[GETPTY_BUFSIZE];
+#endif
struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
+#ifdef ATLAS
+ ts->state= GET_LOGINNAME;
+#endif
+
/*ts->buf1 = (char *)(ts + 1);*/
/*ts->buf2 = ts->buf1 + BUFSIZE;*/
+#ifdef ATLAS
+ ts->ptyfd= 0;
+#else
/* Got a new connection, set up a tty. */
fd = xgetpty(tty_name);
if (fd > maxfd)
maxfd = fd;
ts->ptyfd = fd;
ndelay_on(fd);
+#endif /* ATLAS */
+
#if ENABLE_FEATURE_TELNETD_STANDALONE
ts->sockfd_read = sock;
/* SO_KEEPALIVE by popular demand */
@@ -217,6 +304,25 @@ make_new_session(
ts->size2 = sizeof(iacs_to_send);
}
+#ifdef ATLAS /* Split original function into two */
+ return ts;
+}
+
+static int start_login(struct tsession *ts, char *user)
+{
+ int fd, pid;
+ const char *login_argv[3];
+ struct termios termbuf;
+ char tty_name[GETPTY_BUFSIZE];
+
+ /* Got a new connection, set up a tty. */
+ fd = xgetpty(tty_name);
+ if (fd > maxfd)
+ maxfd = fd;
+ ts->ptyfd = fd;
+ ndelay_on(fd);
+#endif /* ATLAS */
+
fflush(NULL); /* flush all streams */
pid = vfork(); /* NOMMU-friendly */
if (pid < 0) {
@@ -224,12 +330,20 @@ make_new_session(
close(fd);
/* sock will be closed by caller */
bb_perror_msg("vfork");
+#ifdef ATLAS
+ return -1;
+#else
return NULL;
+#endif
}
if (pid > 0) {
/* Parent */
ts->shell_pid = pid;
+#ifdef ATLAS
+ return 0;
+#else
return ts;
+#endif
}
/* Child */
@@ -270,7 +384,12 @@ make_new_session(
/* Exec shell / login / whatever */
login_argv[0] = loginpath;
+#ifdef ATLAS
+ login_argv[1] = user;
+ login_argv[2] = NULL;
+#else
login_argv[1] = NULL;
+#endif
/* exec busybox applet (if PREFER_APPLETS=y), if that fails,
* exec external program */
BB_EXECVP(loginpath, (char **)login_argv);
@@ -281,10 +400,14 @@ make_new_session(
/* Must match getopt32 string */
enum {
+ /* (1 << 0) -f */
+ /* (1 << 1) -l */
OPT_WATCHCHILD = (1 << 2), /* -K */
OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
- OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p */
- OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
+ /* (1 << 4) -P */
+ OPT_PORT = (1 << 5) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p */
+ /* (1 << 6) -b */
+ OPT_FOREGROUND = (1 << 7) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
};
#if ENABLE_FEATURE_TELNETD_STANDALONE
@@ -313,7 +436,12 @@ free_session(struct tsession *ts)
kill(ts->shell_pid, SIGKILL);
wait4(ts->shell_pid, NULL, 0, NULL);
#endif
+#ifdef ATLAS
+ if (ts->ptyfd != 0)
+ close(ts->ptyfd);
+#else
close(ts->ptyfd);
+#endif
close(ts->sockfd_read);
/* We do not need to close(ts->sockfd_write), it's the same
* as sockfd_read unless we are in inetd mode. But in inetd mode
@@ -365,19 +493,33 @@ static void handle_sigchld(int sig UNUSED_PARAM)
}
}
+static void kick_watchdog(void)
+{
+ int fdwatchdog = open("/dev/watchdog", O_RDWR);
+ if (fdwatchdog != -1)
+ {
+ write(fdwatchdog, "1", 1);
+ close(fdwatchdog);
+ }
+}
+
int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int telnetd_main(int argc UNUSED_PARAM, char **argv)
{
fd_set rdfdset, wrfdset;
unsigned opt;
- int count;
+ int count, probe_id;
+ int num_totty;
struct tsession *ts;
+ char *line;
#if ENABLE_FEATURE_TELNETD_STANDALONE
#define IS_INETD (opt & OPT_INETD)
int master_fd = master_fd; /* be happy, gcc */
unsigned portnbr = 23;
char *opt_bindaddr = NULL;
char *opt_portnbr;
+ const char *PidFileName = NULL;
+ char buf[80];
#else
enum {
IS_INETD = 1,
@@ -385,10 +527,12 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
portnbr = 23,
};
#endif
+ struct timeval tv;
+
/* Even if !STANDALONE, we accept (and ignore) -i, thus people
* don't need to guess whether it's ok to pass -i to us */
- opt = getopt32(argv, "f:l:Ki" USE_FEATURE_TELNETD_STANDALONE("p:b:F"),
- &issuefile, &loginpath
+ opt = getopt32(argv, "f:l:KiP:" USE_FEATURE_TELNETD_STANDALONE("p:b:F"),
+ &issuefile, &loginpath, &PidFileName
USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
if (!IS_INETD /*&& !re_execed*/) {
/* inform that we start in standalone mode?
@@ -410,6 +554,11 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
portnbr = xatou16(opt_portnbr);
);
+ if(PidFileName)
+ {
+ write_pidfile(PidFileName);
+ }
+
/* Used to check access(loginpath, X_OK) here. Pointless.
* exec will do this for us for free later. */
@@ -458,6 +607,8 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
FD_ZERO(&rdfdset);
FD_ZERO(&wrfdset);
+ kick_watchdog();
+
/* Select on the master socket, all telnet sockets and their
* ptys if there is room in their session buffers.
* NB: scalability problem: we recalculate entire bitmap
@@ -469,13 +620,24 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
/* Child died and we detected that */
free_session(ts);
} else {
+#ifdef ATLAS
+ if (ts->size1 > 0 && ts->state == DO_TRADITIONAL)
+ /* can write to pty */
+#else
if (ts->size1 > 0) /* can write to pty */
+#endif
FD_SET(ts->ptyfd, &wrfdset);
if (ts->size1 < BUFSIZE) /* can read from socket */
FD_SET(ts->sockfd_read, &rdfdset);
if (ts->size2 > 0) /* can write to socket */
FD_SET(ts->sockfd_write, &wrfdset);
+#ifdef ATLAS
+ if (ts->size2 < BUFSIZE &&
+ ts->state == DO_TRADITIONAL)
+ /* can read from pty */
+#else
if (ts->size2 < BUFSIZE) /* can read from pty */
+#endif
FD_SET(ts->ptyfd, &rdfdset);
}
ts = next;
@@ -489,7 +651,9 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
maxfd = master_fd;
}
- count = select(maxfd + 1, &rdfdset, &wrfdset, NULL, NULL);
+ tv.tv_sec= 10;
+ tv.tv_usec= 0;
+ count = select(maxfd + 1, &rdfdset, &wrfdset, NULL, &tv);
if (count < 0)
goto again; /* EINTR or ENOMEM */
@@ -505,6 +669,18 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
/* Create a new session and link it into our active list */
new_ts = make_new_session(fd);
if (new_ts) {
+#ifdef ATLAS
+ char *hostname;
+
+ hostname= safe_gethostname();
+ probe_id= get_probe_id();
+ add_2sock(new_ts, LOGIN_PREFIX);
+ snprintf(buf, sizeof(buf), "Probe %d (%s)",
+ probe_id, hostname);
+ add_2sock(new_ts, buf);
+ add_2sock(new_ts, LOGIN_PROMPT);
+ free(hostname);
+#endif /* ATLAS */
new_ts->next = sessions;
sessions = new_ts;
} else {
@@ -519,7 +695,6 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
struct tsession *next = ts->next; /* in case we free ts. */
if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
- int num_totty;
unsigned char *ptr;
/* Write to pty from buffer 1. */
ptr = remove_iacs(ts, &num_totty);
@@ -565,10 +740,22 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
ts->wridx2 = 0;
}
- if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
+ if (/*ts->size1 < BUFSIZE &&*/
+ FD_ISSET(ts->sockfd_read, &rdfdset)) {
+#ifdef ATLAS
+ if (ts->size1 < BUFSIZE &&
+ (ts->rdidx1 >= BUFSIZE ||
+ ts->rdidx1 < ts->wridx1))
+ {
+ pack_2pty(ts);
+ }
+#endif
+
/* Read from socket to buffer 1. */
count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
- count = safe_read(ts->sockfd_read, TS_BUF1 + ts->rdidx1, count);
+
+ count = safe_read(ts->sockfd_read,
+ TS_BUF1 + ts->rdidx1, count);
if (count <= 0) {
if (count < 0 && errno == EAGAIN)
goto skip3;
@@ -584,6 +771,208 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
ts->rdidx1 = 0;
}
skip3:
+#ifdef ATLAS
+ switch(ts->state)
+ {
+ case DO_TRADITIONAL:
+ break; /* Nothing to do */
+ case GET_LOGINNAME:
+ {
+ unsigned char *ptr;
+
+ ptr = remove_iacs(ts, &num_totty);
+
+ line= getline_2pty(ts);
+ if (!line)
+ goto skip3a;
+
+ if (strcmp(line, ATLAS_LOGIN) == 0)
+ {
+ free(line); line= NULL;
+ add_2sock(ts, PASSWORD_PROMPT);
+ ts->state= GET_PASSWORD;
+ goto skip3;
+ }
+ else
+ {
+ int r;
+
+ /* Echo login name */
+ add_2sock(ts, line);
+
+ r= start_login(ts, line);
+ free(line); line= NULL;
+ if (r == -1)
+ goto kill_session;
+ ts->state= DO_TRADITIONAL;
+ goto skip3a;
+ }
+
+ }
+ case GET_PASSWORD:
+ remove_iacs(ts, &num_totty);
+
+ line= getline_2pty(ts);
+ if (!line)
+ goto skip3a;
+
+ if (equal_sessionid(line))
+ {
+ free(line); line= NULL;
+
+ if (atlas_ts)
+ {
+ bb_error_msg("found atlas session");
+ /* There is an old session still
+ * around. Take over.
+ */
+ if (atlas_crontab)
+ {
+ fclose(atlas_crontab);
+ atlas_crontab= NULL;
+ }
+ }
+ atlas_ts= ts;
+
+ ts->state= GET_CMD;
+ goto skip3;
+ }
+ else
+ {
+ free(line); line= NULL;
+
+ /* Bad password, the end */
+ add_2sock(ts, BAD_PASSWORD);
+ goto skip3;
+ }
+
+ case GET_CMD:
+ {
+ int r;
+ size_t len;
+
+ if (ts != atlas_ts)
+ goto kill_session; /* Old session */
+
+ remove_iacs(ts, &num_totty);
+
+ line= getline_2pty(ts);
+ if (!line)
+ goto skip3a;
+
+do_cmd:
+ len= strlen(CMD_CRONTAB);
+ if (strncmp(line, CMD_CRONTAB, len) == 0)
+ {
+ r= start_crontab(ts, line);
+ free(line); line= NULL;
+ if (r == -1)
+ {
+ /* Assume start_crontab sent an
+ * error response.
+ */
+ goto skip3;
+ }
+
+ ts->state= DO_CRONTAB;
+ goto skip3;
+ }
+
+ len= strlen(CMD_ONEOFF);
+ if (strncmp(line, CMD_ONEOFF, len) == 0)
+ {
+ do_oneoff(ts, line);
+ free(line); line= NULL;
+
+ /* Assume do_oneoff sent an error response
+ * if something was wrong.
+ */
+ goto skip3;
+ }
+ if (strcmp(line, CMD_REBOOT) == 0)
+ {
+ sync();
+ reboot(LINUX_REBOOT_CMD_RESTART);
+ free(line); line= NULL;
+
+ goto skip3;
+ }
+ if (strlen(line) == 0)
+ {
+ free(line); line= NULL;
+
+ /* End of request */
+ add_2sock(ts, RESULT_OK);
+
+ ts->state= EOM_SEEN;
+ goto skip3;
+ }
+
+ free(line); line= NULL;
+
+ /* Bad command */
+ add_2sock(ts, BAD_COMMAND);
+ goto skip3a;
+ }
+
+ case DO_CRONTAB:
+ {
+ size_t len;
+
+ if (ts != atlas_ts)
+ goto kill_session; /* Old session */
+
+ remove_iacs(ts, &num_totty);
+
+ line= getline_2pty(ts);
+ if (!line)
+ goto skip3a;
+
+ len= strlen(CMD_CRONLINE);
+ if (strncmp(line, CMD_CRONLINE, len) != 0)
+ {
+ end_crontab(ts);
+
+ /* Assume end_crontab sends a response
+ * if there was an error.
+ */
+
+ ts->state= GET_CMD;
+
+ /* Unfortunately, the line that ends the
+ * crontab is the next command.
+ */
+ goto do_cmd;
+ }
+
+ add_to_crontab(ts, line+len);
+ free(line); line= NULL;
+
+ /* And again */
+ goto skip3;
+ }
+
+ case EOM_SEEN:
+ if (ts != atlas_ts)
+ goto kill_session; /* Old session */
+
+ /* Just eat all input and return bad command */
+ remove_iacs(ts, &num_totty);
+
+ line= getline_2pty(ts);
+ if (!line)
+ goto skip3a;
+
+ free(line); line= NULL;
+ add_2sock(ts, BAD_COMMAND);
+ goto skip3;
+
+ default:
+ bb_error_msg("unknown state %d", ts->state);
+ abort();
+ }
+skip3a:
+#endif /* ATLAS */
if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
/* Read from pty to buffer 2. */
count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
@@ -602,9 +991,407 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
ts = next;
continue;
kill_session:
+#ifdef ATLAS
+ if (ts == atlas_ts)
+ {
+ if (atlas_crontab)
+ {
+ fclose(atlas_crontab);
+ atlas_crontab= NULL;
+ }
+ atlas_ts= NULL;
+ }
+#endif /* ATLAS */
free_session(ts);
ts = next;
}
goto again;
}
+
+#ifdef ATLAS
+static int equal_sessionid(char *passwd)
+{
+ size_t len;
+ char *cp;
+ FILE *file;
+ char line[80];
+
+ file= fopen(ATLAS_SESSION_FILE, "r");
+ if (file == NULL)
+ {
+ syslog(LOG_ERR, "unable to open '%s': %m", ATLAS_SESSION_FILE);
+ return 0;
+ }
+
+ if (fgets(line, sizeof(line), file) == NULL)
+ {
+ syslog(LOG_ERR, "unable to read from '%s': %m",
+ ATLAS_SESSION_FILE);
+ fclose(file);
+ return 0;
+ }
+ fclose(file);
+
+ len= strlen(SESSION_ID_PREFIX);
+ if (strlen(line) < len)
+ {
+ syslog(LOG_ERR, "not enough session ID data");
+ return 0;
+ }
+ if (memcmp(line, SESSION_ID_PREFIX, len) != 0)
+ {
+ syslog(LOG_ERR, "missing session ID prefix");
+ return 0;
+ }
+
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ syslog(LOG_ERR, "missing newline in session ID file");
+ return 0;
+ }
+ *cp= '\0';
+
+ if (strcmp(line+len, passwd) == 0)
+ return 1;
+
+ /* Wrong password */
+ return 0;
+}
+
+static void add_2sock(struct tsession *ts, const char *str)
+{
+ size_t len;
+
+ len= strlen(str);
+ if (ts->size2 + len > BUFSIZE)
+ {
+ syslog(LOG_ERR, "add_2sock: buffer full");
+ abort();
+ }
+ if (ts->rdidx2 + len > BUFSIZE)
+ pack_4sock();
+
+ memcpy(TS_BUF2+ts->rdidx2, str, len);
+ ts->rdidx2 += len;
+ ts->size2 += len;
+}
+
+static void pack_4sock(void)
+{
+ syslog(LOG_ERR, "pack_4sock: not implemented");
+ abort();
+}
+
+static char *getline_2pty(struct tsession *ts)
+{
+ size_t size1, len;
+ char *cp, *cp2, *line;
+
+ size1= ts->size1;
+
+
+ if (ts->wridx1 + size1 > BUFSIZE)
+ pack_2pty(ts);
+
+ /* remove_iacs converts a CR-LF to a CR */
+ cp= memchr(TS_BUF1+ts->wridx1, '\r', size1);
+ cp2= memchr(TS_BUF1+ts->wridx1, '\n', size1);
+ if (cp2 != NULL && (cp == NULL || cp2 < cp))
+ {
+ /* Use the LF. Patch '\n' to '\r' */
+ *cp2= '\r';
+ cp= cp2;
+ }
+ if (cp == NULL)
+ return NULL;
+
+ len= cp-((char *)TS_BUF1+ts->wridx1)+1;
+ line= xmalloc(len+1);
+ memcpy(line, (char *)TS_BUF1+ts->wridx1, len);
+ line[len]= '\0';
+
+ ts->wridx1 += len;
+ ts->size1 -= len;
+
+ /* Make sure that the line ends in a \r. If not, just ignore the
+ * line. Otherwise, delete the \r.
+ */
+ cp= strchr(line, '\r');
+ if (cp == NULL || cp-line != strlen(line)-1)
+ {
+ bb_error_msg("bad line '%s', cp %p, cp-line %ld, |line| %ld",
+ line, cp, (long)(cp-line), (long)strlen(line));
+
+ /* Bad line, just ignore it */
+ free(line); line= NULL;
+ return NULL;
+ }
+ *cp= '\0';
+
+ return line;
+}
+
+static void pack_2pty(struct tsession *ts)
+{
+ size_t size1, size_lo, size_hi, wridx1;
+
+ size1= ts->size1;
+ wridx1= ts->wridx1;
+
+ size_hi= BUFSIZE-wridx1; /* Amount at the top of the buffer */
+ size_lo= size1-size_hi;
+
+ /* Move the low part up a bit */
+ memmove(TS_BUF1+size_hi, TS_BUF1, size_lo);
+
+ /* Now move the high part down */
+ memmove(TS_BUF1, TS_BUF1+wridx1, size_hi);
+
+ /* Update wridx1 and rdidx1 */
+ ts->wridx1= 0;
+ ts->rdidx1= size1;
+}
+
+static int start_crontab(struct tsession *ts, char *line)
+{
+ size_t len;
+ char *cp;
+ char filename[256];
+
+ if (atlas_crontab)
+ {
+ add_2sock(ts, CRONTAB_BUSY);
+ return -1;
+ }
+
+ cp= line+strlen(CMD_CRONTAB);
+ len= strlen(cp);
+ if (len+1 > sizeof(atlas_dirname))
+ {
+ add_2sock(ts, NAME_TOO_LONG);
+ return -1;
+ }
+ strlcpy(atlas_dirname, cp, sizeof(atlas_dirname));
+
+ if (len + strlen(CRONTAB_NEW_SUF) + 1 > sizeof(filename))
+ {
+ add_2sock(ts, NAME_TOO_LONG);
+ return -1;
+ }
+
+ strlcpy(filename, atlas_dirname, sizeof(filename));
+ strlcat(filename, CRONTAB_NEW_SUF, sizeof(filename));
+
+ if (!validate_filename(filename, SAFE_PREFIX))
+ {
+ add_2sock(ts, BAD_PATH);
+ return -1;
+ }
+
+ atlas_crontab= fopen(filename, "w");
+ if (!atlas_crontab)
+ {
+ add_2sock(ts, CREATE_FAILED);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void add_to_crontab(struct tsession *ts, char *line)
+{
+ if (!atlas_crontab)
+ return; /* Some error occured earlier */
+ // fprintf(stderr, "telnetd: adding '%s' to crontab\n", line);
+ if (fputs(line, atlas_crontab) == -1 ||
+ fputc('\n', atlas_crontab) == -1)
+ {
+ add_2sock(ts, IO_ERROR);
+ fclose(atlas_crontab);
+ atlas_crontab= NULL;
+ return;
+ }
+}
+
+static void end_crontab(struct tsession *ts)
+{
+ int fd;
+ size_t len;
+ struct stat st;
+ char filename1[256];
+ char filename2[256];
+
+ if (!atlas_crontab)
+ return; /* Some error occured earlier */
+ if (fclose(atlas_crontab) == -1)
+ {
+ atlas_crontab= NULL;
+ add_2sock(ts, IO_ERROR);
+ return;
+ }
+ atlas_crontab= NULL;
+
+ /* Rename */
+ len= strlen(atlas_dirname);
+ if (len + strlen(CRONTAB_NEW_SUF) + 1 > sizeof(filename1))
+ {
+ add_2sock(ts, NAME_TOO_LONG);
+ return;
+ }
+ strlcpy(filename1, atlas_dirname, sizeof(filename1));
+ strlcat(filename1, CRONTAB_NEW_SUF, sizeof(filename1));
+ if (len + strlen(CRONTAB_SUFFIX) + 1 > sizeof(filename2))
+ {
+ add_2sock(ts, NAME_TOO_LONG);
+ return;
+ }
+ strlcpy(filename2, atlas_dirname, sizeof(filename2));
+ strlcat(filename2, CRONTAB_SUFFIX, sizeof(filename2));
+ if (rename(filename1, filename2) == -1)
+ {
+ add_2sock(ts, IO_ERROR);
+ return;
+ }
+
+ /* Inspired by the crontab command, tell cron to load the new
+ * crontab.
+ */
+ if (strlen(atlas_dirname) + strlen(CRONUPDATE) + 1 > sizeof(filename1))
+ {
+ add_2sock(ts, NAME_TOO_LONG);
+ return;
+ }
+
+ strlcpy(filename1, atlas_dirname, sizeof(filename1));
+ strlcat(filename1, CRONUPDATE, sizeof(filename1));
+
+ while (fd= open(filename1, O_WRONLY|O_CREAT|O_TRUNC, 0600), fd >= 0)
+ {
+ len= strlen(UPDATELINE);
+ if (write(fd, UPDATELINE, len) != len)
+ {
+ close(fd);
+ add_2sock(ts, IO_ERROR);
+ return;
+ }
+ if (fstat(fd, &st) != 0)
+ {
+ close(fd);
+ add_2sock(ts, IO_ERROR);
+ return;
+ }
+ close(fd);
+ if (st.st_nlink != 0)
+ break;
+
+ /* Race condition, try again */
+ }
+
+ if (fd < 0)
+ {
+ add_2sock(ts, CREATE_FAILED);
+ return;
+ }
+}
+
+static void do_oneoff(struct tsession *ts, char *line)
+{
+ size_t len;
+ char *cp, *ncp;
+ FILE *file;
+ char filename[256];
+ char filename_new[256];
+
+ cp= line+strlen(CMD_ONEOFF);
+
+ /* Find the end of the filename */
+ ncp= cp;
+ while (ncp[0] != '\0' && !isspace((unsigned char)ncp[0]))
+ ncp++;
+
+ len= ncp-cp;
+ if (len+1 > sizeof(filename))
+ {
+ add_2sock(ts, NAME_TOO_LONG);
+ return;
+ }
+ memcpy(filename, cp, len);
+ filename[len]= '\0';
+
+ if (len + strlen(ONEOFF_SUFFIX) + 1 > sizeof(filename_new))
+ {
+ add_2sock(ts, NAME_TOO_LONG);
+ return;
+ }
+ strlcpy(filename_new, filename, sizeof(filename_new));
+ strlcat(filename_new, ONEOFF_SUFFIX, sizeof(filename_new));
+
+ if (!validate_filename(filename, SAFE_PREFIX))
+ {
+ add_2sock(ts, BAD_PATH);
+ return;
+ }
+
+ /* Try to grab 'filename', if there is any. It doesn't matter if this
+ * fails.
+ */
+ rename(filename, filename_new);
+
+ file= fopen(filename_new, "a");
+ if (!file)
+ {
+ add_2sock(ts, CREATE_FAILED);
+ return;
+ }
+
+ /* Find start of command */
+ cp= ncp;
+ while (cp[0] != '\0' && isspace((unsigned char)cp[0]))
+ cp++;
+
+ if (fprintf(file, "%s\n", cp) == -1)
+ {
+ add_2sock(ts, IO_ERROR);
+ fclose(file);
+ return;
+ }
+
+ fclose(file);
+
+ /* And rename back. Ignore any errors */
+ rename(filename_new, filename);
+}
+
+static int get_probe_id(void)
+{
+ int probe_id;
+ size_t len;
+ char *check;
+ const char *key;
+ FILE *fp;
+ char buf[80];
+
+ fp= fopen("/home/atlas/status/reg_init_reply.txt", "r");
+ if (!fp)
+ return -1;
+
+ probe_id= -1;
+ while (fgets(buf, sizeof(buf), fp) != NULL)
+ {
+ if (strchr(buf, '\n') == NULL)
+ continue;
+ key= "PROBE_ID ";
+ len= strlen(key);
+
+ if (strncmp(buf, key, len) != 0 || strlen(buf) <= len)
+ continue;
+ probe_id= strtol(buf+len, &check, 10);
+ break;
+ }
+ fclose(fp);
+ return probe_id;
+}
+
+#endif /* ATLAS */
diff --git a/networking/tftp.c b/networking/tftp.c
index 1f70685..7f837eb 100644
--- a/networking/tftp.c
+++ b/networking/tftp.c
@@ -626,6 +626,7 @@ int tftp_main(int argc UNUSED_PARAM, char **argv)
#if ENABLE_TFTPD
/* TODO: libbb candidate? */
+#if 0
static len_and_sockaddr *get_sock_lsa(int s)
{
len_and_sockaddr *lsa;
@@ -638,6 +639,7 @@ static len_and_sockaddr *get_sock_lsa(int s)
getsockname(s, &lsa->u.sa, &lsa->len);
return lsa;
}
+#endif
int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int tftpd_main(int argc UNUSED_PARAM, char **argv)
diff --git a/scripts/kconfig/lex.zconf.c b/scripts/kconfig/lex.zconf.c
new file mode 100644
index 0000000..5fc323d
--- /dev/null
+++ b/scripts/kconfig/lex.zconf.c
@@ -0,0 +1,2325 @@
+
+#line 3 "scripts/kconfig/lex.zconf.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 31
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE zconfrestart(zconfin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int zconfleng;
+
+extern FILE *zconfin, *zconfout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up zconftext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up zconftext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef unsigned int yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via zconfrestart()), so that the user can continue scanning by
+ * just pointing zconfin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when zconftext is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int zconfleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow zconfwrap()'s to do buffer switches
+ * instead of setting up a fresh zconfin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void zconfrestart (FILE *input_file );
+void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE zconf_create_buffer (FILE *file,int size );
+void zconf_delete_buffer (YY_BUFFER_STATE b );
+void zconf_flush_buffer (YY_BUFFER_STATE b );
+void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer );
+void zconfpop_buffer_state (void );
+
+static void zconfensure_buffer_stack (void );
+static void zconf_load_buffer_state (void );
+static void zconf_init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER zconf_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE zconf_scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE zconf_scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE zconf_scan_bytes (yyconst char *bytes,int len );
+
+void *zconfalloc (yy_size_t );
+void *zconfrealloc (void *,yy_size_t );
+void zconffree (void * );
+
+#define yy_new_buffer zconf_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ zconfensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ zconf_create_buffer(zconfin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ zconfensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ zconf_create_buffer(zconfin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define zconfwrap() 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+FILE *zconfin = (FILE *) 0, *zconfout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int zconflineno;
+
+int zconflineno = 1;
+
+extern char *zconftext;
+#define yytext_ptr zconftext
+static yyconst flex_int16_t yy_nxt[][17] =
+ {
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0
+ },
+
+ {
+ 11, 12, 13, 14, 12, 12, 15, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12
+ },
+
+ {
+ 11, 12, 13, 14, 12, 12, 15, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12
+ },
+
+ {
+ 11, 16, 16, 17, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 18, 16, 16, 16
+ },
+
+ {
+ 11, 16, 16, 17, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 18, 16, 16, 16
+
+ },
+
+ {
+ 11, 19, 20, 21, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19
+ },
+
+ {
+ 11, 19, 20, 21, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19
+ },
+
+ {
+ 11, 22, 22, 23, 22, 24, 22, 22, 24, 22,
+ 22, 22, 22, 22, 22, 25, 22
+ },
+
+ {
+ 11, 22, 22, 23, 22, 24, 22, 22, 24, 22,
+ 22, 22, 22, 22, 22, 25, 22
+ },
+
+ {
+ 11, 26, 26, 27, 28, 29, 30, 31, 29, 32,
+ 33, 34, 35, 35, 36, 37, 38
+
+ },
+
+ {
+ 11, 26, 26, 27, 28, 29, 30, 31, 29, 32,
+ 33, 34, 35, 35, 36, 37, 38
+ },
+
+ {
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11
+ },
+
+ {
+ 11, -12, -12, -12, -12, -12, -12, -12, -12, -12,
+ -12, -12, -12, -12, -12, -12, -12
+ },
+
+ {
+ 11, -13, 39, 40, -13, -13, 41, -13, -13, -13,
+ -13, -13, -13, -13, -13, -13, -13
+ },
+
+ {
+ 11, -14, -14, -14, -14, -14, -14, -14, -14, -14,
+ -14, -14, -14, -14, -14, -14, -14
+
+ },
+
+ {
+ 11, 42, 42, 43, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42
+ },
+
+ {
+ 11, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ -16, -16, -16, -16, -16, -16, -16
+ },
+
+ {
+ 11, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17
+ },
+
+ {
+ 11, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, 44, -18, -18, -18
+ },
+
+ {
+ 11, 45, 45, -19, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45
+
+ },
+
+ {
+ 11, -20, 46, 47, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20
+ },
+
+ {
+ 11, 48, -21, -21, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48
+ },
+
+ {
+ 11, 49, 49, 50, 49, -22, 49, 49, -22, 49,
+ 49, 49, 49, 49, 49, -22, 49
+ },
+
+ {
+ 11, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, -23, -23, -23, -23
+ },
+
+ {
+ 11, -24, -24, -24, -24, -24, -24, -24, -24, -24,
+ -24, -24, -24, -24, -24, -24, -24
+
+ },
+
+ {
+ 11, 51, 51, 52, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51
+ },
+
+ {
+ 11, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26
+ },
+
+ {
+ 11, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27
+ },
+
+ {
+ 11, -28, -28, -28, -28, -28, -28, -28, -28, -28,
+ -28, -28, -28, -28, 53, -28, -28
+ },
+
+ {
+ 11, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29
+
+ },
+
+ {
+ 11, 54, 54, -30, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54
+ },
+
+ {
+ 11, -31, -31, -31, -31, -31, -31, 55, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31
+ },
+
+ {
+ 11, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32
+ },
+
+ {
+ 11, -33, -33, -33, -33, -33, -33, -33, -33, -33,
+ -33, -33, -33, -33, -33, -33, -33
+ },
+
+ {
+ 11, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, 56, 57, 57, -34, -34, -34
+
+ },
+
+ {
+ 11, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, 57, 57, 57, -35, -35, -35
+ },
+
+ {
+ 11, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36
+ },
+
+ {
+ 11, -37, -37, 58, -37, -37, -37, -37, -37, -37,
+ -37, -37, -37, -37, -37, -37, -37
+ },
+
+ {
+ 11, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, 59
+ },
+
+ {
+ 11, -39, 39, 40, -39, -39, 41, -39, -39, -39,
+ -39, -39, -39, -39, -39, -39, -39
+
+ },
+
+ {
+ 11, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40
+ },
+
+ {
+ 11, 42, 42, 43, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42
+ },
+
+ {
+ 11, 42, 42, 43, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42
+ },
+
+ {
+ 11, -43, -43, -43, -43, -43, -43, -43, -43, -43,
+ -43, -43, -43, -43, -43, -43, -43
+ },
+
+ {
+ 11, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, 44, -44, -44, -44
+
+ },
+
+ {
+ 11, 45, 45, -45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45
+ },
+
+ {
+ 11, -46, 46, 47, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46
+ },
+
+ {
+ 11, 48, -47, -47, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48
+ },
+
+ {
+ 11, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48
+ },
+
+ {
+ 11, 49, 49, 50, 49, -49, 49, 49, -49, 49,
+ 49, 49, 49, 49, 49, -49, 49
+
+ },
+
+ {
+ 11, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ -50, -50, -50, -50, -50, -50, -50
+ },
+
+ {
+ 11, -51, -51, 52, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51
+ },
+
+ {
+ 11, -52, -52, -52, -52, -52, -52, -52, -52, -52,
+ -52, -52, -52, -52, -52, -52, -52
+ },
+
+ {
+ 11, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ -53, -53, -53, -53, -53, -53, -53
+ },
+
+ {
+ 11, 54, 54, -54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54
+
+ },
+
+ {
+ 11, -55, -55, -55, -55, -55, -55, -55, -55, -55,
+ -55, -55, -55, -55, -55, -55, -55
+ },
+
+ {
+ 11, -56, -56, -56, -56, -56, -56, -56, -56, -56,
+ -56, 60, 57, 57, -56, -56, -56
+ },
+
+ {
+ 11, -57, -57, -57, -57, -57, -57, -57, -57, -57,
+ -57, 57, 57, 57, -57, -57, -57
+ },
+
+ {
+ 11, -58, -58, -58, -58, -58, -58, -58, -58, -58,
+ -58, -58, -58, -58, -58, -58, -58
+ },
+
+ {
+ 11, -59, -59, -59, -59, -59, -59, -59, -59, -59,
+ -59, -59, -59, -59, -59, -59, -59
+
+ },
+
+ {
+ 11, -60, -60, -60, -60, -60, -60, -60, -60, -60,
+ -60, 57, 57, 57, -60, -60, -60
+ },
+
+ } ;
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up zconftext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ zconfleng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 33
+#define YY_END_OF_BUFFER 34
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[61] =
+ { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 34, 5, 4, 2, 3, 7, 8, 6, 32, 29,
+ 31, 24, 28, 27, 26, 22, 17, 13, 16, 20,
+ 22, 11, 12, 19, 19, 14, 22, 22, 4, 2,
+ 3, 3, 1, 6, 32, 29, 31, 30, 24, 23,
+ 26, 25, 15, 20, 9, 19, 19, 21, 10, 18
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 5, 6, 1, 1, 7, 8, 9,
+ 10, 1, 1, 1, 11, 12, 12, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 1, 1, 1,
+ 14, 1, 1, 1, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 1, 15, 1, 1, 13, 1, 13, 13, 13, 13,
+
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 1, 16, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+extern int zconf_flex_debug;
+int zconf_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *zconftext;
+
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#define START_STRSIZE 16
+
+static struct {
+ struct file *file;
+ int lineno;
+} current_pos;
+
+static char *text;
+static int text_size, text_asize;
+
+struct buffer {
+ struct buffer *parent;
+ YY_BUFFER_STATE state;
+};
+
+struct buffer *current_buf;
+
+static int last_ts, first_ts;
+
+static void zconf_endhelp(void);
+static void zconf_endfile(void);
+
+void new_string(void)
+{
+ text = malloc(START_STRSIZE);
+ text_asize = START_STRSIZE;
+ text_size = 0;
+ *text = 0;
+}
+
+void append_string(const char *str, int size)
+{
+ int new_size = text_size + size + 1;
+ if (size > 70) {
+ fprintf (stderr, "%s:%d error: Overlong line\n",
+ current_file->name, current_file->lineno);
+ }
+
+ if (new_size > text_asize) {
+ new_size += START_STRSIZE - 1;
+ new_size &= -START_STRSIZE;
+ text = realloc(text, new_size);
+ text_asize = new_size;
+ }
+ memcpy(text + text_size, str, size);
+ text_size += size;
+ text[text_size] = 0;
+}
+
+void alloc_string(const char *str, int size)
+{
+ text = malloc(size + 1);
+ memcpy(text, str, size);
+ text[size] = 0;
+}
+
+#define INITIAL 0
+#define COMMAND 1
+#define HELP 2
+#define STRING 3
+#define PARAM 4
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int zconfwrap (void );
+#else
+extern int zconfwrap (void );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr );
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+//bbox: suppressing "defined but not used" warning
+#define YY_NO_INPUT 1
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( zconftext, zconfleng, 1, zconfout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ errno=0; \
+ while ( (result = read( fileno(zconfin), (char *) buf, max_size )) < 0 ) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(zconfin); \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int zconflex (void);
+
+#define YY_DECL int zconflex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after zconftext and zconfleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+ int str = 0;
+ int ts, i;
+
+ if ( (yy_init) )
+ {
+ (yy_init) = 0;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! zconfin )
+ zconfin = stdin;
+
+ if ( ! zconfout )
+ zconfout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ zconfensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ zconf_create_buffer(zconfin,YY_BUF_SIZE );
+ }
+
+ zconf_load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of zconftext. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+yy_match:
+ while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 )
+ ++yy_cp;
+
+ yy_current_state = -yy_current_state;
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+case 1:
+/* rule 1 can match eol */
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+{
+ current_file->lineno++;
+ return T_EOL;
+}
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+{
+ BEGIN(COMMAND);
+}
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+{
+ unput(zconftext[0]);
+ BEGIN(COMMAND);
+}
+ YY_BREAK
+
+case 6:
+YY_RULE_SETUP
+{
+ struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng);
+ BEGIN(PARAM);
+ current_pos.file = current_file;
+ current_pos.lineno = current_file->lineno;
+ if (id && id->flags & TF_COMMAND) {
+ zconflval.id = id;
+ return id->token;
+ }
+ alloc_string(zconftext, zconfleng);
+ zconflval.string = text;
+ return T_WORD;
+ }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+
+ YY_BREAK
+case 8:
+/* rule 8 can match eol */
+YY_RULE_SETUP
+{
+ BEGIN(INITIAL);
+ current_file->lineno++;
+ return T_EOL;
+ }
+ YY_BREAK
+
+case 9:
+YY_RULE_SETUP
+return T_AND;
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+return T_OR;
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+return T_OPEN_PAREN;
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+return T_CLOSE_PAREN;
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+return T_NOT;
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+return T_EQUAL;
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+return T_UNEQUAL;
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+{
+ str = zconftext[0];
+ new_string();
+ BEGIN(STRING);
+ }
+ YY_BREAK
+case 17:
+/* rule 17 can match eol */
+YY_RULE_SETUP
+BEGIN(INITIAL); current_file->lineno++; return T_EOL;
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+/* ignore */
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+{
+ struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng);
+ if (id && id->flags & TF_PARAM) {
+ zconflval.id = id;
+ return id->token;
+ }
+ alloc_string(zconftext, zconfleng);
+ zconflval.string = text;
+ return T_WORD;
+ }
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+/* comment */
+ YY_BREAK
+case 21:
+/* rule 21 can match eol */
+YY_RULE_SETUP
+current_file->lineno++;
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+
+ YY_BREAK
+case YY_STATE_EOF(PARAM):
+{
+ BEGIN(INITIAL);
+ }
+ YY_BREAK
+
+case 23:
+/* rule 23 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up zconftext again */
+YY_RULE_SETUP
+{
+ append_string(zconftext, zconfleng);
+ zconflval.string = text;
+ return T_WORD_QUOTE;
+ }
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+{
+ append_string(zconftext, zconfleng);
+ }
+ YY_BREAK
+case 25:
+/* rule 25 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up zconftext again */
+YY_RULE_SETUP
+{
+ append_string(zconftext + 1, zconfleng - 1);
+ zconflval.string = text;
+ return T_WORD_QUOTE;
+ }
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+{
+ append_string(zconftext + 1, zconfleng - 1);
+ }
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+{
+ if (str == zconftext[0]) {
+ BEGIN(PARAM);
+ zconflval.string = text;
+ return T_WORD_QUOTE;
+ } else
+ append_string(zconftext, 1);
+ }
+ YY_BREAK
+case 28:
+/* rule 28 can match eol */
+YY_RULE_SETUP
+{
+ printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno());
+ current_file->lineno++;
+ BEGIN(INITIAL);
+ return T_EOL;
+ }
+ YY_BREAK
+case YY_STATE_EOF(STRING):
+{
+ BEGIN(INITIAL);
+ }
+ YY_BREAK
+
+case 29:
+YY_RULE_SETUP
+{
+ ts = 0;
+ for (i = 0; i < zconfleng; i++) {
+ if (zconftext[i] == '\t')
+ ts = (ts & ~7) + 8;
+ else
+ ts++;
+ }
+ last_ts = ts;
+ if (first_ts) {
+ if (ts < first_ts) {
+ zconf_endhelp();
+ return T_HELPTEXT;
+ }
+ ts -= first_ts;
+ while (ts > 8) {
+ append_string(" ", 8);
+ ts -= 8;
+ }
+ append_string(" ", ts);
+ }
+ }
+ YY_BREAK
+case 30:
+/* rule 30 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up zconftext again */
+YY_RULE_SETUP
+{
+ current_file->lineno++;
+ zconf_endhelp();
+ return T_HELPTEXT;
+ }
+ YY_BREAK
+case 31:
+/* rule 31 can match eol */
+YY_RULE_SETUP
+{
+ current_file->lineno++;
+ append_string("\n", 1);
+ }
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+{
+ append_string(zconftext, zconfleng);
+ if (!first_ts)
+ first_ts = last_ts;
+ }
+ YY_BREAK
+case YY_STATE_EOF(HELP):
+{
+ zconf_endhelp();
+ return T_HELPTEXT;
+ }
+ YY_BREAK
+
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(COMMAND):
+{
+ if (current_file) {
+ zconf_endfile();
+ return T_EOL;
+ }
+ fclose(zconfin);
+ yyterminate();
+}
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+YY_FATAL_ERROR( "flex scanner jammed" );
+ YY_BREAK
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed zconfin at a new source and called
+ * zconflex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = zconfin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( zconfwrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * zconftext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of zconflex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ size_t num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ zconfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ zconfrestart(zconfin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+
+ yy_current_state = yy_nxt[yy_current_state][1];
+ yy_is_jam = (yy_current_state <= 0);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp )
+{
+ register char *yy_cp;
+
+ yy_cp = (yy_c_buf_p);
+
+ /* undo effects of setting up zconftext */
+ *yy_cp = (yy_hold_char);
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = (yy_n_chars) + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ (yytext_ptr) = yy_bp;
+ (yy_hold_char) = *yy_cp;
+ (yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ zconfrestart(zconfin );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( zconfwrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve zconftext */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void zconfrestart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ zconfensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ zconf_create_buffer(zconfin,YY_BUF_SIZE );
+ }
+
+ zconf_init_buffer(YY_CURRENT_BUFFER,input_file );
+ zconf_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * zconfpop_buffer_state();
+ * zconfpush_buffer_state(new_buffer);
+ */
+ zconfensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ zconf_load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (zconfwrap()) processing, but the only time this flag
+ * is looked at is after zconfwrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void zconf_load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ zconfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE zconf_create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) zconfalloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ zconf_init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with zconf_create_buffer()
+ *
+ */
+ void zconf_delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ zconffree((void *) b->yy_ch_buf );
+
+ zconffree((void *) b );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a zconfrestart() or at EOF.
+ */
+ static void zconf_init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ zconf_flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then zconf_init_buffer was _probably_
+ * called from zconfrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void zconf_flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ zconf_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ zconfensure_buffer_stack();
+
+ /* This block is copied from zconf_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from zconf_switch_to_buffer. */
+ zconf_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void zconfpop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ zconf_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ zconf_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void zconfensure_buffer_stack (void)
+{
+ int num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)zconfalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)zconfrealloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE zconf_scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ zconf_switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to zconflex() will
+ * scan from a @e copy of @a str.
+ * @param yy_str a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * zconf_scan_bytes() instead.
+ */
+YY_BUFFER_STATE zconf_scan_string (yyconst char * yy_str )
+{
+
+ return zconf_scan_bytes(yy_str,strlen(yy_str) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to zconflex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE zconf_scan_bytes (yyconst char * bytes, int len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = len + 2;
+ buf = (char *) zconfalloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_bytes()" );
+
+ for ( i = 0; i < len; ++i )
+ buf[i] = bytes[i];
+
+ buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = zconf_scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in zconf_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up zconftext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ zconftext[zconfleng] = (yy_hold_char); \
+ (yy_c_buf_p) = zconftext + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ zconfleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int zconfget_lineno (void)
+{
+
+ return zconflineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *zconfget_in (void)
+{
+ return zconfin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *zconfget_out (void)
+{
+ return zconfout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int zconfget_leng (void)
+{
+ return zconfleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *zconfget_text (void)
+{
+ return zconftext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void zconfset_lineno (int line_number )
+{
+
+ zconflineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see zconf_switch_to_buffer
+ */
+void zconfset_in (FILE * in_str )
+{
+ zconfin = in_str ;
+}
+
+void zconfset_out (FILE * out_str )
+{
+ zconfout = out_str ;
+}
+
+int zconfget_debug (void)
+{
+ return zconf_flex_debug;
+}
+
+void zconfset_debug (int bdebug )
+{
+ zconf_flex_debug = bdebug ;
+}
+
+/* zconflex_destroy is for both reentrant and non-reentrant scanners. */
+int zconflex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ zconf_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ zconfpop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ zconffree((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *zconfalloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *zconfrealloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void zconffree (void * ptr )
+{
+ free( (char *) ptr ); /* see zconfrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef yytext_ptr
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+
+void zconf_starthelp(void)
+{
+ new_string();
+ last_ts = first_ts = 0;
+ BEGIN(HELP);
+}
+
+static void zconf_endhelp(void)
+{
+ zconflval.string = text;
+ BEGIN(INITIAL);
+}
+
+/*
+ * Try to open specified file with following names:
+ * ./name
+ * $(srctree)/name
+ * The latter is used when srctree is separate from objtree
+ * when compiling the kernel.
+ * Return NULL if file is not found.
+ */
+FILE *zconf_fopen(const char *name)
+{
+ char *env, fullname[PATH_MAX+1];
+ FILE *f;
+
+ f = fopen(name, "r");
+ if (!f && name[0] != '/') {
+ env = getenv(SRCTREE);
+ if (env) {
+ sprintf(fullname, "%s/%s", env, name);
+ f = fopen(fullname, "r");
+ }
+ }
+ return f;
+}
+
+void zconf_initscan(const char *name)
+{
+ zconfin = zconf_fopen(name);
+ if (!zconfin) {
+ printf("can't find file %s\n", name);
+ exit(1);
+ }
+
+ current_buf = malloc(sizeof(*current_buf));
+ memset(current_buf, 0, sizeof(*current_buf));
+
+ current_file = file_lookup(name);
+ current_file->lineno = 1;
+ current_file->flags = FILE_BUSY;
+}
+
+void zconf_nextfile(const char *name)
+{
+ struct file *file = file_lookup(name);
+ struct buffer *buf = malloc(sizeof(*buf));
+ memset(buf, 0, sizeof(*buf));
+
+ current_buf->state = YY_CURRENT_BUFFER;
+ zconfin = zconf_fopen(name);
+ if (!zconfin) {
+ printf("%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), name);
+ exit(1);
+ }
+ zconf_switch_to_buffer(zconf_create_buffer(zconfin,YY_BUF_SIZE));
+ buf->parent = current_buf;
+ current_buf = buf;
+
+ if (file->flags & FILE_BUSY) {
+ printf("recursive scan (%s)?\n", name);
+ exit(1);
+ }
+ if (file->flags & FILE_SCANNED) {
+ printf("file %s already scanned?\n", name);
+ exit(1);
+ }
+ file->flags |= FILE_BUSY;
+ file->lineno = 1;
+ file->parent = current_file;
+ current_file = file;
+}
+
+static void zconf_endfile(void)
+{
+ struct buffer *parent;
+
+ current_file->flags |= FILE_SCANNED;
+ current_file->flags &= ~FILE_BUSY;
+ current_file = current_file->parent;
+
+ parent = current_buf->parent;
+ if (parent) {
+ fclose(zconfin);
+ zconf_delete_buffer(YY_CURRENT_BUFFER);
+ zconf_switch_to_buffer(parent->state);
+ }
+ free(current_buf);
+ current_buf = parent;
+}
+
+int zconf_lineno(void)
+{
+ return current_pos.lineno;
+}
+
+char *zconf_curname(void)
+{
+ return current_pos.file ? current_pos.file->name : "<none>";
+}
+
diff --git a/scripts/kconfig/zconf.hash.c b/scripts/kconfig/zconf.hash.c
new file mode 100644
index 0000000..345f0fc
--- /dev/null
+++ b/scripts/kconfig/zconf.hash.c
@@ -0,0 +1,231 @@
+/* ANSI-C code produced by gperf version 3.0.1 */
+/* Command-line: gperf */
+/* Computed positions: -k'1,3' */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646. */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#endif
+
+struct kconf_id;
+/* maximum key range = 45, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+kconf_id_hash (register const char *str, register unsigned int len)
+{
+ static unsigned char asso_values[] =
+ {
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 25, 10, 15,
+ 0, 0, 5, 47, 0, 0, 47, 47, 0, 10,
+ 0, 20, 20, 20, 5, 0, 0, 20, 47, 47,
+ 20, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47
+ };
+ register int hval = len;
+
+ switch (hval)
+ {
+ default:
+ hval += asso_values[(unsigned char)str[2]];
+ /*FALLTHROUGH*/
+ case 2:
+ case 1:
+ hval += asso_values[(unsigned char)str[0]];
+ break;
+ }
+ return hval;
+}
+
+struct kconf_id_strings_t
+ {
+ char kconf_id_strings_str2[sizeof("if")];
+ char kconf_id_strings_str3[sizeof("int")];
+ char kconf_id_strings_str4[sizeof("help")];
+ char kconf_id_strings_str5[sizeof("endif")];
+ char kconf_id_strings_str6[sizeof("select")];
+ char kconf_id_strings_str7[sizeof("endmenu")];
+ char kconf_id_strings_str8[sizeof("tristate")];
+ char kconf_id_strings_str9[sizeof("endchoice")];
+ char kconf_id_strings_str10[sizeof("range")];
+ char kconf_id_strings_str11[sizeof("string")];
+ char kconf_id_strings_str12[sizeof("default")];
+ char kconf_id_strings_str13[sizeof("def_bool")];
+ char kconf_id_strings_str14[sizeof("menu")];
+ char kconf_id_strings_str16[sizeof("def_boolean")];
+ char kconf_id_strings_str17[sizeof("def_tristate")];
+ char kconf_id_strings_str18[sizeof("mainmenu")];
+ char kconf_id_strings_str20[sizeof("menuconfig")];
+ char kconf_id_strings_str21[sizeof("config")];
+ char kconf_id_strings_str22[sizeof("on")];
+ char kconf_id_strings_str23[sizeof("hex")];
+ char kconf_id_strings_str26[sizeof("source")];
+ char kconf_id_strings_str27[sizeof("depends")];
+ char kconf_id_strings_str28[sizeof("optional")];
+ char kconf_id_strings_str31[sizeof("enable")];
+ char kconf_id_strings_str32[sizeof("comment")];
+ char kconf_id_strings_str33[sizeof("requires")];
+ char kconf_id_strings_str34[sizeof("bool")];
+ char kconf_id_strings_str37[sizeof("boolean")];
+ char kconf_id_strings_str41[sizeof("choice")];
+ char kconf_id_strings_str46[sizeof("prompt")];
+ };
+static struct kconf_id_strings_t kconf_id_strings_contents =
+ {
+ "if",
+ "int",
+ "help",
+ "endif",
+ "select",
+ "endmenu",
+ "tristate",
+ "endchoice",
+ "range",
+ "string",
+ "default",
+ "def_bool",
+ "menu",
+ "def_boolean",
+ "def_tristate",
+ "mainmenu",
+ "menuconfig",
+ "config",
+ "on",
+ "hex",
+ "source",
+ "depends",
+ "optional",
+ "enable",
+ "comment",
+ "requires",
+ "bool",
+ "boolean",
+ "choice",
+ "prompt"
+ };
+#define kconf_id_strings ((const char *) &kconf_id_strings_contents)
+#ifdef __GNUC__
+__inline
+#endif
+struct kconf_id *
+kconf_id_lookup (register const char *str, register unsigned int len)
+{
+ enum
+ {
+ TOTAL_KEYWORDS = 30,
+ MIN_WORD_LENGTH = 2,
+ MAX_WORD_LENGTH = 12,
+ MIN_HASH_VALUE = 2,
+ MAX_HASH_VALUE = 46
+ };
+
+ static struct kconf_id wordlist[] =
+ {
+ {-1}, {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str2, T_IF, TF_COMMAND|TF_PARAM},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str3, T_TYPE, TF_COMMAND, S_INT},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str4, T_HELP, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str5, T_ENDIF, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str6, T_SELECT, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str7, T_ENDMENU, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str8, T_TYPE, TF_COMMAND, S_TRISTATE},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str9, T_ENDCHOICE, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str10, T_RANGE, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str11, T_TYPE, TF_COMMAND, S_STRING},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str12, T_DEFAULT, TF_COMMAND, S_UNKNOWN},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str13, T_DEFAULT, TF_COMMAND, S_BOOLEAN},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str14, T_MENU, TF_COMMAND},
+ {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str16, T_DEFAULT, TF_COMMAND, S_BOOLEAN},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str17, T_DEFAULT, TF_COMMAND, S_TRISTATE},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str18, T_MAINMENU, TF_COMMAND},
+ {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str20, T_MENUCONFIG, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str21, T_CONFIG, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str22, T_ON, TF_PARAM},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str23, T_TYPE, TF_COMMAND, S_HEX},
+ {-1}, {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str26, T_SOURCE, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str27, T_DEPENDS, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str28, T_OPTIONAL, TF_COMMAND},
+ {-1}, {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str31, T_SELECT, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str32, T_COMMENT, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str33, T_REQUIRES, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str34, T_TYPE, TF_COMMAND, S_BOOLEAN},
+ {-1}, {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str37, T_TYPE, TF_COMMAND, S_BOOLEAN},
+ {-1}, {-1}, {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str41, T_CHOICE, TF_COMMAND},
+ {-1}, {-1}, {-1}, {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str46, T_PROMPT, TF_COMMAND}
+ };
+
+ if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+ {
+ register int key = kconf_id_hash (str, len);
+
+ if (key <= MAX_HASH_VALUE && key >= 0)
+ {
+ register int o = wordlist[key].name;
+ if (o >= 0)
+ {
+ register const char *s = o + kconf_id_strings;
+
+ if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0')
+ return &wordlist[key];
+ }
+ }
+ }
+ return 0;
+}
+
diff --git a/scripts/kconfig/zconf.tab.c b/scripts/kconfig/zconf.tab.c
new file mode 100644
index 0000000..b62724d
--- /dev/null
+++ b/scripts/kconfig/zconf.tab.c
@@ -0,0 +1,2173 @@
+/* A Bison parser, made by GNU Bison 2.0. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Written by Richard Stallman by simplifying the original so called
+ ``semantic'' parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+/* Substitute the variable and function names. */
+#define yyparse zconfparse
+#define yylex zconflex
+#define yyerror zconferror
+#define yylval zconflval
+#define yychar zconfchar
+#define yydebug zconfdebug
+#define yynerrs zconfnerrs
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ T_MAINMENU = 258,
+ T_MENU = 259,
+ T_ENDMENU = 260,
+ T_SOURCE = 261,
+ T_CHOICE = 262,
+ T_ENDCHOICE = 263,
+ T_COMMENT = 264,
+ T_CONFIG = 265,
+ T_MENUCONFIG = 266,
+ T_HELP = 267,
+ T_HELPTEXT = 268,
+ T_IF = 269,
+ T_ENDIF = 270,
+ T_DEPENDS = 271,
+ T_REQUIRES = 272,
+ T_OPTIONAL = 273,
+ T_PROMPT = 274,
+ T_TYPE = 275,
+ T_DEFAULT = 276,
+ T_SELECT = 277,
+ T_RANGE = 278,
+ T_ON = 279,
+ T_WORD = 280,
+ T_WORD_QUOTE = 281,
+ T_UNEQUAL = 282,
+ T_CLOSE_PAREN = 283,
+ T_OPEN_PAREN = 284,
+ T_EOL = 285,
+ T_OR = 286,
+ T_AND = 287,
+ T_EQUAL = 288,
+ T_NOT = 289
+ };
+#endif
+#define T_MAINMENU 258
+#define T_MENU 259
+#define T_ENDMENU 260
+#define T_SOURCE 261
+#define T_CHOICE 262
+#define T_ENDCHOICE 263
+#define T_COMMENT 264
+#define T_CONFIG 265
+#define T_MENUCONFIG 266
+#define T_HELP 267
+#define T_HELPTEXT 268
+#define T_IF 269
+#define T_ENDIF 270
+#define T_DEPENDS 271
+#define T_REQUIRES 272
+#define T_OPTIONAL 273
+#define T_PROMPT 274
+#define T_TYPE 275
+#define T_DEFAULT 276
+#define T_SELECT 277
+#define T_RANGE 278
+#define T_ON 279
+#define T_WORD 280
+#define T_WORD_QUOTE 281
+#define T_UNEQUAL 282
+#define T_CLOSE_PAREN 283
+#define T_OPEN_PAREN 284
+#define T_EOL 285
+#define T_OR 286
+#define T_AND 287
+#define T_EQUAL 288
+#define T_NOT 289
+
+
+
+
+/* Copy the first part of user declarations. */
+
+
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#include "zconf.hash.c"
+
+#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
+
+#define PRINTD 0x0001
+#define DEBUG_PARSE 0x0002
+
+int cdebug = PRINTD;
+
+extern int zconflex(void);
+static void zconfprint(const char *err, ...);
+static void zconf_error(const char *err, ...);
+static void zconferror(const char *err);
+static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken);
+
+struct symbol *symbol_hash[257];
+
+static struct menu *current_menu, *current_entry;
+
+#define YYDEBUG 0
+#if YYDEBUG
+#define YYERROR_VERBOSE
+#endif
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+
+typedef union YYSTYPE {
+ char *string;
+ struct file *file;
+ struct symbol *symbol;
+ struct expr *expr;
+ struct menu *menu;
+ struct kconf_id *id;
+} YYSTYPE;
+/* Line 190 of yacc.c. */
+
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 213 of yacc.c. */
+
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+# ifndef YYFREE
+# define YYFREE free
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# endif
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# else
+# define YYSTACK_ALLOC alloca
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+ && (! defined (__cplusplus) \
+ || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ short int yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (short int) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined (__GNUC__) && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ register YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (0)
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+ typedef signed char yysigned_char;
+#else
+ typedef short int yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 3
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 264
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 35
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 42
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 104
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 175
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 289
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const unsigned char yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const unsigned short int yyprhs[] =
+{
+ 0, 0, 3, 5, 6, 9, 12, 15, 20, 23,
+ 28, 33, 37, 39, 41, 43, 45, 47, 49, 51,
+ 53, 55, 57, 59, 61, 63, 67, 70, 74, 77,
+ 81, 84, 85, 88, 91, 94, 97, 100, 104, 109,
+ 114, 119, 125, 128, 131, 133, 137, 138, 141, 144,
+ 147, 150, 153, 158, 162, 165, 170, 171, 174, 178,
+ 180, 184, 185, 188, 191, 194, 198, 201, 203, 207,
+ 208, 211, 214, 217, 221, 225, 228, 231, 234, 235,
+ 238, 241, 244, 249, 253, 257, 258, 261, 263, 265,
+ 268, 271, 274, 276, 279, 280, 283, 285, 289, 293,
+ 297, 300, 304, 308, 310
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+ 36, 0, -1, 37, -1, -1, 37, 39, -1, 37,
+ 50, -1, 37, 61, -1, 37, 3, 71, 73, -1,
+ 37, 72, -1, 37, 25, 1, 30, -1, 37, 38,
+ 1, 30, -1, 37, 1, 30, -1, 16, -1, 19,
+ -1, 20, -1, 22, -1, 18, -1, 23, -1, 21,
+ -1, 30, -1, 56, -1, 65, -1, 42, -1, 44,
+ -1, 63, -1, 25, 1, 30, -1, 1, 30, -1,
+ 10, 25, 30, -1, 41, 45, -1, 11, 25, 30,
+ -1, 43, 45, -1, -1, 45, 46, -1, 45, 69,
+ -1, 45, 67, -1, 45, 40, -1, 45, 30, -1,
+ 20, 70, 30, -1, 19, 71, 74, 30, -1, 21,
+ 75, 74, 30, -1, 22, 25, 74, 30, -1, 23,
+ 76, 76, 74, 30, -1, 7, 30, -1, 47, 51,
+ -1, 72, -1, 48, 53, 49, -1, -1, 51, 52,
+ -1, 51, 69, -1, 51, 67, -1, 51, 30, -1,
+ 51, 40, -1, 19, 71, 74, 30, -1, 20, 70,
+ 30, -1, 18, 30, -1, 21, 25, 74, 30, -1,
+ -1, 53, 39, -1, 14, 75, 73, -1, 72, -1,
+ 54, 57, 55, -1, -1, 57, 39, -1, 57, 61,
+ -1, 57, 50, -1, 4, 71, 30, -1, 58, 68,
+ -1, 72, -1, 59, 62, 60, -1, -1, 62, 39,
+ -1, 62, 61, -1, 62, 50, -1, 6, 71, 30,
+ -1, 9, 71, 30, -1, 64, 68, -1, 12, 30,
+ -1, 66, 13, -1, -1, 68, 69, -1, 68, 30,
+ -1, 68, 40, -1, 16, 24, 75, 30, -1, 16,
+ 75, 30, -1, 17, 75, 30, -1, -1, 71, 74,
+ -1, 25, -1, 26, -1, 5, 30, -1, 8, 30,
+ -1, 15, 30, -1, 30, -1, 73, 30, -1, -1,
+ 14, 75, -1, 76, -1, 76, 33, 76, -1, 76,
+ 27, 76, -1, 29, 75, 28, -1, 34, 75, -1,
+ 75, 31, 75, -1, 75, 32, 75, -1, 25, -1,
+ 26, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const unsigned short int yyrline[] =
+{
+ 0, 103, 103, 105, 107, 108, 109, 110, 111, 112,
+ 113, 117, 121, 121, 121, 121, 121, 121, 121, 125,
+ 126, 127, 128, 129, 130, 134, 135, 141, 149, 155,
+ 163, 173, 175, 176, 177, 178, 179, 182, 190, 196,
+ 206, 212, 220, 229, 234, 242, 245, 247, 248, 249,
+ 250, 251, 254, 260, 271, 277, 287, 289, 294, 302,
+ 310, 313, 315, 316, 317, 322, 329, 334, 342, 345,
+ 347, 348, 349, 352, 360, 367, 374, 380, 387, 389,
+ 390, 391, 394, 399, 404, 412, 414, 419, 420, 423,
+ 424, 425, 429, 430, 433, 434, 437, 438, 439, 440,
+ 441, 442, 443, 446, 447
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE
+/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "T_MAINMENU", "T_MENU", "T_ENDMENU",
+ "T_SOURCE", "T_CHOICE", "T_ENDCHOICE", "T_COMMENT", "T_CONFIG",
+ "T_MENUCONFIG", "T_HELP", "T_HELPTEXT", "T_IF", "T_ENDIF", "T_DEPENDS",
+ "T_REQUIRES", "T_OPTIONAL", "T_PROMPT", "T_TYPE", "T_DEFAULT",
+ "T_SELECT", "T_RANGE", "T_ON", "T_WORD", "T_WORD_QUOTE", "T_UNEQUAL",
+ "T_CLOSE_PAREN", "T_OPEN_PAREN", "T_EOL", "T_OR", "T_AND", "T_EQUAL",
+ "T_NOT", "$accept", "input", "stmt_list", "option_name", "common_stmt",
+ "option_error", "config_entry_start", "config_stmt",
+ "menuconfig_entry_start", "menuconfig_stmt", "config_option_list",
+ "config_option", "choice", "choice_entry", "choice_end", "choice_stmt",
+ "choice_option_list", "choice_option", "choice_block", "if_entry",
+ "if_end", "if_stmt", "if_block", "menu", "menu_entry", "menu_end",
+ "menu_stmt", "menu_block", "source_stmt", "comment", "comment_stmt",
+ "help_start", "help", "depends_list", "depends", "prompt_stmt_opt",
+ "prompt", "end", "nl", "if_expr", "expr", "symbol", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const unsigned short int yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const unsigned char yyr1[] =
+{
+ 0, 35, 36, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 38, 38, 38, 38, 38, 38, 38, 39,
+ 39, 39, 39, 39, 39, 40, 40, 41, 42, 43,
+ 44, 45, 45, 45, 45, 45, 45, 46, 46, 46,
+ 46, 46, 47, 48, 49, 50, 51, 51, 51, 51,
+ 51, 51, 52, 52, 52, 52, 53, 53, 54, 55,
+ 56, 57, 57, 57, 57, 58, 59, 60, 61, 62,
+ 62, 62, 62, 63, 64, 65, 66, 67, 68, 68,
+ 68, 68, 69, 69, 69, 70, 70, 71, 71, 72,
+ 72, 72, 73, 73, 74, 74, 75, 75, 75, 75,
+ 75, 75, 75, 76, 76
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const unsigned char yyr2[] =
+{
+ 0, 2, 1, 0, 2, 2, 2, 4, 2, 4,
+ 4, 3, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 3, 2, 3, 2, 3,
+ 2, 0, 2, 2, 2, 2, 2, 3, 4, 4,
+ 4, 5, 2, 2, 1, 3, 0, 2, 2, 2,
+ 2, 2, 4, 3, 2, 4, 0, 2, 3, 1,
+ 3, 0, 2, 2, 2, 3, 2, 1, 3, 0,
+ 2, 2, 2, 3, 3, 2, 2, 2, 0, 2,
+ 2, 2, 4, 3, 3, 0, 2, 1, 1, 2,
+ 2, 2, 1, 2, 0, 2, 1, 3, 3, 3,
+ 2, 3, 3, 1, 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const unsigned char yydefact[] =
+{
+ 3, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 12, 16, 13, 14,
+ 18, 15, 17, 0, 19, 0, 4, 31, 22, 31,
+ 23, 46, 56, 5, 61, 20, 78, 69, 6, 24,
+ 78, 21, 8, 11, 87, 88, 0, 0, 89, 0,
+ 42, 90, 0, 0, 0, 103, 104, 0, 0, 0,
+ 96, 91, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 92, 7, 65, 73, 74, 27, 29, 0,
+ 100, 0, 0, 58, 0, 0, 9, 10, 0, 0,
+ 0, 0, 0, 85, 0, 0, 0, 0, 36, 35,
+ 32, 0, 34, 33, 0, 0, 85, 0, 50, 51,
+ 47, 49, 48, 57, 45, 44, 62, 64, 60, 63,
+ 59, 80, 81, 79, 70, 72, 68, 71, 67, 93,
+ 99, 101, 102, 98, 97, 26, 76, 0, 0, 0,
+ 94, 0, 94, 94, 94, 0, 0, 77, 54, 94,
+ 0, 94, 0, 83, 84, 0, 0, 37, 86, 0,
+ 0, 94, 25, 0, 53, 0, 82, 95, 38, 39,
+ 40, 0, 52, 55, 41
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const short int yydefgoto[] =
+{
+ -1, 1, 2, 25, 26, 99, 27, 28, 29, 30,
+ 64, 100, 31, 32, 114, 33, 66, 110, 67, 34,
+ 118, 35, 68, 36, 37, 126, 38, 70, 39, 40,
+ 41, 101, 102, 69, 103, 141, 142, 42, 73, 156,
+ 59, 60
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -78
+static const short int yypact[] =
+{
+ -78, 2, 159, -78, -21, 0, 0, -12, 0, 1,
+ 4, 0, 27, 38, 60, 58, -78, -78, -78, -78,
+ -78, -78, -78, 100, -78, 104, -78, -78, -78, -78,
+ -78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
+ -78, -78, -78, -78, -78, -78, 86, 113, -78, 114,
+ -78, -78, 125, 127, 128, -78, -78, 60, 60, 210,
+ 65, -78, 141, 142, 39, 103, 182, 200, 6, 66,
+ 6, 131, -78, 146, -78, -78, -78, -78, -78, 196,
+ -78, 60, 60, 146, 40, 40, -78, -78, 155, 156,
+ -2, 60, 0, 0, 60, 105, 40, 194, -78, -78,
+ -78, 206, -78, -78, 183, 0, 0, 195, -78, -78,
+ -78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
+ -78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
+ -78, 197, -78, -78, -78, -78, -78, 60, 213, 216,
+ 212, 203, 212, 190, 212, 40, 208, -78, -78, 212,
+ 222, 212, 219, -78, -78, 60, 223, -78, -78, 224,
+ 225, 212, -78, 226, -78, 227, -78, 47, -78, -78,
+ -78, 228, -78, -78, -78
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const short int yypgoto[] =
+{
+ -78, -78, -78, -78, 164, -36, -78, -78, -78, -78,
+ 230, -78, -78, -78, -78, 29, -78, -78, -78, -78,
+ -78, -78, -78, -78, -78, -78, 59, -78, -78, -78,
+ -78, -78, 198, 220, 24, 157, -5, 169, 202, 74,
+ -53, -77
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -76
+static const short int yytable[] =
+{
+ 46, 47, 3, 49, 79, 80, 52, 133, 134, 43,
+ 6, 7, 8, 9, 10, 11, 12, 13, 48, 145,
+ 14, 15, 137, 55, 56, 44, 45, 57, 131, 132,
+ 109, 50, 58, 122, 51, 122, 24, 138, 139, -28,
+ 88, 143, -28, -28, -28, -28, -28, -28, -28, -28,
+ -28, 89, 53, -28, -28, 90, 91, -28, 92, 93,
+ 94, 95, 96, 54, 97, 55, 56, 88, 161, 98,
+ -66, -66, -66, -66, -66, -66, -66, -66, 81, 82,
+ -66, -66, 90, 91, 152, 55, 56, 140, 61, 57,
+ 112, 97, 84, 123, 58, 123, 121, 117, 85, 125,
+ 149, 62, 167, -30, 88, 63, -30, -30, -30, -30,
+ -30, -30, -30, -30, -30, 89, 72, -30, -30, 90,
+ 91, -30, 92, 93, 94, 95, 96, 119, 97, 127,
+ 144, -75, 88, 98, -75, -75, -75, -75, -75, -75,
+ -75, -75, -75, 74, 75, -75, -75, 90, 91, -75,
+ -75, -75, -75, -75, -75, 76, 97, 77, 78, -2,
+ 4, 121, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 86, 87, 14, 15, 16, 129, 17, 18, 19,
+ 20, 21, 22, 88, 23, 135, 136, -43, -43, 24,
+ -43, -43, -43, -43, 89, 146, -43, -43, 90, 91,
+ 104, 105, 106, 107, 155, 7, 8, 97, 10, 11,
+ 12, 13, 108, 148, 14, 15, 158, 159, 160, 147,
+ 151, 81, 82, 163, 130, 165, 155, 81, 82, 82,
+ 24, 113, 116, 157, 124, 171, 115, 120, 162, 128,
+ 72, 81, 82, 153, 81, 82, 154, 81, 82, 166,
+ 81, 82, 164, 168, 169, 170, 172, 173, 174, 65,
+ 71, 83, 0, 150, 111
+};
+
+static const short int yycheck[] =
+{
+ 5, 6, 0, 8, 57, 58, 11, 84, 85, 30,
+ 4, 5, 6, 7, 8, 9, 10, 11, 30, 96,
+ 14, 15, 24, 25, 26, 25, 26, 29, 81, 82,
+ 66, 30, 34, 69, 30, 71, 30, 90, 91, 0,
+ 1, 94, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 25, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 25, 25, 25, 26, 1, 145, 30,
+ 4, 5, 6, 7, 8, 9, 10, 11, 31, 32,
+ 14, 15, 16, 17, 137, 25, 26, 92, 30, 29,
+ 66, 25, 27, 69, 34, 71, 30, 68, 33, 70,
+ 105, 1, 155, 0, 1, 1, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 30, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 68, 25, 70,
+ 25, 0, 1, 30, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 30, 30, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 30, 25, 30, 30, 0,
+ 1, 30, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 30, 30, 14, 15, 16, 30, 18, 19, 20,
+ 21, 22, 23, 1, 25, 30, 30, 5, 6, 30,
+ 8, 9, 10, 11, 12, 1, 14, 15, 16, 17,
+ 18, 19, 20, 21, 14, 5, 6, 25, 8, 9,
+ 10, 11, 30, 30, 14, 15, 142, 143, 144, 13,
+ 25, 31, 32, 149, 28, 151, 14, 31, 32, 32,
+ 30, 67, 68, 30, 70, 161, 67, 68, 30, 70,
+ 30, 31, 32, 30, 31, 32, 30, 31, 32, 30,
+ 31, 32, 30, 30, 30, 30, 30, 30, 30, 29,
+ 40, 59, -1, 106, 66
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const unsigned char yystos[] =
+{
+ 0, 36, 37, 0, 1, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 14, 15, 16, 18, 19, 20,
+ 21, 22, 23, 25, 30, 38, 39, 41, 42, 43,
+ 44, 47, 48, 50, 54, 56, 58, 59, 61, 63,
+ 64, 65, 72, 30, 25, 26, 71, 71, 30, 71,
+ 30, 30, 71, 25, 25, 25, 26, 29, 34, 75,
+ 76, 30, 1, 1, 45, 45, 51, 53, 57, 68,
+ 62, 68, 30, 73, 30, 30, 30, 30, 30, 75,
+ 75, 31, 32, 73, 27, 33, 30, 30, 1, 12,
+ 16, 17, 19, 20, 21, 22, 23, 25, 30, 40,
+ 46, 66, 67, 69, 18, 19, 20, 21, 30, 40,
+ 52, 67, 69, 39, 49, 72, 39, 50, 55, 61,
+ 72, 30, 40, 69, 39, 50, 60, 61, 72, 30,
+ 28, 75, 75, 76, 76, 30, 30, 24, 75, 75,
+ 71, 70, 71, 75, 25, 76, 1, 13, 30, 71,
+ 70, 25, 75, 30, 30, 14, 74, 30, 74, 74,
+ 74, 76, 30, 74, 30, 74, 30, 75, 30, 30,
+ 30, 74, 30, 30, 30
+};
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror ("syntax error: cannot back up");\
+ YYERROR; \
+ } \
+while (0)
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (N) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (0)
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (0)
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yysymprint (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short int *bottom, short int *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ short int *bottom;
+ short int *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (/* Nothing. */; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+ int yyrule;
+#endif
+{
+ int yyi;
+ unsigned int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
+ yyrule - 1, yylno);
+ /* Print the symbols being reduced, and their result. */
+ for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+ YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
+ YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (Rule); \
+} while (0)
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined (__GLIBC__) && defined (_STRING_H)
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+static YYSIZE_T
+# if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+# else
+yystrlen (yystr)
+ const char *yystr;
+# endif
+{
+ register const char *yys = yystr;
+
+ while (*yys++ != '\0')
+ continue;
+
+ return yys - yystr - 1;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+static char *
+# if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+# else
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+# endif
+{
+ register char *yyd = yydest;
+ register const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+#endif /* !YYERROR_VERBOSE */
+
+
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+ YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+ case 48: /* choice_entry */
+
+ {
+ fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+ (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno);
+ if (current_menu == (yyvaluep->menu))
+ menu_end_menu();
+};
+
+ break;
+ case 54: /* if_entry */
+
+ {
+ fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+ (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno);
+ if (current_menu == (yyvaluep->menu))
+ menu_end_menu();
+};
+
+ break;
+ case 59: /* menu_entry */
+
+ {
+ fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+ (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno);
+ if (current_menu == (yyvaluep->menu))
+ menu_end_menu();
+};
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM);
+# else
+int yyparse ();
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+ register int yystate;
+ register int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks through separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ short int yyssa[YYINITDEPTH];
+ short int *yyss = yyssa;
+ register short int *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ register YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK (yyvsp--, yyssp--)
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* When reducing, the number of symbols on the RHS of the reduced
+ rule. */
+ int yylen;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+
+ yyvsp[0] = yylval;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks.
+ */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short int *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow ("parser stack overflow",
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyoverflowlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyoverflowlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ short int *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyoverflowlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a look-ahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 8:
+
+ { zconf_error("unexpected end statement"); ;}
+ break;
+
+ case 9:
+
+ { zconf_error("unknown statement \"%s\"", (yyvsp[-2].string)); ;}
+ break;
+
+ case 10:
+
+ {
+ zconf_error("unexpected option \"%s\"", kconf_id_strings + (yyvsp[-2].id)->name);
+;}
+ break;
+
+ case 11:
+
+ { zconf_error("invalid statement"); ;}
+ break;
+
+ case 25:
+
+ { zconf_error("unknown option \"%s\"", (yyvsp[-2].string)); ;}
+ break;
+
+ case 26:
+
+ { zconf_error("invalid option"); ;}
+ break;
+
+ case 27:
+
+ {
+ struct symbol *sym = sym_lookup((yyvsp[-1].string), 0);
+ sym->flags |= SYMBOL_OPTIONAL;
+ menu_add_entry(sym);
+ printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string));
+;}
+ break;
+
+ case 28:
+
+ {
+ menu_end_entry();
+ printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 29:
+
+ {
+ struct symbol *sym = sym_lookup((yyvsp[-1].string), 0);
+ sym->flags |= SYMBOL_OPTIONAL;
+ menu_add_entry(sym);
+ printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string));
+;}
+ break;
+
+ case 30:
+
+ {
+ if (current_entry->prompt)
+ current_entry->prompt->type = P_MENU;
+ else
+ zconfprint("warning: menuconfig statement without prompt");
+ menu_end_entry();
+ printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 37:
+
+ {
+ menu_set_type((yyvsp[-2].id)->stype);
+ printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+ zconf_curname(), zconf_lineno(),
+ (yyvsp[-2].id)->stype);
+;}
+ break;
+
+ case 38:
+
+ {
+ menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 39:
+
+ {
+ menu_add_expr(P_DEFAULT, (yyvsp[-2].expr), (yyvsp[-1].expr));
+ if ((yyvsp[-3].id)->stype != S_UNKNOWN)
+ menu_set_type((yyvsp[-3].id)->stype);
+ printd(DEBUG_PARSE, "%s:%d:default(%u)\n",
+ zconf_curname(), zconf_lineno(),
+ (yyvsp[-3].id)->stype);
+;}
+ break;
+
+ case 40:
+
+ {
+ menu_add_symbol(P_SELECT, sym_lookup((yyvsp[-2].string), 0), (yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 41:
+
+ {
+ menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,(yyvsp[-3].symbol), (yyvsp[-2].symbol)), (yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 42:
+
+ {
+ struct symbol *sym = sym_lookup(NULL, 0);
+ sym->flags |= SYMBOL_CHOICE;
+ menu_add_entry(sym);
+ menu_add_expr(P_CHOICE, NULL, NULL);
+ printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 43:
+
+ {
+ (yyval.menu) = menu_add_menu();
+;}
+ break;
+
+ case 44:
+
+ {
+ if (zconf_endtoken((yyvsp[0].id), T_CHOICE, T_ENDCHOICE)) {
+ menu_end_menu();
+ printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
+ }
+;}
+ break;
+
+ case 52:
+
+ {
+ menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 53:
+
+ {
+ if ((yyvsp[-2].id)->stype == S_BOOLEAN || (yyvsp[-2].id)->stype == S_TRISTATE) {
+ menu_set_type((yyvsp[-2].id)->stype);
+ printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+ zconf_curname(), zconf_lineno(),
+ (yyvsp[-2].id)->stype);
+ } else
+ YYERROR;
+;}
+ break;
+
+ case 54:
+
+ {
+ current_entry->sym->flags |= SYMBOL_OPTIONAL;
+ printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 55:
+
+ {
+ if ((yyvsp[-3].id)->stype == S_UNKNOWN) {
+ menu_add_symbol(P_DEFAULT, sym_lookup((yyvsp[-2].string), 0), (yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:default\n",
+ zconf_curname(), zconf_lineno());
+ } else
+ YYERROR;
+;}
+ break;
+
+ case 58:
+
+ {
+ printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
+ menu_add_entry(NULL);
+ menu_add_dep((yyvsp[-1].expr));
+ (yyval.menu) = menu_add_menu();
+;}
+ break;
+
+ case 59:
+
+ {
+ if (zconf_endtoken((yyvsp[0].id), T_IF, T_ENDIF)) {
+ menu_end_menu();
+ printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
+ }
+;}
+ break;
+
+ case 65:
+
+ {
+ menu_add_entry(NULL);
+ menu_add_prompt(P_MENU, (yyvsp[-1].string), NULL);
+ printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 66:
+
+ {
+ (yyval.menu) = menu_add_menu();
+;}
+ break;
+
+ case 67:
+
+ {
+ if (zconf_endtoken((yyvsp[0].id), T_MENU, T_ENDMENU)) {
+ menu_end_menu();
+ printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
+ }
+;}
+ break;
+
+ case 73:
+
+ {
+ printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string));
+ zconf_nextfile((yyvsp[-1].string));
+;}
+ break;
+
+ case 74:
+
+ {
+ menu_add_entry(NULL);
+ menu_add_prompt(P_COMMENT, (yyvsp[-1].string), NULL);
+ printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 75:
+
+ {
+ menu_end_entry();
+;}
+ break;
+
+ case 76:
+
+ {
+ printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
+ zconf_starthelp();
+;}
+ break;
+
+ case 77:
+
+ {
+ current_entry->sym->help = (yyvsp[0].string);
+;}
+ break;
+
+ case 82:
+
+ {
+ menu_add_dep((yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 83:
+
+ {
+ menu_add_dep((yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 84:
+
+ {
+ menu_add_dep((yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 86:
+
+ {
+ menu_add_prompt(P_PROMPT, (yyvsp[-1].string), (yyvsp[0].expr));
+;}
+ break;
+
+ case 89:
+
+ { (yyval.id) = (yyvsp[-1].id); ;}
+ break;
+
+ case 90:
+
+ { (yyval.id) = (yyvsp[-1].id); ;}
+ break;
+
+ case 91:
+
+ { (yyval.id) = (yyvsp[-1].id); ;}
+ break;
+
+ case 94:
+
+ { (yyval.expr) = NULL; ;}
+ break;
+
+ case 95:
+
+ { (yyval.expr) = (yyvsp[0].expr); ;}
+ break;
+
+ case 96:
+
+ { (yyval.expr) = expr_alloc_symbol((yyvsp[0].symbol)); ;}
+ break;
+
+ case 97:
+
+ { (yyval.expr) = expr_alloc_comp(E_EQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); ;}
+ break;
+
+ case 98:
+
+ { (yyval.expr) = expr_alloc_comp(E_UNEQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); ;}
+ break;
+
+ case 99:
+
+ { (yyval.expr) = (yyvsp[-1].expr); ;}
+ break;
+
+ case 100:
+
+ { (yyval.expr) = expr_alloc_one(E_NOT, (yyvsp[0].expr)); ;}
+ break;
+
+ case 101:
+
+ { (yyval.expr) = expr_alloc_two(E_OR, (yyvsp[-2].expr), (yyvsp[0].expr)); ;}
+ break;
+
+ case 102:
+
+ { (yyval.expr) = expr_alloc_two(E_AND, (yyvsp[-2].expr), (yyvsp[0].expr)); ;}
+ break;
+
+ case 103:
+
+ { (yyval.symbol) = sym_lookup((yyvsp[0].string), 0); free((yyvsp[0].string)); ;}
+ break;
+
+ case 104:
+
+ { (yyval.symbol) = sym_lookup((yyvsp[0].string), 1); free((yyvsp[0].string)); ;}
+ break;
+
+
+ }
+
+/* Line 1037 of yacc.c. */
+
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+
+
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (YYPACT_NINF < yyn && yyn < YYLAST)
+ {
+ YYSIZE_T yysize = 0;
+ int yytype = YYTRANSLATE (yychar);
+ const char* yyprefix;
+ char *yymsg;
+ int yyx;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 0;
+
+ yyprefix = ", expecting ";
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]);
+ yycount += 1;
+ if (yycount == 5)
+ {
+ yysize = 0;
+ break;
+ }
+ }
+ yysize += (sizeof ("syntax error, unexpected ")
+ + yystrlen (yytname[yytype]));
+ yymsg = (char *) YYSTACK_ALLOC (yysize);
+ if (yymsg != 0)
+ {
+ char *yyp = yystpcpy (yymsg, "syntax error, unexpected ");
+ yyp = yystpcpy (yyp, yytname[yytype]);
+
+ if (yycount < 5)
+ {
+ yyprefix = ", expecting ";
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ yyp = yystpcpy (yyp, yyprefix);
+ yyp = yystpcpy (yyp, yytname[yyx]);
+ yyprefix = " or ";
+ }
+ }
+ yyerror (yymsg);
+ YYSTACK_FREE (yymsg);
+ }
+ else
+ yyerror ("syntax error; also virtual memory exhausted");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror ("syntax error");
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* If at end of input, pop the error token,
+ then the rest of the stack, then return failure. */
+ if (yychar == YYEOF)
+ for (;;)
+ {
+
+ YYPOPSTACK;
+ if (yyssp == yyss)
+ YYABORT;
+ yydestruct ("Error: popping",
+ yystos[*yyssp], yyvsp);
+ }
+ }
+ else
+ {
+ yydestruct ("Error: discarding", yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+#ifdef __GNUC__
+ /* Pacify GCC when the user code never invokes YYERROR and the label
+ yyerrorlab therefore never appears in user code. */
+ if (0)
+ goto yyerrorlab;
+#endif
+
+yyvsp -= yylen;
+ yyssp -= yylen;
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping", yystos[yystate], yyvsp);
+ YYPOPSTACK;
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yydestruct ("Error: discarding lookahead",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*----------------------------------------------.
+| yyoverflowlab -- parser overflow comes here. |
+`----------------------------------------------*/
+yyoverflowlab:
+ yyerror ("parser stack overflow");
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+ return yyresult;
+}
+
+
+
+
+
+void conf_parse(const char *name)
+{
+ struct symbol *sym;
+ int i;
+
+ zconf_initscan(name);
+
+ sym_init();
+ menu_init();
+ modules_sym = sym_lookup("MODULES", 0);
+ rootmenu.prompt = menu_add_prompt(P_MENU, "Busybox Configuration", NULL);
+
+#if YYDEBUG
+ if (getenv("ZCONF_DEBUG"))
+ zconfdebug = 1;
+#endif
+ zconfparse();
+ if (zconfnerrs)
+ exit(1);
+ menu_finalize(&rootmenu);
+ for_all_symbols(i, sym) {
+ sym_check_deps(sym);
+ }
+
+ sym_change_count = 1;
+}
+
+const char *zconf_tokenname(int token)
+{
+ switch (token) {
+ case T_MENU: return "menu";
+ case T_ENDMENU: return "endmenu";
+ case T_CHOICE: return "choice";
+ case T_ENDCHOICE: return "endchoice";
+ case T_IF: return "if";
+ case T_ENDIF: return "endif";
+ case T_DEPENDS: return "depends";
+ }
+ return "<token>";
+}
+
+static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken)
+{
+ if (id->token != endtoken) {
+ zconf_error("unexpected '%s' within %s block",
+ kconf_id_strings + id->name, zconf_tokenname(starttoken));
+ zconfnerrs++;
+ return false;
+ }
+ if (current_menu->file != current_file) {
+ zconf_error("'%s' in different file than '%s'",
+ kconf_id_strings + id->name, zconf_tokenname(starttoken));
+ fprintf(stderr, "%s:%d: location of the '%s'\n",
+ current_menu->file->name, current_menu->lineno,
+ zconf_tokenname(starttoken));
+ zconfnerrs++;
+ return false;
+ }
+ return true;
+}
+
+static void zconfprint(const char *err, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+ va_start(ap, err);
+ vfprintf(stderr, err, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static void zconf_error(const char *err, ...)
+{
+ va_list ap;
+
+ zconfnerrs++;
+ fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+ va_start(ap, err);
+ vfprintf(stderr, err, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static void zconferror(const char *err)
+{
+#if YYDEBUG
+ fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err);
+#endif
+}
+
+void print_quoted_string(FILE *out, const char *str)
+{
+ const char *p;
+ int len;
+
+ putc('"', out);
+ while ((p = strchr(str, '"'))) {
+ len = p - str;
+ if (len)
+ fprintf(out, "%.*s", len, str);
+ fputs("\\\"", out);
+ str = p + 1;
+ }
+ fputs(str, out);
+ putc('"', out);
+}
+
+void print_symbol(FILE *out, struct menu *menu)
+{
+ struct symbol *sym = menu->sym;
+ struct property *prop;
+
+ if (sym_is_choice(sym))
+ fprintf(out, "choice\n");
+ else
+ fprintf(out, "config %s\n", sym->name);
+ switch (sym->type) {
+ case S_BOOLEAN:
+ fputs(" boolean\n", out);
+ break;
+ case S_TRISTATE:
+ fputs(" tristate\n", out);
+ break;
+ case S_STRING:
+ fputs(" string\n", out);
+ break;
+ case S_INT:
+ fputs(" integer\n", out);
+ break;
+ case S_HEX:
+ fputs(" hex\n", out);
+ break;
+ default:
+ fputs(" ???\n", out);
+ break;
+ }
+ for (prop = sym->prop; prop; prop = prop->next) {
+ if (prop->menu != menu)
+ continue;
+ switch (prop->type) {
+ case P_PROMPT:
+ fputs(" prompt ", out);
+ print_quoted_string(out, prop->text);
+ if (!expr_is_yes(prop->visible.expr)) {
+ fputs(" if ", out);
+ expr_fprint(prop->visible.expr, out);
+ }
+ fputc('\n', out);
+ break;
+ case P_DEFAULT:
+ fputs( " default ", out);
+ expr_fprint(prop->expr, out);
+ if (!expr_is_yes(prop->visible.expr)) {
+ fputs(" if ", out);
+ expr_fprint(prop->visible.expr, out);
+ }
+ fputc('\n', out);
+ break;
+ case P_CHOICE:
+ fputs(" #choice value\n", out);
+ break;
+ default:
+ fprintf(out, " unknown prop %d!\n", prop->type);
+ break;
+ }
+ }
+ if (sym->help) {
+ int len = strlen(sym->help);
+ while (sym->help[--len] == '\n')
+ sym->help[len] = 0;
+ fprintf(out, " help\n%s\n", sym->help);
+ }
+ fputc('\n', out);
+}
+
+void zconfdump(FILE *out)
+{
+ struct property *prop;
+ struct symbol *sym;
+ struct menu *menu;
+
+ menu = rootmenu.list;
+ while (menu) {
+ if ((sym = menu->sym))
+ print_symbol(out, menu);
+ else if ((prop = menu->prompt)) {
+ switch (prop->type) {
+ case P_COMMENT:
+ fputs("\ncomment ", out);
+ print_quoted_string(out, prop->text);
+ fputs("\n", out);
+ break;
+ case P_MENU:
+ fputs("\nmenu ", out);
+ print_quoted_string(out, prop->text);
+ fputs("\n", out);
+ break;
+ default:
+ ;
+ }
+ if (!expr_is_yes(prop->visible.expr)) {
+ fputs(" depends ", out);
+ expr_fprint(prop->visible.expr, out);
+ fputc('\n', out);
+ }
+ fputs("\n", out);
+ }
+
+ if (menu->list)
+ menu = menu->list;
+ else if (menu->next)
+ menu = menu->next;
+ else while ((menu = menu->parent)) {
+ if (menu->prompt && menu->prompt->type == P_MENU)
+ fputs("\nendmenu\n", out);
+ if (menu->next) {
+ menu = menu->next;
+ break;
+ }
+ }
+ }
+}
+
+#include "lex.zconf.c"
+#include "util.c"
+#include "confdata.c"
+#include "expr.c"
+#include "symbol.c"
+#include "menu.c"
+
+
diff --git a/shell/ash.c b/shell/ash.c
index d6fd388..6f843a9 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -244,6 +244,7 @@ extern struct globals_misc *const ash_ptr_to_globals_misc;
physdir = nullstr; \
} while (0)
+static int testadd_main(char **argv);
/* ============ Utility functions */
static int isdigit_str9(const char *str)
@@ -8601,11 +8602,14 @@ static int ulimitcmd(int, char **);
#define echocmd echo_main
#define printfcmd printf_main
#define testcmd test_main
+#define testadd testadd_main
/* Keep these in proper order since it is searched via bsearch() */
static const struct builtincmd builtintab[] = {
{ BUILTIN_SPEC_REG ".", dotcmd },
{ BUILTIN_SPEC_REG ":", truecmd },
+ { BUILTIN_REGULAR "[", testadd },
+ { BUILTIN_REGULAR "[[", testadd },
#if ENABLE_ASH_BUILTIN_TEST
{ BUILTIN_REGULAR "[", testcmd },
#if ENABLE_ASH_BASH_COMPAT
@@ -8661,6 +8665,7 @@ static const struct builtincmd builtintab[] = {
{ BUILTIN_SPEC_REG "set", setcmd },
{ BUILTIN_SPEC_REG "shift", shiftcmd },
{ BUILTIN_SPEC_REG "source", dotcmd },
+ { BUILTIN_REGULAR "test", testadd },
#if ENABLE_ASH_BUILTIN_TEST
{ BUILTIN_REGULAR "test", testcmd },
#endif
@@ -13760,3 +13765,18 @@ int main(int argc, char **argv)
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
+
+
+static int testadd_main(char **argv)
+{
+ char *p;
+ int r1;
+ int r2;
+ char * opnd1;
+ char * opnd2;
+ opnd1 = argv[1];
+ opnd2 = argv[2];
+ r1 = strtol(opnd1, &p, 10);
+ r2 = strtol(opnd2, &p, 10);
+ return ((r1+r2));
+}
diff --git a/shell/hush.c b/shell/hush.c
index 4212729..9b0d850 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -68,6 +68,7 @@
#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
#include <glob.h>
+#include <stdlib.h>
/* #include <dmalloc.h> */
#if ENABLE_HUSH_CASE
#include <fnmatch.h>
@@ -458,8 +459,11 @@ struct globals {
smallint fake_mode;
/* these three support $?, $#, and $1 */
smalluint last_return_code;
- char **global_argv;
+ /* is global_argv and global_argv[1..n] malloced? (note: not [0]) */
+ smalluint global_args_malloced;
+ /* how many non-NULL argv's we have. NB: $# + 1 */
int global_argc;
+ char **global_argv;
#if ENABLE_HUSH_LOOPS
unsigned depth_break_continue;
unsigned depth_of_loop;
@@ -633,7 +637,7 @@ static char *unbackslash(char *src)
return dst;
}
-static char **add_strings_to_strings(char **strings, char **add)
+static char **add_strings_to_strings(char **strings, char **add, int need_to_dup)
{
int i;
unsigned count1;
@@ -658,7 +662,7 @@ static char **add_strings_to_strings(char **strings, char **add)
v[count1 + count2] = NULL;
i = count2;
while (--i >= 0)
- v[count1 + i] = add[i];
+ v[count1 + i] = (need_to_dup ? xstrdup(add[i]) : add[i]);
return v;
}
@@ -667,7 +671,7 @@ static char **add_string_to_strings(char **strings, char *add)
char *v[2];
v[0] = add;
v[1] = NULL;
- return add_strings_to_strings(strings, v);
+ return add_strings_to_strings(strings, v, /*dup:*/ 0);
}
static void putenv_all(char **strings)
@@ -756,6 +760,16 @@ static int builtin_help(char **argv);
static int builtin_pwd(char **argv);
static int builtin_read(char **argv);
static int builtin_test(char **argv);
+static int builtin_add(char **argv);
+static int builtin_sub(char **argv);
+static int builtin_rchoose(char **argv);
+static int builtin_findpid(char **argv);
+static int builtin_sleepkick(char **argv);
+static int builtin_buddyinfo(char **argv);
+static int builtin_epoch(char **argv);
+static int builtin_condmv(char **argv);
+static int builtin_dfrm(char **argv);
+static int builtin_rxtxrpt(char **argv);
static int builtin_true(char **argv);
static int builtin_set(char **argv);
static int builtin_shift(char **argv);
@@ -792,6 +806,11 @@ static const struct built_in_command bltins[] = {
BLTIN(":" , builtin_true, "No-op"),
BLTIN("[" , builtin_test, "Test condition"),
BLTIN("[[" , builtin_test, "Test condition"),
+ BLTIN("[" , builtin_add, "Add two integers"),
+ BLTIN("[[" , builtin_add, "Add two integers"),
+ BLTIN("[" , builtin_sub, "Substract two integers"),
+ BLTIN("[[" , builtin_sub, "Substract two integers"),
+
#if ENABLE_HUSH_JOB
BLTIN("bg" , builtin_fg_bg, "Resume a job in the background"),
#endif
@@ -802,6 +821,14 @@ static const struct built_in_command bltins[] = {
#if ENABLE_HUSH_LOOPS
BLTIN("continue", builtin_continue, "Start new loop iteration"),
#endif
+ BLTIN("rchoose" , builtin_rchoose, "return a random one from the args"),
+ BLTIN("findpid" , builtin_findpid, "find process /proc/"),
+ BLTIN("buddyinfo" , builtin_buddyinfo, "print /proc/buddyinfo"),
+ BLTIN("sleepkick" , builtin_sleepkick, "sleep and kick the watchdog"),
+ BLTIN("epoch" , builtin_epoch, "UNIX epoch"),
+ BLTIN("condmv" , builtin_condmv, "conditional move"),
+ BLTIN("dfrm" , builtin_dfrm, "cleanup if free space gets too low"),
+ BLTIN("rxtxrpt" , builtin_rxtxrpt, "report RX and TX"),
BLTIN("echo" , builtin_echo, "Write to stdout"),
BLTIN("eval" , builtin_eval, "Construct and run shell command"),
BLTIN("exec" , builtin_exec, "Execute command, don't return to shell"),
@@ -818,6 +845,8 @@ static const struct built_in_command bltins[] = {
BLTIN("shift" , builtin_shift, "Shift positional parameters"),
// BLTIN("trap" , builtin_not_written, "Trap signals"),
BLTIN("test" , builtin_test, "Test condition"),
+ BLTIN("add" , builtin_add, "Add two integers."),
+ BLTIN("sub" , builtin_sub, "Subtract two integers."),
// BLTIN("ulimit", builtin_not_written, "Control resource limits"),
BLTIN("umask" , builtin_umask, "Set file creation mask"),
BLTIN("unset" , builtin_unset, "Unset environment variable"),
@@ -1213,8 +1242,13 @@ static int o_glob(o_string *o, int n)
* Otherwise, just finish current list[] and start new */
static int o_save_ptr(o_string *o, int n)
{
- if (o->o_glob)
- return o_glob(o, n); /* o_save_ptr_helper is inside */
+ if (o->o_glob) { /* if globbing is requested */
+ /* If o->has_empty_slot, list[n] was already globbed
+ * (if it was requested back then when it was filled)
+ * so don't do that again! */
+ if (!o->has_empty_slot)
+ return o_glob(o, n); /* o_save_ptr_helper is inside */
+ }
return o_save_ptr_helper(o, n);
}
@@ -4279,6 +4313,11 @@ int hush_main(int argc, char **argv)
switch (opt) {
case 'c':
G.global_argv = argv + optind;
+ if (!argv[optind]) {
+ /* -c 'script' (no params): prevent empty $0 */
+ *--G.global_argv = argv[0];
+ optind--;
+ } /* else -c 'script' PAR0 PAR1: $0 is PAR0 */
G.global_argc = argc - optind;
opt = parse_and_run_string(optarg, 0 /* parse_flag */);
goto final_return;
@@ -4412,6 +4451,269 @@ static int builtin_true(char **argv UNUSED_PARAM)
return 0;
}
+static int builtin_sleepkick (char **argv)
+{
+#define WATCHDOGDEV "/dev/watchdog"
+ unsigned duration;
+ unsigned watchdog;
+ int modDuration = 0;
+ if (argv[1] == NULL)
+ return (1);
+
+ duration = xatou(argv[1]);
+
+ if(argv[2])
+ {
+ int fd; /* File handler for watchdog */
+ int i = 0;
+ int iMax = 0;
+
+ watchdog = xatou(argv[2]);
+ iMax = (int) (duration / watchdog);
+
+ 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 0;
+}
+
+static int builtin_add(char **argv)
+{
+ char *p;
+ int r1;
+ int r2;
+ char * opnd1;
+ char * opnd2;
+ opnd1 = argv[1];
+ opnd2 = argv[2];
+ r1 = strtol(opnd1, &p, 10);
+ r2 = strtol(opnd2, &p, 10);
+ return ((r1+r2));
+}
+
+static int builtin_epoch (char **argv)
+{
+ char *p;
+ int r1 = 0;
+ char * opnd1;
+ opnd1 = argv[1];
+ if(argv[1])
+ r1 = strtol(opnd1, &p, 10);
+ if(r1 > 0 )
+ {
+ int r2 = time(0) - r1;
+ printf("%d\n", r2);
+ return EXIT_SUCCESS;
+ }
+ printf ("%ld\n", (time(0)));
+ return EXIT_SUCCESS;
+}
+
+static int builtin_buddyinfo(char **argv)
+{
+ char *lowmemChar;
+ unsigned lowmem = 0;
+ FILE *fp = xfopen_for_read("/proc/buddyinfo");
+ FILE *fp1 = xfopen_for_read("/proc/buddyinfo");
+ char aa[10];
+ char *my_mac ;
+ int i = 0;
+ int j = 0;
+ int memBlock = 4;
+ int fReboot = 1; // don't reboot
+ int freeMem = 0;
+ int jMax = 64; // enough
+ struct sysinfo info;
+
+ lowmemChar = argv[1];
+
+ if(lowmemChar)
+ lowmem = xatou(lowmemChar);
+ fscanf(fp, "%s", aa);
+ fscanf(fp, "%s", aa);
+ fscanf(fp, "%s", aa);
+ fscanf(fp, "%s", aa);
+
+ my_mac = getenv("ETHER_SCANNED");
+
+ if (lowmem >= 4 )
+ {
+ fReboot = 0; // env variable is set sow we check for low thershhold
+ }
+ printf ("RESULT 9001 ongoing %d ", (int)time(0));
+ if (my_mac != NULL)
+ printf("%s ", my_mac);
+ else
+ printf( "AAAAAABBBBBB ");
+ /* get uptime and print it */
+ sysinfo(&info);
+ printf ("%-7ld", info.uptime );
+
+ for (j=0; j < jMax; j++)
+ {
+ if (fscanf(fp, "%d", &i) != 1)
+ break;
+ freeMem += ( memBlock * i);
+ if ( lowmem >= 4)
+ {
+ if( memBlock >= lowmem)
+ {
+ if(fReboot == 0)
+ {
+ if (i > 0 )
+ {
+ fReboot = 1;
+
+ }
+ }
+ }
+ }
+ memBlock *= 2;
+ }
+
+ /* now print it */
+
+ printf (" %-5d " , freeMem);
+
+ fclose (fp);
+ fscanf(fp1, "%s", aa);
+ fscanf(fp1, "%s", aa);
+ fscanf(fp1, "%s", aa);
+ fscanf(fp1, "%s", aa);
+
+
+ for (j=0; j < jMax ; j++)
+ {
+ if (fscanf(fp1, "%d", &i) != 1)
+ break;
+ printf("%-3d ", i);
+ }
+
+ printf ("\n");
+ fclose(fp1);
+ if(fReboot == 0 )
+ {
+ fprintf(stderr, "buddy info returned 1 for block %d\n", lowmem);
+ return (EXIT_FAILURE);
+ }
+ return 0;
+}
+
+static int builtin_findpid(char **argv)
+{
+ procps_status_t* p = NULL;
+ int found = 0;
+
+ if (argv[1])
+ {
+ while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_COMM|PSSCAN_ARGVN)))
+ {
+ if (comm_match(p, argv[1])
+ /* or we require argv0 to match (essential for matching reexeced
+ /proc/self/exe)*/
+ || (p->argv0 && strcmp(bb_basename(p->argv0), *argv) == 0)
+ /* TODO: we can also try /proc/NUM/exe link, do we want that? */
+ )
+ {
+ found = 1; /* found the match but return at the end */
+ /* otherwise free_procps won't be called
+ d will remain open */
+ }
+ }
+ }
+ return !found; /* exit 0 is success */
+}
+
+int condmv_main(int argc, char *argv[]);
+
+static int builtin_condmv(char **argv)
+{
+ int argc;
+
+ for (argc= 0; argv[argc] != 0; argc++)
+ ;
+ return condmv_main(argc, argv);
+}
+
+int dfrm_main(int argc, char *argv[]);
+
+static int builtin_dfrm(char **argv)
+{
+ int argc;
+
+ for (argc= 0; argv[argc] != 0; argc++)
+ ;
+ return dfrm_main(argc, argv);
+}
+
+int rxtxrpt_main(int argc, char *argv[]);
+
+static int builtin_rxtxrpt(char **argv)
+{
+ int argc;
+
+ for (argc= 0; argv[argc] != 0; argc++)
+ ;
+ return rxtxrpt_main(argc, argv);
+}
+
+
+static int builtin_rchoose(char **argv)
+{
+ int argc = 0;
+ int r;
+
+ srandom (time (0));
+ r = random();
+ while (*argv) {
+ argc++;
+ argv++;
+ }
+ argv -= argc;
+ argv++;
+ r %= (argc - 1);
+ printf ("%s\n", argv[r]);
+ return fflush(stdout);
+}
+
+static int builtin_sub(char **argv)
+{
+ char *p;
+ int r1;
+ int r2;
+ char * opnd1;
+ char * opnd2;
+ opnd1 = argv[1];
+ opnd2 = argv[2];
+ r1 = strtol(opnd1, &p, 10);
+ r2 = strtol(opnd2, &p, 10);
+ printf ("%d\n", (r1-r2));
+ return (fflush(stdout));
+}
+
static int builtin_test(char **argv)
{
int argc = 0;
@@ -4639,17 +4941,68 @@ static int builtin_read(char **argv)
return set_local_var(string, 0);
}
-/* built-in 'set [VAR=value]' handler */
+/* built-in 'set' handler
+ * SUSv3 says:
+ * set [-abCefmnuvx] [-h] [-o option] [argument...]
+ * set [+abCefmnuvx] [+h] [+o option] [argument...]
+ * set -- [argument...]
+ * set -o
+ * set +o
+ * Implementations shall support the options in both their hyphen and
+ * plus-sign forms. These options can also be specified as options to sh.
+ * Examples:
+ * Write out all variables and their values: set
+ * Set $1, $2, and $3 and set "$#" to 3: set c a b
+ * Turn on the -x and -v options: set -xv
+ * Unset all positional parameters: set --
+ * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x"
+ * Set the positional parameters to the expansion of x, even if x expands
+ * with a leading '-' or '+': set -- $x
+ *
+ * So far, we only support "set -- [argument...]" by ignoring all options
+ * (also, "-o option" will be mishandled by taking "option" as parameter #1).
+ */
static int builtin_set(char **argv)
{
- char *temp = argv[1];
struct variable *e;
+ char **pp;
+ char *arg = *++argv;
- if (temp == NULL)
+ if (arg == NULL) {
for (e = G.top_var; e; e = e->next)
puts(e->varstr);
- else
- set_local_var(xstrdup(temp), 0);
+ } else {
+ /* NB: G.global_argv[0] ($0) is never freed/changed */
+
+ if (G.global_args_malloced) {
+ pp = G.global_argv;
+ while (*++pp)
+ free(*pp);
+ G.global_argv[1] = NULL;
+ } else {
+ G.global_args_malloced = 1;
+ pp = xzalloc(sizeof(pp[0]) * 2);
+ pp[0] = G.global_argv[0]; /* retain $0 */
+ G.global_argv = pp;
+ }
+ do {
+ if (arg[0] == '+')
+ continue;
+ if (arg[0] != '-')
+ break;
+ if (arg[1] == '-' && arg[2] == '\0') {
+ argv++;
+ break;
+ }
+ } while ((arg = *++argv) != NULL);
+ /* Now argv[0] is 1st argument */
+
+ /* This realloc's G.global_argv */
+ G.global_argv = pp = add_strings_to_strings(G.global_argv, argv, /*dup:*/ 1);
+ G.global_argc = 1;
+ while (*++pp)
+ G.global_argc++;
+ }
return EXIT_SUCCESS;
}
@@ -4661,9 +5014,14 @@ static int builtin_shift(char **argv)
n = atoi(argv[1]);
}
if (n >= 0 && n < G.global_argc) {
- G.global_argv[n] = G.global_argv[0];
+ if (G.global_args_malloced) {
+ int m = 1;
+ while (m <= n)
+ free(G.global_argv[m++]);
+ }
G.global_argc -= n;
- G.global_argv += n;
+ memmove(&G.global_argv[1], &G.global_argv[n+1],
+ G.global_argc * sizeof(G.global_argv[0]));
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
diff --git a/util-linux/Config.in b/util-linux/Config.in
index 976507b..eb4fa32 100644
--- a/util-linux/Config.in
+++ b/util-linux/Config.in
@@ -71,9 +71,7 @@ config FEATURE_FBSET_READMODE
default n
depends on FBSET
help
- This option allows fbset to read the video mode database stored by
- default as /etc/fb.modes, which can be used to set frame buffer
- device to pre-defined video modes.
+ This option allows fbset to read the video mode database stored by default as /etc/fb.modes, which can be used to set frame buffer device to pre-defined video modes.
config FDFLUSH
bool "fdflush"