From 73b16af8feec390afbabd9356d6e5e83c0390838 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 15 May 2015 10:20:47 +0200 Subject: busybox: imported from http://www.busybox.net/downloads/busybox-1.13.3.tar.bz2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bjørn Mork --- libbb/Config.in | 154 ++++ libbb/Kbuild | 149 +++ libbb/README | 11 + libbb/appletlib.c | 783 ++++++++++++++++ libbb/ask_confirmation.c | 34 + libbb/bb_askpass.c | 77 ++ libbb/bb_basename.c | 18 + libbb/bb_do_delay.c | 22 + libbb/bb_pwd.c | 99 ++ libbb/bb_qsort.c | 20 + libbb/bb_strtod.c | 86 ++ libbb/bb_strtonum.c | 139 +++ libbb/change_identity.c | 41 + libbb/chomp.c | 19 + libbb/compare_string_array.c | 78 ++ libbb/concat_path_file.c | 29 + libbb/concat_subpath_file.c | 23 + libbb/copy_file.c | 399 ++++++++ libbb/copyfd.c | 119 +++ libbb/correct_password.c | 80 ++ libbb/crc32.c | 40 + libbb/create_icmp6_socket.c | 36 + libbb/create_icmp_socket.c | 34 + libbb/crypt_make_salt.c | 45 + libbb/default_error_retval.c | 18 + libbb/device_open.c | 32 + libbb/die_if_bad_username.c | 36 + libbb/dump.c | 839 +++++++++++++++++ libbb/error_msg.c | 19 + libbb/error_msg_and_die.c | 20 + libbb/execable.c | 78 ++ libbb/fclose_nonstdin.c | 25 + libbb/fflush_stdout_and_exit.c | 29 + libbb/fgets_str.c | 66 ++ libbb/find_mount_point.c | 53 ++ libbb/find_pid_by_name.c | 117 +++ libbb/find_root_device.c | 74 ++ libbb/full_write.c | 42 + libbb/get_console.c | 80 ++ libbb/get_last_path_component.c | 42 + libbb/get_line_from_file.c | 207 +++++ libbb/getopt32.c | 592 ++++++++++++ libbb/getpty.c | 64 ++ libbb/herror_msg.c | 19 + libbb/herror_msg_and_die.c | 20 + libbb/human_readable.c | 97 ++ libbb/inet_common.c | 221 +++++ libbb/info_msg.c | 30 + libbb/inode_hash.c | 87 ++ libbb/isdirectory.c | 36 + libbb/kernel_version.c | 37 + libbb/last_char_is.c | 24 + libbb/lineedit.c | 1934 +++++++++++++++++++++++++++++++++++++++ libbb/lineedit_ptr_hack.c | 23 + libbb/llist.c | 106 +++ libbb/login.c | 129 +++ libbb/loop.c | 154 ++++ libbb/make_directory.c | 98 ++ libbb/makedev.c | 24 + libbb/match_fstype.c | 44 + libbb/md5.c | 446 +++++++++ libbb/messages.c | 73 ++ libbb/mode_string.c | 128 +++ libbb/mtab.c | 55 ++ libbb/mtab_file.c | 15 + libbb/obscure.c | 170 ++++ libbb/parse_config.c | 218 +++++ libbb/parse_mode.c | 150 +++ libbb/perror_msg.c | 25 + libbb/perror_msg_and_die.c | 26 + libbb/perror_nomsg.c | 22 + libbb/perror_nomsg_and_die.c | 22 + libbb/pidfile.c | 40 + libbb/print_flags.c | 32 + libbb/printable.c | 34 + libbb/process_escape_sequence.c | 89 ++ libbb/procps.c | 476 ++++++++++ libbb/ptr_to_globals.c | 35 + libbb/pw_encrypt.c | 77 ++ libbb/pw_encrypt_des.c | 798 ++++++++++++++++ libbb/pw_encrypt_md5.c | 648 +++++++++++++ libbb/read.c | 394 ++++++++ libbb/read_key.c | 157 ++++ libbb/recursive_action.c | 147 +++ libbb/remove_file.c | 102 +++ libbb/restricted_shell.c | 46 + libbb/rtc.c | 88 ++ libbb/run_shell.c | 90 ++ libbb/safe_gethostname.c | 66 ++ libbb/safe_poll.c | 34 + libbb/safe_strncpy.c | 27 + libbb/safe_write.c | 21 + libbb/selinux_common.c | 54 ++ libbb/setup_environment.c | 70 ++ libbb/sha1.c | 170 ++++ libbb/signals.c | 121 +++ libbb/simplify_path.c | 53 ++ libbb/skip_whitespace.c | 25 + libbb/speed_table.c | 117 +++ libbb/str_tolower.c | 14 + libbb/strrstr.c | 71 ++ libbb/time.c | 66 ++ libbb/trim.c | 31 + libbb/u_signal_names.c | 180 ++++ libbb/udp_io.c | 168 ++++ libbb/update_passwd.c | 153 ++++ libbb/uuencode.c | 71 ++ libbb/vdprintf.c | 21 + libbb/verror_msg.c | 127 +++ libbb/vfork_daemon_rexec.c | 331 +++++++ libbb/warn_ignoring_args.c | 17 + libbb/wfopen.c | 40 + libbb/wfopen_input.c | 48 + libbb/write.c | 20 + libbb/xatonum.c | 70 ++ libbb/xatonum_template.c | 187 ++++ libbb/xconnect.c | 401 ++++++++ libbb/xfunc_die.c | 40 + libbb/xfuncs.c | 296 ++++++ libbb/xfuncs_printf.c | 521 +++++++++++ libbb/xgetcwd.c | 41 + libbb/xgethostbyname.c | 19 + libbb/xreadlink.c | 111 +++ libbb/xrealloc_vector.c | 45 + libbb/xregcomp.c | 32 + 125 files changed, 16533 insertions(+) create mode 100644 libbb/Config.in create mode 100644 libbb/Kbuild create mode 100644 libbb/README create mode 100644 libbb/appletlib.c create mode 100644 libbb/ask_confirmation.c create mode 100644 libbb/bb_askpass.c create mode 100644 libbb/bb_basename.c create mode 100644 libbb/bb_do_delay.c create mode 100644 libbb/bb_pwd.c create mode 100644 libbb/bb_qsort.c create mode 100644 libbb/bb_strtod.c create mode 100644 libbb/bb_strtonum.c create mode 100644 libbb/change_identity.c create mode 100644 libbb/chomp.c create mode 100644 libbb/compare_string_array.c create mode 100644 libbb/concat_path_file.c create mode 100644 libbb/concat_subpath_file.c create mode 100644 libbb/copy_file.c create mode 100644 libbb/copyfd.c create mode 100644 libbb/correct_password.c create mode 100644 libbb/crc32.c create mode 100644 libbb/create_icmp6_socket.c create mode 100644 libbb/create_icmp_socket.c create mode 100644 libbb/crypt_make_salt.c create mode 100644 libbb/default_error_retval.c create mode 100644 libbb/device_open.c create mode 100644 libbb/die_if_bad_username.c create mode 100644 libbb/dump.c create mode 100644 libbb/error_msg.c create mode 100644 libbb/error_msg_and_die.c create mode 100644 libbb/execable.c create mode 100644 libbb/fclose_nonstdin.c create mode 100644 libbb/fflush_stdout_and_exit.c create mode 100644 libbb/fgets_str.c create mode 100644 libbb/find_mount_point.c create mode 100644 libbb/find_pid_by_name.c create mode 100644 libbb/find_root_device.c create mode 100644 libbb/full_write.c create mode 100644 libbb/get_console.c create mode 100644 libbb/get_last_path_component.c create mode 100644 libbb/get_line_from_file.c create mode 100644 libbb/getopt32.c create mode 100644 libbb/getpty.c create mode 100644 libbb/herror_msg.c create mode 100644 libbb/herror_msg_and_die.c create mode 100644 libbb/human_readable.c create mode 100644 libbb/inet_common.c create mode 100644 libbb/info_msg.c create mode 100644 libbb/inode_hash.c create mode 100644 libbb/isdirectory.c create mode 100644 libbb/kernel_version.c create mode 100644 libbb/last_char_is.c create mode 100644 libbb/lineedit.c create mode 100644 libbb/lineedit_ptr_hack.c create mode 100644 libbb/llist.c create mode 100644 libbb/login.c create mode 100644 libbb/loop.c create mode 100644 libbb/make_directory.c create mode 100644 libbb/makedev.c create mode 100644 libbb/match_fstype.c create mode 100644 libbb/md5.c create mode 100644 libbb/messages.c create mode 100644 libbb/mode_string.c create mode 100644 libbb/mtab.c create mode 100644 libbb/mtab_file.c create mode 100644 libbb/obscure.c create mode 100644 libbb/parse_config.c create mode 100644 libbb/parse_mode.c create mode 100644 libbb/perror_msg.c create mode 100644 libbb/perror_msg_and_die.c create mode 100644 libbb/perror_nomsg.c create mode 100644 libbb/perror_nomsg_and_die.c create mode 100644 libbb/pidfile.c create mode 100644 libbb/print_flags.c create mode 100644 libbb/printable.c create mode 100644 libbb/process_escape_sequence.c create mode 100644 libbb/procps.c create mode 100644 libbb/ptr_to_globals.c create mode 100644 libbb/pw_encrypt.c create mode 100644 libbb/pw_encrypt_des.c create mode 100644 libbb/pw_encrypt_md5.c create mode 100644 libbb/read.c create mode 100644 libbb/read_key.c create mode 100644 libbb/recursive_action.c create mode 100644 libbb/remove_file.c create mode 100644 libbb/restricted_shell.c create mode 100644 libbb/rtc.c create mode 100644 libbb/run_shell.c create mode 100644 libbb/safe_gethostname.c create mode 100644 libbb/safe_poll.c create mode 100644 libbb/safe_strncpy.c create mode 100644 libbb/safe_write.c create mode 100644 libbb/selinux_common.c create mode 100644 libbb/setup_environment.c create mode 100644 libbb/sha1.c create mode 100644 libbb/signals.c create mode 100644 libbb/simplify_path.c create mode 100644 libbb/skip_whitespace.c create mode 100644 libbb/speed_table.c create mode 100644 libbb/str_tolower.c create mode 100644 libbb/strrstr.c create mode 100644 libbb/time.c create mode 100644 libbb/trim.c create mode 100644 libbb/u_signal_names.c create mode 100644 libbb/udp_io.c create mode 100644 libbb/update_passwd.c create mode 100644 libbb/uuencode.c create mode 100644 libbb/vdprintf.c create mode 100644 libbb/verror_msg.c create mode 100644 libbb/vfork_daemon_rexec.c create mode 100644 libbb/warn_ignoring_args.c create mode 100644 libbb/wfopen.c create mode 100644 libbb/wfopen_input.c create mode 100644 libbb/write.c create mode 100644 libbb/xatonum.c create mode 100644 libbb/xatonum_template.c create mode 100644 libbb/xconnect.c create mode 100644 libbb/xfunc_die.c create mode 100644 libbb/xfuncs.c create mode 100644 libbb/xfuncs_printf.c create mode 100644 libbb/xgetcwd.c create mode 100644 libbb/xgethostbyname.c create mode 100644 libbb/xreadlink.c create mode 100644 libbb/xrealloc_vector.c create mode 100644 libbb/xregcomp.c (limited to 'libbb') diff --git a/libbb/Config.in b/libbb/Config.in new file mode 100644 index 0000000..f5b804f --- /dev/null +++ b/libbb/Config.in @@ -0,0 +1,154 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Busybox Library Tuning" + +config PASSWORD_MINLEN + int "Minimum password length" + default 6 + range 5 32 + help + Minimum allowable password length. + +config MD5_SIZE_VS_SPEED + int "MD5: Trade Bytes for Speed" + default 2 + range 0 3 + help + Trade binary size versus speed for the md5sum algorithm. + Approximate values running uClibc and hashing + linux-2.4.4.tar.bz2 were: + user times (sec) text size (386) + 0 (fastest) 1.1 6144 + 1 1.4 5392 + 2 3.0 5088 + 3 (smallest) 5.1 4912 + +config FEATURE_FAST_TOP + bool "Faster /proc scanning code (+100 bytes)" + default n + help + This option makes top (and ps) ~20% faster (or 20% less CPU hungry), + but code size is slightly bigger. + +config FEATURE_ETC_NETWORKS + bool "Support for /etc/networks" + default n + help + Enable support for network names in /etc/networks. This is + a rarely used feature which allows you to use names + instead of IP/mask pairs in route command. + +config FEATURE_EDITING + bool "Command line editing" + default n + help + Enable line editing (mainly for shell command line). + +config FEATURE_EDITING_MAX_LEN + int "Maximum length of input" + range 128 8192 + default 1024 + depends on FEATURE_EDITING + help + Line editing code uses on-stack buffers for storage. + You may want to decrease this parameter if your target machine + benefits from smaller stack usage. + +config FEATURE_EDITING_VI + bool "vi-style line editing commands" + default n + depends on FEATURE_EDITING + help + Enable vi-style line editing. In shells, this mode can be + turned on and off with "set -o vi" and "set +o vi". + +config FEATURE_EDITING_HISTORY + int "History size" + range 0 99999 + default 15 + depends on FEATURE_EDITING + help + Specify command history size. + +config FEATURE_EDITING_SAVEHISTORY + bool "History saving" + default n + depends on ASH && FEATURE_EDITING + help + Enable history saving in ash shell. + +config FEATURE_TAB_COMPLETION + bool "Tab completion" + default n + depends on FEATURE_EDITING + help + Enable tab completion. + +config FEATURE_USERNAME_COMPLETION + bool "Username completion" + default n + depends on FEATURE_TAB_COMPLETION + help + Enable username completion. + +config FEATURE_EDITING_FANCY_PROMPT + bool "Fancy shell prompts" + default n + depends on FEATURE_EDITING + help + Setting this option allows for prompts to use things like \w and + \$ and escape codes. + +config FEATURE_VERBOSE_CP_MESSAGE + bool "Give more precise messages when copy fails (cp, mv etc)" + default n + help + Error messages with this feature enabled: + $ cp file /does_not_exist/file + cp: cannot create '/does_not_exist/file': Path does not exist + $ cp file /vmlinuz/file + cp: cannot stat '/vmlinuz/file': Path has non-directory component + If this feature is not enabled, they will be, respectively: + cp: cannot remove '/does_not_exist/file': No such file or directory + cp: cannot stat '/vmlinuz/file': Not a directory + respectively. + This will cost you ~60 bytes. + +config FEATURE_COPYBUF_KB + int "Copy buffer size, in kilobytes" + range 1 1024 + default 4 + help + Size of buffer used by cp, mv, install etc. + Buffers which are 4 kb or less will be allocated on stack. + Bigger buffers will be allocated with mmap, with fallback to 4 kb + stack buffer if mmap fails. + +config MONOTONIC_SYSCALL + bool "Use clock_gettime(CLOCK_MONOTONIC) syscall" + default y + help + Use clock_gettime(CLOCK_MONOTONIC) syscall for measuring + time intervals (time, ping, traceroute etc need this). + Probably requires Linux 2.6+. If not selected, gettimeofday + will be used instead (which gives wrong results if date/time + is reset). + +config IOCTL_HEX2STR_ERROR + bool "Use ioctl names rather than hex values in error messages" + default y + help + Use ioctl names rather than hex values in error messages + (e.g. VT_DISALLOCATE rather than 0x5608). If disabled this + saves about 1400 bytes. + +config FEATURE_HWIB + bool "Support infiniband HW" + default y + help + Support for printing infiniband addresses in + network applets. +endmenu diff --git a/libbb/Kbuild b/libbb/Kbuild new file mode 100644 index 0000000..786cbee --- /dev/null +++ b/libbb/Kbuild @@ -0,0 +1,149 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under the GPL v2, see the file LICENSE in this tarball. + +lib-y:= + +lib-y += appletlib.o +lib-y += ask_confirmation.o +lib-y += bb_askpass.o +lib-y += bb_basename.o +lib-y += bb_do_delay.o +lib-y += bb_pwd.o +lib-y += bb_qsort.o +lib-y += bb_strtod.o +lib-y += bb_strtonum.o +lib-y += change_identity.o +lib-y += chomp.o +lib-y += compare_string_array.o +lib-y += concat_path_file.o +lib-y += concat_subpath_file.o +lib-y += copy_file.o +lib-y += copyfd.o +lib-y += crc32.o +lib-y += create_icmp6_socket.o +lib-y += create_icmp_socket.o +lib-y += default_error_retval.o +lib-y += device_open.o +lib-y += dump.o +lib-y += error_msg.o +lib-y += error_msg_and_die.o +lib-y += execable.o +lib-y += fclose_nonstdin.o +lib-y += fflush_stdout_and_exit.o +lib-y += fgets_str.o +lib-y += find_pid_by_name.o +lib-y += find_root_device.o +lib-y += full_write.o +lib-y += get_console.o +lib-y += get_last_path_component.o +lib-y += get_line_from_file.o +lib-y += getopt32.o +lib-y += getpty.o +lib-y += herror_msg.o +lib-y += herror_msg_and_die.o +lib-y += human_readable.o +lib-y += inet_common.o +lib-y += info_msg.o +lib-y += inode_hash.o +lib-y += isdirectory.o +lib-y += kernel_version.o +lib-y += last_char_is.o +lib-y += lineedit.o lineedit_ptr_hack.o +lib-y += llist.o +lib-y += login.o +lib-y += make_directory.o +lib-y += makedev.o +lib-y += match_fstype.o +lib-y += md5.o +lib-y += messages.o +lib-y += mode_string.o +lib-y += mtab_file.o +lib-y += obscure.o +lib-y += parse_mode.o +lib-y += parse_config.o +lib-y += perror_msg.o +lib-y += perror_msg_and_die.o +lib-y += perror_nomsg.o +lib-y += perror_nomsg_and_die.o +lib-y += pidfile.o +lib-y += printable.o +lib-y += print_flags.o +lib-y += process_escape_sequence.o +lib-y += procps.o +lib-y += ptr_to_globals.o +lib-y += read.o +lib-y += read_key.o +lib-y += recursive_action.o +lib-y += remove_file.o +lib-y += restricted_shell.o +lib-y += run_shell.o +lib-y += safe_gethostname.o +lib-y += safe_poll.o +lib-y += safe_strncpy.o +lib-y += safe_write.o +lib-y += setup_environment.o +lib-y += sha1.o +lib-y += signals.o +lib-y += simplify_path.o +lib-y += skip_whitespace.o +lib-y += speed_table.o +lib-y += str_tolower.o +lib-y += strrstr.o +lib-y += time.o +lib-y += trim.o +lib-y += u_signal_names.o +lib-y += udp_io.o +lib-y += uuencode.o +lib-y += vdprintf.o +lib-y += verror_msg.o +lib-y += vfork_daemon_rexec.o +lib-y += warn_ignoring_args.o +lib-y += wfopen.o +lib-y += wfopen_input.o +lib-y += write.o +lib-y += xatonum.o +lib-y += xconnect.o +lib-y += xfuncs.o +lib-y += xfuncs_printf.o +lib-y += xfunc_die.o +lib-y += xgetcwd.o +lib-y += xgethostbyname.o +lib-y += xreadlink.o +lib-y += xrealloc_vector.o + +# conditionally compiled objects: +lib-$(CONFIG_FEATURE_MOUNT_LOOP) += loop.o +lib-$(CONFIG_LOSETUP) += loop.o +lib-$(CONFIG_FEATURE_MTAB_SUPPORT) += mtab.o +lib-$(CONFIG_PASSWD) += pw_encrypt.o crypt_make_salt.o update_passwd.o +lib-$(CONFIG_CHPASSWD) += pw_encrypt.o crypt_make_salt.o update_passwd.o +lib-$(CONFIG_CRYPTPW) += pw_encrypt.o crypt_make_salt.o +lib-$(CONFIG_SULOGIN) += pw_encrypt.o +lib-$(CONFIG_FEATURE_HTTPD_AUTH_MD5) += pw_encrypt.o +lib-$(CONFIG_VLOCK) += pw_encrypt.o correct_password.o +lib-$(CONFIG_SU) += pw_encrypt.o correct_password.o +lib-$(CONFIG_LOGIN) += pw_encrypt.o correct_password.o +lib-$(CONFIG_DF) += find_mount_point.o +lib-$(CONFIG_MKFS_MINIX) += find_mount_point.o +lib-$(CONFIG_SELINUX) += selinux_common.o +lib-$(CONFIG_HWCLOCK) += rtc.o +lib-$(CONFIG_RTCWAKE) += rtc.o +lib-$(CONFIG_FEATURE_CHECK_NAMES) += die_if_bad_username.o + +# We shouldn't build xregcomp.c if we don't need it - this ensures we don't +# require regex.h to be in the include dir even if we don't need it thereby +# allowing us to build busybox even if uclibc regex support is disabled. + +lib-$(CONFIG_AWK) += xregcomp.o +lib-$(CONFIG_SED) += xregcomp.o +lib-$(CONFIG_GREP) += xregcomp.o +lib-$(CONFIG_EXPR) += xregcomp.o +lib-$(CONFIG_MDEV) += xregcomp.o +lib-$(CONFIG_LESS) += xregcomp.o +lib-$(CONFIG_PGREP) += xregcomp.o +lib-$(CONFIG_PKILL) += xregcomp.o +lib-$(CONFIG_DEVFSD) += xregcomp.o +lib-$(CONFIG_FEATURE_FIND_REGEX) += xregcomp.o diff --git a/libbb/README b/libbb/README new file mode 100644 index 0000000..4f28f7e --- /dev/null +++ b/libbb/README @@ -0,0 +1,11 @@ +Please see the LICENSE file for copyright information (GPLv2) + +libbb is BusyBox's utility library. All of this stuff used to be stuffed into +a single file named utility.c. When I split utility.c to create libbb, some of +the very oldest stuff ended up without their original copyright and licensing +information (which is now lost in the mists of time). If you see something +that you wrote that is mis-attributed, do let me know so we can fix that up. + + Erik Andersen + + diff --git a/libbb/appletlib.c b/libbb/appletlib.c new file mode 100644 index 0000000..2bab0eb --- /dev/null +++ b/libbb/appletlib.c @@ -0,0 +1,783 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) tons of folks. Tracking down who wrote what + * isn't something I'm going to worry about... If you wrote something + * here, please feel free to acknowledge your work. + * + * Based in part on code from sash, Copyright (c) 1999 by David I. Bell + * Permission has been granted to redistribute this code under the GPL. + * + * Licensed under GPLv2 or later, see file License in this tarball for details. + */ + +/* We are trying to not use printf, this benefits the case when selected + * applets are really simple. Example: + * + * $ ./busybox + * ... + * Currently defined functions: + * basename, false, true + * + * $ size busybox + * text data bss dec hex filename + * 4473 52 72 4597 11f5 busybox + * + * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :( + */ + +#include +#include "busybox.h" + + +/* Declare _main() */ +#define PROTOTYPES +#include "applets.h" +#undef PROTOTYPES + +#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE +/* Define usage_messages[] */ +static const char usage_messages[] ALIGN1 = "" +#define MAKE_USAGE +#include "usage.h" +#include "applets.h" +; +#undef MAKE_USAGE +#else +#define usage_messages 0 +#endif /* SHOW_USAGE */ + + +/* Include generated applet names, pointers to _main, etc */ +#include "applet_tables.h" +/* ...and if applet_tables generator says we have only one applet... */ +#ifdef SINGLE_APPLET_MAIN +#undef ENABLE_FEATURE_INDIVIDUAL +#define ENABLE_FEATURE_INDIVIDUAL 1 +#undef USE_FEATURE_INDIVIDUAL +#define USE_FEATURE_INDIVIDUAL(...) __VA_ARGS__ +#endif + + +#if ENABLE_FEATURE_COMPRESS_USAGE + +#include "usage_compressed.h" +#include "unarchive.h" + +static const char *unpack_usage_messages(void) +{ + char *outbuf = NULL; + bunzip_data *bd; + int i; + + i = start_bunzip(&bd, + /* src_fd: */ -1, +//FIXME: can avoid storing these 2 bytes! + /* inbuf: */ (void *)packed_usage + 2, + /* len: */ sizeof(packed_usage)); + /* read_bunzip can longjmp to start_bunzip, and ultimately + * end up here with i != 0 on read data errors! Not trivial */ + if (!i) { + /* Cannot use xmalloc: will leak bd in NOFORK case! */ + outbuf = malloc_or_warn(SIZEOF_usage_messages); + if (outbuf) + read_bunzip(bd, outbuf, SIZEOF_usage_messages); + } + dealloc_bunzip(bd); + return outbuf; +} +#define dealloc_usage_messages(s) free(s) + +#else + +#define unpack_usage_messages() usage_messages +#define dealloc_usage_messages(s) ((void)(s)) + +#endif /* FEATURE_COMPRESS_USAGE */ + + +static void full_write2_str(const char *str) +{ + full_write(STDERR_FILENO, str, strlen(str)); +} + +void FAST_FUNC bb_show_usage(void) +{ + if (ENABLE_SHOW_USAGE) { +#ifdef SINGLE_APPLET_STR + /* Imagine that this applet is "true". Dont suck in printf! */ + const char *p; + const char *usage_string = p = unpack_usage_messages(); + + if (*p == '\b') { + full_write2_str("\nNo help available.\n\n"); + } else { + full_write2_str("\nUsage: "SINGLE_APPLET_STR" "); + full_write2_str(p); + full_write2_str("\n\n"); + } + dealloc_usage_messages((char*)usage_string); +#else + const char *p; + const char *usage_string = p = unpack_usage_messages(); + int ap = find_applet_by_name(applet_name); + + if (ap < 0) /* never happens, paranoia */ + xfunc_die(); + while (ap) { + while (*p++) continue; + ap--; + } + full_write2_str(bb_banner); + full_write2_str(" multi-call binary\n"); + if (*p == '\b') + full_write2_str("\nNo help available.\n\n"); + else { + full_write2_str("\nUsage: "); + full_write2_str(applet_name); + full_write2_str(" "); + full_write2_str(p); + full_write2_str("\n\n"); + } + dealloc_usage_messages((char*)usage_string); +#endif + } + xfunc_die(); +} + +#if NUM_APPLETS > 8 +/* NB: any char pointer will work as well, not necessarily applet_names */ +static int applet_name_compare(const void *name, const void *v) +{ + int i = (const char *)v - applet_names; + return strcmp(name, APPLET_NAME(i)); +} +#endif +int FAST_FUNC find_applet_by_name(const char *name) +{ +#if NUM_APPLETS > 8 + /* Do a binary search to find the applet entry given the name. */ + const char *p; + p = bsearch(name, applet_names, ARRAY_SIZE(applet_main), 1, applet_name_compare); + if (!p) + return -1; + return p - applet_names; +#else + /* A version which does not pull in bsearch */ + int i = 0; + const char *p = applet_names; + while (i < NUM_APPLETS) { + if (strcmp(name, p) == 0) + return i; + p += strlen(p) + 1; + i++; + } + return -1; +#endif +} + + +void lbb_prepare(const char *applet + USE_FEATURE_INDIVIDUAL(, char **argv)) + MAIN_EXTERNALLY_VISIBLE; +void lbb_prepare(const char *applet + USE_FEATURE_INDIVIDUAL(, char **argv)) +{ +#ifdef __GLIBC__ + (*(int **)&bb_errno) = __errno_location(); + barrier(); +#endif + applet_name = applet; + + /* Set locale for everybody except 'init' */ + if (ENABLE_LOCALE_SUPPORT && getpid() != 1) + setlocale(LC_ALL, ""); + +#if ENABLE_FEATURE_INDIVIDUAL + /* Redundant for busybox (run_applet_and_exit covers that case) + * but needed for "individual applet" mode */ + if (argv[1] && !argv[2] && strcmp(argv[1], "--help") == 0) { + /* Special case. POSIX says "test --help" + * should be no different from e.g. "test --foo". */ + if (!ENABLE_TEST || strcmp(applet_name, "test") != 0) + bb_show_usage(); + } +#endif +} + +/* The code below can well be in applets/applets.c, as it is used only + * for busybox binary, not "individual" binaries. + * However, keeping it here and linking it into libbusybox.so + * (together with remaining tiny applets/applets.o) + * makes it possible to avoid --whole-archive at link time. + * This makes (shared busybox) + libbusybox smaller. + * (--gc-sections would be even better....) + */ + +const char *applet_name; +#if !BB_MMU +bool re_execed; +#endif + + +/* If not built as a single-applet executable... */ +#if !defined(SINGLE_APPLET_MAIN) + +USE_FEATURE_SUID(static uid_t ruid;) /* real uid */ + +#if ENABLE_FEATURE_SUID_CONFIG + +/* applets[] is const, so we have to define this "override" structure */ +static struct BB_suid_config { + int m_applet; + uid_t m_uid; + gid_t m_gid; + mode_t m_mode; + struct BB_suid_config *m_next; +} *suid_config; + +static bool suid_cfg_readable; + +/* check if u is member of group g */ +static int ingroup(uid_t u, gid_t g) +{ + struct group *grp = getgrgid(g); + + if (grp) { + char **mem; + + for (mem = grp->gr_mem; *mem; mem++) { + struct passwd *pwd = getpwnam(*mem); + + if (pwd && (pwd->pw_uid == u)) + return 1; + } + } + return 0; +} + +/* This should probably be a libbb routine. In that case, + * I'd probably rename it to something like bb_trimmed_slice. + */ +static char *get_trimmed_slice(char *s, char *e) +{ + /* First, consider the value at e to be nul and back up until we + * reach a non-space char. Set the char after that (possibly at + * the original e) to nul. */ + while (e-- > s) { + if (!isspace(*e)) { + break; + } + } + e[1] = '\0'; + + /* Next, advance past all leading space and return a ptr to the + * first non-space char; possibly the terminating nul. */ + return skip_whitespace(s); +} + +/* Don't depend on the tools to combine strings. */ +static const char config_file[] ALIGN1 = "/etc/busybox.conf"; + +/* We don't supply a value for the nul, so an index adjustment is + * necessary below. Also, we use unsigned short here to save some + * space even though these are really mode_t values. */ +static const unsigned short mode_mask[] ALIGN2 = { + /* SST sst xxx --- */ + S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */ + S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */ + 0, S_IXOTH, S_IXOTH, 0 /* other */ +}; + +#define parse_error(x) do { errmsg = x; goto pe_label; } while (0) + +static void parse_config_file(void) +{ + struct BB_suid_config *sct_head; + struct BB_suid_config *sct; + int applet_no; + FILE *f; + const char *errmsg; + char *s; + char *e; + int i; + unsigned lc; + smallint section; + char buffer[256]; + struct stat st; + + assert(!suid_config); /* Should be set to NULL by bss init. */ + + ruid = getuid(); + if (ruid == 0) /* run by root - don't need to even read config file */ + return; + + if ((stat(config_file, &st) != 0) /* No config file? */ + || !S_ISREG(st.st_mode) /* Not a regular file? */ + || (st.st_uid != 0) /* Not owned by root? */ + || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */ + || !(f = fopen_for_read(config_file)) /* Cannot open? */ + ) { + return; + } + + suid_cfg_readable = 1; + sct_head = NULL; + section = lc = 0; + + while (1) { + s = buffer; + + if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */ +// why? + if (ferror(f)) { /* Make sure it wasn't a read error. */ + parse_error("reading"); + } + fclose(f); + suid_config = sct_head; /* Success, so set the pointer. */ + return; + } + + lc++; /* Got a (partial) line. */ + + /* If a line is too long for our buffer, we consider it an error. + * The following test does mistreat one corner case though. + * If the final line of the file does not end with a newline and + * yet exactly fills the buffer, it will be treated as too long + * even though there isn't really a problem. But it isn't really + * worth adding code to deal with such an unlikely situation, and + * we do err on the side of caution. Besides, the line would be + * too long if it did end with a newline. */ + if (!strchr(s, '\n') && !feof(f)) { + parse_error("line too long"); + } + + /* Trim leading and trailing whitespace, ignoring comments, and + * check if the resulting string is empty. */ + s = get_trimmed_slice(s, strchrnul(s, '#')); + if (!*s) { + continue; + } + + /* Check for a section header. */ + + if (*s == '[') { + /* Unlike the old code, we ignore leading and trailing + * whitespace for the section name. We also require that + * there are no stray characters after the closing bracket. */ + e = strchr(s, ']'); + if (!e /* Missing right bracket? */ + || e[1] /* Trailing characters? */ + || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */ + ) { + parse_error("section header"); + } + /* Right now we only have one section so just check it. + * If more sections are added in the future, please don't + * resort to cascading ifs with multiple strcasecmp calls. + * That kind of bloated code is all too common. A loop + * and a string table would be a better choice unless the + * number of sections is very small. */ + if (strcasecmp(s, "SUID") == 0) { + section = 1; + continue; + } + section = -1; /* Unknown section so set to skip. */ + continue; + } + + /* Process sections. */ + + if (section == 1) { /* SUID */ + /* Since we trimmed leading and trailing space above, we're + * now looking for strings of the form + * [::space::]*=[::space::]* + * where both key and value could contain inner whitespace. */ + + /* First get the key (an applet name in our case). */ + e = strchr(s, '='); + if (e) { + s = get_trimmed_slice(s, e); + } + if (!e || !*s) { /* Missing '=' or empty key. */ + parse_error("keyword"); + } + + /* Ok, we have an applet name. Process the rhs if this + * applet is currently built in and ignore it otherwise. + * Note: this can hide config file bugs which only pop + * up when the busybox configuration is changed. */ + applet_no = find_applet_by_name(s); + if (applet_no >= 0) { + /* Note: We currently don't check for duplicates! + * The last config line for each applet will be the + * one used since we insert at the head of the list. + * I suppose this could be considered a feature. */ + sct = xmalloc(sizeof(struct BB_suid_config)); + sct->m_applet = applet_no; + sct->m_mode = 0; + sct->m_next = sct_head; + sct_head = sct; + + /* Get the specified mode. */ + + e = skip_whitespace(e+1); + + for (i = 0; i < 3; i++) { + /* There are 4 chars + 1 nul for each of user/group/other. */ + static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-"; + + const char *q; + q = strchrnul(mode_chars + 5*i, *e++); + if (!*q) { + parse_error("mode"); + } + /* Adjust by -i to account for nul. */ + sct->m_mode |= mode_mask[(q - mode_chars) - i]; + } + + /* Now get the the user/group info. */ + + s = skip_whitespace(e); + + /* Note: we require whitespace between the mode and the + * user/group info. */ + if ((s == e) || !(e = strchr(s, '.'))) { + parse_error("."); + } + *e++ = '\0'; + + /* We can't use get_ug_id here since it would exit() + * if a uid or gid was not found. Oh well... */ + sct->m_uid = bb_strtoul(s, NULL, 10); + if (errno) { + struct passwd *pwd = getpwnam(s); + if (!pwd) { + parse_error("user"); + } + sct->m_uid = pwd->pw_uid; + } + + sct->m_gid = bb_strtoul(e, NULL, 10); + if (errno) { + struct group *grp; + grp = getgrnam(e); + if (!grp) { + parse_error("group"); + } + sct->m_gid = grp->gr_gid; + } + } + continue; + } + + /* Unknown sections are ignored. */ + + /* Encountering configuration lines prior to seeing a + * section header is treated as an error. This is how + * the old code worked, but it may not be desirable. + * We may want to simply ignore such lines in case they + * are used in some future version of busybox. */ + if (!section) { + parse_error("keyword outside section"); + } + + } /* while (1) */ + + pe_label: + fprintf(stderr, "Parse error in %s, line %d: %s\n", + config_file, lc, errmsg); + + fclose(f); + /* Release any allocated memory before returning. */ + while (sct_head) { + sct = sct_head->m_next; + free(sct_head); + sct_head = sct; + } +} +#else +static inline void parse_config_file(void) +{ + USE_FEATURE_SUID(ruid = getuid();) +} +#endif /* FEATURE_SUID_CONFIG */ + + +#if ENABLE_FEATURE_SUID +static void check_suid(int applet_no) +{ + gid_t rgid; /* real gid */ + + if (ruid == 0) /* set by parse_config_file() */ + return; /* run by root - no need to check more */ + rgid = getgid(); + +#if ENABLE_FEATURE_SUID_CONFIG + if (suid_cfg_readable) { + uid_t uid; + struct BB_suid_config *sct; + mode_t m; + + for (sct = suid_config; sct; sct = sct->m_next) { + if (sct->m_applet == applet_no) + goto found; + } + goto check_need_suid; + found: + m = sct->m_mode; + if (sct->m_uid == ruid) + /* same uid */ + m >>= 6; + else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid)) + /* same group / in group */ + m >>= 3; + + if (!(m & S_IXOTH)) /* is x bit not set ? */ + bb_error_msg_and_die("you have no permission to run this applet!"); + + /* _both_ sgid and group_exec have to be set for setegid */ + if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) + rgid = sct->m_gid; + /* else (no setegid) we will set egid = rgid */ + + /* We set effective AND saved ids. If saved-id is not set + * like we do below, seteiud(0) can still later succeed! */ + if (setresgid(-1, rgid, rgid)) + bb_perror_msg_and_die("setresgid"); + + /* do we have to set effective uid? */ + uid = ruid; + if (sct->m_mode & S_ISUID) + uid = sct->m_uid; + /* else (no seteuid) we will set euid = ruid */ + + if (setresuid(-1, uid, uid)) + bb_perror_msg_and_die("setresuid"); + return; + } +#if !ENABLE_FEATURE_SUID_CONFIG_QUIET + { + static bool onetime = 0; + + if (!onetime) { + onetime = 1; + fprintf(stderr, "Using fallback suid method\n"); + } + } +#endif + check_need_suid: +#endif + if (APPLET_SUID(applet_no) == _BB_SUID_ALWAYS) { + /* Real uid is not 0. If euid isn't 0 too, suid bit + * is most probably not set on our executable */ + if (geteuid()) + bb_error_msg_and_die("must be suid to work properly"); + } else if (APPLET_SUID(applet_no) == _BB_SUID_NEVER) { + xsetgid(rgid); /* drop all privileges */ + xsetuid(ruid); + } +} +#else +#define check_suid(x) ((void)0) +#endif /* FEATURE_SUID */ + + +#if ENABLE_FEATURE_INSTALLER +/* create (sym)links for each applet */ +static void install_links(const char *busybox, int use_symbolic_links) +{ + /* directory table + * this should be consistent w/ the enum, + * busybox.h::bb_install_loc_t, or else... */ + static const char usr_bin [] ALIGN1 = "/usr/bin"; + static const char usr_sbin[] ALIGN1 = "/usr/sbin"; + static const char *const install_dir[] = { + &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */ + &usr_bin [4], /* "/bin" */ + &usr_sbin[4], /* "/sbin" */ + usr_bin, + usr_sbin + }; + + int (*lf)(const char *, const char *); + char *fpc; + unsigned i; + int rc; + + lf = link; + if (use_symbolic_links) + lf = symlink; + + for (i = 0; i < ARRAY_SIZE(applet_main); i++) { + fpc = concat_path_file( + install_dir[APPLET_INSTALL_LOC(i)], + APPLET_NAME(i)); + // debug: bb_error_msg("%slinking %s to busybox", + // use_symbolic_links ? "sym" : "", fpc); + rc = lf(busybox, fpc); + if (rc != 0 && errno != EEXIST) { + bb_simple_perror_msg(fpc); + } + free(fpc); + } +} +#else +#define install_links(x,y) ((void)0) +#endif /* FEATURE_INSTALLER */ + +/* If we were called as "busybox..." */ +static int busybox_main(char **argv) +{ + if (!argv[1]) { + /* Called without arguments */ + const char *a; + unsigned col, output_width; + help: + output_width = 80; + if (ENABLE_FEATURE_AUTOWIDTH) { + /* Obtain the terminal width */ + get_terminal_width_height(0, &output_width, NULL); + } + /* leading tab and room to wrap */ + output_width -= MAX_APPLET_NAME_LEN + 8; + + dup2(1, 2); + full_write2_str(bb_banner); /* reuse const string... */ + full_write2_str(" multi-call binary\n" + "Copyright (C) 1998-2008 Erik Andersen, Rob Landley, Denys Vlasenko\n" + "and others. Licensed under GPLv2.\n" + "See source distribution for full notice.\n" + "\n" + "Usage: busybox [function] [arguments]...\n" + " or: function [arguments]...\n" + "\n" + "\tBusyBox is a multi-call binary that combines many common Unix\n" + "\tutilities into a single executable. Most people will create a\n" + "\tlink to busybox for each function they wish to use and BusyBox\n" + "\twill act like whatever it was invoked as!\n" + "\n" + "Currently defined functions:\n"); + col = 0; + a = applet_names; + while (*a) { + int len; + if (col > output_width) { + full_write2_str(",\n"); + col = 0; + } + full_write2_str(col ? ", " : "\t"); + full_write2_str(a); + len = strlen(a); + col += len + 2; + a += len + 1; + } + full_write2_str("\n\n"); + return 0; + } + + if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) { + const char *busybox; + busybox = xmalloc_readlink(bb_busybox_exec_path); + if (!busybox) + busybox = bb_busybox_exec_path; + /* -s makes symlinks */ + install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0); + return 0; + } + + if (strcmp(argv[1], "--help") == 0) { + /* "busybox --help []" */ + if (!argv[2]) + goto help; + /* convert to " --help" */ + argv[0] = argv[2]; + argv[2] = NULL; + } else { + /* "busybox arg1 arg2 ..." */ + argv++; + } + /* We support "busybox /a/path/to/applet args..." too. Allows for + * "#!/bin/busybox"-style wrappers */ + applet_name = bb_get_last_path_component_nostrip(argv[0]); + run_applet_and_exit(applet_name, argv); + + /*bb_error_msg_and_die("applet not found"); - sucks in printf */ + full_write2_str(applet_name); + full_write2_str(": applet not found\n"); + xfunc_die(); +} + +void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) +{ + int argc = 1; + + while (argv[argc]) + argc++; + + /* Reinit some shared global data */ + xfunc_error_retval = EXIT_FAILURE; + + applet_name = APPLET_NAME(applet_no); + if (argc == 2 && strcmp(argv[1], "--help") == 0) { + /* Special case. POSIX says "test --help" + * should be no different from e.g. "test --foo". */ +//TODO: just compare applet_no with APPLET_NO_test + if (!ENABLE_TEST || strcmp(applet_name, "test") != 0) + bb_show_usage(); + } + if (ENABLE_FEATURE_SUID) + check_suid(applet_no); + exit(applet_main[applet_no](argc, argv)); +} + +void FAST_FUNC run_applet_and_exit(const char *name, char **argv) +{ + int applet = find_applet_by_name(name); + if (applet >= 0) + run_applet_no_and_exit(applet, argv); + if (!strncmp(name, "busybox", 7)) + exit(busybox_main(argv)); +} + +#endif /* !defined(SINGLE_APPLET_MAIN) */ + + + +#if ENABLE_BUILD_LIBBUSYBOX +int lbb_main(char **argv) +#else +int main(int argc UNUSED_PARAM, char **argv) +#endif +{ +#if defined(SINGLE_APPLET_MAIN) + /* Only one applet is selected by the user! */ + /* applet_names in this case is just "applet\0\0" */ + lbb_prepare(applet_names USE_FEATURE_INDIVIDUAL(, argv)); + return SINGLE_APPLET_MAIN(argc, argv); +#else + lbb_prepare("busybox" USE_FEATURE_INDIVIDUAL(, argv)); + +#if !BB_MMU + /* NOMMU re-exec trick sets high-order bit in first byte of name */ + if (argv[0][0] & 0x80) { + re_execed = 1; + argv[0][0] &= 0x7f; + } +#endif + applet_name = argv[0]; + if (applet_name[0] == '-') + applet_name++; + applet_name = bb_basename(applet_name); + + parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */ + + run_applet_and_exit(applet_name, argv); + + /*bb_error_msg_and_die("applet not found"); - sucks in printf */ + full_write2_str(applet_name); + full_write2_str(": applet not found\n"); + xfunc_die(); +#endif +} diff --git a/libbb/ask_confirmation.c b/libbb/ask_confirmation.c new file mode 100644 index 0000000..d08bc51 --- /dev/null +++ b/libbb/ask_confirmation.c @@ -0,0 +1,34 @@ +/* vi: set sw=4 ts=4: */ +/* + * bb_ask_confirmation implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* Read a line from stdin. If the first non-whitespace char is 'y' or 'Y', + * return 1. Otherwise return 0. + */ + +#include "libbb.h" + +int FAST_FUNC bb_ask_confirmation(void) +{ + int retval = 0; + int first = 1; + int c; + + while (((c = getchar()) != EOF) && (c != '\n')) { + /* Make sure we get the actual function call for isspace, + * as speed is not critical here. */ + if (first && !(isspace)(c)) { + --first; + if ((c == 'y') || (c == 'Y')) { + ++retval; + } + } + } + + return retval; +} diff --git a/libbb/bb_askpass.c b/libbb/bb_askpass.c new file mode 100644 index 0000000..c60ef37 --- /dev/null +++ b/libbb/bb_askpass.c @@ -0,0 +1,77 @@ +/* vi: set sw=4 ts=4: */ +/* + * Ask for a password + * I use a static buffer in this function. Plan accordingly. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include + +#include "libbb.h" + +/* do nothing signal handler */ +static void askpass_timeout(int UNUSED_PARAM ignore) +{ +} + +char* FAST_FUNC bb_askpass(int timeout, const char *prompt) +{ + /* Was static char[BIGNUM] */ + enum { sizeof_passwd = 128 }; + static char *passwd; + + char *ret; + int i; + struct sigaction sa, oldsa; + struct termios tio, oldtio; + + if (!passwd) + passwd = xmalloc(sizeof_passwd); + memset(passwd, 0, sizeof_passwd); + + tcgetattr(STDIN_FILENO, &oldtio); + tcflush(STDIN_FILENO, TCIFLUSH); + tio = oldtio; + tio.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY); + tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP); + tcsetattr_stdin_TCSANOW(&tio); + + memset(&sa, 0, sizeof(sa)); + /* sa.sa_flags = 0; - no SA_RESTART! */ + /* SIGINT and SIGALRM will interrupt read below */ + sa.sa_handler = askpass_timeout; + sigaction(SIGINT, &sa, &oldsa); + if (timeout) { + sigaction_set(SIGALRM, &sa); + alarm(timeout); + } + + fputs(prompt, stdout); + fflush(stdout); + ret = NULL; + /* On timeout or Ctrl-C, read will hopefully be interrupted, + * and we return NULL */ + if (read(STDIN_FILENO, passwd, sizeof_passwd - 1) > 0) { + ret = passwd; + i = 0; + /* Last byte is guaranteed to be 0 + (read did not overwrite it) */ + do { + if (passwd[i] == '\r' || passwd[i] == '\n') + passwd[i] = '\0'; + } while (passwd[i++]); + } + + if (timeout) { + alarm(0); + } + sigaction_set(SIGINT, &oldsa); + + tcsetattr_stdin_TCSANOW(&oldtio); + bb_putchar('\n'); + fflush(stdout); + return ret; +} diff --git a/libbb/bb_basename.c b/libbb/bb_basename.c new file mode 100644 index 0000000..bab4166 --- /dev/null +++ b/libbb/bb_basename.c @@ -0,0 +1,18 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2007 Denys Vlasenko + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +const char* FAST_FUNC bb_basename(const char *name) +{ + const char *cp = strrchr(name, '/'); + if (cp) + return cp + 1; + return name; +} diff --git a/libbb/bb_do_delay.c b/libbb/bb_do_delay.c new file mode 100644 index 0000000..3d52cc5 --- /dev/null +++ b/libbb/bb_do_delay.c @@ -0,0 +1,22 @@ +/* vi: set sw=4 ts=4: */ +/* + * Busybox utility routines. + * + * Copyright (C) 2005 by Tito Ragusa + * + * Licensed under the GPL v2, see the file LICENSE in this tarball. + */ + +#include "libbb.h" + +void FAST_FUNC bb_do_delay(int seconds) +{ + time_t start, now; + + time(&start); + now = start; + while (difftime(now, start) < seconds) { + sleep(seconds); + time(&now); + } +} diff --git a/libbb/bb_pwd.c b/libbb/bb_pwd.c new file mode 100644 index 0000000..65eb692 --- /dev/null +++ b/libbb/bb_pwd.c @@ -0,0 +1,99 @@ +/* vi: set sw=4 ts=4: */ +/* + * password utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include "libbb.h" + +#define assert(x) ((void)0) + +/* internal function for bb_getpwuid and bb_getgrgid */ +/* Hacked by Tito Ragusa (c) 2004 to make it more + * flexible: + * + * bufsize > 0: If idname is not NULL it is copied to buffer, + * and buffer is returned. Else id as string is written + * to buffer, and NULL is returned. + * + * bufsize == 0: idname is returned. + * + * bufsize < 0: If idname is not NULL it is returned. + * Else an error message is printed and the program exits. + */ +static char* bb_getug(char *buffer, int bufsize, char *idname, long id, char prefix) +{ + if (bufsize > 0) { + assert(buffer != NULL); + if (idname) { + return safe_strncpy(buffer, idname, bufsize); + } + snprintf(buffer, bufsize, "%ld", id); + } else if (bufsize < 0 && !idname) { + bb_error_msg_and_die("unknown %cid %ld", prefix, id); + } + return idname; +} + +/* bb_getpwuid, bb_getgrgid: + * bb_getXXXid(buf, bufsz, id) - copy user/group name or id + * as a string to buf, return user/group name or NULL + * bb_getXXXid(NULL, 0, id) - return user/group name or NULL + * bb_getXXXid(NULL, -1, id) - return user/group name or exit + */ +/* gets a username given a uid */ +char* FAST_FUNC bb_getpwuid(char *name, int bufsize, long uid) +{ + struct passwd *myuser = getpwuid(uid); + + return bb_getug(name, bufsize, + (myuser ? myuser->pw_name : (char*)myuser), + uid, 'u'); +} +/* gets a groupname given a gid */ +char* FAST_FUNC bb_getgrgid(char *group, int bufsize, long gid) +{ + struct group *mygroup = getgrgid(gid); + + return bb_getug(group, bufsize, + (mygroup ? mygroup->gr_name : (char*)mygroup), + gid, 'g'); +} + +/* returns a gid given a group name */ +long FAST_FUNC xgroup2gid(const char *name) +{ + struct group *mygroup; + + mygroup = getgrnam(name); + if (mygroup == NULL) + bb_error_msg_and_die("unknown group name: %s", name); + + return mygroup->gr_gid; +} + +/* returns a uid given a username */ +long FAST_FUNC xuname2uid(const char *name) +{ + struct passwd *myuser; + + myuser = getpwnam(name); + if (myuser == NULL) + bb_error_msg_and_die("unknown user %s", name); + + return myuser->pw_uid; +} + +unsigned long FAST_FUNC get_ug_id(const char *s, + long FAST_FUNC (*xname2id)(const char *)) +{ + unsigned long r; + + r = bb_strtoul(s, NULL, 10); + if (errno) + return xname2id(s); + return r; +} diff --git a/libbb/bb_qsort.c b/libbb/bb_qsort.c new file mode 100644 index 0000000..9773afa --- /dev/null +++ b/libbb/bb_qsort.c @@ -0,0 +1,20 @@ +/* vi: set sw=4 ts=4: */ +/* + * Wrapper for common string vector sorting operation + * + * Copyright (c) 2008 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +int /* not FAST_FUNC! */ bb_pstrcmp(const void *a, const void *b) +{ + return strcmp(*(char**)a, *(char**)b); +} + +void FAST_FUNC qsort_string_vector(char **sv, unsigned count) +{ + qsort(sv, count, sizeof(char*), bb_pstrcmp); +} diff --git a/libbb/bb_strtod.c b/libbb/bb_strtod.c new file mode 100644 index 0000000..39bdeb5 --- /dev/null +++ b/libbb/bb_strtod.c @@ -0,0 +1,86 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" +#include /* just for HUGE_VAL */ + +#define NOT_DIGIT(a) (((unsigned char)(a-'0')) > 9) + +double FAST_FUNC bb_strtod(const char *arg, char **endp) +{ + double v; + char *endptr; + + /* Allow .NN form. People want to use "sleep .15" etc */ + if (arg[0] != '-' && arg[0] != '.' && NOT_DIGIT(arg[0])) + goto err; + errno = 0; + v = strtod(arg, &endptr); + if (endp) + *endp = endptr; + if (endptr[0]) { + /* "1234abcg" or out-of-range? */ + if (isalnum(endptr[0]) || errno) { + err: + errno = ERANGE; + return HUGE_VAL; + } + /* good number, just suspicious terminator */ + errno = EINVAL; + } + return v; +} + +#if 0 +/* String to timespec: "NNNN[.NNNNN]" -> struct timespec. + * Can be used for other fixed-point needs. + * Returns pointer past last converted char, + * and returns errno similar to bb_strtoXX functions. + */ +char* FAST_FUNC bb_str_to_ts(struct timespec *ts, const char *arg) +{ + if (sizeof(ts->tv_sec) <= sizeof(int)) + ts->tv_sec = bb_strtou(arg, &arg, 10); + else if (sizeof(ts->tv_sec) <= sizeof(long)) + ts->tv_sec = bb_strtoul(arg, &arg, 10); + else + ts->tv_sec = bb_strtoull(arg, &arg, 10); + ts->tv_nsec = 0; + + if (*arg != '.') + return arg; + + /* !EINVAL: number is not ok (alphanumeric ending, overflow etc) */ + if (errno != EINVAL) + return arg; + + if (!*++arg) /* "NNN." */ + return arg; + + { /* "NNN.xxx" - parse xxx */ + int ndigits; + char *p; + char buf[10]; /* we never use more than 9 digits */ + + /* Need to make a copy to avoid false overflow */ + safe_strncpy(buf, arg, 10); + ts->tv_nsec = bb_strtou(buf, &p, 10); + ndigits = p - buf; + arg += ndigits; + /* normalize to nsec */ + while (ndigits < 9) { + ndigits++; + ts->tv_nsec *= 10; + } + while (isdigit(*arg)) /* skip possible 10th plus digits */ + arg++; + } + return arg; +} +#endif diff --git a/libbb/bb_strtonum.c b/libbb/bb_strtonum.c new file mode 100644 index 0000000..87cd744 --- /dev/null +++ b/libbb/bb_strtonum.c @@ -0,0 +1,139 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* On exit: errno = 0 only if there was non-empty, '\0' terminated value + * errno = EINVAL if value was not '\0' terminated, but otherwise ok + * Return value is still valid, caller should just check whether end[0] + * is a valid terminating char for particular case. OTOH, if caller + * requires '\0' terminated input, [s]he can just check errno == 0. + * errno = ERANGE if value had alphanumeric terminating char ("1234abcg"). + * errno = ERANGE if value is out of range, missing, etc. + * errno = ERANGE if value had minus sign for strtouXX (even "-0" is not ok ) + * return value is all-ones in this case. + * + * Test code: + * char *endptr; + * const char *minus = "-"; + * errno = 0; + * bb_strtoi(minus, &endptr, 0); // must set ERANGE + * printf("minus:%p endptr:%p errno:%d EINVAL:%d\n", minus, endptr, errno, EINVAL); + * errno = 0; + * bb_strtoi("-0-", &endptr, 0); // must set EINVAL and point to second '-' + * printf("endptr[0]:%c errno:%d EINVAL:%d\n", endptr[0], errno, EINVAL); + */ + +static unsigned long long ret_ERANGE(void) +{ + errno = ERANGE; /* this ain't as small as it looks (on glibc) */ + return ULLONG_MAX; +} + +static unsigned long long handle_errors(unsigned long long v, char **endp, char *endptr) +{ + if (endp) *endp = endptr; + + /* errno is already set to ERANGE by strtoXXX if value overflowed */ + if (endptr[0]) { + /* "1234abcg" or out-of-range? */ + if (isalnum(endptr[0]) || errno) + return ret_ERANGE(); + /* good number, just suspicious terminator */ + errno = EINVAL; + } + return v; +} + + +unsigned long long FAST_FUNC bb_strtoull(const char *arg, char **endp, int base) +{ + unsigned long long v; + char *endptr; + + /* strtoul(" -4200000000") returns 94967296, errno 0 (!) */ + /* I don't think that this is right. Preventing this... */ + if (!isalnum(arg[0])) return ret_ERANGE(); + + /* not 100% correct for lib func, but convenient for the caller */ + errno = 0; + v = strtoull(arg, &endptr, base); + return handle_errors(v, endp, endptr); +} + +long long FAST_FUNC bb_strtoll(const char *arg, char **endp, int base) +{ + unsigned long long v; + char *endptr; + + /* Check for the weird "feature": + * a "-" string is apparently a valid "number" for strto[u]l[l]! + * It returns zero and errno is 0! :( */ + char first = (arg[0] != '-' ? arg[0] : arg[1]); + if (!isalnum(first)) return ret_ERANGE(); + + errno = 0; + v = strtoll(arg, &endptr, base); + return handle_errors(v, endp, endptr); +} + +#if ULONG_MAX != ULLONG_MAX +unsigned long FAST_FUNC bb_strtoul(const char *arg, char **endp, int base) +{ + unsigned long v; + char *endptr; + + if (!isalnum(arg[0])) return ret_ERANGE(); + errno = 0; + v = strtoul(arg, &endptr, base); + return handle_errors(v, endp, endptr); +} + +long FAST_FUNC bb_strtol(const char *arg, char **endp, int base) +{ + long v; + char *endptr; + + char first = (arg[0] != '-' ? arg[0] : arg[1]); + if (!isalnum(first)) return ret_ERANGE(); + + errno = 0; + v = strtol(arg, &endptr, base); + return handle_errors(v, endp, endptr); +} +#endif + +#if UINT_MAX != ULONG_MAX +unsigned FAST_FUNC bb_strtou(const char *arg, char **endp, int base) +{ + unsigned long v; + char *endptr; + + if (!isalnum(arg[0])) return ret_ERANGE(); + errno = 0; + v = strtoul(arg, &endptr, base); + if (v > UINT_MAX) return ret_ERANGE(); + return handle_errors(v, endp, endptr); +} + +int FAST_FUNC bb_strtoi(const char *arg, char **endp, int base) +{ + long v; + char *endptr; + + char first = (arg[0] != '-' ? arg[0] : arg[1]); + if (!isalnum(first)) return ret_ERANGE(); + + errno = 0; + v = strtol(arg, &endptr, base); + if (v > INT_MAX) return ret_ERANGE(); + if (v < INT_MIN) return ret_ERANGE(); + return handle_errors(v, endp, endptr); +} +#endif diff --git a/libbb/change_identity.c b/libbb/change_identity.c new file mode 100644 index 0000000..619db09 --- /dev/null +++ b/libbb/change_identity.c @@ -0,0 +1,41 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 1989 - 1991, Julianne Frances Haugh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libbb.h" + +/* Become the user and group(s) specified by PW. */ +void FAST_FUNC change_identity(const struct passwd *pw) +{ + if (initgroups(pw->pw_name, pw->pw_gid) == -1) + bb_perror_msg_and_die("can't set groups"); + endgrent(); /* helps to close a fd used internally by libc */ + xsetgid(pw->pw_gid); + xsetuid(pw->pw_uid); +} diff --git a/libbb/chomp.c b/libbb/chomp.c new file mode 100644 index 0000000..ed4bf6b --- /dev/null +++ b/libbb/chomp.c @@ -0,0 +1,19 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. + * If you wrote this, please acknowledge your work. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +void FAST_FUNC chomp(char *s) +{ + char *lc = last_char_is(s, '\n'); + + if (lc) + *lc = '\0'; +} diff --git a/libbb/compare_string_array.c b/libbb/compare_string_array.c new file mode 100644 index 0000000..43c59e8 --- /dev/null +++ b/libbb/compare_string_array.c @@ -0,0 +1,78 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* returns the array index of the string */ +/* (index of first match is returned, or -1) */ +int FAST_FUNC index_in_str_array(const char *const string_array[], const char *key) +{ + int i; + + for (i = 0; string_array[i] != 0; i++) { + if (strcmp(string_array[i], key) == 0) { + return i; + } + } + return -1; +} + +int FAST_FUNC index_in_strings(const char *strings, const char *key) +{ + int idx = 0; + + while (*strings) { + if (strcmp(strings, key) == 0) { + return idx; + } + strings += strlen(strings) + 1; /* skip NUL */ + idx++; + } + return -1; +} + +/* returns the array index of the string, even if it matches only a beginning */ +/* (index of first match is returned, or -1) */ +#ifdef UNUSED +int FAST_FUNC index_in_substr_array(const char *const string_array[], const char *key) +{ + int i; + int len = strlen(key); + if (len) { + for (i = 0; string_array[i] != 0; i++) { + if (strncmp(string_array[i], key, len) == 0) { + return i; + } + } + } + return -1; +} +#endif + +int FAST_FUNC index_in_substrings(const char *strings, const char *key) +{ + int len = strlen(key); + + if (len) { + int idx = 0; + while (*strings) { + if (strncmp(strings, key, len) == 0) { + return idx; + } + strings += strlen(strings) + 1; /* skip NUL */ + idx++; + } + } + return -1; +} + +const char* FAST_FUNC nth_string(const char *strings, int n) +{ + while (n) { + n--; + strings += strlen(strings) + 1; + } + return strings; +} diff --git a/libbb/concat_path_file.c b/libbb/concat_path_file.c new file mode 100644 index 0000000..fb53354 --- /dev/null +++ b/libbb/concat_path_file.c @@ -0,0 +1,29 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. + * If you wrote this, please acknowledge your work. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* Concatenate path and filename to new allocated buffer. + * Add '/' only as needed (no duplicate // are produced). + * If path is NULL, it is assumed to be "/". + * filename should not be NULL. + */ + +#include "libbb.h" + +char* FAST_FUNC concat_path_file(const char *path, const char *filename) +{ + char *lc; + + if (!path) + path = ""; + lc = last_char_is(path, '/'); + while (*filename == '/') + filename++; + return xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename); +} diff --git a/libbb/concat_subpath_file.c b/libbb/concat_subpath_file.c new file mode 100644 index 0000000..313fa63 --- /dev/null +++ b/libbb/concat_subpath_file.c @@ -0,0 +1,23 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) (C) 2003 Vladimir Oleynik + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* + This function make special for recursive actions with usage + concat_path_file(path, filename) + and skipping "." and ".." directory entries +*/ + +#include "libbb.h" + +char* FAST_FUNC concat_subpath_file(const char *path, const char *f) +{ + if (f && DOT_OR_DOTDOT(f)) + return NULL; + return concat_path_file(path, f); +} diff --git a/libbb/copy_file.c b/libbb/copy_file.c new file mode 100644 index 0000000..d804ecc --- /dev/null +++ b/libbb/copy_file.c @@ -0,0 +1,399 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini copy_file implementation for busybox + * + * Copyright (C) 2001 by Matt Kraai + * SELinux support by Yuichi Nakamura + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + */ + +#include "libbb.h" + +// POSIX: if exists and -i, ask (w/o -i assume yes). +// Then open w/o EXCL (yes, not unlink!). +// If open still fails and -f, try unlink, then try open again. +// Result: a mess: +// If dest is a softlink, we overwrite softlink's destination! +// (or fail, if it points to dir/nonexistent location/etc). +// This is strange, but POSIX-correct. +// coreutils cp has --remove-destination to override this... +// +// NB: we have special code which still allows for "cp file /dev/node" +// to work POSIX-ly (the only realistic case where it makes sense) + +#define DO_POSIX_CP 0 /* 1 - POSIX behavior, 0 - safe behavior */ + +// errno must be set to relevant value ("why we cannot create dest?") +// for POSIX mode to give reasonable error message +static int ask_and_unlink(const char *dest, int flags) +{ + int e = errno; +#if DO_POSIX_CP + if (!(flags & (FILEUTILS_FORCE|FILEUTILS_INTERACTIVE))) { + // Either it exists, or the *path* doesnt exist + bb_perror_msg("cannot create '%s'", dest); + return -1; + } +#endif + // If !DO_POSIX_CP, act as if -f is always in effect - we don't want + // "cannot create" msg, we want unlink to be done (silently unless -i). + + // TODO: maybe we should do it only if ctty is present? + if (flags & FILEUTILS_INTERACTIVE) { + // We would not do POSIX insanity. -i asks, + // then _unlinks_ the offender. Presto. + // (No "opening without O_EXCL", no "unlink only if -f") + // Or else we will end up having 3 open()s! + fprintf(stderr, "%s: overwrite '%s'? ", applet_name, dest); + if (!bb_ask_confirmation()) + return 0; // not allowed to overwrite + } + if (unlink(dest) < 0) { +#if ENABLE_FEATURE_VERBOSE_CP_MESSAGE + if (e == errno && e == ENOENT) { + /* e == ENOTDIR is similar: path has non-dir component, + * but in this case we don't even reach copy_file() */ + bb_error_msg("cannot create '%s': Path does not exist", dest); + return -1; // error + } +#endif + errno = e; + bb_perror_msg("cannot create '%s'", dest); + return -1; // error + } + return 1; // ok (to try again) +} + +/* Return: + * -1 error, copy not made + * 0 copy is made or user answered "no" in interactive mode + * (failures to preserve mode/owner/times are not reported in exit code) + */ +int FAST_FUNC copy_file(const char *source, const char *dest, int flags) +{ + /* This is a recursive function, try to minimize stack usage */ + /* NB: each struct stat is ~100 bytes */ + struct stat source_stat; + struct stat dest_stat; + signed char retval = 0; + signed char dest_exists = 0; + signed char ovr; + +/* Inverse of cp -d ("cp without -d") */ +#define FLAGS_DEREF (flags & FILEUTILS_DEREFERENCE) + + if ((FLAGS_DEREF ? stat : lstat)(source, &source_stat) < 0) { + // This may be a dangling symlink. + // Making [sym]links to dangling symlinks works, so... + if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) + goto make_links; + bb_perror_msg("cannot stat '%s'", source); + return -1; + } + + if (lstat(dest, &dest_stat) < 0) { + if (errno != ENOENT) { + bb_perror_msg("cannot stat '%s'", dest); + return -1; + } + } else { + if (source_stat.st_dev == dest_stat.st_dev + && source_stat.st_ino == dest_stat.st_ino + ) { + bb_error_msg("'%s' and '%s' are the same file", source, dest); + return -1; + } + dest_exists = 1; + } + +#if ENABLE_SELINUX + if ((flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) && is_selinux_enabled() > 0) { + security_context_t con; + if (lgetfilecon(source, &con) >= 0) { + if (setfscreatecon(con) < 0) { + bb_perror_msg("cannot set setfscreatecon %s", con); + freecon(con); + return -1; + } + } else if (errno == ENOTSUP || errno == ENODATA) { + setfscreatecon_or_die(NULL); + } else { + bb_perror_msg("cannot lgetfilecon %s", source); + return -1; + } + } +#endif + + if (S_ISDIR(source_stat.st_mode)) { + DIR *dp; + const char *tp; + struct dirent *d; + mode_t saved_umask = 0; + + if (!(flags & FILEUTILS_RECUR)) { + bb_error_msg("omitting directory '%s'", source); + return -1; + } + + /* Did we ever create source ourself before? */ + tp = is_in_ino_dev_hashtable(&source_stat); + if (tp) { + /* We did! it's a recursion! man the lifeboats... */ + bb_error_msg("recursion detected, omitting directory '%s'", + source); + return -1; + } + + /* Create DEST */ + if (dest_exists) { + if (!S_ISDIR(dest_stat.st_mode)) { + bb_error_msg("target '%s' is not a directory", dest); + return -1; + } + /* race here: user can substitute a symlink between + * this check and actual creation of files inside dest */ + } else { + mode_t mode; + saved_umask = umask(0); + + mode = source_stat.st_mode; + if (!(flags & FILEUTILS_PRESERVE_STATUS)) + mode = source_stat.st_mode & ~saved_umask; + /* Allow owner to access new dir (at least for now) */ + mode |= S_IRWXU; + if (mkdir(dest, mode) < 0) { + umask(saved_umask); + bb_perror_msg("cannot create directory '%s'", dest); + return -1; + } + umask(saved_umask); + /* need stat info for add_to_ino_dev_hashtable */ + if (lstat(dest, &dest_stat) < 0) { + bb_perror_msg("cannot stat '%s'", dest); + return -1; + } + } + /* remember (dev,inode) of each created dir. + * NULL: name is not remembered */ + add_to_ino_dev_hashtable(&dest_stat, NULL); + + /* Recursively copy files in SOURCE */ + dp = opendir(source); + if (dp == NULL) { + retval = -1; + goto preserve_mode_ugid_time; + } + + while ((d = readdir(dp)) != NULL) { + char *new_source, *new_dest; + + new_source = concat_subpath_file(source, d->d_name); + if (new_source == NULL) + continue; + new_dest = concat_path_file(dest, d->d_name); + if (copy_file(new_source, new_dest, flags) < 0) + retval = -1; + free(new_source); + free(new_dest); + } + closedir(dp); + + if (!dest_exists + && chmod(dest, source_stat.st_mode & ~saved_umask) < 0 + ) { + bb_perror_msg("cannot preserve %s of '%s'", "permissions", dest); + /* retval = -1; - WRONG! copy *WAS* made */ + } + goto preserve_mode_ugid_time; + } + + if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) { + int (*lf)(const char *oldpath, const char *newpath); + make_links: + // Hmm... maybe + // if (DEREF && MAKE_SOFTLINK) source = realpath(source) ? + // (but realpath returns NULL on dangling symlinks...) + lf = (flags & FILEUTILS_MAKE_SOFTLINK) ? symlink : link; + if (lf(source, dest) < 0) { + ovr = ask_and_unlink(dest, flags); + if (ovr <= 0) + return ovr; + if (lf(source, dest) < 0) { + bb_perror_msg("cannot create link '%s'", dest); + return -1; + } + } + /* _Not_ jumping to preserve_mode_ugid_time: + * hard/softlinks don't have those */ + return 0; + } + + if (/* "cp thing1 thing2" without -R: just open and read() from thing1 */ + !(flags & FILEUTILS_RECUR) + /* "cp [-opts] regular_file thing2" */ + || S_ISREG(source_stat.st_mode) + /* DEREF uses stat, which never returns S_ISLNK() == true. + * So the below is never true: */ + /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */ + ) { + int src_fd; + int dst_fd; + mode_t new_mode; + + if (!FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) { + /* "cp -d symlink dst": create a link */ + goto dont_cat; + } + + if (ENABLE_FEATURE_PRESERVE_HARDLINKS && !FLAGS_DEREF) { + const char *link_target; + link_target = is_in_ino_dev_hashtable(&source_stat); + if (link_target) { + if (link(link_target, dest) < 0) { + ovr = ask_and_unlink(dest, flags); + if (ovr <= 0) + return ovr; + if (link(link_target, dest) < 0) { + bb_perror_msg("cannot create link '%s'", dest); + return -1; + } + } + return 0; + } + add_to_ino_dev_hashtable(&source_stat, dest); + } + + src_fd = open_or_warn(source, O_RDONLY); + if (src_fd < 0) + return -1; + + /* Do not try to open with weird mode fields */ + new_mode = source_stat.st_mode; + if (!S_ISREG(source_stat.st_mode)) + new_mode = 0666; + + /* POSIX way is a security problem versus symlink attacks, + * we do it only for non-symlinks, and only for non-recursive, + * non-interactive cp. NB: it is still racy + * for "cp file /home/bad_user/file" case + * (user can rm file and create a link to /etc/passwd) */ + if (DO_POSIX_CP + || (dest_exists + && !(flags & (FILEUTILS_RECUR|FILEUTILS_INTERACTIVE)) + && !S_ISLNK(dest_stat.st_mode)) + ) { + dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); + } else /* safe way: */ + dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); + if (dst_fd == -1) { + ovr = ask_and_unlink(dest, flags); + if (ovr <= 0) { + close(src_fd); + return ovr; + } + /* It shouldn't exist. If it exists, do not open (symlink attack?) */ + dst_fd = open3_or_warn(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); + if (dst_fd < 0) { + close(src_fd); + return -1; + } + } + +#if ENABLE_SELINUX + if ((flags & (FILEUTILS_PRESERVE_SECURITY_CONTEXT|FILEUTILS_SET_SECURITY_CONTEXT)) + && is_selinux_enabled() > 0 + ) { + security_context_t con; + if (getfscreatecon(&con) == -1) { + bb_perror_msg("getfscreatecon"); + return -1; + } + if (con) { + if (setfilecon(dest, con) == -1) { + bb_perror_msg("setfilecon:%s,%s", dest, con); + freecon(con); + return -1; + } + freecon(con); + } + } +#endif + if (bb_copyfd_eof(src_fd, dst_fd) == -1) + retval = -1; + /* Ok, writing side I can understand... */ + if (close(dst_fd) < 0) { + bb_perror_msg("cannot close '%s'", dest); + retval = -1; + } + /* ...but read size is already checked by bb_copyfd_eof */ + close(src_fd); + /* "cp /dev/something new_file" should not + * copy mode of /dev/something */ + if (!S_ISREG(source_stat.st_mode)) + return retval; + goto preserve_mode_ugid_time; + } + dont_cat: + + /* Source is a symlink or a special file */ + /* We are lazy here, a bit lax with races... */ + if (dest_exists) { + errno = EEXIST; + ovr = ask_and_unlink(dest, flags); + if (ovr <= 0) + return ovr; + } + if (S_ISLNK(source_stat.st_mode)) { + char *lpath = xmalloc_readlink_or_warn(source); + if (lpath) { + int r = symlink(lpath, dest); + free(lpath); + if (r < 0) { + bb_perror_msg("cannot create symlink '%s'", dest); + return -1; + } + if (flags & FILEUTILS_PRESERVE_STATUS) + if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0) + bb_perror_msg("cannot preserve %s of '%s'", "ownership", dest); + } + /* _Not_ jumping to preserve_mode_ugid_time: + * symlinks don't have those */ + return 0; + } + if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) + || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) + ) { + if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) { + bb_perror_msg("cannot create '%s'", dest); + return -1; + } + } else { + bb_error_msg("unrecognized file '%s' with mode %x", source, source_stat.st_mode); + return -1; + } + + preserve_mode_ugid_time: + + if (flags & FILEUTILS_PRESERVE_STATUS + /* Cannot happen: */ + /* && !(flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) */ + ) { + struct utimbuf times; + + times.actime = source_stat.st_atime; + times.modtime = source_stat.st_mtime; + /* BTW, utimes sets usec-precision time - just FYI */ + if (utime(dest, ×) < 0) + bb_perror_msg("cannot preserve %s of '%s'", "times", dest); + if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) { + source_stat.st_mode &= ~(S_ISUID | S_ISGID); + bb_perror_msg("cannot preserve %s of '%s'", "ownership", dest); + } + if (chmod(dest, source_stat.st_mode) < 0) + bb_perror_msg("cannot preserve %s of '%s'", "permissions", dest); + } + + return retval; +} diff --git a/libbb/copyfd.c b/libbb/copyfd.c new file mode 100644 index 0000000..c5f8b5b --- /dev/null +++ b/libbb/copyfd.c @@ -0,0 +1,119 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2005 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* Used by NOFORK applets (e.g. cat) - must not use xmalloc */ + +static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size) +{ + int status = -1; + off_t total = 0; +#if CONFIG_FEATURE_COPYBUF_KB <= 4 + char buffer[CONFIG_FEATURE_COPYBUF_KB * 1024]; + enum { buffer_size = sizeof(buffer) }; +#else + char *buffer; + int buffer_size; + + /* We want page-aligned buffer, just in case kernel is clever + * and can do page-aligned io more efficiently */ + buffer = mmap(NULL, CONFIG_FEATURE_COPYBUF_KB * 1024, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + /* ignored: */ -1, 0); + buffer_size = CONFIG_FEATURE_COPYBUF_KB * 1024; + if (buffer == MAP_FAILED) { + buffer = alloca(4 * 1024); + buffer_size = 4 * 1024; + } +#endif + + if (src_fd < 0) + goto out; + + if (!size) { + size = buffer_size; + status = 1; /* copy until eof */ + } + + while (1) { + ssize_t rd; + + rd = safe_read(src_fd, buffer, size > buffer_size ? buffer_size : size); + + if (!rd) { /* eof - all done */ + status = 0; + break; + } + if (rd < 0) { + bb_perror_msg(bb_msg_read_error); + break; + } + /* dst_fd == -1 is a fake, else... */ + if (dst_fd >= 0) { + ssize_t wr = full_write(dst_fd, buffer, rd); + if (wr < rd) { + bb_perror_msg(bb_msg_write_error); + break; + } + } + total += rd; + if (status < 0) { /* if we aren't copying till EOF... */ + size -= rd; + if (!size) { + /* 'size' bytes copied - all done */ + status = 0; + break; + } + } + } + out: + +#if CONFIG_FEATURE_COPYBUF_KB > 4 + if (buffer_size != 4 * 1024) + munmap(buffer, buffer_size); +#endif + return status ? -1 : total; +} + + +#if 0 +void FAST_FUNC complain_copyfd_and_die(off_t sz) +{ + if (sz != -1) + bb_error_msg_and_die("short read"); + /* if sz == -1, bb_copyfd_XX already complained */ + xfunc_die(); +} +#endif + +off_t FAST_FUNC bb_copyfd_size(int fd1, int fd2, off_t size) +{ + if (size) { + return bb_full_fd_action(fd1, fd2, size); + } + return 0; +} + +void FAST_FUNC bb_copyfd_exact_size(int fd1, int fd2, off_t size) +{ + off_t sz = bb_copyfd_size(fd1, fd2, size); + if (sz == size) + return; + if (sz != -1) + bb_error_msg_and_die("short read"); + /* if sz == -1, bb_copyfd_XX already complained */ + xfunc_die(); +} + +off_t FAST_FUNC bb_copyfd_eof(int fd1, int fd2) +{ + return bb_full_fd_action(fd1, fd2, 0); +} diff --git a/libbb/correct_password.c b/libbb/correct_password.c new file mode 100644 index 0000000..255b048 --- /dev/null +++ b/libbb/correct_password.c @@ -0,0 +1,80 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 1989 - 1991, Julianne Frances Haugh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libbb.h" + +/* Ask the user for a password. + * Return 1 if the user gives the correct password for entry PW, + * 0 if not. Return 1 without asking if PW has an empty password. + * + * NULL pw means "just fake it for login with bad username" */ + +int FAST_FUNC correct_password(const struct passwd *pw) +{ + char *unencrypted, *encrypted; + const char *correct; + int r; +#if ENABLE_FEATURE_SHADOWPASSWDS + /* Using _r function to avoid pulling in static buffers */ + struct spwd spw; + char buffer[256]; +#endif + + /* fake salt. crypt() can choke otherwise. */ + correct = "aa"; + if (!pw) { + /* "aa" will never match */ + goto fake_it; + } + correct = pw->pw_passwd; +#if ENABLE_FEATURE_SHADOWPASSWDS + if ((correct[0] == 'x' || correct[0] == '*') && !correct[1]) { + /* getspnam_r may return 0 yet set result to NULL. + * At least glibc 2.4 does this. Be extra paranoid here. */ + struct spwd *result = NULL; + r = getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result); + correct = (r || !result) ? "aa" : result->sp_pwdp; + } +#endif + + if (!correct[0]) /* empty password field? */ + return 1; + + fake_it: + unencrypted = bb_askpass(0, "Password: "); + if (!unencrypted) { + return 0; + } + encrypted = pw_encrypt(unencrypted, correct, 1); + r = (strcmp(encrypted, correct) == 0); + free(encrypted); + memset(unencrypted, 0, strlen(unencrypted)); + return r; +} diff --git a/libbb/crc32.c b/libbb/crc32.c new file mode 100644 index 0000000..42079b9 --- /dev/null +++ b/libbb/crc32.c @@ -0,0 +1,40 @@ +/* vi: set sw=4 ts=4: */ +/* + * CRC32 table fill function + * Copyright (C) 2006 by Rob Sullivan + * (I can't really claim much credit however, as the algorithm is + * very well-known) + * + * The following function creates a CRC32 table depending on whether + * a big-endian (0x04c11db7) or little-endian (0xedb88320) CRC32 is + * required. Admittedly, there are other CRC32 polynomials floating + * around, but Busybox doesn't use them. + * + * endian = 1: big-endian + * endian = 0: little-endian + */ + +#include "libbb.h" + +uint32_t* FAST_FUNC crc32_filltable(uint32_t *crc_table, int endian) +{ + uint32_t polynomial = endian ? 0x04c11db7 : 0xedb88320; + uint32_t c; + int i, j; + + if (!crc_table) + crc_table = xmalloc(256 * sizeof(uint32_t)); + + for (i = 0; i < 256; i++) { + c = endian ? (i << 24) : i; + for (j = 8; j; j--) { + if (endian) + c = (c&0x80000000) ? ((c << 1) ^ polynomial) : (c << 1); + else + c = (c&1) ? ((c >> 1) ^ polynomial) : (c >> 1); + } + *crc_table++ = c; + } + + return crc_table - 256; +} diff --git a/libbb/create_icmp6_socket.c b/libbb/create_icmp6_socket.c new file mode 100644 index 0000000..2065517 --- /dev/null +++ b/libbb/create_icmp6_socket.c @@ -0,0 +1,36 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * create raw socket for icmp (IPv6 version) protocol + * and drop root privileges if running setuid + */ + +#include "libbb.h" + +#if ENABLE_FEATURE_IPV6 +int FAST_FUNC create_icmp6_socket(void) +{ + int sock; +#if 0 + struct protoent *proto; + proto = getprotobyname("ipv6-icmp"); + /* if getprotobyname failed, just silently force + * proto->p_proto to have the correct value for "ipv6-icmp" */ + sock = socket(AF_INET6, SOCK_RAW, + (proto ? proto->p_proto : IPPROTO_ICMPV6)); +#else + sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); +#endif + if (sock < 0) { + if (errno == EPERM) + bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); + bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket); + } + + /* drop root privs if running setuid */ + xsetuid(getuid()); + + return sock; +} +#endif diff --git a/libbb/create_icmp_socket.c b/libbb/create_icmp_socket.c new file mode 100644 index 0000000..1fa016a --- /dev/null +++ b/libbb/create_icmp_socket.c @@ -0,0 +1,34 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * create raw socket for icmp protocol + * and drop root privileges if running setuid + */ + +#include "libbb.h" + +int FAST_FUNC create_icmp_socket(void) +{ + int sock; +#if 0 + struct protoent *proto; + proto = getprotobyname("icmp"); + /* if getprotobyname failed, just silently force + * proto->p_proto to have the correct value for "icmp" */ + sock = socket(AF_INET, SOCK_RAW, + (proto ? proto->p_proto : 1)); /* 1 == ICMP */ +#else + sock = socket(AF_INET, SOCK_RAW, 1); /* 1 == ICMP */ +#endif + if (sock < 0) { + if (errno == EPERM) + bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); + bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket); + } + + /* drop root privs if running setuid */ + xsetuid(getuid()); + + return sock; +} diff --git a/libbb/crypt_make_salt.c b/libbb/crypt_make_salt.c new file mode 100644 index 0000000..393eba5 --- /dev/null +++ b/libbb/crypt_make_salt.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * crypt_make_salt + * + * i64c was also put here, this is the only function that uses it. + * + * Lifted from loginutils/passwd.c by Thomas Lundquist + * + */ + +#include "libbb.h" + +static int i64c(int i) +{ + i &= 0x3f; + if (i == 0) + return '.'; + if (i == 1) + return '/'; + if (i < 12) + return ('0' - 2 + i); + if (i < 38) + return ('A' - 12 + i); + return ('a' - 38 + i); +} + +int FAST_FUNC crypt_make_salt(char *p, int cnt, int x) +{ + x += getpid() + time(NULL); + do { + /* x = (x*1664525 + 1013904223) % 2^32 generator is lame + * (low-order bit is not "random", etc...), + * but for our purposes it is good enough */ + x = x*1664525 + 1013904223; + /* BTW, Park and Miller's "minimal standard generator" is + * x = x*16807 % ((2^31)-1) + * It has no problem with visibly alternating lowest bit + * but is also weak in cryptographic sense + needs div, + * which needs more code (and slower) on many CPUs */ + *p++ = i64c(x >> 16); + *p++ = i64c(x >> 22); + } while (--cnt); + *p = '\0'; + return x; +} diff --git a/libbb/default_error_retval.c b/libbb/default_error_retval.c new file mode 100644 index 0000000..0b19f21 --- /dev/null +++ b/libbb/default_error_retval.c @@ -0,0 +1,18 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* Seems silly to copyright a global variable. ;-) Oh well. + * + * At least one applet (cmp) returns a value different from the typical + * EXIT_FAILURE values (1) when an error occurs. So, make it configurable + * by the applet. I suppose we could use a wrapper function to set it, but + * that too seems silly. + */ + +#include "libbb.h" + +int xfunc_error_retval = EXIT_FAILURE; diff --git a/libbb/device_open.c b/libbb/device_open.c new file mode 100644 index 0000000..cf8bcf6 --- /dev/null +++ b/libbb/device_open.c @@ -0,0 +1,32 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* try to open up the specified device */ +int FAST_FUNC device_open(const char *device, int mode) +{ + int m, f, fd; + + m = mode | O_NONBLOCK; + + /* Retry up to 5 times */ + /* TODO: explain why it can't be considered insane */ + for (f = 0; f < 5; f++) { + fd = open(device, m, 0600); + if (fd >= 0) + break; + } + if (fd < 0) + return fd; + /* Reset original flags. */ + if (m != mode) + fcntl(fd, F_SETFL, mode); + return fd; +} diff --git a/libbb/die_if_bad_username.c b/libbb/die_if_bad_username.c new file mode 100644 index 0000000..602aadc --- /dev/null +++ b/libbb/die_if_bad_username.c @@ -0,0 +1,36 @@ +/* vi: set sw=4 ts=4: */ +/* + * Ckeck user and group names for illegal characters + * + * Copyright (C) 2008 Tito Ragusa + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* To avoid problems, the username should consist only of + * letters, digits, underscores, periods, at signs and dashes, + * and not start with a dash (as defined by IEEE Std 1003.1-2001). + * For compatibility with Samba machine accounts $ is also supported + * at the end of the username. + */ + +void FAST_FUNC die_if_bad_username(const char *name) +{ + goto skip; /* 1st char being dash isn't valid */ + do { + if (*name == '-') + continue; + skip: + if (isalnum(*name) + || *name == '_' + || *name == '.' + || *name == '@' + || (*name == '$' && !*(name + 1)) + ) { + continue; + } + bb_error_msg_and_die("illegal character '%c'", *name); + } while (*++name); +} diff --git a/libbb/dump.c b/libbb/dump.c new file mode 100644 index 0000000..e7722de --- /dev/null +++ b/libbb/dump.c @@ -0,0 +1,839 @@ +/* vi: set sw=4 ts=4: */ +/* + * Support code for the hexdump and od applets, + * based on code from util-linux v 2.11l + * + * Copyright (c) 1989 + * The Regents of the University of California. All rights reserved. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + * Original copyright notice is retained at the end of this file. + */ + +#include "libbb.h" +#include "dump.h" + +static const char index_str[] ALIGN1 = ".#-+ 0123456789"; + +static const char size_conv_str[] ALIGN1 = +"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG"; + +static const char lcc[] ALIGN1 = "diouxX"; + + +typedef struct priv_dumper_t { + dumper_t pub; + + char **argv; + FU *endfu; + off_t savaddress; /* saved address/offset in stream */ + off_t eaddress; /* end address */ + off_t address; /* address/offset in stream */ + int blocksize; + smallint exitval; /* final exit value */ + + /* former statics */ + smallint next__done; + smallint get__ateof; // = 1; + unsigned char *get__curp; + unsigned char *get__savp; +} priv_dumper_t; + +dumper_t* FAST_FUNC alloc_dumper(void) +{ + priv_dumper_t *dumper = xzalloc(sizeof(*dumper)); + dumper->pub.dump_length = -1; + dumper->pub.dump_vflag = FIRST; + dumper->get__ateof = 1; + return &dumper->pub; +} + + +static NOINLINE int bb_dump_size(FS *fs) +{ + FU *fu; + int bcnt, cur_size; + char *fmt; + const char *p; + int prec; + + /* figure out the data block bb_dump_size needed for each format unit */ + for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) { + if (fu->bcnt) { + cur_size += fu->bcnt * fu->reps; + continue; + } + for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) { + if (*fmt != '%') + continue; + /* + * skip any special chars -- save precision in + * case it's a %s format. + */ + while (strchr(index_str + 1, *++fmt)); + if (*fmt == '.' && isdigit(*++fmt)) { + prec = atoi(fmt); + while (isdigit(*++fmt)) + continue; + } + p = strchr(size_conv_str + 12, *fmt); + if (!p) { + if (*fmt == 's') { + bcnt += prec; + } else if (*fmt == '_') { + ++fmt; + if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) { + bcnt += 1; + } + } + } else { + bcnt += size_conv_str[p - (size_conv_str + 12)]; + } + } + cur_size += bcnt * fu->reps; + } + return cur_size; +} + +static void rewrite(priv_dumper_t *dumper, FS *fs) +{ + enum { NOTOKAY, USEBCNT, USEPREC } sokay; + PR *pr, **nextpr = NULL; + FU *fu; + char *p1, *p2, *p3; + char savech, *fmtp; + const char *byte_count_str; + int nconv, prec = 0; + + for (fu = fs->nextfu; fu; fu = fu->nextfu) { + /* + * break each format unit into print units; each + * conversion character gets its own. + */ + for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) { + /* NOSTRICT */ + /* DBU:[dvae@cray.com] zalloc so that forward ptrs start out NULL*/ + pr = xzalloc(sizeof(PR)); + if (!fu->nextpr) + fu->nextpr = pr; + /* ignore nextpr -- its unused inside the loop and is + * uninitialized 1st time through. + */ + + /* skip preceding text and up to the next % sign */ + for (p1 = fmtp; *p1 && *p1 != '%'; ++p1) + continue; + + /* only text in the string */ + if (!*p1) { + pr->fmt = fmtp; + pr->flags = F_TEXT; + break; + } + + /* + * get precision for %s -- if have a byte count, don't + * need it. + */ + if (fu->bcnt) { + sokay = USEBCNT; + /* skip to conversion character */ + for (++p1; strchr(index_str, *p1); ++p1) + continue; + } else { + /* skip any special chars, field width */ + while (strchr(index_str + 1, *++p1)) + continue; + if (*p1 == '.' && isdigit(*++p1)) { + sokay = USEPREC; + prec = atoi(p1); + while (isdigit(*++p1)) + continue; + } else + sokay = NOTOKAY; + } + + p2 = p1 + 1; /* set end pointer */ + + /* + * figure out the byte count for each conversion; + * rewrite the format as necessary, set up blank- + * pbb_dump_adding for end of data. + */ + if (*p1 == 'c') { + pr->flags = F_CHAR; + DO_BYTE_COUNT_1: + byte_count_str = "\001"; + DO_BYTE_COUNT: + if (fu->bcnt) { + do { + if (fu->bcnt == *byte_count_str) { + break; + } + } while (*++byte_count_str); + } + /* Unlike the original, output the remainder of the format string. */ + if (!*byte_count_str) { + bb_error_msg_and_die("bad byte count for conversion character %s", p1); + } + pr->bcnt = *byte_count_str; + } else if (*p1 == 'l') { + ++p2; + ++p1; + DO_INT_CONV: + { + const char *e; + e = strchr(lcc, *p1); + if (!e) { + goto DO_BAD_CONV_CHAR; + } + pr->flags = F_INT; + if (e > lcc + 1) { + pr->flags = F_UINT; + } + byte_count_str = "\004\002\001"; + goto DO_BYTE_COUNT; + } + /* NOTREACHED */ + } else if (strchr(lcc, *p1)) { + goto DO_INT_CONV; + } else if (strchr("eEfgG", *p1)) { + pr->flags = F_DBL; + byte_count_str = "\010\004"; + goto DO_BYTE_COUNT; + } else if (*p1 == 's') { + pr->flags = F_STR; + if (sokay == USEBCNT) { + pr->bcnt = fu->bcnt; + } else if (sokay == USEPREC) { + pr->bcnt = prec; + } else { /* NOTOKAY */ + bb_error_msg_and_die("%%s requires a precision or a byte count"); + } + } else if (*p1 == '_') { + ++p2; + switch (p1[1]) { + case 'A': + dumper->endfu = fu; + fu->flags |= F_IGNORE; + /* FALLTHROUGH */ + case 'a': + pr->flags = F_ADDRESS; + ++p2; + if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) { + goto DO_BAD_CONV_CHAR; + } + *p1 = p1[2]; + break; + case 'c': + pr->flags = F_C; + /* *p1 = 'c'; set in conv_c */ + goto DO_BYTE_COUNT_1; + case 'p': + pr->flags = F_P; + *p1 = 'c'; + goto DO_BYTE_COUNT_1; + case 'u': + pr->flags = F_U; + /* *p1 = 'c'; set in conv_u */ + goto DO_BYTE_COUNT_1; + default: + goto DO_BAD_CONV_CHAR; + } + } else { + DO_BAD_CONV_CHAR: + bb_error_msg_and_die("bad conversion character %%%s", p1); + } + + /* + * copy to PR format string, set conversion character + * pointer, update original. + */ + savech = *p2; + p1[1] = '\0'; + pr->fmt = xstrdup(fmtp); + *p2 = savech; + //Too early! xrealloc can move pr->fmt! + //pr->cchar = pr->fmt + (p1 - fmtp); + + /* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost. + * Skip subsequent text and up to the next % sign and tack the + * additional text onto fmt: eg. if fmt is "%x is a HEX number", + * we lose the " is a HEX number" part of fmt. + */ + for (p3 = p2; *p3 && *p3 != '%'; p3++) + continue; + if (p3 > p2) { + savech = *p3; + *p3 = '\0'; + pr->fmt = xrealloc(pr->fmt, strlen(pr->fmt) + (p3-p2) + 1); + strcat(pr->fmt, p2); + *p3 = savech; + p2 = p3; + } + + pr->cchar = pr->fmt + (p1 - fmtp); + fmtp = p2; + + /* only one conversion character if byte count */ + if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) { + bb_error_msg_and_die("byte count with multiple conversion characters"); + } + } + /* + * if format unit byte count not specified, figure it out + * so can adjust rep count later. + */ + if (!fu->bcnt) + for (pr = fu->nextpr; pr; pr = pr->nextpr) + fu->bcnt += pr->bcnt; + } + /* + * if the format string interprets any data at all, and it's + * not the same as the blocksize, and its last format unit + * interprets any data at all, and has no iteration count, + * repeat it as necessary. + * + * if, rep count is greater than 1, no trailing whitespace + * gets output from the last iteration of the format unit. + */ + for (fu = fs->nextfu; fu; fu = fu->nextfu) { + if (!fu->nextfu && fs->bcnt < dumper->blocksize + && !(fu->flags & F_SETREP) && fu->bcnt + ) { + fu->reps += (dumper->blocksize - fs->bcnt) / fu->bcnt; + } + if (fu->reps > 1) { + for (pr = fu->nextpr;; pr = pr->nextpr) + if (!pr->nextpr) + break; + for (p1 = pr->fmt, p2 = NULL; *p1; ++p1) + p2 = isspace(*p1) ? p1 : NULL; + if (p2) + pr->nospace = p2; + } + if (!fu->nextfu) + break; + } +} + +static void do_skip(priv_dumper_t *dumper, const char *fname, int statok) +{ + struct stat sbuf; + + if (statok) { + if (fstat(STDIN_FILENO, &sbuf)) { + bb_simple_perror_msg_and_die(fname); + } + if (!(S_ISCHR(sbuf.st_mode) || S_ISBLK(sbuf.st_mode) || S_ISFIFO(sbuf.st_mode)) + && dumper->pub.dump_skip >= sbuf.st_size + ) { + /* If bb_dump_size valid and pub.dump_skip >= size */ + dumper->pub.dump_skip -= sbuf.st_size; + dumper->address += sbuf.st_size; + return; + } + } + if (fseek(stdin, dumper->pub.dump_skip, SEEK_SET)) { + bb_simple_perror_msg_and_die(fname); + } + dumper->address += dumper->pub.dump_skip; + dumper->savaddress = dumper->address; + dumper->pub.dump_skip = 0; +} + +static NOINLINE int next(priv_dumper_t *dumper) +{ + int statok; + + for (;;) { + if (*dumper->argv) { + if (!(freopen(*dumper->argv, "r", stdin))) { + bb_simple_perror_msg(*dumper->argv); + dumper->exitval = 1; + ++dumper->argv; + continue; + } + dumper->next__done = statok = 1; + } else { + if (dumper->next__done) + return 0; + dumper->next__done = 1; + statok = 0; + } + if (dumper->pub.dump_skip) + do_skip(dumper, statok ? *dumper->argv : "stdin", statok); + if (*dumper->argv) + ++dumper->argv; + if (!dumper->pub.dump_skip) + return 1; + } + /* NOTREACHED */ +} + +static unsigned char *get(priv_dumper_t *dumper) +{ + int n; + int need, nread; + int blocksize = dumper->blocksize; + + if (!dumper->get__curp) { + dumper->address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/ + dumper->get__curp = xmalloc(blocksize); + dumper->get__savp = xzalloc(blocksize); /* need to be initialized */ + } else { + unsigned char *tmp = dumper->get__curp; + dumper->get__curp = dumper->get__savp; + dumper->get__savp = tmp; + dumper->savaddress += blocksize; + dumper->address = dumper->savaddress; + } + need = blocksize; + nread = 0; + while (1) { + /* + * if read the right number of bytes, or at EOF for one file, + * and no other files are available, zero-pad the rest of the + * block and set the end flag. + */ + if (!dumper->pub.dump_length || (dumper->get__ateof && !next(dumper))) { + if (need == blocksize) { + return NULL; + } + if (dumper->pub.dump_vflag != ALL && !memcmp(dumper->get__curp, dumper->get__savp, nread)) { + if (dumper->pub.dump_vflag != DUP) { + puts("*"); + } + return NULL; + } + memset(dumper->get__curp + nread, 0, need); + dumper->eaddress = dumper->address + nread; + return dumper->get__curp; + } + n = fread(dumper->get__curp + nread, sizeof(unsigned char), + dumper->pub.dump_length == -1 ? need : MIN(dumper->pub.dump_length, need), stdin); + if (!n) { + if (ferror(stdin)) { + bb_simple_perror_msg(dumper->argv[-1]); + } + dumper->get__ateof = 1; + continue; + } + dumper->get__ateof = 0; + if (dumper->pub.dump_length != -1) { + dumper->pub.dump_length -= n; + } + need -= n; + if (!need) { + if (dumper->pub.dump_vflag == ALL || dumper->pub.dump_vflag == FIRST + || memcmp(dumper->get__curp, dumper->get__savp, blocksize) + ) { + if (dumper->pub.dump_vflag == DUP || dumper->pub.dump_vflag == FIRST) { + dumper->pub.dump_vflag = WAIT; + } + return dumper->get__curp; + } + if (dumper->pub.dump_vflag == WAIT) { + puts("*"); + } + dumper->pub.dump_vflag = DUP; + dumper->savaddress += blocksize; + dumper->address = dumper->savaddress; + need = blocksize; + nread = 0; + } else { + nread += n; + } + } +} + +static void bpad(PR *pr) +{ + char *p1, *p2; + + /* + * remove all conversion flags; '-' is the only one valid + * with %s, and it's not useful here. + */ + pr->flags = F_BPAD; + *pr->cchar = 's'; + for (p1 = pr->fmt; *p1 != '%'; ++p1) + continue; + for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1) + if (pr->nospace) + pr->nospace--; + while ((*p2++ = *p1++) != 0) + continue; +} + +static const char conv_str[] ALIGN1 = + "\0\\0\0" + "\007\\a\0" /* \a */ + "\b\\b\0" + "\f\\b\0" + "\n\\n\0" + "\r\\r\0" + "\t\\t\0" + "\v\\v\0" + ; + + +static void conv_c(PR *pr, unsigned char *p) +{ + const char *str = conv_str; + char buf[10]; + + do { + if (*p == *str) { + ++str; + goto strpr; + } + str += 4; + } while (*str); + + if (isprint(*p)) { + *pr->cchar = 'c'; + printf(pr->fmt, *p); + } else { + sprintf(buf, "%03o", (int) *p); + str = buf; + strpr: + *pr->cchar = 's'; + printf(pr->fmt, str); + } +} + +static void conv_u(PR *pr, unsigned char *p) +{ + static const char list[] ALIGN1 = + "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0" + "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_" + "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0" + "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us"; + + /* od used nl, not lf */ + if (*p <= 0x1f) { + *pr->cchar = 's'; + printf(pr->fmt, list + (4 * (int)*p)); + } else if (*p == 0x7f) { + *pr->cchar = 's'; + printf(pr->fmt, "del"); + } else if (isprint(*p)) { + *pr->cchar = 'c'; + printf(pr->fmt, *p); + } else { + *pr->cchar = 'x'; + printf(pr->fmt, (int) *p); + } +} + +static void display(priv_dumper_t* dumper) +{ + FS *fs; + FU *fu; + PR *pr; + int cnt; + unsigned char *bp, *savebp; + off_t saveaddress; + unsigned char savech = '\0'; + + while ((bp = get(dumper)) != NULL) { + fs = dumper->pub.fshead; + savebp = bp; + saveaddress = dumper->address; + for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) { + for (fu = fs->nextfu; fu; fu = fu->nextfu) { + if (fu->flags & F_IGNORE) { + break; + } + for (cnt = fu->reps; cnt; --cnt) { + for (pr = fu->nextpr; pr; dumper->address += pr->bcnt, + bp += pr->bcnt, pr = pr->nextpr) { + if (dumper->eaddress && dumper->address >= dumper->eaddress + && !(pr->flags & (F_TEXT | F_BPAD)) + ) { + bpad(pr); + } + if (cnt == 1 && pr->nospace) { + savech = *pr->nospace; + *pr->nospace = '\0'; + } +/* PRINT; */ + switch (pr->flags) { + case F_ADDRESS: + printf(pr->fmt, (unsigned) dumper->address); + break; + case F_BPAD: + printf(pr->fmt, ""); + break; + case F_C: + conv_c(pr, bp); + break; + case F_CHAR: + printf(pr->fmt, *bp); + break; + case F_DBL: { + double dval; + float fval; + + switch (pr->bcnt) { + case 4: + memmove(&fval, bp, sizeof(fval)); + printf(pr->fmt, fval); + break; + case 8: + memmove(&dval, bp, sizeof(dval)); + printf(pr->fmt, dval); + break; + } + break; + } + case F_INT: { + int ival; + short sval; + + switch (pr->bcnt) { + case 1: + printf(pr->fmt, (int) *bp); + break; + case 2: + memmove(&sval, bp, sizeof(sval)); + printf(pr->fmt, (int) sval); + break; + case 4: + memmove(&ival, bp, sizeof(ival)); + printf(pr->fmt, ival); + break; + } + break; + } + case F_P: + printf(pr->fmt, isprint(*bp) ? *bp : '.'); + break; + case F_STR: + printf(pr->fmt, (char *) bp); + break; + case F_TEXT: + printf(pr->fmt); + break; + case F_U: + conv_u(pr, bp); + break; + case F_UINT: { + unsigned ival; + unsigned short sval; + + switch (pr->bcnt) { + case 1: + printf(pr->fmt, (unsigned) *bp); + break; + case 2: + memmove(&sval, bp, sizeof(sval)); + printf(pr->fmt, (unsigned) sval); + break; + case 4: + memmove(&ival, bp, sizeof(ival)); + printf(pr->fmt, ival); + break; + } + break; + } + } + if (cnt == 1 && pr->nospace) { + *pr->nospace = savech; + } + } + } + } + } + } + if (dumper->endfu) { + /* + * if eaddress not set, error or file size was multiple + * of blocksize, and no partial block ever found. + */ + if (!dumper->eaddress) { + if (!dumper->address) { + return; + } + dumper->eaddress = dumper->address; + } + for (pr = dumper->endfu->nextpr; pr; pr = pr->nextpr) { + switch (pr->flags) { + case F_ADDRESS: + printf(pr->fmt, (unsigned) dumper->eaddress); + break; + case F_TEXT: + printf(pr->fmt); + break; + } + } + } +} + +#define dumper ((priv_dumper_t*)pub_dumper) +int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv) +{ + FS *tfs; + int blocksize; + + /* figure out the data block bb_dump_size */ + blocksize = 0; + tfs = dumper->pub.fshead; + while (tfs) { + tfs->bcnt = bb_dump_size(tfs); + if (blocksize < tfs->bcnt) { + blocksize = tfs->bcnt; + } + tfs = tfs->nextfs; + } + dumper->blocksize = blocksize; + + /* rewrite the rules, do syntax checking */ + for (tfs = dumper->pub.fshead; tfs; tfs = tfs->nextfs) { + rewrite(dumper, tfs); + } + + dumper->argv = argv; + display(dumper); + + return dumper->exitval; +} + +void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt) +{ + const char *p; + char *p1; + char *p2; + FS *tfs; + FU *tfu, **nextfupp; + const char *savep; + + /* start new linked list of format units */ + tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */ + if (!dumper->pub.fshead) { + dumper->pub.fshead = tfs; + } else { + FS *fslast = dumper->pub.fshead; + while (fslast->nextfs) + fslast = fslast->nextfs; + fslast->nextfs = tfs; + } + nextfupp = &tfs->nextfu; + + /* take the format string and break it up into format units */ + p = fmt; + for (;;) { + p = skip_whitespace(p); + if (!*p) { + break; + } + + /* allocate a new format unit and link it in */ + /* NOSTRICT */ + /* DBU:[dave@cray.com] zalloc so that forward pointers start out NULL */ + tfu = xzalloc(sizeof(FU)); + *nextfupp = tfu; + nextfupp = &tfu->nextfu; + tfu->reps = 1; + + /* if leading digit, repetition count */ + if (isdigit(*p)) { + for (savep = p; isdigit(*p); ++p) + continue; + if (!isspace(*p) && *p != '/') { + bb_error_msg_and_die("bad format {%s}", fmt); + } + /* may overwrite either white space or slash */ + tfu->reps = atoi(savep); + tfu->flags = F_SETREP; + /* skip trailing white space */ + p = skip_whitespace(++p); + } + + /* skip slash and trailing white space */ + if (*p == '/') { + p = skip_whitespace(++p); + } + + /* byte count */ + if (isdigit(*p)) { +// TODO: use bb_strtou + savep = p; + while (isdigit(*++p)) + continue; + if (!isspace(*p)) { + bb_error_msg_and_die("bad format {%s}", fmt); + } + tfu->bcnt = atoi(savep); + /* skip trailing white space */ + p = skip_whitespace(++p); + } + + /* format */ + if (*p != '"') { + bb_error_msg_and_die("bad format {%s}", fmt); + } + for (savep = ++p; *p != '"';) { + if (*p++ == 0) { + bb_error_msg_and_die("bad format {%s}", fmt); + } + } + tfu->fmt = xstrndup(savep, p - savep); +/* escape(tfu->fmt); */ + + p1 = tfu->fmt; + + /* alphabetic escape sequences have to be done in place */ + for (p2 = p1;; ++p1, ++p2) { + if (!*p1) { + *p2 = *p1; + break; + } + if (*p1 == '\\') { + const char *cs = conv_str + 4; + ++p1; + *p2 = *p1; + do { + if (*p1 == cs[2]) { + *p2 = cs[0]; + break; + } + cs += 4; + } while (*cs); + } + } + + p++; + } +} + +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/libbb/error_msg.c b/libbb/error_msg.c new file mode 100644 index 0000000..802fd57 --- /dev/null +++ b/libbb/error_msg.c @@ -0,0 +1,19 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +void FAST_FUNC bb_error_msg(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_verror_msg(s, p, NULL); + va_end(p); +} diff --git a/libbb/error_msg_and_die.c b/libbb/error_msg_and_die.c new file mode 100644 index 0000000..243433b --- /dev/null +++ b/libbb/error_msg_and_die.c @@ -0,0 +1,20 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +void FAST_FUNC bb_error_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_verror_msg(s, p, NULL); + va_end(p); + xfunc_die(); +} diff --git a/libbb/execable.c b/libbb/execable.c new file mode 100644 index 0000000..5c7ac16 --- /dev/null +++ b/libbb/execable.c @@ -0,0 +1,78 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2006 Gabriel Somlo + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* check if path points to an executable file; + * return 1 if found; + * return 0 otherwise; + */ +int FAST_FUNC execable_file(const char *name) +{ + struct stat s; + return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode)); +} + +/* search (*PATHp) for an executable file; + * return allocated string containing full path if found; + * PATHp points to the component after the one where it was found + * (or NULL), + * you may call find_execable again with this PATHp to continue + * (if it's not NULL). + * return NULL otherwise; (PATHp is undefined) + * in all cases (*PATHp) contents will be trashed (s/:/NUL/). + */ +char* FAST_FUNC find_execable(const char *filename, char **PATHp) +{ + char *p, *n; + + p = *PATHp; + while (p) { + n = strchr(p, ':'); + if (n) + *n++ = '\0'; + if (*p != '\0') { /* it's not a PATH="foo::bar" situation */ + p = concat_path_file(p, filename); + if (execable_file(p)) { + *PATHp = n; + return p; + } + free(p); + } + p = n; + } /* on loop exit p == NULL */ + return p; +} + +/* search $PATH for an executable file; + * return 1 if found; + * return 0 otherwise; + */ +int FAST_FUNC exists_execable(const char *filename) +{ + char *path = xstrdup(getenv("PATH")); + char *tmp = path; + char *ret = find_execable(filename, &tmp); + free(path); + if (ret) { + free(ret); + return 1; + } + return 0; +} + +#if ENABLE_FEATURE_PREFER_APPLETS +/* just like the real execvp, but try to launch an applet named 'file' first + */ +int FAST_FUNC bb_execvp(const char *file, char *const argv[]) +{ + return execvp(find_applet_by_name(file) >= 0 ? bb_busybox_exec_path : file, + argv); +} +#endif diff --git a/libbb/fclose_nonstdin.c b/libbb/fclose_nonstdin.c new file mode 100644 index 0000000..6f3f373 --- /dev/null +++ b/libbb/fclose_nonstdin.c @@ -0,0 +1,25 @@ +/* vi: set sw=4 ts=4: */ +/* + * fclose_nonstdin implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* A number of standard utilities can accept multiple command line args + * of '-' for stdin, according to SUSv3. So we encapsulate the check + * here to save a little space. + */ + +#include "libbb.h" + +int FAST_FUNC fclose_if_not_stdin(FILE *f) +{ + /* Some more paranoid applets want ferror() check too */ + int r = ferror(f); /* NB: does NOT set errno! */ + if (r) errno = EIO; /* so we'll help it */ + if (f != stdin) + return (r | fclose(f)); /* fclose does set errno on error */ + return r; +} diff --git a/libbb/fflush_stdout_and_exit.c b/libbb/fflush_stdout_and_exit.c new file mode 100644 index 0000000..742fb9f --- /dev/null +++ b/libbb/fflush_stdout_and_exit.c @@ -0,0 +1,29 @@ +/* vi: set sw=4 ts=4: */ +/* + * fflush_stdout_and_exit implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* Attempt to fflush(stdout), and exit with an error code if stdout is + * in an error state. + */ + +#include "libbb.h" + +void FAST_FUNC fflush_stdout_and_exit(int retval) +{ + if (fflush(stdout)) + bb_perror_msg_and_die(bb_msg_standard_output); + + if (ENABLE_FEATURE_PREFER_APPLETS && die_sleep < 0) { + /* We are in NOFORK applet. Do not exit() directly, + * but use xfunc_die() */ + xfunc_error_retval = retval; + xfunc_die(); + } + + exit(retval); +} diff --git a/libbb/fgets_str.c b/libbb/fgets_str.c new file mode 100644 index 0000000..8026a15 --- /dev/null +++ b/libbb/fgets_str.c @@ -0,0 +1,66 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. + * If you wrote this, please acknowledge your work. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +static char *xmalloc_fgets_internal(FILE *file, const char *terminating_string, int chop_off) +{ + char *linebuf = NULL; + const int term_length = strlen(terminating_string); + int end_string_offset; + int linebufsz = 0; + int idx = 0; + int ch; + + while (1) { + ch = fgetc(file); + if (ch == EOF) { + if (idx == 0) + return linebuf; /* NULL */ + break; + } + + if (idx >= linebufsz) { + linebufsz += 200; + linebuf = xrealloc(linebuf, linebufsz); + } + + linebuf[idx] = ch; + idx++; + + /* Check for terminating string */ + end_string_offset = idx - term_length; + if (end_string_offset >= 0 + && memcmp(&linebuf[end_string_offset], terminating_string, term_length) == 0 + ) { + if (chop_off) + idx -= term_length; + break; + } + } + /* Grow/shrink *first*, then store NUL */ + linebuf = xrealloc(linebuf, idx + 1); + linebuf[idx] = '\0'; + return linebuf; +} + +/* Read up to TERMINATING_STRING from FILE and return it, + * including terminating string. + * Non-terminated string can be returned if EOF is reached. + * Return NULL if EOF is reached immediately. */ +char* FAST_FUNC xmalloc_fgets_str(FILE *file, const char *terminating_string) +{ + return xmalloc_fgets_internal(file, terminating_string, 0); +} + +char* FAST_FUNC xmalloc_fgetline_str(FILE *file, const char *terminating_string) +{ + return xmalloc_fgets_internal(file, terminating_string, 1); +} diff --git a/libbb/find_mount_point.c b/libbb/find_mount_point.c new file mode 100644 index 0000000..4cd6b16 --- /dev/null +++ b/libbb/find_mount_point.c @@ -0,0 +1,53 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" +#include + +/* + * Given a block device, find the mount table entry if that block device + * is mounted. + * + * Given any other file (or directory), find the mount table entry for its + * filesystem. + */ +struct mntent* FAST_FUNC find_mount_point(const char *name, const char *table) +{ + struct stat s; + dev_t mountDevice; + FILE *mountTable; + struct mntent *mountEntry; + + if (stat(name, &s) != 0) + return 0; + + if ((s.st_mode & S_IFMT) == S_IFBLK) + mountDevice = s.st_rdev; + else + mountDevice = s.st_dev; + + + mountTable = setmntent(table ? table : bb_path_mtab_file, "r"); + if (!mountTable) + return 0; + + while ((mountEntry = getmntent(mountTable)) != 0) { + if (strcmp(name, mountEntry->mnt_dir) == 0 + || strcmp(name, mountEntry->mnt_fsname) == 0 + ) { /* String match. */ + break; + } + if (stat(mountEntry->mnt_fsname, &s) == 0 && s.st_rdev == mountDevice) /* Match the device. */ + break; + if (stat(mountEntry->mnt_dir, &s) == 0 && s.st_dev == mountDevice) /* Match the directory's mount point. */ + break; + } + endmntent(mountTable); + return mountEntry; +} diff --git a/libbb/find_pid_by_name.c b/libbb/find_pid_by_name.c new file mode 100644 index 0000000..92d6d02 --- /dev/null +++ b/libbb/find_pid_by_name.c @@ -0,0 +1,117 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include "libbb.h" + +/* +In Linux we have three ways to determine "process name": +1. /proc/PID/stat has "...(name)...", among other things. It's so-called "comm" field. +2. /proc/PID/cmdline's first NUL-terminated string. It's argv[0] from exec syscall. +3. /proc/PID/exe symlink. Points to the running executable file. + +kernel threads: + comm: thread name + cmdline: empty + exe: + +executable + comm: first 15 chars of base name + (if executable is a symlink, then first 15 chars of symlink name are used) + cmdline: argv[0] from exec syscall + exe: points to executable (resolves symlink, unlike comm) + +script (an executable with #!/path/to/interpreter): + comm: first 15 chars of script's base name (symlinks are not resolved) + cmdline: /path/to/interpreter (symlinks are not resolved) + (script name is in argv[1], args are pushed into argv[2] etc) + exe: points to interpreter's executable (symlinks are resolved) + +If FEATURE_PREFER_APPLETS=y (and more so if FEATURE_SH_STANDALONE=y), +some commands started from busybox shell, xargs or find are started by +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 argv1idx; + + /* comm does not match */ + if (strncmp(p->comm, procName, 15) != 0) + return 0; + + /* in Linux, if comm is 15 chars, it may be a truncated */ + if (p->comm[14] == '\0') /* comm is not truncated - match */ + return 1; + + /* comm is truncated, but first 15 chars match. + * This can be crazily_long_script_name.sh! + * The telltale sign is basename(argv[1]) == procName. */ + + if (!p->argv0) + return 0; + + argv1idx = strlen(p->argv0) + 1; + if (argv1idx >= p->argv_len) + return 0; + + if (strcmp(bb_basename(p->argv0 + argv1idx), procName) != 0) + return 0; + + return 1; +} + +/* find_pid_by_name() + * + * Modified by Vladimir Oleynik for use with libbb/procps.c + * This finds the pid of the specified process. + * Currently, it's implemented by rummaging through + * the proc filesystem. + * + * Returns a list of all matching PIDs + * It is the caller's duty to free the returned pidlist. + */ +pid_t* FAST_FUNC find_pid_by_name(const char *procName) +{ + pid_t* pidList; + int i = 0; + procps_status_t* p = NULL; + + pidList = xzalloc(sizeof(*pidList)); + while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_COMM|PSSCAN_ARGVN))) { + if (comm_match(p, procName) + /* or we require argv0 to match (essential for matching reexeced /proc/self/exe)*/ + || (p->argv0 && strcmp(bb_basename(p->argv0), procName) == 0) + /* TODO: we can also try /proc/NUM/exe link, do we want that? */ + ) { + pidList = xrealloc_vector(pidList, 2, i); + pidList[i++] = p->pid; + } + } + + pidList[i] = 0; + return pidList; +} + +pid_t* FAST_FUNC pidlist_reverse(pid_t *pidList) +{ + int i = 0; + while (pidList[i]) + i++; + if (--i >= 0) { + pid_t k; + int j; + for (j = 0; i > j; i--, j++) { + k = pidList[i]; + pidList[i] = pidList[j]; + pidList[j] = k; + } + } + return pidList; +} diff --git a/libbb/find_root_device.c b/libbb/find_root_device.c new file mode 100644 index 0000000..ca46bf5 --- /dev/null +++ b/libbb/find_root_device.c @@ -0,0 +1,74 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* Find block device /dev/XXX which contains specified file + * We handle /dev/dir/dir/dir too, at a cost of ~80 more bytes code */ + +/* Do not reallocate all this stuff on each recursion */ +enum { DEVNAME_MAX = 256 }; +struct arena { + struct stat st; + dev_t dev; + /* Was PATH_MAX, but we recurse _/dev_. We can assume + * people are not crazy enough to have mega-deep tree there */ + char devpath[DEVNAME_MAX]; +}; + +static char *find_block_device_in_dir(struct arena *ap) +{ + DIR *dir; + struct dirent *entry; + char *retpath = NULL; + int len, rem; + + dir = opendir(ap->devpath); + if (!dir) + return NULL; + + len = strlen(ap->devpath); + rem = DEVNAME_MAX-2 - len; + if (rem <= 0) + return NULL; + ap->devpath[len++] = '/'; + + while ((entry = readdir(dir)) != NULL) { + safe_strncpy(ap->devpath + len, entry->d_name, rem); + /* lstat: do not follow links */ + if (lstat(ap->devpath, &ap->st) != 0) + continue; + if (S_ISBLK(ap->st.st_mode) && ap->st.st_rdev == ap->dev) { + retpath = xstrdup(ap->devpath); + break; + } + if (S_ISDIR(ap->st.st_mode)) { + /* Do not recurse for '.' and '..' */ + if (DOT_OR_DOTDOT(entry->d_name)) + continue; + retpath = find_block_device_in_dir(ap); + if (retpath) + break; + } + } + closedir(dir); + + return retpath; +} + +char* FAST_FUNC find_block_device(const char *path) +{ + struct arena a; + + if (stat(path, &a.st) != 0) + return NULL; + a.dev = S_ISBLK(a.st.st_mode) ? a.st.st_rdev : a.st.st_dev; + strcpy(a.devpath, "/dev"); + return find_block_device_in_dir(&a); +} diff --git a/libbb/full_write.c b/libbb/full_write.c new file mode 100644 index 0000000..f353b7d --- /dev/null +++ b/libbb/full_write.c @@ -0,0 +1,42 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* + * Write all of the supplied buffer out to a file. + * This does multiple writes as necessary. + * Returns the amount written, or -1 on an error. + */ +ssize_t FAST_FUNC full_write(int fd, const void *buf, size_t len) +{ + ssize_t cc; + ssize_t total; + + total = 0; + + while (len) { + cc = safe_write(fd, buf, len); + + if (cc < 0) { + if (total) { + /* we already wrote some! */ + /* user can do another write to know the error code */ + return total; + } + return cc; /* write() returns -1 on failure. */ + } + + total += cc; + buf = ((const char *)buf) + cc; + len -= cc; + } + + return total; +} diff --git a/libbb/get_console.c b/libbb/get_console.c new file mode 100644 index 0000000..74022b5 --- /dev/null +++ b/libbb/get_console.c @@ -0,0 +1,80 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. If you wrote this, please + * acknowledge your work. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* From */ +enum { KDGKBTYPE = 0x4B33 }; /* get keyboard type */ + +static int open_a_console(const char *fnam) +{ + int fd; + + /* try read-write */ + fd = open(fnam, O_RDWR); + + /* if failed, try read-only */ + if (fd < 0 && errno == EACCES) + fd = open(fnam, O_RDONLY); + + /* if failed, try write-only */ + if (fd < 0 && errno == EACCES) + fd = open(fnam, O_WRONLY); + + return fd; +} + +/* + * Get an fd for use with kbd/console ioctls. + * We try several things because opening /dev/console will fail + * if someone else used X (which does a chown on /dev/console). + */ +int FAST_FUNC get_console_fd_or_die(void) +{ + static const char *const console_names[] = { + DEV_CONSOLE, CURRENT_VC, CURRENT_TTY + }; + + int fd; + + for (fd = 2; fd >= 0; fd--) { + int fd4name; + int choice_fd; + char arg; + + fd4name = open_a_console(console_names[fd]); + chk_std: + choice_fd = (fd4name >= 0 ? fd4name : fd); + + arg = 0; + if (ioctl(choice_fd, KDGKBTYPE, &arg) == 0) + return choice_fd; + if (fd4name >= 0) { + close(fd4name); + fd4name = -1; + goto chk_std; + } + } + + bb_error_msg_and_die("can't open console"); + /*return fd; - total failure */ +} + +/* From */ +enum { + VT_ACTIVATE = 0x5606, /* make vt active */ + VT_WAITACTIVE = 0x5607 /* wait for vt active */ +}; + +void FAST_FUNC console_make_active(int fd, const int vt_num) +{ + xioctl(fd, VT_ACTIVATE, (void *)(ptrdiff_t)vt_num); + xioctl(fd, VT_WAITACTIVE, (void *)(ptrdiff_t)vt_num); +} diff --git a/libbb/get_last_path_component.c b/libbb/get_last_path_component.c new file mode 100644 index 0000000..7c99116 --- /dev/null +++ b/libbb/get_last_path_component.c @@ -0,0 +1,42 @@ +/* vi: set sw=4 ts=4: */ +/* + * bb_get_last_path_component implementation for busybox + * + * Copyright (C) 2001 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" +/* + * "/" -> "/" + * "abc" -> "abc" + * "abc/def" -> "def" + * "abc/def/" -> "" + */ +char* FAST_FUNC bb_get_last_path_component_nostrip(const char *path) +{ + char *slash = strrchr(path, '/'); + + if (!slash || (slash == path && !slash[1])) + return (char*)path; + + return slash + 1; +} + +/* + * "/" -> "/" + * "abc" -> "abc" + * "abc/def" -> "def" + * "abc/def/" -> "def" !! + */ +char* FAST_FUNC bb_get_last_path_component_strip(char *path) +{ + char *slash = last_char_is(path, '/'); + + if (slash) + while (*slash == '/' && slash != path) + *slash-- = '\0'; + + return bb_get_last_path_component_nostrip(path); +} diff --git a/libbb/get_line_from_file.c b/libbb/get_line_from_file.c new file mode 100644 index 0000000..3cb46d2 --- /dev/null +++ b/libbb/get_line_from_file.c @@ -0,0 +1,207 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2005, 2006 Rob Landley + * Copyright (C) 2004 Erik Andersen + * Copyright (C) 2001 Matt Krai + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* for getline() [GNUism] +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif +*/ +#include "libbb.h" + +/* This function reads an entire line from a text file, up to a newline + * or NUL byte, inclusive. It returns a malloc'ed char * which + * must be free'ed by the caller. If end is NULL '\n' isn't considered + * end of line. If end isn't NULL, length of the chunk is stored in it. + * If lineno is not NULL, *lineno is incremented for each line, + * and also trailing '\' is recognized as line continuation. + * + * Returns NULL if EOF/error. */ +char* FAST_FUNC bb_get_chunk_with_continuation(FILE *file, int *end, int *lineno) +{ + int ch; + int idx = 0; + char *linebuf = NULL; + int linebufsz = 0; + + while ((ch = getc(file)) != EOF) { + /* grow the line buffer as necessary */ + if (idx >= linebufsz) { + linebufsz += 256; + linebuf = xrealloc(linebuf, linebufsz); + } + linebuf[idx++] = (char) ch; + if (!ch) + break; + if (end && ch == '\n') { + if (lineno == NULL) + break; + (*lineno)++; + if (idx < 2 || linebuf[idx-2] != '\\') + break; + idx -= 2; + } + } + if (end) + *end = idx; + if (linebuf) { + // huh, does fgets discard prior data on error like this? + // I don't think so.... + //if (ferror(file)) { + // free(linebuf); + // return NULL; + //} + linebuf = xrealloc(linebuf, idx + 1); + linebuf[idx] = '\0'; + } + return linebuf; +} + +char* FAST_FUNC bb_get_chunk_from_file(FILE *file, int *end) +{ + return bb_get_chunk_with_continuation(file, end, NULL); +} + +/* Get line, including trailing \n if any */ +char* FAST_FUNC xmalloc_fgets(FILE *file) +{ + int i; + + return bb_get_chunk_from_file(file, &i); +} +/* Get line. Remove trailing \n */ +char* FAST_FUNC xmalloc_fgetline(FILE *file) +{ + int i; + char *c = bb_get_chunk_from_file(file, &i); + + if (i && c[--i] == '\n') + c[i] = '\0'; + + return c; +} + +#if 0 +/* GNUism getline() should be faster (not tested) than a loop with fgetc */ + +/* Get line, including trailing \n if any */ +char* FAST_FUNC xmalloc_fgets(FILE *file) +{ + char *res_buf = NULL; + size_t res_sz; + + if (getline(&res_buf, &res_sz, file) == -1) { + free(res_buf); /* uclibc allocates a buffer even on EOF. WTF? */ + res_buf = NULL; + } +//TODO: trimming to res_sz? + return res_buf; +} +/* Get line. Remove trailing \n */ +char* FAST_FUNC xmalloc_fgetline(FILE *file) +{ + char *res_buf = NULL; + size_t res_sz; + + res_sz = getline(&res_buf, &res_sz, file); + + if ((ssize_t)res_sz != -1) { + if (res_buf[res_sz - 1] == '\n') + res_buf[--res_sz] = '\0'; +//TODO: trimming to res_sz? + } else { + free(res_buf); /* uclibc allocates a buffer even on EOF. WTF? */ + res_buf = NULL; + } + return res_buf; +} + +#endif + +#if 0 +/* Faster routines (~twice as fast). +170 bytes. Unused as of 2008-07. + * + * NB: they stop at NUL byte too. + * Performance is important here. Think "grep 50gigabyte_file"... + * Ironically, grep can't use it because of NUL issue. + * We sorely need C lib to provide fgets which reports size! + * + * Update: + * Actually, uclibc and glibc have it. man getline. It's GNUism, + * but very useful one (if it's as fast as this code). + * TODO: + * - currently, sed and sort use bb_get_chunk_from_file and heavily + * depend on its "stop on \n or \0" behavior, and STILL they fail + * to handle all cases with embedded NULs correctly. So: + * - audit sed and sort; convert them to getline FIRST. + * - THEN ditch bb_get_chunk_from_file, replace it with getline. + * - provide getline implementation for non-GNU systems. + */ + +static char* xmalloc_fgets_internal(FILE *file, int *sizep) +{ + int len; + int idx = 0; + char *linebuf = NULL; + + while (1) { + char *r; + + linebuf = xrealloc(linebuf, idx + 0x100); + r = fgets(&linebuf[idx], 0x100, file); + if (!r) { + /* need to terminate in case this is error + * (EOF puts NUL itself) */ + linebuf[idx] = '\0'; + break; + } + /* stupid. fgets knows the len, it should report it somehow */ + len = strlen(&linebuf[idx]); + idx += len; + if (len != 0xff || linebuf[idx - 1] == '\n') + break; + } + *sizep = idx; + if (idx) { + /* xrealloc(linebuf, idx + 1) is up to caller */ + return linebuf; + } + free(linebuf); + return NULL; +} + +/* Get line, remove trailing \n */ +char* FAST_FUNC xmalloc_fgetline_fast(FILE *file) +{ + int sz; + char *r = xmalloc_fgets_internal(file, &sz); + if (r && r[sz - 1] == '\n') + r[--sz] = '\0'; + return r; /* not xrealloc(r, sz + 1)! */ +} + +char* FAST_FUNC xmalloc_fgets(FILE *file) +{ + int sz; + return xmalloc_fgets_internal(file, &sz); +} + +/* Get line, remove trailing \n */ +char* FAST_FUNC xmalloc_fgetline(FILE *file) +{ + int sz; + char *r = xmalloc_fgets_internal(file, &sz); + if (!r) + return r; + if (r[sz - 1] == '\n') + r[--sz] = '\0'; + return xrealloc(r, sz + 1); +} +#endif diff --git a/libbb/getopt32.c b/libbb/getopt32.c new file mode 100644 index 0000000..17babcd --- /dev/null +++ b/libbb/getopt32.c @@ -0,0 +1,592 @@ +/* vi: set sw=4 ts=4: */ +/* + * universal getopt32 implementation for busybox + * + * Copyright (C) 2003-2005 Vladimir Oleynik + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include +#include "libbb.h" + +/* Documentation + +uint32_t +getopt32(char **argv, const char *applet_opts, ...) + + The command line options must be declared in const char + *applet_opts as a string of chars, for example: + + flags = getopt32(argv, "rnug"); + + If one of the given options is found, a flag value is added to + the return value (an unsigned long). + + The flag value is determined by the position of the char in + applet_opts string. For example, in the above case: + + flags = getopt32(argv, "rnug"); + + "r" will add 1 (bit 0) + "n" will add 2 (bit 1) + "u" will add 4 (bit 2) + "g" will add 8 (bit 3) + + and so on. You can also look at the return value as a bit + field and each option sets one bit. + + On exit, global variable optind is set so that if you + will do argc -= optind; argv += optind; then + argc will be equal to number of remaining non-option + arguments, first one would be in argv[0], next in argv[1] and so on + (options and their parameters will be moved into argv[] + positions prior to argv[optind]). + + ":" If one of the options requires an argument, then add a ":" + after the char in applet_opts and provide a pointer to store + the argument. For example: + + char *pointer_to_arg_for_a; + char *pointer_to_arg_for_b; + char *pointer_to_arg_for_c; + char *pointer_to_arg_for_d; + + flags = getopt32(argv, "a:b:c:d:", + &pointer_to_arg_for_a, &pointer_to_arg_for_b, + &pointer_to_arg_for_c, &pointer_to_arg_for_d); + + The type of the pointer (char* or llist_t*) may be controlled + by the "::" special separator that is set in the external string + opt_complementary (see below for more info). + + "::" If option can have an *optional* argument, then add a "::" + after its char in applet_opts and provide a pointer to store + the argument. Note that optional arguments _must_ + immediately follow the option: -oparam, not -o param. + + "+" If the first character in the applet_opts string is a plus, + then option processing will stop as soon as a non-option is + encountered in the argv array. Useful for applets like env + which should not process arguments to subprograms: + env -i ls -d / + Here we want env to process just the '-i', not the '-d'. + +const char *applet_long_options + + This struct allows you to define long options: + + static const char applet_longopts[] ALIGN1 = + //"name\0" has_arg val + "verbose\0" No_argument "v" + ; + applet_long_options = applet_longopts; + + The last member of struct option (val) typically is set to + matching short option from applet_opts. If there is no matching + char in applet_opts, then: + - return bit have next position after short options + - if has_arg is not "No_argument", use ptr for arg also + - opt_complementary affects it too + + Note: a good applet will make long options configurable via the + config process and not a required feature. The current standard + is to name the config option CONFIG_FEATURE__LONG_OPTIONS. + +const char *opt_complementary + + ":" The colon (":") is used to separate groups of two or more chars + and/or groups of chars and special characters (stating some + conditions to be checked). + + "abc" If groups of two or more chars are specified, the first char + is the main option and the other chars are secondary options. + Their flags will be turned on if the main option is found even + if they are not specifed on the command line. For example: + + opt_complementary = "abc"; + flags = getopt32(argv, "abcd") + + If getopt() finds "-a" on the command line, then + getopt32's return value will be as if "-a -b -c" were + found. + + "ww" Adjacent double options have a counter associated which indicates + the number of occurences of the option. + For example the ps applet needs: + if w is given once, GNU ps sets the width to 132, + if w is given more than once, it is "unlimited" + + int w_counter = 0; // must be initialized! + opt_complementary = "ww"; + getopt32(argv, "w", &w_counter); + if (w_counter) + width = (w_counter == 1) ? 132 : INT_MAX; + else + get_terminal_width(...&width...); + + w_counter is a pointer to an integer. It has to be passed to + getopt32() after all other option argument sinks. + + For example: accept multiple -v to indicate the level of verbosity + and for each -b optarg, add optarg to my_b. Finally, if b is given, + turn off c and vice versa: + + llist_t *my_b = NULL; + int verbose_level = 0; + opt_complementary = "vv:b::b-c:c-b"; + f = getopt32(argv, "vb:c", &my_b, &verbose_level); + if (f & 2) // -c after -b unsets -b flag + while (my_b) dosomething_with(llist_pop(&my_b)); + if (my_b) // but llist is stored if -b is specified + free_llist(my_b); + if (verbose_level) printf("verbose level is %d\n", verbose_level); + +Special characters: + + "-" A dash as the first char in a opt_complementary group forces + all arguments to be treated as options, even if they have + no leading dashes. Next char in this case can't be a digit (0-9), + use ':' or end of line. For example: + + opt_complementary = "-:w-x:x-w"; + getopt32(argv, "wx"); + + Allows any arguments to be given without a dash (./program w x) + as well as with a dash (./program -x). + + NB: getopt32() will leak a small amount of memory if you use + this option! Do not use it if there is a possibility of recursive + getopt32() calls. + + "--" A double dash at the beginning of opt_complementary means the + argv[1] string should always be treated as options, even if it isn't + prefixed with a "-". This is useful for special syntax in applets + such as "ar" and "tar": + tar xvf foo.tar + + NB: getopt32() will leak a small amount of memory if you use + this option! Do not use it if there is a possibility of recursive + getopt32() calls. + + "-N" A dash as the first char in a opt_complementary group followed + by a single digit (0-9) means that at least N non-option + arguments must be present on the command line + + "=N" An equal sign as the first char in a opt_complementary group followed + by a single digit (0-9) means that exactly N non-option + arguments must be present on the command line + + "?N" A "?" as the first char in a opt_complementary group followed + by a single digit (0-9) means that at most N arguments must be present + on the command line. + + "V-" An option with dash before colon or end-of-line results in + bb_show_usage() being called if this option is encountered. + This is typically used to implement "print verbose usage message + and exit" option. + + "a-b" A dash between two options causes the second of the two + to be unset (and ignored) if it is given on the command line. + + [FIXME: what if they are the same? like "x-x"? Is it ever useful?] + + For example: + The du applet has the options "-s" and "-d depth". If + getopt32 finds -s, then -d is unset or if it finds -d + then -s is unset. (Note: busybox implements the GNU + "--max-depth" option as "-d".) To obtain this behavior, you + set opt_complementary = "s-d:d-s". Only one flag value is + added to getopt32's return value depending on the + position of the options on the command line. If one of the + two options requires an argument pointer (":" in applet_opts + as in "d:") optarg is set accordingly. + + char *smax_print_depth; + + opt_complementary = "s-d:d-s:x-x"; + opt = getopt32(argv, "sd:x", &smax_print_depth); + + if (opt & 2) + max_print_depth = atoi(smax_print_depth); + if (opt & 4) + printf("Detected odd -x usage\n"); + + "a--b" A double dash between two options, or between an option and a group + of options, means that they are mutually exclusive. Unlike + the "-" case above, an error will be forced if the options + are used together. + + For example: + The cut applet must have only one type of list specified, so + -b, -c and -f are mutually exclusive and should raise an error + if specified together. In this case you must set + opt_complementary = "b--cf:c--bf:f--bc". If two of the + mutually exclusive options are found, getopt32 will call + bb_show_usage() and die. + + "x--x" Variation of the above, it means that -x option should occur + at most once. + + "a+" A plus after a char in opt_complementary means that the parameter + for this option is a nonnegative integer. It will be processed + with xatoi_u() - allowed range is 0..INT_MAX. + + int param; // "unsigned param;" will also work + opt_complementary = "p+"; + getopt32(argv, "p:", ¶m); + + "a::" A double colon after a char in opt_complementary means that the + option can occur multiple times. Each occurrence will be saved as + a llist_t element instead of char*. + + For example: + The grep applet can have one or more "-e pattern" arguments. + In this case you should use getopt32() as follows: + + llist_t *patterns = NULL; + + (this pointer must be initializated to NULL if the list is empty + as required by llist_add_to_end(llist_t **old_head, char *new_item).) + + opt_complementary = "e::"; + + getopt32(argv, "e:", &patterns); + $ grep -e user -e root /etc/passwd + root:x:0:0:root:/root:/bin/bash + user:x:500:500::/home/user:/bin/bash + + "a?b" A "?" between an option and a group of options means that + at least one of them is required to occur if the first option + occurs in preceding command line arguments. + + For example from "id" applet: + + // Don't allow -n -r -rn -ug -rug -nug -rnug + opt_complementary = "r?ug:n?ug:u--g:g--u"; + flags = getopt32(argv, "rnug"); + + This example allowed only: + $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng + + "X" A opt_complementary group with just a single letter means + that this option is required. If more than one such group exists, + at least one option is required to occur (not all of them). + For example from "start-stop-daemon" applet: + + // Don't allow -KS -SK, but -S or -K is required + opt_complementary = "K:S:K--S:S--K"; + flags = getopt32(argv, "KS...); + + + Don't forget to use ':'. For example, "?322-22-23X-x-a" + is interpreted as "?3:22:-2:2-2:2-3Xa:2--x" - + max 3 args; count uses of '-2'; min 2 args; if there is + a '-2' option then unset '-3', '-X' and '-a'; if there is + a '-2' and after it a '-x' then error out. + But it's far too obfuscated. Use ':' to separate groups. +*/ + +/* Code here assumes that 'unsigned' is at least 32 bits wide */ + +const char *const bb_argv_dash[] = { "-", NULL }; + +const char *opt_complementary; + +enum { + PARAM_STRING, + PARAM_LIST, + PARAM_INT, +}; + +typedef struct { + unsigned char opt_char; + smallint param_type; + unsigned switch_on; + unsigned switch_off; + unsigned incongruously; + unsigned requires; + void **optarg; /* char**, llist_t** or int *. */ + int *counter; +} t_complementary; + +/* You can set applet_long_options for parse called long options */ +#if ENABLE_GETOPT_LONG +static const struct option bb_null_long_options[1] = { + { 0, 0, 0, 0 } +}; +const char *applet_long_options; +#endif + +uint32_t option_mask32; + +uint32_t FAST_FUNC +getopt32(char **argv, const char *applet_opts, ...) +{ + int argc; + unsigned flags = 0; + unsigned requires = 0; + t_complementary complementary[33]; /* last stays zero-filled */ + int c; + const unsigned char *s; + t_complementary *on_off; + va_list p; +#if ENABLE_GETOPT_LONG + const struct option *l_o; + struct option *long_options = (struct option *) &bb_null_long_options; +#endif + unsigned trigger; + char **pargv; + int min_arg = 0; + int max_arg = -1; + +#define SHOW_USAGE_IF_ERROR 1 +#define ALL_ARGV_IS_OPTS 2 +#define FIRST_ARGV_IS_OPT 4 + + int spec_flgs = 0; + + /* skip 0: some applets cheat: they do not actually HAVE argv[0] */ + argc = 1; + while (argv[argc]) + argc++; + + va_start(p, applet_opts); + + c = 0; + on_off = complementary; + memset(on_off, 0, sizeof(complementary)); + + /* skip GNU extension */ + s = (const unsigned char *)applet_opts; + if (*s == '+' || *s == '-') + s++; + while (*s) { + if (c >= 32) + break; + on_off->opt_char = *s; + on_off->switch_on = (1 << c); + if (*++s == ':') { + on_off->optarg = va_arg(p, void **); + while (*++s == ':') + continue; + } + on_off++; + c++; + } + +#if ENABLE_GETOPT_LONG + if (applet_long_options) { + const char *optstr; + unsigned i, count; + + count = 1; + optstr = applet_long_options; + while (optstr[0]) { + optstr += strlen(optstr) + 3; /* skip NUL, has_arg, val */ + count++; + } + /* count == no. of longopts + 1 */ + long_options = alloca(count * sizeof(*long_options)); + memset(long_options, 0, count * sizeof(*long_options)); + i = 0; + optstr = applet_long_options; + while (--count) { + long_options[i].name = optstr; + optstr += strlen(optstr) + 1; + long_options[i].has_arg = (unsigned char)(*optstr++); + /* long_options[i].flag = NULL; */ + long_options[i].val = (unsigned char)(*optstr++); + i++; + } + for (l_o = long_options; l_o->name; l_o++) { + if (l_o->flag) + continue; + for (on_off = complementary; on_off->opt_char; on_off++) + if (on_off->opt_char == l_o->val) + goto next_long; + if (c >= 32) + break; + on_off->opt_char = l_o->val; + on_off->switch_on = (1 << c); + if (l_o->has_arg != no_argument) + on_off->optarg = va_arg(p, void **); + c++; + next_long: ; + } + } +#endif /* ENABLE_GETOPT_LONG */ + for (s = (const unsigned char *)opt_complementary; s && *s; s++) { + t_complementary *pair; + unsigned *pair_switch; + + if (*s == ':') + continue; + c = s[1]; + if (*s == '?') { + if (c < '0' || c > '9') { + spec_flgs |= SHOW_USAGE_IF_ERROR; + } else { + max_arg = c - '0'; + s++; + } + continue; + } + if (*s == '-') { + if (c < '0' || c > '9') { + if (c == '-') { + spec_flgs |= FIRST_ARGV_IS_OPT; + s++; + } else + spec_flgs |= ALL_ARGV_IS_OPTS; + } else { + min_arg = c - '0'; + s++; + } + continue; + } + if (*s == '=') { + min_arg = max_arg = c - '0'; + s++; + continue; + } + for (on_off = complementary; on_off->opt_char; on_off++) + if (on_off->opt_char == *s) + break; + if (c == ':' && s[2] == ':') { + on_off->param_type = PARAM_LIST; + continue; + } + if (c == '+' && (s[2] == ':' || s[2] == '\0')) { + on_off->param_type = PARAM_INT; + continue; + } + if (c == ':' || c == '\0') { + requires |= on_off->switch_on; + continue; + } + if (c == '-' && (s[2] == ':' || s[2] == '\0')) { + flags |= on_off->switch_on; + on_off->incongruously |= on_off->switch_on; + s++; + continue; + } + if (c == *s) { + on_off->counter = va_arg(p, int *); + s++; + } + pair = on_off; + pair_switch = &(pair->switch_on); + for (s++; *s && *s != ':'; s++) { + if (*s == '?') { + pair_switch = &(pair->requires); + } else if (*s == '-') { + if (pair_switch == &(pair->switch_off)) + pair_switch = &(pair->incongruously); + else + pair_switch = &(pair->switch_off); + } else { + for (on_off = complementary; on_off->opt_char; on_off++) + if (on_off->opt_char == *s) { + *pair_switch |= on_off->switch_on; + break; + } + } + } + s--; + } + va_end(p); + + if (spec_flgs & (FIRST_ARGV_IS_OPT | ALL_ARGV_IS_OPTS)) { + pargv = argv + 1; + while (*pargv) { + if (pargv[0][0] != '-' && pargv[0][0] != '\0') { + /* Can't use alloca: opts with params will + * return pointers to stack! + * NB: we leak these allocations... */ + char *pp = xmalloc(strlen(*pargv) + 2); + *pp = '-'; + strcpy(pp + 1, *pargv); + *pargv = pp; + } + if (!(spec_flgs & ALL_ARGV_IS_OPTS)) + break; + pargv++; + } + } + + /* In case getopt32 was already called: + * reset the libc getopt() function, which keeps internal state. + * run_nofork_applet_prime() does this, but we might end up here + * also via gunzip_main() -> gzip_main(). Play safe. + */ +#ifdef __GLIBC__ + optind = 0; +#else /* BSD style */ + optind = 1; + /* optreset = 1; */ +#endif + /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */ + + pargv = NULL; + + /* Note: just "getopt() <= 0" will not work well for + * "fake" short options, like this one: + * wget $'-\203' "Test: test" http://kernel.org/ + * (supposed to act as --header, but doesn't) */ +#if ENABLE_GETOPT_LONG + while ((c = getopt_long(argc, argv, applet_opts, + long_options, NULL)) != -1) { +#else + while ((c = getopt(argc, argv, applet_opts)) != -1) { +#endif + /* getopt prints "option requires an argument -- X" + * and returns '?' if an option has no arg, but one is reqd */ + c &= 0xff; /* fight libc's sign extension */ + for (on_off = complementary; on_off->opt_char != c; on_off++) { + /* c can be NUL if long opt has non-NULL ->flag, + * but we construct long opts so that flag + * is always NULL (see above) */ + if (on_off->opt_char == '\0' /* && c != '\0' */) { + /* c is probably '?' - "bad option" */ + bb_show_usage(); + } + } + if (flags & on_off->incongruously) + bb_show_usage(); + trigger = on_off->switch_on & on_off->switch_off; + flags &= ~(on_off->switch_off ^ trigger); + flags |= on_off->switch_on ^ trigger; + flags ^= trigger; + if (on_off->counter) + (*(on_off->counter))++; + if (on_off->param_type == PARAM_LIST) { + if (optarg) + llist_add_to_end((llist_t **)(on_off->optarg), optarg); + } else if (on_off->param_type == PARAM_INT) { + if (optarg) +//TODO: xatoi_u indirectly pulls in printf machinery + *(unsigned*)(on_off->optarg) = xatoi_u(optarg); + } else if (on_off->optarg) { + if (optarg) + *(char **)(on_off->optarg) = optarg; + } + if (pargv != NULL) + break; + } + + /* check depending requires for given options */ + 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(); + } + if (requires && (flags & requires) == 0) + bb_show_usage(); + argc -= optind; + if (argc < min_arg || (max_arg >= 0 && argc > max_arg)) + bb_show_usage(); + + option_mask32 = flags; + return flags; +} diff --git a/libbb/getpty.c b/libbb/getpty.c new file mode 100644 index 0000000..4bffd9a --- /dev/null +++ b/libbb/getpty.c @@ -0,0 +1,64 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini getpty implementation for busybox + * Bjorn Wesen, Axis Communications AB (bjornw@axis.com) + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +#define DEBUG 0 + +int FAST_FUNC xgetpty(char *line) +{ + int p; + +#if ENABLE_FEATURE_DEVPTS + p = open("/dev/ptmx", O_RDWR); + if (p > 0) { + grantpt(p); /* chmod+chown corresponding slave pty */ + unlockpt(p); /* (what does this do?) */ +#if 0 /* if ptsname_r is not available... */ + const char *name; + name = ptsname(p); /* find out the name of slave pty */ + if (!name) { + bb_perror_msg_and_die("ptsname error (is /dev/pts mounted?)"); + } + safe_strncpy(line, name, GETPTY_BUFSIZE); +#else + /* find out the name of slave pty */ + if (ptsname_r(p, line, GETPTY_BUFSIZE-1) != 0) { + bb_perror_msg_and_die("ptsname error (is /dev/pts mounted?)"); + } + line[GETPTY_BUFSIZE-1] = '\0'; +#endif + return p; + } +#else + struct stat stb; + int i; + int j; + + strcpy(line, "/dev/ptyXX"); + + for (i = 0; i < 16; i++) { + line[8] = "pqrstuvwxyzabcde"[i]; + line[9] = '0'; + if (stat(line, &stb) < 0) { + continue; + } + for (j = 0; j < 16; j++) { + line[9] = j < 10 ? j + '0' : j - 10 + 'a'; + if (DEBUG) + fprintf(stderr, "Trying to open device: %s\n", line); + p = open(line, O_RDWR | O_NOCTTY); + if (p >= 0) { + line[5] = 't'; + return p; + } + } + } +#endif /* FEATURE_DEVPTS */ + bb_error_msg_and_die("can't find free pty"); +} diff --git a/libbb/herror_msg.c b/libbb/herror_msg.c new file mode 100644 index 0000000..7e4f640 --- /dev/null +++ b/libbb/herror_msg.c @@ -0,0 +1,19 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +void FAST_FUNC bb_herror_msg(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_verror_msg(s, p, hstrerror(h_errno)); + va_end(p); +} diff --git a/libbb/herror_msg_and_die.c b/libbb/herror_msg_and_die.c new file mode 100644 index 0000000..230fe64 --- /dev/null +++ b/libbb/herror_msg_and_die.c @@ -0,0 +1,20 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +void FAST_FUNC bb_herror_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_verror_msg(s, p, hstrerror(h_errno)); + va_end(p); + xfunc_die(); +} diff --git a/libbb/human_readable.c b/libbb/human_readable.c new file mode 100644 index 0000000..61c8567 --- /dev/null +++ b/libbb/human_readable.c @@ -0,0 +1,97 @@ +/* vi: set sw=4 ts=4: */ +/* + * June 30, 2001 Manuel Novoa III + * + * All-integer version (hey, not everyone has floating point) of + * make_human_readable_str, modified from similar code I had written + * for busybox several months ago. + * + * Notes: + * 1) I'm using an unsigned long long to hold the product size * block_size, + * as df (which calls this routine) could request a representation of a + * partition size in bytes > max of unsigned long. If long longs aren't + * available, it would be possible to do what's needed using polynomial + * representations (say, powers of 1024) and manipulating coefficients. + * The base ten "bytes" output could be handled similarly. + * + * 2) This routine always outputs a decimal point and a tenths digit when + * display_unit != 0. Hence, it isn't uncommon for the returned string + * to have a length of 5 or 6. + * + * It might be nice to add a flag to indicate no decimal digits in + * that case. This could be either an additional parameter, or a + * special value of display_unit. Such a flag would also be nice for du. + * + * Some code to omit the decimal point and tenths digit is sketched out + * and "#if 0"'d below. + */ + +#include "libbb.h" + +const char* FAST_FUNC make_human_readable_str(unsigned long long size, + unsigned long block_size, unsigned long display_unit) +{ + /* The code will adjust for additional (appended) units */ + static const char unit_chars[] ALIGN1 = { + '\0', 'K', 'M', 'G', 'T', 'P', 'E' + }; + static const char fmt[] ALIGN1 = "%llu"; + static const char fmt_tenths[] ALIGN1 = "%llu.%d%c"; + + static char str[21] ALIGN1; /* Sufficient for 64 bit unsigned integers */ + + unsigned long long val; + int frac; + const char *u; + const char *f; + smallint no_tenths; + + if (size == 0) + return "0"; + + /* If block_size is 0 then do not print tenths */ + no_tenths = 0; + if (block_size == 0) { + no_tenths = 1; + block_size = 1; + } + + u = unit_chars; + val = size * block_size; + f = fmt; + frac = 0; + + if (display_unit) { + val += display_unit/2; /* Deal with rounding */ + val /= display_unit; /* Don't combine with the line above!!! */ + /* will just print it as ulonglong (below) */ + } else { + while ((val >= 1024) + && (u < unit_chars + sizeof(unit_chars) - 1) + ) { + f = fmt_tenths; + u++; + frac = (((int)(val % 1024)) * 10 + 1024/2) / 1024; + val /= 1024; + } + if (frac >= 10) { /* We need to round up here. */ + ++val; + frac = 0; + } +#if 1 + /* Sample code to omit decimal point and tenths digit. */ + if (no_tenths) { + if (frac >= 5) { + ++val; + } + f = "%llu%*c" /* fmt_no_tenths */; + frac = 1; + } +#endif + } + + /* If f==fmt then 'frac' and 'u' are ignored. */ + snprintf(str, sizeof(str), f, val, frac, *u); + + return str; +} diff --git a/libbb/inet_common.c b/libbb/inet_common.c new file mode 100644 index 0000000..966a021 --- /dev/null +++ b/libbb/inet_common.c @@ -0,0 +1,221 @@ +/* vi: set sw=4 ts=4: */ +/* + * stolen from net-tools-1.59 and stripped down for busybox by + * Erik Andersen + * + * Heavily modified by Manuel Novoa III Mar 12, 2001 + * + * + */ + +#include "libbb.h" +#include "inet_common.h" + +int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst) +{ + struct hostent *hp; +#if ENABLE_FEATURE_ETC_NETWORKS + struct netent *np; +#endif + + /* Grmpf. -FvK */ + s_in->sin_family = AF_INET; + s_in->sin_port = 0; + + /* Default is special, meaning 0.0.0.0. */ + if (!strcmp(name, bb_str_default)) { + s_in->sin_addr.s_addr = INADDR_ANY; + return 1; + } + /* Look to see if it's a dotted quad. */ + if (inet_aton(name, &s_in->sin_addr)) { + return 0; + } + /* If we expect this to be a hostname, try hostname database first */ +#ifdef DEBUG + if (hostfirst) { + bb_error_msg("gethostbyname(%s)", name); + } +#endif + if (hostfirst) { + hp = gethostbyname(name); + if (hp != NULL) { + memcpy(&s_in->sin_addr, hp->h_addr_list[0], + sizeof(struct in_addr)); + return 0; + } + } +#if ENABLE_FEATURE_ETC_NETWORKS + /* Try the NETWORKS database to see if this is a known network. */ +#ifdef DEBUG + bb_error_msg("getnetbyname(%s)", name); +#endif + np = getnetbyname(name); + if (np != NULL) { + s_in->sin_addr.s_addr = htonl(np->n_net); + return 1; + } +#endif + if (hostfirst) { + /* Don't try again */ + return -1; + } +#ifdef DEBUG + res_init(); + _res.options |= RES_DEBUG; + bb_error_msg("gethostbyname(%s)", name); +#endif + hp = gethostbyname(name); + if (hp == NULL) { + return -1; + } + memcpy(&s_in->sin_addr, hp->h_addr_list[0], sizeof(struct in_addr)); + return 0; +} + + +/* numeric: & 0x8000: default instead of *, + * & 0x4000: host instead of net, + * & 0x0fff: don't resolve + */ +char* FAST_FUNC INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t netmask) +{ + /* addr-to-name cache */ + struct addr { + struct addr *next; + struct sockaddr_in addr; + int host; + char name[1]; + }; + static struct addr *cache = NULL; + + struct addr *pn; + char *name; + uint32_t ad, host_ad; + int host = 0; + + if (s_in->sin_family != AF_INET) { +#ifdef DEBUG + bb_error_msg("rresolve: unsupported address family %d!", + s_in->sin_family); +#endif + errno = EAFNOSUPPORT; + return NULL; + } + ad = s_in->sin_addr.s_addr; +#ifdef DEBUG + bb_error_msg("rresolve: %08x, mask %08x, num %08x", (unsigned)ad, netmask, numeric); +#endif + if (ad == INADDR_ANY) { + if ((numeric & 0x0FFF) == 0) { + if (numeric & 0x8000) + return xstrdup(bb_str_default); + return xstrdup("*"); + } + } + if (numeric & 0x0FFF) + return xstrdup(inet_ntoa(s_in->sin_addr)); + + if ((ad & (~netmask)) != 0 || (numeric & 0x4000)) + host = 1; + pn = cache; + while (pn) { + if (pn->addr.sin_addr.s_addr == ad && pn->host == host) { +#ifdef DEBUG + bb_error_msg("rresolve: found %s %08x in cache", + (host ? "host" : "net"), (unsigned)ad); +#endif + return xstrdup(pn->name); + } + pn = pn->next; + } + + host_ad = ntohl(ad); + name = NULL; + if (host) { + struct hostent *ent; +#ifdef DEBUG + bb_error_msg("gethostbyaddr (%08x)", (unsigned)ad); +#endif + ent = gethostbyaddr((char *) &ad, 4, AF_INET); + if (ent) + name = xstrdup(ent->h_name); + } else if (ENABLE_FEATURE_ETC_NETWORKS) { + struct netent *np; +#ifdef DEBUG + bb_error_msg("getnetbyaddr (%08x)", (unsigned)host_ad); +#endif + np = getnetbyaddr(host_ad, AF_INET); + if (np) + name = xstrdup(np->n_name); + } + if (!name) + name = xstrdup(inet_ntoa(s_in->sin_addr)); + pn = xmalloc(sizeof(*pn) + strlen(name)); /* no '+ 1', it's already accounted for */ + pn->next = cache; + pn->addr = *s_in; + pn->host = host; + strcpy(pn->name, name); + cache = pn; + return name; +} + +#if ENABLE_FEATURE_IPV6 + +int FAST_FUNC INET6_resolve(const char *name, struct sockaddr_in6 *sin6) +{ + struct addrinfo req, *ai; + int s; + + memset(&req, '\0', sizeof req); + req.ai_family = AF_INET6; + s = getaddrinfo(name, NULL, &req, &ai); + if (s) { + bb_error_msg("getaddrinfo: %s: %d", name, s); + return -1; + } + memcpy(sin6, ai->ai_addr, sizeof(struct sockaddr_in6)); + freeaddrinfo(ai); + return 0; +} + +#ifndef IN6_IS_ADDR_UNSPECIFIED +# define IN6_IS_ADDR_UNSPECIFIED(a) \ + (((uint32_t *) (a))[0] == 0 && ((uint32_t *) (a))[1] == 0 && \ + ((uint32_t *) (a))[2] == 0 && ((uint32_t *) (a))[3] == 0) +#endif + + +char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric) +{ + char name[128]; + int s; + + if (sin6->sin6_family != AF_INET6) { +#ifdef DEBUG + bb_error_msg("rresolve: unsupport address family %d!", + sin6->sin6_family); +#endif + errno = EAFNOSUPPORT; + return NULL; + } + if (numeric & 0x7FFF) { + inet_ntop(AF_INET6, &sin6->sin6_addr, name, sizeof(name)); + return xstrdup(name); + } + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + if (numeric & 0x8000) + return xstrdup(bb_str_default); + return xstrdup("*"); + } + + s = getnameinfo((struct sockaddr *) sin6, sizeof(struct sockaddr_in6), + name, sizeof(name), NULL, 0, 0); + if (s) { + bb_error_msg("getnameinfo failed"); + return NULL; + } + return xstrdup(name); +} + +#endif /* CONFIG_FEATURE_IPV6 */ diff --git a/libbb/info_msg.c b/libbb/info_msg.c new file mode 100644 index 0000000..ffef05e --- /dev/null +++ b/libbb/info_msg.c @@ -0,0 +1,30 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" +#include + +void FAST_FUNC bb_info_msg(const char *s, ...) +{ + va_list p; + /* va_copy is used because it is not portable + * to use va_list p twice */ + va_list p2; + + va_start(p, s); + va_copy(p2, p); + if (logmode & LOGMODE_STDIO) { + vprintf(s, p); + fputs(msg_eol, stdout); + } + if (ENABLE_FEATURE_SYSLOG && (logmode & LOGMODE_SYSLOG)) + vsyslog(LOG_INFO, s, p2); + va_end(p2); + va_end(p); +} diff --git a/libbb/inode_hash.c b/libbb/inode_hash.c new file mode 100644 index 0000000..4469671 --- /dev/null +++ b/libbb/inode_hash.c @@ -0,0 +1,87 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. + * If you wrote this, please acknowledge your work. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +typedef struct ino_dev_hash_bucket_struct { + struct ino_dev_hash_bucket_struct *next; + ino_t ino; + dev_t dev; + char name[1]; +} ino_dev_hashtable_bucket_t; + +#define HASH_SIZE 311 /* Should be prime */ +#define hash_inode(i) ((i) % HASH_SIZE) + +/* array of [HASH_SIZE] elements */ +static ino_dev_hashtable_bucket_t **ino_dev_hashtable; + +/* + * Return name if statbuf->st_ino && statbuf->st_dev are recorded in + * ino_dev_hashtable, else return NULL + */ +char* FAST_FUNC is_in_ino_dev_hashtable(const struct stat *statbuf) +{ + ino_dev_hashtable_bucket_t *bucket; + + if (!ino_dev_hashtable) + return NULL; + + bucket = ino_dev_hashtable[hash_inode(statbuf->st_ino)]; + while (bucket != NULL) { + if ((bucket->ino == statbuf->st_ino) + && (bucket->dev == statbuf->st_dev) + ) { + return bucket->name; + } + bucket = bucket->next; + } + return NULL; +} + +/* Add statbuf to statbuf hash table */ +void FAST_FUNC add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) +{ + int i; + ino_dev_hashtable_bucket_t *bucket; + + i = hash_inode(statbuf->st_ino); + if (!name) + name = ""; + bucket = xmalloc(sizeof(ino_dev_hashtable_bucket_t) + strlen(name)); + bucket->ino = statbuf->st_ino; + bucket->dev = statbuf->st_dev; + strcpy(bucket->name, name); + + if (!ino_dev_hashtable) + ino_dev_hashtable = xzalloc(HASH_SIZE * sizeof(*ino_dev_hashtable)); + + bucket->next = ino_dev_hashtable[i]; + ino_dev_hashtable[i] = bucket; +} + +#if ENABLE_FEATURE_CLEAN_UP +/* Clear statbuf hash table */ +void FAST_FUNC reset_ino_dev_hashtable(void) +{ + int i; + ino_dev_hashtable_bucket_t *bucket; + + for (i = 0; ino_dev_hashtable && i < HASH_SIZE; i++) { + while (ino_dev_hashtable[i] != NULL) { + bucket = ino_dev_hashtable[i]->next; + free(ino_dev_hashtable[i]); + ino_dev_hashtable[i] = bucket; + } + } + free(ino_dev_hashtable); + ino_dev_hashtable = NULL; +} +#endif diff --git a/libbb/isdirectory.c b/libbb/isdirectory.c new file mode 100644 index 0000000..28ed3ec --- /dev/null +++ b/libbb/isdirectory.c @@ -0,0 +1,36 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Based in part on code from sash, Copyright (c) 1999 by David I. Bell + * Permission has been granted to redistribute this code under the GPL. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include +#include "libbb.h" + +/* + * Return TRUE if fileName is a directory. + * Nonexistent files return FALSE. + */ +int FAST_FUNC is_directory(const char *fileName, const int followLinks, struct stat *statBuf) +{ + int status; + struct stat astatBuf; + + if (statBuf == NULL) { + /* use auto stack buffer */ + statBuf = &astatBuf; + } + + if (followLinks) + status = stat(fileName, statBuf); + else + status = lstat(fileName, statBuf); + + status = (status == 0 && S_ISDIR(statBuf->st_mode)); + + return status; +} diff --git a/libbb/kernel_version.c b/libbb/kernel_version.c new file mode 100644 index 0000000..8b9c4ec --- /dev/null +++ b/libbb/kernel_version.c @@ -0,0 +1,37 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include /* for uname(2) */ + +#include "libbb.h" + +/* Returns current kernel version encoded as major*65536 + minor*256 + patch, + * so, for example, to check if the kernel is greater than 2.2.11: + * + * if (get_linux_version_code() > KERNEL_VERSION(2,2,11)) { } + */ +int FAST_FUNC get_linux_version_code(void) +{ + struct utsname name; + char *s; + int i, r; + + if (uname(&name) == -1) { + bb_perror_msg("cannot get system information"); + return 0; + } + + s = name.release; + r = 0; + for (i = 0; i < 3; i++) { + r = r * 256 + atoi(strtok(s, ".")); + s = NULL; + } + return r; +} diff --git a/libbb/last_char_is.c b/libbb/last_char_is.c new file mode 100644 index 0000000..b059256 --- /dev/null +++ b/libbb/last_char_is.c @@ -0,0 +1,24 @@ +/* vi: set sw=4 ts=4: */ +/* + * busybox library eXtended function + * + * Copyright (C) 2001 Larry Doolittle, + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* Find out if the last character of a string matches the one given. + * Don't underrun the buffer if the string length is 0. + */ +char* FAST_FUNC last_char_is(const char *s, int c) +{ + if (s && *s) { + size_t sz = strlen(s) - 1; + s += sz; + if ( (unsigned char)*s == c) + return (char*)s; + } + return NULL; +} diff --git a/libbb/lineedit.c b/libbb/lineedit.c new file mode 100644 index 0000000..0be3255 --- /dev/null +++ b/libbb/lineedit.c @@ -0,0 +1,1934 @@ +/* vi: set sw=4 ts=4: */ +/* + * Termios command line History and Editing. + * + * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license. + * Written by: Vladimir Oleynik + * + * Used ideas: + * Adam Rogoyski + * Dave Cinege + * Jakub Jelinek (c) 1995 + * Erik Andersen (Majorly adjusted for busybox) + * + * This code is 'as is' with no warranty. + */ + +/* + * Usage and known bugs: + * Terminal key codes are not extensive, and more will probably + * need to be added. This version was created on Debian GNU/Linux 2.x. + * Delete, Backspace, Home, End, and the arrow keys were tested + * to work in an Xterm and console. Ctrl-A also works as Home. + * Ctrl-E also works as End. + * + * lineedit does not know that the terminal escape sequences do not + * take up space on the screen. The redisplay code assumes, unless + * told otherwise, that each character in the prompt is a printable + * character that takes up one character position on the screen. + * You need to tell lineedit that some sequences of characters + * in the prompt take up no screen space. Compatibly with readline, + * use the \[ escape to begin a sequence of non-printing characters, + * and the \] escape to signal the end of such a sequence. Example: + * + * PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] ' + */ + +#include "libbb.h" + + +/* FIXME: obsolete CONFIG item? */ +#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0 + + +#ifdef TEST + +#define ENABLE_FEATURE_EDITING 0 +#define ENABLE_FEATURE_TAB_COMPLETION 0 +#define ENABLE_FEATURE_USERNAME_COMPLETION 0 +#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0 + +#endif /* TEST */ + + +/* Entire file (except TESTing part) sits inside this #if */ +#if ENABLE_FEATURE_EDITING + +#if ENABLE_LOCALE_SUPPORT +#define Isprint(c) isprint(c) +#else +#define Isprint(c) ((c) >= ' ' && (c) != ((unsigned char)'\233')) +#endif + +#define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \ + (ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT) +#define USE_FEATURE_GETUSERNAME_AND_HOMEDIR(...) +#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR +#undef USE_FEATURE_GETUSERNAME_AND_HOMEDIR +#define USE_FEATURE_GETUSERNAME_AND_HOMEDIR(...) __VA_ARGS__ +#endif + +enum { + /* We use int16_t for positions, need to limit line len */ + MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0 + ? CONFIG_FEATURE_EDITING_MAX_LEN + : 0x7ff0 +}; + +#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR +static const char null_str[] ALIGN1 = ""; +#endif + +/* We try to minimize both static and stack usage. */ +struct lineedit_statics { + line_input_t *state; + + volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */ + sighandler_t previous_SIGWINCH_handler; + + unsigned cmdedit_x; /* real x terminal position */ + unsigned cmdedit_y; /* pseudoreal y terminal position */ + unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */ + + unsigned cursor; + unsigned command_len; + char *command_ps; + + const char *cmdedit_prompt; +#if ENABLE_FEATURE_EDITING_FANCY_PROMPT + int num_ok_lines; /* = 1; */ +#endif + +#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR + char *user_buf; + char *home_pwd_buf; /* = (char*)null_str; */ +#endif + +#if ENABLE_FEATURE_TAB_COMPLETION + char **matches; + unsigned num_matches; +#endif + +#if ENABLE_FEATURE_EDITING_VI +#define DELBUFSIZ 128 + char *delptr; + smallint newdelflag; /* whether delbuf should be reused yet */ + char delbuf[DELBUFSIZ]; /* a place to store deleted characters */ +#endif + + /* Formerly these were big buffers on stack: */ +#if ENABLE_FEATURE_TAB_COMPLETION + char exe_n_cwd_tab_completion__dirbuf[MAX_LINELEN]; + char input_tab__matchBuf[MAX_LINELEN]; + int16_t find_match__int_buf[MAX_LINELEN + 1]; /* need to have 9 bits at least */ + int16_t find_match__pos_buf[MAX_LINELEN + 1]; +#endif +}; + +/* See lineedit_ptr_hack.c */ +extern struct lineedit_statics *const lineedit_ptr_to_statics; + +#define S (*lineedit_ptr_to_statics) +#define state (S.state ) +#define cmdedit_termw (S.cmdedit_termw ) +#define previous_SIGWINCH_handler (S.previous_SIGWINCH_handler) +#define cmdedit_x (S.cmdedit_x ) +#define cmdedit_y (S.cmdedit_y ) +#define cmdedit_prmt_len (S.cmdedit_prmt_len) +#define cursor (S.cursor ) +#define command_len (S.command_len ) +#define command_ps (S.command_ps ) +#define cmdedit_prompt (S.cmdedit_prompt ) +#define num_ok_lines (S.num_ok_lines ) +#define user_buf (S.user_buf ) +#define home_pwd_buf (S.home_pwd_buf ) +#define matches (S.matches ) +#define num_matches (S.num_matches ) +#define delptr (S.delptr ) +#define newdelflag (S.newdelflag ) +#define delbuf (S.delbuf ) + +#define INIT_S() do { \ + (*(struct lineedit_statics**)&lineedit_ptr_to_statics) = xzalloc(sizeof(S)); \ + barrier(); \ + cmdedit_termw = 80; \ + USE_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \ + USE_FEATURE_GETUSERNAME_AND_HOMEDIR(home_pwd_buf = (char*)null_str;) \ +} while (0) +static void deinit_S(void) +{ +#if ENABLE_FEATURE_EDITING_FANCY_PROMPT + /* This one is allocated only if FANCY_PROMPT is on + * (otherwise it points to verbatim prompt (NOT malloced) */ + free((char*)cmdedit_prompt); +#endif +#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR + free(user_buf); + if (home_pwd_buf != null_str) + free(home_pwd_buf); +#endif + free(lineedit_ptr_to_statics); +} +#define DEINIT_S() deinit_S() + + +/* Put 'command_ps[cursor]', cursor++. + * Advance cursor on screen. If we reached right margin, scroll text up + * and remove terminal margin effect by printing 'next_char' */ +#define HACK_FOR_WRONG_WIDTH 1 +#if HACK_FOR_WRONG_WIDTH +static void cmdedit_set_out_char(void) +#define cmdedit_set_out_char(next_char) cmdedit_set_out_char() +#else +static void cmdedit_set_out_char(int next_char) +#endif +{ + int c = (unsigned char)command_ps[cursor]; + + if (c == '\0') { + /* erase character after end of input string */ + c = ' '; + } +#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT + /* Display non-printable characters in reverse */ + if (!Isprint(c)) { + if (c >= 128) + c -= 128; + if (c < ' ') + c += '@'; + if (c == 127) + c = '?'; + printf("\033[7m%c\033[0m", c); + } else +#endif + { + bb_putchar(c); + } + if (++cmdedit_x >= cmdedit_termw) { + /* terminal is scrolled down */ + cmdedit_y++; + cmdedit_x = 0; +#if HACK_FOR_WRONG_WIDTH + /* This works better if our idea of term width is wrong + * and it is actually wider (often happens on serial lines). + * Printing CR,LF *forces* cursor to next line. + * OTOH if terminal width is correct AND terminal does NOT + * have automargin (IOW: it is moving cursor to next line + * by itself (which is wrong for VT-10x terminals)), + * this will break things: there will be one extra empty line */ + puts("\r"); /* + implicit '\n' */ +#else + /* Works ok only if cmdedit_termw is correct */ + /* destroy "(auto)margin" */ + bb_putchar(next_char); + bb_putchar('\b'); +#endif + } +// Huh? What if command_ps[cursor] == '\0' (we are at the end already?) + cursor++; +} + +/* Move to end of line (by printing all chars till the end) */ +static void input_end(void) +{ + while (cursor < command_len) + cmdedit_set_out_char(' '); +} + +/* Go to the next line */ +static void goto_new_line(void) +{ + input_end(); + if (cmdedit_x) + bb_putchar('\n'); +} + + +static void out1str(const char *s) +{ + if (s) + fputs(s, stdout); +} + +static void beep(void) +{ + bb_putchar('\007'); +} + +/* Move back one character */ +/* (optimized for slow terminals) */ +static void input_backward(unsigned num) +{ + int count_y; + + if (num > cursor) + num = cursor; + if (!num) + return; + cursor -= num; + + if (cmdedit_x >= num) { + cmdedit_x -= num; + if (num <= 4) { + /* This is longer by 5 bytes on x86. + * Also gets miscompiled for ARM users + * (busybox.net/bugs/view.php?id=2274). + * printf(("\b\b\b\b" + 4) - num); + * return; + */ + do { + bb_putchar('\b'); + } while (--num); + return; + } + printf("\033[%uD", num); + return; + } + + /* Need to go one or more lines up */ + num -= cmdedit_x; + { + unsigned w = cmdedit_termw; /* volatile var */ + count_y = 1 + (num / w); + cmdedit_y -= count_y; + cmdedit_x = w * count_y - num; + } + /* go to 1st column; go up; go to correct column */ + printf("\r" "\033[%dA" "\033[%dC", count_y, cmdedit_x); +} + +static void put_prompt(void) +{ + out1str(cmdedit_prompt); + cursor = 0; + { + unsigned w = cmdedit_termw; /* volatile var */ + cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */ + cmdedit_x = cmdedit_prmt_len % w; + } +} + +/* draw prompt, editor line, and clear tail */ +static void redraw(int y, int back_cursor) +{ + if (y > 0) /* up to start y */ + printf("\033[%dA", y); + bb_putchar('\r'); + put_prompt(); + input_end(); /* rewrite */ + printf("\033[J"); /* erase after cursor */ + input_backward(back_cursor); +} + +/* Delete the char in front of the cursor, optionally saving it + * for later putback */ +#if !ENABLE_FEATURE_EDITING_VI +static void input_delete(void) +#define input_delete(save) input_delete() +#else +static void input_delete(int save) +#endif +{ + int j = cursor; + + if (j == (int)command_len) + return; + +#if ENABLE_FEATURE_EDITING_VI + if (save) { + if (newdelflag) { + delptr = delbuf; + newdelflag = 0; + } + if ((delptr - delbuf) < DELBUFSIZ) + *delptr++ = command_ps[j]; + } +#endif + + overlapping_strcpy(command_ps + j, command_ps + j + 1); + command_len--; + input_end(); /* rewrite new line */ + cmdedit_set_out_char(' '); /* erase char */ + input_backward(cursor - j); /* back to old pos cursor */ +} + +#if ENABLE_FEATURE_EDITING_VI +static void put(void) +{ + int ocursor; + int j = delptr - delbuf; + + if (j == 0) + return; + ocursor = cursor; + /* open hole and then fill it */ + memmove(command_ps + cursor + j, command_ps + cursor, command_len - cursor + 1); + strncpy(command_ps + cursor, delbuf, j); + command_len += j; + input_end(); /* rewrite new line */ + input_backward(cursor - ocursor - j + 1); /* at end of new text */ +} +#endif + +/* Delete the char in back of the cursor */ +static void input_backspace(void) +{ + if (cursor > 0) { + input_backward(1); + input_delete(0); + } +} + +/* Move forward one character */ +static void input_forward(void) +{ + if (cursor < command_len) + cmdedit_set_out_char(command_ps[cursor + 1]); +} + +#if ENABLE_FEATURE_TAB_COMPLETION + +static void free_tab_completion_data(void) +{ + if (matches) { + while (num_matches) + free(matches[--num_matches]); + free(matches); + matches = NULL; + } +} + +static void add_match(char *matched) +{ + matches = xrealloc_vector(matches, 4, num_matches); + matches[num_matches] = matched; + num_matches++; +} + +#if ENABLE_FEATURE_USERNAME_COMPLETION +static void username_tab_completion(char *ud, char *with_shash_flg) +{ + struct passwd *entry; + int userlen; + + ud++; /* ~user/... to user/... */ + userlen = strlen(ud); + + if (with_shash_flg) { /* "~/..." or "~user/..." */ + char *sav_ud = ud - 1; + char *home = NULL; + + if (*ud == '/') { /* "~/..." */ + home = home_pwd_buf; + } else { + /* "~user/..." */ + char *temp; + temp = strchr(ud, '/'); + *temp = '\0'; /* ~user\0 */ + entry = getpwnam(ud); + *temp = '/'; /* restore ~user/... */ + ud = temp; + if (entry) + home = entry->pw_dir; + } + if (home) { + if ((userlen + strlen(home) + 1) < MAX_LINELEN) { + /* /home/user/... */ + sprintf(sav_ud, "%s%s", home, ud); + } + } + } else { + /* "~[^/]*" */ + /* Using _r function to avoid pulling in static buffers */ + char line_buff[256]; + struct passwd pwd; + struct passwd *result; + + setpwent(); + while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) { + /* Null usernames should result in all users as possible completions. */ + if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) { + add_match(xasprintf("~%s/", pwd.pw_name)); + } + } + endpwent(); + } +} +#endif /* FEATURE_COMMAND_USERNAME_COMPLETION */ + +enum { + FIND_EXE_ONLY = 0, + FIND_DIR_ONLY = 1, + FIND_FILE_ONLY = 2, +}; + +static int path_parse(char ***p, int flags) +{ + int npth; + const char *pth; + char *tmp; + char **res; + + /* if not setenv PATH variable, to search cur dir "." */ + if (flags != FIND_EXE_ONLY) + return 1; + + if (state->flags & WITH_PATH_LOOKUP) + pth = state->path_lookup; + else + pth = getenv("PATH"); + /* PATH= or PATH=: */ + if (!pth || !pth[0] || LONE_CHAR(pth, ':')) + return 1; + + tmp = (char*)pth; + npth = 1; /* path component count */ + while (1) { + tmp = strchr(tmp, ':'); + if (!tmp) + break; + if (*++tmp == '\0') + break; /* : */ + npth++; + } + + res = xmalloc(npth * sizeof(char*)); + res[0] = tmp = xstrdup(pth); + npth = 1; + while (1) { + tmp = strchr(tmp, ':'); + if (!tmp) + break; + *tmp++ = '\0'; /* ':' -> '\0' */ + if (*tmp == '\0') + break; /* : */ + res[npth++] = tmp; + } + *p = res; + return npth; +} + +static void exe_n_cwd_tab_completion(char *command, int type) +{ + DIR *dir; + struct dirent *next; + struct stat st; + char *path1[1]; + char **paths = path1; + int npaths; + int i; + char *found; + char *pfind = strrchr(command, '/'); +/* char dirbuf[MAX_LINELEN]; */ +#define dirbuf (S.exe_n_cwd_tab_completion__dirbuf) + + npaths = 1; + path1[0] = (char*)"."; + + if (pfind == NULL) { + /* no dir, if flags==EXE_ONLY - get paths, else "." */ + npaths = path_parse(&paths, type); + pfind = command; + } else { + /* dirbuf = ".../.../.../" */ + safe_strncpy(dirbuf, command, (pfind - command) + 2); +#if ENABLE_FEATURE_USERNAME_COMPLETION + if (dirbuf[0] == '~') /* ~/... or ~user/... */ + username_tab_completion(dirbuf, dirbuf); +#endif + paths[0] = dirbuf; + /* point to 'l' in "..../last_component" */ + pfind++; + } + + for (i = 0; i < npaths; i++) { + dir = opendir(paths[i]); + if (!dir) + continue; /* don't print an error */ + + while ((next = readdir(dir)) != NULL) { + int len1; + const char *str_found = next->d_name; + + /* matched? */ + if (strncmp(str_found, pfind, strlen(pfind))) + continue; + /* not see .name without .match */ + if (*str_found == '.' && *pfind == '\0') { + if (NOT_LONE_CHAR(paths[i], '/') || str_found[1]) + continue; + str_found = ""; /* only "/" */ + } + found = concat_path_file(paths[i], str_found); + /* hmm, remove in progress? */ + /* NB: stat() first so that we see is it a directory; + * but if that fails, use lstat() so that + * we still match dangling links */ + if (stat(found, &st) && lstat(found, &st)) + goto cont; + /* find with dirs? */ + if (paths[i] != dirbuf) + strcpy(found, next->d_name); /* only name */ + + len1 = strlen(found); + found = xrealloc(found, len1 + 2); + found[len1] = '\0'; + found[len1+1] = '\0'; + + if (S_ISDIR(st.st_mode)) { + /* name is a directory */ + if (found[len1-1] != '/') { + found[len1] = '/'; + } + } else { + /* not put found file if search only dirs for cd */ + if (type == FIND_DIR_ONLY) + goto cont; + } + /* Add it to the list */ + add_match(found); + continue; + cont: + free(found); + } + closedir(dir); + } + if (paths != path1) { + free(paths[0]); /* allocated memory is only in first member */ + free(paths); + } +#undef dirbuf +} + +#define QUOT (UCHAR_MAX+1) + +#define collapse_pos(is, in) do { \ + memmove(int_buf+(is), int_buf+(in), (MAX_LINELEN+1-(is)-(in)) * sizeof(pos_buf[0])); \ + memmove(pos_buf+(is), pos_buf+(in), (MAX_LINELEN+1-(is)-(in)) * sizeof(pos_buf[0])); \ +} while (0) + +static int find_match(char *matchBuf, int *len_with_quotes) +{ + int i, j; + int command_mode; + int c, c2; +/* int16_t int_buf[MAX_LINELEN + 1]; */ +/* int16_t pos_buf[MAX_LINELEN + 1]; */ +#define int_buf (S.find_match__int_buf) +#define pos_buf (S.find_match__pos_buf) + + /* set to integer dimension characters and own positions */ + for (i = 0;; i++) { + int_buf[i] = (unsigned char)matchBuf[i]; + if (int_buf[i] == 0) { + pos_buf[i] = -1; /* indicator end line */ + break; + } + pos_buf[i] = i; + } + + /* mask \+symbol and convert '\t' to ' ' */ + for (i = j = 0; matchBuf[i]; i++, j++) + if (matchBuf[i] == '\\') { + collapse_pos(j, j + 1); + int_buf[j] |= QUOT; + i++; +#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT + if (matchBuf[i] == '\t') /* algorithm equivalent */ + int_buf[j] = ' ' | QUOT; +#endif + } +#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT + else if (matchBuf[i] == '\t') + int_buf[j] = ' '; +#endif + + /* mask "symbols" or 'symbols' */ + c2 = 0; + for (i = 0; int_buf[i]; i++) { + c = int_buf[i]; + if (c == '\'' || c == '"') { + if (c2 == 0) + c2 = c; + else { + if (c == c2) + c2 = 0; + else + int_buf[i] |= QUOT; + } + } else if (c2 != 0 && c != '$') + int_buf[i] |= QUOT; + } + + /* skip commands with arguments if line has commands delimiters */ + /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */ + for (i = 0; int_buf[i]; i++) { + c = int_buf[i]; + c2 = int_buf[i + 1]; + j = i ? int_buf[i - 1] : -1; + command_mode = 0; + if (c == ';' || c == '&' || c == '|') { + command_mode = 1 + (c == c2); + if (c == '&') { + if (j == '>' || j == '<') + command_mode = 0; + } else if (c == '|' && j == '>') + command_mode = 0; + } + if (command_mode) { + collapse_pos(0, i + command_mode); + i = -1; /* hack incremet */ + } + } + /* collapse `command...` */ + for (i = 0; int_buf[i]; i++) + if (int_buf[i] == '`') { + for (j = i + 1; int_buf[j]; j++) + if (int_buf[j] == '`') { + collapse_pos(i, j + 1); + j = 0; + break; + } + if (j) { + /* not found close ` - command mode, collapse all previous */ + collapse_pos(0, i + 1); + break; + } else + i--; /* hack incremet */ + } + + /* collapse (command...(command...)...) or {command...{command...}...} */ + c = 0; /* "recursive" level */ + c2 = 0; + for (i = 0; int_buf[i]; i++) + if (int_buf[i] == '(' || int_buf[i] == '{') { + if (int_buf[i] == '(') + c++; + else + c2++; + collapse_pos(0, i + 1); + i = -1; /* hack incremet */ + } + for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++) + if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) { + if (int_buf[i] == ')') + c--; + else + c2--; + collapse_pos(0, i + 1); + i = -1; /* hack incremet */ + } + + /* skip first not quote space */ + for (i = 0; int_buf[i]; i++) + if (int_buf[i] != ' ') + break; + if (i) + collapse_pos(0, i); + + /* set find mode for completion */ + command_mode = FIND_EXE_ONLY; + for (i = 0; int_buf[i]; i++) + if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') { + if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY + && matchBuf[pos_buf[0]] == 'c' + && matchBuf[pos_buf[1]] == 'd' + ) { + command_mode = FIND_DIR_ONLY; + } else { + command_mode = FIND_FILE_ONLY; + break; + } + } + for (i = 0; int_buf[i]; i++) + /* "strlen" */; + /* find last word */ + for (--i; i >= 0; i--) { + c = int_buf[i]; + if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') { + collapse_pos(0, i + 1); + break; + } + } + /* skip first not quoted '\'' or '"' */ + for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++) + /*skip*/; + /* collapse quote or unquote // or /~ */ + while ((int_buf[i] & ~QUOT) == '/' + && ((int_buf[i+1] & ~QUOT) == '/' || (int_buf[i+1] & ~QUOT) == '~') + ) { + i++; + } + + /* set only match and destroy quotes */ + j = 0; + for (c = 0; pos_buf[i] >= 0; i++) { + matchBuf[c++] = matchBuf[pos_buf[i]]; + j = pos_buf[i] + 1; + } + matchBuf[c] = '\0'; + /* old length matchBuf with quotes symbols */ + *len_with_quotes = j ? j - pos_buf[0] : 0; + + return command_mode; +#undef int_buf +#undef pos_buf +} + +/* + * display by column (original idea from ls applet, + * very optimized by me :) + */ +static void showfiles(void) +{ + int ncols, row; + int column_width = 0; + int nfiles = num_matches; + int nrows = nfiles; + int l; + + /* find the longest file name- use that as the column width */ + for (row = 0; row < nrows; row++) { + l = strlen(matches[row]); + if (column_width < l) + column_width = l; + } + column_width += 2; /* min space for columns */ + ncols = cmdedit_termw / column_width; + + if (ncols > 1) { + nrows /= ncols; + if (nfiles % ncols) + nrows++; /* round up fractionals */ + } else { + ncols = 1; + } + for (row = 0; row < nrows; row++) { + int n = row; + int nc; + + for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) { + printf("%s%-*s", matches[n], + (int)(column_width - strlen(matches[n])), ""); + } + puts(matches[n]); + } +} + +static char *add_quote_for_spec_chars(char *found) +{ + int l = 0; + char *s = xmalloc((strlen(found) + 1) * 2); + + while (*found) { + if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found)) + s[l++] = '\\'; + s[l++] = *found++; + } + s[l] = 0; + return s; +} + +/* Do TAB completion */ +static void input_tab(smallint *lastWasTab) +{ + if (!(state->flags & TAB_COMPLETION)) + return; + + if (!*lastWasTab) { + char *tmp, *tmp1; + size_t len_found; +/* char matchBuf[MAX_LINELEN]; */ +#define matchBuf (S.input_tab__matchBuf) + int find_type; + int recalc_pos; + + *lastWasTab = TRUE; /* flop trigger */ + + /* Make a local copy of the string -- up + * to the position of the cursor */ + tmp = strncpy(matchBuf, command_ps, cursor); + tmp[cursor] = '\0'; + + find_type = find_match(matchBuf, &recalc_pos); + + /* Free up any memory already allocated */ + free_tab_completion_data(); + +#if ENABLE_FEATURE_USERNAME_COMPLETION + /* If the word starts with `~' and there is no slash in the word, + * then try completing this word as a username. */ + if (state->flags & USERNAME_COMPLETION) + if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0) + username_tab_completion(matchBuf, NULL); +#endif + /* Try to match any executable in our path and everything + * in the current working directory */ + if (!matches) + exe_n_cwd_tab_completion(matchBuf, find_type); + /* Sort, then remove any duplicates found */ + if (matches) { + unsigned i; + int n = 0; + qsort_string_vector(matches, num_matches); + for (i = 0; i < num_matches - 1; ++i) { + if (matches[i] && matches[i+1]) { /* paranoia */ + if (strcmp(matches[i], matches[i+1]) == 0) { + free(matches[i]); + matches[i] = NULL; /* paranoia */ + } else { + matches[n++] = matches[i]; + } + } + } + matches[n] = matches[i]; + num_matches = n + 1; + } + /* Did we find exactly one match? */ + if (!matches || num_matches > 1) { + beep(); + if (!matches) + return; /* not found */ + /* find minimal match */ + tmp1 = xstrdup(matches[0]); + for (tmp = tmp1; *tmp; tmp++) + for (len_found = 1; len_found < num_matches; len_found++) + if (matches[len_found][(tmp - tmp1)] != *tmp) { + *tmp = '\0'; + break; + } + if (*tmp1 == '\0') { /* have unique */ + free(tmp1); + return; + } + tmp = add_quote_for_spec_chars(tmp1); + free(tmp1); + } else { /* one match */ + tmp = add_quote_for_spec_chars(matches[0]); + /* for next completion current found */ + *lastWasTab = FALSE; + + len_found = strlen(tmp); + if (tmp[len_found-1] != '/') { + tmp[len_found] = ' '; + tmp[len_found+1] = '\0'; + } + } + len_found = strlen(tmp); + /* have space to placed match? */ + if ((len_found - strlen(matchBuf) + command_len) < MAX_LINELEN) { + /* before word for match */ + command_ps[cursor - recalc_pos] = '\0'; + /* save tail line */ + strcpy(matchBuf, command_ps + cursor); + /* add match */ + strcat(command_ps, tmp); + /* add tail */ + strcat(command_ps, matchBuf); + /* back to begin word for match */ + input_backward(recalc_pos); + /* new pos */ + recalc_pos = cursor + len_found; + /* new len */ + command_len = strlen(command_ps); + /* write out the matched command */ + redraw(cmdedit_y, command_len - recalc_pos); + } + free(tmp); +#undef matchBuf + } else { + /* Ok -- the last char was a TAB. Since they + * just hit TAB again, print a list of all the + * available choices... */ + if (matches && num_matches > 0) { + int sav_cursor = cursor; /* change goto_new_line() */ + + /* Go to the next line */ + goto_new_line(); + showfiles(); + redraw(0, command_len - sav_cursor); + } + } +} + +#endif /* FEATURE_COMMAND_TAB_COMPLETION */ + + +#if MAX_HISTORY > 0 + +static void save_command_ps_at_cur_history(void) +{ + if (command_ps[0] != '\0') { + int cur = state->cur_history; + free(state->history[cur]); + state->history[cur] = xstrdup(command_ps); + } +} + +/* state->flags is already checked to be nonzero */ +static int get_previous_history(void) +{ + if ((state->flags & DO_HISTORY) && state->cur_history) { + save_command_ps_at_cur_history(); + state->cur_history--; + return 1; + } + beep(); + return 0; +} + +static int get_next_history(void) +{ + if (state->flags & DO_HISTORY) { + if (state->cur_history < state->cnt_history) { + save_command_ps_at_cur_history(); /* save the current history line */ + return ++state->cur_history; + } + } + beep(); + return 0; +} + +#if ENABLE_FEATURE_EDITING_SAVEHISTORY +/* state->flags is already checked to be nonzero */ +static void load_history(const char *fromfile) +{ + FILE *fp; + int hi; + + /* NB: do not trash old history if file can't be opened */ + + fp = fopen_for_read(fromfile); + if (fp) { + /* clean up old history */ + for (hi = state->cnt_history; hi > 0;) { + hi--; + free(state->history[hi]); + state->history[hi] = NULL; + } + + for (hi = 0; hi < MAX_HISTORY;) { + char *hl = xmalloc_fgetline(fp); + int l; + + if (!hl) + break; + l = strlen(hl); + if (l >= MAX_LINELEN) + hl[MAX_LINELEN-1] = '\0'; + if (l == 0) { + free(hl); + continue; + } + state->history[hi++] = hl; + } + fclose(fp); + state->cnt_history = hi; + } +} + +/* state->flags is already checked to be nonzero */ +static void save_history(const char *tofile) +{ + FILE *fp; + + fp = fopen_for_write(tofile); + if (fp) { + int i; + + for (i = 0; i < state->cnt_history; i++) { + fprintf(fp, "%s\n", state->history[i]); + } + fclose(fp); + } +} +#else +#define load_history(a) ((void)0) +#define save_history(a) ((void)0) +#endif /* FEATURE_COMMAND_SAVEHISTORY */ + +static void remember_in_history(const char *str) +{ + int i; + + if (!(state->flags & DO_HISTORY)) + return; + if (str[0] == '\0') + return; + i = state->cnt_history; + /* Don't save dupes */ + if (i && strcmp(state->history[i-1], str) == 0) + return; + + free(state->history[MAX_HISTORY]); /* redundant, paranoia */ + state->history[MAX_HISTORY] = NULL; /* redundant, paranoia */ + + /* If history[] is full, remove the oldest command */ + /* we need to keep history[MAX_HISTORY] empty, hence >=, not > */ + if (i >= MAX_HISTORY) { + free(state->history[0]); + for (i = 0; i < MAX_HISTORY-1; i++) + state->history[i] = state->history[i+1]; + /* i == MAX_HISTORY-1 */ + } + /* i <= MAX_HISTORY-1 */ + state->history[i++] = xstrdup(str); + /* i <= MAX_HISTORY */ + state->cur_history = i; + state->cnt_history = i; +#if ENABLE_FEATURE_EDITING_SAVEHISTORY + if ((state->flags & SAVE_HISTORY) && state->hist_file) + save_history(state->hist_file); +#endif + USE_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;) +} + +#else /* MAX_HISTORY == 0 */ +#define remember_in_history(a) ((void)0) +#endif /* MAX_HISTORY */ + + +/* + * This function is used to grab a character buffer + * from the input file descriptor and allows you to + * a string with full command editing (sort of like + * a mini readline). + * + * The following standard commands are not implemented: + * ESC-b -- Move back one word + * ESC-f -- Move forward one word + * ESC-d -- Delete back one word + * ESC-h -- Delete forward one word + * CTL-t -- Transpose two characters + * + * Minimalist vi-style command line editing available if configured. + * vi mode implemented 2005 by Paul Fox + */ + +#if ENABLE_FEATURE_EDITING_VI +static void +vi_Word_motion(char *command, int eat) +{ + while (cursor < command_len && !isspace(command[cursor])) + input_forward(); + if (eat) while (cursor < command_len && isspace(command[cursor])) + input_forward(); +} + +static void +vi_word_motion(char *command, int eat) +{ + if (isalnum(command[cursor]) || command[cursor] == '_') { + while (cursor < command_len + && (isalnum(command[cursor+1]) || command[cursor+1] == '_')) + input_forward(); + } else if (ispunct(command[cursor])) { + while (cursor < command_len && ispunct(command[cursor+1])) + input_forward(); + } + + if (cursor < command_len) + input_forward(); + + if (eat && cursor < command_len && isspace(command[cursor])) + while (cursor < command_len && isspace(command[cursor])) + input_forward(); +} + +static void +vi_End_motion(char *command) +{ + input_forward(); + while (cursor < command_len && isspace(command[cursor])) + input_forward(); + while (cursor < command_len-1 && !isspace(command[cursor+1])) + input_forward(); +} + +static void +vi_end_motion(char *command) +{ + if (cursor >= command_len-1) + return; + input_forward(); + while (cursor < command_len-1 && isspace(command[cursor])) + input_forward(); + if (cursor >= command_len-1) + return; + if (isalnum(command[cursor]) || command[cursor] == '_') { + while (cursor < command_len-1 + && (isalnum(command[cursor+1]) || command[cursor+1] == '_') + ) { + input_forward(); + } + } else if (ispunct(command[cursor])) { + while (cursor < command_len-1 && ispunct(command[cursor+1])) + input_forward(); + } +} + +static void +vi_Back_motion(char *command) +{ + while (cursor > 0 && isspace(command[cursor-1])) + input_backward(1); + while (cursor > 0 && !isspace(command[cursor-1])) + input_backward(1); +} + +static void +vi_back_motion(char *command) +{ + if (cursor <= 0) + return; + input_backward(1); + while (cursor > 0 && isspace(command[cursor])) + input_backward(1); + if (cursor <= 0) + return; + if (isalnum(command[cursor]) || command[cursor] == '_') { + while (cursor > 0 + && (isalnum(command[cursor-1]) || command[cursor-1] == '_') + ) { + input_backward(1); + } + } else if (ispunct(command[cursor])) { + while (cursor > 0 && ispunct(command[cursor-1])) + input_backward(1); + } +} +#endif + + +/* + * read_line_input and its helpers + */ + +#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT +static void parse_and_put_prompt(const char *prmt_ptr) +{ + cmdedit_prompt = prmt_ptr; + cmdedit_prmt_len = strlen(prmt_ptr); + put_prompt(); +} +#else +static void parse_and_put_prompt(const char *prmt_ptr) +{ + int prmt_len = 0; + size_t cur_prmt_len = 0; + char flg_not_length = '['; + char *prmt_mem_ptr = xzalloc(1); + char *cwd_buf = xrealloc_getcwd_or_warn(NULL); + char cbuf[2]; + char c; + char *pbuf; + + cmdedit_prmt_len = 0; + + if (!cwd_buf) { + cwd_buf = (char *)bb_msg_unknown; + } + + cbuf[1] = '\0'; /* never changes */ + + while (*prmt_ptr) { + char *free_me = NULL; + + pbuf = cbuf; + c = *prmt_ptr++; + if (c == '\\') { + const char *cp = prmt_ptr; + int l; + + c = bb_process_escape_sequence(&prmt_ptr); + if (prmt_ptr == cp) { + if (*cp == '\0') + break; + c = *prmt_ptr++; + + switch (c) { +#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR + case 'u': + pbuf = user_buf ? user_buf : (char*)""; + break; +#endif + case 'h': + pbuf = free_me = safe_gethostname(); + *strchrnul(pbuf, '.') = '\0'; + break; + case '$': + c = (geteuid() == 0 ? '#' : '$'); + break; +#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR + case 'w': + /* /home/user[/something] -> ~[/something] */ + pbuf = cwd_buf; + l = strlen(home_pwd_buf); + if (l != 0 + && strncmp(home_pwd_buf, cwd_buf, l) == 0 + && (cwd_buf[l]=='/' || cwd_buf[l]=='\0') + && strlen(cwd_buf + l) < PATH_MAX + ) { + pbuf = free_me = xasprintf("~%s", cwd_buf + l); + } + break; +#endif + case 'W': + pbuf = cwd_buf; + cp = strrchr(pbuf, '/'); + if (cp != NULL && cp != pbuf) + pbuf += (cp-pbuf) + 1; + break; + case '!': + pbuf = free_me = xasprintf("%d", num_ok_lines); + break; + case 'e': case 'E': /* \e \E = \033 */ + c = '\033'; + break; + case 'x': case 'X': { + char buf2[4]; + for (l = 0; l < 3;) { + unsigned h; + buf2[l++] = *prmt_ptr; + buf2[l] = '\0'; + h = strtoul(buf2, &pbuf, 16); + if (h > UCHAR_MAX || (pbuf - buf2) < l) { + buf2[--l] = '\0'; + break; + } + prmt_ptr++; + } + c = (char)strtoul(buf2, NULL, 16); + if (c == 0) + c = '?'; + pbuf = cbuf; + break; + } + case '[': case ']': + if (c == flg_not_length) { + flg_not_length = (flg_not_length == '[' ? ']' : '['); + continue; + } + break; + } /* switch */ + } /* if */ + } /* if */ + cbuf[0] = c; + cur_prmt_len = strlen(pbuf); + prmt_len += cur_prmt_len; + if (flg_not_length != ']') + cmdedit_prmt_len += cur_prmt_len; + prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf); + free(free_me); + } /* while */ + + if (cwd_buf != (char *)bb_msg_unknown) + free(cwd_buf); + cmdedit_prompt = prmt_mem_ptr; + put_prompt(); +} +#endif + +static void cmdedit_setwidth(unsigned w, int redraw_flg) +{ + cmdedit_termw = w; + if (redraw_flg) { + /* new y for current cursor */ + int new_y = (cursor + cmdedit_prmt_len) / w; + /* redraw */ + redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor); + fflush(stdout); + } +} + +static void win_changed(int nsig) +{ + unsigned width; + get_terminal_width_height(0, &width, NULL); + cmdedit_setwidth(width, nsig /* - just a yes/no flag */); + if (nsig == SIGWINCH) + signal(SIGWINCH, win_changed); /* rearm ourself */ +} + +/* + * The emacs and vi modes share much of the code in the big + * command loop. Commands entered when in vi's command mode (aka + * "escape mode") get an extra bit added to distinguish them -- + * this keeps them from being self-inserted. This clutters the + * big switch a bit, but keeps all the code in one place. + */ + +#define vbit 0x100 + +/* leave out the "vi-mode"-only case labels if vi editing isn't + * configured. */ +#define vi_case(caselabel) USE_FEATURE_EDITING(case caselabel) + +/* convert uppercase ascii to equivalent control char, for readability */ +#undef CTRL +#define CTRL(a) ((a) & ~0x40) + +/* Returns: + * -1 on read errors or EOF, or on bare Ctrl-D, + * 0 on ctrl-C (the line entered is still returned in 'command'), + * >0 length of input string, including terminating '\n' + */ +int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, line_input_t *st) +{ + int len; +#if ENABLE_FEATURE_TAB_COMPLETION + smallint lastWasTab = FALSE; +#endif + unsigned ic; + unsigned char c; + smallint break_out = 0; +#if ENABLE_FEATURE_EDITING_VI + smallint vi_cmdmode = 0; + smalluint prevc; +#endif + struct termios initial_settings; + struct termios new_settings; + + INIT_S(); + + if (tcgetattr(STDIN_FILENO, &initial_settings) < 0 + || !(initial_settings.c_lflag & ECHO) + ) { + /* Happens when e.g. stty -echo was run before */ + parse_and_put_prompt(prompt); + fflush(stdout); + if (fgets(command, maxsize, stdin) == NULL) + len = -1; /* EOF or error */ + else + len = strlen(command); + DEINIT_S(); + return len; + } + +// FIXME: audit & improve this + if (maxsize > MAX_LINELEN) + maxsize = MAX_LINELEN; + + /* With null flags, no other fields are ever used */ + state = st ? st : (line_input_t*) &const_int_0; +#if ENABLE_FEATURE_EDITING_SAVEHISTORY + if ((state->flags & SAVE_HISTORY) && state->hist_file) + load_history(state->hist_file); +#endif +#if MAX_HISTORY > 0 + if (state->flags & DO_HISTORY) + state->cur_history = state->cnt_history; +#endif + + /* prepare before init handlers */ + cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */ + command_len = 0; + command_ps = command; + command[0] = '\0'; + + new_settings = initial_settings; + new_settings.c_lflag &= ~ICANON; /* unbuffered input */ + /* Turn off echoing and CTRL-C, so we can trap it */ + new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG); + /* Hmm, in linux c_cc[] is not parsed if ICANON is off */ + new_settings.c_cc[VMIN] = 1; + new_settings.c_cc[VTIME] = 0; + /* Turn off CTRL-C, so we can trap it */ +#ifndef _POSIX_VDISABLE +#define _POSIX_VDISABLE '\0' +#endif + new_settings.c_cc[VINTR] = _POSIX_VDISABLE; + tcsetattr_stdin_TCSANOW(&new_settings); + + /* Now initialize things */ + previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); + win_changed(0); /* do initial resizing */ +#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR + { + struct passwd *entry; + + entry = getpwuid(geteuid()); + if (entry) { + user_buf = xstrdup(entry->pw_name); + home_pwd_buf = xstrdup(entry->pw_dir); + } + } +#endif + +#if 0 + for (ic = 0; ic <= MAX_HISTORY; ic++) + bb_error_msg("history[%d]:'%s'", ic, state->history[ic]); + bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history); +#endif + + /* Print out the command prompt */ + parse_and_put_prompt(prompt); + + while (1) { + fflush(NULL); + + if (nonblock_safe_read(STDIN_FILENO, &c, 1) < 1) { + /* if we can't read input then exit */ + goto prepare_to_die; + } + + ic = c; + +#if ENABLE_FEATURE_EDITING_VI + newdelflag = 1; + if (vi_cmdmode) + ic |= vbit; +#endif + switch (ic) { + case '\n': + case '\r': + vi_case('\n'|vbit:) + vi_case('\r'|vbit:) + /* Enter */ + goto_new_line(); + break_out = 1; + break; + case CTRL('A'): + vi_case('0'|vbit:) + /* Control-a -- Beginning of line */ + input_backward(cursor); + break; + case CTRL('B'): + vi_case('h'|vbit:) + vi_case('\b'|vbit:) + vi_case('\x7f'|vbit:) /* DEL */ + /* Control-b -- Move back one character */ + input_backward(1); + break; + case CTRL('C'): + vi_case(CTRL('C')|vbit:) + /* Control-c -- stop gathering input */ + goto_new_line(); + command_len = 0; + break_out = -1; /* "do not append '\n'" */ + break; + case CTRL('D'): + /* Control-d -- Delete one character, or exit + * if the len=0 and no chars to delete */ + if (command_len == 0) { + errno = 0; + prepare_to_die: + /* to control stopped jobs */ + break_out = command_len = -1; + break; + } + input_delete(0); + break; + + case CTRL('E'): + vi_case('$'|vbit:) + /* Control-e -- End of line */ + input_end(); + break; + case CTRL('F'): + vi_case('l'|vbit:) + vi_case(' '|vbit:) + /* Control-f -- Move forward one character */ + input_forward(); + break; + + case '\b': + case '\x7f': /* DEL */ + /* Control-h and DEL */ + input_backspace(); + break; + +#if ENABLE_FEATURE_TAB_COMPLETION + case '\t': + input_tab(&lastWasTab); + break; +#endif + + case CTRL('K'): + /* Control-k -- clear to end of line */ + command[cursor] = 0; + command_len = cursor; + printf("\033[J"); + break; + case CTRL('L'): + vi_case(CTRL('L')|vbit:) + /* Control-l -- clear screen */ + printf("\033[H"); + redraw(0, command_len - cursor); + break; + +#if MAX_HISTORY > 0 + case CTRL('N'): + vi_case(CTRL('N')|vbit:) + vi_case('j'|vbit:) + /* Control-n -- Get next command in history */ + if (get_next_history()) + goto rewrite_line; + break; + case CTRL('P'): + vi_case(CTRL('P')|vbit:) + vi_case('k'|vbit:) + /* Control-p -- Get previous command from history */ + if (get_previous_history()) + goto rewrite_line; + break; +#endif + + case CTRL('U'): + vi_case(CTRL('U')|vbit:) + /* Control-U -- Clear line before cursor */ + if (cursor) { + overlapping_strcpy(command, command + cursor); + command_len -= cursor; + redraw(cmdedit_y, command_len); + } + break; + case CTRL('W'): + vi_case(CTRL('W')|vbit:) + /* Control-W -- Remove the last word */ + while (cursor > 0 && isspace(command[cursor-1])) + input_backspace(); + while (cursor > 0 && !isspace(command[cursor-1])) + input_backspace(); + break; + +#if ENABLE_FEATURE_EDITING_VI + case 'i'|vbit: + vi_cmdmode = 0; + break; + case 'I'|vbit: + input_backward(cursor); + vi_cmdmode = 0; + break; + case 'a'|vbit: + input_forward(); + vi_cmdmode = 0; + break; + case 'A'|vbit: + input_end(); + vi_cmdmode = 0; + break; + case 'x'|vbit: + input_delete(1); + break; + case 'X'|vbit: + if (cursor > 0) { + input_backward(1); + input_delete(1); + } + break; + case 'W'|vbit: + vi_Word_motion(command, 1); + break; + case 'w'|vbit: + vi_word_motion(command, 1); + break; + case 'E'|vbit: + vi_End_motion(command); + break; + case 'e'|vbit: + vi_end_motion(command); + break; + case 'B'|vbit: + vi_Back_motion(command); + break; + case 'b'|vbit: + vi_back_motion(command); + break; + case 'C'|vbit: + vi_cmdmode = 0; + /* fall through */ + case 'D'|vbit: + goto clear_to_eol; + + case 'c'|vbit: + vi_cmdmode = 0; + /* fall through */ + case 'd'|vbit: { + int nc, sc; + sc = cursor; + prevc = ic; + if (safe_read(STDIN_FILENO, &c, 1) < 1) + goto prepare_to_die; + if (c == (prevc & 0xff)) { + /* "cc", "dd" */ + input_backward(cursor); + goto clear_to_eol; + break; + } + switch (c) { + case 'w': + case 'W': + case 'e': + case 'E': + switch (c) { + case 'w': /* "dw", "cw" */ + vi_word_motion(command, vi_cmdmode); + break; + case 'W': /* 'dW', 'cW' */ + vi_Word_motion(command, vi_cmdmode); + break; + case 'e': /* 'de', 'ce' */ + vi_end_motion(command); + input_forward(); + break; + case 'E': /* 'dE', 'cE' */ + vi_End_motion(command); + input_forward(); + break; + } + nc = cursor; + input_backward(cursor - sc); + while (nc-- > cursor) + input_delete(1); + break; + case 'b': /* "db", "cb" */ + case 'B': /* implemented as B */ + if (c == 'b') + vi_back_motion(command); + else + vi_Back_motion(command); + while (sc-- > cursor) + input_delete(1); + break; + case ' ': /* "d ", "c " */ + input_delete(1); + break; + case '$': /* "d$", "c$" */ + clear_to_eol: + while (cursor < command_len) + input_delete(1); + break; + } + break; + } + case 'p'|vbit: + input_forward(); + /* fallthrough */ + case 'P'|vbit: + put(); + break; + case 'r'|vbit: + if (safe_read(STDIN_FILENO, &c, 1) < 1) + goto prepare_to_die; + if (c == 0) + beep(); + else { + *(command + cursor) = c; + bb_putchar(c); + bb_putchar('\b'); + } + break; +#endif /* FEATURE_COMMAND_EDITING_VI */ + + case '\x1b': /* ESC */ + +#if ENABLE_FEATURE_EDITING_VI + if (state->flags & VI_MODE) { + /* ESC: insert mode --> command mode */ + vi_cmdmode = 1; + input_backward(1); + break; + } +#endif + /* escape sequence follows */ + if (safe_read(STDIN_FILENO, &c, 1) < 1) + goto prepare_to_die; + /* different vt100 emulations */ + if (c == '[' || c == 'O') { + vi_case('['|vbit:) + vi_case('O'|vbit:) + if (safe_read(STDIN_FILENO, &c, 1) < 1) + goto prepare_to_die; + } + if (c >= '1' && c <= '9') { + unsigned char dummy; + + if (safe_read(STDIN_FILENO, &dummy, 1) < 1) + goto prepare_to_die; + if (dummy != '~') + c = '\0'; + } + + switch (c) { +#if ENABLE_FEATURE_TAB_COMPLETION + case '\t': /* Alt-Tab */ + input_tab(&lastWasTab); + break; +#endif +#if MAX_HISTORY > 0 + case 'A': + /* Up Arrow -- Get previous command from history */ + if (get_previous_history()) + goto rewrite_line; + beep(); + break; + case 'B': + /* Down Arrow -- Get next command in history */ + if (!get_next_history()) + break; + rewrite_line: + /* Rewrite the line with the selected history item */ + /* change command */ + command_len = strlen(strcpy(command, state->history[state->cur_history] ? : "")); + /* redraw and go to eol (bol, in vi */ + redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0); + break; +#endif + case 'C': + /* Right Arrow -- Move forward one character */ + input_forward(); + break; + case 'D': + /* Left Arrow -- Move back one character */ + input_backward(1); + break; + case '3': + /* Delete */ + input_delete(0); + break; + case '1': // vt100? linux vt? or what? + case '7': // vt100? linux vt? or what? + case 'H': /* xterm's */ + input_backward(cursor); + break; + case '4': // vt100? linux vt? or what? + case '8': // vt100? linux vt? or what? + case 'F': /* xterm's */ + input_end(); + break; + default: + c = '\0'; + beep(); + } + break; + + default: /* If it's regular input, do the normal thing */ + + /* Control-V -- force insert of next char */ + if (c == CTRL('V')) { + if (safe_read(STDIN_FILENO, &c, 1) < 1) + goto prepare_to_die; + if (c == 0) { + beep(); + break; + } + } + +#if ENABLE_FEATURE_EDITING_VI + if (vi_cmdmode) /* Don't self-insert */ + break; +#endif + if ((int)command_len >= (maxsize - 2)) /* Need to leave space for enter */ + break; + + command_len++; + if (cursor == (command_len - 1)) { /* Append if at the end of the line */ + command[cursor] = c; + command[cursor+1] = '\0'; + cmdedit_set_out_char(' '); + } else { /* Insert otherwise */ + int sc = cursor; + + memmove(command + sc + 1, command + sc, command_len - sc); + command[sc] = c; + sc++; + /* rewrite from cursor */ + input_end(); + /* to prev x pos + 1 */ + input_backward(cursor - sc); + } + break; + } + if (break_out) /* Enter is the command terminator, no more input. */ + break; + +#if ENABLE_FEATURE_TAB_COMPLETION + if (c != '\t') + lastWasTab = FALSE; +#endif + } + + if (command_len > 0) + remember_in_history(command); + + if (break_out > 0) { + command[command_len++] = '\n'; + command[command_len] = '\0'; + } + +#if ENABLE_FEATURE_TAB_COMPLETION + free_tab_completion_data(); +#endif + + /* restore initial_settings */ + tcsetattr_stdin_TCSANOW(&initial_settings); + /* restore SIGWINCH handler */ + signal(SIGWINCH, previous_SIGWINCH_handler); + fflush(stdout); + + len = command_len; + DEINIT_S(); + + return len; /* can't return command_len, DEINIT_S() destroys it */ +} + +line_input_t* FAST_FUNC new_line_input_t(int flags) +{ + line_input_t *n = xzalloc(sizeof(*n)); + n->flags = flags; + return n; +} + +#else + +#undef read_line_input +int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize) +{ + fputs(prompt, stdout); + fflush(stdout); + fgets(command, maxsize, stdin); + return strlen(command); +} + +#endif /* FEATURE_COMMAND_EDITING */ + + +/* + * Testing + */ + +#ifdef TEST + +#include + +const char *applet_name = "debug stuff usage"; + +int main(int argc, char **argv) +{ + char buff[MAX_LINELEN]; + char *prompt = +#if ENABLE_FEATURE_EDITING_FANCY_PROMPT + "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:" + "\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] " + "\\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]"; +#else + "% "; +#endif + +#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT + setlocale(LC_ALL, ""); +#endif + while (1) { + int l; + l = read_line_input(prompt, buff); + if (l <= 0 || buff[l-1] != '\n') + break; + buff[l-1] = 0; + printf("*** read_line_input() returned line =%s=\n", buff); + } + printf("*** read_line_input() detect ^D\n"); + return 0; +} + +#endif /* TEST */ diff --git a/libbb/lineedit_ptr_hack.c b/libbb/lineedit_ptr_hack.c new file mode 100644 index 0000000..53716a2 --- /dev/null +++ b/libbb/lineedit_ptr_hack.c @@ -0,0 +1,23 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2008 by Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +struct lineedit_statics; + +#ifndef GCC_COMBINE + +/* We cheat here. It is declared as const ptr in libbb.h, + * but here we make it live in R/W memory */ +struct lineedit_statics *lineedit_ptr_to_statics; + +#else + +/* gcc -combine will see through and complain */ +/* Using alternative method which is more likely to break + * on weird architectures, compilers, linkers and so on */ +struct lineedit_statics *const lineedit_ptr_to_statics __attribute__ ((section (".data"))); + +#endif diff --git a/libbb/llist.c b/libbb/llist.c new file mode 100644 index 0000000..7e78f7c --- /dev/null +++ b/libbb/llist.c @@ -0,0 +1,106 @@ +/* vi: set sw=4 ts=4: */ +/* + * linked list helper functions. + * + * Copyright (C) 2003 Glenn McGrath + * Copyright (C) 2005 Vladimir Oleynik + * Copyright (C) 2005 Bernhard Reutner-Fischer + * Copyright (C) 2006 Rob Landley + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include "libbb.h" + +/* Add data to the start of the linked list. */ +void FAST_FUNC llist_add_to(llist_t **old_head, void *data) +{ + llist_t *new_head = xmalloc(sizeof(llist_t)); + + new_head->data = data; + new_head->link = *old_head; + *old_head = new_head; +} + +/* Add data to the end of the linked list. */ +void FAST_FUNC llist_add_to_end(llist_t **list_head, void *data) +{ + llist_t *new_item = xmalloc(sizeof(llist_t)); + + new_item->data = data; + new_item->link = NULL; + + if (!*list_head) + *list_head = new_item; + else { + llist_t *tail = *list_head; + + while (tail->link) + tail = tail->link; + tail->link = new_item; + } +} + +/* Remove first element from the list and return it */ +void* FAST_FUNC llist_pop(llist_t **head) +{ + void *data, *next; + + if (!*head) + return NULL; + + data = (*head)->data; + next = (*head)->link; + free(*head); + *head = next; + + return data; +} + +/* Unlink arbitrary given element from the list */ +void FAST_FUNC llist_unlink(llist_t **head, llist_t *elm) +{ + llist_t *crt; + + if (!(elm && *head)) + return; + + if (elm == *head) { + *head = (*head)->link; + return; + } + + for (crt = *head; crt; crt = crt->link) { + if (crt->link == elm) { + crt->link = elm->link; + return; + } + } +} + +/* Recursively free all elements in the linked list. If freeit != NULL + * call it on each datum in the list */ +void FAST_FUNC llist_free(llist_t *elm, void (*freeit) (void *data)) +{ + while (elm) { + void *data = llist_pop(&elm); + + if (freeit) + freeit(data); + } +} + +/* Reverse list order. */ +llist_t* FAST_FUNC llist_rev(llist_t *list) +{ + llist_t *rev = NULL; + + while (list) { + llist_t *next = list->link; + + list->link = rev; + rev = list; + list = next; + } + return rev; +} diff --git a/libbb/login.c b/libbb/login.c new file mode 100644 index 0000000..b3e199c --- /dev/null +++ b/libbb/login.c @@ -0,0 +1,129 @@ +/* vi: set sw=4 ts=4: */ +/* + * issue.c: issue printing code + * + * Copyright (C) 2003 Bastian Blank + * + * Optimize and correcting OCRNL by Vladimir Oleynik + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include /* MAXHOSTNAMELEN */ +#include +#include "libbb.h" + +#define LOGIN " login: " + +static const char fmtstr_d[] ALIGN1 = "%A, %d %B %Y"; +static const char fmtstr_t[] ALIGN1 = "%H:%M:%S"; + +void FAST_FUNC print_login_issue(const char *issue_file, const char *tty) +{ + FILE *fp; + int c; + char buf[256+1]; + const char *outbuf; + time_t t; + struct utsname uts; + + time(&t); + uname(&uts); + + puts("\r"); /* start a new line */ + + fp = fopen_for_read(issue_file); + if (!fp) + return; + while ((c = fgetc(fp)) != EOF) { + outbuf = buf; + buf[0] = c; + buf[1] = '\0'; + if (c == '\n') { + buf[1] = '\r'; + buf[2] = '\0'; + } + if (c == '\\' || c == '%') { + c = fgetc(fp); + switch (c) { + case 's': + outbuf = uts.sysname; + break; + case 'n': + case 'h': + outbuf = uts.nodename; + break; + case 'r': + outbuf = uts.release; + break; + case 'v': + outbuf = uts.version; + break; + case 'm': + outbuf = uts.machine; + break; + case 'D': + case 'o': + outbuf = uts.domainname; + break; + case 'd': + strftime(buf, sizeof(buf), fmtstr_d, localtime(&t)); + break; + case 't': + strftime(buf, sizeof(buf), fmtstr_t, localtime(&t)); + break; + case 'l': + outbuf = tty; + break; + default: + buf[0] = c; + } + } + fputs(outbuf, stdout); + } + fclose(fp); + fflush(stdout); +} + +void FAST_FUNC print_login_prompt(void) +{ + char *hostname = safe_gethostname(); + + fputs(hostname, stdout); + fputs(LOGIN, stdout); + fflush(stdout); + free(hostname); +} + +/* Clear dangerous stuff, set PATH */ +static const char forbid[] ALIGN1 = + "ENV" "\0" + "BASH_ENV" "\0" + "HOME" "\0" + "IFS" "\0" + "SHELL" "\0" + "LD_LIBRARY_PATH" "\0" + "LD_PRELOAD" "\0" + "LD_TRACE_LOADED_OBJECTS" "\0" + "LD_BIND_NOW" "\0" + "LD_AOUT_LIBRARY_PATH" "\0" + "LD_AOUT_PRELOAD" "\0" + "LD_NOWARN" "\0" + "LD_KEEPDIR" "\0"; + +int FAST_FUNC sanitize_env_if_suid(void) +{ + const char *p; + + if (getuid() == geteuid()) + return 0; + + p = forbid; + do { + unsetenv(p); + p += strlen(p) + 1; + } while (*p); + putenv((char*)bb_PATH_root_path); + + return 1; /* we indeed were run by different user! */ +} diff --git a/libbb/loop.c b/libbb/loop.c new file mode 100644 index 0000000..7d2b420 --- /dev/null +++ b/libbb/loop.c @@ -0,0 +1,154 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * Copyright (C) 2005 by Rob Landley + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include "libbb.h" + +/* For 2.6, use the cleaned up header to get the 64 bit API. */ +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +#include +typedef struct loop_info64 bb_loop_info; +#define BB_LOOP_SET_STATUS LOOP_SET_STATUS64 +#define BB_LOOP_GET_STATUS LOOP_GET_STATUS64 + +/* For 2.4 and earlier, use the 32 bit API (and don't trust the headers) */ +#else +/* Stuff stolen from linux/loop.h for 2.4 and earlier kernels*/ +#include +#define LO_NAME_SIZE 64 +#define LO_KEY_SIZE 32 +#define LOOP_SET_FD 0x4C00 +#define LOOP_CLR_FD 0x4C01 +#define BB_LOOP_SET_STATUS 0x4C02 +#define BB_LOOP_GET_STATUS 0x4C03 +typedef struct { + int lo_number; + __kernel_dev_t lo_device; + unsigned long lo_inode; + __kernel_dev_t lo_rdevice; + int lo_offset; + int lo_encrypt_type; + int lo_encrypt_key_size; + int lo_flags; + char lo_file_name[LO_NAME_SIZE]; + unsigned char lo_encrypt_key[LO_KEY_SIZE]; + unsigned long lo_init[2]; + char reserved[4]; +} bb_loop_info; +#endif + +char* FAST_FUNC query_loop(const char *device) +{ + int fd; + bb_loop_info loopinfo; + char *dev = 0; + + fd = open(device, O_RDONLY); + if (fd < 0) return 0; + if (!ioctl(fd, BB_LOOP_GET_STATUS, &loopinfo)) + dev = xasprintf("%ld %s", (long) loopinfo.lo_offset, + (char *)loopinfo.lo_file_name); + close(fd); + + return dev; +} + + +int FAST_FUNC del_loop(const char *device) +{ + int fd, rc; + + fd = open(device, O_RDONLY); + if (fd < 0) return 1; + rc = ioctl(fd, LOOP_CLR_FD, 0); + close(fd); + + return rc; +} + +/* Returns 0 if mounted RW, 1 if mounted read-only, <0 for error. + *device is loop device to use, or if *device==NULL finds a loop device to + mount it on and sets *device to a strdup of that loop device name. This + search will re-use an existing loop device already bound to that + file/offset if it finds one. + */ +int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offset) +{ + char dev[LOOP_NAMESIZE]; + char *try; + bb_loop_info loopinfo; + struct stat statbuf; + int i, dfd, ffd, mode, rc = -1; + + /* Open the file. Barf if this doesn't work. */ + mode = O_RDWR; + ffd = open(file, mode); + if (ffd < 0) { + mode = O_RDONLY; + ffd = open(file, mode); + if (ffd < 0) + return -errno; + } + + /* Find a loop device. */ + try = *device ? : dev; + for (i = 0; rc; i++) { + sprintf(dev, LOOP_FORMAT, i); + + /* Ran out of block devices, return failure. */ + if (stat(try, &statbuf) || !S_ISBLK(statbuf.st_mode)) { + rc = -ENOENT; + break; + } + /* Open the sucker and check its loopiness. */ + dfd = open(try, mode); + if (dfd < 0 && errno == EROFS) { + mode = O_RDONLY; + dfd = open(try, mode); + } + if (dfd < 0) + goto try_again; + + rc = ioctl(dfd, BB_LOOP_GET_STATUS, &loopinfo); + + /* If device is free, claim it. */ + if (rc && errno == ENXIO) { + memset(&loopinfo, 0, sizeof(loopinfo)); + safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE); + loopinfo.lo_offset = offset; + /* Associate free loop device with file. */ + if (!ioctl(dfd, LOOP_SET_FD, ffd)) { + if (!ioctl(dfd, BB_LOOP_SET_STATUS, &loopinfo)) + rc = 0; + else + ioctl(dfd, LOOP_CLR_FD, 0); + } + + /* If this block device already set up right, re-use it. + (Yes this is racy, but associating two loop devices with the same + file isn't pretty either. In general, mounting the same file twice + without using losetup manually is problematic.) + */ + } else if (strcmp(file, (char *)loopinfo.lo_file_name) != 0 + || offset != loopinfo.lo_offset) { + rc = -1; + } + close(dfd); + try_again: + if (*device) break; + } + close(ffd); + if (!rc) { + if (!*device) + *device = xstrdup(dev); + return (mode == O_RDONLY); /* 1:ro, 0:rw */ + } + return rc; +} diff --git a/libbb/make_directory.c b/libbb/make_directory.c new file mode 100644 index 0000000..391493c --- /dev/null +++ b/libbb/make_directory.c @@ -0,0 +1,98 @@ +/* vi: set sw=4 ts=4: */ +/* + * parse_mode implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* Mar 5, 2003 Manuel Novoa III + * + * This is the main work function for the 'mkdir' applet. As such, it + * strives to be SUSv3 compliant in it's behaviour when recursively + * making missing parent dirs, and in it's mode setting of the final + * directory 'path'. + * + * To recursively build all missing intermediate directories, make + * sure that (flags & FILEUTILS_RECUR) is non-zero. Newly created + * intermediate directories will have at least u+wx perms. + * + * To set specific permissions on 'path', pass the appropriate 'mode' + * val. Otherwise, pass -1 to get default permissions. + */ + +#include "libbb.h" + +/* This function is used from NOFORK applets. It must not allocate anything */ + +int FAST_FUNC bb_make_directory(char *path, long mode, int flags) +{ + mode_t mask; + const char *fail_msg; + char *s = path; + char c; + struct stat st; + + mask = umask(0); + umask(mask & ~0300); /* Ensure intermediate dirs are wx */ + + while (1) { + c = '\0'; + + if (flags & FILEUTILS_RECUR) { /* Get the parent. */ + /* Bypass leading non-'/'s and then subsequent '/'s. */ + while (*s) { + if (*s == '/') { + do { + ++s; + } while (*s == '/'); + c = *s; /* Save the current char */ + *s = '\0'; /* and replace it with nul. */ + break; + } + ++s; + } + } + + if (!c) /* Last component uses orig umask */ + umask(mask); + + if (mkdir(path, 0777) < 0) { + /* If we failed for any other reason than the directory + * already exists, output a diagnostic and return -1. */ + if (errno != EEXIST + || !(flags & FILEUTILS_RECUR) + || ((stat(path, &st) < 0) || !S_ISDIR(st.st_mode)) + ) { + fail_msg = "create"; + umask(mask); + break; + } + /* Since the directory exists, don't attempt to change + * permissions if it was the full target. Note that + * this is not an error condition. */ + if (!c) { + umask(mask); + return 0; + } + } + + if (!c) { + /* Done. If necessary, update perms on the newly + * created directory. Failure to update here _is_ + * an error. */ + if ((mode != -1) && (chmod(path, mode) < 0)) { + fail_msg = "set permissions of"; + break; + } + return 0; + } + + /* Remove any inserted nul from the path (recursive mode). */ + *s = c; + } /* while (1) */ + + bb_perror_msg("cannot %s directory '%s'", fail_msg, path); + return -1; +} diff --git a/libbb/makedev.c b/libbb/makedev.c new file mode 100644 index 0000000..ca71fdb --- /dev/null +++ b/libbb/makedev.c @@ -0,0 +1,24 @@ +/* + * Utility routines. + * + * Copyright (C) 2006 Denys Vlasenko + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ + +/* We do not include libbb.h - #define makedev() is there! */ +#include "platform.h" +#include +#include + +#ifdef __GLIBC__ +/* At least glibc has horrendously large inline for this, so wrap it */ +/* uclibc people please check - do we need "&& !__UCLIBC__" above? */ + +/* suppress gcc "no previous prototype" warning */ +unsigned long long FAST_FUNC bb_makedev(unsigned int major, unsigned int minor); +unsigned long long FAST_FUNC bb_makedev(unsigned int major, unsigned int minor) +{ + return makedev(major, minor); +} +#endif diff --git a/libbb/match_fstype.c b/libbb/match_fstype.c new file mode 100644 index 0000000..99e2767 --- /dev/null +++ b/libbb/match_fstype.c @@ -0,0 +1,44 @@ +/* vi: set sw=4 ts=4: */ +/* + * Match fstypes for use in mount unmount + * We accept notmpfs,nfs but not notmpfs,nonfs + * This allows us to match fstypes that start with no like so + * mount -at ,noddy + * + * Returns 0 for a match, otherwise -1 + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +int FAST_FUNC match_fstype(const struct mntent *mt, const char *fstype) +{ + int no = 0; + int len; + + if (!mt) + return -1; + + if (!fstype) + return 0; + + if (fstype[0] == 'n' && fstype[1] == 'o') { + no = -1; + fstype += 2; + } + + len = strlen(mt->mnt_type); + while (fstype) { + if (!strncmp(mt->mnt_type, fstype, len) + && (!fstype[len] || fstype[len] == ',') + ) { + return no; + } + fstype = strchr(fstype, ','); + if (fstype) + fstype++; + } + + return -(no + 1); +} diff --git a/libbb/md5.c b/libbb/md5.c new file mode 100644 index 0000000..4ab06eb --- /dev/null +++ b/libbb/md5.c @@ -0,0 +1,446 @@ +/* vi: set sw=4 ts=4: */ +/* + * md5.c - Compute MD5 checksum of strings according to the + * definition of MD5 in RFC 1321 from April 1992. + * + * Written by Ulrich Drepper , 1995. + * + * Copyright (C) 1995-1999 Free Software Foundation, Inc. + * Copyright (C) 2001 Manuel Novoa III + * Copyright (C) 2003 Glenn L. McGrath + * Copyright (C) 2003 Erik Andersen + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include "libbb.h" + +#if CONFIG_MD5_SIZE_VS_SPEED < 0 || CONFIG_MD5_SIZE_VS_SPEED > 3 +# define MD5_SIZE_VS_SPEED 2 +#else +# define MD5_SIZE_VS_SPEED CONFIG_MD5_SIZE_VS_SPEED +#endif + +/* Initialize structure containing state of computation. + * (RFC 1321, 3.3: Step 3) + */ +void FAST_FUNC md5_begin(md5_ctx_t *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total = 0; + ctx->buflen = 0; +} + +/* These are the four functions used in the four steps of the MD5 algorithm + * and defined in the RFC 1321. The first function is a little bit optimized + * (as found in Colin Plumbs public domain implementation). + * #define FF(b, c, d) ((b & c) | (~b & d)) + */ +# define FF(b, c, d) (d ^ (b & (c ^ d))) +# define FG(b, c, d) FF (d, b, c) +# define FH(b, c, d) (b ^ c ^ d) +# define FI(b, c, d) (c ^ (b | ~d)) + +/* Hash a single block, 64 bytes long and 4-byte aligned. */ +static void md5_hash_block(const void *buffer, md5_ctx_t *ctx) +{ + uint32_t correct_words[16]; + const uint32_t *words = buffer; + +# if MD5_SIZE_VS_SPEED > 0 + static const uint32_t C_array[] = { + /* round 1 */ + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + /* round 2 */ + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + /* round 3 */ + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + /* round 4 */ + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 + }; + + static const char P_array[] ALIGN1 = { +# if MD5_SIZE_VS_SPEED > 1 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */ +# endif /* MD5_SIZE_VS_SPEED > 1 */ + 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */ + 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */ + 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */ + }; + +# if MD5_SIZE_VS_SPEED > 1 + static const char S_array[] ALIGN1 = { + 7, 12, 17, 22, + 5, 9, 14, 20, + 4, 11, 16, 23, + 6, 10, 15, 21 + }; +# endif /* MD5_SIZE_VS_SPEED > 1 */ +# endif + + uint32_t A = ctx->A; + uint32_t B = ctx->B; + uint32_t C = ctx->C; + uint32_t D = ctx->D; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + uint32_t *cwp = correct_words; + uint32_t A_save = A; + uint32_t B_save = B; + uint32_t C_save = C; + uint32_t D_save = D; + +# if MD5_SIZE_VS_SPEED > 1 +# define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + const uint32_t *pc; + const char *pp; + const char *ps; + int i; + uint32_t temp; + + for (i = 0; i < 16; i++) { + cwp[i] = SWAP_LE32(words[i]); + } + words += 16; + +# if MD5_SIZE_VS_SPEED > 2 + pc = C_array; + pp = P_array; + ps = S_array - 4; + + for (i = 0; i < 64; i++) { + if ((i & 0x0f) == 0) + ps += 4; + temp = A; + switch (i >> 4) { + case 0: + temp += FF(B, C, D); + break; + case 1: + temp += FG(B, C, D); + break; + case 2: + temp += FH(B, C, D); + break; + case 3: + temp += FI(B, C, D); + } + temp += cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } +# else + pc = C_array; + pp = P_array; + ps = S_array; + + for (i = 0; i < 16; i++) { + temp = A + FF(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + + ps += 4; + for (i = 0; i < 16; i++) { + temp = A + FG(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + ps += 4; + for (i = 0; i < 16; i++) { + temp = A + FH(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + ps += 4; + for (i = 0; i < 16; i++) { + temp = A + FI(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + +# endif /* MD5_SIZE_VS_SPEED > 2 */ +# else + /* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +# define OP(a, b, c, d, s, T) \ + do { \ + a += FF (b, c, d) + (*cwp++ = SWAP_LE32(*words)) + T; \ + ++words; \ + CYCLIC (a, s); \ + a += b; \ + } while (0) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ + /* gcc 2.95.4 seems to be --aaronl */ +# define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + /* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 + */ + +# if MD5_SIZE_VS_SPEED == 1 + const uint32_t *pc; + const char *pp; + int i; +# endif /* MD5_SIZE_VS_SPEED */ + + /* Round 1. */ +# if MD5_SIZE_VS_SPEED == 1 + pc = C_array; + for (i = 0; i < 4; i++) { + OP(A, B, C, D, 7, *pc++); + OP(D, A, B, C, 12, *pc++); + OP(C, D, A, B, 17, *pc++); + OP(B, C, D, A, 22, *pc++); + } +# else + OP(A, B, C, D, 7, 0xd76aa478); + OP(D, A, B, C, 12, 0xe8c7b756); + OP(C, D, A, B, 17, 0x242070db); + OP(B, C, D, A, 22, 0xc1bdceee); + OP(A, B, C, D, 7, 0xf57c0faf); + OP(D, A, B, C, 12, 0x4787c62a); + OP(C, D, A, B, 17, 0xa8304613); + OP(B, C, D, A, 22, 0xfd469501); + OP(A, B, C, D, 7, 0x698098d8); + OP(D, A, B, C, 12, 0x8b44f7af); + OP(C, D, A, B, 17, 0xffff5bb1); + OP(B, C, D, A, 22, 0x895cd7be); + OP(A, B, C, D, 7, 0x6b901122); + OP(D, A, B, C, 12, 0xfd987193); + OP(C, D, A, B, 17, 0xa679438e); + OP(B, C, D, A, 22, 0x49b40821); +# endif /* MD5_SIZE_VS_SPEED == 1 */ + + /* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +# undef OP +# define OP(f, a, b, c, d, k, s, T) \ + do { \ + a += f (b, c, d) + correct_words[k] + T; \ + CYCLIC (a, s); \ + a += b; \ + } while (0) + + /* Round 2. */ +# if MD5_SIZE_VS_SPEED == 1 + pp = P_array; + for (i = 0; i < 4; i++) { + OP(FG, A, B, C, D, (int) (*pp++), 5, *pc++); + OP(FG, D, A, B, C, (int) (*pp++), 9, *pc++); + OP(FG, C, D, A, B, (int) (*pp++), 14, *pc++); + OP(FG, B, C, D, A, (int) (*pp++), 20, *pc++); + } +# else + OP(FG, A, B, C, D, 1, 5, 0xf61e2562); + OP(FG, D, A, B, C, 6, 9, 0xc040b340); + OP(FG, C, D, A, B, 11, 14, 0x265e5a51); + OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP(FG, A, B, C, D, 5, 5, 0xd62f105d); + OP(FG, D, A, B, C, 10, 9, 0x02441453); + OP(FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP(FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP(FG, D, A, B, C, 14, 9, 0xc33707d6); + OP(FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP(FG, B, C, D, A, 8, 20, 0x455a14ed); + OP(FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP(FG, C, D, A, B, 7, 14, 0x676f02d9); + OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a); +# endif /* MD5_SIZE_VS_SPEED == 1 */ + + /* Round 3. */ +# if MD5_SIZE_VS_SPEED == 1 + for (i = 0; i < 4; i++) { + OP(FH, A, B, C, D, (int) (*pp++), 4, *pc++); + OP(FH, D, A, B, C, (int) (*pp++), 11, *pc++); + OP(FH, C, D, A, B, (int) (*pp++), 16, *pc++); + OP(FH, B, C, D, A, (int) (*pp++), 23, *pc++); + } +# else + OP(FH, A, B, C, D, 5, 4, 0xfffa3942); + OP(FH, D, A, B, C, 8, 11, 0x8771f681); + OP(FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP(FH, B, C, D, A, 14, 23, 0xfde5380c); + OP(FH, A, B, C, D, 1, 4, 0xa4beea44); + OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP(FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP(FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP(FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP(FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP(FH, B, C, D, A, 6, 23, 0x04881d05); + OP(FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP(FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP(FH, B, C, D, A, 2, 23, 0xc4ac5665); +# endif /* MD5_SIZE_VS_SPEED == 1 */ + + /* Round 4. */ +# if MD5_SIZE_VS_SPEED == 1 + for (i = 0; i < 4; i++) { + OP(FI, A, B, C, D, (int) (*pp++), 6, *pc++); + OP(FI, D, A, B, C, (int) (*pp++), 10, *pc++); + OP(FI, C, D, A, B, (int) (*pp++), 15, *pc++); + OP(FI, B, C, D, A, (int) (*pp++), 21, *pc++); + } +# else + OP(FI, A, B, C, D, 0, 6, 0xf4292244); + OP(FI, D, A, B, C, 7, 10, 0x432aff97); + OP(FI, C, D, A, B, 14, 15, 0xab9423a7); + OP(FI, B, C, D, A, 5, 21, 0xfc93a039); + OP(FI, A, B, C, D, 12, 6, 0x655b59c3); + OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP(FI, C, D, A, B, 10, 15, 0xffeff47d); + OP(FI, B, C, D, A, 1, 21, 0x85845dd1); + OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP(FI, C, D, A, B, 6, 15, 0xa3014314); + OP(FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP(FI, A, B, C, D, 4, 6, 0xf7537e82); + OP(FI, D, A, B, C, 11, 10, 0xbd3af235); + OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP(FI, B, C, D, A, 9, 21, 0xeb86d391); +# endif /* MD5_SIZE_VS_SPEED == 1 */ +# endif /* MD5_SIZE_VS_SPEED > 1 */ + + /* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + + /* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} + +/* Feed data through a temporary buffer to call md5_hash_aligned_block() + * with chunks of data that are 4-byte aligned and a multiple of 64 bytes. + * This function's internal buffer remembers previous data until it has 64 + * bytes worth to pass on. Call md5_end() to flush this buffer. */ + +void FAST_FUNC md5_hash(const void *buffer, size_t len, md5_ctx_t *ctx) +{ + char *buf=(char *)buffer; + + /* RFC 1321 specifies the possible length of the file up to 2^64 bits, + * Here we only track the number of bytes. */ + + ctx->total += len; + + // Process all input. + + while (len) { + unsigned i = 64 - ctx->buflen; + + // Copy data into aligned buffer. + + if (i > len) i = len; + memcpy(ctx->buffer + ctx->buflen, buf, i); + len -= i; + ctx->buflen += i; + buf += i; + + // When buffer fills up, process it. + + if (ctx->buflen == 64) { + md5_hash_block(ctx->buffer, ctx); + ctx->buflen = 0; + } + } +} + +/* Process the remaining bytes in the buffer and put result from CTX + * in first 16 bytes following RESBUF. The result is always in little + * endian byte order, so that a byte-wise output yields to the wanted + * ASCII representation of the message digest. + * + * IMPORTANT: On some systems it is required that RESBUF is correctly + * aligned for a 32 bits value. + */ +void* FAST_FUNC md5_end(void *resbuf, md5_ctx_t *ctx) +{ + char *buf = ctx->buffer; + int i; + + /* Pad data to block size. */ + + buf[ctx->buflen++] = 0x80; + memset(buf + ctx->buflen, 0, 128 - ctx->buflen); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + ctx->total <<= 3; + if (ctx->buflen > 56) buf += 64; + for (i = 0; i < 8; i++) buf[56 + i] = ctx->total >> (i*8); + + /* Process last bytes. */ + if (buf != ctx->buffer) md5_hash_block(ctx->buffer, ctx); + md5_hash_block(buf, ctx); + + /* Put result from CTX in first 16 bytes following RESBUF. The result is + * always in little endian byte order, so that a byte-wise output yields + * to the wanted ASCII representation of the message digest. + * + * IMPORTANT: On some systems it is required that RESBUF is correctly + * aligned for a 32 bits value. + */ + ((uint32_t *) resbuf)[0] = SWAP_LE32(ctx->A); + ((uint32_t *) resbuf)[1] = SWAP_LE32(ctx->B); + ((uint32_t *) resbuf)[2] = SWAP_LE32(ctx->C); + ((uint32_t *) resbuf)[3] = SWAP_LE32(ctx->D); + + return resbuf; +} + diff --git a/libbb/messages.c b/libbb/messages.c new file mode 100644 index 0000000..9009028 --- /dev/null +++ b/libbb/messages.c @@ -0,0 +1,73 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* allow default system PATH to be extended via CFLAGS */ +#ifndef BB_ADDITIONAL_PATH +#define BB_ADDITIONAL_PATH "" +#endif + +/* allow version to be extended, via CFLAGS */ +#ifndef BB_EXTRA_VERSION +#define BB_EXTRA_VERSION BB_BT +#endif + +#define BANNER "BusyBox v" BB_VER " (" BB_EXTRA_VERSION ")" + +const char bb_banner[] ALIGN1 = BANNER; + + +const char bb_msg_memory_exhausted[] ALIGN1 = "memory exhausted"; +const char bb_msg_invalid_date[] ALIGN1 = "invalid date '%s'"; +const char bb_msg_write_error[] ALIGN1 = "write error"; +const char bb_msg_read_error[] ALIGN1 = "read error"; +const char bb_msg_unknown[] ALIGN1 = "(unknown)"; +const char bb_msg_can_not_create_raw_socket[] ALIGN1 = "can't create raw socket"; +const char bb_msg_perm_denied_are_you_root[] ALIGN1 = "permission denied. (are you root?)"; +const char bb_msg_requires_arg[] ALIGN1 = "%s requires an argument"; +const char bb_msg_invalid_arg[] ALIGN1 = "invalid argument '%s' to '%s'"; +const char bb_msg_standard_input[] ALIGN1 = "standard input"; +const char bb_msg_standard_output[] ALIGN1 = "standard output"; + +const char bb_str_default[] ALIGN1 = "default"; +const char bb_hexdigits_upcase[] ALIGN1 = "0123456789ABCDEF"; + +const char bb_path_passwd_file[] ALIGN1 = "/etc/passwd"; +const char bb_path_shadow_file[] ALIGN1 = "/etc/shadow"; +const char bb_path_group_file[] ALIGN1 = "/etc/group"; +const char bb_path_gshadow_file[] ALIGN1 = "/etc/gshadow"; +const char bb_path_motd_file[] ALIGN1 = "/etc/motd"; +const char bb_dev_null[] ALIGN1 = "/dev/null"; +const char bb_busybox_exec_path[] ALIGN1 = CONFIG_BUSYBOX_EXEC_PATH; +const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL; +/* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin, + * but I want to save a few bytes here. Check libbb.h before changing! */ +const char bb_PATH_root_path[] ALIGN1 = + "PATH=/sbin:/usr/sbin:/bin:/usr/bin" BB_ADDITIONAL_PATH; + + +const int const_int_1 = 1; +/* explicitly = 0, otherwise gcc may make it a common variable + * and it will end up in bss */ +const int const_int_0 = 0; + +#include +/* This is usually something like "/var/adm/wtmp" or "/var/log/wtmp" */ +const char bb_path_wtmp_file[] ALIGN1 = +#if defined _PATH_WTMP + _PATH_WTMP; +#elif defined WTMP_FILE + WTMP_FILE; +#else +#error unknown path to wtmp file +#endif + +/* We use it for "global" data via *(struct global*)&bb_common_bufsiz1. + * Since gcc insists on aligning struct global's members, it would be a pity + * (and an alignment fault on some CPUs) to mess it up. */ +char bb_common_bufsiz1[COMMON_BUFSIZE] ALIGNED(sizeof(long long)); diff --git a/libbb/mode_string.c b/libbb/mode_string.c new file mode 100644 index 0000000..7d4e514 --- /dev/null +++ b/libbb/mode_string.c @@ -0,0 +1,128 @@ +/* vi: set sw=4 ts=4: */ +/* + * mode_string implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* Aug 13, 2003 + * Fix a bug reported by junkio@cox.net involving the mode_chars index. + */ + + +#include +#include + +#include "libbb.h" + +#if ( S_ISUID != 04000 ) || ( S_ISGID != 02000 ) || ( S_ISVTX != 01000 ) \ + || ( S_IRUSR != 00400 ) || ( S_IWUSR != 00200 ) || ( S_IXUSR != 00100 ) \ + || ( S_IRGRP != 00040 ) || ( S_IWGRP != 00020 ) || ( S_IXGRP != 00010 ) \ + || ( S_IROTH != 00004 ) || ( S_IWOTH != 00002 ) || ( S_IXOTH != 00001 ) +#error permission bitflag value assumption(s) violated! +#endif + +#if ( S_IFSOCK!= 0140000 ) || ( S_IFLNK != 0120000 ) \ + || ( S_IFREG != 0100000 ) || ( S_IFBLK != 0060000 ) \ + || ( S_IFDIR != 0040000 ) || ( S_IFCHR != 0020000 ) \ + || ( S_IFIFO != 0010000 ) +#warning mode type bitflag value assumption(s) violated! falling back to larger version + +#if (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX) == 07777 +#undef mode_t +#define mode_t unsigned short +#endif + +static const mode_t mode_flags[] = { + S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID, + S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID, + S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX +}; + +/* The static const char arrays below are duplicated for the two cases + * because moving them ahead of the mode_flags declaration cause a text + * size increase with the gcc version I'm using. */ + +/* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C', + * and 'B' types don't appear to be available on linux. So I removed them. */ +static const char type_chars[16] ALIGN1 = "?pc?d?b?-?l?s???"; +/***************************************** 0123456789abcdef */ +static const char mode_chars[7] ALIGN1 = "rwxSTst"; + +const char* FAST_FUNC bb_mode_string(mode_t mode) +{ + static char buf[12]; + char *p = buf; + + int i, j, k; + + *p = type_chars[ (mode >> 12) & 0xf ]; + i = 0; + do { + j = k = 0; + do { + *++p = '-'; + if (mode & mode_flags[i+j]) { + *p = mode_chars[j]; + k = j; + } + } while (++j < 3); + if (mode & mode_flags[i+j]) { + *p = mode_chars[3 + (k & 2) + ((i&8) >> 3)]; + } + i += 4; + } while (i < 12); + + /* Note: We don't bother with nul termination because bss initialization + * should have taken care of that for us. If the user scribbled in buf + * memory, they deserve whatever happens. But we'll at least assert. */ + assert(buf[10] == 0); + + return buf; +} + +#else + +/* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C', + * and 'B' types don't appear to be available on linux. So I removed them. */ +static const char type_chars[16] = "?pc?d?b?-?l?s???"; +/********************************** 0123456789abcdef */ +static const char mode_chars[7] = "rwxSTst"; + +const char* FAST_FUNC bb_mode_string(mode_t mode) +{ + static char buf[12]; + char *p = buf; + + int i, j, k, m; + + *p = type_chars[ (mode >> 12) & 0xf ]; + i = 0; + m = 0400; + do { + j = k = 0; + do { + *++p = '-'; + if (mode & m) { + *p = mode_chars[j]; + k = j; + } + m >>= 1; + } while (++j < 3); + ++i; + if (mode & (010000 >> i)) { + *p = mode_chars[3 + (k & 2) + (i == 3)]; + } + } while (i < 3); + + /* Note: We don't bother with nul termination because bss initialization + * should have taken care of that for us. If the user scribbled in buf + * memory, they deserve whatever happens. But we'll at least assert. */ + assert(buf[10] == 0); + + return buf; +} + +#endif diff --git a/libbb/mtab.c b/libbb/mtab.c new file mode 100644 index 0000000..586a661 --- /dev/null +++ b/libbb/mtab.c @@ -0,0 +1,55 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include +#include "libbb.h" + +#if ENABLE_FEATURE_MTAB_SUPPORT +void FAST_FUNC erase_mtab(const char *name) +{ + struct mntent *entries; + int i, count; + FILE *mountTable; + struct mntent *m; + + mountTable = setmntent(bb_path_mtab_file, "r"); + /* Bummer. Fall back on trying the /proc filesystem */ + if (!mountTable) mountTable = setmntent("/proc/mounts", "r"); + if (!mountTable) { + bb_perror_msg(bb_path_mtab_file); + return; + } + + entries = NULL; + count = 0; + while ((m = getmntent(mountTable)) != 0) { + entries = xrealloc_vector(entries, 3, count); + entries[count].mnt_fsname = xstrdup(m->mnt_fsname); + entries[count].mnt_dir = xstrdup(m->mnt_dir); + entries[count].mnt_type = xstrdup(m->mnt_type); + entries[count].mnt_opts = xstrdup(m->mnt_opts); + entries[count].mnt_freq = m->mnt_freq; + entries[count].mnt_passno = m->mnt_passno; + count++; + } + endmntent(mountTable); + +//TODO: make update atomic + mountTable = setmntent(bb_path_mtab_file, "w"); + if (mountTable) { + for (i = 0; i < count; i++) { + if (strcmp(entries[i].mnt_fsname, name) != 0 + && strcmp(entries[i].mnt_dir, name) != 0) + addmntent(mountTable, &entries[i]); + } + endmntent(mountTable); + } else if (errno != EROFS) + bb_perror_msg(bb_path_mtab_file); +} +#endif diff --git a/libbb/mtab_file.c b/libbb/mtab_file.c new file mode 100644 index 0000000..030b148 --- /dev/null +++ b/libbb/mtab_file.c @@ -0,0 +1,15 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* Busybox mount uses either /proc/mounts or /etc/mtab to + * get the list of currently mounted filesystems */ +const char bb_path_mtab_file[] ALIGN1 = +USE_FEATURE_MTAB_SUPPORT("/etc/mtab")SKIP_FEATURE_MTAB_SUPPORT("/proc/mounts"); diff --git a/libbb/obscure.c b/libbb/obscure.c new file mode 100644 index 0000000..19b8752 --- /dev/null +++ b/libbb/obscure.c @@ -0,0 +1,170 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini weak password checker implementation for busybox + * + * Copyright (C) 2006 Tito Ragusa + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* A good password: + 1) should contain at least six characters (man passwd); + 2) empty passwords are not permitted; + 3) should contain a mix of four different types of characters + upper case letters, + lower case letters, + numbers, + special characters such as !@#$%^&*,;". + This password types should not be permitted: + a) pure numbers: birthdates, social security number, license plate, phone numbers; + b) words and all letters only passwords (uppercase, lowercase or mixed) + as palindromes, consecutive or repetitive letters + or adjacent letters on your keyboard; + c) username, real name, company name or (e-mail?) address + in any form (as-is, reversed, capitalized, doubled, etc.). + (we can check only against username, gecos and hostname) + d) common and obvious letter-number replacements + (e.g. replace the letter O with number 0) + such as "M1cr0$0ft" or "P@ssw0rd" (CAVEAT: we cannot check for them + without the use of a dictionary). + + For each missing type of characters an increase of password length is + requested. + + If user is root we warn only. + + CAVEAT: some older versions of crypt() truncates passwords to 8 chars, + so that aaaaaaaa1Q$ is equal to aaaaaaaa making it possible to fool + some of our checks. We don't test for this special case as newer versions + of crypt do not truncate passwords. +*/ + +#include "libbb.h" + +static int string_checker_helper(const char *p1, const char *p2) __attribute__ ((__pure__)); + +static int string_checker_helper(const char *p1, const char *p2) +{ + /* as-is or capitalized */ + if (strcasecmp(p1, p2) == 0 + /* as sub-string */ + || strcasestr(p2, p1) != NULL + /* invert in case haystack is shorter than needle */ + || strcasestr(p1, p2) != NULL) + return 1; + return 0; +} + +static int string_checker(const char *p1, const char *p2) +{ + int size; + /* check string */ + int ret = string_checker_helper(p1, p2); + /* Make our own copy */ + char *p = xstrdup(p1); + /* reverse string */ + size = strlen(p); + + while (size--) { + *p = p1[size]; + p++; + } + /* restore pointer */ + p -= strlen(p1); + /* check reversed string */ + ret |= string_checker_helper(p, p2); + /* clean up */ + memset(p, 0, strlen(p1)); + free(p); + return ret; +} + +#define LOWERCASE 1 +#define UPPERCASE 2 +#define NUMBERS 4 +#define SPECIAL 8 + +static const char *obscure_msg(const char *old_p, const char *new_p, const struct passwd *pw) +{ + int i; + int c; + int length; + int mixed = 0; + /* Add 2 for each type of characters to the minlen of password */ + int size = CONFIG_PASSWORD_MINLEN + 8; + const char *p; + char *hostname; + + /* size */ + if (!new_p || (length = strlen(new_p)) < CONFIG_PASSWORD_MINLEN) + return "too short"; + + /* no username as-is, as sub-string, reversed, capitalized, doubled */ + if (string_checker(new_p, pw->pw_name)) { + return "similar to username"; + } + /* no gecos as-is, as sub-string, reversed, capitalized, doubled */ + if (*pw->pw_gecos && string_checker(new_p, pw->pw_gecos)) { + return "similar to gecos"; + } + /* hostname as-is, as sub-string, reversed, capitalized, doubled */ + hostname = safe_gethostname(); + i = string_checker(new_p, hostname); + free(hostname); + if (i) + return "similar to hostname"; + + /* Should / Must contain a mix of: */ + for (i = 0; i < length; i++) { + if (islower(new_p[i])) { /* a-z */ + mixed |= LOWERCASE; + } else if (isupper(new_p[i])) { /* A-Z */ + mixed |= UPPERCASE; + } else if (isdigit(new_p[i])) { /* 0-9 */ + mixed |= NUMBERS; + } else { /* special characters */ + mixed |= SPECIAL; + } + /* More than 50% similar characters ? */ + c = 0; + p = new_p; + while (1) { + p = strchr(p, new_p[i]); + if (p == NULL) { + break; + } + c++; + if (!++p) { + break; /* move past the matched char if possible */ + } + } + + if (c >= (length / 2)) { + return "too many similar characters"; + } + } + for (i=0; i<4; i++) + if (mixed & (1< + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +#if defined ENABLE_PARSE && ENABLE_PARSE +int parse_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int parse_main(int argc UNUSED_PARAM, char **argv) +{ + const char *delims = "# \t"; + unsigned flags = PARSE_NORMAL; + int mintokens = 0, ntokens = 128; + + opt_complementary = "-1:n+:m+:f+"; + getopt32(argv, "n:m:d:f:", &ntokens, &mintokens, &delims, &flags); + //argc -= optind; + argv += optind; + while (*argv) { + parser_t *p = config_open(*argv); + if (p) { + int n; + char **t = xmalloc(sizeof(char *) * ntokens); + while ((n = config_read(p, t, ntokens, mintokens, delims, flags)) != 0) { + for (int i = 0; i < n; ++i) + printf("[%s]", t[i]); + puts(""); + } + config_close(p); + } + argv++; + } + return EXIT_SUCCESS; +} +#endif + +/* + +Typical usage: + +----- CUT ----- + char *t[3]; // tokens placeholder + parser_t *p = config_open(filename); + if (p) { + // parse line-by-line + while (config_read(p, t, 3, 0, delimiters, flags)) { // 1..3 tokens + // use tokens + bb_error_msg("TOKENS: [%s][%s][%s]", t[0], t[1], t[2]); + } + ... + // free parser + config_close(p); + } +----- CUT ----- + +*/ + +parser_t* FAST_FUNC config_open2(const char *filename, FILE* FAST_FUNC (*fopen_func)(const char *path)) +{ + FILE* fp; + parser_t *parser; + + fp = fopen_func(filename); + if (!fp) + return NULL; + parser = xzalloc(sizeof(*parser)); + parser->fp = fp; + return parser; +} + +parser_t* FAST_FUNC config_open(const char *filename) +{ + return config_open2(filename, fopen_or_warn_stdin); +} + +static void config_free_data(parser_t *const parser) +{ + free(parser->line); + parser->line = NULL; + if (PARSE_KEEP_COPY) { /* compile-time constant */ + free(parser->data); + parser->data = NULL; + } +} + +void FAST_FUNC config_close(parser_t *parser) +{ + if (parser) { + config_free_data(parser); + fclose(parser->fp); + free(parser); + } +} + +/* +0. If parser is NULL return 0. +1. Read a line from config file. If nothing to read then return 0. + Handle continuation character. Advance lineno for each physical line. + Discard everything past comment characher. +2. if PARSE_TRIM is set (default), remove leading and trailing delimiters. +3. If resulting line is empty goto 1. +4. Look for first delimiter. If !PARSE_COLLAPSE or !PARSE_TRIM is set then + remember the token as empty. +5. Else (default) if number of seen tokens is equal to max number of tokens + (token is the last one) and PARSE_GREEDY is set then the remainder + of the line is the last token. + Else (token is not last or PARSE_GREEDY is not set) just replace + first delimiter with '\0' thus delimiting the token. +6. Advance line pointer past the end of token. If number of seen tokens + is less than required number of tokens then goto 4. +7. Check the number of seen tokens is not less the min number of tokens. + Complain or die otherwise depending on PARSE_MIN_DIE. +8. Return the number of seen tokens. + +mintokens > 0 make config_read() print error message if less than mintokens +(but more than 0) are found. Empty lines are always skipped (not warned about). +*/ +#undef config_read +int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const char *delims) +{ + char *line; + int ntokens, mintokens; + int t, len; + + ntokens = flags & 0xFF; + mintokens = (flags & 0xFF00) >> 8; + + if (parser == NULL) + return 0; + +again: + memset(tokens, 0, sizeof(tokens[0]) * ntokens); + config_free_data(parser); + + /* Read one line (handling continuations with backslash) */ + line = bb_get_chunk_with_continuation(parser->fp, &len, &parser->lineno); + if (line == NULL) + return 0; + parser->line = line; + + /* Strip trailing line-feed if any */ + if (len && line[len-1] == '\n') + line[len-1] = '\0'; + + /* Skip token in the start of line? */ + if (flags & PARSE_TRIM) + line += strspn(line, delims + 1); + + if (line[0] == '\0' || line[0] == delims[0]) + goto again; + + if (flags & PARSE_KEEP_COPY) + parser->data = xstrdup(line); + + /* Tokenize the line */ + for (t = 0; *line && *line != delims[0] && t < ntokens; t++) { + /* Pin token */ + tokens[t] = line; + + /* Combine remaining arguments? */ + if ((t != (ntokens-1)) || !(flags & PARSE_GREEDY)) { + /* Vanilla token, find next delimiter */ + line += strcspn(line, delims[0] ? delims : delims + 1); + } else { + /* Combining, find comment char if any */ + line = strchrnul(line, delims[0]); + + /* Trim any extra delimiters from the end */ + if (flags & PARSE_TRIM) { + while (strchr(delims + 1, line[-1]) != NULL) + line--; + } + } + + /* Token not terminated? */ + if (line[0] == delims[0]) + *line = '\0'; + else if (line[0] != '\0') + *(line++) = '\0'; + +#if 0 /* unused so far */ + if (flags & PARSE_ESCAPE) { + const char *from; + char *to; + + from = to = tokens[t]; + while (*from) { + if (*from == '\\') { + from++; + *to++ = bb_process_escape_sequence(&from); + } else { + *to++ = *from++; + } + } + *to = '\0'; + } +#endif + + /* Skip possible delimiters */ + if (flags & PARSE_COLLAPSE) + line += strspn(line, delims + 1); + } + + if (t < mintokens) { + bb_error_msg("bad line %u: %d tokens found, %d needed", + parser->lineno, t, mintokens); + if (flags & PARSE_MIN_DIE) + xfunc_die(); + goto again; + } + + return t; +} diff --git a/libbb/parse_mode.c b/libbb/parse_mode.c new file mode 100644 index 0000000..40105dd --- /dev/null +++ b/libbb/parse_mode.c @@ -0,0 +1,150 @@ +/* vi: set sw=4 ts=4: */ +/* + * parse_mode implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */ + +#include "libbb.h" + +/* This function is used from NOFORK applets. It must not allocate anything */ + +#define FILEMODEBITS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) + +int FAST_FUNC bb_parse_mode(const char *s, mode_t *current_mode) +{ + static const mode_t who_mask[] = { + S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */ + S_ISUID | S_IRWXU, /* u */ + S_ISGID | S_IRWXG, /* g */ + S_IRWXO /* o */ + }; + static const mode_t perm_mask[] = { + S_IRUSR | S_IRGRP | S_IROTH, /* r */ + S_IWUSR | S_IWGRP | S_IWOTH, /* w */ + S_IXUSR | S_IXGRP | S_IXOTH, /* x */ + S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */ + S_ISUID | S_ISGID, /* s */ + S_ISVTX /* t */ + }; + static const char who_chars[] ALIGN1 = "augo"; + static const char perm_chars[] ALIGN1 = "rwxXst"; + + const char *p; + mode_t wholist; + mode_t permlist; + mode_t new_mode; + char op; + + if (((unsigned int)(*s - '0')) < 8) { + unsigned long tmp; + char *e; + + tmp = strtoul(s, &e, 8); + if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */ + return 0; + } + *current_mode = tmp; + return 1; + } + + new_mode = *current_mode; + + /* Note: we allow empty clauses, and hence empty modes. + * We treat an empty mode as no change to perms. */ + + while (*s) { /* Process clauses. */ + if (*s == ',') { /* We allow empty clauses. */ + ++s; + continue; + } + + /* Get a wholist. */ + wholist = 0; + WHO_LIST: + p = who_chars; + do { + if (*p == *s) { + wholist |= who_mask[(int)(p-who_chars)]; + if (!*++s) { + return 0; + } + goto WHO_LIST; + } + } while (*++p); + + do { /* Process action list. */ + if ((*s != '+') && (*s != '-')) { + if (*s != '=') { + return 0; + } + /* Since op is '=', clear all bits corresponding to the + * wholist, or all file bits if wholist is empty. */ + permlist = ~FILEMODEBITS; + if (wholist) { + permlist = ~wholist; + } + new_mode &= permlist; + } + op = *s++; + + /* Check for permcopy. */ + p = who_chars + 1; /* Skip 'a' entry. */ + do { + if (*p == *s) { + int i = 0; + permlist = who_mask[(int)(p-who_chars)] + & (S_IRWXU | S_IRWXG | S_IRWXO) + & new_mode; + do { + if (permlist & perm_mask[i]) { + permlist |= perm_mask[i]; + } + } while (++i < 3); + ++s; + goto GOT_ACTION; + } + } while (*++p); + + /* It was not a permcopy, so get a permlist. */ + permlist = 0; + PERM_LIST: + p = perm_chars; + do { + if (*p == *s) { + if ((*p != 'X') + || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH)) + ) { + permlist |= perm_mask[(int)(p-perm_chars)]; + } + if (!*++s) { + break; + } + goto PERM_LIST; + } + } while (*++p); + GOT_ACTION: + if (permlist) { /* The permlist was nonempty. */ + mode_t tmp = wholist; + if (!wholist) { + mode_t u_mask = umask(0); + umask(u_mask); + tmp = ~u_mask; + } + permlist &= tmp; + if (op == '-') { + new_mode &= ~permlist; + } else { + new_mode |= permlist; + } + } + } while (*s && (*s != ',')); + } + + *current_mode = new_mode; + return 1; +} diff --git a/libbb/perror_msg.c b/libbb/perror_msg.c new file mode 100644 index 0000000..6c8e1b5 --- /dev/null +++ b/libbb/perror_msg.c @@ -0,0 +1,25 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +void FAST_FUNC bb_perror_msg(const char *s, ...) +{ + va_list p; + + va_start(p, s); + /* Guard against ": Success" */ + bb_verror_msg(s, p, errno ? strerror(errno) : NULL); + va_end(p); +} + +void FAST_FUNC bb_simple_perror_msg(const char *s) +{ + bb_perror_msg("%s", s); +} diff --git a/libbb/perror_msg_and_die.c b/libbb/perror_msg_and_die.c new file mode 100644 index 0000000..15615fa --- /dev/null +++ b/libbb/perror_msg_and_die.c @@ -0,0 +1,26 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +void FAST_FUNC bb_perror_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + /* Guard against ": Success" */ + bb_verror_msg(s, p, errno ? strerror(errno) : NULL); + va_end(p); + xfunc_die(); +} + +void FAST_FUNC bb_simple_perror_msg_and_die(const char *s) +{ + bb_perror_msg_and_die("%s", s); +} diff --git a/libbb/perror_nomsg.c b/libbb/perror_nomsg.c new file mode 100644 index 0000000..a157caa --- /dev/null +++ b/libbb/perror_nomsg.c @@ -0,0 +1,22 @@ +/* vi: set sw=4 ts=4: */ +/* + * bb_perror_nomsg implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* gcc warns about a null format string, therefore we provide + * modified definition without "attribute (format)" + * instead of including libbb.h */ +//#include "libbb.h" +#include "platform.h" +extern void bb_perror_msg(const char *s, ...) FAST_FUNC; + +/* suppress gcc "no previous prototype" warning */ +void FAST_FUNC bb_perror_nomsg(void); +void FAST_FUNC bb_perror_nomsg(void) +{ + bb_perror_msg(0); +} diff --git a/libbb/perror_nomsg_and_die.c b/libbb/perror_nomsg_and_die.c new file mode 100644 index 0000000..d56e05d --- /dev/null +++ b/libbb/perror_nomsg_and_die.c @@ -0,0 +1,22 @@ +/* vi: set sw=4 ts=4: */ +/* + * bb_perror_nomsg_and_die implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* gcc warns about a null format string, therefore we provide + * modified definition without "attribute (format)" + * instead of including libbb.h */ +//#include "libbb.h" +#include "platform.h" +extern void bb_perror_msg_and_die(const char *s, ...) FAST_FUNC; + +/* suppress gcc "no previous prototype" warning */ +void FAST_FUNC bb_perror_nomsg_and_die(void); +void FAST_FUNC bb_perror_nomsg_and_die(void) +{ + bb_perror_msg_and_die(0); +} diff --git a/libbb/pidfile.c b/libbb/pidfile.c new file mode 100644 index 0000000..7b8fee2 --- /dev/null +++ b/libbb/pidfile.c @@ -0,0 +1,40 @@ +/* vi: set sw=4 ts=4: */ +/* + * pid file routines + * + * Copyright (C) 2007 by Stephane Billiart + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* Override ENABLE_FEATURE_PIDFILE */ +#define WANT_PIDFILE 1 +#include "libbb.h" + +smallint wrote_pidfile; + +void FAST_FUNC write_pidfile(const char *path) +{ + int pid_fd; + char *end; + char buf[sizeof(int)*3 + 2]; + struct stat sb; + + if (!path) + return; + /* we will overwrite stale pidfile */ + pid_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (pid_fd < 0) + return; + + /* path can be "/dev/null"! Test for such cases */ + wrote_pidfile = (fstat(pid_fd, &sb) == 0) && S_ISREG(sb.st_mode); + + if (wrote_pidfile) { + /* few bytes larger, but doesn't use stdio */ + end = utoa_to_buf(getpid(), buf, sizeof(buf)); + *end = '\n'; + full_write(pid_fd, buf, end - buf + 1); + } + close(pid_fd); +} diff --git a/libbb/print_flags.c b/libbb/print_flags.c new file mode 100644 index 0000000..afa7550 --- /dev/null +++ b/libbb/print_flags.c @@ -0,0 +1,32 @@ +/* vi: set sw=4 ts=4: */ +/* Print string that matches bit masked flags + * + * Copyright (C) 2008 Natanael Copa + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include + +/* returns a set with the flags not printed */ +int FAST_FUNC print_flags_separated(const int *masks, const char *labels, int flags, const char *separator) +{ + const char *need_separator = NULL; + while (*labels) { + if (flags & *masks) { + printf("%s%s", + need_separator ? need_separator : "", + labels); + need_separator = separator; + flags &= ~ *masks; + } + masks++; + labels += strlen(labels) + 1; + } + return flags; +} + +int FAST_FUNC print_flags(const masks_labels_t *ml, int flags) +{ + return print_flags_separated(ml->masks, ml->labels, flags, NULL); +} diff --git a/libbb/printable.c b/libbb/printable.c new file mode 100644 index 0000000..ae93359 --- /dev/null +++ b/libbb/printable.c @@ -0,0 +1,34 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2007 Denys Vlasenko + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +void FAST_FUNC fputc_printable(int ch, FILE *file) +{ + if ((ch & (0x80 + PRINTABLE_META)) == (0x80 + PRINTABLE_META)) { + fputs("M-", file); + ch &= 0x7f; + } + ch = (unsigned char) ch; + if (ch == 0x9b) { + /* VT100's CSI, aka Meta-ESC, is not printable on vt-100 */ + ch = '{'; + goto print_caret; + } + if (ch < ' ') { + ch += '@'; + goto print_caret; + } + if (ch == 0x7f) { + ch = '?'; + print_caret: + fputc('^', file); + } + fputc(ch, file); +} diff --git a/libbb/process_escape_sequence.c b/libbb/process_escape_sequence.c new file mode 100644 index 0000000..6de2cac --- /dev/null +++ b/libbb/process_escape_sequence.c @@ -0,0 +1,89 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) Manuel Novoa III + * and Vladimir Oleynik + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +#define WANT_HEX_ESCAPES 1 + +/* Usual "this only works for ascii compatible encodings" disclaimer. */ +#undef _tolower +#define _tolower(X) ((X)|((char) 0x20)) + +char FAST_FUNC bb_process_escape_sequence(const char **ptr) +{ + /* bash builtin "echo -e '\ec'" interprets \e as ESC, + * but coreutils "/bin/echo -e '\ec'" does not. + * manpages tend to support coreutils way. */ + static const char charmap[] ALIGN1 = { + 'a', 'b', /*'e',*/ 'f', 'n', 'r', 't', 'v', '\\', 0, + '\a', '\b', /*27,*/ '\f', '\n', '\r', '\t', '\v', '\\', '\\' }; + + const char *p; + const char *q; + unsigned num_digits; + unsigned r; + unsigned n; + unsigned d; + unsigned base; + + num_digits = n = 0; + base = 8; + q = *ptr; + +#ifdef WANT_HEX_ESCAPES + if (*q == 'x') { + ++q; + base = 16; + ++num_digits; + } +#endif + + do { + d = (unsigned char)(*q) - '0'; +#ifdef WANT_HEX_ESCAPES + if (d >= 10) { + d = (unsigned char)(_tolower(*q)) - 'a' + 10; + } +#endif + + if (d >= base) { +#ifdef WANT_HEX_ESCAPES + if ((base == 16) && (!--num_digits)) { +/* return '\\'; */ + --q; + } +#endif + break; + } + + r = n * base + d; + if (r > UCHAR_MAX) { + break; + } + + n = r; + ++q; + } while (++num_digits < 3); + + if (num_digits == 0) { /* mnemonic escape sequence? */ + p = charmap; + do { + if (*p == *q) { + q++; + break; + } + } while (*++p); + n = *(p + (sizeof(charmap)/2)); + } + + *ptr = q; + + return (char) n; +} diff --git a/libbb/procps.c b/libbb/procps.c new file mode 100644 index 0000000..4d9a95b --- /dev/null +++ b/libbb/procps.c @@ -0,0 +1,476 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright 1998 by Albert Cahalan; all rights reserved. + * Copyright (C) 2002 by Vladimir Oleynik + * SELinux support: (c) 2007 by Yuichi Nakamura + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + + +typedef struct unsigned_to_name_map_t { + unsigned id; + char name[USERNAME_MAX_SIZE]; +} unsigned_to_name_map_t; + +typedef struct cache_t { + unsigned_to_name_map_t *cache; + int size; +} cache_t; + +static cache_t username, groupname; + +static void clear_cache(cache_t *cp) +{ + free(cp->cache); + cp->cache = NULL; + cp->size = 0; +} +void FAST_FUNC clear_username_cache(void) +{ + clear_cache(&username); + clear_cache(&groupname); +} + +#if 0 /* more generic, but we don't need that yet */ +/* Returns -N-1 if not found. */ +/* cp->cache[N] is allocated and must be filled in this case */ +static int get_cached(cache_t *cp, unsigned id) +{ + int i; + for (i = 0; i < cp->size; i++) + if (cp->cache[i].id == id) + return i; + i = cp->size++; + cp->cache = xrealloc_vector(cp->cache, 2, i); + cp->cache[i++].id = id; + return -i; +} +#endif + +typedef char* FAST_FUNC ug_func(char *name, int bufsize, long uid); +static char* get_cached(cache_t *cp, unsigned id, ug_func* fp) +{ + int i; + for (i = 0; i < cp->size; i++) + if (cp->cache[i].id == id) + return cp->cache[i].name; + i = cp->size++; + cp->cache = xrealloc_vector(cp->cache, 2, i); + cp->cache[i].id = id; + /* Never fails. Generates numeric string if name isn't found */ + fp(cp->cache[i].name, sizeof(cp->cache[i].name), id); + return cp->cache[i].name; +} +const char* FAST_FUNC get_cached_username(uid_t uid) +{ + return get_cached(&username, uid, bb_getpwuid); +} +const char* FAST_FUNC get_cached_groupname(gid_t gid) +{ + return get_cached(&groupname, gid, bb_getgrgid); +} + + +#define PROCPS_BUFSIZE 1024 + +static int read_to_buf(const char *filename, void *buf) +{ + int fd; + /* open_read_close() would do two reads, checking for EOF. + * When you have 10000 /proc/$NUM/stat to read, it isn't desirable */ + ssize_t ret = -1; + fd = open(filename, O_RDONLY); + if (fd >= 0) { + ret = read(fd, buf, PROCPS_BUFSIZE-1); + close(fd); + } + ((char *)buf)[ret > 0 ? ret : 0] = '\0'; + return ret; +} + +static procps_status_t* FAST_FUNC alloc_procps_scan(void) +{ + unsigned n = getpagesize(); + procps_status_t* sp = xzalloc(sizeof(procps_status_t)); + sp->dir = xopendir("/proc"); + while (1) { + n >>= 1; + if (!n) break; + sp->shift_pages_to_bytes++; + } + sp->shift_pages_to_kb = sp->shift_pages_to_bytes - 10; + return sp; +} + +void FAST_FUNC free_procps_scan(procps_status_t* sp) +{ + closedir(sp->dir); + free(sp->argv0); + USE_SELINUX(free(sp->context);) + free(sp); +} + +#if ENABLE_FEATURE_TOPMEM +static unsigned long fast_strtoul_16(char **endptr) +{ + unsigned char c; + char *str = *endptr; + unsigned long n = 0; + + while ((c = *str++) != ' ') { + c = ((c|0x20) - '0'); + if (c > 9) + // c = c + '0' - 'a' + 10: + c = c - ('a' - '0' - 10); + n = n*16 + c; + } + *endptr = str; /* We skip trailing space! */ + return n; +} +/* TOPMEM uses fast_strtoul_10, so... */ +#undef ENABLE_FEATURE_FAST_TOP +#define ENABLE_FEATURE_FAST_TOP 1 +#endif + +#if ENABLE_FEATURE_FAST_TOP +/* We cut a lot of corners here for speed */ +static unsigned long fast_strtoul_10(char **endptr) +{ + char c; + char *str = *endptr; + unsigned long n = *str - '0'; + + while ((c = *++str) != ' ') + n = n*10 + (c - '0'); + + *endptr = str + 1; /* We skip trailing space! */ + return n; +} +static char *skip_fields(char *str, int count) +{ + do { + while (*str++ != ' ') + continue; + /* we found a space char, str points after it */ + } while (--count); + return str; +} +#endif + +void BUG_comm_size(void); +procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) +{ + struct dirent *entry; + char buf[PROCPS_BUFSIZE]; + char filename[sizeof("/proc//cmdline") + sizeof(int)*3]; + char *filename_tail; + long tasknice; + unsigned pid; + int n; + struct stat sb; + + if (!sp) + sp = alloc_procps_scan(); + + for (;;) { + entry = readdir(sp->dir); + if (entry == NULL) { + free_procps_scan(sp); + return NULL; + } + pid = bb_strtou(entry->d_name, NULL, 10); + if (errno) + continue; + + /* After this point we have to break, not continue + * ("continue" would mean that current /proc/NNN + * is not a valid process info) */ + + memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz)); + + sp->pid = pid; + if (!(flags & ~PSSCAN_PID)) break; + +#if ENABLE_SELINUX + if (flags & PSSCAN_CONTEXT) { + if (getpidcon(sp->pid, &sp->context) < 0) + sp->context = NULL; + } +#endif + + filename_tail = filename + sprintf(filename, "/proc/%d", pid); + + if (flags & PSSCAN_UIDGID) { + if (stat(filename, &sb)) + break; + /* Need comment - is this effective or real UID/GID? */ + sp->uid = sb.st_uid; + sp->gid = sb.st_gid; + } + + if (flags & PSSCAN_STAT) { + char *cp, *comm1; + int tty; +#if !ENABLE_FEATURE_FAST_TOP + unsigned long vsz, rss; +#endif + /* see proc(5) for some details on this */ + strcpy(filename_tail, "/stat"); + n = read_to_buf(filename, buf); + if (n < 0) + break; + cp = strrchr(buf, ')'); /* split into "PID (cmd" and "" */ + /*if (!cp || cp[1] != ' ') + break;*/ + cp[0] = '\0'; + if (sizeof(sp->comm) < 16) + BUG_comm_size(); + comm1 = strchr(buf, '('); + /*if (comm1)*/ + safe_strncpy(sp->comm, comm1 + 1, sizeof(sp->comm)); + +#if !ENABLE_FEATURE_FAST_TOP + n = sscanf(cp+2, + "%c %u " /* state, ppid */ + "%u %u %d %*s " /* pgid, sid, tty, tpgid */ + "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */ + "%lu %lu " /* utime, stime */ + "%*s %*s %*s " /* cutime, cstime, priority */ + "%ld " /* nice */ + "%*s %*s " /* timeout, it_real_value */ + "%lu " /* start_time */ + "%lu " /* vsize */ + "%lu " /* rss */ +#if ENABLE_FEATURE_TOP_SMP_PROCESS + "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ + "%*s %*s %*s %*s " /*signal, blocked, sigignore, sigcatch */ + "%*s %*s %*s %*s " /*wchan, nswap, cnswap, exit_signal */ + "%d" /*cpu last seen on*/ +#endif + , + sp->state, &sp->ppid, + &sp->pgid, &sp->sid, &tty, + &sp->utime, &sp->stime, + &tasknice, + &sp->start_time, + &vsz, + &rss +#if ENABLE_FEATURE_TOP_SMP_PROCESS + , &sp->last_seen_on_cpu +#endif + ); + + if (n < 11) + break; +#if ENABLE_FEATURE_TOP_SMP_PROCESS + if (n < 11+15) + sp->last_seen_on_cpu = 0; +#endif + + /* vsz is in bytes and we want kb */ + sp->vsz = vsz >> 10; + /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ + sp->rss = rss << sp->shift_pages_to_kb; + sp->tty_major = (tty >> 8) & 0xfff; + sp->tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00); +#else +/* This costs ~100 bytes more but makes top faster by 20% + * If you run 10000 processes, this may be important for you */ + sp->state[0] = cp[2]; + cp += 4; + sp->ppid = fast_strtoul_10(&cp); + sp->pgid = fast_strtoul_10(&cp); + sp->sid = fast_strtoul_10(&cp); + tty = fast_strtoul_10(&cp); + sp->tty_major = (tty >> 8) & 0xfff; + sp->tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00); + cp = skip_fields(cp, 6); /* tpgid, flags, min_flt, cmin_flt, maj_flt, cmaj_flt */ + sp->utime = fast_strtoul_10(&cp); + sp->stime = fast_strtoul_10(&cp); + cp = skip_fields(cp, 3); /* cutime, cstime, priority */ + tasknice = fast_strtoul_10(&cp); + cp = skip_fields(cp, 2); /* timeout, it_real_value */ + sp->start_time = fast_strtoul_10(&cp); + /* vsz is in bytes and we want kb */ + sp->vsz = fast_strtoul_10(&cp) >> 10; + /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ + sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; +#if ENABLE_FEATURE_TOP_SMP_PROCESS + /* (6): rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ + /* (4): signal, blocked, sigignore, sigcatch */ + /* (4): wchan, nswap, cnswap, exit_signal */ + cp = skip_fields(cp, 14); +//FIXME: is it safe to assume this field exists? + sp->last_seen_on_cpu = fast_strtoul_10(&cp); +#endif +#endif /* end of !ENABLE_FEATURE_TOP_SMP_PROCESS */ + + if (sp->vsz == 0 && sp->state[0] != 'Z') + sp->state[1] = 'W'; + else + sp->state[1] = ' '; + if (tasknice < 0) + sp->state[2] = '<'; + else if (tasknice) /* > 0 */ + sp->state[2] = 'N'; + else + sp->state[2] = ' '; + } + +#if ENABLE_FEATURE_TOPMEM + if (flags & (PSSCAN_SMAPS)) { + FILE *file; + + strcpy(filename_tail, "/smaps"); + file = fopen_for_read(filename); + if (!file) + break; + while (fgets(buf, sizeof(buf), file)) { + unsigned long sz; + char *tp; + char w; +#define SCAN(str, name) \ + if (strncmp(buf, str, sizeof(str)-1) == 0) { \ + tp = skip_whitespace(buf + sizeof(str)-1); \ + sp->name += fast_strtoul_10(&tp); \ + continue; \ + } + SCAN("Shared_Clean:" , shared_clean ); + SCAN("Shared_Dirty:" , shared_dirty ); + SCAN("Private_Clean:", private_clean); + SCAN("Private_Dirty:", private_dirty); +#undef SCAN + // f7d29000-f7d39000 rw-s ADR M:m OFS FILE + tp = strchr(buf, '-'); + if (tp) { + *tp = ' '; + tp = buf; + sz = fast_strtoul_16(&tp); /* start */ + sz = (fast_strtoul_16(&tp) - sz) >> 10; /* end - start */ + // tp -> "rw-s" string + w = tp[1]; + // skipping "rw-s ADR M:m OFS " + tp = skip_whitespace(skip_fields(tp, 4)); + // filter out /dev/something (something != zero) + if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) { + if (w == 'w') { + sp->mapped_rw += sz; + } else if (w == '-') { + sp->mapped_ro += sz; + } + } +//else printf("DROPPING %s (%s)\n", buf, tp); + if (strcmp(tp, "[stack]\n") == 0) + sp->stack += sz; + } + } + fclose(file); + } +#endif /* TOPMEM */ + +#if 0 /* PSSCAN_CMD is not used */ + if (flags & (PSSCAN_CMD|PSSCAN_ARGV0)) { + free(sp->argv0); + sp->argv0 = NULL; + free(sp->cmd); + sp->cmd = NULL; + strcpy(filename_tail, "/cmdline"); + /* TODO: to get rid of size limits, read into malloc buf, + * then realloc it down to real size. */ + n = read_to_buf(filename, buf); + if (n <= 0) + break; + if (flags & PSSCAN_ARGV0) + sp->argv0 = xstrdup(buf); + if (flags & PSSCAN_CMD) { + do { + n--; + if ((unsigned char)(buf[n]) < ' ') + buf[n] = ' '; + } while (n); + sp->cmd = xstrdup(buf); + } + } +#else + if (flags & (PSSCAN_ARGV0|PSSCAN_ARGVN)) { + free(sp->argv0); + sp->argv0 = NULL; + strcpy(filename_tail, "/cmdline"); + n = read_to_buf(filename, buf); + if (n <= 0) + break; + if (flags & PSSCAN_ARGVN) { + sp->argv_len = n; + sp->argv0 = xmalloc(n + 1); + memcpy(sp->argv0, buf, n + 1); + /* sp->argv0[n] = '\0'; - buf has it */ + } else { + sp->argv_len = 0; + sp->argv0 = xstrdup(buf); + } + } +#endif + break; + } + return sp; +} + +void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) +{ + ssize_t sz; + char filename[sizeof("/proc//cmdline") + sizeof(int)*3]; + + sprintf(filename, "/proc/%u/cmdline", pid); + sz = open_read_close(filename, buf, col); + if (sz > 0) { + buf[sz] = '\0'; + while (--sz >= 0) + if ((unsigned char)(buf[sz]) < ' ') + buf[sz] = ' '; + } else { + snprintf(buf, col, "[%s]", comm); + } +} + +/* from kernel: + // pid comm S ppid pgid sid tty_nr tty_pgrp flg + sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ +%lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \ +%lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu %llu\n", + task->pid, + tcomm, + state, + ppid, + pgid, + sid, + tty_nr, + tty_pgrp, + task->flags, + min_flt, + cmin_flt, + maj_flt, + cmaj_flt, + cputime_to_clock_t(utime), + cputime_to_clock_t(stime), + cputime_to_clock_t(cutime), + cputime_to_clock_t(cstime), + priority, + nice, + num_threads, + // 0, + start_time, + vsize, + mm ? get_mm_rss(mm) : 0, + rsslim, + mm ? mm->start_code : 0, + mm ? mm->end_code : 0, + mm ? mm->start_stack : 0, + esp, + eip, +the rest is some obsolete cruft +*/ diff --git a/libbb/ptr_to_globals.c b/libbb/ptr_to_globals.c new file mode 100644 index 0000000..5f30e2a --- /dev/null +++ b/libbb/ptr_to_globals.c @@ -0,0 +1,35 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2008 by Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +#include + +struct globals; + +#ifndef GCC_COMBINE + +/* We cheat here. It is declared as const ptr in libbb.h, + * but here we make it live in R/W memory */ +struct globals *ptr_to_globals; + +#ifdef __GLIBC__ +int *bb_errno; +#endif + + +#else + + +/* gcc -combine will see through and complain */ +/* Using alternative method which is more likely to break + * on weird architectures, compilers, linkers and so on */ +struct globals *const ptr_to_globals __attribute__ ((section (".data"))); + +#ifdef __GLIBC__ +int *const bb_errno __attribute__ ((section (".data"))); +#endif + +#endif diff --git a/libbb/pw_encrypt.c b/libbb/pw_encrypt.c new file mode 100644 index 0000000..0b826f4 --- /dev/null +++ b/libbb/pw_encrypt.c @@ -0,0 +1,77 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routine. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +#if ENABLE_USE_BB_CRYPT + +/* + * DES and MD5 crypt implementations are taken from uclibc. + * They were modified to not use static buffers. + */ +/* Common for them */ +static const uint8_t ascii64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +#include "pw_encrypt_des.c" +#include "pw_encrypt_md5.c" + +/* Other advanced crypt ids: */ +/* $2$ or $2a$: Blowfish */ +/* $5$: SHA-256 */ +/* $6$: SHA-512 */ +/* TODO: implement SHA - http://people.redhat.com/drepper/SHA-crypt.txt */ + +static struct const_des_ctx *des_cctx; +static struct des_ctx *des_ctx; + +/* my_crypt returns malloc'ed data */ +static char *my_crypt(const char *key, const char *salt) +{ + /* First, check if we are supposed to be using the MD5 replacement + * instead of DES... */ + if (salt[0] == '$' && salt[1] == '1' && salt[2] == '$') { + return md5_crypt(xzalloc(MD5_OUT_BUFSIZE), (unsigned char*)key, (unsigned char*)salt); + } + + { + if (!des_cctx) + des_cctx = const_des_init(); + des_ctx = des_init(des_ctx, des_cctx); + return des_crypt(des_ctx, xzalloc(DES_OUT_BUFSIZE), (unsigned char*)key, (unsigned char*)salt); + } +} + +/* So far nobody wants to have it public */ +static void my_crypt_cleanup(void) +{ + free(des_cctx); + free(des_ctx); + des_cctx = NULL; + des_ctx = NULL; +} + +char* FAST_FUNC pw_encrypt(const char *clear, const char *salt, int cleanup) +{ + char *encrypted; + + encrypted = my_crypt(clear, salt); + + if (cleanup) + my_crypt_cleanup(); + + return encrypted; +} + +#else /* if !ENABLE_USE_BB_CRYPT */ + +char* FAST_FUNC pw_encrypt(const char *clear, const char *salt, int cleanup) +{ + return xstrdup(crypt(clear, salt)); +} + +#endif diff --git a/libbb/pw_encrypt_des.c b/libbb/pw_encrypt_des.c new file mode 100644 index 0000000..cd19a63 --- /dev/null +++ b/libbb/pw_encrypt_des.c @@ -0,0 +1,798 @@ +/* + * FreeSec: libcrypt for NetBSD + * + * Copyright (c) 1994 David Burren + * All rights reserved. + * + * Adapted for FreeBSD-2.0 by Geoffrey M. Rehmet + * this file should now *only* export crypt(), in order to make + * binaries of libcrypt exportable from the USA + * + * Adapted for FreeBSD-4.0 by Mark R V Murray + * this file should now *only* export crypt_des(), in order to make + * a module that can be optionally included in libcrypt. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of other contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This is an original implementation of the DES and the crypt(3) interfaces + * by David Burren . + * + * An excellent reference on the underlying algorithm (and related + * algorithms) is: + * + * B. Schneier, Applied Cryptography: protocols, algorithms, + * and source code in C, John Wiley & Sons, 1994. + * + * Note that in that book's description of DES the lookups for the initial, + * pbox, and final permutations are inverted (this has been brought to the + * attention of the author). A list of errata for this book has been + * posted to the sci.crypt newsgroup by the author and is available for FTP. + * + * ARCHITECTURE ASSUMPTIONS: + * It is assumed that the 8-byte arrays passed by reference can be + * addressed as arrays of uint32_t's (ie. the CPU is not picky about + * alignment). + */ + + +/* Parts busybox doesn't need or had optimized */ +#define USE_PRECOMPUTED_u_sbox 1 +#define USE_REPETITIVE_SPEEDUP 0 +#define USE_ip_mask 0 +#define USE_de_keys 0 + + +/* A pile of data */ +static const uint8_t IP[64] = { + 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 +}; + +static const uint8_t key_perm[56] = { + 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 +}; + +static const uint8_t key_shifts[16] = { + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 +}; + +static const uint8_t comp_perm[48] = { + 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 +}; + +/* + * No E box is used, as it's replaced by some ANDs, shifts, and ORs. + */ +#if !USE_PRECOMPUTED_u_sbox +static const uint8_t sbox[8][64] = { + { 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 + }, + { 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 + }, + { 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 + }, + { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 + }, + { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 + }, + { 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 + }, + { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 + }, + { 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 + } +}; +#else /* precomputed, with half-bytes packed into one byte */ +static const uint8_t u_sbox[8][32] = { + { 0x0e, 0xf4, 0x7d, 0x41, 0xe2, 0x2f, 0xdb, 0x18, + 0xa3, 0x6a, 0xc6, 0xbc, 0x95, 0x59, 0x30, 0x87, + 0xf4, 0xc1, 0x8e, 0x28, 0x4d, 0x96, 0x12, 0x7b, + 0x5f, 0xbc, 0x39, 0xe7, 0xa3, 0x0a, 0x65, 0xd0, + }, + { 0x3f, 0xd1, 0x48, 0x7e, 0xf6, 0x2b, 0x83, 0xe4, + 0xc9, 0x07, 0x12, 0xad, 0x6c, 0x90, 0xb5, 0x5a, + 0xd0, 0x8e, 0xa7, 0x1b, 0x3a, 0xf4, 0x4d, 0x21, + 0xb5, 0x68, 0x7c, 0xc6, 0x09, 0x53, 0xe2, 0x9f, + }, + { 0xda, 0x70, 0x09, 0x9e, 0x36, 0x43, 0x6f, 0xa5, + 0x21, 0x8d, 0x5c, 0xe7, 0xcb, 0xb4, 0xf2, 0x18, + 0x1d, 0xa6, 0xd4, 0x09, 0x68, 0x9f, 0x83, 0x70, + 0x4b, 0xf1, 0xe2, 0x3c, 0xb5, 0x5a, 0x2e, 0xc7, + }, + { 0xd7, 0x8d, 0xbe, 0x53, 0x60, 0xf6, 0x09, 0x3a, + 0x41, 0x72, 0x28, 0xc5, 0x1b, 0xac, 0xe4, 0x9f, + 0x3a, 0xf6, 0x09, 0x60, 0xac, 0x1b, 0xd7, 0x8d, + 0x9f, 0x41, 0x53, 0xbe, 0xc5, 0x72, 0x28, 0xe4, + }, + { 0xe2, 0xbc, 0x24, 0xc1, 0x47, 0x7a, 0xdb, 0x16, + 0x58, 0x05, 0xf3, 0xaf, 0x3d, 0x90, 0x8e, 0x69, + 0xb4, 0x82, 0xc1, 0x7b, 0x1a, 0xed, 0x27, 0xd8, + 0x6f, 0xf9, 0x0c, 0x95, 0xa6, 0x43, 0x50, 0x3e, + }, + { 0xac, 0xf1, 0x4a, 0x2f, 0x79, 0xc2, 0x96, 0x58, + 0x60, 0x1d, 0xd3, 0xe4, 0x0e, 0xb7, 0x35, 0x8b, + 0x49, 0x3e, 0x2f, 0xc5, 0x92, 0x58, 0xfc, 0xa3, + 0xb7, 0xe0, 0x14, 0x7a, 0x61, 0x0d, 0x8b, 0xd6, + }, + { 0xd4, 0x0b, 0xb2, 0x7e, 0x4f, 0x90, 0x18, 0xad, + 0xe3, 0x3c, 0x59, 0xc7, 0x25, 0xfa, 0x86, 0x61, + 0x61, 0xb4, 0xdb, 0x8d, 0x1c, 0x43, 0xa7, 0x7e, + 0x9a, 0x5f, 0x06, 0xf8, 0xe0, 0x25, 0x39, 0xc2, + }, + { 0x1d, 0xf2, 0xd8, 0x84, 0xa6, 0x3f, 0x7b, 0x41, + 0xca, 0x59, 0x63, 0xbe, 0x05, 0xe0, 0x9c, 0x27, + 0x27, 0x1b, 0xe4, 0x71, 0x49, 0xac, 0x8e, 0xd2, + 0xf0, 0xc6, 0x9a, 0x0d, 0x3f, 0x53, 0x65, 0xb8, + }, +}; +#endif + +static const uint8_t pbox[32] = { + 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 +}; + +static const uint32_t bits32[32] = +{ + 0x80000000, 0x40000000, 0x20000000, 0x10000000, + 0x08000000, 0x04000000, 0x02000000, 0x01000000, + 0x00800000, 0x00400000, 0x00200000, 0x00100000, + 0x00080000, 0x00040000, 0x00020000, 0x00010000, + 0x00008000, 0x00004000, 0x00002000, 0x00001000, + 0x00000800, 0x00000400, 0x00000200, 0x00000100, + 0x00000080, 0x00000040, 0x00000020, 0x00000010, + 0x00000008, 0x00000004, 0x00000002, 0x00000001 +}; + +static const uint8_t bits8[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; + + +static int +ascii_to_bin(char ch) +{ + if (ch > 'z') + return 0; + if (ch >= 'a') + return (ch - 'a' + 38); + if (ch > 'Z') + return 0; + if (ch >= 'A') + return (ch - 'A' + 12); + if (ch > '9') + return 0; + if (ch >= '.') + return (ch - '.'); + return 0; +} + + +/* Static stuff that stays resident and doesn't change after + * being initialized, and therefore doesn't need to be made + * reentrant. */ +struct const_des_ctx { +#if USE_ip_mask + uint8_t init_perm[64]; /* referenced 2 times */ +#endif + uint8_t final_perm[64]; /* 2 times */ + uint8_t m_sbox[4][4096]; /* 5 times */ +}; +#define C (*cctx) +#define init_perm (C.init_perm ) +#define final_perm (C.final_perm) +#define m_sbox (C.m_sbox ) + +static struct const_des_ctx* +const_des_init(void) +{ + unsigned i, j, b; + struct const_des_ctx *cctx; + +#if !USE_PRECOMPUTED_u_sbox + uint8_t u_sbox[8][64]; + + cctx = xmalloc(sizeof(*cctx)); + + /* Invert the S-boxes, reordering the input bits. */ + for (i = 0; i < 8; i++) { + for (j = 0; j < 64; j++) { + b = (j & 0x20) | ((j & 1) << 4) | ((j >> 1) & 0xf); + u_sbox[i][j] = sbox[i][b]; + } + } + for (i = 0; i < 8; i++) { + fprintf(stderr, "\t{\t"); + for (j = 0; j < 64; j+=2) + fprintf(stderr, " 0x%02x,", u_sbox[i][j] + u_sbox[i][j+1]*16); + fprintf(stderr, "\n\t},\n"); + } + /* + * Convert the inverted S-boxes into 4 arrays of 8 bits. + * Each will handle 12 bits of the S-box input. + */ + for (b = 0; b < 4; b++) + for (i = 0; i < 64; i++) + for (j = 0; j < 64; j++) + m_sbox[b][(i << 6) | j] = + (uint8_t)((u_sbox[(b << 1)][i] << 4) | + u_sbox[(b << 1) + 1][j]); +#else + cctx = xmalloc(sizeof(*cctx)); + + /* + * Convert the inverted S-boxes into 4 arrays of 8 bits. + * Each will handle 12 bits of the S-box input. + */ + for (b = 0; b < 4; b++) + for (i = 0; i < 64; i++) + for (j = 0; j < 64; j++) { + uint8_t lo, hi; + hi = u_sbox[(b << 1)][i / 2]; + if (!(i & 1)) + hi <<= 4; + lo = u_sbox[(b << 1) + 1][j / 2]; + if (j & 1) + lo >>= 4; + m_sbox[b][(i << 6) | j] = (hi & 0xf0) | (lo & 0x0f); + } +#endif + + /* + * Set up the initial & final permutations into a useful form. + */ + for (i = 0; i < 64; i++) { + final_perm[i] = IP[i] - 1; +#if USE_ip_mask + init_perm[final_perm[i]] = (uint8_t)i; +#endif + } + + return cctx; +} + + +struct des_ctx { + const struct const_des_ctx *const_ctx; + uint32_t saltbits; /* referenced 5 times */ +#if USE_REPETITIVE_SPEEDUP + uint32_t old_salt; /* 3 times */ + uint32_t old_rawkey0, old_rawkey1; /* 3 times each */ +#endif + uint8_t un_pbox[32]; /* 2 times */ + uint8_t inv_comp_perm[56]; /* 3 times */ + uint8_t inv_key_perm[64]; /* 3 times */ + uint32_t en_keysl[16], en_keysr[16]; /* 2 times each */ +#if USE_de_keys + uint32_t de_keysl[16], de_keysr[16]; /* 2 times each */ +#endif +#if USE_ip_mask + uint32_t ip_maskl[8][256], ip_maskr[8][256]; /* 9 times each */ +#endif + uint32_t fp_maskl[8][256], fp_maskr[8][256]; /* 9 times each */ + uint32_t key_perm_maskl[8][128], key_perm_maskr[8][128]; /* 9 times */ + uint32_t comp_maskl[8][128], comp_maskr[8][128]; /* 9 times each */ + uint32_t psbox[4][256]; /* 5 times */ +}; +#define D (*ctx) +#define const_ctx (D.const_ctx ) +#define saltbits (D.saltbits ) +#define old_salt (D.old_salt ) +#define old_rawkey0 (D.old_rawkey0 ) +#define old_rawkey1 (D.old_rawkey1 ) +#define un_pbox (D.un_pbox ) +#define inv_comp_perm (D.inv_comp_perm ) +#define inv_key_perm (D.inv_key_perm ) +#define en_keysl (D.en_keysl ) +#define en_keysr (D.en_keysr ) +#define de_keysl (D.de_keysl ) +#define de_keysr (D.de_keysr ) +#define ip_maskl (D.ip_maskl ) +#define ip_maskr (D.ip_maskr ) +#define fp_maskl (D.fp_maskl ) +#define fp_maskr (D.fp_maskr ) +#define key_perm_maskl (D.key_perm_maskl ) +#define key_perm_maskr (D.key_perm_maskr ) +#define comp_maskl (D.comp_maskl ) +#define comp_maskr (D.comp_maskr ) +#define psbox (D.psbox ) + +static struct des_ctx* +des_init(struct des_ctx *ctx, const struct const_des_ctx *cctx) +{ + int i, j, b, k, inbit, obit; + uint32_t p; + const uint32_t *bits28, *bits24; + + if (!ctx) + ctx = xmalloc(sizeof(*ctx)); + const_ctx = cctx; + +#if USE_REPETITIVE_SPEEDUP + old_rawkey0 = old_rawkey1 = 0; + old_salt = 0; +#endif + saltbits = 0; + bits28 = bits32 + 4; + bits24 = bits28 + 4; + + /* Initialise the inverted key permutation. */ + for (i = 0; i < 64; i++) { + inv_key_perm[i] = 255; + } + + /* + * Invert the key permutation and initialise the inverted key + * compression permutation. + */ + for (i = 0; i < 56; i++) { + inv_key_perm[key_perm[i] - 1] = (uint8_t)i; + inv_comp_perm[i] = 255; + } + + /* Invert the key compression permutation. */ + for (i = 0; i < 48; i++) { + inv_comp_perm[comp_perm[i] - 1] = (uint8_t)i; + } + + /* + * Set up the OR-mask arrays for the initial and final permutations, + * and for the key initial and compression permutations. + */ + for (k = 0; k < 8; k++) { + uint32_t il, ir; + uint32_t fl, fr; + for (i = 0; i < 256; i++) { +#if USE_ip_mask + il = 0; + ir = 0; +#endif + fl = 0; + fr = 0; + for (j = 0; j < 8; j++) { + inbit = 8 * k + j; + if (i & bits8[j]) { +#if USE_ip_mask + obit = init_perm[inbit]; + if (obit < 32) + il |= bits32[obit]; + else + ir |= bits32[obit - 32]; +#endif + obit = final_perm[inbit]; + if (obit < 32) + fl |= bits32[obit]; + else + fr |= bits32[obit - 32]; + } + } +#if USE_ip_mask + ip_maskl[k][i] = il; + ip_maskr[k][i] = ir; +#endif + fp_maskl[k][i] = fl; + fp_maskr[k][i] = fr; + } + for (i = 0; i < 128; i++) { + il = 0; + ir = 0; + for (j = 0; j < 7; j++) { + inbit = 8 * k + j; + if (i & bits8[j + 1]) { + obit = inv_key_perm[inbit]; + if (obit == 255) + continue; + if (obit < 28) + il |= bits28[obit]; + else + ir |= bits28[obit - 28]; + } + } + key_perm_maskl[k][i] = il; + key_perm_maskr[k][i] = ir; + il = 0; + ir = 0; + for (j = 0; j < 7; j++) { + inbit = 7 * k + j; + if (i & bits8[j + 1]) { + obit = inv_comp_perm[inbit]; + if (obit == 255) + continue; + if (obit < 24) + il |= bits24[obit]; + else + ir |= bits24[obit - 24]; + } + } + comp_maskl[k][i] = il; + comp_maskr[k][i] = ir; + } + } + + /* + * Invert the P-box permutation, and convert into OR-masks for + * handling the output of the S-box arrays setup above. + */ + for (i = 0; i < 32; i++) + un_pbox[pbox[i] - 1] = (uint8_t)i; + + for (b = 0; b < 4; b++) { + for (i = 0; i < 256; i++) { + p = 0; + for (j = 0; j < 8; j++) { + if (i & bits8[j]) + p |= bits32[un_pbox[8 * b + j]]; + } + psbox[b][i] = p; + } + } + + return ctx; +} + + +static void +setup_salt(struct des_ctx *ctx, uint32_t salt) +{ + uint32_t obit, saltbit; + int i; + +#if USE_REPETITIVE_SPEEDUP + if (salt == old_salt) + return; + old_salt = salt; +#endif + + saltbits = 0; + saltbit = 1; + obit = 0x800000; + for (i = 0; i < 24; i++) { + if (salt & saltbit) + saltbits |= obit; + saltbit <<= 1; + obit >>= 1; + } +} + +static void +des_setkey(struct des_ctx *ctx, const char *key) +{ + uint32_t k0, k1, rawkey0, rawkey1; + int shifts, round; + + rawkey0 = ntohl(*(const uint32_t *) key); + rawkey1 = ntohl(*(const uint32_t *) (key + 4)); + +#if USE_REPETITIVE_SPEEDUP + if ((rawkey0 | rawkey1) + && rawkey0 == old_rawkey0 + && rawkey1 == old_rawkey1 + ) { + /* + * Already setup for this key. + * This optimisation fails on a zero key (which is weak and + * has bad parity anyway) in order to simplify the starting + * conditions. + */ + return; + } + old_rawkey0 = rawkey0; + old_rawkey1 = rawkey1; +#endif + + /* + * Do key permutation and split into two 28-bit subkeys. + */ + k0 = key_perm_maskl[0][rawkey0 >> 25] + | key_perm_maskl[1][(rawkey0 >> 17) & 0x7f] + | key_perm_maskl[2][(rawkey0 >> 9) & 0x7f] + | key_perm_maskl[3][(rawkey0 >> 1) & 0x7f] + | key_perm_maskl[4][rawkey1 >> 25] + | key_perm_maskl[5][(rawkey1 >> 17) & 0x7f] + | key_perm_maskl[6][(rawkey1 >> 9) & 0x7f] + | key_perm_maskl[7][(rawkey1 >> 1) & 0x7f]; + k1 = key_perm_maskr[0][rawkey0 >> 25] + | key_perm_maskr[1][(rawkey0 >> 17) & 0x7f] + | key_perm_maskr[2][(rawkey0 >> 9) & 0x7f] + | key_perm_maskr[3][(rawkey0 >> 1) & 0x7f] + | key_perm_maskr[4][rawkey1 >> 25] + | key_perm_maskr[5][(rawkey1 >> 17) & 0x7f] + | key_perm_maskr[6][(rawkey1 >> 9) & 0x7f] + | key_perm_maskr[7][(rawkey1 >> 1) & 0x7f]; + /* + * Rotate subkeys and do compression permutation. + */ + shifts = 0; + for (round = 0; round < 16; round++) { + uint32_t t0, t1; + + shifts += key_shifts[round]; + + t0 = (k0 << shifts) | (k0 >> (28 - shifts)); + t1 = (k1 << shifts) | (k1 >> (28 - shifts)); + +#if USE_de_keys + de_keysl[15 - round] = +#endif + en_keysl[round] = comp_maskl[0][(t0 >> 21) & 0x7f] + | comp_maskl[1][(t0 >> 14) & 0x7f] + | comp_maskl[2][(t0 >> 7) & 0x7f] + | comp_maskl[3][t0 & 0x7f] + | comp_maskl[4][(t1 >> 21) & 0x7f] + | comp_maskl[5][(t1 >> 14) & 0x7f] + | comp_maskl[6][(t1 >> 7) & 0x7f] + | comp_maskl[7][t1 & 0x7f]; + +#if USE_de_keys + de_keysr[15 - round] = +#endif + en_keysr[round] = comp_maskr[0][(t0 >> 21) & 0x7f] + | comp_maskr[1][(t0 >> 14) & 0x7f] + | comp_maskr[2][(t0 >> 7) & 0x7f] + | comp_maskr[3][t0 & 0x7f] + | comp_maskr[4][(t1 >> 21) & 0x7f] + | comp_maskr[5][(t1 >> 14) & 0x7f] + | comp_maskr[6][(t1 >> 7) & 0x7f] + | comp_maskr[7][t1 & 0x7f]; + } +} + + +static void +do_des(struct des_ctx *ctx, /*uint32_t l_in, uint32_t r_in,*/ uint32_t *l_out, uint32_t *r_out, int count) +{ + const struct const_des_ctx *cctx = const_ctx; + /* + * l_in, r_in, l_out, and r_out are in pseudo-"big-endian" format. + */ + uint32_t l, r, *kl, *kr; + uint32_t f = f; /* silence gcc */ + uint32_t r48l, r48r; + int round; + + /* Do initial permutation (IP). */ +#if USE_ip_mask + uint32_t l_in = 0; + uint32_t r_in = 0; + l = ip_maskl[0][l_in >> 24] + | ip_maskl[1][(l_in >> 16) & 0xff] + | ip_maskl[2][(l_in >> 8) & 0xff] + | ip_maskl[3][l_in & 0xff] + | ip_maskl[4][r_in >> 24] + | ip_maskl[5][(r_in >> 16) & 0xff] + | ip_maskl[6][(r_in >> 8) & 0xff] + | ip_maskl[7][r_in & 0xff]; + r = ip_maskr[0][l_in >> 24] + | ip_maskr[1][(l_in >> 16) & 0xff] + | ip_maskr[2][(l_in >> 8) & 0xff] + | ip_maskr[3][l_in & 0xff] + | ip_maskr[4][r_in >> 24] + | ip_maskr[5][(r_in >> 16) & 0xff] + | ip_maskr[6][(r_in >> 8) & 0xff] + | ip_maskr[7][r_in & 0xff]; +#elif 0 /* -65 bytes (using the fact that l_in == r_in == 0) */ + l = r = 0; + for (round = 0; round < 8; round++) { + l |= ip_maskl[round][0]; + r |= ip_maskr[round][0]; + } + bb_error_msg("l:%x r:%x", l, r); /* reports 0, 0 always! */ +#else /* using the fact that ip_maskX[] is constant (written to by des_init) */ + l = r = 0; +#endif + + do { + /* Do each round. */ + kl = en_keysl; + kr = en_keysr; + round = 16; + do { + /* Expand R to 48 bits (simulate the E-box). */ + r48l = ((r & 0x00000001) << 23) + | ((r & 0xf8000000) >> 9) + | ((r & 0x1f800000) >> 11) + | ((r & 0x01f80000) >> 13) + | ((r & 0x001f8000) >> 15); + + r48r = ((r & 0x0001f800) << 7) + | ((r & 0x00001f80) << 5) + | ((r & 0x000001f8) << 3) + | ((r & 0x0000001f) << 1) + | ((r & 0x80000000) >> 31); + /* + * Do salting for crypt() and friends, and + * XOR with the permuted key. + */ + f = (r48l ^ r48r) & saltbits; + r48l ^= f ^ *kl++; + r48r ^= f ^ *kr++; + /* + * Do sbox lookups (which shrink it back to 32 bits) + * and do the pbox permutation at the same time. + */ + f = psbox[0][m_sbox[0][r48l >> 12]] + | psbox[1][m_sbox[1][r48l & 0xfff]] + | psbox[2][m_sbox[2][r48r >> 12]] + | psbox[3][m_sbox[3][r48r & 0xfff]]; + /* Now that we've permuted things, complete f(). */ + f ^= l; + l = r; + r = f; + } while (--round); + r = l; + l = f; + } while (--count); + + /* Do final permutation (inverse of IP). */ + *l_out = fp_maskl[0][l >> 24] + | fp_maskl[1][(l >> 16) & 0xff] + | fp_maskl[2][(l >> 8) & 0xff] + | fp_maskl[3][l & 0xff] + | fp_maskl[4][r >> 24] + | fp_maskl[5][(r >> 16) & 0xff] + | fp_maskl[6][(r >> 8) & 0xff] + | fp_maskl[7][r & 0xff]; + *r_out = fp_maskr[0][l >> 24] + | fp_maskr[1][(l >> 16) & 0xff] + | fp_maskr[2][(l >> 8) & 0xff] + | fp_maskr[3][l & 0xff] + | fp_maskr[4][r >> 24] + | fp_maskr[5][(r >> 16) & 0xff] + | fp_maskr[6][(r >> 8) & 0xff] + | fp_maskr[7][r & 0xff]; +} + +#define DES_OUT_BUFSIZE 21 + +static char * +NOINLINE +des_crypt(struct des_ctx *ctx, char output[DES_OUT_BUFSIZE], + const unsigned char *key, const unsigned char *setting) +{ + uint32_t salt, l, r0, r1, keybuf[2]; + uint8_t *p, *q; + + /* + * Copy the key, shifting each character up by one bit + * and padding with zeros. + */ + q = (uint8_t *)keybuf; + while (q - (uint8_t *)keybuf != 8) { + *q = *key << 1; + if (*q) + key++; + q++; + } + des_setkey(ctx, (char *)keybuf); + + /* + * setting - 2 bytes of salt + * key - up to 8 characters + */ + salt = (ascii_to_bin(setting[1]) << 6) + | ascii_to_bin(setting[0]); + + output[0] = setting[0]; + /* + * If the encrypted password that the salt was extracted from + * is only 1 character long, the salt will be corrupted. We + * need to ensure that the output string doesn't have an extra + * NUL in it! + */ + output[1] = setting[1] ? setting[1] : output[0]; + + p = (uint8_t *)output + 2; + + setup_salt(ctx, salt); + /* + * Do it. + */ + do_des(ctx, /*0, 0,*/ &r0, &r1, 25 /* count */); + + /* + * Now encode the result... + */ + l = (r0 >> 8); + *p++ = ascii64[(l >> 18) & 0x3f]; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + + l = ((r0 << 16) | (r1 >> 16)); + *p++ = ascii64[(l >> 18) & 0x3f]; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + + l = r1 << 2; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + *p = 0; + + return output; +} + +#undef USE_PRECOMPUTED_u_sbox +#undef USE_REPETITIVE_SPEEDUP +#undef USE_ip_mask +#undef USE_de_keys + +#undef C +#undef init_perm +#undef final_perm +#undef m_sbox +#undef D +#undef const_ctx +#undef saltbits +#undef old_salt +#undef old_rawkey0 +#undef old_rawkey1 +#undef un_pbox +#undef inv_comp_perm +#undef inv_key_perm +#undef en_keysl +#undef en_keysr +#undef de_keysl +#undef de_keysr +#undef ip_maskl +#undef ip_maskr +#undef fp_maskl +#undef fp_maskr +#undef key_perm_maskl +#undef key_perm_maskr +#undef comp_maskl +#undef comp_maskr +#undef psbox diff --git a/libbb/pw_encrypt_md5.c b/libbb/pw_encrypt_md5.c new file mode 100644 index 0000000..8d0a516 --- /dev/null +++ b/libbb/pw_encrypt_md5.c @@ -0,0 +1,648 @@ +/* + * MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + * + * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + * rights reserved. + * + * License to copy and use this software is granted provided that it + * is identified as the "RSA Data Security, Inc. MD5 Message-Digest + * Algorithm" in all material mentioning or referencing this software + * or this function. + * + * License is also granted to make and use derivative works provided + * that such works are identified as "derived from the RSA Data + * Security, Inc. MD5 Message-Digest Algorithm" in all material + * mentioning or referencing the derived work. + * + * RSA Data Security, Inc. makes no representations concerning either + * the merchantability of this software or the suitability of this + * software for any particular purpose. It is provided "as is" + * without express or implied warranty of any kind. + * + * These notices must be retained in any copies of any part of this + * documentation and/or software. + * + * $FreeBSD: src/lib/libmd/md5c.c,v 1.9.2.1 1999/08/29 14:57:12 peter Exp $ + * + * This code is the same as the code published by RSA Inc. It has been + * edited for clarity and style only. + * + * ---------------------------------------------------------------------------- + * The md5_crypt() function was taken from freeBSD's libcrypt and contains + * this license: + * "THE BEER-WARE LICENSE" (Revision 42): + * wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * + * $FreeBSD: src/lib/libcrypt/crypt.c,v 1.7.2.1 1999/08/29 14:56:33 peter Exp $ + * + * ---------------------------------------------------------------------------- + * On April 19th, 2001 md5_crypt() was modified to make it reentrant + * by Erik Andersen + * + * + * June 28, 2001 Manuel Novoa III + * + * "Un-inlined" code using loops and static const tables in order to + * reduce generated code size (on i386 from approx 4k to approx 2.5k). + * + * June 29, 2001 Manuel Novoa III + * + * Completely removed static PADDING array. + * + * Reintroduced the loop unrolling in MD5_Transform and added the + * MD5_SIZE_OVER_SPEED option for configurability. Define below as: + * 0 fully unrolled loops + * 1 partially unrolled (4 ops per loop) + * 2 no unrolling -- introduces the need to swap 4 variables (slow) + * 3 no unrolling and all 4 loops merged into one with switch + * in each loop (glacial) + * On i386, sizes are roughly (-Os -fno-builtin): + * 0: 3k 1: 2.5k 2: 2.2k 3: 2k + * + * + * Since SuSv3 does not require crypt_r, modified again August 7, 2002 + * by Erik Andersen to remove reentrance stuff... + */ + +/* + * Valid values are 1 (fastest/largest) to 3 (smallest/slowest). + */ +#define MD5_SIZE_OVER_SPEED 3 + +/**********************************************************************/ + +/* MD5 context. */ +struct MD5Context { + uint32_t state[4]; /* state (ABCD) */ + uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +}; + +static void __md5_Init(struct MD5Context *); +static void __md5_Update(struct MD5Context *, const unsigned char *, unsigned int); +static void __md5_Pad(struct MD5Context *); +static void __md5_Final(unsigned char [16], struct MD5Context *); +static void __md5_Transform(uint32_t [4], const unsigned char [64]); + + +#define MD5_MAGIC_STR "$1$" +#define MD5_MAGIC_LEN (sizeof(MD5_MAGIC_STR) - 1) +static const unsigned char __md5__magic[] = MD5_MAGIC_STR; + + +#ifdef i386 +#define __md5_Encode memcpy +#define __md5_Decode memcpy +#else /* i386 */ + +/* + * __md5_Encodes input (uint32_t) into output (unsigned char). Assumes len is + * a multiple of 4. + */ +static void +__md5_Encode(unsigned char *output, uint32_t *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = input[i]; + output[j+1] = (input[i] >> 8); + output[j+2] = (input[i] >> 16); + output[j+3] = (input[i] >> 24); + } +} + +/* + * __md5_Decodes input (unsigned char) into output (uint32_t). Assumes len is + * a multiple of 4. + */ +static void +__md5_Decode(uint32_t *output, const unsigned char *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j+1]) << 8) | + (((uint32_t)input[j+2]) << 16) | (((uint32_t)input[j+3]) << 24); +} +#endif /* i386 */ + +/* F, G, H and I are basic MD5 functions. */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* ROTATE_LEFT rotates x left n bits. */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* + * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + * Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. */ +static void __md5_Init(struct MD5Context *context) +{ + context->count[0] = context->count[1] = 0; + + /* Load magic initialization constants. */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* + * MD5 block update operation. Continues an MD5 message-digest + * operation, processing another message block, and updating the + * context. + */ +static void __md5_Update(struct MD5Context *context, const unsigned char *input, unsigned int inputLen) +{ + unsigned int i, idx, partLen; + + /* Compute number of bytes mod 64 */ + idx = (context->count[0] >> 3) & 0x3F; + + /* Update number of bits */ + context->count[0] += (inputLen << 3); + if (context->count[0] < (inputLen << 3)) + context->count[1]++; + context->count[1] += (inputLen >> 29); + + partLen = 64 - idx; + + /* Transform as many times as possible. */ + if (inputLen >= partLen) { + memcpy(&context->buffer[idx], input, partLen); + __md5_Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + __md5_Transform(context->state, &input[i]); + + idx = 0; + } else + i = 0; + + /* Buffer remaining input */ + memcpy(&context->buffer[idx], &input[i], inputLen - i); +} + +/* + * MD5 padding. Adds padding followed by original length. + */ +static void __md5_Pad(struct MD5Context *context) +{ + unsigned char bits[8]; + unsigned int idx, padLen; + unsigned char PADDING[64]; + + memset(PADDING, 0, sizeof(PADDING)); + PADDING[0] = 0x80; + + /* Save number of bits */ + __md5_Encode(bits, context->count, 8); + + /* Pad out to 56 mod 64. */ + idx = (context->count[0] >> 3) & 0x3f; + padLen = (idx < 56) ? (56 - idx) : (120 - idx); + __md5_Update(context, PADDING, padLen); + + /* Append length (before padding) */ + __md5_Update(context, bits, 8); +} + +/* + * MD5 finalization. Ends an MD5 message-digest operation, writing the + * the message digest and zeroizing the context. + */ +static void __md5_Final(unsigned char digest[16], struct MD5Context *context) +{ + /* Do padding. */ + __md5_Pad(context); + + /* Store state in digest */ + __md5_Encode(digest, context->state, 16); + + /* Zeroize sensitive information. */ + memset(context, 0, sizeof(*context)); +} + +/* MD5 basic transformation. Transforms state based on block. */ +static void __md5_Transform(uint32_t state[4], const unsigned char block[64]) +{ + uint32_t a, b, c, d, x[16]; +#if MD5_SIZE_OVER_SPEED > 1 + uint32_t temp; + const unsigned char *ps; + + static const unsigned char S[] = { + 7, 12, 17, 22, + 5, 9, 14, 20, + 4, 11, 16, 23, + 6, 10, 15, 21 + }; +#endif /* MD5_SIZE_OVER_SPEED > 1 */ + +#if MD5_SIZE_OVER_SPEED > 0 + const uint32_t *pc; + const unsigned char *pp; + int i; + + static const uint32_t C[] = { + /* round 1 */ + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + /* round 2 */ + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + /* round 3 */ + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + /* round 4 */ + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 + }; + + static const unsigned char P[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */ + 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */ + 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */ + 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */ + }; + +#endif /* MD5_SIZE_OVER_SPEED > 0 */ + + __md5_Decode(x, block, 64); + + a = state[0]; b = state[1]; c = state[2]; d = state[3]; + +#if MD5_SIZE_OVER_SPEED > 2 + pc = C; pp = P; ps = S - 4; + + for (i = 0; i < 64; i++) { + if ((i & 0x0f) == 0) ps += 4; + temp = a; + switch (i>>4) { + case 0: + temp += F(b, c, d); + break; + case 1: + temp += G(b, c, d); + break; + case 2: + temp += H(b, c, d); + break; + case 3: + temp += I(b, c, d); + break; + } + temp += x[*pp++] + *pc++; + temp = ROTATE_LEFT(temp, ps[i & 3]); + temp += b; + a = d; d = c; c = b; b = temp; + } +#elif MD5_SIZE_OVER_SPEED > 1 + pc = C; pp = P; ps = S; + + /* Round 1 */ + for (i = 0; i < 16; i++) { + FF(a, b, c, d, x[*pp], ps[i & 0x3], *pc); pp++; pc++; + temp = d; d = c; c = b; b = a; a = temp; + } + + /* Round 2 */ + ps += 4; + for (; i < 32; i++) { + GG(a, b, c, d, x[*pp], ps[i & 0x3], *pc); pp++; pc++; + temp = d; d = c; c = b; b = a; a = temp; + } + /* Round 3 */ + ps += 4; + for (; i < 48; i++) { + HH(a, b, c, d, x[*pp], ps[i & 0x3], *pc); pp++; pc++; + temp = d; d = c; c = b; b = a; a = temp; + } + + /* Round 4 */ + ps += 4; + for (; i < 64; i++) { + II(a, b, c, d, x[*pp], ps[i & 0x3], *pc); pp++; pc++; + temp = d; d = c; c = b; b = a; a = temp; + } +#elif MD5_SIZE_OVER_SPEED > 0 + pc = C; pp = P; + + /* Round 1 */ + for (i = 0; i < 4; i++) { + FF(a, b, c, d, x[*pp], 7, *pc); pp++; pc++; + FF(d, a, b, c, x[*pp], 12, *pc); pp++; pc++; + FF(c, d, a, b, x[*pp], 17, *pc); pp++; pc++; + FF(b, c, d, a, x[*pp], 22, *pc); pp++; pc++; + } + + /* Round 2 */ + for (i = 0; i < 4; i++) { + GG(a, b, c, d, x[*pp], 5, *pc); pp++; pc++; + GG(d, a, b, c, x[*pp], 9, *pc); pp++; pc++; + GG(c, d, a, b, x[*pp], 14, *pc); pp++; pc++; + GG(b, c, d, a, x[*pp], 20, *pc); pp++; pc++; + } + /* Round 3 */ + for (i = 0; i < 4; i++) { + HH(a, b, c, d, x[*pp], 4, *pc); pp++; pc++; + HH(d, a, b, c, x[*pp], 11, *pc); pp++; pc++; + HH(c, d, a, b, x[*pp], 16, *pc); pp++; pc++; + HH(b, c, d, a, x[*pp], 23, *pc); pp++; pc++; + } + + /* Round 4 */ + for (i = 0; i < 4; i++) { + II(a, b, c, d, x[*pp], 6, *pc); pp++; pc++; + II(d, a, b, c, x[*pp], 10, *pc); pp++; pc++; + II(c, d, a, b, x[*pp], 15, *pc); pp++; pc++; + II(b, c, d, a, x[*pp], 21, *pc); pp++; pc++; + } +#else + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF(a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF(d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF(c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF(b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF(a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF(d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF(c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF(b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF(a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF(d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG(a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG(d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG(b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG(a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG(b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG(a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG(c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG(b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG(d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG(c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH(a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH(d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH(a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH(d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH(c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH(d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH(c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH(b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH(a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH(b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II(a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II(d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II(b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II(d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II(b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II(a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II(c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II(a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II(c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II(b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ +#endif + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. */ + memset(x, 0, sizeof(x)); +} + + +static char* +__md5_to64(char *s, unsigned v, int n) +{ + while (--n >= 0) { + *s++ = ascii64[v & 0x3f]; + v >>= 6; + } + return s; +} + +/* + * UNIX password + * + * Use MD5 for what it is best at... + */ +#define MD5_OUT_BUFSIZE 36 +static char * +NOINLINE +md5_crypt(char passwd[MD5_OUT_BUFSIZE], const unsigned char *pw, const unsigned char *salt) +{ + const unsigned char *sp, *ep; + char *p; + unsigned char final[17]; /* final[16] exists only to aid in looping */ + int sl, pl, i, pw_len; + struct MD5Context ctx, ctx1; + + /* Refine the Salt first */ + sp = salt; + +// always true for bbox +// /* If it starts with the magic string, then skip that */ +// if (!strncmp(sp, __md5__magic, MD5_MAGIC_LEN)) + sp += MD5_MAGIC_LEN; + + /* It stops at the first '$', max 8 chars */ + for (ep = sp; *ep && *ep != '$' && ep < (sp+8); ep++) + continue; + + /* get the length of the true salt */ + sl = ep - sp; + + __md5_Init(&ctx); + + /* The password first, since that is what is most unknown */ + pw_len = strlen((char*)pw); + __md5_Update(&ctx, pw, pw_len); + + /* Then our magic string */ + __md5_Update(&ctx, __md5__magic, MD5_MAGIC_LEN); + + /* Then the raw salt */ + __md5_Update(&ctx, sp, sl); + + /* Then just as many characters of the MD5(pw, salt, pw) */ + __md5_Init(&ctx1); + __md5_Update(&ctx1, pw, pw_len); + __md5_Update(&ctx1, sp, sl); + __md5_Update(&ctx1, pw, pw_len); + __md5_Final(final, &ctx1); + for (pl = pw_len; pl > 0; pl -= 16) + __md5_Update(&ctx, final, pl > 16 ? 16 : pl); + + /* Don't leave anything around in vm they could use. */ +//TODO: the above comment seems to be wrong. final is used later. + memset(final, 0, sizeof(final)); + + /* Then something really weird... */ + for (i = pw_len; i; i >>= 1) { + __md5_Update(&ctx, ((i & 1) ? final : (const unsigned char *) pw), 1); + } + + /* Now make the output string */ + passwd[0] = '$'; + passwd[1] = '1'; + passwd[2] = '$'; + strncpy(passwd + 3, (char*)sp, sl); + passwd[sl + 3] = '$'; + + __md5_Final(final, &ctx); + + /* + * and now, just to make sure things don't run too fast + * On a 60 Mhz Pentium this takes 34 msec, so you would + * need 30 seconds to build a 1000 entry dictionary... + */ + for (i = 0; i < 1000; i++) { + __md5_Init(&ctx1); + if (i & 1) + __md5_Update(&ctx1, pw, pw_len); + else + __md5_Update(&ctx1, final, 16); + + if (i % 3) + __md5_Update(&ctx1, sp, sl); + + if (i % 7) + __md5_Update(&ctx1, pw, pw_len); + + if (i & 1) + __md5_Update(&ctx1, final, 16); + else + __md5_Update(&ctx1, pw, pw_len); + __md5_Final(final, &ctx1); + } + + p = passwd + sl + 4; /* 12 bytes max (sl is up to 8 bytes) */ + + /* Add 5*4+2 = 22 bytes of hash, + NUL byte. */ + final[16] = final[5]; + for (i = 0; i < 5; i++) { + unsigned l = (final[i] << 16) | (final[i+6] << 8) | final[i+12]; + p = __md5_to64(p, l, 4); + } + p = __md5_to64(p, final[11], 2); + *p = '\0'; + + /* Don't leave anything around in vm they could use. */ + memset(final, 0, sizeof(final)); + + return passwd; +} + +#undef MD5_SIZE_OVER_SPEED +#undef MD5_MAGIC_STR +#undef MD5_MAGIC_LEN +#undef __md5_Encode +#undef __md5_Decode +#undef F +#undef G +#undef H +#undef I +#undef ROTATE_LEFT +#undef FF +#undef GG +#undef HH +#undef II +#undef S11 +#undef S12 +#undef S13 +#undef S14 +#undef S21 +#undef S22 +#undef S23 +#undef S24 +#undef S31 +#undef S32 +#undef S33 +#undef S34 +#undef S41 +#undef S42 +#undef S43 +#undef S44 diff --git a/libbb/read.c b/libbb/read.c new file mode 100644 index 0000000..815007c --- /dev/null +++ b/libbb/read.c @@ -0,0 +1,394 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +#define ZIPPED (ENABLE_FEATURE_SEAMLESS_LZMA \ + || ENABLE_FEATURE_SEAMLESS_BZ2 \ + || ENABLE_FEATURE_SEAMLESS_GZ \ + /* || ENABLE_FEATURE_SEAMLESS_Z */ \ +) + +#if ZIPPED +#include "unarchive.h" +#endif + +ssize_t FAST_FUNC safe_read(int fd, void *buf, size_t count) +{ + ssize_t n; + + do { + n = read(fd, buf, count); + } while (n < 0 && errno == EINTR); + + return n; +} + +/* Suppose that you are a shell. You start child processes. + * They work and eventually exit. You want to get user input. + * You read stdin. But what happens if last child switched + * its stdin into O_NONBLOCK mode? + * + * *** SURPRISE! It will affect the parent too! *** + * *** BIG SURPRISE! It stays even after child exits! *** + * + * This is a design bug in UNIX API. + * fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) | O_NONBLOCK); + * will set nonblocking mode not only on _your_ stdin, but + * also on stdin of your parent, etc. + * + * In general, + * fd2 = dup(fd1); + * fcntl(fd2, F_SETFL, fcntl(fd2, F_GETFL, 0) | O_NONBLOCK); + * sets both fd1 and fd2 to O_NONBLOCK. This includes cases + * where duping is done implicitly by fork() etc. + * + * We need + * fcntl(fd2, F_SETFD, fcntl(fd2, F_GETFD, 0) | O_NONBLOCK); + * (note SETFD, not SETFL!) but such thing doesn't exist. + * + * Alternatively, we need nonblocking_read(fd, ...) which doesn't + * require O_NONBLOCK dance at all. Actually, it exists: + * n = recv(fd, buf, len, MSG_DONTWAIT); + * "MSG_DONTWAIT: + * Enables non-blocking operation; if the operation + * would block, EAGAIN is returned." + * but recv() works only for sockets! + * + * So far I don't see any good solution, I can only propose + * that affected readers should be careful and use this routine, + * which detects EAGAIN and uses poll() to wait on the fd. + * Thankfully, poll() doesn't care about O_NONBLOCK flag. + */ +ssize_t FAST_FUNC nonblock_safe_read(int fd, void *buf, size_t count) +{ + struct pollfd pfd[1]; + ssize_t n; + + while (1) { + n = safe_read(fd, buf, count); + if (n >= 0 || errno != EAGAIN) + return n; + /* fd is in O_NONBLOCK mode. Wait using poll and repeat */ + pfd[0].fd = fd; + pfd[0].events = POLLIN; + safe_poll(pfd, 1, -1); + } +} + +/* + * Read all of the supplied buffer from a file. + * This does multiple reads as necessary. + * Returns the amount read, or -1 on an error. + * A short read is returned on an end of file. + */ +ssize_t FAST_FUNC full_read(int fd, void *buf, size_t len) +{ + ssize_t cc; + ssize_t total; + + total = 0; + + while (len) { + cc = safe_read(fd, buf, len); + + if (cc < 0) { + if (total) { + /* we already have some! */ + /* user can do another read to know the error code */ + return total; + } + return cc; /* read() returns -1 on failure. */ + } + if (cc == 0) + break; + buf = ((char *)buf) + cc; + total += cc; + len -= cc; + } + + return total; +} + +/* Die with an error message if we can't read the entire buffer. */ +void FAST_FUNC xread(int fd, void *buf, size_t count) +{ + if (count) { + ssize_t size = full_read(fd, buf, count); + if ((size_t)size != count) + bb_error_msg_and_die("short read"); + } +} + +/* Die with an error message if we can't read one character. */ +unsigned char FAST_FUNC xread_char(int fd) +{ + char tmp; + xread(fd, &tmp, 1); + return tmp; +} + +// Reads one line a-la fgets (but doesn't save terminating '\n'). +// Reads byte-by-byte. Useful when it is important to not read ahead. +// Bytes are appended to pfx (which must be malloced, or NULL). +char* FAST_FUNC xmalloc_reads(int fd, char *buf, size_t *maxsz_p) +{ + char *p; + size_t sz = buf ? strlen(buf) : 0; + size_t maxsz = maxsz_p ? *maxsz_p : MAXINT(size_t); + + goto jump_in; + while (sz < maxsz) { + if ((size_t)(p - buf) == sz) { + jump_in: + buf = xrealloc(buf, sz + 128); + p = buf + sz; + sz += 128; + } + /* nonblock_safe_read() because we are used by e.g. shells */ + if (nonblock_safe_read(fd, p, 1) != 1) { /* EOF/error */ + if (p == buf) { /* we read nothing */ + free(buf); + return NULL; + } + break; + } + if (*p == '\n') + break; + p++; + } + *p = '\0'; + if (maxsz_p) + *maxsz_p = p - buf; + p++; + return xrealloc(buf, p - buf); +} + +ssize_t FAST_FUNC read_close(int fd, void *buf, size_t size) +{ + /*int e;*/ + size = full_read(fd, buf, size); + /*e = errno;*/ + close(fd); + /*errno = e;*/ + return size; +} + +ssize_t FAST_FUNC open_read_close(const char *filename, void *buf, size_t size) +{ + int fd = open(filename, O_RDONLY); + if (fd < 0) + return fd; + return read_close(fd, buf, size); +} + + +// Read (potentially big) files in one go. File size is estimated +// by stat. Extra '\0' byte is appended. +void* FAST_FUNC xmalloc_read(int fd, size_t *maxsz_p) +{ + char *buf; + size_t size, rd_size, total; + size_t to_read; + struct stat st; + + to_read = maxsz_p ? *maxsz_p : MAXINT(ssize_t); /* max to read */ + + /* Estimate file size */ + st.st_size = 0; /* in case fstat fails, assume 0 */ + fstat(fd, &st); + /* /proc/N/stat files report st_size 0 */ + /* In order to make such files readable, we add small const */ + size = (st.st_size | 0x3ff) + 1; + + total = 0; + buf = NULL; + while (1) { + if (to_read < size) + size = to_read; + buf = xrealloc(buf, total + size + 1); + rd_size = full_read(fd, buf + total, size); + if ((ssize_t)rd_size == (ssize_t)(-1)) { /* error */ + free(buf); + return NULL; + } + total += rd_size; + if (rd_size < size) /* EOF */ + break; + if (to_read <= rd_size) + break; + to_read -= rd_size; + /* grow by 1/8, but in [1k..64k] bounds */ + size = ((total / 8) | 0x3ff) + 1; + if (size > 64*1024) + size = 64*1024; + } + xrealloc(buf, total + 1); + buf[total] = '\0'; + + if (maxsz_p) + *maxsz_p = total; + return buf; +} + +#ifdef USING_LSEEK_TO_GET_SIZE +/* Alternatively, file size can be obtained by lseek to the end. + * The code is slightly bigger. Retained in case fstat approach + * will not work for some weird cases (/proc, block devices, etc). + * (NB: lseek also can fail to work for some weird files) */ + +// Read (potentially big) files in one go. File size is estimated by +// lseek to end. +void* FAST_FUNC xmalloc_open_read_close(const char *filename, size_t *maxsz_p) +{ + char *buf; + size_t size; + int fd; + off_t len; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return NULL; + + /* /proc/N/stat files report len 0 here */ + /* In order to make such files readable, we add small const */ + size = 0x3ff; /* read only 1k on unseekable files */ + len = lseek(fd, 0, SEEK_END) | 0x3ff; /* + up to 1k */ + if (len != (off_t)-1) { + xlseek(fd, 0, SEEK_SET); + size = maxsz_p ? *maxsz_p : INT_MAX; + if (len < size) + size = len; + } + + buf = xmalloc(size + 1); + size = read_close(fd, buf, size); + if ((ssize_t)size < 0) { + free(buf); + return NULL; + } + xrealloc(buf, size + 1); + buf[size] = '\0'; + + if (maxsz_p) + *maxsz_p = size; + return buf; +} +#endif + +// Read (potentially big) files in one go. File size is estimated +// by stat. +void* FAST_FUNC xmalloc_open_read_close(const char *filename, size_t *maxsz_p) +{ + char *buf; + int fd; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return NULL; + + buf = xmalloc_read(fd, maxsz_p); + close(fd); + return buf; +} + +void* FAST_FUNC xmalloc_xopen_read_close(const char *filename, size_t *maxsz_p) +{ + void *buf = xmalloc_open_read_close(filename, maxsz_p); + if (!buf) + bb_perror_msg_and_die("can't read '%s'", filename); + return buf; +} + +int FAST_FUNC open_zipped(const char *fname) +{ +#if !ZIPPED + return open(fname, O_RDONLY); +#else + unsigned char magic[2]; + char *sfx; + int fd; +#if BB_MMU + USE_DESKTOP(long long) int FAST_FUNC (*xformer)(int src_fd, int dst_fd); + enum { xformer_prog = 0 }; +#else + enum { xformer = 0 }; + const char *xformer_prog; +#endif + + fd = open(fname, O_RDONLY); + if (fd < 0) + return fd; + + sfx = strrchr(fname, '.'); + if (sfx) { + if (ENABLE_FEATURE_SEAMLESS_LZMA && strcmp(sfx, ".lzma") == 0) + /* .lzma has no header/signature, just trust it */ + open_transformer(fd, unpack_lzma_stream, "unlzma"); + else + if ((ENABLE_FEATURE_SEAMLESS_GZ && strcmp(sfx, ".gz") == 0) + || (ENABLE_FEATURE_SEAMLESS_BZ2 && strcmp(sfx, ".bz2") == 0) + ) { + /* .gz and .bz2 both have 2-byte signature, and their + * unpack_XXX_stream want this header skipped. */ + xread(fd, &magic, 2); +#if ENABLE_FEATURE_SEAMLESS_GZ +#if BB_MMU + xformer = unpack_gz_stream; +#else + xformer_prog = "gunzip"; +#endif +#endif + if (!ENABLE_FEATURE_SEAMLESS_GZ + || magic[0] != 0x1f || magic[1] != 0x8b + ) { + if (!ENABLE_FEATURE_SEAMLESS_BZ2 + || magic[0] != 'B' || magic[1] != 'Z' + ) { + bb_error_msg_and_die("no gzip" + USE_FEATURE_SEAMLESS_BZ2("/bzip2") + " magic"); + } +#if BB_MMU + xformer = unpack_bz2_stream; +#else + xformer_prog = "bunzip2"; +#endif + } else { +#if !BB_MMU + /* NOMMU version of open_transformer execs + * an external unzipper that wants + * file position at the start of the file */ + xlseek(fd, 0, SEEK_SET); +#endif + } + open_transformer(fd, xformer, xformer_prog); + } + } + + return fd; +#endif +} + +void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p) +{ + int fd; + char *image; + + fd = open_zipped(fname); + if (fd < 0) + return NULL; + + image = xmalloc_read(fd, maxsz_p); + if (!image) + bb_perror_msg("read error from '%s'", fname); + close(fd); + + return image; +} diff --git a/libbb/read_key.c b/libbb/read_key.c new file mode 100644 index 0000000..0f36d20 --- /dev/null +++ b/libbb/read_key.c @@ -0,0 +1,157 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2008 Rob Landley + * Copyright (C) 2008 Denys Vlasenko + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ +#include "libbb.h" + +int FAST_FUNC read_key(int fd, smalluint *nbuffered, char *buffer) +{ + struct pollfd pfd; + const char *seq; + int n; + int c; + + /* Known escape sequences for cursor and function keys */ + static const char esccmds[] ALIGN1 = { + 'O','A' |0x80,KEYCODE_UP , + 'O','B' |0x80,KEYCODE_DOWN , + 'O','C' |0x80,KEYCODE_RIGHT , + 'O','D' |0x80,KEYCODE_LEFT , + 'O','H' |0x80,KEYCODE_HOME , + 'O','F' |0x80,KEYCODE_END , +#if 0 + 'O','P' |0x80,KEYCODE_FUN1 , + /* [ESC] ESC O [2] P - [Alt-][Shift-]F1 */ + /* Ctrl- seems to not affect sequences */ + 'O','Q' |0x80,KEYCODE_FUN2 , + 'O','R' |0x80,KEYCODE_FUN3 , + 'O','S' |0x80,KEYCODE_FUN4 , +#endif + '[','A' |0x80,KEYCODE_UP , + '[','B' |0x80,KEYCODE_DOWN , + '[','C' |0x80,KEYCODE_RIGHT , + '[','D' |0x80,KEYCODE_LEFT , + '[','H' |0x80,KEYCODE_HOME , /* xterm */ + /* [ESC] ESC [ [2] H - [Alt-][Shift-]Home */ + '[','F' |0x80,KEYCODE_END , /* xterm */ + '[','1','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ + '[','2','~' |0x80,KEYCODE_INSERT , + '[','3','~' |0x80,KEYCODE_DELETE , + /* [ESC] ESC [ 3 [;2] ~ - [Alt-][Shift-]Delete */ + '[','4','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ + '[','5','~' |0x80,KEYCODE_PAGEUP , + '[','6','~' |0x80,KEYCODE_PAGEDOWN, + '[','7','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ + '[','8','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ +#if 0 + '[','1','1','~'|0x80,KEYCODE_FUN1 , + '[','1','2','~'|0x80,KEYCODE_FUN2 , + '[','1','3','~'|0x80,KEYCODE_FUN3 , + '[','1','4','~'|0x80,KEYCODE_FUN4 , + '[','1','5','~'|0x80,KEYCODE_FUN5 , + /* [ESC] ESC [ 1 5 [;2] ~ - [Alt-][Shift-]F5 */ + '[','1','7','~'|0x80,KEYCODE_FUN6 , + '[','1','8','~'|0x80,KEYCODE_FUN7 , + '[','1','9','~'|0x80,KEYCODE_FUN8 , + '[','2','0','~'|0x80,KEYCODE_FUN9 , + '[','2','1','~'|0x80,KEYCODE_FUN10 , + '[','2','3','~'|0x80,KEYCODE_FUN11 , + '[','2','4','~'|0x80,KEYCODE_FUN12 , +#endif + 0 + }; + + n = 0; + if (nbuffered) + n = *nbuffered; + if (n == 0) { + /* If no data, block waiting for input. If we read more + * than the minimal ESC sequence size, the "n=0" below + * would instead have to figure out how much to keep, + * resulting in larger code. */ + n = safe_read(fd, buffer, 3); + if (n <= 0) + return -1; + } + + /* Grab character to return from buffer */ + c = (unsigned char)buffer[0]; + n--; + if (n) + memmove(buffer, buffer + 1, n); + + /* Only ESC starts ESC sequences */ + if (c != 27) + goto ret; + + /* Loop through known ESC sequences */ + pfd.fd = fd; + pfd.events = POLLIN; + seq = esccmds; + while (*seq != '\0') { + /* n - position in sequence we did not read yet */ + int i = 0; /* position in sequence to compare */ + + /* Loop through chars in this sequence */ + while (1) { + /* So far escape sequence matched up to [i-1] */ + if (n <= i) { + /* Need more chars, read another one if it wouldn't block. + * Note that escape sequences come in as a unit, + * so if we block for long it's not really an escape sequence. + * Timeout is needed to reconnect escape sequences + * split up by transmission over a serial console. */ + if (safe_poll(&pfd, 1, 50) == 0) { + /* No more data! + * Array is sorted from shortest to longest, + * we can't match anything later in array, + * break out of both loops. */ + goto ret; + } + errno = 0; + if (safe_read(fd, buffer + n, 1) <= 0) { + /* If EAGAIN, then fd is O_NONBLOCK and poll lied: + * in fact, there is no data. */ + if (errno != EAGAIN) + c = -1; /* otherwise it's EOF/error */ + goto ret; + } + n++; + } + if (buffer[i] != (seq[i] & 0x7f)) { + /* This seq doesn't match, go to next */ + seq += i; + /* Forward to last char */ + while (!(*seq & 0x80)) + seq++; + /* Skip it and the keycode which follows */ + seq += 2; + break; + } + if (seq[i] & 0x80) { + /* Entire seq matched */ + c = (signed char)seq[i+1]; + n = 0; + /* n -= i; memmove(...); + * would be more correct, + * but we never read ahead that much, + * and n == i here. */ + goto ret; + } + i++; + } + } + /* We did not find matching sequence, it was a bare ESC. + * We possibly read and stored more input in buffer[] + * by now. */ + + ret: + if (nbuffered) + *nbuffered = n; + return c; +} diff --git a/libbb/recursive_action.c b/libbb/recursive_action.c new file mode 100644 index 0000000..3ec596a --- /dev/null +++ b/libbb/recursive_action.c @@ -0,0 +1,147 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +#undef DEBUG_RECURS_ACTION + +/* + * Walk down all the directories under the specified + * location, and do something (something specified + * by the fileAction and dirAction function pointers). + * + * Unfortunately, while nftw(3) could replace this and reduce + * code size a bit, nftw() wasn't supported before GNU libc 2.1, + * and so isn't sufficiently portable to take over since glibc2.1 + * is so stinking huge. + */ + +static int FAST_FUNC true_action(const char *fileName UNUSED_PARAM, + struct stat *statbuf UNUSED_PARAM, + void* userData UNUSED_PARAM, + int depth UNUSED_PARAM) +{ + return TRUE; +} + +/* fileAction return value of 0 on any file in directory will make + * recursive_action() return 0, but it doesn't stop directory traversal + * (fileAction/dirAction will be called on each file). + * + * If !ACTION_RECURSE, dirAction is called on the directory and its + * return value is returned from recursive_action(). No recursion. + * + * If ACTION_RECURSE, recursive_action() is called on each directory. + * If any one of these calls returns 0, current recursive_action() returns 0. + * + * If ACTION_DEPTHFIRST, dirAction is called after recurse. + * If it returns 0, the warning is printed and recursive_action() returns 0. + * + * If !ACTION_DEPTHFIRST, dirAction is called before we recurse. + * Return value of 0 (FALSE) or 2 (SKIP) prevents recursion + * into that directory, instead recursive_action() returns 0 (if FALSE) + * or 1 (if SKIP) + * + * followLinks=0/1 differs mainly in handling of links to dirs. + * 0: lstat(statbuf). Calls fileAction on link name even if points to dir. + * 1: stat(statbuf). Calls dirAction and optionally recurse on link to dir. + */ + +int FAST_FUNC recursive_action(const char *fileName, + unsigned flags, + int FAST_FUNC (*fileAction)(const char *fileName, struct stat *statbuf, void* userData, int depth), + int FAST_FUNC (*dirAction)(const char *fileName, struct stat *statbuf, void* userData, int depth), + void* userData, + unsigned depth) +{ + struct stat statbuf; + int status; + DIR *dir; + struct dirent *next; + + if (!fileAction) fileAction = true_action; + if (!dirAction) dirAction = true_action; + + status = ACTION_FOLLOWLINKS; /* hijack a variable for bitmask... */ + if (!depth) + status = ACTION_FOLLOWLINKS | ACTION_FOLLOWLINKS_L0; + status = ((flags & status) ? stat : lstat)(fileName, &statbuf); + if (status < 0) { +#ifdef DEBUG_RECURS_ACTION + bb_error_msg("status=%d flags=%x", status, flags); +#endif + goto done_nak_warn; + } + + /* If S_ISLNK(m), then we know that !S_ISDIR(m). + * Then we can skip checking first part: if it is true, then + * (!dir) is also true! */ + if ( /* (!(flags & ACTION_FOLLOWLINKS) && S_ISLNK(statbuf.st_mode)) || */ + !S_ISDIR(statbuf.st_mode) + ) { + return fileAction(fileName, &statbuf, userData, depth); + } + + /* It's a directory (or a link to one, and followLinks is set) */ + + if (!(flags & ACTION_RECURSE)) { + return dirAction(fileName, &statbuf, userData, depth); + } + + if (!(flags & ACTION_DEPTHFIRST)) { + status = dirAction(fileName, &statbuf, userData, depth); + if (!status) + goto done_nak_warn; + if (status == SKIP) + return TRUE; + } + + dir = opendir(fileName); + if (!dir) { + /* findutils-4.1.20 reports this */ + /* (i.e. it doesn't silently return with exit code 1) */ + /* To trigger: "find -exec rm -rf {} \;" */ + goto done_nak_warn; + } + status = TRUE; + while ((next = readdir(dir)) != NULL) { + char *nextFile; + + nextFile = concat_subpath_file(fileName, next->d_name); + if (nextFile == NULL) + continue; + /* process every file (NB: ACTION_RECURSE is set in flags) */ + if (!recursive_action(nextFile, flags, fileAction, dirAction, + userData, depth + 1)) + status = FALSE; +// s = recursive_action(nextFile, flags, fileAction, dirAction, +// userData, depth + 1); + free(nextFile); +//#define RECURSE_RESULT_ABORT 3 +// if (s == RECURSE_RESULT_ABORT) { +// closedir(dir); +// return s; +// } +// if (s == FALSE) +// status = FALSE; + } + closedir(dir); + + if (flags & ACTION_DEPTHFIRST) { + if (!dirAction(fileName, &statbuf, userData, depth)) + goto done_nak_warn; + } + + return status; + + done_nak_warn: + if (!(flags & ACTION_QUIET)) + bb_simple_perror_msg(fileName); + return FALSE; +} diff --git a/libbb/remove_file.c b/libbb/remove_file.c new file mode 100644 index 0000000..8b14f07 --- /dev/null +++ b/libbb/remove_file.c @@ -0,0 +1,102 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini remove_file implementation for busybox + * + * Copyright (C) 2001 Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* Used from NOFORK applets. Must not allocate anything */ + +int FAST_FUNC remove_file(const char *path, int flags) +{ + struct stat path_stat; + + if (lstat(path, &path_stat) < 0) { + if (errno != ENOENT) { + bb_perror_msg("cannot stat '%s'", path); + return -1; + } + if (!(flags & FILEUTILS_FORCE)) { + bb_perror_msg("cannot remove '%s'", path); + return -1; + } + return 0; + } + + if (S_ISDIR(path_stat.st_mode)) { + DIR *dp; + struct dirent *d; + int status = 0; + + if (!(flags & FILEUTILS_RECUR)) { + bb_error_msg("%s: is a directory", path); + return -1; + } + + if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 && isatty(0)) + || (flags & FILEUTILS_INTERACTIVE) + ) { + fprintf(stderr, "%s: descend into directory '%s'? ", applet_name, + path); + if (!bb_ask_confirmation()) + return 0; + } + + dp = opendir(path); + if (dp == NULL) { + return -1; + } + + while ((d = readdir(dp)) != NULL) { + char *new_path; + + new_path = concat_subpath_file(path, d->d_name); + if (new_path == NULL) + continue; + if (remove_file(new_path, flags) < 0) + status = -1; + free(new_path); + } + + if (closedir(dp) < 0) { + bb_perror_msg("cannot close '%s'", path); + return -1; + } + + if (flags & FILEUTILS_INTERACTIVE) { + fprintf(stderr, "%s: remove directory '%s'? ", applet_name, path); + if (!bb_ask_confirmation()) + return status; + } + + if (rmdir(path) < 0) { + bb_perror_msg("cannot remove '%s'", path); + return -1; + } + + return status; + } + + /* !ISDIR */ + if ((!(flags & FILEUTILS_FORCE) + && access(path, W_OK) < 0 + && !S_ISLNK(path_stat.st_mode) + && isatty(0)) + || (flags & FILEUTILS_INTERACTIVE) + ) { + fprintf(stderr, "%s: remove '%s'? ", applet_name, path); + if (!bb_ask_confirmation()) + return 0; + } + + if (unlink(path) < 0) { + bb_perror_msg("cannot remove '%s'", path); + return -1; + } + + return 0; +} diff --git a/libbb/restricted_shell.c b/libbb/restricted_shell.c new file mode 100644 index 0000000..2a5073f --- /dev/null +++ b/libbb/restricted_shell.c @@ -0,0 +1,46 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 1989 - 1991, Julianne Frances Haugh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libbb.h" + +/* Return 1 if SHELL is a restricted shell (one not returned by + getusershell), else 0, meaning it is a standard shell. */ +int FAST_FUNC restricted_shell(const char *shell) +{ + char *line; + + setusershell(); + while ((line = getusershell())) { + if (*line != '#' && strcmp(line, shell) == 0) + return 0; + } + endusershell(); + return 1; +} diff --git a/libbb/rtc.c b/libbb/rtc.c new file mode 100644 index 0000000..222d977 --- /dev/null +++ b/libbb/rtc.c @@ -0,0 +1,88 @@ +/* + * Common RTC functions + */ + +#include "libbb.h" +#include "rtc_.h" + +#if ENABLE_FEATURE_HWCLOCK_ADJTIME_FHS +# define ADJTIME_PATH "/var/lib/hwclock/adjtime" +#else +# define ADJTIME_PATH "/etc/adjtime" +#endif + +int FAST_FUNC rtc_adjtime_is_utc(void) +{ + int utc = 0; + FILE *f = fopen_for_read(ADJTIME_PATH); + + if (f) { + RESERVE_CONFIG_BUFFER(buffer, 128); + + while (fgets(buffer, sizeof(buffer), f)) { + int len = strlen(buffer); + + while (len && isspace(buffer[len - 1])) + len--; + + buffer[len] = 0; + + if (strncmp(buffer, "UTC", 3) == 0) { + utc = 1; + break; + } + } + fclose(f); + + RELEASE_CONFIG_BUFFER(buffer); + } + + return utc; +} + +int FAST_FUNC rtc_xopen(const char **default_rtc, int flags) +{ + int rtc; + + if (!*default_rtc) { + *default_rtc = "/dev/rtc"; + rtc = open(*default_rtc, flags); + if (rtc >= 0) + return rtc; + *default_rtc = "/dev/rtc0"; + rtc = open(*default_rtc, flags); + if (rtc >= 0) + return rtc; + *default_rtc = "/dev/misc/rtc"; + } + + return xopen(*default_rtc, flags); +} + +time_t FAST_FUNC rtc_read_time(int fd, int utc) +{ + struct tm tm; + char *oldtz = 0; + time_t t = 0; + + memset(&tm, 0, sizeof(struct tm)); + xioctl(fd, RTC_RD_TIME, &tm); + tm.tm_isdst = -1; /* not known */ + + if (utc) { + oldtz = getenv("TZ"); + putenv((char*)"TZ=UTC0"); + tzset(); + } + + t = mktime(&tm); + + if (utc) { + unsetenv("TZ"); + if (oldtz) + putenv(oldtz - 3); + tzset(); + } + + return t; +} diff --git a/libbb/run_shell.c b/libbb/run_shell.c new file mode 100644 index 0000000..2ccb3a1 --- /dev/null +++ b/libbb/run_shell.c @@ -0,0 +1,90 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 1989 - 1991, Julianne Frances Haugh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libbb.h" +#if ENABLE_SELINUX +#include /* for setexeccon */ +#endif + +#if ENABLE_SELINUX +static security_context_t current_sid; + +void FAST_FUNC renew_current_security_context(void) +{ + freecon(current_sid); /* Release old context */ + getcon(¤t_sid); /* update */ +} +void FAST_FUNC set_current_security_context(security_context_t sid) +{ + freecon(current_sid); /* Release old context */ + current_sid = sid; +} + +#endif + +/* Run SHELL, or DEFAULT_SHELL if SHELL is empty. + If COMMAND is nonzero, pass it to the shell with the -c option. + If ADDITIONAL_ARGS is nonzero, pass it to the shell as more + arguments. */ + +void FAST_FUNC run_shell(const char *shell, int loginshell, const char *command, const char **additional_args) +{ + const char **args; + int argno = 1; + int additional_args_cnt = 0; + + for (args = additional_args; args && *args; args++) + additional_args_cnt++; + + args = xmalloc(sizeof(char*) * (4 + additional_args_cnt)); + + args[0] = bb_get_last_path_component_nostrip(xstrdup(shell)); + + if (loginshell) + args[0] = xasprintf("-%s", args[0]); + + if (command) { + args[argno++] = "-c"; + args[argno++] = command; + } + if (additional_args) { + for (; *additional_args; ++additional_args) + args[argno++] = *additional_args; + } + args[argno] = NULL; +#if ENABLE_SELINUX + if (current_sid) + setexeccon(current_sid); + if (ENABLE_FEATURE_CLEAN_UP) + freecon(current_sid); +#endif + execv(shell, (char **) args); + bb_perror_msg_and_die("cannot run %s", shell); +} diff --git a/libbb/safe_gethostname.c b/libbb/safe_gethostname.c new file mode 100644 index 0000000..7407fb7 --- /dev/null +++ b/libbb/safe_gethostname.c @@ -0,0 +1,66 @@ +/* vi: set sw=4 ts=4: */ +/* + * Safe gethostname implementation for busybox + * + * Copyright (C) 2008 Tito Ragusa + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* + * SUSv2 guarantees that "Host names are limited to 255 bytes" + * POSIX.1-2001 guarantees that "Host names (not including the terminating + * null byte) are limited to HOST_NAME_MAX bytes" (64 bytes on my box). + * + * RFC1123 says: + * + * The syntax of a legal Internet host name was specified in RFC-952 + * [DNS:4]. One aspect of host name syntax is hereby changed: the + * restriction on the first character is relaxed to allow either a + * letter or a digit. Host software MUST support this more liberal + * syntax. + * + * Host software MUST handle host names of up to 63 characters and + * SHOULD handle host names of up to 255 characters. + */ + +#include "libbb.h" +#include + +/* + * On success return the current malloced and NUL terminated hostname. + * On error return malloced and NUL terminated string "?". + * This is an illegal first character for a hostname. + * The returned malloced string must be freed by the caller. + */ +char* FAST_FUNC safe_gethostname(void) +{ + struct utsname uts; + + /* The length of the arrays in a struct utsname is unspecified; + * the fields are terminated by a null byte. + * Note that there is no standard that says that the hostname + * set by sethostname(2) is the same string as the nodename field of the + * struct returned by uname (indeed, some systems allow a 256-byte host- + * name and an 8-byte nodename), but this is true on Linux. The same holds + * for setdomainname(2) and the domainname field. + */ + + /* Uname can fail only if you pass a bad pointer to it. */ + uname(&uts); + return xstrndup(!uts.nodename[0] ? "?" : uts.nodename, sizeof(uts.nodename)); +} + +/* + * On success return the current malloced and NUL terminated domainname. + * On error return malloced and NUL terminated string "?". + * This is an illegal first character for a domainname. + * The returned malloced string must be freed by the caller. + */ +char* FAST_FUNC safe_getdomainname(void) +{ + struct utsname uts; + + uname(&uts); + return xstrndup(!uts.domainname[0] ? "?" : uts.domainname, sizeof(uts.domainname)); +} diff --git a/libbb/safe_poll.c b/libbb/safe_poll.c new file mode 100644 index 0000000..58c7bda --- /dev/null +++ b/libbb/safe_poll.c @@ -0,0 +1,34 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2007 by Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* Wrapper which restarts poll on EINTR or ENOMEM. + * On other errors does perror("poll") and returns. + * Warning! May take longer than timeout_ms to return! */ +int FAST_FUNC safe_poll(struct pollfd *ufds, nfds_t nfds, int timeout) +{ + while (1) { + int n = poll(ufds, nfds, timeout); + if (n >= 0) + return n; + /* Make sure we inch towards completion */ + if (timeout > 0) + timeout--; + /* E.g. strace causes poll to return this */ + if (errno == EINTR) + continue; + /* Kernel is very low on memory. Retry. */ + /* I doubt many callers would handle this correctly! */ + if (errno == ENOMEM) + continue; + bb_perror_msg("poll"); + return n; + } +} diff --git a/libbb/safe_strncpy.c b/libbb/safe_strncpy.c new file mode 100644 index 0000000..4acd976 --- /dev/null +++ b/libbb/safe_strncpy.c @@ -0,0 +1,27 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* Like strncpy but make sure the resulting string is always 0 terminated. */ +char* FAST_FUNC safe_strncpy(char *dst, const char *src, size_t size) +{ + if (!size) return dst; + dst[--size] = '\0'; + return strncpy(dst, src, size); +} + +/* Like strcpy but can copy overlapping strings. */ +void FAST_FUNC overlapping_strcpy(char *dst, const char *src) +{ + while ((*dst = *src) != '\0') { + dst++; + src++; + } +} diff --git a/libbb/safe_write.c b/libbb/safe_write.c new file mode 100644 index 0000000..e3561f3 --- /dev/null +++ b/libbb/safe_write.c @@ -0,0 +1,21 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +ssize_t FAST_FUNC safe_write(int fd, const void *buf, size_t count) +{ + ssize_t n; + + do { + n = write(fd, buf, count); + } while (n < 0 && errno == EINTR); + + return n; +} diff --git a/libbb/selinux_common.c b/libbb/selinux_common.c new file mode 100644 index 0000000..5fdbe9d --- /dev/null +++ b/libbb/selinux_common.c @@ -0,0 +1,54 @@ +/* + * libbb/selinux_common.c + * -- common SELinux utility functions + * + * Copyright 2007 KaiGai Kohei + */ +#include "libbb.h" +#include + +context_t FAST_FUNC set_security_context_component(security_context_t cur_context, + char *user, char *role, char *type, char *range) +{ + context_t con = context_new(cur_context); + if (!con) + return NULL; + + if (user && context_user_set(con, user)) + goto error; + if (type && context_type_set(con, type)) + goto error; + if (range && context_range_set(con, range)) + goto error; + if (role && context_role_set(con, role)) + goto error; + return con; + +error: + context_free(con); + return NULL; +} + +void FAST_FUNC setfscreatecon_or_die(security_context_t scontext) +{ + if (setfscreatecon(scontext) < 0) { + /* Can be NULL. All known printf implementations + * display "(null)", "" etc */ + bb_perror_msg_and_die("cannot set default " + "file creation context to %s", scontext); + } +} + +void FAST_FUNC selinux_preserve_fcontext(int fdesc) +{ + security_context_t context; + + if (fgetfilecon(fdesc, &context) < 0) { + if (errno == ENODATA || errno == ENOTSUP) + return; + bb_perror_msg_and_die("fgetfilecon failed"); + } + setfscreatecon_or_die(context); + freecon(context); +} + diff --git a/libbb/setup_environment.c b/libbb/setup_environment.c new file mode 100644 index 0000000..78318ce --- /dev/null +++ b/libbb/setup_environment.c @@ -0,0 +1,70 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 1989 - 1991, Julianne Frances Haugh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libbb.h" + +void FAST_FUNC setup_environment(const char *shell, int clear_env, int change_env, const struct passwd *pw) +{ + /* Change the current working directory to be the home directory + * of the user */ + if (chdir(pw->pw_dir)) { + xchdir("/"); + bb_error_msg("can't chdir to home directory '%s'", pw->pw_dir); + } + + if (clear_env) { + const char *term; + + /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. + Unset all other environment variables. */ + term = getenv("TERM"); + clearenv(); + if (term) + xsetenv("TERM", term); + xsetenv("PATH", (pw->pw_uid ? bb_default_path : bb_default_root_path)); + goto shortcut; + // No, gcc (4.2.1) is not clever enougn to do it itself. + //xsetenv("USER", pw->pw_name); + //xsetenv("LOGNAME", pw->pw_name); + //xsetenv("HOME", pw->pw_dir); + //xsetenv("SHELL", shell); + } + else if (change_env) { + /* Set HOME, SHELL, and if not becoming a super-user, + USER and LOGNAME. */ + if (pw->pw_uid) { + shortcut: + xsetenv("USER", pw->pw_name); + xsetenv("LOGNAME", pw->pw_name); + } + xsetenv("HOME", pw->pw_dir); + xsetenv("SHELL", shell); + } +} diff --git a/libbb/sha1.c b/libbb/sha1.c new file mode 100644 index 0000000..cc7edd8 --- /dev/null +++ b/libbb/sha1.c @@ -0,0 +1,170 @@ +/* vi: set sw=4 ts=4: */ +/* + * Based on shasum from http://www.netsw.org/crypto/hash/ + * Majorly hacked up to use Dr Brian Gladman's sha1 code + * + * Copyright (C) 2002 Dr Brian Gladman , Worcester, UK. + * Copyright (C) 2003 Glenn L. McGrath + * Copyright (C) 2003 Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + * --------------------------------------------------------------------------- + * Issue Date: 10/11/2002 + * + * This is a byte oriented version of SHA1 that operates on arrays of bytes + * stored in memory. It runs at 22 cycles per byte on a Pentium P4 processor + */ + +#include "libbb.h" + +#define SHA1_BLOCK_SIZE 64 +#define SHA1_DIGEST_SIZE 20 +#define SHA1_HASH_SIZE SHA1_DIGEST_SIZE +#define SHA2_GOOD 0 +#define SHA2_BAD 1 + +#define rotl32(x,n) (((x) << n) | ((x) >> (32 - n))) + +#define SHA1_MASK (SHA1_BLOCK_SIZE - 1) + +/* reverse byte order in 32-bit words */ +#define ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) +#define parity(x,y,z) ((x) ^ (y) ^ (z)) +#define maj(x,y,z) (((x) & (y)) | ((z) & ((x) | (y)))) + +/* A normal version as set out in the FIPS. This version uses */ +/* partial loop unrolling and is optimised for the Pentium 4 */ +#define rnd(f,k) \ + do { \ + t = a; a = rotl32(a,5) + f(b,c,d) + e + k + w[i]; \ + e = d; d = c; c = rotl32(b, 30); b = t; \ + } while (0) + +static void sha1_compile(sha1_ctx_t *ctx) +{ + uint32_t w[80], i, a, b, c, d, e, t; + + /* note that words are compiled from the buffer into 32-bit */ + /* words in big-endian order so an order reversal is needed */ + /* here on little endian machines */ + for (i = 0; i < SHA1_BLOCK_SIZE / 4; ++i) + w[i] = htonl(ctx->wbuf[i]); + + for (i = SHA1_BLOCK_SIZE / 4; i < 80; ++i) + w[i] = rotl32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); + + a = ctx->hash[0]; + b = ctx->hash[1]; + c = ctx->hash[2]; + d = ctx->hash[3]; + e = ctx->hash[4]; + + for (i = 0; i < 20; ++i) { + rnd(ch, 0x5a827999); + } + + for (i = 20; i < 40; ++i) { + rnd(parity, 0x6ed9eba1); + } + + for (i = 40; i < 60; ++i) { + rnd(maj, 0x8f1bbcdc); + } + + for (i = 60; i < 80; ++i) { + rnd(parity, 0xca62c1d6); + } + + ctx->hash[0] += a; + ctx->hash[1] += b; + ctx->hash[2] += c; + ctx->hash[3] += d; + ctx->hash[4] += e; +} + +void FAST_FUNC sha1_begin(sha1_ctx_t *ctx) +{ + ctx->count[0] = ctx->count[1] = 0; + ctx->hash[0] = 0x67452301; + ctx->hash[1] = 0xefcdab89; + ctx->hash[2] = 0x98badcfe; + ctx->hash[3] = 0x10325476; + ctx->hash[4] = 0xc3d2e1f0; +} + +/* SHA1 hash data in an array of bytes into hash buffer and call the */ +/* hash_compile function as required. */ +void FAST_FUNC sha1_hash(const void *data, size_t length, sha1_ctx_t *ctx) +{ + uint32_t pos = (uint32_t) (ctx->count[0] & SHA1_MASK); + uint32_t freeb = SHA1_BLOCK_SIZE - pos; + const unsigned char *sp = data; + + if ((ctx->count[0] += length) < length) + ++(ctx->count[1]); + + while (length >= freeb) { /* tranfer whole blocks while possible */ + memcpy(((unsigned char *) ctx->wbuf) + pos, sp, freeb); + sp += freeb; + length -= freeb; + freeb = SHA1_BLOCK_SIZE; + pos = 0; + sha1_compile(ctx); + } + + memcpy(((unsigned char *) ctx->wbuf) + pos, sp, length); +} + +void* FAST_FUNC sha1_end(void *resbuf, sha1_ctx_t *ctx) +{ + /* SHA1 Final padding and digest calculation */ +#if BB_BIG_ENDIAN + static uint32_t mask[4] = { 0x00000000, 0xff000000, 0xffff0000, 0xffffff00 }; + static uint32_t bits[4] = { 0x80000000, 0x00800000, 0x00008000, 0x00000080 }; +#else + static uint32_t mask[4] = { 0x00000000, 0x000000ff, 0x0000ffff, 0x00ffffff }; + static uint32_t bits[4] = { 0x00000080, 0x00008000, 0x00800000, 0x80000000 }; +#endif + + uint8_t *hval = resbuf; + uint32_t i, cnt = (uint32_t) (ctx->count[0] & SHA1_MASK); + + /* mask out the rest of any partial 32-bit word and then set */ + /* the next byte to 0x80. On big-endian machines any bytes in */ + /* the buffer will be at the top end of 32 bit words, on little */ + /* endian machines they will be at the bottom. Hence the AND */ + /* and OR masks above are reversed for little endian systems */ + ctx->wbuf[cnt >> 2] = + (ctx->wbuf[cnt >> 2] & mask[cnt & 3]) | bits[cnt & 3]; + + /* we need 9 or more empty positions, one for the padding byte */ + /* (above) and eight for the length count. If there is not */ + /* enough space pad and empty the buffer */ + if (cnt > SHA1_BLOCK_SIZE - 9) { + if (cnt < 60) + ctx->wbuf[15] = 0; + sha1_compile(ctx); + cnt = 0; + } else /* compute a word index for the empty buffer positions */ + cnt = (cnt >> 2) + 1; + + while (cnt < 14) /* and zero pad all but last two positions */ + ctx->wbuf[cnt++] = 0; + + /* assemble the eight byte counter in the buffer in big-endian */ + /* format */ + + ctx->wbuf[14] = htonl((ctx->count[1] << 3) | (ctx->count[0] >> 29)); + ctx->wbuf[15] = htonl(ctx->count[0] << 3); + + sha1_compile(ctx); + + /* extract the hash value as bytes in case the hash buffer is */ + /* misaligned for 32-bit words */ + + for (i = 0; i < SHA1_DIGEST_SIZE; ++i) + hval[i] = (unsigned char) (ctx->hash[i >> 2] >> 8 * (~i & 3)); + + return resbuf; +} diff --git a/libbb/signals.c b/libbb/signals.c new file mode 100644 index 0000000..f56ce65 --- /dev/null +++ b/libbb/signals.c @@ -0,0 +1,121 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * Copyright (C) 2006 Rob Landley + * Copyright (C) 2006 Denys Vlasenko + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* All known arches use small ints for signals */ +smallint bb_got_signal; + +void record_signo(int signo) +{ + bb_got_signal = signo; +} + +/* Saves 2 bytes on x86! Oh my... */ +int FAST_FUNC sigaction_set(int signum, const struct sigaction *act) +{ + return sigaction(signum, act, NULL); +} + +int FAST_FUNC sigprocmask_allsigs(int how) +{ + sigset_t set; + sigfillset(&set); + return sigprocmask(how, &set, NULL); +} + +void FAST_FUNC bb_signals(int sigs, void (*f)(int)) +{ + int sig_no = 0; + int bit = 1; + + while (sigs) { + if (sigs & bit) { + sigs &= ~bit; + signal(sig_no, f); + } + sig_no++; + bit <<= 1; + } +} + +void FAST_FUNC bb_signals_recursive(int sigs, void (*f)(int)) +{ + int sig_no = 0; + int bit = 1; + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = f; + /*sa.sa_flags = 0;*/ + /*sigemptyset(&sa.sa_mask); - hope memset did it*/ + + while (sigs) { + if (sigs & bit) { + sigs &= ~bit; + sigaction_set(sig_no, &sa); + } + sig_no++; + bit <<= 1; + } +} + +void FAST_FUNC sig_block(int sig) +{ + sigset_t ss; + sigemptyset(&ss); + sigaddset(&ss, sig); + sigprocmask(SIG_BLOCK, &ss, NULL); +} + +void FAST_FUNC sig_unblock(int sig) +{ + sigset_t ss; + sigemptyset(&ss); + sigaddset(&ss, sig); + sigprocmask(SIG_UNBLOCK, &ss, NULL); +} + +void FAST_FUNC wait_for_any_sig(void) +{ + sigset_t ss; + sigemptyset(&ss); + sigsuspend(&ss); +} + +/* Assuming the sig is fatal */ +void FAST_FUNC kill_myself_with_sig(int sig) +{ + signal(sig, SIG_DFL); + sig_unblock(sig); + raise(sig); + _exit(EXIT_FAILURE); /* Should not reach it */ +} + +void FAST_FUNC signal_SA_RESTART_empty_mask(int sig, void (*handler)(int)) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + /*sigemptyset(&sa.sa_mask);*/ + sa.sa_flags = SA_RESTART; + sa.sa_handler = handler; + sigaction_set(sig, &sa); +} + +void FAST_FUNC signal_no_SA_RESTART_empty_mask(int sig, void (*handler)(int)) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + /*sigemptyset(&sa.sa_mask);*/ + /*sa.sa_flags = 0;*/ + sa.sa_handler = handler; + sigaction_set(sig, &sa); +} diff --git a/libbb/simplify_path.c b/libbb/simplify_path.c new file mode 100644 index 0000000..367f1f0 --- /dev/null +++ b/libbb/simplify_path.c @@ -0,0 +1,53 @@ +/* vi: set sw=4 ts=4: */ +/* + * bb_simplify_path implementation for busybox + * + * Copyright (C) 2001 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +char* FAST_FUNC bb_simplify_path(const char *path) +{ + char *s, *start, *p; + + if (path[0] == '/') + start = xstrdup(path); + else { + s = xrealloc_getcwd_or_warn(NULL); + start = concat_path_file(s, path); + free(s); + } + p = s = start; + + do { + if (*p == '/') { + if (*s == '/') { /* skip duplicate (or initial) slash */ + continue; + } + if (*s == '.') { + if (s[1] == '/' || !s[1]) { /* remove extra '.' */ + continue; + } + if ((s[1] == '.') && (s[2] == '/' || !s[2])) { + ++s; + if (p > start) { + while (*--p != '/') /* omit previous dir */ + continue; + } + continue; + } + } + } + *++p = *s; + } while (*++s); + + if ((p == start) || (*p != '/')) { /* not a trailing slash */ + ++p; /* so keep last character */ + } + *p = 0; + + return start; +} diff --git a/libbb/skip_whitespace.c b/libbb/skip_whitespace.c new file mode 100644 index 0000000..e85f385 --- /dev/null +++ b/libbb/skip_whitespace.c @@ -0,0 +1,25 @@ +/* vi: set sw=4 ts=4: */ +/* + * skip_whitespace implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +char* FAST_FUNC skip_whitespace(const char *s) +{ + /* NB: isspace('\0') returns 0 */ + while (isspace(*s)) ++s; + + return (char *) s; +} + +char* FAST_FUNC skip_non_whitespace(const char *s) +{ + while (*s && !isspace(*s)) ++s; + + return (char *) s; +} diff --git a/libbb/speed_table.c b/libbb/speed_table.c new file mode 100644 index 0000000..646f914 --- /dev/null +++ b/libbb/speed_table.c @@ -0,0 +1,117 @@ +/* vi: set sw=4 ts=4: */ +/* + * compact speed_t <-> speed functions for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include +#include "libbb.h" + +struct speed_map { + unsigned short speed; + unsigned short value; +}; + +static const struct speed_map speeds[] = { + {B0, 0}, + {B50, 50}, + {B75, 75}, + {B110, 110}, + {B134, 134}, + {B150, 150}, + {B200, 200}, + {B300, 300}, + {B600, 600}, + {B1200, 1200}, + {B1800, 1800}, + {B2400, 2400}, + {B4800, 4800}, + {B9600, 9600}, +#ifdef B19200 + {B19200, 19200}, +#elif defined(EXTA) + {EXTA, 19200}, +#endif +#ifdef B38400 + {B38400, 38400/256 + 0x8000U}, +#elif defined(EXTB) + {EXTB, 38400/256 + 0x8000U}, +#endif +#ifdef B57600 + {B57600, 57600/256 + 0x8000U}, +#endif +#ifdef B115200 + {B115200, 115200/256 + 0x8000U}, +#endif +#ifdef B230400 + {B230400, 230400/256 + 0x8000U}, +#endif +#ifdef B460800 + {B460800, 460800/256 + 0x8000U}, +#endif +}; + +enum { NUM_SPEEDS = ARRAY_SIZE(speeds) }; + +unsigned FAST_FUNC tty_baud_to_value(speed_t speed) +{ + int i = 0; + + do { + if (speed == speeds[i].speed) { + if (speeds[i].value & 0x8000U) { + return ((unsigned long) (speeds[i].value) & 0x7fffU) * 256; + } + return speeds[i].value; + } + } while (++i < NUM_SPEEDS); + + return 0; +} + +speed_t FAST_FUNC tty_value_to_baud(unsigned int value) +{ + int i = 0; + + do { + if (value == tty_baud_to_value(speeds[i].speed)) { + return speeds[i].speed; + } + } while (++i < NUM_SPEEDS); + + return (speed_t) - 1; +} + +#if 0 +/* testing code */ +#include + +int main(void) +{ + unsigned long v; + speed_t s; + + for (v = 0 ; v < 500000; v++) { + s = tty_value_to_baud(v); + if (s == (speed_t) -1) { + continue; + } + printf("v = %lu -- s = %0lo\n", v, (unsigned long) s); + } + + printf("-------------------------------\n"); + + for (s = 0 ; s < 010017+1; s++) { + v = tty_baud_to_value(s); + if (!v) { + continue; + } + printf("v = %lu -- s = %0lo\n", v, (unsigned long) s); + } + + return 0; +} +#endif diff --git a/libbb/str_tolower.c b/libbb/str_tolower.c new file mode 100644 index 0000000..f402e8e --- /dev/null +++ b/libbb/str_tolower.c @@ -0,0 +1,14 @@ +/* vi set: sw=4 ts=4: */ +/* Convert string str to lowercase, return str. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ +#include "libbb.h" + +char* FAST_FUNC str_tolower(char *str) +{ + char *c; + for (c = str; *c; ++c) + *c = tolower(*c); + return str; +} diff --git a/libbb/strrstr.c b/libbb/strrstr.c new file mode 100644 index 0000000..a803dd9 --- /dev/null +++ b/libbb/strrstr.c @@ -0,0 +1,71 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2008 Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file License in this tarball for details. + */ + +#ifdef __DO_STRRSTR_TEST +#include +#include +#include +#else +#include "libbb.h" +#endif + +/* + * The strrstr() function finds the last occurrence of the substring needle + * in the string haystack. The terminating nul characters are not compared. + */ +char* FAST_FUNC strrstr(const char *haystack, const char *needle) +{ + char *r = NULL; + + if (!needle[0]) + return (char*)haystack + strlen(haystack); + while (1) { + char *p = strstr(haystack, needle); + if (!p) + return r; + r = p; + haystack = p + 1; + } +} + +#ifdef __DO_STRRSTR_TEST +int main(int argc, char **argv) +{ + static const struct { + const char *h, *n; + int pos; + } test_array[] = { + /* 0123456789 */ + { "baaabaaab", "aaa", 5 }, + { "baaabaaaab", "aaa", 6 }, + { "baaabaab", "aaa", 1 }, + { "aaa", "aaa", 0 }, + { "aaa", "a", 2 }, + { "aaa", "bbb", -1 }, + { "a", "aaa", -1 }, + { "aaa", "", 3 }, + { "", "aaa", -1 }, + { "", "", 0 }, + }; + + int i; + + i = 0; + while (i < sizeof(test_array) / sizeof(test_array[0])) { + const char *r = strrstr(test_array[i].h, test_array[i].n); + printf("'%s' vs. '%s': '%s' - ", test_array[i].h, test_array[i].n, r); + if (r == NULL) + r = test_array[i].h - 1; + printf("%s\n", r == test_array[i].h + test_array[i].pos ? "PASSED" : "FAILED"); + i++; + } + + return 0; +} +#endif diff --git a/libbb/time.c b/libbb/time.c new file mode 100644 index 0000000..850ac15 --- /dev/null +++ b/libbb/time.c @@ -0,0 +1,66 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2007 Denys Vlasenko + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +#if ENABLE_MONOTONIC_SYSCALL + +#include +/* Old glibc (< 2.3.4) does not provide this constant. We use syscall + * directly so this definition is safe. */ +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC 1 +#endif + +/* libc has incredibly messy way of doing this, + * typically requiring -lrt. We just skip all this mess */ +static void get_mono(struct timespec *ts) +{ + if (syscall(__NR_clock_gettime, CLOCK_MONOTONIC, ts)) + bb_error_msg_and_die("clock_gettime(MONOTONIC) failed"); +} +unsigned long long FAST_FUNC monotonic_ns(void) +{ + struct timespec ts; + get_mono(&ts); + return ts.tv_sec * 1000000000ULL + ts.tv_nsec; +} +unsigned long long FAST_FUNC monotonic_us(void) +{ + struct timespec ts; + get_mono(&ts); + return ts.tv_sec * 1000000ULL + ts.tv_nsec/1000; +} +unsigned FAST_FUNC monotonic_sec(void) +{ + struct timespec ts; + get_mono(&ts); + return ts.tv_sec; +} + +#else + +unsigned long long FAST_FUNC monotonic_ns(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000; +} +unsigned long long FAST_FUNC monotonic_us(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000ULL + tv.tv_usec; +} +unsigned FAST_FUNC monotonic_sec(void) +{ + return time(NULL); +} + +#endif diff --git a/libbb/trim.c b/libbb/trim.c new file mode 100644 index 0000000..ea20ff3 --- /dev/null +++ b/libbb/trim.c @@ -0,0 +1,31 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. + * If you wrote this, please acknowledge your work. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +void FAST_FUNC trim(char *s) +{ + size_t len = strlen(s); + size_t lws; + + /* trim trailing whitespace */ + while (len && isspace(s[len-1])) + --len; + + /* trim leading whitespace */ + if (len) { + lws = strspn(s, " \n\r\t\v"); + if (lws) { + len -= lws; + memmove(s, s + lws, len); + } + } + s[len] = '\0'; +} diff --git a/libbb/u_signal_names.c b/libbb/u_signal_names.c new file mode 100644 index 0000000..915eea5 --- /dev/null +++ b/libbb/u_signal_names.c @@ -0,0 +1,180 @@ +/* vi: set sw=4 ts=4: */ +/* + * Signal name/number conversion routines. + * + * Copyright 2006 Rob Landley + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* Believe it or not, but some arches have more than 32 SIGs! + * HPPA: SIGSTKFLT == 36. */ + +static const char signals[][7] = { + // SUSv3 says kill must support these, and specifies the numerical values, + // http://www.opengroup.org/onlinepubs/009695399/utilities/kill.html + // {0, "EXIT"}, {1, "HUP"}, {2, "INT"}, {3, "QUIT"}, + // {6, "ABRT"}, {9, "KILL"}, {14, "ALRM"}, {15, "TERM"} + // And Posix adds the following: + // {SIGILL, "ILL"}, {SIGTRAP, "TRAP"}, {SIGFPE, "FPE"}, {SIGUSR1, "USR1"}, + // {SIGSEGV, "SEGV"}, {SIGUSR2, "USR2"}, {SIGPIPE, "PIPE"}, {SIGCHLD, "CHLD"}, + // {SIGCONT, "CONT"}, {SIGSTOP, "STOP"}, {SIGTSTP, "TSTP"}, {SIGTTIN, "TTIN"}, + // {SIGTTOU, "TTOU"} + + [0] = "EXIT", +#ifdef SIGHUP + [SIGHUP ] = "HUP", +#endif +#ifdef SIGINT + [SIGINT ] = "INT", +#endif +#ifdef SIGQUIT + [SIGQUIT ] = "QUIT", +#endif +#ifdef SIGILL + [SIGILL ] = "ILL", +#endif +#ifdef SIGTRAP + [SIGTRAP ] = "TRAP", +#endif +#ifdef SIGABRT + [SIGABRT ] = "ABRT", +#endif +#ifdef SIGBUS + [SIGBUS ] = "BUS", +#endif +#ifdef SIGFPE + [SIGFPE ] = "FPE", +#endif +#ifdef SIGKILL + [SIGKILL ] = "KILL", +#endif +#ifdef SIGUSR1 + [SIGUSR1 ] = "USR1", +#endif +#ifdef SIGSEGV + [SIGSEGV ] = "SEGV", +#endif +#ifdef SIGUSR2 + [SIGUSR2 ] = "USR2", +#endif +#ifdef SIGPIPE + [SIGPIPE ] = "PIPE", +#endif +#ifdef SIGALRM + [SIGALRM ] = "ALRM", +#endif +#ifdef SIGTERM + [SIGTERM ] = "TERM", +#endif +#ifdef SIGSTKFLT + [SIGSTKFLT] = "STKFLT", +#endif +#ifdef SIGCHLD + [SIGCHLD ] = "CHLD", +#endif +#ifdef SIGCONT + [SIGCONT ] = "CONT", +#endif +#ifdef SIGSTOP + [SIGSTOP ] = "STOP", +#endif +#ifdef SIGTSTP + [SIGTSTP ] = "TSTP", +#endif +#ifdef SIGTTIN + [SIGTTIN ] = "TTIN", +#endif +#ifdef SIGTTOU + [SIGTTOU ] = "TTOU", +#endif +#ifdef SIGURG + [SIGURG ] = "URG", +#endif +#ifdef SIGXCPU + [SIGXCPU ] = "XCPU", +#endif +#ifdef SIGXFSZ + [SIGXFSZ ] = "XFSZ", +#endif +#ifdef SIGVTALRM + [SIGVTALRM] = "VTALRM", +#endif +#ifdef SIGPROF + [SIGPROF ] = "PROF", +#endif +#ifdef SIGWINCH + [SIGWINCH ] = "WINCH", +#endif +#ifdef SIGPOLL + [SIGPOLL ] = "POLL", +#endif +#ifdef SIGPWR + [SIGPWR ] = "PWR", +#endif +#ifdef SIGSYS + [SIGSYS ] = "SYS", +#endif +}; + +// Convert signal name to number. + +int FAST_FUNC get_signum(const char *name) +{ + unsigned i; + + i = bb_strtou(name, NULL, 10); + if (!errno) + return i; + if (strncasecmp(name, "SIG", 3) == 0) + name += 3; + for (i = 0; i < ARRAY_SIZE(signals); i++) + if (strcasecmp(name, signals[i]) == 0) + return i; + +#if ENABLE_DESKTOP && (defined(SIGIOT) || defined(SIGIO)) + /* SIGIO[T] are aliased to other names, + * thus cannot be stored in the signals[] array. + * Need special code to recognize them */ + if ((name[0] | 0x20) == 'i' && (name[1] | 0x20) == 'o') { +#ifdef SIGIO + if (!name[2]) + return SIGIO; +#endif +#ifdef SIGIOT + if ((name[2] | 0x20) == 't' && !name[3]) + return SIGIOT; +#endif + } +#endif + + return -1; +} + +// Convert signal number to name + +const char* FAST_FUNC get_signame(int number) +{ + if ((unsigned)number < ARRAY_SIZE(signals)) { + if (signals[number][0]) /* if it's not an empty str */ + return signals[number]; + } + + return itoa(number); +} + + +// Print the whole signal list + +void FAST_FUNC print_signames(void) +{ + unsigned signo; + + for (signo = 1; signo < ARRAY_SIZE(signals); signo++) { + const char *name = signals[signo]; + if (name[0]) + puts(name); + } +} diff --git a/libbb/udp_io.c b/libbb/udp_io.c new file mode 100644 index 0000000..b31f284 --- /dev/null +++ b/libbb/udp_io.c @@ -0,0 +1,168 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2007 Denys Vlasenko + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* + * This asks kernel to let us know dst addr/port of incoming packets + * We don't check for errors here. Not supported == won't be used + */ +void FAST_FUNC +socket_want_pktinfo(int fd) +{ +#ifdef IP_PKTINFO + setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &const_int_1, sizeof(int)); +#endif +#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) + setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &const_int_1, sizeof(int)); +#endif +} + + +ssize_t FAST_FUNC +send_to_from(int fd, void *buf, size_t len, int flags, + const struct sockaddr *to, + const struct sockaddr *from, + socklen_t tolen) +{ +#ifndef IP_PKTINFO + return sendto(fd, buf, len, flags, to, tolen); +#else + struct iovec iov[1]; + struct msghdr msg; + union { + char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))]; +#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) + char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; +#endif + } u; + struct cmsghdr* cmsgptr; + + if (from->sa_family != AF_INET +#if ENABLE_FEATURE_IPV6 + && from->sa_family != AF_INET6 +#endif + ) { + /* ANY local address */ + return sendto(fd, buf, len, flags, to, tolen); + } + + /* man recvmsg and man cmsg is needed to make sense of code below */ + + iov[0].iov_base = buf; + iov[0].iov_len = len; + + memset(&u, 0, sizeof(u)); + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (void *)(struct sockaddr *)to; /* or compiler will annoy us */ + msg.msg_namelen = tolen; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = &u; + msg.msg_controllen = sizeof(u); + msg.msg_flags = flags; + + cmsgptr = CMSG_FIRSTHDR(&msg); + if (to->sa_family == AF_INET && from->sa_family == AF_INET) { + struct in_pktinfo *pktptr; + cmsgptr->cmsg_level = IPPROTO_IP; + cmsgptr->cmsg_type = IP_PKTINFO; + cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + pktptr = (struct in_pktinfo *)(CMSG_DATA(cmsgptr)); + /* pktptr->ipi_ifindex = 0; -- already done by memset(cbuf...) */ + pktptr->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr; + } +#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) + else if (to->sa_family == AF_INET6 && from->sa_family == AF_INET6) { + struct in6_pktinfo *pktptr; + cmsgptr->cmsg_level = IPPROTO_IPV6; + cmsgptr->cmsg_type = IPV6_PKTINFO; + cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + pktptr = (struct in6_pktinfo *)(CMSG_DATA(cmsgptr)); + /* pktptr->ipi6_ifindex = 0; -- already done by memset(cbuf...) */ + pktptr->ipi6_addr = ((struct sockaddr_in6*)from)->sin6_addr; + } +#endif + msg.msg_controllen = cmsgptr->cmsg_len; + + return sendmsg(fd, &msg, flags); +#endif +} + +/* NB: this will never set port# in 'to'! + * _Only_ IP/IPv6 address part of 'to' is _maybe_ modified. + * Typical usage is to preinit 'to' with "default" value + * before calling recv_from_to(). */ +ssize_t FAST_FUNC +recv_from_to(int fd, void *buf, size_t len, int flags, + struct sockaddr *from, struct sockaddr *to, + socklen_t sa_size) +{ +#ifndef IP_PKTINFO + return recvfrom(fd, buf, len, flags, from, &sa_size); +#else + /* man recvmsg and man cmsg is needed to make sense of code below */ + struct iovec iov[1]; + union { + char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))]; +#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) + char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; +#endif + } u; + struct cmsghdr *cmsgptr; + struct msghdr msg; + ssize_t recv_length; + + iov[0].iov_base = buf; + iov[0].iov_len = len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (struct sockaddr *)from; + msg.msg_namelen = sa_size; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = &u; + msg.msg_controllen = sizeof(u); + + recv_length = recvmsg(fd, &msg, flags); + if (recv_length < 0) + return recv_length; + + /* Here we try to retrieve destination IP and memorize it */ + for (cmsgptr = CMSG_FIRSTHDR(&msg); + cmsgptr != NULL; + cmsgptr = CMSG_NXTHDR(&msg, cmsgptr) + ) { + if (cmsgptr->cmsg_level == IPPROTO_IP + && cmsgptr->cmsg_type == IP_PKTINFO + ) { +#define pktinfo(cmsgptr) ( (struct in_pktinfo*)(CMSG_DATA(cmsgptr)) ) + to->sa_family = AF_INET; + ((struct sockaddr_in*)to)->sin_addr = pktinfo(cmsgptr)->ipi_addr; + /* ((struct sockaddr_in*)to)->sin_port = 123; */ +#undef pktinfo + break; + } +#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) + if (cmsgptr->cmsg_level == IPPROTO_IPV6 + && cmsgptr->cmsg_type == IPV6_PKTINFO + ) { +#define pktinfo(cmsgptr) ( (struct in6_pktinfo*)(CMSG_DATA(cmsgptr)) ) + to->sa_family = AF_INET6; + ((struct sockaddr_in6*)to)->sin6_addr = pktinfo(cmsgptr)->ipi6_addr; + /* ((struct sockaddr_in6*)to)->sin6_port = 123; */ +#undef pktinfo + break; + } +#endif + } + return recv_length; +#endif +} diff --git a/libbb/update_passwd.c b/libbb/update_passwd.c new file mode 100644 index 0000000..88bc28c --- /dev/null +++ b/libbb/update_passwd.c @@ -0,0 +1,153 @@ +/* vi: set sw=4 ts=4: */ +/* + * update_passwd + * + * update_passwd is a common function for passwd and chpasswd applets; + * it is responsible for updating password file (i.e. /etc/passwd or + * /etc/shadow) for a given user and password. + * + * Moved from loginutils/passwd.c by Alexander Shishkin + */ + +#include "libbb.h" + +#if ENABLE_SELINUX +static void check_selinux_update_passwd(const char *username) +{ + security_context_t context; + char *seuser; + + if (getuid() != (uid_t)0 || is_selinux_enabled() == 0) + return; /* No need to check */ + + if (getprevcon_raw(&context) < 0) + bb_perror_msg_and_die("getprevcon failed"); + seuser = strtok(context, ":"); + if (!seuser) + bb_error_msg_and_die("invalid context '%s'", context); + if (strcmp(seuser, username) != 0) { + if (checkPasswdAccess(PASSWD__PASSWD) != 0) + bb_error_msg_and_die("SELinux: access denied"); + } + if (ENABLE_FEATURE_CLEAN_UP) + freecon(context); +} +#else +#define check_selinux_update_passwd(username) ((void)0) +#endif + +int FAST_FUNC update_passwd(const char *filename, const char *username, + const char *new_pw) +{ + struct stat sb; + struct flock lock; + FILE *old_fp; + FILE *new_fp; + char *fnamesfx; + char *sfx_char; + unsigned user_len; + int old_fd; + int new_fd; + int i; + int cnt = 0; + int ret = -1; /* failure */ + + filename = xmalloc_follow_symlinks(filename); + if (filename == NULL) + return -1; + + check_selinux_update_passwd(username); + + /* New passwd file, "/etc/passwd+" for now */ + fnamesfx = xasprintf("%s+", filename); + sfx_char = &fnamesfx[strlen(fnamesfx)-1]; + username = xasprintf("%s:", username); + user_len = strlen(username); + + old_fp = fopen(filename, "r+"); + if (!old_fp) + goto free_mem; + old_fd = fileno(old_fp); + + selinux_preserve_fcontext(old_fd); + + /* Try to create "/etc/passwd+". Wait if it exists. */ + i = 30; + do { + // FIXME: on last iteration try w/o O_EXCL but with O_TRUNC? + new_fd = open(fnamesfx, O_WRONLY|O_CREAT|O_EXCL, 0600); + if (new_fd >= 0) goto created; + if (errno != EEXIST) break; + usleep(100000); /* 0.1 sec */ + } while (--i); + bb_perror_msg("cannot create '%s'", fnamesfx); + goto close_old_fp; + + created: + if (!fstat(old_fd, &sb)) { + fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */ + fchown(new_fd, sb.st_uid, sb.st_gid); + } + new_fp = fdopen(new_fd, "w"); + if (!new_fp) { + close(new_fd); + goto unlink_new; + } + + /* Backup file is "/etc/passwd-" */ + *sfx_char = '-'; + /* Delete old backup */ + i = (unlink(fnamesfx) && errno != ENOENT); + /* Create backup as a hardlink to current */ + if (i || link(filename, fnamesfx)) + bb_perror_msg("warning: cannot create backup copy '%s'", fnamesfx); + *sfx_char = '+'; + + /* Lock the password file before updating */ + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + if (fcntl(old_fd, F_SETLK, &lock) < 0) + bb_perror_msg("warning: cannot lock '%s'", filename); + lock.l_type = F_UNLCK; + + /* Read current password file, write updated /etc/passwd+ */ + while (1) { + char *line = xmalloc_fgets(old_fp); + if (!line) break; /* EOF/error */ + if (strncmp(username, line, user_len) == 0) { + /* we have a match with "username:"... */ + const char *cp = line + user_len; + /* now cp -> old passwd, skip it: */ + cp = strchrnul(cp, ':'); + /* now cp -> ':' after old passwd or -> "" */ + fprintf(new_fp, "%s%s%s", username, new_pw, cp); + cnt++; + } else + fputs(line, new_fp); + free(line); + } + fcntl(old_fd, F_SETLK, &lock); + + /* We do want all of them to execute, thus | instead of || */ + if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp)) + || rename(fnamesfx, filename) + ) { + /* At least one of those failed */ + goto unlink_new; + } + ret = cnt; /* whee, success! */ + + unlink_new: + if (ret < 0) unlink(fnamesfx); + + close_old_fp: + fclose(old_fp); + + free_mem: + free(fnamesfx); + free((char *)filename); + free((char *)username); + return ret; +} diff --git a/libbb/uuencode.c b/libbb/uuencode.c new file mode 100644 index 0000000..67d98d5 --- /dev/null +++ b/libbb/uuencode.c @@ -0,0 +1,71 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 2006 Rob Landley + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* Conversion table. for base 64 */ +const char bb_uuenc_tbl_base64[65 + 2] ALIGN1 = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', + '=' /* termination character */, + '\n', '\0' /* needed for uudecode.c */ +}; + +const char bb_uuenc_tbl_std[65] ALIGN1 = { + '`', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`' /* termination character */ +}; + +/* + * Encode bytes at S of length LENGTH to uuencode or base64 format and place it + * to STORE. STORE will be 0-terminated, and must point to a writable + * buffer of at least 1+BASE64_LENGTH(length) bytes. + * where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3)) + */ +void FAST_FUNC bb_uuencode(char *p, const void *src, int length, const char *tbl) +{ + const unsigned char *s = src; + + /* Transform the 3x8 bits to 4x6 bits */ + while (length > 0) { + unsigned s1, s2; + + /* Are s[1], s[2] valid or should be assumed 0? */ + s1 = s2 = 0; + length -= 3; /* can be >=0, -1, -2 */ + if (length >= -1) { + s1 = s[1]; + if (length >= 0) + s2 = s[2]; + } + *p++ = tbl[s[0] >> 2]; + *p++ = tbl[((s[0] & 3) << 4) + (s1 >> 4)]; + *p++ = tbl[((s1 & 0xf) << 2) + (s2 >> 6)]; + *p++ = tbl[s2 & 0x3f]; + s += 3; + } + /* Zero-terminate */ + *p = '\0'; + /* If length is -2 or -1, pad last char or two */ + while (length) { + *--p = tbl[64]; + length++; + } +} diff --git a/libbb/vdprintf.c b/libbb/vdprintf.c new file mode 100644 index 0000000..09fffbc --- /dev/null +++ b/libbb/vdprintf.c @@ -0,0 +1,21 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +#if defined(__GLIBC__) && __GLIBC__ < 2 +int FAST_FUNC vdprintf(int d, const char *format, va_list ap) +{ + char buf[BUF_SIZE]; + int len; + + len = vsnprintf(buf, BUF_SIZE, format, ap); + return write(d, buf, len); +} +#endif diff --git a/libbb/verror_msg.c b/libbb/verror_msg.c new file mode 100644 index 0000000..58846d5 --- /dev/null +++ b/libbb/verror_msg.c @@ -0,0 +1,127 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" +#include + +smallint logmode = LOGMODE_STDIO; +const char *msg_eol = "\n"; + +void FAST_FUNC bb_verror_msg(const char *s, va_list p, const char* strerr) +{ + char *msg; + int applet_len, strerr_len, msgeol_len, used; + + if (!logmode) + return; + + if (!s) /* nomsg[_and_die] uses NULL fmt */ + s = ""; /* some libc don't like printf(NULL) */ + + used = vasprintf(&msg, s, p); + if (used < 0) + return; + + /* This is ugly and costs +60 bytes compared to multiple + * fprintf's, but is guaranteed to do a single write. + * This is needed for e.g. httpd logging, when multiple + * children can produce log messages simultaneously. */ + + applet_len = strlen(applet_name) + 2; /* "applet: " */ + strerr_len = strerr ? strlen(strerr) : 0; + msgeol_len = strlen(msg_eol); + /* +3 is for ": " before strerr and for terminating NUL */ + msg = xrealloc(msg, applet_len + used + strerr_len + msgeol_len + 3); + /* TODO: maybe use writev instead of memmoving? Need full_writev? */ + memmove(msg + applet_len, msg, used); + used += applet_len; + strcpy(msg, applet_name); + msg[applet_len - 2] = ':'; + msg[applet_len - 1] = ' '; + if (strerr) { + if (s[0]) { /* not perror_nomsg? */ + msg[used++] = ':'; + msg[used++] = ' '; + } + strcpy(&msg[used], strerr); + used += strerr_len; + } + strcpy(&msg[used], msg_eol); + + if (logmode & LOGMODE_STDIO) { + fflush(stdout); + full_write(STDERR_FILENO, msg, used + msgeol_len); + } + if (logmode & LOGMODE_SYSLOG) { + syslog(LOG_ERR, "%s", msg + applet_len); + } + free(msg); +} + + +#ifdef VERSION_WITH_WRITEV + +/* Code size is approximately the same, but currently it's the only user + * of writev in entire bbox. __libc_writev in uclibc is ~50 bytes. */ + +void FAST_FUNC bb_verror_msg(const char *s, va_list p, const char* strerr) +{ + int strerr_len, msgeol_len; + struct iovec iov[3]; + +#define used (iov[2].iov_len) +#define msgv (iov[2].iov_base) +#define msgc ((char*)(iov[2].iov_base)) +#define msgptr (&(iov[2].iov_base)) + + if (!logmode) + return; + + if (!s) /* nomsg[_and_die] uses NULL fmt */ + s = ""; /* some libc don't like printf(NULL) */ + + /* Prevent "derefing type-punned ptr will break aliasing rules" */ + used = vasprintf((char**)(void*)msgptr, s, p); + if (used < 0) + return; + + /* This is ugly and costs +60 bytes compared to multiple + * fprintf's, but is guaranteed to do a single write. + * This is needed for e.g. httpd logging, when multiple + * children can produce log messages simultaneously. */ + + strerr_len = strerr ? strlen(strerr) : 0; + msgeol_len = strlen(msg_eol); + /* +3 is for ": " before strerr and for terminating NUL */ + msgv = xrealloc(msgv, used + strerr_len + msgeol_len + 3); + if (strerr) { + msgc[used++] = ':'; + msgc[used++] = ' '; + strcpy(msgc + used, strerr); + used += strerr_len; + } + strcpy(msgc + used, msg_eol); + used += msgeol_len; + + if (logmode & LOGMODE_STDIO) { + iov[0].iov_base = (char*)applet_name; + iov[0].iov_len = strlen(applet_name); + iov[1].iov_base = (char*)": "; + iov[1].iov_len = 2; + /*iov[2].iov_base = msgc;*/ + /*iov[2].iov_len = used;*/ + fflush(stdout); + writev(2, iov, 3); + } + if (logmode & LOGMODE_SYSLOG) { + syslog(LOG_ERR, "%s", msgc); + } + free(msgc); +} +#endif diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c new file mode 100644 index 0000000..50dc3af --- /dev/null +++ b/libbb/vfork_daemon_rexec.c @@ -0,0 +1,331 @@ +/* vi: set sw=4 ts=4: */ +/* + * Rexec program for system have fork() as vfork() with foreground option + * + * Copyright (C) Vladimir N. Oleynik + * Copyright (C) 2003 Russ Dill + * + * daemon() portion taken from uClibc: + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Modified for uClibc by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include +#include "busybox.h" /* uses applet tables */ + +/* This does a fork/exec in one call, using vfork(). Returns PID of new child, + * -1 for failure. Runs argv[0], searching path if that has no / in it. */ +pid_t FAST_FUNC spawn(char **argv) +{ + /* Compiler should not optimize stores here */ + volatile int failed; + pid_t pid; + +// Ain't it a good place to fflush(NULL)? + + /* Be nice to nommu machines. */ + failed = 0; + pid = vfork(); + if (pid < 0) /* error */ + return pid; + if (!pid) { /* child */ + /* This macro is ok - it doesn't do NOEXEC/NOFORK tricks */ + BB_EXECVP(argv[0], argv); + + /* We are (maybe) sharing a stack with blocked parent, + * let parent know we failed and then exit to unblock parent + * (but don't run atexit() stuff, which would screw up parent.) + */ + failed = errno; + _exit(111); + } + /* parent */ + /* Unfortunately, this is not reliable: according to standards + * vfork() can be equivalent to fork() and we won't see value + * of 'failed'. + * Interested party can wait on pid and learn exit code. + * If 111 - then it (most probably) failed to exec */ + if (failed) { + errno = failed; + return -1; + } + return pid; +} + +/* Die with an error message if we can't spawn a child process. */ +pid_t FAST_FUNC xspawn(char **argv) +{ + pid_t pid = spawn(argv); + if (pid < 0) + bb_simple_perror_msg_and_die(*argv); + return pid; +} + +pid_t FAST_FUNC safe_waitpid(pid_t pid, int *wstat, int options) +{ + pid_t r; + + do + r = waitpid(pid, wstat, options); + while ((r == -1) && (errno == EINTR)); + return r; +} + +pid_t FAST_FUNC wait_any_nohang(int *wstat) +{ + return safe_waitpid(-1, wstat, WNOHANG); +} + +// Wait for the specified child PID to exit, returning child's error return. +int FAST_FUNC wait4pid(pid_t pid) +{ + int status; + + if (pid <= 0) { + /*errno = ECHILD; -- wrong. */ + /* we expect errno to be already set from failed [v]fork/exec */ + return -1; + } + if (safe_waitpid(pid, &status, 0) == -1) + return -1; + if (WIFEXITED(status)) + return WEXITSTATUS(status); + if (WIFSIGNALED(status)) + return WTERMSIG(status) + 1000; + return 0; +} + +#if ENABLE_FEATURE_PREFER_APPLETS +void FAST_FUNC save_nofork_data(struct nofork_save_area *save) +{ + memcpy(&save->die_jmp, &die_jmp, sizeof(die_jmp)); + save->applet_name = applet_name; + save->xfunc_error_retval = xfunc_error_retval; + save->option_mask32 = option_mask32; + save->die_sleep = die_sleep; + save->saved = 1; +} + +void FAST_FUNC restore_nofork_data(struct nofork_save_area *save) +{ + memcpy(&die_jmp, &save->die_jmp, sizeof(die_jmp)); + applet_name = save->applet_name; + xfunc_error_retval = save->xfunc_error_retval; + option_mask32 = save->option_mask32; + die_sleep = save->die_sleep; +} + +int FAST_FUNC run_nofork_applet_prime(struct nofork_save_area *old, int applet_no, char **argv) +{ + int rc, argc; + + applet_name = APPLET_NAME(applet_no); + + xfunc_error_retval = EXIT_FAILURE; + + /* Special flag for xfunc_die(). If xfunc will "die" + * in NOFORK applet, xfunc_die() sees negative + * die_sleep and longjmp here instead. */ + die_sleep = -1; + + /* In case getopt() or getopt32() was already called: + * reset the libc getopt() function, which keeps internal state. + * + * BSD-derived getopt() functions require that optind be set to 1 in + * order to reset getopt() state. This used to be generally accepted + * way of resetting getopt(). However, glibc's getopt() + * has additional getopt() state beyond optind, and requires that + * optind be set to zero to reset its state. So the unfortunate state of + * affairs is that BSD-derived versions of getopt() misbehave if + * optind is set to 0 in order to reset getopt(), and glibc's getopt() + * will core dump if optind is set 1 in order to reset getopt(). + * + * More modern versions of BSD require that optreset be set to 1 in + * order to reset getopt(). Sigh. Standards, anyone? + */ +#ifdef __GLIBC__ + optind = 0; +#else /* BSD style */ + optind = 1; + /* optreset = 1; */ +#endif + /* optarg = NULL; opterr = 1; optopt = 63; - do we need this too? */ + /* (values above are what they initialized to in glibc and uclibc) */ + /* option_mask32 = 0; - not needed, no applet depends on it being 0 */ + + argc = 1; + while (argv[argc]) + argc++; + + rc = setjmp(die_jmp); + if (!rc) { + /* Some callers (xargs) + * need argv untouched because they free argv[i]! */ + char *tmp_argv[argc+1]; + memcpy(tmp_argv, argv, (argc+1) * sizeof(tmp_argv[0])); + /* Finally we can call NOFORK applet's main() */ + rc = applet_main[applet_no](argc, tmp_argv); + + /* The whole reason behind nofork_save_area is that _main + * may exit non-locally! For example, in hush Ctrl-Z tries + * (modulo bugs) to dynamically create a child (backgrounded task) + * if it detects that Ctrl-Z was pressed when a NOFORK was running. + * Testcase: interactive "rm -i". + * Don't fool yourself into thinking "and _main() returns + * quickly here" and removing "useless" nofork_save_area code. */ + + } else { /* xfunc died in NOFORK applet */ + /* in case they meant to return 0... */ + if (rc == -2222) + rc = 0; + } + + /* Restoring some globals */ + restore_nofork_data(old); + + /* Other globals can be simply reset to defaults */ +#ifdef __GLIBC__ + optind = 0; +#else /* BSD style */ + optind = 1; +#endif + + return rc & 0xff; /* don't confuse people with "exitcodes" >255 */ +} + +int FAST_FUNC run_nofork_applet(int applet_no, char **argv) +{ + struct nofork_save_area old; + + /* Saving globals */ + save_nofork_data(&old); + return run_nofork_applet_prime(&old, applet_no, argv); +} +#endif /* FEATURE_PREFER_APPLETS */ + +int FAST_FUNC spawn_and_wait(char **argv) +{ + int rc; +#if ENABLE_FEATURE_PREFER_APPLETS + int a = find_applet_by_name(argv[0]); + + if (a >= 0 && (APPLET_IS_NOFORK(a) +#if BB_MMU + || APPLET_IS_NOEXEC(a) /* NOEXEC trick needs fork() */ +#endif + )) { +#if BB_MMU + if (APPLET_IS_NOFORK(a)) +#endif + { + return run_nofork_applet(a, argv); + } +#if BB_MMU + /* MMU only */ + /* a->noexec is true */ + rc = fork(); + if (rc) /* parent or error */ + return wait4pid(rc); + /* child */ + xfunc_error_retval = EXIT_FAILURE; + run_applet_no_and_exit(a, argv); +#endif + } +#endif /* FEATURE_PREFER_APPLETS */ + rc = spawn(argv); + return wait4pid(rc); +} + +#if !BB_MMU +void FAST_FUNC re_exec(char **argv) +{ + /* high-order bit of first char in argv[0] is a hidden + * "we have (already) re-execed, don't do it again" flag */ + argv[0][0] |= 0x80; + execv(bb_busybox_exec_path, argv); + bb_perror_msg_and_die("exec %s", bb_busybox_exec_path); +} + +void FAST_FUNC forkexit_or_rexec(char **argv) +{ + pid_t pid; + /* Maybe we are already re-execed and come here again? */ + if (re_execed) + return; + + pid = vfork(); + if (pid < 0) /* wtf? */ + bb_perror_msg_and_die("vfork"); + if (pid) /* parent */ + exit(EXIT_SUCCESS); + /* child - re-exec ourself */ + re_exec(argv); +} +#else +/* Dance around (void)...*/ +#undef forkexit_or_rexec +void FAST_FUNC forkexit_or_rexec(void) +{ + pid_t pid; + pid = fork(); + if (pid < 0) /* wtf? */ + bb_perror_msg_and_die("fork"); + if (pid) /* parent */ + exit(EXIT_SUCCESS); + /* child */ +} +#define forkexit_or_rexec(argv) forkexit_or_rexec() +#endif + +/* Due to a #define in libbb.h on MMU systems we actually have 1 argument - + * char **argv "vanishes" */ +void FAST_FUNC bb_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 */ + } +} + +void FAST_FUNC bb_sanitize_stdio(void) +{ + bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL); +} diff --git a/libbb/warn_ignoring_args.c b/libbb/warn_ignoring_args.c new file mode 100644 index 0000000..65dea32 --- /dev/null +++ b/libbb/warn_ignoring_args.c @@ -0,0 +1,17 @@ +/* vi: set sw=4 ts=4: */ +/* + * warn_ignoring_args implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +void FAST_FUNC bb_warn_ignoring_args(int n) +{ + if (n) { + bb_error_msg("ignoring all arguments"); + } +} diff --git a/libbb/wfopen.c b/libbb/wfopen.c new file mode 100644 index 0000000..4c84b3b --- /dev/null +++ b/libbb/wfopen.c @@ -0,0 +1,40 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +FILE* FAST_FUNC fopen_or_warn(const char *path, const char *mode) +{ + FILE *fp = fopen(path, mode); + if (!fp) { + bb_simple_perror_msg(path); + errno = 0; + } + return fp; +} + +FILE* FAST_FUNC fopen_for_read(const char *path) +{ + return fopen(path, "r"); +} + +FILE* FAST_FUNC xfopen_for_read(const char *path) +{ + return xfopen(path, "r"); +} + +FILE* FAST_FUNC fopen_for_write(const char *path) +{ + return fopen(path, "w"); +} + +FILE* FAST_FUNC xfopen_for_write(const char *path) +{ + return xfopen(path, "w"); +} diff --git a/libbb/wfopen_input.c b/libbb/wfopen_input.c new file mode 100644 index 0000000..46ff7a6 --- /dev/null +++ b/libbb/wfopen_input.c @@ -0,0 +1,48 @@ +/* vi: set sw=4 ts=4: */ +/* + * wfopen_input implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* A number of applets need to open a file for reading, where the filename + * is a command line arg. Since often that arg is '-' (meaning stdin), + * we avoid testing everywhere by consolidating things in this routine. + */ + +#include "libbb.h" + +FILE* FAST_FUNC fopen_or_warn_stdin(const char *filename) +{ + FILE *fp = stdin; + + if (filename != bb_msg_standard_input + && NOT_LONE_DASH(filename) + ) { + fp = fopen_or_warn(filename, "r"); + } + return fp; +} + +FILE* FAST_FUNC xfopen_stdin(const char *filename) +{ + FILE *fp = fopen_or_warn_stdin(filename); + if (fp) + return fp; + xfunc_die(); /* We already output an error message. */ +} + +int FAST_FUNC open_or_warn_stdin(const char *filename) +{ + int fd = STDIN_FILENO; + + if (filename != bb_msg_standard_input + && NOT_LONE_DASH(filename) + ) { + fd = open_or_warn(filename, O_RDONLY); + } + + return fd; +} diff --git a/libbb/write.c b/libbb/write.c new file mode 100644 index 0000000..37f4617 --- /dev/null +++ b/libbb/write.c @@ -0,0 +1,20 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2008 Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* Open file and write string str to it, close file. + * Die on any open or write-error. */ +void FAST_FUNC xopen_xwrite_close(const char* file, const char* str) +{ + int fd = xopen(file, O_WRONLY); + + xwrite(fd, str, strlen(str)); + close(fd); +} diff --git a/libbb/xatonum.c b/libbb/xatonum.c new file mode 100644 index 0000000..3cdf634 --- /dev/null +++ b/libbb/xatonum.c @@ -0,0 +1,70 @@ +/* vi: set sw=4 ts=4: */ +/* + * ascii-to-numbers implementations for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +#define type long long +#define xstrtou(rest) xstrtoull##rest +#define xstrto(rest) xstrtoll##rest +#define xatou(rest) xatoull##rest +#define xato(rest) xatoll##rest +#define XSTR_UTYPE_MAX ULLONG_MAX +#define XSTR_TYPE_MAX LLONG_MAX +#define XSTR_TYPE_MIN LLONG_MIN +#define XSTR_STRTOU strtoull +#include "xatonum_template.c" + +#if ULONG_MAX != ULLONG_MAX +#define type long +#define xstrtou(rest) xstrtoul##rest +#define xstrto(rest) xstrtol##rest +#define xatou(rest) xatoul##rest +#define xato(rest) xatol##rest +#define XSTR_UTYPE_MAX ULONG_MAX +#define XSTR_TYPE_MAX LONG_MAX +#define XSTR_TYPE_MIN LONG_MIN +#define XSTR_STRTOU strtoul +#include "xatonum_template.c" +#endif + +#if UINT_MAX != ULONG_MAX +static ALWAYS_INLINE +unsigned bb_strtoui(const char *str, char **end, int b) +{ + unsigned long v = strtoul(str, end, b); + if (v > UINT_MAX) { + errno = ERANGE; + return UINT_MAX; + } + return v; +} +#define type int +#define xstrtou(rest) xstrtou##rest +#define xstrto(rest) xstrtoi##rest +#define xatou(rest) xatou##rest +#define xato(rest) xatoi##rest +#define XSTR_UTYPE_MAX UINT_MAX +#define XSTR_TYPE_MAX INT_MAX +#define XSTR_TYPE_MIN INT_MIN +/* libc has no strtoui, so we need to create/use our own */ +#define XSTR_STRTOU bb_strtoui +#include "xatonum_template.c" +#endif + +/* A few special cases */ + +int FAST_FUNC xatoi_u(const char *numstr) +{ + return xatou_range(numstr, 0, INT_MAX); +} + +uint16_t FAST_FUNC xatou16(const char *numstr) +{ + return xatou_range(numstr, 0, 0xffff); +} diff --git a/libbb/xatonum_template.c b/libbb/xatonum_template.c new file mode 100644 index 0000000..2360ae8 --- /dev/null +++ b/libbb/xatonum_template.c @@ -0,0 +1,187 @@ +/* +You need to define the following (example): + +#define type long +#define xstrtou(rest) xstrtoul##rest +#define xstrto(rest) xstrtol##rest +#define xatou(rest) xatoul##rest +#define xato(rest) xatol##rest +#define XSTR_UTYPE_MAX ULONG_MAX +#define XSTR_TYPE_MAX LONG_MAX +#define XSTR_TYPE_MIN LONG_MIN +#define XSTR_STRTOU strtoul +*/ + +unsigned type FAST_FUNC xstrtou(_range_sfx)(const char *numstr, int base, + unsigned type lower, + unsigned type upper, + const struct suffix_mult *suffixes) +{ + unsigned type r; + int old_errno; + char *e; + + /* Disallow '-' and any leading whitespace. Make sure we get the + * actual isspace function rather than a macro implementaion. */ + if (*numstr == '-' || *numstr == '+' || (isspace)(*numstr)) + goto inval; + + /* Since this is a lib function, we're not allowed to reset errno to 0. + * Doing so could break an app that is deferring checking of errno. + * So, save the old value so that we can restore it if successful. */ + old_errno = errno; + errno = 0; + r = XSTR_STRTOU(numstr, &e, base); + /* Do the initial validity check. Note: The standards do not + * guarantee that errno is set if no digits were found. So we + * must test for this explicitly. */ + if (errno || numstr == e) + goto inval; /* error / no digits / illegal trailing chars */ + + errno = old_errno; /* Ok. So restore errno. */ + + /* Do optional suffix parsing. Allow 'empty' suffix tables. + * Note that we also allow nul suffixes with associated multipliers, + * to allow for scaling of the numstr by some default multiplier. */ + if (suffixes) { + while (suffixes->mult) { + if (strcmp(suffixes->suffix, e) == 0) { + if (XSTR_UTYPE_MAX / suffixes->mult < r) + goto range; /* overflow! */ + r *= suffixes->mult; + goto chk_range; + } + ++suffixes; + } + } + + /* Note: trailing space is an error. + It would be easy enough to allow though if desired. */ + if (*e) + goto inval; + chk_range: + /* Finally, check for range limits. */ + if (r >= lower && r <= upper) + return r; + range: + bb_error_msg_and_die("number %s is not in %llu..%llu range", + numstr, (unsigned long long)lower, + (unsigned long long)upper); + inval: + bb_error_msg_and_die("invalid number '%s'", numstr); +} + +unsigned type FAST_FUNC xstrtou(_range)(const char *numstr, int base, + unsigned type lower, + unsigned type upper) +{ + return xstrtou(_range_sfx)(numstr, base, lower, upper, NULL); +} + +unsigned type FAST_FUNC xstrtou(_sfx)(const char *numstr, int base, + const struct suffix_mult *suffixes) +{ + return xstrtou(_range_sfx)(numstr, base, 0, XSTR_UTYPE_MAX, suffixes); +} + +unsigned type FAST_FUNC xstrtou()(const char *numstr, int base) +{ + return xstrtou(_range_sfx)(numstr, base, 0, XSTR_UTYPE_MAX, NULL); +} + +unsigned type FAST_FUNC xatou(_range_sfx)(const char *numstr, + unsigned type lower, + unsigned type upper, + const struct suffix_mult *suffixes) +{ + return xstrtou(_range_sfx)(numstr, 10, lower, upper, suffixes); +} + +unsigned type FAST_FUNC xatou(_range)(const char *numstr, + unsigned type lower, + unsigned type upper) +{ + return xstrtou(_range_sfx)(numstr, 10, lower, upper, NULL); +} + +unsigned type FAST_FUNC xatou(_sfx)(const char *numstr, + const struct suffix_mult *suffixes) +{ + return xstrtou(_range_sfx)(numstr, 10, 0, XSTR_UTYPE_MAX, suffixes); +} + +unsigned type FAST_FUNC xatou()(const char *numstr) +{ + return xatou(_sfx)(numstr, NULL); +} + +/* Signed ones */ + +type FAST_FUNC xstrto(_range_sfx)(const char *numstr, int base, + type lower, + type upper, + const struct suffix_mult *suffixes) +{ + unsigned type u = XSTR_TYPE_MAX; + type r; + const char *p = numstr; + + /* NB: if you'll decide to disallow '+': + * at least renice applet needs to allow it */ + if (p[0] == '+' || p[0] == '-') { + ++p; + if (p[0] == '-') + ++u; /* = _MIN (01111... + 1 == 10000...) */ + } + + r = xstrtou(_range_sfx)(p, base, 0, u, suffixes); + + if (*numstr == '-') { + r = -r; + } + + if (r < lower || r > upper) { + bb_error_msg_and_die("number %s is not in %lld..%lld range", + numstr, (long long)lower, (long long)upper); + } + + return r; +} + +type FAST_FUNC xstrto(_range)(const char *numstr, int base, type lower, type upper) +{ + return xstrto(_range_sfx)(numstr, base, lower, upper, NULL); +} + +type FAST_FUNC xato(_range_sfx)(const char *numstr, + type lower, + type upper, + const struct suffix_mult *suffixes) +{ + return xstrto(_range_sfx)(numstr, 10, lower, upper, suffixes); +} + +type FAST_FUNC xato(_range)(const char *numstr, type lower, type upper) +{ + return xstrto(_range_sfx)(numstr, 10, lower, upper, NULL); +} + +type FAST_FUNC xato(_sfx)(const char *numstr, const struct suffix_mult *suffixes) +{ + return xstrto(_range_sfx)(numstr, 10, XSTR_TYPE_MIN, XSTR_TYPE_MAX, suffixes); +} + +type FAST_FUNC xato()(const char *numstr) +{ + return xstrto(_range_sfx)(numstr, 10, XSTR_TYPE_MIN, XSTR_TYPE_MAX, NULL); +} + +#undef type +#undef xstrtou +#undef xstrto +#undef xatou +#undef xato +#undef XSTR_UTYPE_MAX +#undef XSTR_TYPE_MAX +#undef XSTR_TYPE_MIN +#undef XSTR_STRTOU diff --git a/libbb/xconnect.c b/libbb/xconnect.c new file mode 100644 index 0000000..27c7424 --- /dev/null +++ b/libbb/xconnect.c @@ -0,0 +1,401 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Connect to host at port using address resolution from getaddrinfo + * + */ + +#include +#include +#include "libbb.h" + +void FAST_FUNC setsockopt_reuseaddr(int fd) +{ + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_1, sizeof(const_int_1)); +} +int FAST_FUNC setsockopt_broadcast(int fd) +{ + return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &const_int_1, sizeof(const_int_1)); +} +int FAST_FUNC setsockopt_bindtodevice(int fd, const char *iface) +{ + int r; + struct ifreq ifr; + strncpy(ifr.ifr_name, iface, IFNAMSIZ); + /* Actually, ifr_name is at offset 0, and in practice + * just giving char[IFNAMSIZ] instead of struct ifreq works too. + * But just in case it's not true on some obscure arch... */ + r = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)); + if (r) + bb_perror_msg("can't bind to interface %s", iface); + return r; +} + + +void FAST_FUNC xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) +{ + if (connect(s, s_addr, addrlen) < 0) { + 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 + * default_port */ +unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned default_port) +{ + unsigned port_nr = default_port; + if (port) { + int old_errno; + + /* Since this is a lib function, we're not allowed to reset errno to 0. + * Doing so could break an app that is deferring checking of errno. */ + old_errno = errno; + port_nr = bb_strtou(port, NULL, 10); + if (errno || port_nr > 65535) { + struct servent *tserv = getservbyname(port, protocol); + port_nr = default_port; + if (tserv) + port_nr = ntohs(tserv->s_port); + } + errno = old_errno; + } + return (uint16_t)port_nr; +} + + +/* "Old" networking API - only IPv4 */ + +/* +void FAST_FUNC bb_lookup_host(struct sockaddr_in *s_in, const char *host) +{ + struct hostent *he; + + memset(s_in, 0, sizeof(struct sockaddr_in)); + s_in->sin_family = AF_INET; + he = xgethostbyname(host); + memcpy(&(s_in->sin_addr), he->h_addr_list[0], he->h_length); +} + + +int FAST_FUNC xconnect_tcp_v4(struct sockaddr_in *s_addr) +{ + int s = xsocket(AF_INET, SOCK_STREAM, 0); + xconnect(s, (struct sockaddr*) s_addr, sizeof(*s_addr)); + return s; +} +*/ + +/* "New" networking API */ + + +int FAST_FUNC get_nport(const struct sockaddr *sa) +{ +#if ENABLE_FEATURE_IPV6 + if (sa->sa_family == AF_INET6) { + return ((struct sockaddr_in6*)sa)->sin6_port; + } +#endif + if (sa->sa_family == AF_INET) { + return ((struct sockaddr_in*)sa)->sin_port; + } + /* What? UNIX socket? IPX?? :) */ + return -1; +} + +void FAST_FUNC set_nport(len_and_sockaddr *lsa, unsigned port) +{ +#if ENABLE_FEATURE_IPV6 + if (lsa->u.sa.sa_family == AF_INET6) { + lsa->u.sin6.sin6_port = port; + return; + } +#endif + if (lsa->u.sa.sa_family == AF_INET) { + lsa->u.sin.sin_port = port; + return; + } + /* What? UNIX socket? IPX?? :) */ +} + +/* We hijack this constant to mean something else */ +/* It doesn't hurt because we will remove this bit anyway */ +#define DIE_ON_ERROR AI_CANONNAME + +/* host: "1.2.3.4[:port]", "www.google.com[:port]" + * port: if neither of above specifies port # */ +static len_and_sockaddr* str2sockaddr( + const char *host, int port, +USE_FEATURE_IPV6(sa_family_t af,) + int ai_flags) +{ + int rc; + len_and_sockaddr *r = NULL; + struct addrinfo *result = NULL; + struct addrinfo *used_res; + const char *org_host = host; /* only for error msg */ + const char *cp; + struct addrinfo hint; + + /* Ugly parsing of host:addr */ + if (ENABLE_FEATURE_IPV6 && host[0] == '[') { + /* Even uglier parsing of [xx]:nn */ + host++; + cp = strchr(host, ']'); + if (!cp || cp[1] != ':') { /* Malformed: must have [xx]:nn */ + bb_error_msg("bad address '%s'", org_host); + if (ai_flags & DIE_ON_ERROR) + xfunc_die(); + return NULL; + } + } else { + cp = strrchr(host, ':'); + if (ENABLE_FEATURE_IPV6 && cp && strchr(host, ':') != cp) { + /* There is more than one ':' (e.g. "::1") */ + cp = NULL; /* it's not a port spec */ + } + } + if (cp) { /* points to ":" or "]:" */ + int sz = cp - host + 1; + host = safe_strncpy(alloca(sz), host, sz); + if (ENABLE_FEATURE_IPV6 && *cp != ':') + cp++; /* skip ']' */ + cp++; /* skip ':' */ + port = bb_strtou(cp, NULL, 10); + if (errno || (unsigned)port > 0xffff) { + bb_error_msg("bad port spec '%s'", org_host); + if (ai_flags & DIE_ON_ERROR) + xfunc_die(); + return NULL; + } + } + + memset(&hint, 0 , sizeof(hint)); +#if !ENABLE_FEATURE_IPV6 + hint.ai_family = AF_INET; /* do not try to find IPv6 */ +#else + hint.ai_family = af; +#endif + /* Needed. Or else we will get each address thrice (or more) + * for each possible socket type (tcp,udp,raw...): */ + hint.ai_socktype = SOCK_STREAM; + hint.ai_flags = ai_flags & ~DIE_ON_ERROR; + rc = getaddrinfo(host, NULL, &hint, &result); + if (rc || !result) { + bb_error_msg("bad address '%s'", org_host); + if (ai_flags & DIE_ON_ERROR) + xfunc_die(); + goto ret; + } + used_res = result; +#if ENABLE_FEATURE_PREFER_IPV4_ADDRESS + while (1) { + if (used_res->ai_family == AF_INET) + break; + used_res = used_res->ai_next; + if (!used_res) { + used_res = result; + break; + } + } +#endif + r = xmalloc(offsetof(len_and_sockaddr, u.sa) + used_res->ai_addrlen); + r->len = used_res->ai_addrlen; + memcpy(&r->u.sa, used_res->ai_addr, used_res->ai_addrlen); + set_nport(r, htons(port)); + ret: + freeaddrinfo(result); + return r; +} +#if !ENABLE_FEATURE_IPV6 +#define str2sockaddr(host, port, af, ai_flags) str2sockaddr(host, port, ai_flags) +#endif + +#if ENABLE_FEATURE_IPV6 +len_and_sockaddr* FAST_FUNC host_and_af2sockaddr(const char *host, int port, sa_family_t af) +{ + return str2sockaddr(host, port, af, 0); +} + +len_and_sockaddr* FAST_FUNC xhost_and_af2sockaddr(const char *host, int port, sa_family_t af) +{ + return str2sockaddr(host, port, af, DIE_ON_ERROR); +} +#endif + +len_and_sockaddr* FAST_FUNC host2sockaddr(const char *host, int port) +{ + return str2sockaddr(host, port, AF_UNSPEC, 0); +} + +len_and_sockaddr* FAST_FUNC xhost2sockaddr(const char *host, int port) +{ + return str2sockaddr(host, port, AF_UNSPEC, DIE_ON_ERROR); +} + +len_and_sockaddr* FAST_FUNC xdotted2sockaddr(const char *host, int port) +{ + return str2sockaddr(host, port, AF_UNSPEC, AI_NUMERICHOST | DIE_ON_ERROR); +} + +#undef xsocket_type +int FAST_FUNC xsocket_type(len_and_sockaddr **lsap, USE_FEATURE_IPV6(int family,) int sock_type) +{ + SKIP_FEATURE_IPV6(enum { family = AF_INET };) + len_and_sockaddr *lsa; + int fd; + int len; + +#if ENABLE_FEATURE_IPV6 + if (family == AF_UNSPEC) { + fd = socket(AF_INET6, sock_type, 0); + if (fd >= 0) { + family = AF_INET6; + goto done; + } + family = AF_INET; + } +#endif + fd = xsocket(family, sock_type, 0); + len = sizeof(struct sockaddr_in); +#if ENABLE_FEATURE_IPV6 + if (family == AF_INET6) { + done: + len = sizeof(struct sockaddr_in6); + } +#endif + lsa = xzalloc(offsetof(len_and_sockaddr, u.sa) + len); + lsa->len = len; + lsa->u.sa.sa_family = family; + *lsap = lsa; + return fd; +} + +int FAST_FUNC xsocket_stream(len_and_sockaddr **lsap) +{ + return xsocket_type(lsap, USE_FEATURE_IPV6(AF_UNSPEC,) SOCK_STREAM); +} + +static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type) +{ + int fd; + len_and_sockaddr *lsa; + + if (bindaddr && bindaddr[0]) { + lsa = xdotted2sockaddr(bindaddr, port); + /* user specified bind addr dictates family */ + fd = xsocket(lsa->u.sa.sa_family, sock_type, 0); + } else { + fd = xsocket_type(&lsa, USE_FEATURE_IPV6(AF_UNSPEC,) sock_type); + set_nport(lsa, htons(port)); + } + setsockopt_reuseaddr(fd); + xbind(fd, &lsa->u.sa, lsa->len); + free(lsa); + return fd; +} + +int FAST_FUNC create_and_bind_stream_or_die(const char *bindaddr, int port) +{ + return create_and_bind_or_die(bindaddr, port, SOCK_STREAM); +} + +int FAST_FUNC create_and_bind_dgram_or_die(const char *bindaddr, int port) +{ + return create_and_bind_or_die(bindaddr, port, SOCK_DGRAM); +} + + +int FAST_FUNC create_and_connect_stream_or_die(const char *peer, int port) +{ + int fd; + len_and_sockaddr *lsa; + + lsa = xhost2sockaddr(peer, port); + fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0); + setsockopt_reuseaddr(fd); + xconnect(fd, &lsa->u.sa, lsa->len); + free(lsa); + return fd; +} + +int FAST_FUNC xconnect_stream(const len_and_sockaddr *lsa) +{ + int fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0); + xconnect(fd, &lsa->u.sa, lsa->len); + return fd; +} + +/* We hijack this constant to mean something else */ +/* It doesn't hurt because we will add this bit anyway */ +#define IGNORE_PORT NI_NUMERICSERV +static char* FAST_FUNC sockaddr2str(const struct sockaddr *sa, int flags) +{ + char host[128]; + char serv[16]; + int rc; + socklen_t salen; + + salen = LSA_SIZEOF_SA; +#if ENABLE_FEATURE_IPV6 + if (sa->sa_family == AF_INET) + salen = sizeof(struct sockaddr_in); + if (sa->sa_family == AF_INET6) + salen = sizeof(struct sockaddr_in6); +#endif + rc = getnameinfo(sa, salen, + host, sizeof(host), + /* can do ((flags & IGNORE_PORT) ? NULL : serv) but why bother? */ + serv, sizeof(serv), + /* do not resolve port# into service _name_ */ + flags | NI_NUMERICSERV + ); + if (rc) + return NULL; + if (flags & IGNORE_PORT) + return xstrdup(host); +#if ENABLE_FEATURE_IPV6 + if (sa->sa_family == AF_INET6) { + if (strchr(host, ':')) /* heh, it's not a resolved hostname */ + return xasprintf("[%s]:%s", host, serv); + /*return xasprintf("%s:%s", host, serv);*/ + /* - fall through instead */ + } +#endif + /* For now we don't support anything else, so it has to be INET */ + /*if (sa->sa_family == AF_INET)*/ + return xasprintf("%s:%s", host, serv); + /*return xstrdup(host);*/ +} + +char* FAST_FUNC xmalloc_sockaddr2host(const struct sockaddr *sa) +{ + return sockaddr2str(sa, 0); +} + +char* FAST_FUNC xmalloc_sockaddr2host_noport(const struct sockaddr *sa) +{ + return sockaddr2str(sa, IGNORE_PORT); +} + +char* FAST_FUNC xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa) +{ + return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT); +} +char* FAST_FUNC xmalloc_sockaddr2dotted(const struct sockaddr *sa) +{ + return sockaddr2str(sa, NI_NUMERICHOST); +} + +char* FAST_FUNC xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) +{ + return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT); +} diff --git a/libbb/xfunc_die.c b/libbb/xfunc_die.c new file mode 100644 index 0000000..ba9fe93 --- /dev/null +++ b/libbb/xfunc_die.c @@ -0,0 +1,40 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2008 by Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +/* Keeping it separate allows to NOT suck in stdio for VERY small applets. + * Try building busybox with only "true" enabled... */ + +#include "libbb.h" + +int die_sleep; +#if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_HUSH +jmp_buf die_jmp; +#endif + +void FAST_FUNC xfunc_die(void) +{ + if (die_sleep) { + if ((ENABLE_FEATURE_PREFER_APPLETS || ENABLE_HUSH) + && die_sleep < 0 + ) { + /* Special case. We arrive here if NOFORK applet + * calls xfunc, which then decides to die. + * We don't die, but jump instead back to caller. + * NOFORK applets still cannot carelessly call xfuncs: + * p = xmalloc(10); + * q = xmalloc(10); // BUG! if this dies, we leak p! + */ + /* -2222 means "zero" (longjmp can't pass 0) + * run_nofork_applet() catches -2222. */ + longjmp(die_jmp, xfunc_error_retval ? xfunc_error_retval : -2222); + } + sleep(die_sleep); + } + exit(xfunc_error_retval); +} diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c new file mode 100644 index 0000000..e2aee13 --- /dev/null +++ b/libbb/xfuncs.c @@ -0,0 +1,296 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * Copyright (C) 2006 Rob Landley + * Copyright (C) 2006 Denys Vlasenko + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ + +/* We need to have separate xfuncs.c and xfuncs_printf.c because + * with current linkers, even with section garbage collection, + * if *.o module references any of XXXprintf functions, you pull in + * entire printf machinery. Even if you do not use the function + * which uses XXXprintf. + * + * xfuncs.c contains functions (not necessarily xfuncs) + * which do not pull in printf, directly or indirectly. + * xfunc_printf.c contains those which do. + * + * TODO: move xmalloc() and xatonum() here. + */ + +#include "libbb.h" + +/* Turn on nonblocking I/O on a fd */ +int FAST_FUNC ndelay_on(int fd) +{ + return fcntl(fd, F_SETFL, fcntl(fd,F_GETFL) | O_NONBLOCK); +} + +int FAST_FUNC ndelay_off(int fd) +{ + return fcntl(fd, F_SETFL, fcntl(fd,F_GETFL) & ~O_NONBLOCK); +} + +int FAST_FUNC close_on_exec_on(int fd) +{ + return fcntl(fd, F_SETFD, FD_CLOEXEC); +} + +/* Convert unsigned long long value into compact 4-char + * representation. Examples: "1234", "1.2k", " 27M", "123T" + * String is not terminated (buf[4] is untouched) */ +void FAST_FUNC smart_ulltoa4(unsigned long long ul, char buf[5], const char *scale) +{ + const char *fmt; + char c; + unsigned v, u, idx = 0; + + if (ul > 9999) { // do not scale if 9999 or less + ul *= 10; + do { + ul /= 1024; + idx++; + } while (ul >= 10000); + } + v = ul; // ullong divisions are expensive, avoid them + + fmt = " 123456789"; + u = v / 10; + v = v % 10; + if (!idx) { + // 9999 or less: use "1234" format + // u is value/10, v is last digit + c = buf[0] = " 123456789"[u/100]; + if (c != ' ') fmt = "0123456789"; + c = buf[1] = fmt[u/10%10]; + if (c != ' ') fmt = "0123456789"; + buf[2] = fmt[u%10]; + buf[3] = "0123456789"[v]; + } else { + // u is value, v is 1/10ths (allows for 9.2M format) + if (u >= 10) { + // value is >= 10: use "123M', " 12M" formats + c = buf[0] = " 123456789"[u/100]; + if (c != ' ') fmt = "0123456789"; + v = u % 10; + u = u / 10; + buf[1] = fmt[u%10]; + } else { + // value is < 10: use "9.2M" format + buf[0] = "0123456789"[u]; + buf[1] = '.'; + } + buf[2] = "0123456789"[v]; + buf[3] = scale[idx]; /* typically scale = " kmgt..." */ + } +} + +/* Convert unsigned long long value into compact 5-char representation. + * String is not terminated (buf[5] is untouched) */ +void FAST_FUNC smart_ulltoa5(unsigned long long ul, char buf[6], const char *scale) +{ + const char *fmt; + char c; + unsigned v, u, idx = 0; + + if (ul > 99999) { // do not scale if 99999 or less + ul *= 10; + do { + ul /= 1024; + idx++; + } while (ul >= 100000); + } + v = ul; // ullong divisions are expensive, avoid them + + fmt = " 123456789"; + u = v / 10; + v = v % 10; + if (!idx) { + // 99999 or less: use "12345" format + // u is value/10, v is last digit + c = buf[0] = " 123456789"[u/1000]; + if (c != ' ') fmt = "0123456789"; + c = buf[1] = fmt[u/100%10]; + if (c != ' ') fmt = "0123456789"; + c = buf[2] = fmt[u/10%10]; + if (c != ' ') fmt = "0123456789"; + buf[3] = fmt[u%10]; + buf[4] = "0123456789"[v]; + } else { + // value has been scaled into 0..9999.9 range + // u is value, v is 1/10ths (allows for 92.1M format) + if (u >= 100) { + // value is >= 100: use "1234M', " 123M" formats + c = buf[0] = " 123456789"[u/1000]; + if (c != ' ') fmt = "0123456789"; + c = buf[1] = fmt[u/100%10]; + if (c != ' ') fmt = "0123456789"; + v = u % 10; + u = u / 10; + buf[2] = fmt[u%10]; + } else { + // value is < 100: use "92.1M" format + c = buf[0] = " 123456789"[u/10]; + if (c != ' ') fmt = "0123456789"; + buf[1] = fmt[u%10]; + buf[2] = '.'; + } + buf[3] = "0123456789"[v]; + buf[4] = scale[idx]; /* typically scale = " kmgt..." */ + } +} + + +// Convert unsigned integer to ascii, writing into supplied buffer. +// A truncated result contains the first few digits of the result ala strncpy. +// Returns a pointer past last generated digit, does _not_ store NUL. +void BUG_sizeof_unsigned_not_4(void); +char* FAST_FUNC utoa_to_buf(unsigned n, char *buf, unsigned buflen) +{ + unsigned i, out, res; + if (sizeof(unsigned) != 4) + BUG_sizeof_unsigned_not_4(); + if (buflen) { + out = 0; + for (i = 1000000000; i; i /= 10) { + res = n / i; + if (res || out || i == 1) { + if (!--buflen) break; + out++; + n -= res*i; + *buf++ = '0' + res; + } + } + } + return buf; +} + +/* Convert signed integer to ascii, like utoa_to_buf() */ +char* FAST_FUNC itoa_to_buf(int n, char *buf, unsigned buflen) +{ + if (buflen && n < 0) { + n = -n; + *buf++ = '-'; + buflen--; + } + return utoa_to_buf((unsigned)n, buf, buflen); +} + +// The following two functions use a static buffer, so calling either one a +// second time will overwrite previous results. +// +// The largest 32 bit integer is -2 billion plus null terminator, or 12 bytes. +// It so happens that sizeof(int) * 3 is enough for 32+ bits. +// (sizeof(int) * 3 + 2 is correct for any width, even 8-bit) + +static char local_buf[sizeof(int) * 3]; + +// Convert unsigned integer to ascii using a static buffer (returned). +char* FAST_FUNC utoa(unsigned n) +{ + *(utoa_to_buf(n, local_buf, sizeof(local_buf))) = '\0'; + + return local_buf; +} + +/* Convert signed integer to ascii using a static buffer (returned). */ +char* FAST_FUNC itoa(int n) +{ + *(itoa_to_buf(n, local_buf, sizeof(local_buf))) = '\0'; + + return local_buf; +} + +/* Emit a string of hex representation of bytes */ +char* FAST_FUNC bin2hex(char *p, const char *cp, int count) +{ + while (count) { + unsigned char c = *cp++; + /* put lowercase hex digits */ + *p++ = 0x20 | bb_hexdigits_upcase[c >> 4]; + *p++ = 0x20 | bb_hexdigits_upcase[c & 0xf]; + count--; + } + return p; +} + +/* Return how long the file at fd is, if there's any way to determine it. */ +#ifdef UNUSED +off_t FAST_FUNC fdlength(int fd) +{ + off_t bottom = 0, top = 0, pos; + long size; + + // If the ioctl works for this, return it. + + if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512; + + // FIXME: explain why lseek(SEEK_END) is not used here! + + // If not, do a binary search for the last location we can read. (Some + // block devices don't do BLKGETSIZE right.) + + do { + char temp; + + pos = bottom + (top - bottom) / 2; + + // If we can read from the current location, it's bigger. + + if (lseek(fd, pos, SEEK_SET)>=0 && safe_read(fd, &temp, 1)==1) { + if (bottom == top) bottom = top = (top+1) * 2; + else bottom = pos; + + // If we can't, it's smaller. + + } else { + if (bottom == top) { + if (!top) return 0; + bottom = top/2; + } + else top = pos; + } + } while (bottom + 1 != top); + + return pos + 1; +} +#endif + +/* It is perfectly ok to pass in a NULL for either width or for + * height, in which case that value will not be set. */ +int FAST_FUNC get_terminal_width_height(int fd, unsigned *width, unsigned *height) +{ + struct winsize win = { 0, 0, 0, 0 }; + int ret = ioctl(fd, TIOCGWINSZ, &win); + + if (height) { + if (!win.ws_row) { + char *s = getenv("LINES"); + if (s) win.ws_row = atoi(s); + } + if (win.ws_row <= 1 || win.ws_row >= 30000) + win.ws_row = 24; + *height = (int) win.ws_row; + } + + if (width) { + if (!win.ws_col) { + char *s = getenv("COLUMNS"); + if (s) win.ws_col = atoi(s); + } + if (win.ws_col <= 1 || win.ws_col >= 30000) + win.ws_col = 80; + *width = (int) win.ws_col; + } + + return ret; +} + +int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp) +{ + return tcsetattr(STDIN_FILENO, TCSANOW, tp); +} diff --git a/libbb/xfuncs_printf.c b/libbb/xfuncs_printf.c new file mode 100644 index 0000000..108e140 --- /dev/null +++ b/libbb/xfuncs_printf.c @@ -0,0 +1,521 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen + * Copyright (C) 2006 Rob Landley + * Copyright (C) 2006 Denys Vlasenko + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ + +/* We need to have separate xfuncs.c and xfuncs_printf.c because + * with current linkers, even with section garbage collection, + * if *.o module references any of XXXprintf functions, you pull in + * entire printf machinery. Even if you do not use the function + * which uses XXXprintf. + * + * xfuncs.c contains functions (not necessarily xfuncs) + * which do not pull in printf, directly or indirectly. + * xfunc_printf.c contains those which do. + */ + +#include "libbb.h" + + +/* All the functions starting with "x" call bb_error_msg_and_die() if they + * fail, so callers never need to check for errors. If it returned, it + * succeeded. */ + +#ifndef DMALLOC +/* dmalloc provides variants of these that do abort() on failure. + * Since dmalloc's prototypes overwrite the impls here as they are + * included after these prototypes in libbb.h, all is well. + */ +// Warn if we can't allocate size bytes of memory. +void* FAST_FUNC malloc_or_warn(size_t size) +{ + void *ptr = malloc(size); + if (ptr == NULL && size != 0) + bb_error_msg(bb_msg_memory_exhausted); + return ptr; +} + +// Die if we can't allocate size bytes of memory. +void* FAST_FUNC xmalloc(size_t size) +{ + void *ptr = malloc(size); + if (ptr == NULL && size != 0) + bb_error_msg_and_die(bb_msg_memory_exhausted); + return ptr; +} + +// Die if we can't resize previously allocated memory. (This returns a pointer +// to the new memory, which may or may not be the same as the old memory. +// It'll copy the contents to a new chunk and free the old one if necessary.) +void* FAST_FUNC xrealloc(void *ptr, size_t size) +{ + ptr = realloc(ptr, size); + if (ptr == NULL && size != 0) + bb_error_msg_and_die(bb_msg_memory_exhausted); + return ptr; +} +#endif /* DMALLOC */ + +// Die if we can't allocate and zero size bytes of memory. +void* FAST_FUNC xzalloc(size_t size) +{ + void *ptr = xmalloc(size); + memset(ptr, 0, size); + return ptr; +} + +// Die if we can't copy a string to freshly allocated memory. +char* FAST_FUNC xstrdup(const char *s) +{ + char *t; + + if (s == NULL) + return NULL; + + t = strdup(s); + + if (t == NULL) + bb_error_msg_and_die(bb_msg_memory_exhausted); + + return t; +} + +// Die if we can't allocate n+1 bytes (space for the null terminator) and copy +// the (possibly truncated to length n) string into it. +char* FAST_FUNC xstrndup(const char *s, int n) +{ + int m; + char *t; + + if (ENABLE_DEBUG && s == NULL) + bb_error_msg_and_die("xstrndup bug"); + + /* We can just xmalloc(n+1) and strncpy into it, */ + /* but think about xstrndup("abc", 10000) wastage! */ + m = n; + t = (char*) s; + while (m) { + if (!*t) break; + m--; + t++; + } + n -= m; + t = xmalloc(n + 1); + t[n] = '\0'; + + return memcpy(t, s, n); +} + +// Die if we can't open a file and return a FILE* to it. +// Notice we haven't got xfread(), This is for use with fscanf() and friends. +FILE* FAST_FUNC xfopen(const char *path, const char *mode) +{ + FILE *fp = fopen(path, mode); + if (fp == NULL) + bb_perror_msg_and_die("can't open '%s'", path); + return fp; +} + +// Die if we can't open a file and return a fd. +int FAST_FUNC xopen3(const char *pathname, int flags, int mode) +{ + int ret; + + ret = open(pathname, flags, mode); + if (ret < 0) { + bb_perror_msg_and_die("can't open '%s'", pathname); + } + return ret; +} + +// Die if we can't open an existing file and return a fd. +int FAST_FUNC xopen(const char *pathname, int flags) +{ + return xopen3(pathname, flags, 0666); +} + +// Warn if we can't open a file and return a fd. +int FAST_FUNC open3_or_warn(const char *pathname, int flags, int mode) +{ + int ret; + + ret = open(pathname, flags, mode); + if (ret < 0) { + bb_perror_msg("can't open '%s'", pathname); + } + return ret; +} + +// Warn if we can't open a file and return a fd. +int FAST_FUNC open_or_warn(const char *pathname, int flags) +{ + return open3_or_warn(pathname, flags, 0666); +} + +void FAST_FUNC xunlink(const char *pathname) +{ + if (unlink(pathname)) + bb_perror_msg_and_die("can't remove file '%s'", pathname); +} + +void FAST_FUNC xrename(const char *oldpath, const char *newpath) +{ + if (rename(oldpath, newpath)) + bb_perror_msg_and_die("can't move '%s' to '%s'", oldpath, newpath); +} + +int FAST_FUNC rename_or_warn(const char *oldpath, const char *newpath) +{ + int n = rename(oldpath, newpath); + if (n) + bb_perror_msg("can't move '%s' to '%s'", oldpath, newpath); + return n; +} + +void FAST_FUNC xpipe(int filedes[2]) +{ + if (pipe(filedes)) + bb_perror_msg_and_die("can't create pipe"); +} + +void FAST_FUNC xdup2(int from, int to) +{ + if (dup2(from, to) != to) + bb_perror_msg_and_die("can't duplicate file descriptor"); +} + +// "Renumber" opened fd +void FAST_FUNC xmove_fd(int from, int to) +{ + if (from == to) + return; + xdup2(from, to); + close(from); +} + +// Die with an error message if we can't write the entire buffer. +void FAST_FUNC xwrite(int fd, const void *buf, size_t count) +{ + if (count) { + ssize_t size = full_write(fd, buf, count); + if ((size_t)size != count) + bb_error_msg_and_die("short write"); + } +} + +// Die with an error message if we can't lseek to the right spot. +off_t FAST_FUNC xlseek(int fd, off_t offset, int whence) +{ + off_t off = lseek(fd, offset, whence); + if (off == (off_t)-1) { + if (whence == SEEK_SET) + bb_perror_msg_and_die("lseek(%"OFF_FMT"u)", offset); + bb_perror_msg_and_die("lseek"); + } + return off; +} + +// Die with supplied filename if this FILE* has ferror set. +void FAST_FUNC die_if_ferror(FILE *fp, const char *fn) +{ + if (ferror(fp)) { + /* ferror doesn't set useful errno */ + bb_error_msg_and_die("%s: I/O error", fn); + } +} + +// Die with an error message if stdout has ferror set. +void FAST_FUNC die_if_ferror_stdout(void) +{ + die_if_ferror(stdout, bb_msg_standard_output); +} + +// Die with an error message if we have trouble flushing stdout. +void FAST_FUNC xfflush_stdout(void) +{ + if (fflush(stdout)) { + bb_perror_msg_and_die(bb_msg_standard_output); + } +} + + +int FAST_FUNC bb_putchar(int ch) +{ + /* time.c needs putc(ch, stdout), not putchar(ch). + * it does "stdout = stderr;", but then glibc's putchar() + * doesn't work as expected. bad glibc, bad */ + return putc(ch, stdout); +} + +/* Die with an error message if we can't copy an entire FILE* to stdout, + * then close that file. */ +void FAST_FUNC xprint_and_close_file(FILE *file) +{ + fflush(stdout); + // copyfd outputs error messages for us. + if (bb_copyfd_eof(fileno(file), 1) == -1) + xfunc_die(); + + fclose(file); +} + +// Die with an error message if we can't malloc() enough space and do an +// sprintf() into that space. +char* FAST_FUNC xasprintf(const char *format, ...) +{ + va_list p; + int r; + char *string_ptr; + +#if 1 + // GNU extension + va_start(p, format); + r = vasprintf(&string_ptr, format, p); + va_end(p); +#else + // Bloat for systems that haven't got the GNU extension. + va_start(p, format); + r = vsnprintf(NULL, 0, format, p); + va_end(p); + string_ptr = xmalloc(r+1); + va_start(p, format); + r = vsnprintf(string_ptr, r+1, format, p); + va_end(p); +#endif + + if (r < 0) + bb_error_msg_and_die(bb_msg_memory_exhausted); + return string_ptr; +} + +#if 0 /* If we will ever meet a libc which hasn't [f]dprintf... */ +int FAST_FUNC fdprintf(int fd, const char *format, ...) +{ + va_list p; + int r; + char *string_ptr; + +#if 1 + // GNU extension + va_start(p, format); + r = vasprintf(&string_ptr, format, p); + va_end(p); +#else + // Bloat for systems that haven't got the GNU extension. + va_start(p, format); + r = vsnprintf(NULL, 0, format, p) + 1; + va_end(p); + string_ptr = malloc(r); + if (string_ptr) { + va_start(p, format); + r = vsnprintf(string_ptr, r, format, p); + va_end(p); + } +#endif + + if (r >= 0) { + full_write(fd, string_ptr, r); + free(string_ptr); + } + return r; +} +#endif + +void FAST_FUNC xsetenv(const char *key, const char *value) +{ + if (setenv(key, value, 1)) + bb_error_msg_and_die(bb_msg_memory_exhausted); +} + +// Die with an error message if we can't set gid. (Because resource limits may +// limit this user to a given number of processes, and if that fills up the +// setgid() will fail and we'll _still_be_root_, which is bad.) +void FAST_FUNC xsetgid(gid_t gid) +{ + if (setgid(gid)) bb_perror_msg_and_die("setgid"); +} + +// Die with an error message if we can't set uid. (See xsetgid() for why.) +void FAST_FUNC xsetuid(uid_t uid) +{ + if (setuid(uid)) bb_perror_msg_and_die("setuid"); +} + +// Die if we can't chdir to a new path. +void FAST_FUNC xchdir(const char *path) +{ + if (chdir(path)) + bb_perror_msg_and_die("chdir(%s)", path); +} + +void FAST_FUNC xchroot(const char *path) +{ + if (chroot(path)) + bb_perror_msg_and_die("can't change root directory to %s", path); +} + +// Print a warning message if opendir() fails, but don't die. +DIR* FAST_FUNC warn_opendir(const char *path) +{ + DIR *dp; + + dp = opendir(path); + if (!dp) + bb_perror_msg("can't open '%s'", path); + return dp; +} + +// Die with an error message if opendir() fails. +DIR* FAST_FUNC xopendir(const char *path) +{ + DIR *dp; + + dp = opendir(path); + if (!dp) + bb_perror_msg_and_die("can't open '%s'", path); + return dp; +} + +// Die with an error message if we can't open a new socket. +int FAST_FUNC xsocket(int domain, int type, int protocol) +{ + int r = socket(domain, type, protocol); + + if (r < 0) { + /* Hijack vaguely related config option */ +#if ENABLE_VERBOSE_RESOLUTION_ERRORS + const char *s = "INET"; + if (domain == AF_PACKET) s = "PACKET"; + if (domain == AF_NETLINK) s = "NETLINK"; +USE_FEATURE_IPV6(if (domain == AF_INET6) s = "INET6";) + bb_perror_msg_and_die("socket(AF_%s)", s); +#else + bb_perror_msg_and_die("socket"); +#endif + } + + return r; +} + +// Die with an error message if we can't bind a socket to an address. +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"); +} + +// Die with an error message if we can't listen for connections on a socket. +void FAST_FUNC xlisten(int s, int backlog) +{ + if (listen(s, backlog)) bb_perror_msg_and_die("listen"); +} + +/* Die with an error message if sendto failed. + * Return bytes sent otherwise */ +ssize_t FAST_FUNC xsendto(int s, const void *buf, size_t len, const struct sockaddr *to, + socklen_t tolen) +{ + ssize_t ret = sendto(s, buf, len, 0, to, tolen); + if (ret < 0) { + if (ENABLE_FEATURE_CLEAN_UP) + close(s); + bb_perror_msg_and_die("sendto"); + } + return ret; +} + +// xstat() - a stat() which dies on failure with meaningful error message +void FAST_FUNC xstat(const char *name, struct stat *stat_buf) +{ + if (stat(name, stat_buf)) + bb_perror_msg_and_die("can't stat '%s'", name); +} + +// selinux_or_die() - die if SELinux is disabled. +void FAST_FUNC selinux_or_die(void) +{ +#if ENABLE_SELINUX + int rc = is_selinux_enabled(); + if (rc == 0) { + bb_error_msg_and_die("SELinux is disabled"); + } else if (rc < 0) { + bb_error_msg_and_die("is_selinux_enabled() failed"); + } +#else + bb_error_msg_and_die("SELinux support is disabled"); +#endif +} + +int FAST_FUNC ioctl_or_perror_and_die(int fd, unsigned request, void *argp, const char *fmt,...) +{ + int ret; + va_list p; + + ret = ioctl(fd, request, argp); + if (ret < 0) { + va_start(p, fmt); + bb_verror_msg(fmt, p, strerror(errno)); + /* xfunc_die can actually longjmp, so be nice */ + va_end(p); + xfunc_die(); + } + return ret; +} + +int FAST_FUNC ioctl_or_perror(int fd, unsigned request, void *argp, const char *fmt,...) +{ + va_list p; + int ret = ioctl(fd, request, argp); + + if (ret < 0) { + va_start(p, fmt); + bb_verror_msg(fmt, p, strerror(errno)); + va_end(p); + } + return ret; +} + +#if ENABLE_IOCTL_HEX2STR_ERROR +int FAST_FUNC bb_ioctl_or_warn(int fd, unsigned request, void *argp, const char *ioctl_name) +{ + int ret; + + ret = ioctl(fd, request, argp); + if (ret < 0) + bb_simple_perror_msg(ioctl_name); + return ret; +} +int FAST_FUNC bb_xioctl(int fd, unsigned request, void *argp, const char *ioctl_name) +{ + int ret; + + ret = ioctl(fd, request, argp); + if (ret < 0) + bb_simple_perror_msg_and_die(ioctl_name); + return ret; +} +#else +int FAST_FUNC bb_ioctl_or_warn(int fd, unsigned request, void *argp) +{ + int ret; + + ret = ioctl(fd, request, argp); + if (ret < 0) + bb_perror_msg("ioctl %#x failed", request); + return ret; +} +int FAST_FUNC bb_xioctl(int fd, unsigned request, void *argp) +{ + int ret; + + ret = ioctl(fd, request, argp); + if (ret < 0) + bb_perror_msg_and_die("ioctl %#x failed", request); + return ret; +} +#endif diff --git a/libbb/xgetcwd.c b/libbb/xgetcwd.c new file mode 100644 index 0000000..eefe1d6 --- /dev/null +++ b/libbb/xgetcwd.c @@ -0,0 +1,41 @@ +/* vi: set sw=4 ts=4: */ +/* + * xgetcwd.c -- return current directory with unlimited length + * Copyright (C) 1992, 1996 Free Software Foundation, Inc. + * Written by David MacKenzie . + * + * Special function for busybox written by Vladimir Oleynik +*/ + +#include "libbb.h" + +/* Return the current directory, newly allocated, arbitrarily long. + Return NULL and set errno on error. + If argument is not NULL (previous usage allocate memory), call free() +*/ + +char* FAST_FUNC +xrealloc_getcwd_or_warn(char *cwd) +{ +#define PATH_INCR 64 + + char *ret; + unsigned path_max; + + path_max = 128; /* 128 + 64 should be enough for 99% of cases */ + + while (1) { + path_max += PATH_INCR; + cwd = xrealloc(cwd, path_max); + ret = getcwd(cwd, path_max); + if (ret == NULL) { + if (errno == ERANGE) + continue; + free(cwd); + bb_perror_msg("getcwd"); + return NULL; + } + cwd = xrealloc(cwd, strlen(cwd) + 1); + return cwd; + } +} diff --git a/libbb/xgethostbyname.c b/libbb/xgethostbyname.c new file mode 100644 index 0000000..f1839f7 --- /dev/null +++ b/libbb/xgethostbyname.c @@ -0,0 +1,19 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini xgethostbyname implementation. + * + * Copyright (C) 2001 Matt Kraai . + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +//#include +#include "libbb.h" + +struct hostent* FAST_FUNC xgethostbyname(const char *name) +{ + struct hostent *retval = gethostbyname(name); + if (!retval) + bb_herror_msg_and_die("%s", name); + return retval; +} diff --git a/libbb/xreadlink.c b/libbb/xreadlink.c new file mode 100644 index 0000000..2cfc575 --- /dev/null +++ b/libbb/xreadlink.c @@ -0,0 +1,111 @@ +/* vi: set sw=4 ts=4: */ +/* + * xreadlink.c - safe implementation of readlink. + * Returns a NULL on failure... + */ + +#include "libbb.h" + +/* + * NOTE: This function returns a malloced char* that you will have to free + * yourself. + */ +char* FAST_FUNC xmalloc_readlink(const char *path) +{ + enum { GROWBY = 80 }; /* how large we will grow strings by */ + + char *buf = NULL; + int bufsize = 0, readsize = 0; + + do { + bufsize += GROWBY; + buf = xrealloc(buf, bufsize); + readsize = readlink(path, buf, bufsize); + if (readsize == -1) { + free(buf); + return NULL; + } + } while (bufsize < readsize + 1); + + buf[readsize] = '\0'; + + return buf; +} + +/* + * This routine is not the same as realpath(), which + * canonicalizes the given path completely. This routine only + * follows trailing symlinks until a real file is reached and + * returns its name. If the path ends in a dangling link or if + * the target doesn't exist, the path is returned in any case. + * Intermediate symlinks in the path are not expanded -- only + * those at the tail. + * A malloced char* is returned, which must be freed by the caller. + */ +char* FAST_FUNC xmalloc_follow_symlinks(const char *path) +{ + char *buf; + char *lpc; + char *linkpath; + int bufsize; + int looping = MAXSYMLINKS + 1; + + buf = xstrdup(path); + goto jump_in; + + while (1) { + linkpath = xmalloc_readlink(buf); + if (!linkpath) { + /* not a symlink, or doesn't exist */ + if (errno == EINVAL || errno == ENOENT) + return buf; + goto free_buf_ret_null; + } + + if (!--looping) { + free(linkpath); + free_buf_ret_null: + free(buf); + return NULL; + } + + if (*linkpath != '/') { + bufsize += strlen(linkpath); + buf = xrealloc(buf, bufsize); + lpc = bb_get_last_path_component_strip(buf); + strcpy(lpc, linkpath); + free(linkpath); + } else { + free(buf); + buf = linkpath; + jump_in: + bufsize = strlen(buf) + 1; + } + } +} + +char* FAST_FUNC xmalloc_readlink_or_warn(const char *path) +{ + char *buf = xmalloc_readlink(path); + if (!buf) { + /* EINVAL => "file: Invalid argument" => puzzled user */ + bb_error_msg("%s: cannot read link (not a symlink?)", path); + } + return buf; +} + +/* UNUSED */ +#if 0 +char* FAST_FUNC xmalloc_realpath(const char *path) +{ +#if defined(__GLIBC__) && !defined(__UCLIBC__) + /* glibc provides a non-standard extension */ + return realpath(path, NULL); +#else + char buf[PATH_MAX+1]; + + /* on error returns NULL (xstrdup(NULL) ==NULL) */ + return xstrdup(realpath(path, buf)); +#endif +} +#endif diff --git a/libbb/xrealloc_vector.c b/libbb/xrealloc_vector.c new file mode 100644 index 0000000..bbd5ab8 --- /dev/null +++ b/libbb/xrealloc_vector.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2008 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* Resize (grow) malloced vector. + * + * #define magic packed two parameters into one: + * sizeof = sizeof_and_shift >> 8 + * shift = (sizeof_and_shift) & 0xff + * + * Lets say shift = 4. 1 << 4 == 0x10. + * If idx == 0, 0x10, 0x20 etc, vector[] is resized to next higher + * idx step, plus one: if idx == 0x20, vector[] is resized to 0x31, + * thus last usable element is vector[0x30]. + * + * In other words: after xrealloc_vector(v, 4, idx) it's ok to use + * at least v[idx] and v[idx+1], for all idx values. + * + * New elements are zeroed out, but only if realloc was done + * (not on every call). You can depend on v[idx] and v[idx+1] being + * zeroed out if you use it like this: + * v = xrealloc_vector(v, 4, idx); + * v[idx].some_fields = ...; - the rest stays 0/NULL + * idx++; + * If you do not advance idx like above, you should be more careful. + * Next call to xrealloc_vector(v, 4, idx) may or may not zero out v[idx]. + */ +void* FAST_FUNC xrealloc_vector_helper(void *vector, unsigned sizeof_and_shift, int idx) +{ + int mask = 1 << (uint8_t)sizeof_and_shift; + + if (!(idx & (mask - 1))) { + sizeof_and_shift >>= 8; /* sizeof(vector[0]) */ + vector = xrealloc(vector, sizeof_and_shift * (idx + mask + 1)); + memset((char*)vector + (sizeof_and_shift * idx), 0, sizeof_and_shift * (mask + 1)); + } + return vector; +} diff --git a/libbb/xregcomp.c b/libbb/xregcomp.c new file mode 100644 index 0000000..61efb5b --- /dev/null +++ b/libbb/xregcomp.c @@ -0,0 +1,32 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. + * If you wrote this, please acknowledge your work. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" +#include "xregex.h" + +char* FAST_FUNC regcomp_or_errmsg(regex_t *preg, const char *regex, int cflags) +{ + int ret = regcomp(preg, regex, cflags); + if (ret) { + int errmsgsz = regerror(ret, preg, NULL, 0); + char *errmsg = xmalloc(errmsgsz); + regerror(ret, preg, errmsg, errmsgsz); + return errmsg; + } + return NULL; +} + +void FAST_FUNC xregcomp(regex_t *preg, const char *regex, int cflags) +{ + char *errmsg = regcomp_or_errmsg(preg, regex, cflags); + if (errmsg) { + bb_error_msg_and_die("bad regex '%s': %s", regex, errmsg); + } +} -- cgit v1.2.3