From 8d42f86b146871ae4c4cafd3801a85f381249a14 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Wed, 27 Dec 2006 04:35:04 +0000 Subject: Correcting branch name to be like previous ones --- libbb/Config.in | 29 ++ libbb/Kbuild | 117 ++++++ libbb/README | 11 + libbb/ask_confirmation.c | 36 ++ libbb/bb_askpass.c | 75 ++++ libbb/bb_do_delay.c | 24 ++ libbb/bb_pwd.c | 130 +++++++ libbb/bb_strtonum.c | 155 ++++++++ libbb/change_identity.c | 51 +++ libbb/chomp.c | 19 + libbb/compare_string_array.c | 37 ++ libbb/concat_path_file.c | 27 ++ libbb/concat_subpath_file.c | 23 ++ libbb/copy_file.c | 282 ++++++++++++++ libbb/copyfd.c | 86 +++++ libbb/correct_password.c | 65 ++++ libbb/crc32.c | 39 ++ libbb/create_icmp6_socket.c | 39 ++ libbb/create_icmp_socket.c | 37 ++ libbb/default_error_retval.c | 19 + libbb/device_open.c | 33 ++ libbb/dump.c | 803 ++++++++++++++++++++++++++++++++++++++++ libbb/error_msg.c | 23 ++ libbb/error_msg_and_die.c | 28 ++ libbb/execable.c | 61 +++ libbb/fclose_nonstdin.c | 23 ++ libbb/fflush_stdout_and_exit.c | 24 ++ libbb/fgets_str.c | 53 +++ libbb/find_mount_point.c | 53 +++ libbb/find_pid_by_name.c | 55 +++ libbb/find_root_device.c | 33 ++ libbb/full_write.c | 38 ++ libbb/get_console.c | 77 ++++ libbb/get_last_path_component.c | 32 ++ libbb/get_line_from_file.c | 68 ++++ libbb/getopt32.c | 529 ++++++++++++++++++++++++++ libbb/herror_msg.c | 22 ++ libbb/herror_msg_and_die.c | 25 ++ libbb/human_readable.c | 89 +++++ libbb/inet_common.c | 235 ++++++++++++ libbb/info_msg.c | 23 ++ libbb/inode_hash.c | 88 +++++ libbb/isdirectory.c | 39 ++ libbb/kernel_version.c | 37 ++ libbb/last_char_is.c | 24 ++ libbb/llist.c | 78 ++++ libbb/login.c | 105 ++++++ libbb/loop.c | 149 ++++++++ libbb/make_directory.c | 104 ++++++ libbb/makedev.c | 20 + libbb/md5.c | 450 ++++++++++++++++++++++ libbb/messages.c | 53 +++ libbb/mode_string.c | 128 +++++++ libbb/mtab.c | 52 +++ libbb/mtab_file.c | 17 + libbb/obscure.c | 170 +++++++++ libbb/parse_mode.c | 164 ++++++++ libbb/perror_msg.c | 23 ++ libbb/perror_msg_and_die.c | 26 ++ libbb/perror_nomsg.c | 16 + libbb/perror_nomsg_and_die.c | 17 + libbb/process_escape_sequence.c | 89 +++++ libbb/procps.c | 255 +++++++++++++ libbb/pw_encrypt.c | 29 ++ libbb/read.c | 134 +++++++ libbb/recursive_action.c | 126 +++++++ libbb/remove_file.c | 111 ++++++ libbb/restricted_shell.c | 57 +++ libbb/run_shell.c | 101 +++++ libbb/safe_strncpy.c | 21 ++ libbb/safe_write.c | 26 ++ libbb/setup_environment.c | 83 +++++ libbb/sha1.c | 178 +++++++++ libbb/simplify_path.c | 50 +++ libbb/skip_whitespace.c | 18 + libbb/speed_table.c | 117 ++++++ libbb/trim.c | 31 ++ libbb/u_signal_names.c | 58 +++ libbb/uuencode.c | 63 ++++ libbb/vdprintf.c | 25 ++ libbb/verror_msg.c | 46 +++ libbb/vfork_daemon_rexec.c | 67 ++++ libbb/vherror_msg.c | 15 + libbb/vinfo_msg.c | 26 ++ libbb/vperror_msg.c | 15 + libbb/warn_ignoring_args.c | 17 + libbb/wfopen.c | 20 + libbb/wfopen_input.c | 31 ++ libbb/xatonum.c | 69 ++++ libbb/xatonum_template.c | 185 +++++++++ libbb/xconnect.c | 153 ++++++++ libbb/xfuncs.c | 521 ++++++++++++++++++++++++++ libbb/xgetcwd.c | 44 +++ libbb/xgethostbyname.c | 19 + libbb/xgethostbyname2.c | 22 ++ libbb/xreadlink.c | 38 ++ libbb/xregcomp.c | 26 ++ 97 files changed, 8274 insertions(+) create mode 100644 libbb/Config.in create mode 100644 libbb/Kbuild create mode 100644 libbb/README create mode 100644 libbb/ask_confirmation.c create mode 100644 libbb/bb_askpass.c create mode 100644 libbb/bb_do_delay.c create mode 100644 libbb/bb_pwd.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/default_error_retval.c create mode 100644 libbb/device_open.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/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/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/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_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/process_escape_sequence.c create mode 100644 libbb/procps.c create mode 100644 libbb/pw_encrypt.c create mode 100644 libbb/read.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/run_shell.c create mode 100644 libbb/safe_strncpy.c create mode 100644 libbb/safe_write.c create mode 100644 libbb/setup_environment.c create mode 100644 libbb/sha1.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/trim.c create mode 100644 libbb/u_signal_names.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/vherror_msg.c create mode 100644 libbb/vinfo_msg.c create mode 100644 libbb/vperror_msg.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/xatonum.c create mode 100644 libbb/xatonum_template.c create mode 100644 libbb/xconnect.c create mode 100644 libbb/xfuncs.c create mode 100644 libbb/xgetcwd.c create mode 100644 libbb/xgethostbyname.c create mode 100644 libbb/xgethostbyname2.c create mode 100644 libbb/xreadlink.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 000000000..92ee55cbc --- /dev/null +++ b/libbb/Config.in @@ -0,0 +1,29 @@ +# +# 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 + +endmenu diff --git a/libbb/Kbuild b/libbb/Kbuild new file mode 100644 index 000000000..c15615302 --- /dev/null +++ b/libbb/Kbuild @@ -0,0 +1,117 @@ +# 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 += ask_confirmation.o +lib-y += bb_askpass.o +lib-y += bb_do_delay.o +lib-y += bb_pwd.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 += 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 += llist.o +lib-y += login.o +lib-y += make_directory.o +lib-y += makedev.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 += perror_msg.o +lib-y += perror_msg_and_die.o +lib-y += perror_nomsg.o +lib-y += perror_nomsg_and_die.o +lib-y += process_escape_sequence.o +lib-y += procps.o +lib-y += read.o +lib-y += recursive_action.o +lib-y += remove_file.o +lib-y += restricted_shell.o +lib-y += run_shell.o +lib-y += safe_strncpy.o +lib-y += safe_write.o +lib-y += setup_environment.o +lib-y += sha1.o +lib-y += simplify_path.o +lib-y += skip_whitespace.o +lib-y += speed_table.o +lib-y += trim.o +lib-y += u_signal_names.o +lib-y += uuencode.o +lib-y += vdprintf.o +lib-y += verror_msg.o +lib-y += vfork_daemon_rexec.o +lib-y += vherror_msg.o +lib-y += vinfo_msg.o +lib-y += vperror_msg.o +lib-y += warn_ignoring_args.o +lib-y += wfopen.o +lib-y += wfopen_input.o +lib-y += xatonum.o +lib-y += xconnect.o +lib-y += xfuncs.o +lib-y += xgetcwd.o +lib-y += xgethostbyname.o +lib-y += xgethostbyname2.o +lib-y += xreadlink.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 +lib-$(CONFIG_SULOGIN) += pw_encrypt.o +lib-$(CONFIG_FEATURE_HTTPD_AUTH_MD5) += pw_encrypt.o +lib-$(CONFIG_VLOCK) += correct_password.o +lib-$(CONFIG_SU) += correct_password.o +lib-$(CONFIG_LOGIN) += correct_password.o +lib-$(CONFIG_DF) += find_mount_point.o +lib-$(CONFIG_EJECT) += find_mount_point.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_LESS) += xregcomp.o +lib-$(CONFIG_DEVFSD) += xregcomp.o diff --git a/libbb/README b/libbb/README new file mode 100644 index 000000000..4f28f7e34 --- /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/ask_confirmation.c b/libbb/ask_confirmation.c new file mode 100644 index 000000000..4642fa036 --- /dev/null +++ b/libbb/ask_confirmation.c @@ -0,0 +1,36 @@ +/* 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 +#include +#include "libbb.h" + +int 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 000000000..097a0a290 --- /dev/null +++ b/libbb/bb_askpass.c @@ -0,0 +1,75 @@ +/* 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 +#include +#include +#include +#include +#include + +#include "libbb.h" + +/* do nothing signal handler */ +static void askpass_timeout(int ATTRIBUTE_UNUSED ignore) +{ +} + +char *bb_askpass(int timeout, const char * prompt) +{ + static char passwd[64]; + + char *ret; + int i; + struct sigaction sa; + struct termios old, new; + + tcgetattr(STDIN_FILENO, &old); + tcflush(STDIN_FILENO, TCIFLUSH); + + memset(passwd, 0, sizeof(passwd)); + + fputs(prompt, stdout); + fflush(stdout); + + tcgetattr(STDIN_FILENO, &new); + new.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY); + new.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP); + tcsetattr(STDIN_FILENO, TCSANOW, &new); + + if (timeout) { + sa.sa_flags = 0; + sa.sa_handler = askpass_timeout; + sigaction(SIGALRM, &sa, NULL); + alarm(timeout); + } + + ret = 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); + } + + tcsetattr(STDIN_FILENO, TCSANOW, &old); + puts(""); + fflush(stdout); + return ret; +} diff --git a/libbb/bb_do_delay.c b/libbb/bb_do_delay.c new file mode 100644 index 000000000..e14b67a19 --- /dev/null +++ b/libbb/bb_do_delay.c @@ -0,0 +1,24 @@ +/* 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 +#include +#include "libbb.h" + +void 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 000000000..b5125b0f4 --- /dev/null +++ b/libbb/bb_pwd.c @@ -0,0 +1,130 @@ +/* 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 +#include +#include +#include "libbb.h" + + /* + * if bufsize is > 0 char *buffer cannot be set to NULL. + * If idname is not NULL it is written on the static + * allocated buffer (and a pointer to it is returned). + * if idname is NULL, id as string is written to the static + * allocated buffer and NULL is returned. + * if bufsize is = 0 char *buffer can be set to NULL. + * If idname exists a pointer to it is returned, + * else NULL is returned. + * if bufsize is < 0 char *buffer can be set to NULL. + * If idname exists a pointer to it is returned, + * else an error message is printed and the program exits. + */ + +/* internal function for bb_getpwuid and bb_getgrgid */ +static char * bb_getug(char *buffer, char *idname, long id, int bufsize, 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; +} + + /* Hacked by Tito Ragusa (c) 2004 to make it more + * flexible : + * + * if bufsize is > 0 char *group cannot be set to NULL. + * On success groupname is written on static allocated buffer + * group (and a pointer to it is returned). + * On failure gid as string is written to static allocated + * buffer group and NULL is returned. + * if bufsize is = 0 char *group can be set to NULL. + * On success groupname is returned. + * On failure NULL is returned. + * if bufsize is < 0 char *group can be set to NULL. + * On success groupname is returned. + * On failure an error message is printed and + * the program exits. + */ + +/* gets a groupname given a gid */ +char * bb_getgrgid(char *group, long gid, int bufsize) +{ + struct group *mygroup = getgrgid(gid); + + return bb_getug(group, (mygroup) ? + mygroup->gr_name : (char *)mygroup, gid, bufsize, 'g'); +} + +/* returns a gid given a group name */ +long bb_xgetgrnam(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 bb_xgetpwnam(const char *name) +{ + struct passwd *myuser; + + myuser = getpwnam(name); + if (myuser==NULL) + bb_error_msg_and_die("unknown user name: %s", name); + + return myuser->pw_uid; +} + + /* Hacked by Tito Ragusa (c) 2004 to make it more + * flexible : + * + * if bufsize is > 0 char *name cannot be set to NULL. + * On success username is written on the static allocated + * buffer name (and a pointer to it is returned). + * On failure uid as string is written to the static + * allocated buffer name and NULL is returned. + * if bufsize is = 0 char *name can be set to NULL. + * On success username is returned. + * On failure NULL is returned. + * if bufsize is < 0 char *name can be set to NULL + * On success username is returned. + * On failure an error message is printed and + * the program exits. + */ + +/* gets a username given a uid */ +char * bb_getpwuid(char *name, long uid, int bufsize) +{ + struct passwd *myuser = getpwuid(uid); + + return bb_getug(name, myuser ? myuser->pw_name : (char *)myuser, + uid, bufsize, 'u'); +} + +unsigned long get_ug_id(const char *s, + long (*__bb_getxxnam)(const char *)) +{ + unsigned long r; + + r = bb_strtoul(s, NULL, 10); + if (errno) + r = __bb_getxxnam(s); + + return r; +} diff --git a/libbb/bb_strtonum.c b/libbb/bb_strtonum.c new file mode 100644 index 000000000..6fbd1f87d --- /dev/null +++ b/libbb/bb_strtonum.c @@ -0,0 +1,155 @@ +/* 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 othervise 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 ) + */ + +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; + + /* Check for the weird "feature": + * a "-" string is apparently a valid "number" for strto[u]l[l]! + * It returns zero and errno is 0! :( */ + if (endptr[-1] == '-') + return ret_ERANGE(); + + /* 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 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 bb_strtoll(const char *arg, char **endp, int base) +{ + unsigned long long v; + char *endptr; + + if (arg[0] != '-' && !isalnum(arg[0])) return ret_ERANGE(); + errno = 0; + v = strtoll(arg, &endptr, base); + return handle_errors(v, endp, endptr); +} + +#if ULONG_MAX != ULLONG_MAX +unsigned long 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 bb_strtol(const char *arg, char **endp, int base) +{ + long v; + char *endptr; + + if (arg[0] != '-' && !isalnum(arg[0])) return ret_ERANGE(); + errno = 0; + v = strtol(arg, &endptr, base); + return handle_errors(v, endp, endptr); +} +#endif + +#if UINT_MAX != ULONG_MAX +unsigned 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 bb_strtoi(const char *arg, char **endp, int base) +{ + long v; + char *endptr; + + if (arg[0] != '-' && !isalnum(arg[0])) 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 + +/* Floating point */ + +#if 0 + +#include /* just for HUGE_VAL */ +#define NOT_DIGIT(a) (((unsigned char)(a-'0')) > 9) +double bb_strtod(const char *arg, char **endp) +{ + double v; + char *endptr; + + if (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; +} + +#endif diff --git a/libbb/change_identity.c b/libbb/change_identity.c new file mode 100644 index 000000000..3f888f523 --- /dev/null +++ b/libbb/change_identity.c @@ -0,0 +1,51 @@ +/* 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. */ +const char *change_identity_e2str(const struct passwd *pw) +{ + if (initgroups(pw->pw_name, pw->pw_gid) == -1) + return "cannot set groups"; + endgrent(); /* ?? */ + xsetgid(pw->pw_gid); + xsetuid(pw->pw_uid); + return NULL; +} + +void change_identity(const struct passwd *pw) +{ + const char *err_msg = change_identity_e2str(pw); + + if (err_msg) + bb_perror_msg_and_die("%s", err_msg); +} diff --git a/libbb/chomp.c b/libbb/chomp.c new file mode 100644 index 000000000..eab770760 --- /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 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 000000000..d15578ca3 --- /dev/null +++ b/libbb/compare_string_array.c @@ -0,0 +1,37 @@ +/* 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 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; +} + +/* returns the array index of the string, even if it matches only a beginning */ +/* (index of first match is returned, or -1) */ +int index_in_substr_array(const char * const string_array[], const char *key) +{ + int i; + int len = strlen(key); + if (!len) + return -1; + + for (i = 0; string_array[i] != 0; i++) { + if (strncmp(string_array[i], key, len) == 0) { + return i; + } + } + return -1; +} diff --git a/libbb/concat_path_file.c b/libbb/concat_path_file.c new file mode 100644 index 000000000..9aae601a4 --- /dev/null +++ b/libbb/concat_path_file.c @@ -0,0 +1,27 @@ +/* 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 file name to new allocation buffer, + * not adding '/' if path name already has '/' +*/ + +#include "libbb.h" + +char *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 000000000..6ebc01bf5 --- /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 skiping "." and ".." directory entries +*/ + +#include "libbb.h" + +char *concat_subpath_file(const char *path, const char *f) +{ + if(f && *f == '.' && (!f[1] || (f[1] == '.' && !f[2]))) + 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 000000000..0135831fe --- /dev/null +++ b/libbb/copy_file.c @@ -0,0 +1,282 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini copy_file implementation for busybox + * + * Copyright (C) 2001 by Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + */ + +#include "libbb.h" + +static int retry_overwrite(const char *dest, int flags) +{ + if (!(flags & (FILEUTILS_FORCE|FILEUTILS_INTERACTIVE))) { + fprintf(stderr, "'%s' exists\n", dest); + return -1; + } + if (flags & FILEUTILS_INTERACTIVE) { + fprintf(stderr, "%s: overwrite '%s'? ", applet_name, dest); + if (!bb_ask_confirmation()) + return 0; // not allowed to overwrite + } + if (unlink(dest) < 0) { + bb_perror_msg("cannot remove '%s'", dest); + return -1; // error + } + return 1; // ok (to try again) +} + +int copy_file(const char *source, const char *dest, int flags) +{ + struct stat source_stat; + struct stat dest_stat; + int status = 0; + signed char dest_exists = 0; + signed char ovr; + +#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 (S_ISDIR(source_stat.st_mode)) { + DIR *dp; + struct dirent *d; + mode_t saved_umask = 0; + + if (!(flags & FILEUTILS_RECUR)) { + bb_error_msg("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; + } + } 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; + mode |= S_IRWXU; + + if (mkdir(dest, mode) < 0) { + umask(saved_umask); + bb_perror_msg("cannot create directory '%s'", dest); + return -1; + } + + umask(saved_umask); + } + + /* Recursively copy files in SOURCE. */ + dp = opendir(source); + if (dp == NULL) { + status = -1; + goto preserve_status; + } + + 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) + status = -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 change permissions of '%s'", dest); + status = -1; + } + + } else 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 = retry_overwrite(dest, flags); + if (ovr <= 0) + return ovr; + if (lf(source, dest) < 0) { + bb_perror_msg("cannot create link '%s'", dest); + return -1; + } + } + return 0; + + } else if (S_ISREG(source_stat.st_mode) + // Huh? DEREF uses stat, which never returns links IIRC... + || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) + ) { + int src_fd; + int dst_fd; + if (ENABLE_FEATURE_PRESERVE_HARDLINKS) { + char *link_name; + + if (!FLAGS_DEREF + && is_in_ino_dev_hashtable(&source_stat, &link_name) + ) { + if (link(link_name, dest) < 0) { + ovr = retry_overwrite(dest, flags); + if (ovr <= 0) + return ovr; + if (link(link_name, dest) < 0) { + bb_perror_msg("cannot create link '%s'", dest); + return -1; + } + } + return 0; + } + // TODO: probably is_in_.. and add_to_... + // can be combined: find_or_add_... + add_to_ino_dev_hashtable(&source_stat, dest); + } + + src_fd = open(source, O_RDONLY); + if (src_fd == -1) { + bb_perror_msg("cannot open '%s'", source); + return -1; + } + + // POSIX: if exists and -i, ask (w/o -i assume yes). + // Then open w/o EXCL. + // 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... + dst_fd = open(dest, (flags & FILEUTILS_INTERACTIVE) + ? O_WRONLY|O_CREAT|O_TRUNC|O_EXCL + : O_WRONLY|O_CREAT|O_TRUNC, source_stat.st_mode); + if (dst_fd == -1) { + // We would not do POSIX insanity. -i asks, + // then _unlinks_ the offender. Presto. + // Or else we will end up having 3 open()s! + ovr = retry_overwrite(dest, flags); + if (ovr <= 0) { + close(src_fd); + return ovr; + } + dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, source_stat.st_mode); + if (dst_fd == -1) { + bb_perror_msg("cannot open '%s'", dest); + close(src_fd); + return -1; + } + } + + if (bb_copyfd_eof(src_fd, dst_fd) == -1) + status = -1; + if (close(dst_fd) < 0) { + bb_perror_msg("cannot close '%s'", dest); + status = -1; + } + if (close(src_fd) < 0) { + bb_perror_msg("cannot close '%s'", source); + status = -1; + } + + } else 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) + || S_ISLNK(source_stat.st_mode) + ) { + // We are lazy here, a bit lax with races... + if (dest_exists) { + ovr = retry_overwrite(dest, flags); + if (ovr <= 0) + return ovr; + } + if (S_ISFIFO(source_stat.st_mode)) { + if (mkfifo(dest, source_stat.st_mode) < 0) { + bb_perror_msg("cannot create fifo '%s'", dest); + return -1; + } + } else if (S_ISLNK(source_stat.st_mode)) { + char *lpath; + + lpath = xreadlink(source); + if (symlink(lpath, dest) < 0) { + bb_perror_msg("cannot create symlink '%s'", dest); + free(lpath); + return -1; + } + free(lpath); + + 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); + + return 0; + + } else { + 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("internal error: unrecognized file type"); + return -1; + } + + preserve_status: + + 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; + 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 status; +} diff --git a/libbb/copyfd.c b/libbb/copyfd.c new file mode 100644 index 000000000..c6b886647 --- /dev/null +++ b/libbb/copyfd.c @@ -0,0 +1,86 @@ +/* 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 +#include +#include +#include + +#include "libbb.h" + + +#if BUFSIZ < 4096 +#undef BUFSIZ +#define BUFSIZ 4096 +#endif + + +static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size) +{ + int status = -1; + off_t total = 0; + RESERVE_CONFIG_BUFFER(buffer, BUFSIZ); + + if (src_fd < 0) goto out; + + if (!size) { + size = BUFSIZ; + status = 1; /* copy until eof */ + } + + while (1) { + ssize_t rd; + + rd = safe_read(src_fd, buffer, size > BUFSIZ ? BUFSIZ : 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) { + size -= rd; + if (!size) { + status = 0; + break; + } + } + } + +out: + RELEASE_CONFIG_BUFFER(buffer); + + return status ? -1 : total; +} + + +off_t bb_copyfd_size(int fd1, int fd2, off_t size) +{ + if (size) { + return bb_full_fd_action(fd1, fd2, size); + } + return 0; +} + +off_t 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 000000000..fd7e0b56c --- /dev/null +++ b/libbb/correct_password.c @@ -0,0 +1,65 @@ +/* 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 for a password if run by UID 0 + or if PW has an empty password. */ + +int correct_password(const struct passwd *pw) +{ + char *unencrypted, *encrypted, *correct; + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + if (!strcmp(pw->pw_passwd, "x") || !strcmp(pw->pw_passwd, "*")) { + struct spwd *sp = getspnam(pw->pw_name); + + if (!sp) + bb_error_msg_and_die("no valid shadow password"); + + correct = sp->sp_pwdp; + } + else +#endif + correct = pw->pw_passwd; + + if (!correct || correct[0] == '\0') + return 1; + + unencrypted = bb_askpass(0, "Password: "); + if (!unencrypted) { + return 0; + } + encrypted = crypt(unencrypted, correct); + memset(unencrypted, 0, strlen(unencrypted)); + return (!strcmp(encrypted, correct)) ? 1 : 0; +} diff --git a/libbb/crc32.c b/libbb/crc32.c new file mode 100644 index 000000000..1e4a57e8a --- /dev/null +++ b/libbb/crc32.c @@ -0,0 +1,39 @@ +/* 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 *crc32_filltable(int endian) +{ + + uint32_t *crc_table = xmalloc(256 * sizeof(uint32_t)); + uint32_t polynomial = endian ? 0x04c11db7 : 0xedb88320; + uint32_t c; + int i, j; + + 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 000000000..c3d1b5578 --- /dev/null +++ b/libbb/create_icmp6_socket.c @@ -0,0 +1,39 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * create raw socket for icmp (IPv6 version) protocol test permission + * and drop root privileges if running setuid + * + */ + +#include +#include +#include +#include +#include +#include "libbb.h" + +#ifdef CONFIG_FEATURE_IPV6 +int create_icmp6_socket(void) +{ + struct protoent *proto; + int sock; + + proto = getprotobyname("ipv6-icmp"); + /* if getprotobyname failed, just silently force + * proto->p_proto to have the correct value for "ipv6-icmp" */ + if ((sock = socket(AF_INET6, SOCK_RAW, + (proto ? proto->p_proto : IPPROTO_ICMPV6))) < 0) { + if (errno == EPERM) + bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); + else + 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 000000000..431c4d8a7 --- /dev/null +++ b/libbb/create_icmp_socket.c @@ -0,0 +1,37 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * create raw socket for icmp protocol test permission + * and drop root privileges if running setuid + * + */ + +#include +#include +#include +#include +#include +#include "libbb.h" + +int create_icmp_socket(void) +{ + struct protoent *proto; + int sock; + + proto = getprotobyname("icmp"); + /* if getprotobyname failed, just silently force + * proto->p_proto to have the correct value for "icmp" */ + if ((sock = socket(AF_INET, SOCK_RAW, + (proto ? proto->p_proto : 1))) < 0) { /* 1 == ICMP */ + if (errno == EPERM) + bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); + else + 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/default_error_retval.c b/libbb/default_error_retval.c new file mode 100644 index 000000000..f4e46a4b5 --- /dev/null +++ b/libbb/default_error_retval.c @@ -0,0 +1,19 @@ +/* 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 +#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 000000000..c788b6982 --- /dev/null +++ b/libbb/device_open.c @@ -0,0 +1,33 @@ +/* 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 +#include "libbb.h" + + +/* try to open up the specified device */ +int device_open(const char *device, int mode) +{ + int m, f, fd = -1; + + m = mode | O_NONBLOCK; + + /* Retry up to 5 times */ + /* TODO: explain why it can't be considered insane */ + for (f = 0; f < 5; f++) + if ((fd = open(device, m, 0600)) >= 0) + break; + if (fd < 0) + return fd; + /* Reset original flags. */ + if (m != mode) + fcntl(fd, F_SETFL, mode); + return fd; +} diff --git a/libbb/dump.c b/libbb/dump.c new file mode 100644 index 000000000..10710894e --- /dev/null +++ b/libbb/dump.c @@ -0,0 +1,803 @@ +/* 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" + +enum _vflag bb_dump_vflag = FIRST; +FS *bb_dump_fshead; /* head of format strings */ +static FU *endfu; +static char **_argv; +static off_t savaddress; /* saved address/offset in stream */ +static off_t eaddress; /* end address */ +static off_t address; /* address/offset in stream */ +off_t bb_dump_skip; /* bytes to skip */ +static int exitval; /* final exit value */ +int bb_dump_blocksize; /* data block size */ +int bb_dump_length = -1; /* max bytes to read */ + +static const char index_str[] = ".#-+ 0123456789"; + +static const char size_conv_str[] = +"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG"; + +static const char lcc[] = "diouxX"; + +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; + /* + * bb_dump_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)); + } + if (!(p = strchr(size_conv_str + 12, *fmt))) { + 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(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] calloc 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 thru. + */ + + /* bb_dump_skip preceding text and up to the next % sign */ + for (p1 = fmtp; *p1 && *p1 != '%'; ++p1); + + /* 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; + /* bb_dump_skip to conversion character */ + for (++p1; strchr(index_str, *p1); ++p1); + } else { + /* bb_dump_skip any special chars, field width */ + while (strchr(index_str + 1, *++p1)); + if (*p1 == '.' && isdigit(*++p1)) { + sokay = USEPREC; + prec = atoi(p1); + while (isdigit(*++p1)); + } 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; + if (!(e = strchr(lcc, *p1))) { + 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': + 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; + 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++); + 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; + } + + 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 bb_dump_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->nextfu) { + if (!fu->nextfu && fs->bcnt < bb_dump_blocksize && + !(fu->flags & F_SETREP) && fu->bcnt) + fu->reps += (bb_dump_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(char *fname, int statok) +{ + struct stat sbuf; + + if (statok) { + if (fstat(STDIN_FILENO, &sbuf)) { + bb_perror_msg_and_die("%s", fname); + } + if ((!(S_ISCHR(sbuf.st_mode) || + S_ISBLK(sbuf.st_mode) || + S_ISFIFO(sbuf.st_mode))) && bb_dump_skip >= sbuf.st_size) { + /* If bb_dump_size valid and bb_dump_skip >= size */ + bb_dump_skip -= sbuf.st_size; + address += sbuf.st_size; + return; + } + } + if (fseek(stdin, bb_dump_skip, SEEK_SET)) { + bb_perror_msg_and_die("%s", fname); + } + savaddress = address += bb_dump_skip; + bb_dump_skip = 0; +} + +static int next(char **argv) +{ + static int done; + int statok; + + if (argv) { + _argv = argv; + return 1; + } + for (;;) { + if (*_argv) { + if (!(freopen(*_argv, "r", stdin))) { + bb_perror_msg("%s", *_argv); + exitval = 1; + ++_argv; + continue; + } + statok = done = 1; + } else { + if (done++) + return 0; + statok = 0; + } + if (bb_dump_skip) + do_skip(statok ? *_argv : "stdin", statok); + if (*_argv) + ++_argv; + if (!bb_dump_skip) + return 1; + } + /* NOTREACHED */ +} + +static unsigned char *get(void) +{ + static int ateof = 1; + static unsigned char *curp=NULL, *savp; /*DBU:[dave@cray.com]initialize curp */ + int n; + int need, nread; + unsigned char *tmpp; + + if (!curp) { + address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/ + curp = (unsigned char *) xmalloc(bb_dump_blocksize); + savp = (unsigned char *) xmalloc(bb_dump_blocksize); + } else { + tmpp = curp; + curp = savp; + savp = tmpp; + address = savaddress += bb_dump_blocksize; + } + for (need = bb_dump_blocksize, nread = 0;;) { + /* + * 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 (!bb_dump_length || (ateof && !next((char **) NULL))) { + if (need == bb_dump_blocksize) { + return NULL; + } + if (bb_dump_vflag != ALL && !memcmp(curp, savp, nread)) { + if (bb_dump_vflag != DUP) { + puts("*"); + } + return NULL; + } + memset((char *) curp + nread, 0, need); + eaddress = address + nread; + return curp; + } + n = fread((char *) curp + nread, sizeof(unsigned char), + bb_dump_length == -1 ? need : MIN(bb_dump_length, need), stdin); + if (!n) { + if (ferror(stdin)) { + bb_perror_msg("%s", _argv[-1]); + } + ateof = 1; + continue; + } + ateof = 0; + if (bb_dump_length != -1) { + bb_dump_length -= n; + } + if (!(need -= n)) { + if (bb_dump_vflag == ALL || bb_dump_vflag == FIRST + || memcmp(curp, savp, bb_dump_blocksize)) { + if (bb_dump_vflag == DUP || bb_dump_vflag == FIRST) { + bb_dump_vflag = WAIT; + } + return curp; + } + if (bb_dump_vflag == WAIT) { + puts("*"); + } + bb_dump_vflag = DUP; + address = savaddress += bb_dump_blocksize; + need = bb_dump_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); + for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1) + if (pr->nospace) pr->nospace--; + while ((*p2++ = *p1++) != 0); +} + +static const char conv_str[] = + "\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" + "\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'; + (void) 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[] = + "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(void) +{ +/* extern FU *endfu; */ + FS *fs; + FU *fu; + PR *pr; + int cnt; + unsigned char *bp; + + off_t saveaddress; + unsigned char savech = 0, *savebp; + + while ((bp = get()) != NULL) { + for (fs = bb_dump_fshead, savebp = bp, saveaddress = address; fs; + fs = fs->nextfs, bp = savebp, 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; address += pr->bcnt, + bp += pr->bcnt, pr = pr->nextpr) { + if (eaddress && address >= 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 int) 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((char *) &fval, (char *) bp, + sizeof(fval)); + printf(pr->fmt, fval); + break; + case 8: + memmove((char *) &dval, (char *) 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((char *) &sval, (char *) bp, + sizeof(sval)); + printf(pr->fmt, (int) sval); + break; + case 4: + memmove((char *) &ival, (char *) 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 int ival; + unsigned short sval; + + switch (pr->bcnt) { + case 1: + printf(pr->fmt, (unsigned int) * bp); + break; + case 2: + memmove((char *) &sval, (char *) bp, + sizeof(sval)); + printf(pr->fmt, (unsigned int) sval); + break; + case 4: + memmove((char *) &ival, (char *) bp, + sizeof(ival)); + printf(pr->fmt, ival); + break; + } + break; + } + } + if (cnt == 1 && pr->nospace) { + *pr->nospace = savech; + } + } + } + } + } + } + if (endfu) { + /* + * if eaddress not set, error or file bb_dump_size was multiple of + * bb_dump_blocksize, and no partial block ever found. + */ + if (!eaddress) { + if (!address) { + return; + } + eaddress = address; + } + for (pr = endfu->nextpr; pr; pr = pr->nextpr) { + switch (pr->flags) { + case F_ADDRESS: + (void) printf(pr->fmt, (unsigned int) eaddress); + break; + case F_TEXT: + (void) printf(pr->fmt); + break; + } + } + } +} + +int bb_dump_dump(char **argv) +{ + FS *tfs; + + /* figure out the data block bb_dump_size */ + for (bb_dump_blocksize = 0, tfs = bb_dump_fshead; tfs; tfs = tfs->nextfs) { + tfs->bcnt = bb_dump_size(tfs); + if (bb_dump_blocksize < tfs->bcnt) { + bb_dump_blocksize = tfs->bcnt; + } + } + /* rewrite the rules, do syntax checking */ + for (tfs = bb_dump_fshead; tfs; tfs = tfs->nextfs) { + rewrite(tfs); + } + + next(argv); + display(); + + return exitval; +} + +void bb_dump_add(const char *fmt) +{ + const char *p; + char *p1; + char *p2; + static FS **nextfs; + FS *tfs; + FU *tfu, **nextfu; + const char *savep; + + /* start new linked list of format units */ + tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */ + if (!bb_dump_fshead) { + bb_dump_fshead = tfs; + } else { + *nextfs = tfs; + } + nextfs = &tfs->nextfs; + nextfu = &tfs->nextfu; + + /* take the format string and break it up into format units */ + for (p = fmt;;) { + /* bb_dump_skip leading white space */ + p = skip_whitespace(p); + if (!*p) { + break; + } + + /* allocate a new format unit and link it in */ + /* NOSTRICT */ + /* DBU:[dave@cray.com] calloc so that forward pointers start out NULL */ + tfu = xzalloc(sizeof(FU)); + *nextfu = tfu; + nextfu = &tfu->nextfu; + tfu->reps = 1; + + /* if leading digit, repetition count */ + if (isdigit(*p)) { + for (savep = p; isdigit(*p); ++p); + 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; + /* bb_dump_skip trailing white space */ + p = skip_whitespace(++p); + } + + /* bb_dump_skip slash and trailing white space */ + if (*p == '/') { + p = skip_whitespace(++p); + } + + /* byte count */ + if (isdigit(*p)) { + for (savep = p; isdigit(*p); ++p); + if (!isspace(*p)) { + bb_error_msg_and_die("bad format {%s}", fmt); + } + tfu->bcnt = atoi(savep); + /* bb_dump_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 = xmalloc(p - savep + 1); + strncpy(tfu->fmt, savep, p - savep); + tfu->fmt[p - savep] = '\0'; +/* 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 000000000..b2141f3a2 --- /dev/null +++ b/libbb/error_msg.c @@ -0,0 +1,23 @@ +/* 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 +#include +#include +#include "libbb.h" + +void 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 000000000..10d953513 --- /dev/null +++ b/libbb/error_msg_and_die.c @@ -0,0 +1,28 @@ +/* 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 +#include +#include +#include "libbb.h" + +int die_sleep; + +void bb_error_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_verror_msg(s, p, NULL); + va_end(p); + if (die_sleep) + sleep(die_sleep); + exit(xfunc_error_retval); +} diff --git a/libbb/execable.c b/libbb/execable.c new file mode 100644 index 000000000..817c06736 --- /dev/null +++ b/libbb/execable.c @@ -0,0 +1,61 @@ +/* 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 execable_file(const char *name) +{ + struct stat s; + return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode)); +} + +/* search $PATH for an executable file; + * return allocated string containing full path if found; + * return NULL otherwise; + */ +char *find_execable(const char *filename) +{ + char *path, *p, *n; + + p = path = xstrdup(getenv("PATH")); + 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)) { + free(path); + return p; + } + free(p); + } + p = n; + } + free(path); + return NULL; +} + +/* search $PATH for an executable file; + * return 1 if found; + * return 0 otherwise; + */ +int exists_execable(const char *filename) +{ + char *ret = find_execable(filename); + if (ret) { + free(ret); + return 1; + } + return 0; +} diff --git a/libbb/fclose_nonstdin.c b/libbb/fclose_nonstdin.c new file mode 100644 index 000000000..88e8474f2 --- /dev/null +++ b/libbb/fclose_nonstdin.c @@ -0,0 +1,23 @@ +/* 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 fclose_if_not_stdin(FILE *f) +{ + if (f != stdin) { + return fclose(f); + } + return 0; +} diff --git a/libbb/fflush_stdout_and_exit.c b/libbb/fflush_stdout_and_exit.c new file mode 100644 index 000000000..456ce9513 --- /dev/null +++ b/libbb/fflush_stdout_and_exit.c @@ -0,0 +1,24 @@ +/* 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 fflush_stdout_and_exit(int retval) +{ + if (fflush(stdout)) { + retval = xfunc_error_retval; + } + if (die_sleep) + sleep(die_sleep); + exit(retval); +} diff --git a/libbb/fgets_str.c b/libbb/fgets_str.c new file mode 100644 index 000000000..1bc6c3b1c --- /dev/null +++ b/libbb/fgets_str.c @@ -0,0 +1,53 @@ +/* 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" + +/* Read up to (and including) TERMINATING_STRING from FILE and return it. + * Return NULL on EOF. */ + +char *xmalloc_fgets_str(FILE *file, const char *terminating_string) +{ + 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) { + free(linebuf); + return NULL; + } + + /* grow the line buffer as necessary */ + while (idx > linebufsz - 2) { + 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 + ) { + idx -= term_length; + break; + } + } + linebuf = xrealloc(linebuf, idx + 1); + linebuf[idx] = '\0'; + return linebuf; +} diff --git a/libbb/find_mount_point.c b/libbb/find_mount_point.c new file mode 100644 index 000000000..cb00b9806 --- /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 *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 000000000..e98616940 --- /dev/null +++ b/libbb/find_pid_by_name.c @@ -0,0 +1,55 @@ +/* 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" + +/* 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* find_pid_by_name(const char* procName) +{ + pid_t* pidList; + int i = 0; + procps_status_t* p = NULL; + + pidList = xmalloc(sizeof(*pidList)); + while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_COMM))) { + if (strncmp(p->comm, procName, sizeof(p->comm)-1) == 0) { + pidList = xrealloc(pidList, sizeof(*pidList) * (i+2)); + pidList[i++] = p->pid; + } + } + + pidList[i] = 0; + return pidList; +} + +pid_t *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 000000000..71b79b8d0 --- /dev/null +++ b/libbb/find_root_device.c @@ -0,0 +1,33 @@ +/* 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" + +char *find_block_device(char *path) +{ + DIR *dir; + struct dirent *entry; + struct stat st; + dev_t dev; + char *retpath=NULL; + + if(stat(path, &st) || !(dir = opendir("/dev"))) return NULL; + dev = (st.st_mode & S_IFMT) == S_IFBLK ? st.st_rdev : st.st_dev; + while((entry = readdir(dir)) != NULL) { + char devpath[PATH_MAX]; + sprintf(devpath,"/dev/%s", entry->d_name); + if(!stat(devpath, &st) && S_ISBLK(st.st_mode) && st.st_rdev == dev) { + retpath = xstrdup(devpath); + break; + } + } + closedir(dir); + + return retpath; +} diff --git a/libbb/full_write.c b/libbb/full_write.c new file mode 100644 index 000000000..bceac6de5 --- /dev/null +++ b/libbb/full_write.c @@ -0,0 +1,38 @@ +/* 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 +#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 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) + 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 000000000..52ae3d215 --- /dev/null +++ b/libbb/get_console.c @@ -0,0 +1,77 @@ +/* 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 +#include +#include +#include +#include +#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 get_console_fd(void) +{ + int fd; + + static const char * const choise_console_names[] = { + CONSOLE_DEV, CURRENT_VC, CURRENT_TTY + }; + + for (fd = 2; fd >= 0; fd--) { + int fd4name; + int choise_fd; + char arg; + + fd4name = open_a_console(choise_console_names[fd]); + chk_std: + choise_fd = fd4name >= 0 ? fd4name : fd; + + arg = 0; + if (ioctl(choise_fd, KDGKBTYPE, &arg) == 0) + return choise_fd; + if(fd4name >= 0) { + close(fd4name); + fd4name = -1; + goto chk_std; + } + } + + bb_error_msg("cannot get file descriptor referring to console"); + return fd; /* total failure */ +} diff --git a/libbb/get_last_path_component.c b/libbb/get_last_path_component.c new file mode 100644 index 000000000..311909726 --- /dev/null +++ b/libbb/get_last_path_component.c @@ -0,0 +1,32 @@ +/* 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" + +char *bb_get_last_path_component(char *path) +{ + char *first = path; + char *last; + + last = path - 1; + + while (*path) { + if ((*path != '/') && (path > ++last)) { + last = first = path; + } + ++path; + } + + if (*first == '/') { + last = first; + } + last[1] = 0; + + return first; +} diff --git a/libbb/get_line_from_file.c b/libbb/get_line_from_file.c new file mode 100644 index 000000000..3f2c6096e --- /dev/null +++ b/libbb/get_line_from_file.c @@ -0,0 +1,68 @@ +/* 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. + */ + +#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 + * stored and 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 read is stored in it. */ + +char *bb_get_chunk_from_file(FILE * file, int *end) +{ + 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) { + linebuf = xrealloc(linebuf, linebufsz += 80); + } + linebuf[idx++] = (char) ch; + if (!ch || (end && ch == '\n')) + break; + } + 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; +} + +/* Get line, including trailing \n if any */ +char *xmalloc_fgets(FILE * file) +{ + int i; + + return bb_get_chunk_from_file(file, &i); +} + +/* Get line. Remove trailing \n */ +char *xmalloc_getline(FILE * file) +{ + int i; + char *c = bb_get_chunk_from_file(file, &i); + + if (i && c[--i] == '\n') + c[i] = 0; + + return c; +} diff --git a/libbb/getopt32.c b/libbb/getopt32.c new file mode 100644 index 000000000..dddf8121a --- /dev/null +++ b/libbb/getopt32.c @@ -0,0 +1,529 @@ +/* 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 "libbb.h" +#include + +/* Documentation + +uint32_t +getopt32(int argc, 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(argc, 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(argc, 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(argc, 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 struct option *applet_long_options + + This struct allows you to define long options. The syntax for + declaring the array is just like that of getopt's longopts. + (see getopt(3)) + + static const struct option applet_long_options[] = { + //name,has_arg,flag,val + { "verbose", 0, 0, 'v' }, + { 0, 0, 0, 0 } + }; + applet_long_options = applet_long_options; + + 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(argc, 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; + opt_complementary = "ww"; + getopt32(argc, 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(argc, argv, "vb:c", &my_b, &verbose_level); + if (f & 2) // -c after -b unsets -b flag + while (my_b) { dosomething_with(my_b->data); my_b = my_b->link; } + 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(argc, argv, "wx"); + + Allows any arguments to be given without a dash (./program w x) + as well as with a dash (./program -x). + + "--" 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 + + "-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 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(argc, 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 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 mutally 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's + return value will have the error flag set (BB_GETOPT_ERROR) so + that we can check for it: + + if (flags & BB_GETOPT_ERROR) + bb_show_usage(); + + "x--x" Variation of the above, it means that -x option should occur + at most once. + + "?" A "?" as the first char in a opt_complementary group means: + if BB_GETOPT_ERROR is detected, don't return, call bb_show_usage + and exit instead. Next char after '?' can't be a digit. + + "::" 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(llist_t *old_head, char *new_item).) + + opt_complementary = "e::"; + + getopt32(argc, 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 + + "?" An "?" 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(argc, 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(argc, 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. +*/ + +/* Code here assumes that 'unsigned' is at least 32 bits wide */ + +const char *opt_complementary; + +typedef struct { + int opt; + int list_flg; + unsigned switch_on; + unsigned switch_off; + unsigned incongruously; + unsigned requires; + void **optarg; /* char **optarg or llist_t **optarg */ + int *counter; +} t_complementary; + +/* You can set applet_long_options for parse called long options */ +#if ENABLE_GETOPT_LONG +static const struct option bb_default_long_options[] = { +/* { "help", 0, NULL, '?' }, */ + { 0, 0, 0, 0 } +}; + +const struct option *applet_long_options = bb_default_long_options; +#endif + +uint32_t option_mask32; + +uint32_t +getopt32(int argc, char **argv, const char *applet_opts, ...) +{ + unsigned flags = 0; + unsigned requires = 0; + t_complementary complementary[33]; + int c; + const unsigned char *s; + t_complementary *on_off; + va_list p; +#if ENABLE_GETOPT_LONG + const struct option *l_o; +#endif + unsigned trigger; + char **pargv = NULL; + 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 +#define FREE_FIRST_ARGV_IS_OPT 8 + int spec_flgs = 0; + + 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 = *s; + on_off->switch_on = (1 << c); + if (*++s == ':') { + on_off->optarg = va_arg(p, void **); + while (*++s == ':') /* skip */; + } + on_off++; + c++; + } + +#if ENABLE_GETOPT_LONG + for (l_o = applet_long_options; l_o->name; l_o++) { + if (l_o->flag) + continue; + for (on_off = complementary; on_off->opt != 0; on_off++) + if (on_off->opt == l_o->val) + goto next_long; + if (c >= 32) break; + on_off->opt = 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; on_off++) + if (on_off->opt == *s) + break; + if (c == ':' && s[2] == ':') { + on_off->list_flg++; + 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; on_off++) + if (on_off->opt == *s) { + *pair_switch |= on_off->switch_on; + break; + } + } + } + s--; + } + va_end (p); + +#if ENABLE_AR || ENABLE_TAR + if (spec_flgs & FIRST_ARGV_IS_OPT) { + if (argv[1] && argv[1][0] != '-' && argv[1][0] != '\0') { + argv[1] = xasprintf("-%s", argv[1]); + if (ENABLE_FEATURE_CLEAN_UP) + spec_flgs |= FREE_FIRST_ARGV_IS_OPT; + } + } +#endif + /* Note: just "getopt() <= 0" will not work good 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, + applet_long_options, NULL)) != -1) { +#else + while ((c = getopt(argc, argv, applet_opts)) != -1) { +#endif /* ENABLE_GETOPT_LONG */ + c &= 0xff; /* fight libc's sign extends */ +loop_arg_is_opt: + for (on_off = complementary; on_off->opt != c; on_off++) { + /* c==0 if long opt have non NULL flag */ + if (on_off->opt == 0 && c != 0) + bb_show_usage(); + } + if (flags & on_off->incongruously) { + if ((spec_flgs & SHOW_USAGE_IF_ERROR)) + bb_show_usage(); + flags |= BB_GETOPT_ERROR; + } + 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->list_flg) { + llist_add_to((llist_t **)(on_off->optarg), optarg); + } else if (on_off->optarg) { + *(char **)(on_off->optarg) = optarg; + } + if (pargv != NULL) + break; + } + + if (spec_flgs & ALL_ARGV_IS_OPTS) { + /* process argv is option, for example "ps" applet */ + if (pargv == NULL) + pargv = argv + optind; + while (*pargv) { + c = **pargv; + if (c == '\0') { + pargv++; + } else { + (*pargv)++; + goto loop_arg_is_opt; + } + } + } + +#if (ENABLE_AR || ENABLE_TAR) && ENABLE_FEATURE_CLEAN_UP + if (spec_flgs & FREE_FIRST_ARGV_IS_OPT) + free(argv[1]); +#endif + /* check depending requires for given options */ + for (on_off = complementary; on_off->opt; 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/herror_msg.c b/libbb/herror_msg.c new file mode 100644 index 000000000..1e6908d82 --- /dev/null +++ b/libbb/herror_msg.c @@ -0,0 +1,22 @@ +/* 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 + +#include "libbb.h" + +void bb_herror_msg(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_vherror_msg(s, p); + va_end(p); +} diff --git a/libbb/herror_msg_and_die.c b/libbb/herror_msg_and_die.c new file mode 100644 index 000000000..f62ddd2ea --- /dev/null +++ b/libbb/herror_msg_and_die.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 +#include + +#include "libbb.h" + +void bb_herror_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_vherror_msg(s, p); + va_end(p); + if (die_sleep) + sleep(die_sleep); + exit(xfunc_error_retval); +} diff --git a/libbb/human_readable.c b/libbb/human_readable.c new file mode 100644 index 000000000..ff1b55141 --- /dev/null +++ b/libbb/human_readable.c @@ -0,0 +1,89 @@ +/* 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 +#include "libbb.h" + +const char *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 zero_and_units[] = { '0', 0, 'k', 'M', 'G', 'T' }; + static const char fmt[] = "%llu"; + static const char fmt_tenths[] = "%llu.%d%c"; + + static char str[21]; /* Sufficient for 64 bit unsigned integers. */ + + unsigned long long val; + int frac; + const char *u; + const char *f; + + u = zero_and_units; + f = fmt; + frac = 0; + + val = size * block_size; + if (val == 0) { + return u; + } + + if (display_unit) { + val += display_unit/2; /* Deal with rounding. */ + val /= display_unit; /* Don't combine with the line above!!! */ + } else { + ++u; + while ((val >= 1024) + && (u < zero_and_units + sizeof(zero_and_units) - 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 0 + /* Sample code to omit decimal point and tenths digit. */ + if ( /* no_tenths */ 1 ) { + 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 000000000..d8e00353e --- /dev/null +++ b/libbb/inet_common.c @@ -0,0 +1,235 @@ +/* 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 + * + * Version: $Id: inet_common.c,v 1.8 2004/03/10 07:42:38 mjn3 Exp $ + * + */ + +#include "libbb.h" +#include "inet_common.h" + +int INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst) +{ + struct hostent *hp; + struct netent *np; + + /* 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)) != (struct hostent *) NULL) { + memcpy((char *) &s_in->sin_addr, (char *) hp->h_addr_list[0], + sizeof(struct in_addr)); + return 0; + } + /* Try the NETWORKS database to see if this is a known network. */ +#ifdef DEBUG + bb_error_msg("getnetbyname (%s)", name); +#endif + if ((np = getnetbyname(name)) != (struct netent *) NULL) { + s_in->sin_addr.s_addr = htonl(np->n_net); + return 1; + } + if (hostfirst) { + /* Don't try again */ + return -1; + } +#ifdef DEBUG + res_init(); + _res.options |= RES_DEBUG; +#endif + +#ifdef DEBUG + bb_error_msg("gethostbyname (%s)", name); +#endif + if ((hp = gethostbyname(name)) == (struct hostent *) NULL) { + return -1; + } + memcpy((char *) &s_in->sin_addr, (char *) hp->h_addr_list[0], + sizeof(struct in_addr)); + + return 0; +} + +/* cache */ +struct addr { + struct sockaddr_in addr; + char *name; + int host; + struct addr *next; +}; + +static struct addr *INET_nn = NULL; /* addr-to-name cache */ + +/* numeric: & 0x8000: default instead of *, + * & 0x4000: host instead of net, + * & 0x0fff: don't resolve + */ +int INET_rresolve(char *name, size_t len, struct sockaddr_in *s_in, + int numeric, unsigned int netmask) +{ + struct hostent *ent; + struct netent *np; + struct addr *pn; + unsigned long ad, host_ad; + int host = 0; + + /* Grmpf. -FvK */ + if (s_in->sin_family != AF_INET) { +#ifdef DEBUG + bb_error_msg("rresolve: unsupport address family %d !", + s_in->sin_family); +#endif + errno = EAFNOSUPPORT; + return -1; + } + ad = (unsigned long) s_in->sin_addr.s_addr; +#ifdef DEBUG + bb_error_msg("rresolve: %08lx, mask %08x, num %08x", ad, netmask, numeric); +#endif + if (ad == INADDR_ANY) { + if ((numeric & 0x0FFF) == 0) { + if (numeric & 0x8000) + safe_strncpy(name, bb_str_default, len); + else + safe_strncpy(name, "*", len); + return 0; + } + } + if (numeric & 0x0FFF) { + safe_strncpy(name, inet_ntoa(s_in->sin_addr), len); + return 0; + } + + if ((ad & (~netmask)) != 0 || (numeric & 0x4000)) + host = 1; + pn = INET_nn; + while (pn != NULL) { + if (pn->addr.sin_addr.s_addr == ad && pn->host == host) { + safe_strncpy(name, pn->name, len); +#ifdef DEBUG + bb_error_msg("rresolve: found %s %08lx in cache", + (host ? "host" : "net"), ad); +#endif + return 0; + } + pn = pn->next; + } + + host_ad = ntohl(ad); + np = NULL; + ent = NULL; + if (host) { +#ifdef DEBUG + bb_error_msg("gethostbyaddr (%08lx)", ad); +#endif + ent = gethostbyaddr((char *) &ad, 4, AF_INET); + if (ent != NULL) { + safe_strncpy(name, ent->h_name, len); + } + } else { +#ifdef DEBUG + bb_error_msg("getnetbyaddr (%08lx)", host_ad); +#endif + np = getnetbyaddr(host_ad, AF_INET); + if (np != NULL) { + safe_strncpy(name, np->n_name, len); + } + } + if ((ent == NULL) && (np == NULL)) { + safe_strncpy(name, inet_ntoa(s_in->sin_addr), len); + } + pn = (struct addr *) xmalloc(sizeof(struct addr)); + pn->addr = *s_in; + pn->next = INET_nn; + pn->host = host; + pn->name = xstrdup(name); + INET_nn = pn; + + return 0; +} + +#ifdef CONFIG_FEATURE_IPV6 + +int 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 + + +int INET6_rresolve(char *name, size_t len, struct sockaddr_in6 *sin6, + int numeric) +{ + int s; + + /* Grmpf. -FvK */ + if (sin6->sin6_family != AF_INET6) { +#ifdef DEBUG + bb_error_msg("rresolve: unsupport address family %d!", + sin6->sin6_family); +#endif + errno = EAFNOSUPPORT; + return -1; + } + if (numeric & 0x7FFF) { + inet_ntop(AF_INET6, &sin6->sin6_addr, name, len); + return 0; + } + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + if (numeric & 0x8000) { + strcpy(name, bb_str_default); + } else { + name[0] = '*'; + name[1] = '\0'; + } + return 0; + } + + s = getnameinfo((struct sockaddr *) sin6, sizeof(struct sockaddr_in6), name, len, NULL, 0, 0); + if (s) { + bb_error_msg("getnameinfo failed"); + return -1; + } + return 0; +} + +#endif /* CONFIG_FEATURE_IPV6 */ diff --git a/libbb/info_msg.c b/libbb/info_msg.c new file mode 100644 index 000000000..78d5c8f32 --- /dev/null +++ b/libbb/info_msg.c @@ -0,0 +1,23 @@ +/* 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 +#include +#include +#include "libbb.h" + +void bb_info_msg(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_vinfo_msg(s, p); + va_end(p); +} diff --git a/libbb/inode_hash.c b/libbb/inode_hash.c new file mode 100644 index 000000000..2ac1623f4 --- /dev/null +++ b/libbb/inode_hash.c @@ -0,0 +1,88 @@ +/* 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 +#include +#include +#include "libbb.h" + +#define HASH_SIZE 311 /* Should be prime */ +#define hash_inode(i) ((i) % HASH_SIZE) + +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; + +static ino_dev_hashtable_bucket_t *ino_dev_hashtable[HASH_SIZE]; + +/* + * Return 1 if statbuf->st_ino && statbuf->st_dev are recorded in + * `ino_dev_hashtable', else return 0 + * + * If NAME is a non-NULL pointer to a character pointer, and there is + * a match, then set *NAME to the value of the name slot in that + * bucket. + */ +int is_in_ino_dev_hashtable(const struct stat *statbuf, char **name) +{ + ino_dev_hashtable_bucket_t *bucket; + + bucket = ino_dev_hashtable[hash_inode(statbuf->st_ino)]; + while (bucket != NULL) { + if ((bucket->ino == statbuf->st_ino) && + (bucket->dev == statbuf->st_dev)) + { + if (name) *name = bucket->name; + return 1; + } + bucket = bucket->next; + } + return 0; +} + +/* Add statbuf to statbuf hash table */ +void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) +{ + int i; + size_t s; + ino_dev_hashtable_bucket_t *bucket; + + i = hash_inode(statbuf->st_ino); + s = name ? strlen(name) : 0; + bucket = xmalloc(sizeof(ino_dev_hashtable_bucket_t) + s); + bucket->ino = statbuf->st_ino; + bucket->dev = statbuf->st_dev; + if (name) + strcpy(bucket->name, name); + else + bucket->name[0] = '\0'; + bucket->next = ino_dev_hashtable[i]; + ino_dev_hashtable[i] = bucket; +} + +#ifdef CONFIG_FEATURE_CLEAN_UP +/* Clear statbuf hash table */ +void reset_ino_dev_hashtable(void) +{ + int i; + ino_dev_hashtable_bucket_t *bucket; + + for (i = 0; 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; + } + } +} +#endif diff --git a/libbb/isdirectory.c b/libbb/isdirectory.c new file mode 100644 index 000000000..b35919869 --- /dev/null +++ b/libbb/isdirectory.c @@ -0,0 +1,39 @@ +/* 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 a fileName is a directory. + * Nonexistent files return FALSE. + */ +int is_directory(const char *fileName, const int followLinks, struct stat *statBuf) +{ + int status; + struct stat astatBuf; + + if (statBuf == NULL) { + /* set from auto stack buffer */ + statBuf = &astatBuf; + } + + if (followLinks) + status = stat(fileName, statBuf); + else + status = lstat(fileName, statBuf); + + if (status < 0 || !(S_ISDIR(statBuf->st_mode))) { + status = FALSE; + } + else status = TRUE; + + return status; +} diff --git a/libbb/kernel_version.c b/libbb/kernel_version.c new file mode 100644 index 000000000..50b82ae18 --- /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 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 000000000..3616d5916 --- /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. Also avoids a possible + * space-hogging inline of strlen() per usage. + */ +char* last_char_is(const char *s, int c) +{ + if (s) { + s = strrchr(s, c); + if (s && !s[1]) + return (char*)s; + } + return NULL; +} diff --git a/libbb/llist.c b/libbb/llist.c new file mode 100644 index 000000000..8a74832ee --- /dev/null +++ b/libbb/llist.c @@ -0,0 +1,78 @@ +/* vi: set sw=4 ts=4: */ +/* + * linked list helper functions. + * + * Copyright (C) 2003 Glenn McGrath + * Copyright (C) 2005 Vladimir Oleynik + * Copyright (C) 2005 Bernhard Fischer + * Copyright (C) 2006 Rob Landley + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include +#include "libbb.h" + +/* Add data to the start of the linked list. */ +void 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 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 *llist_pop(llist_t **head) +{ + void *data; + + if(!*head) data = *head; + else { + void *next = (*head)->link; + data = (*head)->data; + free(*head); + *head = next; + } + + return data; +} + +/* Recursively free all elements in the linked list. If freeit != NULL + * call it on each datum in the list */ +void llist_free(llist_t *elm, void (*freeit)(void *data)) +{ + while (elm) { + void *data = llist_pop(&elm); + if (freeit) freeit(data); + } +} + +/* Reverse list order. Useful since getopt32 saves option params + * in reverse order */ +llist_t* rev_llist(llist_t *list) +{ + llist_t *new = NULL; + while (list) { + llist_t *next = list->link; + list->link = new; + new = list; + list = next; + } + return new; +} diff --git a/libbb/login.c b/libbb/login.c new file mode 100644 index 000000000..6ebb9a6a0 --- /dev/null +++ b/libbb/login.c @@ -0,0 +1,105 @@ +/* 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 +#include "libbb.h" + +#include +#include + +#define LOGIN " login: " + +static const char fmtstr_d[] = "%A, %d %B %Y"; +static const char fmtstr_t[] = "%H:%M:%S"; + +void print_login_issue(const char *issue_file, const char *tty) +{ + FILE *fd; + 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 */ + + fd = fopen(issue_file, "r"); + if (!fd) + return; + while ((c = fgetc(fd)) != EOF) { + outbuf = buf; + buf[0] = c; + buf[1] = '\0'; + if(c == '\n') { + buf[1] = '\r'; + buf[2] = '\0'; + } + if (c == '\\' || c == '%') { + c = fgetc(fd); + switch (c) { + case 's': + outbuf = uts.sysname; + break; + case 'n': + 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': + c = getdomainname(buf, sizeof(buf) - 1); + buf[c >= 0 ? c : 0] = '\0'; + break; + case 'd': + strftime(buf, sizeof(buf), fmtstr_d, localtime(&t)); + break; + case 't': + strftime(buf, sizeof(buf), fmtstr_t, localtime(&t)); + break; + case 'h': + gethostname(buf, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + break; + case 'l': + outbuf = tty; + break; + default: + buf[0] = c; + } + } + fputs(outbuf, stdout); + } + fclose(fd); + fflush(stdout); +} + +void print_login_prompt(void) +{ + char buf[MAXHOSTNAMELEN+1]; + + if (gethostname(buf, MAXHOSTNAMELEN) == 0) + fputs(buf, stdout); + + fputs(LOGIN, stdout); + fflush(stdout); +} diff --git a/libbb/loop.c b/libbb/loop.c new file mode 100644 index 000000000..14835ec24 --- /dev/null +++ b/libbb/loop.c @@ -0,0 +1,149 @@ +/* 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 *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 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 set_loop(char **device, const char *file, unsigned long long offset) +{ + char dev[20], *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 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) + || offset != loopinfo.lo_offset) { + rc = -1; + } + close(dfd); +try_again: + if (*device) break; + } + close(ffd); + if (!rc) { + if (!*device) *device = strdup(dev); + return mode==O_RDONLY ? 1 : 0; + } + return rc; +} diff --git a/libbb/make_directory.c b/libbb/make_directory.c new file mode 100644 index 000000000..fbec4e20e --- /dev/null +++ b/libbb/make_directory.c @@ -0,0 +1,104 @@ +/* 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 +#include +#include +#include "libbb.h" + +int 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); + if (mode == -1) { + umask(mask); + mode = (S_IXUSR | S_IXGRP | S_IXOTH | + S_IWUSR | S_IWGRP | S_IWOTH | + S_IRUSR | S_IRGRP | S_IROTH) & ~mask; + } else { + umask(mask & ~0300); + } + + do { + 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 (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 conditon. */ + if (!c) { + umask(mask); + return 0; + } + } + + if (!c) { + /* Done. If necessary, updated perms on the newly + * created directory. Failure to update here _is_ + * an error.*/ + umask(mask); + 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 000000000..4903e4783 --- /dev/null +++ b/libbb/makedev.c @@ -0,0 +1,20 @@ +/* + * Utility routines. + * + * Copyright (C) 2006 Denis 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 +#include + +#ifdef __GLIBC__ +/* At least glibc has horrendously large inline for this, so wrap it */ +/* uclibc people please check - do we need "&& !__UCLIBC__" above? */ +unsigned long long bb_makedev(unsigned int major, unsigned int minor) +{ + return makedev(major, minor); +} +#endif diff --git a/libbb/md5.c b/libbb/md5.c new file mode 100644 index 000000000..e672559cf --- /dev/null +++ b/libbb/md5.c @@ -0,0 +1,450 @@ +/* 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 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[] = { +# 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[] = { + 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 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) { + int 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 *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 000000000..c640faf5b --- /dev/null +++ b/libbb/messages.c @@ -0,0 +1,53 @@ +/* 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" + +#ifndef BB_EXTRA_VERSION +#define BANNER "BusyBox v" BB_VER " (" BB_BT ")" +#else +#define BANNER "BusyBox v" BB_VER " (" BB_EXTRA_VERSION ")" +#endif +const char BB_BANNER[] = BANNER; +const char bb_msg_full_version[] = BANNER " multi-call binary"; + +const char bb_msg_memory_exhausted[] = "memory exhausted"; +const char bb_msg_invalid_date[] = "invalid date '%s'"; +const char bb_msg_write_error[] = "write error"; +const char bb_msg_read_error[] = "read error"; +const char bb_msg_unknown[] = "(unknown)"; +const char bb_msg_can_not_create_raw_socket[] = "can't create raw socket"; +const char bb_msg_perm_denied_are_you_root[] = "permission denied. (are you root?)"; +const char bb_msg_requires_arg[] = "%s requires an argument"; +const char bb_msg_invalid_arg[] = "invalid argument '%s' to '%s'"; +const char bb_msg_standard_input[] = "standard input"; +const char bb_msg_standard_output[] = "standard output"; + +const char bb_str_default[] = "default"; + +const char bb_path_passwd_file[] = "/etc/passwd"; +const char bb_path_shadow_file[] = "/etc/shadow"; +const char bb_path_group_file[] = "/etc/group"; +const char bb_path_gshadow_file[] = "/etc/gshadow"; +const char bb_path_nologin_file[] = "/etc/nologin"; +const char bb_path_securetty_file[] = "/etc/securetty"; +const char bb_path_motd_file[] = "/etc/motd"; +const char bb_default_login_shell[] = LIBBB_DEFAULT_LOGIN_SHELL; +const char bb_dev_null[] = "/dev/null"; + +#include +/* This is usually something like "/var/adm/wtmp" or "/var/log/wtmp" */ +const char bb_path_wtmp_file[] = +#if defined _PATH_WTMP +_PATH_WTMP; +#elif defined WTMP_FILE +WTMP_FILE; +#else +# error unknown path to wtmp file +#endif + +char bb_common_bufsiz1[BUFSIZ+1]; diff --git a/libbb/mode_string.c b/libbb/mode_string.c new file mode 100644 index 000000000..01029bfee --- /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] = "?pc?d?b?-?l?s???"; +/* 0123456789abcdef */ +static const char mode_chars[7] = "rwxSTst"; + +const char *bb_mode_string(int 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 *bb_mode_string(int 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 000000000..18386efb5 --- /dev/null +++ b/libbb/mtab.c @@ -0,0 +1,52 @@ +/* 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 erase_mtab(const char *name) +{ + struct mntent *entries = NULL; + int i, count = 0; + 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; + } + + while ((m = getmntent(mountTable)) != 0) { + i = count++; + entries = xrealloc(entries, count * sizeof(entries[0])); + entries[i].mnt_fsname = xstrdup(m->mnt_fsname); + entries[i].mnt_dir = xstrdup(m->mnt_dir); + entries[i].mnt_type = xstrdup(m->mnt_type); + entries[i].mnt_opts = xstrdup(m->mnt_opts); + entries[i].mnt_freq = m->mnt_freq; + entries[i].mnt_passno = m->mnt_passno; + } + endmntent(mountTable); + + 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 000000000..67367e3d7 --- /dev/null +++ b/libbb/mtab_file.c @@ -0,0 +1,17 @@ +/* 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" + + +/* Busybox mount uses either /proc/mounts or /etc/mtab to + * get the list of currently mounted filesystems */ +const char bb_path_mtab_file[] = +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 000000000..2599095df --- /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[255]; + + /* 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 */ + if (gethostname(hostname, 255) == 0) { + hostname[254] = '\0'; + if (string_checker(new_p, hostname)) { + 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) { + if ((p = strchr(p, new_p[i])) == 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. + */ + +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */ + +#include +#include +#include +#include "libbb.h" + +#define FILEMODEBITS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) + +int 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[] = "augo"; + static const char perm_chars[] = "rwxXst"; + + const char *p; + + mode_t wholist; + mode_t permlist; + mode_t mask; + mode_t new_mode; + char op; + + assert(s); + + if (((unsigned int)(*s - '0')) < 8) { + unsigned long tmp; + char *e; + + tmp = strtol(s, &e, 8); + if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */ + return 0; + } + *current_mode = tmp; + return 1; + } + + mask = umask(0); + umask(mask); + + 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, of 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 = ~mask; + if (wholist) { + tmp = wholist; + } + 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 000000000..7fb0830be --- /dev/null +++ b/libbb/perror_msg.c @@ -0,0 +1,23 @@ +/* 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 +#include +#include +#include "libbb.h" + +void bb_perror_msg(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_vperror_msg(s, p); + va_end(p); +} diff --git a/libbb/perror_msg_and_die.c b/libbb/perror_msg_and_die.c new file mode 100644 index 000000000..2303ba211 --- /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 +#include +#include +#include +#include "libbb.h" + +void bb_perror_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_vperror_msg(s, p); + va_end(p); + if (die_sleep) + sleep(die_sleep); + exit(xfunc_error_retval); +} diff --git a/libbb/perror_nomsg.c b/libbb/perror_nomsg.c new file mode 100644 index 000000000..3aefd5301 --- /dev/null +++ b/libbb/perror_nomsg.c @@ -0,0 +1,16 @@ +/* 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. + */ + +#include "libbb.h" + +void bb_perror_nomsg(void) +{ + /* Ignore the gcc warning about a null format string. */ + bb_perror_msg(NULL); +} diff --git a/libbb/perror_nomsg_and_die.c b/libbb/perror_nomsg_and_die.c new file mode 100644 index 000000000..e5623c2a9 --- /dev/null +++ b/libbb/perror_nomsg_and_die.c @@ -0,0 +1,17 @@ +/* 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. + */ + +#include +#include "libbb.h" + +void bb_perror_nomsg_and_die(void) +{ + /* Ignore the gcc warning about a null format string. */ + bb_perror_msg_and_die(NULL); +} diff --git a/libbb/process_escape_sequence.c b/libbb/process_escape_sequence.c new file mode 100644 index 000000000..138e751f5 --- /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 +#include +#include +#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 bb_process_escape_sequence(const char **ptr) +{ + static const char charmap[] = { + 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', 0, + '\a', '\b', '\f', '\n', '\r', '\t', '\v', '\\', '\\' }; + + const char *p; + const char *q; + unsigned int num_digits; + unsigned int r; + unsigned int n; + unsigned int d; + unsigned int 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 int)(*q - '0'); +#ifdef WANT_HEX_ESCAPES + if (d >= 10) { + d = ((unsigned int)(_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 000000000..017710ff4 --- /dev/null +++ b/libbb/procps.c @@ -0,0 +1,255 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright 1998 by Albert Cahalan; all rights reserved. + * Copyright (C) 2002 by Vladimir Oleynik + * + * 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[12]; +} 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 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(cp->cache, cp->size * sizeof(*cp->cache)); + cp->cache[i++].id = id; + return -i; +} +#endif + +typedef char* ug_func(char *name, long uid, int bufsize); +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(cp->cache, cp->size * sizeof(*cp->cache)); + cp->cache[i].id = id; + fp(cp->cache[i].name, id, sizeof(cp->cache[i].name)); + return cp->cache[i].name; +} +const char* get_cached_username(uid_t uid) +{ + return get_cached(&username, uid, bb_getpwuid); +} +const char* 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) +{ + ssize_t ret; + ret = open_read_close(filename, buf, PROCPS_BUFSIZE-1); + ((char *)buf)[ret > 0 ? ret : 0] = '\0'; + return ret; +} + +procps_status_t* alloc_procps_scan(int flags) +{ + procps_status_t* sp = xzalloc(sizeof(procps_status_t)); + sp->dir = xopendir("/proc"); + return sp; +} + +void free_procps_scan(procps_status_t* sp) +{ + closedir(sp->dir); + free(sp->cmd); + free(sp); +} + +void BUG_comm_size(void); +procps_status_t* 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(flags); + + 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->rss, 0, sizeof(*sp) - offsetof(procps_status_t, rss)); + + sp->pid = pid; + if (!(flags & ~PSSCAN_PID)) break; + + filename_tail = filename + sprintf(filename, "/proc/%d", pid); + + if (flags & PSSCAN_UIDGID) { + if (stat(filename, &sb)) + break; + /* Need comment - is this effective or read UID/GID? */ + sp->uid = sb.st_uid; + sp->gid = sb.st_gid; + } + + if (flags & PSSCAN_STAT) { + char *cp; + /* 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(); + sscanf(buf, "%*s (%15c", sp->comm); + n = sscanf(cp+2, + "%c %u " /* state, ppid */ + "%u %u %*s %*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 %*s " /* timeout, it_real_value, start_time */ + "%*s " /* vsize */ + "%lu", /* rss */ + sp->state, &sp->ppid, + &sp->pgid, &sp->sid, + &sp->utime, &sp->stime, + &tasknice, + &sp->rss); + if (n != 8) + break; + + if (sp->rss == 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] = ' '; + +#ifdef PAGE_SHIFT + sp->rss <<= (PAGE_SHIFT - 10); /* 2**10 = 1kb */ +#else + sp->rss *= (getpagesize() >> 10); /* 2**10 = 1kb */ +#endif + } + + if (flags & PSSCAN_CMD) { + free(sp->cmd); + sp->cmd = NULL; + strcpy(filename_tail, "/cmdline"); + n = read_to_buf(filename, buf); + if (n <= 0) + break; + if (buf[n-1] == '\n') { + if (!--n) + break; + buf[n] = '\0'; + } + do { + n--; + if ((unsigned char)(buf[n]) < ' ') + buf[n] = ' '; + } while (n); + sp->cmd = strdup(buf); + } + break; + } + return sp; +} +/* 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/pw_encrypt.c b/libbb/pw_encrypt.c new file mode 100644 index 000000000..d546bc883 --- /dev/null +++ b/libbb/pw_encrypt.c @@ -0,0 +1,29 @@ +/* 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" +#include + +char *pw_encrypt(const char *clear, const char *salt) +{ + static char cipher[128]; + char *cp; + +#if 0 /* was CONFIG_FEATURE_SHA1_PASSWORDS, but there is no such thing??? */ + if (strncmp(salt, "$2$", 3) == 0) { + return sha1_crypt(clear); + } +#endif + cp = (char *) crypt(clear, salt); + /* if crypt (a nonstandard crypt) returns a string too large, + truncate it so we don't overrun buffers and hope there is + enough security in what's left */ + safe_strncpy(cipher, cp, sizeof(cipher)); + return cipher; +} diff --git a/libbb/read.c b/libbb/read.c new file mode 100644 index 000000000..b3648b4d7 --- /dev/null +++ b/libbb/read.c @@ -0,0 +1,134 @@ +/* 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 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; +} + +/* + * 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 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) + 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 xread(int fd, void *buf, size_t count) +{ + if (count) { + ssize_t size = full_read(fd, buf, count); + if (size != count) + bb_error_msg_and_die("short read"); + } +} + +// Die with an error message if we can't read one character. +unsigned char xread_char(int fd) +{ + char tmp; + + xread(fd, &tmp, 1); + + return tmp; +} + +// Read one line a-la fgets. Works only on seekable streams +char *reads(int fd, char *buffer, size_t size) +{ + char *p; + + if (size < 2) + return NULL; + size = full_read(fd, buffer, size-1); + if ((ssize_t)size <= 0) + return NULL; + + buffer[size] = '\0'; + p = strchr(buffer, '\n'); + if (p) { + off_t offset; + *p++ = '\0'; + // avoid incorrect (unsigned) widening + offset = (off_t)(p-buffer) - (off_t)size; + // set fd position the right after the \n + if (offset && lseek(fd, offset, SEEK_CUR) == (off_t)-1) + return NULL; + } + return buffer; +} + +ssize_t 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 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); +} + +void *xmalloc_open_read_close(const char *filename, size_t *sizep) +{ + char *buf; + size_t size = sizep ? *sizep : INT_MAX; + int fd = xopen(filename, O_RDONLY); + off_t len = xlseek(fd, 0, SEEK_END); + xlseek(fd, 0, SEEK_SET); + + if (len > size) + bb_error_msg_and_die("file '%s' is too big", filename); + size = len; + buf = xmalloc(size+1); + size = read_close(fd, buf, size); + if ((ssize_t)size < 0) + bb_perror_msg_and_die("'%s'", filename); + buf[size] = '\0'; + if (sizep) *sizep = size; + return buf; +} diff --git a/libbb/recursive_action.c b/libbb/recursive_action.c new file mode 100644 index 000000000..121a3dffd --- /dev/null +++ b/libbb/recursive_action.c @@ -0,0 +1,126 @@ +/* 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 true_action(const char *fileName, struct stat *statbuf, void* userData, int depth) +{ + 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 !depthFirst, dirAction 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 recursive_action(const char *fileName, + int recurse, int followLinks, int depthFirst, + int (*fileAction)(const char *fileName, struct stat *statbuf, void* userData, int depth), + int (*dirAction)(const char *fileName, struct stat *statbuf, void* userData, int depth), + void* userData, + int depth) +{ + struct stat statbuf; + int status; + DIR *dir; + struct dirent *next; + + if (!fileAction) fileAction = true_action; + if (!dirAction) dirAction = true_action; + + status = (followLinks ? stat : lstat)(fileName, &statbuf); + + if (status < 0) { +#ifdef DEBUG_RECURS_ACTION + bb_error_msg("status=%d followLinks=%d TRUE=%d", + status, followLinks, TRUE); +#endif + bb_perror_msg("%s", fileName); + return FALSE; + } + + /* 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 ( /* (!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 (!recurse) { + return dirAction(fileName, &statbuf, userData, depth); + } + + if (!depthFirst) { + status = dirAction(fileName, &statbuf, userData, depth); + if (!status) { + bb_perror_msg("%s", fileName); + return FALSE; + } + 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 {} \;" */ + bb_perror_msg("%s", fileName); + return FALSE; + } + status = TRUE; + while ((next = readdir(dir)) != NULL) { + char *nextFile; + + nextFile = concat_subpath_file(fileName, next->d_name); + if (nextFile == NULL) + continue; + if (!recursive_action(nextFile, TRUE, followLinks, depthFirst, + fileAction, dirAction, userData, depth+1)) { + status = FALSE; + } + free(nextFile); + } + closedir(dir); + if (depthFirst) { + if (!dirAction(fileName, &statbuf, userData, depth)) { + bb_perror_msg("%s", fileName); + return FALSE; + } + } + + if (!status) + return FALSE; + return TRUE; +} diff --git a/libbb/remove_file.c b/libbb/remove_file.c new file mode 100644 index 000000000..ab159a481 --- /dev/null +++ b/libbb/remove_file.c @@ -0,0 +1,111 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "libbb.h" + +int remove_file(const char *path, int flags) +{ + struct stat path_stat; + int path_exists = 1; + + if (lstat(path, &path_stat) < 0) { + if (errno != ENOENT) { + bb_perror_msg("cannot stat '%s'", path); + return -1; + } + + path_exists = 0; + } + + if (!path_exists) { + 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; + } + + if ((dp = opendir(path)) == 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; + } else { + 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 000000000..74a64140f --- /dev/null +++ b/libbb/restricted_shell.c @@ -0,0 +1,57 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#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 restricted_shell ( const char *shell ) +{ + char *line; + + setusershell ( ); + while (( line = getusershell ( ))) { + if (( *line != '#' ) && ( strcmp ( line, shell ) == 0 )) + break; + } + endusershell ( ); + return line ? 0 : 1; +} + diff --git a/libbb/run_shell.c b/libbb/run_shell.c new file mode 100644 index 000000000..6be09088d --- /dev/null +++ b/libbb/run_shell.c @@ -0,0 +1,101 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include "libbb.h" +#ifdef CONFIG_SELINUX +#include /* for setexeccon */ +#endif + +#ifdef CONFIG_SELINUX +static security_context_t current_sid; + +void +renew_current_security_context(void) +{ + if (current_sid) + freecon(current_sid); /* Release old context */ + getcon(¤t_sid); /* update */ +} +void +set_current_security_context(security_context_t sid) +{ + if (current_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 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(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; +#ifdef CONFIG_SELINUX + if (current_sid && !setexeccon(current_sid)) { + freecon(current_sid); + execve(shell, (char **) args, environ); + } else +#endif + execv(shell, (char **) args); + bb_perror_msg_and_die("cannot run %s", shell); +} diff --git a/libbb/safe_strncpy.c b/libbb/safe_strncpy.c new file mode 100644 index 000000000..42bc16ea0 --- /dev/null +++ b/libbb/safe_strncpy.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 +#include "libbb.h" + + + +/* Like strncpy but make sure the resulting string is always 0 terminated. */ +char * safe_strncpy(char *dst, const char *src, size_t size) +{ + if (!size) return dst; + dst[--size] = '\0'; + return strncpy(dst, src, size); +} diff --git a/libbb/safe_write.c b/libbb/safe_write.c new file mode 100644 index 000000000..c81f1247f --- /dev/null +++ b/libbb/safe_write.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 +#include +#include +#include "libbb.h" + + + +ssize_t 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/setup_environment.c b/libbb/setup_environment.c new file mode 100644 index 000000000..874a58efa --- /dev/null +++ b/libbb/setup_environment.c @@ -0,0 +1,83 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include "libbb.h" + + + +#define DEFAULT_LOGIN_PATH "/bin:/usr/bin" +#define DEFAULT_ROOT_LOGIN_PATH "/usr/sbin:/bin:/usr/bin:/sbin" + +void setup_environment(const char *shell, int loginshell, int changeenv, const struct passwd *pw) +{ + if (loginshell) { + const char *term; + + /* Change the current working directory to be the home directory + * of the user. It is a fatal error for this process to be unable + * to change to that directory. There is no "default" home + * directory. + * Some systems default to HOME=/ + */ + if (chdir(pw->pw_dir)) { + xchdir("/"); + fputs("warning: cannot change to home directory\n", stderr); + } + + /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. + Unset all other environment variables. */ + term = getenv("TERM"); + clearenv(); + if (term) + xsetenv("TERM", term); + xsetenv("HOME", pw->pw_dir); + xsetenv("SHELL", shell); + xsetenv("USER", pw->pw_name); + xsetenv("LOGNAME", pw->pw_name); + xsetenv("PATH", (pw->pw_uid ? DEFAULT_LOGIN_PATH : DEFAULT_ROOT_LOGIN_PATH)); + } + else if (changeenv) { + /* Set HOME, SHELL, and if not becoming a super-user, + USER and LOGNAME. */ + xsetenv("HOME", pw->pw_dir); + xsetenv("SHELL", shell); + if (pw->pw_uid) { + xsetenv("USER", pw->pw_name); + xsetenv("LOGNAME", pw->pw_name); + } + } +} diff --git a/libbb/sha1.c b/libbb/sha1.c new file mode 100644 index 000000000..34813e24a --- /dev/null +++ b/libbb/sha1.c @@ -0,0 +1,178 @@ +/* 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 +#include +#include +#include +#include +#include +#include + +#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 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 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 *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/simplify_path.c b/libbb/simplify_path.c new file mode 100644 index 000000000..b714c66b6 --- /dev/null +++ b/libbb/simplify_path.c @@ -0,0 +1,50 @@ +/* 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 *bb_simplify_path(const char *path) +{ + char *s, *start, *p; + + if (path[0] == '/') + start = xstrdup(path); + else { + s = xgetcwd(NULL); + start = concat_path_file(s, path); + free(s); + } + p = s = start; + + do { + if (*p == '/') { + if (*s == '/') { /* skip duplicate (or initial) slash */ + continue; + } else if (*s == '.') { + if (s[1] == '/' || s[1] == 0) { /* remove extra '.' */ + continue; + } else if ((s[1] == '.') && (s[2] == '/' || s[2] == 0)) { + ++s; + if (p > start) { + while (*--p != '/'); /* omit previous dir */ + } + 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 000000000..02c1f5828 --- /dev/null +++ b/libbb/skip_whitespace.c @@ -0,0 +1,18 @@ +/* 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 +#include "libbb.h" + +char *skip_whitespace(const char *s) +{ + while (isspace(*s)) ++s; + + return (char *) s; +} diff --git a/libbb/speed_table.c b/libbb/speed_table.c new file mode 100644 index 000000000..6137b7731 --- /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 = (sizeof(speeds) / sizeof(struct speed_map)) }; + +unsigned int 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 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/trim.c b/libbb/trim.c new file mode 100644 index 000000000..d36391540 --- /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 +#include +#include +#include "libbb.h" + + +void 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"); + memmove(s, s + lws, len -= lws); + } + s[len] = 0; +} diff --git a/libbb/u_signal_names.c b/libbb/u_signal_names.c new file mode 100644 index 000000000..88311dd9c --- /dev/null +++ b/libbb/u_signal_names.c @@ -0,0 +1,58 @@ +/* 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" + +static const struct signal_name { + int number; + char name[5]; +} signals[] = { + // SUSv3 says kill must support these, and specifies the numerical values, + // http://www.opengroup.org/onlinepubs/009695399/utilities/kill.html + {0, "0"}, {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"} +}; + +// Convert signal name to number. + +int get_signum(const char *name) +{ + int i; + + i = atoi(name); + if (i) return i; + for (i = 0; i < sizeof(signals) / sizeof(struct signal_name); i++) + if (!strcasecmp(signals[i].name, name) || + (!strncasecmp(signals[i].name, "SIG", 3) + && !strcasecmp(signals[i].name+3, signals[i].name))) + return signals[i].number; + return -1; +} + +// Convert signal number to name + +const char *get_signame(int number) +{ + int i; + static char buf[8]; + + for (i=0; i < sizeof(signals) / sizeof(struct signal_name); i++) { + if (number == signals[i].number) { + return signals[i].name; + } + } + + itoa_to_buf(number, buf, 8); + return buf; +} diff --git a/libbb/uuencode.c b/libbb/uuencode.c new file mode 100644 index 000000000..38401205b --- /dev/null +++ b/libbb/uuencode.c @@ -0,0 +1,63 @@ +/* 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] = { + '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 */ +}; + +const char bb_uuenc_tbl_std[65] = { + '`', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '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 the string S of length LENGTH to 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 bb_uuencode(const unsigned char *s, char *store, const int length, const char *tbl) +{ + int i; + char *p = store; + + /* Transform the 3x8 bits to 4x6 bits, as required by base64. */ + for (i = 0; i < length; i += 3) { + *p++ = tbl[s[0] >> 2]; + *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)]; + *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)]; + *p++ = tbl[s[2] & 0x3f]; + s += 3; + } + /* Pad the result if necessary... */ + if (i == length + 1) { + *(p - 1) = tbl[64]; + } + else if (i == length + 2) { + *(p - 1) = *(p - 2) = tbl[64]; + } + /* ...and zero-terminate it. */ + *p = '\0'; +} diff --git a/libbb/vdprintf.c b/libbb/vdprintf.c new file mode 100644 index 000000000..ffcb7a444 --- /dev/null +++ b/libbb/vdprintf.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 +#include +#include "libbb.h" + + + +#if (__GLIBC__ < 2) +int vdprintf(int d, const char *format, va_list ap) +{ + char buf[BUF_SIZE]; + int len; + + len = vsprintf(buf, 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 000000000..0f018c517 --- /dev/null +++ b/libbb/verror_msg.c @@ -0,0 +1,46 @@ +/* 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 + +int logmode = LOGMODE_STDIO; +const char *msg_eol = "\n"; + +void bb_verror_msg(const char *s, va_list p, const char* strerr) +{ + /* va_copy is used because it is not portable + * to use va_list p twice */ + va_list p2; + va_copy(p2, p); + + if (logmode & LOGMODE_STDIO) { + fflush(stdout); + fprintf(stderr, "%s: ", applet_name); + vfprintf(stderr, s, p); + if (!strerr) + fputs(msg_eol, stderr); + else + fprintf(stderr, "%s%s%s", + s ? ": " : "", + strerr, msg_eol); + } + if (ENABLE_FEATURE_SYSLOG && (logmode & LOGMODE_SYSLOG)) { + if (!strerr) + vsyslog(LOG_ERR, s, p2); + else { + char *msg; + if (vasprintf(&msg, s, p2) < 0) + bb_error_msg_and_die(bb_msg_memory_exhausted); + syslog(LOG_ERR, "%s: %s", msg, strerr); + free(msg); + } + } + va_end(p2); +} diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c new file mode 100644 index 000000000..ebd32f8cd --- /dev/null +++ b/libbb/vfork_daemon_rexec.c @@ -0,0 +1,67 @@ +/* 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 +#include +#include +#include "libbb.h" + + +#ifdef BB_NOMMU +void vfork_daemon_rexec(int nochdir, int noclose, + int argc, char **argv, char *foreground_opt) +{ + int fd; + char **vfork_args; + int a = 0; + + setsid(); + + if (!nochdir) + xchdir("/"); + + if (!noclose && (fd = open(bb_dev_null, O_RDWR, 0)) != -1) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > 2) + close(fd); + } + + vfork_args = xcalloc(sizeof(char *), argc + 3); + vfork_args[a++] = CONFIG_BUSYBOX_EXEC_PATH; + while(*argv) { + vfork_args[a++] = *argv; + argv++; + } + vfork_args[a] = foreground_opt; + switch (vfork()) { + case 0: /* child */ + /* Make certain we are not a session leader, or else we + * might reacquire a controlling terminal */ + if (vfork()) + _exit(0); + execv(vfork_args[0], vfork_args); + bb_perror_msg_and_die("execv %s", vfork_args[0]); + case -1: /* error */ + bb_perror_msg_and_die("vfork"); + default: /* parent */ + exit(0); + } +} +#endif /* BB_NOMMU */ diff --git a/libbb/vherror_msg.c b/libbb/vherror_msg.c new file mode 100644 index 000000000..04446a090 --- /dev/null +++ b/libbb/vherror_msg.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" + +void bb_vherror_msg(const char *s, va_list p) +{ + bb_verror_msg(s, p, hstrerror(h_errno)); +} diff --git a/libbb/vinfo_msg.c b/libbb/vinfo_msg.c new file mode 100644 index 000000000..fa2798625 --- /dev/null +++ b/libbb/vinfo_msg.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" +#include + +void bb_vinfo_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_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); +} diff --git a/libbb/vperror_msg.c b/libbb/vperror_msg.c new file mode 100644 index 000000000..c3f79c23b --- /dev/null +++ b/libbb/vperror_msg.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" + +void bb_vperror_msg(const char *s, va_list p) +{ + bb_verror_msg(s, p, strerror(errno)); +} diff --git a/libbb/warn_ignoring_args.c b/libbb/warn_ignoring_args.c new file mode 100644 index 000000000..be78a4414 --- /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 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 000000000..26e6a13e2 --- /dev/null +++ b/libbb/wfopen.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" + +FILE *fopen_or_warn(const char *path, const char *mode) +{ + FILE *fp = fopen(path, mode); + if (!fp) { + bb_perror_msg("%s", path); + errno = 0; + } + return fp; +} diff --git a/libbb/wfopen_input.c b/libbb/wfopen_input.c new file mode 100644 index 000000000..3da855fe6 --- /dev/null +++ b/libbb/wfopen_input.c @@ -0,0 +1,31 @@ +/* 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. + * + * Note: We also consider "" to main stdin (for 'cmp' at least). + */ + +#include "libbb.h" + +FILE *fopen_or_warn_stdin(const char *filename) +{ + FILE *fp = stdin; + + if (filename != bb_msg_standard_input + && filename[0] + && (filename[0] != '-' || filename[1]) + ) { + fp = fopen_or_warn(filename, "r"); + } + + return fp; +} diff --git a/libbb/xatonum.c b/libbb/xatonum.c new file mode 100644 index 000000000..35607c317 --- /dev/null +++ b/libbb/xatonum.c @@ -0,0 +1,69 @@ +/* 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 +extern 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 xatoi_u(const char *numstr) +{ + return xatou_range(numstr, 0, INT_MAX); +} + +uint16_t 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 000000000..53ba544eb --- /dev/null +++ b/libbb/xatonum_template.c @@ -0,0 +1,185 @@ +/* +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 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. Speed isn't critical here + * since we're parsing commandline args. So make sure we get the + * actual isspace function rather than a lnumstrer macro implementaion. */ + if ((*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->suffix) { + if (strcmp(suffixes->suffix, e) == 0) { + if (XSTR_UTYPE_MAX / suffixes->mult < r) + goto range; /* overflow! */ + ++e; + r *= suffixes->mult; + break; + } + ++suffixes; + } + } + + /* Note: trailing space is an error. + It would be easy enough to allow though if desired. */ + if (*e) + goto inval; + /* 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 xstrtou(_range)(const char *numstr, int base, + unsigned type lower, + unsigned type upper) +{ + return xstrtou(_range_sfx)(numstr, base, lower, upper, NULL); +} + +unsigned type 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 xstrtou()(const char *numstr, int base) +{ + return xstrtou(_range_sfx)(numstr, base, 0, XSTR_UTYPE_MAX, NULL); +} + +unsigned type 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 xatou(_range)(const char *numstr, + unsigned type lower, + unsigned type upper) +{ + return xstrtou(_range_sfx)(numstr, 10, lower, upper, NULL); +} + +unsigned type xatou(_sfx)(const char *numstr, + const struct suffix_mult *suffixes) +{ + return xstrtou(_range_sfx)(numstr, 10, 0, XSTR_UTYPE_MAX, suffixes); +} + +unsigned type xatou()(const char *numstr) +{ + return xatou(_sfx)(numstr, NULL); +} + +/* Signed ones */ + +type 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; + + if ((p[0] == '-') && (p[1] != '+')) { + ++p; + ++u; /* two's complement */ + } + + 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 xstrto(_range)(const char *numstr, int base, type lower, type upper) +{ + return xstrto(_range_sfx)(numstr, base, lower, upper, NULL); +} + +type 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 xato(_range)(const char *numstr, type lower, type upper) +{ + return xstrto(_range_sfx)(numstr, 10, lower, upper, NULL); +} + +type xato(_sfx)(const char *numstr, const struct suffix_mult *suffixes) +{ + return xstrto(_range_sfx)(numstr, 10, XSTR_TYPE_MIN, XSTR_TYPE_MAX, suffixes); +} + +type 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 000000000..b85648007 --- /dev/null +++ b/libbb/xconnect.c @@ -0,0 +1,153 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Connect to host at port using address resolution from getaddrinfo + * + */ + +#include "libbb.h" + +/* Return network byte ordered 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 short bb_lookup_port(const char *port, const char *protocol, unsigned short default_port) +{ + unsigned short port_nr = htons(default_port); + if (port) { + char *endptr; + int old_errno; + long port_long; + + /* 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; + errno = 0; + port_long = strtol(port, &endptr, 10); + if (errno != 0 || *endptr!='\0' || endptr==port || port_long < 0 || port_long > 65535) { + struct servent *tserv = getservbyname(port, protocol); + if (tserv) { + port_nr = tserv->s_port; + } + } else { + port_nr = htons(port_long); + } + errno = old_errno; + } + return port_nr; +} + +void 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); +} + +void 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("cannot connect to remote host (%s)", + inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr)); + bb_perror_msg_and_die("cannot connect to remote host"); + } +} + +int 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; +} + +static const int one = 1; +int setsockopt_reuseaddr(int fd) +{ + return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); +} +int setsockopt_broadcast(int fd) +{ + return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)); +} + +int dotted2sockaddr(const char *dotted, struct sockaddr* sp, int socklen) +{ + union { + struct in_addr a4; +#if ENABLE_FEATURE_IPV6 + struct in6_addr a6; +#endif + } a; + + /* TODO maybe: port spec? like n.n.n.n:nn */ + +#if ENABLE_FEATURE_IPV6 + if (socklen >= sizeof(struct sockaddr_in6) + && inet_pton(AF_INET6, dotted, &a.a6) > 0 + ) { + ((struct sockaddr_in6*)sp)->sin6_family = AF_INET6; + ((struct sockaddr_in6*)sp)->sin6_addr = a.a6; + /* ((struct sockaddr_in6*)sp)->sin6_port = */ + return 0; /* success */ + } +#endif + if (socklen >= sizeof(struct sockaddr_in) + && inet_pton(AF_INET, dotted, &a.a4) > 0 + ) { + ((struct sockaddr_in*)sp)->sin_family = AF_INET; + ((struct sockaddr_in*)sp)->sin_addr = a.a4; + /* ((struct sockaddr_in*)sp)->sin_port = */ + return 0; /* success */ + } + return 1; +} + +int xsocket_stream_ip4or6(sa_family_t *fp) +{ + int fd; +#if ENABLE_FEATURE_IPV6 + fd = socket(AF_INET6, SOCK_STREAM, 0); + if (fp) *fp = AF_INET6; + if (fd < 0) +#endif + { + fd = xsocket(AF_INET, SOCK_STREAM, 0); + if (fp) *fp = AF_INET; + } + return fd; +} + +int create_and_bind_socket_ip4or6(const char *hostaddr, int port) +{ + int fd; + sockaddr_inet sa; + + memset(&sa, 0, sizeof(sa)); + if (hostaddr) { + if (dotted2sockaddr(hostaddr, &sa.sa, sizeof(sa))) + bb_error_msg_and_die("bad address '%s'", hostaddr); + /* user specified bind addr dictates family */ + fd = xsocket(sa.sa.sa_family, SOCK_STREAM, 0); + } else + fd = xsocket_stream_ip4or6(&sa.sa.sa_family); + setsockopt_reuseaddr(fd); + + /* if (port >= 0) { */ +#if ENABLE_FEATURE_IPV6 + if (sa.sa.sa_family == AF_INET6 /* && !sa.sin6.sin6_port */) + sa.sin6.sin6_port = htons(port); +#endif + if (sa.sa.sa_family == AF_INET /* && !sa.sin.sin_port */) + sa.sin.sin_port = htons(port); + /* } */ + + xbind(fd, &sa.sa, sizeof(sa)); + return fd; +} diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c new file mode 100644 index 000000000..313e32814 --- /dev/null +++ b/libbb/xfuncs.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 Denis Vlasenko + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ + +#include "busybox.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. + */ +// Die if we can't allocate size bytes of memory. +void *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 *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 *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 * 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 * 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 = 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 *xfopen(const char *path, const char *mode) +{ + FILE *fp = fopen(path, mode); + if (fp == NULL) + bb_perror_msg_and_die("%s", path); + return fp; +} + +// Die if we can't open an existing file and return an fd. +int xopen(const char *pathname, int flags) +{ + //if (ENABLE_DEBUG && (flags & O_CREAT)) + // bb_error_msg_and_die("xopen() with O_CREAT"); + + return xopen3(pathname, flags, 0666); +} + +// Die if we can't open a new file and return an fd. +int xopen3(const char *pathname, int flags, int mode) +{ + int ret; + + ret = open(pathname, flags, mode); + if (ret < 0) { + bb_perror_msg_and_die("%s", pathname); + } + return ret; +} + +/* +int ndelay_off(int fd) +{ + return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK); +} +*/ +// Turn on nonblocking I/O on a fd +int ndelay_on(int fd) +{ + return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK); +} + +// Die with an error message if we can't write the entire buffer. +void xwrite(int fd, const void *buf, size_t count) +{ + if (count) { + ssize_t size = full_write(fd, buf, count); + if (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 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 die_if_ferror(FILE *fp, const char *fn) +{ + if (ferror(fp)) { + bb_error_msg_and_die("%s: I/O error", fn); + } +} + +// Die with an error message if stdout has ferror set. +void 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 xfflush_stdout(void) +{ + if (fflush(stdout)) { + bb_perror_msg_and_die(bb_msg_standard_output); + } +} + +// This does a fork/exec in one call, using vfork(). Return PID of new child, +// -1 for failure. Runs argv[0], searching path if that has no / in it. +pid_t spawn(char **argv) +{ + static int failed; + pid_t pid; + void *app = ENABLE_FEATURE_SH_STANDALONE_SHELL ? find_applet_by_name(argv[0]) : 0; + + // Be nice to nommu machines. + failed = 0; + pid = vfork(); + if (pid < 0) return pid; + if (!pid) { + execvp(app ? CONFIG_BUSYBOX_EXEC_PATH : *argv, argv); + + // We're 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 = -1; + _exit(0); + } + return failed ? failed : pid; +} + +// Die with an error message if we can't spawn a child process. +pid_t xspawn(char **argv) +{ + pid_t pid = spawn(argv); + if (pid < 0) bb_perror_msg_and_die("%s", *argv); + return pid; +} + +// Wait for the specified child PID to exit, returning child's error return. +int wait4pid(int pid) +{ + int status; + + if (pid == -1 || waitpid(pid, &status, 0) == -1) return -1; + if (WIFEXITED(status)) return WEXITSTATUS(status); + if (WIFSIGNALED(status)) return WTERMSIG(status); + return 0; +} + +void xsetenv(const char *key, const char *value) +{ + if (setenv(key, value, 1)) + bb_error_msg_and_die(bb_msg_memory_exhausted); +} + +// Converts unsigned long long value into compact 4-char +// representation. Examples: "1234", "1.2k", " 27M", "123T" +// Fifth char is always '\0' +void smart_ulltoa5(unsigned long long ul, char buf[5]) +{ + char *fmt; + char c; + unsigned v,idx = 0; + ul *= 10; + if (ul > 9999*10) { // do not scale if 9999 or less + while (ul >= 10000) { + ul /= 1024; + idx++; + } + } + v = ul; // ullong divisions are expensive, avoid them + + fmt = " 123456789"; + if (!idx) { // 9999 or less: use 1234 format + c = buf[0] = " 123456789"[v/10000]; + if (c!=' ') fmt = "0123456789"; + c = buf[1] = fmt[v/1000%10]; + if (c!=' ') fmt = "0123456789"; + buf[2] = fmt[v/100%10]; + buf[3] = "0123456789"[v/10%10]; + } else { + if (v>=10*10) { // scaled value is >=10: use 123M format + c = buf[0] = " 123456789"[v/1000]; + if (c!=' ') fmt = "0123456789"; + buf[1] = fmt[v/100%10]; + buf[2] = "0123456789"[v/10%10]; + } else { // scaled value is <10: use 1.2M format + buf[0] = "0123456789"[v/10]; + buf[1] = '.'; + buf[2] = "0123456789"[v%10]; + } + // see http://en.wikipedia.org/wiki/Tera + buf[3] = " kMGTPEZY"[idx]; + } + buf[4] = '\0'; +} + +// Convert unsigned integer to ascii, writing into supplied buffer. A +// truncated result is always null terminated (unless buflen is 0), and +// contains the first few digits of the result ala strncpy. +void BUG_sizeof_unsigned_not_4(void); +void 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; + } + } + *buf = '\0'; + } +} + +// Convert signed integer to ascii, like utoa_to_buf() +void itoa_to_buf(int n, char *buf, unsigned buflen) +{ + if (buflen && n<0) { + n = -n; + *buf++ = '-'; + buflen--; + } + 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. +// Int should always be 32 bits on any remotely Unix-like system, see +// http://www.unix.org/whitepapers/64bit.html for the reasons why. + +static char local_buf[12]; + +// Convert unsigned integer to ascii using a static buffer (returned). +char *utoa(unsigned n) +{ + utoa_to_buf(n, local_buf, sizeof(local_buf)); + + return local_buf; +} + +// Convert signed integer to ascii using a static buffer (returned). +char *itoa(int n) +{ + itoa_to_buf(n, local_buf, sizeof(local_buf)); + + return local_buf; +} + +// 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 xsetgid(gid_t gid) +{ + if (setgid(gid)) bb_error_msg_and_die("setgid"); +} + +// Die with an error message if we can't set uid. (See xsetgid() for why.) +void xsetuid(uid_t uid) +{ + if (setuid(uid)) bb_error_msg_and_die("setuid"); +} + +// Return how long the file at fd is, if there's any way to determine it. +off_t 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; +} + +// Die with an error message if we can't malloc() enough space and do an +// sprintf() into that space. +char *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; +} + +// Die with an error message if we can't copy an entire FILE * to stdout, then +// close that file. +void xprint_and_close_file(FILE *file) +{ + fflush(stdout); + // copyfd outputs error messages for us. + if (bb_copyfd_eof(fileno(file), 1) == -1) + exit(xfunc_error_retval); + + fclose(file); +} + +// Die if we can't chdir to a new path. +void xchdir(const char *path) +{ + if (chdir(path)) + bb_perror_msg_and_die("chdir(%s)", path); +} + +// Print a warning message if opendir() fails, but don't die. +DIR *warn_opendir(const char *path) +{ + DIR *dp; + + if ((dp = opendir(path)) == NULL) { + bb_perror_msg("cannot open '%s'", path); + return NULL; + } + return dp; +} + +// Die with an error message if opendir() fails. +DIR *xopendir(const char *path) +{ + DIR *dp; + + if ((dp = opendir(path)) == NULL) + bb_perror_msg_and_die("cannot open '%s'", path); + return dp; +} + +#ifndef BB_NOMMU +// Die with an error message if we can't daemonize. +void xdaemon(int nochdir, int noclose) +{ + if (daemon(nochdir, noclose)) + bb_perror_msg_and_die("daemon"); +} +#endif + +// Die with an error message if we can't open a new socket. +int xsocket(int domain, int type, int protocol) +{ + int r = socket(domain, type, protocol); + + if (r < 0) bb_perror_msg_and_die("socket"); + + return r; +} + +// Die with an error message if we can't bind a socket to an address. +void 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 xlisten(int s, int backlog) +{ + if (listen(s, backlog)) bb_perror_msg_and_die("listen"); +} + +// xstat() - a stat() which dies on failure with meaningful error message +void xstat(char *name, struct stat *stat_buf) +{ + if (stat(name, stat_buf)) + bb_perror_msg_and_die("can't stat '%s'", name); +} + +/* 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 get_terminal_width_height(int fd, int *width, int *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; +} diff --git a/libbb/xgetcwd.c b/libbb/xgetcwd.c new file mode 100644 index 000000000..0ac450d3b --- /dev/null +++ b/libbb/xgetcwd.c @@ -0,0 +1,44 @@ +/* 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" + +/* Amount to increase buffer size by in each try. */ +#define PATH_INCR 32 + +/* 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 * +xgetcwd(char *cwd) +{ + char *ret; + unsigned path_max; + + path_max = (unsigned) PATH_MAX; + path_max += 2; /* The getcwd docs say to do this. */ + + if (cwd==0) + cwd = xmalloc(path_max); + + while ((ret = getcwd(cwd, path_max)) == NULL && errno == ERANGE) { + path_max += PATH_INCR; + cwd = xrealloc(cwd, path_max); + } + + if (ret == NULL) { + free(cwd); + bb_perror_msg("getcwd"); + return NULL; + } + + return cwd; +} diff --git a/libbb/xgethostbyname.c b/libbb/xgethostbyname.c new file mode 100644 index 000000000..c3158c339 --- /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 *xgethostbyname(const char *name) +{ + struct hostent *retval = gethostbyname(name); + if (!retval) + bb_herror_msg_and_die("%s", name); + return retval; +} diff --git a/libbb/xgethostbyname2.c b/libbb/xgethostbyname2.c new file mode 100644 index 000000000..83d538669 --- /dev/null +++ b/libbb/xgethostbyname2.c @@ -0,0 +1,22 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini xgethostbyname2 implementation. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include +#include "libbb.h" + + +#ifdef CONFIG_FEATURE_IPV6 +struct hostent *xgethostbyname2(const char *name, int af) +{ + struct hostent *retval; + + if ((retval = gethostbyname2(name, af)) == NULL) + bb_herror_msg_and_die("%s", name); + + return retval; +} +#endif diff --git a/libbb/xreadlink.c b/libbb/xreadlink.c new file mode 100644 index 000000000..14863d9d5 --- /dev/null +++ b/libbb/xreadlink.c @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * xreadlink.c - safe implementation of readlink. + * Returns a NULL on failure... + */ + +#include + +/* + * NOTE: This function returns a malloced char* that you will have to free + * yourself. You have been warned. + */ + +#include +#include "libbb.h" + +char *xreadlink(const char *path) +{ + enum { GROWBY = 80 }; /* how large we will grow strings by */ + + char *buf = NULL; + int bufsize = 0, readsize = 0; + + do { + buf = xrealloc(buf, bufsize += GROWBY); + readsize = readlink(path, buf, bufsize); /* 1st try */ + if (readsize == -1) { + bb_perror_msg("%s", path); + free(buf); + return NULL; + } + } + while (bufsize < readsize + 1); + + buf[readsize] = '\0'; + + return buf; +} diff --git a/libbb/xregcomp.c b/libbb/xregcomp.c new file mode 100644 index 000000000..4bcb9aedf --- /dev/null +++ b/libbb/xregcomp.c @@ -0,0 +1,26 @@ +/* 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 +#include "libbb.h" +#include "xregex.h" + + + +void xregcomp(regex_t *preg, const char *regex, int cflags) +{ + int ret; + if ((ret = regcomp(preg, regex, cflags)) != 0) { + int errmsgsz = regerror(ret, preg, NULL, 0); + char *errmsg = xmalloc(errmsgsz); + regerror(ret, preg, errmsg, errmsgsz); + bb_error_msg_and_die("xregcomp: %s", errmsg); + } +} -- cgit v1.2.3-55-g6feb