busybox-1.22.1/0000755000000000000000000000000012320365466011775 5ustar rootrootbusybox-1.22.1/applets/0000755000000000000000000000000012320365365013443 5ustar rootrootbusybox-1.22.1/applets/usage_pod.c0000644000000000000000000000440312263563520015555 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright (C) 2009 Denys Vlasenko. * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include #include #include #include #include #include "autoconf.h" #define SKIP_applet_main #define ALIGN1 /* nothing, just to placate applet_tables.h */ #define ALIGN2 /* nothing, just to placate applet_tables.h */ #include "applet_tables.h" /* Since we can't use platform.h, have to do this again by hand: */ #if ENABLE_NOMMU # define BB_MMU 0 # define USE_FOR_NOMMU(...) __VA_ARGS__ # define USE_FOR_MMU(...) #else # define BB_MMU 1 # define USE_FOR_NOMMU(...) # define USE_FOR_MMU(...) __VA_ARGS__ #endif #include "usage.h" #define MAKE_USAGE(aname, usage) { aname, usage }, static struct usage_data { const char *aname; const char *usage; } usage_array[] = { #include "applets.h" }; static int compare_func(const void *a, const void *b) { const struct usage_data *ua = a; const struct usage_data *ub = b; return strcmp(ua->aname, ub->aname); } int main(void) { int col, len2; int i; int num_messages = sizeof(usage_array) / sizeof(usage_array[0]); if (num_messages == 0) return 0; qsort(usage_array, num_messages, sizeof(usage_array[0]), compare_func); col = 0; for (i = 0; i < num_messages; i++) { len2 = strlen(usage_array[i].aname) + 2; if (col >= 76 - len2) { printf(",\n"); col = 0; } if (col == 0) { col = 6; printf("\t"); } else { printf(", "); } printf(usage_array[i].aname); col += len2; } printf("\n\n"); printf("=head1 COMMAND DESCRIPTIONS\n\n"); printf("=over 4\n\n"); for (i = 0; i < num_messages; i++) { if (usage_array[i].aname[0] >= 'a' && usage_array[i].aname[0] <= 'z' && usage_array[i].usage[0] != NOUSAGE_STR[0] ) { printf("=item B<%s>\n\n", usage_array[i].aname); if (usage_array[i].usage[0]) printf("%s %s\n\n", usage_array[i].aname, usage_array[i].usage); else printf("%s\n\n", usage_array[i].aname); } } return 0; } /* TODO: we used to make options bold with B<> and output an example too: =item B cat [B<-u>] [FILE]... Concatenate FILE(s) and print them to stdout Options: -u Use unbuffered i/o (ignored) Example: $ cat /proc/uptime 110716.72 17.67 */ busybox-1.22.1/applets/install.sh0000755000000000000000000000502012263563520015444 0ustar rootroot#!/bin/sh export LC_ALL=POSIX export LC_CTYPE=POSIX prefix=$1 if [ -z "$prefix" ]; then echo "usage: applets/install.sh DESTINATION [--symlinks/--hardlinks/--scriptwrapper]" exit 1 fi h=`sort busybox.links | uniq` linkopts="" scriptwrapper="n" cleanup="0" noclobber="0" case "$2" in --hardlinks) linkopts="-f";; --symlinks) linkopts="-fs";; --scriptwrapper) scriptwrapper="y";swrapall="y";; --sw-sh-hard) scriptwrapper="y";linkopts="-f";; --sw-sh-sym) scriptwrapper="y";linkopts="-fs";; --cleanup) cleanup="1";; --noclobber) noclobber="1";; "") h="";; *) echo "Unknown install option: $2"; exit 1;; esac if [ -n "$DO_INSTALL_LIBS" ] && [ "$DO_INSTALL_LIBS" != "n" ]; then # get the target dir for the libs # assume it starts with lib libdir=$($CC -print-file-name=libc.so | \ sed -n 's%^.*\(/lib[^\/]*\)/libc.so%\1%p') if test -z "$libdir"; then libdir=/lib fi mkdir -p "$prefix/$libdir" || exit 1 for i in $DO_INSTALL_LIBS; do rm -f "$prefix/$libdir/$i" || exit 1 if [ -f "$i" ]; then cp -pPR "$i" "$prefix/$libdir/" || exit 1 chmod 0644 "$prefix/$libdir/$i" || exit 1 fi done fi if [ "$cleanup" = "1" ] && [ -e "$prefix/bin/busybox" ]; then inode=`ls -i "$prefix/bin/busybox" | awk '{print $1}'` sub_shell_it=` cd "$prefix" for d in usr/sbin usr/bin sbin bin; do pd=$PWD if [ -d "$d" ]; then cd "$d" ls -iL . | grep "^ *$inode" | awk '{print $2}' | env -i xargs rm -f fi cd "$pd" done ` exit 0 fi rm -f "$prefix/bin/busybox" || exit 1 mkdir -p "$prefix/bin" || exit 1 install -m 755 busybox "$prefix/bin/busybox" || exit 1 for i in $h; do appdir=`dirname "$i"` mkdir -p "$prefix/$appdir" || exit 1 if [ "$scriptwrapper" = "y" ]; then if [ "$swrapall" != "y" ] && [ "$i" = "/bin/sh" ]; then ln $linkopts busybox "$prefix/$i" || exit 1 else rm -f "$prefix/$i" echo "#!/bin/busybox" >"$prefix/$i" chmod +x "$prefix/$i" fi echo " $prefix/$i" else if [ "$2" = "--hardlinks" ]; then bb_path="$prefix/bin/busybox" else case "$appdir" in /) bb_path="bin/busybox" ;; /bin) bb_path="busybox" ;; /sbin) bb_path="../bin/busybox" ;; /usr/bin | /usr/sbin) bb_path="../../bin/busybox" ;; *) echo "Unknown installation directory: $appdir" exit 1 ;; esac fi if [ "$noclobber" = "0" ] || [ ! -e "$prefix/$i" ]; then echo " $prefix/$i -> $bb_path" ln $linkopts "$bb_path" "$prefix/$i" || exit 1 else echo " $prefix/$i already exists" fi fi done exit 0 busybox-1.22.1/applets/usage.c0000644000000000000000000000222212263563520014710 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright (C) 2008 Denys Vlasenko. * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include #include #include #include "autoconf.h" /* Since we can't use platform.h, have to do this again by hand: */ #if ENABLE_NOMMU # define BB_MMU 0 # define USE_FOR_NOMMU(...) __VA_ARGS__ # define USE_FOR_MMU(...) #else # define BB_MMU 1 # define USE_FOR_NOMMU(...) # define USE_FOR_MMU(...) __VA_ARGS__ #endif #include "usage.h" #define MAKE_USAGE(aname, usage) { aname, usage }, static struct usage_data { const char *aname; const char *usage; } usage_array[] = { #include "applets.h" }; static int compare_func(const void *a, const void *b) { const struct usage_data *ua = a; const struct usage_data *ub = b; return strcmp(ua->aname, ub->aname); } int main(void) { int i; int num_messages = sizeof(usage_array) / sizeof(usage_array[0]); if (num_messages == 0) return 0; qsort(usage_array, num_messages, sizeof(usage_array[0]), compare_func); for (i = 0; i < num_messages; i++) write(STDOUT_FILENO, usage_array[i].usage, strlen(usage_array[i].usage) + 1); return 0; } busybox-1.22.1/applets/individual.c0000644000000000000000000000070012263563520015733 0ustar rootroot/* Minimal wrapper to build an individual busybox applet. * * Copyright 2005 Rob Landley #include #include "usage.h" int main(int argc, char **argv) { applet_name = argv[0]; return APPLET_main(argc, argv); } void bb_show_usage(void) { fputs(APPLET_full_usage "\n", stdout); exit(EXIT_FAILURE); } busybox-1.22.1/applets/Kbuild.src0000644000000000000000000000264712263563520015376 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. obj-y := obj-y += applets.o hostprogs-y:= hostprogs-y += usage usage_pod applet_tables always:= $(hostprogs-y) # Generated files need additional love # This trick decreases amount of rebuilds # if tree is merely renamed/copied ifeq ($(srctree),$(objtree)) srctree_slash = else srctree_slash = $(srctree)/ endif HOSTCFLAGS_usage.o = -I$(srctree_slash)include -Iinclude HOSTCFLAGS_usage_pod.o = -I$(srctree_slash)include -Iinclude applets/applets.o: include/usage_compressed.h include/applet_tables.h applets/applet_tables: .config include/applets.h applets/usage: .config include/applets.h applets/usage_pod: .config include/applet_tables.h include/applets.h quiet_cmd_gen_usage_compressed = GEN include/usage_compressed.h cmd_gen_usage_compressed = $(srctree_slash)applets/usage_compressed include/usage_compressed.h applets include/usage_compressed.h: applets/usage $(srctree_slash)applets/usage_compressed $(call cmd,gen_usage_compressed) quiet_cmd_gen_applet_tables = GEN include/applet_tables.h cmd_gen_applet_tables = applets/applet_tables include/applet_tables.h include/NUM_APPLETS.h include/applet_tables.h: applets/applet_tables $(call cmd,gen_applet_tables) include/NUM_APPLETS.h: applets/applet_tables $(call cmd,gen_applet_tables) busybox-1.22.1/applets/applet_tables.c0000644000000000000000000000740512263563520016433 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Applet table generator. * Runs on host and produces include/applet_tables.h * * Copyright (C) 2007 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include #include #include #include #include #include #include #undef ARRAY_SIZE #define ARRAY_SIZE(x) ((unsigned)(sizeof(x) / sizeof((x)[0]))) #include "../include/autoconf.h" #include "../include/applet_metadata.h" struct bb_applet { const char *name; const char *main; enum bb_install_loc_t install_loc; enum bb_suid_t need_suid; /* true if instead of fork(); exec("applet"); waitpid(); * one can do fork(); exit(applet_main(argc,argv)); waitpid(); */ unsigned char noexec; /* Even nicer */ /* true if instead of fork(); exec("applet"); waitpid(); * one can simply call applet_main(argc,argv); */ unsigned char nofork; }; /* Define struct bb_applet applets[] */ #include "../include/applets.h" enum { NUM_APPLETS = ARRAY_SIZE(applets) }; static int offset[NUM_APPLETS]; static int cmp_name(const void *a, const void *b) { const struct bb_applet *aa = a; const struct bb_applet *bb = b; return strcmp(aa->name, bb->name); } int main(int argc, char **argv) { int i; int ofs; // unsigned MAX_APPLET_NAME_LEN = 1; qsort(applets, NUM_APPLETS, sizeof(applets[0]), cmp_name); ofs = 0; for (i = 0; i < NUM_APPLETS; i++) { offset[i] = ofs; ofs += strlen(applets[i].name) + 1; } /* We reuse 4 high-order bits of offset array for other purposes, * so if they are indeed needed, refuse to proceed */ if (ofs > 0xfff) return 1; if (!argv[1]) return 1; i = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0666); if (i < 0) return 1; dup2(i, 1); /* Keep in sync with include/busybox.h! */ printf("/* This is a generated file, don't edit */\n\n"); printf("#define NUM_APPLETS %u\n", NUM_APPLETS); if (NUM_APPLETS == 1) { printf("#define SINGLE_APPLET_STR \"%s\"\n", applets[0].name); printf("#define SINGLE_APPLET_MAIN %s_main\n", applets[0].main); } printf("\n"); //printf("#ifndef SKIP_definitions\n"); printf("const char applet_names[] ALIGN1 = \"\"\n"); for (i = 0; i < NUM_APPLETS; i++) { printf("\"%s\" \"\\0\"\n", applets[i].name); // if (MAX_APPLET_NAME_LEN < strlen(applets[i].name)) // MAX_APPLET_NAME_LEN = strlen(applets[i].name); } printf(";\n\n"); printf("#ifndef SKIP_applet_main\n"); printf("int (*const applet_main[])(int argc, char **argv) = {\n"); for (i = 0; i < NUM_APPLETS; i++) { printf("%s_main,\n", applets[i].main); } printf("};\n"); printf("#endif\n\n"); printf("const uint16_t applet_nameofs[] ALIGN2 = {\n"); for (i = 0; i < NUM_APPLETS; i++) { printf("0x%04x,\n", offset[i] #if ENABLE_FEATURE_PREFER_APPLETS + (applets[i].nofork << 12) + (applets[i].noexec << 13) #endif #if ENABLE_FEATURE_SUID + (applets[i].need_suid << 14) /* 2 bits */ #endif ); } printf("};\n\n"); #if ENABLE_FEATURE_INSTALLER printf("const uint8_t applet_install_loc[] ALIGN1 = {\n"); i = 0; while (i < NUM_APPLETS) { int v = applets[i].install_loc; /* 3 bits */ if (++i < NUM_APPLETS) v |= applets[i].install_loc << 4; /* 3 bits */ printf("0x%02x,\n", v); i++; } printf("};\n"); #endif //printf("#endif /* SKIP_definitions */\n"); // printf("\n"); // printf("#define MAX_APPLET_NAME_LEN %u\n", MAX_APPLET_NAME_LEN); if (argv[2]) { char line_old[80]; char line_new[80]; FILE *fp; line_old[0] = 0; fp = fopen(argv[2], "r"); if (fp) { fgets(line_old, sizeof(line_old), fp); fclose(fp); } sprintf(line_new, "#define NUM_APPLETS %u\n", NUM_APPLETS); if (strcmp(line_old, line_new) != 0) { fp = fopen(argv[2], "w"); if (!fp) return 1; fputs(line_new, fp); } } return 0; } busybox-1.22.1/applets/applets.c0000644000000000000000000000053612263563520015262 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Stub for linking busybox binary against libbusybox. * * Copyright (C) 2007 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "busybox.h" #if ENABLE_BUILD_LIBBUSYBOX int main(int argc UNUSED_PARAM, char **argv) { return lbb_main(argv); } #endif busybox-1.22.1/applets/busybox.mksuid0000755000000000000000000000251612263563520016362 0ustar rootroot#!/bin/sh # Make list of configuration variables regarding suid handling # input $1: full path to autoconf.h # input $2: full path to applets.h # input $3: full path to .config # output (stdout): list of CONFIG_ that do or may require suid # If the environment variable SUID is not set or set to DROP, # lists all config options that do not require suid permissions. # Otherwise, lists all config options for applets that DO or MAY require # suid permissions. # Maintainer: Bernhard Reutner-Fischer export LC_ALL=POSIX export LC_CTYPE=POSIX CONFIG_H=${1:-include/autoconf.h} APPLETS_H=${2:-include/applets.h} DOT_CONFIG=${3:-.config} case ${SUID:-DROP} in [dD][rR][oO][pP]) USE="DROP" ;; *) USE="suid" ;; esac $HOSTCC -E -DMAKE_SUID -include $CONFIG_H $APPLETS_H | awk -v USE=${USE} ' /^SUID[ \t]/{ if (USE == "DROP") { if ($2 != "BB_SUID_DROP") next } else { if ($2 == "BB_SUID_DROP") next } cfg = $NF gsub("\"", "", cfg) cfg = substr(cfg, 8) s[i++] = "CONFIG_" cfg s[i++] = "CONFIG_FEATURE_" cfg "_.*" } END{ while (getline < ARGV[2]) { for (j in s) { if ($0 ~ "^" s[j] "=y$") { sub(/=.*/, "") print if (s[j] !~ /\*$/) delete s[j] # can drop this applet now } } } } ' - $DOT_CONFIG busybox-1.22.1/applets/usage_compressed0000755000000000000000000000206112263563520016717 0ustar rootroot#!/bin/sh target="$1" loc="$2" test "$target" || exit 1 test "$loc" || loc=. test -x "$loc/usage" || exit 1 test "$SED" || SED=sed test "$DD" || DD=dd # Some people were bitten by their system lacking a (proper) od od -v -b /dev/null if test $? != 0; then echo 'od tool is not installed or cannot accept "-v -b" options' exit 1 fi exec >"$target.$$" echo '#define UNPACKED_USAGE "" \' "$loc/usage" | od -v -b \ | $SED -e 's/^[^ ]*//' \ -e 's/ //g' \ -e '/^$/d' \ -e 's/\(...\)/\\\1/g' \ -e 's/^/"/' \ -e 's/$/" \\/' echo '' echo '#define PACKED_USAGE \' ## Breaks on big-endian systems! ## # Extra effort to avoid using "od -t x1": -t is not available ## # in non-CONFIG_DESKTOPed busybox od ## ## "$loc/usage" | bzip2 -1 | od -v -x \ ## | $SED -e 's/^[^ ]*//' \ ## -e 's/ //g' \ ## -e '/^$/d' \ ## -e 's/\(..\)\(..\)/0x\2,0x\1,/g' ## -e 's/$/ \\/' "$loc/usage" | bzip2 -1 | $DD bs=2 skip=1 2>/dev/null | od -v -b \ | $SED -e 's/^[^ ]*//' \ -e 's/ //g' \ -e '/^$/d' \ -e 's/\(...\)/0\1,/g' \ -e 's/$/ \\/' echo '' mv -- "$target.$$" "$target" busybox-1.22.1/applets/busybox.mkll0000755000000000000000000000110612263563520016017 0ustar rootroot#!/bin/sh # Make busybox links list file. # input $1: full path to Config.h # input $2: full path to applets.h # output (stdout): list of pathnames that should be linked to busybox # Maintainer: Larry Doolittle export LC_ALL=POSIX export LC_CTYPE=POSIX CONFIG_H=${1:-include/autoconf.h} APPLETS_H=${2:-include/applets.h} $HOSTCC -E -DMAKE_LINKS -include $CONFIG_H $APPLETS_H | awk '/^[ \t]*LINK/{ dir=substr($2,7) gsub("_","/",dir) if(dir=="/ROOT") dir="" file=$3 gsub("\"","",file) if (file=="busybox") next print tolower(dir) "/" file }' busybox-1.22.1/Makefile.help0000644000000000000000000000410012267106022014346 0ustar rootroot# ========================================================================== # Build system # ========================================================================== help: @echo 'Cleaning:' @echo ' clean - delete temporary files created by build' @echo ' distclean - delete all non-source files (including .config)' @echo ' doc-clean - delete all generated documentation' @echo @echo 'Build:' @echo ' all - Executable and documentation' @echo ' busybox - the swiss-army executable' @echo ' doc - docs/BusyBox.{txt,html,1}' @echo ' html - create html-based cross-reference' @echo @echo 'Configuration:' @echo ' allnoconfig - disable all symbols in .config' @echo ' allyesconfig - enable all symbols in .config (see defconfig)' @echo ' config - text based configurator (of last resort)' @echo ' defconfig - set .config to largest generic configuration' @echo ' menuconfig - interactive curses-based configurator' @echo ' oldconfig - resolve any unresolved symbols in .config' @echo ' hosttools - build sed for the host.' @echo ' You can use these commands if the commands on the host' @echo ' is unusable. Afterwards use it like:' @echo ' make SED="$(objtree)/sed"' @$(if $(boards), \ $(foreach b, $(boards), \ printf " %-21s - Build for %s\\n" $(b) $(subst _defconfig,,$(b));) \ echo '') @echo @echo 'Installation:' @echo ' install - install busybox into CONFIG_PREFIX' @echo ' uninstall' @echo @echo 'Development:' @echo ' baseline - create busybox_old for bloatcheck.' @echo ' bloatcheck - show size difference between old and new versions' @echo ' check - run the test suite for all applets' @echo ' checkhelp - check for missing help-entries in Config.in' @echo ' randconfig - generate a random configuration' @echo ' release - create a distribution tarball' @echo ' sizes - show size of all enabled busybox symbols' @echo ' objsizes - show size of each .o object built' @echo ' bigdata - show data objects, biggest first' @echo ' stksizes - show stack users, biggest first' @echo busybox-1.22.1/loginutils/0000755000000000000000000000000012320365365014164 5ustar rootrootbusybox-1.22.1/loginutils/README0000644000000000000000000000570612263563520015053 0ustar rootroot Getty ??? Should getty open tty with or without O_NONBLOCK? For serial lines, it means "should getty wait for Carrier Detect pin?" I checked other getties: - agetty always uses O_NONBLOCK - mgetty uses O_NONBLOCK unless run with -b, or as "getty" ??? If we decided to use O_NONBLOCK (perhaps optionally with -b), when getty should send -I INITSTR data to tty? After open succeeds? What if we also want to initialize *modem* with some AT commands? ??? Should we check/create /var/lock/LCK..ttyPFX lockfiles? ??? mgetty opens tty but does NOT lock it, then waits for input via select/poll, and when input is available, it checks lock file. If it exists, mgetty exits (it assumes someone else uses the line). If no, it creates the file (lock the tty). Sounds like a good algorithm to use if we are called with -w... Getty should establish a new session and process group, and ensure that tty is a ctty. ??? Should getty ensure that other processes which might have opened fds to this tty be dusconnected? agetty has a -R option which makes agetty call vhangup() after tty is opened. (Then agetty opens it again, since it probably vhangup'ed its own fd too). Getty should leave the tty in approximately the same state as "stty sane" before it execs login program. Minor things we do conditionally are: c_iflag |= ICRNL; // if '\r' was used to end username ??? mgetty uses per-tty file to ignore connects, /etc/nologin.ttyxx - is it useful? It should be possible to run "getty 0 -" from a shell prompt. [This currently doesn't work from interactive shell since setsid() fails in process group leader. The workaround is to run it as a child of something. sh -c 'getty - 0; true' usually works. Should we fix this?] It should leave tty in a sane state when it exits (Ctrl-D, -t SEC timeout): echo should be on, speed, control chars properly set, etc. (However, it can't restore ctty. The symptom is that " * Copyright (C) 2007 by Tito Ragusa * * Licensed under GPLv2, see file LICENSE in this source tree. * */ //usage:#define deluser_trivial_usage //usage: "USER" //usage:#define deluser_full_usage "\n\n" //usage: "Delete USER from the system" //usage:#define delgroup_trivial_usage //usage: IF_FEATURE_DEL_USER_FROM_GROUP("[USER] ")"GROUP" //usage:#define delgroup_full_usage "\n\n" //usage: "Delete group GROUP from the system" //usage: IF_FEATURE_DEL_USER_FROM_GROUP(" or user USER from group GROUP") #include "libbb.h" int deluser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int deluser_main(int argc, char **argv) { /* User or group name */ char *name; /* Username (non-NULL only in "delgroup USER GROUP" case) */ char *member; /* Name of passwd or group file */ const char *pfile; /* Name of shadow or gshadow file */ const char *sfile; /* Are we deluser or delgroup? */ int do_deluser = (ENABLE_DELUSER && (!ENABLE_DELGROUP || applet_name[3] == 'u')); if (geteuid() != 0) bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); name = argv[1]; member = NULL; switch (argc) { case 3: if (!ENABLE_FEATURE_DEL_USER_FROM_GROUP || do_deluser) break; /* It's "delgroup USER GROUP" */ member = name; name = argv[2]; /* Fallthrough */ case 2: if (do_deluser) { /* "deluser USER" */ xgetpwnam(name); /* bail out if USER is wrong */ pfile = bb_path_passwd_file; if (ENABLE_FEATURE_SHADOWPASSWDS) sfile = bb_path_shadow_file; } else { struct group *gr; do_delgroup: /* "delgroup GROUP" or "delgroup USER GROUP" */ if (do_deluser < 0) { /* delgroup after deluser? */ gr = getgrnam(name); if (!gr) return EXIT_SUCCESS; } else { gr = xgetgrnam(name); /* bail out if GROUP is wrong */ } if (!member) { /* "delgroup GROUP" */ struct passwd *pw; struct passwd pwent; /* Check if the group is in use */ #define passwd_buf bb_common_bufsiz1 while (!getpwent_r(&pwent, passwd_buf, sizeof(passwd_buf), &pw)) { if (pwent.pw_gid == gr->gr_gid) bb_error_msg_and_die("'%s' still has '%s' as their primary group!", pwent.pw_name, name); } //endpwent(); } pfile = bb_path_group_file; if (ENABLE_FEATURE_SHADOWPASSWDS) sfile = bb_path_gshadow_file; } /* Modify pfile, then sfile */ do { if (update_passwd(pfile, name, NULL, member) == -1) return EXIT_FAILURE; if (ENABLE_FEATURE_SHADOWPASSWDS) { pfile = sfile; sfile = NULL; } } while (ENABLE_FEATURE_SHADOWPASSWDS && pfile); if (ENABLE_DELGROUP && do_deluser > 0) { /* "deluser USER" also should try to delete * same-named group. IOW: do "delgroup USER" */ // On debian deluser is a perl script that calls userdel. // From man userdel: // If USERGROUPS_ENAB is defined to yes in /etc/login.defs, userdel will // delete the group with the same name as the user. do_deluser = -1; goto do_delgroup; } return EXIT_SUCCESS; } /* Reached only if number of command line args is wrong */ bb_show_usage(); } busybox-1.22.1/loginutils/Config.src0000644000000000000000000002336412267106022016103 0ustar rootroot# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Login/Password Management Utilities" INSERT config FEATURE_SHADOWPASSWDS bool "Support for shadow passwords" default y help Build support for shadow password in /etc/shadow. This file is only readable by root and thus the encrypted passwords are no longer publicly readable. config USE_BB_PWD_GRP bool "Use internal password and group functions rather than system functions" default y help If you leave this disabled, busybox will use the system's password and group functions. And if you are using the GNU C library (glibc), you will then need to install the /etc/nsswitch.conf configuration file and the required /lib/libnss_* libraries in order for the password and group functions to work. This generally makes your embedded system quite a bit larger. Enabling this option will cause busybox to directly access the system's /etc/password, /etc/group files (and your system will be smaller, and I will get fewer emails asking about how glibc NSS works). When this option is enabled, you will not be able to use PAM to access remote LDAP password servers and whatnot. And if you want hostname resolution to work with glibc, you still need the /lib/libnss_* libraries. If you need to use glibc's nsswitch.conf mechanism (e.g. if user/group database is NOT stored in /etc/passwd etc), you must NOT use this option. If you enable this option, it will add about 1.5k. config USE_BB_SHADOW bool "Use internal shadow password functions" default y depends on USE_BB_PWD_GRP && FEATURE_SHADOWPASSWDS help If you leave this disabled, busybox will use the system's shadow password handling functions. And if you are using the GNU C library (glibc), you will then need to install the /etc/nsswitch.conf configuration file and the required /lib/libnss_* libraries in order for the shadow password functions to work. This generally makes your embedded system quite a bit larger. Enabling this option will cause busybox to directly access the system's /etc/shadow file when handling shadow passwords. This makes your system smaller (and I will get fewer emails asking about how glibc NSS works). When this option is enabled, you will not be able to use PAM to access shadow passwords from remote LDAP password servers and whatnot. config USE_BB_CRYPT bool "Use internal crypt functions" default y help Busybox has internal DES and MD5 crypt functions. They produce results which are identical to corresponding standard C library functions. If you leave this disabled, busybox will use the system's crypt functions. Most C libraries use large (~70k) static buffers there, and also combine them with more general DES encryption/decryption. For busybox, having large static buffers is undesirable, especially on NOMMU machines. Busybox also doesn't need DES encryption/decryption and can do with smaller code. If you enable this option, it will add about 4.8k of code if you are building dynamically linked executable. In static build, it makes code _smaller_ by about 1.2k, and likely many kilobytes less of bss. config USE_BB_CRYPT_SHA bool "Enable SHA256/512 crypt functions" default y depends on USE_BB_CRYPT help Enable this if you have passwords starting with "$5$" or "$6$" in your /etc/passwd or /etc/shadow files. These passwords are hashed using SHA256 and SHA512 algorithms. Support for them was added to glibc in 2008. With this option off, login will fail password check for any user which has password encrypted with these algorithms. config ADDUSER bool "adduser" default y help Utility for creating a new user account. config FEATURE_ADDUSER_LONG_OPTIONS bool "Enable long options" default y depends on ADDUSER && LONG_OPTS help Support long options for the adduser applet. config FEATURE_CHECK_NAMES bool "Enable sanity check on user/group names in adduser and addgroup" default n depends on ADDUSER || ADDGROUP help Enable sanity check on user and group names in adduser and addgroup. To avoid problems, the user or group name should consist only of letters, digits, underscores, periods, at signs and dashes, and not start with a dash (as defined by IEEE Std 1003.1-2001). For compatibility with Samba machine accounts "$" is also supported at the end of the user or group name. config FIRST_SYSTEM_ID int "First valid system uid or gid for adduser and addgroup" depends on ADDUSER || ADDGROUP range 0 64900 default 100 help First valid system uid or gid for adduser and addgroup config LAST_SYSTEM_ID int "Last valid system uid or gid for adduser and addgroup" depends on ADDUSER || ADDGROUP range 0 64900 default 999 help Last valid system uid or gid for adduser and addgroup config ADDGROUP bool "addgroup" default y help Utility for creating a new group account. config FEATURE_ADDGROUP_LONG_OPTIONS bool "Enable long options" default y depends on ADDGROUP && LONG_OPTS help Support long options for the addgroup applet. config FEATURE_ADDUSER_TO_GROUP bool "Support for adding users to groups" default y depends on ADDGROUP help If called with two non-option arguments, addgroup will add an existing user to an existing group. config DELUSER bool "deluser" default y help Utility for deleting a user account. config DELGROUP bool "delgroup" default y help Utility for deleting a group account. config FEATURE_DEL_USER_FROM_GROUP bool "Support for removing users from groups" default y depends on DELGROUP help If called with two non-option arguments, deluser or delgroup will remove an user from a specified group. config GETTY bool "getty" default y select FEATURE_SYSLOG help getty lets you log in on a tty. It is normally invoked by init. Note that you can save a few bytes by disabling it and using login applet directly. If you need to reset tty attributes before calling login, this script approximates getty: exec /dev/$1 2>&1 || exit 1 reset stty sane; stty ispeed 38400; stty ospeed 38400 printf "%s login: " "`hostname`" read -r login exec /bin/login "$login" config LOGIN bool "login" default y select FEATURE_SYSLOG help login is used when signing onto a system. Note that Busybox binary must be setuid root for this applet to work properly. config LOGIN_SESSION_AS_CHILD bool "Run logged in session in a child process" default y if PAM depends on LOGIN help Run the logged in session in a child process. This allows login to clean up things such as utmp entries or PAM sessions when the login session is complete. If you use PAM, you almost always would want this to be set to Y, else PAM session will not be cleaned up. config PAM bool "Support for PAM (Pluggable Authentication Modules)" default n depends on LOGIN help Use PAM in login(1) instead of direct access to password database. config LOGIN_SCRIPTS bool "Support for login scripts" depends on LOGIN default y help Enable this if you want login to execute $LOGIN_PRE_SUID_SCRIPT just prior to switching from root to logged-in user. config FEATURE_NOLOGIN bool "Support for /etc/nologin" default y depends on LOGIN help The file /etc/nologin is used by (some versions of) login(1). If it exists, non-root logins are prohibited. config FEATURE_SECURETTY bool "Support for /etc/securetty" default y depends on LOGIN help The file /etc/securetty is used by (some versions of) login(1). The file contains the device names of tty lines (one per line, without leading /dev/) on which root is allowed to login. config PASSWD bool "passwd" default y select FEATURE_SYSLOG help passwd changes passwords for user and group accounts. A normal user may only change the password for his/her own account, the super user may change the password for any account. The administrator of a group may change the password for the group. Note that Busybox binary must be setuid root for this applet to work properly. config FEATURE_PASSWD_WEAK_CHECK bool "Check new passwords for weakness" default y depends on PASSWD help With this option passwd will refuse new passwords which are "weak". config CRYPTPW bool "cryptpw" default y help Encrypts the given password with the crypt(3) libc function using the given salt. Debian has this utility under mkpasswd name. Busybox provides mkpasswd as an alias for cryptpw. config CHPASSWD bool "chpasswd" default y help Reads a file of user name and password pairs from standard input and uses this information to update a group of existing users. config FEATURE_DEFAULT_PASSWD_ALGO string "Default password encryption method (passwd -a, cryptpw -m parameter)" default "des" depends on PASSWD || CRYPTPW help Possible choices are "d[es]", "m[d5]", "s[ha256]" or "sha512". config SU bool "su" default y select FEATURE_SYSLOG help su is used to become another user during a login session. Invoked without a username, su defaults to becoming the super user. Note that Busybox binary must be setuid root for this applet to work properly. config FEATURE_SU_SYSLOG bool "Enable su to write to syslog" default y depends on SU config FEATURE_SU_CHECKS_SHELLS bool "Enable su to check user's shell to be listed in /etc/shells" depends on SU default y config SULOGIN bool "sulogin" default y select FEATURE_SYSLOG help sulogin is invoked when the system goes into single user mode (this is done through an entry in inittab). config VLOCK bool "vlock" default y help Build the "vlock" applet which allows you to lock (virtual) terminals. Note that Busybox binary must be setuid root for this applet to work properly. endmenu busybox-1.22.1/loginutils/Kbuild.src0000644000000000000000000000107612263563520016112 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT lib-$(CONFIG_ADDGROUP) += addgroup.o lib-$(CONFIG_ADDUSER) += adduser.o lib-$(CONFIG_CRYPTPW) += cryptpw.o lib-$(CONFIG_CHPASSWD) += chpasswd.o lib-$(CONFIG_GETTY) += getty.o lib-$(CONFIG_LOGIN) += login.o lib-$(CONFIG_PASSWD) += passwd.o lib-$(CONFIG_SU) += su.o lib-$(CONFIG_SULOGIN) += sulogin.o lib-$(CONFIG_VLOCK) += vlock.o lib-$(CONFIG_DELUSER) += deluser.o lib-$(CONFIG_DELGROUP) += deluser.o busybox-1.22.1/loginutils/adduser.c0000644000000000000000000002005412267106022015751 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * adduser - add users to /etc/passwd and /etc/shadow * * Copyright (C) 1999 by Lineo, inc. and John Beppu * Copyright (C) 1999,2000,2001 by John Beppu * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define adduser_trivial_usage //usage: "[OPTIONS] USER [GROUP]" //usage:#define adduser_full_usage "\n\n" //usage: "Create new user, or add USER to GROUP\n" //usage: "\n -h DIR Home directory" //usage: "\n -g GECOS GECOS field" //usage: "\n -s SHELL Login shell" //usage: "\n -G GRP Add user to existing group" //usage: "\n -S Create a system user" //usage: "\n -D Don't assign a password" //usage: "\n -H Don't create home directory" //usage: "\n -u UID User id" #include "libbb.h" #if CONFIG_LAST_SYSTEM_ID < CONFIG_FIRST_SYSTEM_ID #error Bad LAST_SYSTEM_ID or FIRST_SYSTEM_ID in .config #endif /* #define OPT_HOME (1 << 0) */ /* unused */ /* #define OPT_GECOS (1 << 1) */ /* unused */ #define OPT_SHELL (1 << 2) #define OPT_GID (1 << 3) #define OPT_DONT_SET_PASS (1 << 4) #define OPT_SYSTEM_ACCOUNT (1 << 5) #define OPT_DONT_MAKE_HOME (1 << 6) #define OPT_UID (1 << 7) /* We assume UID_T_MAX == INT_MAX */ /* remix */ /* recoded such that the uid may be passed in *p */ static void passwd_study(struct passwd *p) { int max = UINT_MAX; if (getpwnam(p->pw_name)) { bb_error_msg_and_die("%s '%s' in use", "user", p->pw_name); /* this format string is reused in adduser and addgroup */ } if (!(option_mask32 & OPT_UID)) { if (option_mask32 & OPT_SYSTEM_ACCOUNT) { p->pw_uid = CONFIG_FIRST_SYSTEM_ID; max = CONFIG_LAST_SYSTEM_ID; } else { p->pw_uid = CONFIG_LAST_SYSTEM_ID + 1; max = 64999; } } /* check for a free uid (and maybe gid) */ while (getpwuid(p->pw_uid) || (p->pw_gid == (gid_t)-1 && getgrgid(p->pw_uid))) { if (option_mask32 & OPT_UID) { /* -u N, cannot pick uid other than N: error */ bb_error_msg_and_die("%s '%s' in use", "uid", itoa(p->pw_uid)); /* this format string is reused in adduser and addgroup */ } if (p->pw_uid == max) { bb_error_msg_and_die("no %cids left", 'u'); /* this format string is reused in adduser and addgroup */ } p->pw_uid++; } if (p->pw_gid == (gid_t)-1) { p->pw_gid = p->pw_uid; /* new gid = uid */ if (getgrnam(p->pw_name)) { bb_error_msg_and_die("%s '%s' in use", "group", p->pw_name); /* this format string is reused in adduser and addgroup */ } } } static int addgroup_wrapper(struct passwd *p, const char *group_name) { char *argv[6]; argv[0] = (char*)"addgroup"; if (group_name) { /* Add user to existing group */ argv[1] = (char*)"--"; argv[2] = p->pw_name; argv[3] = (char*)group_name; argv[4] = NULL; } else { /* Add user to his own group with the first free gid * found in passwd_study. */ #if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS || !ENABLE_ADDGROUP /* We try to use --gid, not -g, because "standard" addgroup * has no short option -g, it has only long --gid. */ argv[1] = (char*)"--gid"; #else /* Breaks if system in fact does NOT use busybox addgroup */ argv[1] = (char*)"-g"; #endif argv[2] = utoa(p->pw_gid); argv[3] = (char*)"--"; argv[4] = p->pw_name; argv[5] = NULL; } return spawn_and_wait(argv); } static void passwd_wrapper(const char *login_name) NORETURN; static void passwd_wrapper(const char *login_name) { BB_EXECLP("passwd", "passwd", "--", login_name, NULL); bb_error_msg_and_die("can't execute passwd, you must set password manually"); } #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS static const char adduser_longopts[] ALIGN1 = "home\0" Required_argument "h" "gecos\0" Required_argument "g" "shell\0" Required_argument "s" "ingroup\0" Required_argument "G" "disabled-password\0" No_argument "D" "empty-password\0" No_argument "D" "system\0" No_argument "S" "no-create-home\0" No_argument "H" "uid\0" Required_argument "u" ; #endif /* * adduser will take a login_name as its first parameter. * home, shell, gecos: * can be customized via command-line parameters. */ int adduser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int adduser_main(int argc UNUSED_PARAM, char **argv) { struct passwd pw; const char *usegroup = NULL; char *p; unsigned opts; #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS applet_long_options = adduser_longopts; #endif /* got root? */ if (geteuid()) { bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); } pw.pw_gecos = (char *)"Linux User,,,"; /* We assume that newly created users "inherit" root's shell setting */ pw.pw_shell = (char *)get_shell_name(); pw.pw_dir = NULL; /* at least one and at most two non-option args */ /* disable interactive passwd for system accounts */ opt_complementary = "-1:?2:SD:u+"; if (sizeof(pw.pw_uid) == sizeof(int)) { opts = getopt32(argv, "h:g:s:G:DSHu:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &pw.pw_uid); } else { unsigned uid; opts = getopt32(argv, "h:g:s:G:DSHu:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &uid); if (opts & OPT_UID) { pw.pw_uid = uid; } } argv += optind; pw.pw_name = argv[0]; if (!opts && argv[1]) { /* if called with two non-option arguments, adduser * will add an existing user to an existing group. */ return addgroup_wrapper(&pw, argv[1]); } /* fill in the passwd struct */ die_if_bad_username(pw.pw_name); if (!pw.pw_dir) { /* create string for $HOME if not specified already */ pw.pw_dir = xasprintf("/home/%s", argv[0]); } pw.pw_passwd = (char *)"x"; if (opts & OPT_SYSTEM_ACCOUNT) { if (!usegroup) { usegroup = "nogroup"; } if (!(opts & OPT_SHELL)) { pw.pw_shell = (char *) "/bin/false"; } } pw.pw_gid = usegroup ? xgroup2gid(usegroup) : -1; /* exits on failure */ /* make sure everything is kosher and setup uid && maybe gid */ passwd_study(&pw); p = xasprintf("x:%u:%u:%s:%s:%s", (unsigned) pw.pw_uid, (unsigned) pw.pw_gid, pw.pw_gecos, pw.pw_dir, pw.pw_shell); if (update_passwd(bb_path_passwd_file, pw.pw_name, p, NULL) < 0) { return EXIT_FAILURE; } if (ENABLE_FEATURE_CLEAN_UP) free(p); #if ENABLE_FEATURE_SHADOWPASSWDS /* /etc/shadow fields: * 1. username * 2. encrypted password * 3. last password change (unix date (unix time/24*60*60)) * 4. minimum days required between password changes * 5. maximum days password is valid * 6. days before password is to expire that user is warned * 7. days after password expires that account is disabled * 8. unix date when login expires (i.e. when it may no longer be used) */ /* fields: 2 3 4 5 6 78 */ p = xasprintf("!:%u:0:99999:7:::", (unsigned)(time(NULL)) / (24*60*60)); /* ignore errors: if file is missing we suppose admin doesn't want it */ update_passwd(bb_path_shadow_file, pw.pw_name, p, NULL); if (ENABLE_FEATURE_CLEAN_UP) free(p); #endif /* add to group */ addgroup_wrapper(&pw, usegroup); /* clear the umask for this process so it doesn't * screw up the permissions on the mkdir and chown. */ umask(0); if (!(opts & OPT_DONT_MAKE_HOME)) { /* set the owner and group so it is owned by the new user, * then fix up the permissions to 2755. Can't do it before * since chown will clear the setgid bit */ int mkdir_err = mkdir(pw.pw_dir, 0755); if (mkdir_err == 0) { /* New home. Copy /etc/skel to it */ const char *args[] = { "chown", "-R", xasprintf("%u:%u", (int)pw.pw_uid, (int)pw.pw_gid), pw.pw_dir, NULL }; /* Be silent on any errors (like: no /etc/skel) */ logmode = LOGMODE_NONE; copy_file("/etc/skel", pw.pw_dir, FILEUTILS_RECUR); logmode = LOGMODE_STDIO; chown_main(4, (char**)args); } if ((mkdir_err != 0 && errno != EEXIST) || chown(pw.pw_dir, pw.pw_uid, pw.pw_gid) != 0 || chmod(pw.pw_dir, 02755) != 0 /* set setgid bit on homedir */ ) { bb_simple_perror_msg(pw.pw_dir); } } if (!(opts & OPT_DONT_SET_PASS)) { /* interactively set passwd */ passwd_wrapper(pw.pw_name); } return EXIT_SUCCESS; } busybox-1.22.1/loginutils/chpasswd.c0000644000000000000000000000511312263563520016143 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * chpasswd.c * * Written for SLIND (from passwd.c) by Alexander Shishkin * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" //usage:#define chpasswd_trivial_usage //usage: IF_LONG_OPTS("[--md5|--encrypted]") IF_NOT_LONG_OPTS("[-m|-e]") //usage:#define chpasswd_full_usage "\n\n" //usage: "Read user:password from stdin and update /etc/passwd\n" //usage: IF_LONG_OPTS( //usage: "\n -e,--encrypted Supplied passwords are in encrypted form" //usage: "\n -m,--md5 Use MD5 encryption instead of DES" //usage: ) //usage: IF_NOT_LONG_OPTS( //usage: "\n -e Supplied passwords are in encrypted form" //usage: "\n -m Use MD5 encryption instead of DES" //usage: ) //TODO: implement -c ALGO #if ENABLE_LONG_OPTS static const char chpasswd_longopts[] ALIGN1 = "encrypted\0" No_argument "e" "md5\0" No_argument "m" ; #endif #define OPT_ENC 1 #define OPT_MD5 2 int chpasswd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int chpasswd_main(int argc UNUSED_PARAM, char **argv) { char *name; int opt; if (getuid() != 0) bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); opt_complementary = "m--e:e--m"; IF_LONG_OPTS(applet_long_options = chpasswd_longopts;) opt = getopt32(argv, "em"); while ((name = xmalloc_fgetline(stdin)) != NULL) { char *free_me; char *pass; int rc; pass = strchr(name, ':'); if (!pass) bb_error_msg_and_die("missing new password"); *pass++ = '\0'; xuname2uid(name); /* dies if there is no such user */ free_me = NULL; if (!(opt & OPT_ENC)) { char salt[sizeof("$N$XXXXXXXX")]; crypt_make_salt(salt, 1); if (opt & OPT_MD5) { salt[0] = '$'; salt[1] = '1'; salt[2] = '$'; crypt_make_salt(salt + 3, 4); } free_me = pass = pw_encrypt(pass, salt, 0); } /* This is rather complex: if user is not found in /etc/shadow, * we try to find & change his passwd in /etc/passwd */ #if ENABLE_FEATURE_SHADOWPASSWDS rc = update_passwd(bb_path_shadow_file, name, pass, NULL); if (rc > 0) /* password in /etc/shadow was updated */ pass = (char*)"x"; if (rc >= 0) /* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */ #endif rc = update_passwd(bb_path_passwd_file, name, pass, NULL); /* LOGMODE_BOTH logs to syslog also */ logmode = LOGMODE_BOTH; if (rc < 0) bb_error_msg_and_die("an error occurred updating password for %s", name); if (rc) bb_info_msg("Password for '%s' changed", name); logmode = LOGMODE_STDIO; free(name); free(free_me); } return EXIT_SUCCESS; } busybox-1.22.1/loginutils/getty.c0000644000000000000000000005077712263563520015503 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Based on agetty - another getty program for Linux. By W. Z. Venema 1989 * Ported to Linux by Peter Orbaek * This program is freely distributable. * * option added by Eric Rasmussen - 12/28/95 * * 1999-02-22 Arkadiusz Mickiewicz * - Added Native Language Support * * 1999-05-05 Thorsten Kranzkowski * - Enabled hardware flow control before displaying /etc/issue * * 2011-01 Venys Vlasenko * - Removed parity detection code. It can't work reliably: * if all chars received have bit 7 cleared and odd (or even) parity, * it is impossible to determine whether other side is 8-bit,no-parity * or 7-bit,odd(even)-parity. It also interferes with non-ASCII usernames. * - From now on, we assume that parity is correctly set. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" #include #ifndef IUCLC # define IUCLC 0 #endif #ifndef LOGIN_PROCESS # undef ENABLE_FEATURE_UTMP # undef ENABLE_FEATURE_WTMP # define ENABLE_FEATURE_UTMP 0 # define ENABLE_FEATURE_WTMP 0 #endif /* The following is used for understandable diagnostics */ #ifdef DEBUGGING static FILE *dbf; # define DEBUGTERM "/dev/ttyp0" # define debug(...) do { fprintf(dbf, __VA_ARGS__); fflush(dbf); } while (0) #else # define debug(...) ((void)0) #endif /* * Things you may want to modify. * * You may disagree with the default line-editing etc. characters defined * below. Note, however, that DEL cannot be used for interrupt generation * and for line editing at the same time. */ #undef _PATH_LOGIN #define _PATH_LOGIN "/bin/login" /* Displayed before the login prompt. * If ISSUE is not defined, getty will never display the contents of the * /etc/issue file. You will not want to spit out large "issue" files at the * wrong baud rate. */ #define ISSUE "/etc/issue" /* Macro to build Ctrl-LETTER. Assumes ASCII dialect */ #define CTL(x) ((x) ^ 0100) /* * When multiple baud rates are specified on the command line, * the first one we will try is the first one specified. */ #define MAX_SPEED 10 /* max. nr. of baud rates */ struct globals { unsigned timeout; const char *login; /* login program */ const char *fakehost; const char *tty_name; char *initstring; /* modem init string */ const char *issue; /* alternative issue file */ int numspeed; /* number of baud rates to try */ int speeds[MAX_SPEED]; /* baud rates to be tried */ unsigned char eol; /* end-of-line char seen (CR or NL) */ struct termios tty_attrs; char line_buf[128]; }; #define G (*ptr_to_globals) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ } while (0) //usage:#define getty_trivial_usage //usage: "[OPTIONS] BAUD_RATE[,BAUD_RATE]... TTY [TERMTYPE]" //usage:#define getty_full_usage "\n\n" //usage: "Open TTY, prompt for login name, then invoke /bin/login\n" //usage: "\n -h Enable hardware RTS/CTS flow control" //usage: "\n -L Set CLOCAL (ignore Carrier Detect state)" //usage: "\n -m Get baud rate from modem's CONNECT status message" //usage: "\n -n Don't prompt for login name" //usage: "\n -w Wait for CR or LF before sending /etc/issue" //usage: "\n -i Don't display /etc/issue" //usage: "\n -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue" //usage: "\n -l LOGIN Invoke LOGIN instead of /bin/login" //usage: "\n -t SEC Terminate after SEC if no login name is read" //usage: "\n -I INITSTR Send INITSTR before anything else" //usage: "\n -H HOST Log HOST into the utmp file as the hostname" //usage: "\n" //usage: "\nBAUD_RATE of 0 leaves it unchanged" static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn"; #define F_INITSTRING (1 << 0) /* -I */ #define F_LOCAL (1 << 1) /* -L */ #define F_FAKEHOST (1 << 2) /* -H */ #define F_CUSTISSUE (1 << 3) /* -f */ #define F_RTSCTS (1 << 4) /* -h */ #define F_NOISSUE (1 << 5) /* -i */ #define F_LOGIN (1 << 6) /* -l */ #define F_PARSE (1 << 7) /* -m */ #define F_TIMEOUT (1 << 8) /* -t */ #define F_WAITCRLF (1 << 9) /* -w */ #define F_NOPROMPT (1 << 10) /* -n */ /* convert speed string to speed code; return <= 0 on failure */ static int bcode(const char *s) { int value = bb_strtou(s, NULL, 10); /* yes, int is intended! */ if (value < 0) /* bad terminating char, overflow, etc */ return value; return tty_value_to_baud(value); } /* parse alternate baud rates */ static void parse_speeds(char *arg) { char *cp; /* NB: at least one iteration is always done */ debug("entered parse_speeds\n"); while ((cp = strsep(&arg, ",")) != NULL) { G.speeds[G.numspeed] = bcode(cp); if (G.speeds[G.numspeed] < 0) bb_error_msg_and_die("bad speed: %s", cp); /* note: arg "0" turns into speed B0 */ G.numspeed++; if (G.numspeed > MAX_SPEED) bb_error_msg_and_die("too many alternate speeds"); } debug("exiting parse_speeds\n"); } /* parse command-line arguments */ static void parse_args(char **argv) { char *ts; int flags; opt_complementary = "-2:t+"; /* at least 2 args; -t N */ flags = getopt32(argv, opt_string, &G.initstring, &G.fakehost, &G.issue, &G.login, &G.timeout ); if (flags & F_INITSTRING) { G.initstring = xstrdup(G.initstring); /* decode \ddd octal codes into chars */ strcpy_and_process_escape_sequences(G.initstring, G.initstring); } argv += optind; debug("after getopt\n"); /* We loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ G.tty_name = argv[0]; ts = argv[1]; /* baud rate(s) */ if (isdigit(argv[0][0])) { /* A number first, assume it's a speed (BSD style) */ G.tty_name = ts; /* tty name is in argv[1] */ ts = argv[0]; /* baud rate(s) */ } parse_speeds(ts); if (argv[2]) xsetenv("TERM", argv[2]); debug("exiting parse_args\n"); } /* set up tty as standard input, output, error */ static void open_tty(void) { /* Set up new standard input, unless we are given an already opened port */ if (NOT_LONE_DASH(G.tty_name)) { if (G.tty_name[0] != '/') G.tty_name = xasprintf("/dev/%s", G.tty_name); /* will leak it */ /* Open the tty as standard input */ debug("open(2)\n"); close(0); xopen(G.tty_name, O_RDWR | O_NONBLOCK); /* uses fd 0 */ /* Set proper protections and ownership */ fchown(0, 0, 0); /* 0:0 */ fchmod(0, 0620); /* crw--w---- */ } else { char *n; /* * Standard input should already be connected to an open port. * Make sure it is open for read/write. */ if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR) bb_error_msg_and_die("stdin is not open for read/write"); /* Try to get real tty name instead of "-" */ n = xmalloc_ttyname(0); if (n) G.tty_name = n; } applet_name = xasprintf("getty: %s", skip_dev_pfx(G.tty_name)); } static void set_tty_attrs(void) { if (tcsetattr_stdin_TCSANOW(&G.tty_attrs) < 0) bb_perror_msg_and_die("tcsetattr"); } /* We manipulate tty_attrs this way: * - first, we read existing tty_attrs * - init_tty_attrs modifies some parts and sets it * - auto_baud and/or BREAK processing can set different speed and set tty attrs * - finalize_tty_attrs again modifies some parts and sets tty attrs before * execing login */ static void init_tty_attrs(int speed) { /* Try to drain output buffer, with 5 sec timeout. * Added on request from users of ~600 baud serial interface * with biggish buffer on a 90MHz CPU. * They were losing hundreds of bytes of buffered output * on tcflush. */ signal_no_SA_RESTART_empty_mask(SIGALRM, record_signo); alarm(5); tcdrain(STDIN_FILENO); alarm(0); /* Flush input and output queues, important for modems! */ tcflush(STDIN_FILENO, TCIOFLUSH); /* Set speed if it wasn't specified as "0" on command line */ if (speed != B0) cfsetspeed(&G.tty_attrs, speed); /* Initial settings: 8-bit characters, raw mode, blocking i/o. * Special characters are set after we have read the login name; all * reads will be done in raw mode anyway. */ /* Clear all bits except: */ G.tty_attrs.c_cflag &= (0 /* 2 stop bits (1 otherwise) * Enable parity bit (both on input and output) * Odd parity (else even) */ | CSTOPB | PARENB | PARODD #ifdef CMSPAR | CMSPAR /* mark or space parity */ #endif #ifdef CBAUD | CBAUD /* (output) baud rate */ #endif #ifdef CBAUDEX | CBAUDEX /* (output) baud rate */ #endif #ifdef CIBAUD | CIBAUD /* input baud rate */ #endif ); /* Set: 8 bits; hang up (drop DTR) on last close; enable receive */ G.tty_attrs.c_cflag |= CS8 | HUPCL | CREAD; if (option_mask32 & F_LOCAL) { /* ignore Carrier Detect pin: * opens don't block when CD is low, * losing CD doesn't hang up processes whose ctty is this tty */ G.tty_attrs.c_cflag |= CLOCAL; } #ifdef CRTSCTS if (option_mask32 & F_RTSCTS) G.tty_attrs.c_cflag |= CRTSCTS; /* flow control using RTS/CTS pins */ #endif G.tty_attrs.c_iflag = 0; G.tty_attrs.c_lflag = 0; /* non-raw output; add CR to each NL */ G.tty_attrs.c_oflag = OPOST | ONLCR; /* reads would block only if < 1 char is available */ G.tty_attrs.c_cc[VMIN] = 1; /* no timeout (reads block forever) */ G.tty_attrs.c_cc[VTIME] = 0; #ifdef __linux__ G.tty_attrs.c_line = 0; #endif set_tty_attrs(); debug("term_io 2\n"); } static void finalize_tty_attrs(void) { /* software flow control on output (stop sending if XOFF is recvd); * and on input (send XOFF when buffer is full) */ G.tty_attrs.c_iflag |= IXON | IXOFF; if (G.eol == '\r') { G.tty_attrs.c_iflag |= ICRNL; /* map CR on input to NL */ } /* Other bits in c_iflag: * IXANY Any recvd char enables output (any char is also a XON) * INPCK Enable parity check * IGNPAR Ignore parity errors (drop bad bytes) * PARMRK Mark parity errors with 0xff, 0x00 prefix * (else bad byte is received as 0x00) * ISTRIP Strip parity bit * IGNBRK Ignore break condition * BRKINT Send SIGINT on break - maybe set this? * INLCR Map NL to CR * IGNCR Ignore CR * ICRNL Map CR to NL * IUCLC Map uppercase to lowercase * IMAXBEL Echo BEL on input line too long * IUTF8 Appears to affect tty's idea of char widths, * observed to improve backspacing through Unicode chars */ /* line buffered input (NL or EOL or EOF chars end a line); * recognize INT/QUIT/SUSP chars; * echo input chars; * echo BS-SP-BS on erase character; * echo kill char specially, not as ^c (ECHOKE controls how exactly); * erase all input via BS-SP-BS on kill char (else go to next line) */ G.tty_attrs.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; /* Other bits in c_lflag: * XCASE Map uppercase to \lowercase [tried, doesn't work] * ECHONL Echo NL even if ECHO is not set * ECHOCTL Echo ctrl chars as ^c (else don't echo) - maybe set this? * ECHOPRT On erase, echo erased chars * [qwe input looks like "qwe\ewq/" on screen] * NOFLSH Don't flush input buffer after interrupt or quit chars * IEXTEN Enable extended functions (??) * [glibc says it enables c_cc[LNEXT] "enter literal char" * and c_cc[VDISCARD] "toggle discard buffered output" chars] * FLUSHO Output being flushed (c_cc[VDISCARD] is in effect) * PENDIN Retype pending input at next read or input char * (c_cc[VREPRINT] is being processed) * TOSTOP Send SIGTTOU for background output * (why "stty sane" unsets this bit?) */ G.tty_attrs.c_cc[VINTR] = CTL('C'); G.tty_attrs.c_cc[VQUIT] = CTL('\\'); G.tty_attrs.c_cc[VEOF] = CTL('D'); G.tty_attrs.c_cc[VEOL] = '\n'; #ifdef VSWTC G.tty_attrs.c_cc[VSWTC] = 0; #endif #ifdef VSWTCH G.tty_attrs.c_cc[VSWTCH] = 0; #endif G.tty_attrs.c_cc[VKILL] = CTL('U'); /* Other control chars: * VEOL2 * VERASE, VWERASE - (word) erase. we may set VERASE in get_logname * VREPRINT - reprint current input buffer * VLNEXT, VDISCARD, VSTATUS * VSUSP, VDSUSP - send (delayed) SIGTSTP * VSTART, VSTOP - chars used for IXON/IXOFF */ set_tty_attrs(); /* Now the newline character should be properly written */ full_write(STDOUT_FILENO, "\n", 1); } /* extract baud rate from modem status message */ static void auto_baud(void) { int nread; /* * This works only if the modem produces its status code AFTER raising * the DCD line, and if the computer is fast enough to set the proper * baud rate before the message has gone by. We expect a message of the * following format: * * * * The number is interpreted as the baud rate of the incoming call. If the * modem does not tell us the baud rate within one second, we will keep * using the current baud rate. It is advisable to enable BREAK * processing (comma-separated list of baud rates) if the processing of * modem status messages is enabled. */ G.tty_attrs.c_cc[VMIN] = 0; /* don't block reads (min read is 0 chars) */ set_tty_attrs(); /* * Wait for a while, then read everything the modem has said so far and * try to extract the speed of the dial-in call. */ sleep(1); nread = safe_read(STDIN_FILENO, G.line_buf, sizeof(G.line_buf) - 1); if (nread > 0) { int speed; char *bp; G.line_buf[nread] = '\0'; for (bp = G.line_buf; bp < G.line_buf + nread; bp++) { if (isdigit(*bp)) { speed = bcode(bp); if (speed > 0) cfsetspeed(&G.tty_attrs, speed); break; } } } /* Restore terminal settings */ G.tty_attrs.c_cc[VMIN] = 1; /* restore to value set by init_tty_attrs */ set_tty_attrs(); } /* get user name, establish parity, speed, erase, kill, eol; * return NULL on BREAK, logname on success */ static char *get_logname(void) { char *bp; char c; /* Flush pending input (esp. after parsing or switching the baud rate) */ usleep(100*1000); /* 0.1 sec */ tcflush(STDIN_FILENO, TCIFLUSH); /* Prompt for and read a login name */ do { /* Write issue file and prompt */ #ifdef ISSUE if (!(option_mask32 & F_NOISSUE)) print_login_issue(G.issue, G.tty_name); #endif print_login_prompt(); /* Read name, watch for break, erase, kill, end-of-line */ bp = G.line_buf; while (1) { /* Do not report trivial EINTR/EIO errors */ errno = EINTR; /* make read of 0 bytes be silent too */ if (read(STDIN_FILENO, &c, 1) < 1) { finalize_tty_attrs(); if (errno == EINTR || errno == EIO) exit(EXIT_SUCCESS); bb_perror_msg_and_die(bb_msg_read_error); } switch (c) { case '\r': case '\n': *bp = '\0'; G.eol = c; goto got_logname; case CTL('H'): case 0x7f: G.tty_attrs.c_cc[VERASE] = c; if (bp > G.line_buf) { full_write(STDOUT_FILENO, "\010 \010", 3); bp--; } break; case CTL('U'): while (bp > G.line_buf) { full_write(STDOUT_FILENO, "\010 \010", 3); bp--; } break; case CTL('C'): case CTL('D'): finalize_tty_attrs(); exit(EXIT_SUCCESS); case '\0': /* BREAK. If we have speeds to try, * return NULL (will switch speeds and return here) */ if (G.numspeed > 1) return NULL; /* fall through and ignore it */ default: if ((unsigned char)c < ' ') { /* ignore garbage characters */ } else if ((int)(bp - G.line_buf) < sizeof(G.line_buf) - 1) { /* echo and store the character */ full_write(STDOUT_FILENO, &c, 1); *bp++ = c; } break; } } /* end of get char loop */ got_logname: ; } while (G.line_buf[0] == '\0'); /* while logname is empty */ return G.line_buf; } static void alarm_handler(int sig UNUSED_PARAM) { finalize_tty_attrs(); _exit(EXIT_SUCCESS); } int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int getty_main(int argc UNUSED_PARAM, char **argv) { int n; pid_t pid, tsid; char *logname; INIT_G(); G.login = _PATH_LOGIN; /* default login program */ #ifdef ISSUE G.issue = ISSUE; /* default issue file */ #endif G.eol = '\r'; /* Parse command-line arguments */ parse_args(argv); /* Create new session and pgrp, lose controlling tty */ pid = setsid(); /* this also gives us our pid :) */ if (pid < 0) { int fd; /* :( * docs/ctty.htm says: * "This is allowed only when the current process * is not a process group leader". * Thus, setsid() will fail if we _already_ are * a session leader - which is quite possible for getty! */ pid = getpid(); if (getsid(0) != pid) { //for debugging: //bb_perror_msg_and_die("setsid failed:" // " pid %d ppid %d" // " sid %d pgid %d", // pid, getppid(), // getsid(0), getpgid(0)); bb_perror_msg_and_die("setsid"); } /* Looks like we are already a session leader. * In this case (setsid failed) we may still have ctty, * and it may be different from tty we need to control! * If we still have ctty, on Linux ioctl(TIOCSCTTY) * (which we are going to use a bit later) always fails - * even if we try to take ctty which is already ours! * Try to drop old ctty now to prevent that. * Use O_NONBLOCK: old ctty may be a serial line. */ fd = open("/dev/tty", O_RDWR | O_NONBLOCK); if (fd >= 0) { /* TIOCNOTTY sends SIGHUP to the foreground * process group - which may include us! * Make sure to not die on it: */ sighandler_t old = signal(SIGHUP, SIG_IGN); ioctl(fd, TIOCNOTTY); close(fd); signal(SIGHUP, old); } } /* Close stdio, and stray descriptors, just in case */ n = xopen(bb_dev_null, O_RDWR); /* dup2(n, 0); - no, we need to handle "getty - 9600" too */ xdup2(n, 1); xdup2(n, 2); while (n > 2) close(n--); /* Logging. We want special flavor of error_msg_and_die */ die_sleep = 10; msg_eol = "\r\n"; /* most likely will internally use fd #3 in CLOEXEC mode: */ openlog(applet_name, LOG_PID, LOG_AUTH); logmode = LOGMODE_BOTH; #ifdef DEBUGGING dbf = xfopen_for_write(DEBUGTERM); for (n = 1; argv[n]; n++) { debug(argv[n]); debug("\n"); } #endif /* Open the tty as standard input, if it is not "-" */ debug("calling open_tty\n"); open_tty(); ndelay_off(STDIN_FILENO); debug("duping\n"); xdup2(STDIN_FILENO, 1); xdup2(STDIN_FILENO, 2); /* Steal ctty if we don't have it yet */ tsid = tcgetsid(STDIN_FILENO); if (tsid < 0 || pid != tsid) { if (ioctl(STDIN_FILENO, TIOCSCTTY, /*force:*/ (long)1) < 0) bb_perror_msg_and_die("TIOCSCTTY"); } #ifdef __linux__ /* Make ourself a foreground process group within our session */ if (tcsetpgrp(STDIN_FILENO, pid) < 0) bb_perror_msg_and_die("tcsetpgrp"); #endif /* * The following ioctl will fail if stdin is not a tty, but also when * there is noise on the modem control lines. In the latter case, the * common course of action is (1) fix your cables (2) give the modem more * time to properly reset after hanging up. SunOS users can achieve (2) * by patching the SunOS kernel variable "zsadtrlow" to a larger value; * 5 seconds seems to be a good value. */ if (tcgetattr(STDIN_FILENO, &G.tty_attrs) < 0) bb_perror_msg_and_die("tcgetattr"); /* Update the utmp file. This tty is ours now! */ update_utmp(pid, LOGIN_PROCESS, G.tty_name, "LOGIN", G.fakehost); /* Initialize tty attrs (raw mode, eight-bit, blocking i/o) */ debug("calling init_tty_attrs\n"); init_tty_attrs(G.speeds[0]); /* Write the modem init string and DON'T flush the buffers */ if (option_mask32 & F_INITSTRING) { debug("writing init string\n"); full_write1_str(G.initstring); } /* Optionally detect the baud rate from the modem status message */ debug("before autobaud\n"); if (option_mask32 & F_PARSE) auto_baud(); /* Set the optional timer */ signal(SIGALRM, alarm_handler); alarm(G.timeout); /* if 0, alarm is not set */ /* Optionally wait for CR or LF before writing /etc/issue */ if (option_mask32 & F_WAITCRLF) { char ch; debug("waiting for cr-lf\n"); while (safe_read(STDIN_FILENO, &ch, 1) == 1) { debug("read %x\n", (unsigned char)ch); if (ch == '\n' || ch == '\r') break; } } logname = NULL; if (!(option_mask32 & F_NOPROMPT)) { /* NB: init_tty_attrs already set line speed * to G.speeds[0] */ int baud_index = 0; while (1) { /* Read the login name */ debug("reading login name\n"); logname = get_logname(); if (logname) break; /* We are here only if G.numspeed > 1 */ baud_index = (baud_index + 1) % G.numspeed; cfsetspeed(&G.tty_attrs, G.speeds[baud_index]); set_tty_attrs(); } } /* Disable timer */ alarm(0); finalize_tty_attrs(); /* Let the login program take care of password validation */ /* We use PATH because we trust that root doesn't set "bad" PATH, * and getty is not suid-root applet */ /* With -n, logname == NULL, and login will ask for username instead */ BB_EXECLP(G.login, G.login, "--", logname, (char *)0); bb_error_msg_and_die("can't execute '%s'", G.login); } busybox-1.22.1/loginutils/addgroup.c0000644000000000000000000001253712267106022016136 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * addgroup - add groups to /etc/group and /etc/gshadow * * Copyright (C) 1999 by Lineo, inc. and John Beppu * Copyright (C) 1999,2000,2001 by John Beppu * Copyright (C) 2007 by Tito Ragusa * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * */ //usage:#define addgroup_trivial_usage //usage: "[-g GID] " IF_FEATURE_ADDUSER_TO_GROUP("[USER] ") "GROUP" //usage:#define addgroup_full_usage "\n\n" //usage: "Add a group " IF_FEATURE_ADDUSER_TO_GROUP("or add a user to a group") "\n" //usage: "\n -g GID Group id" //usage: "\n -S Create a system group" #include "libbb.h" #if CONFIG_LAST_SYSTEM_ID < CONFIG_FIRST_SYSTEM_ID #error Bad LAST_SYSTEM_ID or FIRST_SYSTEM_ID in .config #endif #define OPT_GID (1 << 0) #define OPT_SYSTEM_ACCOUNT (1 << 1) /* We assume GID_T_MAX == INT_MAX */ static void xgroup_study(struct group *g) { unsigned max = INT_MAX; /* Make sure gr_name is unused */ if (getgrnam(g->gr_name)) { bb_error_msg_and_die("%s '%s' in use", "group", g->gr_name); /* these format strings are reused in adduser and addgroup */ } /* if a specific gid is requested, the --system switch and */ /* min and max values are overridden, and the range of valid */ /* gid values is set to [0, INT_MAX] */ if (!(option_mask32 & OPT_GID)) { if (option_mask32 & OPT_SYSTEM_ACCOUNT) { g->gr_gid = CONFIG_FIRST_SYSTEM_ID; max = CONFIG_LAST_SYSTEM_ID; } else { g->gr_gid = CONFIG_LAST_SYSTEM_ID + 1; max = 64999; } } /* Check if the desired gid is free * or find the first free one */ while (1) { if (!getgrgid(g->gr_gid)) { return; /* found free group: return */ } if (option_mask32 & OPT_GID) { /* -g N, cannot pick gid other than N: error */ bb_error_msg_and_die("%s '%s' in use", "gid", itoa(g->gr_gid)); /* this format strings is reused in adduser and addgroup */ } if (g->gr_gid == max) { /* overflowed: error */ bb_error_msg_and_die("no %cids left", 'g'); /* this format string is reused in adduser and addgroup */ } g->gr_gid++; } } /* append a new user to the passwd file */ static void new_group(char *group, gid_t gid) { struct group gr; char *p; /* make sure gid and group haven't already been allocated */ gr.gr_gid = gid; gr.gr_name = group; xgroup_study(&gr); /* add entry to group */ p = xasprintf("x:%u:", (unsigned) gr.gr_gid); if (update_passwd(bb_path_group_file, group, p, NULL) < 0) exit(EXIT_FAILURE); if (ENABLE_FEATURE_CLEAN_UP) free(p); #if ENABLE_FEATURE_SHADOWPASSWDS /* /etc/gshadow fields: * 1. Group name. * 2. Encrypted password. * If set, non-members of the group can join the group * by typing the password for that group using the newgrp command. * If the value is of this field ! then no user is allowed * to access the group using the newgrp command. A value of !! * is treated the same as a value of ! only it indicates * that a password has never been set before. If the value is null, * only group members can log into the group. * 3. Group administrators (comma delimited list). * Group members listed here can add or remove group members * using the gpasswd command. * 4. Group members (comma delimited list). */ /* Ignore errors: if file is missing we assume admin doesn't want it */ update_passwd(bb_path_gshadow_file, group, "!::", NULL); #endif } #if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS static const char addgroup_longopts[] ALIGN1 = "gid\0" Required_argument "g" "system\0" No_argument "S" ; #endif /* * addgroup will take a login_name as its first parameter. * * gid can be customized via command-line parameters. * If called with two non-option arguments, addgroup * will add an existing user to an existing group. */ int addgroup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int addgroup_main(int argc UNUSED_PARAM, char **argv) { unsigned opts; unsigned gid = 0; /* need to be root */ if (geteuid()) { bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); } #if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS applet_long_options = addgroup_longopts; #endif /* Syntax: * addgroup group * addgroup -g num group * addgroup user group * Check for min, max and missing args */ opt_complementary = "-1:?2:g+"; opts = getopt32(argv, "g:S", &gid); /* move past the commandline options */ argv += optind; //argc -= optind; #if ENABLE_FEATURE_ADDUSER_TO_GROUP if (argv[1]) { struct group *gr; if (opts & OPT_GID) { /* -g was there, but "addgroup -g num user group" * is a no-no */ bb_show_usage(); } /* check if group and user exist */ xuname2uid(argv[0]); /* unknown user: exit */ gr = xgetgrnam(argv[1]); /* unknown group: exit */ /* check if user is already in this group */ for (; *(gr->gr_mem) != NULL; (gr->gr_mem)++) { if (!strcmp(argv[0], *(gr->gr_mem))) { /* user is already in group: do nothing */ return EXIT_SUCCESS; } } if (update_passwd(bb_path_group_file, argv[1], NULL, argv[0]) < 0) { return EXIT_FAILURE; } # if ENABLE_FEATURE_SHADOWPASSWDS update_passwd(bb_path_gshadow_file, argv[1], NULL, argv[0]); # endif } else #endif /* ENABLE_FEATURE_ADDUSER_TO_GROUP */ { die_if_bad_username(argv[0]); new_group(argv[0], gid); } /* Reached only on success */ return EXIT_SUCCESS; } busybox-1.22.1/loginutils/login.c0000644000000000000000000003402312263563520015441 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define login_trivial_usage //usage: "[-p] [-h HOST] [[-f] USER]" //usage:#define login_full_usage "\n\n" //usage: "Begin a new session on the system\n" //usage: "\n -f Don't authenticate (user already authenticated)" //usage: "\n -h Name of the remote host" //usage: "\n -p Preserve environment" #include "libbb.h" #include #include #if ENABLE_SELINUX # include /* for is_selinux_enabled() */ # include /* for get_default_context() */ # include /* for security class definitions */ #endif #if ENABLE_PAM /* PAM may include . We may need to undefine bbox's stub define: */ # undef setlocale /* For some obscure reason, PAM is not in pam/xxx, but in security/xxx. * Apparently they like to confuse people. */ # include # include static const struct pam_conv conv = { misc_conv, NULL }; #endif enum { TIMEOUT = 60, EMPTY_USERNAME_COUNT = 10, /* Some users found 32 chars limit to be too low: */ USERNAME_SIZE = 64, TTYNAME_SIZE = 32, }; struct globals { struct termios tty_attrs; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { } while (0) #if ENABLE_FEATURE_NOLOGIN static void die_if_nologin(void) { FILE *fp; int c; int empty = 1; fp = fopen_for_read("/etc/nologin"); if (!fp) /* assuming it does not exist */ return; while ((c = getc(fp)) != EOF) { if (c == '\n') bb_putchar('\r'); bb_putchar(c); empty = 0; } if (empty) puts("\r\nSystem closed for routine maintenance\r"); fclose(fp); fflush_all(); /* Users say that they do need this prior to exit: */ tcdrain(STDOUT_FILENO); exit(EXIT_FAILURE); } #else # define die_if_nologin() ((void)0) #endif #if ENABLE_FEATURE_SECURETTY && !ENABLE_PAM static int check_securetty(const char *short_tty) { char *buf = (char*)"/etc/securetty"; /* any non-NULL is ok */ parser_t *parser = config_open2("/etc/securetty", fopen_for_read); while (config_read(parser, &buf, 1, 1, "# \t", PARSE_NORMAL)) { if (strcmp(buf, short_tty) == 0) break; buf = NULL; } config_close(parser); /* buf != NULL here if config file was not found, empty * or line was found which equals short_tty */ return buf != NULL; } #else static ALWAYS_INLINE int check_securetty(const char *short_tty UNUSED_PARAM) { return 1; } #endif #if ENABLE_SELINUX static void initselinux(char *username, char *full_tty, security_context_t *user_sid) { security_context_t old_tty_sid, new_tty_sid; if (!is_selinux_enabled()) return; if (get_default_context(username, NULL, user_sid)) { bb_error_msg_and_die("can't get SID for %s", username); } if (getfilecon(full_tty, &old_tty_sid) < 0) { bb_perror_msg_and_die("getfilecon(%s) failed", full_tty); } if (security_compute_relabel(*user_sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0) { bb_perror_msg_and_die("security_change_sid(%s) failed", full_tty); } if (setfilecon(full_tty, new_tty_sid) != 0) { bb_perror_msg_and_die("chsid(%s, %s) failed", full_tty, new_tty_sid); } } #endif #if ENABLE_LOGIN_SCRIPTS static void run_login_script(struct passwd *pw, char *full_tty) { char *t_argv[2]; t_argv[0] = getenv("LOGIN_PRE_SUID_SCRIPT"); if (t_argv[0]) { t_argv[1] = NULL; xsetenv("LOGIN_TTY", full_tty); xsetenv("LOGIN_USER", pw->pw_name); xsetenv("LOGIN_UID", utoa(pw->pw_uid)); xsetenv("LOGIN_GID", utoa(pw->pw_gid)); xsetenv("LOGIN_SHELL", pw->pw_shell); spawn_and_wait(t_argv); /* NOMMU-friendly */ unsetenv("LOGIN_TTY"); unsetenv("LOGIN_USER"); unsetenv("LOGIN_UID"); unsetenv("LOGIN_GID"); unsetenv("LOGIN_SHELL"); } } #else void run_login_script(struct passwd *pw, char *full_tty); #endif #if ENABLE_LOGIN_SESSION_AS_CHILD && ENABLE_PAM static void login_pam_end(pam_handle_t *pamh) { int pamret; pamret = pam_setcred(pamh, PAM_DELETE_CRED); if (pamret != PAM_SUCCESS) { bb_error_msg("pam_%s failed: %s (%d)", "setcred", pam_strerror(pamh, pamret), pamret); } pamret = pam_close_session(pamh, 0); if (pamret != PAM_SUCCESS) { bb_error_msg("pam_%s failed: %s (%d)", "close_session", pam_strerror(pamh, pamret), pamret); } pamret = pam_end(pamh, pamret); if (pamret != PAM_SUCCESS) { bb_error_msg("pam_%s failed: %s (%d)", "end", pam_strerror(pamh, pamret), pamret); } } #endif /* ENABLE_PAM */ static void get_username_or_die(char *buf, int size_buf) { int c, cntdown; cntdown = EMPTY_USERNAME_COUNT; prompt: print_login_prompt(); /* skip whitespace */ do { c = getchar(); if (c == EOF) exit(EXIT_FAILURE); if (c == '\n') { if (!--cntdown) exit(EXIT_FAILURE); goto prompt; } } while (isspace(c)); /* maybe isblank? */ *buf++ = c; if (!fgets(buf, size_buf-2, stdin)) exit(EXIT_FAILURE); if (!strchr(buf, '\n')) exit(EXIT_FAILURE); while ((unsigned char)*buf > ' ') buf++; *buf = '\0'; } static void motd(void) { int fd; fd = open(bb_path_motd_file, O_RDONLY); if (fd >= 0) { fflush_all(); bb_copyfd_eof(fd, STDOUT_FILENO); close(fd); } } static void alarm_handler(int sig UNUSED_PARAM) { /* This is the escape hatch! Poor serial line users and the like * arrive here when their connection is broken. * We don't want to block here */ ndelay_on(STDOUT_FILENO); /* Test for correct attr restoring: * run "getty 0 -" from a shell, enter bogus username, stop at * password prompt, let it time out. Without the tcsetattr below, * when you are back at shell prompt, echo will be still off. */ tcsetattr_stdin_TCSANOW(&G.tty_attrs); printf("\r\nLogin timed out after %u seconds\r\n", TIMEOUT); fflush_all(); /* unix API is brain damaged regarding O_NONBLOCK, * we should undo it, or else we can affect other processes */ ndelay_off(STDOUT_FILENO); _exit(EXIT_SUCCESS); } int login_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int login_main(int argc UNUSED_PARAM, char **argv) { enum { LOGIN_OPT_f = (1<<0), LOGIN_OPT_h = (1<<1), LOGIN_OPT_p = (1<<2), }; char *fromhost; char username[USERNAME_SIZE]; int run_by_root; unsigned opt; int count = 0; struct passwd *pw; char *opt_host = NULL; char *opt_user = opt_user; /* for compiler */ char *full_tty; char *short_tty; IF_SELINUX(security_context_t user_sid = NULL;) #if ENABLE_PAM int pamret; pam_handle_t *pamh; const char *pamuser; const char *failed_msg; struct passwd pwdstruct; char pwdbuf[256]; char **pamenv; #endif #if ENABLE_LOGIN_SESSION_AS_CHILD pid_t child_pid; #endif INIT_G(); /* More of suid paranoia if called by non-root: */ /* Clear dangerous stuff, set PATH */ run_by_root = !sanitize_env_if_suid(); /* Mandatory paranoia for suid applet: * ensure that fd# 0,1,2 are opened (at least to /dev/null) * and any extra open fd's are closed. * (The name of the function is misleading. Not daemonizing here.) */ bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE | DAEMON_CLOSE_EXTRA_FDS, NULL); username[0] = '\0'; opt = getopt32(argv, "f:h:p", &opt_user, &opt_host); if (opt & LOGIN_OPT_f) { if (!run_by_root) bb_error_msg_and_die("-f is for root only"); safe_strncpy(username, opt_user, sizeof(username)); } argv += optind; if (argv[0]) /* user from command line (getty) */ safe_strncpy(username, argv[0], sizeof(username)); /* Save tty attributes - and by doing it, check that it's indeed a tty */ if (tcgetattr(STDIN_FILENO, &G.tty_attrs) < 0 || !isatty(STDOUT_FILENO) /*|| !isatty(STDERR_FILENO) - no, guess some people might want to redirect this */ ) { return EXIT_FAILURE; /* Must be a terminal */ } /* We install timeout handler only _after_ we saved G.tty_attrs */ signal(SIGALRM, alarm_handler); alarm(TIMEOUT); /* Find out and memorize our tty name */ full_tty = xmalloc_ttyname(STDIN_FILENO); if (!full_tty) full_tty = xstrdup("UNKNOWN"); short_tty = skip_dev_pfx(full_tty); if (opt_host) { fromhost = xasprintf(" on '%s' from '%s'", short_tty, opt_host); } else { fromhost = xasprintf(" on '%s'", short_tty); } /* Was breaking "login " from shell command line: */ /*bb_setpgrp();*/ openlog(applet_name, LOG_PID | LOG_CONS, LOG_AUTH); while (1) { /* flush away any type-ahead (as getty does) */ tcflush(0, TCIFLUSH); if (!username[0]) get_username_or_die(username, sizeof(username)); #if ENABLE_PAM pamret = pam_start("login", username, &conv, &pamh); if (pamret != PAM_SUCCESS) { failed_msg = "start"; goto pam_auth_failed; } /* set TTY (so things like securetty work) */ pamret = pam_set_item(pamh, PAM_TTY, short_tty); if (pamret != PAM_SUCCESS) { failed_msg = "set_item(TTY)"; goto pam_auth_failed; } /* set RHOST */ if (opt_host) { pamret = pam_set_item(pamh, PAM_RHOST, opt_host); if (pamret != PAM_SUCCESS) { failed_msg = "set_item(RHOST)"; goto pam_auth_failed; } } if (!(opt & LOGIN_OPT_f)) { pamret = pam_authenticate(pamh, 0); if (pamret != PAM_SUCCESS) { failed_msg = "authenticate"; goto pam_auth_failed; /* TODO: or just "goto auth_failed" * since user seems to enter wrong password * (in this case pamret == 7) */ } } /* check that the account is healthy */ pamret = pam_acct_mgmt(pamh, 0); if (pamret != PAM_SUCCESS) { failed_msg = "acct_mgmt"; goto pam_auth_failed; } /* read user back */ pamuser = NULL; /* gcc: "dereferencing type-punned pointer breaks aliasing rules..." * thus we cast to (void*) */ if (pam_get_item(pamh, PAM_USER, (void*)&pamuser) != PAM_SUCCESS) { failed_msg = "get_item(USER)"; goto pam_auth_failed; } if (!pamuser || !pamuser[0]) goto auth_failed; safe_strncpy(username, pamuser, sizeof(username)); /* Don't use "pw = getpwnam(username);", * PAM is said to be capable of destroying static storage * used by getpwnam(). We are using safe(r) function */ pw = NULL; getpwnam_r(username, &pwdstruct, pwdbuf, sizeof(pwdbuf), &pw); if (!pw) goto auth_failed; pamret = pam_open_session(pamh, 0); if (pamret != PAM_SUCCESS) { failed_msg = "open_session"; goto pam_auth_failed; } pamret = pam_setcred(pamh, PAM_ESTABLISH_CRED); if (pamret != PAM_SUCCESS) { failed_msg = "setcred"; goto pam_auth_failed; } break; /* success, continue login process */ pam_auth_failed: /* syslog, because we don't want potential attacker * to know _why_ login failed */ syslog(LOG_WARNING, "pam_%s call failed: %s (%d)", failed_msg, pam_strerror(pamh, pamret), pamret); safe_strncpy(username, "UNKNOWN", sizeof(username)); #else /* not PAM */ pw = getpwnam(username); if (!pw) { strcpy(username, "UNKNOWN"); goto fake_it; } if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*') goto auth_failed; if (opt & LOGIN_OPT_f) break; /* -f USER: success without asking passwd */ if (pw->pw_uid == 0 && !check_securetty(short_tty)) goto auth_failed; /* Don't check the password if password entry is empty (!) */ if (!pw->pw_passwd[0]) break; fake_it: /* Password reading and authorization takes place here. * Note that reads (in no-echo mode) trash tty attributes. * If we get interrupted by SIGALRM, we need to restore attrs. */ if (ask_and_check_password(pw) > 0) break; #endif /* ENABLE_PAM */ auth_failed: opt &= ~LOGIN_OPT_f; bb_do_delay(LOGIN_FAIL_DELAY); /* TODO: doesn't sound like correct English phrase to me */ puts("Login incorrect"); if (++count == 3) { syslog(LOG_WARNING, "invalid password for '%s'%s", username, fromhost); if (ENABLE_FEATURE_CLEAN_UP) free(fromhost); return EXIT_FAILURE; } username[0] = '\0'; } /* while (1) */ alarm(0); /* We can ignore /etc/nologin if we are logging in as root, * it doesn't matter whether we are run by root or not */ if (pw->pw_uid != 0) die_if_nologin(); #if ENABLE_LOGIN_SESSION_AS_CHILD child_pid = vfork(); if (child_pid != 0) { if (child_pid < 0) bb_perror_msg("vfork"); else { if (safe_waitpid(child_pid, NULL, 0) == -1) bb_perror_msg("waitpid"); update_utmp(child_pid, DEAD_PROCESS, NULL, NULL, NULL); } IF_PAM(login_pam_end(pamh);) return 0; } #endif IF_SELINUX(initselinux(username, full_tty, &user_sid);) /* Try these, but don't complain if they fail. * _f_chown is safe wrt race t=ttyname(0);...;chown(t); */ fchown(0, pw->pw_uid, pw->pw_gid); fchmod(0, 0600); update_utmp(getpid(), USER_PROCESS, short_tty, username, run_by_root ? opt_host : NULL); /* We trust environment only if we run by root */ if (ENABLE_LOGIN_SCRIPTS && run_by_root) run_login_script(pw, full_tty); change_identity(pw); setup_environment(pw->pw_shell, (!(opt & LOGIN_OPT_p) * SETUP_ENV_CLEARENV) + SETUP_ENV_CHANGEENV, pw); #if ENABLE_PAM /* Modules such as pam_env will setup the PAM environment, * which should be copied into the new environment. */ pamenv = pam_getenvlist(pamh); if (pamenv) while (*pamenv) { putenv(*pamenv); pamenv++; } #endif motd(); if (pw->pw_uid == 0) syslog(LOG_INFO, "root login%s", fromhost); if (ENABLE_FEATURE_CLEAN_UP) free(fromhost); /* well, a simple setexeccon() here would do the job as well, * but let's play the game for now */ IF_SELINUX(set_current_security_context(user_sid);) // util-linux login also does: // /* start new session */ // setsid(); // /* TIOCSCTTY: steal tty from other process group */ // if (ioctl(0, TIOCSCTTY, 1)) error_msg... // BBox login used to do this (see above): // bb_setpgrp(); // If this stuff is really needed, add it and explain why! /* Set signals to defaults */ /* Non-ignored signals revert to SIG_DFL on exec anyway */ /*signal(SIGALRM, SIG_DFL);*/ /* Is this correct? This way user can ctrl-c out of /etc/profile, * potentially creating security breach (tested with bash 3.0). * But without this, bash 3.0 will not enable ctrl-c either. * Maybe bash is buggy? * Need to find out what standards say about /bin/login - * should we leave SIGINT etc enabled or disabled? */ signal(SIGINT, SIG_DFL); /* Exec login shell with no additional parameters */ run_shell(pw->pw_shell, 1, NULL, NULL); /* return EXIT_FAILURE; - not reached */ } busybox-1.22.1/loginutils/vlock.c0000644000000000000000000000572512263563520015456 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * vlock implementation for busybox * * Copyright (C) 2000 by spoon * Written by spoon * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* Shoutz to Michael K. Johnson , author of the * original vlock. I snagged a bunch of his code to write this * minimalistic vlock. */ /* Fixed by Erik Andersen to do passwords the tinylogin way... * It now works with md5, sha1, etc passwords. */ //usage:#define vlock_trivial_usage //usage: "[-a]" //usage:#define vlock_full_usage "\n\n" //usage: "Lock a virtual terminal. A password is required to unlock.\n" //usage: "\n -a Lock all VTs" #include "libbb.h" #ifdef __linux__ #include static void release_vt(int signo UNUSED_PARAM) { /* If -a, param is 0, which means: * "no, kernel, we don't allow console switch away from us!" */ ioctl(STDIN_FILENO, VT_RELDISP, (unsigned long) !option_mask32); } static void acquire_vt(int signo UNUSED_PARAM) { /* ACK to kernel that switch to console is successful */ ioctl(STDIN_FILENO, VT_RELDISP, VT_ACKACQ); } #endif int vlock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int vlock_main(int argc UNUSED_PARAM, char **argv) { #ifdef __linux__ struct vt_mode vtm; struct vt_mode ovtm; #endif struct termios term; struct termios oterm; struct passwd *pw; pw = xgetpwuid(getuid()); opt_complementary = "=0"; /* no params! */ getopt32(argv, "a"); /* Ignore some signals so that we don't get killed by them */ bb_signals(0 + (1 << SIGTSTP) + (1 << SIGTTIN) + (1 << SIGTTOU) + (1 << SIGHUP ) + (1 << SIGCHLD) /* paranoia :) */ + (1 << SIGQUIT) + (1 << SIGINT ) , SIG_IGN); #ifdef __linux__ /* We will use SIGUSRx for console switch control: */ /* 1: set handlers */ signal_SA_RESTART_empty_mask(SIGUSR1, release_vt); signal_SA_RESTART_empty_mask(SIGUSR2, acquire_vt); /* 2: unmask them */ sig_unblock(SIGUSR1); sig_unblock(SIGUSR2); #endif /* Revert stdin/out to our controlling tty * (or die if we have none) */ xmove_fd(xopen(CURRENT_TTY, O_RDWR), STDIN_FILENO); xdup2(STDIN_FILENO, STDOUT_FILENO); #ifdef __linux__ xioctl(STDIN_FILENO, VT_GETMODE, &vtm); ovtm = vtm; /* "console switches are controlled by us, not kernel!" */ vtm.mode = VT_PROCESS; vtm.relsig = SIGUSR1; vtm.acqsig = SIGUSR2; ioctl(STDIN_FILENO, VT_SETMODE, &vtm); #endif tcgetattr(STDIN_FILENO, &oterm); term = oterm; term.c_iflag &= ~BRKINT; term.c_iflag |= IGNBRK; term.c_lflag &= ~ISIG; term.c_lflag &= ~(ECHO | ECHOCTL); tcsetattr_stdin_TCSANOW(&term); while (1) { printf("Virtual console%s locked by %s.\n", /* "s" if -a, else "": */ "s" + !option_mask32, pw->pw_name ); if (ask_and_check_password(pw) > 0) { break; } bb_do_delay(LOGIN_FAIL_DELAY); puts("Incorrect password"); } #ifdef __linux__ ioctl(STDIN_FILENO, VT_SETMODE, &ovtm); #endif tcsetattr_stdin_TCSANOW(&oterm); fflush_stdout_and_exit(EXIT_SUCCESS); } busybox-1.22.1/loginutils/passwd.c0000644000000000000000000001364412263563520015640 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define passwd_trivial_usage //usage: "[OPTIONS] [USER]" //usage:#define passwd_full_usage "\n\n" //usage: "Change USER's password (default: current user)" //usage: "\n" //usage: "\n -a ALG Encryption method" //usage: "\n -d Set password to ''" //usage: "\n -l Lock (disable) account" //usage: "\n -u Unlock (enable) account" #include "libbb.h" #include #include /* setrlimit */ static char* new_password(const struct passwd *pw, uid_t myuid, const char *algo) { char salt[MAX_PW_SALT_LEN]; char *orig = (char*)""; char *newp = NULL; char *cp = NULL; char *ret = NULL; /* failure so far */ if (myuid != 0 && pw->pw_passwd[0]) { char *encrypted; orig = bb_ask_stdin("Old password: "); /* returns ptr to static */ if (!orig) goto err_ret; encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */ if (strcmp(encrypted, pw->pw_passwd) != 0) { syslog(LOG_WARNING, "incorrect password for %s", pw->pw_name); bb_do_delay(LOGIN_FAIL_DELAY); puts("Incorrect password"); goto err_ret; } if (ENABLE_FEATURE_CLEAN_UP) free(encrypted); } orig = xstrdup(orig); /* or else bb_ask_stdin() will destroy it */ newp = bb_ask_stdin("New password: "); /* returns ptr to static */ if (!newp) goto err_ret; newp = xstrdup(newp); /* we are going to bb_ask_stdin() again, so save it */ if (ENABLE_FEATURE_PASSWD_WEAK_CHECK && obscure(orig, newp, pw) && myuid != 0 ) { goto err_ret; /* non-root is not allowed to have weak passwd */ } cp = bb_ask_stdin("Retype password: "); if (!cp) goto err_ret; if (strcmp(cp, newp) != 0) { puts("Passwords don't match"); goto err_ret; } crypt_make_pw_salt(salt, algo); /* pw_encrypt returns malloced str */ ret = pw_encrypt(newp, salt, 1); /* whee, success! */ err_ret: nuke_str(orig); if (ENABLE_FEATURE_CLEAN_UP) free(orig); nuke_str(newp); if (ENABLE_FEATURE_CLEAN_UP) free(newp); nuke_str(cp); return ret; } int passwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int passwd_main(int argc UNUSED_PARAM, char **argv) { enum { OPT_algo = (1 << 0), /* -a - password algorithm */ OPT_lock = (1 << 1), /* -l - lock account */ OPT_unlock = (1 << 2), /* -u - unlock account */ OPT_delete = (1 << 3), /* -d - delete password */ OPT_lud = OPT_lock | OPT_unlock | OPT_delete, }; unsigned opt; int rc; const char *opt_a = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO; const char *filename; char *myname; char *name; char *newp; struct passwd *pw; uid_t myuid; struct rlimit rlimit_fsize; char c; #if ENABLE_FEATURE_SHADOWPASSWDS /* Using _r function to avoid pulling in static buffers */ struct spwd spw; char buffer[256]; #endif logmode = LOGMODE_BOTH; openlog(applet_name, 0, LOG_AUTH); opt = getopt32(argv, "a:lud", &opt_a); //argc -= optind; argv += optind; myuid = getuid(); /* -l, -u, -d require root priv and username argument */ if ((opt & OPT_lud) && (myuid != 0 || !argv[0])) bb_show_usage(); /* Will complain and die if username not found */ myname = xstrdup(xuid2uname(myuid)); name = argv[0] ? argv[0] : myname; pw = xgetpwnam(name); if (myuid != 0 && pw->pw_uid != myuid) { /* LOGMODE_BOTH */ bb_error_msg_and_die("%s can't change password for %s", myname, name); } #if ENABLE_FEATURE_SHADOWPASSWDS { /* getspnam_r may return 0 yet set result to NULL. * At least glibc 2.4 does this. Be extra paranoid here. */ struct spwd *result = NULL; errno = 0; if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result) != 0 || !result /* no error, but no record found either */ || strcmp(result->sp_namp, pw->pw_name) != 0 /* paranoia */ ) { if (errno != ENOENT) { /* LOGMODE_BOTH */ bb_perror_msg("no record of %s in %s, using %s", name, bb_path_shadow_file, bb_path_passwd_file); } /* else: /etc/shadow does not exist, * apparently we are on a shadow-less system, * no surprise there */ } else { pw->pw_passwd = result->sp_pwdp; } } #endif /* Decide what the new password will be */ newp = NULL; c = pw->pw_passwd[0] - '!'; if (!(opt & OPT_lud)) { if (myuid != 0 && !c) { /* passwd starts with '!' */ /* LOGMODE_BOTH */ bb_error_msg_and_die("can't change " "locked password for %s", name); } printf("Changing password for %s\n", name); newp = new_password(pw, myuid, opt_a); if (!newp) { logmode = LOGMODE_STDIO; bb_error_msg_and_die("password for %s is unchanged", name); } } else if (opt & OPT_lock) { if (!c) goto skip; /* passwd starts with '!' */ newp = xasprintf("!%s", pw->pw_passwd); } else if (opt & OPT_unlock) { if (c) goto skip; /* not '!' */ /* pw->pw_passwd points to static storage, * strdup'ing to avoid nasty surprizes */ newp = xstrdup(&pw->pw_passwd[1]); } else if (opt & OPT_delete) { newp = (char*)""; } rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000; setrlimit(RLIMIT_FSIZE, &rlimit_fsize); bb_signals(0 + (1 << SIGHUP) + (1 << SIGINT) + (1 << SIGQUIT) , SIG_IGN); umask(077); xsetuid(0); #if ENABLE_FEATURE_SHADOWPASSWDS filename = bb_path_shadow_file; rc = update_passwd(bb_path_shadow_file, name, newp, NULL); if (rc > 0) /* password in /etc/shadow was updated */ newp = (char*) "x"; if (rc >= 0) /* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */ #endif { filename = bb_path_passwd_file; rc = update_passwd(bb_path_passwd_file, name, newp, NULL); } /* LOGMODE_BOTH */ if (rc < 0) bb_error_msg_and_die("can't update password file %s", filename); bb_info_msg("Password for %s changed by %s", name, myname); /*if (ENABLE_FEATURE_CLEAN_UP) free(newp); - can't, it may be non-malloced */ skip: if (!newp) { bb_error_msg_and_die("password for %s is already %slocked", name, (opt & OPT_unlock) ? "un" : ""); } if (ENABLE_FEATURE_CLEAN_UP) free(myname); return 0; } busybox-1.22.1/loginutils/su.c0000644000000000000000000000746112263563520014766 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini su implementation for busybox * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" #include //usage:#define su_trivial_usage //usage: "[OPTIONS] [-] [USER]" //usage:#define su_full_usage "\n\n" //usage: "Run shell under USER (by default, root)\n" //usage: "\n -,-l Clear environment, run shell as login shell" //usage: "\n -p,-m Do not set new $HOME, $SHELL, $USER, $LOGNAME" //usage: "\n -c CMD Command to pass to 'sh -c'" //usage: "\n -s SH Shell to use instead of user's default" #if ENABLE_FEATURE_SU_CHECKS_SHELLS /* Return 1 if SHELL is a restricted shell (one not returned by * getusershell), else 0, meaning it is a standard shell. */ static int restricted_shell(const char *shell) { char *line; int result = 1; /*setusershell(); - getusershell does it itself*/ while ((line = getusershell()) != NULL) { if (/* *line != '#' && */ strcmp(line, shell) == 0) { result = 0; break; } } if (ENABLE_FEATURE_CLEAN_UP) endusershell(); return result; } #endif #define SU_OPT_mp (3) #define SU_OPT_l (4) int su_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int su_main(int argc UNUSED_PARAM, char **argv) { unsigned flags; char *opt_shell = NULL; char *opt_command = NULL; const char *opt_username = "root"; struct passwd *pw; uid_t cur_uid = getuid(); const char *tty; #if ENABLE_FEATURE_UTMP char user_buf[64]; #endif const char *old_user; flags = getopt32(argv, "mplc:s:", &opt_command, &opt_shell); //argc -= optind; argv += optind; if (argv[0] && LONE_DASH(argv[0])) { flags |= SU_OPT_l; argv++; } /* get user if specified */ if (argv[0]) { opt_username = argv[0]; argv++; } if (ENABLE_FEATURE_SU_SYSLOG) { /* The utmp entry (via getlogin) is probably the best way to * identify the user, especially if someone su's from a su-shell. * But getlogin can fail -- usually due to lack of utmp entry. * in this case resort to getpwuid. */ #if ENABLE_FEATURE_UTMP old_user = user_buf; if (getlogin_r(user_buf, sizeof(user_buf)) != 0) #endif { pw = getpwuid(cur_uid); old_user = pw ? xstrdup(pw->pw_name) : ""; } tty = xmalloc_ttyname(2); if (!tty) { tty = "none"; } openlog(applet_name, 0, LOG_AUTH); } pw = xgetpwnam(opt_username); if (cur_uid == 0 || ask_and_check_password(pw) > 0) { if (ENABLE_FEATURE_SU_SYSLOG) syslog(LOG_NOTICE, "%c %s %s:%s", '+', tty, old_user, opt_username); } else { if (ENABLE_FEATURE_SU_SYSLOG) syslog(LOG_NOTICE, "%c %s %s:%s", '-', tty, old_user, opt_username); bb_error_msg_and_die("incorrect password"); } if (ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_SU_SYSLOG) { closelog(); } if (!opt_shell && (flags & SU_OPT_mp)) { /* -s SHELL is not given, but "preserve env" opt is */ opt_shell = getenv("SHELL"); } #if ENABLE_FEATURE_SU_CHECKS_SHELLS if (opt_shell && cur_uid != 0 && pw->pw_shell && restricted_shell(pw->pw_shell)) { /* The user being su'd to has a nonstandard shell, and so is * probably a uucp account or has restricted access. Don't * compromise the account by allowing access with a standard * shell. */ bb_error_msg("using restricted shell"); opt_shell = NULL; /* ignore -s PROG */ } /* else: user can run whatever he wants via "su -s PROG USER". * This is safe since PROG is run under user's uid/gid. */ #endif if (!opt_shell) opt_shell = pw->pw_shell; change_identity(pw); setup_environment(opt_shell, ((flags & SU_OPT_l) / SU_OPT_l * SETUP_ENV_CLEARENV) + (!(flags & SU_OPT_mp) * SETUP_ENV_CHANGEENV) + (!(flags & SU_OPT_l) * SETUP_ENV_NO_CHDIR), pw); IF_SELINUX(set_current_security_context(NULL);) /* Never returns */ run_shell(opt_shell, flags & SU_OPT_l, opt_command, (const char**)argv); /* return EXIT_FAILURE; - not reached */ } busybox-1.22.1/loginutils/cryptpw.c0000644000000000000000000001036212263563520016041 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * cryptpw.c - output a crypt(3)ed password to stdout. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Cooked from passwd.c by Thomas Lundquist * mkpasswd compatible options added by Bernhard Reutner-Fischer * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define cryptpw_trivial_usage //usage: "[OPTIONS] [PASSWORD] [SALT]" /* We do support -s, we just don't mention it */ //usage:#define cryptpw_full_usage "\n\n" //usage: "Crypt PASSWORD using crypt(3)\n" //usage: IF_LONG_OPTS( //usage: "\n -P,--password-fd=N Read password from fd N" /* //usage: "\n -s,--stdin Use stdin; like -P0" */ //usage: "\n -m,--method=TYPE Encryption method" //usage: "\n -S,--salt=SALT" //usage: ) //usage: IF_NOT_LONG_OPTS( //usage: "\n -P N Read password from fd N" /* //usage: "\n -s Use stdin; like -P0" */ //usage: "\n -m TYPE Encryption method TYPE" //usage: "\n -S SALT" //usage: ) /* mkpasswd is an alias to cryptpw */ //usage:#define mkpasswd_trivial_usage //usage: "[OPTIONS] [PASSWORD] [SALT]" /* We do support -s, we just don't mention it */ //usage:#define mkpasswd_full_usage "\n\n" //usage: "Crypt PASSWORD using crypt(3)\n" //usage: IF_LONG_OPTS( //usage: "\n -P,--password-fd=N Read password from fd N" /* //usage: "\n -s,--stdin Use stdin; like -P0" */ //usage: "\n -m,--method=TYPE Encryption method" //usage: "\n -S,--salt=SALT" //usage: ) //usage: IF_NOT_LONG_OPTS( //usage: "\n -P N Read password from fd N" /* //usage: "\n -s Use stdin; like -P0" */ //usage: "\n -m TYPE Encryption method TYPE" //usage: "\n -S SALT" //usage: ) #include "libbb.h" /* Debian has 'mkpasswd' utility, manpage says: NAME mkpasswd - Overfeatured front end to crypt(3) SYNOPSIS mkpasswd PASSWORD SALT ... OPTIONS -S, --salt=STRING Use the STRING as salt. It must not contain prefixes such as $1$. -R, --rounds=NUMBER Use NUMBER rounds. This argument is ignored if the method choosen does not support variable rounds. For the OpenBSD Blowfish method this is the logarithm of the number of rounds. -m, --method=TYPE Compute the password using the TYPE method. If TYPE is 'help' then the available methods are printed. -P, --password-fd=NUM Read the password from file descriptor NUM instead of using getpass(3). If the file descriptor is not connected to a tty then no other message than the hashed password is printed on stdout. -s, --stdin Like --password-fd=0. ENVIRONMENT $MKPASSWD_OPTIONS A list of options which will be evaluated before the ones specified on the command line. BUGS This programs suffers of a bad case of featuritis. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Very true... cryptpw was in bbox before this gem, so we retain it, and alias mkpasswd to cryptpw. -a option (alias for -m) came from cryptpw. */ int cryptpw_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int cryptpw_main(int argc UNUSED_PARAM, char **argv) { char salt[MAX_PW_SALT_LEN]; char *salt_ptr; char *password; const char *opt_m, *opt_S; int fd; #if ENABLE_LONG_OPTS static const char mkpasswd_longopts[] ALIGN1 = "stdin\0" No_argument "s" "password-fd\0" Required_argument "P" "salt\0" Required_argument "S" "method\0" Required_argument "m" ; applet_long_options = mkpasswd_longopts; #endif fd = STDIN_FILENO; opt_m = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO; opt_S = NULL; /* at most two non-option arguments; -P NUM */ opt_complementary = "?2:P+"; getopt32(argv, "sP:S:m:a:", &fd, &opt_S, &opt_m, &opt_m); argv += optind; /* have no idea how to handle -s... */ if (argv[0] && !opt_S) opt_S = argv[1]; salt_ptr = crypt_make_pw_salt(salt, opt_m); if (opt_S) safe_strncpy(salt_ptr, opt_S, sizeof(salt) - (sizeof("$N$")-1)); xmove_fd(fd, STDIN_FILENO); password = argv[0]; if (!password) { /* Only mkpasswd, and only from tty, prompts. * Otherwise it is a plain read. */ password = (isatty(STDIN_FILENO) && applet_name[0] == 'm') ? bb_ask_stdin("Password: ") : xmalloc_fgetline(stdin) ; /* may still be NULL on EOF/error */ } if (password) puts(pw_encrypt(password, salt, 1)); return EXIT_SUCCESS; } busybox-1.22.1/loginutils/add-remove-shell.c0000644000000000000000000000723412263563520017465 0ustar rootroot/* * add-shell and remove-shell implementation for busybox * * Copyright (C) 2010 Nokia Corporation. All rights reserved. * Written by Alexander Shishkin * * Licensed under GPLv2 or later, see the LICENSE file in this source tree * for details. */ //applet:IF_ADD_SHELL( APPLET_ODDNAME(add-shell , add_remove_shell, BB_DIR_USR_SBIN, BB_SUID_DROP, add_shell )) //applet:IF_REMOVE_SHELL(APPLET_ODDNAME(remove-shell, add_remove_shell, BB_DIR_USR_SBIN, BB_SUID_DROP, remove_shell)) //kbuild:lib-$(CONFIG_ADD_SHELL) += add-remove-shell.o //kbuild:lib-$(CONFIG_REMOVE_SHELL) += add-remove-shell.o //config:config ADD_SHELL //config: bool "add-shell" //config: default y if DESKTOP //config: help //config: Add shells to /etc/shells. //config: //config:config REMOVE_SHELL //config: bool "remove-shell" //config: default y if DESKTOP //config: help //config: Remove shells from /etc/shells. //usage:#define add_shell_trivial_usage //usage: "SHELL..." //usage:#define add_shell_full_usage "\n\n" //usage: "Add SHELLs to /etc/shells" //usage:#define remove_shell_trivial_usage //usage: "SHELL..." //usage:#define remove_shell_full_usage "\n\n" //usage: "Remove SHELLs from /etc/shells" #include "libbb.h" #define SHELLS_FILE "/etc/shells" #define REMOVE_SHELL (ENABLE_REMOVE_SHELL && (!ENABLE_ADD_SHELL || applet_name[0] == 'r')) #define ADD_SHELL (ENABLE_ADD_SHELL && (!ENABLE_REMOVE_SHELL || applet_name[0] == 'a')) /* NB: we use the _address_, not the value, of this string * as a "special value of pointer" in the code. */ static const char dont_add[] ALIGN1 = "\n"; int add_remove_shell_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int add_remove_shell_main(int argc UNUSED_PARAM, char **argv) { FILE *orig_fp; char *orig_fn; char *new_fn; argv++; orig_fn = xmalloc_follow_symlinks(SHELLS_FILE); if (!orig_fn) return EXIT_FAILURE; orig_fp = fopen_for_read(orig_fn); new_fn = xasprintf("%s.tmp", orig_fn); /* * O_TRUNC or O_EXCL? At the first glance, O_EXCL looks better, * since it prevents races. But: (1) it requires a retry loop, * (2) if /etc/shells.tmp is *stale*, then retry loop * with O_EXCL will never succeed - it should have a timeout, * after which it should revert to O_TRUNC. * For now, I settle for O_TRUNC instead. */ xmove_fd(xopen(new_fn, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO); /* TODO: struct stat sb; xfstat(fileno(orig_fp), &sb); xfchown(STDOUT_FILENO, sb.st_uid, sb.st_gid); xfchmod(STDOUT_FILENO, sb.st_mode); */ if (orig_fp) { /* Copy old file, possibly skipping removed shell names */ char *line; while ((line = xmalloc_fgetline(orig_fp)) != NULL) { char **cpp = argv; while (*cpp) { if (strcmp(*cpp, line) == 0) { /* Old file has this shell name */ if (REMOVE_SHELL) { /* we are remove-shell */ /* delete this name by not copying it */ goto next_line; } /* we are add-shell */ /* mark this name as "do not add" */ *cpp = (char*)dont_add; } cpp++; } /* copy shell name from old to new file */ printf("%s\n", line); next_line: free(line); } if (ENABLE_FEATURE_CLEAN_UP) fclose(orig_fp); } if (ADD_SHELL) { char **cpp = argv; while (*cpp) { if (*cpp != dont_add) printf("%s\n", *cpp); cpp++; } } /* Ensure we wrote out everything */ if (fclose(stdout) != 0) { xunlink(new_fn); bb_perror_msg_and_die("%s: write error", new_fn); } /* Small hole: if rename fails, /etc/shells.tmp is not removed */ xrename(new_fn, orig_fn); if (ENABLE_FEATURE_CLEAN_UP) { free(orig_fn); free(new_fn); } return EXIT_SUCCESS; } busybox-1.22.1/loginutils/sulogin.c0000644000000000000000000000345112263563520016012 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini sulogin implementation for busybox * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define sulogin_trivial_usage //usage: "[-t N] [TTY]" //usage:#define sulogin_full_usage "\n\n" //usage: "Single user login\n" //usage: "\n -t N Timeout" #include "libbb.h" #include int sulogin_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int sulogin_main(int argc UNUSED_PARAM, char **argv) { int timeout = 0; struct passwd *pwd; const char *shell; logmode = LOGMODE_BOTH; openlog(applet_name, 0, LOG_AUTH); opt_complementary = "t+"; /* -t N */ getopt32(argv, "t:", &timeout); argv += optind; if (argv[0]) { close(0); close(1); dup(xopen(argv[0], O_RDWR)); close(2); dup(0); } /* Malicious use like "sulogin /dev/sda"? */ if (!isatty(0) || !isatty(1) || !isatty(2)) { logmode = LOGMODE_SYSLOG; bb_error_msg_and_die("not a tty"); } /* Clear dangerous stuff, set PATH */ sanitize_env_if_suid(); pwd = getpwuid(0); if (!pwd) { goto auth_error; } while (1) { int r; r = ask_and_check_password_extended(pwd, timeout, "Give root password for system maintenance\n" "(or type Control-D for normal startup):" ); if (r < 0) { /* ^D, ^C, timeout, or read error */ bb_info_msg("Normal startup"); return 0; } if (r > 0) { break; } bb_do_delay(LOGIN_FAIL_DELAY); bb_info_msg("Login incorrect"); } bb_info_msg("System Maintenance Mode"); IF_SELINUX(renew_current_security_context()); shell = getenv("SUSHELL"); if (!shell) shell = getenv("sushell"); if (!shell) shell = pwd->pw_shell; /* Exec login shell with no additional parameters. Never returns. */ run_shell(shell, 1, NULL, NULL); auth_error: bb_error_msg_and_die("no password entry for root"); } busybox-1.22.1/modutils/0000755000000000000000000000000012320365364013632 5ustar rootrootbusybox-1.22.1/modutils/depmod_process.sh0000755000000000000000000000075312263563520017204 0ustar rootroot#!/bin/sh # Depmod output may be hard to diff. # This script sorts dependencies within "xx.ko: yy.ko zz.ko" lines, # and sorts all lines too. # Usage: # # [./busybox] depmod -n | ./depmod_process.sh | sort >OUTFILE # # and then you can diff OUTFILEs. Useful for comparing bbox depmod # with module-init-tools depmod and such. while read -r word rest; do if ! test "${word/*:/}"; then echo -n "$word " echo "$rest" | xargs -n1 | sort | xargs else echo "$word $rest"; fi done busybox-1.22.1/modutils/Config.src0000644000000000000000000001724612263563520015562 0ustar rootroot# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Linux Module Utilities" INSERT config MODPROBE_SMALL bool "Simplified modutils" default y select PLATFORM_LINUX help Simplified modutils. With this option modprobe does not require modules.dep file and does not use /etc/modules.conf file. It scans module files in /lib/modules/`uname -r` and determines dependencies and module alias names on the fly. This may make module loading slower, most notably when one needs to load module by alias (this requires scanning through module _bodies_). At the first attempt to load a module by alias modprobe will try to generate modules.dep.bb file in order to speed up future loads by alias. Failure to do so (read-only /lib/modules, etc) is not reported, and future modprobes will be slow too. NB: modules.dep.bb file format is not compatible with modules.dep file as created/used by standard module tools. Additional module parameters can be stored in /etc/modules/$module_name files. Apart from modprobe, other utilities are also provided: - insmod is an alias to modprobe - rmmod is an alias to modprobe -r - depmod generates modules.dep.bb As of 2008-07, this code is experimental. It is 14kb smaller than "non-small" modutils. config FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE bool "Accept module options on modprobe command line" default y depends on MODPROBE_SMALL select PLATFORM_LINUX help Allow insmod and modprobe take module options from command line. config FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED bool "Skip loading of already loaded modules" default y depends on MODPROBE_SMALL help Check if the module is already loaded. config INSMOD bool "insmod" default n depends on !MODPROBE_SMALL select PLATFORM_LINUX help insmod is used to load specified modules in the running kernel. config RMMOD bool "rmmod" default n depends on !MODPROBE_SMALL select PLATFORM_LINUX help rmmod is used to unload specified modules from the kernel. config LSMOD bool "lsmod" default n depends on !MODPROBE_SMALL select PLATFORM_LINUX help lsmod is used to display a list of loaded modules. config FEATURE_LSMOD_PRETTY_2_6_OUTPUT bool "Pretty output" default n depends on LSMOD select PLATFORM_LINUX help This option makes output format of lsmod adjusted to the format of module-init-tools for Linux kernel 2.6. Increases size somewhat. config MODPROBE bool "modprobe" default n depends on !MODPROBE_SMALL select PLATFORM_LINUX help Handle the loading of modules, and their dependencies on a high level. config FEATURE_MODPROBE_BLACKLIST bool "Blacklist support" default n depends on MODPROBE select PLATFORM_LINUX help Say 'y' here to enable support for the 'blacklist' command in modprobe.conf. This prevents the alias resolver to resolve blacklisted modules. This is useful if you want to prevent your hardware autodetection scripts to load modules like evdev, frame buffer drivers etc. config DEPMOD bool "depmod" default n depends on !MODPROBE_SMALL select PLATFORM_LINUX help depmod generates modules.dep (and potentially modules.alias and modules.symbols) that contain dependency information for modprobe. comment "Options common to multiple modutils" config FEATURE_2_4_MODULES bool "Support version 2.2/2.4 Linux kernels" default n depends on INSMOD || RMMOD || LSMOD select PLATFORM_LINUX help Support module loading for 2.2.x and 2.4.x Linux kernels. This increases size considerably. Say N unless you plan to run ancient kernels. config FEATURE_INSMOD_TRY_MMAP bool "Try to load module from a mmap'ed area" default n depends on INSMOD || MODPROBE_SMALL select PLATFORM_LINUX help This option causes module loading code to try to mmap module first. If it does not work (for example, it does not work for compressed modules), module will be read (and unpacked if needed) into a memory block allocated by malloc. The only case when mmap works but malloc does not is when you are trying to load a big module on a very memory-constrained machine. Malloc will momentarily need 2x as much memory as mmap. Choosing N saves about 250 bytes of code (on 32-bit x86). config FEATURE_INSMOD_VERSION_CHECKING bool "Enable module version checking" default n depends on FEATURE_2_4_MODULES && (INSMOD || MODPROBE) select PLATFORM_LINUX help Support checking of versions for modules. This is used to ensure that the kernel and module are made for each other. config FEATURE_INSMOD_KSYMOOPS_SYMBOLS bool "Add module symbols to kernel symbol table" default n depends on FEATURE_2_4_MODULES && (INSMOD || MODPROBE) select PLATFORM_LINUX help By adding module symbols to the kernel symbol table, Oops messages occuring within kernel modules can be properly debugged. By enabling this feature, module symbols will always be added to the kernel symbol table for proper debugging support. If you are not interested in Oops messages from kernel modules, say N. config FEATURE_INSMOD_LOADINKMEM bool "In kernel memory optimization (uClinux only)" default n depends on FEATURE_2_4_MODULES && (INSMOD || MODPROBE) select PLATFORM_LINUX help This is a special uClinux only memory optimization that lets insmod load the specified kernel module directly into kernel space, reducing memory usage by preventing the need for two copies of the module being loaded into memory. config FEATURE_INSMOD_LOAD_MAP bool "Enable insmod load map (-m) option" default n depends on FEATURE_2_4_MODULES && INSMOD select PLATFORM_LINUX help Enabling this, one would be able to get a load map output on stdout. This makes kernel module debugging easier. If you don't plan to debug kernel modules, you don't need this option. config FEATURE_INSMOD_LOAD_MAP_FULL bool "Symbols in load map" default y depends on FEATURE_INSMOD_LOAD_MAP && !MODPROBE_SMALL select PLATFORM_LINUX help Without this option, -m will only output section load map. With this option, -m will also output symbols load map. config FEATURE_CHECK_TAINTED_MODULE bool "Support tainted module checking with new kernels" default y depends on (LSMOD || FEATURE_2_4_MODULES) && !MODPROBE_SMALL select PLATFORM_LINUX help Support checking for tainted modules. These are usually binary only modules that will make the linux-kernel list ignore your support request. This option is required to support GPLONLY modules. config FEATURE_MODUTILS_ALIAS bool "Support for module.aliases file" default y depends on DEPMOD || MODPROBE select PLATFORM_LINUX help Generate and parse modules.alias containing aliases for bus identifiers: alias pcmcia:m*c*f03fn*pfn*pa*pb*pc*pd* parport_cs and aliases for logical modules names e.g.: alias padlock_aes aes alias aes_i586 aes alias aes_generic aes Say Y if unsure. config FEATURE_MODUTILS_SYMBOLS bool "Support for module.symbols file" default y depends on DEPMOD || MODPROBE select PLATFORM_LINUX help Generate and parse modules.symbols containing aliases for symbol_request() kernel calls, such as: alias symbol:usb_sg_init usbcore Say Y if unsure. config DEFAULT_MODULES_DIR string "Default directory containing modules" default "/lib/modules" depends on DEPMOD || MODPROBE || MODPROBE_SMALL || MODINFO help Directory that contains kernel modules. Defaults to "/lib/modules" config DEFAULT_DEPMOD_FILE string "Default name of modules.dep" default "modules.dep" depends on DEPMOD || MODPROBE || MODPROBE_SMALL || MODINFO help Filename that contains kernel modules dependencies. Defaults to "modules.dep" endmenu busybox-1.22.1/modutils/lsmod.c0000644000000000000000000000625412263563520015123 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini lsmod implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * Copyright (C) 2008 by Vladimir Dronnikov * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //applet:IF_LSMOD(APPLET(lsmod, BB_DIR_SBIN, BB_SUID_DROP)) //usage:#if !ENABLE_MODPROBE_SMALL //usage:#define lsmod_trivial_usage //usage: "" //usage:#define lsmod_full_usage "\n\n" //usage: "List the currently loaded kernel modules" //usage:#endif #include "libbb.h" #include "unicode.h" #if ENABLE_FEATURE_CHECK_TAINTED_MODULE enum { TAINT_PROPRIETORY_MODULE = (1 << 0), TAINT_FORCED_MODULE = (1 << 1), TAINT_UNSAFE_SMP = (1 << 2), }; static void check_tainted(void) { int tainted = 0; char *buf = xmalloc_open_read_close("/proc/sys/kernel/tainted", NULL); if (buf) { tainted = atoi(buf); if (ENABLE_FEATURE_CLEAN_UP) free(buf); } if (tainted) { printf(" Tainted: %c%c%c\n", tainted & TAINT_PROPRIETORY_MODULE ? 'P' : 'G', tainted & TAINT_FORCED_MODULE ? 'F' : ' ', tainted & TAINT_UNSAFE_SMP ? 'S' : ' '); } else { puts(" Not tainted"); } } #else static void check_tainted(void) { putchar('\n'); } #endif int lsmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int lsmod_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { #if ENABLE_FEATURE_LSMOD_PRETTY_2_6_OUTPUT char *token[4]; parser_t *parser = config_open("/proc/modules"); init_unicode(); printf("%-24sSize Used by", "Module"); check_tainted(); if (ENABLE_FEATURE_2_4_MODULES && get_linux_version_code() < KERNEL_VERSION(2,6,0) ) { while (config_read(parser, token, 4, 3, "# \t", PARSE_NORMAL)) { if (token[3] != NULL && token[3][0] == '[') { token[3]++; token[3][strlen(token[3])-1] = '\0'; } else token[3] = (char *) ""; # if ENABLE_UNICODE_SUPPORT { uni_stat_t uni_stat; char *uni_name = unicode_conv_to_printable(&uni_stat, token[0]); unsigned pad_len = (uni_stat.unicode_width > 19) ? 0 : 19 - uni_stat.unicode_width; printf("%s%*s %8s %2s %s\n", uni_name, pad_len, "", token[1], token[2], token[3]); free(uni_name); } # else printf("%-19s %8s %2s %s\n", token[0], token[1], token[2], token[3]); # endif } } else { while (config_read(parser, token, 4, 4, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) { // N.B. token[3] is either '-' (module is not used by others) // or comma-separated list ended by comma // so trimming the trailing char is just what we need! if (token[3][0]) token[3][strlen(token[3]) - 1] = '\0'; # if ENABLE_UNICODE_SUPPORT { uni_stat_t uni_stat; char *uni_name = unicode_conv_to_printable(&uni_stat, token[0]); unsigned pad_len = (uni_stat.unicode_width > 19) ? 0 : 19 - uni_stat.unicode_width; printf("%s%*s %8s %2s %s\n", uni_name, pad_len, "", token[1], token[2], token[3]); free(uni_name); } # else printf("%-19s %8s %2s %s\n", token[0], token[1], token[2], token[3]); # endif } } if (ENABLE_FEATURE_CLEAN_UP) config_close(parser); #else check_tainted(); xprint_and_close_file(xfopen_for_read("/proc/modules")); #endif return EXIT_SUCCESS; } busybox-1.22.1/modutils/depmod.c0000644000000000000000000001677312261105270015254 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * depmod - generate modules.dep * Copyright (c) 2008 Bernhard Reutner-Fischer * Copyrihgt (c) 2008 Timo Teras * Copyright (c) 2008 Vladimir Dronnikov * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //applet:IF_DEPMOD(APPLET(depmod, BB_DIR_SBIN, BB_SUID_DROP)) #include "libbb.h" #include "modutils.h" #include /* uname() */ /* * Theory of operation: * - iterate over all modules and record their full path * - iterate over all modules looking for "depends=" entries * for each depends, look through our list of full paths and emit if found */ typedef struct module_info { struct module_info *next; char *name, *modname; llist_t *dependencies; llist_t *aliases; llist_t *symbols; struct module_info *dnext, *dprev; } module_info; static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARAM, void *data, int depth UNUSED_PARAM) { char modname[MODULE_NAME_LEN]; module_info **first = (module_info **) data; char *image, *ptr; module_info *info; /* Arbitrary. Was sb->st_size, but that breaks .gz etc */ size_t len = (64*1024*1024 - 4096); if (strrstr(fname, ".ko") == NULL) return TRUE; image = xmalloc_open_zipped_read_close(fname, &len); info = xzalloc(sizeof(*info)); info->next = *first; *first = info; info->dnext = info->dprev = info; info->name = xstrdup(fname + 2); /* skip "./" */ info->modname = xstrdup(filename2modname(fname, modname)); for (ptr = image; ptr < image + len - 10; ptr++) { if (strncmp(ptr, "depends=", 8) == 0) { char *u; ptr += 8; for (u = ptr; *u; u++) if (*u == '-') *u = '_'; ptr += string_to_llist(ptr, &info->dependencies, ","); } else if (ENABLE_FEATURE_MODUTILS_ALIAS && strncmp(ptr, "alias=", 6) == 0 ) { llist_add_to(&info->aliases, xstrdup(ptr + 6)); ptr += strlen(ptr); } else if (ENABLE_FEATURE_MODUTILS_SYMBOLS && strncmp(ptr, "__ksymtab_", 10) == 0 ) { ptr += 10; if (strncmp(ptr, "gpl", 3) == 0 || strcmp(ptr, "strings") == 0 ) { continue; } llist_add_to(&info->symbols, xstrdup(ptr)); ptr += strlen(ptr); } } free(image); return TRUE; } static module_info *find_module(module_info *modules, const char *modname) { module_info *m; for (m = modules; m != NULL; m = m->next) if (strcmp(m->modname, modname) == 0) return m; return NULL; } static void order_dep_list(module_info *modules, module_info *start, llist_t *add) { module_info *m; llist_t *n; for (n = add; n != NULL; n = n->link) { m = find_module(modules, n->data); if (m == NULL) continue; /* unlink current entry */ m->dnext->dprev = m->dprev; m->dprev->dnext = m->dnext; /* and add it to tail */ m->dnext = start; m->dprev = start->dprev; start->dprev->dnext = m; start->dprev = m; /* recurse */ order_dep_list(modules, start, m->dependencies); } } static void xfreopen_write(const char *file, FILE *f) { if (freopen(file, "w", f) == NULL) bb_perror_msg_and_die("can't open '%s'", file); } //usage:#if !ENABLE_MODPROBE_SMALL //usage:#define depmod_trivial_usage "[-n] [-b BASE] [VERSION] [MODFILES]..." //usage:#define depmod_full_usage "\n\n" //usage: "Generate modules.dep, alias, and symbols files" //usage: "\n" //usage: "\n -b BASE Use BASE/lib/modules/VERSION" //usage: "\n -n Dry run: print files to stdout" //usage:#endif /* Upstream usage: * [-aAenv] [-C FILE or DIR] [-b BASE] [-F System.map] [VERSION] [MODFILES]... * -a --all * Probe all modules. Default if no MODFILES. * -A --quick * Check modules.dep's mtime against module files' mtimes. * -b --basedir BASE * Use $BASE/lib/modules/VERSION * -C --config FILE or DIR * Path to /etc/depmod.conf or /etc/depmod.d/ * -e --errsyms * When combined with the -F option, this reports any symbols * which are not supplied by other modules or kernel. * -F --filesyms System.map * -n --dry-run * Print modules.dep etc to standard output * -v --verbose * Print to stdout all the symbols each module depends on * and the module's file name which provides that symbol. * -r No-op * -u No-op * -q No-op * * So far we only support: [-n] [-b BASE] [VERSION] [MODFILES]... * Accepted but ignored: * -aAe * -F System.map * -C FILE/DIR * * Not accepted: -v */ enum { //OPT_a = (1 << 0), /* All modules, ignore mods in argv */ //OPT_A = (1 << 1), /* Only emit .ko that are newer than modules.dep file */ OPT_b = (1 << 2), /* base directory when modules are in staging area */ //OPT_e = (1 << 3), /* with -F, print unresolved symbols */ //OPT_F = (1 << 4), /* System.map that contains the symbols */ OPT_n = (1 << 5), /* dry-run, print to stdout only */ OPT_r = (1 << 6), /* Compat dummy. Linux Makefile uses it */ OPT_u = (1 << 7), /* -u,--unresolved-error: ignored */ OPT_q = (1 << 8), /* -q,--quiet: ignored */ OPT_C = (1 << 9), /* -C,--config etc_modules_conf: ignored */ }; int depmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int depmod_main(int argc UNUSED_PARAM, char **argv) { module_info *modules, *m, *dep; const char *moddir_base = "/"; char *moddir, *version; struct utsname uts; int tmp; getopt32(argv, "aAb:eF:nruqC:", &moddir_base, NULL, NULL); argv += optind; /* goto modules location */ xchdir(moddir_base); /* If a version is provided, then that kernel version's module directory * is used, rather than the current kernel version (as returned by * "uname -r"). */ if (*argv && sscanf(*argv, "%u.%u.%u", &tmp, &tmp, &tmp) == 3) { version = *argv++; } else { uname(&uts); version = uts.release; } moddir = concat_path_file(&CONFIG_DEFAULT_MODULES_DIR[1], version); xchdir(moddir); if (ENABLE_FEATURE_CLEAN_UP) free(moddir); /* Scan modules */ modules = NULL; if (*argv) { do { parse_module(*argv, /*sb (unused):*/ NULL, &modules, 0); } while (*++argv); } else { recursive_action(".", ACTION_RECURSE | ACTION_FOLLOWLINKS, parse_module, NULL, &modules, 0); } /* Generate dependency and alias files */ if (!(option_mask32 & OPT_n)) xfreopen_write(CONFIG_DEFAULT_DEPMOD_FILE, stdout); for (m = modules; m != NULL; m = m->next) { printf("%s:", m->name); order_dep_list(modules, m, m->dependencies); while (m->dnext != m) { dep = m->dnext; printf(" %s", dep->name); /* unlink current entry */ dep->dnext->dprev = dep->dprev; dep->dprev->dnext = dep->dnext; dep->dnext = dep->dprev = dep; } bb_putchar('\n'); } #if ENABLE_FEATURE_MODUTILS_ALIAS if (!(option_mask32 & OPT_n)) xfreopen_write("modules.alias", stdout); for (m = modules; m != NULL; m = m->next) { const char *fname = bb_basename(m->name); int fnlen = strchrnul(fname, '.') - fname; while (m->aliases) { /* Last word can well be m->modname instead, * but depmod from module-init-tools 3.4 * uses module basename, i.e., no s/-/_/g. * (pathname and .ko.* are still stripped) * Mimicking that... */ printf("alias %s %.*s\n", (char*)llist_pop(&m->aliases), fnlen, fname); } } #endif #if ENABLE_FEATURE_MODUTILS_SYMBOLS if (!(option_mask32 & OPT_n)) xfreopen_write("modules.symbols", stdout); for (m = modules; m != NULL; m = m->next) { const char *fname = bb_basename(m->name); int fnlen = strchrnul(fname, '.') - fname; while (m->symbols) { printf("alias symbol:%s %.*s\n", (char*)llist_pop(&m->symbols), fnlen, fname); } } #endif if (ENABLE_FEATURE_CLEAN_UP) { while (modules) { module_info *old = modules; modules = modules->next; free(old->name); free(old->modname); free(old); } } return EXIT_SUCCESS; } busybox-1.22.1/modutils/Kbuild.src0000644000000000000000000000106312263563520015555 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT lib-$(CONFIG_MODPROBE_SMALL) += modprobe-small.o lib-$(CONFIG_DEPMOD) += depmod.o modutils.o lib-$(CONFIG_INSMOD) += insmod.o modutils.o lib-$(CONFIG_LSMOD) += lsmod.o modutils.o lib-$(CONFIG_MODPROBE) += modprobe.o modutils.o lib-$(CONFIG_RMMOD) += rmmod.o modutils.o lib-$(CONFIG_FEATURE_2_4_MODULES) += modutils-24.o busybox-1.22.1/modutils/modprobe.c0000644000000000000000000004634512263563520015621 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Modprobe written from scratch for BusyBox * * Copyright (c) 2008 Timo Teras * Copyright (c) 2008 Vladimir Dronnikov * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //applet:IF_MODPROBE(APPLET(modprobe, BB_DIR_SBIN, BB_SUID_DROP)) #include "libbb.h" #include "modutils.h" #include #include //#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__) #define DBG(...) ((void)0) /* Note that unlike older versions of modules.dep/depmod (busybox and m-i-t), * we expect the full dependency list to be specified in modules.dep. * Older versions would only export the direct dependency list. */ //usage:#if !ENABLE_MODPROBE_SMALL //usage:#define modprobe_notes_usage //usage: "modprobe can (un)load a stack of modules, passing each module options (when\n" //usage: "loading). modprobe uses a configuration file to determine what option(s) to\n" //usage: "pass each module it loads.\n" //usage: "\n" //usage: "The configuration file is searched (in this order):\n" //usage: "\n" //usage: " /etc/modprobe.conf (2.6 only)\n" //usage: " /etc/modules.conf\n" //usage: " /etc/conf.modules (deprecated)\n" //usage: "\n" //usage: "They all have the same syntax (see below). If none is present, it is\n" //usage: "_not_ an error; each loaded module is then expected to load without\n" //usage: "options. Once a file is found, the others are tested for.\n" //usage: "\n" //usage: "/etc/modules.conf entry format:\n" //usage: "\n" //usage: " alias \n" //usage: " Makes it possible to modprobe alias_name, when there is no such module.\n" //usage: " It makes sense if your mod_name is long, or you want a more representative\n" //usage: " name for that module (eg. 'scsi' in place of 'aha7xxx').\n" //usage: " This makes it also possible to use a different set of options (below) for\n" //usage: " the module and the alias.\n" //usage: " A module can be aliased more than once.\n" //usage: "\n" //usage: " options \n" //usage: " When loading module mod_name (or the module aliased by alias_name), pass\n" //usage: " the \"symbol=value\" pairs as option to that module.\n" //usage: "\n" //usage: "Sample /etc/modules.conf file:\n" //usage: "\n" //usage: " options tulip irq=3\n" //usage: " alias tulip tulip2\n" //usage: " options tulip2 irq=4 io=0x308\n" //usage: "\n" //usage: "Other functionality offered by 'classic' modprobe is not available in\n" //usage: "this implementation.\n" //usage: "\n" //usage: "If module options are present both in the config file, and on the command line,\n" //usage: "then the options from the command line will be passed to the module _after_\n" //usage: "the options from the config file. That way, you can have defaults in the config\n" //usage: "file, and override them for a specific usage from the command line.\n" //usage:#define modprobe_example_usage //usage: "(with the above /etc/modules.conf):\n\n" //usage: "$ modprobe tulip\n" //usage: " will load the module 'tulip' with default option 'irq=3'\n\n" //usage: "$ modprobe tulip irq=5\n" //usage: " will load the module 'tulip' with option 'irq=5', thus overriding the default\n\n" //usage: "$ modprobe tulip2\n" //usage: " will load the module 'tulip' with default options 'irq=4 io=0x308',\n" //usage: " which are the default for alias 'tulip2'\n\n" //usage: "$ modprobe tulip2 irq=8\n" //usage: " will load the module 'tulip' with default options 'irq=4 io=0x308 irq=8',\n" //usage: " which are the default for alias 'tulip2' overridden by the option 'irq=8'\n\n" //usage: " from the command line\n\n" //usage: "$ modprobe tulip2 irq=2 io=0x210\n" //usage: " will load the module 'tulip' with default options 'irq=4 io=0x308 irq=4 io=0x210',\n" //usage: " which are the default for alias 'tulip2' overridden by the options 'irq=2 io=0x210'\n\n" //usage: " from the command line\n" //usage: //usage:#define modprobe_trivial_usage //usage: "[-alrqvsD" IF_FEATURE_MODPROBE_BLACKLIST("b") "]" //usage: " MODULE [symbol=value]..." //usage:#define modprobe_full_usage "\n\n" //usage: " -a Load multiple MODULEs" //usage: "\n -l List (MODULE is a pattern)" //usage: "\n -r Remove MODULE (stacks) or do autoclean" //usage: "\n -q Quiet" //usage: "\n -v Verbose" //usage: "\n -s Log to syslog" //usage: "\n -D Show dependencies" //usage: IF_FEATURE_MODPROBE_BLACKLIST( //usage: "\n -b Apply blacklist to module names too" //usage: ) //usage:#endif /* !ENABLE_MODPROBE_SMALL */ /* Note: usage text doesn't document various 2.4 options * we pull in through INSMOD_OPTS define * Note2: -b is always accepted, but if !FEATURE_MODPROBE_BLACKLIST, * it is a no-op. */ #define MODPROBE_OPTS "alrDb" /* -a and -D _are_ in fact compatible */ #define MODPROBE_COMPLEMENTARY ("q-v:v-q:l--arD:r--alD:a--lr:D--rl") //#define MODPROBE_OPTS "acd:lnrt:C:b" //#define MODPROBE_COMPLEMENTARY "q-v:v-q:l--acr:a--lr:r--al" enum { OPT_INSERT_ALL = (INSMOD_OPT_UNUSED << 0), /* a */ //OPT_DUMP_ONLY = (INSMOD_OPT_UNUSED << x), /* c */ //OPT_DIRNAME = (INSMOD_OPT_UNUSED << x), /* d */ OPT_LIST_ONLY = (INSMOD_OPT_UNUSED << 1), /* l */ //OPT_SHOW_ONLY = (INSMOD_OPT_UNUSED << x), /* n */ OPT_REMOVE = (INSMOD_OPT_UNUSED << 2), /* r */ //OPT_RESTRICT = (INSMOD_OPT_UNUSED << x), /* t */ //OPT_VERONLY = (INSMOD_OPT_UNUSED << x), /* V */ //OPT_CONFIGFILE = (INSMOD_OPT_UNUSED << x), /* C */ OPT_SHOW_DEPS = (INSMOD_OPT_UNUSED << 3), /* D */ OPT_BLACKLIST = (INSMOD_OPT_UNUSED << 4) * ENABLE_FEATURE_MODPROBE_BLACKLIST, }; #if ENABLE_LONG_OPTS static const char modprobe_longopts[] ALIGN1 = /* nobody asked for long opts (yet) */ // "all\0" No_argument "a" // "list\0" No_argument "l" // "remove\0" No_argument "r" // "quiet\0" No_argument "q" // "verbose\0" No_argument "v" // "syslog\0" No_argument "s" /* module-init-tools 3.11.1 has only long opt --show-depends * but no short -D, we provide long opt for scripts which * were written for 3.11.1: */ "show-depends\0" No_argument "D" // "use-blacklist\0" No_argument "b" ; #endif #define MODULE_FLAG_LOADED 0x0001 #define MODULE_FLAG_NEED_DEPS 0x0002 /* "was seen in modules.dep": */ #define MODULE_FLAG_FOUND_IN_MODDEP 0x0004 #define MODULE_FLAG_BLACKLISTED 0x0008 struct module_entry { /* I'll call it ME. */ unsigned flags; char *modname; /* stripped of /path/, .ext and s/-/_/g */ const char *probed_name; /* verbatim as seen on cmdline */ char *options; /* options from config files */ llist_t *realnames; /* strings. if this module is an alias, */ /* real module name is one of these. */ //Can there really be more than one? Example from real kernel? llist_t *deps; /* strings. modules we depend on */ }; #define DB_HASH_SIZE 256 struct globals { llist_t *probes; /* MEs of module(s) requested on cmdline */ char *cmdline_mopts; /* module options from cmdline */ int num_unresolved_deps; /* bool. "Did we have 'symbol:FOO' requested on cmdline?" */ smallint need_symbols; struct utsname uts; llist_t *db[DB_HASH_SIZE]; /* MEs of all modules ever seen (caching for speed) */ } FIX_ALIASING; #define G (*ptr_to_globals) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ } while (0) static int read_config(const char *path); static char *gather_options_str(char *opts, const char *append) { /* Speed-optimized. We call gather_options_str many times. */ if (append) { if (opts == NULL) { opts = xstrdup(append); } else { int optlen = strlen(opts); opts = xrealloc(opts, optlen + strlen(append) + 2); sprintf(opts + optlen, " %s", append); } } return opts; } /* These three functions called many times, optimizing for speed. * Users reported minute-long delays when they runn iptables repeatedly * (iptables use modprobe to install needed kernel modules). */ static struct module_entry *helper_get_module(const char *module, int create) { char modname[MODULE_NAME_LEN]; struct module_entry *e; llist_t *l; unsigned i; unsigned hash; filename2modname(module, modname); hash = 0; for (i = 0; modname[i]; i++) hash = ((hash << 5) + hash) + modname[i]; hash %= DB_HASH_SIZE; for (l = G.db[hash]; l; l = l->link) { e = (struct module_entry *) l->data; if (strcmp(e->modname, modname) == 0) return e; } if (!create) return NULL; e = xzalloc(sizeof(*e)); e->modname = xstrdup(modname); llist_add_to(&G.db[hash], e); return e; } static ALWAYS_INLINE struct module_entry *get_or_add_modentry(const char *module) { return helper_get_module(module, 1); } static ALWAYS_INLINE struct module_entry *get_modentry(const char *module) { return helper_get_module(module, 0); } static void add_probe(const char *name) { struct module_entry *m; m = get_or_add_modentry(name); if (!(option_mask32 & (OPT_REMOVE | OPT_SHOW_DEPS)) && (m->flags & MODULE_FLAG_LOADED) ) { DBG("skipping %s, it is already loaded", name); return; } DBG("queuing %s", name); m->probed_name = name; m->flags |= MODULE_FLAG_NEED_DEPS; llist_add_to_end(&G.probes, m); G.num_unresolved_deps++; if (ENABLE_FEATURE_MODUTILS_SYMBOLS && strncmp(m->modname, "symbol:", 7) == 0 ) { G.need_symbols = 1; } } static int FAST_FUNC config_file_action(const char *filename, struct stat *statbuf UNUSED_PARAM, void *userdata UNUSED_PARAM, int depth UNUSED_PARAM) { char *tokens[3]; parser_t *p; struct module_entry *m; int rc = TRUE; if (bb_basename(filename)[0] == '.') goto error; p = config_open2(filename, fopen_for_read); if (p == NULL) { rc = FALSE; goto error; } while (config_read(p, tokens, 3, 2, "# \t", PARSE_NORMAL)) { //Use index_in_strings? if (strcmp(tokens[0], "alias") == 0) { /* alias */ llist_t *l; char wildcard[MODULE_NAME_LEN]; char *rmod; if (tokens[2] == NULL) continue; filename2modname(tokens[1], wildcard); for (l = G.probes; l; l = l->link) { m = (struct module_entry *) l->data; if (fnmatch(wildcard, m->modname, 0) != 0) continue; rmod = filename2modname(tokens[2], NULL); llist_add_to(&m->realnames, rmod); if (m->flags & MODULE_FLAG_NEED_DEPS) { m->flags &= ~MODULE_FLAG_NEED_DEPS; G.num_unresolved_deps--; } m = get_or_add_modentry(rmod); if (!(m->flags & MODULE_FLAG_NEED_DEPS)) { m->flags |= MODULE_FLAG_NEED_DEPS; G.num_unresolved_deps++; } } } else if (strcmp(tokens[0], "options") == 0) { /* options */ if (tokens[2] == NULL) continue; m = get_or_add_modentry(tokens[1]); m->options = gather_options_str(m->options, tokens[2]); } else if (strcmp(tokens[0], "include") == 0) { /* include */ read_config(tokens[1]); } else if (ENABLE_FEATURE_MODPROBE_BLACKLIST && strcmp(tokens[0], "blacklist") == 0 ) { /* blacklist */ get_or_add_modentry(tokens[1])->flags |= MODULE_FLAG_BLACKLISTED; } } config_close(p); error: return rc; } static int read_config(const char *path) { return recursive_action(path, ACTION_RECURSE | ACTION_QUIET, config_file_action, NULL, NULL, 1); } static const char *humanly_readable_name(struct module_entry *m) { /* probed_name may be NULL. modname always exists. */ return m->probed_name ? m->probed_name : m->modname; } static char *parse_and_add_kcmdline_module_options(char *options, const char *modulename) { char *kcmdline_buf; char *kcmdline; char *kptr; int len; kcmdline_buf = xmalloc_open_read_close("/proc/cmdline", NULL); if (!kcmdline_buf) return options; kcmdline = kcmdline_buf; len = strlen(modulename); while ((kptr = strsep(&kcmdline, "\n\t ")) != NULL) { if (strncmp(modulename, kptr, len) != 0) continue; kptr += len; if (*kptr != '.') continue; /* It is "modulename.xxxx" */ kptr++; if (strchr(kptr, '=') != NULL) { /* It is "modulename.opt=[val]" */ options = gather_options_str(options, kptr); } } free(kcmdline_buf); return options; } /* Return: similar to bb_init_module: * 0 on success, * -errno on open/read error, * errno on init_module() error */ /* NB: INSMOD_OPT_SILENT bit suppresses ONLY non-existent modules, * not deleted ones (those are still listed in modules.dep). * module-init-tools version 3.4: * # modprobe bogus * FATAL: Module bogus not found. [exitcode 1] * # modprobe -q bogus [silent, exitcode still 1] * but: * # rm kernel/drivers/net/dummy.ko * # modprobe -q dummy * FATAL: Could not open '/lib/modules/xxx/kernel/drivers/net/dummy.ko': No such file or directory * [exitcode 1] */ static int do_modprobe(struct module_entry *m) { int rc, first; if (!(m->flags & MODULE_FLAG_FOUND_IN_MODDEP)) { if (!(option_mask32 & INSMOD_OPT_SILENT)) bb_error_msg("module %s not found in modules.dep", humanly_readable_name(m)); return -ENOENT; } DBG("do_modprob'ing %s", m->modname); if (!(option_mask32 & OPT_REMOVE)) m->deps = llist_rev(m->deps); if (0) { llist_t *l; for (l = m->deps; l; l = l->link) DBG("dep: %s", l->data); } first = 1; rc = 0; while (m->deps) { struct module_entry *m2; char *fn, *options; rc = 0; fn = llist_pop(&m->deps); /* we leak it */ m2 = get_or_add_modentry(fn); if (option_mask32 & OPT_REMOVE) { /* modprobe -r */ if (m2->flags & MODULE_FLAG_LOADED) { rc = bb_delete_module(m2->modname, O_EXCL); if (rc) { if (first) { bb_error_msg("can't unload module %s: %s", humanly_readable_name(m2), moderror(rc)); break; } } else { m2->flags &= ~MODULE_FLAG_LOADED; } } /* do not error out if *deps* fail to unload */ first = 0; continue; } options = m2->options; m2->options = NULL; options = parse_and_add_kcmdline_module_options(options, m2->modname); if (m == m2) options = gather_options_str(options, G.cmdline_mopts); if (option_mask32 & OPT_SHOW_DEPS) { printf(options ? "insmod %s/%s/%s %s\n" : "insmod %s/%s/%s\n", CONFIG_DEFAULT_MODULES_DIR, G.uts.release, fn, options); free(options); continue; } if (m2->flags & MODULE_FLAG_LOADED) { DBG("%s is already loaded, skipping", fn); free(options); continue; } rc = bb_init_module(fn, options); DBG("loaded %s '%s', rc:%d", fn, options, rc); if (rc == EEXIST) rc = 0; free(options); if (rc) { bb_error_msg("can't load module %s (%s): %s", humanly_readable_name(m2), fn, moderror(rc) ); break; } m2->flags |= MODULE_FLAG_LOADED; } return rc; } static void load_modules_dep(void) { struct module_entry *m; char *colon, *tokens[2]; parser_t *p; /* Modprobe does not work at all without modules.dep, * even if the full module name is given. Returning error here * was making us later confuse user with this message: * "module /full/path/to/existing/file/module.ko not found". * It's better to die immediately, with good message. * xfopen_for_read provides that. */ p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read); while (G.num_unresolved_deps && config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL) ) { colon = last_char_is(tokens[0], ':'); if (colon == NULL) continue; *colon = 0; m = get_modentry(tokens[0]); if (m == NULL) continue; /* Optimization... */ if ((m->flags & MODULE_FLAG_LOADED) && !(option_mask32 & (OPT_REMOVE | OPT_SHOW_DEPS)) ) { DBG("skip deps of %s, it's already loaded", tokens[0]); continue; } m->flags |= MODULE_FLAG_FOUND_IN_MODDEP; if ((m->flags & MODULE_FLAG_NEED_DEPS) && (m->deps == NULL)) { G.num_unresolved_deps--; llist_add_to(&m->deps, xstrdup(tokens[0])); if (tokens[1]) string_to_llist(tokens[1], &m->deps, " \t"); } else DBG("skipping dep line"); } config_close(p); } int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int modprobe_main(int argc UNUSED_PARAM, char **argv) { int rc; unsigned opt; struct module_entry *me; INIT_G(); IF_LONG_OPTS(applet_long_options = modprobe_longopts;) opt_complementary = MODPROBE_COMPLEMENTARY; opt = getopt32(argv, INSMOD_OPTS MODPROBE_OPTS INSMOD_ARGS); argv += optind; /* Goto modules location */ xchdir(CONFIG_DEFAULT_MODULES_DIR); uname(&G.uts); xchdir(G.uts.release); if (opt & OPT_LIST_ONLY) { int i; char name[MODULE_NAME_LEN]; char *colon, *tokens[2]; parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read); for (i = 0; argv[i]; i++) replace(argv[i], '-', '_'); while (config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)) { colon = last_char_is(tokens[0], ':'); if (!colon) continue; *colon = '\0'; filename2modname(tokens[0], name); if (!argv[0]) puts(tokens[0]); else { for (i = 0; argv[i]; i++) { if (fnmatch(argv[i], name, 0) == 0) { puts(tokens[0]); } } } } return EXIT_SUCCESS; } /* Yes, for some reason -l ignores -s... */ if (opt & INSMOD_OPT_SYSLOG) logmode = LOGMODE_SYSLOG; if (!argv[0]) { if (opt & OPT_REMOVE) { /* "modprobe -r" (w/o params). * "If name is NULL, all unused modules marked * autoclean will be removed". */ if (bb_delete_module(NULL, O_NONBLOCK | O_EXCL) != 0) bb_perror_msg_and_die("rmmod"); } return EXIT_SUCCESS; } /* Retrieve module names of already loaded modules */ { char *s; parser_t *parser = config_open2("/proc/modules", fopen_for_read); while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) get_or_add_modentry(s)->flags |= MODULE_FLAG_LOADED; config_close(parser); } if (opt & (OPT_INSERT_ALL | OPT_REMOVE)) { /* Each argument is a module name */ do { DBG("adding module %s", *argv); add_probe(*argv++); } while (*argv); } else { /* First argument is module name, rest are parameters */ DBG("probing just module %s", *argv); add_probe(argv[0]); G.cmdline_mopts = parse_cmdline_module_options(argv, /*quote_spaces:*/ 1); } /* Happens if all requested modules are already loaded */ if (G.probes == NULL) return EXIT_SUCCESS; read_config("/etc/modprobe.conf"); read_config("/etc/modprobe.d"); if (ENABLE_FEATURE_MODUTILS_SYMBOLS && G.need_symbols) read_config("modules.symbols"); load_modules_dep(); if (ENABLE_FEATURE_MODUTILS_ALIAS && G.num_unresolved_deps) { read_config("modules.alias"); load_modules_dep(); } rc = 0; while ((me = llist_pop(&G.probes)) != NULL) { if (me->realnames == NULL) { DBG("probing by module name"); /* This is not an alias. Literal names are blacklisted * only if '-b' is given. */ if (!(opt & OPT_BLACKLIST) || !(me->flags & MODULE_FLAG_BLACKLISTED) ) { rc |= do_modprobe(me); } continue; } /* Probe all real names for the alias */ do { char *realname = llist_pop(&me->realnames); struct module_entry *m2; DBG("probing alias %s by realname %s", me->modname, realname); m2 = get_or_add_modentry(realname); if (!(m2->flags & MODULE_FLAG_BLACKLISTED) && (!(m2->flags & MODULE_FLAG_LOADED) || (opt & (OPT_REMOVE | OPT_SHOW_DEPS))) ) { //TODO: we can pass "me" as 2nd param to do_modprobe, //and make do_modprobe emit more meaningful error messages //with alias name included, not just module name alias resolves to. rc |= do_modprobe(m2); } free(realname); } while (me->realnames != NULL); } return (rc != 0); } busybox-1.22.1/modutils/modinfo.c0000644000000000000000000001012012263563520015423 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * modinfo - retrieve module info * Copyright (c) 2008 Pascal Bellard * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //applet:IF_MODINFO(APPLET(modinfo, BB_DIR_SBIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_MODINFO) += modinfo.o modutils.o //config:config MODINFO //config: bool "modinfo" //config: default y //config: select PLATFORM_LINUX //config: help //config: Show information about a Linux Kernel module #include #include /* uname() */ #include "libbb.h" #include "modutils.h" enum { OPT_TAGS = (1 << 12) - 1, /* shortcut count */ OPT_F = (1 << 12), /* field name */ OPT_0 = (1 << 13), /* \0 as separator */ }; struct modinfo_env { char *field; int tags; }; static int display(const char *data, const char *pattern, int flag) { if (flag) { int n = printf("%s:", pattern); while (n++ < 16) bb_putchar(' '); } return printf("%s%c", data, (option_mask32 & OPT_0) ? '\0' : '\n'); } static void modinfo(const char *path, const char *version, const struct modinfo_env *env) { static const char *const shortcuts[] = { "filename", "license", "author", "description", "version", "alias", "srcversion", "depends", "uts_release", "vermagic", "parm", "firmware", }; size_t len; int j, length; char *ptr, *the_module; const char *field = env->field; int tags = env->tags; if (tags & 1) { /* filename */ display(path, shortcuts[0], 1 != tags); } len = MAXINT(ssize_t); the_module = xmalloc_open_zipped_read_close(path, &len); if (!the_module) { if (path[0] == '/') return; /* Newer depmod puts relative paths in modules.dep */ path = xasprintf("%s/%s/%s", CONFIG_DEFAULT_MODULES_DIR, version, path); the_module = xmalloc_open_zipped_read_close(path, &len); free((char*)path); if (!the_module) return; } if (field) tags |= OPT_F; for (j = 1; (1< * Copyright (C) 2008 Timo Teras * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //applet:IF_RMMOD(APPLET(rmmod, BB_DIR_SBIN, BB_SUID_DROP)) //usage:#if !ENABLE_MODPROBE_SMALL //usage:#define rmmod_trivial_usage //usage: "[-wfa] [MODULE]..." //usage:#define rmmod_full_usage "\n\n" //usage: "Unload kernel modules\n" //usage: "\n -w Wait until the module is no longer used" //usage: "\n -f Force unload" //usage: "\n -a Remove all unused modules (recursively)" //usage:#define rmmod_example_usage //usage: "$ rmmod tulip\n" //usage:#endif #include "libbb.h" #include "modutils.h" int rmmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int rmmod_main(int argc UNUSED_PARAM, char **argv) { int n; unsigned flags = O_NONBLOCK | O_EXCL; /* Parse command line. */ n = getopt32(argv, "wfas"); // -s ignored argv += optind; if (n & 1) // --wait flags &= ~O_NONBLOCK; if (n & 2) // --force flags |= O_TRUNC; if (n & 4) { /* Unload _all_ unused modules via NULL delete_module() call */ if (bb_delete_module(NULL, flags) != 0 && errno != EFAULT) bb_perror_msg_and_die("rmmod"); return EXIT_SUCCESS; } if (!*argv) bb_show_usage(); n = ENABLE_FEATURE_2_4_MODULES && get_linux_version_code() < KERNEL_VERSION(2,6,0); while (*argv) { char modname[MODULE_NAME_LEN]; const char *bname; bname = bb_basename(*argv++); if (n) safe_strncpy(modname, bname, MODULE_NAME_LEN); else filename2modname(bname, modname); if (bb_delete_module(modname, flags)) bb_error_msg_and_die("can't unload '%s': %s", modname, moderror(errno)); } return EXIT_SUCCESS; } busybox-1.22.1/modutils/modutils.h0000644000000000000000000000456312263563520015653 0ustar rootroot/* * Common modutils related functions for busybox * * Copyright (C) 2008 by Timo Teras * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #ifndef MODUTILS_H #define MODUTILS_H 1 #include "libbb.h" PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* linux/include/linux/module.h has 64, but this is also used * internally for the maximum alias name length, which can be quite long */ #define MODULE_NAME_LEN 256 void replace(char *s, char what, char with) FAST_FUNC; char *replace_underscores(char *s) FAST_FUNC; int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC; char *filename2modname(const char *filename, char *modname) FAST_FUNC; char *parse_cmdline_module_options(char **argv, int quote_spaces) FAST_FUNC; /* insmod for 2.4 and modprobe's options (insmod 2.6 has no options at all): */ #define INSMOD_OPTS \ "vqs" \ IF_FEATURE_2_4_MODULES("Lfkx" IF_FEATURE_INSMOD_LOAD_MAP("m")) #define INSMOD_ARGS /* (was meant to support -o NAME) , NULL */ enum { INSMOD_OPT_VERBOSE = (1 << 0), INSMOD_OPT_SILENT = (1 << 1), INSMOD_OPT_SYSLOG = (1 << 2), //INSMOD_OPT_OUTPUTNAME = (1 << x) - not supported yet INSMOD_OPT_LOCK = (1 << 3) * ENABLE_FEATURE_2_4_MODULES, INSMOD_OPT_FORCE = (1 << 4) * ENABLE_FEATURE_2_4_MODULES, INSMOD_OPT_KERNELD = (1 << 5) * ENABLE_FEATURE_2_4_MODULES, INSMOD_OPT_NO_EXPORT = (1 << 6) * ENABLE_FEATURE_2_4_MODULES, INSMOD_OPT_PRINT_MAP = (1 << 7) * ENABLE_FEATURE_INSMOD_LOAD_MAP, INSMOD_OPT_UNUSED = (INSMOD_OPT_PRINT_MAP ? INSMOD_OPT_PRINT_MAP : INSMOD_OPT_NO_EXPORT ? INSMOD_OPT_NO_EXPORT : INSMOD_OPT_SYSLOG ) << 1 }; #if ENABLE_FEATURE_INSMOD_TRY_MMAP void* FAST_FUNC try_to_mmap_module(const char *filename, size_t *image_size_p); #else # define try_to_mmap_module(filename, image_size) NULL #endif /* Return: * 0 on success, * -errno on open/read error, * errno on init_module() error */ int FAST_FUNC bb_init_module(const char *module, const char *options); /* Return: * 0 on success, * errno on init_module() error */ int FAST_FUNC bb_delete_module(const char *module, unsigned int flags); /* Translates error return to a string */ const char *moderror(int err) FAST_FUNC; #if ENABLE_FEATURE_2_4_MODULES int FAST_FUNC bb_init_module_24(const char *module, const char *options); #endif POP_SAVED_FUNCTION_VISIBILITY #endif busybox-1.22.1/modutils/modutils.c0000644000000000000000000001120612263563520015636 0ustar rootroot/* * Common modutils related functions for busybox * * Copyright (C) 2008 by Timo Teras * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "modutils.h" #ifdef __UCLIBC__ extern int init_module(void *module, unsigned long len, const char *options); extern int delete_module(const char *module, unsigned int flags); #else # include # define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts) # define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags) #endif void FAST_FUNC replace(char *s, char what, char with) { while (*s) { if (what == *s) *s = with; ++s; } } char* FAST_FUNC replace_underscores(char *s) { replace(s, '-', '_'); return s; } int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim) { char *tok; int len = 0; while ((tok = strsep(&string, delim)) != NULL) { if (tok[0] == '\0') continue; llist_add_to_end(llist, xstrdup(tok)); len += strlen(tok); } return len; } char* FAST_FUNC filename2modname(const char *filename, char *modname) { int i; char *from; if (filename == NULL) return NULL; if (modname == NULL) modname = xmalloc(MODULE_NAME_LEN); from = bb_get_last_path_component_nostrip(filename); for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++) modname[i] = (from[i] == '-') ? '_' : from[i]; modname[i] = '\0'; return modname; } char* FAST_FUNC parse_cmdline_module_options(char **argv, int quote_spaces) { char *options; int optlen; options = xzalloc(1); optlen = 0; while (*++argv) { const char *fmt; const char *var; const char *val; var = *argv; options = xrealloc(options, optlen + 2 + strlen(var) + 2); fmt = "%.*s%s "; val = strchrnul(var, '='); if (quote_spaces) { /* * modprobe (module-init-tools version 3.11.1) compat: * quote only value: * var="val with spaces", not "var=val with spaces" * (note: var *name* is not checked for spaces!) */ if (*val) { /* has var=val format. skip '=' */ val++; if (strchr(val, ' ')) fmt = "%.*s\"%s\" "; } } optlen += sprintf(options + optlen, fmt, (int)(val - var), var, val); } /* Remove trailing space. Disabled */ /* if (optlen != 0) options[optlen-1] = '\0'; */ return options; } #if ENABLE_FEATURE_INSMOD_TRY_MMAP void* FAST_FUNC try_to_mmap_module(const char *filename, size_t *image_size_p) { /* We have user reports of failure to load 3MB module * on a 16MB RAM machine. Apparently even a transient * memory spike to 6MB during module load * is too big for that system. */ void *image; struct stat st; int fd; fd = xopen(filename, O_RDONLY); fstat(fd, &st); image = NULL; /* st.st_size is off_t, we can't just pass it to mmap */ if (st.st_size <= *image_size_p) { size_t image_size = st.st_size; image = mmap(NULL, image_size, PROT_READ, MAP_PRIVATE, fd, 0); if (image == MAP_FAILED) { image = NULL; } else if (*(uint32_t*)image != SWAP_BE32(0x7f454C46)) { /* No ELF signature. Compressed module? */ munmap(image, image_size); image = NULL; } else { /* Success. Report the size */ *image_size_p = image_size; } } close(fd); return image; } #endif /* Return: * 0 on success, * -errno on open/read error, * errno on init_module() error */ int FAST_FUNC bb_init_module(const char *filename, const char *options) { size_t image_size; char *image; int rc; bool mmaped; if (!options) options = ""; //TODO: audit bb_init_module_24 to match error code convention #if ENABLE_FEATURE_2_4_MODULES if (get_linux_version_code() < KERNEL_VERSION(2,6,0)) return bb_init_module_24(filename, options); #endif image_size = INT_MAX - 4095; mmaped = 0; image = try_to_mmap_module(filename, &image_size); if (image) { mmaped = 1; } else { errno = ENOMEM; /* may be changed by e.g. open errors below */ image = xmalloc_open_zipped_read_close(filename, &image_size); if (!image) return -errno; } errno = 0; init_module(image, image_size, options); rc = errno; if (mmaped) munmap(image, image_size); else free(image); return rc; } int FAST_FUNC bb_delete_module(const char *module, unsigned int flags) { errno = 0; delete_module(module, flags); return errno; } const char* FAST_FUNC moderror(int err) { switch (err) { case -1: /* btw: it's -EPERM */ return "no such module"; case ENOEXEC: return "invalid module format"; case ENOENT: return "unknown symbol in module, or unknown parameter"; case ESRCH: return "module has wrong symbol version"; case ENOSYS: return "kernel does not support requested operation"; } if (err < 0) /* should always be */ err = -err; return strerror(err); } busybox-1.22.1/modutils/modutils-24.c0000644000000000000000000027571012263563520016075 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini insmod implementation for busybox * * This version of insmod supports ARM, CRIS, H8/300, x86, ia64, x86_64, * m68k, MIPS, PowerPC, S390, SH3/4/5, Sparc, v850e, and x86_64. * * Copyright (C) 1999-2004 by Erik Andersen * and Ron Alder * * Rodney Radford 17-Aug-2004. * Added x86_64 support. * * Miles Bader added NEC V850E support. * * Modified by Bryan Rittmeyer to support SH4 * and (theoretically) SH3. I have only tested SH4 in little endian mode. * * Modified by Alcove, Julien Gaulmin and * Nicolas Ferre to support ARM7TDMI. Only * very minor changes required to also work with StrongArm and presumably * all ARM based systems. * * Yoshinori Sato 19-May-2004. * added Renesas H8/300 support. * * Paul Mundt 08-Aug-2003. * Integrated support for sh64 (SH-5), from preliminary modutils * patches from Benedict Gaster . * Currently limited to support for 32bit ABI. * * Magnus Damm 22-May-2002. * The plt and got code are now using the same structs. * Added generic linked list code to fully support PowerPC. * Replaced the mess in arch_apply_relocation() with architecture blocks. * The arch_create_got() function got cleaned up with architecture blocks. * These blocks should be easy maintain and sync with obj_xxx.c in modutils. * * Magnus Damm added PowerPC support 20-Feb-2001. * PowerPC specific code stolen from modutils-2.3.16, * written by Paul Mackerras, Copyright 1996, 1997 Linux International. * I've only tested the code on mpc8xx platforms in big-endian mode. * Did some cleanup and added USE_xxx_ENTRIES... * * Quinn Jensen added MIPS support 23-Feb-2001. * based on modutils-2.4.2 * MIPS specific support for Elf loading and relocation. * Copyright 1996, 1997 Linux International. * Contributed by Ralf Baechle * * Based almost entirely on the Linux modutils-2.3.11 implementation. * Copyright 1996, 1997 Linux International. * New implementation contributed by Richard Henderson * Based on original work by Bjorn Ekwall * Restructured (and partly rewritten) by: * Björn Ekwall February 1999 * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" #include "modutils.h" #include #if ENABLE_FEATURE_INSMOD_LOADINKMEM #define LOADBITS 0 #else #define LOADBITS 1 #endif /* Alpha */ #if defined(__alpha__) #define MATCH_MACHINE(x) (x == EM_ALPHA) #define SHT_RELM SHT_RELA #define Elf64_RelM Elf64_Rela #define ELFCLASSM ELFCLASS64 #endif /* ARM support */ #if defined(__arm__) #define MATCH_MACHINE(x) (x == EM_ARM) #define SHT_RELM SHT_REL #define Elf32_RelM Elf32_Rel #define ELFCLASSM ELFCLASS32 #define USE_PLT_ENTRIES #define PLT_ENTRY_SIZE 8 #define USE_GOT_ENTRIES #define GOT_ENTRY_SIZE 8 #define USE_SINGLE #endif /* NDS32 support */ #if defined(__nds32__) || defined(__NDS32__) #define CONFIG_USE_GOT_ENTRIES #define CONFIG_GOT_ENTRY_SIZE 4 #define CONFIG_USE_SINGLE #if defined(__NDS32_EB__) #define MATCH_MACHINE(x) (x == EM_NDS32) #define SHT_RELM SHT_RELA #define Elf32_RelM Elf32_Rela #define ELFCLASSM ELFCLASS32 #endif #if defined(__NDS32_EL__) #define MATCH_MACHINE(x) (x == EM_NDS32) #define SHT_RELM SHT_RELA #define Elf32_RelM Elf32_Rela #define ELFCLASSM ELFCLASS32 #endif #endif /* blackfin */ #if defined(BFIN) #define MATCH_MACHINE(x) (x == EM_BLACKFIN) #define SHT_RELM SHT_RELA #define Elf32_RelM Elf32_Rela #define ELFCLASSM ELFCLASS32 #endif /* CRIS */ #if defined(__cris__) #define MATCH_MACHINE(x) (x == EM_CRIS) #define SHT_RELM SHT_RELA #define Elf32_RelM Elf32_Rela #define ELFCLASSM ELFCLASS32 #ifndef EM_CRIS #define EM_CRIS 76 #define R_CRIS_NONE 0 #define R_CRIS_32 3 #endif #endif /* H8/300 */ #if defined(__H8300H__) || defined(__H8300S__) #define MATCH_MACHINE(x) (x == EM_H8_300) #define SHT_RELM SHT_RELA #define Elf32_RelM Elf32_Rela #define ELFCLASSM ELFCLASS32 #define USE_SINGLE #define SYMBOL_PREFIX "_" #endif /* PA-RISC / HP-PA */ #if defined(__hppa__) #define MATCH_MACHINE(x) (x == EM_PARISC) #define SHT_RELM SHT_RELA #if defined(__LP64__) #define Elf64_RelM Elf64_Rela #define ELFCLASSM ELFCLASS64 #else #define Elf32_RelM Elf32_Rela #define ELFCLASSM ELFCLASS32 #endif #endif /* x86 */ #if defined(__i386__) #ifndef EM_486 #define MATCH_MACHINE(x) (x == EM_386) #else #define MATCH_MACHINE(x) (x == EM_386 || x == EM_486) #endif #define SHT_RELM SHT_REL #define Elf32_RelM Elf32_Rel #define ELFCLASSM ELFCLASS32 #define USE_GOT_ENTRIES #define GOT_ENTRY_SIZE 4 #define USE_SINGLE #endif /* IA64, aka Itanium */ #if defined(__ia64__) #define MATCH_MACHINE(x) (x == EM_IA_64) #define SHT_RELM SHT_RELA #define Elf64_RelM Elf64_Rela #define ELFCLASSM ELFCLASS64 #endif /* m68k */ #if defined(__mc68000__) #define MATCH_MACHINE(x) (x == EM_68K) #define SHT_RELM SHT_RELA #define Elf32_RelM Elf32_Rela #define ELFCLASSM ELFCLASS32 #define USE_GOT_ENTRIES #define GOT_ENTRY_SIZE 4 #define USE_SINGLE #endif /* Microblaze */ #if defined(__microblaze__) #define USE_SINGLE #include #define MATCH_MACHINE(x) (x == EM_XILINX_MICROBLAZE) #define SHT_RELM SHT_RELA #define Elf32_RelM Elf32_Rela #define ELFCLASSM ELFCLASS32 #endif /* MIPS */ #if defined(__mips__) #define MATCH_MACHINE(x) (x == EM_MIPS || x == EM_MIPS_RS3_LE) #define SHT_RELM SHT_REL #define Elf32_RelM Elf32_Rel #define ELFCLASSM ELFCLASS32 /* Account for ELF spec changes. */ #ifndef EM_MIPS_RS3_LE #ifdef EM_MIPS_RS4_BE #define EM_MIPS_RS3_LE EM_MIPS_RS4_BE #else #define EM_MIPS_RS3_LE 10 #endif #endif /* !EM_MIPS_RS3_LE */ #define ARCHDATAM "__dbe_table" #endif /* Nios II */ #if defined(__nios2__) #define MATCH_MACHINE(x) (x == EM_ALTERA_NIOS2) #define SHT_RELM SHT_RELA #define Elf32_RelM Elf32_Rela #define ELFCLASSM ELFCLASS32 #endif /* PowerPC */ #if defined(__powerpc64__) #define MATCH_MACHINE(x) (x == EM_PPC64) #define SHT_RELM SHT_RELA #define Elf64_RelM Elf64_Rela #define ELFCLASSM ELFCLASS64 #elif defined(__powerpc__) #define MATCH_MACHINE(x) (x == EM_PPC) #define SHT_RELM SHT_RELA #define Elf32_RelM Elf32_Rela #define ELFCLASSM ELFCLASS32 #define USE_PLT_ENTRIES #define PLT_ENTRY_SIZE 16 #define USE_PLT_LIST #define LIST_ARCHTYPE ElfW(Addr) #define USE_LIST #define ARCHDATAM "__ftr_fixup" #endif /* S390 */ #if defined(__s390__) #define MATCH_MACHINE(x) (x == EM_S390) #define SHT_RELM SHT_RELA #define Elf32_RelM Elf32_Rela #define ELFCLASSM ELFCLASS32 #define USE_PLT_ENTRIES #define PLT_ENTRY_SIZE 8 #define USE_GOT_ENTRIES #define GOT_ENTRY_SIZE 8 #define USE_SINGLE #endif /* SuperH */ #if defined(__sh__) #define MATCH_MACHINE(x) (x == EM_SH) #define SHT_RELM SHT_RELA #define Elf32_RelM Elf32_Rela #define ELFCLASSM ELFCLASS32 #define USE_GOT_ENTRIES #define GOT_ENTRY_SIZE 4 #define USE_SINGLE /* the SH changes have only been tested in =little endian= mode */ /* I'm not sure about big endian, so let's warn: */ #if defined(__sh__) && BB_BIG_ENDIAN # error insmod.c may require changes for use on big endian SH #endif /* it may or may not work on the SH1/SH2... Error on those also */ #if ((!(defined(__SH3__) || defined(__SH4__) || defined(__SH5__)))) && (defined(__sh__)) #error insmod.c may require changes for SH1 or SH2 use #endif #endif /* Sparc */ #if defined(__sparc__) #define MATCH_MACHINE(x) (x == EM_SPARC) #define SHT_RELM SHT_RELA #define Elf32_RelM Elf32_Rela #define ELFCLASSM ELFCLASS32 #endif /* v850e */ #if defined(__v850e__) #define MATCH_MACHINE(x) ((x) == EM_V850 || (x) == EM_CYGNUS_V850) #define SHT_RELM SHT_RELA #define Elf32_RelM Elf32_Rela #define ELFCLASSM ELFCLASS32 #define USE_PLT_ENTRIES #define PLT_ENTRY_SIZE 8 #define USE_SINGLE #ifndef EM_CYGNUS_V850 /* grumble */ #define EM_CYGNUS_V850 0x9080 #endif #define SYMBOL_PREFIX "_" #endif /* X86_64 */ #if defined(__x86_64__) #define MATCH_MACHINE(x) (x == EM_X86_64) #define SHT_RELM SHT_RELA #define USE_GOT_ENTRIES #define GOT_ENTRY_SIZE 8 #define USE_SINGLE #define Elf64_RelM Elf64_Rela #define ELFCLASSM ELFCLASS64 #endif #ifndef SHT_RELM #error Sorry, but insmod.c does not yet support this architecture... #endif //---------------------------------------------------------------------------- //--------modutils module.h, lines 45-242 //---------------------------------------------------------------------------- /* Definitions for the Linux module syscall interface. Copyright 1996, 1997 Linux International. Contributed by Richard Henderson This file is part of the Linux modutils. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef MODUTILS_MODULE_H /*======================================================================*/ /* For sizeof() which are related to the module platform and not to the environment isnmod is running in, use sizeof_xx instead of sizeof(xx). */ #define tgt_sizeof_char sizeof(char) #define tgt_sizeof_short sizeof(short) #define tgt_sizeof_int sizeof(int) #define tgt_sizeof_long sizeof(long) #define tgt_sizeof_char_p sizeof(char *) #define tgt_sizeof_void_p sizeof(void *) #define tgt_long long #if defined(__sparc__) && !defined(__sparc_v9__) && defined(ARCH_sparc64) #undef tgt_sizeof_long #undef tgt_sizeof_char_p #undef tgt_sizeof_void_p #undef tgt_long enum { tgt_sizeof_long = 8, tgt_sizeof_char_p = 8, tgt_sizeof_void_p = 8 }; #define tgt_long long long #endif /*======================================================================*/ /* The structures used in Linux 2.1. */ /* Note: new_module_symbol does not use tgt_long intentionally */ struct new_module_symbol { unsigned long value; unsigned long name; }; struct new_module_persist; struct new_module_ref { unsigned tgt_long dep; /* kernel addresses */ unsigned tgt_long ref; unsigned tgt_long next_ref; }; struct new_module { unsigned tgt_long size_of_struct; /* == sizeof(module) */ unsigned tgt_long next; unsigned tgt_long name; unsigned tgt_long size; tgt_long usecount; unsigned tgt_long flags; /* AUTOCLEAN et al */ unsigned nsyms; unsigned ndeps; unsigned tgt_long syms; unsigned tgt_long deps; unsigned tgt_long refs; unsigned tgt_long init; unsigned tgt_long cleanup; unsigned tgt_long ex_table_start; unsigned tgt_long ex_table_end; #ifdef __alpha__ unsigned tgt_long gp; #endif /* Everything after here is extension. */ unsigned tgt_long persist_start; unsigned tgt_long persist_end; unsigned tgt_long can_unload; unsigned tgt_long runsize; const char *kallsyms_start; /* All symbols for kernel debugging */ const char *kallsyms_end; const char *archdata_start; /* arch specific data for module */ const char *archdata_end; const char *kernel_data; /* Reserved for kernel internal use */ }; #ifdef ARCHDATAM #define ARCHDATA_SEC_NAME ARCHDATAM #else #define ARCHDATA_SEC_NAME "__archdata" #endif #define KALLSYMS_SEC_NAME "__kallsyms" struct new_module_info { unsigned long addr; unsigned long size; unsigned long flags; long usecount; }; /* Bits of module.flags. */ enum { NEW_MOD_RUNNING = 1, NEW_MOD_DELETED = 2, NEW_MOD_AUTOCLEAN = 4, NEW_MOD_VISITED = 8, NEW_MOD_USED_ONCE = 16 }; int init_module(const char *name, const struct new_module *); int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret); /* Values for query_module's which. */ enum { QM_MODULES = 1, QM_DEPS = 2, QM_REFS = 3, QM_SYMBOLS = 4, QM_INFO = 5 }; /*======================================================================*/ /* The system calls unchanged between 2.0 and 2.1. */ unsigned long create_module(const char *, size_t); int delete_module(const char *module, unsigned int flags); #endif /* module.h */ //---------------------------------------------------------------------------- //--------end of modutils module.h //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- //--------modutils obj.h, lines 253-462 //---------------------------------------------------------------------------- /* Elf object file loading and relocation routines. Copyright 1996, 1997 Linux International. Contributed by Richard Henderson This file is part of the Linux modutils. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef MODUTILS_OBJ_H /* The relocatable object is manipulated using elfin types. */ #include #include #ifndef ElfW # if ELFCLASSM == ELFCLASS32 # define ElfW(x) Elf32_ ## x # define ELFW(x) ELF32_ ## x # else # define ElfW(x) Elf64_ ## x # define ELFW(x) ELF64_ ## x # endif #endif /* For some reason this is missing from some ancient C libraries.... */ #ifndef ELF32_ST_INFO # define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) #endif #ifndef ELF64_ST_INFO # define ELF64_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) #endif #define ELF_ST_BIND(info) ELFW(ST_BIND)(info) #define ELF_ST_TYPE(info) ELFW(ST_TYPE)(info) #define ELF_ST_INFO(bind, type) ELFW(ST_INFO)(bind, type) #define ELF_R_TYPE(val) ELFW(R_TYPE)(val) #define ELF_R_SYM(val) ELFW(R_SYM)(val) struct obj_string_patch; struct obj_symbol_patch; struct obj_section { ElfW(Shdr) header; const char *name; char *contents; struct obj_section *load_next; int idx; }; struct obj_symbol { struct obj_symbol *next; /* hash table link */ const char *name; unsigned long value; unsigned long size; int secidx; /* the defining section index/module */ int info; int ksymidx; /* for export to the kernel symtab */ int referenced; /* actually used in the link */ }; /* Hardcode the hash table size. We shouldn't be needing so many symbols that we begin to degrade performance, and we get a big win by giving the compiler a constant divisor. */ #define HASH_BUCKETS 521 struct obj_file { ElfW(Ehdr) header; ElfW(Addr) baseaddr; struct obj_section **sections; struct obj_section *load_order; struct obj_section **load_order_search_start; struct obj_string_patch *string_patches; struct obj_symbol_patch *symbol_patches; int (*symbol_cmp)(const char *, const char *); /* cant be FAST_FUNC */ unsigned long (*symbol_hash)(const char *) FAST_FUNC; unsigned long local_symtab_size; struct obj_symbol **local_symtab; struct obj_symbol *symtab[HASH_BUCKETS]; }; enum obj_reloc { obj_reloc_ok, obj_reloc_overflow, obj_reloc_dangerous, obj_reloc_unhandled }; struct obj_string_patch { struct obj_string_patch *next; int reloc_secidx; ElfW(Addr) reloc_offset; ElfW(Addr) string_offset; }; struct obj_symbol_patch { struct obj_symbol_patch *next; int reloc_secidx; ElfW(Addr) reloc_offset; struct obj_symbol *sym; }; /* Generic object manipulation routines. */ static unsigned long FAST_FUNC obj_elf_hash(const char *); static unsigned long obj_elf_hash_n(const char *, unsigned long len); static struct obj_symbol *obj_find_symbol(struct obj_file *f, const char *name); static ElfW(Addr) obj_symbol_final_value(struct obj_file *f, struct obj_symbol *sym); #if ENABLE_FEATURE_INSMOD_VERSION_CHECKING static void obj_set_symbol_compare(struct obj_file *f, int (*cmp)(const char *, const char *), unsigned long (*hash)(const char *) FAST_FUNC); #endif static struct obj_section *obj_find_section(struct obj_file *f, const char *name); static void obj_insert_section_load_order(struct obj_file *f, struct obj_section *sec); static struct obj_section *obj_create_alloced_section(struct obj_file *f, const char *name, unsigned long align, unsigned long size); static struct obj_section *obj_create_alloced_section_first(struct obj_file *f, const char *name, unsigned long align, unsigned long size); static void *obj_extend_section(struct obj_section *sec, unsigned long more); static void obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, const char *string); static void obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, struct obj_symbol *sym); static void obj_check_undefineds(struct obj_file *f); static void obj_allocate_commons(struct obj_file *f); static unsigned long obj_load_size(struct obj_file *f); static int obj_relocate(struct obj_file *f, ElfW(Addr) base); #if !LOADBITS #define obj_load(image, image_size, loadprogbits) \ obj_load(image, image_size) #endif static struct obj_file *obj_load(char *image, size_t image_size, int loadprogbits); static int obj_create_image(struct obj_file *f, char *image); /* Architecture specific manipulation routines. */ static struct obj_file *arch_new_file(void); static struct obj_section *arch_new_section(void); static struct obj_symbol *arch_new_symbol(void); static enum obj_reloc arch_apply_relocation(struct obj_file *f, struct obj_section *targsec, /*struct obj_section *symsec,*/ struct obj_symbol *sym, ElfW(RelM) *rel, ElfW(Addr) value); static void arch_create_got(struct obj_file *f); #if ENABLE_FEATURE_CHECK_TAINTED_MODULE static int obj_gpl_license(struct obj_file *f, const char **license); #endif #endif /* obj.h */ //---------------------------------------------------------------------------- //--------end of modutils obj.h //---------------------------------------------------------------------------- /* SPFX is always a string, so it can be concatenated to string constants. */ #ifdef SYMBOL_PREFIX #define SPFX SYMBOL_PREFIX #else #define SPFX "" #endif enum { STRVERSIONLEN = 64 }; /*======================================================================*/ #define flag_force_load (option_mask32 & INSMOD_OPT_FORCE) #define flag_autoclean (option_mask32 & INSMOD_OPT_KERNELD) #define flag_verbose (option_mask32 & INSMOD_OPT_VERBOSE) #define flag_quiet (option_mask32 & INSMOD_OPT_SILENT) #define flag_noexport (option_mask32 & INSMOD_OPT_NO_EXPORT) #define flag_print_load_map (option_mask32 & INSMOD_OPT_PRINT_MAP) /*======================================================================*/ #if defined(USE_LIST) struct arch_list_entry { struct arch_list_entry *next; LIST_ARCHTYPE addend; int offset; int inited : 1; }; #endif #if defined(USE_SINGLE) struct arch_single_entry { int offset; int inited : 1; int allocated : 1; }; #endif #if defined(__mips__) struct mips_hi16 { struct mips_hi16 *next; ElfW(Addr) *addr; ElfW(Addr) value; }; #endif struct arch_file { struct obj_file root; #if defined(USE_PLT_ENTRIES) struct obj_section *plt; #endif #if defined(USE_GOT_ENTRIES) struct obj_section *got; #endif #if defined(__mips__) struct mips_hi16 *mips_hi16_list; #endif }; struct arch_symbol { struct obj_symbol root; #if defined(USE_PLT_ENTRIES) #if defined(USE_PLT_LIST) struct arch_list_entry *pltent; #else struct arch_single_entry pltent; #endif #endif #if defined(USE_GOT_ENTRIES) struct arch_single_entry gotent; #endif }; struct external_module { const char *name; ElfW(Addr) addr; int used; size_t nsyms; struct new_module_symbol *syms; }; static struct new_module_symbol *ksyms; static size_t nksyms; static struct external_module *ext_modules; static int n_ext_modules; static int n_ext_modules_used; /*======================================================================*/ static struct obj_file *arch_new_file(void) { struct arch_file *f; f = xzalloc(sizeof(*f)); return &f->root; /* it's a first member */ } static struct obj_section *arch_new_section(void) { return xzalloc(sizeof(struct obj_section)); } static struct obj_symbol *arch_new_symbol(void) { struct arch_symbol *sym; sym = xzalloc(sizeof(*sym)); return &sym->root; } static enum obj_reloc arch_apply_relocation(struct obj_file *f, struct obj_section *targsec, /*struct obj_section *symsec,*/ struct obj_symbol *sym, ElfW(RelM) *rel, ElfW(Addr) v) { #if defined(__arm__) || defined(__i386__) || defined(__mc68000__) \ || defined(__sh__) || defined(__s390__) || defined(__x86_64__) \ || defined(__powerpc__) || defined(__mips__) struct arch_file *ifile = (struct arch_file *) f; #endif enum obj_reloc ret = obj_reloc_ok; ElfW(Addr) *loc = (ElfW(Addr) *) (targsec->contents + rel->r_offset); #if defined(__arm__) || defined(__H8300H__) || defined(__H8300S__) \ || defined(__i386__) || defined(__mc68000__) || defined(__microblaze__) \ || defined(__mips__) || defined(__nios2__) || defined(__powerpc__) \ || defined(__s390__) || defined(__sh__) || defined(__x86_64__) ElfW(Addr) dot = targsec->header.sh_addr + rel->r_offset; #endif #if defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES) struct arch_symbol *isym = (struct arch_symbol *) sym; #endif #if defined(__arm__) || defined(__i386__) || defined(__mc68000__) \ || defined(__sh__) || defined(__s390__) #if defined(USE_GOT_ENTRIES) ElfW(Addr) got = ifile->got ? ifile->got->header.sh_addr : 0; #endif #endif #if defined(USE_PLT_ENTRIES) ElfW(Addr) plt = ifile->plt ? ifile->plt->header.sh_addr : 0; unsigned long *ip; # if defined(USE_PLT_LIST) struct arch_list_entry *pe; # else struct arch_single_entry *pe; # endif #endif switch (ELF_R_TYPE(rel->r_info)) { #if defined(__arm__) case R_ARM_NONE: break; case R_ARM_ABS32: *loc += v; break; case R_ARM_GOT32: goto bb_use_got; case R_ARM_GOTPC: /* relative reloc, always to _GLOBAL_OFFSET_TABLE_ * (which is .got) similar to branch, * but is full 32 bits relative */ *loc += got - dot; break; case R_ARM_PC24: case R_ARM_PLT32: goto bb_use_plt; case R_ARM_GOTOFF: /* address relative to the got */ *loc += v - got; break; #elif defined(__cris__) case R_CRIS_NONE: break; case R_CRIS_32: /* CRIS keeps the relocation value in the r_addend field and * should not use whats in *loc at all */ *loc = v; break; #elif defined(__H8300H__) || defined(__H8300S__) case R_H8_DIR24R8: loc = (ElfW(Addr) *)((ElfW(Addr))loc - 1); *loc = (*loc & 0xff000000) | ((*loc & 0xffffff) + v); break; case R_H8_DIR24A8: *loc += v; break; case R_H8_DIR32: case R_H8_DIR32A16: *loc += v; break; case R_H8_PCREL16: v -= dot + 2; if ((ElfW(Sword))v > 0x7fff || (ElfW(Sword))v < -(ElfW(Sword))0x8000 ) { ret = obj_reloc_overflow; } else { *(unsigned short *)loc = v; } break; case R_H8_PCREL8: v -= dot + 1; if ((ElfW(Sword))v > 0x7f || (ElfW(Sword))v < -(ElfW(Sword))0x80 ) { ret = obj_reloc_overflow; } else { *(unsigned char *)loc = v; } break; #elif defined(__i386__) case R_386_NONE: break; case R_386_32: *loc += v; break; case R_386_PLT32: case R_386_PC32: case R_386_GOTOFF: *loc += v - dot; break; case R_386_GLOB_DAT: case R_386_JMP_SLOT: *loc = v; break; case R_386_RELATIVE: *loc += f->baseaddr; break; case R_386_GOTPC: *loc += got - dot; break; case R_386_GOT32: goto bb_use_got; break; #elif defined(__microblaze__) case R_MICROBLAZE_NONE: case R_MICROBLAZE_64_NONE: case R_MICROBLAZE_32_SYM_OP_SYM: case R_MICROBLAZE_32_PCREL: break; case R_MICROBLAZE_64_PCREL: { /* dot is the address of the current instruction. * v is the target symbol address. * So we need to extract the offset in the code, * adding v, then subtrating the current address * of this instruction. * Ex: "IMM 0xFFFE bralid 0x0000" = "bralid 0xFFFE0000" */ /* Get split offset stored in code */ unsigned int temp = (loc[0] & 0xFFFF) << 16 | (loc[1] & 0xFFFF); /* Adjust relative offset. -4 adjustment required * because dot points to the IMM insn, but branch * is computed relative to the branch instruction itself. */ temp += v - dot - 4; /* Store back into code */ loc[0] = (loc[0] & 0xFFFF0000) | temp >> 16; loc[1] = (loc[1] & 0xFFFF0000) | (temp & 0xFFFF); break; } case R_MICROBLAZE_32: *loc += v; break; case R_MICROBLAZE_64: { /* Get split pointer stored in code */ unsigned int temp1 = (loc[0] & 0xFFFF) << 16 | (loc[1] & 0xFFFF); /* Add reloc offset */ temp1+=v; /* Store back into code */ loc[0] = (loc[0] & 0xFFFF0000) | temp1 >> 16; loc[1] = (loc[1] & 0xFFFF0000) | (temp1 & 0xFFFF); break; } case R_MICROBLAZE_32_PCREL_LO: case R_MICROBLAZE_32_LO: case R_MICROBLAZE_SRO32: case R_MICROBLAZE_SRW32: ret = obj_reloc_unhandled; break; #elif defined(__mc68000__) case R_68K_NONE: break; case R_68K_32: *loc += v; break; case R_68K_8: if (v > 0xff) { ret = obj_reloc_overflow; } *(char *)loc = v; break; case R_68K_16: if (v > 0xffff) { ret = obj_reloc_overflow; } *(short *)loc = v; break; case R_68K_PC8: v -= dot; if ((ElfW(Sword))v > 0x7f || (ElfW(Sword))v < -(ElfW(Sword))0x80 ) { ret = obj_reloc_overflow; } *(char *)loc = v; break; case R_68K_PC16: v -= dot; if ((ElfW(Sword))v > 0x7fff || (ElfW(Sword))v < -(ElfW(Sword))0x8000 ) { ret = obj_reloc_overflow; } *(short *)loc = v; break; case R_68K_PC32: *(int *)loc = v - dot; break; case R_68K_GLOB_DAT: case R_68K_JMP_SLOT: *loc = v; break; case R_68K_RELATIVE: *(int *)loc += f->baseaddr; break; case R_68K_GOT32: goto bb_use_got; # ifdef R_68K_GOTOFF case R_68K_GOTOFF: *loc += v - got; break; # endif #elif defined(__mips__) case R_MIPS_NONE: break; case R_MIPS_32: *loc += v; break; case R_MIPS_26: if (v % 4) ret = obj_reloc_dangerous; if ((v & 0xf0000000) != ((dot + 4) & 0xf0000000)) ret = obj_reloc_overflow; *loc = (*loc & ~0x03ffffff) | ((*loc + (v >> 2)) & 0x03ffffff); break; case R_MIPS_HI16: { struct mips_hi16 *n; /* We cannot relocate this one now because we don't know the value of the carry we need to add. Save the information, and let LO16 do the actual relocation. */ n = xmalloc(sizeof *n); n->addr = loc; n->value = v; n->next = ifile->mips_hi16_list; ifile->mips_hi16_list = n; break; } case R_MIPS_LO16: { unsigned long insnlo = *loc; ElfW(Addr) val, vallo; /* Sign extend the addend we extract from the lo insn. */ vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; if (ifile->mips_hi16_list != NULL) { struct mips_hi16 *l; l = ifile->mips_hi16_list; while (l != NULL) { struct mips_hi16 *next; unsigned long insn; /* Do the HI16 relocation. Note that we actually don't need to know anything about the LO16 itself, except where to find the low 16 bits of the addend needed by the LO16. */ insn = *l->addr; val = ((insn & 0xffff) << 16) + vallo; val += v; /* Account for the sign extension that will happen in the low bits. */ val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff; insn = (insn & ~0xffff) | val; *l->addr = insn; next = l->next; free(l); l = next; } ifile->mips_hi16_list = NULL; } /* Ok, we're done with the HI16 relocs. Now deal with the LO16. */ val = v + vallo; insnlo = (insnlo & ~0xffff) | (val & 0xffff); *loc = insnlo; break; } #elif defined(__nios2__) case R_NIOS2_NONE: break; case R_NIOS2_BFD_RELOC_32: *loc += v; break; case R_NIOS2_BFD_RELOC_16: if (v > 0xffff) { ret = obj_reloc_overflow; } *(short *)loc = v; break; case R_NIOS2_BFD_RELOC_8: if (v > 0xff) { ret = obj_reloc_overflow; } *(char *)loc = v; break; case R_NIOS2_S16: { Elf32_Addr word; if ((Elf32_Sword)v > 0x7fff || (Elf32_Sword)v < -(Elf32_Sword)0x8000 ) { ret = obj_reloc_overflow; } word = *loc; *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) | (word & 0x3f); } break; case R_NIOS2_U16: { Elf32_Addr word; if (v > 0xffff) { ret = obj_reloc_overflow; } word = *loc; *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) | (word & 0x3f); } break; case R_NIOS2_PCREL16: { Elf32_Addr word; v -= dot + 4; if ((Elf32_Sword)v > 0x7fff || (Elf32_Sword)v < -(Elf32_Sword)0x8000 ) { ret = obj_reloc_overflow; } word = *loc; *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) | (word & 0x3f); } break; case R_NIOS2_GPREL: { Elf32_Addr word, gp; /* get _gp */ gp = obj_symbol_final_value(f, obj_find_symbol(f, SPFX "_gp")); v -= gp; if ((Elf32_Sword)v > 0x7fff || (Elf32_Sword)v < -(Elf32_Sword)0x8000 ) { ret = obj_reloc_overflow; } word = *loc; *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) | (word & 0x3f); } break; case R_NIOS2_CALL26: if (v & 3) ret = obj_reloc_dangerous; if ((v >> 28) != (dot >> 28)) ret = obj_reloc_overflow; *loc = (*loc & 0x3f) | ((v >> 2) << 6); break; case R_NIOS2_IMM5: { Elf32_Addr word; if (v > 0x1f) { ret = obj_reloc_overflow; } word = *loc & ~0x7c0; *loc = word | ((v & 0x1f) << 6); } break; case R_NIOS2_IMM6: { Elf32_Addr word; if (v > 0x3f) { ret = obj_reloc_overflow; } word = *loc & ~0xfc0; *loc = word | ((v & 0x3f) << 6); } break; case R_NIOS2_IMM8: { Elf32_Addr word; if (v > 0xff) { ret = obj_reloc_overflow; } word = *loc & ~0x3fc0; *loc = word | ((v & 0xff) << 6); } break; case R_NIOS2_HI16: { Elf32_Addr word; word = *loc; *loc = ((((word >> 22) << 16) | ((v >>16) & 0xffff)) << 6) | (word & 0x3f); } break; case R_NIOS2_LO16: { Elf32_Addr word; word = *loc; *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) | (word & 0x3f); } break; case R_NIOS2_HIADJ16: { Elf32_Addr word1, word2; word1 = *loc; word2 = ((v >> 16) + ((v >> 15) & 1)) & 0xffff; *loc = ((((word1 >> 22) << 16) | word2) << 6) | (word1 & 0x3f); } break; #elif defined(__powerpc64__) /* PPC64 needs a 2.6 kernel, 2.4 module relocation irrelevant */ #elif defined(__powerpc__) case R_PPC_ADDR16_HA: *(unsigned short *)loc = (v + 0x8000) >> 16; break; case R_PPC_ADDR16_HI: *(unsigned short *)loc = v >> 16; break; case R_PPC_ADDR16_LO: *(unsigned short *)loc = v; break; case R_PPC_REL24: goto bb_use_plt; case R_PPC_REL32: *loc = v - dot; break; case R_PPC_ADDR32: *loc = v; break; #elif defined(__s390__) case R_390_32: *(unsigned int *) loc += v; break; case R_390_16: *(unsigned short *) loc += v; break; case R_390_8: *(unsigned char *) loc += v; break; case R_390_PC32: *(unsigned int *) loc += v - dot; break; case R_390_PC16DBL: *(unsigned short *) loc += (v - dot) >> 1; break; case R_390_PC16: *(unsigned short *) loc += v - dot; break; case R_390_PLT32: case R_390_PLT16DBL: /* find the plt entry and initialize it. */ pe = (struct arch_single_entry *) &isym->pltent; if (pe->inited == 0) { ip = (unsigned long *)(ifile->plt->contents + pe->offset); ip[0] = 0x0d105810; /* basr 1,0; lg 1,10(1); br 1 */ ip[1] = 0x100607f1; if (ELF_R_TYPE(rel->r_info) == R_390_PLT16DBL) ip[2] = v - 2; else ip[2] = v; pe->inited = 1; } /* Insert relative distance to target. */ v = plt + pe->offset - dot; if (ELF_R_TYPE(rel->r_info) == R_390_PLT32) *(unsigned int *) loc = (unsigned int) v; else if (ELF_R_TYPE(rel->r_info) == R_390_PLT16DBL) *(unsigned short *) loc = (unsigned short) ((v + 2) >> 1); break; case R_390_GLOB_DAT: case R_390_JMP_SLOT: *loc = v; break; case R_390_RELATIVE: *loc += f->baseaddr; break; case R_390_GOTPC: *(unsigned long *) loc += got - dot; break; case R_390_GOT12: case R_390_GOT16: case R_390_GOT32: if (!isym->gotent.inited) { isym->gotent.inited = 1; *(ElfW(Addr) *)(ifile->got->contents + isym->gotent.offset) = v; } if (ELF_R_TYPE(rel->r_info) == R_390_GOT12) *(unsigned short *) loc |= (*(unsigned short *) loc + isym->gotent.offset) & 0xfff; else if (ELF_R_TYPE(rel->r_info) == R_390_GOT16) *(unsigned short *) loc += isym->gotent.offset; else if (ELF_R_TYPE(rel->r_info) == R_390_GOT32) *(unsigned int *) loc += isym->gotent.offset; break; # ifndef R_390_GOTOFF32 # define R_390_GOTOFF32 R_390_GOTOFF # endif case R_390_GOTOFF32: *loc += v - got; break; #elif defined(__sh__) case R_SH_NONE: break; case R_SH_DIR32: *loc += v; break; case R_SH_REL32: *loc += v - dot; break; case R_SH_PLT32: *loc = v - dot; break; case R_SH_GLOB_DAT: case R_SH_JMP_SLOT: *loc = v; break; case R_SH_RELATIVE: *loc = f->baseaddr + rel->r_addend; break; case R_SH_GOTPC: *loc = got - dot + rel->r_addend; break; case R_SH_GOT32: goto bb_use_got; case R_SH_GOTOFF: *loc = v - got; break; # if defined(__SH5__) case R_SH_IMM_MEDLOW16: case R_SH_IMM_LOW16: { ElfW(Addr) word; if (ELF_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16) v >>= 16; /* * movi and shori have the format: * * | op | imm | reg | reserved | * 31..26 25..10 9.. 4 3 .. 0 * * so we simply mask and or in imm. */ word = *loc & ~0x3fffc00; word |= (v & 0xffff) << 10; *loc = word; break; } case R_SH_IMM_MEDLOW16_PCREL: case R_SH_IMM_LOW16_PCREL: { ElfW(Addr) word; word = *loc & ~0x3fffc00; v -= dot; if (ELF_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16_PCREL) v >>= 16; word |= (v & 0xffff) << 10; *loc = word; break; } # endif /* __SH5__ */ #elif defined(__v850e__) case R_V850_NONE: break; case R_V850_32: /* We write two shorts instead of a long because even 32-bit insns only need half-word alignment, but 32-bit data needs to be long-word aligned. */ v += ((unsigned short *)loc)[0]; v += ((unsigned short *)loc)[1] << 16; ((unsigned short *)loc)[0] = v & 0xffff; ((unsigned short *)loc)[1] = (v >> 16) & 0xffff; break; case R_V850_22_PCREL: goto bb_use_plt; #elif defined(__x86_64__) case R_X86_64_NONE: break; case R_X86_64_64: *loc += v; break; case R_X86_64_32: *(unsigned int *) loc += v; if (v > 0xffffffff) { ret = obj_reloc_overflow; /* Kernel module compiled without -mcmodel=kernel. */ /* error("Possibly is module compiled without -mcmodel=kernel!"); */ } break; case R_X86_64_32S: *(signed int *) loc += v; break; case R_X86_64_16: *(unsigned short *) loc += v; break; case R_X86_64_8: *(unsigned char *) loc += v; break; case R_X86_64_PC32: *(unsigned int *) loc += v - dot; break; case R_X86_64_PC16: *(unsigned short *) loc += v - dot; break; case R_X86_64_PC8: *(unsigned char *) loc += v - dot; break; case R_X86_64_GLOB_DAT: case R_X86_64_JUMP_SLOT: *loc = v; break; case R_X86_64_RELATIVE: *loc += f->baseaddr; break; case R_X86_64_GOT32: case R_X86_64_GOTPCREL: goto bb_use_got; # if 0 if (!isym->gotent.reloc_done) { isym->gotent.reloc_done = 1; *(Elf64_Addr *)(ifile->got->contents + isym->gotent.offset) = v; } /* XXX are these really correct? */ if (ELF64_R_TYPE(rel->r_info) == R_X86_64_GOTPCREL) *(unsigned int *) loc += v + isym->gotent.offset; else *loc += isym->gotent.offset; break; # endif #else # warning "no idea how to handle relocations on your arch" #endif default: printf("Warning: unhandled reloc %d\n", (int)ELF_R_TYPE(rel->r_info)); ret = obj_reloc_unhandled; break; #if defined(USE_PLT_ENTRIES) bb_use_plt: /* find the plt entry and initialize it if necessary */ #if defined(USE_PLT_LIST) for (pe = isym->pltent; pe != NULL && pe->addend != rel->r_addend;) pe = pe->next; #else pe = &isym->pltent; #endif if (! pe->inited) { ip = (unsigned long *) (ifile->plt->contents + pe->offset); /* generate some machine code */ #if defined(__arm__) ip[0] = 0xe51ff004; /* ldr pc,[pc,#-4] */ ip[1] = v; /* sym@ */ #endif #if defined(__powerpc__) ip[0] = 0x3d600000 + ((v + 0x8000) >> 16); /* lis r11,sym@ha */ ip[1] = 0x396b0000 + (v & 0xffff); /* addi r11,r11,sym@l */ ip[2] = 0x7d6903a6; /* mtctr r11 */ ip[3] = 0x4e800420; /* bctr */ #endif #if defined(__v850e__) /* We have to trash a register, so we assume that any control transfer more than 21-bits away must be a function call (so we can use a call-clobbered register). */ ip[0] = 0x0621 + ((v & 0xffff) << 16); /* mov sym, r1 ... */ ip[1] = ((v >> 16) & 0xffff) + 0x610000; /* ...; jmp r1 */ #endif pe->inited = 1; } /* relative distance to target */ v -= dot; /* if the target is too far away.... */ #if defined(__arm__) || defined(__powerpc__) if ((int)v < -0x02000000 || (int)v >= 0x02000000) #elif defined(__v850e__) if ((ElfW(Sword))v > 0x1fffff || (ElfW(Sword))v < (ElfW(Sword))-0x200000) #endif /* go via the plt */ v = plt + pe->offset - dot; #if defined(__v850e__) if (v & 1) #else if (v & 3) #endif ret = obj_reloc_dangerous; /* merge the offset into the instruction. */ #if defined(__arm__) /* Convert to words. */ v >>= 2; *loc = (*loc & ~0x00ffffff) | ((v + *loc) & 0x00ffffff); #endif #if defined(__powerpc__) *loc = (*loc & ~0x03fffffc) | (v & 0x03fffffc); #endif #if defined(__v850e__) /* We write two shorts instead of a long because even 32-bit insns only need half-word alignment, but the 32-bit data write needs to be long-word aligned. */ ((unsigned short *)loc)[0] = (*(unsigned short *)loc & 0xffc0) /* opcode + reg */ | ((v >> 16) & 0x3f); /* offs high part */ ((unsigned short *)loc)[1] = (v & 0xffff); /* offs low part */ #endif break; #endif /* USE_PLT_ENTRIES */ #if defined(USE_GOT_ENTRIES) bb_use_got: /* needs an entry in the .got: set it, once */ if (!isym->gotent.inited) { isym->gotent.inited = 1; *(ElfW(Addr) *) (ifile->got->contents + isym->gotent.offset) = v; } /* make the reloc with_respect_to_.got */ #if defined(__sh__) *loc += isym->gotent.offset + rel->r_addend; #elif defined(__i386__) || defined(__arm__) || defined(__mc68000__) *loc += isym->gotent.offset; #endif break; #endif /* USE_GOT_ENTRIES */ } return ret; } #if defined(USE_LIST) static int arch_list_add(ElfW(RelM) *rel, struct arch_list_entry **list, int offset, int size) { struct arch_list_entry *pe; for (pe = *list; pe != NULL; pe = pe->next) { if (pe->addend == rel->r_addend) { break; } } if (pe == NULL) { pe = xzalloc(sizeof(struct arch_list_entry)); pe->next = *list; pe->addend = rel->r_addend; pe->offset = offset; /*pe->inited = 0;*/ *list = pe; return size; } return 0; } #endif #if defined(USE_SINGLE) static int arch_single_init(/*ElfW(RelM) *rel,*/ struct arch_single_entry *single, int offset, int size) { if (single->allocated == 0) { single->allocated = 1; single->offset = offset; single->inited = 0; return size; } return 0; } #endif #if defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES) static struct obj_section *arch_xsect_init(struct obj_file *f, const char *name, int offset, int size) { struct obj_section *myrelsec = obj_find_section(f, name); if (offset == 0) { offset += size; } if (myrelsec) { obj_extend_section(myrelsec, offset); } else { myrelsec = obj_create_alloced_section(f, name, size, offset); } return myrelsec; } #endif static void arch_create_got(struct obj_file *f) { #if defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES) struct arch_file *ifile = (struct arch_file *) f; int i; #if defined(USE_GOT_ENTRIES) int got_offset = 0, got_needed = 0, got_allocate; #endif #if defined(USE_PLT_ENTRIES) int plt_offset = 0, plt_needed = 0, plt_allocate; #endif struct obj_section *relsec, *symsec, *strsec; ElfW(RelM) *rel, *relend; ElfW(Sym) *symtab, *extsym; const char *strtab, *name; struct arch_symbol *intsym; for (i = 0; i < f->header.e_shnum; ++i) { relsec = f->sections[i]; if (relsec->header.sh_type != SHT_RELM) continue; symsec = f->sections[relsec->header.sh_link]; strsec = f->sections[symsec->header.sh_link]; rel = (ElfW(RelM) *) relsec->contents; relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM))); symtab = (ElfW(Sym) *) symsec->contents; strtab = (const char *) strsec->contents; for (; rel < relend; ++rel) { extsym = &symtab[ELF_R_SYM(rel->r_info)]; #if defined(USE_GOT_ENTRIES) got_allocate = 0; #endif #if defined(USE_PLT_ENTRIES) plt_allocate = 0; #endif switch (ELF_R_TYPE(rel->r_info)) { #if defined(__arm__) case R_ARM_PC24: case R_ARM_PLT32: plt_allocate = 1; break; case R_ARM_GOTOFF: case R_ARM_GOTPC: got_needed = 1; continue; case R_ARM_GOT32: got_allocate = 1; break; #elif defined(__i386__) case R_386_GOTPC: case R_386_GOTOFF: got_needed = 1; continue; case R_386_GOT32: got_allocate = 1; break; #elif defined(__powerpc__) case R_PPC_REL24: plt_allocate = 1; break; #elif defined(__mc68000__) case R_68K_GOT32: got_allocate = 1; break; #ifdef R_68K_GOTOFF case R_68K_GOTOFF: got_needed = 1; continue; #endif #elif defined(__sh__) case R_SH_GOT32: got_allocate = 1; break; case R_SH_GOTPC: case R_SH_GOTOFF: got_needed = 1; continue; #elif defined(__v850e__) case R_V850_22_PCREL: plt_needed = 1; break; #endif default: continue; } if (extsym->st_name != 0) { name = strtab + extsym->st_name; } else { name = f->sections[extsym->st_shndx]->name; } intsym = (struct arch_symbol *) obj_find_symbol(f, name); #if defined(USE_GOT_ENTRIES) if (got_allocate) { got_offset += arch_single_init( /*rel,*/ &intsym->gotent, got_offset, GOT_ENTRY_SIZE); got_needed = 1; } #endif #if defined(USE_PLT_ENTRIES) if (plt_allocate) { #if defined(USE_PLT_LIST) plt_offset += arch_list_add( rel, &intsym->pltent, plt_offset, PLT_ENTRY_SIZE); #else plt_offset += arch_single_init( /*rel,*/ &intsym->pltent, plt_offset, PLT_ENTRY_SIZE); #endif plt_needed = 1; } #endif } } #if defined(USE_GOT_ENTRIES) if (got_needed) { ifile->got = arch_xsect_init(f, ".got", got_offset, GOT_ENTRY_SIZE); } #endif #if defined(USE_PLT_ENTRIES) if (plt_needed) { ifile->plt = arch_xsect_init(f, ".plt", plt_offset, PLT_ENTRY_SIZE); } #endif #endif /* defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES) */ } /*======================================================================*/ /* Standard ELF hash function. */ static unsigned long obj_elf_hash_n(const char *name, unsigned long n) { unsigned long h = 0; unsigned long g; unsigned char ch; while (n > 0) { ch = *name++; h = (h << 4) + ch; g = (h & 0xf0000000); if (g != 0) { h ^= g >> 24; h &= ~g; } n--; } return h; } static unsigned long FAST_FUNC obj_elf_hash(const char *name) { return obj_elf_hash_n(name, strlen(name)); } #if ENABLE_FEATURE_INSMOD_VERSION_CHECKING /* String comparison for non-co-versioned kernel and module. */ static int ncv_strcmp(const char *a, const char *b) { size_t alen = strlen(a), blen = strlen(b); if (blen == alen + 10 && b[alen] == '_' && b[alen + 1] == 'R') return strncmp(a, b, alen); else if (alen == blen + 10 && a[blen] == '_' && a[blen + 1] == 'R') return strncmp(a, b, blen); else return strcmp(a, b); } /* String hashing for non-co-versioned kernel and module. Here we are simply forced to drop the crc from the hash. */ static unsigned long FAST_FUNC ncv_symbol_hash(const char *str) { size_t len = strlen(str); if (len > 10 && str[len - 10] == '_' && str[len - 9] == 'R') len -= 10; return obj_elf_hash_n(str, len); } static void obj_set_symbol_compare(struct obj_file *f, int (*cmp) (const char *, const char *), unsigned long (*hash) (const char *) FAST_FUNC) { if (cmp) f->symbol_cmp = cmp; if (hash) { struct obj_symbol *tmptab[HASH_BUCKETS], *sym, *next; int i; f->symbol_hash = hash; memcpy(tmptab, f->symtab, sizeof(tmptab)); memset(f->symtab, 0, sizeof(f->symtab)); for (i = 0; i < HASH_BUCKETS; ++i) { for (sym = tmptab[i]; sym; sym = next) { unsigned long h = hash(sym->name) % HASH_BUCKETS; next = sym->next; sym->next = f->symtab[h]; f->symtab[h] = sym; } } } } #endif /* FEATURE_INSMOD_VERSION_CHECKING */ static struct obj_symbol * obj_add_symbol(struct obj_file *f, const char *name, unsigned long symidx, int info, int secidx, ElfW(Addr) value, unsigned long size) { struct obj_symbol *sym; unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS; int n_type = ELF_ST_TYPE(info); int n_binding = ELF_ST_BIND(info); for (sym = f->symtab[hash]; sym; sym = sym->next) { if (f->symbol_cmp(sym->name, name) == 0) { int o_secidx = sym->secidx; int o_info = sym->info; int o_type = ELF_ST_TYPE(o_info); int o_binding = ELF_ST_BIND(o_info); /* A redefinition! Is it legal? */ if (secidx == SHN_UNDEF) return sym; else if (o_secidx == SHN_UNDEF) goto found; else if (n_binding == STB_GLOBAL && o_binding == STB_LOCAL) { /* Cope with local and global symbols of the same name in the same object file, as might have been created by ld -r. The only reason locals are now seen at this level at all is so that we can do semi-sensible things with parameters. */ struct obj_symbol *nsym, **p; nsym = arch_new_symbol(); nsym->next = sym->next; nsym->ksymidx = -1; /* Excise the old (local) symbol from the hash chain. */ for (p = &f->symtab[hash]; *p != sym; p = &(*p)->next) continue; *p = sym = nsym; goto found; } else if (n_binding == STB_LOCAL) { /* Another symbol of the same name has already been defined. Just add this to the local table. */ sym = arch_new_symbol(); sym->next = NULL; sym->ksymidx = -1; f->local_symtab[symidx] = sym; goto found; } else if (n_binding == STB_WEAK) return sym; else if (o_binding == STB_WEAK) goto found; /* Don't unify COMMON symbols with object types the programmer doesn't expect. */ else if (secidx == SHN_COMMON && (o_type == STT_NOTYPE || o_type == STT_OBJECT)) return sym; else if (o_secidx == SHN_COMMON && (n_type == STT_NOTYPE || n_type == STT_OBJECT)) goto found; else { /* Don't report an error if the symbol is coming from the kernel or some external module. */ if (secidx <= SHN_HIRESERVE) bb_error_msg("%s multiply defined", name); return sym; } } } /* Completely new symbol. */ sym = arch_new_symbol(); sym->next = f->symtab[hash]; f->symtab[hash] = sym; sym->ksymidx = -1; if (ELF_ST_BIND(info) == STB_LOCAL && symidx != (unsigned long)(-1)) { if (symidx >= f->local_symtab_size) bb_error_msg("local symbol %s with index %ld exceeds local_symtab_size %ld", name, (long) symidx, (long) f->local_symtab_size); else f->local_symtab[symidx] = sym; } found: sym->name = name; sym->value = value; sym->size = size; sym->secidx = secidx; sym->info = info; return sym; } static struct obj_symbol * obj_find_symbol(struct obj_file *f, const char *name) { struct obj_symbol *sym; unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS; for (sym = f->symtab[hash]; sym; sym = sym->next) if (f->symbol_cmp(sym->name, name) == 0) return sym; return NULL; } static ElfW(Addr) obj_symbol_final_value(struct obj_file * f, struct obj_symbol * sym) { if (sym) { if (sym->secidx >= SHN_LORESERVE) return sym->value; return sym->value + f->sections[sym->secidx]->header.sh_addr; } /* As a special case, a NULL sym has value zero. */ return 0; } static struct obj_section *obj_find_section(struct obj_file *f, const char *name) { int i, n = f->header.e_shnum; for (i = 0; i < n; ++i) if (strcmp(f->sections[i]->name, name) == 0) return f->sections[i]; return NULL; } static int obj_load_order_prio(struct obj_section *a) { unsigned long af, ac; af = a->header.sh_flags; ac = 0; if (a->name[0] != '.' || strlen(a->name) != 10 || strcmp(a->name + 5, ".init") != 0 ) { ac |= 32; } if (af & SHF_ALLOC) ac |= 16; if (!(af & SHF_WRITE)) ac |= 8; if (af & SHF_EXECINSTR) ac |= 4; if (a->header.sh_type != SHT_NOBITS) ac |= 2; return ac; } static void obj_insert_section_load_order(struct obj_file *f, struct obj_section *sec) { struct obj_section **p; int prio = obj_load_order_prio(sec); for (p = f->load_order_search_start; *p; p = &(*p)->load_next) if (obj_load_order_prio(*p) < prio) break; sec->load_next = *p; *p = sec; } static struct obj_section *helper_create_alloced_section(struct obj_file *f, const char *name, unsigned long align, unsigned long size) { int newidx = f->header.e_shnum++; struct obj_section *sec; f->sections = xrealloc_vector(f->sections, 2, newidx); f->sections[newidx] = sec = arch_new_section(); sec->header.sh_type = SHT_PROGBITS; sec->header.sh_flags = SHF_WRITE | SHF_ALLOC; sec->header.sh_size = size; sec->header.sh_addralign = align; sec->name = name; sec->idx = newidx; if (size) sec->contents = xzalloc(size); return sec; } static struct obj_section *obj_create_alloced_section(struct obj_file *f, const char *name, unsigned long align, unsigned long size) { struct obj_section *sec; sec = helper_create_alloced_section(f, name, align, size); obj_insert_section_load_order(f, sec); return sec; } static struct obj_section *obj_create_alloced_section_first(struct obj_file *f, const char *name, unsigned long align, unsigned long size) { struct obj_section *sec; sec = helper_create_alloced_section(f, name, align, size); sec->load_next = f->load_order; f->load_order = sec; if (f->load_order_search_start == &f->load_order) f->load_order_search_start = &sec->load_next; return sec; } static void *obj_extend_section(struct obj_section *sec, unsigned long more) { unsigned long oldsize = sec->header.sh_size; if (more) { sec->header.sh_size += more; sec->contents = xrealloc(sec->contents, sec->header.sh_size); } return sec->contents + oldsize; } /* Conditionally add the symbols from the given symbol set to the new module. */ static int add_symbols_from(struct obj_file *f, int idx, struct new_module_symbol *syms, size_t nsyms) { struct new_module_symbol *s; size_t i; int used = 0; #ifdef SYMBOL_PREFIX char *name_buf = NULL; size_t name_alloced_size = 0; #endif #if ENABLE_FEATURE_CHECK_TAINTED_MODULE int gpl; gpl = obj_gpl_license(f, NULL) == 0; #endif for (i = 0, s = syms; i < nsyms; ++i, ++s) { /* Only add symbols that are already marked external. If we override locals we may cause problems for argument initialization. We will also create a false dependency on the module. */ struct obj_symbol *sym; char *name; /* GPL licensed modules can use symbols exported with * EXPORT_SYMBOL_GPL, so ignore any GPLONLY_ prefix on the * exported names. Non-GPL modules never see any GPLONLY_ * symbols so they cannot fudge it by adding the prefix on * their references. */ if (strncmp((char *)s->name, "GPLONLY_", 8) == 0) { #if ENABLE_FEATURE_CHECK_TAINTED_MODULE if (gpl) s->name += 8; else #endif continue; } name = (char *)s->name; #ifdef SYMBOL_PREFIX /* Prepend SYMBOL_PREFIX to the symbol's name (the kernel exports `C names', but module object files reference `linker names'). */ size_t extra = sizeof SYMBOL_PREFIX; size_t name_size = strlen(name) + extra; if (name_size > name_alloced_size) { name_alloced_size = name_size * 2; name_buf = alloca(name_alloced_size); } strcpy(name_buf, SYMBOL_PREFIX); strcpy(name_buf + extra - 1, name); name = name_buf; #endif sym = obj_find_symbol(f, name); if (sym && !(ELF_ST_BIND(sym->info) == STB_LOCAL)) { #ifdef SYMBOL_PREFIX /* Put NAME_BUF into more permanent storage. */ name = xmalloc(name_size); strcpy(name, name_buf); #endif sym = obj_add_symbol(f, name, -1, ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE), idx, s->value, 0); /* Did our symbol just get installed? If so, mark the module as "used". */ if (sym->secidx == idx) used = 1; } } return used; } static void add_kernel_symbols(struct obj_file *f) { struct external_module *m; int i, nused = 0; /* Add module symbols first. */ for (i = 0, m = ext_modules; i < n_ext_modules; ++i, ++m) { if (m->nsyms && add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms, m->nsyms) ) { m->used = 1; ++nused; } } n_ext_modules_used = nused; /* And finally the symbols from the kernel proper. */ if (nksyms) add_symbols_from(f, SHN_HIRESERVE + 1, ksyms, nksyms); } static char *get_modinfo_value(struct obj_file *f, const char *key) { struct obj_section *sec; char *p, *v, *n, *ep; size_t klen = strlen(key); sec = obj_find_section(f, ".modinfo"); if (sec == NULL) return NULL; p = sec->contents; ep = p + sec->header.sh_size; while (p < ep) { v = strchr(p, '='); n = strchr(p, '\0'); if (v) { if (p + klen == v && strncmp(p, key, klen) == 0) return v + 1; } else { if (p + klen == n && strcmp(p, key) == 0) return n; } p = n + 1; } return NULL; } /*======================================================================*/ /* Functions relating to module loading after 2.1.18. */ /* From Linux-2.6 sources */ /* You can use " around spaces, but can't escape ". */ /* Hyphens and underscores equivalent in parameter names. */ static char *next_arg(char *args, char **param, char **val) { unsigned int i, equals = 0; int in_quote = 0, quoted = 0; char *next; if (*args == '"') { args++; in_quote = 1; quoted = 1; } for (i = 0; args[i]; i++) { if (args[i] == ' ' && !in_quote) break; if (equals == 0) { if (args[i] == '=') equals = i; } if (args[i] == '"') in_quote = !in_quote; } *param = args; if (!equals) *val = NULL; else { args[equals] = '\0'; *val = args + equals + 1; /* Don't include quotes in value. */ if (**val == '"') { (*val)++; if (args[i-1] == '"') args[i-1] = '\0'; } if (quoted && args[i-1] == '"') args[i-1] = '\0'; } if (args[i]) { args[i] = '\0'; next = args + i + 1; } else next = args + i; /* Chew up trailing spaces. */ return skip_whitespace(next); } static void new_process_module_arguments(struct obj_file *f, const char *options) { char *xoptions, *pos; char *param, *val; xoptions = pos = xstrdup(skip_whitespace(options)); while (*pos) { unsigned long charssize = 0; char *tmp, *contents, *loc, *pinfo, *p; struct obj_symbol *sym; int min, max, n, len; pos = next_arg(pos, ¶m, &val); tmp = xasprintf("parm_%s", param); pinfo = get_modinfo_value(f, tmp); free(tmp); if (pinfo == NULL) bb_error_msg_and_die("invalid parameter %s", param); #ifdef SYMBOL_PREFIX tmp = xasprintf(SYMBOL_PREFIX "%s", param); sym = obj_find_symbol(f, tmp); free(tmp); #else sym = obj_find_symbol(f, param); #endif /* Also check that the parameter was not resolved from the kernel. */ if (sym == NULL || sym->secidx > SHN_HIRESERVE) bb_error_msg_and_die("symbol for parameter %s not found", param); /* Number of parameters */ min = max = 1; if (isdigit(*pinfo)) { min = max = strtoul(pinfo, &pinfo, 10); if (*pinfo == '-') max = strtoul(pinfo + 1, &pinfo, 10); } contents = f->sections[sym->secidx]->contents; loc = contents + sym->value; if (*pinfo == 'c') { if (!isdigit(pinfo[1])) { bb_error_msg_and_die("parameter type 'c' for %s must be followed by" " the maximum size", param); } charssize = strtoul(pinfo + 1, NULL, 10); } if (val == NULL) { if (*pinfo != 'b') bb_error_msg_and_die("argument expected for parameter %s", param); val = (char *) "1"; } /* Parse parameter values */ n = 0; p = val; while (*p) { char sv_ch; char *endp; if (++n > max) bb_error_msg_and_die("too many values for %s (max %d)", param, max); switch (*pinfo) { case 's': len = strcspn(p, ","); sv_ch = p[len]; p[len] = '\0'; obj_string_patch(f, sym->secidx, loc - contents, p); loc += tgt_sizeof_char_p; p += len; *p = sv_ch; break; case 'c': len = strcspn(p, ","); sv_ch = p[len]; p[len] = '\0'; if (len >= charssize) bb_error_msg_and_die("string too long for %s (max %ld)", param, charssize - 1); strcpy((char *) loc, p); loc += charssize; p += len; *p = sv_ch; break; case 'b': *loc++ = strtoul(p, &endp, 0); p = endp; /* gcc likes temp var for &endp */ break; case 'h': *(short *) loc = strtoul(p, &endp, 0); loc += tgt_sizeof_short; p = endp; break; case 'i': *(int *) loc = strtoul(p, &endp, 0); loc += tgt_sizeof_int; p = endp; break; case 'l': *(long *) loc = strtoul(p, &endp, 0); loc += tgt_sizeof_long; p = endp; break; default: bb_error_msg_and_die("unknown parameter type '%c' for %s", *pinfo, param); } p = skip_whitespace(p); if (*p != ',') break; p = skip_whitespace(p + 1); } if (n < min) bb_error_msg_and_die("parameter %s requires at least %d arguments", param, min); if (*p != '\0') bb_error_msg_and_die("invalid argument syntax for %s", param); } free(xoptions); } #if ENABLE_FEATURE_INSMOD_VERSION_CHECKING static int new_is_module_checksummed(struct obj_file *f) { const char *p = get_modinfo_value(f, "using_checksums"); if (p) return xatoi(p); return 0; } /* Get the module's kernel version in the canonical integer form. */ static int new_get_module_version(struct obj_file *f, char str[STRVERSIONLEN]) { char *p, *q; int a, b, c; p = get_modinfo_value(f, "kernel_version"); if (p == NULL) return -1; safe_strncpy(str, p, STRVERSIONLEN); a = strtoul(p, &p, 10); if (*p != '.') return -1; b = strtoul(p + 1, &p, 10); if (*p != '.') return -1; c = strtoul(p + 1, &q, 10); if (p + 1 == q) return -1; return a << 16 | b << 8 | c; } #endif /* FEATURE_INSMOD_VERSION_CHECKING */ /* Fetch the loaded modules, and all currently exported symbols. */ static void new_get_kernel_symbols(void) { char *module_names, *mn; struct external_module *modules, *m; struct new_module_symbol *syms, *s; size_t ret, bufsize, nmod, nsyms, i, j; /* Collect the loaded modules. */ bufsize = 256; module_names = xmalloc(bufsize); retry_modules_load: if (query_module(NULL, QM_MODULES, module_names, bufsize, &ret)) { if (errno == ENOSPC && bufsize < ret) { bufsize = ret; module_names = xrealloc(module_names, bufsize); goto retry_modules_load; } bb_perror_msg_and_die("QM_MODULES"); } n_ext_modules = nmod = ret; /* Collect the modules' symbols. */ if (nmod) { ext_modules = modules = xzalloc(nmod * sizeof(*modules)); for (i = 0, mn = module_names, m = modules; i < nmod; ++i, ++m, mn += strlen(mn) + 1) { struct new_module_info info; if (query_module(mn, QM_INFO, &info, sizeof(info), &ret)) { if (errno == ENOENT) { /* The module was removed out from underneath us. */ continue; } bb_perror_msg_and_die("query_module: QM_INFO: %s", mn); } bufsize = 1024; syms = xmalloc(bufsize); retry_mod_sym_load: if (query_module(mn, QM_SYMBOLS, syms, bufsize, &ret)) { switch (errno) { case ENOSPC: bufsize = ret; syms = xrealloc(syms, bufsize); goto retry_mod_sym_load; case ENOENT: /* The module was removed out from underneath us. */ continue; default: bb_perror_msg_and_die("query_module: QM_SYMBOLS: %s", mn); } } nsyms = ret; m->name = mn; m->addr = info.addr; m->nsyms = nsyms; m->syms = syms; for (j = 0, s = syms; j < nsyms; ++j, ++s) { s->name += (unsigned long) syms; } } } /* Collect the kernel's symbols. */ bufsize = 16 * 1024; syms = xmalloc(bufsize); retry_kern_sym_load: if (query_module(NULL, QM_SYMBOLS, syms, bufsize, &ret)) { if (errno == ENOSPC && bufsize < ret) { bufsize = ret; syms = xrealloc(syms, bufsize); goto retry_kern_sym_load; } bb_perror_msg_and_die("kernel: QM_SYMBOLS"); } nksyms = nsyms = ret; ksyms = syms; for (j = 0, s = syms; j < nsyms; ++j, ++s) { s->name += (unsigned long) syms; } } /* Return the kernel symbol checksum version, or zero if not used. */ static int new_is_kernel_checksummed(void) { struct new_module_symbol *s; size_t i; /* Using_Versions is not the first symbol, but it should be in there. */ for (i = 0, s = ksyms; i < nksyms; ++i, ++s) if (strcmp((char *) s->name, "Using_Versions") == 0) return s->value; return 0; } static void new_create_this_module(struct obj_file *f, const char *m_name) { struct obj_section *sec; sec = obj_create_alloced_section_first(f, ".this", tgt_sizeof_long, sizeof(struct new_module)); /* done by obj_create_alloced_section_first: */ /*memset(sec->contents, 0, sizeof(struct new_module));*/ obj_add_symbol(f, SPFX "__this_module", -1, ELF_ST_INFO(STB_LOCAL, STT_OBJECT), sec->idx, 0, sizeof(struct new_module)); obj_string_patch(f, sec->idx, offsetof(struct new_module, name), m_name); } #if ENABLE_FEATURE_INSMOD_KSYMOOPS_SYMBOLS /* add an entry to the __ksymtab section, creating it if necessary */ static void new_add_ksymtab(struct obj_file *f, struct obj_symbol *sym) { struct obj_section *sec; ElfW(Addr) ofs; /* ensure __ksymtab is allocated, EXPORT_NOSYMBOLS creates a non-alloc section. * If __ksymtab is defined but not marked alloc, x out the first character * (no obj_delete routine) and create a new __ksymtab with the correct * characteristics. */ sec = obj_find_section(f, "__ksymtab"); if (sec && !(sec->header.sh_flags & SHF_ALLOC)) { *((char *)(sec->name)) = 'x'; /* override const */ sec = NULL; } if (!sec) sec = obj_create_alloced_section(f, "__ksymtab", tgt_sizeof_void_p, 0); if (!sec) return; sec->header.sh_flags |= SHF_ALLOC; /* Empty section might be byte-aligned */ sec->header.sh_addralign = tgt_sizeof_void_p; ofs = sec->header.sh_size; obj_symbol_patch(f, sec->idx, ofs, sym); obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p, sym->name); obj_extend_section(sec, 2 * tgt_sizeof_char_p); } #endif /* FEATURE_INSMOD_KSYMOOPS_SYMBOLS */ static int new_create_module_ksymtab(struct obj_file *f) { struct obj_section *sec; int i; /* We must always add the module references. */ if (n_ext_modules_used) { struct new_module_ref *dep; struct obj_symbol *tm; sec = obj_create_alloced_section(f, ".kmodtab", tgt_sizeof_void_p, (sizeof(struct new_module_ref) * n_ext_modules_used)); if (!sec) return 0; tm = obj_find_symbol(f, SPFX "__this_module"); dep = (struct new_module_ref *) sec->contents; for (i = 0; i < n_ext_modules; ++i) if (ext_modules[i].used) { dep->dep = ext_modules[i].addr; obj_symbol_patch(f, sec->idx, (char *) &dep->ref - sec->contents, tm); dep->next_ref = 0; ++dep; } } if (!flag_noexport && !obj_find_section(f, "__ksymtab")) { size_t nsyms; int *loaded; sec = obj_create_alloced_section(f, "__ksymtab", tgt_sizeof_void_p, 0); /* We don't want to export symbols residing in sections that aren't loaded. There are a number of these created so that we make sure certain module options don't appear twice. */ i = f->header.e_shnum; loaded = alloca(sizeof(int) * i); while (--i >= 0) loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0; for (nsyms = i = 0; i < HASH_BUCKETS; ++i) { struct obj_symbol *sym; for (sym = f->symtab[i]; sym; sym = sym->next) { if (ELF_ST_BIND(sym->info) != STB_LOCAL && sym->secidx <= SHN_HIRESERVE && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]) ) { ElfW(Addr) ofs = nsyms * 2 * tgt_sizeof_void_p; obj_symbol_patch(f, sec->idx, ofs, sym); obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p, sym->name); nsyms++; } } } obj_extend_section(sec, nsyms * 2 * tgt_sizeof_char_p); } return 1; } static int new_init_module(const char *m_name, struct obj_file *f, unsigned long m_size) { struct new_module *module; struct obj_section *sec; void *image; int ret; tgt_long m_addr; sec = obj_find_section(f, ".this"); if (!sec || !sec->contents) { bb_perror_msg_and_die("corrupt module %s?", m_name); } module = (struct new_module *) sec->contents; m_addr = sec->header.sh_addr; module->size_of_struct = sizeof(*module); module->size = m_size; module->flags = flag_autoclean ? NEW_MOD_AUTOCLEAN : 0; sec = obj_find_section(f, "__ksymtab"); if (sec && sec->header.sh_size) { module->syms = sec->header.sh_addr; module->nsyms = sec->header.sh_size / (2 * tgt_sizeof_char_p); } if (n_ext_modules_used) { sec = obj_find_section(f, ".kmodtab"); module->deps = sec->header.sh_addr; module->ndeps = n_ext_modules_used; } module->init = obj_symbol_final_value(f, obj_find_symbol(f, SPFX "init_module")); module->cleanup = obj_symbol_final_value(f, obj_find_symbol(f, SPFX "cleanup_module")); sec = obj_find_section(f, "__ex_table"); if (sec) { module->ex_table_start = sec->header.sh_addr; module->ex_table_end = sec->header.sh_addr + sec->header.sh_size; } sec = obj_find_section(f, ".text.init"); if (sec) { module->runsize = sec->header.sh_addr - m_addr; } sec = obj_find_section(f, ".data.init"); if (sec) { if (!module->runsize || module->runsize > sec->header.sh_addr - m_addr ) { module->runsize = sec->header.sh_addr - m_addr; } } sec = obj_find_section(f, ARCHDATA_SEC_NAME); if (sec && sec->header.sh_size) { module->archdata_start = (void*)sec->header.sh_addr; module->archdata_end = module->archdata_start + sec->header.sh_size; } sec = obj_find_section(f, KALLSYMS_SEC_NAME); if (sec && sec->header.sh_size) { module->kallsyms_start = (void*)sec->header.sh_addr; module->kallsyms_end = module->kallsyms_start + sec->header.sh_size; } /* Whew! All of the initialization is complete. Collect the final module image and give it to the kernel. */ image = xmalloc(m_size); obj_create_image(f, image); ret = init_module(m_name, (struct new_module *) image); if (ret) bb_perror_msg("init_module: %s", m_name); free(image); return ret == 0; } /*======================================================================*/ static void obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, const char *string) { struct obj_string_patch *p; struct obj_section *strsec; size_t len = strlen(string) + 1; char *loc; p = xzalloc(sizeof(*p)); p->next = f->string_patches; p->reloc_secidx = secidx; p->reloc_offset = offset; f->string_patches = p; strsec = obj_find_section(f, ".kstrtab"); if (strsec == NULL) { strsec = obj_create_alloced_section(f, ".kstrtab", 1, len); /*p->string_offset = 0;*/ loc = strsec->contents; } else { p->string_offset = strsec->header.sh_size; loc = obj_extend_section(strsec, len); } memcpy(loc, string, len); } static void obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, struct obj_symbol *sym) { struct obj_symbol_patch *p; p = xmalloc(sizeof(*p)); p->next = f->symbol_patches; p->reloc_secidx = secidx; p->reloc_offset = offset; p->sym = sym; f->symbol_patches = p; } static void obj_check_undefineds(struct obj_file *f) { unsigned i; for (i = 0; i < HASH_BUCKETS; ++i) { struct obj_symbol *sym; for (sym = f->symtab[i]; sym; sym = sym->next) { if (sym->secidx == SHN_UNDEF) { if (ELF_ST_BIND(sym->info) == STB_WEAK) { sym->secidx = SHN_ABS; sym->value = 0; } else { if (!flag_quiet) bb_error_msg_and_die("unresolved symbol %s", sym->name); } } } } } static void obj_allocate_commons(struct obj_file *f) { struct common_entry { struct common_entry *next; struct obj_symbol *sym; } *common_head = NULL; unsigned long i; for (i = 0; i < HASH_BUCKETS; ++i) { struct obj_symbol *sym; for (sym = f->symtab[i]; sym; sym = sym->next) { if (sym->secidx == SHN_COMMON) { /* Collect all COMMON symbols and sort them by size so as to minimize space wasted by alignment requirements. */ struct common_entry **p, *n; for (p = &common_head; *p; p = &(*p)->next) if (sym->size <= (*p)->sym->size) break; n = alloca(sizeof(*n)); n->next = *p; n->sym = sym; *p = n; } } } for (i = 1; i < f->local_symtab_size; ++i) { struct obj_symbol *sym = f->local_symtab[i]; if (sym && sym->secidx == SHN_COMMON) { struct common_entry **p, *n; for (p = &common_head; *p; p = &(*p)->next) { if (sym == (*p)->sym) break; if (sym->size < (*p)->sym->size) { n = alloca(sizeof(*n)); n->next = *p; n->sym = sym; *p = n; break; } } } } if (common_head) { /* Find the bss section. */ for (i = 0; i < f->header.e_shnum; ++i) if (f->sections[i]->header.sh_type == SHT_NOBITS) break; /* If for some reason there hadn't been one, create one. */ if (i == f->header.e_shnum) { struct obj_section *sec; f->header.e_shnum++; f->sections = xrealloc_vector(f->sections, 2, i); f->sections[i] = sec = arch_new_section(); sec->header.sh_type = SHT_PROGBITS; sec->header.sh_flags = SHF_WRITE | SHF_ALLOC; sec->name = ".bss"; sec->idx = i; } /* Allocate the COMMONS. */ { ElfW(Addr) bss_size = f->sections[i]->header.sh_size; ElfW(Addr) max_align = f->sections[i]->header.sh_addralign; struct common_entry *c; for (c = common_head; c; c = c->next) { ElfW(Addr) align = c->sym->value; if (align > max_align) max_align = align; if (bss_size & (align - 1)) bss_size = (bss_size | (align - 1)) + 1; c->sym->secidx = i; c->sym->value = bss_size; bss_size += c->sym->size; } f->sections[i]->header.sh_size = bss_size; f->sections[i]->header.sh_addralign = max_align; } } /* For the sake of patch relocation and parameter initialization, allocate zeroed data for NOBITS sections now. Note that after this we cannot assume NOBITS are really empty. */ for (i = 0; i < f->header.e_shnum; ++i) { struct obj_section *s = f->sections[i]; if (s->header.sh_type == SHT_NOBITS) { s->contents = NULL; if (s->header.sh_size != 0) s->contents = xzalloc(s->header.sh_size); s->header.sh_type = SHT_PROGBITS; } } } static unsigned long obj_load_size(struct obj_file *f) { unsigned long dot = 0; struct obj_section *sec; /* Finalize the positions of the sections relative to one another. */ for (sec = f->load_order; sec; sec = sec->load_next) { ElfW(Addr) align; align = sec->header.sh_addralign; if (align && (dot & (align - 1))) dot = (dot | (align - 1)) + 1; sec->header.sh_addr = dot; dot += sec->header.sh_size; } return dot; } static int obj_relocate(struct obj_file *f, ElfW(Addr) base) { int i, n = f->header.e_shnum; int ret = 1; /* Finalize the addresses of the sections. */ f->baseaddr = base; for (i = 0; i < n; ++i) f->sections[i]->header.sh_addr += base; /* And iterate over all of the relocations. */ for (i = 0; i < n; ++i) { struct obj_section *relsec, *symsec, *targsec, *strsec; ElfW(RelM) * rel, *relend; ElfW(Sym) * symtab; const char *strtab; relsec = f->sections[i]; if (relsec->header.sh_type != SHT_RELM) continue; symsec = f->sections[relsec->header.sh_link]; targsec = f->sections[relsec->header.sh_info]; strsec = f->sections[symsec->header.sh_link]; rel = (ElfW(RelM) *) relsec->contents; relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM))); symtab = (ElfW(Sym) *) symsec->contents; strtab = (const char *) strsec->contents; for (; rel < relend; ++rel) { ElfW(Addr) value = 0; struct obj_symbol *intsym = NULL; unsigned long symndx; ElfW(Sym) *extsym = NULL; const char *errmsg; /* Attempt to find a value to use for this relocation. */ symndx = ELF_R_SYM(rel->r_info); if (symndx) { /* Note we've already checked for undefined symbols. */ extsym = &symtab[symndx]; if (ELF_ST_BIND(extsym->st_info) == STB_LOCAL) { /* Local symbols we look up in the local table to be sure we get the one that is really intended. */ intsym = f->local_symtab[symndx]; } else { /* Others we look up in the hash table. */ const char *name; if (extsym->st_name) name = strtab + extsym->st_name; else name = f->sections[extsym->st_shndx]->name; intsym = obj_find_symbol(f, name); } value = obj_symbol_final_value(f, intsym); intsym->referenced = 1; } #if SHT_RELM == SHT_RELA #if defined(__alpha__) && defined(AXP_BROKEN_GAS) /* Work around a nasty GAS bug, that is fixed as of 2.7.0.9. */ if (!extsym || !extsym->st_name || ELF_ST_BIND(extsym->st_info) != STB_LOCAL) #endif value += rel->r_addend; #endif /* Do it! */ switch (arch_apply_relocation (f, targsec, /*symsec,*/ intsym, rel, value) ) { case obj_reloc_ok: break; case obj_reloc_overflow: errmsg = "Relocation overflow"; goto bad_reloc; case obj_reloc_dangerous: errmsg = "Dangerous relocation"; goto bad_reloc; case obj_reloc_unhandled: errmsg = "Unhandled relocation"; bad_reloc: if (extsym) { bb_error_msg("%s of type %ld for %s", errmsg, (long) ELF_R_TYPE(rel->r_info), strtab + extsym->st_name); } else { bb_error_msg("%s of type %ld", errmsg, (long) ELF_R_TYPE(rel->r_info)); } ret = 0; break; } } } /* Finally, take care of the patches. */ if (f->string_patches) { struct obj_string_patch *p; struct obj_section *strsec; ElfW(Addr) strsec_base; strsec = obj_find_section(f, ".kstrtab"); strsec_base = strsec->header.sh_addr; for (p = f->string_patches; p; p = p->next) { struct obj_section *targsec = f->sections[p->reloc_secidx]; *(ElfW(Addr) *) (targsec->contents + p->reloc_offset) = strsec_base + p->string_offset; } } if (f->symbol_patches) { struct obj_symbol_patch *p; for (p = f->symbol_patches; p; p = p->next) { struct obj_section *targsec = f->sections[p->reloc_secidx]; *(ElfW(Addr) *) (targsec->contents + p->reloc_offset) = obj_symbol_final_value(f, p->sym); } } return ret; } static int obj_create_image(struct obj_file *f, char *image) { struct obj_section *sec; ElfW(Addr) base = f->baseaddr; for (sec = f->load_order; sec; sec = sec->load_next) { char *secimg; if (sec->contents == 0 || sec->header.sh_size == 0) continue; secimg = image + (sec->header.sh_addr - base); /* Note that we allocated data for NOBITS sections earlier. */ memcpy(secimg, sec->contents, sec->header.sh_size); } return 1; } /*======================================================================*/ static struct obj_file *obj_load(char *image, size_t image_size, int loadprogbits) { typedef uint32_t aliased_uint32_t FIX_ALIASING; #if BB_LITTLE_ENDIAN # define ELFMAG_U32 ((uint32_t)(ELFMAG0 + 0x100 * (ELFMAG1 + (0x100 * (ELFMAG2 + 0x100 * ELFMAG3))))) #else # define ELFMAG_U32 ((uint32_t)((((ELFMAG0 * 0x100) + ELFMAG1) * 0x100 + ELFMAG2) * 0x100 + ELFMAG3)) #endif struct obj_file *f; ElfW(Shdr) * section_headers; size_t shnum, i; char *shstrtab; /* Read the file header. */ f = arch_new_file(); f->symbol_cmp = strcmp; f->symbol_hash = obj_elf_hash; f->load_order_search_start = &f->load_order; if (image_size < sizeof(f->header)) bb_error_msg_and_die("error while loading ELF header"); memcpy(&f->header, image, sizeof(f->header)); if (*(aliased_uint32_t*)(&f->header.e_ident) != ELFMAG_U32) { bb_error_msg_and_die("not an ELF file"); } if (f->header.e_ident[EI_CLASS] != ELFCLASSM || f->header.e_ident[EI_DATA] != (BB_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB) || f->header.e_ident[EI_VERSION] != EV_CURRENT || !MATCH_MACHINE(f->header.e_machine) ) { bb_error_msg_and_die("ELF file not for this architecture"); } if (f->header.e_type != ET_REL) { bb_error_msg_and_die("ELF file not a relocatable object"); } /* Read the section headers. */ if (f->header.e_shentsize != sizeof(ElfW(Shdr))) { bb_error_msg_and_die("section header size mismatch: %lu != %lu", (unsigned long) f->header.e_shentsize, (unsigned long) sizeof(ElfW(Shdr))); } shnum = f->header.e_shnum; /* Growth of ->sections vector will be done by * xrealloc_vector(..., 2, ...), therefore we must allocate * at least 2^2 = 4 extra elements here. */ f->sections = xzalloc(sizeof(f->sections[0]) * (shnum + 4)); section_headers = alloca(sizeof(ElfW(Shdr)) * shnum); if (image_size < f->header.e_shoff + sizeof(ElfW(Shdr)) * shnum) bb_error_msg_and_die("error while loading section headers"); memcpy(section_headers, image + f->header.e_shoff, sizeof(ElfW(Shdr)) * shnum); /* Read the section data. */ for (i = 0; i < shnum; ++i) { struct obj_section *sec; f->sections[i] = sec = arch_new_section(); sec->header = section_headers[i]; sec->idx = i; if (sec->header.sh_size) { switch (sec->header.sh_type) { case SHT_NULL: case SHT_NOTE: case SHT_NOBITS: /* ignore */ break; case SHT_PROGBITS: #if LOADBITS if (!loadprogbits) { sec->contents = NULL; break; } #endif case SHT_SYMTAB: case SHT_STRTAB: case SHT_RELM: #if defined(__mips__) case SHT_MIPS_DWARF: #endif sec->contents = NULL; if (sec->header.sh_size > 0) { sec->contents = xmalloc(sec->header.sh_size); if (image_size < (sec->header.sh_offset + sec->header.sh_size)) bb_error_msg_and_die("error while loading section data"); memcpy(sec->contents, image + sec->header.sh_offset, sec->header.sh_size); } break; #if SHT_RELM == SHT_REL case SHT_RELA: bb_error_msg_and_die("RELA relocations not supported on this architecture"); #else case SHT_REL: bb_error_msg_and_die("REL relocations not supported on this architecture"); #endif default: if (sec->header.sh_type >= SHT_LOPROC) { /* Assume processor specific section types are debug info and can safely be ignored. If this is ever not the case (Hello MIPS?), don't put ifdefs here but create an arch_load_proc_section(). */ break; } bb_error_msg_and_die("can't handle sections of type %ld", (long) sec->header.sh_type); } } } /* Do what sort of interpretation as needed by each section. */ shstrtab = f->sections[f->header.e_shstrndx]->contents; for (i = 0; i < shnum; ++i) { struct obj_section *sec = f->sections[i]; sec->name = shstrtab + sec->header.sh_name; } for (i = 0; i < shnum; ++i) { struct obj_section *sec = f->sections[i]; /* .modinfo should be contents only but gcc has no attribute for that. * The kernel may have marked .modinfo as ALLOC, ignore this bit. */ if (strcmp(sec->name, ".modinfo") == 0) sec->header.sh_flags &= ~SHF_ALLOC; if (sec->header.sh_flags & SHF_ALLOC) obj_insert_section_load_order(f, sec); switch (sec->header.sh_type) { case SHT_SYMTAB: { unsigned long nsym, j; char *strtab; ElfW(Sym) * sym; if (sec->header.sh_entsize != sizeof(ElfW(Sym))) { bb_error_msg_and_die("symbol size mismatch: %lu != %lu", (unsigned long) sec->header.sh_entsize, (unsigned long) sizeof(ElfW(Sym))); } nsym = sec->header.sh_size / sizeof(ElfW(Sym)); strtab = f->sections[sec->header.sh_link]->contents; sym = (ElfW(Sym) *) sec->contents; /* Allocate space for a table of local symbols. */ j = f->local_symtab_size = sec->header.sh_info; f->local_symtab = xzalloc(j * sizeof(struct obj_symbol *)); /* Insert all symbols into the hash table. */ for (j = 1, ++sym; j < nsym; ++j, ++sym) { ElfW(Addr) val = sym->st_value; const char *name; if (sym->st_name) name = strtab + sym->st_name; else if (sym->st_shndx < shnum) name = f->sections[sym->st_shndx]->name; else continue; #if defined(__SH5__) /* * For sh64 it is possible that the target of a branch * requires a mode switch (32 to 16 and back again). * * This is implied by the lsb being set in the target * address for SHmedia mode and clear for SHcompact. */ val |= sym->st_other & 4; #endif obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx, val, sym->st_size); } } break; case SHT_RELM: if (sec->header.sh_entsize != sizeof(ElfW(RelM))) { bb_error_msg_and_die("relocation entry size mismatch: %lu != %lu", (unsigned long) sec->header.sh_entsize, (unsigned long) sizeof(ElfW(RelM))); } break; /* XXX Relocation code from modutils-2.3.19 is not here. * Why? That's about 20 lines of code from obj/obj_load.c, * which gets done in a second pass through the sections. * This BusyBox insmod does similar work in obj_relocate(). */ } } return f; } #if ENABLE_FEATURE_INSMOD_LOADINKMEM /* * load the unloaded sections directly into the memory allocated by * kernel for the module */ static int obj_load_progbits(char *image, size_t image_size, struct obj_file *f, char *imagebase) { ElfW(Addr) base = f->baseaddr; struct obj_section* sec; for (sec = f->load_order; sec; sec = sec->load_next) { /* section already loaded? */ if (sec->contents != NULL) continue; if (sec->header.sh_size == 0) continue; sec->contents = imagebase + (sec->header.sh_addr - base); if (image_size < (sec->header.sh_offset + sec->header.sh_size)) { bb_error_msg("error reading ELF section data"); return 0; /* need to delete half-loaded module! */ } memcpy(sec->contents, image + sec->header.sh_offset, sec->header.sh_size); } return 1; } #endif static void hide_special_symbols(struct obj_file *f) { static const char *const specials[] = { SPFX "cleanup_module", SPFX "init_module", SPFX "kernel_version", NULL }; struct obj_symbol *sym; const char *const *p; for (p = specials; *p; ++p) { sym = obj_find_symbol(f, *p); if (sym != NULL) sym->info = ELF_ST_INFO(STB_LOCAL, ELF_ST_TYPE(sym->info)); } } #if ENABLE_FEATURE_CHECK_TAINTED_MODULE static int obj_gpl_license(struct obj_file *f, const char **license) { struct obj_section *sec; /* This list must match *exactly* the list of allowable licenses in * linux/include/linux/module.h. Checking for leading "GPL" will not * work, somebody will use "GPL sucks, this is proprietary". */ static const char *const gpl_licenses[] = { "GPL", "GPL v2", "GPL and additional rights", "Dual BSD/GPL", "Dual MPL/GPL" }; sec = obj_find_section(f, ".modinfo"); if (sec) { const char *value, *ptr, *endptr; ptr = sec->contents; endptr = ptr + sec->header.sh_size; while (ptr < endptr) { value = strchr(ptr, '='); if (value && strncmp(ptr, "license", value-ptr) == 0) { unsigned i; if (license) *license = value+1; for (i = 0; i < ARRAY_SIZE(gpl_licenses); ++i) { if (strcmp(value+1, gpl_licenses[i]) == 0) return 0; } return 2; } ptr = strchr(ptr, '\0'); if (ptr) ptr++; else ptr = endptr; } } return 1; } #define TAINT_FILENAME "/proc/sys/kernel/tainted" #define TAINT_PROPRIETORY_MODULE (1 << 0) #define TAINT_FORCED_MODULE (1 << 1) #define TAINT_UNSAFE_SMP (1 << 2) #define TAINT_URL "http://www.tux.org/lkml/#export-tainted" static void set_tainted(int fd, const char *m_name, int kernel_has_tainted, int taint, const char *text1, const char *text2) { static smallint printed_info; char buf[80]; int oldval; if (fd < 0 && !kernel_has_tainted) return; /* New modutils on old kernel */ printf("Warning: loading %s will taint the kernel: %s%s\n", m_name, text1, text2); if (!printed_info) { printf(" See %s for information about tainted modules\n", TAINT_URL); printed_info = 1; } if (fd >= 0) { read(fd, buf, sizeof(buf)-1); buf[sizeof(buf)-1] = '\0'; oldval = strtoul(buf, NULL, 10); sprintf(buf, "%d\n", oldval | taint); xwrite_str(fd, buf); } } /* Check if loading this module will taint the kernel. */ static void check_tainted_module(struct obj_file *f, const char *m_name) { int fd, kernel_has_tainted; const char *ptr; kernel_has_tainted = 1; fd = open(TAINT_FILENAME, O_RDWR); if (fd < 0) { if (errno == ENOENT) kernel_has_tainted = 0; else if (errno == EACCES) kernel_has_tainted = 1; else { bb_simple_perror_msg(TAINT_FILENAME); kernel_has_tainted = 0; } } switch (obj_gpl_license(f, &ptr)) { case 0: break; case 1: set_tainted(fd, m_name, kernel_has_tainted, TAINT_PROPRIETORY_MODULE, "no license", ""); break; default: /* case 2: */ /* The module has a non-GPL license so we pretend that the * kernel always has a taint flag to get a warning even on * kernels without the proc flag. */ set_tainted(fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "non-GPL license - ", ptr); break; } if (flag_force_load) set_tainted(fd, m_name, 1, TAINT_FORCED_MODULE, "forced load", ""); if (fd >= 0) close(fd); } #else /* !FEATURE_CHECK_TAINTED_MODULE */ #define check_tainted_module(x, y) do { } while (0); #endif #if ENABLE_FEATURE_INSMOD_KSYMOOPS_SYMBOLS /* add module source, timestamp, kernel version and a symbol for the * start of some sections. this info is used by ksymoops to do better * debugging. */ #if !ENABLE_FEATURE_INSMOD_VERSION_CHECKING #define get_module_version(f, str) get_module_version(str) #endif static int get_module_version(struct obj_file *f, char str[STRVERSIONLEN]) { #if ENABLE_FEATURE_INSMOD_VERSION_CHECKING return new_get_module_version(f, str); #else strncpy(str, "???", sizeof(str)); return -1; #endif } /* add module source, timestamp, kernel version and a symbol for the * start of some sections. this info is used by ksymoops to do better * debugging. */ static void add_ksymoops_symbols(struct obj_file *f, const char *filename, const char *m_name) { static const char symprefix[] ALIGN1 = "__insmod_"; static const char section_names[][8] = { ".text", ".rodata", ".data", ".bss", ".sbss" }; struct obj_section *sec; struct obj_symbol *sym; char *name, *absolute_filename; char str[STRVERSIONLEN]; unsigned i; int lm_name, lfilename, use_ksymtab, version; struct stat statbuf; /* WARNING: was using realpath, but replaced by readlink to stop using * lots of stack. But here it seems to be able to cause problems? */ absolute_filename = xmalloc_readlink(filename); if (!absolute_filename) absolute_filename = xstrdup(filename); lm_name = strlen(m_name); lfilename = strlen(absolute_filename); /* add to ksymtab if it already exists or there is no ksymtab and other symbols * are not to be exported. otherwise leave ksymtab alone for now, the * "export all symbols" compatibility code will export these symbols later. */ use_ksymtab = obj_find_section(f, "__ksymtab") || flag_noexport; sec = obj_find_section(f, ".this"); if (sec) { /* tag the module header with the object name, last modified * timestamp and module version. worst case for module version * is 0xffffff, decimal 16777215. putting all three fields in * one symbol is less readable but saves kernel space. */ if (stat(absolute_filename, &statbuf) != 0) statbuf.st_mtime = 0; version = get_module_version(f, str); /* -1 if not found */ name = xasprintf("%s%s_O%s_M%0*lX_V%d", symprefix, m_name, absolute_filename, (int)(2 * sizeof(statbuf.st_mtime)), (long)statbuf.st_mtime, version); sym = obj_add_symbol(f, name, -1, ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE), sec->idx, sec->header.sh_addr, 0); if (use_ksymtab) new_add_ksymtab(f, sym); } free(absolute_filename); #ifdef _NOT_SUPPORTED_ /* record where the persistent data is going, same address as previous symbol */ if (f->persist) { name = xasprintf("%s%s_P%s", symprefix, m_name, f->persist); sym = obj_add_symbol(f, name, -1, ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE), sec->idx, sec->header.sh_addr, 0); if (use_ksymtab) new_add_ksymtab(f, sym); } #endif /* tag the desired sections if size is non-zero */ for (i = 0; i < ARRAY_SIZE(section_names); ++i) { sec = obj_find_section(f, section_names[i]); if (sec && sec->header.sh_size) { name = xasprintf("%s%s_S%s_L%ld", symprefix, m_name, sec->name, (long)sec->header.sh_size); sym = obj_add_symbol(f, name, -1, ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE), sec->idx, sec->header.sh_addr, 0); if (use_ksymtab) new_add_ksymtab(f, sym); } } } #endif /* FEATURE_INSMOD_KSYMOOPS_SYMBOLS */ #if ENABLE_FEATURE_INSMOD_LOAD_MAP static void print_load_map(struct obj_file *f) { struct obj_section *sec; #if ENABLE_FEATURE_INSMOD_LOAD_MAP_FULL struct obj_symbol **all, **p; int i, nsyms; char *loaded; /* array of booleans */ struct obj_symbol *sym; #endif /* Report on the section layout. */ printf("Sections: Size %-*s Align\n", (int) (2 * sizeof(void *)), "Address"); for (sec = f->load_order; sec; sec = sec->load_next) { int a; unsigned long tmp; for (a = -1, tmp = sec->header.sh_addralign; tmp; ++a) tmp >>= 1; if (a == -1) a = 0; printf("%-15s %08lx %0*lx 2**%d\n", sec->name, (long)sec->header.sh_size, (int) (2 * sizeof(void *)), (long)sec->header.sh_addr, a); } #if ENABLE_FEATURE_INSMOD_LOAD_MAP_FULL /* Quick reference which section indices are loaded. */ i = f->header.e_shnum; loaded = alloca(i * sizeof(loaded[0])); while (--i >= 0) loaded[i] = ((f->sections[i]->header.sh_flags & SHF_ALLOC) != 0); /* Collect the symbols we'll be listing. */ for (nsyms = i = 0; i < HASH_BUCKETS; ++i) for (sym = f->symtab[i]; sym; sym = sym->next) if (sym->secidx <= SHN_HIRESERVE && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]) ) { ++nsyms; } all = alloca(nsyms * sizeof(all[0])); for (i = 0, p = all; i < HASH_BUCKETS; ++i) for (sym = f->symtab[i]; sym; sym = sym->next) if (sym->secidx <= SHN_HIRESERVE && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]) ) { *p++ = sym; } /* And list them. */ printf("\nSymbols:\n"); for (p = all; p < all + nsyms; ++p) { char type = '?'; unsigned long value; sym = *p; if (sym->secidx == SHN_ABS) { type = 'A'; value = sym->value; } else if (sym->secidx == SHN_UNDEF) { type = 'U'; value = 0; } else { sec = f->sections[sym->secidx]; if (sec->header.sh_type == SHT_NOBITS) type = 'B'; else if (sec->header.sh_flags & SHF_ALLOC) { if (sec->header.sh_flags & SHF_EXECINSTR) type = 'T'; else if (sec->header.sh_flags & SHF_WRITE) type = 'D'; else type = 'R'; } value = sym->value + sec->header.sh_addr; } if (ELF_ST_BIND(sym->info) == STB_LOCAL) type |= 0x20; /* tolower. safe for '?' too */ printf("%0*lx %c %s\n", (int) (2 * sizeof(void *)), value, type, sym->name); } #endif } #else /* !FEATURE_INSMOD_LOAD_MAP */ static void print_load_map(struct obj_file *f UNUSED_PARAM) { } #endif int FAST_FUNC bb_init_module_24(const char *m_filename, const char *options) { int k_crcs; unsigned long m_size; ElfW(Addr) m_addr; struct obj_file *f; int exit_status = EXIT_FAILURE; char *m_name; #if ENABLE_FEATURE_INSMOD_VERSION_CHECKING int m_has_modinfo; #endif char *image; size_t image_size; bool mmaped; image_size = INT_MAX - 4095; mmaped = 0; image = try_to_mmap_module(m_filename, &image_size); if (image) { mmaped = 1; } else { /* Load module into memory and unzip if compressed */ image = xmalloc_open_zipped_read_close(m_filename, &image_size); if (!image) return EXIT_FAILURE; } m_name = xstrdup(bb_basename(m_filename)); /* "module.o[.gz]" -> "module" */ *strchrnul(m_name, '.') = '\0'; f = obj_load(image, image_size, LOADBITS); #if ENABLE_FEATURE_INSMOD_VERSION_CHECKING /* Version correspondence? */ m_has_modinfo = (get_modinfo_value(f, "kernel_version") != NULL); if (!flag_quiet) { char m_strversion[STRVERSIONLEN]; struct utsname uts; if (m_has_modinfo) { int m_version = new_get_module_version(f, m_strversion); if (m_version == -1) { bb_error_msg_and_die("can't find the kernel version " "the module was compiled for"); } } uname(&uts); if (strncmp(uts.release, m_strversion, STRVERSIONLEN) != 0) { bb_error_msg("%skernel-module version mismatch\n" "\t%s was compiled for kernel version %s\n" "\twhile this kernel is version %s", flag_force_load ? "warning: " : "", m_name, m_strversion, uts.release); if (!flag_force_load) goto out; } } #endif if (query_module(NULL, 0, NULL, 0, NULL)) bb_error_msg_and_die("old (unsupported) kernel"); new_get_kernel_symbols(); k_crcs = new_is_kernel_checksummed(); #if ENABLE_FEATURE_INSMOD_VERSION_CHECKING { int m_crcs = 0; if (m_has_modinfo) m_crcs = new_is_module_checksummed(f); if (m_crcs != k_crcs) obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash); } #endif /* Let the module know about the kernel symbols. */ add_kernel_symbols(f); /* Allocate common symbols, symbol tables, and string tables. */ new_create_this_module(f, m_name); obj_check_undefineds(f); obj_allocate_commons(f); check_tainted_module(f, m_name); /* Done with the module name, on to the optional var=value arguments */ new_process_module_arguments(f, options); arch_create_got(f); hide_special_symbols(f); #if ENABLE_FEATURE_INSMOD_KSYMOOPS_SYMBOLS add_ksymoops_symbols(f, m_filename, m_name); #endif new_create_module_ksymtab(f); /* Find current size of the module */ m_size = obj_load_size(f); m_addr = create_module(m_name, m_size); if (m_addr == (ElfW(Addr))(-1)) switch (errno) { case EEXIST: bb_error_msg_and_die("a module named %s already exists", m_name); case ENOMEM: bb_error_msg_and_die("can't allocate kernel memory for module; needed %lu bytes", m_size); default: bb_perror_msg_and_die("create_module: %s", m_name); } #if !LOADBITS /* * the PROGBITS section was not loaded by the obj_load * now we can load them directly into the kernel memory */ if (!obj_load_progbits(image, image_size, f, (char*)m_addr)) { delete_module(m_name, 0); goto out; } #endif if (!obj_relocate(f, m_addr)) { delete_module(m_name, 0); goto out; } if (!new_init_module(m_name, f, m_size)) { delete_module(m_name, 0); goto out; } if (flag_print_load_map) print_load_map(f); exit_status = EXIT_SUCCESS; out: if (mmaped) munmap(image, image_size); else free(image); free(m_name); return exit_status; } busybox-1.22.1/modutils/insmod.c0000644000000000000000000000353412263563520015274 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini insmod implementation for busybox * * Copyright (C) 2008 Timo Teras * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //applet:IF_INSMOD(APPLET(insmod, BB_DIR_SBIN, BB_SUID_DROP)) #include "libbb.h" #include "modutils.h" /* 2.6 style insmod has no options and required filename * (not module name - .ko can't be omitted) */ //usage:#if !ENABLE_MODPROBE_SMALL //usage:#define insmod_trivial_usage //usage: IF_FEATURE_2_4_MODULES("[OPTIONS] MODULE ") //usage: IF_NOT_FEATURE_2_4_MODULES("FILE ") //usage: "[SYMBOL=VALUE]..." //usage:#define insmod_full_usage "\n\n" //usage: "Load the specified kernel modules into the kernel" //usage: IF_FEATURE_2_4_MODULES( "\n" //usage: "\n -f Force module to load into the wrong kernel version" //usage: "\n -k Make module autoclean-able" //usage: "\n -v Verbose" //usage: "\n -q Quiet" //usage: "\n -L Lock: prevent simultaneous loads" //usage: IF_FEATURE_INSMOD_LOAD_MAP( //usage: "\n -m Output load map to stdout" //usage: ) //usage: "\n -x Don't export externs" //usage: ) //usage:#endif int insmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int insmod_main(int argc UNUSED_PARAM, char **argv) { char *filename; int rc; /* Compat note: * 2.6 style insmod has no options and required filename * (not module name - .ko can't be omitted). * 2.4 style insmod can take module name without .o * and performs module search in default directories * or in $MODPATH. */ IF_FEATURE_2_4_MODULES( getopt32(argv, INSMOD_OPTS INSMOD_ARGS); argv += optind - 1; ); filename = *++argv; if (!filename) bb_show_usage(); rc = bb_init_module(filename, parse_cmdline_module_options(argv, /*quote_spaces:*/ 0)); if (rc) bb_error_msg("can't insert '%s': %s", filename, moderror(rc)); return rc; } busybox-1.22.1/modutils/modprobe-small.c0000644000000000000000000006126412263563520016724 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * simplified modprobe * * Copyright (c) 2008 Vladimir Dronnikov * Copyright (c) 2008 Bernhard Reutner-Fischer (initial depmod code) * * Licensed under GPLv2, see file LICENSE in this source tree. */ //applet:IF_MODPROBE_SMALL(APPLET(modprobe, BB_DIR_SBIN, BB_SUID_DROP)) //applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(depmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe)) //applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(insmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe)) //applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(lsmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe)) //applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(rmmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe)) #include "libbb.h" /* After libbb.h, since it needs sys/types.h on some systems */ #include /* uname() */ #include extern int init_module(void *module, unsigned long len, const char *options); extern int delete_module(const char *module, unsigned flags); extern int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret); #if 1 # define dbg1_error_msg(...) ((void)0) # define dbg2_error_msg(...) ((void)0) #else # define dbg1_error_msg(...) bb_error_msg(__VA_ARGS__) # define dbg2_error_msg(...) bb_error_msg(__VA_ARGS__) #endif #define DEPFILE_BB CONFIG_DEFAULT_DEPMOD_FILE".bb" enum { OPT_q = (1 << 0), /* be quiet */ OPT_r = (1 << 1), /* module removal instead of loading */ }; typedef struct module_info { char *pathname; char *aliases; char *deps; } module_info; /* * GLOBALS */ struct globals { module_info *modinfo; char *module_load_options; smallint dep_bb_seen; smallint wrote_dep_bb_ok; unsigned module_count; int module_found_idx; unsigned stringbuf_idx; unsigned stringbuf_size; char *stringbuf; /* some modules have lots of stuff */ /* for example, drivers/media/video/saa7134/saa7134.ko */ /* therefore having a fixed biggish buffer is not wise */ }; #define G (*ptr_to_globals) #define modinfo (G.modinfo ) #define dep_bb_seen (G.dep_bb_seen ) #define wrote_dep_bb_ok (G.wrote_dep_bb_ok ) #define module_count (G.module_count ) #define module_found_idx (G.module_found_idx ) #define module_load_options (G.module_load_options) #define stringbuf_idx (G.stringbuf_idx ) #define stringbuf_size (G.stringbuf_size ) #define stringbuf (G.stringbuf ) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ } while (0) static void append(const char *s) { unsigned len = strlen(s); if (stringbuf_idx + len + 15 > stringbuf_size) { stringbuf_size = stringbuf_idx + len + 127; dbg2_error_msg("grow stringbuf to %u", stringbuf_size); stringbuf = xrealloc(stringbuf, stringbuf_size); } memcpy(stringbuf + stringbuf_idx, s, len); stringbuf_idx += len; } static void appendc(char c) { /* We appendc() only after append(), + 15 trick in append() * makes it unnecessary to check for overflow here */ stringbuf[stringbuf_idx++] = c; } static void bksp(void) { if (stringbuf_idx) stringbuf_idx--; } static void reset_stringbuf(void) { stringbuf_idx = 0; } static char* copy_stringbuf(void) { char *copy = xzalloc(stringbuf_idx + 1); /* terminating NUL */ return memcpy(copy, stringbuf, stringbuf_idx); } static char* find_keyword(char *ptr, size_t len, const char *word) { int wlen; if (!ptr) /* happens if xmalloc_open_zipped_read_close cannot read it */ return NULL; wlen = strlen(word); len -= wlen - 1; while ((ssize_t)len > 0) { char *old = ptr; /* search for the first char in word */ ptr = memchr(ptr, *word, len); if (ptr == NULL) /* no occurance left, done */ break; if (strncmp(ptr, word, wlen) == 0) return ptr + wlen; /* found, return ptr past it */ ++ptr; len -= (ptr - old); } return NULL; } static void replace(char *s, char what, char with) { while (*s) { if (what == *s) *s = with; ++s; } } /* Take "word word", return malloced "word",NUL,"word",NUL,NUL */ static char* str_2_list(const char *str) { int len = strlen(str) + 1; char *dst = xmalloc(len + 1); dst[len] = '\0'; memcpy(dst, str, len); //TODO: protect against 2+ spaces: "word word" replace(dst, ' ', '\0'); return dst; } /* We use error numbers in a loose translation... */ static const char *moderror(int err) { switch (err) { case ENOEXEC: return "invalid module format"; case ENOENT: return "unknown symbol in module or invalid parameter"; case ESRCH: return "module has wrong symbol version"; case EINVAL: /* "invalid parameter" */ return "unknown symbol in module or invalid parameter" + sizeof("unknown symbol in module or"); default: return strerror(err); } } static int load_module(const char *fname, const char *options) { #if 1 int r; size_t len = MAXINT(ssize_t); char *module_image; dbg1_error_msg("load_module('%s','%s')", fname, options); module_image = xmalloc_open_zipped_read_close(fname, &len); r = (!module_image || init_module(module_image, len, options ? options : "") != 0); free(module_image); dbg1_error_msg("load_module:%d", r); return r; /* 0 = success */ #else /* For testing */ dbg1_error_msg("load_module('%s','%s')", fname, options); return 1; #endif } static void parse_module(module_info *info, const char *pathname) { char *module_image; char *ptr; size_t len; size_t pos; dbg1_error_msg("parse_module('%s')", pathname); /* Read (possibly compressed) module */ len = 64 * 1024 * 1024; /* 64 Mb at most */ module_image = xmalloc_open_zipped_read_close(pathname, &len); /* module_image == NULL is ok here, find_keyword handles it */ //TODO: optimize redundant module body reads /* "alias1 symbol:sym1 alias2 symbol:sym2" */ reset_stringbuf(); pos = 0; while (1) { ptr = find_keyword(module_image + pos, len - pos, "alias="); if (!ptr) { ptr = find_keyword(module_image + pos, len - pos, "__ksymtab_"); if (!ptr) break; /* DOCME: __ksymtab_gpl and __ksymtab_strings occur * in many modules. What do they mean? */ if (strcmp(ptr, "gpl") == 0 || strcmp(ptr, "strings") == 0) goto skip; dbg2_error_msg("alias:'symbol:%s'", ptr); append("symbol:"); } else { dbg2_error_msg("alias:'%s'", ptr); } append(ptr); appendc(' '); skip: pos = (ptr - module_image); } bksp(); /* remove last ' ' */ info->aliases = copy_stringbuf(); replace(info->aliases, '-', '_'); /* "dependency1 depandency2" */ reset_stringbuf(); ptr = find_keyword(module_image, len, "depends="); if (ptr && *ptr) { replace(ptr, ',', ' '); replace(ptr, '-', '_'); dbg2_error_msg("dep:'%s'", ptr); append(ptr); } info->deps = copy_stringbuf(); free(module_image); } static int pathname_matches_modname(const char *pathname, const char *modname) { const char *fname = bb_get_last_path_component_nostrip(pathname); const char *suffix = strrstr(fname, ".ko"); //TODO: can do without malloc? char *name = xstrndup(fname, suffix - fname); int r; replace(name, '-', '_'); r = (strcmp(name, modname) == 0); free(name); return r; } static FAST_FUNC int fileAction(const char *pathname, struct stat *sb UNUSED_PARAM, void *modname_to_match, int depth UNUSED_PARAM) { int cur; const char *fname; pathname += 2; /* skip "./" */ fname = bb_get_last_path_component_nostrip(pathname); if (!strrstr(fname, ".ko")) { dbg1_error_msg("'%s' is not a module", pathname); return TRUE; /* not a module, continue search */ } cur = module_count++; modinfo = xrealloc_vector(modinfo, 12, cur); modinfo[cur].pathname = xstrdup(pathname); /*modinfo[cur].aliases = NULL; - xrealloc_vector did it */ /*modinfo[cur+1].pathname = NULL;*/ if (!pathname_matches_modname(fname, modname_to_match)) { dbg1_error_msg("'%s' module name doesn't match", pathname); return TRUE; /* module name doesn't match, continue search */ } dbg1_error_msg("'%s' module name matches", pathname); module_found_idx = cur; parse_module(&modinfo[cur], pathname); if (!(option_mask32 & OPT_r)) { if (load_module(pathname, module_load_options) == 0) { /* Load was successful, there is nothing else to do. * This can happen ONLY for "top-level" module load, * not a dep, because deps dont do dirscan. */ exit(EXIT_SUCCESS); } } return TRUE; } static int load_dep_bb(void) { char *line; FILE *fp = fopen_for_read(DEPFILE_BB); if (!fp) return 0; dep_bb_seen = 1; dbg1_error_msg("loading "DEPFILE_BB); /* Why? There is a rare scenario: we did not find modprobe.dep.bb, * we scanned the dir and found no module by name, then we search * for alias (full scan), and we decided to generate modprobe.dep.bb. * But we see modprobe.dep.bb.new! Other modprobe is at work! * We wait and other modprobe renames it to modprobe.dep.bb. * Now we can use it. * But we already have modinfo[] filled, and "module_count = 0" * makes us start anew. Yes, we leak modinfo[].xxx pointers - * there is not much of data there anyway. */ module_count = 0; memset(&modinfo[0], 0, sizeof(modinfo[0])); while ((line = xmalloc_fgetline(fp)) != NULL) { char* space; char* linebuf; int cur; if (!line[0]) { free(line); continue; } space = strchrnul(line, ' '); cur = module_count++; modinfo = xrealloc_vector(modinfo, 12, cur); /*modinfo[cur+1].pathname = NULL; - xrealloc_vector did it */ modinfo[cur].pathname = line; /* we take ownership of malloced block here */ if (*space) *space++ = '\0'; modinfo[cur].aliases = space; linebuf = xmalloc_fgetline(fp); modinfo[cur].deps = linebuf ? linebuf : xzalloc(1); if (modinfo[cur].deps[0]) { /* deps are not "", so next line must be empty */ line = xmalloc_fgetline(fp); /* Refuse to work with damaged config file */ if (line && line[0]) bb_error_msg_and_die("error in %s at '%s'", DEPFILE_BB, line); free(line); } } return 1; } static int start_dep_bb_writeout(void) { int fd; /* depmod -n: write result to stdout */ if (applet_name[0] == 'd' && (option_mask32 & 1)) return STDOUT_FILENO; fd = open(DEPFILE_BB".new", O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644); if (fd < 0) { if (errno == EEXIST) { int count = 5 * 20; dbg1_error_msg(DEPFILE_BB".new exists, waiting for "DEPFILE_BB); while (1) { usleep(1000*1000 / 20); if (load_dep_bb()) { dbg1_error_msg(DEPFILE_BB" appeared"); return -2; /* magic number */ } if (!--count) break; } bb_error_msg("deleting stale %s", DEPFILE_BB".new"); fd = open_or_warn(DEPFILE_BB".new", O_WRONLY | O_CREAT | O_TRUNC); } } dbg1_error_msg("opened "DEPFILE_BB".new:%d", fd); return fd; } static void write_out_dep_bb(int fd) { int i; FILE *fp; /* We want good error reporting. fdprintf is not good enough. */ fp = xfdopen_for_write(fd); i = 0; while (modinfo[i].pathname) { fprintf(fp, "%s%s%s\n" "%s%s\n", modinfo[i].pathname, modinfo[i].aliases[0] ? " " : "", modinfo[i].aliases, modinfo[i].deps, modinfo[i].deps[0] ? "\n" : ""); i++; } /* Badly formatted depfile is a no-no. Be paranoid. */ errno = 0; if (ferror(fp) | fclose(fp)) /* | instead of || is intended */ goto err; if (fd == STDOUT_FILENO) /* it was depmod -n */ goto ok; if (rename(DEPFILE_BB".new", DEPFILE_BB) != 0) { err: bb_perror_msg("can't create '%s'", DEPFILE_BB); unlink(DEPFILE_BB".new"); } else { ok: wrote_dep_bb_ok = 1; dbg1_error_msg("created "DEPFILE_BB); } } static module_info* find_alias(const char *alias) { int i; int dep_bb_fd; module_info *result; dbg1_error_msg("find_alias('%s')", alias); try_again: /* First try to find by name (cheaper) */ i = 0; while (modinfo[i].pathname) { if (pathname_matches_modname(modinfo[i].pathname, alias)) { dbg1_error_msg("found '%s' in module '%s'", alias, modinfo[i].pathname); if (!modinfo[i].aliases) { parse_module(&modinfo[i], modinfo[i].pathname); } return &modinfo[i]; } i++; } /* Ok, we definitely have to scan module bodies. This is a good * moment to generate modprobe.dep.bb, if it does not exist yet */ dep_bb_fd = dep_bb_seen ? -1 : start_dep_bb_writeout(); if (dep_bb_fd == -2) /* modprobe.dep.bb appeared? */ goto try_again; /* Scan all module bodies, extract modinfo (it contains aliases) */ i = 0; result = NULL; while (modinfo[i].pathname) { char *desc, *s; if (!modinfo[i].aliases) { parse_module(&modinfo[i], modinfo[i].pathname); } if (result) { i++; continue; } /* "alias1 symbol:sym1 alias2 symbol:sym2" */ desc = str_2_list(modinfo[i].aliases); /* Does matching substring exist? */ for (s = desc; *s; s += strlen(s) + 1) { /* Aliases in module bodies can be defined with * shell patterns. Example: * "pci:v000010DEd000000D9sv*sd*bc*sc*i*". * Plain strcmp() won't catch that */ if (fnmatch(s, alias, 0) == 0) { dbg1_error_msg("found alias '%s' in module '%s'", alias, modinfo[i].pathname); result = &modinfo[i]; break; } } free(desc); if (result && dep_bb_fd < 0) return result; i++; } /* Create module.dep.bb if needed */ if (dep_bb_fd >= 0) { write_out_dep_bb(dep_bb_fd); } dbg1_error_msg("find_alias '%s' returns %p", alias, result); return result; } #if ENABLE_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED // TODO: open only once, invent config_rewind() static int already_loaded(const char *name) { int ret = 0; char *s; parser_t *parser = config_open2("/proc/modules", xfopen_for_read); while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) { if (strcmp(s, name) == 0) { ret = 1; break; } } config_close(parser); return ret; } #else #define already_loaded(name) is_rmmod #endif /* * Given modules definition and module name (or alias, or symbol) * load/remove the module respecting dependencies. * NB: also called by depmod with bogus name "/", * just in order to force modprobe.dep.bb creation. */ #if !ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE #define process_module(a,b) process_module(a) #define cmdline_options "" #endif static void process_module(char *name, const char *cmdline_options) { char *s, *deps, *options; module_info *info; int is_rmmod = (option_mask32 & OPT_r) != 0; dbg1_error_msg("process_module('%s','%s')", name, cmdline_options); replace(name, '-', '_'); dbg1_error_msg("already_loaded:%d is_rmmod:%d", already_loaded(name), is_rmmod); if (already_loaded(name) != is_rmmod) { dbg1_error_msg("nothing to do for '%s'", name); return; } options = NULL; if (!is_rmmod) { char *opt_filename = xasprintf("/etc/modules/%s", name); options = xmalloc_open_read_close(opt_filename, NULL); if (options) replace(options, '\n', ' '); #if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE if (cmdline_options) { /* NB: cmdline_options always have one leading ' ' * (see main()), we remove it here */ char *op = xasprintf(options ? "%s %s" : "%s %s" + 3, cmdline_options + 1, options); free(options); options = op; } #endif free(opt_filename); module_load_options = options; dbg1_error_msg("process_module('%s'): options:'%s'", name, options); } if (!module_count) { /* Scan module directory. This is done only once. * It will attempt module load, and will exit(EXIT_SUCCESS) * on success. */ module_found_idx = -1; recursive_action(".", ACTION_RECURSE, /* flags */ fileAction, /* file action */ NULL, /* dir action */ name, /* user data */ 0); /* depth */ dbg1_error_msg("dirscan complete"); /* Module was not found, or load failed, or is_rmmod */ if (module_found_idx >= 0) { /* module was found */ info = &modinfo[module_found_idx]; } else { /* search for alias, not a plain module name */ info = find_alias(name); } } else { info = find_alias(name); } // Problem here: there can be more than one module // for the given alias. For example, // "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches // ata_piix because it has an alias "pci:v00008086d00007010sv*sd*bc*sc*i*" // and ata_generic, it has an alias "alias=pci:v*d*sv*sd*bc01sc01i*" // Standard modprobe would load them both. // In this code, find_alias() returns only the first matching module. /* rmmod? unload it by name */ if (is_rmmod) { if (delete_module(name, O_NONBLOCK | O_EXCL) != 0) { if (!(option_mask32 & OPT_q)) bb_perror_msg("remove '%s'", name); goto ret; } if (applet_name[0] == 'r') { /* rmmod: do not remove dependencies, exit */ goto ret; } /* modprobe -r: we do not stop here - * continue to unload modules on which the module depends: * "-r --remove: option causes modprobe to remove a module. * If the modules it depends on are also unused, modprobe * will try to remove them, too." */ } if (!info) { /* both dirscan and find_alias found nothing */ if (!is_rmmod && applet_name[0] != 'd') /* it wasn't rmmod or depmod */ bb_error_msg("module '%s' not found", name); //TODO: _and_die()? or should we continue (un)loading modules listed on cmdline? goto ret; } /* Iterate thru dependencies, trying to (un)load them */ deps = str_2_list(info->deps); for (s = deps; *s; s += strlen(s) + 1) { //if (strcmp(name, s) != 0) // N.B. do loops exist? dbg1_error_msg("recurse on dep '%s'", s); process_module(s, NULL); dbg1_error_msg("recurse on dep '%s' done", s); } free(deps); /* modprobe -> load it */ if (!is_rmmod) { if (!options || strstr(options, "blacklist") == NULL) { errno = 0; if (load_module(info->pathname, options) != 0) { if (EEXIST != errno) { bb_error_msg("'%s': %s", info->pathname, moderror(errno)); } else { dbg1_error_msg("'%s': %s", info->pathname, moderror(errno)); } } } else { dbg1_error_msg("'%s': blacklisted", info->pathname); } } ret: free(options); //TODO: return load attempt result from process_module. //If dep didn't load ok, continuing makes little sense. } #undef cmdline_options /* For reference, module-init-tools v3.4 options: # insmod Usage: insmod filename [args] # rmmod --help Usage: rmmod [-fhswvV] modulename ... -f (or --force) forces a module unload, and may crash your machine. This requires the Forced Module Removal option when the kernel was compiled. -h (or --help) prints this help text -s (or --syslog) says use syslog, not stderr -v (or --verbose) enables more messages -V (or --version) prints the version code -w (or --wait) begins module removal even if it is used and will stop new users from accessing the module (so it should eventually fall to zero). # modprobe Usage: modprobe [-v] [-V] [-C config-file] [-n] [-i] [-q] [-b] [-o ] [ --dump-modversions ] [parameters...] modprobe -r [-n] [-i] [-v] ... modprobe -l -t [ -a ...] # depmod --help depmod 3.4 -- part of module-init-tools depmod -[aA] [-n -e -v -q -V -r -u] [-b basedirectory] [forced_version] depmod [-n -e -v -q -r -u] [-F kernelsyms] module1.ko module2.ko ... If no arguments (except options) are given, "depmod -a" is assumed. depmod will output a dependency list suitable for the modprobe utility. Options: -a, --all Probe all modules -A, --quick Only does the work if there's a new module -n, --show Write the dependency file on stdout only -e, --errsyms Report not supplied symbols -V, --version Print the release version -v, --verbose Enable verbose mode -h, --help Print this usage message The following options are useful for people managing distributions: -b basedirectory --basedir basedirectory Use an image of a module tree -F kernelsyms --filesyms kernelsyms Use the file instead of the current kernel symbols */ //usage:#if ENABLE_MODPROBE_SMALL //// Note: currently, help system shows modprobe --help text for all aliased cmds //// (see APPLET_ODDNAME macro definition). //// All other help texts defined below are not used. FIXME? //usage:#define depmod_trivial_usage NOUSAGE_STR //usage:#define depmod_full_usage "" //usage:#define lsmod_trivial_usage //usage: "" //usage:#define lsmod_full_usage "\n\n" //usage: "List the currently loaded kernel modules" //usage:#define insmod_trivial_usage //usage: IF_FEATURE_2_4_MODULES("[OPTIONS] MODULE ") //usage: IF_NOT_FEATURE_2_4_MODULES("FILE ") //usage: "[SYMBOL=VALUE]..." //usage:#define insmod_full_usage "\n\n" //usage: "Load the specified kernel modules into the kernel" //usage: IF_FEATURE_2_4_MODULES( "\n" //usage: "\n -f Force module to load into the wrong kernel version" //usage: "\n -k Make module autoclean-able" //usage: "\n -v Verbose" //usage: "\n -q Quiet" //usage: "\n -L Lock: prevent simultaneous loads" //usage: IF_FEATURE_INSMOD_LOAD_MAP( //usage: "\n -m Output load map to stdout" //usage: ) //usage: "\n -x Don't export externs" //usage: ) //usage:#define rmmod_trivial_usage //usage: "[-wfa] [MODULE]..." //usage:#define rmmod_full_usage "\n\n" //usage: "Unload kernel modules\n" //usage: "\n -w Wait until the module is no longer used" //usage: "\n -f Force unload" //usage: "\n -a Remove all unused modules (recursively)" //usage: //usage:#define rmmod_example_usage //usage: "$ rmmod tulip\n" //usage:#define modprobe_trivial_usage //usage: "[-qfwrsv] MODULE [symbol=value]..." //usage:#define modprobe_full_usage "\n\n" //usage: " -r Remove MODULE (stacks) or do autoclean" //usage: "\n -q Quiet" //usage: "\n -v Verbose" //usage: "\n -f Force" //usage: "\n -w Wait for unload" //usage: "\n -s Report via syslog instead of stderr" //usage:#endif int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int modprobe_main(int argc UNUSED_PARAM, char **argv) { struct utsname uts; char applet0 = applet_name[0]; IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(char *options;) /* are we lsmod? -> just dump /proc/modules */ if ('l' == applet0) { xprint_and_close_file(xfopen_for_read("/proc/modules")); return EXIT_SUCCESS; } INIT_G(); /* Prevent ugly corner cases with no modules at all */ modinfo = xzalloc(sizeof(modinfo[0])); if ('i' != applet0) { /* not insmod */ /* Goto modules directory */ xchdir(CONFIG_DEFAULT_MODULES_DIR); } uname(&uts); /* never fails */ /* depmod? */ if ('d' == applet0) { /* Supported: * -n: print result to stdout * -a: process all modules (default) * optional VERSION parameter * Ignored: * -A: do work only if a module is newer than depfile * -e: report any symbols which a module needs * which are not supplied by other modules or the kernel * -F FILE: System.map (symbols for -e) * -q, -r, -u: noop? * Not supported: * -b BASEDIR: (TODO!) modules are in * $BASEDIR/lib/modules/$VERSION * -v: human readable deps to stdout * -V: version (don't want to support it - people may depend * on it as an indicator of "standard" depmod) * -h: help (well duh) * module1.o module2.o parameters (just ignored for now) */ getopt32(argv, "na" "AeF:qru" /* "b:vV", NULL */, NULL); argv += optind; /* if (argv[0] && argv[1]) bb_show_usage(); */ /* Goto $VERSION directory */ xchdir(argv[0] ? argv[0] : uts.release); /* Force full module scan by asking to find a bogus module. * This will generate modules.dep.bb as a side effect. */ process_module((char*)"/", NULL); return !wrote_dep_bb_ok; } /* insmod, modprobe, rmmod require at least one argument */ opt_complementary = "-1"; /* only -q (quiet) and -r (rmmod), * the rest are accepted and ignored (compat) */ getopt32(argv, "qrfsvwb"); argv += optind; /* are we rmmod? -> simulate modprobe -r */ if ('r' == applet0) { option_mask32 |= OPT_r; } if ('i' != applet0) { /* not insmod */ /* Goto $VERSION directory */ xchdir(uts.release); } #if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE /* If not rmmod, parse possible module options given on command line. * insmod/modprobe takes one module name, the rest are parameters. */ options = NULL; if ('r' != applet0) { char **arg = argv; while (*++arg) { /* Enclose options in quotes */ char *s = options; options = xasprintf("%s \"%s\"", s ? s : "", *arg); free(s); *arg = NULL; } } #else if ('r' != applet0) argv[1] = NULL; #endif if ('i' == applet0) { /* insmod */ size_t len; void *map; len = MAXINT(ssize_t); map = xmalloc_open_zipped_read_close(*argv, &len); if (!map) bb_perror_msg_and_die("can't read '%s'", *argv); if (init_module(map, len, IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(options ? options : "") IF_NOT_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE("") ) != 0 ) { bb_error_msg_and_die("can't insert '%s': %s", *argv, moderror(errno)); } return 0; } /* Try to load modprobe.dep.bb */ load_dep_bb(); /* Load/remove modules. * Only rmmod loops here, modprobe has only argv[0] */ do { process_module(*argv, options); } while (*++argv); if (ENABLE_FEATURE_CLEAN_UP) { IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(free(options);) } return EXIT_SUCCESS; } busybox-1.22.1/selinux/0000755000000000000000000000000012320365364013461 5ustar rootrootbusybox-1.22.1/selinux/load_policy.c0000644000000000000000000000105512263563520016124 0ustar rootroot/* * load_policy * Author: Yuichi Nakamura * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define load_policy_trivial_usage NOUSAGE_STR //usage:#define load_policy_full_usage "" #include "libbb.h" int load_policy_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int load_policy_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int rc; if (argv[1]) { bb_show_usage(); } rc = selinux_mkload_policy(1); if (rc < 0) { bb_perror_msg_and_die("can't load policy"); } return 0; } busybox-1.22.1/selinux/chcon.c0000644000000000000000000001472012263563520014723 0ustar rootroot/* * chcon -- change security context, based on coreutils-5.97-13 * * Port to busybox: KaiGai Kohei * * Copyright (C) 2006 - 2007 KaiGai Kohei * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define chcon_trivial_usage //usage: "[OPTIONS] CONTEXT FILE..." //usage: "\n chcon [OPTIONS] [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE..." //usage: IF_FEATURE_CHCON_LONG_OPTIONS( //usage: "\n chcon [OPTIONS] --reference=RFILE FILE..." //usage: ) //usage:#define chcon_full_usage "\n\n" //usage: "Change the security context of each FILE to CONTEXT\n" //usage: IF_FEATURE_CHCON_LONG_OPTIONS( //usage: "\n -v,--verbose Verbose" //usage: "\n -c,--changes Report changes made" //usage: "\n -h,--no-dereference Affect symlinks instead of their targets" //usage: "\n -f,--silent,--quiet Suppress most error messages" //usage: "\n --reference=RFILE Use RFILE's group instead of using a CONTEXT value" //usage: "\n -u,--user=USER Set user/role/type/range in the target" //usage: "\n -r,--role=ROLE security context" //usage: "\n -t,--type=TYPE" //usage: "\n -l,--range=RANGE" //usage: "\n -R,--recursive Recurse" //usage: ) //usage: IF_NOT_FEATURE_CHCON_LONG_OPTIONS( //usage: "\n -v Verbose" //usage: "\n -c Report changes made" //usage: "\n -h Affect symlinks instead of their targets" //usage: "\n -f Suppress most error messages" //usage: "\n -u USER Set user/role/type/range in the target security context" //usage: "\n -r ROLE" //usage: "\n -t TYPE" //usage: "\n -l RNG" //usage: "\n -R Recurse" //usage: ) #include #include "libbb.h" #define OPT_RECURSIVE (1<<0) /* 'R' */ #define OPT_CHANHES (1<<1) /* 'c' */ #define OPT_NODEREFERENCE (1<<2) /* 'h' */ #define OPT_QUIET (1<<3) /* 'f' */ #define OPT_USER (1<<4) /* 'u' */ #define OPT_ROLE (1<<5) /* 'r' */ #define OPT_TYPE (1<<6) /* 't' */ #define OPT_RANGE (1<<7) /* 'l' */ #define OPT_VERBOSE (1<<8) /* 'v' */ #define OPT_REFERENCE ((1<<9) * ENABLE_FEATURE_CHCON_LONG_OPTIONS) #define OPT_COMPONENT_SPECIFIED (OPT_USER | OPT_ROLE | OPT_TYPE | OPT_RANGE) static char *user = NULL; static char *role = NULL; static char *type = NULL; static char *range = NULL; static char *specified_context = NULL; static int FAST_FUNC change_filedir_context( const char *fname, struct stat *stbuf UNUSED_PARAM, void *userData UNUSED_PARAM, int depth UNUSED_PARAM) { context_t context = NULL; security_context_t file_context = NULL; security_context_t context_string; int rc = FALSE; int status = 0; if (option_mask32 & OPT_NODEREFERENCE) { status = lgetfilecon(fname, &file_context); } else { status = getfilecon(fname, &file_context); } if (status < 0 && errno != ENODATA) { if ((option_mask32 & OPT_QUIET) == 0) bb_error_msg("can't obtain security context: %s", fname); goto skip; } if (file_context == NULL && specified_context == NULL) { bb_error_msg("can't apply partial context to unlabeled file %s", fname); goto skip; } if (specified_context == NULL) { context = set_security_context_component(file_context, user, role, type, range); if (!context) { bb_error_msg("can't compute security context from %s", file_context); goto skip; } } else { context = context_new(specified_context); if (!context) { bb_error_msg("invalid context: %s", specified_context); goto skip; } } context_string = context_str(context); if (!context_string) { bb_error_msg("can't obtain security context in text expression"); goto skip; } if (file_context == NULL || strcmp(context_string, file_context) != 0) { int fail; if (option_mask32 & OPT_NODEREFERENCE) { fail = lsetfilecon(fname, context_string); } else { fail = setfilecon(fname, context_string); } if ((option_mask32 & OPT_VERBOSE) || ((option_mask32 & OPT_CHANHES) && !fail)) { printf(!fail ? "context of %s changed to %s\n" : "can't change context of %s to %s\n", fname, context_string); } if (!fail) { rc = TRUE; } else if ((option_mask32 & OPT_QUIET) == 0) { bb_error_msg("can't change context of %s to %s", fname, context_string); } } else if (option_mask32 & OPT_VERBOSE) { printf("context of %s retained as %s\n", fname, context_string); rc = TRUE; } skip: context_free(context); freecon(file_context); return rc; } #if ENABLE_FEATURE_CHCON_LONG_OPTIONS static const char chcon_longopts[] ALIGN1 = "recursive\0" No_argument "R" "changes\0" No_argument "c" "no-dereference\0" No_argument "h" "silent\0" No_argument "f" "quiet\0" No_argument "f" "user\0" Required_argument "u" "role\0" Required_argument "r" "type\0" Required_argument "t" "range\0" Required_argument "l" "verbose\0" No_argument "v" "reference\0" Required_argument "\xff" /* no short option */ ; #endif int chcon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int chcon_main(int argc UNUSED_PARAM, char **argv) { char *reference_file; char *fname; int i, errors = 0; #if ENABLE_FEATURE_CHCON_LONG_OPTIONS applet_long_options = chcon_longopts; #endif opt_complementary = "-1" /* at least 1 param */ ":?" /* error if exclusivity constraints are violated */ #if ENABLE_FEATURE_CHCON_LONG_OPTIONS ":\xff--urtl:u--\xff:r--\xff:t--\xff:l--\xff" #endif ":f--v:v--f"; /* 'verbose' and 'quiet' are exclusive */ getopt32(argv, "Rchfu:r:t:l:v", &user, &role, &type, &range, &reference_file); argv += optind; #if ENABLE_FEATURE_CHCON_LONG_OPTIONS if (option_mask32 & OPT_REFERENCE) { /* FIXME: lgetfilecon() should be used when '-h' is specified. * But current implementation follows the original one. */ if (getfilecon(reference_file, &specified_context) < 0) bb_perror_msg_and_die("getfilecon('%s') failed", reference_file); } else #endif if ((option_mask32 & OPT_COMPONENT_SPECIFIED) == 0) { specified_context = *argv++; /* specified_context is never NULL - * "-1" in opt_complementary prevents this. */ if (!argv[0]) bb_error_msg_and_die("too few arguments"); } for (i = 0; (fname = argv[i]) != NULL; i++) { int fname_len = strlen(fname); while (fname_len > 1 && fname[fname_len - 1] == '/') fname_len--; fname[fname_len] = '\0'; if (recursive_action(fname, 1< * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define selinuxenabled_trivial_usage NOUSAGE_STR //usage:#define selinuxenabled_full_usage "" #include "libbb.h" int selinuxenabled_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int selinuxenabled_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { return !is_selinux_enabled(); } busybox-1.22.1/selinux/Config.src0000644000000000000000000000502312263563520015377 0ustar rootroot# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "SELinux Utilities" depends on SELINUX INSERT config CHCON bool "chcon" default n depends on SELINUX help Enable support to change the security context of file. config FEATURE_CHCON_LONG_OPTIONS bool "Enable long options" default y depends on CHCON && LONG_OPTS help Support long options for the chcon applet. config GETENFORCE bool "getenforce" default n depends on SELINUX help Enable support to get the current mode of SELinux. config GETSEBOOL bool "getsebool" default n depends on SELINUX help Enable support to get SELinux boolean values. config LOAD_POLICY bool "load_policy" default n depends on SELINUX help Enable support to load SELinux policy. config MATCHPATHCON bool "matchpathcon" default n depends on SELINUX help Enable support to get default security context of the specified path from the file contexts configuration. config RESTORECON bool "restorecon" default n depends on SELINUX help Enable support to relabel files. The feature is almost the same as setfiles, but usage is a little different. config RUNCON bool "runcon" default n depends on SELINUX help Enable support to run command in speficied security context. config FEATURE_RUNCON_LONG_OPTIONS bool "Enable long options" default y depends on RUNCON && LONG_OPTS help Support long options for the runcon applet. config SELINUXENABLED bool "selinuxenabled" default n depends on SELINUX help Enable support for this command to be used within shell scripts to determine if selinux is enabled. config SETENFORCE bool "setenforce" default n depends on SELINUX help Enable support to modify the mode SELinux is running in. config SETFILES bool "setfiles" default n depends on SELINUX help Enable support to modify to relabel files. Notice: If you built libselinux with -D_FILE_OFFSET_BITS=64, (It is default in libselinux's Makefile), you _must_ enable CONFIG_LFS. config FEATURE_SETFILES_CHECK_OPTION bool "Enable check option" default n depends on SETFILES help Support "-c" option (check the validity of the contexts against the specified binary policy) for setfiles. Requires libsepol. config SETSEBOOL bool "setsebool" default n depends on SELINUX help Enable support for change boolean. semanage and -P option is not supported yet. config SESTATUS bool "sestatus" default n depends on SELINUX help Displays the status of SELinux. endmenu busybox-1.22.1/selinux/matchpathcon.c0000644000000000000000000000511112263563520016274 0ustar rootroot/* matchpathcon - get the default security context for the specified * path from the file contexts configuration. * based on libselinux-1.32 * Port to busybox: KaiGai Kohei * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define matchpathcon_trivial_usage //usage: "[-n] [-N] [-f file_contexts_file] [-p prefix] [-V]" //usage:#define matchpathcon_full_usage "\n\n" //usage: " -n Don't display path" //usage: "\n -N Don't use translations" //usage: "\n -f Use alternate file_context file" //usage: "\n -p Use prefix to speed translations" //usage: "\n -V Verify file context on disk matches defaults" #include "libbb.h" static int print_matchpathcon(char *path, int noprint) { char *buf; int rc = matchpathcon(path, 0, &buf); if (rc < 0) { bb_perror_msg("matchpathcon(%s) failed", path); return 1; } if (!noprint) printf("%s\t%s\n", path, buf); else puts(buf); freecon(buf); return 0; } #define OPT_NOT_PRINT (1<<0) /* -n */ #define OPT_NOT_TRANS (1<<1) /* -N */ #define OPT_FCONTEXT (1<<2) /* -f */ #define OPT_PREFIX (1<<3) /* -p */ #define OPT_VERIFY (1<<4) /* -V */ int matchpathcon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int matchpathcon_main(int argc UNUSED_PARAM, char **argv) { int error = 0; unsigned opts; char *fcontext, *prefix, *path; opt_complementary = "-1" /* at least one param reqd */ ":?:f--p:p--f"; /* mutually exclusive */ opts = getopt32(argv, "nNf:p:V", &fcontext, &prefix); argv += optind; if (opts & OPT_NOT_TRANS) { set_matchpathcon_flags(MATCHPATHCON_NOTRANS); } if (opts & OPT_FCONTEXT) { if (matchpathcon_init(fcontext)) bb_perror_msg_and_die("error while processing %s", fcontext); } if (opts & OPT_PREFIX) { if (matchpathcon_init_prefix(NULL, prefix)) bb_perror_msg_and_die("error while processing %s", prefix); } while ((path = *argv++) != NULL) { security_context_t con; int rc; if (!(opts & OPT_VERIFY)) { error += print_matchpathcon(path, opts & OPT_NOT_PRINT); continue; } if (selinux_file_context_verify(path, 0)) { printf("%s verified\n", path); continue; } if (opts & OPT_NOT_TRANS) rc = lgetfilecon_raw(path, &con); else rc = lgetfilecon(path, &con); if (rc >= 0) { printf("%s has context %s, should be ", path, con); error += print_matchpathcon(path, 1); freecon(con); continue; } printf("actual context unknown: %s, should be ", strerror(errno)); error += print_matchpathcon(path, 1); } matchpathcon_fini(); return error; } busybox-1.22.1/selinux/Kbuild.src0000644000000000000000000000131012263563520015377 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # Copyright (C) 2007 by KaiGai Kohei # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT lib-$(CONFIG_CHCON) += chcon.o lib-$(CONFIG_GETENFORCE) += getenforce.o lib-$(CONFIG_GETSEBOOL) += getsebool.o lib-$(CONFIG_LOAD_POLICY) += load_policy.o lib-$(CONFIG_MATCHPATHCON) += matchpathcon.o lib-$(CONFIG_RUNCON) += runcon.o lib-$(CONFIG_SELINUXENABLED) += selinuxenabled.o lib-$(CONFIG_SETENFORCE) += setenforce.o lib-$(CONFIG_SETFILES) += setfiles.o lib-$(CONFIG_RESTORECON) += setfiles.o lib-$(CONFIG_SETSEBOOL) += setsebool.o lib-$(CONFIG_SESTATUS) += sestatus.o busybox-1.22.1/selinux/setsebool.c0000644000000000000000000000165412263563520015632 0ustar rootroot/* * setsebool * Simple setsebool * NOTE: -P option requires libsemanage, so this feature is * omitted in this version * Yuichi Nakamura * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define setsebool_trivial_usage //usage: "boolean value" //usage:#define setsebool_full_usage "\n\n" //usage: "Change boolean setting" #include "libbb.h" int setsebool_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int setsebool_main(int argc, char **argv) { char *p; int value; if (argc != 3) bb_show_usage(); p = argv[2]; if (LONE_CHAR(p, '1') || strcasecmp(p, "true") == 0 || strcasecmp(p, "on") == 0) { value = 1; } else if (LONE_CHAR(p, '0') || strcasecmp(p, "false") == 0 || strcasecmp(p, "off") == 0) { value = 0; } else { bb_show_usage(); } if (security_set_boolean(argv[1], value) < 0) bb_error_msg_and_die("can't set boolean"); return 0; } busybox-1.22.1/selinux/setfiles.c0000644000000000000000000004270112263563520015447 0ustar rootroot/* setfiles: based on policycoreutils 2.0.19 policycoreutils was released under GPL 2. Port to BusyBox (c) 2007 by Yuichi Nakamura */ //usage:#define setfiles_trivial_usage //usage: "[-dnpqsvW] [-e DIR]... [-o FILE] [-r alt_root_path]" //usage: IF_FEATURE_SETFILES_CHECK_OPTION( //usage: " [-c policyfile] spec_file" //usage: ) //usage: " pathname" //usage:#define setfiles_full_usage "\n\n" //usage: "Reset file contexts under pathname according to spec_file\n" //usage: IF_FEATURE_SETFILES_CHECK_OPTION( //usage: "\n -c FILE Check the validity of the contexts against the specified binary policy" //usage: ) //usage: "\n -d Show which specification matched each file" //usage: "\n -l Log changes in file labels to syslog" //usage: "\n -n Don't change any file labels" //usage: "\n -q Suppress warnings" //usage: "\n -r DIR Use an alternate root path" //usage: "\n -e DIR Exclude DIR" //usage: "\n -F Force reset of context to match file_context for customizable files" //usage: "\n -o FILE Save list of files with incorrect context" //usage: "\n -s Take a list of files from stdin (instead of command line)" //usage: "\n -v Show changes in file labels, if type or role are changing" //usage: "\n -vv Show changes in file labels, if type, role, or user are changing" //usage: "\n -W Display warnings about entries that had no matching files" //usage: //usage:#define restorecon_trivial_usage //usage: "[-iFnRv] [-e EXCLUDEDIR]... [-o FILE] [-f FILE]" //usage:#define restorecon_full_usage "\n\n" //usage: "Reset security contexts of files in pathname\n" //usage: "\n -i Ignore files that don't exist" //usage: "\n -f FILE File with list of files to process" //usage: "\n -e DIR Directory to exclude" //usage: "\n -R,-r Recurse" //usage: "\n -n Don't change any file labels" //usage: "\n -o FILE Save list of files with incorrect context" //usage: "\n -v Verbose" //usage: "\n -vv Show changed labels" //usage: "\n -F Force reset of context to match file_context" //usage: "\n for customizable files, or the user section," //usage: "\n if it has changed" #include "libbb.h" #if ENABLE_FEATURE_SETFILES_CHECK_OPTION #include #endif #define MAX_EXCLUDES 50 struct edir { char *directory; size_t size; }; struct globals { FILE *outfile; char *policyfile; char *rootpath; int rootpathlen; unsigned count; int excludeCtr; int errors; int verbose; /* getopt32 uses it, has to be int */ smallint recurse; /* Recursive descent */ smallint follow_mounts; /* Behavior flags determined based on setfiles vs. restorecon */ smallint expand_realpath; /* Expand paths via realpath */ smallint abort_on_error; /* Abort the file tree walk upon an error */ int add_assoc; /* Track inode associations for conflict detection */ int matchpathcon_flags; /* Flags to matchpathcon */ dev_t dev_id; /* Device id where target file exists */ int nerr; struct edir excludeArray[MAX_EXCLUDES]; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) void BUG_setfiles_globals_too_big(void); #define INIT_G() do { \ if (sizeof(G) > COMMON_BUFSIZE) \ BUG_setfiles_globals_too_big(); \ /* memset(&G, 0, sizeof(G)); - already is */ \ } while (0) #define outfile (G.outfile ) #define policyfile (G.policyfile ) #define rootpath (G.rootpath ) #define rootpathlen (G.rootpathlen ) #define count (G.count ) #define excludeCtr (G.excludeCtr ) #define errors (G.errors ) #define verbose (G.verbose ) #define recurse (G.recurse ) #define follow_mounts (G.follow_mounts ) #define expand_realpath (G.expand_realpath ) #define abort_on_error (G.abort_on_error ) #define add_assoc (G.add_assoc ) #define matchpathcon_flags (G.matchpathcon_flags) #define dev_id (G.dev_id ) #define nerr (G.nerr ) #define excludeArray (G.excludeArray ) /* Must match getopt32 string! */ enum { OPT_d = (1 << 0), OPT_e = (1 << 1), OPT_f = (1 << 2), OPT_i = (1 << 3), OPT_l = (1 << 4), OPT_n = (1 << 5), OPT_p = (1 << 6), OPT_q = (1 << 7), OPT_r = (1 << 8), OPT_s = (1 << 9), OPT_v = (1 << 10), OPT_o = (1 << 11), OPT_F = (1 << 12), OPT_W = (1 << 13), OPT_c = (1 << 14), /* c only for setfiles */ OPT_R = (1 << 14), /* R only for restorecon */ }; #define FLAG_d_debug (option_mask32 & OPT_d) #define FLAG_e (option_mask32 & OPT_e) #define FLAG_f (option_mask32 & OPT_f) #define FLAG_i_ignore_enoent (option_mask32 & OPT_i) #define FLAG_l_take_log (option_mask32 & OPT_l) #define FLAG_n_dry_run (option_mask32 & OPT_n) #define FLAG_p_progress (option_mask32 & OPT_p) #define FLAG_q_quiet (option_mask32 & OPT_q) #define FLAG_r (option_mask32 & OPT_r) #define FLAG_s (option_mask32 & OPT_s) #define FLAG_v (option_mask32 & OPT_v) #define FLAG_o (option_mask32 & OPT_o) #define FLAG_F_force (option_mask32 & OPT_F) #define FLAG_W_warn_no_match (option_mask32 & OPT_W) #define FLAG_c (option_mask32 & OPT_c) #define FLAG_R (option_mask32 & OPT_R) static void qprintf(const char *fmt UNUSED_PARAM, ...) { /* quiet, do nothing */ } static void inc_err(void) { nerr++; if (nerr > 9 && !FLAG_d_debug) { bb_error_msg_and_die("exiting after 10 errors"); } } static void add_exclude(const char *directory) { struct stat sb; size_t len; if (directory == NULL || directory[0] != '/') { bb_error_msg_and_die("full path required for exclude: %s", directory); } if (lstat(directory, &sb)) { bb_error_msg("directory \"%s\" not found, ignoring", directory); return; } if ((sb.st_mode & S_IFDIR) == 0) { bb_error_msg("\"%s\" is not a directory: mode %o, ignoring", directory, sb.st_mode); return; } if (excludeCtr == MAX_EXCLUDES) { bb_error_msg_and_die("maximum excludes %d exceeded", MAX_EXCLUDES); } len = strlen(directory); while (len > 1 && directory[len - 1] == '/') { len--; } excludeArray[excludeCtr].directory = xstrndup(directory, len); excludeArray[excludeCtr++].size = len; } static bool exclude(const char *file) { int i = 0; for (i = 0; i < excludeCtr; i++) { if (strncmp(file, excludeArray[i].directory, excludeArray[i].size) == 0) { if (file[excludeArray[i].size] == '\0' || file[excludeArray[i].size] == '/') { return 1; } } } return 0; } static int match(const char *name, struct stat *sb, char **con) { int ret; char path[PATH_MAX + 1]; char *tmp_path = xstrdup(name); if (excludeCtr > 0 && exclude(name)) { goto err; } ret = lstat(name, sb); if (ret) { if (FLAG_i_ignore_enoent && errno == ENOENT) { free(tmp_path); return 0; } bb_error_msg("stat(%s)", name); goto err; } if (expand_realpath) { if (S_ISLNK(sb->st_mode)) { char *p = NULL; char *file_sep; size_t len = 0; if (verbose > 1) bb_error_msg("warning! %s refers to a symbolic link, not following last component", name); file_sep = strrchr(tmp_path, '/'); if (file_sep == tmp_path) { file_sep++; path[0] = '\0'; p = path; } else if (file_sep) { *file_sep++ = '\0'; p = realpath(tmp_path, path); } else { file_sep = tmp_path; p = realpath("./", path); } if (p) len = strlen(p); if (!p || len + strlen(file_sep) + 2 > PATH_MAX) { bb_perror_msg("realpath(%s) failed", name); goto err; } p += len; /* ensure trailing slash of directory name */ if (len == 0 || p[-1] != '/') { *p++ = '/'; } strcpy(p, file_sep); name = path; if (excludeCtr > 0 && exclude(name)) goto err; } else { char *p; p = realpath(name, path); if (!p) { bb_perror_msg("realpath(%s)", name); goto err; } name = p; if (excludeCtr > 0 && exclude(name)) goto err; } } /* name will be what is matched in the policy */ if (NULL != rootpath) { if (0 != strncmp(rootpath, name, rootpathlen)) { bb_error_msg("%s is not located in %s", name, rootpath); goto err; } name += rootpathlen; } free(tmp_path); if (rootpath != NULL && name[0] == '\0') /* this is actually the root dir of the alt root */ return matchpathcon_index("/", sb->st_mode, con); return matchpathcon_index(name, sb->st_mode, con); err: free(tmp_path); return -1; } /* Compare two contexts to see if their differences are "significant", * or whether the only difference is in the user. */ static bool only_changed_user(const char *a, const char *b) { if (FLAG_F_force) return 0; if (!a || !b) return 0; a = strchr(a, ':'); /* Rest of the context after the user */ b = strchr(b, ':'); if (!a || !b) return 0; return (strcmp(a, b) == 0); } static int restore(const char *file) { char *my_file; struct stat my_sb; int i, j, ret; char *context = NULL; char *newcon = NULL; bool user_only_changed = 0; int retval = 0; my_file = bb_simplify_path(file); i = match(my_file, &my_sb, &newcon); if (i < 0) /* No matching specification. */ goto out; if (FLAG_p_progress) { count++; if (count % 0x400 == 0) { /* every 1024 times */ count = (count % (80*0x400)); if (count == 0) bb_putchar('\n'); bb_putchar('*'); fflush_all(); } } /* * Try to add an association between this inode and * this specification. If there is already an association * for this inode and it conflicts with this specification, * then use the last matching specification. */ if (add_assoc) { j = matchpathcon_filespec_add(my_sb.st_ino, i, my_file); if (j < 0) goto err; if (j != i) { /* There was already an association and it took precedence. */ goto out; } } if (FLAG_d_debug) printf("%s: %s matched by %s\n", applet_name, my_file, newcon); /* Get the current context of the file. */ ret = lgetfilecon_raw(my_file, &context); if (ret < 0) { if (errno == ENODATA) { context = NULL; /* paranoia */ } else { bb_perror_msg("lgetfilecon_raw on %s", my_file); goto err; } user_only_changed = 0; } else user_only_changed = only_changed_user(context, newcon); /* * Do not relabel the file if the matching specification is * <> or the file is already labeled according to the * specification. */ if ((strcmp(newcon, "<>") == 0) || (context && (strcmp(context, newcon) == 0) && !FLAG_F_force)) { goto out; } if (!FLAG_F_force && context && (is_context_customizable(context) > 0)) { if (verbose > 1) { bb_error_msg("skipping %s. %s is customizable_types", my_file, context); } goto out; } if (verbose) { /* If we're just doing "-v", trim out any relabels where * the user has changed but the role and type are the * same. For "-vv", emit everything. */ if (verbose > 1 || !user_only_changed) { bb_info_msg("%s: reset %s context %s->%s", applet_name, my_file, context ? context : "", newcon); } } if (FLAG_l_take_log && !user_only_changed) { if (context) bb_info_msg("relabeling %s from %s to %s", my_file, context, newcon); else bb_info_msg("labeling %s to %s", my_file, newcon); } if (outfile && !user_only_changed) fprintf(outfile, "%s\n", my_file); /* * Do not relabel the file if -n was used. */ if (FLAG_n_dry_run || user_only_changed) goto out; /* * Relabel the file to the specified context. */ ret = lsetfilecon(my_file, newcon); if (ret) { bb_perror_msg("lsetfileconon(%s,%s)", my_file, newcon); goto err; } out: freecon(context); freecon(newcon); free(my_file); return retval; err: retval--; /* -1 */ goto out; } /* * Apply the last matching specification to a file. * This function is called by recursive_action on each file during * the directory traversal. */ static int FAST_FUNC apply_spec( const char *file, struct stat *sb, void *userData UNUSED_PARAM, int depth UNUSED_PARAM) { if (!follow_mounts) { /* setfiles does not process across different mount points */ if (sb->st_dev != dev_id) { return SKIP; } } errors |= restore(file); if (abort_on_error && errors) return FALSE; return TRUE; } static int canoncon(const char *path, unsigned lineno, char **contextp) { static const char err_msg[] ALIGN1 = "%s: line %u has invalid context %s"; char *tmpcon; char *context = *contextp; int invalid = 0; #if ENABLE_FEATURE_SETFILES_CHECK_OPTION if (policyfile) { if (sepol_check_context(context) >= 0) return 0; /* Exit immediately if we're in checking mode. */ bb_error_msg_and_die(err_msg, path, lineno, context); } #endif if (security_canonicalize_context_raw(context, &tmpcon) < 0) { if (errno != ENOENT) { invalid = 1; inc_err(); } } else { free(context); *contextp = tmpcon; } if (invalid) { bb_error_msg(err_msg, path, lineno, context); } return invalid; } static int process_one(char *name) { struct stat sb; int rc; rc = lstat(name, &sb); if (rc < 0) { if (FLAG_i_ignore_enoent && errno == ENOENT) return 0; bb_perror_msg("stat(%s)", name); goto err; } dev_id = sb.st_dev; if (S_ISDIR(sb.st_mode) && recurse) { if (recursive_action(name, ACTION_RECURSE, apply_spec, apply_spec, NULL, 0) != TRUE ) { bb_error_msg("error while labeling %s", name); goto err; } } else { rc = restore(name); if (rc) goto err; } out: if (add_assoc) { if (FLAG_q_quiet) set_matchpathcon_printf(&qprintf); matchpathcon_filespec_eval(); set_matchpathcon_printf(NULL); matchpathcon_filespec_destroy(); } return rc; err: rc = -1; goto out; } int setfiles_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int setfiles_main(int argc UNUSED_PARAM, char **argv) { struct stat sb; int rc, i = 0; const char *input_filename = NULL; char *buf = NULL; size_t buf_len; int flags; llist_t *exclude_dir = NULL; char *out_filename = NULL; INIT_G(); if (applet_name[0] == 's') { /* "setfiles" */ /* * setfiles: * Recursive descent, * Does not expand paths via realpath, * Aborts on errors during the file tree walk, * Try to track inode associations for conflict detection, * Does not follow mounts, * Validates all file contexts at init time. */ recurse = 1; abort_on_error = 1; add_assoc = 1; /* follow_mounts = 0; - already is */ matchpathcon_flags = MATCHPATHCON_VALIDATE | MATCHPATHCON_NOTRANS; } else { /* * restorecon: * No recursive descent unless -r/-R, * Expands paths via realpath, * Do not abort on errors during the file tree walk, * Do not try to track inode associations for conflict detection, * Follows mounts, * Does lazy validation of contexts upon use. */ expand_realpath = 1; follow_mounts = 1; matchpathcon_flags = MATCHPATHCON_NOTRANS; /* restorecon only */ selinux_or_die(); } set_matchpathcon_flags(matchpathcon_flags); opt_complementary = "e::vv:v--p:p--v:v--q:q--v"; /* Option order must match OPT_x definitions! */ if (applet_name[0] == 'r') { /* restorecon */ flags = getopt32(argv, "de:f:ilnpqrsvo:FWR", &exclude_dir, &input_filename, &out_filename, &verbose); } else { /* setfiles */ flags = getopt32(argv, "de:f:ilnpqr:svo:FW" IF_FEATURE_SETFILES_CHECK_OPTION("c:"), &exclude_dir, &input_filename, &rootpath, &out_filename, IF_FEATURE_SETFILES_CHECK_OPTION(&policyfile,) &verbose); } argv += optind; #if ENABLE_FEATURE_SETFILES_CHECK_OPTION if ((applet_name[0] == 's') && (flags & OPT_c)) { FILE *policystream; policystream = xfopen_for_read(policyfile); if (sepol_set_policydb_from_file(policystream) < 0) { bb_error_msg_and_die("sepol_set_policydb_from_file on %s", policyfile); } fclose(policystream); /* Only process the specified file_contexts file, not * any .homedirs or .local files, and do not perform * context translations. */ set_matchpathcon_flags(MATCHPATHCON_BASEONLY | MATCHPATHCON_NOTRANS | MATCHPATHCON_VALIDATE); } #endif while (exclude_dir) add_exclude(llist_pop(&exclude_dir)); if (flags & OPT_o) { outfile = stdout; if (NOT_LONE_CHAR(out_filename, '-')) { outfile = xfopen_for_write(out_filename); } } if (applet_name[0] == 'r') { /* restorecon */ if (flags & (OPT_r | OPT_R)) recurse = 1; } else { /* setfiles */ if (flags & OPT_r) rootpathlen = strlen(rootpath); } if (flags & OPT_s) { input_filename = "-"; add_assoc = 0; } if (applet_name[0] == 's') { /* setfiles */ /* Use our own invalid context checking function so that * we can support either checking against the active policy or * checking against a binary policy file. */ set_matchpathcon_canoncon(&canoncon); if (!argv[0]) bb_show_usage(); xstat(argv[0], &sb); if (!S_ISREG(sb.st_mode)) { bb_error_msg_and_die("spec file %s is not a regular file", argv[0]); } /* Load the file contexts configuration and check it. */ rc = matchpathcon_init(argv[0]); if (rc < 0) { bb_simple_perror_msg_and_die(argv[0]); } if (nerr) exit(EXIT_FAILURE); argv++; } if (input_filename) { ssize_t len; FILE *f = stdin; if (NOT_LONE_CHAR(input_filename, '-')) f = xfopen_for_read(input_filename); while ((len = getline(&buf, &buf_len, f)) > 0) { buf[len - 1] = '\0'; errors |= process_one(buf); } if (ENABLE_FEATURE_CLEAN_UP) fclose_if_not_stdin(f); } else { if (!argv[0]) bb_show_usage(); for (i = 0; argv[i]; i++) { errors |= process_one(argv[i]); } } if (FLAG_W_warn_no_match) matchpathcon_checkmatches(argv[0]); if (ENABLE_FEATURE_CLEAN_UP && outfile) fclose(outfile); return errors; } busybox-1.22.1/selinux/runcon.c0000644000000000000000000001151412263563520015133 0ustar rootroot/* * runcon [ context | * ( [ -c ] [ -r role ] [-t type] [ -u user ] [ -l levelrange ] ) * command [arg1 [arg2 ...] ] * * attempt to run the specified command with the specified context. * * -r role : use the current context with the specified role * -t type : use the current context with the specified type * -u user : use the current context with the specified user * -l level : use the current context with the specified level range * -c : compute process transition context before modifying * * Contexts are interpreted as follows: * * Number of MLS * components system? * * 1 - type * 2 - role:type * 3 Y role:type:range * 3 N user:role:type * 4 Y user:role:type:range * 4 N error * * Port to busybox: KaiGai Kohei * - based on coreutils-5.97 (in Fedora Core 6) * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define runcon_trivial_usage //usage: "[-c] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] PROG ARGS\n" //usage: "runcon CONTEXT PROG ARGS" //usage:#define runcon_full_usage "\n\n" //usage: "Run PROG in a different security context\n" //usage: "\n CONTEXT Complete security context\n" //usage: IF_FEATURE_RUNCON_LONG_OPTIONS( //usage: "\n -c,--compute Compute process transition context before modifying" //usage: "\n -t,--type=TYPE Type (for same role as parent)" //usage: "\n -u,--user=USER User identity" //usage: "\n -r,--role=ROLE Role" //usage: "\n -l,--range=RNG Levelrange" //usage: ) //usage: IF_NOT_FEATURE_RUNCON_LONG_OPTIONS( //usage: "\n -c Compute process transition context before modifying" //usage: "\n -t TYPE Type (for same role as parent)" //usage: "\n -u USER User identity" //usage: "\n -r ROLE Role" //usage: "\n -l RNG Levelrange" //usage: ) #include #include #include "libbb.h" static context_t runcon_compute_new_context(char *user, char *role, char *type, char *range, char *command, int compute_trans) { context_t con; security_context_t cur_context; if (getcon(&cur_context)) bb_error_msg_and_die("can't get current context"); if (compute_trans) { security_context_t file_context, new_context; if (getfilecon(command, &file_context) < 0) bb_error_msg_and_die("can't retrieve attributes of '%s'", command); if (security_compute_create(cur_context, file_context, SECCLASS_PROCESS, &new_context)) bb_error_msg_and_die("unable to compute a new context"); cur_context = new_context; } con = context_new(cur_context); if (!con) bb_error_msg_and_die("'%s' is not a valid context", cur_context); if (user && context_user_set(con, user)) bb_error_msg_and_die("can't set new user '%s'", user); if (type && context_type_set(con, type)) bb_error_msg_and_die("can't set new type '%s'", type); if (range && context_range_set(con, range)) bb_error_msg_and_die("can't set new range '%s'", range); if (role && context_role_set(con, role)) bb_error_msg_and_die("can't set new role '%s'", role); return con; } #if ENABLE_FEATURE_RUNCON_LONG_OPTIONS static const char runcon_longopts[] ALIGN1 = "user\0" Required_argument "u" "role\0" Required_argument "r" "type\0" Required_argument "t" "range\0" Required_argument "l" "compute\0" No_argument "c" "help\0" No_argument "h" ; #endif #define OPTS_ROLE (1<<0) /* r */ #define OPTS_TYPE (1<<1) /* t */ #define OPTS_USER (1<<2) /* u */ #define OPTS_RANGE (1<<3) /* l */ #define OPTS_COMPUTE (1<<4) /* c */ #define OPTS_HELP (1<<5) /* h */ #define OPTS_CONTEXT_COMPONENT (OPTS_ROLE | OPTS_TYPE | OPTS_USER | OPTS_RANGE) int runcon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int runcon_main(int argc UNUSED_PARAM, char **argv) { char *role = NULL; char *range = NULL; char *user = NULL; char *type = NULL; char *context = NULL; unsigned opts; context_t con; selinux_or_die(); #if ENABLE_FEATURE_RUNCON_LONG_OPTIONS applet_long_options = runcon_longopts; #endif opt_complementary = "-1"; opts = getopt32(argv, "r:t:u:l:ch", &role, &type, &user, &range); argv += optind; if (!(opts & OPTS_CONTEXT_COMPONENT)) { context = *argv++; if (!argv[0]) bb_error_msg_and_die("no command given"); } if (context) { con = context_new(context); if (!con) bb_error_msg_and_die("'%s' is not a valid context", context); } else { con = runcon_compute_new_context(user, role, type, range, argv[0], opts & OPTS_COMPUTE); } if (security_check_context(context_str(con))) bb_error_msg_and_die("'%s' is not a valid context", context_str(con)); if (setexeccon(context_str(con))) bb_error_msg_and_die("can't set up security context '%s'", context_str(con)); BB_EXECVP_or_die(argv); } busybox-1.22.1/selinux/getsebool.c0000644000000000000000000000311412263563520015607 0ustar rootroot/* * getsebool * * Based on libselinux 1.33.1 * Port to BusyBox Hiroshi Shinji * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define getsebool_trivial_usage //usage: "-a or getsebool boolean..." //usage:#define getsebool_full_usage "\n\n" //usage: " -a Show all selinux booleans" #include "libbb.h" int getsebool_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int getsebool_main(int argc, char **argv) { int i, rc = 0, active, pending, len = 0; char **names; unsigned opt; selinux_or_die(); opt = getopt32(argv, "a"); if (opt) { /* -a */ if (argc > 2) bb_show_usage(); rc = security_get_boolean_names(&names, &len); if (rc) bb_perror_msg_and_die("can't get boolean names"); if (!len) { puts("No booleans"); return 0; } } if (!len) { if (argc < 2) bb_show_usage(); len = argc - 1; names = xmalloc(sizeof(char *) * len); for (i = 0; i < len; i++) names[i] = xstrdup(argv[i + 1]); } for (i = 0; i < len; i++) { active = security_get_boolean_active(names[i]); if (active < 0) { bb_error_msg_and_die("error getting active value for %s", names[i]); } pending = security_get_boolean_pending(names[i]); if (pending < 0) { bb_error_msg_and_die("error getting pending value for %s", names[i]); } printf("%s --> %s", names[i], (active ? "on" : "off")); if (pending != active) printf(" pending: %s", (pending ? "on" : "off")); bb_putchar('\n'); } if (ENABLE_FEATURE_CLEAN_UP) { for (i = 0; i < len; i++) free(names[i]); free(names); } return rc; } busybox-1.22.1/selinux/getenforce.c0000644000000000000000000000137112263563520015750 0ustar rootroot/* * getenforce * * Based on libselinux 1.33.1 * Port to BusyBox Hiroshi Shinji * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define getenforce_trivial_usage NOUSAGE_STR //usage:#define getenforce_full_usage "" #include "libbb.h" int getenforce_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int getenforce_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int rc; rc = is_selinux_enabled(); if (rc < 0) bb_error_msg_and_die("is_selinux_enabled() failed"); if (rc == 1) { rc = security_getenforce(); if (rc < 0) bb_error_msg_and_die("getenforce() failed"); if (rc) puts("Enforcing"); else puts("Permissive"); } else { puts("Disabled"); } return 0; } busybox-1.22.1/selinux/setenforce.c0000644000000000000000000000177012263563520015767 0ustar rootroot/* * setenforce * * Based on libselinux 1.33.1 * Port to BusyBox Hiroshi Shinji * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define setenforce_trivial_usage //usage: "[Enforcing | Permissive | 1 | 0]" //usage:#define setenforce_full_usage "" #include "libbb.h" /* These strings are arranged so that odd ones * result in security_setenforce(1) being done, * the rest will do security_setenforce(0) */ static const char *const setenforce_cmd[] = { "0", "1", "permissive", "enforcing", NULL, }; int setenforce_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int setenforce_main(int argc UNUSED_PARAM, char **argv) { int i, rc; if (!argv[1] || argv[2]) bb_show_usage(); selinux_or_die(); for (i = 0; setenforce_cmd[i]; i++) { if (strcasecmp(argv[1], setenforce_cmd[i]) != 0) continue; rc = security_setenforce(i & 1); if (rc < 0) bb_perror_msg_and_die("setenforce() failed"); return 0; } bb_show_usage(); } busybox-1.22.1/selinux/sestatus.c0000644000000000000000000001143312263563520015502 0ustar rootroot/* * sestatus -- displays the status of SELinux * * Ported to busybox: KaiGai Kohei * * Copyright (C) KaiGai Kohei * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define sestatus_trivial_usage //usage: "[-vb]" //usage:#define sestatus_full_usage "\n\n" //usage: " -v Verbose" //usage: "\n -b Display current state of booleans" #include "libbb.h" extern char *selinux_mnt; #define OPT_VERBOSE (1 << 0) #define OPT_BOOLEAN (1 << 1) #define COL_FMT "%-31s " static void display_boolean(void) { char **bools; int i, active, pending, nbool; if (security_get_boolean_names(&bools, &nbool) < 0) return; puts("\nPolicy booleans:"); for (i = 0; i < nbool; i++) { active = security_get_boolean_active(bools[i]); if (active < 0) goto skip; pending = security_get_boolean_pending(bools[i]); if (pending < 0) goto skip; printf(COL_FMT "%s", bools[i], active == 0 ? "off" : "on"); if (active != pending) printf(" (%sactivate pending)", pending == 0 ? "in" : ""); bb_putchar('\n'); skip: if (ENABLE_FEATURE_CLEAN_UP) free(bools[i]); } if (ENABLE_FEATURE_CLEAN_UP) free(bools); } static void read_config(char **pc, int npc, char **fc, int nfc) { char *buf; parser_t *parser; int pc_ofs = 0, fc_ofs = 0, section = -1; pc[0] = fc[0] = NULL; parser = config_open("/etc/sestatus.conf"); while (config_read(parser, &buf, 1, 1, "# \t", PARSE_NORMAL)) { if (strcmp(buf, "[process]") == 0) { section = 1; } else if (strcmp(buf, "[files]") == 0) { section = 2; } else { if (section == 1 && pc_ofs < npc -1) { pc[pc_ofs++] = xstrdup(buf); pc[pc_ofs] = NULL; } else if (section == 2 && fc_ofs < nfc - 1) { fc[fc_ofs++] = xstrdup(buf); fc[fc_ofs] = NULL; } } } config_close(parser); } static void display_verbose(void) { security_context_t con, _con; char *fc[50], *pc[50], *cterm; pid_t *pidList; int i; read_config(pc, ARRAY_SIZE(pc), fc, ARRAY_SIZE(fc)); /* process contexts */ puts("\nProcess contexts:"); /* current context */ if (getcon(&con) == 0) { printf(COL_FMT "%s\n", "Current context:", con); if (ENABLE_FEATURE_CLEAN_UP) freecon(con); } /* /sbin/init context */ if (getpidcon(1, &con) == 0) { printf(COL_FMT "%s\n", "Init context:", con); if (ENABLE_FEATURE_CLEAN_UP) freecon(con); } /* [process] context */ for (i = 0; pc[i] != NULL; i++) { pidList = find_pid_by_name(bb_basename(pc[i])); if (pidList[0] > 0 && getpidcon(pidList[0], &con) == 0) { printf(COL_FMT "%s\n", pc[i], con); if (ENABLE_FEATURE_CLEAN_UP) freecon(con); } if (ENABLE_FEATURE_CLEAN_UP) free(pidList); } /* files contexts */ puts("\nFile contexts:"); cterm = xmalloc_ttyname(0); //FIXME: if cterm == NULL, we segfault!?? puts(cterm); if (cterm && lgetfilecon(cterm, &con) >= 0) { printf(COL_FMT "%s\n", "Controlling term:", con); if (ENABLE_FEATURE_CLEAN_UP) freecon(con); } for (i = 0; fc[i] != NULL; i++) { struct stat stbuf; if (lgetfilecon(fc[i], &con) < 0) continue; if (lstat(fc[i], &stbuf) == 0) { if (S_ISLNK(stbuf.st_mode)) { if (getfilecon(fc[i], &_con) >= 0) { printf(COL_FMT "%s -> %s\n", fc[i], _con, con); if (ENABLE_FEATURE_CLEAN_UP) freecon(_con); } } else { printf(COL_FMT "%s\n", fc[i], con); } } if (ENABLE_FEATURE_CLEAN_UP) freecon(con); } } int sestatus_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int sestatus_main(int argc UNUSED_PARAM, char **argv) { unsigned opts; const char *pol_path; int rc; opt_complementary = "?0"; /* no arguments are required. */ opts = getopt32(argv, "vb"); /* SELinux status: line */ rc = is_selinux_enabled(); if (rc < 0) goto error; printf(COL_FMT "%s\n", "SELinux status:", rc == 1 ? "enabled" : "disabled"); /* SELinuxfs mount: line */ if (!selinux_mnt) goto error; printf(COL_FMT "%s\n", "SELinuxfs mount:", selinux_mnt); /* Current mode: line */ rc = security_getenforce(); if (rc < 0) goto error; printf(COL_FMT "%s\n", "Current mode:", rc == 0 ? "permissive" : "enforcing"); /* Mode from config file: line */ if (selinux_getenforcemode(&rc) != 0) goto error; printf(COL_FMT "%s\n", "Mode from config file:", rc < 0 ? "disabled" : (rc == 0 ? "permissive" : "enforcing")); /* Policy version: line */ rc = security_policyvers(); if (rc < 0) goto error; printf(COL_FMT "%u\n", "Policy version:", rc); /* Policy from config file: line */ pol_path = selinux_policy_root(); if (!pol_path) goto error; printf(COL_FMT "%s\n", "Policy from config file:", bb_basename(pol_path)); if (opts & OPT_BOOLEAN) display_boolean(); if (opts & OPT_VERBOSE) display_verbose(); return 0; error: bb_perror_msg_and_die("libselinux returns unknown state"); } busybox-1.22.1/README0000644000000000000000000002107312263563520012655 0ustar rootrootPlease see the LICENSE file for details on copying and usage. Please refer to the INSTALL file for instructions on how to build. What is busybox: BusyBox combines tiny versions of many common UNIX utilities into a single small executable. It provides minimalist replacements for most of the utilities you usually find in bzip2, coreutils, dhcp, diffutils, e2fsprogs, file, findutils, gawk, grep, inetutils, less, modutils, net-tools, procps, sed, shadow, sysklogd, sysvinit, tar, util-linux, and vim. The utilities in BusyBox often have fewer options than their full-featured cousins; however, the options that are included provide the expected functionality and behave very much like their larger counterparts. BusyBox has been written with size-optimization and limited resources in mind, both to produce small binaries and to reduce run-time memory usage. Busybox is also extremely modular so you can easily include or exclude commands (or features) at compile time. This makes it easy to customize embedded systems; to create a working system, just add /dev, /etc, and a Linux kernel. Busybox (usually together with uClibc) has also been used as a component of "thin client" desktop systems, live-CD distributions, rescue disks, installers, and so on. BusyBox provides a fairly complete POSIX environment for any small system, both embedded environments and more full featured systems concerned about space. Busybox is slowly working towards implementing the full Single Unix Specification V3 (http://www.opengroup.org/onlinepubs/009695399/), but isn't there yet (and for size reasons will probably support at most UTF-8 for internationalization). We are also interested in passing the Linux Test Project (http://ltp.sourceforge.net). ---------------- Using busybox: BusyBox is extremely configurable. This allows you to include only the components and options you need, thereby reducing binary size. Run 'make config' or 'make menuconfig' to select the functionality that you wish to enable. (See 'make help' for more commands.) The behavior of busybox is determined by the name it's called under: as "cp" it behaves like cp, as "sed" it behaves like sed, and so on. Called as "busybox" it takes the second argument as the name of the applet to run (I.E. "./busybox ls -l /proc"). The "standalone shell" mode is an easy way to try out busybox; this is a command shell that calls the built-in applets without needing them to be installed in the path. (Note that this requires /proc to be mounted, if testing from a boot floppy or in a chroot environment.) The build automatically generates a file "busybox.links", which is used by 'make install' to create symlinks to the BusyBox binary for all compiled in commands. This uses the CONFIG_PREFIX environment variable to specify where to install, and installs hardlinks or symlinks depending on the configuration preferences. (You can also manually run the install script at "applets/install.sh"). ---------------- Downloading the current source code: Source for the latest released version, as well as daily snapshots, can always be downloaded from http://busybox.net/downloads/ You can browse the up to the minute source code and change history online. http://git.busybox.net/busybox/ Anonymous GIT access is available. For instructions, check out: http://www.busybox.net/source.html For those that are actively contributing and would like to check files in, see: http://busybox.net/developer.html The developers also have a bug and patch tracking system (https://bugs.busybox.net) although posting a bug/patch to the mailing list is generally a faster way of getting it fixed, and the complete archive of what happened is the git changelog. Note: if you want to compile busybox in a busybox environment you must select CONFIG_DESKTOP. ---------------- Getting help: when you find you need help, you can check out the busybox mailing list archives at http://busybox.net/lists/busybox/ or even join the mailing list if you are interested. ---------------- Bugs: if you find bugs, please submit a detailed bug report to the busybox mailing list at busybox@busybox.net. a well-written bug report should include a transcript of a shell session that demonstrates the bad behavior and enables anyone else to duplicate the bug on their own machine. the following is such an example: to: busybox@busybox.net from: diligent@testing.linux.org subject: /bin/date doesn't work package: busybox version: 1.00 when i execute busybox 'date' it produces unexpected results. with gnu date i get the following output: $ date fri oct 8 14:19:41 mdt 2004 but when i use busybox date i get this instead: $ date illegal instruction i am using debian unstable, kernel version 2.4.25-vrs2 on a netwinder, and the latest uclibc from cvs. -diligent note the careful description and use of examples showing not only what busybox does, but also a counter example showing what an equivalent app does (or pointing to the text of a relevant standard). Bug reports lacking such detail may never be fixed... Thanks for understanding. ---------------- Portability: Busybox is developed and tested on Linux 2.4 and 2.6 kernels, compiled with gcc (the unit-at-a-time optimizations in version 3.4 and later are worth upgrading to get, but older versions should work), and linked against uClibc (0.9.27 or greater) or glibc (2.2 or greater). In such an environment, the full set of busybox features should work, and if anything doesn't we want to know about it so we can fix it. There are many other environments out there, in which busybox may build and run just fine. We just don't test them. Since busybox consists of a large number of more or less independent applets, portability is a question of which features work where. Some busybox applets (such as cat and rm) are highly portable and likely to work just about anywhere, while others (such as insmod and losetup) require recent Linux kernels with recent C libraries. Earlier versions of Linux and glibc may or may not work, for any given configuration. Linux 2.2 or earlier should mostly work (there's still some support code in things like mount.c) but this is no longer regularly tested, and inherently won't support certain features (such as long files and --bind mounts). The same is true for glibc 2.0 and 2.1: expect a higher testing and debugging burden using such old infrastructure. (The busybox developers are not very interested in supporting these older versions, but will probably accept small self-contained patches to fix simple problems.) Some environments are not recommended. Early versions of uClibc were buggy and missing many features: upgrade. Linking against libc5 or dietlibc is not supported and not interesting to the busybox developers. (The first is obsolete and has no known size or feature advantages over uClibc, the second has known bugs that its developers have actively refused to fix.) Ancient Linux kernels (2.0.x and earlier) are similarly uninteresting. In theory it's possible to use Busybox under other operating systems (such as MacOS X, Solaris, Cygwin, or the BSD Fork Du Jour). This generally involves a different kernel and a different C library at the same time. While it should be possible to port the majority of the code to work in one of these environments, don't be surprised if it doesn't work out of the box. If you're into that sort of thing, start small (selecting just a few applets) and work your way up. In 2005 Shaun Jackman has ported busybox to a combination of newlib and libgloss, and some of his patches have been integrated. Supported hardware: BusyBox in general will build on any architecture supported by gcc. We support both 32 and 64 bit platforms, and both big and little endian systems. Under 2.4 Linux kernels, kernel module loading was implemented in a platform-specific manner. Busybox's insmod utility has been reported to work under ARM, CRIS, H8/300, x86, ia64, x86_64, m68k, MIPS, PowerPC, S390, SH3/4/5, Sparc, v850e, and x86_64. Anything else probably won't work. The module loading mechanism for the 2.6 kernel is much more generic, and we believe 2.6.x kernel module loading support should work on all architectures supported by the kernel. ---------------- Please feed suggestions, bug reports, insults, and bribes back to the busybox mailing list: busybox@busybox.net and/or maintainer: Denys Vlasenko busybox-1.22.1/configs/0000755000000000000000000000000012320365364013422 5ustar rootrootbusybox-1.22.1/configs/android2_defconfig0000644000000000000000000006527512263563520017072 0ustar rootroot# Run "make android2_defconfig", then "make". # # Tested with the standalone toolchain from ndk r6: # android-ndk-r6/build/tools/make-standalone-toolchain.sh --platform=android-8 # CONFIG_HAVE_DOT_CONFIG=y # # Busybox Settings # # # General Configuration # # CONFIG_DESKTOP is not set # CONFIG_EXTRA_COMPAT is not set # CONFIG_INCLUDE_SUSv2 is not set # CONFIG_USE_PORTABLE_CODE is not set CONFIG_PLATFORM_LINUX=y CONFIG_FEATURE_BUFFERS_USE_MALLOC=y # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set # CONFIG_SHOW_USAGE is not set # CONFIG_FEATURE_VERBOSE_USAGE is not set # CONFIG_FEATURE_COMPRESS_USAGE is not set # CONFIG_FEATURE_INSTALLER is not set # CONFIG_INSTALL_NO_USR is not set # CONFIG_LOCALE_SUPPORT is not set # CONFIG_UNICODE_SUPPORT is not set # CONFIG_UNICODE_USING_LOCALE is not set # CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set CONFIG_SUBST_WCHAR=0 CONFIG_LAST_SUPPORTED_WCHAR=0 # CONFIG_UNICODE_COMBINING_WCHARS is not set # CONFIG_UNICODE_WIDE_WCHARS is not set # CONFIG_UNICODE_BIDI_SUPPORT is not set # CONFIG_UNICODE_NEUTRAL_TABLE is not set # CONFIG_UNICODE_PRESERVE_BROKEN is not set # CONFIG_LONG_OPTS is not set # CONFIG_FEATURE_DEVPTS is not set # CONFIG_FEATURE_CLEAN_UP is not set # CONFIG_FEATURE_UTMP is not set # CONFIG_FEATURE_WTMP is not set # CONFIG_FEATURE_PIDFILE is not set # CONFIG_FEATURE_SUID is not set # CONFIG_FEATURE_SUID_CONFIG is not set # CONFIG_FEATURE_SUID_CONFIG_QUIET is not set # CONFIG_SELINUX is not set # CONFIG_FEATURE_PREFER_APPLETS is not set CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" CONFIG_FEATURE_SYSLOG=y # CONFIG_FEATURE_HAVE_RPC is not set # # Build Options # # CONFIG_STATIC is not set # CONFIG_PIE is not set # CONFIG_NOMMU is not set # CONFIG_BUILD_LIBBUSYBOX is not set # CONFIG_FEATURE_INDIVIDUAL is not set # CONFIG_FEATURE_SHARED_BUSYBOX is not set # CONFIG_LFS is not set CONFIG_CROSS_COMPILER_PREFIX="arm-linux-androideabi-" CONFIG_EXTRA_CFLAGS="" # # Debugging Options # # CONFIG_DEBUG is not set # CONFIG_DEBUG_PESSIMIZE is not set # CONFIG_WERROR is not set CONFIG_NO_DEBUG_LIB=y # CONFIG_DMALLOC is not set # CONFIG_EFENCE is not set # # Installation Options ("make install" behavior) # CONFIG_INSTALL_APPLET_SYMLINKS=y # CONFIG_INSTALL_APPLET_HARDLINKS is not set # CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set # CONFIG_INSTALL_APPLET_DONT is not set # CONFIG_INSTALL_SH_APPLET_SYMLINK is not set # CONFIG_INSTALL_SH_APPLET_HARDLINK is not set # CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set CONFIG_PREFIX="./_install" # # Busybox Library Tuning # # CONFIG_FEATURE_SYSTEMD is not set # CONFIG_FEATURE_RTMINMAX is not set CONFIG_PASSWORD_MINLEN=6 CONFIG_MD5_SMALL=1 # CONFIG_FEATURE_FAST_TOP is not set # CONFIG_FEATURE_ETC_NETWORKS is not set CONFIG_FEATURE_USE_TERMIOS=y # CONFIG_FEATURE_EDITING is not set CONFIG_FEATURE_EDITING_MAX_LEN=0 # CONFIG_FEATURE_EDITING_VI is not set CONFIG_FEATURE_EDITING_HISTORY=0 # CONFIG_FEATURE_EDITING_SAVEHISTORY is not set # CONFIG_FEATURE_TAB_COMPLETION is not set # CONFIG_FEATURE_USERNAME_COMPLETION is not set # CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set # CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set # CONFIG_FEATURE_NON_POSIX_CP is not set # CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set CONFIG_FEATURE_COPYBUF_KB=4 # CONFIG_FEATURE_SKIP_ROOTFS is not set # CONFIG_MONOTONIC_SYSCALL is not set # CONFIG_IOCTL_HEX2STR_ERROR is not set # CONFIG_FEATURE_HWIB is not set # # Applets # # # Archival Utilities # CONFIG_FEATURE_SEAMLESS_XZ=y CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y CONFIG_FEATURE_SEAMLESS_Z=y CONFIG_AR=y CONFIG_FEATURE_AR_LONG_FILENAMES=y CONFIG_FEATURE_AR_CREATE=y CONFIG_BUNZIP2=y CONFIG_BZIP2=y CONFIG_CPIO=y CONFIG_FEATURE_CPIO_O=y CONFIG_FEATURE_CPIO_P=y CONFIG_DPKG=y CONFIG_DPKG_DEB=y # CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set CONFIG_GUNZIP=y CONFIG_GZIP=y # CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set CONFIG_LZOP=y CONFIG_LZOP_COMPR_HIGH=y CONFIG_RPM2CPIO=y CONFIG_RPM=y CONFIG_TAR=y CONFIG_FEATURE_TAR_CREATE=y CONFIG_FEATURE_TAR_AUTODETECT=y CONFIG_FEATURE_TAR_FROM=y CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y # CONFIG_FEATURE_TAR_LONG_OPTIONS is not set # CONFIG_FEATURE_TAR_TO_COMMAND is not set CONFIG_FEATURE_TAR_UNAME_GNAME=y CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y # CONFIG_FEATURE_TAR_SELINUX is not set CONFIG_UNCOMPRESS=y CONFIG_UNLZMA=y CONFIG_FEATURE_LZMA_FAST=y CONFIG_LZMA=y CONFIG_UNXZ=y CONFIG_XZ=y CONFIG_UNZIP=y # # Coreutils # CONFIG_BASENAME=y CONFIG_CAT=y # CONFIG_DATE is not set # CONFIG_FEATURE_DATE_ISOFMT is not set # CONFIG_FEATURE_DATE_NANO is not set # CONFIG_FEATURE_DATE_COMPAT is not set # CONFIG_ID is not set # CONFIG_GROUPS is not set CONFIG_TEST=y CONFIG_FEATURE_TEST_64=y CONFIG_TOUCH=y CONFIG_TR=y CONFIG_FEATURE_TR_CLASSES=y CONFIG_FEATURE_TR_EQUIV=y CONFIG_BASE64=y CONFIG_CAL=y CONFIG_CATV=y CONFIG_CHGRP=y CONFIG_CHMOD=y CONFIG_CHOWN=y # CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set CONFIG_CHROOT=y CONFIG_CKSUM=y CONFIG_COMM=y CONFIG_CP=y # CONFIG_FEATURE_CP_LONG_OPTIONS is not set CONFIG_CUT=y CONFIG_DD=y CONFIG_FEATURE_DD_SIGNAL_HANDLING=y CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y CONFIG_FEATURE_DD_IBS_OBS=y # CONFIG_DF is not set # CONFIG_FEATURE_DF_FANCY is not set CONFIG_DIRNAME=y CONFIG_DOS2UNIX=y CONFIG_UNIX2DOS=y CONFIG_DU=y CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y CONFIG_ECHO=y CONFIG_FEATURE_FANCY_ECHO=y # CONFIG_ENV is not set # CONFIG_FEATURE_ENV_LONG_OPTIONS is not set CONFIG_EXPAND=y # CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set # CONFIG_EXPR is not set # CONFIG_EXPR_MATH_SUPPORT_64 is not set CONFIG_FALSE=y CONFIG_FOLD=y # CONFIG_FSYNC is not set CONFIG_HEAD=y CONFIG_FEATURE_FANCY_HEAD=y # CONFIG_HOSTID is not set CONFIG_INSTALL=y # CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set CONFIG_LN=y # CONFIG_LOGNAME is not set CONFIG_LS=y CONFIG_FEATURE_LS_FILETYPES=y CONFIG_FEATURE_LS_FOLLOWLINKS=y CONFIG_FEATURE_LS_RECURSIVE=y CONFIG_FEATURE_LS_SORTFILES=y CONFIG_FEATURE_LS_TIMESTAMPS=y CONFIG_FEATURE_LS_USERNAME=y # CONFIG_FEATURE_LS_COLOR is not set # CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set CONFIG_MD5SUM=y CONFIG_MKDIR=y # CONFIG_FEATURE_MKDIR_LONG_OPTIONS is not set CONFIG_MKFIFO=y CONFIG_MKNOD=y CONFIG_MV=y # CONFIG_FEATURE_MV_LONG_OPTIONS is not set CONFIG_NICE=y CONFIG_NOHUP=y CONFIG_OD=y CONFIG_PRINTENV=y CONFIG_PRINTF=y CONFIG_PWD=y CONFIG_READLINK=y CONFIG_FEATURE_READLINK_FOLLOW=y CONFIG_REALPATH=y CONFIG_RM=y CONFIG_RMDIR=y # CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set CONFIG_SEQ=y CONFIG_SHA1SUM=y CONFIG_SHA256SUM=y CONFIG_SHA512SUM=y CONFIG_SLEEP=y CONFIG_FEATURE_FANCY_SLEEP=y CONFIG_FEATURE_FLOAT_SLEEP=y CONFIG_SORT=y CONFIG_FEATURE_SORT_BIG=y CONFIG_SPLIT=y CONFIG_FEATURE_SPLIT_FANCY=y # CONFIG_STAT is not set # CONFIG_FEATURE_STAT_FORMAT is not set CONFIG_STTY=y CONFIG_SUM=y CONFIG_SYNC=y CONFIG_TAC=y CONFIG_TAIL=y CONFIG_FEATURE_FANCY_TAIL=y CONFIG_TEE=y CONFIG_FEATURE_TEE_USE_BLOCK_IO=y CONFIG_TRUE=y # CONFIG_TTY is not set CONFIG_UNAME=y CONFIG_UNEXPAND=y # CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set CONFIG_UNIQ=y # CONFIG_USLEEP is not set CONFIG_UUDECODE=y CONFIG_UUENCODE=y CONFIG_WC=y CONFIG_FEATURE_WC_LARGE=y # CONFIG_WHO is not set CONFIG_WHOAMI=y CONFIG_YES=y # # Common options for cp and mv # CONFIG_FEATURE_PRESERVE_HARDLINKS=y # # Common options for ls, more and telnet # CONFIG_FEATURE_AUTOWIDTH=y # # Common options for df, du, ls # CONFIG_FEATURE_HUMAN_READABLE=y # # Common options for md5sum, sha1sum, sha256sum, sha512sum # CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y # # Console Utilities # CONFIG_CHVT=y CONFIG_FGCONSOLE=y CONFIG_CLEAR=y CONFIG_DEALLOCVT=y CONFIG_DUMPKMAP=y # CONFIG_KBD_MODE is not set # CONFIG_LOADFONT is not set CONFIG_LOADKMAP=y CONFIG_OPENVT=y CONFIG_RESET=y CONFIG_RESIZE=y CONFIG_FEATURE_RESIZE_PRINT=y CONFIG_SETCONSOLE=y # CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set # CONFIG_SETFONT is not set # CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set CONFIG_DEFAULT_SETFONT_DIR="" CONFIG_SETKEYCODES=y CONFIG_SETLOGCONS=y CONFIG_SHOWKEY=y # CONFIG_FEATURE_LOADFONT_PSF2 is not set # CONFIG_FEATURE_LOADFONT_RAW is not set # # Debian Utilities # CONFIG_MKTEMP=y CONFIG_PIPE_PROGRESS=y CONFIG_RUN_PARTS=y # CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set CONFIG_FEATURE_RUN_PARTS_FANCY=y CONFIG_START_STOP_DAEMON=y CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y # CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set CONFIG_WHICH=y # # Editors # CONFIG_PATCH=y # CONFIG_VI is not set CONFIG_FEATURE_VI_MAX_LEN=0 # CONFIG_FEATURE_VI_8BIT is not set # CONFIG_FEATURE_VI_COLON is not set # CONFIG_FEATURE_VI_YANKMARK is not set # CONFIG_FEATURE_VI_SEARCH is not set # CONFIG_FEATURE_VI_REGEX_SEARCH is not set # CONFIG_FEATURE_VI_USE_SIGNALS is not set # CONFIG_FEATURE_VI_DOT_CMD is not set # CONFIG_FEATURE_VI_READONLY is not set # CONFIG_FEATURE_VI_SETOPTS is not set # CONFIG_FEATURE_VI_SET is not set # CONFIG_FEATURE_VI_WIN_RESIZE is not set # CONFIG_FEATURE_VI_ASK_TERMINAL is not set # CONFIG_AWK is not set # CONFIG_FEATURE_AWK_LIBM is not set CONFIG_CMP=y CONFIG_DIFF=y # CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set CONFIG_FEATURE_DIFF_DIR=y # CONFIG_ED is not set # CONFIG_SED is not set # CONFIG_FEATURE_ALLOW_EXEC is not set # # Finding Utilities # # CONFIG_FIND is not set # CONFIG_FEATURE_FIND_PRINT0 is not set # CONFIG_FEATURE_FIND_MTIME is not set # CONFIG_FEATURE_FIND_MMIN is not set # CONFIG_FEATURE_FIND_PERM is not set # CONFIG_FEATURE_FIND_TYPE is not set # CONFIG_FEATURE_FIND_XDEV is not set # CONFIG_FEATURE_FIND_MAXDEPTH is not set # CONFIG_FEATURE_FIND_NEWER is not set # CONFIG_FEATURE_FIND_INUM is not set # CONFIG_FEATURE_FIND_EXEC is not set # CONFIG_FEATURE_FIND_USER is not set # CONFIG_FEATURE_FIND_GROUP is not set # CONFIG_FEATURE_FIND_NOT is not set # CONFIG_FEATURE_FIND_DEPTH is not set # CONFIG_FEATURE_FIND_PAREN is not set # CONFIG_FEATURE_FIND_SIZE is not set # CONFIG_FEATURE_FIND_PRUNE is not set # CONFIG_FEATURE_FIND_DELETE is not set # CONFIG_FEATURE_FIND_PATH is not set # CONFIG_FEATURE_FIND_REGEX is not set # CONFIG_FEATURE_FIND_CONTEXT is not set # CONFIG_FEATURE_FIND_LINKS is not set # CONFIG_GREP is not set # CONFIG_FEATURE_GREP_EGREP_ALIAS is not set # CONFIG_FEATURE_GREP_FGREP_ALIAS is not set # CONFIG_FEATURE_GREP_CONTEXT is not set CONFIG_XARGS=y CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y # # Init Utilities # # CONFIG_BOOTCHARTD is not set # CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set # CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set CONFIG_HALT=y # CONFIG_FEATURE_CALL_TELINIT is not set CONFIG_TELINIT_PATH="" CONFIG_INIT=y CONFIG_FEATURE_USE_INITTAB=y # CONFIG_FEATURE_KILL_REMOVED is not set CONFIG_FEATURE_KILL_DELAY=0 CONFIG_FEATURE_INIT_SCTTY=y CONFIG_FEATURE_INIT_SYSLOG=y CONFIG_FEATURE_EXTRA_QUIET=y CONFIG_FEATURE_INIT_COREDUMPS=y CONFIG_FEATURE_INITRD=y CONFIG_INIT_TERMINAL_TYPE="linux" CONFIG_MESG=y CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y # # Login/Password Management Utilities # # CONFIG_ADD_SHELL is not set # CONFIG_REMOVE_SHELL is not set # CONFIG_FEATURE_SHADOWPASSWDS is not set # CONFIG_USE_BB_PWD_GRP is not set # CONFIG_USE_BB_SHADOW is not set # CONFIG_USE_BB_CRYPT is not set # CONFIG_USE_BB_CRYPT_SHA is not set # CONFIG_ADDUSER is not set # CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set # CONFIG_FEATURE_CHECK_NAMES is not set CONFIG_FIRST_SYSTEM_ID=0 CONFIG_LAST_SYSTEM_ID=0 # CONFIG_ADDGROUP is not set # CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set # CONFIG_FEATURE_ADDUSER_TO_GROUP is not set # CONFIG_DELUSER is not set # CONFIG_DELGROUP is not set # CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set # CONFIG_GETTY is not set # CONFIG_LOGIN is not set # CONFIG_PAM is not set # CONFIG_LOGIN_SCRIPTS is not set # CONFIG_FEATURE_NOLOGIN is not set # CONFIG_FEATURE_SECURETTY is not set # CONFIG_PASSWD is not set # CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set # CONFIG_CRYPTPW is not set # CONFIG_CHPASSWD is not set # CONFIG_SU is not set # CONFIG_FEATURE_SU_SYSLOG is not set # CONFIG_FEATURE_SU_CHECKS_SHELLS is not set # CONFIG_SULOGIN is not set # CONFIG_VLOCK is not set # # Linux Ext2 FS Progs # CONFIG_CHATTR=y # CONFIG_FSCK is not set CONFIG_LSATTR=y CONFIG_TUNE2FS=y # # Linux Module Utilities # CONFIG_MODINFO=y CONFIG_MODPROBE_SMALL=y CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE=y CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y # CONFIG_INSMOD is not set # CONFIG_RMMOD is not set # CONFIG_LSMOD is not set # CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set # CONFIG_MODPROBE is not set # CONFIG_FEATURE_MODPROBE_BLACKLIST is not set # CONFIG_DEPMOD is not set # # Options common to multiple modutils # # CONFIG_FEATURE_2_4_MODULES is not set # CONFIG_FEATURE_INSMOD_TRY_MMAP is not set # CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set # CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set # CONFIG_FEATURE_INSMOD_LOADINKMEM is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set # CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set # CONFIG_FEATURE_MODUTILS_ALIAS is not set # CONFIG_FEATURE_MODUTILS_SYMBOLS is not set CONFIG_DEFAULT_MODULES_DIR="/lib/modules" CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" # # Linux System Utilities # CONFIG_BLOCKDEV=y CONFIG_REV=y # CONFIG_ACPID is not set # CONFIG_FEATURE_ACPID_COMPAT is not set CONFIG_BLKID=y # CONFIG_FEATURE_BLKID_TYPE is not set CONFIG_DMESG=y CONFIG_FEATURE_DMESG_PRETTY=y CONFIG_FBSET=y CONFIG_FEATURE_FBSET_FANCY=y CONFIG_FEATURE_FBSET_READMODE=y CONFIG_FDFLUSH=y CONFIG_FDFORMAT=y CONFIG_FDISK=y CONFIG_FDISK_SUPPORT_LARGE_DISKS=y CONFIG_FEATURE_FDISK_WRITABLE=y # CONFIG_FEATURE_AIX_LABEL is not set # CONFIG_FEATURE_SGI_LABEL is not set # CONFIG_FEATURE_SUN_LABEL is not set # CONFIG_FEATURE_OSF_LABEL is not set # CONFIG_FEATURE_GPT_LABEL is not set CONFIG_FEATURE_FDISK_ADVANCED=y CONFIG_FINDFS=y CONFIG_FLOCK=y CONFIG_FREERAMDISK=y # CONFIG_FSCK_MINIX is not set # CONFIG_MKFS_EXT2 is not set # CONFIG_MKFS_MINIX is not set # CONFIG_FEATURE_MINIX2 is not set # CONFIG_MKFS_REISER is not set # CONFIG_MKFS_VFAT is not set CONFIG_GETOPT=y CONFIG_FEATURE_GETOPT_LONG=y CONFIG_HEXDUMP=y CONFIG_FEATURE_HEXDUMP_REVERSE=y CONFIG_HD=y # CONFIG_HWCLOCK is not set # CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set # CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set # CONFIG_IPCRM is not set # CONFIG_IPCS is not set CONFIG_LOSETUP=y CONFIG_LSPCI=y CONFIG_LSUSB=y # CONFIG_MDEV is not set # CONFIG_FEATURE_MDEV_CONF is not set # CONFIG_FEATURE_MDEV_RENAME is not set # CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set # CONFIG_FEATURE_MDEV_EXEC is not set # CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set CONFIG_MKSWAP=y CONFIG_FEATURE_MKSWAP_UUID=y CONFIG_MORE=y # CONFIG_MOUNT is not set # CONFIG_FEATURE_MOUNT_FAKE is not set # CONFIG_FEATURE_MOUNT_VERBOSE is not set # CONFIG_FEATURE_MOUNT_HELPERS is not set # CONFIG_FEATURE_MOUNT_LABEL is not set # CONFIG_FEATURE_MOUNT_NFS is not set # CONFIG_FEATURE_MOUNT_CIFS is not set # CONFIG_FEATURE_MOUNT_FLAGS is not set # CONFIG_FEATURE_MOUNT_FSTAB is not set # CONFIG_PIVOT_ROOT is not set # CONFIG_RDATE is not set CONFIG_RDEV=y CONFIG_READPROFILE=y CONFIG_RTCWAKE=y CONFIG_SCRIPT=y CONFIG_SCRIPTREPLAY=y # CONFIG_SETARCH is not set # CONFIG_SWAPONOFF is not set # CONFIG_FEATURE_SWAPON_PRI is not set # CONFIG_SWITCH_ROOT is not set # CONFIG_UMOUNT is not set # CONFIG_FEATURE_UMOUNT_ALL is not set # CONFIG_FEATURE_MOUNT_LOOP is not set # CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set # CONFIG_FEATURE_MTAB_SUPPORT is not set CONFIG_VOLUMEID=y # # Filesystem/Volume identification # CONFIG_FEATURE_VOLUMEID_EXT=y CONFIG_FEATURE_VOLUMEID_BTRFS=y CONFIG_FEATURE_VOLUMEID_REISERFS=y CONFIG_FEATURE_VOLUMEID_FAT=y CONFIG_FEATURE_VOLUMEID_HFS=y CONFIG_FEATURE_VOLUMEID_JFS=y CONFIG_FEATURE_VOLUMEID_XFS=y CONFIG_FEATURE_VOLUMEID_NTFS=y CONFIG_FEATURE_VOLUMEID_ISO9660=y CONFIG_FEATURE_VOLUMEID_UDF=y CONFIG_FEATURE_VOLUMEID_LUKS=y CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y CONFIG_FEATURE_VOLUMEID_CRAMFS=y CONFIG_FEATURE_VOLUMEID_ROMFS=y CONFIG_FEATURE_VOLUMEID_SYSV=y CONFIG_FEATURE_VOLUMEID_OCFS2=y CONFIG_FEATURE_VOLUMEID_LINUXRAID=y # # Miscellaneous Utilities # # CONFIG_CONSPY is not set # CONFIG_NANDWRITE is not set CONFIG_NANDDUMP=y CONFIG_SETSERIAL=y # CONFIG_UBIATTACH is not set # CONFIG_UBIDETACH is not set # CONFIG_UBIMKVOL is not set # CONFIG_UBIRMVOL is not set # CONFIG_UBIRSVOL is not set # CONFIG_UBIUPDATEVOL is not set # CONFIG_ADJTIMEX is not set # CONFIG_BBCONFIG is not set # CONFIG_FEATURE_COMPRESS_BBCONFIG is not set CONFIG_BEEP=y CONFIG_FEATURE_BEEP_FREQ=4000 CONFIG_FEATURE_BEEP_LENGTH_MS=30 CONFIG_CHAT=y CONFIG_FEATURE_CHAT_NOFAIL=y # CONFIG_FEATURE_CHAT_TTY_HIFI is not set CONFIG_FEATURE_CHAT_IMPLICIT_CR=y CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y CONFIG_FEATURE_CHAT_SEND_ESCAPES=y CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y CONFIG_FEATURE_CHAT_CLR_ABORT=y CONFIG_CHRT=y # CONFIG_CROND is not set # CONFIG_FEATURE_CROND_D is not set # CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set CONFIG_FEATURE_CROND_DIR="" # CONFIG_CRONTAB is not set CONFIG_DC=y CONFIG_FEATURE_DC_LIBM=y # CONFIG_DEVFSD is not set # CONFIG_DEVFSD_MODLOAD is not set # CONFIG_DEVFSD_FG_NP is not set # CONFIG_DEVFSD_VERBOSE is not set # CONFIG_FEATURE_DEVFS is not set CONFIG_DEVMEM=y # CONFIG_EJECT is not set # CONFIG_FEATURE_EJECT_SCSI is not set CONFIG_FBSPLASH=y CONFIG_FLASHCP=y CONFIG_FLASH_LOCK=y CONFIG_FLASH_UNLOCK=y # CONFIG_FLASH_ERASEALL is not set # CONFIG_IONICE is not set CONFIG_INOTIFYD=y # CONFIG_LAST is not set # CONFIG_FEATURE_LAST_SMALL is not set # CONFIG_FEATURE_LAST_FANCY is not set # CONFIG_LESS is not set CONFIG_FEATURE_LESS_MAXLINES=0 # CONFIG_FEATURE_LESS_BRACKETS is not set # CONFIG_FEATURE_LESS_FLAGS is not set # CONFIG_FEATURE_LESS_MARKS is not set # CONFIG_FEATURE_LESS_REGEXP is not set # CONFIG_FEATURE_LESS_WINCH is not set # CONFIG_FEATURE_LESS_DASHCMD is not set # CONFIG_FEATURE_LESS_LINENUMS is not set CONFIG_HDPARM=y CONFIG_FEATURE_HDPARM_GET_IDENTITY=y CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y CONFIG_MAKEDEVS=y # CONFIG_FEATURE_MAKEDEVS_LEAF is not set CONFIG_FEATURE_MAKEDEVS_TABLE=y CONFIG_MAN=y # CONFIG_MICROCOM is not set # CONFIG_MOUNTPOINT is not set # CONFIG_MT is not set CONFIG_RAIDAUTORUN=y # CONFIG_READAHEAD is not set # CONFIG_RFKILL is not set # CONFIG_RUNLEVEL is not set CONFIG_RX=y CONFIG_SETSID=y CONFIG_STRINGS=y # CONFIG_TASKSET is not set # CONFIG_FEATURE_TASKSET_FANCY is not set CONFIG_TIME=y CONFIG_TIMEOUT=y CONFIG_TTYSIZE=y CONFIG_VOLNAME=y # CONFIG_WALL is not set # CONFIG_WATCHDOG is not set # # Networking Utilities # # CONFIG_NAMEIF is not set # CONFIG_FEATURE_NAMEIF_EXTENDED is not set CONFIG_NBDCLIENT=y CONFIG_NC=y CONFIG_NC_SERVER=y CONFIG_NC_EXTRA=y # CONFIG_NC_110_COMPAT is not set # CONFIG_PING is not set # CONFIG_PING6 is not set # CONFIG_FEATURE_FANCY_PING is not set CONFIG_WHOIS=y # CONFIG_FEATURE_IPV6 is not set # CONFIG_FEATURE_UNIX_LOCAL is not set # CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set # CONFIG_VERBOSE_RESOLUTION_ERRORS is not set CONFIG_ARP=y # CONFIG_ARPING is not set # CONFIG_BRCTL is not set # CONFIG_FEATURE_BRCTL_FANCY is not set # CONFIG_FEATURE_BRCTL_SHOW is not set CONFIG_DNSD=y # CONFIG_ETHER_WAKE is not set CONFIG_FAKEIDENTD=y CONFIG_FTPD=y CONFIG_FEATURE_FTP_WRITE=y CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y CONFIG_FTPGET=y CONFIG_FTPPUT=y # CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set # CONFIG_HOSTNAME is not set CONFIG_HTTPD=y CONFIG_FEATURE_HTTPD_RANGES=y CONFIG_FEATURE_HTTPD_USE_SENDFILE=y CONFIG_FEATURE_HTTPD_SETUID=y CONFIG_FEATURE_HTTPD_BASIC_AUTH=y # CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set CONFIG_FEATURE_HTTPD_CGI=y CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y CONFIG_FEATURE_HTTPD_ERROR_PAGES=y CONFIG_FEATURE_HTTPD_PROXY=y CONFIG_FEATURE_HTTPD_GZIP=y CONFIG_IFCONFIG=y CONFIG_FEATURE_IFCONFIG_STATUS=y # CONFIG_FEATURE_IFCONFIG_SLIP is not set CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y CONFIG_FEATURE_IFCONFIG_HW=y CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y # CONFIG_IFENSLAVE is not set # CONFIG_IFPLUGD is not set CONFIG_IFUPDOWN=y CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate" CONFIG_FEATURE_IFUPDOWN_IP=y CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y # CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set CONFIG_FEATURE_IFUPDOWN_IPV4=y # CONFIG_FEATURE_IFUPDOWN_IPV6 is not set CONFIG_FEATURE_IFUPDOWN_MAPPING=y # CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set # CONFIG_INETD is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set # CONFIG_FEATURE_INETD_RPC is not set CONFIG_IP=y CONFIG_FEATURE_IP_ADDRESS=y CONFIG_FEATURE_IP_LINK=y CONFIG_FEATURE_IP_ROUTE=y CONFIG_FEATURE_IP_TUNNEL=y CONFIG_FEATURE_IP_RULE=y CONFIG_FEATURE_IP_SHORT_FORMS=y # CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set CONFIG_IPADDR=y CONFIG_IPLINK=y CONFIG_IPROUTE=y CONFIG_IPTUNNEL=y CONFIG_IPRULE=y CONFIG_IPCALC=y CONFIG_FEATURE_IPCALC_FANCY=y # CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set CONFIG_NETSTAT=y CONFIG_FEATURE_NETSTAT_WIDE=y CONFIG_FEATURE_NETSTAT_PRG=y # CONFIG_NSLOOKUP is not set # CONFIG_NTPD is not set # CONFIG_FEATURE_NTPD_SERVER is not set CONFIG_PSCAN=y CONFIG_ROUTE=y # CONFIG_SLATTACH is not set CONFIG_TCPSVD=y # CONFIG_TELNET is not set # CONFIG_FEATURE_TELNET_TTYPE is not set # CONFIG_FEATURE_TELNET_AUTOLOGIN is not set # CONFIG_TELNETD is not set # CONFIG_FEATURE_TELNETD_STANDALONE is not set # CONFIG_FEATURE_TELNETD_INETD_WAIT is not set # CONFIG_TFTP is not set # CONFIG_TFTPD is not set # CONFIG_FEATURE_TFTP_GET is not set # CONFIG_FEATURE_TFTP_PUT is not set # CONFIG_FEATURE_TFTP_BLOCKSIZE is not set # CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set # CONFIG_TFTP_DEBUG is not set # CONFIG_TRACEROUTE is not set # CONFIG_TRACEROUTE6 is not set # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set CONFIG_TUNCTL=y CONFIG_FEATURE_TUNCTL_UG=y # CONFIG_UDHCPD is not set # CONFIG_DHCPRELAY is not set # CONFIG_DUMPLEASES is not set # CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set # CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set CONFIG_DHCPD_LEASES_FILE="" CONFIG_UDHCPC=y CONFIG_FEATURE_UDHCPC_ARPING=y # CONFIG_FEATURE_UDHCP_PORT is not set CONFIG_UDHCP_DEBUG=9 CONFIG_FEATURE_UDHCP_RFC3397=y CONFIG_FEATURE_UDHCP_8021Q=y CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script" CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80 CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n" # CONFIG_UDPSVD is not set # CONFIG_VCONFIG is not set CONFIG_WGET=y CONFIG_FEATURE_WGET_STATUSBAR=y CONFIG_FEATURE_WGET_AUTHENTICATION=y # CONFIG_FEATURE_WGET_LONG_OPTIONS is not set CONFIG_FEATURE_WGET_TIMEOUT=y # CONFIG_ZCIP is not set # # Print Utilities # CONFIG_LPD=y CONFIG_LPR=y CONFIG_LPQ=y # # Mail Utilities # CONFIG_MAKEMIME=y CONFIG_FEATURE_MIME_CHARSET="us-ascii" CONFIG_POPMAILDIR=y CONFIG_FEATURE_POPMAILDIR_DELIVERY=y CONFIG_REFORMIME=y CONFIG_FEATURE_REFORMIME_COMPAT=y CONFIG_SENDMAIL=y # # Process Utilities # CONFIG_IOSTAT=y CONFIG_MPSTAT=y CONFIG_NMETER=y CONFIG_PMAP=y CONFIG_POWERTOP=y CONFIG_PSTREE=y CONFIG_PWDX=y CONFIG_SMEMCAP=y # CONFIG_FREE is not set CONFIG_FUSER=y # CONFIG_KILL is not set # CONFIG_KILLALL is not set # CONFIG_KILLALL5 is not set # CONFIG_PGREP is not set CONFIG_PIDOF=y CONFIG_FEATURE_PIDOF_SINGLE=y CONFIG_FEATURE_PIDOF_OMIT=y # CONFIG_PKILL is not set # CONFIG_PS is not set # CONFIG_FEATURE_PS_WIDE is not set # CONFIG_FEATURE_PS_TIME is not set # CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set # CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set CONFIG_RENICE=y CONFIG_BB_SYSCTL=y CONFIG_TOP=y CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y CONFIG_FEATURE_TOP_SMP_CPU=y CONFIG_FEATURE_TOP_DECIMALS=y CONFIG_FEATURE_TOP_SMP_PROCESS=y CONFIG_FEATURE_TOPMEM=y CONFIG_FEATURE_SHOW_THREADS=y # CONFIG_UPTIME is not set CONFIG_WATCH=y # # Runit Utilities # CONFIG_RUNSV=y CONFIG_RUNSVDIR=y # CONFIG_FEATURE_RUNSVDIR_LOG is not set CONFIG_SV=y CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service" CONFIG_SVLOGD=y CONFIG_CHPST=y CONFIG_SETUIDGID=y CONFIG_ENVUIDGID=y CONFIG_ENVDIR=y CONFIG_SOFTLIMIT=y # CONFIG_CHCON is not set # CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set # CONFIG_GETENFORCE is not set # CONFIG_GETSEBOOL is not set # CONFIG_LOAD_POLICY is not set # CONFIG_MATCHPATHCON is not set # CONFIG_RESTORECON is not set # CONFIG_RUNCON is not set # CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set # CONFIG_SELINUXENABLED is not set # CONFIG_SETENFORCE is not set # CONFIG_SETFILES is not set # CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set # CONFIG_SETSEBOOL is not set # CONFIG_SESTATUS is not set # # Shells # # CONFIG_ASH is not set # CONFIG_ASH_BASH_COMPAT is not set # CONFIG_ASH_IDLE_TIMEOUT is not set # CONFIG_ASH_JOB_CONTROL is not set # CONFIG_ASH_ALIAS is not set # CONFIG_ASH_GETOPTS is not set # CONFIG_ASH_BUILTIN_ECHO is not set # CONFIG_ASH_BUILTIN_PRINTF is not set # CONFIG_ASH_BUILTIN_TEST is not set # CONFIG_ASH_CMDCMD is not set # CONFIG_ASH_MAIL is not set # CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set # CONFIG_ASH_RANDOM_SUPPORT is not set # CONFIG_ASH_EXPAND_PRMT is not set CONFIG_CTTYHACK=y # CONFIG_HUSH is not set # CONFIG_HUSH_BASH_COMPAT is not set # CONFIG_HUSH_BRACE_EXPANSION is not set # CONFIG_HUSH_HELP is not set # CONFIG_HUSH_INTERACTIVE is not set # CONFIG_HUSH_SAVEHISTORY is not set # CONFIG_HUSH_JOB is not set # CONFIG_HUSH_TICK is not set # CONFIG_HUSH_IF is not set # CONFIG_HUSH_LOOPS is not set # CONFIG_HUSH_CASE is not set # CONFIG_HUSH_FUNCTIONS is not set # CONFIG_HUSH_LOCAL is not set # CONFIG_HUSH_RANDOM_SUPPORT is not set # CONFIG_HUSH_EXPORT_N is not set # CONFIG_HUSH_MODE_X is not set # CONFIG_MSH is not set # CONFIG_FEATURE_SH_IS_ASH is not set # CONFIG_FEATURE_SH_IS_HUSH is not set CONFIG_FEATURE_SH_IS_NONE=y # CONFIG_FEATURE_BASH_IS_ASH is not set # CONFIG_FEATURE_BASH_IS_HUSH is not set CONFIG_FEATURE_BASH_IS_NONE=y # CONFIG_SH_MATH_SUPPORT is not set # CONFIG_SH_MATH_SUPPORT_64 is not set # CONFIG_FEATURE_SH_EXTRA_QUIET is not set # CONFIG_FEATURE_SH_STANDALONE is not set # CONFIG_FEATURE_SH_NOFORK is not set # CONFIG_FEATURE_SH_HISTFILESIZE is not set # # System Logging Utilities # # CONFIG_SYSLOGD is not set # CONFIG_FEATURE_ROTATE_LOGFILE is not set # CONFIG_FEATURE_REMOTE_LOG is not set # CONFIG_FEATURE_SYSLOGD_DUP is not set # CONFIG_FEATURE_SYSLOGD_CFG is not set CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0 # CONFIG_FEATURE_IPC_SYSLOG is not set CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0 # CONFIG_LOGREAD is not set # CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set CONFIG_KLOGD=y CONFIG_FEATURE_KLOGD_KLOGCTL=y # CONFIG_LOGGER is not set busybox-1.22.1/configs/TEST_noprintf_defconfig0000644000000000000000000007031012263563520020050 0ustar rootroot# # Automatically generated make config: don't edit # Busybox version: 1.17.0.git # Mon Jun 7 13:37:55 2010 # CONFIG_HAVE_DOT_CONFIG=y # # Busybox Settings # # # General Configuration # CONFIG_DESKTOP=y CONFIG_EXTRA_COMPAT=y CONFIG_INCLUDE_SUSv2=y # CONFIG_USE_PORTABLE_CODE is not set CONFIG_FEATURE_BUFFERS_USE_MALLOC=y # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set CONFIG_SHOW_USAGE=y CONFIG_FEATURE_VERBOSE_USAGE=y # CONFIG_FEATURE_COMPRESS_USAGE is not set # CONFIG_FEATURE_INSTALLER is not set # CONFIG_LOCALE_SUPPORT is not set CONFIG_UNICODE_SUPPORT=y # CONFIG_UNICODE_USING_LOCALE is not set CONFIG_FEATURE_CHECK_UNICODE_IN_ENV=y CONFIG_SUBST_WCHAR=63 CONFIG_LAST_SUPPORTED_WCHAR=65535 CONFIG_UNICODE_COMBINING_WCHARS=y CONFIG_UNICODE_WIDE_WCHARS=y CONFIG_UNICODE_BIDI_SUPPORT=y # CONFIG_UNICODE_NEUTRAL_TABLE is not set CONFIG_UNICODE_PRESERVE_BROKEN=y CONFIG_LONG_OPTS=y CONFIG_FEATURE_DEVPTS=y # CONFIG_FEATURE_CLEAN_UP is not set CONFIG_FEATURE_UTMP=y CONFIG_FEATURE_WTMP=y CONFIG_FEATURE_PIDFILE=y # CONFIG_FEATURE_SUID is not set # CONFIG_FEATURE_SUID_CONFIG is not set # CONFIG_FEATURE_SUID_CONFIG_QUIET is not set # CONFIG_SELINUX is not set # CONFIG_FEATURE_PREFER_APPLETS is not set CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" # CONFIG_FEATURE_SYSLOG is not set # CONFIG_FEATURE_HAVE_RPC is not set # # Build Options # CONFIG_STATIC=y # CONFIG_PIE is not set # CONFIG_NOMMU is not set # CONFIG_BUILD_LIBBUSYBOX is not set # CONFIG_FEATURE_INDIVIDUAL is not set # CONFIG_FEATURE_SHARED_BUSYBOX is not set CONFIG_LFS=y CONFIG_CROSS_COMPILER_PREFIX="i486-linux-uclibc-" CONFIG_EXTRA_CFLAGS="" # # Debugging Options # # CONFIG_DEBUG is not set # CONFIG_DEBUG_PESSIMIZE is not set CONFIG_WERROR=y CONFIG_NO_DEBUG_LIB=y # CONFIG_DMALLOC is not set # CONFIG_EFENCE is not set # # Installation Options # # CONFIG_INSTALL_NO_USR is not set CONFIG_INSTALL_APPLET_SYMLINKS=y # CONFIG_INSTALL_APPLET_HARDLINKS is not set # CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set # CONFIG_INSTALL_APPLET_DONT is not set # CONFIG_INSTALL_SH_APPLET_SYMLINK is not set # CONFIG_INSTALL_SH_APPLET_HARDLINK is not set # CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set CONFIG_PREFIX="./_install" # # Busybox Library Tuning # CONFIG_PASSWORD_MINLEN=6 CONFIG_MD5_SMALL=1 CONFIG_FEATURE_FAST_TOP=y # CONFIG_FEATURE_ETC_NETWORKS is not set CONFIG_FEATURE_EDITING=y CONFIG_FEATURE_EDITING_MAX_LEN=1024 CONFIG_FEATURE_EDITING_VI=y CONFIG_FEATURE_EDITING_HISTORY=15 # CONFIG_FEATURE_EDITING_SAVEHISTORY is not set CONFIG_FEATURE_TAB_COMPLETION=y CONFIG_FEATURE_USERNAME_COMPLETION=y CONFIG_FEATURE_EDITING_FANCY_PROMPT=y CONFIG_FEATURE_EDITING_ASK_TERMINAL=y CONFIG_FEATURE_NON_POSIX_CP=y # CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set CONFIG_FEATURE_COPYBUF_KB=64 CONFIG_MONOTONIC_SYSCALL=y # CONFIG_IOCTL_HEX2STR_ERROR is not set CONFIG_FEATURE_HWIB=y # # Applets # # # Archival Utilities # CONFIG_FEATURE_SEAMLESS_XZ=y CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y CONFIG_FEATURE_SEAMLESS_Z=y # CONFIG_AR is not set # CONFIG_FEATURE_AR_LONG_FILENAMES is not set # CONFIG_FEATURE_AR_CREATE is not set # CONFIG_BUNZIP2 is not set # CONFIG_BZIP2 is not set # CONFIG_CPIO is not set # CONFIG_FEATURE_CPIO_O is not set # CONFIG_FEATURE_CPIO_P is not set # CONFIG_DPKG is not set # CONFIG_DPKG_DEB is not set # CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set # CONFIG_GUNZIP is not set # CONFIG_GZIP is not set # CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set # CONFIG_LZOP is not set # CONFIG_LZOP_COMPR_HIGH is not set # CONFIG_RPM2CPIO is not set # CONFIG_RPM is not set # CONFIG_TAR is not set # CONFIG_FEATURE_TAR_CREATE is not set # CONFIG_FEATURE_TAR_AUTODETECT is not set # CONFIG_FEATURE_TAR_FROM is not set # CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY is not set # CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set # CONFIG_FEATURE_TAR_GNU_EXTENSIONS is not set # CONFIG_FEATURE_TAR_LONG_OPTIONS is not set # CONFIG_FEATURE_TAR_UNAME_GNAME is not set # CONFIG_FEATURE_TAR_NOPRESERVE_TIME is not set # CONFIG_FEATURE_TAR_SELINUX is not set # CONFIG_UNCOMPRESS is not set # CONFIG_UNLZMA is not set # CONFIG_FEATURE_LZMA_FAST is not set # CONFIG_LZMA is not set # CONFIG_UNXZ is not set # CONFIG_XZ is not set # CONFIG_UNZIP is not set # # Coreutils # CONFIG_BASENAME=y # CONFIG_CAT is not set # CONFIG_DATE is not set # CONFIG_FEATURE_DATE_ISOFMT is not set # CONFIG_FEATURE_DATE_NANO is not set # CONFIG_FEATURE_DATE_COMPAT is not set # CONFIG_TEST is not set # CONFIG_FEATURE_TEST_64 is not set # CONFIG_TR is not set # CONFIG_FEATURE_TR_CLASSES is not set # CONFIG_FEATURE_TR_EQUIV is not set # CONFIG_CAL is not set # CONFIG_CATV is not set # CONFIG_CHGRP is not set # CONFIG_CHMOD is not set # CONFIG_CHOWN is not set # CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set # CONFIG_CHROOT is not set # CONFIG_CKSUM is not set # CONFIG_COMM is not set # CONFIG_CP is not set # CONFIG_FEATURE_CP_LONG_OPTIONS is not set # CONFIG_CUT is not set # CONFIG_DD is not set # CONFIG_FEATURE_DD_SIGNAL_HANDLING is not set # CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set # CONFIG_FEATURE_DD_IBS_OBS is not set # CONFIG_DF is not set # CONFIG_FEATURE_DF_FANCY is not set # CONFIG_DIRNAME is not set # CONFIG_DOS2UNIX is not set # CONFIG_UNIX2DOS is not set # CONFIG_DU is not set # CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K is not set # CONFIG_ECHO is not set # CONFIG_FEATURE_FANCY_ECHO is not set # CONFIG_ENV is not set # CONFIG_FEATURE_ENV_LONG_OPTIONS is not set # CONFIG_EXPAND is not set # CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set # CONFIG_EXPR is not set # CONFIG_EXPR_MATH_SUPPORT_64 is not set CONFIG_FALSE=y # CONFIG_FOLD is not set # CONFIG_FSYNC is not set # CONFIG_HEAD is not set # CONFIG_FEATURE_FANCY_HEAD is not set # CONFIG_HOSTID is not set # CONFIG_ID is not set # CONFIG_INSTALL is not set # CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set # CONFIG_LN is not set # CONFIG_LOGNAME is not set # CONFIG_LS is not set # CONFIG_FEATURE_LS_FILETYPES is not set # CONFIG_FEATURE_LS_FOLLOWLINKS is not set # CONFIG_FEATURE_LS_RECURSIVE is not set # CONFIG_FEATURE_LS_SORTFILES is not set # CONFIG_FEATURE_LS_TIMESTAMPS is not set # CONFIG_FEATURE_LS_USERNAME is not set # CONFIG_FEATURE_LS_COLOR is not set # CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set # CONFIG_MD5SUM is not set # CONFIG_MKDIR is not set # CONFIG_FEATURE_MKDIR_LONG_OPTIONS is not set # CONFIG_MKFIFO is not set # CONFIG_MKNOD is not set # CONFIG_MV is not set # CONFIG_FEATURE_MV_LONG_OPTIONS is not set # CONFIG_NICE is not set # CONFIG_NOHUP is not set # CONFIG_OD is not set # CONFIG_PRINTENV is not set # CONFIG_PRINTF is not set # CONFIG_PWD is not set # CONFIG_READLINK is not set # CONFIG_FEATURE_READLINK_FOLLOW is not set # CONFIG_REALPATH is not set # CONFIG_RM is not set # CONFIG_RMDIR is not set # CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set # CONFIG_SEQ is not set # CONFIG_SHA1SUM is not set # CONFIG_SHA256SUM is not set # CONFIG_SHA512SUM is not set # CONFIG_SLEEP is not set # CONFIG_FEATURE_FANCY_SLEEP is not set # CONFIG_FEATURE_FLOAT_SLEEP is not set # CONFIG_SORT is not set # CONFIG_FEATURE_SORT_BIG is not set # CONFIG_SPLIT is not set # CONFIG_FEATURE_SPLIT_FANCY is not set # CONFIG_STAT is not set # CONFIG_FEATURE_STAT_FORMAT is not set # CONFIG_STTY is not set # CONFIG_SUM is not set # CONFIG_SYNC is not set # CONFIG_TAC is not set # CONFIG_TAIL is not set # CONFIG_FEATURE_FANCY_TAIL is not set # CONFIG_TEE is not set # CONFIG_FEATURE_TEE_USE_BLOCK_IO is not set # CONFIG_TOUCH is not set CONFIG_TRUE=y # CONFIG_TTY is not set # CONFIG_UNAME is not set # CONFIG_UNEXPAND is not set # CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set # CONFIG_UNIQ is not set # CONFIG_USLEEP is not set # CONFIG_UUDECODE is not set # CONFIG_UUENCODE is not set # CONFIG_WC is not set # CONFIG_FEATURE_WC_LARGE is not set # CONFIG_WHO is not set # CONFIG_WHOAMI is not set # CONFIG_YES is not set # CONFIG_FEATURE_PRESERVE_HARDLINKS is not set # CONFIG_FEATURE_AUTOWIDTH is not set # CONFIG_FEATURE_HUMAN_READABLE is not set # CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set # # Console Utilities # # CONFIG_CHVT is not set # CONFIG_FGCONSOLE is not set # CONFIG_CLEAR is not set # CONFIG_DEALLOCVT is not set # CONFIG_DUMPKMAP is not set # CONFIG_KBD_MODE is not set # CONFIG_LOADFONT is not set # CONFIG_LOADKMAP is not set # CONFIG_OPENVT is not set # CONFIG_RESET is not set # CONFIG_RESIZE is not set # CONFIG_FEATURE_RESIZE_PRINT is not set # CONFIG_SETCONSOLE is not set # CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set # CONFIG_SETFONT is not set # CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set CONFIG_DEFAULT_SETFONT_DIR="" # CONFIG_SETKEYCODES is not set # CONFIG_SETLOGCONS is not set # CONFIG_SHOWKEY is not set # CONFIG_FEATURE_LOADFONT_PSF2 is not set # CONFIG_FEATURE_LOADFONT_RAW is not set # # Debian Utilities # # CONFIG_MKTEMP is not set CONFIG_PIPE_PROGRESS=y # CONFIG_RUN_PARTS is not set # CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set # CONFIG_FEATURE_RUN_PARTS_FANCY is not set # CONFIG_START_STOP_DAEMON is not set # CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set # CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set # CONFIG_WHICH is not set # # Editors # # CONFIG_AWK is not set # CONFIG_FEATURE_AWK_LIBM is not set # CONFIG_CMP is not set # CONFIG_DIFF is not set # CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set # CONFIG_FEATURE_DIFF_DIR is not set # CONFIG_ED is not set # CONFIG_PATCH is not set # CONFIG_SED is not set # CONFIG_VI is not set CONFIG_FEATURE_VI_MAX_LEN=0 # CONFIG_FEATURE_VI_8BIT is not set # CONFIG_FEATURE_VI_COLON is not set # CONFIG_FEATURE_VI_YANKMARK is not set # CONFIG_FEATURE_VI_SEARCH is not set # CONFIG_FEATURE_VI_USE_SIGNALS is not set # CONFIG_FEATURE_VI_DOT_CMD is not set # CONFIG_FEATURE_VI_READONLY is not set # CONFIG_FEATURE_VI_SETOPTS is not set # CONFIG_FEATURE_VI_SET is not set # CONFIG_FEATURE_VI_WIN_RESIZE is not set # CONFIG_FEATURE_VI_ASK_TERMINAL is not set # CONFIG_FEATURE_ALLOW_EXEC is not set # # Finding Utilities # # CONFIG_FIND is not set # CONFIG_FEATURE_FIND_PRINT0 is not set # CONFIG_FEATURE_FIND_MTIME is not set # CONFIG_FEATURE_FIND_MMIN is not set # CONFIG_FEATURE_FIND_PERM is not set # CONFIG_FEATURE_FIND_TYPE is not set # CONFIG_FEATURE_FIND_XDEV is not set # CONFIG_FEATURE_FIND_MAXDEPTH is not set # CONFIG_FEATURE_FIND_NEWER is not set # CONFIG_FEATURE_FIND_INUM is not set # CONFIG_FEATURE_FIND_EXEC is not set # CONFIG_FEATURE_FIND_USER is not set # CONFIG_FEATURE_FIND_GROUP is not set # CONFIG_FEATURE_FIND_NOT is not set # CONFIG_FEATURE_FIND_DEPTH is not set # CONFIG_FEATURE_FIND_PAREN is not set # CONFIG_FEATURE_FIND_SIZE is not set # CONFIG_FEATURE_FIND_PRUNE is not set # CONFIG_FEATURE_FIND_DELETE is not set # CONFIG_FEATURE_FIND_PATH is not set # CONFIG_FEATURE_FIND_REGEX is not set # CONFIG_FEATURE_FIND_CONTEXT is not set # CONFIG_FEATURE_FIND_LINKS is not set # CONFIG_GREP is not set # CONFIG_FEATURE_GREP_EGREP_ALIAS is not set # CONFIG_FEATURE_GREP_FGREP_ALIAS is not set # CONFIG_FEATURE_GREP_CONTEXT is not set # CONFIG_XARGS is not set # CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set # CONFIG_FEATURE_XARGS_SUPPORT_QUOTES is not set # CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT is not set # CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM is not set # # Init Utilities # # CONFIG_INIT is not set # CONFIG_FEATURE_USE_INITTAB is not set # CONFIG_FEATURE_KILL_REMOVED is not set CONFIG_FEATURE_KILL_DELAY=0 # CONFIG_FEATURE_INIT_SCTTY is not set # CONFIG_FEATURE_INIT_SYSLOG is not set # CONFIG_FEATURE_EXTRA_QUIET is not set # CONFIG_FEATURE_INIT_COREDUMPS is not set # CONFIG_FEATURE_INITRD is not set # CONFIG_HALT is not set # CONFIG_FEATURE_CALL_TELINIT is not set CONFIG_TELINIT_PATH="" # CONFIG_MESG is not set # CONFIG_BOOTCHARTD is not set # # Login/Password Management Utilities # # CONFIG_FEATURE_SHADOWPASSWDS is not set # CONFIG_USE_BB_PWD_GRP is not set # CONFIG_USE_BB_SHADOW is not set # CONFIG_USE_BB_CRYPT is not set # CONFIG_USE_BB_CRYPT_SHA is not set # CONFIG_ADDGROUP is not set # CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set # CONFIG_FEATURE_ADDUSER_TO_GROUP is not set # CONFIG_DELGROUP is not set # CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set # CONFIG_FEATURE_CHECK_NAMES is not set # CONFIG_ADDUSER is not set # CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set CONFIG_FIRST_SYSTEM_ID=0 CONFIG_LAST_SYSTEM_ID=0 # CONFIG_DELUSER is not set # CONFIG_GETTY is not set # CONFIG_LOGIN is not set # CONFIG_PAM is not set # CONFIG_LOGIN_SCRIPTS is not set # CONFIG_FEATURE_NOLOGIN is not set # CONFIG_FEATURE_SECURETTY is not set # CONFIG_PASSWD is not set # CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set # CONFIG_CRYPTPW is not set # CONFIG_CHPASSWD is not set # CONFIG_SU is not set # CONFIG_FEATURE_SU_SYSLOG is not set # CONFIG_FEATURE_SU_CHECKS_SHELLS is not set # CONFIG_SULOGIN is not set # CONFIG_VLOCK is not set # # Linux Ext2 FS Progs # # CONFIG_CHATTR is not set # CONFIG_FSCK is not set # CONFIG_LSATTR is not set # CONFIG_TUNE2FS is not set # # Linux Module Utilities # # CONFIG_MODINFO is not set # CONFIG_MODPROBE_SMALL is not set # CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set # CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set # CONFIG_INSMOD is not set # CONFIG_RMMOD is not set # CONFIG_LSMOD is not set # CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set # CONFIG_MODPROBE is not set # CONFIG_FEATURE_MODPROBE_BLACKLIST is not set # CONFIG_DEPMOD is not set # # Options common to multiple modutils # # CONFIG_FEATURE_2_4_MODULES is not set # CONFIG_FEATURE_INSMOD_TRY_MMAP is not set # CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set # CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set # CONFIG_FEATURE_INSMOD_LOADINKMEM is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set # CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set # CONFIG_FEATURE_MODUTILS_ALIAS is not set # CONFIG_FEATURE_MODUTILS_SYMBOLS is not set CONFIG_DEFAULT_MODULES_DIR="" CONFIG_DEFAULT_DEPMOD_FILE="" # # Linux System Utilities # # CONFIG_ACPID is not set # CONFIG_FEATURE_ACPID_COMPAT is not set # CONFIG_BLKID is not set # CONFIG_DMESG is not set # CONFIG_FEATURE_DMESG_PRETTY is not set # CONFIG_FBSET is not set # CONFIG_FEATURE_FBSET_FANCY is not set # CONFIG_FEATURE_FBSET_READMODE is not set # CONFIG_FDFLUSH is not set # CONFIG_FDFORMAT is not set # CONFIG_FDISK is not set CONFIG_FDISK_SUPPORT_LARGE_DISKS=y # CONFIG_FEATURE_FDISK_WRITABLE is not set # CONFIG_FEATURE_AIX_LABEL is not set # CONFIG_FEATURE_SGI_LABEL is not set # CONFIG_FEATURE_SUN_LABEL is not set # CONFIG_FEATURE_OSF_LABEL is not set # CONFIG_FEATURE_FDISK_ADVANCED is not set # CONFIG_FINDFS is not set # CONFIG_FLOCK is not set # CONFIG_FREERAMDISK is not set # CONFIG_FSCK_MINIX is not set # CONFIG_MKFS_EXT2 is not set # CONFIG_MKFS_MINIX is not set # CONFIG_FEATURE_MINIX2 is not set # CONFIG_MKFS_REISER is not set # CONFIG_MKFS_VFAT is not set # CONFIG_GETOPT is not set # CONFIG_FEATURE_GETOPT_LONG is not set # CONFIG_HEXDUMP is not set # CONFIG_FEATURE_HEXDUMP_REVERSE is not set # CONFIG_HD is not set # CONFIG_HWCLOCK is not set # CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set # CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set # CONFIG_IPCRM is not set # CONFIG_IPCS is not set # CONFIG_LOSETUP is not set # CONFIG_LSPCI is not set # CONFIG_LSUSB is not set # CONFIG_MDEV is not set # CONFIG_FEATURE_MDEV_CONF is not set # CONFIG_FEATURE_MDEV_RENAME is not set # CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set # CONFIG_FEATURE_MDEV_EXEC is not set # CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set # CONFIG_MKSWAP is not set # CONFIG_FEATURE_MKSWAP_UUID is not set # CONFIG_MORE is not set # CONFIG_FEATURE_USE_TERMIOS is not set CONFIG_VOLUMEID=y # CONFIG_FEATURE_VOLUMEID_EXT is not set # CONFIG_FEATURE_VOLUMEID_BTRFS is not set # CONFIG_FEATURE_VOLUMEID_REISERFS is not set # CONFIG_FEATURE_VOLUMEID_FAT is not set # CONFIG_FEATURE_VOLUMEID_HFS is not set # CONFIG_FEATURE_VOLUMEID_JFS is not set # CONFIG_FEATURE_VOLUMEID_XFS is not set # CONFIG_FEATURE_VOLUMEID_NTFS is not set # CONFIG_FEATURE_VOLUMEID_ISO9660 is not set # CONFIG_FEATURE_VOLUMEID_UDF is not set # CONFIG_FEATURE_VOLUMEID_LUKS is not set # CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set # CONFIG_FEATURE_VOLUMEID_CRAMFS is not set # CONFIG_FEATURE_VOLUMEID_ROMFS is not set # CONFIG_FEATURE_VOLUMEID_SYSV is not set # CONFIG_FEATURE_VOLUMEID_OCFS2 is not set # CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set # CONFIG_MOUNT is not set # CONFIG_FEATURE_MOUNT_FAKE is not set # CONFIG_FEATURE_MOUNT_VERBOSE is not set # CONFIG_FEATURE_MOUNT_HELPERS is not set # CONFIG_FEATURE_MOUNT_LABEL is not set # CONFIG_FEATURE_MOUNT_NFS is not set # CONFIG_FEATURE_MOUNT_CIFS is not set # CONFIG_FEATURE_MOUNT_FLAGS is not set # CONFIG_FEATURE_MOUNT_FSTAB is not set # CONFIG_PIVOT_ROOT is not set # CONFIG_RDATE is not set # CONFIG_RDEV is not set # CONFIG_READPROFILE is not set # CONFIG_RTCWAKE is not set # CONFIG_SCRIPT is not set # CONFIG_SCRIPTREPLAY is not set # CONFIG_SETARCH is not set # CONFIG_SWAPONOFF is not set # CONFIG_FEATURE_SWAPON_PRI is not set # CONFIG_SWITCH_ROOT is not set # CONFIG_UMOUNT is not set # CONFIG_FEATURE_UMOUNT_ALL is not set # CONFIG_FEATURE_MOUNT_LOOP is not set # CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set # CONFIG_FEATURE_MTAB_SUPPORT is not set # # Miscellaneous Utilities # # CONFIG_ADJTIMEX is not set CONFIG_BBCONFIG=y # CONFIG_BEEP is not set CONFIG_FEATURE_BEEP_FREQ=0 CONFIG_FEATURE_BEEP_LENGTH_MS=0 # CONFIG_CHAT is not set # CONFIG_FEATURE_CHAT_NOFAIL is not set # CONFIG_FEATURE_CHAT_TTY_HIFI is not set # CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set # CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set # CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set # CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set # CONFIG_FEATURE_CHAT_CLR_ABORT is not set # CONFIG_CHRT is not set # CONFIG_CROND is not set # CONFIG_FEATURE_CROND_D is not set # CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set CONFIG_FEATURE_CROND_DIR="" # CONFIG_CRONTAB is not set # CONFIG_DC is not set # CONFIG_FEATURE_DC_LIBM is not set # CONFIG_DEVFSD is not set # CONFIG_DEVFSD_MODLOAD is not set # CONFIG_DEVFSD_FG_NP is not set # CONFIG_DEVFSD_VERBOSE is not set # CONFIG_FEATURE_DEVFS is not set # CONFIG_DEVMEM is not set # CONFIG_EJECT is not set # CONFIG_FEATURE_EJECT_SCSI is not set # CONFIG_FBSPLASH is not set # CONFIG_FLASHCP is not set # CONFIG_FLASH_LOCK is not set # CONFIG_FLASH_UNLOCK is not set # CONFIG_FLASH_ERASEALL is not set # CONFIG_IONICE is not set # CONFIG_INOTIFYD is not set # CONFIG_LAST is not set # CONFIG_FEATURE_LAST_SMALL is not set # CONFIG_FEATURE_LAST_FANCY is not set # CONFIG_LESS is not set CONFIG_FEATURE_LESS_MAXLINES=0 # CONFIG_FEATURE_LESS_BRACKETS is not set # CONFIG_FEATURE_LESS_FLAGS is not set # CONFIG_FEATURE_LESS_MARKS is not set # CONFIG_FEATURE_LESS_REGEXP is not set # CONFIG_FEATURE_LESS_WINCH is not set # CONFIG_FEATURE_LESS_DASHCMD is not set # CONFIG_FEATURE_LESS_LINENUMS is not set # CONFIG_HDPARM is not set # CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set # CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set # CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set # CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set # CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set # CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set # CONFIG_MAKEDEVS is not set # CONFIG_FEATURE_MAKEDEVS_LEAF is not set # CONFIG_FEATURE_MAKEDEVS_TABLE is not set # CONFIG_MAN is not set # CONFIG_MICROCOM is not set # CONFIG_MOUNTPOINT is not set # CONFIG_MT is not set # CONFIG_RAIDAUTORUN is not set # CONFIG_READAHEAD is not set # CONFIG_RFKILL is not set # CONFIG_RUNLEVEL is not set # CONFIG_RX is not set # CONFIG_SETSID is not set # CONFIG_STRINGS is not set # CONFIG_TASKSET is not set # CONFIG_FEATURE_TASKSET_FANCY is not set # CONFIG_TIME is not set # CONFIG_TIMEOUT is not set # CONFIG_TTYSIZE is not set # CONFIG_VOLNAME is not set # CONFIG_WALL is not set # CONFIG_WATCHDOG is not set # # Networking Utilities # # CONFIG_FEATURE_IPV6 is not set # CONFIG_FEATURE_UNIX_LOCAL is not set # CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set # CONFIG_VERBOSE_RESOLUTION_ERRORS is not set # CONFIG_ARP is not set # CONFIG_ARPING is not set # CONFIG_BRCTL is not set # CONFIG_FEATURE_BRCTL_FANCY is not set # CONFIG_FEATURE_BRCTL_SHOW is not set # CONFIG_DNSD is not set # CONFIG_ETHER_WAKE is not set # CONFIG_FAKEIDENTD is not set # CONFIG_FTPD is not set # CONFIG_FEATURE_FTP_WRITE is not set # CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set # CONFIG_FTPGET is not set # CONFIG_FTPPUT is not set # CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set # CONFIG_HOSTNAME is not set # CONFIG_HTTPD is not set # CONFIG_FEATURE_HTTPD_RANGES is not set # CONFIG_FEATURE_HTTPD_USE_SENDFILE is not set # CONFIG_FEATURE_HTTPD_SETUID is not set # CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set # CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set # CONFIG_FEATURE_HTTPD_CGI is not set # CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set # CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set # CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set # CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set # CONFIG_FEATURE_HTTPD_PROXY is not set # CONFIG_IFCONFIG is not set # CONFIG_FEATURE_IFCONFIG_STATUS is not set # CONFIG_FEATURE_IFCONFIG_SLIP is not set # CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set # CONFIG_FEATURE_IFCONFIG_HW is not set # CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set # CONFIG_IFENSLAVE is not set # CONFIG_IFPLUGD is not set # CONFIG_IFUPDOWN is not set CONFIG_IFUPDOWN_IFSTATE_PATH="" # CONFIG_FEATURE_IFUPDOWN_IP is not set # CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN is not set # CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set # CONFIG_FEATURE_IFUPDOWN_IPV4 is not set # CONFIG_FEATURE_IFUPDOWN_IPV6 is not set # CONFIG_FEATURE_IFUPDOWN_MAPPING is not set # CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set # CONFIG_INETD is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set # CONFIG_FEATURE_INETD_RPC is not set # CONFIG_IP is not set # CONFIG_FEATURE_IP_ADDRESS is not set # CONFIG_FEATURE_IP_LINK is not set # CONFIG_FEATURE_IP_ROUTE is not set # CONFIG_FEATURE_IP_TUNNEL is not set # CONFIG_FEATURE_IP_RULE is not set # CONFIG_FEATURE_IP_SHORT_FORMS is not set # CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set # CONFIG_IPADDR is not set # CONFIG_IPLINK is not set # CONFIG_IPROUTE is not set # CONFIG_IPTUNNEL is not set # CONFIG_IPRULE is not set # CONFIG_IPCALC is not set # CONFIG_FEATURE_IPCALC_FANCY is not set # CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set # CONFIG_NAMEIF is not set # CONFIG_FEATURE_NAMEIF_EXTENDED is not set # CONFIG_NC is not set # CONFIG_NC_SERVER is not set # CONFIG_NC_EXTRA is not set # CONFIG_NETSTAT is not set # CONFIG_FEATURE_NETSTAT_WIDE is not set # CONFIG_FEATURE_NETSTAT_PRG is not set # CONFIG_NSLOOKUP is not set # CONFIG_NTPD is not set # CONFIG_FEATURE_NTPD_SERVER is not set # CONFIG_PING is not set # CONFIG_PING6 is not set # CONFIG_FEATURE_FANCY_PING is not set # CONFIG_PSCAN is not set # CONFIG_ROUTE is not set # CONFIG_SLATTACH is not set # CONFIG_TCPSVD is not set # CONFIG_TELNET is not set # CONFIG_FEATURE_TELNET_TTYPE is not set # CONFIG_FEATURE_TELNET_AUTOLOGIN is not set # CONFIG_TELNETD is not set # CONFIG_FEATURE_TELNETD_STANDALONE is not set # CONFIG_FEATURE_TELNETD_INETD_WAIT is not set # CONFIG_TFTP is not set # CONFIG_TFTPD is not set # CONFIG_FEATURE_TFTP_GET is not set # CONFIG_FEATURE_TFTP_PUT is not set # CONFIG_FEATURE_TFTP_BLOCKSIZE is not set # CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set # CONFIG_TFTP_DEBUG is not set # CONFIG_TRACEROUTE is not set # CONFIG_TRACEROUTE6 is not set # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set # CONFIG_TUNCTL is not set # CONFIG_FEATURE_TUNCTL_UG is not set # CONFIG_UDHCPD is not set # CONFIG_DHCPRELAY is not set # CONFIG_DUMPLEASES is not set # CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set CONFIG_DHCPD_LEASES_FILE="" # CONFIG_UDHCPC is not set # CONFIG_FEATURE_UDHCPC_ARPING is not set # CONFIG_FEATURE_UDHCP_PORT is not set CONFIG_UDHCP_DEBUG=0 # CONFIG_FEATURE_UDHCP_RFC3397 is not set CONFIG_UDHCPC_DEFAULT_SCRIPT="" CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0 CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="" # CONFIG_UDPSVD is not set # CONFIG_VCONFIG is not set # CONFIG_WGET is not set # CONFIG_FEATURE_WGET_STATUSBAR is not set # CONFIG_FEATURE_WGET_AUTHENTICATION is not set # CONFIG_FEATURE_WGET_LONG_OPTIONS is not set # CONFIG_ZCIP is not set # # Print Utilities # # CONFIG_LPD is not set # CONFIG_LPR is not set # CONFIG_LPQ is not set # # Mail Utilities # # CONFIG_MAKEMIME is not set CONFIG_FEATURE_MIME_CHARSET="" # CONFIG_POPMAILDIR is not set # CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set # CONFIG_REFORMIME is not set # CONFIG_FEATURE_REFORMIME_COMPAT is not set # CONFIG_SENDMAIL is not set # # Process Utilities # # CONFIG_FREE is not set # CONFIG_FUSER is not set # CONFIG_KILL is not set # CONFIG_KILLALL is not set # CONFIG_KILLALL5 is not set # CONFIG_NMETER is not set # CONFIG_PGREP is not set # CONFIG_PIDOF is not set # CONFIG_FEATURE_PIDOF_SINGLE is not set # CONFIG_FEATURE_PIDOF_OMIT is not set # CONFIG_PKILL is not set # CONFIG_PS is not set # CONFIG_FEATURE_PS_WIDE is not set # CONFIG_FEATURE_PS_TIME is not set # CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set # CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set # CONFIG_RENICE is not set # CONFIG_BB_SYSCTL is not set # CONFIG_TOP is not set # CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set # CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set # CONFIG_FEATURE_TOP_SMP_CPU is not set # CONFIG_FEATURE_TOP_DECIMALS is not set # CONFIG_FEATURE_TOP_SMP_PROCESS is not set # CONFIG_FEATURE_TOPMEM is not set # CONFIG_FEATURE_SHOW_THREADS is not set # CONFIG_UPTIME is not set # CONFIG_WATCH is not set # # Runit Utilities # # CONFIG_RUNSV is not set # CONFIG_RUNSVDIR is not set # CONFIG_FEATURE_RUNSVDIR_LOG is not set # CONFIG_SV is not set CONFIG_SV_DEFAULT_SERVICE_DIR="" # CONFIG_SVLOGD is not set # CONFIG_CHPST is not set # CONFIG_SETUIDGID is not set # CONFIG_ENVUIDGID is not set # CONFIG_ENVDIR is not set # CONFIG_SOFTLIMIT is not set # CONFIG_CHCON is not set # CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set # CONFIG_GETENFORCE is not set # CONFIG_GETSEBOOL is not set # CONFIG_LOAD_POLICY is not set # CONFIG_MATCHPATHCON is not set # CONFIG_RESTORECON is not set # CONFIG_RUNCON is not set # CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set # CONFIG_SELINUXENABLED is not set # CONFIG_SETENFORCE is not set # CONFIG_SETFILES is not set # CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set # CONFIG_SETSEBOOL is not set # CONFIG_SESTATUS is not set # # Shells # # CONFIG_ASH is not set # CONFIG_ASH_BASH_COMPAT is not set # CONFIG_ASH_JOB_CONTROL is not set # CONFIG_ASH_ALIAS is not set # CONFIG_ASH_GETOPTS is not set # CONFIG_ASH_BUILTIN_ECHO is not set # CONFIG_ASH_BUILTIN_PRINTF is not set # CONFIG_ASH_BUILTIN_TEST is not set # CONFIG_ASH_CMDCMD is not set # CONFIG_ASH_MAIL is not set # CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set # CONFIG_ASH_RANDOM_SUPPORT is not set # CONFIG_ASH_EXPAND_PRMT is not set # CONFIG_HUSH is not set # CONFIG_HUSH_BASH_COMPAT is not set # CONFIG_HUSH_HELP is not set # CONFIG_HUSH_INTERACTIVE is not set # CONFIG_HUSH_JOB is not set # CONFIG_HUSH_TICK is not set # CONFIG_HUSH_IF is not set # CONFIG_HUSH_LOOPS is not set # CONFIG_HUSH_CASE is not set # CONFIG_HUSH_FUNCTIONS is not set # CONFIG_HUSH_LOCAL is not set # CONFIG_HUSH_EXPORT_N is not set # CONFIG_HUSH_RANDOM_SUPPORT is not set # CONFIG_FEATURE_SH_IS_ASH is not set # CONFIG_FEATURE_SH_IS_HUSH is not set CONFIG_FEATURE_SH_IS_NONE=y # CONFIG_FEATURE_BASH_IS_ASH is not set # CONFIG_FEATURE_BASH_IS_HUSH is not set CONFIG_FEATURE_BASH_IS_NONE=y # CONFIG_MSH is not set # CONFIG_SH_MATH_SUPPORT is not set # CONFIG_SH_MATH_SUPPORT_64 is not set # CONFIG_FEATURE_SH_EXTRA_QUIET is not set # CONFIG_FEATURE_SH_STANDALONE is not set # CONFIG_FEATURE_SH_NOFORK is not set # CONFIG_CTTYHACK is not set # # System Logging Utilities # # CONFIG_SYSLOGD is not set # CONFIG_FEATURE_ROTATE_LOGFILE is not set # CONFIG_FEATURE_REMOTE_LOG is not set # CONFIG_FEATURE_SYSLOGD_DUP is not set CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0 # CONFIG_FEATURE_IPC_SYSLOG is not set CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0 # CONFIG_LOGREAD is not set # CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set # CONFIG_KLOGD is not set # CONFIG_LOGGER is not set busybox-1.22.1/configs/cygwin_defconfig0000644000000000000000000006321112263563520016654 0ustar rootroot# # Automatically generated make config: don't edit # Busybox version: 1.19.0.git # Sun Jul 10 12:48:50 2011 # CONFIG_HAVE_DOT_CONFIG=y # # Busybox Settings # # # General Configuration # CONFIG_DESKTOP=y # CONFIG_EXTRA_COMPAT is not set CONFIG_INCLUDE_SUSv2=y # CONFIG_USE_PORTABLE_CODE is not set CONFIG_PLATFORM_LINUX=y CONFIG_FEATURE_BUFFERS_USE_MALLOC=y # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set CONFIG_SHOW_USAGE=y CONFIG_FEATURE_VERBOSE_USAGE=y CONFIG_FEATURE_COMPRESS_USAGE=y CONFIG_FEATURE_INSTALLER=y # CONFIG_INSTALL_NO_USR is not set # CONFIG_LOCALE_SUPPORT is not set CONFIG_UNICODE_SUPPORT=y # CONFIG_UNICODE_USING_LOCALE is not set # CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set CONFIG_SUBST_WCHAR=65533 CONFIG_LAST_SUPPORTED_WCHAR=0 # CONFIG_UNICODE_COMBINING_WCHARS is not set # CONFIG_UNICODE_WIDE_WCHARS is not set # CONFIG_UNICODE_BIDI_SUPPORT is not set # CONFIG_UNICODE_NEUTRAL_TABLE is not set # CONFIG_UNICODE_PRESERVE_BROKEN is not set CONFIG_LONG_OPTS=y CONFIG_FEATURE_DEVPTS=y # CONFIG_FEATURE_CLEAN_UP is not set # CONFIG_FEATURE_UTMP is not set # CONFIG_FEATURE_WTMP is not set CONFIG_FEATURE_PIDFILE=y CONFIG_FEATURE_SUID=y # CONFIG_FEATURE_SUID_CONFIG is not set # CONFIG_FEATURE_SUID_CONFIG_QUIET is not set # CONFIG_SELINUX is not set # CONFIG_FEATURE_PREFER_APPLETS is not set CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" CONFIG_FEATURE_SYSLOG=y # CONFIG_FEATURE_HAVE_RPC is not set # # Build Options # # CONFIG_STATIC is not set # CONFIG_PIE is not set # CONFIG_NOMMU is not set # CONFIG_BUILD_LIBBUSYBOX is not set # CONFIG_FEATURE_INDIVIDUAL is not set # CONFIG_FEATURE_SHARED_BUSYBOX is not set CONFIG_LFS=y CONFIG_CROSS_COMPILER_PREFIX="" CONFIG_EXTRA_CFLAGS="" # # Debugging Options # # CONFIG_DEBUG is not set # CONFIG_DEBUG_PESSIMIZE is not set # CONFIG_WERROR is not set CONFIG_NO_DEBUG_LIB=y # CONFIG_DMALLOC is not set # CONFIG_EFENCE is not set # # Installation Options ("make install" behavior) # CONFIG_INSTALL_APPLET_SYMLINKS=y # CONFIG_INSTALL_APPLET_HARDLINKS is not set # CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set # CONFIG_INSTALL_APPLET_DONT is not set # CONFIG_INSTALL_SH_APPLET_SYMLINK is not set # CONFIG_INSTALL_SH_APPLET_HARDLINK is not set # CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set CONFIG_PREFIX="./_install" # # Busybox Library Tuning # # CONFIG_FEATURE_SYSTEMD is not set CONFIG_FEATURE_RTMINMAX=y CONFIG_PASSWORD_MINLEN=6 CONFIG_MD5_SMALL=1 CONFIG_FEATURE_FAST_TOP=y # CONFIG_FEATURE_ETC_NETWORKS is not set CONFIG_FEATURE_USE_TERMIOS=y CONFIG_FEATURE_EDITING=y CONFIG_FEATURE_EDITING_MAX_LEN=1024 # CONFIG_FEATURE_EDITING_VI is not set CONFIG_FEATURE_EDITING_HISTORY=255 CONFIG_FEATURE_EDITING_SAVEHISTORY=y CONFIG_FEATURE_TAB_COMPLETION=y # CONFIG_FEATURE_USERNAME_COMPLETION is not set CONFIG_FEATURE_EDITING_FANCY_PROMPT=y # CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set CONFIG_FEATURE_NON_POSIX_CP=y # CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set CONFIG_FEATURE_COPYBUF_KB=4 CONFIG_FEATURE_SKIP_ROOTFS=y # CONFIG_MONOTONIC_SYSCALL is not set CONFIG_IOCTL_HEX2STR_ERROR=y CONFIG_FEATURE_HWIB=y # # Applets # # # Archival Utilities # CONFIG_FEATURE_SEAMLESS_XZ=y CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y # CONFIG_FEATURE_SEAMLESS_Z is not set # CONFIG_AR is not set # CONFIG_FEATURE_AR_LONG_FILENAMES is not set # CONFIG_FEATURE_AR_CREATE is not set CONFIG_BUNZIP2=y CONFIG_BZIP2=y CONFIG_CPIO=y CONFIG_FEATURE_CPIO_O=y CONFIG_FEATURE_CPIO_P=y # CONFIG_DPKG is not set # CONFIG_DPKG_DEB is not set # CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set CONFIG_GUNZIP=y CONFIG_GZIP=y CONFIG_FEATURE_GZIP_LONG_OPTIONS=y CONFIG_LZOP=y # CONFIG_LZOP_COMPR_HIGH is not set CONFIG_RPM2CPIO=y CONFIG_RPM=y CONFIG_TAR=y CONFIG_FEATURE_TAR_CREATE=y CONFIG_FEATURE_TAR_AUTODETECT=y CONFIG_FEATURE_TAR_FROM=y CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y CONFIG_FEATURE_TAR_LONG_OPTIONS=y CONFIG_FEATURE_TAR_TO_COMMAND=y CONFIG_FEATURE_TAR_UNAME_GNAME=y CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y # CONFIG_FEATURE_TAR_SELINUX is not set # CONFIG_UNCOMPRESS is not set CONFIG_UNLZMA=y CONFIG_FEATURE_LZMA_FAST=y CONFIG_LZMA=y CONFIG_UNXZ=y CONFIG_XZ=y CONFIG_UNZIP=y # # Coreutils # CONFIG_BASENAME=y CONFIG_CAT=y CONFIG_DATE=y CONFIG_FEATURE_DATE_ISOFMT=y # CONFIG_FEATURE_DATE_NANO is not set CONFIG_FEATURE_DATE_COMPAT=y CONFIG_ID=y CONFIG_GROUPS=y CONFIG_TEST=y CONFIG_FEATURE_TEST_64=y CONFIG_TOUCH=y CONFIG_TR=y CONFIG_FEATURE_TR_CLASSES=y CONFIG_FEATURE_TR_EQUIV=y CONFIG_BASE64=y CONFIG_CAL=y CONFIG_CATV=y CONFIG_CHGRP=y CONFIG_CHMOD=y CONFIG_CHOWN=y CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y CONFIG_CHROOT=y CONFIG_CKSUM=y CONFIG_COMM=y CONFIG_CP=y CONFIG_FEATURE_CP_LONG_OPTIONS=y CONFIG_CUT=y CONFIG_DD=y CONFIG_FEATURE_DD_SIGNAL_HANDLING=y CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y CONFIG_FEATURE_DD_IBS_OBS=y CONFIG_DF=y CONFIG_FEATURE_DF_FANCY=y CONFIG_DIRNAME=y CONFIG_DOS2UNIX=y CONFIG_UNIX2DOS=y CONFIG_DU=y # CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K is not set CONFIG_ECHO=y CONFIG_FEATURE_FANCY_ECHO=y CONFIG_ENV=y CONFIG_FEATURE_ENV_LONG_OPTIONS=y CONFIG_EXPAND=y CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y CONFIG_EXPR=y CONFIG_EXPR_MATH_SUPPORT_64=y CONFIG_FALSE=y CONFIG_FOLD=y CONFIG_FSYNC=y CONFIG_HEAD=y CONFIG_FEATURE_FANCY_HEAD=y CONFIG_HOSTID=y CONFIG_INSTALL=y CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y CONFIG_LN=y CONFIG_LOGNAME=y CONFIG_LS=y CONFIG_FEATURE_LS_FILETYPES=y CONFIG_FEATURE_LS_FOLLOWLINKS=y CONFIG_FEATURE_LS_RECURSIVE=y CONFIG_FEATURE_LS_SORTFILES=y CONFIG_FEATURE_LS_TIMESTAMPS=y CONFIG_FEATURE_LS_USERNAME=y CONFIG_FEATURE_LS_COLOR=y CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y CONFIG_MD5SUM=y CONFIG_MKDIR=y CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y CONFIG_MKFIFO=y CONFIG_MKNOD=y CONFIG_MV=y CONFIG_FEATURE_MV_LONG_OPTIONS=y CONFIG_NICE=y CONFIG_NOHUP=y CONFIG_OD=y CONFIG_PRINTENV=y CONFIG_PRINTF=y CONFIG_PWD=y CONFIG_READLINK=y CONFIG_FEATURE_READLINK_FOLLOW=y CONFIG_REALPATH=y CONFIG_RM=y CONFIG_RMDIR=y CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y CONFIG_SEQ=y CONFIG_SHA1SUM=y CONFIG_SHA256SUM=y CONFIG_SHA512SUM=y CONFIG_SLEEP=y CONFIG_FEATURE_FANCY_SLEEP=y CONFIG_FEATURE_FLOAT_SLEEP=y CONFIG_SORT=y CONFIG_FEATURE_SORT_BIG=y CONFIG_SPLIT=y CONFIG_FEATURE_SPLIT_FANCY=y # CONFIG_STAT is not set # CONFIG_FEATURE_STAT_FORMAT is not set CONFIG_STTY=y CONFIG_SUM=y CONFIG_SYNC=y CONFIG_TAC=y CONFIG_TAIL=y CONFIG_FEATURE_FANCY_TAIL=y CONFIG_TEE=y CONFIG_FEATURE_TEE_USE_BLOCK_IO=y CONFIG_TRUE=y CONFIG_TTY=y CONFIG_UNAME=y CONFIG_UNEXPAND=y CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y CONFIG_UNIQ=y CONFIG_USLEEP=y CONFIG_UUDECODE=y CONFIG_UUENCODE=y CONFIG_WC=y CONFIG_FEATURE_WC_LARGE=y # CONFIG_WHO is not set CONFIG_WHOAMI=y CONFIG_YES=y # # Common options for cp and mv # CONFIG_FEATURE_PRESERVE_HARDLINKS=y # # Common options for ls, more and telnet # CONFIG_FEATURE_AUTOWIDTH=y # # Common options for df, du, ls # CONFIG_FEATURE_HUMAN_READABLE=y # # Common options for md5sum, sha1sum, sha256sum, sha512sum # CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y # # Console Utilities # # CONFIG_CHVT is not set # CONFIG_FGCONSOLE is not set CONFIG_CLEAR=y # CONFIG_DEALLOCVT is not set # CONFIG_DUMPKMAP is not set # CONFIG_KBD_MODE is not set # CONFIG_LOADFONT is not set # CONFIG_LOADKMAP is not set # CONFIG_OPENVT is not set CONFIG_RESET=y CONFIG_RESIZE=y CONFIG_FEATURE_RESIZE_PRINT=y # CONFIG_SETCONSOLE is not set # CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set # CONFIG_SETFONT is not set # CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set CONFIG_DEFAULT_SETFONT_DIR="" # CONFIG_SETKEYCODES is not set # CONFIG_SETLOGCONS is not set # CONFIG_SHOWKEY is not set # CONFIG_FEATURE_LOADFONT_PSF2 is not set # CONFIG_FEATURE_LOADFONT_RAW is not set # # Debian Utilities # CONFIG_MKTEMP=y CONFIG_PIPE_PROGRESS=y CONFIG_RUN_PARTS=y CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y CONFIG_FEATURE_RUN_PARTS_FANCY=y CONFIG_START_STOP_DAEMON=y CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS=y CONFIG_WHICH=y # # Editors # CONFIG_PATCH=y CONFIG_VI=y CONFIG_FEATURE_VI_MAX_LEN=4096 # CONFIG_FEATURE_VI_8BIT is not set CONFIG_FEATURE_VI_COLON=y CONFIG_FEATURE_VI_YANKMARK=y CONFIG_FEATURE_VI_SEARCH=y # CONFIG_FEATURE_VI_REGEX_SEARCH is not set CONFIG_FEATURE_VI_USE_SIGNALS=y CONFIG_FEATURE_VI_DOT_CMD=y CONFIG_FEATURE_VI_READONLY=y CONFIG_FEATURE_VI_SETOPTS=y CONFIG_FEATURE_VI_SET=y CONFIG_FEATURE_VI_WIN_RESIZE=y CONFIG_FEATURE_VI_ASK_TERMINAL=y CONFIG_AWK=y CONFIG_FEATURE_AWK_LIBM=y CONFIG_CMP=y CONFIG_DIFF=y CONFIG_FEATURE_DIFF_LONG_OPTIONS=y CONFIG_FEATURE_DIFF_DIR=y CONFIG_ED=y CONFIG_SED=y CONFIG_FEATURE_ALLOW_EXEC=y # # Finding Utilities # CONFIG_FIND=y CONFIG_FEATURE_FIND_PRINT0=y CONFIG_FEATURE_FIND_MTIME=y CONFIG_FEATURE_FIND_MMIN=y CONFIG_FEATURE_FIND_PERM=y CONFIG_FEATURE_FIND_TYPE=y CONFIG_FEATURE_FIND_XDEV=y CONFIG_FEATURE_FIND_MAXDEPTH=y CONFIG_FEATURE_FIND_NEWER=y CONFIG_FEATURE_FIND_INUM=y CONFIG_FEATURE_FIND_EXEC=y CONFIG_FEATURE_FIND_USER=y CONFIG_FEATURE_FIND_GROUP=y CONFIG_FEATURE_FIND_NOT=y CONFIG_FEATURE_FIND_DEPTH=y CONFIG_FEATURE_FIND_PAREN=y CONFIG_FEATURE_FIND_SIZE=y CONFIG_FEATURE_FIND_PRUNE=y CONFIG_FEATURE_FIND_DELETE=y CONFIG_FEATURE_FIND_PATH=y CONFIG_FEATURE_FIND_REGEX=y # CONFIG_FEATURE_FIND_CONTEXT is not set CONFIG_FEATURE_FIND_LINKS=y CONFIG_GREP=y CONFIG_FEATURE_GREP_EGREP_ALIAS=y CONFIG_FEATURE_GREP_FGREP_ALIAS=y CONFIG_FEATURE_GREP_CONTEXT=y CONFIG_XARGS=y CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y # # Init Utilities # # CONFIG_BOOTCHARTD is not set # CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set # CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set # CONFIG_HALT is not set # CONFIG_FEATURE_CALL_TELINIT is not set CONFIG_TELINIT_PATH="" # CONFIG_INIT is not set # CONFIG_FEATURE_USE_INITTAB is not set # CONFIG_FEATURE_KILL_REMOVED is not set CONFIG_FEATURE_KILL_DELAY=0 # CONFIG_FEATURE_INIT_SCTTY is not set # CONFIG_FEATURE_INIT_SYSLOG is not set # CONFIG_FEATURE_EXTRA_QUIET is not set # CONFIG_FEATURE_INIT_COREDUMPS is not set # CONFIG_FEATURE_INITRD is not set CONFIG_INIT_TERMINAL_TYPE="" CONFIG_MESG=y CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y # # Login/Password Management Utilities # CONFIG_ADD_SHELL=y CONFIG_REMOVE_SHELL=y CONFIG_FEATURE_SHADOWPASSWDS=y CONFIG_USE_BB_PWD_GRP=y CONFIG_USE_BB_SHADOW=y CONFIG_USE_BB_CRYPT=y CONFIG_USE_BB_CRYPT_SHA=y CONFIG_ADDUSER=y CONFIG_FEATURE_ADDUSER_LONG_OPTIONS=y # CONFIG_FEATURE_CHECK_NAMES is not set CONFIG_FIRST_SYSTEM_ID=100 CONFIG_LAST_SYSTEM_ID=999 CONFIG_ADDGROUP=y CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y CONFIG_FEATURE_ADDUSER_TO_GROUP=y CONFIG_DELUSER=y CONFIG_DELGROUP=y CONFIG_FEATURE_DEL_USER_FROM_GROUP=y # CONFIG_GETTY is not set CONFIG_LOGIN=y # CONFIG_PAM is not set CONFIG_LOGIN_SCRIPTS=y CONFIG_FEATURE_NOLOGIN=y CONFIG_FEATURE_SECURETTY=y CONFIG_PASSWD=y CONFIG_FEATURE_PASSWD_WEAK_CHECK=y CONFIG_CRYPTPW=y CONFIG_CHPASSWD=y CONFIG_SU=y CONFIG_FEATURE_SU_SYSLOG=y CONFIG_FEATURE_SU_CHECKS_SHELLS=y CONFIG_SULOGIN=y CONFIG_VLOCK=y # # Linux Ext2 FS Progs # CONFIG_CHATTR=y # CONFIG_FSCK is not set # CONFIG_LSATTR is not set # CONFIG_TUNE2FS is not set # # Linux Module Utilities # # CONFIG_MODINFO is not set # CONFIG_MODPROBE_SMALL is not set # CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set # CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set # CONFIG_INSMOD is not set # CONFIG_RMMOD is not set # CONFIG_LSMOD is not set # CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set # CONFIG_MODPROBE is not set # CONFIG_FEATURE_MODPROBE_BLACKLIST is not set # CONFIG_DEPMOD is not set # # Options common to multiple modutils # # CONFIG_FEATURE_2_4_MODULES is not set # CONFIG_FEATURE_INSMOD_TRY_MMAP is not set # CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set # CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set # CONFIG_FEATURE_INSMOD_LOADINKMEM is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set # CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set # CONFIG_FEATURE_MODUTILS_ALIAS is not set # CONFIG_FEATURE_MODUTILS_SYMBOLS is not set CONFIG_DEFAULT_MODULES_DIR="" CONFIG_DEFAULT_DEPMOD_FILE="" # # Linux System Utilities # # CONFIG_BLOCKDEV is not set CONFIG_REV=y # CONFIG_ACPID is not set # CONFIG_FEATURE_ACPID_COMPAT is not set # CONFIG_BLKID is not set # CONFIG_FEATURE_BLKID_TYPE is not set # CONFIG_DMESG is not set # CONFIG_FEATURE_DMESG_PRETTY is not set # CONFIG_FBSET is not set # CONFIG_FEATURE_FBSET_FANCY is not set # CONFIG_FEATURE_FBSET_READMODE is not set # CONFIG_FDFLUSH is not set # CONFIG_FDFORMAT is not set # CONFIG_FDISK is not set # CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set # CONFIG_FEATURE_FDISK_WRITABLE is not set # CONFIG_FEATURE_AIX_LABEL is not set # CONFIG_FEATURE_SGI_LABEL is not set # CONFIG_FEATURE_SUN_LABEL is not set # CONFIG_FEATURE_OSF_LABEL is not set # CONFIG_FEATURE_GPT_LABEL is not set # CONFIG_FEATURE_FDISK_ADVANCED is not set # CONFIG_FINDFS is not set CONFIG_FLOCK=y # CONFIG_FREERAMDISK is not set CONFIG_FSCK_MINIX=y # CONFIG_MKFS_EXT2 is not set # CONFIG_MKFS_MINIX is not set CONFIG_FEATURE_MINIX2=y # CONFIG_MKFS_REISER is not set # CONFIG_MKFS_VFAT is not set CONFIG_GETOPT=y CONFIG_FEATURE_GETOPT_LONG=y CONFIG_HEXDUMP=y CONFIG_FEATURE_HEXDUMP_REVERSE=y CONFIG_HD=y # CONFIG_HWCLOCK is not set # CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set # CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set CONFIG_IPCRM=y # CONFIG_IPCS is not set # CONFIG_LOSETUP is not set # CONFIG_LSPCI is not set # CONFIG_LSUSB is not set # CONFIG_MDEV is not set # CONFIG_FEATURE_MDEV_CONF is not set # CONFIG_FEATURE_MDEV_RENAME is not set # CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set # CONFIG_FEATURE_MDEV_EXEC is not set # CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set CONFIG_MKSWAP=y CONFIG_FEATURE_MKSWAP_UUID=y CONFIG_MORE=y # CONFIG_MOUNT is not set # CONFIG_FEATURE_MOUNT_FAKE is not set # CONFIG_FEATURE_MOUNT_VERBOSE is not set # CONFIG_FEATURE_MOUNT_HELPERS is not set # CONFIG_FEATURE_MOUNT_LABEL is not set # CONFIG_FEATURE_MOUNT_NFS is not set # CONFIG_FEATURE_MOUNT_CIFS is not set # CONFIG_FEATURE_MOUNT_FLAGS is not set # CONFIG_FEATURE_MOUNT_FSTAB is not set # CONFIG_PIVOT_ROOT is not set # CONFIG_RDATE is not set CONFIG_RDEV=y CONFIG_READPROFILE=y # CONFIG_RTCWAKE is not set CONFIG_SCRIPT=y CONFIG_SCRIPTREPLAY=y # CONFIG_SETARCH is not set # CONFIG_SWAPONOFF is not set # CONFIG_FEATURE_SWAPON_PRI is not set # CONFIG_SWITCH_ROOT is not set # CONFIG_UMOUNT is not set # CONFIG_FEATURE_UMOUNT_ALL is not set # CONFIG_FEATURE_MOUNT_LOOP is not set # CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set # CONFIG_FEATURE_MTAB_SUPPORT is not set # CONFIG_VOLUMEID is not set # CONFIG_FEATURE_VOLUMEID_EXT is not set # CONFIG_FEATURE_VOLUMEID_BTRFS is not set # CONFIG_FEATURE_VOLUMEID_REISERFS is not set # CONFIG_FEATURE_VOLUMEID_FAT is not set # CONFIG_FEATURE_VOLUMEID_HFS is not set # CONFIG_FEATURE_VOLUMEID_JFS is not set # CONFIG_FEATURE_VOLUMEID_XFS is not set # CONFIG_FEATURE_VOLUMEID_NTFS is not set # CONFIG_FEATURE_VOLUMEID_ISO9660 is not set # CONFIG_FEATURE_VOLUMEID_UDF is not set # CONFIG_FEATURE_VOLUMEID_LUKS is not set # CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set # CONFIG_FEATURE_VOLUMEID_CRAMFS is not set # CONFIG_FEATURE_VOLUMEID_ROMFS is not set # CONFIG_FEATURE_VOLUMEID_SYSV is not set # CONFIG_FEATURE_VOLUMEID_OCFS2 is not set # CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set # # Miscellaneous Utilities # # CONFIG_CONSPY is not set # CONFIG_NANDWRITE is not set # CONFIG_NANDDUMP is not set # CONFIG_SETSERIAL is not set # CONFIG_UBIATTACH is not set # CONFIG_UBIDETACH is not set # CONFIG_UBIMKVOL is not set # CONFIG_UBIRMVOL is not set # CONFIG_UBIRSVOL is not set # CONFIG_UBIUPDATEVOL is not set # CONFIG_ADJTIMEX is not set # CONFIG_BBCONFIG is not set # CONFIG_FEATURE_COMPRESS_BBCONFIG is not set # CONFIG_BEEP is not set CONFIG_FEATURE_BEEP_FREQ=0 CONFIG_FEATURE_BEEP_LENGTH_MS=0 CONFIG_CHAT=y CONFIG_FEATURE_CHAT_NOFAIL=y # CONFIG_FEATURE_CHAT_TTY_HIFI is not set CONFIG_FEATURE_CHAT_IMPLICIT_CR=y CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y CONFIG_FEATURE_CHAT_SEND_ESCAPES=y CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y CONFIG_FEATURE_CHAT_CLR_ABORT=y CONFIG_CHRT=y CONFIG_CROND=y CONFIG_FEATURE_CROND_D=y CONFIG_FEATURE_CROND_CALL_SENDMAIL=y CONFIG_FEATURE_CROND_DIR="/var/spool/cron" CONFIG_CRONTAB=y CONFIG_DC=y CONFIG_FEATURE_DC_LIBM=y # CONFIG_DEVFSD is not set # CONFIG_DEVFSD_MODLOAD is not set # CONFIG_DEVFSD_FG_NP is not set # CONFIG_DEVFSD_VERBOSE is not set # CONFIG_FEATURE_DEVFS is not set CONFIG_DEVMEM=y # CONFIG_EJECT is not set # CONFIG_FEATURE_EJECT_SCSI is not set # CONFIG_FBSPLASH is not set # CONFIG_FLASHCP is not set # CONFIG_FLASH_LOCK is not set # CONFIG_FLASH_UNLOCK is not set # CONFIG_FLASH_ERASEALL is not set # CONFIG_IONICE is not set # CONFIG_INOTIFYD is not set # CONFIG_LAST is not set # CONFIG_FEATURE_LAST_SMALL is not set # CONFIG_FEATURE_LAST_FANCY is not set CONFIG_LESS=y CONFIG_FEATURE_LESS_MAXLINES=9999999 CONFIG_FEATURE_LESS_BRACKETS=y CONFIG_FEATURE_LESS_FLAGS=y CONFIG_FEATURE_LESS_MARKS=y CONFIG_FEATURE_LESS_REGEXP=y CONFIG_FEATURE_LESS_WINCH=y CONFIG_FEATURE_LESS_DASHCMD=y CONFIG_FEATURE_LESS_LINENUMS=y # CONFIG_HDPARM is not set # CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set # CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set # CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set # CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set # CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set # CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set # CONFIG_MAKEDEVS is not set # CONFIG_FEATURE_MAKEDEVS_LEAF is not set # CONFIG_FEATURE_MAKEDEVS_TABLE is not set CONFIG_MAN=y # CONFIG_MICROCOM is not set # CONFIG_MOUNTPOINT is not set CONFIG_MT=y # CONFIG_RAIDAUTORUN is not set # CONFIG_READAHEAD is not set # CONFIG_RFKILL is not set # CONFIG_RUNLEVEL is not set # CONFIG_RX is not set CONFIG_SETSID=y CONFIG_STRINGS=y # CONFIG_TASKSET is not set # CONFIG_FEATURE_TASKSET_FANCY is not set CONFIG_TIME=y CONFIG_TIMEOUT=y CONFIG_TTYSIZE=y CONFIG_VOLNAME=y # CONFIG_WALL is not set # CONFIG_WATCHDOG is not set # # Networking Utilities # # CONFIG_NAMEIF is not set # CONFIG_FEATURE_NAMEIF_EXTENDED is not set # CONFIG_NBDCLIENT is not set CONFIG_NC=y CONFIG_NC_SERVER=y CONFIG_NC_EXTRA=y # CONFIG_NC_110_COMPAT is not set # CONFIG_PING is not set # CONFIG_PING6 is not set # CONFIG_FEATURE_FANCY_PING is not set CONFIG_WHOIS=y CONFIG_FEATURE_IPV6=y # CONFIG_FEATURE_UNIX_LOCAL is not set CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y # CONFIG_VERBOSE_RESOLUTION_ERRORS is not set # CONFIG_ARP is not set # CONFIG_ARPING is not set # CONFIG_BRCTL is not set # CONFIG_FEATURE_BRCTL_FANCY is not set # CONFIG_FEATURE_BRCTL_SHOW is not set CONFIG_DNSD=y # CONFIG_ETHER_WAKE is not set CONFIG_FAKEIDENTD=y CONFIG_FTPD=y CONFIG_FEATURE_FTP_WRITE=y CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y CONFIG_FTPGET=y CONFIG_FTPPUT=y CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y CONFIG_HOSTNAME=y CONFIG_HTTPD=y CONFIG_FEATURE_HTTPD_RANGES=y # CONFIG_FEATURE_HTTPD_USE_SENDFILE is not set CONFIG_FEATURE_HTTPD_SETUID=y CONFIG_FEATURE_HTTPD_BASIC_AUTH=y CONFIG_FEATURE_HTTPD_AUTH_MD5=y CONFIG_FEATURE_HTTPD_CGI=y CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y CONFIG_FEATURE_HTTPD_ERROR_PAGES=y CONFIG_FEATURE_HTTPD_PROXY=y CONFIG_FEATURE_HTTPD_GZIP=y # CONFIG_IFCONFIG is not set # CONFIG_FEATURE_IFCONFIG_STATUS is not set # CONFIG_FEATURE_IFCONFIG_SLIP is not set # CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set # CONFIG_FEATURE_IFCONFIG_HW is not set # CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set # CONFIG_IFENSLAVE is not set # CONFIG_IFPLUGD is not set # CONFIG_IFUPDOWN is not set CONFIG_IFUPDOWN_IFSTATE_PATH="" # CONFIG_FEATURE_IFUPDOWN_IP is not set # CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN is not set # CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set # CONFIG_FEATURE_IFUPDOWN_IPV4 is not set # CONFIG_FEATURE_IFUPDOWN_IPV6 is not set # CONFIG_FEATURE_IFUPDOWN_MAPPING is not set # CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set CONFIG_INETD=y CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO=y CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD=y CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME=y CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME=y CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN=y # CONFIG_FEATURE_INETD_RPC is not set # CONFIG_IP is not set # CONFIG_FEATURE_IP_ADDRESS is not set # CONFIG_FEATURE_IP_LINK is not set # CONFIG_FEATURE_IP_ROUTE is not set # CONFIG_FEATURE_IP_TUNNEL is not set # CONFIG_FEATURE_IP_RULE is not set # CONFIG_FEATURE_IP_SHORT_FORMS is not set # CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set # CONFIG_IPADDR is not set # CONFIG_IPLINK is not set # CONFIG_IPROUTE is not set # CONFIG_IPTUNNEL is not set # CONFIG_IPRULE is not set CONFIG_IPCALC=y CONFIG_FEATURE_IPCALC_FANCY=y CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y # CONFIG_NETSTAT is not set # CONFIG_FEATURE_NETSTAT_WIDE is not set # CONFIG_FEATURE_NETSTAT_PRG is not set # CONFIG_NSLOOKUP is not set # CONFIG_NTPD is not set # CONFIG_FEATURE_NTPD_SERVER is not set CONFIG_PSCAN=y # CONFIG_ROUTE is not set # CONFIG_SLATTACH is not set CONFIG_TCPSVD=y CONFIG_TELNET=y CONFIG_FEATURE_TELNET_TTYPE=y CONFIG_FEATURE_TELNET_AUTOLOGIN=y CONFIG_TELNETD=y CONFIG_FEATURE_TELNETD_STANDALONE=y CONFIG_FEATURE_TELNETD_INETD_WAIT=y CONFIG_TFTP=y CONFIG_TFTPD=y # # Common options for tftp/tftpd # CONFIG_FEATURE_TFTP_GET=y CONFIG_FEATURE_TFTP_PUT=y CONFIG_FEATURE_TFTP_BLOCKSIZE=y CONFIG_FEATURE_TFTP_PROGRESS_BAR=y # CONFIG_TFTP_DEBUG is not set # CONFIG_TRACEROUTE is not set # CONFIG_TRACEROUTE6 is not set # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set # CONFIG_TUNCTL is not set # CONFIG_FEATURE_TUNCTL_UG is not set # CONFIG_UDHCPD is not set # CONFIG_DHCPRELAY is not set # CONFIG_DUMPLEASES is not set # CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set # CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set CONFIG_DHCPD_LEASES_FILE="" # CONFIG_UDHCPC is not set # CONFIG_FEATURE_UDHCPC_ARPING is not set # CONFIG_FEATURE_UDHCP_PORT is not set CONFIG_UDHCP_DEBUG=0 # CONFIG_FEATURE_UDHCP_RFC3397 is not set # CONFIG_FEATURE_UDHCP_8021Q is not set CONFIG_UDHCPC_DEFAULT_SCRIPT="" CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0 CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="" CONFIG_UDPSVD=y # CONFIG_VCONFIG is not set CONFIG_WGET=y CONFIG_FEATURE_WGET_STATUSBAR=y CONFIG_FEATURE_WGET_AUTHENTICATION=y CONFIG_FEATURE_WGET_LONG_OPTIONS=y CONFIG_FEATURE_WGET_TIMEOUT=y # CONFIG_ZCIP is not set # # Print Utilities # CONFIG_LPD=y CONFIG_LPR=y CONFIG_LPQ=y # # Mail Utilities # CONFIG_MAKEMIME=y CONFIG_FEATURE_MIME_CHARSET="us-ascii" CONFIG_POPMAILDIR=y CONFIG_FEATURE_POPMAILDIR_DELIVERY=y CONFIG_REFORMIME=y CONFIG_FEATURE_REFORMIME_COMPAT=y CONFIG_SENDMAIL=y # # Process Utilities # CONFIG_IOSTAT=y CONFIG_MPSTAT=y CONFIG_NMETER=y # CONFIG_PMAP is not set # CONFIG_POWERTOP is not set CONFIG_PSTREE=y CONFIG_PWDX=y CONFIG_SMEMCAP=y # CONFIG_FREE is not set CONFIG_FUSER=y CONFIG_KILL=y CONFIG_KILLALL=y CONFIG_KILLALL5=y CONFIG_PGREP=y CONFIG_PIDOF=y CONFIG_FEATURE_PIDOF_SINGLE=y CONFIG_FEATURE_PIDOF_OMIT=y CONFIG_PKILL=y CONFIG_PS=y CONFIG_FEATURE_PS_WIDE=y # CONFIG_FEATURE_PS_TIME is not set CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y # CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set CONFIG_RENICE=y CONFIG_BB_SYSCTL=y CONFIG_TOP=y CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y CONFIG_FEATURE_TOP_SMP_CPU=y CONFIG_FEATURE_TOP_DECIMALS=y CONFIG_FEATURE_TOP_SMP_PROCESS=y CONFIG_FEATURE_TOPMEM=y # CONFIG_FEATURE_SHOW_THREADS is not set # CONFIG_UPTIME is not set CONFIG_WATCH=y # # Runit Utilities # CONFIG_RUNSV=y CONFIG_RUNSVDIR=y # CONFIG_FEATURE_RUNSVDIR_LOG is not set CONFIG_SV=y CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service" CONFIG_SVLOGD=y CONFIG_CHPST=y CONFIG_SETUIDGID=y CONFIG_ENVUIDGID=y CONFIG_ENVDIR=y CONFIG_SOFTLIMIT=y # CONFIG_CHCON is not set # CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set # CONFIG_GETENFORCE is not set # CONFIG_GETSEBOOL is not set # CONFIG_LOAD_POLICY is not set # CONFIG_MATCHPATHCON is not set # CONFIG_RESTORECON is not set # CONFIG_RUNCON is not set # CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set # CONFIG_SELINUXENABLED is not set # CONFIG_SETENFORCE is not set # CONFIG_SETFILES is not set # CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set # CONFIG_SETSEBOOL is not set # CONFIG_SESTATUS is not set # # Shells # CONFIG_ASH=y CONFIG_ASH_BASH_COMPAT=y # CONFIG_ASH_IDLE_TIMEOUT is not set CONFIG_ASH_JOB_CONTROL=y CONFIG_ASH_ALIAS=y CONFIG_ASH_GETOPTS=y CONFIG_ASH_BUILTIN_ECHO=y CONFIG_ASH_BUILTIN_PRINTF=y CONFIG_ASH_BUILTIN_TEST=y CONFIG_ASH_CMDCMD=y # CONFIG_ASH_MAIL is not set CONFIG_ASH_OPTIMIZE_FOR_SIZE=y CONFIG_ASH_RANDOM_SUPPORT=y CONFIG_ASH_EXPAND_PRMT=y # CONFIG_CTTYHACK is not set CONFIG_HUSH=y CONFIG_HUSH_BASH_COMPAT=y CONFIG_HUSH_BRACE_EXPANSION=y CONFIG_HUSH_HELP=y CONFIG_HUSH_INTERACTIVE=y CONFIG_HUSH_SAVEHISTORY=y CONFIG_HUSH_JOB=y CONFIG_HUSH_TICK=y CONFIG_HUSH_IF=y CONFIG_HUSH_LOOPS=y CONFIG_HUSH_CASE=y CONFIG_HUSH_FUNCTIONS=y CONFIG_HUSH_LOCAL=y CONFIG_HUSH_RANDOM_SUPPORT=y CONFIG_HUSH_EXPORT_N=y CONFIG_HUSH_MODE_X=y # CONFIG_MSH is not set CONFIG_FEATURE_SH_IS_ASH=y # CONFIG_FEATURE_SH_IS_HUSH is not set # CONFIG_FEATURE_SH_IS_NONE is not set # CONFIG_FEATURE_BASH_IS_ASH is not set # CONFIG_FEATURE_BASH_IS_HUSH is not set CONFIG_FEATURE_BASH_IS_NONE=y CONFIG_SH_MATH_SUPPORT=y CONFIG_SH_MATH_SUPPORT_64=y CONFIG_FEATURE_SH_EXTRA_QUIET=y # CONFIG_FEATURE_SH_STANDALONE is not set # CONFIG_FEATURE_SH_NOFORK is not set CONFIG_FEATURE_SH_HISTFILESIZE=y # # System Logging Utilities # CONFIG_SYSLOGD=y CONFIG_FEATURE_ROTATE_LOGFILE=y CONFIG_FEATURE_REMOTE_LOG=y CONFIG_FEATURE_SYSLOGD_DUP=y CONFIG_FEATURE_SYSLOGD_CFG=y CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256 CONFIG_FEATURE_IPC_SYSLOG=y CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16 CONFIG_LOGREAD=y CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y # CONFIG_KLOGD is not set # CONFIG_FEATURE_KLOGD_KLOGCTL is not set CONFIG_LOGGER=y busybox-1.22.1/configs/TEST_rh9_defconfig0000644000000000000000000005361412263563520016723 0ustar rootroot# # Automatically generated make config: don't edit # Busybox version: 1.17.0.git # Fri Apr 16 22:25:22 2010 # CONFIG_HAVE_DOT_CONFIG=y # # Busybox Settings # # # General Configuration # # CONFIG_DESKTOP is not set # CONFIG_EXTRA_COMPAT is not set CONFIG_INCLUDE_SUSv2=y # CONFIG_USE_PORTABLE_CODE is not set CONFIG_FEATURE_BUFFERS_USE_MALLOC=y # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set CONFIG_SHOW_USAGE=y CONFIG_FEATURE_VERBOSE_USAGE=y CONFIG_FEATURE_COMPRESS_USAGE=y CONFIG_FEATURE_INSTALLER=y CONFIG_LOCALE_SUPPORT=y CONFIG_UNICODE_SUPPORT=y # CONFIG_UNICODE_USING_LOCALE is not set # CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set CONFIG_SUBST_WCHAR=63 CONFIG_LAST_SUPPORTED_WCHAR=767 # CONFIG_UNICODE_COMBINING_WCHARS is not set # CONFIG_UNICODE_WIDE_WCHARS is not set # CONFIG_UNICODE_BIDI_SUPPORT is not set # CONFIG_UNICODE_NEUTRAL_TABLE is not set CONFIG_LONG_OPTS=y CONFIG_FEATURE_DEVPTS=y # CONFIG_FEATURE_CLEAN_UP is not set CONFIG_FEATURE_UTMP=y CONFIG_FEATURE_WTMP=y CONFIG_FEATURE_PIDFILE=y CONFIG_FEATURE_SUID=y CONFIG_FEATURE_SUID_CONFIG=y CONFIG_FEATURE_SUID_CONFIG_QUIET=y # CONFIG_SELINUX is not set # CONFIG_FEATURE_PREFER_APPLETS is not set CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" CONFIG_FEATURE_SYSLOG=y CONFIG_FEATURE_HAVE_RPC=y # # Build Options # # CONFIG_STATIC is not set # CONFIG_PIE is not set # CONFIG_NOMMU is not set # CONFIG_BUILD_LIBBUSYBOX is not set # CONFIG_FEATURE_INDIVIDUAL is not set # CONFIG_FEATURE_SHARED_BUSYBOX is not set CONFIG_LFS=y CONFIG_CROSS_COMPILER_PREFIX="" CONFIG_EXTRA_CFLAGS="" # # Debugging Options # # CONFIG_DEBUG is not set # CONFIG_DEBUG_PESSIMIZE is not set # CONFIG_WERROR is not set CONFIG_NO_DEBUG_LIB=y # CONFIG_DMALLOC is not set # CONFIG_EFENCE is not set # # Installation Options # # CONFIG_INSTALL_NO_USR is not set CONFIG_INSTALL_APPLET_SYMLINKS=y # CONFIG_INSTALL_APPLET_HARDLINKS is not set # CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set # CONFIG_INSTALL_APPLET_DONT is not set # CONFIG_INSTALL_SH_APPLET_SYMLINK is not set # CONFIG_INSTALL_SH_APPLET_HARDLINK is not set # CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set CONFIG_PREFIX="./_install" # # Busybox Library Tuning # CONFIG_PASSWORD_MINLEN=6 CONFIG_MD5_SMALL=1 CONFIG_FEATURE_FAST_TOP=y # CONFIG_FEATURE_ETC_NETWORKS is not set CONFIG_FEATURE_EDITING=y CONFIG_FEATURE_EDITING_MAX_LEN=1024 # CONFIG_FEATURE_EDITING_VI is not set CONFIG_FEATURE_EDITING_HISTORY=15 CONFIG_FEATURE_EDITING_SAVEHISTORY=y CONFIG_FEATURE_TAB_COMPLETION=y # CONFIG_FEATURE_USERNAME_COMPLETION is not set # CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set # CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set CONFIG_FEATURE_NON_POSIX_CP=y # CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set CONFIG_FEATURE_COPYBUF_KB=4 # CONFIG_MONOTONIC_SYSCALL is not set CONFIG_IOCTL_HEX2STR_ERROR=y # CONFIG_FEATURE_HWIB is not set # # Applets # # # Archival Utilities # CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y CONFIG_FEATURE_SEAMLESS_Z=y CONFIG_AR=y CONFIG_FEATURE_AR_LONG_FILENAMES=y CONFIG_FEATURE_AR_CREATE=y CONFIG_BUNZIP2=y CONFIG_BZIP2=y CONFIG_CPIO=y CONFIG_FEATURE_CPIO_O=y CONFIG_FEATURE_CPIO_P=y # CONFIG_DPKG is not set # CONFIG_DPKG_DEB is not set # CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set CONFIG_GUNZIP=y CONFIG_GZIP=y CONFIG_FEATURE_GZIP_LONG_OPTIONS=y CONFIG_LZOP=y # CONFIG_LZOP_COMPR_HIGH is not set CONFIG_RPM2CPIO=y CONFIG_RPM=y CONFIG_TAR=y CONFIG_FEATURE_TAR_CREATE=y CONFIG_FEATURE_TAR_AUTODETECT=y CONFIG_FEATURE_TAR_FROM=y CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y CONFIG_FEATURE_TAR_LONG_OPTIONS=y CONFIG_FEATURE_TAR_UNAME_GNAME=y CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y # CONFIG_FEATURE_TAR_SELINUX is not set CONFIG_UNCOMPRESS=y CONFIG_UNLZMA=y CONFIG_FEATURE_LZMA_FAST=y CONFIG_UNZIP=y # # Coreutils # CONFIG_BASENAME=y CONFIG_CAL=y CONFIG_CAT=y CONFIG_CATV=y CONFIG_CHGRP=y CONFIG_CHMOD=y CONFIG_CHOWN=y CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y CONFIG_CHROOT=y CONFIG_CKSUM=y CONFIG_COMM=y CONFIG_CP=y CONFIG_FEATURE_CP_LONG_OPTIONS=y CONFIG_CUT=y CONFIG_DATE=y CONFIG_FEATURE_DATE_ISOFMT=y CONFIG_FEATURE_DATE_COMPAT=y CONFIG_DD=y CONFIG_FEATURE_DD_SIGNAL_HANDLING=y CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y CONFIG_FEATURE_DD_IBS_OBS=y CONFIG_DF=y CONFIG_FEATURE_DF_FANCY=y CONFIG_DIRNAME=y CONFIG_DOS2UNIX=y CONFIG_UNIX2DOS=y CONFIG_DU=y CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y CONFIG_ECHO=y CONFIG_FEATURE_FANCY_ECHO=y CONFIG_ENV=y CONFIG_FEATURE_ENV_LONG_OPTIONS=y CONFIG_EXPAND=y CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y CONFIG_EXPR=y CONFIG_EXPR_MATH_SUPPORT_64=y CONFIG_FALSE=y CONFIG_FOLD=y CONFIG_FSYNC=y CONFIG_HEAD=y CONFIG_FEATURE_FANCY_HEAD=y CONFIG_HOSTID=y CONFIG_ID=y CONFIG_INSTALL=y CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y CONFIG_LN=y CONFIG_LOGNAME=y CONFIG_LS=y CONFIG_FEATURE_LS_FILETYPES=y CONFIG_FEATURE_LS_FOLLOWLINKS=y CONFIG_FEATURE_LS_RECURSIVE=y CONFIG_FEATURE_LS_SORTFILES=y CONFIG_FEATURE_LS_TIMESTAMPS=y CONFIG_FEATURE_LS_USERNAME=y CONFIG_FEATURE_LS_COLOR=y CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y CONFIG_MD5SUM=y CONFIG_MKDIR=y CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y CONFIG_MKFIFO=y CONFIG_MKNOD=y CONFIG_MV=y CONFIG_FEATURE_MV_LONG_OPTIONS=y CONFIG_NICE=y CONFIG_NOHUP=y CONFIG_OD=y CONFIG_PRINTENV=y CONFIG_PRINTF=y CONFIG_PWD=y CONFIG_READLINK=y CONFIG_FEATURE_READLINK_FOLLOW=y CONFIG_REALPATH=y CONFIG_RM=y CONFIG_RMDIR=y CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y CONFIG_SEQ=y CONFIG_SHA1SUM=y CONFIG_SHA256SUM=y CONFIG_SHA512SUM=y CONFIG_SLEEP=y CONFIG_FEATURE_FANCY_SLEEP=y CONFIG_FEATURE_FLOAT_SLEEP=y CONFIG_SORT=y CONFIG_FEATURE_SORT_BIG=y CONFIG_SPLIT=y CONFIG_FEATURE_SPLIT_FANCY=y CONFIG_STAT=y CONFIG_FEATURE_STAT_FORMAT=y CONFIG_STTY=y CONFIG_SUM=y CONFIG_SYNC=y CONFIG_TAC=y CONFIG_TAIL=y CONFIG_FEATURE_FANCY_TAIL=y CONFIG_TEE=y CONFIG_FEATURE_TEE_USE_BLOCK_IO=y CONFIG_TEST=y CONFIG_FEATURE_TEST_64=y CONFIG_TOUCH=y CONFIG_TR=y CONFIG_FEATURE_TR_CLASSES=y CONFIG_FEATURE_TR_EQUIV=y CONFIG_TRUE=y CONFIG_TTY=y CONFIG_UNAME=y CONFIG_UNEXPAND=y CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y CONFIG_UNIQ=y CONFIG_USLEEP=y CONFIG_UUDECODE=y CONFIG_UUENCODE=y CONFIG_WC=y CONFIG_FEATURE_WC_LARGE=y CONFIG_WHO=y CONFIG_WHOAMI=y CONFIG_YES=y # # Common options for cp and mv # CONFIG_FEATURE_PRESERVE_HARDLINKS=y # # Common options for ls, more and telnet # CONFIG_FEATURE_AUTOWIDTH=y # # Common options for df, du, ls # CONFIG_FEATURE_HUMAN_READABLE=y # # Common options for md5sum, sha1sum, sha256sum, sha512sum # CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y # # Console Utilities # CONFIG_CHVT=y CONFIG_CLEAR=y CONFIG_DEALLOCVT=y CONFIG_DUMPKMAP=y CONFIG_KBD_MODE=y CONFIG_LOADFONT=y CONFIG_LOADKMAP=y CONFIG_OPENVT=y CONFIG_RESET=y CONFIG_RESIZE=y CONFIG_FEATURE_RESIZE_PRINT=y CONFIG_SETCONSOLE=y CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS=y CONFIG_SETFONT=y CONFIG_FEATURE_SETFONT_TEXTUAL_MAP=y CONFIG_DEFAULT_SETFONT_DIR="" CONFIG_SETKEYCODES=y CONFIG_SETLOGCONS=y CONFIG_SHOWKEY=y # # Common options for loadfont and setfont # # CONFIG_FEATURE_LOADFONT_PSF2 is not set # CONFIG_FEATURE_LOADFONT_RAW is not set # # Debian Utilities # CONFIG_MKTEMP=y CONFIG_PIPE_PROGRESS=y CONFIG_RUN_PARTS=y CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y CONFIG_FEATURE_RUN_PARTS_FANCY=y CONFIG_START_STOP_DAEMON=y CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS=y CONFIG_WHICH=y # # Editors # CONFIG_AWK=y CONFIG_FEATURE_AWK_LIBM=y CONFIG_CMP=y CONFIG_DIFF=y CONFIG_FEATURE_DIFF_LONG_OPTIONS=y CONFIG_FEATURE_DIFF_DIR=y CONFIG_ED=y CONFIG_PATCH=y CONFIG_SED=y CONFIG_VI=y CONFIG_FEATURE_VI_MAX_LEN=4096 # CONFIG_FEATURE_VI_8BIT is not set CONFIG_FEATURE_VI_COLON=y CONFIG_FEATURE_VI_YANKMARK=y CONFIG_FEATURE_VI_SEARCH=y CONFIG_FEATURE_VI_USE_SIGNALS=y CONFIG_FEATURE_VI_DOT_CMD=y CONFIG_FEATURE_VI_READONLY=y CONFIG_FEATURE_VI_SETOPTS=y CONFIG_FEATURE_VI_SET=y CONFIG_FEATURE_VI_WIN_RESIZE=y CONFIG_FEATURE_ALLOW_EXEC=y # # Finding Utilities # CONFIG_FIND=y CONFIG_FEATURE_FIND_PRINT0=y CONFIG_FEATURE_FIND_MTIME=y CONFIG_FEATURE_FIND_MMIN=y CONFIG_FEATURE_FIND_PERM=y CONFIG_FEATURE_FIND_TYPE=y CONFIG_FEATURE_FIND_XDEV=y CONFIG_FEATURE_FIND_MAXDEPTH=y CONFIG_FEATURE_FIND_NEWER=y CONFIG_FEATURE_FIND_INUM=y CONFIG_FEATURE_FIND_EXEC=y CONFIG_FEATURE_FIND_USER=y CONFIG_FEATURE_FIND_GROUP=y CONFIG_FEATURE_FIND_NOT=y CONFIG_FEATURE_FIND_DEPTH=y CONFIG_FEATURE_FIND_PAREN=y CONFIG_FEATURE_FIND_SIZE=y CONFIG_FEATURE_FIND_PRUNE=y CONFIG_FEATURE_FIND_DELETE=y CONFIG_FEATURE_FIND_PATH=y CONFIG_FEATURE_FIND_REGEX=y # CONFIG_FEATURE_FIND_CONTEXT is not set CONFIG_FEATURE_FIND_LINKS=y CONFIG_GREP=y CONFIG_FEATURE_GREP_EGREP_ALIAS=y CONFIG_FEATURE_GREP_FGREP_ALIAS=y CONFIG_FEATURE_GREP_CONTEXT=y CONFIG_XARGS=y CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y # # Init Utilities # CONFIG_INIT=y CONFIG_FEATURE_USE_INITTAB=y # CONFIG_FEATURE_KILL_REMOVED is not set CONFIG_FEATURE_KILL_DELAY=0 CONFIG_FEATURE_INIT_SCTTY=y CONFIG_FEATURE_INIT_SYSLOG=y CONFIG_FEATURE_EXTRA_QUIET=y CONFIG_FEATURE_INIT_COREDUMPS=y CONFIG_FEATURE_INITRD=y CONFIG_HALT=y # CONFIG_FEATURE_CALL_TELINIT is not set CONFIG_TELINIT_PATH="" CONFIG_MESG=y # # Login/Password Management Utilities # CONFIG_FEATURE_SHADOWPASSWDS=y CONFIG_USE_BB_PWD_GRP=y CONFIG_USE_BB_SHADOW=y CONFIG_USE_BB_CRYPT=y CONFIG_USE_BB_CRYPT_SHA=y CONFIG_ADDGROUP=y CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y CONFIG_FEATURE_ADDUSER_TO_GROUP=y CONFIG_DELGROUP=y CONFIG_FEATURE_DEL_USER_FROM_GROUP=y # CONFIG_FEATURE_CHECK_NAMES is not set CONFIG_ADDUSER=y CONFIG_FEATURE_ADDUSER_LONG_OPTIONS=y CONFIG_FIRST_SYSTEM_ID=100 CONFIG_LAST_SYSTEM_ID=999 CONFIG_DELUSER=y CONFIG_GETTY=y CONFIG_LOGIN=y # CONFIG_PAM is not set CONFIG_LOGIN_SCRIPTS=y CONFIG_FEATURE_NOLOGIN=y CONFIG_FEATURE_SECURETTY=y CONFIG_PASSWD=y CONFIG_FEATURE_PASSWD_WEAK_CHECK=y CONFIG_CRYPTPW=y CONFIG_CHPASSWD=y CONFIG_SU=y CONFIG_FEATURE_SU_SYSLOG=y CONFIG_FEATURE_SU_CHECKS_SHELLS=y CONFIG_SULOGIN=y CONFIG_VLOCK=y # # Linux Ext2 FS Progs # CONFIG_CHATTR=y CONFIG_FSCK=y CONFIG_LSATTR=y # # Linux Module Utilities # CONFIG_MODPROBE_SMALL=y CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE=y CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y # CONFIG_INSMOD is not set # CONFIG_RMMOD is not set # CONFIG_LSMOD is not set # CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set # CONFIG_MODPROBE is not set # CONFIG_FEATURE_MODPROBE_BLACKLIST is not set # CONFIG_DEPMOD is not set # # Options common to multiple modutils # # CONFIG_FEATURE_2_4_MODULES is not set # CONFIG_FEATURE_INSMOD_TRY_MMAP is not set # CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set # CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set # CONFIG_FEATURE_INSMOD_LOADINKMEM is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set # CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set # CONFIG_FEATURE_MODUTILS_ALIAS is not set # CONFIG_FEATURE_MODUTILS_SYMBOLS is not set CONFIG_DEFAULT_MODULES_DIR="/lib/modules" CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" # # Linux System Utilities # # CONFIG_ACPID is not set # CONFIG_FEATURE_ACPID_COMPAT is not set CONFIG_BLKID=y CONFIG_DMESG=y CONFIG_FEATURE_DMESG_PRETTY=y CONFIG_FBSET=y CONFIG_FEATURE_FBSET_FANCY=y CONFIG_FEATURE_FBSET_READMODE=y CONFIG_FDFLUSH=y CONFIG_FDFORMAT=y CONFIG_FDISK=y CONFIG_FDISK_SUPPORT_LARGE_DISKS=y CONFIG_FEATURE_FDISK_WRITABLE=y # CONFIG_FEATURE_AIX_LABEL is not set # CONFIG_FEATURE_SGI_LABEL is not set # CONFIG_FEATURE_SUN_LABEL is not set # CONFIG_FEATURE_OSF_LABEL is not set CONFIG_FEATURE_FDISK_ADVANCED=y CONFIG_FINDFS=y # CONFIG_FLOCK is not set CONFIG_FREERAMDISK=y CONFIG_FSCK_MINIX=y # CONFIG_MKFS_EXT2 is not set CONFIG_MKFS_MINIX=y # # Minix filesystem support # CONFIG_FEATURE_MINIX2=y # CONFIG_MKFS_REISER is not set CONFIG_MKFS_VFAT=y CONFIG_GETOPT=y CONFIG_FEATURE_GETOPT_LONG=y CONFIG_HEXDUMP=y CONFIG_FEATURE_HEXDUMP_REVERSE=y CONFIG_HD=y CONFIG_HWCLOCK=y CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS=y CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS=y CONFIG_IPCRM=y CONFIG_IPCS=y CONFIG_LOSETUP=y CONFIG_LSPCI=y CONFIG_LSUSB=y CONFIG_MDEV=y CONFIG_FEATURE_MDEV_CONF=y CONFIG_FEATURE_MDEV_RENAME=y CONFIG_FEATURE_MDEV_RENAME_REGEXP=y CONFIG_FEATURE_MDEV_EXEC=y CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y CONFIG_MKSWAP=y CONFIG_FEATURE_MKSWAP_UUID=y CONFIG_MORE=y CONFIG_FEATURE_USE_TERMIOS=y CONFIG_VOLUMEID=y CONFIG_FEATURE_VOLUMEID_EXT=y CONFIG_FEATURE_VOLUMEID_BTRFS=y CONFIG_FEATURE_VOLUMEID_REISERFS=y CONFIG_FEATURE_VOLUMEID_FAT=y CONFIG_FEATURE_VOLUMEID_HFS=y CONFIG_FEATURE_VOLUMEID_JFS=y CONFIG_FEATURE_VOLUMEID_XFS=y CONFIG_FEATURE_VOLUMEID_NTFS=y CONFIG_FEATURE_VOLUMEID_ISO9660=y CONFIG_FEATURE_VOLUMEID_UDF=y CONFIG_FEATURE_VOLUMEID_LUKS=y CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y CONFIG_FEATURE_VOLUMEID_CRAMFS=y CONFIG_FEATURE_VOLUMEID_ROMFS=y CONFIG_FEATURE_VOLUMEID_SYSV=y CONFIG_FEATURE_VOLUMEID_OCFS2=y CONFIG_FEATURE_VOLUMEID_LINUXRAID=y CONFIG_MOUNT=y CONFIG_FEATURE_MOUNT_FAKE=y CONFIG_FEATURE_MOUNT_VERBOSE=y # CONFIG_FEATURE_MOUNT_HELPERS is not set CONFIG_FEATURE_MOUNT_LABEL=y CONFIG_FEATURE_MOUNT_NFS=y CONFIG_FEATURE_MOUNT_CIFS=y CONFIG_FEATURE_MOUNT_FLAGS=y CONFIG_FEATURE_MOUNT_FSTAB=y CONFIG_PIVOT_ROOT=y CONFIG_RDATE=y CONFIG_RDEV=y CONFIG_READPROFILE=y CONFIG_RTCWAKE=y CONFIG_SCRIPT=y CONFIG_SCRIPTREPLAY=y CONFIG_SETARCH=y CONFIG_SWAPONOFF=y CONFIG_FEATURE_SWAPON_PRI=y CONFIG_SWITCH_ROOT=y CONFIG_UMOUNT=y CONFIG_FEATURE_UMOUNT_ALL=y # # Common options for mount/umount # CONFIG_FEATURE_MOUNT_LOOP=y # CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set # CONFIG_FEATURE_MTAB_SUPPORT is not set # # Miscellaneous Utilities # CONFIG_ADJTIMEX=y # CONFIG_BBCONFIG is not set CONFIG_BEEP=y CONFIG_FEATURE_BEEP_FREQ=4000 CONFIG_FEATURE_BEEP_LENGTH_MS=30 CONFIG_CHAT=y CONFIG_FEATURE_CHAT_NOFAIL=y # CONFIG_FEATURE_CHAT_TTY_HIFI is not set CONFIG_FEATURE_CHAT_IMPLICIT_CR=y CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y CONFIG_FEATURE_CHAT_SEND_ESCAPES=y CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y CONFIG_FEATURE_CHAT_CLR_ABORT=y CONFIG_CHRT=y CONFIG_CROND=y CONFIG_FEATURE_CROND_D=y CONFIG_FEATURE_CROND_CALL_SENDMAIL=y CONFIG_FEATURE_CROND_DIR="/var/spool/cron" CONFIG_CRONTAB=y CONFIG_DC=y CONFIG_FEATURE_DC_LIBM=y # CONFIG_DEVFSD is not set # CONFIG_DEVFSD_MODLOAD is not set # CONFIG_DEVFSD_FG_NP is not set # CONFIG_DEVFSD_VERBOSE is not set # CONFIG_FEATURE_DEVFS is not set CONFIG_DEVMEM=y CONFIG_EJECT=y CONFIG_FEATURE_EJECT_SCSI=y CONFIG_FBSPLASH=y # CONFIG_FLASHCP is not set # CONFIG_FLASH_LOCK is not set # CONFIG_FLASH_UNLOCK is not set # CONFIG_FLASH_ERASEALL is not set # CONFIG_IONICE is not set # CONFIG_INOTIFYD is not set CONFIG_LAST=y # CONFIG_FEATURE_LAST_SMALL is not set CONFIG_FEATURE_LAST_FANCY=y CONFIG_LESS=y CONFIG_FEATURE_LESS_MAXLINES=9999999 CONFIG_FEATURE_LESS_BRACKETS=y CONFIG_FEATURE_LESS_FLAGS=y CONFIG_FEATURE_LESS_MARKS=y CONFIG_FEATURE_LESS_REGEXP=y CONFIG_FEATURE_LESS_WINCH=y CONFIG_FEATURE_LESS_DASHCMD=y CONFIG_FEATURE_LESS_LINENUMS=y CONFIG_HDPARM=y CONFIG_FEATURE_HDPARM_GET_IDENTITY=y CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y CONFIG_MAKEDEVS=y # CONFIG_FEATURE_MAKEDEVS_LEAF is not set CONFIG_FEATURE_MAKEDEVS_TABLE=y CONFIG_MAN=y CONFIG_MICROCOM=y CONFIG_MOUNTPOINT=y CONFIG_MT=y CONFIG_RAIDAUTORUN=y # CONFIG_READAHEAD is not set CONFIG_RUNLEVEL=y CONFIG_RX=y CONFIG_SETSID=y CONFIG_STRINGS=y # CONFIG_TASKSET is not set # CONFIG_FEATURE_TASKSET_FANCY is not set CONFIG_TIME=y CONFIG_TIMEOUT=y CONFIG_TTYSIZE=y CONFIG_VOLNAME=y CONFIG_WALL=y # CONFIG_WATCHDOG is not set # # Networking Utilities # CONFIG_FEATURE_IPV6=y # CONFIG_FEATURE_UNIX_LOCAL is not set CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y # CONFIG_VERBOSE_RESOLUTION_ERRORS is not set CONFIG_ARP=y CONFIG_ARPING=y CONFIG_BRCTL=y CONFIG_FEATURE_BRCTL_FANCY=y CONFIG_FEATURE_BRCTL_SHOW=y CONFIG_DNSD=y CONFIG_ETHER_WAKE=y CONFIG_FAKEIDENTD=y CONFIG_FTPD=y CONFIG_FEATURE_FTP_WRITE=y CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y CONFIG_FTPGET=y CONFIG_FTPPUT=y CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y CONFIG_HOSTNAME=y CONFIG_HTTPD=y CONFIG_FEATURE_HTTPD_RANGES=y CONFIG_FEATURE_HTTPD_USE_SENDFILE=y CONFIG_FEATURE_HTTPD_SETUID=y CONFIG_FEATURE_HTTPD_BASIC_AUTH=y CONFIG_FEATURE_HTTPD_AUTH_MD5=y CONFIG_FEATURE_HTTPD_CGI=y CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y CONFIG_FEATURE_HTTPD_ERROR_PAGES=y CONFIG_FEATURE_HTTPD_PROXY=y CONFIG_IFCONFIG=y CONFIG_FEATURE_IFCONFIG_STATUS=y CONFIG_FEATURE_IFCONFIG_SLIP=y CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y CONFIG_FEATURE_IFCONFIG_HW=y CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y # CONFIG_IFENSLAVE is not set CONFIG_IFPLUGD=y CONFIG_IFUPDOWN=y CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate" CONFIG_FEATURE_IFUPDOWN_IP=y CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y # CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set CONFIG_FEATURE_IFUPDOWN_IPV4=y CONFIG_FEATURE_IFUPDOWN_IPV6=y CONFIG_FEATURE_IFUPDOWN_MAPPING=y # CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set CONFIG_INETD=y CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO=y CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD=y CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME=y CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME=y CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN=y CONFIG_FEATURE_INETD_RPC=y CONFIG_IP=y CONFIG_FEATURE_IP_ADDRESS=y CONFIG_FEATURE_IP_LINK=y CONFIG_FEATURE_IP_ROUTE=y CONFIG_FEATURE_IP_TUNNEL=y CONFIG_FEATURE_IP_RULE=y CONFIG_FEATURE_IP_SHORT_FORMS=y # CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set CONFIG_IPADDR=y CONFIG_IPLINK=y CONFIG_IPROUTE=y CONFIG_IPTUNNEL=y CONFIG_IPRULE=y CONFIG_IPCALC=y CONFIG_FEATURE_IPCALC_FANCY=y CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y CONFIG_NAMEIF=y CONFIG_FEATURE_NAMEIF_EXTENDED=y CONFIG_NC=y CONFIG_NC_SERVER=y CONFIG_NC_EXTRA=y CONFIG_NETSTAT=y CONFIG_FEATURE_NETSTAT_WIDE=y CONFIG_FEATURE_NETSTAT_PRG=y CONFIG_NSLOOKUP=y CONFIG_NTPD=y CONFIG_FEATURE_NTPD_SERVER=y CONFIG_PING=y CONFIG_PING6=y CONFIG_FEATURE_FANCY_PING=y CONFIG_PSCAN=y CONFIG_ROUTE=y CONFIG_SLATTACH=y CONFIG_TCPSVD=y CONFIG_TELNET=y CONFIG_FEATURE_TELNET_TTYPE=y CONFIG_FEATURE_TELNET_AUTOLOGIN=y CONFIG_TELNETD=y CONFIG_FEATURE_TELNETD_STANDALONE=y CONFIG_FEATURE_TELNETD_INETD_WAIT=y CONFIG_TFTP=y CONFIG_TFTPD=y CONFIG_FEATURE_TFTP_GET=y CONFIG_FEATURE_TFTP_PUT=y CONFIG_FEATURE_TFTP_BLOCKSIZE=y CONFIG_FEATURE_TFTP_PROGRESS_BAR=y # CONFIG_TFTP_DEBUG is not set CONFIG_TRACEROUTE=y CONFIG_TRACEROUTE6=y CONFIG_FEATURE_TRACEROUTE_VERBOSE=y # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set CONFIG_TUNCTL=y CONFIG_FEATURE_TUNCTL_UG=y CONFIG_UDHCPD=y CONFIG_DHCPRELAY=y CONFIG_DUMPLEASES=y CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY=y CONFIG_DHCPD_LEASES_FILE="/var/lib/misc/udhcpd.leases" CONFIG_UDHCPC=y CONFIG_FEATURE_UDHCPC_ARPING=y CONFIG_FEATURE_UDHCP_PORT=y CONFIG_UDHCP_DEBUG=9 CONFIG_FEATURE_UDHCP_RFC3397=y CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script" CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80 CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n" CONFIG_UDPSVD=y CONFIG_VCONFIG=y CONFIG_WGET=y CONFIG_FEATURE_WGET_STATUSBAR=y CONFIG_FEATURE_WGET_AUTHENTICATION=y CONFIG_FEATURE_WGET_LONG_OPTIONS=y CONFIG_FEATURE_WGET_TIMEOUT=y CONFIG_ZCIP=y # # Print Utilities # CONFIG_LPD=y CONFIG_LPR=y CONFIG_LPQ=y # # Mail Utilities # CONFIG_MAKEMIME=y CONFIG_FEATURE_MIME_CHARSET="us-ascii" CONFIG_POPMAILDIR=y CONFIG_FEATURE_POPMAILDIR_DELIVERY=y CONFIG_REFORMIME=y CONFIG_FEATURE_REFORMIME_COMPAT=y CONFIG_SENDMAIL=y # # Process Utilities # CONFIG_FREE=y CONFIG_FUSER=y CONFIG_KILL=y CONFIG_KILLALL=y CONFIG_KILLALL5=y CONFIG_NMETER=y CONFIG_PGREP=y CONFIG_PIDOF=y CONFIG_FEATURE_PIDOF_SINGLE=y CONFIG_FEATURE_PIDOF_OMIT=y CONFIG_PKILL=y CONFIG_PS=y CONFIG_FEATURE_PS_WIDE=y # CONFIG_FEATURE_PS_TIME is not set # CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set # CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set CONFIG_RENICE=y CONFIG_BB_SYSCTL=y CONFIG_TOP=y CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y CONFIG_FEATURE_TOP_SMP_CPU=y CONFIG_FEATURE_TOP_DECIMALS=y CONFIG_FEATURE_TOP_SMP_PROCESS=y CONFIG_FEATURE_TOPMEM=y CONFIG_FEATURE_SHOW_THREADS=y CONFIG_UPTIME=y CONFIG_WATCH=y # # Runit Utilities # CONFIG_RUNSV=y CONFIG_RUNSVDIR=y # CONFIG_FEATURE_RUNSVDIR_LOG is not set CONFIG_SV=y CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service" CONFIG_SVLOGD=y CONFIG_CHPST=y CONFIG_SETUIDGID=y CONFIG_ENVUIDGID=y CONFIG_ENVDIR=y CONFIG_SOFTLIMIT=y # CONFIG_CHCON is not set # CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set # CONFIG_GETENFORCE is not set # CONFIG_GETSEBOOL is not set # CONFIG_LOAD_POLICY is not set # CONFIG_MATCHPATHCON is not set # CONFIG_RESTORECON is not set # CONFIG_RUNCON is not set # CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set # CONFIG_SELINUXENABLED is not set # CONFIG_SETENFORCE is not set # CONFIG_SETFILES is not set # CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set # CONFIG_SETSEBOOL is not set # CONFIG_SESTATUS is not set # # Shells # CONFIG_FEATURE_SH_IS_ASH=y # CONFIG_FEATURE_SH_IS_HUSH is not set # CONFIG_FEATURE_SH_IS_NONE is not set CONFIG_ASH=y CONFIG_ASH_BASH_COMPAT=y CONFIG_ASH_JOB_CONTROL=y CONFIG_ASH_ALIAS=y CONFIG_ASH_GETOPTS=y CONFIG_ASH_BUILTIN_ECHO=y CONFIG_ASH_BUILTIN_PRINTF=y CONFIG_ASH_BUILTIN_TEST=y CONFIG_ASH_CMDCMD=y # CONFIG_ASH_MAIL is not set CONFIG_ASH_OPTIMIZE_FOR_SIZE=y CONFIG_ASH_RANDOM_SUPPORT=y CONFIG_ASH_EXPAND_PRMT=y CONFIG_HUSH=y CONFIG_HUSH_BASH_COMPAT=y CONFIG_HUSH_HELP=y CONFIG_HUSH_INTERACTIVE=y CONFIG_HUSH_JOB=y CONFIG_HUSH_TICK=y CONFIG_HUSH_IF=y CONFIG_HUSH_LOOPS=y CONFIG_HUSH_CASE=y CONFIG_HUSH_FUNCTIONS=y CONFIG_HUSH_LOCAL=y CONFIG_HUSH_EXPORT_N=y CONFIG_HUSH_RANDOM_SUPPORT=y CONFIG_MSH=y CONFIG_SH_MATH_SUPPORT=y CONFIG_SH_MATH_SUPPORT_64=y CONFIG_FEATURE_SH_EXTRA_QUIET=y # CONFIG_FEATURE_SH_STANDALONE is not set # CONFIG_FEATURE_SH_NOFORK is not set CONFIG_CTTYHACK=y # # System Logging Utilities # CONFIG_SYSLOGD=y CONFIG_FEATURE_ROTATE_LOGFILE=y CONFIG_FEATURE_REMOTE_LOG=y CONFIG_FEATURE_SYSLOGD_DUP=y CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256 CONFIG_FEATURE_IPC_SYSLOG=y CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16 CONFIG_LOGREAD=y CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y CONFIG_KLOGD=y CONFIG_LOGGER=y busybox-1.22.1/configs/android_ndk_defconfig0000644000000000000000000006471712263563520017644 0ustar rootroot# # Automatically generated make config: don't edit # Busybox version: 1.21.0.git # Mon May 28 21:51:18 2012 # CONFIG_HAVE_DOT_CONFIG=y # # Busybox Settings # # # General Configuration # CONFIG_DESKTOP=y # CONFIG_EXTRA_COMPAT is not set # CONFIG_INCLUDE_SUSv2 is not set # CONFIG_USE_PORTABLE_CODE is not set CONFIG_PLATFORM_LINUX=y CONFIG_FEATURE_BUFFERS_USE_MALLOC=y # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set CONFIG_SHOW_USAGE=y CONFIG_FEATURE_VERBOSE_USAGE=y CONFIG_FEATURE_COMPRESS_USAGE=y CONFIG_FEATURE_INSTALLER=y CONFIG_INSTALL_NO_USR=y # CONFIG_LOCALE_SUPPORT is not set # CONFIG_UNICODE_SUPPORT is not set # CONFIG_UNICODE_USING_LOCALE is not set # CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set CONFIG_SUBST_WCHAR=0 CONFIG_LAST_SUPPORTED_WCHAR=0 # CONFIG_UNICODE_COMBINING_WCHARS is not set # CONFIG_UNICODE_WIDE_WCHARS is not set # CONFIG_UNICODE_BIDI_SUPPORT is not set # CONFIG_UNICODE_NEUTRAL_TABLE is not set # CONFIG_UNICODE_PRESERVE_BROKEN is not set CONFIG_LONG_OPTS=y # CONFIG_FEATURE_DEVPTS is not set # CONFIG_FEATURE_CLEAN_UP is not set # CONFIG_FEATURE_UTMP is not set # CONFIG_FEATURE_WTMP is not set # CONFIG_FEATURE_PIDFILE is not set # CONFIG_FEATURE_SUID is not set # CONFIG_FEATURE_SUID_CONFIG is not set # CONFIG_FEATURE_SUID_CONFIG_QUIET is not set # CONFIG_SELINUX is not set # CONFIG_FEATURE_PREFER_APPLETS is not set CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" CONFIG_FEATURE_SYSLOG=y # CONFIG_FEATURE_HAVE_RPC is not set # # Build Options # # CONFIG_STATIC is not set # CONFIG_PIE is not set # CONFIG_NOMMU is not set # CONFIG_BUILD_LIBBUSYBOX is not set # CONFIG_FEATURE_INDIVIDUAL is not set # CONFIG_FEATURE_SHARED_BUSYBOX is not set # CONFIG_LFS is not set CONFIG_CROSS_COMPILER_PREFIX="arm-linux-androideabi-" CONFIG_SYSROOT="/opt/android-ndk/platforms/android-9/arch-arm" CONFIG_EXTRA_CFLAGS="-DANDROID -D__ANDROID__ -DSK_RELEASE -nostdlib -march=armv7-a -msoft-float -mfloat-abi=softfp -mfpu=neon -mthumb -mthumb-interwork -fpic -fno-short-enums -fgcse-after-reload -frename-registers" CONFIG_EXTRA_LDFLAGS="-Xlinker -z -Xlinker muldefs -nostdlib -Bdynamic -Xlinker -dynamic-linker -Xlinker /system/bin/linker -Xlinker -z -Xlinker nocopyreloc -Xlinker --no-undefined ${SYSROOT}/usr/lib/crtbegin_dynamic.o ${SYSROOT}/usr/lib/crtend_android.o" CONFIG_EXTRA_LDLIBS="dl m c gcc" # # Debugging Options # # CONFIG_DEBUG is not set # CONFIG_DEBUG_PESSIMIZE is not set # CONFIG_WERROR is not set CONFIG_NO_DEBUG_LIB=y # CONFIG_DMALLOC is not set # CONFIG_EFENCE is not set # # Installation Options ("make install" behavior) # CONFIG_INSTALL_APPLET_SYMLINKS=y # CONFIG_INSTALL_APPLET_HARDLINKS is not set # CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set # CONFIG_INSTALL_APPLET_DONT is not set # CONFIG_INSTALL_SH_APPLET_SYMLINK is not set # CONFIG_INSTALL_SH_APPLET_HARDLINK is not set # CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set CONFIG_PREFIX="./_install" # # Busybox Library Tuning # # CONFIG_FEATURE_SYSTEMD is not set # CONFIG_FEATURE_RTMINMAX is not set CONFIG_PASSWORD_MINLEN=6 CONFIG_MD5_SMALL=1 # CONFIG_FEATURE_FAST_TOP is not set # CONFIG_FEATURE_ETC_NETWORKS is not set CONFIG_FEATURE_USE_TERMIOS=y # CONFIG_FEATURE_EDITING is not set CONFIG_FEATURE_EDITING_MAX_LEN=0 # CONFIG_FEATURE_EDITING_VI is not set CONFIG_FEATURE_EDITING_HISTORY=0 # CONFIG_FEATURE_EDITING_SAVEHISTORY is not set # CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set # CONFIG_FEATURE_REVERSE_SEARCH is not set # CONFIG_FEATURE_TAB_COMPLETION is not set # CONFIG_FEATURE_USERNAME_COMPLETION is not set # CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set # CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set # CONFIG_FEATURE_NON_POSIX_CP is not set CONFIG_FEATURE_VERBOSE_CP_MESSAGE=y CONFIG_FEATURE_COPYBUF_KB=4 # CONFIG_FEATURE_SKIP_ROOTFS is not set # CONFIG_MONOTONIC_SYSCALL is not set # CONFIG_IOCTL_HEX2STR_ERROR is not set # CONFIG_FEATURE_HWIB is not set # # Applets # # # Archival Utilities # CONFIG_FEATURE_SEAMLESS_XZ=y CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y CONFIG_FEATURE_SEAMLESS_Z=y CONFIG_AR=y CONFIG_FEATURE_AR_LONG_FILENAMES=y CONFIG_FEATURE_AR_CREATE=y CONFIG_BUNZIP2=y CONFIG_BZIP2=y CONFIG_CPIO=y CONFIG_FEATURE_CPIO_O=y CONFIG_FEATURE_CPIO_P=y CONFIG_DPKG=y CONFIG_DPKG_DEB=y # CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set CONFIG_GUNZIP=y CONFIG_GZIP=y # CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set CONFIG_GZIP_FAST=0 CONFIG_LZOP=y CONFIG_LZOP_COMPR_HIGH=y CONFIG_RPM2CPIO=y CONFIG_RPM=y CONFIG_TAR=y CONFIG_FEATURE_TAR_CREATE=y CONFIG_FEATURE_TAR_AUTODETECT=y CONFIG_FEATURE_TAR_FROM=y CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y # CONFIG_FEATURE_TAR_LONG_OPTIONS is not set # CONFIG_FEATURE_TAR_TO_COMMAND is not set CONFIG_FEATURE_TAR_UNAME_GNAME=y CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y # CONFIG_FEATURE_TAR_SELINUX is not set CONFIG_UNCOMPRESS=y CONFIG_UNLZMA=y CONFIG_FEATURE_LZMA_FAST=y CONFIG_LZMA=y CONFIG_UNXZ=y CONFIG_XZ=y CONFIG_UNZIP=y # # Coreutils # CONFIG_BASENAME=y CONFIG_CAT=y # CONFIG_DATE is not set # CONFIG_FEATURE_DATE_ISOFMT is not set # CONFIG_FEATURE_DATE_NANO is not set # CONFIG_FEATURE_DATE_COMPAT is not set # CONFIG_HOSTID is not set # CONFIG_ID is not set # CONFIG_GROUPS is not set CONFIG_TEST=y CONFIG_FEATURE_TEST_64=y CONFIG_TOUCH=y CONFIG_FEATURE_TOUCH_SUSV3=y CONFIG_TR=y CONFIG_FEATURE_TR_CLASSES=y CONFIG_FEATURE_TR_EQUIV=y CONFIG_BASE64=y # CONFIG_WHO is not set # CONFIG_USERS is not set CONFIG_CAL=y CONFIG_CATV=y CONFIG_CHGRP=y CONFIG_CHMOD=y CONFIG_CHOWN=y # CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set CONFIG_CHROOT=y CONFIG_CKSUM=y CONFIG_COMM=y CONFIG_CP=y # CONFIG_FEATURE_CP_LONG_OPTIONS is not set CONFIG_CUT=y CONFIG_DD=y CONFIG_FEATURE_DD_SIGNAL_HANDLING=y CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y CONFIG_FEATURE_DD_IBS_OBS=y # CONFIG_DF is not set # CONFIG_FEATURE_DF_FANCY is not set CONFIG_DIRNAME=y CONFIG_DOS2UNIX=y CONFIG_UNIX2DOS=y CONFIG_DU=y CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y CONFIG_ECHO=y CONFIG_FEATURE_FANCY_ECHO=y CONFIG_ENV=y # CONFIG_FEATURE_ENV_LONG_OPTIONS is not set CONFIG_EXPAND=y # CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set CONFIG_EXPR=y CONFIG_EXPR_MATH_SUPPORT_64=y CONFIG_FALSE=y CONFIG_FOLD=y CONFIG_FSYNC=y CONFIG_HEAD=y CONFIG_FEATURE_FANCY_HEAD=y CONFIG_INSTALL=y # CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set CONFIG_LN=y # CONFIG_LOGNAME is not set CONFIG_LS=y CONFIG_FEATURE_LS_FILETYPES=y CONFIG_FEATURE_LS_FOLLOWLINKS=y CONFIG_FEATURE_LS_RECURSIVE=y CONFIG_FEATURE_LS_SORTFILES=y CONFIG_FEATURE_LS_TIMESTAMPS=y CONFIG_FEATURE_LS_USERNAME=y CONFIG_FEATURE_LS_COLOR=y # CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set CONFIG_MD5SUM=y CONFIG_MKDIR=y # CONFIG_FEATURE_MKDIR_LONG_OPTIONS is not set CONFIG_MKFIFO=y CONFIG_MKNOD=y CONFIG_MV=y # CONFIG_FEATURE_MV_LONG_OPTIONS is not set CONFIG_NICE=y CONFIG_NOHUP=y CONFIG_OD=y CONFIG_PRINTENV=y CONFIG_PRINTF=y CONFIG_PWD=y CONFIG_READLINK=y CONFIG_FEATURE_READLINK_FOLLOW=y CONFIG_REALPATH=y CONFIG_RM=y CONFIG_RMDIR=y # CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set CONFIG_SEQ=y CONFIG_SHA1SUM=y CONFIG_SHA256SUM=y CONFIG_SHA512SUM=y CONFIG_SLEEP=y CONFIG_FEATURE_FANCY_SLEEP=y CONFIG_FEATURE_FLOAT_SLEEP=y CONFIG_SORT=y CONFIG_FEATURE_SORT_BIG=y CONFIG_SPLIT=y CONFIG_FEATURE_SPLIT_FANCY=y # CONFIG_STAT is not set # CONFIG_FEATURE_STAT_FORMAT is not set CONFIG_STTY=y CONFIG_SUM=y CONFIG_SYNC=y CONFIG_TAC=y CONFIG_TAIL=y CONFIG_FEATURE_FANCY_TAIL=y CONFIG_TEE=y CONFIG_FEATURE_TEE_USE_BLOCK_IO=y CONFIG_TRUE=y # CONFIG_TTY is not set CONFIG_UNAME=y CONFIG_UNEXPAND=y # CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set CONFIG_UNIQ=y CONFIG_USLEEP=y CONFIG_UUDECODE=y CONFIG_UUENCODE=y CONFIG_WC=y CONFIG_FEATURE_WC_LARGE=y CONFIG_WHOAMI=y CONFIG_YES=y # # Common options for cp and mv # CONFIG_FEATURE_PRESERVE_HARDLINKS=y # # Common options for ls, more and telnet # CONFIG_FEATURE_AUTOWIDTH=y # # Common options for df, du, ls # CONFIG_FEATURE_HUMAN_READABLE=y # # Common options for md5sum, sha1sum, sha256sum, sha512sum # CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y # # Console Utilities # CONFIG_CHVT=y CONFIG_FGCONSOLE=y CONFIG_CLEAR=y CONFIG_DEALLOCVT=y CONFIG_DUMPKMAP=y # CONFIG_KBD_MODE is not set # CONFIG_LOADFONT is not set CONFIG_LOADKMAP=y CONFIG_OPENVT=y CONFIG_RESET=y CONFIG_RESIZE=y CONFIG_FEATURE_RESIZE_PRINT=y CONFIG_SETCONSOLE=y # CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set # CONFIG_SETFONT is not set # CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set CONFIG_DEFAULT_SETFONT_DIR="" CONFIG_SETKEYCODES=y CONFIG_SETLOGCONS=y CONFIG_SHOWKEY=y # CONFIG_FEATURE_LOADFONT_PSF2 is not set # CONFIG_FEATURE_LOADFONT_RAW is not set # # Debian Utilities # CONFIG_MKTEMP=y CONFIG_PIPE_PROGRESS=y CONFIG_RUN_PARTS=y # CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set CONFIG_FEATURE_RUN_PARTS_FANCY=y CONFIG_START_STOP_DAEMON=y CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y # CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set CONFIG_WHICH=y # # Editors # CONFIG_PATCH=y CONFIG_VI=y CONFIG_FEATURE_VI_MAX_LEN=4096 CONFIG_FEATURE_VI_8BIT=y CONFIG_FEATURE_VI_COLON=y CONFIG_FEATURE_VI_YANKMARK=y CONFIG_FEATURE_VI_SEARCH=y # CONFIG_FEATURE_VI_REGEX_SEARCH is not set CONFIG_FEATURE_VI_USE_SIGNALS=y CONFIG_FEATURE_VI_DOT_CMD=y CONFIG_FEATURE_VI_READONLY=y CONFIG_FEATURE_VI_SETOPTS=y CONFIG_FEATURE_VI_SET=y CONFIG_FEATURE_VI_WIN_RESIZE=y CONFIG_FEATURE_VI_ASK_TERMINAL=y CONFIG_AWK=y CONFIG_FEATURE_AWK_LIBM=y CONFIG_CMP=y CONFIG_DIFF=y # CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set CONFIG_FEATURE_DIFF_DIR=y CONFIG_ED=y CONFIG_SED=y CONFIG_FEATURE_ALLOW_EXEC=y # # Finding Utilities # CONFIG_FIND=y CONFIG_FEATURE_FIND_PRINT0=y CONFIG_FEATURE_FIND_MTIME=y CONFIG_FEATURE_FIND_MMIN=y CONFIG_FEATURE_FIND_PERM=y CONFIG_FEATURE_FIND_TYPE=y CONFIG_FEATURE_FIND_XDEV=y CONFIG_FEATURE_FIND_MAXDEPTH=y CONFIG_FEATURE_FIND_NEWER=y CONFIG_FEATURE_FIND_INUM=y CONFIG_FEATURE_FIND_EXEC=y CONFIG_FEATURE_FIND_USER=y CONFIG_FEATURE_FIND_GROUP=y CONFIG_FEATURE_FIND_NOT=y CONFIG_FEATURE_FIND_DEPTH=y CONFIG_FEATURE_FIND_PAREN=y CONFIG_FEATURE_FIND_SIZE=y CONFIG_FEATURE_FIND_PRUNE=y CONFIG_FEATURE_FIND_DELETE=y CONFIG_FEATURE_FIND_PATH=y CONFIG_FEATURE_FIND_REGEX=y # CONFIG_FEATURE_FIND_CONTEXT is not set CONFIG_FEATURE_FIND_LINKS=y CONFIG_GREP=y CONFIG_FEATURE_GREP_EGREP_ALIAS=y CONFIG_FEATURE_GREP_FGREP_ALIAS=y CONFIG_FEATURE_GREP_CONTEXT=y CONFIG_XARGS=y CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y # # Init Utilities # CONFIG_BOOTCHARTD=y CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER=y CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE=y CONFIG_HALT=y # CONFIG_FEATURE_CALL_TELINIT is not set CONFIG_TELINIT_PATH="" CONFIG_INIT=y CONFIG_FEATURE_USE_INITTAB=y # CONFIG_FEATURE_KILL_REMOVED is not set CONFIG_FEATURE_KILL_DELAY=0 CONFIG_FEATURE_INIT_SCTTY=y CONFIG_FEATURE_INIT_SYSLOG=y CONFIG_FEATURE_EXTRA_QUIET=y CONFIG_FEATURE_INIT_COREDUMPS=y CONFIG_FEATURE_INITRD=y CONFIG_INIT_TERMINAL_TYPE="linux" CONFIG_MESG=y CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y # # Login/Password Management Utilities # # CONFIG_ADD_SHELL is not set # CONFIG_REMOVE_SHELL is not set # CONFIG_FEATURE_SHADOWPASSWDS is not set # CONFIG_USE_BB_PWD_GRP is not set # CONFIG_USE_BB_SHADOW is not set # CONFIG_USE_BB_CRYPT is not set # CONFIG_USE_BB_CRYPT_SHA is not set # CONFIG_ADDUSER is not set # CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set # CONFIG_FEATURE_CHECK_NAMES is not set CONFIG_FIRST_SYSTEM_ID=0 CONFIG_LAST_SYSTEM_ID=0 # CONFIG_ADDGROUP is not set # CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set # CONFIG_FEATURE_ADDUSER_TO_GROUP is not set # CONFIG_DELUSER is not set # CONFIG_DELGROUP is not set # CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set # CONFIG_GETTY is not set # CONFIG_LOGIN is not set # CONFIG_LOGIN_SESSION_AS_CHILD is not set # CONFIG_PAM is not set # CONFIG_LOGIN_SCRIPTS is not set # CONFIG_FEATURE_NOLOGIN is not set # CONFIG_FEATURE_SECURETTY is not set # CONFIG_PASSWD is not set # CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set # CONFIG_CRYPTPW is not set # CONFIG_CHPASSWD is not set CONFIG_FEATURE_DEFAULT_PASSWD_ALGO="" # CONFIG_SU is not set # CONFIG_FEATURE_SU_SYSLOG is not set # CONFIG_FEATURE_SU_CHECKS_SHELLS is not set # CONFIG_SULOGIN is not set # CONFIG_VLOCK is not set # # Linux Ext2 FS Progs # CONFIG_CHATTR=y # CONFIG_FSCK is not set CONFIG_LSATTR=y CONFIG_TUNE2FS=y # # Linux Module Utilities # CONFIG_MODINFO=y CONFIG_MODPROBE_SMALL=y CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE=y CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y # CONFIG_INSMOD is not set # CONFIG_RMMOD is not set # CONFIG_LSMOD is not set # CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set # CONFIG_MODPROBE is not set # CONFIG_FEATURE_MODPROBE_BLACKLIST is not set # CONFIG_DEPMOD is not set # # Options common to multiple modutils # # CONFIG_FEATURE_2_4_MODULES is not set # CONFIG_FEATURE_INSMOD_TRY_MMAP is not set # CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set # CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set # CONFIG_FEATURE_INSMOD_LOADINKMEM is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set # CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set # CONFIG_FEATURE_MODUTILS_ALIAS is not set # CONFIG_FEATURE_MODUTILS_SYMBOLS is not set CONFIG_DEFAULT_MODULES_DIR="/system/lib/modules" CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" # # Linux System Utilities # CONFIG_BLOCKDEV=y CONFIG_MDEV=y CONFIG_FEATURE_MDEV_CONF=y CONFIG_FEATURE_MDEV_RENAME=y CONFIG_FEATURE_MDEV_RENAME_REGEXP=y CONFIG_FEATURE_MDEV_EXEC=y CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y CONFIG_REV=y # CONFIG_ACPID is not set # CONFIG_FEATURE_ACPID_COMPAT is not set CONFIG_BLKID=y CONFIG_FEATURE_BLKID_TYPE=y CONFIG_DMESG=y CONFIG_FEATURE_DMESG_PRETTY=y CONFIG_FBSET=y CONFIG_FEATURE_FBSET_FANCY=y CONFIG_FEATURE_FBSET_READMODE=y CONFIG_FDFLUSH=y CONFIG_FDFORMAT=y CONFIG_FDISK=y CONFIG_FDISK_SUPPORT_LARGE_DISKS=y CONFIG_FEATURE_FDISK_WRITABLE=y # CONFIG_FEATURE_AIX_LABEL is not set # CONFIG_FEATURE_SGI_LABEL is not set # CONFIG_FEATURE_SUN_LABEL is not set # CONFIG_FEATURE_OSF_LABEL is not set # CONFIG_FEATURE_GPT_LABEL is not set CONFIG_FEATURE_FDISK_ADVANCED=y CONFIG_FINDFS=y CONFIG_FLOCK=y CONFIG_FREERAMDISK=y # CONFIG_FSCK_MINIX is not set # CONFIG_MKFS_EXT2 is not set # CONFIG_MKFS_MINIX is not set # CONFIG_FEATURE_MINIX2 is not set # CONFIG_MKFS_REISER is not set # CONFIG_MKFS_VFAT is not set CONFIG_GETOPT=y CONFIG_FEATURE_GETOPT_LONG=y CONFIG_HEXDUMP=y CONFIG_FEATURE_HEXDUMP_REVERSE=y CONFIG_HD=y CONFIG_HWCLOCK=y # CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set # CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set # CONFIG_IPCRM is not set # CONFIG_IPCS is not set CONFIG_LOSETUP=y CONFIG_LSPCI=y CONFIG_LSUSB=y CONFIG_MKSWAP=y CONFIG_FEATURE_MKSWAP_UUID=y CONFIG_MORE=y # CONFIG_MOUNT is not set # CONFIG_FEATURE_MOUNT_FAKE is not set # CONFIG_FEATURE_MOUNT_VERBOSE is not set # CONFIG_FEATURE_MOUNT_HELPERS is not set # CONFIG_FEATURE_MOUNT_LABEL is not set # CONFIG_FEATURE_MOUNT_NFS is not set # CONFIG_FEATURE_MOUNT_CIFS is not set # CONFIG_FEATURE_MOUNT_FLAGS is not set # CONFIG_FEATURE_MOUNT_FSTAB is not set # CONFIG_PIVOT_ROOT is not set # CONFIG_RDATE is not set CONFIG_RDEV=y CONFIG_READPROFILE=y CONFIG_RTCWAKE=y CONFIG_SCRIPT=y CONFIG_SCRIPTREPLAY=y # CONFIG_SETARCH is not set # CONFIG_SWAPONOFF is not set # CONFIG_FEATURE_SWAPON_PRI is not set CONFIG_SWITCH_ROOT=y # CONFIG_UMOUNT is not set # CONFIG_FEATURE_UMOUNT_ALL is not set # CONFIG_FEATURE_MOUNT_LOOP is not set # CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set # CONFIG_FEATURE_MTAB_SUPPORT is not set CONFIG_VOLUMEID=y # # Filesystem/Volume identification # CONFIG_FEATURE_VOLUMEID_EXT=y CONFIG_FEATURE_VOLUMEID_BTRFS=y CONFIG_FEATURE_VOLUMEID_REISERFS=y CONFIG_FEATURE_VOLUMEID_FAT=y CONFIG_FEATURE_VOLUMEID_HFS=y CONFIG_FEATURE_VOLUMEID_JFS=y CONFIG_FEATURE_VOLUMEID_XFS=y CONFIG_FEATURE_VOLUMEID_NTFS=y CONFIG_FEATURE_VOLUMEID_ISO9660=y CONFIG_FEATURE_VOLUMEID_UDF=y CONFIG_FEATURE_VOLUMEID_LUKS=y CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y CONFIG_FEATURE_VOLUMEID_CRAMFS=y CONFIG_FEATURE_VOLUMEID_ROMFS=y CONFIG_FEATURE_VOLUMEID_SYSV=y CONFIG_FEATURE_VOLUMEID_OCFS2=y CONFIG_FEATURE_VOLUMEID_LINUXRAID=y # # Miscellaneous Utilities # # CONFIG_CONSPY is not set CONFIG_LESS=y CONFIG_FEATURE_LESS_MAXLINES=9999999 CONFIG_FEATURE_LESS_BRACKETS=y CONFIG_FEATURE_LESS_FLAGS=y CONFIG_FEATURE_LESS_MARKS=y CONFIG_FEATURE_LESS_REGEXP=y CONFIG_FEATURE_LESS_WINCH=y CONFIG_FEATURE_LESS_ASK_TERMINAL=y CONFIG_FEATURE_LESS_DASHCMD=y CONFIG_FEATURE_LESS_LINENUMS=y # CONFIG_NANDWRITE is not set CONFIG_NANDDUMP=y CONFIG_SETSERIAL=y # CONFIG_UBIATTACH is not set # CONFIG_UBIDETACH is not set # CONFIG_UBIMKVOL is not set # CONFIG_UBIRMVOL is not set # CONFIG_UBIRSVOL is not set # CONFIG_UBIUPDATEVOL is not set # CONFIG_ADJTIMEX is not set CONFIG_BBCONFIG=y CONFIG_FEATURE_COMPRESS_BBCONFIG=y CONFIG_BEEP=y CONFIG_FEATURE_BEEP_FREQ=4000 CONFIG_FEATURE_BEEP_LENGTH_MS=30 CONFIG_CHAT=y CONFIG_FEATURE_CHAT_NOFAIL=y # CONFIG_FEATURE_CHAT_TTY_HIFI is not set CONFIG_FEATURE_CHAT_IMPLICIT_CR=y CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y CONFIG_FEATURE_CHAT_SEND_ESCAPES=y CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y CONFIG_FEATURE_CHAT_CLR_ABORT=y CONFIG_CHRT=y CONFIG_CROND=y CONFIG_FEATURE_CROND_D=y CONFIG_FEATURE_CROND_CALL_SENDMAIL=y CONFIG_FEATURE_CROND_DIR="/var/spool/cron" CONFIG_CRONTAB=y CONFIG_DC=y CONFIG_FEATURE_DC_LIBM=y # CONFIG_DEVFSD is not set # CONFIG_DEVFSD_MODLOAD is not set # CONFIG_DEVFSD_FG_NP is not set # CONFIG_DEVFSD_VERBOSE is not set # CONFIG_FEATURE_DEVFS is not set CONFIG_DEVMEM=y # CONFIG_EJECT is not set # CONFIG_FEATURE_EJECT_SCSI is not set CONFIG_FBSPLASH=y CONFIG_FLASHCP=y CONFIG_FLASH_LOCK=y CONFIG_FLASH_UNLOCK=y # CONFIG_FLASH_ERASEALL is not set # CONFIG_IONICE is not set CONFIG_INOTIFYD=y # CONFIG_LAST is not set # CONFIG_FEATURE_LAST_SMALL is not set # CONFIG_FEATURE_LAST_FANCY is not set CONFIG_HDPARM=y CONFIG_FEATURE_HDPARM_GET_IDENTITY=y CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y CONFIG_MAKEDEVS=y # CONFIG_FEATURE_MAKEDEVS_LEAF is not set CONFIG_FEATURE_MAKEDEVS_TABLE=y CONFIG_MAN=y # CONFIG_MICROCOM is not set # CONFIG_MOUNTPOINT is not set # CONFIG_MT is not set CONFIG_RAIDAUTORUN=y # CONFIG_READAHEAD is not set # CONFIG_RFKILL is not set # CONFIG_RUNLEVEL is not set CONFIG_RX=y CONFIG_SETSID=y CONFIG_STRINGS=y # CONFIG_TASKSET is not set # CONFIG_FEATURE_TASKSET_FANCY is not set CONFIG_TIME=y CONFIG_TIMEOUT=y CONFIG_TTYSIZE=y CONFIG_VOLNAME=y # CONFIG_WALL is not set # CONFIG_WATCHDOG is not set # # Networking Utilities # # CONFIG_NAMEIF is not set # CONFIG_FEATURE_NAMEIF_EXTENDED is not set CONFIG_NBDCLIENT=y CONFIG_NC=y CONFIG_NC_SERVER=y CONFIG_NC_EXTRA=y # CONFIG_NC_110_COMPAT is not set CONFIG_PING=y # CONFIG_PING6 is not set CONFIG_FEATURE_FANCY_PING=y CONFIG_WHOIS=y # CONFIG_FEATURE_IPV6 is not set # CONFIG_FEATURE_UNIX_LOCAL is not set # CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set # CONFIG_VERBOSE_RESOLUTION_ERRORS is not set CONFIG_ARP=y # CONFIG_ARPING is not set # CONFIG_BRCTL is not set # CONFIG_FEATURE_BRCTL_FANCY is not set # CONFIG_FEATURE_BRCTL_SHOW is not set CONFIG_DNSD=y # CONFIG_ETHER_WAKE is not set CONFIG_FAKEIDENTD=y CONFIG_FTPD=y CONFIG_FEATURE_FTP_WRITE=y CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y CONFIG_FTPGET=y CONFIG_FTPPUT=y # CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set # CONFIG_HOSTNAME is not set CONFIG_HTTPD=y CONFIG_FEATURE_HTTPD_RANGES=y CONFIG_FEATURE_HTTPD_USE_SENDFILE=y CONFIG_FEATURE_HTTPD_SETUID=y CONFIG_FEATURE_HTTPD_BASIC_AUTH=y # CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set CONFIG_FEATURE_HTTPD_CGI=y CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y CONFIG_FEATURE_HTTPD_ERROR_PAGES=y CONFIG_FEATURE_HTTPD_PROXY=y CONFIG_FEATURE_HTTPD_GZIP=y CONFIG_IFCONFIG=y CONFIG_FEATURE_IFCONFIG_STATUS=y # CONFIG_FEATURE_IFCONFIG_SLIP is not set CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y CONFIG_FEATURE_IFCONFIG_HW=y CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y # CONFIG_IFENSLAVE is not set # CONFIG_IFPLUGD is not set CONFIG_IFUPDOWN=y CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate" CONFIG_FEATURE_IFUPDOWN_IP=y CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y # CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set CONFIG_FEATURE_IFUPDOWN_IPV4=y # CONFIG_FEATURE_IFUPDOWN_IPV6 is not set CONFIG_FEATURE_IFUPDOWN_MAPPING=y CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP=y # CONFIG_INETD is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set # CONFIG_FEATURE_INETD_RPC is not set CONFIG_IP=y CONFIG_FEATURE_IP_ADDRESS=y CONFIG_FEATURE_IP_LINK=y CONFIG_FEATURE_IP_ROUTE=y CONFIG_FEATURE_IP_TUNNEL=y CONFIG_FEATURE_IP_RULE=y CONFIG_FEATURE_IP_SHORT_FORMS=y # CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set CONFIG_IPADDR=y CONFIG_IPLINK=y CONFIG_IPROUTE=y CONFIG_IPTUNNEL=y CONFIG_IPRULE=y CONFIG_IPCALC=y CONFIG_FEATURE_IPCALC_FANCY=y # CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set CONFIG_NETSTAT=y CONFIG_FEATURE_NETSTAT_WIDE=y CONFIG_FEATURE_NETSTAT_PRG=y # CONFIG_NSLOOKUP is not set # CONFIG_NTPD is not set # CONFIG_FEATURE_NTPD_SERVER is not set CONFIG_PSCAN=y CONFIG_ROUTE=y # CONFIG_SLATTACH is not set CONFIG_TCPSVD=y CONFIG_TELNET=y CONFIG_FEATURE_TELNET_TTYPE=y CONFIG_FEATURE_TELNET_AUTOLOGIN=y CONFIG_TELNETD=y CONFIG_FEATURE_TELNETD_STANDALONE=y CONFIG_FEATURE_TELNETD_INETD_WAIT=y CONFIG_TFTP=y CONFIG_TFTPD=y # # Common options for tftp/tftpd # CONFIG_FEATURE_TFTP_GET=y CONFIG_FEATURE_TFTP_PUT=y CONFIG_FEATURE_TFTP_BLOCKSIZE=y CONFIG_FEATURE_TFTP_PROGRESS_BAR=y # CONFIG_TFTP_DEBUG is not set CONFIG_TRACEROUTE=y # CONFIG_TRACEROUTE6 is not set CONFIG_FEATURE_TRACEROUTE_VERBOSE=y # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set CONFIG_TUNCTL=y CONFIG_FEATURE_TUNCTL_UG=y # CONFIG_UDHCPC6 is not set # CONFIG_UDHCPD is not set # CONFIG_DHCPRELAY is not set # CONFIG_DUMPLEASES is not set # CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set # CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set CONFIG_DHCPD_LEASES_FILE="" CONFIG_UDHCPC=y CONFIG_FEATURE_UDHCPC_ARPING=y CONFIG_FEATURE_UDHCP_PORT=y CONFIG_UDHCP_DEBUG=9 CONFIG_FEATURE_UDHCP_RFC3397=y CONFIG_FEATURE_UDHCP_8021Q=y CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script" CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80 CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n" CONFIG_UDPSVD=y CONFIG_VCONFIG=y CONFIG_WGET=y CONFIG_FEATURE_WGET_STATUSBAR=y CONFIG_FEATURE_WGET_AUTHENTICATION=y # CONFIG_FEATURE_WGET_LONG_OPTIONS is not set CONFIG_FEATURE_WGET_TIMEOUT=y # CONFIG_ZCIP is not set # # Print Utilities # CONFIG_LPD=y CONFIG_LPR=y CONFIG_LPQ=y # # Mail Utilities # CONFIG_MAKEMIME=y CONFIG_FEATURE_MIME_CHARSET="us-ascii" CONFIG_POPMAILDIR=y CONFIG_FEATURE_POPMAILDIR_DELIVERY=y CONFIG_REFORMIME=y CONFIG_FEATURE_REFORMIME_COMPAT=y CONFIG_SENDMAIL=y # # Process Utilities # CONFIG_IOSTAT=y CONFIG_LSOF=y CONFIG_MPSTAT=y CONFIG_NMETER=y CONFIG_PMAP=y CONFIG_POWERTOP=y CONFIG_PSTREE=y CONFIG_PWDX=y CONFIG_SMEMCAP=y CONFIG_UPTIME=y # CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set CONFIG_FREE=y CONFIG_FUSER=y # CONFIG_KILL is not set # CONFIG_KILLALL is not set # CONFIG_KILLALL5 is not set # CONFIG_PGREP is not set CONFIG_PIDOF=y CONFIG_FEATURE_PIDOF_SINGLE=y CONFIG_FEATURE_PIDOF_OMIT=y # CONFIG_PKILL is not set CONFIG_PS=y # CONFIG_FEATURE_PS_WIDE is not set # CONFIG_FEATURE_PS_LONG is not set CONFIG_FEATURE_PS_TIME=y CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y # CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set CONFIG_RENICE=y CONFIG_BB_SYSCTL=y CONFIG_TOP=y CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y CONFIG_FEATURE_TOP_SMP_CPU=y CONFIG_FEATURE_TOP_DECIMALS=y CONFIG_FEATURE_TOP_SMP_PROCESS=y CONFIG_FEATURE_TOPMEM=y CONFIG_FEATURE_SHOW_THREADS=y CONFIG_WATCH=y # # Runit Utilities # CONFIG_RUNSV=y CONFIG_RUNSVDIR=y # CONFIG_FEATURE_RUNSVDIR_LOG is not set CONFIG_SV=y CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service" CONFIG_SVLOGD=y CONFIG_CHPST=y CONFIG_SETUIDGID=y CONFIG_ENVUIDGID=y CONFIG_ENVDIR=y CONFIG_SOFTLIMIT=y # CONFIG_CHCON is not set # CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set # CONFIG_GETENFORCE is not set # CONFIG_GETSEBOOL is not set # CONFIG_LOAD_POLICY is not set # CONFIG_MATCHPATHCON is not set # CONFIG_RESTORECON is not set # CONFIG_RUNCON is not set # CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set # CONFIG_SELINUXENABLED is not set # CONFIG_SETENFORCE is not set # CONFIG_SETFILES is not set # CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set # CONFIG_SETSEBOOL is not set # CONFIG_SESTATUS is not set # # Shells # # CONFIG_ASH is not set # CONFIG_ASH_BASH_COMPAT is not set # CONFIG_ASH_IDLE_TIMEOUT is not set # CONFIG_ASH_JOB_CONTROL is not set # CONFIG_ASH_ALIAS is not set # CONFIG_ASH_GETOPTS is not set # CONFIG_ASH_BUILTIN_ECHO is not set # CONFIG_ASH_BUILTIN_PRINTF is not set # CONFIG_ASH_BUILTIN_TEST is not set # CONFIG_ASH_CMDCMD is not set # CONFIG_ASH_MAIL is not set # CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set # CONFIG_ASH_RANDOM_SUPPORT is not set # CONFIG_ASH_EXPAND_PRMT is not set CONFIG_CTTYHACK=y # CONFIG_HUSH is not set # CONFIG_HUSH_BASH_COMPAT is not set # CONFIG_HUSH_BRACE_EXPANSION is not set # CONFIG_HUSH_HELP is not set # CONFIG_HUSH_INTERACTIVE is not set # CONFIG_HUSH_SAVEHISTORY is not set # CONFIG_HUSH_JOB is not set # CONFIG_HUSH_TICK is not set # CONFIG_HUSH_IF is not set # CONFIG_HUSH_LOOPS is not set # CONFIG_HUSH_CASE is not set # CONFIG_HUSH_FUNCTIONS is not set # CONFIG_HUSH_LOCAL is not set # CONFIG_HUSH_RANDOM_SUPPORT is not set # CONFIG_HUSH_EXPORT_N is not set # CONFIG_HUSH_MODE_X is not set # CONFIG_MSH is not set # CONFIG_FEATURE_SH_IS_ASH is not set # CONFIG_FEATURE_SH_IS_HUSH is not set CONFIG_FEATURE_SH_IS_NONE=y # CONFIG_FEATURE_BASH_IS_ASH is not set # CONFIG_FEATURE_BASH_IS_HUSH is not set CONFIG_FEATURE_BASH_IS_NONE=y # CONFIG_SH_MATH_SUPPORT is not set # CONFIG_SH_MATH_SUPPORT_64 is not set # CONFIG_FEATURE_SH_EXTRA_QUIET is not set # CONFIG_FEATURE_SH_STANDALONE is not set # CONFIG_FEATURE_SH_NOFORK is not set # CONFIG_FEATURE_SH_HISTFILESIZE is not set # # System Logging Utilities # # CONFIG_SYSLOGD is not set # CONFIG_FEATURE_ROTATE_LOGFILE is not set # CONFIG_FEATURE_REMOTE_LOG is not set # CONFIG_FEATURE_SYSLOGD_DUP is not set # CONFIG_FEATURE_SYSLOGD_CFG is not set CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0 # CONFIG_FEATURE_IPC_SYSLOG is not set CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0 # CONFIG_LOGREAD is not set # CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set CONFIG_KLOGD=y CONFIG_FEATURE_KLOGD_KLOGCTL=y # CONFIG_LOGGER is not set busybox-1.22.1/configs/android_defconfig0000644000000000000000000006617312263563520017006 0ustar rootroot# # Automatically generated make config: don't edit # Busybox version: 1.20.0.git # Sun Nov 6 07:51:38 2011 # CONFIG_HAVE_DOT_CONFIG=y # # Busybox Settings # # # General Configuration # CONFIG_DESKTOP=y # CONFIG_EXTRA_COMPAT is not set # CONFIG_INCLUDE_SUSv2 is not set # CONFIG_USE_PORTABLE_CODE is not set CONFIG_PLATFORM_LINUX=y CONFIG_FEATURE_BUFFERS_USE_MALLOC=y # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set # CONFIG_SHOW_USAGE is not set # CONFIG_FEATURE_VERBOSE_USAGE is not set # CONFIG_FEATURE_COMPRESS_USAGE is not set # CONFIG_FEATURE_INSTALLER is not set # CONFIG_INSTALL_NO_USR is not set # CONFIG_LOCALE_SUPPORT is not set # CONFIG_UNICODE_SUPPORT is not set # CONFIG_UNICODE_USING_LOCALE is not set # CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set CONFIG_SUBST_WCHAR=0 CONFIG_LAST_SUPPORTED_WCHAR=0 # CONFIG_UNICODE_COMBINING_WCHARS is not set # CONFIG_UNICODE_WIDE_WCHARS is not set # CONFIG_UNICODE_BIDI_SUPPORT is not set # CONFIG_UNICODE_NEUTRAL_TABLE is not set # CONFIG_UNICODE_PRESERVE_BROKEN is not set # CONFIG_LONG_OPTS is not set # CONFIG_FEATURE_DEVPTS is not set # CONFIG_FEATURE_CLEAN_UP is not set # CONFIG_FEATURE_UTMP is not set # CONFIG_FEATURE_WTMP is not set # CONFIG_FEATURE_PIDFILE is not set # CONFIG_FEATURE_SUID is not set # CONFIG_FEATURE_SUID_CONFIG is not set # CONFIG_FEATURE_SUID_CONFIG_QUIET is not set # CONFIG_SELINUX is not set # CONFIG_FEATURE_PREFER_APPLETS is not set CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" CONFIG_FEATURE_SYSLOG=y # CONFIG_FEATURE_HAVE_RPC is not set # # Build Options # # CONFIG_STATIC is not set # CONFIG_PIE is not set # CONFIG_NOMMU is not set # CONFIG_BUILD_LIBBUSYBOX is not set # CONFIG_FEATURE_INDIVIDUAL is not set # CONFIG_FEATURE_SHARED_BUSYBOX is not set # CONFIG_LFS is not set CONFIG_CROSS_COMPILER_PREFIX="arm-eabi-" # # Removed: # warning flags: # -Wno-multichar -W -Wall -Wno-unused -Winit-self -Wpointer-arith # -Werror=return-type -Werror=non-virtual-dtor -Werror=address # -Werror=sequence-point -Wstrict-aliasing=2 -Wno-undef -Wno-shadow # bbox already adds these: # -ffunction-sections -fomit-frame-pointer # enabled implicitly by -Os: # -frerun-cse-after-loop # should be not needed, or even increases code size: # -finline-functions -fno-inline-functions-called-once -finline-limit=64 # -fstack-protector -fno-strict-aliasing -fno-exceptions -funwind-tables # -fmessage-length=0 (only affects error message format) # todo: do we need these? - # -fno-short-enums # -fgcse-after-reload # -frename-registers CONFIG_EXTRA_CFLAGS="-I$A/system/core/include -I$A/bionic/libc/arch-arm/include -I$A/bionic/libc/include -I$A/bionic/libc/kernel/common -I$A/bionic/libc/kernel/arch-arm -I$A/bionic/libm/include -I$A/bionic/libm/include/arch/arm -include $A/system/core/include/arch/linux-arm/AndroidConfig.h -I$A/system/core/include/arch/linux-arm/ -D__ANDROID__ -DSK_RELEASE -nostdlib -march=armv7-a -msoft-float -mfloat-abi=softfp -mfpu=neon -mthumb -mthumb-interwork -fpic -fno-short-enums -fgcse-after-reload -frename-registers" # # Debugging Options # # CONFIG_DEBUG is not set # CONFIG_DEBUG_PESSIMIZE is not set # CONFIG_WERROR is not set CONFIG_NO_DEBUG_LIB=y # CONFIG_DMALLOC is not set # CONFIG_EFENCE is not set # # Installation Options ("make install" behavior) # CONFIG_INSTALL_APPLET_SYMLINKS=y # CONFIG_INSTALL_APPLET_HARDLINKS is not set # CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set # CONFIG_INSTALL_APPLET_DONT is not set # CONFIG_INSTALL_SH_APPLET_SYMLINK is not set # CONFIG_INSTALL_SH_APPLET_HARDLINK is not set # CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set CONFIG_PREFIX="./_install" # # Busybox Library Tuning # # CONFIG_FEATURE_SYSTEMD is not set # CONFIG_FEATURE_RTMINMAX is not set CONFIG_PASSWORD_MINLEN=6 CONFIG_MD5_SMALL=1 # CONFIG_FEATURE_FAST_TOP is not set # CONFIG_FEATURE_ETC_NETWORKS is not set CONFIG_FEATURE_USE_TERMIOS=y # CONFIG_FEATURE_EDITING is not set CONFIG_FEATURE_EDITING_MAX_LEN=0 # CONFIG_FEATURE_EDITING_VI is not set CONFIG_FEATURE_EDITING_HISTORY=0 # CONFIG_FEATURE_EDITING_SAVEHISTORY is not set # CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set # CONFIG_FEATURE_REVERSE_SEARCH is not set # CONFIG_FEATURE_TAB_COMPLETION is not set # CONFIG_FEATURE_USERNAME_COMPLETION is not set # CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set # CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set # CONFIG_FEATURE_NON_POSIX_CP is not set # CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set CONFIG_FEATURE_COPYBUF_KB=4 # CONFIG_FEATURE_SKIP_ROOTFS is not set # CONFIG_MONOTONIC_SYSCALL is not set # CONFIG_IOCTL_HEX2STR_ERROR is not set # CONFIG_FEATURE_HWIB is not set # # Applets # # # Archival Utilities # CONFIG_FEATURE_SEAMLESS_XZ=y CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y CONFIG_FEATURE_SEAMLESS_Z=y CONFIG_AR=y CONFIG_FEATURE_AR_LONG_FILENAMES=y CONFIG_FEATURE_AR_CREATE=y CONFIG_BUNZIP2=y CONFIG_BZIP2=y CONFIG_CPIO=y CONFIG_FEATURE_CPIO_O=y CONFIG_FEATURE_CPIO_P=y CONFIG_DPKG=y CONFIG_DPKG_DEB=y # CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set CONFIG_GUNZIP=y CONFIG_GZIP=y # CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set CONFIG_GZIP_FAST=0 CONFIG_LZOP=y CONFIG_LZOP_COMPR_HIGH=y CONFIG_RPM2CPIO=y CONFIG_RPM=y CONFIG_TAR=y CONFIG_FEATURE_TAR_CREATE=y CONFIG_FEATURE_TAR_AUTODETECT=y CONFIG_FEATURE_TAR_FROM=y CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y # CONFIG_FEATURE_TAR_LONG_OPTIONS is not set # CONFIG_FEATURE_TAR_TO_COMMAND is not set CONFIG_FEATURE_TAR_UNAME_GNAME=y CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y # CONFIG_FEATURE_TAR_SELINUX is not set CONFIG_UNCOMPRESS=y CONFIG_UNLZMA=y CONFIG_FEATURE_LZMA_FAST=y CONFIG_LZMA=y CONFIG_UNXZ=y CONFIG_XZ=y CONFIG_UNZIP=y # # Coreutils # CONFIG_BASENAME=y CONFIG_CAT=y # CONFIG_DATE is not set # CONFIG_FEATURE_DATE_ISOFMT is not set # CONFIG_FEATURE_DATE_NANO is not set # CONFIG_FEATURE_DATE_COMPAT is not set # CONFIG_HOSTID is not set # CONFIG_ID is not set # CONFIG_GROUPS is not set CONFIG_TEST=y CONFIG_FEATURE_TEST_64=y CONFIG_TOUCH=y CONFIG_FEATURE_TOUCH_SUSV3=y CONFIG_TR=y CONFIG_FEATURE_TR_CLASSES=y CONFIG_FEATURE_TR_EQUIV=y CONFIG_BASE64=y # CONFIG_WHO is not set # CONFIG_USERS is not set CONFIG_CAL=y CONFIG_CATV=y CONFIG_CHGRP=y CONFIG_CHMOD=y CONFIG_CHOWN=y # CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set CONFIG_CHROOT=y CONFIG_CKSUM=y CONFIG_COMM=y CONFIG_CP=y # CONFIG_FEATURE_CP_LONG_OPTIONS is not set CONFIG_CUT=y CONFIG_DD=y CONFIG_FEATURE_DD_SIGNAL_HANDLING=y CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y CONFIG_FEATURE_DD_IBS_OBS=y # CONFIG_DF is not set # CONFIG_FEATURE_DF_FANCY is not set CONFIG_DIRNAME=y CONFIG_DOS2UNIX=y CONFIG_UNIX2DOS=y CONFIG_DU=y CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y CONFIG_ECHO=y CONFIG_FEATURE_FANCY_ECHO=y CONFIG_ENV=y # CONFIG_FEATURE_ENV_LONG_OPTIONS is not set CONFIG_EXPAND=y # CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set CONFIG_EXPR=y CONFIG_EXPR_MATH_SUPPORT_64=y CONFIG_FALSE=y CONFIG_FOLD=y CONFIG_FSYNC=y CONFIG_HEAD=y CONFIG_FEATURE_FANCY_HEAD=y CONFIG_INSTALL=y # CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set CONFIG_LN=y # CONFIG_LOGNAME is not set CONFIG_LS=y CONFIG_FEATURE_LS_FILETYPES=y CONFIG_FEATURE_LS_FOLLOWLINKS=y CONFIG_FEATURE_LS_RECURSIVE=y CONFIG_FEATURE_LS_SORTFILES=y CONFIG_FEATURE_LS_TIMESTAMPS=y CONFIG_FEATURE_LS_USERNAME=y # CONFIG_FEATURE_LS_COLOR is not set # CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set CONFIG_MD5SUM=y CONFIG_MKDIR=y # CONFIG_FEATURE_MKDIR_LONG_OPTIONS is not set CONFIG_MKFIFO=y CONFIG_MKNOD=y CONFIG_MV=y # CONFIG_FEATURE_MV_LONG_OPTIONS is not set CONFIG_NICE=y CONFIG_NOHUP=y CONFIG_OD=y CONFIG_PRINTENV=y CONFIG_PRINTF=y CONFIG_PWD=y CONFIG_READLINK=y CONFIG_FEATURE_READLINK_FOLLOW=y CONFIG_REALPATH=y CONFIG_RM=y CONFIG_RMDIR=y # CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set CONFIG_SEQ=y CONFIG_SHA1SUM=y CONFIG_SHA256SUM=y CONFIG_SHA512SUM=y CONFIG_SLEEP=y CONFIG_FEATURE_FANCY_SLEEP=y CONFIG_FEATURE_FLOAT_SLEEP=y CONFIG_SORT=y CONFIG_FEATURE_SORT_BIG=y CONFIG_SPLIT=y CONFIG_FEATURE_SPLIT_FANCY=y # CONFIG_STAT is not set # CONFIG_FEATURE_STAT_FORMAT is not set CONFIG_STTY=y CONFIG_SUM=y CONFIG_SYNC=y CONFIG_TAC=y CONFIG_TAIL=y CONFIG_FEATURE_FANCY_TAIL=y CONFIG_TEE=y CONFIG_FEATURE_TEE_USE_BLOCK_IO=y CONFIG_TRUE=y # CONFIG_TTY is not set CONFIG_UNAME=y CONFIG_UNEXPAND=y # CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set CONFIG_UNIQ=y CONFIG_USLEEP=y CONFIG_UUDECODE=y CONFIG_UUENCODE=y CONFIG_WC=y CONFIG_FEATURE_WC_LARGE=y CONFIG_WHOAMI=y CONFIG_YES=y # # Common options for cp and mv # CONFIG_FEATURE_PRESERVE_HARDLINKS=y # # Common options for ls, more and telnet # CONFIG_FEATURE_AUTOWIDTH=y # # Common options for df, du, ls # CONFIG_FEATURE_HUMAN_READABLE=y # # Common options for md5sum, sha1sum, sha256sum, sha512sum # CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y # # Console Utilities # CONFIG_CHVT=y CONFIG_FGCONSOLE=y CONFIG_CLEAR=y CONFIG_DEALLOCVT=y CONFIG_DUMPKMAP=y # CONFIG_KBD_MODE is not set # CONFIG_LOADFONT is not set CONFIG_LOADKMAP=y CONFIG_OPENVT=y CONFIG_RESET=y CONFIG_RESIZE=y CONFIG_FEATURE_RESIZE_PRINT=y CONFIG_SETCONSOLE=y # CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set # CONFIG_SETFONT is not set # CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set CONFIG_DEFAULT_SETFONT_DIR="" CONFIG_SETKEYCODES=y CONFIG_SETLOGCONS=y CONFIG_SHOWKEY=y # CONFIG_FEATURE_LOADFONT_PSF2 is not set # CONFIG_FEATURE_LOADFONT_RAW is not set # # Debian Utilities # CONFIG_MKTEMP=y CONFIG_PIPE_PROGRESS=y CONFIG_RUN_PARTS=y # CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set CONFIG_FEATURE_RUN_PARTS_FANCY=y CONFIG_START_STOP_DAEMON=y CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y # CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set CONFIG_WHICH=y # # Editors # CONFIG_PATCH=y CONFIG_VI=y CONFIG_FEATURE_VI_MAX_LEN=4096 CONFIG_FEATURE_VI_8BIT=y CONFIG_FEATURE_VI_COLON=y CONFIG_FEATURE_VI_YANKMARK=y CONFIG_FEATURE_VI_SEARCH=y # CONFIG_FEATURE_VI_REGEX_SEARCH is not set CONFIG_FEATURE_VI_USE_SIGNALS=y CONFIG_FEATURE_VI_DOT_CMD=y CONFIG_FEATURE_VI_READONLY=y CONFIG_FEATURE_VI_SETOPTS=y CONFIG_FEATURE_VI_SET=y CONFIG_FEATURE_VI_WIN_RESIZE=y CONFIG_FEATURE_VI_ASK_TERMINAL=y CONFIG_AWK=y CONFIG_FEATURE_AWK_LIBM=y CONFIG_CMP=y CONFIG_DIFF=y # CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set CONFIG_FEATURE_DIFF_DIR=y CONFIG_ED=y CONFIG_SED=y CONFIG_FEATURE_ALLOW_EXEC=y # # Finding Utilities # CONFIG_FIND=y CONFIG_FEATURE_FIND_PRINT0=y CONFIG_FEATURE_FIND_MTIME=y CONFIG_FEATURE_FIND_MMIN=y CONFIG_FEATURE_FIND_PERM=y CONFIG_FEATURE_FIND_TYPE=y CONFIG_FEATURE_FIND_XDEV=y CONFIG_FEATURE_FIND_MAXDEPTH=y CONFIG_FEATURE_FIND_NEWER=y CONFIG_FEATURE_FIND_INUM=y CONFIG_FEATURE_FIND_EXEC=y CONFIG_FEATURE_FIND_USER=y CONFIG_FEATURE_FIND_GROUP=y CONFIG_FEATURE_FIND_NOT=y CONFIG_FEATURE_FIND_DEPTH=y CONFIG_FEATURE_FIND_PAREN=y CONFIG_FEATURE_FIND_SIZE=y CONFIG_FEATURE_FIND_PRUNE=y CONFIG_FEATURE_FIND_DELETE=y CONFIG_FEATURE_FIND_PATH=y CONFIG_FEATURE_FIND_REGEX=y # CONFIG_FEATURE_FIND_CONTEXT is not set CONFIG_FEATURE_FIND_LINKS=y CONFIG_GREP=y CONFIG_FEATURE_GREP_EGREP_ALIAS=y CONFIG_FEATURE_GREP_FGREP_ALIAS=y CONFIG_FEATURE_GREP_CONTEXT=y CONFIG_XARGS=y CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y # # Init Utilities # CONFIG_BOOTCHARTD=y CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER=y CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE=y CONFIG_HALT=y # CONFIG_FEATURE_CALL_TELINIT is not set CONFIG_TELINIT_PATH="" CONFIG_INIT=y CONFIG_FEATURE_USE_INITTAB=y # CONFIG_FEATURE_KILL_REMOVED is not set CONFIG_FEATURE_KILL_DELAY=0 CONFIG_FEATURE_INIT_SCTTY=y CONFIG_FEATURE_INIT_SYSLOG=y CONFIG_FEATURE_EXTRA_QUIET=y CONFIG_FEATURE_INIT_COREDUMPS=y CONFIG_FEATURE_INITRD=y CONFIG_INIT_TERMINAL_TYPE="linux" CONFIG_MESG=y CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y # # Login/Password Management Utilities # # CONFIG_ADD_SHELL is not set # CONFIG_REMOVE_SHELL is not set # CONFIG_FEATURE_SHADOWPASSWDS is not set # CONFIG_USE_BB_PWD_GRP is not set # CONFIG_USE_BB_SHADOW is not set # CONFIG_USE_BB_CRYPT is not set # CONFIG_USE_BB_CRYPT_SHA is not set # CONFIG_ADDUSER is not set # CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set # CONFIG_FEATURE_CHECK_NAMES is not set CONFIG_FIRST_SYSTEM_ID=0 CONFIG_LAST_SYSTEM_ID=0 # CONFIG_ADDGROUP is not set # CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set # CONFIG_FEATURE_ADDUSER_TO_GROUP is not set # CONFIG_DELUSER is not set # CONFIG_DELGROUP is not set # CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set # CONFIG_GETTY is not set # CONFIG_LOGIN is not set # CONFIG_LOGIN_SESSION_AS_CHILD is not set # CONFIG_PAM is not set # CONFIG_LOGIN_SCRIPTS is not set # CONFIG_FEATURE_NOLOGIN is not set # CONFIG_FEATURE_SECURETTY is not set # CONFIG_PASSWD is not set # CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set # CONFIG_CRYPTPW is not set # CONFIG_CHPASSWD is not set # CONFIG_SU is not set # CONFIG_FEATURE_SU_SYSLOG is not set # CONFIG_FEATURE_SU_CHECKS_SHELLS is not set # CONFIG_SULOGIN is not set # CONFIG_VLOCK is not set # # Linux Ext2 FS Progs # CONFIG_CHATTR=y # CONFIG_FSCK is not set CONFIG_LSATTR=y CONFIG_TUNE2FS=y # # Linux Module Utilities # CONFIG_MODINFO=y CONFIG_MODPROBE_SMALL=y CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE=y CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y # CONFIG_INSMOD is not set # CONFIG_RMMOD is not set # CONFIG_LSMOD is not set # CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set # CONFIG_MODPROBE is not set # CONFIG_FEATURE_MODPROBE_BLACKLIST is not set # CONFIG_DEPMOD is not set # # Options common to multiple modutils # # CONFIG_FEATURE_2_4_MODULES is not set # CONFIG_FEATURE_INSMOD_TRY_MMAP is not set # CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set # CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set # CONFIG_FEATURE_INSMOD_LOADINKMEM is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set # CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set # CONFIG_FEATURE_MODUTILS_ALIAS is not set # CONFIG_FEATURE_MODUTILS_SYMBOLS is not set CONFIG_DEFAULT_MODULES_DIR="/lib/modules" CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" # # Linux System Utilities # CONFIG_BLOCKDEV=y CONFIG_MDEV=y CONFIG_FEATURE_MDEV_CONF=y CONFIG_FEATURE_MDEV_RENAME=y CONFIG_FEATURE_MDEV_RENAME_REGEXP=y CONFIG_FEATURE_MDEV_EXEC=y CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y CONFIG_REV=y # CONFIG_ACPID is not set # CONFIG_FEATURE_ACPID_COMPAT is not set CONFIG_BLKID=y CONFIG_FEATURE_BLKID_TYPE=y CONFIG_DMESG=y CONFIG_FEATURE_DMESG_PRETTY=y CONFIG_FBSET=y CONFIG_FEATURE_FBSET_FANCY=y CONFIG_FEATURE_FBSET_READMODE=y CONFIG_FDFLUSH=y CONFIG_FDFORMAT=y CONFIG_FDISK=y CONFIG_FDISK_SUPPORT_LARGE_DISKS=y CONFIG_FEATURE_FDISK_WRITABLE=y # CONFIG_FEATURE_AIX_LABEL is not set # CONFIG_FEATURE_SGI_LABEL is not set # CONFIG_FEATURE_SUN_LABEL is not set # CONFIG_FEATURE_OSF_LABEL is not set # CONFIG_FEATURE_GPT_LABEL is not set CONFIG_FEATURE_FDISK_ADVANCED=y CONFIG_FINDFS=y CONFIG_FLOCK=y CONFIG_FREERAMDISK=y # CONFIG_FSCK_MINIX is not set # CONFIG_MKFS_EXT2 is not set # CONFIG_MKFS_MINIX is not set # CONFIG_FEATURE_MINIX2 is not set # CONFIG_MKFS_REISER is not set # CONFIG_MKFS_VFAT is not set CONFIG_GETOPT=y CONFIG_FEATURE_GETOPT_LONG=y CONFIG_HEXDUMP=y CONFIG_FEATURE_HEXDUMP_REVERSE=y CONFIG_HD=y CONFIG_HWCLOCK=y # CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set # CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set # CONFIG_IPCRM is not set # CONFIG_IPCS is not set CONFIG_LOSETUP=y CONFIG_LSPCI=y CONFIG_LSUSB=y CONFIG_MKSWAP=y CONFIG_FEATURE_MKSWAP_UUID=y CONFIG_MORE=y # CONFIG_MOUNT is not set # CONFIG_FEATURE_MOUNT_FAKE is not set # CONFIG_FEATURE_MOUNT_VERBOSE is not set # CONFIG_FEATURE_MOUNT_HELPERS is not set # CONFIG_FEATURE_MOUNT_LABEL is not set # CONFIG_FEATURE_MOUNT_NFS is not set # CONFIG_FEATURE_MOUNT_CIFS is not set # CONFIG_FEATURE_MOUNT_FLAGS is not set # CONFIG_FEATURE_MOUNT_FSTAB is not set # CONFIG_PIVOT_ROOT is not set # CONFIG_RDATE is not set CONFIG_RDEV=y CONFIG_READPROFILE=y CONFIG_RTCWAKE=y CONFIG_SCRIPT=y CONFIG_SCRIPTREPLAY=y # CONFIG_SETARCH is not set # CONFIG_SWAPONOFF is not set # CONFIG_FEATURE_SWAPON_PRI is not set CONFIG_SWITCH_ROOT=y # CONFIG_UMOUNT is not set # CONFIG_FEATURE_UMOUNT_ALL is not set # CONFIG_FEATURE_MOUNT_LOOP is not set # CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set # CONFIG_FEATURE_MTAB_SUPPORT is not set CONFIG_VOLUMEID=y # # Filesystem/Volume identification # CONFIG_FEATURE_VOLUMEID_EXT=y CONFIG_FEATURE_VOLUMEID_BTRFS=y CONFIG_FEATURE_VOLUMEID_REISERFS=y CONFIG_FEATURE_VOLUMEID_FAT=y CONFIG_FEATURE_VOLUMEID_HFS=y CONFIG_FEATURE_VOLUMEID_JFS=y CONFIG_FEATURE_VOLUMEID_XFS=y CONFIG_FEATURE_VOLUMEID_NTFS=y CONFIG_FEATURE_VOLUMEID_ISO9660=y CONFIG_FEATURE_VOLUMEID_UDF=y CONFIG_FEATURE_VOLUMEID_LUKS=y CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y CONFIG_FEATURE_VOLUMEID_CRAMFS=y CONFIG_FEATURE_VOLUMEID_ROMFS=y CONFIG_FEATURE_VOLUMEID_SYSV=y CONFIG_FEATURE_VOLUMEID_OCFS2=y CONFIG_FEATURE_VOLUMEID_LINUXRAID=y # # Miscellaneous Utilities # # CONFIG_CONSPY is not set CONFIG_LESS=y CONFIG_FEATURE_LESS_MAXLINES=9999999 CONFIG_FEATURE_LESS_BRACKETS=y CONFIG_FEATURE_LESS_FLAGS=y CONFIG_FEATURE_LESS_MARKS=y CONFIG_FEATURE_LESS_REGEXP=y CONFIG_FEATURE_LESS_WINCH=y CONFIG_FEATURE_LESS_ASK_TERMINAL=y CONFIG_FEATURE_LESS_DASHCMD=y CONFIG_FEATURE_LESS_LINENUMS=y # CONFIG_NANDWRITE is not set CONFIG_NANDDUMP=y CONFIG_SETSERIAL=y # CONFIG_UBIATTACH is not set # CONFIG_UBIDETACH is not set # CONFIG_UBIMKVOL is not set # CONFIG_UBIRMVOL is not set # CONFIG_UBIRSVOL is not set # CONFIG_UBIUPDATEVOL is not set # CONFIG_ADJTIMEX is not set # CONFIG_BBCONFIG is not set # CONFIG_FEATURE_COMPRESS_BBCONFIG is not set CONFIG_BEEP=y CONFIG_FEATURE_BEEP_FREQ=4000 CONFIG_FEATURE_BEEP_LENGTH_MS=30 CONFIG_CHAT=y CONFIG_FEATURE_CHAT_NOFAIL=y # CONFIG_FEATURE_CHAT_TTY_HIFI is not set CONFIG_FEATURE_CHAT_IMPLICIT_CR=y CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y CONFIG_FEATURE_CHAT_SEND_ESCAPES=y CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y CONFIG_FEATURE_CHAT_CLR_ABORT=y CONFIG_CHRT=y CONFIG_CROND=y CONFIG_FEATURE_CROND_D=y CONFIG_FEATURE_CROND_CALL_SENDMAIL=y CONFIG_FEATURE_CROND_DIR="/var/spool/cron" CONFIG_CRONTAB=y CONFIG_DC=y CONFIG_FEATURE_DC_LIBM=y # CONFIG_DEVFSD is not set # CONFIG_DEVFSD_MODLOAD is not set # CONFIG_DEVFSD_FG_NP is not set # CONFIG_DEVFSD_VERBOSE is not set # CONFIG_FEATURE_DEVFS is not set CONFIG_DEVMEM=y # CONFIG_EJECT is not set # CONFIG_FEATURE_EJECT_SCSI is not set CONFIG_FBSPLASH=y CONFIG_FLASHCP=y CONFIG_FLASH_LOCK=y CONFIG_FLASH_UNLOCK=y # CONFIG_FLASH_ERASEALL is not set # CONFIG_IONICE is not set CONFIG_INOTIFYD=y # CONFIG_LAST is not set # CONFIG_FEATURE_LAST_SMALL is not set # CONFIG_FEATURE_LAST_FANCY is not set CONFIG_HDPARM=y CONFIG_FEATURE_HDPARM_GET_IDENTITY=y CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y CONFIG_MAKEDEVS=y # CONFIG_FEATURE_MAKEDEVS_LEAF is not set CONFIG_FEATURE_MAKEDEVS_TABLE=y CONFIG_MAN=y # CONFIG_MICROCOM is not set # CONFIG_MOUNTPOINT is not set # CONFIG_MT is not set CONFIG_RAIDAUTORUN=y # CONFIG_READAHEAD is not set # CONFIG_RFKILL is not set # CONFIG_RUNLEVEL is not set CONFIG_RX=y CONFIG_SETSID=y CONFIG_STRINGS=y # CONFIG_TASKSET is not set # CONFIG_FEATURE_TASKSET_FANCY is not set CONFIG_TIME=y CONFIG_TIMEOUT=y CONFIG_TTYSIZE=y CONFIG_VOLNAME=y # CONFIG_WALL is not set # CONFIG_WATCHDOG is not set # # Networking Utilities # # CONFIG_NAMEIF is not set # CONFIG_FEATURE_NAMEIF_EXTENDED is not set CONFIG_NBDCLIENT=y CONFIG_NC=y CONFIG_NC_SERVER=y CONFIG_NC_EXTRA=y # CONFIG_NC_110_COMPAT is not set CONFIG_PING=y # CONFIG_PING6 is not set CONFIG_FEATURE_FANCY_PING=y CONFIG_WHOIS=y # CONFIG_FEATURE_IPV6 is not set # CONFIG_FEATURE_UNIX_LOCAL is not set # CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set # CONFIG_VERBOSE_RESOLUTION_ERRORS is not set CONFIG_ARP=y # CONFIG_ARPING is not set # CONFIG_BRCTL is not set # CONFIG_FEATURE_BRCTL_FANCY is not set # CONFIG_FEATURE_BRCTL_SHOW is not set CONFIG_DNSD=y # CONFIG_ETHER_WAKE is not set CONFIG_FAKEIDENTD=y CONFIG_FTPD=y CONFIG_FEATURE_FTP_WRITE=y CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y CONFIG_FTPGET=y CONFIG_FTPPUT=y # CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set # CONFIG_HOSTNAME is not set CONFIG_HTTPD=y CONFIG_FEATURE_HTTPD_RANGES=y CONFIG_FEATURE_HTTPD_USE_SENDFILE=y CONFIG_FEATURE_HTTPD_SETUID=y CONFIG_FEATURE_HTTPD_BASIC_AUTH=y # CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set CONFIG_FEATURE_HTTPD_CGI=y CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y CONFIG_FEATURE_HTTPD_ERROR_PAGES=y CONFIG_FEATURE_HTTPD_PROXY=y CONFIG_FEATURE_HTTPD_GZIP=y CONFIG_IFCONFIG=y CONFIG_FEATURE_IFCONFIG_STATUS=y # CONFIG_FEATURE_IFCONFIG_SLIP is not set CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y CONFIG_FEATURE_IFCONFIG_HW=y CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y # CONFIG_IFENSLAVE is not set # CONFIG_IFPLUGD is not set CONFIG_IFUPDOWN=y CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate" CONFIG_FEATURE_IFUPDOWN_IP=y CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y # CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set CONFIG_FEATURE_IFUPDOWN_IPV4=y # CONFIG_FEATURE_IFUPDOWN_IPV6 is not set CONFIG_FEATURE_IFUPDOWN_MAPPING=y CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP=y # CONFIG_INETD is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set # CONFIG_FEATURE_INETD_RPC is not set CONFIG_IP=y CONFIG_FEATURE_IP_ADDRESS=y CONFIG_FEATURE_IP_LINK=y CONFIG_FEATURE_IP_ROUTE=y CONFIG_FEATURE_IP_TUNNEL=y CONFIG_FEATURE_IP_RULE=y CONFIG_FEATURE_IP_SHORT_FORMS=y # CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set CONFIG_IPADDR=y CONFIG_IPLINK=y CONFIG_IPROUTE=y CONFIG_IPTUNNEL=y CONFIG_IPRULE=y CONFIG_IPCALC=y CONFIG_FEATURE_IPCALC_FANCY=y # CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set CONFIG_NETSTAT=y CONFIG_FEATURE_NETSTAT_WIDE=y CONFIG_FEATURE_NETSTAT_PRG=y # CONFIG_NSLOOKUP is not set # CONFIG_NTPD is not set # CONFIG_FEATURE_NTPD_SERVER is not set CONFIG_PSCAN=y CONFIG_ROUTE=y # CONFIG_SLATTACH is not set CONFIG_TCPSVD=y CONFIG_TELNET=y CONFIG_FEATURE_TELNET_TTYPE=y CONFIG_FEATURE_TELNET_AUTOLOGIN=y CONFIG_TELNETD=y CONFIG_FEATURE_TELNETD_STANDALONE=y CONFIG_FEATURE_TELNETD_INETD_WAIT=y CONFIG_TFTP=y CONFIG_TFTPD=y # # Common options for tftp/tftpd # CONFIG_FEATURE_TFTP_GET=y CONFIG_FEATURE_TFTP_PUT=y CONFIG_FEATURE_TFTP_BLOCKSIZE=y CONFIG_FEATURE_TFTP_PROGRESS_BAR=y # CONFIG_TFTP_DEBUG is not set CONFIG_TRACEROUTE=y # CONFIG_TRACEROUTE6 is not set CONFIG_FEATURE_TRACEROUTE_VERBOSE=y # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set CONFIG_TUNCTL=y CONFIG_FEATURE_TUNCTL_UG=y # CONFIG_UDHCPC6 is not set # CONFIG_UDHCPD is not set # CONFIG_DHCPRELAY is not set # CONFIG_DUMPLEASES is not set # CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set # CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set CONFIG_DHCPD_LEASES_FILE="" CONFIG_UDHCPC=y CONFIG_FEATURE_UDHCPC_ARPING=y CONFIG_FEATURE_UDHCP_PORT=y CONFIG_UDHCP_DEBUG=9 CONFIG_FEATURE_UDHCP_RFC3397=y CONFIG_FEATURE_UDHCP_8021Q=y CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script" CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80 CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n" CONFIG_UDPSVD=y CONFIG_VCONFIG=y CONFIG_WGET=y CONFIG_FEATURE_WGET_STATUSBAR=y CONFIG_FEATURE_WGET_AUTHENTICATION=y # CONFIG_FEATURE_WGET_LONG_OPTIONS is not set CONFIG_FEATURE_WGET_TIMEOUT=y # CONFIG_ZCIP is not set # # Print Utilities # CONFIG_LPD=y CONFIG_LPR=y CONFIG_LPQ=y # # Mail Utilities # CONFIG_MAKEMIME=y CONFIG_FEATURE_MIME_CHARSET="us-ascii" CONFIG_POPMAILDIR=y CONFIG_FEATURE_POPMAILDIR_DELIVERY=y CONFIG_REFORMIME=y CONFIG_FEATURE_REFORMIME_COMPAT=y CONFIG_SENDMAIL=y # # Process Utilities # CONFIG_IOSTAT=y CONFIG_MPSTAT=y CONFIG_NMETER=y CONFIG_PMAP=y CONFIG_POWERTOP=y CONFIG_PSTREE=y CONFIG_PWDX=y CONFIG_SMEMCAP=y CONFIG_UPTIME=y # CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set CONFIG_FREE=y CONFIG_FUSER=y # CONFIG_KILL is not set # CONFIG_KILLALL is not set # CONFIG_KILLALL5 is not set # CONFIG_PGREP is not set CONFIG_PIDOF=y CONFIG_FEATURE_PIDOF_SINGLE=y CONFIG_FEATURE_PIDOF_OMIT=y # CONFIG_PKILL is not set CONFIG_PS=y # CONFIG_FEATURE_PS_WIDE is not set # CONFIG_FEATURE_PS_LONG is not set CONFIG_FEATURE_PS_TIME=y CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y # CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set CONFIG_RENICE=y CONFIG_BB_SYSCTL=y CONFIG_TOP=y CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y CONFIG_FEATURE_TOP_SMP_CPU=y CONFIG_FEATURE_TOP_DECIMALS=y CONFIG_FEATURE_TOP_SMP_PROCESS=y CONFIG_FEATURE_TOPMEM=y CONFIG_FEATURE_SHOW_THREADS=y CONFIG_WATCH=y # # Runit Utilities # CONFIG_RUNSV=y CONFIG_RUNSVDIR=y # CONFIG_FEATURE_RUNSVDIR_LOG is not set CONFIG_SV=y CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service" CONFIG_SVLOGD=y CONFIG_CHPST=y CONFIG_SETUIDGID=y CONFIG_ENVUIDGID=y CONFIG_ENVDIR=y CONFIG_SOFTLIMIT=y # CONFIG_CHCON is not set # CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set # CONFIG_GETENFORCE is not set # CONFIG_GETSEBOOL is not set # CONFIG_LOAD_POLICY is not set # CONFIG_MATCHPATHCON is not set # CONFIG_RESTORECON is not set # CONFIG_RUNCON is not set # CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set # CONFIG_SELINUXENABLED is not set # CONFIG_SETENFORCE is not set # CONFIG_SETFILES is not set # CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set # CONFIG_SETSEBOOL is not set # CONFIG_SESTATUS is not set # # Shells # # CONFIG_ASH is not set # CONFIG_ASH_BASH_COMPAT is not set # CONFIG_ASH_IDLE_TIMEOUT is not set # CONFIG_ASH_JOB_CONTROL is not set # CONFIG_ASH_ALIAS is not set # CONFIG_ASH_GETOPTS is not set # CONFIG_ASH_BUILTIN_ECHO is not set # CONFIG_ASH_BUILTIN_PRINTF is not set # CONFIG_ASH_BUILTIN_TEST is not set # CONFIG_ASH_CMDCMD is not set # CONFIG_ASH_MAIL is not set # CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set # CONFIG_ASH_RANDOM_SUPPORT is not set # CONFIG_ASH_EXPAND_PRMT is not set CONFIG_CTTYHACK=y # CONFIG_HUSH is not set # CONFIG_HUSH_BASH_COMPAT is not set # CONFIG_HUSH_BRACE_EXPANSION is not set # CONFIG_HUSH_HELP is not set # CONFIG_HUSH_INTERACTIVE is not set # CONFIG_HUSH_SAVEHISTORY is not set # CONFIG_HUSH_JOB is not set # CONFIG_HUSH_TICK is not set # CONFIG_HUSH_IF is not set # CONFIG_HUSH_LOOPS is not set # CONFIG_HUSH_CASE is not set # CONFIG_HUSH_FUNCTIONS is not set # CONFIG_HUSH_LOCAL is not set # CONFIG_HUSH_RANDOM_SUPPORT is not set # CONFIG_HUSH_EXPORT_N is not set # CONFIG_HUSH_MODE_X is not set # CONFIG_MSH is not set # CONFIG_FEATURE_SH_IS_ASH is not set # CONFIG_FEATURE_SH_IS_HUSH is not set CONFIG_FEATURE_SH_IS_NONE=y # CONFIG_FEATURE_BASH_IS_ASH is not set # CONFIG_FEATURE_BASH_IS_HUSH is not set CONFIG_FEATURE_BASH_IS_NONE=y # CONFIG_SH_MATH_SUPPORT is not set # CONFIG_SH_MATH_SUPPORT_64 is not set # CONFIG_FEATURE_SH_EXTRA_QUIET is not set # CONFIG_FEATURE_SH_STANDALONE is not set # CONFIG_FEATURE_SH_NOFORK is not set # CONFIG_FEATURE_SH_HISTFILESIZE is not set # # System Logging Utilities # # CONFIG_SYSLOGD is not set # CONFIG_FEATURE_ROTATE_LOGFILE is not set # CONFIG_FEATURE_REMOTE_LOG is not set # CONFIG_FEATURE_SYSLOGD_DUP is not set # CONFIG_FEATURE_SYSLOGD_CFG is not set CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0 # CONFIG_FEATURE_IPC_SYSLOG is not set CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0 # CONFIG_LOGREAD is not set # CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set CONFIG_KLOGD=y CONFIG_FEATURE_KLOGD_KLOGCTL=y # CONFIG_LOGGER is not set busybox-1.22.1/configs/TEST_nommu_defconfig0000644000000000000000000005140712263563520017352 0ustar rootroot# # Automatically generated make config: don't edit # Busybox version: 1.16.0 # Wed Jan 27 21:01:26 2010 # CONFIG_HAVE_DOT_CONFIG=y # # Busybox Settings # # # General Configuration # CONFIG_DESKTOP=y CONFIG_EXTRA_COMPAT=y CONFIG_INCLUDE_SUSv2=y # CONFIG_USE_PORTABLE_CODE is not set CONFIG_FEATURE_BUFFERS_USE_MALLOC=y # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set CONFIG_SHOW_USAGE=y CONFIG_FEATURE_VERBOSE_USAGE=y CONFIG_FEATURE_COMPRESS_USAGE=y CONFIG_FEATURE_INSTALLER=y # CONFIG_LOCALE_SUPPORT is not set # CONFIG_UNICODE_SUPPORT is not set # CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set CONFIG_LONG_OPTS=y CONFIG_FEATURE_DEVPTS=y # CONFIG_FEATURE_CLEAN_UP is not set CONFIG_FEATURE_PIDFILE=y CONFIG_FEATURE_SUID=y CONFIG_FEATURE_SUID_CONFIG=y CONFIG_FEATURE_SUID_CONFIG_QUIET=y CONFIG_SELINUX=y CONFIG_FEATURE_PREFER_APPLETS=y CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" CONFIG_FEATURE_SYSLOG=y CONFIG_FEATURE_HAVE_RPC=y # # Build Options # # CONFIG_STATIC is not set # CONFIG_PIE is not set CONFIG_NOMMU=y # CONFIG_BUILD_LIBBUSYBOX is not set # CONFIG_FEATURE_INDIVIDUAL is not set # CONFIG_FEATURE_SHARED_BUSYBOX is not set CONFIG_LFS=y CONFIG_CROSS_COMPILER_PREFIX="" CONFIG_EXTRA_CFLAGS="" # # Debugging Options # # CONFIG_DEBUG is not set # CONFIG_DEBUG_PESSIMIZE is not set # CONFIG_WERROR is not set CONFIG_NO_DEBUG_LIB=y # CONFIG_DMALLOC is not set # CONFIG_EFENCE is not set # # Installation Options # # CONFIG_INSTALL_NO_USR is not set CONFIG_INSTALL_APPLET_SYMLINKS=y # CONFIG_INSTALL_APPLET_HARDLINKS is not set # CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set # CONFIG_INSTALL_APPLET_DONT is not set # CONFIG_INSTALL_SH_APPLET_SYMLINK is not set # CONFIG_INSTALL_SH_APPLET_HARDLINK is not set # CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set CONFIG_PREFIX="./_install" # # Busybox Library Tuning # CONFIG_PASSWORD_MINLEN=6 CONFIG_MD5_SMALL=1 CONFIG_FEATURE_FAST_TOP=y CONFIG_FEATURE_ETC_NETWORKS=y CONFIG_FEATURE_EDITING=y CONFIG_FEATURE_EDITING_MAX_LEN=1024 CONFIG_FEATURE_EDITING_VI=y CONFIG_FEATURE_EDITING_HISTORY=15 # CONFIG_FEATURE_EDITING_SAVEHISTORY is not set CONFIG_FEATURE_TAB_COMPLETION=y CONFIG_FEATURE_USERNAME_COMPLETION=y CONFIG_FEATURE_EDITING_FANCY_PROMPT=y CONFIG_FEATURE_EDITING_ASK_TERMINAL=y CONFIG_FEATURE_NON_POSIX_CP=y CONFIG_FEATURE_VERBOSE_CP_MESSAGE=y CONFIG_FEATURE_COPYBUF_KB=4 CONFIG_MONOTONIC_SYSCALL=y CONFIG_IOCTL_HEX2STR_ERROR=y CONFIG_FEATURE_HWIB=y # # Applets # # # Archival Utilities # CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y CONFIG_FEATURE_SEAMLESS_Z=y CONFIG_AR=y CONFIG_FEATURE_AR_LONG_FILENAMES=y CONFIG_BUNZIP2=y CONFIG_BZIP2=y CONFIG_CPIO=y CONFIG_FEATURE_CPIO_O=y CONFIG_FEATURE_CPIO_P=y CONFIG_DPKG=y CONFIG_DPKG_DEB=y CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY=y CONFIG_GUNZIP=y CONFIG_GZIP=y CONFIG_FEATURE_GZIP_LONG_OPTIONS=y CONFIG_LZOP=y CONFIG_LZOP_COMPR_HIGH=y CONFIG_RPM2CPIO=y CONFIG_RPM=y CONFIG_TAR=y CONFIG_FEATURE_TAR_CREATE=y CONFIG_FEATURE_TAR_AUTODETECT=y CONFIG_FEATURE_TAR_FROM=y CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y CONFIG_FEATURE_TAR_LONG_OPTIONS=y CONFIG_FEATURE_TAR_UNAME_GNAME=y CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y CONFIG_UNCOMPRESS=y CONFIG_UNLZMA=y CONFIG_FEATURE_LZMA_FAST=y CONFIG_UNZIP=y # # Coreutils # CONFIG_BASENAME=y CONFIG_CAL=y CONFIG_CAT=y CONFIG_CATV=y CONFIG_CHGRP=y CONFIG_CHMOD=y CONFIG_CHOWN=y CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y CONFIG_CHROOT=y CONFIG_CKSUM=y CONFIG_COMM=y CONFIG_CP=y CONFIG_FEATURE_CP_LONG_OPTIONS=y CONFIG_CUT=y CONFIG_DATE=y CONFIG_FEATURE_DATE_ISOFMT=y CONFIG_FEATURE_DATE_COMPAT=y CONFIG_DD=y CONFIG_FEATURE_DD_SIGNAL_HANDLING=y CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y CONFIG_FEATURE_DD_IBS_OBS=y CONFIG_DF=y CONFIG_FEATURE_DF_FANCY=y CONFIG_DIRNAME=y CONFIG_DOS2UNIX=y CONFIG_UNIX2DOS=y CONFIG_DU=y CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y CONFIG_ECHO=y CONFIG_FEATURE_FANCY_ECHO=y CONFIG_ENV=y CONFIG_FEATURE_ENV_LONG_OPTIONS=y CONFIG_EXPAND=y CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y CONFIG_EXPR=y CONFIG_EXPR_MATH_SUPPORT_64=y CONFIG_FALSE=y CONFIG_FOLD=y CONFIG_FSYNC=y CONFIG_HEAD=y CONFIG_FEATURE_FANCY_HEAD=y CONFIG_HOSTID=y CONFIG_ID=y CONFIG_INSTALL=y CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y CONFIG_LN=y CONFIG_LOGNAME=y CONFIG_LS=y CONFIG_FEATURE_LS_FILETYPES=y CONFIG_FEATURE_LS_FOLLOWLINKS=y CONFIG_FEATURE_LS_RECURSIVE=y CONFIG_FEATURE_LS_SORTFILES=y CONFIG_FEATURE_LS_TIMESTAMPS=y CONFIG_FEATURE_LS_USERNAME=y CONFIG_FEATURE_LS_COLOR=y CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y CONFIG_MD5SUM=y CONFIG_MKDIR=y CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y CONFIG_MKFIFO=y CONFIG_MKNOD=y CONFIG_MV=y CONFIG_FEATURE_MV_LONG_OPTIONS=y CONFIG_NICE=y CONFIG_NOHUP=y CONFIG_OD=y CONFIG_PRINTENV=y CONFIG_PRINTF=y CONFIG_PWD=y CONFIG_READLINK=y CONFIG_FEATURE_READLINK_FOLLOW=y CONFIG_REALPATH=y CONFIG_RM=y CONFIG_RMDIR=y CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y CONFIG_SEQ=y CONFIG_SHA1SUM=y CONFIG_SHA256SUM=y CONFIG_SHA512SUM=y CONFIG_SLEEP=y CONFIG_FEATURE_FANCY_SLEEP=y CONFIG_FEATURE_FLOAT_SLEEP=y CONFIG_SORT=y CONFIG_FEATURE_SORT_BIG=y CONFIG_SPLIT=y CONFIG_FEATURE_SPLIT_FANCY=y CONFIG_STAT=y CONFIG_FEATURE_STAT_FORMAT=y CONFIG_STTY=y CONFIG_SUM=y CONFIG_SYNC=y CONFIG_TAC=y CONFIG_TAIL=y CONFIG_FEATURE_FANCY_TAIL=y CONFIG_TEE=y CONFIG_FEATURE_TEE_USE_BLOCK_IO=y CONFIG_TEST=y CONFIG_FEATURE_TEST_64=y CONFIG_TOUCH=y CONFIG_TR=y CONFIG_FEATURE_TR_CLASSES=y CONFIG_FEATURE_TR_EQUIV=y CONFIG_TRUE=y CONFIG_TTY=y CONFIG_UNAME=y CONFIG_UNEXPAND=y CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y CONFIG_UNIQ=y CONFIG_USLEEP=y CONFIG_UUDECODE=y CONFIG_UUENCODE=y CONFIG_WC=y CONFIG_FEATURE_WC_LARGE=y CONFIG_WHO=y CONFIG_WHOAMI=y CONFIG_YES=y # # Common options for cp and mv # CONFIG_FEATURE_PRESERVE_HARDLINKS=y # # Common options for ls, more and telnet # CONFIG_FEATURE_AUTOWIDTH=y # # Common options for df, du, ls # CONFIG_FEATURE_HUMAN_READABLE=y # # Common options for md5sum, sha1sum, sha256sum, sha512sum # CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y # # Console Utilities # CONFIG_CHVT=y CONFIG_CLEAR=y CONFIG_DEALLOCVT=y CONFIG_DUMPKMAP=y CONFIG_KBD_MODE=y CONFIG_LOADFONT=y CONFIG_LOADKMAP=y CONFIG_OPENVT=y CONFIG_RESET=y CONFIG_RESIZE=y CONFIG_FEATURE_RESIZE_PRINT=y CONFIG_SETCONSOLE=y CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS=y CONFIG_SETFONT=y CONFIG_FEATURE_SETFONT_TEXTUAL_MAP=y CONFIG_DEFAULT_SETFONT_DIR="" CONFIG_SETKEYCODES=y CONFIG_SETLOGCONS=y CONFIG_SHOWKEY=y # # Debian Utilities # CONFIG_MKTEMP=y CONFIG_PIPE_PROGRESS=y CONFIG_RUN_PARTS=y CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y CONFIG_FEATURE_RUN_PARTS_FANCY=y CONFIG_START_STOP_DAEMON=y CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS=y CONFIG_WHICH=y # # Editors # CONFIG_AWK=y CONFIG_FEATURE_AWK_LIBM=y CONFIG_CMP=y CONFIG_DIFF=y CONFIG_FEATURE_DIFF_LONG_OPTIONS=y CONFIG_FEATURE_DIFF_DIR=y CONFIG_ED=y CONFIG_PATCH=y CONFIG_SED=y CONFIG_VI=y CONFIG_FEATURE_VI_MAX_LEN=4096 CONFIG_FEATURE_VI_8BIT=y CONFIG_FEATURE_VI_COLON=y CONFIG_FEATURE_VI_YANKMARK=y CONFIG_FEATURE_VI_SEARCH=y CONFIG_FEATURE_VI_USE_SIGNALS=y CONFIG_FEATURE_VI_DOT_CMD=y CONFIG_FEATURE_VI_READONLY=y CONFIG_FEATURE_VI_SETOPTS=y CONFIG_FEATURE_VI_SET=y CONFIG_FEATURE_VI_WIN_RESIZE=y CONFIG_FEATURE_ALLOW_EXEC=y # # Finding Utilities # CONFIG_FIND=y CONFIG_FEATURE_FIND_PRINT0=y CONFIG_FEATURE_FIND_MTIME=y CONFIG_FEATURE_FIND_MMIN=y CONFIG_FEATURE_FIND_PERM=y CONFIG_FEATURE_FIND_TYPE=y CONFIG_FEATURE_FIND_XDEV=y CONFIG_FEATURE_FIND_MAXDEPTH=y CONFIG_FEATURE_FIND_NEWER=y CONFIG_FEATURE_FIND_INUM=y CONFIG_FEATURE_FIND_EXEC=y CONFIG_FEATURE_FIND_USER=y CONFIG_FEATURE_FIND_GROUP=y CONFIG_FEATURE_FIND_NOT=y CONFIG_FEATURE_FIND_DEPTH=y CONFIG_FEATURE_FIND_PAREN=y CONFIG_FEATURE_FIND_SIZE=y CONFIG_FEATURE_FIND_PRUNE=y CONFIG_FEATURE_FIND_DELETE=y CONFIG_FEATURE_FIND_PATH=y CONFIG_FEATURE_FIND_REGEX=y CONFIG_FEATURE_FIND_CONTEXT=y CONFIG_FEATURE_FIND_LINKS=y CONFIG_GREP=y CONFIG_FEATURE_GREP_EGREP_ALIAS=y CONFIG_FEATURE_GREP_FGREP_ALIAS=y CONFIG_FEATURE_GREP_CONTEXT=y CONFIG_XARGS=y CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y # # Init Utilities # CONFIG_INIT=y CONFIG_FEATURE_USE_INITTAB=y CONFIG_FEATURE_KILL_REMOVED=y CONFIG_FEATURE_KILL_DELAY=1 CONFIG_FEATURE_INIT_SCTTY=y CONFIG_FEATURE_INIT_SYSLOG=y CONFIG_FEATURE_EXTRA_QUIET=y CONFIG_FEATURE_INIT_COREDUMPS=y CONFIG_FEATURE_INITRD=y CONFIG_HALT=y # CONFIG_FEATURE_CALL_TELINIT is not set CONFIG_TELINIT_PATH="" CONFIG_MESG=y # # Login/Password Management Utilities # CONFIG_FEATURE_SHADOWPASSWDS=y CONFIG_USE_BB_PWD_GRP=y CONFIG_USE_BB_SHADOW=y CONFIG_USE_BB_CRYPT=y CONFIG_USE_BB_CRYPT_SHA=y CONFIG_ADDGROUP=y CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y CONFIG_FEATURE_ADDUSER_TO_GROUP=y CONFIG_DELGROUP=y CONFIG_FEATURE_DEL_USER_FROM_GROUP=y CONFIG_FEATURE_CHECK_NAMES=y CONFIG_ADDUSER=y CONFIG_FEATURE_ADDUSER_LONG_OPTIONS=y CONFIG_FIRST_SYSTEM_ID=100 CONFIG_LAST_SYSTEM_ID=999 CONFIG_DELUSER=y CONFIG_GETTY=y CONFIG_FEATURE_UTMP=y CONFIG_FEATURE_WTMP=y CONFIG_LOGIN=y # CONFIG_PAM is not set CONFIG_LOGIN_SCRIPTS=y CONFIG_FEATURE_NOLOGIN=y CONFIG_FEATURE_SECURETTY=y CONFIG_PASSWD=y CONFIG_FEATURE_PASSWD_WEAK_CHECK=y CONFIG_CRYPTPW=y CONFIG_CHPASSWD=y CONFIG_SU=y CONFIG_FEATURE_SU_SYSLOG=y CONFIG_FEATURE_SU_CHECKS_SHELLS=y CONFIG_SULOGIN=y CONFIG_VLOCK=y # # Linux Ext2 FS Progs # CONFIG_CHATTR=y CONFIG_FSCK=y CONFIG_LSATTR=y # # Linux Module Utilities # CONFIG_MODPROBE_SMALL=y CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE=y CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y # CONFIG_INSMOD is not set # CONFIG_RMMOD is not set # CONFIG_LSMOD is not set # CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set # CONFIG_MODPROBE is not set # CONFIG_FEATURE_MODPROBE_BLACKLIST is not set # CONFIG_DEPMOD is not set # # Options common to multiple modutils # # CONFIG_FEATURE_2_4_MODULES is not set CONFIG_FEATURE_INSMOD_TRY_MMAP=y # CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set # CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set # CONFIG_FEATURE_INSMOD_LOADINKMEM is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set # CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set # CONFIG_FEATURE_MODUTILS_ALIAS is not set # CONFIG_FEATURE_MODUTILS_SYMBOLS is not set CONFIG_DEFAULT_MODULES_DIR="/lib/modules" CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" # # Linux System Utilities # # CONFIG_ACPID is not set # CONFIG_FEATURE_ACPID_COMPAT is not set CONFIG_BLKID=y CONFIG_DMESG=y CONFIG_FEATURE_DMESG_PRETTY=y CONFIG_FBSET=y CONFIG_FEATURE_FBSET_FANCY=y CONFIG_FEATURE_FBSET_READMODE=y CONFIG_FDFLUSH=y CONFIG_FDFORMAT=y CONFIG_FDISK=y CONFIG_FDISK_SUPPORT_LARGE_DISKS=y CONFIG_FEATURE_FDISK_WRITABLE=y CONFIG_FEATURE_AIX_LABEL=y CONFIG_FEATURE_SGI_LABEL=y CONFIG_FEATURE_SUN_LABEL=y CONFIG_FEATURE_OSF_LABEL=y CONFIG_FEATURE_FDISK_ADVANCED=y CONFIG_FINDFS=y CONFIG_FREERAMDISK=y CONFIG_FSCK_MINIX=y CONFIG_MKFS_EXT2=y CONFIG_MKFS_MINIX=y # # Minix filesystem support # CONFIG_FEATURE_MINIX2=y CONFIG_MKFS_REISER=y CONFIG_MKFS_VFAT=y CONFIG_GETOPT=y CONFIG_FEATURE_GETOPT_LONG=y CONFIG_HEXDUMP=y CONFIG_FEATURE_HEXDUMP_REVERSE=y CONFIG_HD=y CONFIG_HWCLOCK=y CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS=y CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS=y CONFIG_IPCRM=y CONFIG_IPCS=y CONFIG_LOSETUP=y CONFIG_LSPCI=y CONFIG_LSUSB=y CONFIG_MDEV=y CONFIG_FEATURE_MDEV_CONF=y CONFIG_FEATURE_MDEV_RENAME=y CONFIG_FEATURE_MDEV_RENAME_REGEXP=y CONFIG_FEATURE_MDEV_EXEC=y CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y CONFIG_MKSWAP=y CONFIG_FEATURE_MKSWAP_UUID=y CONFIG_MORE=y CONFIG_FEATURE_USE_TERMIOS=y CONFIG_VOLUMEID=y CONFIG_FEATURE_VOLUMEID_EXT=y CONFIG_FEATURE_VOLUMEID_BTRFS=y CONFIG_FEATURE_VOLUMEID_REISERFS=y CONFIG_FEATURE_VOLUMEID_FAT=y CONFIG_FEATURE_VOLUMEID_HFS=y CONFIG_FEATURE_VOLUMEID_JFS=y CONFIG_FEATURE_VOLUMEID_XFS=y CONFIG_FEATURE_VOLUMEID_NTFS=y CONFIG_FEATURE_VOLUMEID_ISO9660=y CONFIG_FEATURE_VOLUMEID_UDF=y CONFIG_FEATURE_VOLUMEID_LUKS=y CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y CONFIG_FEATURE_VOLUMEID_CRAMFS=y CONFIG_FEATURE_VOLUMEID_ROMFS=y CONFIG_FEATURE_VOLUMEID_SYSV=y CONFIG_FEATURE_VOLUMEID_OCFS2=y CONFIG_FEATURE_VOLUMEID_LINUXRAID=y CONFIG_MOUNT=y CONFIG_FEATURE_MOUNT_FAKE=y CONFIG_FEATURE_MOUNT_VERBOSE=y CONFIG_FEATURE_MOUNT_HELPERS=y CONFIG_FEATURE_MOUNT_LABEL=y CONFIG_FEATURE_MOUNT_NFS=y CONFIG_FEATURE_MOUNT_CIFS=y CONFIG_FEATURE_MOUNT_FLAGS=y CONFIG_FEATURE_MOUNT_FSTAB=y CONFIG_PIVOT_ROOT=y CONFIG_RDATE=y CONFIG_RDEV=y CONFIG_READPROFILE=y CONFIG_RTCWAKE=y CONFIG_SCRIPT=y CONFIG_SCRIPTREPLAY=y CONFIG_SETARCH=y CONFIG_SWAPONOFF=y CONFIG_FEATURE_SWAPON_PRI=y CONFIG_SWITCH_ROOT=y CONFIG_UMOUNT=y CONFIG_FEATURE_UMOUNT_ALL=y # # Common options for mount/umount # CONFIG_FEATURE_MOUNT_LOOP=y # CONFIG_FEATURE_MTAB_SUPPORT is not set # # Miscellaneous Utilities # CONFIG_ADJTIMEX=y CONFIG_BBCONFIG=y CONFIG_BEEP=y CONFIG_FEATURE_BEEP_FREQ=4000 CONFIG_FEATURE_BEEP_LENGTH_MS=30 CONFIG_CHAT=y CONFIG_FEATURE_CHAT_NOFAIL=y CONFIG_FEATURE_CHAT_TTY_HIFI=y CONFIG_FEATURE_CHAT_IMPLICIT_CR=y CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y CONFIG_FEATURE_CHAT_SEND_ESCAPES=y CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y CONFIG_FEATURE_CHAT_CLR_ABORT=y CONFIG_CHRT=y CONFIG_CROND=y CONFIG_FEATURE_CROND_D=y CONFIG_FEATURE_CROND_CALL_SENDMAIL=y CONFIG_FEATURE_CROND_DIR="/var/spool/cron" CONFIG_CRONTAB=y CONFIG_DC=y CONFIG_FEATURE_DC_LIBM=y # CONFIG_DEVFSD is not set # CONFIG_DEVFSD_MODLOAD is not set # CONFIG_DEVFSD_FG_NP is not set # CONFIG_DEVFSD_VERBOSE is not set # CONFIG_FEATURE_DEVFS is not set CONFIG_DEVMEM=y CONFIG_EJECT=y CONFIG_FEATURE_EJECT_SCSI=y CONFIG_FBSPLASH=y CONFIG_FLASHCP=y # CONFIG_FLASH_LOCK is not set # CONFIG_FLASH_UNLOCK is not set # CONFIG_FLASH_ERASEALL is not set CONFIG_IONICE=y CONFIG_INOTIFYD=y CONFIG_LAST=y CONFIG_FEATURE_LAST_SMALL=y # CONFIG_FEATURE_LAST_FANCY is not set CONFIG_LESS=y CONFIG_FEATURE_LESS_MAXLINES=9999999 CONFIG_FEATURE_LESS_BRACKETS=y CONFIG_FEATURE_LESS_FLAGS=y CONFIG_FEATURE_LESS_MARKS=y CONFIG_FEATURE_LESS_REGEXP=y CONFIG_FEATURE_LESS_WINCH=y CONFIG_FEATURE_LESS_DASHCMD=y CONFIG_FEATURE_LESS_LINENUMS=y CONFIG_HDPARM=y CONFIG_FEATURE_HDPARM_GET_IDENTITY=y CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y CONFIG_MAKEDEVS=y # CONFIG_FEATURE_MAKEDEVS_LEAF is not set CONFIG_FEATURE_MAKEDEVS_TABLE=y CONFIG_MAN=y CONFIG_MICROCOM=y CONFIG_MOUNTPOINT=y CONFIG_MT=y CONFIG_RAIDAUTORUN=y CONFIG_READAHEAD=y CONFIG_RUNLEVEL=y CONFIG_RX=y CONFIG_SETSID=y CONFIG_STRINGS=y CONFIG_TASKSET=y CONFIG_FEATURE_TASKSET_FANCY=y CONFIG_TIME=y CONFIG_TIMEOUT=y CONFIG_TTYSIZE=y CONFIG_VOLNAME=y CONFIG_WALL=y CONFIG_WATCHDOG=y # # Networking Utilities # CONFIG_FEATURE_IPV6=y CONFIG_FEATURE_UNIX_LOCAL=y CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y CONFIG_VERBOSE_RESOLUTION_ERRORS=y CONFIG_ARP=y CONFIG_ARPING=y CONFIG_BRCTL=y CONFIG_FEATURE_BRCTL_FANCY=y CONFIG_FEATURE_BRCTL_SHOW=y CONFIG_DNSD=y CONFIG_ETHER_WAKE=y CONFIG_FAKEIDENTD=y CONFIG_FTPD=y CONFIG_FEATURE_FTP_WRITE=y CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y CONFIG_FTPGET=y CONFIG_FTPPUT=y CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y CONFIG_HOSTNAME=y CONFIG_HTTPD=y CONFIG_FEATURE_HTTPD_RANGES=y CONFIG_FEATURE_HTTPD_USE_SENDFILE=y CONFIG_FEATURE_HTTPD_SETUID=y CONFIG_FEATURE_HTTPD_BASIC_AUTH=y CONFIG_FEATURE_HTTPD_AUTH_MD5=y CONFIG_FEATURE_HTTPD_CGI=y CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y CONFIG_FEATURE_HTTPD_ERROR_PAGES=y CONFIG_FEATURE_HTTPD_PROXY=y CONFIG_IFCONFIG=y CONFIG_FEATURE_IFCONFIG_STATUS=y CONFIG_FEATURE_IFCONFIG_SLIP=y CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y CONFIG_FEATURE_IFCONFIG_HW=y CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y CONFIG_IFENSLAVE=y CONFIG_IFPLUGD=y CONFIG_IFUPDOWN=y CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate" CONFIG_FEATURE_IFUPDOWN_IP=y CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y # CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set CONFIG_FEATURE_IFUPDOWN_IPV4=y CONFIG_FEATURE_IFUPDOWN_IPV6=y CONFIG_FEATURE_IFUPDOWN_MAPPING=y CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP=y CONFIG_INETD=y CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO=y CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD=y CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME=y CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME=y CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN=y CONFIG_FEATURE_INETD_RPC=y CONFIG_IP=y CONFIG_FEATURE_IP_ADDRESS=y CONFIG_FEATURE_IP_LINK=y CONFIG_FEATURE_IP_ROUTE=y CONFIG_FEATURE_IP_TUNNEL=y CONFIG_FEATURE_IP_RULE=y CONFIG_FEATURE_IP_SHORT_FORMS=y CONFIG_FEATURE_IP_RARE_PROTOCOLS=y CONFIG_IPADDR=y CONFIG_IPLINK=y CONFIG_IPROUTE=y CONFIG_IPTUNNEL=y CONFIG_IPRULE=y CONFIG_IPCALC=y CONFIG_FEATURE_IPCALC_FANCY=y CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y CONFIG_NAMEIF=y CONFIG_FEATURE_NAMEIF_EXTENDED=y CONFIG_NC=y CONFIG_NC_SERVER=y CONFIG_NC_EXTRA=y CONFIG_NETSTAT=y CONFIG_FEATURE_NETSTAT_WIDE=y CONFIG_FEATURE_NETSTAT_PRG=y CONFIG_NSLOOKUP=y CONFIG_NTPD=y CONFIG_FEATURE_NTPD_SERVER=y CONFIG_PING=y CONFIG_PING6=y CONFIG_FEATURE_FANCY_PING=y CONFIG_PSCAN=y CONFIG_ROUTE=y CONFIG_SLATTACH=y CONFIG_TELNET=y CONFIG_FEATURE_TELNET_TTYPE=y CONFIG_FEATURE_TELNET_AUTOLOGIN=y CONFIG_TELNETD=y CONFIG_FEATURE_TELNETD_STANDALONE=y CONFIG_FEATURE_TELNETD_INETD_WAIT=y CONFIG_TFTP=y CONFIG_TFTPD=y CONFIG_FEATURE_TFTP_GET=y CONFIG_FEATURE_TFTP_PUT=y CONFIG_FEATURE_TFTP_BLOCKSIZE=y CONFIG_FEATURE_TFTP_PROGRESS_BAR=y CONFIG_TFTP_DEBUG=y CONFIG_TRACEROUTE=y CONFIG_TRACEROUTE6=y CONFIG_FEATURE_TRACEROUTE_VERBOSE=y CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE=y CONFIG_FEATURE_TRACEROUTE_USE_ICMP=y CONFIG_UDHCPD=y CONFIG_DHCPRELAY=y CONFIG_DUMPLEASES=y CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY=y CONFIG_DHCPD_LEASES_FILE="/var/lib/misc/udhcpd.leases" CONFIG_UDHCPC=y CONFIG_FEATURE_UDHCPC_ARPING=y CONFIG_FEATURE_UDHCP_PORT=y CONFIG_UDHCP_DEBUG=9 CONFIG_FEATURE_UDHCP_RFC3397=y CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script" CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80 CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n" CONFIG_VCONFIG=y CONFIG_WGET=y CONFIG_FEATURE_WGET_STATUSBAR=y CONFIG_FEATURE_WGET_AUTHENTICATION=y CONFIG_FEATURE_WGET_LONG_OPTIONS=y CONFIG_ZCIP=y CONFIG_TCPSVD=y CONFIG_TUNCTL=y CONFIG_FEATURE_TUNCTL_UG=y CONFIG_UDPSVD=y # # Print Utilities # CONFIG_LPD=y CONFIG_LPR=y CONFIG_LPQ=y # # Mail Utilities # CONFIG_MAKEMIME=y CONFIG_FEATURE_MIME_CHARSET="us-ascii" CONFIG_POPMAILDIR=y CONFIG_FEATURE_POPMAILDIR_DELIVERY=y CONFIG_REFORMIME=y CONFIG_FEATURE_REFORMIME_COMPAT=y CONFIG_SENDMAIL=y # # Process Utilities # CONFIG_FREE=y CONFIG_FUSER=y CONFIG_KILL=y CONFIG_KILLALL=y CONFIG_KILLALL5=y CONFIG_NMETER=y CONFIG_PGREP=y CONFIG_PIDOF=y CONFIG_FEATURE_PIDOF_SINGLE=y CONFIG_FEATURE_PIDOF_OMIT=y CONFIG_PKILL=y CONFIG_PS=y CONFIG_FEATURE_PS_WIDE=y CONFIG_FEATURE_PS_TIME=y CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS=y CONFIG_RENICE=y CONFIG_BB_SYSCTL=y CONFIG_TOP=y CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y CONFIG_FEATURE_TOP_SMP_CPU=y CONFIG_FEATURE_TOP_DECIMALS=y CONFIG_FEATURE_TOP_SMP_PROCESS=y CONFIG_FEATURE_TOPMEM=y CONFIG_FEATURE_SHOW_THREADS=y CONFIG_UPTIME=y CONFIG_WATCH=y # # Runit Utilities # CONFIG_RUNSV=y CONFIG_RUNSVDIR=y CONFIG_FEATURE_RUNSVDIR_LOG=y CONFIG_SV=y CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service" CONFIG_SVLOGD=y CONFIG_CHPST=y CONFIG_SETUIDGID=y CONFIG_ENVUIDGID=y CONFIG_ENVDIR=y CONFIG_SOFTLIMIT=y # # SELinux Utilities # CONFIG_CHCON=y CONFIG_FEATURE_CHCON_LONG_OPTIONS=y CONFIG_GETENFORCE=y CONFIG_GETSEBOOL=y CONFIG_LOAD_POLICY=y CONFIG_MATCHPATHCON=y CONFIG_RESTORECON=y CONFIG_RUNCON=y CONFIG_FEATURE_RUNCON_LONG_OPTIONS=y CONFIG_SELINUXENABLED=y CONFIG_SETENFORCE=y CONFIG_SETFILES=y CONFIG_FEATURE_SETFILES_CHECK_OPTION=y CONFIG_SETSEBOOL=y CONFIG_SESTATUS=y # # Shells # # CONFIG_FEATURE_SH_IS_ASH is not set CONFIG_FEATURE_SH_IS_HUSH=y # CONFIG_FEATURE_SH_IS_NONE is not set # CONFIG_ASH is not set # CONFIG_ASH_BASH_COMPAT is not set # CONFIG_ASH_JOB_CONTROL is not set # CONFIG_ASH_ALIAS is not set # CONFIG_ASH_GETOPTS is not set # CONFIG_ASH_BUILTIN_ECHO is not set # CONFIG_ASH_BUILTIN_PRINTF is not set # CONFIG_ASH_BUILTIN_TEST is not set # CONFIG_ASH_CMDCMD is not set # CONFIG_ASH_MAIL is not set # CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set # CONFIG_ASH_RANDOM_SUPPORT is not set # CONFIG_ASH_EXPAND_PRMT is not set CONFIG_HUSH=y CONFIG_HUSH_BASH_COMPAT=y CONFIG_HUSH_HELP=y CONFIG_HUSH_INTERACTIVE=y CONFIG_HUSH_JOB=y CONFIG_HUSH_TICK=y CONFIG_HUSH_IF=y CONFIG_HUSH_LOOPS=y CONFIG_HUSH_CASE=y CONFIG_HUSH_FUNCTIONS=y CONFIG_HUSH_LOCAL=y CONFIG_HUSH_EXPORT_N=y CONFIG_HUSH_RANDOM_SUPPORT=y CONFIG_MSH=y CONFIG_SH_MATH_SUPPORT=y CONFIG_SH_MATH_SUPPORT_64=y CONFIG_FEATURE_SH_EXTRA_QUIET=y CONFIG_FEATURE_SH_STANDALONE=y CONFIG_FEATURE_SH_NOFORK=y CONFIG_CTTYHACK=y # # System Logging Utilities # CONFIG_SYSLOGD=y CONFIG_FEATURE_ROTATE_LOGFILE=y CONFIG_FEATURE_REMOTE_LOG=y CONFIG_FEATURE_SYSLOGD_DUP=y CONFIG_FEATURE_IPC_SYSLOG=y CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16 CONFIG_LOGREAD=y CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y CONFIG_KLOGD=y CONFIG_LOGGER=y busybox-1.22.1/configs/freebsd_defconfig0000644000000000000000000006440312263563520016772 0ustar rootroot# # Automatically generated make config: don't edit # Busybox version: 1.18.1 # Tue Dec 21 19:47:40 2010 # CONFIG_HAVE_DOT_CONFIG=y # # Busybox Settings # # # General Configuration # # CONFIG_DESKTOP is not set # CONFIG_EXTRA_COMPAT is not set CONFIG_INCLUDE_SUSv2=y CONFIG_USE_PORTABLE_CODE=y # CONFIG_PLATFORM_LINUX is not set CONFIG_FEATURE_BUFFERS_USE_MALLOC=y # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set CONFIG_SHOW_USAGE=y CONFIG_FEATURE_VERBOSE_USAGE=y CONFIG_FEATURE_COMPRESS_USAGE=y CONFIG_FEATURE_INSTALLER=y # CONFIG_INSTALL_NO_USR is not set CONFIG_LOCALE_SUPPORT=y CONFIG_UNICODE_SUPPORT=y # CONFIG_UNICODE_USING_LOCALE is not set # CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set CONFIG_SUBST_WCHAR=63 CONFIG_LAST_SUPPORTED_WCHAR=767 # CONFIG_UNICODE_COMBINING_WCHARS is not set # CONFIG_UNICODE_WIDE_WCHARS is not set # CONFIG_UNICODE_BIDI_SUPPORT is not set # CONFIG_UNICODE_NEUTRAL_TABLE is not set # CONFIG_UNICODE_PRESERVE_BROKEN is not set CONFIG_LONG_OPTS=y CONFIG_FEATURE_DEVPTS=y # CONFIG_FEATURE_CLEAN_UP is not set # CONFIG_FEATURE_WTMP is not set # CONFIG_FEATURE_UTMP is not set CONFIG_FEATURE_PIDFILE=y CONFIG_FEATURE_SUID=y CONFIG_FEATURE_SUID_CONFIG=y CONFIG_FEATURE_SUID_CONFIG_QUIET=y # CONFIG_SELINUX is not set # CONFIG_FEATURE_PREFER_APPLETS is not set CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" CONFIG_FEATURE_SYSLOG=y # CONFIG_FEATURE_HAVE_RPC is not set # # Build Options # # CONFIG_STATIC is not set # CONFIG_PIE is not set # CONFIG_NOMMU is not set # CONFIG_BUILD_LIBBUSYBOX is not set # CONFIG_FEATURE_INDIVIDUAL is not set # CONFIG_FEATURE_SHARED_BUSYBOX is not set CONFIG_LFS=y CONFIG_CROSS_COMPILER_PREFIX="" CONFIG_EXTRA_CFLAGS="" # # Debugging Options # # CONFIG_DEBUG is not set # CONFIG_DEBUG_PESSIMIZE is not set # CONFIG_WERROR is not set CONFIG_NO_DEBUG_LIB=y # CONFIG_DMALLOC is not set # CONFIG_EFENCE is not set # # Installation Options ("make install" behavior) # CONFIG_INSTALL_APPLET_SYMLINKS=y # CONFIG_INSTALL_APPLET_HARDLINKS is not set # CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set # CONFIG_INSTALL_APPLET_DONT is not set # CONFIG_INSTALL_SH_APPLET_SYMLINK is not set # CONFIG_INSTALL_SH_APPLET_HARDLINK is not set # CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set CONFIG_PREFIX="./_install" # # Busybox Library Tuning # CONFIG_PASSWORD_MINLEN=6 CONFIG_MD5_SMALL=1 CONFIG_FEATURE_FAST_TOP=y # CONFIG_FEATURE_ETC_NETWORKS is not set CONFIG_FEATURE_USE_TERMIOS=y CONFIG_FEATURE_EDITING=y CONFIG_FEATURE_EDITING_MAX_LEN=1024 # CONFIG_FEATURE_EDITING_VI is not set CONFIG_FEATURE_EDITING_HISTORY=30 # CONFIG_FEATURE_EDITING_SAVEHISTORY is not set CONFIG_FEATURE_TAB_COMPLETION=y # CONFIG_FEATURE_USERNAME_COMPLETION is not set # CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set # CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set CONFIG_FEATURE_NON_POSIX_CP=y # CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set CONFIG_FEATURE_COPYBUF_KB=4 # CONFIG_MONOTONIC_SYSCALL is not set CONFIG_IOCTL_HEX2STR_ERROR=y CONFIG_FEATURE_HWIB=y # # Applets # # # Archival Utilities # CONFIG_FEATURE_SEAMLESS_XZ=y CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y CONFIG_FEATURE_SEAMLESS_Z=y CONFIG_AR=y CONFIG_FEATURE_AR_LONG_FILENAMES=y CONFIG_FEATURE_AR_CREATE=y CONFIG_BUNZIP2=y CONFIG_BZIP2=y CONFIG_CPIO=y # CONFIG_FEATURE_CPIO_O is not set # CONFIG_FEATURE_CPIO_P is not set # CONFIG_DPKG is not set # CONFIG_DPKG_DEB is not set # CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set CONFIG_GUNZIP=y CONFIG_GZIP=y CONFIG_FEATURE_GZIP_LONG_OPTIONS=y CONFIG_LZOP=y # CONFIG_LZOP_COMPR_HIGH is not set CONFIG_RPM2CPIO=y CONFIG_RPM=y CONFIG_TAR=y CONFIG_FEATURE_TAR_CREATE=y CONFIG_FEATURE_TAR_AUTODETECT=y CONFIG_FEATURE_TAR_FROM=y CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y CONFIG_FEATURE_TAR_LONG_OPTIONS=y CONFIG_FEATURE_TAR_TO_COMMAND=y # CONFIG_FEATURE_TAR_UNAME_GNAME is not set CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y # CONFIG_FEATURE_TAR_SELINUX is not set CONFIG_UNCOMPRESS=y CONFIG_UNLZMA=y CONFIG_FEATURE_LZMA_FAST=y CONFIG_LZMA=y CONFIG_UNXZ=y CONFIG_XZ=y CONFIG_UNZIP=y # # Coreutils # CONFIG_BASENAME=y CONFIG_CAT=y # CONFIG_DATE is not set # CONFIG_FEATURE_DATE_ISOFMT is not set # CONFIG_FEATURE_DATE_NANO is not set # CONFIG_FEATURE_DATE_COMPAT is not set CONFIG_TEST=y CONFIG_FEATURE_TEST_64=y CONFIG_TR=y CONFIG_FEATURE_TR_CLASSES=y CONFIG_FEATURE_TR_EQUIV=y # CONFIG_BASE64 is not set CONFIG_CAL=y CONFIG_CATV=y CONFIG_CHGRP=y CONFIG_CHMOD=y CONFIG_CHOWN=y CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y CONFIG_CHROOT=y CONFIG_CKSUM=y CONFIG_COMM=y CONFIG_CP=y CONFIG_FEATURE_CP_LONG_OPTIONS=y CONFIG_CUT=y CONFIG_DD=y CONFIG_FEATURE_DD_SIGNAL_HANDLING=y CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y CONFIG_FEATURE_DD_IBS_OBS=y # CONFIG_DF is not set # CONFIG_FEATURE_DF_FANCY is not set CONFIG_DIRNAME=y CONFIG_DOS2UNIX=y CONFIG_UNIX2DOS=y CONFIG_DU=y CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y CONFIG_ECHO=y CONFIG_FEATURE_FANCY_ECHO=y CONFIG_ENV=y CONFIG_FEATURE_ENV_LONG_OPTIONS=y CONFIG_EXPAND=y CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y CONFIG_EXPR=y CONFIG_EXPR_MATH_SUPPORT_64=y CONFIG_FALSE=y CONFIG_FOLD=y CONFIG_FSYNC=y CONFIG_HEAD=y CONFIG_FEATURE_FANCY_HEAD=y CONFIG_HOSTID=y CONFIG_ID=y CONFIG_INSTALL=y CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y CONFIG_LENGTH=y CONFIG_LN=y CONFIG_LOGNAME=y CONFIG_LS=y CONFIG_FEATURE_LS_FILETYPES=y CONFIG_FEATURE_LS_FOLLOWLINKS=y CONFIG_FEATURE_LS_RECURSIVE=y CONFIG_FEATURE_LS_SORTFILES=y CONFIG_FEATURE_LS_TIMESTAMPS=y CONFIG_FEATURE_LS_USERNAME=y CONFIG_FEATURE_LS_COLOR=y CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y CONFIG_MD5SUM=y CONFIG_MKDIR=y CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y CONFIG_MKFIFO=y # CONFIG_MKNOD is not set CONFIG_MV=y CONFIG_FEATURE_MV_LONG_OPTIONS=y CONFIG_NICE=y CONFIG_NOHUP=y CONFIG_OD=y CONFIG_PRINTENV=y CONFIG_PRINTF=y CONFIG_PWD=y CONFIG_READLINK=y CONFIG_FEATURE_READLINK_FOLLOW=y CONFIG_REALPATH=y CONFIG_RM=y CONFIG_RMDIR=y # CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set CONFIG_SEQ=y CONFIG_SHA1SUM=y CONFIG_SHA256SUM=y CONFIG_SHA512SUM=y CONFIG_SLEEP=y CONFIG_FEATURE_FANCY_SLEEP=y CONFIG_FEATURE_FLOAT_SLEEP=y CONFIG_SORT=y CONFIG_FEATURE_SORT_BIG=y CONFIG_SPLIT=y CONFIG_FEATURE_SPLIT_FANCY=y # CONFIG_STAT is not set # CONFIG_FEATURE_STAT_FORMAT is not set # CONFIG_STTY is not set CONFIG_SUM=y CONFIG_SYNC=y # CONFIG_TAC is not set CONFIG_TAIL=y CONFIG_FEATURE_FANCY_TAIL=y CONFIG_TEE=y CONFIG_FEATURE_TEE_USE_BLOCK_IO=y CONFIG_TOUCH=y CONFIG_TRUE=y CONFIG_TTY=y CONFIG_UNAME=y CONFIG_UNEXPAND=y CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y CONFIG_UNIQ=y CONFIG_USLEEP=y CONFIG_UUDECODE=y CONFIG_UUENCODE=y CONFIG_WC=y CONFIG_FEATURE_WC_LARGE=y # CONFIG_WHO is not set CONFIG_WHOAMI=y CONFIG_YES=y # # Common options for cp and mv # CONFIG_FEATURE_PRESERVE_HARDLINKS=y # # Common options for ls, more and telnet # CONFIG_FEATURE_AUTOWIDTH=y # # Common options for df, du, ls # CONFIG_FEATURE_HUMAN_READABLE=y # # Common options for md5sum, sha1sum, sha256sum, sha512sum # CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y # # Console Utilities # # CONFIG_CHVT is not set # CONFIG_FGCONSOLE is not set CONFIG_CLEAR=y # CONFIG_DEALLOCVT is not set # CONFIG_DUMPKMAP is not set # CONFIG_KBD_MODE is not set # CONFIG_LOADFONT is not set # CONFIG_LOADKMAP is not set # CONFIG_OPENVT is not set CONFIG_RESET=y CONFIG_RESIZE=y CONFIG_FEATURE_RESIZE_PRINT=y # CONFIG_SETCONSOLE is not set # CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set # CONFIG_SETFONT is not set # CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set CONFIG_DEFAULT_SETFONT_DIR="" # CONFIG_SETKEYCODES is not set # CONFIG_SETLOGCONS is not set # CONFIG_SHOWKEY is not set # CONFIG_FEATURE_LOADFONT_PSF2 is not set # CONFIG_FEATURE_LOADFONT_RAW is not set # # Debian Utilities # CONFIG_MKTEMP=y CONFIG_PIPE_PROGRESS=y CONFIG_RUN_PARTS=y CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y CONFIG_FEATURE_RUN_PARTS_FANCY=y # CONFIG_START_STOP_DAEMON is not set # CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set # CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set CONFIG_WHICH=y # # Editors # CONFIG_PATCH=y CONFIG_AWK=y CONFIG_FEATURE_AWK_LIBM=y CONFIG_CMP=y CONFIG_DIFF=y CONFIG_FEATURE_DIFF_LONG_OPTIONS=y CONFIG_FEATURE_DIFF_DIR=y CONFIG_ED=y CONFIG_SED=y CONFIG_VI=y CONFIG_FEATURE_VI_MAX_LEN=1024 CONFIG_FEATURE_VI_8BIT=y CONFIG_FEATURE_VI_COLON=y CONFIG_FEATURE_VI_YANKMARK=y CONFIG_FEATURE_VI_SEARCH=y CONFIG_FEATURE_VI_USE_SIGNALS=y CONFIG_FEATURE_VI_DOT_CMD=y CONFIG_FEATURE_VI_READONLY=y CONFIG_FEATURE_VI_SETOPTS=y CONFIG_FEATURE_VI_SET=y CONFIG_FEATURE_VI_WIN_RESIZE=y CONFIG_FEATURE_VI_ASK_TERMINAL=y CONFIG_FEATURE_ALLOW_EXEC=y # # Finding Utilities # CONFIG_FIND=y CONFIG_FEATURE_FIND_PRINT0=y CONFIG_FEATURE_FIND_MTIME=y CONFIG_FEATURE_FIND_MMIN=y CONFIG_FEATURE_FIND_PERM=y CONFIG_FEATURE_FIND_TYPE=y CONFIG_FEATURE_FIND_XDEV=y CONFIG_FEATURE_FIND_MAXDEPTH=y CONFIG_FEATURE_FIND_NEWER=y CONFIG_FEATURE_FIND_INUM=y CONFIG_FEATURE_FIND_EXEC=y CONFIG_FEATURE_FIND_USER=y CONFIG_FEATURE_FIND_GROUP=y CONFIG_FEATURE_FIND_NOT=y CONFIG_FEATURE_FIND_DEPTH=y CONFIG_FEATURE_FIND_PAREN=y CONFIG_FEATURE_FIND_SIZE=y CONFIG_FEATURE_FIND_PRUNE=y CONFIG_FEATURE_FIND_DELETE=y CONFIG_FEATURE_FIND_PATH=y CONFIG_FEATURE_FIND_REGEX=y # CONFIG_FEATURE_FIND_CONTEXT is not set CONFIG_FEATURE_FIND_LINKS=y CONFIG_GREP=y CONFIG_FEATURE_GREP_EGREP_ALIAS=y CONFIG_FEATURE_GREP_FGREP_ALIAS=y CONFIG_FEATURE_GREP_CONTEXT=y CONFIG_XARGS=y CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y # # Init Utilities # # CONFIG_BOOTCHARTD is not set # CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set # CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set # CONFIG_HALT is not set # CONFIG_FEATURE_CALL_TELINIT is not set CONFIG_TELINIT_PATH="" # CONFIG_INIT is not set # CONFIG_FEATURE_USE_INITTAB is not set # CONFIG_FEATURE_KILL_REMOVED is not set CONFIG_FEATURE_KILL_DELAY=0 # CONFIG_FEATURE_INIT_SCTTY is not set # CONFIG_FEATURE_INIT_SYSLOG is not set # CONFIG_FEATURE_EXTRA_QUIET is not set # CONFIG_FEATURE_INIT_COREDUMPS is not set # CONFIG_FEATURE_INITRD is not set CONFIG_INIT_TERMINAL_TYPE="" # CONFIG_MESG is not set # # Login/Password Management Utilities # # CONFIG_ADD_SHELL is not set # CONFIG_REMOVE_SHELL is not set # CONFIG_FEATURE_SHADOWPASSWDS is not set CONFIG_USE_BB_PWD_GRP=y # CONFIG_USE_BB_SHADOW is not set # CONFIG_USE_BB_CRYPT is not set # CONFIG_USE_BB_CRYPT_SHA is not set # CONFIG_ADDUSER is not set # CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set # CONFIG_FEATURE_CHECK_NAMES is not set CONFIG_FIRST_SYSTEM_ID=100 CONFIG_LAST_SYSTEM_ID=999 CONFIG_ADDGROUP=y CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y CONFIG_FEATURE_ADDUSER_TO_GROUP=y # CONFIG_DELUSER is not set CONFIG_DELGROUP=y CONFIG_FEATURE_DEL_USER_FROM_GROUP=y # CONFIG_GETTY is not set # CONFIG_LOGIN is not set # CONFIG_PAM is not set # CONFIG_LOGIN_SCRIPTS is not set # CONFIG_FEATURE_NOLOGIN is not set # CONFIG_FEATURE_SECURETTY is not set # CONFIG_PASSWD is not set # CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set # CONFIG_CRYPTPW is not set # CONFIG_CHPASSWD is not set # CONFIG_SU is not set # CONFIG_FEATURE_SU_SYSLOG is not set # CONFIG_FEATURE_SU_CHECKS_SHELLS is not set # CONFIG_SULOGIN is not set # CONFIG_VLOCK is not set # # Linux Ext2 FS Progs # CONFIG_CHATTR=y # CONFIG_FSCK is not set # CONFIG_LSATTR is not set # CONFIG_TUNE2FS is not set # CONFIG_MODINFO is not set # CONFIG_MODPROBE_SMALL is not set # CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set # CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set # CONFIG_INSMOD is not set # CONFIG_RMMOD is not set # CONFIG_LSMOD is not set # CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set # CONFIG_MODPROBE is not set # CONFIG_FEATURE_MODPROBE_BLACKLIST is not set # CONFIG_DEPMOD is not set # CONFIG_FEATURE_2_4_MODULES is not set # CONFIG_FEATURE_INSMOD_TRY_MMAP is not set # CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set # CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set # CONFIG_FEATURE_INSMOD_LOADINKMEM is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP is not set # CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set # CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set # CONFIG_FEATURE_MODUTILS_ALIAS is not set # CONFIG_FEATURE_MODUTILS_SYMBOLS is not set CONFIG_DEFAULT_MODULES_DIR="" CONFIG_DEFAULT_DEPMOD_FILE="" # # Linux System Utilities # # CONFIG_BLOCKDEV is not set CONFIG_REV=y # CONFIG_ACPID is not set # CONFIG_FEATURE_ACPID_COMPAT is not set # CONFIG_BLKID is not set # CONFIG_DMESG is not set # CONFIG_FEATURE_DMESG_PRETTY is not set # CONFIG_FBSET is not set # CONFIG_FEATURE_FBSET_FANCY is not set # CONFIG_FEATURE_FBSET_READMODE is not set # CONFIG_FDFLUSH is not set # CONFIG_FDFORMAT is not set # CONFIG_FDISK is not set CONFIG_FDISK_SUPPORT_LARGE_DISKS=y # CONFIG_FEATURE_FDISK_WRITABLE is not set # CONFIG_FEATURE_AIX_LABEL is not set # CONFIG_FEATURE_SGI_LABEL is not set # CONFIG_FEATURE_SUN_LABEL is not set # CONFIG_FEATURE_OSF_LABEL is not set # CONFIG_FEATURE_GPT_LABEL is not set # CONFIG_FEATURE_FDISK_ADVANCED is not set # CONFIG_FINDFS is not set CONFIG_FLOCK=y # CONFIG_FREERAMDISK is not set # CONFIG_FSCK_MINIX is not set # CONFIG_MKFS_EXT2 is not set # CONFIG_MKFS_MINIX is not set # CONFIG_FEATURE_MINIX2 is not set # CONFIG_MKFS_REISER is not set # CONFIG_MKFS_VFAT is not set CONFIG_GETOPT=y CONFIG_FEATURE_GETOPT_LONG=y CONFIG_HEXDUMP=y # CONFIG_FEATURE_HEXDUMP_REVERSE is not set CONFIG_HD=y # CONFIG_HWCLOCK is not set # CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set # CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set # CONFIG_IPCRM is not set # CONFIG_IPCS is not set # CONFIG_LOSETUP is not set CONFIG_LSPCI=y CONFIG_LSUSB=y # CONFIG_MDEV is not set # CONFIG_FEATURE_MDEV_CONF is not set # CONFIG_FEATURE_MDEV_RENAME is not set # CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set # CONFIG_FEATURE_MDEV_EXEC is not set # CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set # CONFIG_MKSWAP is not set # CONFIG_FEATURE_MKSWAP_UUID is not set CONFIG_MORE=y # CONFIG_MOUNT is not set # CONFIG_FEATURE_MOUNT_FAKE is not set # CONFIG_FEATURE_MOUNT_VERBOSE is not set # CONFIG_FEATURE_MOUNT_HELPERS is not set # CONFIG_FEATURE_MOUNT_LABEL is not set # CONFIG_FEATURE_MOUNT_NFS is not set # CONFIG_FEATURE_MOUNT_CIFS is not set # CONFIG_FEATURE_MOUNT_FLAGS is not set # CONFIG_FEATURE_MOUNT_FSTAB is not set # CONFIG_PIVOT_ROOT is not set # CONFIG_RDATE is not set # CONFIG_RDEV is not set CONFIG_READPROFILE=y # CONFIG_RTCWAKE is not set # CONFIG_SCRIPT is not set CONFIG_SCRIPTREPLAY=y # CONFIG_SETARCH is not set # CONFIG_SWAPONOFF is not set # CONFIG_FEATURE_SWAPON_PRI is not set # CONFIG_SWITCH_ROOT is not set # CONFIG_UMOUNT is not set # CONFIG_FEATURE_UMOUNT_ALL is not set # CONFIG_FEATURE_MOUNT_LOOP is not set # CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set # CONFIG_FEATURE_MTAB_SUPPORT is not set # CONFIG_VOLUMEID is not set # CONFIG_FEATURE_VOLUMEID_EXT is not set # CONFIG_FEATURE_VOLUMEID_BTRFS is not set # CONFIG_FEATURE_VOLUMEID_REISERFS is not set # CONFIG_FEATURE_VOLUMEID_FAT is not set # CONFIG_FEATURE_VOLUMEID_HFS is not set # CONFIG_FEATURE_VOLUMEID_JFS is not set # CONFIG_FEATURE_VOLUMEID_XFS is not set # CONFIG_FEATURE_VOLUMEID_NTFS is not set # CONFIG_FEATURE_VOLUMEID_ISO9660 is not set # CONFIG_FEATURE_VOLUMEID_UDF is not set # CONFIG_FEATURE_VOLUMEID_LUKS is not set # CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set # CONFIG_FEATURE_VOLUMEID_CRAMFS is not set # CONFIG_FEATURE_VOLUMEID_ROMFS is not set # CONFIG_FEATURE_VOLUMEID_SYSV is not set # CONFIG_FEATURE_VOLUMEID_OCFS2 is not set # CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set # # Miscellaneous Utilities # # CONFIG_CONSPY is not set # CONFIG_NANDWRITE is not set # CONFIG_NANDDUMP is not set # CONFIG_UBIATTACH is not set # CONFIG_UBIDETACH is not set # CONFIG_ADJTIMEX is not set # CONFIG_BBCONFIG is not set # CONFIG_FEATURE_COMPRESS_BBCONFIG is not set # CONFIG_BEEP is not set CONFIG_FEATURE_BEEP_FREQ=0 CONFIG_FEATURE_BEEP_LENGTH_MS=0 # CONFIG_CHAT is not set # CONFIG_FEATURE_CHAT_NOFAIL is not set # CONFIG_FEATURE_CHAT_TTY_HIFI is not set # CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set # CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set # CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set # CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set # CONFIG_FEATURE_CHAT_CLR_ABORT is not set CONFIG_CHRT=y # CONFIG_CROND is not set # CONFIG_FEATURE_CROND_D is not set # CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set CONFIG_FEATURE_CROND_DIR="/var/spool/cron" CONFIG_CRONTAB=y CONFIG_DC=y CONFIG_FEATURE_DC_LIBM=y # CONFIG_DEVFSD is not set # CONFIG_DEVFSD_MODLOAD is not set # CONFIG_DEVFSD_FG_NP is not set # CONFIG_DEVFSD_VERBOSE is not set # CONFIG_FEATURE_DEVFS is not set # CONFIG_DEVMEM is not set # CONFIG_EJECT is not set # CONFIG_FEATURE_EJECT_SCSI is not set # CONFIG_FBSPLASH is not set # CONFIG_FLASHCP is not set # CONFIG_FLASH_LOCK is not set # CONFIG_FLASH_UNLOCK is not set # CONFIG_FLASH_ERASEALL is not set # CONFIG_IONICE is not set # CONFIG_INOTIFYD is not set # CONFIG_LAST is not set # CONFIG_FEATURE_LAST_SMALL is not set # CONFIG_FEATURE_LAST_FANCY is not set CONFIG_LESS=y CONFIG_FEATURE_LESS_MAXLINES=9999999 CONFIG_FEATURE_LESS_BRACKETS=y CONFIG_FEATURE_LESS_FLAGS=y CONFIG_FEATURE_LESS_MARKS=y CONFIG_FEATURE_LESS_REGEXP=y # CONFIG_FEATURE_LESS_WINCH is not set # CONFIG_FEATURE_LESS_DASHCMD is not set # CONFIG_FEATURE_LESS_LINENUMS is not set # CONFIG_HDPARM is not set # CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set # CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set # CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set # CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set # CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set # CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set # CONFIG_MAKEDEVS is not set # CONFIG_FEATURE_MAKEDEVS_LEAF is not set # CONFIG_FEATURE_MAKEDEVS_TABLE is not set # CONFIG_MAN is not set CONFIG_MICROCOM=y # CONFIG_MOUNTPOINT is not set # CONFIG_MT is not set # CONFIG_RAIDAUTORUN is not set # CONFIG_READAHEAD is not set # CONFIG_RFKILL is not set # CONFIG_RUNLEVEL is not set # CONFIG_RX is not set CONFIG_SETSID=y CONFIG_STRINGS=y # CONFIG_TASKSET is not set # CONFIG_FEATURE_TASKSET_FANCY is not set # CONFIG_TIME is not set CONFIG_TIMEOUT=y CONFIG_TTYSIZE=y CONFIG_VOLNAME=y # CONFIG_WALL is not set # CONFIG_WATCHDOG is not set # # Networking Utilities # # CONFIG_NBDCLIENT is not set CONFIG_NC=y # CONFIG_NC_SERVER is not set # CONFIG_NC_EXTRA is not set # CONFIG_NC_110_COMPAT is not set # CONFIG_FEATURE_IPV6 is not set # CONFIG_FEATURE_UNIX_LOCAL is not set # CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set # CONFIG_VERBOSE_RESOLUTION_ERRORS is not set # CONFIG_ARP is not set # CONFIG_ARPING is not set # CONFIG_BRCTL is not set # CONFIG_FEATURE_BRCTL_FANCY is not set # CONFIG_FEATURE_BRCTL_SHOW is not set CONFIG_DNSD=y # CONFIG_ETHER_WAKE is not set CONFIG_FAKEIDENTD=y CONFIG_FTPD=y CONFIG_FEATURE_FTP_WRITE=y CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y CONFIG_FTPGET=y CONFIG_FTPPUT=y CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y CONFIG_HOSTNAME=y CONFIG_HTTPD=y CONFIG_FEATURE_HTTPD_RANGES=y # CONFIG_FEATURE_HTTPD_USE_SENDFILE is not set # CONFIG_FEATURE_HTTPD_SETUID is not set CONFIG_FEATURE_HTTPD_BASIC_AUTH=y CONFIG_FEATURE_HTTPD_AUTH_MD5=y CONFIG_FEATURE_HTTPD_CGI=y CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y CONFIG_FEATURE_HTTPD_ERROR_PAGES=y CONFIG_FEATURE_HTTPD_PROXY=y CONFIG_FEATURE_HTTPD_GZIP=y # CONFIG_IFCONFIG is not set # CONFIG_FEATURE_IFCONFIG_STATUS is not set # CONFIG_FEATURE_IFCONFIG_SLIP is not set # CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set # CONFIG_FEATURE_IFCONFIG_HW is not set # CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set # CONFIG_IFENSLAVE is not set # CONFIG_IFPLUGD is not set # CONFIG_IFUPDOWN is not set CONFIG_IFUPDOWN_IFSTATE_PATH="" # CONFIG_FEATURE_IFUPDOWN_IP is not set # CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN is not set # CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set # CONFIG_FEATURE_IFUPDOWN_IPV4 is not set # CONFIG_FEATURE_IFUPDOWN_IPV6 is not set # CONFIG_FEATURE_IFUPDOWN_MAPPING is not set # CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set # CONFIG_INETD is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set # CONFIG_FEATURE_INETD_RPC is not set # CONFIG_IP is not set # CONFIG_FEATURE_IP_ADDRESS is not set # CONFIG_FEATURE_IP_LINK is not set # CONFIG_FEATURE_IP_ROUTE is not set # CONFIG_FEATURE_IP_TUNNEL is not set # CONFIG_FEATURE_IP_RULE is not set # CONFIG_FEATURE_IP_SHORT_FORMS is not set # CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set # CONFIG_IPADDR is not set # CONFIG_IPLINK is not set # CONFIG_IPROUTE is not set # CONFIG_IPTUNNEL is not set # CONFIG_IPRULE is not set CONFIG_IPCALC=y CONFIG_FEATURE_IPCALC_FANCY=y CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y # CONFIG_NAMEIF is not set # CONFIG_FEATURE_NAMEIF_EXTENDED is not set # CONFIG_NETSTAT is not set # CONFIG_FEATURE_NETSTAT_WIDE is not set # CONFIG_FEATURE_NETSTAT_PRG is not set # CONFIG_NSLOOKUP is not set # CONFIG_NTPD is not set # CONFIG_FEATURE_NTPD_SERVER is not set # CONFIG_PING is not set # CONFIG_PING6 is not set # CONFIG_FEATURE_FANCY_PING is not set CONFIG_PSCAN=y # CONFIG_ROUTE is not set # CONFIG_SLATTACH is not set # CONFIG_TCPSVD is not set CONFIG_TELNET=y CONFIG_FEATURE_TELNET_TTYPE=y CONFIG_FEATURE_TELNET_AUTOLOGIN=y CONFIG_TELNETD=y CONFIG_FEATURE_TELNETD_STANDALONE=y CONFIG_FEATURE_TELNETD_INETD_WAIT=y CONFIG_TFTP=y CONFIG_TFTPD=y # # Common options for tftp/tftpd # CONFIG_FEATURE_TFTP_GET=y CONFIG_FEATURE_TFTP_PUT=y CONFIG_FEATURE_TFTP_BLOCKSIZE=y CONFIG_FEATURE_TFTP_PROGRESS_BAR=y # CONFIG_TFTP_DEBUG is not set # CONFIG_TRACEROUTE is not set # CONFIG_TRACEROUTE6 is not set # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set # CONFIG_TUNCTL is not set # CONFIG_FEATURE_TUNCTL_UG is not set # CONFIG_UDHCPD is not set # CONFIG_DHCPRELAY is not set # CONFIG_DUMPLEASES is not set # CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set CONFIG_DHCPD_LEASES_FILE="" # CONFIG_UDHCPC is not set # CONFIG_FEATURE_UDHCPC_ARPING is not set # CONFIG_FEATURE_UDHCP_PORT is not set CONFIG_UDHCP_DEBUG=0 # CONFIG_FEATURE_UDHCP_RFC3397 is not set CONFIG_UDHCPC_DEFAULT_SCRIPT="" CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0 CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="" # CONFIG_UDPSVD is not set # CONFIG_VCONFIG is not set CONFIG_WGET=y CONFIG_FEATURE_WGET_STATUSBAR=y CONFIG_FEATURE_WGET_AUTHENTICATION=y CONFIG_FEATURE_WGET_LONG_OPTIONS=y CONFIG_FEATURE_WGET_TIMEOUT=y # CONFIG_ZCIP is not set # # Print Utilities # # CONFIG_LPD is not set CONFIG_LPR=y CONFIG_LPQ=y # # Mail Utilities # # CONFIG_MAKEMIME is not set CONFIG_FEATURE_MIME_CHARSET="" # CONFIG_POPMAILDIR is not set # CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set # CONFIG_REFORMIME is not set # CONFIG_FEATURE_REFORMIME_COMPAT is not set # CONFIG_SENDMAIL is not set # # Process Utilities # CONFIG_IOSTAT=y CONFIG_MPSTAT=y CONFIG_PMAP=y CONFIG_POWERTOP=y CONFIG_SMEMCAP=y # CONFIG_FREE is not set # CONFIG_FUSER is not set CONFIG_KILL=y CONFIG_KILLALL=y CONFIG_KILLALL5=y # CONFIG_NMETER is not set CONFIG_PGREP=y # CONFIG_PIDOF is not set # CONFIG_FEATURE_PIDOF_SINGLE is not set # CONFIG_FEATURE_PIDOF_OMIT is not set CONFIG_PKILL=y CONFIG_PS=y CONFIG_FEATURE_PS_WIDE=y # CONFIG_FEATURE_PS_TIME is not set # CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set # CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set CONFIG_RENICE=y CONFIG_BB_SYSCTL=y # CONFIG_TOP is not set # CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set # CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set # CONFIG_FEATURE_TOP_SMP_CPU is not set # CONFIG_FEATURE_TOP_DECIMALS is not set # CONFIG_FEATURE_TOP_SMP_PROCESS is not set # CONFIG_FEATURE_TOPMEM is not set CONFIG_FEATURE_SHOW_THREADS=y # CONFIG_UPTIME is not set CONFIG_WATCH=y # # Runit Utilities # # CONFIG_RUNSV is not set # CONFIG_RUNSVDIR is not set # CONFIG_FEATURE_RUNSVDIR_LOG is not set # CONFIG_SV is not set CONFIG_SV_DEFAULT_SERVICE_DIR="" # CONFIG_SVLOGD is not set # CONFIG_CHPST is not set # CONFIG_SETUIDGID is not set # CONFIG_ENVUIDGID is not set # CONFIG_ENVDIR is not set # CONFIG_SOFTLIMIT is not set # CONFIG_CHCON is not set # CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set # CONFIG_GETENFORCE is not set # CONFIG_GETSEBOOL is not set # CONFIG_LOAD_POLICY is not set # CONFIG_MATCHPATHCON is not set # CONFIG_RESTORECON is not set # CONFIG_RUNCON is not set # CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set # CONFIG_SELINUXENABLED is not set # CONFIG_SETENFORCE is not set # CONFIG_SETFILES is not set # CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set # CONFIG_SETSEBOOL is not set # CONFIG_SESTATUS is not set # # Shells # CONFIG_ASH=y # CONFIG_ASH_BASH_COMPAT is not set # CONFIG_ASH_JOB_CONTROL is not set # CONFIG_ASH_ALIAS is not set # CONFIG_ASH_GETOPTS is not set # CONFIG_ASH_BUILTIN_ECHO is not set # CONFIG_ASH_BUILTIN_PRINTF is not set # CONFIG_ASH_BUILTIN_TEST is not set # CONFIG_ASH_CMDCMD is not set # CONFIG_ASH_MAIL is not set # CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set # CONFIG_ASH_RANDOM_SUPPORT is not set # CONFIG_ASH_EXPAND_PRMT is not set # CONFIG_CTTYHACK is not set # CONFIG_HUSH is not set # CONFIG_HUSH_BASH_COMPAT is not set # CONFIG_HUSH_BRACE_EXPANSION is not set # CONFIG_HUSH_HELP is not set # CONFIG_HUSH_INTERACTIVE is not set # CONFIG_HUSH_SAVEHISTORY is not set # CONFIG_HUSH_JOB is not set # CONFIG_HUSH_TICK is not set # CONFIG_HUSH_IF is not set # CONFIG_HUSH_LOOPS is not set # CONFIG_HUSH_CASE is not set # CONFIG_HUSH_FUNCTIONS is not set # CONFIG_HUSH_LOCAL is not set # CONFIG_HUSH_RANDOM_SUPPORT is not set # CONFIG_HUSH_EXPORT_N is not set # CONFIG_HUSH_MODE_X is not set # CONFIG_MSH is not set CONFIG_FEATURE_SH_IS_ASH=y # CONFIG_FEATURE_SH_IS_HUSH is not set # CONFIG_FEATURE_SH_IS_NONE is not set # CONFIG_FEATURE_BASH_IS_ASH is not set # CONFIG_FEATURE_BASH_IS_HUSH is not set CONFIG_FEATURE_BASH_IS_NONE=y CONFIG_SH_MATH_SUPPORT=y CONFIG_SH_MATH_SUPPORT_64=y # CONFIG_FEATURE_SH_EXTRA_QUIET is not set # CONFIG_FEATURE_SH_STANDALONE is not set # CONFIG_FEATURE_SH_NOFORK is not set # # System Logging Utilities # CONFIG_SYSLOGD=y CONFIG_FEATURE_ROTATE_LOGFILE=y CONFIG_FEATURE_REMOTE_LOG=y # CONFIG_FEATURE_SYSLOGD_DUP is not set CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256 CONFIG_FEATURE_IPC_SYSLOG=y CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16 CONFIG_LOGREAD=y CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y # CONFIG_KLOGD is not set # CONFIG_FEATURE_KLOGD_KLOGCTL is not set CONFIG_LOGGER=y busybox-1.22.1/include/0000755000000000000000000000000012320365364013415 5ustar rootrootbusybox-1.22.1/include/usage.src.h0000644000000000000000000000100012263563520015447 0ustar rootroot/* vi: set sw=8 ts=8: */ /* * This file suffers from chronically incorrect tabification * of messages. Before editing this file: * 1. Switch you editor to 8-space tab mode. * 2. Do not use \t in messages, use real tab character. * 3. Start each source line with message as follows: * |<7 spaces>"text with tabs".... * or * |<5 spaces>"\ntext with tabs".... */ #ifndef BB_USAGE_H #define BB_USAGE_H 1 #define NOUSAGE_STR "\b" INSERT #define busybox_notes_usage \ "Hello world!\n" #endif busybox-1.22.1/include/pwd_.h0000644000000000000000000000721412263563520014523 0ustar rootroot/* vi: set sw=4 ts=4: */ /* Copyright (C) 1991,92,95,96,97,98,99,2001 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ /* * POSIX Standard: 9.2.2 User Database Access */ #ifndef BB_PWD_H #define BB_PWD_H 1 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* This file is #included after #include * We will use libc-defined structures, but will #define function names * so that function calls are directed to bb_internal_XXX replacements */ #undef endpwent #define setpwent bb_internal_setpwent #define endpwent bb_internal_endpwent #define getpwent bb_internal_getpwent #define fgetpwent bb_internal_fgetpwent #define putpwent bb_internal_putpwent #define getpwuid bb_internal_getpwuid #define getpwnam bb_internal_getpwnam #define getpwent_r bb_internal_getpwent_r #define getpwuid_r bb_internal_getpwuid_r #define getpwnam_r bb_internal_getpwnam_r #define fgetpwent_r bb_internal_fgetpwent_r /* All function names below should be remapped by #defines above * in order to not collide with libc names. */ /* Rewind the password-file stream. */ extern void setpwent(void); /* Close the password-file stream. */ extern void endpwent(void); #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS /* Read an entry from the password-file stream, opening it if necessary. */ extern struct passwd *getpwent(void); /* Read an entry from STREAM. */ extern struct passwd *fgetpwent(FILE *__stream); /* Write the given entry onto the given stream. */ extern int putpwent(const struct passwd *__restrict __p, FILE *__restrict __f); #endif /* Search for an entry with a matching user ID. */ extern struct passwd *getpwuid(uid_t __uid); /* Search for an entry with a matching username. */ extern struct passwd *getpwnam(const char *__name); /* Reentrant versions of some of the functions above. PLEASE NOTE: the `getpwent_r' function is not (yet) standardized. The interface may change in later versions of this library. But the interface is designed following the principals used for the other reentrant functions so the chances are good this is what the POSIX people would choose. */ extern int getpwent_r(struct passwd *__restrict __resultbuf, char *__restrict __buffer, size_t __buflen, struct passwd **__restrict __result); extern int getpwuid_r(uid_t __uid, struct passwd *__restrict __resultbuf, char *__restrict __buffer, size_t __buflen, struct passwd **__restrict __result); extern int getpwnam_r(const char *__restrict __name, struct passwd *__restrict __resultbuf, char *__restrict __buffer, size_t __buflen, struct passwd **__restrict __result); /* Read an entry from STREAM. This function is not standardized and probably never will. */ extern int fgetpwent_r(FILE *__restrict __stream, struct passwd *__restrict __resultbuf, char *__restrict __buffer, size_t __buflen, struct passwd **__restrict __result); POP_SAVED_FUNCTION_VISIBILITY #endif busybox-1.22.1/include/dump.h0000644000000000000000000000326612263563520014542 0ustar rootroot/* vi: set sw=4 ts=4: */ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN #define F_IGNORE 0x01 /* %_A */ #define F_SETREP 0x02 /* rep count set, not default */ #define F_ADDRESS 0x001 /* print offset */ #define F_BPAD 0x002 /* blank pad */ #define F_C 0x004 /* %_c */ #define F_CHAR 0x008 /* %c */ #define F_DBL 0x010 /* %[EefGf] */ #define F_INT 0x020 /* %[di] */ #define F_P 0x040 /* %_p */ #define F_STR 0x080 /* %s */ #define F_U 0x100 /* %_u */ #define F_UINT 0x200 /* %[ouXx] */ #define F_TEXT 0x400 /* no conversions */ enum dump_vflag_t { ALL, DUP, FIRST, WAIT }; /* -v values */ typedef struct PR { struct PR *nextpr; /* next print unit */ unsigned flags; /* flag values */ int bcnt; /* byte count */ char *cchar; /* conversion character */ char *fmt; /* printf format */ char *nospace; /* no whitespace version */ } PR; typedef struct FU { struct FU *nextfu; /* next format unit */ struct PR *nextpr; /* next print unit */ unsigned flags; /* flag values */ int reps; /* repetition count */ int bcnt; /* byte count */ char *fmt; /* format string */ } FU; typedef struct FS { /* format strings */ struct FS *nextfs; /* linked list of format strings */ struct FU *nextfu; /* linked list of format units */ int bcnt; } FS; typedef struct dumper_t { off_t dump_skip; /* bytes to skip */ int dump_length; /* max bytes to read */ smallint dump_vflag; /*enum dump_vflag_t*/ FS *fshead; } dumper_t; dumper_t* alloc_dumper(void) FAST_FUNC; extern void bb_dump_add(dumper_t *dumper, const char *fmt) FAST_FUNC; extern int bb_dump_dump(dumper_t *dumper, char **argv) FAST_FUNC; POP_SAVED_FUNCTION_VISIBILITY busybox-1.22.1/include/applet_metadata.h0000644000000000000000000000116012263563520016711 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #ifndef APPLET_METADATA_H #define APPLET_METADATA_H 1 /* Note: can be included by both host and target builds! */ /* order matters: used as index into "install_dir[]" in appletlib.c */ typedef enum bb_install_loc_t { BB_DIR_ROOT = 0, BB_DIR_BIN, BB_DIR_SBIN, #if ENABLE_INSTALL_NO_USR BB_DIR_USR_BIN = BB_DIR_BIN, BB_DIR_USR_SBIN = BB_DIR_SBIN, #else BB_DIR_USR_BIN, BB_DIR_USR_SBIN, #endif } bb_install_loc_t; typedef enum bb_suid_t { BB_SUID_DROP = 0, BB_SUID_MAYBE, BB_SUID_REQUIRE } bb_suid_t; #endif busybox-1.22.1/include/bb_e2fs_defs.h0000644000000000000000000004777712263563520016117 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * linux/include/linux/ext2_fs.h * * Copyright (C) 1992, 1993, 1994, 1995 * Remy Card (card@masi.ibp.fr) * Laboratoire MASI - Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * Copyright (C) 1991, 1992 Linus Torvalds */ #ifndef LINUX_EXT2_FS_H #define LINUX_EXT2_FS_H 1 /* * Special inode numbers */ #define EXT2_BAD_INO 1 /* Bad blocks inode */ #define EXT2_ROOT_INO 2 /* Root inode */ #define EXT2_ACL_IDX_INO 3 /* ACL inode */ #define EXT2_ACL_DATA_INO 4 /* ACL inode */ #define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ #define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ #define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */ #define EXT2_JOURNAL_INO 8 /* Journal inode */ /* First non-reserved inode for old ext2 filesystems */ #define EXT2_GOOD_OLD_FIRST_INO 11 /* * The second extended file system magic number */ #define EXT2_SUPER_MAGIC 0xEF53 /* Assume that user mode programs are passing in an ext2fs superblock, not * a kernel struct super_block. This will allow us to call the feature-test * macros from user land. */ #define EXT2_SB(sb) (sb) /* * Maximal count of links to a file */ #define EXT2_LINK_MAX 32000 /* * Macro-instructions used to manage several block sizes */ #define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */ #define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */ #define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE) #define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE) #define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) #define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) #define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size) #define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino) #define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(uint32_t)) /* * Macro-instructions used to manage fragments */ #define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE #define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE #define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE #define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size) #define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s)) /* * ACL structures */ struct ext2_acl_header { /* Header of Access Control Lists */ uint32_t aclh_size; uint32_t aclh_file_count; uint32_t aclh_acle_count; uint32_t aclh_first_acle; }; struct ext2_acl_entry { /* Access Control List Entry */ uint32_t acle_size; uint16_t acle_perms; /* Access permissions */ uint16_t acle_type; /* Type of entry */ uint16_t acle_tag; /* User or group identity */ uint16_t acle_pad1; uint32_t acle_next; /* Pointer on next entry for the */ /* same inode or on next free entry */ }; /* * Structure of a blocks group descriptor */ struct ext2_group_desc { uint32_t bg_block_bitmap; /* Blocks bitmap block */ uint32_t bg_inode_bitmap; /* Inodes bitmap block */ uint32_t bg_inode_table; /* Inodes table block */ uint16_t bg_free_blocks_count; /* Free blocks count */ uint16_t bg_free_inodes_count; /* Free inodes count */ uint16_t bg_used_dirs_count; /* Directories count */ uint16_t bg_pad; uint32_t bg_reserved[3]; }; /* * Data structures used by the directory indexing feature * * Note: all of the multibyte integer fields are little endian. */ /* * Note: dx_root_info is laid out so that if it should somehow get * overlaid by a dirent the two low bits of the hash version will be * zero. Therefore, the hash version mod 4 should never be 0. * Sincerely, the paranoia department. */ struct ext2_dx_root_info { uint32_t reserved_zero; uint8_t hash_version; /* 0 now, 1 at release */ uint8_t info_length; /* 8 */ uint8_t indirect_levels; uint8_t unused_flags; }; #define EXT2_HASH_LEGACY 0 #define EXT2_HASH_HALF_MD4 1 #define EXT2_HASH_TEA 2 #define EXT2_HASH_FLAG_INCOMPAT 0x1 struct ext2_dx_entry { uint32_t hash; uint32_t block; }; struct ext2_dx_countlimit { uint16_t limit; uint16_t count; }; /* * Macro-instructions used to manage group descriptors */ #define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group) #define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group) #define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s)) /* limits imposed by 16-bit value gd_free_{blocks,inode}_count */ #define EXT2_MAX_BLOCKS_PER_GROUP(s) ((1 << 16) - 8) #define EXT2_MAX_INODES_PER_GROUP(s) ((1 << 16) - EXT2_INODES_PER_BLOCK(s)) #define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) /* * Constants relative to the data blocks */ #define EXT2_NDIR_BLOCKS 12 #define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS #define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) #define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) #define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) /* * Inode flags */ #define EXT2_SECRM_FL 0x00000001 /* Secure deletion */ #define EXT2_UNRM_FL 0x00000002 /* Undelete */ #define EXT2_COMPR_FL 0x00000004 /* Compress file */ #define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */ #define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ #define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ #define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ #define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ /* Reserved for compression usage... */ #define EXT2_DIRTY_FL 0x00000100 #define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ #define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */ #define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ /* End compression flags --- maybe not all used */ #define EXT2_BTREE_FL 0x00001000 /* btree format dir */ #define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */ #define EXT2_IMAGIC_FL 0x00002000 #define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */ #define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */ #define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */ #define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ #define EXT3_EXTENTS_FL 0x00080000 /* Inode uses extents */ #define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ #define EXT2_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */ #define EXT2_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */ /* * ioctl commands */ #define EXT2_IOC_GETFLAGS _IOR('f', 1, long) #define EXT2_IOC_SETFLAGS _IOW('f', 2, long) #define EXT2_IOC_GETVERSION _IOR('v', 1, long) #define EXT2_IOC_SETVERSION _IOW('v', 2, long) /* * Structure of an inode on the disk */ struct ext2_inode { uint16_t i_mode; /* File mode */ uint16_t i_uid; /* Low 16 bits of Owner Uid */ uint32_t i_size; /* Size in bytes */ uint32_t i_atime; /* Access time */ uint32_t i_ctime; /* Creation time */ uint32_t i_mtime; /* Modification time */ uint32_t i_dtime; /* Deletion Time */ uint16_t i_gid; /* Low 16 bits of Group Id */ uint16_t i_links_count; /* Links count */ uint32_t i_blocks; /* Blocks count */ uint32_t i_flags; /* File flags */ union { struct { uint32_t l_i_reserved1; } linux1; struct { uint32_t h_i_translator; } hurd1; struct { uint32_t m_i_reserved1; } masix1; } osd1; /* OS dependent 1 */ uint32_t i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ uint32_t i_generation; /* File version (for NFS) */ uint32_t i_file_acl; /* File ACL */ uint32_t i_dir_acl; /* Directory ACL */ uint32_t i_faddr; /* Fragment address */ union { struct { uint8_t l_i_frag; /* Fragment number */ uint8_t l_i_fsize; /* Fragment size */ uint16_t i_pad1; uint16_t l_i_uid_high; /* these 2 fields */ uint16_t l_i_gid_high; /* were reserved2[0] */ uint32_t l_i_reserved2; } linux2; struct { uint8_t h_i_frag; /* Fragment number */ uint8_t h_i_fsize; /* Fragment size */ uint16_t h_i_mode_high; uint16_t h_i_uid_high; uint16_t h_i_gid_high; uint32_t h_i_author; } hurd2; struct { uint8_t m_i_frag; /* Fragment number */ uint8_t m_i_fsize; /* Fragment size */ uint16_t m_pad1; uint32_t m_i_reserved2[2]; } masix2; } osd2; /* OS dependent 2 */ }; /* * Permanent part of an large inode on the disk */ struct ext2_inode_large { uint16_t i_mode; /* File mode */ uint16_t i_uid; /* Low 16 bits of Owner Uid */ uint32_t i_size; /* Size in bytes */ uint32_t i_atime; /* Access time */ uint32_t i_ctime; /* Creation time */ uint32_t i_mtime; /* Modification time */ uint32_t i_dtime; /* Deletion Time */ uint16_t i_gid; /* Low 16 bits of Group Id */ uint16_t i_links_count; /* Links count */ uint32_t i_blocks; /* Blocks count */ uint32_t i_flags; /* File flags */ union { struct { uint32_t l_i_reserved1; } linux1; struct { uint32_t h_i_translator; } hurd1; struct { uint32_t m_i_reserved1; } masix1; } osd1; /* OS dependent 1 */ uint32_t i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ uint32_t i_generation; /* File version (for NFS) */ uint32_t i_file_acl; /* File ACL */ uint32_t i_dir_acl; /* Directory ACL */ uint32_t i_faddr; /* Fragment address */ union { struct { uint8_t l_i_frag; /* Fragment number */ uint8_t l_i_fsize; /* Fragment size */ uint16_t i_pad1; uint16_t l_i_uid_high; /* these 2 fields */ uint16_t l_i_gid_high; /* were reserved2[0] */ uint32_t l_i_reserved2; } linux2; struct { uint8_t h_i_frag; /* Fragment number */ uint8_t h_i_fsize; /* Fragment size */ uint16_t h_i_mode_high; uint16_t h_i_uid_high; uint16_t h_i_gid_high; uint32_t h_i_author; } hurd2; struct { uint8_t m_i_frag; /* Fragment number */ uint8_t m_i_fsize; /* Fragment size */ uint16_t m_pad1; uint32_t m_i_reserved2[2]; } masix2; } osd2; /* OS dependent 2 */ uint16_t i_extra_isize; uint16_t i_pad1; }; #define i_size_high i_dir_acl /* * File system states */ #define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ #define EXT2_ERROR_FS 0x0002 /* Errors detected */ /* * Mount flags */ #define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */ #define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ #define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ #define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ #define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ #define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ #define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ #define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ #define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt #define set_opt(o, opt) o |= EXT2_MOUNT_##opt #define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \ EXT2_MOUNT_##opt) /* * Maximal mount counts between two filesystem checks */ #define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */ #define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */ /* * Behaviour when detecting errors */ #define EXT2_ERRORS_CONTINUE 1 /* Continue execution */ #define EXT2_ERRORS_RO 2 /* Remount fs read-only */ #define EXT2_ERRORS_PANIC 3 /* Panic */ #define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE /* * Structure of the super block */ struct ext2_super_block { uint32_t s_inodes_count; /* Inodes count */ uint32_t s_blocks_count; /* Blocks count */ uint32_t s_r_blocks_count; /* Reserved blocks count */ uint32_t s_free_blocks_count; /* Free blocks count */ uint32_t s_free_inodes_count; /* Free inodes count */ uint32_t s_first_data_block; /* First Data Block */ uint32_t s_log_block_size; /* Block size */ int32_t s_log_frag_size; /* Fragment size */ uint32_t s_blocks_per_group; /* # Blocks per group */ uint32_t s_frags_per_group; /* # Fragments per group */ uint32_t s_inodes_per_group; /* # Inodes per group */ uint32_t s_mtime; /* Mount time */ uint32_t s_wtime; /* Write time */ uint16_t s_mnt_count; /* Mount count */ int16_t s_max_mnt_count; /* Maximal mount count */ uint16_t s_magic; /* Magic signature */ uint16_t s_state; /* File system state */ uint16_t s_errors; /* Behaviour when detecting errors */ uint16_t s_minor_rev_level; /* minor revision level */ uint32_t s_lastcheck; /* time of last check */ uint32_t s_checkinterval; /* max. time between checks */ uint32_t s_creator_os; /* OS */ uint32_t s_rev_level; /* Revision level */ uint16_t s_def_resuid; /* Default uid for reserved blocks */ uint16_t s_def_resgid; /* Default gid for reserved blocks */ /* * These fields are for EXT2_DYNAMIC_REV superblocks only. * * Note: the difference between the compatible feature set and * the incompatible feature set is that if there is a bit set * in the incompatible feature set that the kernel doesn't * know about, it should refuse to mount the filesystem. * * e2fsck's requirements are more strict; if it doesn't know * about a feature in either the compatible or incompatible * feature set, it must abort and not try to meddle with * things it doesn't understand... */ uint32_t s_first_ino; /* First non-reserved inode */ uint16_t s_inode_size; /* size of inode structure */ uint16_t s_block_group_nr; /* block group # of this superblock */ uint32_t s_feature_compat; /* compatible feature set */ uint32_t s_feature_incompat; /* incompatible feature set */ uint32_t s_feature_ro_compat; /* readonly-compatible feature set */ uint8_t s_uuid[16]; /* 128-bit uuid for volume */ char s_volume_name[16]; /* volume name */ char s_last_mounted[64]; /* directory where last mounted */ uint32_t s_algorithm_usage_bitmap; /* For compression */ /* * Performance hints. Directory preallocation should only * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on. */ uint8_t s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ uint8_t s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ uint16_t s_reserved_gdt_blocks; /* Per group table for online growth */ /* * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set. */ /*D0*/ uint8_t s_journal_uuid[16]; /* uuid of journal superblock */ /*E0*/ uint32_t s_journal_inum; /* inode number of journal file */ uint32_t s_journal_dev; /* device number of journal file */ uint32_t s_last_orphan; /* start of list of inodes to delete */ uint32_t s_hash_seed[4]; /* HTREE hash seed */ uint8_t s_def_hash_version; /* Default hash version to use */ uint8_t s_jnl_backup_type; /* Default type of journal backup */ uint16_t s_reserved_word_pad; /*100*/ uint32_t s_default_mount_opts; uint32_t s_first_meta_bg; /* First metablock group */ /* ext3 additions */ uint32_t s_mkfs_time; /* When the filesystem was created */ uint32_t s_jnl_blocks[17]; /* Backup of the journal inode */ /* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */ /*150*/ uint32_t s_blocks_count_hi; /* Blocks count */ uint32_t s_r_blocks_count_hi; /* Reserved blocks count */ uint32_t s_free_blocks_count_hi; /* Free blocks count */ uint16_t s_min_extra_isize; /* All inodes have at least # bytes */ uint16_t s_want_extra_isize; /* New inodes should reserve # bytes */ uint32_t s_flags; /* Miscellaneous flags */ uint16_t s_raid_stride; /* RAID stride */ uint16_t s_mmp_interval; /* # seconds to wait in MMP checking */ uint64_t s_mmp_block; /* Block for multi-mount protection */ uint32_t s_raid_stripe_width; /* blocks on all data disks (N*stride)*/ uint8_t s_log_groups_per_flex; /* FLEX_BG group size */ uint8_t s_reserved_char_pad2; uint16_t s_reserved_pad; uint32_t s_reserved[162]; /* Padding to the end of the block */ }; struct BUG_ext2_super_block { char bug[sizeof(struct ext2_super_block) == 1024 ? 1 : -1]; }; /* * Codes for operating systems */ #define EXT2_OS_LINUX 0 #define EXT2_OS_HURD 1 #define EXT2_OS_MASIX 2 #define EXT2_OS_FREEBSD 3 #define EXT2_OS_LITES 4 /* * Revision levels */ #define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ #define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ #define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV #define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV #define EXT2_GOOD_OLD_INODE_SIZE 128 /* * Journal inode backup types */ #define EXT3_JNL_BACKUP_BLOCKS 1 /* * Feature set definitions */ #define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ ( EXT2_SB(sb)->s_feature_compat & (mask) ) #define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ ( EXT2_SB(sb)->s_feature_ro_compat & (mask) ) #define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ ( EXT2_SB(sb)->s_feature_incompat & (mask) ) /* for s_feature_compat */ #define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 #define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 #define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 #define EXT2_FEATURE_COMPAT_RESIZE_INO 0x0010 #define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 /* for s_feature_ro_compat */ #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 /* not used */ #define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 #define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 #define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 #define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 /* for s_feature_incompat */ #define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 #define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 #define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 #define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 #define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 #define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 #define EXT4_FEATURE_INCOMPAT_MMP 0x0100 #define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 #define EXT2_FEATURE_COMPAT_SUPP 0 #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ EXT2_FEATURE_RO_COMPAT_BTREE_DIR) #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ EXT2_FEATURE_INCOMPAT_META_BG) #define EXT2_FEATURE_INCOMPAT_UNSUPPORTED (~EXT2_FEATURE_INCOMPAT_SUPP) #define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED (~EXT2_FEATURE_RO_COMPAT_SUPP) #define EXT3_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ EXT2_FEATURE_RO_COMPAT_BTREE_DIR) #define EXT3_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ EXT3_FEATURE_INCOMPAT_RECOVER| \ EXT2_FEATURE_INCOMPAT_META_BG) #define EXT3_FEATURE_INCOMPAT_UNSUPPORTED (~EXT3_FEATURE_INCOMPAT_SUPP) #define EXT3_FEATURE_RO_COMPAT_UNSUPPORTED (~EXT3_FEATURE_RO_COMPAT_SUPP) /* * Default values for user and/or group using reserved blocks */ #define EXT2_DEF_RESUID 0 #define EXT2_DEF_RESGID 0 /* * Default mount options */ #define EXT2_DEFM_DEBUG 0x0001 #define EXT2_DEFM_BSDGROUPS 0x0002 #define EXT2_DEFM_XATTR_USER 0x0004 #define EXT2_DEFM_ACL 0x0008 #define EXT2_DEFM_UID16 0x0010 #define EXT3_DEFM_JMODE 0x0060 #define EXT3_DEFM_JMODE_DATA 0x0020 #define EXT3_DEFM_JMODE_ORDERED 0x0040 #define EXT3_DEFM_JMODE_WBACK 0x0060 /* * Structure of a directory entry */ #define EXT2_NAME_LEN 255 struct ext2_dir_entry { uint32_t inode; /* Inode number */ uint16_t rec_len; /* Directory entry length */ uint16_t name_len; /* Name length */ char name[EXT2_NAME_LEN]; /* File name */ }; /* * The new version of the directory entry. Since EXT2 structures are * stored in intel byte order, and the name_len field could never be * bigger than 255 chars, it's safe to reclaim the extra byte for the * file_type field. */ struct ext2_dir_entry_2 { uint32_t inode; /* Inode number */ uint16_t rec_len; /* Directory entry length */ uint8_t name_len; /* Name length */ uint8_t file_type; char name[EXT2_NAME_LEN]; /* File name */ }; /* * Ext2 directory file types. Only the low 3 bits are used. The * other bits are reserved for now. */ #define EXT2_FT_UNKNOWN 0 #define EXT2_FT_REG_FILE 1 #define EXT2_FT_DIR 2 #define EXT2_FT_CHRDEV 3 #define EXT2_FT_BLKDEV 4 #define EXT2_FT_FIFO 5 #define EXT2_FT_SOCK 6 #define EXT2_FT_SYMLINK 7 #define EXT2_FT_MAX 8 /* * EXT2_DIR_PAD defines the directory entries boundaries * * NOTE: It must be a multiple of 4 */ #define EXT2_DIR_PAD 4 #define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) #define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ ~EXT2_DIR_ROUND) #endif busybox-1.22.1/include/applets.src.h0000644000000000000000000005572712263563520016044 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * applets.h - a listing of all busybox applets. * * If you write a new applet, you need to add an entry to this list to make * busybox aware of it. */ /* name - applet name as it is typed on command line name2 - applet name, converted to C (ether-wake: name2 = ether_wake) main - corresponding _main to call (bzcat: main = bunzip2) l - location to install link to: [/usr]/[s]bin s - suid type: BB_SUID_REQUIRE: will complain if busybox isn't suid and is run by non-root (applet_main() will not be called at all) BB_SUID_DROP: will drop suid prior to applet_main() BB_SUID_MAYBE: neither of the above (every instance of BB_SUID_REQUIRE and BB_SUID_MAYBE needs to be justified in comment) NB: please update FEATURE_SUID help text whenever you add/remove BB_SUID_REQUIRE or BB_SUID_MAYBE applet. */ #if defined(PROTOTYPES) # define APPLET(name,l,s) int name##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; # define APPLET_ODDNAME(name,main,l,s,name2) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; # define APPLET_NOEXEC(name,main,l,s,name2) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; # define APPLET_NOFORK(name,main,l,s,name2) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; #elif defined(NAME_MAIN_CNAME) # define APPLET(name,l,s) name name##_main name # define APPLET_ODDNAME(name,main,l,s,name2) name main##_main name2 # define APPLET_NOEXEC(name,main,l,s,name2) name main##_main name2 # define APPLET_NOFORK(name,main,l,s,name2) name main##_main name2 #elif defined(MAKE_USAGE) && ENABLE_FEATURE_VERBOSE_USAGE # define APPLET(name,l,s) MAKE_USAGE(#name, name##_trivial_usage name##_full_usage) # define APPLET_ODDNAME(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage name2##_full_usage) # define APPLET_NOEXEC(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage name2##_full_usage) # define APPLET_NOFORK(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage name2##_full_usage) #elif defined(MAKE_USAGE) && !ENABLE_FEATURE_VERBOSE_USAGE # define APPLET(name,l,s) MAKE_USAGE(#name, name##_trivial_usage) # define APPLET_ODDNAME(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage) # define APPLET_NOEXEC(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage) # define APPLET_NOFORK(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage) #elif defined(MAKE_LINKS) # define APPLET(name,l,c) LINK l name # define APPLET_ODDNAME(name,main,l,s,name2) LINK l name # define APPLET_NOEXEC(name,main,l,s,name2) LINK l name # define APPLET_NOFORK(name,main,l,s,name2) LINK l name #elif defined(MAKE_SUID) # define APPLET(name,l,s) SUID s l name # define APPLET_ODDNAME(name,main,l,s,name2) SUID s l name # define APPLET_NOEXEC(name,main,l,s,name2) SUID s l name # define APPLET_NOFORK(name,main,l,s,name2) SUID s l name #else static struct bb_applet applets[] = { /* name, main, location, need_suid */ # define APPLET(name,l,s) { #name, #name, l, s }, # define APPLET_ODDNAME(name,main,l,s,name2) { #name, #main, l, s }, # define APPLET_NOEXEC(name,main,l,s,name2) { #name, #main, l, s, 1 }, # define APPLET_NOFORK(name,main,l,s,name2) { #name, #main, l, s, 1, 1 }, #endif #if ENABLE_INSTALL_NO_USR # define BB_DIR_USR_BIN BB_DIR_BIN # define BB_DIR_USR_SBIN BB_DIR_SBIN #endif INSERT IF_TEST(APPLET_NOFORK([, test, BB_DIR_USR_BIN, BB_SUID_DROP, test)) IF_TEST(APPLET_NOFORK([[, test, BB_DIR_USR_BIN, BB_SUID_DROP, test)) IF_ACPID(APPLET(acpid, BB_DIR_SBIN, BB_SUID_DROP)) IF_ADDGROUP(APPLET(addgroup, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_ADDUSER(APPLET(adduser, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_ADJTIMEX(APPLET(adjtimex, BB_DIR_SBIN, BB_SUID_DROP)) IF_ARP(APPLET(arp, BB_DIR_SBIN, BB_SUID_DROP)) IF_ARPING(APPLET(arping, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_BASENAME(APPLET_NOFORK(basename, basename, BB_DIR_USR_BIN, BB_SUID_DROP, basename)) IF_BBCONFIG(APPLET(bbconfig, BB_DIR_BIN, BB_SUID_DROP)) IF_BEEP(APPLET(beep, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_BLKID(APPLET(blkid, BB_DIR_SBIN, BB_SUID_DROP)) IF_BRCTL(APPLET(brctl, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_CAL(APPLET(cal, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_CAT(APPLET_NOFORK(cat, cat, BB_DIR_BIN, BB_SUID_DROP, cat)) IF_CATV(APPLET(catv, BB_DIR_BIN, BB_SUID_DROP)) IF_CHAT(APPLET(chat, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_CHATTR(APPLET(chattr, BB_DIR_BIN, BB_SUID_DROP)) IF_CHCON(APPLET(chcon, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_CHGRP(APPLET_NOEXEC(chgrp, chgrp, BB_DIR_BIN, BB_SUID_DROP, chgrp)) IF_CHMOD(APPLET_NOEXEC(chmod, chmod, BB_DIR_BIN, BB_SUID_DROP, chmod)) IF_CHOWN(APPLET_NOEXEC(chown, chown, BB_DIR_BIN, BB_SUID_DROP, chown)) IF_CHPASSWD(APPLET(chpasswd, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_CHPST(APPLET(chpst, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_CHROOT(APPLET(chroot, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_CHRT(APPLET(chrt, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_CHVT(APPLET(chvt, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_CKSUM(APPLET_NOEXEC(cksum, cksum, BB_DIR_USR_BIN, BB_SUID_DROP, cksum)) IF_CLEAR(APPLET(clear, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_COMM(APPLET(comm, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_CP(APPLET_NOEXEC(cp, cp, BB_DIR_BIN, BB_SUID_DROP, cp)) IF_CROND(APPLET(crond, BB_DIR_USR_SBIN, BB_SUID_DROP)) /* Needs to be run by root or be suid root - needs to change /var/spool/cron* files: */ IF_CRONTAB(APPLET(crontab, BB_DIR_USR_BIN, BB_SUID_REQUIRE)) IF_CRYPTPW(APPLET(cryptpw, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_CUT(APPLET_NOEXEC(cut, cut, BB_DIR_USR_BIN, BB_SUID_DROP, cut)) IF_DC(APPLET(dc, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_DD(APPLET_NOEXEC(dd, dd, BB_DIR_BIN, BB_SUID_DROP, dd)) IF_DEALLOCVT(APPLET(deallocvt, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_DELGROUP(APPLET_ODDNAME(delgroup, deluser, BB_DIR_USR_SBIN, BB_SUID_DROP, delgroup)) IF_DELUSER(APPLET(deluser, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_DEVFSD(APPLET(devfsd, BB_DIR_SBIN, BB_SUID_DROP)) IF_DEVMEM(APPLET(devmem, BB_DIR_SBIN, BB_SUID_DROP)) IF_DF(APPLET(df, BB_DIR_BIN, BB_SUID_DROP)) IF_DHCPRELAY(APPLET(dhcprelay, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_DIRNAME(APPLET_NOFORK(dirname, dirname, BB_DIR_USR_BIN, BB_SUID_DROP, dirname)) IF_DMESG(APPLET(dmesg, BB_DIR_BIN, BB_SUID_DROP)) IF_DNSD(APPLET(dnsd, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_HOSTNAME(APPLET_ODDNAME(dnsdomainname, hostname, BB_DIR_BIN, BB_SUID_DROP, dnsdomainname)) IF_DOS2UNIX(APPLET_NOEXEC(dos2unix, dos2unix, BB_DIR_USR_BIN, BB_SUID_DROP, dos2unix)) IF_DU(APPLET(du, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_DUMPKMAP(APPLET(dumpkmap, BB_DIR_BIN, BB_SUID_DROP)) IF_DUMPLEASES(APPLET(dumpleases, BB_DIR_USR_BIN, BB_SUID_DROP)) //IF_E2FSCK(APPLET(e2fsck, BB_DIR_SBIN, BB_SUID_DROP)) //IF_E2LABEL(APPLET_ODDNAME(e2label, tune2fs, BB_DIR_SBIN, BB_SUID_DROP, e2label)) IF_ECHO(APPLET_NOFORK(echo, echo, BB_DIR_BIN, BB_SUID_DROP, echo)) IF_EJECT(APPLET(eject, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_ENV(APPLET_NOEXEC(env, env, BB_DIR_USR_BIN, BB_SUID_DROP, env)) IF_ENVDIR(APPLET_ODDNAME(envdir, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, envdir)) IF_ENVUIDGID(APPLET_ODDNAME(envuidgid, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, envuidgid)) IF_ETHER_WAKE(APPLET_ODDNAME(ether-wake, ether_wake, BB_DIR_USR_SBIN, BB_SUID_DROP, ether_wake)) IF_EXPAND(APPLET(expand, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_EXPR(APPLET(expr, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_FAKEIDENTD(APPLET(fakeidentd, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_FALSE(APPLET_NOFORK(false, false, BB_DIR_BIN, BB_SUID_DROP, false)) IF_FBSET(APPLET(fbset, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_FBSPLASH(APPLET(fbsplash, BB_DIR_SBIN, BB_SUID_DROP)) IF_FDFLUSH(APPLET_ODDNAME(fdflush, freeramdisk, BB_DIR_BIN, BB_SUID_DROP, fdflush)) IF_FDFORMAT(APPLET(fdformat, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_FDISK(APPLET(fdisk, BB_DIR_SBIN, BB_SUID_DROP)) IF_FGCONSOLE(APPLET(fgconsole, BB_DIR_USR_BIN, BB_SUID_DROP)) /* Benefits from suid root: better access to /dev/BLOCKDEVs: */ IF_FINDFS(APPLET(findfs, BB_DIR_SBIN, BB_SUID_MAYBE)) IF_FLASH_ERASEALL(APPLET(flash_eraseall, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_FLASH_LOCK(APPLET_ODDNAME(flash_lock, flash_lock_unlock, BB_DIR_USR_SBIN, BB_SUID_DROP, flash_lock)) IF_FLASH_UNLOCK(APPLET_ODDNAME(flash_unlock, flash_lock_unlock, BB_DIR_USR_SBIN, BB_SUID_DROP, flash_unlock)) IF_FLASHCP(APPLET(flashcp, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_FLOCK(APPLET(flock, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_FOLD(APPLET_NOEXEC(fold, fold, BB_DIR_USR_BIN, BB_SUID_DROP, fold)) IF_FREE(APPLET(free, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_FREERAMDISK(APPLET(freeramdisk, BB_DIR_SBIN, BB_SUID_DROP)) IF_FSCK(APPLET(fsck, BB_DIR_SBIN, BB_SUID_DROP)) //IF_E2FSCK(APPLET_ODDNAME(fsck.ext2, e2fsck, BB_DIR_SBIN, BB_SUID_DROP, fsck_ext2)) //IF_E2FSCK(APPLET_ODDNAME(fsck.ext3, e2fsck, BB_DIR_SBIN, BB_SUID_DROP, fsck_ext3)) IF_FSCK_MINIX(APPLET_ODDNAME(fsck.minix, fsck_minix, BB_DIR_SBIN, BB_SUID_DROP, fsck_minix)) IF_FSYNC(APPLET_NOFORK(fsync, fsync, BB_DIR_BIN, BB_SUID_DROP, fsync)) IF_FTPD(APPLET(ftpd, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_FTPGET(APPLET_ODDNAME(ftpget, ftpgetput, BB_DIR_USR_BIN, BB_SUID_DROP, ftpget)) IF_FTPPUT(APPLET_ODDNAME(ftpput, ftpgetput, BB_DIR_USR_BIN, BB_SUID_DROP, ftpput)) IF_FUSER(APPLET(fuser, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_GETENFORCE(APPLET(getenforce, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_GETOPT(APPLET(getopt, BB_DIR_BIN, BB_SUID_DROP)) IF_GETSEBOOL(APPLET(getsebool, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_GETTY(APPLET(getty, BB_DIR_SBIN, BB_SUID_DROP)) IF_HD(APPLET_NOEXEC(hd, hexdump, BB_DIR_USR_BIN, BB_SUID_DROP, hd)) IF_HDPARM(APPLET(hdparm, BB_DIR_SBIN, BB_SUID_DROP)) IF_HEAD(APPLET_NOEXEC(head, head, BB_DIR_USR_BIN, BB_SUID_DROP, head)) IF_HEXDUMP(APPLET_NOEXEC(hexdump, hexdump, BB_DIR_USR_BIN, BB_SUID_DROP, hexdump)) IF_HOSTNAME(APPLET(hostname, BB_DIR_BIN, BB_SUID_DROP)) IF_HTTPD(APPLET(httpd, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_HWCLOCK(APPLET(hwclock, BB_DIR_SBIN, BB_SUID_DROP)) IF_IFCONFIG(APPLET(ifconfig, BB_DIR_SBIN, BB_SUID_DROP)) IF_IFUPDOWN(APPLET_ODDNAME(ifdown, ifupdown, BB_DIR_SBIN, BB_SUID_DROP, ifdown)) IF_IFENSLAVE(APPLET(ifenslave, BB_DIR_SBIN, BB_SUID_DROP)) IF_IFPLUGD(APPLET(ifplugd, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_IFUPDOWN(APPLET_ODDNAME(ifup, ifupdown, BB_DIR_SBIN, BB_SUID_DROP, ifup)) IF_INETD(APPLET(inetd, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_INOTIFYD(APPLET(inotifyd, BB_DIR_SBIN, BB_SUID_DROP)) IF_INSTALL(APPLET(install, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_IONICE(APPLET(ionice, BB_DIR_BIN, BB_SUID_DROP)) #if ENABLE_FEATURE_IP_ADDRESS \ || ENABLE_FEATURE_IP_ROUTE \ || ENABLE_FEATURE_IP_LINK \ || ENABLE_FEATURE_IP_TUNNEL \ || ENABLE_FEATURE_IP_RULE IF_IP(APPLET(ip, BB_DIR_SBIN, BB_SUID_DROP)) #endif IF_IPADDR(APPLET(ipaddr, BB_DIR_SBIN, BB_SUID_DROP)) IF_IPCALC(APPLET(ipcalc, BB_DIR_BIN, BB_SUID_DROP)) IF_IPCRM(APPLET(ipcrm, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_IPCS(APPLET(ipcs, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_IPLINK(APPLET(iplink, BB_DIR_SBIN, BB_SUID_DROP)) IF_IPROUTE(APPLET(iproute, BB_DIR_SBIN, BB_SUID_DROP)) IF_IPRULE(APPLET(iprule, BB_DIR_SBIN, BB_SUID_DROP)) IF_IPTUNNEL(APPLET(iptunnel, BB_DIR_SBIN, BB_SUID_DROP)) IF_KBD_MODE(APPLET(kbd_mode, BB_DIR_BIN, BB_SUID_DROP)) IF_KILL(APPLET(kill, BB_DIR_BIN, BB_SUID_DROP)) IF_KILLALL(APPLET_ODDNAME(killall, kill, BB_DIR_USR_BIN, BB_SUID_DROP, killall)) IF_KILLALL5(APPLET_ODDNAME(killall5, kill, BB_DIR_USR_SBIN, BB_SUID_DROP, killall5)) IF_KLOGD(APPLET(klogd, BB_DIR_SBIN, BB_SUID_DROP)) IF_LAST(APPLET(last, BB_DIR_USR_BIN, BB_SUID_DROP)) //IF_LENGTH(APPLET_NOFORK(length, length, BB_DIR_USR_BIN, BB_SUID_DROP, length)) IF_LESS(APPLET(less, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_SETARCH(APPLET_ODDNAME(linux32, setarch, BB_DIR_BIN, BB_SUID_DROP, linux32)) IF_SETARCH(APPLET_ODDNAME(linux64, setarch, BB_DIR_BIN, BB_SUID_DROP, linux64)) IF_LN(APPLET_NOEXEC(ln, ln, BB_DIR_BIN, BB_SUID_DROP, ln)) IF_LOAD_POLICY(APPLET(load_policy, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_LOADFONT(APPLET(loadfont, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_LOADKMAP(APPLET(loadkmap, BB_DIR_SBIN, BB_SUID_DROP)) IF_LOGGER(APPLET(logger, BB_DIR_USR_BIN, BB_SUID_DROP)) /* Needs to be run by root or be suid root - needs to change uid and gid: */ IF_LOGIN(APPLET(login, BB_DIR_BIN, BB_SUID_REQUIRE)) IF_LOGNAME(APPLET_NOFORK(logname, logname, BB_DIR_USR_BIN, BB_SUID_DROP, logname)) IF_LOGREAD(APPLET(logread, BB_DIR_SBIN, BB_SUID_DROP)) IF_LOSETUP(APPLET(losetup, BB_DIR_SBIN, BB_SUID_DROP)) IF_LPD(APPLET(lpd, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_LPQ(APPLET_ODDNAME(lpq, lpqr, BB_DIR_USR_BIN, BB_SUID_DROP, lpq)) IF_LPR(APPLET_ODDNAME(lpr, lpqr, BB_DIR_USR_BIN, BB_SUID_DROP, lpr)) IF_LS(APPLET_NOEXEC(ls, ls, BB_DIR_BIN, BB_SUID_DROP, ls)) IF_LSATTR(APPLET(lsattr, BB_DIR_BIN, BB_SUID_DROP)) IF_LSPCI(APPLET(lspci, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_LSUSB(APPLET(lsusb, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_MAKEDEVS(APPLET(makedevs, BB_DIR_SBIN, BB_SUID_DROP)) IF_MAKEMIME(APPLET(makemime, BB_DIR_BIN, BB_SUID_DROP)) IF_MAN(APPLET(man, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_MATCHPATHCON(APPLET(matchpathcon, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_MD5SUM(APPLET_NOEXEC(md5sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, md5sum)) IF_MICROCOM(APPLET(microcom, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, BB_DIR_BIN, BB_SUID_DROP, mkdir)) IF_MKFS_VFAT(APPLET_ODDNAME(mkdosfs, mkfs_vfat, BB_DIR_SBIN, BB_SUID_DROP, mkfs_vfat)) IF_MKFS_EXT2(APPLET_ODDNAME(mke2fs, mkfs_ext2, BB_DIR_SBIN, BB_SUID_DROP, mkfs_ext2)) IF_MKFIFO(APPLET_NOEXEC(mkfifo, mkfifo, BB_DIR_USR_BIN, BB_SUID_DROP, mkfifo)) IF_MKFS_EXT2(APPLET_ODDNAME(mkfs.ext2, mkfs_ext2, BB_DIR_SBIN, BB_SUID_DROP, mkfs_ext2)) //IF_MKE2FS(APPLET_ODDNAME(mkfs.ext3, mke2fs, BB_DIR_SBIN, BB_SUID_DROP, mkfs_ext3)) IF_MKFS_MINIX(APPLET_ODDNAME(mkfs.minix, mkfs_minix, BB_DIR_SBIN, BB_SUID_DROP, mkfs_minix)) IF_MKFS_REISER(APPLET_ODDNAME(mkfs.reiser, mkfs_reiser, BB_DIR_SBIN, BB_SUID_DROP, mkfs_reiser)) IF_MKFS_VFAT(APPLET_ODDNAME(mkfs.vfat, mkfs_vfat, BB_DIR_SBIN, BB_SUID_DROP, mkfs_vfat)) IF_MKNOD(APPLET_NOEXEC(mknod, mknod, BB_DIR_BIN, BB_SUID_DROP, mknod)) IF_CRYPTPW(APPLET_ODDNAME(mkpasswd, cryptpw, BB_DIR_USR_BIN, BB_SUID_DROP, mkpasswd)) IF_MKSWAP(APPLET(mkswap, BB_DIR_SBIN, BB_SUID_DROP)) IF_MKTEMP(APPLET(mktemp, BB_DIR_BIN, BB_SUID_DROP)) IF_MORE(APPLET(more, BB_DIR_BIN, BB_SUID_DROP)) /* On full-blown systems, requires suid for user mounts. * But it's not unthinkable to have it available in non-suid flavor on some systems, * for viewing mount table. * Therefore we use BB_SUID_MAYBE instead of BB_SUID_REQUIRE: */ IF_MOUNT(APPLET(mount, BB_DIR_BIN, IF_DESKTOP(BB_SUID_MAYBE) IF_NOT_DESKTOP(BB_SUID_DROP))) IF_MOUNTPOINT(APPLET(mountpoint, BB_DIR_BIN, BB_SUID_DROP)) IF_MT(APPLET(mt, BB_DIR_BIN, BB_SUID_DROP)) IF_MV(APPLET(mv, BB_DIR_BIN, BB_SUID_DROP)) IF_NAMEIF(APPLET(nameif, BB_DIR_SBIN, BB_SUID_DROP)) IF_NC(APPLET(nc, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_NETSTAT(APPLET(netstat, BB_DIR_BIN, BB_SUID_DROP)) IF_NICE(APPLET(nice, BB_DIR_BIN, BB_SUID_DROP)) IF_NOHUP(APPLET(nohup, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_NSLOOKUP(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_NTPD(APPLET(ntpd, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_OD(APPLET(od, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_OPENVT(APPLET(openvt, BB_DIR_USR_BIN, BB_SUID_DROP)) //IF_PARSE(APPLET(parse, BB_DIR_USR_BIN, BB_SUID_DROP)) /* Needs to be run by root or be suid root - needs to change /etc/{passwd,shadow}: */ IF_PASSWD(APPLET(passwd, BB_DIR_USR_BIN, BB_SUID_REQUIRE)) IF_PGREP(APPLET(pgrep, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_PIDOF(APPLET(pidof, BB_DIR_BIN, BB_SUID_DROP)) IF_PIPE_PROGRESS(APPLET(pipe_progress, BB_DIR_BIN, BB_SUID_DROP)) IF_PIVOT_ROOT(APPLET(pivot_root, BB_DIR_SBIN, BB_SUID_DROP)) IF_PKILL(APPLET_ODDNAME(pkill, pgrep, BB_DIR_USR_BIN, BB_SUID_DROP, pkill)) IF_POPMAILDIR(APPLET(popmaildir, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_PRINTENV(APPLET_NOFORK(printenv, printenv, BB_DIR_BIN, BB_SUID_DROP, printenv)) IF_PRINTF(APPLET_NOFORK(printf, printf, BB_DIR_USR_BIN, BB_SUID_DROP, printf)) IF_PS(APPLET(ps, BB_DIR_BIN, BB_SUID_DROP)) IF_PSCAN(APPLET(pscan, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_PWD(APPLET_NOFORK(pwd, pwd, BB_DIR_BIN, BB_SUID_DROP, pwd)) IF_RAIDAUTORUN(APPLET(raidautorun, BB_DIR_SBIN, BB_SUID_DROP)) IF_RDATE(APPLET(rdate, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_RDEV(APPLET(rdev, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_READAHEAD(APPLET(readahead, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_READLINK(APPLET(readlink, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_READPROFILE(APPLET(readprofile, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_REALPATH(APPLET(realpath, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_REFORMIME(APPLET(reformime, BB_DIR_BIN, BB_SUID_DROP)) IF_RENICE(APPLET(renice, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_RESET(APPLET(reset, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_RESIZE(APPLET(resize, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_RESTORECON(APPLET_ODDNAME(restorecon, setfiles, BB_DIR_SBIN, BB_SUID_DROP, restorecon)) IF_RM(APPLET_NOFORK(rm, rm, BB_DIR_BIN, BB_SUID_DROP, rm)) IF_RMDIR(APPLET_NOFORK(rmdir, rmdir, BB_DIR_BIN, BB_SUID_DROP, rmdir)) IF_ROUTE(APPLET(route, BB_DIR_SBIN, BB_SUID_DROP)) IF_RTCWAKE(APPLET(rtcwake, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_RUN_PARTS(APPLET_ODDNAME(run-parts, run_parts, BB_DIR_BIN, BB_SUID_DROP, run_parts)) IF_RUNCON(APPLET(runcon, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_RUNLEVEL(APPLET(runlevel, BB_DIR_SBIN, BB_SUID_DROP)) IF_RUNSV(APPLET(runsv, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_RUNSVDIR(APPLET(runsvdir, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_RX(APPLET(rx, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_SCRIPT(APPLET(script, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_SCRIPTREPLAY(APPLET(scriptreplay, BB_DIR_BIN, BB_SUID_DROP)) IF_SELINUXENABLED(APPLET(selinuxenabled, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_SENDMAIL(APPLET(sendmail, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_SEQ(APPLET_NOFORK(seq, seq, BB_DIR_USR_BIN, BB_SUID_DROP, seq)) IF_SESTATUS(APPLET(sestatus, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_SETARCH(APPLET(setarch, BB_DIR_BIN, BB_SUID_DROP)) IF_SETCONSOLE(APPLET(setconsole, BB_DIR_SBIN, BB_SUID_DROP)) IF_SETENFORCE(APPLET(setenforce, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_SETFILES(APPLET(setfiles, BB_DIR_SBIN, BB_SUID_DROP)) IF_SETFONT(APPLET(setfont, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_SETKEYCODES(APPLET(setkeycodes, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_SETLOGCONS(APPLET(setlogcons, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_SETSEBOOL(APPLET(setsebool, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_SETSID(APPLET(setsid, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_SETUIDGID(APPLET_ODDNAME(setuidgid, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, setuidgid)) IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum)) IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum)) IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum)) IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum)) IF_SHOWKEY(APPLET(showkey, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_SLATTACH(APPLET(slattach, BB_DIR_SBIN, BB_SUID_DROP)) /* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells: */ IF_SLEEP(APPLET(sleep, BB_DIR_BIN, BB_SUID_DROP)) IF_SOFTLIMIT(APPLET_ODDNAME(softlimit, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, softlimit)) IF_SORT(APPLET_NOEXEC(sort, sort, BB_DIR_USR_BIN, BB_SUID_DROP, sort)) IF_SPLIT(APPLET(split, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_START_STOP_DAEMON(APPLET_ODDNAME(start-stop-daemon, start_stop_daemon, BB_DIR_SBIN, BB_SUID_DROP, start_stop_daemon)) IF_STAT(APPLET(stat, BB_DIR_BIN, BB_SUID_DROP)) IF_STRINGS(APPLET(strings, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_STTY(APPLET(stty, BB_DIR_BIN, BB_SUID_DROP)) /* Needs to be run by root or be suid root - needs to change uid and gid: */ IF_SU(APPLET(su, BB_DIR_BIN, BB_SUID_REQUIRE)) IF_SULOGIN(APPLET(sulogin, BB_DIR_SBIN, BB_SUID_DROP)) IF_SUM(APPLET(sum, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_SV(APPLET(sv, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_SVLOGD(APPLET(svlogd, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_SWAPONOFF(APPLET_ODDNAME(swapoff, swap_on_off, BB_DIR_SBIN, BB_SUID_DROP, swapoff)) IF_SWAPONOFF(APPLET_ODDNAME(swapon, swap_on_off, BB_DIR_SBIN, BB_SUID_DROP, swapon)) IF_SWITCH_ROOT(APPLET(switch_root, BB_DIR_SBIN, BB_SUID_DROP)) IF_SYNC(APPLET_NOFORK(sync, sync, BB_DIR_BIN, BB_SUID_DROP, sync)) IF_BB_SYSCTL(APPLET(sysctl, BB_DIR_SBIN, BB_SUID_DROP)) IF_SYSLOGD(APPLET(syslogd, BB_DIR_SBIN, BB_SUID_DROP)) IF_TAC(APPLET_NOEXEC(tac, tac, BB_DIR_USR_BIN, BB_SUID_DROP, tac)) IF_TAIL(APPLET(tail, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_TASKSET(APPLET(taskset, BB_DIR_USR_BIN, BB_SUID_DROP)) /* IF_TC(APPLET(tc, BB_DIR_SBIN, BB_SUID_DROP)) */ IF_TCPSVD(APPLET_ODDNAME(tcpsvd, tcpudpsvd, BB_DIR_USR_BIN, BB_SUID_DROP, tcpsvd)) IF_TEE(APPLET(tee, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_TELNET(APPLET(telnet, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_TELNETD(APPLET(telnetd, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_TEST(APPLET_NOFORK(test, test, BB_DIR_USR_BIN, BB_SUID_DROP, test)) #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT IF_TFTP(APPLET(tftp, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_TFTPD(APPLET(tftpd, BB_DIR_USR_SBIN, BB_SUID_DROP)) #endif IF_TIME(APPLET(time, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_TIMEOUT(APPLET(timeout, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_TOP(APPLET(top, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_TR(APPLET(tr, BB_DIR_USR_BIN, BB_SUID_DROP)) /* Needs socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), therefore BB_SUID_MAYBE: */ IF_TRACEROUTE(APPLET(traceroute, BB_DIR_USR_BIN, BB_SUID_MAYBE)) IF_TRACEROUTE6(APPLET(traceroute6, BB_DIR_USR_BIN, BB_SUID_MAYBE)) IF_TRUE(APPLET_NOFORK(true, true, BB_DIR_BIN, BB_SUID_DROP, true)) IF_TTY(APPLET(tty, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_TTYSIZE(APPLET(ttysize, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_TUNCTL(APPLET(tunctl, BB_DIR_SBIN, BB_SUID_DROP)) IF_TUNE2FS(APPLET(tune2fs, BB_DIR_SBIN, BB_SUID_DROP)) IF_UDHCPC(APPLET(udhcpc, BB_DIR_SBIN, BB_SUID_DROP)) IF_UDHCPD(APPLET(udhcpd, BB_DIR_USR_SBIN, BB_SUID_DROP)) IF_UDPSVD(APPLET_ODDNAME(udpsvd, tcpudpsvd, BB_DIR_USR_BIN, BB_SUID_DROP, udpsvd)) IF_UMOUNT(APPLET(umount, BB_DIR_BIN, BB_SUID_DROP)) IF_UNAME(APPLET(uname, BB_DIR_BIN, BB_SUID_DROP)) IF_UNEXPAND(APPLET_ODDNAME(unexpand, expand, BB_DIR_USR_BIN, BB_SUID_DROP, unexpand)) IF_UNIQ(APPLET(uniq, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_UNIX2DOS(APPLET_NOEXEC(unix2dos, dos2unix, BB_DIR_USR_BIN, BB_SUID_DROP, unix2dos)) IF_UPTIME(APPLET(uptime, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_USLEEP(APPLET_NOFORK(usleep, usleep, BB_DIR_BIN, BB_SUID_DROP, usleep)) IF_UUDECODE(APPLET(uudecode, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_UUENCODE(APPLET(uuencode, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_VCONFIG(APPLET(vconfig, BB_DIR_SBIN, BB_SUID_DROP)) /* Needs to be run by root or be suid root - needs to change uid and gid: */ IF_VLOCK(APPLET(vlock, BB_DIR_USR_BIN, BB_SUID_REQUIRE)) IF_VOLNAME(APPLET(volname, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_WATCH(APPLET(watch, BB_DIR_BIN, BB_SUID_DROP)) IF_WATCHDOG(APPLET(watchdog, BB_DIR_SBIN, BB_SUID_DROP)) IF_WC(APPLET(wc, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_WGET(APPLET(wget, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_WHICH(APPLET(which, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_WHOAMI(APPLET_NOFORK(whoami, whoami, BB_DIR_USR_BIN, BB_SUID_DROP, whoami)) IF_YES(APPLET_NOFORK(yes, yes, BB_DIR_USR_BIN, BB_SUID_DROP, yes)) IF_ZCIP(APPLET(zcip, BB_DIR_SBIN, BB_SUID_DROP)) #if !defined(PROTOTYPES) && !defined(NAME_MAIN_CNAME) && !defined(MAKE_USAGE) \ && !defined(MAKE_LINKS) && !defined(MAKE_SUID) }; #endif #undef APPLET #undef APPLET_ODDNAME #undef APPLET_NOEXEC #undef APPLET_NOFORK busybox-1.22.1/include/xregex.h0000644000000000000000000000124612263563520015073 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Busybox xregcomp utility routine. This isn't in libbb.h because the * C library we're linking against may not support regex.h. * * Based in part on code from sash, Copyright (c) 1999 by David I. Bell * Permission has been granted to redistribute this code under GPL. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #ifndef BB_REGEX_H #define BB_REGEX_H 1 #include PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN char* regcomp_or_errmsg(regex_t *preg, const char *regex, int cflags) FAST_FUNC; void xregcomp(regex_t *preg, const char *regex, int cflags) FAST_FUNC; POP_SAVED_FUNCTION_VISIBILITY #endif busybox-1.22.1/include/rtc_.h0000644000000000000000000000521112263563520014514 0ustar rootroot/* * Common defines/structures/etc... for applets that need to work with the RTC. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #ifndef BB_RTC_H #define BB_RTC_H 1 #include "libbb.h" PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN int rtc_adjtime_is_utc(void) FAST_FUNC; int rtc_xopen(const char **default_rtc, int flags) FAST_FUNC; void rtc_read_tm(struct tm *ptm, int fd) FAST_FUNC; time_t rtc_tm2time(struct tm *ptm, int utc) FAST_FUNC; /* * Everything below this point has been copied from linux/rtc.h * to eliminate the kernel header dependency */ struct linux_rtc_time { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; }; struct linux_rtc_wkalrm { unsigned char enabled; /* 0 = alarm disabled, 1 = alarm enabled */ unsigned char pending; /* 0 = alarm not pending, 1 = alarm pending */ struct linux_rtc_time time; /* time the alarm is set to */ }; /* * ioctl calls that are permitted to the /dev/rtc interface, if * any of the RTC drivers are enabled. */ #define RTC_AIE_ON _IO('p', 0x01) /* Alarm int. enable on */ #define RTC_AIE_OFF _IO('p', 0x02) /* ... off */ #define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on */ #define RTC_UIE_OFF _IO('p', 0x04) /* ... off */ #define RTC_PIE_ON _IO('p', 0x05) /* Periodic int. enable on */ #define RTC_PIE_OFF _IO('p', 0x06) /* ... off */ #define RTC_WIE_ON _IO('p', 0x0f) /* Watchdog int. enable on */ #define RTC_WIE_OFF _IO('p', 0x10) /* ... off */ #define RTC_ALM_SET _IOW('p', 0x07, struct linux_rtc_time) /* Set alarm time */ #define RTC_ALM_READ _IOR('p', 0x08, struct linux_rtc_time) /* Read alarm time */ #define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time */ #define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time */ #define RTC_IRQP_READ _IOR('p', 0x0b, unsigned long) /* Read IRQ rate */ #define RTC_IRQP_SET _IOW('p', 0x0c, unsigned long) /* Set IRQ rate */ #define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */ #define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */ #define RTC_WKALM_SET _IOW('p', 0x0f, struct linux_rtc_wkalrm)/* Set wakeup alarm*/ #define RTC_WKALM_RD _IOR('p', 0x10, struct linux_rtc_wkalrm)/* Get wakeup alarm*/ /* interrupt flags */ #define RTC_IRQF 0x80 /* any of the following is active */ #define RTC_PF 0x40 #define RTC_AF 0x20 #define RTC_UF 0x10 POP_SAVED_FUNCTION_VISIBILITY #endif busybox-1.22.1/include/ar.h0000644000000000000000000000071412263563520014172 0ustar rootroot/* * busybox ar archive data structures * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #ifndef AR_H #define AR_H PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN struct ar_header { char name[16]; char date[12]; char uid[6]; char gid[6]; char mode[8]; char size[10]; char magic[2]; }; #define AR_HEADER_LEN sizeof(struct ar_header) #define AR_MAGIC "!" #define AR_MAGIC_LEN 7 POP_SAVED_FUNCTION_VISIBILITY #endif busybox-1.22.1/include/volume_id.h0000644000000000000000000000233512263563520015554 0ustar rootroot/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2005 Kay Sievers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ char *get_devname_from_label(const char *spec); char *get_devname_from_uuid(const char *spec); void display_uuid_cache(int scan_devices); /* Returns: * 0: no UUID= or LABEL= prefix found * 1: UUID= or LABEL= prefix found. In this case, * *fsname is replaced if device with such UUID or LABEL is found */ int resolve_mount_spec(char **fsname); int add_to_uuid_cache(const char *device); busybox-1.22.1/include/busybox.h0000644000000000000000000000310612263563520015261 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #ifndef BUSYBOX_H #define BUSYBOX_H 1 #include "libbb.h" /* BB_DIR_foo and BB_SUID_bar constants: */ #include "applet_metadata.h" PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* Defined in appletlib.c (by including generated applet_tables.h) */ /* Keep in sync with applets/applet_tables.c! */ extern const char applet_names[] ALIGN1; extern int (*const applet_main[])(int argc, char **argv); extern const uint16_t applet_nameofs[]; extern const uint8_t applet_install_loc[] ALIGN1; #if ENABLE_FEATURE_SUID || ENABLE_FEATURE_PREFER_APPLETS # define APPLET_NAME(i) (applet_names + (applet_nameofs[i] & 0x0fff)) #else # define APPLET_NAME(i) (applet_names + applet_nameofs[i]) #endif #if ENABLE_FEATURE_PREFER_APPLETS # define APPLET_IS_NOFORK(i) (applet_nameofs[i] & (1 << 12)) # define APPLET_IS_NOEXEC(i) (applet_nameofs[i] & (1 << 13)) #else # define APPLET_IS_NOFORK(i) 0 # define APPLET_IS_NOEXEC(i) 0 #endif #if ENABLE_FEATURE_SUID # define APPLET_SUID(i) ((applet_nameofs[i] >> 14) & 0x3) #endif #if ENABLE_FEATURE_INSTALLER #define APPLET_INSTALL_LOC(i) ({ \ unsigned v = (i); \ if (v & 1) v = applet_install_loc[v/2] >> 4; \ else v = applet_install_loc[v/2] & 0xf; \ v; }) #endif /* Length of these names has effect on size of libbusybox * and "individual" binaries. Keep them short. */ #if ENABLE_BUILD_LIBBUSYBOX #if ENABLE_FEATURE_SHARED_BUSYBOX int lbb_main(char **argv) EXTERNALLY_VISIBLE; #else int lbb_main(char **argv); #endif #endif POP_SAVED_FUNCTION_VISIBILITY #endif busybox-1.22.1/include/shadow_.h0000644000000000000000000000722712263563520015222 0ustar rootroot/* vi: set sw=4 ts=4: */ /* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ /* Declaration of types and functions for shadow password suite */ #ifndef BB_SHADOW_H #define BB_SHADOW_H 1 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* Structure of the password file */ struct spwd { char *sp_namp; /* Login name */ char *sp_pwdp; /* Encrypted password */ long sp_lstchg; /* Date of last change */ long sp_min; /* Minimum number of days between changes */ long sp_max; /* Maximum number of days between changes */ long sp_warn; /* Number of days to warn user to change the password */ long sp_inact; /* Number of days the account may be inactive */ long sp_expire; /* Number of days since 1970-01-01 until account expires */ unsigned long sp_flag; /* Reserved */ }; #define setspent bb_internal_setspent #define endspent bb_internal_endspent #define getspent bb_internal_getspent #define getspnam bb_internal_getspnam #define sgetspent bb_internal_sgetspent #define fgetspent bb_internal_fgetspent #define putspent bb_internal_putspent #define getspent_r bb_internal_getspent_r #define getspnam_r bb_internal_getspnam_r #define sgetspent_r bb_internal_sgetspent_r #define fgetspent_r bb_internal_fgetspent_r #define lckpwdf bb_internal_lckpwdf #define ulckpwdf bb_internal_ulckpwdf /* All function names below should be remapped by #defines above * in order to not collide with libc names. */ #ifdef UNUSED_FOR_NOW /* Open database for reading */ extern void setspent(void); /* Close database */ extern void endspent(void); /* Get next entry from database, perhaps after opening the file */ extern struct spwd *getspent(void); /* Get shadow entry matching NAME */ extern struct spwd *getspnam(const char *__name); /* Read shadow entry from STRING */ extern struct spwd *sgetspent(const char *__string); /* Read next shadow entry from STREAM */ extern struct spwd *fgetspent(FILE *__stream); /* Write line containing shadow password entry to stream */ extern int putspent(const struct spwd *__p, FILE *__stream); /* Reentrant versions of some of the functions above */ extern int getspent_r(struct spwd *__result_buf, char *__buffer, size_t __buflen, struct spwd **__result); #endif extern int getspnam_r(const char *__name, struct spwd *__result_buf, char *__buffer, size_t __buflen, struct spwd **__result); #ifdef UNUSED_FOR_NOW extern int sgetspent_r(const char *__string, struct spwd *__result_buf, char *__buffer, size_t __buflen, struct spwd **__result); extern int fgetspent_r(FILE *__stream, struct spwd *__result_buf, char *__buffer, size_t __buflen, struct spwd **__result); /* Protect password file against multi writers */ extern int lckpwdf(void); /* Unlock password file */ extern int ulckpwdf(void); #endif POP_SAVED_FUNCTION_VISIBILITY #endif /* shadow.h */ busybox-1.22.1/include/unicode.h0000644000000000000000000000710012263563520015212 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2, see file LICENSE in this source tree. */ #ifndef UNICODE_H #define UNICODE_H 1 #if ENABLE_UNICODE_USING_LOCALE # include # include #endif PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN enum { UNICODE_UNKNOWN = 0, UNICODE_OFF = 1, UNICODE_ON = 2, }; #define unicode_bidi_isrtl(wc) 0 #define unicode_bidi_is_neutral_wchar(wc) (wc <= 126 && !isalpha(wc)) #if !ENABLE_UNICODE_SUPPORT # define unicode_strlen(string) strlen(string) # define unicode_strwidth(string) strlen(string) # define unicode_status UNICODE_OFF # define init_unicode() ((void)0) # define reinit_unicode(LANG) ((void)0) #else # if CONFIG_LAST_SUPPORTED_WCHAR < 126 || CONFIG_LAST_SUPPORTED_WCHAR >= 0x30000 # undef CONFIG_LAST_SUPPORTED_WCHAR # define CONFIG_LAST_SUPPORTED_WCHAR 0x2ffff # endif # if CONFIG_LAST_SUPPORTED_WCHAR < 0x300 # undef ENABLE_UNICODE_COMBINING_WCHARS # define ENABLE_UNICODE_COMBINING_WCHARS 0 # endif # if CONFIG_LAST_SUPPORTED_WCHAR < 0x1100 # undef ENABLE_UNICODE_WIDE_WCHARS # define ENABLE_UNICODE_WIDE_WCHARS 0 # endif # if CONFIG_LAST_SUPPORTED_WCHAR < 0x590 # undef ENABLE_UNICODE_BIDI_SUPPORT # define ENABLE_UNICODE_BIDI_SUPPORT 0 # endif /* Number of unicode chars. Falls back to strlen() on invalid unicode */ size_t FAST_FUNC unicode_strlen(const char *string); /* Width on terminal */ size_t FAST_FUNC unicode_strwidth(const char *string); enum { UNI_FLAG_PAD = (1 << 0), }; //UNUSED: unsigned FAST_FUNC unicode_padding_to_width(unsigned width, const char *src); //UNUSED: char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char *src, unsigned width, int flags); char* FAST_FUNC unicode_conv_to_printable(uni_stat_t *stats, const char *src); //UNUSED: char* FAST_FUNC unicode_conv_to_printable_maxwidth(uni_stat_t *stats, const char *src, unsigned maxwidth); char* FAST_FUNC unicode_conv_to_printable_fixedwidth(/*uni_stat_t *stats,*/ const char *src, unsigned width); # if ENABLE_UNICODE_USING_LOCALE extern uint8_t unicode_status; void init_unicode(void) FAST_FUNC; void reinit_unicode(const char *LANG) FAST_FUNC; # else /* Homegrown Unicode support. It knows only C and Unicode locales. */ # if !ENABLE_FEATURE_CHECK_UNICODE_IN_ENV # define unicode_status UNICODE_ON # define init_unicode() ((void)0) # define reinit_unicode(LANG) ((void)0) # else extern uint8_t unicode_status; void init_unicode(void) FAST_FUNC; void reinit_unicode(const char *LANG) FAST_FUNC; # endif # undef MB_CUR_MAX # define MB_CUR_MAX 6 /* Prevent name collisions */ # define wint_t bb_wint_t # define mbstate_t bb_mbstate_t # define mbstowcs bb_mbstowcs # define wcstombs bb_wcstombs # define wcrtomb bb_wcrtomb # define iswspace bb_iswspace # define iswalnum bb_iswalnum # define iswpunct bb_iswpunct # define wcwidth bb_wcwidth typedef int32_t wint_t; typedef struct { char bogus; } mbstate_t; size_t mbstowcs(wchar_t *dest, const char *src, size_t n) FAST_FUNC; size_t wcstombs(char *dest, const wchar_t *src, size_t n) FAST_FUNC; size_t wcrtomb(char *s, wchar_t wc, mbstate_t *ps) FAST_FUNC; int iswspace(wint_t wc) FAST_FUNC; int iswalnum(wint_t wc) FAST_FUNC; int iswpunct(wint_t wc) FAST_FUNC; int wcwidth(unsigned ucs) FAST_FUNC; # if ENABLE_UNICODE_BIDI_SUPPORT # undef unicode_bidi_isrtl int unicode_bidi_isrtl(wint_t wc) FAST_FUNC; # if ENABLE_UNICODE_NEUTRAL_TABLE # undef unicode_bidi_is_neutral_wchar int unicode_bidi_is_neutral_wchar(wint_t wc) FAST_FUNC; # endif # endif # endif /* !UNICODE_USING_LOCALE */ #endif /* UNICODE_SUPPORT */ POP_SAVED_FUNCTION_VISIBILITY #endif busybox-1.22.1/include/inet_common.h0000644000000000000000000000164112263563520016077 0ustar rootroot/* 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 * */ #ifndef INET_COMMON_H #define INET_COMMON_H 1 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* hostfirst!=0 If we expect this to be a hostname, try hostname database first */ int INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst) FAST_FUNC; /* numeric: & 0x8000: "default" instead of "*", * & 0x4000: host instead of net, * & 0x0fff: don't resolve */ int INET6_resolve(const char *name, struct sockaddr_in6 *sin6) FAST_FUNC; /* These return malloced string */ char *INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t netmask) FAST_FUNC; char *INET6_rresolve(struct sockaddr_in6 *sin6, int numeric) FAST_FUNC; POP_SAVED_FUNCTION_VISIBILITY #endif busybox-1.22.1/include/libbb.h0000644000000000000000000022256212263563520014651 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Busybox main internal header file * * Based in part on code from sash, Copyright (c) 1999 by David I. Bell * Permission has been granted to redistribute this code under GPL. * * Licensed under GPLv2, see file LICENSE in this source tree. */ #ifndef LIBBB_H #define LIBBB_H 1 #include "platform.h" #include #include #include #include #include #include #include #include #if defined __UCLIBC__ /* TODO: and glibc? */ /* use inlined versions of these: */ # define sigfillset(s) __sigfillset(s) # define sigemptyset(s) __sigemptyset(s) # define sigisemptyset(s) __sigisemptyset(s) #endif #include #include #include #include #include #include /* There are two incompatible basename's, let's not use them! */ /* See the dirname/basename man page for details */ #include /* dirname,basename */ #undef basename #define basename dont_use_basename #include #include #include #include #include #include #include #ifndef major # include #endif #include #include #include #include #include #include #if ENABLE_FEATURE_SHADOWPASSWDS # if !ENABLE_USE_BB_SHADOW /* If using busybox's shadow implementation, do not include the shadow.h * header as the toolchain may not provide it at all. */ # include # endif #endif #if defined(ANDROID) || defined(__ANDROID__) # define endpwent() ((void)0) # define endgrent() ((void)0) #endif #ifdef HAVE_MNTENT_H # include #endif #ifdef HAVE_SYS_STATFS_H # include #endif /* Don't do this here: * #include * Some linux/ includes pull in conflicting definition * of struct sysinfo (only in some toolchanins), which breaks build. * Include sys/sysinfo.h only in those files which need it. */ #if ENABLE_SELINUX # include # include # include # include #endif #if ENABLE_FEATURE_UTMP # include #endif #if ENABLE_LOCALE_SUPPORT # include #else # define setlocale(x,y) ((void)0) #endif #ifdef DMALLOC # include #endif /* Just in case libc doesn't define some of these... */ #ifndef _PATH_PASSWD #define _PATH_PASSWD "/etc/passwd" #endif #ifndef _PATH_GROUP #define _PATH_GROUP "/etc/group" #endif #ifndef _PATH_SHADOW #define _PATH_SHADOW "/etc/shadow" #endif #ifndef _PATH_GSHADOW #define _PATH_GSHADOW "/etc/gshadow" #endif #if defined __FreeBSD__ || defined __OpenBSD__ # include # include #elif defined __APPLE__ # include #else # include # if !defined(__socklen_t_defined) && !defined(_SOCKLEN_T_DECLARED) /* We #define socklen_t *after* includes, otherwise we get * typedef redefinition errors from system headers * (in case "is it defined already" detection above failed) */ # define socklen_t bb_socklen_t typedef unsigned socklen_t; # endif #endif #ifndef HAVE_CLEARENV # define clearenv() do { if (environ) environ[0] = NULL; } while (0) #endif #ifndef HAVE_FDATASYNC # define fdatasync fsync #endif #ifndef HAVE_XTABS # define XTABS TAB3 #endif /* Some libc's forget to declare these, do it ourself */ extern char **environ; #if defined(__GLIBC__) && __GLIBC__ < 2 int vdprintf(int d, const char *format, va_list ap); #endif /* klogctl is in libc's klog.h, but we cheat and not #include that */ int klogctl(int type, char *b, int len); #ifndef PATH_MAX # define PATH_MAX 256 #endif #ifndef BUFSIZ # define BUFSIZ 4096 #endif /* Busybox does not use threads, we can speed up stdio. */ #ifdef HAVE_UNLOCKED_STDIO # undef getc # define getc(stream) getc_unlocked(stream) # undef getchar # define getchar() getchar_unlocked() # undef putc # define putc(c, stream) putc_unlocked(c, stream) # undef putchar # define putchar(c) putchar_unlocked(c) # undef fgetc # define fgetc(stream) getc_unlocked(stream) # undef fputc # define fputc(c, stream) putc_unlocked(c, stream) #endif /* Above functions are required by POSIX.1-2008, below ones are extensions */ #ifdef HAVE_UNLOCKED_LINE_OPS # undef fgets # define fgets(s, n, stream) fgets_unlocked(s, n, stream) # undef fputs # define fputs(s, stream) fputs_unlocked(s, stream) #endif /* Make all declarations hidden (-fvisibility flag only affects definitions) */ /* (don't include system headers after this until corresponding pop!) */ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN #if ENABLE_USE_BB_PWD_GRP # include "pwd_.h" # include "grp_.h" #endif #if ENABLE_FEATURE_SHADOWPASSWDS # if ENABLE_USE_BB_SHADOW # include "shadow_.h" # endif #endif /* Tested to work correctly with all int types (IIRC :]) */ #define MAXINT(T) (T)( \ ((T)-1) > 0 \ ? (T)-1 \ : (T)~((T)1 << (sizeof(T)*8-1)) \ ) #define MININT(T) (T)( \ ((T)-1) > 0 \ ? (T)0 \ : ((T)1 << (sizeof(T)*8-1)) \ ) /* Large file support */ /* Note that CONFIG_LFS=y forces bbox to be built with all common ops * (stat, lseek etc) mapped to "largefile" variants by libc. * Practically it means that open() automatically has O_LARGEFILE added * and all filesize/file_offset parameters and struct members are "large" * (in today's world - signed 64bit). For full support of large files, * we need a few helper #defines (below) and careful use of off_t * instead of int/ssize_t. No lseek64(), O_LARGEFILE etc necessary */ #if ENABLE_LFS /* CONFIG_LFS is on */ # if ULONG_MAX > 0xffffffff /* "long" is long enough on this system */ typedef unsigned long uoff_t; # define XATOOFF(a) xatoul_range((a), 0, LONG_MAX) /* usage: sz = BB_STRTOOFF(s, NULL, 10); if (errno || sz < 0) die(); */ # define BB_STRTOOFF bb_strtoul # define STRTOOFF strtoul /* usage: printf("size: %"OFF_FMT"d (%"OFF_FMT"x)\n", sz, sz); */ # define OFF_FMT "l" # else /* "long" is too short, need "long long" */ typedef unsigned long long uoff_t; # define XATOOFF(a) xatoull_range((a), 0, LLONG_MAX) # define BB_STRTOOFF bb_strtoull # define STRTOOFF strtoull # define OFF_FMT "ll" # endif #else /* CONFIG_LFS is off */ # if UINT_MAX == 0xffffffff /* While sizeof(off_t) == sizeof(int), off_t is typedef'ed to long anyway. * gcc will throw warnings on printf("%d", off_t). Crap... */ typedef unsigned long uoff_t; # define XATOOFF(a) xatoi_positive(a) # define BB_STRTOOFF bb_strtou # define STRTOOFF strtol # define OFF_FMT "l" # else typedef unsigned long uoff_t; # define XATOOFF(a) xatoul_range((a), 0, LONG_MAX) # define BB_STRTOOFF bb_strtoul # define STRTOOFF strtol # define OFF_FMT "l" # endif #endif /* scary. better ideas? (but do *test* them first!) */ #define OFF_T_MAX ((off_t)~((off_t)1 << (sizeof(off_t)*8-1))) /* Users report bionic to use 32-bit off_t even if LARGEFILE support is requested. * We misdetected that. Don't let it build: */ struct BUG_off_t_size_is_misdetected { char BUG_off_t_size_is_misdetected[sizeof(off_t) == sizeof(uoff_t) ? 1 : -1]; }; /* Some useful definitions */ #undef FALSE #define FALSE ((int) 0) #undef TRUE #define TRUE ((int) 1) #undef SKIP #define SKIP ((int) 2) /* Macros for min/max. */ #ifndef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #endif #ifndef MAX #define MAX(a,b) (((a)>(b))?(a):(b)) #endif /* buffer allocation schemes */ #if ENABLE_FEATURE_BUFFERS_GO_ON_STACK #define RESERVE_CONFIG_BUFFER(buffer,len) char buffer[len] #define RESERVE_CONFIG_UBUFFER(buffer,len) unsigned char buffer[len] #define RELEASE_CONFIG_BUFFER(buffer) ((void)0) #else #if ENABLE_FEATURE_BUFFERS_GO_IN_BSS #define RESERVE_CONFIG_BUFFER(buffer,len) static char buffer[len] #define RESERVE_CONFIG_UBUFFER(buffer,len) static unsigned char buffer[len] #define RELEASE_CONFIG_BUFFER(buffer) ((void)0) #else #define RESERVE_CONFIG_BUFFER(buffer,len) char *buffer = xmalloc(len) #define RESERVE_CONFIG_UBUFFER(buffer,len) unsigned char *buffer = xmalloc(len) #define RELEASE_CONFIG_BUFFER(buffer) free(buffer) #endif #endif #if defined(__GLIBC__) /* glibc uses __errno_location() to get a ptr to errno */ /* We can just memorize it once - no multithreading in busybox :) */ extern int *const bb_errno; #undef errno #define errno (*bb_errno) #endif #if !(ULONG_MAX > 0xffffffff) /* Only 32-bit CPUs need this, 64-bit ones use inlined version */ uint64_t bb_bswap_64(uint64_t x) FAST_FUNC; #endif unsigned long long monotonic_ns(void) FAST_FUNC; unsigned long long monotonic_us(void) FAST_FUNC; unsigned long long monotonic_ms(void) FAST_FUNC; unsigned monotonic_sec(void) FAST_FUNC; extern void chomp(char *s) FAST_FUNC; extern void trim(char *s) FAST_FUNC; extern char *skip_whitespace(const char *) FAST_FUNC; extern char *skip_non_whitespace(const char *) FAST_FUNC; extern char *skip_dev_pfx(const char *tty_name) FAST_FUNC; extern char *strrstr(const char *haystack, const char *needle) FAST_FUNC; //TODO: supply a pointer to char[11] buffer (avoid statics)? extern const char *bb_mode_string(mode_t mode) FAST_FUNC; extern int is_directory(const char *name, int followLinks) FAST_FUNC; enum { /* DO NOT CHANGE THESE VALUES! cp.c, mv.c, install.c depend on them. */ FILEUTILS_PRESERVE_STATUS = 1 << 0, /* -p */ FILEUTILS_DEREFERENCE = 1 << 1, /* !-d */ FILEUTILS_RECUR = 1 << 2, /* -R */ FILEUTILS_FORCE = 1 << 3, /* -f */ FILEUTILS_INTERACTIVE = 1 << 4, /* -i */ FILEUTILS_MAKE_HARDLINK = 1 << 5, /* -l */ FILEUTILS_MAKE_SOFTLINK = 1 << 6, /* -s */ FILEUTILS_DEREF_SOFTLINK = 1 << 7, /* -L */ FILEUTILS_DEREFERENCE_L0 = 1 << 8, /* -H */ #if ENABLE_SELINUX FILEUTILS_PRESERVE_SECURITY_CONTEXT = 1 << 9, /* -c */ FILEUTILS_SET_SECURITY_CONTEXT = 1 << 10, #endif FILEUTILS_IGNORE_CHMOD_ERR = 1 << 11, }; #define FILEUTILS_CP_OPTSTR "pdRfilsLH" IF_SELINUX("c") extern int remove_file(const char *path, int flags) FAST_FUNC; /* NB: without FILEUTILS_RECUR in flags, it will basically "cat" * the source, not copy (unless "source" is a directory). * This makes "cp /dev/null file" and "install /dev/null file" (!!!) * work coreutils-compatibly. */ extern int copy_file(const char *source, const char *dest, int flags) FAST_FUNC; enum { ACTION_RECURSE = (1 << 0), ACTION_FOLLOWLINKS = (1 << 1), ACTION_FOLLOWLINKS_L0 = (1 << 2), ACTION_DEPTHFIRST = (1 << 3), /*ACTION_REVERSE = (1 << 4), - unused */ ACTION_QUIET = (1 << 5), ACTION_DANGLING_OK = (1 << 6), }; typedef uint8_t recurse_flags_t; extern int recursive_action(const char *fileName, unsigned flags, int FAST_FUNC (*fileAction)(const char *fileName, struct stat* statbuf, void* userData, int depth), int FAST_FUNC (*dirAction)(const char *fileName, struct stat* statbuf, void* userData, int depth), void* userData, unsigned depth) FAST_FUNC; extern int device_open(const char *device, int mode) FAST_FUNC; enum { GETPTY_BUFSIZE = 16 }; /* more than enough for "/dev/ttyXXX" */ extern int xgetpty(char *line) FAST_FUNC; extern int get_console_fd_or_die(void) FAST_FUNC; extern void console_make_active(int fd, const int vt_num) FAST_FUNC; extern char *find_block_device(const char *path) FAST_FUNC; /* bb_copyfd_XX print read/write errors and return -1 if they occur */ extern off_t bb_copyfd_eof(int fd1, int fd2) FAST_FUNC; extern off_t bb_copyfd_size(int fd1, int fd2, off_t size) FAST_FUNC; extern void bb_copyfd_exact_size(int fd1, int fd2, off_t size) FAST_FUNC; /* "short" copy can be detected by return value < size */ /* this helper yells "short read!" if param is not -1 */ extern void complain_copyfd_and_die(off_t sz) NORETURN FAST_FUNC; extern char bb_process_escape_sequence(const char **ptr) FAST_FUNC; char* strcpy_and_process_escape_sequences(char *dst, const char *src) FAST_FUNC; /* xxxx_strip version can modify its parameter: * "/" -> "/" * "abc" -> "abc" * "abc/def" -> "def" * "abc/def/" -> "def" !! */ char *bb_get_last_path_component_strip(char *path) FAST_FUNC; /* "abc/def/" -> "" and it never modifies 'path' */ char *bb_get_last_path_component_nostrip(const char *path) FAST_FUNC; /* Simpler version: does not special case "/" string */ const char *bb_basename(const char *name) FAST_FUNC; /* NB: can violate const-ness (similarly to strchr) */ char *last_char_is(const char *s, int c) FAST_FUNC; const char* endofname(const char *name) FAST_FUNC; void ndelay_on(int fd) FAST_FUNC; void ndelay_off(int fd) FAST_FUNC; void close_on_exec_on(int fd) FAST_FUNC; void xdup2(int, int) FAST_FUNC; void xmove_fd(int, int) FAST_FUNC; DIR *xopendir(const char *path) FAST_FUNC; DIR *warn_opendir(const char *path) FAST_FUNC; char *xmalloc_realpath(const char *path) FAST_FUNC RETURNS_MALLOC; char *xmalloc_readlink(const char *path) FAST_FUNC RETURNS_MALLOC; char *xmalloc_readlink_or_warn(const char *path) FAST_FUNC RETURNS_MALLOC; /* !RETURNS_MALLOC: it's a realloc-like function */ char *xrealloc_getcwd_or_warn(char *cwd) FAST_FUNC; char *xmalloc_follow_symlinks(const char *path) FAST_FUNC RETURNS_MALLOC; enum { /* bb_signals(BB_FATAL_SIGS, handler) catches all signals which * otherwise would kill us, except for those resulting from bugs: * SIGSEGV, SIGILL, SIGFPE. * Other fatal signals not included (TODO?): * SIGBUS Bus error (bad memory access) * SIGPOLL Pollable event. Synonym of SIGIO * SIGPROF Profiling timer expired * SIGSYS Bad argument to routine * SIGTRAP Trace/breakpoint trap * * The only known arch with some of these sigs not fitting * into 32 bits is parisc (SIGXCPU=33, SIGXFSZ=34, SIGSTKFLT=36). * Dance around with long long to guard against that... */ BB_FATAL_SIGS = (int)(0 + (1LL << SIGHUP) + (1LL << SIGINT) + (1LL << SIGTERM) + (1LL << SIGPIPE) // Write to pipe with no readers + (1LL << SIGQUIT) // Quit from keyboard + (1LL << SIGABRT) // Abort signal from abort(3) + (1LL << SIGALRM) // Timer signal from alarm(2) + (1LL << SIGVTALRM) // Virtual alarm clock + (1LL << SIGXCPU) // CPU time limit exceeded + (1LL << SIGXFSZ) // File size limit exceeded + (1LL << SIGUSR1) // Yes kids, these are also fatal! + (1LL << SIGUSR2) + 0), }; void bb_signals(int sigs, void (*f)(int)) FAST_FUNC; /* Unlike signal() and bb_signals, sets handler with sigaction() * and in a way that while signal handler is run, no other signals * will be blocked; syscalls will not be restarted: */ void bb_signals_recursive_norestart(int sigs, void (*f)(int)) FAST_FUNC; /* syscalls like read() will be interrupted with EINTR: */ void signal_no_SA_RESTART_empty_mask(int sig, void (*handler)(int)) FAST_FUNC; /* syscalls like read() won't be interrupted (though select/poll will be): */ void signal_SA_RESTART_empty_mask(int sig, void (*handler)(int)) FAST_FUNC; void wait_for_any_sig(void) FAST_FUNC; void kill_myself_with_sig(int sig) NORETURN FAST_FUNC; void sig_block(int sig) FAST_FUNC; void sig_unblock(int sig) FAST_FUNC; /* Will do sigaction(signum, act, NULL): */ int sigaction_set(int sig, const struct sigaction *act) FAST_FUNC; /* SIG_BLOCK/SIG_UNBLOCK all signals: */ int sigprocmask_allsigs(int how) FAST_FUNC; /* Standard handler which just records signo */ extern smallint bb_got_signal; void record_signo(int signo); /* not FAST_FUNC! */ void xsetgid(gid_t gid) FAST_FUNC; void xsetuid(uid_t uid) FAST_FUNC; void xsetegid(gid_t egid) FAST_FUNC; void xseteuid(uid_t euid) FAST_FUNC; void xchdir(const char *path) FAST_FUNC; void xchroot(const char *path) FAST_FUNC; void xsetenv(const char *key, const char *value) FAST_FUNC; void bb_unsetenv(const char *key) FAST_FUNC; void bb_unsetenv_and_free(char *key) FAST_FUNC; void xunlink(const char *pathname) FAST_FUNC; void xstat(const char *pathname, struct stat *buf) FAST_FUNC; void xfstat(int fd, struct stat *buf, const char *errmsg) FAST_FUNC; int open3_or_warn(const char *pathname, int flags, int mode) FAST_FUNC; int open_or_warn(const char *pathname, int flags) FAST_FUNC; int xopen3(const char *pathname, int flags, int mode) FAST_FUNC; int xopen(const char *pathname, int flags) FAST_FUNC; int xopen_nonblocking(const char *pathname) FAST_FUNC; int xopen_as_uid_gid(const char *pathname, int flags, uid_t u, gid_t g) FAST_FUNC; int open_or_warn_stdin(const char *pathname) FAST_FUNC; int xopen_stdin(const char *pathname) FAST_FUNC; void xrename(const char *oldpath, const char *newpath) FAST_FUNC; int rename_or_warn(const char *oldpath, const char *newpath) FAST_FUNC; off_t xlseek(int fd, off_t offset, int whence) FAST_FUNC; int xmkstemp(char *template) FAST_FUNC; off_t fdlength(int fd) FAST_FUNC; uoff_t FAST_FUNC get_volume_size_in_bytes(int fd, const char *override, unsigned override_units, int extend); void xpipe(int filedes[2]) FAST_FUNC; /* In this form code with pipes is much more readable */ struct fd_pair { int rd; int wr; }; #define piped_pair(pair) pipe(&((pair).rd)) #define xpiped_pair(pair) xpipe(&((pair).rd)) /* Useful for having small structure members/global variables */ typedef int8_t socktype_t; typedef int8_t family_t; struct BUG_too_small { char BUG_socktype_t_too_small[(0 | SOCK_STREAM | SOCK_DGRAM | SOCK_RDM | SOCK_SEQPACKET | SOCK_RAW ) <= 127 ? 1 : -1]; char BUG_family_t_too_small[(0 | AF_UNSPEC | AF_INET | AF_INET6 | AF_UNIX #ifdef AF_PACKET | AF_PACKET #endif #ifdef AF_NETLINK | AF_NETLINK #endif /* | AF_DECnet */ /* | AF_IPX */ ) <= 127 ? 1 : -1]; }; void parse_datestr(const char *date_str, struct tm *ptm) FAST_FUNC; time_t validate_tm_time(const char *date_str, struct tm *ptm) FAST_FUNC; char *strftime_HHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC; char *strftime_YYYYMMDDHHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC; int xsocket(int domain, int type, int protocol) FAST_FUNC; void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) FAST_FUNC; void xlisten(int s, int backlog) FAST_FUNC; void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) FAST_FUNC; ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to, socklen_t tolen) FAST_FUNC; /* SO_REUSEADDR allows a server to rebind to an address that is already * "in use" by old connections to e.g. previous server instance which is * killed or crashed. Without it bind will fail until all such connections * time out. Linux does not allow multiple live binds on same ip:port * regardless of SO_REUSEADDR (unlike some other flavors of Unix). * Turn it on before you call bind(). */ void setsockopt_reuseaddr(int fd) FAST_FUNC; /* On Linux this never fails. */ int setsockopt_broadcast(int fd) FAST_FUNC; int setsockopt_bindtodevice(int fd, const char *iface) FAST_FUNC; /* NB: returns port in host byte order */ unsigned bb_lookup_port(const char *port, const char *protocol, unsigned default_port) FAST_FUNC; typedef struct len_and_sockaddr { socklen_t len; union { struct sockaddr sa; struct sockaddr_in sin; #if ENABLE_FEATURE_IPV6 struct sockaddr_in6 sin6; #endif } u; } len_and_sockaddr; enum { LSA_LEN_SIZE = offsetof(len_and_sockaddr, u), LSA_SIZEOF_SA = sizeof( union { struct sockaddr sa; struct sockaddr_in sin; #if ENABLE_FEATURE_IPV6 struct sockaddr_in6 sin6; #endif } ) }; /* Create stream socket, and allocate suitable lsa. * (lsa of correct size and lsa->sa.sa_family (AF_INET/AF_INET6)) * af == AF_UNSPEC will result in trying to create IPv6 socket, * and if kernel doesn't support it, fall back to IPv4. * This is useful if you plan to bind to resulting local lsa. */ int xsocket_type(len_and_sockaddr **lsap, int af, int sock_type) FAST_FUNC; int xsocket_stream(len_and_sockaddr **lsap) FAST_FUNC; /* Create server socket bound to bindaddr:port. bindaddr can be NULL, * numeric IP ("N.N.N.N") or numeric IPv6 address, * and can have ":PORT" suffix (for IPv6 use "[X:X:...:X]:PORT"). * Only if there is no suffix, port argument is used */ /* NB: these set SO_REUSEADDR before bind */ int create_and_bind_stream_or_die(const char *bindaddr, int port) FAST_FUNC; int create_and_bind_dgram_or_die(const char *bindaddr, int port) FAST_FUNC; /* Create client TCP socket connected to peer:port. Peer cannot be NULL. * Peer can be numeric IP ("N.N.N.N"), numeric IPv6 address or hostname, * and can have ":PORT" suffix (for IPv6 use "[X:X:...:X]:PORT"). * If there is no suffix, port argument is used */ int create_and_connect_stream_or_die(const char *peer, int port) FAST_FUNC; /* Connect to peer identified by lsa */ int xconnect_stream(const len_and_sockaddr *lsa) FAST_FUNC; /* Get local address of bound or accepted socket */ len_and_sockaddr *get_sock_lsa(int fd) FAST_FUNC RETURNS_MALLOC; /* Get remote address of connected or accepted socket */ len_and_sockaddr *get_peer_lsa(int fd) FAST_FUNC RETURNS_MALLOC; /* Return malloc'ed len_and_sockaddr with socket address of host:port * Currently will return IPv4 or IPv6 sockaddrs only * (depending on host), but in theory nothing prevents e.g. * UNIX socket address being returned, IPX sockaddr etc... * On error does bb_error_msg and returns NULL */ len_and_sockaddr* host2sockaddr(const char *host, int port) FAST_FUNC RETURNS_MALLOC; /* Version which dies on error */ len_and_sockaddr* xhost2sockaddr(const char *host, int port) FAST_FUNC RETURNS_MALLOC; len_and_sockaddr* xdotted2sockaddr(const char *host, int port) FAST_FUNC RETURNS_MALLOC; /* Same, useful if you want to force family (e.g. IPv6) */ #if !ENABLE_FEATURE_IPV6 #define host_and_af2sockaddr(host, port, af) host2sockaddr((host), (port)) #define xhost_and_af2sockaddr(host, port, af) xhost2sockaddr((host), (port)) #else len_and_sockaddr* host_and_af2sockaddr(const char *host, int port, sa_family_t af) FAST_FUNC RETURNS_MALLOC; len_and_sockaddr* xhost_and_af2sockaddr(const char *host, int port, sa_family_t af) FAST_FUNC RETURNS_MALLOC; #endif /* Assign sin[6]_port member if the socket is an AF_INET[6] one, * otherwise no-op. Useful for ftp. * NB: does NOT do htons() internally, just direct assignment. */ void set_nport(struct sockaddr *sa, unsigned port) FAST_FUNC; /* Retrieve sin[6]_port or return -1 for non-INET[6] lsa's */ int get_nport(const struct sockaddr *sa) FAST_FUNC; /* Reverse DNS. Returns NULL on failure. */ char* xmalloc_sockaddr2host(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC; /* This one doesn't append :PORTNUM */ char* xmalloc_sockaddr2host_noport(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC; /* This one also doesn't fall back to dotted IP (returns NULL) */ char* xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC; /* inet_[ap]ton on steroids */ char* xmalloc_sockaddr2dotted(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC; char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC; // "old" (ipv4 only) API // users: traceroute.c hostname.c - use _list_ of all IPs struct hostent *xgethostbyname(const char *name) FAST_FUNC; // Also mount.c and inetd.c are using gethostbyname(), // + inet_common.c has additional IPv4-only stuff void socket_want_pktinfo(int fd) FAST_FUNC; ssize_t send_to_from(int fd, void *buf, size_t len, int flags, const struct sockaddr *to, const struct sockaddr *from, socklen_t tolen) FAST_FUNC; ssize_t recv_from_to(int fd, void *buf, size_t len, int flags, struct sockaddr *from, struct sockaddr *to, socklen_t sa_size) FAST_FUNC; uint16_t inet_cksum(uint16_t *addr, int len) FAST_FUNC; char *xstrdup(const char *s) FAST_FUNC RETURNS_MALLOC; char *xstrndup(const char *s, int n) FAST_FUNC RETURNS_MALLOC; void overlapping_strcpy(char *dst, const char *src) FAST_FUNC; char *safe_strncpy(char *dst, const char *src, size_t size) FAST_FUNC; char *strncpy_IFNAMSIZ(char *dst, const char *src) FAST_FUNC; /* Guaranteed to NOT be a macro (smallest code). Saves nearly 2k on uclibc. * But potentially slow, don't use in one-billion-times loops */ int bb_putchar(int ch) FAST_FUNC; /* Note: does not use stdio, writes to fd 2 directly */ int bb_putchar_stderr(char ch) FAST_FUNC; char *xasprintf(const char *format, ...) __attribute__ ((format(printf, 1, 2))) FAST_FUNC RETURNS_MALLOC; // gcc-4.1.1 still isn't good enough at optimizing it // (+200 bytes compared to macro) //static ALWAYS_INLINE //int LONE_DASH(const char *s) { return s[0] == '-' && !s[1]; } //static ALWAYS_INLINE //int NOT_LONE_DASH(const char *s) { return s[0] != '-' || s[1]; } #define LONE_DASH(s) ((s)[0] == '-' && !(s)[1]) #define NOT_LONE_DASH(s) ((s)[0] != '-' || (s)[1]) #define LONE_CHAR(s,c) ((s)[0] == (c) && !(s)[1]) #define NOT_LONE_CHAR(s,c) ((s)[0] != (c) || (s)[1]) #define DOT_OR_DOTDOT(s) ((s)[0] == '.' && (!(s)[1] || ((s)[1] == '.' && !(s)[2]))) typedef struct uni_stat_t { unsigned byte_count; unsigned unicode_count; unsigned unicode_width; } uni_stat_t; /* Returns a string with unprintable chars replaced by '?' or * SUBST_WCHAR. This function is unicode-aware. */ const char* FAST_FUNC printable_string(uni_stat_t *stats, const char *str); /* Prints unprintable char ch as ^C or M-c to file * (M-c is used only if ch is ORed with PRINTABLE_META), * else it is printed as-is (except for ch = 0x9b) */ enum { PRINTABLE_META = 0x100 }; void fputc_printable(int ch, FILE *file) FAST_FUNC; /* Return a string that is the printable representation of character ch. * Buffer must hold at least four characters. */ enum { VISIBLE_ENDLINE = 1 << 0, VISIBLE_SHOW_TABS = 1 << 1, }; void visible(unsigned ch, char *buf, int flags) FAST_FUNC; /* dmalloc will redefine these to it's own implementation. It is safe * to have the prototypes here unconditionally. */ void *malloc_or_warn(size_t size) FAST_FUNC RETURNS_MALLOC; void *xmalloc(size_t size) FAST_FUNC RETURNS_MALLOC; void *xzalloc(size_t size) FAST_FUNC RETURNS_MALLOC; void *xrealloc(void *old, size_t size) FAST_FUNC; /* After v = xrealloc_vector(v, SHIFT, idx) it's ok to use * at least v[idx] and v[idx+1], for all idx values. * SHIFT specifies how many new elements are added (1:2, 2:4, ..., 8:256...) * when all elements are used up. New elements are zeroed out. * xrealloc_vector(v, SHIFT, idx) *MUST* be called with consecutive IDXs - * skipping an index is a bad bug - it may miss a realloc! */ #define xrealloc_vector(vector, shift, idx) \ xrealloc_vector_helper((vector), (sizeof((vector)[0]) << 8) + (shift), (idx)) void* xrealloc_vector_helper(void *vector, unsigned sizeof_and_shift, int idx) FAST_FUNC; extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC; extern ssize_t nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR) FAST_FUNC; // NB: will return short read on error, not -1, // if some data was read before error occurred extern ssize_t full_read(int fd, void *buf, size_t count) FAST_FUNC; extern void xread(int fd, void *buf, size_t count) FAST_FUNC; extern unsigned char xread_char(int fd) FAST_FUNC; extern ssize_t read_close(int fd, void *buf, size_t maxsz) FAST_FUNC; extern ssize_t open_read_close(const char *filename, void *buf, size_t maxsz) FAST_FUNC; // Reads one line a-la fgets (but doesn't save terminating '\n'). // Reads byte-by-byte. Useful when it is important to not read ahead. // Bytes are appended to pfx (which must be malloced, or NULL). extern char *xmalloc_reads(int fd, size_t *maxsz_p) FAST_FUNC; /* Reads block up to *maxsz_p (default: INT_MAX - 4095) */ extern void *xmalloc_read(int fd, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; /* Returns NULL if file can't be opened (default max size: INT_MAX - 4095) */ extern void *xmalloc_open_read_close(const char *filename, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; /* Never returns NULL */ extern void *xmalloc_xopen_read_close(const char *filename, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; #define SEAMLESS_COMPRESSION (0 \ || ENABLE_FEATURE_SEAMLESS_XZ \ || ENABLE_FEATURE_SEAMLESS_LZMA \ || ENABLE_FEATURE_SEAMLESS_BZ2 \ || ENABLE_FEATURE_SEAMLESS_GZ \ || ENABLE_FEATURE_SEAMLESS_Z) #if SEAMLESS_COMPRESSION /* Autodetects gzip/bzip2 formats. fd may be in the middle of the file! */ extern int setup_unzip_on_fd(int fd, int fail_if_not_detected) FAST_FUNC; /* Autodetects .gz etc */ extern int open_zipped(const char *fname) FAST_FUNC; #else # define setup_unzip_on_fd(...) (0) # define open_zipped(fname) open((fname), O_RDONLY); #endif extern void *xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; extern ssize_t safe_write(int fd, const void *buf, size_t count) FAST_FUNC; // NB: will return short write on error, not -1, // if some data was written before error occurred extern ssize_t full_write(int fd, const void *buf, size_t count) FAST_FUNC; extern void xwrite(int fd, const void *buf, size_t count) FAST_FUNC; extern void xwrite_str(int fd, const char *str) FAST_FUNC; extern ssize_t full_write1_str(const char *str) FAST_FUNC; extern ssize_t full_write2_str(const char *str) FAST_FUNC; extern void xopen_xwrite_close(const char* file, const char *str) FAST_FUNC; /* Close fd, but check for failures (some types of write errors) */ extern void xclose(int fd) FAST_FUNC; /* Reads and prints to stdout till eof, then closes FILE. Exits on error: */ extern void xprint_and_close_file(FILE *file) FAST_FUNC; /* Reads a line from a text file, up to a newline or NUL byte, inclusive. * Returns malloc'ed char*. If end is NULL '\n' isn't considered * end of line. If end isn't NULL, length of the chunk is stored in it. * Returns NULL if EOF/error. */ extern char *bb_get_chunk_from_file(FILE *file, int *end) FAST_FUNC; /* Reads up to (and including) TERMINATING_STRING: */ extern char *xmalloc_fgets_str(FILE *file, const char *terminating_string) FAST_FUNC RETURNS_MALLOC; /* Same, with limited max size, and returns the length (excluding NUL): */ extern char *xmalloc_fgets_str_len(FILE *file, const char *terminating_string, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; /* Chops off TERMINATING_STRING from the end: */ extern char *xmalloc_fgetline_str(FILE *file, const char *terminating_string) FAST_FUNC RETURNS_MALLOC; /* Reads up to (and including) "\n" or NUL byte: */ extern char *xmalloc_fgets(FILE *file) FAST_FUNC RETURNS_MALLOC; /* Chops off '\n' from the end, unlike fgets: */ extern char *xmalloc_fgetline(FILE *file) FAST_FUNC RETURNS_MALLOC; /* Same, but doesn't try to conserve space (may have some slack after the end) */ /* extern char *xmalloc_fgetline_fast(FILE *file) FAST_FUNC RETURNS_MALLOC; */ void die_if_ferror(FILE *file, const char *msg) FAST_FUNC; void die_if_ferror_stdout(void) FAST_FUNC; int fflush_all(void) FAST_FUNC; void fflush_stdout_and_exit(int retval) NORETURN FAST_FUNC; int fclose_if_not_stdin(FILE *file) FAST_FUNC; FILE* xfopen(const char *filename, const char *mode) FAST_FUNC; /* Prints warning to stderr and returns NULL on failure: */ FILE* fopen_or_warn(const char *filename, const char *mode) FAST_FUNC; /* "Opens" stdin if filename is special, else just opens file: */ FILE* xfopen_stdin(const char *filename) FAST_FUNC; FILE* fopen_or_warn_stdin(const char *filename) FAST_FUNC; FILE* fopen_for_read(const char *path) FAST_FUNC; FILE* xfopen_for_read(const char *path) FAST_FUNC; FILE* fopen_for_write(const char *path) FAST_FUNC; FILE* xfopen_for_write(const char *path) FAST_FUNC; FILE* xfdopen_for_read(int fd) FAST_FUNC; FILE* xfdopen_for_write(int fd) FAST_FUNC; int bb_pstrcmp(const void *a, const void *b) /* not FAST_FUNC! */; void qsort_string_vector(char **sv, unsigned count) FAST_FUNC; /* Wrapper which restarts poll on EINTR or ENOMEM. * On other errors complains [perror("poll")] and returns. * Warning! May take (much) longer than timeout_ms to return! * If this is a problem, use bare poll and open-code EINTR/ENOMEM handling */ int safe_poll(struct pollfd *ufds, nfds_t nfds, int timeout_ms) FAST_FUNC; char *safe_gethostname(void) FAST_FUNC; /* Convert each alpha char in str to lower-case */ char* str_tolower(char *str) FAST_FUNC; char *utoa(unsigned n) FAST_FUNC; char *itoa(int n) FAST_FUNC; /* Returns a pointer past the formatted number, does NOT null-terminate */ char *utoa_to_buf(unsigned n, char *buf, unsigned buflen) FAST_FUNC; char *itoa_to_buf(int n, char *buf, unsigned buflen) FAST_FUNC; /* Intelligent formatters of bignums */ char *smart_ulltoa4(unsigned long long ul, char buf[4], const char *scale) FAST_FUNC; char *smart_ulltoa5(unsigned long long ul, char buf[5], const char *scale) FAST_FUNC; /* If block_size == 0, display size without fractional part, * else display (size * block_size) with one decimal digit. * If display_unit == 0, show value no bigger than 1024 with suffix (K,M,G...), * else divide by display_unit and do not use suffix. */ #define HUMAN_READABLE_MAX_WIDTH 7 /* "1024.0G" */ #define HUMAN_READABLE_MAX_WIDTH_STR "7" //TODO: provide pointer to buf (avoid statics)? const char *make_human_readable_str(unsigned long long size, unsigned long block_size, unsigned long display_unit) FAST_FUNC; /* Put a string of hex bytes ("1b2e66fe"...), return advanced pointer */ char *bin2hex(char *dst, const char *src, int count) FAST_FUNC; /* Reverse */ char* hex2bin(char *dst, const char *src, int count) FAST_FUNC; /* Generate a UUID */ void generate_uuid(uint8_t *buf) FAST_FUNC; /* Last element is marked by mult == 0 */ struct suffix_mult { char suffix[4]; unsigned mult; }; extern const struct suffix_mult bkm_suffixes[]; #define km_suffixes (bkm_suffixes + 1) #include "xatonum.h" /* Specialized: */ /* Using xatoi() instead of naive atoi() is not always convenient - * in many places people want *non-negative* values, but store them * in signed int. Therefore we need this one: * dies if input is not in [0, INT_MAX] range. Also will reject '-0' etc. * It should really be named xatoi_nonnegative (since it allows 0), * but that would be too long. */ int xatoi_positive(const char *numstr) FAST_FUNC; /* Useful for reading port numbers */ uint16_t xatou16(const char *numstr) FAST_FUNC; /* These parse entries in /etc/passwd and /etc/group. This is desirable * for BusyBox since we want to avoid using the glibc NSS stuff, which * increases target size and is often not needed on embedded systems. */ long xuname2uid(const char *name) FAST_FUNC; long xgroup2gid(const char *name) FAST_FUNC; /* wrapper: allows string to contain numeric uid or gid */ unsigned long get_ug_id(const char *s, long FAST_FUNC (*xname2id)(const char *)) FAST_FUNC; /* from chpst. Does not die, returns 0 on failure */ struct bb_uidgid_t { uid_t uid; gid_t gid; }; /* always sets uid and gid */ int get_uidgid(struct bb_uidgid_t*, const char*, int numeric_ok) FAST_FUNC; /* always sets uid and gid, allows numeric; exits on failure */ void xget_uidgid(struct bb_uidgid_t*, const char*) FAST_FUNC; /* chown-like handling of "user[:[group]" */ void parse_chown_usergroup_or_die(struct bb_uidgid_t *u, char *user_group) FAST_FUNC; struct passwd* xgetpwnam(const char *name) FAST_FUNC; struct group* xgetgrnam(const char *name) FAST_FUNC; struct passwd* xgetpwuid(uid_t uid) FAST_FUNC; struct group* xgetgrgid(gid_t gid) FAST_FUNC; char* xuid2uname(uid_t uid) FAST_FUNC; char* xgid2group(gid_t gid) FAST_FUNC; char* uid2uname(uid_t uid) FAST_FUNC; char* gid2group(gid_t gid) FAST_FUNC; char* uid2uname_utoa(uid_t uid) FAST_FUNC; char* gid2group_utoa(gid_t gid) FAST_FUNC; /* versions which cache results (useful for ps, ls etc) */ const char* get_cached_username(uid_t uid) FAST_FUNC; const char* get_cached_groupname(gid_t gid) FAST_FUNC; void clear_username_cache(void) FAST_FUNC; /* internally usernames are saved in fixed-sized char[] buffers */ enum { USERNAME_MAX_SIZE = 32 - sizeof(uid_t) }; #if ENABLE_FEATURE_CHECK_NAMES void die_if_bad_username(const char* name) FAST_FUNC; #else #define die_if_bad_username(name) ((void)(name)) #endif #if ENABLE_FEATURE_UTMP void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); #else # define write_new_utmp(pid, new_type, tty_name, username, hostname) ((void)0) # define update_utmp(pid, new_type, tty_name, username, hostname) ((void)0) #endif int execable_file(const char *name) FAST_FUNC; char *find_execable(const char *filename, char **PATHp) FAST_FUNC; int exists_execable(const char *filename) FAST_FUNC; /* BB_EXECxx always execs (it's not doing NOFORK/NOEXEC stuff), * but it may exec busybox and call applet instead of searching PATH. */ #if ENABLE_FEATURE_PREFER_APPLETS int BB_EXECVP(const char *file, char *const argv[]) FAST_FUNC; #define BB_EXECLP(prog,cmd,...) \ do { \ if (find_applet_by_name(prog) >= 0) \ execlp(bb_busybox_exec_path, cmd, __VA_ARGS__); \ execlp(prog, cmd, __VA_ARGS__); \ } while (0) #else #define BB_EXECVP(prog,cmd) execvp(prog,cmd) #define BB_EXECLP(prog,cmd,...) execlp(prog,cmd,__VA_ARGS__) #endif int BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC; /* xvfork() can't be a _function_, return after vfork mangles stack * in the parent. It must be a macro. */ #define xvfork() \ ({ \ pid_t bb__xvfork_pid = vfork(); \ if (bb__xvfork_pid < 0) \ bb_perror_msg_and_die("vfork"); \ bb__xvfork_pid; \ }) #if BB_MMU pid_t xfork(void) FAST_FUNC; #endif /* NOMMU friendy fork+exec: */ pid_t spawn(char **argv) FAST_FUNC; pid_t xspawn(char **argv) FAST_FUNC; pid_t safe_waitpid(pid_t pid, int *wstat, int options) FAST_FUNC; pid_t wait_any_nohang(int *wstat) FAST_FUNC; /* wait4pid: unlike waitpid, waits ONLY for one process. * Returns sig + 0x180 if child is killed by signal. * It's safe to pass negative 'pids' from failed [v]fork - * wait4pid will return -1 (and will not clobber [v]fork's errno). * IOW: rc = wait4pid(spawn(argv)); * if (rc < 0) bb_perror_msg("%s", argv[0]); * if (rc > 0) bb_error_msg("exit code: %d", rc & 0xff); */ int wait4pid(pid_t pid) FAST_FUNC; /* Same as wait4pid(spawn(argv)), but with NOFORK/NOEXEC if configured: */ int spawn_and_wait(char **argv) FAST_FUNC; /* Does NOT check that applet is NOFORK, just blindly runs it */ int run_nofork_applet(int applet_no, char **argv) FAST_FUNC; /* Helpers for daemonization. * * bb_daemonize(flags) = daemonize, does not compile on NOMMU * * bb_daemonize_or_rexec(flags, argv) = daemonizes on MMU (and ignores argv), * rexec's itself on NOMMU with argv passed as command line. * Thus bb_daemonize_or_rexec may cause your _main() to be re-executed * from the start. (It will detect it and not reexec again second time). * You have to audit carefully that you don't do something twice as a result * (opening files/sockets, parsing config files etc...)! * * Both of the above will redirect fd 0,1,2 to /dev/null and drop ctty * (will do setsid()). * * fork_or_rexec(argv) = bare-bones fork on MMU, * "vfork + re-exec ourself" on NOMMU. No fd redirection, no setsid(). * On MMU ignores argv. * * Helper for network daemons in foreground mode: * * bb_sanitize_stdio() = make sure that fd 0,1,2 are opened by opening them * to /dev/null if they are not. */ enum { DAEMON_CHDIR_ROOT = 1, DAEMON_DEVNULL_STDIO = 2, DAEMON_CLOSE_EXTRA_FDS = 4, DAEMON_ONLY_SANITIZE = 8, /* internal use */ DAEMON_DOUBLE_FORK = 16, /* double fork to avoid controlling tty */ }; #if BB_MMU enum { re_execed = 0 }; # define fork_or_rexec(argv) xfork() # define bb_daemonize_or_rexec(flags, argv) bb_daemonize_or_rexec(flags) # define bb_daemonize(flags) bb_daemonize_or_rexec(flags, bogus) #else extern bool re_execed; /* Note: re_exec() and fork_or_rexec() do argv[0][0] |= 0x80 on NOMMU! * _Parent_ needs to undo it if it doesn't want to have argv[0] mangled. */ void re_exec(char **argv) NORETURN FAST_FUNC; pid_t fork_or_rexec(char **argv) FAST_FUNC; int BUG_fork_is_unavailable_on_nommu(void) FAST_FUNC; int BUG_daemon_is_unavailable_on_nommu(void) FAST_FUNC; void BUG_bb_daemonize_is_unavailable_on_nommu(void) FAST_FUNC; # define fork() BUG_fork_is_unavailable_on_nommu() # define xfork() BUG_fork_is_unavailable_on_nommu() # define daemon(a,b) BUG_daemon_is_unavailable_on_nommu() # define bb_daemonize(a) BUG_bb_daemonize_is_unavailable_on_nommu() #endif void bb_daemonize_or_rexec(int flags, char **argv) FAST_FUNC; void bb_sanitize_stdio(void) FAST_FUNC; /* Clear dangerous stuff, set PATH. Return 1 if was run by different user. */ int sanitize_env_if_suid(void) FAST_FUNC; char* single_argv(char **argv) FAST_FUNC; extern const char *const bb_argv_dash[]; /* "-", NULL */ extern const char *opt_complementary; #if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG #define No_argument "\0" #define Required_argument "\001" #define Optional_argument "\002" extern const char *applet_long_options; #endif extern uint32_t option_mask32; extern uint32_t getopt32(char **argv, const char *applet_opts, ...) FAST_FUNC; /* Having next pointer as a first member allows easy creation * of "llist-compatible" structs, and using llist_FOO functions * on them. */ typedef struct llist_t { struct llist_t *link; char *data; } llist_t; void llist_add_to(llist_t **old_head, void *data) FAST_FUNC; void llist_add_to_end(llist_t **list_head, void *data) FAST_FUNC; void *llist_pop(llist_t **elm) FAST_FUNC; void llist_unlink(llist_t **head, llist_t *elm) FAST_FUNC; void llist_free(llist_t *elm, void (*freeit)(void *data)) FAST_FUNC; llist_t *llist_rev(llist_t *list) FAST_FUNC; llist_t *llist_find_str(llist_t *first, const char *str) FAST_FUNC; /* BTW, surprisingly, changing API to * llist_t *llist_add_to(llist_t *old_head, void *data) * etc does not result in smaller code... */ /* start_stop_daemon and udhcpc are special - they want * to create pidfiles regardless of FEATURE_PIDFILE */ #if ENABLE_FEATURE_PIDFILE || defined(WANT_PIDFILE) /* True only if we created pidfile which is *file*, not /dev/null etc */ extern smallint wrote_pidfile; void write_pidfile(const char *path) FAST_FUNC; #define remove_pidfile(path) do { if (wrote_pidfile) unlink(path); } while (0) #else enum { wrote_pidfile = 0 }; #define write_pidfile(path) ((void)0) #define remove_pidfile(path) ((void)0) #endif enum { LOGMODE_NONE = 0, LOGMODE_STDIO = (1 << 0), LOGMODE_SYSLOG = (1 << 1) * ENABLE_FEATURE_SYSLOG, LOGMODE_BOTH = LOGMODE_SYSLOG + LOGMODE_STDIO, }; extern const char *msg_eol; extern smallint logmode; extern int die_sleep; extern uint8_t xfunc_error_retval; extern jmp_buf die_jmp; extern void xfunc_die(void) NORETURN FAST_FUNC; extern void bb_show_usage(void) NORETURN FAST_FUNC; extern void bb_error_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))) FAST_FUNC; extern void bb_error_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2))) FAST_FUNC; extern void bb_perror_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))) FAST_FUNC; extern void bb_simple_perror_msg(const char *s) FAST_FUNC; extern void bb_perror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2))) FAST_FUNC; extern void bb_simple_perror_msg_and_die(const char *s) NORETURN FAST_FUNC; extern void bb_herror_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))) FAST_FUNC; extern void bb_herror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2))) FAST_FUNC; extern void bb_perror_nomsg_and_die(void) NORETURN FAST_FUNC; extern void bb_perror_nomsg(void) FAST_FUNC; extern void bb_info_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))) FAST_FUNC; extern void bb_verror_msg(const char *s, va_list p, const char *strerr) FAST_FUNC; /* We need to export XXX_main from libbusybox * only if we build "individual" binaries */ #if ENABLE_FEATURE_INDIVIDUAL #define MAIN_EXTERNALLY_VISIBLE EXTERNALLY_VISIBLE #else #define MAIN_EXTERNALLY_VISIBLE #endif /* Applets which are useful from another applets */ int bb_cat(char** argv); /* If shell needs them, they exist even if not enabled as applets */ int echo_main(int argc, char** argv) IF_ECHO(MAIN_EXTERNALLY_VISIBLE); int printf_main(int argc, char **argv) IF_PRINTF(MAIN_EXTERNALLY_VISIBLE); int test_main(int argc, char **argv) IF_TEST(MAIN_EXTERNALLY_VISIBLE); int kill_main(int argc, char **argv) IF_KILL(MAIN_EXTERNALLY_VISIBLE); /* Similar, but used by chgrp, not shell */ int chown_main(int argc, char **argv) IF_CHOWN(MAIN_EXTERNALLY_VISIBLE); /* Used by ftpd */ int ls_main(int argc, char **argv) IF_LS(MAIN_EXTERNALLY_VISIBLE); /* Don't need IF_xxx() guard for these */ int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; #if ENABLE_ROUTE void bb_displayroutes(int noresolve, int netstatfmt) FAST_FUNC; #endif /* Networking */ /* This structure defines protocol families and their handlers. */ struct aftype { const char *name; const char *title; int af; int alen; char* FAST_FUNC (*print)(unsigned char *); const char* FAST_FUNC (*sprint)(struct sockaddr *, int numeric); int FAST_FUNC (*input)(/*int type,*/ const char *bufp, struct sockaddr *); void FAST_FUNC (*herror)(char *text); int FAST_FUNC (*rprint)(int options); int FAST_FUNC (*rinput)(int typ, int ext, char **argv); /* may modify src */ int FAST_FUNC (*getmask)(char *src, struct sockaddr *mask, char *name); }; /* This structure defines hardware protocols and their handlers. */ struct hwtype { const char *name; const char *title; int type; int alen; char* FAST_FUNC (*print)(unsigned char *); int FAST_FUNC (*input)(const char *, struct sockaddr *); int FAST_FUNC (*activate)(int fd); int suppress_null_addr; }; extern smallint interface_opt_a; int display_interfaces(char *ifname) FAST_FUNC; int in_ether(const char *bufp, struct sockaddr *sap) FAST_FUNC; #if ENABLE_FEATURE_HWIB int in_ib(const char *bufp, struct sockaddr *sap) FAST_FUNC; #else #define in_ib(a, b) 1 /* fail */ #endif const struct aftype *get_aftype(const char *name) FAST_FUNC; const struct hwtype *get_hwtype(const char *name) FAST_FUNC; const struct hwtype *get_hwntype(int type) FAST_FUNC; #ifndef BUILD_INDIVIDUAL extern int find_applet_by_name(const char *name) FAST_FUNC; /* Returns only if applet is not found. */ extern void run_applet_and_exit(const char *name, char **argv) FAST_FUNC; extern void run_applet_no_and_exit(int a, char **argv) NORETURN FAST_FUNC; #endif #ifdef HAVE_MNTENT_H extern int match_fstype(const struct mntent *mt, const char *fstypes) FAST_FUNC; extern struct mntent *find_mount_point(const char *name, int subdir_too) FAST_FUNC; #endif extern void erase_mtab(const char * name) FAST_FUNC; extern unsigned int tty_baud_to_value(speed_t speed) FAST_FUNC; extern speed_t tty_value_to_baud(unsigned int value) FAST_FUNC; #if ENABLE_DESKTOP extern void bb_warn_ignoring_args(char *arg) FAST_FUNC; #else # define bb_warn_ignoring_args(arg) ((void)0) #endif extern int get_linux_version_code(void) FAST_FUNC; extern char *query_loop(const char *device) FAST_FUNC; extern int del_loop(const char *device) FAST_FUNC; /* If *devname is not NULL, use that name, otherwise try to find free one, * malloc and return it in *devname. * return value: 1: read-only loopdev was setup, 0: rw, < 0: error */ extern int set_loop(char **devname, const char *file, unsigned long long offset, int ro) FAST_FUNC; /* Like bb_ask below, but asks on stdin with no timeout. */ char *bb_ask_stdin(const char * prompt) FAST_FUNC; //TODO: pass buf pointer or return allocated buf (avoid statics)? char *bb_ask(const int fd, int timeout, const char * prompt) FAST_FUNC; int bb_ask_confirmation(void) FAST_FUNC; int bb_parse_mode(const char* s, mode_t* theMode) FAST_FUNC; /* * Config file parser */ enum { PARSE_COLLAPSE = 0x00010000, // treat consecutive delimiters as one PARSE_TRIM = 0x00020000, // trim leading and trailing delimiters // TODO: COLLAPSE and TRIM seem to always go in pair PARSE_GREEDY = 0x00040000, // last token takes entire remainder of the line PARSE_MIN_DIE = 0x00100000, // die if < min tokens found // keep a copy of current line PARSE_KEEP_COPY = 0x00200000 * ENABLE_FEATURE_CROND_D, PARSE_EOL_COMMENTS = 0x00400000, // comments are recognized even if they aren't the first char // NORMAL is: // * remove leading and trailing delimiters and collapse // multiple delimiters into one // * warn and continue if less than mintokens delimiters found // * grab everything into last token // * comments are recognized even if they aren't the first char PARSE_NORMAL = PARSE_COLLAPSE | PARSE_TRIM | PARSE_GREEDY | PARSE_EOL_COMMENTS, }; typedef struct parser_t { FILE *fp; char *data; char *line, *nline; size_t line_alloc, nline_alloc; int lineno; } parser_t; parser_t* config_open(const char *filename) FAST_FUNC; parser_t* config_open2(const char *filename, FILE* FAST_FUNC (*fopen_func)(const char *path)) FAST_FUNC; /* delims[0] is a comment char (use '\0' to disable), the rest are token delimiters */ int config_read(parser_t *parser, char **tokens, unsigned flags, const char *delims) FAST_FUNC; #define config_read(parser, tokens, max, min, str, flags) \ config_read(parser, tokens, ((flags) | (((min) & 0xFF) << 8) | ((max) & 0xFF)), str) void config_close(parser_t *parser) FAST_FUNC; /* Concatenate path and filename to new allocated buffer. * Add "/" only as needed (no duplicate "//" are produced). * If path is NULL, it is assumed to be "/". * filename should not be NULL. */ char *concat_path_file(const char *path, const char *filename) FAST_FUNC; /* Returns NULL on . and .. */ char *concat_subpath_file(const char *path, const char *filename) FAST_FUNC; int bb_make_directory(char *path, long mode, int flags) FAST_FUNC; int get_signum(const char *name) FAST_FUNC; const char *get_signame(int number) FAST_FUNC; void print_signames(void) FAST_FUNC; char *bb_simplify_path(const char *path) FAST_FUNC; /* Returns ptr to NUL */ char *bb_simplify_abs_path_inplace(char *path) FAST_FUNC; #define LOGIN_FAIL_DELAY 3 extern void bb_do_delay(int seconds) FAST_FUNC; extern void change_identity(const struct passwd *pw) FAST_FUNC; extern void run_shell(const char *shell, int loginshell, const char *command, const char **additional_args) NORETURN FAST_FUNC; /* Returns $SHELL, getpwuid(getuid())->pw_shell, or DEFAULT_SHELL. * Note that getpwuid result might need xstrdup'ing * if there is a possibility of intervening getpwxxx() calls. */ const char *get_shell_name(void) FAST_FUNC; #if ENABLE_SELINUX extern void renew_current_security_context(void) FAST_FUNC; extern void set_current_security_context(security_context_t sid) FAST_FUNC; extern context_t set_security_context_component(security_context_t cur_context, char *user, char *role, char *type, char *range) FAST_FUNC; extern void setfscreatecon_or_die(security_context_t scontext) FAST_FUNC; extern void selinux_preserve_fcontext(int fdesc) FAST_FUNC; #else #define selinux_preserve_fcontext(fdesc) ((void)0) #endif extern void selinux_or_die(void) FAST_FUNC; /* systemd support */ #define SD_LISTEN_FDS_START 3 int sd_listen_fds(void); /* setup_environment: * if chdir pw->pw_dir: ok: else if to_tmp == 1: goto /tmp else: goto / or die * if clear_env = 1: cd(pw->pw_dir), clear environment, then set * TERM=(old value) * USER=pw->pw_name, LOGNAME=pw->pw_name * PATH=bb_default_[root_]path * HOME=pw->pw_dir * SHELL=shell * else if change_env = 1: * if not root (if pw->pw_uid != 0): * USER=pw->pw_name, LOGNAME=pw->pw_name * HOME=pw->pw_dir * SHELL=shell * else does nothing */ #define SETUP_ENV_CHANGEENV (1 << 0) #define SETUP_ENV_CLEARENV (1 << 1) #define SETUP_ENV_TO_TMP (1 << 2) #define SETUP_ENV_NO_CHDIR (1 << 4) void setup_environment(const char *shell, int flags, const struct passwd *pw) FAST_FUNC; void nuke_str(char *str) FAST_FUNC; int ask_and_check_password_extended(const struct passwd *pw, int timeout, const char *prompt) FAST_FUNC; int ask_and_check_password(const struct passwd *pw) FAST_FUNC; /* Returns a malloced string */ #if !ENABLE_USE_BB_CRYPT #define pw_encrypt(clear, salt, cleanup) pw_encrypt(clear, salt) #endif extern char *pw_encrypt(const char *clear, const char *salt, int cleanup) FAST_FUNC; extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC; /* * rnd is additional random input. New one is returned. * Useful if you call crypt_make_salt many times in a row: * rnd = crypt_make_salt(buf1, 4, 0); * rnd = crypt_make_salt(buf2, 4, rnd); * rnd = crypt_make_salt(buf3, 4, rnd); * (otherwise we risk having same salt generated) */ extern int crypt_make_salt(char *p, int cnt /*, int rnd*/) FAST_FUNC; /* "$N$" + sha_salt_16_bytes + NUL */ #define MAX_PW_SALT_LEN (3 + 16 + 1) extern char* crypt_make_pw_salt(char p[MAX_PW_SALT_LEN], const char *algo) FAST_FUNC; /* Returns number of lines changed, or -1 on error */ #if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP) #define update_passwd(filename, username, data, member) \ update_passwd(filename, username, data) #endif extern int update_passwd(const char *filename, const char *username, const char *data, const char *member) FAST_FUNC; int index_in_str_array(const char *const string_array[], const char *key) FAST_FUNC; int index_in_strings(const char *strings, const char *key) FAST_FUNC; int index_in_substr_array(const char *const string_array[], const char *key) FAST_FUNC; int index_in_substrings(const char *strings, const char *key) FAST_FUNC; const char *nth_string(const char *strings, int n) FAST_FUNC; extern void print_login_issue(const char *issue_file, const char *tty) FAST_FUNC; extern void print_login_prompt(void) FAST_FUNC; char *xmalloc_ttyname(int fd) FAST_FUNC RETURNS_MALLOC; /* NB: typically you want to pass fd 0, not 1. Think 'applet | grep something' */ int get_terminal_width_height(int fd, unsigned *width, unsigned *height) FAST_FUNC; int tcsetattr_stdin_TCSANOW(const struct termios *tp) FAST_FUNC; /* NB: "unsigned request" is crucial! "int request" will break some arches! */ int ioctl_or_perror(int fd, unsigned request, void *argp, const char *fmt,...) __attribute__ ((format (printf, 4, 5))) FAST_FUNC; int ioctl_or_perror_and_die(int fd, unsigned request, void *argp, const char *fmt,...) __attribute__ ((format (printf, 4, 5))) FAST_FUNC; #if ENABLE_IOCTL_HEX2STR_ERROR int bb_ioctl_or_warn(int fd, unsigned request, void *argp, const char *ioctl_name) FAST_FUNC; int bb_xioctl(int fd, unsigned request, void *argp, const char *ioctl_name) FAST_FUNC; #define ioctl_or_warn(fd,request,argp) bb_ioctl_or_warn(fd,request,argp,#request) #define xioctl(fd,request,argp) bb_xioctl(fd,request,argp,#request) #else int bb_ioctl_or_warn(int fd, unsigned request, void *argp) FAST_FUNC; int bb_xioctl(int fd, unsigned request, void *argp) FAST_FUNC; #define ioctl_or_warn(fd,request,argp) bb_ioctl_or_warn(fd,request,argp) #define xioctl(fd,request,argp) bb_xioctl(fd,request,argp) #endif char *is_in_ino_dev_hashtable(const struct stat *statbuf) FAST_FUNC; void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) FAST_FUNC; void reset_ino_dev_hashtable(void) FAST_FUNC; #ifdef __GLIBC__ /* At least glibc has horrendously large inline for this, so wrap it */ unsigned long long bb_makedev(unsigned major, unsigned minor) FAST_FUNC; #undef makedev #define makedev(a,b) bb_makedev(a,b) #endif /* "Keycodes" that report an escape sequence. * We use something which fits into signed char, * yet doesn't represent any valid Unicode character. * Also, -1 is reserved for error indication and we don't use it. */ enum { KEYCODE_UP = -2, KEYCODE_DOWN = -3, KEYCODE_RIGHT = -4, KEYCODE_LEFT = -5, KEYCODE_HOME = -6, KEYCODE_END = -7, KEYCODE_INSERT = -8, KEYCODE_DELETE = -9, KEYCODE_PAGEUP = -10, KEYCODE_PAGEDOWN = -11, // -12 is reserved for Alt/Ctrl/Shift-TAB #if 0 KEYCODE_FUN1 = -13, KEYCODE_FUN2 = -14, KEYCODE_FUN3 = -15, KEYCODE_FUN4 = -16, KEYCODE_FUN5 = -17, KEYCODE_FUN6 = -18, KEYCODE_FUN7 = -19, KEYCODE_FUN8 = -20, KEYCODE_FUN9 = -21, KEYCODE_FUN10 = -22, KEYCODE_FUN11 = -23, KEYCODE_FUN12 = -24, #endif /* Be sure that last defined value is small enough * to not interfere with Alt/Ctrl/Shift bits. * So far we do not exceed -31 (0xfff..fffe1), * which gives us three upper bits in LSB to play with. */ //KEYCODE_SHIFT_TAB = (-12) & ~0x80, //KEYCODE_SHIFT_... = KEYCODE_... & ~0x80, //KEYCODE_CTRL_UP = KEYCODE_UP & ~0x40, //KEYCODE_CTRL_DOWN = KEYCODE_DOWN & ~0x40, KEYCODE_CTRL_RIGHT = KEYCODE_RIGHT & ~0x40, KEYCODE_CTRL_LEFT = KEYCODE_LEFT & ~0x40, //KEYCODE_ALT_UP = KEYCODE_UP & ~0x20, //KEYCODE_ALT_DOWN = KEYCODE_DOWN & ~0x20, KEYCODE_ALT_RIGHT = KEYCODE_RIGHT & ~0x20, KEYCODE_ALT_LEFT = KEYCODE_LEFT & ~0x20, KEYCODE_CURSOR_POS = -0x100, /* 0xfff..fff00 */ /* How long is the longest ESC sequence we know? * We want it big enough to be able to contain * cursor position sequence "ESC [ 9999 ; 9999 R" */ KEYCODE_BUFFER_SIZE = 16 }; /* Note: fd may be in blocking or non-blocking mode, both make sense. * For one, less uses non-blocking mode. * Only the first read syscall inside read_key may block indefinitely * (unless fd is in non-blocking mode), * subsequent reads will time out after a few milliseconds. * Return of -1 means EOF or error (errno == 0 on EOF). * buffer[0] is used as a counter of buffered chars and must be 0 * on first call. * timeout: * -2: do not poll for input; * -1: poll(-1) (i.e. block); * >=0: poll for TIMEOUT milliseconds, return -1/EAGAIN on timeout */ int64_t read_key(int fd, char *buffer, int timeout) FAST_FUNC; void read_key_ungets(char *buffer, const char *str, unsigned len) FAST_FUNC; #if ENABLE_FEATURE_EDITING /* It's NOT just ENABLEd or disabled. It's a number: */ # if defined CONFIG_FEATURE_EDITING_HISTORY && CONFIG_FEATURE_EDITING_HISTORY > 0 # define MAX_HISTORY (CONFIG_FEATURE_EDITING_HISTORY + 0) unsigned size_from_HISTFILESIZE(const char *hp) FAST_FUNC; # else # define MAX_HISTORY 0 # endif typedef struct line_input_t { int flags; const char *path_lookup; # if MAX_HISTORY int cnt_history; int cur_history; int max_history; /* must never be <= 0 */ # if ENABLE_FEATURE_EDITING_SAVEHISTORY /* meaning of this field depends on FEATURE_EDITING_SAVE_ON_EXIT: * if !FEATURE_EDITING_SAVE_ON_EXIT: "how many lines are * in on-disk history" * if FEATURE_EDITING_SAVE_ON_EXIT: "how many in-memory lines are * also in on-disk history (and thus need to be skipped on save)" */ unsigned cnt_history_in_file; const char *hist_file; # endif char *history[MAX_HISTORY + 1]; # endif } line_input_t; enum { DO_HISTORY = 1 * (MAX_HISTORY > 0), TAB_COMPLETION = 2 * ENABLE_FEATURE_TAB_COMPLETION, USERNAME_COMPLETION = 4 * ENABLE_FEATURE_USERNAME_COMPLETION, VI_MODE = 8 * ENABLE_FEATURE_EDITING_VI, WITH_PATH_LOOKUP = 0x10, FOR_SHELL = DO_HISTORY | TAB_COMPLETION | USERNAME_COMPLETION, }; line_input_t *new_line_input_t(int flags) FAST_FUNC; /* So far static: void free_line_input_t(line_input_t *n) FAST_FUNC; */ /* * maxsize must be >= 2. * Returns: * -1 on read errors or EOF, or on bare Ctrl-D, * 0 on ctrl-C (the line entered is still returned in 'command'), * >0 length of input string, including terminating '\n' */ int read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize, int timeout) FAST_FUNC; void show_history(const line_input_t *st) FAST_FUNC; # if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT void save_history(line_input_t *st); # endif #else #define MAX_HISTORY 0 int read_line_input(const char* prompt, char* command, int maxsize) FAST_FUNC; #define read_line_input(state, prompt, command, maxsize, timeout) \ read_line_input(prompt, command, maxsize) #endif #ifndef COMM_LEN # ifdef TASK_COMM_LEN enum { COMM_LEN = TASK_COMM_LEN }; # else /* synchronize with sizeof(task_struct.comm) in /usr/include/linux/sched.h */ enum { COMM_LEN = 16 }; # endif #endif struct smaprec { unsigned long mapped_rw; unsigned long mapped_ro; unsigned long shared_clean; unsigned long shared_dirty; unsigned long private_clean; unsigned long private_dirty; unsigned long stack; unsigned long smap_pss, smap_swap; unsigned long smap_size; unsigned long smap_start; char smap_mode[5]; char *smap_name; }; #if !ENABLE_PMAP #define procps_read_smaps(pid, total, cb, data) \ procps_read_smaps(pid, total) #endif int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, void (*cb)(struct smaprec *, void *), void *data); typedef struct procps_status_t { DIR *dir; IF_FEATURE_SHOW_THREADS(DIR *task_dir;) uint8_t shift_pages_to_bytes; uint8_t shift_pages_to_kb; /* Fields are set to 0/NULL if failed to determine (or not requested) */ uint16_t argv_len; char *argv0; char *exe; IF_SELINUX(char *context;) IF_FEATURE_SHOW_THREADS(unsigned main_thread_pid;) /* Everything below must contain no ptrs to malloc'ed data: * it is memset(0) for each process in procps_scan() */ unsigned long vsz, rss; /* we round it to kbytes */ unsigned long stime, utime; unsigned long start_time; unsigned pid; unsigned ppid; unsigned pgid; unsigned sid; unsigned uid; unsigned gid; #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS unsigned ruid; unsigned rgid; int niceness; #endif unsigned tty_major,tty_minor; #if ENABLE_FEATURE_TOPMEM struct smaprec smaps; #endif char state[4]; /* basename of executable in exec(2), read from /proc/N/stat * (if executable is symlink or script, it is NOT replaced * by link target or interpreter name) */ char comm[COMM_LEN]; /* user/group? - use passwd/group parsing functions */ #if ENABLE_FEATURE_TOP_SMP_PROCESS int last_seen_on_cpu; #endif } procps_status_t; /* flag bits for procps_scan(xx, flags) calls */ enum { PSSCAN_PID = 1 << 0, PSSCAN_PPID = 1 << 1, PSSCAN_PGID = 1 << 2, PSSCAN_SID = 1 << 3, PSSCAN_UIDGID = 1 << 4, PSSCAN_COMM = 1 << 5, /* PSSCAN_CMD = 1 << 6, - use read_cmdline instead */ PSSCAN_ARGV0 = 1 << 7, PSSCAN_EXE = 1 << 8, PSSCAN_STATE = 1 << 9, PSSCAN_VSZ = 1 << 10, PSSCAN_RSS = 1 << 11, PSSCAN_STIME = 1 << 12, PSSCAN_UTIME = 1 << 13, PSSCAN_TTY = 1 << 14, PSSCAN_SMAPS = (1 << 15) * ENABLE_FEATURE_TOPMEM, /* NB: used by find_pid_by_name(). Any applet using it * needs to be mentioned here. */ PSSCAN_ARGVN = (1 << 16) * (ENABLE_KILLALL || ENABLE_PGREP || ENABLE_PKILL || ENABLE_PIDOF || ENABLE_SESTATUS ), PSSCAN_CONTEXT = (1 << 17) * ENABLE_SELINUX, PSSCAN_START_TIME = 1 << 18, PSSCAN_CPU = (1 << 19) * ENABLE_FEATURE_TOP_SMP_PROCESS, PSSCAN_NICE = (1 << 20) * ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS, PSSCAN_RUIDGID = (1 << 21) * ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS, PSSCAN_TASKS = (1 << 22) * ENABLE_FEATURE_SHOW_THREADS, }; //procps_status_t* alloc_procps_scan(void) FAST_FUNC; void free_procps_scan(procps_status_t* sp) FAST_FUNC; procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC; /* Format cmdline (up to col chars) into char buf[size] */ /* Puts [comm] if cmdline is empty (-> process is a kernel thread) */ void read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC; pid_t *find_pid_by_name(const char* procName) FAST_FUNC; pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC; int starts_with_cpu(const char *str) FAST_FUNC; unsigned get_cpu_count(void) FAST_FUNC; /* Use strict=1 if you process input from untrusted source: * it will return NULL on invalid %xx (bad hex chars) * and str + 1 if decoded char is / or NUL. * In non-strict mode, it always succeeds (returns str), * and also it additionally decoded '+' to space. */ char *percent_decode_in_place(char *str, int strict) FAST_FUNC; extern const char bb_uuenc_tbl_base64[] ALIGN1; extern const char bb_uuenc_tbl_std[] ALIGN1; void bb_uuencode(char *store, const void *s, int length, const char *tbl) FAST_FUNC; enum { BASE64_FLAG_UU_STOP = 0x100, /* Sign-extends to a value which never matches fgetc result: */ BASE64_FLAG_NO_STOP_CHAR = 0x80, }; const char *decode_base64(char **pp_dst, const char *src) FAST_FUNC; void read_base64(FILE *src_stream, FILE *dst_stream, int flags) FAST_FUNC; typedef struct md5_ctx_t { uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */ void (*process_block)(struct md5_ctx_t*) FAST_FUNC; uint64_t total64; /* must be directly before hash[] */ uint32_t hash[8]; /* 4 elements for md5, 5 for sha1, 8 for sha256 */ } md5_ctx_t; typedef struct md5_ctx_t sha1_ctx_t; typedef struct md5_ctx_t sha256_ctx_t; typedef struct sha512_ctx_t { uint64_t total64[2]; /* must be directly before hash[] */ uint64_t hash[8]; uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */ } sha512_ctx_t; typedef struct sha3_ctx_t { uint64_t state[25]; unsigned bytes_queued; } sha3_ctx_t; void md5_begin(md5_ctx_t *ctx) FAST_FUNC; void md5_hash(md5_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; void md5_end(md5_ctx_t *ctx, void *resbuf) FAST_FUNC; void sha1_begin(sha1_ctx_t *ctx) FAST_FUNC; #define sha1_hash md5_hash void sha1_end(sha1_ctx_t *ctx, void *resbuf) FAST_FUNC; void sha256_begin(sha256_ctx_t *ctx) FAST_FUNC; #define sha256_hash md5_hash #define sha256_end sha1_end void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC; void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; void sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC; void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC; void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; void sha3_end(sha3_ctx_t *ctx, void *resbuf) FAST_FUNC; extern uint32_t *global_crc32_table; uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC; uint32_t crc32_block_endian1(uint32_t val, const void *buf, unsigned len, uint32_t *crc_table) FAST_FUNC; uint32_t crc32_block_endian0(uint32_t val, const void *buf, unsigned len, uint32_t *crc_table) FAST_FUNC; typedef struct masks_labels_t { const char *labels; const int masks[]; } masks_labels_t; int print_flags_separated(const int *masks, const char *labels, int flags, const char *separator) FAST_FUNC; int print_flags(const masks_labels_t *ml, int flags) FAST_FUNC; typedef struct bb_progress_t { unsigned last_size; unsigned last_update_sec; unsigned last_change_sec; unsigned start_sec; const char *curfile; } bb_progress_t; #define is_bb_progress_inited(p) ((p)->curfile != NULL) #define bb_progress_free(p) do { \ if (ENABLE_UNICODE_SUPPORT) free((char*)((p)->curfile)); \ (p)->curfile = NULL; \ } while (0) void bb_progress_init(bb_progress_t *p, const char *curfile) FAST_FUNC; void bb_progress_update(bb_progress_t *p, uoff_t beg_range, uoff_t transferred, uoff_t totalsize) FAST_FUNC; extern const char *applet_name; /* Some older linkers don't perform string merging, we used to have common strings * as global arrays to do it by hand. But: * (1) newer linkers do it themselves, * (2) however, they DONT merge string constants with global arrays, * even if the value is the same (!). Thus global arrays actually * increased size a bit: for example, "/etc/passwd" string from libc * wasn't merged with bb_path_passwd_file[] array! * Therefore now we use #defines. */ /* "BusyBox vN.N.N (timestamp or extra_version)" */ extern const char bb_banner[] ALIGN1; extern const char bb_msg_memory_exhausted[] ALIGN1; extern const char bb_msg_invalid_date[] ALIGN1; #define bb_msg_read_error "read error" #define bb_msg_write_error "write error" extern const char bb_msg_unknown[] ALIGN1; extern const char bb_msg_can_not_create_raw_socket[] ALIGN1; extern const char bb_msg_perm_denied_are_you_root[] ALIGN1; extern const char bb_msg_you_must_be_root[] ALIGN1; extern const char bb_msg_requires_arg[] ALIGN1; extern const char bb_msg_invalid_arg[] ALIGN1; extern const char bb_msg_standard_input[] ALIGN1; extern const char bb_msg_standard_output[] ALIGN1; /* NB: (bb_hexdigits_upcase[i] | 0x20) -> lowercase hex digit */ extern const char bb_hexdigits_upcase[] ALIGN1; extern const char bb_path_wtmp_file[] ALIGN1; /* Busybox mount uses either /proc/mounts or /etc/mtab to * get the list of currently mounted filesystems */ #define bb_path_mtab_file IF_FEATURE_MTAB_SUPPORT("/etc/mtab")IF_NOT_FEATURE_MTAB_SUPPORT("/proc/mounts") #define bb_path_passwd_file _PATH_PASSWD #define bb_path_group_file _PATH_GROUP #define bb_path_shadow_file _PATH_SHADOW #define bb_path_gshadow_file _PATH_GSHADOW #define bb_path_motd_file "/etc/motd" #define bb_dev_null "/dev/null" extern const char bb_busybox_exec_path[] ALIGN1; /* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin, * but I want to save a few bytes here */ extern const char bb_PATH_root_path[] ALIGN1; /* "PATH=/sbin:/usr/sbin:/bin:/usr/bin" */ #define bb_default_root_path (bb_PATH_root_path + sizeof("PATH")) #define bb_default_path (bb_PATH_root_path + sizeof("PATH=/sbin:/usr/sbin")) extern const int const_int_0; extern const int const_int_1; /* Providing hard guarantee on minimum size (think of BUFSIZ == 128) */ enum { COMMON_BUFSIZE = (BUFSIZ >= 256*sizeof(void*) ? BUFSIZ+1 : 256*sizeof(void*)) }; extern char bb_common_bufsiz1[COMMON_BUFSIZE]; /* This struct is deliberately not defined. */ /* See docs/keep_data_small.txt */ struct globals; /* '*const' ptr makes gcc optimize code much better. * Magic prevents ptr_to_globals from going into rodata. * If you want to assign a value, use SET_PTR_TO_GLOBALS(x) */ extern struct globals *const ptr_to_globals; /* At least gcc 3.4.6 on mipsel system needs optimization barrier */ #define barrier() __asm__ __volatile__("":::"memory") #define SET_PTR_TO_GLOBALS(x) do { \ (*(struct globals**)&ptr_to_globals) = (void*)(x); \ barrier(); \ } while (0) #define FREE_PTR_TO_GLOBALS() do { \ if (ENABLE_FEATURE_CLEAN_UP) { \ free(ptr_to_globals); \ } \ } while (0) /* You can change LIBBB_DEFAULT_LOGIN_SHELL, but don't use it, * use bb_default_login_shell and following defines. * If you change LIBBB_DEFAULT_LOGIN_SHELL, * don't forget to change increment constant. */ #define LIBBB_DEFAULT_LOGIN_SHELL "-/bin/sh" extern const char bb_default_login_shell[] ALIGN1; /* "/bin/sh" */ #define DEFAULT_SHELL (bb_default_login_shell+1) /* "sh" */ #define DEFAULT_SHELL_SHORT_NAME (bb_default_login_shell+6) /* The following devices are the same on all systems. */ #define CURRENT_TTY "/dev/tty" #define DEV_CONSOLE "/dev/console" #if defined(__FreeBSD_kernel__) # define CURRENT_VC CURRENT_TTY # define VC_1 "/dev/ttyv0" # define VC_2 "/dev/ttyv1" # define VC_3 "/dev/ttyv2" # define VC_4 "/dev/ttyv3" # define VC_5 "/dev/ttyv4" # define VC_FORMAT "/dev/ttyv%d" #elif defined(__GNU__) # define CURRENT_VC CURRENT_TTY # define VC_1 "/dev/tty1" # define VC_2 "/dev/tty2" # define VC_3 "/dev/tty3" # define VC_4 "/dev/tty4" # define VC_5 "/dev/tty5" # define VC_FORMAT "/dev/tty%d" #elif ENABLE_FEATURE_DEVFS /*Linux, obsolete devfs names */ # define CURRENT_VC "/dev/vc/0" # define VC_1 "/dev/vc/1" # define VC_2 "/dev/vc/2" # define VC_3 "/dev/vc/3" # define VC_4 "/dev/vc/4" # define VC_5 "/dev/vc/5" # define VC_FORMAT "/dev/vc/%d" # define LOOP_FORMAT "/dev/loop/%u" # define LOOP_NAMESIZE (sizeof("/dev/loop/") + sizeof(int)*3 + 1) # define LOOP_NAME "/dev/loop/" # define FB_0 "/dev/fb/0" #else /*Linux, normal names */ # define CURRENT_VC "/dev/tty0" # define VC_1 "/dev/tty1" # define VC_2 "/dev/tty2" # define VC_3 "/dev/tty3" # define VC_4 "/dev/tty4" # define VC_5 "/dev/tty5" # define VC_FORMAT "/dev/tty%d" # define LOOP_FORMAT "/dev/loop%u" # define LOOP_NAMESIZE (sizeof("/dev/loop") + sizeof(int)*3 + 1) # define LOOP_NAME "/dev/loop" # define FB_0 "/dev/fb0" #endif #define ARRAY_SIZE(x) ((unsigned)(sizeof(x) / sizeof((x)[0]))) /* We redefine ctype macros. Unicode-correct handling of char types * can't be done with such byte-oriented operations anyway, * we don't lose anything. */ #undef isalnum #undef isalpha #undef isascii #undef isblank #undef iscntrl #undef isdigit #undef isgraph #undef islower #undef isprint #undef ispunct #undef isspace #undef isupper #undef isxdigit #undef toupper #undef tolower /* We save ~500 bytes on isdigit alone. * BTW, x86 likes (unsigned char) cast more than (unsigned). */ /* These work the same for ASCII and Unicode, * assuming no one asks "is this a *Unicode* letter?" using isalpha(letter) */ #define isascii(a) ((unsigned char)(a) <= 0x7f) #define isdigit(a) ((unsigned char)((a) - '0') <= 9) #define isupper(a) ((unsigned char)((a) - 'A') <= ('Z' - 'A')) #define islower(a) ((unsigned char)((a) - 'a') <= ('z' - 'a')) #define isalpha(a) ((unsigned char)(((a)|0x20) - 'a') <= ('z' - 'a')) #define isblank(a) ({ unsigned char bb__isblank = (a); bb__isblank == ' ' || bb__isblank == '\t'; }) #define iscntrl(a) ({ unsigned char bb__iscntrl = (a); bb__iscntrl < ' ' || bb__iscntrl == 0x7f; }) /* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space. * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13. */ #define isspace(a) ({ unsigned char bb__isspace = (a) - 9; bb__isspace == (' ' - 9) || bb__isspace <= (13 - 9); }) // Unsafe wrt NUL: #define ispunct(a) (strchr("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", (a)) != NULL) #define ispunct(a) (strchrnul("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", (a))[0]) // Bigger code: #define isalnum(a) ({ unsigned char bb__isalnum = (a) - '0'; bb__isalnum <= 9 || ((bb__isalnum - ('A' - '0')) & 0xdf) <= 25; }) #define isalnum(a) bb_ascii_isalnum(a) static ALWAYS_INLINE int bb_ascii_isalnum(unsigned char a) { unsigned char b = a - '0'; if (b <= 9) return (b <= 9); b = (a|0x20) - 'a'; return b <= 'z' - 'a'; } #define isxdigit(a) bb_ascii_isxdigit(a) static ALWAYS_INLINE int bb_ascii_isxdigit(unsigned char a) { unsigned char b = a - '0'; if (b <= 9) return (b <= 9); b = (a|0x20) - 'a'; return b <= 'f' - 'a'; } #define toupper(a) bb_ascii_toupper(a) static ALWAYS_INLINE unsigned char bb_ascii_toupper(unsigned char a) { unsigned char b = a - 'a'; if (b <= ('z' - 'a')) a -= 'a' - 'A'; return a; } #define tolower(a) bb_ascii_tolower(a) static ALWAYS_INLINE unsigned char bb_ascii_tolower(unsigned char a) { unsigned char b = a - 'A'; if (b <= ('Z' - 'A')) a += 'a' - 'A'; return a; } /* In ASCII and Unicode, these are likely to be very different. * Let's prevent ambiguous usage from the start */ #define isgraph(a) isgraph_is_ambiguous_dont_use(a) #define isprint(a) isprint_is_ambiguous_dont_use(a) /* NB: must not treat EOF as isgraph or isprint */ #define isgraph_asciionly(a) ((unsigned)((a) - 0x21) <= 0x7e - 0x21) #define isprint_asciionly(a) ((unsigned)((a) - 0x20) <= 0x7e - 0x20) POP_SAVED_FUNCTION_VISIBILITY #endif busybox-1.22.1/include/xatonum.h0000644000000000000000000001637512263563520015275 0ustar rootroot/* 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 source tree. */ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* Provides extern declarations of functions */ #define DECLARE_STR_CONV(type, T, UT) \ \ unsigned type xstrto##UT##_range_sfx(const char *str, int b, unsigned type l, unsigned type u, const struct suffix_mult *sfx) FAST_FUNC; \ unsigned type xstrto##UT##_range(const char *str, int b, unsigned type l, unsigned type u) FAST_FUNC; \ unsigned type xstrto##UT##_sfx(const char *str, int b, const struct suffix_mult *sfx) FAST_FUNC; \ unsigned type xstrto##UT(const char *str, int b) FAST_FUNC; \ unsigned type xato##UT##_range_sfx(const char *str, unsigned type l, unsigned type u, const struct suffix_mult *sfx) FAST_FUNC; \ unsigned type xato##UT##_range(const char *str, unsigned type l, unsigned type u) FAST_FUNC; \ unsigned type xato##UT##_sfx(const char *str, const struct suffix_mult *sfx) FAST_FUNC; \ unsigned type xato##UT(const char *str) FAST_FUNC; \ type xstrto##T##_range_sfx(const char *str, int b, type l, type u, const struct suffix_mult *sfx) FAST_FUNC; \ type xstrto##T##_range(const char *str, int b, type l, type u) FAST_FUNC; \ type xstrto##T(const char *str, int b) FAST_FUNC; \ type xato##T##_range_sfx(const char *str, type l, type u, const struct suffix_mult *sfx) FAST_FUNC; \ type xato##T##_range(const char *str, type l, type u) FAST_FUNC; \ type xato##T##_sfx(const char *str, const struct suffix_mult *sfx) FAST_FUNC; \ type xato##T(const char *str) FAST_FUNC; \ /* Unsigned long long functions always exist */ DECLARE_STR_CONV(long long, ll, ull) /* Provides inline definitions of functions */ /* (useful for mapping them to the type of the same width) */ #define DEFINE_EQUIV_STR_CONV(narrow, N, W, UN, UW) \ \ static ALWAYS_INLINE \ unsigned narrow xstrto##UN##_range_sfx(const char *str, int b, unsigned narrow l, unsigned narrow u, const struct suffix_mult *sfx) \ { return xstrto##UW##_range_sfx(str, b, l, u, sfx); } \ static ALWAYS_INLINE \ unsigned narrow xstrto##UN##_range(const char *str, int b, unsigned narrow l, unsigned narrow u) \ { return xstrto##UW##_range(str, b, l, u); } \ static ALWAYS_INLINE \ unsigned narrow xstrto##UN##_sfx(const char *str, int b, const struct suffix_mult *sfx) \ { return xstrto##UW##_sfx(str, b, sfx); } \ static ALWAYS_INLINE \ unsigned narrow xstrto##UN(const char *str, int b) \ { return xstrto##UW(str, b); } \ static ALWAYS_INLINE \ unsigned narrow xato##UN##_range_sfx(const char *str, unsigned narrow l, unsigned narrow u, const struct suffix_mult *sfx) \ { return xato##UW##_range_sfx(str, l, u, sfx); } \ static ALWAYS_INLINE \ unsigned narrow xato##UN##_range(const char *str, unsigned narrow l, unsigned narrow u) \ { return xato##UW##_range(str, l, u); } \ static ALWAYS_INLINE \ unsigned narrow xato##UN##_sfx(const char *str, const struct suffix_mult *sfx) \ { return xato##UW##_sfx(str, sfx); } \ static ALWAYS_INLINE \ unsigned narrow xato##UN(const char *str) \ { return xato##UW(str); } \ static ALWAYS_INLINE \ narrow xstrto##N##_range_sfx(const char *str, int b, narrow l, narrow u, const struct suffix_mult *sfx) \ { return xstrto##W##_range_sfx(str, b, l, u, sfx); } \ static ALWAYS_INLINE \ narrow xstrto##N##_range(const char *str, int b, narrow l, narrow u) \ { return xstrto##W##_range(str, b, l, u); } \ static ALWAYS_INLINE \ narrow xstrto##N(const char *str, int b) \ { return xstrto##W(str, b); } \ static ALWAYS_INLINE \ narrow xato##N##_range_sfx(const char *str, narrow l, narrow u, const struct suffix_mult *sfx) \ { return xato##W##_range_sfx(str, l, u, sfx); } \ static ALWAYS_INLINE \ narrow xato##N##_range(const char *str, narrow l, narrow u) \ { return xato##W##_range(str, l, u); } \ static ALWAYS_INLINE \ narrow xato##N##_sfx(const char *str, const struct suffix_mult *sfx) \ { return xato##W##_sfx(str, sfx); } \ static ALWAYS_INLINE \ narrow xato##N(const char *str) \ { return xato##W(str); } \ /* If long == long long, then just map them one-to-one */ #if ULONG_MAX == ULLONG_MAX DEFINE_EQUIV_STR_CONV(long, l, ll, ul, ull) #else /* Else provide extern defs */ DECLARE_STR_CONV(long, l, ul) #endif /* Same for int -> [long] long */ #if UINT_MAX == ULLONG_MAX DEFINE_EQUIV_STR_CONV(int, i, ll, u, ull) #elif UINT_MAX == ULONG_MAX DEFINE_EQUIV_STR_CONV(int, i, l, u, ul) #else DECLARE_STR_CONV(int, i, u) #endif /* Specialized */ uint32_t BUG_xatou32_unimplemented(void); static ALWAYS_INLINE uint32_t xatou32(const char *numstr) { if (UINT_MAX == 0xffffffff) return xatou(numstr); if (ULONG_MAX == 0xffffffff) return xatoul(numstr); return BUG_xatou32_unimplemented(); } /* Non-aborting kind of convertors: bb_strto[u][l]l */ /* On exit: errno = 0 only if there was non-empty, '\0' terminated value * errno = EINVAL if value was not '\0' terminated, but otherwise ok * Return value is still valid, caller should just check whether end[0] * is a valid terminating char for particular case. OTOH, if caller * requires '\0' terminated input, [s]he can just check errno == 0. * errno = ERANGE if value had alphanumeric terminating char ("1234abcg"). * errno = ERANGE if value is out of range, missing, etc. * errno = ERANGE if value had minus sign for strtouXX (even "-0" is not ok ) * return value is all-ones in this case. */ unsigned long long bb_strtoull(const char *arg, char **endp, int base) FAST_FUNC; long long bb_strtoll(const char *arg, char **endp, int base) FAST_FUNC; #if ULONG_MAX == ULLONG_MAX static ALWAYS_INLINE unsigned long bb_strtoul(const char *arg, char **endp, int base) { return bb_strtoull(arg, endp, base); } static ALWAYS_INLINE long bb_strtol(const char *arg, char **endp, int base) { return bb_strtoll(arg, endp, base); } #else unsigned long bb_strtoul(const char *arg, char **endp, int base) FAST_FUNC; long bb_strtol(const char *arg, char **endp, int base) FAST_FUNC; #endif #if UINT_MAX == ULLONG_MAX static ALWAYS_INLINE unsigned bb_strtou(const char *arg, char **endp, int base) { return bb_strtoull(arg, endp, base); } static ALWAYS_INLINE int bb_strtoi(const char *arg, char **endp, int base) { return bb_strtoll(arg, endp, base); } #elif UINT_MAX == ULONG_MAX static ALWAYS_INLINE unsigned bb_strtou(const char *arg, char **endp, int base) { return bb_strtoul(arg, endp, base); } static ALWAYS_INLINE int bb_strtoi(const char *arg, char **endp, int base) { return bb_strtol(arg, endp, base); } #else unsigned bb_strtou(const char *arg, char **endp, int base) FAST_FUNC; int bb_strtoi(const char *arg, char **endp, int base) FAST_FUNC; #endif uint32_t BUG_bb_strtou32_unimplemented(void); static ALWAYS_INLINE uint32_t bb_strtou32(const char *arg, char **endp, int base) { if (sizeof(uint32_t) == sizeof(unsigned)) return bb_strtou(arg, endp, base); if (sizeof(uint32_t) == sizeof(unsigned long)) return bb_strtoul(arg, endp, base); return BUG_bb_strtou32_unimplemented(); } static ALWAYS_INLINE int32_t bb_strtoi32(const char *arg, char **endp, int base) { if (sizeof(int32_t) == sizeof(int)) return bb_strtoi(arg, endp, base); if (sizeof(int32_t) == sizeof(long)) return bb_strtol(arg, endp, base); return BUG_bb_strtou32_unimplemented(); } /* Floating point */ double bb_strtod(const char *arg, char **endp) FAST_FUNC; POP_SAVED_FUNCTION_VISIBILITY busybox-1.22.1/include/grp_.h0000644000000000000000000001053512263563520014521 0ustar rootroot/* vi: set sw=4 ts=4: */ /* Copyright (C) 1991,92,95,96,97,98,99,2000,01 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ /* * POSIX Standard: 9.2.1 Group Database Access */ #ifndef BB_GRP_H #define BB_GRP_H 1 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* This file is #included after #include * We will use libc-defined structures, but will #define function names * so that function calls are directed to bb_internal_XXX replacements */ #undef endgrent #define setgrent bb_internal_setgrent #define endgrent bb_internal_endgrent #define getgrent bb_internal_getgrent #define fgetgrent bb_internal_fgetgrent #define putgrent bb_internal_putgrent #define getgrgid bb_internal_getgrgid #define getgrnam bb_internal_getgrnam #define getgrent_r bb_internal_getgrent_r #define getgrgid_r bb_internal_getgrgid_r #define getgrnam_r bb_internal_getgrnam_r #define fgetgrent_r bb_internal_fgetgrent_r #define getgrouplist bb_internal_getgrouplist #define initgroups bb_internal_initgroups /* All function names below should be remapped by #defines above * in order to not collide with libc names. */ /* Rewind the group-file stream. */ extern void setgrent(void); /* Close the group-file stream. */ extern void endgrent(void); #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS /* Read an entry from the group-file stream, opening it if necessary. */ extern struct group *getgrent(void); /* Read a group entry from STREAM. */ extern struct group *fgetgrent(FILE *__stream); /* Write the given entry onto the given stream. */ extern int putgrent(const struct group *__restrict __p, FILE *__restrict __f); #endif /* Search for an entry with a matching group ID. */ extern struct group *getgrgid(gid_t __gid); /* Search for an entry with a matching group name. */ extern struct group *getgrnam(const char *__name); /* Reentrant versions of some of the functions above. PLEASE NOTE: the `getgrent_r' function is not (yet) standardized. The interface may change in later versions of this library. But the interface is designed following the principals used for the other reentrant functions so the chances are good this is what the POSIX people would choose. */ extern int getgrent_r(struct group *__restrict __resultbuf, char *__restrict __buffer, size_t __buflen, struct group **__restrict __result); /* Search for an entry with a matching group ID. */ extern int getgrgid_r(gid_t __gid, struct group *__restrict __resultbuf, char *__restrict __buffer, size_t __buflen, struct group **__restrict __result); /* Search for an entry with a matching group name. */ extern int getgrnam_r(const char *__restrict __name, struct group *__restrict __resultbuf, char *__restrict __buffer, size_t __buflen, struct group **__restrict __result); /* Read a group entry from STREAM. This function is not standardized an probably never will. */ extern int fgetgrent_r(FILE *__restrict __stream, struct group *__restrict __resultbuf, char *__restrict __buffer, size_t __buflen, struct group **__restrict __result); /* Store at most *NGROUPS members of the group set for USER into *GROUPS. Also include GROUP. The actual number of groups found is returned in *NGROUPS. Return -1 if the if *NGROUPS is too small. */ extern int getgrouplist(const char *__user, gid_t __group, gid_t *__groups, int *__ngroups); /* Initialize the group set for the current user by reading the group database and using all groups of which USER is a member. Also include GROUP. */ extern int initgroups(const char *__user, gid_t __group); POP_SAVED_FUNCTION_VISIBILITY #endif busybox-1.22.1/include/platform.h0000644000000000000000000003517512267106022015417 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright 2006, Bernhard Reutner-Fischer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #ifndef BB_PLATFORM_H #define BB_PLATFORM_H 1 /* Convenience macros to test the version of gcc. */ #undef __GNUC_PREREQ #if defined __GNUC__ && defined __GNUC_MINOR__ # define __GNUC_PREREQ(maj, min) \ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) #else # define __GNUC_PREREQ(maj, min) 0 #endif /* __restrict is known in EGCS 1.2 and above. */ #if !__GNUC_PREREQ(2,92) # ifndef __restrict # define __restrict # endif #endif #if !__GNUC_PREREQ(2,7) # ifndef __attribute__ # define __attribute__(x) # endif #endif #undef inline #if defined(__STDC_VERSION__) && __STDC_VERSION__ > 199901L /* it's a keyword */ #elif __GNUC_PREREQ(2,7) # define inline __inline__ #else # define inline #endif #ifndef __const # define __const const #endif #define UNUSED_PARAM __attribute__ ((__unused__)) #define NORETURN __attribute__ ((__noreturn__)) /* "The malloc attribute is used to tell the compiler that a function * may be treated as if any non-NULL pointer it returns cannot alias * any other pointer valid when the function returns. This will often * improve optimization. Standard functions with this property include * malloc and calloc. realloc-like functions have this property as long * as the old pointer is never referred to (including comparing it * to the new pointer) after the function returns a non-NULL value." */ #define RETURNS_MALLOC __attribute__ ((malloc)) #define PACKED __attribute__ ((__packed__)) #define ALIGNED(m) __attribute__ ((__aligned__(m))) /* __NO_INLINE__: some gcc's do not honor inlining! :( */ #if __GNUC_PREREQ(3,0) && !defined(__NO_INLINE__) # define ALWAYS_INLINE __attribute__ ((always_inline)) inline /* I've seen a toolchain where I needed __noinline__ instead of noinline */ # define NOINLINE __attribute__((__noinline__)) # if !ENABLE_WERROR # define DEPRECATED __attribute__ ((__deprecated__)) # define UNUSED_PARAM_RESULT __attribute__ ((warn_unused_result)) # else # define DEPRECATED # define UNUSED_PARAM_RESULT # endif #else # define ALWAYS_INLINE inline # define NOINLINE # define DEPRECATED # define UNUSED_PARAM_RESULT #endif /* -fwhole-program makes all symbols local. The attribute externally_visible * forces a symbol global. */ #if __GNUC_PREREQ(4,1) # define EXTERNALLY_VISIBLE __attribute__(( visibility("default") )) //__attribute__ ((__externally_visible__)) #else # define EXTERNALLY_VISIBLE #endif /* At 4.4 gcc become much more anal about this, need to use "aliased" types */ #if __GNUC_PREREQ(4,4) # define FIX_ALIASING __attribute__((__may_alias__)) #else # define FIX_ALIASING #endif /* We use __extension__ in some places to suppress -pedantic warnings * about GCC extensions. This feature didn't work properly before * gcc 2.8. */ #if !__GNUC_PREREQ(2,8) # ifndef __extension__ # define __extension__ # endif #endif /* FAST_FUNC is a qualifier which (possibly) makes function call faster * and/or smaller by using modified ABI. It is usually only needed * on non-static, busybox internal functions. Recent versions of gcc * optimize statics automatically. FAST_FUNC on static is required * only if you need to match a function pointer's type */ #if __GNUC_PREREQ(3,0) && defined(i386) /* || defined(__x86_64__)? */ /* stdcall makes callee to pop arguments from stack, not caller */ # define FAST_FUNC __attribute__((regparm(3),stdcall)) /* #elif ... - add your favorite arch today! */ #else # define FAST_FUNC #endif /* Make all declarations hidden (-fvisibility flag only affects definitions) */ /* (don't include system headers after this until corresponding pop!) */ #if __GNUC_PREREQ(4,1) && !defined(__CYGWIN__) # define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN _Pragma("GCC visibility push(hidden)") # define POP_SAVED_FUNCTION_VISIBILITY _Pragma("GCC visibility pop") #else # define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN # define POP_SAVED_FUNCTION_VISIBILITY #endif /* gcc-2.95 had no va_copy but only __va_copy. */ #if !__GNUC_PREREQ(3,0) # include # if !defined va_copy && defined __va_copy # define va_copy(d,s) __va_copy((d),(s)) # endif #endif /* ---- Endian Detection ------------------------------------ */ #include #if defined(__digital__) && defined(__unix__) # include #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ || defined(__APPLE__) # include /* rlimit */ # include # define bswap_64 __bswap64 # define bswap_32 __bswap32 # define bswap_16 __bswap16 #else # include # include #endif #if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN # define BB_BIG_ENDIAN 1 # define BB_LITTLE_ENDIAN 0 #elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN # define BB_BIG_ENDIAN 0 # define BB_LITTLE_ENDIAN 1 #elif defined(_BYTE_ORDER) && _BYTE_ORDER == _BIG_ENDIAN # define BB_BIG_ENDIAN 1 # define BB_LITTLE_ENDIAN 0 #elif defined(_BYTE_ORDER) && _BYTE_ORDER == _LITTLE_ENDIAN # define BB_BIG_ENDIAN 0 # define BB_LITTLE_ENDIAN 1 #elif defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN # define BB_BIG_ENDIAN 1 # define BB_LITTLE_ENDIAN 0 #elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN # define BB_BIG_ENDIAN 0 # define BB_LITTLE_ENDIAN 1 #elif defined(__386__) # define BB_BIG_ENDIAN 0 # define BB_LITTLE_ENDIAN 1 #else # error "Can't determine endianness" #endif #if ULONG_MAX > 0xffffffff # define bb_bswap_64(x) bswap_64(x) #endif /* SWAP_LEnn means "convert CPU<->little_endian by swapping bytes" */ #if BB_BIG_ENDIAN # define SWAP_BE16(x) (x) # define SWAP_BE32(x) (x) # define SWAP_BE64(x) (x) # define SWAP_LE16(x) bswap_16(x) # define SWAP_LE32(x) bswap_32(x) # define SWAP_LE64(x) bb_bswap_64(x) # define IF_BIG_ENDIAN(...) __VA_ARGS__ # define IF_LITTLE_ENDIAN(...) #else # define SWAP_BE16(x) bswap_16(x) # define SWAP_BE32(x) bswap_32(x) # define SWAP_BE64(x) bb_bswap_64(x) # define SWAP_LE16(x) (x) # define SWAP_LE32(x) (x) # define SWAP_LE64(x) (x) # define IF_BIG_ENDIAN(...) # define IF_LITTLE_ENDIAN(...) __VA_ARGS__ #endif /* ---- Unaligned access ------------------------------------ */ #include typedef int bb__aliased_int FIX_ALIASING; typedef long bb__aliased_long FIX_ALIASING; typedef uint16_t bb__aliased_uint16_t FIX_ALIASING; typedef uint32_t bb__aliased_uint32_t FIX_ALIASING; typedef uint64_t bb__aliased_uint64_t FIX_ALIASING; /* NB: unaligned parameter should be a pointer, aligned one - * a lvalue. This makes it more likely to not swap them by mistake */ #if defined(i386) || defined(__x86_64__) || defined(__powerpc__) # define move_from_unaligned_int(v, intp) ((v) = *(bb__aliased_int*)(intp)) # define move_from_unaligned_long(v, longp) ((v) = *(bb__aliased_long*)(longp)) # define move_from_unaligned16(v, u16p) ((v) = *(bb__aliased_uint16_t*)(u16p)) # define move_from_unaligned32(v, u32p) ((v) = *(bb__aliased_uint32_t*)(u32p)) # define move_to_unaligned16(u16p, v) (*(bb__aliased_uint16_t*)(u16p) = (v)) # define move_to_unaligned32(u32p, v) (*(bb__aliased_uint32_t*)(u32p) = (v)) /* #elif ... - add your favorite arch today! */ #else /* performs reasonably well (gcc usually inlines memcpy here) */ # define move_from_unaligned_int(v, intp) (memcpy(&(v), (intp), sizeof(int))) # define move_from_unaligned_long(v, longp) (memcpy(&(v), (longp), sizeof(long))) # define move_from_unaligned16(v, u16p) (memcpy(&(v), (u16p), 2)) # define move_from_unaligned32(v, u32p) (memcpy(&(v), (u32p), 4)) # define move_to_unaligned16(u16p, v) do { \ uint16_t __t = (v); \ memcpy((u16p), &__t, 2); \ } while (0) # define move_to_unaligned32(u32p, v) do { \ uint32_t __t = (v); \ memcpy((u32p), &__t, 4); \ } while (0) #endif /* ---- Size-saving "small" ints (arch-dependent) ----------- */ #if defined(i386) || defined(__x86_64__) || defined(__mips__) || defined(__cris__) /* add other arches which benefit from this... */ typedef signed char smallint; typedef unsigned char smalluint; #else /* for arches where byte accesses generate larger code: */ typedef int smallint; typedef unsigned smalluint; #endif /* ISO C Standard: 7.16 Boolean type and values */ #if (defined __digital__ && defined __unix__) /* old system without (proper) C99 support */ # define bool smalluint #else /* modern system, so use it */ # include #endif /*----- Kernel versioning ------------------------------------*/ #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) #ifdef __UCLIBC__ # define UCLIBC_VERSION KERNEL_VERSION(__UCLIBC_MAJOR__, __UCLIBC_MINOR__, __UCLIBC_SUBLEVEL__) #else # define UCLIBC_VERSION 0 #endif /* ---- Miscellaneous --------------------------------------- */ #if defined __GLIBC__ \ || defined __UCLIBC__ \ || defined __dietlibc__ \ || defined __BIONIC__ \ || defined _NEWLIB_VERSION # include #endif /* Define bb_setpgrp */ #if defined(__digital__) && defined(__unix__) /* use legacy setpgrp(pid_t, pid_t) for now. move to platform.c */ # define bb_setpgrp() do { pid_t __me = getpid(); setpgrp(__me, __me); } while (0) #else # define bb_setpgrp() setpgrp() #endif /* fdprintf is more readable, we used it before dprintf was standardized */ #include #define fdprintf dprintf /* Useful for defeating gcc's alignment of "char message[]"-like data */ #if !defined(__s390__) /* on s390[x], non-word-aligned data accesses require larger code */ # define ALIGN1 __attribute__((aligned(1))) # define ALIGN2 __attribute__((aligned(2))) # define ALIGN4 __attribute__((aligned(4))) #else /* Arches which MUST have 2 or 4 byte alignment for everything are here */ # define ALIGN1 # define ALIGN2 # define ALIGN4 #endif /* * For 0.9.29 and svn, __ARCH_USE_MMU__ indicates no-mmu reliably. * For earlier versions there is no reliable way to check if we are building * for a mmu-less system. */ #if ENABLE_NOMMU || \ (defined __UCLIBC__ && \ UCLIBC_VERSION > KERNEL_VERSION(0, 9, 28) && \ !defined __ARCH_USE_MMU__) # define BB_MMU 0 # define USE_FOR_NOMMU(...) __VA_ARGS__ # define USE_FOR_MMU(...) #else # define BB_MMU 1 # define USE_FOR_NOMMU(...) # define USE_FOR_MMU(...) __VA_ARGS__ #endif #if defined(__digital__) && defined(__unix__) # include # include # define PRIu32 "u" # if !defined ADJ_OFFSET_SINGLESHOT && defined MOD_CLKA && defined MOD_OFFSET # define ADJ_OFFSET_SINGLESHOT (MOD_CLKA | MOD_OFFSET) # endif # if !defined ADJ_FREQUENCY && defined MOD_FREQUENCY # define ADJ_FREQUENCY MOD_FREQUENCY # endif # if !defined ADJ_TIMECONST && defined MOD_TIMECONST # define ADJ_TIMECONST MOD_TIMECONST # endif # if !defined ADJ_TICK && defined MOD_CLKB # define ADJ_TICK MOD_CLKB # endif #endif #if defined(__CYGWIN__) # define MAXSYMLINKS SYMLOOP_MAX #endif #if defined(ANDROID) || defined(__ANDROID__) # define BB_ADDITIONAL_PATH ":/system/sbin:/system/bin:/system/xbin" # define SYS_ioprio_set __NR_ioprio_set # define SYS_ioprio_get __NR_ioprio_get #endif /* ---- Who misses what? ------------------------------------ */ /* Assume all these functions and header files exist by default. * Platforms where it is not true will #undef them below. */ #define HAVE_CLEARENV 1 #define HAVE_FDATASYNC 1 #define HAVE_DPRINTF 1 #define HAVE_MEMRCHR 1 #define HAVE_MKDTEMP 1 #define HAVE_PTSNAME_R 1 #define HAVE_SETBIT 1 #define HAVE_SIGHANDLER_T 1 #define HAVE_STPCPY 1 #define HAVE_STRCASESTR 1 #define HAVE_STRCHRNUL 1 #define HAVE_STRSEP 1 #define HAVE_STRSIGNAL 1 #define HAVE_STRVERSCMP 1 #define HAVE_VASPRINTF 1 #define HAVE_UNLOCKED_STDIO 1 #define HAVE_UNLOCKED_LINE_OPS 1 #define HAVE_GETLINE 1 #define HAVE_XTABS 1 #define HAVE_MNTENT_H 1 #define HAVE_NET_ETHERNET_H 1 #define HAVE_SYS_STATFS_H 1 #if defined(__UCLIBC__) && UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32) # undef HAVE_STRVERSCMP #endif #if defined(__dietlibc__) # undef HAVE_STRCHRNUL #endif #if defined(__WATCOMC__) # undef HAVE_DPRINTF # undef HAVE_GETLINE # undef HAVE_MEMRCHR # undef HAVE_MKDTEMP # undef HAVE_SETBIT # undef HAVE_STPCPY # undef HAVE_STRCASESTR # undef HAVE_STRCHRNUL # undef HAVE_STRSEP # undef HAVE_STRSIGNAL # undef HAVE_STRVERSCMP # undef HAVE_VASPRINTF # undef HAVE_UNLOCKED_STDIO # undef HAVE_UNLOCKED_LINE_OPS # undef HAVE_NET_ETHERNET_H #endif #if defined(__CYGWIN__) # undef HAVE_CLEARENV # undef HAVE_FDPRINTF # undef HAVE_MEMRCHR # undef HAVE_PTSNAME_R # undef HAVE_STRVERSCMP # undef HAVE_UNLOCKED_LINE_OPS #endif /* These BSD-derived OSes share many similarities */ #if (defined __digital__ && defined __unix__) \ || defined __APPLE__ \ || defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ # undef HAVE_CLEARENV # undef HAVE_FDATASYNC # undef HAVE_GETLINE # undef HAVE_MNTENT_H # undef HAVE_PTSNAME_R # undef HAVE_SYS_STATFS_H # undef HAVE_SIGHANDLER_T # undef HAVE_STRVERSCMP # undef HAVE_XTABS # undef HAVE_DPRINTF # undef HAVE_UNLOCKED_STDIO # undef HAVE_UNLOCKED_LINE_OPS #endif #if defined(__FreeBSD__) || defined(__APPLE__) # undef HAVE_STRCHRNUL #endif #if defined(__NetBSD__) # define HAVE_GETLINE 1 /* Recent NetBSD versions have getline() */ #endif #if defined(__digital__) && defined(__unix__) # undef HAVE_STPCPY #endif #if defined(ANDROID) || defined(__ANDROID__) # undef HAVE_DPRINTF # undef HAVE_GETLINE # undef HAVE_STPCPY # undef HAVE_STRCHRNUL # undef HAVE_STRVERSCMP # undef HAVE_UNLOCKED_LINE_OPS # undef HAVE_NET_ETHERNET_H #endif /* * Now, define prototypes for all the functions defined in platform.c * These must come after all the HAVE_* macros are defined (or not) */ #ifndef HAVE_DPRINTF extern int dprintf(int fd, const char *format, ...); #endif #ifndef HAVE_MEMRCHR extern void *memrchr(const void *s, int c, size_t n) FAST_FUNC; #endif #ifndef HAVE_MKDTEMP extern char *mkdtemp(char *template) FAST_FUNC; #endif #ifndef HAVE_SETBIT # define setbit(a, b) ((a)[(b) >> 3] |= 1 << ((b) & 7)) # define clrbit(a, b) ((a)[(b) >> 3] &= ~(1 << ((b) & 7))) #endif #ifndef HAVE_SIGHANDLER_T typedef void (*sighandler_t)(int); #endif #ifndef HAVE_STPCPY extern char *stpcpy(char *p, const char *to_add) FAST_FUNC; #endif #ifndef HAVE_STRCASESTR extern char *strcasestr(const char *s, const char *pattern) FAST_FUNC; #endif #ifndef HAVE_STRCHRNUL extern char *strchrnul(const char *s, int c) FAST_FUNC; #endif #ifndef HAVE_STRSEP extern char *strsep(char **stringp, const char *delim) FAST_FUNC; #endif #ifndef HAVE_STRSIGNAL /* Not exactly the same: instead of "Stopped" it shows "STOP" etc */ # define strsignal(sig) get_signame(sig) #endif #ifndef HAVE_VASPRINTF extern int vasprintf(char **string_ptr, const char *format, va_list p) FAST_FUNC; #endif #ifndef HAVE_GETLINE # include /* for FILE */ # include /* size_t */ extern ssize_t getline(char **lineptr, size_t *n, FILE *stream) FAST_FUNC; #endif #endif busybox-1.22.1/include/fix_u32.h0000644000000000000000000000221712263563520015047 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * This header makes it easier to include kernel headers * which use u32 and such. * * Licensed under GPLv2, see file LICENSE in this source tree. */ #ifndef FIX_U32_H #define FIX_U32_H 1 /* Try hard to pull in u32 types and such. * Otherwise, #include "fix_u32.h" + #include * may end up typedef'ing bb_hack_u32 inside foo.h, * and repeated typedefs aren't allowed in C/C++. */ #include #include /* In case above includes still failed to provide the types, * provide them ourself */ #undef __u64 #undef u64 #undef u32 #undef u16 #undef u8 #undef __s64 #undef s64 #undef s32 #undef s16 #undef s8 #define __u64 bb_hack___u64 #define u64 bb_hack_u64 #define u32 bb_hack_u32 #define u16 bb_hack_u16 #define u8 bb_hack_u8 #define __s64 bb_hack___s64 #define s64 bb_hack_s64 #define s32 bb_hack_s32 #define s16 bb_hack_s16 #define s8 bb_hack_s8 typedef uint64_t __u64; typedef uint64_t u64; typedef uint32_t u32; typedef uint16_t u16; typedef uint8_t u8; typedef int64_t __s64; typedef int64_t s64; typedef int32_t s32; typedef int16_t s16; typedef int8_t s8; #endif busybox-1.22.1/include/liblzo_interface.h0000644000000000000000000000466112263563520017110 0ustar rootroot/* This file is part of the LZO real-time data compression library. Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer All Rights Reserved. Markus F.X.J. Oberhumer http://www.oberhumer.com/opensource/lzo/ The LZO library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The LZO library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the LZO library; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #define LZO1X #undef LZO1Y #undef assert /* static void die_at(int line) { bb_error_msg_and_die("internal error at %d", line); } #define assert(v) if (!(v)) die_at(__LINE__) */ #define assert(v) ((void)0) int lzo1x_1_compress(const uint8_t* src, unsigned src_len, uint8_t* dst, unsigned* dst_len, void* wrkmem); int lzo1x_1_15_compress(const uint8_t* src, unsigned src_len, uint8_t* dst, unsigned* dst_len, void* wrkmem); int lzo1x_999_compress_level(const uint8_t* in, unsigned in_len, uint8_t* out, unsigned* out_len, void* wrkmem, int compression_level); /* decompression */ //int lzo1x_decompress(const uint8_t* src, unsigned src_len, // uint8_t* dst, unsigned* dst_len, // void* wrkmem /* NOT USED */); /* safe decompression with overrun testing */ int lzo1x_decompress_safe(const uint8_t* src, unsigned src_len, uint8_t* dst, unsigned* dst_len, void* wrkmem /* NOT USED */); #define LZO_E_OK 0 #define LZO_E_ERROR (-1) #define LZO_E_OUT_OF_MEMORY (-2) /* [not used right now] */ #define LZO_E_NOT_COMPRESSIBLE (-3) /* [not used right now] */ #define LZO_E_INPUT_OVERRUN (-4) #define LZO_E_OUTPUT_OVERRUN (-5) #define LZO_E_LOOKBEHIND_OVERRUN (-6) #define LZO_E_EOF_NOT_FOUND (-7) #define LZO_E_INPUT_NOT_CONSUMED (-8) #define LZO_E_NOT_YET_IMPLEMENTED (-9) /* [not used right now] */ /* lzo-2.03/include/lzo/lzoconf.h */ #define LZO_VERSION 0x2030 busybox-1.22.1/include/bb_archive.h0000644000000000000000000002203712263563520015656 0ustar rootroot/* vi: set sw=4 ts=4: */ #ifndef UNARCHIVE_H #define UNARCHIVE_H 1 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN enum { #if BB_BIG_ENDIAN COMPRESS_MAGIC = 0x1f9d, GZIP_MAGIC = 0x1f8b, BZIP2_MAGIC = 256 * 'B' + 'Z', /* .xz signature: 0xfd, '7', 'z', 'X', 'Z', 0x00 */ /* More info at: http://tukaani.org/xz/xz-file-format.txt */ XZ_MAGIC1 = 256 * 0xfd + '7', XZ_MAGIC2 = 256 * (unsigned)(256 * (256 * 'z' + 'X') + 'Z') + 0, /* Different form: 32 bits, then 16 bits: */ /* (unsigned) cast suppresses "integer overflow in expression" warning */ XZ_MAGIC1a = 256 * (unsigned)(256 * (256 * 0xfd + '7') + 'z') + 'X', XZ_MAGIC2a = 256 * 'Z' + 0, #else COMPRESS_MAGIC = 0x9d1f, GZIP_MAGIC = 0x8b1f, BZIP2_MAGIC = 'B' + 'Z' * 256, XZ_MAGIC1 = 0xfd + '7' * 256, XZ_MAGIC2 = 'z' + ('X' + ('Z' + 0 * 256) * 256) * 256, XZ_MAGIC1a = 0xfd + ('7' + ('z' + 'X' * 256) * 256) * 256, XZ_MAGIC2a = 'Z' + 0 * 256, #endif }; typedef struct file_header_t { char *name; char *link_target; #if ENABLE_FEATURE_TAR_UNAME_GNAME char *tar__uname; char *tar__gname; #endif off_t size; uid_t uid; gid_t gid; mode_t mode; time_t mtime; dev_t device; } file_header_t; struct hardlinks_t; typedef struct archive_handle_t { /* Flags. 1st since it is most used member */ unsigned ah_flags; /* The raw stream as read from disk or stdin */ int src_fd; /* Define if the header and data component should be processed */ char FAST_FUNC (*filter)(struct archive_handle_t *); /* List of files that have been accepted */ llist_t *accept; /* List of files that have been rejected */ llist_t *reject; /* List of files that have successfully been worked on */ llist_t *passed; /* Currently processed file's header */ file_header_t *file_header; /* Process the header component, e.g. tar -t */ void FAST_FUNC (*action_header)(const file_header_t *); /* Process the data component, e.g. extract to filesystem */ void FAST_FUNC (*action_data)(struct archive_handle_t *); /* Function that skips data */ void FAST_FUNC (*seek)(int fd, off_t amount); /* Count processed bytes */ off_t offset; /* Archiver specific. Can make it a union if it ever gets big */ #define PAX_NEXT_FILE 0 #define PAX_GLOBAL 1 #if ENABLE_TAR || ENABLE_DPKG || ENABLE_DPKG_DEB smallint tar__end; # if ENABLE_FEATURE_TAR_GNU_EXTENSIONS char* tar__longname; char* tar__linkname; # endif # if ENABLE_FEATURE_TAR_TO_COMMAND char* tar__to_command; const char* tar__to_command_shell; # endif # if ENABLE_FEATURE_TAR_SELINUX char* tar__sctx[2]; # endif #endif #if ENABLE_CPIO || ENABLE_RPM2CPIO || ENABLE_RPM uoff_t cpio__blocks; struct hardlinks_t *cpio__hardlinks_to_create; struct hardlinks_t *cpio__created_hardlinks; #endif #if ENABLE_DPKG || ENABLE_DPKG_DEB /* Temporary storage */ char *dpkg__buffer; /* How to process any sub archive, e.g. get_header_tar_gz */ char FAST_FUNC (*dpkg__action_data_subarchive)(struct archive_handle_t *); /* Contains the handle to a sub archive */ struct archive_handle_t *dpkg__sub_archive; #endif #if ENABLE_FEATURE_AR_CREATE const char *ar__name; struct archive_handle_t *ar__out; #endif } archive_handle_t; /* bits in ah_flags */ #define ARCHIVE_RESTORE_DATE (1 << 0) #define ARCHIVE_CREATE_LEADING_DIRS (1 << 1) #define ARCHIVE_UNLINK_OLD (1 << 2) #define ARCHIVE_EXTRACT_QUIET (1 << 3) #define ARCHIVE_EXTRACT_NEWER (1 << 4) #define ARCHIVE_DONT_RESTORE_OWNER (1 << 5) #define ARCHIVE_DONT_RESTORE_PERM (1 << 6) #define ARCHIVE_NUMERIC_OWNER (1 << 7) #define ARCHIVE_O_TRUNC (1 << 8) #define ARCHIVE_REMEMBER_NAMES (1 << 9) #if ENABLE_RPM #define ARCHIVE_REPLACE_VIA_RENAME (1 << 10) #endif /* POSIX tar Header Block, from POSIX 1003.1-1990 */ #define TAR_BLOCK_SIZE 512 #define NAME_SIZE 100 #define NAME_SIZE_STR "100" typedef struct tar_header_t { /* byte offset */ char name[NAME_SIZE]; /* 0-99 */ char mode[8]; /* 100-107 */ char uid[8]; /* 108-115 */ char gid[8]; /* 116-123 */ char size[12]; /* 124-135 */ char mtime[12]; /* 136-147 */ char chksum[8]; /* 148-155 */ char typeflag; /* 156-156 */ char linkname[NAME_SIZE]; /* 157-256 */ /* POSIX: "ustar" NUL "00" */ /* GNU tar: "ustar " NUL */ /* Normally it's defined as magic[6] followed by * version[2], but we put them together to save code. */ char magic[8]; /* 257-264 */ char uname[32]; /* 265-296 */ char gname[32]; /* 297-328 */ char devmajor[8]; /* 329-336 */ char devminor[8]; /* 337-344 */ char prefix[155]; /* 345-499 */ char padding[12]; /* 500-512 (pad to exactly TAR_BLOCK_SIZE) */ } tar_header_t; struct BUG_tar_header { char c[sizeof(tar_header_t) == TAR_BLOCK_SIZE ? 1 : -1]; }; archive_handle_t *init_handle(void) FAST_FUNC; char filter_accept_all(archive_handle_t *archive_handle) FAST_FUNC; char filter_accept_list(archive_handle_t *archive_handle) FAST_FUNC; char filter_accept_list_reassign(archive_handle_t *archive_handle) FAST_FUNC; char filter_accept_reject_list(archive_handle_t *archive_handle) FAST_FUNC; void unpack_ar_archive(archive_handle_t *ar_archive) FAST_FUNC; void data_skip(archive_handle_t *archive_handle) FAST_FUNC; void data_extract_all(archive_handle_t *archive_handle) FAST_FUNC; void data_extract_to_stdout(archive_handle_t *archive_handle) FAST_FUNC; void data_extract_to_command(archive_handle_t *archive_handle) FAST_FUNC; void header_skip(const file_header_t *file_header) FAST_FUNC; void header_list(const file_header_t *file_header) FAST_FUNC; void header_verbose_list(const file_header_t *file_header) FAST_FUNC; char get_header_ar(archive_handle_t *archive_handle) FAST_FUNC; char get_header_cpio(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC; void seek_by_jump(int fd, off_t amount) FAST_FUNC; void seek_by_read(int fd, off_t amount) FAST_FUNC; const char *strip_unsafe_prefix(const char *str) FAST_FUNC; void data_align(archive_handle_t *archive_handle, unsigned boundary) FAST_FUNC; const llist_t *find_list_entry(const llist_t *list, const char *filename) FAST_FUNC; const llist_t *find_list_entry2(const llist_t *list, const char *filename) FAST_FUNC; /* A bit of bunzip2 internals are exposed for compressed help support: */ typedef struct bunzip_data bunzip_data; int start_bunzip(bunzip_data **bdp, int in_fd, const void *inbuf, int len) FAST_FUNC; /* NB: read_bunzip returns < 0 on error, or the number of *unfilled* bytes * in outbuf. IOW: on EOF returns len ("all bytes are not filled"), not 0: */ int read_bunzip(bunzip_data *bd, char *outbuf, int len) FAST_FUNC; void dealloc_bunzip(bunzip_data *bd) FAST_FUNC; /* Meaning and direction (input/output) of the fields are transformer-specific */ typedef struct transformer_aux_data_t { smallint check_signature; /* most often referenced member */ off_t bytes_out; off_t bytes_in; /* used in unzip code only: needs to know packed size */ uint32_t crc32; time_t mtime; /* gunzip code may set this on exit */ } transformer_aux_data_t; void init_transformer_aux_data(transformer_aux_data_t *aux) FAST_FUNC; int FAST_FUNC check_signature16(transformer_aux_data_t *aux, int src_fd, unsigned magic16) FAST_FUNC; IF_DESKTOP(long long) int inflate_unzip(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC; IF_DESKTOP(long long) int unpack_Z_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC; IF_DESKTOP(long long) int unpack_gz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC; IF_DESKTOP(long long) int unpack_bz2_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC; IF_DESKTOP(long long) int unpack_lzma_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC; IF_DESKTOP(long long) int unpack_xz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC; char* append_ext(char *filename, const char *expected_ext) FAST_FUNC; int bbunpack(char **argv, IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(transformer_aux_data_t *aux), char* FAST_FUNC (*make_new_name)(char *filename, const char *expected_ext), const char *expected_ext ) FAST_FUNC; void check_errors_in_children(int signo); #if BB_MMU void open_transformer(int fd, int check_signature, IF_DESKTOP(long long) int FAST_FUNC (*transformer)(transformer_aux_data_t *aux, int src_fd, int dst_fd) ) FAST_FUNC; #define open_transformer_with_sig(fd, transformer, transform_prog) open_transformer((fd), 1, (transformer)) #define open_transformer_with_no_sig(fd, transformer) open_transformer((fd), 0, (transformer)) #else void open_transformer(int fd, const char *transform_prog) FAST_FUNC; #define open_transformer_with_sig(fd, transformer, transform_prog) open_transformer((fd), (transform_prog)) /* open_transformer_with_no_sig() does not exist on NOMMU */ #endif POP_SAVED_FUNCTION_VISIBILITY #endif busybox-1.22.1/init/0000755000000000000000000000000012320365364012735 5ustar rootrootbusybox-1.22.1/init/bootchartd.c0000644000000000000000000003276112263563520015243 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //applet:IF_BOOTCHARTD(APPLET(bootchartd, BB_DIR_SBIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_BOOTCHARTD) += bootchartd.o //config:config BOOTCHARTD //config: bool "bootchartd" //config: default y //config: help //config: bootchartd is commonly used to profile the boot process //config: for the purpose of speeding it up. In this case, it is started //config: by the kernel as the init process. This is configured by adding //config: the init=/sbin/bootchartd option to the kernel command line. //config: //config: It can also be used to monitor the resource usage of a specific //config: application or the running system in general. In this case, //config: bootchartd is started interactively by running bootchartd start //config: and stopped using bootchartd stop. //config: //config:config FEATURE_BOOTCHARTD_BLOATED_HEADER //config: bool "Compatible, bloated header" //config: default y //config: depends on BOOTCHARTD //config: help //config: Create extended header file compatible with "big" bootchartd. //config: "Big" bootchartd is a shell script and it dumps some //config: "convenient" info int the header, such as: //config: title = Boot chart for `hostname` (`date`) //config: system.uname = `uname -srvm` //config: system.release = `cat /etc/DISTRO-release` //config: system.cpu = `grep '^model name' /proc/cpuinfo | head -1` ($cpucount) //config: system.kernel.options = `cat /proc/cmdline` //config: This data is not mandatory for bootchart graph generation, //config: and is considered bloat. Nevertheless, this option //config: makes bootchartd applet to dump a subset of it. //config: //config:config FEATURE_BOOTCHARTD_CONFIG_FILE //config: bool "Support bootchartd.conf" //config: default y //config: depends on BOOTCHARTD //config: help //config: Enable reading and parsing of $PWD/bootchartd.conf //config: and /etc/bootchartd.conf files. #include "libbb.h" /* After libbb.h, since it needs sys/types.h on some systems */ #include #ifdef __linux__ # include # ifndef MS_SILENT # define MS_SILENT (1 << 15) # endif # ifndef MNT_DETACH # define MNT_DETACH 0x00000002 # endif #endif #if !ENABLE_TAR && !ENABLE_WERROR # warning Note: bootchartd requires tar command, but you did not select it. #elif !ENABLE_FEATURE_SEAMLESS_GZ && !ENABLE_WERROR # warning Note: bootchartd requires tar -z support, but you did not select it. #endif #define BC_VERSION_STR "0.8" /* For debugging, set to 0: * strace won't work with DO_SIGNAL_SYNC set to 1. */ #define DO_SIGNAL_SYNC 1 //$PWD/bootchartd.conf and /etc/bootchartd.conf: //supported options: //# Sampling period (in seconds) //SAMPLE_PERIOD=0.2 // //not yet supported: //# tmpfs size //# (32 MB should suffice for ~20 minutes worth of log data, but YMMV) //TMPFS_SIZE=32m // //# Whether to enable and store BSD process accounting information. The //# kernel needs to be configured to enable v3 accounting //# (CONFIG_BSD_PROCESS_ACCT_V3). accton from the GNU accounting utilities //# is also required. //PROCESS_ACCOUNTING="no" // //# Tarball for the various boot log files //BOOTLOG_DEST=/var/log/bootchart.tgz // //# Whether to automatically stop logging as the boot process completes. //# The logger will look for known processes that indicate bootup completion //# at a specific runlevel (e.g. gdm-binary, mingetty, etc.). //AUTO_STOP_LOGGER="yes" // //# Whether to automatically generate the boot chart once the boot logger //# completes. The boot chart will be generated in $AUTO_RENDER_DIR. //# Note that the bootchart package must be installed. //AUTO_RENDER="no" // //# Image format to use for the auto-generated boot chart //# (choose between png, svg and eps). //AUTO_RENDER_FORMAT="png" // //# Output directory for auto-generated boot charts //AUTO_RENDER_DIR="/var/log" /* Globals */ struct globals { char jiffy_line[COMMON_BUFSIZE]; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { } while (0) static void dump_file(FILE *fp, const char *filename) { int fd = open(filename, O_RDONLY); if (fd >= 0) { fputs(G.jiffy_line, fp); fflush(fp); bb_copyfd_eof(fd, fileno(fp)); close(fd); fputc('\n', fp); } } static int dump_procs(FILE *fp, int look_for_login_process) { struct dirent *entry; DIR *dir = opendir("/proc"); int found_login_process = 0; fputs(G.jiffy_line, fp); while ((entry = readdir(dir)) != NULL) { char name[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; int stat_fd; unsigned pid = bb_strtou(entry->d_name, NULL, 10); if (errno) continue; /* Android's version reads /proc/PID/cmdline and extracts * non-truncated process name. Do we want to do that? */ sprintf(name, "/proc/%u/stat", pid); stat_fd = open(name, O_RDONLY); if (stat_fd >= 0) { char *p; char stat_line[4*1024]; int rd = safe_read(stat_fd, stat_line, sizeof(stat_line)-2); close(stat_fd); if (rd < 0) continue; stat_line[rd] = '\0'; p = strchrnul(stat_line, '\n'); *p++ = '\n'; *p = '\0'; fputs(stat_line, fp); if (!look_for_login_process) continue; p = strchr(stat_line, '('); if (!p) continue; p++; strchrnul(p, ')')[0] = '\0'; /* Is it gdm, kdm or a getty? */ if (((p[0] == 'g' || p[0] == 'k' || p[0] == 'x') && p[1] == 'd' && p[2] == 'm') || strstr(p, "getty") ) { found_login_process = 1; } } } closedir(dir); fputc('\n', fp); return found_login_process; } static char *make_tempdir(void) { char template[] = "/tmp/bootchart.XXXXXX"; char *tempdir = xstrdup(mkdtemp(template)); if (!tempdir) { #ifdef __linux__ /* /tmp is not writable (happens when we are used as init). * Try to mount a tmpfs, them cd and lazily unmount it. * Since we unmount it at once, we can mount it anywhere. * Try a few locations which are likely ti exist. */ static const char dirs[] = "/mnt\0""/tmp\0""/boot\0""/proc\0"; const char *try_dir = dirs; while (mount("none", try_dir, "tmpfs", MS_SILENT, "size=16m") != 0) { try_dir += strlen(try_dir) + 1; if (!try_dir[0]) bb_perror_msg_and_die("can't %smount tmpfs", ""); } //bb_error_msg("mounted tmpfs on %s", try_dir); xchdir(try_dir); if (umount2(try_dir, MNT_DETACH) != 0) { bb_perror_msg_and_die("can't %smount tmpfs", "un"); } #else bb_perror_msg_and_die("can't create temporary directory"); #endif } else { xchdir(tempdir); } return tempdir; } static void do_logging(unsigned sample_period_us, int process_accounting) { FILE *proc_stat = xfopen("proc_stat.log", "w"); FILE *proc_diskstats = xfopen("proc_diskstats.log", "w"); //FILE *proc_netdev = xfopen("proc_netdev.log", "w"); FILE *proc_ps = xfopen("proc_ps.log", "w"); int look_for_login_process = (getppid() == 1); unsigned count = 60*1000*1000 / sample_period_us; /* ~1 minute */ if (process_accounting) { close(xopen("kernel_pacct", O_WRONLY | O_CREAT | O_TRUNC)); acct("kernel_pacct"); } while (--count && !bb_got_signal) { char *p; int len = open_read_close("/proc/uptime", G.jiffy_line, sizeof(G.jiffy_line)-2); if (len < 0) goto wait_more; /* /proc/uptime has format "NNNNNN.MM NNNNNNN.MM" */ /* we convert it to "NNNNNNMM\n" (using first value) */ G.jiffy_line[len] = '\0'; p = strchr(G.jiffy_line, '.'); if (!p) goto wait_more; while (isdigit(*++p)) p[-1] = *p; p[-1] = '\n'; p[0] = '\0'; dump_file(proc_stat, "/proc/stat"); dump_file(proc_diskstats, "/proc/diskstats"); //dump_file(proc_netdev, "/proc/net/dev"); if (dump_procs(proc_ps, look_for_login_process)) { /* dump_procs saw a getty or {g,k,x}dm * stop logging in 2 seconds: */ if (count > 2*1000*1000 / sample_period_us) count = 2*1000*1000 / sample_period_us; } fflush_all(); wait_more: usleep(sample_period_us); } } static void finalize(char *tempdir, const char *prog, int process_accounting) { //# Stop process accounting if configured //local pacct= //[ -e kernel_pacct ] && pacct=kernel_pacct FILE *header_fp = xfopen("header", "w"); if (process_accounting) acct(NULL); if (prog) fprintf(header_fp, "profile.process = %s\n", prog); fputs("version = "BC_VERSION_STR"\n", header_fp); if (ENABLE_FEATURE_BOOTCHARTD_BLOATED_HEADER) { char *hostname; char *kcmdline; time_t t; struct tm tm_time; /* x2 for possible localized weekday/month names */ char date_buf[sizeof("Mon Jun 21 05:29:03 CEST 2010") * 2]; struct utsname unamebuf; hostname = safe_gethostname(); time(&t); localtime_r(&t, &tm_time); strftime(date_buf, sizeof(date_buf), "%a %b %e %H:%M:%S %Z %Y", &tm_time); fprintf(header_fp, "title = Boot chart for %s (%s)\n", hostname, date_buf); if (ENABLE_FEATURE_CLEAN_UP) free(hostname); uname(&unamebuf); /* never fails */ /* same as uname -srvm */ fprintf(header_fp, "system.uname = %s %s %s %s\n", unamebuf.sysname, unamebuf.release, unamebuf.version, unamebuf.machine ); //system.release = `cat /etc/DISTRO-release` //system.cpu = `grep '^model name' /proc/cpuinfo | head -1` ($cpucount) kcmdline = xmalloc_open_read_close("/proc/cmdline", NULL); /* kcmdline includes trailing "\n" */ fprintf(header_fp, "system.kernel.options = %s", kcmdline); if (ENABLE_FEATURE_CLEAN_UP) free(kcmdline); } fclose(header_fp); /* Package log files */ system(xasprintf("tar -zcf /var/log/bootlog.tgz header %s *.log", process_accounting ? "kernel_pacct" : "")); /* Clean up (if we are not in detached tmpfs) */ if (tempdir) { unlink("header"); unlink("proc_stat.log"); unlink("proc_diskstats.log"); //unlink("proc_netdev.log"); unlink("proc_ps.log"); if (process_accounting) unlink("kernel_pacct"); rmdir(tempdir); } /* shell-based bootchartd tries to run /usr/bin/bootchart if $AUTO_RENDER=yes: * /usr/bin/bootchart -o "$AUTO_RENDER_DIR" -f $AUTO_RENDER_FORMAT "$BOOTLOG_DEST" */ } //usage:#define bootchartd_trivial_usage //usage: "start [PROG ARGS]|stop|init" //usage:#define bootchartd_full_usage "\n\n" //usage: "Create /var/log/bootchart.tgz with boot chart data\n" //usage: "\nstart: start background logging; with PROG, run PROG, then kill logging with USR1" //usage: "\nstop: send USR1 to all bootchartd processes" //usage: "\ninit: start background logging; stop when getty/xdm is seen (for init scripts)" //usage: "\nUnder PID 1: as init, then exec $bootchart_init, /init, /sbin/init" int bootchartd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int bootchartd_main(int argc UNUSED_PARAM, char **argv) { unsigned sample_period_us; pid_t parent_pid, logger_pid; smallint cmd; int process_accounting; enum { CMD_STOP = 0, CMD_START, CMD_INIT, CMD_PID1, /* used to mark pid 1 case */ }; INIT_G(); parent_pid = getpid(); if (argv[1]) { cmd = index_in_strings("stop\0""start\0""init\0", argv[1]); if (cmd < 0) bb_show_usage(); if (cmd == CMD_STOP) { pid_t *pidList = find_pid_by_name("bootchartd"); while (*pidList != 0) { if (*pidList != parent_pid) kill(*pidList, SIGUSR1); pidList++; } return EXIT_SUCCESS; } } else { if (parent_pid != 1) bb_show_usage(); cmd = CMD_PID1; } /* Here we are in START, INIT or CMD_PID1 state */ /* Read config file: */ sample_period_us = 200 * 1000; process_accounting = 0; if (ENABLE_FEATURE_BOOTCHARTD_CONFIG_FILE) { char* token[2]; parser_t *parser = config_open2("/etc/bootchartd.conf" + 5, fopen_for_read); if (!parser) parser = config_open2("/etc/bootchartd.conf", fopen_for_read); while (config_read(parser, token, 2, 0, "#=", PARSE_NORMAL & ~PARSE_COLLAPSE)) { if (strcmp(token[0], "SAMPLE_PERIOD") == 0 && token[1]) sample_period_us = atof(token[1]) * 1000000; if (strcmp(token[0], "PROCESS_ACCOUNTING") == 0 && token[1] && (strcmp(token[1], "on") == 0 || strcmp(token[1], "yes") == 0) ) { process_accounting = 1; } } config_close(parser); if ((int)sample_period_us <= 0) sample_period_us = 1; /* prevent division by 0 */ } /* Create logger child: */ logger_pid = fork_or_rexec(argv); if (logger_pid == 0) { /* child */ char *tempdir; bb_signals(0 + (1 << SIGUSR1) + (1 << SIGUSR2) + (1 << SIGTERM) + (1 << SIGQUIT) + (1 << SIGINT) + (1 << SIGHUP) , record_signo); if (DO_SIGNAL_SYNC) /* Inform parent that we are ready */ raise(SIGSTOP); /* If we are started by kernel, PATH might be unset. * In order to find "tar", let's set some sane PATH: */ if (cmd == CMD_PID1 && !getenv("PATH")) putenv((char*)bb_PATH_root_path); tempdir = make_tempdir(); do_logging(sample_period_us, process_accounting); finalize(tempdir, cmd == CMD_START ? argv[2] : NULL, process_accounting); return EXIT_SUCCESS; } /* parent */ USE_FOR_NOMMU(argv[0][0] &= 0x7f); /* undo fork_or_rexec() damage */ if (DO_SIGNAL_SYNC) { /* Wait for logger child to set handlers, then unpause it. * Otherwise with short-lived PROG (e.g. "bootchartd start true") * we might send SIGUSR1 before logger sets its handler. */ waitpid(logger_pid, NULL, WUNTRACED); kill(logger_pid, SIGCONT); } if (cmd == CMD_PID1) { char *bootchart_init = getenv("bootchart_init"); if (bootchart_init) execl(bootchart_init, bootchart_init, NULL); execl("/init", "init", NULL); execl("/sbin/init", "init", NULL); bb_perror_msg_and_die("can't execute '%s'", "/sbin/init"); } if (cmd == CMD_START && argv[2]) { /* "start PROG ARGS" */ pid_t pid = xvfork(); if (pid == 0) { /* child */ argv += 2; BB_EXECVP_or_die(argv); } /* parent */ waitpid(pid, NULL, 0); kill(logger_pid, SIGUSR1); } return EXIT_SUCCESS; } busybox-1.22.1/init/Config.src0000644000000000000000000000022412263563520014651 0ustar rootroot# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Init Utilities" INSERT endmenu busybox-1.22.1/init/Kbuild.src0000644000000000000000000000025512263563520014662 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT busybox-1.22.1/init/reboot.h0000644000000000000000000000134112263563520014377 0ustar rootroot/* * Definitions related to the reboot() system call, * shared between init.c and halt.c. */ #include #ifndef RB_HALT_SYSTEM # if defined(__linux__) # define RB_HALT_SYSTEM 0xcdef0123 # define RB_ENABLE_CAD 0x89abcdef # define RB_DISABLE_CAD 0 # define RB_POWER_OFF 0x4321fedc # define RB_AUTOBOOT 0x01234567 # elif defined(RB_HALT) # define RB_HALT_SYSTEM RB_HALT # endif #endif /* Stop system and switch power off if possible. */ #ifndef RB_POWER_OFF # if defined(RB_POWERDOWN) # define RB_POWER_OFF RB_POWERDOWN # elif defined(__linux__) # define RB_POWER_OFF 0x4321fedc # else # warning "poweroff unsupported, using halt as fallback" # define RB_POWER_OFF RB_HALT_SYSTEM # endif #endif busybox-1.22.1/init/mesg.c0000644000000000000000000000415012263563520014034 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * mesg implementation for busybox * * Copyright (c) 2002 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //config:config MESG //config: bool "mesg" //config: default y //config: help //config: Mesg controls access to your terminal by others. It is typically //config: used to allow or disallow other users to write to your terminal //config: //config:config FEATURE_MESG_ENABLE_ONLY_GROUP //config: bool "Enable writing to tty only by group, not by everybody" //config: default y //config: depends on MESG //config: help //config: Usually, ttys are owned by group "tty", and "write" tool is //config: setgid to this group. This way, "mesg y" only needs to enable //config: "write by owning group" bit in tty mode. //config: //config: If you set this option to N, "mesg y" will enable writing //config: by anybody at all. This is not recommended. //applet:IF_MESG(APPLET(mesg, BB_DIR_USR_BIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_MESG) += mesg.o //usage:#define mesg_trivial_usage //usage: "[y|n]" //usage:#define mesg_full_usage "\n\n" //usage: "Control write access to your terminal\n" //usage: " y Allow write access to your terminal\n" //usage: " n Disallow write access to your terminal" #include "libbb.h" #if ENABLE_FEATURE_MESG_ENABLE_ONLY_GROUP #define S_IWGRP_OR_S_IWOTH S_IWGRP #else #define S_IWGRP_OR_S_IWOTH (S_IWGRP | S_IWOTH) #endif int mesg_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mesg_main(int argc UNUSED_PARAM, char **argv) { struct stat sb; mode_t m; char c = 0; argv++; if (argv[0] && (argv[1] || ((c = argv[0][0]) != 'y' && c != 'n')) ) { bb_show_usage(); } if (!isatty(STDIN_FILENO)) bb_error_msg_and_die("not a tty"); xfstat(STDIN_FILENO, &sb, "stderr"); if (c == 0) { puts((sb.st_mode & (S_IWGRP|S_IWOTH)) ? "is y" : "is n"); return EXIT_SUCCESS; } m = (c == 'y') ? sb.st_mode | S_IWGRP_OR_S_IWOTH : sb.st_mode & ~(S_IWGRP|S_IWOTH); if (fchmod(STDIN_FILENO, m) != 0) bb_perror_nomsg_and_die(); return EXIT_SUCCESS; } busybox-1.22.1/init/init.c0000644000000000000000000012035112263563520014046 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini init implementation for busybox * * Copyright (C) 1995, 1996 by Bruce Perens . * Copyright (C) 1999-2004 by Erik Andersen * Adjusted by so many folks, it's impossible to keep track. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //config:config INIT //config: bool "init" //config: default y //config: select FEATURE_SYSLOG //config: help //config: init is the first program run when the system boots. //config: //config:config FEATURE_USE_INITTAB //config: bool "Support reading an inittab file" //config: default y //config: depends on INIT //config: help //config: Allow init to read an inittab file when the system boot. //config: //config:config FEATURE_KILL_REMOVED //config: bool "Support killing processes that have been removed from inittab" //config: default n //config: depends on FEATURE_USE_INITTAB //config: help //config: When respawn entries are removed from inittab and a SIGHUP is //config: sent to init, this option will make init kill the processes //config: that have been removed. //config: //config:config FEATURE_KILL_DELAY //config: int "How long to wait between TERM and KILL (0 - send TERM only)" if FEATURE_KILL_REMOVED //config: range 0 1024 //config: default 0 //config: depends on FEATURE_KILL_REMOVED //config: help //config: With nonzero setting, init sends TERM, forks, child waits N //config: seconds, sends KILL and exits. Setting it too high is unwise //config: (child will hang around for too long and could actually kill //config: the wrong process!) //config: //config:config FEATURE_INIT_SCTTY //config: bool "Run commands with leading dash with controlling tty" //config: default y //config: depends on INIT //config: help //config: If this option is enabled, init will try to give a controlling //config: tty to any command which has leading hyphen (often it's "-/bin/sh"). //config: More precisely, init will do "ioctl(STDIN_FILENO, TIOCSCTTY, 0)". //config: If device attached to STDIN_FILENO can be a ctty but is not yet //config: a ctty for other session, it will become this process' ctty. //config: This is not the traditional init behavour, but is often what you want //config: in an embedded system where the console is only accessed during //config: development or for maintenance. //config: NB: using cttyhack applet may work better. //config: //config:config FEATURE_INIT_SYSLOG //config: bool "Enable init to write to syslog" //config: default y //config: depends on INIT //config: //config:config FEATURE_EXTRA_QUIET //config: bool "Be _extra_ quiet on boot" //config: default y //config: depends on INIT //config: help //config: Prevent init from logging some messages to the console during boot. //config: //config:config FEATURE_INIT_COREDUMPS //config: bool "Support dumping core for child processes (debugging only)" //config: default y //config: depends on INIT //config: help //config: If this option is enabled and the file /.init_enable_core //config: exists, then init will call setrlimit() to allow unlimited //config: core file sizes. If this option is disabled, processes //config: will not generate any core files. //config: //config:config FEATURE_INITRD //config: bool "Support running init from within an initrd (not initramfs)" //config: default y //config: depends on INIT //config: help //config: Legacy support for running init under the old-style initrd. Allows //config: the name linuxrc to act as init, and it doesn't assume init is PID 1. //config: //config: This does not apply to initramfs, which runs /init as PID 1 and //config: requires no special support. //config: //config:config INIT_TERMINAL_TYPE //config: string "Initial terminal type" //config: default "linux" //config: depends on INIT //config: help //config: This is the initial value set by init for the TERM environment //config: variable. This variable is used by programs which make use of //config: extended terminal capabilities. //config: //config: Note that on Linux, init attempts to detect serial terminal and //config: sets TERM to "vt102" if one is found. //applet:IF_INIT(APPLET(init, BB_DIR_SBIN, BB_SUID_DROP)) //applet:IF_FEATURE_INITRD(APPLET_ODDNAME(linuxrc, init, BB_DIR_ROOT, BB_SUID_DROP, linuxrc)) //kbuild:lib-$(CONFIG_INIT) += init.o #define DEBUG_SEGV_HANDLER 0 #include "libbb.h" #include #include #include #ifdef __linux__ # include # include #endif #include "reboot.h" /* reboot() constants */ #if DEBUG_SEGV_HANDLER # undef _GNU_SOURCE # define _GNU_SOURCE 1 # undef __USE_GNU # define __USE_GNU 1 # include # include #endif /* Used only for sanitizing purposes in set_sane_term() below. On systems where * the baud rate is stored in a separate field, we can safely disable them. */ #ifndef CBAUD # define CBAUD 0 # define CBAUDEX 0 #endif /* Was a CONFIG_xxx option. A lot of people were building * not fully functional init by switching it on! */ #define DEBUG_INIT 0 #define CONSOLE_NAME_SIZE 32 /* Default sysinit script. */ #ifndef INIT_SCRIPT # define INIT_SCRIPT "/etc/init.d/rcS" #endif /* Each type of actions can appear many times. They will be * handled in order. RESTART is an exception, only 1st is used. */ /* Start these actions first and wait for completion */ #define SYSINIT 0x01 /* Start these after SYSINIT and wait for completion */ #define WAIT 0x02 /* Start these after WAIT and *dont* wait for completion */ #define ONCE 0x04 /* * NB: while SYSINIT/WAIT/ONCE are being processed, * SIGHUP ("reread /etc/inittab") will be processed only after * each group of actions. If new inittab adds, say, a SYSINIT action, * it will not be run, since init is already "past SYSINIT stage". */ /* Start these after ONCE are started, restart on exit */ #define RESPAWN 0x08 /* Like RESPAWN, but wait for to be pressed on tty */ #define ASKFIRST 0x10 /* * Start these on SIGINT, and wait for completion. * Then go back to respawning RESPAWN and ASKFIRST actions. * NB: kernel sends SIGINT to us if Ctrl-Alt-Del was pressed. */ #define CTRLALTDEL 0x20 /* * Start these before killing all processes in preparation for * running RESTART actions or doing low-level halt/reboot/poweroff * (initiated by SIGUSR1/SIGTERM/SIGUSR2). * Wait for completion before proceeding. */ #define SHUTDOWN 0x40 /* * exec() on SIGQUIT. SHUTDOWN actions are started and waited for, * then all processes are killed, then init exec's 1st RESTART action, * replacing itself by it. If no RESTART action specified, * SIGQUIT has no effect. */ #define RESTART 0x80 /* A linked list of init_actions, to be read from inittab */ struct init_action { struct init_action *next; pid_t pid; uint8_t action_type; char terminal[CONSOLE_NAME_SIZE]; char command[1]; }; static struct init_action *init_action_list = NULL; static const char *log_console = VC_5; enum { L_LOG = 0x1, L_CONSOLE = 0x2, }; /* Print a message to the specified device. * "where" may be bitwise-or'd from L_LOG | L_CONSOLE * NB: careful, we can be called after vfork! */ #define dbg_message(...) do { if (DEBUG_INIT) message(__VA_ARGS__); } while (0) static void message(int where, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); static void message(int where, const char *fmt, ...) { va_list arguments; unsigned l; char msg[128]; msg[0] = '\r'; va_start(arguments, fmt); l = 1 + vsnprintf(msg + 1, sizeof(msg) - 2, fmt, arguments); if (l > sizeof(msg) - 2) l = sizeof(msg) - 2; va_end(arguments); #if ENABLE_FEATURE_INIT_SYSLOG msg[l] = '\0'; if (where & L_LOG) { /* Log the message to syslogd */ openlog(applet_name, 0, LOG_DAEMON); /* don't print "\r" */ syslog(LOG_INFO, "%s", msg + 1); closelog(); } msg[l++] = '\n'; msg[l] = '\0'; #else { static int log_fd = -1; msg[l++] = '\n'; msg[l] = '\0'; /* Take full control of the log tty, and never close it. * It's mine, all mine! Muhahahaha! */ if (log_fd < 0) { if (!log_console) { log_fd = STDERR_FILENO; } else { log_fd = device_open(log_console, O_WRONLY | O_NONBLOCK | O_NOCTTY); if (log_fd < 0) { bb_error_msg("can't log to %s", log_console); where = L_CONSOLE; } else { close_on_exec_on(log_fd); } } } if (where & L_LOG) { full_write(log_fd, msg, l); if (log_fd == STDERR_FILENO) return; /* don't print dup messages */ } } #endif if (where & L_CONSOLE) { /* Send console messages to console so people will see them. */ full_write(STDERR_FILENO, msg, l); } } static void console_init(void) { #ifdef VT_OPENQRY int vtno; #endif char *s; s = getenv("CONSOLE"); if (!s) s = getenv("console"); if (s) { int fd = open(s, O_RDWR | O_NONBLOCK | O_NOCTTY); if (fd >= 0) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); xmove_fd(fd, STDERR_FILENO); } dbg_message(L_LOG, "console='%s'", s); } else { /* Make sure fd 0,1,2 are not closed * (so that they won't be used by future opens) */ bb_sanitize_stdio(); // Users report problems // /* Make sure init can't be blocked by writing to stderr */ // fcntl(STDERR_FILENO, F_SETFL, fcntl(STDERR_FILENO, F_GETFL) | O_NONBLOCK); } s = getenv("TERM"); #ifdef VT_OPENQRY if (ioctl(STDIN_FILENO, VT_OPENQRY, &vtno) != 0) { /* Not a linux terminal, probably serial console. * Force the TERM setting to vt102 * if TERM is set to linux (the default) */ if (!s || strcmp(s, "linux") == 0) putenv((char*)"TERM=vt102"); if (!ENABLE_FEATURE_INIT_SYSLOG) log_console = NULL; } else #endif if (!s) putenv((char*)"TERM=" CONFIG_INIT_TERMINAL_TYPE); } /* Set terminal settings to reasonable defaults. * NB: careful, we can be called after vfork! */ static void set_sane_term(void) { struct termios tty; tcgetattr(STDIN_FILENO, &tty); /* set control chars */ tty.c_cc[VINTR] = 3; /* C-c */ tty.c_cc[VQUIT] = 28; /* C-\ */ tty.c_cc[VERASE] = 127; /* C-? */ tty.c_cc[VKILL] = 21; /* C-u */ tty.c_cc[VEOF] = 4; /* C-d */ tty.c_cc[VSTART] = 17; /* C-q */ tty.c_cc[VSTOP] = 19; /* C-s */ tty.c_cc[VSUSP] = 26; /* C-z */ #ifdef __linux__ /* use line discipline 0 */ tty.c_line = 0; #endif /* Make it be sane */ #ifndef CRTSCTS # define CRTSCTS 0 #endif /* added CRTSCTS to fix Debian bug 528560 */ tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD | CRTSCTS; tty.c_cflag |= CREAD | HUPCL | CLOCAL; /* input modes */ tty.c_iflag = ICRNL | IXON | IXOFF; /* output modes */ tty.c_oflag = OPOST | ONLCR; /* local modes */ tty.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; tcsetattr_stdin_TCSANOW(&tty); } /* Open the new terminal device. * NB: careful, we can be called after vfork! */ static int open_stdio_to_tty(const char* tty_name) { /* empty tty_name means "use init's tty", else... */ if (tty_name[0]) { int fd; close(STDIN_FILENO); /* fd can be only < 0 or 0: */ fd = device_open(tty_name, O_RDWR); if (fd) { message(L_LOG | L_CONSOLE, "can't open %s: %s", tty_name, strerror(errno)); return 0; /* failure */ } dup2(STDIN_FILENO, STDOUT_FILENO); dup2(STDIN_FILENO, STDERR_FILENO); } set_sane_term(); return 1; /* success */ } static void reset_sighandlers_and_unblock_sigs(void) { bb_signals(0 + (1 << SIGUSR1) + (1 << SIGUSR2) + (1 << SIGTERM) + (1 << SIGQUIT) + (1 << SIGINT) + (1 << SIGHUP) + (1 << SIGTSTP) + (1 << SIGSTOP) , SIG_DFL); sigprocmask_allsigs(SIG_UNBLOCK); } /* Wrapper around exec: * Takes string. * If chars like '>' detected, execs '[-]/bin/sh -c "exec ......."'. * Otherwise splits words on whitespace, deals with leading dash, * and uses plain exec(). * NB: careful, we can be called after vfork! */ static void init_exec(const char *command) { /* +8 allows to write VLA sizes below more efficiently: */ unsigned command_size = strlen(command) + 8; /* strlen(command) + strlen("exec ")+1: */ char buf[command_size]; /* strlen(command) / 2 + 4: */ char *cmd[command_size / 2]; int dash; dash = (command[0] == '-' /* maybe? && command[1] == '/' */); command += dash; /* See if any special /bin/sh requiring characters are present */ if (strpbrk(command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) { sprintf(buf, "exec %s", command); /* excluding "-" */ /* NB: LIBBB_DEFAULT_LOGIN_SHELL define has leading dash */ cmd[0] = (char*)(LIBBB_DEFAULT_LOGIN_SHELL + !dash); cmd[1] = (char*)"-c"; cmd[2] = buf; cmd[3] = NULL; command = LIBBB_DEFAULT_LOGIN_SHELL + 1; } else { /* Convert command (char*) into cmd (char**, one word per string) */ char *word, *next; int i = 0; next = strcpy(buf, command - dash); /* command including "-" */ command = next + dash; while ((word = strsep(&next, " \t")) != NULL) { if (*word != '\0') { /* not two spaces/tabs together? */ cmd[i] = word; i++; } } cmd[i] = NULL; } /* If we saw leading "-", it is interactive shell. * Try harder to give it a controlling tty. */ if (ENABLE_FEATURE_INIT_SCTTY && dash) { /* _Attempt_ to make stdin a controlling tty. */ ioctl(STDIN_FILENO, TIOCSCTTY, 0 /*only try, don't steal*/); } /* Here command never contains the dash, cmd[0] might */ BB_EXECVP(command, cmd); message(L_LOG | L_CONSOLE, "can't run '%s': %s", command, strerror(errno)); /* returns if execvp fails */ } /* Used only by run_actions */ static pid_t run(const struct init_action *a) { pid_t pid; /* Careful: don't be affected by a signal in vforked child */ sigprocmask_allsigs(SIG_BLOCK); if (BB_MMU && (a->action_type & ASKFIRST)) pid = fork(); else pid = vfork(); if (pid < 0) message(L_LOG | L_CONSOLE, "can't fork"); if (pid) { sigprocmask_allsigs(SIG_UNBLOCK); return pid; /* Parent or error */ } /* Child */ /* Reset signal handlers that were set by the parent process */ reset_sighandlers_and_unblock_sigs(); /* Create a new session and make ourself the process group leader */ setsid(); /* Open the new terminal device */ if (!open_stdio_to_tty(a->terminal)) _exit(EXIT_FAILURE); /* NB: on NOMMU we can't wait for input in child, so * "askfirst" will work the same as "respawn". */ if (BB_MMU && (a->action_type & ASKFIRST)) { static const char press_enter[] ALIGN1 = #ifdef CUSTOMIZED_BANNER #include CUSTOMIZED_BANNER #endif "\nPlease press Enter to activate this console. "; char c; /* * Save memory by not exec-ing anything large (like a shell) * before the user wants it. This is critical if swap is not * enabled and the system has low memory. Generally this will * be run on the second virtual console, and the first will * be allowed to start a shell or whatever an init script * specifies. */ dbg_message(L_LOG, "waiting for enter to start '%s'" "(pid %d, tty '%s')\n", a->command, getpid(), a->terminal); full_write(STDOUT_FILENO, press_enter, sizeof(press_enter) - 1); while (safe_read(STDIN_FILENO, &c, 1) == 1 && c != '\n') continue; } /* * When a file named /.init_enable_core exists, setrlimit is called * before processes are spawned to set core file size as unlimited. * This is for debugging only. Don't use this is production, unless * you want core dumps lying about.... */ if (ENABLE_FEATURE_INIT_COREDUMPS) { if (access("/.init_enable_core", F_OK) == 0) { struct rlimit limit; limit.rlim_cur = RLIM_INFINITY; limit.rlim_max = RLIM_INFINITY; setrlimit(RLIMIT_CORE, &limit); } } /* Log the process name and args */ message(L_LOG, "starting pid %d, tty '%s': '%s'", getpid(), a->terminal, a->command); /* Now run it. The new program will take over this PID, * so nothing further in init.c should be run. */ init_exec(a->command); /* We're still here? Some error happened. */ _exit(-1); } static struct init_action *mark_terminated(pid_t pid) { struct init_action *a; if (pid > 0) { update_utmp(pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL ); for (a = init_action_list; a; a = a->next) { if (a->pid == pid) { a->pid = 0; return a; } } } return NULL; } static void waitfor(pid_t pid) { /* waitfor(run(x)): protect against failed fork inside run() */ if (pid <= 0) return; /* Wait for any child (prevent zombies from exiting orphaned processes) * but exit the loop only when specified one has exited. */ while (1) { pid_t wpid = wait(NULL); mark_terminated(wpid); /* Unsafe. SIGTSTP handler might have wait'ed it already */ /*if (wpid == pid) break;*/ /* More reliable: */ if (kill(pid, 0)) break; } } /* Run all commands of a particular type */ static void run_actions(int action_type) { struct init_action *a; for (a = init_action_list; a; a = a->next) { if (!(a->action_type & action_type)) continue; if (a->action_type & (SYSINIT | WAIT | ONCE | CTRLALTDEL | SHUTDOWN)) { pid_t pid = run(a); if (a->action_type & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN)) waitfor(pid); } if (a->action_type & (RESPAWN | ASKFIRST)) { /* Only run stuff with pid == 0. If pid != 0, * it is already running */ if (a->pid == 0) a->pid = run(a); } } } static void new_init_action(uint8_t action_type, const char *command, const char *cons) { struct init_action *a, **nextp; /* Scenario: * old inittab: * ::shutdown:umount -a -r * ::shutdown:swapoff -a * new inittab: * ::shutdown:swapoff -a * ::shutdown:umount -a -r * On reload, we must ensure entries end up in correct order. * To achieve that, if we find a matching entry, we move it * to the end. */ nextp = &init_action_list; while ((a = *nextp) != NULL) { /* Don't enter action if it's already in the list. * This prevents losing running RESPAWNs. */ if (strcmp(a->command, command) == 0 && strcmp(a->terminal, cons) == 0 ) { /* Remove from list */ *nextp = a->next; /* Find the end of the list */ while (*nextp != NULL) nextp = &(*nextp)->next; a->next = NULL; goto append; } nextp = &a->next; } a = xzalloc(sizeof(*a) + strlen(command)); /* Append to the end of the list */ append: *nextp = a; a->action_type = action_type; strcpy(a->command, command); safe_strncpy(a->terminal, cons, sizeof(a->terminal)); dbg_message(L_LOG | L_CONSOLE, "command='%s' action=%x tty='%s'\n", a->command, a->action_type, a->terminal); } /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined, * then parse_inittab() simply adds in some default * actions (i.e., runs INIT_SCRIPT and then starts a pair * of "askfirst" shells). If CONFIG_FEATURE_USE_INITTAB * _is_ defined, but /etc/inittab is missing, this * results in the same set of default behaviors. */ static void parse_inittab(void) { #if ENABLE_FEATURE_USE_INITTAB char *token[4]; parser_t *parser = config_open2("/etc/inittab", fopen_for_read); if (parser == NULL) #endif { /* No inittab file - set up some default behavior */ /* Sysinit */ new_init_action(SYSINIT, INIT_SCRIPT, ""); /* Askfirst shell on tty1-4 */ new_init_action(ASKFIRST, bb_default_login_shell, ""); //TODO: VC_1 instead of ""? "" is console -> ctty problems -> angry users new_init_action(ASKFIRST, bb_default_login_shell, VC_2); new_init_action(ASKFIRST, bb_default_login_shell, VC_3); new_init_action(ASKFIRST, bb_default_login_shell, VC_4); /* Reboot on Ctrl-Alt-Del */ new_init_action(CTRLALTDEL, "reboot", ""); /* Umount all filesystems on halt/reboot */ new_init_action(SHUTDOWN, "umount -a -r", ""); /* Swapoff on halt/reboot */ new_init_action(SHUTDOWN, "swapoff -a", ""); /* Restart init when a QUIT is received */ new_init_action(RESTART, "init", ""); return; } #if ENABLE_FEATURE_USE_INITTAB /* optional_tty:ignored_runlevel:action:command * Delims are not to be collapsed and need exactly 4 tokens */ while (config_read(parser, token, 4, 0, "#:", PARSE_NORMAL & ~(PARSE_TRIM | PARSE_COLLAPSE))) { /* order must correspond to SYSINIT..RESTART constants */ static const char actions[] ALIGN1 = "sysinit\0""wait\0""once\0""respawn\0""askfirst\0" "ctrlaltdel\0""shutdown\0""restart\0"; int action; char *tty = token[0]; if (!token[3]) /* less than 4 tokens */ goto bad_entry; action = index_in_strings(actions, token[2]); if (action < 0 || !token[3][0]) /* token[3]: command */ goto bad_entry; /* turn .*TTY -> /dev/TTY */ if (tty[0]) { tty = concat_path_file("/dev/", skip_dev_pfx(tty)); } new_init_action(1 << action, token[3], tty); if (tty[0]) free(tty); continue; bad_entry: message(L_LOG | L_CONSOLE, "Bad inittab entry at line %d", parser->lineno); } config_close(parser); #endif } static void pause_and_low_level_reboot(unsigned magic) NORETURN; static void pause_and_low_level_reboot(unsigned magic) { pid_t pid; /* Allow time for last message to reach serial console, etc */ sleep(1); /* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS) * in linux/kernel/sys.c, which can cause the machine to panic when * the init process exits... */ pid = vfork(); if (pid == 0) { /* child */ reboot(magic); _exit(EXIT_SUCCESS); } while (1) sleep(1); } static void run_shutdown_and_kill_processes(void) { /* Run everything to be run at "shutdown". This is done _prior_ * to killing everything, in case people wish to use scripts to * shut things down gracefully... */ run_actions(SHUTDOWN); message(L_CONSOLE | L_LOG, "The system is going down NOW!"); /* Send signals to every process _except_ pid 1 */ kill(-1, SIGTERM); message(L_CONSOLE | L_LOG, "Sent SIG%s to all processes", "TERM"); sync(); sleep(1); kill(-1, SIGKILL); message(L_CONSOLE, "Sent SIG%s to all processes", "KILL"); sync(); /*sleep(1); - callers take care about making a pause */ } /* Signal handling by init: * * For process with PID==1, on entry kernel sets all signals to SIG_DFL * and unmasks all signals. However, for process with PID==1, * default action (SIG_DFL) on any signal is to ignore it, * even for special signals SIGKILL and SIGCONT. * Also, any signal can be caught or blocked. * (but SIGSTOP is still handled specially, at least in 2.6.20) * * We install two kinds of handlers, "immediate" and "delayed". * * Immediate handlers execute at any time, even while, say, sysinit * is running. * * Delayed handlers just set a flag variable. The variable is checked * in the main loop and acted upon. * * halt/poweroff/reboot and restart have immediate handlers. * They only traverse linked list of struct action's, never modify it, * this should be safe to do even in signal handler. Also they * never return. * * SIGSTOP and SIGTSTP have immediate handlers. They just wait * for SIGCONT to happen. * * SIGHUP has a delayed handler, because modifying linked list * of struct action's from a signal handler while it is manipulated * by the program may be disastrous. * * Ctrl-Alt-Del has a delayed handler. Not a must, but allowing * it to happen even somewhere inside "sysinit" would be a bit awkward. * * There is a tiny probability that SIGHUP and Ctrl-Alt-Del will collide * and only one will be remembered and acted upon. */ /* The SIGPWR/SIGUSR[12]/SIGTERM handler */ static void halt_reboot_pwoff(int sig) NORETURN; static void halt_reboot_pwoff(int sig) { const char *m; unsigned rb; /* We may call run() and it unmasks signals, * including the one masked inside this signal handler. * Testcase which would start multiple reboot scripts: * while true; do reboot; done * Preventing it: */ reset_sighandlers_and_unblock_sigs(); run_shutdown_and_kill_processes(); m = "halt"; rb = RB_HALT_SYSTEM; if (sig == SIGTERM) { m = "reboot"; rb = RB_AUTOBOOT; } else if (sig == SIGUSR2) { m = "poweroff"; rb = RB_POWER_OFF; } message(L_CONSOLE, "Requesting system %s", m); pause_and_low_level_reboot(rb); /* not reached */ } /* Handler for QUIT - exec "restart" action, * else (no such action defined) do nothing */ static void restart_handler(int sig UNUSED_PARAM) { struct init_action *a; for (a = init_action_list; a; a = a->next) { if (!(a->action_type & RESTART)) continue; /* Starting from here, we won't return. * Thus don't need to worry about preserving errno * and such. */ reset_sighandlers_and_unblock_sigs(); run_shutdown_and_kill_processes(); #ifdef RB_ENABLE_CAD /* Allow Ctrl-Alt-Del to reboot the system. * This is how kernel sets it up for init, we follow suit. */ reboot(RB_ENABLE_CAD); /* misnomer */ #endif if (open_stdio_to_tty(a->terminal)) { dbg_message(L_CONSOLE, "Trying to re-exec %s", a->command); /* Theoretically should be safe. * But in practice, kernel bugs may leave * unkillable processes, and wait() may block forever. * Oh well. Hoping "new" init won't be too surprised * by having children it didn't create. */ //while (wait(NULL) > 0) // continue; init_exec(a->command); } /* Open or exec failed */ pause_and_low_level_reboot(RB_HALT_SYSTEM); /* not reached */ } } /* The SIGSTOP/SIGTSTP handler * NB: inside it, all signals except SIGCONT are masked * via appropriate setup in sigaction(). */ static void stop_handler(int sig UNUSED_PARAM) { smallint saved_bb_got_signal; int saved_errno; saved_bb_got_signal = bb_got_signal; saved_errno = errno; signal(SIGCONT, record_signo); while (1) { pid_t wpid; if (bb_got_signal == SIGCONT) break; /* NB: this can accidentally wait() for a process * which we waitfor() elsewhere! waitfor() must have * code which is resilient against this. */ wpid = wait_any_nohang(NULL); mark_terminated(wpid); sleep(1); } signal(SIGCONT, SIG_DFL); errno = saved_errno; bb_got_signal = saved_bb_got_signal; } #if ENABLE_FEATURE_USE_INITTAB static void reload_inittab(void) { struct init_action *a, **nextp; message(L_LOG, "reloading /etc/inittab"); /* Disable old entries */ for (a = init_action_list; a; a = a->next) a->action_type = 0; /* Append new entries, or modify existing entries * (incl. setting a->action_type) if cmd and device name * match new ones. End result: only entries with * a->action_type == 0 are stale. */ parse_inittab(); #if ENABLE_FEATURE_KILL_REMOVED /* Kill stale entries */ /* Be nice and send SIGTERM first */ for (a = init_action_list; a; a = a->next) if (a->action_type == 0 && a->pid != 0) kill(a->pid, SIGTERM); if (CONFIG_FEATURE_KILL_DELAY) { /* NB: parent will wait in NOMMU case */ if ((BB_MMU ? fork() : vfork()) == 0) { /* child */ sleep(CONFIG_FEATURE_KILL_DELAY); for (a = init_action_list; a; a = a->next) if (a->action_type == 0 && a->pid != 0) kill(a->pid, SIGKILL); _exit(EXIT_SUCCESS); } } #endif /* Remove stale entries and SYSINIT entries. * We never rerun SYSINIT entries anyway, * removing them too saves a few bytes */ nextp = &init_action_list; while ((a = *nextp) != NULL) { /* * Why pid == 0 check? * Process can be removed from inittab and added *later*. * If we delete its entry but process still runs, * duplicate is spawned when the entry is re-added. */ if ((a->action_type & ~SYSINIT) == 0 && a->pid == 0) { *nextp = a->next; free(a); } else { nextp = &a->next; } } /* Not needed: */ /* run_actions(RESPAWN | ASKFIRST); */ /* - we return to main loop, which does this automagically */ } #endif static int check_delayed_sigs(void) { int sigs_seen = 0; while (1) { smallint sig = bb_got_signal; if (!sig) return sigs_seen; bb_got_signal = 0; sigs_seen = 1; #if ENABLE_FEATURE_USE_INITTAB if (sig == SIGHUP) reload_inittab(); #endif if (sig == SIGINT) run_actions(CTRLALTDEL); } } #if DEBUG_SEGV_HANDLER static void handle_sigsegv(int sig, siginfo_t *info, void *ucontext) { long ip; ucontext_t *uc; uc = ucontext; ip = uc->uc_mcontext.gregs[REG_EIP]; fdprintf(2, "signal:%d address:0x%lx ip:0x%lx\n", sig, /* this is void*, but using %p would print "(null)" * even for ptrs which are not exactly 0, but, say, 0x123: */ (long)info->si_addr, ip); { /* glibc extension */ void *array[50]; int size; size = backtrace(array, 50); backtrace_symbols_fd(array, size, 2); } for (;;) sleep(9999); } #endif int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int init_main(int argc UNUSED_PARAM, char **argv) { if (argv[1] && strcmp(argv[1], "-q") == 0) { return kill(1, SIGHUP); } #if DEBUG_SEGV_HANDLER { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = handle_sigsegv; sa.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &sa, NULL); sigaction(SIGILL, &sa, NULL); sigaction(SIGFPE, &sa, NULL); sigaction(SIGBUS, &sa, NULL); } #endif if (!DEBUG_INIT) { /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ if (getpid() != 1 && (!ENABLE_FEATURE_INITRD || applet_name[0] != 'l') /* not linuxrc? */ ) { bb_error_msg_and_die("must be run as PID 1"); } #ifdef RB_DISABLE_CAD /* Turn off rebooting via CTL-ALT-DEL - we get a * SIGINT on CAD so we can shut things down gracefully... */ reboot(RB_DISABLE_CAD); /* misnomer */ #endif } /* If, say, xmalloc would ever die, we don't want to oops kernel * by exiting. * NB: we set die_sleep *after* PID 1 check and bb_show_usage. * Otherwise, for example, "init u" ("please rexec yourself" * command for sysvinit) will show help text (which isn't too bad), * *and sleep forever* (which is bad!) */ die_sleep = 30 * 24*60*60; /* Figure out where the default console should be */ console_init(); set_sane_term(); xchdir("/"); setsid(); /* Make sure environs is set to something sane */ putenv((char *) "HOME=/"); putenv((char *) bb_PATH_root_path); putenv((char *) "SHELL=/bin/sh"); putenv((char *) "USER=root"); /* needed? why? */ if (argv[1]) xsetenv("RUNLEVEL", argv[1]); #if !ENABLE_FEATURE_EXTRA_QUIET /* Hello world */ message(L_CONSOLE | L_LOG, "init started: %s", bb_banner); #endif #if 0 /* It's 2013, does anyone really still depend on this? */ /* If you do, consider adding swapon to sysinot actions then! */ /* struct sysinfo is linux-specific */ # ifdef __linux__ /* Make sure there is enough memory to do something useful. */ /*if (ENABLE_SWAPONOFF) - WRONG: we may have non-bbox swapon*/ { struct sysinfo info; if (sysinfo(&info) == 0 && (info.mem_unit ? info.mem_unit : 1) * (long long)info.totalram < 1024*1024 ) { message(L_CONSOLE, "Low memory, forcing swapon"); /* swapon -a requires /proc typically */ new_init_action(SYSINIT, "mount -t proc proc /proc", ""); /* Try to turn on swap */ new_init_action(SYSINIT, "swapon -a", ""); run_actions(SYSINIT); /* wait and removing */ } } # endif #endif /* Check if we are supposed to be in single user mode */ if (argv[1] && (strcmp(argv[1], "single") == 0 || strcmp(argv[1], "-s") == 0 || LONE_CHAR(argv[1], '1')) ) { /* ??? shouldn't we set RUNLEVEL="b" here? */ /* Start a shell on console */ new_init_action(RESPAWN, bb_default_login_shell, ""); } else { /* Not in single user mode - see what inittab says */ /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined, * then parse_inittab() simply adds in some default * actions (i.e., INIT_SCRIPT and a pair * of "askfirst" shells) */ parse_inittab(); } #if ENABLE_SELINUX if (getenv("SELINUX_INIT") == NULL) { int enforce = 0; putenv((char*)"SELINUX_INIT=YES"); if (selinux_init_load_policy(&enforce) == 0) { BB_EXECVP(argv[0], argv); } else if (enforce > 0) { /* SELinux in enforcing mode but load_policy failed */ message(L_CONSOLE, "can't load SELinux Policy. " "Machine is in enforcing mode. Halting now."); return EXIT_FAILURE; } } #endif /* Make the command line just say "init" - thats all, nothing else */ strncpy(argv[0], "init", strlen(argv[0])); /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */ while (*++argv) nuke_str(*argv); /* Set up signal handlers */ if (!DEBUG_INIT) { struct sigaction sa; bb_signals(0 + (1 << SIGPWR) /* halt */ + (1 << SIGUSR1) /* halt */ + (1 << SIGTERM) /* reboot */ + (1 << SIGUSR2) /* poweroff */ , halt_reboot_pwoff); signal(SIGQUIT, restart_handler); /* re-exec another init */ /* Stop handler must allow only SIGCONT inside itself */ memset(&sa, 0, sizeof(sa)); sigfillset(&sa.sa_mask); sigdelset(&sa.sa_mask, SIGCONT); sa.sa_handler = stop_handler; /* NB: sa_flags doesn't have SA_RESTART. * It must be able to interrupt wait(). */ sigaction_set(SIGTSTP, &sa); /* pause */ /* Does not work as intended, at least in 2.6.20. * SIGSTOP is simply ignored by init: */ sigaction_set(SIGSTOP, &sa); /* pause */ /* SIGINT (Ctrl-Alt-Del) must interrupt wait(), * setting handler without SA_RESTART flag. */ bb_signals_recursive_norestart((1 << SIGINT), record_signo); } /* Set up "reread /etc/inittab" handler. * Handler is set up without SA_RESTART, it will interrupt syscalls. */ if (!DEBUG_INIT && ENABLE_FEATURE_USE_INITTAB) bb_signals_recursive_norestart((1 << SIGHUP), record_signo); /* Now run everything that needs to be run */ /* First run the sysinit command */ run_actions(SYSINIT); check_delayed_sigs(); /* Next run anything that wants to block */ run_actions(WAIT); check_delayed_sigs(); /* Next run anything to be run only once */ run_actions(ONCE); /* Now run the looping stuff for the rest of forever. */ while (1) { int maybe_WNOHANG; maybe_WNOHANG = check_delayed_sigs(); /* (Re)run the respawn/askfirst stuff */ run_actions(RESPAWN | ASKFIRST); maybe_WNOHANG |= check_delayed_sigs(); /* Don't consume all CPU time - sleep a bit */ sleep(1); maybe_WNOHANG |= check_delayed_sigs(); /* Wait for any child process(es) to exit. * * If check_delayed_sigs above reported that a signal * was caught, wait will be nonblocking. This ensures * that if SIGHUP has reloaded inittab, respawn and askfirst * actions will not be delayed until next child death. */ if (maybe_WNOHANG) maybe_WNOHANG = WNOHANG; while (1) { pid_t wpid; struct init_action *a; /* If signals happen _in_ the wait, they interrupt it, * bb_signals_recursive_norestart set them up that way */ wpid = waitpid(-1, NULL, maybe_WNOHANG); if (wpid <= 0) break; a = mark_terminated(wpid); if (a) { message(L_LOG, "process '%s' (pid %d) exited. " "Scheduling for restart.", a->command, wpid); } /* See if anyone else is waiting to be reaped */ maybe_WNOHANG = WNOHANG; } } /* while (1) */ } //usage:#define linuxrc_trivial_usage NOUSAGE_STR //usage:#define linuxrc_full_usage "" //usage:#define init_trivial_usage //usage: "" //usage:#define init_full_usage "\n\n" //usage: "Init is the first process started during boot. It never exits." //usage: IF_FEATURE_USE_INITTAB( //usage: "\n""It (re)spawns children according to /etc/inittab." //usage: ) //usage: IF_NOT_FEATURE_USE_INITTAB( //usage: "\n""This version of init doesn't use /etc/inittab," //usage: "\n""has fixed set of processed to run." //usage: ) //usage: //usage:#define init_notes_usage //usage: "This version of init is designed to be run only by the kernel.\n" //usage: "\n" //usage: "BusyBox init doesn't support multiple runlevels. The runlevels field of\n" //usage: "the /etc/inittab file is completely ignored by BusyBox init. If you want\n" //usage: "runlevels, use sysvinit.\n" //usage: "\n" //usage: "BusyBox init works just fine without an inittab. If no inittab is found,\n" //usage: "it has the following default behavior:\n" //usage: "\n" //usage: " ::sysinit:/etc/init.d/rcS\n" //usage: " ::askfirst:/bin/sh\n" //usage: " ::ctrlaltdel:/sbin/reboot\n" //usage: " ::shutdown:/sbin/swapoff -a\n" //usage: " ::shutdown:/bin/umount -a -r\n" //usage: " ::restart:/sbin/init\n" //usage: " tty2::askfirst:/bin/sh\n" //usage: " tty3::askfirst:/bin/sh\n" //usage: " tty4::askfirst:/bin/sh\n" //usage: "\n" //usage: "If you choose to use an /etc/inittab file, the inittab entry format is as follows:\n" //usage: "\n" //usage: " :::\n" //usage: "\n" //usage: " :\n" //usage: "\n" //usage: " WARNING: This field has a non-traditional meaning for BusyBox init!\n" //usage: " The id field is used by BusyBox init to specify the controlling tty for\n" //usage: " the specified process to run on. The contents of this field are\n" //usage: " appended to \"/dev/\" and used as-is. There is no need for this field to\n" //usage: " be unique, although if it isn't you may have strange results. If this\n" //usage: " field is left blank, then the init's stdin/out will be used.\n" //usage: "\n" //usage: " :\n" //usage: "\n" //usage: " The runlevels field is completely ignored.\n" //usage: "\n" //usage: " :\n" //usage: "\n" //usage: " Valid actions include: sysinit, respawn, askfirst, wait,\n" //usage: " once, restart, ctrlaltdel, and shutdown.\n" //usage: "\n" //usage: " The available actions can be classified into two groups: actions\n" //usage: " that are run only once, and actions that are re-run when the specified\n" //usage: " process exits.\n" //usage: "\n" //usage: " Run only-once actions:\n" //usage: "\n" //usage: " 'sysinit' is the first item run on boot. init waits until all\n" //usage: " sysinit actions are completed before continuing. Following the\n" //usage: " completion of all sysinit actions, all 'wait' actions are run.\n" //usage: " 'wait' actions, like 'sysinit' actions, cause init to wait until\n" //usage: " the specified task completes. 'once' actions are asynchronous,\n" //usage: " therefore, init does not wait for them to complete. 'restart' is\n" //usage: " the action taken to restart the init process. By default this should\n" //usage: " simply run /sbin/init, but can be a script which runs pivot_root or it\n" //usage: " can do all sorts of other interesting things. The 'ctrlaltdel' init\n" //usage: " actions are run when the system detects that someone on the system\n" //usage: " console has pressed the CTRL-ALT-DEL key combination. Typically one\n" //usage: " wants to run 'reboot' at this point to cause the system to reboot.\n" //usage: " Finally the 'shutdown' action specifies the actions to taken when\n" //usage: " init is told to reboot. Unmounting filesystems and disabling swap\n" //usage: " is a very good here.\n" //usage: "\n" //usage: " Run repeatedly actions:\n" //usage: "\n" //usage: " 'respawn' actions are run after the 'once' actions. When a process\n" //usage: " started with a 'respawn' action exits, init automatically restarts\n" //usage: " it. Unlike sysvinit, BusyBox init does not stop processes from\n" //usage: " respawning out of control. The 'askfirst' actions acts just like\n" //usage: " respawn, except that before running the specified process it\n" //usage: " displays the line \"Please press Enter to activate this console.\"\n" //usage: " and then waits for the user to press enter before starting the\n" //usage: " specified process.\n" //usage: "\n" //usage: " Unrecognized actions (like initdefault) will cause init to emit an\n" //usage: " error message, and then go along with its business. All actions are\n" //usage: " run in the order they appear in /etc/inittab.\n" //usage: "\n" //usage: " :\n" //usage: "\n" //usage: " Specifies the process to be executed and its command line.\n" //usage: "\n" //usage: "Example /etc/inittab file:\n" //usage: "\n" //usage: " # This is run first except when booting in single-user mode\n" //usage: " #\n" //usage: " ::sysinit:/etc/init.d/rcS\n" //usage: " \n" //usage: " # /bin/sh invocations on selected ttys\n" //usage: " #\n" //usage: " # Start an \"askfirst\" shell on the console (whatever that may be)\n" //usage: " ::askfirst:-/bin/sh\n" //usage: " # Start an \"askfirst\" shell on /dev/tty2-4\n" //usage: " tty2::askfirst:-/bin/sh\n" //usage: " tty3::askfirst:-/bin/sh\n" //usage: " tty4::askfirst:-/bin/sh\n" //usage: " \n" //usage: " # /sbin/getty invocations for selected ttys\n" //usage: " #\n" //usage: " tty4::respawn:/sbin/getty 38400 tty4\n" //usage: " tty5::respawn:/sbin/getty 38400 tty5\n" //usage: " \n" //usage: " \n" //usage: " # Example of how to put a getty on a serial line (for a terminal)\n" //usage: " #\n" //usage: " #::respawn:/sbin/getty -L ttyS0 9600 vt100\n" //usage: " #::respawn:/sbin/getty -L ttyS1 9600 vt100\n" //usage: " #\n" //usage: " # Example how to put a getty on a modem line\n" //usage: " #::respawn:/sbin/getty 57600 ttyS2\n" //usage: " \n" //usage: " # Stuff to do when restarting the init process\n" //usage: " ::restart:/sbin/init\n" //usage: " \n" //usage: " # Stuff to do before rebooting\n" //usage: " ::ctrlaltdel:/sbin/reboot\n" //usage: " ::shutdown:/bin/umount -a -r\n" //usage: " ::shutdown:/sbin/swapoff -a\n" busybox-1.22.1/init/halt.c0000644000000000000000000001150512263563520014033 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Poweroff reboot and halt, oh my. * * Copyright 2006 by Rob Landley * * Licensed under GPLv2, see file LICENSE in this source tree. */ //applet:IF_HALT(APPLET(halt, BB_DIR_SBIN, BB_SUID_DROP)) //applet:IF_HALT(APPLET_ODDNAME(poweroff, halt, BB_DIR_SBIN, BB_SUID_DROP, poweroff)) //applet:IF_HALT(APPLET_ODDNAME(reboot, halt, BB_DIR_SBIN, BB_SUID_DROP, reboot)) //kbuild:lib-$(CONFIG_HALT) += halt.o //config:config HALT //config: bool "poweroff, halt, and reboot" //config: default y //config: help //config: Stop all processes and either halt, reboot, or power off the system. //config: //config:config FEATURE_CALL_TELINIT //config: bool "Call telinit on shutdown and reboot" //config: default y //config: depends on HALT && !INIT //config: help //config: Call an external program (normally telinit) to facilitate //config: a switch to a proper runlevel. //config: //config: This option is only available if you selected halt and friends, //config: but did not select init. //config: //config:config TELINIT_PATH //config: string "Path to telinit executable" //config: default "/sbin/telinit" //config: depends on FEATURE_CALL_TELINIT //config: help //config: When busybox halt and friends have to call external telinit //config: to facilitate proper shutdown, this path is to be used when //config: locating telinit executable. //usage:#define halt_trivial_usage //usage: "[-d DELAY] [-n] [-f]" IF_FEATURE_WTMP(" [-w]") //usage:#define halt_full_usage "\n\n" //usage: "Halt the system\n" //usage: "\n -d SEC Delay interval" //usage: "\n -n Do not sync" //usage: "\n -f Force (don't go through init)" //usage: IF_FEATURE_WTMP( //usage: "\n -w Only write a wtmp record" //usage: ) //usage: //usage:#define poweroff_trivial_usage //usage: "[-d DELAY] [-n] [-f]" //usage:#define poweroff_full_usage "\n\n" //usage: "Halt and shut off power\n" //usage: "\n -d SEC Delay interval" //usage: "\n -n Do not sync" //usage: "\n -f Force (don't go through init)" //usage: //usage:#define reboot_trivial_usage //usage: "[-d DELAY] [-n] [-f]" //usage:#define reboot_full_usage "\n\n" //usage: "Reboot the system\n" //usage: "\n -d SEC Delay interval" //usage: "\n -n Do not sync" //usage: "\n -f Force (don't go through init)" #include "libbb.h" #include "reboot.h" #if ENABLE_FEATURE_WTMP #include static void write_wtmp(void) { struct utmp utmp; struct utsname uts; /* "man utmp" says wtmp file should *not* be created automagically */ /*if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) { close(creat(bb_path_wtmp_file, 0664)); }*/ memset(&utmp, 0, sizeof(utmp)); utmp.ut_tv.tv_sec = time(NULL); strcpy(utmp.ut_user, "shutdown"); /* it is wide enough */ utmp.ut_type = RUN_LVL; utmp.ut_id[0] = '~'; utmp.ut_id[1] = '~'; /* = strcpy(utmp.ut_id, "~~"); */ utmp.ut_line[0] = '~'; utmp.ut_line[1] = '~'; /* = strcpy(utmp.ut_line, "~~"); */ uname(&uts); safe_strncpy(utmp.ut_host, uts.release, sizeof(utmp.ut_host)); updwtmp(bb_path_wtmp_file, &utmp); } #else #define write_wtmp() ((void)0) #endif int halt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int halt_main(int argc UNUSED_PARAM, char **argv) { static const int magic[] = { RB_HALT_SYSTEM, RB_POWER_OFF, RB_AUTOBOOT }; static const smallint signals[] = { SIGUSR1, SIGUSR2, SIGTERM }; int delay = 0; int which, flags, rc; /* Figure out which applet we're running */ for (which = 0; "hpr"[which] != applet_name[0]; which++) continue; /* Parse and handle arguments */ opt_complementary = "d+"; /* -d N */ /* We support -w even if !ENABLE_FEATURE_WTMP, * in order to not break scripts. * -i (shut down network interfaces) is ignored. */ flags = getopt32(argv, "d:nfwi", &delay); sleep(delay); write_wtmp(); if (flags & 8) /* -w */ return EXIT_SUCCESS; if (!(flags & 2)) /* no -n */ sync(); /* Perform action. */ rc = 1; if (!(flags & 4)) { /* no -f */ //TODO: I tend to think that signalling linuxrc is wrong // pity original author didn't comment on it... if (ENABLE_FEATURE_INITRD) { /* talk to linuxrc */ /* bbox init/linuxrc assumed */ pid_t *pidlist = find_pid_by_name("linuxrc"); if (pidlist[0] > 0) rc = kill(pidlist[0], signals[which]); if (ENABLE_FEATURE_CLEAN_UP) free(pidlist); } if (rc) { /* talk to init */ if (!ENABLE_FEATURE_CALL_TELINIT) { /* bbox init assumed */ rc = kill(1, signals[which]); } else { /* SysV style init assumed */ /* runlevels: * 0 == shutdown * 6 == reboot */ execlp(CONFIG_TELINIT_PATH, CONFIG_TELINIT_PATH, which == 2 ? "6" : "0", (char *)NULL ); bb_perror_msg_and_die("can't execute '%s'", CONFIG_TELINIT_PATH); } } } else { rc = reboot(magic[which]); } if (rc) bb_perror_nomsg_and_die(); return rc; } busybox-1.22.1/TODO_unicode0000644000000000000000000000133312263563520014170 0ustar rootrootAlready fixed applets: cal lsmod df dumpleases Applets which may need unicode handling (more extensive than sanitizing of filenames in error messages): ls - work in progress expand, unexpand - uses unicode_strlen, not scrlen ash, hush through lineedit - uses unicode_strlen, not scrlen top - need to sanitize process args ps - need to sanitize process args less more vi ed cut awk sed tr grep egrep fgrep fold sort head, tail catv - "display nonprinting chars" - what this could mean for unicode? wc chat dumpkmap last - just line up columns man microcom strings watch Unsure, may need fixing: hostname - do we really want to protect against bad chars in it? patch addgroup, adduser, delgroup, deluser telnet telnetd od printf busybox-1.22.1/libbb/0000755000000000000000000000000012320365433013041 5ustar rootrootbusybox-1.22.1/libbb/time.c0000644000000000000000000001715312320365433014152 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 2007 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm) { char end = '\0'; const char *last_colon = strrchr(date_str, ':'); if (last_colon != NULL) { /* Parse input and assign appropriately to ptm */ #if ENABLE_DESKTOP const char *endp; #endif /* HH:MM */ if (sscanf(date_str, "%u:%u%c", &ptm->tm_hour, &ptm->tm_min, &end) >= 2 ) { /* no adjustments needed */ } else /* mm.dd-HH:MM */ if (sscanf(date_str, "%u.%u-%u:%u%c", &ptm->tm_mon, &ptm->tm_mday, &ptm->tm_hour, &ptm->tm_min, &end) >= 4 ) { /* Adjust month from 1-12 to 0-11 */ ptm->tm_mon -= 1; } else /* yyyy.mm.dd-HH:MM */ if (sscanf(date_str, "%u.%u.%u-%u:%u%c", &ptm->tm_year, &ptm->tm_mon, &ptm->tm_mday, &ptm->tm_hour, &ptm->tm_min, &end) >= 5 /* yyyy-mm-dd HH:MM */ || sscanf(date_str, "%u-%u-%u %u:%u%c", &ptm->tm_year, &ptm->tm_mon, &ptm->tm_mday, &ptm->tm_hour, &ptm->tm_min, &end) >= 5 ) { ptm->tm_year -= 1900; /* Adjust years */ ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */ } else #if ENABLE_DESKTOP /* strptime is BIG: ~1k in uclibc, ~10k in glibc */ /* month_name d HH:MM:SS YYYY. Supported by GNU date */ if ((endp = strptime(date_str, "%b %d %T %Y", ptm)) != NULL && *endp == '\0' ) { return; /* don't fall through to end == ":" check */ } else #endif { bb_error_msg_and_die(bb_msg_invalid_date, date_str); } if (end == ':') { /* xxx:SS */ if (sscanf(last_colon + 1, "%u%c", &ptm->tm_sec, &end) == 1) end = '\0'; /* else end != NUL and we error out */ } } else if (strchr(date_str, '-') /* Why strchr('-') check? * sscanf below will trash ptm->tm_year, this breaks * if parse_str is "10101010" (iow, "MMddhhmm" form) * because we destroy year. Do these sscanf * only if we saw a dash in parse_str. */ /* yyyy-mm-dd HH */ && (sscanf(date_str, "%u-%u-%u %u%c", &ptm->tm_year, &ptm->tm_mon, &ptm->tm_mday, &ptm->tm_hour, &end) >= 4 /* yyyy-mm-dd */ || sscanf(date_str, "%u-%u-%u%c", &ptm->tm_year, &ptm->tm_mon, &ptm->tm_mday, &end) >= 3 ) ) { ptm->tm_year -= 1900; /* Adjust years */ ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */ } else if (date_str[0] == '@') { time_t t = bb_strtol(date_str + 1, NULL, 10); if (!errno) { struct tm *lt = localtime(&t); if (lt) { *ptm = *lt; return; } } end = '1'; } else { /* Googled the following on an old date manpage: * * The canonical representation for setting the date/time is: * cc Century (either 19 or 20) * yy Year in abbreviated form (e.g. 89, 06) * mm Numeric month, a number from 1 to 12 * dd Day, a number from 1 to 31 * HH Hour, a number from 0 to 23 * MM Minutes, a number from 0 to 59 * .SS Seconds, a number from 0 to 61 (with leap seconds) * Everything but the minutes is optional * * "touch -t DATETIME" format: [[[[[YY]YY]MM]DD]hh]mm[.ss] * Some, but not all, Unix "date DATETIME" commands * move [[YY]YY] past minutes mm field (!). * Coreutils date does it, and SUS mandates it. * (date -s DATETIME does not support this format. lovely!) * In bbox, this format is special-cased in date applet * (IOW: this function assumes "touch -t" format). */ unsigned cur_year = ptm->tm_year; int len = strchrnul(date_str, '.') - date_str; /* MM[.SS] */ if (len == 2 && sscanf(date_str, "%2u%2u%2u%2u""%2u%c" + 12, &ptm->tm_min, &end) >= 1) { } else /* HHMM[.SS] */ if (len == 4 && sscanf(date_str, "%2u%2u%2u""%2u%2u%c" + 9, &ptm->tm_hour, &ptm->tm_min, &end) >= 2) { } else /* ddHHMM[.SS] */ if (len == 6 && sscanf(date_str, "%2u%2u""%2u%2u%2u%c" + 6, &ptm->tm_mday, &ptm->tm_hour, &ptm->tm_min, &end) >= 3) { } else /* mmddHHMM[.SS] */ if (len == 8 && sscanf(date_str, "%2u""%2u%2u%2u%2u%c" + 3, &ptm->tm_mon, &ptm->tm_mday, &ptm->tm_hour, &ptm->tm_min, &end) >= 4) { /* Adjust month from 1-12 to 0-11 */ ptm->tm_mon -= 1; } else /* yymmddHHMM[.SS] */ if (len == 10 && sscanf(date_str, "%2u%2u%2u%2u%2u%c", &ptm->tm_year, &ptm->tm_mon, &ptm->tm_mday, &ptm->tm_hour, &ptm->tm_min, &end) >= 5) { /* Adjust month from 1-12 to 0-11 */ ptm->tm_mon -= 1; if ((int)cur_year >= 50) { /* >= 1950 */ /* Adjust year: */ /* 1. Put it in the current century */ ptm->tm_year += (cur_year / 100) * 100; /* 2. If too far in the past, +100 years */ if (ptm->tm_year < cur_year - 50) ptm->tm_year += 100; /* 3. If too far in the future, -100 years */ if (ptm->tm_year > cur_year + 50) ptm->tm_year -= 100; } } else /* ccyymmddHHMM[.SS] */ if (len == 12 && sscanf(date_str, "%4u%2u%2u%2u%2u%c", &ptm->tm_year, &ptm->tm_mon, &ptm->tm_mday, &ptm->tm_hour, &ptm->tm_min, &end) >= 5) { ptm->tm_year -= 1900; /* Adjust years */ ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */ } else { bb_error_msg_and_die(bb_msg_invalid_date, date_str); } if (end == '.') { /* xxx.SS */ if (sscanf(strchr(date_str, '.') + 1, "%u%c", &ptm->tm_sec, &end) == 1) end = '\0'; /* else end != NUL and we error out */ } } if (end != '\0') { bb_error_msg_and_die(bb_msg_invalid_date, date_str); } } time_t FAST_FUNC validate_tm_time(const char *date_str, struct tm *ptm) { time_t t = mktime(ptm); if (t == (time_t) -1L) { bb_error_msg_and_die(bb_msg_invalid_date, date_str); } return t; } static char* strftime_fmt(char *buf, unsigned len, time_t *tp, const char *fmt) { time_t t; if (!tp) { tp = &t; time(tp); } /* Returns pointer to NUL */ return buf + strftime(buf, len, fmt, localtime(tp)); } char* FAST_FUNC strftime_HHMMSS(char *buf, unsigned len, time_t *tp) { return strftime_fmt(buf, len, tp, "%H:%M:%S"); } char* FAST_FUNC strftime_YYYYMMDDHHMMSS(char *buf, unsigned len, time_t *tp) { return strftime_fmt(buf, len, tp, "%Y-%m-%d %H:%M:%S"); } #if ENABLE_MONOTONIC_SYSCALL #include /* Old glibc (< 2.3.4) does not provide this constant. We use syscall * directly so this definition is safe. */ #ifndef CLOCK_MONOTONIC #define CLOCK_MONOTONIC 1 #endif /* libc has incredibly messy way of doing this, * typically requiring -lrt. We just skip all this mess */ static void get_mono(struct timespec *ts) { if (syscall(__NR_clock_gettime, CLOCK_MONOTONIC, ts)) bb_error_msg_and_die("clock_gettime(MONOTONIC) failed"); } unsigned long long FAST_FUNC monotonic_ns(void) { struct timespec ts; get_mono(&ts); return ts.tv_sec * 1000000000ULL + ts.tv_nsec; } unsigned long long FAST_FUNC monotonic_us(void) { struct timespec ts; get_mono(&ts); return ts.tv_sec * 1000000ULL + ts.tv_nsec/1000; } unsigned long long FAST_FUNC monotonic_ms(void) { struct timespec ts; get_mono(&ts); return ts.tv_sec * 1000ULL + ts.tv_nsec/1000000; } unsigned FAST_FUNC monotonic_sec(void) { struct timespec ts; get_mono(&ts); return ts.tv_sec; } #else unsigned long long FAST_FUNC monotonic_ns(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000; } unsigned long long FAST_FUNC monotonic_us(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000000ULL + tv.tv_usec; } unsigned long long FAST_FUNC monotonic_ms(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000ULL + tv.tv_usec / 1000; } unsigned FAST_FUNC monotonic_sec(void) { return time(NULL); } #endif busybox-1.22.1/libbb/setup_environment.c0000644000000000000000000000552712263563520017005 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright 1989 - 1991, Julianne Frances Haugh * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Julianne F. Haugh nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "libbb.h" void FAST_FUNC setup_environment(const char *shell, int flags, const struct passwd *pw) { if (!shell || !shell[0]) shell = DEFAULT_SHELL; /* Change the current working directory to be the home directory * of the user */ if (!(flags & SETUP_ENV_NO_CHDIR)) { if (chdir(pw->pw_dir) != 0) { bb_error_msg("can't change directory to '%s'", pw->pw_dir); xchdir((flags & SETUP_ENV_TO_TMP) ? "/tmp" : "/"); } } if (flags & SETUP_ENV_CLEARENV) { const char *term; /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. * Unset all other environment variables. */ term = getenv("TERM"); clearenv(); if (term) xsetenv("TERM", term); xsetenv("PATH", (pw->pw_uid ? bb_default_path : bb_default_root_path)); goto shortcut; // No, gcc (4.2.1) is not clever enougn to do it itself. //xsetenv("USER", pw->pw_name); //xsetenv("LOGNAME", pw->pw_name); //xsetenv("HOME", pw->pw_dir); //xsetenv("SHELL", shell); } else if (flags & SETUP_ENV_CHANGEENV) { /* Set HOME, SHELL, and if not becoming a super-user, * USER and LOGNAME. */ if (pw->pw_uid) { shortcut: xsetenv("USER", pw->pw_name); xsetenv("LOGNAME", pw->pw_name); } xsetenv("HOME", pw->pw_dir); xsetenv("SHELL", shell); } } busybox-1.22.1/libbb/process_escape_sequence.c0000644000000000000000000000454112263563520020102 0ustar rootroot/* 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 source tree. */ #include "libbb.h" #define WANT_HEX_ESCAPES 1 /* Usual "this only works for ascii compatible encodings" disclaimer. */ #undef _tolower #define _tolower(X) ((X)|((char) 0x20)) char FAST_FUNC bb_process_escape_sequence(const char **ptr) { const char *q; unsigned num_digits; unsigned n; unsigned base; num_digits = n = 0; base = 8; q = *ptr; if (WANT_HEX_ESCAPES && *q == 'x') { ++q; base = 16; ++num_digits; } /* bash requires leading 0 in octal escapes: * \02 works, \2 does not (prints \ and 2). * We treat \2 as a valid octal escape sequence. */ do { unsigned r; #if !WANT_HEX_ESCAPES unsigned d = (unsigned char)(*q) - '0'; #else unsigned d = (unsigned char)_tolower(*q) - '0'; if (d >= 10) d += ('0' - 'a' + 10); #endif if (d >= base) { if (WANT_HEX_ESCAPES && base == 16) { --num_digits; if (num_digits == 0) { /* \x: return '\', * leave ptr pointing to x */ return '\\'; } } break; } r = n * base + d; if (r > UCHAR_MAX) { break; } n = r; ++q; } while (++num_digits < 3); if (num_digits == 0) { /* Not octal or hex escape sequence. * Is it one-letter one? */ /* bash builtin "echo -e '\ec'" interprets \e as ESC, * but coreutils "/bin/echo -e '\ec'" does not. * Manpages tend to support coreutils way. * Update: coreutils added support for \e on 28 Oct 2009. */ static const char charmap[] ALIGN1 = { 'a', 'b', 'e', 'f', 'n', 'r', 't', 'v', '\\', '\0', '\a', '\b', 27, '\f', '\n', '\r', '\t', '\v', '\\', '\\', }; const char *p = charmap; do { if (*p == *q) { q++; break; } } while (*++p != '\0'); /* p points to found escape char or NUL, * advance it and find what it translates to. * Note that \NUL and unrecognized sequence \z return '\' * and leave ptr pointing to NUL or z. */ n = p[sizeof(charmap) / 2]; } *ptr = q; return (char) n; } char* FAST_FUNC strcpy_and_process_escape_sequences(char *dst, const char *src) { while (1) { char c, c1; c = c1 = *src++; if (c1 == '\\') c1 = bb_process_escape_sequence(&src); *dst = c1; if (c == '\0') return dst; dst++; } } busybox-1.22.1/libbb/vfork_daemon_rexec.c0000644000000000000000000001656312263563520017063 0ustar rootroot/* 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 source tree. */ #include "busybox.h" /* uses applet tables */ /* This does a fork/exec in one call, using vfork(). Returns PID of new child, * -1 for failure. Runs argv[0], searching path if that has no / in it. */ pid_t FAST_FUNC spawn(char **argv) { /* Compiler should not optimize stores here */ volatile int failed; pid_t pid; fflush_all(); /* Be nice to nommu machines. */ failed = 0; pid = vfork(); if (pid < 0) /* error */ return pid; if (!pid) { /* child */ /* This macro is ok - it doesn't do NOEXEC/NOFORK tricks */ BB_EXECVP(argv[0], argv); /* We are (maybe) sharing a stack with blocked parent, * let parent know we failed and then exit to unblock parent * (but don't run atexit() stuff, which would screw up parent.) */ failed = errno; /* mount, for example, does not want the message */ /*bb_perror_msg("can't execute '%s'", argv[0]);*/ _exit(111); } /* parent */ /* Unfortunately, this is not reliable: according to standards * vfork() can be equivalent to fork() and we won't see value * of 'failed'. * Interested party can wait on pid and learn exit code. * If 111 - then it (most probably) failed to exec */ if (failed) { safe_waitpid(pid, NULL, 0); /* prevent zombie */ errno = failed; return -1; } return pid; } /* Die with an error message if we can't spawn a child process. */ pid_t FAST_FUNC xspawn(char **argv) { pid_t pid = spawn(argv); if (pid < 0) bb_simple_perror_msg_and_die(*argv); return pid; } #if ENABLE_FEATURE_PREFER_APPLETS struct nofork_save_area { jmp_buf die_jmp; const char *applet_name; uint32_t option_mask32; int die_sleep; uint8_t xfunc_error_retval; }; static void save_nofork_data(struct nofork_save_area *save) { memcpy(&save->die_jmp, &die_jmp, sizeof(die_jmp)); save->applet_name = applet_name; save->xfunc_error_retval = xfunc_error_retval; save->option_mask32 = option_mask32; save->die_sleep = die_sleep; } static void restore_nofork_data(struct nofork_save_area *save) { memcpy(&die_jmp, &save->die_jmp, sizeof(die_jmp)); applet_name = save->applet_name; xfunc_error_retval = save->xfunc_error_retval; option_mask32 = save->option_mask32; die_sleep = save->die_sleep; } int FAST_FUNC run_nofork_applet(int applet_no, char **argv) { int rc, argc; struct nofork_save_area old; save_nofork_data(&old); applet_name = APPLET_NAME(applet_no); xfunc_error_retval = EXIT_FAILURE; /* In case getopt() or getopt32() was already called: * reset the libc getopt() function, which keeps internal state. * * BSD-derived getopt() functions require that optind be set to 1 in * order to reset getopt() state. This used to be generally accepted * way of resetting getopt(). However, glibc's getopt() * has additional getopt() state beyond optind, and requires that * optind be set to zero to reset its state. So the unfortunate state of * affairs is that BSD-derived versions of getopt() misbehave if * optind is set to 0 in order to reset getopt(), and glibc's getopt() * will core dump if optind is set 1 in order to reset getopt(). * * More modern versions of BSD require that optreset be set to 1 in * order to reset getopt(). Sigh. Standards, anyone? */ #ifdef __GLIBC__ optind = 0; #else /* BSD style */ optind = 1; /* optreset = 1; */ #endif /* optarg = NULL; opterr = 1; optopt = 63; - do we need this too? */ /* (values above are what they initialized to in glibc and uclibc) */ /* option_mask32 = 0; - not needed, no applet depends on it being 0 */ argc = 1; while (argv[argc]) argc++; /* Special flag for xfunc_die(). If xfunc will "die" * in NOFORK applet, xfunc_die() sees negative * die_sleep and longjmp here instead. */ die_sleep = -1; rc = setjmp(die_jmp); if (!rc) { /* Some callers (xargs) * need argv untouched because they free argv[i]! */ char *tmp_argv[argc+1]; memcpy(tmp_argv, argv, (argc+1) * sizeof(tmp_argv[0])); /* Finally we can call NOFORK applet's main() */ rc = applet_main[applet_no](argc, tmp_argv); } else { /* xfunc died in NOFORK applet */ /* in case they meant to return 0... */ if (rc == -2222) rc = 0; } /* Restoring some globals */ restore_nofork_data(&old); /* Other globals can be simply reset to defaults */ #ifdef __GLIBC__ optind = 0; #else /* BSD style */ optind = 1; #endif return rc & 0xff; /* don't confuse people with "exitcodes" >255 */ } #endif /* FEATURE_PREFER_APPLETS */ int FAST_FUNC spawn_and_wait(char **argv) { int rc; #if ENABLE_FEATURE_PREFER_APPLETS int a = find_applet_by_name(argv[0]); if (a >= 0 && (APPLET_IS_NOFORK(a) # if BB_MMU || APPLET_IS_NOEXEC(a) /* NOEXEC trick needs fork() */ # endif )) { # if BB_MMU if (APPLET_IS_NOFORK(a)) # endif { return run_nofork_applet(a, argv); } # if BB_MMU /* MMU only */ /* a->noexec is true */ rc = fork(); if (rc) /* parent or error */ return wait4pid(rc); /* child */ xfunc_error_retval = EXIT_FAILURE; run_applet_no_and_exit(a, argv); # endif } #endif /* FEATURE_PREFER_APPLETS */ rc = spawn(argv); return wait4pid(rc); } #if !BB_MMU void FAST_FUNC re_exec(char **argv) { /* high-order bit of first char in argv[0] is a hidden * "we have (already) re-execed, don't do it again" flag */ argv[0][0] |= 0x80; execv(bb_busybox_exec_path, argv); bb_perror_msg_and_die("can't execute '%s'", bb_busybox_exec_path); } pid_t FAST_FUNC fork_or_rexec(char **argv) { pid_t pid; /* Maybe we are already re-execed and come here again? */ if (re_execed) return 0; pid = xvfork(); if (pid) /* parent */ return pid; /* child - re-exec ourself */ re_exec(argv); } #endif /* Due to a #define in libbb.h on MMU systems we actually have 1 argument - * char **argv "vanishes" */ void FAST_FUNC bb_daemonize_or_rexec(int flags, char **argv) { int fd; if (flags & DAEMON_CHDIR_ROOT) xchdir("/"); if (flags & DAEMON_DEVNULL_STDIO) { close(0); close(1); close(2); } fd = open(bb_dev_null, O_RDWR); if (fd < 0) { /* NB: we can be called as bb_sanitize_stdio() from init * or mdev, and there /dev/null may legitimately not (yet) exist! * Do not use xopen above, but obtain _ANY_ open descriptor, * even bogus one as below. */ fd = xopen("/", O_RDONLY); /* don't believe this can fail */ } while ((unsigned)fd < 2) fd = dup(fd); /* have 0,1,2 open at least to /dev/null */ if (!(flags & DAEMON_ONLY_SANITIZE)) { if (fork_or_rexec(argv)) exit(EXIT_SUCCESS); /* parent */ /* if daemonizing, detach from stdio & ctty */ setsid(); dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); if (flags & DAEMON_DOUBLE_FORK) { /* On Linux, session leader can acquire ctty * unknowingly, by opening a tty. * Prevent this: stop being a session leader. */ if (fork_or_rexec(argv)) exit(EXIT_SUCCESS); /* parent */ } } while (fd > 2) { close(fd--); if (!(flags & DAEMON_CLOSE_EXTRA_FDS)) return; /* else close everything after fd#2 */ } } void FAST_FUNC bb_sanitize_stdio(void) { bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL); } busybox-1.22.1/libbb/ptr_to_globals.c0000644000000000000000000000134312263563520016223 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright (C) 2008 by Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include struct globals; #ifndef GCC_COMBINE /* We cheat here. It is declared as const ptr in libbb.h, * but here we make it live in R/W memory */ struct globals *ptr_to_globals; #ifdef __GLIBC__ int *bb_errno; #endif #else /* gcc -combine will see through and complain */ /* Using alternative method which is more likely to break * on weird architectures, compilers, linkers and so on */ struct globals *const ptr_to_globals __attribute__ ((section (".data"))); #ifdef __GLIBC__ int *const bb_errno __attribute__ ((section (".data"))); #endif #endif busybox-1.22.1/libbb/parse_config.c0000644000000000000000000001437112263563520015655 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * config file parser helper * * Copyright (C) 2008 by Vladimir Dronnikov * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * Also for use in uClibc (http://uclibc.org/) licensed under LGPLv2.1 or later. */ /* Uncomment to enable test applet */ ////config:config PARSE ////config: bool "Uniform config file parser debugging applet: parse" ////config: default n ////config: help ////config: Typical usage of parse API: ////config: char *t[3]; ////config: parser_t *p = config_open(filename); ////config: while (config_read(p, t, 3, 0, delimiters, flags)) { // 1..3 tokens ////config: bb_error_msg("TOKENS: '%s''%s''%s'", t[0], t[1], t[2]); ////config: } ////config: config_close(p); ////applet:IF_PARSE(APPLET(parse, BB_DIR_USR_BIN, BB_SUID_DROP)) //kbuild:lib-y += parse_config.o //usage:#define parse_trivial_usage //usage: "[-x] [-n MAXTOKENS] [-m MINTOKENS] [-d DELIMS] [-f FLAGS] FILE..." //usage:#define parse_full_usage "\n\n" //usage: " -x Suppress output (for benchmarking)" #include "libbb.h" #if defined ENABLE_PARSE && ENABLE_PARSE int parse_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int parse_main(int argc UNUSED_PARAM, char **argv) { const char *delims = "# \t"; char **t; unsigned flags = PARSE_NORMAL; int mintokens = 0, ntokens = 128; unsigned noout; opt_complementary = "-1:n+:m+:f+"; noout = 1 & getopt32(argv, "xn:m:d:f:", &ntokens, &mintokens, &delims, &flags); //argc -= optind; argv += optind; t = xmalloc(sizeof(t[0]) * ntokens); while (*argv) { int n; parser_t *p = config_open(*argv); while ((n = config_read(p, t, ntokens, mintokens, delims, flags)) != 0) { if (!noout) { for (int i = 0; i < n; ++i) printf("[%s]", t[i]); puts(""); } } config_close(p); argv++; } return EXIT_SUCCESS; } #endif parser_t* FAST_FUNC config_open2(const char *filename, FILE* FAST_FUNC (*fopen_func)(const char *path)) { FILE* fp; parser_t *parser; fp = fopen_func(filename); if (!fp) return NULL; parser = xzalloc(sizeof(*parser)); parser->fp = fp; return parser; } parser_t* FAST_FUNC config_open(const char *filename) { return config_open2(filename, fopen_or_warn_stdin); } void FAST_FUNC config_close(parser_t *parser) { if (parser) { if (PARSE_KEEP_COPY) /* compile-time constant */ free(parser->data); fclose(parser->fp); free(parser->line); free(parser->nline); free(parser); } } /* This function reads an entire line from a text file, * up to a newline, exclusive. * Trailing '\' is recognized as line continuation. * Returns -1 if EOF/error. */ static int get_line_with_continuation(parser_t *parser) { ssize_t len, nlen; char *line; len = getline(&parser->line, &parser->line_alloc, parser->fp); if (len <= 0) return len; line = parser->line; for (;;) { parser->lineno++; if (line[len - 1] == '\n') len--; if (len == 0 || line[len - 1] != '\\') break; len--; nlen = getline(&parser->nline, &parser->nline_alloc, parser->fp); if (nlen <= 0) break; if (parser->line_alloc < len + nlen + 1) { parser->line_alloc = len + nlen + 1; line = parser->line = xrealloc(line, parser->line_alloc); } memcpy(&line[len], parser->nline, nlen); len += nlen; } line[len] = '\0'; return len; } /* 0. If parser is NULL return 0. 1. Read a line from config file. If nothing to read then return 0. Handle continuation character. Advance lineno for each physical line. Discard everything past comment character. 2. if PARSE_TRIM is set (default), remove leading and trailing delimiters. 3. If resulting line is empty goto 1. 4. Look for first delimiter. If !PARSE_COLLAPSE or !PARSE_TRIM is set then remember the token as empty. 5. Else (default) if number of seen tokens is equal to max number of tokens (token is the last one) and PARSE_GREEDY is set then the remainder of the line is the last token. Else (token is not last or PARSE_GREEDY is not set) just replace first delimiter with '\0' thus delimiting the token. 6. Advance line pointer past the end of token. If number of seen tokens is less than required number of tokens then goto 4. 7. Check the number of seen tokens is not less the min number of tokens. Complain or die otherwise depending on PARSE_MIN_DIE. 8. Return the number of seen tokens. mintokens > 0 make config_read() print error message if less than mintokens (but more than 0) are found. Empty lines are always skipped (not warned about). */ #undef config_read int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const char *delims) { char *line; int ntokens, mintokens; int t; if (!parser) return 0; ntokens = (uint8_t)flags; mintokens = (uint8_t)(flags >> 8); again: memset(tokens, 0, sizeof(tokens[0]) * ntokens); /* Read one line (handling continuations with backslash) */ if (get_line_with_continuation(parser) < 0) return 0; line = parser->line; /* Skip token in the start of line? */ if (flags & PARSE_TRIM) line += strspn(line, delims + 1); if (line[0] == '\0' || line[0] == delims[0]) goto again; if (flags & PARSE_KEEP_COPY) { free(parser->data); parser->data = xstrdup(line); } /* Tokenize the line */ t = 0; do { /* Pin token */ tokens[t] = line; /* Combine remaining arguments? */ if ((t != (ntokens-1)) || !(flags & PARSE_GREEDY)) { /* Vanilla token, find next delimiter */ line += strcspn(line, delims[0] ? delims : delims + 1); } else { /* Combining, find comment char if any */ line = strchrnul(line, PARSE_EOL_COMMENTS ? delims[0] : '\0'); /* Trim any extra delimiters from the end */ if (flags & PARSE_TRIM) { while (strchr(delims + 1, line[-1]) != NULL) line--; } } /* Token not terminated? */ if (*line == delims[0]) *line = '\0'; else if (*line != '\0') *line++ = '\0'; #if 0 /* unused so far */ if (flags & PARSE_ESCAPE) { strcpy_and_process_escape_sequences(tokens[t], tokens[t]); } #endif /* Skip possible delimiters */ if (flags & PARSE_COLLAPSE) line += strspn(line, delims + 1); t++; } while (*line && *line != delims[0] && t < ntokens); if (t < mintokens) { bb_error_msg("bad line %u: %d tokens found, %d needed", parser->lineno, t, mintokens); if (flags & PARSE_MIN_DIE) xfunc_die(); goto again; } return t; } busybox-1.22.1/libbb/udp_io.c0000644000000000000000000001246412263563520014476 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 2007 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" /* * This asks kernel to let us know dst addr/port of incoming packets * We don't check for errors here. Not supported == won't be used */ void FAST_FUNC socket_want_pktinfo(int fd UNUSED_PARAM) { #ifdef IP_PKTINFO setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &const_int_1, sizeof(int)); #endif #if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &const_int_1, sizeof(int)); #endif } ssize_t FAST_FUNC send_to_from(int fd, void *buf, size_t len, int flags, const struct sockaddr *to, const struct sockaddr *from, socklen_t tolen) { #ifndef IP_PKTINFO (void)from; /* suppress "unused from" warning */ return sendto(fd, buf, len, flags, to, tolen); #else struct iovec iov[1]; struct msghdr msg; union { char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))]; # if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; # endif } u; struct cmsghdr* cmsgptr; if (from->sa_family != AF_INET # if ENABLE_FEATURE_IPV6 && from->sa_family != AF_INET6 # endif ) { /* ANY local address */ return sendto(fd, buf, len, flags, to, tolen); } /* man recvmsg and man cmsg is needed to make sense of code below */ iov[0].iov_base = buf; iov[0].iov_len = len; memset(&u, 0, sizeof(u)); memset(&msg, 0, sizeof(msg)); msg.msg_name = (void *)(struct sockaddr *)to; /* or compiler will annoy us */ msg.msg_namelen = tolen; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = &u; msg.msg_controllen = sizeof(u); msg.msg_flags = flags; cmsgptr = CMSG_FIRSTHDR(&msg); if (to->sa_family == AF_INET && from->sa_family == AF_INET) { struct in_pktinfo *pktptr; cmsgptr->cmsg_level = IPPROTO_IP; cmsgptr->cmsg_type = IP_PKTINFO; cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); pktptr = (struct in_pktinfo *)(CMSG_DATA(cmsgptr)); /*pktptr->ipi_ifindex = 0; -- already done by memset(u...) */ /* In general, CMSG_DATA() can be unaligned, but in this case * we know for sure it is sufficiently aligned: * CMSG_FIRSTHDR simply returns &u above, * and CMSG_DATA returns &u + size_t + int + int. * Thus direct assignment is ok: */ pktptr->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr; } # if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) else if (to->sa_family == AF_INET6 && from->sa_family == AF_INET6) { struct in6_pktinfo *pktptr; cmsgptr->cmsg_level = IPPROTO_IPV6; cmsgptr->cmsg_type = IPV6_PKTINFO; cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); pktptr = (struct in6_pktinfo *)(CMSG_DATA(cmsgptr)); /* pktptr->ipi6_ifindex = 0; -- already done by memset(u...) */ pktptr->ipi6_addr = ((struct sockaddr_in6*)from)->sin6_addr; } # endif msg.msg_controllen = cmsgptr->cmsg_len; return sendmsg(fd, &msg, flags); #endif } /* NB: this will never set port# in 'to'! * _Only_ IP/IPv6 address part of 'to' is _maybe_ modified. * Typical usage is to preinit 'to' with "default" value * before calling recv_from_to(). */ ssize_t FAST_FUNC recv_from_to(int fd, void *buf, size_t len, int flags, struct sockaddr *from, struct sockaddr *to, socklen_t sa_size) { #ifndef IP_PKTINFO (void)to; /* suppress "unused to" warning */ return recvfrom(fd, buf, len, flags, from, &sa_size); #else /* man recvmsg and man cmsg is needed to make sense of code below */ struct iovec iov[1]; union { char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))]; # if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; # endif } u; struct cmsghdr *cmsgptr; struct msghdr msg; ssize_t recv_length; iov[0].iov_base = buf; iov[0].iov_len = len; memset(&msg, 0, sizeof(msg)); msg.msg_name = (struct sockaddr *)from; msg.msg_namelen = sa_size; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = &u; msg.msg_controllen = sizeof(u); recv_length = recvmsg(fd, &msg, flags); if (recv_length < 0) return recv_length; # define to4 ((struct sockaddr_in*)to) # define to6 ((struct sockaddr_in6*)to) /* Here we try to retrieve destination IP and memorize it */ for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr) ) { if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_PKTINFO ) { const int IPI_ADDR_OFF = offsetof(struct in_pktinfo, ipi_addr); to->sa_family = AF_INET; /*# define pktinfo(cmsgptr) ( (struct in_pktinfo*)(CMSG_DATA(cmsgptr)) )*/ /*to4->sin_addr = pktinfo(cmsgptr)->ipi_addr; - may be unaligned */ memcpy(&to4->sin_addr, (char*)(CMSG_DATA(cmsgptr)) + IPI_ADDR_OFF, sizeof(to4->sin_addr)); /*to4->sin_port = 123; - this data is not supplied by kernel */ break; } # if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) if (cmsgptr->cmsg_level == IPPROTO_IPV6 && cmsgptr->cmsg_type == IPV6_PKTINFO ) { const int IPI6_ADDR_OFF = offsetof(struct in6_pktinfo, ipi6_addr); to->sa_family = AF_INET6; /*# define pktinfo(cmsgptr) ( (struct in6_pktinfo*)(CMSG_DATA(cmsgptr)) )*/ /*to6->sin6_addr = pktinfo(cmsgptr)->ipi6_addr; - may be unaligned */ memcpy(&to6->sin6_addr, (char*)(CMSG_DATA(cmsgptr)) + IPI6_ADDR_OFF, sizeof(to6->sin6_addr)); /*to6->sin6_port = 123; */ break; } # endif } return recv_length; #endif } busybox-1.22.1/libbb/nuke_str.c0000644000000000000000000000055612263563520015050 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 2008 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ //kbuild:lib-y += nuke_str.o #include "libbb.h" void FAST_FUNC nuke_str(char *str) { if (str) { while (*str) *str++ = 0; /* or: memset(str, 0, strlen(str)); - not as small as above */ } } busybox-1.22.1/libbb/compare_string_array.c0000644000000000000000000000373012263563520017425 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" /* returns the array index of the string */ /* (index of first match is returned, or -1) */ int FAST_FUNC index_in_str_array(const char *const string_array[], const char *key) { int i; for (i = 0; string_array[i] != 0; i++) { if (strcmp(string_array[i], key) == 0) { return i; } } return -1; } int FAST_FUNC index_in_strings(const char *strings, const char *key) { int idx = 0; while (*strings) { if (strcmp(strings, key) == 0) { return idx; } strings += strlen(strings) + 1; /* skip NUL */ idx++; } return -1; } /* returns the array index of the string, even if it matches only a beginning */ /* (index of first match is returned, or -1) */ #ifdef UNUSED int FAST_FUNC index_in_substr_array(const char *const string_array[], const char *key) { int i; int len = strlen(key); if (len) { for (i = 0; string_array[i] != 0; i++) { if (strncmp(string_array[i], key, len) == 0) { return i; } } } return -1; } #endif int FAST_FUNC index_in_substrings(const char *strings, const char *key) { int matched_idx = -1; const int len = strlen(key); if (len) { int idx = 0; while (*strings) { if (strncmp(strings, key, len) == 0) { if (strings[len] == '\0') return idx; /* exact match */ if (matched_idx >= 0) return -1; /* ambiguous match */ matched_idx = idx; } strings += strlen(strings) + 1; /* skip NUL */ idx++; } } return matched_idx; } const char* FAST_FUNC nth_string(const char *strings, int n) { while (n) { n--; strings += strlen(strings) + 1; } return strings; } #ifdef UNUSED_SO_FAR /* only brctl.c needs it yet */ /* Returns 0 for no, 1 for yes or a negative value on error. */ smallint FAST_FUNC yesno(const char *str) { static const char no_yes[] ALIGN1 = "0\0" "off\0" "no\0" "1\0" "on\0" "yes\0"; int ret = index_in_substrings(no_yes, str); return ret / 3; } #endif busybox-1.22.1/libbb/hash_md5prime.c0000644000000000000000000003433212263563520015742 0ustar rootroot/* This file is not used by busybox right now. * However, the code here seems to be a tiny bit smaller * than one in md5.c. Need to investigate which one * is better overall... * Hint: grep for md5prime to find places where you can switch * md5.c/md5prime.c */ /* * MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm * * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All * rights reserved. * * License to copy and use this software is granted provided that it * is identified as the "RSA Data Security, Inc. MD5 Message-Digest * Algorithm" in all material mentioning or referencing this software * or this function. * * License is also granted to make and use derivative works provided * that such works are identified as "derived from the RSA Data * Security, Inc. MD5 Message-Digest Algorithm" in all material * mentioning or referencing the derived work. * * RSA Data Security, Inc. makes no representations concerning either * the merchantability of this software or the suitability of this * software for any particular purpose. It is provided "as is" * without express or implied warranty of any kind. * * These notices must be retained in any copies of any part of this * documentation and/or software. * * $FreeBSD: src/lib/libmd/md5c.c,v 1.9.2.1 1999/08/29 14:57:12 peter Exp $ * * This code is the same as the code published by RSA Inc. It has been * edited for clarity and style only. * * ---------------------------------------------------------------------------- * The md5_crypt() function was taken from freeBSD's libcrypt and contains * this license: * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * * $FreeBSD: src/lib/libcrypt/crypt.c,v 1.7.2.1 1999/08/29 14:56:33 peter Exp $ * * ---------------------------------------------------------------------------- * On April 19th, 2001 md5_crypt() was modified to make it reentrant * by Erik Andersen * * June 28, 2001 Manuel Novoa III * * "Un-inlined" code using loops and static const tables in order to * reduce generated code size (on i386 from approx 4k to approx 2.5k). * * June 29, 2001 Manuel Novoa III * * Completely removed static PADDING array. * * Reintroduced the loop unrolling in md5_transform and added the * MD5_SMALL option for configurability. Define below as: * 0 fully unrolled loops * 1 partially unrolled (4 ops per loop) * 2 no unrolling -- introduces the need to swap 4 variables (slow) * 3 no unrolling and all 4 loops merged into one with switch * in each loop (glacial) * On i386, sizes are roughly (-Os -fno-builtin): * 0: 3k 1: 2.5k 2: 2.2k 3: 2k * * Since SuSv3 does not require crypt_r, modified again August 7, 2002 * by Erik Andersen to remove reentrance stuff... */ #include "libbb.h" /* 1: fastest, 3: smallest */ #if CONFIG_MD5_SMALL < 1 # define MD5_SMALL 1 #elif CONFIG_MD5_SMALL > 3 # define MD5_SMALL 3 #else # define MD5_SMALL CONFIG_MD5_SMALL #endif #if BB_LITTLE_ENDIAN #define memcpy32_cpu2le memcpy #define memcpy32_le2cpu memcpy #else /* Encodes input (uint32_t) into output (unsigned char). * Assumes len is a multiple of 4. */ static void memcpy32_cpu2le(unsigned char *output, uint32_t *input, unsigned len) { unsigned i, j; for (i = 0, j = 0; j < len; i++, j += 4) { output[j] = input[i]; output[j+1] = (input[i] >> 8); output[j+2] = (input[i] >> 16); output[j+3] = (input[i] >> 24); } } /* Decodes input (unsigned char) into output (uint32_t). * Assumes len is a multiple of 4. */ static void memcpy32_le2cpu(uint32_t *output, const unsigned char *input, unsigned len) { unsigned i, j; for (i = 0, j = 0; j < len; i++, j += 4) output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j+1]) << 8) | (((uint32_t)input[j+2]) << 16) | (((uint32_t)input[j+3]) << 24); } #endif /* i386 */ /* F, G, H and I are basic MD5 functions. */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | ~(z))) /* rotl32 rotates x left n bits. */ #define rotl32(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. * Rotation is separate from addition to prevent recomputation. */ #define FF(a, b, c, d, x, s, ac) { \ (a) += F((b), (c), (d)) + (x) + (uint32_t)(ac); \ (a) = rotl32((a), (s)); \ (a) += (b); \ } #define GG(a, b, c, d, x, s, ac) { \ (a) += G((b), (c), (d)) + (x) + (uint32_t)(ac); \ (a) = rotl32((a), (s)); \ (a) += (b); \ } #define HH(a, b, c, d, x, s, ac) { \ (a) += H((b), (c), (d)) + (x) + (uint32_t)(ac); \ (a) = rotl32((a), (s)); \ (a) += (b); \ } #define II(a, b, c, d, x, s, ac) { \ (a) += I((b), (c), (d)) + (x) + (uint32_t)(ac); \ (a) = rotl32((a), (s)); \ (a) += (b); \ } /* MD5 basic transformation. Transforms state based on block. */ static void md5_transform(uint32_t state[4], const unsigned char block[64]) { uint32_t a, b, c, d, x[16]; #if MD5_SMALL > 1 uint32_t temp; const unsigned char *ps; static const unsigned char S[] = { 7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21 }; #endif /* MD5_SMALL > 1 */ #if MD5_SMALL > 0 const uint32_t *pc; const unsigned char *pp; int i; static const uint32_t C[] = { /* round 1 */ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, /* round 2 */ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, /* round 3 */ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, /* round 4 */ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 }; static const unsigned char P[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */ 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */ 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */ 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */ }; #endif /* MD5_SMALL > 0 */ memcpy32_le2cpu(x, block, 64); a = state[0]; b = state[1]; c = state[2]; d = state[3]; #if MD5_SMALL > 2 pc = C; pp = P; ps = S - 4; for (i = 0; i < 64; i++) { if ((i & 0x0f) == 0) ps += 4; temp = a; switch (i>>4) { case 0: temp += F(b, c, d); break; case 1: temp += G(b, c, d); break; case 2: temp += H(b, c, d); break; case 3: temp += I(b, c, d); break; } temp += x[*pp++] + *pc++; temp = rotl32(temp, ps[i & 3]); temp += b; a = d; d = c; c = b; b = temp; } #elif MD5_SMALL > 1 pc = C; pp = P; ps = S; /* Round 1 */ for (i = 0; i < 16; i++) { FF(a, b, c, d, x[*pp], ps[i & 0x3], *pc); pp++; pc++; temp = d; d = c; c = b; b = a; a = temp; } /* Round 2 */ ps += 4; for (; i < 32; i++) { GG(a, b, c, d, x[*pp], ps[i & 0x3], *pc); pp++; pc++; temp = d; d = c; c = b; b = a; a = temp; } /* Round 3 */ ps += 4; for (; i < 48; i++) { HH(a, b, c, d, x[*pp], ps[i & 0x3], *pc); pp++; pc++; temp = d; d = c; c = b; b = a; a = temp; } /* Round 4 */ ps += 4; for (; i < 64; i++) { II(a, b, c, d, x[*pp], ps[i & 0x3], *pc); pp++; pc++; temp = d; d = c; c = b; b = a; a = temp; } #elif MD5_SMALL > 0 pc = C; pp = P; /* Round 1 */ for (i = 0; i < 4; i++) { FF(a, b, c, d, x[*pp], 7, *pc); pp++; pc++; FF(d, a, b, c, x[*pp], 12, *pc); pp++; pc++; FF(c, d, a, b, x[*pp], 17, *pc); pp++; pc++; FF(b, c, d, a, x[*pp], 22, *pc); pp++; pc++; } /* Round 2 */ for (i = 0; i < 4; i++) { GG(a, b, c, d, x[*pp], 5, *pc); pp++; pc++; GG(d, a, b, c, x[*pp], 9, *pc); pp++; pc++; GG(c, d, a, b, x[*pp], 14, *pc); pp++; pc++; GG(b, c, d, a, x[*pp], 20, *pc); pp++; pc++; } /* Round 3 */ for (i = 0; i < 4; i++) { HH(a, b, c, d, x[*pp], 4, *pc); pp++; pc++; HH(d, a, b, c, x[*pp], 11, *pc); pp++; pc++; HH(c, d, a, b, x[*pp], 16, *pc); pp++; pc++; HH(b, c, d, a, x[*pp], 23, *pc); pp++; pc++; } /* Round 4 */ for (i = 0; i < 4; i++) { II(a, b, c, d, x[*pp], 6, *pc); pp++; pc++; II(d, a, b, c, x[*pp], 10, *pc); pp++; pc++; II(c, d, a, b, x[*pp], 15, *pc); pp++; pc++; II(b, c, d, a, x[*pp], 21, *pc); pp++; pc++; } #else /* Round 1 */ #define S11 7 #define S12 12 #define S13 17 #define S14 22 FF(a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ FF(d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ FF(c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ FF(b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ FF(a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ FF(d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ FF(c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ FF(b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ FF(a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ FF(d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ /* Round 2 */ #define S21 5 #define S22 9 #define S23 14 #define S24 20 GG(a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ GG(d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ GG(b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ GG(a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ GG(b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ GG(a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ GG(c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ GG(b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ GG(d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ GG(c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ /* Round 3 */ #define S31 4 #define S32 11 #define S33 16 #define S34 23 HH(a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ HH(d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ HH(a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ HH(d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ HH(c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ HH(d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ HH(c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ HH(b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ HH(a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ HH(b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ /* Round 4 */ #define S41 6 #define S42 10 #define S43 15 #define S44 21 II(a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ II(d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ II(b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ II(d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ II(b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ II(a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ II(c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ II(a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ II(c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ II(b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ #endif state[0] += a; state[1] += b; state[2] += c; state[3] += d; /* Zeroize sensitive information. */ memset(x, 0, sizeof(x)); } /* MD5 initialization. */ void FAST_FUNC md5_begin(md5_ctx_t *context) { context->count[0] = context->count[1] = 0; /* Load magic initialization constants. */ context->state[0] = 0x67452301; context->state[1] = 0xefcdab89; context->state[2] = 0x98badcfe; context->state[3] = 0x10325476; } /* * MD5 block update operation. Continues an MD5 message-digest * operation, processing another message block, and updating * the context. */ void FAST_FUNC md5_hash(const void *buffer, size_t inputLen, md5_ctx_t *context) { unsigned i, idx, partLen; const unsigned char *input = buffer; /* Compute number of bytes mod 64 */ idx = (context->count[0] >> 3) & 0x3F; /* Update number of bits */ context->count[0] += (inputLen << 3); if (context->count[0] < (inputLen << 3)) context->count[1]++; context->count[1] += (inputLen >> 29); /* Transform as many times as possible. */ i = 0; partLen = 64 - idx; if (inputLen >= partLen) { memcpy(&context->buffer[idx], input, partLen); md5_transform(context->state, context->buffer); for (i = partLen; i + 63 < inputLen; i += 64) md5_transform(context->state, &input[i]); idx = 0; } /* Buffer remaining input */ memcpy(&context->buffer[idx], &input[i], inputLen - i); } /* * MD5 finalization. Ends an MD5 message-digest operation, * writing the message digest. */ void FAST_FUNC md5_end(void *digest, md5_ctx_t *context) { unsigned idx, padLen; unsigned char bits[8]; unsigned char padding[64]; /* Add padding followed by original length. */ memset(padding, 0, sizeof(padding)); padding[0] = 0x80; /* save number of bits */ memcpy32_cpu2le(bits, context->count, 8); /* pad out to 56 mod 64 */ idx = (context->count[0] >> 3) & 0x3f; padLen = (idx < 56) ? (56 - idx) : (120 - idx); md5_hash(padding, padLen, context); /* append length (before padding) */ md5_hash(bits, 8, context); /* Store state in digest */ memcpy32_cpu2le(digest, context->state, 16); } busybox-1.22.1/libbb/pidfile.c0000644000000000000000000000157512263563520014634 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * pid file routines * * Copyright (C) 2007 by Stephane Billiart * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* Override ENABLE_FEATURE_PIDFILE */ #define WANT_PIDFILE 1 #include "libbb.h" smallint wrote_pidfile; void FAST_FUNC write_pidfile(const char *path) { int pid_fd; char *end; char buf[sizeof(int)*3 + 2]; struct stat sb; if (!path) return; /* we will overwrite stale pidfile */ pid_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666); if (pid_fd < 0) return; /* path can be "/dev/null"! Test for such cases */ wrote_pidfile = (fstat(pid_fd, &sb) == 0) && S_ISREG(sb.st_mode); if (wrote_pidfile) { /* few bytes larger, but doesn't use stdio */ end = utoa_to_buf(getpid(), buf, sizeof(buf)); *end = '\n'; full_write(pid_fd, buf, end - buf + 1); } close(pid_fd); } busybox-1.22.1/libbb/getpty.c0000644000000000000000000000265012263563520014527 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini getpty implementation for busybox * Bjorn Wesen, Axis Communications AB (bjornw@axis.com) * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" #define DEBUG 0 int FAST_FUNC xgetpty(char *line) { int p; #if ENABLE_FEATURE_DEVPTS p = open("/dev/ptmx", O_RDWR); if (p > 0) { grantpt(p); /* chmod+chown corresponding slave pty */ unlockpt(p); /* (what does this do?) */ # ifndef HAVE_PTSNAME_R { const char *name; name = ptsname(p); /* find out the name of slave pty */ if (!name) { bb_perror_msg_and_die("ptsname error (is /dev/pts mounted?)"); } safe_strncpy(line, name, GETPTY_BUFSIZE); } # else /* find out the name of slave pty */ if (ptsname_r(p, line, GETPTY_BUFSIZE-1) != 0) { bb_perror_msg_and_die("ptsname error (is /dev/pts mounted?)"); } line[GETPTY_BUFSIZE-1] = '\0'; # endif return p; } #else struct stat stb; int i; int j; strcpy(line, "/dev/ptyXX"); for (i = 0; i < 16; i++) { line[8] = "pqrstuvwxyzabcde"[i]; line[9] = '0'; if (stat(line, &stb) < 0) { continue; } for (j = 0; j < 16; j++) { line[9] = j < 10 ? j + '0' : j - 10 + 'a'; if (DEBUG) fprintf(stderr, "Trying to open device: %s\n", line); p = open(line, O_RDWR | O_NOCTTY); if (p >= 0) { line[5] = 't'; return p; } } } #endif /* FEATURE_DEVPTS */ bb_error_msg_and_die("can't find free pty"); } busybox-1.22.1/libbb/last_char_is.c0000644000000000000000000000103612263563520015643 0ustar rootroot/* 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 source tree. */ #include "libbb.h" /* Find out if the last character of a string matches the one given. * Don't underrun the buffer if the string length is 0. */ char* FAST_FUNC last_char_is(const char *s, int c) { if (s && *s) { size_t sz = strlen(s) - 1; s += sz; if ( (unsigned char)*s == c) return (char*)s; } return NULL; } busybox-1.22.1/libbb/parse_mode.c0000644000000000000000000000654712263563520015342 0ustar rootroot/* 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 source tree. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */ #include "libbb.h" /* This function is used from NOFORK applets. It must not allocate anything */ #define FILEMODEBITS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) int FAST_FUNC bb_parse_mode(const char *s, mode_t *current_mode) { static const mode_t who_mask[] = { S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */ S_ISUID | S_IRWXU, /* u */ S_ISGID | S_IRWXG, /* g */ S_IRWXO /* o */ }; static const mode_t perm_mask[] = { S_IRUSR | S_IRGRP | S_IROTH, /* r */ S_IWUSR | S_IWGRP | S_IWOTH, /* w */ S_IXUSR | S_IXGRP | S_IXOTH, /* x */ S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */ S_ISUID | S_ISGID, /* s */ S_ISVTX /* t */ }; static const char who_chars[] ALIGN1 = "augo"; static const char perm_chars[] ALIGN1 = "rwxXst"; const char *p; mode_t wholist; mode_t permlist; mode_t new_mode; char op; if ((unsigned char)(*s - '0') < 8) { unsigned long tmp; char *e; tmp = strtoul(s, &e, 8); if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */ return 0; } *current_mode = tmp; return 1; } new_mode = *current_mode; /* Note: we allow empty clauses, and hence empty modes. * We treat an empty mode as no change to perms. */ while (*s) { /* Process clauses. */ if (*s == ',') { /* We allow empty clauses. */ ++s; continue; } /* Get a wholist. */ wholist = 0; WHO_LIST: p = who_chars; do { if (*p == *s) { wholist |= who_mask[(int)(p-who_chars)]; if (!*++s) { return 0; } goto WHO_LIST; } } while (*++p); do { /* Process action list. */ if ((*s != '+') && (*s != '-')) { if (*s != '=') { return 0; } /* Since op is '=', clear all bits corresponding to the * wholist, or all file bits if wholist is empty. */ permlist = ~FILEMODEBITS; if (wholist) { permlist = ~wholist; } new_mode &= permlist; } op = *s++; /* Check for permcopy. */ p = who_chars + 1; /* Skip 'a' entry. */ do { if (*p == *s) { int i = 0; permlist = who_mask[(int)(p-who_chars)] & (S_IRWXU | S_IRWXG | S_IRWXO) & new_mode; do { if (permlist & perm_mask[i]) { permlist |= perm_mask[i]; } } while (++i < 3); ++s; goto GOT_ACTION; } } while (*++p); /* It was not a permcopy, so get a permlist. */ permlist = 0; PERM_LIST: p = perm_chars; do { if (*p == *s) { if ((*p != 'X') || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH)) ) { permlist |= perm_mask[(int)(p-perm_chars)]; } if (!*++s) { break; } goto PERM_LIST; } } while (*++p); GOT_ACTION: if (permlist) { /* The permlist was nonempty. */ mode_t tmp = wholist; if (!wholist) { mode_t u_mask = umask(0); umask(u_mask); tmp = ~u_mask; } permlist &= tmp; if (op == '-') { new_mode &= ~permlist; } else { new_mode |= permlist; } } } while (*s && (*s != ',')); } *current_mode = new_mode; return 1; } busybox-1.22.1/libbb/change_identity.c0000644000000000000000000000364412263563520016355 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright 1989 - 1991, Julianne Frances Haugh * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Julianne F. Haugh nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "libbb.h" /* Become the user and group(s) specified by PW. */ void FAST_FUNC change_identity(const struct passwd *pw) { if (initgroups(pw->pw_name, pw->pw_gid) == -1) bb_perror_msg_and_die("can't set groups"); endgrent(); /* helps to close a fd used internally by libc */ xsetgid(pw->pw_gid); xsetuid(pw->pw_uid); } busybox-1.22.1/libbb/makedev.c0000644000000000000000000000153712263563520014632 0ustar rootroot/* * Utility routines. * * Copyright (C) 2006 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* We do not include libbb.h - #define makedev() is there! */ #include "platform.h" /* Different Unixes want different headers for makedev */ #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ || defined(__APPLE__) # include #else # include # include #endif #ifdef __GLIBC__ /* At least glibc has horrendously large inline for this, so wrap it. */ /* uclibc people please check - do we need "&& !__UCLIBC__" above? */ /* Suppress gcc "no previous prototype" warning */ unsigned long long FAST_FUNC bb_makedev(unsigned major, unsigned minor); unsigned long long FAST_FUNC bb_makedev(unsigned major, unsigned minor) { return makedev(major, minor); } #endif busybox-1.22.1/libbb/fflush_stdout_and_exit.c0000644000000000000000000000123612263563520017756 0ustar rootroot/* 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 source tree. */ /* Attempt to fflush(stdout), and exit with an error code if stdout is * in an error state. */ #include "libbb.h" void FAST_FUNC fflush_stdout_and_exit(int retval) { if (fflush(stdout)) bb_perror_msg_and_die(bb_msg_standard_output); if (ENABLE_FEATURE_PREFER_APPLETS && die_sleep < 0) { /* We are in NOFORK applet. Do not exit() directly, * but use xfunc_die() */ xfunc_error_retval = retval; xfunc_die(); } exit(retval); } busybox-1.22.1/libbb/ask_confirmation.c0000644000000000000000000000106112263563520016534 0ustar rootroot/* 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 source tree. */ /* Read a line from stdin. If the first non-whitespace char is 'y' or 'Y', * return 1. Otherwise return 0. */ #include "libbb.h" int FAST_FUNC bb_ask_confirmation(void) { char first = 0; int c; while (((c = getchar()) != EOF) && (c != '\n')) { if (first == 0 && !isblank(c)) { first = c|0x20; } } return first == 'y'; } busybox-1.22.1/libbb/concat_path_file.c0000644000000000000000000000126712263563520016500 0ustar rootroot/* 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 source tree. */ /* Concatenate path and filename to new allocated buffer. * Add '/' only as needed (no duplicate // are produced). * If path is NULL, it is assumed to be "/". * filename should not be NULL. */ #include "libbb.h" char* FAST_FUNC concat_path_file(const char *path, const char *filename) { char *lc; if (!path) path = ""; lc = last_char_is(path, '/'); while (*filename == '/') filename++; return xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename); } busybox-1.22.1/libbb/crc32.c0000644000000000000000000000323212263563520014124 0ustar rootroot/* 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 * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" uint32_t *global_crc32_table; uint32_t* FAST_FUNC crc32_filltable(uint32_t *crc_table, int endian) { uint32_t polynomial = endian ? 0x04c11db7 : 0xedb88320; uint32_t c; int i, j; if (!crc_table) crc_table = xmalloc(256 * sizeof(uint32_t)); for (i = 0; i < 256; i++) { c = endian ? (i << 24) : i; for (j = 8; j; j--) { if (endian) c = (c&0x80000000) ? ((c << 1) ^ polynomial) : (c << 1); else c = (c&1) ? ((c >> 1) ^ polynomial) : (c >> 1); } *crc_table++ = c; } return crc_table - 256; } uint32_t FAST_FUNC crc32_block_endian1(uint32_t val, const void *buf, unsigned len, uint32_t *crc_table) { const void *end = (uint8_t*)buf + len; while (buf != end) { val = (val << 8) ^ crc_table[(val >> 24) ^ *(uint8_t*)buf]; buf = (uint8_t*)buf + 1; } return val; } uint32_t FAST_FUNC crc32_block_endian0(uint32_t val, const void *buf, unsigned len, uint32_t *crc_table) { const void *end = (uint8_t*)buf + len; while (buf != end) { val = crc_table[(uint8_t)val ^ *(uint8_t*)buf] ^ (val >> 8); buf = (uint8_t*)buf + 1; } return val; } busybox-1.22.1/libbb/messages.c0000644000000000000000000000445312263563520015025 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright (C) 1999-2004 by Erik Andersen * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" /* allow default system PATH to be extended via CFLAGS */ #ifndef BB_ADDITIONAL_PATH #define BB_ADDITIONAL_PATH "" #endif /* allow version to be extended, via CFLAGS */ #ifndef BB_EXTRA_VERSION #define BB_EXTRA_VERSION BB_BT #endif #define BANNER "BusyBox v" BB_VER " (" BB_EXTRA_VERSION ")" const char bb_banner[] ALIGN1 = BANNER; const char bb_msg_memory_exhausted[] ALIGN1 = "out of memory"; const char bb_msg_invalid_date[] ALIGN1 = "invalid date '%s'"; const char bb_msg_unknown[] ALIGN1 = "(unknown)"; const char bb_msg_can_not_create_raw_socket[] ALIGN1 = "can't create raw socket"; const char bb_msg_perm_denied_are_you_root[] ALIGN1 = "permission denied (are you root?)"; const char bb_msg_you_must_be_root[] ALIGN1 = "you must be root"; const char bb_msg_requires_arg[] ALIGN1 = "%s requires an argument"; const char bb_msg_invalid_arg[] ALIGN1 = "invalid argument '%s' to '%s'"; const char bb_msg_standard_input[] ALIGN1 = "standard input"; const char bb_msg_standard_output[] ALIGN1 = "standard output"; const char bb_hexdigits_upcase[] ALIGN1 = "0123456789ABCDEF"; const char bb_busybox_exec_path[] ALIGN1 = CONFIG_BUSYBOX_EXEC_PATH; const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL; /* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin, * but I want to save a few bytes here. Check libbb.h before changing! */ const char bb_PATH_root_path[] ALIGN1 = "PATH=/sbin:/usr/sbin:/bin:/usr/bin" BB_ADDITIONAL_PATH; const int const_int_1 = 1; /* explicitly = 0, otherwise gcc may make it a common variable * and it will end up in bss */ const int const_int_0 = 0; #if ENABLE_FEATURE_WTMP /* This is usually something like "/var/adm/wtmp" or "/var/log/wtmp" */ const char bb_path_wtmp_file[] ALIGN1 = # if defined _PATH_WTMP _PATH_WTMP; # elif defined WTMP_FILE WTMP_FILE; # else # error unknown path to wtmp file # endif #endif /* We use it for "global" data via *(struct global*)&bb_common_bufsiz1. * Since gcc insists on aligning struct global's members, it would be a pity * (and an alignment fault on some CPUs) to mess it up. */ char bb_common_bufsiz1[COMMON_BUFSIZE] ALIGNED(sizeof(long long)); busybox-1.22.1/libbb/appletlib.c0000644000000000000000000005356112263563520015176 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) tons of folks. Tracking down who wrote what * isn't something I'm going to worry about... If you wrote something * here, please feel free to acknowledge your work. * * Based in part on code from sash, Copyright (c) 1999 by David I. Bell * Permission has been granted to redistribute this code under GPL. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* We are trying to not use printf, this benefits the case when selected * applets are really simple. Example: * * $ ./busybox * ... * Currently defined functions: * basename, false, true * * $ size busybox * text data bss dec hex filename * 4473 52 72 4597 11f5 busybox * * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :( */ #include "busybox.h" #if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ || defined(__APPLE__) \ ) # include /* for mallopt */ #endif /* Declare _main() */ #define PROTOTYPES #include "applets.h" #undef PROTOTYPES /* Include generated applet names, pointers to _main, etc */ #include "applet_tables.h" /* ...and if applet_tables generator says we have only one applet... */ #ifdef SINGLE_APPLET_MAIN # undef ENABLE_FEATURE_INDIVIDUAL # define ENABLE_FEATURE_INDIVIDUAL 1 # undef IF_FEATURE_INDIVIDUAL # define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__ #endif #include "usage_compressed.h" #if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE static const char usage_messages[] ALIGN1 = UNPACKED_USAGE; #else # define usage_messages 0 #endif #if ENABLE_FEATURE_COMPRESS_USAGE static const char packed_usage[] ALIGN1 = { PACKED_USAGE }; # include "bb_archive.h" static const char *unpack_usage_messages(void) { char *outbuf = NULL; bunzip_data *bd; int i; i = start_bunzip(&bd, /* src_fd: */ -1, /* inbuf: */ packed_usage, /* len: */ sizeof(packed_usage)); /* read_bunzip can longjmp to start_bunzip, and ultimately * end up here with i != 0 on read data errors! Not trivial */ if (!i) { /* Cannot use xmalloc: will leak bd in NOFORK case! */ outbuf = malloc_or_warn(sizeof(UNPACKED_USAGE)); if (outbuf) read_bunzip(bd, outbuf, sizeof(UNPACKED_USAGE)); } dealloc_bunzip(bd); return outbuf; } # define dealloc_usage_messages(s) free(s) #else # define unpack_usage_messages() usage_messages # define dealloc_usage_messages(s) ((void)(s)) #endif /* FEATURE_COMPRESS_USAGE */ void FAST_FUNC bb_show_usage(void) { if (ENABLE_SHOW_USAGE) { #ifdef SINGLE_APPLET_STR /* Imagine that this applet is "true". Dont suck in printf! */ const char *usage_string = unpack_usage_messages(); if (*usage_string == '\b') { full_write2_str("No help available.\n\n"); } else { full_write2_str("Usage: "SINGLE_APPLET_STR" "); full_write2_str(usage_string); full_write2_str("\n\n"); } if (ENABLE_FEATURE_CLEAN_UP) dealloc_usage_messages((char*)usage_string); #else const char *p; const char *usage_string = p = unpack_usage_messages(); int ap = find_applet_by_name(applet_name); if (ap < 0) /* never happens, paranoia */ xfunc_die(); while (ap) { while (*p++) continue; ap--; } full_write2_str(bb_banner); full_write2_str(" multi-call binary.\n"); if (*p == '\b') full_write2_str("\nNo help available.\n\n"); else { full_write2_str("\nUsage: "); full_write2_str(applet_name); full_write2_str(" "); full_write2_str(p); full_write2_str("\n\n"); } if (ENABLE_FEATURE_CLEAN_UP) dealloc_usage_messages((char*)usage_string); #endif } xfunc_die(); } #if NUM_APPLETS > 8 static int applet_name_compare(const void *name, const void *idx) { int i = (int)(ptrdiff_t)idx - 1; return strcmp(name, APPLET_NAME(i)); } #endif int FAST_FUNC find_applet_by_name(const char *name) { #if NUM_APPLETS > 8 /* Do a binary search to find the applet entry given the name. */ const char *p; p = bsearch(name, (void*)(ptrdiff_t)1, ARRAY_SIZE(applet_main), 1, applet_name_compare); /* * if (!p) return -1; * ^^^^^^^^^^^^^^^^^^ the code below will do this if p == NULL :) */ return (int)(ptrdiff_t)p - 1; #else /* A version which does not pull in bsearch */ int i = 0; const char *p = applet_names; while (i < NUM_APPLETS) { if (strcmp(name, p) == 0) return i; p += strlen(p) + 1; i++; } return -1; #endif } void lbb_prepare(const char *applet IF_FEATURE_INDIVIDUAL(, char **argv)) MAIN_EXTERNALLY_VISIBLE; void lbb_prepare(const char *applet IF_FEATURE_INDIVIDUAL(, char **argv)) { #ifdef __GLIBC__ (*(int **)&bb_errno) = __errno_location(); barrier(); #endif applet_name = applet; /* Set locale for everybody except 'init' */ if (ENABLE_LOCALE_SUPPORT && getpid() != 1) setlocale(LC_ALL, ""); #if ENABLE_FEATURE_INDIVIDUAL /* Redundant for busybox (run_applet_and_exit covers that case) * but needed for "individual applet" mode */ if (argv[1] && !argv[2] && strcmp(argv[1], "--help") == 0 && strncmp(applet, "busybox", 7) != 0 ) { /* Special case. POSIX says "test --help" * should be no different from e.g. "test --foo". */ if (!ENABLE_TEST || strcmp(applet_name, "test") != 0) bb_show_usage(); } #endif } /* The code below can well be in applets/applets.c, as it is used only * for busybox binary, not "individual" binaries. * However, keeping it here and linking it into libbusybox.so * (together with remaining tiny applets/applets.o) * makes it possible to avoid --whole-archive at link time. * This makes (shared busybox) + libbusybox smaller. * (--gc-sections would be even better....) */ const char *applet_name; #if !BB_MMU bool re_execed; #endif /* If not built as a single-applet executable... */ #if !defined(SINGLE_APPLET_MAIN) IF_FEATURE_SUID(static uid_t ruid;) /* real uid */ # if ENABLE_FEATURE_SUID_CONFIG static struct suid_config_t { /* next ptr must be first: this struct needs to be llist-compatible */ struct suid_config_t *m_next; struct bb_uidgid_t m_ugid; int m_applet; mode_t m_mode; } *suid_config; static bool suid_cfg_readable; /* check if u is member of group g */ static int ingroup(uid_t u, gid_t g) { struct group *grp = getgrgid(g); if (grp) { char **mem; for (mem = grp->gr_mem; *mem; mem++) { struct passwd *pwd = getpwnam(*mem); if (pwd && (pwd->pw_uid == u)) return 1; } } return 0; } /* libbb candidate */ static char *get_trimmed_slice(char *s, char *e) { /* First, consider the value at e to be nul and back up until we * reach a non-space char. Set the char after that (possibly at * the original e) to nul. */ while (e-- > s) { if (!isspace(*e)) { break; } } e[1] = '\0'; /* Next, advance past all leading space and return a ptr to the * first non-space char; possibly the terminating nul. */ return skip_whitespace(s); } static void parse_config_file(void) { /* Don't depend on the tools to combine strings. */ static const char config_file[] ALIGN1 = "/etc/busybox.conf"; struct suid_config_t *sct_head; int applet_no; FILE *f; const char *errmsg; unsigned lc; smallint section; struct stat st; ruid = getuid(); if (ruid == 0) /* run by root - don't need to even read config file */ return; if ((stat(config_file, &st) != 0) /* No config file? */ || !S_ISREG(st.st_mode) /* Not a regular file? */ || (st.st_uid != 0) /* Not owned by root? */ || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */ || !(f = fopen_for_read(config_file)) /* Cannot open? */ ) { return; } suid_cfg_readable = 1; sct_head = NULL; section = lc = 0; while (1) { char buffer[256]; char *s; if (!fgets(buffer, sizeof(buffer), f)) { /* Are we done? */ // Looks like bloat //if (ferror(f)) { /* Make sure it wasn't a read error. */ // errmsg = "reading"; // goto pe_label; //} fclose(f); suid_config = sct_head; /* Success, so set the pointer. */ return; } s = buffer; lc++; /* Got a (partial) line. */ /* If a line is too long for our buffer, we consider it an error. * The following test does mistreat one corner case though. * If the final line of the file does not end with a newline and * yet exactly fills the buffer, it will be treated as too long * even though there isn't really a problem. But it isn't really * worth adding code to deal with such an unlikely situation, and * we do err on the side of caution. Besides, the line would be * too long if it did end with a newline. */ if (!strchr(s, '\n') && !feof(f)) { errmsg = "line too long"; goto pe_label; } /* Trim leading and trailing whitespace, ignoring comments, and * check if the resulting string is empty. */ s = get_trimmed_slice(s, strchrnul(s, '#')); if (!*s) { continue; } /* Check for a section header. */ if (*s == '[') { /* Unlike the old code, we ignore leading and trailing * whitespace for the section name. We also require that * there are no stray characters after the closing bracket. */ char *e = strchr(s, ']'); if (!e /* Missing right bracket? */ || e[1] /* Trailing characters? */ || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */ ) { errmsg = "section header"; goto pe_label; } /* Right now we only have one section so just check it. * If more sections are added in the future, please don't * resort to cascading ifs with multiple strcasecmp calls. * That kind of bloated code is all too common. A loop * and a string table would be a better choice unless the * number of sections is very small. */ if (strcasecmp(s, "SUID") == 0) { section = 1; continue; } section = -1; /* Unknown section so set to skip. */ continue; } /* Process sections. */ if (section == 1) { /* SUID */ /* Since we trimmed leading and trailing space above, we're * now looking for strings of the form * [::space::]*=[::space::]* * where both key and value could contain inner whitespace. */ /* First get the key (an applet name in our case). */ char *e = strchr(s, '='); if (e) { s = get_trimmed_slice(s, e); } if (!e || !*s) { /* Missing '=' or empty key. */ errmsg = "keyword"; goto pe_label; } /* Ok, we have an applet name. Process the rhs if this * applet is currently built in and ignore it otherwise. * Note: this can hide config file bugs which only pop * up when the busybox configuration is changed. */ applet_no = find_applet_by_name(s); if (applet_no >= 0) { unsigned i; struct suid_config_t *sct; /* Note: We currently don't check for duplicates! * The last config line for each applet will be the * one used since we insert at the head of the list. * I suppose this could be considered a feature. */ sct = xzalloc(sizeof(*sct)); sct->m_applet = applet_no; /*sct->m_mode = 0;*/ sct->m_next = sct_head; sct_head = sct; /* Get the specified mode. */ e = skip_whitespace(e+1); for (i = 0; i < 3; i++) { /* There are 4 chars for each of user/group/other. * "x-xx" instead of "x-" are to make * "idx > 3" check catch invalid chars. */ static const char mode_chars[] ALIGN1 = "Ssx-" "Ssx-" "x-xx"; static const unsigned short mode_mask[] ALIGN2 = { S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* Ssx- */ S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* Ssx- */ S_IXOTH, 0 /* x- */ }; const char *q = strchrnul(mode_chars + 4*i, *e); unsigned idx = q - (mode_chars + 4*i); if (idx > 3) { errmsg = "mode"; goto pe_label; } sct->m_mode |= mode_mask[q - mode_chars]; e++; } /* Now get the user/group info. */ s = skip_whitespace(e); /* Default is 0.0, else parse USER.GROUP: */ if (*s) { /* We require whitespace between mode and USER.GROUP */ if ((s == e) || !(e = strchr(s, '.'))) { errmsg = "uid.gid"; goto pe_label; } *e = ':'; /* get_uidgid needs USER:GROUP syntax */ if (get_uidgid(&sct->m_ugid, s, /*allow_numeric:*/ 1) == 0) { errmsg = "unknown user/group"; goto pe_label; } } } continue; } /* Unknown sections are ignored. */ /* Encountering configuration lines prior to seeing a * section header is treated as an error. This is how * the old code worked, but it may not be desirable. * We may want to simply ignore such lines in case they * are used in some future version of busybox. */ if (!section) { errmsg = "keyword outside section"; goto pe_label; } } /* while (1) */ pe_label: fclose(f); bb_error_msg("parse error in %s, line %u: %s", config_file, lc, errmsg); /* Release any allocated memory before returning. */ llist_free((llist_t*)sct_head, NULL); } # else static inline void parse_config_file(void) { IF_FEATURE_SUID(ruid = getuid();) } # endif /* FEATURE_SUID_CONFIG */ # if ENABLE_FEATURE_SUID static void check_suid(int applet_no) { gid_t rgid; /* real gid */ if (ruid == 0) /* set by parse_config_file() */ return; /* run by root - no need to check more */ rgid = getgid(); # if ENABLE_FEATURE_SUID_CONFIG if (suid_cfg_readable) { uid_t uid; struct suid_config_t *sct; mode_t m; for (sct = suid_config; sct; sct = sct->m_next) { if (sct->m_applet == applet_no) goto found; } goto check_need_suid; found: /* Is this user allowed to run this applet? */ m = sct->m_mode; if (sct->m_ugid.uid == ruid) /* same uid */ m >>= 6; else if ((sct->m_ugid.gid == rgid) || ingroup(ruid, sct->m_ugid.gid)) /* same group / in group */ m >>= 3; if (!(m & S_IXOTH)) /* is x bit not set? */ bb_error_msg_and_die("you have no permission to run this applet"); /* We set effective AND saved ids. If saved-id is not set * like we do below, seteuid(0) can still later succeed! */ /* Are we directed to change gid * (APPLET = *s* USER.GROUP or APPLET = *S* USER.GROUP)? */ if (sct->m_mode & S_ISGID) rgid = sct->m_ugid.gid; /* else: we will set egid = rgid, thus dropping sgid effect */ if (setresgid(-1, rgid, rgid)) bb_perror_msg_and_die("setresgid"); /* Are we directed to change uid * (APPLET = s** USER.GROUP or APPLET = S** USER.GROUP)? */ uid = ruid; if (sct->m_mode & S_ISUID) uid = sct->m_ugid.uid; /* else: we will set euid = ruid, thus dropping suid effect */ if (setresuid(-1, uid, uid)) bb_perror_msg_and_die("setresuid"); goto ret; } # if !ENABLE_FEATURE_SUID_CONFIG_QUIET { static bool onetime = 0; if (!onetime) { onetime = 1; bb_error_msg("using fallback suid method"); } } # endif check_need_suid: # endif if (APPLET_SUID(applet_no) == BB_SUID_REQUIRE) { /* Real uid is not 0. If euid isn't 0 too, suid bit * is most probably not set on our executable */ if (geteuid()) bb_error_msg_and_die("must be suid to work properly"); } else if (APPLET_SUID(applet_no) == BB_SUID_DROP) { xsetgid(rgid); /* drop all privileges */ xsetuid(ruid); } # if ENABLE_FEATURE_SUID_CONFIG ret: ; llist_free((llist_t*)suid_config, NULL); # endif } # else # define check_suid(x) ((void)0) # endif /* FEATURE_SUID */ # if ENABLE_FEATURE_INSTALLER static const char usr_bin [] ALIGN1 = "/usr/bin/"; static const char usr_sbin[] ALIGN1 = "/usr/sbin/"; static const char *const install_dir[] = { &usr_bin [8], /* "/" */ &usr_bin [4], /* "/bin/" */ &usr_sbin[4] /* "/sbin/" */ # if !ENABLE_INSTALL_NO_USR ,usr_bin ,usr_sbin # endif }; /* create (sym)links for each applet */ static void install_links(const char *busybox, int use_symbolic_links, char *custom_install_dir) { /* directory table * this should be consistent w/ the enum, * busybox.h::bb_install_loc_t, or else... */ int (*lf)(const char *, const char *); char *fpc; unsigned i; int rc; lf = link; if (use_symbolic_links) lf = symlink; for (i = 0; i < ARRAY_SIZE(applet_main); i++) { fpc = concat_path_file( custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)], APPLET_NAME(i)); // debug: bb_error_msg("%slinking %s to busybox", // use_symbolic_links ? "sym" : "", fpc); rc = lf(busybox, fpc); if (rc != 0 && errno != EEXIST) { bb_simple_perror_msg(fpc); } free(fpc); } } # else # define install_links(x,y,z) ((void)0) # endif /* If we were called as "busybox..." */ static int busybox_main(char **argv) { if (!argv[1]) { /* Called without arguments */ const char *a; int col; unsigned output_width; help: output_width = 80; if (ENABLE_FEATURE_AUTOWIDTH) { /* Obtain the terminal width */ get_terminal_width_height(0, &output_width, NULL); } dup2(1, 2); full_write2_str(bb_banner); /* reuse const string */ full_write2_str(" multi-call binary.\n"); /* reuse */ full_write2_str( "BusyBox is copyrighted by many authors between 1998-2012.\n" "Licensed under GPLv2. See source distribution for detailed\n" "copyright notices.\n" "\n" "Usage: busybox [function [arguments]...]\n" " or: busybox --list"IF_FEATURE_INSTALLER("[-full]")"\n" IF_FEATURE_INSTALLER( " or: busybox --install [-s] [DIR]\n" ) " or: function [arguments]...\n" "\n" "\tBusyBox is a multi-call binary that combines many common Unix\n" "\tutilities into a single executable. Most people will create a\n" "\tlink to busybox for each function they wish to use and BusyBox\n" "\twill act like whatever it was invoked as.\n" "\n" "Currently defined functions:\n" ); col = 0; a = applet_names; /* prevent last comma to be in the very last pos */ output_width--; while (*a) { int len2 = strlen(a) + 2; if (col >= (int)output_width - len2) { full_write2_str(",\n"); col = 0; } if (col == 0) { col = 6; full_write2_str("\t"); } else { full_write2_str(", "); } full_write2_str(a); col += len2; a += len2 - 1; } full_write2_str("\n\n"); return 0; } if (strncmp(argv[1], "--list", 6) == 0) { unsigned i = 0; const char *a = applet_names; dup2(1, 2); while (*a) { # if ENABLE_FEATURE_INSTALLER if (argv[1][6]) /* --list-full? */ full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1); # endif full_write2_str(a); full_write2_str("\n"); i++; a += strlen(a) + 1; } return 0; } if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) { int use_symbolic_links; const char *busybox; busybox = xmalloc_readlink(bb_busybox_exec_path); if (!busybox) { /* bb_busybox_exec_path is usually "/proc/self/exe". * In chroot, readlink("/proc/self/exe") usually fails. * In such case, better use argv[0] as symlink target * if it is a full path name. */ if (argv[0][0] != '/') bb_error_msg_and_die("'%s' is not an absolute path", argv[0]); busybox = argv[0]; } /* busybox --install [-s] [DIR]: * -s: make symlinks * DIR: directory to install links to */ use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && ++argv); install_links(busybox, use_symbolic_links, argv[2]); return 0; } if (strcmp(argv[1], "--help") == 0) { /* "busybox --help []" */ if (!argv[2]) goto help; /* convert to " --help" */ argv[0] = argv[2]; argv[2] = NULL; } else { /* "busybox arg1 arg2 ..." */ argv++; } /* We support "busybox /a/path/to/applet args..." too. Allows for * "#!/bin/busybox"-style wrappers */ applet_name = bb_get_last_path_component_nostrip(argv[0]); run_applet_and_exit(applet_name, argv); /*bb_error_msg_and_die("applet not found"); - sucks in printf */ full_write2_str(applet_name); full_write2_str(": applet not found\n"); xfunc_die(); } void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) { int argc = 1; while (argv[argc]) argc++; /* Reinit some shared global data */ xfunc_error_retval = EXIT_FAILURE; applet_name = APPLET_NAME(applet_no); if (argc == 2 && strcmp(argv[1], "--help") == 0) { /* Special case. POSIX says "test --help" * should be no different from e.g. "test --foo". */ //TODO: just compare applet_no with APPLET_NO_test if (!ENABLE_TEST || strcmp(applet_name, "test") != 0) { /* If you want "foo --help" to return 0: */ xfunc_error_retval = 0; bb_show_usage(); } } if (ENABLE_FEATURE_SUID) check_suid(applet_no); exit(applet_main[applet_no](argc, argv)); } void FAST_FUNC run_applet_and_exit(const char *name, char **argv) { int applet = find_applet_by_name(name); if (applet >= 0) run_applet_no_and_exit(applet, argv); if (strncmp(name, "busybox", 7) == 0) exit(busybox_main(argv)); } #endif /* !defined(SINGLE_APPLET_MAIN) */ #if ENABLE_BUILD_LIBBUSYBOX int lbb_main(char **argv) #else int main(int argc UNUSED_PARAM, char **argv) #endif { /* Tweak malloc for reduced memory consumption */ #ifdef M_TRIM_THRESHOLD /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory * to keep before releasing to the OS * Default is way too big: 256k */ mallopt(M_TRIM_THRESHOLD, 8 * 1024); #endif #ifdef M_MMAP_THRESHOLD /* M_MMAP_THRESHOLD is the request size threshold for using mmap() * Default is too big: 256k */ mallopt(M_MMAP_THRESHOLD, 32 * 1024 - 256); #endif #if !BB_MMU /* NOMMU re-exec trick sets high-order bit in first byte of name */ if (argv[0][0] & 0x80) { re_execed = 1; argv[0][0] &= 0x7f; } #endif #if defined(SINGLE_APPLET_MAIN) /* Only one applet is selected in .config */ if (argv[1] && strncmp(argv[0], "busybox", 7) == 0) { /* "busybox " should still work as expected */ argv++; } /* applet_names in this case is just "applet\0\0" */ lbb_prepare(applet_names IF_FEATURE_INDIVIDUAL(, argv)); return SINGLE_APPLET_MAIN(argc, argv); #else lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv)); applet_name = argv[0]; if (applet_name[0] == '-') applet_name++; applet_name = bb_basename(applet_name); parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */ run_applet_and_exit(applet_name, argv); /*bb_error_msg_and_die("applet not found"); - sucks in printf */ full_write2_str(applet_name); full_write2_str(": applet not found\n"); xfunc_die(); #endif } busybox-1.22.1/libbb/README0000644000000000000000000000075612263563520013734 0ustar rootrootPlease 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 busybox-1.22.1/libbb/verror_msg.c0000644000000000000000000000757612263563520015414 0ustar rootroot/* 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 source tree. */ #include "libbb.h" #if ENABLE_FEATURE_SYSLOG # include #endif smallint logmode = LOGMODE_STDIO; const char *msg_eol = "\n"; void FAST_FUNC bb_verror_msg(const char *s, va_list p, const char* strerr) { char *msg, *msg1; int applet_len, strerr_len, msgeol_len, used; if (!logmode) return; if (!s) /* nomsg[_and_die] uses NULL fmt */ s = ""; /* some libc don't like printf(NULL) */ used = vasprintf(&msg, s, p); if (used < 0) return; /* This is ugly and costs +60 bytes compared to multiple * fprintf's, but is guaranteed to do a single write. * This is needed for e.g. httpd logging, when multiple * children can produce log messages simultaneously. */ applet_len = strlen(applet_name) + 2; /* "applet: " */ strerr_len = strerr ? strlen(strerr) : 0; msgeol_len = strlen(msg_eol); /* can't use xrealloc: it calls error_msg on failure, * that may result in a recursion */ /* +3 is for ": " before strerr and for terminating NUL */ msg1 = realloc(msg, applet_len + used + strerr_len + msgeol_len + 3); if (!msg1) { msg[used++] = '\n'; /* overwrites NUL */ applet_len = 0; } else { msg = msg1; /* TODO: maybe use writev instead of memmoving? Need full_writev? */ memmove(msg + applet_len, msg, used); used += applet_len; strcpy(msg, applet_name); msg[applet_len - 2] = ':'; msg[applet_len - 1] = ' '; if (strerr) { if (s[0]) { /* not perror_nomsg? */ msg[used++] = ':'; msg[used++] = ' '; } strcpy(&msg[used], strerr); used += strerr_len; } strcpy(&msg[used], msg_eol); used += msgeol_len; } if (logmode & LOGMODE_STDIO) { fflush_all(); full_write(STDERR_FILENO, msg, used); } #if ENABLE_FEATURE_SYSLOG if (logmode & LOGMODE_SYSLOG) { syslog(LOG_ERR, "%s", msg + applet_len); } #endif free(msg); } #ifdef VERSION_WITH_WRITEV /* Code size is approximately the same, but currently it's the only user * of writev in entire bbox. __libc_writev in uclibc is ~50 bytes. */ void FAST_FUNC bb_verror_msg(const char *s, va_list p, const char* strerr) { int strerr_len, msgeol_len; struct iovec iov[3]; #define used (iov[2].iov_len) #define msgv (iov[2].iov_base) #define msgc ((char*)(iov[2].iov_base)) #define msgptr (&(iov[2].iov_base)) if (!logmode) return; if (!s) /* nomsg[_and_die] uses NULL fmt */ s = ""; /* some libc don't like printf(NULL) */ /* Prevent "derefing type-punned ptr will break aliasing rules" */ used = vasprintf((char**)(void*)msgptr, s, p); if (used < 0) return; /* This is ugly and costs +60 bytes compared to multiple * fprintf's, but is guaranteed to do a single write. * This is needed for e.g. httpd logging, when multiple * children can produce log messages simultaneously. */ strerr_len = strerr ? strlen(strerr) : 0; msgeol_len = strlen(msg_eol); /* +3 is for ": " before strerr and for terminating NUL */ msgv = xrealloc(msgv, used + strerr_len + msgeol_len + 3); if (strerr) { msgc[used++] = ':'; msgc[used++] = ' '; strcpy(msgc + used, strerr); used += strerr_len; } strcpy(msgc + used, msg_eol); used += msgeol_len; if (logmode & LOGMODE_STDIO) { iov[0].iov_base = (char*)applet_name; iov[0].iov_len = strlen(applet_name); iov[1].iov_base = (char*)": "; iov[1].iov_len = 2; /*iov[2].iov_base = msgc;*/ /*iov[2].iov_len = used;*/ fflush_all(); writev(STDERR_FILENO, iov, 3); } # if ENABLE_FEATURE_SYSLOG if (logmode & LOGMODE_SYSLOG) { syslog(LOG_ERR, "%s", msgc); } # endif free(msgc); } #endif void FAST_FUNC bb_error_msg_and_die(const char *s, ...) { va_list p; va_start(p, s); bb_verror_msg(s, p, NULL); va_end(p); xfunc_die(); } void FAST_FUNC bb_error_msg(const char *s, ...) { va_list p; va_start(p, s); bb_verror_msg(s, p, NULL); va_end(p); } busybox-1.22.1/libbb/trim.c0000644000000000000000000000104512263563520014163 0ustar rootroot/* 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 source tree. */ #include "libbb.h" void FAST_FUNC trim(char *s) { size_t len = strlen(s); /* trim trailing whitespace */ while (len && isspace(s[len-1])) --len; /* trim leading whitespace */ if (len) { char *nws = skip_whitespace(s); if ((nws - s) != 0) { len -= (nws - s); memmove(s, nws, len); } } s[len] = '\0'; } busybox-1.22.1/libbb/device_open.c0000644000000000000000000000120112263563520015462 0ustar rootroot/* 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 source tree. */ #include "libbb.h" /* try to open up the specified device */ int FAST_FUNC device_open(const char *device, int mode) { int m, f, fd; m = mode | O_NONBLOCK; /* Retry up to 5 times */ /* TODO: explain why it can't be considered insane */ for (f = 0; f < 5; f++) { fd = open(device, m, 0600); if (fd >= 0) break; } if (fd < 0) return fd; /* Reset original flags. */ if (m != mode) fcntl(fd, F_SETFL, mode); return fd; } busybox-1.22.1/libbb/find_pid_by_name.c0000644000000000000000000000635612263563520016470 0ustar rootroot/* 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 source tree. */ #include "libbb.h" /* In Linux we have three ways to determine "process name": 1. /proc/PID/stat has "...(name)...", among other things. It's so-called "comm" field. 2. /proc/PID/cmdline's first NUL-terminated string. It's argv[0] from exec syscall. 3. /proc/PID/exe symlink. Points to the running executable file. kernel threads: comm: thread name cmdline: empty exe: executable comm: first 15 chars of base name (if executable is a symlink, then first 15 chars of symlink name are used) cmdline: argv[0] from exec syscall exe: points to executable (resolves symlink, unlike comm) script (an executable with #!/path/to/interpreter): comm: first 15 chars of script's base name (symlinks are not resolved) cmdline: /path/to/interpreter (symlinks are not resolved) (script name is in argv[1], args are pushed into argv[2] etc) exe: points to interpreter's executable (symlinks are resolved) If FEATURE_PREFER_APPLETS=y (and more so if FEATURE_SH_STANDALONE=y), some commands started from busybox shell, xargs or find are started by execXXX("/proc/self/exe", applet_name, params....) and therefore comm field contains "exe". */ static int comm_match(procps_status_t *p, const char *procName) { int argv1idx; const char *argv1; if (strncmp(p->comm, procName, 15) != 0) return 0; /* comm does not match */ /* In Linux, if comm is 15 chars, it is truncated. * (or maybe the name was exactly 15 chars, but there is * no way to know that) */ if (p->comm[14] == '\0') return 1; /* comm is not truncated - matches */ /* comm is truncated, but first 15 chars match. * This can be crazily_long_script_name.sh! * The telltale sign is basename(argv[1]) == procName */ if (!p->argv0) return 0; argv1idx = strlen(p->argv0) + 1; if (argv1idx >= p->argv_len) return 0; argv1 = p->argv0 + argv1idx; if (strcmp(bb_basename(argv1), procName) != 0) return 0; return 1; } /* 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. * * Modified by Vladimir Oleynik for use with libbb/procps.c */ pid_t* FAST_FUNC find_pid_by_name(const char *procName) { pid_t* pidList; int i = 0; procps_status_t* p = NULL; pidList = xzalloc(sizeof(*pidList)); while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_COMM|PSSCAN_ARGVN|PSSCAN_EXE))) { if (comm_match(p, procName) /* or we require argv0 to match (essential for matching reexeced /proc/self/exe)*/ || (p->argv0 && strcmp(bb_basename(p->argv0), procName) == 0) /* or we require /proc/PID/exe link to match */ || (p->exe && strcmp(bb_basename(p->exe), procName) == 0) ) { pidList = xrealloc_vector(pidList, 2, i); pidList[i++] = p->pid; } } pidList[i] = 0; return pidList; } pid_t* FAST_FUNC pidlist_reverse(pid_t *pidList) { int i = 0; while (pidList[i]) i++; if (--i >= 0) { pid_t k; int j; for (j = 0; i > j; i--, j++) { k = pidList[i]; pidList[i] = pidList[j]; pidList[j] = k; } } return pidList; } busybox-1.22.1/libbb/chomp.c0000644000000000000000000000052112263563520014314 0ustar rootroot/* 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 source tree. */ #include "libbb.h" void FAST_FUNC chomp(char *s) { char *lc = last_char_is(s, '\n'); if (lc) *lc = '\0'; } busybox-1.22.1/libbb/printable.c0000644000000000000000000000175012263563520015173 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 2007 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" void FAST_FUNC fputc_printable(int ch, FILE *file) { if ((ch & (0x80 + PRINTABLE_META)) == (0x80 + PRINTABLE_META)) { fputs("M-", file); ch &= 0x7f; } ch = (unsigned char) ch; if (ch == 0x9b) { /* VT100's CSI, aka Meta-ESC, is not printable on vt-100 */ ch = '{'; goto print_caret; } if (ch < ' ') { ch += '@'; goto print_caret; } if (ch == 0x7f) { ch = '?'; print_caret: fputc('^', file); } fputc(ch, file); } void FAST_FUNC visible(unsigned ch, char *buf, int flags) { if (ch == '\t' && !(flags & VISIBLE_SHOW_TABS)) { goto raw; } if (ch == '\n') { if (flags & VISIBLE_ENDLINE) *buf++ = '$'; } else { if (ch >= 128) { ch -= 128; *buf++ = 'M'; *buf++ = '-'; } if (ch < 32 || ch == 127) { *buf++ = '^'; ch ^= 0x40; } } raw: *buf++ = ch; *buf = '\0'; } busybox-1.22.1/libbb/die_if_bad_username.c0000644000000000000000000000330212263563520017132 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Check user and group names for illegal characters * * Copyright (C) 2008 Tito Ragusa * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" /* To avoid problems, the username should consist only of * letters, digits, underscores, periods, at signs and dashes, * and not start with a dash (as defined by IEEE Std 1003.1-2001). * For compatibility with Samba machine accounts $ is also supported * at the end of the username. */ void FAST_FUNC die_if_bad_username(const char *name) { const char *start = name; /* 1st char being dash or dot isn't valid: * for example, name like ".." can make adduser * chown "/home/.." recursively - NOT GOOD. * Name of just a single "$" is also rejected. */ goto skip; do { unsigned char ch; /* These chars are valid unless they are at the 1st pos: */ if (*name == '-' || *name == '.' /* $ is allowed if it's the last char: */ || (*name == '$' && !name[1]) ) { continue; } skip: ch = *name; if (ch == '_' /* || ch == '@' -- we disallow this too. Think about "user@host" */ /* open-coded isalnum: */ || (ch >= '0' && ch <= '9') || ((ch|0x20) >= 'a' && (ch|0x20) <= 'z') ) { continue; } bb_error_msg_and_die("illegal character with code %u at position %u", (unsigned)ch, (unsigned)(name - start)); } while (*++name); /* The minimum size of the login name is one char or two if * last char is the '$'. Violations of this are caught above. * The maximum size of the login name is LOGIN_NAME_MAX * including the terminating null byte. */ if (name - start >= LOGIN_NAME_MAX) bb_error_msg_and_die("name is too long"); } busybox-1.22.1/libbb/Config.src0000644000000000000000000001561412263563520014771 0ustar rootroot# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Busybox Library Tuning" INSERT config PASSWORD_MINLEN int "Minimum password length" default 6 range 5 32 help Minimum allowable password length. config MD5_SMALL int "MD5: Trade bytes for speed (0:fast, 3:slow)" default 1 range 0 3 help Trade binary size versus speed for the md5sum algorithm. Approximate values running uClibc and hashing linux-2.4.4.tar.bz2 were: user times (sec) text size (386) 0 (fastest) 1.1 6144 1 1.4 5392 2 3.0 5088 3 (smallest) 5.1 4912 config SHA3_SMALL int "SHA3: Trade bytes for speed (0:fast, 1:slow)" default 1 range 0 1 help Trade binary size versus speed for the sha3sum algorithm. SHA3_SMALL=0 compared to SHA3_SMALL=1 (approximate): 64-bit x86: +270 bytes of code, 45% faster 32-bit x86: +450 bytes of code, 75% faster config FEATURE_FAST_TOP bool "Faster /proc scanning code (+100 bytes)" default y help This option makes top (and ps) ~20% faster (or 20% less CPU hungry), but code size is slightly bigger. config FEATURE_ETC_NETWORKS bool "Support for /etc/networks" default n help Enable support for network names in /etc/networks. This is a rarely used feature which allows you to use names instead of IP/mask pairs in route command. config FEATURE_USE_TERMIOS bool "Use termios to manipulate the screen" default y depends on MORE || TOP || POWERTOP help This option allows utilities such as 'more' and 'top' to determine the size of the screen. If you leave this disabled, your utilities that display things on the screen will be especially primitive and will be unable to determine the current screen size, and will be unable to move the cursor. config FEATURE_EDITING bool "Command line editing" default y help Enable line editing (mainly for shell command line). config FEATURE_EDITING_MAX_LEN int "Maximum length of input" range 128 8192 default 1024 depends on FEATURE_EDITING help Line editing code uses on-stack buffers for storage. You may want to decrease this parameter if your target machine benefits from smaller stack usage. config FEATURE_EDITING_VI bool "vi-style line editing commands" default n depends on FEATURE_EDITING help Enable vi-style line editing. In shells, this mode can be turned on and off with "set -o vi" and "set +o vi". config FEATURE_EDITING_HISTORY int "History size" # Don't allow way too big values here, code uses fixed "char *history[N]" struct member range 0 9999 default 255 depends on FEATURE_EDITING help Specify command history size (0 - disable). config FEATURE_EDITING_SAVEHISTORY bool "History saving" default y depends on FEATURE_EDITING help Enable history saving in shells. config FEATURE_EDITING_SAVE_ON_EXIT bool "Save history on shell exit, not after every command" default n depends on FEATURE_EDITING_SAVEHISTORY help Save history on shell exit, not after every command. config FEATURE_REVERSE_SEARCH bool "Reverse history search" default y depends on FEATURE_EDITING_SAVEHISTORY help Enable readline-like Ctrl-R combination for reverse history search. Increases code by about 0.5k. config FEATURE_TAB_COMPLETION bool "Tab completion" default y depends on FEATURE_EDITING help Enable tab completion. config FEATURE_USERNAME_COMPLETION bool "Username completion" default n depends on FEATURE_TAB_COMPLETION help Enable username completion. config FEATURE_EDITING_FANCY_PROMPT bool "Fancy shell prompts" default y depends on FEATURE_EDITING help Setting this option allows for prompts to use things like \w and \$ and escape codes. config FEATURE_EDITING_ASK_TERMINAL bool "Query cursor position from terminal" default n depends on FEATURE_EDITING help Allow usage of "ESC [ 6 n" sequence. Terminal answers back with current cursor position. This information is used to make line editing more robust in some cases. If you are not sure whether your terminals respond to this code correctly, or want to save on code size (about 400 bytes), then do not turn this option on. config FEATURE_NON_POSIX_CP bool "Non-POSIX, but safer, copying to special nodes" default y help With this option, "cp file symlink" will delete symlink and create a regular file. This does not conform to POSIX, but prevents a symlink attack. Similarly, "cp file device" will not send file's data to the device. (To do that, use "cat file >device") config FEATURE_VERBOSE_CP_MESSAGE bool "Give more precise messages when copy fails (cp, mv etc)" default n help Error messages with this feature enabled: $ cp file /does_not_exist/file cp: cannot create '/does_not_exist/file': Path does not exist $ cp file /vmlinuz/file cp: cannot stat '/vmlinuz/file': Path has non-directory component If this feature is not enabled, they will be, respectively: cp: cannot create '/does_not_exist/file': No such file or directory cp: cannot stat '/vmlinuz/file': Not a directory This will cost you ~60 bytes. config FEATURE_COPYBUF_KB int "Copy buffer size, in kilobytes" range 1 1024 default 4 help Size of buffer used by cp, mv, install, wget etc. Buffers which are 4 kb or less will be allocated on stack. Bigger buffers will be allocated with mmap, with fallback to 4 kb stack buffer if mmap fails. config FEATURE_SKIP_ROOTFS bool "Skip rootfs in mount table" default y help Ignore rootfs entry in mount table. In Linux, kernel has a special filesystem, rootfs, which is initially mounted on /. It contains initramfs data, if kernel is configured to have one. Usually, another file system is mounted over / early in boot process, and therefore most tools which manipulate mount table, such as df, will skip rootfs entry. However, some systems do not mount anything on /. If you need to configure busybox for one of these systems, you may find it useful to turn this option off to make df show initramfs statistics. Otherwise, choose Y. config MONOTONIC_SYSCALL bool "Use clock_gettime(CLOCK_MONOTONIC) syscall" default n select PLATFORM_LINUX help Use clock_gettime(CLOCK_MONOTONIC) syscall for measuring time intervals (time, ping, traceroute etc need this). Probably requires Linux 2.6+. If not selected, gettimeofday will be used instead (which gives wrong results if date/time is reset). config IOCTL_HEX2STR_ERROR bool "Use ioctl names rather than hex values in error messages" default y help Use ioctl names rather than hex values in error messages (e.g. VT_DISALLOCATE rather than 0x5608). If disabled this saves about 1400 bytes. config FEATURE_HWIB bool "Support infiniband HW" default y help Support for printing infiniband addresses in network applets. endmenu busybox-1.22.1/libbb/simplify_path.c0000644000000000000000000000215212263563520016060 0ustar rootroot/* 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 source tree. */ #include "libbb.h" char* FAST_FUNC bb_simplify_abs_path_inplace(char *start) { char *s, *p; p = s = start; do { if (*p == '/') { if (*s == '/') { /* skip duplicate (or initial) slash */ continue; } if (*s == '.') { if (s[1] == '/' || !s[1]) { /* remove extra '.' */ continue; } if ((s[1] == '.') && (s[2] == '/' || !s[2])) { ++s; if (p > start) { while (*--p != '/') /* omit previous dir */ continue; } continue; } } } *++p = *s; } while (*++s); if ((p == start) || (*p != '/')) { /* not a trailing slash */ ++p; /* so keep last character */ } *p = '\0'; return p; } char* FAST_FUNC bb_simplify_path(const char *path) { char *s, *p; if (path[0] == '/') s = xstrdup(path); else { p = xrealloc_getcwd_or_warn(NULL); s = concat_path_file(p, path); free(p); } bb_simplify_abs_path_inplace(s); return s; } busybox-1.22.1/libbb/llist.c0000644000000000000000000000366312263563520014347 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * linked list helper functions. * * Copyright (C) 2003 Glenn McGrath * Copyright (C) 2005 Vladimir Oleynik * Copyright (C) 2005 Bernhard Reutner-Fischer * Copyright (C) 2006 Rob Landley * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" /* Add data to the start of the linked list. */ void FAST_FUNC llist_add_to(llist_t **old_head, void *data) { llist_t *new_head = xmalloc(sizeof(llist_t)); new_head->data = data; new_head->link = *old_head; *old_head = new_head; } /* Add data to the end of the linked list. */ void FAST_FUNC llist_add_to_end(llist_t **list_head, void *data) { while (*list_head) list_head = &(*list_head)->link; *list_head = xzalloc(sizeof(llist_t)); (*list_head)->data = data; /*(*list_head)->link = NULL;*/ } /* Remove first element from the list and return it */ void* FAST_FUNC llist_pop(llist_t **head) { void *data = NULL; llist_t *temp = *head; if (temp) { data = temp->data; *head = temp->link; free(temp); } return data; } /* Unlink arbitrary given element from the list */ void FAST_FUNC llist_unlink(llist_t **head, llist_t *elm) { if (!elm) return; while (*head) { if (*head == elm) { *head = (*head)->link; break; } head = &(*head)->link; } } /* Recursively free all elements in the linked list. If freeit != NULL * call it on each datum in the list */ void FAST_FUNC llist_free(llist_t *elm, void (*freeit)(void *data)) { while (elm) { void *data = llist_pop(&elm); if (freeit) freeit(data); } } /* Reverse list order. */ llist_t* FAST_FUNC llist_rev(llist_t *list) { llist_t *rev = NULL; while (list) { llist_t *next = list->link; list->link = rev; rev = list; list = next; } return rev; } llist_t* FAST_FUNC llist_find_str(llist_t *list, const char *str) { while (list) { if (strcmp(list->data, str) == 0) break; list = list->link; } return list; } busybox-1.22.1/libbb/kernel_version.c0000644000000000000000000000146612263563520016244 0ustar rootroot/* 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 source tree. */ #include "libbb.h" /* After libbb.h, since it needs sys/types.h on some systems */ #include /* for uname(2) */ /* Returns current kernel version encoded as major*65536 + minor*256 + patch, * so, for example, to check if the kernel is greater than 2.2.11: * * if (get_linux_version_code() > KERNEL_VERSION(2,2,11)) { } */ int FAST_FUNC get_linux_version_code(void) { struct utsname name; char *s, *t; int i, r; uname(&name); /* never fails */ s = name.release; r = 0; for (i = 0; i < 3; i++) { t = strtok(s, "."); r = r * 256 + (t ? atoi(t) : 0); s = NULL; } return r; } busybox-1.22.1/libbb/safe_strncpy.c0000644000000000000000000000135412263563520015713 0ustar rootroot/* 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 source tree. */ #include "libbb.h" /* Like strncpy but make sure the resulting string is always 0 terminated. */ char* FAST_FUNC safe_strncpy(char *dst, const char *src, size_t size) { if (!size) return dst; dst[--size] = '\0'; return strncpy(dst, src, size); } /* Like strcpy but can copy overlapping strings. */ void FAST_FUNC overlapping_strcpy(char *dst, const char *src) { /* Cheap optimization for dst == src case - * better to have it here than in many callers. */ if (dst != src) { while ((*dst = *src) != '\0') { dst++; src++; } } } busybox-1.22.1/libbb/make_directory.c0000644000000000000000000000654212263563520016220 0ustar rootroot/* 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 source tree. */ /* Mar 5, 2003 Manuel Novoa III * * This is the main work function for the 'mkdir' applet. As such, it * strives to be SUSv3 compliant in it's behaviour when recursively * making missing parent dirs, and in it's mode setting of the final * directory 'path'. * * To recursively build all missing intermediate directories, make * sure that (flags & FILEUTILS_RECUR) is non-zero. Newly created * intermediate directories will have at least u+wx perms. * * To set specific permissions on 'path', pass the appropriate 'mode' * val. Otherwise, pass -1 to get default permissions. */ #include "libbb.h" /* This function is used from NOFORK applets. It must not allocate anything */ int FAST_FUNC bb_make_directory(char *path, long mode, int flags) { mode_t cur_mask; mode_t org_mask; const char *fail_msg; char *s; char c; struct stat st; /* Happens on bb_make_directory(dirname("no_slashes"),...) */ if (LONE_CHAR(path, '.')) return 0; org_mask = cur_mask = (mode_t)-1L; s = path; while (1) { c = '\0'; if (flags & FILEUTILS_RECUR) { /* Get the parent */ /* Bypass leading non-'/'s and then subsequent '/'s */ while (*s) { if (*s == '/') { do { ++s; } while (*s == '/'); c = *s; /* Save the current char */ *s = '\0'; /* and replace it with nul */ break; } ++s; } } if (c != '\0') { /* Intermediate dirs: must have wx for user */ if (cur_mask == (mode_t)-1L) { /* wasn't done yet? */ mode_t new_mask; org_mask = umask(0); cur_mask = 0; /* Clear u=wx in umask - this ensures * they won't be cleared on mkdir */ new_mask = (org_mask & ~(mode_t)0300); //bb_error_msg("org_mask:%o cur_mask:%o", org_mask, new_mask); if (new_mask != cur_mask) { cur_mask = new_mask; umask(new_mask); } } } else { /* Last component: uses original umask */ //bb_error_msg("1 org_mask:%o", org_mask); if (org_mask != cur_mask) { cur_mask = org_mask; umask(org_mask); } } if (mkdir(path, 0777) < 0) { /* If we failed for any other reason than the directory * already exists, output a diagnostic and return -1 */ if ((errno != EEXIST && errno != EISDIR) || !(flags & FILEUTILS_RECUR) || ((stat(path, &st) < 0) || !S_ISDIR(st.st_mode)) ) { fail_msg = "create"; break; } /* Since the directory exists, don't attempt to change * permissions if it was the full target. Note that * this is not an error condition. */ if (!c) { goto ret0; } } if (!c) { /* Done. If necessary, update perms on the newly * created directory. Failure to update here _is_ * an error. */ if ((mode != -1) && (chmod(path, mode) < 0)) { fail_msg = "set permissions of"; if (flags & FILEUTILS_IGNORE_CHMOD_ERR) { flags = 0; goto print_err; } break; } goto ret0; } /* Remove any inserted nul from the path (recursive mode) */ *s = c; } /* while (1) */ flags = -1; print_err: bb_perror_msg("can't %s directory '%s'", fail_msg, path); goto ret; ret0: flags = 0; ret: //bb_error_msg("2 org_mask:%o", org_mask); if (org_mask != cur_mask) umask(org_mask); return flags; } busybox-1.22.1/libbb/bb_askpass.c0000644000000000000000000000365212263563520015326 0ustar rootroot/* 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 source tree. */ #include "libbb.h" /* do nothing signal handler */ static void askpass_timeout(int UNUSED_PARAM ignore) { } char* FAST_FUNC bb_ask_stdin(const char *prompt) { return bb_ask(STDIN_FILENO, 0, prompt); } char* FAST_FUNC bb_ask(const int fd, int timeout, const char *prompt) { /* Was static char[BIGNUM] */ enum { sizeof_passwd = 128 }; static char *passwd; char *ret; int i; struct sigaction sa, oldsa; struct termios tio, oldtio; fputs(prompt, stdout); fflush_all(); tcflush(fd, TCIFLUSH); tcgetattr(fd, &oldtio); tio = oldtio; #if 0 /* Switch off UPPERCASE->lowercase conversion (never used since 198x) * and XON/XOFF (why we want to mess with this??) */ # ifndef IUCLC # define IUCLC 0 # endif tio.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY); #endif /* Switch off echo */ tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL); tcsetattr(fd, TCSANOW, &tio); memset(&sa, 0, sizeof(sa)); /* sa.sa_flags = 0; - no SA_RESTART! */ /* SIGINT and SIGALRM will interrupt reads below */ sa.sa_handler = askpass_timeout; sigaction(SIGINT, &sa, &oldsa); if (timeout) { sigaction_set(SIGALRM, &sa); alarm(timeout); } if (!passwd) passwd = xmalloc(sizeof_passwd); ret = passwd; i = 0; while (1) { int r = read(fd, &ret[i], 1); if ((i == 0 && r == 0) /* EOF (^D) with no password */ || r < 0 ) { /* read is interrupted by timeout or ^C */ ret = NULL; break; } if (r == 0 /* EOF */ || ret[i] == '\r' || ret[i] == '\n' /* EOL */ || ++i == sizeof_passwd-1 /* line limit */ ) { ret[i] = '\0'; break; } } if (timeout) { alarm(0); } sigaction_set(SIGINT, &oldsa); tcsetattr(fd, TCSANOW, &oldtio); bb_putchar('\n'); fflush_all(); return ret; } busybox-1.22.1/libbb/dump.c0000644000000000000000000004742612263563520014172 0ustar rootroot/* 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 source tree. * * Original copyright notice is retained at the end of this file. */ #include "libbb.h" #include "dump.h" static const char index_str[] ALIGN1 = ".#-+ 0123456789"; static const char size_conv_str[] ALIGN1 = "\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG"; static const char lcc[] ALIGN1 = "diouxX"; typedef struct priv_dumper_t { dumper_t pub; char **argv; FU *endfu; off_t savaddress; /* saved address/offset in stream */ off_t eaddress; /* end address */ off_t address; /* address/offset in stream */ int blocksize; smallint exitval; /* final exit value */ /* former statics */ smallint next__done; smallint get__ateof; // = 1; unsigned char *get__curp; unsigned char *get__savp; } priv_dumper_t; dumper_t* FAST_FUNC alloc_dumper(void) { priv_dumper_t *dumper = xzalloc(sizeof(*dumper)); dumper->pub.dump_length = -1; dumper->pub.dump_vflag = FIRST; dumper->get__ateof = 1; return &dumper->pub; } static NOINLINE int bb_dump_size(FS *fs) { FU *fu; int bcnt, cur_size; char *fmt; const char *p; int prec; /* figure out the data block bb_dump_size needed for each format unit */ for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) { if (fu->bcnt) { cur_size += fu->bcnt * fu->reps; continue; } for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) { if (*fmt != '%') continue; /* * skip any special chars -- save precision in * case it's a %s format. */ while (strchr(index_str + 1, *++fmt)) continue; if (*fmt == '.' && isdigit(*++fmt)) { prec = atoi(fmt); while (isdigit(*++fmt)) continue; } p = strchr(size_conv_str + 12, *fmt); if (!p) { if (*fmt == 's') { bcnt += prec; } else if (*fmt == '_') { ++fmt; if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) { bcnt += 1; } } } else { bcnt += size_conv_str[p - (size_conv_str + 12)]; } } cur_size += bcnt * fu->reps; } return cur_size; } static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs) { enum { NOTOKAY, USEBCNT, USEPREC } sokay; FU *fu; PR *pr; 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; ) { /* NOSTRICT */ /* DBU:[dvae@cray.com] zalloc so that forward ptrs start out NULL*/ pr = xzalloc(sizeof(PR)); if (!fu->nextpr) fu->nextpr = pr; /* skip preceding text and up to the next % sign */ for (p1 = fmtp; *p1 && *p1 != '%'; ++p1) continue; /* only text in the string */ if (!*p1) { pr->fmt = fmtp; pr->flags = F_TEXT; break; } /* * get precision for %s -- if have a byte count, don't * need it. */ if (fu->bcnt) { sokay = USEBCNT; /* skip to conversion character */ for (++p1; strchr(index_str, *p1); ++p1) continue; } else { /* skip any special chars, field width */ while (strchr(index_str + 1, *++p1)) continue; if (*p1 == '.' && isdigit(*++p1)) { sokay = USEPREC; prec = atoi(p1); while (isdigit(*++p1)) continue; } else sokay = NOTOKAY; } p2 = p1 + 1; /* set end pointer */ /* * figure out the byte count for each conversion; * rewrite the format as necessary, set up blank- * pbb_dump_adding for end of data. */ if (*p1 == 'c') { pr->flags = F_CHAR; DO_BYTE_COUNT_1: byte_count_str = "\001"; DO_BYTE_COUNT: if (fu->bcnt) { do { if (fu->bcnt == *byte_count_str) { break; } } while (*++byte_count_str); } /* Unlike the original, output the remainder of the format string. */ if (!*byte_count_str) { bb_error_msg_and_die("bad byte count for conversion character %s", p1); } pr->bcnt = *byte_count_str; } else if (*p1 == 'l') { ++p2; ++p1; DO_INT_CONV: { const char *e; e = strchr(lcc, *p1); if (!e) { goto DO_BAD_CONV_CHAR; } pr->flags = F_INT; if (e > lcc + 1) { pr->flags = F_UINT; } byte_count_str = "\004\002\001"; goto DO_BYTE_COUNT; } /* NOTREACHED */ } else if (strchr(lcc, *p1)) { goto DO_INT_CONV; } else if (strchr("eEfgG", *p1)) { pr->flags = F_DBL; byte_count_str = "\010\004"; goto DO_BYTE_COUNT; } else if (*p1 == 's') { pr->flags = F_STR; if (sokay == USEBCNT) { pr->bcnt = fu->bcnt; } else if (sokay == USEPREC) { pr->bcnt = prec; } else { /* NOTOKAY */ bb_error_msg_and_die("%%s requires a precision or a byte count"); } } else if (*p1 == '_') { ++p2; switch (p1[1]) { case 'A': dumper->endfu = fu; fu->flags |= F_IGNORE; /* FALLTHROUGH */ case 'a': pr->flags = F_ADDRESS; ++p2; if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) { goto DO_BAD_CONV_CHAR; } *p1 = p1[2]; break; case 'c': pr->flags = F_C; /* *p1 = 'c'; set in conv_c */ goto DO_BYTE_COUNT_1; case 'p': pr->flags = F_P; *p1 = 'c'; goto DO_BYTE_COUNT_1; case 'u': pr->flags = F_U; /* *p1 = 'c'; set in conv_u */ goto DO_BYTE_COUNT_1; default: goto DO_BAD_CONV_CHAR; } } else { DO_BAD_CONV_CHAR: bb_error_msg_and_die("bad conversion character %%%s", p1); } /* * copy to PR format string, set conversion character * pointer, update original. */ savech = *p2; p1[1] = '\0'; pr->fmt = xstrdup(fmtp); *p2 = savech; //Too early! xrealloc can move pr->fmt! //pr->cchar = pr->fmt + (p1 - fmtp); /* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost. * Skip subsequent text and up to the next % sign and tack the * additional text onto fmt: eg. if fmt is "%x is a HEX number", * we lose the " is a HEX number" part of fmt. */ for (p3 = p2; *p3 && *p3 != '%'; p3++) continue; if (p3 > p2) { savech = *p3; *p3 = '\0'; pr->fmt = xrealloc(pr->fmt, strlen(pr->fmt) + (p3-p2) + 1); strcat(pr->fmt, p2); *p3 = savech; p2 = p3; } pr->cchar = pr->fmt + (p1 - fmtp); fmtp = p2; /* only one conversion character if byte count */ if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) { bb_error_msg_and_die("byte count with multiple conversion characters"); } } /* * if format unit byte count not specified, figure it out * so can adjust rep count later. */ if (!fu->bcnt) for (pr = fu->nextpr; pr; pr = pr->nextpr) fu->bcnt += pr->bcnt; } /* * if the format string interprets any data at all, and it's * not the same as the blocksize, and its last format unit * interprets any data at all, and has no iteration count, * repeat it as necessary. * * if rep count is greater than 1, no trailing whitespace * gets output from the last iteration of the format unit. */ for (fu = fs->nextfu; fu; fu = fu->nextfu) { if (!fu->nextfu && fs->bcnt < dumper->blocksize && !(fu->flags & F_SETREP) && fu->bcnt ) { fu->reps += (dumper->blocksize - fs->bcnt) / fu->bcnt; } if (fu->reps > 1 && fu->nextpr) { for (pr = fu->nextpr;; pr = pr->nextpr) if (!pr->nextpr) break; for (p1 = pr->fmt, p2 = NULL; *p1; ++p1) p2 = isspace(*p1) ? p1 : NULL; if (p2) pr->nospace = p2; } if (!fu->nextfu) break; } } static void do_skip(priv_dumper_t *dumper, const char *fname, int statok) { struct stat sbuf; if (statok) { xfstat(STDIN_FILENO, &sbuf, fname); if (!(S_ISCHR(sbuf.st_mode) || S_ISBLK(sbuf.st_mode) || S_ISFIFO(sbuf.st_mode)) && dumper->pub.dump_skip >= sbuf.st_size ) { /* If bb_dump_size valid and pub.dump_skip >= size */ dumper->pub.dump_skip -= sbuf.st_size; dumper->address += sbuf.st_size; return; } } if (fseeko(stdin, dumper->pub.dump_skip, SEEK_SET)) { bb_simple_perror_msg_and_die(fname); } dumper->address += dumper->pub.dump_skip; dumper->savaddress = dumper->address; dumper->pub.dump_skip = 0; } static NOINLINE int next(priv_dumper_t *dumper) { int statok; for (;;) { if (*dumper->argv) { dumper->next__done = statok = 1; if (!(freopen(*dumper->argv, "r", stdin))) { bb_simple_perror_msg(*dumper->argv); dumper->exitval = 1; ++dumper->argv; continue; } } else { if (dumper->next__done) return 0; /* no next file */ dumper->next__done = 1; statok = 0; } if (dumper->pub.dump_skip) do_skip(dumper, statok ? *dumper->argv : "stdin", statok); if (*dumper->argv) ++dumper->argv; if (!dumper->pub.dump_skip) return 1; } /* NOTREACHED */ } static unsigned char *get(priv_dumper_t *dumper) { int n; int need, nread; int blocksize = dumper->blocksize; if (!dumper->get__curp) { dumper->address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/ dumper->get__curp = xmalloc(blocksize); dumper->get__savp = xzalloc(blocksize); /* need to be initialized */ } else { unsigned char *tmp = dumper->get__curp; dumper->get__curp = dumper->get__savp; dumper->get__savp = tmp; dumper->savaddress += blocksize; dumper->address = dumper->savaddress; } need = blocksize; nread = 0; while (1) { /* * if read the right number of bytes, or at EOF for one file, * and no other files are available, zero-pad the rest of the * block and set the end flag. */ if (!dumper->pub.dump_length || (dumper->get__ateof && !next(dumper))) { if (need == blocksize) { return NULL; } if (dumper->pub.dump_vflag != ALL && !memcmp(dumper->get__curp, dumper->get__savp, nread)) { if (dumper->pub.dump_vflag != DUP) { puts("*"); } return NULL; } memset(dumper->get__curp + nread, 0, need); dumper->eaddress = dumper->address + nread; return dumper->get__curp; } n = fread(dumper->get__curp + nread, sizeof(unsigned char), dumper->pub.dump_length == -1 ? need : MIN(dumper->pub.dump_length, need), stdin); if (!n) { if (ferror(stdin)) { bb_simple_perror_msg(dumper->argv[-1]); } dumper->get__ateof = 1; continue; } dumper->get__ateof = 0; if (dumper->pub.dump_length != -1) { dumper->pub.dump_length -= n; } need -= n; if (!need) { if (dumper->pub.dump_vflag == ALL || dumper->pub.dump_vflag == FIRST || memcmp(dumper->get__curp, dumper->get__savp, blocksize) ) { if (dumper->pub.dump_vflag == DUP || dumper->pub.dump_vflag == FIRST) { dumper->pub.dump_vflag = WAIT; } return dumper->get__curp; } if (dumper->pub.dump_vflag == WAIT) { puts("*"); } dumper->pub.dump_vflag = DUP; dumper->savaddress += blocksize; dumper->address = dumper->savaddress; need = blocksize; nread = 0; } else { nread += n; } } } static void bpad(PR *pr) { char *p1, *p2; /* * remove all conversion flags; '-' is the only one valid * with %s, and it's not useful here. */ pr->flags = F_BPAD; *pr->cchar = 's'; for (p1 = pr->fmt; *p1 != '%'; ++p1) continue; for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1) if (pr->nospace) pr->nospace--; while ((*p2++ = *p1++) != 0) continue; } static const char conv_str[] ALIGN1 = "\0\\0\0" "\007\\a\0" /* \a */ "\b\\b\0" "\f\\b\0" "\n\\n\0" "\r\\r\0" "\t\\t\0" "\v\\v\0" ; static void conv_c(PR *pr, unsigned char *p) { const char *str = conv_str; char buf[10]; do { if (*p == *str) { ++str; goto strpr; } str += 4; } while (*str); if (isprint_asciionly(*p)) { *pr->cchar = 'c'; printf(pr->fmt, *p); } else { sprintf(buf, "%03o", (int) *p); str = buf; strpr: *pr->cchar = 's'; printf(pr->fmt, str); } } static void conv_u(PR *pr, unsigned char *p) { static const char list[] ALIGN1 = "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0" "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_" "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0" "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us"; /* od used nl, not lf */ if (*p <= 0x1f) { *pr->cchar = 's'; printf(pr->fmt, list + (4 * (int)*p)); } else if (*p == 0x7f) { *pr->cchar = 's'; printf(pr->fmt, "del"); } else if (*p < 0x7f) { /* isprint() */ *pr->cchar = 'c'; printf(pr->fmt, *p); } else { *pr->cchar = 'x'; printf(pr->fmt, (int) *p); } } static void display(priv_dumper_t* dumper) { FS *fs; FU *fu; PR *pr; int cnt; unsigned char *bp, *savebp; off_t saveaddress; unsigned char savech = '\0'; while ((bp = get(dumper)) != NULL) { fs = dumper->pub.fshead; savebp = bp; saveaddress = dumper->address; for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) { for (fu = fs->nextfu; fu; fu = fu->nextfu) { if (fu->flags & F_IGNORE) { break; } for (cnt = fu->reps; cnt; --cnt) { for (pr = fu->nextpr; pr; dumper->address += pr->bcnt, bp += pr->bcnt, pr = pr->nextpr) { if (dumper->eaddress && dumper->address >= dumper->eaddress && !(pr->flags & (F_TEXT | F_BPAD)) ) { bpad(pr); } if (cnt == 1 && pr->nospace) { savech = *pr->nospace; *pr->nospace = '\0'; } /* PRINT; */ switch (pr->flags) { case F_ADDRESS: printf(pr->fmt, (unsigned) dumper->address); break; case F_BPAD: printf(pr->fmt, ""); break; case F_C: conv_c(pr, bp); break; case F_CHAR: printf(pr->fmt, *bp); break; case F_DBL: { double dval; float fval; switch (pr->bcnt) { case 4: memcpy(&fval, bp, sizeof(fval)); printf(pr->fmt, fval); break; case 8: memcpy(&dval, bp, sizeof(dval)); printf(pr->fmt, dval); break; } break; } case F_INT: { int ival; short sval; switch (pr->bcnt) { case 1: printf(pr->fmt, (int) *bp); break; case 2: memcpy(&sval, bp, sizeof(sval)); printf(pr->fmt, (int) sval); break; case 4: memcpy(&ival, bp, sizeof(ival)); printf(pr->fmt, ival); break; } break; } case F_P: printf(pr->fmt, isprint_asciionly(*bp) ? *bp : '.'); break; case F_STR: printf(pr->fmt, (char *) bp); break; case F_TEXT: printf(pr->fmt); break; case F_U: conv_u(pr, bp); break; case F_UINT: { unsigned ival; unsigned short sval; switch (pr->bcnt) { case 1: printf(pr->fmt, (unsigned) *bp); break; case 2: memcpy(&sval, bp, sizeof(sval)); printf(pr->fmt, (unsigned) sval); break; case 4: memcpy(&ival, bp, sizeof(ival)); printf(pr->fmt, ival); break; } break; } } if (cnt == 1 && pr->nospace) { *pr->nospace = savech; } } } } } } if (dumper->endfu) { /* * if eaddress not set, error or file size was multiple * of blocksize, and no partial block ever found. */ if (!dumper->eaddress) { if (!dumper->address) { return; } dumper->eaddress = dumper->address; } for (pr = dumper->endfu->nextpr; pr; pr = pr->nextpr) { switch (pr->flags) { case F_ADDRESS: printf(pr->fmt, (unsigned) dumper->eaddress); break; case F_TEXT: printf(pr->fmt); break; } } } } #define dumper ((priv_dumper_t*)pub_dumper) int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv) { FS *tfs; int blocksize; /* figure out the data block bb_dump_size */ blocksize = 0; tfs = dumper->pub.fshead; while (tfs) { tfs->bcnt = bb_dump_size(tfs); if (blocksize < tfs->bcnt) { blocksize = tfs->bcnt; } tfs = tfs->nextfs; } dumper->blocksize = blocksize; /* rewrite the rules, do syntax checking */ for (tfs = dumper->pub.fshead; tfs; tfs = tfs->nextfs) { rewrite(dumper, tfs); } dumper->argv = argv; display(dumper); return dumper->exitval; } void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt) { const char *p; char *p1; char *p2; FS *tfs; FU *tfu, **nextfupp; const char *savep; /* start new linked list of format units */ tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */ if (!dumper->pub.fshead) { dumper->pub.fshead = tfs; } else { FS *fslast = dumper->pub.fshead; while (fslast->nextfs) fslast = fslast->nextfs; fslast->nextfs = tfs; } nextfupp = &tfs->nextfu; /* take the format string and break it up into format units */ p = fmt; for (;;) { p = skip_whitespace(p); if (*p == '\0') { break; } /* allocate a new format unit and link it in */ /* NOSTRICT */ /* DBU:[dave@cray.com] zalloc so that forward pointers start out NULL */ tfu = xzalloc(sizeof(FU)); *nextfupp = tfu; nextfupp = &tfu->nextfu; tfu->reps = 1; /* if leading digit, repetition count */ if (isdigit(*p)) { for (savep = p; isdigit(*p); ++p) continue; if (!isspace(*p) && *p != '/') { bb_error_msg_and_die("bad format {%s}", fmt); } /* may overwrite either white space or slash */ tfu->reps = atoi(savep); tfu->flags = F_SETREP; /* skip trailing white space */ p = skip_whitespace(++p); } /* skip slash and trailing white space */ if (*p == '/') { p = skip_whitespace(p + 1); } /* byte count */ if (isdigit(*p)) { // TODO: use bb_strtou savep = p; while (isdigit(*++p)) continue; if (!isspace(*p)) { bb_error_msg_and_die("bad format {%s}", fmt); } tfu->bcnt = atoi(savep); /* skip trailing white space */ p = skip_whitespace(p + 1); } /* format */ if (*p != '"') { bb_error_msg_and_die("bad format {%s}", fmt); } for (savep = ++p; *p != '"';) { if (*p++ == '\0') { bb_error_msg_and_die("bad format {%s}", fmt); } } tfu->fmt = xstrndup(savep, p - savep); /* escape(tfu->fmt); */ p1 = tfu->fmt; /* alphabetic escape sequences have to be done in place */ for (p2 = p1;; ++p1, ++p2) { if (*p1 == '\0') { *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. */ busybox-1.22.1/libbb/vdprintf.c0000644000000000000000000000067112263563520015050 0ustar rootroot/* 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 source tree. */ #include "libbb.h" #if defined(__GLIBC__) && __GLIBC__ < 2 int FAST_FUNC vdprintf(int d, const char *format, va_list ap) { char buf[8 * 1024]; int len; len = vsnprintf(buf, sizeof(buf), format, ap); return write(d, buf, len); } #endif busybox-1.22.1/libbb/Kbuild.src0000644000000000000000000001213712263563520014773 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. libbb/appletlib.o: include/usage_compressed.h lib-y:= INSERT lib-y += appletlib.o lib-y += ask_confirmation.o lib-y += bb_askpass.o lib-y += bb_bswap_64.o lib-y += bb_do_delay.o lib-y += bb_pwd.o lib-y += bb_qsort.o #lib-y += bb_strtod.o lib-y += bb_strtonum.o lib-y += change_identity.o lib-y += chomp.o lib-y += compare_string_array.o lib-y += concat_path_file.o lib-y += concat_subpath_file.o lib-y += copy_file.o lib-y += copyfd.o lib-y += crc32.o lib-y += default_error_retval.o lib-y += device_open.o lib-y += dump.o lib-y += execable.o lib-y += fclose_nonstdin.o lib-y += fflush_stdout_and_exit.o lib-y += fgets_str.o lib-y += find_pid_by_name.o lib-y += find_root_device.o lib-y += full_write.o lib-y += get_console.o lib-y += get_last_path_component.o lib-y += get_line_from_file.o lib-y += getopt32.o lib-y += getpty.o lib-y += get_volsize.o lib-y += herror_msg.o lib-y += human_readable.o lib-y += inet_common.o lib-y += info_msg.o lib-y += inode_hash.o lib-y += isdirectory.o lib-y += kernel_version.o lib-y += last_char_is.o lib-y += lineedit.o lineedit_ptr_hack.o lib-y += llist.o lib-y += login.o lib-y += make_directory.o lib-y += makedev.o lib-y += hash_md5_sha.o # Alternative (disabled) MD5 implementation #lib-y += hash_md5prime.o lib-y += messages.o lib-y += mode_string.o lib-y += parse_mode.o lib-y += perror_msg.o lib-y += perror_nomsg.o lib-y += perror_nomsg_and_die.o lib-y += pidfile.o lib-y += platform.o lib-y += printable.o lib-y += printable_string.o lib-y += print_flags.o lib-y += process_escape_sequence.o lib-y += procps.o lib-y += progress.o lib-y += ptr_to_globals.o lib-y += read.o lib-y += read_printf.o lib-y += read_key.o lib-y += recursive_action.o lib-y += remove_file.o lib-y += run_shell.o lib-y += safe_gethostname.o lib-y += safe_poll.o lib-y += safe_strncpy.o lib-y += safe_write.o lib-y += setup_environment.o lib-y += signals.o lib-y += simplify_path.o lib-y += single_argv.o lib-y += skip_whitespace.o lib-y += speed_table.o lib-y += str_tolower.o lib-y += strrstr.o lib-y += time.o lib-y += trim.o lib-y += u_signal_names.o lib-y += uuencode.o lib-y += vdprintf.o lib-y += verror_msg.o lib-y += vfork_daemon_rexec.o lib-y += warn_ignoring_args.o lib-y += wfopen.o lib-y += wfopen_input.o lib-y += write.o lib-y += xatonum.o lib-y += xconnect.o lib-y += xfuncs.o lib-y += xfuncs_printf.o lib-y += xfunc_die.o lib-y += xgetcwd.o lib-y += xgethostbyname.o lib-y += xreadlink.o lib-y += xrealloc_vector.o lib-$(CONFIG_PLATFORM_LINUX) += match_fstype.o lib-$(CONFIG_FEATURE_UTMP) += utmp.o # A mix of optimizations (why build stuff we know won't be used) # and objects which may fail to build (SELinux on selinux-less system) lib-$(CONFIG_SELINUX) += selinux_common.o lib-$(CONFIG_FEATURE_MTAB_SUPPORT) += mtab.o lib-$(CONFIG_UNICODE_SUPPORT) += unicode.o lib-$(CONFIG_FEATURE_CHECK_NAMES) += die_if_bad_username.o lib-$(CONFIG_NC) += udp_io.o lib-$(CONFIG_DNSD) += udp_io.o lib-$(CONFIG_NTPD) += udp_io.o lib-$(CONFIG_TFTP) += udp_io.o lib-$(CONFIG_TFTPD) += udp_io.o lib-$(CONFIG_TCPSVD) += udp_io.o lib-$(CONFIG_UDPSVD) += udp_io.o lib-$(CONFIG_TRACEROUTE) += udp_io.o lib-$(CONFIG_LOSETUP) += loop.o lib-$(CONFIG_FEATURE_MOUNT_LOOP) += loop.o lib-$(CONFIG_ADDGROUP) += update_passwd.o lib-$(CONFIG_ADDUSER) += update_passwd.o lib-$(CONFIG_DELGROUP) += update_passwd.o lib-$(CONFIG_DELUSER) += update_passwd.o lib-$(CONFIG_PASSWD) += pw_encrypt.o update_passwd.o obscure.o lib-$(CONFIG_CHPASSWD) += pw_encrypt.o update_passwd.o lib-$(CONFIG_CRYPTPW) += pw_encrypt.o lib-$(CONFIG_SULOGIN) += pw_encrypt.o correct_password.o lib-$(CONFIG_VLOCK) += pw_encrypt.o correct_password.o lib-$(CONFIG_SU) += pw_encrypt.o correct_password.o lib-$(CONFIG_LOGIN) += pw_encrypt.o correct_password.o lib-$(CONFIG_FEATURE_HTTPD_AUTH_MD5) += pw_encrypt.o lib-$(CONFIG_DF) += find_mount_point.o lib-$(CONFIG_MKFS_MINIX) += find_mount_point.o lib-$(CONFIG_MKFS_EXT2) += find_mount_point.o lib-$(CONFIG_MKFS_REISER) += find_mount_point.o lib-$(CONFIG_FSCK_MINIX) += find_mount_point.o lib-$(CONFIG_MOUNT) += find_mount_point.o lib-$(CONFIG_HWCLOCK) += rtc.o lib-$(CONFIG_RTCWAKE) += rtc.o lib-$(CONFIG_IOSTAT) += get_cpu_count.o lib-$(CONFIG_MPSTAT) += get_cpu_count.o lib-$(CONFIG_POWERTOP) += get_cpu_count.o lib-$(CONFIG_PING) += inet_cksum.o lib-$(CONFIG_TRACEROUTE) += inet_cksum.o lib-$(CONFIG_TRACEROUTE6) += inet_cksum.o lib-$(CONFIG_UDHCPC) += inet_cksum.o lib-$(CONFIG_UDHCPC6) += inet_cksum.o lib-$(CONFIG_UDHCPD) += inet_cksum.o # We shouldn't build xregcomp.c if we don't need it - this ensures we don't # require regex.h to be in the include dir even if we don't need it thereby # allowing us to build busybox even if uclibc regex support is disabled. lib-$(CONFIG_AWK) += xregcomp.o lib-$(CONFIG_SED) += xregcomp.o lib-$(CONFIG_GREP) += xregcomp.o lib-$(CONFIG_EXPR) += xregcomp.o lib-$(CONFIG_MDEV) += xregcomp.o lib-$(CONFIG_LESS) += xregcomp.o lib-$(CONFIG_PGREP) += xregcomp.o lib-$(CONFIG_PKILL) += xregcomp.o lib-$(CONFIG_DEVFSD) += xregcomp.o lib-$(CONFIG_FEATURE_FIND_REGEX) += xregcomp.o busybox-1.22.1/libbb/isdirectory.c0000644000000000000000000000124312263563520015550 0ustar rootroot/* 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 GPL. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include #include "libbb.h" /* * Return TRUE if fileName is a directory. * Nonexistent files return FALSE. */ int FAST_FUNC is_directory(const char *fileName, int followLinks) { int status; struct stat statBuf; if (followLinks) status = stat(fileName, &statBuf); else status = lstat(fileName, &statBuf); status = (status == 0 && S_ISDIR(statBuf.st_mode)); return status; } busybox-1.22.1/libbb/endofname.c0000644000000000000000000000074612263563520015153 0ustar rootroot/* * Utility routines. * * Copyright (C) 2013 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ //kbuild:lib-y += endofname.o #include "libbb.h" #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) const char* FAST_FUNC endofname(const char *name) { if (!is_name(*name)) return name; while (*++name) { if (!is_in_name(*name)) break; } return name; } busybox-1.22.1/libbb/lineedit.c0000644000000000000000000021452712267106071015016 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Command line editing. * * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license. * Written by: Vladimir Oleynik * * Used ideas: * Adam Rogoyski * Dave Cinege * Jakub Jelinek (c) 1995 * Erik Andersen (Majorly adjusted for busybox) * * This code is 'as is' with no warranty. */ /* * Usage and known bugs: * Terminal key codes are not extensive, more needs to be added. * This version was created on Debian GNU/Linux 2.x. * Delete, Backspace, Home, End, and the arrow keys were tested * to work in an Xterm and console. Ctrl-A also works as Home. * Ctrl-E also works as End. * * The following readline-like commands are not implemented: * ESC-b -- Move back one word * ESC-f -- Move forward one word * ESC-d -- Delete forward one word * CTL-t -- Transpose two characters * * lineedit does not know that the terminal escape sequences do not * take up space on the screen. The redisplay code assumes, unless * told otherwise, that each character in the prompt is a printable * character that takes up one character position on the screen. * You need to tell lineedit that some sequences of characters * in the prompt take up no screen space. Compatibly with readline, * use the \[ escape to begin a sequence of non-printing characters, * and the \] escape to signal the end of such a sequence. Example: * * PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] ' * * Unicode in PS1 is not fully supported: prompt length calulation is wrong, * resulting in line wrap problems with long (multi-line) input. * * Multi-line PS1 (e.g. PS1="\n[\w]\n$ ") has problems with history * browsing: up/down arrows result in scrolling. * It stems from simplistic "cmdedit_y = cmdedit_prmt_len / cmdedit_termw" * calculation of how many lines the prompt takes. */ #include "libbb.h" #include "unicode.h" #ifndef _POSIX_VDISABLE # define _POSIX_VDISABLE '\0' #endif #ifdef TEST # define ENABLE_FEATURE_EDITING 0 # define ENABLE_FEATURE_TAB_COMPLETION 0 # define ENABLE_FEATURE_USERNAME_COMPLETION 0 #endif /* Entire file (except TESTing part) sits inside this #if */ #if ENABLE_FEATURE_EDITING #define ENABLE_USERNAME_OR_HOMEDIR \ (ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT) #define IF_USERNAME_OR_HOMEDIR(...) #if ENABLE_USERNAME_OR_HOMEDIR # undef IF_USERNAME_OR_HOMEDIR # define IF_USERNAME_OR_HOMEDIR(...) __VA_ARGS__ #endif #undef CHAR_T #if ENABLE_UNICODE_SUPPORT # define BB_NUL ((wchar_t)0) # define CHAR_T wchar_t static bool BB_isspace(CHAR_T c) { return ((unsigned)c < 256 && isspace(c)); } # if ENABLE_FEATURE_EDITING_VI static bool BB_isalnum(CHAR_T c) { return ((unsigned)c < 256 && isalnum(c)); } # endif static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); } # undef isspace # undef isalnum # undef ispunct # undef isprint # define isspace isspace_must_not_be_used # define isalnum isalnum_must_not_be_used # define ispunct ispunct_must_not_be_used # define isprint isprint_must_not_be_used #else # define BB_NUL '\0' # define CHAR_T char # define BB_isspace(c) isspace(c) # define BB_isalnum(c) isalnum(c) # define BB_ispunct(c) ispunct(c) #endif #if ENABLE_UNICODE_PRESERVE_BROKEN # define unicode_mark_raw_byte(wc) ((wc) | 0x20000000) # define unicode_is_raw_byte(wc) ((wc) & 0x20000000) #else # define unicode_is_raw_byte(wc) 0 #endif #define ESC "\033" #define SEQ_CLEAR_TILL_END_OF_SCREEN ESC"[J" //#define SEQ_CLEAR_TILL_END_OF_LINE ESC"[K" enum { MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0 ? CONFIG_FEATURE_EDITING_MAX_LEN : 0x7ff0 }; #if ENABLE_USERNAME_OR_HOMEDIR static const char null_str[] ALIGN1 = ""; #endif /* We try to minimize both static and stack usage. */ struct lineedit_statics { line_input_t *state; volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */ sighandler_t previous_SIGWINCH_handler; unsigned cmdedit_x; /* real x (col) terminal position */ unsigned cmdedit_y; /* pseudoreal y (row) terminal position */ unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */ unsigned cursor; int command_len; /* must be signed */ /* signed maxsize: we want x in "if (x > S.maxsize)" * to _not_ be promoted to unsigned */ int maxsize; CHAR_T *command_ps; const char *cmdedit_prompt; #if ENABLE_USERNAME_OR_HOMEDIR char *user_buf; char *home_pwd_buf; /* = (char*)null_str; */ #endif #if ENABLE_FEATURE_TAB_COMPLETION char **matches; unsigned num_matches; #endif #if ENABLE_FEATURE_EDITING_VI # define DELBUFSIZ 128 CHAR_T *delptr; smallint newdelflag; /* whether delbuf should be reused yet */ CHAR_T delbuf[DELBUFSIZ]; /* a place to store deleted characters */ #endif #if ENABLE_FEATURE_EDITING_ASK_TERMINAL smallint sent_ESC_br6n; #endif }; /* See lineedit_ptr_hack.c */ extern struct lineedit_statics *const lineedit_ptr_to_statics; #define S (*lineedit_ptr_to_statics) #define state (S.state ) #define cmdedit_termw (S.cmdedit_termw ) #define previous_SIGWINCH_handler (S.previous_SIGWINCH_handler) #define cmdedit_x (S.cmdedit_x ) #define cmdedit_y (S.cmdedit_y ) #define cmdedit_prmt_len (S.cmdedit_prmt_len) #define cursor (S.cursor ) #define command_len (S.command_len ) #define command_ps (S.command_ps ) #define cmdedit_prompt (S.cmdedit_prompt ) #define user_buf (S.user_buf ) #define home_pwd_buf (S.home_pwd_buf ) #define matches (S.matches ) #define num_matches (S.num_matches ) #define delptr (S.delptr ) #define newdelflag (S.newdelflag ) #define delbuf (S.delbuf ) #define INIT_S() do { \ (*(struct lineedit_statics**)&lineedit_ptr_to_statics) = xzalloc(sizeof(S)); \ barrier(); \ cmdedit_termw = 80; \ IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \ IF_FEATURE_EDITING_VI(delptr = delbuf;) \ } while (0) static void deinit_S(void) { #if ENABLE_FEATURE_EDITING_FANCY_PROMPT /* This one is allocated only if FANCY_PROMPT is on * (otherwise it points to verbatim prompt (NOT malloced)) */ free((char*)cmdedit_prompt); #endif #if ENABLE_USERNAME_OR_HOMEDIR free(user_buf); if (home_pwd_buf != null_str) free(home_pwd_buf); #endif free(lineedit_ptr_to_statics); } #define DEINIT_S() deinit_S() #if ENABLE_UNICODE_SUPPORT static size_t load_string(const char *src) { if (unicode_status == UNICODE_ON) { ssize_t len = mbstowcs(command_ps, src, S.maxsize - 1); if (len < 0) len = 0; command_ps[len] = BB_NUL; return len; } else { unsigned i = 0; while (src[i] && i < S.maxsize - 1) { command_ps[i] = src[i]; i++; } command_ps[i] = BB_NUL; return i; } } static unsigned save_string(char *dst, unsigned maxsize) { if (unicode_status == UNICODE_ON) { # if !ENABLE_UNICODE_PRESERVE_BROKEN ssize_t len = wcstombs(dst, command_ps, maxsize - 1); if (len < 0) len = 0; dst[len] = '\0'; return len; # else unsigned dstpos = 0; unsigned srcpos = 0; maxsize--; while (dstpos < maxsize) { wchar_t wc; int n = srcpos; /* Convert up to 1st invalid byte (or up to end) */ while ((wc = command_ps[srcpos]) != BB_NUL && !unicode_is_raw_byte(wc) ) { srcpos++; } command_ps[srcpos] = BB_NUL; n = wcstombs(dst + dstpos, command_ps + n, maxsize - dstpos); if (n < 0) /* should not happen */ break; dstpos += n; if (wc == BB_NUL) /* usually is */ break; /* We do have invalid byte here! */ command_ps[srcpos] = wc; /* restore it */ srcpos++; if (dstpos == maxsize) break; dst[dstpos++] = (char) wc; } dst[dstpos] = '\0'; return dstpos; # endif } else { unsigned i = 0; while ((dst[i] = command_ps[i]) != 0) i++; return i; } } /* I thought just fputwc(c, stdout) would work. But no... */ static void BB_PUTCHAR(wchar_t c) { if (unicode_status == UNICODE_ON) { char buf[MB_CUR_MAX + 1]; mbstate_t mbst = { 0 }; ssize_t len = wcrtomb(buf, c, &mbst); if (len > 0) { buf[len] = '\0'; fputs(buf, stdout); } } else { /* In this case, c is always one byte */ putchar(c); } } # if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS static wchar_t adjust_width_and_validate_wc(unsigned *width_adj, wchar_t wc) # else static wchar_t adjust_width_and_validate_wc(wchar_t wc) # define adjust_width_and_validate_wc(width_adj, wc) \ ((*(width_adj))++, adjust_width_and_validate_wc(wc)) # endif { int w = 1; if (unicode_status == UNICODE_ON) { if (wc > CONFIG_LAST_SUPPORTED_WCHAR) { /* note: also true for unicode_is_raw_byte(wc) */ goto subst; } w = wcwidth(wc); if ((ENABLE_UNICODE_COMBINING_WCHARS && w < 0) || (!ENABLE_UNICODE_COMBINING_WCHARS && w <= 0) || (!ENABLE_UNICODE_WIDE_WCHARS && w > 1) ) { subst: w = 1; wc = CONFIG_SUBST_WCHAR; } } # if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS *width_adj += w; #endif return wc; } #else /* !UNICODE */ static size_t load_string(const char *src) { safe_strncpy(command_ps, src, S.maxsize); return strlen(command_ps); } # if ENABLE_FEATURE_TAB_COMPLETION static void save_string(char *dst, unsigned maxsize) { safe_strncpy(dst, command_ps, maxsize); } # endif # define BB_PUTCHAR(c) bb_putchar(c) /* Should never be called: */ int adjust_width_and_validate_wc(unsigned *width_adj, int wc); #endif /* Put 'command_ps[cursor]', cursor++. * Advance cursor on screen. If we reached right margin, scroll text up * and remove terminal margin effect by printing 'next_char' */ #define HACK_FOR_WRONG_WIDTH 1 static void put_cur_glyph_and_inc_cursor(void) { CHAR_T c = command_ps[cursor]; unsigned width = 0; int ofs_to_right; if (c == BB_NUL) { /* erase character after end of input string */ c = ' '; } else { /* advance cursor only if we aren't at the end yet */ cursor++; if (unicode_status == UNICODE_ON) { IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;) c = adjust_width_and_validate_wc(&cmdedit_x, c); IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;) } else { cmdedit_x++; } } ofs_to_right = cmdedit_x - cmdedit_termw; if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) { /* c fits on this line */ BB_PUTCHAR(c); } if (ofs_to_right >= 0) { /* we go to the next line */ #if HACK_FOR_WRONG_WIDTH /* This works better if our idea of term width is wrong * and it is actually wider (often happens on serial lines). * Printing CR,LF *forces* cursor to next line. * OTOH if terminal width is correct AND terminal does NOT * have automargin (IOW: it is moving cursor to next line * by itself (which is wrong for VT-10x terminals)), * this will break things: there will be one extra empty line */ puts("\r"); /* + implicit '\n' */ #else /* VT-10x terminals don't wrap cursor to next line when last char * on the line is printed - cursor stays "over" this char. * Need to print _next_ char too (first one to appear on next line) * to make cursor move down to next line. */ /* Works ok only if cmdedit_termw is correct. */ c = command_ps[cursor]; if (c == BB_NUL) c = ' '; BB_PUTCHAR(c); bb_putchar('\b'); #endif cmdedit_y++; if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) { width = 0; } else { /* ofs_to_right > 0 */ /* wide char c didn't fit on prev line */ BB_PUTCHAR(c); } cmdedit_x = width; } } /* Move to end of line (by printing all chars till the end) */ static void put_till_end_and_adv_cursor(void) { while (cursor < command_len) put_cur_glyph_and_inc_cursor(); } /* Go to the next line */ static void goto_new_line(void) { put_till_end_and_adv_cursor(); if (cmdedit_x != 0) bb_putchar('\n'); } static void beep(void) { bb_putchar('\007'); } static void put_prompt(void) { unsigned w; fputs(cmdedit_prompt, stdout); fflush_all(); cursor = 0; w = cmdedit_termw; /* read volatile var once */ cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */ cmdedit_x = cmdedit_prmt_len % w; } /* Move back one character */ /* (optimized for slow terminals) */ static void input_backward(unsigned num) { if (num > cursor) num = cursor; if (num == 0) return; cursor -= num; if ((ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS) && unicode_status == UNICODE_ON ) { /* correct NUM to be equal to _screen_ width */ int n = num; num = 0; while (--n >= 0) adjust_width_and_validate_wc(&num, command_ps[cursor + n]); if (num == 0) return; } if (cmdedit_x >= num) { cmdedit_x -= num; if (num <= 4) { /* This is longer by 5 bytes on x86. * Also gets miscompiled for ARM users * (busybox.net/bugs/view.php?id=2274). * printf(("\b\b\b\b" + 4) - num); * return; */ do { bb_putchar('\b'); } while (--num); return; } printf(ESC"[%uD", num); return; } /* Need to go one or more lines up */ if (ENABLE_UNICODE_WIDE_WCHARS) { /* With wide chars, it is hard to "backtrack" * and reliably figure out where to put cursor. * Example (<> is a wide char; # is an ordinary char, _ cursor): * |prompt: <><> | * |<><><><><><> | * |_ | * and user presses left arrow. num = 1, cmdedit_x = 0, * We need to go up one line, and then - how do we know that * we need to go *10* positions to the right? Because * |prompt: <>#<>| * |<><><>#<><><>| * |_ | * in this situation we need to go *11* positions to the right. * * A simpler thing to do is to redraw everything from the start * up to new cursor position (which is already known): */ unsigned sv_cursor; /* go to 1st column; go up to first line */ printf("\r" ESC"[%uA", cmdedit_y); cmdedit_y = 0; sv_cursor = cursor; put_prompt(); /* sets cursor to 0 */ while (cursor < sv_cursor) put_cur_glyph_and_inc_cursor(); } else { int lines_up; unsigned width; /* num = chars to go back from the beginning of current line: */ num -= cmdedit_x; width = cmdedit_termw; /* read volatile var once */ /* num=1...w: one line up, w+1...2w: two, etc: */ lines_up = 1 + (num - 1) / width; cmdedit_x = (width * cmdedit_y - num) % width; cmdedit_y -= lines_up; /* go to 1st column; go up */ printf("\r" ESC"[%uA", lines_up); /* go to correct column. * xterm, konsole, Linux VT interpret 0 as 1 below! wow. * need to *make sure* we skip it if cmdedit_x == 0 */ if (cmdedit_x) printf(ESC"[%uC", cmdedit_x); } } /* draw prompt, editor line, and clear tail */ static void redraw(int y, int back_cursor) { if (y > 0) /* up y lines */ printf(ESC"[%uA", y); bb_putchar('\r'); put_prompt(); put_till_end_and_adv_cursor(); printf(SEQ_CLEAR_TILL_END_OF_SCREEN); input_backward(back_cursor); } /* Delete the char in front of the cursor, optionally saving it * for later putback */ #if !ENABLE_FEATURE_EDITING_VI static void input_delete(void) #define input_delete(save) input_delete() #else static void input_delete(int save) #endif { int j = cursor; if (j == (int)command_len) return; #if ENABLE_FEATURE_EDITING_VI if (save) { if (newdelflag) { delptr = delbuf; newdelflag = 0; } if ((delptr - delbuf) < DELBUFSIZ) *delptr++ = command_ps[j]; } #endif memmove(command_ps + j, command_ps + j + 1, /* (command_len + 1 [because of NUL]) - (j + 1) * simplified into (command_len - j) */ (command_len - j) * sizeof(command_ps[0])); command_len--; put_till_end_and_adv_cursor(); /* Last char is still visible, erase it (and more) */ printf(SEQ_CLEAR_TILL_END_OF_SCREEN); input_backward(cursor - j); /* back to old pos cursor */ } #if ENABLE_FEATURE_EDITING_VI static void put(void) { int ocursor; int j = delptr - delbuf; if (j == 0) return; ocursor = cursor; /* open hole and then fill it */ memmove(command_ps + cursor + j, command_ps + cursor, (command_len - cursor + 1) * sizeof(command_ps[0])); memcpy(command_ps + cursor, delbuf, j * sizeof(command_ps[0])); command_len += j; put_till_end_and_adv_cursor(); input_backward(cursor - ocursor - j + 1); /* at end of new text */ } #endif /* Delete the char in back of the cursor */ static void input_backspace(void) { if (cursor > 0) { input_backward(1); input_delete(0); } } /* Move forward one character */ static void input_forward(void) { if (cursor < command_len) put_cur_glyph_and_inc_cursor(); } #if ENABLE_FEATURE_TAB_COMPLETION //FIXME: //needs to be more clever: currently it thinks that "foo\ b //matches the file named "foo bar", which is untrue. //Also, perhaps "foo b needs to complete to "foo bar" , //not "foo bar ... static void free_tab_completion_data(void) { if (matches) { while (num_matches) free(matches[--num_matches]); free(matches); matches = NULL; } } static void add_match(char *matched) { matches = xrealloc_vector(matches, 4, num_matches); matches[num_matches] = matched; num_matches++; } # if ENABLE_FEATURE_USERNAME_COMPLETION /* Replace "~user/..." with "/homedir/...". * The parameter is malloced, free it or return it * unchanged if no user is matched. */ static char *username_path_completion(char *ud) { struct passwd *entry; char *tilde_name = ud; char *home = NULL; ud++; /* skip ~ */ if (*ud == '/') { /* "~/..." */ home = home_pwd_buf; } else { /* "~user/..." */ ud = strchr(ud, '/'); *ud = '\0'; /* "~user" */ entry = getpwnam(tilde_name + 1); *ud = '/'; /* restore "~user/..." */ if (entry) home = entry->pw_dir; } if (home) { ud = concat_path_file(home, ud); free(tilde_name); tilde_name = ud; } return tilde_name; } /* ~use - find all users with this prefix. * Return the length of the prefix used for matching. */ static NOINLINE unsigned complete_username(const char *ud) { /* Using _r function to avoid pulling in static buffers */ char line_buff[256]; struct passwd pwd; struct passwd *result; unsigned userlen; ud++; /* skip ~ */ userlen = strlen(ud); setpwent(); while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) { /* Null usernames should result in all users as possible completions. */ if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) { add_match(xasprintf("~%s/", pwd.pw_name)); } } endpwent(); return 1 + userlen; } # endif /* FEATURE_USERNAME_COMPLETION */ enum { FIND_EXE_ONLY = 0, FIND_DIR_ONLY = 1, FIND_FILE_ONLY = 2, }; static int path_parse(char ***p) { int npth; const char *pth; char *tmp; char **res; if (state->flags & WITH_PATH_LOOKUP) pth = state->path_lookup; else pth = getenv("PATH"); /* PATH="" or PATH=":"? */ if (!pth || !pth[0] || LONE_CHAR(pth, ':')) return 1; tmp = (char*)pth; npth = 1; /* path component count */ while (1) { tmp = strchr(tmp, ':'); if (!tmp) break; tmp++; if (*tmp == '\0') break; /* : */ npth++; } *p = res = xmalloc(npth * sizeof(res[0])); res[0] = tmp = xstrdup(pth); npth = 1; while (1) { tmp = strchr(tmp, ':'); if (!tmp) break; *tmp++ = '\0'; /* ':' -> '\0' */ if (*tmp == '\0') break; /* : */ res[npth++] = tmp; } return npth; } /* Complete command, directory or file name. * Return the length of the prefix used for matching. */ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) { char *path1[1]; char **paths = path1; int npaths; int i; unsigned pf_len; const char *pfind; char *dirbuf = NULL; npaths = 1; path1[0] = (char*)"."; pfind = strrchr(command, '/'); if (!pfind) { if (type == FIND_EXE_ONLY) npaths = path_parse(&paths); pfind = command; } else { /* point to 'l' in "..../last_component" */ pfind++; /* dirbuf = ".../.../.../" */ dirbuf = xstrndup(command, pfind - command); # if ENABLE_FEATURE_USERNAME_COMPLETION if (dirbuf[0] == '~') /* ~/... or ~user/... */ dirbuf = username_path_completion(dirbuf); # endif path1[0] = dirbuf; } pf_len = strlen(pfind); for (i = 0; i < npaths; i++) { DIR *dir; struct dirent *next; struct stat st; char *found; dir = opendir(paths[i]); if (!dir) continue; /* don't print an error */ while ((next = readdir(dir)) != NULL) { unsigned len; const char *name_found = next->d_name; /* .../: bash 3.2.0 shows dotfiles, but not . and .. */ if (!pfind[0] && DOT_OR_DOTDOT(name_found)) continue; /* match? */ if (strncmp(name_found, pfind, pf_len) != 0) continue; /* no */ found = concat_path_file(paths[i], name_found); /* NB: stat() first so that we see is it a directory; * but if that fails, use lstat() so that * we still match dangling links */ if (stat(found, &st) && lstat(found, &st)) goto cont; /* hmm, remove in progress? */ /* Save only name */ len = strlen(name_found); found = xrealloc(found, len + 2); /* +2: for slash and NUL */ strcpy(found, name_found); if (S_ISDIR(st.st_mode)) { /* name is a directory, add slash */ found[len] = '/'; found[len + 1] = '\0'; } else { /* skip files if looking for dirs only (example: cd) */ if (type == FIND_DIR_ONLY) goto cont; } /* add it to the list */ add_match(found); continue; cont: free(found); } closedir(dir); } /* for every path */ if (paths != path1) { free(paths[0]); /* allocated memory is only in first member */ free(paths); } free(dirbuf); return pf_len; } /* build_match_prefix: * On entry, match_buf contains everything up to cursor at the moment * was pressed. This function looks at it, figures out what part of it * constitutes the command/file/directory prefix to use for completion, * and rewrites match_buf to contain only that part. */ #define dbg_bmp 0 /* Helpers: */ /* QUOT is used on elements of int_buf[], which are bytes, * not Unicode chars. Therefore it works correctly even in Unicode mode. */ #define QUOT (UCHAR_MAX+1) static void remove_chunk(int16_t *int_buf, int beg, int end) { /* beg must be <= end */ if (beg == end) return; while ((int_buf[beg] = int_buf[end]) != 0) beg++, end++; if (dbg_bmp) { int i; for (i = 0; int_buf[i]; i++) bb_putchar((unsigned char)int_buf[i]); bb_putchar('\n'); } } /* Caller ensures that match_buf points to a malloced buffer * big enough to hold strlen(match_buf)*2 + 2 */ static NOINLINE int build_match_prefix(char *match_buf) { int i, j; int command_mode; int16_t *int_buf = (int16_t*)match_buf; if (dbg_bmp) printf("\n%s\n", match_buf); /* Copy in reverse order, since they overlap */ i = strlen(match_buf); do { int_buf[i] = (unsigned char)match_buf[i]; i--; } while (i >= 0); /* Mark every \c as "quoted c" */ for (i = 0; int_buf[i]; i++) { if (int_buf[i] == '\\') { remove_chunk(int_buf, i, i + 1); int_buf[i] |= QUOT; } } /* Quote-mark "chars" and 'chars', drop delimiters */ { int in_quote = 0; i = 0; while (int_buf[i]) { int cur = int_buf[i]; if (!cur) break; if (cur == '\'' || cur == '"') { if (!in_quote || (cur == in_quote)) { in_quote ^= cur; remove_chunk(int_buf, i, i + 1); continue; } } if (in_quote) int_buf[i] = cur | QUOT; i++; } } /* Remove everything up to command delimiters: * ';' ';;' '&' '|' '&&' '||', * but careful with '>&' '<&' '>|' */ for (i = 0; int_buf[i]; i++) { int cur = int_buf[i]; if (cur == ';' || cur == '&' || cur == '|') { int prev = i ? int_buf[i - 1] : 0; if (cur == '&' && (prev == '>' || prev == '<')) { continue; } else if (cur == '|' && prev == '>') { continue; } remove_chunk(int_buf, 0, i + 1 + (cur == int_buf[i + 1])); i = -1; /* back to square 1 */ } } /* Remove all `cmd` */ for (i = 0; int_buf[i]; i++) { if (int_buf[i] == '`') { for (j = i + 1; int_buf[j]; j++) { if (int_buf[j] == '`') { /* `cmd` should count as a word: * `cmd` c should search for files c*, * not commands c*. Therefore we don't drop * `cmd` entirely, we replace it with single `. */ remove_chunk(int_buf, i, j); goto next; } } /* No closing ` - command mode, remove all up to ` */ remove_chunk(int_buf, 0, i + 1); break; next: ; } } /* Remove "cmd (" and "cmd {" * Example: "if { c" * In this example, c should be matched as command pfx. */ for (i = 0; int_buf[i]; i++) { if (int_buf[i] == '(' || int_buf[i] == '{') { remove_chunk(int_buf, 0, i + 1); i = -1; /* back to square 1 */ } } /* Remove leading unquoted spaces */ for (i = 0; int_buf[i]; i++) if (int_buf[i] != ' ') break; remove_chunk(int_buf, 0, i); /* Determine completion mode */ command_mode = FIND_EXE_ONLY; for (i = 0; int_buf[i]; i++) { if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') { if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY && (char)int_buf[0] == 'c' && (char)int_buf[1] == 'd' && i == 2 /* -> int_buf[2] == ' ' */ ) { command_mode = FIND_DIR_ONLY; } else { command_mode = FIND_FILE_ONLY; break; } } } if (dbg_bmp) printf("command_mode(0:exe/1:dir/2:file):%d\n", command_mode); /* Remove everything except last word */ for (i = 0; int_buf[i]; i++) /* quasi-strlen(int_buf) */ continue; for (--i; i >= 0; i--) { int cur = int_buf[i]; if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&') { remove_chunk(int_buf, 0, i + 1); break; } } /* Convert back to string of _chars_ */ i = 0; while ((match_buf[i] = int_buf[i]) != '\0') i++; if (dbg_bmp) printf("final match_buf:'%s'\n", match_buf); return command_mode; } /* * Display by column (original idea from ls applet, * very optimized by me [Vladimir] :) */ static void showfiles(void) { int ncols, row; int column_width = 0; int nfiles = num_matches; int nrows = nfiles; int l; /* find the longest file name - use that as the column width */ for (row = 0; row < nrows; row++) { l = unicode_strwidth(matches[row]); if (column_width < l) column_width = l; } column_width += 2; /* min space for columns */ ncols = cmdedit_termw / column_width; if (ncols > 1) { nrows /= ncols; if (nfiles % ncols) nrows++; /* round up fractionals */ } else { ncols = 1; } for (row = 0; row < nrows; row++) { int n = row; int nc; for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) { printf("%s%-*s", matches[n], (int)(column_width - unicode_strwidth(matches[n])), "" ); } if (ENABLE_UNICODE_SUPPORT) puts(printable_string(NULL, matches[n])); else puts(matches[n]); } } static const char *is_special_char(char c) { return strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", c); } static char *quote_special_chars(char *found) { int l = 0; char *s = xzalloc((strlen(found) + 1) * 2); while (*found) { if (is_special_char(*found)) s[l++] = '\\'; s[l++] = *found++; } /* s[l] = '\0'; - already is */ return s; } /* Do TAB completion */ static NOINLINE void input_tab(smallint *lastWasTab) { char *chosen_match; char *match_buf; size_t len_found; /* Length of string used for matching */ unsigned match_pfx_len = match_pfx_len; int find_type; # if ENABLE_UNICODE_SUPPORT /* cursor pos in command converted to multibyte form */ int cursor_mb; # endif if (!(state->flags & TAB_COMPLETION)) return; if (*lastWasTab) { /* The last char was a TAB too. * Print a list of all the available choices. */ if (num_matches > 0) { /* cursor will be changed by goto_new_line() */ int sav_cursor = cursor; goto_new_line(); showfiles(); redraw(0, command_len - sav_cursor); } return; } *lastWasTab = 1; chosen_match = NULL; /* Make a local copy of the string up to the position of the cursor. * build_match_prefix will expand it into int16_t's, need to allocate * twice as much as the string_len+1. * (we then also (ab)use this extra space later - see (**)) */ match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t)); # if !ENABLE_UNICODE_SUPPORT save_string(match_buf, cursor + 1); /* +1 for NUL */ # else { CHAR_T wc = command_ps[cursor]; command_ps[cursor] = BB_NUL; save_string(match_buf, MAX_LINELEN); command_ps[cursor] = wc; cursor_mb = strlen(match_buf); } # endif find_type = build_match_prefix(match_buf); /* Free up any memory already allocated */ free_tab_completion_data(); # if ENABLE_FEATURE_USERNAME_COMPLETION /* If the word starts with ~ and there is no slash in the word, * then try completing this word as a username. */ if (state->flags & USERNAME_COMPLETION) if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL) match_pfx_len = complete_username(match_buf); # endif /* If complete_username() did not match, * try to match a command in $PATH, or a directory, or a file */ if (!matches) match_pfx_len = complete_cmd_dir_file(match_buf, find_type); /* Account for backslashes which will be inserted * by quote_special_chars() later */ { const char *e = match_buf + strlen(match_buf); const char *s = e - match_pfx_len; while (s < e) if (is_special_char(*s++)) match_pfx_len++; } /* Remove duplicates */ if (matches) { unsigned i, n = 0; qsort_string_vector(matches, num_matches); for (i = 0; i < num_matches - 1; ++i) { //if (matches[i] && matches[i+1]) { /* paranoia */ if (strcmp(matches[i], matches[i+1]) == 0) { free(matches[i]); //matches[i] = NULL; /* paranoia */ } else { matches[n++] = matches[i]; } //} } matches[n++] = matches[i]; num_matches = n; } /* Did we find exactly one match? */ if (num_matches != 1) { /* no */ char *cp; beep(); if (!matches) goto ret; /* no matches at all */ /* Find common prefix */ chosen_match = xstrdup(matches[0]); for (cp = chosen_match; *cp; cp++) { unsigned n; for (n = 1; n < num_matches; n++) { if (matches[n][cp - chosen_match] != *cp) { goto stop; } } } stop: if (cp == chosen_match) { /* have unique prefix? */ goto ret; /* no */ } *cp = '\0'; cp = quote_special_chars(chosen_match); free(chosen_match); chosen_match = cp; len_found = strlen(chosen_match); } else { /* exactly one match */ /* Next is not a double-tab */ *lastWasTab = 0; chosen_match = quote_special_chars(matches[0]); len_found = strlen(chosen_match); if (chosen_match[len_found-1] != '/') { chosen_match[len_found] = ' '; chosen_match[++len_found] = '\0'; } } # if !ENABLE_UNICODE_SUPPORT /* Have space to place the match? */ /* The result consists of three parts with these lengths: */ /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */ /* it simplifies into: */ if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) { int pos; /* save tail */ strcpy(match_buf, &command_ps[cursor]); /* add match and tail */ sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf); command_len = strlen(command_ps); /* new pos */ pos = cursor + len_found - match_pfx_len; /* write out the matched command */ redraw(cmdedit_y, command_len - pos); } # else { /* Use 2nd half of match_buf as scratch space - see (**) */ char *command = match_buf + MAX_LINELEN; int len = save_string(command, MAX_LINELEN); /* Have space to place the match? */ /* cursor_mb + (len_found - match_pfx_len) + (len - cursor_mb) */ if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) { int pos; /* save tail */ strcpy(match_buf, &command[cursor_mb]); /* where do we want to have cursor after all? */ strcpy(&command[cursor_mb], chosen_match + match_pfx_len); len = load_string(command); /* add match and tail */ sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, match_buf); command_len = load_string(command); /* write out the matched command */ /* paranoia: load_string can return 0 on conv error, * prevent passing pos = (0 - 12) to redraw */ pos = command_len - len; redraw(cmdedit_y, pos >= 0 ? pos : 0); } } # endif ret: free(chosen_match); free(match_buf); } #endif /* FEATURE_TAB_COMPLETION */ line_input_t* FAST_FUNC new_line_input_t(int flags) { line_input_t *n = xzalloc(sizeof(*n)); n->flags = flags; #if MAX_HISTORY > 0 n->max_history = MAX_HISTORY; #endif return n; } #if MAX_HISTORY > 0 unsigned FAST_FUNC size_from_HISTFILESIZE(const char *hp) { int size = MAX_HISTORY; if (hp) { size = atoi(hp); if (size <= 0) return 1; if (size > MAX_HISTORY) return MAX_HISTORY; } return size; } static void save_command_ps_at_cur_history(void) { if (command_ps[0] != BB_NUL) { int cur = state->cur_history; free(state->history[cur]); # if ENABLE_UNICODE_SUPPORT { char tbuf[MAX_LINELEN]; save_string(tbuf, sizeof(tbuf)); state->history[cur] = xstrdup(tbuf); } # else state->history[cur] = xstrdup(command_ps); # endif } } /* state->flags is already checked to be nonzero */ static int get_previous_history(void) { if ((state->flags & DO_HISTORY) && state->cur_history) { save_command_ps_at_cur_history(); state->cur_history--; return 1; } beep(); return 0; } static int get_next_history(void) { if (state->flags & DO_HISTORY) { if (state->cur_history < state->cnt_history) { save_command_ps_at_cur_history(); /* save the current history line */ return ++state->cur_history; } } beep(); return 0; } /* Lists command history. Used by shell 'history' builtins */ void FAST_FUNC show_history(const line_input_t *st) { int i; if (!st) return; for (i = 0; i < st->cnt_history; i++) printf("%4d %s\n", i, st->history[i]); } # if ENABLE_FEATURE_EDITING_SAVEHISTORY /* We try to ensure that concurrent additions to the history * do not overwrite each other. * Otherwise shell users get unhappy. * * History file is trimmed lazily, when it grows several times longer * than configured MAX_HISTORY lines. */ static void free_line_input_t(line_input_t *n) { int i = n->cnt_history; while (i > 0) free(n->history[--i]); free(n); } /* state->flags is already checked to be nonzero */ static void load_history(line_input_t *st_parm) { char *temp_h[MAX_HISTORY]; char *line; FILE *fp; unsigned idx, i, line_len; /* NB: do not trash old history if file can't be opened */ fp = fopen_for_read(st_parm->hist_file); if (fp) { /* clean up old history */ for (idx = st_parm->cnt_history; idx > 0;) { idx--; free(st_parm->history[idx]); st_parm->history[idx] = NULL; } /* fill temp_h[], retaining only last MAX_HISTORY lines */ memset(temp_h, 0, sizeof(temp_h)); idx = 0; st_parm->cnt_history_in_file = 0; while ((line = xmalloc_fgetline(fp)) != NULL) { if (line[0] == '\0') { free(line); continue; } free(temp_h[idx]); temp_h[idx] = line; st_parm->cnt_history_in_file++; idx++; if (idx == st_parm->max_history) idx = 0; } fclose(fp); /* find first non-NULL temp_h[], if any */ if (st_parm->cnt_history_in_file) { while (temp_h[idx] == NULL) { idx++; if (idx == st_parm->max_history) idx = 0; } } /* copy temp_h[] to st_parm->history[] */ for (i = 0; i < st_parm->max_history;) { line = temp_h[idx]; if (!line) break; idx++; if (idx == st_parm->max_history) idx = 0; line_len = strlen(line); if (line_len >= MAX_LINELEN) line[MAX_LINELEN-1] = '\0'; st_parm->history[i++] = line; } st_parm->cnt_history = i; if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT) st_parm->cnt_history_in_file = i; } } # if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT void save_history(line_input_t *st) { FILE *fp; if (!st->hist_file) return; if (st->cnt_history <= st->cnt_history_in_file) return; fp = fopen(st->hist_file, "a"); if (fp) { int i, fd; char *new_name; line_input_t *st_temp; for (i = st->cnt_history_in_file; i < st->cnt_history; i++) fprintf(fp, "%s\n", st->history[i]); fclose(fp); /* we may have concurrently written entries from others. * load them */ st_temp = new_line_input_t(st->flags); st_temp->hist_file = st->hist_file; st_temp->max_history = st->max_history; load_history(st_temp); /* write out temp file and replace hist_file atomically */ new_name = xasprintf("%s.%u.new", st->hist_file, (int) getpid()); fd = open(new_name, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd >= 0) { fp = xfdopen_for_write(fd); for (i = 0; i < st_temp->cnt_history; i++) fprintf(fp, "%s\n", st_temp->history[i]); fclose(fp); if (rename(new_name, st->hist_file) == 0) st->cnt_history_in_file = st_temp->cnt_history; } free(new_name); free_line_input_t(st_temp); } } # else static void save_history(char *str) { int fd; int len, len2; if (!state->hist_file) return; fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600); if (fd < 0) return; xlseek(fd, 0, SEEK_END); /* paranoia */ len = strlen(str); str[len] = '\n'; /* we (try to) do atomic write */ len2 = full_write(fd, str, len + 1); str[len] = '\0'; close(fd); if (len2 != len + 1) return; /* "wtf?" */ /* did we write so much that history file needs trimming? */ state->cnt_history_in_file++; if (state->cnt_history_in_file > state->max_history * 4) { char *new_name; line_input_t *st_temp; /* we may have concurrently written entries from others. * load them */ st_temp = new_line_input_t(state->flags); st_temp->hist_file = state->hist_file; st_temp->max_history = state->max_history; load_history(st_temp); /* write out temp file and replace hist_file atomically */ new_name = xasprintf("%s.%u.new", state->hist_file, (int) getpid()); fd = open(new_name, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd >= 0) { FILE *fp; int i; fp = xfdopen_for_write(fd); for (i = 0; i < st_temp->cnt_history; i++) fprintf(fp, "%s\n", st_temp->history[i]); fclose(fp); if (rename(new_name, state->hist_file) == 0) state->cnt_history_in_file = st_temp->cnt_history; } free(new_name); free_line_input_t(st_temp); } } # endif # else # define load_history(a) ((void)0) # define save_history(a) ((void)0) # endif /* FEATURE_COMMAND_SAVEHISTORY */ static void remember_in_history(char *str) { int i; if (!(state->flags & DO_HISTORY)) return; if (str[0] == '\0') return; i = state->cnt_history; /* Don't save dupes */ if (i && strcmp(state->history[i-1], str) == 0) return; free(state->history[state->max_history]); /* redundant, paranoia */ state->history[state->max_history] = NULL; /* redundant, paranoia */ /* If history[] is full, remove the oldest command */ /* we need to keep history[state->max_history] empty, hence >=, not > */ if (i >= state->max_history) { free(state->history[0]); for (i = 0; i < state->max_history-1; i++) state->history[i] = state->history[i+1]; /* i == state->max_history-1 */ # if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT if (state->cnt_history_in_file) state->cnt_history_in_file--; # endif } /* i <= state->max_history-1 */ state->history[i++] = xstrdup(str); /* i <= state->max_history */ state->cur_history = i; state->cnt_history = i; # if ENABLE_FEATURE_EDITING_SAVEHISTORY && !ENABLE_FEATURE_EDITING_SAVE_ON_EXIT save_history(str); # endif } #else /* MAX_HISTORY == 0 */ # define remember_in_history(a) ((void)0) #endif /* MAX_HISTORY */ #if ENABLE_FEATURE_EDITING_VI /* * vi mode implemented 2005 by Paul Fox */ static void vi_Word_motion(int eat) { CHAR_T *command = command_ps; while (cursor < command_len && !BB_isspace(command[cursor])) input_forward(); if (eat) while (cursor < command_len && BB_isspace(command[cursor])) input_forward(); } static void vi_word_motion(int eat) { CHAR_T *command = command_ps; if (BB_isalnum(command[cursor]) || command[cursor] == '_') { while (cursor < command_len && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_') ) { input_forward(); } } else if (BB_ispunct(command[cursor])) { while (cursor < command_len && BB_ispunct(command[cursor+1])) input_forward(); } if (cursor < command_len) input_forward(); if (eat) { while (cursor < command_len && BB_isspace(command[cursor])) input_forward(); } } static void vi_End_motion(void) { CHAR_T *command = command_ps; input_forward(); while (cursor < command_len && BB_isspace(command[cursor])) input_forward(); while (cursor < command_len-1 && !BB_isspace(command[cursor+1])) input_forward(); } static void vi_end_motion(void) { CHAR_T *command = command_ps; if (cursor >= command_len-1) return; input_forward(); while (cursor < command_len-1 && BB_isspace(command[cursor])) input_forward(); if (cursor >= command_len-1) return; if (BB_isalnum(command[cursor]) || command[cursor] == '_') { while (cursor < command_len-1 && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_') ) { input_forward(); } } else if (BB_ispunct(command[cursor])) { while (cursor < command_len-1 && BB_ispunct(command[cursor+1])) input_forward(); } } static void vi_Back_motion(void) { CHAR_T *command = command_ps; while (cursor > 0 && BB_isspace(command[cursor-1])) input_backward(1); while (cursor > 0 && !BB_isspace(command[cursor-1])) input_backward(1); } static void vi_back_motion(void) { CHAR_T *command = command_ps; if (cursor <= 0) return; input_backward(1); while (cursor > 0 && BB_isspace(command[cursor])) input_backward(1); if (cursor <= 0) return; if (BB_isalnum(command[cursor]) || command[cursor] == '_') { while (cursor > 0 && (BB_isalnum(command[cursor-1]) || command[cursor-1] == '_') ) { input_backward(1); } } else if (BB_ispunct(command[cursor])) { while (cursor > 0 && BB_ispunct(command[cursor-1])) input_backward(1); } } #endif /* Modelled after bash 4.0 behavior of Ctrl- */ static void ctrl_left(void) { CHAR_T *command = command_ps; while (1) { CHAR_T c; input_backward(1); if (cursor == 0) break; c = command[cursor]; if (c != ' ' && !BB_ispunct(c)) { /* we reached a "word" delimited by spaces/punct. * go to its beginning */ while (1) { c = command[cursor - 1]; if (c == ' ' || BB_ispunct(c)) break; input_backward(1); if (cursor == 0) break; } break; } } } static void ctrl_right(void) { CHAR_T *command = command_ps; while (1) { CHAR_T c; c = command[cursor]; if (c == BB_NUL) break; if (c != ' ' && !BB_ispunct(c)) { /* we reached a "word" delimited by spaces/punct. * go to its end + 1 */ while (1) { input_forward(); c = command[cursor]; if (c == BB_NUL || c == ' ' || BB_ispunct(c)) break; } break; } input_forward(); } } /* * read_line_input and its helpers */ #if ENABLE_FEATURE_EDITING_ASK_TERMINAL static void ask_terminal(void) { /* Ask terminal where is the cursor now. * lineedit_read_key handles response and corrects * our idea of current cursor position. * Testcase: run "echo -n long_line_long_line_long_line", * then type in a long, wrapping command and try to * delete it using backspace key. * Note: we print it _after_ prompt, because * prompt may contain CR. Example: PS1='\[\r\n\]\w ' */ /* Problem: if there is buffered input on stdin, * the response will be delivered later, * possibly to an unsuspecting application. * Testcase: "sleep 1; busybox ash" + press and hold [Enter]. * Result: * ~/srcdevel/bbox/fix/busybox.t4 # * ~/srcdevel/bbox/fix/busybox.t4 # * ^[[59;34~/srcdevel/bbox/fix/busybox.t4 # <-- garbage * ~/srcdevel/bbox/fix/busybox.t4 # * * Checking for input with poll only makes the race narrower, * I still can trigger it. Strace: * * write(1, "~/srcdevel/bbox/fix/busybox.t4 # ", 33) = 33 * poll([{fd=0, events=POLLIN}], 1, 0) = 0 (Timeout) <-- no input exists * write(1, "\33[6n", 4) = 4 <-- send the ESC sequence, quick! * poll([{fd=0, events=POLLIN}], 1, -1) = 1 ([{fd=0, revents=POLLIN}]) * read(0, "\n", 1) = 1 <-- oh crap, user's input got in first */ struct pollfd pfd; pfd.fd = STDIN_FILENO; pfd.events = POLLIN; if (safe_poll(&pfd, 1, 0) == 0) { S.sent_ESC_br6n = 1; fputs(ESC"[6n", stdout); fflush_all(); /* make terminal see it ASAP! */ } } #else #define ask_terminal() ((void)0) #endif /* Called just once at read_line_input() init time */ #if !ENABLE_FEATURE_EDITING_FANCY_PROMPT static void parse_and_put_prompt(const char *prmt_ptr) { const char *p; cmdedit_prompt = prmt_ptr; p = strrchr(prmt_ptr, '\n'); cmdedit_prmt_len = unicode_strwidth(p ? p+1 : prmt_ptr); put_prompt(); } #else static void parse_and_put_prompt(const char *prmt_ptr) { int prmt_size = 0; char *prmt_mem_ptr = xzalloc(1); # if ENABLE_USERNAME_OR_HOMEDIR char *cwd_buf = NULL; # endif char flg_not_length = '['; char cbuf[2]; /*cmdedit_prmt_len = 0; - already is */ cbuf[1] = '\0'; /* never changes */ while (*prmt_ptr) { char timebuf[sizeof("HH:MM:SS")]; char *free_me = NULL; char *pbuf; char c; pbuf = cbuf; c = *prmt_ptr++; if (c == '\\') { const char *cp; int l; /* * Supported via bb_process_escape_sequence: * \a ASCII bell character (07) * \e ASCII escape character (033) * \n newline * \r carriage return * \\ backslash * \nnn char with octal code nnn * Supported: * \$ if the effective UID is 0, a #, otherwise a $ * \w current working directory, with $HOME abbreviated with a tilde * Note: we do not support $PROMPT_DIRTRIM=n feature * \W basename of the current working directory, with $HOME abbreviated with a tilde * \h hostname up to the first '.' * \H hostname * \u username * \[ begin a sequence of non-printing characters * \] end a sequence of non-printing characters * \T current time in 12-hour HH:MM:SS format * \@ current time in 12-hour am/pm format * \A current time in 24-hour HH:MM format * \t current time in 24-hour HH:MM:SS format * (all of the above work as \A) * Not supported: * \! history number of this command * \# command number of this command * \j number of jobs currently managed by the shell * \l basename of the shell's terminal device name * \s name of the shell, the basename of $0 (the portion following the final slash) * \V release of bash, version + patch level (e.g., 2.00.0) * \d date in "Weekday Month Date" format (e.g., "Tue May 26") * \D{format} * format is passed to strftime(3). * An empty format results in a locale-specific time representation. * The braces are required. * Mishandled by bb_process_escape_sequence: * \v version of bash (e.g., 2.00) */ cp = prmt_ptr; c = *cp; if (c != 't') /* don't treat \t as tab */ c = bb_process_escape_sequence(&prmt_ptr); if (prmt_ptr == cp) { if (*cp == '\0') break; c = *prmt_ptr++; switch (c) { # if ENABLE_USERNAME_OR_HOMEDIR case 'u': pbuf = user_buf ? user_buf : (char*)""; break; # endif case 'H': case 'h': pbuf = free_me = safe_gethostname(); if (c == 'h') strchrnul(pbuf, '.')[0] = '\0'; break; case '$': c = (geteuid() == 0 ? '#' : '$'); break; case 'T': /* 12-hour HH:MM:SS format */ case '@': /* 12-hour am/pm format */ case 'A': /* 24-hour HH:MM format */ case 't': /* 24-hour HH:MM:SS format */ /* We show all of them as 24-hour HH:MM */ strftime_HHMMSS(timebuf, sizeof(timebuf), NULL)[-3] = '\0'; pbuf = timebuf; break; # if ENABLE_USERNAME_OR_HOMEDIR case 'w': /* current dir */ case 'W': /* basename of cur dir */ if (!cwd_buf) { cwd_buf = xrealloc_getcwd_or_warn(NULL); if (!cwd_buf) cwd_buf = (char *)bb_msg_unknown; else { /* /home/user[/something] -> ~[/something] */ l = strlen(home_pwd_buf); if (l != 0 && strncmp(home_pwd_buf, cwd_buf, l) == 0 && (cwd_buf[l] == '/' || cwd_buf[l] == '\0') ) { cwd_buf[0] = '~'; overlapping_strcpy(cwd_buf + 1, cwd_buf + l); } } } pbuf = cwd_buf; if (c == 'w') break; cp = strrchr(pbuf, '/'); if (cp) pbuf = (char*)cp + 1; break; # endif // bb_process_escape_sequence does this now: // case 'e': case 'E': /* \e \E = \033 */ // c = '\033'; // break; case 'x': case 'X': { char buf2[4]; for (l = 0; l < 3;) { unsigned h; buf2[l++] = *prmt_ptr; buf2[l] = '\0'; h = strtoul(buf2, &pbuf, 16); if (h > UCHAR_MAX || (pbuf - buf2) < l) { buf2[--l] = '\0'; break; } prmt_ptr++; } c = (char)strtoul(buf2, NULL, 16); if (c == 0) c = '?'; pbuf = cbuf; break; } case '[': case ']': if (c == flg_not_length) { /* Toggle '['/']' hex 5b/5d */ flg_not_length ^= 6; continue; } break; } /* switch */ } /* if */ } /* if */ cbuf[0] = c; { int n = strlen(pbuf); prmt_size += n; if (c == '\n') cmdedit_prmt_len = 0; else if (flg_not_length != ']') { #if 0 /*ENABLE_UNICODE_SUPPORT*/ /* Won't work, pbuf is one BYTE string here instead of an one Unicode char string. */ /* FIXME */ cmdedit_prmt_len += unicode_strwidth(pbuf); #else cmdedit_prmt_len += n; #endif } } prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_size+1), pbuf); free(free_me); } /* while */ # if ENABLE_USERNAME_OR_HOMEDIR if (cwd_buf != (char *)bb_msg_unknown) free(cwd_buf); # endif cmdedit_prompt = prmt_mem_ptr; put_prompt(); } #endif static void cmdedit_setwidth(unsigned w, int redraw_flg) { cmdedit_termw = w; if (redraw_flg) { /* new y for current cursor */ int new_y = (cursor + cmdedit_prmt_len) / w; /* redraw */ redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor); fflush_all(); } } static void win_changed(int nsig) { int sv_errno = errno; unsigned width; get_terminal_width_height(0, &width, NULL); //FIXME: cmdedit_setwidth() -> redraw() -> printf() -> KABOOM! (we are in signal handler!) cmdedit_setwidth(width, /*redraw_flg:*/ nsig); errno = sv_errno; } static int lineedit_read_key(char *read_key_buffer, int timeout) { int64_t ic; #if ENABLE_UNICODE_SUPPORT char unicode_buf[MB_CUR_MAX + 1]; int unicode_idx = 0; #endif while (1) { /* Wait for input. TIMEOUT = -1 makes read_key wait even * on nonblocking stdin, TIMEOUT = 50 makes sure we won't * insist on full MB_CUR_MAX buffer to declare input like * "\xff\n",pause,"ls\n" invalid and thus won't lose "ls". * * Note: read_key sets errno to 0 on success. */ ic = read_key(STDIN_FILENO, read_key_buffer, timeout); if (errno) { #if ENABLE_UNICODE_SUPPORT if (errno == EAGAIN && unicode_idx != 0) goto pushback; #endif break; } #if ENABLE_FEATURE_EDITING_ASK_TERMINAL if ((int32_t)ic == KEYCODE_CURSOR_POS && S.sent_ESC_br6n ) { S.sent_ESC_br6n = 0; if (cursor == 0) { /* otherwise it may be bogus */ int col = ((ic >> 32) & 0x7fff) - 1; /* * Is col > cmdedit_prmt_len? * If yes (terminal says cursor is farther to the right * of where we think it should be), * the prompt wasn't printed starting at col 1, * there was additional text before it. */ if ((int)(col - cmdedit_prmt_len) > 0) { /* Fix our understanding of current x position */ cmdedit_x += (col - cmdedit_prmt_len); while (cmdedit_x >= cmdedit_termw) { cmdedit_x -= cmdedit_termw; cmdedit_y++; } } } continue; } #endif #if ENABLE_UNICODE_SUPPORT if (unicode_status == UNICODE_ON) { wchar_t wc; if ((int32_t)ic < 0) /* KEYCODE_xxx */ break; // TODO: imagine sequence like: 0xff,: we are currently losing 0xff... unicode_buf[unicode_idx++] = ic; unicode_buf[unicode_idx] = '\0'; if (mbstowcs(&wc, unicode_buf, 1) != 1) { /* Not (yet?) a valid unicode char */ if (unicode_idx < MB_CUR_MAX) { timeout = 50; continue; } pushback: /* Invalid sequence. Save all "bad bytes" except first */ read_key_ungets(read_key_buffer, unicode_buf + 1, unicode_idx - 1); # if !ENABLE_UNICODE_PRESERVE_BROKEN ic = CONFIG_SUBST_WCHAR; # else ic = unicode_mark_raw_byte(unicode_buf[0]); # endif } else { /* Valid unicode char, return its code */ ic = wc; } } #endif break; } return ic; } #if ENABLE_UNICODE_BIDI_SUPPORT static int isrtl_str(void) { int idx = cursor; while (idx < command_len && unicode_bidi_is_neutral_wchar(command_ps[idx])) idx++; return unicode_bidi_isrtl(command_ps[idx]); } #else # define isrtl_str() 0 #endif /* leave out the "vi-mode"-only case labels if vi editing isn't * configured. */ #define vi_case(caselabel) IF_FEATURE_EDITING_VI(case caselabel) /* convert uppercase ascii to equivalent control char, for readability */ #undef CTRL #define CTRL(a) ((a) & ~0x40) enum { VI_CMDMODE_BIT = 0x40000000, /* 0x80000000 bit flags KEYCODE_xxx */ }; #if ENABLE_FEATURE_REVERSE_SEARCH /* Mimic readline Ctrl-R reverse history search. * When invoked, it shows the following prompt: * (reverse-i-search)'': user_input [cursor pos unchanged by Ctrl-R] * and typing results in search being performed: * (reverse-i-search)'tmp': cd /tmp [cursor under t in /tmp] * Search is performed by looking at progressively older lines in history. * Ctrl-R again searches for the next match in history. * Backspace deletes last matched char. * Control keys exit search and return to normal editing (at current history line). */ static int32_t reverse_i_search(void) { char match_buf[128]; /* for user input */ char read_key_buffer[KEYCODE_BUFFER_SIZE]; const char *matched_history_line; const char *saved_prompt; unsigned saved_prmt_len; int32_t ic; matched_history_line = NULL; read_key_buffer[0] = 0; match_buf[0] = '\0'; /* Save and replace the prompt */ saved_prompt = cmdedit_prompt; saved_prmt_len = cmdedit_prmt_len; goto set_prompt; while (1) { int h; unsigned match_buf_len = strlen(match_buf); fflush_all(); //FIXME: correct timeout? ic = lineedit_read_key(read_key_buffer, -1); switch (ic) { case CTRL('R'): /* searching for the next match */ break; case '\b': case '\x7f': /* Backspace */ if (unicode_status == UNICODE_ON) { while (match_buf_len != 0) { uint8_t c = match_buf[--match_buf_len]; if ((c & 0xc0) != 0x80) /* start of UTF-8 char? */ break; /* yes */ } } else { if (match_buf_len != 0) match_buf_len--; } match_buf[match_buf_len] = '\0'; break; default: if (ic < ' ' || (!ENABLE_UNICODE_SUPPORT && ic >= 256) || (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT) ) { goto ret; } /* Append this char */ #if ENABLE_UNICODE_SUPPORT if (unicode_status == UNICODE_ON) { mbstate_t mbstate = { 0 }; char buf[MB_CUR_MAX + 1]; int len = wcrtomb(buf, ic, &mbstate); if (len > 0) { buf[len] = '\0'; if (match_buf_len + len < sizeof(match_buf)) strcpy(match_buf + match_buf_len, buf); } } else #endif if (match_buf_len < sizeof(match_buf) - 1) { match_buf[match_buf_len] = ic; match_buf[match_buf_len + 1] = '\0'; } break; } /* switch (ic) */ /* Search in history for match_buf */ h = state->cur_history; if (ic == CTRL('R')) h--; while (h >= 0) { if (state->history[h]) { char *match = strstr(state->history[h], match_buf); if (match) { state->cur_history = h; matched_history_line = state->history[h]; command_len = load_string(matched_history_line); cursor = match - matched_history_line; //FIXME: cursor position for Unicode case free((char*)cmdedit_prompt); set_prompt: cmdedit_prompt = xasprintf("(reverse-i-search)'%s': ", match_buf); cmdedit_prmt_len = unicode_strwidth(cmdedit_prompt); goto do_redraw; } } h--; } /* Not found */ match_buf[match_buf_len] = '\0'; beep(); continue; do_redraw: redraw(cmdedit_y, command_len - cursor); } /* while (1) */ ret: if (matched_history_line) command_len = load_string(matched_history_line); free((char*)cmdedit_prompt); cmdedit_prompt = saved_prompt; cmdedit_prmt_len = saved_prmt_len; redraw(cmdedit_y, command_len - cursor); return ic; } #endif /* maxsize must be >= 2. * Returns: * -1 on read errors or EOF, or on bare Ctrl-D, * 0 on ctrl-C (the line entered is still returned in 'command'), * >0 length of input string, including terminating '\n' */ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize, int timeout) { int len; #if ENABLE_FEATURE_TAB_COMPLETION smallint lastWasTab = 0; #endif smallint break_out = 0; #if ENABLE_FEATURE_EDITING_VI smallint vi_cmdmode = 0; #endif struct termios initial_settings; struct termios new_settings; char read_key_buffer[KEYCODE_BUFFER_SIZE]; INIT_S(); if (tcgetattr(STDIN_FILENO, &initial_settings) < 0 || !(initial_settings.c_lflag & ECHO) ) { /* Happens when e.g. stty -echo was run before */ parse_and_put_prompt(prompt); /* fflush_all(); - done by parse_and_put_prompt */ if (fgets(command, maxsize, stdin) == NULL) len = -1; /* EOF or error */ else len = strlen(command); DEINIT_S(); return len; } init_unicode(); // FIXME: audit & improve this if (maxsize > MAX_LINELEN) maxsize = MAX_LINELEN; S.maxsize = maxsize; /* With zero flags, no other fields are ever used */ state = st ? st : (line_input_t*) &const_int_0; #if MAX_HISTORY > 0 # if ENABLE_FEATURE_EDITING_SAVEHISTORY if (state->hist_file) if (state->cnt_history == 0) load_history(state); # endif if (state->flags & DO_HISTORY) state->cur_history = state->cnt_history; #endif /* prepare before init handlers */ cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */ command_len = 0; #if ENABLE_UNICODE_SUPPORT command_ps = xzalloc(maxsize * sizeof(command_ps[0])); #else command_ps = command; command[0] = '\0'; #endif #define command command_must_not_be_used new_settings = initial_settings; /* ~ICANON: unbuffered input (most c_cc[] are disabled, VMIN/VTIME are enabled) */ /* ~ECHO, ~ECHONL: turn off echoing, including newline echoing */ /* ~ISIG: turn off INTR (ctrl-C), QUIT, SUSP */ new_settings.c_lflag &= ~(ICANON | ECHO | ECHONL | ISIG); /* reads would block only if < 1 char is available */ new_settings.c_cc[VMIN] = 1; /* no timeout (reads block forever) */ new_settings.c_cc[VTIME] = 0; /* Should be not needed if ISIG is off: */ /* Turn off CTRL-C */ /* new_settings.c_cc[VINTR] = _POSIX_VDISABLE; */ tcsetattr_stdin_TCSANOW(&new_settings); #if ENABLE_USERNAME_OR_HOMEDIR { struct passwd *entry; entry = getpwuid(geteuid()); if (entry) { user_buf = xstrdup(entry->pw_name); home_pwd_buf = xstrdup(entry->pw_dir); } } #endif #if 0 for (i = 0; i <= state->max_history; i++) bb_error_msg("history[%d]:'%s'", i, state->history[i]); bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history); #endif /* Print out the command prompt, optionally ask where cursor is */ parse_and_put_prompt(prompt); ask_terminal(); /* Install window resize handler (NB: after *all* init is complete) */ //FIXME: save entire sigaction! previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); win_changed(0); /* get initial window size */ read_key_buffer[0] = 0; while (1) { /* * The emacs and vi modes share much of the code in the big * command loop. Commands entered when in vi's command mode * (aka "escape mode") get an extra bit added to distinguish * them - this keeps them from being self-inserted. This * clutters the big switch a bit, but keeps all the code * in one place. */ int32_t ic, ic_raw; fflush_all(); ic = ic_raw = lineedit_read_key(read_key_buffer, timeout); #if ENABLE_FEATURE_REVERSE_SEARCH again: #endif #if ENABLE_FEATURE_EDITING_VI newdelflag = 1; if (vi_cmdmode) { /* btw, since KEYCODE_xxx are all < 0, this doesn't * change ic if it contains one of them: */ ic |= VI_CMDMODE_BIT; } #endif switch (ic) { case '\n': case '\r': vi_case('\n'|VI_CMDMODE_BIT:) vi_case('\r'|VI_CMDMODE_BIT:) /* Enter */ goto_new_line(); break_out = 1; break; case CTRL('A'): vi_case('0'|VI_CMDMODE_BIT:) /* Control-a -- Beginning of line */ input_backward(cursor); break; case CTRL('B'): vi_case('h'|VI_CMDMODE_BIT:) vi_case('\b'|VI_CMDMODE_BIT:) /* ^H */ vi_case('\x7f'|VI_CMDMODE_BIT:) /* DEL */ input_backward(1); /* Move back one character */ break; case CTRL('E'): vi_case('$'|VI_CMDMODE_BIT:) /* Control-e -- End of line */ put_till_end_and_adv_cursor(); break; case CTRL('F'): vi_case('l'|VI_CMDMODE_BIT:) vi_case(' '|VI_CMDMODE_BIT:) input_forward(); /* Move forward one character */ break; case '\b': /* ^H */ case '\x7f': /* DEL */ if (!isrtl_str()) input_backspace(); else input_delete(0); break; case KEYCODE_DELETE: if (!isrtl_str()) input_delete(0); else input_backspace(); break; #if ENABLE_FEATURE_TAB_COMPLETION case '\t': input_tab(&lastWasTab); break; #endif case CTRL('K'): /* Control-k -- clear to end of line */ command_ps[cursor] = BB_NUL; command_len = cursor; printf(SEQ_CLEAR_TILL_END_OF_SCREEN); break; case CTRL('L'): vi_case(CTRL('L')|VI_CMDMODE_BIT:) /* Control-l -- clear screen */ printf(ESC"[H"); /* cursor to top,left */ redraw(0, command_len - cursor); break; #if MAX_HISTORY > 0 case CTRL('N'): vi_case(CTRL('N')|VI_CMDMODE_BIT:) vi_case('j'|VI_CMDMODE_BIT:) /* Control-n -- Get next command in history */ if (get_next_history()) goto rewrite_line; break; case CTRL('P'): vi_case(CTRL('P')|VI_CMDMODE_BIT:) vi_case('k'|VI_CMDMODE_BIT:) /* Control-p -- Get previous command from history */ if (get_previous_history()) goto rewrite_line; break; #endif case CTRL('U'): vi_case(CTRL('U')|VI_CMDMODE_BIT:) /* Control-U -- Clear line before cursor */ if (cursor) { command_len -= cursor; memmove(command_ps, command_ps + cursor, (command_len + 1) * sizeof(command_ps[0])); redraw(cmdedit_y, command_len); } break; case CTRL('W'): vi_case(CTRL('W')|VI_CMDMODE_BIT:) /* Control-W -- Remove the last word */ while (cursor > 0 && BB_isspace(command_ps[cursor-1])) input_backspace(); while (cursor > 0 && !BB_isspace(command_ps[cursor-1])) input_backspace(); break; #if ENABLE_FEATURE_REVERSE_SEARCH case CTRL('R'): ic = ic_raw = reverse_i_search(); goto again; #endif #if ENABLE_FEATURE_EDITING_VI case 'i'|VI_CMDMODE_BIT: vi_cmdmode = 0; break; case 'I'|VI_CMDMODE_BIT: input_backward(cursor); vi_cmdmode = 0; break; case 'a'|VI_CMDMODE_BIT: input_forward(); vi_cmdmode = 0; break; case 'A'|VI_CMDMODE_BIT: put_till_end_and_adv_cursor(); vi_cmdmode = 0; break; case 'x'|VI_CMDMODE_BIT: input_delete(1); break; case 'X'|VI_CMDMODE_BIT: if (cursor > 0) { input_backward(1); input_delete(1); } break; case 'W'|VI_CMDMODE_BIT: vi_Word_motion(1); break; case 'w'|VI_CMDMODE_BIT: vi_word_motion(1); break; case 'E'|VI_CMDMODE_BIT: vi_End_motion(); break; case 'e'|VI_CMDMODE_BIT: vi_end_motion(); break; case 'B'|VI_CMDMODE_BIT: vi_Back_motion(); break; case 'b'|VI_CMDMODE_BIT: vi_back_motion(); break; case 'C'|VI_CMDMODE_BIT: vi_cmdmode = 0; /* fall through */ case 'D'|VI_CMDMODE_BIT: goto clear_to_eol; case 'c'|VI_CMDMODE_BIT: vi_cmdmode = 0; /* fall through */ case 'd'|VI_CMDMODE_BIT: { int nc, sc; ic = lineedit_read_key(read_key_buffer, timeout); if (errno) /* error */ goto return_error_indicator; if (ic == ic_raw) { /* "cc", "dd" */ input_backward(cursor); goto clear_to_eol; break; } sc = cursor; switch (ic) { case 'w': case 'W': case 'e': case 'E': switch (ic) { case 'w': /* "dw", "cw" */ vi_word_motion(vi_cmdmode); break; case 'W': /* 'dW', 'cW' */ vi_Word_motion(vi_cmdmode); break; case 'e': /* 'de', 'ce' */ vi_end_motion(); input_forward(); break; case 'E': /* 'dE', 'cE' */ vi_End_motion(); input_forward(); break; } nc = cursor; input_backward(cursor - sc); while (nc-- > cursor) input_delete(1); break; case 'b': /* "db", "cb" */ case 'B': /* implemented as B */ if (ic == 'b') vi_back_motion(); else vi_Back_motion(); while (sc-- > cursor) input_delete(1); break; case ' ': /* "d ", "c " */ input_delete(1); break; case '$': /* "d$", "c$" */ clear_to_eol: while (cursor < command_len) input_delete(1); break; } break; } case 'p'|VI_CMDMODE_BIT: input_forward(); /* fallthrough */ case 'P'|VI_CMDMODE_BIT: put(); break; case 'r'|VI_CMDMODE_BIT: //FIXME: unicode case? ic = lineedit_read_key(read_key_buffer, timeout); if (errno) /* error */ goto return_error_indicator; if (ic < ' ' || ic > 255) { beep(); } else { command_ps[cursor] = ic; bb_putchar(ic); bb_putchar('\b'); } break; case '\x1b': /* ESC */ if (state->flags & VI_MODE) { /* insert mode --> command mode */ vi_cmdmode = 1; input_backward(1); } /* Handle a few ESC- combinations the same way * standard readline bindings (IOW: bash) do. * Often, Alt- generates ESC-. */ ic = lineedit_read_key(read_key_buffer, timeout); switch (ic) { //case KEYCODE_LEFT: - bash doesn't do this case 'b': ctrl_left(); break; //case KEYCODE_RIGHT: - bash doesn't do this case 'f': ctrl_right(); break; //case KEYCODE_DELETE: - bash doesn't do this case 'd': /* Alt-D */ { /* Delete word forward */ int nc, sc = cursor; ctrl_right(); nc = cursor - sc; input_backward(nc); while (--nc >= 0) input_delete(1); break; } case '\b': /* Alt-Backspace(?) */ case '\x7f': /* Alt-Backspace(?) */ //case 'w': - bash doesn't do this { /* Delete word backward */ int sc = cursor; ctrl_left(); while (sc-- > cursor) input_delete(1); break; } } break; #endif /* FEATURE_COMMAND_EDITING_VI */ #if MAX_HISTORY > 0 case KEYCODE_UP: if (get_previous_history()) goto rewrite_line; beep(); break; case KEYCODE_DOWN: if (!get_next_history()) break; rewrite_line: /* Rewrite the line with the selected history item */ /* change command */ command_len = load_string(state->history[state->cur_history] ? state->history[state->cur_history] : ""); /* redraw and go to eol (bol, in vi) */ redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0); break; #endif case KEYCODE_RIGHT: input_forward(); break; case KEYCODE_LEFT: input_backward(1); break; case KEYCODE_CTRL_LEFT: case KEYCODE_ALT_LEFT: /* bash doesn't do it */ ctrl_left(); break; case KEYCODE_CTRL_RIGHT: case KEYCODE_ALT_RIGHT: /* bash doesn't do it */ ctrl_right(); break; case KEYCODE_HOME: input_backward(cursor); break; case KEYCODE_END: put_till_end_and_adv_cursor(); break; default: if (initial_settings.c_cc[VINTR] != 0 && ic_raw == initial_settings.c_cc[VINTR] ) { /* Ctrl-C (usually) - stop gathering input */ goto_new_line(); command_len = 0; break_out = -1; /* "do not append '\n'" */ break; } if (initial_settings.c_cc[VEOF] != 0 && ic_raw == initial_settings.c_cc[VEOF] ) { /* Ctrl-D (usually) - delete one character, * or exit if len=0 and no chars to delete */ if (command_len == 0) { errno = 0; case -1: /* error (e.g. EIO when tty is destroyed) */ IF_FEATURE_EDITING_VI(return_error_indicator:) break_out = command_len = -1; break; } input_delete(0); break; } // /* Control-V -- force insert of next char */ // if (c == CTRL('V')) { // if (safe_read(STDIN_FILENO, &c, 1) < 1) // goto return_error_indicator; // if (c == 0) { // beep(); // break; // } // } if (ic < ' ' || (!ENABLE_UNICODE_SUPPORT && ic >= 256) || (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT) ) { /* If VI_CMDMODE_BIT is set, ic is >= 256 * and vi mode ignores unexpected chars. * Otherwise, we are here if ic is a * control char or an unhandled ESC sequence, * which is also ignored. */ break; } if ((int)command_len >= (maxsize - 2)) { /* Not enough space for the char and EOL */ break; } command_len++; if (cursor == (command_len - 1)) { /* We are at the end, append */ command_ps[cursor] = ic; command_ps[cursor + 1] = BB_NUL; put_cur_glyph_and_inc_cursor(); if (unicode_bidi_isrtl(ic)) input_backward(1); } else { /* In the middle, insert */ int sc = cursor; memmove(command_ps + sc + 1, command_ps + sc, (command_len - sc) * sizeof(command_ps[0])); command_ps[sc] = ic; /* is right-to-left char, or neutral one (e.g. comma) was just added to rtl text? */ if (!isrtl_str()) sc++; /* no */ put_till_end_and_adv_cursor(); /* to prev x pos + 1 */ input_backward(cursor - sc); } break; } /* switch (ic) */ if (break_out) break; #if ENABLE_FEATURE_TAB_COMPLETION if (ic_raw != '\t') lastWasTab = 0; #endif } /* while (1) */ #if ENABLE_FEATURE_EDITING_ASK_TERMINAL if (S.sent_ESC_br6n) { /* "sleep 1; busybox ash" + hold [Enter] to trigger. * We sent "ESC [ 6 n", but got '\n' first, and * KEYCODE_CURSOR_POS response is now buffered from terminal. * It's bad already and not much can be done with it * (it _will_ be visible for the next process to read stdin), * but without this delay it even shows up on the screen * as garbage because we restore echo settings with tcsetattr * before it comes in. UGLY! */ usleep(20*1000); } #endif /* End of bug-catching "command_must_not_be_used" trick */ #undef command #if ENABLE_UNICODE_SUPPORT command[0] = '\0'; if (command_len > 0) command_len = save_string(command, maxsize - 1); free(command_ps); #endif if (command_len > 0) { remember_in_history(command); } if (break_out > 0) { command[command_len++] = '\n'; command[command_len] = '\0'; } #if ENABLE_FEATURE_TAB_COMPLETION free_tab_completion_data(); #endif /* restore initial_settings */ tcsetattr_stdin_TCSANOW(&initial_settings); /* restore SIGWINCH handler */ signal(SIGWINCH, previous_SIGWINCH_handler); fflush_all(); len = command_len; DEINIT_S(); return len; /* can't return command_len, DEINIT_S() destroys it */ } #else /* !FEATURE_EDITING */ #undef read_line_input int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize) { fputs(prompt, stdout); fflush_all(); if (!fgets(command, maxsize, stdin)) return -1; return strlen(command); } #endif /* !FEATURE_EDITING */ /* * Testing */ #ifdef TEST #include const char *applet_name = "debug stuff usage"; int main(int argc, char **argv) { char buff[MAX_LINELEN]; char *prompt = #if ENABLE_FEATURE_EDITING_FANCY_PROMPT "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:" "\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] " "\\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]"; #else "% "; #endif while (1) { int l; l = read_line_input(prompt, buff); if (l <= 0 || buff[l-1] != '\n') break; buff[l-1] = '\0'; printf("*** read_line_input() returned line =%s=\n", buff); } printf("*** read_line_input() detect ^D\n"); return 0; } #endif /* TEST */ busybox-1.22.1/libbb/copy_file.c0000644000000000000000000002640212263563520015165 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini copy_file implementation for busybox * * Copyright (C) 2001 by Matt Kraai * SELinux support by Yuichi Nakamura * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" // FEATURE_NON_POSIX_CP: // // POSIX: if exists and -i, ask (w/o -i assume yes). // Then open w/o EXCL (yes, not unlink!). // If open still fails and -f, try unlink, then try open again. // Result: a mess: // If dest is a (sym)link, we overwrite link 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... /* Called if open of destination, link creation etc fails. * errno must be set to relevant value ("why we cannot create dest?") * to give reasonable error message */ static int ask_and_unlink(const char *dest, int flags) { int e = errno; #if !ENABLE_FEATURE_NON_POSIX_CP if (!(flags & (FILEUTILS_FORCE|FILEUTILS_INTERACTIVE))) { /* Either it exists, or the *path* doesnt exist */ bb_perror_msg("can't create '%s'", dest); return -1; } #endif // else: act as if -f is always in effect. // We don't want "can't create" msg, we want unlink to be done // (silently unless -i). Why? POSIX cp usually succeeds with // O_TRUNC open of existing file, and user is left ignorantly happy. // With above block unconditionally enabled, non-POSIX cp // will complain a lot more than POSIX one. /* TODO: maybe we should do it only if ctty is present? */ if (flags & FILEUTILS_INTERACTIVE) { // We would not do POSIX insanity. -i asks, // then _unlinks_ the offender. Presto. // (No "opening without O_EXCL", no "unlink only if -f") // Or else we will end up having 3 open()s! fprintf(stderr, "%s: overwrite '%s'? ", applet_name, dest); if (!bb_ask_confirmation()) return 0; /* not allowed to overwrite */ } if (unlink(dest) < 0) { #if ENABLE_FEATURE_VERBOSE_CP_MESSAGE if (e == errno && e == ENOENT) { /* e == ENOTDIR is similar: path has non-dir component, * but in this case we don't even reach copy_file() */ bb_error_msg("can't create '%s': Path does not exist", dest); return -1; /* error */ } #endif errno = e; /* do not use errno from unlink */ bb_perror_msg("can't create '%s'", dest); return -1; /* error */ } return 1; /* ok (to try again) */ } /* Return: * -1 error, copy not made * 0 copy is made or user answered "no" in interactive mode * (failures to preserve mode/owner/times are not reported in exit code) */ int FAST_FUNC copy_file(const char *source, const char *dest, int flags) { /* This is a recursive function, try to minimize stack usage */ /* NB: each struct stat is ~100 bytes */ struct stat source_stat; struct stat dest_stat; smallint retval = 0; smallint dest_exists = 0; smallint ovr; /* Inverse of cp -d ("cp without -d") */ #define FLAGS_DEREF (flags & (FILEUTILS_DEREFERENCE + FILEUTILS_DEREFERENCE_L0)) 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("can't stat '%s'", source); return -1; } if (lstat(dest, &dest_stat) < 0) { if (errno != ENOENT) { bb_perror_msg("can't stat '%s'", dest); return -1; } } else { if (source_stat.st_dev == dest_stat.st_dev && source_stat.st_ino == dest_stat.st_ino ) { bb_error_msg("'%s' and '%s' are the same file", source, dest); return -1; } dest_exists = 1; } #if ENABLE_SELINUX if ((flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) && is_selinux_enabled() > 0) { security_context_t con; if (lgetfilecon(source, &con) >= 0) { if (setfscreatecon(con) < 0) { bb_perror_msg("can't set setfscreatecon %s", con); freecon(con); return -1; } } else if (errno == ENOTSUP || errno == ENODATA) { setfscreatecon_or_die(NULL); } else { bb_perror_msg("can't lgetfilecon %s", source); return -1; } } #endif if (S_ISDIR(source_stat.st_mode)) { DIR *dp; const char *tp; struct dirent *d; mode_t saved_umask = 0; if (!(flags & FILEUTILS_RECUR)) { bb_error_msg("omitting directory '%s'", source); return -1; } /* Did we ever create source ourself before? */ tp = is_in_ino_dev_hashtable(&source_stat); if (tp) { /* We did! it's a recursion! man the lifeboats... */ bb_error_msg("recursion detected, omitting directory '%s'", source); return -1; } if (dest_exists) { if (!S_ISDIR(dest_stat.st_mode)) { bb_error_msg("target '%s' is not a directory", dest); return -1; } /* race here: user can substitute a symlink between * this check and actual creation of files inside dest */ } else { /* Create DEST */ mode_t mode; saved_umask = umask(0); mode = source_stat.st_mode; if (!(flags & FILEUTILS_PRESERVE_STATUS)) mode = source_stat.st_mode & ~saved_umask; /* Allow owner to access new dir (at least for now) */ mode |= S_IRWXU; if (mkdir(dest, mode) < 0) { umask(saved_umask); bb_perror_msg("can't create directory '%s'", dest); return -1; } umask(saved_umask); /* need stat info for add_to_ino_dev_hashtable */ if (lstat(dest, &dest_stat) < 0) { bb_perror_msg("can't stat '%s'", dest); return -1; } } /* remember (dev,inode) of each created dir. * NULL: name is not remembered */ add_to_ino_dev_hashtable(&dest_stat, NULL); /* Recursively copy files in SOURCE */ dp = opendir(source); if (dp == NULL) { retval = -1; goto preserve_mode_ugid_time; } while ((d = readdir(dp)) != NULL) { char *new_source, *new_dest; new_source = concat_subpath_file(source, d->d_name); if (new_source == NULL) continue; new_dest = concat_path_file(dest, d->d_name); if (copy_file(new_source, new_dest, flags & ~FILEUTILS_DEREFERENCE_L0) < 0) retval = -1; free(new_source); free(new_dest); } closedir(dp); if (!dest_exists && chmod(dest, source_stat.st_mode & ~saved_umask) < 0 ) { bb_perror_msg("can't preserve %s of '%s'", "permissions", dest); /* retval = -1; - WRONG! copy *WAS* made */ } goto preserve_mode_ugid_time; } if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) { int (*lf)(const char *oldpath, const char *newpath); make_links: /* Hmm... maybe * if (DEREF && MAKE_SOFTLINK) source = realpath(source) ? * (but realpath returns NULL on dangling symlinks...) */ lf = (flags & FILEUTILS_MAKE_SOFTLINK) ? symlink : link; if (lf(source, dest) < 0) { ovr = ask_and_unlink(dest, flags); if (ovr <= 0) return ovr; if (lf(source, dest) < 0) { bb_perror_msg("can't create link '%s'", dest); return -1; } } /* _Not_ jumping to preserve_mode_ugid_time: * (sym)links don't have those */ return 0; } if (/* "cp thing1 thing2" without -R: just open and read() from thing1 */ !(flags & FILEUTILS_RECUR) /* "cp [-opts] regular_file thing2" */ || S_ISREG(source_stat.st_mode) /* DEREF uses stat, which never returns S_ISLNK() == true. * So the below is never true: */ /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */ ) { int src_fd; int dst_fd; mode_t new_mode; if (!FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) { /* "cp -d symlink dst": create a link */ goto dont_cat; } if (ENABLE_FEATURE_PRESERVE_HARDLINKS && !FLAGS_DEREF) { const char *link_target; link_target = is_in_ino_dev_hashtable(&source_stat); if (link_target) { if (link(link_target, dest) < 0) { ovr = ask_and_unlink(dest, flags); if (ovr <= 0) return ovr; if (link(link_target, dest) < 0) { bb_perror_msg("can't create link '%s'", dest); return -1; } } return 0; } add_to_ino_dev_hashtable(&source_stat, dest); } src_fd = open_or_warn(source, O_RDONLY); if (src_fd < 0) return -1; /* Do not try to open with weird mode fields */ new_mode = source_stat.st_mode; if (!S_ISREG(source_stat.st_mode)) new_mode = 0666; // POSIX way is a security problem versus (sym)link attacks if (!ENABLE_FEATURE_NON_POSIX_CP) { dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); } else { /* safe way: */ dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); } if (dst_fd == -1) { ovr = ask_and_unlink(dest, flags); if (ovr <= 0) { close(src_fd); return ovr; } /* It shouldn't exist. If it exists, do not open (symlink attack?) */ dst_fd = open3_or_warn(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); if (dst_fd < 0) { close(src_fd); return -1; } } #if ENABLE_SELINUX if ((flags & (FILEUTILS_PRESERVE_SECURITY_CONTEXT|FILEUTILS_SET_SECURITY_CONTEXT)) && is_selinux_enabled() > 0 ) { security_context_t con; if (getfscreatecon(&con) == -1) { bb_perror_msg("getfscreatecon"); return -1; } if (con) { if (setfilecon(dest, con) == -1) { bb_perror_msg("setfilecon:%s,%s", dest, con); freecon(con); return -1; } freecon(con); } } #endif if (bb_copyfd_eof(src_fd, dst_fd) == -1) retval = -1; /* Careful with writing... */ if (close(dst_fd) < 0) { bb_perror_msg("error writing to '%s'", dest); retval = -1; } /* ...but read size is already checked by bb_copyfd_eof */ close(src_fd); /* "cp /dev/something new_file" should not * copy mode of /dev/something */ if (!S_ISREG(source_stat.st_mode)) return retval; goto preserve_mode_ugid_time; } dont_cat: /* Source is a symlink or a special file */ /* We are lazy here, a bit lax with races... */ if (dest_exists) { errno = EEXIST; ovr = ask_and_unlink(dest, flags); if (ovr <= 0) return ovr; } if (S_ISLNK(source_stat.st_mode)) { char *lpath = xmalloc_readlink_or_warn(source); if (lpath) { int r = symlink(lpath, dest); free(lpath); if (r < 0) { bb_perror_msg("can't create symlink '%s'", dest); return -1; } if (flags & FILEUTILS_PRESERVE_STATUS) if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0) bb_perror_msg("can't preserve %s of '%s'", "ownership", dest); } /* _Not_ jumping to preserve_mode_ugid_time: * symlinks don't have those */ return 0; } if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) ) { if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) { bb_perror_msg("can't create '%s'", dest); return -1; } } else { bb_error_msg("unrecognized file '%s' with mode %x", source, source_stat.st_mode); return -1; } preserve_mode_ugid_time: if (flags & FILEUTILS_PRESERVE_STATUS /* Cannot happen: */ /* && !(flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) */ ) { struct timeval times[2]; times[1].tv_sec = times[0].tv_sec = source_stat.st_mtime; times[1].tv_usec = times[0].tv_usec = 0; /* BTW, utimes sets usec-precision time - just FYI */ if (utimes(dest, times) < 0) bb_perror_msg("can't 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("can't preserve %s of '%s'", "ownership", dest); } if (chmod(dest, source_stat.st_mode) < 0) bb_perror_msg("can't preserve %s of '%s'", "permissions", dest); } return retval; } busybox-1.22.1/libbb/procps.c0000644000000000000000000004124712263563520014526 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright 1998 by Albert Cahalan; all rights reserved. * Copyright (C) 2002 by Vladimir Oleynik * SELinux support: (c) 2007 by Yuichi Nakamura * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" typedef struct id_to_name_map_t { uid_t id; char name[USERNAME_MAX_SIZE]; } id_to_name_map_t; typedef struct cache_t { id_to_name_map_t *cache; int size; } cache_t; static cache_t username, groupname; static void clear_cache(cache_t *cp) { free(cp->cache); cp->cache = NULL; cp->size = 0; } void FAST_FUNC clear_username_cache(void) { clear_cache(&username); clear_cache(&groupname); } #if 0 /* more generic, but we don't need that yet */ /* Returns -N-1 if not found. */ /* cp->cache[N] is allocated and must be filled in this case */ static int get_cached(cache_t *cp, uid_t id) { int i; for (i = 0; i < cp->size; i++) if (cp->cache[i].id == id) return i; i = cp->size++; cp->cache = xrealloc_vector(cp->cache, 2, i); cp->cache[i++].id = id; return -i; } #endif static char* get_cached(cache_t *cp, uid_t id, char* FAST_FUNC x2x_utoa(uid_t id)) { int i; for (i = 0; i < cp->size; i++) if (cp->cache[i].id == id) return cp->cache[i].name; i = cp->size++; cp->cache = xrealloc_vector(cp->cache, 2, i); cp->cache[i].id = id; /* Never fails. Generates numeric string if name isn't found */ safe_strncpy(cp->cache[i].name, x2x_utoa(id), sizeof(cp->cache[i].name)); return cp->cache[i].name; } const char* FAST_FUNC get_cached_username(uid_t uid) { return get_cached(&username, uid, uid2uname_utoa); } const char* FAST_FUNC get_cached_groupname(gid_t gid) { return get_cached(&groupname, gid, gid2group_utoa); } #define PROCPS_BUFSIZE 1024 static int read_to_buf(const char *filename, void *buf) { int fd; /* open_read_close() would do two reads, checking for EOF. * When you have 10000 /proc/$NUM/stat to read, it isn't desirable */ ssize_t ret = -1; fd = open(filename, O_RDONLY); if (fd >= 0) { ret = read(fd, buf, PROCPS_BUFSIZE-1); close(fd); } ((char *)buf)[ret > 0 ? ret : 0] = '\0'; return ret; } static procps_status_t* FAST_FUNC alloc_procps_scan(void) { unsigned n = getpagesize(); procps_status_t* sp = xzalloc(sizeof(procps_status_t)); sp->dir = xopendir("/proc"); while (1) { n >>= 1; if (!n) break; sp->shift_pages_to_bytes++; } sp->shift_pages_to_kb = sp->shift_pages_to_bytes - 10; return sp; } void FAST_FUNC free_procps_scan(procps_status_t* sp) { closedir(sp->dir); #if ENABLE_FEATURE_SHOW_THREADS if (sp->task_dir) closedir(sp->task_dir); #endif free(sp->argv0); free(sp->exe); IF_SELINUX(free(sp->context);) free(sp); } #if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP static unsigned long fast_strtoul_16(char **endptr) { unsigned char c; char *str = *endptr; unsigned long n = 0; /* Need to stop on both ' ' and '\n' */ while ((c = *str++) > ' ') { c = ((c|0x20) - '0'); if (c > 9) /* c = c + '0' - 'a' + 10: */ c = c - ('a' - '0' - 10); n = n*16 + c; } *endptr = str; /* We skip trailing space! */ return n; } #endif #if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP /* We cut a lot of corners here for speed */ static unsigned long fast_strtoul_10(char **endptr) { unsigned char c; char *str = *endptr; unsigned long n = *str - '0'; /* Need to stop on both ' ' and '\n' */ while ((c = *++str) > ' ') n = n*10 + (c - '0'); *endptr = str + 1; /* We skip trailing space! */ return n; } # if ENABLE_FEATURE_FAST_TOP static long fast_strtol_10(char **endptr) { if (**endptr != '-') return fast_strtoul_10(endptr); (*endptr)++; return - (long)fast_strtoul_10(endptr); } # endif static char *skip_fields(char *str, int count) { do { while (*str++ != ' ') continue; /* we found a space char, str points after it */ } while (--count); return str; } #endif #if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, void (*cb)(struct smaprec *, void *), void *data) { FILE *file; struct smaprec currec; char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; char buf[PROCPS_BUFSIZE]; #if !ENABLE_PMAP void (*cb)(struct smaprec *, void *) = NULL; void *data = NULL; #endif sprintf(filename, "/proc/%u/smaps", (int)pid); file = fopen_for_read(filename); if (!file) return 1; memset(&currec, 0, sizeof(currec)); while (fgets(buf, PROCPS_BUFSIZE, file)) { // Each mapping datum has this form: // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME // Size: nnn kB // Rss: nnn kB // ..... char *tp = buf, *p; #define SCAN(S, X) \ if (strncmp(tp, S, sizeof(S)-1) == 0) { \ tp = skip_whitespace(tp + sizeof(S)-1); \ total->X += currec.X = fast_strtoul_10(&tp); \ continue; \ } if (cb) { SCAN("Pss:" , smap_pss ); SCAN("Swap:" , smap_swap ); } SCAN("Private_Dirty:", private_dirty); SCAN("Private_Clean:", private_clean); SCAN("Shared_Dirty:" , shared_dirty ); SCAN("Shared_Clean:" , shared_clean ); #undef SCAN tp = strchr(buf, '-'); if (tp) { // We reached next mapping - the line of this form: // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME if (cb) { /* If we have a previous record, there's nothing more * for it, call the callback and clear currec */ if (currec.smap_size) cb(&currec, data); free(currec.smap_name); } memset(&currec, 0, sizeof(currec)); *tp = ' '; tp = buf; currec.smap_start = fast_strtoul_16(&tp); currec.smap_size = (fast_strtoul_16(&tp) - currec.smap_start) >> 10; strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1); // skipping "rw-s FILEOFS M:m INODE " tp = skip_whitespace(skip_fields(tp, 4)); // filter out /dev/something (something != zero) if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) { if (currec.smap_mode[1] == 'w') { currec.mapped_rw = currec.smap_size; total->mapped_rw += currec.smap_size; } else if (currec.smap_mode[1] == '-') { currec.mapped_ro = currec.smap_size; total->mapped_ro += currec.smap_size; } } if (strcmp(tp, "[stack]\n") == 0) total->stack += currec.smap_size; if (cb) { p = skip_non_whitespace(tp); if (p == tp) { currec.smap_name = xstrdup(" [ anon ]"); } else { *p = '\0'; currec.smap_name = xstrdup(tp); } } total->smap_size += currec.smap_size; } } fclose(file); if (cb) { if (currec.smap_size) cb(&currec, data); free(currec.smap_name); } return 0; } #endif void BUG_comm_size(void); procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) { if (!sp) sp = alloc_procps_scan(); for (;;) { struct dirent *entry; char buf[PROCPS_BUFSIZE]; long tasknice; unsigned pid; int n; char filename[sizeof("/proc/%u/task/%u/cmdline") + sizeof(int)*3 * 2]; char *filename_tail; #if ENABLE_FEATURE_SHOW_THREADS if (sp->task_dir) { entry = readdir(sp->task_dir); if (entry) goto got_entry; closedir(sp->task_dir); sp->task_dir = NULL; } #endif entry = readdir(sp->dir); if (entry == NULL) { free_procps_scan(sp); return NULL; } IF_FEATURE_SHOW_THREADS(got_entry:) pid = bb_strtou(entry->d_name, NULL, 10); if (errno) continue; #if ENABLE_FEATURE_SHOW_THREADS if ((flags & PSSCAN_TASKS) && !sp->task_dir) { /* We found another /proc/PID. Do not use it, * there will be /proc/PID/task/PID (same PID!), * so just go ahead and dive into /proc/PID/task. */ sprintf(filename, "/proc/%u/task", pid); /* Note: if opendir fails, we just go to next /proc/XXX */ sp->task_dir = opendir(filename); sp->main_thread_pid = pid; continue; } #endif /* After this point we can: * "break": stop parsing, return the data * "continue": try next /proc/XXX */ memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz)); sp->pid = pid; if (!(flags & ~PSSCAN_PID)) break; /* we needed only pid, we got it */ #if ENABLE_SELINUX if (flags & PSSCAN_CONTEXT) { if (getpidcon(sp->pid, &sp->context) < 0) sp->context = NULL; } #endif #if ENABLE_FEATURE_SHOW_THREADS if (sp->task_dir) filename_tail = filename + sprintf(filename, "/proc/%u/task/%u/", sp->main_thread_pid, pid); else #endif filename_tail = filename + sprintf(filename, "/proc/%u/", pid); if (flags & PSSCAN_UIDGID) { struct stat sb; if (stat(filename, &sb)) continue; /* process probably exited */ /* Effective UID/GID, not real */ sp->uid = sb.st_uid; sp->gid = sb.st_gid; } /* These are all retrieved from proc/NN/stat in one go: */ if (flags & (PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID | PSSCAN_COMM | PSSCAN_STATE | PSSCAN_VSZ | PSSCAN_RSS | PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_START_TIME | PSSCAN_TTY | PSSCAN_NICE | PSSCAN_CPU) ) { char *cp, *comm1; int tty; #if !ENABLE_FEATURE_FAST_TOP unsigned long vsz, rss; #endif /* see proc(5) for some details on this */ strcpy(filename_tail, "stat"); n = read_to_buf(filename, buf); if (n < 0) continue; /* process probably exited */ cp = strrchr(buf, ')'); /* split into "PID (cmd" and "" */ /*if (!cp || cp[1] != ' ') continue;*/ cp[0] = '\0'; if (sizeof(sp->comm) < 16) BUG_comm_size(); comm1 = strchr(buf, '('); /*if (comm1)*/ safe_strncpy(sp->comm, comm1 + 1, sizeof(sp->comm)); #if !ENABLE_FEATURE_FAST_TOP n = sscanf(cp+2, "%c %u " /* state, ppid */ "%u %u %d %*s " /* pgid, sid, tty, tpgid */ "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */ "%lu %lu " /* utime, stime */ "%*s %*s %*s " /* cutime, cstime, priority */ "%ld " /* nice */ "%*s %*s " /* timeout, it_real_value */ "%lu " /* start_time */ "%lu " /* vsize */ "%lu " /* rss */ # if ENABLE_FEATURE_TOP_SMP_PROCESS "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ "%*s %*s %*s %*s " /*signal, blocked, sigignore, sigcatch */ "%*s %*s %*s %*s " /*wchan, nswap, cnswap, exit_signal */ "%d" /*cpu last seen on*/ # endif , sp->state, &sp->ppid, &sp->pgid, &sp->sid, &tty, &sp->utime, &sp->stime, &tasknice, &sp->start_time, &vsz, &rss # if ENABLE_FEATURE_TOP_SMP_PROCESS , &sp->last_seen_on_cpu # endif ); if (n < 11) continue; /* bogus data, get next /proc/XXX */ # if ENABLE_FEATURE_TOP_SMP_PROCESS if (n == 11) sp->last_seen_on_cpu = 0; # endif /* vsz is in bytes and we want kb */ sp->vsz = vsz >> 10; /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ sp->rss = rss << sp->shift_pages_to_kb; sp->tty_major = (tty >> 8) & 0xfff; sp->tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00); #else /* This costs ~100 bytes more but makes top faster by 20% * If you run 10000 processes, this may be important for you */ sp->state[0] = cp[2]; cp += 4; sp->ppid = fast_strtoul_10(&cp); sp->pgid = fast_strtoul_10(&cp); sp->sid = fast_strtoul_10(&cp); tty = fast_strtoul_10(&cp); sp->tty_major = (tty >> 8) & 0xfff; sp->tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00); cp = skip_fields(cp, 6); /* tpgid, flags, min_flt, cmin_flt, maj_flt, cmaj_flt */ sp->utime = fast_strtoul_10(&cp); sp->stime = fast_strtoul_10(&cp); cp = skip_fields(cp, 3); /* cutime, cstime, priority */ tasknice = fast_strtol_10(&cp); cp = skip_fields(cp, 2); /* timeout, it_real_value */ sp->start_time = fast_strtoul_10(&cp); /* vsz is in bytes and we want kb */ sp->vsz = fast_strtoul_10(&cp) >> 10; /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; # if ENABLE_FEATURE_TOP_SMP_PROCESS /* (6): rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ /* (4): signal, blocked, sigignore, sigcatch */ /* (4): wchan, nswap, cnswap, exit_signal */ cp = skip_fields(cp, 14); //FIXME: is it safe to assume this field exists? sp->last_seen_on_cpu = fast_strtoul_10(&cp); # endif #endif /* FEATURE_FAST_TOP */ #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS sp->niceness = tasknice; #endif if (sp->vsz == 0 && sp->state[0] != 'Z') sp->state[1] = 'W'; else sp->state[1] = ' '; if (tasknice < 0) sp->state[2] = '<'; else if (tasknice) /* > 0 */ sp->state[2] = 'N'; else sp->state[2] = ' '; } #if ENABLE_FEATURE_TOPMEM if (flags & PSSCAN_SMAPS) procps_read_smaps(pid, &sp->smaps, NULL, NULL); #endif /* TOPMEM */ #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS if (flags & PSSCAN_RUIDGID) { FILE *file; strcpy(filename_tail, "status"); file = fopen_for_read(filename); if (file) { while (fgets(buf, sizeof(buf), file)) { char *tp; #define SCAN_TWO(str, name, statement) \ if (strncmp(buf, str, sizeof(str)-1) == 0) { \ tp = skip_whitespace(buf + sizeof(str)-1); \ sscanf(tp, "%u", &sp->name); \ statement; \ } SCAN_TWO("Uid:", ruid, continue); SCAN_TWO("Gid:", rgid, break); #undef SCAN_TWO } fclose(file); } } #endif /* PS_ADDITIONAL_COLUMNS */ if (flags & PSSCAN_EXE) { strcpy(filename_tail, "exe"); free(sp->exe); sp->exe = xmalloc_readlink(filename); } /* Note: if /proc/PID/cmdline is empty, * code below "breaks". Therefore it must be * the last code to parse /proc/PID/xxx data * (we used to have /proc/PID/exe parsing after it * and were getting stale sp->exe). */ #if 0 /* PSSCAN_CMD is not used */ if (flags & (PSSCAN_CMD|PSSCAN_ARGV0)) { free(sp->argv0); sp->argv0 = NULL; free(sp->cmd); sp->cmd = NULL; strcpy(filename_tail, "cmdline"); /* TODO: to get rid of size limits, read into malloc buf, * then realloc it down to real size. */ n = read_to_buf(filename, buf); if (n <= 0) break; if (flags & PSSCAN_ARGV0) sp->argv0 = xstrdup(buf); if (flags & PSSCAN_CMD) { do { n--; if ((unsigned char)(buf[n]) < ' ') buf[n] = ' '; } while (n); sp->cmd = xstrdup(buf); } } #else if (flags & (PSSCAN_ARGV0|PSSCAN_ARGVN)) { free(sp->argv0); sp->argv0 = NULL; strcpy(filename_tail, "cmdline"); n = read_to_buf(filename, buf); if (n <= 0) break; if (flags & PSSCAN_ARGVN) { sp->argv_len = n; sp->argv0 = xmalloc(n + 1); memcpy(sp->argv0, buf, n + 1); /* sp->argv0[n] = '\0'; - buf has it */ } else { sp->argv_len = 0; sp->argv0 = xstrdup(buf); } } #endif break; } /* for (;;) */ return sp; } void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) { int sz; char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; sprintf(filename, "/proc/%u/cmdline", pid); sz = open_read_close(filename, buf, col - 1); if (sz > 0) { const char *base; int comm_len; buf[sz] = '\0'; while (--sz >= 0 && buf[sz] == '\0') continue; /* Prevent basename("process foo/bar") = "bar" */ strchrnul(buf, ' ')[0] = '\0'; base = bb_basename(buf); /* before we replace argv0's NUL with space */ while (sz >= 0) { if ((unsigned char)(buf[sz]) < ' ') buf[sz] = ' '; sz--; } /* If comm differs from argv0, prepend "{comm} ". * It allows to see thread names set by prctl(PR_SET_NAME). */ if (base[0] == '-') /* "-sh" (login shell)? */ base++; comm_len = strlen(comm); /* Why compare up to comm_len, not COMM_LEN-1? * Well, some processes rewrite argv, and use _spaces_ there * while rewriting. (KDE is observed to do it). * I prefer to still treat argv0 "process foo bar" * as 'equal' to comm "process". */ if (strncmp(base, comm, comm_len) != 0) { comm_len += 3; if (col > comm_len) memmove(buf + comm_len, buf, col - comm_len); snprintf(buf, col, "{%s}", comm); if (col <= comm_len) return; buf[comm_len - 1] = ' '; buf[col - 1] = '\0'; } } else { snprintf(buf, col, "[%s]", comm); } } /* from kernel: // pid comm S ppid pgid sid tty_nr tty_pgrp flg sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ %lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \ %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu %llu\n", task->pid, tcomm, state, ppid, pgid, sid, tty_nr, tty_pgrp, task->flags, min_flt, cmin_flt, maj_flt, cmaj_flt, cputime_to_clock_t(utime), cputime_to_clock_t(stime), cputime_to_clock_t(cutime), cputime_to_clock_t(cstime), priority, nice, num_threads, // 0, start_time, vsize, mm ? get_mm_rss(mm) : 0, rsslim, mm ? mm->start_code : 0, mm ? mm->end_code : 0, mm ? mm->start_stack : 0, esp, eip, the rest is some obsolete cruft */ busybox-1.22.1/libbb/xfuncs.c0000644000000000000000000001603312263563520014521 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 1999-2004 by Erik Andersen * Copyright (C) 2006 Rob Landley * Copyright (C) 2006 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* We need to have separate xfuncs.c and xfuncs_printf.c because * with current linkers, even with section garbage collection, * if *.o module references any of XXXprintf functions, you pull in * entire printf machinery. Even if you do not use the function * which uses XXXprintf. * * xfuncs.c contains functions (not necessarily xfuncs) * which do not pull in printf, directly or indirectly. * xfunc_printf.c contains those which do. * * TODO: move xmalloc() and xatonum() here. */ #include "libbb.h" /* Turn on nonblocking I/O on a fd */ void FAST_FUNC ndelay_on(int fd) { int flags = fcntl(fd, F_GETFL); if (flags & O_NONBLOCK) return; fcntl(fd, F_SETFL, flags | O_NONBLOCK); } void FAST_FUNC ndelay_off(int fd) { int flags = fcntl(fd, F_GETFL); if (!(flags & O_NONBLOCK)) return; fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); } void FAST_FUNC close_on_exec_on(int fd) { fcntl(fd, F_SETFD, FD_CLOEXEC); } char* FAST_FUNC strncpy_IFNAMSIZ(char *dst, const char *src) { #ifndef IFNAMSIZ enum { IFNAMSIZ = 16 }; #endif return strncpy(dst, src, IFNAMSIZ); } /* Convert unsigned integer to ascii, writing into supplied buffer. * A truncated result contains the first few digits of the result ala strncpy. * Returns a pointer past last generated digit, does _not_ store NUL. */ void BUG_sizeof(void); char* FAST_FUNC utoa_to_buf(unsigned n, char *buf, unsigned buflen) { unsigned i, out, res; if (buflen) { out = 0; if (sizeof(n) == 4) // 2^32-1 = 4294967295 i = 1000000000; #if UINT_MAX > 4294967295 /* prevents warning about "const too large" */ else if (sizeof(n) == 8) // 2^64-1 = 18446744073709551615 i = 10000000000000000000; #endif else BUG_sizeof(); for (; i; i /= 10) { res = n / i; n = n % i; if (res || out || i == 1) { if (--buflen == 0) break; out++; *buf++ = '0' + res; } } } return buf; } /* Convert signed integer to ascii, like utoa_to_buf() */ char* FAST_FUNC itoa_to_buf(int n, char *buf, unsigned buflen) { if (!buflen) return buf; if (n < 0) { n = -n; *buf++ = '-'; buflen--; } return utoa_to_buf((unsigned)n, buf, buflen); } // The following two functions use a static buffer, so calling either one a // second time will overwrite previous results. // // The largest 32 bit integer is -2 billion plus NUL, or 1+10+1=12 bytes. // It so happens that sizeof(int) * 3 is enough for 32+ bit ints. // (sizeof(int) * 3 + 2 is correct for any width, even 8-bit) static char local_buf[sizeof(int) * 3]; /* Convert unsigned integer to ascii using a static buffer (returned). */ char* FAST_FUNC utoa(unsigned n) { *(utoa_to_buf(n, local_buf, sizeof(local_buf) - 1)) = '\0'; return local_buf; } /* Convert signed integer to ascii using a static buffer (returned). */ char* FAST_FUNC itoa(int n) { *(itoa_to_buf(n, local_buf, sizeof(local_buf) - 1)) = '\0'; return local_buf; } /* Emit a string of hex representation of bytes */ char* FAST_FUNC bin2hex(char *p, const char *cp, int count) { while (count) { unsigned char c = *cp++; /* put lowercase hex digits */ *p++ = 0x20 | bb_hexdigits_upcase[c >> 4]; *p++ = 0x20 | bb_hexdigits_upcase[c & 0xf]; count--; } return p; } /* Convert "[x]x[:][x]x[:][x]x[:][x]x" hex string to binary, no more than COUNT bytes */ char* FAST_FUNC hex2bin(char *dst, const char *str, int count) { errno = EINVAL; while (*str && count) { uint8_t val; uint8_t c = *str++; if (isdigit(c)) val = c - '0'; else if ((c|0x20) >= 'a' && (c|0x20) <= 'f') val = (c|0x20) - ('a' - 10); else return NULL; val <<= 4; c = *str; if (isdigit(c)) val |= c - '0'; else if ((c|0x20) >= 'a' && (c|0x20) <= 'f') val |= (c|0x20) - ('a' - 10); else if (c == ':' || c == '\0') val >>= 4; else return NULL; *dst++ = val; if (c != '\0') str++; if (*str == ':') str++; count--; } errno = (*str ? ERANGE : 0); return dst; } /* Return how long the file at fd is, if there's any way to determine it. */ #ifdef UNUSED off_t FAST_FUNC fdlength(int fd) { off_t bottom = 0, top = 0, pos; long size; // If the ioctl works for this, return it. if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512; // FIXME: explain why lseek(SEEK_END) is not used here! // If not, do a binary search for the last location we can read. (Some // block devices don't do BLKGETSIZE right.) do { char temp; pos = bottom + (top - bottom) / 2; // If we can read from the current location, it's bigger. if (lseek(fd, pos, SEEK_SET)>=0 && safe_read(fd, &temp, 1)==1) { if (bottom == top) bottom = top = (top+1) * 2; else bottom = pos; // If we can't, it's smaller. } else { if (bottom == top) { if (!top) return 0; bottom = top/2; } else top = pos; } } while (bottom + 1 != top); return pos + 1; } #endif int FAST_FUNC bb_putchar_stderr(char ch) { return write(STDERR_FILENO, &ch, 1); } ssize_t FAST_FUNC full_write1_str(const char *str) { return full_write(STDOUT_FILENO, str, strlen(str)); } ssize_t FAST_FUNC full_write2_str(const char *str) { return full_write(STDERR_FILENO, str, strlen(str)); } static int wh_helper(int value, int def_val, const char *env_name, int *err) { if (value == 0) { char *s = getenv(env_name); if (s) { value = atoi(s); /* If LINES/COLUMNS are set, pretend that there is * no error getting w/h, this prevents some ugly * cursor tricks by our callers */ *err = 0; } } if (value <= 1 || value >= 30000) value = def_val; return value; } /* It is perfectly ok to pass in a NULL for either width or for * height, in which case that value will not be set. */ int FAST_FUNC get_terminal_width_height(int fd, unsigned *width, unsigned *height) { struct winsize win; int err; win.ws_row = 0; win.ws_col = 0; /* I've seen ioctl returning 0, but row/col is (still?) 0. * We treat that as an error too. */ err = ioctl(fd, TIOCGWINSZ, &win) != 0 || win.ws_row == 0; if (height) *height = wh_helper(win.ws_row, 24, "LINES", &err); if (width) *width = wh_helper(win.ws_col, 80, "COLUMNS", &err); return err; } int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp) { return tcsetattr(STDIN_FILENO, TCSANOW, tp); } pid_t FAST_FUNC safe_waitpid(pid_t pid, int *wstat, int options) { pid_t r; do r = waitpid(pid, wstat, options); while ((r == -1) && (errno == EINTR)); return r; } pid_t FAST_FUNC wait_any_nohang(int *wstat) { return safe_waitpid(-1, wstat, WNOHANG); } // Wait for the specified child PID to exit, returning child's error return. int FAST_FUNC wait4pid(pid_t pid) { int status; if (pid <= 0) { /*errno = ECHILD; -- wrong. */ /* we expect errno to be already set from failed [v]fork/exec */ return -1; } if (safe_waitpid(pid, &status, 0) == -1) return -1; if (WIFEXITED(status)) return WEXITSTATUS(status); if (WIFSIGNALED(status)) return WTERMSIG(status) + 0x180; return 0; } busybox-1.22.1/libbb/fclose_nonstdin.c0000644000000000000000000000130512263563520016376 0ustar rootroot/* 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 source tree. */ /* A number of standard utilities can accept multiple command line args * of '-' for stdin, according to SUSv3. So we encapsulate the check * here to save a little space. */ #include "libbb.h" int FAST_FUNC fclose_if_not_stdin(FILE *f) { /* Some more paranoid applets want ferror() check too */ int r = ferror(f); /* NB: does NOT set errno! */ if (r) errno = EIO; /* so we'll help it */ if (f != stdin) return (r | fclose(f)); /* fclose does set errno on error */ return r; } busybox-1.22.1/libbb/match_fstype.c0000644000000000000000000000155712263563520015706 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Match fstypes for use in mount unmount * We accept notmpfs,nfs but not notmpfs,nonfs * This allows us to match fstypes that start with no like so * mount -at ,noddy * * Returns 1 for a match, otherwise 0 * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" #ifdef HAVE_MNTENT_H int FAST_FUNC match_fstype(const struct mntent *mt, const char *t_fstype) { int match = 1; int len; if (!t_fstype) return match; if (t_fstype[0] == 'n' && t_fstype[1] == 'o') { match--; t_fstype += 2; } len = strlen(mt->mnt_type); while (1) { if (strncmp(mt->mnt_type, t_fstype, len) == 0 && (t_fstype[len] == '\0' || t_fstype[len] == ',') ) { return match; } t_fstype = strchr(t_fstype, ','); if (!t_fstype) break; t_fstype++; } return !match; } #endif /* HAVE_MNTENT_H */ busybox-1.22.1/libbb/safe_write.c0000644000000000000000000000060412263563520015340 0ustar rootroot/* 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 source tree. */ #include "libbb.h" ssize_t FAST_FUNC safe_write(int fd, const void *buf, size_t count) { ssize_t n; do { n = write(fd, buf, count); } while (n < 0 && errno == EINTR); return n; } busybox-1.22.1/libbb/xgetcwd.c0000644000000000000000000000176712263563520014670 0ustar rootroot/* 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 * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" /* Return the current directory, newly allocated, arbitrarily long. Return NULL and set errno on error. If argument is not NULL (previous usage allocate memory), call free() */ char* FAST_FUNC xrealloc_getcwd_or_warn(char *cwd) { #define PATH_INCR 64 char *ret; unsigned path_max; path_max = 128; /* 128 + 64 should be enough for 99% of cases */ while (1) { path_max += PATH_INCR; cwd = xrealloc(cwd, path_max); ret = getcwd(cwd, path_max); if (ret == NULL) { if (errno == ERANGE) continue; free(cwd); bb_perror_msg("getcwd"); return NULL; } cwd = xrealloc(cwd, strlen(cwd) + 1); return cwd; } } busybox-1.22.1/libbb/bb_strtonum.c0000644000000000000000000000775312263563520015562 0ustar rootroot/* 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 source tree. */ #include "libbb.h" /* On exit: errno = 0 only if there was non-empty, '\0' terminated value * errno = EINVAL if value was not '\0' terminated, but otherwise ok * Return value is still valid, caller should just check whether end[0] * is a valid terminating char for particular case. OTOH, if caller * requires '\0' terminated input, [s]he can just check errno == 0. * errno = ERANGE if value had alphanumeric terminating char ("1234abcg"). * errno = ERANGE if value is out of range, missing, etc. * errno = ERANGE if value had minus sign for strtouXX (even "-0" is not ok ) * return value is all-ones in this case. * * Test code: * char *endptr; * const char *minus = "-"; * errno = 0; * bb_strtoi(minus, &endptr, 0); // must set ERANGE * printf("minus:%p endptr:%p errno:%d EINVAL:%d\n", minus, endptr, errno, EINVAL); * errno = 0; * bb_strtoi("-0-", &endptr, 0); // must set EINVAL and point to second '-' * printf("endptr[0]:%c errno:%d EINVAL:%d\n", endptr[0], errno, EINVAL); */ static unsigned long long ret_ERANGE(void) { errno = ERANGE; /* this ain't as small as it looks (on glibc) */ return ULLONG_MAX; } static unsigned long long handle_errors(unsigned long long v, char **endp) { char next_ch = **endp; /* errno is already set to ERANGE by strtoXXX if value overflowed */ if (next_ch) { /* "1234abcg" or out-of-range? */ if (isalnum(next_ch) || errno) return ret_ERANGE(); /* good number, just suspicious terminator */ errno = EINVAL; } return v; } unsigned long long FAST_FUNC bb_strtoull(const char *arg, char **endp, int base) { unsigned long long v; char *endptr; if (!endp) endp = &endptr; *endp = (char*) arg; /* 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, endp, base); return handle_errors(v, endp); } long long FAST_FUNC bb_strtoll(const char *arg, char **endp, int base) { unsigned long long v; char *endptr; char first; if (!endp) endp = &endptr; *endp = (char*) arg; /* Check for the weird "feature": * a "-" string is apparently a valid "number" for strto[u]l[l]! * It returns zero and errno is 0! :( */ first = (arg[0] != '-' ? arg[0] : arg[1]); if (!isalnum(first)) return ret_ERANGE(); errno = 0; v = strtoll(arg, endp, base); return handle_errors(v, endp); } #if ULONG_MAX != ULLONG_MAX unsigned long FAST_FUNC bb_strtoul(const char *arg, char **endp, int base) { unsigned long v; char *endptr; if (!endp) endp = &endptr; *endp = (char*) arg; if (!isalnum(arg[0])) return ret_ERANGE(); errno = 0; v = strtoul(arg, endp, base); return handle_errors(v, endp); } long FAST_FUNC bb_strtol(const char *arg, char **endp, int base) { long v; char *endptr; char first; if (!endp) endp = &endptr; *endp = (char*) arg; first = (arg[0] != '-' ? arg[0] : arg[1]); if (!isalnum(first)) return ret_ERANGE(); errno = 0; v = strtol(arg, endp, base); return handle_errors(v, endp); } #endif #if UINT_MAX != ULONG_MAX unsigned FAST_FUNC bb_strtou(const char *arg, char **endp, int base) { unsigned long v; char *endptr; if (!endp) endp = &endptr; *endp = (char*) arg; if (!isalnum(arg[0])) return ret_ERANGE(); errno = 0; v = strtoul(arg, endp, base); if (v > UINT_MAX) return ret_ERANGE(); return handle_errors(v, endp); } int FAST_FUNC bb_strtoi(const char *arg, char **endp, int base) { long v; char *endptr; char first; if (!endp) endp = &endptr; *endp = (char*) arg; first = (arg[0] != '-' ? arg[0] : arg[1]); if (!isalnum(first)) return ret_ERANGE(); errno = 0; v = strtol(arg, endp, base); if (v > INT_MAX) return ret_ERANGE(); if (v < INT_MIN) return ret_ERANGE(); return handle_errors(v, endp); } #endif busybox-1.22.1/libbb/mtab.c0000644000000000000000000000264612263563520014143 0ustar rootroot/* 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 source tree. */ #include #include "libbb.h" #if ENABLE_FEATURE_MTAB_SUPPORT void FAST_FUNC erase_mtab(const char *name) { struct mntent *entries; int i, count; FILE *mountTable; struct mntent *m; mountTable = setmntent(bb_path_mtab_file, "r"); /* Bummer. Fall back on trying the /proc filesystem */ if (!mountTable) mountTable = setmntent("/proc/mounts", "r"); if (!mountTable) { bb_perror_msg(bb_path_mtab_file); return; } entries = NULL; count = 0; while ((m = getmntent(mountTable)) != 0) { entries = xrealloc_vector(entries, 3, count); entries[count].mnt_fsname = xstrdup(m->mnt_fsname); entries[count].mnt_dir = xstrdup(m->mnt_dir); entries[count].mnt_type = xstrdup(m->mnt_type); entries[count].mnt_opts = xstrdup(m->mnt_opts); entries[count].mnt_freq = m->mnt_freq; entries[count].mnt_passno = m->mnt_passno; count++; } endmntent(mountTable); //TODO: make update atomic mountTable = setmntent(bb_path_mtab_file, "w"); if (mountTable) { for (i = 0; i < count; i++) { if (strcmp(entries[i].mnt_fsname, name) != 0 && strcmp(entries[i].mnt_dir, name) != 0) addmntent(mountTable, &entries[i]); } endmntent(mountTable); } else if (errno != EROFS) bb_perror_msg(bb_path_mtab_file); } #endif busybox-1.22.1/libbb/copyfd.c0000644000000000000000000000547212263563520014504 0ustar rootroot/* 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 source tree. */ #include "libbb.h" /* Used by NOFORK applets (e.g. cat) - must not use xmalloc. * size < 0 means "ignore write errors", used by tar --to-command * size = 0 means "copy till EOF" */ static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size) { int status = -1; off_t total = 0; bool continue_on_write_error = 0; #if CONFIG_FEATURE_COPYBUF_KB <= 4 char buffer[CONFIG_FEATURE_COPYBUF_KB * 1024]; enum { buffer_size = sizeof(buffer) }; #else char *buffer; int buffer_size; #endif if (size < 0) { size = -size; continue_on_write_error = 1; } #if CONFIG_FEATURE_COPYBUF_KB > 4 if (size > 0 && size <= 4 * 1024) goto use_small_buf; /* We want page-aligned buffer, just in case kernel is clever * and can do page-aligned io more efficiently */ buffer = mmap(NULL, CONFIG_FEATURE_COPYBUF_KB * 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, /* ignored: */ -1, 0); buffer_size = CONFIG_FEATURE_COPYBUF_KB * 1024; if (buffer == MAP_FAILED) { use_small_buf: buffer = alloca(4 * 1024); buffer_size = 4 * 1024; } #endif if (src_fd < 0) goto out; if (!size) { size = buffer_size; status = 1; /* copy until eof */ } while (1) { ssize_t rd; rd = safe_read(src_fd, buffer, size > buffer_size ? buffer_size : size); if (!rd) { /* eof - all done */ status = 0; break; } if (rd < 0) { bb_perror_msg(bb_msg_read_error); break; } /* dst_fd == -1 is a fake, else... */ if (dst_fd >= 0) { ssize_t wr = full_write(dst_fd, buffer, rd); if (wr < rd) { if (!continue_on_write_error) { bb_perror_msg(bb_msg_write_error); break; } dst_fd = -1; } } total += rd; if (status < 0) { /* if we aren't copying till EOF... */ size -= rd; if (!size) { /* 'size' bytes copied - all done */ status = 0; break; } } } out: #if CONFIG_FEATURE_COPYBUF_KB > 4 if (buffer_size != 4 * 1024) munmap(buffer, buffer_size); #endif return status ? -1 : total; } #if 0 void FAST_FUNC complain_copyfd_and_die(off_t sz) { if (sz != -1) bb_error_msg_and_die("short read"); /* if sz == -1, bb_copyfd_XX already complained */ xfunc_die(); } #endif off_t FAST_FUNC bb_copyfd_size(int fd1, int fd2, off_t size) { if (size) { return bb_full_fd_action(fd1, fd2, size); } return 0; } void FAST_FUNC bb_copyfd_exact_size(int fd1, int fd2, off_t size) { off_t sz = bb_copyfd_size(fd1, fd2, size); if (sz == (size >= 0 ? size : -size)) return; if (sz != -1) bb_error_msg_and_die("short read"); /* if sz == -1, bb_copyfd_XX already complained */ xfunc_die(); } off_t FAST_FUNC bb_copyfd_eof(int fd1, int fd2) { return bb_full_fd_action(fd1, fd2, 0); } busybox-1.22.1/libbb/xconnect.c0000644000000000000000000003057312263563520015041 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Connect to host at port using address resolution from getaddrinfo * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include #include /* netinet/in.h needs it */ #include #include #include #include "libbb.h" void FAST_FUNC setsockopt_reuseaddr(int fd) { setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_1, sizeof(const_int_1)); } int FAST_FUNC setsockopt_broadcast(int fd) { return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &const_int_1, sizeof(const_int_1)); } #ifdef SO_BINDTODEVICE int FAST_FUNC setsockopt_bindtodevice(int fd, const char *iface) { int r; struct ifreq ifr; strncpy_IFNAMSIZ(ifr.ifr_name, iface); /* NB: passing (iface, strlen(iface) + 1) does not work! * (maybe it works on _some_ kernels, but not on 2.6.26) * Actually, ifr_name is at offset 0, and in practice * just giving char[IFNAMSIZ] instead of struct ifreq works too. * But just in case it's not true on some obscure arch... */ r = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)); if (r) bb_perror_msg("can't bind to interface %s", iface); return r; } #else int FAST_FUNC setsockopt_bindtodevice(int fd UNUSED_PARAM, const char *iface UNUSED_PARAM) { bb_error_msg("SO_BINDTODEVICE is not supported on this system"); return -1; } #endif static len_and_sockaddr* get_lsa(int fd, int (*get_name)(int fd, struct sockaddr *addr, socklen_t *addrlen)) { len_and_sockaddr lsa; len_and_sockaddr *lsa_ptr; lsa.len = LSA_SIZEOF_SA; if (get_name(fd, &lsa.u.sa, &lsa.len) != 0) return NULL; lsa_ptr = xzalloc(LSA_LEN_SIZE + lsa.len); if (lsa.len > LSA_SIZEOF_SA) { /* rarely (if ever) happens */ lsa_ptr->len = lsa.len; get_name(fd, &lsa_ptr->u.sa, &lsa_ptr->len); } else { memcpy(lsa_ptr, &lsa, LSA_LEN_SIZE + lsa.len); } return lsa_ptr; } len_and_sockaddr* FAST_FUNC get_sock_lsa(int fd) { return get_lsa(fd, getsockname); } len_and_sockaddr* FAST_FUNC get_peer_lsa(int fd) { return get_lsa(fd, getpeername); } void FAST_FUNC xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) { if (connect(s, s_addr, addrlen) < 0) { if (ENABLE_FEATURE_CLEAN_UP) close(s); if (s_addr->sa_family == AF_INET) bb_perror_msg_and_die("%s (%s)", "can't connect to remote host", inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr)); bb_perror_msg_and_die("can't connect to remote host"); } } /* Return port number for a service. * If "port" is a number use it as the port. * If "port" is a name it is looked up in /etc/services, * if it isnt found return default_port */ unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned default_port) { unsigned port_nr = default_port; if (port) { int old_errno; /* Since this is a lib function, we're not allowed to reset errno to 0. * Doing so could break an app that is deferring checking of errno. */ old_errno = errno; port_nr = bb_strtou(port, NULL, 10); if (errno || port_nr > 65535) { struct servent *tserv = getservbyname(port, protocol); port_nr = default_port; if (tserv) port_nr = ntohs(tserv->s_port); } errno = old_errno; } return (uint16_t)port_nr; } /* "New" networking API */ int FAST_FUNC get_nport(const struct sockaddr *sa) { #if ENABLE_FEATURE_IPV6 if (sa->sa_family == AF_INET6) { return ((struct sockaddr_in6*)sa)->sin6_port; } #endif if (sa->sa_family == AF_INET) { return ((struct sockaddr_in*)sa)->sin_port; } /* What? UNIX socket? IPX?? :) */ return -1; } void FAST_FUNC set_nport(struct sockaddr *sa, unsigned port) { #if ENABLE_FEATURE_IPV6 if (sa->sa_family == AF_INET6) { struct sockaddr_in6 *sin6 = (void*) sa; sin6->sin6_port = port; return; } #endif if (sa->sa_family == AF_INET) { struct sockaddr_in *sin = (void*) sa; sin->sin_port = port; return; } /* What? UNIX socket? IPX?? :) */ } /* We hijack this constant to mean something else */ /* It doesn't hurt because we will remove this bit anyway */ #define DIE_ON_ERROR AI_CANONNAME /* host: "1.2.3.4[:port]", "www.google.com[:port]" * port: if neither of above specifies port # */ static len_and_sockaddr* str2sockaddr( const char *host, int port, IF_FEATURE_IPV6(sa_family_t af,) int ai_flags) { IF_NOT_FEATURE_IPV6(sa_family_t af = AF_INET;) int rc; len_and_sockaddr *r; struct addrinfo *result = NULL; struct addrinfo *used_res; const char *org_host = host; /* only for error msg */ const char *cp; struct addrinfo hint; if (ENABLE_FEATURE_UNIX_LOCAL && strncmp(host, "local:", 6) == 0) { struct sockaddr_un *sun; r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_un)); r->len = sizeof(struct sockaddr_un); r->u.sa.sa_family = AF_UNIX; sun = (struct sockaddr_un *)&r->u.sa; safe_strncpy(sun->sun_path, host + 6, sizeof(sun->sun_path)); return r; } r = NULL; /* Ugly parsing of host:addr */ if (ENABLE_FEATURE_IPV6 && host[0] == '[') { /* Even uglier parsing of [xx]:nn */ host++; cp = strchr(host, ']'); if (!cp || (cp[1] != ':' && cp[1] != '\0')) { /* Malformed: must be [xx]:nn or [xx] */ bb_error_msg("bad address '%s'", org_host); if (ai_flags & DIE_ON_ERROR) xfunc_die(); return NULL; } } else { cp = strrchr(host, ':'); if (ENABLE_FEATURE_IPV6 && cp && strchr(host, ':') != cp) { /* There is more than one ':' (e.g. "::1") */ cp = NULL; /* it's not a port spec */ } } if (cp) { /* points to ":" or "]:" */ int sz = cp - host + 1; host = safe_strncpy(alloca(sz), host, sz); if (ENABLE_FEATURE_IPV6 && *cp != ':') { cp++; /* skip ']' */ if (*cp == '\0') /* [xx] without port */ goto skip; } cp++; /* skip ':' */ port = bb_strtou(cp, NULL, 10); if (errno || (unsigned)port > 0xffff) { bb_error_msg("bad port spec '%s'", org_host); if (ai_flags & DIE_ON_ERROR) xfunc_die(); return NULL; } skip: ; } /* Next two if blocks allow to skip getaddrinfo() * in case host name is a numeric IP(v6) address. * getaddrinfo() initializes DNS resolution machinery, * scans network config and such - tens of syscalls. */ /* If we were not asked specifically for IPv6, * check whether this is a numeric IPv4 */ IF_FEATURE_IPV6(if(af != AF_INET6)) { struct in_addr in4; if (inet_aton(host, &in4) != 0) { r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_in)); r->len = sizeof(struct sockaddr_in); r->u.sa.sa_family = AF_INET; r->u.sin.sin_addr = in4; goto set_port; } } #if ENABLE_FEATURE_IPV6 /* If we were not asked specifically for IPv4, * check whether this is a numeric IPv6 */ if (af != AF_INET) { struct in6_addr in6; if (inet_pton(AF_INET6, host, &in6) > 0) { r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_in6)); r->len = sizeof(struct sockaddr_in6); r->u.sa.sa_family = AF_INET6; r->u.sin6.sin6_addr = in6; goto set_port; } } #endif memset(&hint, 0 , sizeof(hint)); hint.ai_family = af; /* Need SOCK_STREAM, or else we get each address thrice (or more) * for each possible socket type (tcp,udp,raw...): */ hint.ai_socktype = SOCK_STREAM; hint.ai_flags = ai_flags & ~DIE_ON_ERROR; rc = getaddrinfo(host, NULL, &hint, &result); if (rc || !result) { bb_error_msg("bad address '%s'", org_host); if (ai_flags & DIE_ON_ERROR) xfunc_die(); goto ret; } used_res = result; #if ENABLE_FEATURE_PREFER_IPV4_ADDRESS while (1) { if (used_res->ai_family == AF_INET) break; used_res = used_res->ai_next; if (!used_res) { used_res = result; break; } } #endif r = xmalloc(LSA_LEN_SIZE + used_res->ai_addrlen); r->len = used_res->ai_addrlen; memcpy(&r->u.sa, used_res->ai_addr, used_res->ai_addrlen); set_port: set_nport(&r->u.sa, htons(port)); ret: if (result) freeaddrinfo(result); return r; } #if !ENABLE_FEATURE_IPV6 #define str2sockaddr(host, port, af, ai_flags) str2sockaddr(host, port, ai_flags) #endif #if ENABLE_FEATURE_IPV6 len_and_sockaddr* FAST_FUNC host_and_af2sockaddr(const char *host, int port, sa_family_t af) { return str2sockaddr(host, port, af, 0); } len_and_sockaddr* FAST_FUNC xhost_and_af2sockaddr(const char *host, int port, sa_family_t af) { return str2sockaddr(host, port, af, DIE_ON_ERROR); } #endif len_and_sockaddr* FAST_FUNC host2sockaddr(const char *host, int port) { return str2sockaddr(host, port, AF_UNSPEC, 0); } len_and_sockaddr* FAST_FUNC xhost2sockaddr(const char *host, int port) { return str2sockaddr(host, port, AF_UNSPEC, DIE_ON_ERROR); } len_and_sockaddr* FAST_FUNC xdotted2sockaddr(const char *host, int port) { return str2sockaddr(host, port, AF_UNSPEC, AI_NUMERICHOST | DIE_ON_ERROR); } int FAST_FUNC xsocket_type(len_and_sockaddr **lsap, int family, int sock_type) { len_and_sockaddr *lsa; int fd; int len; if (family == AF_UNSPEC) { #if ENABLE_FEATURE_IPV6 fd = socket(AF_INET6, sock_type, 0); if (fd >= 0) { family = AF_INET6; goto done; } #endif family = AF_INET; } fd = xsocket(family, sock_type, 0); len = sizeof(struct sockaddr_in); if (family == AF_UNIX) len = sizeof(struct sockaddr_un); #if ENABLE_FEATURE_IPV6 if (family == AF_INET6) { done: len = sizeof(struct sockaddr_in6); } #endif lsa = xzalloc(LSA_LEN_SIZE + len); lsa->len = len; lsa->u.sa.sa_family = family; *lsap = lsa; return fd; } int FAST_FUNC xsocket_stream(len_and_sockaddr **lsap) { return xsocket_type(lsap, AF_UNSPEC, SOCK_STREAM); } static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type) { int fd; len_and_sockaddr *lsa; if (bindaddr && bindaddr[0]) { lsa = xdotted2sockaddr(bindaddr, port); /* user specified bind addr dictates family */ fd = xsocket(lsa->u.sa.sa_family, sock_type, 0); } else { fd = xsocket_type(&lsa, AF_UNSPEC, sock_type); set_nport(&lsa->u.sa, htons(port)); } setsockopt_reuseaddr(fd); xbind(fd, &lsa->u.sa, lsa->len); free(lsa); return fd; } int FAST_FUNC create_and_bind_stream_or_die(const char *bindaddr, int port) { return create_and_bind_or_die(bindaddr, port, SOCK_STREAM); } int FAST_FUNC create_and_bind_dgram_or_die(const char *bindaddr, int port) { return create_and_bind_or_die(bindaddr, port, SOCK_DGRAM); } int FAST_FUNC create_and_connect_stream_or_die(const char *peer, int port) { int fd; len_and_sockaddr *lsa; lsa = xhost2sockaddr(peer, port); fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0); setsockopt_reuseaddr(fd); xconnect(fd, &lsa->u.sa, lsa->len); free(lsa); return fd; } int FAST_FUNC xconnect_stream(const len_and_sockaddr *lsa) { int fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0); xconnect(fd, &lsa->u.sa, lsa->len); return fd; } /* We hijack this constant to mean something else */ /* It doesn't hurt because we will add this bit anyway */ #define IGNORE_PORT NI_NUMERICSERV static char* FAST_FUNC sockaddr2str(const struct sockaddr *sa, int flags) { char host[128]; char serv[16]; int rc; socklen_t salen; if (ENABLE_FEATURE_UNIX_LOCAL && sa->sa_family == AF_UNIX) { struct sockaddr_un *sun = (struct sockaddr_un *)sa; return xasprintf("local:%.*s", (int) sizeof(sun->sun_path), sun->sun_path); } salen = LSA_SIZEOF_SA; #if ENABLE_FEATURE_IPV6 if (sa->sa_family == AF_INET) salen = sizeof(struct sockaddr_in); if (sa->sa_family == AF_INET6) salen = sizeof(struct sockaddr_in6); #endif rc = getnameinfo(sa, salen, host, sizeof(host), /* can do ((flags & IGNORE_PORT) ? NULL : serv) but why bother? */ serv, sizeof(serv), /* do not resolve port# into service _name_ */ flags | NI_NUMERICSERV ); if (rc) return NULL; if (flags & IGNORE_PORT) return xstrdup(host); #if ENABLE_FEATURE_IPV6 if (sa->sa_family == AF_INET6) { if (strchr(host, ':')) /* heh, it's not a resolved hostname */ return xasprintf("[%s]:%s", host, serv); /*return xasprintf("%s:%s", host, serv);*/ /* - fall through instead */ } #endif /* For now we don't support anything else, so it has to be INET */ /*if (sa->sa_family == AF_INET)*/ return xasprintf("%s:%s", host, serv); /*return xstrdup(host);*/ } char* FAST_FUNC xmalloc_sockaddr2host(const struct sockaddr *sa) { return sockaddr2str(sa, 0); } char* FAST_FUNC xmalloc_sockaddr2host_noport(const struct sockaddr *sa) { return sockaddr2str(sa, IGNORE_PORT); } char* FAST_FUNC xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa) { return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT); } char* FAST_FUNC xmalloc_sockaddr2dotted(const struct sockaddr *sa) { return sockaddr2str(sa, NI_NUMERICHOST); } char* FAST_FUNC xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) { return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT); } busybox-1.22.1/libbb/xfuncs_printf.c0000644000000000000000000003652612263563520016114 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 1999-2004 by Erik Andersen * Copyright (C) 2006 Rob Landley * Copyright (C) 2006 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* We need to have separate xfuncs.c and xfuncs_printf.c because * with current linkers, even with section garbage collection, * if *.o module references any of XXXprintf functions, you pull in * entire printf machinery. Even if you do not use the function * which uses XXXprintf. * * xfuncs.c contains functions (not necessarily xfuncs) * which do not pull in printf, directly or indirectly. * xfunc_printf.c contains those which do. */ #include "libbb.h" /* All the functions starting with "x" call bb_error_msg_and_die() if they * fail, so callers never need to check for errors. If it returned, it * succeeded. */ #ifndef DMALLOC /* dmalloc provides variants of these that do abort() on failure. * Since dmalloc's prototypes overwrite the impls here as they are * included after these prototypes in libbb.h, all is well. */ // Warn if we can't allocate size bytes of memory. void* FAST_FUNC malloc_or_warn(size_t size) { void *ptr = malloc(size); if (ptr == NULL && size != 0) bb_error_msg(bb_msg_memory_exhausted); return ptr; } // Die if we can't allocate size bytes of memory. void* FAST_FUNC xmalloc(size_t size) { void *ptr = malloc(size); if (ptr == NULL && size != 0) bb_error_msg_and_die(bb_msg_memory_exhausted); return ptr; } // Die if we can't resize previously allocated memory. (This returns a pointer // to the new memory, which may or may not be the same as the old memory. // It'll copy the contents to a new chunk and free the old one if necessary.) void* FAST_FUNC xrealloc(void *ptr, size_t size) { ptr = realloc(ptr, size); if (ptr == NULL && size != 0) bb_error_msg_and_die(bb_msg_memory_exhausted); return ptr; } #endif /* DMALLOC */ // Die if we can't allocate and zero size bytes of memory. void* FAST_FUNC xzalloc(size_t size) { void *ptr = xmalloc(size); memset(ptr, 0, size); return ptr; } // Die if we can't copy a string to freshly allocated memory. char* FAST_FUNC xstrdup(const char *s) { char *t; if (s == NULL) return NULL; t = strdup(s); if (t == NULL) bb_error_msg_and_die(bb_msg_memory_exhausted); return t; } // Die if we can't allocate n+1 bytes (space for the null terminator) and copy // the (possibly truncated to length n) string into it. char* FAST_FUNC xstrndup(const char *s, int n) { int m; char *t; if (ENABLE_DEBUG && s == NULL) bb_error_msg_and_die("xstrndup bug"); /* We can just xmalloc(n+1) and strncpy into it, */ /* but think about xstrndup("abc", 10000) wastage! */ m = n; t = (char*) s; while (m) { if (!*t) break; m--; t++; } n -= m; t = xmalloc(n + 1); t[n] = '\0'; return memcpy(t, s, n); } // Die if we can't open a file and return a FILE* to it. // Notice we haven't got xfread(), This is for use with fscanf() and friends. FILE* FAST_FUNC xfopen(const char *path, const char *mode) { FILE *fp = fopen(path, mode); if (fp == NULL) bb_perror_msg_and_die("can't open '%s'", path); return fp; } // Die if we can't open a file and return a fd. int FAST_FUNC xopen3(const char *pathname, int flags, int mode) { int ret; ret = open(pathname, flags, mode); if (ret < 0) { bb_perror_msg_and_die("can't open '%s'", pathname); } return ret; } // Die if we can't open a file and return a fd. int FAST_FUNC xopen(const char *pathname, int flags) { return xopen3(pathname, flags, 0666); } // Warn if we can't open a file and return a fd. int FAST_FUNC open3_or_warn(const char *pathname, int flags, int mode) { int ret; ret = open(pathname, flags, mode); if (ret < 0) { bb_perror_msg("can't open '%s'", pathname); } return ret; } // Warn if we can't open a file and return a fd. int FAST_FUNC open_or_warn(const char *pathname, int flags) { return open3_or_warn(pathname, flags, 0666); } /* Die if we can't open an existing file readonly with O_NONBLOCK * and return the fd. * Note that for ioctl O_RDONLY is sufficient. */ int FAST_FUNC xopen_nonblocking(const char *pathname) { return xopen(pathname, O_RDONLY | O_NONBLOCK); } int FAST_FUNC xopen_as_uid_gid(const char *pathname, int flags, uid_t u, gid_t g) { int fd; uid_t old_euid = geteuid(); gid_t old_egid = getegid(); xsetegid(g); xseteuid(u); fd = xopen(pathname, flags); xseteuid(old_euid); xsetegid(old_egid); return fd; } void FAST_FUNC xunlink(const char *pathname) { if (unlink(pathname)) bb_perror_msg_and_die("can't remove file '%s'", pathname); } void FAST_FUNC xrename(const char *oldpath, const char *newpath) { if (rename(oldpath, newpath)) bb_perror_msg_and_die("can't move '%s' to '%s'", oldpath, newpath); } int FAST_FUNC rename_or_warn(const char *oldpath, const char *newpath) { int n = rename(oldpath, newpath); if (n) bb_perror_msg("can't move '%s' to '%s'", oldpath, newpath); return n; } void FAST_FUNC xpipe(int filedes[2]) { if (pipe(filedes)) bb_perror_msg_and_die("can't create pipe"); } void FAST_FUNC xdup2(int from, int to) { if (dup2(from, to) != to) bb_perror_msg_and_die("can't duplicate file descriptor"); } // "Renumber" opened fd void FAST_FUNC xmove_fd(int from, int to) { if (from == to) return; xdup2(from, to); close(from); } // Die with an error message if we can't write the entire buffer. void FAST_FUNC xwrite(int fd, const void *buf, size_t count) { if (count) { ssize_t size = full_write(fd, buf, count); if ((size_t)size != count) bb_error_msg_and_die("short write"); } } void FAST_FUNC xwrite_str(int fd, const char *str) { xwrite(fd, str, strlen(str)); } void FAST_FUNC xclose(int fd) { if (close(fd)) bb_perror_msg_and_die("close failed"); } // Die with an error message if we can't lseek to the right spot. off_t FAST_FUNC xlseek(int fd, off_t offset, int whence) { off_t off = lseek(fd, offset, whence); if (off == (off_t)-1) { if (whence == SEEK_SET) bb_perror_msg_and_die("lseek(%"OFF_FMT"u)", offset); bb_perror_msg_and_die("lseek"); } return off; } int FAST_FUNC xmkstemp(char *template) { int fd = mkstemp(template); if (fd < 0) bb_perror_msg_and_die("can't create temp file '%s'", template); return fd; } // Die with supplied filename if this FILE* has ferror set. void FAST_FUNC die_if_ferror(FILE *fp, const char *fn) { if (ferror(fp)) { /* ferror doesn't set useful errno */ bb_error_msg_and_die("%s: I/O error", fn); } } // Die with an error message if stdout has ferror set. void FAST_FUNC die_if_ferror_stdout(void) { die_if_ferror(stdout, bb_msg_standard_output); } int FAST_FUNC fflush_all(void) { return fflush(NULL); } int FAST_FUNC bb_putchar(int ch) { return putchar(ch); } /* Die with an error message if we can't copy an entire FILE* to stdout, * then close that file. */ void FAST_FUNC xprint_and_close_file(FILE *file) { fflush_all(); // copyfd outputs error messages for us. if (bb_copyfd_eof(fileno(file), STDOUT_FILENO) == -1) xfunc_die(); fclose(file); } // Die with an error message if we can't malloc() enough space and do an // sprintf() into that space. char* FAST_FUNC xasprintf(const char *format, ...) { va_list p; int r; char *string_ptr; va_start(p, format); r = vasprintf(&string_ptr, format, p); va_end(p); if (r < 0) bb_error_msg_and_die(bb_msg_memory_exhausted); return string_ptr; } void FAST_FUNC xsetenv(const char *key, const char *value) { if (setenv(key, value, 1)) bb_error_msg_and_die(bb_msg_memory_exhausted); } /* Handles "VAR=VAL" strings, even those which are part of environ * _right now_ */ void FAST_FUNC bb_unsetenv(const char *var) { char *tp = strchr(var, '='); if (!tp) { unsetenv(var); return; } /* In case var was putenv'ed, we can't replace '=' * with NUL and unsetenv(var) - it won't work, * env is modified by the replacement, unsetenv * sees "VAR" instead of "VAR=VAL" and does not remove it! * horror :( */ tp = xstrndup(var, tp - var); unsetenv(tp); free(tp); } void FAST_FUNC bb_unsetenv_and_free(char *var) { bb_unsetenv(var); free(var); } // Die with an error message if we can't set gid. (Because resource limits may // limit this user to a given number of processes, and if that fills up the // setgid() will fail and we'll _still_be_root_, which is bad.) void FAST_FUNC xsetgid(gid_t gid) { if (setgid(gid)) bb_perror_msg_and_die("setgid"); } // Die with an error message if we can't set uid. (See xsetgid() for why.) void FAST_FUNC xsetuid(uid_t uid) { if (setuid(uid)) bb_perror_msg_and_die("setuid"); } void FAST_FUNC xsetegid(gid_t egid) { if (setegid(egid)) bb_perror_msg_and_die("setegid"); } void FAST_FUNC xseteuid(uid_t euid) { if (seteuid(euid)) bb_perror_msg_and_die("seteuid"); } // Die if we can't chdir to a new path. void FAST_FUNC xchdir(const char *path) { if (chdir(path)) bb_perror_msg_and_die("can't change directory to '%s'", path); } void FAST_FUNC xchroot(const char *path) { if (chroot(path)) bb_perror_msg_and_die("can't change root directory to '%s'", path); xchdir("/"); } // Print a warning message if opendir() fails, but don't die. DIR* FAST_FUNC warn_opendir(const char *path) { DIR *dp; dp = opendir(path); if (!dp) bb_perror_msg("can't open '%s'", path); return dp; } // Die with an error message if opendir() fails. DIR* FAST_FUNC xopendir(const char *path) { DIR *dp; dp = opendir(path); if (!dp) bb_perror_msg_and_die("can't open '%s'", path); return dp; } // Die with an error message if we can't open a new socket. int FAST_FUNC xsocket(int domain, int type, int protocol) { int r = socket(domain, type, protocol); if (r < 0) { /* Hijack vaguely related config option */ #if ENABLE_VERBOSE_RESOLUTION_ERRORS const char *s = "INET"; # ifdef AF_PACKET if (domain == AF_PACKET) s = "PACKET"; # endif # ifdef AF_NETLINK if (domain == AF_NETLINK) s = "NETLINK"; # endif IF_FEATURE_IPV6(if (domain == AF_INET6) s = "INET6";) bb_perror_msg_and_die("socket(AF_%s,%d,%d)", s, type, protocol); #else bb_perror_msg_and_die("socket"); #endif } return r; } // Die with an error message if we can't bind a socket to an address. void FAST_FUNC xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) { if (bind(sockfd, my_addr, addrlen)) bb_perror_msg_and_die("bind"); } // Die with an error message if we can't listen for connections on a socket. void FAST_FUNC xlisten(int s, int backlog) { if (listen(s, backlog)) bb_perror_msg_and_die("listen"); } /* Die with an error message if sendto failed. * Return bytes sent otherwise */ ssize_t FAST_FUNC xsendto(int s, const void *buf, size_t len, const struct sockaddr *to, socklen_t tolen) { ssize_t ret = sendto(s, buf, len, 0, to, tolen); if (ret < 0) { if (ENABLE_FEATURE_CLEAN_UP) close(s); bb_perror_msg_and_die("sendto"); } return ret; } // xstat() - a stat() which dies on failure with meaningful error message void FAST_FUNC xstat(const char *name, struct stat *stat_buf) { if (stat(name, stat_buf)) bb_perror_msg_and_die("can't stat '%s'", name); } void FAST_FUNC xfstat(int fd, struct stat *stat_buf, const char *errmsg) { /* errmsg is usually a file name, but not always: * xfstat may be called in a spot where file name is no longer * available, and caller may give e.g. "can't stat input file" string. */ if (fstat(fd, stat_buf)) bb_simple_perror_msg_and_die(errmsg); } // selinux_or_die() - die if SELinux is disabled. void FAST_FUNC selinux_or_die(void) { #if ENABLE_SELINUX int rc = is_selinux_enabled(); if (rc == 0) { bb_error_msg_and_die("SELinux is disabled"); } else if (rc < 0) { bb_error_msg_and_die("is_selinux_enabled() failed"); } #else bb_error_msg_and_die("SELinux support is disabled"); #endif } int FAST_FUNC ioctl_or_perror_and_die(int fd, unsigned request, void *argp, const char *fmt,...) { int ret; va_list p; ret = ioctl(fd, request, argp); if (ret < 0) { va_start(p, fmt); bb_verror_msg(fmt, p, strerror(errno)); /* xfunc_die can actually longjmp, so be nice */ va_end(p); xfunc_die(); } return ret; } int FAST_FUNC ioctl_or_perror(int fd, unsigned request, void *argp, const char *fmt,...) { va_list p; int ret = ioctl(fd, request, argp); if (ret < 0) { va_start(p, fmt); bb_verror_msg(fmt, p, strerror(errno)); va_end(p); } return ret; } #if ENABLE_IOCTL_HEX2STR_ERROR int FAST_FUNC bb_ioctl_or_warn(int fd, unsigned request, void *argp, const char *ioctl_name) { int ret; ret = ioctl(fd, request, argp); if (ret < 0) bb_simple_perror_msg(ioctl_name); return ret; } int FAST_FUNC bb_xioctl(int fd, unsigned request, void *argp, const char *ioctl_name) { int ret; ret = ioctl(fd, request, argp); if (ret < 0) bb_simple_perror_msg_and_die(ioctl_name); return ret; } #else int FAST_FUNC bb_ioctl_or_warn(int fd, unsigned request, void *argp) { int ret; ret = ioctl(fd, request, argp); if (ret < 0) bb_perror_msg("ioctl %#x failed", request); return ret; } int FAST_FUNC bb_xioctl(int fd, unsigned request, void *argp) { int ret; ret = ioctl(fd, request, argp); if (ret < 0) bb_perror_msg_and_die("ioctl %#x failed", request); return ret; } #endif char* FAST_FUNC xmalloc_ttyname(int fd) { char buf[128]; int r = ttyname_r(fd, buf, sizeof(buf) - 1); if (r) return NULL; return xstrdup(buf); } void FAST_FUNC generate_uuid(uint8_t *buf) { /* http://www.ietf.org/rfc/rfc4122.txt * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | time_low | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | time_mid | time_hi_and_version | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |clk_seq_and_variant | node (0-1) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | node (2-5) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * IOW, uuid has this layout: * uint32_t time_low (big endian) * uint16_t time_mid (big endian) * uint16_t time_hi_and_version (big endian) * version is a 4-bit field: * 1 Time-based * 2 DCE Security, with embedded POSIX UIDs * 3 Name-based (MD5) * 4 Randomly generated * 5 Name-based (SHA-1) * uint16_t clk_seq_and_variant (big endian) * variant is a 3-bit field: * 0xx Reserved, NCS backward compatibility * 10x The variant specified in rfc4122 * 110 Reserved, Microsoft backward compatibility * 111 Reserved for future definition * uint8_t node[6] * * For version 4, these bits are set/cleared: * time_hi_and_version & 0x0fff | 0x4000 * clk_seq_and_variant & 0x3fff | 0x8000 */ pid_t pid; int i; i = open("/dev/urandom", O_RDONLY); if (i >= 0) { read(i, buf, 16); close(i); } /* Paranoia. /dev/urandom may be missing. * rand() is guaranteed to generate at least [0, 2^15) range, * but lowest bits in some libc are not so "random". */ srand(monotonic_us()); /* pulls in printf */ pid = getpid(); while (1) { for (i = 0; i < 16; i++) buf[i] ^= rand() >> 5; if (pid == 0) break; srand(pid); pid = 0; } /* version = 4 */ buf[4 + 2 ] = (buf[4 + 2 ] & 0x0f) | 0x40; /* variant = 10x */ buf[4 + 2 + 2] = (buf[4 + 2 + 2] & 0x3f) | 0x80; } #if BB_MMU pid_t FAST_FUNC xfork(void) { pid_t pid; pid = fork(); if (pid < 0) /* wtf? */ bb_perror_msg_and_die("vfork"+1); return pid; } #endif busybox-1.22.1/libbb/read_key.c0000644000000000000000000002117212263563520014776 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 2008 Rob Landley * Copyright (C) 2008 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout) { struct pollfd pfd; const char *seq; int n; /* Known escape sequences for cursor and function keys. * See "Xterm Control Sequences" * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html */ static const char esccmds[] ALIGN1 = { 'O','A' |0x80,KEYCODE_UP , 'O','B' |0x80,KEYCODE_DOWN , 'O','C' |0x80,KEYCODE_RIGHT , 'O','D' |0x80,KEYCODE_LEFT , 'O','H' |0x80,KEYCODE_HOME , 'O','F' |0x80,KEYCODE_END , #if 0 'O','P' |0x80,KEYCODE_FUN1 , /* [ESC] ESC O [2] P - [Alt-][Shift-]F1 */ /* ESC [ O 1 ; 2 P - Shift-F1 */ /* ESC [ O 1 ; 3 P - Alt-F1 */ /* ESC [ O 1 ; 4 P - Alt-Shift-F1 */ /* ESC [ O 1 ; 5 P - Ctrl-F1 */ /* ESC [ O 1 ; 6 P - Ctrl-Shift-F1 */ 'O','Q' |0x80,KEYCODE_FUN2 , 'O','R' |0x80,KEYCODE_FUN3 , 'O','S' |0x80,KEYCODE_FUN4 , #endif '[','A' |0x80,KEYCODE_UP , '[','B' |0x80,KEYCODE_DOWN , '[','C' |0x80,KEYCODE_RIGHT , '[','D' |0x80,KEYCODE_LEFT , /* ESC [ 1 ; 2 x, where x = A/B/C/D: Shift- */ /* ESC [ 1 ; 3 x, where x = A/B/C/D: Alt- - implemented below */ /* ESC [ 1 ; 4 x, where x = A/B/C/D: Alt-Shift- */ /* ESC [ 1 ; 5 x, where x = A/B/C/D: Ctrl- - implemented below */ /* ESC [ 1 ; 6 x, where x = A/B/C/D: Ctrl-Shift- */ /* ESC [ 1 ; 7 x, where x = A/B/C/D: Ctrl-Alt- */ /* ESC [ 1 ; 8 x, where x = A/B/C/D: Ctrl-Alt-Shift- */ '[','H' |0x80,KEYCODE_HOME , /* xterm */ '[','F' |0x80,KEYCODE_END , /* xterm */ /* [ESC] ESC [ [2] H - [Alt-][Shift-]Home (End similarly?) */ /* '[','Z' |0x80,KEYCODE_SHIFT_TAB, */ '[','1','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ '[','2','~' |0x80,KEYCODE_INSERT , /* ESC [ 2 ; 3 ~ - Alt-Insert */ '[','3','~' |0x80,KEYCODE_DELETE , /* [ESC] ESC [ 3 [;2] ~ - [Alt-][Shift-]Delete */ /* ESC [ 3 ; 3 ~ - Alt-Delete */ /* ESC [ 3 ; 5 ~ - Ctrl-Delete */ '[','4','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ '[','5','~' |0x80,KEYCODE_PAGEUP , /* ESC [ 5 ; 3 ~ - Alt-PgUp */ /* ESC [ 5 ; 5 ~ - Ctrl-PgUp */ /* ESC [ 5 ; 7 ~ - Ctrl-Alt-PgUp */ '[','6','~' |0x80,KEYCODE_PAGEDOWN, '[','7','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ '[','8','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ #if 0 '[','1','1','~'|0x80,KEYCODE_FUN1 , /* old xterm, deprecated by ESC O P */ '[','1','2','~'|0x80,KEYCODE_FUN2 , /* old xterm... */ '[','1','3','~'|0x80,KEYCODE_FUN3 , /* old xterm... */ '[','1','4','~'|0x80,KEYCODE_FUN4 , /* old xterm... */ '[','1','5','~'|0x80,KEYCODE_FUN5 , /* [ESC] ESC [ 1 5 [;2] ~ - [Alt-][Shift-]F5 */ '[','1','7','~'|0x80,KEYCODE_FUN6 , '[','1','8','~'|0x80,KEYCODE_FUN7 , '[','1','9','~'|0x80,KEYCODE_FUN8 , '[','2','0','~'|0x80,KEYCODE_FUN9 , '[','2','1','~'|0x80,KEYCODE_FUN10 , '[','2','3','~'|0x80,KEYCODE_FUN11 , '[','2','4','~'|0x80,KEYCODE_FUN12 , /* ESC [ 2 4 ; 2 ~ - Shift-F12 */ /* ESC [ 2 4 ; 3 ~ - Alt-F12 */ /* ESC [ 2 4 ; 4 ~ - Alt-Shift-F12 */ /* ESC [ 2 4 ; 5 ~ - Ctrl-F12 */ /* ESC [ 2 4 ; 6 ~ - Ctrl-Shift-F12 */ #endif /* '[','1',';','5','A' |0x80,KEYCODE_CTRL_UP , - unused */ /* '[','1',';','5','B' |0x80,KEYCODE_CTRL_DOWN , - unused */ '[','1',';','5','C' |0x80,KEYCODE_CTRL_RIGHT, '[','1',';','5','D' |0x80,KEYCODE_CTRL_LEFT , /* '[','1',';','3','A' |0x80,KEYCODE_ALT_UP , - unused */ /* '[','1',';','3','B' |0x80,KEYCODE_ALT_DOWN , - unused */ '[','1',';','3','C' |0x80,KEYCODE_ALT_RIGHT, '[','1',';','3','D' |0x80,KEYCODE_ALT_LEFT , /* '[','3',';','3','~' |0x80,KEYCODE_ALT_DELETE, - unused */ 0 }; pfd.fd = fd; pfd.events = POLLIN; buffer++; /* saved chars counter is in buffer[-1] now */ start_over: errno = 0; n = (unsigned char)buffer[-1]; if (n == 0) { /* If no data, wait for input. * If requested, wait TIMEOUT ms. TIMEOUT = -1 is useful * if fd can be in non-blocking mode. */ if (timeout >= -1) { if (safe_poll(&pfd, 1, timeout) == 0) { /* Timed out */ errno = EAGAIN; return -1; } } /* It is tempting to read more than one byte here, * but it breaks pasting. Example: at shell prompt, * user presses "c","a","t" and then pastes "\nline\n". * When we were reading 3 bytes here, we were eating * "li" too, and cat was getting wrong input. */ n = safe_read(fd, buffer, 1); if (n <= 0) return -1; } { unsigned char c = buffer[0]; n--; if (n) memmove(buffer, buffer + 1, n); /* Only ESC starts ESC sequences */ if (c != 27) { buffer[-1] = n; return c; } } /* Loop through known ESC sequences */ seq = esccmds; while (*seq != '\0') { /* n - position in sequence we did not read yet */ int i = 0; /* position in sequence to compare */ /* Loop through chars in this sequence */ while (1) { /* So far escape sequence matched up to [i-1] */ if (n <= i) { /* Need more chars, read another one if it wouldn't block. * Note that escape sequences come in as a unit, * so if we block for long it's not really an escape sequence. * Timeout is needed to reconnect escape sequences * split up by transmission over a serial console. */ if (safe_poll(&pfd, 1, 50) == 0) { /* No more data! * Array is sorted from shortest to longest, * we can't match anything later in array - * anything later is longer than this seq. * Break out of both loops. */ goto got_all; } errno = 0; if (safe_read(fd, buffer + n, 1) <= 0) { /* If EAGAIN, then fd is O_NONBLOCK and poll lied: * in fact, there is no data. */ if (errno != EAGAIN) { /* otherwise: it's EOF/error */ buffer[-1] = 0; return -1; } goto got_all; } n++; } if (buffer[i] != (seq[i] & 0x7f)) { /* This seq doesn't match, go to next */ seq += i; /* Forward to last char */ while (!(*seq & 0x80)) seq++; /* Skip it and the keycode which follows */ seq += 2; break; } if (seq[i] & 0x80) { /* Entire seq matched */ n = 0; /* n -= i; memmove(...); * would be more correct, * but we never read ahead that much, * and n == i here. */ buffer[-1] = 0; return (signed char)seq[i+1]; } i++; } } /* We did not find matching sequence. * We possibly read and stored more input in buffer[] by now. * n = bytes read. Try to read more until we time out. */ while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for count byte at buffer[-1] */ if (safe_poll(&pfd, 1, 50) == 0) { /* No more data! */ break; } errno = 0; if (safe_read(fd, buffer + n, 1) <= 0) { /* If EAGAIN, then fd is O_NONBLOCK and poll lied: * in fact, there is no data. */ if (errno != EAGAIN) { /* otherwise: it's EOF/error */ buffer[-1] = 0; return -1; } break; } n++; /* Try to decipher "ESC [ NNN ; NNN R" sequence */ if ((ENABLE_FEATURE_EDITING_ASK_TERMINAL || ENABLE_FEATURE_VI_ASK_TERMINAL || ENABLE_FEATURE_LESS_ASK_TERMINAL ) && n >= 5 && buffer[0] == '[' && buffer[n-1] == 'R' && isdigit(buffer[1]) ) { char *end; unsigned long row, col; row = strtoul(buffer + 1, &end, 10); if (*end != ';' || !isdigit(end[1])) continue; col = strtoul(end + 1, &end, 10); if (*end != 'R') continue; if (row < 1 || col < 1 || (row | col) > 0x7fff) continue; buffer[-1] = 0; /* Pack into "1 " 32-bit sequence */ col |= (((-1 << 15) | row) << 16); /* Return it in high-order word */ return ((int64_t) col << 32) | (uint32_t)KEYCODE_CURSOR_POS; } } got_all: if (n <= 1) { /* Alt-x is usually returned as ESC x. * Report ESC, x is remembered for the next call. */ buffer[-1] = n; return 27; } /* We were doing "buffer[-1] = n; return c;" here, but this results * in unknown key sequences being interpreted as ESC + garbage. * This was not useful. Pretend there was no key pressed, * go and wait for a new keypress: */ buffer[-1] = 0; goto start_over; } void FAST_FUNC read_key_ungets(char *buffer, const char *str, unsigned len) { unsigned cur_len = (unsigned char)buffer[0]; if (len > KEYCODE_BUFFER_SIZE-1 - cur_len) len = KEYCODE_BUFFER_SIZE-1 - cur_len; memcpy(buffer + 1 + cur_len, str, len); buffer[0] += len; } busybox-1.22.1/libbb/systemd_support.c0000644000000000000000000000403012263563520016471 0ustar rootroot/* * Copyright (C) 2011 Davide Cavalca * * Based on http://cgit.freedesktop.org/systemd/tree/src/sd-daemon.c * Copyright 2010 Lennart Poettering * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "libbb.h" //config:config FEATURE_SYSTEMD //config: bool "Enable systemd support" //config: default y //config: help //config: If you plan to use busybox daemons on a system where daemons //config: are controlled by systemd, enable this option. //config: If you don't use systemd, it is still safe to enable it, //config: but the downside is increased code size. //kbuild:lib-$(CONFIG_FEATURE_SYSTEMD) += systemd_support.o int sd_listen_fds(void) { const char *e; int n; int fd; e = getenv("LISTEN_PID"); if (!e) return 0; n = xatoi_positive(e); /* Is this for us? */ if (getpid() != (pid_t) n) return 0; e = getenv("LISTEN_FDS"); if (!e) return 0; n = xatoi_positive(e); for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) close_on_exec_on(fd); return n; } busybox-1.22.1/libbb/wfopen.c0000644000000000000000000000210412263563520014503 0ustar rootroot/* 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 source tree. */ #include "libbb.h" FILE* FAST_FUNC fopen_or_warn(const char *path, const char *mode) { FILE *fp = fopen(path, mode); if (!fp) { bb_simple_perror_msg(path); //errno = 0; /* why? */ } return fp; } FILE* FAST_FUNC fopen_for_read(const char *path) { return fopen(path, "r"); } FILE* FAST_FUNC xfopen_for_read(const char *path) { return xfopen(path, "r"); } FILE* FAST_FUNC fopen_for_write(const char *path) { return fopen(path, "w"); } FILE* FAST_FUNC xfopen_for_write(const char *path) { return xfopen(path, "w"); } static FILE* xfdopen_helper(unsigned fd_and_rw_bit) { FILE* fp = fdopen(fd_and_rw_bit >> 1, fd_and_rw_bit & 1 ? "w" : "r"); if (!fp) bb_error_msg_and_die(bb_msg_memory_exhausted); return fp; } FILE* FAST_FUNC xfdopen_for_read(int fd) { return xfdopen_helper(fd << 1); } FILE* FAST_FUNC xfdopen_for_write(int fd) { return xfdopen_helper((fd << 1) + 1); } busybox-1.22.1/libbb/unicode.c0000644000000000000000000007677712263563520014666 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Unicode support routines. * * Copyright (C) 2009 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" #include "unicode.h" /* If it's not #defined as a constant in unicode.h... */ #ifndef unicode_status uint8_t unicode_status; #endif /* This file is compiled only if UNICODE_SUPPORT is on. * We check other options and decide whether to use libc support * via locale, or use our own logic: */ #if ENABLE_UNICODE_USING_LOCALE /* Unicode support using libc locale support. */ void FAST_FUNC reinit_unicode(const char *LANG) { static const char unicode_0x394[] = { 0xce, 0x94, 0 }; size_t width; /* We pass "" instead of "C" because some libc's have * non-ASCII default locale for setlocale("") call * (this allows users of such libc to have Unicoded * system without having to mess with env). * * We set LC_CTYPE because (a) we may be called with $LC_CTYPE * value in LANG, not with $LC_ALL, (b) internationalized * LC_NUMERIC and LC_TIME are more PITA than benefit * (for one, some utilities have hard time with comma * used as a fractional separator). */ //TODO: avoid repeated calls by caching last string? setlocale(LC_CTYPE, LANG ? LANG : ""); /* In unicode, this is a one character string */ width = unicode_strlen(unicode_0x394); unicode_status = (width == 1 ? UNICODE_ON : UNICODE_OFF); } void FAST_FUNC init_unicode(void) { /* Some people set only $LC_CTYPE, not $LC_ALL, because they want * only Unicode to be activated on their system, not the whole * shebang of wrong decimal points, strange date formats and so on. */ if (unicode_status == UNICODE_UNKNOWN) { char *s = getenv("LC_ALL"); if (!s) s = getenv("LC_CTYPE"); if (!s) s = getenv("LANG"); reinit_unicode(s); } } #else /* Homegrown Unicode support. It knows only C and Unicode locales. */ # if ENABLE_FEATURE_CHECK_UNICODE_IN_ENV void FAST_FUNC reinit_unicode(const char *LANG) { unicode_status = UNICODE_OFF; if (!LANG || !(strstr(LANG, ".utf") || strstr(LANG, ".UTF"))) return; unicode_status = UNICODE_ON; } void FAST_FUNC init_unicode(void) { if (unicode_status == UNICODE_UNKNOWN) { char *s = getenv("LC_ALL"); if (!s) s = getenv("LC_CTYPE"); if (!s) s = getenv("LANG"); reinit_unicode(s); } } # endif static size_t wcrtomb_internal(char *s, wchar_t wc) { int n, i; uint32_t v = wc; if (v <= 0x7f) { *s = v; return 1; } /* RFC 3629 says that Unicode ends at 10FFFF, * but we cover entire 32 bits */ /* 4000000-FFFFFFFF -> 111111tt 10tttttt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */ /* 200000-3FFFFFF -> 111110tt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */ /* 10000-1FFFFF -> 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx */ /* 800-FFFF -> 1110yyyy 10yyyyxx 10xxxxxx */ /* 80-7FF -> 110yyyxx 10xxxxxx */ /* How many bytes do we need? */ n = 2; /* (0x80000000+ would result in n = 7, limiting n to 6) */ while (v >= 0x800 && n < 6) { v >>= 5; n++; } /* Fill bytes n-1..1 */ i = n; while (--i) { s[i] = (wc & 0x3f) | 0x80; wc >>= 6; } /* Fill byte 0 */ s[0] = wc | (uint8_t)(0x3f00 >> n); return n; } size_t FAST_FUNC wcrtomb(char *s, wchar_t wc, mbstate_t *ps UNUSED_PARAM) { if (unicode_status != UNICODE_ON) { *s = wc; return 1; } return wcrtomb_internal(s, wc); } size_t FAST_FUNC wcstombs(char *dest, const wchar_t *src, size_t n) { size_t org_n = n; if (unicode_status != UNICODE_ON) { while (n) { wchar_t c = *src++; *dest++ = c; if (c == 0) break; n--; } return org_n - n; } while (n >= MB_CUR_MAX) { wchar_t wc = *src++; size_t len = wcrtomb_internal(dest, wc); if (wc == L'\0') return org_n - n; dest += len; n -= len; } while (n) { char tbuf[MB_CUR_MAX]; wchar_t wc = *src++; size_t len = wcrtomb_internal(tbuf, wc); if (len > n) break; memcpy(dest, tbuf, len); if (wc == L'\0') return org_n - n; dest += len; n -= len; } return org_n - n; } # define ERROR_WCHAR (~(wchar_t)0) static const char *mbstowc_internal(wchar_t *res, const char *src) { int bytes; unsigned c = (unsigned char) *src++; if (c <= 0x7f) { *res = c; return src; } /* 80-7FF -> 110yyyxx 10xxxxxx */ /* 800-FFFF -> 1110yyyy 10yyyyxx 10xxxxxx */ /* 10000-1FFFFF -> 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx */ /* 200000-3FFFFFF -> 111110tt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */ /* 4000000-FFFFFFFF -> 111111tt 10tttttt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */ bytes = 0; do { c <<= 1; bytes++; } while ((c & 0x80) && bytes < 6); if (bytes == 1) { /* A bare "continuation" byte. Say, 80 */ *res = ERROR_WCHAR; return src; } c = (uint8_t)(c) >> bytes; while (--bytes) { unsigned ch = (unsigned char) *src; if ((ch & 0xc0) != 0x80) { /* Missing "continuation" byte. Example: e0 80 */ *res = ERROR_WCHAR; return src; } c = (c << 6) + (ch & 0x3f); src++; } /* TODO */ /* Need to check that c isn't produced by overlong encoding */ /* Example: 11000000 10000000 converts to NUL */ /* 11110000 10000000 10000100 10000000 converts to 0x100 */ /* correct encoding: 11000100 10000000 */ if (c <= 0x7f) { /* crude check */ *res = ERROR_WCHAR; return src; } *res = c; return src; } size_t FAST_FUNC mbstowcs(wchar_t *dest, const char *src, size_t n) { size_t org_n = n; if (unicode_status != UNICODE_ON) { while (n) { unsigned char c = *src++; if (dest) *dest++ = c; if (c == 0) break; n--; } return org_n - n; } while (n) { wchar_t wc; src = mbstowc_internal(&wc, src); if (wc == ERROR_WCHAR) /* error */ return (size_t) -1L; if (dest) *dest++ = wc; if (wc == 0) /* end-of-string */ break; n--; } return org_n - n; } int FAST_FUNC iswspace(wint_t wc) { return (unsigned)wc <= 0x7f && isspace(wc); } int FAST_FUNC iswalnum(wint_t wc) { return (unsigned)wc <= 0x7f && isalnum(wc); } int FAST_FUNC iswpunct(wint_t wc) { return (unsigned)wc <= 0x7f && ispunct(wc); } # if CONFIG_LAST_SUPPORTED_WCHAR >= 0x300 struct interval { uint16_t first; uint16_t last; }; /* auxiliary function for binary search in interval table */ static int in_interval_table(unsigned ucs, const struct interval *table, unsigned max) { unsigned min; unsigned mid; if (ucs < table[0].first || ucs > table[max].last) return 0; min = 0; while (max >= min) { mid = (min + max) / 2; if (ucs > table[mid].last) min = mid + 1; else if (ucs < table[mid].first) max = mid - 1; else return 1; } return 0; } static int in_uint16_table(unsigned ucs, const uint16_t *table, unsigned max) { unsigned min; unsigned mid; unsigned first, last; first = table[0] >> 2; last = first + (table[0] & 3); if (ucs < first || ucs > last) return 0; min = 0; while (max >= min) { mid = (min + max) / 2; first = table[mid] >> 2; last = first + (table[mid] & 3); if (ucs > last) min = mid + 1; else if (ucs < first) max = mid - 1; else return 1; } return 0; } # endif /* * This is an implementation of wcwidth() and wcswidth() (defined in * IEEE Std 1002.1-2001) for Unicode. * * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html * * In fixed-width output devices, Latin characters all occupy a single * "cell" position of equal width, whereas ideographic CJK characters * occupy two such cells. Interoperability between terminal-line * applications and (teletype-style) character terminals using the * UTF-8 encoding requires agreement on which character should advance * the cursor by how many cell positions. No established formal * standards exist at present on which Unicode character shall occupy * how many cell positions on character terminals. These routines are * a first attempt of defining such behavior based on simple rules * applied to data provided by the Unicode Consortium. * * For some graphical characters, the Unicode standard explicitly * defines a character-cell width via the definition of the East Asian * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. * In all these cases, there is no ambiguity about which width a * terminal shall use. For characters in the East Asian Ambiguous (A) * class, the width choice depends purely on a preference of backward * compatibility with either historic CJK or Western practice. * Choosing single-width for these characters is easy to justify as * the appropriate long-term solution, as the CJK practice of * displaying these characters as double-width comes from historic * implementation simplicity (8-bit encoded characters were displayed * single-width and 16-bit ones double-width, even for Greek, * Cyrillic, etc.) and not any typographic considerations. * * Much less clear is the choice of width for the Not East Asian * (Neutral) class. Existing practice does not dictate a width for any * of these characters. It would nevertheless make sense * typographically to allocate two character cells to characters such * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be * represented adequately with a single-width glyph. The following * routines at present merely assign a single-cell width to all * neutral characters, in the interest of simplicity. This is not * entirely satisfactory and should be reconsidered before * establishing a formal standard in this area. At the moment, the * decision which Not East Asian (Neutral) characters should be * represented by double-width glyphs cannot yet be answered by * applying a simple rule from the Unicode database content. Setting * up a proper standard for the behavior of UTF-8 character terminals * will require a careful analysis not only of each Unicode character, * but also of each presentation form, something the author of these * routines has avoided to do so far. * * http://www.unicode.org/unicode/reports/tr11/ * * Markus Kuhn -- 2007-05-26 (Unicode 5.0) * * Permission to use, copy, modify, and distribute this software * for any purpose and without fee is hereby granted. The author * disclaims all warranties with regard to this software. * * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c */ /* Assigned Unicode character ranges: * Plane Range * 0 0000–FFFF Basic Multilingual Plane * 1 10000–1FFFF Supplementary Multilingual Plane * 2 20000–2FFFF Supplementary Ideographic Plane * 3 30000-3FFFF Tertiary Ideographic Plane (no chars assigned yet) * 4-13 40000–DFFFF currently unassigned * 14 E0000–EFFFF Supplementary Special-purpose Plane * 15 F0000–FFFFF Supplementary Private Use Area-A * 16 100000–10FFFF Supplementary Private Use Area-B * * "Supplementary Special-purpose Plane currently contains non-graphical * characters in two blocks of 128 and 240 characters. The first block * is for language tag characters for use when language cannot be indicated * through other protocols (such as the xml:lang attribute in XML). * The other block contains glyph variation selectors to indicate * an alternate glyph for a character that cannot be determined by context." * * In simpler terms: it is a tool to fix the "Han unification" mess * created by Unicode committee, to select Chinese/Japanese/Korean/Taiwan * version of a character. (They forgot that the whole purpose of the Unicode * was to be able to write all chars in one charset without such tricks). * Until East Asian users say it is actually necessary to support these * code points in console applications like busybox * (i.e. do these chars ever appear in filenames, hostnames, text files * and such?), we are treating these code points as invalid. * * Tertiary Ideographic Plane is also ignored for now, * until Unicode committee assigns something there. */ /* The following two functions define the column width of an ISO 10646 * character as follows: * * - The null character (U+0000) has a column width of 0. * * - Other C0/C1 control characters and DEL will lead to a return * value of -1. * * - Non-spacing and enclosing combining characters (general * category code Mn or Me in the Unicode database) have a * column width of 0. * * - SOFT HYPHEN (U+00AD) has a column width of 1. * * - Other format characters (general category code Cf in the Unicode * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. * * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) * have a column width of 0. * * - Spacing characters in the East Asian Wide (W) or East Asian * Full-width (F) category as defined in Unicode Technical * Report #11 have a column width of 2. * * - All remaining characters (including all printable * ISO 8859-1 and WGL4 characters, Unicode control characters, * etc.) have a column width of 1. * * This implementation assumes that wchar_t characters are encoded * in ISO 10646. */ int FAST_FUNC wcwidth(unsigned ucs) { # if CONFIG_LAST_SUPPORTED_WCHAR >= 0x300 /* sorted list of non-overlapping intervals of non-spacing characters */ /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ # define BIG_(a,b) { a, b }, # define PAIR(a,b) # define ARRAY /* PAIR if < 0x4000 and no more than 4 chars big */ \ BIG_(0x0300, 0x036F) \ PAIR(0x0483, 0x0486) \ PAIR(0x0488, 0x0489) \ BIG_(0x0591, 0x05BD) \ PAIR(0x05BF, 0x05BF) \ PAIR(0x05C1, 0x05C2) \ PAIR(0x05C4, 0x05C5) \ PAIR(0x05C7, 0x05C7) \ PAIR(0x0600, 0x0603) \ BIG_(0x0610, 0x0615) \ BIG_(0x064B, 0x065E) \ PAIR(0x0670, 0x0670) \ BIG_(0x06D6, 0x06E4) \ PAIR(0x06E7, 0x06E8) \ PAIR(0x06EA, 0x06ED) \ PAIR(0x070F, 0x070F) \ PAIR(0x0711, 0x0711) \ BIG_(0x0730, 0x074A) \ BIG_(0x07A6, 0x07B0) \ BIG_(0x07EB, 0x07F3) \ PAIR(0x0901, 0x0902) \ PAIR(0x093C, 0x093C) \ BIG_(0x0941, 0x0948) \ PAIR(0x094D, 0x094D) \ PAIR(0x0951, 0x0954) \ PAIR(0x0962, 0x0963) \ PAIR(0x0981, 0x0981) \ PAIR(0x09BC, 0x09BC) \ PAIR(0x09C1, 0x09C4) \ PAIR(0x09CD, 0x09CD) \ PAIR(0x09E2, 0x09E3) \ PAIR(0x0A01, 0x0A02) \ PAIR(0x0A3C, 0x0A3C) \ PAIR(0x0A41, 0x0A42) \ PAIR(0x0A47, 0x0A48) \ PAIR(0x0A4B, 0x0A4D) \ PAIR(0x0A70, 0x0A71) \ PAIR(0x0A81, 0x0A82) \ PAIR(0x0ABC, 0x0ABC) \ BIG_(0x0AC1, 0x0AC5) \ PAIR(0x0AC7, 0x0AC8) \ PAIR(0x0ACD, 0x0ACD) \ PAIR(0x0AE2, 0x0AE3) \ PAIR(0x0B01, 0x0B01) \ PAIR(0x0B3C, 0x0B3C) \ PAIR(0x0B3F, 0x0B3F) \ PAIR(0x0B41, 0x0B43) \ PAIR(0x0B4D, 0x0B4D) \ PAIR(0x0B56, 0x0B56) \ PAIR(0x0B82, 0x0B82) \ PAIR(0x0BC0, 0x0BC0) \ PAIR(0x0BCD, 0x0BCD) \ PAIR(0x0C3E, 0x0C40) \ PAIR(0x0C46, 0x0C48) \ PAIR(0x0C4A, 0x0C4D) \ PAIR(0x0C55, 0x0C56) \ PAIR(0x0CBC, 0x0CBC) \ PAIR(0x0CBF, 0x0CBF) \ PAIR(0x0CC6, 0x0CC6) \ PAIR(0x0CCC, 0x0CCD) \ PAIR(0x0CE2, 0x0CE3) \ PAIR(0x0D41, 0x0D43) \ PAIR(0x0D4D, 0x0D4D) \ PAIR(0x0DCA, 0x0DCA) \ PAIR(0x0DD2, 0x0DD4) \ PAIR(0x0DD6, 0x0DD6) \ PAIR(0x0E31, 0x0E31) \ BIG_(0x0E34, 0x0E3A) \ BIG_(0x0E47, 0x0E4E) \ PAIR(0x0EB1, 0x0EB1) \ BIG_(0x0EB4, 0x0EB9) \ PAIR(0x0EBB, 0x0EBC) \ BIG_(0x0EC8, 0x0ECD) \ PAIR(0x0F18, 0x0F19) \ PAIR(0x0F35, 0x0F35) \ PAIR(0x0F37, 0x0F37) \ PAIR(0x0F39, 0x0F39) \ BIG_(0x0F71, 0x0F7E) \ BIG_(0x0F80, 0x0F84) \ PAIR(0x0F86, 0x0F87) \ PAIR(0x0FC6, 0x0FC6) \ BIG_(0x0F90, 0x0F97) \ BIG_(0x0F99, 0x0FBC) \ PAIR(0x102D, 0x1030) \ PAIR(0x1032, 0x1032) \ PAIR(0x1036, 0x1037) \ PAIR(0x1039, 0x1039) \ PAIR(0x1058, 0x1059) \ BIG_(0x1160, 0x11FF) \ PAIR(0x135F, 0x135F) \ PAIR(0x1712, 0x1714) \ PAIR(0x1732, 0x1734) \ PAIR(0x1752, 0x1753) \ PAIR(0x1772, 0x1773) \ PAIR(0x17B4, 0x17B5) \ BIG_(0x17B7, 0x17BD) \ PAIR(0x17C6, 0x17C6) \ BIG_(0x17C9, 0x17D3) \ PAIR(0x17DD, 0x17DD) \ PAIR(0x180B, 0x180D) \ PAIR(0x18A9, 0x18A9) \ PAIR(0x1920, 0x1922) \ PAIR(0x1927, 0x1928) \ PAIR(0x1932, 0x1932) \ PAIR(0x1939, 0x193B) \ PAIR(0x1A17, 0x1A18) \ PAIR(0x1B00, 0x1B03) \ PAIR(0x1B34, 0x1B34) \ BIG_(0x1B36, 0x1B3A) \ PAIR(0x1B3C, 0x1B3C) \ PAIR(0x1B42, 0x1B42) \ BIG_(0x1B6B, 0x1B73) \ BIG_(0x1DC0, 0x1DCA) \ PAIR(0x1DFE, 0x1DFF) \ BIG_(0x200B, 0x200F) \ BIG_(0x202A, 0x202E) \ PAIR(0x2060, 0x2063) \ BIG_(0x206A, 0x206F) \ BIG_(0x20D0, 0x20EF) \ BIG_(0x302A, 0x302F) \ PAIR(0x3099, 0x309A) \ /* Too big to be packed in PAIRs: */ \ BIG_(0xA806, 0xA806) \ BIG_(0xA80B, 0xA80B) \ BIG_(0xA825, 0xA826) \ BIG_(0xFB1E, 0xFB1E) \ BIG_(0xFE00, 0xFE0F) \ BIG_(0xFE20, 0xFE23) \ BIG_(0xFEFF, 0xFEFF) \ BIG_(0xFFF9, 0xFFFB) static const struct interval combining[] = { ARRAY }; # undef BIG_ # undef PAIR # define BIG_(a,b) # define PAIR(a,b) (a << 2) | (b-a), static const uint16_t combining1[] = { ARRAY }; # undef BIG_ # undef PAIR # define BIG_(a,b) char big_##a[b < 0x4000 && b-a <= 3 ? -1 : 1]; # define PAIR(a,b) char pair##a[b >= 0x4000 || b-a > 3 ? -1 : 1]; struct CHECK { ARRAY }; # undef BIG_ # undef PAIR # undef ARRAY # endif if (ucs == 0) return 0; /* Test for 8-bit control characters (00-1f, 80-9f, 7f) */ if ((ucs & ~0x80) < 0x20 || ucs == 0x7f) return -1; /* Quick abort if it is an obviously invalid char */ if (ucs > CONFIG_LAST_SUPPORTED_WCHAR) return -1; /* Optimization: no combining chars below 0x300 */ if (CONFIG_LAST_SUPPORTED_WCHAR < 0x300 || ucs < 0x300) return 1; # if CONFIG_LAST_SUPPORTED_WCHAR >= 0x300 /* Binary search in table of non-spacing characters */ if (in_interval_table(ucs, combining, ARRAY_SIZE(combining) - 1)) return 0; if (in_uint16_table(ucs, combining1, ARRAY_SIZE(combining1) - 1)) return 0; /* Optimization: all chars below 0x1100 are not double-width */ if (CONFIG_LAST_SUPPORTED_WCHAR < 0x1100 || ucs < 0x1100) return 1; # if CONFIG_LAST_SUPPORTED_WCHAR >= 0x1100 /* Invalid code points: */ /* High (d800..dbff) and low (dc00..dfff) surrogates (valid only in UTF16) */ /* Private Use Area (e000..f8ff) */ /* Noncharacters fdd0..fdef */ if ((CONFIG_LAST_SUPPORTED_WCHAR >= 0xd800 && ucs >= 0xd800 && ucs <= 0xf8ff) || (CONFIG_LAST_SUPPORTED_WCHAR >= 0xfdd0 && ucs >= 0xfdd0 && ucs <= 0xfdef) ) { return -1; } /* 0xfffe and 0xffff in every plane are invalid */ if (CONFIG_LAST_SUPPORTED_WCHAR >= 0xfffe && ((ucs & 0xfffe) == 0xfffe)) { return -1; } # if CONFIG_LAST_SUPPORTED_WCHAR >= 0x10000 if (ucs >= 0x10000) { /* Combining chars in Supplementary Multilingual Plane 0x1xxxx */ static const struct interval combining0x10000[] = { { 0x0A01, 0x0A03 }, { 0x0A05, 0x0A06 }, { 0x0A0C, 0x0A0F }, { 0x0A38, 0x0A3A }, { 0x0A3F, 0x0A3F }, { 0xD167, 0xD169 }, { 0xD173, 0xD182 }, { 0xD185, 0xD18B }, { 0xD1AA, 0xD1AD }, { 0xD242, 0xD244 } }; /* Binary search in table of non-spacing characters in Supplementary Multilingual Plane */ if (in_interval_table(ucs ^ 0x10000, combining0x10000, ARRAY_SIZE(combining0x10000) - 1)) return 0; /* Check a few non-spacing chars in Supplementary Special-purpose Plane 0xExxxx */ if (CONFIG_LAST_SUPPORTED_WCHAR >= 0xE0001 && ( ucs == 0xE0001 || (ucs >= 0xE0020 && ucs <= 0xE007F) || (ucs >= 0xE0100 && ucs <= 0xE01EF) ) ) { return 0; } } # endif /* If we arrive here, ucs is not a combining or C0/C1 control character. * Check whether it's 1 char or 2-shar wide. */ return 1 + ( (/*ucs >= 0x1100 &&*/ ucs <= 0x115f) /* Hangul Jamo init. consonants */ || ucs == 0x2329 /* left-pointing angle bracket; also CJK punct. char */ || ucs == 0x232a /* right-pointing angle bracket; also CJK punct. char */ || (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) /* CJK ... Yi */ # if CONFIG_LAST_SUPPORTED_WCHAR >= 0xac00 || (ucs >= 0xac00 && ucs <= 0xd7a3) /* Hangul Syllables */ || (ucs >= 0xf900 && ucs <= 0xfaff) /* CJK Compatibility Ideographs */ || (ucs >= 0xfe10 && ucs <= 0xfe19) /* Vertical forms */ || (ucs >= 0xfe30 && ucs <= 0xfe6f) /* CJK Compatibility Forms */ || (ucs >= 0xff00 && ucs <= 0xff60) /* Fullwidth Forms */ || (ucs >= 0xffe0 && ucs <= 0xffe6) || ((ucs >> 17) == (2 >> 1)) /* 20000..3ffff: Supplementary and Tertiary Ideographic Planes */ # endif ); # endif /* >= 0x1100 */ # endif /* >= 0x300 */ } # if ENABLE_UNICODE_BIDI_SUPPORT int FAST_FUNC unicode_bidi_isrtl(wint_t wc) { /* ranges taken from * http://www.unicode.org/Public/5.2.0/ucd/extracted/DerivedBidiClass.txt * Bidi_Class=Left_To_Right | Bidi_Class=Arabic_Letter */ # define BIG_(a,b) { a, b }, # define PAIR(a,b) # define ARRAY \ PAIR(0x0590, 0x0590) \ PAIR(0x05BE, 0x05BE) \ PAIR(0x05C0, 0x05C0) \ PAIR(0x05C3, 0x05C3) \ PAIR(0x05C6, 0x05C6) \ BIG_(0x05C8, 0x05FF) \ PAIR(0x0604, 0x0605) \ PAIR(0x0608, 0x0608) \ PAIR(0x060B, 0x060B) \ PAIR(0x060D, 0x060D) \ BIG_(0x061B, 0x064A) \ PAIR(0x065F, 0x065F) \ PAIR(0x066D, 0x066F) \ BIG_(0x0671, 0x06D5) \ PAIR(0x06E5, 0x06E6) \ PAIR(0x06EE, 0x06EF) \ BIG_(0x06FA, 0x070E) \ PAIR(0x0710, 0x0710) \ BIG_(0x0712, 0x072F) \ BIG_(0x074B, 0x07A5) \ BIG_(0x07B1, 0x07EA) \ PAIR(0x07F4, 0x07F5) \ BIG_(0x07FA, 0x0815) \ PAIR(0x081A, 0x081A) \ PAIR(0x0824, 0x0824) \ PAIR(0x0828, 0x0828) \ BIG_(0x082E, 0x08FF) \ PAIR(0x200F, 0x200F) \ PAIR(0x202B, 0x202B) \ PAIR(0x202E, 0x202E) \ BIG_(0xFB1D, 0xFB1D) \ BIG_(0xFB1F, 0xFB28) \ BIG_(0xFB2A, 0xFD3D) \ BIG_(0xFD40, 0xFDCF) \ BIG_(0xFDC8, 0xFDCF) \ BIG_(0xFDF0, 0xFDFC) \ BIG_(0xFDFE, 0xFDFF) \ BIG_(0xFE70, 0xFEFE) /* Probably not necessary {0x10800, 0x1091E}, {0x10920, 0x10A00}, {0x10A04, 0x10A04}, {0x10A07, 0x10A0B}, {0x10A10, 0x10A37}, {0x10A3B, 0x10A3E}, {0x10A40, 0x10A7F}, {0x10B36, 0x10B38}, {0x10B40, 0x10E5F}, {0x10E7F, 0x10FFF}, {0x1E800, 0x1EFFF} */ static const struct interval rtl_b[] = { ARRAY }; # undef BIG_ # undef PAIR # define BIG_(a,b) # define PAIR(a,b) (a << 2) | (b-a), static const uint16_t rtl_p[] = { ARRAY }; # undef BIG_ # undef PAIR # define BIG_(a,b) char big_##a[b < 0x4000 && b-a <= 3 ? -1 : 1]; # define PAIR(a,b) char pair##a[b >= 0x4000 || b-a > 3 ? -1 : 1]; struct CHECK { ARRAY }; # undef BIG_ # undef PAIR # undef ARRAY if (in_interval_table(wc, rtl_b, ARRAY_SIZE(rtl_b) - 1)) return 1; if (in_uint16_table(wc, rtl_p, ARRAY_SIZE(rtl_p) - 1)) return 1; return 0; } # if ENABLE_UNICODE_NEUTRAL_TABLE int FAST_FUNC unicode_bidi_is_neutral_wchar(wint_t wc) { /* ranges taken from * http://www.unicode.org/Public/5.2.0/ucd/extracted/DerivedBidiClass.txt * Bidi_Classes: Paragraph_Separator, Segment_Separator, * White_Space, Other_Neutral, European_Number, European_Separator, * European_Terminator, Arabic_Number, Common_Separator */ # define BIG_(a,b) { a, b }, # define PAIR(a,b) # define ARRAY \ BIG_(0x0009, 0x000D) \ BIG_(0x001C, 0x0040) \ BIG_(0x005B, 0x0060) \ PAIR(0x007B, 0x007E) \ PAIR(0x0085, 0x0085) \ BIG_(0x00A0, 0x00A9) \ PAIR(0x00AB, 0x00AC) \ BIG_(0x00AE, 0x00B4) \ PAIR(0x00B6, 0x00B9) \ BIG_(0x00BB, 0x00BF) \ PAIR(0x00D7, 0x00D7) \ PAIR(0x00F7, 0x00F7) \ PAIR(0x02B9, 0x02BA) \ BIG_(0x02C2, 0x02CF) \ BIG_(0x02D2, 0x02DF) \ BIG_(0x02E5, 0x02FF) \ PAIR(0x0374, 0x0375) \ PAIR(0x037E, 0x037E) \ PAIR(0x0384, 0x0385) \ PAIR(0x0387, 0x0387) \ PAIR(0x03F6, 0x03F6) \ PAIR(0x058A, 0x058A) \ PAIR(0x0600, 0x0603) \ PAIR(0x0606, 0x0607) \ PAIR(0x0609, 0x060A) \ PAIR(0x060C, 0x060C) \ PAIR(0x060E, 0x060F) \ BIG_(0x0660, 0x066C) \ PAIR(0x06DD, 0x06DD) \ PAIR(0x06E9, 0x06E9) \ BIG_(0x06F0, 0x06F9) \ PAIR(0x07F6, 0x07F9) \ PAIR(0x09F2, 0x09F3) \ PAIR(0x09FB, 0x09FB) \ PAIR(0x0AF1, 0x0AF1) \ BIG_(0x0BF3, 0x0BFA) \ BIG_(0x0C78, 0x0C7E) \ PAIR(0x0CF1, 0x0CF2) \ PAIR(0x0E3F, 0x0E3F) \ PAIR(0x0F3A, 0x0F3D) \ BIG_(0x1390, 0x1400) \ PAIR(0x1680, 0x1680) \ PAIR(0x169B, 0x169C) \ PAIR(0x17DB, 0x17DB) \ BIG_(0x17F0, 0x17F9) \ BIG_(0x1800, 0x180A) \ PAIR(0x180E, 0x180E) \ PAIR(0x1940, 0x1940) \ PAIR(0x1944, 0x1945) \ BIG_(0x19DE, 0x19FF) \ PAIR(0x1FBD, 0x1FBD) \ PAIR(0x1FBF, 0x1FC1) \ PAIR(0x1FCD, 0x1FCF) \ PAIR(0x1FDD, 0x1FDF) \ PAIR(0x1FED, 0x1FEF) \ PAIR(0x1FFD, 0x1FFE) \ BIG_(0x2000, 0x200A) \ BIG_(0x2010, 0x2029) \ BIG_(0x202F, 0x205F) \ PAIR(0x2070, 0x2070) \ BIG_(0x2074, 0x207E) \ BIG_(0x2080, 0x208E) \ BIG_(0x20A0, 0x20B8) \ PAIR(0x2100, 0x2101) \ PAIR(0x2103, 0x2106) \ PAIR(0x2108, 0x2109) \ PAIR(0x2114, 0x2114) \ PAIR(0x2116, 0x2118) \ BIG_(0x211E, 0x2123) \ PAIR(0x2125, 0x2125) \ PAIR(0x2127, 0x2127) \ PAIR(0x2129, 0x2129) \ PAIR(0x212E, 0x212E) \ PAIR(0x213A, 0x213B) \ BIG_(0x2140, 0x2144) \ PAIR(0x214A, 0x214D) \ BIG_(0x2150, 0x215F) \ PAIR(0x2189, 0x2189) \ BIG_(0x2190, 0x2335) \ BIG_(0x237B, 0x2394) \ BIG_(0x2396, 0x23E8) \ BIG_(0x2400, 0x2426) \ BIG_(0x2440, 0x244A) \ BIG_(0x2460, 0x249B) \ BIG_(0x24EA, 0x26AB) \ BIG_(0x26AD, 0x26CD) \ BIG_(0x26CF, 0x26E1) \ PAIR(0x26E3, 0x26E3) \ BIG_(0x26E8, 0x26FF) \ PAIR(0x2701, 0x2704) \ PAIR(0x2706, 0x2709) \ BIG_(0x270C, 0x2727) \ BIG_(0x2729, 0x274B) \ PAIR(0x274D, 0x274D) \ PAIR(0x274F, 0x2752) \ BIG_(0x2756, 0x275E) \ BIG_(0x2761, 0x2794) \ BIG_(0x2798, 0x27AF) \ BIG_(0x27B1, 0x27BE) \ BIG_(0x27C0, 0x27CA) \ PAIR(0x27CC, 0x27CC) \ BIG_(0x27D0, 0x27FF) \ BIG_(0x2900, 0x2B4C) \ BIG_(0x2B50, 0x2B59) \ BIG_(0x2CE5, 0x2CEA) \ BIG_(0x2CF9, 0x2CFF) \ BIG_(0x2E00, 0x2E99) \ BIG_(0x2E9B, 0x2EF3) \ BIG_(0x2F00, 0x2FD5) \ BIG_(0x2FF0, 0x2FFB) \ BIG_(0x3000, 0x3004) \ BIG_(0x3008, 0x3020) \ PAIR(0x3030, 0x3030) \ PAIR(0x3036, 0x3037) \ PAIR(0x303D, 0x303D) \ PAIR(0x303E, 0x303F) \ PAIR(0x309B, 0x309C) \ PAIR(0x30A0, 0x30A0) \ PAIR(0x30FB, 0x30FB) \ BIG_(0x31C0, 0x31E3) \ PAIR(0x321D, 0x321E) \ BIG_(0x3250, 0x325F) \ PAIR(0x327C, 0x327E) \ BIG_(0x32B1, 0x32BF) \ PAIR(0x32CC, 0x32CF) \ PAIR(0x3377, 0x337A) \ PAIR(0x33DE, 0x33DF) \ PAIR(0x33FF, 0x33FF) \ BIG_(0x4DC0, 0x4DFF) \ BIG_(0xA490, 0xA4C6) \ BIG_(0xA60D, 0xA60F) \ BIG_(0xA673, 0xA673) \ BIG_(0xA67E, 0xA67F) \ BIG_(0xA700, 0xA721) \ BIG_(0xA788, 0xA788) \ BIG_(0xA828, 0xA82B) \ BIG_(0xA838, 0xA839) \ BIG_(0xA874, 0xA877) \ BIG_(0xFB29, 0xFB29) \ BIG_(0xFD3E, 0xFD3F) \ BIG_(0xFDFD, 0xFDFD) \ BIG_(0xFE10, 0xFE19) \ BIG_(0xFE30, 0xFE52) \ BIG_(0xFE54, 0xFE66) \ BIG_(0xFE68, 0xFE6B) \ BIG_(0xFF01, 0xFF20) \ BIG_(0xFF3B, 0xFF40) \ BIG_(0xFF5B, 0xFF65) \ BIG_(0xFFE0, 0xFFE6) \ BIG_(0xFFE8, 0xFFEE) \ BIG_(0xFFF9, 0xFFFD) /* {0x10101, 0x10101}, {0x10140, 0x1019B}, {0x1091F, 0x1091F}, {0x10B39, 0x10B3F}, {0x10E60, 0x10E7E}, {0x1D200, 0x1D241}, {0x1D245, 0x1D245}, {0x1D300, 0x1D356}, {0x1D6DB, 0x1D6DB}, {0x1D715, 0x1D715}, {0x1D74F, 0x1D74F}, {0x1D789, 0x1D789}, {0x1D7C3, 0x1D7C3}, {0x1D7CE, 0x1D7FF}, {0x1F000, 0x1F02B}, {0x1F030, 0x1F093}, {0x1F100, 0x1F10A} */ static const struct interval neutral_b[] = { ARRAY }; # undef BIG_ # undef PAIR # define BIG_(a,b) # define PAIR(a,b) (a << 2) | (b-a), static const uint16_t neutral_p[] = { ARRAY }; # undef BIG_ # undef PAIR # define BIG_(a,b) char big_##a[b < 0x4000 && b-a <= 3 ? -1 : 1]; # define PAIR(a,b) char pair##a[b >= 0x4000 || b-a > 3 ? -1 : 1]; struct CHECK { ARRAY }; # undef BIG_ # undef PAIR # undef ARRAY if (in_interval_table(wc, neutral_b, ARRAY_SIZE(neutral_b) - 1)) return 1; if (in_uint16_table(wc, neutral_p, ARRAY_SIZE(neutral_p) - 1)) return 1; return 0; } # endif # endif /* UNICODE_BIDI_SUPPORT */ #endif /* Homegrown Unicode support */ /* The rest is mostly same for libc and for "homegrown" support */ size_t FAST_FUNC unicode_strlen(const char *string) { size_t width = mbstowcs(NULL, string, INT_MAX); if (width == (size_t)-1L) return strlen(string); return width; } size_t FAST_FUNC unicode_strwidth(const char *string) { uni_stat_t uni_stat; printable_string(&uni_stat, string); return uni_stat.unicode_width; } static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char *src, unsigned width, int flags) { char *dst; unsigned dst_len; unsigned uni_count; unsigned uni_width; if (unicode_status != UNICODE_ON) { char *d; if (flags & UNI_FLAG_PAD) { d = dst = xmalloc(width + 1); while ((int)--width >= 0) { unsigned char c = *src; if (c == '\0') { do *d++ = ' '; while ((int)--width >= 0); break; } *d++ = (c >= ' ' && c < 0x7f) ? c : '?'; src++; } *d = '\0'; } else { d = dst = xstrndup(src, width); while (*d) { unsigned char c = *d; if (c < ' ' || c >= 0x7f) *d = '?'; d++; } } if (stats) { stats->byte_count = (d - dst); stats->unicode_count = (d - dst); stats->unicode_width = (d - dst); } return dst; } dst = NULL; uni_count = uni_width = 0; dst_len = 0; while (1) { int w; wchar_t wc; #if ENABLE_UNICODE_USING_LOCALE { mbstate_t mbst = { 0 }; ssize_t rc = mbsrtowcs(&wc, &src, 1, &mbst); /* If invalid sequence is seen: -1 is returned, * src points to the invalid sequence, errno = EILSEQ. * Else number of wchars (excluding terminating L'\0') * written to dest is returned. * If len (here: 1) non-L'\0' wchars stored at dest, * src points to the next char to be converted. * If string is completely converted: src = NULL. */ if (rc == 0) /* end-of-string */ break; if (rc < 0) { /* error */ src++; goto subst; } if (!iswprint(wc)) goto subst; } #else src = mbstowc_internal(&wc, src); /* src is advanced to next mb char * wc == ERROR_WCHAR: invalid sequence is seen * else: wc is set */ if (wc == ERROR_WCHAR) /* error */ goto subst; if (wc == 0) /* end-of-string */ break; #endif if (CONFIG_LAST_SUPPORTED_WCHAR && wc > CONFIG_LAST_SUPPORTED_WCHAR) goto subst; w = wcwidth(wc); if ((ENABLE_UNICODE_COMBINING_WCHARS && w < 0) /* non-printable wchar */ || (!ENABLE_UNICODE_COMBINING_WCHARS && w <= 0) || (!ENABLE_UNICODE_WIDE_WCHARS && w > 1) ) { subst: wc = CONFIG_SUBST_WCHAR; w = 1; } width -= w; /* Note: if width == 0, we still may add more chars, * they may be zero-width or combining ones */ if ((int)width < 0) { /* can't add this wc, string would become longer than width */ width += w; break; } uni_count++; uni_width += w; dst = xrealloc(dst, dst_len + MB_CUR_MAX); #if ENABLE_UNICODE_USING_LOCALE { mbstate_t mbst = { 0 }; dst_len += wcrtomb(&dst[dst_len], wc, &mbst); } #else dst_len += wcrtomb_internal(&dst[dst_len], wc); #endif } /* Pad to remaining width */ if (flags & UNI_FLAG_PAD) { dst = xrealloc(dst, dst_len + width + 1); uni_count += width; uni_width += width; while ((int)--width >= 0) { dst[dst_len++] = ' '; } } dst[dst_len] = '\0'; if (stats) { stats->byte_count = dst_len; stats->unicode_count = uni_count; stats->unicode_width = uni_width; } return dst; } char* FAST_FUNC unicode_conv_to_printable(uni_stat_t *stats, const char *src) { return unicode_conv_to_printable2(stats, src, INT_MAX, 0); } char* FAST_FUNC unicode_conv_to_printable_fixedwidth(/*uni_stat_t *stats,*/ const char *src, unsigned width) { return unicode_conv_to_printable2(/*stats:*/ NULL, src, width, UNI_FLAG_PAD); } #ifdef UNUSED char* FAST_FUNC unicode_conv_to_printable_maxwidth(uni_stat_t *stats, const char *src, unsigned maxwidth) { return unicode_conv_to_printable2(stats, src, maxwidth, 0); } unsigned FAST_FUNC unicode_padding_to_width(unsigned width, const char *src) { if (unicode_status != UNICODE_ON) { return width - strnlen(src, width); } while (1) { int w; wchar_t wc; #if ENABLE_UNICODE_USING_LOCALE { mbstate_t mbst = { 0 }; ssize_t rc = mbsrtowcs(&wc, &src, 1, &mbst); if (rc <= 0) /* error, or end-of-string */ return width; } #else src = mbstowc_internal(&wc, src); if (wc == ERROR_WCHAR || wc == 0) /* error, or end-of-string */ return width; #endif w = wcwidth(wc); if (w < 0) /* non-printable wchar */ return width; width -= w; if ((int)width <= 0) /* string is longer than width */ return 0; } } #endif busybox-1.22.1/libbb/u_signal_names.c0000644000000000000000000001163712263563520016204 0ustar rootroot/* 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 source tree. */ //config:config FEATURE_RTMINMAX //config: bool "Support RTMIN[+n] and RTMAX[-n] signal names" //config: default y //config: help //config: Support RTMIN[+n] and RTMAX[-n] signal names //config: in kill, killall etc. This costs ~250 bytes. #include "libbb.h" /* Believe it or not, but some arches have more than 32 SIGs! * HPPA: SIGSTKFLT == 36. */ static const char signals[][7] = { // SUSv3 says kill must support these, and specifies the numerical values, // http://www.opengroup.org/onlinepubs/009695399/utilities/kill.html // {0, "EXIT"}, {1, "HUP"}, {2, "INT"}, {3, "QUIT"}, // {6, "ABRT"}, {9, "KILL"}, {14, "ALRM"}, {15, "TERM"} // And Posix adds the following: // {SIGILL, "ILL"}, {SIGTRAP, "TRAP"}, {SIGFPE, "FPE"}, {SIGUSR1, "USR1"}, // {SIGSEGV, "SEGV"}, {SIGUSR2, "USR2"}, {SIGPIPE, "PIPE"}, {SIGCHLD, "CHLD"}, // {SIGCONT, "CONT"}, {SIGSTOP, "STOP"}, {SIGTSTP, "TSTP"}, {SIGTTIN, "TTIN"}, // {SIGTTOU, "TTOU"} [0] = "EXIT", #ifdef SIGHUP [SIGHUP ] = "HUP", #endif #ifdef SIGINT [SIGINT ] = "INT", #endif #ifdef SIGQUIT [SIGQUIT ] = "QUIT", #endif #ifdef SIGILL [SIGILL ] = "ILL", #endif #ifdef SIGTRAP [SIGTRAP ] = "TRAP", #endif #ifdef SIGABRT [SIGABRT ] = "ABRT", #endif #ifdef SIGBUS [SIGBUS ] = "BUS", #endif #ifdef SIGFPE [SIGFPE ] = "FPE", #endif #ifdef SIGKILL [SIGKILL ] = "KILL", #endif #ifdef SIGUSR1 [SIGUSR1 ] = "USR1", #endif #ifdef SIGSEGV [SIGSEGV ] = "SEGV", #endif #ifdef SIGUSR2 [SIGUSR2 ] = "USR2", #endif #ifdef SIGPIPE [SIGPIPE ] = "PIPE", #endif #ifdef SIGALRM [SIGALRM ] = "ALRM", #endif #ifdef SIGTERM [SIGTERM ] = "TERM", #endif #ifdef SIGSTKFLT [SIGSTKFLT] = "STKFLT", #endif #ifdef SIGCHLD [SIGCHLD ] = "CHLD", #endif #ifdef SIGCONT [SIGCONT ] = "CONT", #endif #ifdef SIGSTOP [SIGSTOP ] = "STOP", #endif #ifdef SIGTSTP [SIGTSTP ] = "TSTP", #endif #ifdef SIGTTIN [SIGTTIN ] = "TTIN", #endif #ifdef SIGTTOU [SIGTTOU ] = "TTOU", #endif #ifdef SIGURG [SIGURG ] = "URG", #endif #ifdef SIGXCPU [SIGXCPU ] = "XCPU", #endif #ifdef SIGXFSZ [SIGXFSZ ] = "XFSZ", #endif #ifdef SIGVTALRM [SIGVTALRM] = "VTALRM", #endif #ifdef SIGPROF [SIGPROF ] = "PROF", #endif #ifdef SIGWINCH [SIGWINCH ] = "WINCH", #endif #ifdef SIGPOLL [SIGPOLL ] = "POLL", #endif #ifdef SIGPWR [SIGPWR ] = "PWR", #endif #ifdef SIGSYS [SIGSYS ] = "SYS", #endif #if ENABLE_FEATURE_RTMINMAX # ifdef __SIGRTMIN [__SIGRTMIN] = "RTMIN", # endif // This makes array about x2 bigger. // More compact approach is to special-case SIGRTMAX in print_signames() //# ifdef __SIGRTMAX // [__SIGRTMAX] = "RTMAX", //# endif #endif }; // Convert signal name to number. int FAST_FUNC get_signum(const char *name) { unsigned i; i = bb_strtou(name, NULL, 10); if (!errno) return i; if (strncasecmp(name, "SIG", 3) == 0) name += 3; for (i = 0; i < ARRAY_SIZE(signals); i++) if (strcasecmp(name, signals[i]) == 0) return i; #if ENABLE_DESKTOP # if defined(SIGIOT) || defined(SIGIO) /* SIGIO[T] are aliased to other names, * thus cannot be stored in the signals[] array. * Need special code to recognize them */ if ((name[0] | 0x20) == 'i' && (name[1] | 0x20) == 'o') { # ifdef SIGIO if (!name[2]) return SIGIO; # endif # ifdef SIGIOT if ((name[2] | 0x20) == 't' && !name[3]) return SIGIOT; # endif } # endif #endif #if ENABLE_FEATURE_RTMINMAX # if defined(SIGRTMIN) && defined(SIGRTMAX) /* libc may use some rt sigs for pthreads and therefore "remap" SIGRTMIN/MAX, * but we want to use "raw" SIGRTMIN/MAX. Underscored names, if exist, provide * them. If they don't exist, fall back to non-underscored ones: */ # if !defined(__SIGRTMIN) # define __SIGRTMIN SIGRTMIN # endif # if !defined(__SIGRTMAX) # define __SIGRTMAX SIGRTMAX # endif if (strncasecmp(name, "RTMIN", 5) == 0) { if (!name[5]) return __SIGRTMIN; if (name[5] == '+') { i = bb_strtou(name + 6, NULL, 10); if (!errno && i <= __SIGRTMAX - __SIGRTMIN) return __SIGRTMIN + i; } } else if (strncasecmp(name, "RTMAX", 5) == 0) { if (!name[5]) return __SIGRTMAX; if (name[5] == '-') { i = bb_strtou(name + 6, NULL, 10); if (!errno && i <= __SIGRTMAX - __SIGRTMIN) return __SIGRTMAX - i; } } # endif #endif return -1; } // Convert signal number to name const char* FAST_FUNC get_signame(int number) { if ((unsigned)number < ARRAY_SIZE(signals)) { if (signals[number][0]) /* if it's not an empty str */ return signals[number]; } return itoa(number); } // Print the whole signal list void FAST_FUNC print_signames(void) { unsigned signo; for (signo = 1; signo < ARRAY_SIZE(signals); signo++) { const char *name = signals[signo]; if (name[0]) printf("%2u) %s\n", signo, name); } #if ENABLE_FEATURE_RTMINMAX # ifdef __SIGRTMAX printf("%2u) %s\n", __SIGRTMAX, "RTMAX"); # endif #endif } busybox-1.22.1/libbb/hash_md5_sha.c0000644000000000000000000007523012263563520015542 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 2010 Denys Vlasenko * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" /* gcc 4.2.1 optimizes rotr64 better with inline than with macro * (for rotX32, there is no difference). Why? My guess is that * macro requires clever common subexpression elimination heuristics * in gcc, while inline basically forces it to happen. */ //#define rotl32(x,n) (((x) << (n)) | ((x) >> (32 - (n)))) static ALWAYS_INLINE uint32_t rotl32(uint32_t x, unsigned n) { return (x << n) | (x >> (32 - n)); } //#define rotr32(x,n) (((x) >> (n)) | ((x) << (32 - (n)))) static ALWAYS_INLINE uint32_t rotr32(uint32_t x, unsigned n) { return (x >> n) | (x << (32 - n)); } /* rotr64 in needed for sha512 only: */ //#define rotr64(x,n) (((x) >> (n)) | ((x) << (64 - (n)))) static ALWAYS_INLINE uint64_t rotr64(uint64_t x, unsigned n) { return (x >> n) | (x << (64 - n)); } /* rotl64 only used for sha3 currently */ static ALWAYS_INLINE uint64_t rotl64(uint64_t x, unsigned n) { return (x << n) | (x >> (64 - n)); } /* Feed data through a temporary buffer. * The internal buffer remembers previous data until it has 64 * bytes worth to pass on. */ static void FAST_FUNC common64_hash(md5_ctx_t *ctx, const void *buffer, size_t len) { unsigned bufpos = ctx->total64 & 63; ctx->total64 += len; while (1) { unsigned remaining = 64 - bufpos; if (remaining > len) remaining = len; /* Copy data into aligned buffer */ memcpy(ctx->wbuffer + bufpos, buffer, remaining); len -= remaining; buffer = (const char *)buffer + remaining; bufpos += remaining; /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */ bufpos -= 64; if (bufpos != 0) break; /* Buffer is filled up, process it */ ctx->process_block(ctx); /*bufpos = 0; - already is */ } } /* Process the remaining bytes in the buffer */ static void FAST_FUNC common64_end(md5_ctx_t *ctx, int swap_needed) { unsigned bufpos = ctx->total64 & 63; /* Pad the buffer to the next 64-byte boundary with 0x80,0,0,0... */ ctx->wbuffer[bufpos++] = 0x80; /* This loop iterates either once or twice, no more, no less */ while (1) { unsigned remaining = 64 - bufpos; memset(ctx->wbuffer + bufpos, 0, remaining); /* Do we have enough space for the length count? */ if (remaining >= 8) { /* Store the 64-bit counter of bits in the buffer */ uint64_t t = ctx->total64 << 3; if (swap_needed) t = bb_bswap_64(t); /* wbuffer is suitably aligned for this */ *(bb__aliased_uint64_t *) (&ctx->wbuffer[64 - 8]) = t; } ctx->process_block(ctx); if (remaining >= 8) break; bufpos = 0; } } /* * 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 GPLv2 or later, see file LICENSE in this source tree. */ /* 0: fastest, 3: smallest */ #if CONFIG_MD5_SMALL < 0 # define MD5_SMALL 0 #elif CONFIG_MD5_SMALL > 3 # define MD5_SMALL 3 #else # define MD5_SMALL CONFIG_MD5_SMALL #endif /* 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)) */ #undef FF #undef FG #undef FH #undef FI #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 FAST_FUNC md5_process_block64(md5_ctx_t *ctx) { #if MD5_SMALL > 0 /* 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 */ 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, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, /* round 3 */ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, /* round 4 */ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 }; static const char P_array[] ALIGN1 = { # if MD5_SMALL > 1 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */ # endif 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */ 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */ 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */ }; #endif uint32_t *words = (void*) ctx->wbuffer; uint32_t A = ctx->hash[0]; uint32_t B = ctx->hash[1]; uint32_t C = ctx->hash[2]; uint32_t D = ctx->hash[3]; #if MD5_SMALL >= 2 /* 2 or 3 */ static const char S_array[] ALIGN1 = { 7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21 }; const uint32_t *pc; const char *pp; const char *ps; int i; uint32_t temp; if (BB_BIG_ENDIAN) for (i = 0; i < 16; i++) words[i] = SWAP_LE32(words[i]); # if MD5_SMALL == 3 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 += words[(int) (*pp++)] + *pc++; temp = rotl32(temp, ps[i & 3]); temp += B; A = D; D = C; C = B; B = temp; } # else /* MD5_SMALL == 2 */ pc = C_array; pp = P_array; ps = S_array; for (i = 0; i < 16; i++) { temp = A + FF(B, C, D) + words[(int) (*pp++)] + *pc++; temp = rotl32(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) + words[(int) (*pp++)] + *pc++; temp = rotl32(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) + words[(int) (*pp++)] + *pc++; temp = rotl32(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) + words[(int) (*pp++)] + *pc++; temp = rotl32(temp, ps[i & 3]); temp += B; A = D; D = C; C = B; B = temp; } # endif /* Add checksum to the starting values */ ctx->hash[0] += A; ctx->hash[1] += B; ctx->hash[2] += C; ctx->hash[3] += D; #else /* MD5_SMALL == 0 or 1 */ uint32_t A_save = A; uint32_t B_save = B; uint32_t C_save = C; uint32_t D_save = D; # if MD5_SMALL == 1 const uint32_t *pc; const char *pp; int i; # endif /* First round: using the given function, the context and a constant the next context is computed. Because the algorithm's 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 save swapped words in WORDS array. */ # undef OP # define OP(a, b, c, d, s, T) \ do { \ a += FF(b, c, d) + (*words IF_BIG_ENDIAN(= SWAP_LE32(*words))) + T; \ words++; \ a = rotl32(a, s); \ a += b; \ } while (0) /* Round 1 */ # if MD5_SMALL == 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 words -= 16; /* For the second to fourth round we have the possibly swapped words in 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) + words[k] + T; \ a = rotl32(a, s); \ a += b; \ } while (0) /* Round 2 */ # if MD5_SMALL == 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 /* Round 3 */ # if MD5_SMALL == 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 /* Round 4 */ # if MD5_SMALL == 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); # undef OP # endif /* Add checksum to the starting values */ ctx->hash[0] = A_save + A; ctx->hash[1] = B_save + B; ctx->hash[2] = C_save + C; ctx->hash[3] = D_save + D; #endif } #undef FF #undef FG #undef FH #undef FI /* Initialize structure containing state of computation. * (RFC 1321, 3.3: Step 3) */ void FAST_FUNC md5_begin(md5_ctx_t *ctx) { ctx->hash[0] = 0x67452301; ctx->hash[1] = 0xefcdab89; ctx->hash[2] = 0x98badcfe; ctx->hash[3] = 0x10325476; ctx->total64 = 0; ctx->process_block = md5_process_block64; } /* Used also for sha1 and sha256 */ void FAST_FUNC md5_hash(md5_ctx_t *ctx, const void *buffer, size_t len) { common64_hash(ctx, buffer, len); } /* 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. */ void FAST_FUNC md5_end(md5_ctx_t *ctx, void *resbuf) { /* MD5 stores total in LE, need to swap on BE arches: */ common64_end(ctx, /*swap_needed:*/ BB_BIG_ENDIAN); /* The MD5 result is in little endian byte order */ if (BB_BIG_ENDIAN) { ctx->hash[0] = SWAP_LE32(ctx->hash[0]); ctx->hash[1] = SWAP_LE32(ctx->hash[1]); ctx->hash[2] = SWAP_LE32(ctx->hash[2]); ctx->hash[3] = SWAP_LE32(ctx->hash[3]); } memcpy(resbuf, ctx->hash, sizeof(ctx->hash[0]) * 4); } /* * SHA1 part is: * Copyright 2007 Rob Landley * * Based on the public domain SHA-1 in C by Steve Reid * from http://www.mirrors.wiretapped.net/security/cryptography/hashes/sha1/ * * Licensed under GPLv2, see file LICENSE in this source tree. * * --------------------------------------------------------------------------- * * SHA256 and SHA512 parts are: * Released into the Public Domain by Ulrich Drepper . * Shrank by Denys Vlasenko. * * --------------------------------------------------------------------------- * * The best way to test random blocksizes is to go to coreutils/md5_sha1_sum.c * and replace "4096" with something like "2000 + time(NULL) % 2097", * then rebuild and compare "shaNNNsum bigfile" results. */ static void FAST_FUNC sha1_process_block64(sha1_ctx_t *ctx) { static const uint32_t rconsts[] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 }; int i, j; int cnt; uint32_t W[16+16]; uint32_t a, b, c, d, e; /* On-stack work buffer frees up one register in the main loop * which otherwise will be needed to hold ctx pointer */ for (i = 0; i < 16; i++) W[i] = W[i+16] = SWAP_BE32(((uint32_t*)ctx->wbuffer)[i]); a = ctx->hash[0]; b = ctx->hash[1]; c = ctx->hash[2]; d = ctx->hash[3]; e = ctx->hash[4]; /* 4 rounds of 20 operations each */ cnt = 0; for (i = 0; i < 4; i++) { j = 19; do { uint32_t work; work = c ^ d; if (i == 0) { work = (work & b) ^ d; if (j <= 3) goto ge16; /* Used to do SWAP_BE32 here, but this * requires ctx (see comment above) */ work += W[cnt]; } else { if (i == 2) work = ((b | c) & d) | (b & c); else /* i = 1 or 3 */ work ^= b; ge16: W[cnt] = W[cnt+16] = rotl32(W[cnt+13] ^ W[cnt+8] ^ W[cnt+2] ^ W[cnt], 1); work += W[cnt]; } work += e + rotl32(a, 5) + rconsts[i]; /* Rotate by one for next time */ e = d; d = c; c = /* b = */ rotl32(b, 30); b = a; a = work; cnt = (cnt + 1) & 15; } while (--j >= 0); } ctx->hash[0] += a; ctx->hash[1] += b; ctx->hash[2] += c; ctx->hash[3] += d; ctx->hash[4] += e; } /* Constants for SHA512 from FIPS 180-2:4.2.3. * SHA256 constants from FIPS 180-2:4.2.2 * are the most significant half of first 64 elements * of the same array. */ static const uint64_t sha_K[80] = { 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, /* [64]+ are used for sha512 only */ 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL }; #undef Ch #undef Maj #undef S0 #undef S1 #undef R0 #undef R1 static void FAST_FUNC sha256_process_block64(sha256_ctx_t *ctx) { unsigned t; uint32_t W[64], a, b, c, d, e, f, g, h; const uint32_t *words = (uint32_t*) ctx->wbuffer; /* Operators defined in FIPS 180-2:4.1.2. */ #define Ch(x, y, z) ((x & y) ^ (~x & z)) #define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) #define S0(x) (rotr32(x, 2) ^ rotr32(x, 13) ^ rotr32(x, 22)) #define S1(x) (rotr32(x, 6) ^ rotr32(x, 11) ^ rotr32(x, 25)) #define R0(x) (rotr32(x, 7) ^ rotr32(x, 18) ^ (x >> 3)) #define R1(x) (rotr32(x, 17) ^ rotr32(x, 19) ^ (x >> 10)) /* Compute the message schedule according to FIPS 180-2:6.2.2 step 2. */ for (t = 0; t < 16; ++t) W[t] = SWAP_BE32(words[t]); for (/*t = 16*/; t < 64; ++t) W[t] = R1(W[t - 2]) + W[t - 7] + R0(W[t - 15]) + W[t - 16]; a = ctx->hash[0]; b = ctx->hash[1]; c = ctx->hash[2]; d = ctx->hash[3]; e = ctx->hash[4]; f = ctx->hash[5]; g = ctx->hash[6]; h = ctx->hash[7]; /* The actual computation according to FIPS 180-2:6.2.2 step 3. */ for (t = 0; t < 64; ++t) { /* Need to fetch upper half of sha_K[t] * (I hope compiler is clever enough to just fetch * upper half) */ uint32_t K_t = sha_K[t] >> 32; uint32_t T1 = h + S1(e) + Ch(e, f, g) + K_t + W[t]; uint32_t T2 = S0(a) + Maj(a, b, c); h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; } #undef Ch #undef Maj #undef S0 #undef S1 #undef R0 #undef R1 /* Add the starting values of the context according to FIPS 180-2:6.2.2 step 4. */ ctx->hash[0] += a; ctx->hash[1] += b; ctx->hash[2] += c; ctx->hash[3] += d; ctx->hash[4] += e; ctx->hash[5] += f; ctx->hash[6] += g; ctx->hash[7] += h; } static void FAST_FUNC sha512_process_block128(sha512_ctx_t *ctx) { unsigned t; uint64_t W[80]; /* On i386, having assignments here (not later as sha256 does) * produces 99 bytes smaller code with gcc 4.3.1 */ uint64_t a = ctx->hash[0]; uint64_t b = ctx->hash[1]; uint64_t c = ctx->hash[2]; uint64_t d = ctx->hash[3]; uint64_t e = ctx->hash[4]; uint64_t f = ctx->hash[5]; uint64_t g = ctx->hash[6]; uint64_t h = ctx->hash[7]; const uint64_t *words = (uint64_t*) ctx->wbuffer; /* Operators defined in FIPS 180-2:4.1.2. */ #define Ch(x, y, z) ((x & y) ^ (~x & z)) #define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) #define S0(x) (rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39)) #define S1(x) (rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41)) #define R0(x) (rotr64(x, 1) ^ rotr64(x, 8) ^ (x >> 7)) #define R1(x) (rotr64(x, 19) ^ rotr64(x, 61) ^ (x >> 6)) /* Compute the message schedule according to FIPS 180-2:6.3.2 step 2. */ for (t = 0; t < 16; ++t) W[t] = SWAP_BE64(words[t]); for (/*t = 16*/; t < 80; ++t) W[t] = R1(W[t - 2]) + W[t - 7] + R0(W[t - 15]) + W[t - 16]; /* The actual computation according to FIPS 180-2:6.3.2 step 3. */ for (t = 0; t < 80; ++t) { uint64_t T1 = h + S1(e) + Ch(e, f, g) + sha_K[t] + W[t]; uint64_t T2 = S0(a) + Maj(a, b, c); h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; } #undef Ch #undef Maj #undef S0 #undef S1 #undef R0 #undef R1 /* Add the starting values of the context according to FIPS 180-2:6.3.2 step 4. */ ctx->hash[0] += a; ctx->hash[1] += b; ctx->hash[2] += c; ctx->hash[3] += d; ctx->hash[4] += e; ctx->hash[5] += f; ctx->hash[6] += g; ctx->hash[7] += h; } void FAST_FUNC sha1_begin(sha1_ctx_t *ctx) { ctx->hash[0] = 0x67452301; ctx->hash[1] = 0xefcdab89; ctx->hash[2] = 0x98badcfe; ctx->hash[3] = 0x10325476; ctx->hash[4] = 0xc3d2e1f0; ctx->total64 = 0; ctx->process_block = sha1_process_block64; } static const uint32_t init256[] = { 0, 0, 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, }; static const uint32_t init512_lo[] = { 0, 0, 0xf3bcc908, 0x84caa73b, 0xfe94f82b, 0x5f1d36f1, 0xade682d1, 0x2b3e6c1f, 0xfb41bd6b, 0x137e2179, }; /* Initialize structure containing state of computation. (FIPS 180-2:5.3.2) */ void FAST_FUNC sha256_begin(sha256_ctx_t *ctx) { memcpy(&ctx->total64, init256, sizeof(init256)); /*ctx->total64 = 0; - done by prepending two 32-bit zeros to init256 */ ctx->process_block = sha256_process_block64; } /* Initialize structure containing state of computation. (FIPS 180-2:5.3.3) */ void FAST_FUNC sha512_begin(sha512_ctx_t *ctx) { int i; /* Two extra iterations zero out ctx->total64[2] */ uint64_t *tp = ctx->total64; for (i = 0; i < 2+8; i++) tp[i] = ((uint64_t)(init256[i]) << 32) + init512_lo[i]; /*ctx->total64[0] = ctx->total64[1] = 0; - already done */ } void FAST_FUNC sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) { unsigned bufpos = ctx->total64[0] & 127; unsigned remaining; /* First increment the byte count. FIPS 180-2 specifies the possible length of the file up to 2^128 _bits_. We compute the number of _bytes_ and convert to bits later. */ ctx->total64[0] += len; if (ctx->total64[0] < len) ctx->total64[1]++; #if 0 remaining = 128 - bufpos; /* Hash whole blocks */ while (len >= remaining) { memcpy(ctx->wbuffer + bufpos, buffer, remaining); buffer = (const char *)buffer + remaining; len -= remaining; remaining = 128; bufpos = 0; sha512_process_block128(ctx); } /* Save last, partial blosk */ memcpy(ctx->wbuffer + bufpos, buffer, len); #else while (1) { remaining = 128 - bufpos; if (remaining > len) remaining = len; /* Copy data into aligned buffer */ memcpy(ctx->wbuffer + bufpos, buffer, remaining); len -= remaining; buffer = (const char *)buffer + remaining; bufpos += remaining; /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */ bufpos -= 128; if (bufpos != 0) break; /* Buffer is filled up, process it */ sha512_process_block128(ctx); /*bufpos = 0; - already is */ } #endif } /* Used also for sha256 */ void FAST_FUNC sha1_end(sha1_ctx_t *ctx, void *resbuf) { unsigned hash_size; /* SHA stores total in BE, need to swap on LE arches: */ common64_end(ctx, /*swap_needed:*/ BB_LITTLE_ENDIAN); hash_size = (ctx->process_block == sha1_process_block64) ? 5 : 8; /* This way we do not impose alignment constraints on resbuf: */ if (BB_LITTLE_ENDIAN) { unsigned i; for (i = 0; i < hash_size; ++i) ctx->hash[i] = SWAP_BE32(ctx->hash[i]); } memcpy(resbuf, ctx->hash, sizeof(ctx->hash[0]) * hash_size); } void FAST_FUNC sha512_end(sha512_ctx_t *ctx, void *resbuf) { unsigned bufpos = ctx->total64[0] & 127; /* Pad the buffer to the next 128-byte boundary with 0x80,0,0,0... */ ctx->wbuffer[bufpos++] = 0x80; while (1) { unsigned remaining = 128 - bufpos; memset(ctx->wbuffer + bufpos, 0, remaining); if (remaining >= 16) { /* Store the 128-bit counter of bits in the buffer in BE format */ uint64_t t; t = ctx->total64[0] << 3; t = SWAP_BE64(t); *(bb__aliased_uint64_t *) (&ctx->wbuffer[128 - 8]) = t; t = (ctx->total64[1] << 3) | (ctx->total64[0] >> 61); t = SWAP_BE64(t); *(bb__aliased_uint64_t *) (&ctx->wbuffer[128 - 16]) = t; } sha512_process_block128(ctx); if (remaining >= 16) break; bufpos = 0; } if (BB_LITTLE_ENDIAN) { unsigned i; for (i = 0; i < ARRAY_SIZE(ctx->hash); ++i) ctx->hash[i] = SWAP_BE64(ctx->hash[i]); } memcpy(resbuf, ctx->hash, sizeof(ctx->hash)); } /* * The Keccak sponge function, designed by Guido Bertoni, Joan Daemen, * Michael Peeters and Gilles Van Assche. For more information, feedback or * questions, please refer to our website: http://keccak.noekeon.org/ * * Implementation by Ronny Van Keer, * hereby denoted as "the implementer". * * To the extent possible under law, the implementer has waived all copyright * and related or neighboring rights to the source code in this file. * http://creativecommons.org/publicdomain/zero/1.0/ * * Busybox modifications (C) Lauri Kasanen, under the GPLv2. */ #if CONFIG_SHA3_SMALL < 0 # define SHA3_SMALL 0 #elif CONFIG_SHA3_SMALL > 1 # define SHA3_SMALL 1 #else # define SHA3_SMALL CONFIG_SHA3_SMALL #endif enum { SHA3_IBLK_BYTES = 72, /* 576 bits / 8 */ }; /* * In the crypto literature this function is usually called Keccak-f(). */ static void sha3_process_block72(uint64_t *state) { enum { NROUNDS = 24 }; /* Elements should be 64-bit, but top half is always zero or 0x80000000. * We encode 63rd bits in a separate word below. * Same is true for 31th bits, which lets us use 16-bit table instead of 64-bit. * The speed penalty is lost in the noise. */ static const uint16_t IOTA_CONST[NROUNDS] = { 0x0001, 0x8082, 0x808a, 0x8000, 0x808b, 0x0001, 0x8081, 0x8009, 0x008a, 0x0088, 0x8009, 0x000a, 0x808b, 0x008b, 0x8089, 0x8003, 0x8002, 0x0080, 0x800a, 0x000a, 0x8081, 0x8080, 0x0001, 0x8008, }; /* bit for CONST[0] is in msb: 0011 0011 0000 0111 1101 1101 */ const uint32_t IOTA_CONST_bit63 = (uint32_t)(0x3307dd00); /* bit for CONST[0] is in msb: 0001 0110 0011 1000 0001 1011 */ const uint32_t IOTA_CONST_bit31 = (uint32_t)(0x16381b00); static const uint8_t ROT_CONST[24] = { 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44, }; static const uint8_t PI_LANE[24] = { 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1, }; /*static const uint8_t MOD5[10] = { 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, };*/ unsigned x, y; unsigned round; if (BB_BIG_ENDIAN) { for (x = 0; x < 25; x++) { state[x] = SWAP_LE64(state[x]); } } for (round = 0; round < NROUNDS; ++round) { /* Theta */ { uint64_t BC[10]; for (x = 0; x < 5; ++x) { BC[x + 5] = BC[x] = state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ state[x + 20]; } /* Using 2x5 vector above eliminates the need to use * BC[MOD5[x+N]] trick below to fetch BC[(x+N) % 5], * and the code is a bit _smaller_. */ for (x = 0; x < 5; ++x) { uint64_t temp = BC[x + 4] ^ rotl64(BC[x + 1], 1); state[x] ^= temp; state[x + 5] ^= temp; state[x + 10] ^= temp; state[x + 15] ^= temp; state[x + 20] ^= temp; } } /* Rho Pi */ if (SHA3_SMALL) { uint64_t t1 = state[1]; for (x = 0; x < 24; ++x) { uint64_t t0 = state[PI_LANE[x]]; state[PI_LANE[x]] = rotl64(t1, ROT_CONST[x]); t1 = t0; } } else { /* Especially large benefit for 32-bit arch (75% faster): * 64-bit rotations by non-constant usually are SLOW on those. * We resort to unrolling here. * This optimizes out PI_LANE[] and ROT_CONST[], * but generates 300-500 more bytes of code. */ uint64_t t0; uint64_t t1 = state[1]; #define RhoPi_twice(x) \ t0 = state[PI_LANE[x ]]; \ state[PI_LANE[x ]] = rotl64(t1, ROT_CONST[x ]); \ t1 = state[PI_LANE[x+1]]; \ state[PI_LANE[x+1]] = rotl64(t0, ROT_CONST[x+1]); RhoPi_twice(0); RhoPi_twice(2); RhoPi_twice(4); RhoPi_twice(6); RhoPi_twice(8); RhoPi_twice(10); RhoPi_twice(12); RhoPi_twice(14); RhoPi_twice(16); RhoPi_twice(18); RhoPi_twice(20); RhoPi_twice(22); #undef RhoPi_twice } /* Chi */ for (y = 0; y <= 20; y += 5) { uint64_t BC0, BC1, BC2, BC3, BC4; BC0 = state[y + 0]; BC1 = state[y + 1]; BC2 = state[y + 2]; state[y + 0] = BC0 ^ ((~BC1) & BC2); BC3 = state[y + 3]; state[y + 1] = BC1 ^ ((~BC2) & BC3); BC4 = state[y + 4]; state[y + 2] = BC2 ^ ((~BC3) & BC4); state[y + 3] = BC3 ^ ((~BC4) & BC0); state[y + 4] = BC4 ^ ((~BC0) & BC1); } /* Iota */ state[0] ^= IOTA_CONST[round] | (uint32_t)((IOTA_CONST_bit31 << round) & 0x80000000) | (uint64_t)((IOTA_CONST_bit63 << round) & 0x80000000) << 32; } if (BB_BIG_ENDIAN) { for (x = 0; x < 25; x++) { state[x] = SWAP_LE64(state[x]); } } } void FAST_FUNC sha3_begin(sha3_ctx_t *ctx) { memset(ctx, 0, sizeof(*ctx)); } void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) { #if SHA3_SMALL const uint8_t *data = buffer; unsigned bufpos = ctx->bytes_queued; while (1) { unsigned remaining = SHA3_IBLK_BYTES - bufpos; if (remaining > len) remaining = len; len -= remaining; /* XOR data into buffer */ while (remaining != 0) { uint8_t *buf = (uint8_t*)ctx->state; buf[bufpos] ^= *data++; bufpos++; remaining--; } /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */ bufpos -= SHA3_IBLK_BYTES; if (bufpos != 0) break; /* Buffer is filled up, process it */ sha3_process_block72(ctx->state); /*bufpos = 0; - already is */ } ctx->bytes_queued = bufpos + SHA3_IBLK_BYTES; #else /* +50 bytes code size, but a bit faster because of long-sized XORs */ const uint8_t *data = buffer; unsigned bufpos = ctx->bytes_queued; /* If already data in queue, continue queuing first */ while (len != 0 && bufpos != 0) { uint8_t *buf = (uint8_t*)ctx->state; buf[bufpos] ^= *data++; len--; bufpos++; if (bufpos == SHA3_IBLK_BYTES) { bufpos = 0; goto do_block; } } /* Absorb complete blocks */ while (len >= SHA3_IBLK_BYTES) { /* XOR data onto beginning of state[]. * We try to be efficient - operate one word at a time, not byte. * Careful wrt unaligned access: can't just use "*(long*)data"! */ unsigned count = SHA3_IBLK_BYTES / sizeof(long); long *buf = (long*)ctx->state; do { long v; move_from_unaligned_long(v, (long*)data); *buf++ ^= v; data += sizeof(long); } while (--count); len -= SHA3_IBLK_BYTES; do_block: sha3_process_block72(ctx->state); } /* Queue remaining data bytes */ while (len != 0) { uint8_t *buf = (uint8_t*)ctx->state; buf[bufpos] ^= *data++; bufpos++; len--; } ctx->bytes_queued = bufpos; #endif } void FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf) { /* Padding */ uint8_t *buf = (uint8_t*)ctx->state; buf[ctx->bytes_queued] ^= 1; buf[SHA3_IBLK_BYTES - 1] ^= 0x80; sha3_process_block72(ctx->state); /* Output */ memcpy(resbuf, ctx->state, 64); } busybox-1.22.1/libbb/mode_string.c0000644000000000000000000000661512263563520015532 0ustar rootroot/* 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 source tree. */ /* Aug 13, 2003 * Fix a bug reported by junkio@cox.net involving the mode_chars index. */ #include #include #include "libbb.h" #if ( S_ISUID != 04000 ) || ( S_ISGID != 02000 ) || ( S_ISVTX != 01000 ) \ || ( S_IRUSR != 00400 ) || ( S_IWUSR != 00200 ) || ( S_IXUSR != 00100 ) \ || ( S_IRGRP != 00040 ) || ( S_IWGRP != 00020 ) || ( S_IXGRP != 00010 ) \ || ( S_IROTH != 00004 ) || ( S_IWOTH != 00002 ) || ( S_IXOTH != 00001 ) #error permission bitflag value assumption(s) violated! #endif #if ( S_IFSOCK!= 0140000 ) || ( S_IFLNK != 0120000 ) \ || ( S_IFREG != 0100000 ) || ( S_IFBLK != 0060000 ) \ || ( S_IFDIR != 0040000 ) || ( S_IFCHR != 0020000 ) \ || ( S_IFIFO != 0010000 ) #warning mode type bitflag value assumption(s) violated! falling back to larger version #if (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX) == 07777 #undef mode_t #define mode_t unsigned short #endif static const mode_t mode_flags[] = { S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID, S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID, S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX }; /* The static const char arrays below are duplicated for the two cases * because moving them ahead of the mode_flags declaration cause a text * size increase with the gcc version I'm using. */ /* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C', * and 'B' types don't appear to be available on linux. So I removed them. */ static const char type_chars[16] ALIGN1 = "?pc?d?b?-?l?s???"; /***************************************** 0123456789abcdef */ static const char mode_chars[7] ALIGN1 = "rwxSTst"; const char* FAST_FUNC bb_mode_string(mode_t mode) { static char buf[12]; char *p = buf; int i, j, k; *p = type_chars[ (mode >> 12) & 0xf ]; i = 0; do { j = k = 0; do { *++p = '-'; if (mode & mode_flags[i+j]) { *p = mode_chars[j]; k = j; } } while (++j < 3); if (mode & mode_flags[i+j]) { *p = mode_chars[3 + (k & 2) + ((i&8) >> 3)]; } i += 4; } while (i < 12); /* Note: We don't bother with nul termination because bss initialization * should have taken care of that for us. If the user scribbled in buf * memory, they deserve whatever happens. But we'll at least assert. */ assert(buf[10] == 0); return buf; } #else /* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C', * and 'B' types don't appear to be available on linux. So I removed them. */ static const char type_chars[16] = "?pc?d?b?-?l?s???"; /********************************** 0123456789abcdef */ static const char mode_chars[7] = "rwxSTst"; const char* FAST_FUNC bb_mode_string(mode_t mode) { static char buf[12]; char *p = buf; int i, j, k, m; *p = type_chars[ (mode >> 12) & 0xf ]; i = 0; m = 0400; do { j = k = 0; do { *++p = '-'; if (mode & m) { *p = mode_chars[j]; k = j; } m >>= 1; } while (++j < 3); ++i; if (mode & (010000 >> i)) { *p = mode_chars[3 + (k & 2) + (i == 3)]; } } while (i < 3); /* Note: We don't bother with nul termination because bss initialization * should have taken care of that for us. If the user scribbled in buf * memory, they deserve whatever happens. But we'll at least assert. */ assert(buf[10] == 0); return buf; } #endif busybox-1.22.1/libbb/xregcomp.c0000644000000000000000000000140112263563520015030 0ustar rootroot/* 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 source tree. */ #include "libbb.h" #include "xregex.h" char* FAST_FUNC regcomp_or_errmsg(regex_t *preg, const char *regex, int cflags) { int ret = regcomp(preg, regex, cflags); if (ret) { int errmsgsz = regerror(ret, preg, NULL, 0); char *errmsg = xmalloc(errmsgsz); regerror(ret, preg, errmsg, errmsgsz); return errmsg; } return NULL; } void FAST_FUNC xregcomp(regex_t *preg, const char *regex, int cflags) { char *errmsg = regcomp_or_errmsg(preg, regex, cflags); if (errmsg) { bb_error_msg_and_die("bad regex '%s': %s", regex, errmsg); } } busybox-1.22.1/libbb/inet_cksum.c0000644000000000000000000000154312263563520015354 0ustar rootroot/* * Checksum routine for Internet Protocol family headers (C Version) * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" uint16_t FAST_FUNC inet_cksum(uint16_t *addr, int nleft) { /* * Our algorithm is simple, using a 32 bit accumulator, * we add sequential 16 bit words to it, and at the end, fold * back all the carry bits from the top 16 bits into the lower * 16 bits. */ unsigned sum = 0; while (nleft > 1) { sum += *addr++; nleft -= 2; } /* Mop up an odd byte, if necessary */ if (nleft == 1) { if (BB_LITTLE_ENDIAN) sum += *(uint8_t*)addr; else sum += *(uint8_t*)addr << 8; } /* Add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ return (uint16_t)~sum; } busybox-1.22.1/libbb/missing_syscalls.c0000644000000000000000000000144612263563520016603 0ustar rootroot/* * Copyright 2012, Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ //kbuild:lib-y += missing_syscalls.o /*#include - for struct timex, but may collide with */ #include #include "libbb.h" #if defined(ANDROID) || defined(__ANDROID__) pid_t getsid(pid_t pid) { return syscall(__NR_getsid, pid); } int stime(const time_t *t) { struct timeval tv; tv.tv_sec = *t; tv.tv_usec = 0; return settimeofday(&tv, NULL); } int sethostname(const char *name, size_t len) { return syscall(__NR_sethostname, name, len); } struct timex; int adjtimex(struct timex *buf) { return syscall(__NR_adjtimex, buf); } int pivot_root(const char *new_root, const char *put_old) { return syscall(__NR_pivot_root, new_root, put_old); } #endif busybox-1.22.1/libbb/pw_encrypt_md5.c0000644000000000000000000001177712263563520016164 0ustar rootroot/* * MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm * * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All * rights reserved. * * License to copy and use this software is granted provided that it * is identified as the "RSA Data Security, Inc. MD5 Message-Digest * Algorithm" in all material mentioning or referencing this software * or this function. * * License is also granted to make and use derivative works provided * that such works are identified as "derived from the RSA Data * Security, Inc. MD5 Message-Digest Algorithm" in all material * mentioning or referencing the derived work. * * RSA Data Security, Inc. makes no representations concerning either * the merchantability of this software or the suitability of this * software for any particular purpose. It is provided "as is" * without express or implied warranty of any kind. * * These notices must be retained in any copies of any part of this * documentation and/or software. * * $FreeBSD: src/lib/libmd/md5c.c,v 1.9.2.1 1999/08/29 14:57:12 peter Exp $ * * This code is the same as the code published by RSA Inc. It has been * edited for clarity and style only. * * ---------------------------------------------------------------------------- * The md5_crypt() function was taken from freeBSD's libcrypt and contains * this license: * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * * $FreeBSD: src/lib/libcrypt/crypt.c,v 1.7.2.1 1999/08/29 14:56:33 peter Exp $ * * ---------------------------------------------------------------------------- * On April 19th, 2001 md5_crypt() was modified to make it reentrant * by Erik Andersen * * * June 28, 2001 Manuel Novoa III * * "Un-inlined" code using loops and static const tables in order to * reduce generated code size (on i386 from approx 4k to approx 2.5k). * * June 29, 2001 Manuel Novoa III * * Completely removed static PADDING array. * * Reintroduced the loop unrolling in MD5_Transform and added the * MD5_SIZE_OVER_SPEED option for configurability. Define below as: * 0 fully unrolled loops * 1 partially unrolled (4 ops per loop) * 2 no unrolling -- introduces the need to swap 4 variables (slow) * 3 no unrolling and all 4 loops merged into one with switch * in each loop (glacial) * On i386, sizes are roughly (-Os -fno-builtin): * 0: 3k 1: 2.5k 2: 2.2k 3: 2k * * Since SuSv3 does not require crypt_r, modified again August 7, 2002 * by Erik Andersen to remove reentrance stuff... */ /* * UNIX password * * Use MD5 for what it is best at... */ #define MD5_OUT_BUFSIZE 36 static char * NOINLINE md5_crypt(char result[MD5_OUT_BUFSIZE], const unsigned char *pw, const unsigned char *salt) { char *p; unsigned char final[17]; /* final[16] exists only to aid in looping */ int sl, pl, i, pw_len; md5_ctx_t ctx, ctx1; /* NB: in busybox, "$1$" in salt is always present */ /* Refine the Salt first */ /* Get the length of the salt including "$1$" */ sl = 3; while (sl < (3 + 8) && salt[sl] && salt[sl] != '$') sl++; /* Hash. the password first, since that is what is most unknown */ md5_begin(&ctx); pw_len = strlen((char*)pw); md5_hash(&ctx, pw, pw_len); /* Then the salt including "$1$" */ md5_hash(&ctx, salt, sl); /* Copy salt to result; skip "$1$" */ memcpy(result, salt, sl); result[sl] = '$'; salt += 3; sl -= 3; /* Then just as many characters of the MD5(pw, salt, pw) */ md5_begin(&ctx1); md5_hash(&ctx1, pw, pw_len); md5_hash(&ctx1, salt, sl); md5_hash(&ctx1, pw, pw_len); md5_end(&ctx1, final); for (pl = pw_len; pl > 0; pl -= 16) md5_hash(&ctx, final, pl > 16 ? 16 : pl); /* Then something really weird... */ memset(final, 0, sizeof(final)); for (i = pw_len; i; i >>= 1) { md5_hash(&ctx, ((i & 1) ? final : (const unsigned char *) pw), 1); } md5_end(&ctx, final); /* And now, just to make sure things don't run too fast. * On a 60 Mhz Pentium this takes 34 msec, so you would * need 30 seconds to build a 1000 entry dictionary... */ for (i = 0; i < 1000; i++) { md5_begin(&ctx1); if (i & 1) md5_hash(&ctx1, pw, pw_len); else md5_hash(&ctx1, final, 16); if (i % 3) md5_hash(&ctx1, salt, sl); if (i % 7) md5_hash(&ctx1, pw, pw_len); if (i & 1) md5_hash(&ctx1, final, 16); else md5_hash(&ctx1, pw, pw_len); md5_end(&ctx1, final); } p = result + sl + 4; /* 12 bytes max (sl is up to 8 bytes) */ /* Add 5*4+2 = 22 bytes of hash, + NUL byte. */ final[16] = final[5]; for (i = 0; i < 5; i++) { unsigned l = (final[i] << 16) | (final[i+6] << 8) | final[i+12]; p = to64(p, l, 4); } p = to64(p, final[11], 2); *p = '\0'; /* Don't leave anything around in vm they could use. */ memset(final, 0, sizeof(final)); return result; } busybox-1.22.1/libbb/warn_ignoring_args.c0000644000000000000000000000056712263563520017077 0ustar rootroot/* 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 source tree. */ #include "libbb.h" #if ENABLE_DESKTOP void FAST_FUNC bb_warn_ignoring_args(char *arg) { if (arg) { bb_error_msg("ignoring all arguments"); } } #endif busybox-1.22.1/libbb/uuencode.c0000644000000000000000000001351212263563520015021 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright 2003, Glenn McGrath * Copyright 2006, Rob Landley * Copyright 2010, Denys Vlasenko * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" /* Conversion table. for base 64 */ const char bb_uuenc_tbl_base64[65 + 1] ALIGN1 = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '=' /* termination character */, '\0' /* needed for uudecode.c only */ }; const char bb_uuenc_tbl_std[65] ALIGN1 = { '`', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`' /* termination character */ }; /* * Encode bytes at S of length LENGTH to uuencode or base64 format and place it * to STORE. STORE will be 0-terminated, and must point to a writable * buffer of at least 1+BASE64_LENGTH(length) bytes. * where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3)) */ void FAST_FUNC bb_uuencode(char *p, const void *src, int length, const char *tbl) { const unsigned char *s = src; /* Transform the 3x8 bits to 4x6 bits */ while (length > 0) { unsigned s1, s2; /* Are s[1], s[2] valid or should be assumed 0? */ s1 = s2 = 0; length -= 3; /* can be >=0, -1, -2 */ if (length >= -1) { s1 = s[1]; if (length >= 0) s2 = s[2]; } *p++ = tbl[s[0] >> 2]; *p++ = tbl[((s[0] & 3) << 4) + (s1 >> 4)]; *p++ = tbl[((s1 & 0xf) << 2) + (s2 >> 6)]; *p++ = tbl[s2 & 0x3f]; s += 3; } /* Zero-terminate */ *p = '\0'; /* If length is -2 or -1, pad last char or two */ while (length) { *--p = tbl[64]; length++; } } /* * Decode base64 encoded string. Stops on '\0'. * * Returns: pointer to the undecoded part of source. * If points to '\0', then the source was fully decoded. * (*pp_dst): advanced past the last written byte. */ const char* FAST_FUNC decode_base64(char **pp_dst, const char *src) { char *dst = *pp_dst; const char *src_tail; while (1) { unsigned char six_bit[4]; int count = 0; /* Fetch up to four 6-bit values */ src_tail = src; while (count < 4) { char *table_ptr; int ch; /* Get next _valid_ character. * bb_uuenc_tbl_base64[] contains this string: * 0 1 2 3 4 5 6 * 01234567890123456789012345678901234567890123456789012345678901234 * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" */ do { ch = *src; if (ch == '\0') { if (count == 0) { /* Example: * If we decode "QUJD ", we want * to return ptr to NUL, not to ' ', * because we did fully decode * the string (to "ABC"). */ src_tail = src; } goto ret; } src++; table_ptr = strchr(bb_uuenc_tbl_base64, ch); //TODO: add BASE64_FLAG_foo to die on bad char? } while (!table_ptr); /* Convert encoded character to decimal */ ch = table_ptr - bb_uuenc_tbl_base64; /* ch is 64 if char was '=', otherwise 0..63 */ if (ch == 64) break; six_bit[count] = ch; count++; } /* Transform 6-bit values to 8-bit ones. * count can be < 4 when we decode the tail: * "eQ==" -> "y", not "y NUL NUL". * Note that (count > 1) is always true, * "x===" encoding is not valid: * even a single zero byte encodes as "AA==". * However, with current logic we come here with count == 1 * when we decode "==" tail. */ if (count > 1) *dst++ = six_bit[0] << 2 | six_bit[1] >> 4; if (count > 2) *dst++ = six_bit[1] << 4 | six_bit[2] >> 2; if (count > 3) *dst++ = six_bit[2] << 6 | six_bit[3]; /* Note that if we decode "AA==" and ate first '=', * we just decoded one char (count == 2) and now we'll * do the loop once more to decode second '='. */ } /* while (1) */ ret: *pp_dst = dst; return src_tail; } /* * Decode base64 encoded stream. * Can stop on EOF, specified char, or on uuencode-style "====" line: * flags argument controls it. */ void FAST_FUNC read_base64(FILE *src_stream, FILE *dst_stream, int flags) { /* Note that EOF _can_ be passed as exit_char too */ #define exit_char ((int)(signed char)flags) #define uu_style_end (flags & BASE64_FLAG_UU_STOP) /* uuencoded files have 61 byte lines. Use 64 byte buffer * to process line at a time. */ enum { BUFFER_SIZE = 64 }; char in_buf[BUFFER_SIZE + 2]; char out_buf[BUFFER_SIZE / 4 * 3 + 2]; char *out_tail; const char *in_tail; int term_seen = 0; int in_count = 0; while (1) { while (in_count < BUFFER_SIZE) { int ch = fgetc(src_stream); if (ch == exit_char) { if (in_count == 0) return; term_seen = 1; break; } if (ch == EOF) { term_seen = 1; break; } /* Prevent "====" line to be split: stop if we see '\n'. * We can also skip other whitespace and skirt the problem * of files with NULs by stopping on any control char or space: */ if (ch <= ' ') break; in_buf[in_count++] = ch; } in_buf[in_count] = '\0'; /* Did we encounter "====" line? */ if (uu_style_end && strcmp(in_buf, "====") == 0) return; out_tail = out_buf; in_tail = decode_base64(&out_tail, in_buf); fwrite(out_buf, (out_tail - out_buf), 1, dst_stream); if (term_seen) { /* Did we consume ALL characters? */ if (*in_tail == '\0') return; /* No */ bb_error_msg_and_die("truncated base64 input"); } /* It was partial decode */ in_count = strlen(in_tail); memmove(in_buf, in_tail, in_count); } } busybox-1.22.1/libbb/read_printf.c0000644000000000000000000001406212263563520015510 0ustar rootroot/* 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 source tree. */ #include "libbb.h" /* Suppose that you are a shell. You start child processes. * They work and eventually exit. You want to get user input. * You read stdin. But what happens if last child switched * its stdin into O_NONBLOCK mode? * * *** SURPRISE! It will affect the parent too! *** * *** BIG SURPRISE! It stays even after child exits! *** * * This is a design bug in UNIX API. * fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK); * will set nonblocking mode not only on _your_ stdin, but * also on stdin of your parent, etc. * * In general, * fd2 = dup(fd1); * fcntl(fd2, F_SETFL, fcntl(fd2, F_GETFL) | O_NONBLOCK); * sets both fd1 and fd2 to O_NONBLOCK. This includes cases * where duping is done implicitly by fork() etc. * * We need * fcntl(fd2, F_SETFD, fcntl(fd2, F_GETFD) | O_NONBLOCK); * (note SETFD, not SETFL!) but such thing doesn't exist. * * Alternatively, we need nonblocking_read(fd, ...) which doesn't * require O_NONBLOCK dance at all. Actually, it exists: * n = recv(fd, buf, len, MSG_DONTWAIT); * "MSG_DONTWAIT: * Enables non-blocking operation; if the operation * would block, EAGAIN is returned." * but recv() works only for sockets! * * So far I don't see any good solution, I can only propose * that affected readers should be careful and use this routine, * which detects EAGAIN and uses poll() to wait on the fd. * Thankfully, poll() doesn't care about O_NONBLOCK flag. */ ssize_t FAST_FUNC nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR) { struct pollfd pfd[1]; ssize_t n; while (1) { n = loop_on_EINTR ? safe_read(fd, buf, count) : read(fd, buf, count); if (n >= 0 || errno != EAGAIN) return n; /* fd is in O_NONBLOCK mode. Wait using poll and repeat */ pfd[0].fd = fd; pfd[0].events = POLLIN; /* note: safe_poll pulls in printf */ loop_on_EINTR ? safe_poll(pfd, 1, -1) : poll(pfd, 1, -1); } } // Reads one line a-la fgets (but doesn't save terminating '\n'). // Reads byte-by-byte. Useful when it is important to not read ahead. // Bytes are appended to pfx (which must be malloced, or NULL). char* FAST_FUNC xmalloc_reads(int fd, size_t *maxsz_p) { char *p; char *buf = NULL; size_t sz = 0; size_t maxsz = maxsz_p ? *maxsz_p : (INT_MAX - 4095); goto jump_in; while (sz < maxsz) { if ((size_t)(p - buf) == sz) { jump_in: buf = xrealloc(buf, sz + 128); p = buf + sz; sz += 128; } if (nonblock_immune_read(fd, p, 1, /*loop_on_EINTR:*/ 1) != 1) { /* EOF/error */ if (p == buf) { /* we read nothing */ free(buf); return NULL; } break; } if (*p == '\n') break; p++; } *p = '\0'; if (maxsz_p) *maxsz_p = p - buf; p++; return xrealloc(buf, p - buf); } // Read (potentially big) files in one go. File size is estimated // by stat. Extra '\0' byte is appended. void* FAST_FUNC xmalloc_read(int fd, size_t *maxsz_p) { char *buf; size_t size, rd_size, total; size_t to_read; struct stat st; to_read = maxsz_p ? *maxsz_p : (INT_MAX - 4095); /* max to read */ /* Estimate file size */ st.st_size = 0; /* in case fstat fails, assume 0 */ fstat(fd, &st); /* /proc/N/stat files report st_size 0 */ /* In order to make such files readable, we add small const */ size = (st.st_size | 0x3ff) + 1; total = 0; buf = NULL; while (1) { if (to_read < size) size = to_read; buf = xrealloc(buf, total + size + 1); rd_size = full_read(fd, buf + total, size); if ((ssize_t)rd_size == (ssize_t)(-1)) { /* error */ free(buf); return NULL; } total += rd_size; if (rd_size < size) /* EOF */ break; if (to_read <= rd_size) break; to_read -= rd_size; /* grow by 1/8, but in [1k..64k] bounds */ size = ((total / 8) | 0x3ff) + 1; if (size > 64*1024) size = 64*1024; } buf = xrealloc(buf, total + 1); buf[total] = '\0'; if (maxsz_p) *maxsz_p = total; return buf; } #ifdef USING_LSEEK_TO_GET_SIZE /* Alternatively, file size can be obtained by lseek to the end. * The code is slightly bigger. Retained in case fstat approach * will not work for some weird cases (/proc, block devices, etc). * (NB: lseek also can fail to work for some weird files) */ // Read (potentially big) files in one go. File size is estimated by // lseek to end. void* FAST_FUNC xmalloc_open_read_close(const char *filename, size_t *maxsz_p) { char *buf; size_t size; int fd; off_t len; fd = open(filename, O_RDONLY); if (fd < 0) return NULL; /* /proc/N/stat files report len 0 here */ /* In order to make such files readable, we add small const */ size = 0x3ff; /* read only 1k on unseekable files */ len = lseek(fd, 0, SEEK_END) | 0x3ff; /* + up to 1k */ if (len != (off_t)-1) { xlseek(fd, 0, SEEK_SET); size = maxsz_p ? *maxsz_p : (INT_MAX - 4095); if (len < size) size = len; } buf = xmalloc(size + 1); size = read_close(fd, buf, size); if ((ssize_t)size < 0) { free(buf); return NULL; } buf = xrealloc(buf, size + 1); buf[size] = '\0'; if (maxsz_p) *maxsz_p = size; return buf; } #endif // Read (potentially big) files in one go. File size is estimated // by stat. void* FAST_FUNC xmalloc_open_read_close(const char *filename, size_t *maxsz_p) { char *buf; int fd; fd = open(filename, O_RDONLY); if (fd < 0) return NULL; buf = xmalloc_read(fd, maxsz_p); close(fd); return buf; } /* Die with an error message if we can't read the entire buffer. */ void FAST_FUNC xread(int fd, void *buf, size_t count) { if (count) { ssize_t size = full_read(fd, buf, count); if ((size_t)size != count) bb_error_msg_and_die("short read"); } } /* Die with an error message if we can't read one character. */ unsigned char FAST_FUNC xread_char(int fd) { char tmp; xread(fd, &tmp, 1); return tmp; } void* FAST_FUNC xmalloc_xopen_read_close(const char *filename, size_t *maxsz_p) { void *buf = xmalloc_open_read_close(filename, maxsz_p); if (!buf) bb_perror_msg_and_die("can't read '%s'", filename); return buf; } busybox-1.22.1/libbb/strrstr.c0000644000000000000000000000301312263563520014730 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 2008 Bernhard Reutner-Fischer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #ifdef __DO_STRRSTR_TEST #include #include #include #else #include "libbb.h" #endif /* * The strrstr() function finds the last occurrence of the substring needle * in the string haystack. The terminating nul characters are not compared. */ char* FAST_FUNC strrstr(const char *haystack, const char *needle) { char *r = NULL; if (!needle[0]) return (char*)haystack + strlen(haystack); while (1) { char *p = strstr(haystack, needle); if (!p) return r; r = p; haystack = p + 1; } } #ifdef __DO_STRRSTR_TEST int main(int argc, char **argv) { static const struct { const char *h, *n; int pos; } test_array[] = { /* 0123456789 */ { "baaabaaab", "aaa", 5 }, { "baaabaaaab", "aaa", 6 }, { "baaabaab", "aaa", 1 }, { "aaa", "aaa", 0 }, { "aaa", "a", 2 }, { "aaa", "bbb", -1 }, { "a", "aaa", -1 }, { "aaa", "", 3 }, { "", "aaa", -1 }, { "", "", 0 }, }; int i; i = 0; while (i < sizeof(test_array) / sizeof(test_array[0])) { const char *r = strrstr(test_array[i].h, test_array[i].n); printf("'%s' vs. '%s': '%s' - ", test_array[i].h, test_array[i].n, r); if (r == NULL) r = test_array[i].h - 1; printf("%s\n", r == test_array[i].h + test_array[i].pos ? "PASSED" : "FAILED"); i++; } return 0; } #endif busybox-1.22.1/libbb/find_mount_point.c0000644000000000000000000000325712263563520016572 0ustar rootroot/* 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 source tree. */ #include "libbb.h" #include /* * Given a block device, find the mount table entry if that block device * is mounted. * * Given any other file (or directory), find the mount table entry for its * filesystem. */ struct mntent* FAST_FUNC find_mount_point(const char *name, int subdir_too) { struct stat s; FILE *mtab_fp; struct mntent *mountEntry; dev_t devno_of_name; bool block_dev; if (stat(name, &s) != 0) return NULL; devno_of_name = s.st_dev; block_dev = 0; /* Why S_ISCHR? - UBI volumes use char devices, not block */ if (S_ISBLK(s.st_mode) || S_ISCHR(s.st_mode)) { devno_of_name = s.st_rdev; block_dev = 1; } mtab_fp = setmntent(bb_path_mtab_file, "r"); if (!mtab_fp) return NULL; while ((mountEntry = getmntent(mtab_fp)) != NULL) { /* rootfs mount in Linux 2.6 exists always, * and it makes sense to always ignore it. * Otherwise people can't reference their "real" root! */ if (ENABLE_FEATURE_SKIP_ROOTFS && strcmp(mountEntry->mnt_fsname, "rootfs") == 0) continue; if (strcmp(name, mountEntry->mnt_dir) == 0 || strcmp(name, mountEntry->mnt_fsname) == 0 ) { /* String match. */ break; } if (!(subdir_too || block_dev)) continue; /* Is device's dev_t == name's dev_t? */ if (stat(mountEntry->mnt_fsname, &s) == 0 && s.st_rdev == devno_of_name) break; /* Match the directory's mount point. */ if (stat(mountEntry->mnt_dir, &s) == 0 && s.st_dev == devno_of_name) break; } endmntent(mtab_fp); return mountEntry; } busybox-1.22.1/libbb/safe_gethostname.c0000644000000000000000000000344712263563520016534 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Safe gethostname implementation for busybox * * Copyright (C) 2008 Tito Ragusa * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* * SUSv2 guarantees that "Host names are limited to 255 bytes" * POSIX.1-2001 guarantees that "Host names (not including the terminating * null byte) are limited to HOST_NAME_MAX bytes" (64 bytes on my box). * * RFC1123 says: * * The syntax of a legal Internet host name was specified in RFC-952 * [DNS:4]. One aspect of host name syntax is hereby changed: the * restriction on the first character is relaxed to allow either a * letter or a digit. Host software MUST support this more liberal * syntax. * * Host software MUST handle host names of up to 63 characters and * SHOULD handle host names of up to 255 characters. */ #include "libbb.h" #include /* * On success return the current malloced and NUL terminated hostname. * On error return malloced and NUL terminated string "?". * This is an illegal first character for a hostname. * The returned malloced string must be freed by the caller. */ char* FAST_FUNC safe_gethostname(void) { struct utsname uts; /* The length of the arrays in a struct utsname is unspecified; * the fields are terminated by a null byte. * Note that there is no standard that says that the hostname * set by sethostname(2) is the same string as the nodename field of the * struct returned by uname (indeed, some systems allow a 256-byte host- * name and an 8-byte nodename), but this is true on Linux. The same holds * for setdomainname(2) and the domainname field. */ /* Uname can fail only if you pass a bad pointer to it. */ uname(&uts); return xstrndup(!uts.nodename[0] ? "?" : uts.nodename, sizeof(uts.nodename)); } busybox-1.22.1/libbb/get_console.c0000644000000000000000000000322312263563520015511 0ustar rootroot/* 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 source tree. */ #include "libbb.h" /* From */ enum { KDGKBTYPE = 0x4B33 }; /* get keyboard type */ static int open_a_console(const char *fnam) { int fd; /* try read-write */ fd = open(fnam, O_RDWR); /* if failed, try read-only */ if (fd < 0 && errno == EACCES) fd = open(fnam, O_RDONLY); /* if failed, try write-only */ if (fd < 0 && errno == EACCES) fd = open(fnam, O_WRONLY); return fd; } /* * Get an fd for use with kbd/console ioctls. * We try several things because opening /dev/console will fail * if someone else used X (which does a chown on /dev/console). */ int FAST_FUNC get_console_fd_or_die(void) { static const char *const console_names[] = { DEV_CONSOLE, CURRENT_VC, CURRENT_TTY }; int fd; for (fd = 2; fd >= 0; fd--) { int fd4name; int choice_fd; char arg; fd4name = open_a_console(console_names[fd]); chk_std: choice_fd = (fd4name >= 0 ? fd4name : fd); arg = 0; if (ioctl(choice_fd, KDGKBTYPE, &arg) == 0) return choice_fd; if (fd4name >= 0) { close(fd4name); fd4name = -1; goto chk_std; } } bb_error_msg_and_die("can't open console"); /*return fd; - total failure */ } /* From */ enum { VT_ACTIVATE = 0x5606, /* make vt active */ VT_WAITACTIVE = 0x5607 /* wait for vt active */ }; void FAST_FUNC console_make_active(int fd, const int vt_num) { xioctl(fd, VT_ACTIVATE, (void *)(ptrdiff_t)vt_num); xioctl(fd, VT_WAITACTIVE, (void *)(ptrdiff_t)vt_num); } busybox-1.22.1/libbb/human_readable.c0000644000000000000000000001241312263563520016140 0ustar rootroot/* 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. * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" const char* FAST_FUNC make_human_readable_str(unsigned long long val, unsigned long block_size, unsigned long display_unit) { static const char unit_chars[] ALIGN1 = { '\0', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' }; static char *str; unsigned frac; /* 0..9 - the fractional digit */ const char *u; const char *fmt; if (val == 0) return "0"; fmt = "%llu"; if (block_size > 1) val *= block_size; frac = 0; u = unit_chars; if (display_unit) { val += display_unit/2; /* Deal with rounding */ val /= display_unit; /* Don't combine with the line above! */ /* will just print it as ulonglong (below) */ } else { while ((val >= 1024) /* && (u < unit_chars + sizeof(unit_chars) - 1) - always true */ ) { fmt = "%llu.%u%c"; u++; frac = (((unsigned)val % 1024) * 10 + 1024/2) / 1024; val /= 1024; } if (frac >= 10) { /* we need to round up here */ ++val; frac = 0; } #if 1 /* If block_size is 0, dont print fractional part */ if (block_size == 0) { if (frac >= 5) { ++val; } fmt = "%llu%*c"; frac = 1; } #endif } if (!str) { /* sufficient for any width of val */ str = xmalloc(sizeof(val)*3 + 2 + 3); } sprintf(str, fmt, val, frac, *u); return str; } /* vda's implementations of the similar idea */ /* Convert unsigned long long value into compact 5-char representation. * String is not terminated (buf[5] is untouched) */ char* FAST_FUNC smart_ulltoa5(unsigned long long ul, char buf[5], const char *scale) { const char *fmt; char c; unsigned v, u, idx = 0; if (ul > 99999) { // do not scale if 99999 or less ul *= 10; do { ul /= 1024; idx++; } while (ul >= 100000); } v = ul; // ullong divisions are expensive, avoid them fmt = " 123456789"; u = v / 10; v = v % 10; if (!idx) { // 99999 or less: use "12345" format // u is value/10, v is last digit c = buf[0] = " 123456789"[u/1000]; if (c != ' ') fmt = "0123456789"; c = buf[1] = fmt[u/100%10]; if (c != ' ') fmt = "0123456789"; c = buf[2] = fmt[u/10%10]; if (c != ' ') fmt = "0123456789"; buf[3] = fmt[u%10]; buf[4] = "0123456789"[v]; } else { // value has been scaled into 0..9999.9 range // u is value, v is 1/10ths (allows for 92.1M format) if (u >= 100) { // value is >= 100: use "1234M', " 123M" formats c = buf[0] = " 123456789"[u/1000]; if (c != ' ') fmt = "0123456789"; c = buf[1] = fmt[u/100%10]; if (c != ' ') fmt = "0123456789"; v = u % 10; u = u / 10; buf[2] = fmt[u%10]; } else { // value is < 100: use "92.1M" format c = buf[0] = " 123456789"[u/10]; if (c != ' ') fmt = "0123456789"; buf[1] = fmt[u%10]; buf[2] = '.'; } buf[3] = "0123456789"[v]; buf[4] = scale[idx]; /* typically scale = " kmgt..." */ } return buf + 5; } /* Convert unsigned long long value into compact 4-char * representation. Examples: "1234", "1.2k", " 27M", "123T" * String is not terminated (buf[4] is untouched) */ char* FAST_FUNC smart_ulltoa4(unsigned long long ul, char buf[4], const char *scale) { const char *fmt; char c; unsigned v, u, idx = 0; if (ul > 9999) { // do not scale if 9999 or less ul *= 10; do { ul /= 1024; idx++; } while (ul >= 10000); } v = ul; // ullong divisions are expensive, avoid them fmt = " 123456789"; u = v / 10; v = v % 10; if (!idx) { // 9999 or less: use "1234" format // u is value/10, v is last digit c = buf[0] = " 123456789"[u/100]; if (c != ' ') fmt = "0123456789"; c = buf[1] = fmt[u/10%10]; if (c != ' ') fmt = "0123456789"; buf[2] = fmt[u%10]; buf[3] = "0123456789"[v]; } else { // u is value, v is 1/10ths (allows for 9.2M format) if (u >= 10) { // value is >= 10: use "123M', " 12M" formats c = buf[0] = " 123456789"[u/100]; if (c != ' ') fmt = "0123456789"; v = u % 10; u = u / 10; buf[1] = fmt[u%10]; } else { // value is < 10: use "9.2M" format buf[0] = "0123456789"[u]; buf[1] = '.'; } buf[2] = "0123456789"[v]; buf[3] = scale[idx]; /* typically scale = " kmgt..." */ } return buf + 4; } busybox-1.22.1/libbb/get_last_path_component.c0000644000000000000000000000175412263563520020117 0ustar rootroot/* 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 source tree. */ #include "libbb.h" const char* FAST_FUNC bb_basename(const char *name) { const char *cp = strrchr(name, '/'); if (cp) return cp + 1; return name; } /* * "/" -> "/" * "abc" -> "abc" * "abc/def" -> "def" * "abc/def/" -> "" */ char* FAST_FUNC bb_get_last_path_component_nostrip(const char *path) { char *slash = strrchr(path, '/'); if (!slash || (slash == path && !slash[1])) return (char*)path; return slash + 1; } /* * "/" -> "/" * "abc" -> "abc" * "abc/def" -> "def" * "abc/def/" -> "def" !! */ char* FAST_FUNC bb_get_last_path_component_strip(char *path) { char *slash = last_char_is(path, '/'); if (slash) while (*slash == '/' && slash != path) *slash-- = '\0'; return bb_get_last_path_component_nostrip(path); } busybox-1.22.1/libbb/loop.c0000644000000000000000000001111412263563520014157 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 1999-2004 by Erik Andersen * Copyright (C) 2005 by Rob Landley * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) /* For 2.6, use the cleaned up header to get the 64 bit API. */ // Commented out per Rob's request //# include "fix_u32.h" /* some old toolchains need __u64 for linux/loop.h */ # include typedef struct loop_info64 bb_loop_info; # define BB_LOOP_SET_STATUS LOOP_SET_STATUS64 # define BB_LOOP_GET_STATUS LOOP_GET_STATUS64 #else /* For 2.4 and earlier, use the 32 bit API (and don't trust the headers) */ /* Stuff stolen from linux/loop.h for 2.4 and earlier kernels */ # include # define LO_NAME_SIZE 64 # define LO_KEY_SIZE 32 # define LOOP_SET_FD 0x4C00 # define LOOP_CLR_FD 0x4C01 # define BB_LOOP_SET_STATUS 0x4C02 # define BB_LOOP_GET_STATUS 0x4C03 typedef struct { int lo_number; __kernel_dev_t lo_device; unsigned long lo_inode; __kernel_dev_t lo_rdevice; int lo_offset; int lo_encrypt_type; int lo_encrypt_key_size; int lo_flags; char lo_file_name[LO_NAME_SIZE]; unsigned char lo_encrypt_key[LO_KEY_SIZE]; unsigned long lo_init[2]; char reserved[4]; } bb_loop_info; #endif char* FAST_FUNC query_loop(const char *device) { int fd; bb_loop_info loopinfo; char *dev = NULL; fd = open(device, O_RDONLY); if (fd >= 0) { if (ioctl(fd, BB_LOOP_GET_STATUS, &loopinfo) == 0) { dev = xasprintf("%"OFF_FMT"u %s", (off_t) loopinfo.lo_offset, (char *)loopinfo.lo_file_name); } close(fd); } return dev; } int FAST_FUNC del_loop(const char *device) { int fd, rc; fd = open(device, O_RDONLY); if (fd < 0) return 1; rc = ioctl(fd, LOOP_CLR_FD, 0); close(fd); return rc; } /* Returns 0 if mounted RW, 1 if mounted read-only, <0 for error. *device is loop device to use, or if *device==NULL finds a loop device to mount it on and sets *device to a strdup of that loop device name. This search will re-use an existing loop device already bound to that file/offset if it finds one. */ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offset, int ro) { char dev[LOOP_NAMESIZE]; char *try; bb_loop_info loopinfo; struct stat statbuf; int i, dfd, ffd, mode, rc = -1; /* Open the file. Barf if this doesn't work. */ mode = ro ? O_RDONLY : O_RDWR; ffd = open(file, mode); if (ffd < 0) { if (mode != O_RDONLY) { mode = O_RDONLY; ffd = open(file, mode); } if (ffd < 0) return -errno; } /* Find a loop device. */ try = *device ? *device : dev; /* 1048575 is a max possible minor number in Linux circa 2010 */ for (i = 0; rc && i < 1048576; i++) { sprintf(dev, LOOP_FORMAT, i); IF_FEATURE_MOUNT_LOOP_CREATE(errno = 0;) if (stat(try, &statbuf) != 0 || !S_ISBLK(statbuf.st_mode)) { if (ENABLE_FEATURE_MOUNT_LOOP_CREATE && errno == ENOENT && try == dev ) { /* Node doesn't exist, try to create it. */ if (mknod(dev, S_IFBLK|0644, makedev(7, i)) == 0) goto try_to_open; } /* Ran out of block devices, return failure. */ rc = -ENOENT; break; } try_to_open: /* Open the sucker and check its loopiness. */ dfd = open(try, mode); if (dfd < 0 && errno == EROFS) { mode = O_RDONLY; dfd = open(try, mode); } if (dfd < 0) goto try_again; rc = ioctl(dfd, BB_LOOP_GET_STATUS, &loopinfo); /* If device is free, claim it. */ if (rc && errno == ENXIO) { memset(&loopinfo, 0, sizeof(loopinfo)); safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE); loopinfo.lo_offset = offset; /* Associate free loop device with file. */ if (ioctl(dfd, LOOP_SET_FD, ffd) == 0) { if (ioctl(dfd, BB_LOOP_SET_STATUS, &loopinfo) == 0) rc = 0; else ioctl(dfd, LOOP_CLR_FD, 0); } /* If this block device already set up right, re-use it. * (Yes this is racy, but associating two loop devices with the same * file isn't pretty either. In general, mounting the same file twice * without using losetup manually is problematic.) */ } else if (strcmp(file, (char *)loopinfo.lo_file_name) != 0 || offset != loopinfo.lo_offset ) { rc = -1; } close(dfd); try_again: if (*device) break; } close(ffd); if (rc == 0) { if (!*device) *device = xstrdup(dev); return (mode == O_RDONLY); /* 1:ro, 0:rw */ } return rc; } busybox-1.22.1/libbb/bb_qsort.c0000644000000000000000000000066412263563520015031 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Wrapper for common string vector sorting operation * * Copyright (c) 2008 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" int /* not FAST_FUNC! */ bb_pstrcmp(const void *a, const void *b) { return strcmp(*(char**)a, *(char**)b); } void FAST_FUNC qsort_string_vector(char **sv, unsigned count) { qsort(sv, count, sizeof(char*), bb_pstrcmp); } busybox-1.22.1/libbb/pw_encrypt_sha.c0000644000000000000000000002232512263563520016241 0ustar rootroot/* SHA256 and SHA512-based Unix crypt implementation. * Released into the Public Domain by Ulrich Drepper . */ /* Prefix for optional rounds specification. */ static const char str_rounds[] ALIGN1 = "rounds=%u$"; /* Maximum salt string length. */ #define SALT_LEN_MAX 16 /* Default number of rounds if not explicitly specified. */ #define ROUNDS_DEFAULT 5000 /* Minimum number of rounds. */ #define ROUNDS_MIN 1000 /* Maximum number of rounds. */ #define ROUNDS_MAX 999999999 static char * NOINLINE sha_crypt(/*const*/ char *key_data, /*const*/ char *salt_data) { void (*sha_begin)(void *ctx) FAST_FUNC; void (*sha_hash)(void *ctx, const void *buffer, size_t len) FAST_FUNC; void (*sha_end)(void *ctx, void *resbuf) FAST_FUNC; int _32or64; char *result, *resptr; /* btw, sha256 needs [32] and uint32_t only */ struct { unsigned char alt_result[64]; unsigned char temp_result[64]; union { sha256_ctx_t x; sha512_ctx_t y; } ctx; union { sha256_ctx_t x; sha512_ctx_t y; } alt_ctx; } L __attribute__((__aligned__(__alignof__(uint64_t)))); #define alt_result (L.alt_result ) #define temp_result (L.temp_result) #define ctx (L.ctx ) #define alt_ctx (L.alt_ctx ) unsigned salt_len; unsigned key_len; unsigned cnt; unsigned rounds; char *cp; char is_sha512; /* Analyze salt, construct already known part of result */ cnt = strlen(salt_data) + 1 + 43 + 1; is_sha512 = salt_data[1]; if (is_sha512 == '6') cnt += 43; result = resptr = xzalloc(cnt); /* will provide NUL terminator */ *resptr++ = '$'; *resptr++ = is_sha512; *resptr++ = '$'; rounds = ROUNDS_DEFAULT; salt_data += 3; if (strncmp(salt_data, str_rounds, 7) == 0) { /* 7 == strlen("rounds=") */ char *endp; cnt = bb_strtou(salt_data + 7, &endp, 10); if (*endp == '$') { salt_data = endp + 1; rounds = cnt; if (rounds < ROUNDS_MIN) rounds = ROUNDS_MIN; if (rounds > ROUNDS_MAX) rounds = ROUNDS_MAX; /* add "rounds=NNNNN$" to result */ resptr += sprintf(resptr, str_rounds, rounds); } } salt_len = strchrnul(salt_data, '$') - salt_data; if (salt_len > SALT_LEN_MAX) salt_len = SALT_LEN_MAX; /* xstrdup assures suitable alignment; also we will use it as a scratch space later. */ salt_data = xstrndup(salt_data, salt_len); /* add "salt$" to result */ strcpy(resptr, salt_data); resptr += salt_len; *resptr++ = '$'; /* key data doesn't need much processing */ key_len = strlen(key_data); key_data = xstrdup(key_data); /* Which flavor of SHAnnn ops to use? */ sha_begin = (void*)sha256_begin; sha_hash = (void*)sha256_hash; sha_end = (void*)sha256_end; _32or64 = 32; if (is_sha512 == '6') { sha_begin = (void*)sha512_begin; sha_hash = (void*)sha512_hash; sha_end = (void*)sha512_end; _32or64 = 64; } /* Add KEY, SALT. */ sha_begin(&ctx); sha_hash(&ctx, key_data, key_len); sha_hash(&ctx, salt_data, salt_len); /* Compute alternate SHA sum with input KEY, SALT, and KEY. The final result will be added to the first context. */ sha_begin(&alt_ctx); sha_hash(&alt_ctx, key_data, key_len); sha_hash(&alt_ctx, salt_data, salt_len); sha_hash(&alt_ctx, key_data, key_len); sha_end(&alt_ctx, alt_result); /* Add result of this to the other context. */ /* Add for any character in the key one byte of the alternate sum. */ for (cnt = key_len; cnt > _32or64; cnt -= _32or64) sha_hash(&ctx, alt_result, _32or64); sha_hash(&ctx, alt_result, cnt); /* Take the binary representation of the length of the key and for every 1 add the alternate sum, for every 0 the key. */ for (cnt = key_len; cnt != 0; cnt >>= 1) if ((cnt & 1) != 0) sha_hash(&ctx, alt_result, _32or64); else sha_hash(&ctx, key_data, key_len); /* Create intermediate result. */ sha_end(&ctx, alt_result); /* Start computation of P byte sequence. */ /* For every character in the password add the entire password. */ sha_begin(&alt_ctx); for (cnt = 0; cnt < key_len; ++cnt) sha_hash(&alt_ctx, key_data, key_len); sha_end(&alt_ctx, temp_result); /* NB: past this point, raw key_data is not used anymore */ /* Create byte sequence P. */ #define p_bytes key_data /* reuse the buffer as it is of the key_len size */ cp = p_bytes; /* was: ... = alloca(key_len); */ for (cnt = key_len; cnt >= _32or64; cnt -= _32or64) { cp = memcpy(cp, temp_result, _32or64); cp += _32or64; } memcpy(cp, temp_result, cnt); /* Start computation of S byte sequence. */ /* For every character in the password add the entire password. */ sha_begin(&alt_ctx); for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) sha_hash(&alt_ctx, salt_data, salt_len); sha_end(&alt_ctx, temp_result); /* NB: past this point, raw salt_data is not used anymore */ /* Create byte sequence S. */ #define s_bytes salt_data /* reuse the buffer as it is of the salt_len size */ cp = s_bytes; /* was: ... = alloca(salt_len); */ for (cnt = salt_len; cnt >= _32or64; cnt -= _32or64) { cp = memcpy(cp, temp_result, _32or64); cp += _32or64; } memcpy(cp, temp_result, cnt); /* Repeatedly run the collected hash value through SHA to burn CPU cycles. */ for (cnt = 0; cnt < rounds; ++cnt) { sha_begin(&ctx); /* Add key or last result. */ if ((cnt & 1) != 0) sha_hash(&ctx, p_bytes, key_len); else sha_hash(&ctx, alt_result, _32or64); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) sha_hash(&ctx, s_bytes, salt_len); /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) sha_hash(&ctx, p_bytes, key_len); /* Add key or last result. */ if ((cnt & 1) != 0) sha_hash(&ctx, alt_result, _32or64); else sha_hash(&ctx, p_bytes, key_len); sha_end(&ctx, alt_result); } /* Append encrypted password to result buffer */ //TODO: replace with something like // bb_uuencode(cp, src, length, bb_uuenc_tbl_XXXbase64); #define b64_from_24bit(B2, B1, B0, N) \ do { \ unsigned w = ((B2) << 16) | ((B1) << 8) | (B0); \ resptr = to64(resptr, w, N); \ } while (0) if (is_sha512 == '5') { unsigned i = 0; while (1) { unsigned j = i + 10; unsigned k = i + 20; if (j >= 30) j -= 30; if (k >= 30) k -= 30; b64_from_24bit(alt_result[i], alt_result[j], alt_result[k], 4); if (k == 29) break; i = k + 1; } b64_from_24bit(0, alt_result[31], alt_result[30], 3); /* was: b64_from_24bit(alt_result[0], alt_result[10], alt_result[20], 4); b64_from_24bit(alt_result[21], alt_result[1], alt_result[11], 4); b64_from_24bit(alt_result[12], alt_result[22], alt_result[2], 4); b64_from_24bit(alt_result[3], alt_result[13], alt_result[23], 4); b64_from_24bit(alt_result[24], alt_result[4], alt_result[14], 4); b64_from_24bit(alt_result[15], alt_result[25], alt_result[5], 4); b64_from_24bit(alt_result[6], alt_result[16], alt_result[26], 4); b64_from_24bit(alt_result[27], alt_result[7], alt_result[17], 4); b64_from_24bit(alt_result[18], alt_result[28], alt_result[8], 4); b64_from_24bit(alt_result[9], alt_result[19], alt_result[29], 4); b64_from_24bit(0, alt_result[31], alt_result[30], 3); */ } else { unsigned i = 0; while (1) { unsigned j = i + 21; unsigned k = i + 42; if (j >= 63) j -= 63; if (k >= 63) k -= 63; b64_from_24bit(alt_result[i], alt_result[j], alt_result[k], 4); if (j == 20) break; i = j + 1; } b64_from_24bit(0, 0, alt_result[63], 2); /* was: b64_from_24bit(alt_result[0], alt_result[21], alt_result[42], 4); b64_from_24bit(alt_result[22], alt_result[43], alt_result[1], 4); b64_from_24bit(alt_result[44], alt_result[2], alt_result[23], 4); b64_from_24bit(alt_result[3], alt_result[24], alt_result[45], 4); b64_from_24bit(alt_result[25], alt_result[46], alt_result[4], 4); b64_from_24bit(alt_result[47], alt_result[5], alt_result[26], 4); b64_from_24bit(alt_result[6], alt_result[27], alt_result[48], 4); b64_from_24bit(alt_result[28], alt_result[49], alt_result[7], 4); b64_from_24bit(alt_result[50], alt_result[8], alt_result[29], 4); b64_from_24bit(alt_result[9], alt_result[30], alt_result[51], 4); b64_from_24bit(alt_result[31], alt_result[52], alt_result[10], 4); b64_from_24bit(alt_result[53], alt_result[11], alt_result[32], 4); b64_from_24bit(alt_result[12], alt_result[33], alt_result[54], 4); b64_from_24bit(alt_result[34], alt_result[55], alt_result[13], 4); b64_from_24bit(alt_result[56], alt_result[14], alt_result[35], 4); b64_from_24bit(alt_result[15], alt_result[36], alt_result[57], 4); b64_from_24bit(alt_result[37], alt_result[58], alt_result[16], 4); b64_from_24bit(alt_result[59], alt_result[17], alt_result[38], 4); b64_from_24bit(alt_result[18], alt_result[39], alt_result[60], 4); b64_from_24bit(alt_result[40], alt_result[61], alt_result[19], 4); b64_from_24bit(alt_result[62], alt_result[20], alt_result[41], 4); b64_from_24bit(0, 0, alt_result[63], 2); */ } /* *resptr = '\0'; - xzalloc did it */ #undef b64_from_24bit /* Clear the buffer for the intermediate result so that people attaching to processes or reading core dumps cannot get any information. */ memset(&L, 0, sizeof(L)); /* [alt]_ctx and XXX_result buffers */ memset(key_data, 0, key_len); /* also p_bytes */ memset(salt_data, 0, salt_len); /* also s_bytes */ free(key_data); free(salt_data); #undef p_bytes #undef s_bytes return result; #undef alt_result #undef temp_result #undef ctx #undef alt_ctx } busybox-1.22.1/libbb/get_shell_name.c0000644000000000000000000000065612263563520016165 0ustar rootroot/* * Copyright 2011, Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ //kbuild:lib-y += get_shell_name.o #include "libbb.h" const char* FAST_FUNC get_shell_name(void) { struct passwd *pw; char *shell; shell = getenv("SHELL"); if (shell && shell[0]) return shell; pw = getpwuid(getuid()); if (pw && pw->pw_shell && pw->pw_shell[0]) return pw->pw_shell; return DEFAULT_SHELL; } busybox-1.22.1/libbb/execable.c0000644000000000000000000000376412263563520014772 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 2006 Gabriel Somlo * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" /* check if path points to an executable file; * return 1 if found; * return 0 otherwise; */ int FAST_FUNC execable_file(const char *name) { struct stat s; return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode)); } /* search (*PATHp) for an executable file; * return allocated string containing full path if found; * PATHp points to the component after the one where it was found * (or NULL), * you may call find_execable again with this PATHp to continue * (if it's not NULL). * return NULL otherwise; (PATHp is undefined) * in all cases (*PATHp) contents will be trashed (s/:/NUL/). */ char* FAST_FUNC find_execable(const char *filename, char **PATHp) { char *p, *n; p = *PATHp; while (p) { n = strchr(p, ':'); if (n) *n++ = '\0'; if (*p != '\0') { /* it's not a PATH="foo::bar" situation */ p = concat_path_file(p, filename); if (execable_file(p)) { *PATHp = n; return p; } free(p); } p = n; } /* on loop exit p == NULL */ return p; } /* search $PATH for an executable file; * return 1 if found; * return 0 otherwise; */ int FAST_FUNC exists_execable(const char *filename) { char *path = xstrdup(getenv("PATH")); char *tmp = path; char *ret = find_execable(filename, &tmp); free(path); if (ret) { free(ret); return 1; } return 0; } #if ENABLE_FEATURE_PREFER_APPLETS /* just like the real execvp, but try to launch an applet named 'file' first */ int FAST_FUNC BB_EXECVP(const char *file, char *const argv[]) { if (find_applet_by_name(file) >= 0) execvp(bb_busybox_exec_path, argv); return execvp(file, argv); } #endif int FAST_FUNC BB_EXECVP_or_die(char **argv) { BB_EXECVP(argv[0], argv); /* SUSv3-mandated exit codes */ xfunc_error_retval = (errno == ENOENT) ? 127 : 126; bb_perror_msg_and_die("can't execute '%s'", argv[0]); } busybox-1.22.1/libbb/bb_do_delay.c0000644000000000000000000000057212263563520015437 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Busybox utility routines. * * Copyright (C) 2005 by Tito Ragusa * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" void FAST_FUNC bb_do_delay(int seconds) { time_t start, now; start = time(NULL); do { sleep(seconds); now = time(NULL); } while ((now - start) < seconds); } busybox-1.22.1/libbb/safe_poll.c0000644000000000000000000000153612263563520015161 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 2007 by Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" /* Wrapper which restarts poll on EINTR or ENOMEM. * On other errors does perror("poll") and returns. * Warning! May take longer than timeout_ms to return! */ int FAST_FUNC safe_poll(struct pollfd *ufds, nfds_t nfds, int timeout) { while (1) { int n = poll(ufds, nfds, timeout); if (n >= 0) return n; /* Make sure we inch towards completion */ if (timeout > 0) timeout--; /* E.g. strace causes poll to return this */ if (errno == EINTR) continue; /* Kernel is very low on memory. Retry. */ /* I doubt many callers would handle this correctly! */ if (errno == ENOMEM) continue; bb_perror_msg("poll"); return n; } } busybox-1.22.1/libbb/percent_decode.c0000644000000000000000000000257112263563520016160 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //kbuild:lib-y += percent_decode.o #include "libbb.h" static unsigned hex_to_bin(unsigned char c) { unsigned v; v = c - '0'; if (v <= 9) return v; /* c | 0x20: letters to lower case, non-letters * to (potentially different) non-letters */ v = (unsigned)(c | 0x20) - 'a'; if (v <= 5) return v + 10; return ~0; /* For testing: void t(char c) { printf("'%c'(%u) %u\n", c, c, hex_to_bin(c)); } int main() { t(0x10); t(0x20); t('0'); t('9'); t('A'); t('F'); t('a'); t('f'); t('0'-1); t('9'+1); t('A'-1); t('F'+1); t('a'-1); t('f'+1); return 0; } */ } char* FAST_FUNC percent_decode_in_place(char *str, int strict) { /* note that decoded string is always shorter than original */ char *src = str; char *dst = str; char c; while ((c = *src++) != '\0') { unsigned v; if (!strict && c == '+') { *dst++ = ' '; continue; } if (c != '%') { *dst++ = c; continue; } v = hex_to_bin(src[0]); if (v > 15) { bad_hex: if (strict) return NULL; *dst++ = '%'; continue; } v = (v * 16) | hex_to_bin(src[1]); if (v > 255) goto bad_hex; if (strict && (v == '/' || v == '\0')) { /* caller takes it as indication of invalid * (dangerous wrt exploits) chars */ return str + 1; } *dst++ = v; src += 2; } *dst = '\0'; return str; } busybox-1.22.1/libbb/platform.c0000644000000000000000000000631412263563520015040 0ustar rootroot/* * Replacements for common but usually nonstandard functions that aren't * supplied by all platforms. * * Copyright (C) 2009 by Dan Fandrich , et. al. * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" #ifndef HAVE_STRCHRNUL char* FAST_FUNC strchrnul(const char *s, int c) { while (*s != '\0' && *s != c) s++; return (char*)s; } #endif #ifndef HAVE_VASPRINTF int FAST_FUNC vasprintf(char **string_ptr, const char *format, va_list p) { int r; va_list p2; char buf[128]; va_copy(p2, p); r = vsnprintf(buf, 128, format, p); va_end(p); /* Note: can't use xstrdup/xmalloc, they call vasprintf (us) on failure! */ if (r < 128) { va_end(p2); *string_ptr = strdup(buf); return (*string_ptr ? r : -1); } *string_ptr = malloc(r+1); r = (*string_ptr ? vsnprintf(*string_ptr, r+1, format, p2) : -1); va_end(p2); return r; } #endif #ifndef HAVE_DPRINTF /* dprintf is now part of POSIX.1, but was only added in 2008 */ int dprintf(int fd, const char *format, ...) { va_list p; int r; char *string_ptr; va_start(p, format); r = vasprintf(&string_ptr, format, p); va_end(p); if (r >= 0) { r = full_write(fd, string_ptr, r); free(string_ptr); } return r; } #endif #ifndef HAVE_MEMRCHR /* Copyright (C) 2005 Free Software Foundation, Inc. * memrchr() is a GNU function that might not be available everywhere. * It's basically the inverse of memchr() - search backwards in a * memory block for a particular character. */ void* FAST_FUNC memrchr(const void *s, int c, size_t n) { const char *start = s, *end = s; end += n - 1; while (end >= start) { if (*end == (char)c) return (void *) end; end--; } return NULL; } #endif #ifndef HAVE_MKDTEMP /* This is now actually part of POSIX.1, but was only added in 2008 */ char* FAST_FUNC mkdtemp(char *template) { if (mktemp(template) == NULL || mkdir(template, 0700) != 0) return NULL; return template; } #endif #ifndef HAVE_STRCASESTR /* Copyright (c) 1999, 2000 The ht://Dig Group */ char* FAST_FUNC strcasestr(const char *s, const char *pattern) { int length = strlen(pattern); while (*s) { if (strncasecmp(s, pattern, length) == 0) return (char *)s; s++; } return 0; } #endif #ifndef HAVE_STRSEP /* Copyright (C) 2004 Free Software Foundation, Inc. */ char* FAST_FUNC strsep(char **stringp, const char *delim) { char *start = *stringp; char *ptr; if (!start) return NULL; if (!*delim) ptr = start + strlen(start); else { ptr = strpbrk(start, delim); if (!ptr) { *stringp = NULL; return start; } } *ptr = '\0'; *stringp = ptr + 1; return start; } #endif #ifndef HAVE_STPCPY char* FAST_FUNC stpcpy(char *p, const char *to_add) { while ((*p = *to_add) != '\0') { p++; to_add++; } return p; } #endif #ifndef HAVE_GETLINE ssize_t FAST_FUNC getline(char **lineptr, size_t *n, FILE *stream) { int ch; char *line = *lineptr; size_t alloced = *n; size_t len = 0; do { ch = fgetc(stream); if (ch == EOF) break; if (len + 1 >= alloced) { alloced += alloced/4 + 64; line = xrealloc(line, alloced); } line[len++] = ch; } while (ch != '\n'); if (len == 0) return -1; line[len] = '\0'; *lineptr = line; *n = alloced; return len; } #endif busybox-1.22.1/libbb/progress.c0000644000000000000000000001523712263563520015064 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Progress bar code. */ /* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff, * much of which was blatantly stolen from openssh. */ /*- * Copyright (c) 1992, 1993 * 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. BSD Advertising Clause omitted per the July 22, 1999 licensing change * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change * * 4. 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. */ #include "libbb.h" #include "unicode.h" enum { /* Seconds when xfer considered "stalled" */ STALLTIME = 5 }; static unsigned int get_tty2_width(void) { unsigned width; get_terminal_width_height(2, &width, NULL); return width; } void FAST_FUNC bb_progress_init(bb_progress_t *p, const char *curfile) { #if ENABLE_UNICODE_SUPPORT init_unicode(); p->curfile = unicode_conv_to_printable_fixedwidth(/*NULL,*/ curfile, 20); #else p->curfile = curfile; #endif p->start_sec = monotonic_sec(); p->last_update_sec = p->start_sec; p->last_change_sec = p->start_sec; p->last_size = 0; } /* File already had beg_size bytes. * Then we started downloading. * We downloaded "transferred" bytes so far. * Download is expected to stop when total size (beg_size + transferred) * will be "totalsize" bytes. * If totalsize == 0, then it is unknown. */ void FAST_FUNC bb_progress_update(bb_progress_t *p, uoff_t beg_size, uoff_t transferred, uoff_t totalsize) { uoff_t beg_and_transferred; unsigned since_last_update, elapsed; int barlength; int kiloscale; //transferred = 1234; /* use for stall detection testing */ //totalsize = 0; /* use for unknown size download testing */ elapsed = monotonic_sec(); since_last_update = elapsed - p->last_update_sec; p->last_update_sec = elapsed; if (totalsize != 0 && transferred >= totalsize - beg_size) { /* Last call. Do not skip this update */ transferred = totalsize - beg_size; /* sanitize just in case */ } else if (since_last_update == 0) { /* * Do not update on every call * (we can be called on every network read!) */ return; } kiloscale = 0; /* * Scale sizes down if they are close to overflowing. * This allows calculations like (100 * transferred / totalsize) * without risking overflow: we guarantee 10 highest bits to be 0. * Introduced error is less than 1 / 2^12 ~= 0.025% */ if (ULONG_MAX > 0xffffffff || sizeof(off_t) == 4 || sizeof(off_t) != 8) { /* * 64-bit CPU || small off_t: in either case, * >> is cheap, single-word operation. * ... || strange off_t: also use this code * (it is safe, just suboptimal wrt code size), * because 32/64 optimized one works only for 64-bit off_t. */ if (totalsize >= (1 << 22)) { totalsize >>= 10; beg_size >>= 10; transferred >>= 10; kiloscale = 1; } } else { /* 32-bit CPU and 64-bit off_t. * Use a 40-bit shift, it is easier to do on 32-bit CPU. */ /* ONE suppresses "warning: shift count >= width of type" */ #define ONE (sizeof(off_t) > 4) if (totalsize >= (uoff_t)(1ULL << 54*ONE)) { totalsize = (uint32_t)(totalsize >> 32*ONE) >> 8; beg_size = (uint32_t)(beg_size >> 32*ONE) >> 8; transferred = (uint32_t)(transferred >> 32*ONE) >> 8; kiloscale = 4; } } if (ENABLE_UNICODE_SUPPORT) fprintf(stderr, "\r%s", p->curfile); else fprintf(stderr, "\r%-20.20s", p->curfile); beg_and_transferred = beg_size + transferred; if (totalsize != 0) { unsigned ratio = 100 * beg_and_transferred / totalsize; fprintf(stderr, "%4u%%", ratio); barlength = get_tty2_width() - 49; if (barlength > 0) { /* god bless gcc for variable arrays :) */ char buf[barlength + 1]; unsigned stars = (unsigned)barlength * beg_and_transferred / totalsize; memset(buf, ' ', barlength); buf[barlength] = '\0'; memset(buf, '*', stars); fprintf(stderr, " |%s|", buf); } } while (beg_and_transferred >= 100000) { beg_and_transferred >>= 10; kiloscale++; } /* see http://en.wikipedia.org/wiki/Tera */ fprintf(stderr, "%6u%c", (unsigned)beg_and_transferred, " kMGTPEZY"[kiloscale]); #define beg_and_transferred dont_use_beg_and_transferred_below() since_last_update = elapsed - p->last_change_sec; if ((unsigned)transferred != p->last_size) { p->last_change_sec = elapsed; p->last_size = (unsigned)transferred; if (since_last_update >= STALLTIME) { /* We "cut out" these seconds from elapsed time * by adjusting start time */ p->start_sec += since_last_update; } since_last_update = 0; /* we are un-stalled now */ } elapsed -= p->start_sec; /* now it's "elapsed since start" */ if (since_last_update >= STALLTIME) { fprintf(stderr, " - stalled -"); } else if (!totalsize || !transferred || (int)elapsed < 0) { fprintf(stderr, " --:--:-- ETA"); } else { unsigned eta, secs, hours; totalsize -= beg_size; /* now it's "total to upload" */ /* Estimated remaining time = * estimated_sec_to_dl_totalsize_bytes - elapsed_sec = * totalsize / average_bytes_sec_so_far - elapsed = * totalsize / (transferred/elapsed) - elapsed = * totalsize * elapsed / transferred - elapsed */ eta = totalsize * elapsed / transferred - elapsed; if (eta >= 1000*60*60) eta = 1000*60*60 - 1; secs = eta % 3600; hours = eta / 3600; fprintf(stderr, "%3u:%02u:%02u ETA", hours, secs / 60, secs % 60); } } busybox-1.22.1/libbb/remove_file.c0000644000000000000000000000406112263563520015505 0ustar rootroot/* 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 source tree. */ #include "libbb.h" /* Used from NOFORK applets. Must not allocate anything */ int FAST_FUNC remove_file(const char *path, int flags) { struct stat path_stat; if (lstat(path, &path_stat) < 0) { if (errno != ENOENT) { bb_perror_msg("can't stat '%s'", path); return -1; } if (!(flags & FILEUTILS_FORCE)) { bb_perror_msg("can't remove '%s'", path); return -1; } return 0; } if (S_ISDIR(path_stat.st_mode)) { DIR *dp; struct dirent *d; int status = 0; if (!(flags & FILEUTILS_RECUR)) { bb_error_msg("'%s' is a directory", path); return -1; } if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 && isatty(0)) || (flags & FILEUTILS_INTERACTIVE) ) { fprintf(stderr, "%s: descend into directory '%s'? ", applet_name, path); if (!bb_ask_confirmation()) return 0; } dp = opendir(path); if (dp == NULL) { return -1; } while ((d = readdir(dp)) != NULL) { char *new_path; new_path = concat_subpath_file(path, d->d_name); if (new_path == NULL) continue; if (remove_file(new_path, flags) < 0) status = -1; free(new_path); } if (closedir(dp) < 0) { bb_perror_msg("can't 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("can't remove '%s'", path); return -1; } return status; } /* !ISDIR */ if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 && !S_ISLNK(path_stat.st_mode) && isatty(0)) || (flags & FILEUTILS_INTERACTIVE) ) { fprintf(stderr, "%s: remove '%s'? ", applet_name, path); if (!bb_ask_confirmation()) return 0; } if (unlink(path) < 0) { bb_perror_msg("can't remove '%s'", path); return -1; } return 0; } busybox-1.22.1/libbb/update_passwd.c0000644000000000000000000001725712263563520016067 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * update_passwd * * update_passwd is a common function for passwd and chpasswd applets; * it is responsible for updating password file (i.e. /etc/passwd or * /etc/shadow) for a given user and password. * * Moved from loginutils/passwd.c by Alexander Shishkin * * Modified to be able to add or delete users, groups and users to/from groups * by Tito Ragusa * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" #if ENABLE_SELINUX static void check_selinux_update_passwd(const char *username) { security_context_t context; char *seuser; if (getuid() != (uid_t)0 || is_selinux_enabled() == 0) return; /* No need to check */ if (getprevcon_raw(&context) < 0) bb_perror_msg_and_die("getprevcon failed"); seuser = strtok(context, ":"); if (!seuser) bb_error_msg_and_die("invalid context '%s'", context); if (strcmp(seuser, username) != 0) { if (checkPasswdAccess(PASSWD__PASSWD) != 0) bb_error_msg_and_die("SELinux: access denied"); } if (ENABLE_FEATURE_CLEAN_UP) freecon(context); } #else # define check_selinux_update_passwd(username) ((void)0) #endif /* 1) add a user: update_passwd(FILE, USER, REMAINING_PWLINE, NULL) only if CONFIG_ADDUSER=y and applet_name[0] == 'a' like in adduser 2) add a group: update_passwd(FILE, GROUP, REMAINING_GRLINE, NULL) only if CONFIG_ADDGROUP=y and applet_name[0] == 'a' like in addgroup 3) add a user to a group: update_passwd(FILE, GROUP, NULL, MEMBER) only if CONFIG_FEATURE_ADDUSER_TO_GROUP=y, applet_name[0] == 'a' like in addgroup and member != NULL 4) delete a user: update_passwd(FILE, USER, NULL, NULL) 5) delete a group: update_passwd(FILE, GROUP, NULL, NULL) 6) delete a user from a group: update_passwd(FILE, GROUP, NULL, MEMBER) only if CONFIG_FEATURE_DEL_USER_FROM_GROUP=y and member != NULL 7) change user's password: update_passwd(FILE, USER, NEW_PASSWD, NULL) only if CONFIG_PASSWD=y and applet_name[0] == 'p' like in passwd or if CONFIG_CHPASSWD=y and applet_name[0] == 'c' like in chpasswd This function does not validate the arguments fed to it so the calling program should take care of that. Returns number of lines changed, or -1 on error. */ int FAST_FUNC update_passwd(const char *filename, const char *name, const char *new_passwd, const char *member) { #if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP) #define member NULL #endif struct stat sb; struct flock lock; FILE *old_fp; FILE *new_fp; char *fnamesfx; char *sfx_char; char *name_colon; unsigned user_len; int old_fd; int new_fd; int i; int changed_lines; int ret = -1; /* failure */ /* used as a bool: "are we modifying /etc/shadow?" */ #if ENABLE_FEATURE_SHADOWPASSWDS const char *shadow = strstr(filename, "shadow"); #else # define shadow NULL #endif filename = xmalloc_follow_symlinks(filename); if (filename == NULL) return ret; check_selinux_update_passwd(name); /* New passwd file, "/etc/passwd+" for now */ fnamesfx = xasprintf("%s+", filename); sfx_char = &fnamesfx[strlen(fnamesfx)-1]; name_colon = xasprintf("%s:", name); user_len = strlen(name_colon); if (shadow) old_fp = fopen(filename, "r+"); else old_fp = fopen_or_warn(filename, "r+"); if (!old_fp) { if (shadow) ret = 0; /* missing shadow is not an error */ goto free_mem; } old_fd = fileno(old_fp); selinux_preserve_fcontext(old_fd); /* Try to create "/etc/passwd+". Wait if it exists. */ i = 30; do { // FIXME: on last iteration try w/o O_EXCL but with O_TRUNC? new_fd = open(fnamesfx, O_WRONLY|O_CREAT|O_EXCL, 0600); if (new_fd >= 0) goto created; if (errno != EEXIST) break; usleep(100000); /* 0.1 sec */ } while (--i); bb_perror_msg("can't create '%s'", fnamesfx); goto close_old_fp; created: if (fstat(old_fd, &sb) == 0) { fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */ fchown(new_fd, sb.st_uid, sb.st_gid); } errno = 0; new_fp = xfdopen_for_write(new_fd); /* Backup file is "/etc/passwd-" */ *sfx_char = '-'; /* Delete old backup */ i = (unlink(fnamesfx) && errno != ENOENT); /* Create backup as a hardlink to current */ if (i || link(filename, fnamesfx)) bb_perror_msg("warning: can't create backup copy '%s'", fnamesfx); *sfx_char = '+'; /* Lock the password file before updating */ lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(old_fd, F_SETLK, &lock) < 0) bb_perror_msg("warning: can't lock '%s'", filename); lock.l_type = F_UNLCK; /* Read current password file, write updated /etc/passwd+ */ changed_lines = 0; while (1) { char *cp, *line; line = xmalloc_fgetline(old_fp); if (!line) /* EOF/error */ break; if (strncmp(name_colon, line, user_len) != 0) { fprintf(new_fp, "%s\n", line); goto next; } /* We have a match with "name:"... */ cp = line + user_len; /* move past name: */ #if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP if (member) { /* It's actually /etc/group+, not /etc/passwd+ */ if (ENABLE_FEATURE_ADDUSER_TO_GROUP && applet_name[0] == 'a' ) { /* Add user to group */ fprintf(new_fp, "%s%s%s\n", line, last_char_is(line, ':') ? "" : ",", member); changed_lines++; } else if (ENABLE_FEATURE_DEL_USER_FROM_GROUP /* && applet_name[0] == 'd' */ ) { /* Delete user from group */ char *tmp; const char *fmt = "%s"; /* find the start of the member list: last ':' */ cp = strrchr(line, ':'); /* cut it */ *cp++ = '\0'; /* write the cut line name:passwd:gid: * or name:!:: */ fprintf(new_fp, "%s:", line); /* parse the tokens of the member list */ tmp = cp; while ((cp = strsep(&tmp, ",")) != NULL) { if (strcmp(member, cp) != 0) { fprintf(new_fp, fmt, cp); fmt = ",%s"; } else { /* found member, skip it */ changed_lines++; } } fprintf(new_fp, "\n"); } } else #endif if ((ENABLE_PASSWD && applet_name[0] == 'p') || (ENABLE_CHPASSWD && applet_name[0] == 'c') ) { /* Change passwd */ cp = strchrnul(cp, ':'); /* move past old passwd */ if (shadow && *cp == ':') { /* /etc/shadow's field 3 (passwd change date) needs updating */ /* move past old change date */ cp = strchrnul(cp + 1, ':'); /* "name:" + "new_passwd" + ":" + "change date" + ":rest of line" */ fprintf(new_fp, "%s%s:%u%s\n", name_colon, new_passwd, (unsigned)(time(NULL)) / (24*60*60), cp); } else { /* "name:" + "new_passwd" + ":rest of line" */ fprintf(new_fp, "%s%s%s\n", name_colon, new_passwd, cp); } changed_lines++; } /* else delete user or group: skip the line */ next: free(line); } if (changed_lines == 0) { #if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP if (member) { if (ENABLE_ADDGROUP && applet_name[0] == 'a') bb_error_msg("can't find %s in %s", name, filename); if (ENABLE_DELGROUP && applet_name[0] == 'd') bb_error_msg("can't find %s in %s", member, filename); } #endif if ((ENABLE_ADDUSER || ENABLE_ADDGROUP) && applet_name[0] == 'a' && !member ) { /* add user or group */ fprintf(new_fp, "%s%s\n", name_colon, new_passwd); changed_lines++; } } fcntl(old_fd, F_SETLK, &lock); /* We do want all of them to execute, thus | instead of || */ errno = 0; if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp)) || rename(fnamesfx, filename) ) { /* At least one of those failed */ bb_perror_nomsg(); goto unlink_new; } /* Success: ret >= 0 */ ret = changed_lines; unlink_new: if (ret < 0) unlink(fnamesfx); close_old_fp: fclose(old_fp); free_mem: free(fnamesfx); free((char *)filename); free(name_colon); return ret; } busybox-1.22.1/libbb/selinux_common.c0000644000000000000000000000244112263563520016250 0ustar rootroot/* * libbb/selinux_common.c * -- common SELinux utility functions * * Copyright 2007 KaiGai Kohei * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" #include context_t FAST_FUNC set_security_context_component(security_context_t cur_context, char *user, char *role, char *type, char *range) { context_t con = context_new(cur_context); if (!con) return NULL; if (user && context_user_set(con, user)) goto error; if (type && context_type_set(con, type)) goto error; if (range && context_range_set(con, range)) goto error; if (role && context_role_set(con, role)) goto error; return con; error: context_free(con); return NULL; } void FAST_FUNC setfscreatecon_or_die(security_context_t scontext) { if (setfscreatecon(scontext) < 0) { /* Can be NULL. All known printf implementations * display "(null)", "" etc */ bb_perror_msg_and_die("can't set default " "file creation context to %s", scontext); } } void FAST_FUNC selinux_preserve_fcontext(int fdesc) { security_context_t context; if (fgetfilecon(fdesc, &context) < 0) { if (errno == ENODATA || errno == ENOTSUP) return; bb_perror_msg_and_die("fgetfilecon failed"); } setfscreatecon_or_die(context); freecon(context); } busybox-1.22.1/libbb/xatonum.c0000644000000000000000000000342312263563520014705 0ustar rootroot/* 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 source tree. */ #include "libbb.h" #define type long long #define xstrtou(rest) xstrtoull##rest #define xstrto(rest) xstrtoll##rest #define xatou(rest) xatoull##rest #define xato(rest) xatoll##rest #define XSTR_UTYPE_MAX ULLONG_MAX #define XSTR_TYPE_MAX LLONG_MAX #define XSTR_TYPE_MIN LLONG_MIN #define XSTR_STRTOU strtoull #include "xatonum_template.c" #if ULONG_MAX != ULLONG_MAX #define type long #define xstrtou(rest) xstrtoul##rest #define xstrto(rest) xstrtol##rest #define xatou(rest) xatoul##rest #define xato(rest) xatol##rest #define XSTR_UTYPE_MAX ULONG_MAX #define XSTR_TYPE_MAX LONG_MAX #define XSTR_TYPE_MIN LONG_MIN #define XSTR_STRTOU strtoul #include "xatonum_template.c" #endif #if UINT_MAX != ULONG_MAX static ALWAYS_INLINE unsigned bb_strtoui(const char *str, char **end, int b) { unsigned long v = strtoul(str, end, b); if (v > UINT_MAX) { errno = ERANGE; return UINT_MAX; } return v; } #define type int #define xstrtou(rest) xstrtou##rest #define xstrto(rest) xstrtoi##rest #define xatou(rest) xatou##rest #define xato(rest) xatoi##rest #define XSTR_UTYPE_MAX UINT_MAX #define XSTR_TYPE_MAX INT_MAX #define XSTR_TYPE_MIN INT_MIN /* libc has no strtoui, so we need to create/use our own */ #define XSTR_STRTOU bb_strtoui #include "xatonum_template.c" #endif /* A few special cases */ int FAST_FUNC xatoi_positive(const char *numstr) { return xatou_range(numstr, 0, INT_MAX); } uint16_t FAST_FUNC xatou16(const char *numstr) { return xatou_range(numstr, 0, 0xffff); } const struct suffix_mult bkm_suffixes[] = { { "b", 512 }, { "k", 1024 }, { "m", 1024*1024 }, { "", 0 } }; busybox-1.22.1/libbb/get_volsize.c0000644000000000000000000000220712263563520015543 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 2010 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" uoff_t FAST_FUNC get_volume_size_in_bytes(int fd, const char *override, unsigned override_units, int extend) { uoff_t result; if (override) { result = XATOOFF(override); if (result >= (uoff_t)(MAXINT(off_t)) / override_units) bb_error_msg_and_die("image size is too big"); result *= override_units; /* seek past end fails on block devices but works on files */ if (lseek(fd, result - 1, SEEK_SET) != (off_t)-1) { if (extend) xwrite(fd, "", 1); /* file grows if needed */ } //else { // bb_error_msg("warning, block device is smaller"); //} } else { /* more portable than BLKGETSIZE[64] */ result = xlseek(fd, 0, SEEK_END); } xlseek(fd, 0, SEEK_SET); /* Prevent things like this: * $ dd if=/dev/zero of=foo count=1 bs=1024 * $ mkswap foo * Setting up swapspace version 1, size = 18446744073709548544 bytes * * Picked 16k arbitrarily: */ if (result < 16*1024) bb_error_msg_and_die("image is too small"); return result; } busybox-1.22.1/libbb/inode_hash.c0000644000000000000000000000405512263563520015315 0ustar rootroot/* 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 source tree. */ #include "libbb.h" typedef struct ino_dev_hash_bucket_struct { struct ino_dev_hash_bucket_struct *next; ino_t ino; dev_t dev; char name[1]; } ino_dev_hashtable_bucket_t; #define HASH_SIZE 311 /* Should be prime */ #define hash_inode(i) ((i) % HASH_SIZE) /* array of [HASH_SIZE] elements */ static ino_dev_hashtable_bucket_t **ino_dev_hashtable; /* * Return name if statbuf->st_ino && statbuf->st_dev are recorded in * ino_dev_hashtable, else return NULL */ char* FAST_FUNC is_in_ino_dev_hashtable(const struct stat *statbuf) { ino_dev_hashtable_bucket_t *bucket; if (!ino_dev_hashtable) return NULL; bucket = ino_dev_hashtable[hash_inode(statbuf->st_ino)]; while (bucket != NULL) { if ((bucket->ino == statbuf->st_ino) && (bucket->dev == statbuf->st_dev) ) { return bucket->name; } bucket = bucket->next; } return NULL; } /* Add statbuf to statbuf hash table */ void FAST_FUNC add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) { int i; ino_dev_hashtable_bucket_t *bucket; i = hash_inode(statbuf->st_ino); if (!name) name = ""; bucket = xmalloc(sizeof(ino_dev_hashtable_bucket_t) + strlen(name)); bucket->ino = statbuf->st_ino; bucket->dev = statbuf->st_dev; strcpy(bucket->name, name); if (!ino_dev_hashtable) ino_dev_hashtable = xzalloc(HASH_SIZE * sizeof(*ino_dev_hashtable)); bucket->next = ino_dev_hashtable[i]; ino_dev_hashtable[i] = bucket; } #if ENABLE_DU || ENABLE_FEATURE_CLEAN_UP /* Clear statbuf hash table */ void FAST_FUNC reset_ino_dev_hashtable(void) { int i; ino_dev_hashtable_bucket_t *bucket; for (i = 0; ino_dev_hashtable && i < HASH_SIZE; i++) { while (ino_dev_hashtable[i] != NULL) { bucket = ino_dev_hashtable[i]->next; free(ino_dev_hashtable[i]); ino_dev_hashtable[i] = bucket; } } free(ino_dev_hashtable); ino_dev_hashtable = NULL; } #endif busybox-1.22.1/libbb/pw_encrypt.c0000644000000000000000000000637112263563520015411 0ustar rootroot/* 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 source tree. */ #include "libbb.h" /* static const uint8_t ascii64[] = * "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; */ static int i64c(int i) { i &= 0x3f; if (i == 0) return '.'; if (i == 1) return '/'; if (i < 12) return ('0' - 2 + i); if (i < 38) return ('A' - 12 + i); return ('a' - 38 + i); } int FAST_FUNC crypt_make_salt(char *p, int cnt /*, int x */) { /* was: x += ... */ int x = getpid() + monotonic_us(); do { /* x = (x*1664525 + 1013904223) % 2^32 generator is lame * (low-order bit is not "random", etc...), * but for our purposes it is good enough */ x = x*1664525 + 1013904223; /* BTW, Park and Miller's "minimal standard generator" is * x = x*16807 % ((2^31)-1) * It has no problem with visibly alternating lowest bit * but is also weak in cryptographic sense + needs div, * which needs more code (and slower) on many CPUs */ *p++ = i64c(x >> 16); *p++ = i64c(x >> 22); } while (--cnt); *p = '\0'; return x; } char* FAST_FUNC crypt_make_pw_salt(char salt[MAX_PW_SALT_LEN], const char *algo) { int len = 2/2; char *salt_ptr = salt; if (algo[0] != 'd') { /* not des */ len = 8/2; /* so far assuming md5 */ *salt_ptr++ = '$'; *salt_ptr++ = '1'; *salt_ptr++ = '$'; #if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA if (algo[0] == 's') { /* sha */ salt[1] = '5' + (strcmp(algo, "sha512") == 0); len = 16/2; } #endif } crypt_make_salt(salt_ptr, len); return salt_ptr; } #if ENABLE_USE_BB_CRYPT static char* to64(char *s, unsigned v, int n) { while (--n >= 0) { /* *s++ = ascii64[v & 0x3f]; */ *s++ = i64c(v); v >>= 6; } return s; } /* * DES and MD5 crypt implementations are taken from uclibc. * They were modified to not use static buffers. */ #include "pw_encrypt_des.c" #include "pw_encrypt_md5.c" #if ENABLE_USE_BB_CRYPT_SHA #include "pw_encrypt_sha.c" #endif /* Other advanced crypt ids (TODO?): */ /* $2$ or $2a$: Blowfish */ static struct const_des_ctx *des_cctx; static struct des_ctx *des_ctx; /* my_crypt returns malloc'ed data */ static char *my_crypt(const char *key, const char *salt) { /* MD5 or SHA? */ if (salt[0] == '$' && salt[1] && salt[2] == '$') { if (salt[1] == '1') return md5_crypt(xzalloc(MD5_OUT_BUFSIZE), (unsigned char*)key, (unsigned char*)salt); #if ENABLE_USE_BB_CRYPT_SHA if (salt[1] == '5' || salt[1] == '6') return sha_crypt((char*)key, (char*)salt); #endif } if (!des_cctx) des_cctx = const_des_init(); des_ctx = des_init(des_ctx, des_cctx); return des_crypt(des_ctx, xzalloc(DES_OUT_BUFSIZE), (unsigned char*)key, (unsigned char*)salt); } /* So far nobody wants to have it public */ static void my_crypt_cleanup(void) { free(des_cctx); free(des_ctx); des_cctx = NULL; des_ctx = NULL; } char* FAST_FUNC pw_encrypt(const char *clear, const char *salt, int cleanup) { char *encrypted; encrypted = my_crypt(clear, salt); if (cleanup) my_crypt_cleanup(); return encrypted; } #else /* if !ENABLE_USE_BB_CRYPT */ char* FAST_FUNC pw_encrypt(const char *clear, const char *salt, int cleanup) { return xstrdup(crypt(clear, salt)); } #endif busybox-1.22.1/libbb/xatonum_template.c0000644000000000000000000001167712263563520016612 0ustar rootroot/* * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* You need to define the following (example): #define type long #define xstrtou(rest) xstrtoul##rest #define xstrto(rest) xstrtol##rest #define xatou(rest) xatoul##rest #define xato(rest) xatol##rest #define XSTR_UTYPE_MAX ULONG_MAX #define XSTR_TYPE_MAX LONG_MAX #define XSTR_TYPE_MIN LONG_MIN #define XSTR_STRTOU strtoul */ unsigned type FAST_FUNC xstrtou(_range_sfx)(const char *numstr, int base, unsigned type lower, unsigned type upper, const struct suffix_mult *suffixes) { unsigned type r; int old_errno; char *e; /* Disallow '-' and any leading whitespace. */ if (*numstr == '-' || *numstr == '+' || isspace(*numstr)) goto inval; /* Since this is a lib function, we're not allowed to reset errno to 0. * Doing so could break an app that is deferring checking of errno. * So, save the old value so that we can restore it if successful. */ old_errno = errno; errno = 0; r = XSTR_STRTOU(numstr, &e, base); /* Do the initial validity check. Note: The standards do not * guarantee that errno is set if no digits were found. So we * must test for this explicitly. */ if (errno || numstr == e) goto inval; /* error / no digits / illegal trailing chars */ errno = old_errno; /* Ok. So restore errno. */ /* Do optional suffix parsing. Allow 'empty' suffix tables. * Note that we also allow nul suffixes with associated multipliers, * to allow for scaling of the numstr by some default multiplier. */ if (suffixes) { while (suffixes->mult) { if (strcmp(suffixes->suffix, e) == 0) { if (XSTR_UTYPE_MAX / suffixes->mult < r) goto range; /* overflow! */ r *= suffixes->mult; goto chk_range; } ++suffixes; } } /* Note: trailing space is an error. * It would be easy enough to allow though if desired. */ if (*e) goto inval; chk_range: /* Finally, check for range limits. */ if (r >= lower && r <= upper) return r; range: bb_error_msg_and_die("number %s is not in %llu..%llu range", numstr, (unsigned long long)lower, (unsigned long long)upper); inval: bb_error_msg_and_die("invalid number '%s'", numstr); } unsigned type FAST_FUNC xstrtou(_range)(const char *numstr, int base, unsigned type lower, unsigned type upper) { return xstrtou(_range_sfx)(numstr, base, lower, upper, NULL); } unsigned type FAST_FUNC xstrtou(_sfx)(const char *numstr, int base, const struct suffix_mult *suffixes) { return xstrtou(_range_sfx)(numstr, base, 0, XSTR_UTYPE_MAX, suffixes); } unsigned type FAST_FUNC xstrtou()(const char *numstr, int base) { return xstrtou(_range_sfx)(numstr, base, 0, XSTR_UTYPE_MAX, NULL); } unsigned type FAST_FUNC xatou(_range_sfx)(const char *numstr, unsigned type lower, unsigned type upper, const struct suffix_mult *suffixes) { return xstrtou(_range_sfx)(numstr, 10, lower, upper, suffixes); } unsigned type FAST_FUNC xatou(_range)(const char *numstr, unsigned type lower, unsigned type upper) { return xstrtou(_range_sfx)(numstr, 10, lower, upper, NULL); } unsigned type FAST_FUNC xatou(_sfx)(const char *numstr, const struct suffix_mult *suffixes) { return xstrtou(_range_sfx)(numstr, 10, 0, XSTR_UTYPE_MAX, suffixes); } unsigned type FAST_FUNC xatou()(const char *numstr) { return xatou(_sfx)(numstr, NULL); } /* Signed ones */ type FAST_FUNC xstrto(_range_sfx)(const char *numstr, int base, type lower, type upper, const struct suffix_mult *suffixes) { unsigned type u = XSTR_TYPE_MAX; type r; const char *p = numstr; /* NB: if you'll decide to disallow '+': * at least renice applet needs to allow it */ if (p[0] == '+' || p[0] == '-') { ++p; if (p[0] == '-') ++u; /* = _MIN (01111... + 1 == 10000...) */ } r = xstrtou(_range_sfx)(p, base, 0, u, suffixes); if (*numstr == '-') { r = -r; } if (r < lower || r > upper) { bb_error_msg_and_die("number %s is not in %lld..%lld range", numstr, (long long)lower, (long long)upper); } return r; } type FAST_FUNC xstrto(_range)(const char *numstr, int base, type lower, type upper) { return xstrto(_range_sfx)(numstr, base, lower, upper, NULL); } type FAST_FUNC xstrto()(const char *numstr, int base) { return xstrto(_range_sfx)(numstr, base, XSTR_TYPE_MIN, XSTR_TYPE_MAX, NULL); } type FAST_FUNC xato(_range_sfx)(const char *numstr, type lower, type upper, const struct suffix_mult *suffixes) { return xstrto(_range_sfx)(numstr, 10, lower, upper, suffixes); } type FAST_FUNC xato(_range)(const char *numstr, type lower, type upper) { return xstrto(_range_sfx)(numstr, 10, lower, upper, NULL); } type FAST_FUNC xato(_sfx)(const char *numstr, const struct suffix_mult *suffixes) { return xstrto(_range_sfx)(numstr, 10, XSTR_TYPE_MIN, XSTR_TYPE_MAX, suffixes); } type FAST_FUNC xato()(const char *numstr) { return xstrto(_range_sfx)(numstr, 10, XSTR_TYPE_MIN, XSTR_TYPE_MAX, NULL); } #undef type #undef xstrtou #undef xstrto #undef xatou #undef xato #undef XSTR_UTYPE_MAX #undef XSTR_TYPE_MAX #undef XSTR_TYPE_MIN #undef XSTR_STRTOU busybox-1.22.1/libbb/recursive_action.c0000644000000000000000000001074112263563520016557 0ustar rootroot/* 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 source tree. */ #include "libbb.h" #undef DEBUG_RECURS_ACTION /* * Walk down all the directories under the specified * location, and do something (something specified * by the fileAction and dirAction function pointers). * * Unfortunately, while nftw(3) could replace this and reduce * code size a bit, nftw() wasn't supported before GNU libc 2.1, * and so isn't sufficiently portable to take over since glibc2.1 * is so stinking huge. */ static int FAST_FUNC true_action(const char *fileName UNUSED_PARAM, struct stat *statbuf UNUSED_PARAM, void* userData UNUSED_PARAM, int depth UNUSED_PARAM) { return TRUE; } /* fileAction return value of 0 on any file in directory will make * recursive_action() return 0, but it doesn't stop directory traversal * (fileAction/dirAction will be called on each file). * * If !ACTION_RECURSE, dirAction is called on the directory and its * return value is returned from recursive_action(). No recursion. * * If ACTION_RECURSE, recursive_action() is called on each directory. * If any one of these calls returns 0, current recursive_action() returns 0. * * If ACTION_DEPTHFIRST, dirAction is called after recurse. * If it returns 0, the warning is printed and recursive_action() returns 0. * * If !ACTION_DEPTHFIRST, dirAction is called before we recurse. * Return value of 0 (FALSE) or 2 (SKIP) prevents recursion * into that directory, instead recursive_action() returns 0 (if FALSE) * or 1 (if SKIP) * * ACTION_FOLLOWLINKS mainly controls handling of links to dirs. * 0: lstat(statbuf). Calls fileAction on link name even if points to dir. * 1: stat(statbuf). Calls dirAction and optionally recurse on link to dir. */ int FAST_FUNC recursive_action(const char *fileName, unsigned flags, int FAST_FUNC (*fileAction)(const char *fileName, struct stat *statbuf, void* userData, int depth), int FAST_FUNC (*dirAction)(const char *fileName, struct stat *statbuf, void* userData, int depth), void* userData, unsigned depth) { struct stat statbuf; unsigned follow; int status; DIR *dir; struct dirent *next; if (!fileAction) fileAction = true_action; if (!dirAction) dirAction = true_action; follow = ACTION_FOLLOWLINKS; if (depth == 0) follow = ACTION_FOLLOWLINKS | ACTION_FOLLOWLINKS_L0; follow &= flags; status = (follow ? stat : lstat)(fileName, &statbuf); if (status < 0) { #ifdef DEBUG_RECURS_ACTION bb_error_msg("status=%d flags=%x", status, flags); #endif if ((flags & ACTION_DANGLING_OK) && errno == ENOENT && lstat(fileName, &statbuf) == 0 ) { /* Dangling link */ return fileAction(fileName, &statbuf, userData, depth); } goto done_nak_warn; } /* If S_ISLNK(m), then we know that !S_ISDIR(m). * Then we can skip checking first part: if it is true, then * (!dir) is also true! */ if ( /* (!(flags & ACTION_FOLLOWLINKS) && S_ISLNK(statbuf.st_mode)) || */ !S_ISDIR(statbuf.st_mode) ) { return fileAction(fileName, &statbuf, userData, depth); } /* It's a directory (or a link to one, and followLinks is set) */ if (!(flags & ACTION_RECURSE)) { return dirAction(fileName, &statbuf, userData, depth); } if (!(flags & ACTION_DEPTHFIRST)) { status = dirAction(fileName, &statbuf, userData, depth); if (!status) goto done_nak_warn; if (status == SKIP) return TRUE; } dir = opendir(fileName); if (!dir) { /* findutils-4.1.20 reports this */ /* (i.e. it doesn't silently return with exit code 1) */ /* To trigger: "find -exec rm -rf {} \;" */ goto done_nak_warn; } status = TRUE; while ((next = readdir(dir)) != NULL) { char *nextFile; nextFile = concat_subpath_file(fileName, next->d_name); if (nextFile == NULL) continue; /* process every file (NB: ACTION_RECURSE is set in flags) */ if (!recursive_action(nextFile, flags, fileAction, dirAction, userData, depth + 1)) status = FALSE; // s = recursive_action(nextFile, flags, fileAction, dirAction, // userData, depth + 1); free(nextFile); //#define RECURSE_RESULT_ABORT 3 // if (s == RECURSE_RESULT_ABORT) { // closedir(dir); // return s; // } // if (s == FALSE) // status = FALSE; } closedir(dir); if (flags & ACTION_DEPTHFIRST) { if (!dirAction(fileName, &statbuf, userData, depth)) goto done_nak_warn; } return status; done_nak_warn: if (!(flags & ACTION_QUIET)) bb_simple_perror_msg(fileName); return FALSE; } busybox-1.22.1/libbb/utmp.c0000644000000000000000000000706512263563520014205 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * utmp/wtmp support routines. * * Copyright (C) 2010 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" static void touch(const char *filename) { if (access(filename, R_OK | W_OK) == -1) close(open(filename, O_WRONLY | O_CREAT, 0664)); } void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname) { struct utmp utent; char *id; unsigned width; memset(&utent, 0, sizeof(utent)); utent.ut_pid = pid; utent.ut_type = new_type; tty_name = skip_dev_pfx(tty_name); safe_strncpy(utent.ut_line, tty_name, sizeof(utent.ut_line)); if (username) safe_strncpy(utent.ut_user, username, sizeof(utent.ut_user)); if (hostname) safe_strncpy(utent.ut_host, hostname, sizeof(utent.ut_host)); utent.ut_tv.tv_sec = time(NULL); /* Invent our own ut_id. ut_id is only 4 chars wide. * Try to fit something remotely meaningful... */ id = utent.ut_id; width = sizeof(utent.ut_id); if (tty_name[0] == 'p') { /* if "ptyXXX", map to "pXXX" */ /* if "pts/XX", map to "p/XX" */ *id++ = 'p'; width--; } /* else: usually it's "ttyXXXX", map to "XXXX" */ if (strlen(tty_name) > 3) tty_name += 3; strncpy(id, tty_name, width); touch(_PATH_UTMP); //utmpname(_PATH_UTMP); setutent(); /* Append new one (hopefully, unless we collide on ut_id) */ pututline(&utent); endutent(); #if ENABLE_FEATURE_WTMP /* "man utmp" says wtmp file should *not* be created automagically */ /*touch(bb_path_wtmp_file);*/ updwtmp(bb_path_wtmp_file, &utent); #endif } /* * Read "man utmp" to make sense out of it. */ void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname) { struct utmp utent; struct utmp *utp; touch(_PATH_UTMP); //utmpname(_PATH_UTMP); setutent(); /* Did init/getty/telnetd/sshd/... create an entry for us? * It should be (new_type-1), but we'd also reuse * any other potentially stale xxx_PROCESS entry */ while ((utp = getutent()) != NULL) { if (utp->ut_pid == pid // && ut->ut_line[0] && utp->ut_id[0] /* must have nonzero id */ && ( utp->ut_type == INIT_PROCESS || utp->ut_type == LOGIN_PROCESS || utp->ut_type == USER_PROCESS || utp->ut_type == DEAD_PROCESS ) ) { if (utp->ut_type >= new_type) { /* Stale record. Nuke hostname */ memset(utp->ut_host, 0, sizeof(utp->ut_host)); } /* NB: pututline (see later) searches for matching utent * using getutid(utent) - we must not change ut_id * if we want *exactly this* record to be overwritten! */ break; } } //endutent(); - no need, pututline can deal with (and actually likes) //the situation when utmp file is positioned on found record if (!utp) { if (new_type != DEAD_PROCESS) write_new_utmp(pid, new_type, tty_name, username, hostname); else endutent(); return; } /* Make a copy. We can't use *utp, pututline's internal getutid * will overwrite it before it is used! */ utent = *utp; utent.ut_type = new_type; if (tty_name) safe_strncpy(utent.ut_line, skip_dev_pfx(tty_name), sizeof(utent.ut_line)); if (username) safe_strncpy(utent.ut_user, username, sizeof(utent.ut_user)); if (hostname) safe_strncpy(utent.ut_host, hostname, sizeof(utent.ut_host)); utent.ut_tv.tv_sec = time(NULL); /* Update, or append new one */ //setutent(); pututline(&utent); endutent(); #if ENABLE_FEATURE_WTMP /* "man utmp" says wtmp file should *not* be created automagically */ /*touch(bb_path_wtmp_file);*/ updwtmp(bb_path_wtmp_file, &utent); #endif } busybox-1.22.1/libbb/read.c0000644000000000000000000000254012263563520014124 0ustar rootroot/* 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 source tree. */ #include "libbb.h" ssize_t FAST_FUNC safe_read(int fd, void *buf, size_t count) { ssize_t n; do { n = read(fd, buf, count); } while (n < 0 && errno == EINTR); return n; } /* * Read all of the supplied buffer from a file. * This does multiple reads as necessary. * Returns the amount read, or -1 on an error. * A short read is returned on an end of file. */ ssize_t FAST_FUNC full_read(int fd, void *buf, size_t len) { ssize_t cc; ssize_t total; total = 0; while (len) { cc = safe_read(fd, buf, len); if (cc < 0) { if (total) { /* we already have some! */ /* user can do another read to know the error code */ return total; } return cc; /* read() returns -1 on failure. */ } if (cc == 0) break; buf = ((char *)buf) + cc; total += cc; len -= cc; } return total; } ssize_t FAST_FUNC read_close(int fd, void *buf, size_t size) { /*int e;*/ size = full_read(fd, buf, size); /*e = errno;*/ close(fd); /*errno = e;*/ return size; } ssize_t FAST_FUNC open_read_close(const char *filename, void *buf, size_t size) { int fd = open(filename, O_RDONLY); if (fd < 0) return fd; return read_close(fd, buf, size); } busybox-1.22.1/libbb/pw_encrypt_des.c0000644000000000000000000005653712263563520016255 0ustar rootroot/* * FreeSec: libcrypt for NetBSD * * Copyright (c) 1994 David Burren * All rights reserved. * * Adapted for FreeBSD-2.0 by Geoffrey M. Rehmet * this file should now *only* export crypt(), in order to make * binaries of libcrypt exportable from the USA * * Adapted for FreeBSD-4.0 by Mark R V Murray * this file should now *only* export crypt_des(), in order to make * a module that can be optionally included in libcrypt. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of other contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This is an original implementation of the DES and the crypt(3) interfaces * by David Burren . * * An excellent reference on the underlying algorithm (and related * algorithms) is: * * B. Schneier, Applied Cryptography: protocols, algorithms, * and source code in C, John Wiley & Sons, 1994. * * Note that in that book's description of DES the lookups for the initial, * pbox, and final permutations are inverted (this has been brought to the * attention of the author). A list of errata for this book has been * posted to the sci.crypt newsgroup by the author and is available for FTP. * * ARCHITECTURE ASSUMPTIONS: * It is assumed that the 8-byte arrays passed by reference can be * addressed as arrays of uint32_t's (ie. the CPU is not picky about * alignment). */ /* Parts busybox doesn't need or had optimized */ #define USE_PRECOMPUTED_u_sbox 1 #define USE_REPETITIVE_SPEEDUP 0 #define USE_ip_mask 0 #define USE_de_keys 0 /* A pile of data */ static const uint8_t IP[64] = { 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 }; static const uint8_t key_perm[56] = { 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 }; static const uint8_t key_shifts[16] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 }; static const uint8_t comp_perm[48] = { 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 }; /* * No E box is used, as it's replaced by some ANDs, shifts, and ORs. */ #if !USE_PRECOMPUTED_u_sbox static const uint8_t sbox[8][64] = { { 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 }, { 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 }, { 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 }, { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 }, { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 }, { 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 }, { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 }, { 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 } }; #else /* precomputed, with half-bytes packed into one byte */ static const uint8_t u_sbox[8][32] = { { 0x0e, 0xf4, 0x7d, 0x41, 0xe2, 0x2f, 0xdb, 0x18, 0xa3, 0x6a, 0xc6, 0xbc, 0x95, 0x59, 0x30, 0x87, 0xf4, 0xc1, 0x8e, 0x28, 0x4d, 0x96, 0x12, 0x7b, 0x5f, 0xbc, 0x39, 0xe7, 0xa3, 0x0a, 0x65, 0xd0, }, { 0x3f, 0xd1, 0x48, 0x7e, 0xf6, 0x2b, 0x83, 0xe4, 0xc9, 0x07, 0x12, 0xad, 0x6c, 0x90, 0xb5, 0x5a, 0xd0, 0x8e, 0xa7, 0x1b, 0x3a, 0xf4, 0x4d, 0x21, 0xb5, 0x68, 0x7c, 0xc6, 0x09, 0x53, 0xe2, 0x9f, }, { 0xda, 0x70, 0x09, 0x9e, 0x36, 0x43, 0x6f, 0xa5, 0x21, 0x8d, 0x5c, 0xe7, 0xcb, 0xb4, 0xf2, 0x18, 0x1d, 0xa6, 0xd4, 0x09, 0x68, 0x9f, 0x83, 0x70, 0x4b, 0xf1, 0xe2, 0x3c, 0xb5, 0x5a, 0x2e, 0xc7, }, { 0xd7, 0x8d, 0xbe, 0x53, 0x60, 0xf6, 0x09, 0x3a, 0x41, 0x72, 0x28, 0xc5, 0x1b, 0xac, 0xe4, 0x9f, 0x3a, 0xf6, 0x09, 0x60, 0xac, 0x1b, 0xd7, 0x8d, 0x9f, 0x41, 0x53, 0xbe, 0xc5, 0x72, 0x28, 0xe4, }, { 0xe2, 0xbc, 0x24, 0xc1, 0x47, 0x7a, 0xdb, 0x16, 0x58, 0x05, 0xf3, 0xaf, 0x3d, 0x90, 0x8e, 0x69, 0xb4, 0x82, 0xc1, 0x7b, 0x1a, 0xed, 0x27, 0xd8, 0x6f, 0xf9, 0x0c, 0x95, 0xa6, 0x43, 0x50, 0x3e, }, { 0xac, 0xf1, 0x4a, 0x2f, 0x79, 0xc2, 0x96, 0x58, 0x60, 0x1d, 0xd3, 0xe4, 0x0e, 0xb7, 0x35, 0x8b, 0x49, 0x3e, 0x2f, 0xc5, 0x92, 0x58, 0xfc, 0xa3, 0xb7, 0xe0, 0x14, 0x7a, 0x61, 0x0d, 0x8b, 0xd6, }, { 0xd4, 0x0b, 0xb2, 0x7e, 0x4f, 0x90, 0x18, 0xad, 0xe3, 0x3c, 0x59, 0xc7, 0x25, 0xfa, 0x86, 0x61, 0x61, 0xb4, 0xdb, 0x8d, 0x1c, 0x43, 0xa7, 0x7e, 0x9a, 0x5f, 0x06, 0xf8, 0xe0, 0x25, 0x39, 0xc2, }, { 0x1d, 0xf2, 0xd8, 0x84, 0xa6, 0x3f, 0x7b, 0x41, 0xca, 0x59, 0x63, 0xbe, 0x05, 0xe0, 0x9c, 0x27, 0x27, 0x1b, 0xe4, 0x71, 0x49, 0xac, 0x8e, 0xd2, 0xf0, 0xc6, 0x9a, 0x0d, 0x3f, 0x53, 0x65, 0xb8, }, }; #endif static const uint8_t pbox[32] = { 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 }; static const uint32_t bits32[32] = { 0x80000000, 0x40000000, 0x20000000, 0x10000000, 0x08000000, 0x04000000, 0x02000000, 0x01000000, 0x00800000, 0x00400000, 0x00200000, 0x00100000, 0x00080000, 0x00040000, 0x00020000, 0x00010000, 0x00008000, 0x00004000, 0x00002000, 0x00001000, 0x00000800, 0x00000400, 0x00000200, 0x00000100, 0x00000080, 0x00000040, 0x00000020, 0x00000010, 0x00000008, 0x00000004, 0x00000002, 0x00000001 }; static const uint8_t bits8[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; static int ascii_to_bin(char ch) { if (ch > 'z') return 0; if (ch >= 'a') return (ch - 'a' + 38); if (ch > 'Z') return 0; if (ch >= 'A') return (ch - 'A' + 12); if (ch > '9') return 0; if (ch >= '.') return (ch - '.'); return 0; } /* Static stuff that stays resident and doesn't change after * being initialized, and therefore doesn't need to be made * reentrant. */ struct const_des_ctx { #if USE_ip_mask uint8_t init_perm[64]; /* referenced 2 times */ #endif uint8_t final_perm[64]; /* 2 times */ uint8_t m_sbox[4][4096]; /* 5 times */ }; #define C (*cctx) #define init_perm (C.init_perm ) #define final_perm (C.final_perm) #define m_sbox (C.m_sbox ) static struct const_des_ctx* const_des_init(void) { unsigned i, j, b; struct const_des_ctx *cctx; #if !USE_PRECOMPUTED_u_sbox uint8_t u_sbox[8][64]; cctx = xmalloc(sizeof(*cctx)); /* Invert the S-boxes, reordering the input bits. */ for (i = 0; i < 8; i++) { for (j = 0; j < 64; j++) { b = (j & 0x20) | ((j & 1) << 4) | ((j >> 1) & 0xf); u_sbox[i][j] = sbox[i][b]; } } for (i = 0; i < 8; i++) { fprintf(stderr, "\t{\t"); for (j = 0; j < 64; j+=2) fprintf(stderr, " 0x%02x,", u_sbox[i][j] + u_sbox[i][j+1]*16); fprintf(stderr, "\n\t},\n"); } /* * Convert the inverted S-boxes into 4 arrays of 8 bits. * Each will handle 12 bits of the S-box input. */ for (b = 0; b < 4; b++) for (i = 0; i < 64; i++) for (j = 0; j < 64; j++) m_sbox[b][(i << 6) | j] = (uint8_t)((u_sbox[(b << 1)][i] << 4) | u_sbox[(b << 1) + 1][j]); #else cctx = xmalloc(sizeof(*cctx)); /* * Convert the inverted S-boxes into 4 arrays of 8 bits. * Each will handle 12 bits of the S-box input. */ for (b = 0; b < 4; b++) for (i = 0; i < 64; i++) for (j = 0; j < 64; j++) { uint8_t lo, hi; hi = u_sbox[(b << 1)][i / 2]; if (!(i & 1)) hi <<= 4; lo = u_sbox[(b << 1) + 1][j / 2]; if (j & 1) lo >>= 4; m_sbox[b][(i << 6) | j] = (hi & 0xf0) | (lo & 0x0f); } #endif /* * Set up the initial & final permutations into a useful form. */ for (i = 0; i < 64; i++) { final_perm[i] = IP[i] - 1; #if USE_ip_mask init_perm[final_perm[i]] = (uint8_t)i; #endif } return cctx; } struct des_ctx { const struct const_des_ctx *const_ctx; uint32_t saltbits; /* referenced 5 times */ #if USE_REPETITIVE_SPEEDUP uint32_t old_salt; /* 3 times */ uint32_t old_rawkey0, old_rawkey1; /* 3 times each */ #endif uint8_t un_pbox[32]; /* 2 times */ uint8_t inv_comp_perm[56]; /* 3 times */ uint8_t inv_key_perm[64]; /* 3 times */ uint32_t en_keysl[16], en_keysr[16]; /* 2 times each */ #if USE_de_keys uint32_t de_keysl[16], de_keysr[16]; /* 2 times each */ #endif #if USE_ip_mask uint32_t ip_maskl[8][256], ip_maskr[8][256]; /* 9 times each */ #endif uint32_t fp_maskl[8][256], fp_maskr[8][256]; /* 9 times each */ uint32_t key_perm_maskl[8][128], key_perm_maskr[8][128]; /* 9 times */ uint32_t comp_maskl[8][128], comp_maskr[8][128]; /* 9 times each */ uint32_t psbox[4][256]; /* 5 times */ }; #define D (*ctx) #define const_ctx (D.const_ctx ) #define saltbits (D.saltbits ) #define old_salt (D.old_salt ) #define old_rawkey0 (D.old_rawkey0 ) #define old_rawkey1 (D.old_rawkey1 ) #define un_pbox (D.un_pbox ) #define inv_comp_perm (D.inv_comp_perm ) #define inv_key_perm (D.inv_key_perm ) #define en_keysl (D.en_keysl ) #define en_keysr (D.en_keysr ) #define de_keysl (D.de_keysl ) #define de_keysr (D.de_keysr ) #define ip_maskl (D.ip_maskl ) #define ip_maskr (D.ip_maskr ) #define fp_maskl (D.fp_maskl ) #define fp_maskr (D.fp_maskr ) #define key_perm_maskl (D.key_perm_maskl ) #define key_perm_maskr (D.key_perm_maskr ) #define comp_maskl (D.comp_maskl ) #define comp_maskr (D.comp_maskr ) #define psbox (D.psbox ) static struct des_ctx* des_init(struct des_ctx *ctx, const struct const_des_ctx *cctx) { int i, j, b, k, inbit, obit; uint32_t p; const uint32_t *bits28, *bits24; if (!ctx) ctx = xmalloc(sizeof(*ctx)); const_ctx = cctx; #if USE_REPETITIVE_SPEEDUP old_rawkey0 = old_rawkey1 = 0; old_salt = 0; #endif saltbits = 0; bits28 = bits32 + 4; bits24 = bits28 + 4; /* Initialise the inverted key permutation. */ for (i = 0; i < 64; i++) { inv_key_perm[i] = 255; } /* * Invert the key permutation and initialise the inverted key * compression permutation. */ for (i = 0; i < 56; i++) { inv_key_perm[key_perm[i] - 1] = (uint8_t)i; inv_comp_perm[i] = 255; } /* Invert the key compression permutation. */ for (i = 0; i < 48; i++) { inv_comp_perm[comp_perm[i] - 1] = (uint8_t)i; } /* * Set up the OR-mask arrays for the initial and final permutations, * and for the key initial and compression permutations. */ for (k = 0; k < 8; k++) { uint32_t il, ir; uint32_t fl, fr; for (i = 0; i < 256; i++) { #if USE_ip_mask il = 0; ir = 0; #endif fl = 0; fr = 0; for (j = 0; j < 8; j++) { inbit = 8 * k + j; if (i & bits8[j]) { #if USE_ip_mask obit = init_perm[inbit]; if (obit < 32) il |= bits32[obit]; else ir |= bits32[obit - 32]; #endif obit = final_perm[inbit]; if (obit < 32) fl |= bits32[obit]; else fr |= bits32[obit - 32]; } } #if USE_ip_mask ip_maskl[k][i] = il; ip_maskr[k][i] = ir; #endif fp_maskl[k][i] = fl; fp_maskr[k][i] = fr; } for (i = 0; i < 128; i++) { il = 0; ir = 0; for (j = 0; j < 7; j++) { inbit = 8 * k + j; if (i & bits8[j + 1]) { obit = inv_key_perm[inbit]; if (obit == 255) continue; if (obit < 28) il |= bits28[obit]; else ir |= bits28[obit - 28]; } } key_perm_maskl[k][i] = il; key_perm_maskr[k][i] = ir; il = 0; ir = 0; for (j = 0; j < 7; j++) { inbit = 7 * k + j; if (i & bits8[j + 1]) { obit = inv_comp_perm[inbit]; if (obit == 255) continue; if (obit < 24) il |= bits24[obit]; else ir |= bits24[obit - 24]; } } comp_maskl[k][i] = il; comp_maskr[k][i] = ir; } } /* * Invert the P-box permutation, and convert into OR-masks for * handling the output of the S-box arrays setup above. */ for (i = 0; i < 32; i++) un_pbox[pbox[i] - 1] = (uint8_t)i; for (b = 0; b < 4; b++) { for (i = 0; i < 256; i++) { p = 0; for (j = 0; j < 8; j++) { if (i & bits8[j]) p |= bits32[un_pbox[8 * b + j]]; } psbox[b][i] = p; } } return ctx; } static void setup_salt(struct des_ctx *ctx, uint32_t salt) { uint32_t obit, saltbit; int i; #if USE_REPETITIVE_SPEEDUP if (salt == old_salt) return; old_salt = salt; #endif saltbits = 0; saltbit = 1; obit = 0x800000; for (i = 0; i < 24; i++) { if (salt & saltbit) saltbits |= obit; saltbit <<= 1; obit >>= 1; } } static void des_setkey(struct des_ctx *ctx, const char *key) { uint32_t k0, k1, rawkey0, rawkey1; int shifts, round; rawkey0 = ntohl(*(const uint32_t *) key); rawkey1 = ntohl(*(const uint32_t *) (key + 4)); #if USE_REPETITIVE_SPEEDUP if ((rawkey0 | rawkey1) && rawkey0 == old_rawkey0 && rawkey1 == old_rawkey1 ) { /* * Already setup for this key. * This optimisation fails on a zero key (which is weak and * has bad parity anyway) in order to simplify the starting * conditions. */ return; } old_rawkey0 = rawkey0; old_rawkey1 = rawkey1; #endif /* * Do key permutation and split into two 28-bit subkeys. */ k0 = key_perm_maskl[0][rawkey0 >> 25] | key_perm_maskl[1][(rawkey0 >> 17) & 0x7f] | key_perm_maskl[2][(rawkey0 >> 9) & 0x7f] | key_perm_maskl[3][(rawkey0 >> 1) & 0x7f] | key_perm_maskl[4][rawkey1 >> 25] | key_perm_maskl[5][(rawkey1 >> 17) & 0x7f] | key_perm_maskl[6][(rawkey1 >> 9) & 0x7f] | key_perm_maskl[7][(rawkey1 >> 1) & 0x7f]; k1 = key_perm_maskr[0][rawkey0 >> 25] | key_perm_maskr[1][(rawkey0 >> 17) & 0x7f] | key_perm_maskr[2][(rawkey0 >> 9) & 0x7f] | key_perm_maskr[3][(rawkey0 >> 1) & 0x7f] | key_perm_maskr[4][rawkey1 >> 25] | key_perm_maskr[5][(rawkey1 >> 17) & 0x7f] | key_perm_maskr[6][(rawkey1 >> 9) & 0x7f] | key_perm_maskr[7][(rawkey1 >> 1) & 0x7f]; /* * Rotate subkeys and do compression permutation. */ shifts = 0; for (round = 0; round < 16; round++) { uint32_t t0, t1; shifts += key_shifts[round]; t0 = (k0 << shifts) | (k0 >> (28 - shifts)); t1 = (k1 << shifts) | (k1 >> (28 - shifts)); #if USE_de_keys de_keysl[15 - round] = #endif en_keysl[round] = comp_maskl[0][(t0 >> 21) & 0x7f] | comp_maskl[1][(t0 >> 14) & 0x7f] | comp_maskl[2][(t0 >> 7) & 0x7f] | comp_maskl[3][t0 & 0x7f] | comp_maskl[4][(t1 >> 21) & 0x7f] | comp_maskl[5][(t1 >> 14) & 0x7f] | comp_maskl[6][(t1 >> 7) & 0x7f] | comp_maskl[7][t1 & 0x7f]; #if USE_de_keys de_keysr[15 - round] = #endif en_keysr[round] = comp_maskr[0][(t0 >> 21) & 0x7f] | comp_maskr[1][(t0 >> 14) & 0x7f] | comp_maskr[2][(t0 >> 7) & 0x7f] | comp_maskr[3][t0 & 0x7f] | comp_maskr[4][(t1 >> 21) & 0x7f] | comp_maskr[5][(t1 >> 14) & 0x7f] | comp_maskr[6][(t1 >> 7) & 0x7f] | comp_maskr[7][t1 & 0x7f]; } } static void do_des(struct des_ctx *ctx, /*uint32_t l_in, uint32_t r_in,*/ uint32_t *l_out, uint32_t *r_out, int count) { const struct const_des_ctx *cctx = const_ctx; /* * l_in, r_in, l_out, and r_out are in pseudo-"big-endian" format. */ uint32_t l, r, *kl, *kr; uint32_t f = f; /* silence gcc */ uint32_t r48l, r48r; int round; /* Do initial permutation (IP). */ #if USE_ip_mask uint32_t l_in = 0; uint32_t r_in = 0; l = ip_maskl[0][l_in >> 24] | ip_maskl[1][(l_in >> 16) & 0xff] | ip_maskl[2][(l_in >> 8) & 0xff] | ip_maskl[3][l_in & 0xff] | ip_maskl[4][r_in >> 24] | ip_maskl[5][(r_in >> 16) & 0xff] | ip_maskl[6][(r_in >> 8) & 0xff] | ip_maskl[7][r_in & 0xff]; r = ip_maskr[0][l_in >> 24] | ip_maskr[1][(l_in >> 16) & 0xff] | ip_maskr[2][(l_in >> 8) & 0xff] | ip_maskr[3][l_in & 0xff] | ip_maskr[4][r_in >> 24] | ip_maskr[5][(r_in >> 16) & 0xff] | ip_maskr[6][(r_in >> 8) & 0xff] | ip_maskr[7][r_in & 0xff]; #elif 0 /* -65 bytes (using the fact that l_in == r_in == 0) */ l = r = 0; for (round = 0; round < 8; round++) { l |= ip_maskl[round][0]; r |= ip_maskr[round][0]; } bb_error_msg("l:%x r:%x", l, r); /* reports 0, 0 always! */ #else /* using the fact that ip_maskX[] is constant (written to by des_init) */ l = r = 0; #endif do { /* Do each round. */ kl = en_keysl; kr = en_keysr; round = 16; do { /* Expand R to 48 bits (simulate the E-box). */ r48l = ((r & 0x00000001) << 23) | ((r & 0xf8000000) >> 9) | ((r & 0x1f800000) >> 11) | ((r & 0x01f80000) >> 13) | ((r & 0x001f8000) >> 15); r48r = ((r & 0x0001f800) << 7) | ((r & 0x00001f80) << 5) | ((r & 0x000001f8) << 3) | ((r & 0x0000001f) << 1) | ((r & 0x80000000) >> 31); /* * Do salting for crypt() and friends, and * XOR with the permuted key. */ f = (r48l ^ r48r) & saltbits; r48l ^= f ^ *kl++; r48r ^= f ^ *kr++; /* * Do sbox lookups (which shrink it back to 32 bits) * and do the pbox permutation at the same time. */ f = psbox[0][m_sbox[0][r48l >> 12]] | psbox[1][m_sbox[1][r48l & 0xfff]] | psbox[2][m_sbox[2][r48r >> 12]] | psbox[3][m_sbox[3][r48r & 0xfff]]; /* Now that we've permuted things, complete f(). */ f ^= l; l = r; r = f; } while (--round); r = l; l = f; } while (--count); /* Do final permutation (inverse of IP). */ *l_out = fp_maskl[0][l >> 24] | fp_maskl[1][(l >> 16) & 0xff] | fp_maskl[2][(l >> 8) & 0xff] | fp_maskl[3][l & 0xff] | fp_maskl[4][r >> 24] | fp_maskl[5][(r >> 16) & 0xff] | fp_maskl[6][(r >> 8) & 0xff] | fp_maskl[7][r & 0xff]; *r_out = fp_maskr[0][l >> 24] | fp_maskr[1][(l >> 16) & 0xff] | fp_maskr[2][(l >> 8) & 0xff] | fp_maskr[3][l & 0xff] | fp_maskr[4][r >> 24] | fp_maskr[5][(r >> 16) & 0xff] | fp_maskr[6][(r >> 8) & 0xff] | fp_maskr[7][r & 0xff]; } #define DES_OUT_BUFSIZE 21 static void to64_msb_first(char *s, unsigned v) { #if 0 *s++ = ascii64[(v >> 18) & 0x3f]; /* bits 23..18 */ *s++ = ascii64[(v >> 12) & 0x3f]; /* bits 17..12 */ *s++ = ascii64[(v >> 6) & 0x3f]; /* bits 11..6 */ *s = ascii64[v & 0x3f]; /* bits 5..0 */ #endif *s++ = i64c(v >> 18); /* bits 23..18 */ *s++ = i64c(v >> 12); /* bits 17..12 */ *s++ = i64c(v >> 6); /* bits 11..6 */ *s = i64c(v); /* bits 5..0 */ } static char * NOINLINE des_crypt(struct des_ctx *ctx, char output[DES_OUT_BUFSIZE], const unsigned char *key, const unsigned char *setting) { uint32_t salt, r0, r1, keybuf[2]; uint8_t *q; /* * Copy the key, shifting each character up by one bit * and padding with zeros. */ q = (uint8_t *)keybuf; while (q - (uint8_t *)keybuf != 8) { *q = *key << 1; if (*q) key++; q++; } des_setkey(ctx, (char *)keybuf); /* * setting - 2 bytes of salt * key - up to 8 characters */ salt = (ascii_to_bin(setting[1]) << 6) | ascii_to_bin(setting[0]); output[0] = setting[0]; /* * If the encrypted password that the salt was extracted from * is only 1 character long, the salt will be corrupted. We * need to ensure that the output string doesn't have an extra * NUL in it! */ output[1] = setting[1] ? setting[1] : output[0]; setup_salt(ctx, salt); /* Do it. */ do_des(ctx, /*0, 0,*/ &r0, &r1, 25 /* count */); /* Now encode the result. */ #if 0 { uint32_t l = (r0 >> 8); q = (uint8_t *)output + 2; *q++ = ascii64[(l >> 18) & 0x3f]; /* bits 31..26 of r0 */ *q++ = ascii64[(l >> 12) & 0x3f]; /* bits 25..20 of r0 */ *q++ = ascii64[(l >> 6) & 0x3f]; /* bits 19..14 of r0 */ *q++ = ascii64[l & 0x3f]; /* bits 13..8 of r0 */ l = ((r0 << 16) | (r1 >> 16)); *q++ = ascii64[(l >> 18) & 0x3f]; /* bits 7..2 of r0 */ *q++ = ascii64[(l >> 12) & 0x3f]; /* bits 1..2 of r0 and 31..28 of r1 */ *q++ = ascii64[(l >> 6) & 0x3f]; /* bits 27..22 of r1 */ *q++ = ascii64[l & 0x3f]; /* bits 21..16 of r1 */ l = r1 << 2; *q++ = ascii64[(l >> 12) & 0x3f]; /* bits 15..10 of r1 */ *q++ = ascii64[(l >> 6) & 0x3f]; /* bits 9..4 of r1 */ *q++ = ascii64[l & 0x3f]; /* bits 3..0 of r1 + 00 */ *q = 0; } #else /* Each call takes low-order 24 bits and stores 4 chars */ /* bits 31..8 of r0 */ to64_msb_first(output + 2, (r0 >> 8)); /* bits 7..0 of r0 and 31..16 of r1 */ to64_msb_first(output + 6, (r0 << 16) | (r1 >> 16)); /* bits 15..0 of r1 and two zero bits (plus extra zero byte) */ to64_msb_first(output + 10, (r1 << 8)); /* extra zero byte is encoded as '.', fixing it */ output[13] = '\0'; #endif return output; } #undef USE_PRECOMPUTED_u_sbox #undef USE_REPETITIVE_SPEEDUP #undef USE_ip_mask #undef USE_de_keys #undef C #undef init_perm #undef final_perm #undef m_sbox #undef D #undef const_ctx #undef saltbits #undef old_salt #undef old_rawkey0 #undef old_rawkey1 #undef un_pbox #undef inv_comp_perm #undef inv_key_perm #undef en_keysl #undef en_keysr #undef de_keysl #undef de_keysr #undef ip_maskl #undef ip_maskr #undef fp_maskl #undef fp_maskr #undef key_perm_maskl #undef key_perm_maskr #undef comp_maskl #undef comp_maskr #undef psbox busybox-1.22.1/libbb/write.c0000644000000000000000000000065712263563520014352 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 2008 Bernhard Reutner-Fischer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" /* Open file and write string str to it, close file. * Die on any open or write error. */ void FAST_FUNC xopen_xwrite_close(const char* file, const char* str) { int fd = xopen(file, O_WRONLY); xwrite_str(fd, str); close(fd); } busybox-1.22.1/libbb/xgethostbyname.c0000644000000000000000000000063612263563520016256 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini xgethostbyname implementation. * * Copyright (C) 2001 Matt Kraai . * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" struct hostent* FAST_FUNC xgethostbyname(const char *name) { struct hostent *retval = gethostbyname(name); if (!retval) bb_herror_msg_and_die("%s", name); return retval; } busybox-1.22.1/libbb/perror_msg.c0000644000000000000000000000145612263563520015375 0ustar rootroot/* 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 source tree. */ #include "libbb.h" void FAST_FUNC bb_perror_msg(const char *s, ...) { va_list p; va_start(p, s); /* Guard against ": Success" */ bb_verror_msg(s, p, errno ? strerror(errno) : NULL); va_end(p); } void FAST_FUNC bb_perror_msg_and_die(const char *s, ...) { va_list p; va_start(p, s); /* Guard against ": Success" */ bb_verror_msg(s, p, errno ? strerror(errno) : NULL); va_end(p); xfunc_die(); } void FAST_FUNC bb_simple_perror_msg(const char *s) { bb_perror_msg("%s", s); } void FAST_FUNC bb_simple_perror_msg_and_die(const char *s) { bb_perror_msg_and_die("%s", s); } busybox-1.22.1/libbb/find_root_device.c0000644000000000000000000000326212263563520016515 0ustar rootroot/* 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 source tree. */ #include "libbb.h" /* Find block device /dev/XXX which contains specified file * We handle /dev/dir/dir/dir too, at a cost of ~80 more bytes code */ /* Do not reallocate all this stuff on each recursion */ enum { DEVNAME_MAX = 256 }; struct arena { struct stat st; dev_t dev; /* Was PATH_MAX, but we recurse _/dev_. We can assume * people are not crazy enough to have mega-deep tree there */ char devpath[DEVNAME_MAX]; }; static char *find_block_device_in_dir(struct arena *ap) { DIR *dir; struct dirent *entry; char *retpath = NULL; int len, rem; len = strlen(ap->devpath); rem = DEVNAME_MAX-2 - len; if (rem <= 0) return NULL; dir = opendir(ap->devpath); if (!dir) return NULL; ap->devpath[len++] = '/'; while ((entry = readdir(dir)) != NULL) { safe_strncpy(ap->devpath + len, entry->d_name, rem); /* lstat: do not follow links */ if (lstat(ap->devpath, &ap->st) != 0) continue; if (S_ISBLK(ap->st.st_mode) && ap->st.st_rdev == ap->dev) { retpath = xstrdup(ap->devpath); break; } if (S_ISDIR(ap->st.st_mode)) { /* Do not recurse for '.' and '..' */ if (DOT_OR_DOTDOT(entry->d_name)) continue; retpath = find_block_device_in_dir(ap); if (retpath) break; } } closedir(dir); return retpath; } char* FAST_FUNC find_block_device(const char *path) { struct arena a; if (stat(path, &a.st) != 0) return NULL; a.dev = S_ISBLK(a.st.st_mode) ? a.st.st_rdev : a.st.st_dev; strcpy(a.devpath, "/dev"); return find_block_device_in_dir(&a); } busybox-1.22.1/libbb/signals.c0000644000000000000000000000433412263563520014654 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 1999-2004 by Erik Andersen * Copyright (C) 2006 Rob Landley * Copyright (C) 2006 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" /* All known arches use small ints for signals */ smallint bb_got_signal; void record_signo(int signo) { bb_got_signal = signo; } /* Saves 2 bytes on x86! Oh my... */ int FAST_FUNC sigaction_set(int signum, const struct sigaction *act) { return sigaction(signum, act, NULL); } int FAST_FUNC sigprocmask_allsigs(int how) { sigset_t set; sigfillset(&set); return sigprocmask(how, &set, NULL); } void FAST_FUNC bb_signals(int sigs, void (*f)(int)) { int sig_no = 0; int bit = 1; while (sigs) { if (sigs & bit) { sigs -= bit; signal(sig_no, f); } sig_no++; bit <<= 1; } } void FAST_FUNC bb_signals_recursive_norestart(int sigs, void (*f)(int)) { int sig_no = 0; int bit = 1; struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = f; /*sa.sa_flags = 0;*/ /*sigemptyset(&sa.sa_mask); - hope memset did it*/ while (sigs) { if (sigs & bit) { sigs -= bit; sigaction_set(sig_no, &sa); } sig_no++; bit <<= 1; } } void FAST_FUNC sig_block(int sig) { sigset_t ss; sigemptyset(&ss); sigaddset(&ss, sig); sigprocmask(SIG_BLOCK, &ss, NULL); } void FAST_FUNC sig_unblock(int sig) { sigset_t ss; sigemptyset(&ss); sigaddset(&ss, sig); sigprocmask(SIG_UNBLOCK, &ss, NULL); } void FAST_FUNC wait_for_any_sig(void) { sigset_t ss; sigemptyset(&ss); sigsuspend(&ss); } /* Assuming the sig is fatal */ void FAST_FUNC kill_myself_with_sig(int sig) { signal(sig, SIG_DFL); sig_unblock(sig); raise(sig); _exit(sig | 128); /* Should not reach it */ } void FAST_FUNC signal_SA_RESTART_empty_mask(int sig, void (*handler)(int)) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); /*sigemptyset(&sa.sa_mask);*/ sa.sa_flags = SA_RESTART; sa.sa_handler = handler; sigaction_set(sig, &sa); } void FAST_FUNC signal_no_SA_RESTART_empty_mask(int sig, void (*handler)(int)) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); /*sigemptyset(&sa.sa_mask);*/ /*sa.sa_flags = 0;*/ sa.sa_handler = handler; sigaction_set(sig, &sa); } busybox-1.22.1/libbb/obscure.c0000644000000000000000000001100312263563520014645 0ustar rootroot/* 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 source tree. */ /* 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 sub-string */ if (strcasestr(p2, p1) != NULL /* invert in case haystack is shorter than needle */ || strcasestr(p1, p2) != NULL /* as-is or capitalized */ /* || strcasecmp(p1, p2) == 0 - 1st strcasestr should catch this too */ ) { return 1; } return 0; } static int string_checker(const char *p1, const char *p2) { int size, i; /* check string */ int ret = string_checker_helper(p1, p2); /* make our own copy */ char *p = xstrdup(p1); /* reverse string */ i = size = strlen(p1); while (--i >= 0) { *p++ = p1[i]; } p -= size; /* restore pointer */ /* check reversed string */ ret |= string_checker_helper(p, p2); /* clean up */ memset(p, 0, size); free(p); return ret; } #define CATEGORIES 4 #define LOWERCASE 1 #define UPPERCASE 2 #define NUMBERS 4 #define SPECIAL 8 #define LAST_CAT 8 static const char *obscure_msg(const char *old_p, const char *new_p, const struct passwd *pw) { unsigned length; unsigned size; unsigned mixed; unsigned c; unsigned i; const char *p; char *hostname; /* size */ if (!new_p || (length = strlen(new_p)) < CONFIG_PASSWORD_MINLEN) return "too short"; /* no username as-is, as sub-string, reversed, capitalized, doubled */ if (string_checker(new_p, pw->pw_name)) { return "similar to username"; } #ifndef __BIONIC__ /* no gecos as-is, as sub-string, reversed, capitalized, doubled */ if (pw->pw_gecos[0] && string_checker(new_p, pw->pw_gecos)) { return "similar to gecos"; } #endif /* hostname as-is, as sub-string, reversed, capitalized, doubled */ hostname = safe_gethostname(); i = string_checker(new_p, hostname); free(hostname); if (i) return "similar to hostname"; /* Should / Must contain a mix of: */ mixed = 0; 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; } /* Count i'th char */ c = 0; p = new_p; while (1) { p = strchr(p, new_p[i]); if (p == NULL) { break; } c++; p++; if (!*p) { break; } } /* More than 50% similar characters ? */ if (c*2 >= length) { return "too many similar characters"; } } size = CONFIG_PASSWORD_MINLEN + 2*CATEGORIES; for (i = 1; i <= LAST_CAT; i <<= 1) if (mixed & i) size -= 2; if (length < size) return "too weak"; if (old_p && old_p[0]) { /* check vs. old password */ if (string_checker(new_p, old_p)) { return "similar to old password"; } } return NULL; } int FAST_FUNC obscure(const char *old, const char *newval, const struct passwd *pw) { const char *msg; msg = obscure_msg(old, newval, pw); if (msg) { printf("Bad password: %s\n", msg); return 1; } return 0; } busybox-1.22.1/libbb/info_msg.c0000644000000000000000000000216112263563520015011 0ustar rootroot/* 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 source tree. */ #include "libbb.h" #if ENABLE_FEATURE_SYSLOG # include #endif void FAST_FUNC bb_info_msg(const char *s, ...) { #ifdef THIS_ONE_DOESNT_DO_SINGLE_WRITE va_list p; /* va_copy is used because it is not portable * to use va_list p twice */ va_list p2; va_start(p, s); va_copy(p2, p); if (logmode & LOGMODE_STDIO) { vprintf(s, p); fputs(msg_eol, stdout); } # if ENABLE_FEATURE_SYSLOG if (logmode & LOGMODE_SYSLOG) vsyslog(LOG_INFO, s, p2); # endif va_end(p2); va_end(p); #else int used; char *msg; va_list p; if (logmode == 0) return; va_start(p, s); used = vasprintf(&msg, s, p); va_end(p); if (used < 0) return; # if ENABLE_FEATURE_SYSLOG if (logmode & LOGMODE_SYSLOG) syslog(LOG_INFO, "%s", msg); # endif if (logmode & LOGMODE_STDIO) { fflush_all(); /* used = strlen(msg); - must be true already */ msg[used++] = '\n'; full_write(STDOUT_FILENO, msg, used); } free(msg); #endif } busybox-1.22.1/libbb/get_cpu_count.c0000644000000000000000000000176012263563520016052 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Factored out of mpstat/iostat. * * Copyright (C) 2010 Marek Polacek * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" /* Does str start with "cpu"? */ int FAST_FUNC starts_with_cpu(const char *str) { return ((str[0] - 'c') | (str[1] - 'p') | (str[2] - 'u')) == 0; } /* * Get number of processors. Uses /proc/stat. * Return value 0 means one CPU and non SMP kernel. * Otherwise N means N processor(s) and SMP kernel. */ unsigned FAST_FUNC get_cpu_count(void) { FILE *fp; char line[256]; int proc_nr = -1; fp = xfopen_for_read("/proc/stat"); while (fgets(line, sizeof(line), fp)) { if (!starts_with_cpu(line)) { if (proc_nr >= 0) break; /* we are past "cpuN..." lines */ continue; } if (line[3] != ' ') { /* "cpuN" */ int num_proc; if (sscanf(line + 3, "%u", &num_proc) == 1 && num_proc > proc_nr ) { proc_nr = num_proc; } } } fclose(fp); return proc_nr + 1; } busybox-1.22.1/libbb/fgets_str.c0000644000000000000000000000401412263563520015207 0ustar rootroot/* 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 source tree. */ #include "libbb.h" static char *xmalloc_fgets_internal(FILE *file, const char *terminating_string, int chop_off, size_t *maxsz_p) { char *linebuf = NULL; const int term_length = strlen(terminating_string); int end_string_offset; int linebufsz = 0; int idx = 0; int ch; size_t maxsz = *maxsz_p; while (1) { ch = fgetc(file); if (ch == EOF) { if (idx == 0) return linebuf; /* NULL */ break; } if (idx >= linebufsz) { linebufsz += 200; linebuf = xrealloc(linebuf, linebufsz); if (idx >= maxsz) { linebuf[idx] = ch; idx++; break; } } linebuf[idx] = ch; idx++; /* Check for terminating string */ end_string_offset = idx - term_length; if (end_string_offset >= 0 && memcmp(&linebuf[end_string_offset], terminating_string, term_length) == 0 ) { if (chop_off) idx -= term_length; break; } } /* Grow/shrink *first*, then store NUL */ linebuf = xrealloc(linebuf, idx + 1); linebuf[idx] = '\0'; *maxsz_p = idx; return linebuf; } /* Read up to TERMINATING_STRING from FILE and return it, * including terminating string. * Non-terminated string can be returned if EOF is reached. * Return NULL if EOF is reached immediately. */ char* FAST_FUNC xmalloc_fgets_str(FILE *file, const char *terminating_string) { size_t maxsz = INT_MAX - 4095; return xmalloc_fgets_internal(file, terminating_string, 0, &maxsz); } char* FAST_FUNC xmalloc_fgets_str_len(FILE *file, const char *terminating_string, size_t *maxsz_p) { size_t maxsz; if (!maxsz_p) { maxsz = INT_MAX - 4095; maxsz_p = &maxsz; } return xmalloc_fgets_internal(file, terminating_string, 0, maxsz_p); } char* FAST_FUNC xmalloc_fgetline_str(FILE *file, const char *terminating_string) { size_t maxsz = INT_MAX - 4095; return xmalloc_fgets_internal(file, terminating_string, 1, &maxsz); } busybox-1.22.1/libbb/login.c0000644000000000000000000000456512263563520014332 0ustar rootroot/* 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 source tree. */ #include "libbb.h" /* After libbb.h, since it needs sys/types.h on some systems */ #include #define LOGIN " login: " static const char fmtstr_d[] ALIGN1 = "%A, %d %B %Y"; void FAST_FUNC print_login_issue(const char *issue_file, const char *tty) { FILE *fp; int c; char buf[256+1]; const char *outbuf; time_t t; struct utsname uts; time(&t); uname(&uts); puts("\r"); /* start a new line */ fp = fopen_for_read(issue_file); if (!fp) return; while ((c = fgetc(fp)) != EOF) { outbuf = buf; buf[0] = c; buf[1] = '\0'; if (c == '\n') { buf[1] = '\r'; buf[2] = '\0'; } if (c == '\\' || c == '%') { c = fgetc(fp); switch (c) { case 's': outbuf = uts.sysname; break; case 'n': case 'h': outbuf = uts.nodename; break; case 'r': outbuf = uts.release; break; case 'v': outbuf = uts.version; break; case 'm': outbuf = uts.machine; break; /* The field domainname of struct utsname is Linux specific. */ #if defined(__linux__) case 'D': case 'o': outbuf = uts.domainname; break; #endif case 'd': strftime(buf, sizeof(buf), fmtstr_d, localtime(&t)); break; case 't': strftime_HHMMSS(buf, sizeof(buf), &t); break; case 'l': outbuf = tty; break; default: buf[0] = c; } } fputs(outbuf, stdout); } fclose(fp); fflush_all(); } void FAST_FUNC print_login_prompt(void) { char *hostname = safe_gethostname(); fputs(hostname, stdout); fputs(LOGIN, stdout); fflush_all(); free(hostname); } /* Clear dangerous stuff, set PATH */ static const char forbid[] ALIGN1 = "ENV" "\0" "BASH_ENV" "\0" "HOME" "\0" "IFS" "\0" "SHELL" "\0" "LD_LIBRARY_PATH" "\0" "LD_PRELOAD" "\0" "LD_TRACE_LOADED_OBJECTS" "\0" "LD_BIND_NOW" "\0" "LD_AOUT_LIBRARY_PATH" "\0" "LD_AOUT_PRELOAD" "\0" "LD_NOWARN" "\0" "LD_KEEPDIR" "\0"; int FAST_FUNC sanitize_env_if_suid(void) { const char *p; if (getuid() == geteuid()) return 0; p = forbid; do { unsetenv(p); p += strlen(p) + 1; } while (*p); putenv((char*)bb_PATH_root_path); return 1; /* we indeed were run by different user! */ } busybox-1.22.1/libbb/get_line_from_file.c0000644000000000000000000001003112263563520017013 0ustar rootroot/* 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 source tree. */ #include "libbb.h" char* FAST_FUNC bb_get_chunk_from_file(FILE *file, int *end) { int ch; unsigned idx = 0; char *linebuf = NULL; while ((ch = getc(file)) != EOF) { /* grow the line buffer as necessary */ if (!(idx & 0xff)) linebuf = xrealloc(linebuf, idx + 0x100); linebuf[idx++] = (char) ch; if (ch == '\0') break; if (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* FAST_FUNC xmalloc_fgets(FILE *file) { int i; return bb_get_chunk_from_file(file, &i); } /* Get line. Remove trailing \n */ char* FAST_FUNC xmalloc_fgetline(FILE *file) { int i; char *c = bb_get_chunk_from_file(file, &i); if (i && c[--i] == '\n') c[i] = '\0'; return c; } #if 0 /* GNUism getline() should be faster (not tested) than a loop with fgetc */ /* Get line, including trailing \n if any */ char* FAST_FUNC xmalloc_fgets(FILE *file) { char *res_buf = NULL; size_t res_sz; if (getline(&res_buf, &res_sz, file) == -1) { free(res_buf); /* uclibc allocates a buffer even on EOF. WTF? */ res_buf = NULL; } //TODO: trimming to res_sz? return res_buf; } /* Get line. Remove trailing \n */ char* FAST_FUNC xmalloc_fgetline(FILE *file) { char *res_buf = NULL; size_t res_sz; res_sz = getline(&res_buf, &res_sz, file); if ((ssize_t)res_sz != -1) { if (res_buf[res_sz - 1] == '\n') res_buf[--res_sz] = '\0'; //TODO: trimming to res_sz? } else { free(res_buf); /* uclibc allocates a buffer even on EOF. WTF? */ res_buf = NULL; } return res_buf; } #endif #if 0 /* Faster routines (~twice as fast). +170 bytes. Unused as of 2008-07. * * NB: they stop at NUL byte too. * Performance is important here. Think "grep 50gigabyte_file"... * Ironically, grep can't use it because of NUL issue. * We sorely need C lib to provide fgets which reports size! * * Update: * Actually, uclibc and glibc have it. man getline. It's GNUism, * but very useful one (if it's as fast as this code). * TODO: * - currently, sed and sort use bb_get_chunk_from_file and heavily * depend on its "stop on \n or \0" behavior, and STILL they fail * to handle all cases with embedded NULs correctly. So: * - audit sed and sort; convert them to getline FIRST. * - THEN ditch bb_get_chunk_from_file, replace it with getline. * - provide getline implementation for non-GNU systems. */ static char* xmalloc_fgets_internal(FILE *file, int *sizep) { int len; int idx = 0; char *linebuf = NULL; while (1) { char *r; linebuf = xrealloc(linebuf, idx + 0x100); r = fgets(&linebuf[idx], 0x100, file); if (!r) { /* need to terminate in case this is error * (EOF puts NUL itself) */ linebuf[idx] = '\0'; break; } /* stupid. fgets knows the len, it should report it somehow */ len = strlen(&linebuf[idx]); idx += len; if (len != 0xff || linebuf[idx - 1] == '\n') break; } *sizep = idx; if (idx) { /* xrealloc(linebuf, idx + 1) is up to caller */ return linebuf; } free(linebuf); return NULL; } /* Get line, remove trailing \n */ char* FAST_FUNC xmalloc_fgetline_fast(FILE *file) { int sz; char *r = xmalloc_fgets_internal(file, &sz); if (r && r[sz - 1] == '\n') r[--sz] = '\0'; return r; /* not xrealloc(r, sz + 1)! */ } char* FAST_FUNC xmalloc_fgets(FILE *file) { int sz; return xmalloc_fgets_internal(file, &sz); } /* Get line, remove trailing \n */ char* FAST_FUNC xmalloc_fgetline(FILE *file) { int sz; char *r = xmalloc_fgets_internal(file, &sz); if (!r) return r; if (r[sz - 1] == '\n') r[--sz] = '\0'; return xrealloc(r, sz + 1); } #endif busybox-1.22.1/libbb/str_tolower.c0000644000000000000000000000043212263563520015572 0ustar rootroot/* vi set: sw=4 ts=4: */ /* Convert string str to lowercase, return str. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" char* FAST_FUNC str_tolower(char *str) { char *c; for (c = str; *c; ++c) *c = tolower(*c); return str; } busybox-1.22.1/libbb/default_error_retval.c0000644000000000000000000000104612263563520017423 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright (C) 2003 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* Seems silly to copyright a global variable. ;-) Oh well. * * At least one applet (cmp) returns a value different from the typical * EXIT_FAILURE values (1) when an error occurs. So, make it configurable * by the applet. I suppose we could use a wrapper function to set it, but * that too seems silly. */ #include "libbb.h" uint8_t xfunc_error_retval = EXIT_FAILURE; busybox-1.22.1/libbb/wfopen_input.c0000644000000000000000000000232412263563520015726 0ustar rootroot/* 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 source tree. */ /* A number of applets need to open a file for reading, where the filename * is a command line arg. Since often that arg is '-' (meaning stdin), * we avoid testing everywhere by consolidating things in this routine. */ #include "libbb.h" FILE* FAST_FUNC fopen_or_warn_stdin(const char *filename) { FILE *fp = stdin; if (filename != bb_msg_standard_input && NOT_LONE_DASH(filename) ) { fp = fopen_or_warn(filename, "r"); } return fp; } FILE* FAST_FUNC xfopen_stdin(const char *filename) { FILE *fp = fopen_or_warn_stdin(filename); if (fp) return fp; xfunc_die(); /* We already output an error message. */ } int FAST_FUNC open_or_warn_stdin(const char *filename) { int fd = STDIN_FILENO; if (filename != bb_msg_standard_input && NOT_LONE_DASH(filename) ) { fd = open_or_warn(filename, O_RDONLY); } return fd; } int FAST_FUNC xopen_stdin(const char *filename) { int fd = open_or_warn_stdin(filename); if (fd >= 0) return fd; xfunc_die(); /* We already output an error message. */ } busybox-1.22.1/libbb/bb_bswap_64.c0000644000000000000000000000040412263563520015276 0ustar rootroot/* * Utility routines. * * Copyright (C) 2010 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" #if !(ULONG_MAX > 0xffffffff) uint64_t FAST_FUNC bb_bswap_64(uint64_t x) { return bswap_64(x); } #endif busybox-1.22.1/libbb/bb_pwd.c0000644000000000000000000000632412263563520014452 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * password utility routines. * * Copyright (C) 1999-2004 by Erik Andersen * Copyright (C) 2008 by Tito Ragusa * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" /* TODO: maybe change API to return malloced data? * This will allow to stop using libc functions returning * pointers to static data (getpwuid) */ struct passwd* FAST_FUNC xgetpwnam(const char *name) { struct passwd *pw = getpwnam(name); if (!pw) bb_error_msg_and_die("unknown user %s", name); return pw; } struct group* FAST_FUNC xgetgrnam(const char *name) { struct group *gr = getgrnam(name); if (!gr) bb_error_msg_and_die("unknown group %s", name); return gr; } struct passwd* FAST_FUNC xgetpwuid(uid_t uid) { struct passwd *pw = getpwuid(uid); if (!pw) bb_error_msg_and_die("unknown uid %u", (unsigned)uid); return pw; } struct group* FAST_FUNC xgetgrgid(gid_t gid) { struct group *gr = getgrgid(gid); if (!gr) bb_error_msg_and_die("unknown gid %u", (unsigned)gid); return gr; } char* FAST_FUNC xuid2uname(uid_t uid) { struct passwd *pw = xgetpwuid(uid); return pw->pw_name; } char* FAST_FUNC xgid2group(gid_t gid) { struct group *gr = xgetgrgid(gid); return gr->gr_name; } char* FAST_FUNC uid2uname(uid_t uid) { struct passwd *pw = getpwuid(uid); return (pw) ? pw->pw_name : NULL; } char* FAST_FUNC gid2group(gid_t gid) { struct group *gr = getgrgid(gid); return (gr) ? gr->gr_name : NULL; } char* FAST_FUNC uid2uname_utoa(uid_t uid) { char *name = uid2uname(uid); return (name) ? name : utoa(uid); } char* FAST_FUNC gid2group_utoa(gid_t gid) { char *name = gid2group(gid); return (name) ? name : utoa(gid); } long FAST_FUNC xuname2uid(const char *name) { struct passwd *myuser; myuser = xgetpwnam(name); return myuser->pw_uid; } long FAST_FUNC xgroup2gid(const char *name) { struct group *mygroup; mygroup = xgetgrnam(name); return mygroup->gr_gid; } unsigned long FAST_FUNC get_ug_id(const char *s, long FAST_FUNC (*xname2id)(const char *)) { unsigned long r; r = bb_strtoul(s, NULL, 10); if (errno) return xname2id(s); return r; } /* Experimental "mallocing" API. * The goal is nice: "we want to support a case when "guests" group is very large" * but the code is butt-ugly. */ #if 0 static char *find_latest(char last, char *cp) { if (!cp) return last; cp += strlen(cp) + 1; if (last < cp) last = cp; return last; } struct group* FAST_FUNC xmalloc_getgrnam(const char *name) { struct { struct group gr; // May still be not enough! char buf[64*1024 - sizeof(struct group) - 16]; } *s; struct group *grp; int r; char *last; char **gr_mem; s = xmalloc(sizeof(*s)); r = getgrnam_r(name, &s->gr, s->buf, sizeof(s->buf), &grp); if (!grp) { free(s); return grp; } last = find_latest(s->buf, grp->gr_name); last = find_latest(last, grp->gr_passwd); gr_mem = grp->gr_mem; while (*gr_mem) last = find_latest(last, *gr_mem++); gr_mem++; /* points past NULL */ if (last < (char*)gr_mem) last = (char*)gr_mem; //FIXME: what if we get not only truncated, but also moved here? // grp->gr_name pointer and friends are invalid now!!! s = xrealloc(s, last - (char*)s); return grp; } #endif busybox-1.22.1/libbb/inet_common.c0000644000000000000000000001174112263563520015523 0ustar rootroot/* 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 * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" #include "inet_common.h" int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst) { struct hostent *hp; #if ENABLE_FEATURE_ETC_NETWORKS struct netent *np; #endif /* Grmpf. -FvK */ s_in->sin_family = AF_INET; s_in->sin_port = 0; /* Default is special, meaning 0.0.0.0. */ if (strcmp(name, "default") == 0) { s_in->sin_addr.s_addr = INADDR_ANY; return 1; } /* Look to see if it's a dotted quad. */ if (inet_aton(name, &s_in->sin_addr)) { return 0; } /* If we expect this to be a hostname, try hostname database first */ #ifdef DEBUG if (hostfirst) { bb_error_msg("gethostbyname(%s)", name); } #endif if (hostfirst) { hp = gethostbyname(name); if (hp != NULL) { memcpy(&s_in->sin_addr, hp->h_addr_list[0], sizeof(struct in_addr)); return 0; } } #if ENABLE_FEATURE_ETC_NETWORKS /* Try the NETWORKS database to see if this is a known network. */ #ifdef DEBUG bb_error_msg("getnetbyname(%s)", name); #endif np = getnetbyname(name); if (np != NULL) { s_in->sin_addr.s_addr = htonl(np->n_net); return 1; } #endif if (hostfirst) { /* Don't try again */ return -1; } #ifdef DEBUG res_init(); _res.options |= RES_DEBUG; bb_error_msg("gethostbyname(%s)", name); #endif hp = gethostbyname(name); if (hp == NULL) { return -1; } memcpy(&s_in->sin_addr, hp->h_addr_list[0], sizeof(struct in_addr)); return 0; } /* numeric: & 0x8000: default instead of *, * & 0x4000: host instead of net, * & 0x0fff: don't resolve */ char* FAST_FUNC INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t netmask) { /* addr-to-name cache */ struct addr { struct addr *next; struct sockaddr_in addr; int host; char name[1]; }; static struct addr *cache = NULL; struct addr *pn; char *name; uint32_t ad, host_ad; int host = 0; if (s_in->sin_family != AF_INET) { #ifdef DEBUG bb_error_msg("rresolve: unsupported address family %d!", s_in->sin_family); #endif errno = EAFNOSUPPORT; return NULL; } ad = s_in->sin_addr.s_addr; #ifdef DEBUG bb_error_msg("rresolve: %08x, mask %08x, num %08x", (unsigned)ad, netmask, numeric); #endif if (ad == INADDR_ANY) { if ((numeric & 0x0FFF) == 0) { if (numeric & 0x8000) return xstrdup("default"); return xstrdup("*"); } } if (numeric & 0x0FFF) return xstrdup(inet_ntoa(s_in->sin_addr)); if ((ad & (~netmask)) != 0 || (numeric & 0x4000)) host = 1; pn = cache; while (pn) { if (pn->addr.sin_addr.s_addr == ad && pn->host == host) { #ifdef DEBUG bb_error_msg("rresolve: found %s %08x in cache", (host ? "host" : "net"), (unsigned)ad); #endif return xstrdup(pn->name); } pn = pn->next; } host_ad = ntohl(ad); name = NULL; if (host) { struct hostent *ent; #ifdef DEBUG bb_error_msg("gethostbyaddr (%08x)", (unsigned)ad); #endif ent = gethostbyaddr((char *) &ad, 4, AF_INET); if (ent) name = xstrdup(ent->h_name); } else if (ENABLE_FEATURE_ETC_NETWORKS) { struct netent *np; #ifdef DEBUG bb_error_msg("getnetbyaddr (%08x)", (unsigned)host_ad); #endif np = getnetbyaddr(host_ad, AF_INET); if (np) name = xstrdup(np->n_name); } if (!name) name = xstrdup(inet_ntoa(s_in->sin_addr)); pn = xmalloc(sizeof(*pn) + strlen(name)); /* no '+ 1', it's already accounted for */ pn->next = cache; pn->addr = *s_in; pn->host = host; strcpy(pn->name, name); cache = pn; return name; } #if ENABLE_FEATURE_IPV6 int FAST_FUNC INET6_resolve(const char *name, struct sockaddr_in6 *sin6) { struct addrinfo req, *ai = NULL; int s; memset(&req, 0, sizeof(req)); req.ai_family = AF_INET6; s = getaddrinfo(name, NULL, &req, &ai); if (s != 0) { bb_error_msg("getaddrinfo: %s: %d", name, s); return -1; } memcpy(sin6, ai->ai_addr, sizeof(*sin6)); freeaddrinfo(ai); return 0; } #ifndef IN6_IS_ADDR_UNSPECIFIED # define IN6_IS_ADDR_UNSPECIFIED(a) \ (((uint32_t *) (a))[0] == 0 && ((uint32_t *) (a))[1] == 0 && \ ((uint32_t *) (a))[2] == 0 && ((uint32_t *) (a))[3] == 0) #endif char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric) { char name[128]; int s; if (sin6->sin6_family != AF_INET6) { #ifdef DEBUG bb_error_msg("rresolve: unsupported address family %d!", sin6->sin6_family); #endif errno = EAFNOSUPPORT; return NULL; } if (numeric & 0x7FFF) { inet_ntop(AF_INET6, &sin6->sin6_addr, name, sizeof(name)); return xstrdup(name); } if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { if (numeric & 0x8000) return xstrdup("default"); return xstrdup("*"); } s = getnameinfo((struct sockaddr *) sin6, sizeof(*sin6), name, sizeof(name), /*serv,servlen:*/ NULL, 0, 0); if (s != 0) { bb_error_msg("getnameinfo failed"); return NULL; } return xstrdup(name); } #endif /* CONFIG_FEATURE_IPV6 */ busybox-1.22.1/libbb/perror_nomsg.c0000644000000000000000000000115112263563520015722 0ustar rootroot/* 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 source tree. */ /* gcc warns about a null format string, therefore we provide * modified definition without "attribute (format)" * instead of including libbb.h */ //#include "libbb.h" #include "platform.h" extern void bb_perror_msg(const char *s, ...) FAST_FUNC; /* suppress gcc "no previous prototype" warning */ void FAST_FUNC bb_perror_nomsg(void); void FAST_FUNC bb_perror_nomsg(void) { bb_perror_msg(0); } busybox-1.22.1/libbb/print_flags.c0000644000000000000000000000143712263563520015525 0ustar rootroot/* vi: set sw=4 ts=4: */ /* Print string that matches bit masked flags * * Copyright (C) 2008 Natanael Copa * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" /* returns a set with the flags not printed */ int FAST_FUNC print_flags_separated(const int *masks, const char *labels, int flags, const char *separator) { const char *need_separator = NULL; while (*labels) { if (flags & *masks) { printf("%s%s", need_separator ? need_separator : "", labels); need_separator = separator; flags &= ~ *masks; } masks++; labels += strlen(labels) + 1; } return flags; } int FAST_FUNC print_flags(const masks_labels_t *ml, int flags) { return print_flags_separated(ml->masks, ml->labels, flags, NULL); } busybox-1.22.1/libbb/printable_string.c0000644000000000000000000000225712263563520016564 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Unicode support routines. * * Copyright (C) 2010 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" #include "unicode.h" const char* FAST_FUNC printable_string(uni_stat_t *stats, const char *str) { static char *saved[4]; static unsigned cur_saved; /* = 0 */ char *dst; const char *s; s = str; while (1) { unsigned char c = *s; if (c == '\0') { /* 99+% of inputs do not need conversion */ if (stats) { stats->byte_count = (s - str); stats->unicode_count = (s - str); stats->unicode_width = (s - str); } return str; } if (c < ' ') break; if (c >= 0x7f) break; s++; } #if ENABLE_UNICODE_SUPPORT dst = unicode_conv_to_printable(stats, str); #else { char *d = dst = xstrdup(str); while (1) { unsigned char c = *d; if (c == '\0') break; if (c < ' ' || c >= 0x7f) *d = '?'; d++; } if (stats) { stats->byte_count = (d - dst); stats->unicode_count = (d - dst); stats->unicode_width = (d - dst); } } #endif free(saved[cur_saved]); saved[cur_saved] = dst; cur_saved = (cur_saved + 1) & (ARRAY_SIZE(saved)-1); return dst; } busybox-1.22.1/libbb/herror_msg.c0000644000000000000000000000100412263563520015352 0ustar rootroot/* 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 source tree. */ #include "libbb.h" void FAST_FUNC bb_herror_msg(const char *s, ...) { va_list p; va_start(p, s); bb_verror_msg(s, p, hstrerror(h_errno)); va_end(p); } void FAST_FUNC bb_herror_msg_and_die(const char *s, ...) { va_list p; va_start(p, s); bb_verror_msg(s, p, hstrerror(h_errno)); va_end(p); xfunc_die(); } busybox-1.22.1/libbb/lineedit_ptr_hack.c0000644000000000000000000000120012263563520016651 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright (C) 2008 by Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ struct lineedit_statics; #ifndef GCC_COMBINE /* We cheat here. It is declared as const ptr in libbb.h, * but here we make it live in R/W memory */ struct lineedit_statics *lineedit_ptr_to_statics; #else /* gcc -combine will see through and complain */ /* Using alternative method which is more likely to break * on weird architectures, compilers, linkers and so on */ struct lineedit_statics *const lineedit_ptr_to_statics __attribute__ ((section (".data"))); #endif busybox-1.22.1/libbb/run_shell.c0000644000000000000000000000615612263563520015213 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright 1989 - 1991, Julianne Frances Haugh * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Julianne F. Haugh nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "libbb.h" #if ENABLE_SELINUX #include /* for setexeccon */ #endif #if ENABLE_SELINUX static security_context_t current_sid; void FAST_FUNC renew_current_security_context(void) { freecon(current_sid); /* Release old context */ getcon(¤t_sid); /* update */ } void FAST_FUNC set_current_security_context(security_context_t sid) { freecon(current_sid); /* Release old context */ current_sid = sid; } #endif /* Run SHELL, or DEFAULT_SHELL if SHELL is "" or NULL. * If COMMAND is nonzero, pass it to the shell with the -c option. * If ADDITIONAL_ARGS is nonzero, pass it to the shell as more * arguments. */ void FAST_FUNC run_shell(const char *shell, int loginshell, const char *command, const char **additional_args) { const char **args; int argno; int additional_args_cnt = 0; for (args = additional_args; args && *args; args++) additional_args_cnt++; args = xmalloc(sizeof(char*) * (4 + additional_args_cnt)); if (!shell || !shell[0]) shell = DEFAULT_SHELL; args[0] = bb_get_last_path_component_nostrip(shell); if (loginshell) args[0] = xasprintf("-%s", args[0]); argno = 1; if (command) { args[argno++] = "-c"; args[argno++] = command; } if (additional_args) { for (; *additional_args; ++additional_args) args[argno++] = *additional_args; } args[argno] = NULL; #if ENABLE_SELINUX if (current_sid) setexeccon(current_sid); if (ENABLE_FEATURE_CLEAN_UP) freecon(current_sid); #endif execv(shell, (char **) args); bb_perror_msg_and_die("can't execute '%s'", shell); } busybox-1.22.1/libbb/concat_subpath_file.c0000644000000000000000000000100712263563520017202 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) (C) 2003 Vladimir Oleynik * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* This function make special for recursive actions with usage concat_path_file(path, filename) and skipping "." and ".." directory entries */ #include "libbb.h" char* FAST_FUNC concat_subpath_file(const char *path, const char *f) { if (f && DOT_OR_DOTDOT(f)) return NULL; return concat_path_file(path, f); } busybox-1.22.1/libbb/getopt32.c0000644000000000000000000004656212263563520014674 0ustar rootroot/* 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 source tree. */ #if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG # include #endif #include "libbb.h" /* Documentation uint32_t getopt32(char **argv, const char *applet_opts, ...) The command line options must be declared in const char *applet_opts as a string of chars, for example: flags = getopt32(argv, "rnug"); If one of the given options is found, a flag value is added to the return value (an unsigned long). The flag value is determined by the position of the char in applet_opts string. For example, in the above case: flags = getopt32(argv, "rnug"); "r" will add 1 (bit 0) "n" will add 2 (bit 1) "u" will add 4 (bit 2) "g" will add 8 (bit 3) and so on. You can also look at the return value as a bit field and each option sets one bit. On exit, global variable optind is set so that if you will do argc -= optind; argv += optind; then argc will be equal to number of remaining non-option arguments, first one would be in argv[0], next in argv[1] and so on (options and their parameters will be moved into argv[] positions prior to argv[optind]). ":" If one of the options requires an argument, then add a ":" after the char in applet_opts and provide a pointer to store the argument. For example: char *pointer_to_arg_for_a; char *pointer_to_arg_for_b; char *pointer_to_arg_for_c; char *pointer_to_arg_for_d; flags = getopt32(argv, "a:b:c:d:", &pointer_to_arg_for_a, &pointer_to_arg_for_b, &pointer_to_arg_for_c, &pointer_to_arg_for_d); The type of the pointer (char* or llist_t*) may be controlled by the "::" special separator that is set in the external string opt_complementary (see below for more info). "::" If option can have an *optional* argument, then add a "::" after its char in applet_opts and provide a pointer to store the argument. Note that optional arguments _must_ immediately follow the option: -oparam, not -o param. "+" If the first character in the applet_opts string is a plus, then option processing will stop as soon as a non-option is encountered in the argv array. Useful for applets like env which should not process arguments to subprograms: env -i ls -d / Here we want env to process just the '-i', not the '-d'. "!" Report bad option, missing required options, inconsistent options with all-ones return value (instead of abort). const char *applet_long_options This struct allows you to define long options: static const char applet_longopts[] ALIGN1 = //"name\0" has_arg val "verbose\0" No_argument "v" ; applet_long_options = applet_longopts; The last member of struct option (val) typically is set to matching short option from applet_opts. If there is no matching char in applet_opts, then: - return bit have next position after short options - if has_arg is not "No_argument", use ptr for arg also - opt_complementary affects it too Note: a good applet will make long options configurable via the config process and not a required feature. The current standard is to name the config option CONFIG_FEATURE__LONG_OPTIONS. const char *opt_complementary ":" The colon (":") is used to separate groups of two or more chars and/or groups of chars and special characters (stating some conditions to be checked). "abc" If groups of two or more chars are specified, the first char is the main option and the other chars are secondary options. Their flags will be turned on if the main option is found even if they are not specifed on the command line. For example: opt_complementary = "abc"; flags = getopt32(argv, "abcd") If getopt() finds "-a" on the command line, then getopt32's return value will be as if "-a -b -c" were found. "ww" Adjacent double options have a counter associated which indicates the number of occurrences of the option. For example the ps applet needs: if w is given once, GNU ps sets the width to 132, if w is given more than once, it is "unlimited" int w_counter = 0; // must be initialized! opt_complementary = "ww"; getopt32(argv, "w", &w_counter); if (w_counter) width = (w_counter == 1) ? 132 : INT_MAX; else get_terminal_width(...&width...); w_counter is a pointer to an integer. It has to be passed to getopt32() after all other option argument sinks. For example: accept multiple -v to indicate the level of verbosity and for each -b optarg, add optarg to my_b. Finally, if b is given, turn off c and vice versa: llist_t *my_b = NULL; int verbose_level = 0; opt_complementary = "vv:b::b-c:c-b"; f = getopt32(argv, "vb:c", &my_b, &verbose_level); if (f & 2) // -c after -b unsets -b flag while (my_b) dosomething_with(llist_pop(&my_b)); if (my_b) // but llist is stored if -b is specified free_llist(my_b); if (verbose_level) printf("verbose level is %d\n", verbose_level); Special characters: "-" A group consisting of just a dash 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. Example: opt_complementary = "-:w-x:x-w"; // "-w-x:x-w" would also work, getopt32(argv, "wx"); // but is less readable This makes it possible to use options without a dash (./program w x) as well as with a dash (./program -x). NB: getopt32() will leak a small amount of memory if you use this option! Do not use it if there is a possibility of recursive getopt32() calls. "--" A double dash at the beginning of opt_complementary means the argv[1] string should always be treated as options, even if it isn't prefixed with a "-". This is useful for special syntax in applets such as "ar" and "tar": tar xvf foo.tar NB: getopt32() will leak a small amount of memory if you use this option! Do not use it if there is a possibility of recursive getopt32() calls. "-N" A dash as the first char in a opt_complementary group followed by a single digit (0-9) means that at least N non-option arguments must be present on the command line "=N" An equal sign as the first char in a opt_complementary group followed by a single digit (0-9) means that exactly N non-option arguments must be present on the command line "?N" A "?" as the first char in a opt_complementary group followed by a single digit (0-9) means that at most N arguments must be present on the command line. "V-" An option with dash before colon or end-of-line results in bb_show_usage() being called if this option is encountered. This is typically used to implement "print verbose usage message and exit" option. "a-b" A dash between two options causes the second of the two to be unset (and ignored) if it is given on the command line. [FIXME: what if they are the same? like "x-x"? Is it ever useful?] For example: The du applet has the options "-s" and "-d depth". If getopt32 finds -s, then -d is unset or if it finds -d then -s is unset. (Note: busybox implements the GNU "--max-depth" option as "-d".) To obtain this behavior, you set opt_complementary = "s-d:d-s". Only one flag value is added to getopt32's return value depending on the position of the options on the command line. If one of the two options requires an argument pointer (":" in applet_opts as in "d:") optarg is set accordingly. char *smax_print_depth; opt_complementary = "s-d:d-s:x-x"; opt = getopt32(argv, "sd:x", &smax_print_depth); if (opt & 2) max_print_depth = atoi(smax_print_depth); if (opt & 4) printf("Detected odd -x usage\n"); "a--b" A double dash between two options, or between an option and a group of options, means that they are mutually exclusive. Unlike the "-" case above, an error will be forced if the options are used together. For example: The cut applet must have only one type of list specified, so -b, -c and -f are mutually exclusive and should raise an error if specified together. In this case you must set opt_complementary = "b--cf:c--bf:f--bc". If two of the mutually exclusive options are found, getopt32 will call bb_show_usage() and die. "x--x" Variation of the above, it means that -x option should occur at most once. "a+" A plus after a char in opt_complementary means that the parameter for this option is a nonnegative integer. It will be processed with xatoi_positive() - allowed range is 0..INT_MAX. int param; // "unsigned param;" will also work opt_complementary = "p+"; getopt32(argv, "p:", ¶m); "a::" A double colon after a char in opt_complementary means that the option can occur multiple times. Each occurrence will be saved as a llist_t element instead of char*. For example: The grep applet can have one or more "-e pattern" arguments. In this case you should use getopt32() as follows: llist_t *patterns = NULL; (this pointer must be initializated to NULL if the list is empty as required by llist_add_to_end(llist_t **old_head, char *new_item).) opt_complementary = "e::"; getopt32(argv, "e:", &patterns); $ grep -e user -e root /etc/passwd root:x:0:0:root:/root:/bin/bash user:x:500:500::/home/user:/bin/bash "a?b" A "?" between an option and a group of options means that at least one of them is required to occur if the first option occurs in preceding command line arguments. For example from "id" applet: // Don't allow -n -r -rn -ug -rug -nug -rnug opt_complementary = "r?ug:n?ug:u--g:g--u"; flags = getopt32(argv, "rnug"); This example allowed only: $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng "X" A opt_complementary group with just a single letter means that this option is required. If more than one such group exists, at least one option is required to occur (not all of them). For example from "start-stop-daemon" applet: // Don't allow -KS -SK, but -S or -K is required opt_complementary = "K:S:K--S:S--K"; flags = getopt32(argv, "KS...); Don't forget to use ':'. For example, "?322-22-23X-x-a" is interpreted as "?3:22:-2:2-2:2-3Xa:2--x" - max 3 args; count uses of '-2'; min 2 args; if there is a '-2' option then unset '-3', '-X' and '-a'; if there is a '-2' and after it a '-x' then error out. But it's far too obfuscated. Use ':' to separate groups. */ /* Code here assumes that 'unsigned' is at least 32 bits wide */ const char *const bb_argv_dash[] = { "-", NULL }; const char *opt_complementary; enum { PARAM_STRING, PARAM_LIST, PARAM_INT, }; typedef struct { unsigned char opt_char; smallint param_type; unsigned switch_on; unsigned switch_off; unsigned incongruously; unsigned requires; void **optarg; /* char**, llist_t** or int *. */ int *counter; } t_complementary; /* You can set applet_long_options for parse called long options */ #if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG static const struct option bb_null_long_options[1] = { { 0, 0, 0, 0 } }; const char *applet_long_options; #endif uint32_t option_mask32; uint32_t FAST_FUNC getopt32(char **argv, const char *applet_opts, ...) { int argc; unsigned flags = 0; unsigned requires = 0; t_complementary complementary[33]; /* last stays zero-filled */ char first_char; int c; const unsigned char *s; t_complementary *on_off; va_list p; #if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG const struct option *l_o; struct option *long_options = (struct option *) &bb_null_long_options; #endif unsigned trigger; char **pargv; int min_arg = 0; int max_arg = -1; #define SHOW_USAGE_IF_ERROR 1 #define ALL_ARGV_IS_OPTS 2 #define FIRST_ARGV_IS_OPT 4 int spec_flgs = 0; /* skip 0: some applets cheat: they do not actually HAVE argv[0] */ argc = 1; while (argv[argc]) argc++; va_start(p, applet_opts); c = 0; on_off = complementary; memset(on_off, 0, sizeof(complementary)); /* skip bbox extension */ first_char = applet_opts[0]; if (first_char == '!') applet_opts++; /* skip GNU extension */ s = (const unsigned char *)applet_opts; if (*s == '+' || *s == '-') s++; while (*s) { if (c >= 32) break; on_off->opt_char = *s; on_off->switch_on = (1 << c); if (*++s == ':') { on_off->optarg = va_arg(p, void **); while (*++s == ':') continue; } on_off++; c++; } #if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG if (applet_long_options) { const char *optstr; unsigned i, count; count = 1; optstr = applet_long_options; while (optstr[0]) { optstr += strlen(optstr) + 3; /* skip NUL, has_arg, val */ count++; } /* count == no. of longopts + 1 */ long_options = alloca(count * sizeof(*long_options)); memset(long_options, 0, count * sizeof(*long_options)); i = 0; optstr = applet_long_options; while (--count) { long_options[i].name = optstr; optstr += strlen(optstr) + 1; long_options[i].has_arg = (unsigned char)(*optstr++); /* long_options[i].flag = NULL; */ long_options[i].val = (unsigned char)(*optstr++); i++; } for (l_o = long_options; l_o->name; l_o++) { if (l_o->flag) continue; for (on_off = complementary; on_off->opt_char; on_off++) if (on_off->opt_char == l_o->val) goto next_long; if (c >= 32) break; on_off->opt_char = l_o->val; on_off->switch_on = (1 << c); if (l_o->has_arg != no_argument) on_off->optarg = va_arg(p, void **); c++; next_long: ; } /* Make it unnecessary to clear applet_long_options * by hand after each call to getopt32 */ applet_long_options = NULL; } #endif /* ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG */ for (s = (const unsigned char *)opt_complementary; s && *s; s++) { t_complementary *pair; unsigned *pair_switch; if (*s == ':') continue; c = s[1]; if (*s == '?') { if (c < '0' || c > '9') { spec_flgs |= SHOW_USAGE_IF_ERROR; } else { max_arg = c - '0'; s++; } continue; } if (*s == '-') { if (c < '0' || c > '9') { if (c == '-') { spec_flgs |= FIRST_ARGV_IS_OPT; s++; } else spec_flgs |= ALL_ARGV_IS_OPTS; } else { min_arg = c - '0'; s++; } continue; } if (*s == '=') { min_arg = max_arg = c - '0'; s++; continue; } for (on_off = complementary; on_off->opt_char; on_off++) if (on_off->opt_char == *s) goto found_opt; /* Without this, diagnostic of such bugs is not easy */ bb_error_msg_and_die("NO OPT %c!", *s); found_opt: if (c == ':' && s[2] == ':') { on_off->param_type = PARAM_LIST; continue; } if (c == '+' && (s[2] == ':' || s[2] == '\0')) { on_off->param_type = PARAM_INT; s++; continue; } if (c == ':' || c == '\0') { requires |= on_off->switch_on; continue; } if (c == '-' && (s[2] == ':' || s[2] == '\0')) { flags |= on_off->switch_on; on_off->incongruously |= on_off->switch_on; s++; continue; } if (c == *s) { on_off->counter = va_arg(p, int *); s++; } pair = on_off; pair_switch = &pair->switch_on; for (s++; *s && *s != ':'; s++) { if (*s == '?') { pair_switch = &pair->requires; } else if (*s == '-') { if (pair_switch == &pair->switch_off) pair_switch = &pair->incongruously; else pair_switch = &pair->switch_off; } else { for (on_off = complementary; on_off->opt_char; on_off++) if (on_off->opt_char == *s) { *pair_switch |= on_off->switch_on; break; } } } s--; } opt_complementary = NULL; va_end(p); if (spec_flgs & (FIRST_ARGV_IS_OPT | ALL_ARGV_IS_OPTS)) { pargv = argv + 1; while (*pargv) { if (pargv[0][0] != '-' && pargv[0][0] != '\0') { /* Can't use alloca: opts with params will * return pointers to stack! * NB: we leak these allocations... */ char *pp = xmalloc(strlen(*pargv) + 2); *pp = '-'; strcpy(pp + 1, *pargv); *pargv = pp; } if (!(spec_flgs & ALL_ARGV_IS_OPTS)) break; pargv++; } } /* In case getopt32 was already called: * reset the libc getopt() function, which keeps internal state. * run_nofork_applet() does this, but we might end up here * also via gunzip_main() -> gzip_main(). Play safe. */ #ifdef __GLIBC__ optind = 0; #else /* BSD style */ optind = 1; /* optreset = 1; */ #endif /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */ /* Note: just "getopt() <= 0" will not work well for * "fake" short options, like this one: * wget $'-\203' "Test: test" http://kernel.org/ * (supposed to act as --header, but doesn't) */ #if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG while ((c = getopt_long(argc, argv, applet_opts, long_options, NULL)) != -1) { #else while ((c = getopt(argc, argv, applet_opts)) != -1) { #endif /* getopt prints "option requires an argument -- X" * and returns '?' if an option has no arg, but one is reqd */ c &= 0xff; /* fight libc's sign extension */ for (on_off = complementary; on_off->opt_char != c; on_off++) { /* c can be NUL if long opt has non-NULL ->flag, * but we construct long opts so that flag * is always NULL (see above) */ if (on_off->opt_char == '\0' /* && c != '\0' */) { /* c is probably '?' - "bad option" */ goto error; } } if (flags & on_off->incongruously) goto 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 (optarg) { if (on_off->param_type == PARAM_LIST) { llist_add_to_end((llist_t **)(on_off->optarg), optarg); } else if (on_off->param_type == PARAM_INT) { //TODO: xatoi_positive indirectly pulls in printf machinery *(unsigned*)(on_off->optarg) = xatoi_positive(optarg); } else if (on_off->optarg) { *(char **)(on_off->optarg) = optarg; } } } /* check depending requires for given options */ for (on_off = complementary; on_off->opt_char; on_off++) { if (on_off->requires && (flags & on_off->switch_on) && (flags & on_off->requires) == 0 ) { goto error; } } if (requires && (flags & requires) == 0) goto error; argc -= optind; if (argc < min_arg || (max_arg >= 0 && argc > max_arg)) goto error; option_mask32 = flags; return flags; error: if (first_char != '!') bb_show_usage(); return (int32_t)-1; } busybox-1.22.1/libbb/xfunc_die.c0000644000000000000000000000206112263563520015153 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 2008 by Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* Keeping it separate allows to NOT suck in stdio for VERY small applets. * Try building busybox with only "true" enabled... */ #include "libbb.h" int die_sleep; #if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_HUSH jmp_buf die_jmp; #endif void FAST_FUNC xfunc_die(void) { if (die_sleep) { if ((ENABLE_FEATURE_PREFER_APPLETS || ENABLE_HUSH) && die_sleep < 0 ) { /* Special case. We arrive here if NOFORK applet * calls xfunc, which then decides to die. * We don't die, but jump instead back to caller. * NOFORK applets still cannot carelessly call xfuncs: * p = xmalloc(10); * q = xmalloc(10); // BUG! if this dies, we leak p! */ /* -2222 means "zero" (longjmp can't pass 0) * run_nofork_applet() catches -2222. */ longjmp(die_jmp, xfunc_error_retval ? xfunc_error_retval : -2222); } sleep(die_sleep); } exit(xfunc_error_retval); } busybox-1.22.1/libbb/in_ether.c0000644000000000000000000000216012263563520015004 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. */ //kbuild:lib-$(CONFIG_ARP) += in_ether.o //kbuild:lib-$(CONFIG_IFCONFIG) += in_ether.o //kbuild:lib-$(CONFIG_IFENSLAVE) += in_ether.o #include "libbb.h" #include #include /* Convert Ethernet address from "XX[:]XX[:]XX[:]XX[:]XX[:]XX" to sockaddr. * Return nonzero on error. */ int FAST_FUNC in_ether(const char *bufp, struct sockaddr *sap) { char *ptr; int i, j; unsigned char val; unsigned char c; sap->sa_family = ARPHRD_ETHER; ptr = (char *) sap->sa_data; i = ETH_ALEN; goto first; do { /* We might get a semicolon here */ if (*bufp == ':') bufp++; first: j = val = 0; do { c = *bufp; if (((unsigned char)(c - '0')) <= 9) { c -= '0'; } else if ((unsigned char)((c|0x20) - 'a') <= 5) { c = (unsigned char)((c|0x20) - 'a') + 10; } else { if (j && (c == ':' || c == '\0')) /* One-digit byte: __:X:__ */ break; return -1; } ++bufp; val <<= 4; val += c; j ^= 1; } while (j); *ptr++ = val; } while (--i); /* Error if we aren't at end of string */ return *bufp; } busybox-1.22.1/libbb/skip_whitespace.c0000644000000000000000000000165112263563520016375 0ustar rootroot/* 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 source tree. */ #include "libbb.h" char* FAST_FUNC skip_whitespace(const char *s) { /* In POSIX/C locale (the only locale we care about: do we REALLY want * to allow Unicode whitespace in, say, .conf files? nuts!) * isspace is only these chars: "\t\n\v\f\r" and space. * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13. * Use that. */ while (*s == ' ' || (unsigned char)(*s - 9) <= (13 - 9)) s++; return (char *) s; } char* FAST_FUNC skip_non_whitespace(const char *s) { while (*s != '\0' && *s != ' ' && (unsigned char)(*s - 9) > (13 - 9)) s++; return (char *) s; } char* FAST_FUNC skip_dev_pfx(const char *tty_name) { if (strncmp(tty_name, "/dev/", 5) == 0) tty_name += 5; return (char*)tty_name; } busybox-1.22.1/libbb/full_write.c0000644000000000000000000000146212263563520015367 0ustar rootroot/* 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 source tree. */ #include "libbb.h" /* * Write all of the supplied buffer out to a file. * This does multiple writes as necessary. * Returns the amount written, or -1 on an error. */ ssize_t FAST_FUNC full_write(int fd, const void *buf, size_t len) { ssize_t cc; ssize_t total; total = 0; while (len) { cc = safe_write(fd, buf, len); if (cc < 0) { if (total) { /* we already wrote some! */ /* user can do another write to know the error code */ return total; } return cc; /* write() returns -1 on failure. */ } total += cc; buf = ((const char *)buf) + cc; len -= cc; } return total; } busybox-1.22.1/libbb/xrealloc_vector.c0000644000000000000000000000276612263563520016416 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 2008 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" /* Resize (grow) malloced vector. * * #define magic packed two parameters into one: * sizeof = sizeof_and_shift >> 8 * shift = (sizeof_and_shift) & 0xff * * Lets say shift = 4. 1 << 4 == 0x10. * If idx == 0, 0x10, 0x20 etc, vector[] is resized to next higher * idx step, plus one: if idx == 0x20, vector[] is resized to 0x31, * thus last usable element is vector[0x30]. * * In other words: after xrealloc_vector(v, 4, idx), with any idx, * it's ok to use at least v[idx] and v[idx+1]. * v[idx+2] etc generally are not ok. * * New elements are zeroed out, but only if realloc was done * (not on every call). You can depend on v[idx] and v[idx+1] being * zeroed out if you use it like this: * v = xrealloc_vector(v, 4, idx); * v[idx].some_fields = ...; - the rest stays 0/NULL * idx++; * If you do not advance idx like above, you should be more careful. * Next call to xrealloc_vector(v, 4, idx) may or may not zero out v[idx]. */ void* FAST_FUNC xrealloc_vector_helper(void *vector, unsigned sizeof_and_shift, int idx) { int mask = 1 << (uint8_t)sizeof_and_shift; if (!(idx & (mask - 1))) { sizeof_and_shift >>= 8; /* sizeof(vector[0]) */ vector = xrealloc(vector, sizeof_and_shift * (idx + mask + 1)); memset((char*)vector + (sizeof_and_shift * idx), 0, sizeof_and_shift * (mask + 1)); } return vector; } busybox-1.22.1/libbb/single_argv.c0000644000000000000000000000052412263563520015511 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) 2009 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" char* FAST_FUNC single_argv(char **argv) { if (argv[1] && strcmp(argv[1], "--") == 0) argv++; if (!argv[1] || argv[2]) bb_show_usage(); return argv[1]; } busybox-1.22.1/libbb/bb_strtod.c0000644000000000000000000000372112263563520015175 0ustar rootroot/* 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 source tree. */ #include "libbb.h" #include /* just for HUGE_VAL */ #define NOT_DIGIT(a) (((unsigned char)(a-'0')) > 9) #if 0 // UNUSED double FAST_FUNC bb_strtod(const char *arg, char **endp) { double v; char *endptr; /* Allow .NN form. People want to use "sleep .15" etc */ if (arg[0] != '-' && arg[0] != '.' && NOT_DIGIT(arg[0])) goto err; errno = 0; v = strtod(arg, &endptr); if (endp) *endp = endptr; if (endptr[0]) { /* "1234abcg" or out-of-range? */ if (isalnum(endptr[0]) || errno) { err: errno = ERANGE; return HUGE_VAL; } /* good number, just suspicious terminator */ errno = EINVAL; } return v; } #endif #if 0 /* String to timespec: "NNNN[.NNNNN]" -> struct timespec. * Can be used for other fixed-point needs. * Returns pointer past last converted char, * and returns errno similar to bb_strtoXX functions. */ char* FAST_FUNC bb_str_to_ts(struct timespec *ts, const char *arg) { if (sizeof(ts->tv_sec) <= sizeof(int)) ts->tv_sec = bb_strtou(arg, &arg, 10); else if (sizeof(ts->tv_sec) <= sizeof(long)) ts->tv_sec = bb_strtoul(arg, &arg, 10); else ts->tv_sec = bb_strtoull(arg, &arg, 10); ts->tv_nsec = 0; if (*arg != '.') return arg; /* !EINVAL: number is not ok (alphanumeric ending, overflow etc) */ if (errno != EINVAL) return arg; if (!*++arg) /* "NNN." */ return arg; { /* "NNN.xxx" - parse xxx */ int ndigits; char *p; char buf[10]; /* we never use more than 9 digits */ /* Need to make a copy to avoid false overflow */ safe_strncpy(buf, arg, 10); ts->tv_nsec = bb_strtou(buf, &p, 10); ndigits = p - buf; arg += ndigits; /* normalize to nsec */ while (ndigits < 9) { ndigits++; ts->tv_nsec *= 10; } while (isdigit(*arg)) /* skip possible 10th plus digits */ arg++; } return arg; } #endif busybox-1.22.1/libbb/perror_nomsg_and_die.c0000644000000000000000000000122112263563520017363 0ustar rootroot/* 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 source tree. */ /* gcc warns about a null format string, therefore we provide * modified definition without "attribute (format)" * instead of including libbb.h */ //#include "libbb.h" #include "platform.h" extern void bb_perror_msg_and_die(const char *s, ...) FAST_FUNC; /* suppress gcc "no previous prototype" warning */ void FAST_FUNC bb_perror_nomsg_and_die(void); void FAST_FUNC bb_perror_nomsg_and_die(void) { bb_perror_msg_and_die(0); } busybox-1.22.1/libbb/speed_table.c0000644000000000000000000000376712267106022015466 0ustar rootroot/* 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 source tree. */ #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 #ifdef B921600 {B921600, 921600/256 + 0x8000U}, #endif }; enum { NUM_SPEEDS = ARRAY_SIZE(speeds) }; unsigned FAST_FUNC tty_baud_to_value(speed_t speed) { int i = 0; do { if (speed == speeds[i].speed) { if (speeds[i].value & 0x8000U) { return ((unsigned long) (speeds[i].value) & 0x7fffU) * 256; } return speeds[i].value; } } while (++i < NUM_SPEEDS); return 0; } speed_t FAST_FUNC tty_value_to_baud(unsigned int value) { int i = 0; do { if (value == tty_baud_to_value(speeds[i].speed)) { return speeds[i].speed; } } while (++i < NUM_SPEEDS); return (speed_t) - 1; } #if 0 /* testing code */ #include int main(void) { unsigned long v; speed_t s; for (v = 0 ; v < 1000000; 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 busybox-1.22.1/libbb/xreadlink.c0000644000000000000000000000546212263563520015200 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * xreadlink.c - safe implementation of readlink. * Returns a NULL on failure... * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" /* some systems (eg Hurd) does not have MAXSYMLINKS definition, * set it to some reasonable value if it isn't defined */ #ifndef MAXSYMLINKS # define MAXSYMLINKS 20 #endif /* * NOTE: This function returns a malloced char* that you will have to free * yourself. */ char* FAST_FUNC xmalloc_readlink(const char *path) { enum { GROWBY = 80 }; /* how large we will grow strings by */ char *buf = NULL; int bufsize = 0, readsize = 0; do { bufsize += GROWBY; buf = xrealloc(buf, bufsize); readsize = readlink(path, buf, bufsize); if (readsize == -1) { free(buf); return NULL; } } while (bufsize < readsize + 1); buf[readsize] = '\0'; return buf; } /* * This routine is not the same as realpath(), which * canonicalizes the given path completely. This routine only * follows trailing symlinks until a real file is reached and * returns its name. If the path ends in a dangling link or if * the target doesn't exist, the path is returned in any case. * Intermediate symlinks in the path are not expanded -- only * those at the tail. * A malloced char* is returned, which must be freed by the caller. */ char* FAST_FUNC xmalloc_follow_symlinks(const char *path) { char *buf; char *lpc; char *linkpath; int bufsize; int looping = MAXSYMLINKS + 1; buf = xstrdup(path); goto jump_in; while (1) { linkpath = xmalloc_readlink(buf); if (!linkpath) { /* not a symlink, or doesn't exist */ if (errno == EINVAL || errno == ENOENT) return buf; goto free_buf_ret_null; } if (!--looping) { free(linkpath); free_buf_ret_null: free(buf); return NULL; } if (*linkpath != '/') { bufsize += strlen(linkpath); buf = xrealloc(buf, bufsize); lpc = bb_get_last_path_component_strip(buf); strcpy(lpc, linkpath); free(linkpath); } else { free(buf); buf = linkpath; jump_in: bufsize = strlen(buf) + 1; } } } char* FAST_FUNC xmalloc_readlink_or_warn(const char *path) { char *buf = xmalloc_readlink(path); if (!buf) { /* EINVAL => "file: Invalid argument" => puzzled user */ const char *errmsg = "not a symlink"; int err = errno; if (err != EINVAL) errmsg = strerror(err); bb_error_msg("%s: cannot read link: %s", path, errmsg); } return buf; } char* FAST_FUNC xmalloc_realpath(const char *path) { #if defined(__GLIBC__) || \ (defined(__UCLIBC__) && UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 31)) /* glibc provides a non-standard extension */ /* new: POSIX.1-2008 specifies this behavior as well */ return realpath(path, NULL); #else char buf[PATH_MAX+1]; /* on error returns NULL (xstrdup(NULL) == NULL) */ return xstrdup(realpath(path, buf)); #endif } busybox-1.22.1/libbb/correct_password.c0000644000000000000000000000623012263563520016574 0ustar rootroot/* 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 without asking if PW has an empty password. * Return -1 on EOF, error while reading input, or timeout. * Return 1 if the user gives the correct password for entry PW, * 0 if not. * * NULL pw means "just fake it for login with bad username" */ int FAST_FUNC ask_and_check_password_extended(const struct passwd *pw, int timeout, const char *prompt) { char *unencrypted, *encrypted; const char *correct; int r; /* fake salt. crypt() can choke otherwise. */ correct = "aa"; if (!pw) { /* "aa" will never match */ goto fake_it; } correct = pw->pw_passwd; #if ENABLE_FEATURE_SHADOWPASSWDS /* Using _r function to avoid pulling in static buffers */ if ((correct[0] == 'x' || correct[0] == '*') && !correct[1]) { struct spwd spw; char buffer[256]; /* getspnam_r may return 0 yet set result to NULL. * At least glibc 2.4 does this. Be extra paranoid here. */ struct spwd *result = NULL; r = getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result); correct = (r || !result) ? "aa" : result->sp_pwdp; } #endif if (!correct[0]) /* empty password field? */ return 1; fake_it: unencrypted = bb_ask(STDIN_FILENO, timeout, prompt); if (!unencrypted) { /* EOF (such as ^D) or error (such as ^C) or timeout */ return -1; } encrypted = pw_encrypt(unencrypted, correct, 1); r = (strcmp(encrypted, correct) == 0); free(encrypted); nuke_str(unencrypted); return r; } int FAST_FUNC ask_and_check_password(const struct passwd *pw) { return ask_and_check_password_extended(pw, 0, "Password: "); } busybox-1.22.1/libbb/rtc.c0000644000000000000000000000254012263563520014001 0ustar rootroot/* * Common RTC functions * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" #include "rtc_.h" #if ENABLE_FEATURE_HWCLOCK_ADJTIME_FHS # define ADJTIME_PATH "/var/lib/hwclock/adjtime" #else # define ADJTIME_PATH "/etc/adjtime" #endif int FAST_FUNC rtc_adjtime_is_utc(void) { int utc = 0; FILE *f = fopen_for_read(ADJTIME_PATH); if (f) { char buffer[128]; while (fgets(buffer, sizeof(buffer), f)) { if (strncmp(buffer, "UTC", 3) == 0) { utc = 1; break; } } fclose(f); } return utc; } int FAST_FUNC rtc_xopen(const char **default_rtc, int flags) { int rtc; if (!*default_rtc) { *default_rtc = "/dev/rtc"; rtc = open(*default_rtc, flags); if (rtc >= 0) return rtc; *default_rtc = "/dev/rtc0"; rtc = open(*default_rtc, flags); if (rtc >= 0) return rtc; *default_rtc = "/dev/misc/rtc"; } return xopen(*default_rtc, flags); } void FAST_FUNC rtc_read_tm(struct tm *ptm, int fd) { memset(ptm, 0, sizeof(*ptm)); xioctl(fd, RTC_RD_TIME, ptm); ptm->tm_isdst = -1; /* "not known" */ } time_t FAST_FUNC rtc_tm2time(struct tm *ptm, int utc) { char *oldtz = oldtz; /* for compiler */ time_t t; if (utc) { oldtz = getenv("TZ"); putenv((char*)"TZ=UTC0"); tzset(); } t = mktime(ptm); if (utc) { unsetenv("TZ"); if (oldtz) putenv(oldtz - 3); tzset(); } return t; } busybox-1.22.1/miscutils/0000755000000000000000000000000012320365364014006 5ustar rootrootbusybox-1.22.1/miscutils/ubi_tools.c0000644000000000000000000002410712263563520016155 0ustar rootroot/* Ported to busybox from mtd-utils. * * Licensed under GPLv2, see file LICENSE in this source tree. */ //config:config UBIATTACH //config: bool "ubiattach" //config: default y //config: select PLATFORM_LINUX //config: help //config: Attach MTD device to an UBI device. //config: //config:config UBIDETACH //config: bool "ubidetach" //config: default y //config: select PLATFORM_LINUX //config: help //config: Detach MTD device from an UBI device. //config: //config:config UBIMKVOL //config: bool "ubimkvol" //config: default y //config: select PLATFORM_LINUX //config: help //config: Create a UBI volume. //config: //config:config UBIRMVOL //config: bool "ubirmvol" //config: default y //config: select PLATFORM_LINUX //config: help //config: Delete a UBI volume. //config: //config:config UBIRSVOL //config: bool "ubirsvol" //config: default y //config: select PLATFORM_LINUX //config: help //config: Resize a UBI volume. //config: //config:config UBIUPDATEVOL //config: bool "ubiupdatevol" //config: default y //config: select PLATFORM_LINUX //config: help //config: Update a UBI volume. //applet:IF_UBIATTACH(APPLET_ODDNAME(ubiattach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiattach)) //applet:IF_UBIDETACH(APPLET_ODDNAME(ubidetach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubidetach)) //applet:IF_UBIMKVOL(APPLET_ODDNAME(ubimkvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubimkvol)) //applet:IF_UBIRMVOL(APPLET_ODDNAME(ubirmvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirmvol)) //applet:IF_UBIRSVOL(APPLET_ODDNAME(ubirsvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirsvol)) //applet:IF_UBIUPDATEVOL(APPLET_ODDNAME(ubiupdatevol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiupdatevol)) //kbuild:lib-$(CONFIG_UBIATTACH) += ubi_tools.o //kbuild:lib-$(CONFIG_UBIDETACH) += ubi_tools.o //kbuild:lib-$(CONFIG_UBIMKVOL) += ubi_tools.o //kbuild:lib-$(CONFIG_UBIRMVOL) += ubi_tools.o //kbuild:lib-$(CONFIG_UBIRSVOL) += ubi_tools.o //kbuild:lib-$(CONFIG_UBIUPDATEVOL) += ubi_tools.o #include "libbb.h" /* Some versions of kernel have broken headers, need this hack */ #ifndef __packed # define __packed __attribute__((packed)) #endif #include #define do_attach (ENABLE_UBIATTACH && applet_name[3] == 'a') #define do_detach (ENABLE_UBIDETACH && applet_name[3] == 'd') #define do_mkvol (ENABLE_UBIMKVOL && applet_name[3] == 'm') #define do_rmvol (ENABLE_UBIRMVOL && applet_name[4] == 'm') #define do_rsvol (ENABLE_UBIRSVOL && applet_name[4] == 's') #define do_update (ENABLE_UBIUPDATEVOL && applet_name[3] == 'u') static unsigned get_num_from_file(const char *path, unsigned max, const char *errmsg) { char buf[sizeof(long long)*3]; unsigned long long num; if (open_read_close(path, buf, sizeof(buf)) < 0) bb_perror_msg_and_die(errmsg, path); /* It can be \n terminated, xatoull won't work well */ if (sscanf(buf, "%llu", &num) != 1 || num > max) bb_error_msg_and_die(errmsg, path); return num; } /* To prevent malloc(1G) accidents */ #define MAX_SANE_ERASEBLOCK (16*1024*1024) int ubi_tools_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ubi_tools_main(int argc UNUSED_PARAM, char **argv) { static const struct suffix_mult size_suffixes[] = { { "KiB", 1024 }, { "MiB", 1024*1024 }, { "GiB", 1024*1024*1024 }, { "", 0 } }; unsigned opts; char *ubi_ctrl; int fd; int mtd_num; int dev_num = UBI_DEV_NUM_AUTO; int vol_id = UBI_VOL_NUM_AUTO; char *vol_name; unsigned long long size_bytes = size_bytes; /* for compiler */ char *size_bytes_str; int alignment = 1; char *type; union { struct ubi_attach_req attach_req; struct ubi_mkvol_req mkvol_req; struct ubi_rsvol_req rsvol_req; } req_structs; #define attach_req req_structs.attach_req #define mkvol_req req_structs.mkvol_req #define rsvol_req req_structs.rsvol_req char path[sizeof("/sys/class/ubi/ubi%d_%d/usable_eb_size") + 2 * sizeof(int)*3 + /*just in case:*/ 16]; #define path_sys_class_ubi_ubi (path + sizeof("/sys/class/ubi/ubi")-1) strcpy(path, "/sys/class/ubi/ubi"); memset(&req_structs, 0, sizeof(req_structs)); if (do_mkvol) { opt_complementary = "-1:d+:n+:a+"; opts = getopt32(argv, "md:n:N:s:a:t:", &dev_num, &vol_id, &vol_name, &size_bytes_str, &alignment, &type ); } else { opt_complementary = "-1:m+:d+:n+:a+"; opts = getopt32(argv, "m:d:n:N:s:a:t:", &mtd_num, &dev_num, &vol_id, &vol_name, &size_bytes_str, &alignment, &type ); } #define OPTION_m (1 << 0) #define OPTION_d (1 << 1) #define OPTION_n (1 << 2) #define OPTION_N (1 << 3) #define OPTION_s (1 << 4) #define OPTION_a (1 << 5) #define OPTION_t (1 << 6) if (opts & OPTION_s) size_bytes = xatoull_sfx(size_bytes_str, size_suffixes); argv += optind; ubi_ctrl = *argv++; fd = xopen(ubi_ctrl, O_RDWR); //xfstat(fd, &st, ubi_ctrl); //if (!S_ISCHR(st.st_mode)) // bb_error_msg_and_die("%s: not a char device", ubi_ctrl); //usage:#define ubiattach_trivial_usage //usage: "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV" //usage:#define ubiattach_full_usage "\n\n" //usage: "Attach MTD device to UBI\n" //usage: "\n -m MTD_NUM MTD device number to attach" //usage: "\n -d UBI_NUM UBI device number to assign" if (do_attach) { if (!(opts & OPTION_m)) bb_error_msg_and_die("%s device not specified", "MTD"); attach_req.mtd_num = mtd_num; attach_req.ubi_num = dev_num; xioctl(fd, UBI_IOCATT, &attach_req); } else //usage:#define ubidetach_trivial_usage //usage: "-d UBI_NUM UBI_CTRL_DEV" //usage:#define ubidetach_full_usage "\n\n" //usage: "Detach MTD device from UBI\n" //usage: "\n -d UBI_NUM UBI device number" if (do_detach) { if (!(opts & OPTION_d)) bb_error_msg_and_die("%s device not specified", "UBI"); /* FIXME? kernel expects int32_t* here: */ xioctl(fd, UBI_IOCDET, &dev_num); } else //usage:#define ubimkvol_trivial_usage //usage: "UBI_DEVICE -N NAME [-s SIZE | -m]" //usage:#define ubimkvol_full_usage "\n\n" //usage: "Create UBI volume\n" //usage: "\n -a ALIGNMENT Volume alignment (default 1)" //usage: "\n -m Set volume size to maximum available" //usage: "\n -n VOLID Volume ID. If not specified," //usage: "\n assigned automatically" //usage: "\n -N NAME Volume name" //usage: "\n -s SIZE Size in bytes" //usage: "\n -t TYPE Volume type (static|dynamic)" if (do_mkvol) { if (opts & OPTION_m) { unsigned leb_avail; unsigned leb_size; unsigned num; char *p; if (sscanf(ubi_ctrl, "/dev/ubi%u", &num) != 1) bb_error_msg_and_die("wrong format of UBI device name"); p = path_sys_class_ubi_ubi + sprintf(path_sys_class_ubi_ubi, "%u/", num); strcpy(p, "avail_eraseblocks"); leb_avail = get_num_from_file(path, UINT_MAX, "Can't get available eraseblocks from '%s'"); strcpy(p, "eraseblock_size"); leb_size = get_num_from_file(path, MAX_SANE_ERASEBLOCK, "Can't get eraseblock size from '%s'"); size_bytes = leb_avail * (unsigned long long)leb_size; //if (size_bytes <= 0) // bb_error_msg_and_die("%s invalid maximum size calculated", "UBI"); } else if (!(opts & OPTION_s)) bb_error_msg_and_die("size not specified"); if (!(opts & OPTION_N)) bb_error_msg_and_die("name not specified"); mkvol_req.vol_id = vol_id; mkvol_req.vol_type = UBI_DYNAMIC_VOLUME; if ((opts & OPTION_t) && type[0] == 's') mkvol_req.vol_type = UBI_STATIC_VOLUME; mkvol_req.alignment = alignment; mkvol_req.bytes = size_bytes; /* signed int64_t */ strncpy(mkvol_req.name, vol_name, UBI_MAX_VOLUME_NAME); mkvol_req.name_len = strlen(vol_name); if (mkvol_req.name_len > UBI_MAX_VOLUME_NAME) bb_error_msg_and_die("volume name too long: '%s'", vol_name); xioctl(fd, UBI_IOCMKVOL, &mkvol_req); } else //usage:#define ubirmvol_trivial_usage //usage: "UBI_DEVICE -n VOLID" //usage:#define ubirmvol_full_usage "\n\n" //usage: "Remove UBI volume\n" //usage: "\n -n VOLID Volume ID" if (do_rmvol) { if (!(opts & OPTION_n)) bb_error_msg_and_die("volume id not specified"); /* FIXME? kernel expects int32_t* here: */ xioctl(fd, UBI_IOCRMVOL, &vol_id); } else //usage:#define ubirsvol_trivial_usage //usage: "UBI_DEVICE -n VOLID -s SIZE" //usage:#define ubirsvol_full_usage "\n\n" //usage: "Resize UBI volume\n" //usage: "\n -n VOLID Volume ID" //usage: "\n -s SIZE Size in bytes" if (do_rsvol) { if (!(opts & OPTION_s)) bb_error_msg_and_die("size not specified"); if (!(opts & OPTION_n)) bb_error_msg_and_die("volume id not specified"); rsvol_req.bytes = size_bytes; /* signed int64_t */ rsvol_req.vol_id = vol_id; xioctl(fd, UBI_IOCRSVOL, &rsvol_req); } else //usage:#define ubiupdatevol_trivial_usage //usage: "UBI_DEVICE [-t | [-s SIZE] IMG_FILE]" //usage:#define ubiupdatevol_full_usage "\n\n" //usage: "Update UBI volume\n" //usage: "\n -t Truncate to zero size" //usage: "\n -s SIZE Size in bytes to resize to" if (do_update) { int64_t bytes64; if (opts & OPTION_t) { /* truncate the volume by starting an update for size 0 */ bytes64 = 0; /* this ioctl expects int64_t* parameter */ xioctl(fd, UBI_IOCVOLUP, &bytes64); } else { struct stat st; unsigned ubinum, volnum; unsigned leb_size; ssize_t len; char *input_data; /* Assume that device is in normal format. */ /* Removes need for scanning sysfs tree as full libubi does. */ if (sscanf(ubi_ctrl, "/dev/ubi%u_%u", &ubinum, &volnum) != 2) bb_error_msg_and_die("wrong format of UBI device name"); sprintf(path_sys_class_ubi_ubi, "%u_%u/usable_eb_size", ubinum, volnum); leb_size = get_num_from_file(path, MAX_SANE_ERASEBLOCK, "Can't get usable eraseblock size from '%s'"); if (!(opts & OPTION_s)) { if (!*argv) bb_show_usage(); xstat(*argv, &st); size_bytes = st.st_size; xmove_fd(xopen(*argv, O_RDONLY), STDIN_FILENO); } bytes64 = size_bytes; /* this ioctl expects signed int64_t* parameter */ xioctl(fd, UBI_IOCVOLUP, &bytes64); input_data = xmalloc(leb_size); while ((len = full_read(STDIN_FILENO, input_data, leb_size)) > 0) { xwrite(fd, input_data, len); } if (len < 0) bb_perror_msg_and_die("UBI volume update failed"); } } if (ENABLE_FEATURE_CLEAN_UP) close(fd); return EXIT_SUCCESS; } busybox-1.22.1/miscutils/ionice.c0000644000000000000000000000470512263563520015426 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * ionice implementation for busybox based on linux-utils-ng 2.14 * * Copyright (C) 2008 by * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define ionice_trivial_usage //usage: "[-c 1-3] [-n 0-7] [-p PID] [PROG]" //usage:#define ionice_full_usage "\n\n" //usage: "Change I/O priority and class\n" //usage: "\n -c Class. 1:realtime 2:best-effort 3:idle" //usage: "\n -n Priority" #include #include #include "libbb.h" static int ioprio_set(int which, int who, int ioprio) { return syscall(SYS_ioprio_set, which, who, ioprio); } static int ioprio_get(int which, int who) { return syscall(SYS_ioprio_get, which, who); } enum { IOPRIO_WHO_PROCESS = 1, IOPRIO_WHO_PGRP, IOPRIO_WHO_USER }; enum { IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE }; static const char to_prio[] = "none\0realtime\0best-effort\0idle"; #define IOPRIO_CLASS_SHIFT 13 int ionice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ionice_main(int argc UNUSED_PARAM, char **argv) { /* Defaults */ int ioclass = 0; int pri = 0; int pid = 0; /* affect own porcess */ int opt; enum { OPT_n = 1, OPT_c = 2, OPT_p = 4, }; /* Numeric params */ opt_complementary = "n+:c+:p+"; /* '+': stop at first non-option */ opt = getopt32(argv, "+n:c:p:", &pri, &ioclass, &pid); argv += optind; if (opt & OPT_c) { if (ioclass > 3) bb_error_msg_and_die("bad class %d", ioclass); // Do we need this (compat?)? // if (ioclass == IOPRIO_CLASS_NONE) // ioclass = IOPRIO_CLASS_BE; // if (ioclass == IOPRIO_CLASS_IDLE) { // //if (opt & OPT_n) // // bb_error_msg("ignoring priority for idle class"); // pri = 7; // } } if (!(opt & (OPT_n|OPT_c))) { if (!(opt & OPT_p) && *argv) pid = xatoi_positive(*argv); pri = ioprio_get(IOPRIO_WHO_PROCESS, pid); if (pri == -1) bb_perror_msg_and_die("ioprio_%cet", 'g'); ioclass = (pri >> IOPRIO_CLASS_SHIFT) & 0x3; pri &= 0xff; printf((ioclass == IOPRIO_CLASS_IDLE) ? "%s\n" : "%s: prio %d\n", nth_string(to_prio, ioclass), pri); } else { //printf("pri=%d class=%d val=%x\n", //pri, ioclass, pri | (ioclass << IOPRIO_CLASS_SHIFT)); pri |= (ioclass << IOPRIO_CLASS_SHIFT); if (ioprio_set(IOPRIO_WHO_PROCESS, pid, pri) == -1) bb_perror_msg_and_die("ioprio_%cet", 's'); if (argv[0]) { BB_EXECVP_or_die(argv); } } return EXIT_SUCCESS; } busybox-1.22.1/miscutils/mountpoint.c0000644000000000000000000000507412263563520016374 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * mountpoint implementation for busybox * * Copyright (C) 2005 Bernhard Reutner-Fischer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Based on sysvinit's mountpoint */ //usage:#define mountpoint_trivial_usage //usage: "[-q] <[-dn] DIR | -x DEVICE>" //usage:#define mountpoint_full_usage "\n\n" //usage: "Check if the directory is a mountpoint\n" //usage: "\n -q Quiet" //usage: "\n -d Print major/minor device number of the filesystem" //usage: "\n -n Print device name of the filesystem" //usage: "\n -x Print major/minor device number of the blockdevice" //usage: //usage:#define mountpoint_example_usage //usage: "$ mountpoint /proc\n" //usage: "/proc is not a mountpoint\n" //usage: "$ mountpoint /sys\n" //usage: "/sys is a mountpoint\n" #include "libbb.h" int mountpoint_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mountpoint_main(int argc UNUSED_PARAM, char **argv) { struct stat st; const char *msg; char *arg; int rc, opt; opt_complementary = "=1"; /* must have one argument */ opt = getopt32(argv, "qdxn"); #define OPT_q (1) #define OPT_d (2) #define OPT_x (4) #define OPT_n (8) arg = argv[optind]; msg = "%s"; rc = (opt & OPT_x) ? stat(arg, &st) : lstat(arg, &st); if (rc != 0) goto err; if (opt & OPT_x) { if (S_ISBLK(st.st_mode)) { printf("%u:%u\n", major(st.st_rdev), minor(st.st_rdev)); return EXIT_SUCCESS; } errno = 0; /* make perror_msg work as error_msg */ msg = "%s: not a block device"; goto err; } errno = ENOTDIR; if (S_ISDIR(st.st_mode)) { dev_t st_dev = st.st_dev; ino_t st_ino = st.st_ino; char *p = xasprintf("%s/..", arg); if (stat(p, &st) == 0) { //int is_mnt = (st_dev != st.st_dev) || (st_dev == st.st_dev && st_ino == st.st_ino); int is_not_mnt = (st_dev == st.st_dev) && (st_ino != st.st_ino); if (opt & OPT_d) printf("%u:%u\n", major(st_dev), minor(st_dev)); if (opt & OPT_n) { const char *d = find_block_device(arg); /* name is undefined, but device is mounted -> anonymous superblock! */ /* happens with btrfs */ if (!d) { d = "UNKNOWN"; /* TODO: iterate /proc/mounts, or /proc/self/mountinfo * to find out the device name */ } printf("%s %s\n", d, arg); } if (!(opt & (OPT_q | OPT_d | OPT_n))) printf("%s is %sa mountpoint\n", arg, is_not_mnt ? "not " : ""); return is_not_mnt; } arg = p; /* else: stat had set errno, just fall through */ } err: if (!(opt & OPT_q)) bb_perror_msg(msg, arg); return EXIT_FAILURE; } busybox-1.22.1/miscutils/crontab.c0000644000000000000000000001157112263563520015607 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * CRONTAB * * usually setuid root, -c option only works if getuid() == geteuid() * * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com) * Vladimir Oleynik (C) 2002 * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define crontab_trivial_usage //usage: "[-c DIR] [-u USER] [-ler]|[FILE]" //usage:#define crontab_full_usage "\n\n" //usage: " -c Crontab directory" //usage: "\n -u User" //usage: "\n -l List crontab" //usage: "\n -e Edit crontab" //usage: "\n -r Delete crontab" //usage: "\n FILE Replace crontab by FILE ('-': stdin)" #include "libbb.h" #define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs" #ifndef CRONUPDATE #define CRONUPDATE "cron.update" #endif static void edit_file(const struct passwd *pas, const char *file) { const char *ptr; pid_t pid; pid = xvfork(); if (pid) { /* parent */ wait4pid(pid); return; } /* CHILD - change user and run editor */ /* initgroups, setgid, setuid */ change_identity(pas); setup_environment(pas->pw_shell, SETUP_ENV_CHANGEENV | SETUP_ENV_TO_TMP, pas); ptr = getenv("VISUAL"); if (!ptr) { ptr = getenv("EDITOR"); if (!ptr) ptr = "vi"; } BB_EXECLP(ptr, ptr, file, NULL); bb_perror_msg_and_die("can't execute '%s'", ptr); } int crontab_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int crontab_main(int argc UNUSED_PARAM, char **argv) { const struct passwd *pas; const char *crontab_dir = CRONTABS; char *tmp_fname; char *new_fname; char *user_name; /* -u USER */ int fd; int src_fd; int opt_ler; /* file [opts] Replace crontab from file * - [opts] Replace crontab from stdin * -u user User * -c dir Crontab directory * -l List crontab for user * -e Edit crontab for user * -r Delete crontab for user * bbox also supports -d == -r, but most other crontab * implementations do not. Deprecated. */ enum { OPT_u = (1 << 0), OPT_c = (1 << 1), OPT_l = (1 << 2), OPT_e = (1 << 3), OPT_r = (1 << 4), OPT_ler = OPT_l + OPT_e + OPT_r, }; opt_complementary = "?1:dr"; /* max one argument; -d implies -r */ opt_ler = getopt32(argv, "u:c:lerd", &user_name, &crontab_dir); argv += optind; if (sanitize_env_if_suid()) { /* Clears dangerous stuff, sets PATH */ /* Run by non-root */ if (opt_ler & (OPT_u|OPT_c)) bb_error_msg_and_die(bb_msg_you_must_be_root); } if (opt_ler & OPT_u) { pas = xgetpwnam(user_name); } else { pas = xgetpwuid(getuid()); } #define user_name DONT_USE_ME_BEYOND_THIS_POINT /* From now on, keep only -l, -e, -r bits */ opt_ler &= OPT_ler; if ((opt_ler - 1) & opt_ler) /* more than one bit set? */ bb_show_usage(); /* Read replacement file under user's UID/GID/group vector */ src_fd = STDIN_FILENO; if (!opt_ler) { /* Replace? */ if (!argv[0]) bb_show_usage(); if (NOT_LONE_DASH(argv[0])) { src_fd = xopen_as_uid_gid(argv[0], O_RDONLY, pas->pw_uid, pas->pw_gid); } } /* cd to our crontab directory */ xchdir(crontab_dir); tmp_fname = NULL; /* Handle requested operation */ switch (opt_ler) { default: /* case OPT_r: Delete */ unlink(pas->pw_name); break; case OPT_l: /* List */ { char *args[2] = { pas->pw_name, NULL }; return bb_cat(args); /* list exits, * the rest go play with cron update file */ } case OPT_e: /* Edit */ tmp_fname = xasprintf("%s.%u", crontab_dir, (unsigned)getpid()); /* No O_EXCL: we don't want to be stuck if earlier crontabs * were killed, leaving stale temp file behind */ src_fd = xopen3(tmp_fname, O_RDWR|O_CREAT|O_TRUNC, 0600); fchown(src_fd, pas->pw_uid, pas->pw_gid); fd = open(pas->pw_name, O_RDONLY); if (fd >= 0) { bb_copyfd_eof(fd, src_fd); close(fd); xlseek(src_fd, 0, SEEK_SET); } close_on_exec_on(src_fd); /* don't want editor to see this fd */ edit_file(pas, tmp_fname); /* fall through */ case 0: /* Replace (no -l, -e, or -r were given) */ new_fname = xasprintf("%s.new", pas->pw_name); fd = open(new_fname, O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 0600); if (fd >= 0) { bb_copyfd_eof(src_fd, fd); close(fd); xrename(new_fname, pas->pw_name); } else { bb_error_msg("can't create %s/%s", crontab_dir, new_fname); } if (tmp_fname) unlink(tmp_fname); /*free(tmp_fname);*/ /*free(new_fname);*/ } /* switch */ /* Bump notification file. Handle window where crond picks file up * before we can write our entry out. */ while ((fd = open(CRONUPDATE, O_WRONLY|O_CREAT|O_APPEND, 0600)) >= 0) { struct stat st; fdprintf(fd, "%s\n", pas->pw_name); if (fstat(fd, &st) != 0 || st.st_nlink != 0) { /*close(fd);*/ break; } /* st.st_nlink == 0: * file was deleted, maybe crond missed our notification */ close(fd); /* loop */ } if (fd < 0) { bb_error_msg("can't append to %s/%s", crontab_dir, CRONUPDATE); } return 0; } busybox-1.22.1/miscutils/last_fancy.c0000644000000000000000000001427612263563520016307 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * (sysvinit like) last implementation * * Copyright (C) 2008 by Patricia Muscalu * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" /* NB: ut_name and ut_user are the same field, use only one name (ut_user) * to reduce confusion */ #ifndef SHUTDOWN_TIME # define SHUTDOWN_TIME 254 #endif #define HEADER_FORMAT "%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n" #define HEADER_LINE "USER", "TTY", \ INET_ADDRSTRLEN, INET_ADDRSTRLEN, "HOST", "LOGIN", " TIME", "" #define HEADER_LINE_WIDE "USER", "TTY", \ INET6_ADDRSTRLEN, INET6_ADDRSTRLEN, "HOST", "LOGIN", " TIME", "" enum { NORMAL, LOGGED, DOWN, REBOOT, CRASH, GONE }; enum { LAST_OPT_W = (1 << 0), /* -W wide */ LAST_OPT_f = (1 << 1), /* -f input file */ LAST_OPT_H = (1 << 2), /* -H header */ }; #define show_wide (option_mask32 & LAST_OPT_W) static void show_entry(struct utmp *ut, int state, time_t dur_secs) { unsigned days, hours, mins; char duration[32]; char login_time[17]; char logout_time[8]; const char *logout_str; const char *duration_str; time_t tmp; /* manpages say ut_tv.tv_sec *is* time_t, * but some systems have it wrong */ tmp = ut->ut_tv.tv_sec; safe_strncpy(login_time, ctime(&tmp), 17); snprintf(logout_time, 8, "- %s", ctime(&dur_secs) + 11); dur_secs = MAX(dur_secs - (time_t)ut->ut_tv.tv_sec, (time_t)0); /* unsigned int is easier to divide than time_t (which may be signed long) */ mins = dur_secs / 60; days = mins / (24*60); mins = mins % (24*60); hours = mins / 60; mins = mins % 60; // if (days) { sprintf(duration, "(%u+%02u:%02u)", days, hours, mins); // } else { // sprintf(duration, " (%02u:%02u)", hours, mins); // } logout_str = logout_time; duration_str = duration; switch (state) { case NORMAL: break; case LOGGED: logout_str = " still"; duration_str = "logged in"; break; case DOWN: logout_str = "- down "; break; case REBOOT: break; case CRASH: logout_str = "- crash"; break; case GONE: logout_str = " gone"; duration_str = "- no logout"; break; } printf(HEADER_FORMAT, ut->ut_user, ut->ut_line, show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN, show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN, ut->ut_host, login_time, logout_str, duration_str); } static int get_ut_type(struct utmp *ut) { if (ut->ut_line[0] == '~') { if (strcmp(ut->ut_user, "shutdown") == 0) { return SHUTDOWN_TIME; } if (strcmp(ut->ut_user, "reboot") == 0) { return BOOT_TIME; } if (strcmp(ut->ut_user, "runlevel") == 0) { return RUN_LVL; } return ut->ut_type; } if (ut->ut_user[0] == 0) { return DEAD_PROCESS; } if ((ut->ut_type != DEAD_PROCESS) && (strcmp(ut->ut_user, "LOGIN") != 0) && ut->ut_user[0] && ut->ut_line[0] ) { ut->ut_type = USER_PROCESS; } if (strcmp(ut->ut_user, "date") == 0) { if (ut->ut_line[0] == '|') { return OLD_TIME; } if (ut->ut_line[0] == '{') { return NEW_TIME; } } return ut->ut_type; } static int is_runlevel_shutdown(struct utmp *ut) { if (((ut->ut_pid & 255) == '0') || ((ut->ut_pid & 255) == '6')) { return 1; } return 0; } int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int last_main(int argc UNUSED_PARAM, char **argv) { struct utmp ut; const char *filename = _PATH_WTMP; llist_t *zlist; off_t pos; time_t start_time; time_t boot_time; time_t down_time; int file; smallint going_down; smallint boot_down; /*opt =*/ getopt32(argv, "Wf:" /* "H" */, &filename); #ifdef BUT_UTIL_LINUX_LAST_HAS_NO_SUCH_OPT if (opt & LAST_OPT_H) { /* Print header line */ if (opt & LAST_OPT_W) { printf(HEADER_FORMAT, HEADER_LINE_WIDE); } else { printf(HEADER_FORMAT, HEADER_LINE); } } #endif file = xopen(filename, O_RDONLY); { /* in case the file is empty... */ struct stat st; fstat(file, &st); start_time = st.st_ctime; } time(&down_time); going_down = 0; boot_down = NORMAL; /* 0 */ zlist = NULL; boot_time = 0; /* get file size, rounding down to last full record */ pos = xlseek(file, 0, SEEK_END) / sizeof(ut) * sizeof(ut); for (;;) { pos -= (off_t)sizeof(ut); if (pos < 0) { /* Beyond the beginning of the file boundary => * the whole file has been read. */ break; } xlseek(file, pos, SEEK_SET); xread(file, &ut, sizeof(ut)); /* rewritten by each record, eventially will have * first record's ut_tv.tv_sec: */ start_time = ut.ut_tv.tv_sec; switch (get_ut_type(&ut)) { case SHUTDOWN_TIME: down_time = ut.ut_tv.tv_sec; boot_down = DOWN; going_down = 1; break; case RUN_LVL: if (is_runlevel_shutdown(&ut)) { down_time = ut.ut_tv.tv_sec; going_down = 1; boot_down = DOWN; } break; case BOOT_TIME: strcpy(ut.ut_line, "system boot"); show_entry(&ut, REBOOT, down_time); boot_down = CRASH; going_down = 1; break; case DEAD_PROCESS: if (!ut.ut_line[0]) { break; } /* add_entry */ llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut))); break; case USER_PROCESS: { int show; if (!ut.ut_line[0]) { break; } /* find_entry */ show = 1; { llist_t *el, *next; for (el = zlist; el; el = next) { struct utmp *up = (struct utmp *)el->data; next = el->link; if (strncmp(up->ut_line, ut.ut_line, UT_LINESIZE) == 0) { if (show) { show_entry(&ut, NORMAL, up->ut_tv.tv_sec); show = 0; } llist_unlink(&zlist, el); free(el->data); free(el); } } } if (show) { int state = boot_down; if (boot_time == 0) { state = LOGGED; /* Check if the process is alive */ if ((ut.ut_pid > 0) && (kill(ut.ut_pid, 0) != 0) && (errno == ESRCH)) { state = GONE; } } show_entry(&ut, state, boot_time); } /* add_entry */ llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut))); break; } } if (going_down) { boot_time = ut.ut_tv.tv_sec; llist_free(zlist, free); zlist = NULL; going_down = 0; } } if (ENABLE_FEATURE_CLEAN_UP) { llist_free(zlist, free); } printf("\nwtmp begins %s", ctime(&start_time)); if (ENABLE_FEATURE_CLEAN_UP) close(file); fflush_stdout_and_exit(EXIT_SUCCESS); } busybox-1.22.1/miscutils/microcom.c0000644000000000000000000001117412263563520015766 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * bare bones 'talk to modem' program - similar to 'cu -l $device' * inspired by mgetty's microcom * * Copyright (C) 2008 by Vladimir Dronnikov * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define microcom_trivial_usage //usage: "[-d DELAY] [-t TIMEOUT] [-s SPEED] [-X] TTY" //usage:#define microcom_full_usage "\n\n" //usage: "Copy bytes for stdin to TTY and from TTY to stdout\n" //usage: "\n -d Wait up to DELAY ms for TTY output before sending every" //usage: "\n next byte to it" //usage: "\n -t Exit if both stdin and TTY are silent for TIMEOUT ms" //usage: "\n -s Set serial line to SPEED" //usage: "\n -X Disable special meaning of NUL and Ctrl-X from stdin" #include "libbb.h" // set raw tty mode static void xget1(int fd, struct termios *t, struct termios *oldt) { tcgetattr(fd, oldt); *t = *oldt; cfmakeraw(t); // t->c_lflag &= ~(ISIG|ICANON|ECHO|IEXTEN); // t->c_iflag &= ~(BRKINT|IXON|ICRNL); // t->c_oflag &= ~(ONLCR); // t->c_cc[VMIN] = 1; // t->c_cc[VTIME] = 0; } static int xset1(int fd, struct termios *tio, const char *device) { int ret = tcsetattr(fd, TCSAFLUSH, tio); if (ret) { bb_perror_msg("can't tcsetattr for %s", device); } return ret; } int microcom_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int microcom_main(int argc UNUSED_PARAM, char **argv) { int sfd; int nfd; struct pollfd pfd[2]; struct termios tio0, tiosfd, tio; char *device_lock_file; enum { OPT_X = 1 << 0, // do not respect Ctrl-X, Ctrl-@ OPT_s = 1 << 1, // baudrate OPT_d = 1 << 2, // wait for device response, ms OPT_t = 1 << 3, // timeout, ms }; speed_t speed = 9600; int delay = -1; int timeout = -1; unsigned opts; // fetch options opt_complementary = "=1:s+:d+:t+"; // exactly one arg, numeric options opts = getopt32(argv, "Xs:d:t:", &speed, &delay, &timeout); // argc -= optind; argv += optind; // try to create lock file in /var/lock device_lock_file = (char *)bb_basename(argv[0]); device_lock_file = xasprintf("/var/lock/LCK..%s", device_lock_file); sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644); if (sfd < 0) { // device already locked -> bail out if (errno == EEXIST) bb_perror_msg_and_die("can't create '%s'", device_lock_file); // can't create lock -> don't care if (ENABLE_FEATURE_CLEAN_UP) free(device_lock_file); device_lock_file = NULL; } else { // %4d to make concurrent mgetty (if any) happy. // Mgetty treats 4-bytes lock files as binary, // not text, PID. Making 5+ char file. Brrr... fdprintf(sfd, "%4d\n", getpid()); close(sfd); } // setup signals bb_signals(0 + (1 << SIGHUP) + (1 << SIGINT) + (1 << SIGTERM) + (1 << SIGPIPE) , record_signo); // error exit code if we fail to open the device bb_got_signal = 1; // open device sfd = open_or_warn(argv[0], O_RDWR | O_NOCTTY | O_NONBLOCK); if (sfd < 0) goto done; fcntl(sfd, F_SETFL, O_RDWR); // put device to "raw mode" xget1(sfd, &tio, &tiosfd); // set device speed cfsetspeed(&tio, tty_value_to_baud(speed)); if (xset1(sfd, &tio, argv[0])) goto done; // put stdin to "raw mode" (if stdin is a TTY), // handle one character at a time if (isatty(STDIN_FILENO)) { xget1(STDIN_FILENO, &tio, &tio0); if (xset1(STDIN_FILENO, &tio, "stdin")) goto done; } // main loop: check with poll(), then read/write bytes across pfd[0].fd = sfd; pfd[0].events = POLLIN; pfd[1].fd = STDIN_FILENO; pfd[1].events = POLLIN; bb_got_signal = 0; nfd = 2; // Not safe_poll: we want to exit on signal while (!bb_got_signal && poll(pfd, nfd, timeout) > 0) { if (nfd > 1 && pfd[1].revents) { char c; // read from stdin -> write to device if (safe_read(STDIN_FILENO, &c, 1) < 1) { // don't poll stdin anymore if we got EOF/error nfd--; goto skip_write; } // do we need special processing? if (!(opts & OPT_X)) { // ^@ sends Break if (VINTR == c) { tcsendbreak(sfd, 0); goto skip_write; } // ^X exits if (24 == c) break; } write(sfd, &c, 1); if (delay >= 0) safe_poll(pfd, 1, delay); skip_write: ; } if (pfd[0].revents) { #define iobuf bb_common_bufsiz1 ssize_t len; // read from device -> write to stdout len = safe_read(sfd, iobuf, sizeof(iobuf)); if (len > 0) full_write(STDOUT_FILENO, iobuf, len); else { // EOF/error -> bail out bb_got_signal = SIGHUP; break; } } } // restore device mode tcsetattr(sfd, TCSAFLUSH, &tiosfd); if (isatty(STDIN_FILENO)) tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0); done: if (device_lock_file) unlink(device_lock_file); return bb_got_signal; } busybox-1.22.1/miscutils/Config.src0000644000000000000000000003714112263563520015732 0ustar rootroot# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Miscellaneous Utilities" INSERT config ADJTIMEX bool "adjtimex" default y select PLATFORM_LINUX help Adjtimex reads and optionally sets adjustment parameters for the Linux clock adjustment algorithm. config BBCONFIG bool "bbconfig" default n help The bbconfig applet will print the config file with which busybox was built. config FEATURE_COMPRESS_BBCONFIG bool "Compress bbconfig data" default y depends on BBCONFIG help Store bbconfig data in compressed form, uncompress them on-the-fly before output. If you have a really tiny busybox with few applets enabled (and bunzip2 isn't one of them), the overhead of the decompressor might be noticeable. Also, if you run executables directly from ROM and have very little memory, this might not be a win. Otherwise, you probably want this. config BEEP bool "beep" default y select PLATFORM_LINUX help The beep applets beeps in a given freq/Hz. config FEATURE_BEEP_FREQ int "default frequency" range 0 2147483647 default 4000 depends on BEEP help Frequency for default beep. config FEATURE_BEEP_LENGTH_MS int "default length" range 0 2147483647 default 30 depends on BEEP help Length in ms for default beep. config CHAT bool "chat" default y help Simple chat utility. config FEATURE_CHAT_NOFAIL bool "Enable NOFAIL expect strings" depends on CHAT default y help When enabled expect strings which are started with a dash trigger no-fail mode. That is when expectation is not met within timeout the script is not terminated but sends next SEND string and waits for next EXPECT string. This allows to compose far more flexible scripts. config FEATURE_CHAT_TTY_HIFI bool "Force STDIN to be a TTY" depends on CHAT default n help Original chat always treats STDIN as a TTY device and sets for it so-called raw mode. This option turns on such behaviour. config FEATURE_CHAT_IMPLICIT_CR bool "Enable implicit Carriage Return" depends on CHAT default y help When enabled make chat to terminate all SEND strings with a "\r" unless "\c" is met anywhere in the string. config FEATURE_CHAT_SWALLOW_OPTS bool "Swallow options" depends on CHAT default y help Busybox chat require no options. To make it not fail when used in place of original chat (which has a bunch of options) turn this on. config FEATURE_CHAT_SEND_ESCAPES bool "Support weird SEND escapes" depends on CHAT default y help Original chat uses some escape sequences in SEND arguments which are not sent to device but rather performs special actions. E.g. "\K" means to send a break sequence to device. "\d" delays execution for a second, "\p" -- for a 1/100 of second. Before turning this option on think twice: do you really need them? config FEATURE_CHAT_VAR_ABORT_LEN bool "Support variable-length ABORT conditions" depends on CHAT default y help Original chat uses fixed 50-bytes length ABORT conditions. Say N here. config FEATURE_CHAT_CLR_ABORT bool "Support revoking of ABORT conditions" depends on CHAT default y help Support CLR_ABORT directive. config CHRT bool "chrt" default y help manipulate real-time attributes of a process. This requires sched_{g,s}etparam support in your libc. config CROND bool "crond" default y select FEATURE_SYSLOG help Crond is a background daemon that parses individual crontab files and executes commands on behalf of the users in question. This is a port of dcron from slackware. It uses files of the format /var/spool/cron/crontabs/ files, for example: $ cat /var/spool/cron/crontabs/root # Run daily cron jobs at 4:40 every day: 40 4 * * * /etc/cron/daily > /dev/null 2>&1 config FEATURE_CROND_D bool "Support option -d to redirect output to stderr" depends on CROND default y help -d sets loglevel to 0 (most verbose) and directs all output to stderr. config FEATURE_CROND_CALL_SENDMAIL bool "Report command output via email (using sendmail)" default y depends on CROND help Command output will be sent to corresponding user via email. config FEATURE_CROND_DIR string "crond spool directory" default "/var/spool/cron" depends on CROND || CRONTAB help Location of crond spool. config CRONTAB bool "crontab" default y help Crontab manipulates the crontab for a particular user. Only the superuser may specify a different user and/or crontab directory. Note that Busybox binary must be setuid root for this applet to work properly. config DC bool "dc" default y help Dc is a reverse-polish desk calculator which supports unlimited precision arithmetic. config FEATURE_DC_LIBM bool "Enable power and exp functions (requires libm)" default y depends on DC help Enable power and exp functions. NOTE: This will require libm to be present for linking. config DEVFSD bool "devfsd (obsolete)" default n select PLATFORM_LINUX select FEATURE_SYSLOG help This is deprecated and should NOT be used anymore. Use linux >= 2.6 (optionally with hotplug) and mdev instead! See docs/mdev.txt for detailed instructions on how to use mdev instead. Provides compatibility with old device names on a devfs systems. You should set it to true if you have devfs enabled. The following keywords in devsfd.conf are supported: "CLEAR_CONFIG", "INCLUDE", "OPTIONAL_INCLUDE", "RESTORE", "PERMISSIONS", "EXECUTE", "COPY", "IGNORE", "MKOLDCOMPAT", "MKNEWCOMPAT","RMOLDCOMPAT", "RMNEWCOMPAT". But only if they are written UPPERCASE!!!!!!!! config DEVFSD_MODLOAD bool "Adds support for MODLOAD keyword in devsfd.conf" default y depends on DEVFSD help This actually doesn't work with busybox modutils but needs the external modutils. config DEVFSD_FG_NP bool "Enables the -fg and -np options" default y depends on DEVFSD help -fg Run the daemon in the foreground. -np Exit after parsing the configuration file. Do not poll for events. config DEVFSD_VERBOSE bool "Increases logging (and size)" default y depends on DEVFSD help Increases logging to stderr or syslog. config FEATURE_DEVFS bool "Use devfs names for all devices (obsolete)" default n select PLATFORM_LINUX help This is obsolete and should NOT be used anymore. Use linux >= 2.6 (optionally with hotplug) and mdev instead! For legacy systems -- if there is no way around devfsd -- this tells busybox to look for names like /dev/loop/0 instead of /dev/loop0. If your /dev directory has normal names instead of devfs names, you don't want this. config DEVMEM bool "devmem" default y help devmem is a small program that reads and writes from physical memory using /dev/mem. config EJECT bool "eject" default y select PLATFORM_LINUX help Used to eject cdroms. (defaults to /dev/cdrom) config FEATURE_EJECT_SCSI bool "SCSI support" default y depends on EJECT help Add the -s option to eject, this allows to eject SCSI-Devices and usb-storage devices. config FBSPLASH bool "fbsplash" default y select PLATFORM_LINUX help Shows splash image and progress bar on framebuffer device. Can be used during boot phase of an embedded device. ~2kb. Usage: - use kernel option 'vga=xxx' or otherwise enable fb device. - put somewhere fbsplash.cfg file and an image in .ppm format. - $ setsid fbsplash [params] & -c: hide cursor -d /dev/fbN: framebuffer device (if not /dev/fb0) -s path_to_image_file (can be "-" for stdin) -i path_to_cfg_file (can be "-" for stdin) -f path_to_fifo (can be "-" for stdin) - if you want to run it only in presence of kernel parameter: grep -q "fbsplash=on" = 2.6.13 config INOTIFYD bool "inotifyd" default n # doesn't build on Knoppix 5 help Simple inotify daemon. Reports filesystem changes. Requires kernel >= 2.6.13 config LAST bool "last" default y depends on FEATURE_WTMP help 'last' displays a list of the last users that logged into the system. choice prompt "Choose last implementation" depends on LAST default FEATURE_LAST_FANCY config FEATURE_LAST_SMALL bool "small" help This is a small version of last with just the basic set of features. config FEATURE_LAST_FANCY bool "huge" help 'last' displays detailed information about the last users that logged into the system (mimics sysvinit last). +900 bytes. endchoice config HDPARM bool "hdparm" default y select PLATFORM_LINUX help Get/Set hard drive parameters. Primarily intended for ATA drives. Adds about 13k (or around 30k if you enable the FEATURE_HDPARM_GET_IDENTITY option).... config FEATURE_HDPARM_GET_IDENTITY bool "Support obtaining detailed information directly from drives" default y depends on HDPARM help Enables the -I and -i options to obtain detailed information directly from drives about their capabilities and supported ATA feature set. If no device name is specified, hdparm will read identify data from stdin. Enabling this option will add about 16k... config FEATURE_HDPARM_HDIO_SCAN_HWIF bool "Register an IDE interface (DANGEROUS)" default y depends on HDPARM help Enables the 'hdparm -R' option to register an IDE interface. This is dangerous stuff, so you should probably say N. config FEATURE_HDPARM_HDIO_UNREGISTER_HWIF bool "Un-register an IDE interface (DANGEROUS)" default y depends on HDPARM help Enables the 'hdparm -U' option to un-register an IDE interface. This is dangerous stuff, so you should probably say N. config FEATURE_HDPARM_HDIO_DRIVE_RESET bool "Perform device reset (DANGEROUS)" default y depends on HDPARM help Enables the 'hdparm -w' option to perform a device reset. This is dangerous stuff, so you should probably say N. config FEATURE_HDPARM_HDIO_TRISTATE_HWIF bool "Tristate device for hotswap (DANGEROUS)" default y depends on HDPARM help Enables the 'hdparm -x' option to tristate device for hotswap, and the '-b' option to get/set bus state. This is dangerous stuff, so you should probably say N. config FEATURE_HDPARM_HDIO_GETSET_DMA bool "Get/set using_dma flag" default y depends on HDPARM help Enables the 'hdparm -d' option to get/set using_dma flag. config MAKEDEVS bool "makedevs" default y help 'makedevs' is a utility used to create a batch of devices with one command. There are two choices for command line behaviour, the interface as used by LEAF/Linux Router Project, or a device table file. 'leaf' is traditionally what busybox follows, it allows multiple devices of a particluar type to be created per command. e.g. /dev/hda[0-9] Device properties are passed as command line arguments. 'table' reads device properties from a file or stdin, allowing a batch of unrelated devices to be made with one command. User/group names are allowed as an alternative to uid/gid. choice prompt "Choose makedevs behaviour" depends on MAKEDEVS default FEATURE_MAKEDEVS_TABLE config FEATURE_MAKEDEVS_LEAF bool "leaf" config FEATURE_MAKEDEVS_TABLE bool "table" endchoice config MAN bool "man" default y help Format and display manual pages. config MICROCOM bool "microcom" default y help The poor man's minicom utility for chatting with serial port devices. config MOUNTPOINT bool "mountpoint" default y help mountpoint checks if the directory is a mountpoint. config MT bool "mt" default y help mt is used to control tape devices. You can use the mt utility to advance or rewind a tape past a specified number of archive files on the tape. config RAIDAUTORUN bool "raidautorun" default y select PLATFORM_LINUX help raidautorun tells the kernel md driver to search and start RAID arrays. config READAHEAD bool "readahead" default y depends on LFS select PLATFORM_LINUX help Preload the files listed on the command line into RAM cache so that subsequent reads on these files will not block on disk I/O. This applet just calls the readahead(2) system call on each file. It is mainly useful in system startup scripts to preload files or executables before they are used. When used at the right time (in particular when a CPU bound process is running) it can significantly speed up system startup. As readahead(2) blocks until each file has been read, it is best to run this applet as a background job. config RUNLEVEL bool "runlevel" default y depends on FEATURE_UTMP help find the current and previous system runlevel. This applet uses utmp but does not rely on busybox supporing utmp on purpose. It is used by e.g. emdebian via /etc/init.d/rc. config RX bool "rx" default y select PLATFORM_LINUX help Receive files using the Xmodem protocol. config SETSID bool "setsid" default y help setsid runs a program in a new session config STRINGS bool "strings" default y help strings prints the printable character sequences for each file specified. config TASKSET bool "taskset" default n # doesn't build on some non-x86 targets (m68k) help Retrieve or set a processes's CPU affinity. This requires sched_{g,s}etaffinity support in your libc. config FEATURE_TASKSET_FANCY bool "Fancy output" default y depends on TASKSET help Add code for fancy output. This merely silences a compiler-warning and adds about 135 Bytes. May be needed for machines with alot of CPUs. config TIME bool "time" default y help The time command runs the specified program with the given arguments. When the command finishes, time writes a message to standard output giving timing statistics about this program run. config TIMEOUT bool "timeout" default y help Runs a program and watches it. If it does not terminate in specified number of seconds, it is sent a signal. config TTYSIZE bool "ttysize" default y help A replacement for "stty size". Unlike stty, can report only width, only height, or both, in any order. It also does not complain on error, but returns default 80x24. Usage in shell scripts: width=`ttysize w`. config VOLNAME bool "volname" default y help Prints a CD-ROM volume name. config WATCHDOG bool "watchdog" default y select PLATFORM_LINUX help The watchdog utility is used with hardware or software watchdog device drivers. It opens the specified watchdog device special file and periodically writes a magic character to the device. If the watchdog applet ever fails to write the magic character within a certain amount of time, the watchdog device assumes the system has hung, and will cause the hardware to reboot. endmenu busybox-1.22.1/miscutils/bbconfig.c0000644000000000000000000000216212263563520015724 0ustar rootroot/* vi: set sw=4 ts=4: */ /* This file was released into the public domain by Paul Fox. */ //usage:#define bbconfig_trivial_usage //usage: "" //usage:#define bbconfig_full_usage "\n\n" //usage: "Print the config file used by busybox build" #include "libbb.h" #include "bbconfigopts.h" #if ENABLE_FEATURE_COMPRESS_BBCONFIG # include "bb_archive.h" # include "bbconfigopts_bz2.h" #endif int bbconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int bbconfig_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { #if ENABLE_FEATURE_COMPRESS_BBCONFIG bunzip_data *bd; int i = start_bunzip(&bd, /* src_fd: */ -1, /* inbuf: */ bbconfig_config_bz2, /* len: */ sizeof(bbconfig_config_bz2)); /* read_bunzip can longjmp to start_bunzip, and ultimately * end up here with i != 0 on read data errors! Not trivial */ if (!i) { /* Cannot use xmalloc: will leak bd in NOFORK case! */ char *outbuf = malloc_or_warn(sizeof(bbconfig_config)); if (outbuf) { read_bunzip(bd, outbuf, sizeof(bbconfig_config)); full_write1_str(outbuf); } } #else full_write1_str(bbconfig_config); #endif return 0; } busybox-1.22.1/miscutils/taskset.c0000644000000000000000000001044012263563520015627 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * taskset - retrieve or set a processes' CPU affinity * Copyright (c) 2006 Bernhard Reutner-Fischer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define taskset_trivial_usage //usage: "[-p] [MASK] [PID | PROG ARGS]" //usage:#define taskset_full_usage "\n\n" //usage: "Set or get CPU affinity\n" //usage: "\n -p Operate on an existing PID" //usage: //usage:#define taskset_example_usage //usage: "$ taskset 0x7 ./dgemm_test&\n" //usage: "$ taskset -p 0x1 $!\n" //usage: "pid 4790's current affinity mask: 7\n" //usage: "pid 4790's new affinity mask: 1\n" //usage: "$ taskset 0x7 /bin/sh -c './taskset -p 0x1 $$'\n" //usage: "pid 6671's current affinity mask: 1\n" //usage: "pid 6671's new affinity mask: 1\n" //usage: "$ taskset -p 1\n" //usage: "pid 1's current affinity mask: 3\n" #include #include "libbb.h" #if ENABLE_FEATURE_TASKSET_FANCY #define TASKSET_PRINTF_MASK "%s" /* craft a string from the mask */ static char *from_cpuset(cpu_set_t *mask) { int i; char *ret = NULL; char *str = xzalloc((CPU_SETSIZE / 4) + 1); /* we will leak it */ for (i = CPU_SETSIZE - 4; i >= 0; i -= 4) { int val = 0; int off; for (off = 0; off <= 3; ++off) if (CPU_ISSET(i + off, mask)) val |= 1 << off; if (!ret && val) ret = str; *str++ = bb_hexdigits_upcase[val] | 0x20; } return ret; } #else #define TASKSET_PRINTF_MASK "%llx" static unsigned long long from_cpuset(cpu_set_t *mask) { struct BUG_CPU_SETSIZE_is_too_small { char BUG_CPU_SETSIZE_is_too_small[ CPU_SETSIZE < sizeof(int) ? -1 : 1]; }; char *p = (void*)mask; /* Take the least significant bits. Careful! * Consider both CPU_SETSIZE=4 and CPU_SETSIZE=1024 cases */ #if BB_BIG_ENDIAN /* For big endian, it means LAST bits */ if (CPU_SETSIZE < sizeof(long)) p += CPU_SETSIZE - sizeof(int); else if (CPU_SETSIZE < sizeof(long long)) p += CPU_SETSIZE - sizeof(long); else p += CPU_SETSIZE - sizeof(long long); #endif if (CPU_SETSIZE < sizeof(long)) return *(unsigned*)p; if (CPU_SETSIZE < sizeof(long long)) return *(unsigned long*)p; return *(unsigned long long*)p; } #endif int taskset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int taskset_main(int argc UNUSED_PARAM, char **argv) { cpu_set_t mask; pid_t pid = 0; unsigned opt_p; const char *current_new; char *pid_str; char *aff = aff; /* for compiler */ /* NB: we mimic util-linux's taskset: -p does not take * an argument, i.e., "-pN" is NOT valid, only "-p N"! * Indeed, util-linux-2.13-pre7 uses: * getopt_long(argc, argv, "+pchV", ...), not "...p:..." */ opt_complementary = "-1"; /* at least 1 arg */ opt_p = getopt32(argv, "+p"); argv += optind; if (opt_p) { pid_str = *argv++; if (*argv) { /* "-p ...rest.is.ignored..." */ aff = pid_str; pid_str = *argv; /* NB: *argv != NULL in this case */ } /* else it was just "-p ", and *argv == NULL */ pid = xatoul_range(pid_str, 1, ((unsigned)(pid_t)ULONG_MAX) >> 1); } else { aff = *argv++; /* */ if (!*argv) bb_show_usage(); } current_new = "current\0new"; if (opt_p) { print_aff: if (sched_getaffinity(pid, sizeof(mask), &mask) < 0) bb_perror_msg_and_die("can't %cet pid %d's affinity", 'g', pid); printf("pid %d's %s affinity mask: "TASKSET_PRINTF_MASK"\n", pid, current_new, from_cpuset(&mask)); if (!*argv) { /* Either it was just "-p ", * or it was "-p " and we came here * for the second time (see goto below) */ return EXIT_SUCCESS; } *argv = NULL; current_new += 8; /* "new" */ } { /* Affinity was specified, translate it into cpu_set_t */ unsigned i; /* Do not allow zero mask: */ unsigned long long m = xstrtoull_range(aff, 0, 1, ULLONG_MAX); enum { CNT_BIT = CPU_SETSIZE < sizeof(m)*8 ? CPU_SETSIZE : sizeof(m)*8 }; CPU_ZERO(&mask); for (i = 0; i < CNT_BIT; i++) { unsigned long long bit = (1ULL << i); if (bit & m) CPU_SET(i, &mask); } } /* Set pid's or our own (pid==0) affinity */ if (sched_setaffinity(pid, sizeof(mask), &mask)) bb_perror_msg_and_die("can't %cet pid %d's affinity", 's', pid); if (!argv[0]) /* "-p [...ignored...]" */ goto print_aff; /* print new affinity and exit */ BB_EXECVP_or_die(argv); } busybox-1.22.1/miscutils/inotifyd.c0000644000000000000000000001421212263563520015777 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * simple inotify daemon * reports filesystem changes via userspace agent * * Copyright (C) 2008 by Vladimir Dronnikov * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* * Use as follows: * # inotifyd /user/space/agent dir/or/file/being/watched[:mask] ... * * When a filesystem event matching the specified mask is occured on specified file (or directory) * a userspace agent is spawned and given the following parameters: * $1. actual event(s) * $2. file (or directory) name * $3. name of subfile (if any), in case of watching a directory * * E.g. inotifyd ./dev-watcher /dev:n * * ./dev-watcher can be, say: * #!/bin/sh * echo "We have new device in here! Hello, $3!" * * See below for mask names explanation. */ //usage:#define inotifyd_trivial_usage //usage: "PROG FILE1[:MASK]..." //usage:#define inotifyd_full_usage "\n\n" //usage: "Run PROG on filesystem changes." //usage: "\nWhen a filesystem event matching MASK occurs on FILEn," //usage: "\nPROG ACTUAL_EVENTS FILEn [SUBFILE] is run." //usage: "\nIf PROG is -, events are sent to stdout." //usage: "\nEvents:" //usage: "\n a File is accessed" //usage: "\n c File is modified" //usage: "\n e Metadata changed" //usage: "\n w Writable file is closed" //usage: "\n 0 Unwritable file is closed" //usage: "\n r File is opened" //usage: "\n D File is deleted" //usage: "\n M File is moved" //usage: "\n u Backing fs is unmounted" //usage: "\n o Event queue overflowed" //usage: "\n x File can't be watched anymore" //usage: "\nIf watching a directory:" //usage: "\n m Subfile is moved into dir" //usage: "\n y Subfile is moved out of dir" //usage: "\n n Subfile is created" //usage: "\n d Subfile is deleted" //usage: "\n" //usage: "\ninotifyd waits for PROG to exit." //usage: "\nWhen x event happens for all FILEs, inotifyd exits." #include "libbb.h" #include static const char mask_names[] ALIGN1 = "a" // 0x00000001 File was accessed "c" // 0x00000002 File was modified "e" // 0x00000004 Metadata changed "w" // 0x00000008 Writable file was closed "0" // 0x00000010 Unwritable file closed "r" // 0x00000020 File was opened "m" // 0x00000040 File was moved from X "y" // 0x00000080 File was moved to Y "n" // 0x00000100 Subfile was created "d" // 0x00000200 Subfile was deleted "D" // 0x00000400 Self was deleted "M" // 0x00000800 Self was moved "\0" // 0x00001000 (unused) // Kernel events, always reported: "u" // 0x00002000 Backing fs was unmounted "o" // 0x00004000 Event queued overflowed "x" // 0x00008000 File is no longer watched (usually deleted) ; enum { MASK_BITS = sizeof(mask_names) - 1 }; int inotifyd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int inotifyd_main(int argc, char **argv) { int n; unsigned mask; struct pollfd pfd; char **watches; // names of files being watched const char *args[5]; // sanity check: agent and at least one watch must be given if (!argv[1] || !argv[2]) bb_show_usage(); argv++; // inotify_add_watch will number watched files // starting from 1, thus watches[0] is unimportant, // and 1st file name is watches[1]. watches = argv; args[0] = *argv; args[4] = NULL; argc -= 2; // number of files we watch // open inotify pfd.fd = inotify_init(); if (pfd.fd < 0) bb_perror_msg_and_die("no kernel support"); // setup watches while (*++argv) { char *path = *argv; char *masks = strchr(path, ':'); mask = 0x0fff; // assuming we want all non-kernel events // if mask is specified -> if (masks) { *masks = '\0'; // split path and mask // convert mask names to mask bitset mask = 0; while (*++masks) { const char *found; found = memchr(mask_names, *masks, MASK_BITS); if (found) mask |= (1 << (found - mask_names)); } } // add watch n = inotify_add_watch(pfd.fd, path, mask); if (n < 0) bb_perror_msg_and_die("add watch (%s) failed", path); //bb_error_msg("added %d [%s]:%4X", n, path, mask); } // setup signals bb_signals(BB_FATAL_SIGS, record_signo); // do watch pfd.events = POLLIN; while (1) { int len; void *buf; struct inotify_event *ie; again: if (bb_got_signal) break; n = poll(&pfd, 1, -1); // Signal interrupted us? if (n < 0 && errno == EINTR) goto again; // Under Linux, above if() is not necessary. // Non-fatal signals, e.g. SIGCHLD, when set to SIG_DFL, // are not interrupting poll(). // Thus we can just break if n <= 0 (see below), // because EINTR will happen only on SIGTERM et al. // But this might be not true under other Unixes, // and is generally way too subtle to depend on. if (n <= 0) // strange error? break; // read out all pending events // (NB: len must be int, not ssize_t or long!) xioctl(pfd.fd, FIONREAD, &len); #define eventbuf bb_common_bufsiz1 ie = buf = (len <= sizeof(eventbuf)) ? eventbuf : xmalloc(len); len = full_read(pfd.fd, buf, len); // process events. N.B. events may vary in length while (len > 0) { int i; // cache relevant events mask unsigned m = ie->mask & ((1 << MASK_BITS) - 1); if (m) { char events[MASK_BITS + 1]; char *s = events; for (i = 0; i < MASK_BITS; ++i, m >>= 1) { if ((m & 1) && (mask_names[i] != '\0')) *s++ = mask_names[i]; } *s = '\0'; if (LONE_CHAR(args[0], '-')) { /* "inotifyd - FILE": built-in echo */ printf(ie->len ? "%s\t%s\t%s\n" : "%s\t%s\n", events, watches[ie->wd], ie->name); fflush(stdout); } else { // bb_error_msg("exec %s %08X\t%s\t%s\t%s", args[0], // ie->mask, events, watches[ie->wd], ie->len ? ie->name : ""); args[1] = events; args[2] = watches[ie->wd]; args[3] = ie->len ? ie->name : NULL; spawn_and_wait((char **)args); } // we are done if all files got final x event if (ie->mask & 0x8000) { if (--argc <= 0) goto done; inotify_rm_watch(pfd.fd, ie->wd); } } // next event i = sizeof(struct inotify_event) + ie->len; len -= i; ie = (void*)((char*)ie + i); } if (eventbuf != buf) free(buf); } // while (1) done: return bb_got_signal; } busybox-1.22.1/miscutils/Kbuild.src0000644000000000000000000000326412263563520015736 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT lib-$(CONFIG_ADJTIMEX) += adjtimex.o lib-$(CONFIG_BBCONFIG) += bbconfig.o lib-$(CONFIG_BEEP) += beep.o lib-$(CONFIG_CHAT) += chat.o lib-$(CONFIG_CHRT) += chrt.o lib-$(CONFIG_CROND) += crond.o lib-$(CONFIG_CRONTAB) += crontab.o lib-$(CONFIG_DC) += dc.o lib-$(CONFIG_DEVFSD) += devfsd.o lib-$(CONFIG_DEVMEM) += devmem.o lib-$(CONFIG_EJECT) += eject.o lib-$(CONFIG_FBSPLASH) += fbsplash.o lib-$(CONFIG_FLASHCP) += flashcp.o lib-$(CONFIG_FLASH_ERASEALL) += flash_eraseall.o lib-$(CONFIG_FLASH_LOCK) += flash_lock_unlock.o lib-$(CONFIG_FLASH_UNLOCK) += flash_lock_unlock.o lib-$(CONFIG_IONICE) += ionice.o lib-$(CONFIG_HDPARM) += hdparm.o lib-$(CONFIG_INOTIFYD) += inotifyd.o lib-$(CONFIG_FEATURE_LAST_SMALL)+= last.o lib-$(CONFIG_FEATURE_LAST_FANCY)+= last_fancy.o lib-$(CONFIG_LESS) += less.o lib-$(CONFIG_MAKEDEVS) += makedevs.o lib-$(CONFIG_MAN) += man.o lib-$(CONFIG_MICROCOM) += microcom.o lib-$(CONFIG_MOUNTPOINT) += mountpoint.o lib-$(CONFIG_MT) += mt.o lib-$(CONFIG_RAIDAUTORUN) += raidautorun.o lib-$(CONFIG_READAHEAD) += readahead.o lib-$(CONFIG_RUNLEVEL) += runlevel.o lib-$(CONFIG_RX) += rx.o lib-$(CONFIG_SETSID) += setsid.o lib-$(CONFIG_STRINGS) += strings.o lib-$(CONFIG_TASKSET) += taskset.o lib-$(CONFIG_TIME) += time.o lib-$(CONFIG_TIMEOUT) += timeout.o lib-$(CONFIG_TTYSIZE) += ttysize.o lib-$(CONFIG_VOLNAME) += volname.o lib-$(CONFIG_WATCHDOG) += watchdog.o busybox-1.22.1/miscutils/flash_lock_unlock.c0000644000000000000000000000407012263563520017633 0ustar rootroot/* vi: set sw=4 ts=4: */ /* Ported to busybox from mtd-utils. * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define flash_lock_trivial_usage //usage: "MTD_DEVICE OFFSET SECTORS" //usage:#define flash_lock_full_usage "\n\n" //usage: "Lock part or all of an MTD device. If SECTORS is -1, then all sectors\n" //usage: "will be locked, regardless of the value of OFFSET" //usage: //usage:#define flash_unlock_trivial_usage //usage: "MTD_DEVICE" //usage:#define flash_unlock_full_usage "\n\n" //usage: "Unlock an MTD device" #include "libbb.h" #include int flash_lock_unlock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int flash_lock_unlock_main(int argc UNUSED_PARAM, char **argv) { /* note: fields in these structs are 32-bits. * apparently we can't win anything by using off_t * or long long's for offset and/or sectors vars. */ struct mtd_info_user info; struct erase_info_user lock; unsigned long offset; long sectors; int fd; #define do_lock (ENABLE_FLASH_LOCK && (!ENABLE_FLASH_UNLOCK || (applet_name[6] == 'l'))) if (!argv[1]) bb_show_usage(); /* parse offset and number of sectors to lock */ offset = 0; sectors = -1; if (do_lock) { if (!argv[2] || !argv[3]) bb_show_usage(); offset = xstrtoul(argv[2], 0); sectors = xstrtol(argv[3], 0); } fd = xopen(argv[1], O_RDWR); xioctl(fd, MEMGETINFO, &info); lock.start = 0; lock.length = info.size; if (do_lock) { unsigned long size = info.size - info.erasesize; if (offset > size) { bb_error_msg_and_die("%lx is beyond device size %lx\n", offset, size); } if (sectors == -1) { sectors = info.size / info.erasesize; } else { // isn't this useless? unsigned long num = info.size / info.erasesize; if (sectors > num) { bb_error_msg_and_die("%ld are too many " "sectors, device only has " "%ld\n", sectors, num); } } lock.start = offset; lock.length = sectors * info.erasesize; xioctl(fd, MEMLOCK, &lock); } else { xioctl(fd, MEMUNLOCK, &lock); } return EXIT_SUCCESS; } busybox-1.22.1/miscutils/fbsplash.cfg0000644000000000000000000000017612263563520016275 0ustar rootroot# progress bar position BAR_LEFT=170 BAR_TOP=300 BAR_WIDTH=300 BAR_HEIGHT=20 # progress bar color BAR_R=80 BAR_G=80 BAR_B=130 busybox-1.22.1/miscutils/volname.c0000644000000000000000000000326112263563520015615 0ustar rootroot/* * Reads and displays CD-ROM volume name * * Several people have asked how to read CD volume names so I wrote this * small program to do it. * * usage: volname [] * * Copyright (C) 2000-2001 Jeff Tranter (tranter@pobox.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * mods from distributed source (eject-2.0.13) are by * Matthew Stoltenberg */ //usage:#define volname_trivial_usage //usage: "[DEVICE]" //usage:#define volname_full_usage "\n\n" //usage: "Show CD volume name of the DEVICE (default /dev/cdrom)" #include "libbb.h" int volname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int volname_main(int argc UNUSED_PARAM, char **argv) { int fd; char buffer[32]; const char *device; device = "/dev/cdrom"; if (argv[1]) { device = argv[1]; if (argv[2]) bb_show_usage(); } fd = xopen(device, O_RDONLY); xlseek(fd, 32808, SEEK_SET); xread(fd, buffer, 32); printf("%32.32s\n", buffer); if (ENABLE_FEATURE_CLEAN_UP) { close(fd); } return 0; } busybox-1.22.1/miscutils/watchdog.c0000644000000000000000000000661612263563520015763 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini watchdog implementation for busybox * * Copyright (C) 2003 Paul Mundt * Copyright (C) 2006 Bernhard Reutner-Fischer * Copyright (C) 2008 Darius Augulis * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define watchdog_trivial_usage //usage: "[-t N[ms]] [-T N[ms]] [-F] DEV" //usage:#define watchdog_full_usage "\n\n" //usage: "Periodically write to watchdog device DEV\n" //usage: "\n -T N Reboot after N seconds if not reset (default 60)" //usage: "\n -t N Reset every N seconds (default 30)" //usage: "\n -F Run in foreground" //usage: "\n" //usage: "\nUse 500ms to specify period in milliseconds" #include "libbb.h" #include "linux/types.h" /* for __u32 */ #include "linux/watchdog.h" #define OPT_FOREGROUND (1 << 0) #define OPT_STIMER (1 << 1) #define OPT_HTIMER (1 << 2) static void watchdog_shutdown(int sig UNUSED_PARAM) { static const char V = 'V'; remove_pidfile(CONFIG_PID_FILE_PATH "/watchdog.pid"); write(3, &V, 1); /* Magic, see watchdog-api.txt in kernel */ if (ENABLE_FEATURE_CLEAN_UP) close(3); _exit(EXIT_SUCCESS); } int watchdog_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int watchdog_main(int argc, char **argv) { static const struct suffix_mult suffixes[] = { { "ms", 1 }, { "", 1000 }, { "", 0 } }; unsigned opts; unsigned stimer_duration; /* how often to restart */ unsigned htimer_duration = 60000; /* reboots after N ms if not restarted */ char *st_arg; char *ht_arg; opt_complementary = "=1"; /* must have exactly 1 argument */ opts = getopt32(argv, "Ft:T:", &st_arg, &ht_arg); /* We need to daemonize *before* opening the watchdog as many drivers * will only allow one process at a time to do so. Since daemonizing * is not perfect (child may run before parent finishes exiting), we * can't rely on parent exiting before us (let alone *cleanly* releasing * the watchdog fd -- something else that may not even be allowed). */ if (!(opts & OPT_FOREGROUND)) bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); if (opts & OPT_HTIMER) htimer_duration = xatou_sfx(ht_arg, suffixes); stimer_duration = htimer_duration / 2; if (opts & OPT_STIMER) stimer_duration = xatou_sfx(st_arg, suffixes); bb_signals(BB_FATAL_SIGS, watchdog_shutdown); /* Use known fd # - avoid needing global 'int fd' */ xmove_fd(xopen(argv[argc - 1], O_WRONLY), 3); /* WDIOC_SETTIMEOUT takes seconds, not milliseconds */ htimer_duration = htimer_duration / 1000; #ifndef WDIOC_SETTIMEOUT # error WDIOC_SETTIMEOUT is not defined, cannot compile watchdog applet #else # if defined WDIOC_SETOPTIONS && defined WDIOS_ENABLECARD { static const int enable = WDIOS_ENABLECARD; ioctl_or_warn(3, WDIOC_SETOPTIONS, (void*) &enable); } # endif ioctl_or_warn(3, WDIOC_SETTIMEOUT, &htimer_duration); #endif #if 0 ioctl_or_warn(3, WDIOC_GETTIMEOUT, &htimer_duration); printf("watchdog: SW timer is %dms, HW timer is %ds\n", stimer_duration, htimer_duration * 1000); #endif write_pidfile(CONFIG_PID_FILE_PATH "/watchdog.pid"); while (1) { /* * Make sure we clear the counter before sleeping, * as the counter value is undefined at this point -- PFM */ write(3, "", 1); /* write zero byte */ usleep(stimer_duration * 1000L); } return EXIT_SUCCESS; /* - not reached, but gcc 4.2.1 is too dumb! */ } busybox-1.22.1/miscutils/timeout.c0000644000000000000000000000717212263563520015647 0ustar rootroot/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * COPYING NOTES * * timeout.c -- a timeout handler for shell commands * * Copyright (C) 2005-6, Roberto A. Foglietta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * REVISION NOTES: * released 17-11-2005 by Roberto A. Foglietta * talarm 04-12-2005 by Roberto A. Foglietta * modified 05-12-2005 by Roberto A. Foglietta * sizerdct 06-12-2005 by Roberto A. Foglietta * splitszf 12-05-2006 by Roberto A. Foglietta * rewrite 14-11-2008 vda */ //usage:#define timeout_trivial_usage //usage: "[-t SECS] [-s SIG] PROG ARGS" //usage:#define timeout_full_usage "\n\n" //usage: "Runs PROG. Sends SIG to it if it is not gone in SECS seconds.\n" //usage: "Defaults: SECS: 10, SIG: TERM." #include "libbb.h" int timeout_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int timeout_main(int argc UNUSED_PARAM, char **argv) { int signo; int status; int parent = 0; int timeout = 10; pid_t pid; #if !BB_MMU char *sv1, *sv2; #endif const char *opt_s = "TERM"; /* -p option is not documented, it is needed to support NOMMU. */ /* -t SECONDS; -p PARENT_PID */ opt_complementary = "t+" USE_FOR_NOMMU(":p+"); /* '+': stop at first non-option */ getopt32(argv, "+s:t:" USE_FOR_NOMMU("p:"), &opt_s, &timeout, &parent); /*argv += optind; - no, wait for bb_daemonize_or_rexec! */ signo = get_signum(opt_s); if (signo < 0) bb_error_msg_and_die("unknown signal '%s'", opt_s); /* We want to create a grandchild which will watch * and kill the grandparent. Other methods: * making parent watch child disrupts parent<->child link * (example: "tcpsvd 0.0.0.0 1234 timeout service_prog" - * it's better if service_prog is a child of tcpsvd!), * making child watch parent results in programs having * unexpected children. */ if (parent) /* we were re-execed, already grandchild */ goto grandchild; if (!argv[optind]) /* no PROG? */ bb_show_usage(); #if !BB_MMU sv1 = argv[optind]; sv2 = argv[optind + 1]; #endif pid = xvfork(); if (pid == 0) { /* Child: spawn grandchild and exit */ parent = getppid(); #if !BB_MMU argv[optind] = xasprintf("-p%u", parent); argv[optind + 1] = NULL; #endif /* NB: exits with nonzero on error: */ bb_daemonize_or_rexec(0, argv); /* Here we are grandchild. Sleep, then kill grandparent */ grandchild: /* Just sleep(HUGE_NUM); kill(parent) may kill wrong process! */ while (1) { sleep(1); if (--timeout <= 0) break; if (kill(parent, 0)) { /* process is gone */ return EXIT_SUCCESS; } } kill(parent, signo); return EXIT_SUCCESS; } /* Parent */ wait(&status); /* wait for child to die */ /* Did intermediate [v]fork or exec fail? */ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) return EXIT_FAILURE; /* Ok, exec a program as requested */ argv += optind; #if !BB_MMU argv[0] = sv1; argv[1] = sv2; #endif BB_EXECVP_or_die(argv); } busybox-1.22.1/miscutils/less.c0000644000000000000000000014622312263563520015130 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini less implementation for busybox * * Copyright (C) 2005 by Rob Sullivan * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* * TODO: * - Add more regular expression support - search modifiers, certain matches, etc. * - Add more complex bracket searching - currently, nested brackets are * not considered. * - Add support for "F" as an input. This causes less to act in * a similar way to tail -f. * - Allow horizontal scrolling. * * Notes: * - the inp file pointer is used so that keyboard input works after * redirected input has been read from stdin */ //config:config LESS //config: bool "less" //config: default y //config: help //config: 'less' is a pager, meaning that it displays text files. It possesses //config: a wide array of features, and is an improvement over 'more'. //config: //config:config FEATURE_LESS_MAXLINES //config: int "Max number of input lines less will try to eat" //config: default 9999999 //config: depends on LESS //config: //config:config FEATURE_LESS_BRACKETS //config: bool "Enable bracket searching" //config: default y //config: depends on LESS //config: help //config: This option adds the capability to search for matching left and right //config: brackets, facilitating programming. //config: //config:config FEATURE_LESS_FLAGS //config: bool "Enable -m/-M" //config: default y //config: depends on LESS //config: help //config: The -M/-m flag enables a more sophisticated status line. //config: //config:config FEATURE_LESS_MARKS //config: bool "Enable marks" //config: default y //config: depends on LESS //config: help //config: Marks enable positions in a file to be stored for easy reference. //config: //config:config FEATURE_LESS_REGEXP //config: bool "Enable regular expressions" //config: default y //config: depends on LESS //config: help //config: Enable regular expressions, allowing complex file searches. //config: //config:config FEATURE_LESS_WINCH //config: bool "Enable automatic resizing on window size changes" //config: default y //config: depends on LESS //config: help //config: Makes less track window size changes. //config: //config:config FEATURE_LESS_ASK_TERMINAL //config: bool "Use 'tell me cursor position' ESC sequence to measure window" //config: default y //config: depends on FEATURE_LESS_WINCH //config: help //config: Makes less track window size changes. //config: If terminal size can't be retrieved and $LINES/$COLUMNS are not set, //config: this option makes less perform a last-ditch effort to find it: //config: position cursor to 999,999 and ask terminal to report real //config: cursor position using "ESC [ 6 n" escape sequence, then read stdin. //config: //config: This is not clean but helps a lot on serial lines and such. //config: //config:config FEATURE_LESS_DASHCMD //config: bool "Enable flag changes ('-' command)" //config: default y //config: depends on LESS //config: help //config: This enables the ability to change command-line flags within //config: less itself ('-' keyboard command). //config: //config:config FEATURE_LESS_LINENUMS //config: bool "Enable dynamic switching of line numbers" //config: default y //config: depends on FEATURE_LESS_DASHCMD //config: help //config: Enables "-N" command. //usage:#define less_trivial_usage //usage: "[-E" IF_FEATURE_LESS_FLAGS("Mm") "Nh~I?] [FILE]..." //usage:#define less_full_usage "\n\n" //usage: "View FILE (or stdin) one screenful at a time\n" //usage: "\n -E Quit once the end of a file is reached" //usage: IF_FEATURE_LESS_FLAGS( //usage: "\n -M,-m Display status line with line numbers" //usage: "\n and percentage through the file" //usage: ) //usage: "\n -N Prefix line number to each line" //usage: "\n -I Ignore case in all searches" //usage: "\n -~ Suppress ~s displayed past EOF" #include /* sched_yield() */ #include "libbb.h" #if ENABLE_FEATURE_LESS_REGEXP #include "xregex.h" #endif #define ESC "\033" /* The escape codes for highlighted and normal text */ #define HIGHLIGHT ESC"[7m" #define NORMAL ESC"[0m" /* The escape code to home and clear to the end of screen */ #define CLEAR ESC"[H\033[J" /* The escape code to clear to the end of line */ #define CLEAR_2_EOL ESC"[K" enum { /* Absolute max of lines eaten */ MAXLINES = CONFIG_FEATURE_LESS_MAXLINES, /* This many "after the end" lines we will show (at max) */ TILDES = 1, }; /* Command line options */ enum { FLAG_E = 1 << 0, FLAG_M = 1 << 1, FLAG_m = 1 << 2, FLAG_N = 1 << 3, FLAG_TILDE = 1 << 4, FLAG_I = 1 << 5, FLAG_S = (1 << 6) * ENABLE_FEATURE_LESS_DASHCMD, /* hijack command line options variable for internal state vars */ LESS_STATE_MATCH_BACKWARDS = 1 << 15, }; #if !ENABLE_FEATURE_LESS_REGEXP enum { pattern_valid = 0 }; #endif struct globals { int cur_fline; /* signed */ int kbd_fd; /* fd to get input from */ int less_gets_pos; /* last position in last line, taking into account tabs */ size_t last_line_pos; unsigned max_fline; unsigned max_lineno; /* this one tracks linewrap */ unsigned max_displayed_line; unsigned width; #if ENABLE_FEATURE_LESS_WINCH unsigned winch_counter; #endif ssize_t eof_error; /* eof if 0, error if < 0 */ ssize_t readpos; ssize_t readeof; /* must be signed */ const char **buffer; const char **flines; const char *empty_line_marker; unsigned num_files; unsigned current_file; char *filename; char **files; #if ENABLE_FEATURE_LESS_MARKS unsigned num_marks; unsigned mark_lines[15][2]; #endif #if ENABLE_FEATURE_LESS_REGEXP unsigned *match_lines; int match_pos; /* signed! */ int wanted_match; /* signed! */ int num_matches; regex_t pattern; smallint pattern_valid; #endif #if ENABLE_FEATURE_LESS_ASK_TERMINAL smallint winsize_err; #endif smallint terminated; struct termios term_orig, term_less; char kbd_input[KEYCODE_BUFFER_SIZE]; }; #define G (*ptr_to_globals) #define cur_fline (G.cur_fline ) #define kbd_fd (G.kbd_fd ) #define less_gets_pos (G.less_gets_pos ) #define last_line_pos (G.last_line_pos ) #define max_fline (G.max_fline ) #define max_lineno (G.max_lineno ) #define max_displayed_line (G.max_displayed_line) #define width (G.width ) #define winch_counter (G.winch_counter ) /* This one is 100% not cached by compiler on read access */ #define WINCH_COUNTER (*(volatile unsigned *)&winch_counter) #define eof_error (G.eof_error ) #define readpos (G.readpos ) #define readeof (G.readeof ) #define buffer (G.buffer ) #define flines (G.flines ) #define empty_line_marker (G.empty_line_marker ) #define num_files (G.num_files ) #define current_file (G.current_file ) #define filename (G.filename ) #define files (G.files ) #define num_marks (G.num_marks ) #define mark_lines (G.mark_lines ) #if ENABLE_FEATURE_LESS_REGEXP #define match_lines (G.match_lines ) #define match_pos (G.match_pos ) #define num_matches (G.num_matches ) #define wanted_match (G.wanted_match ) #define pattern (G.pattern ) #define pattern_valid (G.pattern_valid ) #endif #define terminated (G.terminated ) #define term_orig (G.term_orig ) #define term_less (G.term_less ) #define kbd_input (G.kbd_input ) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ less_gets_pos = -1; \ empty_line_marker = "~"; \ num_files = 1; \ current_file = 1; \ eof_error = 1; \ terminated = 1; \ IF_FEATURE_LESS_REGEXP(wanted_match = -1;) \ } while (0) /* flines[] are lines read from stdin, each in malloc'ed buffer. * Line numbers are stored as uint32_t prepended to each line. * Pointer is adjusted so that flines[i] points directly past * line number. Accesor: */ #define MEMPTR(p) ((char*)(p) - 4) #define LINENO(p) (*(uint32_t*)((p) - 4)) /* Reset terminal input to normal */ static void set_tty_cooked(void) { fflush_all(); tcsetattr(kbd_fd, TCSANOW, &term_orig); } /* Move the cursor to a position (x,y), where (0,0) is the top-left corner of the console */ static void move_cursor(int line, int row) { printf(ESC"[%u;%uH", line, row); } static void clear_line(void) { printf(ESC"[%u;0H" CLEAR_2_EOL, max_displayed_line + 2); } static void print_hilite(const char *str) { printf(HIGHLIGHT"%s"NORMAL, str); } static void print_statusline(const char *str) { clear_line(); printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str); } /* Exit the program gracefully */ static void less_exit(int code) { set_tty_cooked(); clear_line(); if (code < 0) kill_myself_with_sig(- code); /* does not return */ exit(code); } #if (ENABLE_FEATURE_LESS_DASHCMD && ENABLE_FEATURE_LESS_LINENUMS) \ || ENABLE_FEATURE_LESS_WINCH static void re_wrap(void) { int w = width; int new_line_pos; int src_idx; int dst_idx; int new_cur_fline = 0; uint32_t lineno; char linebuf[w + 1]; const char **old_flines = flines; const char *s; char **new_flines = NULL; char *d; if (option_mask32 & FLAG_N) w -= 8; src_idx = 0; dst_idx = 0; s = old_flines[0]; lineno = LINENO(s); d = linebuf; new_line_pos = 0; while (1) { *d = *s; if (*d != '\0') { new_line_pos++; if (*d == '\t') /* tab */ new_line_pos += 7; s++; d++; if (new_line_pos >= w) { int sz; /* new line is full, create next one */ *d = '\0'; next_new: sz = (d - linebuf) + 1; /* + 1: NUL */ d = ((char*)xmalloc(sz + 4)) + 4; LINENO(d) = lineno; memcpy(d, linebuf, sz); new_flines = xrealloc_vector(new_flines, 8, dst_idx); new_flines[dst_idx] = d; dst_idx++; if (new_line_pos < w) { /* if we came here thru "goto next_new" */ if (src_idx > max_fline) break; lineno = LINENO(s); } d = linebuf; new_line_pos = 0; } continue; } /* *d == NUL: old line ended, go to next old one */ free(MEMPTR(old_flines[src_idx])); /* btw, convert cur_fline... */ if (cur_fline == src_idx) new_cur_fline = dst_idx; src_idx++; /* no more lines? finish last new line (and exit the loop) */ if (src_idx > max_fline) goto next_new; s = old_flines[src_idx]; if (lineno != LINENO(s)) { /* this is not a continuation line! * create next _new_ line too */ goto next_new; } } free(old_flines); flines = (const char **)new_flines; max_fline = dst_idx - 1; last_line_pos = new_line_pos; cur_fline = new_cur_fline; /* max_lineno is screen-size independent */ #if ENABLE_FEATURE_LESS_REGEXP pattern_valid = 0; #endif } #endif #if ENABLE_FEATURE_LESS_REGEXP static void fill_match_lines(unsigned pos); #else #define fill_match_lines(pos) ((void)0) #endif /* Devilishly complex routine. * * Has to deal with EOF and EPIPE on input, * with line wrapping, with last line not ending in '\n' * (possibly not ending YET!), with backspace and tabs. * It reads input again if last time we got an EOF (thus supporting * growing files) or EPIPE (watching output of slow process like make). * * Variables used: * flines[] - array of lines already read. Linewrap may cause * one source file line to occupy several flines[n]. * flines[max_fline] - last line, possibly incomplete. * terminated - 1 if flines[max_fline] is 'terminated' * (if there was '\n' [which isn't stored itself, we just remember * that it was seen]) * max_lineno - last line's number, this one doesn't increment * on line wrap, only on "real" new lines. * readbuf[0..readeof-1] - small preliminary buffer. * readbuf[readpos] - next character to add to current line. * last_line_pos - screen line position of next char to be read * (takes into account tabs and backspaces) * eof_error - < 0 error, == 0 EOF, > 0 not EOF/error */ static void read_lines(void) { #define readbuf bb_common_bufsiz1 char *current_line, *p; int w = width; char last_terminated = terminated; #if ENABLE_FEATURE_LESS_REGEXP unsigned old_max_fline = max_fline; time_t last_time = 0; unsigned seconds_p1 = 3; /* seconds_to_loop + 1 */ #endif if (option_mask32 & FLAG_N) w -= 8; IF_FEATURE_LESS_REGEXP(again0:) p = current_line = ((char*)xmalloc(w + 4)) + 4; max_fline += last_terminated; if (!last_terminated) { const char *cp = flines[max_fline]; strcpy(p, cp); p += strlen(current_line); free(MEMPTR(flines[max_fline])); /* last_line_pos is still valid from previous read_lines() */ } else { last_line_pos = 0; } while (1) { /* read lines until we reach cur_fline or wanted_match */ *p = '\0'; terminated = 0; while (1) { /* read chars until we have a line */ char c; /* if no unprocessed chars left, eat more */ if (readpos >= readeof) { ndelay_on(0); eof_error = safe_read(STDIN_FILENO, readbuf, sizeof(readbuf)); ndelay_off(0); readpos = 0; readeof = eof_error; if (eof_error <= 0) goto reached_eof; } c = readbuf[readpos]; /* backspace? [needed for manpages] */ /* is (a) insane and */ /* (b) harder to do correctly, so we refuse to do it */ if (c == '\x8' && last_line_pos && p[-1] != '\t') { readpos++; /* eat it */ last_line_pos--; /* was buggy (p could end up <= current_line)... */ *--p = '\0'; continue; } { size_t new_last_line_pos = last_line_pos + 1; if (c == '\t') { new_last_line_pos += 7; new_last_line_pos &= (~7); } if ((int)new_last_line_pos >= w) break; last_line_pos = new_last_line_pos; } /* ok, we will eat this char */ readpos++; if (c == '\n') { terminated = 1; last_line_pos = 0; break; } /* NUL is substituted by '\n'! */ if (c == '\0') c = '\n'; *p++ = c; *p = '\0'; } /* end of "read chars until we have a line" loop */ /* Corner case: linewrap with only "" wrapping to next line */ /* Looks ugly on screen, so we do not store this empty line */ if (!last_terminated && !current_line[0]) { last_terminated = 1; max_lineno++; continue; } reached_eof: last_terminated = terminated; flines = xrealloc_vector(flines, 8, max_fline); flines[max_fline] = (char*)xrealloc(MEMPTR(current_line), strlen(current_line) + 1 + 4) + 4; LINENO(flines[max_fline]) = max_lineno; if (terminated) max_lineno++; if (max_fline >= MAXLINES) { eof_error = 0; /* Pretend we saw EOF */ break; } if (!(option_mask32 & FLAG_S) ? (max_fline > cur_fline + max_displayed_line) : (max_fline >= cur_fline && max_lineno > LINENO(flines[cur_fline]) + max_displayed_line) ) { #if !ENABLE_FEATURE_LESS_REGEXP break; #else if (wanted_match >= num_matches) { /* goto_match called us */ fill_match_lines(old_max_fline); old_max_fline = max_fline; } if (wanted_match < num_matches) break; #endif } if (eof_error <= 0) { if (eof_error < 0) { if (errno == EAGAIN) { /* not yet eof or error, reset flag (or else * we will hog CPU - select() will return * immediately */ eof_error = 1; } else { print_statusline(bb_msg_read_error); } } #if !ENABLE_FEATURE_LESS_REGEXP break; #else if (wanted_match < num_matches) { break; } else { /* goto_match called us */ time_t t = time(NULL); if (t != last_time) { last_time = t; if (--seconds_p1 == 0) break; } sched_yield(); goto again0; /* go loop again (max 2 seconds) */ } #endif } max_fline++; current_line = ((char*)xmalloc(w + 4)) + 4; p = current_line; last_line_pos = 0; } /* end of "read lines until we reach cur_fline" loop */ fill_match_lines(old_max_fline); #if ENABLE_FEATURE_LESS_REGEXP /* prevent us from being stuck in search for a match */ wanted_match = -1; #endif #undef readbuf } #if ENABLE_FEATURE_LESS_FLAGS /* Interestingly, writing calc_percent as a function saves around 32 bytes * on my build. */ static int calc_percent(void) { unsigned p = (100 * (cur_fline+max_displayed_line+1) + max_fline/2) / (max_fline+1); return p <= 100 ? p : 100; } /* Print a status line if -M was specified */ static void m_status_print(void) { int percentage; if (less_gets_pos >= 0) /* don't touch statusline while input is done! */ return; clear_line(); printf(HIGHLIGHT"%s", filename); if (num_files > 1) printf(" (file %i of %i)", current_file, num_files); printf(" lines %i-%i/%i ", cur_fline + 1, cur_fline + max_displayed_line + 1, max_fline + 1); if (cur_fline >= (int)(max_fline - max_displayed_line)) { printf("(END)"NORMAL); if (num_files > 1 && current_file != num_files) printf(HIGHLIGHT" - next: %s"NORMAL, files[current_file]); return; } percentage = calc_percent(); printf("%i%%"NORMAL, percentage); } #endif /* Print the status line */ static void status_print(void) { const char *p; if (less_gets_pos >= 0) /* don't touch statusline while input is done! */ return; /* Change the status if flags have been set */ #if ENABLE_FEATURE_LESS_FLAGS if (option_mask32 & (FLAG_M|FLAG_m)) { m_status_print(); return; } /* No flags set */ #endif clear_line(); if (cur_fline && cur_fline < (int)(max_fline - max_displayed_line)) { bb_putchar(':'); return; } p = "(END)"; if (!cur_fline) p = filename; if (num_files > 1) { printf(HIGHLIGHT"%s (file %i of %i)"NORMAL, p, current_file, num_files); return; } print_hilite(p); } static void cap_cur_fline(int nlines) { int diff; if (cur_fline < 0) cur_fline = 0; if (cur_fline + max_displayed_line > max_fline + TILDES) { cur_fline -= nlines; if (cur_fline < 0) cur_fline = 0; diff = max_fline - (cur_fline + max_displayed_line) + TILDES; /* As the number of lines requested was too large, we just move * to the end of the file */ if (diff > 0) cur_fline += diff; } } static const char controls[] ALIGN1 = /* NUL: never encountered; TAB: not converted */ /**/"\x01\x02\x03\x04\x05\x06\x07\x08" "\x0a\x0b\x0c\x0d\x0e\x0f" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" "\x7f\x9b"; /* DEL and infamous Meta-ESC :( */ static const char ctrlconv[] ALIGN1 = /* why 40 instead of 4a below? - it is a replacement for '\n'. * '\n' is a former NUL - we subst it with @, not J */ "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x40\x4b\x4c\x4d\x4e\x4f" "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"; static void lineno_str(char *nbuf9, const char *line) { nbuf9[0] = '\0'; if (option_mask32 & FLAG_N) { const char *fmt; unsigned n; if (line == empty_line_marker) { memset(nbuf9, ' ', 8); nbuf9[8] = '\0'; return; } /* Width of 7 preserves tab spacing in the text */ fmt = "%7u "; n = LINENO(line) + 1; if (n > 9999999) { n %= 10000000; fmt = "%07u "; } sprintf(nbuf9, fmt, n); } } #if ENABLE_FEATURE_LESS_REGEXP static void print_found(const char *line) { int match_status; int eflags; char *growline; regmatch_t match_structs; char buf[width]; char nbuf9[9]; const char *str = line; char *p = buf; size_t n; while (*str) { n = strcspn(str, controls); if (n) { if (!str[n]) break; memcpy(p, str, n); p += n; str += n; } n = strspn(str, controls); memset(p, '.', n); p += n; str += n; } strcpy(p, str); /* buf[] holds quarantined version of str */ /* Each part of the line that matches has the HIGHLIGHT * and NORMAL escape sequences placed around it. * NB: we regex against line, but insert text * from quarantined copy (buf[]) */ str = buf; growline = NULL; eflags = 0; goto start; while (match_status == 0) { char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"NORMAL, growline ? growline : "", (int)match_structs.rm_so, str, (int)(match_structs.rm_eo - match_structs.rm_so), str + match_structs.rm_so); free(growline); growline = new; str += match_structs.rm_eo; line += match_structs.rm_eo; eflags = REG_NOTBOL; start: /* Most of the time doesn't find the regex, optimize for that */ match_status = regexec(&pattern, line, 1, &match_structs, eflags); /* if even "" matches, treat it as "not a match" */ if (match_structs.rm_so >= match_structs.rm_eo) match_status = 1; } lineno_str(nbuf9, line); if (!growline) { printf(CLEAR_2_EOL"%s%s\n", nbuf9, str); return; } printf(CLEAR_2_EOL"%s%s%s\n", nbuf9, growline, str); free(growline); } #else void print_found(const char *line); #endif static void print_ascii(const char *str) { char buf[width]; char nbuf9[9]; char *p; size_t n; lineno_str(nbuf9, str); printf(CLEAR_2_EOL"%s", nbuf9); while (*str) { n = strcspn(str, controls); if (n) { if (!str[n]) break; printf("%.*s", (int) n, str); str += n; } n = strspn(str, controls); p = buf; do { if (*str == 0x7f) *p++ = '?'; else if (*str == (char)0x9b) /* VT100's CSI, aka Meta-ESC. Who's inventor? */ /* I want to know who committed this sin */ *p++ = '{'; else *p++ = ctrlconv[(unsigned char)*str]; str++; } while (--n); *p = '\0'; print_hilite(buf); } puts(str); } /* Print the buffer */ static void buffer_print(void) { unsigned i; move_cursor(0, 0); for (i = 0; i <= max_displayed_line; i++) if (pattern_valid) print_found(buffer[i]); else print_ascii(buffer[i]); status_print(); } static void buffer_fill_and_print(void) { unsigned i; #if ENABLE_FEATURE_LESS_DASHCMD int fpos = cur_fline; if (option_mask32 & FLAG_S) { /* Go back to the beginning of this line */ while (fpos && LINENO(flines[fpos]) == LINENO(flines[fpos-1])) fpos--; } i = 0; while (i <= max_displayed_line && fpos <= max_fline) { int lineno = LINENO(flines[fpos]); buffer[i] = flines[fpos]; i++; do { fpos++; } while ((fpos <= max_fline) && (option_mask32 & FLAG_S) && lineno == LINENO(flines[fpos]) ); } #else for (i = 0; i <= max_displayed_line && cur_fline + i <= max_fline; i++) { buffer[i] = flines[cur_fline + i]; } #endif for (; i <= max_displayed_line; i++) { buffer[i] = empty_line_marker; } buffer_print(); } /* Move the buffer up and down in the file in order to scroll */ static void buffer_down(int nlines) { cur_fline += nlines; read_lines(); cap_cur_fline(nlines); buffer_fill_and_print(); } static void buffer_up(int nlines) { cur_fline -= nlines; if (cur_fline < 0) cur_fline = 0; read_lines(); buffer_fill_and_print(); } static void buffer_line(int linenum) { if (linenum < 0) linenum = 0; cur_fline = linenum; read_lines(); if (linenum + max_displayed_line > max_fline) linenum = max_fline - max_displayed_line + TILDES; if (linenum < 0) linenum = 0; cur_fline = linenum; buffer_fill_and_print(); } static void open_file_and_read_lines(void) { if (filename) { xmove_fd(xopen(filename, O_RDONLY), STDIN_FILENO); } else { /* "less" with no arguments in argv[] */ /* For status line only */ filename = xstrdup(bb_msg_standard_input); } readpos = 0; readeof = 0; last_line_pos = 0; terminated = 1; read_lines(); } /* Reinitialize everything for a new file - free the memory and start over */ static void reinitialize(void) { unsigned i; if (flines) { for (i = 0; i <= max_fline; i++) free(MEMPTR(flines[i])); free(flines); flines = NULL; } max_fline = -1; cur_fline = 0; max_lineno = 0; open_file_and_read_lines(); #if ENABLE_FEATURE_LESS_ASK_TERMINAL if (G.winsize_err) printf("\033[999;999H" "\033[6n"); #endif buffer_fill_and_print(); } static int64_t getch_nowait(void) { int rd; int64_t key64; struct pollfd pfd[2]; pfd[0].fd = STDIN_FILENO; pfd[0].events = POLLIN; pfd[1].fd = kbd_fd; pfd[1].events = POLLIN; again: tcsetattr(kbd_fd, TCSANOW, &term_less); /* NB: select/poll returns whenever read will not block. Therefore: * if eof is reached, select/poll will return immediately * because read will immediately return 0 bytes. * Even if select/poll says that input is available, read CAN block * (switch fd into O_NONBLOCK'ed mode to avoid it) */ rd = 1; /* Are we interested in stdin? */ //TODO: reuse code for determining this if (!(option_mask32 & FLAG_S) ? !(max_fline > cur_fline + max_displayed_line) : !(max_fline >= cur_fline && max_lineno > LINENO(flines[cur_fline]) + max_displayed_line) ) { if (eof_error > 0) /* did NOT reach eof yet */ rd = 0; /* yes, we are interested in stdin */ } /* Position cursor if line input is done */ if (less_gets_pos >= 0) move_cursor(max_displayed_line + 2, less_gets_pos + 1); fflush_all(); if (kbd_input[0] == 0) { /* if nothing is buffered */ #if ENABLE_FEATURE_LESS_WINCH while (1) { int r; /* NB: SIGWINCH interrupts poll() */ r = poll(pfd + rd, 2 - rd, -1); if (/*r < 0 && errno == EINTR &&*/ winch_counter) return '\\'; /* anything which has no defined function */ if (r) break; } #else safe_poll(pfd + rd, 2 - rd, -1); #endif } /* We have kbd_fd in O_NONBLOCK mode, read inside read_key() * would not block even if there is no input available */ key64 = read_key(kbd_fd, kbd_input, /*timeout off:*/ -2); if ((int)key64 == -1) { if (errno == EAGAIN) { /* No keyboard input available. Since poll() did return, * we should have input on stdin */ read_lines(); buffer_fill_and_print(); goto again; } /* EOF/error (ssh session got killed etc) */ less_exit(0); } set_tty_cooked(); return key64; } /* Grab a character from input without requiring the return key. * May return KEYCODE_xxx values. * Note that this function works best with raw input. */ static int64_t less_getch(int pos) { int64_t key64; int key; again: less_gets_pos = pos; key = key64 = getch_nowait(); less_gets_pos = -1; /* Discard Ctrl-something chars. * (checking only lower 32 bits is a size optimization: * upper 32 bits are used only by KEYCODE_CURSOR_POS) */ if (key >= 0 && key < ' ' && key != 0x0d && key != 8) goto again; return key64; } static char* less_gets(int sz) { int c; unsigned i = 0; char *result = xzalloc(1); while (1) { c = '\0'; less_gets_pos = sz + i; c = getch_nowait(); if (c == 0x0d) { result[i] = '\0'; less_gets_pos = -1; return result; } if (c == 0x7f) c = 8; if (c == 8 && i) { printf("\x8 \x8"); i--; } if (c < ' ') /* filters out KEYCODE_xxx too (<0) */ continue; if (i >= width - sz - 1) continue; /* len limit */ bb_putchar(c); result[i++] = c; result = xrealloc(result, i+1); } } static void examine_file(void) { char *new_fname; print_statusline("Examine: "); new_fname = less_gets(sizeof("Examine: ") - 1); if (!new_fname[0]) { status_print(); err: free(new_fname); return; } if (access(new_fname, R_OK) != 0) { print_statusline("Cannot read this file"); goto err; } free(filename); filename = new_fname; /* files start by = argv. why we assume that argv is infinitely long?? files[num_files] = filename; current_file = num_files + 1; num_files++; */ files[0] = filename; num_files = current_file = 1; reinitialize(); } /* This function changes the file currently being paged. direction can be one of the following: * -1: go back one file * 0: go to the first file * 1: go forward one file */ static void change_file(int direction) { if (current_file != ((direction > 0) ? num_files : 1)) { current_file = direction ? current_file + direction : 1; free(filename); filename = xstrdup(files[current_file - 1]); reinitialize(); } else { print_statusline(direction > 0 ? "No next file" : "No previous file"); } } static void remove_current_file(void) { unsigned i; if (num_files < 2) return; if (current_file != 1) { change_file(-1); for (i = 3; i <= num_files; i++) files[i - 2] = files[i - 1]; num_files--; } else { change_file(1); for (i = 2; i <= num_files; i++) files[i - 2] = files[i - 1]; num_files--; current_file--; } } static void colon_process(void) { int keypress; /* Clear the current line and print a prompt */ print_statusline(" :"); keypress = less_getch(2); switch (keypress) { case 'd': remove_current_file(); break; case 'e': examine_file(); break; #if ENABLE_FEATURE_LESS_FLAGS case 'f': m_status_print(); break; #endif case 'n': change_file(1); break; case 'p': change_file(-1); break; case 'q': less_exit(EXIT_SUCCESS); break; case 'x': change_file(0); break; } } #if ENABLE_FEATURE_LESS_REGEXP static void normalize_match_pos(int match) { if (match >= num_matches) match = num_matches - 1; if (match < 0) match = 0; match_pos = match; } static void goto_match(int match) { if (!pattern_valid) return; if (match < 0) match = 0; /* Try to find next match if eof isn't reached yet */ if (match >= num_matches && eof_error > 0) { wanted_match = match; /* "I want to read until I see N'th match" */ read_lines(); } if (num_matches) { normalize_match_pos(match); buffer_line(match_lines[match_pos]); } else { print_statusline("No matches found"); } } static void fill_match_lines(unsigned pos) { if (!pattern_valid) return; /* Run the regex on each line of the current file */ while (pos <= max_fline) { /* If this line matches */ if (regexec(&pattern, flines[pos], 0, NULL, 0) == 0 /* and we didn't match it last time */ && !(num_matches && match_lines[num_matches-1] == pos) ) { match_lines = xrealloc_vector(match_lines, 4, num_matches); match_lines[num_matches++] = pos; } pos++; } } static void regex_process(void) { char *uncomp_regex, *err; /* Reset variables */ free(match_lines); match_lines = NULL; match_pos = 0; num_matches = 0; if (pattern_valid) { regfree(&pattern); pattern_valid = 0; } /* Get the uncompiled regular expression from the user */ clear_line(); bb_putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/'); uncomp_regex = less_gets(1); if (!uncomp_regex[0]) { free(uncomp_regex); buffer_print(); return; } /* Compile the regex and check for errors */ err = regcomp_or_errmsg(&pattern, uncomp_regex, (option_mask32 & FLAG_I) ? REG_ICASE : 0); free(uncomp_regex); if (err) { print_statusline(err); free(err); return; } pattern_valid = 1; match_pos = 0; fill_match_lines(0); while (match_pos < num_matches) { if ((int)match_lines[match_pos] > cur_fline) break; match_pos++; } if (option_mask32 & LESS_STATE_MATCH_BACKWARDS) match_pos--; /* It's possible that no matches are found yet. * goto_match() will read input looking for match, * if needed */ goto_match(match_pos); } #endif static void number_process(int first_digit) { unsigned i; int num; int keypress; char num_input[sizeof(int)*4]; /* more than enough */ num_input[0] = first_digit; /* Clear the current line, print a prompt, and then print the digit */ clear_line(); printf(":%c", first_digit); /* Receive input until a letter is given */ i = 1; while (i < sizeof(num_input)-1) { keypress = less_getch(i + 1); if ((unsigned)keypress > 255 || !isdigit(num_input[i])) break; num_input[i] = keypress; bb_putchar(keypress); i++; } num_input[i] = '\0'; num = bb_strtou(num_input, NULL, 10); /* on format error, num == -1 */ if (num < 1 || num > MAXLINES) { buffer_print(); return; } /* We now know the number and the letter entered, so we process them */ switch (keypress) { case KEYCODE_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015': buffer_down(num); break; case KEYCODE_UP: case 'b': case 'w': case 'y': case 'u': buffer_up(num); break; case 'g': case '<': case 'G': case '>': cur_fline = num + max_displayed_line; read_lines(); buffer_line(num - 1); break; case 'p': case '%': num = num * (max_fline / 100); /* + max_fline / 2; */ cur_fline = num + max_displayed_line; read_lines(); buffer_line(num); break; #if ENABLE_FEATURE_LESS_REGEXP case 'n': goto_match(match_pos + num); break; case '/': option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS; regex_process(); break; case '?': option_mask32 |= LESS_STATE_MATCH_BACKWARDS; regex_process(); break; #endif } } #if ENABLE_FEATURE_LESS_DASHCMD static void flag_change(void) { int keypress; clear_line(); bb_putchar('-'); keypress = less_getch(1); switch (keypress) { case 'M': option_mask32 ^= FLAG_M; break; case 'm': option_mask32 ^= FLAG_m; break; case 'E': option_mask32 ^= FLAG_E; break; case '~': option_mask32 ^= FLAG_TILDE; break; case 'S': option_mask32 ^= FLAG_S; buffer_fill_and_print(); break; #if ENABLE_FEATURE_LESS_LINENUMS case 'N': option_mask32 ^= FLAG_N; re_wrap(); buffer_fill_and_print(); break; #endif } } #ifdef BLOAT static void show_flag_status(void) { int keypress; int flag_val; clear_line(); bb_putchar('_'); keypress = less_getch(1); switch (keypress) { case 'M': flag_val = option_mask32 & FLAG_M; break; case 'm': flag_val = option_mask32 & FLAG_m; break; case '~': flag_val = option_mask32 & FLAG_TILDE; break; case 'N': flag_val = option_mask32 & FLAG_N; break; case 'E': flag_val = option_mask32 & FLAG_E; break; default: flag_val = 0; break; } clear_line(); printf(HIGHLIGHT"The status of the flag is: %u"NORMAL, flag_val != 0); } #endif #endif /* ENABLE_FEATURE_LESS_DASHCMD */ static void save_input_to_file(void) { const char *msg = ""; char *current_line; unsigned i; FILE *fp; print_statusline("Log file: "); current_line = less_gets(sizeof("Log file: ")-1); if (current_line[0]) { fp = fopen_for_write(current_line); if (!fp) { msg = "Error opening log file"; goto ret; } for (i = 0; i <= max_fline; i++) fprintf(fp, "%s\n", flines[i]); fclose(fp); msg = "Done"; } ret: print_statusline(msg); free(current_line); } #if ENABLE_FEATURE_LESS_MARKS static void add_mark(void) { int letter; print_statusline("Mark: "); letter = less_getch(sizeof("Mark: ") - 1); if (isalpha(letter)) { /* If we exceed 15 marks, start overwriting previous ones */ if (num_marks == 14) num_marks = 0; mark_lines[num_marks][0] = letter; mark_lines[num_marks][1] = cur_fline; num_marks++; } else { print_statusline("Invalid mark letter"); } } static void goto_mark(void) { int letter; int i; print_statusline("Go to mark: "); letter = less_getch(sizeof("Go to mark: ") - 1); clear_line(); if (isalpha(letter)) { for (i = 0; i <= num_marks; i++) if (letter == mark_lines[i][0]) { buffer_line(mark_lines[i][1]); break; } if (num_marks == 14 && letter != mark_lines[14][0]) print_statusline("Mark not set"); } else print_statusline("Invalid mark letter"); } #endif #if ENABLE_FEATURE_LESS_BRACKETS static char opp_bracket(char bracket) { switch (bracket) { case '{': case '[': /* '}' == '{' + 2. Same for '[' */ bracket++; case '(': /* ')' == '(' + 1 */ bracket++; break; case '}': case ']': bracket--; case ')': bracket--; break; }; return bracket; } static void match_right_bracket(char bracket) { unsigned i; if (strchr(flines[cur_fline], bracket) == NULL) { print_statusline("No bracket in top line"); return; } bracket = opp_bracket(bracket); for (i = cur_fline + 1; i < max_fline; i++) { if (strchr(flines[i], bracket) != NULL) { buffer_line(i); return; } } print_statusline("No matching bracket found"); } static void match_left_bracket(char bracket) { int i; if (strchr(flines[cur_fline + max_displayed_line], bracket) == NULL) { print_statusline("No bracket in bottom line"); return; } bracket = opp_bracket(bracket); for (i = cur_fline + max_displayed_line; i >= 0; i--) { if (strchr(flines[i], bracket) != NULL) { buffer_line(i); return; } } print_statusline("No matching bracket found"); } #endif /* FEATURE_LESS_BRACKETS */ static void keypress_process(int keypress) { switch (keypress) { case KEYCODE_DOWN: case 'e': case 'j': case 0x0d: buffer_down(1); break; case KEYCODE_UP: case 'y': case 'k': buffer_up(1); break; case KEYCODE_PAGEDOWN: case ' ': case 'z': case 'f': buffer_down(max_displayed_line + 1); break; case KEYCODE_PAGEUP: case 'w': case 'b': buffer_up(max_displayed_line + 1); break; case 'd': buffer_down((max_displayed_line + 1) / 2); break; case 'u': buffer_up((max_displayed_line + 1) / 2); break; case KEYCODE_HOME: case 'g': case 'p': case '<': case '%': buffer_line(0); break; case KEYCODE_END: case 'G': case '>': cur_fline = MAXLINES; read_lines(); buffer_line(cur_fline); break; case 'q': case 'Q': less_exit(EXIT_SUCCESS); break; #if ENABLE_FEATURE_LESS_MARKS case 'm': add_mark(); buffer_print(); break; case '\'': goto_mark(); buffer_print(); break; #endif case 'r': case 'R': /* TODO: (1) also bind ^R, ^L to this? * (2) re-measure window size? */ buffer_print(); break; /*case 'R': full_repaint(); break;*/ case 's': save_input_to_file(); break; case 'E': examine_file(); break; #if ENABLE_FEATURE_LESS_FLAGS case '=': m_status_print(); break; #endif #if ENABLE_FEATURE_LESS_REGEXP case '/': option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS; regex_process(); break; case 'n': goto_match(match_pos + 1); break; case 'N': goto_match(match_pos - 1); break; case '?': option_mask32 |= LESS_STATE_MATCH_BACKWARDS; regex_process(); break; #endif #if ENABLE_FEATURE_LESS_DASHCMD case '-': flag_change(); buffer_print(); break; #ifdef BLOAT case '_': show_flag_status(); break; #endif #endif #if ENABLE_FEATURE_LESS_BRACKETS case '{': case '(': case '[': match_right_bracket(keypress); break; case '}': case ')': case ']': match_left_bracket(keypress); break; #endif case ':': colon_process(); break; } if (isdigit(keypress)) number_process(keypress); } static void sig_catcher(int sig) { less_exit(- sig); } #if ENABLE_FEATURE_LESS_WINCH static void sigwinch_handler(int sig UNUSED_PARAM) { winch_counter++; } #endif int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int less_main(int argc, char **argv) { char *tty_name; int tty_fd; INIT_G(); /* TODO: -x: do not interpret backspace, -xx: tab also */ /* -xxx: newline also */ /* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */ getopt32(argv, "EMmN~I" IF_FEATURE_LESS_DASHCMD("S")); argc -= optind; argv += optind; num_files = argc; files = argv; /* Another popular pager, most, detects when stdout * is not a tty and turns into cat. This makes sense. */ if (!isatty(STDOUT_FILENO)) return bb_cat(argv); if (!num_files) { if (isatty(STDIN_FILENO)) { /* Just "less"? No args and no redirection? */ bb_error_msg("missing filename"); bb_show_usage(); } } else { filename = xstrdup(files[0]); } if (option_mask32 & FLAG_TILDE) empty_line_marker = ""; /* Some versions of less can survive w/o controlling tty, * try to do the same. This also allows to specify an alternative * tty via "less 1<>TTY". * We don't try to use STDOUT_FILENO directly, * since we want to set this fd to non-blocking mode, * and not bother with restoring it on exit. */ tty_name = xmalloc_ttyname(STDOUT_FILENO); if (tty_name) { tty_fd = open(tty_name, O_RDONLY); free(tty_name); if (tty_fd < 0) goto try_ctty; } else { /* Try controlling tty */ try_ctty: tty_fd = open(CURRENT_TTY, O_RDONLY); if (tty_fd < 0) return bb_cat(argv); } ndelay_on(tty_fd); kbd_fd = tty_fd; /* save in a global */ tcgetattr(kbd_fd, &term_orig); term_less = term_orig; term_less.c_lflag &= ~(ICANON | ECHO); term_less.c_iflag &= ~(IXON | ICRNL); /*term_less.c_oflag &= ~ONLCR;*/ term_less.c_cc[VMIN] = 1; term_less.c_cc[VTIME] = 0; IF_FEATURE_LESS_ASK_TERMINAL(G.winsize_err =) get_terminal_width_height(kbd_fd, &width, &max_displayed_line); /* 20: two tabstops + 4 */ if (width < 20 || max_displayed_line < 3) return bb_cat(argv); max_displayed_line -= 2; /* We want to restore term_orig on exit */ bb_signals(BB_FATAL_SIGS, sig_catcher); #if ENABLE_FEATURE_LESS_WINCH signal(SIGWINCH, sigwinch_handler); #endif buffer = xmalloc((max_displayed_line+1) * sizeof(char *)); reinitialize(); while (1) { int64_t keypress; #if ENABLE_FEATURE_LESS_WINCH while (WINCH_COUNTER) { again: winch_counter--; IF_FEATURE_LESS_ASK_TERMINAL(G.winsize_err =) get_terminal_width_height(kbd_fd, &width, &max_displayed_line); IF_FEATURE_LESS_ASK_TERMINAL(got_size:) /* 20: two tabstops + 4 */ if (width < 20) width = 20; if (max_displayed_line < 3) max_displayed_line = 3; max_displayed_line -= 2; free(buffer); buffer = xmalloc((max_displayed_line+1) * sizeof(char *)); /* Avoid re-wrap and/or redraw if we already know * we need to do it again. These ops are expensive */ if (WINCH_COUNTER) goto again; re_wrap(); if (WINCH_COUNTER) goto again; buffer_fill_and_print(); /* This took some time. Loop back and check, * were there another SIGWINCH? */ } keypress = less_getch(-1); /* -1: do not position cursor */ # if ENABLE_FEATURE_LESS_ASK_TERMINAL if ((int32_t)keypress == KEYCODE_CURSOR_POS) { uint32_t rc = (keypress >> 32); width = (rc & 0x7fff); max_displayed_line = ((rc >> 16) & 0x7fff); goto got_size; } # endif #else keypress = less_getch(-1); /* -1: do not position cursor */ #endif keypress_process(keypress); } } /* Help text of less version 418 is below. If you are implementing something, keeping key and/or command line switch compatibility is a good idea: SUMMARY OF LESS COMMANDS Commands marked with * may be preceded by a number, N. Notes in parentheses indicate the behavior if N is given. h H Display this help. q :q Q :Q ZZ Exit. --------------------------------------------------------------------------- MOVING e ^E j ^N CR * Forward one line (or N lines). y ^Y k ^K ^P * Backward one line (or N lines). f ^F ^V SPACE * Forward one window (or N lines). b ^B ESC-v * Backward one window (or N lines). z * Forward one window (and set window to N). w * Backward one window (and set window to N). ESC-SPACE * Forward one window, but don't stop at end-of-file. d ^D * Forward one half-window (and set half-window to N). u ^U * Backward one half-window (and set half-window to N). ESC-) RightArrow * Left one half screen width (or N positions). ESC-( LeftArrow * Right one half screen width (or N positions). F Forward forever; like "tail -f". r ^R ^L Repaint screen. R Repaint screen, discarding buffered input. --------------------------------------------------- Default "window" is the screen height. Default "half-window" is half of the screen height. --------------------------------------------------------------------------- SEARCHING /pattern * Search forward for (N-th) matching line. ?pattern * Search backward for (N-th) matching line. n * Repeat previous search (for N-th occurrence). N * Repeat previous search in reverse direction. ESC-n * Repeat previous search, spanning files. ESC-N * Repeat previous search, reverse dir. & spanning files. ESC-u Undo (toggle) search highlighting. --------------------------------------------------- Search patterns may be modified by one or more of: ^N or ! Search for NON-matching lines. ^E or * Search multiple files (pass thru END OF FILE). ^F or @ Start search at FIRST file (for /) or last file (for ?). ^K Highlight matches, but don't move (KEEP position). ^R Don't use REGULAR EXPRESSIONS. --------------------------------------------------------------------------- JUMPING g < ESC-< * Go to first line in file (or line N). G > ESC-> * Go to last line in file (or line N). p % * Go to beginning of file (or N percent into file). t * Go to the (N-th) next tag. T * Go to the (N-th) previous tag. { ( [ * Find close bracket } ) ]. } ) ] * Find open bracket { ( [. ESC-^F * Find close bracket . ESC-^B * Find open bracket --------------------------------------------------- Each "find close bracket" command goes forward to the close bracket matching the (N-th) open bracket in the top line. Each "find open bracket" command goes backward to the open bracket matching the (N-th) close bracket in the bottom line. m Mark the current position with . ' Go to a previously marked position. '' Go to the previous position. ^X^X Same as '. --------------------------------------------------- A mark is any upper-case or lower-case letter. Certain marks are predefined: ^ means beginning of the file $ means end of the file --------------------------------------------------------------------------- CHANGING FILES :e [file] Examine a new file. ^X^V Same as :e. :n * Examine the (N-th) next file from the command line. :p * Examine the (N-th) previous file from the command line. :x * Examine the first (or N-th) file from the command line. :d Delete the current file from the command line list. = ^G :f Print current file name. --------------------------------------------------------------------------- MISCELLANEOUS COMMANDS - Toggle a command line option [see OPTIONS below]. -- Toggle a command line option, by name. _ Display the setting of a command line option. __ Display the setting of an option, by name. +cmd Execute the less cmd each time a new file is examined. !command Execute the shell command with $SHELL. |Xcommand Pipe file between current pos & mark X to shell command. v Edit the current file with $VISUAL or $EDITOR. V Print version number of "less". --------------------------------------------------------------------------- OPTIONS Most options may be changed either on the command line, or from within less by using the - or -- command. Options may be given in one of two forms: either a single character preceded by a -, or a name preceeded by --. -? ........ --help Display help (from command line). -a ........ --search-skip-screen Forward search skips current screen. -b [N] .... --buffers=[N] Number of buffers. -B ........ --auto-buffers Don't automatically allocate buffers for pipes. -c ........ --clear-screen Repaint by clearing rather than scrolling. -d ........ --dumb Dumb terminal. -D [xn.n] . --color=xn.n Set screen colors. (MS-DOS only) -e -E .... --quit-at-eof --QUIT-AT-EOF Quit at end of file. -f ........ --force Force open non-regular files. -F ........ --quit-if-one-screen Quit if entire file fits on first screen. -g ........ --hilite-search Highlight only last match for searches. -G ........ --HILITE-SEARCH Don't highlight any matches for searches. -h [N] .... --max-back-scroll=[N] Backward scroll limit. -i ........ --ignore-case Ignore case in searches that do not contain uppercase. -I ........ --IGNORE-CASE Ignore case in all searches. -j [N] .... --jump-target=[N] Screen position of target lines. -J ........ --status-column Display a status column at left edge of screen. -k [file] . --lesskey-file=[file] Use a lesskey file. -L ........ --no-lessopen Ignore the LESSOPEN environment variable. -m -M .... --long-prompt --LONG-PROMPT Set prompt style. -n -N .... --line-numbers --LINE-NUMBERS Don't use line numbers. -o [file] . --log-file=[file] Copy to log file (standard input only). -O [file] . --LOG-FILE=[file] Copy to log file (unconditionally overwrite). -p [pattern] --pattern=[pattern] Start at pattern (from command line). -P [prompt] --prompt=[prompt] Define new prompt. -q -Q .... --quiet --QUIET --silent --SILENT Quiet the terminal bell. -r -R .... --raw-control-chars --RAW-CONTROL-CHARS Output "raw" control characters. -s ........ --squeeze-blank-lines Squeeze multiple blank lines. -S ........ --chop-long-lines Chop long lines. -t [tag] .. --tag=[tag] Find a tag. -T [tagsfile] --tag-file=[tagsfile] Use an alternate tags file. -u -U .... --underline-special --UNDERLINE-SPECIAL Change handling of backspaces. -V ........ --version Display the version number of "less". -w ........ --hilite-unread Highlight first new line after forward-screen. -W ........ --HILITE-UNREAD Highlight first new line after any forward movement. -x [N[,...]] --tabs=[N[,...]] Set tab stops. -X ........ --no-init Don't use termcap init/deinit strings. --no-keypad Don't use termcap keypad init/deinit strings. -y [N] .... --max-forw-scroll=[N] Forward scroll limit. -z [N] .... --window=[N] Set size of window. -" [c[c]] . --quotes=[c[c]] Set shell quote characters. -~ ........ --tilde Don't display tildes after end of file. -# [N] .... --shift=[N] Horizontal scroll amount (0 = one half screen width) --------------------------------------------------------------------------- LINE EDITING These keys can be used to edit text being entered on the "command line" at the bottom of the screen. RightArrow ESC-l Move cursor right one character. LeftArrow ESC-h Move cursor left one character. CNTL-RightArrow ESC-RightArrow ESC-w Move cursor right one word. CNTL-LeftArrow ESC-LeftArrow ESC-b Move cursor left one word. HOME ESC-0 Move cursor to start of line. END ESC-$ Move cursor to end of line. BACKSPACE Delete char to left of cursor. DELETE ESC-x Delete char under cursor. CNTL-BACKSPACE ESC-BACKSPACE Delete word to left of cursor. CNTL-DELETE ESC-DELETE ESC-X Delete word under cursor. CNTL-U ESC (MS-DOS only) Delete entire line. UpArrow ESC-k Retrieve previous command line. DownArrow ESC-j Retrieve next command line. TAB Complete filename & cycle. SHIFT-TAB ESC-TAB Complete filename & reverse cycle. CNTL-L Complete filename, list all. */ busybox-1.22.1/miscutils/man.c0000644000000000000000000001614612263563520014735 0ustar rootroot/* mini man implementation for busybox * Copyright (C) 2008 Denys Vlasenko * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define man_trivial_usage //usage: "[-aw] [MANPAGE]..." //usage:#define man_full_usage "\n\n" //usage: "Format and display manual page\n" //usage: "\n -a Display all pages" //usage: "\n -w Show page locations" #include "libbb.h" enum { OPT_a = 1, /* all */ OPT_w = 2, /* print path */ }; /* This is what I see on my desktop system being executed: ( echo ".ll 12.4i" echo ".nr LL 12.4i" echo ".pl 1100i" gunzip -c '/usr/man/man1/bzip2.1.gz' echo ".\\\"" echo ".pl \n(nlu+10" ) | gtbl | nroff -Tlatin1 -mandoc | less */ static int show_manpage(const char *pager, char *man_filename, int man, int level); static int run_pipe(const char *pager, char *man_filename, int man, int level) { char *cmd; /* Prevent man page link loops */ if (level > 10) return 0; if (access(man_filename, R_OK) != 0) return 0; if (option_mask32 & OPT_w) { puts(man_filename); return 1; } if (man) { /* man page, not cat page */ /* Is this a link to another manpage? */ /* The link has the following on the first line: */ /* ".so another_man_page" */ struct stat sb; char *line; char *linkname, *p; /* On my system: * man1/genhostid.1.gz: 203 bytes - smallest real manpage * man2/path_resolution.2.gz: 114 bytes - largest link */ xstat(man_filename, &sb); if (sb.st_size > 300) /* err on the safe side */ goto ordinary_manpage; line = xmalloc_open_zipped_read_close(man_filename, NULL); if (!line || strncmp(line, ".so ", 4) != 0) { free(line); goto ordinary_manpage; } /* Example: man2/path_resolution.2.gz contains * ".so man7/path_resolution.7\n" */ *strchrnul(line, '\n') = '\0'; linkname = skip_whitespace(&line[4]); /* If link has no slashes, we just replace man page name. * If link has slashes (however many), we go back *once*. * ".so zzz/ggg/page.3" does NOT go back two levels. */ p = strrchr(man_filename, '/'); if (!p) goto ordinary_manpage; *p = '\0'; if (strchr(linkname, '/')) { p = strrchr(man_filename, '/'); if (!p) goto ordinary_manpage; *p = '\0'; } /* Links do not have .gz extensions, even if manpage * is compressed */ man_filename = xasprintf("%s/%s", man_filename, linkname); free(line); /* Note: we leak "new" man_filename string as well... */ if (show_manpage(pager, man_filename, man, level + 1)) return 1; /* else: show the link, it's better than nothing */ } ordinary_manpage: close(STDIN_FILENO); open_zipped(man_filename); /* guaranteed to use fd 0 (STDIN_FILENO) */ /* "2>&1" is added so that nroff errors are shown in pager too. * Otherwise it may show just empty screen */ cmd = xasprintf( man ? "gtbl | nroff -Tlatin1 -mandoc 2>&1 | %s" : "%s", pager); system(cmd); free(cmd); return 1; } /* man_filename is of the form "/dir/dir/dir/name.s" */ static int show_manpage(const char *pager, char *man_filename, int man, int level) { #if SEAMLESS_COMPRESSION /* We leak this allocation... */ char *filename_with_zext = xasprintf("%s.lzma", man_filename); char *ext = strrchr(filename_with_zext, '.') + 1; #endif #if ENABLE_FEATURE_SEAMLESS_LZMA if (run_pipe(pager, filename_with_zext, man, level)) return 1; #endif #if ENABLE_FEATURE_SEAMLESS_XZ strcpy(ext, "xz"); if (run_pipe(pager, filename_with_zext, man, level)) return 1; #endif #if ENABLE_FEATURE_SEAMLESS_BZ2 strcpy(ext, "bz2"); if (run_pipe(pager, filename_with_zext, man, level)) return 1; #endif #if ENABLE_FEATURE_SEAMLESS_GZ strcpy(ext, "gz"); if (run_pipe(pager, filename_with_zext, man, level)) return 1; #endif return run_pipe(pager, man_filename, man, level); } int man_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int man_main(int argc UNUSED_PARAM, char **argv) { parser_t *parser; const char *pager; char **man_path_list; char *sec_list; char *cur_path, *cur_sect; int count_mp, cur_mp; int opt, not_found; char *token[2]; opt_complementary = "-1"; /* at least one argument */ opt = getopt32(argv, "+aw"); argv += optind; sec_list = xstrdup("0p:1:1p:2:3:3p:4:5:6:7:8:9"); /* Last valid man_path_list[] is [0x10] */ count_mp = 0; man_path_list = xzalloc(0x11 * sizeof(man_path_list[0])); man_path_list[0] = getenv("MANPATH"); if (!man_path_list[0]) /* default, may be overridden by /etc/man.conf */ man_path_list[0] = (char*)"/usr/man"; else count_mp++; pager = getenv("MANPAGER"); if (!pager) { pager = getenv("PAGER"); if (!pager) pager = "more"; } /* Parse man.conf[ig] or man_db.conf */ /* man version 1.6f uses man.config */ /* man-db implementation of man uses man_db.conf */ parser = config_open2("/etc/man.config", fopen_for_read); if (!parser) parser = config_open2("/etc/man.conf", fopen_for_read); if (!parser) parser = config_open2("/etc/man_db.conf", fopen_for_read); while (config_read(parser, token, 2, 0, "# \t", PARSE_NORMAL)) { if (!token[1]) continue; if (strcmp("MANDATORY_MANPATH"+10, token[0]) == 0 /* "MANPATH"? */ || strcmp("MANDATORY_MANPATH", token[0]) == 0 ) { char *path = token[1]; while (*path) { char *next_path; char **path_element; next_path = strchr(path, ':'); if (next_path) { *next_path = '\0'; if (next_path++ == path) /* "::"? */ goto next; } /* Do we already have path? */ path_element = man_path_list; while (*path_element) { if (strcmp(*path_element, path) == 0) goto skip; path_element++; } man_path_list = xrealloc_vector(man_path_list, 4, count_mp); man_path_list[count_mp] = xstrdup(path); count_mp++; /* man_path_list is NULL terminated */ /*man_path_list[count_mp] = NULL; - xrealloc_vector did it */ skip: if (!next_path) break; next: path = next_path; } } if (strcmp("MANSECT", token[0]) == 0) { free(sec_list); sec_list = xstrdup(token[1]); } } config_close(parser); not_found = 0; do { /* for each argv[] */ int found = 0; cur_mp = 0; if (strchr(*argv, '/')) { found = show_manpage(pager, *argv, /*man:*/ 1, 0); goto check_found; } while ((cur_path = man_path_list[cur_mp++]) != NULL) { /* for each MANPATH */ cur_sect = sec_list; do { /* for each section */ char *next_sect = strchrnul(cur_sect, ':'); int sect_len = next_sect - cur_sect; char *man_filename; int cat0man1 = 0; /* Search for cat, then man page */ while (cat0man1 < 2) { int found_here; man_filename = xasprintf("%s/%s%.*s/%s.%.*s", cur_path, "cat\0man" + (cat0man1 * 4), sect_len, cur_sect, *argv, sect_len, cur_sect); found_here = show_manpage(pager, man_filename, cat0man1, 0); found |= found_here; cat0man1 += found_here + 1; free(man_filename); } if (found && !(opt & OPT_a)) goto next_arg; cur_sect = next_sect; while (*cur_sect == ':') cur_sect++; } while (*cur_sect); } check_found: if (!found) { bb_error_msg("no manual entry for '%s'", *argv); not_found = 1; } next_arg: argv++; } while (*argv); return not_found; } busybox-1.22.1/miscutils/strings.c0000644000000000000000000000376512263563520015656 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * strings implementation for busybox * * Copyright 2003 Tito Ragusa * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define strings_trivial_usage //usage: "[-afo] [-n LEN] [FILE]..." //usage:#define strings_full_usage "\n\n" //usage: "Display printable strings in a binary file\n" //usage: "\n -a Scan whole file (default)" //usage: "\n -f Precede strings with filenames" //usage: "\n -n LEN At least LEN characters form a string (default 4)" //usage: "\n -o Precede strings with decimal offsets" #include "libbb.h" #define WHOLE_FILE 1 #define PRINT_NAME 2 #define PRINT_OFFSET 4 #define SIZE 8 int strings_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int strings_main(int argc UNUSED_PARAM, char **argv) { int n, c, status = EXIT_SUCCESS; unsigned count; off_t offset; FILE *file; char *string; const char *fmt = "%s: "; const char *n_arg = "4"; getopt32(argv, "afon:", &n_arg); /* -a is our default behaviour */ /*argc -= optind;*/ argv += optind; n = xatou_range(n_arg, 1, INT_MAX); string = xzalloc(n + 1); n--; if (!*argv) { fmt = "{%s}: "; *--argv = (char *)bb_msg_standard_input; } do { file = fopen_or_warn_stdin(*argv); if (!file) { status = EXIT_FAILURE; continue; } offset = 0; count = 0; do { c = fgetc(file); if (isprint_asciionly(c) || c == '\t') { if (count > n) { bb_putchar(c); } else { string[count] = c; if (count == n) { if (option_mask32 & PRINT_NAME) { printf(fmt, *argv); } if (option_mask32 & PRINT_OFFSET) { printf("%7"OFF_FMT"o ", offset - n); } fputs(string, stdout); } count++; } } else { if (count > n) { bb_putchar('\n'); } count = 0; } offset++; } while (c != EOF); fclose_if_not_stdin(file); } while (*++argv); if (ENABLE_FEATURE_CLEAN_UP) free(string); fflush_stdout_and_exit(status); } busybox-1.22.1/miscutils/raidautorun.c0000644000000000000000000000141212263563520016505 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * raidautorun implementation for busybox * * Copyright (C) 2006 Bernhard Reutner-Fischer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * */ //usage:#define raidautorun_trivial_usage //usage: "DEVICE" //usage:#define raidautorun_full_usage "\n\n" //usage: "Tell the kernel to automatically search and start RAID arrays" //usage: //usage:#define raidautorun_example_usage //usage: "$ raidautorun /dev/md0" #include "libbb.h" #include #include int raidautorun_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int raidautorun_main(int argc UNUSED_PARAM, char **argv) { xioctl(xopen(single_argv(argv), O_RDONLY), RAID_AUTORUN, NULL); return EXIT_SUCCESS; } busybox-1.22.1/miscutils/conspy.c0000644000000000000000000003341612263563520015474 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * A text-mode VNC like program for Linux virtual terminals. * * pascal.bellard@ads-lu.com * * Based on Russell Stuart's conspy.c * http://ace-host.stuart.id.au/russell/files/conspy.c * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //applet:IF_CONSPY(APPLET(conspy, BB_DIR_BIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_CONSPY) += conspy.o //config:config CONSPY //config: bool "conspy" //config: default y //config: select PLATFORM_LINUX //config: help //config: A text-mode VNC like program for Linux virtual terminals. //config: example: conspy NUM shared access to console num //config: or conspy -nd NUM screenshot of console num //config: or conspy -cs NUM poor man's GNU screen like //usage:#define conspy_trivial_usage //usage: "[-vcsndfFQ] [-x COL] [-y LINE] [CONSOLE_NO]" //usage:#define conspy_full_usage "\n\n" //usage: "A text-mode VNC like program for Linux virtual consoles." //usage: "\nTo exit, quickly press ESC 3 times." //usage: "\n" //usage: "\n -v Don't send keystrokes to the console" //usage: "\n -c Create missing /dev/{tty,vcsa}N" //usage: "\n -s Open a SHELL session" //usage: "\n -n Black & white" //usage: "\n -d Dump console to stdout" //usage: "\n -f Follow cursor" //usage: "\n -F Assume console is on a framebuffer device" //usage: "\n -Q Disable exit on ESC-ESC-ESC" //usage: "\n -x COL Starting column" //usage: "\n -y LINE Starting line" #include "libbb.h" #include #define ESC "\033" #define CURSOR_ON -1 #define CURSOR_OFF 1 #define DEV_TTY "/dev/tty" #define DEV_VCSA "/dev/vcsa" struct screen_info { unsigned char lines, cols, cursor_x, cursor_y; }; #define CHAR(x) (*(uint8_t*)(x)) #define ATTR(x) (((uint8_t*)(x))[1]) #define NEXT(x) ((x) += 2) #define DATA(x) (*(uint16_t*)(x)) struct globals { char* data; int size; int x, y; int kbd_fd; int ioerror_count; int key_count; int escape_count; int nokeys; int current; int first_line_offset; int last_attr; // cached local tty parameters unsigned width; unsigned height; unsigned col; unsigned line; smallint curoff; // unknown:0 cursor on:-1 cursor off:1 char attrbuf[sizeof("0;1;5;30;40m")]; // remote console struct screen_info remote; // saved local tty terminfo struct termios term_orig; char vcsa_name[sizeof(DEV_VCSA "NN")]; }; #define G (*ptr_to_globals) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ G.width = G.height = UINT_MAX; \ G.last_attr--; \ } while (0) enum { FLAG_v, // view only FLAG_c, // create device if need FLAG_Q, // never exit FLAG_s, // session FLAG_n, // no colors FLAG_d, // dump screen FLAG_f, // follow cursor FLAG_F, // framebuffer }; #define FLAG(x) (1 << FLAG_##x) #define BW (option_mask32 & FLAG(n)) static void putcsi(const char *s) { fputs(ESC"[", stdout); fputs(s, stdout); } static void clrscr(void) { // Home, clear till end of screen putcsi("1;1H" ESC"[J"); G.col = G.line = 0; } static void set_cursor(int state) { if (G.curoff != state) { G.curoff = state; putcsi("?25"); bb_putchar("h?l"[1 + state]); } } static void gotoxy(int col, int line) { if (G.col != col || G.line != line) { G.col = col; G.line = line; printf(ESC"[%u;%uH", line + 1, col + 1); } } static void cleanup(int code) NORETURN; static void cleanup(int code) { set_cursor(CURSOR_ON); tcsetattr(G.kbd_fd, TCSANOW, &G.term_orig); if (ENABLE_FEATURE_CLEAN_UP) { close(G.kbd_fd); } // Reset attributes if (!BW) putcsi("0m"); bb_putchar('\n'); if (code > EXIT_FAILURE) kill_myself_with_sig(code); exit(code); } static void screen_read_close(void) { unsigned i, j; int vcsa_fd; char *data; // Close & re-open vcsa in case they have swapped virtual consoles vcsa_fd = xopen(G.vcsa_name, O_RDONLY); xread(vcsa_fd, &G.remote, 4); i = G.remote.cols * 2; G.first_line_offset = G.y * i; i *= G.remote.lines; if (G.data == NULL) { G.size = i; G.data = xzalloc(2 * i); } if (G.size != i) { cleanup(EXIT_FAILURE); } data = G.data + G.current; xread(vcsa_fd, data, G.size); close(vcsa_fd); for (i = 0; i < G.remote.lines; i++) { for (j = 0; j < G.remote.cols; j++, NEXT(data)) { unsigned x = j - G.x; // if will catch j < G.x too unsigned y = i - G.y; // if will catch i < G.y too if (y >= G.height || x >= G.width) DATA(data) = 0; else { uint8_t ch = CHAR(data); if (ch < ' ') CHAR(data) = ch | 0x40; else if (ch > 0x7e) CHAR(data) = '?'; } } } } static void screen_char(char *data) { if (!BW) { uint8_t attr_diff; uint8_t attr = ATTR(data); if (option_mask32 & FLAG(F)) { attr >>= 1; } attr_diff = G.last_attr ^ attr; if (attr_diff) { // Attribute layout for VGA compatible text videobuffer: // blinking text // |red bkgd // ||green bkgd // |||blue bkgd // vvvv // 00000000 <- lsb bit on the right // bold text / text 8th bit // red text // green text // blue text // TODO: apparently framebuffer-based console uses different layout // (bug? attempt to get 8th text bit in better position?) // red bkgd // |green bkgd // ||blue bkgd // vvv // 00000000 <- lsb bit on the right // bold text // red text // green text // blue text // text 8th bit // converting RGB color bit triad to BGR: static const char color[8] = "04261537"; const uint8_t fg_mask = 0x07, bold_mask = 0x08; const uint8_t bg_mask = 0x70, blink_mask = 0x80; char *ptr; ptr = G.attrbuf; // (G.last_attr & ~attr) has 1 only where // G.last_attr has 1 but attr has 0. // Here we check whether we have transition // bold->non-bold or blink->non-blink: if (G.last_attr < 0 // initial value || ((G.last_attr & ~attr) & (bold_mask | blink_mask)) != 0 ) { *ptr++ = '0'; // "reset all attrs" *ptr++ = ';'; // must set fg & bg, maybe need to set bold or blink: attr_diff = attr | ~(bold_mask | blink_mask); } G.last_attr = attr; if (attr_diff & bold_mask) { *ptr++ = '1'; *ptr++ = ';'; } if (attr_diff & blink_mask) { *ptr++ = '5'; *ptr++ = ';'; } if (attr_diff & fg_mask) { *ptr++ = '3'; *ptr++ = color[attr & fg_mask]; *ptr++ = ';'; } if (attr_diff & bg_mask) { *ptr++ = '4'; *ptr++ = color[(attr & bg_mask) >> 4]; ptr++; // last attribute } if (ptr != G.attrbuf) { ptr[-1] = 'm'; *ptr = '\0'; putcsi(G.attrbuf); } } } putchar(CHAR(data)); G.col++; } static void screen_dump(void) { int linefeed_cnt; int line, col; int linecnt = G.remote.lines - G.y; char *data = G.data + G.current + G.first_line_offset; linefeed_cnt = 0; for (line = 0; line < linecnt && line < G.height; line++) { int space_cnt = 0; for (col = 0; col < G.remote.cols; col++, NEXT(data)) { unsigned tty_col = col - G.x; // if will catch col < G.x too if (tty_col >= G.width) continue; space_cnt++; if (BW && CHAR(data) == ' ') continue; while (linefeed_cnt != 0) { //bb_putchar('\r'); - tty driver does it for us bb_putchar('\n'); linefeed_cnt--; } while (--space_cnt) bb_putchar(' '); screen_char(data); } linefeed_cnt++; } } static void curmove(void) { unsigned cx = G.remote.cursor_x - G.x; unsigned cy = G.remote.cursor_y - G.y; int cursor = CURSOR_OFF; if (cx < G.width && cy < G.height) { gotoxy(cx, cy); cursor = CURSOR_ON; } set_cursor(cursor); } static void create_cdev_if_doesnt_exist(const char* name, dev_t dev) { int fd = open(name, O_RDONLY); if (fd != -1) close(fd); else if (errno == ENOENT) mknod(name, S_IFCHR | 0660, dev); } static NOINLINE void start_shell_in_child(const char* tty_name) { int pid = xvfork(); if (pid == 0) { struct termios termchild; const char *shell = get_shell_name(); signal(SIGHUP, SIG_IGN); // set tty as a controlling tty setsid(); // make tty to be input, output, error close(0); xopen(tty_name, O_RDWR); // uses fd 0 xdup2(0, 1); xdup2(0, 2); ioctl(0, TIOCSCTTY, 1); tcsetpgrp(0, getpid()); tcgetattr(0, &termchild); termchild.c_lflag |= ECHO; termchild.c_oflag |= ONLCR | XTABS; termchild.c_iflag |= ICRNL; termchild.c_iflag &= ~IXOFF; tcsetattr_stdin_TCSANOW(&termchild); execl(shell, shell, "-i", (char *) NULL); bb_simple_perror_msg_and_die(shell); } } int conspy_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int conspy_main(int argc UNUSED_PARAM, char **argv) { char tty_name[sizeof(DEV_TTY "NN")]; #define keybuf bb_common_bufsiz1 struct termios termbuf; unsigned opts; unsigned ttynum; int poll_timeout_ms; #if ENABLE_LONG_OPTS static const char getopt_longopts[] ALIGN1 = "viewonly\0" No_argument "v" "createdevice\0" No_argument "c" "neverquit\0" No_argument "Q" "session\0" No_argument "s" "nocolors\0" No_argument "n" "dump\0" No_argument "d" "follow\0" No_argument "f" "framebuffer\0" No_argument "F" ; applet_long_options = getopt_longopts; #endif INIT_G(); strcpy(G.vcsa_name, DEV_VCSA); opt_complementary = "x+:y+"; // numeric params opts = getopt32(argv, "vcQsndfFx:y:", &G.x, &G.y); argv += optind; ttynum = 0; if (argv[0]) { ttynum = xatou_range(argv[0], 0, 63); sprintf(G.vcsa_name + sizeof(DEV_VCSA)-1, "%u", ttynum); } sprintf(tty_name, "%s%u", DEV_TTY, ttynum); if (opts & FLAG(c)) { if ((opts & (FLAG(s)|FLAG(v))) != FLAG(v)) create_cdev_if_doesnt_exist(tty_name, makedev(4, ttynum)); create_cdev_if_doesnt_exist(G.vcsa_name, makedev(7, 128 + ttynum)); } if ((opts & FLAG(s)) && ttynum) { start_shell_in_child(tty_name); } screen_read_close(); if (opts & FLAG(d)) { screen_dump(); bb_putchar('\n'); return 0; } bb_signals(BB_FATAL_SIGS, cleanup); // All characters must be passed through to us unaltered G.kbd_fd = xopen(CURRENT_TTY, O_RDONLY); tcgetattr(G.kbd_fd, &G.term_orig); termbuf = G.term_orig; termbuf.c_iflag &= ~(BRKINT|INLCR|ICRNL|IXON|IXOFF|IUCLC|IXANY|IMAXBEL); //termbuf.c_oflag &= ~(OPOST); - no, we still want \n -> \r\n termbuf.c_lflag &= ~(ISIG|ICANON|ECHO); termbuf.c_cc[VMIN] = 1; termbuf.c_cc[VTIME] = 0; tcsetattr(G.kbd_fd, TCSANOW, &termbuf); poll_timeout_ms = 250; while (1) { struct pollfd pfd; int bytes_read; int i, j; char *data, *old; // in the first loop G.width = G.height = 0: refresh i = G.width; j = G.height; get_terminal_width_height(G.kbd_fd, &G.width, &G.height); if (option_mask32 & FLAG(f)) { int nx = G.remote.cursor_x - G.width + 1; int ny = G.remote.cursor_y - G.height + 1; if (G.remote.cursor_x < G.x) { G.x = G.remote.cursor_x; i = 0; // force refresh } if (nx > G.x) { G.x = nx; i = 0; // force refresh } if (G.remote.cursor_y < G.y) { G.y = G.remote.cursor_y; i = 0; // force refresh } if (ny > G.y) { G.y = ny; i = 0; // force refresh } } // Scan console data and redraw our tty where needed old = G.data + G.current; G.current = G.size - G.current; data = G.data + G.current; screen_read_close(); if (i != G.width || j != G.height) { clrscr(); screen_dump(); } else { // For each remote line old += G.first_line_offset; data += G.first_line_offset; for (i = G.y; i < G.remote.lines; i++) { char *first = NULL; // first char which needs updating char *last = last; // last char which needs updating unsigned iy = i - G.y; if (iy >= G.height) break; for (j = 0; j < G.remote.cols; j++, NEXT(old), NEXT(data)) { unsigned jx = j - G.x; // if will catch j >= G.x too if (jx < G.width && DATA(data) != DATA(old)) { last = data; if (!first) { first = data; gotoxy(jx, iy); } } } if (first) { // Rewrite updated data on the local screen for (; first <= last; NEXT(first)) screen_char(first); } } } curmove(); // Wait for local user keypresses fflush_all(); pfd.fd = G.kbd_fd; pfd.events = POLLIN; bytes_read = 0; switch (poll(&pfd, 1, poll_timeout_ms)) { char *k; case -1: if (errno != EINTR) goto abort; break; case 0: if (++G.nokeys >= 4) G.nokeys = G.escape_count = 0; break; default: // Read the keys pressed k = keybuf + G.key_count; bytes_read = read(G.kbd_fd, k, sizeof(keybuf) - G.key_count); if (bytes_read < 0) goto abort; // Do exit processing if (!(option_mask32 & FLAG(Q))) { for (i = 0; i < bytes_read; i++) { if (k[i] != '\033') G.escape_count = -1; if (++G.escape_count >= 3) cleanup(EXIT_SUCCESS); } } } poll_timeout_ms = 250; if (option_mask32 & FLAG(v)) continue; // Insert all keys pressed into the virtual console's input // buffer. Don't do this if the virtual console is in scan // code mode - giving ASCII characters to a program expecting // scan codes will confuse it. G.key_count += bytes_read; if (G.escape_count == 0) { int handle, result; long kbd_mode; handle = xopen(tty_name, O_WRONLY); result = ioctl(handle, KDGKBMODE, &kbd_mode); if (result >= 0) { char *p = keybuf; G.ioerror_count = 0; if (kbd_mode != K_XLATE && kbd_mode != K_UNICODE) { G.key_count = 0; // scan code mode } for (; G.key_count != 0; p++, G.key_count--) { result = ioctl(handle, TIOCSTI, p); if (result < 0) { memmove(keybuf, p, G.key_count); break; } // If there is an application on console which reacts // to keypresses, we need to make our first sleep // shorter to quickly redraw whatever it printed there. poll_timeout_ms = 20; } } // We sometimes get spurious IO errors on the TTY // as programs close and re-open it else if (errno != EIO || ++G.ioerror_count > 4) { if (ENABLE_FEATURE_CLEAN_UP) close(handle); goto abort; } // Close & re-open tty in case they have // swapped virtual consoles close(handle); } } /* while (1) */ abort: cleanup(EXIT_FAILURE); } busybox-1.22.1/miscutils/setsid.c0000644000000000000000000000364112263563520015451 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * setsid.c -- execute a command in a new session * Rick Sladkey * In the public domain. * * 1999-02-22 Arkadiusz Mickiewicz * - added Native Language Support * * 2001-01-18 John Fremlin * - fork in case we are process group leader * * 2004-11-12 Paul Fox * - busyboxed */ //usage:#define setsid_trivial_usage //usage: "PROG ARGS" //usage:#define setsid_full_usage "\n\n" //usage: "Run PROG in a new session. PROG will have no controlling terminal\n" //usage: "and will not be affected by keyboard signals (Ctrl-C etc).\n" //usage: "See setsid(2) for details." #include "libbb.h" int setsid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int setsid_main(int argc UNUSED_PARAM, char **argv) { if (!argv[1]) bb_show_usage(); /* setsid() is allowed only when we are not a process group leader. * Otherwise our PID serves as PGID of some existing process group * and cannot be used as PGID of a new process group. * * Example: setsid() below fails when run alone in interactive shell: * $ setsid PROG * because shell's child (setsid) is put in a new process group. * But doesn't fail if shell is not interactive * (and therefore doesn't create process groups for pipes), * or if setsid is not the first process in the process group: * $ true | setsid PROG * or if setsid is executed in backquotes (`setsid PROG`)... */ if (setsid() < 0) { pid_t pid = fork_or_rexec(argv); if (pid != 0) { /* parent */ /* TODO: * we can waitpid(pid, &status, 0) and then even * emulate exitcode, making the behavior consistent * in both forked and non forked cases. * However, the code is larger and upstream * does not do such trick. */ return EXIT_SUCCESS; } /* child */ /* now there should be no error: */ setsid(); } argv++; BB_EXECVP_or_die(argv); } busybox-1.22.1/miscutils/rfkill.c0000644000000000000000000000756412263563520015451 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * rfkill implementation for busybox * * Copyright (C) 2010 Malek Degachi * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //config:config RFKILL //config: bool "rfkill" //config: default n # doesn't build on Ubuntu 9.04 //config: select PLATFORM_LINUX //config: help //config: Enable/disable wireless devices. //config: //config: rfkill list : list all wireless devices //config: rfkill list bluetooth : list all bluetooth devices //config: rfkill list 1 : list device corresponding to the given index //config: rfkill block|unblock wlan : block/unblock all wlan(wifi) devices //config: //applet:IF_RFKILL(APPLET(rfkill, BB_DIR_USR_SBIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_RFKILL) += rfkill.o //usage:#define rfkill_trivial_usage //usage: "COMMAND [INDEX|TYPE]" //usage:#define rfkill_full_usage "\n\n" //usage: "Enable/disable wireless devices\n" //usage: "\nCommands:" //usage: "\n list [INDEX|TYPE] List current state" //usage: "\n block INDEX|TYPE Disable device" //usage: "\n unblock INDEX|TYPE Enable device" //usage: "\n" //usage: "\n TYPE: all, wlan(wifi), bluetooth, uwb(ultrawideband)," //usage: "\n wimax, wwan, gps, fm" #include "libbb.h" #include enum { OPT_b = (1 << 0), /* must be = 1 */ OPT_u = (1 << 1), OPT_l = (1 << 2), }; int rfkill_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int rfkill_main(int argc UNUSED_PARAM, char **argv) { struct rfkill_event event; const char *rf_name; int rf_fd; int mode; int rf_type; int rf_idx; unsigned rf_opt = 0; argv++; /* Must have one or two params */ if (!argv[0] || (argv[1] && argv[2])) bb_show_usage(); mode = O_RDWR | O_NONBLOCK; rf_name = argv[1]; if (strcmp(argv[0], "list") == 0) { rf_opt |= OPT_l; mode = O_RDONLY | O_NONBLOCK; } else if (strcmp(argv[0], "block") == 0 && rf_name) { rf_opt |= OPT_b; } else if (strcmp(argv[0], "unblock") == 0 && rf_name) { rf_opt |= OPT_u; } else bb_show_usage(); rf_type = RFKILL_TYPE_ALL; rf_idx = -1; if (rf_name) { static const char rfkill_types[] ALIGN1 = "all\0wlan\0bluetooth\0uwb\0wimax\0wwan\0gps\0fm\0"; if (strcmp(rf_name, "wifi") == 0) rf_name = "wlan"; if (strcmp(rf_name, "ultrawideband") == 0) rf_name = "uwb"; rf_type = index_in_strings(rfkill_types, rf_name); if (rf_type < 0) { rf_idx = xatoi_positive(rf_name); } } rf_fd = device_open("/dev/rfkill", mode); if (rf_fd < 0) bb_perror_msg_and_die("/dev/rfkill"); if (rf_opt & OPT_l) { while (full_read(rf_fd, &event, sizeof(event)) == RFKILL_EVENT_SIZE_V1) { parser_t *parser; char *tokens[2]; char rf_sysfs[sizeof("/sys/class/rfkill/rfkill%u/uevent") + sizeof(int)*3]; char *name, *type; if (rf_type && rf_type != event.type && rf_idx < 0) { continue; } if (rf_idx >= 0 && event.idx != rf_idx) { continue; } name = NULL; type = NULL; sprintf(rf_sysfs, "/sys/class/rfkill/rfkill%u/uevent", event.idx); parser = config_open2(rf_sysfs, fopen_for_read); while (config_read(parser, tokens, 2, 2, "\n=", PARSE_NORMAL)) { if (strcmp(tokens[0], "RFKILL_NAME") == 0) { name = xstrdup(tokens[1]); continue; } if (strcmp(tokens[0], "RFKILL_TYPE") == 0) { type = xstrdup(tokens[1]); continue; } } config_close(parser); printf("%u: %s: %s\n", event.idx, name, type); printf("\tSoft blocked: %s\n", event.soft ? "yes" : "no"); printf("\tHard blocked: %s\n", event.hard ? "yes" : "no"); free(name); free(type); } } else { memset(&event, 0, sizeof(event)); if (rf_type >= 0) { event.type = rf_type; event.op = RFKILL_OP_CHANGE_ALL; } if (rf_idx >= 0) { event.idx = rf_idx; event.op = RFKILL_OP_CHANGE; } /* Note: OPT_b == 1 */ event.soft = (rf_opt & OPT_b); xwrite(rf_fd, &event, sizeof(event)); } return EXIT_SUCCESS; } busybox-1.22.1/miscutils/rx.c0000644000000000000000000001411612263563520014606 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright: Copyright (C) 2001, Hewlett-Packard Company * Author: Christopher Hoover * Description: xmodem functionality for uploading of kernels * and the like * Created at: Thu Dec 20 01:58:08 PST 2001 * * xmodem functionality for uploading of kernels and the like * * Copyright (C) 2001 Hewlett-Packard Laboratories * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * This was originally written for blob and then adapted for busybox. */ //usage:#define rx_trivial_usage //usage: "FILE" //usage:#define rx_full_usage "\n\n" //usage: "Receive a file using the xmodem protocol" //usage: //usage:#define rx_example_usage //usage: "$ rx /tmp/foo\n" #include "libbb.h" #define SOH 0x01 #define STX 0x02 #define EOT 0x04 #define ACK 0x06 #define NAK 0x15 #define BS 0x08 #define PAD 0x1A /* Cf: http://www.textfiles.com/apple/xmodem http://www.phys.washington.edu/~belonis/xmodem/docxmodem.txt http://www.phys.washington.edu/~belonis/xmodem/docymodem.txt http://www.phys.washington.edu/~belonis/xmodem/modmprot.col */ #define TIMEOUT 1 #define TIMEOUT_LONG 10 #define MAXERRORS 10 #define read_fd STDIN_FILENO #define write_fd STDOUT_FILENO static int read_byte(unsigned timeout) { unsigned char buf; int n; alarm(timeout); /* NOT safe_read! We want ALRM to interrupt us */ n = read(read_fd, &buf, 1); alarm(0); if (n == 1) return buf; return -1; } static int receive(/*int read_fd, */int file_fd) { unsigned char blockBuf[1024]; unsigned blockLength = 0; unsigned errors = 0; unsigned wantBlockNo = 1; unsigned length = 0; int do_crc = 1; char reply_char; unsigned timeout = TIMEOUT_LONG; /* Flush pending input */ tcflush(read_fd, TCIFLUSH); /* Ask for CRC; if we get errors, we will go with checksum */ reply_char = 'C'; full_write(write_fd, &reply_char, 1); for (;;) { int blockBegin; int blockNo, blockNoOnesCompl; int cksum_or_crc; int expected; int i, j; blockBegin = read_byte(timeout); if (blockBegin < 0) goto timeout; /* If last block, remove padding */ if (blockBegin == EOT) { /* Data blocks can be padded with ^Z characters */ /* This code tries to detect and remove them */ if (blockLength >= 3 && blockBuf[blockLength - 1] == PAD && blockBuf[blockLength - 2] == PAD && blockBuf[blockLength - 3] == PAD ) { while (blockLength && blockBuf[blockLength - 1] == PAD ) { blockLength--; } } } /* Write previously received block */ errno = 0; if (full_write(file_fd, blockBuf, blockLength) != blockLength) { bb_perror_msg(bb_msg_write_error); goto fatal; } timeout = TIMEOUT; reply_char = NAK; switch (blockBegin) { case SOH: case STX: break; case EOT: reply_char = ACK; full_write(write_fd, &reply_char, 1); return length; default: goto error; } /* Block no */ blockNo = read_byte(TIMEOUT); if (blockNo < 0) goto timeout; /* Block no, in one's complement form */ blockNoOnesCompl = read_byte(TIMEOUT); if (blockNoOnesCompl < 0) goto timeout; if (blockNo != (255 - blockNoOnesCompl)) { bb_error_msg("bad block ones compl"); goto error; } blockLength = (blockBegin == SOH) ? 128 : 1024; for (i = 0; i < blockLength; i++) { int cc = read_byte(TIMEOUT); if (cc < 0) goto timeout; blockBuf[i] = cc; } cksum_or_crc = read_byte(TIMEOUT); if (cksum_or_crc < 0) goto timeout; if (do_crc) { cksum_or_crc = (cksum_or_crc << 8) | read_byte(TIMEOUT); if (cksum_or_crc < 0) goto timeout; } if (blockNo == ((wantBlockNo - 1) & 0xff)) { /* a repeat of the last block is ok, just ignore it. */ /* this also ignores the initial block 0 which is */ /* meta data. */ blockLength = 0; goto next; } if (blockNo != (wantBlockNo & 0xff)) { bb_error_msg("unexpected block no, 0x%08x, expecting 0x%08x", blockNo, wantBlockNo); goto error; } expected = 0; if (do_crc) { for (i = 0; i < blockLength; i++) { expected = expected ^ blockBuf[i] << 8; for (j = 0; j < 8; j++) { if (expected & 0x8000) expected = (expected << 1) ^ 0x1021; else expected = (expected << 1); } } expected &= 0xffff; } else { for (i = 0; i < blockLength; i++) expected += blockBuf[i]; expected &= 0xff; } if (cksum_or_crc != expected) { bb_error_msg(do_crc ? "crc error, expected 0x%04x, got 0x%04x" : "checksum error, expected 0x%02x, got 0x%02x", expected, cksum_or_crc); goto error; } wantBlockNo++; length += blockLength; next: errors = 0; reply_char = ACK; full_write(write_fd, &reply_char, 1); continue; error: timeout: blockLength = 0; errors++; if (errors == MAXERRORS) { /* Abort */ /* If were asking for crc, try again w/o crc */ if (reply_char == 'C') { reply_char = NAK; errors = 0; do_crc = 0; goto timeout; } bb_error_msg("too many errors; giving up"); fatal: /* 5 CAN followed by 5 BS. Don't try too hard... */ safe_write(write_fd, "\030\030\030\030\030\010\010\010\010\010", 10); return -1; } /* Flush pending input */ tcflush(read_fd, TCIFLUSH); full_write(write_fd, &reply_char, 1); } /* for (;;) */ } static void sigalrm_handler(int UNUSED_PARAM signum) { } int rx_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int rx_main(int argc UNUSED_PARAM, char **argv) { struct termios tty, orig_tty; int termios_err; int file_fd; int n; /* Disabled by vda: * why we can't receive from stdin? Why we *require* * controlling tty?? */ /*read_fd = xopen(CURRENT_TTY, O_RDWR);*/ file_fd = xopen(single_argv(argv), O_RDWR|O_CREAT|O_TRUNC); termios_err = tcgetattr(read_fd, &tty); if (termios_err == 0) { orig_tty = tty; cfmakeraw(&tty); tcsetattr(read_fd, TCSAFLUSH, &tty); } /* No SA_RESTART: we want ALRM to interrupt read() */ signal_no_SA_RESTART_empty_mask(SIGALRM, sigalrm_handler); n = receive(file_fd); if (termios_err == 0) tcsetattr(read_fd, TCSAFLUSH, &orig_tty); if (ENABLE_FEATURE_CLEAN_UP) close(file_fd); fflush_stdout_and_exit(n >= 0); } busybox-1.22.1/miscutils/readahead.c0000644000000000000000000000203112263563520016044 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * readahead implementation for busybox * * Preloads the given files in RAM, to reduce access time. * Does this by calling the readahead(2) system call. * * Copyright (C) 2006 Michael Opdenacker * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define readahead_trivial_usage //usage: "[FILE]..." //usage:#define readahead_full_usage "\n\n" //usage: "Preload FILEs to RAM" #include "libbb.h" int readahead_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int readahead_main(int argc UNUSED_PARAM, char **argv) { int retval = EXIT_SUCCESS; if (!argv[1]) { bb_show_usage(); } while (*++argv) { int fd = open_or_warn(*argv, O_RDONLY); if (fd >= 0) { off_t len; int r; /* fdlength was reported to be unreliable - use seek */ len = xlseek(fd, 0, SEEK_END); xlseek(fd, 0, SEEK_SET); r = readahead(fd, 0, len); close(fd); if (r >= 0) continue; } retval = EXIT_FAILURE; } return retval; } busybox-1.22.1/miscutils/devfsd.c0000644000000000000000000015434412263563520015440 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* devfsd implementation for busybox Copyright (C) 2003 by Tito Ragusa Busybox version is based on some previous work and ideas Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it> devfsd.c Main file for devfsd (devfs daemon for Linux). Copyright (C) 1998-2002 Richard Gooch devfsd.h Header file for devfsd (devfs daemon for Linux). Copyright (C) 1998-2000 Richard Gooch compat_name.c Compatibility name file for devfsd (build compatibility names). Copyright (C) 1998-2002 Richard Gooch expression.c This code provides Borne Shell-like expression expansion. Copyright (C) 1997-1999 Richard Gooch This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Richard Gooch may be reached by email at rgooch@atnf.csiro.au The postal address is: Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. */ //usage:#define devfsd_trivial_usage //usage: "mntpnt [-v]" IF_DEVFSD_FG_NP("[-fg][-np]") //usage:#define devfsd_full_usage "\n\n" //usage: "Manage devfs permissions and old device name symlinks\n" //usage: "\n mntpnt The mount point where devfs is mounted" //usage: "\n -v Print the protocol version numbers for devfsd" //usage: "\n and the kernel-side protocol version and exit" //usage: IF_DEVFSD_FG_NP( //usage: "\n -fg Run in foreground" //usage: "\n -np Exit after parsing the configuration file" //usage: "\n and processing synthetic REGISTER events," //usage: "\n don't poll for events" //usage: ) #include "libbb.h" #include "xregex.h" #include #include #include /* Various defines taken from linux/major.h */ #define IDE0_MAJOR 3 #define IDE1_MAJOR 22 #define IDE2_MAJOR 33 #define IDE3_MAJOR 34 #define IDE4_MAJOR 56 #define IDE5_MAJOR 57 #define IDE6_MAJOR 88 #define IDE7_MAJOR 89 #define IDE8_MAJOR 90 #define IDE9_MAJOR 91 /* Various defines taken from linux/devfs_fs.h */ #define DEVFSD_PROTOCOL_REVISION_KERNEL 5 #define DEVFSD_IOCTL_BASE 'd' /* These are the various ioctls */ #define DEVFSDIOC_GET_PROTO_REV _IOR(DEVFSD_IOCTL_BASE, 0, int) #define DEVFSDIOC_SET_EVENT_MASK _IOW(DEVFSD_IOCTL_BASE, 2, int) #define DEVFSDIOC_RELEASE_EVENT_QUEUE _IOW(DEVFSD_IOCTL_BASE, 3, int) #define DEVFSDIOC_SET_CONFIG_DEBUG_MASK _IOW(DEVFSD_IOCTL_BASE, 4, int) #define DEVFSD_NOTIFY_REGISTERED 0 #define DEVFSD_NOTIFY_UNREGISTERED 1 #define DEVFSD_NOTIFY_ASYNC_OPEN 2 #define DEVFSD_NOTIFY_CLOSE 3 #define DEVFSD_NOTIFY_LOOKUP 4 #define DEVFSD_NOTIFY_CHANGE 5 #define DEVFSD_NOTIFY_CREATE 6 #define DEVFSD_NOTIFY_DELETE 7 #define DEVFS_PATHLEN 1024 /* Never change this otherwise the binary interface will change */ struct devfsd_notify_struct { /* Use native C types to ensure same types in kernel and user space */ unsigned int type; /* DEVFSD_NOTIFY_* value */ unsigned int mode; /* Mode of the inode or device entry */ unsigned int major; /* Major number of device entry */ unsigned int minor; /* Minor number of device entry */ unsigned int uid; /* Uid of process, inode or device entry */ unsigned int gid; /* Gid of process, inode or device entry */ unsigned int overrun_count; /* Number of lost events */ unsigned int namelen; /* Number of characters not including '\0' */ /* The device name MUST come last */ char devname[DEVFS_PATHLEN]; /* This will be '\0' terminated */ }; #define BUFFER_SIZE 16384 #define DEVFSD_VERSION "1.3.25" #define CONFIG_FILE "/etc/devfsd.conf" #define MODPROBE "/sbin/modprobe" #define MODPROBE_SWITCH_1 "-k" #define MODPROBE_SWITCH_2 "-C" #define CONFIG_MODULES_DEVFS "/etc/modules.devfs" #define MAX_ARGS (6 + 1) #define MAX_SUBEXPR 10 #define STRING_LENGTH 255 /* for get_uid_gid() */ #define UID 0 #define GID 1 /* fork_and_execute() */ # define DIE 1 # define NO_DIE 0 /* for dir_operation() */ #define RESTORE 0 #define SERVICE 1 #define READ_CONFIG 2 /* Update only after changing code to reflect new protocol */ #define DEVFSD_PROTOCOL_REVISION_DAEMON 5 /* Compile-time check */ #if DEVFSD_PROTOCOL_REVISION_KERNEL != DEVFSD_PROTOCOL_REVISION_DAEMON #error protocol version mismatch. Update your kernel headers #endif #define AC_PERMISSIONS 0 #define AC_MODLOAD 1 #define AC_EXECUTE 2 #define AC_MFUNCTION 3 /* not supported by busybox */ #define AC_CFUNCTION 4 /* not supported by busybox */ #define AC_COPY 5 #define AC_IGNORE 6 #define AC_MKOLDCOMPAT 7 #define AC_MKNEWCOMPAT 8 #define AC_RMOLDCOMPAT 9 #define AC_RMNEWCOMPAT 10 #define AC_RESTORE 11 struct permissions_type { mode_t mode; uid_t uid; gid_t gid; }; struct execute_type { char *argv[MAX_ARGS + 1]; /* argv[0] must always be the programme */ }; struct copy_type { const char *source; const char *destination; }; struct action_type { unsigned int what; unsigned int when; }; struct config_entry_struct { struct action_type action; regex_t preg; union { struct permissions_type permissions; struct execute_type execute; struct copy_type copy; } u; struct config_entry_struct *next; }; struct get_variable_info { const struct devfsd_notify_struct *info; const char *devname; char devpath[STRING_LENGTH]; }; static void dir_operation(int , const char * , int, unsigned long*); static void service(struct stat statbuf, char *path); static int st_expr_expand(char *, unsigned, const char *, const char *(*)(const char *, void *), void *); static const char *get_old_name(const char *, unsigned, char *, unsigned, unsigned); static int mksymlink(const char *oldpath, const char *newpath); static void read_config_file(char *path, int optional, unsigned long *event_mask); static void process_config_line(const char *, unsigned long *); static int do_servicing(int, unsigned long); static void service_name(const struct devfsd_notify_struct *); static void action_permissions(const struct devfsd_notify_struct *, const struct config_entry_struct *); static void action_execute(const struct devfsd_notify_struct *, const struct config_entry_struct *, const regmatch_t *, unsigned); static void action_modload(const struct devfsd_notify_struct *info, const struct config_entry_struct *entry); static void action_copy(const struct devfsd_notify_struct *, const struct config_entry_struct *, const regmatch_t *, unsigned); static void action_compat(const struct devfsd_notify_struct *, unsigned); static void free_config(void); static void restore(char *spath, struct stat source_stat, int rootlen); static int copy_inode(const char *, const struct stat *, mode_t, const char *, const struct stat *); static mode_t get_mode(const char *); static void signal_handler(int); static const char *get_variable(const char *, void *); static int make_dir_tree(const char *); static int expand_expression(char *, unsigned, const char *, const char *(*)(const char *, void *), void *, const char *, const regmatch_t *, unsigned); static void expand_regexp(char *, size_t, const char *, const char *, const regmatch_t *, unsigned); static const char *expand_variable( char *, unsigned, unsigned *, const char *, const char *(*)(const char *, void *), void *); static const char *get_variable_v2(const char *, const char *(*)(const char *, void *), void *); static char get_old_ide_name(unsigned, unsigned); static char *write_old_sd_name(char *, unsigned, unsigned, const char *); /* busybox functions */ static int get_uid_gid(int flag, const char *string); static void safe_memcpy(char * dest, const char * src, int len); static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, const char *ptr); static unsigned int scan_dev_name(const char *d, unsigned int n, const char *ptr); /* Structs and vars */ static struct config_entry_struct *first_config = NULL; static struct config_entry_struct *last_config = NULL; static char *mount_point = NULL; static volatile int caught_signal = FALSE; static volatile int caught_sighup = FALSE; static struct initial_symlink_struct { const char *dest; const char *name; } initial_symlinks[] = { {"/proc/self/fd", "fd"}, {"fd/0", "stdin"}, {"fd/1", "stdout"}, {"fd/2", "stderr"}, {NULL, NULL}, }; static struct event_type { unsigned int type; /* The DEVFSD_NOTIFY_* value */ const char *config_name; /* The name used in the config file */ } event_types[] = { {DEVFSD_NOTIFY_REGISTERED, "REGISTER"}, {DEVFSD_NOTIFY_UNREGISTERED, "UNREGISTER"}, {DEVFSD_NOTIFY_ASYNC_OPEN, "ASYNC_OPEN"}, {DEVFSD_NOTIFY_CLOSE, "CLOSE"}, {DEVFSD_NOTIFY_LOOKUP, "LOOKUP"}, {DEVFSD_NOTIFY_CHANGE, "CHANGE"}, {DEVFSD_NOTIFY_CREATE, "CREATE"}, {DEVFSD_NOTIFY_DELETE, "DELETE"}, {0xffffffff, NULL} }; /* Busybox messages */ static const char bb_msg_proto_rev[] ALIGN1 = "protocol revision"; static const char bb_msg_bad_config[] ALIGN1 = "bad %s config file: %s"; static const char bb_msg_small_buffer[] ALIGN1 = "buffer too small"; static const char bb_msg_variable_not_found[] ALIGN1 = "variable: %s not found"; /* Busybox stuff */ #if ENABLE_DEVFSD_VERBOSE || ENABLE_DEBUG #define info_logger(p, fmt, args...) bb_info_msg(fmt, ## args) #define msg_logger(p, fmt, args...) bb_error_msg(fmt, ## args) #define msg_logger_and_die(p, fmt, args...) bb_error_msg_and_die(fmt, ## args) #define error_logger(p, fmt, args...) bb_perror_msg(fmt, ## args) #define error_logger_and_die(p, fmt, args...) bb_perror_msg_and_die(fmt, ## args) #else #define info_logger(p, fmt, args...) #define msg_logger(p, fmt, args...) #define msg_logger_and_die(p, fmt, args...) exit(EXIT_FAILURE) #define error_logger(p, fmt, args...) #define error_logger_and_die(p, fmt, args...) exit(EXIT_FAILURE) #endif static void safe_memcpy(char *dest, const char *src, int len) { memcpy(dest , src, len); dest[len] = '\0'; } static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, const char *ptr) { if (d[n - 4] == 'd' && d[n - 3] == 'i' && d[n - 2] == 's' && d[n - 1] == 'c') return 2 + addendum; if (d[n - 2] == 'c' && d[n - 1] == 'd') return 3 + addendum; if (ptr[0] == 'p' && ptr[1] == 'a' && ptr[2] == 'r' && ptr[3] == 't') return 4 + addendum; if (ptr[n - 2] == 'm' && ptr[n - 1] == 't') return 5 + addendum; return 0; } static unsigned int scan_dev_name(const char *d, unsigned int n, const char *ptr) { if (d[0] == 's' && d[1] == 'c' && d[2] == 's' && d[3] == 'i' && d[4] == '/') { if (d[n - 7] == 'g' && d[n - 6] == 'e' && d[n - 5] == 'n' && d[n - 4] == 'e' && d[n - 3] == 'r' && d[n - 2] == 'i' && d[n - 1] == 'c' ) return 1; return scan_dev_name_common(d, n, 0, ptr); } if (d[0] == 'i' && d[1] == 'd' && d[2] == 'e' && d[3] == '/' && d[4] == 'h' && d[5] == 'o' && d[6] == 's' && d[7] == 't' ) return scan_dev_name_common(d, n, 4, ptr); if (d[0] == 's' && d[1] == 'b' && d[2] == 'p' && d[3] == '/') return 10; if (d[0] == 'v' && d[1] == 'c' && d[2] == 'c' && d[3] == '/') return 11; if (d[0] == 'p' && d[1] == 't' && d[2] == 'y' && d[3] == '/') return 12; return 0; } /* Public functions follow */ int devfsd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int devfsd_main(int argc, char **argv) { int print_version = FALSE; int do_daemon = TRUE; int no_polling = FALSE; int do_scan; int fd, proto_rev, count; unsigned long event_mask = 0; struct sigaction new_action; struct initial_symlink_struct *curr; if (argc < 2) bb_show_usage(); for (count = 2; count < argc; ++count) { if (argv[count][0] == '-') { if (argv[count][1] == 'v' && !argv[count][2]) /* -v */ print_version = TRUE; else if (ENABLE_DEVFSD_FG_NP && argv[count][1] == 'f' && argv[count][2] == 'g' && !argv[count][3]) /* -fg */ do_daemon = FALSE; else if (ENABLE_DEVFSD_FG_NP && argv[count][1] == 'n' && argv[count][2] == 'p' && !argv[count][3]) /* -np */ no_polling = TRUE; else bb_show_usage(); } } mount_point = bb_simplify_path(argv[1]); xchdir(mount_point); fd = xopen(".devfsd", O_RDONLY); close_on_exec_on(fd); xioctl(fd, DEVFSDIOC_GET_PROTO_REV, &proto_rev); /*setup initial entries */ for (curr = initial_symlinks; curr->dest != NULL; ++curr) symlink(curr->dest, curr->name); /* NB: The check for CONFIG_FILE is done in read_config_file() */ if (print_version || (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev)) { printf("%s v%s\nDaemon %s:\t%d\nKernel-side %s:\t%d\n", applet_name, DEVFSD_VERSION, bb_msg_proto_rev, DEVFSD_PROTOCOL_REVISION_DAEMON, bb_msg_proto_rev, proto_rev); if (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev) bb_error_msg_and_die("%s mismatch!", bb_msg_proto_rev); exit(EXIT_SUCCESS); /* -v */ } /* Tell kernel we are special(i.e. we get to see hidden entries) */ xioctl(fd, DEVFSDIOC_SET_EVENT_MASK, 0); /* Set up SIGHUP and SIGUSR1 handlers */ sigemptyset(&new_action.sa_mask); new_action.sa_flags = 0; new_action.sa_handler = signal_handler; sigaction_set(SIGHUP, &new_action); sigaction_set(SIGUSR1, &new_action); printf("%s v%s started for %s\n", applet_name, DEVFSD_VERSION, mount_point); /* Set umask so that mknod(2), open(2) and mkdir(2) have complete control over permissions */ umask(0); read_config_file((char*)CONFIG_FILE, FALSE, &event_mask); /* Do the scan before forking, so that boot scripts see the finished product */ dir_operation(SERVICE, mount_point, 0, NULL); if (ENABLE_DEVFSD_FG_NP && no_polling) exit(EXIT_SUCCESS); if (ENABLE_DEVFSD_VERBOSE || ENABLE_DEBUG) logmode = LOGMODE_BOTH; else if (do_daemon == TRUE) logmode = LOGMODE_SYSLOG; /* This is the default */ /*else logmode = LOGMODE_STDIO; */ if (do_daemon) { /* Release so that the child can grab it */ xioctl(fd, DEVFSDIOC_RELEASE_EVENT_QUEUE, 0); bb_daemonize_or_rexec(0, argv); } else if (ENABLE_DEVFSD_FG_NP) { setpgid(0, 0); /* Become process group leader */ } while (TRUE) { do_scan = do_servicing(fd, event_mask); free_config(); read_config_file((char*)CONFIG_FILE, FALSE, &event_mask); if (do_scan) dir_operation(SERVICE, mount_point, 0, NULL); } if (ENABLE_FEATURE_CLEAN_UP) free(mount_point); } /* End Function main */ /* Private functions follow */ static void read_config_file(char *path, int optional, unsigned long *event_mask) /* [SUMMARY] Read a configuration database. The path to read the database from. If this is a directory, all entries in that directory will be read(except hidden entries). If TRUE, the routine will silently ignore a missing config file. The event mask is written here. This is not initialised. [RETURNS] Nothing. */ { struct stat statbuf; FILE *fp; char buf[STRING_LENGTH]; char *line = NULL; char *p; if (stat(path, &statbuf) == 0) { /* Don't read 0 length files: ignored */ /*if (statbuf.st_size == 0) return;*/ if (S_ISDIR(statbuf.st_mode)) { p = bb_simplify_path(path); dir_operation(READ_CONFIG, p, 0, event_mask); free(p); return; } fp = fopen_for_read(path); if (fp != NULL) { while (fgets(buf, STRING_LENGTH, fp) != NULL) { /* Skip whitespace */ line = buf; line = skip_whitespace(line); if (line[0] == '\0' || line[0] == '#') continue; process_config_line(line, event_mask); } fclose(fp); } else { goto read_config_file_err; } } else { read_config_file_err: if (optional == 0 && errno == ENOENT) error_logger_and_die(LOG_ERR, "read config file: %s", path); } } /* End Function read_config_file */ static void process_config_line(const char *line, unsigned long *event_mask) /* [SUMMARY] Process a line from a configuration file. The configuration line. The event mask is written here. This is not initialised. [RETURNS] Nothing. */ { int num_args, count; struct config_entry_struct *new; char p[MAX_ARGS][STRING_LENGTH]; char when[STRING_LENGTH], what[STRING_LENGTH]; char name[STRING_LENGTH]; const char *msg = ""; char *ptr; int i; /* !!!! Only Uppercase Keywords in devsfd.conf */ static const char options[] ALIGN1 = "CLEAR_CONFIG\0""INCLUDE\0""OPTIONAL_INCLUDE\0" "RESTORE\0""PERMISSIONS\0""MODLOAD\0""EXECUTE\0" "COPY\0""IGNORE\0""MKOLDCOMPAT\0""MKNEWCOMPAT\0" "RMOLDCOMPAT\0""RMNEWCOMPAT\0"; for (count = 0; count < MAX_ARGS; ++count) p[count][0] = '\0'; num_args = sscanf(line, "%s %s %s %s %s %s %s %s %s %s", when, name, what, p[0], p[1], p[2], p[3], p[4], p[5], p[6]); i = index_in_strings(options, when); /* "CLEAR_CONFIG" */ if (i == 0) { free_config(); *event_mask = 0; return; } if (num_args < 2) goto process_config_line_err; /* "INCLUDE" & "OPTIONAL_INCLUDE" */ if (i == 1 || i == 2) { st_expr_expand(name, STRING_LENGTH, name, get_variable, NULL); info_logger(LOG_INFO, "%sinclude: %s", (toupper(when[0]) == 'I') ? "": "optional_", name); read_config_file(name, (toupper(when[0]) == 'I') ? FALSE : TRUE, event_mask); return; } /* "RESTORE" */ if (i == 3) { dir_operation(RESTORE, name, strlen(name),NULL); return; } if (num_args < 3) goto process_config_line_err; new = xzalloc(sizeof *new); for (count = 0; event_types[count].config_name != NULL; ++count) { if (strcasecmp(when, event_types[count].config_name) != 0) continue; new->action.when = event_types[count].type; break; } if (event_types[count].config_name == NULL) { msg = "WHEN in"; goto process_config_line_err; } i = index_in_strings(options, what); switch (i) { case 4: /* "PERMISSIONS" */ new->action.what = AC_PERMISSIONS; /* Get user and group */ ptr = strchr(p[0], '.'); if (ptr == NULL) { msg = "UID.GID"; goto process_config_line_err; /*"missing '.' in UID.GID"*/ } *ptr++ = '\0'; new->u.permissions.uid = get_uid_gid(UID, p[0]); new->u.permissions.gid = get_uid_gid(GID, ptr); /* Get mode */ new->u.permissions.mode = get_mode(p[1]); break; case 5: /* MODLOAD */ /*This action will pass "/dev/$devname"(i.e. "/dev/" prefixed to the device name) to the module loading facility. In addition, the /etc/modules.devfs configuration file is used.*/ if (ENABLE_DEVFSD_MODLOAD) new->action.what = AC_MODLOAD; break; case 6: /* EXECUTE */ new->action.what = AC_EXECUTE; num_args -= 3; for (count = 0; count < num_args; ++count) new->u.execute.argv[count] = xstrdup(p[count]); new->u.execute.argv[num_args] = NULL; break; case 7: /* COPY */ new->action.what = AC_COPY; num_args -= 3; if (num_args != 2) goto process_config_line_err; /* missing path and function in line */ new->u.copy.source = xstrdup(p[0]); new->u.copy.destination = xstrdup(p[1]); break; case 8: /* IGNORE */ /* FALLTROUGH */ case 9: /* MKOLDCOMPAT */ /* FALLTROUGH */ case 10: /* MKNEWCOMPAT */ /* FALLTROUGH */ case 11:/* RMOLDCOMPAT */ /* FALLTROUGH */ case 12: /* RMNEWCOMPAT */ /* AC_IGNORE 6 AC_MKOLDCOMPAT 7 AC_MKNEWCOMPAT 8 AC_RMOLDCOMPAT 9 AC_RMNEWCOMPAT 10*/ new->action.what = i - 2; break; default: msg = "WHAT in"; goto process_config_line_err; /*esac*/ } /* switch (i) */ xregcomp(&new->preg, name, REG_EXTENDED); *event_mask |= 1 << new->action.when; new->next = NULL; if (first_config == NULL) first_config = new; else last_config->next = new; last_config = new; return; process_config_line_err: msg_logger_and_die(LOG_ERR, bb_msg_bad_config, msg , line); } /* End Function process_config_line */ static int do_servicing(int fd, unsigned long event_mask) /* [SUMMARY] Service devfs changes until a signal is received. The open control file. The event mask. [RETURNS] TRUE if SIGHUP was caught, else FALSE. */ { ssize_t bytes; struct devfsd_notify_struct info; /* (void*) cast is only in order to match prototype */ xioctl(fd, DEVFSDIOC_SET_EVENT_MASK, (void*)event_mask); while (!caught_signal) { errno = 0; bytes = read(fd, (char *) &info, sizeof info); if (caught_signal) break; /* Must test for this first */ if (errno == EINTR) continue; /* Yes, the order is important */ if (bytes < 1) break; service_name(&info); } if (caught_signal) { int c_sighup = caught_sighup; caught_signal = FALSE; caught_sighup = FALSE; return c_sighup; } msg_logger_and_die(LOG_ERR, "read error on control file"); } /* End Function do_servicing */ static void service_name(const struct devfsd_notify_struct *info) /* [SUMMARY] Service a single devfs change. The devfs change. [RETURNS] Nothing. */ { unsigned int n; regmatch_t mbuf[MAX_SUBEXPR]; struct config_entry_struct *entry; if (ENABLE_DEBUG && info->overrun_count > 0) msg_logger(LOG_ERR, "lost %u events", info->overrun_count); /* Discard lookups on "/dev/log" and "/dev/initctl" */ if (info->type == DEVFSD_NOTIFY_LOOKUP && ((info->devname[0] == 'l' && info->devname[1] == 'o' && info->devname[2] == 'g' && !info->devname[3]) || (info->devname[0] == 'i' && info->devname[1] == 'n' && info->devname[2] == 'i' && info->devname[3] == 't' && info->devname[4] == 'c' && info->devname[5] == 't' && info->devname[6] == 'l' && !info->devname[7])) ) return; for (entry = first_config; entry != NULL; entry = entry->next) { /* First check if action matches the type, then check if name matches */ if (info->type != entry->action.when || regexec(&entry->preg, info->devname, MAX_SUBEXPR, mbuf, 0) != 0) continue; for (n = 0;(n < MAX_SUBEXPR) && (mbuf[n].rm_so != -1); ++n) /* VOID */; switch (entry->action.what) { case AC_PERMISSIONS: action_permissions(info, entry); break; case AC_MODLOAD: if (ENABLE_DEVFSD_MODLOAD) action_modload(info, entry); break; case AC_EXECUTE: action_execute(info, entry, mbuf, n); break; case AC_COPY: action_copy(info, entry, mbuf, n); break; case AC_IGNORE: return; /*break;*/ case AC_MKOLDCOMPAT: case AC_MKNEWCOMPAT: case AC_RMOLDCOMPAT: case AC_RMNEWCOMPAT: action_compat(info, entry->action.what); break; default: msg_logger_and_die(LOG_ERR, "Unknown action"); } } } /* End Function service_name */ static void action_permissions(const struct devfsd_notify_struct *info, const struct config_entry_struct *entry) /* [SUMMARY] Update permissions for a device entry. The devfs change. The config file entry. [RETURNS] Nothing. */ { struct stat statbuf; if (stat(info->devname, &statbuf) != 0 || chmod(info->devname, (statbuf.st_mode & S_IFMT) | (entry->u.permissions.mode & ~S_IFMT)) != 0 || chown(info->devname, entry->u.permissions.uid, entry->u.permissions.gid) != 0 ) error_logger(LOG_ERR, "Can't chmod or chown: %s", info->devname); } /* End Function action_permissions */ static void action_modload(const struct devfsd_notify_struct *info, const struct config_entry_struct *entry UNUSED_PARAM) /* [SUMMARY] Load a module. The devfs change. The config file entry. [RETURNS] Nothing. */ { char *argv[6]; argv[0] = (char*)MODPROBE; argv[1] = (char*)MODPROBE_SWITCH_1; /* "-k" */ argv[2] = (char*)MODPROBE_SWITCH_2; /* "-C" */ argv[3] = (char*)CONFIG_MODULES_DEVFS; argv[4] = concat_path_file("/dev", info->devname); /* device */ argv[5] = NULL; spawn_and_wait(argv); free(argv[4]); } /* End Function action_modload */ static void action_execute(const struct devfsd_notify_struct *info, const struct config_entry_struct *entry, const regmatch_t *regexpr, unsigned int numexpr) /* [SUMMARY] Execute a programme. The devfs change. The config file entry. The number of subexpression(start, end) offsets within the device name. The number of elements within <>. [RETURNS] Nothing. */ { unsigned int count; struct get_variable_info gv_info; char *argv[MAX_ARGS + 1]; char largv[MAX_ARGS + 1][STRING_LENGTH]; gv_info.info = info; gv_info.devname = info->devname; snprintf(gv_info.devpath, sizeof(gv_info.devpath), "%s/%s", mount_point, info->devname); for (count = 0; entry->u.execute.argv[count] != NULL; ++count) { expand_expression(largv[count], STRING_LENGTH, entry->u.execute.argv[count], get_variable, &gv_info, gv_info.devname, regexpr, numexpr); argv[count] = largv[count]; } argv[count] = NULL; spawn_and_wait(argv); } /* End Function action_execute */ static void action_copy(const struct devfsd_notify_struct *info, const struct config_entry_struct *entry, const regmatch_t *regexpr, unsigned int numexpr) /* [SUMMARY] Copy permissions. The devfs change. The config file entry. This list of subexpression(start, end) offsets within the device name. The number of elements in <>. [RETURNS] Nothing. */ { mode_t new_mode; struct get_variable_info gv_info; struct stat source_stat, dest_stat; char source[STRING_LENGTH], destination[STRING_LENGTH]; int ret = 0; dest_stat.st_mode = 0; if ((info->type == DEVFSD_NOTIFY_CHANGE) && S_ISLNK(info->mode)) return; gv_info.info = info; gv_info.devname = info->devname; snprintf(gv_info.devpath, sizeof(gv_info.devpath), "%s/%s", mount_point, info->devname); expand_expression(source, STRING_LENGTH, entry->u.copy.source, get_variable, &gv_info, gv_info.devname, regexpr, numexpr); expand_expression(destination, STRING_LENGTH, entry->u.copy.destination, get_variable, &gv_info, gv_info.devname, regexpr, numexpr); if (!make_dir_tree(destination) || lstat(source, &source_stat) != 0) return; lstat(destination, &dest_stat); new_mode = source_stat.st_mode & ~S_ISVTX; if (info->type == DEVFSD_NOTIFY_CREATE) new_mode |= S_ISVTX; else if ((info->type == DEVFSD_NOTIFY_CHANGE) &&(dest_stat.st_mode & S_ISVTX)) new_mode |= S_ISVTX; ret = copy_inode(destination, &dest_stat, new_mode, source, &source_stat); if (ENABLE_DEBUG && ret && (errno != EEXIST)) error_logger(LOG_ERR, "copy_inode: %s to %s", source, destination); } /* End Function action_copy */ static void action_compat(const struct devfsd_notify_struct *info, unsigned int action) /* [SUMMARY] Process a compatibility request. The devfs change. The action to take. [RETURNS] Nothing. */ { int ret; const char *compat_name = NULL; const char *dest_name = info->devname; const char *ptr; char compat_buf[STRING_LENGTH], dest_buf[STRING_LENGTH]; int mode, host, bus, target, lun; unsigned int i; char rewind_; /* 1 to 5 "scsi/" , 6 to 9 "ide/host" */ static const char *const fmt[] = { NULL , "sg/c%db%dt%du%d", /* scsi/generic */ "sd/c%db%dt%du%d", /* scsi/disc */ "sr/c%db%dt%du%d", /* scsi/cd */ "sd/c%db%dt%du%dp%d", /* scsi/part */ "st/c%db%dt%du%dm%d%c", /* scsi/mt */ "ide/hd/c%db%dt%du%d", /* ide/host/disc */ "ide/cd/c%db%dt%du%d", /* ide/host/cd */ "ide/hd/c%db%dt%du%dp%d", /* ide/host/part */ "ide/mt/c%db%dt%du%d%s", /* ide/host/mt */ NULL }; /* First construct compatibility name */ switch (action) { case AC_MKOLDCOMPAT: case AC_RMOLDCOMPAT: compat_name = get_old_name(info->devname, info->namelen, compat_buf, info->major, info->minor); break; case AC_MKNEWCOMPAT: case AC_RMNEWCOMPAT: ptr = bb_basename(info->devname); i = scan_dev_name(info->devname, info->namelen, ptr); /* nothing found */ if (i == 0 || i > 9) return; sscanf(info->devname + ((i < 6) ? 5 : 4), "host%d/bus%d/target%d/lun%d/", &host, &bus, &target, &lun); snprintf(dest_buf, sizeof(dest_buf), "../%s", info->devname + (( i > 5) ? 4 : 0)); dest_name = dest_buf; compat_name = compat_buf; /* 1 == scsi/generic 2 == scsi/disc 3 == scsi/cd 6 == ide/host/disc 7 == ide/host/cd */ if (i == 1 || i == 2 || i == 3 || i == 6 || i ==7) sprintf(compat_buf, fmt[i], host, bus, target, lun); /* 4 == scsi/part 8 == ide/host/part */ if (i == 4 || i == 8) sprintf(compat_buf, fmt[i], host, bus, target, lun, atoi(ptr + 4)); /* 5 == scsi/mt */ if (i == 5) { rewind_ = info->devname[info->namelen - 1]; if (rewind_ != 'n') rewind_ = '\0'; mode=0; if (ptr[2] == 'l' /*108*/ || ptr[2] == 'm'/*109*/) mode = ptr[2] - 107; /* 1 or 2 */ if (ptr[2] == 'a') mode = 3; sprintf(compat_buf, fmt[i], host, bus, target, lun, mode, rewind_); } /* 9 == ide/host/mt */ if (i == 9) snprintf(compat_buf, sizeof(compat_buf), fmt[i], host, bus, target, lun, ptr + 2); /* esac */ } /* switch (action) */ if (compat_name == NULL) return; /* Now decide what to do with it */ switch (action) { case AC_MKOLDCOMPAT: case AC_MKNEWCOMPAT: mksymlink(dest_name, compat_name); break; case AC_RMOLDCOMPAT: case AC_RMNEWCOMPAT: ret = unlink(compat_name); if (ENABLE_DEBUG && ret) error_logger(LOG_ERR, "unlink: %s", compat_name); break; /*esac*/ } /* switch (action) */ } /* End Function action_compat */ static void restore(char *spath, struct stat source_stat, int rootlen) { char *dpath; struct stat dest_stat; dest_stat.st_mode = 0; dpath = concat_path_file(mount_point, spath + rootlen); lstat(dpath, &dest_stat); free(dpath); if (S_ISLNK(source_stat.st_mode) || (source_stat.st_mode & S_ISVTX)) copy_inode(dpath, &dest_stat, (source_stat.st_mode & ~S_ISVTX), spath, &source_stat); if (S_ISDIR(source_stat.st_mode)) dir_operation(RESTORE, spath, rootlen, NULL); } static int copy_inode(const char *destpath, const struct stat *dest_stat, mode_t new_mode, const char *sourcepath, const struct stat *source_stat) /* [SUMMARY] Copy an inode. The destination path. An existing inode may be deleted. The destination stat(2) information. The desired new mode for the destination. The source path. The source stat(2) information. [RETURNS] TRUE on success, else FALSE. */ { int source_len, dest_len; char source_link[STRING_LENGTH], dest_link[STRING_LENGTH]; int fd, val; struct sockaddr_un un_addr; char symlink_val[STRING_LENGTH]; if ((source_stat->st_mode & S_IFMT) ==(dest_stat->st_mode & S_IFMT)) { /* Same type */ if (S_ISLNK(source_stat->st_mode)) { source_len = readlink(sourcepath, source_link, STRING_LENGTH - 1); if ((source_len < 0) || (dest_len = readlink(destpath, dest_link, STRING_LENGTH - 1)) < 0 ) return FALSE; source_link[source_len] = '\0'; dest_link[dest_len] = '\0'; if ((source_len != dest_len) || (strcmp(source_link, dest_link) != 0)) { unlink(destpath); symlink(source_link, destpath); } return TRUE; } /* Else not a symlink */ chmod(destpath, new_mode & ~S_IFMT); chown(destpath, source_stat->st_uid, source_stat->st_gid); return TRUE; } /* Different types: unlink and create */ unlink(destpath); switch (source_stat->st_mode & S_IFMT) { case S_IFSOCK: fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) break; un_addr.sun_family = AF_UNIX; snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s", destpath); val = bind(fd, (struct sockaddr *) &un_addr, (int) sizeof un_addr); close(fd); if (val != 0 || chmod(destpath, new_mode & ~S_IFMT) != 0) break; goto do_chown; case S_IFLNK: val = readlink(sourcepath, symlink_val, STRING_LENGTH - 1); if (val < 0) break; symlink_val[val] = '\0'; if (symlink(symlink_val, destpath) == 0) return TRUE; break; case S_IFREG: fd = open(destpath, O_RDONLY | O_CREAT, new_mode & ~S_IFMT); if (fd < 0) break; close(fd); if (chmod(destpath, new_mode & ~S_IFMT) != 0) break; goto do_chown; case S_IFBLK: case S_IFCHR: case S_IFIFO: if (mknod(destpath, new_mode, source_stat->st_rdev) != 0) break; goto do_chown; case S_IFDIR: if (mkdir(destpath, new_mode & ~S_IFMT) != 0) break; do_chown: if (chown(destpath, source_stat->st_uid, source_stat->st_gid) == 0) return TRUE; /*break;*/ } return FALSE; } /* End Function copy_inode */ static void free_config(void) /* [SUMMARY] Free the configuration information. [RETURNS] Nothing. */ { struct config_entry_struct *c_entry; void *next; for (c_entry = first_config; c_entry != NULL; c_entry = next) { unsigned int count; next = c_entry->next; regfree(&c_entry->preg); if (c_entry->action.what == AC_EXECUTE) { for (count = 0; count < MAX_ARGS; ++count) { if (c_entry->u.execute.argv[count] == NULL) break; free(c_entry->u.execute.argv[count]); } } free(c_entry); } first_config = NULL; last_config = NULL; } /* End Function free_config */ static int get_uid_gid(int flag, const char *string) /* [SUMMARY] Convert a string to a UID or GID value. "UID" or "GID". The string. [RETURNS] The UID or GID value. */ { struct passwd *pw_ent; struct group *grp_ent; const char *msg; if (isdigit(string[0]) || ((string[0] == '-') && isdigit(string[1]))) return atoi(string); if (flag == UID && (pw_ent = getpwnam(string)) != NULL) return pw_ent->pw_uid; if (ENABLE_DEVFSD_VERBOSE) msg = "user"; if (flag == GID) { if ((grp_ent = getgrnam(string)) != NULL) return grp_ent->gr_gid; if (ENABLE_DEVFSD_VERBOSE) msg = "group"; } if (ENABLE_DEVFSD_VERBOSE) msg_logger(LOG_ERR, "unknown %s: %s, defaulting to %cid=0", msg, string, msg[0]); return 0; }/* End Function get_uid_gid */ static mode_t get_mode(const char *string) /* [SUMMARY] Convert a string to a mode value. The string. [RETURNS] The mode value. */ { mode_t mode; int i; if (isdigit(string[0])) return strtoul(string, NULL, 8); if (strlen(string) != 9) msg_logger_and_die(LOG_ERR, "bad mode: %s", string); mode = 0; i = S_IRUSR; while (i > 0) { if (string[0] == 'r' || string[0] == 'w' || string[0] == 'x') mode += i; i = i / 2; string++; } return mode; } /* End Function get_mode */ static void signal_handler(int sig) { caught_signal = TRUE; if (sig == SIGHUP) caught_sighup = TRUE; info_logger(LOG_INFO, "Caught signal %d", sig); } /* End Function signal_handler */ static const char *get_variable(const char *variable, void *info) { static char sbuf[sizeof(int)*3 + 2]; /* sign and NUL */ static char *hostname; struct get_variable_info *gv_info = info; const char *field_names[] = { "hostname", "mntpt", "devpath", "devname", "uid", "gid", "mode", hostname, mount_point, gv_info->devpath, gv_info->devname, NULL }; int i; if (!hostname) hostname = safe_gethostname(); /* index_in_str_array returns i>=0 */ i = index_in_str_array(field_names, variable); if (i > 6 || i < 0 || (i > 1 && gv_info == NULL)) return NULL; if (i >= 0 && i <= 3) return field_names[i + 7]; if (i == 4) sprintf(sbuf, "%u", gv_info->info->uid); else if (i == 5) sprintf(sbuf, "%u", gv_info->info->gid); else if (i == 6) sprintf(sbuf, "%o", gv_info->info->mode); return sbuf; } /* End Function get_variable */ static void service(struct stat statbuf, char *path) { struct devfsd_notify_struct info; memset(&info, 0, sizeof info); info.type = DEVFSD_NOTIFY_REGISTERED; info.mode = statbuf.st_mode; info.major = major(statbuf.st_rdev); info.minor = minor(statbuf.st_rdev); info.uid = statbuf.st_uid; info.gid = statbuf.st_gid; snprintf(info.devname, sizeof(info.devname), "%s", path + strlen(mount_point) + 1); info.namelen = strlen(info.devname); service_name(&info); if (S_ISDIR(statbuf.st_mode)) dir_operation(SERVICE, path, 0, NULL); } static void dir_operation(int type, const char * dir_name, int var, unsigned long *event_mask) /* [SUMMARY] Scan a directory tree and generate register events on leaf nodes. To choose which function to perform The directory pointer. This is closed upon completion. The name of the directory. string length parameter. [RETURNS] Nothing. */ { struct stat statbuf; DIR *dp; struct dirent *de; char *path; dp = warn_opendir(dir_name); if (dp == NULL) return; while ((de = readdir(dp)) != NULL) { if (de->d_name && DOT_OR_DOTDOT(de->d_name)) continue; path = concat_path_file(dir_name, de->d_name); if (lstat(path, &statbuf) == 0) { switch (type) { case SERVICE: service(statbuf, path); break; case RESTORE: restore(path, statbuf, var); break; case READ_CONFIG: read_config_file(path, var, event_mask); break; } } free(path); } closedir(dp); } /* End Function do_scan_and_service */ static int mksymlink(const char *oldpath, const char *newpath) /* [SUMMARY] Create a symlink, creating intervening directories as required. The string contained in the symlink. The name of the new symlink. [RETURNS] 0 on success, else -1. */ { if (!make_dir_tree(newpath)) return -1; if (symlink(oldpath, newpath) != 0) { if (errno != EEXIST) return -1; } return 0; } /* End Function mksymlink */ static int make_dir_tree(const char *path) /* [SUMMARY] Creating intervening directories for a path as required. The full pathname(including the leaf node). [RETURNS] TRUE on success, else FALSE. */ { if (bb_make_directory(dirname((char *)path), -1, FILEUTILS_RECUR) == -1) return FALSE; return TRUE; } /* End Function make_dir_tree */ static int expand_expression(char *output, unsigned int outsize, const char *input, const char *(*get_variable_func)(const char *variable, void *info), void *info, const char *devname, const regmatch_t *ex, unsigned int numexp) /* [SUMMARY] Expand environment variables and regular subexpressions in string. The output expanded expression is written here. The size of the output buffer. The input expression. This may equal <>. A function which will be used to get variable values. If this returns NULL, the environment is searched instead. If this is NULL, only the environment is searched. An arbitrary pointer passed to <>. Device name; specifically, this is the string that contains all of the regular subexpressions. Array of start / end offsets into info->devname for each subexpression Number of regular subexpressions found in <>. [RETURNS] TRUE on success, else FALSE. */ { char temp[STRING_LENGTH]; if (!st_expr_expand(temp, STRING_LENGTH, input, get_variable_func, info)) return FALSE; expand_regexp(output, outsize, temp, devname, ex, numexp); return TRUE; } /* End Function expand_expression */ static void expand_regexp(char *output, size_t outsize, const char *input, const char *devname, const regmatch_t *ex, unsigned int numex) /* [SUMMARY] Expand all occurrences of the regular subexpressions \0 to \9. The output expanded expression is written here. The size of the output buffer. The input expression. This may NOT equal <>, because supporting that would require yet another string-copy. However, it's not hard to write a simple wrapper function to add this functionality for those few cases that need it. Device name; specifically, this is the string that contains all of the regular subexpressions. An array of start and end offsets into <>, one for each subexpression Number of subexpressions in the offset-array <>. [RETURNS] Nothing. */ { const char last_exp = '0' - 1 + numex; int c = -1; /* Guarantee NULL termination by writing an explicit '\0' character into the very last byte */ if (outsize) output[--outsize] = '\0'; /* Copy the input string into the output buffer, replacing '\\' with '\' and '\0' .. '\9' with subexpressions 0 .. 9, if they exist. Other \x codes are deleted */ while ((c != '\0') && (outsize != 0)) { c = *input; ++input; if (c == '\\') { c = *input; ++input; if (c != '\\') { if ((c >= '0') && (c <= last_exp)) { const regmatch_t *subexp = ex + (c - '0'); unsigned int sublen = subexp->rm_eo - subexp->rm_so; /* Range checking */ if (sublen > outsize) sublen = outsize; strncpy(output, devname + subexp->rm_so, sublen); output += sublen; outsize -= sublen; } continue; } } *output = c; ++output; --outsize; } /* while */ } /* End Function expand_regexp */ /* from compat_name.c */ struct translate_struct { const char *match; /* The string to match to(up to length) */ const char *format; /* Format of output, "%s" takes data past match string, NULL is effectively "%s"(just more efficient) */ }; static struct translate_struct translate_table[] = { {"sound/", NULL}, {"printers/", "lp%s"}, {"v4l/", NULL}, {"parports/", "parport%s"}, {"fb/", "fb%s"}, {"netlink/", NULL}, {"loop/", "loop%s"}, {"floppy/", "fd%s"}, {"rd/", "ram%s"}, {"md/", "md%s"}, /* Meta-devices */ {"vc/", "tty%s"}, {"misc/", NULL}, {"isdn/", NULL}, {"pg/", "pg%s"}, /* Parallel port generic ATAPI interface*/ {"i2c/", "i2c-%s"}, {"staliomem/", "staliomem%s"}, /* Stallion serial driver control */ {"tts/E", "ttyE%s"}, /* Stallion serial driver */ {"cua/E", "cue%s"}, /* Stallion serial driver callout */ {"tts/R", "ttyR%s"}, /* Rocketport serial driver */ {"cua/R", "cur%s"}, /* Rocketport serial driver callout */ {"ip2/", "ip2%s"}, /* Computone serial driver control */ {"tts/F", "ttyF%s"}, /* Computone serial driver */ {"cua/F", "cuf%s"}, /* Computone serial driver callout */ {"tts/C", "ttyC%s"}, /* Cyclades serial driver */ {"cua/C", "cub%s"}, /* Cyclades serial driver callout */ {"tts/", "ttyS%s"}, /* Generic serial: must be after others */ {"cua/", "cua%s"}, /* Generic serial: must be after others */ {"input/js", "js%s"}, /* Joystick driver */ {NULL, NULL} }; const char *get_old_name(const char *devname, unsigned int namelen, char *buffer, unsigned int major, unsigned int minor) /* [SUMMARY] Translate a kernel-supplied name into an old name. The device name provided by the kernel. The length of the name. A buffer that may be used. This should be at least 128 bytes long. The major number for the device. The minor number for the device. [RETURNS] A pointer to the old name if known, else NULL. */ { const char *compat_name = NULL; const char *ptr; struct translate_struct *trans; unsigned int i; char mode; int indexx; const char *pty1; const char *pty2; size_t len; /* 1 to 5 "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */ static const char *const fmt[] = { NULL , "sg%u", /* scsi/generic */ NULL, /* scsi/disc */ "sr%u", /* scsi/cd */ NULL, /* scsi/part */ "nst%u%c", /* scsi/mt */ "hd%c" , /* ide/host/disc */ "hd%c" , /* ide/host/cd */ "hd%c%s", /* ide/host/part */ "%sht%d", /* ide/host/mt */ "sbpcd%u", /* sbp/ */ "vcs%s", /* vcc/ */ "%cty%c%c", /* pty/ */ NULL }; for (trans = translate_table; trans->match != NULL; ++trans) { len = strlen(trans->match); if (strncmp(devname, trans->match, len) == 0) { if (trans->format == NULL) return devname + len; sprintf(buffer, trans->format, devname + len); return buffer; } } ptr = bb_basename(devname); i = scan_dev_name(devname, namelen, ptr); if (i > 0 && i < 13) compat_name = buffer; else return NULL; /* 1 == scsi/generic, 3 == scsi/cd, 10 == sbp/ */ if (i == 1 || i == 3 || i == 10) sprintf(buffer, fmt[i], minor); /* 2 ==scsi/disc, 4 == scsi/part */ if (i == 2 || i == 4) compat_name = write_old_sd_name(buffer, major, minor, ((i == 2) ? "" : (ptr + 4))); /* 5 == scsi/mt */ if (i == 5) { mode = ptr[2]; if (mode == 'n') mode = '\0'; sprintf(buffer, fmt[i], minor & 0x1f, mode); if (devname[namelen - 1] != 'n') ++compat_name; } /* 6 == ide/host/disc, 7 == ide/host/cd, 8 == ide/host/part */ if (i == 6 || i == 7 || i == 8) /* last arg should be ignored for i == 6 or i== 7 */ sprintf(buffer, fmt[i] , get_old_ide_name(major, minor), ptr + 4); /* 9 == ide/host/mt */ if (i == 9) sprintf(buffer, fmt[i], ptr + 2, minor & 0x7f); /* 11 == vcc/ */ if (i == 11) { sprintf(buffer, fmt[i], devname + 4); if (buffer[3] == '0') buffer[3] = '\0'; } /* 12 == pty/ */ if (i == 12) { pty1 = "pqrstuvwxyzabcde"; pty2 = "0123456789abcdef"; indexx = atoi(devname + 5); sprintf(buffer, fmt[i], (devname[4] == 'm') ? 'p' : 't', pty1[indexx >> 4], pty2[indexx & 0x0f]); } return compat_name; } /* End Function get_old_name */ static char get_old_ide_name(unsigned int major, unsigned int minor) /* [SUMMARY] Get the old IDE name for a device. The major number for the device. The minor number for the device. [RETURNS] The drive letter. */ { char letter = 'y'; /* 121 */ char c = 'a'; /* 97 */ int i = IDE0_MAJOR; /* I hope it works like the previous code as it saves a few bytes. Tito ;P */ do { if (i == IDE0_MAJOR || i == IDE1_MAJOR || i == IDE2_MAJOR || i == IDE3_MAJOR || i == IDE4_MAJOR || i == IDE5_MAJOR || i == IDE6_MAJOR || i == IDE7_MAJOR || i == IDE8_MAJOR || i == IDE9_MAJOR ) { if ((unsigned int)i == major) { letter = c; break; } c += 2; } i++; } while (i <= IDE9_MAJOR); if (minor > 63) ++letter; return letter; } /* End Function get_old_ide_name */ static char *write_old_sd_name(char *buffer, unsigned int major, unsigned int minor, const char *part) /* [SUMMARY] Write the old SCSI disc name to a buffer. The buffer to write to. The major number for the device. The minor number for the device. The partition string. Must be "" for a whole-disc entry. [RETURNS] A pointer to the buffer on success, else NULL. */ { unsigned int disc_index; if (major == 8) { sprintf(buffer, "sd%c%s", 'a' + (minor >> 4), part); return buffer; } if ((major > 64) && (major < 72)) { disc_index = ((major - 64) << 4) +(minor >> 4); if (disc_index < 26) sprintf(buffer, "sd%c%s", 'a' + disc_index, part); else sprintf(buffer, "sd%c%c%s", 'a' +(disc_index / 26) - 1, 'a' + disc_index % 26, part); return buffer; } return NULL; } /* End Function write_old_sd_name */ /* expression.c */ /*EXPERIMENTAL_FUNCTION*/ int st_expr_expand(char *output, unsigned int length, const char *input, const char *(*get_variable_func)(const char *variable, void *info), void *info) /* [SUMMARY] Expand an expression using Borne Shell-like unquoted rules. The output expanded expression is written here. The size of the output buffer. The input expression. This may equal <>. A function which will be used to get variable values. If this returns NULL, the environment is searched instead. If this is NULL, only the environment is searched. An arbitrary pointer passed to <>. [RETURNS] TRUE on success, else FALSE. */ { char ch; unsigned int len; unsigned int out_pos = 0; const char *env; const char *ptr; struct passwd *pwent; char buffer[BUFFER_SIZE], tmp[STRING_LENGTH]; if (length > BUFFER_SIZE) length = BUFFER_SIZE; for (; TRUE; ++input) { switch (ch = *input) { case '$': /* Variable expansion */ input = expand_variable(buffer, length, &out_pos, ++input, get_variable_func, info); if (input == NULL) return FALSE; break; case '~': /* Home directory expansion */ ch = input[1]; if (isspace(ch) ||(ch == '/') ||(ch == '\0')) { /* User's own home directory: leave separator for next time */ env = getenv("HOME"); if (env == NULL) { info_logger(LOG_INFO, bb_msg_variable_not_found, "HOME"); return FALSE; } len = strlen(env); if (len + out_pos >= length) goto st_expr_expand_out; memcpy(buffer + out_pos, env, len + 1); out_pos += len; continue; } /* Someone else's home directory */ for (ptr = ++input; !isspace(ch) && (ch != '/') && (ch != '\0'); ch = *++ptr) /* VOID */; len = ptr - input; if (len >= sizeof tmp) goto st_expr_expand_out; safe_memcpy(tmp, input, len); input = ptr - 1; pwent = getpwnam(tmp); if (pwent == NULL) { info_logger(LOG_INFO, "no pwent for: %s", tmp); return FALSE; } len = strlen(pwent->pw_dir); if (len + out_pos >= length) goto st_expr_expand_out; memcpy(buffer + out_pos, pwent->pw_dir, len + 1); out_pos += len; break; case '\0': /* Falltrough */ default: if (out_pos >= length) goto st_expr_expand_out; buffer[out_pos++] = ch; if (ch == '\0') { memcpy(output, buffer, out_pos); return TRUE; } break; /* esac */ } } return FALSE; st_expr_expand_out: info_logger(LOG_INFO, bb_msg_small_buffer); return FALSE; } /* End Function st_expr_expand */ /* Private functions follow */ static const char *expand_variable(char *buffer, unsigned int length, unsigned int *out_pos, const char *input, const char *(*func)(const char *variable, void *info), void *info) /* [SUMMARY] Expand a variable. The buffer to write to. The length of the output buffer. The current output position. This is updated. A pointer to the input character pointer. A function which will be used to get variable values. If this returns NULL, the environment is searched instead. If this is NULL, only the environment is searched. An arbitrary pointer passed to <>. Diagnostic messages are written here. [RETURNS] A pointer to the end of this subexpression on success, else NULL. */ { char ch; int len; unsigned int open_braces; const char *env, *ptr; char tmp[STRING_LENGTH]; ch = input[0]; if (ch == '$') { /* Special case for "$$": PID */ sprintf(tmp, "%d", (int) getpid()); len = strlen(tmp); if (len + *out_pos >= length) goto expand_variable_out; memcpy(buffer + *out_pos, tmp, len + 1); out_pos += len; return input; } /* Ordinary variable expansion, possibly in braces */ if (ch != '{') { /* Simple variable expansion */ for (ptr = input; isalnum(ch) || (ch == '_') || (ch == ':'); ch = *++ptr) /* VOID */; len = ptr - input; if ((size_t)len >= sizeof tmp) goto expand_variable_out; safe_memcpy(tmp, input, len); input = ptr - 1; env = get_variable_v2(tmp, func, info); if (env == NULL) { info_logger(LOG_INFO, bb_msg_variable_not_found, tmp); return NULL; } len = strlen(env); if (len + *out_pos >= length) goto expand_variable_out; memcpy(buffer + *out_pos, env, len + 1); *out_pos += len; return input; } /* Variable in braces: check for ':' tricks */ ch = *++input; for (ptr = input; isalnum(ch) || (ch == '_'); ch = *++ptr) /* VOID */; if (ch == '}') { /* Must be simple variable expansion with "${var}" */ len = ptr - input; if ((size_t)len >= sizeof tmp) goto expand_variable_out; safe_memcpy(tmp, input, len); ptr = expand_variable(buffer, length, out_pos, tmp, func, info); if (ptr == NULL) return NULL; return input + len; } if (ch != ':' || ptr[1] != '-') { info_logger(LOG_INFO, "illegal char in var name"); return NULL; } /* It's that handy "${var:-word}" expression. Check if var is defined */ len = ptr - input; if ((size_t)len >= sizeof tmp) goto expand_variable_out; safe_memcpy(tmp, input, len); /* Move input pointer to ':' */ input = ptr; /* First skip to closing brace, taking note of nested expressions */ ptr += 2; ch = ptr[0]; for (open_braces = 1; open_braces > 0; ch = *++ptr) { switch (ch) { case '{': ++open_braces; break; case '}': --open_braces; break; case '\0': info_logger(LOG_INFO, "\"}\" not found in: %s", input); return NULL; default: break; } } --ptr; /* At this point ptr should point to closing brace of "${var:-word}" */ env = get_variable_v2(tmp, func, info); if (env != NULL) { /* Found environment variable, so skip the input to the closing brace and return the variable */ input = ptr; len = strlen(env); if (len + *out_pos >= length) goto expand_variable_out; memcpy(buffer + *out_pos, env, len + 1); *out_pos += len; return input; } /* Environment variable was not found, so process word. Advance input pointer to start of word in "${var:-word}" */ input += 2; len = ptr - input; if ((size_t)len >= sizeof tmp) goto expand_variable_out; safe_memcpy(tmp, input, len); input = ptr; if (!st_expr_expand(tmp, STRING_LENGTH, tmp, func, info)) return NULL; len = strlen(tmp); if (len + *out_pos >= length) goto expand_variable_out; memcpy(buffer + *out_pos, tmp, len + 1); *out_pos += len; return input; expand_variable_out: info_logger(LOG_INFO, bb_msg_small_buffer); return NULL; } /* End Function expand_variable */ static const char *get_variable_v2(const char *variable, const char *(*func)(const char *variable, void *info), void *info) /* [SUMMARY] Get a variable from the environment or . The variable name. A function which will be used to get the variable. If this returns NULL, the environment is searched instead. If this is NULL, only the environment is searched. [RETURNS] The value of the variable on success, else NULL. */ { const char *value; if (func != NULL) { value = (*func)(variable, info); if (value != NULL) return value; } return getenv(variable); } /* End Function get_variable */ /* END OF CODE */ busybox-1.22.1/miscutils/mt.c0000644000000000000000000000601112263563520014570 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define mt_trivial_usage //usage: "[-f device] opcode value" //usage:#define mt_full_usage "\n\n" //usage: "Control magnetic tape drive operation\n" //usage: "\n" //usage: "Available Opcodes:\n" //usage: "\n" //usage: "bsf bsfm bsr bss datacompression drvbuffer eof eom erase\n" //usage: "fsf fsfm fsr fss load lock mkpart nop offline ras1 ras2\n" //usage: "ras3 reset retension rewind rewoffline seek setblk setdensity\n" //usage: "setpart tell unload unlock weof wset" #include "libbb.h" #include /* missing: eod/seod, stoptions, stwrthreshold, densities */ static const short opcode_value[] = { MTBSF, MTBSFM, MTBSR, MTBSS, MTCOMPRESSION, MTEOM, MTERASE, MTFSF, MTFSFM, MTFSR, MTFSS, MTLOAD, MTLOCK, MTMKPART, MTNOP, MTOFFL, MTOFFL, MTRAS1, MTRAS2, MTRAS3, MTRESET, MTRETEN, MTREW, MTSEEK, MTSETBLK, MTSETDENSITY, MTSETDRVBUFFER, MTSETPART, MTTELL, MTWSM, MTUNLOAD, MTUNLOCK, MTWEOF, MTWEOF }; static const char opcode_name[] ALIGN1 = "bsf" "\0" "bsfm" "\0" "bsr" "\0" "bss" "\0" "datacompression" "\0" "eom" "\0" "erase" "\0" "fsf" "\0" "fsfm" "\0" "fsr" "\0" "fss" "\0" "load" "\0" "lock" "\0" "mkpart" "\0" "nop" "\0" "offline" "\0" "rewoffline" "\0" "ras1" "\0" "ras2" "\0" "ras3" "\0" "reset" "\0" "retension" "\0" "rewind" "\0" "seek" "\0" "setblk" "\0" "setdensity" "\0" "drvbuffer" "\0" "setpart" "\0" "tell" "\0" "wset" "\0" "unload" "\0" "unlock" "\0" "eof" "\0" "weof" "\0"; int mt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mt_main(int argc UNUSED_PARAM, char **argv) { const char *file = "/dev/tape"; struct mtop op; struct mtpos position; int fd, mode, idx; if (!argv[1]) { bb_show_usage(); } if (strcmp(argv[1], "-f") == 0) { if (!argv[2] || !argv[3]) bb_show_usage(); file = argv[2]; argv += 2; } idx = index_in_strings(opcode_name, argv[1]); if (idx < 0) bb_error_msg_and_die("unrecognized opcode %s", argv[1]); op.mt_op = opcode_value[idx]; if (argv[2]) op.mt_count = xatoi_positive(argv[2]); else op.mt_count = 1; /* One, not zero, right? */ switch (opcode_value[idx]) { case MTWEOF: case MTERASE: case MTWSM: case MTSETDRVBUFFER: mode = O_WRONLY; break; default: mode = O_RDONLY; break; } fd = xopen(file, mode); switch (opcode_value[idx]) { case MTTELL: ioctl_or_perror_and_die(fd, MTIOCPOS, &position, "%s", file); printf("At block %d\n", (int) position.mt_blkno); break; default: ioctl_or_perror_and_die(fd, MTIOCTOP, &op, "%s", file); break; } return EXIT_SUCCESS; } busybox-1.22.1/miscutils/ttysize.c0000644000000000000000000000222012263563520015661 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Replacement for "stty size", which is awkward for shell script use. * - Allows to request width, height, or both, in any order. * - Does not complain on error, but returns width 80, height 24. * - Size: less than 200 bytes * * Copyright (C) 2007 by Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define ttysize_trivial_usage //usage: "[w] [h]" //usage:#define ttysize_full_usage "\n\n" //usage: "Print dimension(s) of stdin's terminal, on error return 80x25" #include "libbb.h" int ttysize_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ttysize_main(int argc UNUSED_PARAM, char **argv) { unsigned w, h; struct winsize wsz; w = 80; h = 24; if (!ioctl(0, TIOCGWINSZ, &wsz)) { w = wsz.ws_col; h = wsz.ws_row; } if (!argv[1]) { printf("%u %u", w, h); } else { const char *fmt, *arg; fmt = "%u %u" + 3; /* "%u" */ while ((arg = *++argv) != NULL) { char c = arg[0]; if (c == 'w') printf(fmt, w); if (c == 'h') printf(fmt, h); fmt = "%u %u" + 2; /* " %u" */ } } bb_putchar('\n'); return 0; } busybox-1.22.1/miscutils/setserial.c0000644000000000000000000004006512263563520016152 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * setserial implementation for busybox * * * Copyright (C) 2011 Marek BeÄka * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //config:config SETSERIAL //config: bool "setserial" //config: default y //config: select PLATFORM_LINUX //config: help //config: Retrieve or set Linux serial port. //applet:IF_SETSERIAL(APPLET(setserial, BB_DIR_BIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_SETSERIAL) += setserial.o #include "libbb.h" #include #ifndef PORT_UNKNOWN # define PORT_UNKNOWN 0 #endif #ifndef PORT_8250 # define PORT_8250 1 #endif #ifndef PORT_16450 # define PORT_16450 2 #endif #ifndef PORT_16550 # define PORT_16550 3 #endif #ifndef PORT_16550A # define PORT_16550A 4 #endif #ifndef PORT_CIRRUS # define PORT_CIRRUS 5 #endif #ifndef PORT_16650 # define PORT_16650 6 #endif #ifndef PORT_16650V2 # define PORT_16650V2 7 #endif #ifndef PORT_16750 # define PORT_16750 8 #endif #ifndef PORT_STARTECH # define PORT_STARTECH 9 #endif #ifndef PORT_16C950 # define PORT_16C950 10 #endif #ifndef PORT_16654 # define PORT_16654 11 #endif #ifndef PORT_16850 # define PORT_16850 12 #endif #ifndef PORT_RSA # define PORT_RSA 13 #endif #ifndef PORT_NS16550A # define PORT_NS16550A 14 #endif #ifndef PORT_XSCALE # define PORT_XSCALE 15 #endif #ifndef PORT_RM9000 # define PORT_RM9000 16 #endif #ifndef PORT_OCTEON # define PORT_OCTEON 17 #endif #ifndef PORT_AR7 # define PORT_AR7 18 #endif #ifndef PORT_U6_16550A # define PORT_U6_16550A 19 #endif #ifndef ASYNCB_HUP_NOTIFY # define ASYNCB_HUP_NOTIFY 0 #endif #ifndef ASYNCB_FOURPORT # define ASYNCB_FOURPORT 1 #endif #ifndef ASYNCB_SAK # define ASYNCB_SAK 2 #endif #ifndef ASYNCB_SPLIT_TERMIOS # define ASYNCB_SPLIT_TERMIOS 3 #endif #ifndef ASYNCB_SPD_HI # define ASYNCB_SPD_HI 4 #endif #ifndef ASYNCB_SPD_VHI # define ASYNCB_SPD_VHI 5 #endif #ifndef ASYNCB_SKIP_TEST # define ASYNCB_SKIP_TEST 6 #endif #ifndef ASYNCB_AUTO_IRQ # define ASYNCB_AUTO_IRQ 7 #endif #ifndef ASYNCB_SESSION_LOCKOUT # define ASYNCB_SESSION_LOCKOUT 8 #endif #ifndef ASYNCB_PGRP_LOCKOUT # define ASYNCB_PGRP_LOCKOUT 9 #endif #ifndef ASYNCB_CALLOUT_NOHUP # define ASYNCB_CALLOUT_NOHUP 10 #endif #ifndef ASYNCB_SPD_SHI # define ASYNCB_SPD_SHI 12 #endif #ifndef ASYNCB_LOW_LATENCY # define ASYNCB_LOW_LATENCY 13 #endif #ifndef ASYNCB_BUGGY_UART # define ASYNCB_BUGGY_UART 14 #endif #ifndef ASYNC_HUP_NOTIFY # define ASYNC_HUP_NOTIFY (1U << ASYNCB_HUP_NOTIFY) #endif #ifndef ASYNC_FOURPORT # define ASYNC_FOURPORT (1U << ASYNCB_FOURPORT) #endif #ifndef ASYNC_SAK # define ASYNC_SAK (1U << ASYNCB_SAK) #endif #ifndef ASYNC_SPLIT_TERMIOS # define ASYNC_SPLIT_TERMIOS (1U << ASYNCB_SPLIT_TERMIOS) #endif #ifndef ASYNC_SPD_HI # define ASYNC_SPD_HI (1U << ASYNCB_SPD_HI) #endif #ifndef ASYNC_SPD_VHI # define ASYNC_SPD_VHI (1U << ASYNCB_SPD_VHI) #endif #ifndef ASYNC_SKIP_TEST # define ASYNC_SKIP_TEST (1U << ASYNCB_SKIP_TEST) #endif #ifndef ASYNC_AUTO_IRQ # define ASYNC_AUTO_IRQ (1U << ASYNCB_AUTO_IRQ) #endif #ifndef ASYNC_SESSION_LOCKOUT # define ASYNC_SESSION_LOCKOUT (1U << ASYNCB_SESSION_LOCKOUT) #endif #ifndef ASYNC_PGRP_LOCKOUT # define ASYNC_PGRP_LOCKOUT (1U << ASYNCB_PGRP_LOCKOUT) #endif #ifndef ASYNC_CALLOUT_NOHUP # define ASYNC_CALLOUT_NOHUP (1U << ASYNCB_CALLOUT_NOHUP) #endif #ifndef ASYNC_SPD_SHI # define ASYNC_SPD_SHI (1U << ASYNCB_SPD_SHI) #endif #ifndef ASYNC_LOW_LATENCY # define ASYNC_LOW_LATENCY (1U << ASYNCB_LOW_LATENCY) #endif #ifndef ASYNC_BUGGY_UART # define ASYNC_BUGGY_UART (1U << ASYNCB_BUGGY_UART) #endif #ifndef ASYNC_SPD_CUST # define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI) #endif #ifndef ASYNC_SPD_WARP # define ASYNC_SPD_WARP (ASYNC_SPD_HI|ASYNC_SPD_SHI) #endif #ifndef ASYNC_SPD_MASK # define ASYNC_SPD_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI) #endif #ifndef ASYNC_CLOSING_WAIT_INF # define ASYNC_CLOSING_WAIT_INF 0 #endif #ifndef ASYNC_CLOSING_WAIT_NONE # define ASYNC_CLOSING_WAIT_NONE 65535 #endif #ifndef _LINUX_SERIAL_H struct serial_struct { int type; int line; unsigned int port; int irq; int flags; int xmit_fifo_size; int custom_divisor; int baud_base; unsigned short close_delay; char io_type; char reserved_char[1]; int hub6; unsigned short closing_wait; /* time to wait before closing */ unsigned short closing_wait2; /* no longer used... */ unsigned char *iomem_base; unsigned short iomem_reg_shift; unsigned int port_high; unsigned long iomap_base; /* cookie passed into ioremap */ }; #endif //usage:#define setserial_trivial_usage //usage: "[-gabGvzV] DEVICE [PARAMETER [ARG]]..." //usage:#define setserial_full_usage "\n\n" //usage: "Request or set Linux serial port information\n" //usage: "\n" //usage: " -g Interpret parameters as list of devices for reporting\n" //usage: " -a Print all available information\n" //usage: " -b Print summary information\n" //usage: " -G Print in form which can be fed back\n" //usage: " to setserial as command line parameters\n" //usage: " -z Zero out serial flags before setting\n" //usage: " -v Verbose\n" //usage: "\n" //usage: "Parameters: (* = takes an argument, ^ = can be turned off by preceding ^)\n" //usage: " *port, *irq, *divisor, *uart, *baud_base, *close_delay, *closing_wait,\n" //usage: " ^fourport, ^auto_irq, ^skip_test, ^sak, ^session_lockout, ^pgrp_lockout,\n" //usage: " ^callout_nohup, ^split_termios, ^hup_notify, ^low_latency, autoconfig,\n" //usage: " spd_normal, spd_hi, spd_vhi, spd_shi, spd_warp, spd_cust\n" //usage: "\n" //usage: "UART types:\n" //usage: " unknown, 8250, 16450, 16550, 16550A, Cirrus, 16650, 16650V2, 16750,\n" //usage: " 16950, 16954, 16654, 16850, RSA, NS16550A, XSCALE, RM9000, OCTEON, AR7,\n" //usage: " U6_16550A" #define OPT_PRINT_SUMMARY (1 << 0) #define OPT_PRINT_FEDBACK (1 << 1) #define OPT_PRINT_ALL (1 << 2) #define OPT_VERBOSE (1 << 3) #define OPT_ZERO (1 << 4) #define OPT_GET (1 << 5) #define OPT_MODE_MASK \ (OPT_PRINT_ALL | OPT_PRINT_SUMMARY | OPT_PRINT_FEDBACK) enum print_mode { PRINT_NORMAL = 0, PRINT_SUMMARY = (1 << 0), PRINT_FEDBACK = (1 << 1), PRINT_ALL = (1 << 2), }; #define CTL_SET (1 << 0) #define CTL_CONFIG (1 << 1) #define CTL_GET (1 << 2) #define CTL_CLOSE (1 << 3) #define CTL_NODIE (1 << 4) static const char serial_types[] = "unknown\0" /* 0 */ "8250\0" /* 1 */ "16450\0" /* 2 */ "16550\0" /* 3 */ "16550A\0" /* 4 */ "Cirrus\0" /* 5 */ "16650\0" /* 6 */ "16650V2\0" /* 7 */ "16750\0" /* 8 */ "16950\0" /* 9 UNIMPLEMENTED: also know as "16950/954" */ "16954\0" /* 10 */ "16654\0" /* 11 */ "16850\0" /* 12 */ "RSA\0" /* 13 */ #ifndef SETSERIAL_BASE "NS16550A\0" /* 14 */ "XSCALE\0" /* 15 */ "RM9000\0" /* 16 */ "OCTEON\0" /* 17 */ "AR7\0" /* 18 */ "U6_16550A\0" /* 19 */ #endif ; #ifndef SETSERIAL_BASE # define MAX_SERIAL_TYPE 19 #else # define MAX_SERIAL_TYPE 13 #endif static const char commands[] = "spd_normal\0" "spd_hi\0" "spd_vhi\0" "spd_shi\0" "spd_warp\0" "spd_cust\0" "sak\0" "fourport\0" "hup_notify\0" "skip_test\0" "auto_irq\0" "split_termios\0" "session_lockout\0" "pgrp_lockout\0" "callout_nohup\0" "low_latency\0" "port\0" "irq\0" "divisor\0" "uart\0" "baud_base\0" "close_delay\0" "closing_wait\0" "autoconfig\0" ; enum { CMD_SPD_NORMAL = 0, CMD_SPD_HI, CMD_SPD_VHI, CMD_SPD_SHI, CMD_SPD_WARP, CMD_SPD_CUST, CMD_FLAG_SAK, CMD_FLAG_FOURPORT, CMD_FLAG_NUP_NOTIFY, CMD_FLAG_SKIP_TEST, CMD_FLAG_AUTO_IRQ, CMD_FLAG_SPLIT_TERMIOS, CMD_FLAG_SESSION_LOCKOUT, CMD_FLAG_PGRP_LOCKOUT, CMD_FLAG_CALLOUT_NOHUP, CMD_FLAG_LOW_LATENCY, CMD_PORT, CMD_IRQ, CMD_DIVISOR, CMD_UART, CMD_BASE, CMD_DELAY, CMD_WAIT, CMD_AUTOCONFIG, CMD_FLAG_FIRST = CMD_FLAG_SAK, CMD_FLAG_LAST = CMD_FLAG_LOW_LATENCY, }; static bool cmd_noprint(int cmd) { return (cmd >= CMD_FLAG_SKIP_TEST && cmd <= CMD_FLAG_CALLOUT_NOHUP); } static bool cmd_is_flag(int cmd) { return (cmd >= CMD_FLAG_FIRST && cmd <= CMD_FLAG_LAST); } static bool cmd_need_arg(int cmd) { return (cmd >= CMD_PORT && cmd <= CMD_WAIT); } #define ALL_SPD ( \ ASYNC_SPD_HI | ASYNC_SPD_VHI | ASYNC_SPD_SHI | \ ASYNC_SPD_WARP | ASYNC_SPD_CUST \ ) #define ALL_FLAGS ( \ ASYNC_SAK | ASYNC_FOURPORT | ASYNC_HUP_NOTIFY | \ ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ | ASYNC_SPLIT_TERMIOS | \ ASYNC_SESSION_LOCKOUT | ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP | \ ASYNC_LOW_LATENCY \ ) #if (ALL_SPD | ALL_FLAGS) > 0xffff # error "Unexpected flags size" #endif static const uint16_t setbits[CMD_FLAG_LAST + 1] = { 0, ASYNC_SPD_HI, ASYNC_SPD_VHI, ASYNC_SPD_SHI, ASYNC_SPD_WARP, ASYNC_SPD_CUST, ASYNC_SAK, ASYNC_FOURPORT, ASYNC_HUP_NOTIFY, ASYNC_SKIP_TEST, ASYNC_AUTO_IRQ, ASYNC_SPLIT_TERMIOS, ASYNC_SESSION_LOCKOUT, ASYNC_PGRP_LOCKOUT, ASYNC_CALLOUT_NOHUP, ASYNC_LOW_LATENCY }; static const char STR_INFINITE[] = "infinite"; static const char STR_NONE[] = "none"; static const char *uart_type(int type) { if (type > MAX_SERIAL_TYPE) return "undefined"; return nth_string(serial_types, type); } /* libbb candidate */ static int index_in_strings_case_insensitive(const char *strings, const char *key) { int idx = 0; while (*strings) { if (strcasecmp(strings, key) == 0) { return idx; } strings += strlen(strings) + 1; /* skip NUL */ idx++; } return -1; } static int uart_id(const char *name) { return index_in_strings_case_insensitive(serial_types, name); } static const char *get_spd(int flags, enum print_mode mode) { int idx; switch (flags & ASYNC_SPD_MASK) { case ASYNC_SPD_HI: idx = CMD_SPD_HI; break; case ASYNC_SPD_VHI: idx = CMD_SPD_VHI; break; case ASYNC_SPD_SHI: idx = CMD_SPD_SHI; break; case ASYNC_SPD_WARP: idx = CMD_SPD_WARP; break; case ASYNC_SPD_CUST: idx = CMD_SPD_CUST; break; default: if (mode < PRINT_FEDBACK) return NULL; idx = CMD_SPD_NORMAL; } return nth_string(commands, idx); } static int get_numeric(const char *arg) { return bb_strtol(arg, NULL, 0); } static int get_wait(const char *arg) { if (strcasecmp(arg, STR_NONE) == 0) return ASYNC_CLOSING_WAIT_NONE; if (strcasecmp(arg, STR_INFINITE) == 0) return ASYNC_CLOSING_WAIT_INF; return get_numeric(arg); } static int get_uart(const char *arg) { int uart = uart_id(arg); if (uart < 0) bb_error_msg_and_die("illegal UART type: %s", arg); return uart; } static int serial_open(const char *dev, bool quiet) { int fd; fd = device_open(dev, O_RDWR | O_NONBLOCK); if (fd < 0 && !quiet) bb_simple_perror_msg(dev); return fd; } static int serial_ctl(int fd, int ops, struct serial_struct *serinfo) { int ret = 0; const char *err; if (ops & CTL_SET) { ret = ioctl(fd, TIOCSSERIAL, serinfo); if (ret < 0) { err = "can't set serial info"; goto fail; } } if (ops & CTL_CONFIG) { ret = ioctl(fd, TIOCSERCONFIG); if (ret < 0) { err = "can't autoconfigure port"; goto fail; } } if (ops & CTL_GET) { ret = ioctl(fd, TIOCGSERIAL, serinfo); if (ret < 0) { err = "can't get serial info"; goto fail; } } nodie: if (ops & CTL_CLOSE) close(fd); return ret; fail: bb_simple_perror_msg(err); if (ops & CTL_NODIE) goto nodie; exit(EXIT_FAILURE); } static void print_flag(const char **prefix, const char *flag) { printf("%s%s", *prefix, flag); *prefix = " "; } static void print_serial_flags(int serial_flags, enum print_mode mode, const char *prefix, const char *postfix) { int i; const char *spd, *pr; pr = prefix; spd = get_spd(serial_flags, mode); if (spd) print_flag(&pr, spd); for (i = CMD_FLAG_FIRST; i <= CMD_FLAG_LAST; i++) { if ((serial_flags & setbits[i]) && (mode > PRINT_SUMMARY || !cmd_noprint(i)) ) { print_flag(&pr, nth_string(commands, i)); } } puts(pr == prefix ? "" : postfix); } static void print_closing_wait(unsigned int closing_wait) { switch (closing_wait) { case ASYNC_CLOSING_WAIT_NONE: puts(STR_NONE); break; case ASYNC_CLOSING_WAIT_INF: puts(STR_INFINITE); break; default: printf("%u\n", closing_wait); } } static void serial_get(const char *device, enum print_mode mode) { int fd, ret; const char *uart, *prefix, *postfix; struct serial_struct serinfo; fd = serial_open(device, /*quiet:*/ mode == PRINT_SUMMARY); if (fd < 0) return; ret = serial_ctl(fd, CTL_GET | CTL_CLOSE | CTL_NODIE, &serinfo); if (ret < 0) return; uart = uart_type(serinfo.type); prefix = ", Flags: "; postfix = ""; switch (mode) { case PRINT_NORMAL: printf("%s, UART: %s, Port: 0x%.4x, IRQ: %d", device, uart, serinfo.port, serinfo.irq); break; case PRINT_SUMMARY: if (!serinfo.type) return; printf("%s at 0x%.4x (irq = %d) is a %s", device, serinfo.port, serinfo.irq, uart); prefix = " ("; postfix = ")"; break; case PRINT_FEDBACK: printf("%s uart %s port 0x%.4x irq %d baud_base %d", device, uart, serinfo.port, serinfo.irq, serinfo.baud_base); prefix = " "; break; case PRINT_ALL: printf("%s, Line %d, UART: %s, Port: 0x%.4x, IRQ: %d\n", device, serinfo.line, uart, serinfo.port, serinfo.irq); printf("\tBaud_base: %d, close_delay: %u, divisor: %d\n", serinfo.baud_base, serinfo.close_delay, serinfo.custom_divisor); printf("\tclosing_wait: "); print_closing_wait(serinfo.closing_wait); prefix = "\tFlags: "; postfix = "\n"; break; default: assert(0); } print_serial_flags(serinfo.flags, mode, prefix, postfix); } static int find_cmd(const char *cmd) { int idx; idx = index_in_strings_case_insensitive(commands, cmd); if (idx < 0) bb_error_msg_and_die("invalid flag: %s", cmd); return idx; } static void serial_set(char **arg, int opts) { struct serial_struct serinfo; int cmd; const char *word; int fd; fd = serial_open(*arg++, /*quiet:*/ false); if (fd < 0) exit(201); serial_ctl(fd, CTL_GET, &serinfo); if (opts & OPT_ZERO) serinfo.flags = 0; while (*arg) { int invert; word = *arg++; invert = (*word == '^'); word += invert; cmd = find_cmd(word); if (*arg == NULL && cmd_need_arg(cmd)) bb_error_msg_and_die(bb_msg_requires_arg, word); if (invert && !cmd_is_flag(cmd)) bb_error_msg_and_die("can't invert %s", word); switch (cmd) { case CMD_SPD_NORMAL: case CMD_SPD_HI: case CMD_SPD_VHI: case CMD_SPD_SHI: case CMD_SPD_WARP: case CMD_SPD_CUST: serinfo.flags &= ~ASYNC_SPD_MASK; /* fallthrough */ case CMD_FLAG_SAK: case CMD_FLAG_FOURPORT: case CMD_FLAG_NUP_NOTIFY: case CMD_FLAG_SKIP_TEST: case CMD_FLAG_AUTO_IRQ: case CMD_FLAG_SPLIT_TERMIOS: case CMD_FLAG_SESSION_LOCKOUT: case CMD_FLAG_PGRP_LOCKOUT: case CMD_FLAG_CALLOUT_NOHUP: case CMD_FLAG_LOW_LATENCY: if (invert) serinfo.flags &= ~setbits[cmd]; else serinfo.flags |= setbits[cmd]; break; case CMD_PORT: serinfo.port = get_numeric(*arg++); break; case CMD_IRQ: serinfo.irq = get_numeric(*arg++); break; case CMD_DIVISOR: serinfo.custom_divisor = get_numeric(*arg++); break; case CMD_UART: serinfo.type = get_uart(*arg++); break; case CMD_BASE: serinfo.baud_base = get_numeric(*arg++); break; case CMD_DELAY: serinfo.close_delay = get_numeric(*arg++); break; case CMD_WAIT: serinfo.closing_wait = get_wait(*arg++); break; case CMD_AUTOCONFIG: serial_ctl(fd, CTL_SET | CTL_CONFIG | CTL_GET, &serinfo); break; default: assert(0); } } serial_ctl(fd, CTL_SET | CTL_CLOSE, &serinfo); } int setserial_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int setserial_main(int argc UNUSED_PARAM, char **argv) { int opts; opt_complementary = "-1:b-aG:G-ab:a-bG"; opts = getopt32(argv, "bGavzg"); argv += optind; if (!argv[1]) /* one arg only? */ opts |= OPT_GET; if (!(opts & OPT_GET)) { serial_set(argv, opts); argv[1] = NULL; } if (opts & (OPT_VERBOSE | OPT_GET)) { do { serial_get(*argv++, opts & OPT_MODE_MASK); } while (*argv); } return EXIT_SUCCESS; } busybox-1.22.1/miscutils/last.c0000644000000000000000000001003112263563520015110 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * last implementation for busybox * * Copyright (C) 2003-2004 by Erik Andersen * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define last_trivial_usage //usage: ""IF_FEATURE_LAST_FANCY("[-HW] [-f FILE]") //usage:#define last_full_usage "\n\n" //usage: "Show listing of the last users that logged into the system" //usage: IF_FEATURE_LAST_FANCY( "\n" /* //usage: "\n -H Show header line" */ //usage: "\n -W Display with no host column truncation" //usage: "\n -f FILE Read from FILE instead of /var/log/wtmp" //usage: ) #include "libbb.h" /* NB: ut_name and ut_user are the same field, use only one name (ut_user) * to reduce confusion */ #ifndef SHUTDOWN_TIME # define SHUTDOWN_TIME 254 #endif /* Grr... utmp char[] members do not have to be nul-terminated. * Do what we can while still keeping this reasonably small. * Note: We are assuming the ut_id[] size is fixed at 4. */ #if defined UT_LINESIZE \ && ((UT_LINESIZE != 32) || (UT_NAMESIZE != 32) || (UT_HOSTSIZE != 256)) #error struct utmp member char[] size(s) have changed! #elif defined __UT_LINESIZE \ && ((__UT_LINESIZE != 32) || (__UT_NAMESIZE != 64) || (__UT_HOSTSIZE != 256)) #error struct utmp member char[] size(s) have changed! #endif #if EMPTY != 0 || RUN_LVL != 1 || BOOT_TIME != 2 || NEW_TIME != 3 || \ OLD_TIME != 4 #error Values for the ut_type field of struct utmp changed #endif int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int last_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { struct utmp ut; int n, file = STDIN_FILENO; time_t t_tmp; off_t pos; static const char _ut_usr[] ALIGN1 = "runlevel\0" "reboot\0" "shutdown\0"; static const char _ut_lin[] ALIGN1 = "~\0" "{\0" "|\0" /* "LOGIN\0" "date\0" */; enum { TYPE_RUN_LVL = RUN_LVL, /* 1 */ TYPE_BOOT_TIME = BOOT_TIME, /* 2 */ TYPE_SHUTDOWN_TIME = SHUTDOWN_TIME }; enum { _TILDE = EMPTY, /* 0 */ TYPE_NEW_TIME, /* NEW_TIME, 3 */ TYPE_OLD_TIME /* OLD_TIME, 4 */ }; if (argv[1]) { bb_show_usage(); } file = xopen(bb_path_wtmp_file, O_RDONLY); printf("%-10s %-14s %-18s %-12.12s %s\n", "USER", "TTY", "HOST", "LOGIN", "TIME"); /* yikes. We reverse over the file and that is a not too elegant way */ pos = xlseek(file, 0, SEEK_END); pos = lseek(file, pos - sizeof(ut), SEEK_SET); while ((n = full_read(file, &ut, sizeof(ut))) > 0) { if (n != sizeof(ut)) { bb_perror_msg_and_die("short read"); } n = index_in_strings(_ut_lin, ut.ut_line); if (n == _TILDE) { /* '~' */ #if 1 /* do we really need to be cautious here? */ n = index_in_strings(_ut_usr, ut.ut_user); if (++n > 0) ut.ut_type = n != 3 ? n : SHUTDOWN_TIME; #else if (strncmp(ut.ut_user, "shutdown", 8) == 0) ut.ut_type = SHUTDOWN_TIME; else if (strncmp(ut.ut_user, "reboot", 6) == 0) ut.ut_type = BOOT_TIME; else if (strncmp(ut.ut_user, "runlevel", 8) == 0) ut.ut_type = RUN_LVL; #endif } else { if (ut.ut_user[0] == '\0' || strcmp(ut.ut_user, "LOGIN") == 0) { /* Don't bother. This means we can't find how long * someone was logged in for. Oh well. */ goto next; } if (ut.ut_type != DEAD_PROCESS && ut.ut_user[0] && ut.ut_line[0] ) { ut.ut_type = USER_PROCESS; } if (strcmp(ut.ut_user, "date") == 0) { if (n == TYPE_OLD_TIME) { /* '|' */ ut.ut_type = OLD_TIME; } if (n == TYPE_NEW_TIME) { /* '{' */ ut.ut_type = NEW_TIME; } } } if (ut.ut_type != USER_PROCESS) { switch (ut.ut_type) { case OLD_TIME: case NEW_TIME: case RUN_LVL: case SHUTDOWN_TIME: goto next; case BOOT_TIME: strcpy(ut.ut_line, "system boot"); } } /* manpages say ut_tv.tv_sec *is* time_t, * but some systems have it wrong */ t_tmp = (time_t)ut.ut_tv.tv_sec; printf("%-10s %-14s %-18s %-12.12s\n", ut.ut_user, ut.ut_line, ut.ut_host, ctime(&t_tmp) + 4); next: pos -= sizeof(ut); if (pos <= 0) break; /* done. */ xlseek(file, pos, SEEK_SET); } fflush_stdout_and_exit(EXIT_SUCCESS); } busybox-1.22.1/miscutils/wall.c0000644000000000000000000000303112263563520015106 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * wall - write a message to all logged-in users * Copyright (c) 2009 Bernhard Reutner-Fischer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //config:config WALL //config: bool "wall" //config: default y //config: depends on FEATURE_UTMP //config: help //config: Write a message to all users that are logged in. /* Needs to be run by root or be suid root - needs to write to /dev/TTY: */ //applet:IF_WALL(APPLET(wall, BB_DIR_USR_BIN, BB_SUID_REQUIRE)) //kbuild:lib-$(CONFIG_WALL) += wall.o //usage:#define wall_trivial_usage //usage: "[FILE]" //usage:#define wall_full_usage "\n\n" //usage: "Write content of FILE or stdin to all logged-in users" //usage: //usage:#define wall_sample_usage //usage: "echo foo | wall\n" //usage: "wall ./mymessage" #include "libbb.h" int wall_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int wall_main(int argc UNUSED_PARAM, char **argv) { struct utmp *ut; char *msg; int fd; fd = STDIN_FILENO; if (argv[1]) { /* The applet is setuid. * Access to the file must be under user's uid/gid. */ fd = xopen_as_uid_gid(argv[1], O_RDONLY, getuid(), getgid()); } msg = xmalloc_read(fd, NULL); if (ENABLE_FEATURE_CLEAN_UP && argv[1]) close(fd); setutent(); while ((ut = getutent()) != NULL) { char *line; if (ut->ut_type != USER_PROCESS) continue; line = concat_path_file("/dev", ut->ut_line); xopen_xwrite_close(line, msg); free(line); } if (ENABLE_FEATURE_CLEAN_UP) { endutent(); free(msg); } return EXIT_SUCCESS; } busybox-1.22.1/miscutils/nandwrite.c0000644000000000000000000001536412263563520016156 0ustar rootroot/* * nandwrite and nanddump ported to busybox from mtd-utils * * Author: Baruch Siach , Orex Computed Radiography * * Licensed under GPLv2, see file LICENSE in this source tree. * * TODO: add support for large (>4GB) MTD devices */ //config:config NANDWRITE //config: bool "nandwrite" //config: default y //config: select PLATFORM_LINUX //config: help //config: Write to the specified MTD device, with bad blocks awareness //config: //config:config NANDDUMP //config: bool "nanddump" //config: default y //config: select PLATFORM_LINUX //config: help //config: Dump the content of raw NAND chip //applet:IF_NANDWRITE(APPLET(nandwrite, BB_DIR_USR_SBIN, BB_SUID_DROP)) //applet:IF_NANDDUMP(APPLET_ODDNAME(nanddump, nandwrite, BB_DIR_USR_SBIN, BB_SUID_DROP, nanddump)) //kbuild:lib-$(CONFIG_NANDWRITE) += nandwrite.o //kbuild:lib-$(CONFIG_NANDDUMP) += nandwrite.o //usage:#define nandwrite_trivial_usage //usage: "[-p] [-s ADDR] MTD_DEVICE [FILE]" //usage:#define nandwrite_full_usage "\n\n" //usage: "Write to MTD_DEVICE\n" //usage: "\n -p Pad to page size" //usage: "\n -s ADDR Start address" //usage:#define nanddump_trivial_usage //usage: "[-o] [-b] [-s ADDR] [-l LEN] [-f FILE] MTD_DEVICE" //usage:#define nanddump_full_usage "\n\n" //usage: "Dump MTD_DEVICE\n" //usage: "\n -o Dump oob data" //usage: "\n -b Omit bad block from the dump" //usage: "\n -s ADDR Start address" //usage: "\n -l LEN Length" //usage: "\n -f FILE Dump to file ('-' for stdout)" #include "libbb.h" #include #define IS_NANDDUMP (ENABLE_NANDDUMP && (!ENABLE_NANDWRITE || (applet_name[4] == 'd'))) #define IS_NANDWRITE (ENABLE_NANDWRITE && (!ENABLE_NANDDUMP || (applet_name[4] != 'd'))) #define OPT_p (1 << 0) /* nandwrite only */ #define OPT_o (1 << 0) /* nanddump only */ #define OPT_s (1 << 1) #define OPT_b (1 << 2) #define OPT_f (1 << 3) #define OPT_l (1 << 4) /* helper for writing out 0xff for bad blocks pad */ static void dump_bad(struct mtd_info_user *meminfo, unsigned len, int oob) { unsigned char buf[meminfo->writesize]; unsigned count; /* round len to the next page */ len = (len | ~(meminfo->writesize - 1)) + 1; memset(buf, 0xff, sizeof(buf)); for (count = 0; count < len; count += meminfo->writesize) { xwrite(STDOUT_FILENO, buf, meminfo->writesize); if (oob) xwrite(STDOUT_FILENO, buf, meminfo->oobsize); } } static unsigned next_good_eraseblock(int fd, struct mtd_info_user *meminfo, unsigned block_offset) { while (1) { loff_t offs; if (block_offset >= meminfo->size) { if (IS_NANDWRITE) bb_error_msg_and_die("not enough space in MTD device"); return block_offset; /* let the caller exit */ } offs = block_offset; if (xioctl(fd, MEMGETBADBLOCK, &offs) == 0) return block_offset; /* ioctl returned 1 => "bad block" */ if (IS_NANDWRITE) printf("Skipping bad block at 0x%08x\n", block_offset); block_offset += meminfo->erasesize; } } int nandwrite_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int nandwrite_main(int argc UNUSED_PARAM, char **argv) { /* Buffer for OOB data */ unsigned char *oobbuf; unsigned opts; int fd; ssize_t cnt; unsigned mtdoffset, meminfo_writesize, blockstart, limit; unsigned end_addr = ~0; struct mtd_info_user meminfo; struct mtd_oob_buf oob; unsigned char *filebuf; const char *opt_s = "0", *opt_f = "-", *opt_l; if (IS_NANDDUMP) { opt_complementary = "=1"; opts = getopt32(argv, "os:bf:l:", &opt_s, &opt_f, &opt_l); } else { /* nandwrite */ opt_complementary = "-1:?2"; opts = getopt32(argv, "ps:", &opt_s); } argv += optind; if (IS_NANDWRITE && argv[1]) opt_f = argv[1]; if (!LONE_DASH(opt_f)) { int tmp_fd = xopen(opt_f, IS_NANDDUMP ? O_WRONLY | O_TRUNC | O_CREAT : O_RDONLY ); xmove_fd(tmp_fd, IS_NANDDUMP ? STDOUT_FILENO : STDIN_FILENO); } fd = xopen(argv[0], IS_NANDWRITE ? O_RDWR : O_RDONLY); xioctl(fd, MEMGETINFO, &meminfo); mtdoffset = xstrtou(opt_s, 0); if (IS_NANDDUMP && (opts & OPT_l)) { unsigned length = xstrtou(opt_l, 0); if (length < meminfo.size - mtdoffset) end_addr = mtdoffset + length; } /* Pull it into a CPU register (hopefully) - smaller code that way */ meminfo_writesize = meminfo.writesize; if (mtdoffset & (meminfo_writesize - 1)) bb_error_msg_and_die("start address is not page aligned"); filebuf = xmalloc(meminfo_writesize); oobbuf = xmalloc(meminfo.oobsize); oob.start = 0; oob.length = meminfo.oobsize; oob.ptr = oobbuf; blockstart = mtdoffset & ~(meminfo.erasesize - 1); if (blockstart != mtdoffset) { unsigned tmp; /* mtdoffset is in the middle of an erase block, verify that * this block is OK. Advance mtdoffset only if this block is * bad. */ tmp = next_good_eraseblock(fd, &meminfo, blockstart); if (tmp != blockstart) { /* bad block(s), advance mtdoffset */ if (IS_NANDDUMP && !(opts & OPT_b)) { int bad_len = MIN(tmp, end_addr) - mtdoffset; dump_bad(&meminfo, bad_len, opts & OPT_o); } mtdoffset = tmp; } } cnt = -1; limit = MIN(meminfo.size, end_addr); while (mtdoffset < limit) { int input_fd = IS_NANDWRITE ? STDIN_FILENO : fd; int output_fd = IS_NANDWRITE ? fd : STDOUT_FILENO; blockstart = mtdoffset & ~(meminfo.erasesize - 1); if (blockstart == mtdoffset) { /* starting a new eraseblock */ mtdoffset = next_good_eraseblock(fd, &meminfo, blockstart); if (IS_NANDWRITE) printf("Writing at 0x%08x\n", mtdoffset); else if (mtdoffset > blockstart && !(opts & OPT_b)) { int bad_len = MIN(mtdoffset, limit) - blockstart; dump_bad(&meminfo, bad_len, opts & OPT_o); } if (mtdoffset >= limit) break; } xlseek(fd, mtdoffset, SEEK_SET); /* get some more data from input */ cnt = full_read(input_fd, filebuf, meminfo_writesize); if (cnt == 0) { /* even with -p, we do not pad past the end of input * (-p only zero-pads last incomplete page) */ break; } if (cnt < meminfo_writesize) { if (IS_NANDDUMP) bb_error_msg_and_die("short read"); if (!(opts & OPT_p)) bb_error_msg_and_die("input size is not rounded up to page size, " "use -p to zero pad"); /* zero pad to end of write block */ memset(filebuf + cnt, 0, meminfo_writesize - cnt); } xwrite(output_fd, filebuf, meminfo_writesize); if (IS_NANDDUMP && (opts & OPT_o)) { /* Dump OOB data */ oob.start = mtdoffset; xioctl(fd, MEMREADOOB, &oob); xwrite(output_fd, oobbuf, meminfo.oobsize); } mtdoffset += meminfo_writesize; if (cnt < meminfo_writesize) break; } if (IS_NANDWRITE && cnt != 0) { /* We filled entire MTD, but did we reach EOF on input? */ if (full_read(STDIN_FILENO, filebuf, meminfo_writesize) != 0) { /* no */ bb_error_msg_and_die("not enough space in MTD device"); } } if (ENABLE_FEATURE_CLEAN_UP) { free(filebuf); close(fd); } return EXIT_SUCCESS; } busybox-1.22.1/miscutils/time.c0000644000000000000000000003242412263563520015115 0ustar rootroot/* vi: set sw=4 ts=4: */ /* 'time' utility to display resource usage of processes. Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc. Licensed under GPLv2, see file LICENSE in this source tree. */ /* Originally written by David Keppel . Heavily modified by David MacKenzie . Heavily modified for busybox by Erik Andersen */ //usage:#define time_trivial_usage //usage: "[-v] PROG ARGS" //usage:#define time_full_usage "\n\n" //usage: "Run PROG, display resource usage when it exits\n" //usage: "\n -v Verbose" #include "libbb.h" #include /* getrusage */ /* Information on the resources used by a child process. */ typedef struct { int waitstatus; struct rusage ru; unsigned elapsed_ms; /* Wallclock time of process. */ } resource_t; /* msec = milliseconds = 1/1,000 (1*10e-3) second. usec = microseconds = 1/1,000,000 (1*10e-6) second. */ #define UL unsigned long static const char default_format[] ALIGN1 = "real\t%E\nuser\t%u\nsys\t%T"; /* The output format for the -p option .*/ static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S"; /* Format string for printing all statistics verbosely. Keep this output to 24 lines so users on terminals can see it all.*/ static const char long_format[] ALIGN1 = "\tCommand being timed: \"%C\"\n" "\tUser time (seconds): %U\n" "\tSystem time (seconds): %S\n" "\tPercent of CPU this job got: %P\n" "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n" "\tAverage shared text size (kbytes): %X\n" "\tAverage unshared data size (kbytes): %D\n" "\tAverage stack size (kbytes): %p\n" "\tAverage total size (kbytes): %K\n" "\tMaximum resident set size (kbytes): %M\n" "\tAverage resident set size (kbytes): %t\n" "\tMajor (requiring I/O) page faults: %F\n" "\tMinor (reclaiming a frame) page faults: %R\n" "\tVoluntary context switches: %w\n" "\tInvoluntary context switches: %c\n" "\tSwaps: %W\n" "\tFile system inputs: %I\n" "\tFile system outputs: %O\n" "\tSocket messages sent: %s\n" "\tSocket messages received: %r\n" "\tSignals delivered: %k\n" "\tPage size (bytes): %Z\n" "\tExit status: %x"; /* Wait for and fill in data on child process PID. Return 0 on error, 1 if ok. */ /* pid_t is short on BSDI, so don't try to promote it. */ static void resuse_end(pid_t pid, resource_t *resp) { pid_t caught; /* Ignore signals, but don't ignore the children. When wait3 * returns the child process, set the time the command finished. */ while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) { if (caught == -1 && errno != EINTR) { bb_perror_msg("wait"); return; } } resp->elapsed_ms = monotonic_ms() - resp->elapsed_ms; } static void printargv(char *const *argv) { const char *fmt = " %s" + 1; do { printf(fmt, *argv); fmt = " %s"; } while (*++argv); } /* Return the number of kilobytes corresponding to a number of pages PAGES. (Actually, we use it to convert pages*ticks into kilobytes*ticks.) Try to do arithmetic so that the risk of overflow errors is minimized. This is funky since the pagesize could be less than 1K. Note: Some machines express getrusage statistics in terms of K, others in terms of pages. */ static unsigned long ptok(const unsigned pagesize, const unsigned long pages) { unsigned long tmp; /* Conversion. */ if (pages > (LONG_MAX / pagesize)) { /* Could overflow. */ tmp = pages / 1024; /* Smaller first, */ return tmp * pagesize; /* then larger. */ } /* Could underflow. */ tmp = pages * pagesize; /* Larger first, */ return tmp / 1024; /* then smaller. */ } /* summarize: Report on the system use of a command. Print the FMT argument except that `%' sequences have special meaning, and `\n' and `\t' are translated into newline and tab, respectively, and `\\' is translated into `\'. The character following a `%' can be: (* means the tcsh time builtin also recognizes it) % == a literal `%' C == command name and arguments * D == average unshared data size in K (ru_idrss+ru_isrss) * E == elapsed real (wall clock) time in [hour:]min:sec * F == major page faults (required physical I/O) (ru_majflt) * I == file system inputs (ru_inblock) * K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss) * M == maximum resident set size in K (ru_maxrss) * O == file system outputs (ru_oublock) * P == percent of CPU this job got (total cpu time / elapsed time) * R == minor page faults (reclaims; no physical I/O involved) (ru_minflt) * S == system (kernel) time (seconds) (ru_stime) * T == system time in [hour:]min:sec * U == user time (seconds) (ru_utime) * u == user time in [hour:]min:sec * W == times swapped out (ru_nswap) * X == average amount of shared text in K (ru_ixrss) Z == page size * c == involuntary context switches (ru_nivcsw) e == elapsed real time in seconds * k == signals delivered (ru_nsignals) p == average unshared stack size in K (ru_isrss) * r == socket messages received (ru_msgrcv) * s == socket messages sent (ru_msgsnd) t == average resident set size in K (ru_idrss) * w == voluntary context switches (ru_nvcsw) x == exit status of command Various memory usages are found by converting from page-seconds to kbytes by multiplying by the page size, dividing by 1024, and dividing by elapsed real time. FMT is the format string, interpreted as described above. COMMAND is the command and args that are being summarized. RESP is resource information on the command. */ #ifndef TICKS_PER_SEC #define TICKS_PER_SEC 100 #endif static void summarize(const char *fmt, char **command, resource_t *resp) { unsigned vv_ms; /* Elapsed virtual (CPU) milliseconds */ unsigned cpu_ticks; /* Same, in "CPU ticks" */ unsigned pagesize = getpagesize(); /* Impossible: we do not use WUNTRACED flag in wait()... if (WIFSTOPPED(resp->waitstatus)) printf("Command stopped by signal %u\n", WSTOPSIG(resp->waitstatus)); else */ if (WIFSIGNALED(resp->waitstatus)) printf("Command terminated by signal %u\n", WTERMSIG(resp->waitstatus)); else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus)) printf("Command exited with non-zero status %u\n", WEXITSTATUS(resp->waitstatus)); vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000 + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000; #if (1000 / TICKS_PER_SEC) * TICKS_PER_SEC == 1000 /* 1000 is exactly divisible by TICKS_PER_SEC (typical) */ cpu_ticks = vv_ms / (1000 / TICKS_PER_SEC); #else cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000; #endif if (!cpu_ticks) cpu_ticks = 1; /* we divide by it, must be nonzero */ while (*fmt) { /* Handle leading literal part */ int n = strcspn(fmt, "%\\"); if (n) { printf("%.*s", n, fmt); fmt += n; continue; } switch (*fmt) { #ifdef NOT_NEEDED /* Handle literal char */ /* Usually we optimize for size, but there is a limit * for everything. With this we do a lot of 1-byte writes */ default: bb_putchar(*fmt); break; #endif case '%': switch (*++fmt) { #ifdef NOT_NEEDED_YET /* Our format strings do not have these */ /* and we do not take format str from user */ default: bb_putchar('%'); /*FALLTHROUGH*/ case '%': if (!*fmt) goto ret; bb_putchar(*fmt); break; #endif case 'C': /* The command that got timed. */ printargv(command); break; case 'D': /* Average unshared data size. */ printf("%lu", (ptok(pagesize, (UL) resp->ru.ru_idrss) + ptok(pagesize, (UL) resp->ru.ru_isrss)) / cpu_ticks); break; case 'E': { /* Elapsed real (wall clock) time. */ unsigned seconds = resp->elapsed_ms / 1000; if (seconds >= 3600) /* One hour -> h:m:s. */ printf("%uh %um %02us", seconds / 3600, (seconds % 3600) / 60, seconds % 60); else printf("%um %u.%02us", /* -> m:s. */ seconds / 60, seconds % 60, (unsigned)(resp->elapsed_ms / 10) % 100); break; } case 'F': /* Major page faults. */ printf("%lu", resp->ru.ru_majflt); break; case 'I': /* Inputs. */ printf("%lu", resp->ru.ru_inblock); break; case 'K': /* Average mem usage == data+stack+text. */ printf("%lu", (ptok(pagesize, (UL) resp->ru.ru_idrss) + ptok(pagesize, (UL) resp->ru.ru_isrss) + ptok(pagesize, (UL) resp->ru.ru_ixrss)) / cpu_ticks); break; case 'M': /* Maximum resident set size. */ printf("%lu", ptok(pagesize, (UL) resp->ru.ru_maxrss)); break; case 'O': /* Outputs. */ printf("%lu", resp->ru.ru_oublock); break; case 'P': /* Percent of CPU this job got. */ /* % cpu is (total cpu time)/(elapsed time). */ if (resp->elapsed_ms > 0) printf("%u%%", (unsigned)(vv_ms * 100 / resp->elapsed_ms)); else printf("?%%"); break; case 'R': /* Minor page faults (reclaims). */ printf("%lu", resp->ru.ru_minflt); break; case 'S': /* System time. */ printf("%u.%02u", (unsigned)resp->ru.ru_stime.tv_sec, (unsigned)(resp->ru.ru_stime.tv_usec / 10000)); break; case 'T': /* System time. */ if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */ printf("%uh %um %02us", (unsigned)(resp->ru.ru_stime.tv_sec / 3600), (unsigned)(resp->ru.ru_stime.tv_sec % 3600) / 60, (unsigned)(resp->ru.ru_stime.tv_sec % 60)); else printf("%um %u.%02us", /* -> m:s. */ (unsigned)(resp->ru.ru_stime.tv_sec / 60), (unsigned)(resp->ru.ru_stime.tv_sec % 60), (unsigned)(resp->ru.ru_stime.tv_usec / 10000)); break; case 'U': /* User time. */ printf("%u.%02u", (unsigned)resp->ru.ru_utime.tv_sec, (unsigned)(resp->ru.ru_utime.tv_usec / 10000)); break; case 'u': /* User time. */ if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */ printf("%uh %um %02us", (unsigned)(resp->ru.ru_utime.tv_sec / 3600), (unsigned)(resp->ru.ru_utime.tv_sec % 3600) / 60, (unsigned)(resp->ru.ru_utime.tv_sec % 60)); else printf("%um %u.%02us", /* -> m:s. */ (unsigned)(resp->ru.ru_utime.tv_sec / 60), (unsigned)(resp->ru.ru_utime.tv_sec % 60), (unsigned)(resp->ru.ru_utime.tv_usec / 10000)); break; case 'W': /* Times swapped out. */ printf("%lu", resp->ru.ru_nswap); break; case 'X': /* Average shared text size. */ printf("%lu", ptok(pagesize, (UL) resp->ru.ru_ixrss) / cpu_ticks); break; case 'Z': /* Page size. */ printf("%u", pagesize); break; case 'c': /* Involuntary context switches. */ printf("%lu", resp->ru.ru_nivcsw); break; case 'e': /* Elapsed real time in seconds. */ printf("%u.%02u", (unsigned)resp->elapsed_ms / 1000, (unsigned)(resp->elapsed_ms / 10) % 100); break; case 'k': /* Signals delivered. */ printf("%lu", resp->ru.ru_nsignals); break; case 'p': /* Average stack segment. */ printf("%lu", ptok(pagesize, (UL) resp->ru.ru_isrss) / cpu_ticks); break; case 'r': /* Incoming socket messages received. */ printf("%lu", resp->ru.ru_msgrcv); break; case 's': /* Outgoing socket messages sent. */ printf("%lu", resp->ru.ru_msgsnd); break; case 't': /* Average resident set size. */ printf("%lu", ptok(pagesize, (UL) resp->ru.ru_idrss) / cpu_ticks); break; case 'w': /* Voluntary context switches. */ printf("%lu", resp->ru.ru_nvcsw); break; case 'x': /* Exit status. */ printf("%u", WEXITSTATUS(resp->waitstatus)); break; } break; #ifdef NOT_NEEDED_YET case '\\': /* Format escape. */ switch (*++fmt) { default: bb_putchar('\\'); /*FALLTHROUGH*/ case '\\': if (!*fmt) goto ret; bb_putchar(*fmt); break; case 't': bb_putchar('\t'); break; case 'n': bb_putchar('\n'); break; } break; #endif } ++fmt; } /* ret: */ bb_putchar('\n'); } /* Run command CMD and return statistics on it. Put the statistics in *RESP. */ static void run_command(char *const *cmd, resource_t *resp) { pid_t pid; void (*interrupt_signal)(int); void (*quit_signal)(int); resp->elapsed_ms = monotonic_ms(); pid = xvfork(); if (pid == 0) { /* Child */ BB_EXECVP_or_die((char**)cmd); } /* Have signals kill the child but not self (if possible). */ //TODO: just block all sigs? and reenable them in the very end in main? interrupt_signal = signal(SIGINT, SIG_IGN); quit_signal = signal(SIGQUIT, SIG_IGN); resuse_end(pid, resp); /* Re-enable signals. */ signal(SIGINT, interrupt_signal); signal(SIGQUIT, quit_signal); } int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int time_main(int argc UNUSED_PARAM, char **argv) { resource_t res; const char *output_format = default_format; int opt; opt_complementary = "-1"; /* at least one arg */ /* "+": stop on first non-option */ opt = getopt32(argv, "+vp"); argv += optind; if (opt & 1) output_format = long_format; if (opt & 2) output_format = posix_format; run_command(argv, &res); /* Cheat. printf's are shorter :) */ xdup2(STDERR_FILENO, STDOUT_FILENO); summarize(output_format, argv, &res); if (WIFSTOPPED(res.waitstatus)) return WSTOPSIG(res.waitstatus); if (WIFSIGNALED(res.waitstatus)) return WTERMSIG(res.waitstatus); if (WIFEXITED(res.waitstatus)) return WEXITSTATUS(res.waitstatus); fflush_stdout_and_exit(EXIT_SUCCESS); } busybox-1.22.1/miscutils/runlevel.c0000644000000000000000000000245312263563520016012 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Prints out the previous and the current runlevel. * * Version: @(#)runlevel 1.20 16-Apr-1997 MvS * * This file is part of the sysvinit suite, * Copyright 1991-1997 Miquel van Smoorenburg. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * initially busyboxified by Bernhard Reutner-Fischer */ //usage:#define runlevel_trivial_usage //usage: "[FILE]" //usage:#define runlevel_full_usage "\n\n" //usage: "Find the current and previous system runlevel\n" //usage: "\n" //usage: "If no utmp FILE exists or if no runlevel record can be found,\n" //usage: "print \"unknown\"" //usage: //usage:#define runlevel_example_usage //usage: "$ runlevel /var/run/utmp\n" //usage: "N 2" #include "libbb.h" int runlevel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int runlevel_main(int argc UNUSED_PARAM, char **argv) { struct utmp *ut; char prev; if (argv[1]) utmpname(argv[1]); setutent(); while ((ut = getutent()) != NULL) { if (ut->ut_type == RUN_LVL) { prev = ut->ut_pid / 256; if (prev == 0) prev = 'N'; printf("%c %c\n", prev, ut->ut_pid % 256); if (ENABLE_FEATURE_CLEAN_UP) endutent(); return 0; } } puts("unknown"); if (ENABLE_FEATURE_CLEAN_UP) endutent(); return 1; } busybox-1.22.1/miscutils/adjtimex.c0000644000000000000000000000712612263563520015765 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * adjtimex.c - read, and possibly modify, the Linux kernel `timex' variables. * * Originally written: October 1997 * Last hack: March 2001 * Copyright 1997, 2000, 2001 Larry Doolittle * * busyboxed 20 March 2001, Larry Doolittle * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define adjtimex_trivial_usage //usage: "[-q] [-o OFF] [-f FREQ] [-p TCONST] [-t TICK]" //usage:#define adjtimex_full_usage "\n\n" //usage: "Read and optionally set system timebase parameters. See adjtimex(2)\n" //usage: "\n -q Quiet" //usage: "\n -o OFF Time offset, microseconds" //usage: "\n -f FREQ Frequency adjust, integer kernel units (65536 is 1ppm)" //usage: "\n (positive values make clock run faster)" //usage: "\n -t TICK Microseconds per tick, usually 10000" //usage: "\n -p TCONST" #include "libbb.h" #ifdef __BIONIC__ # include #else # include #endif static const uint16_t statlist_bit[] = { STA_PLL, STA_PPSFREQ, STA_PPSTIME, STA_FLL, STA_INS, STA_DEL, STA_UNSYNC, STA_FREQHOLD, STA_PPSSIGNAL, STA_PPSJITTER, STA_PPSWANDER, STA_PPSERROR, STA_CLOCKERR, 0 }; static const char statlist_name[] = "PLL" "\0" "PPSFREQ" "\0" "PPSTIME" "\0" "FFL" "\0" "INS" "\0" "DEL" "\0" "UNSYNC" "\0" "FREQHOLD" "\0" "PPSSIGNAL" "\0" "PPSJITTER" "\0" "PPSWANDER" "\0" "PPSERROR" "\0" "CLOCKERR" ; static const char ret_code_descript[] = "clock synchronized" "\0" "insert leap second" "\0" "delete leap second" "\0" "leap second in progress" "\0" "leap second has occurred" "\0" "clock not synchronized" ; int adjtimex_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int adjtimex_main(int argc UNUSED_PARAM, char **argv) { enum { OPT_quiet = 0x1 }; unsigned opt; char *opt_o, *opt_f, *opt_p, *opt_t; struct timex txc; int i, ret; const char *descript; opt_complementary = "=0"; /* no valid non-option parameters */ opt = getopt32(argv, "qo:f:p:t:", &opt_o, &opt_f, &opt_p, &opt_t); txc.modes = 0; //if (opt & 0x1) // -q if (opt & 0x2) { // -o txc.offset = xatol(opt_o); txc.modes |= ADJ_OFFSET_SINGLESHOT; } if (opt & 0x4) { // -f txc.freq = xatol(opt_f); txc.modes |= ADJ_FREQUENCY; } if (opt & 0x8) { // -p txc.constant = xatol(opt_p); txc.modes |= ADJ_TIMECONST; } if (opt & 0x10) { // -t txc.tick = xatol(opt_t); txc.modes |= ADJ_TICK; } ret = adjtimex(&txc); if (ret < 0) { bb_perror_nomsg_and_die(); } if (!(opt & OPT_quiet)) { int sep; const char *name; printf( " mode: %d\n" "-o offset: %ld\n" "-f frequency: %ld\n" " maxerror: %ld\n" " esterror: %ld\n" " status: %d (", txc.modes, txc.offset, txc.freq, txc.maxerror, txc.esterror, txc.status); /* representative output of next code fragment: "PLL | PPSTIME" */ name = statlist_name; sep = 0; for (i = 0; statlist_bit[i]; i++) { if (txc.status & statlist_bit[i]) { if (sep) fputs(" | ", stdout); fputs(name, stdout); sep = 1; } name += strlen(name) + 1; } descript = "error"; if (ret <= 5) descript = nth_string(ret_code_descript, ret); printf(")\n" "-p timeconstant: %ld\n" " precision: %ld\n" " tolerance: %ld\n" "-t tick: %ld\n" " time.tv_sec: %ld\n" " time.tv_usec: %ld\n" " return value: %d (%s)\n", txc.constant, txc.precision, txc.tolerance, txc.tick, (long)txc.time.tv_sec, (long)txc.time.tv_usec, ret, descript); } return 0; } busybox-1.22.1/miscutils/crond.c0000644000000000000000000005671212263563520015272 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * crond -d[#] -c -f -b * * run as root, but NOT setuid root * * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com) * (version 2.3.2) * Vladimir Oleynik (C) 2002 * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define crond_trivial_usage //usage: "-fbS -l N " IF_FEATURE_CROND_D("-d N ") "-L LOGFILE -c DIR" //usage:#define crond_full_usage "\n\n" //usage: " -f Foreground" //usage: "\n -b Background (default)" //usage: "\n -S Log to syslog (default)" //usage: "\n -l Set log level. 0 is the most verbose, default 8" //usage: IF_FEATURE_CROND_D( //usage: "\n -d Set log level, log to stderr" //usage: ) //usage: "\n -L Log to file" //usage: "\n -c Working dir" #include "libbb.h" #include /* glibc frees previous setenv'ed value when we do next setenv() * of the same variable. uclibc does not do this! */ #if (defined(__GLIBC__) && !defined(__UCLIBC__)) /* || OTHER_SAFE_LIBC... */ # define SETENV_LEAKS 0 #else # define SETENV_LEAKS 1 #endif #define TMPDIR CONFIG_FEATURE_CROND_DIR #define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs" #ifndef SENDMAIL # define SENDMAIL "sendmail" #endif #ifndef SENDMAIL_ARGS # define SENDMAIL_ARGS "-ti" #endif #ifndef CRONUPDATE # define CRONUPDATE "cron.update" #endif #ifndef MAXLINES # define MAXLINES 256 /* max lines in non-root crontabs */ #endif typedef struct CronFile { struct CronFile *cf_next; struct CronLine *cf_lines; char *cf_username; smallint cf_wants_starting; /* bool: one or more jobs ready */ smallint cf_has_running; /* bool: one or more jobs running */ smallint cf_deleted; /* marked for deletion (but still has running jobs) */ } CronFile; typedef struct CronLine { struct CronLine *cl_next; char *cl_cmd; /* shell command */ pid_t cl_pid; /* >0:running, <0:needs to be started in this minute, 0:dormant */ #if ENABLE_FEATURE_CROND_CALL_SENDMAIL int cl_empty_mail_size; /* size of mail header only, 0 if no mailfile */ char *cl_mailto; /* whom to mail results, may be NULL */ #endif /* ordered by size, not in natural order. makes code smaller: */ char cl_Dow[7]; /* 0-6, beginning sunday */ char cl_Mons[12]; /* 0-11 */ char cl_Hrs[24]; /* 0-23 */ char cl_Days[32]; /* 1-31 */ char cl_Mins[60]; /* 0-59 */ } CronLine; #define DAEMON_UID 0 enum { OPT_l = (1 << 0), OPT_L = (1 << 1), OPT_f = (1 << 2), OPT_b = (1 << 3), OPT_S = (1 << 4), OPT_c = (1 << 5), OPT_d = (1 << 6) * ENABLE_FEATURE_CROND_D, }; #if ENABLE_FEATURE_CROND_D # define DebugOpt (option_mask32 & OPT_d) #else # define DebugOpt 0 #endif struct globals { unsigned log_level; /* = 8; */ time_t crontab_dir_mtime; const char *log_filename; const char *crontab_dir_name; /* = CRONTABS; */ CronFile *cron_files; #if SETENV_LEAKS char *env_var_user; char *env_var_home; #endif } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { \ G.log_level = 8; \ G.crontab_dir_name = CRONTABS; \ } while (0) /* 0 is the most verbose, default 8 */ #define LVL5 "\x05" #define LVL7 "\x07" #define LVL8 "\x08" #define WARN9 "\x49" #define DIE9 "\xc9" /* level >= 20 is "error" */ #define ERR20 "\x14" static void crondlog(const char *ctl, ...) __attribute__ ((format (printf, 1, 2))); static void crondlog(const char *ctl, ...) { va_list va; int level = (ctl[0] & 0x1f); va_start(va, ctl); if (level >= (int)G.log_level) { /* Debug mode: all to (non-redirected) stderr, */ /* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */ if (!DebugOpt && G.log_filename) { /* Otherwise (log to file): we reopen log file at every write: */ int logfd = open_or_warn(G.log_filename, O_WRONLY | O_CREAT | O_APPEND); if (logfd >= 0) xmove_fd(logfd, STDERR_FILENO); } /* When we log to syslog, level > 8 is logged at LOG_ERR * syslog level, level <= 8 is logged at LOG_INFO. */ if (level > 8) { bb_verror_msg(ctl + 1, va, /* strerr: */ NULL); } else { char *msg = NULL; vasprintf(&msg, ctl + 1, va); bb_info_msg("%s: %s", applet_name, msg); free(msg); } } va_end(va); if (ctl[0] & 0x80) exit(20); } static const char DowAry[] ALIGN1 = "sun""mon""tue""wed""thu""fri""sat" /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */ ; static const char MonAry[] ALIGN1 = "jan""feb""mar""apr""may""jun""jul""aug""sep""oct""nov""dec" /* "Jan""Feb""Mar""Apr""May""Jun""Jul""Aug""Sep""Oct""Nov""Dec" */ ; static void ParseField(char *user, char *ary, int modvalue, int off, const char *names, char *ptr) /* 'names' is a pointer to a set of 3-char abbreviations */ { char *base = ptr; int n1 = -1; int n2 = -1; // this can't happen due to config_read() /*if (base == NULL) return;*/ while (1) { int skip = 0; /* Handle numeric digit or symbol or '*' */ if (*ptr == '*') { n1 = 0; /* everything will be filled */ n2 = modvalue - 1; skip = 1; ++ptr; } else if (isdigit(*ptr)) { char *endp; if (n1 < 0) { n1 = strtol(ptr, &endp, 10) + off; } else { n2 = strtol(ptr, &endp, 10) + off; } ptr = endp; /* gcc likes temp var for &endp */ skip = 1; } else if (names) { int i; for (i = 0; names[i]; i += 3) { /* was using strncmp before... */ if (strncasecmp(ptr, &names[i], 3) == 0) { ptr += 3; if (n1 < 0) { n1 = i / 3; } else { n2 = i / 3; } skip = 1; break; } } } /* handle optional range '-' */ if (skip == 0) { goto err; } if (*ptr == '-' && n2 < 0) { ++ptr; continue; } /* * collapse single-value ranges, handle skipmark, and fill * in the character array appropriately. */ if (n2 < 0) { n2 = n1; } if (*ptr == '/') { char *endp; skip = strtol(ptr + 1, &endp, 10); ptr = endp; /* gcc likes temp var for &endp */ } /* * fill array, using a failsafe is the easiest way to prevent * an endless loop */ { int s0 = 1; int failsafe = 1024; --n1; do { n1 = (n1 + 1) % modvalue; if (--s0 == 0) { ary[n1 % modvalue] = 1; s0 = skip; } if (--failsafe == 0) { goto err; } } while (n1 != n2); } if (*ptr != ',') { break; } ++ptr; n1 = -1; n2 = -1; } if (*ptr) { err: crondlog(WARN9 "user %s: parse error at %s", user, base); return; } if (DebugOpt && (G.log_level <= 5)) { /* like LVL5 */ /* can't use crondlog, it inserts '\n' */ int i; for (i = 0; i < modvalue; ++i) fprintf(stderr, "%d", (unsigned char)ary[i]); bb_putchar_stderr('\n'); } } static void FixDayDow(CronLine *line) { unsigned i; int weekUsed = 0; int daysUsed = 0; for (i = 0; i < ARRAY_SIZE(line->cl_Dow); ++i) { if (line->cl_Dow[i] == 0) { weekUsed = 1; break; } } for (i = 0; i < ARRAY_SIZE(line->cl_Days); ++i) { if (line->cl_Days[i] == 0) { daysUsed = 1; break; } } if (weekUsed != daysUsed) { if (weekUsed) memset(line->cl_Days, 0, sizeof(line->cl_Days)); else /* daysUsed */ memset(line->cl_Dow, 0, sizeof(line->cl_Dow)); } } /* * delete_cronfile() - delete user database * * Note: multiple entries for same user may exist if we were unable to * completely delete a database due to running processes. */ //FIXME: we will start a new job even if the old job is running //if crontab was reloaded: crond thinks that "new" job is different from "old" //even if they are in fact completely the same. Example //Crontab was: // 0-59 * * * * job1 // 0-59 * * * * long_running_job2 //User edits crontab to: // 0-59 * * * * job1_updated // 0-59 * * * * long_running_job2 //Bug: crond can now start another long_running_job2 even if old one //is still running. //OTOH most other versions of cron do not wait for job termination anyway, //they end up with multiple copies of jobs if they don't terminate soon enough. static void delete_cronfile(const char *userName) { CronFile **pfile = &G.cron_files; CronFile *file; while ((file = *pfile) != NULL) { if (strcmp(userName, file->cf_username) == 0) { CronLine **pline = &file->cf_lines; CronLine *line; file->cf_has_running = 0; file->cf_deleted = 1; while ((line = *pline) != NULL) { if (line->cl_pid > 0) { file->cf_has_running = 1; pline = &line->cl_next; } else { *pline = line->cl_next; free(line->cl_cmd); free(line); } } if (file->cf_has_running == 0) { *pfile = file->cf_next; free(file->cf_username); free(file); continue; } } pfile = &file->cf_next; } } static void load_crontab(const char *fileName) { struct parser_t *parser; struct stat sbuf; int maxLines; char *tokens[6]; #if ENABLE_FEATURE_CROND_CALL_SENDMAIL char *mailTo = NULL; #endif delete_cronfile(fileName); if (!getpwnam(fileName)) { crondlog(LVL7 "ignoring file '%s' (no such user)", fileName); return; } parser = config_open(fileName); if (!parser) return; maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES; if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DAEMON_UID) { CronFile *file = xzalloc(sizeof(CronFile)); CronLine **pline; int n; file->cf_username = xstrdup(fileName); pline = &file->cf_lines; while (1) { CronLine *line; if (!--maxLines) break; n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY); if (!n) break; if (DebugOpt) crondlog(LVL5 "user:%s entry:%s", fileName, parser->data); /* check if line is setting MAILTO= */ if (0 == strncmp(tokens[0], "MAILTO=", 7)) { #if ENABLE_FEATURE_CROND_CALL_SENDMAIL free(mailTo); mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL; #endif /* otherwise just ignore such lines */ continue; } /* check if a minimum of tokens is specified */ if (n < 6) continue; *pline = line = xzalloc(sizeof(*line)); /* parse date ranges */ ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]); ParseField(file->cf_username, line->cl_Hrs, 24, 0, NULL, tokens[1]); ParseField(file->cf_username, line->cl_Days, 32, 0, NULL, tokens[2]); ParseField(file->cf_username, line->cl_Mons, 12, -1, MonAry, tokens[3]); ParseField(file->cf_username, line->cl_Dow, 7, 0, DowAry, tokens[4]); /* * fix days and dow - if one is not "*" and the other * is "*", the other is set to 0, and vise-versa */ FixDayDow(line); #if ENABLE_FEATURE_CROND_CALL_SENDMAIL /* copy mailto (can be NULL) */ line->cl_mailto = xstrdup(mailTo); #endif /* copy command */ line->cl_cmd = xstrdup(tokens[5]); if (DebugOpt) { crondlog(LVL5 " command:%s", tokens[5]); } pline = &line->cl_next; //bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]); } *pline = NULL; file->cf_next = G.cron_files; G.cron_files = file; if (maxLines == 0) { crondlog(WARN9 "user %s: too many lines", fileName); } } config_close(parser); } static void process_cron_update_file(void) { FILE *fi; char buf[256]; fi = fopen_for_read(CRONUPDATE); if (fi != NULL) { unlink(CRONUPDATE); while (fgets(buf, sizeof(buf), fi) != NULL) { /* use first word only */ skip_non_whitespace(buf)[0] = '\0'; load_crontab(buf); } fclose(fi); } } static void rescan_crontab_dir(void) { CronFile *file; /* Delete all files until we only have ones with running jobs (or none) */ again: for (file = G.cron_files; file; file = file->cf_next) { if (!file->cf_deleted) { delete_cronfile(file->cf_username); goto again; } } /* Remove cron update file */ unlink(CRONUPDATE); /* Re-chdir, in case directory was renamed & deleted */ if (chdir(G.crontab_dir_name) < 0) { crondlog(DIE9 "chdir(%s)", G.crontab_dir_name); } /* Scan directory and add associated users */ { DIR *dir = opendir("."); struct dirent *den; if (!dir) crondlog(DIE9 "chdir(%s)", "."); /* exits */ while ((den = readdir(dir)) != NULL) { if (strchr(den->d_name, '.') != NULL) { continue; } load_crontab(den->d_name); } closedir(dir); } } #if SETENV_LEAKS /* We set environment *before* vfork (because we want to use vfork), * so we cannot use setenv() - repeated calls to setenv() may leak memory! * Using putenv(), and freeing memory after unsetenv() won't leak */ static void safe_setenv(char **pvar_val, const char *var, const char *val) { char *var_val = *pvar_val; if (var_val) { bb_unsetenv_and_free(var_val); } *pvar_val = xasprintf("%s=%s", var, val); putenv(*pvar_val); } #endif static void set_env_vars(struct passwd *pas) { #if SETENV_LEAKS safe_setenv(&G.env_var_user, "USER", pas->pw_name); safe_setenv(&G.env_var_home, "HOME", pas->pw_dir); /* if we want to set user's shell instead: */ /*safe_setenv(G.env_var_shell, "SHELL", pas->pw_shell);*/ #else xsetenv("USER", pas->pw_name); xsetenv("HOME", pas->pw_dir); #endif /* currently, we use constant one: */ /*setenv("SHELL", DEFAULT_SHELL, 1); - done earlier */ } static void change_user(struct passwd *pas) { /* careful: we're after vfork! */ change_identity(pas); /* - initgroups, setgid, setuid */ if (chdir(pas->pw_dir) < 0) { crondlog(WARN9 "chdir(%s)", pas->pw_dir); if (chdir(TMPDIR) < 0) { crondlog(DIE9 "chdir(%s)", TMPDIR); /* exits */ } } } // TODO: sendmail should be _run-time_ option, not compile-time! #if ENABLE_FEATURE_CROND_CALL_SENDMAIL static pid_t fork_job(const char *user, int mailFd, const char *prog, const char *shell_cmd /* if NULL, we run sendmail */ ) { struct passwd *pas; pid_t pid; /* prepare things before vfork */ pas = getpwnam(user); if (!pas) { crondlog(WARN9 "can't get uid for %s", user); goto err; } set_env_vars(pas); pid = vfork(); if (pid == 0) { /* CHILD */ /* initgroups, setgid, setuid, and chdir to home or TMPDIR */ change_user(pas); if (DebugOpt) { crondlog(LVL5 "child running %s", prog); } if (mailFd >= 0) { xmove_fd(mailFd, shell_cmd ? 1 : 0); dup2(1, 2); } /* crond 3.0pl1-100 puts tasks in separate process groups */ bb_setpgrp(); execlp(prog, prog, (shell_cmd ? "-c" : SENDMAIL_ARGS), shell_cmd, (char *) NULL); crondlog(ERR20 "can't execute '%s' for user %s", prog, user); if (shell_cmd) { fdprintf(1, "Exec failed: %s -c %s\n", prog, shell_cmd); } _exit(EXIT_SUCCESS); } if (pid < 0) { /* FORK FAILED */ crondlog(ERR20 "can't vfork"); err: pid = 0; } /* else: PARENT, FORK SUCCESS */ /* * Close the mail file descriptor.. we can't just leave it open in * a structure, closing it later, because we might run out of descriptors */ if (mailFd >= 0) { close(mailFd); } return pid; } static void start_one_job(const char *user, CronLine *line) { char mailFile[128]; int mailFd = -1; line->cl_pid = 0; line->cl_empty_mail_size = 0; if (line->cl_mailto) { /* Open mail file (owner is root so nobody can screw with it) */ snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, getpid()); mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600); if (mailFd >= 0) { fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", line->cl_mailto, line->cl_cmd); line->cl_empty_mail_size = lseek(mailFd, 0, SEEK_CUR); } else { crondlog(ERR20 "can't create mail file %s for user %s, " "discarding output", mailFile, user); } } line->cl_pid = fork_job(user, mailFd, DEFAULT_SHELL, line->cl_cmd); if (mailFd >= 0) { if (line->cl_pid <= 0) { unlink(mailFile); } else { /* rename mail-file based on pid of process */ char *mailFile2 = xasprintf("%s/cron.%s.%d", TMPDIR, user, (int)line->cl_pid); rename(mailFile, mailFile2); // TODO: xrename? free(mailFile2); } } } /* * process_finished_job - called when job terminates and when mail terminates */ static void process_finished_job(const char *user, CronLine *line) { pid_t pid; int mailFd; char mailFile[128]; struct stat sbuf; pid = line->cl_pid; line->cl_pid = 0; if (pid <= 0) { /* No job */ return; } if (line->cl_empty_mail_size <= 0) { /* End of job and no mail file, or end of sendmail job */ return; } /* * End of primary job - check for mail file. * If size has changed and the file is still valid, we send it. */ snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, (int)pid); mailFd = open(mailFile, O_RDONLY); unlink(mailFile); if (mailFd < 0) { return; } if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DAEMON_UID || sbuf.st_nlink != 0 || sbuf.st_size == line->cl_empty_mail_size || !S_ISREG(sbuf.st_mode) ) { close(mailFd); return; } line->cl_empty_mail_size = 0; /* if (line->cl_mailto) - always true if cl_empty_mail_size was nonzero */ line->cl_pid = fork_job(user, mailFd, SENDMAIL, NULL); } #else /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */ static void start_one_job(const char *user, CronLine *line) { struct passwd *pas; pid_t pid; pas = getpwnam(user); if (!pas) { crondlog(WARN9 "can't get uid for %s", user); goto err; } /* Prepare things before vfork */ set_env_vars(pas); /* Fork as the user in question and run program */ pid = vfork(); if (pid == 0) { /* CHILD */ /* initgroups, setgid, setuid, and chdir to home or TMPDIR */ change_user(pas); if (DebugOpt) { crondlog(LVL5 "child running %s", DEFAULT_SHELL); } /* crond 3.0pl1-100 puts tasks in separate process groups */ bb_setpgrp(); execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_cmd, (char *) NULL); crondlog(ERR20 "can't execute '%s' for user %s", DEFAULT_SHELL, user); _exit(EXIT_SUCCESS); } if (pid < 0) { /* FORK FAILED */ crondlog(ERR20 "can't vfork"); err: pid = 0; } line->cl_pid = pid; } #define process_finished_job(user, line) ((line)->cl_pid = 0) #endif /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */ /* * Determine which jobs need to be run. Under normal conditions, the * period is about a minute (one scan). Worst case it will be one * hour (60 scans). */ static void flag_starting_jobs(time_t t1, time_t t2) { time_t t; /* Find jobs > t1 and <= t2 */ for (t = t1 - t1 % 60; t <= t2; t += 60) { struct tm *ptm; CronFile *file; CronLine *line; if (t <= t1) continue; ptm = localtime(&t); for (file = G.cron_files; file; file = file->cf_next) { if (DebugOpt) crondlog(LVL5 "file %s:", file->cf_username); if (file->cf_deleted) continue; for (line = file->cf_lines; line; line = line->cl_next) { if (DebugOpt) crondlog(LVL5 " line %s", line->cl_cmd); if (line->cl_Mins[ptm->tm_min] && line->cl_Hrs[ptm->tm_hour] && (line->cl_Days[ptm->tm_mday] || line->cl_Dow[ptm->tm_wday]) && line->cl_Mons[ptm->tm_mon] ) { if (DebugOpt) { crondlog(LVL5 " job: %d %s", (int)line->cl_pid, line->cl_cmd); } if (line->cl_pid > 0) { crondlog(LVL8 "user %s: process already running: %s", file->cf_username, line->cl_cmd); } else if (line->cl_pid == 0) { line->cl_pid = -1; file->cf_wants_starting = 1; } } } } } } static void start_jobs(void) { CronFile *file; CronLine *line; for (file = G.cron_files; file; file = file->cf_next) { if (!file->cf_wants_starting) continue; file->cf_wants_starting = 0; for (line = file->cf_lines; line; line = line->cl_next) { pid_t pid; if (line->cl_pid >= 0) continue; start_one_job(file->cf_username, line); pid = line->cl_pid; crondlog(LVL8 "USER %s pid %3d cmd %s", file->cf_username, (int)pid, line->cl_cmd); if (pid < 0) { file->cf_wants_starting = 1; } if (pid > 0) { file->cf_has_running = 1; } } } } /* * Check for job completion, return number of jobs still running after * all done. */ static int check_completions(void) { CronFile *file; CronLine *line; int num_still_running = 0; for (file = G.cron_files; file; file = file->cf_next) { if (!file->cf_has_running) continue; file->cf_has_running = 0; for (line = file->cf_lines; line; line = line->cl_next) { int r; if (line->cl_pid <= 0) continue; r = waitpid(line->cl_pid, NULL, WNOHANG); if (r < 0 || r == line->cl_pid) { process_finished_job(file->cf_username, line); if (line->cl_pid == 0) { /* sendmail was not started for it */ continue; } /* else: sendmail was started, job is still running, fall thru */ } /* else: r == 0: "process is still running" */ file->cf_has_running = 1; } //FIXME: if !file->cf_has_running && file->deleted: delete it! //otherwise deleted entries will stay forever, right? num_still_running += file->cf_has_running; } return num_still_running; } int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int crond_main(int argc UNUSED_PARAM, char **argv) { time_t t2; int rescan; int sleep_time; unsigned opts; INIT_G(); /* "-b after -f is ignored", and so on for every pair a-b */ opt_complementary = "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l") /* -l and -d have numeric param */ ":l+" IF_FEATURE_CROND_D(":d+"); opts = getopt32(argv, "l:L:fbSc:" IF_FEATURE_CROND_D("d:"), &G.log_level, &G.log_filename, &G.crontab_dir_name IF_FEATURE_CROND_D(,&G.log_level)); /* both -d N and -l N set the same variable: G.log_level */ if (!(opts & OPT_f)) { /* close stdin, stdout, stderr. * close unused descriptors - don't need them. */ bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); } if (!(opts & OPT_d) && G.log_filename == NULL) { /* logging to syslog */ openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON); logmode = LOGMODE_SYSLOG; } xchdir(G.crontab_dir_name); //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */ crondlog(LVL8 "crond (busybox "BB_VER") started, log level %d", G.log_level); rescan_crontab_dir(); write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid"); /* Main loop */ t2 = time(NULL); rescan = 60; sleep_time = 60; for (;;) { struct stat sbuf; time_t t1; long dt; t1 = t2; /* Synchronize to 1 minute, minimum 1 second */ sleep(sleep_time - (time(NULL) % sleep_time) + 1); t2 = time(NULL); dt = (long)t2 - (long)t1; /* * The file 'cron.update' is checked to determine new cron * jobs. The directory is rescanned once an hour to deal * with any screwups. * * Check for time jump. Disparities over an hour either way * result in resynchronization. A negative disparity * less than an hour causes us to effectively sleep until we * match the original time (i.e. no re-execution of jobs that * have just been run). A positive disparity less than * an hour causes intermediate jobs to be run, but only once * in the worst case. * * When running jobs, the inequality used is greater but not * equal to t1, and less then or equal to t2. */ if (stat(G.crontab_dir_name, &sbuf) != 0) sbuf.st_mtime = 0; /* force update (once) if dir was deleted */ if (G.crontab_dir_mtime != sbuf.st_mtime) { G.crontab_dir_mtime = sbuf.st_mtime; rescan = 1; } if (--rescan == 0) { rescan = 60; rescan_crontab_dir(); } process_cron_update_file(); if (DebugOpt) crondlog(LVL5 "wakeup dt=%ld", dt); if (dt < -60 * 60 || dt > 60 * 60) { crondlog(WARN9 "time disparity of %ld minutes detected", dt / 60); /* and we do not run any jobs in this case */ } else if (dt > 0) { /* Usual case: time advances forward, as expected */ flag_starting_jobs(t1, t2); start_jobs(); if (check_completions() > 0) { /* some jobs are still running */ sleep_time = 10; } else { sleep_time = 60; } } /* else: time jumped back, do not run any jobs */ } /* for (;;) */ return 0; /* not reached */ } busybox-1.22.1/miscutils/beep.c0000644000000000000000000000570512263563520015074 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * beep implementation for busybox * * Copyright (C) 2009 Bernhard Reutner-Fischer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * */ //usage:#define beep_trivial_usage //usage: "-f FREQ -l LEN -d DELAY -r COUNT -n" //usage:#define beep_full_usage "\n\n" //usage: " -f Frequency in Hz" //usage: "\n -l Length in ms" //usage: "\n -d Delay in ms" //usage: "\n -r Repetitions" //usage: "\n -n Start new tone" #include "libbb.h" #include #ifndef CLOCK_TICK_RATE # define CLOCK_TICK_RATE 1193180 #endif /* defaults */ #ifndef CONFIG_FEATURE_BEEP_FREQ # define FREQ (4000) #else # define FREQ (CONFIG_FEATURE_BEEP_FREQ) #endif #ifndef CONFIG_FEATURE_BEEP_LENGTH_MS # define LENGTH (30) #else # define LENGTH (CONFIG_FEATURE_BEEP_LENGTH_MS) #endif #define DELAY (0) #define REPETITIONS (1) int beep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int beep_main(int argc, char **argv) { int speaker = get_console_fd_or_die(); unsigned tickrate_div_freq = tickrate_div_freq; /* for compiler */ unsigned length = length; unsigned delay = delay; unsigned rep = rep; int c; c = 'n'; while (c != -1) { if (c == 'n') { tickrate_div_freq = CLOCK_TICK_RATE / FREQ; length = LENGTH; delay = DELAY; rep = REPETITIONS; } c = getopt(argc, argv, "f:l:d:r:n"); /* TODO: -s, -c: * pipe stdin to stdout, but also beep after each line (-s) or char (-c) */ switch (c) { case 'f': /* TODO: what "-f 0" should do? */ tickrate_div_freq = (unsigned)CLOCK_TICK_RATE / xatou(optarg); continue; case 'l': length = xatou(optarg); continue; case 'd': /* TODO: * -d N, -D N * specify a delay of N milliseconds between repetitions. * -d specifies that this delay should only occur between beeps, * that is, it should not occur after the last repetition. * -D indicates that the delay should occur after every repetition */ delay = xatou(optarg); continue; case 'r': rep = xatou(optarg); continue; case 'n': case -1: break; default: bb_show_usage(); } while (rep) { //bb_info_msg("rep[%d] freq=%d, length=%d, delay=%d", rep, freq, length, delay); xioctl(speaker, KIOCSOUND, (void*)(uintptr_t)tickrate_div_freq); usleep(1000 * length); ioctl(speaker, KIOCSOUND, (void*)0); if (--rep) usleep(1000 * delay); } } if (ENABLE_FEATURE_CLEAN_UP) close(speaker); return EXIT_SUCCESS; } /* * so, e.g. Beethoven's 9th symphony "Ode an die Freude" would be * something like: a=$((220*3)) b=$((247*3)) c=$((262*3)) d=$((294*3)) e=$((329*3)) f=$((349*3)) g=$((392*3)) #./beep -f$d -l200 -r2 -n -f$e -l100 -d 10 -n -f$c -l400 -f$g -l200 ./beep -f$e -l200 -r2 \ -n -d 100 -f$f -l200 \ -n -f$g -l200 -r2 \ -n -f$f -l200 \ -n -f$e -l200 \ -n -f$d -l200 \ -n -f$c -l200 -r2 \ -n -f$d -l200 \ -n -f$e -l200 \ -n -f$e -l400 \ -n -f$d -l100 \ -n -f$d -l200 \ */ busybox-1.22.1/miscutils/hdparm.c0000644000000000000000000021727612263563520015444 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * hdparm implementation for busybox * * Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it> * Hacked by Tito for size optimization. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * This program is based on the source code of hdparm: see below... * hdparm.c - Command line interface to get/set hard disk parameters * - by Mark Lord (C) 1994-2002 -- freely distributable */ //usage:#define hdparm_trivial_usage //usage: "[OPTIONS] [DEVICE]" //usage:#define hdparm_full_usage "\n\n" //usage: " -a Get/set fs readahead" //usage: "\n -A Set drive read-lookahead flag (0/1)" //usage: "\n -b Get/set bus state (0 == off, 1 == on, 2 == tristate)" //usage: "\n -B Set Advanced Power Management setting (1-255)" //usage: "\n -c Get/set IDE 32-bit IO setting" //usage: "\n -C Check IDE power mode status" //usage: IF_FEATURE_HDPARM_HDIO_GETSET_DMA( //usage: "\n -d Get/set using_dma flag") //usage: "\n -D Enable/disable drive defect-mgmt" //usage: "\n -f Flush buffer cache for device on exit" //usage: "\n -g Display drive geometry" //usage: "\n -h Display terse usage information" //usage: IF_FEATURE_HDPARM_GET_IDENTITY( //usage: "\n -i Display drive identification") //usage: IF_FEATURE_HDPARM_GET_IDENTITY( //usage: "\n -I Detailed/current information directly from drive") //usage: "\n -k Get/set keep_settings_over_reset flag (0/1)" //usage: "\n -K Set drive keep_features_over_reset flag (0/1)" //usage: "\n -L Set drive doorlock (0/1) (removable harddisks only)" //usage: "\n -m Get/set multiple sector count" //usage: "\n -n Get/set ignore-write-errors flag (0/1)" //usage: "\n -p Set PIO mode on IDE interface chipset (0,1,2,3,4,...)" //usage: "\n -P Set drive prefetch count" /* //usage: "\n -q Change next setting quietly" - not supported ib bbox */ //usage: "\n -Q Get/set DMA tagged-queuing depth (if supported)" //usage: "\n -r Get/set readonly flag (DANGEROUS to set)" //usage: IF_FEATURE_HDPARM_HDIO_SCAN_HWIF( //usage: "\n -R Register an IDE interface (DANGEROUS)") //usage: "\n -S Set standby (spindown) timeout" //usage: "\n -t Perform device read timings" //usage: "\n -T Perform cache read timings" //usage: "\n -u Get/set unmaskirq flag (0/1)" //usage: IF_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF( //usage: "\n -U Unregister an IDE interface (DANGEROUS)") //usage: "\n -v Defaults; same as -mcudkrag for IDE drives" //usage: "\n -V Display program version and exit immediately" //usage: IF_FEATURE_HDPARM_HDIO_DRIVE_RESET( //usage: "\n -w Perform device reset (DANGEROUS)") //usage: "\n -W Set drive write-caching flag (0/1) (DANGEROUS)" //usage: IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF( //usage: "\n -x Tristate device for hotswap (0/1) (DANGEROUS)") //usage: "\n -X Set IDE xfer mode (DANGEROUS)" //usage: "\n -y Put IDE drive in standby mode" //usage: "\n -Y Put IDE drive to sleep" //usage: "\n -Z Disable Seagate auto-powersaving mode" //usage: "\n -z Reread partition table" #include "libbb.h" /* must be _after_ libbb.h: */ #include #include #if !defined(BLKGETSIZE64) # define BLKGETSIZE64 _IOR(0x12,114,size_t) #endif /* device types */ /* ------------ */ #define NO_DEV 0xffff #define ATA_DEV 0x0000 #define ATAPI_DEV 0x0001 /* word definitions */ /* ---------------- */ #define GEN_CONFIG 0 /* general configuration */ #define LCYLS 1 /* number of logical cylinders */ #define CONFIG 2 /* specific configuration */ #define LHEADS 3 /* number of logical heads */ #define TRACK_BYTES 4 /* number of bytes/track (ATA-1) */ #define SECT_BYTES 5 /* number of bytes/sector (ATA-1) */ #define LSECTS 6 /* number of logical sectors/track */ #define START_SERIAL 10 /* ASCII serial number */ #define LENGTH_SERIAL 10 /* 10 words (20 bytes or characters) */ #define BUF_TYPE 20 /* buffer type (ATA-1) */ #define BUFFER__SIZE 21 /* buffer size (ATA-1) */ #define RW_LONG 22 /* extra bytes in R/W LONG cmd ( < ATA-4)*/ #define START_FW_REV 23 /* ASCII firmware revision */ #define LENGTH_FW_REV 4 /* 4 words (8 bytes or characters) */ #define START_MODEL 27 /* ASCII model number */ #define LENGTH_MODEL 20 /* 20 words (40 bytes or characters) */ #define SECTOR_XFER_MAX 47 /* r/w multiple: max sectors xfered */ #define DWORD_IO 48 /* can do double-word IO (ATA-1 only) */ #define CAPAB_0 49 /* capabilities */ #define CAPAB_1 50 #define PIO_MODE 51 /* max PIO mode supported (obsolete)*/ #define DMA_MODE 52 /* max Singleword DMA mode supported (obs)*/ #define WHATS_VALID 53 /* what fields are valid */ #define LCYLS_CUR 54 /* current logical cylinders */ #define LHEADS_CUR 55 /* current logical heads */ #define LSECTS_CUR 56 /* current logical sectors/track */ #define CAPACITY_LSB 57 /* current capacity in sectors */ #define CAPACITY_MSB 58 #define SECTOR_XFER_CUR 59 /* r/w multiple: current sectors xfered */ #define LBA_SECTS_LSB 60 /* LBA: total number of user */ #define LBA_SECTS_MSB 61 /* addressable sectors */ #define SINGLE_DMA 62 /* singleword DMA modes */ #define MULTI_DMA 63 /* multiword DMA modes */ #define ADV_PIO_MODES 64 /* advanced PIO modes supported */ /* multiword DMA xfer cycle time: */ #define DMA_TIME_MIN 65 /* - minimum */ #define DMA_TIME_NORM 66 /* - manufacturer's recommended */ /* minimum PIO xfer cycle time: */ #define PIO_NO_FLOW 67 /* - without flow control */ #define PIO_FLOW 68 /* - with IORDY flow control */ #define PKT_REL 71 /* typical #ns from PKT cmd to bus rel */ #define SVC_NBSY 72 /* typical #ns from SERVICE cmd to !BSY */ #define CDR_MAJOR 73 /* CD ROM: major version number */ #define CDR_MINOR 74 /* CD ROM: minor version number */ #define QUEUE_DEPTH 75 /* queue depth */ #define MAJOR 80 /* major version number */ #define MINOR 81 /* minor version number */ #define CMDS_SUPP_0 82 /* command/feature set(s) supported */ #define CMDS_SUPP_1 83 #define CMDS_SUPP_2 84 #define CMDS_EN_0 85 /* command/feature set(s) enabled */ #define CMDS_EN_1 86 #define CMDS_EN_2 87 #define ULTRA_DMA 88 /* ultra DMA modes */ /* time to complete security erase */ #define ERASE_TIME 89 /* - ordinary */ #define ENH_ERASE_TIME 90 /* - enhanced */ #define ADV_PWR 91 /* current advanced power management level in low byte, 0x40 in high byte. */ #define PSWD_CODE 92 /* master password revision code */ #define HWRST_RSLT 93 /* hardware reset result */ #define ACOUSTIC 94 /* acoustic mgmt values ( >= ATA-6) */ #define LBA_LSB 100 /* LBA: maximum. Currently only 48 */ #define LBA_MID 101 /* bits are used, but addr 103 */ #define LBA_48_MSB 102 /* has been reserved for LBA in */ #define LBA_64_MSB 103 /* the future. */ #define RM_STAT 127 /* removable media status notification feature set support */ #define SECU_STATUS 128 /* security status */ #define CFA_PWR_MODE 160 /* CFA power mode 1 */ #define START_MEDIA 176 /* media serial number */ #define LENGTH_MEDIA 20 /* 20 words (40 bytes or characters)*/ #define START_MANUF 196 /* media manufacturer I.D. */ #define LENGTH_MANUF 10 /* 10 words (20 bytes or characters) */ #define INTEGRITY 255 /* integrity word */ /* bit definitions within the words */ /* -------------------------------- */ /* many words are considered valid if bit 15 is 0 and bit 14 is 1 */ #define VALID 0xc000 #define VALID_VAL 0x4000 /* many words are considered invalid if they are either all-0 or all-1 */ #define NOVAL_0 0x0000 #define NOVAL_1 0xffff /* word 0: gen_config */ #define NOT_ATA 0x8000 #define NOT_ATAPI 0x4000 /* (check only if bit 15 == 1) */ #define MEDIA_REMOVABLE 0x0080 #define DRIVE_NOT_REMOVABLE 0x0040 /* bit obsoleted in ATA 6 */ #define INCOMPLETE 0x0004 #define CFA_SUPPORT_VAL 0x848a /* 848a=CFA feature set support */ #define DRQ_RESPONSE_TIME 0x0060 #define DRQ_3MS_VAL 0x0000 #define DRQ_INTR_VAL 0x0020 #define DRQ_50US_VAL 0x0040 #define PKT_SIZE_SUPPORTED 0x0003 #define PKT_SIZE_12_VAL 0x0000 #define PKT_SIZE_16_VAL 0x0001 #define EQPT_TYPE 0x1f00 #define SHIFT_EQPT 8 #define CDROM 0x0005 /* word 1: number of logical cylinders */ #define LCYLS_MAX 0x3fff /* maximum allowable value */ /* word 2: specific configuration * (a) require SET FEATURES to spin-up * (b) require spin-up to fully reply to IDENTIFY DEVICE */ #define STBY_NID_VAL 0x37c8 /* (a) and (b) */ #define STBY_ID_VAL 0x738c /* (a) and not (b) */ #define PWRD_NID_VAL 0x8c73 /* not (a) and (b) */ #define PWRD_ID_VAL 0xc837 /* not (a) and not (b) */ /* words 47 & 59: sector_xfer_max & sector_xfer_cur */ #define SECTOR_XFER 0x00ff /* sectors xfered on r/w multiple cmds*/ #define MULTIPLE_SETTING_VALID 0x0100 /* 1=multiple sector setting is valid */ /* word 49: capabilities 0 */ #define STD_STBY 0x2000 /* 1=standard values supported (ATA); 0=vendor specific values */ #define IORDY_SUP 0x0800 /* 1=support; 0=may be supported */ #define IORDY_OFF 0x0400 /* 1=may be disabled */ #define LBA_SUP 0x0200 /* 1=Logical Block Address support */ #define DMA_SUP 0x0100 /* 1=Direct Memory Access support */ #define DMA_IL_SUP 0x8000 /* 1=interleaved DMA support (ATAPI) */ #define CMD_Q_SUP 0x4000 /* 1=command queuing support (ATAPI) */ #define OVLP_SUP 0x2000 /* 1=overlap operation support (ATAPI) */ #define SWRST_REQ 0x1000 /* 1=ATA SW reset required (ATAPI, obsolete */ /* word 50: capabilities 1 */ #define MIN_STANDBY_TIMER 0x0001 /* 1=device specific standby timer value minimum */ /* words 51 & 52: PIO & DMA cycle times */ #define MODE 0xff00 /* the mode is in the MSBs */ /* word 53: whats_valid */ #define OK_W88 0x0004 /* the ultra_dma info is valid */ #define OK_W64_70 0x0002 /* see above for word descriptions */ #define OK_W54_58 0x0001 /* current cyl, head, sector, cap. info valid */ /*word 63,88: dma_mode, ultra_dma_mode*/ #define MODE_MAX 7 /* bit definitions force udma <=7 (when * udma >=8 comes out it'll have to be * defined in a new dma_mode word!) */ /* word 64: PIO transfer modes */ #define PIO_SUP 0x00ff /* only bits 0 & 1 are used so far, */ #define PIO_MODE_MAX 8 /* but all 8 bits are defined */ /* word 75: queue_depth */ #define DEPTH_BITS 0x001f /* bits used for queue depth */ /* words 80-81: version numbers */ /* NOVAL_0 or NOVAL_1 means device does not report version */ /* word 81: minor version number */ #define MINOR_MAX 0x22 /* words 82-84: cmds/feats supported */ #define CMDS_W82 0x77ff /* word 82: defined command locations*/ #define CMDS_W83 0x3fff /* word 83: defined command locations*/ #define CMDS_W84 0x002f /* word 83: defined command locations*/ #define SUPPORT_48_BIT 0x0400 #define NUM_CMD_FEAT_STR 48 /* words 85-87: cmds/feats enabled */ /* use cmd_feat_str[] to display what commands and features have * been enabled with words 85-87 */ /* words 89, 90, SECU ERASE TIME */ #define ERASE_BITS 0x00ff /* word 92: master password revision */ /* NOVAL_0 or NOVAL_1 means no support for master password revision */ /* word 93: hw reset result */ #define CBLID 0x2000 /* CBLID status */ #define RST0 0x0001 /* 1=reset to device #0 */ #define DEV_DET 0x0006 /* how device num determined */ #define JUMPER_VAL 0x0002 /* device num determined by jumper */ #define CSEL_VAL 0x0004 /* device num determined by CSEL_VAL */ /* word 127: removable media status notification feature set support */ #define RM_STAT_BITS 0x0003 #define RM_STAT_SUP 0x0001 /* word 128: security */ #define SECU_ENABLED 0x0002 #define SECU_LEVEL 0x0010 #define NUM_SECU_STR 6 /* word 160: CFA power mode */ #define VALID_W160 0x8000 /* 1=word valid */ #define PWR_MODE_REQ 0x2000 /* 1=CFA power mode req'd by some cmds*/ #define PWR_MODE_OFF 0x1000 /* 1=CFA power moded disabled */ #define MAX_AMPS 0x0fff /* value = max current in ma */ /* word 255: integrity */ #define SIG 0x00ff /* signature location */ #define SIG_VAL 0x00a5 /* signature value */ #define TIMING_BUF_MB 1 #define TIMING_BUF_BYTES (TIMING_BUF_MB * 1024 * 1024) #undef DO_FLUSHCACHE /* under construction: force cache flush on -W0 */ #define IS_GET 1 #define IS_SET 2 enum { fd = 3 }; struct globals { smallint get_identity, get_geom; smallint do_flush; smallint do_ctimings, do_timings; smallint reread_partn; smallint set_piomode, noisy_piomode; smallint getset_readahead; smallint getset_readonly; smallint getset_unmask; smallint getset_mult; #ifdef HDIO_GET_QDMA smallint getset_dma_q; #endif smallint getset_nowerr; smallint getset_keep; smallint getset_io32bit; int piomode; unsigned long Xreadahead; unsigned long readonly; unsigned long unmask; unsigned long mult; #ifdef HDIO_SET_QDMA unsigned long dma_q; #endif unsigned long nowerr; unsigned long keep; unsigned long io32bit; #if ENABLE_FEATURE_HDPARM_HDIO_GETSET_DMA unsigned long dma; smallint getset_dma; #endif #ifdef HDIO_DRIVE_CMD smallint set_xfermode, get_xfermode; smallint getset_dkeep; smallint getset_standby; smallint getset_lookahead; smallint getset_prefetch; smallint getset_defects; smallint getset_wcache; smallint getset_doorlock; smallint set_seagate; smallint set_standbynow; smallint set_sleepnow; smallint get_powermode; smallint getset_apmmode; int xfermode_requested; unsigned long dkeep; unsigned long standby_requested; /* 0..255 */ unsigned long lookahead; unsigned long prefetch; unsigned long defects; unsigned long wcache; unsigned long doorlock; unsigned long apmmode; #endif IF_FEATURE_HDPARM_GET_IDENTITY( smallint get_IDentity;) IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF( smallint getset_busstate;) IF_FEATURE_HDPARM_HDIO_DRIVE_RESET( smallint perform_reset;) IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF( smallint perform_tristate;) IF_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF(smallint unregister_hwif;) IF_FEATURE_HDPARM_HDIO_SCAN_HWIF( smallint scan_hwif;) IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF( unsigned long busstate;) IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF( unsigned long tristate;) IF_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF(unsigned long hwif;) #if ENABLE_FEATURE_HDPARM_HDIO_SCAN_HWIF unsigned long hwif_data; unsigned long hwif_ctrl; unsigned long hwif_irq; #endif #ifdef DO_FLUSHCACHE unsigned char flushcache[4] = { WIN_FLUSHCACHE, 0, 0, 0 }; #endif } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) struct BUG_G_too_big { char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1]; }; #define get_identity (G.get_identity ) #define get_geom (G.get_geom ) #define do_flush (G.do_flush ) #define do_ctimings (G.do_ctimings ) #define do_timings (G.do_timings ) #define reread_partn (G.reread_partn ) #define set_piomode (G.set_piomode ) #define noisy_piomode (G.noisy_piomode ) #define getset_readahead (G.getset_readahead ) #define getset_readonly (G.getset_readonly ) #define getset_unmask (G.getset_unmask ) #define getset_mult (G.getset_mult ) #define getset_dma_q (G.getset_dma_q ) #define getset_nowerr (G.getset_nowerr ) #define getset_keep (G.getset_keep ) #define getset_io32bit (G.getset_io32bit ) #define piomode (G.piomode ) #define Xreadahead (G.Xreadahead ) #define readonly (G.readonly ) #define unmask (G.unmask ) #define mult (G.mult ) #define dma_q (G.dma_q ) #define nowerr (G.nowerr ) #define keep (G.keep ) #define io32bit (G.io32bit ) #define dma (G.dma ) #define getset_dma (G.getset_dma ) #define set_xfermode (G.set_xfermode ) #define get_xfermode (G.get_xfermode ) #define getset_dkeep (G.getset_dkeep ) #define getset_standby (G.getset_standby ) #define getset_lookahead (G.getset_lookahead ) #define getset_prefetch (G.getset_prefetch ) #define getset_defects (G.getset_defects ) #define getset_wcache (G.getset_wcache ) #define getset_doorlock (G.getset_doorlock ) #define set_seagate (G.set_seagate ) #define set_standbynow (G.set_standbynow ) #define set_sleepnow (G.set_sleepnow ) #define get_powermode (G.get_powermode ) #define getset_apmmode (G.getset_apmmode ) #define xfermode_requested (G.xfermode_requested ) #define dkeep (G.dkeep ) #define standby_requested (G.standby_requested ) #define lookahead (G.lookahead ) #define prefetch (G.prefetch ) #define defects (G.defects ) #define wcache (G.wcache ) #define doorlock (G.doorlock ) #define apmmode (G.apmmode ) #define get_IDentity (G.get_IDentity ) #define getset_busstate (G.getset_busstate ) #define perform_reset (G.perform_reset ) #define perform_tristate (G.perform_tristate ) #define unregister_hwif (G.unregister_hwif ) #define scan_hwif (G.scan_hwif ) #define busstate (G.busstate ) #define tristate (G.tristate ) #define hwif (G.hwif ) #define hwif_data (G.hwif_data ) #define hwif_ctrl (G.hwif_ctrl ) #define hwif_irq (G.hwif_irq ) #define INIT_G() do { } while (0) /* Busybox messages and functions */ #if ENABLE_IOCTL_HEX2STR_ERROR static int ioctl_alt_func(/*int fd,*/ int cmd, unsigned char *args, int alt, const char *string) { if (!ioctl(fd, cmd, args)) return 0; args[0] = alt; return bb_ioctl_or_warn(fd, cmd, args, string); } #define ioctl_alt_or_warn(cmd,args,alt) ioctl_alt_func(cmd,args,alt,#cmd) #else static int ioctl_alt_func(/*int fd,*/ int cmd, unsigned char *args, int alt) { if (!ioctl(fd, cmd, args)) return 0; args[0] = alt; return bb_ioctl_or_warn(fd, cmd, args); } #define ioctl_alt_or_warn(cmd,args,alt) ioctl_alt_func(cmd,args,alt) #endif static void on_off(int value) { puts(value ? " (on)" : " (off)"); } static void print_flag_on_off(int get_arg, const char *s, unsigned long arg) { if (get_arg) { printf(" setting %s to %lu", s, arg); on_off(arg); } } static void print_value_on_off(const char *str, unsigned long argp) { printf(" %s\t= %2lu", str, argp); on_off(argp != 0); } #if ENABLE_FEATURE_HDPARM_GET_IDENTITY static void print_ascii(const char *p, int length) { #if BB_BIG_ENDIAN #define LE_ONLY(x) enum { ofs = 0 }; #else #define LE_ONLY(x) x /* every 16bit word is big-endian (i.e. inverted) */ /* accessing bytes in 1,0, 3,2, 5,4... sequence */ int ofs = 1; #endif length *= 2; /* find first non-space & print it */ while (length && p[ofs] != ' ') { p++; LE_ONLY(ofs = -ofs;) length--; } while (length && p[ofs]) { bb_putchar(p[ofs]); p++; LE_ONLY(ofs = -ofs;) length--; } bb_putchar('\n'); #undef LE_ONLY } static void xprint_ascii(uint16_t *val, int i, const char *string, int n) { if (val[i]) { printf("\t%-20s", string); print_ascii((void*)&val[i], n); } } static uint8_t mode_loop(uint16_t mode_sup, uint16_t mode_sel, int cc, uint8_t *have_mode) { uint16_t ii; uint8_t err_dma = 0; for (ii = 0; ii <= MODE_MAX; ii++) { if (mode_sel & 0x0001) { printf("*%cdma%u ", cc, ii); if (*have_mode) err_dma = 1; *have_mode = 1; } else if (mode_sup & 0x0001) printf("%cdma%u ", cc, ii); mode_sup >>= 1; mode_sel >>= 1; } return err_dma; } static const char pkt_str[] ALIGN1 = "Direct-access device" "\0" /* word 0, bits 12-8 = 00 */ "Sequential-access device" "\0" /* word 0, bits 12-8 = 01 */ "Printer" "\0" /* word 0, bits 12-8 = 02 */ "Processor" "\0" /* word 0, bits 12-8 = 03 */ "Write-once device" "\0" /* word 0, bits 12-8 = 04 */ "CD-ROM" "\0" /* word 0, bits 12-8 = 05 */ "Scanner" "\0" /* word 0, bits 12-8 = 06 */ "Optical memory" "\0" /* word 0, bits 12-8 = 07 */ "Medium changer" "\0" /* word 0, bits 12-8 = 08 */ "Communications device" "\0" /* word 0, bits 12-8 = 09 */ "ACS-IT8 device" "\0" /* word 0, bits 12-8 = 0a */ "ACS-IT8 device" "\0" /* word 0, bits 12-8 = 0b */ "Array controller" "\0" /* word 0, bits 12-8 = 0c */ "Enclosure services" "\0" /* word 0, bits 12-8 = 0d */ "Reduced block command device" "\0" /* word 0, bits 12-8 = 0e */ "Optical card reader/writer" "\0" /* word 0, bits 12-8 = 0f */ ; static const char ata1_cfg_str[] ALIGN1 = /* word 0 in ATA-1 mode */ "reserved" "\0" /* bit 0 */ "hard sectored" "\0" /* bit 1 */ "soft sectored" "\0" /* bit 2 */ "not MFM encoded " "\0" /* bit 3 */ "head switch time > 15us" "\0" /* bit 4 */ "spindle motor control option" "\0" /* bit 5 */ "fixed drive" "\0" /* bit 6 */ "removable drive" "\0" /* bit 7 */ "disk xfer rate <= 5Mbs" "\0" /* bit 8 */ "disk xfer rate > 5Mbs, <= 10Mbs" "\0" /* bit 9 */ "disk xfer rate > 5Mbs" "\0" /* bit 10 */ "rotational speed tol." "\0" /* bit 11 */ "data strobe offset option" "\0" /* bit 12 */ "track offset option" "\0" /* bit 13 */ "format speed tolerance gap reqd" "\0" /* bit 14 */ "ATAPI" /* bit 14 */ ; static const char minor_str[] ALIGN1 = /* word 81 value: */ "Unspecified" "\0" /* 0x0000 */ "ATA-1 X3T9.2 781D prior to rev.4" "\0" /* 0x0001 */ "ATA-1 published, ANSI X3.221-1994" "\0" /* 0x0002 */ "ATA-1 X3T9.2 781D rev.4" "\0" /* 0x0003 */ "ATA-2 published, ANSI X3.279-1996" "\0" /* 0x0004 */ "ATA-2 X3T10 948D prior to rev.2k" "\0" /* 0x0005 */ "ATA-3 X3T10 2008D rev.1" "\0" /* 0x0006 */ "ATA-2 X3T10 948D rev.2k" "\0" /* 0x0007 */ "ATA-3 X3T10 2008D rev.0" "\0" /* 0x0008 */ "ATA-2 X3T10 948D rev.3" "\0" /* 0x0009 */ "ATA-3 published, ANSI X3.298-199x" "\0" /* 0x000a */ "ATA-3 X3T10 2008D rev.6" "\0" /* 0x000b */ "ATA-3 X3T13 2008D rev.7 and 7a" "\0" /* 0x000c */ "ATA/ATAPI-4 X3T13 1153D rev.6" "\0" /* 0x000d */ "ATA/ATAPI-4 T13 1153D rev.13" "\0" /* 0x000e */ "ATA/ATAPI-4 X3T13 1153D rev.7" "\0" /* 0x000f */ "ATA/ATAPI-4 T13 1153D rev.18" "\0" /* 0x0010 */ "ATA/ATAPI-4 T13 1153D rev.15" "\0" /* 0x0011 */ "ATA/ATAPI-4 published, ANSI INCITS 317-1998" "\0" /* 0x0012 */ "ATA/ATAPI-5 T13 1321D rev.3" "\0" /* 0x0013 */ "ATA/ATAPI-4 T13 1153D rev.14" "\0" /* 0x0014 */ "ATA/ATAPI-5 T13 1321D rev.1" "\0" /* 0x0015 */ "ATA/ATAPI-5 published, ANSI INCITS 340-2000" "\0" /* 0x0016 */ "ATA/ATAPI-4 T13 1153D rev.17" "\0" /* 0x0017 */ "ATA/ATAPI-6 T13 1410D rev.0" "\0" /* 0x0018 */ "ATA/ATAPI-6 T13 1410D rev.3a" "\0" /* 0x0019 */ "ATA/ATAPI-7 T13 1532D rev.1" "\0" /* 0x001a */ "ATA/ATAPI-6 T13 1410D rev.2" "\0" /* 0x001b */ "ATA/ATAPI-6 T13 1410D rev.1" "\0" /* 0x001c */ "ATA/ATAPI-7 published, ANSI INCITS 397-2005" "\0" /* 0x001d */ "ATA/ATAPI-7 T13 1532D rev.0" "\0" /* 0x001e */ "reserved" "\0" /* 0x001f */ "reserved" "\0" /* 0x0020 */ "ATA/ATAPI-7 T13 1532D rev.4a" "\0" /* 0x0021 */ "ATA/ATAPI-6 published, ANSI INCITS 361-2002" "\0" /* 0x0022 */ "reserved" /* 0x0023-0xfffe */ ; static const char actual_ver[MINOR_MAX + 2] ALIGN1 = { /* word 81 value: */ 0, /* 0x0000 WARNING: actual_ver[] array */ 1, /* 0x0001 WARNING: corresponds */ 1, /* 0x0002 WARNING: *exactly* */ 1, /* 0x0003 WARNING: to the ATA/ */ 2, /* 0x0004 WARNING: ATAPI version */ 2, /* 0x0005 WARNING: listed in */ 3, /* 0x0006 WARNING: the */ 2, /* 0x0007 WARNING: minor_str */ 3, /* 0x0008 WARNING: array */ 2, /* 0x0009 WARNING: above. */ 3, /* 0x000a WARNING: */ 3, /* 0x000b WARNING: If you change */ 3, /* 0x000c WARNING: that one, */ 4, /* 0x000d WARNING: change this one */ 4, /* 0x000e WARNING: too!!! */ 4, /* 0x000f */ 4, /* 0x0010 */ 4, /* 0x0011 */ 4, /* 0x0012 */ 5, /* 0x0013 */ 4, /* 0x0014 */ 5, /* 0x0015 */ 5, /* 0x0016 */ 4, /* 0x0017 */ 6, /* 0x0018 */ 6, /* 0x0019 */ 7, /* 0x001a */ 6, /* 0x001b */ 6, /* 0x001c */ 7, /* 0x001d */ 7, /* 0x001e */ 0, /* 0x001f */ 0, /* 0x0020 */ 7, /* 0x0021 */ 6, /* 0x0022 */ 0 /* 0x0023-0xfffe */ }; static const char cmd_feat_str[] ALIGN1 = "" "\0" /* word 82 bit 15: obsolete */ "NOP cmd" "\0" /* word 82 bit 14 */ "READ BUFFER cmd" "\0" /* word 82 bit 13 */ "WRITE BUFFER cmd" "\0" /* word 82 bit 12 */ "" "\0" /* word 82 bit 11: obsolete */ "Host Protected Area feature set" "\0" /* word 82 bit 10 */ "DEVICE RESET cmd" "\0" /* word 82 bit 9 */ "SERVICE interrupt" "\0" /* word 82 bit 8 */ "Release interrupt" "\0" /* word 82 bit 7 */ "Look-ahead" "\0" /* word 82 bit 6 */ "Write cache" "\0" /* word 82 bit 5 */ "PACKET command feature set" "\0" /* word 82 bit 4 */ "Power Management feature set" "\0" /* word 82 bit 3 */ "Removable Media feature set" "\0" /* word 82 bit 2 */ "Security Mode feature set" "\0" /* word 82 bit 1 */ "SMART feature set" "\0" /* word 82 bit 0 */ /* -------------- */ "" "\0" /* word 83 bit 15: !valid bit */ "" "\0" /* word 83 bit 14: valid bit */ "FLUSH CACHE EXT cmd" "\0" /* word 83 bit 13 */ "Mandatory FLUSH CACHE cmd " "\0" /* word 83 bit 12 */ "Device Configuration Overlay feature set " "\0" "48-bit Address feature set " "\0" /* word 83 bit 10 */ "" "\0" "SET MAX security extension" "\0" /* word 83 bit 8 */ "Address Offset Reserved Area Boot" "\0" /* word 83 bit 7 */ "SET FEATURES subcommand required to spinup after power up" "\0" "Power-Up In Standby feature set" "\0" /* word 83 bit 5 */ "Removable Media Status Notification feature set" "\0" "Adv. Power Management feature set" "\0" /* word 83 bit 3 */ "CFA feature set" "\0" /* word 83 bit 2 */ "READ/WRITE DMA QUEUED" "\0" /* word 83 bit 1 */ "DOWNLOAD MICROCODE cmd" "\0" /* word 83 bit 0 */ /* -------------- */ "" "\0" /* word 84 bit 15: !valid bit */ "" "\0" /* word 84 bit 14: valid bit */ "" "\0" /* word 84 bit 13: reserved */ "" "\0" /* word 84 bit 12: reserved */ "" "\0" /* word 84 bit 11: reserved */ "" "\0" /* word 84 bit 10: reserved */ "" "\0" /* word 84 bit 9: reserved */ "" "\0" /* word 84 bit 8: reserved */ "" "\0" /* word 84 bit 7: reserved */ "" "\0" /* word 84 bit 6: reserved */ "General Purpose Logging feature set" "\0" /* word 84 bit 5 */ "" "\0" /* word 84 bit 4: reserved */ "Media Card Pass Through Command feature set " "\0" "Media serial number " "\0" /* word 84 bit 2 */ "SMART self-test " "\0" /* word 84 bit 1 */ "SMART error logging " /* word 84 bit 0 */ ; static const char secu_str[] ALIGN1 = "supported" "\0" /* word 128, bit 0 */ "enabled" "\0" /* word 128, bit 1 */ "locked" "\0" /* word 128, bit 2 */ "frozen" "\0" /* word 128, bit 3 */ "expired: security count" "\0" /* word 128, bit 4 */ "supported: enhanced erase" /* word 128, bit 5 */ ; // Parse 512 byte disk identification block and print much crap. static void identify(uint16_t *val) NORETURN; static void identify(uint16_t *val) { uint16_t ii, jj, kk; uint16_t like_std = 1, std = 0, min_std = 0xffff; uint16_t dev = NO_DEV, eqpt = NO_DEV; uint8_t have_mode = 0, err_dma = 0; uint8_t chksum = 0; uint32_t ll, mm, nn, oo; uint64_t bbbig; /* (:) */ const char *strng; #if BB_BIG_ENDIAN uint16_t buf[256]; // Adjust for endianness swab(val, buf, sizeof(buf)); val = buf; #endif /* check if we recognize the device type */ bb_putchar('\n'); if (!(val[GEN_CONFIG] & NOT_ATA)) { dev = ATA_DEV; printf("ATA device, with "); } else if (val[GEN_CONFIG]==CFA_SUPPORT_VAL) { dev = ATA_DEV; like_std = 4; printf("CompactFlash ATA device, with "); } else if (!(val[GEN_CONFIG] & NOT_ATAPI)) { dev = ATAPI_DEV; eqpt = (val[GEN_CONFIG] & EQPT_TYPE) >> SHIFT_EQPT; printf("ATAPI %s, with ", eqpt <= 0xf ? nth_string(pkt_str, eqpt) : "unknown"); like_std = 3; } else /* "Unknown device type:\n\tbits 15&14 of general configuration word 0 both set to 1.\n" */ bb_error_msg_and_die("unknown device type"); printf("%sremovable media\n", !(val[GEN_CONFIG] & MEDIA_REMOVABLE) ? "non-" : ""); /* Info from the specific configuration word says whether or not the * ID command completed correctly. It is only defined, however in * ATA/ATAPI-5 & 6; it is reserved (value theoretically 0) in prior * standards. Since the values allowed for this word are extremely * specific, it should be safe to check it now, even though we don't * know yet what standard this device is using. */ if ((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL) || (val[CONFIG]==PWRD_NID_VAL) || (val[CONFIG]==PWRD_ID_VAL) ) { like_std = 5; if ((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL)) printf("powers-up in standby; SET FEATURES subcmd spins-up.\n"); if (((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==PWRD_NID_VAL)) && (val[GEN_CONFIG] & INCOMPLETE)) printf("\n\tWARNING: ID response incomplete.\n\tFollowing data may be incorrect.\n\n"); } /* output the model and serial numbers and the fw revision */ xprint_ascii(val, START_MODEL, "Model Number:", LENGTH_MODEL); xprint_ascii(val, START_SERIAL, "Serial Number:", LENGTH_SERIAL); xprint_ascii(val, START_FW_REV, "Firmware Revision:", LENGTH_FW_REV); xprint_ascii(val, START_MEDIA, "Media Serial Num:", LENGTH_MEDIA); xprint_ascii(val, START_MANUF, "Media Manufacturer:", LENGTH_MANUF); /* major & minor standards version number (Note: these words were not * defined until ATA-3 & the CDROM std uses different words.) */ printf("Standards:"); if (eqpt != CDROM) { if (val[MINOR] && (val[MINOR] <= MINOR_MAX)) { if (like_std < 3) like_std = 3; std = actual_ver[val[MINOR]]; if (std) printf("\n\tUsed: %s ", nth_string(minor_str, val[MINOR])); } /* looks like when they up-issue the std, they obsolete one; * thus, only the newest 4 issues need be supported. (That's * what "kk" and "min_std" are all about.) */ if (val[MAJOR] && (val[MAJOR] != NOVAL_1)) { printf("\n\tSupported: "); jj = val[MAJOR] << 1; kk = like_std >4 ? like_std-4: 0; for (ii = 14; (ii >0)&&(ii>kk); ii--) { if (jj & 0x8000) { printf("%u ", ii); if (like_std < ii) { like_std = ii; kk = like_std >4 ? like_std-4: 0; } if (min_std > ii) min_std = ii; } jj <<= 1; } if (like_std < 3) like_std = 3; } /* Figure out what standard the device is using if it hasn't told * us. If we know the std, check if the device is using any of * the words from the next level up. It happens. */ if (like_std < std) like_std = std; if (((std == 5) || (!std && (like_std < 6))) && ((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) && (( val[CMDS_SUPP_1] & CMDS_W83) > 0x00ff)) || ((( val[CMDS_SUPP_2] & VALID) == VALID_VAL) && ( val[CMDS_SUPP_2] & CMDS_W84) ) ) ) { like_std = 6; } else if (((std == 4) || (!std && (like_std < 5))) && ((((val[INTEGRITY] & SIG) == SIG_VAL) && !chksum) || (( val[HWRST_RSLT] & VALID) == VALID_VAL) || ((( val[CMDS_SUPP_1] & VALID) == VALID_VAL) && (( val[CMDS_SUPP_1] & CMDS_W83) > 0x001f)) ) ) { like_std = 5; } else if (((std == 3) || (!std && (like_std < 4))) && ((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) && ((( val[CMDS_SUPP_1] & CMDS_W83) > 0x0000) || (( val[CMDS_SUPP_0] & CMDS_W82) > 0x000f))) || (( val[CAPAB_1] & VALID) == VALID_VAL) || (( val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) || (( val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP) ) ) { like_std = 4; } else if (((std == 2) || (!std && (like_std < 3))) && ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) ) { like_std = 3; } else if (((std == 1) || (!std && (like_std < 2))) && ((val[CAPAB_0] & (IORDY_SUP | IORDY_OFF)) || (val[WHATS_VALID] & OK_W64_70)) ) { like_std = 2; } if (!std) printf("\n\tLikely used: %u\n", like_std); else if (like_std > std) printf("& some of %u\n", like_std); else bb_putchar('\n'); } else { /* TBD: do CDROM stuff more thoroughly. For now... */ kk = 0; if (val[CDR_MINOR] == 9) { kk = 1; printf("\n\tUsed: ATAPI for CD-ROMs, SFF-8020i, r2.5"); } if (val[CDR_MAJOR] && (val[CDR_MAJOR] !=NOVAL_1)) { kk = 1; printf("\n\tSupported: CD-ROM ATAPI"); jj = val[CDR_MAJOR] >> 1; for (ii = 1; ii < 15; ii++) { if (jj & 0x0001) printf("-%u ", ii); jj >>= 1; } } puts(kk ? "" : "\n\tLikely used CD-ROM ATAPI-1"); /* the cdrom stuff is more like ATA-2 than anything else, so: */ like_std = 2; } if (min_std == 0xffff) min_std = like_std > 4 ? like_std - 3 : 1; printf("Configuration:\n"); /* more info from the general configuration word */ if ((eqpt != CDROM) && (like_std == 1)) { jj = val[GEN_CONFIG] >> 1; for (ii = 1; ii < 15; ii++) { if (jj & 0x0001) printf("\t%s\n", nth_string(ata1_cfg_str, ii)); jj >>=1; } } if (dev == ATAPI_DEV) { if ((val[GEN_CONFIG] & DRQ_RESPONSE_TIME) == DRQ_3MS_VAL) strng = "3ms"; else if ((val[GEN_CONFIG] & DRQ_RESPONSE_TIME) == DRQ_INTR_VAL) strng = "<=10ms with INTRQ"; else if ((val[GEN_CONFIG] & DRQ_RESPONSE_TIME) == DRQ_50US_VAL) strng ="50us"; else strng = "unknown"; printf("\tDRQ response: %s\n\tPacket size: ", strng); /* Data Request (DRQ) */ if ((val[GEN_CONFIG] & PKT_SIZE_SUPPORTED) == PKT_SIZE_12_VAL) strng = "12 bytes"; else if ((val[GEN_CONFIG] & PKT_SIZE_SUPPORTED) == PKT_SIZE_16_VAL) strng = "16 bytes"; else strng = "unknown"; puts(strng); } else { /* addressing...CHS? See section 6.2 of ATA specs 4 or 5 */ ll = (uint32_t)val[LBA_SECTS_MSB] << 16 | val[LBA_SECTS_LSB]; mm = 0; bbbig = 0; if ((ll > 0x00FBFC10) && (!val[LCYLS])) printf("\tCHS addressing not supported\n"); else { jj = val[WHATS_VALID] & OK_W54_58; printf("\tLogical\t\tmax\tcurrent\n" "\tcylinders\t%u\t%u\n" "\theads\t\t%u\t%u\n" "\tsectors/track\t%u\t%u\n" "\t--\n", val[LCYLS], jj ? val[LCYLS_CUR] : 0, val[LHEADS], jj ? val[LHEADS_CUR] : 0, val[LSECTS], jj ? val[LSECTS_CUR] : 0); if ((min_std == 1) && (val[TRACK_BYTES] || val[SECT_BYTES])) printf("\tbytes/track: %u\tbytes/sector: %u\n", val[TRACK_BYTES], val[SECT_BYTES]); if (jj) { mm = (uint32_t)val[CAPACITY_MSB] << 16 | val[CAPACITY_LSB]; if (like_std < 3) { /* check Endian of capacity bytes */ nn = val[LCYLS_CUR] * val[LHEADS_CUR] * val[LSECTS_CUR]; oo = (uint32_t)val[CAPACITY_LSB] << 16 | val[CAPACITY_MSB]; if (abs(mm - nn) > abs(oo - nn)) mm = oo; } printf("\tCHS current addressable sectors:%11u\n", mm); } } /* LBA addressing */ printf("\tLBA user addressable sectors:%11u\n", ll); if (((val[CMDS_SUPP_1] & VALID) == VALID_VAL) && (val[CMDS_SUPP_1] & SUPPORT_48_BIT) ) { bbbig = (uint64_t)val[LBA_64_MSB] << 48 | (uint64_t)val[LBA_48_MSB] << 32 | (uint64_t)val[LBA_MID] << 16 | val[LBA_LSB]; printf("\tLBA48 user addressable sectors:%11"PRIu64"\n", bbbig); } if (!bbbig) bbbig = (uint64_t)(ll>mm ? ll : mm); /* # 512 byte blocks */ printf("\tdevice size with M = 1024*1024: %11"PRIu64" MBytes\n", bbbig>>11); bbbig = (bbbig << 9) / 1000000; printf("\tdevice size with M = 1000*1000: %11"PRIu64" MBytes ", bbbig); if (bbbig > 1000) printf("(%"PRIu64" GB)\n", bbbig/1000); else bb_putchar('\n'); } /* hw support of commands (capabilities) */ printf("Capabilities:\n\t"); if (dev == ATAPI_DEV) { if (eqpt != CDROM && (val[CAPAB_0] & CMD_Q_SUP)) printf("Cmd queuing, "); if (val[CAPAB_0] & OVLP_SUP) printf("Cmd overlap, "); } if (val[CAPAB_0] & LBA_SUP) printf("LBA, "); if (like_std != 1) { printf("IORDY%s(can%s be disabled)\n", !(val[CAPAB_0] & IORDY_SUP) ? "(may be)" : "", (val[CAPAB_0] & IORDY_OFF) ? "" :"not"); } else printf("no IORDY\n"); if ((like_std == 1) && val[BUF_TYPE]) { printf("\tBuffer type: %04x: %s%s\n", val[BUF_TYPE], (val[BUF_TYPE] < 2) ? "single port, single-sector" : "dual port, multi-sector", (val[BUF_TYPE] > 2) ? " with read caching ability" : ""); } if ((min_std == 1) && (val[BUFFER__SIZE] && (val[BUFFER__SIZE] != NOVAL_1))) { printf("\tBuffer size: %.1fkB\n", (float)val[BUFFER__SIZE]/2); } if ((min_std < 4) && (val[RW_LONG])) { printf("\tbytes avail on r/w long: %u\n", val[RW_LONG]); } if ((eqpt != CDROM) && (like_std > 3)) { printf("\tQueue depth: %u\n", (val[QUEUE_DEPTH] & DEPTH_BITS) + 1); } if (dev == ATA_DEV) { if (like_std == 1) printf("\tCan%s perform double-word IO\n", (!val[DWORD_IO]) ? "not" : ""); else { printf("\tStandby timer values: spec'd by %s", (val[CAPAB_0] & STD_STBY) ? "standard" : "vendor"); if ((like_std > 3) && ((val[CAPAB_1] & VALID) == VALID_VAL)) printf(", %s device specific minimum\n", (val[CAPAB_1] & MIN_STANDBY_TIMER) ? "with" : "no"); else bb_putchar('\n'); } printf("\tR/W multiple sector transfer: "); if ((like_std < 3) && !(val[SECTOR_XFER_MAX] & SECTOR_XFER)) printf("not supported\n"); else { printf("Max = %u\tCurrent = ", val[SECTOR_XFER_MAX] & SECTOR_XFER); if (val[SECTOR_XFER_CUR] & MULTIPLE_SETTING_VALID) printf("%u\n", val[SECTOR_XFER_CUR] & SECTOR_XFER); else printf("?\n"); } if ((like_std > 3) && (val[CMDS_SUPP_1] & 0x0008)) { /* We print out elsewhere whether the APM feature is enabled or * not. If it's not enabled, let's not repeat the info; just print * nothing here. */ printf("\tAdvancedPM level: "); if ((val[ADV_PWR] & 0xFF00) == 0x4000) { uint8_t apm_level = val[ADV_PWR] & 0x00FF; printf("%u (0x%x)\n", apm_level, apm_level); } else printf("unknown setting (0x%04x)\n", val[ADV_PWR]); } if (like_std > 5 && val[ACOUSTIC]) { printf("\tRecommended acoustic management value: %u, current value: %u\n", (val[ACOUSTIC] >> 8) & 0x00ff, val[ACOUSTIC] & 0x00ff); } } else { /* ATAPI */ if (eqpt != CDROM && (val[CAPAB_0] & SWRST_REQ)) printf("\tATA sw reset required\n"); if (val[PKT_REL] || val[SVC_NBSY]) { printf("\tOverlap support:"); if (val[PKT_REL]) printf(" %uus to release bus.", val[PKT_REL]); if (val[SVC_NBSY]) printf(" %uus to clear BSY after SERVICE cmd.", val[SVC_NBSY]); bb_putchar('\n'); } } /* DMA stuff. Check that only one DMA mode is selected. */ printf("\tDMA: "); if (!(val[CAPAB_0] & DMA_SUP)) printf("not supported\n"); else { if (val[DMA_MODE] && !val[SINGLE_DMA] && !val[MULTI_DMA]) printf(" sdma%u\n", (val[DMA_MODE] & MODE) >> 8); if (val[SINGLE_DMA]) { jj = val[SINGLE_DMA]; kk = val[SINGLE_DMA] >> 8; err_dma += mode_loop(jj, kk, 's', &have_mode); } if (val[MULTI_DMA]) { jj = val[MULTI_DMA]; kk = val[MULTI_DMA] >> 8; err_dma += mode_loop(jj, kk, 'm', &have_mode); } if ((val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) { jj = val[ULTRA_DMA]; kk = val[ULTRA_DMA] >> 8; err_dma += mode_loop(jj, kk, 'u', &have_mode); } if (err_dma || !have_mode) printf("(?)"); bb_putchar('\n'); if ((dev == ATAPI_DEV) && (eqpt != CDROM) && (val[CAPAB_0] & DMA_IL_SUP)) printf("\t\tInterleaved DMA support\n"); if ((val[WHATS_VALID] & OK_W64_70) && (val[DMA_TIME_MIN] || val[DMA_TIME_NORM]) ) { printf("\t\tCycle time:"); if (val[DMA_TIME_MIN]) printf(" min=%uns", val[DMA_TIME_MIN]); if (val[DMA_TIME_NORM]) printf(" recommended=%uns", val[DMA_TIME_NORM]); bb_putchar('\n'); } } /* Programmed IO stuff */ printf("\tPIO: "); /* If a drive supports mode n (e.g. 3), it also supports all modes less * than n (e.g. 3, 2, 1 and 0). Print all the modes. */ if ((val[WHATS_VALID] & OK_W64_70) && (val[ADV_PIO_MODES] & PIO_SUP)) { jj = ((val[ADV_PIO_MODES] & PIO_SUP) << 3) | 0x0007; for (ii = 0; ii <= PIO_MODE_MAX; ii++) { if (jj & 0x0001) printf("pio%d ", ii); jj >>=1; } bb_putchar('\n'); } else if (((min_std < 5) || (eqpt == CDROM)) && (val[PIO_MODE] & MODE)) { for (ii = 0; ii <= val[PIO_MODE]>>8; ii++) printf("pio%d ", ii); bb_putchar('\n'); } else puts("unknown"); if (val[WHATS_VALID] & OK_W64_70) { if (val[PIO_NO_FLOW] || val[PIO_FLOW]) { printf("\t\tCycle time:"); if (val[PIO_NO_FLOW]) printf(" no flow control=%uns", val[PIO_NO_FLOW]); if (val[PIO_FLOW]) printf(" IORDY flow control=%uns", val[PIO_FLOW]); bb_putchar('\n'); } } if ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) { printf("Commands/features:\n" "\tEnabled\tSupported:\n"); jj = val[CMDS_SUPP_0]; kk = val[CMDS_EN_0]; for (ii = 0; ii < NUM_CMD_FEAT_STR; ii++) { const char *feat_str = nth_string(cmd_feat_str, ii); if ((jj & 0x8000) && (*feat_str != '\0')) { printf("\t%s\t%s\n", (kk & 0x8000) ? " *" : "", feat_str); } jj <<= 1; kk <<= 1; if (ii % 16 == 15) { jj = val[CMDS_SUPP_0+1+(ii/16)]; kk = val[CMDS_EN_0+1+(ii/16)]; } if (ii == 31) { if ((val[CMDS_SUPP_2] & VALID) != VALID_VAL) ii +=16; } } } /* Removable Media Status Notification feature set */ if ((val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP) printf("\t%s supported\n", nth_string(cmd_feat_str, 27)); /* security */ if ((eqpt != CDROM) && (like_std > 3) && (val[SECU_STATUS] || val[ERASE_TIME] || val[ENH_ERASE_TIME]) ) { printf("Security:\n"); if (val[PSWD_CODE] && (val[PSWD_CODE] != NOVAL_1)) printf("\tMaster password revision code = %u\n", val[PSWD_CODE]); jj = val[SECU_STATUS]; if (jj) { for (ii = 0; ii < NUM_SECU_STR; ii++) { printf("\t%s\t%s\n", (!(jj & 0x0001)) ? "not" : "", nth_string(secu_str, ii)); jj >>=1; } if (val[SECU_STATUS] & SECU_ENABLED) { printf("\tSecurity level %s\n", (val[SECU_STATUS] & SECU_LEVEL) ? "maximum" : "high"); } } jj = val[ERASE_TIME] & ERASE_BITS; kk = val[ENH_ERASE_TIME] & ERASE_BITS; if (jj || kk) { bb_putchar('\t'); if (jj) printf("%umin for %sSECURITY ERASE UNIT. ", jj==ERASE_BITS ? 508 : jj<<1, ""); if (kk) printf("%umin for %sSECURITY ERASE UNIT. ", kk==ERASE_BITS ? 508 : kk<<1, "ENHANCED "); bb_putchar('\n'); } } /* reset result */ jj = val[HWRST_RSLT]; if ((jj & VALID) == VALID_VAL) { oo = (jj & RST0); if (!oo) jj >>= 8; if ((jj & DEV_DET) == JUMPER_VAL) strng = " determined by the jumper"; else if ((jj & DEV_DET) == CSEL_VAL) strng = " determined by CSEL"; else strng = ""; printf("HW reset results:\n" "\tCBLID- %s Vih\n" "\tDevice num = %i%s\n", (val[HWRST_RSLT] & CBLID) ? "above" : "below", !(oo), strng); } /* more stuff from std 5 */ if ((like_std > 4) && (eqpt != CDROM)) { if (val[CFA_PWR_MODE] & VALID_W160) { printf("CFA power mode 1:\n" "\t%s%s\n", (val[CFA_PWR_MODE] & PWR_MODE_OFF) ? "disabled" : "enabled", (val[CFA_PWR_MODE] & PWR_MODE_REQ) ? " and required by some commands" : ""); if (val[CFA_PWR_MODE] & MAX_AMPS) printf("\tMaximum current = %uma\n", val[CFA_PWR_MODE] & MAX_AMPS); } if ((val[INTEGRITY] & SIG) == SIG_VAL) { printf("Checksum: %scorrect\n", chksum ? "in" : ""); } } exit(EXIT_SUCCESS); } #endif // Historically, if there was no HDIO_OBSOLETE_IDENTITY, then // then the HDIO_GET_IDENTITY only returned 142 bytes. // Otherwise, HDIO_OBSOLETE_IDENTITY returns 142 bytes, // and HDIO_GET_IDENTITY returns 512 bytes. But the latest // 2.5.xx kernels no longer define HDIO_OBSOLETE_IDENTITY // (which they should, but they should just return -EINVAL). // // So.. we must now assume that HDIO_GET_IDENTITY returns 512 bytes. // On a really old system, it will not, and we will be confused. // Too bad, really. #if ENABLE_FEATURE_HDPARM_GET_IDENTITY static const char cfg_str[] ALIGN1 = """\0" "HardSect""\0" "SoftSect""\0" "NotMFM""\0" "HdSw>15uSec""\0" "SpinMotCtl""\0" "Fixed""\0" "Removeable""\0" "DTR<=5Mbs""\0" "DTR>5Mbs""\0" "DTR>10Mbs""\0" "RotSpdTol>.5%""\0" "dStbOff""\0" "TrkOff""\0" "FmtGapReq""\0" "nonMagnetic" ; static const char BuffType[] ALIGN1 = "unknown""\0" "1Sect""\0" "DualPort""\0" "DualPortCache" ; static NOINLINE void dump_identity(const struct hd_driveid *id) { int i; const unsigned short *id_regs = (const void*) id; printf("\n Model=%.40s, FwRev=%.8s, SerialNo=%.20s\n Config={", id->model, id->fw_rev, id->serial_no); for (i = 0; i <= 15; i++) { if (id->config & (1<cyls, id->heads, id->sectors, id->track_bytes, id->sector_bytes, id->ecc_bytes, id->buf_type, nth_string(BuffType, (id->buf_type > 3) ? 0 : id->buf_type), id->buf_size/2, id->max_multsect); if (id->max_multsect) { printf(", MultSect="); if (!(id->multsect_valid & 1)) printf("?%u?", id->multsect); else if (id->multsect) printf("%u", id->multsect); else printf("off"); } bb_putchar('\n'); if (!(id->field_valid & 1)) printf(" (maybe):"); printf(" CurCHS=%u/%u/%u, CurSects=%lu, LBA=%s", id->cur_cyls, id->cur_heads, id->cur_sectors, (BB_BIG_ENDIAN) ? (unsigned long)(id->cur_capacity0 << 16) | id->cur_capacity1 : (unsigned long)(id->cur_capacity1 << 16) | id->cur_capacity0, ((id->capability&2) == 0) ? "no" : "yes"); if (id->capability & 2) printf(", LBAsects=%u", id->lba_capacity); printf("\n IORDY=%s", (id->capability & 8) ? ((id->capability & 4) ? "on/off" : "yes") : "no"); if (((id->capability & 8) || (id->field_valid & 2)) && (id->field_valid & 2)) printf(", tPIO={min:%u,w/IORDY:%u}", id->eide_pio, id->eide_pio_iordy); if ((id->capability & 1) && (id->field_valid & 2)) printf(", tDMA={min:%u,rec:%u}", id->eide_dma_min, id->eide_dma_time); printf("\n PIO modes: "); if (id->tPIO <= 5) { printf("pio0 "); if (id->tPIO >= 1) printf("pio1 "); if (id->tPIO >= 2) printf("pio2 "); } if (id->field_valid & 2) { static const masks_labels_t pio_modes = { .masks = { 1, 2, ~3 }, .labels = "pio3 \0""pio4 \0""pio? \0", }; print_flags(&pio_modes, id->eide_pio_modes); } if (id->capability & 1) { if (id->dma_1word | id->dma_mword) { static const int dma_wmode_masks[] = { 0x100, 1, 0x200, 2, 0x400, 4, 0xf800, 0xf8 }; printf("\n DMA modes: "); print_flags_separated(dma_wmode_masks, "*\0""sdma0 \0""*\0""sdma1 \0""*\0""sdma2 \0""*\0""sdma? \0", id->dma_1word, NULL); print_flags_separated(dma_wmode_masks, "*\0""mdma0 \0""*\0""mdma1 \0""*\0""mdma2 \0""*\0""mdma? \0", id->dma_mword, NULL); } } if (((id->capability & 8) || (id->field_valid & 2)) && id->field_valid & 4) { static const masks_labels_t ultra_modes1 = { .masks = { 0x100, 0x001, 0x200, 0x002, 0x400, 0x004 }, .labels = "*\0""udma0 \0""*\0""udma1 \0""*\0""udma2 \0", }; printf("\n UDMA modes: "); print_flags(&ultra_modes1, id->dma_ultra); #ifdef __NEW_HD_DRIVE_ID if (id->hw_config & 0x2000) { #else /* !__NEW_HD_DRIVE_ID */ if (id->word93 & 0x2000) { #endif /* __NEW_HD_DRIVE_ID */ static const masks_labels_t ultra_modes2 = { .masks = { 0x0800, 0x0008, 0x1000, 0x0010, 0x2000, 0x0020, 0x4000, 0x0040, 0x8000, 0x0080 }, .labels = "*\0""udma3 \0""*\0""udma4 \0" "*\0""udma5 \0""*\0""udma6 \0" "*\0""udma7 \0" }; print_flags(&ultra_modes2, id->dma_ultra); } } printf("\n AdvancedPM=%s", (!(id_regs[83] & 8)) ? "no" : "yes"); if (id_regs[83] & 8) { if (!(id_regs[86] & 8)) printf(": disabled (255)"); else if ((id_regs[91] & 0xFF00) != 0x4000) printf(": unknown setting"); else printf(": mode=0x%02X (%u)", id_regs[91] & 0xFF, id_regs[91] & 0xFF); } if (id_regs[82] & 0x20) printf(" WriteCache=%s", (id_regs[85] & 0x20) ? "enabled" : "disabled"); #ifdef __NEW_HD_DRIVE_ID if ((id->minor_rev_num && id->minor_rev_num <= 31) || (id->major_rev_num && id->minor_rev_num <= 31) ) { printf("\n Drive conforms to: %s: ", (id->minor_rev_num <= 31) ? nth_string(minor_str, id->minor_rev_num) : "unknown"); if (id->major_rev_num != 0x0000 /* NOVAL_0 */ && id->major_rev_num != 0xFFFF /* NOVAL_1 */ ) { for (i = 0; i <= 15; i++) { if (id->major_rev_num & (1< UINT_MAX) return UINT_MAX; return u.blksize64; } static void print_timing(unsigned m, unsigned elapsed_us) { unsigned sec = elapsed_us / 1000000; unsigned hs = (elapsed_us % 1000000) / 10000; printf("%5u MB in %u.%02u seconds = %u kB/s\n", m, sec, hs, /* "| 1" prevents div-by-0 */ (unsigned) ((unsigned long long)m * (1024 * 1000000) / (elapsed_us | 1)) // ~= (m * 1024) / (elapsed_us / 1000000) // = kb / elapsed_sec ); } static void do_time(int cache /*,int fd*/) /* cache=1: time cache: repeatedly read N MB at offset 0 * cache=0: time device: linear read, starting at offset 0 */ { unsigned max_iterations, iterations; unsigned start; /* doesn't need to be long long */ unsigned elapsed, elapsed2; unsigned total_MB; char *buf = xmalloc(TIMING_BUF_BYTES); if (mlock(buf, TIMING_BUF_BYTES)) bb_perror_msg_and_die("mlock"); /* Clear out the device request queues & give them time to complete. * NB: *small* delay. User is expected to have a clue and to not run * heavy io in parallel with measurements. */ sync(); sleep(1); if (cache) { /* Time cache */ seek_to_zero(); read_big_block(buf); printf("Timing buffer-cache reads: "); } else { /* Time device */ printf("Timing buffered disk reads:"); } fflush_all(); /* Now do the timing */ iterations = 0; /* Max time to run (small for cache, avoids getting * huge total_MB which can overlow unsigned type) */ elapsed2 = 510000; /* cache */ max_iterations = UINT_MAX; if (!cache) { elapsed2 = 3000000; /* not cache */ /* Don't want to read past the end! */ max_iterations = dev_size_mb() / TIMING_BUF_MB; } start = monotonic_us(); do { if (cache) seek_to_zero(); read_big_block(buf); elapsed = (unsigned)monotonic_us() - start; ++iterations; } while (elapsed < elapsed2 && iterations < max_iterations); total_MB = iterations * TIMING_BUF_MB; //printf(" elapsed:%u iterations:%u ", elapsed, iterations); if (cache) { /* Cache: remove lseek() and monotonic_us() overheads * from elapsed */ start = monotonic_us(); do { seek_to_zero(); elapsed2 = (unsigned)monotonic_us() - start; } while (--iterations); //printf(" elapsed2:%u ", elapsed2); elapsed -= elapsed2; total_MB *= 2; // BUFCACHE_FACTOR (why?) flush_buffer_cache(); } print_timing(total_MB, elapsed); munlock(buf, TIMING_BUF_BYTES); free(buf); } #if ENABLE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF static void bus_state_value(unsigned value) { if (value == BUSSTATE_ON) on_off(1); else if (value == BUSSTATE_OFF) on_off(0); else if (value == BUSSTATE_TRISTATE) printf(" (tristate)\n"); else printf(" (unknown: %u)\n", value); } #endif #ifdef HDIO_DRIVE_CMD static void interpret_standby(uint8_t standby) { printf(" ("); if (standby == 0) { printf("off"); } else if (standby <= 240 || standby == 252 || standby == 255) { /* standby is in 5 sec units */ unsigned t = standby * 5; printf("%u minutes %u seconds", t / 60, t % 60); } else if (standby <= 251) { unsigned t = (standby - 240); /* t is in 30 min units */; printf("%u.%c hours", t / 2, (t & 1) ? '5' : '0'); } if (standby == 253) printf("vendor-specific"); if (standby == 254) printf("reserved"); printf(")\n"); } static const uint8_t xfermode_val[] ALIGN1 = { 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 32, 33, 34, 35, 36, 37, 38, 39, 64, 65, 66, 67, 68, 69, 70, 71 }; /* NB: we save size by _not_ storing terninating NUL! */ static const char xfermode_name[][5] ALIGN1 = { "pio0", "pio1", "pio2", "pio3", "pio4", "pio5", "pio6", "pio7", "sdma0","sdma1","sdma2","sdma3","sdma4","sdma5","sdma6","sdma7", "mdma0","mdma1","mdma2","mdma3","mdma4","mdma5","mdma6","mdma7", "udma0","udma1","udma2","udma3","udma4","udma5","udma6","udma7" }; static int translate_xfermode(const char *name) { int val; unsigned i; for (i = 0; i < ARRAY_SIZE(xfermode_val); i++) { if (!strncmp(name, xfermode_name[i], 5)) if (strlen(name) <= 5) return xfermode_val[i]; } /* Negative numbers are invalid and are caught later */ val = bb_strtoi(name, NULL, 10); if (!errno) return val; return -1; } static void interpret_xfermode(unsigned xfermode) { printf(" ("); if (xfermode == 0) printf("default PIO mode"); else if (xfermode == 1) printf("default PIO mode, disable IORDY"); else if (xfermode >= 8 && xfermode <= 15) printf("PIO flow control mode%u", xfermode - 8); else if (xfermode >= 16 && xfermode <= 23) printf("singleword DMA mode%u", xfermode - 16); else if (xfermode >= 32 && xfermode <= 39) printf("multiword DMA mode%u", xfermode - 32); else if (xfermode >= 64 && xfermode <= 71) printf("UltraDMA mode%u", xfermode - 64); else printf("unknown"); printf(")\n"); } #endif /* HDIO_DRIVE_CMD */ static void print_flag(int flag, const char *s, unsigned long value) { if (flag) printf(" setting %s to %lu\n", s, value); } static void process_dev(char *devname) { /*int fd;*/ long parm, multcount; #ifndef HDIO_DRIVE_CMD int force_operation = 0; #endif /* Please restore args[n] to these values after each ioctl except for args[2] */ unsigned char args[4] = { WIN_SETFEATURES, 0, 0, 0 }; const char *fmt = " %s\t= %2ld"; /*fd = xopen_nonblocking(devname);*/ xmove_fd(xopen_nonblocking(devname), fd); printf("\n%s:\n", devname); if (getset_readahead == IS_SET) { print_flag(getset_readahead, "fs readahead", Xreadahead); ioctl_or_warn(fd, BLKRASET, (int *)Xreadahead); } #if ENABLE_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF if (unregister_hwif) { printf(" attempting to unregister hwif#%lu\n", hwif); ioctl_or_warn(fd, HDIO_UNREGISTER_HWIF, (int *)(unsigned long)hwif); } #endif #if ENABLE_FEATURE_HDPARM_HDIO_SCAN_HWIF if (scan_hwif == IS_SET) { printf(" attempting to scan hwif (0x%lx, 0x%lx, %lu)\n", hwif_data, hwif_ctrl, hwif_irq); args[0] = hwif_data; args[1] = hwif_ctrl; args[2] = hwif_irq; ioctl_or_warn(fd, HDIO_SCAN_HWIF, args); args[0] = WIN_SETFEATURES; args[1] = 0; } #endif if (set_piomode) { if (noisy_piomode) { printf(" attempting to "); if (piomode == 255) printf("auto-tune PIO mode\n"); else if (piomode < 100) printf("set PIO mode to %d\n", piomode); else if (piomode < 200) printf("set MDMA mode to %d\n", (piomode-100)); else printf("set UDMA mode to %d\n", (piomode-200)); } ioctl_or_warn(fd, HDIO_SET_PIO_MODE, (int *)(unsigned long)piomode); } if (getset_io32bit == IS_SET) { print_flag(getset_io32bit, "32-bit IO_support flag", io32bit); ioctl_or_warn(fd, HDIO_SET_32BIT, (int *)io32bit); } if (getset_mult == IS_SET) { print_flag(getset_mult, "multcount", mult); #ifdef HDIO_DRIVE_CMD ioctl_or_warn(fd, HDIO_SET_MULTCOUNT, (void *)mult); #else force_operation |= (!ioctl_or_warn(fd, HDIO_SET_MULTCOUNT, (void *)mult)); #endif } if (getset_readonly == IS_SET) { print_flag_on_off(getset_readonly, "readonly", readonly); ioctl_or_warn(fd, BLKROSET, &readonly); } if (getset_unmask == IS_SET) { print_flag_on_off(getset_unmask, "unmaskirq", unmask); ioctl_or_warn(fd, HDIO_SET_UNMASKINTR, (int *)unmask); } #if ENABLE_FEATURE_HDPARM_HDIO_GETSET_DMA if (getset_dma == IS_SET) { print_flag_on_off(getset_dma, "using_dma", dma); ioctl_or_warn(fd, HDIO_SET_DMA, (int *)dma); } #endif /* FEATURE_HDPARM_HDIO_GETSET_DMA */ #ifdef HDIO_SET_QDMA if (getset_dma_q == IS_SET) { print_flag_on_off(getset_dma_q, "DMA queue_depth", dma_q); ioctl_or_warn(fd, HDIO_SET_QDMA, (int *)dma_q); } #endif if (getset_nowerr == IS_SET) { print_flag_on_off(getset_nowerr, "nowerr", nowerr); ioctl_or_warn(fd, HDIO_SET_NOWERR, (int *)nowerr); } if (getset_keep == IS_SET) { print_flag_on_off(getset_keep, "keep_settings", keep); ioctl_or_warn(fd, HDIO_SET_KEEPSETTINGS, (int *)keep); } #ifdef HDIO_DRIVE_CMD if (getset_doorlock == IS_SET) { args[0] = doorlock ? WIN_DOORLOCK : WIN_DOORUNLOCK; args[2] = 0; print_flag_on_off(getset_doorlock, "drive doorlock", doorlock); ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args); args[0] = WIN_SETFEATURES; } if (getset_dkeep == IS_SET) { /* lock/unlock the drive's "feature" settings */ print_flag_on_off(getset_dkeep, "drive keep features", dkeep); args[2] = dkeep ? 0x66 : 0xcc; ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args); } if (getset_defects == IS_SET) { args[2] = defects ? 0x04 : 0x84; print_flag(getset_defects, "drive defect-mgmt", defects); ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args); } if (getset_prefetch == IS_SET) { args[1] = prefetch; args[2] = 0xab; print_flag(getset_prefetch, "drive prefetch", prefetch); ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args); args[1] = 0; } if (set_xfermode) { args[1] = xfermode_requested; args[2] = 3; print_flag(1, "xfermode", xfermode_requested); interpret_xfermode(xfermode_requested); ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args); args[1] = 0; } if (getset_lookahead == IS_SET) { args[2] = lookahead ? 0xaa : 0x55; print_flag_on_off(getset_lookahead, "drive read-lookahead", lookahead); ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args); } if (getset_apmmode == IS_SET) { /* feature register */ args[2] = (apmmode == 255) ? 0x85 /* disable */ : 0x05 /* set */; args[1] = apmmode; /* sector count register 1-255 */ printf(" setting APM level to %s 0x%02lX (%ld)\n", (apmmode == 255) ? "disabled" : "", apmmode, apmmode); ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args); args[1] = 0; } if (getset_wcache == IS_SET) { #ifdef DO_FLUSHCACHE #ifndef WIN_FLUSHCACHE #define WIN_FLUSHCACHE 0xe7 #endif #endif /* DO_FLUSHCACHE */ args[2] = wcache ? 0x02 : 0x82; print_flag_on_off(getset_wcache, "drive write-caching", wcache); #ifdef DO_FLUSHCACHE if (!wcache) ioctl_or_warn(fd, HDIO_DRIVE_CMD, &flushcache); #endif /* DO_FLUSHCACHE */ ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args); #ifdef DO_FLUSHCACHE if (!wcache) ioctl_or_warn(fd, HDIO_DRIVE_CMD, &flushcache); #endif /* DO_FLUSHCACHE */ } /* In code below, we do not preserve args[0], but the rest is preserved, including args[2] */ args[2] = 0; if (set_standbynow) { #ifndef WIN_STANDBYNOW1 #define WIN_STANDBYNOW1 0xE0 #endif #ifndef WIN_STANDBYNOW2 #define WIN_STANDBYNOW2 0x94 #endif printf(" issuing standby command\n"); args[0] = WIN_STANDBYNOW1; ioctl_alt_or_warn(HDIO_DRIVE_CMD, args, WIN_STANDBYNOW2); } if (set_sleepnow) { #ifndef WIN_SLEEPNOW1 #define WIN_SLEEPNOW1 0xE6 #endif #ifndef WIN_SLEEPNOW2 #define WIN_SLEEPNOW2 0x99 #endif printf(" issuing sleep command\n"); args[0] = WIN_SLEEPNOW1; ioctl_alt_or_warn(HDIO_DRIVE_CMD, args, WIN_SLEEPNOW2); } if (set_seagate) { args[0] = 0xfb; printf(" disabling Seagate auto powersaving mode\n"); ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args); } if (getset_standby == IS_SET) { args[0] = WIN_SETIDLE1; args[1] = standby_requested; print_flag(1, "standby", standby_requested); interpret_standby(standby_requested); ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args); args[1] = 0; } #else /* HDIO_DRIVE_CMD */ if (force_operation) { char buf[512]; flush_buffer_cache(); if (-1 == read(fd, buf, sizeof(buf))) bb_perror_msg("read of 512 bytes failed"); } #endif /* HDIO_DRIVE_CMD */ if (getset_mult || get_identity) { multcount = -1; if (ioctl(fd, HDIO_GET_MULTCOUNT, &multcount)) { /* To be coherent with ioctl_or_warn. */ if (getset_mult && ENABLE_IOCTL_HEX2STR_ERROR) bb_perror_msg("HDIO_GET_MULTCOUNT"); else bb_perror_msg("ioctl %#x failed", HDIO_GET_MULTCOUNT); } else if (getset_mult) { printf(fmt, "multcount", multcount); on_off(multcount != 0); } } if (getset_io32bit) { if (!ioctl_or_warn(fd, HDIO_GET_32BIT, &parm)) { printf(" IO_support\t=%3ld (", parm); if (parm == 0) printf("default 16-bit)\n"); else if (parm == 2) printf("16-bit)\n"); else if (parm == 1) printf("32-bit)\n"); else if (parm == 3) printf("32-bit w/sync)\n"); else if (parm == 8) printf("Request-Queue-Bypass)\n"); else printf("\?\?\?)\n"); } } if (getset_unmask) { if (!ioctl_or_warn(fd, HDIO_GET_UNMASKINTR, &parm)) print_value_on_off("unmaskirq", parm); } #if ENABLE_FEATURE_HDPARM_HDIO_GETSET_DMA if (getset_dma) { if (!ioctl_or_warn(fd, HDIO_GET_DMA, &parm)) { printf(fmt, "using_dma", parm); if (parm == 8) printf(" (DMA-Assisted-PIO)\n"); else on_off(parm != 0); } } #endif #ifdef HDIO_GET_QDMA if (getset_dma_q) { if (!ioctl_or_warn(fd, HDIO_GET_QDMA, &parm)) print_value_on_off("queue_depth", parm); } #endif if (getset_keep) { if (!ioctl_or_warn(fd, HDIO_GET_KEEPSETTINGS, &parm)) print_value_on_off("keepsettings", parm); } if (getset_nowerr) { if (!ioctl_or_warn(fd, HDIO_GET_NOWERR, &parm)) print_value_on_off("nowerr", parm); } if (getset_readonly) { if (!ioctl_or_warn(fd, BLKROGET, &parm)) print_value_on_off("readonly", parm); } if (getset_readahead) { if (!ioctl_or_warn(fd, BLKRAGET, &parm)) print_value_on_off("readahead", parm); } if (get_geom) { if (!ioctl_or_warn(fd, BLKGETSIZE, &parm)) { struct hd_geometry g; if (!ioctl_or_warn(fd, HDIO_GETGEO, &g)) printf(" geometry\t= %u/%u/%u, sectors = %ld, start = %ld\n", g.cylinders, g.heads, g.sectors, parm, g.start); } } #ifdef HDIO_DRIVE_CMD if (get_powermode) { #ifndef WIN_CHECKPOWERMODE1 #define WIN_CHECKPOWERMODE1 0xE5 #endif #ifndef WIN_CHECKPOWERMODE2 #define WIN_CHECKPOWERMODE2 0x98 #endif const char *state; args[0] = WIN_CHECKPOWERMODE1; if (ioctl_alt_or_warn(HDIO_DRIVE_CMD, args, WIN_CHECKPOWERMODE2)) { if (errno != EIO || args[0] != 0 || args[1] != 0) state = "unknown"; else state = "sleeping"; } else state = (args[2] == 255) ? "active/idle" : "standby"; args[1] = args[2] = 0; printf(" drive state is: %s\n", state); } #endif #if ENABLE_FEATURE_HDPARM_HDIO_DRIVE_RESET if (perform_reset) { ioctl_or_warn(fd, HDIO_DRIVE_RESET, NULL); } #endif /* FEATURE_HDPARM_HDIO_DRIVE_RESET */ #if ENABLE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF if (perform_tristate) { args[0] = 0; args[1] = tristate; ioctl_or_warn(fd, HDIO_TRISTATE_HWIF, &args); } #endif /* FEATURE_HDPARM_HDIO_TRISTATE_HWIF */ #if ENABLE_FEATURE_HDPARM_GET_IDENTITY if (get_identity) { struct hd_driveid id; if (!ioctl(fd, HDIO_GET_IDENTITY, &id)) { if (multcount != -1) { id.multsect = multcount; id.multsect_valid |= 1; } else id.multsect_valid &= ~1; dump_identity(&id); } else if (errno == -ENOMSG) printf(" no identification info available\n"); else if (ENABLE_IOCTL_HEX2STR_ERROR) /* To be coherent with ioctl_or_warn */ bb_perror_msg("HDIO_GET_IDENTITY"); else bb_perror_msg("ioctl %#x failed", HDIO_GET_IDENTITY); } if (get_IDentity) { unsigned char args1[4+512]; /* = { ... } will eat 0.5k of rodata! */ memset(args1, 0, sizeof(args1)); args1[0] = WIN_IDENTIFY; args1[3] = 1; if (!ioctl_alt_or_warn(HDIO_DRIVE_CMD, args1, WIN_PIDENTIFY)) identify((void *)(args1 + 4)); } #endif #if ENABLE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF if (getset_busstate == IS_SET) { print_flag(1, "bus state", busstate); bus_state_value(busstate); ioctl_or_warn(fd, HDIO_SET_BUSSTATE, (int *)(unsigned long)busstate); } if (getset_busstate) { if (!ioctl_or_warn(fd, HDIO_GET_BUSSTATE, &parm)) { printf(fmt, "bus state", parm); bus_state_value(parm); } } #endif if (reread_partn) ioctl_or_warn(fd, BLKRRPART, NULL); if (do_ctimings) do_time(1 /*,fd*/); /* time cache */ if (do_timings) do_time(0 /*,fd*/); /* time device */ if (do_flush) flush_buffer_cache(); close(fd); } #if ENABLE_FEATURE_HDPARM_GET_IDENTITY static int fromhex(unsigned char c) { if (isdigit(c)) return (c - '0'); if (c >= 'a' && c <= 'f') return (c - ('a' - 10)); bb_error_msg_and_die("bad char: '%c' 0x%02x", c, c); } static void identify_from_stdin(void) NORETURN; static void identify_from_stdin(void) { uint16_t sbuf[256]; unsigned char buf[1280]; unsigned char *b = (unsigned char *)buf; int i; xread(STDIN_FILENO, buf, 1280); // Convert the newline-separated hex data into an identify block. for (i = 0; i < 256; i++) { int j; for (j = 0; j < 4; j++) sbuf[i] = (sbuf[i] << 4) + fromhex(*(b++)); } // Parse the data. identify(sbuf); } #else void identify_from_stdin(void); #endif /* busybox specific stuff */ static int parse_opts(unsigned long *value, int min, int max) { if (optarg) { *value = xatol_range(optarg, min, max); return IS_SET; } return IS_GET; } static int parse_opts_0_max(unsigned long *value, int max) { return parse_opts(value, 0, max); } static int parse_opts_0_1(unsigned long *value) { return parse_opts(value, 0, 1); } static int parse_opts_0_INTMAX(unsigned long *value) { return parse_opts(value, 0, INT_MAX); } static void parse_xfermode(int flag, smallint *get, smallint *set, int *value) { if (flag) { *get = IS_GET; if (optarg) { *value = translate_xfermode(optarg); *set = (*value > -1); } } } /*------- getopt short options --------*/ static const char hdparm_options[] ALIGN1 = "gfu::n::p:r::m::c::k::a::B:tT" IF_FEATURE_HDPARM_GET_IDENTITY("iI") IF_FEATURE_HDPARM_HDIO_GETSET_DMA("d::") #ifdef HDIO_DRIVE_CMD "S:D:P:X:K:A:L:W:CyYzZ" #endif IF_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF("U:") #ifdef HDIO_GET_QDMA #ifdef HDIO_SET_QDMA "Q:" #else "Q" #endif #endif IF_FEATURE_HDPARM_HDIO_DRIVE_RESET("w") IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF("x::b:") IF_FEATURE_HDPARM_HDIO_SCAN_HWIF("R:"); /*-------------------------------------*/ /* our main() routine: */ int hdparm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int hdparm_main(int argc, char **argv) { int c; int flagcount = 0; INIT_G(); while ((c = getopt(argc, argv, hdparm_options)) >= 0) { flagcount++; IF_FEATURE_HDPARM_GET_IDENTITY(get_IDentity |= (c == 'I')); IF_FEATURE_HDPARM_GET_IDENTITY(get_identity |= (c == 'i')); get_geom |= (c == 'g'); do_flush |= (c == 'f'); if (c == 'u') getset_unmask = parse_opts_0_1(&unmask); IF_FEATURE_HDPARM_HDIO_GETSET_DMA( if (c == 'd') getset_dma = parse_opts_0_max(&dma, 9); ) if (c == 'n') getset_nowerr = parse_opts_0_1(&nowerr); parse_xfermode((c == 'p'), &noisy_piomode, &set_piomode, &piomode); if (c == 'r') getset_readonly = parse_opts_0_1(&readonly); if (c == 'm') getset_mult = parse_opts_0_INTMAX(&mult /*32*/); if (c == 'c') getset_io32bit = parse_opts_0_INTMAX(&io32bit /*8*/); if (c == 'k') getset_keep = parse_opts_0_1(&keep); if (c == 'a') getset_readahead = parse_opts_0_INTMAX(&Xreadahead); if (c == 'B') getset_apmmode = parse_opts(&apmmode, 1, 255); do_flush |= do_timings |= (c == 't'); do_flush |= do_ctimings |= (c == 'T'); #ifdef HDIO_DRIVE_CMD if (c == 'S') getset_standby = parse_opts_0_max(&standby_requested, 255); if (c == 'D') getset_defects = parse_opts_0_INTMAX(&defects); if (c == 'P') getset_prefetch = parse_opts_0_INTMAX(&prefetch); parse_xfermode((c == 'X'), &get_xfermode, &set_xfermode, &xfermode_requested); if (c == 'K') getset_dkeep = parse_opts_0_1(&prefetch); if (c == 'A') getset_lookahead = parse_opts_0_1(&lookahead); if (c == 'L') getset_doorlock = parse_opts_0_1(&doorlock); if (c == 'W') getset_wcache = parse_opts_0_1(&wcache); get_powermode |= (c == 'C'); set_standbynow |= (c == 'y'); set_sleepnow |= (c == 'Y'); reread_partn |= (c == 'z'); set_seagate |= (c == 'Z'); #endif IF_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF(if (c == 'U') unregister_hwif = parse_opts_0_INTMAX(&hwif)); #ifdef HDIO_GET_QDMA if (c == 'Q') { getset_dma_q = parse_opts_0_INTMAX(&dma_q); } #endif IF_FEATURE_HDPARM_HDIO_DRIVE_RESET(perform_reset = (c == 'r')); IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF(if (c == 'x') perform_tristate = parse_opts_0_1(&tristate)); IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF(if (c == 'b') getset_busstate = parse_opts_0_max(&busstate, 2)); #if ENABLE_FEATURE_HDPARM_HDIO_SCAN_HWIF if (c == 'R') { scan_hwif = parse_opts_0_INTMAX(&hwif_data); hwif_ctrl = xatoi_positive((argv[optind]) ? argv[optind] : ""); hwif_irq = xatoi_positive((argv[optind+1]) ? argv[optind+1] : ""); /* Move past the 2 additional arguments */ argv += 2; argc -= 2; } #endif } /* When no flags are given (flagcount = 0), -acdgkmnru is assumed. */ if (!flagcount) { getset_mult = getset_io32bit = getset_unmask = getset_keep = getset_readonly = getset_readahead = get_geom = IS_GET; IF_FEATURE_HDPARM_HDIO_GETSET_DMA(getset_dma = IS_GET); } argv += optind; if (!*argv) { if (ENABLE_FEATURE_HDPARM_GET_IDENTITY && !isatty(STDIN_FILENO)) identify_from_stdin(); /* EXIT */ bb_show_usage(); } do { process_dev(*argv++); } while (*argv); return EXIT_SUCCESS; } busybox-1.22.1/miscutils/devmem.c0000644000000000000000000000754412263563520015441 0ustar rootroot/* * Licensed under GPLv2 or later, see file LICENSE in this source tree. * Copyright (C) 2000, Jan-Derk Bakker (J.D.Bakker@its.tudelft.nl) * Copyright (C) 2008, BusyBox Team. -solar 4/26/08 */ //usage:#define devmem_trivial_usage //usage: "ADDRESS [WIDTH [VALUE]]" //usage:#define devmem_full_usage "\n\n" //usage: "Read/write from physical address\n" //usage: "\n ADDRESS Address to act upon" //usage: "\n WIDTH Width (8/16/...)" //usage: "\n VALUE Data to be written" #include "libbb.h" int devmem_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int devmem_main(int argc UNUSED_PARAM, char **argv) { void *map_base, *virt_addr; uint64_t read_result; uint64_t writeval = writeval; /* for compiler */ off_t target; unsigned page_size, mapped_size, offset_in_page; int fd; unsigned width = 8 * sizeof(int); /* devmem ADDRESS [WIDTH [VALUE]] */ // TODO: options? // -r: read and output only the value in hex, with 0x prefix // -w: write only, no reads before or after, and no output // or make this behavior default? // Let's try this and see how users react. /* ADDRESS */ if (!argv[1]) bb_show_usage(); errno = 0; target = bb_strtoull(argv[1], NULL, 0); /* allows hex, oct etc */ /* WIDTH */ if (argv[2]) { if (isdigit(argv[2][0]) || argv[2][1]) width = xatou(argv[2]); else { static const char bhwl[] ALIGN1 = "bhwl"; static const uint8_t sizes[] ALIGN1 = { 8 * sizeof(char), 8 * sizeof(short), 8 * sizeof(int), 8 * sizeof(long), 0 /* bad */ }; width = strchrnul(bhwl, (argv[2][0] | 0x20)) - bhwl; width = sizes[width]; } /* VALUE */ if (argv[3]) writeval = bb_strtoull(argv[3], NULL, 0); } else { /* argv[2] == NULL */ /* make argv[3] to be a valid thing to fetch */ argv--; } if (errno) bb_show_usage(); /* one of bb_strtouXX failed */ fd = xopen("/dev/mem", argv[3] ? (O_RDWR | O_SYNC) : (O_RDONLY | O_SYNC)); mapped_size = page_size = getpagesize(); offset_in_page = (unsigned)target & (page_size - 1); if (offset_in_page + width > page_size) { /* This access spans pages. * Must map two pages to make it possible: */ mapped_size *= 2; } map_base = mmap(NULL, mapped_size, argv[3] ? (PROT_READ | PROT_WRITE) : PROT_READ, MAP_SHARED, fd, target & ~(off_t)(page_size - 1)); if (map_base == MAP_FAILED) bb_perror_msg_and_die("mmap"); // printf("Memory mapped at address %p.\n", map_base); virt_addr = (char*)map_base + offset_in_page; if (!argv[3]) { switch (width) { case 8: read_result = *(volatile uint8_t*)virt_addr; break; case 16: read_result = *(volatile uint16_t*)virt_addr; break; case 32: read_result = *(volatile uint32_t*)virt_addr; break; case 64: read_result = *(volatile uint64_t*)virt_addr; break; default: bb_error_msg_and_die("bad width"); } // printf("Value at address 0x%"OFF_FMT"X (%p): 0x%llX\n", // target, virt_addr, // (unsigned long long)read_result); /* Zero-padded output shows the width of access just done */ printf("0x%0*llX\n", (width >> 2), (unsigned long long)read_result); } else { switch (width) { case 8: *(volatile uint8_t*)virt_addr = writeval; // read_result = *(volatile uint8_t*)virt_addr; break; case 16: *(volatile uint16_t*)virt_addr = writeval; // read_result = *(volatile uint16_t*)virt_addr; break; case 32: *(volatile uint32_t*)virt_addr = writeval; // read_result = *(volatile uint32_t*)virt_addr; break; case 64: *(volatile uint64_t*)virt_addr = writeval; // read_result = *(volatile uint64_t*)virt_addr; break; default: bb_error_msg_and_die("bad width"); } // printf("Written 0x%llX; readback 0x%llX\n", // (unsigned long long)writeval, // (unsigned long long)read_result); } if (ENABLE_FEATURE_CLEAN_UP) { if (munmap(map_base, mapped_size) == -1) bb_perror_msg_and_die("munmap"); close(fd); } return EXIT_SUCCESS; } busybox-1.22.1/miscutils/chrt.c0000644000000000000000000000732712263563520015123 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * chrt - manipulate real-time attributes of a process * Copyright (c) 2006-2007 Bernhard Reutner-Fischer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define chrt_trivial_usage //usage: "[-prfom] [PRIO] [PID | PROG ARGS]" //usage:#define chrt_full_usage "\n\n" //usage: "Change scheduling priority and class for a process\n" //usage: "\n -p Operate on PID" //usage: "\n -r Set SCHED_RR class" //usage: "\n -f Set SCHED_FIFO class" //usage: "\n -o Set SCHED_OTHER class" //usage: "\n -m Show min/max priorities" //usage: //usage:#define chrt_example_usage //usage: "$ chrt -r 4 sleep 900; x=$!\n" //usage: "$ chrt -f -p 3 $x\n" //usage: "You need CAP_SYS_NICE privileges to set scheduling attributes of a process" #include #include "libbb.h" static const struct { int policy; char name[sizeof("SCHED_OTHER")]; } policies[] = { {SCHED_OTHER, "SCHED_OTHER"}, {SCHED_FIFO, "SCHED_FIFO"}, {SCHED_RR, "SCHED_RR"} }; //TODO: add // -b, SCHED_BATCH // -i, SCHED_IDLE static void show_min_max(int pol) { const char *fmt = "%s min/max priority\t: %u/%u\n"; int max, min; max = sched_get_priority_max(pol); min = sched_get_priority_min(pol); if ((max|min) < 0) fmt = "%s not supported\n"; printf(fmt, policies[pol].name, min, max); } #define OPT_m (1<<0) #define OPT_p (1<<1) #define OPT_r (1<<2) #define OPT_f (1<<3) #define OPT_o (1<<4) int chrt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int chrt_main(int argc UNUSED_PARAM, char **argv) { pid_t pid = 0; unsigned opt; struct sched_param sp; char *pid_str; char *priority = priority; /* for compiler */ const char *current_new; int policy = SCHED_RR; /* only one policy accepted */ opt_complementary = "r--fo:f--ro:o--rf"; opt = getopt32(argv, "+mprfo"); if (opt & OPT_m) { /* print min/max and exit */ show_min_max(SCHED_FIFO); show_min_max(SCHED_RR); show_min_max(SCHED_OTHER); fflush_stdout_and_exit(EXIT_SUCCESS); } if (opt & OPT_r) policy = SCHED_RR; if (opt & OPT_f) policy = SCHED_FIFO; if (opt & OPT_o) policy = SCHED_OTHER; argv += optind; if (!argv[0]) bb_show_usage(); if (opt & OPT_p) { pid_str = *argv++; if (*argv) { /* "-p [...]" */ priority = pid_str; pid_str = *argv; } /* else "-p ", and *argv == NULL */ pid = xatoul_range(pid_str, 1, ((unsigned)(pid_t)ULONG_MAX) >> 1); } else { priority = *argv++; if (!*argv) bb_show_usage(); } current_new = "current\0new"; if (opt & OPT_p) { int pol; print_rt_info: pol = sched_getscheduler(pid); if (pol < 0) bb_perror_msg_and_die("can't %cet pid %d's policy", 'g', (int)pid); printf("pid %d's %s scheduling policy: %s\n", pid, current_new, policies[pol].name); if (sched_getparam(pid, &sp)) bb_perror_msg_and_die("can't get pid %d's attributes", (int)pid); printf("pid %d's %s scheduling priority: %d\n", (int)pid, current_new, sp.sched_priority); if (!*argv) { /* Either it was just "-p ", * or it was "-p " and we came here * for the second time (see goto below) */ return EXIT_SUCCESS; } *argv = NULL; current_new += 8; } /* from the manpage of sched_getscheduler: [...] sched_priority can have a value in the range 0 to 99. [...] SCHED_OTHER or SCHED_BATCH must be assigned static priority 0. [...] SCHED_FIFO or SCHED_RR can have static priority in 1..99 range. */ sp.sched_priority = xstrtou_range(priority, 0, policy != SCHED_OTHER ? 1 : 0, 99); if (sched_setscheduler(pid, policy, &sp) < 0) bb_perror_msg_and_die("can't %cet pid %d's policy", 's', (int)pid); if (!argv[0]) /* "-p [...]" */ goto print_rt_info; BB_EXECVP_or_die(argv); } busybox-1.22.1/miscutils/makedevs.c0000644000000000000000000001750712263563520015763 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * public domain -- Dave 'Kill a Cop' Cinege * * makedevs * Make ranges of device files quickly. * known bugs: can't deal with alpha ranges */ //usage:#if ENABLE_FEATURE_MAKEDEVS_LEAF //usage:#define makedevs_trivial_usage //usage: "NAME TYPE MAJOR MINOR FIRST LAST [s]" //usage:#define makedevs_full_usage "\n\n" //usage: "Create a range of block or character special files" //usage: "\n" //usage: "\nTYPE is:" //usage: "\n b Block device" //usage: "\n c Character device" //usage: "\n f FIFO, MAJOR and MINOR are ignored" //usage: "\n" //usage: "\nFIRST..LAST specify numbers appended to NAME." //usage: "\nIf 's' is the last argument, the base device is created as well." //usage: "\n" //usage: "\nExamples:" //usage: "\n makedevs /dev/ttyS c 4 66 2 63 -> ttyS2-ttyS63" //usage: "\n makedevs /dev/hda b 3 0 0 8 s -> hda,hda1-hda8" //usage: //usage:#define makedevs_example_usage //usage: "# makedevs /dev/ttyS c 4 66 2 63\n" //usage: "[creates ttyS2-ttyS63]\n" //usage: "# makedevs /dev/hda b 3 0 0 8 s\n" //usage: "[creates hda,hda1-hda8]\n" //usage:#endif //usage: //usage:#if ENABLE_FEATURE_MAKEDEVS_TABLE //usage:#define makedevs_trivial_usage //usage: "[-d device_table] rootdir" //usage:#define makedevs_full_usage "\n\n" //usage: "Create a range of special files as specified in a device table.\n" //usage: "Device table entries take the form of:\n" //usage: " \n" //usage: "Where name is the file name, type can be one of:\n" //usage: " f Regular file\n" //usage: " d Directory\n" //usage: " c Character device\n" //usage: " b Block device\n" //usage: " p Fifo (named pipe)\n" //usage: "uid is the user id for the target file, gid is the group id for the\n" //usage: "target file. The rest of the entries (major, minor, etc) apply to\n" //usage: "to device special files. A '-' may be used for blank entries." //usage: //usage:#define makedevs_example_usage //usage: "For example:\n" //usage: " \n" //usage: "/dev d 755 0 0 - - - - -\n" //usage: "/dev/console c 666 0 0 5 1 - - -\n" //usage: "/dev/null c 666 0 0 1 3 0 0 -\n" //usage: "/dev/zero c 666 0 0 1 5 0 0 -\n" //usage: "/dev/hda b 640 0 0 3 0 0 0 -\n" //usage: "/dev/hda b 640 0 0 3 1 1 1 15\n\n" //usage: "Will Produce:\n" //usage: "/dev\n" //usage: "/dev/console\n" //usage: "/dev/null\n" //usage: "/dev/zero\n" //usage: "/dev/hda\n" //usage: "/dev/hda[0-15]\n" //usage:#endif #include "libbb.h" #if ENABLE_FEATURE_MAKEDEVS_LEAF /* makedevs NAME TYPE MAJOR MINOR FIRST LAST [s] TYPEs: b Block device c Character device f FIFO FIRST..LAST specify numbers appended to NAME. If 's' is the last argument, the base device is created as well. Examples: makedevs /dev/ttyS c 4 66 2 63 -> ttyS2-ttyS63 makedevs /dev/hda b 3 0 0 8 s -> hda,hda1-hda8 */ int makedevs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int makedevs_main(int argc, char **argv) { mode_t mode; char *basedev, *type, *nodname, *buf; int Smajor, Sminor, S, E; if (argc < 7 || argv[1][0] == '-') bb_show_usage(); basedev = argv[1]; buf = xasprintf("%s%u", argv[1], (unsigned)-1); type = argv[2]; Smajor = xatoi_positive(argv[3]); Sminor = xatoi_positive(argv[4]); S = xatoi_positive(argv[5]); E = xatoi_positive(argv[6]); nodname = argv[7] ? basedev : buf; mode = 0660; switch (type[0]) { case 'c': mode |= S_IFCHR; break; case 'b': mode |= S_IFBLK; break; case 'f': mode |= S_IFIFO; break; default: bb_show_usage(); } while (S <= E) { sprintf(buf, "%s%u", basedev, S); /* if mode != S_IFCHR and != S_IFBLK, * third param in mknod() ignored */ if (mknod(nodname, mode, makedev(Smajor, Sminor))) bb_perror_msg("can't create '%s'", nodname); /*if (nodname == basedev)*/ /* ex. /dev/hda - to /dev/hda1 ... */ nodname = buf; S++; Sminor++; } return 0; } #elif ENABLE_FEATURE_MAKEDEVS_TABLE /* Licensed under GPLv2 or later, see file LICENSE in this source tree. */ int makedevs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int makedevs_main(int argc UNUSED_PARAM, char **argv) { parser_t *parser; char *line = (char *)"-"; int ret = EXIT_SUCCESS; opt_complementary = "=1"; /* exactly one param */ getopt32(argv, "d:", &line); argv += optind; xchdir(*argv); /* ensure root dir exists */ umask(0); printf("rootdir=%s\ntable=", *argv); if (NOT_LONE_DASH(line)) { printf("'%s'\n", line); } else { puts(""); } parser = config_open(line); while (config_read(parser, &line, 1, 1, "# \t", PARSE_NORMAL)) { int linenum; char type; unsigned mode = 0755; unsigned major = 0; unsigned minor = 0; unsigned count = 0; unsigned increment = 0; unsigned start = 0; char name[41]; char user[41]; char group[41]; char *full_name = name; uid_t uid; gid_t gid; linenum = parser->lineno; if ((2 > sscanf(line, "%40s %c %o %40s %40s %u %u %u %u %u", name, &type, &mode, user, group, &major, &minor, &start, &increment, &count)) || ((unsigned)(major | minor | start | count | increment) > 255) ) { bb_error_msg("invalid line %d: '%s'", linenum, line); ret = EXIT_FAILURE; continue; } gid = (*group) ? get_ug_id(group, xgroup2gid) : getgid(); uid = (*user) ? get_ug_id(user, xuname2uid) : getuid(); /* We are already in the right root dir, * so make absolute paths relative */ if ('/' == *full_name) full_name++; if (type == 'd') { bb_make_directory(full_name, mode | S_IFDIR, FILEUTILS_RECUR); if (chown(full_name, uid, gid) == -1) { chown_fail: bb_perror_msg("line %d: can't chown %s", linenum, full_name); ret = EXIT_FAILURE; continue; } if (chmod(full_name, mode) < 0) { chmod_fail: bb_perror_msg("line %d: can't chmod %s", linenum, full_name); ret = EXIT_FAILURE; continue; } } else if (type == 'f') { struct stat st; if ((stat(full_name, &st) < 0 || !S_ISREG(st.st_mode))) { bb_perror_msg("line %d: regular file '%s' does not exist", linenum, full_name); ret = EXIT_FAILURE; continue; } if (chown(full_name, uid, gid) < 0) goto chown_fail; if (chmod(full_name, mode) < 0) goto chmod_fail; } else { dev_t rdev; unsigned i; char *full_name_inc; if (type == 'p') { mode |= S_IFIFO; } else if (type == 'c') { mode |= S_IFCHR; } else if (type == 'b') { mode |= S_IFBLK; } else { bb_error_msg("line %d: unsupported file type %c", linenum, type); ret = EXIT_FAILURE; continue; } full_name_inc = xmalloc(strlen(full_name) + sizeof(int)*3 + 2); if (count) count--; for (i = start; i <= start + count; i++) { sprintf(full_name_inc, count ? "%s%u" : "%s", full_name, i); rdev = makedev(major, minor + (i - start) * increment); if (mknod(full_name_inc, mode, rdev) < 0) { bb_perror_msg("line %d: can't create node %s", linenum, full_name_inc); ret = EXIT_FAILURE; } else if (chown(full_name_inc, uid, gid) < 0) { bb_perror_msg("line %d: can't chown %s", linenum, full_name_inc); ret = EXIT_FAILURE; } else if (chmod(full_name_inc, mode) < 0) { bb_perror_msg("line %d: can't chmod %s", linenum, full_name_inc); ret = EXIT_FAILURE; } } free(full_name_inc); } } if (ENABLE_FEATURE_CLEAN_UP) config_close(parser); return ret; } #else # error makedevs configuration error, either leaf or table must be selected #endif busybox-1.22.1/miscutils/fbsplash.c0000644000000000000000000003276312263563520015767 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright (C) 2008 Michele Sanges * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Usage: * - use kernel option 'vga=xxx' or otherwise enable framebuffer device. * - put somewhere fbsplash.cfg file and an image in .ppm format. * - run applet: $ setsid fbsplash [params] & * -c: hide cursor * -d /dev/fbN: framebuffer device (if not /dev/fb0) * -s path_to_image_file (can be "-" for stdin) * -i path_to_cfg_file * -f path_to_fifo (can be "-" for stdin) * - if you want to run it only in presence of a kernel parameter * (for example fbsplash=on), use: * grep -q "fbsplash=on" /* If you want logging messages on /tmp/fbsplash.log... */ #define DEBUG 0 struct globals { #if DEBUG bool bdebug_messages; // enable/disable logging FILE *logfile_fd; // log file #endif unsigned char *addr; // pointer to framebuffer memory unsigned ns[7]; // n-parameters const char *image_filename; struct fb_var_screeninfo scr_var; struct fb_fix_screeninfo scr_fix; unsigned bytes_per_pixel; // cached (8 - scr_var.COLOR.length): unsigned red_shift; unsigned green_shift; unsigned blue_shift; }; #define G (*ptr_to_globals) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ } while (0) #define nbar_width ns[0] // progress bar width #define nbar_height ns[1] // progress bar height #define nbar_posx ns[2] // progress bar horizontal position #define nbar_posy ns[3] // progress bar vertical position #define nbar_colr ns[4] // progress bar color red component #define nbar_colg ns[5] // progress bar color green component #define nbar_colb ns[6] // progress bar color blue component #if DEBUG #define DEBUG_MESSAGE(strMessage, args...) \ if (G.bdebug_messages) { \ fprintf(G.logfile_fd, "[%s][%s] - %s\n", \ __FILE__, __FUNCTION__, strMessage); \ } #else #define DEBUG_MESSAGE(...) ((void)0) #endif /** * Configure palette for RGB:332 */ static void fb_setpal(int fd) { struct fb_cmap cmap; /* fb colors are 16 bit */ unsigned short red[256], green[256], blue[256]; unsigned i; /* RGB:332 */ for (i = 0; i < 256; i++) { /* Color is encoded in pixel value as rrrgggbb. * 3-bit color is mapped to 16-bit one as: * 000 -> 00000000 00000000 * 001 -> 00100100 10010010 * ... * 011 -> 01101101 10110110 * 100 -> 10010010 01001001 * ... * 111 -> 11111111 11111111 */ red[i] = (( i >> 5 ) * 0x9249) >> 2; // rrr * 00 10010010 01001001 >> 2 green[i] = (((i >> 2) & 0x7) * 0x9249) >> 2; // ggg * 00 10010010 01001001 >> 2 /* 2-bit color is easier: */ blue[i] = ( i & 0x3) * 0x5555; // bb * 01010101 01010101 } cmap.start = 0; cmap.len = 256; cmap.red = red; cmap.green = green; cmap.blue = blue; cmap.transp = 0; xioctl(fd, FBIOPUTCMAP, &cmap); } /** * Open and initialize the framebuffer device * \param *strfb_device pointer to framebuffer device */ static void fb_open(const char *strfb_device) { int fbfd = xopen(strfb_device, O_RDWR); // framebuffer properties xioctl(fbfd, FBIOGET_VSCREENINFO, &G.scr_var); xioctl(fbfd, FBIOGET_FSCREENINFO, &G.scr_fix); switch (G.scr_var.bits_per_pixel) { case 8: fb_setpal(fbfd); break; case 16: case 24: case 32: break; default: bb_error_msg_and_die("unsupported %u bpp", (int)G.scr_var.bits_per_pixel); break; } G.red_shift = 8 - G.scr_var.red.length; G.green_shift = 8 - G.scr_var.green.length; G.blue_shift = 8 - G.scr_var.blue.length; G.bytes_per_pixel = (G.scr_var.bits_per_pixel + 7) >> 3; // map the device in memory G.addr = mmap(NULL, G.scr_var.yres * G.scr_fix.line_length, PROT_WRITE, MAP_SHARED, fbfd, 0); if (G.addr == MAP_FAILED) bb_perror_msg_and_die("mmap"); // point to the start of the visible screen G.addr += G.scr_var.yoffset * G.scr_fix.line_length + G.scr_var.xoffset * G.bytes_per_pixel; close(fbfd); } /** * Return pixel value of the passed RGB color. * This is performance critical fn. */ static unsigned fb_pixel_value(unsigned r, unsigned g, unsigned b) { /* We assume that the r,g,b values are <= 255 */ if (G.bytes_per_pixel == 1) { r = r & 0xe0; // 3-bit red g = (g >> 3) & 0x1c; // 3-bit green b = b >> 6; // 2-bit blue return r + g + b; } if (G.bytes_per_pixel == 2) { // ARM PL110 on Integrator/CP has RGBA5551 bit arrangement. // We want to support bit locations like that. // // First shift out unused bits r = r >> G.red_shift; g = g >> G.green_shift; b = b >> G.blue_shift; // Then shift the remaining bits to their offset return (r << G.scr_var.red.offset) + (g << G.scr_var.green.offset) + (b << G.scr_var.blue.offset); } // RGB 888 return b + (g << 8) + (r << 16); } /** * Draw pixel on framebuffer */ static void fb_write_pixel(unsigned char *addr, unsigned pixel) { switch (G.bytes_per_pixel) { case 1: *addr = pixel; break; case 2: *(uint16_t *)addr = pixel; break; case 4: *(uint32_t *)addr = pixel; break; default: // 24 bits per pixel addr[0] = pixel; addr[1] = pixel >> 8; addr[2] = pixel >> 16; } } /** * Draw hollow rectangle on framebuffer */ static void fb_drawrectangle(void) { int cnt; unsigned thispix; unsigned char *ptr1, *ptr2; unsigned char nred = G.nbar_colr/2; unsigned char ngreen = G.nbar_colg/2; unsigned char nblue = G.nbar_colb/2; thispix = fb_pixel_value(nred, ngreen, nblue); // horizontal lines ptr1 = G.addr + G.nbar_posy * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel; ptr2 = G.addr + (G.nbar_posy + G.nbar_height - 1) * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel; cnt = G.nbar_width - 1; do { fb_write_pixel(ptr1, thispix); fb_write_pixel(ptr2, thispix); ptr1 += G.bytes_per_pixel; ptr2 += G.bytes_per_pixel; } while (--cnt >= 0); // vertical lines ptr1 = G.addr + G.nbar_posy * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel; ptr2 = G.addr + G.nbar_posy * G.scr_fix.line_length + (G.nbar_posx + G.nbar_width - 1) * G.bytes_per_pixel; cnt = G.nbar_height - 1; do { fb_write_pixel(ptr1, thispix); fb_write_pixel(ptr2, thispix); ptr1 += G.scr_fix.line_length; ptr2 += G.scr_fix.line_length; } while (--cnt >= 0); } /** * Draw filled rectangle on framebuffer * \param nx1pos,ny1pos upper left position * \param nx2pos,ny2pos down right position * \param nred,ngreen,nblue rgb color */ static void fb_drawfullrectangle(int nx1pos, int ny1pos, int nx2pos, int ny2pos, unsigned char nred, unsigned char ngreen, unsigned char nblue) { int cnt1, cnt2, nypos; unsigned thispix; unsigned char *ptr; thispix = fb_pixel_value(nred, ngreen, nblue); cnt1 = ny2pos - ny1pos; nypos = ny1pos; do { ptr = G.addr + nypos * G.scr_fix.line_length + nx1pos * G.bytes_per_pixel; cnt2 = nx2pos - nx1pos; do { fb_write_pixel(ptr, thispix); ptr += G.bytes_per_pixel; } while (--cnt2 >= 0); nypos++; } while (--cnt1 >= 0); } /** * Draw a progress bar on framebuffer * \param percent percentage of loading */ static void fb_drawprogressbar(unsigned percent) { int left_x, top_y, pos_x; unsigned width, height; // outer box left_x = G.nbar_posx; top_y = G.nbar_posy; width = G.nbar_width - 1; height = G.nbar_height - 1; if ((int)(height | width) < 0) return; // NB: "width" of 1 actually makes rect with width of 2! fb_drawrectangle(); // inner "empty" rectangle left_x++; top_y++; width -= 2; height -= 2; if ((int)(height | width) < 0) return; pos_x = left_x; if (percent > 0) { int i, y; // actual progress bar pos_x += (unsigned)(width * percent) / 100; y = top_y; i = height; if (height == 0) height++; // divide by 0 is bad while (i >= 0) { // draw one-line thick "rectangle" // top line will have gray lvl 200, bottom one 100 unsigned gray_level = 100 + (unsigned)i*100 / height; fb_drawfullrectangle( left_x, y, pos_x, y, gray_level, gray_level, gray_level); y++; i--; } } fb_drawfullrectangle( pos_x, top_y, left_x + width, top_y + height, G.nbar_colr, G.nbar_colg, G.nbar_colb); } /** * Draw image from PPM file */ static void fb_drawimage(void) { FILE *theme_file; char *read_ptr; unsigned char *pixline; unsigned i, j, width, height, line_size; if (LONE_DASH(G.image_filename)) { theme_file = stdin; } else { int fd = open_zipped(G.image_filename); if (fd < 0) bb_simple_perror_msg_and_die(G.image_filename); theme_file = xfdopen_for_read(fd); } /* Parse ppm header: * - Magic: two characters "P6". * - Whitespace (blanks, TABs, CRs, LFs). * - A width, formatted as ASCII characters in decimal. * - Whitespace. * - A height, ASCII decimal. * - Whitespace. * - The maximum color value, ASCII decimal, in 0..65535 * - Newline or other single whitespace character. * (we support newline only) * - A raster of Width * Height pixels in triplets of rgb * in pure binary by 1 or 2 bytes. (we support only 1 byte) */ #define concat_buf bb_common_bufsiz1 read_ptr = concat_buf; while (1) { int w, h, max_color_val; int rem = concat_buf + sizeof(concat_buf) - read_ptr; if (rem < 2 || fgets(read_ptr, rem, theme_file) == NULL ) { bb_error_msg_and_die("bad PPM file '%s'", G.image_filename); } read_ptr = strchrnul(read_ptr, '#'); *read_ptr = '\0'; /* ignore #comments */ if (sscanf(concat_buf, "P6 %u %u %u", &w, &h, &max_color_val) == 3 && max_color_val <= 255 ) { width = w; /* w is on stack, width may be in register */ height = h; break; } } line_size = width*3; pixline = xmalloc(line_size); if (width > G.scr_var.xres) width = G.scr_var.xres; if (height > G.scr_var.yres) height = G.scr_var.yres; for (j = 0; j < height; j++) { unsigned char *pixel; unsigned char *src; if (fread(pixline, 1, line_size, theme_file) != line_size) bb_error_msg_and_die("bad PPM file '%s'", G.image_filename); pixel = pixline; src = G.addr + j * G.scr_fix.line_length; for (i = 0; i < width; i++) { unsigned thispix = fb_pixel_value(pixel[0], pixel[1], pixel[2]); fb_write_pixel(src, thispix); src += G.bytes_per_pixel; pixel += 3; } } free(pixline); fclose(theme_file); } /** * Parse configuration file * \param *cfg_filename name of the configuration file */ static void init(const char *cfg_filename) { static const char param_names[] ALIGN1 = "BAR_WIDTH\0" "BAR_HEIGHT\0" "BAR_LEFT\0" "BAR_TOP\0" "BAR_R\0" "BAR_G\0" "BAR_B\0" #if DEBUG "DEBUG\0" #endif ; char *token[2]; parser_t *parser = config_open2(cfg_filename, xfopen_stdin); while (config_read(parser, token, 2, 2, "#=", (PARSE_NORMAL | PARSE_MIN_DIE) & ~(PARSE_TRIM | PARSE_COLLAPSE))) { unsigned val = xatoi_positive(token[1]); int i = index_in_strings(param_names, token[0]); if (i < 0) bb_error_msg_and_die("syntax error: %s", token[0]); if (i >= 0 && i < 7) G.ns[i] = val; #if DEBUG if (i == 7) { G.bdebug_messages = val; if (G.bdebug_messages) G.logfile_fd = xfopen_for_write("/tmp/fbsplash.log"); } #endif } config_close(parser); } int fbsplash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fbsplash_main(int argc UNUSED_PARAM, char **argv) { const char *fb_device, *cfg_filename, *fifo_filename; FILE *fp = fp; // for compiler char *num_buf; unsigned num; bool bCursorOff; INIT_G(); // parse command line options fb_device = "/dev/fb0"; cfg_filename = NULL; fifo_filename = NULL; bCursorOff = 1 & getopt32(argv, "cs:d:i:f:", &G.image_filename, &fb_device, &cfg_filename, &fifo_filename); // parse configuration file if (cfg_filename) init(cfg_filename); // We must have -s IMG if (!G.image_filename) bb_show_usage(); fb_open(fb_device); if (fifo_filename && bCursorOff) { // hide cursor (BEFORE any fb ops) full_write(STDOUT_FILENO, "\033[?25l", 6); } fb_drawimage(); if (!fifo_filename) return EXIT_SUCCESS; fp = xfopen_stdin(fifo_filename); if (fp != stdin) { // For named pipes, we want to support this: // mkfifo cmd_pipe // fbsplash -f cmd_pipe .... & // ... // echo 33 >cmd_pipe // ... // echo 66 >cmd_pipe // This means that we don't want fbsplash to get EOF // when last writer closes input end. // The simplest way is to open fifo for writing too // and become an additional writer :) open(fifo_filename, O_WRONLY); // errors are ignored } fb_drawprogressbar(0); // Block on read, waiting for some input. // Use of style I/O allows to correctly // handle a case when we have many buffered lines // already in the pipe while ((num_buf = xmalloc_fgetline(fp)) != NULL) { if (strncmp(num_buf, "exit", 4) == 0) { DEBUG_MESSAGE("exit"); break; } num = atoi(num_buf); if (isdigit(num_buf[0]) && (num <= 100)) { #if DEBUG DEBUG_MESSAGE(itoa(num)); #endif fb_drawprogressbar(num); } free(num_buf); } if (bCursorOff) // restore cursor full_write(STDOUT_FILENO, "\033[?25h", 6); return EXIT_SUCCESS; } busybox-1.22.1/miscutils/eject.c0000644000000000000000000000662112263563520015251 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * eject implementation for busybox * * Copyright (C) 2004 Peter Willis * Copyright (C) 2005 Tito Ragusa * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* * This is a simple hack of eject based on something Erik posted in #uclibc. * Most of the dirty work blatantly ripped off from cat.c =) */ //usage:#define eject_trivial_usage //usage: "[-t] [-T] [DEVICE]" //usage:#define eject_full_usage "\n\n" //usage: "Eject DEVICE or default /dev/cdrom\n" //usage: IF_FEATURE_EJECT_SCSI( //usage: "\n -s SCSI device" //usage: ) //usage: "\n -t Close tray" //usage: "\n -T Open/close tray (toggle)" #include #include "libbb.h" /* Must be after libbb.h: they need size_t */ #include "fix_u32.h" #include #include /* various defines swiped from linux/cdrom.h */ #define CDROMCLOSETRAY 0x5319 /* pendant of CDROMEJECT */ #define CDROMEJECT 0x5309 /* Ejects the cdrom media */ #define CDROM_DRIVE_STATUS 0x5326 /* Get tray position, etc. */ /* drive status possibilities returned by CDROM_DRIVE_STATUS ioctl */ #define CDS_TRAY_OPEN 2 #define dev_fd 3 /* Code taken from the original eject (http://eject.sourceforge.net/), * refactored it a bit for busybox (ne-bb@nicoerfurth.de) */ static void eject_scsi(const char *dev) { static const char sg_commands[3][6] = { { ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0 }, { START_STOP, 0, 0, 0, 1, 0 }, { START_STOP, 0, 0, 0, 2, 0 } }; unsigned i; unsigned char sense_buffer[32]; unsigned char inqBuff[2]; sg_io_hdr_t io_hdr; if ((ioctl(dev_fd, SG_GET_VERSION_NUM, &i) < 0) || (i < 30000)) bb_error_msg_and_die("not a sg device or old sg driver"); memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = 6; io_hdr.mx_sb_len = sizeof(sense_buffer); io_hdr.dxfer_direction = SG_DXFER_NONE; /* io_hdr.dxfer_len = 0; */ io_hdr.dxferp = inqBuff; io_hdr.sbp = sense_buffer; io_hdr.timeout = 2000; for (i = 0; i < 3; i++) { io_hdr.cmdp = (void *)sg_commands[i]; ioctl_or_perror_and_die(dev_fd, SG_IO, (void *)&io_hdr, "%s", dev); } /* force kernel to reread partition table when new disc is inserted */ ioctl(dev_fd, BLKRRPART); } #define FLAG_CLOSE 1 #define FLAG_SMART 2 #define FLAG_SCSI 4 static void eject_cdrom(unsigned flags, const char *dev) { int cmd = CDROMEJECT; if (flags & FLAG_CLOSE || ((flags & FLAG_SMART) && ioctl(dev_fd, CDROM_DRIVE_STATUS) == CDS_TRAY_OPEN) ) { cmd = CDROMCLOSETRAY; } ioctl_or_perror_and_die(dev_fd, cmd, NULL, "%s", dev); } int eject_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int eject_main(int argc UNUSED_PARAM, char **argv) { unsigned flags; const char *device; opt_complementary = "?1:t--T:T--t"; flags = getopt32(argv, "tT" IF_FEATURE_EJECT_SCSI("s")); device = argv[optind] ? argv[optind] : "/dev/cdrom"; /* We used to do "umount " here, but it was buggy if something was mounted OVER cdrom and if cdrom is mounted many times. This works equally well (or better): #!/bin/sh umount /dev/cdrom eject /dev/cdrom */ xmove_fd(xopen_nonblocking(device), dev_fd); if (ENABLE_FEATURE_EJECT_SCSI && (flags & FLAG_SCSI)) eject_scsi(device); else eject_cdrom(flags, device); if (ENABLE_FEATURE_CLEAN_UP) close(dev_fd); return EXIT_SUCCESS; } busybox-1.22.1/miscutils/dc.c0000644000000000000000000001205312263563520014541 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" #include //usage:#define dc_trivial_usage //usage: "EXPRESSION..." //usage: //usage:#define dc_full_usage "\n\n" //usage: "Tiny RPN calculator. Operations:\n" //usage: "+, add, -, sub, *, mul, /, div, %, mod, "IF_FEATURE_DC_LIBM("**, exp, ")"and, or, not, xor,\n" //usage: "p - print top of the stack (without popping),\n" //usage: "f - print entire stack,\n" //usage: "o - pop the value and set output radix (must be 10, 16, 8 or 2).\n" //usage: "Examples: 'dc 2 2 add p' -> 4, 'dc 8 8 mul 2 2 + / p' -> 16" //usage: //usage:#define dc_example_usage //usage: "$ dc 2 2 + p\n" //usage: "4\n" //usage: "$ dc 8 8 \\* 2 2 + / p\n" //usage: "16\n" //usage: "$ dc 0 1 and p\n" //usage: "0\n" //usage: "$ dc 0 1 or p\n" //usage: "1\n" //usage: "$ echo 72 9 div 8 mul p | dc\n" //usage: "64\n" #if 0 typedef unsigned data_t; #define DATA_FMT "" #elif 0 typedef unsigned long data_t; #define DATA_FMT "l" #else typedef unsigned long long data_t; #define DATA_FMT "ll" #endif struct globals { unsigned pointer; unsigned base; double stack[1]; } FIX_ALIASING; enum { STACK_SIZE = (COMMON_BUFSIZE - offsetof(struct globals, stack)) / sizeof(double) }; #define G (*(struct globals*)&bb_common_bufsiz1) #define pointer (G.pointer ) #define base (G.base ) #define stack (G.stack ) #define INIT_G() do { \ base = 10; \ } while (0) static void push(double a) { if (pointer >= STACK_SIZE) bb_error_msg_and_die("stack overflow"); stack[pointer++] = a; } static double pop(void) { if (pointer == 0) bb_error_msg_and_die("stack underflow"); return stack[--pointer]; } static void add(void) { push(pop() + pop()); } static void sub(void) { double subtrahend = pop(); push(pop() - subtrahend); } static void mul(void) { push(pop() * pop()); } #if ENABLE_FEATURE_DC_LIBM static void power(void) { double topower = pop(); push(pow(pop(), topower)); } #endif static void divide(void) { double divisor = pop(); push(pop() / divisor); } static void mod(void) { data_t d = pop(); push((data_t) pop() % d); } static void and(void) { push((data_t) pop() & (data_t) pop()); } static void or(void) { push((data_t) pop() | (data_t) pop()); } static void eor(void) { push((data_t) pop() ^ (data_t) pop()); } static void not(void) { push(~(data_t) pop()); } static void set_output_base(void) { static const char bases[] ALIGN1 = { 2, 8, 10, 16, 0 }; unsigned b = (unsigned)pop(); base = *strchrnul(bases, b); if (base == 0) { bb_error_msg("error, base %u is not supported", b); base = 10; } } static void print_base(double print) { data_t x, i; x = (data_t) print; if (base == 10) { if (x == print) /* exactly representable as unsigned integer */ printf("%"DATA_FMT"u\n", x); else printf("%g\n", print); return; } switch (base) { case 16: printf("%"DATA_FMT"x\n", x); break; case 8: printf("%"DATA_FMT"o\n", x); break; default: /* base 2 */ i = MAXINT(data_t) - (MAXINT(data_t) >> 1); /* i is 100000...00000 */ do { if (x & i) break; i >>= 1; } while (i > 1); do { bb_putchar('1' - !(x & i)); i >>= 1; } while (i); bb_putchar('\n'); } } static void print_stack_no_pop(void) { unsigned i = pointer; while (i) print_base(stack[--i]); } static void print_no_pop(void) { print_base(stack[pointer-1]); } struct op { const char name[4]; void (*function) (void); }; static const struct op operators[] = { {"+", add}, {"add", add}, {"-", sub}, {"sub", sub}, {"*", mul}, {"mul", mul}, {"/", divide}, {"div", divide}, #if ENABLE_FEATURE_DC_LIBM {"**", power}, {"exp", power}, {"pow", power}, #endif {"%", mod}, {"mod", mod}, {"and", and}, {"or", or}, {"not", not}, {"eor", eor}, {"xor", eor}, {"p", print_no_pop}, {"f", print_stack_no_pop}, {"o", set_output_base}, }; static void stack_machine(const char *argument) { char *end; double d; const struct op *o; d = strtod(argument, &end); if (end != argument && *end == '\0') { push(d); return; } o = operators; do { if (strcmp(o->name, argument) == 0) { o->function(); return; } o++; } while (o != operators + ARRAY_SIZE(operators)); bb_error_msg_and_die("syntax error at '%s'", argument); } int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int dc_main(int argc UNUSED_PARAM, char **argv) { INIT_G(); argv++; if (!argv[0]) { /* take stuff from stdin if no args are given */ char *line; char *cursor; char *token; while ((line = xmalloc_fgetline(stdin)) != NULL) { cursor = line; while (1) { token = skip_whitespace(cursor); if (*token == '\0') break; cursor = skip_non_whitespace(token); if (*cursor != '\0') *cursor++ = '\0'; stack_machine(token); } free(line); } } else { // why? it breaks "dc -2 2 + p" //if (argv[0][0] == '-') // bb_show_usage(); do { stack_machine(*argv); } while (*++argv); } return EXIT_SUCCESS; } busybox-1.22.1/miscutils/flashcp.c0000644000000000000000000000774712263563520015611 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * busybox reimplementation of flashcp * * (C) 2009 Stefan Seyfried * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define flashcp_trivial_usage //usage: "-v FILE MTD_DEVICE" //usage:#define flashcp_full_usage "\n\n" //usage: "Copy an image to MTD device\n" //usage: "\n -v Verbose" #include "libbb.h" #include /* If 1, simulates "flashing" by writing to existing regular file */ #define MTD_DEBUG 0 #define OPT_v (1 << 0) #define BUFSIZE (8 * 1024) static void progress(int mode, uoff_t count, uoff_t total) { uoff_t percent; if (!option_mask32) //if (!(option_mask32 & OPT_v)) return; percent = count * 100; if (total) percent = (unsigned) (percent / total); printf("\r%s: %"OFF_FMT"u/%"OFF_FMT"u (%u%%) ", (mode < 0) ? "Erasing block" : ((mode == 0) ? "Writing kb" : "Verifying kb"), count, total, (unsigned)percent); fflush_all(); } static void progress_newline(void) { if (!option_mask32) //if (!(option_mask32 & OPT_v)) return; bb_putchar('\n'); } int flashcp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int flashcp_main(int argc UNUSED_PARAM, char **argv) { int fd_f, fd_d; /* input file and mtd device file descriptors */ int i; uoff_t erase_count; struct mtd_info_user mtd; struct erase_info_user e; struct stat statb; // const char *filename, *devicename; RESERVE_CONFIG_UBUFFER(buf, BUFSIZE); RESERVE_CONFIG_UBUFFER(buf2, BUFSIZE); opt_complementary = "=2"; /* exactly 2 non-option args: file, dev */ /*opts =*/ getopt32(argv, "v"); argv += optind; // filename = *argv++; // devicename = *argv; #define filename argv[0] #define devicename argv[1] /* open input file and mtd device and do sanity checks */ fd_f = xopen(filename, O_RDONLY); fstat(fd_f, &statb); fd_d = xopen(devicename, O_SYNC | O_RDWR); #if !MTD_DEBUG if (ioctl(fd_d, MEMGETINFO, &mtd) < 0) { bb_error_msg_and_die("%s is not a MTD flash device", devicename); } if (statb.st_size > mtd.size) { bb_error_msg_and_die("%s bigger than %s", filename, devicename); } #else mtd.erasesize = 64 * 1024; #endif /* always erase a complete block */ erase_count = (uoff_t)(statb.st_size + mtd.erasesize - 1) / mtd.erasesize; /* erase 1 block at a time to be able to give verbose output */ e.length = mtd.erasesize; #if 0 /* (1) bloat * (2) will it work for multi-gigabyte devices? * (3) worse wrt error detection granularity */ /* optimization: if not verbose, erase in one go */ if (!opts) { // if (!(opts & OPT_v)) e.length = mtd.erasesize * erase_count; erase_count = 1; } #endif e.start = 0; for (i = 1; i <= erase_count; i++) { progress(-1, i, erase_count); #if !MTD_DEBUG if (ioctl(fd_d, MEMERASE, &e) < 0) { bb_perror_msg_and_die("erase error at 0x%llx on %s", (long long)e.start, devicename); } #else usleep(100*1000); #endif e.start += mtd.erasesize; } progress_newline(); /* doing this outer loop gives significantly smaller code * than doing two separate loops for writing and verifying */ for (i = 0; i <= 1; i++) { uoff_t done; unsigned count; xlseek(fd_f, 0, SEEK_SET); xlseek(fd_d, 0, SEEK_SET); done = 0; count = BUFSIZE; while (1) { uoff_t rem; progress(i, done / 1024, (uoff_t)statb.st_size / 1024); rem = statb.st_size - done; if (rem == 0) break; if (rem < BUFSIZE) count = rem; xread(fd_f, buf, count); if (i == 0) { int ret; if (count < BUFSIZE) memset((char*)buf + count, 0, BUFSIZE - count); errno = 0; ret = full_write(fd_d, buf, BUFSIZE); if (ret != BUFSIZE) { bb_perror_msg_and_die("write error at 0x%"OFF_FMT"x on %s, " "write returned %d", done, devicename, ret); } } else { /* i == 1 */ xread(fd_d, buf2, count); if (memcmp(buf, buf2, count) != 0) { bb_error_msg_and_die("verification mismatch at 0x%"OFF_FMT"x", done); } } done += count; } progress_newline(); } /* we won't come here if there was an error */ return EXIT_SUCCESS; } busybox-1.22.1/miscutils/chat.c0000644000000000000000000002603412263563520015076 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * bare bones chat utility * inspired by ppp's chat * * Copyright (C) 2008 by Vladimir Dronnikov * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define chat_trivial_usage //usage: "EXPECT [SEND [EXPECT [SEND...]]]" //usage:#define chat_full_usage "\n\n" //usage: "Useful for interacting with a modem connected to stdin/stdout.\n" //usage: "A script consists of one or more \"expect-send\" pairs of strings,\n" //usage: "each pair is a pair of arguments. Example:\n" //usage: "chat '' ATZ OK ATD123456 CONNECT '' ogin: pppuser word: ppppass '~'" #include "libbb.h" // default timeout: 45 sec #define DEFAULT_CHAT_TIMEOUT 45*1000 // max length of "abort string", // i.e. device reply which causes termination #define MAX_ABORT_LEN 50 // possible exit codes enum { ERR_OK = 0, // all's well ERR_MEM, // read too much while expecting ERR_IO, // signalled or I/O error ERR_TIMEOUT, // timed out while expecting ERR_ABORT, // first abort condition was met // ERR_ABORT2, // second abort condition was met // ... }; // exit code #define exitcode bb_got_signal // trap for critical signals static void signal_handler(UNUSED_PARAM int signo) { // report I/O error condition exitcode = ERR_IO; } #if !ENABLE_FEATURE_CHAT_IMPLICIT_CR #define unescape(s, nocr) unescape(s) #endif static size_t unescape(char *s, int *nocr) { char *start = s; char *p = s; while (*s) { char c = *s; // do we need special processing? // standard escapes + \s for space and \N for \0 // \c inhibits terminating \r for commands and is noop for expects if ('\\' == c) { c = *++s; if (c) { #if ENABLE_FEATURE_CHAT_IMPLICIT_CR if ('c' == c) { *nocr = 1; goto next; } #endif if ('N' == c) { c = '\0'; } else if ('s' == c) { c = ' '; #if ENABLE_FEATURE_CHAT_NOFAIL // unescape leading dash only // TODO: and only for expect, not command string } else if ('-' == c && (start + 1 == s)) { //c = '-'; #endif } else { c = bb_process_escape_sequence((const char **)&s); s--; } } // ^A becomes \001, ^B -- \002 and so on... } else if ('^' == c) { c = *++s-'@'; } // put unescaped char *p++ = c; #if ENABLE_FEATURE_CHAT_IMPLICIT_CR next: #endif // next char s++; } *p = '\0'; return p - start; } int chat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int chat_main(int argc UNUSED_PARAM, char **argv) { int record_fd = -1; bool echo = 0; // collection of device replies which cause unconditional termination llist_t *aborts = NULL; // inactivity period int timeout = DEFAULT_CHAT_TIMEOUT; // maximum length of abort string #if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN size_t max_abort_len = 0; #else #define max_abort_len MAX_ABORT_LEN #endif #if ENABLE_FEATURE_CHAT_TTY_HIFI struct termios tio0, tio; #endif // directive names enum { DIR_HANGUP = 0, DIR_ABORT, #if ENABLE_FEATURE_CHAT_CLR_ABORT DIR_CLR_ABORT, #endif DIR_TIMEOUT, DIR_ECHO, DIR_SAY, DIR_RECORD, }; // make x* functions fail with correct exitcode xfunc_error_retval = ERR_IO; // trap vanilla signals to prevent process from being killed suddenly bb_signals(0 + (1 << SIGHUP) + (1 << SIGINT) + (1 << SIGTERM) + (1 << SIGPIPE) , signal_handler); #if ENABLE_FEATURE_CHAT_TTY_HIFI tcgetattr(STDIN_FILENO, &tio); tio0 = tio; cfmakeraw(&tio); tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio); #endif #if ENABLE_FEATURE_CHAT_SWALLOW_OPTS getopt32(argv, "vVsSE"); argv += optind; #else argv++; // goto first arg #endif // handle chat expect-send pairs while (*argv) { // directive given? process it int key = index_in_strings( "HANGUP\0" "ABORT\0" #if ENABLE_FEATURE_CHAT_CLR_ABORT "CLR_ABORT\0" #endif "TIMEOUT\0" "ECHO\0" "SAY\0" "RECORD\0" , *argv ); if (key >= 0) { // cache directive value char *arg = *++argv; // OFF -> 0, anything else -> 1 bool onoff = (0 != strcmp("OFF", arg)); // process directive if (DIR_HANGUP == key) { // turn SIGHUP on/off signal(SIGHUP, onoff ? signal_handler : SIG_IGN); } else if (DIR_ABORT == key) { // append the string to abort conditions #if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN size_t len = strlen(arg); if (len > max_abort_len) max_abort_len = len; #endif llist_add_to_end(&aborts, arg); #if ENABLE_FEATURE_CHAT_CLR_ABORT } else if (DIR_CLR_ABORT == key) { llist_t *l; // remove the string from abort conditions // N.B. gotta refresh maximum length too... # if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN max_abort_len = 0; # endif for (l = aborts; l; l = l->link) { # if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN size_t len = strlen(l->data); # endif if (strcmp(arg, l->data) == 0) { llist_unlink(&aborts, l); continue; } # if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN if (len > max_abort_len) max_abort_len = len; # endif } #endif } else if (DIR_TIMEOUT == key) { // set new timeout // -1 means OFF timeout = atoi(arg) * 1000; // 0 means default // >0 means value in msecs if (!timeout) timeout = DEFAULT_CHAT_TIMEOUT; } else if (DIR_ECHO == key) { // turn echo on/off // N.B. echo means dumping device input/output to stderr echo = onoff; } else if (DIR_RECORD == key) { // turn record on/off // N.B. record means dumping device input to a file // close previous record_fd if (record_fd > 0) close(record_fd); // N.B. do we have to die here on open error? record_fd = (onoff) ? xopen(arg, O_WRONLY|O_CREAT|O_TRUNC) : -1; } else if (DIR_SAY == key) { // just print argument verbatim // TODO: should we use full_write() to avoid unistd/stdio conflict? bb_error_msg("%s", arg); } // next, please! argv++; // ordinary expect-send pair! } else { //----------------------- // do expect //----------------------- int expect_len; size_t buf_len = 0; size_t max_len = max_abort_len; struct pollfd pfd; #if ENABLE_FEATURE_CHAT_NOFAIL int nofail = 0; #endif char *expect = *argv++; // sanity check: shall we really expect something? if (!expect) goto expect_done; #if ENABLE_FEATURE_CHAT_NOFAIL // if expect starts with - if ('-' == *expect) { // swallow - expect++; // and enter nofail mode nofail++; } #endif #ifdef ___TEST___BUF___ // test behaviour with a small buffer # undef COMMON_BUFSIZE # define COMMON_BUFSIZE 6 #endif // expand escape sequences in expect expect_len = unescape(expect, &expect_len /*dummy*/); if (expect_len > max_len) max_len = expect_len; // sanity check: // we should expect more than nothing but not more than input buffer // TODO: later we'll get rid of fixed-size buffer if (!expect_len) goto expect_done; if (max_len >= COMMON_BUFSIZE) { exitcode = ERR_MEM; goto expect_done; } // get reply pfd.fd = STDIN_FILENO; pfd.events = POLLIN; while (!exitcode && poll(&pfd, 1, timeout) > 0 && (pfd.revents & POLLIN) ) { #define buf bb_common_bufsiz1 llist_t *l; ssize_t delta; // read next char from device if (safe_read(STDIN_FILENO, buf+buf_len, 1) > 0) { // dump device input if RECORD fname if (record_fd > 0) { full_write(record_fd, buf+buf_len, 1); } // dump device input if ECHO ON if (echo) { // if (buf[buf_len] < ' ') { // full_write(STDERR_FILENO, "^", 1); // buf[buf_len] += '@'; // } full_write(STDERR_FILENO, buf+buf_len, 1); } buf_len++; // move input frame if we've reached higher bound if (buf_len > COMMON_BUFSIZE) { memmove(buf, buf+buf_len-max_len, max_len); buf_len = max_len; } } // N.B. rule of thumb: values being looked for can // be found only at the end of input buffer // this allows to get rid of strstr() and memmem() // TODO: make expect and abort strings processed uniformly // abort condition is met? -> bail out for (l = aborts, exitcode = ERR_ABORT; l; l = l->link, ++exitcode) { size_t len = strlen(l->data); delta = buf_len-len; if (delta >= 0 && !memcmp(buf+delta, l->data, len)) goto expect_done; } exitcode = ERR_OK; // expected reply received? -> goto next command delta = buf_len - expect_len; if (delta >= 0 && !memcmp(buf+delta, expect, expect_len)) goto expect_done; #undef buf } /* while (have data) */ // device timed out or unexpected reply received exitcode = ERR_TIMEOUT; expect_done: #if ENABLE_FEATURE_CHAT_NOFAIL // on success and when in nofail mode // we should skip following subsend-subexpect pairs if (nofail) { if (!exitcode) { // find last send before non-dashed expect while (*argv && argv[1] && '-' == argv[1][0]) argv += 2; // skip the pair // N.B. do we really need this?! if (!*argv++ || !*argv++) break; } // nofail mode also clears all but IO errors (or signals) if (ERR_IO != exitcode) exitcode = ERR_OK; } #endif // bail out unless we expected successfully if (exitcode) break; //----------------------- // do send //----------------------- if (*argv) { #if ENABLE_FEATURE_CHAT_IMPLICIT_CR int nocr = 0; // inhibit terminating command with \r #endif char *loaded = NULL; // loaded command size_t len; char *buf = *argv++; // if command starts with @ // load "real" command from file named after @ if ('@' == *buf) { // skip the @ and any following white-space trim(++buf); buf = loaded = xmalloc_xopen_read_close(buf, NULL); } // expand escape sequences in command len = unescape(buf, &nocr); // send command alarm(timeout); pfd.fd = STDOUT_FILENO; pfd.events = POLLOUT; while (len && !exitcode && poll(&pfd, 1, -1) > 0 && (pfd.revents & POLLOUT) ) { #if ENABLE_FEATURE_CHAT_SEND_ESCAPES // "\\d" means 1 sec delay, "\\p" means 0.01 sec delay // "\\K" means send BREAK char c = *buf; if ('\\' == c) { c = *++buf; if ('d' == c) { sleep(1); len--; continue; } if ('p' == c) { usleep(10000); len--; continue; } if ('K' == c) { tcsendbreak(STDOUT_FILENO, 0); len--; continue; } buf--; } if (safe_write(STDOUT_FILENO, buf, 1) != 1) break; len--; buf++; #else len -= full_write(STDOUT_FILENO, buf, len); #endif } /* while (can write) */ alarm(0); // report I/O error if there still exists at least one non-sent char if (len) exitcode = ERR_IO; // free loaded command (if any) if (loaded) free(loaded); #if ENABLE_FEATURE_CHAT_IMPLICIT_CR // or terminate command with \r (if not inhibited) else if (!nocr) xwrite(STDOUT_FILENO, "\r", 1); #endif // bail out unless we sent command successfully if (exitcode) break; } /* if (*argv) */ } } /* while (*argv) */ #if ENABLE_FEATURE_CHAT_TTY_HIFI tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0); #endif return exitcode; } busybox-1.22.1/miscutils/flash_eraseall.c0000644000000000000000000001246112263563520017123 0ustar rootroot/* vi: set sw=4 ts=4: */ /* eraseall.c -- erase the whole of a MTD device * * Ported to busybox from mtd-utils. * * Copyright (C) 2000 Arcom Control System Ltd * * Renamed to flash_eraseall.c * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define flash_eraseall_trivial_usage //usage: "[-jNq] MTD_DEVICE" //usage:#define flash_eraseall_full_usage "\n\n" //usage: "Erase an MTD device\n" //usage: "\n -j Format the device for jffs2" //usage: "\n -N Don't skip bad blocks" //usage: "\n -q Don't display progress messages" #include "libbb.h" #include #include #define OPTION_J (1 << 0) #define OPTION_N (1 << 1) #define OPTION_Q (1 << 2) #define IS_NAND (1 << 3) /* mtd/jffs2-user.h used to have this atrocity: extern int target_endian; #define t16(x) ({ __u16 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); }) #define t32(x) ({ __u32 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); }) #define cpu_to_je16(x) ((jint16_t){t16(x)}) #define cpu_to_je32(x) ((jint32_t){t32(x)}) #define cpu_to_jemode(x) ((jmode_t){t32(x)}) #define je16_to_cpu(x) (t16((x).v16)) #define je32_to_cpu(x) (t32((x).v32)) #define jemode_to_cpu(x) (t32((x).m)) but mtd/jffs2-user.h is gone now (at least 2.6.31.6 does not have it anymore) */ /* We always use native endianness */ #undef cpu_to_je16 #undef cpu_to_je32 #define cpu_to_je16(v) ((jint16_t){(v)}) #define cpu_to_je32(v) ((jint32_t){(v)}) static void show_progress(mtd_info_t *meminfo, erase_info_t *erase) { printf("\rErasing %u Kibyte @ %x - %2u%% complete.", (unsigned)meminfo->erasesize / 1024, erase->start, (unsigned) ((unsigned long long) erase->start * 100 / meminfo->size) ); fflush_all(); } int flash_eraseall_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int flash_eraseall_main(int argc UNUSED_PARAM, char **argv) { struct jffs2_unknown_node cleanmarker; mtd_info_t meminfo; int fd, clmpos, clmlen; erase_info_t erase; struct stat st; unsigned int flags; char *mtd_name; opt_complementary = "=1"; flags = getopt32(argv, "jNq"); mtd_name = argv[optind]; fd = xopen(mtd_name, O_RDWR); fstat(fd, &st); if (!S_ISCHR(st.st_mode)) bb_error_msg_and_die("%s: not a char device", mtd_name); xioctl(fd, MEMGETINFO, &meminfo); erase.length = meminfo.erasesize; if (meminfo.type == MTD_NANDFLASH) flags |= IS_NAND; clmpos = 0; clmlen = 8; if (flags & OPTION_J) { uint32_t *crc32_table; crc32_table = crc32_filltable(NULL, 0); cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); if (!(flags & IS_NAND)) cleanmarker.totlen = cpu_to_je32(sizeof(struct jffs2_unknown_node)); else { struct nand_oobinfo oobinfo; xioctl(fd, MEMGETOOBSEL, &oobinfo); /* Check for autoplacement */ if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) { /* Get the position of the free bytes */ clmpos = oobinfo.oobfree[0][0]; clmlen = oobinfo.oobfree[0][1]; if (clmlen > 8) clmlen = 8; if (clmlen == 0) bb_error_msg_and_die("autoplacement selected and no empty space in oob"); } else { /* Legacy mode */ switch (meminfo.oobsize) { case 8: clmpos = 6; clmlen = 2; break; case 16: clmpos = 8; /*clmlen = 8;*/ break; case 64: clmpos = 16; /*clmlen = 8;*/ break; } } cleanmarker.totlen = cpu_to_je32(8); } cleanmarker.hdr_crc = cpu_to_je32( crc32_block_endian0(0, &cleanmarker, sizeof(struct jffs2_unknown_node) - 4, crc32_table) ); } /* Don't want to destroy progress indicator by bb_error_msg's */ applet_name = xasprintf("\n%s: %s", applet_name, mtd_name); for (erase.start = 0; erase.start < meminfo.size; erase.start += meminfo.erasesize) { if (!(flags & OPTION_N)) { int ret; loff_t offset = erase.start; ret = ioctl(fd, MEMGETBADBLOCK, &offset); if (ret > 0) { if (!(flags & OPTION_Q)) bb_info_msg("\nSkipping bad block at 0x%08x", erase.start); continue; } if (ret < 0) { /* Black block table is not available on certain flash * types e.g. NOR */ if (errno == EOPNOTSUPP) { flags |= OPTION_N; if (flags & IS_NAND) bb_error_msg_and_die("bad block check not available"); } else { bb_perror_msg_and_die("MEMGETBADBLOCK error"); } } } if (!(flags & OPTION_Q)) show_progress(&meminfo, &erase); xioctl(fd, MEMERASE, &erase); /* format for JFFS2 ? */ if (!(flags & OPTION_J)) continue; /* write cleanmarker */ if (flags & IS_NAND) { struct mtd_oob_buf oob; oob.ptr = (unsigned char *) &cleanmarker; oob.start = erase.start + clmpos; oob.length = clmlen; xioctl(fd, MEMWRITEOOB, &oob); } else { xlseek(fd, erase.start, SEEK_SET); /* if (lseek(fd, erase.start, SEEK_SET) < 0) { bb_perror_msg("MTD %s failure", "seek"); continue; } */ xwrite(fd, &cleanmarker, sizeof(cleanmarker)); /* if (write(fd, &cleanmarker, sizeof(cleanmarker)) != sizeof(cleanmarker)) { bb_perror_msg("MTD %s failure", "write"); continue; } */ } if (!(flags & OPTION_Q)) printf(" Cleanmarker written at %x.", erase.start); } if (!(flags & OPTION_Q)) { show_progress(&meminfo, &erase); bb_putchar('\n'); } if (ENABLE_FEATURE_CLEAN_UP) close(fd); return EXIT_SUCCESS; } busybox-1.22.1/coreutils/0000755000000000000000000000000012320365363014002 5ustar rootrootbusybox-1.22.1/coreutils/split.c0000644000000000000000000000674112263563520015312 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * split - split a file into pieces * Copyright (c) 2007 Bernhard Reutner-Fischer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT: SUSv3 compliant * SUSv3 requirements: * http://www.opengroup.org/onlinepubs/009695399/utilities/split.html */ //usage:#define split_trivial_usage //usage: "[OPTIONS] [INPUT [PREFIX]]" //usage:#define split_full_usage "\n\n" //usage: " -b N[k|m] Split by N (kilo|mega)bytes" //usage: "\n -l N Split by N lines" //usage: "\n -a N Use N letters as suffix" //usage: //usage:#define split_example_usage //usage: "$ split TODO foo\n" //usage: "$ cat TODO | split -a 2 -l 2 TODO_\n" #include "libbb.h" #if ENABLE_FEATURE_SPLIT_FANCY static const struct suffix_mult split_suffixes[] = { { "b", 512 }, { "k", 1024 }, { "m", 1024*1024 }, { "g", 1024*1024*1024 }, { "", 0 } }; #endif /* Increment the suffix part of the filename. * Returns NULL if we are out of filenames. */ static char *next_file(char *old, unsigned suffix_len) { size_t end = strlen(old); unsigned i = 1; char *curr; while (1) { curr = old + end - i; if (*curr < 'z') { *curr += 1; break; } i++; if (i > suffix_len) { return NULL; } *curr = 'a'; } return old; } #define read_buffer bb_common_bufsiz1 enum { READ_BUFFER_SIZE = COMMON_BUFSIZE - 1 }; #define SPLIT_OPT_l (1<<0) #define SPLIT_OPT_b (1<<1) #define SPLIT_OPT_a (1<<2) int split_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int split_main(int argc UNUSED_PARAM, char **argv) { unsigned suffix_len = 2; char *pfx; char *count_p; const char *sfx; off_t cnt = 1000; off_t remaining = 0; unsigned opt; ssize_t bytes_read, to_write; char *src; opt_complementary = "?2:a+"; /* max 2 args; -a N */ opt = getopt32(argv, "l:b:a:", &count_p, &count_p, &suffix_len); if (opt & SPLIT_OPT_l) cnt = XATOOFF(count_p); if (opt & SPLIT_OPT_b) // FIXME: also needs XATOOFF cnt = xatoull_sfx(count_p, IF_FEATURE_SPLIT_FANCY(split_suffixes) IF_NOT_FEATURE_SPLIT_FANCY(km_suffixes) ); sfx = "x"; argv += optind; if (argv[0]) { int fd; if (argv[1]) sfx = argv[1]; fd = xopen_stdin(argv[0]); xmove_fd(fd, STDIN_FILENO); } else { argv[0] = (char *) bb_msg_standard_input; } if (NAME_MAX < strlen(sfx) + suffix_len) bb_error_msg_and_die("suffix too long"); { char *char_p = xzalloc(suffix_len + 1); memset(char_p, 'a', suffix_len); pfx = xasprintf("%s%s", sfx, char_p); if (ENABLE_FEATURE_CLEAN_UP) free(char_p); } while (1) { bytes_read = safe_read(STDIN_FILENO, read_buffer, READ_BUFFER_SIZE); if (!bytes_read) break; if (bytes_read < 0) bb_simple_perror_msg_and_die(argv[0]); src = read_buffer; do { if (!remaining) { if (!pfx) bb_error_msg_and_die("suffixes exhausted"); xmove_fd(xopen(pfx, O_WRONLY | O_CREAT | O_TRUNC), 1); pfx = next_file(pfx, suffix_len); remaining = cnt; } if (opt & SPLIT_OPT_b) { /* split by bytes */ to_write = (bytes_read < remaining) ? bytes_read : remaining; remaining -= to_write; } else { /* split by lines */ /* can be sped up by using _memrchr_ * and writing many lines at once... */ char *end = memchr(src, '\n', bytes_read); if (end) { --remaining; to_write = end - src + 1; } else { to_write = bytes_read; } } xwrite(STDOUT_FILENO, src, to_write); bytes_read -= to_write; src += to_write; } while (bytes_read); } return EXIT_SUCCESS; } busybox-1.22.1/coreutils/sleep.c0000644000000000000000000000573712263563520015273 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * sleep implementation for busybox * * Copyright (C) 2003 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* BB_AUDIT GNU issues -- fancy version matches except args must be ints. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/sleep.html */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Rewritten to do proper arg and error checking. * Also, added a 'fancy' configuration to accept multiple args with * time suffixes for seconds, minutes, hours, and days. */ //usage:#define sleep_trivial_usage //usage: IF_FEATURE_FANCY_SLEEP("[") "N" IF_FEATURE_FANCY_SLEEP("]...") //usage:#define sleep_full_usage "\n\n" //usage: IF_NOT_FEATURE_FANCY_SLEEP("Pause for N seconds") //usage: IF_FEATURE_FANCY_SLEEP( //usage: "Pause for a time equal to the total of the args given, where each arg can\n" //usage: "have an optional suffix of (s)econds, (m)inutes, (h)ours, or (d)ays") //usage: //usage:#define sleep_example_usage //usage: "$ sleep 2\n" //usage: "[2 second delay results]\n" //usage: IF_FEATURE_FANCY_SLEEP( //usage: "$ sleep 1d 3h 22m 8s\n" //usage: "[98528 second delay results]\n") #include "libbb.h" /* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells */ #if ENABLE_FEATURE_FANCY_SLEEP || ENABLE_FEATURE_FLOAT_SLEEP static const struct suffix_mult sfx[] = { { "s", 1 }, { "m", 60 }, { "h", 60*60 }, { "d", 24*60*60 }, { "", 0 } }; #endif int sleep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int sleep_main(int argc UNUSED_PARAM, char **argv) { #if ENABLE_FEATURE_FLOAT_SLEEP double duration; struct timespec ts; #else unsigned duration; #endif ++argv; if (!*argv) bb_show_usage(); #if ENABLE_FEATURE_FLOAT_SLEEP # if ENABLE_LOCALE_SUPPORT /* undo busybox.c setlocale */ setlocale(LC_NUMERIC, "C"); # endif duration = 0; do { char *arg = *argv; if (strchr(arg, '.')) { double d; char *pp; int len = strspn(arg, "0123456789."); char sv = arg[len]; arg[len] = '\0'; errno = 0; d = strtod(arg, &pp); if (errno || *pp) bb_show_usage(); arg += len; *arg-- = sv; sv = *arg; *arg = '1'; duration += d * xatoul_sfx(arg, sfx); *arg = sv; } else { duration += xatoul_sfx(arg, sfx); } } while (*++argv); ts.tv_sec = MAXINT(typeof(ts.tv_sec)); ts.tv_nsec = 0; if (duration >= 0 && duration < ts.tv_sec) { ts.tv_sec = duration; ts.tv_nsec = (duration - ts.tv_sec) * 1000000000; } do { errno = 0; nanosleep(&ts, &ts); } while (errno == EINTR); #elif ENABLE_FEATURE_FANCY_SLEEP duration = 0; do { duration += xatou_range_sfx(*argv, 0, UINT_MAX - duration, sfx); } while (*++argv); sleep(duration); #else /* simple */ duration = xatou(*argv); sleep(duration); // Off. If it's really needed, provide example why //if (sleep(duration)) { // bb_perror_nomsg_and_die(); //} #endif return EXIT_SUCCESS; } busybox-1.22.1/coreutils/fsync.c0000644000000000000000000000213112263563520015266 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini fsync implementation for busybox * * Copyright (C) 2008 Nokia Corporation. All rights reserved. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define fsync_trivial_usage //usage: "[-d] FILE..." //usage:#define fsync_full_usage "\n\n" //usage: "Write files' buffered blocks to disk\n" //usage: "\n -d Avoid syncing metadata" #include "libbb.h" #ifndef O_NOATIME # define O_NOATIME 0 #endif /* This is a NOFORK applet. Be very careful! */ int fsync_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fsync_main(int argc UNUSED_PARAM, char **argv) { int status; int opts; opts = getopt32(argv, "d"); /* fdatasync */ argv += optind; if (!*argv) { bb_show_usage(); } status = EXIT_SUCCESS; do { int fd = open_or_warn(*argv, O_NOATIME | O_NOCTTY | O_RDONLY); if (fd == -1) { status = EXIT_FAILURE; continue; } if ((opts ? fdatasync(fd) : fsync(fd))) { //status = EXIT_FAILURE; - do we want this? bb_simple_perror_msg(*argv); } close(fd); } while (*++argv); return status; } busybox-1.22.1/coreutils/uniq.c0000644000000000000000000000615512263563520015132 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * uniq implementation for busybox * * Copyright (C) 2005 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/uniq.html */ //usage:#define uniq_trivial_usage //usage: "[-cdu][-f,s,w N] [INPUT [OUTPUT]]" //usage:#define uniq_full_usage "\n\n" //usage: "Discard duplicate lines\n" //usage: "\n -c Prefix lines by the number of occurrences" //usage: "\n -d Only print duplicate lines" //usage: "\n -u Only print unique lines" //usage: "\n -f N Skip first N fields" //usage: "\n -s N Skip first N chars (after any skipped fields)" //usage: "\n -w N Compare N characters in line" //usage: //usage:#define uniq_example_usage //usage: "$ echo -e \"a\\na\\nb\\nc\\nc\\na\" | sort | uniq\n" //usage: "a\n" //usage: "b\n" //usage: "c\n" #include "libbb.h" int uniq_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int uniq_main(int argc UNUSED_PARAM, char **argv) { const char *input_filename; unsigned skip_fields, skip_chars, max_chars; unsigned opt; char *cur_line; const char *cur_compare; enum { OPT_c = 0x1, OPT_d = 0x2, /* print only dups */ OPT_u = 0x4, /* print only uniq */ OPT_f = 0x8, OPT_s = 0x10, OPT_w = 0x20, }; skip_fields = skip_chars = 0; max_chars = INT_MAX; opt_complementary = "f+:s+:w+"; opt = getopt32(argv, "cduf:s:w:", &skip_fields, &skip_chars, &max_chars); argv += optind; input_filename = argv[0]; if (input_filename) { const char *output; if (input_filename[0] != '-' || input_filename[1]) { close(STDIN_FILENO); /* == 0 */ xopen(input_filename, O_RDONLY); /* fd will be 0 */ } output = argv[1]; if (output) { if (argv[2]) bb_show_usage(); if (output[0] != '-' || output[1]) { // Won't work with "uniq - FILE" and closed stdin: //close(STDOUT_FILENO); //xopen(output, O_WRONLY | O_CREAT | O_TRUNC); xmove_fd(xopen(output, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO); } } } cur_compare = cur_line = NULL; /* prime the pump */ do { unsigned i; unsigned long dups; char *old_line; const char *old_compare; old_line = cur_line; old_compare = cur_compare; dups = 0; /* gnu uniq ignores newlines */ while ((cur_line = xmalloc_fgetline(stdin)) != NULL) { cur_compare = cur_line; for (i = skip_fields; i; i--) { cur_compare = skip_whitespace(cur_compare); cur_compare = skip_non_whitespace(cur_compare); } for (i = skip_chars; *cur_compare && i; i--) { ++cur_compare; } if (!old_line || strncmp(old_compare, cur_compare, max_chars)) { break; } free(cur_line); ++dups; /* testing for overflow seems excessive */ } if (old_line) { if (!(opt & (OPT_d << !!dups))) { /* (if dups, opt & OPT_u) */ if (opt & OPT_c) { /* %7lu matches GNU coreutils 6.9 */ printf("%7lu ", dups + 1); } printf("%s\n", old_line); } free(old_line); } } while (cur_line); die_if_ferror(stdin, input_filename); fflush_stdout_and_exit(EXIT_SUCCESS); } busybox-1.22.1/coreutils/dirname.c0000644000000000000000000000162212263563520015567 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini dirname implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/dirname.html */ //usage:#define dirname_trivial_usage //usage: "FILENAME" //usage:#define dirname_full_usage "\n\n" //usage: "Strip non-directory suffix from FILENAME" //usage: //usage:#define dirname_example_usage //usage: "$ dirname /tmp/foo\n" //usage: "/tmp\n" //usage: "$ dirname /tmp/foo/\n" //usage: "/tmp\n" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ int dirname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int dirname_main(int argc UNUSED_PARAM, char **argv) { puts(dirname(single_argv(argv))); return fflush_all(); } busybox-1.22.1/coreutils/yes.c0000644000000000000000000000172012263563520014747 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * yes implementation for busybox * * Copyright (C) 2003 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Size reductions and removed redundant applet name prefix from error messages. */ #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ //usage:#define yes_trivial_usage //usage: "[STRING]" //usage:#define yes_full_usage "\n\n" //usage: "Repeatedly output a line with STRING, or 'y'" int yes_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int yes_main(int argc UNUSED_PARAM, char **argv) { char **pp; argv[0] = (char*)"y"; if (argv[1]) ++argv; do { pp = argv; while (1) { fputs(*pp, stdout); if (!*++pp) break; putchar(' '); } } while (putchar('\n') != EOF); bb_perror_nomsg_and_die(); } busybox-1.22.1/coreutils/rm.c0000644000000000000000000000305112263563520014564 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini rm implementation for busybox * * Copyright (C) 2001 Matt Kraai * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/rm.html */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Size reduction. */ //usage:#define rm_trivial_usage //usage: "[-irf] FILE..." //usage:#define rm_full_usage "\n\n" //usage: "Remove (unlink) FILEs\n" //usage: "\n -i Always prompt before removing" //usage: "\n -f Never prompt" //usage: "\n -R,-r Recurse" //usage: //usage:#define rm_example_usage //usage: "$ rm -rf /tmp/foo\n" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ int rm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int rm_main(int argc UNUSED_PARAM, char **argv) { int status = 0; int flags = 0; unsigned opt; opt_complementary = "f-i:i-f"; /* -v (verbose) is ignored */ opt = getopt32(argv, "fiRrv"); argv += optind; if (opt & 1) flags |= FILEUTILS_FORCE; if (opt & 2) flags |= FILEUTILS_INTERACTIVE; if (opt & (8|4)) flags |= FILEUTILS_RECUR; if (*argv != NULL) { do { const char *base = bb_get_last_path_component_strip(*argv); if (DOT_OR_DOTDOT(base)) { bb_error_msg("can't remove '.' or '..'"); } else if (remove_file(*argv, flags) >= 0) { continue; } status = 1; } while (*++argv); } else if (!(flags & FILEUTILS_FORCE)) { bb_show_usage(); } return status; } busybox-1.22.1/coreutils/install.c0000644000000000000000000001414112263563520015616 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright (C) 2003 by Glenn McGrath * SELinux support: by Yuichi Nakamura * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* -v, -b, -c are ignored */ //usage:#define install_trivial_usage //usage: "[-cdDsp] [-o USER] [-g GRP] [-m MODE] [SOURCE]... DEST" //usage:#define install_full_usage "\n\n" //usage: "Copy files and set attributes\n" //usage: "\n -c Just copy (default)" //usage: "\n -d Create directories" //usage: "\n -D Create leading target directories" //usage: "\n -s Strip symbol table" //usage: "\n -p Preserve date" //usage: "\n -o USER Set ownership" //usage: "\n -g GRP Set group ownership" //usage: "\n -m MODE Set permissions" //usage: IF_SELINUX( //usage: "\n -Z Set security context" //usage: ) #include "libbb.h" #include "libcoreutils/coreutils.h" #if ENABLE_FEATURE_INSTALL_LONG_OPTIONS static const char install_longopts[] ALIGN1 = "directory\0" No_argument "d" "preserve-timestamps\0" No_argument "p" "strip\0" No_argument "s" "group\0" Required_argument "g" "mode\0" Required_argument "m" "owner\0" Required_argument "o" /* autofs build insists of using -b --suffix=.orig */ /* TODO? (short option for --suffix is -S) */ #if ENABLE_SELINUX "context\0" Required_argument "Z" "preserve_context\0" No_argument "\xff" "preserve-context\0" No_argument "\xff" #endif ; #endif #if ENABLE_SELINUX static void setdefaultfilecon(const char *path) { struct stat s; security_context_t scontext = NULL; if (!is_selinux_enabled()) { return; } if (lstat(path, &s) != 0) { return; } if (matchpathcon(path, s.st_mode, &scontext) < 0) { goto out; } if (strcmp(scontext, "<>") == 0) { goto out; } if (lsetfilecon(path, scontext) < 0) { if (errno != ENOTSUP) { bb_perror_msg("warning: can't change context" " of %s to %s", path, scontext); } } out: freecon(scontext); } #endif int install_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int install_main(int argc, char **argv) { struct stat statbuf; mode_t mode; uid_t uid; gid_t gid; char *arg, *last; const char *gid_str; const char *uid_str; const char *mode_str; int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE; int opts; int min_args = 1; int ret = EXIT_SUCCESS; int isdir = 0; #if ENABLE_SELINUX security_context_t scontext; bool use_default_selinux_context = 1; #endif enum { OPT_c = 1 << 0, OPT_v = 1 << 1, OPT_b = 1 << 2, OPT_MKDIR_LEADING = 1 << 3, OPT_DIRECTORY = 1 << 4, OPT_PRESERVE_TIME = 1 << 5, OPT_STRIP = 1 << 6, OPT_GROUP = 1 << 7, OPT_MODE = 1 << 8, OPT_OWNER = 1 << 9, #if ENABLE_SELINUX OPT_SET_SECURITY_CONTEXT = 1 << 10, OPT_PRESERVE_SECURITY_CONTEXT = 1 << 11, #endif }; #if ENABLE_FEATURE_INSTALL_LONG_OPTIONS applet_long_options = install_longopts; #endif opt_complementary = "s--d:d--s" IF_FEATURE_INSTALL_LONG_OPTIONS(IF_SELINUX(":Z--\xff:\xff--Z")); /* -c exists for backwards compatibility, it's needed */ /* -v is ignored ("print name of each created directory") */ /* -b is ignored ("make a backup of each existing destination file") */ opts = getopt32(argv, "cvb" "Ddpsg:m:o:" IF_SELINUX("Z:"), &gid_str, &mode_str, &uid_str IF_SELINUX(, &scontext)); argc -= optind; argv += optind; #if ENABLE_SELINUX if (opts & (OPT_PRESERVE_SECURITY_CONTEXT|OPT_SET_SECURITY_CONTEXT)) { selinux_or_die(); use_default_selinux_context = 0; if (opts & OPT_PRESERVE_SECURITY_CONTEXT) { copy_flags |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; } if (opts & OPT_SET_SECURITY_CONTEXT) { setfscreatecon_or_die(scontext); copy_flags |= FILEUTILS_SET_SECURITY_CONTEXT; } } #endif /* preserve access and modification time, this is GNU behaviour, * BSD only preserves modification time */ if (opts & OPT_PRESERVE_TIME) { copy_flags |= FILEUTILS_PRESERVE_STATUS; } mode = 0755; /* GNU coreutils 6.10 compat */ if (opts & OPT_MODE) bb_parse_mode(mode_str, &mode); uid = (opts & OPT_OWNER) ? get_ug_id(uid_str, xuname2uid) : getuid(); gid = (opts & OPT_GROUP) ? get_ug_id(gid_str, xgroup2gid) : getgid(); last = argv[argc - 1]; if (!(opts & OPT_DIRECTORY)) { argv[argc - 1] = NULL; min_args++; /* coreutils install resolves link in this case, don't use lstat */ isdir = stat(last, &statbuf) < 0 ? 0 : S_ISDIR(statbuf.st_mode); } if (argc < min_args) bb_show_usage(); while ((arg = *argv++) != NULL) { char *dest = last; if (opts & OPT_DIRECTORY) { dest = arg; /* GNU coreutils 6.9 does not set uid:gid * on intermediate created directories * (only on last one) */ if (bb_make_directory(dest, 0755, FILEUTILS_RECUR)) { ret = EXIT_FAILURE; goto next; } } else { if (opts & OPT_MKDIR_LEADING) { char *ddir = xstrdup(dest); bb_make_directory(dirname(ddir), 0755, FILEUTILS_RECUR); /* errors are not checked. copy_file * will fail if dir is not created. */ free(ddir); } if (isdir) dest = concat_path_file(last, bb_basename(arg)); if (copy_file(arg, dest, copy_flags) != 0) { /* copy is not made */ ret = EXIT_FAILURE; goto next; } if (opts & OPT_STRIP) { char *args[4]; args[0] = (char*)"strip"; args[1] = (char*)"-p"; /* -p --preserve-dates */ args[2] = dest; args[3] = NULL; if (spawn_and_wait(args)) { bb_perror_msg("strip"); ret = EXIT_FAILURE; } } } /* Set the file mode (always, not only with -m). * GNU coreutils 6.10 is not affected by umask. */ if (chmod(dest, mode) == -1) { bb_perror_msg("can't change %s of %s", "permissions", dest); ret = EXIT_FAILURE; } #if ENABLE_SELINUX if (use_default_selinux_context) setdefaultfilecon(dest); #endif /* Set the user and group id */ if ((opts & (OPT_OWNER|OPT_GROUP)) && lchown(dest, uid, gid) == -1 ) { bb_perror_msg("can't change %s of %s", "ownership", dest); ret = EXIT_FAILURE; } next: if (ENABLE_FEATURE_CLEAN_UP && isdir) free(dest); } return ret; } busybox-1.22.1/coreutils/printf.c0000644000000000000000000002442712263563520015462 0ustar rootroot/* vi: set sw=4 ts=4: */ /* printf - format and print data Copyright 1999 Dave Cinege Portions copyright (C) 1990-1996 Free Software Foundation, Inc. Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* Usage: printf format [argument...] A front end to the printf function that lets it be used from the shell. Backslash escapes: \" = double quote \\ = backslash \a = alert (bell) \b = backspace \c = produce no further output \f = form feed \n = new line \r = carriage return \t = horizontal tab \v = vertical tab \0ooo = octal number (ooo is 0 to 3 digits) \xhhh = hexadecimal number (hhh is 1 to 3 digits) Additional directive: %b = print an argument string, interpreting backslash escapes The 'format' argument is re-used as many times as necessary to convert all of the given arguments. David MacKenzie */ /* 19990508 Busy Boxed! Dave Cinege */ //usage:#define printf_trivial_usage //usage: "FORMAT [ARG]..." //usage:#define printf_full_usage "\n\n" //usage: "Format and print ARG(s) according to FORMAT (a-la C printf)" //usage: //usage:#define printf_example_usage //usage: "$ printf \"Val=%d\\n\" 5\n" //usage: "Val=5\n" #include "libbb.h" /* A note on bad input: neither bash 3.2 nor coreutils 6.10 stop on it. * They report it: * bash: printf: XXX: invalid number * printf: XXX: expected a numeric value * bash: printf: 123XXX: invalid number * printf: 123XXX: value not completely converted * but then they use 0 (or partially converted numeric prefix) as a value * and continue. They exit with 1 in this case. * Both accept insane field width/precision (e.g. %9999999999.9999999999d). * Both print error message and assume 0 if %*.*f width/precision is "bad" * (but negative numbers are not "bad"). * Both accept negative numbers for %u specifier. * * We try to be compatible. */ typedef void FAST_FUNC (*converter)(const char *arg, void *result); static int multiconvert(const char *arg, void *result, converter convert) { if (*arg == '"' || *arg == '\'') { arg = utoa((unsigned char)arg[1]); } errno = 0; convert(arg, result); if (errno) { bb_error_msg("invalid number '%s'", arg); return 1; } return 0; } static void FAST_FUNC conv_strtoull(const char *arg, void *result) { *(unsigned long long*)result = bb_strtoull(arg, NULL, 0); /* both coreutils 6.10 and bash 3.2: * $ printf '%x\n' -2 * fffffffffffffffe * Mimic that: */ if (errno) { *(unsigned long long*)result = bb_strtoll(arg, NULL, 0); } } static void FAST_FUNC conv_strtoll(const char *arg, void *result) { *(long long*)result = bb_strtoll(arg, NULL, 0); } static void FAST_FUNC conv_strtod(const char *arg, void *result) { char *end; /* Well, this one allows leading whitespace... so what? */ /* What I like much less is that "-" accepted too! :( */ *(double*)result = strtod(arg, &end); if (end[0]) { errno = ERANGE; *(double*)result = 0; } } /* Callers should check errno to detect errors */ static unsigned long long my_xstrtoull(const char *arg) { unsigned long long result; if (multiconvert(arg, &result, conv_strtoull)) result = 0; return result; } static long long my_xstrtoll(const char *arg) { long long result; if (multiconvert(arg, &result, conv_strtoll)) result = 0; return result; } static double my_xstrtod(const char *arg) { double result; multiconvert(arg, &result, conv_strtod); return result; } /* Handles %b */ static void print_esc_string(const char *str) { char c; while ((c = *str) != '\0') { str++; if (c == '\\') { /* %b also accepts 4-digit octals of the form \0### */ if (*str == '0') { if ((unsigned char)(str[1] - '0') < 8) { /* 2nd char is 0..7: skip leading '0' */ str++; } } { /* optimization: don't force arg to be on-stack, * use another variable for that. */ const char *z = str; c = bb_process_escape_sequence(&z); str = z; } } putchar(c); } } static void print_direc(char *format, unsigned fmt_length, int field_width, int precision, const char *argument) { long long llv; double dv; char saved; char *have_prec, *have_width; saved = format[fmt_length]; format[fmt_length] = '\0'; have_prec = strstr(format, ".*"); have_width = strchr(format, '*'); if (have_width - 1 == have_prec) have_width = NULL; errno = 0; switch (format[fmt_length - 1]) { case 'c': printf(format, *argument); break; case 'd': case 'i': llv = my_xstrtoll(argument); print_long: if (!have_width) { if (!have_prec) printf(format, llv); else printf(format, precision, llv); } else { if (!have_prec) printf(format, field_width, llv); else printf(format, field_width, precision, llv); } break; case 'o': case 'u': case 'x': case 'X': llv = my_xstrtoull(argument); /* cheat: unsigned long and long have same width, so... */ goto print_long; case 's': /* Are char* and long long the same? */ if (sizeof(argument) == sizeof(llv)) { llv = (long long)(ptrdiff_t)argument; goto print_long; } else { /* Hope compiler will optimize it out by moving call * instruction after the ifs... */ if (!have_width) { if (!have_prec) printf(format, argument, /*unused:*/ argument, argument); else printf(format, precision, argument, /*unused:*/ argument); } else { if (!have_prec) printf(format, field_width, argument, /*unused:*/ argument); else printf(format, field_width, precision, argument); } break; } case 'f': case 'e': case 'E': case 'g': case 'G': dv = my_xstrtod(argument); if (!have_width) { if (!have_prec) printf(format, dv); else printf(format, precision, dv); } else { if (!have_prec) printf(format, field_width, dv); else printf(format, field_width, precision, dv); } break; } /* switch */ format[fmt_length] = saved; } /* Handle params for "%*.*f". Negative numbers are ok (compat). */ static int get_width_prec(const char *str) { int v = bb_strtoi(str, NULL, 10); if (errno) { bb_error_msg("invalid number '%s'", str); v = 0; } return v; } /* Print the text in FORMAT, using ARGV for arguments to any '%' directives. Return advanced ARGV. */ static char **print_formatted(char *f, char **argv, int *conv_err) { char *direc_start; /* Start of % directive. */ unsigned direc_length; /* Length of % directive. */ int field_width; /* Arg to first '*' */ int precision; /* Arg to second '*' */ char **saved_argv = argv; for (; *f; ++f) { switch (*f) { case '%': direc_start = f++; direc_length = 1; field_width = precision = 0; if (*f == '%') { bb_putchar('%'); break; } if (*f == 'b') { if (*argv) { print_esc_string(*argv); ++argv; } break; } if (strchr("-+ #", *f)) { ++f; ++direc_length; } if (*f == '*') { ++f; ++direc_length; if (*argv) field_width = get_width_prec(*argv++); } else { while (isdigit(*f)) { ++f; ++direc_length; } } if (*f == '.') { ++f; ++direc_length; if (*f == '*') { ++f; ++direc_length; if (*argv) precision = get_width_prec(*argv++); } else { while (isdigit(*f)) { ++f; ++direc_length; } } } /* Remove "lLhz" size modifiers, repeatedly. * bash does not like "%lld", but coreutils * happily takes even "%Llllhhzhhzd"! * We are permissive like coreutils */ while ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') { overlapping_strcpy(f, f + 1); } /* Add "ll" if integer modifier, then print */ { static const char format_chars[] ALIGN1 = "diouxXfeEgGcs"; char *p = strchr(format_chars, *f); /* needed - try "printf %" without it */ if (p == NULL) { bb_error_msg("%s: invalid format", direc_start); /* causes main() to exit with error */ return saved_argv - 1; } ++direc_length; if (p - format_chars <= 5) { /* it is one of "diouxX" */ p = xmalloc(direc_length + 3); memcpy(p, direc_start, direc_length); p[direc_length + 1] = p[direc_length - 1]; p[direc_length - 1] = 'l'; p[direc_length] = 'l'; //bb_error_msg("<%s>", p); direc_length += 2; direc_start = p; } else { p = NULL; } if (*argv) { print_direc(direc_start, direc_length, field_width, precision, *argv++); } else { print_direc(direc_start, direc_length, field_width, precision, ""); } *conv_err |= errno; free(p); } break; case '\\': if (*++f == 'c') { return saved_argv; /* causes main() to exit */ } bb_putchar(bb_process_escape_sequence((const char **)&f)); f--; break; default: putchar(*f); } } return argv; } int printf_main(int argc UNUSED_PARAM, char **argv) { int conv_err; char *format; char **argv2; /* We must check that stdout is not closed. * The reason for this is highly non-obvious. * printf_main is used from shell. * Shell must correctly handle 'printf "%s" foo' * if stdout is closed. With stdio, output gets shoveled into * stdout buffer, and even fflush cannot clear it out. It seems that * even if libc receives EBADF on write attempts, it feels determined * to output data no matter what. So it will try later, * and possibly will clobber future output. Not good. */ // TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR? if (fcntl(1, F_GETFL) == -1) return 1; /* match coreutils 6.10 (sans error msg to stderr) */ //if (dup2(1, 1) != 1) - old way // return 1; /* bash builtin errors out on "printf '-%s-\n' foo", * coreutils-6.9 works. Both work with "printf -- '-%s-\n' foo". * We will mimic coreutils. */ if (argv[1] && argv[1][0] == '-' && argv[1][1] == '-' && !argv[1][2]) argv++; if (!argv[1]) { if (ENABLE_ASH_BUILTIN_PRINTF && applet_name[0] != 'p' ) { bb_error_msg("usage: printf FORMAT [ARGUMENT...]"); return 2; /* bash compat */ } bb_show_usage(); } format = argv[1]; argv2 = argv + 2; conv_err = 0; do { argv = argv2; argv2 = print_formatted(format, argv, &conv_err); } while (argv2 > argv && *argv2); /* coreutils compat (bash doesn't do this): if (*argv) fprintf(stderr, "excess args ignored"); */ return (argv2 < argv) /* if true, print_formatted errored out */ || conv_err; /* print_formatted saw invalid number */ } busybox-1.22.1/coreutils/cp.c0000644000000000000000000001415312263563520014555 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini cp implementation for busybox * * Copyright (C) 2000 by Matt Kraai * SELinux support by Yuichi Nakamura * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Size reduction. */ //usage:#define cp_trivial_usage //usage: "[OPTIONS] SOURCE... DEST" //usage:#define cp_full_usage "\n\n" //usage: "Copy SOURCE(s) to DEST\n" //usage: "\n -a Same as -dpR" //usage: IF_SELINUX( //usage: "\n -c Preserve security context" //usage: ) //usage: "\n -R,-r Recurse" //usage: "\n -d,-P Preserve symlinks (default if -R)" //usage: "\n -L Follow all symlinks" //usage: "\n -H Follow symlinks on command line" //usage: "\n -p Preserve file attributes if possible" //usage: "\n -f Overwrite" //usage: "\n -i Prompt before overwrite" //usage: "\n -l,-s Create (sym)links" #include "libbb.h" #include "libcoreutils/coreutils.h" /* This is a NOEXEC applet. Be very careful! */ int cp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int cp_main(int argc, char **argv) { struct stat source_stat; struct stat dest_stat; const char *last; const char *dest; int s_flags; int d_flags; int flags; int status; enum { OPT_a = 1 << (sizeof(FILEUTILS_CP_OPTSTR)-1), OPT_r = 1 << (sizeof(FILEUTILS_CP_OPTSTR)), OPT_P = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+1), OPT_v = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+2), #if ENABLE_FEATURE_CP_LONG_OPTIONS OPT_parents = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+3), #endif }; // Need at least two arguments // Soft- and hardlinking doesn't mix // -P and -d are the same (-P is POSIX, -d is GNU) // -r and -R are the same // -R (and therefore -r) turns on -d (coreutils does this) // -a = -pdR opt_complementary = "-2:l--s:s--l:Pd:rRd:Rd:apdR"; #if ENABLE_FEATURE_CP_LONG_OPTIONS applet_long_options = "archive\0" No_argument "a" "force\0" No_argument "f" "interactive\0" No_argument "i" "link\0" No_argument "l" "dereference\0" No_argument "L" "no-dereference\0" No_argument "P" "recursive\0" No_argument "R" "symbolic-link\0" No_argument "s" "verbose\0" No_argument "v" "parents\0" No_argument "\xff" ; #endif // -v (--verbose) is ignored flags = getopt32(argv, FILEUTILS_CP_OPTSTR "arPv"); /* Options of cp from GNU coreutils 6.10: * -a, --archive * -f, --force * -i, --interactive * -l, --link * -L, --dereference * -P, --no-dereference * -R, -r, --recursive * -s, --symbolic-link * -v, --verbose * -H follow command-line symbolic links in SOURCE * -d same as --no-dereference --preserve=links * -p same as --preserve=mode,ownership,timestamps * -c same as --preserve=context * --parents * use full source file name under DIRECTORY * NOT SUPPORTED IN BBOX: * --backup[=CONTROL] * make a backup of each existing destination file * -b like --backup but does not accept an argument * --copy-contents * copy contents of special files when recursive * --preserve[=ATTR_LIST] * preserve attributes (default: mode,ownership,timestamps), * if possible additional attributes: security context,links,all * --no-preserve=ATTR_LIST * --remove-destination * remove each existing destination file before attempting to open * --sparse=WHEN * control creation of sparse files * --strip-trailing-slashes * remove any trailing slashes from each SOURCE argument * -S, --suffix=SUFFIX * override the usual backup suffix * -t, --target-directory=DIRECTORY * copy all SOURCE arguments into DIRECTORY * -T, --no-target-directory * treat DEST as a normal file * -u, --update * copy only when the SOURCE file is newer than the destination * file or when the destination file is missing * -x, --one-file-system * stay on this file system * -Z, --context=CONTEXT * (SELinux) set SELinux security context of copy to CONTEXT */ argc -= optind; argv += optind; /* Reverse this bit. If there is -d, bit is not set: */ flags ^= FILEUTILS_DEREFERENCE; /* coreutils 6.9 compat: * by default, "cp" derefs symlinks (creates regular dest files), * but "cp -R" does not. We switch off deref if -r or -R (see above). * However, "cp -RL" must still deref symlinks: */ if (flags & FILEUTILS_DEREF_SOFTLINK) /* -L */ flags |= FILEUTILS_DEREFERENCE; #if ENABLE_SELINUX if (flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) { selinux_or_die(); } #endif status = EXIT_SUCCESS; last = argv[argc - 1]; /* If there are only two arguments and... */ if (argc == 2) { s_flags = cp_mv_stat2(*argv, &source_stat, (flags & FILEUTILS_DEREFERENCE) ? stat : lstat); if (s_flags < 0) return EXIT_FAILURE; d_flags = cp_mv_stat(last, &dest_stat); if (d_flags < 0) return EXIT_FAILURE; #if ENABLE_FEATURE_CP_LONG_OPTIONS if (flags & OPT_parents) { if (!(d_flags & 2)) { bb_error_msg_and_die("with --parents, the destination must be a directory"); } } #endif /* ...if neither is a directory... */ if (!((s_flags | d_flags) & 2) /* ...or: recursing, the 1st is a directory, and the 2nd doesn't exist... */ || ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags) ) { /* Do a simple copy */ dest = last; goto DO_COPY; /* NB: argc==2 -> *++argv==last */ } } while (1) { #if ENABLE_FEATURE_CP_LONG_OPTIONS if (flags & OPT_parents) { char *dest_dup; char *dest_dir; dest = concat_path_file(last, *argv); dest_dup = xstrdup(dest); dest_dir = dirname(dest_dup); if (bb_make_directory(dest_dir, -1, FILEUTILS_RECUR)) { return EXIT_FAILURE; } free(dest_dup); goto DO_COPY; } #endif dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); DO_COPY: if (copy_file(*argv, dest, flags) < 0) { status = EXIT_FAILURE; } if (*++argv == last) { /* possibly leaking dest... */ break; } /* don't move up: dest may be == last and not malloced! */ free((void*)dest); } /* Exit. We are NOEXEC, not NOFORK. We do exit at the end of main() */ return status; } busybox-1.22.1/coreutils/tty.c0000644000000000000000000000300212263563520014762 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * tty implementation for busybox * * Copyright (C) 2003 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv4 compliant */ /* http://www.opengroup.org/onlinepubs/9699919799/utilities/tty.html */ //usage:#define tty_trivial_usage //usage: "" //usage:#define tty_full_usage "\n\n" //usage: "Print file name of stdin's terminal" //usage: IF_INCLUDE_SUSv2( "\n" //usage: "\n -s Print nothing, only return exit status" //usage: ) //usage: //usage:#define tty_example_usage //usage: "$ tty\n" //usage: "/dev/tty2\n" #include "libbb.h" int tty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int tty_main(int argc UNUSED_PARAM, char **argv) { const char *s; IF_INCLUDE_SUSv2(int silent;) /* Note: No longer relevant in SUSv3. */ int retval; xfunc_error_retval = 2; /* SUSv3 requires > 1 for error. */ IF_INCLUDE_SUSv2(silent = getopt32(argv, "s");) IF_INCLUDE_SUSv2(argv += optind;) IF_NOT_INCLUDE_SUSv2(argv += 1;) /* gnu tty outputs a warning that it is ignoring all args. */ bb_warn_ignoring_args(argv[0]); retval = EXIT_SUCCESS; s = xmalloc_ttyname(STDIN_FILENO); if (s == NULL) { /* According to SUSv3, ttyname can fail with EBADF or ENOTTY. * We know the file descriptor is good, so failure means not a tty. */ s = "not a tty"; retval = EXIT_FAILURE; } IF_INCLUDE_SUSv2(if (!silent) puts(s);) IF_NOT_INCLUDE_SUSv2(puts(s);) fflush_stdout_and_exit(retval); } busybox-1.22.1/coreutils/expand.c0000644000000000000000000001326112263563520015431 0ustar rootroot/* expand - convert tabs to spaces * unexpand - convert spaces to tabs * * Copyright (C) 89, 91, 1995-2006 Free Software Foundation, Inc. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * David MacKenzie * * Options for expand: * -t num --tabs=NUM Convert tabs to num spaces (default 8 spaces). * -i --initial Only convert initial tabs on each line to spaces. * * Options for unexpand: * -a --all Convert all blanks, instead of just initial blanks. * -f --first-only Convert only leading sequences of blanks (default). * -t num --tabs=NUM Have tabs num characters apart instead of 8. * * Busybox version (C) 2007 by Tito Ragusa * * Caveat: this versions of expand and unexpand don't accept tab lists. */ //usage:#define expand_trivial_usage //usage: "[-i] [-t N] [FILE]..." //usage:#define expand_full_usage "\n\n" //usage: "Convert tabs to spaces, writing to stdout\n" //usage: IF_FEATURE_EXPAND_LONG_OPTIONS( //usage: "\n -i,--initial Don't convert tabs after non blanks" //usage: "\n -t,--tabs=N Tabstops every N chars" //usage: ) //usage: IF_NOT_FEATURE_EXPAND_LONG_OPTIONS( //usage: "\n -i Don't convert tabs after non blanks" //usage: "\n -t Tabstops every N chars" //usage: ) //usage:#define unexpand_trivial_usage //usage: "[-fa][-t N] [FILE]..." //usage:#define unexpand_full_usage "\n\n" //usage: "Convert spaces to tabs, writing to stdout\n" //usage: IF_FEATURE_UNEXPAND_LONG_OPTIONS( //usage: "\n -a,--all Convert all blanks" //usage: "\n -f,--first-only Convert only leading blanks" //usage: "\n -t,--tabs=N Tabstops every N chars" //usage: ) //usage: IF_NOT_FEATURE_UNEXPAND_LONG_OPTIONS( //usage: "\n -a Convert all blanks" //usage: "\n -f Convert only leading blanks" //usage: "\n -t N Tabstops every N chars" //usage: ) #include "libbb.h" #include "unicode.h" enum { OPT_INITIAL = 1 << 0, OPT_TABS = 1 << 1, OPT_ALL = 1 << 2, }; #if ENABLE_EXPAND static void expand(FILE *file, unsigned tab_size, unsigned opt) { char *line; while ((line = xmalloc_fgets(file)) != NULL) { unsigned char c; char *ptr; char *ptr_strbeg; ptr = ptr_strbeg = line; while ((c = *ptr) != '\0') { if ((opt & OPT_INITIAL) && !isblank(c)) { /* not space or tab */ break; } if (c == '\t') { unsigned len; *ptr = '\0'; # if ENABLE_UNICODE_SUPPORT len = unicode_strwidth(ptr_strbeg); # else len = ptr - ptr_strbeg; # endif len = tab_size - (len % tab_size); /*while (ptr[1] == '\t') { ptr++; len += tab_size; } - can handle many tabs at once */ printf("%s%*s", ptr_strbeg, len, ""); ptr_strbeg = ptr + 1; } ptr++; } fputs(ptr_strbeg, stdout); free(line); } } #endif #if ENABLE_UNEXPAND static void unexpand(FILE *file, unsigned tab_size, unsigned opt) { char *line; while ((line = xmalloc_fgets(file)) != NULL) { char *ptr = line; unsigned column = 0; while (*ptr) { unsigned n; unsigned len = 0; while (*ptr == ' ') { ptr++; len++; } column += len; if (*ptr == '\t') { column += tab_size - (column % tab_size); ptr++; continue; } n = column / tab_size; if (n) { len = column = column % tab_size; while (n--) putchar('\t'); } if ((opt & OPT_INITIAL) && ptr != line) { printf("%*s%s", len, "", ptr); break; } n = strcspn(ptr, "\t "); printf("%*s%.*s", len, "", n, ptr); # if ENABLE_UNICODE_SUPPORT { char c = ptr[n]; ptr[n] = '\0'; len = unicode_strwidth(ptr); ptr[n] = c; } # else len = n; # endif ptr += n; column = (column + len) % tab_size; } free(line); } } #endif int expand_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int expand_main(int argc UNUSED_PARAM, char **argv) { /* Default 8 spaces for 1 tab */ const char *opt_t = "8"; FILE *file; unsigned tab_size; unsigned opt; int exit_status = EXIT_SUCCESS; #if ENABLE_FEATURE_EXPAND_LONG_OPTIONS static const char expand_longopts[] ALIGN1 = /* name, has_arg, val */ "initial\0" No_argument "i" "tabs\0" Required_argument "t" ; #endif #if ENABLE_FEATURE_UNEXPAND_LONG_OPTIONS static const char unexpand_longopts[] ALIGN1 = /* name, has_arg, val */ "first-only\0" No_argument "i" "tabs\0" Required_argument "t" "all\0" No_argument "a" ; #endif init_unicode(); if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) { IF_FEATURE_EXPAND_LONG_OPTIONS(applet_long_options = expand_longopts); opt = getopt32(argv, "it:", &opt_t); } else { IF_FEATURE_UNEXPAND_LONG_OPTIONS(applet_long_options = unexpand_longopts); /* -t NUM sets also -a */ opt_complementary = "ta"; opt = getopt32(argv, "ft:a", &opt_t); /* -f --first-only is the default */ if (!(opt & OPT_ALL)) opt |= OPT_INITIAL; } tab_size = xatou_range(opt_t, 1, UINT_MAX); argv += optind; if (!*argv) { *--argv = (char*)bb_msg_standard_input; } do { file = fopen_or_warn_stdin(*argv); if (!file) { exit_status = EXIT_FAILURE; continue; } if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) IF_EXPAND(expand(file, tab_size, opt)); else IF_UNEXPAND(unexpand(file, tab_size, opt)); /* Check and close the file */ if (fclose_if_not_stdin(file)) { bb_simple_perror_msg(*argv); exit_status = EXIT_FAILURE; } /* If stdin also clear EOF */ if (file == stdin) clearerr(file); } while (*++argv); /* Now close stdin also */ /* (if we didn't read from it, it's a no-op) */ if (fclose(stdin)) bb_perror_msg_and_die(bb_msg_standard_input); fflush_stdout_and_exit(exit_status); } busybox-1.22.1/coreutils/basename.c0000644000000000000000000000410512263563520015722 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini basename implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Changes: * 1) Now checks for too many args. Need at least one and at most two. * 2) Don't check for options, as per SUSv3. * 3) Save some space by using strcmp(). Calling strncmp() here was silly. */ /* BB_AUDIT SUSv3 compliant */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/basename.html */ //kbuild:lib-$(CONFIG_BASENAME) += basename.o //config:config BASENAME //config: bool "basename" //config: default y //config: help //config: basename is used to strip the directory and suffix from filenames, //config: leaving just the filename itself. Enable this option if you wish //config: to enable the 'basename' utility. //usage:#define basename_trivial_usage //usage: "FILE [SUFFIX]" //usage:#define basename_full_usage "\n\n" //usage: "Strip directory path and .SUFFIX from FILE\n" //usage: //usage:#define basename_example_usage //usage: "$ basename /usr/local/bin/foo\n" //usage: "foo\n" //usage: "$ basename /usr/local/bin/\n" //usage: "bin\n" //usage: "$ basename /foo/bar.txt .txt\n" //usage: "bar" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ int basename_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int basename_main(int argc, char **argv) { size_t m, n; char *s; if (argv[1] && strcmp(argv[1], "--") == 0) { argv++; argc--; } if ((unsigned)(argc-2) >= 2) { bb_show_usage(); } /* It should strip slash: /abc/def/ -> def */ s = bb_get_last_path_component_strip(*++argv); m = strlen(s); if (*++argv) { n = strlen(*argv); if ((m > n) && (strcmp(s+m-n, *argv) == 0)) { m -= n; /*s[m] = '\0'; - redundant */ } } /* puts(s) will do, but we can do without stdio this way: */ s[m++] = '\n'; /* NB: != is correct here: */ return full_write(STDOUT_FILENO, s, m) != (ssize_t)m; } busybox-1.22.1/coreutils/tr.c0000644000000000000000000002240012263563520014572 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini tr implementation for busybox * ** Copyright (c) 1987,1997, Prentice Hall All rights reserved. * * The name of Prentice Hall may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * Copyright (c) Michiel Huisjes * * This version of tr is adapted from Minix tr and was modified * by Erik Andersen to be used in busybox. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* http://www.opengroup.org/onlinepubs/009695399/utilities/tr.html * TODO: graph, print */ //kbuild:lib-$(CONFIG_TR) += tr.o //config:config TR //config: bool "tr" //config: default y //config: help //config: tr is used to squeeze, and/or delete characters from standard //config: input, writing to standard output. //config: //config:config FEATURE_TR_CLASSES //config: bool "Enable character classes (such as [:upper:])" //config: default y //config: depends on TR //config: help //config: Enable character classes, enabling commands such as: //config: tr [:upper:] [:lower:] to convert input into lowercase. //config: //config:config FEATURE_TR_EQUIV //config: bool "Enable equivalence classes" //config: default y //config: depends on TR //config: help //config: Enable equivalence classes, which essentially add the enclosed //config: character to the current set. For instance, tr [=a=] xyz would //config: replace all instances of 'a' with 'xyz'. This option is mainly //config: useful for cases when no other way of expressing a character //config: is possible. //usage:#define tr_trivial_usage //usage: "[-cds] STRING1 [STRING2]" //usage:#define tr_full_usage "\n\n" //usage: "Translate, squeeze, or delete characters from stdin, writing to stdout\n" //usage: "\n -c Take complement of STRING1" //usage: "\n -d Delete input characters coded STRING1" //usage: "\n -s Squeeze multiple output characters of STRING2 into one character" //usage: //usage:#define tr_example_usage //usage: "$ echo \"gdkkn vnqkc\" | tr [a-y] [b-z]\n" //usage: "hello world\n" #include "libbb.h" enum { ASCII = 256, /* string buffer needs to be at least as big as the whole "alphabet". * BUFSIZ == ASCII is ok, but we will realloc in expand * even for smallest patterns, let's avoid that by using *2: */ TR_BUFSIZ = (BUFSIZ > ASCII*2) ? BUFSIZ : ASCII*2, }; static void map(char *pvector, char *string1, unsigned string1_len, char *string2, unsigned string2_len) { char last = '0'; unsigned i, j; for (j = 0, i = 0; i < string1_len; i++) { if (string2_len <= j) pvector[(unsigned char)(string1[i])] = last; else pvector[(unsigned char)(string1[i])] = last = string2[j++]; } } /* supported constructs: * Ranges, e.g., 0-9 ==> 0123456789 * Escapes, e.g., \a ==> Control-G * Character classes, e.g. [:upper:] ==> A...Z * Equiv classess, e.g. [=A=] ==> A (hmmmmmmm?) * not supported: * \ooo-\ooo - octal ranges * [x*N] - repeat char x N times * [x*] - repeat char x until it fills STRING2: * # echo qwe123 | /usr/bin/tr 123456789 '[d]' * qwe[d] * # echo qwe123 | /usr/bin/tr 123456789 '[d*]' * qweddd */ static unsigned expand(const char *arg, char **buffer_p) { char *buffer = *buffer_p; unsigned pos = 0; unsigned size = TR_BUFSIZ; unsigned i; /* can't be unsigned char: must be able to hold 256 */ unsigned char ac; while (*arg) { if (pos + ASCII > size) { size += ASCII; *buffer_p = buffer = xrealloc(buffer, size); } if (*arg == '\\') { arg++; buffer[pos++] = bb_process_escape_sequence(&arg); continue; } if (arg[1] == '-') { /* "0-9..." */ ac = arg[2]; if (ac == '\0') { /* "0-": copy verbatim */ buffer[pos++] = *arg++; /* copy '0' */ continue; /* next iter will copy '-' and stop */ } i = (unsigned char) *arg; while (i <= ac) /* ok: i is unsigned _int_ */ buffer[pos++] = i++; arg += 3; /* skip 0-9 */ continue; } if ((ENABLE_FEATURE_TR_CLASSES || ENABLE_FEATURE_TR_EQUIV) && *arg == '[' ) { arg++; i = (unsigned char) *arg++; /* "[xyz...". i=x, arg points to y */ if (ENABLE_FEATURE_TR_CLASSES && i == ':') { /* [:class:] */ #define CLO ":]\0" static const char classes[] ALIGN1 = "alpha"CLO "alnum"CLO "digit"CLO "lower"CLO "upper"CLO "space"CLO "blank"CLO "punct"CLO "cntrl"CLO "xdigit"CLO; enum { CLASS_invalid = 0, /* we increment the retval */ CLASS_alpha = 1, CLASS_alnum = 2, CLASS_digit = 3, CLASS_lower = 4, CLASS_upper = 5, CLASS_space = 6, CLASS_blank = 7, CLASS_punct = 8, CLASS_cntrl = 9, CLASS_xdigit = 10, //CLASS_graph = 11, //CLASS_print = 12, }; smalluint j; char *tmp; /* xdigit needs 8, not 7 */ i = 7 + (arg[0] == 'x'); tmp = xstrndup(arg, i); j = index_in_strings(classes, tmp) + 1; free(tmp); if (j == CLASS_invalid) goto skip_bracket; arg += i; if (j == CLASS_alnum || j == CLASS_digit || j == CLASS_xdigit) { for (i = '0'; i <= '9'; i++) buffer[pos++] = i; } if (j == CLASS_alpha || j == CLASS_alnum || j == CLASS_upper) { for (i = 'A'; i <= 'Z'; i++) buffer[pos++] = i; } if (j == CLASS_alpha || j == CLASS_alnum || j == CLASS_lower) { for (i = 'a'; i <= 'z'; i++) buffer[pos++] = i; } if (j == CLASS_space || j == CLASS_blank) { buffer[pos++] = '\t'; if (j == CLASS_space) { buffer[pos++] = '\n'; buffer[pos++] = '\v'; buffer[pos++] = '\f'; buffer[pos++] = '\r'; } buffer[pos++] = ' '; } if (j == CLASS_punct || j == CLASS_cntrl) { for (i = '\0'; i < ASCII; i++) { if ((j == CLASS_punct && isprint_asciionly(i) && !isalnum(i) && !isspace(i)) || (j == CLASS_cntrl && iscntrl(i)) ) { buffer[pos++] = i; } } } if (j == CLASS_xdigit) { for (i = 'A'; i <= 'F'; i++) { buffer[pos + 6] = i | 0x20; buffer[pos++] = i; } pos += 6; } continue; } /* "[xyz...", i=x, arg points to y */ if (ENABLE_FEATURE_TR_EQUIV && i == '=') { /* [=CHAR=] */ buffer[pos++] = *arg; /* copy CHAR */ if (!arg[0] || arg[1] != '=' || arg[2] != ']') bb_show_usage(); arg += 3; /* skip CHAR=] */ continue; } /* The rest of "[xyz..." cases is treated as normal * string, "[" has no special meaning here: * tr "[a-z]" "[A-Z]" can be written as tr "a-z" "A-Z", * also try tr "[a-z]" "_A-Z+" and you'll see that * [] is not special here. */ skip_bracket: arg -= 2; /* points to "[" in "[xyz..." */ } buffer[pos++] = *arg++; } return pos; } /* NB: buffer is guaranteed to be at least TR_BUFSIZE * (which is >= ASCII) big. */ static int complement(char *buffer, int buffer_len) { int len; char conv[ASCII]; unsigned char ch; len = 0; ch = '\0'; while (1) { if (memchr(buffer, ch, buffer_len) == NULL) conv[len++] = ch; if (++ch == '\0') break; } memcpy(buffer, conv, len); return len; } int tr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int tr_main(int argc UNUSED_PARAM, char **argv) { int i; smalluint opts; ssize_t read_chars; size_t in_index, out_index; unsigned last = UCHAR_MAX + 1; /* not equal to any char */ unsigned char coded, c; char *str1 = xmalloc(TR_BUFSIZ); char *str2 = xmalloc(TR_BUFSIZ); int str2_length; int str1_length; char *vector = xzalloc(ASCII * 3); char *invec = vector + ASCII; char *outvec = vector + ASCII * 2; #define TR_OPT_complement (3 << 0) #define TR_OPT_delete (1 << 2) #define TR_OPT_squeeze_reps (1 << 3) for (i = 0; i < ASCII; i++) { vector[i] = i; /*invec[i] = outvec[i] = FALSE; - done by xzalloc */ } /* -C/-c difference is that -C complements "characters", * and -c complements "values" (binary bytes I guess). * In POSIX locale, these are the same. */ opt_complementary = "-1"; opts = getopt32(argv, "+Ccds"); /* '+': stop at first non-option */ argv += optind; str1_length = expand(*argv++, &str1); str2_length = 0; if (opts & TR_OPT_complement) str1_length = complement(str1, str1_length); if (*argv) { if (argv[0][0] == '\0') bb_error_msg_and_die("STRING2 cannot be empty"); str2_length = expand(*argv, &str2); map(vector, str1, str1_length, str2, str2_length); } for (i = 0; i < str1_length; i++) invec[(unsigned char)(str1[i])] = TRUE; for (i = 0; i < str2_length; i++) outvec[(unsigned char)(str2[i])] = TRUE; goto start_from; /* In this loop, str1 space is reused as input buffer, * str2 - as output one. */ for (;;) { /* If we're out of input, flush output and read more input. */ if ((ssize_t)in_index == read_chars) { if (out_index) { xwrite(STDOUT_FILENO, str2, out_index); start_from: out_index = 0; } read_chars = safe_read(STDIN_FILENO, str1, TR_BUFSIZ); if (read_chars <= 0) { if (read_chars < 0) bb_perror_msg_and_die(bb_msg_read_error); break; } in_index = 0; } c = str1[in_index++]; if ((opts & TR_OPT_delete) && invec[c]) continue; coded = vector[c]; if ((opts & TR_OPT_squeeze_reps) && last == coded && (invec[c] || outvec[coded]) ) { continue; } str2[out_index++] = last = coded; } if (ENABLE_FEATURE_CLEAN_UP) { free(vector); free(str2); free(str1); } return EXIT_SUCCESS; } busybox-1.22.1/coreutils/wc.c0000644000000000000000000001365012263563520014565 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * wc implementation for busybox * * Copyright (C) 2003 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/wc.html */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Rewritten to fix a number of problems and do some size optimizations. * Problems in the previous busybox implementation (besides bloat) included: * 1) broken 'wc -c' optimization (read note below) * 2) broken handling of '-' args * 3) no checking of ferror on EOF returns * 4) isprint() wasn't considered when word counting. * * NOTES: * * The previous busybox wc attempted an optimization using stat for the * case of counting chars only. I omitted that because it was broken. * It didn't take into account the possibility of input coming from a * pipe, or input from a file with file pointer not at the beginning. * * To implement such a speed optimization correctly, not only do you * need the size, but also the file position. Note also that the * file position may be past the end of file. Consider the example * (adapted from example in gnu wc.c) * * echo hello > /tmp/testfile && * (dd ibs=1k skip=1 count=0 &> /dev/null; wc -c) < /tmp/testfile * * for which 'wc -c' should output '0'. */ #include "libbb.h" #include "unicode.h" #if !ENABLE_LOCALE_SUPPORT # undef isprint # undef isspace # define isprint(c) ((unsigned)((c) - 0x20) <= (0x7e - 0x20)) # define isspace(c) ((c) == ' ') #endif #if ENABLE_FEATURE_WC_LARGE # define COUNT_T unsigned long long # define COUNT_FMT "llu" #else # define COUNT_T unsigned # define COUNT_FMT "u" #endif /* We support -m even when UNICODE_SUPPORT is off, * we just don't advertise it in help text, * since it is the same as -c in this case. */ //usage:#define wc_trivial_usage //usage: "[-c"IF_UNICODE_SUPPORT("m")"lwL] [FILE]..." //usage: //usage:#define wc_full_usage "\n\n" //usage: "Count lines, words, and bytes for each FILE (or stdin)\n" //usage: "\n -c Count bytes" //usage: IF_UNICODE_SUPPORT( //usage: "\n -m Count characters" //usage: ) //usage: "\n -l Count newlines" //usage: "\n -w Count words" //usage: "\n -L Print longest line length" //usage: //usage:#define wc_example_usage //usage: "$ wc /etc/passwd\n" //usage: " 31 46 1365 /etc/passwd\n" /* Order is important if we want to be compatible with * column order in "wc -cmlwL" output: */ enum { WC_LINES = 0, /* -l */ WC_WORDS = 1, /* -w */ WC_UNICHARS = 2, /* -m */ WC_BYTES = 3, /* -c */ WC_LENGTH = 4, /* -L */ NUM_WCS = 5, }; int wc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int wc_main(int argc UNUSED_PARAM, char **argv) { const char *arg; const char *start_fmt = " %9"COUNT_FMT + 1; const char *fname_fmt = " %s\n"; COUNT_T *pcounts; COUNT_T counts[NUM_WCS]; COUNT_T totals[NUM_WCS]; int num_files; smallint status = EXIT_SUCCESS; unsigned print_type; init_unicode(); print_type = getopt32(argv, "lwmcL"); if (print_type == 0) { print_type = (1 << WC_LINES) | (1 << WC_WORDS) | (1 << WC_BYTES); } argv += optind; if (!argv[0]) { *--argv = (char *) bb_msg_standard_input; fname_fmt = "\n"; } if (!argv[1]) { /* zero or one filename? */ if (!((print_type-1) & print_type)) /* exactly one option? */ start_fmt = "%"COUNT_FMT; } memset(totals, 0, sizeof(totals)); pcounts = counts; num_files = 0; while ((arg = *argv++) != NULL) { FILE *fp; const char *s; unsigned u; unsigned linepos; smallint in_word; ++num_files; fp = fopen_or_warn_stdin(arg); if (!fp) { status = EXIT_FAILURE; continue; } memset(counts, 0, sizeof(counts)); linepos = 0; in_word = 0; while (1) { int c; /* Our -w doesn't match GNU wc exactly... oh well */ c = getc(fp); if (c == EOF) { if (ferror(fp)) { bb_simple_perror_msg(arg); status = EXIT_FAILURE; } goto DO_EOF; /* Treat an EOF as '\r'. */ } /* Cater for -c and -m */ ++counts[WC_BYTES]; if (unicode_status != UNICODE_ON /* every byte is a new char */ || (c & 0xc0) != 0x80 /* it isn't a 2nd+ byte of a Unicode char */ ) { ++counts[WC_UNICHARS]; } if (isprint_asciionly(c)) { /* FIXME: not unicode-aware */ ++linepos; if (!isspace(c)) { in_word = 1; continue; } } else if ((unsigned)(c - 9) <= 4) { /* \t 9 * \n 10 * \v 11 * \f 12 * \r 13 */ if (c == '\t') { linepos = (linepos | 7) + 1; } else { /* '\n', '\r', '\f', or '\v' */ DO_EOF: if (linepos > counts[WC_LENGTH]) { counts[WC_LENGTH] = linepos; } if (c == '\n') { ++counts[WC_LINES]; } if (c != '\v') { linepos = 0; } } } else { continue; } counts[WC_WORDS] += in_word; in_word = 0; if (c == EOF) { break; } } fclose_if_not_stdin(fp); if (totals[WC_LENGTH] < counts[WC_LENGTH]) { totals[WC_LENGTH] = counts[WC_LENGTH]; } totals[WC_LENGTH] -= counts[WC_LENGTH]; OUTPUT: /* coreutils wc tries hard to print pretty columns * (saves results for all files, finds max col len etc...) * we won't try that hard, it will bloat us too much */ s = start_fmt; u = 0; do { if (print_type & (1 << u)) { printf(s, pcounts[u]); s = " %9"COUNT_FMT; /* Ok... restore the leading space. */ } totals[u] += pcounts[u]; } while (++u < NUM_WCS); printf(fname_fmt, arg); } /* If more than one file was processed, we want the totals. To save some * space, we set the pcounts ptr to the totals array. This has the side * effect of trashing the totals array after outputting it, but that's * irrelavent since we no longer need it. */ if (num_files > 1) { num_files = 0; /* Make sure we don't get here again. */ arg = "total"; pcounts = totals; --argv; goto OUTPUT; } fflush_stdout_and_exit(status); } busybox-1.22.1/coreutils/mv.c0000644000000000000000000000753112263563520014577 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini mv implementation for busybox * * Copyright (C) 2000 by Matt Kraai * SELinux support by Yuichi Nakamura * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Size reduction and improved error checking. */ #include "libbb.h" #include "libcoreutils/coreutils.h" //usage:#define mv_trivial_usage //usage: "[-fin] SOURCE DEST\n" //usage: "or: mv [-fin] SOURCE... DIRECTORY" //usage:#define mv_full_usage "\n\n" //usage: "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY\n" //usage: "\n -f Don't prompt before overwriting" //usage: "\n -i Interactive, prompt before overwrite" //usage: "\n -n Don't overwrite an existing file" //usage: //usage:#define mv_example_usage //usage: "$ mv /tmp/foo /bin/bar\n" #if ENABLE_FEATURE_MV_LONG_OPTIONS static const char mv_longopts[] ALIGN1 = "interactive\0" No_argument "i" "force\0" No_argument "f" "no-clobber\0" No_argument "n" "verbose\0" No_argument "v" ; #endif #define OPT_FORCE (1 << 0) #define OPT_INTERACTIVE (1 << 1) #define OPT_NOCLOBBER (1 << 2) int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mv_main(int argc, char **argv) { struct stat dest_stat; const char *last; const char *dest; unsigned flags; int dest_exists; int status = 0; int copy_flag = 0; #if ENABLE_FEATURE_MV_LONG_OPTIONS applet_long_options = mv_longopts; #endif /* Need at least two arguments. * If more than one of -f, -i, -n is specified , only the final one * takes effect (it unsets previous options). * -v is accepted but ignored. */ opt_complementary = "-2:f-in:i-fn:n-fi"; flags = getopt32(argv, "finv"); argc -= optind; argv += optind; last = argv[argc - 1]; if (argc == 2) { dest_exists = cp_mv_stat(last, &dest_stat); if (dest_exists < 0) { return EXIT_FAILURE; } if (!(dest_exists & 2)) { /* last is not a directory */ dest = last; goto DO_MOVE; } } do { dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); dest_exists = cp_mv_stat(dest, &dest_stat); if (dest_exists < 0) { goto RET_1; } DO_MOVE: if (dest_exists) { if (flags & OPT_NOCLOBBER) goto RET_0; if (!(flags & OPT_FORCE) && ((access(dest, W_OK) < 0 && isatty(0)) || (flags & OPT_INTERACTIVE)) ) { if (fprintf(stderr, "mv: overwrite '%s'? ", dest) < 0) { goto RET_1; /* Ouch! fprintf failed! */ } if (!bb_ask_confirmation()) { goto RET_0; } } } if (rename(*argv, dest) < 0) { struct stat source_stat; int source_exists; if (errno != EXDEV || (source_exists = cp_mv_stat2(*argv, &source_stat, lstat)) < 1 ) { bb_perror_msg("can't rename '%s'", *argv); } else { static const char fmt[] ALIGN1 = "can't overwrite %sdirectory with %sdirectory"; if (dest_exists) { if (dest_exists == 3) { if (source_exists != 3) { bb_error_msg(fmt, "", "non-"); goto RET_1; } } else { if (source_exists == 3) { bb_error_msg(fmt, "non-", ""); goto RET_1; } } if (unlink(dest) < 0) { bb_perror_msg("can't remove '%s'", dest); goto RET_1; } } /* FILEUTILS_RECUR also prevents nasties like * "read from device and write contents to dst" * instead of "create same device node" */ copy_flag = FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS; #if ENABLE_SELINUX copy_flag |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; #endif if ((copy_file(*argv, dest, copy_flag) >= 0) && (remove_file(*argv, FILEUTILS_RECUR | FILEUTILS_FORCE) >= 0) ) { goto RET_0; } } RET_1: status = 1; } RET_0: if (dest != last) { free((void *) dest); } } while (*++argv != last); return status; } busybox-1.22.1/coreutils/readlink.c0000644000000000000000000000502612263563520015743 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini readlink implementation for busybox * * Copyright (C) 2000,2001 Matt Kraai * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define readlink_trivial_usage //usage: IF_FEATURE_READLINK_FOLLOW("[-fnv] ") "FILE" //usage:#define readlink_full_usage "\n\n" //usage: "Display the value of a symlink" //usage: IF_FEATURE_READLINK_FOLLOW( "\n" //usage: "\n -f Canonicalize by following all symlinks" //usage: "\n -n Don't add newline" //usage: "\n -v Verbose" //usage: ) #include "libbb.h" /* * # readlink --version * readlink (GNU coreutils) 6.10 * # readlink --help * -f, --canonicalize * canonicalize by following every symlink in * every component of the given name recursively; * all but the last component must exist * -e, --canonicalize-existing * canonicalize by following every symlink in * every component of the given name recursively, * all components must exist * -m, --canonicalize-missing * canonicalize by following every symlink in * every component of the given name recursively, * without requirements on components existence * -n, --no-newline do not output the trailing newline * -q, --quiet, -s, --silent suppress most error messages * -v, --verbose report error messages * * bbox supports: -f (partially) -n -v (fully), -q -s (accepts but ignores) * Note: we export the -f flag, but our -f behaves like coreutils' -e. * Unfortunately, there isn't a C lib function we can leverage to get this * behavior which means we'd have to implement the full stack ourselves :(. */ int readlink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int readlink_main(int argc UNUSED_PARAM, char **argv) { char *buf; char *fname; IF_FEATURE_READLINK_FOLLOW( unsigned opt; /* We need exactly one non-option argument. */ opt_complementary = "=1"; opt = getopt32(argv, "fnvsq"); fname = argv[optind]; ) IF_NOT_FEATURE_READLINK_FOLLOW( const unsigned opt = 0; if (argc != 2) bb_show_usage(); fname = argv[1]; ) /* compat: coreutils readlink reports errors silently via exit code */ if (!(opt & 4)) /* not -v */ logmode = LOGMODE_NONE; if (opt & 1) { /* -f */ buf = xmalloc_realpath(fname); } else { buf = xmalloc_readlink_or_warn(fname); } if (!buf) return EXIT_FAILURE; printf((opt & 2) ? "%s" : "%s\n", buf); if (ENABLE_FEATURE_CLEAN_UP) free(buf); fflush_stdout_and_exit(EXIT_SUCCESS); } busybox-1.22.1/coreutils/sort.c0000644000000000000000000003075512267106022015142 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * SuS3 compliant sort implementation for busybox * * Copyright (C) 2004 by Rob Landley * * MAINTAINER: Rob Landley * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * See SuS3 sort standard at: * http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html */ //usage:#define sort_trivial_usage //usage: "[-nru" //usage: IF_FEATURE_SORT_BIG("gMcszbdfimSTokt] [-o FILE] [-k start[.offset][opts][,end[.offset][opts]] [-t CHAR") //usage: "] [FILE]..." //usage:#define sort_full_usage "\n\n" //usage: "Sort lines of text\n" //usage: IF_FEATURE_SORT_BIG( //usage: "\n -b Ignore leading blanks" //usage: "\n -c Check whether input is sorted" //usage: "\n -d Dictionary order (blank or alphanumeric only)" //usage: "\n -f Ignore case" //usage: "\n -g General numerical sort" //usage: "\n -i Ignore unprintable characters" //usage: "\n -k Sort key" //usage: "\n -M Sort month" //usage: ) //usage: "\n -n Sort numbers" //usage: IF_FEATURE_SORT_BIG( //usage: "\n -o Output to file" //usage: "\n -k Sort by key" //usage: "\n -t CHAR Key separator" //usage: ) //usage: "\n -r Reverse sort order" //usage: IF_FEATURE_SORT_BIG( //usage: "\n -s Stable (don't sort ties alphabetically)" //usage: ) //usage: "\n -u Suppress duplicate lines" //usage: IF_FEATURE_SORT_BIG( //usage: "\n -z Lines are terminated by NUL, not newline" //usage: "\n -mST Ignored for GNU compatibility") //usage: //usage:#define sort_example_usage //usage: "$ echo -e \"e\\nf\\nb\\nd\\nc\\na\" | sort\n" //usage: "a\n" //usage: "b\n" //usage: "c\n" //usage: "d\n" //usage: "e\n" //usage: "f\n" //usage: IF_FEATURE_SORT_BIG( //usage: "$ echo -e \"c 3\\nb 2\\nd 2\" | $SORT -k 2,2n -k 1,1r\n" //usage: "d 2\n" //usage: "b 2\n" //usage: "c 3\n" //usage: ) //usage: "" #include "libbb.h" /* This is a NOEXEC applet. Be very careful! */ /* sort [-m][-o output][-bdfinru][-t char][-k keydef]... [file...] sort -c [-bdfinru][-t char][-k keydef][file] */ /* These are sort types */ static const char OPT_STR[] ALIGN1 = "ngMucszbrdfimS:T:o:k:t:"; enum { FLAG_n = 1, /* Numeric sort */ FLAG_g = 2, /* Sort using strtod() */ FLAG_M = 4, /* Sort date */ /* ucsz apply to root level only, not keys. b at root level implies bb */ FLAG_u = 8, /* Unique */ FLAG_c = 0x10, /* Check: no output, exit(!ordered) */ FLAG_s = 0x20, /* Stable sort, no ascii fallback at end */ FLAG_z = 0x40, /* Input and output is NUL terminated, not \n */ /* These can be applied to search keys, the previous four can't */ FLAG_b = 0x80, /* Ignore leading blanks */ FLAG_r = 0x100, /* Reverse */ FLAG_d = 0x200, /* Ignore !(isalnum()|isspace()) */ FLAG_f = 0x400, /* Force uppercase */ FLAG_i = 0x800, /* Ignore !isprint() */ FLAG_m = 0x1000, /* ignored: merge already sorted files; do not sort */ FLAG_S = 0x2000, /* ignored: -S, --buffer-size=SIZE */ FLAG_T = 0x4000, /* ignored: -T, --temporary-directory=DIR */ FLAG_o = 0x8000, FLAG_k = 0x10000, FLAG_t = 0x20000, FLAG_bb = 0x80000000, /* Ignore trailing blanks */ }; #if ENABLE_FEATURE_SORT_BIG static char key_separator; static struct sort_key { struct sort_key *next_key; /* linked list */ unsigned range[4]; /* start word, start char, end word, end char */ unsigned flags; } *key_list; static char *get_key(char *str, struct sort_key *key, int flags) { int start = 0, end = 0, len, j; unsigned i; /* Special case whole string, so we don't have to make a copy */ if (key->range[0] == 1 && !key->range[1] && !key->range[2] && !key->range[3] && !(flags & (FLAG_b | FLAG_d | FLAG_f | FLAG_i | FLAG_bb)) ) { return str; } /* Find start of key on first pass, end on second pass */ len = strlen(str); for (j = 0; j < 2; j++) { if (!key->range[2*j]) end = len; /* Loop through fields */ else { end = 0; for (i = 1; i < key->range[2*j] + j; i++) { if (key_separator) { /* Skip body of key and separator */ while (str[end]) { if (str[end++] == key_separator) break; } } else { /* Skip leading blanks */ while (isspace(str[end])) end++; /* Skip body of key */ while (str[end]) { if (isspace(str[end])) break; end++; } } } } if (!j) start = end; } /* Strip leading whitespace if necessary */ //XXX: skip_whitespace() if (flags & FLAG_b) while (isspace(str[start])) start++; /* Strip trailing whitespace if necessary */ if (flags & FLAG_bb) while (end > start && isspace(str[end-1])) end--; /* Handle offsets on start and end */ if (key->range[3]) { end += key->range[3] - 1; if (end > len) end = len; } if (key->range[1]) { start += key->range[1] - 1; if (start > len) start = len; } /* Make the copy */ if (end < start) end = start; str = xstrndup(str+start, end-start); /* Handle -d */ if (flags & FLAG_d) { for (start = end = 0; str[end]; end++) if (isspace(str[end]) || isalnum(str[end])) str[start++] = str[end]; str[start] = '\0'; } /* Handle -i */ if (flags & FLAG_i) { for (start = end = 0; str[end]; end++) if (isprint_asciionly(str[end])) str[start++] = str[end]; str[start] = '\0'; } /* Handle -f */ if (flags & FLAG_f) for (i = 0; str[i]; i++) str[i] = toupper(str[i]); return str; } static struct sort_key *add_key(void) { struct sort_key **pkey = &key_list; while (*pkey) pkey = &((*pkey)->next_key); return *pkey = xzalloc(sizeof(struct sort_key)); } #define GET_LINE(fp) \ ((option_mask32 & FLAG_z) \ ? bb_get_chunk_from_file(fp, NULL) \ : xmalloc_fgetline(fp)) #else #define GET_LINE(fp) xmalloc_fgetline(fp) #endif /* Iterate through keys list and perform comparisons */ static int compare_keys(const void *xarg, const void *yarg) { int flags = option_mask32, retval = 0; char *x, *y; #if ENABLE_FEATURE_SORT_BIG struct sort_key *key; for (key = key_list; !retval && key; key = key->next_key) { flags = key->flags ? key->flags : option_mask32; /* Chop out and modify key chunks, handling -dfib */ x = get_key(*(char **)xarg, key, flags); y = get_key(*(char **)yarg, key, flags); #else /* This curly bracket serves no purpose but to match the nesting * level of the for () loop we're not using */ { x = *(char **)xarg; y = *(char **)yarg; #endif /* Perform actual comparison */ switch (flags & 7) { default: bb_error_msg_and_die("unknown sort type"); break; /* Ascii sort */ case 0: #if ENABLE_LOCALE_SUPPORT retval = strcoll(x, y); #else retval = strcmp(x, y); #endif break; #if ENABLE_FEATURE_SORT_BIG case FLAG_g: { char *xx, *yy; double dx = strtod(x, &xx); double dy = strtod(y, &yy); /* not numbers < NaN < -infinity < numbers < +infinity) */ if (x == xx) retval = (y == yy ? 0 : -1); else if (y == yy) retval = 1; /* Check for isnan */ else if (dx != dx) retval = (dy != dy) ? 0 : -1; else if (dy != dy) retval = 1; /* Check for infinity. Could underflow, but it avoids libm. */ else if (1.0 / dx == 0.0) { if (dx < 0) retval = (1.0 / dy == 0.0 && dy < 0) ? 0 : -1; else retval = (1.0 / dy == 0.0 && dy > 0) ? 0 : 1; } else if (1.0 / dy == 0.0) retval = (dy < 0) ? 1 : -1; else retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0); break; } case FLAG_M: { struct tm thyme; int dx; char *xx, *yy; xx = strptime(x, "%b", &thyme); dx = thyme.tm_mon; yy = strptime(y, "%b", &thyme); if (!xx) retval = (!yy) ? 0 : -1; else if (!yy) retval = 1; else retval = (dx == thyme.tm_mon) ? 0 : dx - thyme.tm_mon; break; } /* Full floating point version of -n */ case FLAG_n: { double dx = atof(x); double dy = atof(y); retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0); break; } } /* switch */ /* Free key copies. */ if (x != *(char **)xarg) free(x); if (y != *(char **)yarg) free(y); /* if (retval) break; - done by for () anyway */ #else /* Integer version of -n for tiny systems */ case FLAG_n: retval = atoi(x) - atoi(y); break; } /* switch */ #endif } /* for */ /* Perform fallback sort if necessary */ if (!retval && !(option_mask32 & FLAG_s)) retval = strcmp(*(char **)xarg, *(char **)yarg); if (flags & FLAG_r) return -retval; return retval; } #if ENABLE_FEATURE_SORT_BIG static unsigned str2u(char **str) { unsigned long lu; if (!isdigit((*str)[0])) bb_error_msg_and_die("bad field specification"); lu = strtoul(*str, str, 10); if ((sizeof(long) > sizeof(int) && lu > INT_MAX) || !lu) bb_error_msg_and_die("bad field specification"); return lu; } #endif int sort_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int sort_main(int argc UNUSED_PARAM, char **argv) { char *line, **lines; char *str_ignored, *str_o, *str_t; llist_t *lst_k = NULL; int i, flag; int linecount; unsigned opts; xfunc_error_retval = 2; /* Parse command line options */ /* -o and -t can be given at most once */ opt_complementary = "o--o:t--t:" /* -t, -o: at most one of each */ "k::"; /* -k takes list */ opts = getopt32(argv, OPT_STR, &str_ignored, &str_ignored, &str_o, &lst_k, &str_t); /* global b strips leading and trailing spaces */ if (opts & FLAG_b) option_mask32 |= FLAG_bb; #if ENABLE_FEATURE_SORT_BIG if (opts & FLAG_t) { if (!str_t[0] || str_t[1]) bb_error_msg_and_die("bad -t parameter"); key_separator = str_t[0]; } /* note: below this point we use option_mask32, not opts, * since that reduces register pressure and makes code smaller */ /* parse sort key */ while (lst_k) { enum { FLAG_allowed_for_k = FLAG_n | /* Numeric sort */ FLAG_g | /* Sort using strtod() */ FLAG_M | /* Sort date */ FLAG_b | /* Ignore leading blanks */ FLAG_r | /* Reverse */ FLAG_d | /* Ignore !(isalnum()|isspace()) */ FLAG_f | /* Force uppercase */ FLAG_i | /* Ignore !isprint() */ 0 }; struct sort_key *key = add_key(); char *str_k = llist_pop(&lst_k); i = 0; /* i==0 before comma, 1 after (-k3,6) */ while (*str_k) { /* Start of range */ /* Cannot use bb_strtou - suffix can be a letter */ key->range[2*i] = str2u(&str_k); if (*str_k == '.') { str_k++; key->range[2*i+1] = str2u(&str_k); } while (*str_k) { const char *temp2; if (*str_k == ',' && !i++) { str_k++; break; } /* no else needed: fall through to syntax error because comma isn't in OPT_STR */ temp2 = strchr(OPT_STR, *str_k); if (!temp2) bb_error_msg_and_die("unknown key option"); flag = 1 << (temp2 - OPT_STR); if (flag & ~FLAG_allowed_for_k) bb_error_msg_and_die("unknown sort type"); /* b after ',' means strip _trailing_ space */ if (i && flag == FLAG_b) flag = FLAG_bb; key->flags |= flag; str_k++; } } } #endif /* Open input files and read data */ argv += optind; if (!*argv) *--argv = (char*)"-"; linecount = 0; lines = NULL; do { /* coreutils 6.9 compat: abort on first open error, * do not continue to next file: */ FILE *fp = xfopen_stdin(*argv); for (;;) { line = GET_LINE(fp); if (!line) break; lines = xrealloc_vector(lines, 6, linecount); lines[linecount++] = line; } fclose_if_not_stdin(fp); } while (*++argv); #if ENABLE_FEATURE_SORT_BIG /* if no key, perform alphabetic sort */ if (!key_list) add_key()->range[0] = 1; /* handle -c */ if (option_mask32 & FLAG_c) { int j = (option_mask32 & FLAG_u) ? -1 : 0; for (i = 1; i < linecount; i++) { if (compare_keys(&lines[i-1], &lines[i]) > j) { fprintf(stderr, "Check line %u\n", i); return EXIT_FAILURE; } } return EXIT_SUCCESS; } #endif /* Perform the actual sort */ qsort(lines, linecount, sizeof(lines[0]), compare_keys); /* handle -u */ if (option_mask32 & FLAG_u) { flag = 0; /* coreutils 6.3 drop lines for which only key is the same */ /* -- disabling last-resort compare... */ option_mask32 |= FLAG_s; for (i = 1; i < linecount; i++) { if (compare_keys(&lines[flag], &lines[i]) == 0) free(lines[i]); else lines[++flag] = lines[i]; } if (linecount) linecount = flag+1; } /* Print it */ #if ENABLE_FEATURE_SORT_BIG /* Open output file _after_ we read all input ones */ if (option_mask32 & FLAG_o) xmove_fd(xopen(str_o, O_WRONLY|O_CREAT|O_TRUNC), STDOUT_FILENO); #endif flag = (option_mask32 & FLAG_z) ? '\0' : '\n'; for (i = 0; i < linecount; i++) printf("%s%c", lines[i], flag); fflush_stdout_and_exit(EXIT_SUCCESS); } busybox-1.22.1/coreutils/hostid.c0000644000000000000000000000213512263563520015442 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini hostid implementation for busybox * * Copyright (C) 2000 Edward Betts . * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ //config:config HOSTID //config: bool "hostid" //config: default y //config: help //config: hostid prints the numeric identifier (in hexadecimal) for //config: the current host. //applet:IF_HOSTID(APPLET_NOFORK(hostid, hostid, BB_DIR_USR_BIN, BB_SUID_DROP, hostid)) //kbuild:lib-$(CONFIG_HOSTID) += hostid.o //usage:#define hostid_trivial_usage //usage: "" //usage:#define hostid_full_usage "\n\n" //usage: "Print out a unique 32-bit identifier for the machine" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ int hostid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int hostid_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { if (argv[1]) { bb_show_usage(); } /* POSIX says gethostid returns a "32-bit identifier" */ printf("%08x\n", (unsigned)(uint32_t)gethostid()); return fflush_all(); } busybox-1.22.1/coreutils/printenv.c0000644000000000000000000000224412263563520016016 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * printenv implementation for busybox * * Copyright (C) 2005 by Erik Andersen * Copyright (C) 2005 by Mike Frysinger * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define printenv_trivial_usage //usage: "[VARIABLE]..." //usage:#define printenv_full_usage "\n\n" //usage: "Print environment VARIABLEs.\n" //usage: "If no VARIABLE specified, print all." #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ int printenv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int printenv_main(int argc UNUSED_PARAM, char **argv) { int exit_code = EXIT_SUCCESS; /* no variables specified, show whole env */ if (!argv[1]) { char **e = environ; /* environ can be NULL! (for example, after clearenv()) * Check for that: */ if (e) while (*e) puts(*e++); } else { /* search for specified variables and print them out if found */ char *arg, *env; while ((arg = *++argv) != NULL) { env = getenv(arg); if (env) puts(env); else exit_code = EXIT_FAILURE; } } fflush_stdout_and_exit(exit_code); } busybox-1.22.1/coreutils/Config.src0000644000000000000000000004054512267106022015723 0ustar rootroot# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Coreutils" INSERT config CAL bool "cal" default y help cal is used to display a monthly calender. config CATV bool "catv" default y help Display nonprinting characters as escape sequences (like some implementations' cat -v option). config CHGRP bool "chgrp" default y help chgrp is used to change the group ownership of files. config CHMOD bool "chmod" default y help chmod is used to change the access permission of files. config CHOWN bool "chown" default y help chown is used to change the user and/or group ownership of files. config FEATURE_CHOWN_LONG_OPTIONS bool "Enable long options" default y depends on CHOWN && LONG_OPTS help Enable use of long options config CHROOT bool "chroot" default y help chroot is used to change the root directory and run a command. The default command is `/bin/sh'. config CKSUM bool "cksum" default y help cksum is used to calculate the CRC32 checksum of a file. config COMM bool "comm" default y help comm is used to compare two files line by line and return a three-column output. config CP bool "cp" default y help cp is used to copy files and directories. config FEATURE_CP_LONG_OPTIONS bool "Enable long options for cp" default y depends on CP && LONG_OPTS help Enable long options for cp. Also add support for --parents option. config CUT bool "cut" default y help cut is used to print selected parts of lines from each file to stdout. config DD bool "dd" default y help dd copies a file (from standard input to standard output, by default) using specific input and output blocksizes, while optionally performing conversions on it. config FEATURE_DD_SIGNAL_HANDLING bool "Enable DD signal handling for status reporting" default y depends on DD help Sending a SIGUSR1 signal to a running `dd' process makes it print to standard error the number of records read and written so far, then to resume copying. $ dd if=/dev/zero of=/dev/null& $ pid=$! kill -USR1 $pid; sleep 1; kill $pid 10899206+0 records in 10899206+0 records out config FEATURE_DD_THIRD_STATUS_LINE bool "Enable the third status line upon signal" default y depends on DD && FEATURE_DD_SIGNAL_HANDLING help Displays a coreutils-like third status line with transferred bytes, elapsed time and speed. config FEATURE_DD_IBS_OBS bool "Enable ibs, obs and conv options" default y depends on DD help Enables support for writing a certain number of bytes in and out, at a time, and performing conversions on the data stream. config DF bool "df" default y help df reports the amount of disk space used and available on filesystems. config FEATURE_DF_FANCY bool "Enable -a, -i, -B" default y depends on DF help This option enables -a, -i and -B. -a Show all filesystems -i Inodes -B Blocksize config DIRNAME bool "dirname" default y help dirname is used to strip a non-directory suffix from a file name. config DOS2UNIX bool "dos2unix/unix2dos" default y help dos2unix is used to convert a text file from DOS format to UNIX format, and vice versa. config UNIX2DOS bool default y depends on DOS2UNIX help unix2dos is used to convert a text file from UNIX format to DOS format, and vice versa. config DU bool "du (default blocksize of 512 bytes)" default y help du is used to report the amount of disk space used for specified files. config FEATURE_DU_DEFAULT_BLOCKSIZE_1K bool "Use a default blocksize of 1024 bytes (1K)" default y depends on DU help Use a blocksize of (1K) instead of the default 512b. config ECHO bool "echo (basic SuSv3 version taking no options)" default y help echo is used to print a specified string to stdout. # this entry also appears in shell/Config.in, next to the echo builtin config FEATURE_FANCY_ECHO bool "Enable echo options (-n and -e)" default y depends on ECHO || ASH_BUILTIN_ECHO || HUSH help This adds options (-n and -e) to echo. config ENV bool "env" default y help env is used to set an environment variable and run a command; without options it displays the current environment. config FEATURE_ENV_LONG_OPTIONS bool "Enable long options" default y depends on ENV && LONG_OPTS help Support long options for the env applet. config EXPAND bool "expand" default y help By default, convert all tabs to spaces. config FEATURE_EXPAND_LONG_OPTIONS bool "Enable long options" default y depends on EXPAND && LONG_OPTS help Support long options for the expand applet. config EXPR bool "expr" default y help expr is used to calculate numbers and print the result to standard output. config EXPR_MATH_SUPPORT_64 bool "Extend Posix numbers support to 64 bit" default y depends on EXPR help Enable 64-bit math support in the expr applet. This will make the applet slightly larger, but will allow computation with very large numbers. config FALSE bool "false" default y help false returns an exit code of FALSE (1). config FOLD bool "fold" default y help Wrap text to fit a specific width. config FSYNC bool "fsync" default y help fsync is used to flush file-related cached blocks to disk. config HEAD bool "head" default y help head is used to print the first specified number of lines from files. config FEATURE_FANCY_HEAD bool "Enable head options (-c, -q, and -v)" default y depends on HEAD help This enables the head options (-c, -q, and -v). config INSTALL bool "install" default y help Copy files and set attributes. config FEATURE_INSTALL_LONG_OPTIONS bool "Enable long options" default y depends on INSTALL && LONG_OPTS help Support long options for the install applet. ####config LENGTH #### bool "length" #### default y #### help #### length is used to print out the length of a specified string. config LN bool "ln" default y help ln is used to create hard or soft links between files. config LOGNAME bool "logname" default y help logname is used to print the current user's login name. config LS bool "ls" default y help ls is used to list the contents of directories. config FEATURE_LS_FILETYPES bool "Enable filetyping options (-p and -F)" default y depends on LS help Enable the ls options (-p and -F). config FEATURE_LS_FOLLOWLINKS bool "Enable symlinks dereferencing (-L)" default y depends on LS help Enable the ls option (-L). config FEATURE_LS_RECURSIVE bool "Enable recursion (-R)" default y depends on LS help Enable the ls option (-R). config FEATURE_LS_SORTFILES bool "Sort the file names" default y depends on LS help Allow ls to sort file names alphabetically. config FEATURE_LS_TIMESTAMPS bool "Show file timestamps" default y depends on LS help Allow ls to display timestamps for files. config FEATURE_LS_USERNAME bool "Show username/groupnames" default y depends on LS help Allow ls to display username/groupname for files. config FEATURE_LS_COLOR bool "Allow use of color to identify file types" default y depends on LS && LONG_OPTS help This enables the --color option to ls. config FEATURE_LS_COLOR_IS_DEFAULT bool "Produce colored ls output by default" default y depends on FEATURE_LS_COLOR help Saying yes here will turn coloring on by default, even if no "--color" option is given to the ls command. This is not recommended, since the colors are not configurable, and the output may not be legible on many output screens. config MD5SUM bool "md5sum" default y help md5sum is used to print or check MD5 checksums. config MKDIR bool "mkdir" default y help mkdir is used to create directories with the specified names. config FEATURE_MKDIR_LONG_OPTIONS bool "Enable long options" default y depends on MKDIR && LONG_OPTS help Support long options for the mkdir applet. config MKFIFO bool "mkfifo" default y help mkfifo is used to create FIFOs (named pipes). The `mknod' program can also create FIFOs. config MKNOD bool "mknod" default y help mknod is used to create FIFOs or block/character special files with the specified names. config MV bool "mv" default y help mv is used to move or rename files or directories. config FEATURE_MV_LONG_OPTIONS bool "Enable long options" default y depends on MV && LONG_OPTS help Support long options for the mv applet. config NICE bool "nice" default y help nice runs a program with modified scheduling priority. config NOHUP bool "nohup" default y help run a command immune to hangups, with output to a non-tty. config OD bool "od" default y help od is used to dump binary files in octal and other formats. config PRINTENV bool "printenv" default y help printenv is used to print all or part of environment. config PRINTF bool "printf" default y help printf is used to format and print specified strings. It's similar to `echo' except it has more options. config PWD bool "pwd" default y help pwd is used to print the current directory. config READLINK bool "readlink" default y help This program reads a symbolic link and returns the name of the file it points to config FEATURE_READLINK_FOLLOW bool "Enable canonicalization by following all symlinks (-f)" default y depends on READLINK help Enable the readlink option (-f). config REALPATH bool "realpath" default y help Return the canonicalized absolute pathname. This isn't provided by GNU shellutils, but where else does it belong. config RM bool "rm" default y help rm is used to remove files or directories. config RMDIR bool "rmdir" default y help rmdir is used to remove empty directories. config FEATURE_RMDIR_LONG_OPTIONS bool "Enable long options" default y depends on RMDIR && LONG_OPTS help Support long options for the rmdir applet, including --ignore-fail-on-non-empty for compatibility with GNU rmdir. config SEQ bool "seq" default y help print a sequence of numbers config SHA1SUM bool "sha1sum" default y help Compute and check SHA1 message digest config SHA256SUM bool "sha256sum" default y help Compute and check SHA256 message digest config SHA512SUM bool "sha512sum" default y help Compute and check SHA512 message digest config SHA3SUM bool "sha3sum" default y help Compute and check SHA3 (512-bit) message digest config SLEEP bool "sleep" default y help sleep is used to pause for a specified number of seconds. It comes in 3 versions: - small: takes one integer parameter - fancy: takes multiple integer arguments with suffixes: sleep 1d 2h 3m 15s - fancy with fractional numbers: sleep 2.3s 4.5h sleeps for 16202.3 seconds Last one is "the most compatible" with coreutils sleep, but it adds around 1k of code. config FEATURE_FANCY_SLEEP bool "Enable multiple arguments and s/m/h/d suffixes" default y depends on SLEEP help Allow sleep to pause for specified minutes, hours, and days. config FEATURE_FLOAT_SLEEP bool "Enable fractional arguments" default y depends on FEATURE_FANCY_SLEEP help Allow for fractional numeric parameters. config SORT bool "sort" default y help sort is used to sort lines of text in specified files. config FEATURE_SORT_BIG bool "Full SuSv3 compliant sort (support -ktcsbdfiozgM)" default y depends on SORT help Without this, sort only supports -r, -u, and an integer version of -n. Selecting this adds sort keys, floating point support, and more. This adds a little over 3k to a nonstatic build on x86. The SuSv3 sort standard is available at: http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html config SPLIT bool "split" default y help split a file into pieces. config FEATURE_SPLIT_FANCY bool "Fancy extensions" default y depends on SPLIT help Add support for features not required by SUSv3. Supports additional suffixes 'b' for 512 bytes, 'g' for 1GiB for the -b option. config STAT bool "stat" default y select PLATFORM_LINUX # statfs() help display file or filesystem status. config FEATURE_STAT_FORMAT bool "Enable custom formats (-c)" default y depends on STAT help Without this, stat will not support the '-c format' option where users can pass a custom format string for output. This adds about 7k to a nonstatic build on amd64. config STTY bool "stty" default y help stty is used to change and print terminal line settings. config SUM bool "sum" default y help checksum and count the blocks in a file config SYNC bool "sync" default y help sync is used to flush filesystem buffers. config TAC bool "tac" default y help tac is used to concatenate and print files in reverse. config TAIL bool "tail" default y help tail is used to print the last specified number of lines from files. config FEATURE_FANCY_TAIL bool "Enable extra tail options (-q, -s, -v, and -F)" default y depends on TAIL help The options (-q, -s, and -v) are provided by GNU tail, but are not specific in the SUSv3 standard. -q Never output headers giving file names -s SEC Wait SEC seconds between reads with -f -v Always output headers giving file names config TEE bool "tee" default y help tee is used to read from standard input and write to standard output and files. config FEATURE_TEE_USE_BLOCK_IO bool "Enable block I/O (larger/faster) instead of byte I/O" default y depends on TEE help Enable this option for a faster tee, at expense of size. config TRUE bool "true" default y help true returns an exit code of TRUE (0). config TTY bool "tty" default y help tty is used to print the name of the current terminal to standard output. config UNAME bool "uname" default y help uname is used to print system information. config UNEXPAND bool "unexpand" default y help By default, convert only leading sequences of blanks to tabs. config FEATURE_UNEXPAND_LONG_OPTIONS bool "Enable long options" default y depends on UNEXPAND && LONG_OPTS help Support long options for the unexpand applet. config UNIQ bool "uniq" default y help uniq is used to remove duplicate lines from a sorted file. config USLEEP bool "usleep" default y help usleep is used to pause for a specified number of microseconds. config UUDECODE bool "uudecode" default y help uudecode is used to decode a uuencoded file. config UUENCODE bool "uuencode" default y help uuencode is used to uuencode a file. config WC bool "wc" default y help wc is used to print the number of bytes, words, and lines, in specified files. config FEATURE_WC_LARGE bool "Support very large files in wc" default y depends on WC help Use "unsigned long long" in wc for counter variables. config WHOAMI bool "whoami" default y help whoami is used to print the username of the current user id (same as id -un). config YES bool "yes" default y help yes is used to repeatedly output a specific string, or the default string `y'. comment "Common options for cp and mv" depends on CP || MV config FEATURE_PRESERVE_HARDLINKS bool "Preserve hard links" default y depends on CP || MV help Allow cp and mv to preserve hard links. comment "Common options for ls, more and telnet" depends on LS || MORE || TELNET config FEATURE_AUTOWIDTH bool "Calculate terminal & column widths" default y depends on LS || MORE || TELNET help This option allows utilities such as 'ls', 'more' and 'telnet' to determine the width of the screen, which can allow them to display additional text or avoid wrapping text onto the next line. If you leave this disabled, your utilities will be especially primitive and will be unable to determine the current screen width. comment "Common options for df, du, ls" depends on DF || DU || LS config FEATURE_HUMAN_READABLE bool "Support for human readable output (example 13k, 23M, 235G)" default y depends on DF || DU || LS help Allow df, du, and ls to have human readable output. comment "Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum" depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM config FEATURE_MD5_SHA1_SUM_CHECK bool "Enable -c, -s and -w options" default y depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM help Enabling the -c options allows files to be checked against pre-calculated hash values. -s and -w are useful options when verifying checksums. endmenu busybox-1.22.1/coreutils/seq.c0000644000000000000000000000466112263563520014746 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * seq implementation for busybox * * Copyright (C) 2004, Glenn McGrath * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define seq_trivial_usage //usage: "[-w] [-s SEP] [FIRST [INC]] LAST" //usage:#define seq_full_usage "\n\n" //usage: "Print numbers from FIRST to LAST, in steps of INC.\n" //usage: "FIRST, INC default to 1.\n" //usage: "\n -w Pad to last with leading zeros" //usage: "\n -s SEP String separator" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ int seq_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int seq_main(int argc, char **argv) { enum { OPT_w = (1 << 0), OPT_s = (1 << 1), }; double first, last, increment, v; unsigned n; unsigned width; unsigned frac_part; const char *sep, *opt_s = "\n"; unsigned opt; #if ENABLE_LOCALE_SUPPORT /* Undo busybox.c: on input, we want to use dot * as fractional separator, regardless of current locale */ setlocale(LC_NUMERIC, "C"); #endif opt = getopt32(argv, "+ws:", &opt_s); argc -= optind; argv += optind; first = increment = 1; errno = 0; switch (argc) { char *pp; case 3: increment = strtod(argv[1], &pp); errno |= *pp; case 2: first = strtod(argv[0], &pp); errno |= *pp; case 1: last = strtod(argv[argc-1], &pp); if (!errno && *pp == '\0') break; default: bb_show_usage(); } #if ENABLE_LOCALE_SUPPORT setlocale(LC_NUMERIC, ""); #endif /* Last checked to be compatible with: coreutils-6.10 */ width = 0; frac_part = 0; while (1) { char *dot = strchrnul(*argv, '.'); int w = (dot - *argv); int f = strlen(dot); if (width < w) width = w; argv++; if (!*argv) break; /* Why do the above _before_ frac check below? * Try "seq 1 2.0" and "seq 1.0 2.0": * coreutils never pay attention to the number * of fractional digits in last arg. */ if (frac_part < f) frac_part = f; } if (frac_part) { frac_part--; if (frac_part) width += frac_part + 1; } if (!(opt & OPT_w)) width = 0; sep = ""; v = first; n = 0; while (increment >= 0 ? v <= last : v >= last) { if (printf("%s%0*.*f", sep, width, frac_part, v) < 0) break; /* I/O error, bail out (yes, this really happens) */ sep = opt_s; /* v += increment; - would accumulate floating point errors */ n++; v = first + n * increment; } if (n) /* if while loop executed at least once */ bb_putchar('\n'); return fflush_all(); } busybox-1.22.1/coreutils/touch.c0000644000000000000000000001316512263563520015277 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini touch implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 _NOT_ compliant -- options -a, -m not supported. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/touch.html */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Previous version called open() and then utime(). While this will be * be necessary to implement -r and -t, it currently only makes things bigger. * Also, exiting on a failure was a bug. All args should be processed. */ #include "libbb.h" //config:config TOUCH //config: bool "touch" //config: default y //config: help //config: touch is used to create or change the access and/or //config: modification timestamp of specified files. //config: //config:config FEATURE_TOUCH_NODEREF //config: bool "Add support for -h" //config: default y //config: depends on TOUCH //config: help //config: Enable touch to have the -h option. //config: This requires libc support for lutimes() function. //config: //config:config FEATURE_TOUCH_SUSV3 //config: bool "Add support for SUSV3 features (-d -t -r)" //config: default y //config: depends on TOUCH //config: help //config: Enable touch to use a reference file or a given date/time argument. //applet:IF_TOUCH(APPLET_NOFORK(touch, touch, BB_DIR_BIN, BB_SUID_DROP, touch)) //kbuild:lib-$(CONFIG_TOUCH) += touch.o //usage:#define touch_trivial_usage //usage: "[-c]" IF_FEATURE_TOUCH_SUSV3(" [-d DATE] [-t DATE] [-r FILE]") " FILE..." //usage:#define touch_full_usage "\n\n" //usage: "Update the last-modified date on the given FILE[s]\n" //usage: "\n -c Don't create files" //usage: IF_FEATURE_TOUCH_NODEREF( //usage: "\n -h Don't follow links" //usage: ) //usage: IF_FEATURE_TOUCH_SUSV3( //usage: "\n -d DT Date/time to use" //usage: "\n -t DT Date/time to use" //usage: "\n -r FILE Use FILE's date/time" //usage: ) //usage: //usage:#define touch_example_usage //usage: "$ ls -l /tmp/foo\n" //usage: "/bin/ls: /tmp/foo: No such file or directory\n" //usage: "$ touch /tmp/foo\n" //usage: "$ ls -l /tmp/foo\n" //usage: "-rw-rw-r-- 1 andersen andersen 0 Apr 15 01:11 /tmp/foo\n" /* This is a NOFORK applet. Be very careful! */ /* coreutils implements: * -a change only the access time * -c, --no-create * do not create any files * -d, --date=STRING * parse STRING and use it instead of current time * -f (ignored, BSD compat) * -m change only the modification time * -h, --no-dereference * -r, --reference=FILE * use this file's times instead of current time * -t STAMP * use [[CC]YY]MMDDhhmm[.ss] instead of current time * --time=WORD * change the specified time: WORD is access, atime, or use */ int touch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int touch_main(int argc UNUSED_PARAM, char **argv) { int fd; int status = EXIT_SUCCESS; int opts; enum { OPT_c = (1 << 0), OPT_r = (1 << 1) * ENABLE_FEATURE_TOUCH_SUSV3, OPT_d = (1 << 2) * ENABLE_FEATURE_TOUCH_SUSV3, OPT_t = (1 << 3) * ENABLE_FEATURE_TOUCH_SUSV3, OPT_h = (1 << 4) * ENABLE_FEATURE_TOUCH_NODEREF, }; #if ENABLE_FEATURE_TOUCH_SUSV3 # if ENABLE_LONG_OPTS static const char touch_longopts[] ALIGN1 = /* name, has_arg, val */ "no-create\0" No_argument "c" "reference\0" Required_argument "r" "date\0" Required_argument "d" IF_FEATURE_TOUCH_NODEREF("no-dereference\0" No_argument "h") ; # endif char *reference_file = NULL; char *date_str = NULL; struct timeval timebuf[2]; timebuf[1].tv_usec = timebuf[0].tv_usec = 0; #else # define reference_file NULL # define date_str NULL # define timebuf ((struct timeval*)NULL) #endif #if ENABLE_FEATURE_TOUCH_SUSV3 && ENABLE_LONG_OPTS applet_long_options = touch_longopts; #endif /* -d and -t both set time. In coreutils, * accepted data format differs a bit between -d and -t. * We accept the same formats for both */ opts = getopt32(argv, "c" IF_FEATURE_TOUCH_SUSV3("r:d:t:") IF_FEATURE_TOUCH_NODEREF("h") /*ignored:*/ "fma" IF_FEATURE_TOUCH_SUSV3(, &reference_file) IF_FEATURE_TOUCH_SUSV3(, &date_str) IF_FEATURE_TOUCH_SUSV3(, &date_str) ); argv += optind; if (!*argv) { bb_show_usage(); } if (reference_file) { struct stat stbuf; xstat(reference_file, &stbuf); timebuf[1].tv_sec = timebuf[0].tv_sec = stbuf.st_mtime; /* Can use .st_mtim.tv_nsec * (or is it .st_mtimensec?? see date.c) * to set microseconds too. */ } if (date_str) { struct tm tm_time; time_t t; //memset(&tm_time, 0, sizeof(tm_time)); /* Better than memset: makes "HH:MM" dates meaningful */ time(&t); localtime_r(&t, &tm_time); parse_datestr(date_str, &tm_time); /* Correct any day of week and day of year etc. fields */ tm_time.tm_isdst = -1; /* Be sure to recheck dst */ t = validate_tm_time(date_str, &tm_time); timebuf[1].tv_sec = timebuf[0].tv_sec = t; } do { int result; result = ( #if ENABLE_FEATURE_TOUCH_NODEREF (opts & OPT_h) ? lutimes : #endif utimes)(*argv, (reference_file || date_str) ? timebuf : NULL); if (result != 0) { if (errno == ENOENT) { /* no such file? */ if (opts & OPT_c) { /* Creation is disabled, so ignore */ continue; } /* Try to create the file */ fd = open(*argv, O_RDWR | O_CREAT, 0666); if (fd >= 0) { xclose(fd); if (reference_file || date_str) utimes(*argv, timebuf); continue; } } status = EXIT_FAILURE; bb_simple_perror_msg(*argv); } } while (*++argv); return status; } busybox-1.22.1/coreutils/whoami.c0000644000000000000000000000141512263563520015434 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini whoami implementation for busybox * * Copyright (C) 2000 Edward Betts . * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ //usage:#define whoami_trivial_usage //usage: "" //usage:#define whoami_full_usage "\n\n" //usage: "Print the user name associated with the current effective user id" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ int whoami_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int whoami_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { if (argv[1]) bb_show_usage(); /* Will complain and die if username not found */ puts(xuid2uname(geteuid())); return fflush_all(); } busybox-1.22.1/coreutils/nice.c0000644000000000000000000000265412263563520015074 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * nice implementation for busybox * * Copyright (C) 2005 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define nice_trivial_usage //usage: "[-n ADJUST] [PROG ARGS]" //usage:#define nice_full_usage "\n\n" //usage: "Change scheduling priority, run PROG\n" //usage: "\n -n ADJUST Adjust priority by ADJUST" #include #include "libbb.h" int nice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int nice_main(int argc, char **argv) { int old_priority, adjustment; old_priority = getpriority(PRIO_PROCESS, 0); if (!*++argv) { /* No args, so (GNU) output current nice value. */ printf("%d\n", old_priority); fflush_stdout_and_exit(EXIT_SUCCESS); } adjustment = 10; /* Set default adjustment. */ if (argv[0][0] == '-') { if (argv[0][1] == 'n') { /* -n */ if (argv[0][2]) { /* -nNNNN (w/o space) */ argv[0] += 2; argv--; argc++; } } else { /* -NNN (NNN may be negative) == -n NNN */ argv[0] += 1; argv--; argc++; } if (argc < 4) { /* Missing priority and/or utility! */ bb_show_usage(); } adjustment = xatoi_range(argv[1], INT_MIN/2, INT_MAX/2); argv += 2; } { /* Set our priority. */ int prio = old_priority + adjustment; if (setpriority(PRIO_PROCESS, 0, prio) < 0) { bb_perror_msg_and_die("setpriority(%d)", prio); } } BB_EXECVP_or_die(argv); } busybox-1.22.1/coreutils/dos2unix.c0000644000000000000000000000537312263563520015732 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * dos2unix for BusyBox * * dos2unix '\n' convertor 0.5.0 * based on Unix2Dos 0.9.0 by Peter Hanecak (made 19.2.1997) * Copyright 1997,.. by Peter Hanecak . * All rights reserved. * * dos2unix filters reading input from stdin and writing output to stdout. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define dos2unix_trivial_usage //usage: "[-ud] [FILE]" //usage:#define dos2unix_full_usage "\n\n" //usage: "Convert FILE in-place from DOS to Unix format.\n" //usage: "When no file is given, use stdin/stdout.\n" //usage: "\n -u dos2unix" //usage: "\n -d unix2dos" //usage: //usage:#define unix2dos_trivial_usage //usage: "[-ud] [FILE]" //usage:#define unix2dos_full_usage "\n\n" //usage: "Convert FILE in-place from Unix to DOS format.\n" //usage: "When no file is given, use stdin/stdout.\n" //usage: "\n -u dos2unix" //usage: "\n -d unix2dos" #include "libbb.h" /* This is a NOEXEC applet. Be very careful! */ enum { CT_UNIX2DOS = 1, CT_DOS2UNIX }; /* if fn is NULL then input is stdin and output is stdout */ static void convert(char *fn, int conv_type) { FILE *in, *out; int i; char *temp_fn = temp_fn; /* for compiler */ char *resolved_fn = resolved_fn; in = stdin; out = stdout; if (fn != NULL) { struct stat st; resolved_fn = xmalloc_follow_symlinks(fn); if (resolved_fn == NULL) bb_simple_perror_msg_and_die(fn); in = xfopen_for_read(resolved_fn); fstat(fileno(in), &st); temp_fn = xasprintf("%sXXXXXX", resolved_fn); i = xmkstemp(temp_fn); if (fchmod(i, st.st_mode) == -1) bb_simple_perror_msg_and_die(temp_fn); out = xfdopen_for_write(i); } while ((i = fgetc(in)) != EOF) { if (i == '\r') continue; if (i == '\n') if (conv_type == CT_UNIX2DOS) fputc('\r', out); fputc(i, out); } if (fn != NULL) { if (fclose(in) < 0 || fclose(out) < 0) { unlink(temp_fn); bb_perror_nomsg_and_die(); } xrename(temp_fn, resolved_fn); free(temp_fn); free(resolved_fn); } } int dos2unix_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int dos2unix_main(int argc UNUSED_PARAM, char **argv) { int o, conv_type; /* See if we are supposed to be doing dos2unix or unix2dos */ conv_type = CT_UNIX2DOS; if (applet_name[0] == 'd') { conv_type = CT_DOS2UNIX; } /* -u convert to unix, -d convert to dos */ opt_complementary = "u--d:d--u"; /* mutually exclusive */ o = getopt32(argv, "du"); /* Do the conversion requested by an argument else do the default * conversion depending on our name. */ if (o) conv_type = o; argv += optind; do { /* might be convert(NULL) if there is no filename given */ convert(*argv, conv_type); } while (*argv && *++argv); return 0; } busybox-1.22.1/coreutils/mkdir.c0000644000000000000000000000462012263563520015257 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini mkdir implementation for busybox * * Copyright (C) 2001 Matt Kraai * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/mkdir.html */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Fixed broken permission setting when -p was used; especially in * conjunction with -m. */ /* Nov 28, 2006 Yoshinori Sato : Add SELinux Support. */ //usage:#define mkdir_trivial_usage //usage: "[OPTIONS] DIRECTORY..." //usage:#define mkdir_full_usage "\n\n" //usage: "Create DIRECTORY\n" //usage: "\n -m MODE Mode" //usage: "\n -p No error if exists; make parent directories as needed" //usage: IF_SELINUX( //usage: "\n -Z Set security context" //usage: ) //usage: //usage:#define mkdir_example_usage //usage: "$ mkdir /tmp/foo\n" //usage: "$ mkdir /tmp/foo\n" //usage: "/tmp/foo: File exists\n" //usage: "$ mkdir /tmp/foo/bar/baz\n" //usage: "/tmp/foo/bar/baz: No such file or directory\n" //usage: "$ mkdir -p /tmp/foo/bar/baz\n" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ #if ENABLE_FEATURE_MKDIR_LONG_OPTIONS static const char mkdir_longopts[] ALIGN1 = "mode\0" Required_argument "m" "parents\0" No_argument "p" #if ENABLE_SELINUX "context\0" Required_argument "Z" #endif "verbose\0" No_argument "v" ; #endif int mkdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mkdir_main(int argc UNUSED_PARAM, char **argv) { long mode = -1; int status = EXIT_SUCCESS; int flags = 0; unsigned opt; char *smode; #if ENABLE_SELINUX security_context_t scontext; #endif #if ENABLE_FEATURE_MKDIR_LONG_OPTIONS applet_long_options = mkdir_longopts; #endif opt = getopt32(argv, "m:p" IF_SELINUX("Z:") "v", &smode IF_SELINUX(,&scontext)); if (opt & 1) { mode_t mmode = 0777; if (!bb_parse_mode(smode, &mmode)) { bb_error_msg_and_die("invalid mode '%s'", smode); } mode = mmode; } if (opt & 2) flags |= FILEUTILS_RECUR; #if ENABLE_SELINUX if (opt & 4) { selinux_or_die(); setfscreatecon_or_die(scontext); } #endif argv += optind; if (!argv[0]) bb_show_usage(); do { if (bb_make_directory(*argv, mode, flags)) { status = EXIT_FAILURE; } } while (*++argv); return status; } busybox-1.22.1/coreutils/cut.c0000644000000000000000000002066012263563520014746 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * cut.c - minimalist version of cut * * Copyright (C) 1999,2000,2001 by Lineo, inc. * Written by Mark Whitley * debloated by Bernhard Reutner-Fischer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define cut_trivial_usage //usage: "[OPTIONS] [FILE]..." //usage:#define cut_full_usage "\n\n" //usage: "Print selected fields from each input FILE to stdout\n" //usage: "\n -b LIST Output only bytes from LIST" //usage: "\n -c LIST Output only characters from LIST" //usage: "\n -d CHAR Use CHAR instead of tab as the field delimiter" //usage: "\n -s Output only the lines containing delimiter" //usage: "\n -f N Print only these fields" //usage: "\n -n Ignored" //usage: //usage:#define cut_example_usage //usage: "$ echo \"Hello world\" | cut -f 1 -d ' '\n" //usage: "Hello\n" //usage: "$ echo \"Hello world\" | cut -f 2 -d ' '\n" //usage: "world\n" #include "libbb.h" /* This is a NOEXEC applet. Be very careful! */ /* option vars */ static const char optstring[] ALIGN1 = "b:c:f:d:sn"; #define CUT_OPT_BYTE_FLGS (1 << 0) #define CUT_OPT_CHAR_FLGS (1 << 1) #define CUT_OPT_FIELDS_FLGS (1 << 2) #define CUT_OPT_DELIM_FLGS (1 << 3) #define CUT_OPT_SUPPRESS_FLGS (1 << 4) struct cut_list { int startpos; int endpos; }; enum { BOL = 0, EOL = INT_MAX, NON_RANGE = -1 }; static int cmpfunc(const void *a, const void *b) { return (((struct cut_list *) a)->startpos - ((struct cut_list *) b)->startpos); } static void cut_file(FILE *file, char delim, const struct cut_list *cut_lists, unsigned nlists) { char *line; unsigned linenum = 0; /* keep these zero-based to be consistent */ /* go through every line in the file */ while ((line = xmalloc_fgetline(file)) != NULL) { /* set up a list so we can keep track of what's been printed */ int linelen = strlen(line); char *printed = xzalloc(linelen + 1); char *orig_line = line; unsigned cl_pos = 0; int spos; /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */ if (option_mask32 & (CUT_OPT_CHAR_FLGS | CUT_OPT_BYTE_FLGS)) { /* print the chars specified in each cut list */ for (; cl_pos < nlists; cl_pos++) { spos = cut_lists[cl_pos].startpos; while (spos < linelen) { if (!printed[spos]) { printed[spos] = 'X'; putchar(line[spos]); } spos++; if (spos > cut_lists[cl_pos].endpos /* NON_RANGE is -1, so if below is true, * the above was true too (spos is >= 0) */ /* || cut_lists[cl_pos].endpos == NON_RANGE */ ) { break; } } } } else if (delim == '\n') { /* cut by lines */ spos = cut_lists[cl_pos].startpos; /* get out if we have no more lists to process or if the lines * are lower than what we're interested in */ if (((int)linenum < spos) || (cl_pos >= nlists)) goto next_line; /* if the line we're looking for is lower than the one we were * passed, it means we displayed it already, so move on */ while (spos < (int)linenum) { spos++; /* go to the next list if we're at the end of this one */ if (spos > cut_lists[cl_pos].endpos || cut_lists[cl_pos].endpos == NON_RANGE ) { cl_pos++; /* get out if there's no more lists to process */ if (cl_pos >= nlists) goto next_line; spos = cut_lists[cl_pos].startpos; /* get out if the current line is lower than the one * we just became interested in */ if ((int)linenum < spos) goto next_line; } } /* If we made it here, it means we've found the line we're * looking for, so print it */ puts(line); goto next_line; } else { /* cut by fields */ int ndelim = -1; /* zero-based / one-based problem */ int nfields_printed = 0; char *field = NULL; char delimiter[2]; delimiter[0] = delim; delimiter[1] = 0; /* does this line contain any delimiters? */ if (strchr(line, delim) == NULL) { if (!(option_mask32 & CUT_OPT_SUPPRESS_FLGS)) puts(line); goto next_line; } /* process each list on this line, for as long as we've got * a line to process */ for (; cl_pos < nlists && line; cl_pos++) { spos = cut_lists[cl_pos].startpos; do { /* find the field we're looking for */ while (line && ndelim < spos) { field = strsep(&line, delimiter); ndelim++; } /* we found it, and it hasn't been printed yet */ if (field && ndelim == spos && !printed[ndelim]) { /* if this isn't our first time through, we need to * print the delimiter after the last field that was * printed */ if (nfields_printed > 0) putchar(delim); fputs(field, stdout); printed[ndelim] = 'X'; nfields_printed++; /* shouldn't overflow.. */ } spos++; /* keep going as long as we have a line to work with, * this is a list, and we're not at the end of that * list */ } while (spos <= cut_lists[cl_pos].endpos && line && cut_lists[cl_pos].endpos != NON_RANGE); } } /* if we printed anything at all, we need to finish it with a * newline cuz we were handed a chomped line */ putchar('\n'); next_line: linenum++; free(printed); free(orig_line); } } int cut_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int cut_main(int argc UNUSED_PARAM, char **argv) { /* growable array holding a series of lists */ struct cut_list *cut_lists = NULL; unsigned nlists = 0; /* number of elements in above list */ char delim = '\t'; /* delimiter, default is tab */ char *sopt, *ltok; unsigned opt; opt_complementary = "b--bcf:c--bcf:f--bcf"; opt = getopt32(argv, optstring, &sopt, &sopt, &sopt, <ok); // argc -= optind; argv += optind; if (!(opt & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS))) bb_error_msg_and_die("expected a list of bytes, characters, or fields"); if (opt & CUT_OPT_DELIM_FLGS) { if (ltok[0] && ltok[1]) { /* more than 1 char? */ bb_error_msg_and_die("the delimiter must be a single character"); } delim = ltok[0]; } /* non-field (char or byte) cutting has some special handling */ if (!(opt & CUT_OPT_FIELDS_FLGS)) { static const char _op_on_field[] ALIGN1 = " only when operating on fields"; if (opt & CUT_OPT_SUPPRESS_FLGS) { bb_error_msg_and_die ("suppressing non-delimited lines makes sense%s", _op_on_field); } if (delim != '\t') { bb_error_msg_and_die ("a delimiter may be specified%s", _op_on_field); } } /* * parse list and put values into startpos and endpos. * valid list formats: N, N-, N-M, -M * more than one list can be separated by commas */ { char *ntok; int s = 0, e = 0; /* take apart the lists, one by one (they are separated with commas) */ while ((ltok = strsep(&sopt, ",")) != NULL) { /* it's actually legal to pass an empty list */ if (!ltok[0]) continue; /* get the start pos */ ntok = strsep(<ok, "-"); if (!ntok[0]) { s = BOL; } else { s = xatoi_positive(ntok); /* account for the fact that arrays are zero based, while * the user expects the first char on the line to be char #1 */ if (s != 0) s--; } /* get the end pos */ if (ltok == NULL) { e = NON_RANGE; } else if (!ltok[0]) { e = EOL; } else { e = xatoi_positive(ltok); /* if the user specified and end position of 0, * that means "til the end of the line" */ if (e == 0) e = EOL; e--; /* again, arrays are zero based, lines are 1 based */ if (e == s) e = NON_RANGE; } /* add the new list */ cut_lists = xrealloc_vector(cut_lists, 4, nlists); /* NB: startpos is always >= 0, * while endpos may be = NON_RANGE (-1) */ cut_lists[nlists].startpos = s; cut_lists[nlists].endpos = e; nlists++; } /* make sure we got some cut positions out of all that */ if (nlists == 0) bb_error_msg_and_die("missing list of positions"); /* now that the lists are parsed, we need to sort them to make life * easier on us when it comes time to print the chars / fields / lines */ qsort(cut_lists, nlists, sizeof(cut_lists[0]), cmpfunc); } { int retval = EXIT_SUCCESS; if (!*argv) *--argv = (char *)"-"; do { FILE *file = fopen_or_warn_stdin(*argv); if (!file) { retval = EXIT_FAILURE; continue; } cut_file(file, delim, cut_lists, nlists); fclose_if_not_stdin(file); } while (*++argv); if (ENABLE_FEATURE_CLEAN_UP) free(cut_lists); fflush_stdout_and_exit(retval); } } busybox-1.22.1/coreutils/du.c0000644000000000000000000001643012263563520014563 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini du implementation for busybox * * Copyright (C) 1999,2000,2001 by Lineo, inc. and John Beppu * Copyright (C) 1999,2000,2001 by John Beppu * Copyright (C) 2002 Edward Betts * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Mostly rewritten for SUSv3 compliance and to fix bugs/defects. * 1) Added support for SUSv3 -a, -H, -L, gnu -c, and (busybox) -d options. * The -d option allows setting of max depth (similar to gnu --max-depth). * 2) Fixed incorrect size calculations for links and directories, especially * when errors occurred. Calculates sizes should now match gnu du output. * 3) Added error checking of output. * 4) Fixed busybox bug #1284 involving long overflow with human_readable. */ //usage:#define du_trivial_usage //usage: "[-aHLdclsx" IF_FEATURE_HUMAN_READABLE("hm") "k] [FILE]..." //usage:#define du_full_usage "\n\n" //usage: "Summarize disk space used for each FILE and/or directory\n" //usage: "\n -a Show file sizes too" //usage: "\n -L Follow all symlinks" //usage: "\n -H Follow symlinks on command line" //usage: "\n -d N Limit output to directories (and files with -a) of depth < N" //usage: "\n -c Show grand total" //usage: "\n -l Count sizes many times if hard linked" //usage: "\n -s Display only a total for each argument" //usage: "\n -x Skip directories on different filesystems" //usage: IF_FEATURE_HUMAN_READABLE( //usage: "\n -h Sizes in human readable format (e.g., 1K 243M 2G)" //usage: "\n -m Sizes in megabytes" //usage: ) //usage: "\n -k Sizes in kilobytes" IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(" (default)") //usage: IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K( //usage: "\n Default unit is 512 bytes" //usage: ) //usage: //usage:#define du_example_usage //usage: "$ du\n" //usage: "16 ./CVS\n" //usage: "12 ./kernel-patches/CVS\n" //usage: "80 ./kernel-patches\n" //usage: "12 ./tests/CVS\n" //usage: "36 ./tests\n" //usage: "12 ./scripts/CVS\n" //usage: "16 ./scripts\n" //usage: "12 ./docs/CVS\n" //usage: "104 ./docs\n" //usage: "2417 .\n" #include "libbb.h" enum { OPT_a_files_too = (1 << 0), OPT_H_follow_links = (1 << 1), OPT_k_kbytes = (1 << 2), OPT_L_follow_links = (1 << 3), OPT_s_total_norecurse = (1 << 4), OPT_x_one_FS = (1 << 5), OPT_d_maxdepth = (1 << 6), OPT_l_hardlinks = (1 << 7), OPT_c_total = (1 << 8), OPT_h_for_humans = (1 << 9), OPT_m_mbytes = (1 << 10), }; struct globals { #if ENABLE_FEATURE_HUMAN_READABLE unsigned long disp_hr; #else unsigned disp_k; #endif int max_print_depth; bool status; int slink_depth; int du_depth; dev_t dir_dev; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { } while (0) /* FIXME? coreutils' du rounds sizes up: * for example, 1025k file is shown as "2" by du -m. * We round to nearest. */ static void print(unsigned long long size, const char *filename) { /* TODO - May not want to defer error checking here. */ #if ENABLE_FEATURE_HUMAN_READABLE printf("%s\t%s\n", /* size x 512 / G.disp_hr, show one fractional, * use suffixes if G.disp_hr == 0 */ make_human_readable_str(size, 512, G.disp_hr), filename); #else if (G.disp_k) { size++; size >>= 1; } printf("%llu\t%s\n", size, filename); #endif } /* tiny recursive du */ static unsigned long long du(const char *filename) { struct stat statbuf; unsigned long long sum; if (lstat(filename, &statbuf) != 0) { bb_simple_perror_msg(filename); G.status = EXIT_FAILURE; return 0; } if (option_mask32 & OPT_x_one_FS) { if (G.du_depth == 0) { G.dir_dev = statbuf.st_dev; } else if (G.dir_dev != statbuf.st_dev) { return 0; } } sum = statbuf.st_blocks; if (S_ISLNK(statbuf.st_mode)) { if (G.slink_depth > G.du_depth) { /* -H or -L */ if (stat(filename, &statbuf) != 0) { bb_simple_perror_msg(filename); G.status = EXIT_FAILURE; return 0; } sum = statbuf.st_blocks; if (G.slink_depth == 1) { /* Convert -H to -L */ G.slink_depth = INT_MAX; } } } if (!(option_mask32 & OPT_l_hardlinks) && statbuf.st_nlink > 1 ) { /* Add files/directories with links only once */ if (is_in_ino_dev_hashtable(&statbuf)) { return 0; } add_to_ino_dev_hashtable(&statbuf, NULL); } if (S_ISDIR(statbuf.st_mode)) { DIR *dir; struct dirent *entry; char *newfile; dir = warn_opendir(filename); if (!dir) { G.status = EXIT_FAILURE; return sum; } while ((entry = readdir(dir))) { newfile = concat_subpath_file(filename, entry->d_name); if (newfile == NULL) continue; ++G.du_depth; sum += du(newfile); --G.du_depth; free(newfile); } closedir(dir); } else { if (!(option_mask32 & OPT_a_files_too) && G.du_depth != 0) return sum; } if (G.du_depth <= G.max_print_depth) { print(sum, filename); } return sum; } int du_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int du_main(int argc UNUSED_PARAM, char **argv) { unsigned long long total; int slink_depth_save; unsigned opt; INIT_G(); #if ENABLE_FEATURE_HUMAN_READABLE IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_hr = 1024;) IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_hr = 512;) if (getenv("POSIXLY_CORRECT")) /* TODO - a new libbb function? */ G.disp_hr = 512; #else IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 1;) /* IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 0;) - G is pre-zeroed */ #endif G.max_print_depth = INT_MAX; /* Note: SUSv3 specifies that -a and -s options cannot be used together * in strictly conforming applications. However, it also says that some * du implementations may produce output when -a and -s are used together. * gnu du exits with an error code in this case. We choose to simply * ignore -a. This is consistent with -s being equivalent to -d 0. */ #if ENABLE_FEATURE_HUMAN_READABLE opt_complementary = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s:d+"; opt = getopt32(argv, "aHkLsx" "d:" "lc" "hm", &G.max_print_depth); argv += optind; if (opt & OPT_h_for_humans) { G.disp_hr = 0; } if (opt & OPT_m_mbytes) { G.disp_hr = 1024*1024; } if (opt & OPT_k_kbytes) { G.disp_hr = 1024; } #else opt_complementary = "H-L:L-H:s-d:d-s:d+"; opt = getopt32(argv, "aHkLsx" "d:" "lc", &G.max_print_depth); argv += optind; #if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K if (opt & OPT_k_kbytes) { G.disp_k = 1; } #endif #endif if (opt & OPT_H_follow_links) { G.slink_depth = 1; } if (opt & OPT_L_follow_links) { G.slink_depth = INT_MAX; } if (opt & OPT_s_total_norecurse) { G.max_print_depth = 0; } /* go through remaining args (if any) */ if (!*argv) { *--argv = (char*)"."; if (G.slink_depth == 1) { G.slink_depth = 0; } } slink_depth_save = G.slink_depth; total = 0; do { total += du(*argv); /* otherwise du /dir /dir won't show /dir twice: */ reset_ino_dev_hashtable(); G.slink_depth = slink_depth_save; } while (*++argv); if (opt & OPT_c_total) print(total, "total"); fflush_stdout_and_exit(G.status); } busybox-1.22.1/coreutils/sum.c0000644000000000000000000000542612263563520014762 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * sum -- checksum and count the blocks in a file * Like BSD sum or SysV sum -r, except like SysV sum if -s option is given. * * Copyright (C) 86, 89, 91, 1995-2002, 2004 Free Software Foundation, Inc. * Copyright (C) 2005 by Erik Andersen * Copyright (C) 2005 by Mike Frysinger * * Written by Kayvan Aghaiepour and David MacKenzie * Taken from coreutils and turned into a busybox applet by Mike Frysinger * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define sum_trivial_usage //usage: "[-rs] [FILE]..." //usage:#define sum_full_usage "\n\n" //usage: "Checksum and count the blocks in a file\n" //usage: "\n -r Use BSD sum algorithm (1K blocks)" //usage: "\n -s Use System V sum algorithm (512byte blocks)" #include "libbb.h" enum { SUM_BSD, PRINT_NAME, SUM_SYSV }; /* BSD: calculate and print the rotated checksum and the size in 1K blocks The checksum varies depending on sizeof (int). */ /* SYSV: calculate and print the checksum and the size in 512-byte blocks */ /* Return 1 if successful. */ static unsigned sum_file(const char *file, unsigned type) { #define buf bb_common_bufsiz1 unsigned long long total_bytes = 0; int fd, r; /* The sum of all the input bytes, modulo (UINT_MAX + 1). */ unsigned s = 0; fd = open_or_warn_stdin(file); if (fd == -1) return 0; while (1) { size_t bytes_read = safe_read(fd, buf, BUFSIZ); if ((ssize_t)bytes_read <= 0) { r = (fd && close(fd) != 0); if (!bytes_read && !r) /* no error */ break; bb_simple_perror_msg(file); return 0; } total_bytes += bytes_read; if (type >= SUM_SYSV) { do s += buf[--bytes_read]; while (bytes_read); } else { r = 0; do { s = (s >> 1) + ((s & 1) << 15); s += buf[r++]; s &= 0xffff; /* Keep it within bounds. */ } while (--bytes_read); } } if (type < PRINT_NAME) file = ""; if (type >= SUM_SYSV) { r = (s & 0xffff) + ((s & 0xffffffff) >> 16); s = (r & 0xffff) + (r >> 16); printf("%u %llu %s\n", s, (total_bytes + 511) / 512, file); } else printf("%05u %5llu %s\n", s, (total_bytes + 1023) / 1024, file); return 1; #undef buf } int sum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int sum_main(int argc UNUSED_PARAM, char **argv) { unsigned n; unsigned type = SUM_BSD; n = getopt32(argv, "sr"); argv += optind; if (n & 1) type = SUM_SYSV; /* give the bsd priority over sysv func */ if (n & 2) type = SUM_BSD; if (!argv[0]) { /* Do not print the name */ n = sum_file("-", type); } else { /* Need to print the name if either * - more than one file given * - doing sysv */ type += (argv[1] || type == SUM_SYSV); n = 1; do { n &= sum_file(*argv, type); } while (*++argv); } return !n; } busybox-1.22.1/coreutils/nohup.c0000644000000000000000000000443212263563520015303 0ustar rootroot/* vi: set sw=4 ts=4: */ /* nohup - invoke a utility immune to hangups. * * Busybox version based on nohup specification at * http://www.opengroup.org/onlinepubs/007904975/utilities/nohup.html * * Copyright 2006 Rob Landley * Copyright 2006 Bernhard Reutner-Fischer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define nohup_trivial_usage //usage: "PROG ARGS" //usage:#define nohup_full_usage "\n\n" //usage: "Run PROG immune to hangups, with output to a non-tty" //usage: //usage:#define nohup_example_usage //usage: "$ nohup make &" #include "libbb.h" /* Compat info: nohup (GNU coreutils 6.8) does this: # nohup true nohup: ignoring input and appending output to `nohup.out' # nohup true 1>/dev/null nohup: ignoring input and redirecting stderr to stdout # nohup true 2>zz # cat zz nohup: ignoring input and appending output to `nohup.out' # nohup true 2>zz 1>/dev/null # cat zz nohup: ignoring input # nohup true /dev/null nohup: redirecting stderr to stdout # nohup true zz 1>/dev/null # cat zz (nothing) # */ int nohup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int nohup_main(int argc UNUSED_PARAM, char **argv) { const char *nohupout; char *home; xfunc_error_retval = 127; if (!argv[1]) { bb_show_usage(); } /* If stdin is a tty, detach from it. */ if (isatty(STDIN_FILENO)) { /* bb_error_msg("ignoring input"); */ close(STDIN_FILENO); xopen(bb_dev_null, O_RDONLY); /* will be fd 0 (STDIN_FILENO) */ } nohupout = "nohup.out"; /* Redirect stdout to nohup.out, either in "." or in "$HOME". */ if (isatty(STDOUT_FILENO)) { close(STDOUT_FILENO); if (open(nohupout, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR) < 0) { home = getenv("HOME"); if (home) { nohupout = concat_path_file(home, nohupout); xopen3(nohupout, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR); } else { xopen(bb_dev_null, O_RDONLY); /* will be fd 1 */ } } bb_error_msg("appending output to %s", nohupout); } /* If we have a tty on stderr, redirect to stdout. */ if (isatty(STDERR_FILENO)) { /* if (stdout_wasnt_a_tty) bb_error_msg("redirecting stderr to stdout"); */ dup2(STDOUT_FILENO, STDERR_FILENO); } signal(SIGHUP, SIG_IGN); argv++; BB_EXECVP_or_die(argv); } busybox-1.22.1/coreutils/ln.c0000644000000000000000000000731712263563520014570 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini ln implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* BB_AUDIT GNU options missing: -d, -F, -i, and -v. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/ln.html */ //usage:#define ln_trivial_usage //usage: "[OPTIONS] TARGET... LINK|DIR" //usage:#define ln_full_usage "\n\n" //usage: "Create a link LINK or DIR/TARGET to the specified TARGET(s)\n" //usage: "\n -s Make symlinks instead of hardlinks" //usage: "\n -f Remove existing destinations" //usage: "\n -n Don't dereference symlinks - treat like normal file" //usage: "\n -b Make a backup of the target (if exists) before link operation" //usage: "\n -S suf Use suffix instead of ~ when making backup files" //usage: "\n -T 2nd arg must be a DIR" //usage: "\n -v Verbose" //usage: //usage:#define ln_example_usage //usage: "$ ln -s BusyBox /tmp/ls\n" //usage: "$ ls -l /tmp/ls\n" //usage: "lrwxrwxrwx 1 root root 7 Apr 12 18:39 ls -> BusyBox*\n" #include "libbb.h" /* This is a NOEXEC applet. Be very careful! */ #define LN_SYMLINK (1 << 0) #define LN_FORCE (1 << 1) #define LN_NODEREFERENCE (1 << 2) #define LN_BACKUP (1 << 3) #define LN_SUFFIX (1 << 4) #define LN_VERBOSE (1 << 5) #define LN_LINKFILE (1 << 6) int ln_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ln_main(int argc, char **argv) { int status = EXIT_SUCCESS; int opts; char *last; char *src_name; char *src; char *suffix = (char*)"~"; struct stat statbuf; int (*link_func)(const char *, const char *); opt_complementary = "-1"; /* min one arg */ opts = getopt32(argv, "sfnbS:vT", &suffix); last = argv[argc - 1]; argv += optind; argc -= optind; if ((opts & LN_LINKFILE) && argc > 2) { bb_error_msg_and_die("-T accepts 2 args max"); } if (!argv[1]) { /* "ln PATH/TO/FILE" -> "ln PATH/TO/FILE FILE" */ *--argv = last; /* xstrdup is needed: "ln -s PATH/TO/FILE/" is equivalent to * "ln -s PATH/TO/FILE/ FILE", not "ln -s PATH/TO/FILE FILE" */ last = bb_get_last_path_component_strip(xstrdup(last)); } do { src_name = NULL; src = last; if (is_directory(src, (opts & LN_NODEREFERENCE) ^ LN_NODEREFERENCE ) ) { if (opts & LN_LINKFILE) { bb_error_msg_and_die("'%s' is a directory", src); } src_name = xstrdup(*argv); src = concat_path_file(src, bb_get_last_path_component_strip(src_name)); free(src_name); src_name = src; } if (!(opts & LN_SYMLINK) && stat(*argv, &statbuf)) { // coreutils: "ln dangling_symlink new_hardlink" works if (lstat(*argv, &statbuf) || !S_ISLNK(statbuf.st_mode)) { bb_simple_perror_msg(*argv); status = EXIT_FAILURE; free(src_name); continue; } } if (opts & LN_BACKUP) { char *backup; backup = xasprintf("%s%s", src, suffix); if (rename(src, backup) < 0 && errno != ENOENT) { bb_simple_perror_msg(src); status = EXIT_FAILURE; free(backup); continue; } free(backup); /* * When the source and dest are both hard links to the same * inode, a rename may succeed even though nothing happened. * Therefore, always unlink(). */ unlink(src); } else if (opts & LN_FORCE) { unlink(src); } link_func = link; if (opts & LN_SYMLINK) { link_func = symlink; } if (opts & LN_VERBOSE) { printf("'%s' -> '%s'\n", src, *argv); } if (link_func(*argv, src) != 0) { bb_simple_perror_msg(src); status = EXIT_FAILURE; } free(src_name); } while ((++argv)[1]); return status; } busybox-1.22.1/coreutils/libcoreutils/0000755000000000000000000000000012320365363016502 5ustar rootrootbusybox-1.22.1/coreutils/libcoreutils/Kbuild.src0000644000000000000000000000056712263563520020436 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2004 by Erik Andersen # # Licensed under GPLv2 or later, see file LICENSE in this source tree. lib-y:= INSERT lib-$(CONFIG_MKFIFO) += getopt_mk_fifo_nod.o lib-$(CONFIG_MKNOD) += getopt_mk_fifo_nod.o lib-$(CONFIG_INSTALL) += cp_mv_stat.o lib-$(CONFIG_CP) += cp_mv_stat.o lib-$(CONFIG_MV) += cp_mv_stat.o busybox-1.22.1/coreutils/libcoreutils/getopt_mk_fifo_nod.c0000644000000000000000000000243212263563520022504 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * coreutils utility routine * * Copyright (C) 2003 Manuel Novoa III * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "libbb.h" #include "coreutils.h" mode_t FAST_FUNC getopt_mk_fifo_nod(char **argv) { mode_t mode = 0666; char *smode = NULL; #if ENABLE_SELINUX security_context_t scontext; #endif int opt; opt = getopt32(argv, "m:" IF_SELINUX("Z:"), &smode IF_SELINUX(,&scontext)); if (opt & 1) { if (bb_parse_mode(smode, &mode)) umask(0); } #if ENABLE_SELINUX if (opt & 2) { selinux_or_die(); setfscreatecon_or_die(scontext); } #endif return mode; } busybox-1.22.1/coreutils/libcoreutils/cp_mv_stat.c0000644000000000000000000000262512263563520021013 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * coreutils utility routine * * Copyright (C) 2003 Manuel Novoa III * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "libbb.h" #include "coreutils.h" int FAST_FUNC cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf) { if (sf(fn, fn_stat) < 0) { if (errno != ENOENT) { #if ENABLE_FEATURE_VERBOSE_CP_MESSAGE if (errno == ENOTDIR) { bb_error_msg("can't stat '%s': Path has non-directory component", fn); return -1; } #endif bb_perror_msg("can't stat '%s'", fn); return -1; } return 0; } if (S_ISDIR(fn_stat->st_mode)) { return 3; } return 1; } int FAST_FUNC cp_mv_stat(const char *fn, struct stat *fn_stat) { return cp_mv_stat2(fn, fn_stat, stat); } busybox-1.22.1/coreutils/libcoreutils/coreutils.h0000644000000000000000000000074512263563520020673 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #ifndef COREUTILS_H #define COREUTILS_H 1 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN typedef int (*stat_func)(const char *fn, struct stat *ps); int cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf) FAST_FUNC; int cp_mv_stat(const char *fn, struct stat *fn_stat) FAST_FUNC; mode_t getopt_mk_fifo_nod(char **argv) FAST_FUNC; POP_SAVED_FUNCTION_VISIBILITY #endif busybox-1.22.1/coreutils/Kbuild.src0000644000000000000000000000542712263563520015736 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. libs-y += libcoreutils/ lib-y:= INSERT lib-$(CONFIG_CAL) += cal.o lib-$(CONFIG_CATV) += catv.o lib-$(CONFIG_CHGRP) += chgrp.o chown.o lib-$(CONFIG_CHMOD) += chmod.o lib-$(CONFIG_CHOWN) += chown.o lib-$(CONFIG_ADDUSER) += chown.o # used by adduser lib-$(CONFIG_ADDGROUP) += chown.o # used by adduser lib-$(CONFIG_CHROOT) += chroot.o lib-$(CONFIG_CKSUM) += cksum.o lib-$(CONFIG_COMM) += comm.o lib-$(CONFIG_CP) += cp.o lib-$(CONFIG_CUT) += cut.o lib-$(CONFIG_DD) += dd.o lib-$(CONFIG_DF) += df.o lib-$(CONFIG_DIRNAME) += dirname.o lib-$(CONFIG_DOS2UNIX) += dos2unix.o lib-$(CONFIG_DU) += du.o lib-$(CONFIG_ECHO) += echo.o lib-$(CONFIG_ASH) += echo.o # used by ash lib-$(CONFIG_HUSH) += echo.o # used by hush lib-$(CONFIG_ENV) += env.o lib-$(CONFIG_EXPR) += expr.o lib-$(CONFIG_EXPAND) += expand.o lib-$(CONFIG_FALSE) += false.o lib-$(CONFIG_FOLD) += fold.o lib-$(CONFIG_FSYNC) += fsync.o lib-$(CONFIG_INSTALL) += install.o #lib-$(CONFIG_LENGTH) += length.o lib-$(CONFIG_LN) += ln.o lib-$(CONFIG_LOGNAME) += logname.o lib-$(CONFIG_LS) += ls.o lib-$(CONFIG_FTPD) += ls.o lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o lib-$(CONFIG_MKDIR) += mkdir.o lib-$(CONFIG_MKFIFO) += mkfifo.o lib-$(CONFIG_MKNOD) += mknod.o lib-$(CONFIG_MV) += mv.o lib-$(CONFIG_NICE) += nice.o lib-$(CONFIG_NOHUP) += nohup.o lib-$(CONFIG_OD) += od.o lib-$(CONFIG_PRINTENV) += printenv.o lib-$(CONFIG_PRINTF) += printf.o lib-$(CONFIG_ASH_BUILTIN_PRINTF) += printf.o lib-$(CONFIG_PWD) += pwd.o lib-$(CONFIG_READLINK) += readlink.o lib-$(CONFIG_REALPATH) += realpath.o lib-$(CONFIG_RM) += rm.o lib-$(CONFIG_RMDIR) += rmdir.o lib-$(CONFIG_SEQ) += seq.o lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o lib-$(CONFIG_SHA3SUM) += md5_sha1_sum.o lib-$(CONFIG_SLEEP) += sleep.o lib-$(CONFIG_SPLIT) += split.o lib-$(CONFIG_SORT) += sort.o lib-$(CONFIG_STAT) += stat.o lib-$(CONFIG_STTY) += stty.o lib-$(CONFIG_SUM) += sum.o lib-$(CONFIG_SYNC) += sync.o lib-$(CONFIG_TAC) += tac.o lib-$(CONFIG_TEE) += tee.o lib-$(CONFIG_TRUE) += true.o lib-$(CONFIG_TTY) += tty.o lib-$(CONFIG_UNAME) += uname.o lib-$(CONFIG_UNEXPAND) += expand.o lib-$(CONFIG_UNIQ) += uniq.o lib-$(CONFIG_USLEEP) += usleep.o lib-$(CONFIG_UUDECODE) += uudecode.o lib-$(CONFIG_UUENCODE) += uuencode.o lib-$(CONFIG_WC) += wc.o lib-$(CONFIG_WHOAMI) += whoami.o lib-$(CONFIG_YES) += yes.o busybox-1.22.1/coreutils/test_ptr_hack.c0000644000000000000000000000115412263563520017002 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright (C) 2008 by Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ struct test_statics; #ifndef GCC_COMBINE /* We cheat here. It is declared as const ptr in libbb.h, * but here we make it live in R/W memory */ struct test_statics *test_ptr_to_statics; #else /* gcc -combine will see through and complain */ /* Using alternative method which is more likely to break * on weird architectures, compilers, linkers and so on */ struct test_statics *const test_ptr_to_statics __attribute__ ((section (".data"))); #endif busybox-1.22.1/coreutils/id.c0000644000000000000000000001632712263563520014554 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini id implementation for busybox * * Copyright (C) 2000 by Randolph Chung * Copyright (C) 2008 by Tito Ragusa * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant. */ /* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever * length and to be more similar to GNU id. * -Z option support: by Yuichi Nakamura * Added -G option Tito Ragusa (C) 2008 for SUSv3. */ //config:config ID //config: bool "id" //config: default y //config: help //config: id displays the current user and group ID names. //config:config GROUPS //config: bool "groups" //config: default y //config: help //config: Print the group names associated with current user id. //kbuild:lib-$(CONFIG_GROUPS) += id.o //kbuild:lib-$(CONFIG_ID) += id.o //applet:IF_GROUPS(APPLET_NOEXEC(groups, id, BB_DIR_USR_BIN, BB_SUID_DROP, groups)) //applet:IF_ID( APPLET_NOEXEC(id, id, BB_DIR_USR_BIN, BB_SUID_DROP, id )) //usage:#define id_trivial_usage //usage: "[OPTIONS] [USER]" //usage:#define id_full_usage "\n\n" //usage: "Print information about USER or the current user\n" //usage: IF_SELINUX( //usage: "\n -Z Security context" //usage: ) //usage: "\n -u User ID" //usage: "\n -g Group ID" //usage: "\n -G Supplementary group IDs" //usage: "\n -n Print names instead of numbers" //usage: "\n -r Print real ID instead of effective ID" //usage: //usage:#define id_example_usage //usage: "$ id\n" //usage: "uid=1000(andersen) gid=1000(andersen)\n" //usage:#define groups_trivial_usage //usage: "[USER]" //usage:#define groups_full_usage "\n\n" //usage: "Print the group memberships of USER or for the current process" //usage: //usage:#define groups_example_usage //usage: "$ groups\n" //usage: "andersen lp dialout cdrom floppy\n" #include "libbb.h" /* This is a NOEXEC applet. Be very careful! */ #if !ENABLE_USE_BB_PWD_GRP #if defined(__UCLIBC__) && UCLIBC_VERSION < KERNEL_VERSION(0, 9, 30) #error "Sorry, you need at least uClibc version 0.9.30 for id applet to build" #endif #endif enum { PRINT_REAL = (1 << 0), NAME_NOT_NUMBER = (1 << 1), JUST_USER = (1 << 2), JUST_GROUP = (1 << 3), JUST_ALL_GROUPS = (1 << 4), #if ENABLE_SELINUX JUST_CONTEXT = (1 << 5), #endif }; static int print_common(unsigned id, const char *name, const char *prefix) { if (prefix) { printf("%s", prefix); } if (!(option_mask32 & NAME_NOT_NUMBER) || !name) { printf("%u", id); } if (!option_mask32 || (option_mask32 & NAME_NOT_NUMBER)) { if (name) { printf(option_mask32 ? "%s" : "(%s)", name); } else { /* Don't set error status flag in default mode */ if (option_mask32) { if (ENABLE_DESKTOP) bb_error_msg("unknown ID %u", id); return EXIT_FAILURE; } } } return EXIT_SUCCESS; } static int print_group(gid_t id, const char *prefix) { return print_common(id, gid2group(id), prefix); } static int print_user(uid_t id, const char *prefix) { return print_common(id, uid2uname(id), prefix); } /* On error set *n < 0 and return >= 0 * If *n is too small, update it and return < 0 * (ok to trash groups[] in both cases) * Otherwise fill in groups[] and return >= 0 */ static int get_groups(const char *username, gid_t rgid, gid_t *groups, int *n) { int m; if (username) { /* If the user is a member of more than * *n groups, then -1 is returned. Otherwise >= 0. * (and no defined way of detecting errors?!) */ m = getgrouplist(username, rgid, groups, n); /* I guess *n < 0 might indicate error. Anyway, * malloc'ing -1 bytes won't be good, so: */ if (*n < 0) return 0; return m; } *n = getgroups(*n, groups); if (*n >= 0) return *n; /* Error */ if (errno == EINVAL) /* *n is too small? */ *n = getgroups(0, groups); /* get needed *n */ /* if *n >= 0, return -1 (got new *n), else return 0 (error): */ return -(*n >= 0); } int id_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int id_main(int argc UNUSED_PARAM, char **argv) { uid_t ruid; gid_t rgid; uid_t euid; gid_t egid; unsigned opt; int i; int status = EXIT_SUCCESS; const char *prefix; const char *username; #if ENABLE_SELINUX security_context_t scontext = NULL; #endif if (ENABLE_GROUPS && (!ENABLE_ID || applet_name[0] == 'g')) { /* TODO: coreutils groups prepend "USER : " prefix, * and accept many usernames. Example: * # groups root root * root : root * root : root */ opt = option_mask32 = getopt32(argv, "") | JUST_ALL_GROUPS | NAME_NOT_NUMBER; } else { /* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/ /* Don't allow more than one username */ opt_complementary = "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG" IF_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G"); opt = getopt32(argv, "rnugG" IF_SELINUX("Z")); } username = argv[optind]; if (username) { struct passwd *p = xgetpwnam(username); euid = ruid = p->pw_uid; egid = rgid = p->pw_gid; } else { egid = getegid(); rgid = getgid(); euid = geteuid(); ruid = getuid(); } /* JUST_ALL_GROUPS ignores -r PRINT_REAL flag even if man page for */ /* id says: print the real ID instead of the effective ID, with -ugG */ /* in fact in this case egid is always printed if egid != rgid */ if (!opt || (opt & JUST_ALL_GROUPS)) { gid_t *groups; int n; if (!opt) { /* Default Mode */ status |= print_user(ruid, "uid="); status |= print_group(rgid, " gid="); if (euid != ruid) status |= print_user(euid, " euid="); if (egid != rgid) status |= print_group(egid, " egid="); } else { /* JUST_ALL_GROUPS */ status |= print_group(rgid, NULL); if (egid != rgid) status |= print_group(egid, " "); } /* We are supplying largish buffer, trying * to not run get_groups() twice. That might be slow * ("user database in remote SQL server" case) */ groups = xmalloc(64 * sizeof(groups[0])); n = 64; if (get_groups(username, rgid, groups, &n) < 0) { /* Need bigger buffer after all */ groups = xrealloc(groups, n * sizeof(groups[0])); get_groups(username, rgid, groups, &n); } if (n > 0) { /* Print the list */ prefix = " groups="; for (i = 0; i < n; i++) { if (opt && (groups[i] == rgid || groups[i] == egid)) continue; status |= print_group(groups[i], opt ? " " : prefix); prefix = ","; } } else if (n < 0) { /* error in get_groups() */ if (ENABLE_DESKTOP) bb_error_msg_and_die("can't get groups"); return EXIT_FAILURE; } if (ENABLE_FEATURE_CLEAN_UP) free(groups); #if ENABLE_SELINUX if (is_selinux_enabled()) { if (getcon(&scontext) == 0) printf(" context=%s", scontext); } #endif } else if (opt & PRINT_REAL) { euid = ruid; egid = rgid; } if (opt & JUST_USER) status |= print_user(euid, NULL); else if (opt & JUST_GROUP) status |= print_group(egid, NULL); #if ENABLE_SELINUX else if (opt & JUST_CONTEXT) { selinux_or_die(); if (username || getcon(&scontext)) { bb_error_msg_and_die("can't get process context%s", username ? " for a different user" : ""); } fputs(scontext, stdout); } /* freecon(NULL) seems to be harmless */ if (ENABLE_FEATURE_CLEAN_UP) freecon(scontext); #endif bb_putchar('\n'); fflush_stdout_and_exit(status); } busybox-1.22.1/coreutils/sync.c0000644000000000000000000000131512263563520015123 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini sync implementation for busybox * * Copyright (C) 1995, 1996 by Bruce Perens . * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ //usage:#define sync_trivial_usage //usage: "" //usage:#define sync_full_usage "\n\n" //usage: "Write all buffered blocks to disk" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ int sync_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int sync_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM)) { /* coreutils-6.9 compat */ bb_warn_ignoring_args(argv[1]); sync(); return EXIT_SUCCESS; } busybox-1.22.1/coreutils/df.c0000644000000000000000000001715312263563520014547 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini df implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * based on original code by (I think) Bruce Perens . * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 _NOT_ compliant -- option -t missing. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/df.html */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Size reduction. Removed floating point dependency. Added error checking * on output. Output stats on 0-sized filesystems if specifically listed on * the command line. Properly round *-blocks, Used, and Available quantities. * * Aug 28, 2008 Bernhard Reutner-Fischer * * Implement -P and -B; better coreutils compat; cleanup */ //usage:#define df_trivial_usage //usage: "[-Pk" //usage: IF_FEATURE_HUMAN_READABLE("mh") //usage: IF_FEATURE_DF_FANCY("ai] [-B SIZE") //usage: "] [FILESYSTEM]..." //usage:#define df_full_usage "\n\n" //usage: "Print filesystem usage statistics\n" //usage: "\n -P POSIX output format" //usage: "\n -k 1024-byte blocks (default)" //usage: IF_FEATURE_HUMAN_READABLE( //usage: "\n -m 1M-byte blocks" //usage: "\n -h Human readable (e.g. 1K 243M 2G)" //usage: ) //usage: IF_FEATURE_DF_FANCY( //usage: "\n -a Show all filesystems" //usage: "\n -i Inodes" //usage: "\n -B SIZE Blocksize" //usage: ) //usage: //usage:#define df_example_usage //usage: "$ df\n" //usage: "Filesystem 1K-blocks Used Available Use% Mounted on\n" //usage: "/dev/sda3 8690864 8553540 137324 98% /\n" //usage: "/dev/sda1 64216 36364 27852 57% /boot\n" //usage: "$ df /dev/sda3\n" //usage: "Filesystem 1K-blocks Used Available Use% Mounted on\n" //usage: "/dev/sda3 8690864 8553540 137324 98% /\n" //usage: "$ POSIXLY_CORRECT=sure df /dev/sda3\n" //usage: "Filesystem 512B-blocks Used Available Use% Mounted on\n" //usage: "/dev/sda3 17381728 17107080 274648 98% /\n" //usage: "$ POSIXLY_CORRECT=yep df -P /dev/sda3\n" //usage: "Filesystem 512-blocks Used Available Capacity Mounted on\n" //usage: "/dev/sda3 17381728 17107080 274648 98% /\n" #include #include #include "libbb.h" #include "unicode.h" #if !ENABLE_FEATURE_HUMAN_READABLE static unsigned long kscale(unsigned long b, unsigned long bs) { return (b * (unsigned long long) bs + 1024/2) / 1024; } #endif int df_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int df_main(int argc UNUSED_PARAM, char **argv) { unsigned long blocks_used; unsigned blocks_percent_used; unsigned long df_disp_hr = 1024; int status = EXIT_SUCCESS; unsigned opt; FILE *mount_table; struct mntent *mount_entry; struct statfs s; enum { OPT_KILO = (1 << 0), OPT_POSIX = (1 << 1), OPT_ALL = (1 << 2) * ENABLE_FEATURE_DF_FANCY, OPT_INODE = (1 << 3) * ENABLE_FEATURE_DF_FANCY, OPT_BSIZE = (1 << 4) * ENABLE_FEATURE_DF_FANCY, OPT_HUMAN = (1 << (2 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE, OPT_MEGA = (1 << (3 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE, }; const char *disp_units_hdr = NULL; char *chp; init_unicode(); #if ENABLE_FEATURE_HUMAN_READABLE && ENABLE_FEATURE_DF_FANCY opt_complementary = "k-mB:m-Bk:B-km"; #elif ENABLE_FEATURE_HUMAN_READABLE opt_complementary = "k-m:m-k"; #endif opt = getopt32(argv, "kP" IF_FEATURE_DF_FANCY("aiB:") IF_FEATURE_HUMAN_READABLE("hm") IF_FEATURE_DF_FANCY(, &chp)); if (opt & OPT_MEGA) df_disp_hr = 1024*1024; if (opt & OPT_BSIZE) df_disp_hr = xatoul_range(chp, 1, ULONG_MAX); /* disallow 0 */ /* From the manpage of df from coreutils-6.10: * Disk space is shown in 1K blocks by default, unless the environment * variable POSIXLY_CORRECT is set, in which case 512-byte blocks are used. */ if (getenv("POSIXLY_CORRECT")) /* TODO - a new libbb function? */ df_disp_hr = 512; if (opt & OPT_HUMAN) { df_disp_hr = 0; disp_units_hdr = " Size"; } if (opt & OPT_INODE) disp_units_hdr = " Inodes"; if (disp_units_hdr == NULL) { #if ENABLE_FEATURE_HUMAN_READABLE disp_units_hdr = xasprintf("%s-blocks", /* print df_disp_hr, show no fractionals, * use suffixes if OPT_POSIX is set in opt */ make_human_readable_str(df_disp_hr, 0, !!(opt & OPT_POSIX)) ); #else disp_units_hdr = xasprintf("%lu-blocks", df_disp_hr); #endif } printf("Filesystem %-15sUsed Available %s Mounted on\n", disp_units_hdr, (opt & OPT_POSIX) ? "Capacity" : "Use%"); mount_table = NULL; argv += optind; if (!argv[0]) { mount_table = setmntent(bb_path_mtab_file, "r"); if (!mount_table) bb_perror_msg_and_die(bb_path_mtab_file); } while (1) { const char *device; const char *mount_point; if (mount_table) { mount_entry = getmntent(mount_table); if (!mount_entry) { endmntent(mount_table); break; } } else { mount_point = *argv++; if (!mount_point) break; mount_entry = find_mount_point(mount_point, 1); if (!mount_entry) { bb_error_msg("%s: can't find mount point", mount_point); set_error: status = EXIT_FAILURE; continue; } } device = mount_entry->mnt_fsname; mount_point = mount_entry->mnt_dir; if (statfs(mount_point, &s) != 0) { bb_simple_perror_msg(mount_point); goto set_error; } if ((s.f_blocks > 0) || !mount_table || (opt & OPT_ALL)) { if (opt & OPT_INODE) { s.f_blocks = s.f_files; s.f_bavail = s.f_bfree = s.f_ffree; s.f_bsize = 1; if (df_disp_hr) df_disp_hr = 1; } blocks_used = s.f_blocks - s.f_bfree; blocks_percent_used = 0; if (blocks_used + s.f_bavail) { blocks_percent_used = (blocks_used * 100ULL + (blocks_used + s.f_bavail)/2 ) / (blocks_used + s.f_bavail); } /* GNU coreutils 6.10 skips certain mounts, try to be compatible. */ if (ENABLE_FEATURE_SKIP_ROOTFS && strcmp(device, "rootfs") == 0) continue; #ifdef WHY_WE_DO_IT_FOR_DEV_ROOT_ONLY if (strcmp(device, "/dev/root") == 0) { /* Adjusts device to be the real root device, * or leaves device alone if it can't find it */ device = find_block_device("/"); if (!device) { goto set_error; } } #endif #if ENABLE_UNICODE_SUPPORT { uni_stat_t uni_stat; char *uni_dev = unicode_conv_to_printable(&uni_stat, device); if (uni_stat.unicode_width > 20 && !(opt & OPT_POSIX)) { printf("%s\n%20s", uni_dev, ""); } else { printf("%s%*s", uni_dev, 20 - (int)uni_stat.unicode_width, ""); } free(uni_dev); } #else if (printf("\n%-20s" + 1, device) > 20 && !(opt & OPT_POSIX)) printf("\n%-20s", ""); #endif #if ENABLE_FEATURE_HUMAN_READABLE printf(" %9s ", /* f_blocks x f_bsize / df_disp_hr, show one fractional, * use suffixes if df_disp_hr == 0 */ make_human_readable_str(s.f_blocks, s.f_bsize, df_disp_hr)); printf(" %9s " + 1, /* EXPR x f_bsize / df_disp_hr, show one fractional, * use suffixes if df_disp_hr == 0 */ make_human_readable_str((s.f_blocks - s.f_bfree), s.f_bsize, df_disp_hr)); printf("%9s %3u%% %s\n", /* f_bavail x f_bsize / df_disp_hr, show one fractional, * use suffixes if df_disp_hr == 0 */ make_human_readable_str(s.f_bavail, s.f_bsize, df_disp_hr), blocks_percent_used, mount_point); #else printf(" %9lu %9lu %9lu %3u%% %s\n", kscale(s.f_blocks, s.f_bsize), kscale(s.f_blocks - s.f_bfree, s.f_bsize), kscale(s.f_bavail, s.f_bsize), blocks_percent_used, mount_point); #endif } } return status; } busybox-1.22.1/coreutils/uname.c0000644000000000000000000001230412263563520015254 0ustar rootroot/* vi: set sw=4 ts=4: */ /* uname -- print system information * Copyright (C) 1989-1999 Free Software Foundation, Inc. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/uname.html */ /* Option Example * -s, --sysname SunOS * -n, --nodename rocky8 * -r, --release 4.0 * -v, --version * -m, --machine sun * -a, --all SunOS rocky8 4.0 sun * * The default behavior is equivalent to '-s'. * * David MacKenzie * * GNU coreutils 6.10: * Option: struct Example(s): * utsname * field: * -s, --kernel-name sysname Linux * -n, --nodename nodename localhost.localdomain * -r, --kernel-release release 2.6.29 * -v, --kernel-version version #1 SMP Sun Jan 11 20:52:37 EST 2009 * -m, --machine machine x86_64 i686 * -p, --processor (none) x86_64 i686 * -i, --hardware-platform (none) x86_64 i386 * NB: vanilla coreutils reports "unknown" -p and -i, * x86_64 and i686/i386 shown above are Fedora's inventions. * -o, --operating-system (none) GNU/Linux * -a, --all: all of the above, in the order shown. * If -p or -i is not known, don't show them */ /* Busyboxed by Erik Andersen * * Before 2003: Glenn McGrath and Manuel Novoa III * Further size reductions. * Mar 16, 2003: Manuel Novoa III (mjn3@codepoet.org) * Now does proper error checking on i/o. Plus some further space savings. * Jan 2009: * Fix handling of -a to not print "unknown", add -o and -i support. */ //usage:#define uname_trivial_usage //usage: "[-amnrspv]" //usage:#define uname_full_usage "\n\n" //usage: "Print system information\n" //usage: "\n -a Print all" //usage: "\n -m The machine (hardware) type" //usage: "\n -n Hostname" //usage: "\n -r OS release" //usage: "\n -s OS name (default)" //usage: "\n -p Processor type" //usage: "\n -v OS version" //usage: //usage:#define uname_example_usage //usage: "$ uname -a\n" //usage: "Linux debian 2.4.23 #2 Tue Dec 23 17:09:10 MST 2003 i686 GNU/Linux\n" #include "libbb.h" /* After libbb.h, since it needs sys/types.h on some systems */ #include typedef struct { struct utsname name; char processor[sizeof(((struct utsname*)NULL)->machine)]; char platform[sizeof(((struct utsname*)NULL)->machine)]; char os[sizeof("GNU/Linux")]; } uname_info_t; static const char options[] ALIGN1 = "snrvmpioa"; static const unsigned short utsname_offset[] = { offsetof(uname_info_t, name.sysname), /* -s */ offsetof(uname_info_t, name.nodename), /* -n */ offsetof(uname_info_t, name.release), /* -r */ offsetof(uname_info_t, name.version), /* -v */ offsetof(uname_info_t, name.machine), /* -m */ offsetof(uname_info_t, processor), /* -p */ offsetof(uname_info_t, platform), /* -i */ offsetof(uname_info_t, os), /* -o */ }; int uname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int uname_main(int argc UNUSED_PARAM, char **argv) { #if ENABLE_LONG_OPTS static const char uname_longopts[] ALIGN1 = /* name, has_arg, val */ "all\0" No_argument "a" "kernel-name\0" No_argument "s" "nodename\0" No_argument "n" "kernel-release\0" No_argument "r" "release\0" No_argument "r" "kernel-version\0" No_argument "v" "machine\0" No_argument "m" "processor\0" No_argument "p" "hardware-platform\0" No_argument "i" "operating-system\0" No_argument "o" ; #endif uname_info_t uname_info; #if defined(__sparc__) && defined(__linux__) char *fake_sparc = getenv("FAKE_SPARC"); #endif const char *unknown_str = "unknown"; const char *fmt; const unsigned short *delta; unsigned toprint; IF_LONG_OPTS(applet_long_options = uname_longopts); toprint = getopt32(argv, options); if (argv[optind]) { /* coreutils-6.9 compat */ bb_show_usage(); } if (toprint & (1 << 8)) { /* -a => all opts on */ toprint = (1 << 8) - 1; unknown_str = ""; /* -a does not print unknown fields */ } if (toprint == 0) { /* no opts => -s (sysname) */ toprint = 1; } uname(&uname_info.name); /* never fails */ #if defined(__sparc__) && defined(__linux__) if (fake_sparc && (fake_sparc[0] | 0x20) == 'y') { strcpy(uname_info.name.machine, "sparc"); } #endif strcpy(uname_info.processor, unknown_str); strcpy(uname_info.platform, unknown_str); strcpy(uname_info.os, "GNU/Linux"); #if 0 /* Fedora does something like this */ strcpy(uname_info.processor, uname_info.name.machine); strcpy(uname_info.platform, uname_info.name.machine); if (uname_info.platform[0] == 'i' && uname_info.platform[1] && uname_info.platform[2] == '8' && uname_info.platform[3] == '6' ) { uname_info.platform[1] = '3'; } #endif delta = utsname_offset; fmt = " %s" + 1; do { if (toprint & 1) { const char *p = (char *)(&uname_info) + *delta; if (p[0]) { printf(fmt, p); fmt = " %s"; } } ++delta; } while (toprint >>= 1); bb_putchar('\n'); fflush_stdout_and_exit(EXIT_SUCCESS); /* coreutils-6.9 compat */ } busybox-1.22.1/coreutils/tac.c0000644000000000000000000000477512263563520014733 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * tac implementation for busybox * * Copyright (C) 2003 Yang Xiaopeng * Copyright (C) 2007 Natanael Copa * Copyright (C) 2007 Tito Ragusa * * Licensed under GPLv2, see file LICENSE in this source tree. * */ /* tac - concatenate and print files in reverse */ /* Based on Yang Xiaopeng's (yxp at hanwang.com.cn) patch * http://www.uclibc.org/lists/busybox/2003-July/008813.html */ //usage:#define tac_trivial_usage //usage: "[FILE]..." //usage:#define tac_full_usage "\n\n" //usage: "Concatenate FILEs and print them in reverse" #include "libbb.h" /* This is a NOEXEC applet. Be very careful! */ struct lstring { int size; char buf[1]; }; int tac_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int tac_main(int argc UNUSED_PARAM, char **argv) { char **name; FILE *f; struct lstring *line = NULL; llist_t *list = NULL; int retval = EXIT_SUCCESS; #if ENABLE_DESKTOP /* tac from coreutils 6.9 supports: -b, --before attach the separator before instead of after -r, --regex interpret the separator as a regular expression -s, --separator=STRING use STRING as the separator instead of newline We support none, but at least we will complain or handle "--": */ getopt32(argv, ""); argv += optind; #else argv++; #endif if (!*argv) *--argv = (char *)"-"; /* We will read from last file to first */ name = argv; while (*name) name++; do { int ch, i; name--; f = fopen_or_warn_stdin(*name); if (f == NULL) { /* error message is printed by fopen_or_warn_stdin */ retval = EXIT_FAILURE; continue; } errno = i = 0; do { ch = fgetc(f); if (ch != EOF) { if (!(i & 0x7f)) /* Grow on every 128th char */ line = xrealloc(line, i + 0x7f + sizeof(int) + 1); line->buf[i++] = ch; } if (ch == '\n' || (ch == EOF && i != 0)) { line = xrealloc(line, i + sizeof(int)); line->size = i; llist_add_to(&list, line); line = NULL; i = 0; } } while (ch != EOF); /* fgetc sets errno to ENOENT on EOF, we don't want * to warn on this non-error! */ if (errno && errno != ENOENT) { bb_simple_perror_msg(*name); retval = EXIT_FAILURE; } } while (name != argv); while (list) { line = (struct lstring *)list->data; xwrite(STDOUT_FILENO, line->buf, line->size); if (ENABLE_FEATURE_CLEAN_UP) { free(llist_pop(&list)); } else { list = list->link; } } return retval; } busybox-1.22.1/coreutils/false.c0000644000000000000000000000145612263563520015247 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini false implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* http://www.opengroup.org/onlinepubs/000095399/utilities/false.html */ //usage:#define false_trivial_usage //usage: "" //usage:#define false_full_usage "\n\n" //usage: "Return an exit code of FALSE (1)" //usage: //usage:#define false_example_usage //usage: "$ false\n" //usage: "$ echo $?\n" //usage: "1\n" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ int false_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int false_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { return EXIT_FAILURE; } busybox-1.22.1/coreutils/env.c0000644000000000000000000001030512263563520014736 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * env implementation for busybox * * Copyright (c) 1988, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Original copyright notice is retained at the end of this file. * * Modified for BusyBox by Erik Andersen */ /* BB_AUDIT SUSv3 compliant */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/env.html */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Fixed bug involving exit return codes if execvp fails. Also added * output error checking. */ /* * Modified by Vladimir Oleynik (C) 2003 * - correct "-" option usage * - multiple "-u unsetenv" support * - GNU long option support * - use xfunc_error_retval */ /* This is a NOEXEC applet. Be very careful! */ //usage:#define env_trivial_usage //usage: "[-iu] [-] [name=value]... [PROG ARGS]" //usage:#define env_full_usage "\n\n" //usage: "Print the current environment or run PROG after setting up\n" //usage: "the specified environment\n" //usage: "\n -, -i Start with an empty environment" //usage: "\n -u Remove variable from the environment" #include "libbb.h" #if ENABLE_FEATURE_ENV_LONG_OPTIONS static const char env_longopts[] ALIGN1 = "ignore-environment\0" No_argument "i" "unset\0" Required_argument "u" ; #endif int env_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int env_main(int argc UNUSED_PARAM, char **argv) { unsigned opts; llist_t *unset_env = NULL; opt_complementary = "u::"; #if ENABLE_FEATURE_ENV_LONG_OPTIONS applet_long_options = env_longopts; #endif opts = getopt32(argv, "+iu:", &unset_env); argv += optind; if (argv[0] && LONE_DASH(argv[0])) { opts |= 1; ++argv; } if (opts & 1) { clearenv(); } while (unset_env) { char *var = llist_pop(&unset_env); /* This does not handle -uVAR=VAL * (coreutils _sets_ the variable in that case): */ /*unsetenv(var);*/ /* This does, but uses somewhan undocumented feature that * putenv("name_without_equal_sign") unsets the variable: */ putenv(var); } while (*argv && (strchr(*argv, '=') != NULL)) { if (putenv(*argv) < 0) { bb_perror_msg_and_die("putenv"); } ++argv; } if (argv[0]) { BB_EXECVP_or_die(argv); } if (environ) { /* clearenv() may set environ == NULL! */ char **ep; for (ep = environ; *ep; ep++) { puts(*ep); } } fflush_stdout_and_exit(EXIT_SUCCESS); } /* * Copyright (c) 1988, 1993, 1994 * 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. BSD Advertising Clause omitted per the July 22, 1999 licensing change * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change * * 4. 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. */ busybox-1.22.1/coreutils/uudecode.c0000644000000000000000000001374412263563520015755 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright 2003, Glenn McGrath * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Based on specification from * http://www.opengroup.org/onlinepubs/007904975/utilities/uuencode.html * * Bugs: the spec doesn't mention anything about "`\n`\n" prior to the * "end" line */ //usage:#define uudecode_trivial_usage //usage: "[-o OUTFILE] [INFILE]" //usage:#define uudecode_full_usage "\n\n" //usage: "Uudecode a file\n" //usage: "Finds OUTFILE in uuencoded source unless -o is given" //usage: //usage:#define uudecode_example_usage //usage: "$ uudecode -o busybox busybox.uu\n" //usage: "$ ls -l busybox\n" //usage: "-rwxr-xr-x 1 ams ams 245264 Jun 7 21:35 busybox\n" #include "libbb.h" #if ENABLE_UUDECODE static void FAST_FUNC read_stduu(FILE *src_stream, FILE *dst_stream, int flags UNUSED_PARAM) { char *line; while ((line = xmalloc_fgetline(src_stream)) != NULL) { int encoded_len, str_len; char *line_ptr, *dst; if (strcmp(line, "end") == 0) { return; /* the only non-error exit */ } line_ptr = line; while (*line_ptr) { *line_ptr = (*line_ptr - 0x20) & 0x3f; line_ptr++; } str_len = line_ptr - line; encoded_len = line[0] * 4 / 3; /* Check that line is not too short. (we tolerate * overly _long_ line to accomodate possible extra '`'). * Empty line case is also caught here. */ if (str_len <= encoded_len) { break; /* go to bb_error_msg_and_die("short file"); */ } if (encoded_len <= 0) { /* Ignore the "`\n" line, why is it even in the encode file ? */ free(line); continue; } if (encoded_len > 60) { bb_error_msg_and_die("line too long"); } dst = line; line_ptr = line + 1; do { /* Merge four 6 bit chars to three 8 bit chars */ *dst++ = line_ptr[0] << 2 | line_ptr[1] >> 4; encoded_len--; if (encoded_len == 0) { break; } *dst++ = line_ptr[1] << 4 | line_ptr[2] >> 2; encoded_len--; if (encoded_len == 0) { break; } *dst++ = line_ptr[2] << 6 | line_ptr[3]; line_ptr += 4; encoded_len -= 2; } while (encoded_len > 0); fwrite(line, 1, dst - line, dst_stream); free(line); } bb_error_msg_and_die("short file"); } #endif #if ENABLE_UUDECODE int uudecode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int uudecode_main(int argc UNUSED_PARAM, char **argv) { FILE *src_stream; char *outname = NULL; char *line; opt_complementary = "?1"; /* 1 argument max */ getopt32(argv, "o:", &outname); argv += optind; if (!argv[0]) *--argv = (char*)"-"; src_stream = xfopen_stdin(argv[0]); /* Search for the start of the encoding */ while ((line = xmalloc_fgetline(src_stream)) != NULL) { void FAST_FUNC (*decode_fn_ptr)(FILE *src, FILE *dst, int flags); char *line_ptr; FILE *dst_stream; int mode; if (strncmp(line, "begin-base64 ", 13) == 0) { line_ptr = line + 13; decode_fn_ptr = read_base64; } else if (strncmp(line, "begin ", 6) == 0) { line_ptr = line + 6; decode_fn_ptr = read_stduu; } else { free(line); continue; } /* begin line found. decode and exit */ mode = bb_strtou(line_ptr, NULL, 8); if (outname == NULL) { outname = strchr(line_ptr, ' '); if (!outname) break; outname++; if (!outname[0]) break; } dst_stream = stdout; if (NOT_LONE_DASH(outname)) { dst_stream = xfopen_for_write(outname); fchmod(fileno(dst_stream), mode & (S_IRWXU | S_IRWXG | S_IRWXO)); } free(line); decode_fn_ptr(src_stream, dst_stream, /*flags:*/ BASE64_FLAG_UU_STOP + BASE64_FLAG_NO_STOP_CHAR); /* fclose_if_not_stdin(src_stream); - redundant */ return EXIT_SUCCESS; } bb_error_msg_and_die("no 'begin' line"); } #endif //applet:IF_BASE64(APPLET(base64, BB_DIR_BIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_BASE64) += uudecode.o //config:config BASE64 //config: bool "base64" //config: default y //config: help //config: Base64 encode and decode //usage:#define base64_trivial_usage //usage: "[-d] [FILE]" //usage:#define base64_full_usage "\n\n" //usage: "Base64 encode or decode FILE to standard output" //usage: "\n -d Decode data" ////usage: "\n -w COL Wrap lines at COL (default 76, 0 disables)" ////usage: "\n -i When decoding, ignore non-alphabet characters" #if ENABLE_BASE64 int base64_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int base64_main(int argc UNUSED_PARAM, char **argv) { FILE *src_stream; unsigned opts; opt_complementary = "?1"; /* 1 argument max */ opts = getopt32(argv, "d"); argv += optind; if (!argv[0]) *--argv = (char*)"-"; src_stream = xfopen_stdin(argv[0]); if (opts) { read_base64(src_stream, stdout, /*flags:*/ (char)EOF); } else { enum { SRC_BUF_SIZE = 76/4*3, /* This *MUST* be a multiple of 3 */ DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3), }; char src_buf[SRC_BUF_SIZE]; char dst_buf[DST_BUF_SIZE + 1]; int src_fd = fileno(src_stream); while (1) { size_t size = full_read(src_fd, src_buf, SRC_BUF_SIZE); if (!size) break; if ((ssize_t)size < 0) bb_perror_msg_and_die(bb_msg_read_error); /* Encode the buffer we just read in */ bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64); xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3)); bb_putchar('\n'); fflush(stdout); } } fflush_stdout_and_exit(EXIT_SUCCESS); } #endif /* Test script. Put this into an empty dir with busybox binary, an run. #!/bin/sh test -x busybox || { echo "No ./busybox?"; exit; } ln -sf busybox uudecode ln -sf busybox uuencode >A_null echo -n A >A echo -n AB >AB echo -n ABC >ABC echo -n ABCD >ABCD echo -n ABCDE >ABCDE echo -n ABCDEF >ABCDEF cat busybox >A_bbox for f in A*; do echo uuencode $f ./uuencode $f <$f >u_$f ./uuencode -m $f <$f >m_$f done mkdir unpk_u unpk_m 2>/dev/null for f in u_*; do ./uudecode <$f -o unpk_u/${f:2} diff -a ${f:2} unpk_u/${f:2} >/dev/null 2>&1 echo uudecode $f: $? done for f in m_*; do ./uudecode <$f -o unpk_m/${f:2} diff -a ${f:2} unpk_m/${f:2} >/dev/null 2>&1 echo uudecode $f: $? done */ busybox-1.22.1/coreutils/tee.c0000644000000000000000000000604512263563520014731 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * tee implementation for busybox * * Copyright (C) 2003 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/tee.html */ //usage:#define tee_trivial_usage //usage: "[-ai] [FILE]..." //usage:#define tee_full_usage "\n\n" //usage: "Copy stdin to each FILE, and also to stdout\n" //usage: "\n -a Append to the given FILEs, don't overwrite" //usage: "\n -i Ignore interrupt signals (SIGINT)" //usage: //usage:#define tee_example_usage //usage: "$ echo \"Hello\" | tee /tmp/foo\n" //usage: "$ cat /tmp/foo\n" //usage: "Hello\n" #include "libbb.h" int tee_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int tee_main(int argc, char **argv) { const char *mode = "w\0a"; FILE **files; FILE **fp; char **names; char **np; char retval; //TODO: make unconditional #if ENABLE_FEATURE_TEE_USE_BLOCK_IO ssize_t c; # define buf bb_common_bufsiz1 #else int c; #endif retval = getopt32(argv, "ia"); /* 'a' must be 2nd */ argc -= optind; argv += optind; mode += (retval & 2); /* Since 'a' is the 2nd option... */ if (retval & 1) { signal(SIGINT, SIG_IGN); /* TODO - switch to sigaction. (why?) */ } retval = EXIT_SUCCESS; /* gnu tee ignores SIGPIPE in case one of the output files is a pipe * that doesn't consume all its input. Good idea... */ signal(SIGPIPE, SIG_IGN); /* Allocate an array of FILE *'s, with one extra for a sentinel. */ fp = files = xzalloc(sizeof(FILE *) * (argc + 2)); np = names = argv - 1; files[0] = stdout; goto GOT_NEW_FILE; do { *fp = stdout; if (NOT_LONE_DASH(*argv)) { *fp = fopen_or_warn(*argv, mode); if (*fp == NULL) { retval = EXIT_FAILURE; argv++; continue; } } *np = *argv++; GOT_NEW_FILE: setbuf(*fp, NULL); /* tee must not buffer output. */ fp++; np++; } while (*argv); /* names[0] will be filled later */ #if ENABLE_FEATURE_TEE_USE_BLOCK_IO while ((c = safe_read(STDIN_FILENO, buf, sizeof(buf))) > 0) { fp = files; do fwrite(buf, 1, c, *fp); while (*++fp); } if (c < 0) { /* Make sure read errors are signaled. */ retval = EXIT_FAILURE; } #else setvbuf(stdout, NULL, _IONBF, 0); while ((c = getchar()) != EOF) { fp = files; do putc(c, *fp); while (*++fp); } #endif /* Now we need to check for i/o errors on stdin and the various * output files. Since we know that the first entry in the output * file table is stdout, we can save one "if ferror" test by * setting the first entry to stdin and checking stdout error * status with fflush_stdout_and_exit()... although fflush()ing * is unnecessary here. */ np = names; fp = files; names[0] = (char *) bb_msg_standard_input; files[0] = stdin; do { /* Now check for input and output errors. */ /* Checking ferror should be sufficient, but we may want to fclose. * If we do, remember not to close stdin! */ die_if_ferror(*fp++, *np++); } while (*fp); fflush_stdout_and_exit(retval); } busybox-1.22.1/coreutils/id_test.sh0000755000000000000000000001362012263563520015777 0ustar rootroot#!/bin/bash # Test script for busybox id vs. coreutils id. # Needs root privileges for some tests. cp /usr/bin/id . BUSYBOX=./busybox ID=./id LIST=`awk -F: '{ printf "%s\n", $1 }' /etc/passwd` FLAG_USER_EXISTS="no" TEST_USER="f583ca884c1d93458fb61ed137ff44f6" echo "test 1: id [options] nousername" rm -f foo bar for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" do #echo "$OPTIONS" $BUSYBOX id $OPTIONS >foo 2>/dev/null RET1=$? $ID $OPTIONS >bar 2>/dev/null RET2=$? if test "$RET1" != "$RET2"; then echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" fi diff foo bar done echo "test 2: id [options] username" rm -f foo bar for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" do #echo "$OPTIONS" for i in $LIST ; do if test "$i" = "$TEST_USER"; then FLAG_USER_EXISTS="yes" fi $BUSYBOX id $OPTIONS $i >foo 2>/dev/null RET1=$? $ID $OPTIONS $i >bar 2>/dev/null RET2=$? if test "$RET1" != "$RET2"; then echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" fi diff foo bar done done if test $FLAG_USER_EXISTS = "yes"; then echo "test 3,4,5,6,7,8,9,10,11,12 skipped because test user $TEST_USER already exists" rm -f foo bar exit 1 fi adduser -s /bin/true -g "" -H -D "$TEST_USER" || exit 1 chown $TEST_USER.$TEST_USER $BUSYBOX chmod u+s $BUSYBOX 2>&1 /dev/null chown $TEST_USER.$TEST_USER $ID chmod u+s $ID 2>&1 /dev/null echo "test 3 setuid, existing user: id [options] no username" rm -f foo bar for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" do #echo "$OPTIONS" $BUSYBOX id $OPTIONS >foo 2>/dev/null RET1=$? $ID $OPTIONS >bar 2>/dev/null RET2=$? if test "$RET1" != "$RET2"; then echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" fi diff foo bar #done done echo "test 4 setuid, existing user: id [options] username" rm -f foo bar for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" do #echo "$OPTIONS" for i in $LIST ; do $BUSYBOX id $OPTIONS $i >foo 2>/dev/null RET1=$? $ID $OPTIONS $i >bar 2>/dev/null RET2=$? if test "$RET1" != "$RET2"; then echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" fi diff foo bar done done chown $TEST_USER.$TEST_USER $BUSYBOX chmod g+s $BUSYBOX 2>&1 /dev/null chown $TEST_USER.$TEST_USER $ID chmod g+s $ID 2>&1 /dev/null echo "test 5 setgid, existing user: id [options] no username" rm -f foo bar for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" do #echo "$OPTIONS" $BUSYBOX id $OPTIONS >foo 2>/dev/null RET1=$? $ID $OPTIONS >bar 2>/dev/null RET2=$? if test "$RET1" != "$RET2"; then echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" fi diff foo bar #done done echo "test 6 setgid, existing user: id [options] username" rm -f foo bar for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" do #echo "$OPTIONS" for i in $LIST ; do $BUSYBOX id $OPTIONS $i >foo 2>/dev/null RET1=$? $ID $OPTIONS $i >bar 2>/dev/null RET2=$? if test "$RET1" != "$RET2"; then echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" fi diff foo bar done done chown $TEST_USER.$TEST_USER $BUSYBOX chmod u+s,g+s $BUSYBOX 2>&1 /dev/null chown $TEST_USER.$TEST_USER $ID chmod u+s,g+s $ID 2>&1 /dev/null echo "test 7 setuid, setgid, existing user: id [options] no username" rm -f foo bar for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" do #echo "$OPTIONS" $BUSYBOX id $OPTIONS >foo 2>/dev/null RET1=$? $ID $OPTIONS >bar 2>/dev/null RET2=$? if test "$RET1" != "$RET2"; then echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" fi diff foo bar #done done echo "test 8 setuid, setgid, existing user: id [options] username" rm -f foo bar for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" do #echo "$OPTIONS" for i in $LIST ; do $BUSYBOX id $OPTIONS $i >foo 2>/dev/null RET1=$? $ID $OPTIONS $i >bar 2>/dev/null RET2=$? if test "$RET1" != "$RET2"; then echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" fi diff foo bar done done deluser $TEST_USER || exit 1 echo "test 9 setuid, setgid, not existing user: id [options] no username" rm -f foo bar for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" do #echo "$OPTIONS" $BUSYBOX id $OPTIONS >foo 2>/dev/null RET1=$? $ID $OPTIONS >bar 2>/dev/null RET2=$? if test "$RET1" != "$RET2"; then echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" fi diff foo bar done echo "test 10 setuid, setgid, not existing user: id [options] username" rm -f foo bar for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" do #echo "$OPTIONS" for i in $LIST ; do $BUSYBOX id $OPTIONS $i >foo 2>/dev/null RET1=$? $ID $OPTIONS $i >bar 2>/dev/null RET2=$? if test "$RET1" != "$RET2"; then echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" fi diff foo bar done done chown .root $BUSYBOX 2>&1 /dev/null chown .root $ID 2>&1 /dev/null chmod g+s $BUSYBOX 2>&1 /dev/null chmod g+s $ID 2>&1 /dev/null echo "test 11 setgid, not existing group: id [options] no username" rm -f foo bar for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" do #echo "$OPTIONS" $BUSYBOX id $OPTIONS >foo 2>/dev/null RET1=$? $ID $OPTIONS >bar 2>/dev/null RET2=$? if test "$RET1" != "$RET2"; then echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" fi diff foo bar #done done echo "test 12 setgid, not existing group: id [options] username" rm -f foo bar for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" do #echo "$OPTIONS" for i in $LIST ; do $BUSYBOX id $OPTIONS $i >foo 2>/dev/null RET1=$? $ID $OPTIONS $i >bar 2>/dev/null RET2=$? if test "$RET1" != "$RET2"; then echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" fi diff foo bar done done chown root.root $BUSYBOX 2>&1 /dev/null chown root.root $ID 2>&1 /dev/null rm -f $ID rm -f foo bar busybox-1.22.1/coreutils/uuencode.c0000644000000000000000000000411512263563520015757 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright (C) 2000 by Glenn McGrath * * based on the function base64_encode from http.c in wget v1.6 * Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define uuencode_trivial_usage //usage: "[-m] [FILE] STORED_FILENAME" //usage:#define uuencode_full_usage "\n\n" //usage: "Uuencode FILE (or stdin) to stdout\n" //usage: "\n -m Use base64 encoding per RFC1521" //usage: //usage:#define uuencode_example_usage //usage: "$ uuencode busybox busybox\n" //usage: "begin 755 busybox\n" //usage: "\n" //usage: "$ uudecode busybox busybox > busybox.uu\n" //usage: "$\n" #include "libbb.h" enum { SRC_BUF_SIZE = 15*3, /* This *MUST* be a multiple of 3 */ DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3), }; int uuencode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int uuencode_main(int argc UNUSED_PARAM, char **argv) { struct stat stat_buf; int src_fd = STDIN_FILENO; const char *tbl; mode_t mode; char src_buf[SRC_BUF_SIZE]; char dst_buf[DST_BUF_SIZE + 1]; tbl = bb_uuenc_tbl_std; mode = 0666 & ~umask(0666); opt_complementary = "-1:?2"; /* must have 1 or 2 args */ if (getopt32(argv, "m")) { tbl = bb_uuenc_tbl_base64; } argv += optind; if (argv[1]) { src_fd = xopen(argv[0], O_RDONLY); fstat(src_fd, &stat_buf); mode = stat_buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); argv++; } printf("begin%s %o %s", tbl == bb_uuenc_tbl_std ? "" : "-base64", mode, *argv); while (1) { size_t size = full_read(src_fd, src_buf, SRC_BUF_SIZE); if (!size) break; if ((ssize_t)size < 0) bb_perror_msg_and_die(bb_msg_read_error); /* Encode the buffer we just read in */ bb_uuencode(dst_buf, src_buf, size, tbl); bb_putchar('\n'); if (tbl == bb_uuenc_tbl_std) { bb_putchar(tbl[size]); } fflush(stdout); xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3)); } printf(tbl == bb_uuenc_tbl_std ? "\n`\nend\n" : "\n====\n"); fflush_stdout_and_exit(EXIT_SUCCESS); } busybox-1.22.1/coreutils/realpath.c0000644000000000000000000000174112263563520015752 0ustar rootroot/* vi: set sw=4 ts=4: */ /* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Now does proper error checking on output and returns a failure exit code * if one or more paths cannot be resolved. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define realpath_trivial_usage //usage: "FILE..." //usage:#define realpath_full_usage "\n\n" //usage: "Return the absolute pathnames of given FILE" #include "libbb.h" int realpath_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int realpath_main(int argc UNUSED_PARAM, char **argv) { int retval = EXIT_SUCCESS; if (!*++argv) { bb_show_usage(); } do { char *resolved_path = xmalloc_realpath(*argv); if (resolved_path != NULL) { puts(resolved_path); free(resolved_path); } else { retval = EXIT_FAILURE; bb_simple_perror_msg(*argv); } } while (*++argv); fflush_stdout_and_exit(retval); } busybox-1.22.1/coreutils/chgrp.c0000644000000000000000000000324512263563520015256 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini chgrp implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 defects - none? */ /* BB_AUDIT GNU defects - unsupported long options. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/chgrp.html */ //usage:#define chgrp_trivial_usage //usage: "[-RhLHP"IF_DESKTOP("cvf")"]... GROUP FILE..." //usage:#define chgrp_full_usage "\n\n" //usage: "Change the group membership of each FILE to GROUP\n" //usage: "\n -R Recurse" //usage: "\n -h Affect symlinks instead of symlink targets" //usage: "\n -L Traverse all symlinks to directories" //usage: "\n -H Traverse symlinks on command line only" //usage: "\n -P Don't traverse symlinks (default)" //usage: IF_DESKTOP( //usage: "\n -c List changed files" //usage: "\n -v Verbose" //usage: "\n -f Hide errors" //usage: ) //usage: //usage:#define chgrp_example_usage //usage: "$ ls -l /tmp/foo\n" //usage: "-r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo\n" //usage: "$ chgrp root /tmp/foo\n" //usage: "$ ls -l /tmp/foo\n" //usage: "-r--r--r-- 1 andersen root 0 Apr 12 18:25 /tmp/foo\n" #include "libbb.h" /* This is a NOEXEC applet. Be very careful! */ int chgrp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int chgrp_main(int argc, char **argv) { /* "chgrp [opts] abc file(s)" == "chown [opts] :abc file(s)" */ char **p = argv; while (*++p) { if (p[0][0] != '-') { p[0] = xasprintf(":%s", p[0]); break; } } return chown_main(argc, argv); } busybox-1.22.1/coreutils/cksum.c0000644000000000000000000000340112263563520015267 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * cksum - calculate the CRC32 checksum of a file * * Copyright (C) 2006 by Rob Sullivan, with ideas from code by Walter Harms * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define cksum_trivial_usage //usage: "FILES..." //usage:#define cksum_full_usage "\n\n" //usage: "Calculate the CRC32 checksums of FILES" #include "libbb.h" /* This is a NOEXEC applet. Be very careful! */ int cksum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int cksum_main(int argc UNUSED_PARAM, char **argv) { uint32_t *crc32_table = crc32_filltable(NULL, 1); uint32_t crc; off_t length, filesize; int bytes_read; int exit_code = EXIT_SUCCESS; #if ENABLE_DESKTOP getopt32(argv, ""); /* coreutils 6.9 compat */ argv += optind; #else argv++; #endif do { int fd = open_or_warn_stdin(*argv ? *argv : bb_msg_standard_input); if (fd < 0) { exit_code = EXIT_FAILURE; continue; } crc = 0; length = 0; #define read_buf bb_common_bufsiz1 while ((bytes_read = safe_read(fd, read_buf, sizeof(read_buf))) > 0) { length += bytes_read; crc = crc32_block_endian1(crc, read_buf, bytes_read, crc32_table); } close(fd); filesize = length; while (length) { crc = (crc << 8) ^ crc32_table[(uint8_t)(crc >> 24) ^ (uint8_t)length]; /* must ensure that shift is unsigned! */ if (sizeof(length) <= sizeof(unsigned)) length = (unsigned)length >> 8; else if (sizeof(length) <= sizeof(unsigned long)) length = (unsigned long)length >> 8; else length = (unsigned long long)length >> 8; } crc = ~crc; printf((*argv ? "%"PRIu32" %"OFF_FMT"i %s\n" : "%"PRIu32" %"OFF_FMT"i\n"), crc, filesize, *argv); } while (*argv && *++argv); fflush_stdout_and_exit(exit_code); } busybox-1.22.1/coreutils/md5_sha1_sum.c0000644000000000000000000001703412263563520016441 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright (C) 2003 Glenn L. McGrath * Copyright (C) 2003-2004 Erik Andersen * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define md5sum_trivial_usage //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." //usage:#define md5sum_full_usage "\n\n" //usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " MD5 checksums" //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" //usage: "\n -c Check sums against list in FILEs" //usage: "\n -s Don't output anything, status code shows success" //usage: "\n -w Warn about improperly formatted checksum lines" //usage: ) //usage: //usage:#define md5sum_example_usage //usage: "$ md5sum < busybox\n" //usage: "6fd11e98b98a58f64ff3398d7b324003\n" //usage: "$ md5sum busybox\n" //usage: "6fd11e98b98a58f64ff3398d7b324003 busybox\n" //usage: "$ md5sum -c -\n" //usage: "6fd11e98b98a58f64ff3398d7b324003 busybox\n" //usage: "busybox: OK\n" //usage: "^D\n" //usage: //usage:#define sha1sum_trivial_usage //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." //usage:#define sha1sum_full_usage "\n\n" //usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA1 checksums" //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" //usage: "\n -c Check sums against list in FILEs" //usage: "\n -s Don't output anything, status code shows success" //usage: "\n -w Warn about improperly formatted checksum lines" //usage: ) //usage: //usage:#define sha256sum_trivial_usage //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." //usage:#define sha256sum_full_usage "\n\n" //usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA256 checksums" //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" //usage: "\n -c Check sums against list in FILEs" //usage: "\n -s Don't output anything, status code shows success" //usage: "\n -w Warn about improperly formatted checksum lines" //usage: ) //usage: //usage:#define sha512sum_trivial_usage //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." //usage:#define sha512sum_full_usage "\n\n" //usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA512 checksums" //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" //usage: "\n -c Check sums against list in FILEs" //usage: "\n -s Don't output anything, status code shows success" //usage: "\n -w Warn about improperly formatted checksum lines" //usage: ) //usage: //usage:#define sha3sum_trivial_usage //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." //usage:#define sha3sum_full_usage "\n\n" //usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA3-512 checksums" //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" //usage: "\n -c Check sums against list in FILEs" //usage: "\n -s Don't output anything, status code shows success" //usage: "\n -w Warn about improperly formatted checksum lines" //usage: ) #include "libbb.h" /* This is a NOEXEC applet. Be very careful! */ enum { /* 4th letter of applet_name is... */ HASH_MD5 = 's', /* "md5>s 0) { update(&context, in_buf, count); } hash_value = NULL; if (count < 0) bb_perror_msg("can't read '%s'", filename); else /* count == 0 */ { final(&context, in_buf); hash_value = hash_bin_to_hex(in_buf, hash_len); } RELEASE_CONFIG_BUFFER(in_buf); } if (src_fd != STDIN_FILENO) { close(src_fd); } return hash_value; } int md5_sha1_sum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv) { int return_value = EXIT_SUCCESS; unsigned flags; if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK) { /* -b "binary", -t "text" are ignored (shaNNNsum compat) */ flags = getopt32(argv, "scwbt"); argv += optind; //argc -= optind; } else { argv += 1; //argc -= 1; } if (!*argv) *--argv = (char*)"-"; if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && !(flags & FLAG_CHECK)) { if (flags & FLAG_SILENT) { bb_error_msg_and_die("-%c is meaningful only with -c", 's'); } if (flags & FLAG_WARN) { bb_error_msg_and_die("-%c is meaningful only with -c", 'w'); } } do { if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) { FILE *pre_computed_stream; char *line; int count_total = 0; int count_failed = 0; pre_computed_stream = xfopen_stdin(*argv); while ((line = xmalloc_fgetline(pre_computed_stream)) != NULL) { uint8_t *hash_value; char *filename_ptr; count_total++; filename_ptr = strstr(line, " "); /* handle format for binary checksums */ if (filename_ptr == NULL) { filename_ptr = strstr(line, " *"); } if (filename_ptr == NULL) { if (flags & FLAG_WARN) { bb_error_msg("invalid format"); } count_failed++; return_value = EXIT_FAILURE; free(line); continue; } *filename_ptr = '\0'; filename_ptr += 2; hash_value = hash_file(filename_ptr); if (hash_value && (strcmp((char*)hash_value, line) == 0)) { if (!(flags & FLAG_SILENT)) printf("%s: OK\n", filename_ptr); } else { if (!(flags & FLAG_SILENT)) printf("%s: FAILED\n", filename_ptr); count_failed++; return_value = EXIT_FAILURE; } /* possible free(NULL) */ free(hash_value); free(line); } if (count_failed && !(flags & FLAG_SILENT)) { bb_error_msg("WARNING: %d of %d computed checksums did NOT match", count_failed, count_total); } fclose_if_not_stdin(pre_computed_stream); } else { uint8_t *hash_value = hash_file(*argv); if (hash_value == NULL) { return_value = EXIT_FAILURE; } else { printf("%s %s\n", hash_value, *argv); free(hash_value); } } } while (*++argv); return return_value; } busybox-1.22.1/coreutils/chroot.c0000644000000000000000000000235612263563520015453 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini chroot implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ //usage:#define chroot_trivial_usage //usage: "NEWROOT [PROG ARGS]" //usage:#define chroot_full_usage "\n\n" //usage: "Run PROG with root directory set to NEWROOT" //usage: //usage:#define chroot_example_usage //usage: "$ ls -l /bin/ls\n" //usage: "lrwxrwxrwx 1 root root 12 Apr 13 00:46 /bin/ls -> /BusyBox\n" //usage: "# mount /dev/hdc1 /mnt -t minix\n" //usage: "# chroot /mnt\n" //usage: "# ls -l /bin/ls\n" //usage: "-rwxr-xr-x 1 root root 40816 Feb 5 07:45 /bin/ls*\n" #include "libbb.h" int chroot_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int chroot_main(int argc UNUSED_PARAM, char **argv) { ++argv; if (!*argv) bb_show_usage(); xchroot(*argv); ++argv; if (!*argv) { /* no 2nd param (PROG), use shell */ argv -= 2; argv[0] = (char *) get_shell_name(); argv[1] = (char *) "-i"; /* GNU coreutils 8.4 compat */ /*argv[2] = NULL; - already is */ } BB_EXECVP_or_die(argv); } busybox-1.22.1/coreutils/od.c0000644000000000000000000001435312263563520014557 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * od implementation for busybox * Based on code from util-linux v 2.11l * * Copyright (c) 1990 * The Regents of the University of California. All rights reserved. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Original copyright notice is retained at the end of this file. */ //usage:#if !ENABLE_DESKTOP //usage:#define od_trivial_usage //usage: "[-aBbcDdeFfHhIiLlOovXx] [FILE]" //usage:#define od_full_usage "\n\n" //usage: "Print FILE (or stdin) unambiguously, as octal bytes by default" //usage:#endif #include "libbb.h" #if ENABLE_DESKTOP /* This one provides -t (busybox's own build script needs it) */ #include "od_bloaty.c" #else #include "dump.h" static void odoffset(dumper_t *dumper, int argc, char ***argvp) { char *num, *p; int base; char *end; /* * The offset syntax of od(1) was genuinely bizarre. First, if * it started with a plus it had to be an offset. Otherwise, if * there were at least two arguments, a number or lower-case 'x' * followed by a number makes it an offset. By default it was * octal; if it started with 'x' or '0x' it was hex. If it ended * in a '.', it was decimal. If a 'b' or 'B' was appended, it * multiplied the number by 512 or 1024 byte units. There was * no way to assign a block count to a hex offset. * * We assumes it's a file if the offset is bad. */ p = **argvp; if (!p) { /* hey someone is probably piping to us ... */ return; } if ((*p != '+') && (argc < 2 || (!isdigit(p[0]) && ((p[0] != 'x') || !isxdigit(p[1]))))) return; base = 0; /* * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and * set base. */ if (p[0] == '+') ++p; if (p[0] == 'x' && isxdigit(p[1])) { ++p; base = 16; } else if (p[0] == '0' && p[1] == 'x') { p += 2; base = 16; } /* skip over the number */ if (base == 16) for (num = p; isxdigit(*p); ++p) continue; else for (num = p; isdigit(*p); ++p) continue; /* check for no number */ if (num == p) return; /* if terminates with a '.', base is decimal */ if (*p == '.') { if (base) return; base = 10; } dumper->dump_skip = strtol(num, &end, base ? base : 8); /* if end isn't the same as p, we got a non-octal digit */ if (end != p) dumper->dump_skip = 0; else { if (*p) { if (*p == 'b') { dumper->dump_skip *= 512; ++p; } else if (*p == 'B') { dumper->dump_skip *= 1024; ++p; } } if (*p) dumper->dump_skip = 0; else { ++*argvp; /* * If the offset uses a non-octal base, the base of * the offset is changed as well. This isn't pretty, * but it's easy. */ #define TYPE_OFFSET 7 { char x_or_d; if (base == 16) { x_or_d = 'x'; goto DO_X_OR_D; } if (base == 10) { x_or_d = 'd'; DO_X_OR_D: dumper->fshead->nextfu->fmt[TYPE_OFFSET] = dumper->fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = x_or_d; } } } } } static const char *const add_strings[] = { "16/1 \"%3_u \" \"\\n\"", /* a */ "8/2 \" %06o \" \"\\n\"", /* B, o */ "16/1 \"%03o \" \"\\n\"", /* b */ "16/1 \"%3_c \" \"\\n\"", /* c */ "8/2 \" %05u \" \"\\n\"", /* d */ "4/4 \" %010u \" \"\\n\"", /* D */ "2/8 \" %21.14e \" \"\\n\"", /* e (undocumented in od), F */ "4/4 \" %14.7e \" \"\\n\"", /* f */ "4/4 \" %08x \" \"\\n\"", /* H, X */ "8/2 \" %04x \" \"\\n\"", /* h, x */ "4/4 \" %11d \" \"\\n\"", /* I, L, l */ "8/2 \" %6d \" \"\\n\"", /* i */ "4/4 \" %011o \" \"\\n\"", /* O */ }; static const char od_opts[] ALIGN1 = "aBbcDdeFfHhIiLlOoXxv"; static const char od_o2si[] ALIGN1 = { 0, 1, 2, 3, 5, 4, 6, 6, 7, 8, 9, 0xa, 0xb, 0xa, 0xa, 0xb, 1, 8, 9, }; int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int od_main(int argc, char **argv) { int ch; int first = 1; char *p; dumper_t *dumper = alloc_dumper(); while ((ch = getopt(argc, argv, od_opts)) > 0) { if (ch == 'v') { dumper->dump_vflag = ALL; } else if (((p = strchr(od_opts, ch)) != NULL) && (*p != '\0')) { if (first) { first = 0; bb_dump_add(dumper, "\"%07.7_Ao\n\""); bb_dump_add(dumper, "\"%07.7_ao \""); } else { bb_dump_add(dumper, "\" \""); } bb_dump_add(dumper, add_strings[(int)od_o2si[(p - od_opts)]]); } else { /* P, p, s, w, or other unhandled */ bb_show_usage(); } } if (!dumper->fshead) { bb_dump_add(dumper, "\"%07.7_Ao\n\""); bb_dump_add(dumper, "\"%07.7_ao \" 8/2 \"%06o \" \"\\n\""); } argc -= optind; argv += optind; odoffset(dumper, argc, &argv); return bb_dump_dump(dumper, argv); } #endif /* ENABLE_DESKTOP */ /*- * Copyright (c) 1990 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. */ busybox-1.22.1/coreutils/length.c.disabled0000644000000000000000000000131712263563520017200 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 N/A -- Apparently a busybox (obsolete?) extension. */ //usage:#define length_trivial_usage //usage: "STRING" //usage:#define length_full_usage "\n\n" //usage: "Print STRING's length" //usage: //usage:#define length_example_usage //usage: "$ length Hello\n" //usage: "5\n" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ int length_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int length_main(int argc, char **argv) { if ((argc != 2) || (**(++argv) == '-')) { bb_show_usage(); } printf("%u\n", (unsigned)strlen(*argv)); return fflush_all(); } busybox-1.22.1/coreutils/dd.c0000644000000000000000000002717512263563520014552 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini dd implementation for busybox * * * Copyright (C) 2000,2001 Matt Kraai * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define dd_trivial_usage //usage: "[if=FILE] [of=FILE] " IF_FEATURE_DD_IBS_OBS("[ibs=N] [obs=N] ") "[bs=N] [count=N] [skip=N]\n" //usage: " [seek=N]" IF_FEATURE_DD_IBS_OBS(" [conv=notrunc|noerror|sync|fsync]") //usage:#define dd_full_usage "\n\n" //usage: "Copy a file with converting and formatting\n" //usage: "\n if=FILE Read from FILE instead of stdin" //usage: "\n of=FILE Write to FILE instead of stdout" //usage: "\n bs=N Read and write N bytes at a time" //usage: IF_FEATURE_DD_IBS_OBS( //usage: "\n ibs=N Read N bytes at a time" //usage: ) //usage: IF_FEATURE_DD_IBS_OBS( //usage: "\n obs=N Write N bytes at a time" //usage: ) //usage: "\n count=N Copy only N input blocks" //usage: "\n skip=N Skip N input blocks" //usage: "\n seek=N Skip N output blocks" //usage: IF_FEATURE_DD_IBS_OBS( //usage: "\n conv=notrunc Don't truncate output file" //usage: "\n conv=noerror Continue after read errors" //usage: "\n conv=sync Pad blocks with zeros" //usage: "\n conv=fsync Physically write data out before finishing" //usage: "\n conv=swab Swap every pair of bytes" //usage: ) //usage: "\n" //usage: "\nN may be suffixed by c (1), w (2), b (512), kD (1000), k (1024), MD, M, GD, G" //usage: //usage:#define dd_example_usage //usage: "$ dd if=/dev/zero of=/dev/ram1 bs=1M count=4\n" //usage: "4+0 records in\n" //usage: "4+0 records out\n" #include "libbb.h" /* This is a NOEXEC applet. Be very careful! */ enum { ifd = STDIN_FILENO, ofd = STDOUT_FILENO, }; static const struct suffix_mult dd_suffixes[] = { { "c", 1 }, { "w", 2 }, { "b", 512 }, { "kD", 1000 }, { "k", 1024 }, { "K", 1024 }, /* compat with coreutils dd */ { "MD", 1000000 }, { "M", 1048576 }, { "GD", 1000000000 }, { "G", 1073741824 }, { "", 0 } }; struct globals { off_t out_full, out_part, in_full, in_part; #if ENABLE_FEATURE_DD_THIRD_STATUS_LINE unsigned long long total_bytes; unsigned long long begin_time_us; #endif } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { \ /* we have to zero it out because of NOEXEC */ \ memset(&G, 0, sizeof(G)); \ } while (0) static void dd_output_status(int UNUSED_PARAM cur_signal) { #if ENABLE_FEATURE_DD_THIRD_STATUS_LINE double seconds; unsigned long long bytes_sec; unsigned long long now_us = monotonic_us(); /* before fprintf */ #endif /* Deliberately using %u, not %d */ fprintf(stderr, "%"OFF_FMT"u+%"OFF_FMT"u records in\n" "%"OFF_FMT"u+%"OFF_FMT"u records out\n", G.in_full, G.in_part, G.out_full, G.out_part); #if ENABLE_FEATURE_DD_THIRD_STATUS_LINE fprintf(stderr, "%llu bytes (%sB) copied, ", G.total_bytes, /* show fractional digit, use suffixes */ make_human_readable_str(G.total_bytes, 1, 0) ); /* Corner cases: * ./busybox dd /dev/null * ./busybox dd bs=1M count=2000 /dev/null * (echo DONE) | ./busybox dd >/dev/null * (sleep 1; echo DONE) | ./busybox dd >/dev/null */ seconds = (now_us - G.begin_time_us) / 1000000.0; bytes_sec = G.total_bytes / seconds; fprintf(stderr, "%f seconds, %sB/s\n", seconds, /* show fractional digit, use suffixes */ make_human_readable_str(bytes_sec, 1, 0) ); #endif } static ssize_t full_write_or_warn(const void *buf, size_t len, const char *const filename) { ssize_t n = full_write(ofd, buf, len); if (n < 0) bb_perror_msg("writing '%s'", filename); return n; } static bool write_and_stats(const void *buf, size_t len, size_t obs, const char *filename) { ssize_t n = full_write_or_warn(buf, len, filename); if (n < 0) return 1; if ((size_t)n == obs) G.out_full++; else if (n) /* > 0 */ G.out_part++; #if ENABLE_FEATURE_DD_THIRD_STATUS_LINE G.total_bytes += n; #endif return 0; } #if ENABLE_LFS # define XATOU_SFX xatoull_sfx #else # define XATOU_SFX xatoul_sfx #endif int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int dd_main(int argc UNUSED_PARAM, char **argv) { enum { /* Must be in the same order as OP_conv_XXX! */ /* (see "flags |= (1 << what)" below) */ FLAG_NOTRUNC = (1 << 0) * ENABLE_FEATURE_DD_IBS_OBS, FLAG_SYNC = (1 << 1) * ENABLE_FEATURE_DD_IBS_OBS, FLAG_NOERROR = (1 << 2) * ENABLE_FEATURE_DD_IBS_OBS, FLAG_FSYNC = (1 << 3) * ENABLE_FEATURE_DD_IBS_OBS, FLAG_SWAB = (1 << 4) * ENABLE_FEATURE_DD_IBS_OBS, /* end of conv flags */ FLAG_TWOBUFS = (1 << 5) * ENABLE_FEATURE_DD_IBS_OBS, FLAG_COUNT = 1 << 6, }; static const char keywords[] ALIGN1 = "bs\0""count\0""seek\0""skip\0""if\0""of\0" #if ENABLE_FEATURE_DD_IBS_OBS "ibs\0""obs\0""conv\0" #endif ; #if ENABLE_FEATURE_DD_IBS_OBS static const char conv_words[] ALIGN1 = "notrunc\0""sync\0""noerror\0""fsync\0""swab\0"; #endif enum { OP_bs = 0, OP_count, OP_seek, OP_skip, OP_if, OP_of, #if ENABLE_FEATURE_DD_IBS_OBS OP_ibs, OP_obs, OP_conv, /* Must be in the same order as FLAG_XXX! */ OP_conv_notrunc = 0, OP_conv_sync, OP_conv_noerror, OP_conv_fsync, OP_conv_swab, /* Unimplemented conv=XXX: */ //nocreat do not create the output file //excl fail if the output file already exists //fdatasync physically write output file data before finishing //lcase change upper case to lower case //ucase change lower case to upper case //block pad newline-terminated records with spaces to cbs-size //unblock replace trailing spaces in cbs-size records with newline //ascii from EBCDIC to ASCII //ebcdic from ASCII to EBCDIC //ibm from ASCII to alternate EBCDIC /* Partially implemented: */ //swab swap every pair of input bytes: will abort on non-even reads #endif }; smallint exitcode = EXIT_FAILURE; int i; size_t ibs = 512; char *ibuf; #if ENABLE_FEATURE_DD_IBS_OBS size_t obs = 512; char *obuf; #else # define obs ibs # define obuf ibuf #endif /* These are all zeroed at once! */ struct { int flags; size_t oc; ssize_t prev_read_size; /* for detecting swab failure */ off_t count; off_t seek, skip; const char *infile, *outfile; } Z; #define flags (Z.flags ) #define oc (Z.oc ) #define prev_read_size (Z.prev_read_size) #define count (Z.count ) #define seek (Z.seek ) #define skip (Z.skip ) #define infile (Z.infile ) #define outfile (Z.outfile) memset(&Z, 0, sizeof(Z)); INIT_G(); //fflush_all(); - is this needed because of NOEXEC? for (i = 1; argv[i]; i++) { int what; char *val; char *arg = argv[i]; #if ENABLE_DESKTOP /* "dd --". NB: coreutils 6.9 will complain if they see * more than one of them. We wouldn't. */ if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0') continue; #endif val = strchr(arg, '='); if (val == NULL) bb_show_usage(); *val = '\0'; what = index_in_strings(keywords, arg); if (what < 0) bb_show_usage(); /* *val = '='; - to preserve ps listing? */ val++; #if ENABLE_FEATURE_DD_IBS_OBS if (what == OP_ibs) { /* Must fit into positive ssize_t */ ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes); /*continue;*/ } if (what == OP_obs) { obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes); /*continue;*/ } if (what == OP_conv) { while (1) { int n; /* find ',', replace them with NUL so we can use val for * index_in_strings() without copying. * We rely on val being non-null, else strchr would fault. */ arg = strchr(val, ','); if (arg) *arg = '\0'; n = index_in_strings(conv_words, val); if (n < 0) bb_error_msg_and_die(bb_msg_invalid_arg, val, "conv"); flags |= (1 << n); if (!arg) /* no ',' left, so this was the last specifier */ break; /* *arg = ','; - to preserve ps listing? */ val = arg + 1; /* skip this keyword and ',' */ } /*continue;*/ } #endif if (what == OP_bs) { ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes); obs = ibs; /*continue;*/ } /* These can be large: */ if (what == OP_count) { flags |= FLAG_COUNT; count = XATOU_SFX(val, dd_suffixes); /*continue;*/ } if (what == OP_seek) { seek = XATOU_SFX(val, dd_suffixes); /*continue;*/ } if (what == OP_skip) { skip = XATOU_SFX(val, dd_suffixes); /*continue;*/ } if (what == OP_if) { infile = val; /*continue;*/ } if (what == OP_of) { outfile = val; /*continue;*/ } } /* end of "for (argv[i])" */ //XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever ibuf = xmalloc(ibs); obuf = ibuf; #if ENABLE_FEATURE_DD_IBS_OBS if (ibs != obs) { flags |= FLAG_TWOBUFS; obuf = xmalloc(obs); } #endif #if ENABLE_FEATURE_DD_SIGNAL_HANDLING signal_SA_RESTART_empty_mask(SIGUSR1, dd_output_status); #endif #if ENABLE_FEATURE_DD_THIRD_STATUS_LINE G.begin_time_us = monotonic_us(); #endif if (infile) { xmove_fd(xopen(infile, O_RDONLY), ifd); } else { infile = bb_msg_standard_input; } if (outfile) { int oflag = O_WRONLY | O_CREAT; if (!seek && !(flags & FLAG_NOTRUNC)) oflag |= O_TRUNC; xmove_fd(xopen(outfile, oflag), ofd); if (seek && !(flags & FLAG_NOTRUNC)) { if (ftruncate(ofd, seek * obs) < 0) { struct stat st; if (fstat(ofd, &st) < 0 || S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) ) { goto die_outfile; } } } } else { outfile = bb_msg_standard_output; } if (skip) { if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) { do { ssize_t n = safe_read(ifd, ibuf, ibs); if (n < 0) goto die_infile; if (n == 0) break; } while (--skip != 0); } } if (seek) { if (lseek(ofd, seek * obs, SEEK_CUR) < 0) goto die_outfile; } while (!(flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) { ssize_t n; n = safe_read(ifd, ibuf, ibs); if (n == 0) break; if (n < 0) { /* "Bad block" */ if (!(flags & FLAG_NOERROR)) goto die_infile; bb_simple_perror_msg(infile); /* GNU dd with conv=noerror skips over bad blocks */ xlseek(ifd, ibs, SEEK_CUR); /* conv=noerror,sync writes NULs, * conv=noerror just ignores input bad blocks */ n = 0; } if (flags & FLAG_SWAB) { uint16_t *p16; ssize_t n2; /* Our code allows only last read to be odd-sized */ if (prev_read_size & 1) bb_error_msg_and_die("can't swab %lu byte buffer", (unsigned long)prev_read_size); prev_read_size = n; /* If n is odd, last byte is not swapped: * echo -n "qwe" | dd conv=swab * prints "wqe". */ p16 = (void*) ibuf; n2 = (n >> 1); while (--n2 >= 0) { *p16 = bswap_16(*p16); p16++; } } if ((size_t)n == ibs) G.in_full++; else { G.in_part++; if (flags & FLAG_SYNC) { memset(ibuf + n, 0, ibs - n); n = ibs; } } if (flags & FLAG_TWOBUFS) { char *tmp = ibuf; while (n) { size_t d = obs - oc; if (d > (size_t)n) d = n; memcpy(obuf + oc, tmp, d); n -= d; tmp += d; oc += d; if (oc == obs) { if (write_and_stats(obuf, obs, obs, outfile)) goto out_status; oc = 0; } } } else { if (write_and_stats(ibuf, n, obs, outfile)) goto out_status; } if (flags & FLAG_FSYNC) { if (fsync(ofd) < 0) goto die_outfile; } } if (ENABLE_FEATURE_DD_IBS_OBS && oc) { if (write_and_stats(obuf, oc, obs, outfile)) goto out_status; } if (close(ifd) < 0) { die_infile: bb_simple_perror_msg_and_die(infile); } if (close(ofd) < 0) { die_outfile: bb_simple_perror_msg_and_die(outfile); } exitcode = EXIT_SUCCESS; out_status: dd_output_status(0); if (ENABLE_FEATURE_CLEAN_UP) { free(obuf); if (flags & FLAG_TWOBUFS) free(ibuf); } return exitcode; } busybox-1.22.1/coreutils/tail.c0000644000000000000000000002312512267106022015075 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini tail implementation for busybox * * Copyright (C) 2001 by Matt Kraai * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant (need fancy for -c) */ /* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Pretty much rewritten to fix numerous bugs and reduce realloc() calls. * Bugs fixed (although I may have forgotten one or two... it was pretty bad) * 1) mixing printf/write without fflush()ing stdout * 2) no check that any open files are present * 3) optstring had -q taking an arg * 4) no error checking on write in some cases, and a warning even then * 5) q and s interaction bug * 6) no check for lseek error * 7) lseek attempted when count==0 even if arg was +0 (from top) */ //kbuild:lib-$(CONFIG_TAIL) += tail.o //usage:#define tail_trivial_usage //usage: "[OPTIONS] [FILE]..." //usage:#define tail_full_usage "\n\n" //usage: "Print last 10 lines of each FILE (or stdin) to stdout.\n" //usage: "With more than one FILE, precede each with a filename header.\n" //usage: "\n -f Print data as file grows" //usage: IF_FEATURE_FANCY_TAIL( //usage: "\n -s SECONDS Wait SECONDS between reads with -f" //usage: ) //usage: "\n -n N[kbm] Print last N lines" //usage: "\n -n +N[kbm] Start on Nth line and print the rest" //usage: IF_FEATURE_FANCY_TAIL( //usage: "\n -c [+]N[kbm] Print last N bytes" //usage: "\n -q Never print headers" //usage: "\n -v Always print headers" //usage: "\n" //usage: "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)." //usage: ) //usage: //usage:#define tail_example_usage //usage: "$ tail -n 1 /etc/resolv.conf\n" //usage: "nameserver 10.0.0.1\n" #include "libbb.h" struct globals { bool from_top; bool exitcode; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { } while (0) static void tail_xprint_header(const char *fmt, const char *filename) { if (fdprintf(STDOUT_FILENO, fmt, filename) < 0) bb_perror_nomsg_and_die(); } static ssize_t tail_read(int fd, char *buf, size_t count) { ssize_t r; r = full_read(fd, buf, count); if (r < 0) { bb_perror_msg(bb_msg_read_error); G.exitcode = EXIT_FAILURE; } return r; } #define header_fmt_str "\n==> %s <==\n" static unsigned eat_num(const char *p) { if (*p == '-') p++; else if (*p == '+') { p++; G.from_top = 1; } return xatou_sfx(p, bkm_suffixes); } int tail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int tail_main(int argc, char **argv) { unsigned count = 10; unsigned sleep_period = 1; const char *str_c, *str_n; char *tailbuf; size_t tailbufsize; unsigned header_threshhold = 1; unsigned nfiles; int i, opt; int *fds; const char *fmt; int prev_fd; INIT_G(); #if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_TAIL /* Allow legacy syntax of an initial numeric option without -n. */ if (argv[1] && (argv[1][0] == '+' || argv[1][0] == '-') && isdigit(argv[1][1]) ) { count = eat_num(argv[1]); argv++; argc--; } #endif /* -s NUM, -F imlies -f */ IF_FEATURE_FANCY_TAIL(opt_complementary = "s+:Ff";) opt = getopt32(argv, "fc:n:" IF_FEATURE_FANCY_TAIL("qs:vF"), &str_c, &str_n IF_FEATURE_FANCY_TAIL(,&sleep_period)); #define FOLLOW (opt & 0x1) #define COUNT_BYTES (opt & 0x2) //if (opt & 0x1) // -f if (opt & 0x2) count = eat_num(str_c); // -c if (opt & 0x4) count = eat_num(str_n); // -n #if ENABLE_FEATURE_FANCY_TAIL /* q: make it impossible for nfiles to be > header_threshhold */ if (opt & 0x8) header_threshhold = UINT_MAX; // -q //if (opt & 0x10) // -s if (opt & 0x20) header_threshhold = 0; // -v # define FOLLOW_RETRY (opt & 0x40) #else # define FOLLOW_RETRY 0 #endif argc -= optind; argv += optind; /* open all the files */ fds = xmalloc(sizeof(fds[0]) * (argc + 1)); if (!argv[0]) { struct stat statbuf; if (fstat(STDIN_FILENO, &statbuf) == 0 && S_ISFIFO(statbuf.st_mode) ) { opt &= ~1; /* clear FOLLOW */ } argv[0] = (char *) bb_msg_standard_input; } nfiles = i = 0; do { int fd = open_or_warn_stdin(argv[i]); if (fd < 0 && !FOLLOW_RETRY) { G.exitcode = EXIT_FAILURE; continue; } fds[nfiles] = fd; argv[nfiles++] = argv[i]; } while (++i < argc); if (!nfiles) bb_error_msg_and_die("no files"); /* prepare the buffer */ tailbufsize = BUFSIZ; if (!G.from_top && COUNT_BYTES) { if (tailbufsize < count + BUFSIZ) { tailbufsize = count + BUFSIZ; } } /* tail -c1024m REGULAR_FILE doesn't really need 1G mem block. * (In fact, it doesn't need ANY memory). So delay allocation. */ tailbuf = NULL; /* tail the files */ fmt = header_fmt_str + 1; /* skip leading newline in the header on the first output */ i = 0; do { char *buf; int taillen; int newlines_seen; unsigned seen; int nread; int fd = fds[i]; if (ENABLE_FEATURE_FANCY_TAIL && fd < 0) continue; /* may happen with -F */ if (nfiles > header_threshhold) { tail_xprint_header(fmt, argv[i]); fmt = header_fmt_str; } if (!G.from_top) { off_t current = lseek(fd, 0, SEEK_END); if (current > 0) { unsigned off; if (COUNT_BYTES) { /* Optimizing count-bytes case if the file is seekable. * Beware of backing up too far. * Also we exclude files with size 0 (because of /proc/xxx) */ if (count == 0) continue; /* showing zero bytes is easy :) */ current -= count; if (current < 0) current = 0; xlseek(fd, current, SEEK_SET); bb_copyfd_size(fd, STDOUT_FILENO, count); continue; } #if 1 /* This is technically incorrect for *LONG* strings, but very useful */ /* Optimizing count-lines case if the file is seekable. * We assume the lines are <64k. * (Users complain that tail takes too long * on multi-gigabyte files) */ off = (count | 0xf); /* for small counts, be more paranoid */ if (off > (INT_MAX / (64*1024))) off = (INT_MAX / (64*1024)); current -= off * (64*1024); if (current < 0) current = 0; xlseek(fd, current, SEEK_SET); #endif } } if (!tailbuf) tailbuf = xmalloc(tailbufsize); buf = tailbuf; taillen = 0; /* "We saw 1st line/byte". * Used only by +N code ("start from Nth", 1-based): */ seen = 1; newlines_seen = 0; while ((nread = tail_read(fd, buf, tailbufsize - taillen)) > 0) { if (G.from_top) { int nwrite = nread; if (seen < count) { /* We need to skip a few more bytes/lines */ if (COUNT_BYTES) { nwrite -= (count - seen); seen += nread; } else { char *s = buf; do { --nwrite; if (*s++ == '\n' && ++seen == count) { break; } } while (nwrite); } } if (nwrite > 0) xwrite(STDOUT_FILENO, buf + nread - nwrite, nwrite); } else if (count) { if (COUNT_BYTES) { taillen += nread; if (taillen > (int)count) { memmove(tailbuf, tailbuf + taillen - count, count); taillen = count; } } else { int k = nread; int newlines_in_buf = 0; do { /* count '\n' in last read */ k--; if (buf[k] == '\n') { newlines_in_buf++; } } while (k); if (newlines_seen + newlines_in_buf < (int)count) { newlines_seen += newlines_in_buf; taillen += nread; } else { int extra = (buf[nread-1] != '\n'); char *s; k = newlines_seen + newlines_in_buf + extra - count; s = tailbuf; while (k) { if (*s == '\n') { k--; } s++; } taillen += nread - (s - tailbuf); memmove(tailbuf, s, taillen); newlines_seen = count - extra; } if (tailbufsize < (size_t)taillen + BUFSIZ) { tailbufsize = taillen + BUFSIZ; tailbuf = xrealloc(tailbuf, tailbufsize); } } buf = tailbuf + taillen; } } /* while (tail_read() > 0) */ if (!G.from_top) { xwrite(STDOUT_FILENO, tailbuf, taillen); } } while (++i < nfiles); prev_fd = fds[i-1]; tailbuf = xrealloc(tailbuf, BUFSIZ); fmt = NULL; if (FOLLOW) while (1) { sleep(sleep_period); i = 0; do { int nread; const char *filename = argv[i]; int fd = fds[i]; if (FOLLOW_RETRY) { struct stat sbuf, fsbuf; if (fd < 0 || fstat(fd, &fsbuf) < 0 || stat(filename, &sbuf) < 0 || fsbuf.st_dev != sbuf.st_dev || fsbuf.st_ino != sbuf.st_ino ) { int new_fd; if (fd >= 0) close(fd); new_fd = open(filename, O_RDONLY); if (new_fd >= 0) { bb_error_msg("%s has %s; following end of new file", filename, (fd < 0) ? "appeared" : "been replaced" ); } else if (fd >= 0) { bb_perror_msg("%s has become inaccessible", filename); } fds[i] = fd = new_fd; } } if (ENABLE_FEATURE_FANCY_TAIL && fd < 0) continue; if (nfiles > header_threshhold) { fmt = header_fmt_str; } for (;;) { /* tail -f keeps following files even if they are truncated */ struct stat sbuf; /* /proc files report zero st_size, don't lseek them */ if (fstat(fd, &sbuf) == 0 && sbuf.st_size > 0) { off_t current = lseek(fd, 0, SEEK_CUR); if (sbuf.st_size < current) xlseek(fd, 0, SEEK_SET); } nread = tail_read(fd, tailbuf, BUFSIZ); if (nread <= 0) break; if (fmt && (fd != prev_fd)) { tail_xprint_header(fmt, filename); fmt = NULL; prev_fd = fd; } xwrite(STDOUT_FILENO, tailbuf, nread); } } while (++i < nfiles); } /* while (1) */ if (ENABLE_FEATURE_CLEAN_UP) { free(fds); free(tailbuf); } return G.exitcode; } busybox-1.22.1/coreutils/who.c0000644000000000000000000000700612263563520014747 0ustar rootroot/* vi: set sw=4 ts=4: */ /*---------------------------------------------------------------------- * Mini who is used to display user name, login time, * idle time and host name. * * Author: Da Chen * * This is a free document; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation: * http://www.gnu.org/copyleft/gpl.html * * Copyright (c) 2002 AYR Networks, Inc. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * *---------------------------------------------------------------------- */ /* BB_AUDIT SUSv3 _NOT_ compliant -- missing options -b, -d, -l, -m, -p, -q, -r, -s, -t, -T, -u; Missing argument 'file'. */ //config:config WHO //config: bool "who" //config: default y //config: depends on FEATURE_UTMP //config: help //config: who is used to show who is logged on. //config:config USERS //config: bool "users" //config: default y //config: depends on FEATURE_UTMP //config: help //config: Print users currently logged on. //applet:IF_USERS(APPLET_ODDNAME(users, who, BB_DIR_USR_BIN, BB_SUID_DROP, users)) //applet:IF_WHO( APPLET( who, BB_DIR_USR_BIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_USERS) += who.o //kbuild:lib-$(CONFIG_WHO) += who.o //usage:#define users_trivial_usage //usage: "" //usage:#define users_full_usage "\n\n" //usage: "Print the users currently logged on" //usage:#define who_trivial_usage //usage: "[-a]" //usage:#define who_full_usage "\n\n" //usage: "Show who is logged on\n" //usage: "\n -a Show all" //usage: "\n -H Print column headers" #include "libbb.h" static void idle_string(char *str6, time_t t) { t = time(NULL) - t; /*if (t < 60) { str6[0] = '.'; str6[1] = '\0'; return; }*/ if (t >= 0 && t < (24 * 60 * 60)) { sprintf(str6, "%02d:%02d", (int) (t / (60 * 60)), (int) ((t % (60 * 60)) / 60)); return; } strcpy(str6, "old"); } int who_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int who_main(int argc UNUSED_PARAM, char **argv) { struct utmp *ut; unsigned opt; int do_users = (ENABLE_USERS && (!ENABLE_WHO || applet_name[0] == 'u')); const char *fmt = "%s"; opt_complementary = "=0"; opt = getopt32(argv, do_users ? "" : "aH"); if (opt & 2) // -H printf("USER\t\tTTY\t\tIDLE\tTIME\t\t HOST\n"); setutent(); while ((ut = getutent()) != NULL) { if (ut->ut_user[0] && ((opt & 1) || ut->ut_type == USER_PROCESS) ) { if (!do_users) { char str6[6]; char name[sizeof("/dev/") + sizeof(ut->ut_line) + 1]; struct stat st; time_t seconds; str6[0] = '?'; str6[1] = '\0'; strcpy(name, "/dev/"); safe_strncpy(ut->ut_line[0] == '/' ? name : name + sizeof("/dev/")-1, ut->ut_line, sizeof(ut->ut_line)+1 ); if (stat(name, &st) == 0) idle_string(str6, st.st_atime); /* manpages say ut_tv.tv_sec *is* time_t, * but some systems have it wrong */ seconds = ut->ut_tv.tv_sec; /* How wide time field can be? * "Nov 10 19:33:20": 15 chars * "2010-11-10 19:33": 16 chars */ printf("%-15.*s %-15.*s %-7s %-16.16s %.*s\n", (int)sizeof(ut->ut_user), ut->ut_user, (int)sizeof(ut->ut_line), ut->ut_line, str6, ctime(&seconds) + 4, (int)sizeof(ut->ut_host), ut->ut_host ); } else { printf(fmt, ut->ut_user); fmt = " %s"; } } } if (do_users) bb_putchar('\n'); if (ENABLE_FEATURE_CLEAN_UP) endutent(); return EXIT_SUCCESS; } busybox-1.22.1/coreutils/fold.c0000644000000000000000000001123012263563520015070 0ustar rootroot/* vi: set sw=4 ts=4: */ /* fold -- wrap each input line to fit in specified width. Written by David MacKenzie, djm@gnu.ai.mit.edu. Copyright (C) 91, 1995-2002 Free Software Foundation, Inc. Modified for busybox based on coreutils v 5.0 Copyright (C) 2003 Glenn McGrath Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define fold_trivial_usage //usage: "[-bs] [-w WIDTH] [FILE]..." //usage:#define fold_full_usage "\n\n" //usage: "Wrap input lines in each FILE (or stdin), writing to stdout\n" //usage: "\n -b Count bytes rather than columns" //usage: "\n -s Break at spaces" //usage: "\n -w Use WIDTH columns instead of 80" #include "libbb.h" #include "unicode.h" /* This is a NOEXEC applet. Be very careful! */ /* Must match getopt32 call */ #define FLAG_COUNT_BYTES 1 #define FLAG_BREAK_SPACES 2 #define FLAG_WIDTH 4 /* Assuming the current column is COLUMN, return the column that printing C will move the cursor to. The first column is 0. */ static int adjust_column(unsigned column, char c) { if (option_mask32 & FLAG_COUNT_BYTES) return ++column; if (c == '\t') return column + 8 - column % 8; if (c == '\b') { if ((int)--column < 0) column = 0; } else if (c == '\r') column = 0; else { /* just a printable char */ if (unicode_status != UNICODE_ON /* every byte is a new char */ || (c & 0xc0) != 0x80 /* it isn't a 2nd+ byte of a Unicode char */ ) { column++; } } return column; } /* Note that this function can write NULs, unlike fputs etc. */ static void write2stdout(const void *buf, unsigned size) { fwrite(buf, 1, size, stdout); } int fold_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fold_main(int argc UNUSED_PARAM, char **argv) { char *line_out = NULL; const char *w_opt = "80"; unsigned width; smallint exitcode = EXIT_SUCCESS; init_unicode(); if (ENABLE_INCLUDE_SUSv2) { /* Turn any numeric options into -w options. */ int i; for (i = 1; argv[i]; i++) { const char *a = argv[i]; if (*a == '-') { a++; if (*a == '-' && !a[1]) /* "--" */ break; if (isdigit(*a)) argv[i] = xasprintf("-w%s", a); } } } getopt32(argv, "bsw:", &w_opt); width = xatou_range(w_opt, 1, 10000); argv += optind; if (!*argv) *--argv = (char*)"-"; do { FILE *istream = fopen_or_warn_stdin(*argv); int c; unsigned column = 0; /* Screen column where next char will go */ unsigned offset_out = 0; /* Index in 'line_out' for next char */ if (istream == NULL) { exitcode = EXIT_FAILURE; continue; } while ((c = getc(istream)) != EOF) { /* We grow line_out in chunks of 0x1000 bytes */ if ((offset_out & 0xfff) == 0) { line_out = xrealloc(line_out, offset_out + 0x1000); } rescan: line_out[offset_out] = c; if (c == '\n') { write2stdout(line_out, offset_out + 1); column = offset_out = 0; continue; } column = adjust_column(column, c); if (column <= width || offset_out == 0) { /* offset_out == 0 case happens * with small width (say, 1) and tabs. * The very first tab already goes to column 8, * but we must not wrap it */ offset_out++; continue; } /* This character would make the line too long. * Print the line plus a newline, and make this character * start the next line */ if (option_mask32 & FLAG_BREAK_SPACES) { unsigned i; unsigned logical_end; /* Look for the last blank. */ for (logical_end = offset_out - 1; (int)logical_end >= 0; logical_end--) { if (!isblank(line_out[logical_end])) continue; /* Found a space or tab. * Output up to and including it, and start a new line */ logical_end++; /*line_out[logical_end] = '\n'; - NO! this nukes one buffered character */ write2stdout(line_out, logical_end); putchar('\n'); /* Move the remainder to the beginning of the next line. * The areas being copied here might overlap. */ memmove(line_out, line_out + logical_end, offset_out - logical_end); offset_out -= logical_end; for (column = i = 0; i < offset_out; i++) { column = adjust_column(column, line_out[i]); } goto rescan; } /* No blank found, wrap will split the overlong word */ } /* Output what we accumulated up to now, and start a new line */ line_out[offset_out] = '\n'; write2stdout(line_out, offset_out + 1); column = offset_out = 0; goto rescan; } /* while (not EOF) */ if (offset_out) { write2stdout(line_out, offset_out); } if (fclose_if_not_stdin(istream)) { bb_simple_perror_msg(*argv); exitcode = EXIT_FAILURE; } } while (*++argv); fflush_stdout_and_exit(exitcode); } busybox-1.22.1/coreutils/comm.c0000644000000000000000000000441312263563520015104 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini comm implementation for busybox * * Copyright (C) 2005 by Robert Sullivan * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define comm_trivial_usage //usage: "[-123] FILE1 FILE2" //usage:#define comm_full_usage "\n\n" //usage: "Compare FILE1 with FILE2\n" //usage: "\n -1 Suppress lines unique to FILE1" //usage: "\n -2 Suppress lines unique to FILE2" //usage: "\n -3 Suppress lines common to both files" #include "libbb.h" #define COMM_OPT_1 (1 << 0) #define COMM_OPT_2 (1 << 1) #define COMM_OPT_3 (1 << 2) /* writeline outputs the input given, appropriately aligned according to class */ static void writeline(char *line, int class) { int flags = option_mask32; if (class == 0) { if (flags & COMM_OPT_1) return; } else if (class == 1) { if (flags & COMM_OPT_2) return; if (!(flags & COMM_OPT_1)) putchar('\t'); } else /*if (class == 2)*/ { if (flags & COMM_OPT_3) return; if (!(flags & COMM_OPT_1)) putchar('\t'); if (!(flags & COMM_OPT_2)) putchar('\t'); } puts(line); } int comm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int comm_main(int argc UNUSED_PARAM, char **argv) { char *thisline[2]; FILE *stream[2]; int i; int order; opt_complementary = "=2"; getopt32(argv, "123"); argv += optind; for (i = 0; i < 2; ++i) { stream[i] = xfopen_stdin(argv[i]); } order = 0; thisline[1] = thisline[0] = NULL; while (1) { if (order <= 0) { free(thisline[0]); thisline[0] = xmalloc_fgetline(stream[0]); } if (order >= 0) { free(thisline[1]); thisline[1] = xmalloc_fgetline(stream[1]); } i = !thisline[0] + (!thisline[1] << 1); if (i) break; order = strcmp(thisline[0], thisline[1]); if (order >= 0) writeline(thisline[1], order ? 1 : 2); else writeline(thisline[0], 0); } /* EOF at least on one of the streams */ i &= 1; if (thisline[i]) { /* stream[i] is not at EOF yet */ /* we did not print thisline[i] yet */ char *p = thisline[i]; writeline(p, i); while (1) { free(p); p = xmalloc_fgetline(stream[i]); if (!p) break; writeline(p, i); } } if (ENABLE_FEATURE_CLEAN_UP) { fclose(stream[0]); fclose(stream[1]); } return EXIT_SUCCESS; } busybox-1.22.1/coreutils/stat.c0000644000000000000000000005517612263563520015140 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * stat -- display file or file system status * * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation. * Copyright (C) 2005 by Erik Andersen * Copyright (C) 2005 by Mike Frysinger * Copyright (C) 2006 by Yoshinori Sato * * Written by Michael Meskes * Taken from coreutils and turned into a busybox applet by Mike Frysinger * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define stat_trivial_usage //usage: "[OPTIONS] FILE..." //usage:#define stat_full_usage "\n\n" //usage: "Display file (default) or filesystem status\n" //usage: IF_FEATURE_STAT_FORMAT( //usage: "\n -c fmt Use the specified format" //usage: ) //usage: "\n -f Display filesystem status" //usage: "\n -L Follow links" //usage: "\n -t Display info in terse form" //usage: IF_SELINUX( //usage: "\n -Z Print security context" //usage: ) //usage: IF_FEATURE_STAT_FORMAT( //usage: "\n\nValid format sequences for files:\n" //usage: " %a Access rights in octal\n" //usage: " %A Access rights in human readable form\n" //usage: " %b Number of blocks allocated (see %B)\n" //usage: " %B The size in bytes of each block reported by %b\n" //usage: " %d Device number in decimal\n" //usage: " %D Device number in hex\n" //usage: " %f Raw mode in hex\n" //usage: " %F File type\n" //usage: " %g Group ID of owner\n" //usage: " %G Group name of owner\n" //usage: " %h Number of hard links\n" //usage: " %i Inode number\n" //usage: " %n File name\n" //usage: " %N File name, with -> TARGET if symlink\n" //usage: " %o I/O block size\n" //usage: " %s Total size, in bytes\n" //usage: " %t Major device type in hex\n" //usage: " %T Minor device type in hex\n" //usage: " %u User ID of owner\n" //usage: " %U User name of owner\n" //usage: " %x Time of last access\n" //usage: " %X Time of last access as seconds since Epoch\n" //usage: " %y Time of last modification\n" //usage: " %Y Time of last modification as seconds since Epoch\n" //usage: " %z Time of last change\n" //usage: " %Z Time of last change as seconds since Epoch\n" //usage: "\nValid format sequences for file systems:\n" //usage: " %a Free blocks available to non-superuser\n" //usage: " %b Total data blocks in file system\n" //usage: " %c Total file nodes in file system\n" //usage: " %d Free file nodes in file system\n" //usage: " %f Free blocks in file system\n" //usage: IF_SELINUX( //usage: " %C Security context in selinux\n" //usage: ) //usage: " %i File System ID in hex\n" //usage: " %l Maximum length of filenames\n" //usage: " %n File name\n" //usage: " %s Block size (for faster transfer)\n" //usage: " %S Fundamental block size (for block counts)\n" //usage: " %t Type in hex\n" //usage: " %T Type in human readable form" //usage: ) #include "libbb.h" #define OPT_FILESYS (1 << 0) #define OPT_TERSE (1 << 1) #define OPT_DEREFERENCE (1 << 2) #define OPT_SELINUX (1 << 3) #if ENABLE_FEATURE_STAT_FORMAT typedef bool (*statfunc_ptr)(const char *, const char *); #else typedef bool (*statfunc_ptr)(const char *); #endif static const char *file_type(const struct stat *st) { /* See POSIX 1003.1-2001 XCU Table 4-8 lines 17093-17107 * for some of these formats. * To keep diagnostics grammatical in English, the * returned string must start with a consonant. */ if (S_ISREG(st->st_mode)) return st->st_size == 0 ? "regular empty file" : "regular file"; if (S_ISDIR(st->st_mode)) return "directory"; if (S_ISBLK(st->st_mode)) return "block special file"; if (S_ISCHR(st->st_mode)) return "character special file"; if (S_ISFIFO(st->st_mode)) return "fifo"; if (S_ISLNK(st->st_mode)) return "symbolic link"; if (S_ISSOCK(st->st_mode)) return "socket"; #ifdef S_TYPEISMQ if (S_TYPEISMQ(st)) return "message queue"; #endif #ifdef S_TYPEISSEM if (S_TYPEISSEM(st)) return "semaphore"; #endif #ifdef S_TYPEISSHM if (S_TYPEISSHM(st)) return "shared memory object"; #endif #ifdef S_TYPEISTMO if (S_TYPEISTMO(st)) return "typed memory object"; #endif return "weird file"; } static const char *human_time(time_t t) { /* Old static char *str; str = ctime(&t); str[strlen(str)-1] = '\0'; return str; */ /* coreutils 6.3 compat: */ /*static char buf[sizeof("YYYY-MM-DD HH:MM:SS.000000000")] ALIGN1;*/ #define buf bb_common_bufsiz1 strcpy(strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &t), ".000000000"); return buf; #undef buf } /* Return the type of the specified file system. * Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris) * Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2) * Still others have neither and have to get by with f_type (Linux). */ static const char *human_fstype(uint32_t f_type) { static const struct types { uint32_t type; const char *const fs; } humantypes[] = { { 0xADFF, "affs" }, { 0x1Cd1, "devpts" }, { 0x137D, "ext" }, { 0xEF51, "ext2" }, { 0xEF53, "ext2/ext3" }, { 0x3153464a, "jfs" }, { 0x58465342, "xfs" }, { 0xF995E849, "hpfs" }, { 0x9660, "isofs" }, { 0x4000, "isofs" }, { 0x4004, "isofs" }, { 0x137F, "minix" }, { 0x138F, "minix (30 char.)" }, { 0x2468, "minix v2" }, { 0x2478, "minix v2 (30 char.)" }, { 0x4d44, "msdos" }, { 0x4006, "fat" }, { 0x564c, "novell" }, { 0x6969, "nfs" }, { 0x9fa0, "proc" }, { 0x517B, "smb" }, { 0x012FF7B4, "xenix" }, { 0x012FF7B5, "sysv4" }, { 0x012FF7B6, "sysv2" }, { 0x012FF7B7, "coh" }, { 0x00011954, "ufs" }, { 0x012FD16D, "xia" }, { 0x5346544e, "ntfs" }, { 0x1021994, "tmpfs" }, { 0x52654973, "reiserfs" }, { 0x28cd3d45, "cramfs" }, { 0x7275, "romfs" }, { 0x858458f6, "romfs" }, { 0x73717368, "squashfs" }, { 0x62656572, "sysfs" }, { 0, "UNKNOWN" } }; int i; for (i = 0; humantypes[i].type; ++i) if (humantypes[i].type == f_type) break; return humantypes[i].fs; } /* "man statfs" says that statfsbuf->f_fsid is a mess */ /* coreutils treats it as an array of ints, most significant first */ static unsigned long long get_f_fsid(const struct statfs *statfsbuf) { const unsigned *p = (const void*) &statfsbuf->f_fsid; unsigned sz = sizeof(statfsbuf->f_fsid) / sizeof(unsigned); unsigned long long r = 0; do r = (r << (sizeof(unsigned)*8)) | *p++; while (--sz > 0); return r; } #if ENABLE_FEATURE_STAT_FORMAT static void strcatc(char *str, char c) { int len = strlen(str); str[len++] = c; str[len] = '\0'; } static void printfs(char *pformat, const char *msg) { strcatc(pformat, 's'); printf(pformat, msg); } /* print statfs info */ static void FAST_FUNC print_statfs(char *pformat, const char m, const char *const filename, const void *data IF_SELINUX(, security_context_t scontext)) { const struct statfs *statfsbuf = data; if (m == 'n') { printfs(pformat, filename); } else if (m == 'i') { strcat(pformat, "llx"); printf(pformat, get_f_fsid(statfsbuf)); } else if (m == 'l') { strcat(pformat, "lu"); printf(pformat, (unsigned long) statfsbuf->f_namelen); } else if (m == 't') { strcat(pformat, "lx"); printf(pformat, (unsigned long) statfsbuf->f_type); /* no equiv */ } else if (m == 'T') { printfs(pformat, human_fstype(statfsbuf->f_type)); } else if (m == 'b') { strcat(pformat, "llu"); printf(pformat, (unsigned long long) statfsbuf->f_blocks); } else if (m == 'f') { strcat(pformat, "llu"); printf(pformat, (unsigned long long) statfsbuf->f_bfree); } else if (m == 'a') { strcat(pformat, "llu"); printf(pformat, (unsigned long long) statfsbuf->f_bavail); } else if (m == 's' || m == 'S') { strcat(pformat, "lu"); printf(pformat, (unsigned long) statfsbuf->f_bsize); } else if (m == 'c') { strcat(pformat, "llu"); printf(pformat, (unsigned long long) statfsbuf->f_files); } else if (m == 'd') { strcat(pformat, "llu"); printf(pformat, (unsigned long long) statfsbuf->f_ffree); # if ENABLE_SELINUX } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) { printfs(pformat, scontext); # endif } else { strcatc(pformat, 'c'); printf(pformat, m); } } /* print stat info */ static void FAST_FUNC print_stat(char *pformat, const char m, const char *const filename, const void *data IF_SELINUX(, security_context_t scontext)) { #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) struct stat *statbuf = (struct stat *) data; struct passwd *pw_ent; struct group *gw_ent; if (m == 'n') { printfs(pformat, filename); } else if (m == 'N') { strcatc(pformat, 's'); if (S_ISLNK(statbuf->st_mode)) { char *linkname = xmalloc_readlink_or_warn(filename); if (linkname == NULL) return; printf("'%s' -> '%s'", filename, linkname); free(linkname); } else { printf(pformat, filename); } } else if (m == 'd') { strcat(pformat, "llu"); printf(pformat, (unsigned long long) statbuf->st_dev); } else if (m == 'D') { strcat(pformat, "llx"); printf(pformat, (unsigned long long) statbuf->st_dev); } else if (m == 'i') { strcat(pformat, "llu"); printf(pformat, (unsigned long long) statbuf->st_ino); } else if (m == 'a') { strcat(pformat, "lo"); printf(pformat, (unsigned long) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO))); } else if (m == 'A') { printfs(pformat, bb_mode_string(statbuf->st_mode)); } else if (m == 'f') { strcat(pformat, "lx"); printf(pformat, (unsigned long) statbuf->st_mode); } else if (m == 'F') { printfs(pformat, file_type(statbuf)); } else if (m == 'h') { strcat(pformat, "lu"); printf(pformat, (unsigned long) statbuf->st_nlink); } else if (m == 'u') { strcat(pformat, "lu"); printf(pformat, (unsigned long) statbuf->st_uid); } else if (m == 'U') { pw_ent = getpwuid(statbuf->st_uid); printfs(pformat, (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN"); } else if (m == 'g') { strcat(pformat, "lu"); printf(pformat, (unsigned long) statbuf->st_gid); } else if (m == 'G') { gw_ent = getgrgid(statbuf->st_gid); printfs(pformat, (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN"); } else if (m == 't') { strcat(pformat, "lx"); printf(pformat, (unsigned long) major(statbuf->st_rdev)); } else if (m == 'T') { strcat(pformat, "lx"); printf(pformat, (unsigned long) minor(statbuf->st_rdev)); } else if (m == 's') { strcat(pformat, "llu"); printf(pformat, (unsigned long long) statbuf->st_size); } else if (m == 'B') { strcat(pformat, "lu"); printf(pformat, (unsigned long) 512); //ST_NBLOCKSIZE } else if (m == 'b') { strcat(pformat, "llu"); printf(pformat, (unsigned long long) statbuf->st_blocks); } else if (m == 'o') { strcat(pformat, "lu"); printf(pformat, (unsigned long) statbuf->st_blksize); } else if (m == 'x') { printfs(pformat, human_time(statbuf->st_atime)); } else if (m == 'X') { strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu"); /* note: (unsigned long) would be wrong: * imagine (unsigned long64)int32 */ printf(pformat, (long) statbuf->st_atime); } else if (m == 'y') { printfs(pformat, human_time(statbuf->st_mtime)); } else if (m == 'Y') { strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu"); printf(pformat, (long) statbuf->st_mtime); } else if (m == 'z') { printfs(pformat, human_time(statbuf->st_ctime)); } else if (m == 'Z') { strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu"); printf(pformat, (long) statbuf->st_ctime); # if ENABLE_SELINUX } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) { printfs(pformat, scontext); # endif } else { strcatc(pformat, 'c'); printf(pformat, m); } } static void print_it(const char *masterformat, const char *filename, void FAST_FUNC (*print_func)(char*, char, const char*, const void* IF_SELINUX(, security_context_t scontext)), const void *data IF_SELINUX(, security_context_t scontext)) { /* Create a working copy of the format string */ char *format = xstrdup(masterformat); /* Add 2 to accomodate our conversion of the stat '%s' format string * to the printf '%llu' one. */ char *dest = xmalloc(strlen(format) + 2 + 1); char *b; b = format; while (b) { /* Each iteration finds next %spec, * prints preceding string and handles found %spec */ size_t len; char *p = strchr(b, '%'); if (!p) { /* coreutils 6.3 always prints newline at the end */ /*fputs(b, stdout);*/ puts(b); break; } /* dest = "%" */ len = 1 + strspn(p + 1, "#-+.I 0123456789"); memcpy(dest, p, len); dest[len] = '\0'; /* print preceding string */ *p = '\0'; fputs(b, stdout); p += len; b = p + 1; switch (*p) { case '\0': b = NULL; /* fall through */ case '%': bb_putchar('%'); break; default: /* Completes "%" with specifier and printfs */ print_func(dest, *p, filename, data IF_SELINUX(,scontext)); break; } } free(format); free(dest); } #endif /* FEATURE_STAT_FORMAT */ /* Stat the file system and print what we find. */ #if !ENABLE_FEATURE_STAT_FORMAT #define do_statfs(filename, format) do_statfs(filename) #endif static bool do_statfs(const char *filename, const char *format) { struct statfs statfsbuf; #if !ENABLE_FEATURE_STAT_FORMAT const char *format; #endif #if ENABLE_SELINUX security_context_t scontext = NULL; if (option_mask32 & OPT_SELINUX) { if ((option_mask32 & OPT_DEREFERENCE ? lgetfilecon(filename, &scontext) : getfilecon(filename, &scontext) ) < 0 ) { bb_simple_perror_msg(filename); return 0; } } #endif if (statfs(filename, &statfsbuf) != 0) { bb_perror_msg("can't read file system information for '%s'", filename); return 0; } #if ENABLE_FEATURE_STAT_FORMAT if (format == NULL) { # if !ENABLE_SELINUX format = (option_mask32 & OPT_TERSE ? "%n %i %l %t %s %b %f %a %c %d\n" : " File: \"%n\"\n" " ID: %-8i Namelen: %-7l Type: %T\n" "Block size: %-10s\n" "Blocks: Total: %-10b Free: %-10f Available: %a\n" "Inodes: Total: %-10c Free: %d"); # else format = (option_mask32 & OPT_TERSE ? (option_mask32 & OPT_SELINUX ? "%n %i %l %t %s %b %f %a %c %d %C\n": "%n %i %l %t %s %b %f %a %c %d\n") : (option_mask32 & OPT_SELINUX ? " File: \"%n\"\n" " ID: %-8i Namelen: %-7l Type: %T\n" "Block size: %-10s\n" "Blocks: Total: %-10b Free: %-10f Available: %a\n" "Inodes: Total: %-10c Free: %d" " S_context: %C\n": " File: \"%n\"\n" " ID: %-8i Namelen: %-7l Type: %T\n" "Block size: %-10s\n" "Blocks: Total: %-10b Free: %-10f Available: %a\n" "Inodes: Total: %-10c Free: %d\n") ); # endif /* SELINUX */ } print_it(format, filename, print_statfs, &statfsbuf IF_SELINUX(, scontext)); #else /* FEATURE_STAT_FORMAT */ format = (option_mask32 & OPT_TERSE ? "%s %llx %lu " : " File: \"%s\"\n" " ID: %-8llx Namelen: %-7lu "); printf(format, filename, get_f_fsid(&statfsbuf), statfsbuf.f_namelen); if (option_mask32 & OPT_TERSE) printf("%lx ", (unsigned long) statfsbuf.f_type); else printf("Type: %s\n", human_fstype(statfsbuf.f_type)); # if !ENABLE_SELINUX format = (option_mask32 & OPT_TERSE ? "%lu %llu %llu %llu %llu %llu\n" : "Block size: %-10lu\n" "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n" "Inodes: Total: %-10llu Free: %llu\n"); printf(format, (unsigned long) statfsbuf.f_bsize, (unsigned long long) statfsbuf.f_blocks, (unsigned long long) statfsbuf.f_bfree, (unsigned long long) statfsbuf.f_bavail, (unsigned long long) statfsbuf.f_files, (unsigned long long) statfsbuf.f_ffree); # else format = (option_mask32 & OPT_TERSE ? (option_mask32 & OPT_SELINUX ? "%lu %llu %llu %llu %llu %llu %C\n" : "%lu %llu %llu %llu %llu %llu\n") : (option_mask32 & OPT_SELINUX ? "Block size: %-10lu\n" "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n" "Inodes: Total: %-10llu Free: %llu" "S_context: %C\n" : "Block size: %-10lu\n" "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n" "Inodes: Total: %-10llu Free: %llu\n" ) ); printf(format, (unsigned long) statfsbuf.f_bsize, (unsigned long long) statfsbuf.f_blocks, (unsigned long long) statfsbuf.f_bfree, (unsigned long long) statfsbuf.f_bavail, (unsigned long long) statfsbuf.f_files, (unsigned long long) statfsbuf.f_ffree, scontext); if (scontext) freecon(scontext); # endif #endif /* FEATURE_STAT_FORMAT */ return 1; } /* stat the file and print what we find */ #if !ENABLE_FEATURE_STAT_FORMAT #define do_stat(filename, format) do_stat(filename) #endif static bool do_stat(const char *filename, const char *format) { struct stat statbuf; #if ENABLE_SELINUX security_context_t scontext = NULL; if (option_mask32 & OPT_SELINUX) { if ((option_mask32 & OPT_DEREFERENCE ? lgetfilecon(filename, &scontext) : getfilecon(filename, &scontext) ) < 0 ) { bb_simple_perror_msg(filename); return 0; } } #endif if ((option_mask32 & OPT_DEREFERENCE ? stat : lstat) (filename, &statbuf) != 0) { bb_perror_msg("can't stat '%s'", filename); return 0; } #if ENABLE_FEATURE_STAT_FORMAT if (format == NULL) { # if !ENABLE_SELINUX if (option_mask32 & OPT_TERSE) { format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; } else { if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) { format = " File: %N\n" " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" "Device: %Dh/%dd\tInode: %-10i Links: %-5h" " Device type: %t,%T\n" "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" "Access: %x\n" "Modify: %y\n" "Change: %z\n"; } else { format = " File: %N\n" " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" "Device: %Dh/%dd\tInode: %-10i Links: %h\n" "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" "Access: %x\n" "Modify: %y\n" "Change: %z\n"; } } # else if (option_mask32 & OPT_TERSE) { format = (option_mask32 & OPT_SELINUX ? "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C\n" : "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n" ); } else { if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) { format = (option_mask32 & OPT_SELINUX ? " File: %N\n" " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" "Device: %Dh/%dd\tInode: %-10i Links: %-5h" " Device type: %t,%T\n" "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" " S_Context: %C\n" "Access: %x\n" "Modify: %y\n" "Change: %z\n" : " File: %N\n" " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" "Device: %Dh/%dd\tInode: %-10i Links: %-5h" " Device type: %t,%T\n" "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" "Access: %x\n" "Modify: %y\n" "Change: %z\n" ); } else { format = (option_mask32 & OPT_SELINUX ? " File: %N\n" " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" "Device: %Dh/%dd\tInode: %-10i Links: %h\n" "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" "S_Context: %C\n" "Access: %x\n" "Modify: %y\n" "Change: %z\n" : " File: %N\n" " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" "Device: %Dh/%dd\tInode: %-10i Links: %h\n" "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" "Access: %x\n" "Modify: %y\n" "Change: %z\n" ); } } # endif } print_it(format, filename, print_stat, &statbuf IF_SELINUX(, scontext)); #else /* FEATURE_STAT_FORMAT */ if (option_mask32 & OPT_TERSE) { printf("%s %llu %llu %lx %lu %lu %llx %llu %lu %lx %lx %lu %lu %lu %lu" IF_NOT_SELINUX("\n"), filename, (unsigned long long) statbuf.st_size, (unsigned long long) statbuf.st_blocks, (unsigned long) statbuf.st_mode, (unsigned long) statbuf.st_uid, (unsigned long) statbuf.st_gid, (unsigned long long) statbuf.st_dev, (unsigned long long) statbuf.st_ino, (unsigned long) statbuf.st_nlink, (unsigned long) major(statbuf.st_rdev), (unsigned long) minor(statbuf.st_rdev), (unsigned long) statbuf.st_atime, (unsigned long) statbuf.st_mtime, (unsigned long) statbuf.st_ctime, (unsigned long) statbuf.st_blksize ); # if ENABLE_SELINUX if (option_mask32 & OPT_SELINUX) printf(" %lc\n", *scontext); else bb_putchar('\n'); # endif } else { char *linkname = NULL; struct passwd *pw_ent; struct group *gw_ent; gw_ent = getgrgid(statbuf.st_gid); pw_ent = getpwuid(statbuf.st_uid); if (S_ISLNK(statbuf.st_mode)) linkname = xmalloc_readlink_or_warn(filename); if (linkname) { printf(" File: '%s' -> '%s'\n", filename, linkname); free(linkname); } else { printf(" File: '%s'\n", filename); } printf(" Size: %-10llu\tBlocks: %-10llu IO Block: %-6lu %s\n" "Device: %llxh/%llud\tInode: %-10llu Links: %-5lu", (unsigned long long) statbuf.st_size, (unsigned long long) statbuf.st_blocks, (unsigned long) statbuf.st_blksize, file_type(&statbuf), (unsigned long long) statbuf.st_dev, (unsigned long long) statbuf.st_dev, (unsigned long long) statbuf.st_ino, (unsigned long) statbuf.st_nlink); if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) printf(" Device type: %lx,%lx\n", (unsigned long) major(statbuf.st_rdev), (unsigned long) minor(statbuf.st_rdev)); else bb_putchar('\n'); printf("Access: (%04lo/%10.10s) Uid: (%5lu/%8s) Gid: (%5lu/%8s)\n", (unsigned long) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)), bb_mode_string(statbuf.st_mode), (unsigned long) statbuf.st_uid, (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN", (unsigned long) statbuf.st_gid, (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN"); # if ENABLE_SELINUX printf(" S_Context: %lc\n", *scontext); # endif printf("Access: %s\n", human_time(statbuf.st_atime)); printf("Modify: %s\n", human_time(statbuf.st_mtime)); printf("Change: %s\n", human_time(statbuf.st_ctime)); } #endif /* FEATURE_STAT_FORMAT */ return 1; } int stat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int stat_main(int argc UNUSED_PARAM, char **argv) { IF_FEATURE_STAT_FORMAT(char *format = NULL;) int i; int ok; unsigned opts; statfunc_ptr statfunc = do_stat; opt_complementary = "-1"; /* min one arg */ opts = getopt32(argv, "ftL" IF_SELINUX("Z") IF_FEATURE_STAT_FORMAT("c:", &format) ); if (opts & OPT_FILESYS) /* -f */ statfunc = do_statfs; #if ENABLE_SELINUX if (opts & OPT_SELINUX) { selinux_or_die(); } #endif ok = 1; argv += optind; for (i = 0; argv[i]; ++i) ok &= statfunc(argv[i] IF_FEATURE_STAT_FORMAT(, format)); return (ok ? EXIT_SUCCESS : EXIT_FAILURE); } busybox-1.22.1/coreutils/usleep.c0000644000000000000000000000145212263563520015446 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * usleep implementation for busybox * * Copyright (C) 2003 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */ //usage:#define usleep_trivial_usage //usage: "N" //usage:#define usleep_full_usage "\n\n" //usage: "Pause for N microseconds" //usage: //usage:#define usleep_example_usage //usage: "$ usleep 1000000\n" //usage: "[pauses for 1 second]\n" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ int usleep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int usleep_main(int argc UNUSED_PARAM, char **argv) { if (!argv[1]) { bb_show_usage(); } usleep(xatou(argv[1])); return EXIT_SUCCESS; } busybox-1.22.1/coreutils/test.c0000644000000000000000000005103112263563520015126 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * test implementation for busybox * * Copyright (c) by a whole pile of folks: * * test(1); version 7-like -- author Erik Baalbergen * modified by Eric Gisin to be used as built-in. * modified by Arnold Robbins to add SVR3 compatibility * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). * modified by J.T. Conklin for NetBSD. * modified by Herbert Xu to be used as built-in in ash. * modified by Erik Andersen to be used * in busybox. * modified by Bernhard Reutner-Fischer to be useable (i.e. a bit less bloaty). * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Original copyright notice states: * "This program is in the Public Domain." */ //kbuild:lib-$(CONFIG_TEST) += test.o test_ptr_hack.o //kbuild:lib-$(CONFIG_ASH) += test.o test_ptr_hack.o //kbuild:lib-$(CONFIG_HUSH) += test.o test_ptr_hack.o //config:config TEST //config: bool "test" //config: default y //config: help //config: test is used to check file types and compare values, //config: returning an appropriate exit code. The bash shell //config: has test built in, ash can build it in optionally. //config: //config:config FEATURE_TEST_64 //config: bool "Extend test to 64 bit" //config: default y //config: depends on TEST || ASH_BUILTIN_TEST || HUSH //config: help //config: Enable 64-bit support in test. /* "test --help" does not print help (POSIX compat), only "[ --help" does. * We display " EXPRESSION ]" here (not " EXPRESSION") * Unfortunately, it screws up generated BusyBox.html. TODO. */ //usage:#define test_trivial_usage //usage: "EXPRESSION ]" //usage:#define test_full_usage "\n\n" //usage: "Check file types, compare values etc. Return a 0/1 exit code\n" //usage: "depending on logical value of EXPRESSION" //usage: //usage:#define test_example_usage //usage: "$ test 1 -eq 2\n" //usage: "$ echo $?\n" //usage: "1\n" //usage: "$ test 1 -eq 1\n" //usage: "$ echo $?\n" //usage: "0\n" //usage: "$ [ -d /etc ]\n" //usage: "$ echo $?\n" //usage: "0\n" //usage: "$ [ -d /junk ]\n" //usage: "$ echo $?\n" //usage: "1\n" #include "libbb.h" #include /* This is a NOFORK applet. Be very careful! */ /* test_main() is called from shells, and we need to be extra careful here. * This is true regardless of PREFER_APPLETS and SH_STANDALONE * state. */ /* test(1) accepts the following grammar: oexpr ::= aexpr | aexpr "-o" oexpr ; aexpr ::= nexpr | nexpr "-a" aexpr ; nexpr ::= primary | "!" primary primary ::= unary-operator operand | operand binary-operator operand | operand | "(" oexpr ")" ; unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| "-nt"|"-ot"|"-ef"; operand ::= */ /* TODO: handle [[ expr ]] bashism bash-compatibly. * [[ ]] is meant to be a "better [ ]", with less weird syntax * and without the risk of variables and quoted strings misinterpreted * as operators. * This will require support from shells - we need to know quote status * of each parameter (see below). * * Word splitting and pathname expansion should NOT be performed: * # a="a b"; [[ $a = "a b" ]] && echo YES * YES * # [[ /bin/m* ]] && echo YES * YES * * =~ should do regexp match * = and == should do pattern match against right side: * # [[ *a* == bab ]] && echo YES * # [[ bab == *a* ]] && echo YES * YES * != does the negated == (i.e., also with pattern matching). * Pattern matching is quotation-sensitive: * # [[ bab == "b"a* ]] && echo YES * YES * # [[ bab == b"a*" ]] && echo YES * * Conditional operators such as -f must be unquoted literals to be recognized: * # [[ -e /bin ]] && echo YES * YES * # [[ '-e' /bin ]] && echo YES * bash: conditional binary operator expected... * # A='-e'; [[ $A /bin ]] && echo YES * bash: conditional binary operator expected... * * || and && should work as -o and -a work in [ ] * -a and -o aren't recognized (&& and || are to be used instead) * ( and ) do not need to be quoted unlike in [ ]: * # [[ ( abc ) && '' ]] && echo YES * # [[ ( abc ) || '' ]] && echo YES * YES * # [[ ( abc ) -o '' ]] && echo YES * bash: syntax error in conditional expression... * * Apart from the above, [[ expr ]] should work as [ expr ] */ #define TEST_DEBUG 0 enum token { EOI, FILRD, /* file access */ FILWR, FILEX, FILEXIST, FILREG, /* file type */ FILDIR, FILCDEV, FILBDEV, FILFIFO, FILSOCK, FILSYM, FILGZ, FILTT, FILSUID, /* file bit */ FILSGID, FILSTCK, FILNT, /* file ops */ FILOT, FILEQ, FILUID, FILGID, STREZ, /* str ops */ STRNZ, STREQ, STRNE, STRLT, STRGT, INTEQ, /* int ops */ INTNE, INTGE, INTGT, INTLE, INTLT, UNOT, BAND, BOR, LPAREN, RPAREN, OPERAND }; #define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5) #define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5) #define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2) #define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2) #define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5) #define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2) #if TEST_DEBUG int depth; #define nest_msg(...) do { \ depth++; \ fprintf(stderr, "%*s", depth*2, ""); \ fprintf(stderr, __VA_ARGS__); \ } while (0) #define unnest_msg(...) do { \ fprintf(stderr, "%*s", depth*2, ""); \ fprintf(stderr, __VA_ARGS__); \ depth--; \ } while (0) #define dbg_msg(...) do { \ fprintf(stderr, "%*s", depth*2, ""); \ fprintf(stderr, __VA_ARGS__); \ } while (0) #define unnest_msg_and_return(expr, ...) do { \ number_t __res = (expr); \ fprintf(stderr, "%*s", depth*2, ""); \ fprintf(stderr, __VA_ARGS__, res); \ depth--; \ return __res; \ } while (0) static const char *const TOKSTR[] = { "EOI", "FILRD", "FILWR", "FILEX", "FILEXIST", "FILREG", "FILDIR", "FILCDEV", "FILBDEV", "FILFIFO", "FILSOCK", "FILSYM", "FILGZ", "FILTT", "FILSUID", "FILSGID", "FILSTCK", "FILNT", "FILOT", "FILEQ", "FILUID", "FILGID", "STREZ", "STRNZ", "STREQ", "STRNE", "STRLT", "STRGT", "INTEQ", "INTNE", "INTGE", "INTGT", "INTLE", "INTLT", "UNOT", "BAND", "BOR", "LPAREN", "RPAREN", "OPERAND" }; #else #define nest_msg(...) ((void)0) #define unnest_msg(...) ((void)0) #define dbg_msg(...) ((void)0) #define unnest_msg_and_return(expr, ...) return expr #endif enum { UNOP, BINOP, BUNOP, BBINOP, PAREN }; struct operator_t { unsigned char op_num, op_type; }; static const struct operator_t ops_table[] = { { /* "-r" */ FILRD , UNOP }, { /* "-w" */ FILWR , UNOP }, { /* "-x" */ FILEX , UNOP }, { /* "-e" */ FILEXIST, UNOP }, { /* "-f" */ FILREG , UNOP }, { /* "-d" */ FILDIR , UNOP }, { /* "-c" */ FILCDEV , UNOP }, { /* "-b" */ FILBDEV , UNOP }, { /* "-p" */ FILFIFO , UNOP }, { /* "-u" */ FILSUID , UNOP }, { /* "-g" */ FILSGID , UNOP }, { /* "-k" */ FILSTCK , UNOP }, { /* "-s" */ FILGZ , UNOP }, { /* "-t" */ FILTT , UNOP }, { /* "-z" */ STREZ , UNOP }, { /* "-n" */ STRNZ , UNOP }, { /* "-h" */ FILSYM , UNOP }, /* for backwards compat */ { /* "-O" */ FILUID , UNOP }, { /* "-G" */ FILGID , UNOP }, { /* "-L" */ FILSYM , UNOP }, { /* "-S" */ FILSOCK , UNOP }, { /* "=" */ STREQ , BINOP }, { /* "==" */ STREQ , BINOP }, { /* "!=" */ STRNE , BINOP }, { /* "<" */ STRLT , BINOP }, { /* ">" */ STRGT , BINOP }, { /* "-eq"*/ INTEQ , BINOP }, { /* "-ne"*/ INTNE , BINOP }, { /* "-ge"*/ INTGE , BINOP }, { /* "-gt"*/ INTGT , BINOP }, { /* "-le"*/ INTLE , BINOP }, { /* "-lt"*/ INTLT , BINOP }, { /* "-nt"*/ FILNT , BINOP }, { /* "-ot"*/ FILOT , BINOP }, { /* "-ef"*/ FILEQ , BINOP }, { /* "!" */ UNOT , BUNOP }, { /* "-a" */ BAND , BBINOP }, { /* "-o" */ BOR , BBINOP }, { /* "(" */ LPAREN , PAREN }, { /* ")" */ RPAREN , PAREN }, }; /* Please keep these two tables in sync */ static const char ops_texts[] ALIGN1 = "-r" "\0" "-w" "\0" "-x" "\0" "-e" "\0" "-f" "\0" "-d" "\0" "-c" "\0" "-b" "\0" "-p" "\0" "-u" "\0" "-g" "\0" "-k" "\0" "-s" "\0" "-t" "\0" "-z" "\0" "-n" "\0" "-h" "\0" "-O" "\0" "-G" "\0" "-L" "\0" "-S" "\0" "=" "\0" "==" "\0" "!=" "\0" "<" "\0" ">" "\0" "-eq" "\0" "-ne" "\0" "-ge" "\0" "-gt" "\0" "-le" "\0" "-lt" "\0" "-nt" "\0" "-ot" "\0" "-ef" "\0" "!" "\0" "-a" "\0" "-o" "\0" "(" "\0" ")" "\0" ; #if ENABLE_FEATURE_TEST_64 typedef int64_t number_t; #else typedef int number_t; #endif /* We try to minimize both static and stack usage. */ struct test_statics { char **args; /* set only by check_operator(), either to bogus struct * or points to matching operator_t struct. Never NULL. */ const struct operator_t *last_operator; gid_t *group_array; int ngroups; jmp_buf leaving; }; /* See test_ptr_hack.c */ extern struct test_statics *const test_ptr_to_statics; #define S (*test_ptr_to_statics) #define args (S.args ) #define last_operator (S.last_operator) #define group_array (S.group_array ) #define ngroups (S.ngroups ) #define leaving (S.leaving ) #define INIT_S() do { \ (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \ barrier(); \ } while (0) #define DEINIT_S() do { \ free(test_ptr_to_statics); \ } while (0) static number_t primary(enum token n); static void syntax(const char *op, const char *msg) NORETURN; static void syntax(const char *op, const char *msg) { if (op && *op) { bb_error_msg("%s: %s", op, msg); } else { bb_error_msg("%s: %s"+4, msg); } longjmp(leaving, 2); } /* atoi with error detection */ //XXX: FIXME: duplicate of existing libbb function? static number_t getn(const char *s) { char *p; #if ENABLE_FEATURE_TEST_64 long long r; #else long r; #endif errno = 0; #if ENABLE_FEATURE_TEST_64 r = strtoll(s, &p, 10); #else r = strtol(s, &p, 10); #endif if (errno != 0) syntax(s, "out of range"); if (p == s || *(skip_whitespace(p)) != '\0') syntax(s, "bad number"); return r; } /* UNUSED static int newerf(const char *f1, const char *f2) { struct stat b1, b2; return (stat(f1, &b1) == 0 && stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime); } static int olderf(const char *f1, const char *f2) { struct stat b1, b2; return (stat(f1, &b1) == 0 && stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime); } static int equalf(const char *f1, const char *f2) { struct stat b1, b2; return (stat(f1, &b1) == 0 && stat(f2, &b2) == 0 && b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino); } */ static enum token check_operator(const char *s) { static const struct operator_t no_op = { .op_num = -1, .op_type = -1 }; int n; last_operator = &no_op; if (s == NULL) return EOI; n = index_in_strings(ops_texts, s); if (n < 0) return OPERAND; last_operator = &ops_table[n]; return ops_table[n].op_num; } static int binop(void) { const char *opnd1, *opnd2; const struct operator_t *op; number_t val1, val2; opnd1 = *args; check_operator(*++args); op = last_operator; opnd2 = *++args; if (opnd2 == NULL) syntax(args[-1], "argument expected"); if (is_int_op(op->op_num)) { val1 = getn(opnd1); val2 = getn(opnd2); if (op->op_num == INTEQ) return val1 == val2; if (op->op_num == INTNE) return val1 != val2; if (op->op_num == INTGE) return val1 >= val2; if (op->op_num == INTGT) return val1 > val2; if (op->op_num == INTLE) return val1 <= val2; /*if (op->op_num == INTLT)*/ return val1 < val2; } if (is_str_op(op->op_num)) { val1 = strcmp(opnd1, opnd2); if (op->op_num == STREQ) return val1 == 0; if (op->op_num == STRNE) return val1 != 0; if (op->op_num == STRLT) return val1 < 0; /*if (op->op_num == STRGT)*/ return val1 > 0; } /* We are sure that these three are by now the only binops we didn't check * yet, so we do not check if the class is correct: */ /* if (is_file_op(op->op_num)) */ { struct stat b1, b2; if (stat(opnd1, &b1) || stat(opnd2, &b2)) return 0; /* false, since at least one stat failed */ if (op->op_num == FILNT) return b1.st_mtime > b2.st_mtime; if (op->op_num == FILOT) return b1.st_mtime < b2.st_mtime; /*if (op->op_num == FILEQ)*/ return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino; } /*return 1; - NOTREACHED */ } static void initialize_group_array(void) { int n; /* getgroups may be expensive, try to use it only once */ ngroups = 32; do { /* FIXME: ash tries so hard to not die on OOM, * and we spoil it with just one xrealloc here */ /* We realloc, because test_main can be entered repeatedly by shell. * Testcase (ash): 'while true; do test -x some_file; done' * and watch top. (some_file must have owner != you) */ n = ngroups; group_array = xrealloc(group_array, n * sizeof(gid_t)); ngroups = getgroups(n, group_array); } while (ngroups > n); } /* Return non-zero if GID is one that we have in our groups list. */ //XXX: FIXME: duplicate of existing libbb function? // see toplevel TODO file: // possible code duplication ingroup() and is_a_group_member() static int is_a_group_member(gid_t gid) { int i; /* Short-circuit if possible, maybe saving a call to getgroups(). */ if (gid == getgid() || gid == getegid()) return 1; if (ngroups == 0) initialize_group_array(); /* Search through the list looking for GID. */ for (i = 0; i < ngroups; i++) if (gid == group_array[i]) return 1; return 0; } /* Do the same thing access(2) does, but use the effective uid and gid, and don't make the mistake of telling root that any file is executable. */ static int test_eaccess(char *path, int mode) { struct stat st; unsigned int euid = geteuid(); if (stat(path, &st) < 0) return -1; if (euid == 0) { /* Root can read or write any file. */ if (mode != X_OK) return 0; /* Root can execute any file that has any one of the execute * bits set. */ if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) return 0; } if (st.st_uid == euid) /* owner */ mode <<= 6; else if (is_a_group_member(st.st_gid)) mode <<= 3; if (st.st_mode & mode) return 0; return -1; } static int filstat(char *nm, enum token mode) { struct stat s; unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */ if (mode == FILSYM) { #ifdef S_IFLNK if (lstat(nm, &s) == 0) { i = S_IFLNK; goto filetype; } #endif return 0; } if (stat(nm, &s) != 0) return 0; if (mode == FILEXIST) return 1; if (is_file_access(mode)) { if (mode == FILRD) i = R_OK; if (mode == FILWR) i = W_OK; if (mode == FILEX) i = X_OK; return test_eaccess(nm, i) == 0; } if (is_file_type(mode)) { if (mode == FILREG) i = S_IFREG; if (mode == FILDIR) i = S_IFDIR; if (mode == FILCDEV) i = S_IFCHR; if (mode == FILBDEV) i = S_IFBLK; if (mode == FILFIFO) { #ifdef S_IFIFO i = S_IFIFO; #else return 0; #endif } if (mode == FILSOCK) { #ifdef S_IFSOCK i = S_IFSOCK; #else return 0; #endif } filetype: return ((s.st_mode & S_IFMT) == i); } if (is_file_bit(mode)) { if (mode == FILSUID) i = S_ISUID; if (mode == FILSGID) i = S_ISGID; if (mode == FILSTCK) i = S_ISVTX; return ((s.st_mode & i) != 0); } if (mode == FILGZ) return s.st_size > 0L; if (mode == FILUID) return s.st_uid == geteuid(); if (mode == FILGID) return s.st_gid == getegid(); return 1; /* NOTREACHED */ } static number_t nexpr(enum token n) { number_t res; nest_msg(">nexpr(%s)\n", TOKSTR[n]); if (n == UNOT) { n = check_operator(*++args); if (n == EOI) { /* special case: [ ! ], [ a -a ! ] are valid */ /* IOW, "! ARG" may miss ARG */ args--; unnest_msg("aexpr(%s)\n", TOKSTR[n]); res = nexpr(n); dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]); if (check_operator(*++args) == BAND) { dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]); res = aexpr(check_operator(*++args)) && res; unnest_msg("oexpr(%s)\n", TOKSTR[n]); res = aexpr(n); dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]); if (check_operator(*++args) == BOR) { dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]); res = oexpr(check_operator(*++args)) || res; unnest_msg("primary(%s)\n", TOKSTR[n]); if (n == EOI) { syntax(NULL, "argument expected"); } if (n == LPAREN) { res = oexpr(check_operator(*++args)); if (check_operator(*++args) != RPAREN) syntax(NULL, "closing paren expected"); unnest_msg("op_type == BINOP) unnest_msg_and_return(binop(), "op_type == UNOP) { /* unary expression */ if (args[1] == NULL) // syntax(args0_op->op_text, "argument expected"); goto check_emptiness; args++; if (n == STREZ) unnest_msg_and_return(args[0][0] == '\0', "op_type == BINOP) { /* args[2] is known to be NULL, isn't it bound to fail? */ unnest_msg_and_return(binop(), "op_type == BINOP) { /* "test [!] arg1 arg2" */ args = argv; res = (binop() == 0); goto ret; } } /* Some complex expression. Undo '!' removal */ if (negate) { negate = 0; //argc++; argv--; } #endif args = argv; res = !oexpr(check_operator(*args)); if (*args != NULL && *++args != NULL) { /* Examples: * test 3 -lt 5 6 * test -t 1 2 */ bb_error_msg("%s: unknown operand", *args); res = 2; } ret: DEINIT_S(); // return negate ? !res : res; return res; } busybox-1.22.1/coreutils/true.c0000644000000000000000000000144512263563520015132 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini true implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/true.html */ //usage:#define true_trivial_usage //usage: "" //usage:#define true_full_usage "\n\n" //usage: "Return an exit code of TRUE (0)" //usage: //usage:#define true_example_usage //usage: "$ true\n" //usage: "$ echo $?\n" //usage: "0\n" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ int true_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int true_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { return EXIT_SUCCESS; } busybox-1.22.1/coreutils/mknod.c0000644000000000000000000000373712263563520015271 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * mknod implementation for busybox * * Copyright (C) 2003 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ //usage:#define mknod_trivial_usage //usage: "[-m MODE] " IF_SELINUX("[-Z] ") "NAME TYPE MAJOR MINOR" //usage:#define mknod_full_usage "\n\n" //usage: "Create a special file (block, character, or pipe)\n" //usage: "\n -m MODE Creation mode (default a=rw)" //usage: IF_SELINUX( //usage: "\n -Z Set security context" //usage: ) //usage: "\nTYPE:" //usage: "\n b Block device" //usage: "\n c or u Character device" //usage: "\n p Named pipe (MAJOR and MINOR are ignored)" //usage: //usage:#define mknod_example_usage //usage: "$ mknod /dev/fd0 b 2 0\n" //usage: "$ mknod -m 644 /tmp/pipe p\n" #include // For makedev #include "libbb.h" #include "libcoreutils/coreutils.h" /* This is a NOEXEC applet. Be very careful! */ static const char modes_chars[] ALIGN1 = { 'p', 'c', 'u', 'b', 0, 1, 1, 2 }; static const mode_t modes_cubp[] = { S_IFIFO, S_IFCHR, S_IFBLK }; int mknod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mknod_main(int argc, char **argv) { mode_t mode; dev_t dev; const char *name; mode = getopt_mk_fifo_nod(argv); argv += optind; argc -= optind; if (argc >= 2) { name = strchr(modes_chars, argv[1][0]); if (name != NULL) { mode |= modes_cubp[(int)(name[4])]; dev = 0; if (*name != 'p') { argc -= 2; if (argc == 2) { /* Autodetect what the system supports; these macros should * optimize out to two constants. */ dev = makedev(xatoul_range(argv[2], 0, major(UINT_MAX)), xatoul_range(argv[3], 0, minor(UINT_MAX))); } } if (argc == 2) { name = *argv; if (mknod(name, mode, dev) == 0) { return EXIT_SUCCESS; } bb_simple_perror_msg_and_die(name); } } } bb_show_usage(); } busybox-1.22.1/coreutils/od_bloaty.c0000644000000000000000000011021612263563520016124 0ustar rootroot/* od -- dump files in octal and other formats Copyright (C) 92, 1995-2004 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Written by Jim Meyering. */ /* Busyboxed by Denys Vlasenko, based on od.c from coreutils-5.2.1 */ /* #include "libbb.h" - done in od.c */ #define assert(a) ((void)0) //usage:#if ENABLE_DESKTOP //usage:#define od_trivial_usage //usage: "[-abcdfhilovxs] [-t TYPE] [-A RADIX] [-N SIZE] [-j SKIP] [-S MINSTR] [-w WIDTH] [FILE]..." // We don't support: // ... [FILE] [[+]OFFSET[.][b]] // Support is buggy for: // od --traditional [OPTION]... [FILE] [[+]OFFSET[.][b] [+][LABEL][.][b]] //usage:#define od_full_usage "\n\n" //usage: "Print FILEs (or stdin) unambiguously, as octal bytes by default" //usage:#endif enum { OPT_A = 1 << 0, OPT_N = 1 << 1, OPT_a = 1 << 2, OPT_b = 1 << 3, OPT_c = 1 << 4, OPT_d = 1 << 5, OPT_f = 1 << 6, OPT_h = 1 << 7, OPT_i = 1 << 8, OPT_j = 1 << 9, OPT_l = 1 << 10, OPT_o = 1 << 11, OPT_t = 1 << 12, /* When zero and two or more consecutive blocks are equal, format only the first block and output an asterisk alone on the following line to indicate that identical blocks have been elided: */ OPT_v = 1 << 13, OPT_x = 1 << 14, OPT_s = 1 << 15, OPT_S = 1 << 16, OPT_w = 1 << 17, OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS, }; #define OD_GETOPT32() getopt32(argv, \ "A:N:abcdfhij:lot:vxsS:w::", \ /* -w with optional param */ \ /* -S was -s and also had optional parameter */ \ /* but in coreutils 6.3 it was renamed and now has */ \ /* _mandatory_ parameter */ \ &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block) /* Check for 0x7f is a coreutils 6.3 addition */ #define ISPRINT(c) (((c) >= ' ') && (c) < 0x7f) typedef long double longdouble_t; typedef unsigned long long ulonglong_t; typedef long long llong; #if ENABLE_LFS # define xstrtooff_sfx xstrtoull_sfx #else # define xstrtooff_sfx xstrtoul_sfx #endif /* The default number of input bytes per output line. */ #define DEFAULT_BYTES_PER_BLOCK 16 /* The number of decimal digits of precision in a float. */ #ifndef FLT_DIG # define FLT_DIG 7 #endif /* The number of decimal digits of precision in a double. */ #ifndef DBL_DIG # define DBL_DIG 15 #endif /* The number of decimal digits of precision in a long double. */ #ifndef LDBL_DIG # define LDBL_DIG DBL_DIG #endif enum size_spec { NO_SIZE, CHAR, SHORT, INT, LONG, LONG_LONG, FLOAT_SINGLE, FLOAT_DOUBLE, FLOAT_LONG_DOUBLE, N_SIZE_SPECS }; enum output_format { SIGNED_DECIMAL, UNSIGNED_DECIMAL, OCTAL, HEXADECIMAL, FLOATING_POINT, NAMED_CHARACTER, CHARACTER }; /* Each output format specification (from '-t spec' or from old-style options) is represented by one of these structures. */ struct tspec { enum output_format fmt; enum size_spec size; void (*print_function) (size_t, const char *, const char *); char *fmt_string; int hexl_mode_trailer; int field_width; }; /* Convert the number of 8-bit bytes of a binary representation to the number of characters (digits + sign if the type is signed) required to represent the same quantity in the specified base/type. For example, a 32-bit (4-byte) quantity may require a field width as wide as the following for these types: 11 unsigned octal 11 signed decimal 10 unsigned decimal 8 unsigned hexadecimal */ static const uint8_t bytes_to_oct_digits[] ALIGN1 = {0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43}; static const uint8_t bytes_to_signed_dec_digits[] ALIGN1 = {1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40}; static const uint8_t bytes_to_unsigned_dec_digits[] ALIGN1 = {0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39}; static const uint8_t bytes_to_hex_digits[] ALIGN1 = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32}; /* Convert enum size_spec to the size of the named type. */ static const signed char width_bytes[] ALIGN1 = { -1, sizeof(char), sizeof(short), sizeof(int), sizeof(long), sizeof(ulonglong_t), sizeof(float), sizeof(double), sizeof(longdouble_t) }; /* Ensure that for each member of 'enum size_spec' there is an initializer in the width_bytes array. */ struct ERR_width_bytes_has_bad_size { char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1]; }; static smallint exit_code; static unsigned string_min; /* An array of specs describing how to format each input block. */ static size_t n_specs; static struct tspec *spec; /* Function that accepts an address and an optional following char, and prints the address and char to stdout. */ static void (*format_address)(off_t, char); /* The difference between the old-style pseudo starting address and the number of bytes to skip. */ #if ENABLE_LONG_OPTS static off_t pseudo_offset; #else enum { pseudo_offset = 0 }; #endif /* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all input is formatted. */ /* The number of input bytes formatted per output line. It must be a multiple of the least common multiple of the sizes associated with the specified output types. It should be as large as possible, but no larger than 16 -- unless specified with the -w option. */ static unsigned bytes_per_block = 32; /* have to use unsigned, not size_t */ /* A NULL-terminated list of the file-arguments from the command line. */ static const char *const *file_list; /* The input stream associated with the current file. */ static FILE *in_stream; #define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t) static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 = { [sizeof(char)] = CHAR, #if USHRT_MAX != UCHAR_MAX [sizeof(short)] = SHORT, #endif #if UINT_MAX != USHRT_MAX [sizeof(int)] = INT, #endif #if ULONG_MAX != UINT_MAX [sizeof(long)] = LONG, #endif #if ULLONG_MAX != ULONG_MAX [sizeof(ulonglong_t)] = LONG_LONG, #endif }; #define MAX_FP_TYPE_SIZE sizeof(longdouble_t) static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = { /* gcc seems to allow repeated indexes. Last one wins */ [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE, [sizeof(double)] = FLOAT_DOUBLE, [sizeof(float)] = FLOAT_SINGLE }; static unsigned gcd(unsigned u, unsigned v) { unsigned t; while (v != 0) { t = u % v; u = v; v = t; } return u; } /* Compute the least common multiple of U and V. */ static unsigned lcm(unsigned u, unsigned v) { unsigned t = gcd(u, v); if (t == 0) return 0; return u * v / t; } static void print_s_char(size_t n_bytes, const char *block, const char *fmt_string) { while (n_bytes--) { int tmp = *(signed char *) block; printf(fmt_string, tmp); block += sizeof(unsigned char); } } static void print_char(size_t n_bytes, const char *block, const char *fmt_string) { while (n_bytes--) { unsigned tmp = *(unsigned char *) block; printf(fmt_string, tmp); block += sizeof(unsigned char); } } static void print_s_short(size_t n_bytes, const char *block, const char *fmt_string) { n_bytes /= sizeof(signed short); while (n_bytes--) { int tmp = *(signed short *) block; printf(fmt_string, tmp); block += sizeof(unsigned short); } } static void print_short(size_t n_bytes, const char *block, const char *fmt_string) { n_bytes /= sizeof(unsigned short); while (n_bytes--) { unsigned tmp = *(unsigned short *) block; printf(fmt_string, tmp); block += sizeof(unsigned short); } } static void print_int(size_t n_bytes, const char *block, const char *fmt_string) { n_bytes /= sizeof(unsigned); while (n_bytes--) { unsigned tmp = *(unsigned *) block; printf(fmt_string, tmp); block += sizeof(unsigned); } } #if UINT_MAX == ULONG_MAX # define print_long print_int #else static void print_long(size_t n_bytes, const char *block, const char *fmt_string) { n_bytes /= sizeof(unsigned long); while (n_bytes--) { unsigned long tmp = *(unsigned long *) block; printf(fmt_string, tmp); block += sizeof(unsigned long); } } #endif #if ULONG_MAX == ULLONG_MAX # define print_long_long print_long #else static void print_long_long(size_t n_bytes, const char *block, const char *fmt_string) { n_bytes /= sizeof(ulonglong_t); while (n_bytes--) { ulonglong_t tmp = *(ulonglong_t *) block; printf(fmt_string, tmp); block += sizeof(ulonglong_t); } } #endif static void print_float(size_t n_bytes, const char *block, const char *fmt_string) { n_bytes /= sizeof(float); while (n_bytes--) { float tmp = *(float *) block; printf(fmt_string, tmp); block += sizeof(float); } } static void print_double(size_t n_bytes, const char *block, const char *fmt_string) { n_bytes /= sizeof(double); while (n_bytes--) { double tmp = *(double *) block; printf(fmt_string, tmp); block += sizeof(double); } } static void print_long_double(size_t n_bytes, const char *block, const char *fmt_string) { n_bytes /= sizeof(longdouble_t); while (n_bytes--) { longdouble_t tmp = *(longdouble_t *) block; printf(fmt_string, tmp); block += sizeof(longdouble_t); } } /* print_[named]_ascii are optimized for speed. * Remember, someday you may want to pump gigabytes through this thing. * Saving a dozen of .text bytes here is counter-productive */ static void print_named_ascii(size_t n_bytes, const char *block, const char *unused_fmt_string UNUSED_PARAM) { /* Names for some non-printing characters. */ static const char charname[33][3] ALIGN1 = { "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", " bs", " ht", " nl", " vt", " ff", " cr", " so", " si", "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", "can", " em", "sub", "esc", " fs", " gs", " rs", " us", " sp" }; // buf[N] pos: 01234 56789 char buf[12] = " x\0 0xx\0"; // actually " x\0 xxx\0", but want to share string with print_ascii. // [12] because we take three 32bit stack slots anyway, and // gcc is too dumb to initialize with constant stores, // it copies initializer from rodata. Oh well. while (n_bytes--) { unsigned masked_c = *(unsigned char *) block++; masked_c &= 0x7f; if (masked_c == 0x7f) { fputs(" del", stdout); continue; } if (masked_c > ' ') { buf[3] = masked_c; fputs(buf, stdout); continue; } /* Why? Because printf(" %3.3s") is much slower... */ buf[6] = charname[masked_c][0]; buf[7] = charname[masked_c][1]; buf[8] = charname[masked_c][2]; fputs(buf+5, stdout); } } static void print_ascii(size_t n_bytes, const char *block, const char *unused_fmt_string UNUSED_PARAM) { // buf[N] pos: 01234 56789 char buf[12] = " x\0 0xx\0"; while (n_bytes--) { const char *s; unsigned c = *(unsigned char *) block++; if (ISPRINT(c)) { buf[3] = c; fputs(buf, stdout); continue; } switch (c) { case '\0': s = " \\0"; break; case '\007': s = " \\a"; break; case '\b': s = " \\b"; break; case '\f': s = " \\f"; break; case '\n': s = " \\n"; break; case '\r': s = " \\r"; break; case '\t': s = " \\t"; break; case '\v': s = " \\v"; break; case '\x7f': s = " 177"; break; default: /* c is never larger than 040 */ buf[7] = (c >> 3) + '0'; buf[8] = (c & 7) + '0'; s = buf + 5; } fputs(s, stdout); } } /* Given a list of one or more input filenames FILE_LIST, set the global file pointer IN_STREAM and the global string INPUT_FILENAME to the first one that can be successfully opened. Modify FILE_LIST to reference the next filename in the list. A file name of "-" is interpreted as standard input. If any file open fails, give an error message and return nonzero. */ static void open_next_file(void) { while (1) { if (!*file_list) return; in_stream = fopen_or_warn_stdin(*file_list++); if (in_stream) { break; } exit_code = 1; } if ((option_mask32 & (OPT_N|OPT_S)) == OPT_N) setbuf(in_stream, NULL); } /* Test whether there have been errors on in_stream, and close it if it is not standard input. Return nonzero if there has been an error on in_stream or stdout; return zero otherwise. This function will report more than one error only if both a read and a write error have occurred. IN_ERRNO, if nonzero, is the error number corresponding to the most recent action for IN_STREAM. */ static void check_and_close(void) { if (in_stream) { if (ferror(in_stream)) { bb_error_msg("%s: read error", (in_stream == stdin) ? bb_msg_standard_input : file_list[-1] ); exit_code = 1; } fclose_if_not_stdin(in_stream); in_stream = NULL; } if (ferror(stdout)) { bb_error_msg_and_die(bb_msg_write_error); } } /* If S points to a single valid modern od format string, put a description of that format in *TSPEC, return pointer to character following the just-decoded format. For example, if S were "d4afL", we will return a rtp to "afL" and *TSPEC would be { fmt = SIGNED_DECIMAL; size = INT or LONG; (whichever integral_type_size[4] resolves to) print_function = print_int; (assuming size == INT) fmt_string = "%011d%c"; } S_ORIG is solely for reporting errors. It should be the full format string argument. */ static NOINLINE const char * decode_one_format(const char *s_orig, const char *s, struct tspec *tspec) { enum size_spec size_spec; unsigned size; enum output_format fmt; const char *p; char *end; char *fmt_string = NULL; void (*print_function) (size_t, const char *, const char *); unsigned c; unsigned field_width = 0; int pos; switch (*s) { case 'd': case 'o': case 'u': case 'x': { static const char CSIL[] ALIGN1 = "CSIL"; c = *s++; p = strchr(CSIL, *s); /* if *s == NUL, p != NULL! Testcase: "od -tx" */ if (!p || *p == '\0') { size = sizeof(int); if (isdigit(s[0])) { size = bb_strtou(s, &end, 0); if (errno == ERANGE || MAX_INTEGRAL_TYPE_SIZE < size || integral_type_size[size] == NO_SIZE ) { bb_error_msg_and_die("invalid type string '%s'; " "%u-byte %s type is not supported", s_orig, size, "integral"); } s = end; } } else { static const uint8_t CSIL_sizeof[4] = { sizeof(char), sizeof(short), sizeof(int), sizeof(long), }; size = CSIL_sizeof[p - CSIL]; s++; /* skip C/S/I/L */ } #define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \ ((Spec) == LONG_LONG ? (Max_format) \ : ((Spec) == LONG ? (Long_format) : (Min_format))) #define FMT_BYTES_ALLOCATED 9 size_spec = integral_type_size[size]; { static const char doux[] ALIGN1 = "doux"; static const char doux_fmt_letter[][4] = { "lld", "llo", "llu", "llx" }; static const enum output_format doux_fmt[] = { SIGNED_DECIMAL, OCTAL, UNSIGNED_DECIMAL, HEXADECIMAL, }; static const uint8_t *const doux_bytes_to_XXX[] = { bytes_to_signed_dec_digits, bytes_to_oct_digits, bytes_to_unsigned_dec_digits, bytes_to_hex_digits, }; static const char doux_fmtstring[][sizeof(" %%0%u%s")] = { " %%%u%s", " %%0%u%s", " %%%u%s", " %%0%u%s", }; pos = strchr(doux, c) - doux; fmt = doux_fmt[pos]; field_width = doux_bytes_to_XXX[pos][size]; p = doux_fmt_letter[pos] + 2; if (size_spec == LONG) p--; if (size_spec == LONG_LONG) p -= 2; fmt_string = xasprintf(doux_fmtstring[pos], field_width, p); } switch (size_spec) { case CHAR: print_function = (fmt == SIGNED_DECIMAL ? print_s_char : print_char); break; case SHORT: print_function = (fmt == SIGNED_DECIMAL ? print_s_short : print_short); break; case INT: print_function = print_int; break; case LONG: print_function = print_long; break; default: /* case LONG_LONG: */ print_function = print_long_long; break; } break; } case 'f': { static const char FDL[] ALIGN1 = "FDL"; fmt = FLOATING_POINT; ++s; p = strchr(FDL, *s); if (!p) { size = sizeof(double); if (isdigit(s[0])) { size = bb_strtou(s, &end, 0); if (errno == ERANGE || size > MAX_FP_TYPE_SIZE || fp_type_size[size] == NO_SIZE ) { bb_error_msg_and_die("invalid type string '%s'; " "%u-byte %s type is not supported", s_orig, size, "floating point"); } s = end; } } else { static const uint8_t FDL_sizeof[] = { sizeof(float), sizeof(double), sizeof(longdouble_t), }; size = FDL_sizeof[p - FDL]; } size_spec = fp_type_size[size]; switch (size_spec) { case FLOAT_SINGLE: print_function = print_float; field_width = FLT_DIG + 8; /* Don't use %#e; not all systems support it. */ fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG); break; case FLOAT_DOUBLE: print_function = print_double; field_width = DBL_DIG + 8; fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG); break; default: /* case FLOAT_LONG_DOUBLE: */ print_function = print_long_double; field_width = LDBL_DIG + 8; fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG); break; } break; } case 'a': ++s; fmt = NAMED_CHARACTER; size_spec = CHAR; print_function = print_named_ascii; field_width = 3; break; case 'c': ++s; fmt = CHARACTER; size_spec = CHAR; print_function = print_ascii; field_width = 3; break; default: bb_error_msg_and_die("invalid character '%c' " "in type string '%s'", *s, s_orig); } tspec->size = size_spec; tspec->fmt = fmt; tspec->print_function = print_function; tspec->fmt_string = fmt_string; tspec->field_width = field_width; tspec->hexl_mode_trailer = (*s == 'z'); if (tspec->hexl_mode_trailer) s++; return s; } /* Decode the modern od format string S. Append the decoded representation to the global array SPEC, reallocating SPEC if necessary. */ static void decode_format_string(const char *s) { const char *s_orig = s; while (*s != '\0') { struct tspec tspec; const char *next; next = decode_one_format(s_orig, s, &tspec); assert(s != next); s = next; spec = xrealloc_vector(spec, 4, n_specs); memcpy(&spec[n_specs], &tspec, sizeof(spec[0])); n_specs++; } } /* Given a list of one or more input filenames FILE_LIST, set the global file pointer IN_STREAM to position N_SKIP in the concatenation of those files. If any file operation fails or if there are fewer than N_SKIP bytes in the combined input, give an error message and return nonzero. When possible, use seek rather than read operations to advance IN_STREAM. */ static void skip(off_t n_skip) { if (n_skip == 0) return; while (in_stream) { /* !EOF */ struct stat file_stats; /* First try seeking. For large offsets, this extra work is worthwhile. If the offset is below some threshold it may be more efficient to move the pointer by reading. There are two issues when trying to seek: - the file must be seekable. - before seeking to the specified position, make sure that the new position is in the current file. Try to do that by getting file's size using fstat. But that will work only for regular files. */ /* The st_size field is valid only for regular files (and for symbolic links, which cannot occur here). If the number of bytes left to skip is at least as large as the size of the current file, we can decrement n_skip and go on to the next file. */ if (fstat(fileno(in_stream), &file_stats) == 0 && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0 ) { if (file_stats.st_size < n_skip) { n_skip -= file_stats.st_size; /* take "check & close / open_next" route */ } else { if (fseeko(in_stream, n_skip, SEEK_CUR) != 0) exit_code = 1; return; } } else { /* If it's not a regular file with positive size, position the file pointer by reading. */ char buf[1024]; size_t n_bytes_to_read = 1024; size_t n_bytes_read; while (n_skip > 0) { if (n_skip < n_bytes_to_read) n_bytes_to_read = n_skip; n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream); n_skip -= n_bytes_read; if (n_bytes_read != n_bytes_to_read) break; /* EOF on this file or error */ } } if (n_skip == 0) return; check_and_close(); open_next_file(); } if (n_skip) bb_error_msg_and_die("can't skip past end of combined input"); } typedef void FN_format_address(off_t address, char c); static void format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM) { } static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc"; /* Corresponds to 'x' above */ #define address_base_char address_fmt[sizeof(address_fmt)-3] /* Corresponds to 'n' above */ #define address_pad_len_char address_fmt[2] static void format_address_std(off_t address, char c) { /* Corresponds to 'c' */ address_fmt[sizeof(address_fmt)-2] = c; printf(address_fmt, address); } #if ENABLE_LONG_OPTS /* only used with --traditional */ static void format_address_paren(off_t address, char c) { putchar('('); format_address_std(address, ')'); if (c) putchar(c); } static void format_address_label(off_t address, char c) { format_address_std(address, ' '); format_address_paren(address + pseudo_offset, c); } #endif static void dump_hexl_mode_trailer(size_t n_bytes, const char *block) { fputs(" >", stdout); while (n_bytes--) { unsigned c = *(unsigned char *) block++; c = (ISPRINT(c) ? c : '.'); putchar(c); } putchar('<'); } /* Write N_BYTES bytes from CURR_BLOCK to standard output once for each of the N_SPEC format specs. CURRENT_OFFSET is the byte address of CURR_BLOCK in the concatenation of input files, and it is printed (optionally) only before the output line associated with the first format spec. When duplicate blocks are being abbreviated, the output for a sequence of identical input blocks is the output for the first block followed by an asterisk alone on a line. It is valid to compare the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK. That condition may be false only for the last input block -- and then only when it has not been padded to length BYTES_PER_BLOCK. */ static void write_block(off_t current_offset, size_t n_bytes, const char *prev_block, const char *curr_block) { static char first = 1; static char prev_pair_equal = 0; size_t i; if (!(option_mask32 & OPT_v) && !first && n_bytes == bytes_per_block && memcmp(prev_block, curr_block, bytes_per_block) == 0 ) { if (prev_pair_equal) { /* The two preceding blocks were equal, and the current block is the same as the last one, so print nothing. */ } else { puts("*"); prev_pair_equal = 1; } } else { first = 0; prev_pair_equal = 0; for (i = 0; i < n_specs; i++) { if (i == 0) format_address(current_offset, '\0'); else printf("%*s", address_pad_len_char - '0', ""); (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string); if (spec[i].hexl_mode_trailer) { /* space-pad out to full line width, then dump the trailer */ unsigned datum_width = width_bytes[spec[i].size]; unsigned blank_fields = (bytes_per_block - n_bytes) / datum_width; unsigned field_width = spec[i].field_width + 1; printf("%*s", blank_fields * field_width, ""); dump_hexl_mode_trailer(n_bytes, curr_block); } putchar('\n'); } } } static void read_block(size_t n, char *block, size_t *n_bytes_in_buffer) { assert(0 < n && n <= bytes_per_block); *n_bytes_in_buffer = 0; if (n == 0) return; while (in_stream != NULL) { /* EOF. */ size_t n_needed; size_t n_read; n_needed = n - *n_bytes_in_buffer; n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream); *n_bytes_in_buffer += n_read; if (n_read == n_needed) break; /* error check is done in check_and_close */ check_and_close(); open_next_file(); } } /* Return the least common multiple of the sizes associated with the format specs. */ static int get_lcm(void) { size_t i; int l_c_m = 1; for (i = 0; i < n_specs; i++) l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]); return l_c_m; } /* Read a chunk of size BYTES_PER_BLOCK from the input files, write the formatted block to standard output, and repeat until the specified maximum number of bytes has been read or until all input has been processed. If the last block read is smaller than BYTES_PER_BLOCK and its size is not a multiple of the size associated with a format spec, extend the input block with zero bytes until its length is a multiple of all format spec sizes. Write the final block. Finally, write on a line by itself the offset of the byte after the last byte read. */ static void dump(off_t current_offset, off_t end_offset) { char *block[2]; int idx; size_t n_bytes_read; block[0] = xmalloc(2 * bytes_per_block); block[1] = block[0] + bytes_per_block; idx = 0; if (option_mask32 & OPT_N) { while (1) { size_t n_needed; if (current_offset >= end_offset) { n_bytes_read = 0; break; } n_needed = MIN(end_offset - current_offset, (off_t) bytes_per_block); read_block(n_needed, block[idx], &n_bytes_read); if (n_bytes_read < bytes_per_block) break; assert(n_bytes_read == bytes_per_block); write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]); current_offset += n_bytes_read; idx ^= 1; } } else { while (1) { read_block(bytes_per_block, block[idx], &n_bytes_read); if (n_bytes_read < bytes_per_block) break; assert(n_bytes_read == bytes_per_block); write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]); current_offset += n_bytes_read; idx ^= 1; } } if (n_bytes_read > 0) { int l_c_m; size_t bytes_to_write; l_c_m = get_lcm(); /* Make bytes_to_write the smallest multiple of l_c_m that is at least as large as n_bytes_read. */ bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m); memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read); write_block(current_offset, bytes_to_write, block[idx ^ 1], block[idx]); current_offset += n_bytes_read; } format_address(current_offset, '\n'); if ((option_mask32 & OPT_N) && current_offset >= end_offset) check_and_close(); free(block[0]); } /* Read N bytes into BLOCK from the concatenation of the input files named in the global array FILE_LIST. On the first call to this function, the global variable IN_STREAM is expected to be an open stream associated with the input file INPUT_FILENAME. If all N bytes cannot be read from IN_STREAM, close IN_STREAM and update the global variables IN_STREAM and INPUT_FILENAME. Then try to read the remaining bytes from the newly opened file. Repeat if necessary until EOF is reached for the last file in FILE_LIST. On subsequent calls, don't modify BLOCK and return zero. Set *N_BYTES_IN_BUFFER to the number of bytes read. If an error occurs, it will be detected through ferror when the stream is about to be closed. If there is an error, give a message but continue reading as usual and return nonzero. Otherwise return zero. */ /* STRINGS mode. Find each "string constant" in the input. A string constant is a run of at least 'string_min' ASCII graphic (or formatting) characters terminated by a null. Based on a function written by Richard Stallman for a traditional version of od. */ static void dump_strings(off_t address, off_t end_offset) { unsigned bufsize = MAX(100, string_min); unsigned char *buf = xmalloc(bufsize); while (1) { size_t i; int c; /* See if the next 'string_min' chars are all printing chars. */ tryline: if ((option_mask32 & OPT_N) && (end_offset - string_min <= address)) break; i = 0; while (!(option_mask32 & OPT_N) || address < end_offset) { if (i == bufsize) { bufsize += bufsize/8; buf = xrealloc(buf, bufsize); } while (in_stream) { /* !EOF */ c = fgetc(in_stream); if (c != EOF) goto got_char; check_and_close(); open_next_file(); } /* EOF */ goto ret; got_char: address++; if (!c) break; if (!ISPRINT(c)) goto tryline; /* It isn't; give up on this string. */ buf[i++] = c; /* String continues; store it all. */ } if (i < string_min) /* Too short! */ goto tryline; /* If we get here, the string is all printable and NUL-terminated */ buf[i] = 0; format_address(address - i - 1, ' '); for (i = 0; (c = buf[i]); i++) { switch (c) { case '\007': fputs("\\a", stdout); break; case '\b': fputs("\\b", stdout); break; case '\f': fputs("\\f", stdout); break; case '\n': fputs("\\n", stdout); break; case '\r': fputs("\\r", stdout); break; case '\t': fputs("\\t", stdout); break; case '\v': fputs("\\v", stdout); break; default: putchar(c); } } putchar('\n'); } /* We reach this point only if we search through (max_bytes_to_format - string_min) bytes before reaching EOF. */ check_and_close(); ret: free(buf); } #if ENABLE_LONG_OPTS /* If S is a valid traditional offset specification with an optional leading '+' return nonzero and set *OFFSET to the offset it denotes. */ static int parse_old_offset(const char *s, off_t *offset) { static const struct suffix_mult Bb[] = { { "B", 1024 }, { "b", 512 }, { "", 0 } }; char *p; int radix; /* Skip over any leading '+'. */ if (s[0] == '+') ++s; if (!isdigit(s[0])) return 0; /* not a number */ /* Determine the radix we'll use to interpret S. If there is a '.', * it's decimal, otherwise, if the string begins with '0X'or '0x', * it's hexadecimal, else octal. */ p = strchr(s, '.'); radix = 8; if (p) { p[0] = '\0'; /* cheating */ radix = 10; } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) radix = 16; *offset = xstrtooff_sfx(s, radix, Bb); if (p) p[0] = '.'; return (*offset >= 0); } #endif int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int od_main(int argc UNUSED_PARAM, char **argv) { #if ENABLE_LONG_OPTS static const char od_longopts[] ALIGN1 = "skip-bytes\0" Required_argument "j" "address-radix\0" Required_argument "A" "read-bytes\0" Required_argument "N" "format\0" Required_argument "t" "output-duplicates\0" No_argument "v" /* Yes, it's true: -S NUM, but --strings[=NUM]! * that is, NUM is mandatory for -S but optional for --strings! */ "strings\0" Optional_argument "S" "width\0" Optional_argument "w" "traditional\0" No_argument "\xff" ; #endif const char *str_A, *str_N, *str_j, *str_S = "3"; llist_t *lst_t = NULL; unsigned opt; int l_c_m; /* The number of input bytes to skip before formatting and writing. */ off_t n_bytes_to_skip = 0; /* The offset of the first byte after the last byte to be formatted. */ off_t end_offset = 0; /* The maximum number of bytes that will be formatted. */ off_t max_bytes_to_format = 0; spec = NULL; format_address = format_address_std; address_base_char = 'o'; address_pad_len_char = '7'; /* Parse command line */ opt_complementary = "w+:t::"; /* -w N, -t is a list */ #if ENABLE_LONG_OPTS applet_long_options = od_longopts; #endif opt = OD_GETOPT32(); argv += optind; if (opt & OPT_A) { static const char doxn[] ALIGN1 = "doxn"; static const char doxn_address_base_char[] ALIGN1 = { 'u', 'o', 'x', /* '?' fourth one is not important */ }; static const uint8_t doxn_address_pad_len_char[] ALIGN1 = { '7', '7', '6', /* '?' */ }; char *p; int pos; p = strchr(doxn, str_A[0]); if (!p) bb_error_msg_and_die("bad output address radix " "'%c' (must be [doxn])", str_A[0]); pos = p - doxn; if (pos == 3) format_address = format_address_none; address_base_char = doxn_address_base_char[pos]; address_pad_len_char = doxn_address_pad_len_char[pos]; } if (opt & OPT_N) { max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm_suffixes); } if (opt & OPT_a) decode_format_string("a"); if (opt & OPT_b) decode_format_string("oC"); if (opt & OPT_c) decode_format_string("c"); if (opt & OPT_d) decode_format_string("u2"); if (opt & OPT_f) decode_format_string("fF"); if (opt & OPT_h) decode_format_string("x2"); if (opt & OPT_i) decode_format_string("d2"); if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm_suffixes); if (opt & OPT_l) decode_format_string("d4"); if (opt & OPT_o) decode_format_string("o2"); while (lst_t) { decode_format_string(llist_pop(&lst_t)); } if (opt & OPT_x) decode_format_string("x2"); if (opt & OPT_s) decode_format_string("d2"); if (opt & OPT_S) { string_min = xstrtou_sfx(str_S, 0, bkm_suffixes); } // Bloat: //if ((option_mask32 & OPT_S) && n_specs > 0) // bb_error_msg_and_die("no type may be specified when dumping strings"); /* If the --traditional option is used, there may be from * 0 to 3 remaining command line arguments; handle each case * separately. * od [FILE] [[+]OFFSET[.][b] [[+]LABEL[.][b]]] * The offset and pseudo_start have the same syntax. * * FIXME: POSIX 1003.1-2001 with XSI requires support for the * traditional syntax even if --traditional is not given. */ #if ENABLE_LONG_OPTS if (opt & OPT_traditional) { if (argv[0]) { off_t pseudo_start = -1; off_t o1, o2; if (!argv[1]) { /* one arg */ if (parse_old_offset(argv[0], &o1)) { /* od --traditional OFFSET */ n_bytes_to_skip = o1; argv++; } /* od --traditional FILE */ } else if (!argv[2]) { /* two args */ if (parse_old_offset(argv[0], &o1) && parse_old_offset(argv[1], &o2) ) { /* od --traditional OFFSET LABEL */ n_bytes_to_skip = o1; pseudo_start = o2; argv += 2; } else if (parse_old_offset(argv[1], &o2)) { /* od --traditional FILE OFFSET */ n_bytes_to_skip = o2; argv[1] = NULL; } else { bb_error_msg_and_die("invalid second argument '%s'", argv[1]); } } else if (!argv[3]) { /* three args */ if (parse_old_offset(argv[1], &o1) && parse_old_offset(argv[2], &o2) ) { /* od --traditional FILE OFFSET LABEL */ n_bytes_to_skip = o1; pseudo_start = o2; argv[1] = NULL; } else { bb_error_msg_and_die("the last two arguments must be offsets"); } } else { /* >3 args */ bb_error_msg_and_die("too many arguments"); } if (pseudo_start >= 0) { if (format_address == format_address_none) { address_base_char = 'o'; address_pad_len_char = '7'; format_address = format_address_paren; } else { format_address = format_address_label; } pseudo_offset = pseudo_start - n_bytes_to_skip; } } /* else: od --traditional (without args) */ } #endif if (option_mask32 & OPT_N) { end_offset = n_bytes_to_skip + max_bytes_to_format; if (end_offset < n_bytes_to_skip) bb_error_msg_and_die("SKIP + SIZE is too large"); } if (n_specs == 0) { decode_format_string("o2"); /*n_specs = 1; - done by decode_format_string */ } /* If no files were listed on the command line, set the global pointer FILE_LIST so that it references the null-terminated list of one name: "-". */ file_list = bb_argv_dash; if (argv[0]) { /* Set the global pointer FILE_LIST so that it references the first file-argument on the command-line. */ file_list = (char const *const *) argv; } /* Open the first input file */ open_next_file(); /* Skip over any unwanted header bytes */ skip(n_bytes_to_skip); if (!in_stream) return EXIT_FAILURE; /* Compute output block length */ l_c_m = get_lcm(); if (opt & OPT_w) { /* -w: width */ if (!bytes_per_block || bytes_per_block % l_c_m != 0) { bb_error_msg("warning: invalid width %u; using %d instead", (unsigned)bytes_per_block, l_c_m); bytes_per_block = l_c_m; } } else { bytes_per_block = l_c_m; if (l_c_m < DEFAULT_BYTES_PER_BLOCK) bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m; } #ifdef DEBUG for (i = 0; i < n_specs; i++) { printf("%d: fmt=\"%s\" width=%d\n", i, spec[i].fmt_string, width_bytes[spec[i].size]); } #endif if (option_mask32 & OPT_S) dump_strings(n_bytes_to_skip, end_offset); else dump(n_bytes_to_skip, end_offset); if (fclose(stdin)) bb_perror_msg_and_die(bb_msg_standard_input); return exit_code; } busybox-1.22.1/coreutils/catv.c0000644000000000000000000000356312263563520015113 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * cat -v implementation for busybox * * Copyright (C) 2006 Rob Landley * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* See "Cat -v considered harmful" at * http://cm.bell-labs.com/cm/cs/doc/84/kp.ps.gz */ //usage:#define catv_trivial_usage //usage: "[-etv] [FILE]..." //usage:#define catv_full_usage "\n\n" //usage: "Display nonprinting characters as ^x or M-x\n" //usage: "\n -e End each line with $" //usage: "\n -t Show tabs as ^I" //usage: "\n -v Don't use ^x or M-x escapes" #include "libbb.h" int catv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int catv_main(int argc UNUSED_PARAM, char **argv) { int retval = EXIT_SUCCESS; int fd; unsigned opts; #define CATV_OPT_e (1<<0) #define CATV_OPT_t (1<<1) #define CATV_OPT_v (1<<2) typedef char BUG_const_mismatch[ CATV_OPT_e == VISIBLE_ENDLINE && CATV_OPT_t == VISIBLE_SHOW_TABS ? 1 : -1 ]; opts = getopt32(argv, "etv"); argv += optind; #if 0 /* These consts match, we can just pass "opts" to visible() */ if (opts & CATV_OPT_e) flags |= VISIBLE_ENDLINE; if (opts & CATV_OPT_t) flags |= VISIBLE_SHOW_TABS; #endif /* Read from stdin if there's nothing else to do. */ if (!argv[0]) *--argv = (char*)"-"; do { fd = open_or_warn_stdin(*argv); if (fd < 0) { retval = EXIT_FAILURE; continue; } for (;;) { int i, res; #define read_buf bb_common_bufsiz1 res = read(fd, read_buf, COMMON_BUFSIZE); if (res < 0) retval = EXIT_FAILURE; if (res <= 0) break; for (i = 0; i < res; i++) { unsigned char c = read_buf[i]; if (opts & CATV_OPT_v) { putchar(c); } else { char buf[sizeof("M-^c")]; visible(c, buf, opts); fputs(buf, stdout); } } } if (ENABLE_FEATURE_CLEAN_UP && fd) close(fd); } while (*++argv); fflush_stdout_and_exit(retval); } busybox-1.22.1/coreutils/cat.c0000644000000000000000000000321012263563520014712 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * cat implementation for busybox * * Copyright (C) 2003 Manuel Novoa III * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/cat.html */ //kbuild:lib-$(CONFIG_CAT) += cat.o //kbuild:lib-$(CONFIG_MORE) += cat.o # more uses it if stdout isn't a tty //kbuild:lib-$(CONFIG_LESS) += cat.o # less too //kbuild:lib-$(CONFIG_CRONTAB) += cat.o # crontab -l //config:config CAT //config: bool "cat" //config: default y //config: help //config: cat is used to concatenate files and print them to the standard //config: output. Enable this option if you wish to enable the 'cat' utility. //usage:#define cat_trivial_usage //usage: "[FILE]..." //usage:#define cat_full_usage "\n\n" //usage: "Concatenate FILEs and print them to stdout" //usage: //usage:#define cat_example_usage //usage: "$ cat /proc/uptime\n" //usage: "110716.72 17.67" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ int bb_cat(char **argv) { int fd; int retval = EXIT_SUCCESS; if (!*argv) argv = (char**) &bb_argv_dash; do { fd = open_or_warn_stdin(*argv); if (fd >= 0) { /* This is not a xfunc - never exits */ off_t r = bb_copyfd_eof(fd, STDOUT_FILENO); if (fd != STDIN_FILENO) close(fd); if (r >= 0) continue; } retval = EXIT_FAILURE; } while (*++argv); return retval; } int cat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int cat_main(int argc UNUSED_PARAM, char **argv) { getopt32(argv, "u"); argv += optind; return bb_cat(argv); } busybox-1.22.1/coreutils/pwd.c0000644000000000000000000000332712263563520014746 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini pwd implementation for busybox * * Copyright (C) 1995, 1996 by Bruce Perens . * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define pwd_trivial_usage //usage: "" //usage:#define pwd_full_usage "\n\n" //usage: "Print the full filename of the current working directory" //usage: //usage:#define pwd_example_usage //usage: "$ pwd\n" //usage: "/root\n" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ static int logical_getcwd(void) { struct stat st1; struct stat st2; char *wd; char *p; wd = getenv("PWD"); if (!wd || wd[0] != '/') return 0; p = wd; while (*p) { /* doing strstr(p, "/.") by hand is smaller and faster... */ if (*p++ != '/') continue; if (*p != '.') continue; /* we found "/.", skip to next char */ p++; if (*p == '.') p++; /* we found "/.." */ if (*p == '\0' || *p == '/') return 0; /* "/./" or "/../" component: bad */ } if (stat(wd, &st1) != 0) return 0; if (stat(".", &st2) != 0) return 0; if (st1.st_ino != st2.st_ino) return 0; if (st1.st_dev != st2.st_dev) return 0; puts(wd); return 1; } int pwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int pwd_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { char *buf; if (ENABLE_DESKTOP) { /* TODO: assume -L if $POSIXLY_CORRECT? (coreutils does that) * Rationale: * POSIX requires a default of -L, but most scripts expect -P */ unsigned opt = getopt32(argv, "LP"); if ((opt & 1) && logical_getcwd()) return fflush_all(); } buf = xrealloc_getcwd_or_warn(NULL); if (buf) { puts(buf); free(buf); return fflush_all(); } return EXIT_FAILURE; } busybox-1.22.1/coreutils/date.c0000644000000000000000000002664712263563520015103 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini date implementation for busybox * * by Matthew Grant * * iso-format handling added by Robert Griebl * bugfixes and cleanup by Bernhard Reutner-Fischer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* This 'date' command supports only 2 time setting formats, all the GNU strftime stuff (its in libc, lets use it), setting time using UTC and displaying it, as well as an RFC 2822 compliant date output for shell scripting mail commands */ /* Input parsing code is always bulky - used heavy duty libc stuff as much as possible, missed out a lot of bounds checking */ //applet:IF_DATE(APPLET(date, BB_DIR_BIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_DATE) += date.o //config:config DATE //config: bool "date" //config: default y //config: help //config: date is used to set the system date or display the //config: current time in the given format. //config: //config:config FEATURE_DATE_ISOFMT //config: bool "Enable ISO date format output (-I)" //config: default y //config: depends on DATE //config: help //config: Enable option (-I) to output an ISO-8601 compliant //config: date/time string. //config: //config:# defaults to "no": stat's nanosecond field is a bit non-portable //config:config FEATURE_DATE_NANO //config: bool "Support %[num]N nanosecond format specifier" //config: default n //config: depends on DATE # syscall(__NR_clock_gettime) //config: select PLATFORM_LINUX //config: help //config: Support %[num]N format specifier. Adds ~250 bytes of code. //config: //config:config FEATURE_DATE_COMPAT //config: bool "Support weird 'date MMDDhhmm[[YY]YY][.ss]' format" //config: default y //config: depends on DATE //config: help //config: System time can be set by 'date -s DATE' and simply 'date DATE', //config: but formats of DATE string are different. 'date DATE' accepts //config: a rather weird MMDDhhmm[[YY]YY][.ss] format with completely //config: unnatural placement of year between minutes and seconds. //config: date -s (and other commands like touch -d) use more sensible //config: formats (for one, ISO format YYYY-MM-DD hh:mm:ss.ssssss). //config: //config: With this option off, 'date DATE' is 'date -s DATE' support //config: the same format. With it on, 'date DATE' additionally supports //config: MMDDhhmm[[YY]YY][.ss] format. /* GNU coreutils 6.9 man page: * date [OPTION]... [+FORMAT] * date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]] * -d, --date=STRING * display time described by STRING, not `now' * -f, --file=DATEFILE * like --date once for each line of DATEFILE * -r, --reference=FILE * display the last modification time of FILE * -R, --rfc-2822 * output date and time in RFC 2822 format. * Example: Mon, 07 Aug 2006 12:34:56 -0600 * --rfc-3339=TIMESPEC * output date and time in RFC 3339 format. * TIMESPEC='date', 'seconds', or 'ns' * Date and time components are separated by a single space: * 2006-08-07 12:34:56-06:00 * -s, --set=STRING * set time described by STRING * -u, --utc, --universal * print or set Coordinated Universal Time * * Busybox: * long options are not supported * -f is not supported * -I seems to roughly match --rfc-3339, but -I has _optional_ param * (thus "-I seconds" doesn't work, only "-Iseconds"), * and does not support -Ins * -D FMT is a bbox extension for _input_ conversion of -d DATE */ //usage:#define date_trivial_usage //usage: "[OPTIONS] [+FMT] [TIME]" //usage:#define date_full_usage "\n\n" //usage: "Display time (using +FMT), or set time\n" //usage: IF_NOT_LONG_OPTS( //usage: "\n [-s] TIME Set time to TIME" //usage: "\n -u Work in UTC (don't convert to local time)" //usage: "\n -R Output RFC-2822 compliant date string" //usage: ) IF_LONG_OPTS( //usage: "\n [-s,--set] TIME Set time to TIME" //usage: "\n -u,--utc Work in UTC (don't convert to local time)" //usage: "\n -R,--rfc-2822 Output RFC-2822 compliant date string" //usage: ) //usage: IF_FEATURE_DATE_ISOFMT( //usage: "\n -I[SPEC] Output ISO-8601 compliant date string" //usage: "\n SPEC='date' (default) for date only," //usage: "\n 'hours', 'minutes', or 'seconds' for date and" //usage: "\n time to the indicated precision" //usage: ) //usage: IF_NOT_LONG_OPTS( //usage: "\n -r FILE Display last modification time of FILE" //usage: "\n -d TIME Display TIME, not 'now'" //usage: ) IF_LONG_OPTS( //usage: "\n -r,--reference FILE Display last modification time of FILE" //usage: "\n -d,--date TIME Display TIME, not 'now'" //usage: ) //usage: IF_FEATURE_DATE_ISOFMT( //usage: "\n -D FMT Use FMT for -d TIME conversion" //usage: ) //usage: "\n" //usage: "\nRecognized TIME formats:" //usage: "\n hh:mm[:ss]" //usage: "\n [YYYY.]MM.DD-hh:mm[:ss]" //usage: "\n YYYY-MM-DD hh:mm[:ss]" //usage: "\n [[[[[YY]YY]MM]DD]hh]mm[.ss]" //usage: IF_FEATURE_DATE_COMPAT( //usage: "\n 'date TIME' form accepts MMDDhhmm[[YY]YY][.ss] instead" //usage: ) //usage: //usage:#define date_example_usage //usage: "$ date\n" //usage: "Wed Apr 12 18:52:41 MDT 2000\n" #include "libbb.h" #if ENABLE_FEATURE_DATE_NANO # include #endif enum { OPT_RFC2822 = (1 << 0), /* R */ OPT_SET = (1 << 1), /* s */ OPT_UTC = (1 << 2), /* u */ OPT_DATE = (1 << 3), /* d */ OPT_REFERENCE = (1 << 4), /* r */ OPT_TIMESPEC = (1 << 5) * ENABLE_FEATURE_DATE_ISOFMT, /* I */ OPT_HINT = (1 << 6) * ENABLE_FEATURE_DATE_ISOFMT, /* D */ }; static void maybe_set_utc(int opt) { if (opt & OPT_UTC) putenv((char*)"TZ=UTC0"); } #if ENABLE_LONG_OPTS static const char date_longopts[] ALIGN1 = "rfc-822\0" No_argument "R" "rfc-2822\0" No_argument "R" "set\0" Required_argument "s" "utc\0" No_argument "u" /* "universal\0" No_argument "u" */ "date\0" Required_argument "d" "reference\0" Required_argument "r" ; #endif int date_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int date_main(int argc UNUSED_PARAM, char **argv) { struct timespec ts; struct tm tm_time; char buf_fmt_dt2str[64]; unsigned opt; int ifmt = -1; char *date_str; char *fmt_dt2str; char *fmt_str2dt; char *filename; char *isofmt_arg = NULL; opt_complementary = "d--s:s--d" IF_FEATURE_DATE_ISOFMT(":R--I:I--R"); IF_LONG_OPTS(applet_long_options = date_longopts;) opt = getopt32(argv, "Rs:ud:r:" IF_FEATURE_DATE_ISOFMT("I::D:"), &date_str, &date_str, &filename IF_FEATURE_DATE_ISOFMT(, &isofmt_arg, &fmt_str2dt)); argv += optind; maybe_set_utc(opt); if (ENABLE_FEATURE_DATE_ISOFMT && (opt & OPT_TIMESPEC)) { ifmt = 0; /* default is date */ if (isofmt_arg) { static const char isoformats[] ALIGN1 = "date\0""hours\0""minutes\0""seconds\0"; /* ns? */ ifmt = index_in_substrings(isoformats, isofmt_arg); if (ifmt < 0) bb_show_usage(); } } fmt_dt2str = NULL; if (argv[0] && argv[0][0] == '+') { fmt_dt2str = &argv[0][1]; /* skip over the '+' */ argv++; } if (!(opt & (OPT_SET | OPT_DATE))) { opt |= OPT_SET; date_str = argv[0]; /* can be NULL */ if (date_str) { #if ENABLE_FEATURE_DATE_COMPAT int len = strspn(date_str, "0123456789"); if (date_str[len] == '\0' || (date_str[len] == '.' && isdigit(date_str[len+1]) && isdigit(date_str[len+2]) && date_str[len+3] == '\0' ) ) { /* Dreaded MMDDhhmm[[CC]YY][.ss] format! * It does not match -d or -s format. * Some users actually do use it. */ len -= 8; if (len < 0 || len > 4 || (len & 1)) bb_error_msg_and_die(bb_msg_invalid_date, date_str); if (len != 0) { /* move YY or CCYY to front */ char buf[4]; memcpy(buf, date_str + 8, len); memmove(date_str + len, date_str, 8); memcpy(date_str, buf, len); } } #endif argv++; } } if (*argv) bb_show_usage(); /* Now we have parsed all the information except the date format * which depends on whether the clock is being set or read */ if (opt & OPT_REFERENCE) { struct stat statbuf; xstat(filename, &statbuf); ts.tv_sec = statbuf.st_mtime; #if ENABLE_FEATURE_DATE_NANO ts.tv_nsec = statbuf.st_mtim.tv_nsec; /* Some toolchains use .st_mtimensec instead of st_mtim.tv_nsec. * If you need #define _SVID_SOURCE 1 to enable st_mtim.tv_nsec, * drop a mail to project mailing list please */ #endif } else { #if ENABLE_FEATURE_DATE_NANO /* libc has incredibly messy way of doing this, * typically requiring -lrt. We just skip all this mess */ syscall(__NR_clock_gettime, CLOCK_REALTIME, &ts); #else time(&ts.tv_sec); #endif } localtime_r(&ts.tv_sec, &tm_time); /* If date string is given, update tm_time, and maybe set date */ if (date_str != NULL) { /* Zero out fields - take her back to midnight! */ tm_time.tm_sec = 0; tm_time.tm_min = 0; tm_time.tm_hour = 0; /* Process any date input to UNIX time since 1 Jan 1970 */ if (ENABLE_FEATURE_DATE_ISOFMT && (opt & OPT_HINT)) { if (strptime(date_str, fmt_str2dt, &tm_time) == NULL) bb_error_msg_and_die(bb_msg_invalid_date, date_str); } else { parse_datestr(date_str, &tm_time); } /* Correct any day of week and day of year etc. fields */ /* Be sure to recheck dst (but not if date is time_t format) */ if (date_str[0] != '@') tm_time.tm_isdst = -1; ts.tv_sec = validate_tm_time(date_str, &tm_time); maybe_set_utc(opt); /* if setting time, set it */ if ((opt & OPT_SET) && stime(&ts.tv_sec) < 0) { bb_perror_msg("can't set date"); } } /* Display output */ /* Deal with format string */ if (fmt_dt2str == NULL) { int i; fmt_dt2str = buf_fmt_dt2str; if (ENABLE_FEATURE_DATE_ISOFMT && ifmt >= 0) { /* -I[SPEC]: 0:date 1:hours 2:minutes 3:seconds */ strcpy(fmt_dt2str, "%Y-%m-%dT%H:%M:%S"); i = 8 + 3 * ifmt; if (ifmt != 0) { /* TODO: if (ifmt==4) i += sprintf(&fmt_dt2str[i], ",%09u", nanoseconds); */ format_utc: fmt_dt2str[i++] = '%'; fmt_dt2str[i++] = (opt & OPT_UTC) ? 'Z' : 'z'; } fmt_dt2str[i] = '\0'; } else if (opt & OPT_RFC2822) { /* -R. undo busybox.c setlocale */ if (ENABLE_LOCALE_SUPPORT) setlocale(LC_TIME, "C"); strcpy(fmt_dt2str, "%a, %d %b %Y %H:%M:%S "); i = sizeof("%a, %d %b %Y %H:%M:%S ")-1; goto format_utc; } else { /* default case */ fmt_dt2str = (char*)"%a %b %e %H:%M:%S %Z %Y"; } } #if ENABLE_FEATURE_DATE_NANO else { /* User-specified fmt_dt2str */ /* Search for and process "%N" */ char *p = fmt_dt2str; while ((p = strchr(p, '%')) != NULL) { int n, m; unsigned pres, scale; p++; if (*p == '%') { p++; continue; } n = strspn(p, "0123456789"); if (p[n] != 'N') { p += n; continue; } /* We have "%[nnn]N" */ p[-1] = '\0'; p[n] = '\0'; scale = 1; pres = 9; if (n) { pres = xatoi_positive(p); if (pres == 0) pres = 9; m = 9 - pres; while (--m >= 0) scale *= 10; } m = p - fmt_dt2str; p += n + 1; fmt_dt2str = xasprintf("%s%0*u%s", fmt_dt2str, pres, (unsigned)ts.tv_nsec / scale, p); p = fmt_dt2str + m; } } #endif #define date_buf bb_common_bufsiz1 if (*fmt_dt2str == '\0') { /* With no format string, just print a blank line */ date_buf[0] = '\0'; } else { /* Handle special conversions */ if (strncmp(fmt_dt2str, "%f", 2) == 0) { fmt_dt2str = (char*)"%Y.%m.%d-%H:%M:%S"; } /* Generate output string */ strftime(date_buf, sizeof(date_buf), fmt_dt2str, &tm_time); } puts(date_buf); return EXIT_SUCCESS; } busybox-1.22.1/coreutils/logname.c0000644000000000000000000000267312263563520015601 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini logname implementation for busybox * * Copyright (C) 2000 Edward Betts . * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/logname.html */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * SUSv3 specifies the string used is that returned from getlogin(). * The previous implementation used getpwuid() for geteuid(), which * is _not_ the same. Erik apparently made this change almost 3 years * ago to avoid failing when no utmp was available. However, the * correct course of action wrt SUSv3 for a failing getlogin() is * a diagnostic message and an error return. */ //usage:#define logname_trivial_usage //usage: "" //usage:#define logname_full_usage "\n\n" //usage: "Print the name of the current user" //usage: //usage:#define logname_example_usage //usage: "$ logname\n" //usage: "root\n" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ int logname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int logname_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { char buf[64]; if (argv[1]) { bb_show_usage(); } /* Using _r function - avoid pulling in static buffer from libc */ if (getlogin_r(buf, sizeof(buf)) == 0) { puts(buf); return fflush_all(); } bb_perror_msg_and_die("getlogin"); } busybox-1.22.1/coreutils/cal.c0000644000000000000000000002375412263563520014721 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Calendar implementation for busybox * * See original copyright at the end of this file * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant with -j and -y extensions (from util-linux). */ /* BB_AUDIT BUG: The output of 'cal -j 1752' is incorrect. The upstream * BB_AUDIT BUG: version in util-linux seems to be broken as well. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/cal.html */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Major size reduction... over 50% (>1.5k) on i386. */ //usage:#define cal_trivial_usage //usage: "[-jy] [[MONTH] YEAR]" //usage:#define cal_full_usage "\n\n" //usage: "Display a calendar\n" //usage: "\n -j Use julian dates" //usage: "\n -y Display the entire year" #include "libbb.h" #include "unicode.h" /* We often use "unsigned" intead of "int", it's easier to div on most CPUs */ #define THURSDAY 4 /* for reformation */ #define SATURDAY 6 /* 1 Jan 1 was a Saturday */ #define FIRST_MISSING_DAY 639787 /* 3 Sep 1752 */ #define NUMBER_MISSING_DAYS 11 /* 11 day correction */ #define MAXDAYS 42 /* max slots in a month array */ #define SPACE -1 /* used in day array */ static const unsigned char days_in_month[] ALIGN1 = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static const unsigned char sep1752[] ALIGN1 = { 1, 2, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }; /* Set to 0 or 1 in main */ #define julian ((unsigned)option_mask32) /* leap year -- account for Gregorian reformation in 1752 */ static int leap_year(unsigned yr) { if (yr <= 1752) return !(yr % 4); return (!(yr % 4) && (yr % 100)) || !(yr % 400); } /* number of centuries since 1700, not inclusive */ #define centuries_since_1700(yr) \ ((yr) > 1700 ? (yr) / 100 - 17 : 0) /* number of centuries since 1700 whose modulo of 400 is 0 */ #define quad_centuries_since_1700(yr) \ ((yr) > 1600 ? ((yr) - 1600) / 400 : 0) /* number of leap years between year 1 and this year, not inclusive */ #define leap_years_since_year_1(yr) \ ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr)) static void center(char *, unsigned, unsigned); static void day_array(unsigned, unsigned, unsigned *); static void trim_trailing_spaces_and_print(char *); static void blank_string(char *buf, size_t buflen); static char *build_row(char *p, unsigned *dp); #define DAY_LEN 3 /* 3 spaces per day */ #define J_DAY_LEN (DAY_LEN + 1) #define WEEK_LEN 20 /* 7 * 3 - one space at the end */ #define J_WEEK_LEN (WEEK_LEN + 7) #define HEAD_SEP 2 /* spaces between day headings */ int cal_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int cal_main(int argc UNUSED_PARAM, char **argv) { struct tm zero_tm; time_t now; unsigned month, year, flags, i; char *month_names[12]; /* normal heading: */ /* "Su Mo Tu We Th Fr Sa" */ /* -j heading: */ /* " Su Mo Tu We Th Fr Sa" */ char day_headings[ENABLE_UNICODE_SUPPORT ? 28 * 6 : 28]; IF_UNICODE_SUPPORT(char *hp = day_headings;) char buf[40]; init_unicode(); flags = getopt32(argv, "jy"); /* This sets julian = flags & 1: */ option_mask32 &= 1; month = 0; argv += optind; if (!argv[0]) { struct tm *ptm; time(&now); ptm = localtime(&now); year = ptm->tm_year + 1900; if (!(flags & 2)) { /* no -y */ month = ptm->tm_mon + 1; } } else { if (argv[1]) { if (argv[2]) { bb_show_usage(); } if (!(flags & 2)) { /* no -y */ month = xatou_range(*argv, 1, 12); } argv++; } year = xatou_range(*argv, 1, 9999); } blank_string(day_headings, sizeof(day_headings) - 7 + 7*julian); i = 0; do { zero_tm.tm_mon = i; /* full month name according to locale */ strftime(buf, sizeof(buf), "%B", &zero_tm); month_names[i] = xstrdup(buf); if (i < 7) { zero_tm.tm_wday = i; /* abbreviated weekday name according to locale */ strftime(buf, sizeof(buf), "%a", &zero_tm); #if ENABLE_UNICODE_SUPPORT if (julian) *hp++ = ' '; { char *two_wchars = unicode_conv_to_printable_fixedwidth(/*NULL,*/ buf, 2); strcpy(hp, two_wchars); free(two_wchars); } hp += strlen(hp); *hp++ = ' '; #else strncpy(day_headings + i * (3+julian) + julian, buf, 2); #endif } } while (++i < 12); IF_UNICODE_SUPPORT(hp[-1] = '\0';) if (month) { unsigned row, len, days[MAXDAYS]; unsigned *dp = days; char lineout[30]; day_array(month, year, dp); len = sprintf(lineout, "%s %u", month_names[month - 1], year); printf("%*s%s\n%s\n", ((7*julian + WEEK_LEN) - len) / 2, "", lineout, day_headings); for (row = 0; row < 6; row++) { build_row(lineout, dp)[0] = '\0'; dp += 7; trim_trailing_spaces_and_print(lineout); } } else { unsigned row, which_cal, week_len, days[12][MAXDAYS]; unsigned *dp; char lineout[80]; sprintf(lineout, "%u", year); center(lineout, (WEEK_LEN * 3 + HEAD_SEP * 2) + julian * (J_WEEK_LEN * 2 + HEAD_SEP - (WEEK_LEN * 3 + HEAD_SEP * 2)), 0 ); puts("\n"); /* two \n's */ for (i = 0; i < 12; i++) { day_array(i + 1, year, days[i]); } blank_string(lineout, sizeof(lineout)); week_len = WEEK_LEN + julian * (J_WEEK_LEN - WEEK_LEN); for (month = 0; month < 12; month += 3-julian) { center(month_names[month], week_len, HEAD_SEP); if (!julian) { center(month_names[month + 1], week_len, HEAD_SEP); } center(month_names[month + 2 - julian], week_len, 0); printf("\n%s%*s%s", day_headings, HEAD_SEP, "", day_headings); if (!julian) { printf("%*s%s", HEAD_SEP, "", day_headings); } bb_putchar('\n'); for (row = 0; row < (6*7); row += 7) { for (which_cal = 0; which_cal < 3-julian; which_cal++) { dp = days[month + which_cal] + row; build_row(lineout + which_cal * (week_len + 2), dp); } /* blank_string took care of nul termination. */ trim_trailing_spaces_and_print(lineout); } } } fflush_stdout_and_exit(EXIT_SUCCESS); } /* * day_array -- * Fill in an array of 42 integers with a calendar. Assume for a moment * that you took the (maximum) 6 rows in a calendar and stretched them * out end to end. You would have 42 numbers or spaces. This routine * builds that array for any month from Jan. 1 through Dec. 9999. */ static void day_array(unsigned month, unsigned year, unsigned *days) { unsigned long temp; unsigned i; unsigned day, dw, dm; memset(days, SPACE, MAXDAYS * sizeof(int)); if ((month == 9) && (year == 1752)) { /* Assumes the Gregorian reformation eliminates * 3 Sep. 1752 through 13 Sep. 1752. */ unsigned j_offset = julian * 244; size_t oday = 0; do { days[oday+2] = sep1752[oday] + j_offset; } while (++oday < sizeof(sep1752)); return; } /* day_in_year * return the 1 based day number within the year */ day = 1; if ((month > 2) && leap_year(year)) { ++day; } i = month; while (i) { day += days_in_month[--i]; } /* day_in_week * return the 0 based day number for any date from 1 Jan. 1 to * 31 Dec. 9999. Assumes the Gregorian reformation eliminates * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all * missing days. */ temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1) + day; if (temp < FIRST_MISSING_DAY) { dw = ((temp - 1 + SATURDAY) % 7); } else { dw = (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7); } if (!julian) { day = 1; } dm = days_in_month[month]; if ((month == 2) && leap_year(year)) { ++dm; } do { days[dw++] = day++; } while (--dm); } static void trim_trailing_spaces_and_print(char *s) { char *p = s; while (*p) { ++p; } while (p != s) { --p; if (!isspace(*p)) { p[1] = '\0'; break; } } puts(s); } static void center(char *str, unsigned len, unsigned separate) { unsigned n = strlen(str); len -= n; printf("%*s%*s", (len/2) + n, str, (len/2) + (len % 2) + separate, ""); } static void blank_string(char *buf, size_t buflen) { memset(buf, ' ', buflen); buf[buflen-1] = '\0'; } static char *build_row(char *p, unsigned *dp) { unsigned col, val, day; memset(p, ' ', (julian + DAY_LEN) * 7); col = 0; do { day = *dp++; if (day != SPACE) { if (julian) { ++p; if (day >= 100) { *p = '0'; p[-1] = (day / 100) + '0'; day %= 100; } } val = day / 10; if (val > 0) { *p = val + '0'; } *++p = day % 10 + '0'; p += 2; } else { p += DAY_LEN + julian; } } while (++col < 7); return p; } /* * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Kim Letkeman. * * 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. */ busybox-1.22.1/coreutils/echo.c0000644000000000000000000002177412263563520015100 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * echo implementation for busybox * * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Original copyright notice is retained at the end of this file. */ /* BB_AUDIT SUSv3 compliant -- unless configured as fancy echo. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/echo.html */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Because of behavioral differences, implemented configurable SUSv3 * or 'fancy' gnu-ish behaviors. Also, reduced size and fixed bugs. * 1) In handling '\c' escape, the previous version only suppressed the * trailing newline. SUSv3 specifies _no_ output after '\c'. * 2) SUSv3 specifies that octal escapes are of the form \0{#{#{#}}}. * The previous version did not allow 4-digit octals. */ //usage:#define echo_trivial_usage //usage: IF_FEATURE_FANCY_ECHO("[-neE] ") "[ARG]..." //usage:#define echo_full_usage "\n\n" //usage: "Print the specified ARGs to stdout" //usage: IF_FEATURE_FANCY_ECHO( "\n" //usage: "\n -n Suppress trailing newline" //usage: "\n -e Interpret backslash escapes (i.e., \\t=tab)" //usage: "\n -E Don't interpret backslash escapes (default)" //usage: ) //usage: //usage:#define echo_example_usage //usage: "$ echo \"Erik is cool\"\n" //usage: "Erik is cool\n" //usage: IF_FEATURE_FANCY_ECHO("$ echo -e \"Erik\\nis\\ncool\"\n" //usage: "Erik\n" //usage: "is\n" //usage: "cool\n" //usage: "$ echo \"Erik\\nis\\ncool\"\n" //usage: "Erik\\nis\\ncool\n") #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ /* NB: can be used by shell even if not enabled as applet */ /* * NB2: we don't use stdio, we need better error handing. * Examples include writing into non-opened stdout and error on write. * * With stdio, output gets shoveled into stdout buffer, and even * fflush cannot clear it out. It seems that even if libc receives * EBADF on write attempts, it feels determined to output data no matter what. * If echo is called by shell, it will try writing again later, and possibly * will clobber future output. Not good. * * Solaris has fpurge which discards buffered input. glibc has __fpurge. * But this function is not standard. */ int echo_main(int argc UNUSED_PARAM, char **argv) { char **pp; const char *arg; char *out; char *buffer; unsigned buflen; #if !ENABLE_FEATURE_FANCY_ECHO enum { eflag = '\\', nflag = 1, /* 1 -- print '\n' */ }; argv++; #else char nflag = 1; char eflag = 0; while ((arg = *++argv) != NULL) { char n, e; if (arg[0] != '-') break; /* not an option arg, echo it */ /* If it appears that we are handling options, then make sure * that all of the options specified are actually valid. * Otherwise, the string should just be echoed. */ arg++; n = nflag; e = eflag; do { if (*arg == 'n') n = 0; else if (*arg == 'e') e = '\\'; else if (*arg != 'E') { /* "-ccc" arg with one of c's invalid, echo it */ /* arg consisting from just "-" also handled here */ goto just_echo; } } while (*++arg); nflag = n; eflag = e; } just_echo: #endif buflen = 0; pp = argv; while ((arg = *pp) != NULL) { buflen += strlen(arg) + 1; pp++; } out = buffer = xmalloc(buflen + 1); /* +1 is needed for "no args" case */ while ((arg = *argv) != NULL) { int c; if (!eflag) { /* optimization for very common case */ out = stpcpy(out, arg); } else while ((c = *arg++) != '\0') { if (c == eflag) { /* This is an "\x" sequence */ if (*arg == 'c') { /* "\c" means cancel newline and * ignore all subsequent chars. */ goto do_write; } /* Since SUSv3 mandates a first digit of 0, 4-digit octals * of the form \0### are accepted. */ if (*arg == '0') { if ((unsigned char)(arg[1] - '0') < 8) { /* 2nd char is 0..7: skip leading '0' */ arg++; } } /* bb_process_escape_sequence handles NUL correctly * ("...\" case). */ { /* optimization: don't force arg to be on-stack, * use another variable for that. ~30 bytes win */ const char *z = arg; c = bb_process_escape_sequence(&z); arg = z; } } *out++ = c; } if (!*++argv) break; *out++ = ' '; } if (nflag) { *out++ = '\n'; } do_write: /* Careful to error out on partial writes too (think ENOSPC!) */ errno = 0; /*r =*/ full_write(STDOUT_FILENO, buffer, out - buffer); free(buffer); if (/*WRONG:r < 0*/ errno) { bb_perror_msg(bb_msg_write_error); return 1; } return 0; } /* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * 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. * * California, Berkeley and its contributors. * 4. 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. * * @(#)echo.c 8.1 (Berkeley) 5/31/93 */ #ifdef VERSION_WITH_WRITEV /* We can't use stdio. * The reason for this is highly non-obvious. * echo_main is used from shell. Shell must correctly handle "echo foo" * if stdout is closed. With stdio, output gets shoveled into * stdout buffer, and even fflush cannot clear it out. It seems that * even if libc receives EBADF on write attempts, it feels determined * to output data no matter what. So it will try later, * and possibly will clobber future output. Not good. * * Using writev instead, with 'direct' conversion of argv vector. */ int echo_main(int argc, char **argv) { struct iovec io[argc]; struct iovec *cur_io = io; char *arg; char *p; #if !ENABLE_FEATURE_FANCY_ECHO enum { eflag = '\\', nflag = 1, /* 1 -- print '\n' */ }; arg = *++argv; if (!arg) goto newline_ret; #else char nflag = 1; char eflag = 0; while (1) { arg = *++argv; if (!arg) goto newline_ret; if (*arg != '-') break; /* If it appears that we are handling options, then make sure * that all of the options specified are actually valid. * Otherwise, the string should just be echoed. */ p = arg + 1; if (!*p) /* A single '-', so echo it. */ goto just_echo; do { if (!strchr("neE", *p)) goto just_echo; } while (*++p); /* All of the options in this arg are valid, so handle them. */ p = arg + 1; do { if (*p == 'n') nflag = 0; if (*p == 'e') eflag = '\\'; } while (*++p); } just_echo: #endif while (1) { /* arg is already == *argv and isn't NULL */ int c; cur_io->iov_base = p = arg; if (!eflag) { /* optimization for very common case */ p += strlen(arg); } else while ((c = *arg++)) { if (c == eflag) { /* This is an "\x" sequence */ if (*arg == 'c') { /* "\c" means cancel newline and * ignore all subsequent chars. */ cur_io->iov_len = p - (char*)cur_io->iov_base; cur_io++; goto ret; } /* Since SUSv3 mandates a first digit of 0, 4-digit octals * of the form \0### are accepted. */ if (*arg == '0' && (unsigned char)(arg[1] - '0') < 8) { arg++; } /* bb_process_escape_sequence can handle nul correctly */ c = bb_process_escape_sequence( (void*) &arg); } *p++ = c; } arg = *++argv; if (arg) *p++ = ' '; cur_io->iov_len = p - (char*)cur_io->iov_base; cur_io++; if (!arg) break; } newline_ret: if (nflag) { cur_io->iov_base = (char*)"\n"; cur_io->iov_len = 1; cur_io++; } ret: /* TODO: implement and use full_writev? */ return writev(1, io, (cur_io - io)) >= 0; } #endif busybox-1.22.1/coreutils/stty.c0000644000000000000000000012473512263563520015166 0ustar rootroot/* vi: set sw=4 ts=4: */ /* stty -- change and print terminal line settings Copyright (C) 1990-1999 Free Software Foundation, Inc. Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* Usage: stty [-ag] [-F device] [setting...] Options: -a Write all current settings to stdout in human-readable form. -g Write all current settings to stdout in stty-readable form. -F Open and use the specified device instead of stdin If no args are given, write to stdout the baud rate and settings that have been changed from their defaults. Mode reading and changes are done on the specified device, or stdin if none was specified. David MacKenzie Special for busybox ported by Vladimir Oleynik 2001 */ //usage:#define stty_trivial_usage //usage: "[-a|g] [-F DEVICE] [SETTING]..." //usage:#define stty_full_usage "\n\n" //usage: "Without arguments, prints baud rate, line discipline,\n" //usage: "and deviations from stty sane\n" //usage: "\n -F DEVICE Open device instead of stdin" //usage: "\n -a Print all current settings in human-readable form" //usage: "\n -g Print in stty-readable form" //usage: "\n [SETTING] See manpage" #include "libbb.h" #ifndef _POSIX_VDISABLE # define _POSIX_VDISABLE ((unsigned char) 0) #endif #define Control(c) ((c) & 0x1f) /* Canonical values for control characters */ #ifndef CINTR # define CINTR Control('c') #endif #ifndef CQUIT # define CQUIT 28 #endif #ifndef CERASE # define CERASE 127 #endif #ifndef CKILL # define CKILL Control('u') #endif #ifndef CEOF # define CEOF Control('d') #endif #ifndef CEOL # define CEOL _POSIX_VDISABLE #endif #ifndef CSTART # define CSTART Control('q') #endif #ifndef CSTOP # define CSTOP Control('s') #endif #ifndef CSUSP # define CSUSP Control('z') #endif #if defined(VEOL2) && !defined(CEOL2) # define CEOL2 _POSIX_VDISABLE #endif /* glibc-2.12.1 uses only VSWTC name */ #if defined(VSWTC) && !defined(VSWTCH) # define VSWTCH VSWTC #endif /* ISC renamed swtch to susp for termios, but we'll accept either name */ #if defined(VSUSP) && !defined(VSWTCH) # define VSWTCH VSUSP # define CSWTCH CSUSP #endif #if defined(VSWTCH) && !defined(CSWTCH) # define CSWTCH _POSIX_VDISABLE #endif /* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'. So the default is to disable 'swtch.' */ #if defined(__sparc__) && defined(__svr4__) # undef CSWTCH # define CSWTCH _POSIX_VDISABLE #endif #if defined(VWERSE) && !defined(VWERASE) /* AIX-3.2.5 */ # define VWERASE VWERSE #endif #if defined(VDSUSP) && !defined(CDSUSP) # define CDSUSP Control('y') #endif #if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */ # define VREPRINT VRPRNT #endif #if defined(VREPRINT) && !defined(CRPRNT) # define CRPRNT Control('r') #endif #if defined(VWERASE) && !defined(CWERASE) # define CWERASE Control('w') #endif #if defined(VLNEXT) && !defined(CLNEXT) # define CLNEXT Control('v') #endif #if defined(VDISCARD) && !defined(VFLUSHO) # define VFLUSHO VDISCARD #endif #if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */ # define VFLUSHO VFLUSH #endif #if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */ # define ECHOCTL CTLECH #endif #if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */ # define ECHOCTL TCTLECH #endif #if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */ # define ECHOKE CRTKIL #endif #if defined(VFLUSHO) && !defined(CFLUSHO) # define CFLUSHO Control('o') #endif #if defined(VSTATUS) && !defined(CSTATUS) # define CSTATUS Control('t') #endif /* Save us from #ifdef forest plague */ #ifndef BSDLY # define BSDLY 0 #endif #ifndef CIBAUD # define CIBAUD 0 #endif #ifndef CRDLY # define CRDLY 0 #endif #ifndef CRTSCTS # define CRTSCTS 0 #endif #ifndef ECHOCTL # define ECHOCTL 0 #endif #ifndef ECHOKE # define ECHOKE 0 #endif #ifndef ECHOPRT # define ECHOPRT 0 #endif #ifndef FFDLY # define FFDLY 0 #endif #ifndef IEXTEN # define IEXTEN 0 #endif #ifndef IMAXBEL # define IMAXBEL 0 #endif #ifndef IUCLC # define IUCLC 0 #endif #ifndef IXANY # define IXANY 0 #endif #ifndef NLDLY # define NLDLY 0 #endif #ifndef OCRNL # define OCRNL 0 #endif #ifndef OFDEL # define OFDEL 0 #endif #ifndef OFILL # define OFILL 0 #endif #ifndef OLCUC # define OLCUC 0 #endif #ifndef ONLCR # define ONLCR 0 #endif #ifndef ONLRET # define ONLRET 0 #endif #ifndef ONOCR # define ONOCR 0 #endif #ifndef OXTABS # define OXTABS 0 #endif #ifndef TABDLY # define TABDLY 0 #endif #ifndef TAB1 # define TAB1 0 #endif #ifndef TAB2 # define TAB2 0 #endif #ifndef TOSTOP # define TOSTOP 0 #endif #ifndef VDSUSP # define VDSUSP 0 #endif #ifndef VEOL2 # define VEOL2 0 #endif #ifndef VFLUSHO # define VFLUSHO 0 #endif #ifndef VLNEXT # define VLNEXT 0 #endif #ifndef VREPRINT # define VREPRINT 0 #endif #ifndef VSTATUS # define VSTATUS 0 #endif #ifndef VSWTCH # define VSWTCH 0 #endif #ifndef VTDLY # define VTDLY 0 #endif #ifndef VWERASE # define VWERASE 0 #endif #ifndef XCASE # define XCASE 0 #endif #ifndef IUTF8 # define IUTF8 0 #endif /* Which speeds to set */ enum speed_setting { input_speed, output_speed, both_speeds }; /* Which member(s) of 'struct termios' a mode uses */ enum { control, input, output, local, combination }; static tcflag_t *get_ptr_to_tcflag(unsigned type, const struct termios *mode) { static const uint8_t tcflag_offsets[] ALIGN1 = { offsetof(struct termios, c_cflag), /* control */ offsetof(struct termios, c_iflag), /* input */ offsetof(struct termios, c_oflag), /* output */ offsetof(struct termios, c_lflag) /* local */ }; if (type <= local) { return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]); } return NULL; } /* Flags for 'struct mode_info' */ #define SANE_SET 1 /* Set in 'sane' mode */ #define SANE_UNSET 2 /* Unset in 'sane' mode */ #define REV 4 /* Can be turned off by prepending '-' */ #define OMIT 8 /* Don't display value */ /* Each mode. * This structure should be kept as small as humanly possible. */ struct mode_info { const uint8_t type; /* Which structure element to change */ const uint8_t flags; /* Setting and display options */ /* only these values are ever used, so... */ #if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100 const uint8_t mask; #elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000 const uint16_t mask; #else const tcflag_t mask; /* Other bits to turn off for this mode */ #endif /* was using short here, but ppc32 was unhappy */ const tcflag_t bits; /* Bits to set for this mode */ }; enum { /* Must match mode_name[] and mode_info[] order! */ IDX_evenp = 0, IDX_parity, IDX_oddp, IDX_nl, IDX_ek, IDX_sane, IDX_cooked, IDX_raw, IDX_pass8, IDX_litout, IDX_cbreak, IDX_crt, IDX_dec, #if IXANY IDX_decctlq, #endif #if TABDLY || OXTABS IDX_tabs, #endif #if XCASE && IUCLC && OLCUC IDX_lcase, IDX_LCASE, #endif }; #define MI_ENTRY(N,T,F,B,M) N "\0" /* Mode names given on command line */ static const char mode_name[] = MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) MI_ENTRY("ek", combination, OMIT, 0, 0 ) MI_ENTRY("sane", combination, OMIT, 0, 0 ) MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) MI_ENTRY("crt", combination, OMIT, 0, 0 ) MI_ENTRY("dec", combination, OMIT, 0, 0 ) #if IXANY MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ) #endif #if TABDLY || OXTABS MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ) #endif #if XCASE && IUCLC && OLCUC MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ) MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ) #endif MI_ENTRY("parenb", control, REV, PARENB, 0 ) MI_ENTRY("parodd", control, REV, PARODD, 0 ) MI_ENTRY("cs5", control, 0, CS5, CSIZE) MI_ENTRY("cs6", control, 0, CS6, CSIZE) MI_ENTRY("cs7", control, 0, CS7, CSIZE) MI_ENTRY("cs8", control, 0, CS8, CSIZE) MI_ENTRY("hupcl", control, REV, HUPCL, 0 ) MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ) MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ) MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ) MI_ENTRY("clocal", control, REV, CLOCAL, 0 ) #if CRTSCTS MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ) #endif MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ) MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ) MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ) MI_ENTRY("parmrk", input, REV, PARMRK, 0 ) MI_ENTRY("inpck", input, REV, INPCK, 0 ) MI_ENTRY("istrip", input, REV, ISTRIP, 0 ) MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ) MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ) MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) MI_ENTRY("ixon", input, REV, IXON, 0 ) MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 ) #if IUCLC MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) #endif #if IXANY MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ) #endif #if IMAXBEL MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ) #endif #if IUTF8 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 ) #endif MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ) #if OLCUC MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ) #endif #if OCRNL MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ) #endif #if ONLCR MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ) #endif #if ONOCR MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ) #endif #if ONLRET MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ) #endif #if OFILL MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ) #endif #if OFDEL MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ) #endif #if NLDLY MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY) MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY) #endif #if CRDLY MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY) MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY) MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY) MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY) #endif #if TABDLY MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY) # if TAB2 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY) # endif # if TAB1 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY) # endif MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY) #else # if OXTABS MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ) # endif #endif #if BSDLY MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY) MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY) #endif #if VTDLY MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY) MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY) #endif #if FFDLY MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY) MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY) #endif MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ) MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ) #if IEXTEN MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) #endif MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) #if XCASE MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ) #endif #if TOSTOP MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ) #endif #if ECHOPRT MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 ) #endif #if ECHOCTL MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 ) #endif #if ECHOKE MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 ) #endif ; #undef MI_ENTRY #define MI_ENTRY(N,T,F,B,M) { T, F, M, B }, static const struct mode_info mode_info[] = { /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */ MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) MI_ENTRY("ek", combination, OMIT, 0, 0 ) MI_ENTRY("sane", combination, OMIT, 0, 0 ) MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) MI_ENTRY("crt", combination, OMIT, 0, 0 ) MI_ENTRY("dec", combination, OMIT, 0, 0 ) #if IXANY MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ) #endif #if TABDLY || OXTABS MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ) #endif #if XCASE && IUCLC && OLCUC MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ) MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ) #endif MI_ENTRY("parenb", control, REV, PARENB, 0 ) MI_ENTRY("parodd", control, REV, PARODD, 0 ) MI_ENTRY("cs5", control, 0, CS5, CSIZE) MI_ENTRY("cs6", control, 0, CS6, CSIZE) MI_ENTRY("cs7", control, 0, CS7, CSIZE) MI_ENTRY("cs8", control, 0, CS8, CSIZE) MI_ENTRY("hupcl", control, REV, HUPCL, 0 ) MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ) MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ) MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ) MI_ENTRY("clocal", control, REV, CLOCAL, 0 ) #if CRTSCTS MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ) #endif MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ) MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ) MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ) MI_ENTRY("parmrk", input, REV, PARMRK, 0 ) MI_ENTRY("inpck", input, REV, INPCK, 0 ) MI_ENTRY("istrip", input, REV, ISTRIP, 0 ) MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ) MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ) MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) MI_ENTRY("ixon", input, REV, IXON, 0 ) MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 ) #if IUCLC MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) #endif #if IXANY MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ) #endif #if IMAXBEL MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ) #endif #if IUTF8 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 ) #endif MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ) #if OLCUC MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ) #endif #if OCRNL MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ) #endif #if ONLCR MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ) #endif #if ONOCR MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ) #endif #if ONLRET MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ) #endif #if OFILL MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ) #endif #if OFDEL MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ) #endif #if NLDLY MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY) MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY) #endif #if CRDLY MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY) MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY) MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY) MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY) #endif #if TABDLY MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY) # if TAB2 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY) # endif # if TAB1 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY) # endif MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY) #else # if OXTABS MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ) # endif #endif #if BSDLY MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY) MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY) #endif #if VTDLY MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY) MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY) #endif #if FFDLY MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY) MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY) #endif MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ) MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ) #if IEXTEN MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) #endif MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) #if XCASE MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ) #endif #if TOSTOP MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ) #endif #if ECHOPRT MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 ) #endif #if ECHOCTL MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 ) #endif #if ECHOKE MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 ) #endif }; enum { NUM_mode_info = ARRAY_SIZE(mode_info) }; /* Control characters */ struct control_info { const uint8_t saneval; /* Value to set for 'stty sane' */ const uint8_t offset; /* Offset in c_cc */ }; enum { /* Must match control_name[] and control_info[] order! */ CIDX_intr = 0, CIDX_quit, CIDX_erase, CIDX_kill, CIDX_eof, CIDX_eol, #if VEOL2 CIDX_eol2, #endif #if VSWTCH CIDX_swtch, #endif CIDX_start, CIDX_stop, CIDX_susp, #if VDSUSP CIDX_dsusp, #endif #if VREPRINT CIDX_rprnt, #endif #if VWERASE CIDX_werase, #endif #if VLNEXT CIDX_lnext, #endif #if VFLUSHO CIDX_flush, #endif #if VSTATUS CIDX_status, #endif CIDX_min, CIDX_time, }; #define CI_ENTRY(n,s,o) n "\0" /* Name given on command line */ static const char control_name[] = CI_ENTRY("intr", CINTR, VINTR ) CI_ENTRY("quit", CQUIT, VQUIT ) CI_ENTRY("erase", CERASE, VERASE ) CI_ENTRY("kill", CKILL, VKILL ) CI_ENTRY("eof", CEOF, VEOF ) CI_ENTRY("eol", CEOL, VEOL ) #if VEOL2 CI_ENTRY("eol2", CEOL2, VEOL2 ) #endif #if VSWTCH CI_ENTRY("swtch", CSWTCH, VSWTCH ) #endif CI_ENTRY("start", CSTART, VSTART ) CI_ENTRY("stop", CSTOP, VSTOP ) CI_ENTRY("susp", CSUSP, VSUSP ) #if VDSUSP CI_ENTRY("dsusp", CDSUSP, VDSUSP ) #endif #if VREPRINT CI_ENTRY("rprnt", CRPRNT, VREPRINT) #endif #if VWERASE CI_ENTRY("werase", CWERASE, VWERASE ) #endif #if VLNEXT CI_ENTRY("lnext", CLNEXT, VLNEXT ) #endif #if VFLUSHO CI_ENTRY("flush", CFLUSHO, VFLUSHO ) #endif #if VSTATUS CI_ENTRY("status", CSTATUS, VSTATUS ) #endif /* These must be last because of the display routines */ CI_ENTRY("min", 1, VMIN ) CI_ENTRY("time", 0, VTIME ) ; #undef CI_ENTRY #define CI_ENTRY(n,s,o) { s, o }, static const struct control_info control_info[] = { /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */ CI_ENTRY("intr", CINTR, VINTR ) CI_ENTRY("quit", CQUIT, VQUIT ) CI_ENTRY("erase", CERASE, VERASE ) CI_ENTRY("kill", CKILL, VKILL ) CI_ENTRY("eof", CEOF, VEOF ) CI_ENTRY("eol", CEOL, VEOL ) #if VEOL2 CI_ENTRY("eol2", CEOL2, VEOL2 ) #endif #if VSWTCH CI_ENTRY("swtch", CSWTCH, VSWTCH ) #endif CI_ENTRY("start", CSTART, VSTART ) CI_ENTRY("stop", CSTOP, VSTOP ) CI_ENTRY("susp", CSUSP, VSUSP ) #if VDSUSP CI_ENTRY("dsusp", CDSUSP, VDSUSP ) #endif #if VREPRINT CI_ENTRY("rprnt", CRPRNT, VREPRINT) #endif #if VWERASE CI_ENTRY("werase", CWERASE, VWERASE ) #endif #if VLNEXT CI_ENTRY("lnext", CLNEXT, VLNEXT ) #endif #if VFLUSHO CI_ENTRY("flush", CFLUSHO, VFLUSHO ) #endif #if VSTATUS CI_ENTRY("status", CSTATUS, VSTATUS ) #endif /* These must be last because of the display routines */ CI_ENTRY("min", 1, VMIN ) CI_ENTRY("time", 0, VTIME ) }; enum { NUM_control_info = ARRAY_SIZE(control_info) }; struct globals { const char *device_name; /* The width of the screen, for output wrapping */ unsigned max_col; /* Current position, to know when to wrap */ unsigned current_col; char buf[10]; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { \ G.device_name = bb_msg_standard_input; \ G.max_col = 80; \ } while (0) static void set_speed_or_die(enum speed_setting type, const char *arg, struct termios *mode) { speed_t baud; baud = tty_value_to_baud(xatou(arg)); if (type != output_speed) { /* either input or both */ cfsetispeed(mode, baud); } if (type != input_speed) { /* either output or both */ cfsetospeed(mode, baud); } } static NORETURN void perror_on_device_and_die(const char *fmt) { bb_perror_msg_and_die(fmt, G.device_name); } static void perror_on_device(const char *fmt) { bb_perror_msg(fmt, G.device_name); } /* Print format string MESSAGE and optional args. Wrap to next line first if it won't fit. Print a space first unless MESSAGE will start a new line */ static void wrapf(const char *message, ...) { char buf[128]; va_list args; unsigned buflen; va_start(args, message); buflen = vsnprintf(buf, sizeof(buf), message, args); va_end(args); /* We seem to be called only with suitable lengths, but check if somebody failed to adhere to this assumption just to be sure. */ if (!buflen || buflen >= sizeof(buf)) return; if (G.current_col > 0) { G.current_col++; if (buf[0] != '\n') { if (G.current_col + buflen >= G.max_col) { bb_putchar('\n'); G.current_col = 0; } else bb_putchar(' '); } } fputs(buf, stdout); G.current_col += buflen; if (buf[buflen-1] == '\n') G.current_col = 0; } static void newline(void) { if (G.current_col != 0) wrapf("\n"); } #ifdef TIOCGWINSZ static void set_window_size(int rows, int cols) { struct winsize win = { 0, 0, 0, 0 }; if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) { if (errno != EINVAL) { goto bail; } memset(&win, 0, sizeof(win)); } if (rows >= 0) win.ws_row = rows; if (cols >= 0) win.ws_col = cols; if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win)) bail: perror_on_device("%s"); } #endif static void display_window_size(int fancy) { const char *fmt_str = "%s\0%s: no size information for this device"; unsigned width, height; if (get_terminal_width_height(STDIN_FILENO, &width, &height)) { if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) { perror_on_device(fmt_str); } } else { wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n", height, width); } } static const struct suffix_mult stty_suffixes[] = { { "b", 512 }, { "k", 1024 }, { "B", 1024 }, { "", 0 } }; static const struct mode_info *find_mode(const char *name) { int i = index_in_strings(mode_name, name); return i >= 0 ? &mode_info[i] : NULL; } static const struct control_info *find_control(const char *name) { int i = index_in_strings(control_name, name); return i >= 0 ? &control_info[i] : NULL; } enum { param_need_arg = 0x80, param_line = 1 | 0x80, param_rows = 2 | 0x80, param_cols = 3 | 0x80, param_columns = 4 | 0x80, param_size = 5, param_speed = 6, param_ispeed = 7 | 0x80, param_ospeed = 8 | 0x80, }; static int find_param(const char *name) { static const char params[] ALIGN1 = "line\0" /* 1 */ "rows\0" /* 2 */ "cols\0" /* 3 */ "columns\0" /* 4 */ "size\0" /* 5 */ "speed\0" /* 6 */ "ispeed\0" "ospeed\0"; int i = index_in_strings(params, name) + 1; if (i == 0) return 0; if (i != 5 && i != 6) i |= 0x80; return i; } static int recover_mode(const char *arg, struct termios *mode) { int i, n; unsigned chr; unsigned long iflag, oflag, cflag, lflag; /* Scan into temporaries since it is too much trouble to figure out the right format for 'tcflag_t' */ if (sscanf(arg, "%lx:%lx:%lx:%lx%n", &iflag, &oflag, &cflag, &lflag, &n) != 4) return 0; mode->c_iflag = iflag; mode->c_oflag = oflag; mode->c_cflag = cflag; mode->c_lflag = lflag; arg += n; for (i = 0; i < NCCS; ++i) { if (sscanf(arg, ":%x%n", &chr, &n) != 1) return 0; mode->c_cc[i] = chr; arg += n; } /* Fail if there are too many fields */ if (*arg != '\0') return 0; return 1; } static void display_recoverable(const struct termios *mode, int UNUSED_PARAM dummy) { int i; printf("%lx:%lx:%lx:%lx", (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag, (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag); for (i = 0; i < NCCS; ++i) printf(":%x", (unsigned int) mode->c_cc[i]); bb_putchar('\n'); } static void display_speed(const struct termios *mode, int fancy) { //____________________ 01234567 8 9 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;"; unsigned long ispeed, ospeed; ispeed = cfgetispeed(mode); ospeed = cfgetospeed(mode); if (ispeed == 0 || ispeed == ospeed) { ispeed = ospeed; /* in case ispeed was 0 */ //________ 0123 4 5 6 7 8 9 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;"; } if (fancy) fmt_str += 9; wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed)); } static void do_display(const struct termios *mode, int all) { int i; tcflag_t *bitsp; unsigned long mask; int prev_type = control; display_speed(mode, 1); if (all) display_window_size(1); #ifdef __linux__ wrapf("line = %u;\n", mode->c_line); #else newline(); #endif for (i = 0; i != CIDX_min; ++i) { char ch; /* If swtch is the same as susp, don't print both */ #if VSWTCH == VSUSP if (i == CIDX_swtch) continue; #endif /* If eof uses the same slot as min, only print whichever applies */ #if VEOF == VMIN if (!(mode->c_lflag & ICANON) && (i == CIDX_eof || i == CIDX_eol) ) { continue; } #endif ch = mode->c_cc[control_info[i].offset]; if (ch == _POSIX_VDISABLE) strcpy(G.buf, ""); else visible(ch, G.buf, 0); wrapf("%s = %s;", nth_string(control_name, i), G.buf); } #if VEOF == VMIN if ((mode->c_lflag & ICANON) == 0) #endif wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]); newline(); for (i = 0; i < NUM_mode_info; ++i) { if (mode_info[i].flags & OMIT) continue; if (mode_info[i].type != prev_type) { newline(); prev_type = mode_info[i].type; } bitsp = get_ptr_to_tcflag(mode_info[i].type, mode); mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits; if ((*bitsp & mask) == mode_info[i].bits) { if (all || (mode_info[i].flags & SANE_UNSET)) wrapf("-%s"+1, nth_string(mode_name, i)); } else { if ((all && mode_info[i].flags & REV) || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) ) { wrapf("-%s", nth_string(mode_name, i)); } } } newline(); } static void sane_mode(struct termios *mode) { int i; for (i = 0; i < NUM_control_info; ++i) { #if VMIN == VEOF if (i == CIDX_min) break; #endif mode->c_cc[control_info[i].offset] = control_info[i].saneval; } for (i = 0; i < NUM_mode_info; ++i) { tcflag_t val; tcflag_t *bitsp = get_ptr_to_tcflag(mode_info[i].type, mode); if (!bitsp) continue; val = *bitsp & ~((unsigned long)mode_info[i].mask); if (mode_info[i].flags & SANE_SET) { *bitsp = val | mode_info[i].bits; } else if (mode_info[i].flags & SANE_UNSET) { *bitsp = val & ~mode_info[i].bits; } } } static void set_mode(const struct mode_info *info, int reversed, struct termios *mode) { tcflag_t *bitsp; bitsp = get_ptr_to_tcflag(info->type, mode); if (bitsp) { tcflag_t val = *bitsp & ~info->mask; if (reversed) *bitsp = val & ~info->bits; else *bitsp = val | info->bits; return; } /* !bitsp - it's a "combination" mode */ if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) { if (reversed) mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; else mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7; } else if (info == &mode_info[IDX_oddp]) { if (reversed) mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; else mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB; } else if (info == &mode_info[IDX_nl]) { if (reversed) { mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR; mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET; } else { mode->c_iflag = mode->c_iflag & ~ICRNL; if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR; } } else if (info == &mode_info[IDX_ek]) { mode->c_cc[VERASE] = CERASE; mode->c_cc[VKILL] = CKILL; } else if (info == &mode_info[IDX_sane]) { sane_mode(mode); } else if (info == &mode_info[IDX_cbreak]) { if (reversed) mode->c_lflag |= ICANON; else mode->c_lflag &= ~ICANON; } else if (info == &mode_info[IDX_pass8]) { if (reversed) { mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB; mode->c_iflag |= ISTRIP; } else { mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; mode->c_iflag &= ~ISTRIP; } } else if (info == &mode_info[IDX_litout]) { if (reversed) { mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB; mode->c_iflag |= ISTRIP; mode->c_oflag |= OPOST; } else { mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; mode->c_iflag &= ~ISTRIP; mode->c_oflag &= ~OPOST; } } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) { if ((info == &mode_info[IDX_raw] && reversed) || (info == &mode_info[IDX_cooked] && !reversed) ) { /* Cooked mode */ mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON; mode->c_oflag |= OPOST; mode->c_lflag |= ISIG | ICANON; #if VMIN == VEOF mode->c_cc[VEOF] = CEOF; #endif #if VTIME == VEOL mode->c_cc[VEOL] = CEOL; #endif } else { /* Raw mode */ mode->c_iflag = 0; mode->c_oflag &= ~OPOST; mode->c_lflag &= ~(ISIG | ICANON | XCASE); mode->c_cc[VMIN] = 1; mode->c_cc[VTIME] = 0; } } #if IXANY else if (info == &mode_info[IDX_decctlq]) { if (reversed) mode->c_iflag |= IXANY; else mode->c_iflag &= ~IXANY; } #endif #if TABDLY else if (info == &mode_info[IDX_tabs]) { if (reversed) mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3; else mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0; } #endif #if OXTABS else if (info == &mode_info[IDX_tabs]) { if (reversed) mode->c_oflag |= OXTABS; else mode->c_oflag &= ~OXTABS; } #endif #if XCASE && IUCLC && OLCUC else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) { if (reversed) { mode->c_lflag &= ~XCASE; mode->c_iflag &= ~IUCLC; mode->c_oflag &= ~OLCUC; } else { mode->c_lflag |= XCASE; mode->c_iflag |= IUCLC; mode->c_oflag |= OLCUC; } } #endif else if (info == &mode_info[IDX_crt]) { mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE; } else if (info == &mode_info[IDX_dec]) { mode->c_cc[VINTR] = 3; /* ^C */ mode->c_cc[VERASE] = 127; /* DEL */ mode->c_cc[VKILL] = 21; /* ^U */ mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE; if (IXANY) mode->c_iflag &= ~IXANY; } } static void set_control_char_or_die(const struct control_info *info, const char *arg, struct termios *mode) { unsigned char value; if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time]) value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes); else if (arg[0] == '\0' || arg[1] == '\0') value = arg[0]; else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0) value = _POSIX_VDISABLE; else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */ value = arg[1] & 0x1f; /* Non-letters get weird results */ if (arg[1] == '?') value = 127; } else value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes); mode->c_cc[info->offset] = value; } #define STTY_require_set_attr (1 << 0) #define STTY_speed_was_set (1 << 1) #define STTY_verbose_output (1 << 2) #define STTY_recoverable_output (1 << 3) #define STTY_noargs (1 << 4) int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int stty_main(int argc UNUSED_PARAM, char **argv) { struct termios mode; void (*output_func)(const struct termios *, int); const char *file_name = NULL; int display_all = 0; int stty_state; int k; INIT_G(); stty_state = STTY_noargs; output_func = do_display; /* First pass: only parse/verify command line params */ k = 0; while (argv[++k]) { const struct mode_info *mp; const struct control_info *cp; const char *arg = argv[k]; const char *argnext = argv[k+1]; int param; if (arg[0] == '-') { int i; mp = find_mode(arg+1); if (mp) { if (!(mp->flags & REV)) goto invalid_argument; stty_state &= ~STTY_noargs; continue; } /* It is an option - parse it */ i = 0; while (arg[++i]) { switch (arg[i]) { case 'a': stty_state |= STTY_verbose_output; output_func = do_display; display_all = 1; break; case 'g': stty_state |= STTY_recoverable_output; output_func = display_recoverable; break; case 'F': if (file_name) bb_error_msg_and_die("only one device may be specified"); file_name = &arg[i+1]; /* "-Fdevice" ? */ if (!file_name[0]) { /* nope, "-F device" */ int p = k+1; /* argv[p] is argnext */ file_name = argnext; if (!file_name) bb_error_msg_and_die(bb_msg_requires_arg, "-F"); /* remove -F param from arg[vc] */ while (argv[p]) { argv[p] = argv[p+1]; ++p; } } goto end_option; default: goto invalid_argument; } } end_option: continue; } mp = find_mode(arg); if (mp) { stty_state &= ~STTY_noargs; continue; } cp = find_control(arg); if (cp) { if (!argnext) bb_error_msg_and_die(bb_msg_requires_arg, arg); /* called for the side effect of xfunc death only */ set_control_char_or_die(cp, argnext, &mode); stty_state &= ~STTY_noargs; ++k; continue; } param = find_param(arg); if (param & param_need_arg) { if (!argnext) bb_error_msg_and_die(bb_msg_requires_arg, arg); ++k; } switch (param) { #ifdef __linux__ case param_line: # ifndef TIOCGWINSZ xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); break; # endif /* else fall-through */ #endif #ifdef TIOCGWINSZ case param_rows: case param_cols: case param_columns: xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); break; case param_size: #endif case param_speed: break; case param_ispeed: /* called for the side effect of xfunc death only */ set_speed_or_die(input_speed, argnext, &mode); break; case param_ospeed: /* called for the side effect of xfunc death only */ set_speed_or_die(output_speed, argnext, &mode); break; default: if (recover_mode(arg, &mode) == 1) break; if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break; invalid_argument: bb_error_msg_and_die("invalid argument '%s'", arg); } stty_state &= ~STTY_noargs; } /* Specifying both -a and -g is an error */ if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) == (STTY_verbose_output | STTY_recoverable_output) ) { bb_error_msg_and_die("-a and -g are mutually exclusive"); } /* Specifying -a or -g with non-options is an error */ if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) && !(stty_state & STTY_noargs) ) { bb_error_msg_and_die("modes may not be set when -a or -g is used"); } /* Now it is safe to start doing things */ if (file_name) { G.device_name = file_name; xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO); ndelay_off(STDIN_FILENO); } /* Initialize to all zeroes so there is no risk memcmp will report a spurious difference in an uninitialized portion of the structure */ memset(&mode, 0, sizeof(mode)); if (tcgetattr(STDIN_FILENO, &mode)) perror_on_device_and_die("%s"); if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) { get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL); output_func(&mode, display_all); return EXIT_SUCCESS; } /* Second pass: perform actions */ k = 0; while (argv[++k]) { const struct mode_info *mp; const struct control_info *cp; const char *arg = argv[k]; const char *argnext = argv[k+1]; int param; if (arg[0] == '-') { mp = find_mode(arg+1); if (mp) { set_mode(mp, 1 /* reversed */, &mode); stty_state |= STTY_require_set_attr; } /* It is an option - already parsed. Skip it */ continue; } mp = find_mode(arg); if (mp) { set_mode(mp, 0 /* non-reversed */, &mode); stty_state |= STTY_require_set_attr; continue; } cp = find_control(arg); if (cp) { ++k; set_control_char_or_die(cp, argnext, &mode); stty_state |= STTY_require_set_attr; continue; } param = find_param(arg); if (param & param_need_arg) { ++k; } switch (param) { #ifdef __linux__ case param_line: mode.c_line = xatoul_sfx(argnext, stty_suffixes); stty_state |= STTY_require_set_attr; break; #endif #ifdef TIOCGWINSZ case param_cols: case param_columns: set_window_size(-1, xatoul_sfx(argnext, stty_suffixes)); break; case param_size: display_window_size(0); break; case param_rows: set_window_size(xatoul_sfx(argnext, stty_suffixes), -1); break; #endif case param_speed: display_speed(&mode, 0); break; case param_ispeed: set_speed_or_die(input_speed, argnext, &mode); stty_state |= (STTY_require_set_attr | STTY_speed_was_set); break; case param_ospeed: set_speed_or_die(output_speed, argnext, &mode); stty_state |= (STTY_require_set_attr | STTY_speed_was_set); break; default: if (recover_mode(arg, &mode) == 1) stty_state |= STTY_require_set_attr; else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{ set_speed_or_die(both_speeds, arg, &mode); stty_state |= (STTY_require_set_attr | STTY_speed_was_set); } /* else - impossible (caught in the first pass): bb_error_msg_and_die("invalid argument '%s'", arg); */ } } if (stty_state & STTY_require_set_attr) { struct termios new_mode; if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode)) perror_on_device_and_die("%s"); /* POSIX (according to Zlotnick's book) tcsetattr returns zero if it performs *any* of the requested operations. This means it can report 'success' when it has actually failed to perform some proper subset of the requested operations. To detect this partial failure, get the current terminal attributes and compare them to the requested ones */ /* Initialize to all zeroes so there is no risk memcmp will report a spurious difference in an uninitialized portion of the structure */ memset(&new_mode, 0, sizeof(new_mode)); if (tcgetattr(STDIN_FILENO, &new_mode)) perror_on_device_and_die("%s"); if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) { /* * I think the below chunk is not necessary on Linux. * If you are deleting it, also delete STTY_speed_was_set bit - * it is only ever checked here. */ #if 0 /* was "if CIBAUD" */ /* SunOS 4.1.3 (at least) has the problem that after this sequence, tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2); sometimes (m1 != m2). The only difference is in the four bits of the c_cflag field corresponding to the baud rate. To save Sun users a little confusion, don't report an error if this happens. But suppress the error only if we haven't tried to set the baud rate explicitly -- otherwise we'd never give an error for a true failure to set the baud rate */ new_mode.c_cflag &= (~CIBAUD); if ((stty_state & STTY_speed_was_set) || memcmp(&mode, &new_mode, sizeof(mode)) != 0) #endif perror_on_device_and_die("%s: cannot perform all requested operations"); } } return EXIT_SUCCESS; } busybox-1.22.1/coreutils/ls.c0000644000000000000000000010651312263563520014573 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright (C) 1996 Brian Candler * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* [date unknown. Perhaps before year 2000] * To achieve a small memory footprint, this version of 'ls' doesn't do any * file sorting, and only has the most essential command line switches * (i.e., the ones I couldn't live without :-) All features which involve * linking in substantial chunks of libc can be disabled. * * Although I don't really want to add new features to this program to * keep it small, I *am* interested to receive bug fixes and ways to make * it more portable. * * KNOWN BUGS: * 1. hidden files can make column width too large * * NON-OPTIMAL BEHAVIOUR: * 1. autowidth reads directories twice * 2. if you do a short directory listing without filetype characters * appended, there's no need to stat each one * PORTABILITY: * 1. requires lstat (BSD) - how do you do it without? * * [2009-03] * ls sorts listing now, and supports almost all options. */ //usage:#define ls_trivial_usage //usage: "[-1AaCxd" //usage: IF_FEATURE_LS_FOLLOWLINKS("LH") //usage: IF_FEATURE_LS_RECURSIVE("R") //usage: IF_FEATURE_LS_FILETYPES("Fp") "lins" //usage: IF_FEATURE_LS_TIMESTAMPS("e") //usage: IF_FEATURE_HUMAN_READABLE("h") //usage: IF_FEATURE_LS_SORTFILES("rSXv") //usage: IF_FEATURE_LS_TIMESTAMPS("ctu") //usage: IF_SELINUX("kKZ") "]" //usage: IF_FEATURE_AUTOWIDTH(" [-w WIDTH]") " [FILE]..." //usage:#define ls_full_usage "\n\n" //usage: "List directory contents\n" //usage: "\n -1 One column output" //usage: "\n -a Include entries which start with ." //usage: "\n -A Like -a, but exclude . and .." //usage: "\n -C List by columns" //usage: "\n -x List by lines" //usage: "\n -d List directory entries instead of contents" //usage: IF_FEATURE_LS_FOLLOWLINKS( //usage: "\n -L Follow symlinks" //usage: "\n -H Follow symlinks on command line" //usage: ) //usage: IF_FEATURE_LS_RECURSIVE( //usage: "\n -R Recurse" //usage: ) //usage: IF_FEATURE_LS_FILETYPES( //usage: "\n -p Append / to dir entries" //usage: "\n -F Append indicator (one of */=@|) to entries" //usage: ) //usage: "\n -l Long listing format" //usage: "\n -i List inode numbers" //usage: "\n -n List numeric UIDs and GIDs instead of names" //usage: "\n -s List allocated blocks" //usage: IF_FEATURE_LS_TIMESTAMPS( //usage: "\n -e List full date and time" //usage: ) //usage: IF_FEATURE_HUMAN_READABLE( //usage: "\n -h List sizes in human readable format (1K 243M 2G)" //usage: ) //usage: IF_FEATURE_LS_SORTFILES( //usage: "\n -r Sort in reverse order" //usage: "\n -S Sort by size" //usage: "\n -X Sort by extension" //usage: "\n -v Sort by version" //usage: ) //usage: IF_FEATURE_LS_TIMESTAMPS( //usage: "\n -c With -l: sort by ctime" //usage: "\n -t With -l: sort by mtime" //usage: "\n -u With -l: sort by atime" //usage: ) //usage: IF_SELINUX( //usage: "\n -k List security context" //usage: "\n -K List security context in long format" //usage: "\n -Z List security context and permission" //usage: ) //usage: IF_FEATURE_AUTOWIDTH( //usage: "\n -w N Assume the terminal is N columns wide" //usage: ) //usage: IF_FEATURE_LS_COLOR( //usage: "\n --color[={always,never,auto}] Control coloring" //usage: ) #include "libbb.h" #include "unicode.h" /* This is a NOEXEC applet. Be very careful! */ #if ENABLE_FTPD /* ftpd uses ls, and without timestamps Mozilla won't understand * ftpd's LIST output. */ # undef CONFIG_FEATURE_LS_TIMESTAMPS # undef ENABLE_FEATURE_LS_TIMESTAMPS # undef IF_FEATURE_LS_TIMESTAMPS # undef IF_NOT_FEATURE_LS_TIMESTAMPS # define CONFIG_FEATURE_LS_TIMESTAMPS 1 # define ENABLE_FEATURE_LS_TIMESTAMPS 1 # define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__ # define IF_NOT_FEATURE_LS_TIMESTAMPS(...) #endif enum { TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ SPLIT_FILE = 0, SPLIT_DIR = 1, SPLIT_SUBDIR = 2, /* Bits in G.all_fmt: */ /* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ /* what file information will be listed */ LIST_INO = 1 << 0, LIST_BLOCKS = 1 << 1, LIST_MODEBITS = 1 << 2, LIST_NLINKS = 1 << 3, LIST_ID_NAME = 1 << 4, LIST_ID_NUMERIC = 1 << 5, LIST_CONTEXT = 1 << 6, LIST_SIZE = 1 << 7, LIST_DATE_TIME = 1 << 8, LIST_FULLTIME = 1 << 9, LIST_SYMLINK = 1 << 10, LIST_FILETYPE = 1 << 11, /* show / suffix for dirs */ LIST_CLASSIFY = 1 << 12, /* requires LIST_FILETYPE, also show *,|,@,= suffixes */ LIST_MASK = (LIST_CLASSIFY << 1) - 1, /* what files will be displayed */ DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */ DISP_HIDDEN = 1 << 14, /* show filenames starting with . */ DISP_DOT = 1 << 15, /* show . and .. */ DISP_NOLIST = 1 << 16, /* show directory as itself, not contents */ DISP_RECURSIVE = 1 << 17, /* show directory and everything below it */ DISP_ROWS = 1 << 18, /* print across rows */ DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1), /* what is the overall style of the listing */ STYLE_COLUMNAR = 1 << 19, /* many records per line */ STYLE_LONG = 2 << 19, /* one record per line, extended info */ STYLE_SINGLE = 3 << 19, /* one record per line */ STYLE_MASK = STYLE_SINGLE, /* which of the three times will be used */ TIME_CHANGE = (1 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS, TIME_ACCESS = (2 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS, TIME_MASK = (3 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS, /* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */ SORT_REVERSE = 1 << 23, SORT_NAME = 0, /* sort by file name */ SORT_SIZE = 1 << 24, /* sort by file size */ SORT_ATIME = 2 << 24, /* sort by last access time */ SORT_CTIME = 3 << 24, /* sort by last change time */ SORT_MTIME = 4 << 24, /* sort by last modification time */ SORT_VERSION = 5 << 24, /* sort by version */ SORT_EXT = 6 << 24, /* sort by file name extension */ SORT_DIR = 7 << 24, /* sort by file or directory */ SORT_MASK = (7 << 24) * ENABLE_FEATURE_LS_SORTFILES, LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \ LIST_DATE_TIME | LIST_SYMLINK, }; /* -Cadil1 Std options, busybox always supports */ /* -gnsxA Std options, busybox always supports */ /* -Q GNU option, busybox always supports */ /* -k SELinux option, busybox always supports (ignores if !SELinux) */ /* Std has -k which means "show sizes in kbytes" */ /* -LHRctur Std options, busybox optionally supports */ /* -Fp Std options, busybox optionally supports */ /* -SXvhTw GNU options, busybox optionally supports */ /* -T WIDTH Ignored (we don't use tabs on output) */ /* -KZ SELinux mandated options, busybox optionally supports */ /* (coreutils 8.4 has no -K, remove it?) */ /* -e I think we made this one up (looks similar to GNU --full-time) */ /* We already used up all 32 bits, if we need to add more, candidates for removal: */ /* -K, -T, -e (add --full-time instead) */ static const char ls_options[] ALIGN1 = "Cadil1gnsxQAk" /* 13 opts, total 13 */ IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */ IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */ IF_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */ IF_FEATURE_LS_RECURSIVE("R") /* 1, 24 */ IF_SELINUX("KZ") /* 2, 26 */ IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 28 */ IF_FEATURE_HUMAN_READABLE("h") /* 1, 29 */ IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 31 */ /* with --color, we use all 32 bits */; enum { //OPT_C = (1 << 0), //OPT_a = (1 << 1), //OPT_d = (1 << 2), //OPT_i = (1 << 3), //OPT_l = (1 << 4), //OPT_1 = (1 << 5), OPT_g = (1 << 6), //OPT_n = (1 << 7), //OPT_s = (1 << 8), //OPT_x = (1 << 9), OPT_Q = (1 << 10), //OPT_A = (1 << 11), //OPT_k = (1 << 12), OPTBIT_c = 13, OPTBIT_e, OPTBIT_t, OPTBIT_u, OPTBIT_S = OPTBIT_c + 4 * ENABLE_FEATURE_LS_TIMESTAMPS, OPTBIT_X, /* 18 */ OPTBIT_r, OPTBIT_v, OPTBIT_F = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES, OPTBIT_p, /* 22 */ OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES, OPTBIT_K = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE, OPTBIT_Z, /* 25 */ OPTBIT_L = OPTBIT_K + 2 * ENABLE_SELINUX, OPTBIT_H, /* 27 */ OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS, OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE, OPTBIT_w, /* 30 */ OPTBIT_color = OPTBIT_T + 2 * ENABLE_FEATURE_AUTOWIDTH, OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS, OPT_e = (1 << OPTBIT_e) * ENABLE_FEATURE_LS_TIMESTAMPS, OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS, OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS, OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES, OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES, OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES, OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES, OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES, OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES, OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE, OPT_K = (1 << OPTBIT_K) * ENABLE_SELINUX, OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX, OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS, OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS, OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE, OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_AUTOWIDTH, OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_AUTOWIDTH, OPT_color = (1 << OPTBIT_color) * ENABLE_FEATURE_LS_COLOR, }; /* TODO: simple toggles may be stored as OPT_xxx bits instead */ static const uint32_t opt_flags[] = { STYLE_COLUMNAR, /* C */ DISP_HIDDEN | DISP_DOT, /* a */ DISP_NOLIST, /* d */ LIST_INO, /* i */ LIST_LONG | STYLE_LONG, /* l */ STYLE_SINGLE, /* 1 */ LIST_LONG | STYLE_LONG, /* g (don't show owner) - handled via OPT_g. assumes l */ LIST_ID_NUMERIC | LIST_LONG | STYLE_LONG, /* n (assumes l) */ LIST_BLOCKS, /* s */ DISP_ROWS | STYLE_COLUMNAR, /* x */ 0, /* Q (quote filename) - handled via OPT_Q */ DISP_HIDDEN, /* A */ ENABLE_SELINUX * (LIST_CONTEXT|STYLE_SINGLE), /* k (ignored if !SELINUX) */ #if ENABLE_FEATURE_LS_TIMESTAMPS TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */ LIST_FULLTIME, /* e */ ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */ TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */ #endif #if ENABLE_FEATURE_LS_SORTFILES SORT_SIZE, /* S */ SORT_EXT, /* X */ SORT_REVERSE, /* r */ SORT_VERSION, /* v */ #endif #if ENABLE_FEATURE_LS_FILETYPES LIST_FILETYPE | LIST_CLASSIFY, /* F */ LIST_FILETYPE, /* p */ #endif #if ENABLE_FEATURE_LS_RECURSIVE DISP_RECURSIVE, /* R */ #endif #if ENABLE_SELINUX LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME|STYLE_SINGLE, /* K */ LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT|STYLE_SINGLE, /* Z */ #endif (1U << 31) /* options after Z are not processed through opt_flags */ }; /* * a directory entry and its stat info */ struct dnode { const char *name; /* usually basename, but think "ls -l dir/file" */ const char *fullname; /* full name (usable for stat etc) */ struct dnode *dn_next; /* for linked list */ IF_SELINUX(security_context_t sid;) smallint fname_allocated; /* Used to avoid re-doing [l]stat at printout stage * if we already collected needed data in scan stage: */ mode_t dn_mode_lstat; /* obtained with lstat, or 0 */ mode_t dn_mode_stat; /* obtained with stat, or 0 */ // struct stat dstat; // struct stat is huge. We don't need it in full. // At least we don't need st_dev and st_blksize, // but there are invisible fields as well // (such as nanosecond-resolution timespamps) // and padding, which we also don't want to store. // We also can pre-parse dev_t dn_rdev (in glibc, it's huge). // On 32-bit uclibc: dnode size went from 112 to 84 bytes. // /* Same names as in struct stat, but with dn_ instead of st_ pfx: */ mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */ off_t dn_size; #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES time_t dn_atime; time_t dn_mtime; time_t dn_ctime; #endif ino_t dn_ino; blkcnt_t dn_blocks; nlink_t dn_nlink; uid_t dn_uid; gid_t dn_gid; int dn_rdev_maj; int dn_rdev_min; // dev_t dn_dev; // blksize_t dn_blksize; }; struct globals { #if ENABLE_FEATURE_LS_COLOR smallint show_color; # define G_show_color (G.show_color) #else # define G_show_color 0 #endif smallint exit_code; unsigned all_fmt; #if ENABLE_FEATURE_AUTOWIDTH unsigned terminal_width; # define G_terminal_width (G.terminal_width) #else # define G_terminal_width TERMINAL_WIDTH #endif #if ENABLE_FEATURE_LS_TIMESTAMPS /* Do time() just once. Saves one syscall per file for "ls -l" */ time_t current_time_t; #endif } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { \ /* we have to zero it out because of NOEXEC */ \ memset(&G, 0, sizeof(G)); \ IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \ IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \ } while (0) /*** Output code ***/ /* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file * 3/7:multiplexed char/block device) * and we use 0 for unknown and 15 for executables (see below) */ #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) /* un fi chr - dir - blk - file - link - sock - - exe */ #define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)]) /* 036 black foreground 050 black background 037 red foreground 051 red background 040 green foreground 052 green background 041 brown foreground 053 brown background 042 blue foreground 054 blue background 043 magenta (purple) foreground 055 magenta background 044 cyan (light blue) foreground 056 cyan background 045 gray foreground 057 white background */ #define COLOR(mode) ( \ /*un fi chr - dir - blk - file - link - sock - - exe */ \ "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \ [TYPEINDEX(mode)]) /* Select normal (0) [actually "reset all"] or bold (1) * (other attributes are 2:dim 4:underline 5:blink 7:reverse, * let's use 7 for "impossible" types, just for fun) * Note: coreutils 6.9 uses inverted red for setuid binaries. */ #define ATTR(mode) ( \ /*un fi chr - dir - blk - file- link- sock- - exe */ \ "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \ [TYPEINDEX(mode)]) #if ENABLE_FEATURE_LS_COLOR /* mode of zero is interpreted as "unknown" (stat failed) */ static char fgcolor(mode_t mode) { if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return COLOR(0xF000); /* File is executable ... */ return COLOR(mode); } static char bold(mode_t mode) { if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return ATTR(0xF000); /* File is executable ... */ return ATTR(mode); } #endif #if ENABLE_FEATURE_LS_FILETYPES static char append_char(mode_t mode) { if (!(G.all_fmt & LIST_FILETYPE)) return '\0'; if (S_ISDIR(mode)) return '/'; if (!(G.all_fmt & LIST_CLASSIFY)) return '\0'; if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return '*'; return APPCHAR(mode); } #endif static unsigned calc_name_len(const char *name) { unsigned len; uni_stat_t uni_stat; // TODO: quote tab as \t, etc, if -Q name = printable_string(&uni_stat, name); if (!(option_mask32 & OPT_Q)) { return uni_stat.unicode_width; } len = 2 + uni_stat.unicode_width; while (*name) { if (*name == '"' || *name == '\\') { len++; } name++; } return len; } /* Return the number of used columns. * Note that only STYLE_COLUMNAR uses return value. * STYLE_SINGLE and STYLE_LONG don't care. * coreutils 7.2 also supports: * ls -b (--escape) = octal escapes (although it doesn't look like working) * ls -N (--literal) = not escape at all */ static unsigned print_name(const char *name) { unsigned len; uni_stat_t uni_stat; // TODO: quote tab as \t, etc, if -Q name = printable_string(&uni_stat, name); if (!(option_mask32 & OPT_Q)) { fputs(name, stdout); return uni_stat.unicode_width; } len = 2 + uni_stat.unicode_width; putchar('"'); while (*name) { if (*name == '"' || *name == '\\') { putchar('\\'); len++; } putchar(*name); name++; } putchar('"'); return len; } /* Return the number of used columns. * Note that only STYLE_COLUMNAR uses return value, * STYLE_SINGLE and STYLE_LONG don't care. */ static NOINLINE unsigned display_single(const struct dnode *dn) { unsigned column = 0; char *lpath; #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR struct stat statbuf; char append; #endif #if ENABLE_FEATURE_LS_FILETYPES append = append_char(dn->dn_mode); #endif /* Do readlink early, so that if it fails, error message * does not appear *inside* the "ls -l" line */ lpath = NULL; if (G.all_fmt & LIST_SYMLINK) if (S_ISLNK(dn->dn_mode)) lpath = xmalloc_readlink_or_warn(dn->fullname); if (G.all_fmt & LIST_INO) column += printf("%7llu ", (long long) dn->dn_ino); //TODO: -h should affect -s too: if (G.all_fmt & LIST_BLOCKS) column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1)); if (G.all_fmt & LIST_MODEBITS) column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode)); if (G.all_fmt & LIST_NLINKS) column += printf("%4lu ", (long) dn->dn_nlink); if (G.all_fmt & LIST_ID_NUMERIC) { if (option_mask32 & OPT_g) column += printf("%-8u ", (int) dn->dn_gid); else column += printf("%-8u %-8u ", (int) dn->dn_uid, (int) dn->dn_gid); } #if ENABLE_FEATURE_LS_USERNAME else if (G.all_fmt & LIST_ID_NAME) { if (option_mask32 & OPT_g) { column += printf("%-8.8s ", get_cached_groupname(dn->dn_gid)); } else { column += printf("%-8.8s %-8.8s ", get_cached_username(dn->dn_uid), get_cached_groupname(dn->dn_gid)); } } #endif if (G.all_fmt & LIST_SIZE) { if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) { column += printf("%4u, %3u ", dn->dn_rdev_maj, dn->dn_rdev_min); } else { if (option_mask32 & OPT_h) { column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ", /* print size, show one fractional, use suffixes */ make_human_readable_str(dn->dn_size, 1, 0) ); } else { column += printf("%9"OFF_FMT"u ", dn->dn_size); } } } #if ENABLE_FEATURE_LS_TIMESTAMPS if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) { char *filetime; time_t ttime = dn->dn_mtime; if (G.all_fmt & TIME_ACCESS) ttime = dn->dn_atime; if (G.all_fmt & TIME_CHANGE) ttime = dn->dn_ctime; filetime = ctime(&ttime); /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */ if (G.all_fmt & LIST_FULLTIME) { /* -e */ /* Note: coreutils 8.4 ls --full-time prints: * 2009-07-13 17:49:27.000000000 +0200 */ column += printf("%.24s ", filetime); } else { /* LIST_DATE_TIME */ /* G.current_time_t ~== time(NULL) */ time_t age = G.current_time_t - ttime; printf("%.6s ", filetime + 4); /* "Jun 30" */ if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { /* hh:mm if less than 6 months old */ printf("%.5s ", filetime + 11); } else { /* year. buggy if year > 9999 ;) */ printf(" %.4s ", filetime + 20); } column += 13; } } #endif #if ENABLE_SELINUX if (G.all_fmt & LIST_CONTEXT) { column += printf("%-32s ", dn->sid ? dn->sid : "unknown"); freecon(dn->sid); } #endif #if ENABLE_FEATURE_LS_COLOR if (G_show_color) { mode_t mode = dn->dn_mode_lstat; if (!mode) if (lstat(dn->fullname, &statbuf) == 0) mode = statbuf.st_mode; printf("\033[%u;%um", bold(mode), fgcolor(mode)); } #endif column += print_name(dn->name); if (G_show_color) { printf("\033[0m"); } if (lpath) { printf(" -> "); #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR if ((G.all_fmt & LIST_FILETYPE) || G_show_color) { mode_t mode = dn->dn_mode_stat; if (!mode) if (stat(dn->fullname, &statbuf) == 0) mode = statbuf.st_mode; # if ENABLE_FEATURE_LS_FILETYPES append = append_char(mode); # endif # if ENABLE_FEATURE_LS_COLOR if (G_show_color) { printf("\033[%u;%um", bold(mode), fgcolor(mode)); } # endif } #endif column += print_name(lpath) + 4; free(lpath); if (G_show_color) { printf("\033[0m"); } } #if ENABLE_FEATURE_LS_FILETYPES if (G.all_fmt & LIST_FILETYPE) { if (append) { putchar(append); column++; } } #endif return column; } static void display_files(struct dnode **dn, unsigned nfiles) { unsigned i, ncols, nrows, row, nc; unsigned column; unsigned nexttab; unsigned column_width = 0; /* used only by STYLE_COLUMNAR */ if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */ ncols = 1; } else { /* find the longest file name, use that as the column width */ for (i = 0; dn[i]; i++) { int len = calc_name_len(dn[i]->name); if (column_width < len) column_width = len; } column_width += 1 + IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + ) ((G.all_fmt & LIST_INO) ? 8 : 0) + ((G.all_fmt & LIST_BLOCKS) ? 5 : 0); ncols = (unsigned)G_terminal_width / column_width; } if (ncols > 1) { nrows = nfiles / ncols; if (nrows * ncols < nfiles) nrows++; /* round up fractionals */ } else { nrows = nfiles; ncols = 1; } column = 0; nexttab = 0; for (row = 0; row < nrows; row++) { for (nc = 0; nc < ncols; nc++) { /* reach into the array based on the column and row */ if (G.all_fmt & DISP_ROWS) i = (row * ncols) + nc; /* display across row */ else i = (nc * nrows) + row; /* display by column */ if (i < nfiles) { if (column > 0) { nexttab -= column; printf("%*s ", nexttab, ""); column += nexttab + 1; } nexttab = column + column_width; column += display_single(dn[i]); } } putchar('\n'); column = 0; } } /*** Dir scanning code ***/ static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) { struct stat statbuf; struct dnode *cur; cur = xzalloc(sizeof(*cur)); cur->fullname = fullname; cur->name = name; if ((option_mask32 & OPT_L) || force_follow) { #if ENABLE_SELINUX if (is_selinux_enabled()) { getfilecon(fullname, &cur->sid); } #endif if (stat(fullname, &statbuf)) { bb_simple_perror_msg(fullname); G.exit_code = EXIT_FAILURE; free(cur); return NULL; } cur->dn_mode_stat = statbuf.st_mode; } else { #if ENABLE_SELINUX if (is_selinux_enabled()) { lgetfilecon(fullname, &cur->sid); } #endif if (lstat(fullname, &statbuf)) { bb_simple_perror_msg(fullname); G.exit_code = EXIT_FAILURE; free(cur); return NULL; } cur->dn_mode_lstat = statbuf.st_mode; } /* cur->dstat = statbuf: */ cur->dn_mode = statbuf.st_mode ; cur->dn_size = statbuf.st_size ; #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES cur->dn_atime = statbuf.st_atime ; cur->dn_mtime = statbuf.st_mtime ; cur->dn_ctime = statbuf.st_ctime ; #endif cur->dn_ino = statbuf.st_ino ; cur->dn_blocks = statbuf.st_blocks; cur->dn_nlink = statbuf.st_nlink ; cur->dn_uid = statbuf.st_uid ; cur->dn_gid = statbuf.st_gid ; cur->dn_rdev_maj = major(statbuf.st_rdev); cur->dn_rdev_min = minor(statbuf.st_rdev); return cur; } static unsigned count_dirs(struct dnode **dn, int which) { unsigned dirs, all; if (!dn) return 0; dirs = all = 0; for (; *dn; dn++) { const char *name; all++; if (!S_ISDIR((*dn)->dn_mode)) continue; name = (*dn)->name; if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */ /* or if it's not . or .. */ || name[0] != '.' || (name[1] && (name[1] != '.' || name[2])) ) { dirs++; } } return which != SPLIT_FILE ? dirs : all - dirs; } /* get memory to hold an array of pointers */ static struct dnode **dnalloc(unsigned num) { if (num < 1) return NULL; num++; /* so that we have terminating NULL */ return xzalloc(num * sizeof(struct dnode *)); } #if ENABLE_FEATURE_LS_RECURSIVE static void dfree(struct dnode **dnp) { unsigned i; if (dnp == NULL) return; for (i = 0; dnp[i]; i++) { struct dnode *cur = dnp[i]; if (cur->fname_allocated) free((char*)cur->fullname); free(cur); } free(dnp); } #else #define dfree(...) ((void)0) #endif /* Returns NULL-terminated malloced vector of pointers (or NULL) */ static struct dnode **splitdnarray(struct dnode **dn, int which) { unsigned dncnt, d; struct dnode **dnp; if (dn == NULL) return NULL; /* count how many dirs or files there are */ dncnt = count_dirs(dn, which); /* allocate a file array and a dir array */ dnp = dnalloc(dncnt); /* copy the entrys into the file or dir array */ for (d = 0; *dn; dn++) { if (S_ISDIR((*dn)->dn_mode)) { const char *name; if (which == SPLIT_FILE) continue; name = (*dn)->name; if ((which & SPLIT_DIR) /* any dir... */ /* ... or not . or .. */ || name[0] != '.' || (name[1] && (name[1] != '.' || name[2])) ) { dnp[d++] = *dn; } } else if (which == SPLIT_FILE) { dnp[d++] = *dn; } } return dnp; } #if ENABLE_FEATURE_LS_SORTFILES static int sortcmp(const void *a, const void *b) { struct dnode *d1 = *(struct dnode **)a; struct dnode *d2 = *(struct dnode **)b; unsigned sort_opts = G.all_fmt & SORT_MASK; off_t dif; dif = 0; /* assume SORT_NAME */ // TODO: use pre-initialized function pointer // instead of branch forest if (sort_opts == SORT_SIZE) { dif = (d2->dn_size - d1->dn_size); } else if (sort_opts == SORT_ATIME) { dif = (d2->dn_atime - d1->dn_atime); } else if (sort_opts == SORT_CTIME) { dif = (d2->dn_ctime - d1->dn_ctime); } else if (sort_opts == SORT_MTIME) { dif = (d2->dn_mtime - d1->dn_mtime); } else if (sort_opts == SORT_DIR) { dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode); } else #if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1 if (sort_opts == SORT_VERSION) { dif = strverscmp(d1->name, d2->name); } else #endif if (sort_opts == SORT_EXT) { dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.')); } if (dif == 0) { /* sort by name, use as tie breaker for other sorts */ if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name); else dif = strcmp(d1->name, d2->name); } /* Make dif fit into an int */ if (sizeof(dif) > sizeof(int)) { enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) }; /* shift leaving only "int" worth of bits */ if (dif != 0) { dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT); } } return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif; } static void dnsort(struct dnode **dn, int size) { qsort(dn, size, sizeof(*dn), sortcmp); } static void sort_and_display_files(struct dnode **dn, unsigned nfiles) { dnsort(dn, nfiles); display_files(dn, nfiles); } #else # define dnsort(dn, size) ((void)0) # define sort_and_display_files(dn, nfiles) display_files(dn, nfiles) #endif /* Returns NULL-terminated malloced vector of pointers (or NULL) */ static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p) { struct dnode *dn, *cur, **dnp; struct dirent *entry; DIR *dir; unsigned i, nfiles; *nfiles_p = 0; dir = warn_opendir(path); if (dir == NULL) { G.exit_code = EXIT_FAILURE; return NULL; /* could not open the dir */ } dn = NULL; nfiles = 0; while ((entry = readdir(dir)) != NULL) { char *fullname; /* are we going to list the file- it may be . or .. or a hidden file */ if (entry->d_name[0] == '.') { if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2])) && !(G.all_fmt & DISP_DOT) ) { continue; } if (!(G.all_fmt & DISP_HIDDEN)) continue; } fullname = concat_path_file(path, entry->d_name); cur = my_stat(fullname, bb_basename(fullname), 0); if (!cur) { free(fullname); continue; } cur->fname_allocated = 1; cur->dn_next = dn; dn = cur; nfiles++; } closedir(dir); if (dn == NULL) return NULL; /* now that we know how many files there are * allocate memory for an array to hold dnode pointers */ *nfiles_p = nfiles; dnp = dnalloc(nfiles); for (i = 0; /* i < nfiles - detected via !dn below */; i++) { dnp[i] = dn; /* save pointer to node in array */ dn = dn->dn_next; if (!dn) break; } return dnp; } #if ENABLE_DESKTOP /* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html * If any of the -l, -n, -s options is specified, each list * of files within the directory shall be preceded by a * status line indicating the number of file system blocks * occupied by files in the directory in 512-byte units if * the -k option is not specified, or 1024-byte units if the * -k option is specified, rounded up to the next integral * number of units. */ /* by Jorgen Overgaard (jorgen AT antistaten.se) */ static off_t calculate_blocks(struct dnode **dn) { uoff_t blocks = 1; if (dn) { while (*dn) { /* st_blocks is in 512 byte blocks */ blocks += (*dn)->dn_blocks; dn++; } } /* Even though standard says use 512 byte blocks, coreutils use 1k */ /* Actually, we round up by calculating (blocks + 1) / 2, * "+ 1" was done when we initialized blocks to 1 */ return blocks >> 1; } #endif static void scan_and_display_dirs_recur(struct dnode **dn, int first) { unsigned nfiles; struct dnode **subdnp; for (; *dn; dn++) { if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) { if (!first) bb_putchar('\n'); first = 0; printf("%s:\n", (*dn)->fullname); } subdnp = scan_one_dir((*dn)->fullname, &nfiles); #if ENABLE_DESKTOP if ((G.all_fmt & STYLE_MASK) == STYLE_LONG) printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp)); #endif if (nfiles > 0) { /* list all files at this level */ sort_and_display_files(subdnp, nfiles); if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_RECURSIVE) ) { struct dnode **dnd; unsigned dndirs; /* recursive - list the sub-dirs */ dnd = splitdnarray(subdnp, SPLIT_SUBDIR); dndirs = count_dirs(subdnp, SPLIT_SUBDIR); if (dndirs > 0) { dnsort(dnd, dndirs); scan_and_display_dirs_recur(dnd, 0); /* free the array of dnode pointers to the dirs */ free(dnd); } } /* free the dnodes and the fullname mem */ dfree(subdnp); } } } int ls_main(int argc UNUSED_PARAM, char **argv) { struct dnode **dnd; struct dnode **dnf; struct dnode **dnp; struct dnode *dn; struct dnode *cur; unsigned opt; unsigned nfiles; unsigned dnfiles; unsigned dndirs; unsigned i; #if ENABLE_FEATURE_LS_COLOR /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */ /* coreutils 6.10: * # ls --color=BOGUS * ls: invalid argument 'BOGUS' for '--color' * Valid arguments are: * 'always', 'yes', 'force' * 'never', 'no', 'none' * 'auto', 'tty', 'if-tty' * (and substrings: "--color=alwa" work too) */ static const char ls_longopts[] ALIGN1 = "color\0" Optional_argument "\xff"; /* no short equivalent */ static const char color_str[] ALIGN1 = "always\0""yes\0""force\0" "auto\0""tty\0""if-tty\0"; /* need to initialize since --color has _an optional_ argument */ const char *color_opt = color_str; /* "always" */ #endif INIT_G(); init_unicode(); if (ENABLE_FEATURE_LS_SORTFILES) G.all_fmt = SORT_NAME; #if ENABLE_FEATURE_AUTOWIDTH /* obtain the terminal width */ get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL); /* go one less... */ G_terminal_width--; #endif /* process options */ IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;) opt_complementary = /* -e implies -l */ IF_FEATURE_LS_TIMESTAMPS("el") /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html: * in some pairs of opts, only last one takes effect: */ IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */ // ":m-l:l-m" - we don't have -m IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H") ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */ ":C-1:1-C" /* bycols/oneline */ ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */ IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */ /* -w NUM: */ IF_FEATURE_AUTOWIDTH(":w+"); opt = getopt32(argv, ls_options IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width) IF_FEATURE_LS_COLOR(, &color_opt) ); for (i = 0; opt_flags[i] != (1U << 31); i++) { if (opt & (1 << i)) { uint32_t flags = opt_flags[i]; if (flags & STYLE_MASK) G.all_fmt &= ~STYLE_MASK; if (flags & SORT_MASK) G.all_fmt &= ~SORT_MASK; if (flags & TIME_MASK) G.all_fmt &= ~TIME_MASK; G.all_fmt |= flags; } } #if ENABLE_FEATURE_LS_COLOR /* set G_show_color = 1/0 */ if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) { char *p = getenv("LS_COLORS"); /* LS_COLORS is unset, or (not empty && not "none") ? */ if (!p || (p[0] && strcmp(p, "none") != 0)) G_show_color = 1; } if (opt & OPT_color) { if (color_opt[0] == 'n') G_show_color = 0; else switch (index_in_substrings(color_str, color_opt)) { case 3: case 4: case 5: if (isatty(STDOUT_FILENO)) { case 0: case 1: case 2: G_show_color = 1; } } } #endif /* sort out which command line options take precedence */ if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST)) G.all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { if (G.all_fmt & TIME_CHANGE) G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME; if (G.all_fmt & TIME_ACCESS) G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME; } if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */ G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME); /* choose a display format if one was not already specified by an option */ if (!(G.all_fmt & STYLE_MASK)) G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE); argv += optind; if (!argv[0]) *--argv = (char*)"."; if (argv[1]) G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */ /* stuff the command line file names into a dnode array */ dn = NULL; nfiles = 0; do { cur = my_stat(*argv, *argv, /* follow links on command line unless -l, -s or -F: */ !((G.all_fmt & STYLE_MASK) == STYLE_LONG || (G.all_fmt & LIST_BLOCKS) || (option_mask32 & OPT_F) ) /* ... or if -H: */ || (option_mask32 & OPT_H) /* ... or if -L, but my_stat always follows links if -L */ ); argv++; if (!cur) continue; /*cur->fname_allocated = 0; - already is */ cur->dn_next = dn; dn = cur; nfiles++; } while (*argv); /* nfiles _may_ be 0 here - try "ls doesnt_exist" */ if (nfiles == 0) return G.exit_code; /* now that we know how many files there are * allocate memory for an array to hold dnode pointers */ dnp = dnalloc(nfiles); for (i = 0; /* i < nfiles - detected via !dn below */; i++) { dnp[i] = dn; /* save pointer to node in array */ dn = dn->dn_next; if (!dn) break; } if (G.all_fmt & DISP_NOLIST) { sort_and_display_files(dnp, nfiles); } else { dnd = splitdnarray(dnp, SPLIT_DIR); dnf = splitdnarray(dnp, SPLIT_FILE); dndirs = count_dirs(dnp, SPLIT_DIR); dnfiles = nfiles - dndirs; if (dnfiles > 0) { sort_and_display_files(dnf, dnfiles); if (ENABLE_FEATURE_CLEAN_UP) free(dnf); } if (dndirs > 0) { dnsort(dnd, dndirs); scan_and_display_dirs_recur(dnd, dnfiles == 0); if (ENABLE_FEATURE_CLEAN_UP) free(dnd); } } if (ENABLE_FEATURE_CLEAN_UP) dfree(dnp); return G.exit_code; } busybox-1.22.1/coreutils/expr.c0000644000000000000000000002673412263563520015141 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini expr implementation for busybox * * based on GNU expr Mike Parker. * Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc. * * Busybox modifications * Copyright (c) 2000 Edward Betts . * Copyright (C) 2003-2005 Vladimir Oleynik * - reduced 464 bytes. * - 64 math support * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* This program evaluates expressions. Each token (operator, operand, * parenthesis) of the expression must be a separate argument. The * parser used is a reasonably general one, though any incarnation of * it is language-specific. It is especially nice for expressions. * * No parse tree is needed; a new node is evaluated immediately. * One function can handle multiple operators all of equal precedence, * provided they all associate ((x op x) op x). */ /* no getopt needed */ //usage:#define expr_trivial_usage //usage: "EXPRESSION" //usage:#define expr_full_usage "\n\n" //usage: "Print the value of EXPRESSION to stdout\n" //usage: "\n" //usage: "EXPRESSION may be:\n" //usage: " ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n" //usage: " ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n" //usage: " ARG1 < ARG2 1 if ARG1 is less than ARG2, else 0. Similarly:\n" //usage: " ARG1 <= ARG2\n" //usage: " ARG1 = ARG2\n" //usage: " ARG1 != ARG2\n" //usage: " ARG1 >= ARG2\n" //usage: " ARG1 > ARG2\n" //usage: " ARG1 + ARG2 Sum of ARG1 and ARG2. Similarly:\n" //usage: " ARG1 - ARG2\n" //usage: " ARG1 * ARG2\n" //usage: " ARG1 / ARG2\n" //usage: " ARG1 % ARG2\n" //usage: " STRING : REGEXP Anchored pattern match of REGEXP in STRING\n" //usage: " match STRING REGEXP Same as STRING : REGEXP\n" //usage: " substr STRING POS LENGTH Substring of STRING, POS counted from 1\n" //usage: " index STRING CHARS Index in STRING where any CHARS is found, or 0\n" //usage: " length STRING Length of STRING\n" //usage: " quote TOKEN Interpret TOKEN as a string, even if\n" //usage: " it is a keyword like 'match' or an\n" //usage: " operator like '/'\n" //usage: " (EXPRESSION) Value of EXPRESSION\n" //usage: "\n" //usage: "Beware that many operators need to be escaped or quoted for shells.\n" //usage: "Comparisons are arithmetic if both ARGs are numbers, else\n" //usage: "lexicographical. Pattern matches return the string matched between\n" //usage: "\\( and \\) or null; if \\( and \\) are not used, they return the number\n" //usage: "of characters matched or 0." #include "libbb.h" #include "xregex.h" #if ENABLE_EXPR_MATH_SUPPORT_64 typedef int64_t arith_t; #define PF_REZ "ll" #define PF_REZ_TYPE (long long) #define STRTOL(s, e, b) strtoll(s, e, b) #else typedef long arith_t; #define PF_REZ "l" #define PF_REZ_TYPE (long) #define STRTOL(s, e, b) strtol(s, e, b) #endif /* TODO: use bb_strtol[l]? It's easier to check for errors... */ /* The kinds of value we can have. */ enum { INTEGER, STRING }; /* A value is.... */ struct valinfo { smallint type; /* Which kind. */ union { /* The value itself. */ arith_t i; char *s; } u; }; typedef struct valinfo VALUE; /* The arguments given to the program, minus the program name. */ struct globals { char **args; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { } while (0) /* forward declarations */ static VALUE *eval(void); /* Return a VALUE for I. */ static VALUE *int_value(arith_t i) { VALUE *v; v = xzalloc(sizeof(VALUE)); if (INTEGER) /* otherwise xzaaloc did it already */ v->type = INTEGER; v->u.i = i; return v; } /* Return a VALUE for S. */ static VALUE *str_value(const char *s) { VALUE *v; v = xzalloc(sizeof(VALUE)); if (STRING) /* otherwise xzaaloc did it already */ v->type = STRING; v->u.s = xstrdup(s); return v; } /* Free VALUE V, including structure components. */ static void freev(VALUE *v) { if (v->type == STRING) free(v->u.s); free(v); } /* Return nonzero if V is a null-string or zero-number. */ static int null(VALUE *v) { if (v->type == INTEGER) return v->u.i == 0; /* STRING: */ return v->u.s[0] == '\0' || LONE_CHAR(v->u.s, '0'); } /* Coerce V to a STRING value (can't fail). */ static void tostring(VALUE *v) { if (v->type == INTEGER) { v->u.s = xasprintf("%" PF_REZ "d", PF_REZ_TYPE v->u.i); v->type = STRING; } } /* Coerce V to an INTEGER value. Return 1 on success, 0 on failure. */ static bool toarith(VALUE *v) { if (v->type == STRING) { arith_t i; char *e; /* Don't interpret the empty string as an integer. */ /* Currently does not worry about overflow or int/long differences. */ i = STRTOL(v->u.s, &e, 10); if ((v->u.s == e) || *e) return 0; free(v->u.s); v->u.i = i; v->type = INTEGER; } return 1; } /* Return str[0]+str[1] if the next token matches STR exactly. STR must not be NULL. */ static int nextarg(const char *str) { if (*G.args == NULL || strcmp(*G.args, str) != 0) return 0; return (unsigned char)str[0] + (unsigned char)str[1]; } /* The comparison operator handling functions. */ static int cmp_common(VALUE *l, VALUE *r, int op) { arith_t ll, rr; ll = l->u.i; rr = r->u.i; if (l->type == STRING || r->type == STRING) { tostring(l); tostring(r); ll = strcmp(l->u.s, r->u.s); rr = 0; } /* calculating ll - rr and checking the result is prone to overflows. * We'll do it differently: */ if (op == '<') return ll < rr; if (op == ('<' + '=')) return ll <= rr; if (op == '=' || (op == '=' + '=')) return ll == rr; if (op == '!' + '=') return ll != rr; if (op == '>') return ll > rr; /* >= */ return ll >= rr; } /* The arithmetic operator handling functions. */ static arith_t arithmetic_common(VALUE *l, VALUE *r, int op) { arith_t li, ri; if (!toarith(l) || !toarith(r)) bb_error_msg_and_die("non-numeric argument"); li = l->u.i; ri = r->u.i; if (op == '+') return li + ri; if (op == '-') return li - ri; if (op == '*') return li * ri; if (ri == 0) bb_error_msg_and_die("division by zero"); if (op == '/') return li / ri; return li % ri; } /* Do the : operator. SV is the VALUE for the lhs (the string), PV is the VALUE for the rhs (the pattern). */ static VALUE *docolon(VALUE *sv, VALUE *pv) { enum { NMATCH = 2 }; VALUE *v; regex_t re_buffer; regmatch_t re_regs[NMATCH]; tostring(sv); tostring(pv); if (pv->u.s[0] == '^') { bb_error_msg( "warning: '%s': using '^' as the first character\n" "of a basic regular expression is not portable; it is ignored", pv->u.s); } memset(&re_buffer, 0, sizeof(re_buffer)); memset(re_regs, 0, sizeof(re_regs)); xregcomp(&re_buffer, pv->u.s, 0); /* expr uses an anchored pattern match, so check that there was a * match and that the match starts at offset 0. */ if (regexec(&re_buffer, sv->u.s, NMATCH, re_regs, 0) != REG_NOMATCH && re_regs[0].rm_so == 0 ) { /* Were \(...\) used? */ if (re_buffer.re_nsub > 0 && re_regs[1].rm_so >= 0) { sv->u.s[re_regs[1].rm_eo] = '\0'; v = str_value(sv->u.s + re_regs[1].rm_so); } else { v = int_value(re_regs[0].rm_eo); } } else { /* Match failed -- return the right kind of null. */ if (re_buffer.re_nsub > 0) v = str_value(""); else v = int_value(0); } regfree(&re_buffer); return v; } /* Handle bare operands and ( expr ) syntax. */ static VALUE *eval7(void) { VALUE *v; if (!*G.args) bb_error_msg_and_die("syntax error"); if (nextarg("(")) { G.args++; v = eval(); if (!nextarg(")")) bb_error_msg_and_die("syntax error"); G.args++; return v; } if (nextarg(")")) bb_error_msg_and_die("syntax error"); return str_value(*G.args++); } /* Handle match, substr, index, length, and quote keywords. */ static VALUE *eval6(void) { static const char keywords[] ALIGN1 = "quote\0""length\0""match\0""index\0""substr\0"; VALUE *r, *i1, *i2; VALUE *l = l; /* silence gcc */ VALUE *v = v; /* silence gcc */ int key = *G.args ? index_in_strings(keywords, *G.args) + 1 : 0; if (key == 0) /* not a keyword */ return eval7(); G.args++; /* We have a valid token, so get the next argument. */ if (key == 1) { /* quote */ if (!*G.args) bb_error_msg_and_die("syntax error"); return str_value(*G.args++); } if (key == 2) { /* length */ r = eval6(); tostring(r); v = int_value(strlen(r->u.s)); freev(r); } else l = eval6(); if (key == 3) { /* match */ r = eval6(); v = docolon(l, r); freev(l); freev(r); } if (key == 4) { /* index */ r = eval6(); tostring(l); tostring(r); v = int_value(strcspn(l->u.s, r->u.s) + 1); if (v->u.i == (arith_t) strlen(l->u.s) + 1) v->u.i = 0; freev(l); freev(r); } if (key == 5) { /* substr */ i1 = eval6(); i2 = eval6(); tostring(l); if (!toarith(i1) || !toarith(i2) || i1->u.i > (arith_t) strlen(l->u.s) || i1->u.i <= 0 || i2->u.i <= 0) v = str_value(""); else { v = xmalloc(sizeof(VALUE)); v->type = STRING; v->u.s = xstrndup(l->u.s + i1->u.i - 1, i2->u.i); } freev(l); freev(i1); freev(i2); } return v; } /* Handle : operator (pattern matching). Calls docolon to do the real work. */ static VALUE *eval5(void) { VALUE *l, *r, *v; l = eval6(); while (nextarg(":")) { G.args++; r = eval6(); v = docolon(l, r); freev(l); freev(r); l = v; } return l; } /* Handle *, /, % operators. */ static VALUE *eval4(void) { VALUE *l, *r; int op; arith_t val; l = eval5(); while (1) { op = nextarg("*"); if (!op) { op = nextarg("/"); if (!op) { op = nextarg("%"); if (!op) return l; }} G.args++; r = eval5(); val = arithmetic_common(l, r, op); freev(l); freev(r); l = int_value(val); } } /* Handle +, - operators. */ static VALUE *eval3(void) { VALUE *l, *r; int op; arith_t val; l = eval4(); while (1) { op = nextarg("+"); if (!op) { op = nextarg("-"); if (!op) return l; } G.args++; r = eval4(); val = arithmetic_common(l, r, op); freev(l); freev(r); l = int_value(val); } } /* Handle comparisons. */ static VALUE *eval2(void) { VALUE *l, *r; int op; arith_t val; l = eval3(); while (1) { op = nextarg("<"); if (!op) { op = nextarg("<="); if (!op) { op = nextarg("="); if (!op) { op = nextarg("=="); if (!op) { op = nextarg("!="); if (!op) { op = nextarg(">="); if (!op) { op = nextarg(">"); if (!op) return l; }}}}}} G.args++; r = eval3(); toarith(l); toarith(r); val = cmp_common(l, r, op); freev(l); freev(r); l = int_value(val); } } /* Handle &. */ static VALUE *eval1(void) { VALUE *l, *r; l = eval2(); while (nextarg("&")) { G.args++; r = eval2(); if (null(l) || null(r)) { freev(l); freev(r); l = int_value(0); } else freev(r); } return l; } /* Handle |. */ static VALUE *eval(void) { VALUE *l, *r; l = eval1(); while (nextarg("|")) { G.args++; r = eval1(); if (null(l)) { freev(l); l = r; } else freev(r); } return l; } int expr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int expr_main(int argc UNUSED_PARAM, char **argv) { VALUE *v; INIT_G(); xfunc_error_retval = 2; /* coreutils compat */ G.args = argv + 1; if (*G.args == NULL) { bb_error_msg_and_die("too few arguments"); } v = eval(); if (*G.args) bb_error_msg_and_die("syntax error"); if (v->type == INTEGER) printf("%" PF_REZ "d\n", PF_REZ_TYPE v->u.i); else puts(v->u.s); fflush_stdout_and_exit(null(v)); } busybox-1.22.1/coreutils/chown.c0000644000000000000000000001416712267106022015270 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini chown implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 defects - none? */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */ //usage:#define chown_trivial_usage //usage: "[-RhLHP"IF_DESKTOP("cvf")"]... OWNER[<.|:>[GROUP]] FILE..." //usage:#define chown_full_usage "\n\n" //usage: "Change the owner and/or group of each FILE to OWNER and/or GROUP\n" //usage: "\n -R Recurse" //usage: "\n -h Affect symlinks instead of symlink targets" //usage: "\n -L Traverse all symlinks to directories" //usage: "\n -H Traverse symlinks on command line only" //usage: "\n -P Don't traverse symlinks (default)" //usage: IF_DESKTOP( //usage: "\n -c List changed files" //usage: "\n -v List all files" //usage: "\n -f Hide errors" //usage: ) //usage: //usage:#define chown_example_usage //usage: "$ ls -l /tmp/foo\n" //usage: "-r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo\n" //usage: "$ chown root /tmp/foo\n" //usage: "$ ls -l /tmp/foo\n" //usage: "-r--r--r-- 1 root andersen 0 Apr 12 18:25 /tmp/foo\n" //usage: "$ chown root.root /tmp/foo\n" //usage: "ls -l /tmp/foo\n" //usage: "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n" #include "libbb.h" /* This is a NOEXEC applet. Be very careful! */ #define OPT_STR ("Rh" IF_DESKTOP("vcfLHP")) #define BIT_RECURSE 1 #define OPT_RECURSE (opt & 1) #define OPT_NODEREF (opt & 2) #define OPT_VERBOSE (IF_DESKTOP(opt & 0x04) IF_NOT_DESKTOP(0)) #define OPT_CHANGED (IF_DESKTOP(opt & 0x08) IF_NOT_DESKTOP(0)) #define OPT_QUIET (IF_DESKTOP(opt & 0x10) IF_NOT_DESKTOP(0)) /* POSIX options * -L traverse every symbolic link to a directory encountered * -H if a command line argument is a symbolic link to a directory, traverse it * -P do not traverse any symbolic links (default) * We do not conform to the following: * "Specifying more than one of -H, -L, and -P is not an error. * The last option specified shall determine the behavior of the utility." */ /* -L */ #define BIT_TRAVERSE 0x20 #define OPT_TRAVERSE (IF_DESKTOP(opt & BIT_TRAVERSE) IF_NOT_DESKTOP(0)) /* -H or -L */ #define BIT_TRAVERSE_TOP (0x20|0x40) #define OPT_TRAVERSE_TOP (IF_DESKTOP(opt & BIT_TRAVERSE_TOP) IF_NOT_DESKTOP(0)) #if ENABLE_FEATURE_CHOWN_LONG_OPTIONS static const char chown_longopts[] ALIGN1 = "recursive\0" No_argument "R" "dereference\0" No_argument "\xff" "no-dereference\0" No_argument "h" # if ENABLE_DESKTOP "changes\0" No_argument "c" "silent\0" No_argument "f" "quiet\0" No_argument "f" "verbose\0" No_argument "v" # endif ; #endif typedef int (*chown_fptr)(const char *, uid_t, gid_t); struct param_t { struct bb_uidgid_t ugid; chown_fptr chown_func; }; static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void *vparam, int depth UNUSED_PARAM) { #define param (*(struct param_t*)vparam) #define opt option_mask32 uid_t u = (param.ugid.uid == (uid_t)-1L) ? statbuf->st_uid : param.ugid.uid; gid_t g = (param.ugid.gid == (gid_t)-1L) ? statbuf->st_gid : param.ugid.gid; if (param.chown_func(fileName, u, g) == 0) { if (OPT_VERBOSE || (OPT_CHANGED && (statbuf->st_uid != u || statbuf->st_gid != g)) ) { printf("changed ownership of '%s' to %u:%u\n", fileName, (unsigned)u, (unsigned)g); } return TRUE; } if (!OPT_QUIET) bb_simple_perror_msg(fileName); return FALSE; #undef opt #undef param } int chown_main(int argc UNUSED_PARAM, char **argv) { int retval = EXIT_SUCCESS; int opt, flags; struct param_t param; /* Just -1 might not work: uid_t may be unsigned long */ param.ugid.uid = -1L; param.ugid.gid = -1L; #if ENABLE_FEATURE_CHOWN_LONG_OPTIONS applet_long_options = chown_longopts; #endif opt_complementary = "-2"; opt = getopt32(argv, OPT_STR); argv += optind; /* This matches coreutils behavior (almost - see below) */ param.chown_func = chown; if (OPT_NODEREF /* || (OPT_RECURSE && !OPT_TRAVERSE_TOP): */ IF_DESKTOP( || (opt & (BIT_RECURSE|BIT_TRAVERSE_TOP)) == BIT_RECURSE) ) { param.chown_func = lchown; } flags = ACTION_DEPTHFIRST; /* match coreutils order */ if (OPT_RECURSE) flags |= ACTION_RECURSE; if (OPT_TRAVERSE_TOP) flags |= ACTION_FOLLOWLINKS_L0; /* -H/-L: follow links on depth 0 */ if (OPT_TRAVERSE) flags |= ACTION_FOLLOWLINKS; /* follow links if -L */ parse_chown_usergroup_or_die(¶m.ugid, argv[0]); /* Ok, ready to do the deed now */ while (*++argv) { if (!recursive_action(*argv, flags, /* flags */ fileAction, /* file action */ fileAction, /* dir action */ ¶m, /* user data */ 0) /* depth */ ) { retval = EXIT_FAILURE; } } return retval; } /* Testcase. Run in empty directory. #!/bin/sh t1="/tmp/busybox chown" t2="/usr/bin/chown" create() { rm -rf $1; mkdir $1 ( cd $1 || exit 1 mkdir dir dir2 >up >file >dir/file >dir2/file ln -s dir linkdir ln -s file linkfile ln -s ../up dir/linkup ln -s ../dir2 dir/linkupdir2 ) chown -R 0:0 $1 } tst() { create test1 create test2 echo "[$1]" >>test1.out echo "[$1]" >>test2.out (cd test1; $t1 $1) >>test1.out 2>&1 (cd test2; $t2 $1) >>test2.out 2>&1 (cd test1; ls -lnR) >out1 (cd test2; ls -lnR) >out2 echo "chown $1" >out.diff if ! diff -u out1 out2 >>out.diff; then exit 1; fi rm out.diff } tst_for_each() { tst "$1 1:1 file" tst "$1 1:1 dir" tst "$1 1:1 linkdir" tst "$1 1:1 linkfile" } echo "If script produced 'out.diff' file, then at least one testcase failed" >test1.out >test2.out # These match coreutils 6.8: tst_for_each "-v" tst_for_each "-vR" tst_for_each "-vRP" tst_for_each "-vRL" tst_for_each "-vRH" tst_for_each "-vh" tst_for_each "-vhR" tst_for_each "-vhRP" tst_for_each "-vhRL" tst_for_each "-vhRH" # Fix `name' in coreutils output sed 's/`/'"'"'/g' -i test2.out # Compare us with coreutils output diff -u test1.out test2.out */ busybox-1.22.1/coreutils/rmdir.c0000644000000000000000000000430312263563520015264 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * rmdir implementation for busybox * * Copyright (C) 2003 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/rmdir.html */ //usage:#define rmdir_trivial_usage //usage: "[OPTIONS] DIRECTORY..." //usage:#define rmdir_full_usage "\n\n" //usage: "Remove DIRECTORY if it is empty\n" //usage: IF_FEATURE_RMDIR_LONG_OPTIONS( //usage: "\n -p|--parents Include parents" //usage: "\n --ignore-fail-on-non-empty" //usage: ) //usage: IF_NOT_FEATURE_RMDIR_LONG_OPTIONS( //usage: "\n -p Include parents" //usage: ) //usage: //usage:#define rmdir_example_usage //usage: "# rmdir /tmp/foo\n" #include "libbb.h" /* This is a NOFORK applet. Be very careful! */ #define PARENTS (1 << 0) //efine VERBOSE (1 << 1) //accepted but ignored #define IGNORE_NON_EMPTY (1 << 2) int rmdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int rmdir_main(int argc UNUSED_PARAM, char **argv) { int status = EXIT_SUCCESS; int flags; char *path; #if ENABLE_FEATURE_RMDIR_LONG_OPTIONS static const char rmdir_longopts[] ALIGN1 = "parents\0" No_argument "p" "verbose\0" No_argument "v" /* Debian etch: many packages fail to be purged or installed * because they desperately want this option: */ "ignore-fail-on-non-empty\0" No_argument "\xff" ; applet_long_options = rmdir_longopts; #endif flags = getopt32(argv, "pv"); argv += optind; if (!*argv) { bb_show_usage(); } do { path = *argv; while (1) { if (rmdir(path) < 0) { #if ENABLE_FEATURE_RMDIR_LONG_OPTIONS if ((flags & IGNORE_NON_EMPTY) && errno == ENOTEMPTY) break; #endif bb_perror_msg("'%s'", path); /* Match gnu rmdir msg. */ status = EXIT_FAILURE; } else if (flags & PARENTS) { /* Note: path was not "" since rmdir succeeded. */ path = dirname(path); /* Path is now just the parent component. Dirname * returns "." if there are no parents. */ if (NOT_LONE_CHAR(path, '.')) { continue; } } break; } } while (*++argv); return status; } busybox-1.22.1/coreutils/head.c0000644000000000000000000001246712263563520015062 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * head implementation for busybox * * Copyright (C) 2003 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/head.html */ //kbuild:lib-$(CONFIG_HEAD) += head.o //usage:#define head_trivial_usage //usage: "[OPTIONS] [FILE]..." //usage:#define head_full_usage "\n\n" //usage: "Print first 10 lines of each FILE (or stdin) to stdout.\n" //usage: "With more than one FILE, precede each with a filename header.\n" //usage: "\n -n N[kbm] Print first N lines" //usage: IF_FEATURE_FANCY_HEAD( //usage: "\n -n -N[kbm] Print all except N last lines" //usage: "\n -c [-]N[kbm] Print first N bytes" //usage: "\n -q Never print headers" //usage: "\n -v Always print headers" //usage: ) //usage: "\n" //usage: "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)." //usage: //usage:#define head_example_usage //usage: "$ head -n 2 /etc/passwd\n" //usage: "root:x:0:0:root:/root:/bin/bash\n" //usage: "daemon:x:1:1:daemon:/usr/sbin:/bin/sh\n" #include "libbb.h" /* This is a NOEXEC applet. Be very careful! */ #if !ENABLE_FEATURE_FANCY_HEAD # define print_first_N(fp,count,bytes) print_first_N(fp,count) #endif static void print_first_N(FILE *fp, unsigned long count, bool count_bytes) { #if !ENABLE_FEATURE_FANCY_HEAD const int count_bytes = 0; #endif while (count) { int c = getc(fp); if (c == EOF) break; if (count_bytes || (c == '\n')) --count; putchar(c); } } #if ENABLE_FEATURE_FANCY_HEAD static void print_except_N_last_bytes(FILE *fp, unsigned count) { unsigned char *circle = xmalloc(++count); unsigned head = 0; for(;;) { int c; c = getc(fp); if (c == EOF) goto ret; circle[head++] = c; if (head == count) break; } for (;;) { int c; if (head == count) head = 0; putchar(circle[head]); c = getc(fp); if (c == EOF) goto ret; circle[head] = c; head++; } ret: free(circle); } static void print_except_N_last_lines(FILE *fp, unsigned count) { char **circle = xzalloc((++count) * sizeof(circle[0])); unsigned head = 0; for(;;) { char *c; c = xmalloc_fgets(fp); if (!c) goto ret; circle[head++] = c; if (head == count) break; } for (;;) { char *c; if (head == count) head = 0; fputs(circle[head], stdout); c = xmalloc_fgets(fp); if (!c) goto ret; free(circle[head]); circle[head++] = c; } ret: head = 0; for(;;) { free(circle[head++]); if (head == count) break; } free(circle); } #else /* Must never be called */ void print_except_N_last_bytes(FILE *fp, unsigned count); void print_except_N_last_lines(FILE *fp, unsigned count); #endif #if !ENABLE_FEATURE_FANCY_HEAD # define eat_num(negative_N,p) eat_num(p) #endif static unsigned long eat_num(bool *negative_N, const char *p) { #if ENABLE_FEATURE_FANCY_HEAD if (*p == '-') { *negative_N = 1; p++; } #endif return xatoul_sfx(p, bkm_suffixes); } static const char head_opts[] ALIGN1 = "n:" #if ENABLE_FEATURE_FANCY_HEAD "c:qv" #endif ; #define header_fmt_str "\n==> %s <==\n" int head_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int head_main(int argc, char **argv) { unsigned long count = 10; #if ENABLE_FEATURE_FANCY_HEAD int header_threshhold = 1; bool count_bytes = 0; bool negative_N = 0; #else # define header_threshhold 1 # define count_bytes 0 # define negative_N 0 #endif FILE *fp; const char *fmt; char *p; int opt; int retval = EXIT_SUCCESS; #if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD /* Allow legacy syntax of an initial numeric option without -n. */ if (argv[1] && argv[1][0] == '-' && isdigit(argv[1][1]) ) { --argc; ++argv; p = argv[0] + 1; goto GET_COUNT; } #endif /* No size benefit in converting this to getopt32 */ while ((opt = getopt(argc, argv, head_opts)) > 0) { switch (opt) { #if ENABLE_FEATURE_FANCY_HEAD case 'q': header_threshhold = INT_MAX; break; case 'v': header_threshhold = -1; break; case 'c': count_bytes = 1; /* fall through */ #endif case 'n': p = optarg; #if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD GET_COUNT: #endif count = eat_num(&negative_N, p); break; default: bb_show_usage(); } } argc -= optind; argv += optind; if (!*argv) *--argv = (char*)"-"; fmt = header_fmt_str + 1; if (argc <= header_threshhold) { #if ENABLE_FEATURE_FANCY_HEAD header_threshhold = 0; #else fmt += 11; /* "" */ #endif } if (negative_N) { if (count >= INT_MAX / sizeof(char*)) bb_error_msg("count is too big: %lu", count); } do { fp = fopen_or_warn_stdin(*argv); if (fp) { if (fp == stdin) { *argv = (char *) bb_msg_standard_input; } if (header_threshhold) { printf(fmt, *argv); } if (negative_N) { if (count_bytes) { print_except_N_last_bytes(fp, count); } else { print_except_N_last_lines(fp, count); } } else { print_first_N(fp, count, count_bytes); } die_if_ferror_stdout(); if (fclose_if_not_stdin(fp)) { bb_simple_perror_msg(*argv); retval = EXIT_FAILURE; } } else { retval = EXIT_FAILURE; } fmt = header_fmt_str; } while (*++argv); fflush_stdout_and_exit(retval); } busybox-1.22.1/coreutils/chmod.c0000644000000000000000000001163312263563520015245 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini chmod implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * * Reworked by (C) 2002 Vladimir Oleynik * to correctly parse '-rwxgoa' * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* BB_AUDIT GNU defects - unsupported long options. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */ //usage:#define chmod_trivial_usage //usage: "[-R"IF_DESKTOP("cvf")"] MODE[,MODE]... FILE..." //usage:#define chmod_full_usage "\n\n" //usage: "Each MODE is one or more of the letters ugoa, one of the\n" //usage: "symbols +-= and one or more of the letters rwxst\n" //usage: "\n -R Recurse" //usage: IF_DESKTOP( //usage: "\n -c List changed files" //usage: "\n -v List all files" //usage: "\n -f Hide errors" //usage: ) //usage: //usage:#define chmod_example_usage //usage: "$ ls -l /tmp/foo\n" //usage: "-rw-rw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n" //usage: "$ chmod u+x /tmp/foo\n" //usage: "$ ls -l /tmp/foo\n" //usage: "-rwxrw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo*\n" //usage: "$ chmod 444 /tmp/foo\n" //usage: "$ ls -l /tmp/foo\n" //usage: "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n" #include "libbb.h" /* This is a NOEXEC applet. Be very careful! */ #define OPT_RECURSE (option_mask32 & 1) #define OPT_VERBOSE (IF_DESKTOP(option_mask32 & 2) IF_NOT_DESKTOP(0)) #define OPT_CHANGED (IF_DESKTOP(option_mask32 & 4) IF_NOT_DESKTOP(0)) #define OPT_QUIET (IF_DESKTOP(option_mask32 & 8) IF_NOT_DESKTOP(0)) #define OPT_STR "R" IF_DESKTOP("vcf") /* coreutils: * chmod never changes the permissions of symbolic links; the chmod * system call cannot change their permissions. This is not a problem * since the permissions of symbolic links are never used. * However, for each symbolic link listed on the command line, chmod changes * the permissions of the pointed-to file. In contrast, chmod ignores * symbolic links encountered during recursive directory traversals. */ static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void* param, int depth) { mode_t newmode; /* match coreutils behavior */ if (depth == 0) { /* statbuf holds lstat result, but we need stat (follow link) */ if (stat(fileName, statbuf)) goto err; } else { /* depth > 0: skip links */ if (S_ISLNK(statbuf->st_mode)) return TRUE; } newmode = statbuf->st_mode; if (!bb_parse_mode((char *)param, &newmode)) bb_error_msg_and_die("invalid mode '%s'", (char *)param); if (chmod(fileName, newmode) == 0) { if (OPT_VERBOSE || (OPT_CHANGED && statbuf->st_mode != newmode) ) { printf("mode of '%s' changed to %04o (%s)\n", fileName, newmode & 07777, bb_mode_string(newmode)+1); } return TRUE; } err: if (!OPT_QUIET) bb_simple_perror_msg(fileName); return FALSE; } int chmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int chmod_main(int argc UNUSED_PARAM, char **argv) { int retval = EXIT_SUCCESS; char *arg, **argp; char *smode; /* Convert first encountered -r into ar, -w into aw etc * so that getopt would not eat it */ argp = argv; while ((arg = *++argp)) { /* Mode spec must be the first arg (sans -R etc) */ /* (protect against mishandling e.g. "chmod 644 -r") */ if (arg[0] != '-') { arg = NULL; break; } /* An option. Not a -- or valid option? */ if (arg[1] && !strchr("-"OPT_STR, arg[1])) { arg[0] = 'a'; break; } } /* Parse options */ opt_complementary = "-2"; getopt32(argv, ("-"OPT_STR) + 1); /* Reuse string */ argv += optind; /* Restore option-like mode if needed */ if (arg) arg[0] = '-'; /* Ok, ready to do the deed now */ smode = *argv++; do { if (!recursive_action(*argv, OPT_RECURSE, // recurse fileAction, // file action fileAction, // dir action smode, // user data 0) // depth ) { retval = EXIT_FAILURE; } } while (*++argv); return retval; } /* Security: chmod is too important and too subtle. This is a test script (busybox chmod versus coreutils). Run it in empty directory. #!/bin/sh t1="/tmp/busybox chmod" t2="/usr/bin/chmod" create() { rm -rf $1; mkdir $1 ( cd $1 || exit 1 mkdir dir >up >file >dir/file ln -s dir linkdir ln -s file linkfile ln -s ../up dir/up ) } tst() { (cd test1; $t1 $1) (cd test2; $t2 $1) (cd test1; ls -lR) >out1 (cd test2; ls -lR) >out2 echo "chmod $1" >out.diff if ! diff -u out1 out2 >>out.diff; then exit 1; fi rm out.diff } echo "If script produced 'out.diff' file, then at least one testcase failed" create test1; create test2 tst "a+w file" tst "a-w dir" tst "a+w linkfile" tst "a-w linkdir" tst "-R a+w file" tst "-R a-w dir" tst "-R a+w linkfile" tst "-R a-w linkdir" tst "a-r,a+x linkfile" */ busybox-1.22.1/coreutils/mkfifo.c0000644000000000000000000000215712263563520015427 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * mkfifo implementation for busybox * * Copyright (C) 2003 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 compliant */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/mkfifo.html */ //usage:#define mkfifo_trivial_usage //usage: "[-m MODE] " IF_SELINUX("[-Z] ") "NAME" //usage:#define mkfifo_full_usage "\n\n" //usage: "Create named pipe\n" //usage: "\n -m MODE Mode (default a=rw)" //usage: IF_SELINUX( //usage: "\n -Z Set security context" //usage: ) #include "libbb.h" #include "libcoreutils/coreutils.h" /* This is a NOEXEC applet. Be very careful! */ int mkfifo_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mkfifo_main(int argc UNUSED_PARAM, char **argv) { mode_t mode; int retval = EXIT_SUCCESS; mode = getopt_mk_fifo_nod(argv); argv += optind; if (!*argv) { bb_show_usage(); } do { if (mkfifo(*argv, mode) < 0) { bb_simple_perror_msg(*argv); /* Avoid multibyte problems. */ retval = EXIT_FAILURE; } } while (*++argv); return retval; } busybox-1.22.1/debianutils/0000755000000000000000000000000012320365363014274 5ustar rootrootbusybox-1.22.1/debianutils/run_parts.c0000644000000000000000000001314312263563520016460 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini run-parts implementation for busybox * * Copyright (C) 2007 Bernhard Reutner-Fischer * * Based on a older version that was in busybox which was 1k big. * Copyright (C) 2001 by Emanuele Aina * * Based on the Debian run-parts program, version 1.15 * Copyright (C) 1996 Jeff Noxon , * Copyright (C) 1996-1999 Guy Maor * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* This is my first attempt to write a program in C (well, this is my first * attempt to write a program! :-) . */ /* This piece of code is heavily based on the original version of run-parts, * taken from debian-utils. I've only removed the long options and the * report mode. As the original run-parts support only long options, I've * broken compatibility because the BusyBox policy doesn't allow them. */ //usage:#define run_parts_trivial_usage //usage: "[-a ARG]... [-u UMASK] " //usage: IF_FEATURE_RUN_PARTS_LONG_OPTIONS("[--reverse] [--test] [--exit-on-error] "IF_FEATURE_RUN_PARTS_FANCY("[--list] ")) //usage: "DIRECTORY" //usage:#define run_parts_full_usage "\n\n" //usage: "Run a bunch of scripts in DIRECTORY\n" //usage: "\n -a ARG Pass ARG as argument to scripts" //usage: "\n -u UMASK Set UMASK before running scripts" //usage: IF_FEATURE_RUN_PARTS_LONG_OPTIONS( //usage: "\n --reverse Reverse execution order" //usage: "\n --test Dry run" //usage: "\n --exit-on-error Exit if a script exits with non-zero" //usage: IF_FEATURE_RUN_PARTS_FANCY( //usage: "\n --list Print names of matching files even if they are not executable" //usage: ) //usage: ) //usage: //usage:#define run_parts_example_usage //usage: "$ run-parts -a start /etc/init.d\n" //usage: "$ run-parts -a stop=now /etc/init.d\n\n" //usage: "Let's assume you have a script foo/dosomething:\n" //usage: "#!/bin/sh\n" //usage: "for i in $*; do eval $i; done; unset i\n" //usage: "case \"$1\" in\n" //usage: "start*) echo starting something;;\n" //usage: "stop*) set -x; shutdown -h $stop;;\n" //usage: "esac\n\n" //usage: "Running this yields:\n" //usage: "$run-parts -a stop=+4m foo/\n" //usage: "+ shutdown -h +4m" #include "libbb.h" struct globals { char **names; int cur; char *cmd[2 /* using 1 provokes compiler warning */]; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define names (G.names) #define cur (G.cur ) #define cmd (G.cmd ) #define INIT_G() do { } while (0) enum { NUM_CMD = (COMMON_BUFSIZE - sizeof(G)) / sizeof(cmd[0]) - 1 }; enum { OPT_a = (1 << 0), OPT_u = (1 << 1), OPT_r = (1 << 2) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS, OPT_t = (1 << 3) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS, OPT_e = (1 << 4) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS, OPT_l = (1 << 5) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS * ENABLE_FEATURE_RUN_PARTS_FANCY, }; /* Is this a valid filename (upper/lower alpha, digits, * underscores, and hyphens only?) */ static bool invalid_name(const char *c) { c = bb_basename(c); while (*c && (isalnum(*c) || *c == '_' || *c == '-')) c++; return *c; /* TRUE (!0) if terminating NUL is not reached */ } static int bb_alphasort(const void *p1, const void *p2) { int r = strcmp(*(char **) p1, *(char **) p2); return (option_mask32 & OPT_r) ? -r : r; } static int FAST_FUNC act(const char *file, struct stat *statbuf, void *args UNUSED_PARAM, int depth) { if (depth == 1) return TRUE; if (depth == 2 && ( !(statbuf->st_mode & (S_IFREG | S_IFLNK)) || invalid_name(file) || (!(option_mask32 & OPT_l) && access(file, X_OK) != 0)) ) { return SKIP; } names = xrealloc_vector(names, 4, cur); names[cur++] = xstrdup(file); /*names[cur] = NULL; - xrealloc_vector did it */ return TRUE; } #if ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS static const char runparts_longopts[] ALIGN1 = "arg\0" Required_argument "a" "umask\0" Required_argument "u" //TODO: "verbose\0" No_argument "v" "reverse\0" No_argument "\xf0" "test\0" No_argument "\xf1" "exit-on-error\0" No_argument "\xf2" #if ENABLE_FEATURE_RUN_PARTS_FANCY "list\0" No_argument "\xf3" #endif ; #endif int run_parts_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int run_parts_main(int argc UNUSED_PARAM, char **argv) { const char *umask_p = "22"; llist_t *arg_list = NULL; unsigned n; int ret; INIT_G(); #if ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS applet_long_options = runparts_longopts; #endif /* We require exactly one argument: the directory name */ opt_complementary = "=1:a::"; getopt32(argv, "a:u:", &arg_list, &umask_p); umask(xstrtou_range(umask_p, 8, 0, 07777)); n = 1; while (arg_list && n < NUM_CMD) { cmd[n++] = llist_pop(&arg_list); } /* cmd[n] = NULL; - is already zeroed out */ /* run-parts has to sort executables by name before running them */ recursive_action(argv[optind], ACTION_RECURSE|ACTION_FOLLOWLINKS, act, /* file action */ act, /* dir action */ NULL, /* user data */ 1 /* depth */ ); if (!names) return 0; qsort(names, cur, sizeof(char *), bb_alphasort); n = 0; while (1) { char *name = *names++; if (!name) break; if (option_mask32 & (OPT_t | OPT_l)) { puts(name); continue; } cmd[0] = name; ret = spawn_and_wait(cmd); if (ret == 0) continue; n = 1; if (ret < 0) bb_perror_msg("can't execute '%s'", name); else /* ret > 0 */ bb_error_msg("%s exited with code %d", name, ret & 0xff); if (option_mask32 & OPT_e) xfunc_die(); } return n; } busybox-1.22.1/debianutils/start_stop_daemon.c0000644000000000000000000003737112263563520020201 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini start-stop-daemon implementation(s) for busybox * * Written by Marek Michalkiewicz , * Adapted for busybox David Kimdon * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* This is how it is supposed to work: start-stop-daemon [OPTIONS] [--start|--stop] [[--] arguments...] One (only) of these must be given: -S,--start Start -K,--stop Stop Search for matching processes. If --stop is given, stop all matching processes (by sending a signal). If --start is given, start a new process unless a matching process was found. Options controlling process matching (if multiple conditions are specified, all must match): -u,--user USERNAME|UID Only consider this user's processes -n,--name PROCESS_NAME Look for processes by matching PROCESS_NAME with comm field in /proc/$PID/stat. Only basename is compared: "ntpd" == "./ntpd" == "/path/to/ntpd". [TODO: can PROCESS_NAME be a full pathname? Should we require full match then with /proc/$PID/exe or argv[0] (comm can't be matched, it never contains path)] -x,--exec EXECUTABLE Look for processes that were started with this command in /proc/$PID/exe and /proc/$PID/cmdline (/proc/$PID/cmdline is a bbox extension) Unlike -n, we match against the full path: "ntpd" != "./ntpd" != "/path/to/ntpd" -p,--pidfile PID_FILE Look for processes with PID from this file Options which are valid for --start only: -x,--exec EXECUTABLE Program to run (1st arg of execvp). Mandatory. -a,--startas NAME argv[0] (defaults to EXECUTABLE) -b,--background Put process into background -N,--nicelevel N Add N to process' nice level -c,--chuid USER[:[GRP]] Change to specified user [and group] -m,--make-pidfile Write PID to the pidfile (both -m and -p must be given!) Options which are valid for --stop only: -s,--signal SIG Signal to send (default:TERM) -t,--test Exit with status 0 if process is found (we don't actually start or stop daemons) Misc options: -o,--oknodo Exit with status 0 if nothing is done -q,--quiet Quiet -v,--verbose Verbose */ //usage:#define start_stop_daemon_trivial_usage //usage: "[OPTIONS] [-S|-K] ... [-- ARGS...]" //usage:#define start_stop_daemon_full_usage "\n\n" //usage: "Search for matching processes, and then\n" //usage: "-K: stop all matching processes.\n" //usage: "-S: start a process unless a matching process is found.\n" //usage: IF_FEATURE_START_STOP_DAEMON_LONG_OPTIONS( //usage: "\nProcess matching:" //usage: "\n -u,--user USERNAME|UID Match only this user's processes" //usage: "\n -n,--name NAME Match processes with NAME" //usage: "\n in comm field in /proc/PID/stat" //usage: "\n -x,--exec EXECUTABLE Match processes with this command" //usage: "\n in /proc/PID/{exe,cmdline}" //usage: "\n -p,--pidfile FILE Match a process with PID from the file" //usage: "\n All specified conditions must match" //usage: "\n-S only:" //usage: "\n -x,--exec EXECUTABLE Program to run" //usage: "\n -a,--startas NAME Zeroth argument" //usage: "\n -b,--background Background" //usage: IF_FEATURE_START_STOP_DAEMON_FANCY( //usage: "\n -N,--nicelevel N Change nice level" //usage: ) //usage: "\n -c,--chuid USER[:[GRP]] Change to user/group" //usage: "\n -m,--make-pidfile Write PID to the pidfile specified by -p" //usage: "\n-K only:" //usage: "\n -s,--signal SIG Signal to send" //usage: "\n -t,--test Match only, exit with 0 if a process is found" //usage: "\nOther:" //usage: IF_FEATURE_START_STOP_DAEMON_FANCY( //usage: "\n -o,--oknodo Exit with status 0 if nothing is done" //usage: "\n -v,--verbose Verbose" //usage: ) //usage: "\n -q,--quiet Quiet" //usage: ) //usage: IF_NOT_FEATURE_START_STOP_DAEMON_LONG_OPTIONS( //usage: "\nProcess matching:" //usage: "\n -u USERNAME|UID Match only this user's processes" //usage: "\n -n NAME Match processes with NAME" //usage: "\n in comm field in /proc/PID/stat" //usage: "\n -x EXECUTABLE Match processes with this command" //usage: "\n command in /proc/PID/cmdline" //usage: "\n -p FILE Match a process with PID from the file" //usage: "\n All specified conditions must match" //usage: "\n-S only:" //usage: "\n -x EXECUTABLE Program to run" //usage: "\n -a NAME Zeroth argument" //usage: "\n -b Background" //usage: IF_FEATURE_START_STOP_DAEMON_FANCY( //usage: "\n -N N Change nice level" //usage: ) //usage: "\n -c USER[:[GRP]] Change to user/group" //usage: "\n -m Write PID to the pidfile specified by -p" //usage: "\n-K only:" //usage: "\n -s SIG Signal to send" //usage: "\n -t Match only, exit with 0 if a process is found" //usage: "\nOther:" //usage: IF_FEATURE_START_STOP_DAEMON_FANCY( //usage: "\n -o Exit with status 0 if nothing is done" //usage: "\n -v Verbose" //usage: ) //usage: "\n -q Quiet" //usage: ) #include /* Override ENABLE_FEATURE_PIDFILE */ #define WANT_PIDFILE 1 #include "libbb.h" struct pid_list { struct pid_list *next; pid_t pid; }; enum { CTX_STOP = (1 << 0), CTX_START = (1 << 1), OPT_BACKGROUND = (1 << 2), // -b OPT_QUIET = (1 << 3), // -q OPT_TEST = (1 << 4), // -t OPT_MAKEPID = (1 << 5), // -m OPT_a = (1 << 6), // -a OPT_n = (1 << 7), // -n OPT_s = (1 << 8), // -s OPT_u = (1 << 9), // -u OPT_c = (1 << 10), // -c OPT_x = (1 << 11), // -x OPT_p = (1 << 12), // -p OPT_OKNODO = (1 << 13) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -o OPT_VERBOSE = (1 << 14) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -v OPT_NICELEVEL = (1 << 15) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -N }; #define QUIET (option_mask32 & OPT_QUIET) #define TEST (option_mask32 & OPT_TEST) struct globals { struct pid_list *found_procs; char *userspec; char *cmdname; char *execname; char *pidfile; char *execname_cmpbuf; unsigned execname_sizeof; int user_id; smallint signal_nr; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define userspec (G.userspec ) #define cmdname (G.cmdname ) #define execname (G.execname ) #define pidfile (G.pidfile ) #define user_id (G.user_id ) #define signal_nr (G.signal_nr ) #define INIT_G() do { \ user_id = -1; \ signal_nr = 15; \ } while (0) #ifdef OLDER_VERSION_OF_X /* -x,--exec EXECUTABLE * Look for processes with matching /proc/$PID/exe. * Match is performed using device+inode. */ static int pid_is_exec(pid_t pid) { struct stat st; char buf[sizeof("/proc/%u/exe") + sizeof(int)*3]; sprintf(buf, "/proc/%u/exe", (unsigned)pid); if (stat(buf, &st) < 0) return 0; if (st.st_dev == execstat.st_dev && st.st_ino == execstat.st_ino) return 1; return 0; } #endif static int pid_is_exec(pid_t pid) { ssize_t bytes; char buf[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; char *procname, *exelink; int match; procname = buf + sprintf(buf, "/proc/%u/exe", (unsigned)pid) - 3; exelink = xmalloc_readlink(buf); match = (exelink && strcmp(execname, exelink) == 0); free(exelink); if (match) return match; strcpy(procname, "cmdline"); bytes = open_read_close(buf, G.execname_cmpbuf, G.execname_sizeof); if (bytes > 0) { G.execname_cmpbuf[bytes] = '\0'; return strcmp(execname, G.execname_cmpbuf) == 0; } return 0; } static int pid_is_name(pid_t pid) { /* /proc/PID/stat is "PID (comm_15_bytes_max) ..." */ char buf[32]; /* should be enough */ char *p, *pe; sprintf(buf, "/proc/%u/stat", (unsigned)pid); if (open_read_close(buf, buf, sizeof(buf) - 1) < 0) return 0; buf[sizeof(buf) - 1] = '\0'; /* paranoia */ p = strchr(buf, '('); if (!p) return 0; pe = strrchr(++p, ')'); if (!pe) return 0; *pe = '\0'; /* we require comm to match and to not be truncated */ /* in Linux, if comm is 15 chars, it may be a truncated * name, so we don't allow that to match */ if (strlen(p) >= COMM_LEN - 1) /* COMM_LEN is 16 */ return 0; return strcmp(p, cmdname) == 0; } static int pid_is_user(int pid) { struct stat sb; char buf[sizeof("/proc/") + sizeof(int)*3]; sprintf(buf, "/proc/%u", (unsigned)pid); if (stat(buf, &sb) != 0) return 0; return (sb.st_uid == (uid_t)user_id); } static void check(int pid) { struct pid_list *p; if (execname && !pid_is_exec(pid)) { return; } if (cmdname && !pid_is_name(pid)) { return; } if (userspec && !pid_is_user(pid)) { return; } p = xmalloc(sizeof(*p)); p->next = G.found_procs; p->pid = pid; G.found_procs = p; } static void do_pidfile(void) { FILE *f; unsigned pid; f = fopen_for_read(pidfile); if (f) { if (fscanf(f, "%u", &pid) == 1) check(pid); fclose(f); } else if (errno != ENOENT) bb_perror_msg_and_die("open pidfile %s", pidfile); } static void do_procinit(void) { DIR *procdir; struct dirent *entry; int pid; if (pidfile) { do_pidfile(); return; } procdir = xopendir("/proc"); pid = 0; while (1) { errno = 0; /* clear any previous error */ entry = readdir(procdir); // TODO: this check is too generic, it's better // to check for exact errno(s) which mean that we got stale entry if (errno) /* Stale entry, process has died after opendir */ continue; if (!entry) /* EOF, no more entries */ break; pid = bb_strtou(entry->d_name, NULL, 10); if (errno) /* NaN */ continue; check(pid); } closedir(procdir); if (!pid) bb_error_msg_and_die("nothing in /proc - not mounted?"); } static int do_stop(void) { char *what; struct pid_list *p; int killed = 0; if (cmdname) { if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(cmdname); if (!ENABLE_FEATURE_CLEAN_UP) what = cmdname; } else if (execname) { if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(execname); if (!ENABLE_FEATURE_CLEAN_UP) what = execname; } else if (pidfile) { what = xasprintf("process in pidfile '%s'", pidfile); } else if (userspec) { what = xasprintf("process(es) owned by '%s'", userspec); } else { bb_error_msg_and_die("internal error, please report"); } if (!G.found_procs) { if (!QUIET) printf("no %s found; none killed\n", what); killed = -1; goto ret; } for (p = G.found_procs; p; p = p->next) { if (kill(p->pid, TEST ? 0 : signal_nr) == 0) { killed++; } else { bb_perror_msg("warning: killing process %u", (unsigned)p->pid); p->pid = 0; if (TEST) { /* Example: -K --test --pidfile PIDFILE detected * that PIDFILE's pid doesn't exist */ killed = -1; goto ret; } } } if (!QUIET && killed) { printf("stopped %s (pid", what); for (p = G.found_procs; p; p = p->next) if (p->pid) printf(" %u", (unsigned)p->pid); puts(")"); } ret: if (ENABLE_FEATURE_CLEAN_UP) free(what); return killed; } #if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS static const char start_stop_daemon_longopts[] ALIGN1 = "stop\0" No_argument "K" "start\0" No_argument "S" "background\0" No_argument "b" "quiet\0" No_argument "q" "test\0" No_argument "t" "make-pidfile\0" No_argument "m" #if ENABLE_FEATURE_START_STOP_DAEMON_FANCY "oknodo\0" No_argument "o" "verbose\0" No_argument "v" "nicelevel\0" Required_argument "N" #endif "startas\0" Required_argument "a" "name\0" Required_argument "n" "signal\0" Required_argument "s" "user\0" Required_argument "u" "chuid\0" Required_argument "c" "exec\0" Required_argument "x" "pidfile\0" Required_argument "p" #if ENABLE_FEATURE_START_STOP_DAEMON_FANCY "retry\0" Required_argument "R" #endif ; #endif int start_stop_daemon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv) { unsigned opt; char *signame; char *startas; char *chuid; #ifdef OLDER_VERSION_OF_X struct stat execstat; #endif #if ENABLE_FEATURE_START_STOP_DAEMON_FANCY // char *retry_arg = NULL; // int retries = -1; char *opt_N; #endif INIT_G(); #if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS applet_long_options = start_stop_daemon_longopts; #endif /* -K or -S is required; they are mutually exclusive */ /* -p is required if -m is given */ /* -xpun (at least one) is required if -K is given */ /* -xa (at least one) is required if -S is given */ /* -q turns off -v */ opt_complementary = "K:S:K--S:S--K:m?p:K?xpun:S?xa" IF_FEATURE_START_STOP_DAEMON_FANCY("q-v"); opt = getopt32(argv, "KSbqtma:n:s:u:c:x:p:" IF_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:"), &startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile IF_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N) /* We accept and ignore -R / --retry */ IF_FEATURE_START_STOP_DAEMON_FANCY(,NULL) ); if (opt & OPT_s) { signal_nr = get_signum(signame); if (signal_nr < 0) bb_show_usage(); } if (!(opt & OPT_a)) startas = execname; if (!execname) /* in case -a is given and -x is not */ execname = startas; if (execname) { G.execname_sizeof = strlen(execname) + 1; G.execname_cmpbuf = xmalloc(G.execname_sizeof + 1); } // IF_FEATURE_START_STOP_DAEMON_FANCY( // if (retry_arg) // retries = xatoi_positive(retry_arg); // ) //argc -= optind; argv += optind; if (userspec) { user_id = bb_strtou(userspec, NULL, 10); if (errno) user_id = xuname2uid(userspec); } /* Both start and stop need to know current processes */ do_procinit(); if (opt & CTX_STOP) { int i = do_stop(); return (opt & OPT_OKNODO) ? 0 : (i <= 0); } if (G.found_procs) { if (!QUIET) printf("%s is already running\n%u\n", execname, (unsigned)G.found_procs->pid); return !(opt & OPT_OKNODO); } #ifdef OLDER_VERSION_OF_X if (execname) xstat(execname, &execstat); #endif *--argv = startas; if (opt & OPT_BACKGROUND) { #if BB_MMU bb_daemonize(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS + DAEMON_DOUBLE_FORK); /* DAEMON_DEVNULL_STDIO is superfluous - * it's always done by bb_daemonize() */ #else pid_t pid = xvfork(); if (pid != 0) { /* parent */ /* why _exit? the child may have changed the stack, * so "return 0" may do bad things */ _exit(EXIT_SUCCESS); } /* Child */ setsid(); /* detach from controlling tty */ /* Redirect stdio to /dev/null, close extra FDs. * We do not actually daemonize because of DAEMON_ONLY_SANITIZE */ bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS + DAEMON_ONLY_SANITIZE, NULL /* argv, unused */ ); #endif } if (opt & OPT_MAKEPID) { /* User wants _us_ to make the pidfile */ write_pidfile(pidfile); } if (opt & OPT_c) { struct bb_uidgid_t ugid = { -1, -1 }; parse_chown_usergroup_or_die(&ugid, chuid); if (ugid.uid != (uid_t) -1) { struct passwd *pw = xgetpwuid(ugid.uid); if (ugid.gid != (gid_t) -1) pw->pw_gid = ugid.gid; /* initgroups, setgid, setuid: */ change_identity(pw); } else if (ugid.gid != (gid_t) -1) { xsetgid(ugid.gid); setgroups(1, &ugid.gid); } } #if ENABLE_FEATURE_START_STOP_DAEMON_FANCY if (opt & OPT_NICELEVEL) { /* Set process priority */ int prio = getpriority(PRIO_PROCESS, 0) + xatoi_range(opt_N, INT_MIN/2, INT_MAX/2); if (setpriority(PRIO_PROCESS, 0, prio) < 0) { bb_perror_msg_and_die("setpriority(%d)", prio); } } #endif execvp(startas, argv); bb_perror_msg_and_die("can't execute '%s'", startas); } busybox-1.22.1/debianutils/Config.src0000644000000000000000000000375112263563520016221 0ustar rootroot# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Debian Utilities" INSERT config MKTEMP bool "mktemp" default y help mktemp is used to create unique temporary files config PIPE_PROGRESS bool "pipe_progress" default y help Display a dot to indicate pipe activity. config RUN_PARTS bool "run-parts" default y help run-parts is a utility designed to run all the scripts in a directory. It is useful to set up a directory like cron.daily, where you need to execute all the scripts in that directory. In this implementation of run-parts some features (such as report mode) are not implemented. Unless you know that run-parts is used in some of your scripts you can safely say N here. config FEATURE_RUN_PARTS_LONG_OPTIONS bool "Enable long options" default y depends on RUN_PARTS && LONG_OPTS help Support long options for the run-parts applet. config FEATURE_RUN_PARTS_FANCY bool "Support additional arguments" default y depends on RUN_PARTS help Support additional options: -l --list print the names of the all matching files (not limited to executables), but don't actually run them. config START_STOP_DAEMON bool "start-stop-daemon" default y help start-stop-daemon is used to control the creation and termination of system-level processes, usually the ones started during the startup of the system. config FEATURE_START_STOP_DAEMON_FANCY bool "Support additional arguments" default y depends on START_STOP_DAEMON help Support additional arguments. -o|--oknodo ignored since we exit with 0 anyway -v|--verbose -N|--nicelevel N config FEATURE_START_STOP_DAEMON_LONG_OPTIONS bool "Enable long options" default y depends on START_STOP_DAEMON && LONG_OPTS help Support long options for the start-stop-daemon applet. config WHICH bool "which" default y help which is used to find programs in your PATH and print out their pathnames. endmenu busybox-1.22.1/debianutils/Kbuild.src0000644000000000000000000000063512263563520016224 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT lib-$(CONFIG_MKTEMP) += mktemp.o lib-$(CONFIG_PIPE_PROGRESS) += pipe_progress.o lib-$(CONFIG_RUN_PARTS) += run_parts.o lib-$(CONFIG_START_STOP_DAEMON) += start_stop_daemon.o lib-$(CONFIG_WHICH) += which.o busybox-1.22.1/debianutils/mktemp.c0000644000000000000000000000652612263563520015747 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini mktemp implementation for busybox * * * Copyright (C) 2000 by Daniel Jacobowitz * Written by Daniel Jacobowitz * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* Coreutils 6.12 man page says: * mktemp [OPTION]... [TEMPLATE] * Create a temporary file or directory, safely, and print its name. If * TEMPLATE is not specified, use tmp.XXXXXXXXXX. * -d, --directory * create a directory, not a file * -q, --quiet * suppress diagnostics about file/dir-creation failure * -u, --dry-run * do not create anything; merely print a name (unsafe) * --tmpdir[=DIR] * interpret TEMPLATE relative to DIR. If DIR is not specified, * use $TMPDIR if set, else /tmp. With this option, TEMPLATE must * not be an absolute name. Unlike with -t, TEMPLATE may contain * slashes, but even here, mktemp still creates only the final com- * ponent. * -p DIR use DIR as a prefix; implies -t [deprecated] * -t interpret TEMPLATE as a single file name component, relative to * a directory: $TMPDIR, if set; else the directory specified via * -p; else /tmp [deprecated] */ //usage:#define mktemp_trivial_usage //usage: "[-dt] [-p DIR] [TEMPLATE]" //usage:#define mktemp_full_usage "\n\n" //usage: "Create a temporary file with name based on TEMPLATE and print its name.\n" //usage: "TEMPLATE must end with XXXXXX (e.g. [/dir/]nameXXXXXX).\n" //usage: "Without TEMPLATE, -t tmp.XXXXXX is assumed.\n" //usage: "\n -d Make directory, not file" //usage: "\n -q Fail silently on errors" //usage: "\n -t Prepend base directory name to TEMPLATE" //usage: "\n -p DIR Use DIR as a base directory (implies -t)" //usage: "\n -u Do not create anything; print a name" //usage: "\n" //usage: "\nBase directory is: -p DIR, else $TMPDIR, else /tmp" //usage: //usage:#define mktemp_example_usage //usage: "$ mktemp /tmp/temp.XXXXXX\n" //usage: "/tmp/temp.mWiLjM\n" //usage: "$ ls -la /tmp/temp.mWiLjM\n" //usage: "-rw------- 1 andersen andersen 0 Apr 25 17:10 /tmp/temp.mWiLjM\n" #include "libbb.h" int mktemp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mktemp_main(int argc UNUSED_PARAM, char **argv) { const char *path; char *chp; unsigned opts; enum { OPT_d = 1 << 0, OPT_q = 1 << 1, OPT_t = 1 << 2, OPT_p = 1 << 3, OPT_u = 1 << 4, }; path = getenv("TMPDIR"); if (!path || path[0] == '\0') path = "/tmp"; opt_complementary = "?1"; /* 1 argument max */ opts = getopt32(argv, "dqtp:u", &path); chp = argv[optind]; if (!chp) { /* GNU coreutils 8.4: * bare "mktemp" -> "mktemp -t tmp.XXXXXX" */ chp = xstrdup("tmp.XXXXXX"); opts |= OPT_t; } #if 0 /* Don't allow directory separator in template */ if ((opts & OPT_t) && bb_basename(chp) != chp) { errno = EINVAL; goto error; } #endif if (opts & (OPT_t|OPT_p)) chp = concat_path_file(path, chp); if (opts & OPT_u) { chp = mktemp(chp); if (chp[0] == '\0') goto error; } else if (opts & OPT_d) { if (mkdtemp(chp) == NULL) goto error; } else { if (mkstemp(chp) < 0) goto error; } puts(chp); return EXIT_SUCCESS; error: if (opts & OPT_q) return EXIT_FAILURE; /* don't use chp as it gets mangled in case of error */ bb_perror_nomsg_and_die(); } busybox-1.22.1/debianutils/which.c0000644000000000000000000000377512263563520015557 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Which implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * Copyright (C) 2006 Gabriel Somlo * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Based on which from debianutils */ //usage:#define which_trivial_usage //usage: "[COMMAND]..." //usage:#define which_full_usage "\n\n" //usage: "Locate a COMMAND" //usage: //usage:#define which_example_usage //usage: "$ which login\n" //usage: "/bin/login\n" #include "libbb.h" int which_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int which_main(int argc UNUSED_PARAM, char **argv) { IF_DESKTOP(int opt;) int status = EXIT_SUCCESS; char *path; char *p; opt_complementary = "-1"; /* at least one argument */ IF_DESKTOP(opt =) getopt32(argv, "a"); argv += optind; /* This matches what is seen on e.g. ubuntu. * "which" there is a shell script. */ path = getenv("PATH"); if (!path) { path = (char*)bb_PATH_root_path; putenv(path); path += 5; /* skip "PATH=" */ } do { #if ENABLE_DESKTOP /* Much bloat just to support -a */ if (strchr(*argv, '/')) { if (execable_file(*argv)) { puts(*argv); continue; } status = EXIT_FAILURE; } else { char *path2 = xstrdup(path); char *tmp = path2; p = find_execable(*argv, &tmp); if (!p) status = EXIT_FAILURE; else { print: puts(p); free(p); if (opt) { /* -a: show matches in all PATH components */ if (tmp) { p = find_execable(*argv, &tmp); if (p) goto print; } } } free(path2); } #else /* Just ignoring -a */ if (strchr(*argv, '/')) { if (execable_file(*argv)) { puts(*argv); continue; } } else { char *path2 = xstrdup(path); char *tmp = path2; p = find_execable(*argv, &tmp); free(path2); if (p) { puts(p); free(p); continue; } } status = EXIT_FAILURE; #endif } while (*(++argv) != NULL); fflush_stdout_and_exit(status); } busybox-1.22.1/debianutils/pipe_progress.c0000644000000000000000000000167212263563520017330 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Monitor a pipe with a simple progress display. * * Copyright (C) 2003 by Rob Landley , Joey Hess * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define pipe_progress_trivial_usage NOUSAGE_STR //usage:#define pipe_progress_full_usage "" #include "libbb.h" #define PIPE_PROGRESS_SIZE 4096 /* Read a block of data from stdin, write it to stdout. * Activity is indicated by a '.' to stderr */ int pipe_progress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int pipe_progress_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { char buf[PIPE_PROGRESS_SIZE]; time_t t = time(NULL); int len; while ((len = safe_read(STDIN_FILENO, buf, PIPE_PROGRESS_SIZE)) > 0) { time_t new_time = time(NULL); if (new_time != t) { t = new_time; bb_putchar_stderr('.'); } full_write(STDOUT_FILENO, buf, len); } bb_putchar_stderr('\n'); return 0; } busybox-1.22.1/console-tools/0000755000000000000000000000000012320365363014571 5ustar rootrootbusybox-1.22.1/console-tools/dumpkmap.c0000644000000000000000000000433012263563520016554 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini dumpkmap implementation for busybox * * Copyright (C) Arne Bernin * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * */ /* no options, no getopt */ //usage:#define dumpkmap_trivial_usage //usage: "> keymap" //usage:#define dumpkmap_full_usage "\n\n" //usage: "Print a binary keyboard translation table to stdout" //usage: //usage:#define dumpkmap_example_usage //usage: "$ dumpkmap > keymap\n" #include "libbb.h" /* From */ struct kbentry { unsigned char kb_table; unsigned char kb_index; unsigned short kb_value; }; #define KDGKBENT 0x4B46 /* gets one entry in translation table */ /* From */ #define NR_KEYS 128 #define MAX_NR_KEYMAPS 256 int dumpkmap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int dumpkmap_main(int argc UNUSED_PARAM, char **argv) { struct kbentry ke; int i, j, fd; #define flags bb_common_bufsiz1 /* When user accidentally runs "dumpkmap FILE" * instead of "dumpkmap >FILE", we'd dump binary stuff to tty. * Let's prevent it: */ if (argv[1]) bb_show_usage(); /* bb_warn_ignoring_args(argv[1]);*/ fd = get_console_fd_or_die(); #if 0 write(STDOUT_FILENO, "bkeymap", 7); /* Here we want to set everything to 0 except for indexes: * [0-2] [4-6] [8-10] [12] */ /*memset(flags, 0x00, MAX_NR_KEYMAPS); - already is */ memset(flags, 0x01, 13); flags[3] = flags[7] = flags[11] = 0; /* dump flags */ write(STDOUT_FILENO, flags, MAX_NR_KEYMAPS); #define flags7 flags #else /* Same effect */ /* 0 1 2 3 4 5 6 7 8 9 a b c=12 */ memcpy(flags, "bkeymap\1\1\1\0\1\1\1\0\1\1\1\0\1", /* Can use sizeof, or sizeof-1. sizeof is even, using that */ /****/ sizeof("bkeymap\1\1\1\0\1\1\1\0\1\1\1\0\1") ); write(STDOUT_FILENO, flags, 7 + MAX_NR_KEYMAPS); #define flags7 (flags + 7) #endif for (i = 0; i < 13; i++) { if (flags7[i]) { for (j = 0; j < NR_KEYS; j++) { ke.kb_index = j; ke.kb_table = i; if (!ioctl_or_perror(fd, KDGKBENT, &ke, "ioctl(KDGKBENT{%d,%d}) failed", j, i) ) { write(STDOUT_FILENO, &ke.kb_value, 2); } } } } if (ENABLE_FEATURE_CLEAN_UP) { close(fd); } return EXIT_SUCCESS; } busybox-1.22.1/console-tools/showkey.c0000644000000000000000000000677512263563520016446 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * shows keys pressed. inspired by kbd package * * Copyright (C) 2008 by Vladimir Dronnikov * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define showkey_trivial_usage //usage: "[-a | -k | -s]" //usage:#define showkey_full_usage "\n\n" //usage: "Show keys pressed\n" //usage: "\n -a Display decimal/octal/hex values of the keys" //usage: "\n -k Display interpreted keycodes (default)" //usage: "\n -s Display raw scan-codes" #include "libbb.h" #include struct globals { int kbmode; struct termios tio, tio0; }; #define G (*ptr_to_globals) #define kbmode (G.kbmode) #define tio (G.tio) #define tio0 (G.tio0) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ } while (0) // set raw tty mode // also used by microcom // libbb candidates? static void xget1(struct termios *t, struct termios *oldt) { tcgetattr(STDIN_FILENO, oldt); *t = *oldt; cfmakeraw(t); } static void xset1(struct termios *t) { int ret = tcsetattr(STDIN_FILENO, TCSAFLUSH, t); if (ret) { bb_perror_msg("can't tcsetattr for stdin"); } } int showkey_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int showkey_main(int argc UNUSED_PARAM, char **argv) { enum { OPT_a = (1<<0), // display the decimal/octal/hex values of the keys OPT_k = (1<<1), // display only the interpreted keycodes (default) OPT_s = (1<<2), // display only the raw scan-codes }; INIT_G(); // FIXME: aks are all mutually exclusive getopt32(argv, "aks"); // prepare for raw mode xget1(&tio, &tio0); // put stdin in raw mode xset1(&tio); #define press_keys "Press any keys, program terminates %s:\r\n\n" if (option_mask32 & OPT_a) { // just read stdin char by char unsigned char c; printf(press_keys, "on EOF (ctrl-D)"); // read and show byte values while (1 == read(STDIN_FILENO, &c, 1)) { printf("%3u 0%03o 0x%02x\r\n", c, c, c); if (04 /*CTRL-D*/ == c) break; } } else { // we assume a PC keyboard xioctl(STDIN_FILENO, KDGKBMODE, &kbmode); printf("Keyboard mode was %s.\r\n\n", kbmode == K_RAW ? "RAW" : (kbmode == K_XLATE ? "XLATE" : (kbmode == K_MEDIUMRAW ? "MEDIUMRAW" : (kbmode == K_UNICODE ? "UNICODE" : "UNKNOWN"))) ); // set raw keyboard mode xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)((option_mask32 & OPT_k) ? K_MEDIUMRAW : K_RAW)); // we should exit on any signal; signals should interrupt read bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo); // inform user that program ends after time of inactivity printf(press_keys, "10s after last keypress"); // read and show scancodes while (!bb_got_signal) { char buf[18]; int i, n; // setup 10s watchdog alarm(10); // read scancodes n = read(STDIN_FILENO, buf, sizeof(buf)); i = 0; while (i < n) { if (option_mask32 & OPT_s) { // show raw scancodes printf("0x%02x ", buf[i++]); } else { // show interpreted scancodes (default) char c = buf[i]; int kc; if (i+2 < n && (c & 0x7f) == 0 && (buf[i+1] & 0x80) != 0 && (buf[i+2] & 0x80) != 0 ) { kc = ((buf[i+1] & 0x7f) << 7) | (buf[i+2] & 0x7f); i += 3; } else { kc = (c & 0x7f); i++; } printf("keycode %3u %s", kc, (c & 0x80) ? "release" : "press"); } } puts("\r"); } // restore keyboard mode xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)kbmode); } // restore console settings xset1(&tio0); return EXIT_SUCCESS; } busybox-1.22.1/console-tools/loadfont.c0000644000000000000000000003200112263563520016540 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * loadfont.c - Eugene Crosser & Andries Brouwer * * Version 0.96bb * * Loads the console font, and possibly the corresponding screen map(s). * (Adapted for busybox by Matej Vela.) * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define loadfont_trivial_usage //usage: "< font" //usage:#define loadfont_full_usage "\n\n" //usage: "Load a console font from stdin" /* //usage: "\n -C TTY Affect TTY instead of /dev/tty" */ //usage: //usage:#define loadfont_example_usage //usage: "$ loadfont < /etc/i18n/fontname\n" //usage: //usage:#define setfont_trivial_usage //usage: "FONT [-m MAPFILE] [-C TTY]" //usage:#define setfont_full_usage "\n\n" //usage: "Load a console font\n" //usage: "\n -m MAPFILE Load console screen map" //usage: "\n -C TTY Affect TTY instead of /dev/tty" //usage: //usage:#define setfont_example_usage //usage: "$ setfont -m koi8-r /etc/i18n/fontname\n" #include "libbb.h" #include #ifndef KDFONTOP # define KDFONTOP 0x4B72 struct console_font_op { unsigned op; /* KD_FONT_OP_* */ unsigned flags; /* KD_FONT_FLAG_* */ unsigned width, height; unsigned charcount; unsigned char *data; /* font data with height fixed to 32 */ }; # define KD_FONT_OP_SET 0 /* Set font */ # define KD_FONT_OP_GET 1 /* Get font */ # define KD_FONT_OP_SET_DEFAULT 2 /* Set font to default, data points to name / NULL */ # define KD_FONT_OP_COPY 3 /* Copy from another console */ # define KD_FONT_FLAG_OLD 0x80000000 /* Invoked via old interface */ # define KD_FONT_FLAG_DONT_RECALC 1 /* Don't call adjust_height() */ /* (Used internally for PIO_FONT support) */ #endif /* KDFONTOP */ enum { PSF1_MAGIC0 = 0x36, PSF1_MAGIC1 = 0x04, PSF1_MODE512 = 0x01, PSF1_MODEHASTAB = 0x02, PSF1_MODEHASSEQ = 0x04, PSF1_MAXMODE = 0x05, PSF1_STARTSEQ = 0xfffe, PSF1_SEPARATOR = 0xffff, }; struct psf1_header { unsigned char magic[2]; /* Magic number */ unsigned char mode; /* PSF font mode */ unsigned char charsize; /* Character size */ }; #define psf1h(x) ((struct psf1_header*)(x)) #define PSF1_MAGIC_OK(x) ( \ (x)->magic[0] == PSF1_MAGIC0 \ && (x)->magic[1] == PSF1_MAGIC1 \ ) #if ENABLE_FEATURE_LOADFONT_PSF2 enum { PSF2_MAGIC0 = 0x72, PSF2_MAGIC1 = 0xb5, PSF2_MAGIC2 = 0x4a, PSF2_MAGIC3 = 0x86, PSF2_HAS_UNICODE_TABLE = 0x01, PSF2_MAXVERSION = 0, PSF2_STARTSEQ = 0xfe, PSF2_SEPARATOR = 0xff }; struct psf2_header { unsigned char magic[4]; unsigned int version; unsigned int headersize; /* offset of bitmaps in file */ unsigned int flags; unsigned int length; /* number of glyphs */ unsigned int charsize; /* number of bytes for each character */ unsigned int height; /* max dimensions of glyphs */ unsigned int width; /* charsize = height * ((width + 7) / 8) */ }; #define psf2h(x) ((struct psf2_header*)(x)) #define PSF2_MAGIC_OK(x) ( \ (x)->magic[0] == PSF2_MAGIC0 \ && (x)->magic[1] == PSF2_MAGIC1 \ && (x)->magic[2] == PSF2_MAGIC2 \ && (x)->magic[3] == PSF2_MAGIC3 \ ) #endif /* ENABLE_FEATURE_LOADFONT_PSF2 */ static void do_loadfont(int fd, unsigned char *inbuf, int height, int width, int charsize, int fontsize) { unsigned char *buf; int charwidth = 32 * ((width+7)/8); int i; if (height < 1 || height > 32 || width < 1 || width > 32) bb_error_msg_and_die("bad character size %dx%d", height, width); buf = xzalloc(charwidth * ((fontsize < 128) ? 128 : fontsize)); for (i = 0; i < fontsize; i++) memcpy(buf + (i*charwidth), inbuf + (i*charsize), charsize); { /* KDFONTOP */ struct console_font_op cfo; cfo.op = KD_FONT_OP_SET; cfo.flags = 0; cfo.width = width; cfo.height = height; cfo.charcount = fontsize; cfo.data = buf; xioctl(fd, KDFONTOP, &cfo); } free(buf); } /* * Format of the Unicode information: * * For each font position ** * where is a 2-byte little endian Unicode value (PSF1) * or an UTF-8 coded value (PSF2), * = *, = psf1 ? 0xFFFE : 0xFE, * = psf1 ? 0xFFFF : 0xFF. * and * denotes zero or more occurrences of the preceding item. * * Semantics: * The leading * part gives Unicode symbols that are all * represented by this font position. The following sequences * are sequences of Unicode symbols - probably a symbol * together with combining accents - also represented by * this font position. * * Example: * At the font position for a capital A-ring glyph, we * may have: * 00C5,212B,FFFE,0041,030A,FFFF * Some font positions may be described by sequences only, * namely when there is no precomposed Unicode value for the glyph. */ #if !ENABLE_FEATURE_LOADFONT_PSF2 #define do_loadtable(fd, inbuf, tailsz, fontsize, psf2) \ do_loadtable(fd, inbuf, tailsz, fontsize) #endif static void do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize, int psf2) { #if !ENABLE_FEATURE_LOADFONT_PSF2 /* gcc 4.3.1 code size: */ # define psf2 0 /* +0 bytes */ // const int psf2 = 0; /* +8 bytes */ // enum { psf2 = 0 }; /* +13 bytes */ #endif struct unimapinit advice; struct unimapdesc ud; struct unipair *up; int ct = 0, maxct; int glyph; uint16_t unicode; maxct = tailsz; /* more than enough */ up = xmalloc(maxct * sizeof(*up)); for (glyph = 0; glyph < fontsize; glyph++) { while (tailsz > 0) { if (!psf2) { /* PSF1 */ unicode = (((uint16_t) inbuf[1]) << 8) + inbuf[0]; tailsz -= 2; inbuf += 2; if (unicode == PSF1_SEPARATOR) break; } else { /* PSF2 */ #if ENABLE_FEATURE_LOADFONT_PSF2 --tailsz; unicode = *inbuf++; if (unicode == PSF2_SEPARATOR) { break; } else if (unicode == PSF2_STARTSEQ) { bb_error_msg_and_die("unicode sequences not implemented"); } else if (unicode >= 0xC0) { if (unicode >= 0xFC) unicode &= 0x01, maxct = 5; else if (unicode >= 0xF8) unicode &= 0x03, maxct = 4; else if (unicode >= 0xF0) unicode &= 0x07, maxct = 3; else if (unicode >= 0xE0) unicode &= 0x0F, maxct = 2; else unicode &= 0x1F, maxct = 1; do { if (tailsz <= 0 || *inbuf < 0x80 || *inbuf > 0xBF) bb_error_msg_and_die("illegal UTF-8 character"); --tailsz; unicode = (unicode << 6) + (*inbuf++ & 0x3F); } while (--maxct > 0); } else if (unicode >= 0x80) { bb_error_msg_and_die("illegal UTF-8 character"); } #else return; #endif } up[ct].unicode = unicode; up[ct].fontpos = glyph; ct++; } } /* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP * this printf did not work on many kernels */ advice.advised_hashsize = 0; advice.advised_hashstep = 0; advice.advised_hashlevel = 0; xioctl(fd, PIO_UNIMAPCLR, &advice); ud.entry_ct = ct; ud.entries = up; xioctl(fd, PIO_UNIMAP, &ud); #undef psf2 } static void do_load(int fd, unsigned char *buffer, size_t len) { int height; int width = 8; int charsize; int fontsize = 256; int has_table = 0; unsigned char *font = buffer; unsigned char *table; if (len >= sizeof(struct psf1_header) && PSF1_MAGIC_OK(psf1h(buffer))) { if (psf1h(buffer)->mode > PSF1_MAXMODE) bb_error_msg_and_die("unsupported psf file mode"); if (psf1h(buffer)->mode & PSF1_MODE512) fontsize = 512; if (psf1h(buffer)->mode & PSF1_MODEHASTAB) has_table = 1; height = charsize = psf1h(buffer)->charsize; font += sizeof(struct psf1_header); } else #if ENABLE_FEATURE_LOADFONT_PSF2 if (len >= sizeof(struct psf2_header) && PSF2_MAGIC_OK(psf2h(buffer))) { if (psf2h(buffer)->version > PSF2_MAXVERSION) bb_error_msg_and_die("unsupported psf file version"); fontsize = psf2h(buffer)->length; if (psf2h(buffer)->flags & PSF2_HAS_UNICODE_TABLE) has_table = 2; charsize = psf2h(buffer)->charsize; height = psf2h(buffer)->height; width = psf2h(buffer)->width; font += psf2h(buffer)->headersize; } else #endif #if ENABLE_FEATURE_LOADFONT_RAW if (len == 9780) { /* file with three code pages? */ charsize = height = 16; font += 40; } else if ((len & 0377) == 0) { /* bare font */ charsize = height = len / 256; } else #endif { bb_error_msg_and_die("input file: bad length or unsupported font type"); } #if !defined(PIO_FONTX) || defined(__sparc__) if (fontsize != 256) bb_error_msg_and_die("only fontsize 256 supported"); #endif table = font + fontsize * charsize; buffer += len; if (table > buffer || (!has_table && table != buffer)) bb_error_msg_and_die("input file: bad length"); do_loadfont(fd, font, height, width, charsize, fontsize); if (has_table) do_loadtable(fd, table, buffer - table, fontsize, has_table - 1); } #if ENABLE_LOADFONT int loadfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int loadfont_main(int argc UNUSED_PARAM, char **argv) { size_t len; unsigned char *buffer; // no arguments allowed! opt_complementary = "=0"; getopt32(argv, ""); /* * We used to look at the length of the input file * with stat(); now that we accept compressed files, * just read the entire file. */ len = 32*1024; // can't be larger buffer = xmalloc_read(STDIN_FILENO, &len); // xmalloc_open_zipped_read_close(filename, &len); if (!buffer) bb_perror_msg_and_die("error reading input font"); do_load(get_console_fd_or_die(), buffer, len); return EXIT_SUCCESS; } #endif #if ENABLE_SETFONT /* kbd-1.12: setfont [-O font+umap.orig] [-o font.orig] [-om cmap.orig] [-ou umap.orig] [-N] [font.new ...] [-m cmap] [-u umap] [-C console] [-hNN] [-v] [-V] -h NN Override font height -o file Save previous font in file -O file Save previous font and Unicode map in file -om file Store console map in file -ou file Save previous Unicode map in file -m file Load console map or Unicode console map from file -u file Load Unicode table describing the font from file Example: # cp866 0x00-0x7f idem # 0x80 U+0410 # CYRILLIC CAPITAL LETTER A 0x81 U+0411 # CYRILLIC CAPITAL LETTER BE 0x82 U+0412 # CYRILLIC CAPITAL LETTER VE -C console Set the font for the indicated console -v Verbose -V Version */ #if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP static int ctoi(char *s) { if (s[0] == '\'' && s[1] != '\0' && s[2] == '\'' && s[3] == '\0') return s[1]; // U+ means 0x if (s[0] == 'U' && s[1] == '+') { s[0] = '0'; s[1] = 'x'; } if (!isdigit(s[0])) return -1; return xstrtoul(s, 0); } #endif int setfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int setfont_main(int argc UNUSED_PARAM, char **argv) { size_t len; unsigned opts; int fd; unsigned char *buffer; char *mapfilename; const char *tty_name = CURRENT_TTY; opt_complementary = "=1"; opts = getopt32(argv, "m:C:", &mapfilename, &tty_name); argv += optind; fd = xopen_nonblocking(tty_name); if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not "" if (*argv[0] != '/') { // goto default fonts location. don't die if doesn't exist chdir(CONFIG_DEFAULT_SETFONT_DIR "/consolefonts"); } } // load font len = 32*1024; // can't be larger buffer = xmalloc_open_zipped_read_close(*argv, &len); if (!buffer) bb_simple_perror_msg_and_die(*argv); do_load(fd, buffer, len); // load the screen map, if any if (opts & 1) { // -m unsigned mode = PIO_SCRNMAP; void *map; if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not "" if (mapfilename[0] != '/') { // goto default keymaps location chdir(CONFIG_DEFAULT_SETFONT_DIR "/consoletrans"); } } // fetch keymap map = xmalloc_open_zipped_read_close(mapfilename, &len); if (!map) bb_simple_perror_msg_and_die(mapfilename); // file size is 256 or 512 bytes? -> assume binary map if (len == E_TABSZ || len == 2*E_TABSZ) { if (len == 2*E_TABSZ) mode = PIO_UNISCRNMAP; } #if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP // assume textual Unicode console maps: // 0x00 U+0000 # NULL (NUL) // 0x01 U+0001 # START OF HEADING (SOH) // 0x02 U+0002 # START OF TEXT (STX) // 0x03 U+0003 # END OF TEXT (ETX) else { int i; char *token[2]; parser_t *parser; if (ENABLE_FEATURE_CLEAN_UP) free(map); map = xmalloc(E_TABSZ * sizeof(unsigned short)); #define unicodes ((unsigned short *)map) // fill vanilla map for (i = 0; i < E_TABSZ; i++) unicodes[i] = 0xf000 + i; parser = config_open(mapfilename); while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) { // parse code/value pair int a = ctoi(token[0]); int b = ctoi(token[1]); if (a < 0 || a >= E_TABSZ || b < 0 || b > 65535 ) { bb_error_msg_and_die("map format"); } // patch map unicodes[a] = b; // unicode character is met? if (b > 255) mode = PIO_UNISCRNMAP; } if (ENABLE_FEATURE_CLEAN_UP) config_close(parser); if (mode != PIO_UNISCRNMAP) { #define asciis ((unsigned char *)map) for (i = 0; i < E_TABSZ; i++) asciis[i] = unicodes[i]; #undef asciis } #undef unicodes } #endif // ENABLE_FEATURE_SETFONT_TEXTUAL_MAP // do set screen map xioctl(fd, mode, map); if (ENABLE_FEATURE_CLEAN_UP) free(map); } return EXIT_SUCCESS; } #endif busybox-1.22.1/console-tools/setconsole.c0000644000000000000000000000230412263563520017113 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * setconsole.c - redirect system console output * * Copyright (C) 2004,2005 Enrik Berkhan * Copyright (C) 2008 Bernhard Reutner-Fischer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define setconsole_trivial_usage //usage: "[-r" IF_FEATURE_SETCONSOLE_LONG_OPTIONS("|--reset") "] [DEVICE]" //usage:#define setconsole_full_usage "\n\n" //usage: "Redirect system console output to DEVICE (default: /dev/tty)\n" //usage: "\n -r Reset output to /dev/console" #include "libbb.h" int setconsole_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int setconsole_main(int argc UNUSED_PARAM, char **argv) { const char *device = CURRENT_TTY; bool reset; #if ENABLE_FEATURE_SETCONSOLE_LONG_OPTIONS static const char setconsole_longopts[] ALIGN1 = "reset\0" No_argument "r" ; applet_long_options = setconsole_longopts; #endif /* at most one non-option argument */ opt_complementary = "?1"; reset = getopt32(argv, "r"); argv += 1 + reset; if (*argv) { device = *argv; } else { if (reset) device = DEV_CONSOLE; } xioctl(xopen(device, O_WRONLY), TIOCCONS, NULL); return EXIT_SUCCESS; } busybox-1.22.1/console-tools/resize.c0000644000000000000000000000406512263563520016244 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * resize - set terminal width and height. * * Copyright 2006 Bernhard Reutner-Fischer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* no options, no getopt */ //usage:#define resize_trivial_usage //usage: "" //usage:#define resize_full_usage "\n\n" //usage: "Resize the screen" #include "libbb.h" #define ESC "\033" #define old_termios_p ((struct termios*)&bb_common_bufsiz1) static void onintr(int sig UNUSED_PARAM) { tcsetattr(STDERR_FILENO, TCSANOW, old_termios_p); _exit(EXIT_FAILURE); } int resize_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int resize_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { struct termios new; struct winsize w = { 0, 0, 0, 0 }; int ret; /* We use _stderr_ in order to make resize usable * in shell backticks (those redirect stdout away from tty). * NB: other versions of resize open "/dev/tty" * and operate on it - should we do the same? */ tcgetattr(STDERR_FILENO, old_termios_p); /* fiddle echo */ memcpy(&new, old_termios_p, sizeof(new)); new.c_cflag |= (CLOCAL | CREAD); new.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); bb_signals(0 + (1 << SIGINT) + (1 << SIGQUIT) + (1 << SIGTERM) + (1 << SIGALRM) , onintr); tcsetattr(STDERR_FILENO, TCSANOW, &new); /* save_cursor_pos 7 * scroll_whole_screen [r * put_cursor_waaaay_off [$x;$yH * get_cursor_pos [6n * restore_cursor_pos 8 */ fprintf(stderr, ESC"7" ESC"[r" ESC"[999;999H" ESC"[6n"); alarm(3); /* Just in case terminal won't answer */ //BUG: death by signal won't restore termios scanf(ESC"[%hu;%huR", &w.ws_row, &w.ws_col); fprintf(stderr, ESC"8"); /* BTW, other versions of resize recalculate w.ws_xpixel, ws.ws_ypixel * by calculating character cell HxW from old values * (gotten via TIOCGWINSZ) and recomputing *pixel values */ ret = ioctl(STDERR_FILENO, TIOCSWINSZ, &w); tcsetattr(STDERR_FILENO, TCSANOW, old_termios_p); if (ENABLE_FEATURE_RESIZE_PRINT) printf("COLUMNS=%d;LINES=%d;export COLUMNS LINES;\n", w.ws_col, w.ws_row); return ret; } busybox-1.22.1/console-tools/Config.src0000644000000000000000000000711512263563520016514 0ustar rootroot# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Console Utilities" INSERT config CHVT bool "chvt" default y select PLATFORM_LINUX help This program is used to change to another terminal. Example: chvt 4 (change to terminal /dev/tty4) config FGCONSOLE bool "fgconsole" default y select PLATFORM_LINUX help This program prints active (foreground) console number. config CLEAR bool "clear" default y help This program clears the terminal screen. config DEALLOCVT bool "deallocvt" default y select PLATFORM_LINUX help This program deallocates unused virtual consoles. config DUMPKMAP bool "dumpkmap" default y select PLATFORM_LINUX help This program dumps the kernel's keyboard translation table to stdout, in binary format. You can then use loadkmap to load it. config KBD_MODE bool "kbd_mode" default y select PLATFORM_LINUX help This program reports and sets keyboard mode. config LOADFONT bool "loadfont" default y select PLATFORM_LINUX help This program loads a console font from standard input. config LOADKMAP bool "loadkmap" default y select PLATFORM_LINUX help This program loads a keyboard translation table from standard input. config OPENVT bool "openvt" default y select PLATFORM_LINUX help This program is used to start a command on an unused virtual terminal. config RESET bool "reset" default y help This program is used to reset the terminal screen, if it gets messed up. config RESIZE bool "resize" default y help This program is used to (re)set the width and height of your current terminal. config FEATURE_RESIZE_PRINT bool "Print environment variables" default y depends on RESIZE help Prints the newly set size (number of columns and rows) of the terminal. E.g.: COLUMNS=80;LINES=44;export COLUMNS LINES; config SETCONSOLE bool "setconsole" default y select PLATFORM_LINUX help This program redirects the system console to another device, like the current tty while logged in via telnet. config FEATURE_SETCONSOLE_LONG_OPTIONS bool "Enable long options" default y depends on SETCONSOLE && LONG_OPTS help Support long options for the setconsole applet. config SETFONT bool "setfont" default y select PLATFORM_LINUX help Allows to load console screen map. Useful for i18n. config FEATURE_SETFONT_TEXTUAL_MAP bool "Support reading textual screen maps" default y depends on SETFONT help Support reading textual screen maps. config DEFAULT_SETFONT_DIR string "Default directory for console-tools files" default "" depends on SETFONT help Directory to use if setfont's params are simple filenames (not /path/to/file or ./file). Default is "" (no default directory). config SETKEYCODES bool "setkeycodes" default y select PLATFORM_LINUX help This program loads entries into the kernel's scancode-to-keycode map, allowing unusual keyboards to generate usable keycodes. config SETLOGCONS bool "setlogcons" default y select PLATFORM_LINUX help This program redirects the output console of kernel messages. config SHOWKEY bool "showkey" default y select PLATFORM_LINUX help Shows keys pressed. comment "Common options for loadfont and setfont" depends on LOADFONT || SETFONT config FEATURE_LOADFONT_PSF2 bool "Support for PSF2 console fonts" default y depends on LOADFONT || SETFONT help Support PSF2 console fonts. config FEATURE_LOADFONT_RAW bool "Support for old (raw) console fonts" default y depends on LOADFONT || SETFONT help Support old (raw) console fonts. endmenu busybox-1.22.1/console-tools/reset.c0000644000000000000000000000273712263563520016071 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini reset implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * Written by Erik Andersen and Kent Robotti * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BTW, which "standard" package has this utility? It doesn't seem * to be ncurses, coreutils, console-tools... then what? */ //usage:#define reset_trivial_usage //usage: "" //usage:#define reset_full_usage "\n\n" //usage: "Reset the screen" #include "libbb.h" #define ESC "\033" #if ENABLE_STTY int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; #endif int reset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int reset_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { static const char *const args[] = { "stty", "sane", NULL }; /* no options, no getopt */ if (/*isatty(STDIN_FILENO) &&*/ isatty(STDOUT_FILENO)) { /* See 'man 4 console_codes' for details: * "ESC c" -- Reset * "ESC ( B" -- Select G0 Character Set (B = US) * "ESC [ 0 m" -- Reset all display attributes * "ESC [ J" -- Erase to the end of screen * "ESC [ ? 25 h" -- Make cursor visible */ printf(ESC"c" ESC"(B" ESC"[0m" ESC"[J" ESC"[?25h"); /* http://bugs.busybox.net/view.php?id=1414: * people want it to reset echo etc: */ #if ENABLE_STTY return stty_main(2, (char**)args); #else execvp("stty", (char**)args); #endif } return EXIT_SUCCESS; } busybox-1.22.1/console-tools/fgconsole.c0000644000000000000000000000163212263563520016717 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini fgconsole implementation for busybox * * Copyright (C) 2010 by Grigory Batalov * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define fgconsole_trivial_usage //usage: "" //usage:#define fgconsole_full_usage "\n\n" //usage: "Get active console" #include "libbb.h" /* From */ struct vt_stat { unsigned short v_active; /* active vt */ unsigned short v_signal; /* signal to send */ unsigned short v_state; /* vt bitmask */ }; enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */ int fgconsole_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fgconsole_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { struct vt_stat vtstat; vtstat.v_active = 0; xioctl(get_console_fd_or_die(), VT_GETSTATE, &vtstat); printf("%d\n", vtstat.v_active); return EXIT_SUCCESS; } busybox-1.22.1/console-tools/loadkmap.c0000644000000000000000000000461712263563520016536 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini loadkmap implementation for busybox * * Copyright (C) 1998 Enrique Zanardi * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define loadkmap_trivial_usage //usage: "< keymap" //usage:#define loadkmap_full_usage "\n\n" //usage: "Load a binary keyboard translation table from stdin\n" /* //usage: "\n -C TTY Affect TTY instead of /dev/tty" */ //usage: //usage:#define loadkmap_example_usage //usage: "$ loadkmap < /etc/i18n/lang-keymap\n" #include "libbb.h" #define BINARY_KEYMAP_MAGIC "bkeymap" /* From */ struct kbentry { unsigned char kb_table; unsigned char kb_index; unsigned short kb_value; }; /* sets one entry in translation table */ #define KDSKBENT 0x4B47 /* From */ #define NR_KEYS 128 #define MAX_NR_KEYMAPS 256 int loadkmap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int loadkmap_main(int argc UNUSED_PARAM, char **argv) { struct kbentry ke; int i, j, fd; uint16_t ibuff[NR_KEYS]; /* const char *tty_name = CURRENT_TTY; */ RESERVE_CONFIG_BUFFER(flags, MAX_NR_KEYMAPS); /* When user accidentally runs "loadkmap FILE" * instead of "loadkmap # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT lib-$(CONFIG_CHVT) += chvt.o lib-$(CONFIG_FGCONSOLE) += fgconsole.o lib-$(CONFIG_CLEAR) += clear.o lib-$(CONFIG_DEALLOCVT) += deallocvt.o lib-$(CONFIG_DUMPKMAP) += dumpkmap.o lib-$(CONFIG_SETCONSOLE) += setconsole.o lib-$(CONFIG_KBD_MODE) += kbd_mode.o lib-$(CONFIG_LOADFONT) += loadfont.o lib-$(CONFIG_LOADKMAP) += loadkmap.o lib-$(CONFIG_OPENVT) += openvt.o lib-$(CONFIG_RESET) += reset.o lib-$(CONFIG_RESIZE) += resize.o lib-$(CONFIG_SETFONT) += loadfont.o lib-$(CONFIG_SETKEYCODES) += setkeycodes.o lib-$(CONFIG_SETLOGCONS) += setlogcons.o lib-$(CONFIG_SHOWKEY) += showkey.o busybox-1.22.1/console-tools/openvt.c0000644000000000000000000001160012263563520016247 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * openvt.c - open a vt to run a command. * * busyboxed by Quy Tonthat * hacked by Tito * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define openvt_trivial_usage //usage: "[-c N] [-sw] [PROG ARGS]" //usage:#define openvt_full_usage "\n\n" //usage: "Start PROG on a new virtual terminal\n" //usage: "\n -c N Use specified VT" //usage: "\n -s Switch to the VT" /* //usage: "\n -l Run PROG as login shell (by prepending '-')" */ //usage: "\n -w Wait for PROG to exit" //usage: //usage:#define openvt_example_usage //usage: "openvt 2 /bin/ash\n" #include #include "libbb.h" /* "Standard" openvt's man page (we do not support all of this): openvt [-c NUM] [-fsulv] [--] [command [args]] Find the first available VT, and run command on it. Stdio is directed to that VT. If no command is specified then $SHELL is used. -c NUM Use the given VT number, not the first free one. -f Force opening a VT: don't try to check if VT is already in use. -s Switch to the new VT when starting the command. The VT of the new command will be made the new current VT. -u Figure out the owner of the current VT, and run login as that user. Suitable to be called by init. Shouldn't be used with -c or -l. -l Make the command a login shell: a "-" is prepended to the argv[0] when command is executed. -v Verbose. -w Wait for command to complete. If -w and -s are used together, switch back to the controlling terminal when the command completes. bbox: -u: not implemented -f: always in effect -l: not implemented, ignored -v: ignored -ws: does NOT switch back */ /* Helper: does this fd understand VT_xxx? */ static int not_vt_fd(int fd) { struct vt_stat vtstat; return ioctl(fd, VT_GETSTATE, &vtstat); /* !0: error, it's not VT fd */ } /* Helper: get a fd suitable for VT_xxx */ static int get_vt_fd(void) { int fd; /* Do we, by chance, already have it? */ for (fd = 0; fd < 3; fd++) if (!not_vt_fd(fd)) return fd; fd = open(DEV_CONSOLE, O_RDONLY | O_NONBLOCK); if (fd >= 0 && !not_vt_fd(fd)) return fd; bb_error_msg_and_die("can't find open VT"); } static int find_free_vtno(void) { int vtno; int fd = get_vt_fd(); errno = 0; /*xfunc_error_retval = 3; - do we need compat? */ if (ioctl(fd, VT_OPENQRY, &vtno) != 0 || vtno <= 0) bb_perror_msg_and_die("can't find open VT"); // Not really needed, grep for DAEMON_ONLY_SANITIZE // if (fd > 2) // close(fd); return vtno; } /* vfork scares gcc, it generates bigger code. * Keep it away from main program. * TODO: move to libbb; or adapt existing libbb's spawn(). */ static NOINLINE void vfork_child(char **argv) { if (vfork() == 0) { /* CHILD */ /* Try to make this VT our controlling tty */ setsid(); /* lose old ctty */ ioctl(STDIN_FILENO, TIOCSCTTY, 0 /* 0: don't forcibly steal */); //bb_error_msg("our sid %d", getsid(0)); //bb_error_msg("our pgrp %d", getpgrp()); //bb_error_msg("VT's sid %d", tcgetsid(0)); //bb_error_msg("VT's pgrp %d", tcgetpgrp(0)); BB_EXECVP_or_die(argv); } } int openvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int openvt_main(int argc UNUSED_PARAM, char **argv) { char vtname[sizeof(VC_FORMAT) + sizeof(int)*3]; struct vt_stat vtstat; char *str_c; int vtno; int flags; enum { OPT_c = (1 << 0), OPT_w = (1 << 1), OPT_s = (1 << 2), OPT_l = (1 << 3), OPT_f = (1 << 4), OPT_v = (1 << 5), }; /* "+" - stop on first non-option */ flags = getopt32(argv, "+c:wslfv", &str_c); argv += optind; if (flags & OPT_c) { /* Check for illegal vt number: < 1 or > 63 */ vtno = xatou_range(str_c, 1, 63); } else { vtno = find_free_vtno(); } /* Grab new VT */ sprintf(vtname, VC_FORMAT, vtno); /* (Try to) clean up stray open fds above fd 2 */ bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS | DAEMON_ONLY_SANITIZE, NULL); close(STDIN_FILENO); /*setsid(); - BAD IDEA: after we exit, child is SIGHUPed... */ xopen(vtname, O_RDWR); xioctl(STDIN_FILENO, VT_GETSTATE, &vtstat); if (flags & OPT_s) { console_make_active(STDIN_FILENO, vtno); } if (!argv[0]) { argv--; argv[0] = (char *) get_shell_name(); /*argv[1] = NULL; - already is */ } xdup2(STDIN_FILENO, STDOUT_FILENO); xdup2(STDIN_FILENO, STDERR_FILENO); #ifdef BLOAT { /* Handle -l (login shell) option */ const char *prog = argv[0]; if (flags & OPT_l) argv[0] = xasprintf("-%s", argv[0]); } #endif vfork_child(argv); if (flags & OPT_w) { /* We have only one child, wait for it */ safe_waitpid(-1, NULL, 0); /* loops on EINTR */ if (flags & OPT_s) { console_make_active(STDIN_FILENO, vtstat.v_active); // Compat: even with -c N (try to) disallocate: // # /usr/app/kbd-1.12/bin/openvt -f -c 9 -ws sleep 5 // openvt: could not deallocate console 9 xioctl(STDIN_FILENO, VT_DISALLOCATE, (void*)(ptrdiff_t)vtno); } } return EXIT_SUCCESS; } busybox-1.22.1/console-tools/setlogcons.c0000644000000000000000000000160612263563520017121 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * setlogcons: Send kernel messages to the current console or to console N * * Copyright (C) 2006 by Jan Kiszka * * Based on setlogcons (kbd-1.12) by Andries E. Brouwer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define setlogcons_trivial_usage //usage: "N" //usage:#define setlogcons_full_usage "\n\n" //usage: "Redirect the kernel output to console N (0 for current)" #include "libbb.h" int setlogcons_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int setlogcons_main(int argc UNUSED_PARAM, char **argv) { struct { char fn; char subarg; } arg = { 11, /* redirect kernel messages */ 0 /* to specified console (current as default) */ }; if (argv[1]) arg.subarg = xatou_range(argv[1], 0, 63); xioctl(xopen(VC_1, O_RDONLY), TIOCLINUX, &arg); return EXIT_SUCCESS; } busybox-1.22.1/console-tools/kbd_mode.c0000644000000000000000000000345512263563520016511 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini kbd_mode implementation for busybox * * Copyright (C) 2007 Loic Grenie * written using Andries Brouwer 's kbd_mode from * console-utils v0.2.3, licensed under GNU GPLv2 * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define kbd_mode_trivial_usage //usage: "[-a|k|s|u] [-C TTY]" //usage:#define kbd_mode_full_usage "\n\n" //usage: "Report or set the keyboard mode\n" //usage: "\n -a Default (ASCII)" //usage: "\n -k Medium-raw (keyboard)" //usage: "\n -s Raw (scancode)" //usage: "\n -u Unicode (utf-8)" //usage: "\n -C TTY Affect TTY instead of /dev/tty" #include "libbb.h" #include int kbd_mode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int kbd_mode_main(int argc UNUSED_PARAM, char **argv) { enum { SCANCODE = (1 << 0), ASCII = (1 << 1), MEDIUMRAW = (1 << 2), UNICODE = (1 << 3), }; int fd; unsigned opt; const char *tty_name = CURRENT_TTY; opt = getopt32(argv, "sakuC:", &tty_name); fd = xopen_nonblocking(tty_name); opt &= 0xf; /* clear -C bit, see (*) */ if (!opt) { /* print current setting */ const char *mode = "unknown"; int m; xioctl(fd, KDGKBMODE, &m); if (m == K_RAW) mode = "raw (scancode)"; else if (m == K_XLATE) mode = "default (ASCII)"; else if (m == K_MEDIUMRAW) mode = "mediumraw (keycode)"; else if (m == K_UNICODE) mode = "Unicode (UTF-8)"; printf("The keyboard is in %s mode\n", mode); } else { /* here we depend on specific bits assigned to options (*) */ opt = opt & UNICODE ? 3 : opt >> 1; /* double cast prevents warnings about widening conversion */ xioctl(fd, KDSKBMODE, (void*)(ptrdiff_t)opt); } if (ENABLE_FEATURE_CLEAN_UP) close(fd); return EXIT_SUCCESS; } busybox-1.22.1/console-tools/deallocvt.c0000644000000000000000000000200712263563520016712 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Disallocate virtual terminal(s) * * Copyright (C) 2003 by Tito Ragusa * Copyright (C) 1999-2004 by Erik Andersen * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* no options, no getopt */ //usage:#define deallocvt_trivial_usage //usage: "[N]" //usage:#define deallocvt_full_usage "\n\n" //usage: "Deallocate unused virtual terminal /dev/ttyN" #include "libbb.h" /* From */ enum { VT_DISALLOCATE = 0x5608 }; /* free memory associated to vt */ int deallocvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int deallocvt_main(int argc UNUSED_PARAM, char **argv) { /* num = 0 deallocate all unused consoles */ int num = 0; if (argv[1]) { if (argv[2]) bb_show_usage(); num = xatou_range(argv[1], 1, 63); } /* double cast suppresses "cast to ptr from int of different size" */ xioctl(get_console_fd_or_die(), VT_DISALLOCATE, (void *)(ptrdiff_t)num); return EXIT_SUCCESS; } busybox-1.22.1/console-tools/setkeycodes.c0000644000000000000000000000273312263563520017265 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * setkeycodes * * Copyright (C) 1994-1998 Andries E. Brouwer * * Adjusted for BusyBox by Erik Andersen * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define setkeycodes_trivial_usage //usage: "SCANCODE KEYCODE..." //usage:#define setkeycodes_full_usage "\n\n" //usage: "Set entries into the kernel's scancode-to-keycode map,\n" //usage: "allowing unusual keyboards to generate usable keycodes.\n\n" //usage: "SCANCODE may be either xx or e0xx (hexadecimal),\n" //usage: "and KEYCODE is given in decimal." //usage: //usage:#define setkeycodes_example_usage //usage: "$ setkeycodes e030 127\n" #include "libbb.h" /* From */ struct kbkeycode { unsigned scancode, keycode; }; enum { KDSETKEYCODE = 0x4B4D /* write kernel keycode table entry */ }; int setkeycodes_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int setkeycodes_main(int argc, char **argv) { int fd; struct kbkeycode a; if (!(argc & 1) /* if even */ || argc < 2) { bb_show_usage(); } fd = get_console_fd_or_die(); while (argv[1]) { int sc = xstrtoul_range(argv[1], 16, 0, 0xe07f); if (sc >= 0xe000) { sc -= 0xe000; sc += 0x0080; } a.scancode = sc; a.keycode = xatou_range(argv[2], 0, 255); ioctl_or_perror_and_die(fd, KDSETKEYCODE, &a, "can't set SCANCODE %x to KEYCODE %d", sc, a.keycode); argv += 2; } return EXIT_SUCCESS; } busybox-1.22.1/console-tools/chvt.c0000644000000000000000000000120212263563520015675 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini chvt implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define chvt_trivial_usage //usage: "N" //usage:#define chvt_full_usage "\n\n" //usage: "Change the foreground virtual terminal to /dev/ttyN" #include "libbb.h" int chvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int chvt_main(int argc UNUSED_PARAM, char **argv) { int num = xatou_range(single_argv(argv), 1, 63); console_make_active(get_console_fd_or_die(), num); return EXIT_SUCCESS; } busybox-1.22.1/console-tools/clear.c0000644000000000000000000000111012263563520016015 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini clear implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define clear_trivial_usage //usage: "" //usage:#define clear_full_usage "\n\n" //usage: "Clear screen" #include "libbb.h" int clear_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int clear_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { /* home; clear to the end of screen */ return full_write1_str("\033[H""\033[J") != 6; } busybox-1.22.1/Config.in0000644000000000000000000006437112263563520013542 0ustar rootroot# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # mainmenu "BusyBox Configuration" config HAVE_DOT_CONFIG bool default y menu "Busybox Settings" menu "General Configuration" config DESKTOP bool "Enable options for full-blown desktop systems" default y help Enable options and features which are not essential. Select this only if you plan to use busybox on full-blown desktop machine with common Linux distro, not on an embedded box. config EXTRA_COMPAT bool "Provide compatible behavior for rare corner cases (bigger code)" default n help This option makes grep, sed etc handle rare corner cases (embedded NUL bytes and such). This makes code bigger and uses some GNU extensions in libc. You probably only need this option if you plan to run busybox on desktop. config INCLUDE_SUSv2 bool "Enable obsolete features removed before SUSv3" default y help This option will enable backwards compatibility with SuSv2, specifically, old-style numeric options ('command -1 ') will be supported in head, tail, and fold. (Note: should affect renice too.) config USE_PORTABLE_CODE bool "Avoid using GCC-specific code constructs" default n help Use this option if you are trying to compile busybox with compiler other than gcc. If you do use gcc, this option may needlessly increase code size. config PLATFORM_LINUX bool "Enable Linux-specific applets and features" default y help For the most part, busybox requires only POSIX compatibility from the target system, but some applets and features use Linux-specific interfaces. Answering 'N' here will disable such applets and hide the corresponding configuration options. choice prompt "Buffer allocation policy" default FEATURE_BUFFERS_USE_MALLOC help There are 3 ways BusyBox can handle buffer allocations: - Use malloc. This costs code size for the call to xmalloc. - Put them on stack. For some very small machines with limited stack space, this can be deadly. For most folks, this works just fine. - Put them in BSS. This works beautifully for computers with a real MMU (and OS support), but wastes runtime RAM for uCLinux. This behavior was the only one available for BusyBox versions 0.48 and earlier. config FEATURE_BUFFERS_USE_MALLOC bool "Allocate with Malloc" config FEATURE_BUFFERS_GO_ON_STACK bool "Allocate on the Stack" config FEATURE_BUFFERS_GO_IN_BSS bool "Allocate in the .bss section" endchoice config SHOW_USAGE bool "Show applet usage messages" default y help Enabling this option, BusyBox applets will show terse help messages when invoked with wrong arguments. If you do not want to show any (helpful) usage message when issuing wrong command syntax, you can say 'N' here, saving approximately 7k. config FEATURE_VERBOSE_USAGE bool "Show verbose applet usage messages" default y depends on SHOW_USAGE help All BusyBox applets will show verbose help messages when busybox is invoked with --help. This will add a lot of text to the busybox binary. In the default configuration, this will add about 13k, but it can add much more depending on your configuration. config FEATURE_COMPRESS_USAGE bool "Store applet usage messages in compressed form" default y depends on SHOW_USAGE help Store usage messages in .bz compressed form, uncompress them on-the-fly when --help is called. If you have a really tiny busybox with few applets enabled (and bunzip2 isn't one of them), the overhead of the decompressor might be noticeable. Also, if you run executables directly from ROM and have very little memory, this might not be a win. Otherwise, you probably want this. config FEATURE_INSTALLER bool "Support --install [-s] to install applet links at runtime" default y help Enable 'busybox --install [-s]' support. This will allow you to use busybox at runtime to create hard links or symlinks for all the applets that are compiled into busybox. config INSTALL_NO_USR bool "Don't use /usr" default n help Disable use of /usr. busybox --install and "make install" will install applets only to /bin and /sbin, never to /usr/bin or /usr/sbin. config LOCALE_SUPPORT bool "Enable locale support (system needs locale for this to work)" default n help Enable this if your system has locale support and you would like busybox to support locale settings. config UNICODE_SUPPORT bool "Support Unicode" default y help This makes various applets aware that one byte is not one character on screen. Busybox aims to eventually work correctly with Unicode displays. Any older encodings are not guaranteed to work. Probably by the time when busybox will be fully Unicode-clean, other encodings will be mainly of historic interest. config UNICODE_USING_LOCALE bool "Use libc routines for Unicode (else uses internal ones)" default n depends on UNICODE_SUPPORT && LOCALE_SUPPORT help With this option on, Unicode support is implemented using libc routines. Otherwise, internal implementation is used. Internal implementation is smaller. config FEATURE_CHECK_UNICODE_IN_ENV bool "Check $LC_ALL, $LC_CTYPE and $LANG environment variables" default n depends on UNICODE_SUPPORT && !UNICODE_USING_LOCALE help With this option on, Unicode support is activated only if locale-related variables have the value of the form "xxxx.utf8" Otherwise, Unicode support will be always enabled and active. config SUBST_WCHAR int "Character code to substitute unprintable characters with" depends on UNICODE_SUPPORT default 63 help Typical values are 63 for '?' (works with any output device), 30 for ASCII substitute control code, 65533 (0xfffd) for Unicode replacement character. config LAST_SUPPORTED_WCHAR int "Range of supported Unicode characters" depends on UNICODE_SUPPORT default 767 help Any character with Unicode value bigger than this is assumed to be non-printable on output device. Many applets replace such chars with substitution character. The idea is that many valid printable Unicode chars are nevertheless are not displayed correctly. Think about combining charachers, double-wide hieroglyphs, obscure characters in dozens of ancient scripts... Many terminals, terminal emulators, xterms etc will fail to handle them correctly. Choose the smallest value which suits your needs. Typical values are: 126 - ASCII only 767 (0x2ff) - there are no combining chars in [0..767] range (the range includes Latin 1, Latin Ext. A and B), code is ~700 bytes smaller for this case. 4351 (0x10ff) - there are no double-wide chars in [0..4351] range, code is ~300 bytes smaller for this case. 12799 (0x31ff) - nearly all non-ideographic characters are available in [0..12799] range, including East Asian scripts like katakana, hiragana, hangul, bopomofo... 0 - off, any valid printable Unicode character will be printed. config UNICODE_COMBINING_WCHARS bool "Allow zero-width Unicode characters on output" default n depends on UNICODE_SUPPORT help With this option off, any Unicode char with width of 0 is substituted on output. config UNICODE_WIDE_WCHARS bool "Allow wide Unicode characters on output" default n depends on UNICODE_SUPPORT help With this option off, any Unicode char with width > 1 is substituted on output. config UNICODE_BIDI_SUPPORT bool "Bidirectional character-aware line input" default n depends on UNICODE_SUPPORT && !UNICODE_USING_LOCALE help With this option on, right-to-left Unicode characters are treated differently on input (e.g. cursor movement). config UNICODE_NEUTRAL_TABLE bool "In bidi input, support non-ASCII neutral chars too" default n depends on UNICODE_BIDI_SUPPORT help In most cases it's enough to treat only ASCII non-letters (i.e. punctuation, numbers and space) as characters with neutral directionality. With this option on, more extensive (and bigger) table of neutral chars will be used. config UNICODE_PRESERVE_BROKEN bool "Make it possible to enter sequences of chars which are not Unicode" default n depends on UNICODE_SUPPORT help With this option on, on line-editing input (such as used by shells) invalid UTF-8 bytes are not substituted with the selected substitution character. For example, this means that entering 'l', 's', ' ', 0xff, [Enter] at shell prompt will list file named 0xff (single char name with char value 255), not file named '?'. config LONG_OPTS bool "Support for --long-options" default y help Enable this if you want busybox applets to use the gnu --long-option style, in addition to single character -a -b -c style options. config FEATURE_DEVPTS bool "Use the devpts filesystem for Unix98 PTYs" default y help Enable if you want BusyBox to use Unix98 PTY support. If enabled, busybox will use /dev/ptmx for the master side of the pseudoterminal and /dev/pts/ for the slave side. Otherwise, BSD style /dev/ttyp will be used. To use this option, you should have devpts mounted. config FEATURE_CLEAN_UP bool "Clean up all memory before exiting (usually not needed)" default n help As a size optimization, busybox normally exits without explicitly freeing dynamically allocated memory or closing files. This saves space since the OS will clean up for us, but it can confuse debuggers like valgrind, which report tons of memory and resource leaks. Don't enable this unless you have a really good reason to clean things up manually. config FEATURE_UTMP bool "Support utmp file" default y help The file /var/run/utmp is used to track who is currently logged in. With this option on, certain applets (getty, login, telnetd etc) will create and delete entries there. "who" applet requires this option. config FEATURE_WTMP bool "Support wtmp file" default y depends on FEATURE_UTMP help The file /var/run/wtmp is used to track when users have logged into and logged out of the system. With this option on, certain applets (getty, login, telnetd etc) will append new entries there. "last" applet requires this option. config FEATURE_PIDFILE bool "Support writing pidfiles" default y help This option makes some applets (e.g. crond, syslogd, inetd) write a pidfile at the configured PID_FILE_PATH. It has no effect on applets which require pidfiles to run. config PID_FILE_PATH string "Path to directory for pidfile" default "/var/run" depends on FEATURE_PIDFILE help This is the default path where pidfiles are created. Applets which allow you to set the pidfile path on the command line will override this value. The option has no effect on applets that require you to specify a pidfile path. config FEATURE_SUID bool "Support for SUID/SGID handling" default y help With this option you can install the busybox binary belonging to root with the suid bit set, enabling some applets to perform root-level operations even when run by ordinary users (for example, mounting of user mounts in fstab needs this). Busybox will automatically drop privileges for applets that don't need root access. If you are really paranoid and don't want to do this, build two busybox binaries with different applets in them (and the appropriate symlinks pointing to each binary), and only set the suid bit on the one that needs it. The applets which require root rights (need suid bit or to be run by root) and will refuse to execute otherwise: crontab, login, passwd, su, vlock, wall. The applets which will use root rights if they have them (via suid bit, or because run by root), but would try to work without root right nevertheless: findfs, ping[6], traceroute[6], mount. Note that if you DONT select this option, but DO make busybox suid root, ALL applets will run under root, which is a huge security hole (think "cp /some/file /etc/passwd"). config FEATURE_SUID_CONFIG bool "Runtime SUID/SGID configuration via /etc/busybox.conf" default y depends on FEATURE_SUID help Allow the SUID / SGID state of an applet to be determined at runtime by checking /etc/busybox.conf. (This is sort of a poor man's sudo.) The format of this file is as follows: APPLET = [Ssx-][Ssx-][x-] [USER.GROUP] s: USER or GROUP is allowed to execute APPLET. APPLET will run under USER or GROUP (reagardless of who's running it). S: USER or GROUP is NOT allowed to execute APPLET. APPLET will run under USER or GROUP. This option is not very sensical. x: USER/GROUP/others are allowed to execute APPLET. No UID/GID change will be done when it is run. -: USER/GROUP/others are not allowed to execute APPLET. An example might help: [SUID] su = ssx root.0 # applet su can be run by anyone and runs with # euid=0/egid=0 su = ssx # exactly the same mount = sx- root.disk # applet mount can be run by root and members # of group disk (but not anyone else) # and runs with euid=0 (egid is not changed) cp = --- # disable applet cp for everyone The file has to be owned by user root, group root and has to be writeable only by root: (chown 0.0 /etc/busybox.conf; chmod 600 /etc/busybox.conf) The busybox executable has to be owned by user root, group root and has to be setuid root for this to work: (chown 0.0 /bin/busybox; chmod 4755 /bin/busybox) Robert 'sandman' Griebl has more information here: . config FEATURE_SUID_CONFIG_QUIET bool "Suppress warning message if /etc/busybox.conf is not readable" default y depends on FEATURE_SUID_CONFIG help /etc/busybox.conf should be readable by the user needing the SUID, check this option to avoid users to be notified about missing permissions. config SELINUX bool "Support NSA Security Enhanced Linux" default n select PLATFORM_LINUX help Enable support for SELinux in applets ls, ps, and id. Also provide the option of compiling in SELinux applets. If you do not have a complete SELinux userland installed, this stuff will not compile. Go visit http://www.nsa.gov/selinux/index.html to download the necessary stuff to allow busybox to compile with this option enabled. Specifially, libselinux 1.28 or better is directly required by busybox. If the installation is located in a non-standard directory, provide it by invoking make as follows: CFLAGS=-I \ LDFLAGS=-L \ make Most people will leave this set to 'N'. config FEATURE_PREFER_APPLETS bool "exec prefers applets" default n help This is an experimental option which directs applets about to call 'exec' to try and find an applicable busybox applet before searching the PATH. This is typically done by exec'ing /proc/self/exe. This may affect shell, find -exec, xargs and similar applets. They will use applets even if /bin/ -> busybox link is missing (or is not a link to busybox). However, this causes problems in chroot jails without mounted /proc and with ps/top (command name can be shown as 'exe' for applets started this way). config BUSYBOX_EXEC_PATH string "Path to BusyBox executable" default "/proc/self/exe" help When Busybox applets need to run other busybox applets, BusyBox sometimes needs to exec() itself. When the /proc filesystem is mounted, /proc/self/exe always points to the currently running executable. If you haven't got /proc, set this to wherever you want to run BusyBox from. # These are auto-selected by other options config FEATURE_SYSLOG bool #No description makes it a hidden option default n #help # This option is auto-selected when you select any applet which may # send its output to syslog. You do not need to select it manually. config FEATURE_HAVE_RPC bool #No description makes it a hidden option default n #help # This is automatically selected if any of enabled applets need it. # You do not need to select it manually. endmenu menu 'Build Options' config STATIC bool "Build BusyBox as a static binary (no shared libs)" default n help If you want to build a static BusyBox binary, which does not use or require any shared libraries, then enable this option. This can cause BusyBox to be considerably larger, so you should leave this option false unless you have a good reason (i.e. your target platform does not support shared libraries, or you are building an initrd which doesn't need anything but BusyBox, etc). Most people will leave this set to 'N'. config PIE bool "Build BusyBox as a position independent executable" default n depends on !STATIC help Hardened code option. PIE binaries are loaded at a different address at each invocation. This has some overhead, particularly on x86-32 which is short on registers. Most people will leave this set to 'N'. config NOMMU bool "Force NOMMU build" default n help Busybox tries to detect whether architecture it is being built against supports MMU or not. If this detection fails, or if you want to build NOMMU version of busybox for testing, you may force NOMMU build here. Most people will leave this set to 'N'. # PIE can be made to work with BUILD_LIBBUSYBOX, but currently # build system does not support that config BUILD_LIBBUSYBOX bool "Build shared libbusybox" default n depends on !FEATURE_PREFER_APPLETS && !PIE && !STATIC help Build a shared library libbusybox.so.N.N.N which contains all busybox code. This feature allows every applet to be built as a tiny separate executable. Enabling it for "one big busybox binary" approach serves no purpose and increases code size. You should almost certainly say "no" to this. ### config FEATURE_FULL_LIBBUSYBOX ### bool "Feature-complete libbusybox" ### default n if !FEATURE_SHARED_BUSYBOX ### depends on BUILD_LIBBUSYBOX ### help ### Build a libbusybox with the complete feature-set, disregarding ### the actually selected config. ### ### Normally, libbusybox will only contain the features which are ### used by busybox itself. If you plan to write a separate ### standalone application which uses libbusybox say 'Y'. ### ### Note: libbusybox is GPL, not LGPL, and exports no stable API that ### might act as a copyright barrier. We can and will modify the ### exported function set between releases (even minor version number ### changes), and happily break out-of-tree features. ### ### Say 'N' if in doubt. config FEATURE_INDIVIDUAL bool "Produce a binary for each applet, linked against libbusybox" default y depends on BUILD_LIBBUSYBOX help If your CPU architecture doesn't allow for sharing text/rodata sections of running binaries, but allows for runtime dynamic libraries, this option will allow you to reduce memory footprint when you have many different applets running at once. If your CPU architecture allows for sharing text/rodata, having single binary is more optimal. Each applet will be a tiny program, dynamically linked against libbusybox.so.N.N.N. You need to have a working dynamic linker. config FEATURE_SHARED_BUSYBOX bool "Produce additional busybox binary linked against libbusybox" default y depends on BUILD_LIBBUSYBOX help Build busybox, dynamically linked against libbusybox.so.N.N.N. You need to have a working dynamic linker. ### config BUILD_AT_ONCE ### bool "Compile all sources at once" ### default n ### help ### Normally each source-file is compiled with one invocation of ### the compiler. ### If you set this option, all sources are compiled at once. ### This gives the compiler more opportunities to optimize which can ### result in smaller and/or faster binaries. ### ### Setting this option will consume alot of memory, e.g. if you ### enable all applets with all features, gcc uses more than 300MB ### RAM during compilation of busybox. ### ### This option is most likely only beneficial for newer compilers ### such as gcc-4.1 and above. ### ### Say 'N' unless you know what you are doing. config LFS bool "Build with Large File Support (for accessing files > 2 GB)" default y help If you want to build BusyBox with large file support, then enable this option. This will have no effect if your kernel or your C library lacks large file support for large files. Some of the programs that can benefit from large file support include dd, gzip, cp, mount, tar, and many others. If you want to access files larger than 2 Gigabytes, enable this option. Otherwise, leave it set to 'N'. config CROSS_COMPILER_PREFIX string "Cross Compiler prefix" default "" help If you want to build BusyBox with a cross compiler, then you will need to set this to the cross-compiler prefix, for example, "i386-uclibc-". Note that CROSS_COMPILE environment variable or "make CROSS_COMPILE=xxx ..." will override this selection. Native builds leave this empty. config SYSROOT string "Path to sysroot" default "" help If you want to build BusyBox with a cross compiler, then you might also need to specify where /usr/include and /usr/lib will be found. For example, BusyBox can be built against an installed Android NDK, platform version 9, for ARM ABI with CONFIG_SYSROOT=/opt/android-ndk/platforms/android-9/arch-arm Native builds leave this empty. config EXTRA_CFLAGS string "Additional CFLAGS" default "" help Additional CFLAGS to pass to the compiler verbatim. config EXTRA_LDFLAGS string "Additional LDFLAGS" default "" help Additional LDFLAGS to pass to the linker verbatim. config EXTRA_LDLIBS string "Additional LDLIBS" default "" help Additional LDLIBS to pass to the linker with -l. endmenu menu 'Debugging Options' config DEBUG bool "Build BusyBox with extra Debugging symbols" default n help Say Y here if you wish to examine BusyBox internals while applets are running. This increases the size of the binary considerably, and should only be used when doing development. If you are doing development and want to debug BusyBox, answer Y. Most people should answer N. config DEBUG_PESSIMIZE bool "Disable compiler optimizations" default n depends on DEBUG help The compiler's optimization of source code can eliminate and reorder code, resulting in an executable that's hard to understand when stepping through it with a debugger. This switches it off, resulting in a much bigger executable that more closely matches the source code. config WERROR bool "Abort compilation on any warning" default n help Selecting this will add -Werror to gcc command line. Most people should answer N. choice prompt "Additional debugging library" default NO_DEBUG_LIB help Using an additional debugging library will make BusyBox become considerable larger and will cause it to run more slowly. You should always leave this option disabled for production use. dmalloc support: ---------------- This enables compiling with dmalloc ( http://dmalloc.com/ ) which is an excellent public domain mem leak and malloc problem detector. To enable dmalloc, before running busybox you will want to properly set your environment, for example: export DMALLOC_OPTIONS=debug=0x34f47d83,inter=100,log=logfile The 'debug=' value is generated using the following command dmalloc -p log-stats -p log-non-free -p log-bad-space \ -p log-elapsed-time -p check-fence -p check-heap \ -p check-lists -p check-blank -p check-funcs -p realloc-copy \ -p allow-free-null Electric-fence support: ----------------------- This enables compiling with Electric-fence support. Electric fence is another very useful malloc debugging library which uses your computer's virtual memory hardware to detect illegal memory accesses. This support will make BusyBox be considerable larger and run slower, so you should leave this option disabled unless you are hunting a hard to find memory problem. config NO_DEBUG_LIB bool "None" config DMALLOC bool "Dmalloc" config EFENCE bool "Electric-fence" endchoice endmenu menu 'Installation Options ("make install" behavior)' choice prompt "What kind of applet links to install" default INSTALL_APPLET_SYMLINKS help Choose what kind of links to applets are created by "make install". config INSTALL_APPLET_SYMLINKS bool "as soft-links" help Install applets as soft-links to the busybox binary. This needs some free inodes on the filesystem, but might help with filesystem generators that can't cope with hard-links. config INSTALL_APPLET_HARDLINKS bool "as hard-links" help Install applets as hard-links to the busybox binary. This might count on a filesystem with few inodes. config INSTALL_APPLET_SCRIPT_WRAPPERS bool "as script wrappers" help Install applets as script wrappers that call the busybox binary. config INSTALL_APPLET_DONT bool "not installed" help Do not install applet links. Useful when you plan to use busybox --install for installing links, or plan to use a standalone shell and thus don't need applet links. endchoice choice prompt "/bin/sh applet link" default INSTALL_SH_APPLET_SYMLINK depends on INSTALL_APPLET_SCRIPT_WRAPPERS help Choose how you install /bin/sh applet link. config INSTALL_SH_APPLET_SYMLINK bool "as soft-link" help Install /bin/sh applet as soft-link to the busybox binary. config INSTALL_SH_APPLET_HARDLINK bool "as hard-link" help Install /bin/sh applet as hard-link to the busybox binary. config INSTALL_SH_APPLET_SCRIPT_WRAPPER bool "as script wrapper" help Install /bin/sh applet as script wrapper that calls the busybox binary. endchoice config PREFIX string "BusyBox installation prefix" default "./_install" help Define your directory to install BusyBox files/subdirs in. endmenu source libbb/Config.in endmenu comment "Applets" source archival/Config.in source coreutils/Config.in source console-tools/Config.in source debianutils/Config.in source editors/Config.in source findutils/Config.in source init/Config.in source loginutils/Config.in source e2fsprogs/Config.in source modutils/Config.in source util-linux/Config.in source miscutils/Config.in source networking/Config.in source printutils/Config.in source mailutils/Config.in source procps/Config.in source runit/Config.in source selinux/Config.in source shell/Config.in source sysklogd/Config.in busybox-1.22.1/networking/0000755000000000000000000000000012320365455014162 5ustar rootrootbusybox-1.22.1/networking/wget.c0000644000000000000000000006757012320365455015313 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * wget - retrieve a file using HTTP or FTP * * Chip Rosenthal Covad Communications * Licensed under GPLv2, see file LICENSE in this source tree. * * Copyright (C) 2010 Bradley M. Kuhn * Kuhn's copyrights are licensed GPLv2-or-later. File as a whole remains GPLv2. */ //usage:#define wget_trivial_usage //usage: IF_FEATURE_WGET_LONG_OPTIONS( //usage: "[-c|--continue] [-s|--spider] [-q|--quiet] [-O|--output-document FILE]\n" //usage: " [--header 'header: value'] [-Y|--proxy on/off] [-P DIR]\n" /* Since we ignore these opts, we don't show them in --help */ /* //usage: " [--no-check-certificate] [--no-cache]" */ //usage: " [-U|--user-agent AGENT]" IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..." //usage: ) //usage: IF_NOT_FEATURE_WGET_LONG_OPTIONS( //usage: "[-csq] [-O FILE] [-Y on/off] [-P DIR] [-U AGENT]" //usage: IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..." //usage: ) //usage:#define wget_full_usage "\n\n" //usage: "Retrieve files via HTTP or FTP\n" //usage: "\n -s Spider mode - only check file existence" //usage: "\n -c Continue retrieval of aborted transfer" //usage: "\n -q Quiet" //usage: "\n -P DIR Save to DIR (default .)" //usage: IF_FEATURE_WGET_TIMEOUT( //usage: "\n -T SEC Network read timeout is SEC seconds" //usage: ) //usage: "\n -O FILE Save to FILE ('-' for stdout)" //usage: "\n -U STR Use STR for User-Agent header" //usage: "\n -Y Use proxy ('on' or 'off')" #include "libbb.h" #if 0 # define log_io(...) bb_error_msg(__VA_ARGS__) #else # define log_io(...) ((void)0) #endif struct host_info { char *allocated; const char *path; const char *user; char *host; int port; smallint is_ftp; }; /* Globals */ struct globals { off_t content_len; /* Content-length of the file */ off_t beg_range; /* Range at which continue begins */ #if ENABLE_FEATURE_WGET_STATUSBAR off_t transferred; /* Number of bytes transferred so far */ const char *curfile; /* Name of current file being transferred */ bb_progress_t pmt; #endif char *dir_prefix; #if ENABLE_FEATURE_WGET_LONG_OPTIONS char *post_data; char *extra_headers; #endif char *fname_out; /* where to direct output (-O) */ const char *proxy_flag; /* Use proxies if env vars are set */ const char *user_agent; /* "User-Agent" header field */ #if ENABLE_FEATURE_WGET_TIMEOUT unsigned timeout_seconds; bool connecting; #endif int output_fd; int o_flags; smallint chunked; /* chunked transfer encoding */ smallint got_clen; /* got content-length: from server */ /* Local downloads do benefit from big buffer. * With 512 byte buffer, it was measured to be * an order of magnitude slower than with big one. */ uint64_t just_to_align_next_member; char wget_buf[CONFIG_FEATURE_COPYBUF_KB*1024]; } FIX_ALIASING; #define G (*ptr_to_globals) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ } while (0) #define FINI_G() do { \ FREE_PTR_TO_GLOBALS(); \ } while (0) /* Must match option string! */ enum { WGET_OPT_CONTINUE = (1 << 0), WGET_OPT_SPIDER = (1 << 1), WGET_OPT_QUIET = (1 << 2), WGET_OPT_OUTNAME = (1 << 3), WGET_OPT_PREFIX = (1 << 4), WGET_OPT_PROXY = (1 << 5), WGET_OPT_USER_AGENT = (1 << 6), WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 7), WGET_OPT_RETRIES = (1 << 8), WGET_OPT_PASSIVE = (1 << 9), WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS, WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS, }; enum { PROGRESS_START = -1, PROGRESS_END = 0, PROGRESS_BUMP = 1, }; #if ENABLE_FEATURE_WGET_STATUSBAR static void progress_meter(int flag) { if (option_mask32 & WGET_OPT_QUIET) return; if (flag == PROGRESS_START) bb_progress_init(&G.pmt, G.curfile); bb_progress_update(&G.pmt, G.beg_range, G.transferred, (G.chunked || !G.got_clen) ? 0 : G.beg_range + G.transferred + G.content_len ); if (flag == PROGRESS_END) { bb_progress_free(&G.pmt); bb_putchar_stderr('\n'); G.transferred = 0; } } #else static ALWAYS_INLINE void progress_meter(int flag UNUSED_PARAM) { } #endif /* IPv6 knows scoped address types i.e. link and site local addresses. Link * local addresses can have a scope identifier to specify the * interface/link an address is valid on (e.g. fe80::1%eth0). This scope * identifier is only valid on a single node. * * RFC 4007 says that the scope identifier MUST NOT be sent across the wire, * unless all nodes agree on the semantic. Apache e.g. regards zone identifiers * in the Host header as invalid requests, see * https://issues.apache.org/bugzilla/show_bug.cgi?id=35122 */ static void strip_ipv6_scope_id(char *host) { char *scope, *cp; /* bbox wget actually handles IPv6 addresses without [], like * wget "http://::1/xxx", but this is not standard. * To save code, _here_ we do not support it. */ if (host[0] != '[') return; /* not IPv6 */ scope = strchr(host, '%'); if (!scope) return; /* Remove the IPv6 zone identifier from the host address */ cp = strchr(host, ']'); if (!cp || (cp[1] != ':' && cp[1] != '\0')) { /* malformed address (not "[xx]:nn" or "[xx]") */ return; } /* cp points to "]...", scope points to "%eth0]..." */ overlapping_strcpy(scope, cp); } #if ENABLE_FEATURE_WGET_AUTHENTICATION /* Base64-encode character string. */ static char *base64enc(const char *str) { unsigned len = strlen(str); if (len > sizeof(G.wget_buf)/4*3 - 10) /* paranoia */ len = sizeof(G.wget_buf)/4*3 - 10; bb_uuencode(G.wget_buf, str, len, bb_uuenc_tbl_base64); return G.wget_buf; } #endif static char* sanitize_string(char *s) { unsigned char *p = (void *) s; while (*p >= ' ') p++; *p = '\0'; return s; } #if ENABLE_FEATURE_WGET_TIMEOUT static void alarm_handler(int sig UNUSED_PARAM) { /* This is theoretically unsafe (uses stdio and malloc in signal handler) */ if (G.connecting) bb_error_msg_and_die("download timed out"); } #endif static FILE *open_socket(len_and_sockaddr *lsa) { int fd; FILE *fp; IF_FEATURE_WGET_TIMEOUT(alarm(G.timeout_seconds); G.connecting = 1;) fd = xconnect_stream(lsa); IF_FEATURE_WGET_TIMEOUT(G.connecting = 0;) /* glibc 2.4 seems to try seeking on it - ??! */ /* hopefully it understands what ESPIPE means... */ fp = fdopen(fd, "r+"); if (fp == NULL) bb_perror_msg_and_die(bb_msg_memory_exhausted); return fp; } /* Returns '\n' if it was seen, else '\0'. Trims at first '\r' or '\n' */ /* FIXME: does not respect FEATURE_WGET_TIMEOUT and -T N: */ static char fgets_and_trim(FILE *fp) { char c; char *buf_ptr; if (fgets(G.wget_buf, sizeof(G.wget_buf) - 1, fp) == NULL) bb_perror_msg_and_die("error getting response"); buf_ptr = strchrnul(G.wget_buf, '\n'); c = *buf_ptr; *buf_ptr = '\0'; buf_ptr = strchrnul(G.wget_buf, '\r'); *buf_ptr = '\0'; log_io("< %s", G.wget_buf); return c; } static int ftpcmd(const char *s1, const char *s2, FILE *fp) { int result; if (s1) { if (!s2) s2 = ""; fprintf(fp, "%s%s\r\n", s1, s2); fflush(fp); log_io("> %s%s", s1, s2); } do { fgets_and_trim(fp); } while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' '); G.wget_buf[3] = '\0'; result = xatoi_positive(G.wget_buf); G.wget_buf[3] = ' '; return result; } static void parse_url(const char *src_url, struct host_info *h) { char *url, *p, *sp; free(h->allocated); h->allocated = url = xstrdup(src_url); if (strncmp(url, "ftp://", 6) == 0) { h->port = bb_lookup_port("ftp", "tcp", 21); h->host = url + 6; h->is_ftp = 1; } else if (strncmp(url, "http://", 7) == 0) { h->host = url + 7; http: h->port = bb_lookup_port("http", "tcp", 80); h->is_ftp = 0; } else if (!strstr(url, "//")) { // GNU wget is user-friendly and falls back to http:// h->host = url; goto http; } else bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url)); // FYI: // "Real" wget 'http://busybox.net?var=a/b' sends this request: // 'GET /?var=a/b HTTP 1.0' // and saves 'index.html?var=a%2Fb' (we save 'b') // wget 'http://busybox.net?login=john@doe': // request: 'GET /?login=john@doe HTTP/1.0' // saves: 'index.html?login=john@doe' (we save '?login=john@doe') // wget 'http://busybox.net#test/test': // request: 'GET / HTTP/1.0' // saves: 'index.html' (we save 'test') // // We also don't add unique .N suffix if file exists... sp = strchr(h->host, '/'); p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p; p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p; if (!sp) { h->path = ""; } else if (*sp == '/') { *sp = '\0'; h->path = sp + 1; } else { // '#' or '?' // http://busybox.net?login=john@doe is a valid URL // memmove converts to: // http:/busybox.nett?login=john@doe... memmove(h->host - 1, h->host, sp - h->host); h->host--; sp[-1] = '\0'; h->path = sp; } // We used to set h->user to NULL here, but this interferes // with handling of code 302 ("object was moved") sp = strrchr(h->host, '@'); if (sp != NULL) { // URL-decode "user:password" string before base64-encoding: // wget http://test:my%20pass@example.com should send // Authorization: Basic dGVzdDpteSBwYXNz // which decodes to "test:my pass". // Standard wget and curl do this too. *sp = '\0'; h->user = percent_decode_in_place(h->host, /*strict:*/ 0); h->host = sp + 1; } sp = h->host; } static char *gethdr(FILE *fp) { char *s, *hdrval; int c; /* retrieve header line */ c = fgets_and_trim(fp); /* end of the headers? */ if (G.wget_buf[0] == '\0') return NULL; /* convert the header name to lower case */ for (s = G.wget_buf; isalnum(*s) || *s == '-' || *s == '.' || *s == '_'; ++s) { /* * No-op for 20-3f and 60-7f. "0-9a-z-." are in these ranges. * 40-5f range ("@A-Z[\]^_") maps to 60-7f. * "A-Z" maps to "a-z". * "@[\]" can't occur in header names. * "^_" maps to "~,DEL" (which is wrong). * "^" was never seen yet, "_" was seen from web.archive.org * (x-archive-orig-x_commoncrawl_Signature: HEXSTRING). */ *s |= 0x20; } /* verify we are at the end of the header name */ if (*s != ':') bb_error_msg_and_die("bad header line: %s", sanitize_string(G.wget_buf)); /* locate the start of the header value */ *s++ = '\0'; hdrval = skip_whitespace(s); if (c != '\n') { /* Rats! The buffer isn't big enough to hold the entire header value */ while (c = getc(fp), c != EOF && c != '\n') continue; } return hdrval; } static void reset_beg_range_to_zero(void) { bb_error_msg("restart failed"); G.beg_range = 0; xlseek(G.output_fd, 0, SEEK_SET); /* Done at the end instead: */ /* ftruncate(G.output_fd, 0); */ } static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa) { FILE *sfp; char *str; int port; if (!target->user) target->user = xstrdup("anonymous:busybox@"); sfp = open_socket(lsa); if (ftpcmd(NULL, NULL, sfp) != 220) bb_error_msg_and_die("%s", sanitize_string(G.wget_buf + 4)); /* * Splitting username:password pair, * trying to log in */ str = strchr(target->user, ':'); if (str) *str++ = '\0'; switch (ftpcmd("USER ", target->user, sfp)) { case 230: break; case 331: if (ftpcmd("PASS ", str, sfp) == 230) break; /* fall through (failed login) */ default: bb_error_msg_and_die("ftp login: %s", sanitize_string(G.wget_buf + 4)); } ftpcmd("TYPE I", NULL, sfp); /* * Querying file size */ if (ftpcmd("SIZE ", target->path, sfp) == 213) { G.content_len = BB_STRTOOFF(G.wget_buf + 4, NULL, 10); if (G.content_len < 0 || errno) { bb_error_msg_and_die("SIZE value is garbage"); } G.got_clen = 1; } /* * Entering passive mode */ if (ftpcmd("PASV", NULL, sfp) != 227) { pasv_error: bb_error_msg_and_die("bad response to %s: %s", "PASV", sanitize_string(G.wget_buf)); } // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage] // Server's IP is N1.N2.N3.N4 (we ignore it) // Server's port for data connection is P1*256+P2 str = strrchr(G.wget_buf, ')'); if (str) str[0] = '\0'; str = strrchr(G.wget_buf, ','); if (!str) goto pasv_error; port = xatou_range(str+1, 0, 255); *str = '\0'; str = strrchr(G.wget_buf, ','); if (!str) goto pasv_error; port += xatou_range(str+1, 0, 255) * 256; set_nport(&lsa->u.sa, htons(port)); *dfpp = open_socket(lsa); if (G.beg_range != 0) { sprintf(G.wget_buf, "REST %"OFF_FMT"u", G.beg_range); if (ftpcmd(G.wget_buf, NULL, sfp) == 350) G.content_len -= G.beg_range; else reset_beg_range_to_zero(); } if (ftpcmd("RETR ", target->path, sfp) > 150) bb_error_msg_and_die("bad response to %s: %s", "RETR", sanitize_string(G.wget_buf)); return sfp; } static void NOINLINE retrieve_file_data(FILE *dfp) { #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT # if ENABLE_FEATURE_WGET_TIMEOUT unsigned second_cnt = G.timeout_seconds; # endif struct pollfd polldata; polldata.fd = fileno(dfp); polldata.events = POLLIN | POLLPRI; #endif progress_meter(PROGRESS_START); if (G.chunked) goto get_clen; /* Loops only if chunked */ while (1) { #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT /* Must use nonblocking I/O, otherwise fread will loop * and *block* until it reads full buffer, * which messes up progress bar and/or timeout logic. * Because of nonblocking I/O, we need to dance * very carefully around EAGAIN. See explanation at * clearerr() calls. */ ndelay_on(polldata.fd); #endif while (1) { int n; unsigned rdsz; #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT /* fread internally uses read loop, which in our case * is usually exited when we get EAGAIN. * In this case, libc sets error marker on the stream. * Need to clear it before next fread to avoid possible * rare false positive ferror below. Rare because usually * fread gets more than zero bytes, and we don't fall * into if (n <= 0) ... */ clearerr(dfp); #endif errno = 0; rdsz = sizeof(G.wget_buf); if (G.got_clen) { if (G.content_len < (off_t)sizeof(G.wget_buf)) { if ((int)G.content_len <= 0) break; rdsz = (unsigned)G.content_len; } } n = fread(G.wget_buf, 1, rdsz, dfp); if (n > 0) { xwrite(G.output_fd, G.wget_buf, n); #if ENABLE_FEATURE_WGET_STATUSBAR G.transferred += n; #endif if (G.got_clen) { G.content_len -= n; if (G.content_len == 0) break; } #if ENABLE_FEATURE_WGET_TIMEOUT second_cnt = G.timeout_seconds; #endif continue; } /* n <= 0. * man fread: * If error occurs, or EOF is reached, the return value * is a short item count (or zero). * fread does not distinguish between EOF and error. */ if (errno != EAGAIN) { if (ferror(dfp)) { progress_meter(PROGRESS_END); bb_perror_msg_and_die(bb_msg_read_error); } break; /* EOF, not error */ } #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT /* It was EAGAIN. There is no data. Wait up to one second * then abort if timed out, or update the bar and try reading again. */ if (safe_poll(&polldata, 1, 1000) == 0) { # if ENABLE_FEATURE_WGET_TIMEOUT if (second_cnt != 0 && --second_cnt == 0) { progress_meter(PROGRESS_END); bb_error_msg_and_die("download timed out"); } # endif /* We used to loop back to poll here, * but there is no great harm in letting fread * to try reading anyway. */ } /* Need to do it _every_ second for "stalled" indicator * to be shown properly. */ progress_meter(PROGRESS_BUMP); #endif } /* while (reading data) */ #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT clearerr(dfp); ndelay_off(polldata.fd); /* else fgets can get very unhappy */ #endif if (!G.chunked) break; fgets_and_trim(dfp); /* Eat empty line */ get_clen: fgets_and_trim(dfp); G.content_len = STRTOOFF(G.wget_buf, NULL, 16); /* FIXME: error check? */ if (G.content_len == 0) break; /* all done! */ G.got_clen = 1; /* * Note that fgets may result in some data being buffered in dfp. * We loop back to fread, which will retrieve this data. * Also note that code has to be arranged so that fread * is done _before_ one-second poll wait - poll doesn't know * about stdio buffering and can result in spurious one second waits! */ } /* If -c failed, we restart from the beginning, * but we do not truncate file then, we do it only now, at the end. * This lets user to ^C if his 99% complete 10 GB file download * failed to restart *without* losing the almost complete file. */ { off_t pos = lseek(G.output_fd, 0, SEEK_CUR); if (pos != (off_t)-1) ftruncate(G.output_fd, pos); } /* Draw full bar and free its resources */ G.chunked = 0; /* makes it show 100% even for chunked download */ G.got_clen = 1; /* makes it show 100% even for download of (formerly) unknown size */ progress_meter(PROGRESS_END); } static void download_one_url(const char *url) { bool use_proxy; /* Use proxies if env vars are set */ int redir_limit; len_and_sockaddr *lsa; FILE *sfp; /* socket to web/ftp server */ FILE *dfp; /* socket to ftp server (data) */ char *proxy = NULL; char *fname_out_alloc; char *redirected_path = NULL; struct host_info server; struct host_info target; server.allocated = NULL; target.allocated = NULL; server.user = NULL; target.user = NULL; parse_url(url, &target); /* Use the proxy if necessary */ use_proxy = (strcmp(G.proxy_flag, "off") != 0); if (use_proxy) { proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy"); use_proxy = (proxy && proxy[0]); if (use_proxy) parse_url(proxy, &server); } if (!use_proxy) { server.port = target.port; if (ENABLE_FEATURE_IPV6) { //free(server.allocated); - can't be non-NULL server.host = server.allocated = xstrdup(target.host); } else { server.host = target.host; } } if (ENABLE_FEATURE_IPV6) strip_ipv6_scope_id(target.host); /* If there was no -O FILE, guess output filename */ fname_out_alloc = NULL; if (!(option_mask32 & WGET_OPT_OUTNAME)) { G.fname_out = bb_get_last_path_component_nostrip(target.path); /* handle "wget http://kernel.org//" */ if (G.fname_out[0] == '/' || !G.fname_out[0]) G.fname_out = (char*)"index.html"; /* -P DIR is considered only if there was no -O FILE */ if (G.dir_prefix) G.fname_out = fname_out_alloc = concat_path_file(G.dir_prefix, G.fname_out); else { /* redirects may free target.path later, need to make a copy */ G.fname_out = fname_out_alloc = xstrdup(G.fname_out); } } #if ENABLE_FEATURE_WGET_STATUSBAR G.curfile = bb_get_last_path_component_nostrip(G.fname_out); #endif /* Determine where to start transfer */ G.beg_range = 0; if (option_mask32 & WGET_OPT_CONTINUE) { G.output_fd = open(G.fname_out, O_WRONLY); if (G.output_fd >= 0) { G.beg_range = xlseek(G.output_fd, 0, SEEK_END); } /* File doesn't exist. We do not create file here yet. * We are not sure it exists on remote side */ } redir_limit = 5; resolve_lsa: lsa = xhost2sockaddr(server.host, server.port); if (!(option_mask32 & WGET_OPT_QUIET)) { char *s = xmalloc_sockaddr2dotted(&lsa->u.sa); fprintf(stderr, "Connecting to %s (%s)\n", server.host, s); free(s); } establish_session: /*G.content_len = 0; - redundant, got_clen = 0 is enough */ G.got_clen = 0; G.chunked = 0; if (use_proxy || !target.is_ftp) { /* * HTTP session */ char *str; int status; /* Open socket to http server */ sfp = open_socket(lsa); /* Send HTTP request */ if (use_proxy) { fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n", target.is_ftp ? "f" : "ht", target.host, target.path); } else { if (option_mask32 & WGET_OPT_POST_DATA) fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path); else fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path); } fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n", target.host, G.user_agent); /* Ask server to close the connection as soon as we are done * (IOW: we do not intend to send more requests) */ fprintf(sfp, "Connection: close\r\n"); #if ENABLE_FEATURE_WGET_AUTHENTICATION if (target.user) { fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6, base64enc(target.user)); } if (use_proxy && server.user) { fprintf(sfp, "Proxy-Authorization: Basic %s\r\n", base64enc(server.user)); } #endif if (G.beg_range != 0) fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range); #if ENABLE_FEATURE_WGET_LONG_OPTIONS if (G.extra_headers) fputs(G.extra_headers, sfp); if (option_mask32 & WGET_OPT_POST_DATA) { fprintf(sfp, "Content-Type: application/x-www-form-urlencoded\r\n" "Content-Length: %u\r\n" "\r\n" "%s", (int) strlen(G.post_data), G.post_data ); } else #endif { fprintf(sfp, "\r\n"); } fflush(sfp); /* * Retrieve HTTP response line and check for "200" status code. */ read_response: fgets_and_trim(sfp); str = G.wget_buf; str = skip_non_whitespace(str); str = skip_whitespace(str); // FIXME: no error check // xatou wouldn't work: "200 OK" status = atoi(str); switch (status) { case 0: case 100: while (gethdr(sfp) != NULL) /* eat all remaining headers */; goto read_response; case 200: /* Response 204 doesn't say "null file", it says "metadata has changed but data didn't": "10.2.5 204 No Content The server has fulfilled the request but does not need to return an entity-body, and might want to return updated metainformation. The response MAY include new or updated metainformation in the form of entity-headers, which if present SHOULD be associated with the requested variant. If the client is a user agent, it SHOULD NOT change its document view from that which caused the request to be sent. This response is primarily intended to allow input for actions to take place without causing a change to the user agent's active document view, although any new or updated metainformation SHOULD be applied to the document currently in the user agent's active view. The 204 response MUST NOT include a message-body, and thus is always terminated by the first empty line after the header fields." However, in real world it was observed that some web servers (e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero. */ case 204: if (G.beg_range != 0) { /* "Range:..." was not honored by the server. * Restart download from the beginning. */ reset_beg_range_to_zero(); } break; case 300: /* redirection */ case 301: case 302: case 303: break; case 206: /* Partial Content */ if (G.beg_range != 0) /* "Range:..." worked. Good. */ break; /* Partial Content even though we did not ask for it??? */ /* fall through */ default: bb_error_msg_and_die("server returned error: %s", sanitize_string(G.wget_buf)); } /* * Retrieve HTTP headers. */ while ((str = gethdr(sfp)) != NULL) { static const char keywords[] ALIGN1 = "content-length\0""transfer-encoding\0""location\0"; enum { KEY_content_length = 1, KEY_transfer_encoding, KEY_location }; smalluint key; /* gethdr converted "FOO:" string to lowercase */ /* strip trailing whitespace */ char *s = strchrnul(str, '\0') - 1; while (s >= str && (*s == ' ' || *s == '\t')) { *s = '\0'; s--; } key = index_in_strings(keywords, G.wget_buf) + 1; if (key == KEY_content_length) { G.content_len = BB_STRTOOFF(str, NULL, 10); if (G.content_len < 0 || errno) { bb_error_msg_and_die("content-length %s is garbage", sanitize_string(str)); } G.got_clen = 1; continue; } if (key == KEY_transfer_encoding) { if (strcmp(str_tolower(str), "chunked") != 0) bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str)); G.chunked = 1; } if (key == KEY_location && status >= 300) { if (--redir_limit == 0) bb_error_msg_and_die("too many redirections"); fclose(sfp); if (str[0] == '/') { free(redirected_path); target.path = redirected_path = xstrdup(str+1); /* lsa stays the same: it's on the same server */ } else { parse_url(str, &target); if (!use_proxy) { free(server.allocated); server.allocated = NULL; server.host = target.host; /* strip_ipv6_scope_id(target.host); - no! */ /* we assume remote never gives us IPv6 addr with scope id */ server.port = target.port; free(lsa); goto resolve_lsa; } /* else: lsa stays the same: we use proxy */ } goto establish_session; } } // if (status >= 300) // bb_error_msg_and_die("bad redirection (no Location: header from server)"); /* For HTTP, data is pumped over the same connection */ dfp = sfp; } else { /* * FTP session */ sfp = prepare_ftp_session(&dfp, &target, lsa); } free(lsa); if (!(option_mask32 & WGET_OPT_SPIDER)) { if (G.output_fd < 0) G.output_fd = xopen(G.fname_out, G.o_flags); retrieve_file_data(dfp); if (!(option_mask32 & WGET_OPT_OUTNAME)) { xclose(G.output_fd); G.output_fd = -1; } } if (dfp != sfp) { /* It's ftp. Close data connection properly */ fclose(dfp); if (ftpcmd(NULL, NULL, sfp) != 226) bb_error_msg_and_die("ftp error: %s", sanitize_string(G.wget_buf + 4)); /* ftpcmd("QUIT", NULL, sfp); - why bother? */ } fclose(sfp); free(server.allocated); free(target.allocated); free(fname_out_alloc); free(redirected_path); } int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int wget_main(int argc UNUSED_PARAM, char **argv) { #if ENABLE_FEATURE_WGET_LONG_OPTIONS static const char wget_longopts[] ALIGN1 = /* name, has_arg, val */ "continue\0" No_argument "c" //FIXME: -s isn't --spider, it's --save-headers! "spider\0" No_argument "s" "quiet\0" No_argument "q" "output-document\0" Required_argument "O" "directory-prefix\0" Required_argument "P" "proxy\0" Required_argument "Y" "user-agent\0" Required_argument "U" #if ENABLE_FEATURE_WGET_TIMEOUT "timeout\0" Required_argument "T" #endif /* Ignored: */ // "tries\0" Required_argument "t" /* Ignored (we always use PASV): */ "passive-ftp\0" No_argument "\xff" "header\0" Required_argument "\xfe" "post-data\0" Required_argument "\xfd" /* Ignored (we don't do ssl) */ "no-check-certificate\0" No_argument "\xfc" /* Ignored (we don't support caching) */ "no-cache\0" No_argument "\xfb" ; #endif #if ENABLE_FEATURE_WGET_LONG_OPTIONS llist_t *headers_llist = NULL; #endif INIT_G(); #if ENABLE_FEATURE_WGET_TIMEOUT G.timeout_seconds = CONFIG_WGET_DEFAULT_TIMEOUT; signal(SIGALRM, alarm_handler); #endif G.proxy_flag = "on"; /* use proxies if env vars are set */ G.user_agent = "Wget"; /* "User-Agent" header field */ #if ENABLE_FEATURE_WGET_LONG_OPTIONS applet_long_options = wget_longopts; #endif opt_complementary = "-1" IF_FEATURE_WGET_TIMEOUT(":T+") IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::"); getopt32(argv, "csqO:P:Y:U:T:" /*ignored:*/ "t:", &G.fname_out, &G.dir_prefix, &G.proxy_flag, &G.user_agent, IF_FEATURE_WGET_TIMEOUT(&G.timeout_seconds) IF_NOT_FEATURE_WGET_TIMEOUT(NULL), NULL /* -t RETRIES */ IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist) IF_FEATURE_WGET_LONG_OPTIONS(, &G.post_data) ); argv += optind; #if ENABLE_FEATURE_WGET_LONG_OPTIONS if (headers_llist) { int size = 1; char *cp; llist_t *ll = headers_llist; while (ll) { size += strlen(ll->data) + 2; ll = ll->link; } G.extra_headers = cp = xmalloc(size); while (headers_llist) { cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist)); } } #endif G.output_fd = -1; G.o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL; if (G.fname_out) { /* -O FILE ? */ if (LONE_DASH(G.fname_out)) { /* -O - ? */ G.output_fd = 1; option_mask32 &= ~WGET_OPT_CONTINUE; } /* compat with wget: -O FILE can overwrite */ G.o_flags = O_WRONLY | O_CREAT | O_TRUNC; } while (*argv) download_one_url(*argv++); if (G.output_fd >= 0) xclose(G.output_fd); #if ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_WGET_LONG_OPTIONS free(G.extra_headers); #endif FINI_G(); return EXIT_SUCCESS; } busybox-1.22.1/networking/Config.src0000644000000000000000000006276612320365455016121 0ustar rootroot# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Networking Utilities" INSERT config FEATURE_IPV6 bool "Enable IPv6 support" default y help Enable IPv6 support in busybox. This adds IPv6 support in the networking applets. config FEATURE_UNIX_LOCAL bool "Enable Unix domain socket support (usually not needed)" default n help Enable Unix domain socket support in all busybox networking applets. Address of the form local:/path/to/unix/socket will be recognized. This extension is almost never used in real world usage. You most likely want to say N. config FEATURE_PREFER_IPV4_ADDRESS bool "Prefer IPv4 addresses from DNS queries" default y depends on FEATURE_IPV6 help Use IPv4 address of network host if it has one. If this option is off, the first returned address will be used. This may cause problems when your DNS server is IPv6-capable and is returning IPv6 host addresses too. If IPv6 address precedes IPv4 one in DNS reply, busybox network applets (e.g. wget) will use IPv6 address. On an IPv6-incapable host or network applets will fail to connect to the host using IPv6 address. config VERBOSE_RESOLUTION_ERRORS bool "Verbose resolution errors" default n help Enable if you are not satisfied with simplistic "can't resolve 'hostname.com'" and want to know more. This may increase size of your executable a bit. config ARP bool "arp" default y select PLATFORM_LINUX help Manipulate the system ARP cache. config ARPING bool "arping" default y select PLATFORM_LINUX help Ping hosts by ARP packets. config BRCTL bool "brctl" default y select PLATFORM_LINUX help Manage ethernet bridges. Supports addbr/delbr and addif/delif. config FEATURE_BRCTL_FANCY bool "Fancy options" default y depends on BRCTL help Add support for extended option like: setageing, setfd, sethello, setmaxage, setpathcost, setportprio, setbridgeprio, stp This adds about 600 bytes. config FEATURE_BRCTL_SHOW bool "Support show" default y depends on BRCTL && FEATURE_BRCTL_FANCY help Add support for option which prints the current config: show config DNSD bool "dnsd" default y help Small and static DNS server daemon. config ETHER_WAKE bool "ether-wake" default y select PLATFORM_LINUX help Send a magic packet to wake up sleeping machines. config FAKEIDENTD bool "fakeidentd" default y select FEATURE_SYSLOG help fakeidentd listens on the ident port and returns a predefined fake value on any query. config FTPD bool "ftpd" default y help simple FTP daemon. You have to run it via inetd. config FEATURE_FTP_WRITE bool "Enable upload commands" default y depends on FTPD help Enable all kinds of FTP upload commands (-w option) config FEATURE_FTPD_ACCEPT_BROKEN_LIST bool "Enable workaround for RFC-violating clients" default y depends on FTPD help Some ftp clients (among them KDE's Konqueror) issue illegal "LIST -l" requests. This option works around such problems. It might prevent you from listing files starting with "-" and it increases the code size by ~40 bytes. Most other ftp servers seem to behave similar to this. config FTPGET bool "ftpget" default y help Retrieve a remote file via FTP. config FTPPUT bool "ftpput" default y help Store a remote file via FTP. config FEATURE_FTPGETPUT_LONG_OPTIONS bool "Enable long options in ftpget/ftpput" default y depends on LONG_OPTS && (FTPGET || FTPPUT) help Support long options for the ftpget/ftpput applet. config HOSTNAME bool "hostname" default y help Show or set the system's host name. config HTTPD bool "httpd" default y help Serve web pages via an HTTP server. config FEATURE_HTTPD_RANGES bool "Support 'Ranges:' header" default y depends on HTTPD help Makes httpd emit "Accept-Ranges: bytes" header and understand "Range: bytes=NNN-[MMM]" header. Allows for resuming interrupted downloads, seeking in multimedia players etc. config FEATURE_HTTPD_USE_SENDFILE bool "Use sendfile system call" default y depends on HTTPD help When enabled, httpd will use the kernel sendfile() function instead of read/write loop. config FEATURE_HTTPD_SETUID bool "Enable -u option" default y depends on HTTPD help This option allows the server to run as a specific user rather than defaulting to the user that starts the server. Use of this option requires special privileges to change to a different user. config FEATURE_HTTPD_BASIC_AUTH bool "Enable Basic http Authentication" default y depends on HTTPD help Utilizes password settings from /etc/httpd.conf for basic authentication on a per url basis. Example for httpd.conf file: /adm:toor:PaSsWd config FEATURE_HTTPD_AUTH_MD5 bool "Support MD5 crypted passwords for http Authentication" default y depends on FEATURE_HTTPD_BASIC_AUTH help Enables encrypted passwords, and wildcard user/passwords in httpd.conf file. User '*' means 'any system user name is ok', password of '*' means 'use system password for this user' Examples: /adm:toor:$1$P/eKnWXS$aI1aPGxT.dJD5SzqAKWrF0 /adm:root:* /wiki:*:* config FEATURE_HTTPD_CGI bool "Support Common Gateway Interface (CGI)" default y depends on HTTPD help This option allows scripts and executables to be invoked when specific URLs are requested. config FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR bool "Support for running scripts through an interpreter" default y depends on FEATURE_HTTPD_CGI help This option enables support for running scripts through an interpreter. Turn this on if you want PHP scripts to work properly. You need to supply an additional line in your httpd.conf file: *.php:/path/to/your/php config FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV bool "Set REMOTE_PORT environment variable for CGI" default y depends on FEATURE_HTTPD_CGI help Use of this option can assist scripts in generating references that contain a unique port number. config FEATURE_HTTPD_ENCODE_URL_STR bool "Enable -e option (useful for CGIs written as shell scripts)" default y depends on HTTPD help This option allows html encoding of arbitrary strings for display by the browser. Output goes to stdout. For example, httpd -e "" produces "<Hello World>". config FEATURE_HTTPD_ERROR_PAGES bool "Support for custom error pages" default y depends on HTTPD help This option allows you to define custom error pages in the configuration file instead of the default HTTP status error pages. For instance, if you add the line: E404:/path/e404.html in the config file, the server will respond the specified '/path/e404.html' file instead of the terse '404 NOT FOUND' message. config FEATURE_HTTPD_PROXY bool "Support for reverse proxy" default y depends on HTTPD help This option allows you to define URLs that will be forwarded to another HTTP server. To setup add the following line to the configuration file P:/url/:http://hostname[:port]/new/path/ Then a request to /url/myfile will be forwarded to http://hostname[:port]/new/path/myfile. config FEATURE_HTTPD_GZIP bool "Support for GZIP content encoding" default y depends on HTTPD help Makes httpd send files using GZIP content encoding if the client supports it and a pre-compressed .gz exists. config IFCONFIG bool "ifconfig" default y select PLATFORM_LINUX help Ifconfig is used to configure the kernel-resident network interfaces. config FEATURE_IFCONFIG_STATUS bool "Enable status reporting output (+7k)" default y depends on IFCONFIG help If ifconfig is called with no arguments it will display the status of the currently active interfaces. config FEATURE_IFCONFIG_SLIP bool "Enable slip-specific options \"keepalive\" and \"outfill\"" default y depends on IFCONFIG help Allow "keepalive" and "outfill" support for SLIP. If you're not planning on using serial lines, leave this unchecked. config FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ bool "Enable options \"mem_start\", \"io_addr\", and \"irq\"" default y depends on IFCONFIG help Allow the start address for shared memory, start address for I/O, and/or the interrupt line used by the specified device. config FEATURE_IFCONFIG_HW bool "Enable option \"hw\" (ether only)" default y depends on IFCONFIG help Set the hardware address of this interface, if the device driver supports this operation. Currently, we only support the 'ether' class. config FEATURE_IFCONFIG_BROADCAST_PLUS bool "Set the broadcast automatically" default y depends on IFCONFIG help Setting this will make ifconfig attempt to find the broadcast automatically if the value '+' is used. config IFENSLAVE bool "ifenslave" default y select PLATFORM_LINUX help Userspace application to bind several interfaces to a logical interface (use with kernel bonding driver). config IFPLUGD bool "ifplugd" default y select PLATFORM_LINUX help Network interface plug detection daemon. config IFUPDOWN bool "ifupdown" default y help Activate or deactivate the specified interfaces. This applet makes use of either "ifconfig" and "route" or the "ip" command to actually configure network interfaces. Therefore, you will probably also want to enable either IFCONFIG and ROUTE, or enable FEATURE_IFUPDOWN_IP and the various IP options. Of course you could use non-busybox versions of these programs, so against my better judgement (since this will surely result in plenty of support questions on the mailing list), I do not force you to enable these additional options. It is up to you to supply either "ifconfig", "route" and "run-parts" or the "ip" command, either via busybox or via standalone utilities. config IFUPDOWN_IFSTATE_PATH string "Absolute path to ifstate file" default "/var/run/ifstate" depends on IFUPDOWN help ifupdown keeps state information in a file called ifstate. Typically it is located in /var/run/ifstate, however some distributions tend to put it in other places (debian, for example, uses /etc/network/run/ifstate). This config option defines location of ifstate. config FEATURE_IFUPDOWN_IP bool "Use ip applet" default y depends on IFUPDOWN help Use the iproute "ip" command to implement "ifup" and "ifdown", rather than the default of using the older 'ifconfig' and 'route' utilities. config FEATURE_IFUPDOWN_IP_BUILTIN bool "Use busybox ip applet" default y depends on FEATURE_IFUPDOWN_IP select PLATFORM_LINUX select IP select FEATURE_IP_ADDRESS select FEATURE_IP_LINK select FEATURE_IP_ROUTE help Use the busybox iproute "ip" applet to implement "ifupdown". If left disabled, you must install the full-blown iproute2 utility or the "ifup" and "ifdown" applets will not work. config FEATURE_IFUPDOWN_IFCONFIG_BUILTIN bool "Use busybox ifconfig and route applets" default n depends on IFUPDOWN && !FEATURE_IFUPDOWN_IP select IFCONFIG select ROUTE help Use the busybox iproute "ifconfig" and "route" applets to implement the "ifup" and "ifdown" utilities. If left disabled, you must install the full-blown ifconfig and route utilities, or the "ifup" and "ifdown" applets will not work. config FEATURE_IFUPDOWN_IPV4 bool "Support for IPv4" default y depends on IFUPDOWN help If you want ifup/ifdown to talk IPv4, leave this on. config FEATURE_IFUPDOWN_IPV6 bool "Support for IPv6" default y depends on IFUPDOWN && FEATURE_IPV6 help If you need support for IPv6, turn this option on. ### UNUSED ###config FEATURE_IFUPDOWN_IPX ### bool "Support for IPX" ### default y ### depends on IFUPDOWN ### help ### If this option is selected you can use busybox to work with IPX ### networks. config FEATURE_IFUPDOWN_MAPPING bool "Enable mapping support" default y depends on IFUPDOWN help This enables support for the "mapping" stanza, unless you have a weird network setup you don't need it. config FEATURE_IFUPDOWN_EXTERNAL_DHCP bool "Support for external dhcp clients" default n depends on IFUPDOWN help This enables support for the external dhcp clients. Clients are tried in the following order: dhcpcd, dhclient, pump and udhcpc. Otherwise, if udhcpc applet is enabled, it is used. Otherwise, ifup/ifdown will have no support for DHCP. config INETD bool "inetd" default y select FEATURE_SYSLOG help Internet superserver daemon config FEATURE_INETD_SUPPORT_BUILTIN_ECHO bool "Support echo service" default y depends on INETD help Echo received data internal inetd service config FEATURE_INETD_SUPPORT_BUILTIN_DISCARD bool "Support discard service" default y depends on INETD help Internet /dev/null internal inetd service config FEATURE_INETD_SUPPORT_BUILTIN_TIME bool "Support time service" default y depends on INETD help Return 32 bit time since 1900 internal inetd service config FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME bool "Support daytime service" default y depends on INETD help Return human-readable time internal inetd service config FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN bool "Support chargen service" default y depends on INETD help Familiar character generator internal inetd service config FEATURE_INETD_RPC bool "Support RPC services" default y depends on INETD select FEATURE_HAVE_RPC help Support Sun-RPC based services config IP bool "ip" default y select PLATFORM_LINUX help The "ip" applet is a TCP/IP interface configuration and routing utility. You generally don't need "ip" to use busybox with TCP/IP. config FEATURE_IP_ADDRESS bool "ip address" default y depends on IP help Address manipulation support for the "ip" applet. config FEATURE_IP_LINK bool "ip link" default y depends on IP help Configure network devices with "ip". config FEATURE_IP_ROUTE bool "ip route" default y depends on IP help Add support for routing table management to "ip". config FEATURE_IP_TUNNEL bool "ip tunnel" default y depends on IP help Add support for tunneling commands to "ip". config FEATURE_IP_RULE bool "ip rule" default y depends on IP help Add support for rule commands to "ip". config FEATURE_IP_SHORT_FORMS bool "Support short forms of ip commands" default y depends on IP help Also support short-form of ip commands: ip addr -> ipaddr ip link -> iplink ip route -> iproute ip tunnel -> iptunnel ip rule -> iprule Say N unless you desparately need the short form of the ip object commands. config FEATURE_IP_RARE_PROTOCOLS bool "Support displaying rarely used link types" default n depends on IP help If you are not going to use links of type "frad", "econet", "bif" etc, you probably don't need to enable this. Ethernet, wireless, infrared, ppp/slip, ip tunnelling link types are supported without this option selected. config IPADDR bool default y depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ADDRESS config IPLINK bool default y depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_LINK config IPROUTE bool default y depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ROUTE config IPTUNNEL bool default y depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_TUNNEL config IPRULE bool default y depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_RULE config IPCALC bool "ipcalc" default y help ipcalc takes an IP address and netmask and calculates the resulting broadcast, network, and host range. config FEATURE_IPCALC_FANCY bool "Fancy IPCALC, more options, adds 1 kbyte" default y depends on IPCALC help Adds the options hostname, prefix and silent to the output of "ipcalc". config FEATURE_IPCALC_LONG_OPTIONS bool "Enable long options" default y depends on IPCALC && LONG_OPTS help Support long options for the ipcalc applet. config NETSTAT bool "netstat" default y select PLATFORM_LINUX help netstat prints information about the Linux networking subsystem. config FEATURE_NETSTAT_WIDE bool "Enable wide netstat output" default y depends on NETSTAT help Add support for wide columns. Useful when displaying IPv6 addresses (-W option). config FEATURE_NETSTAT_PRG bool "Enable PID/Program name output" default y depends on NETSTAT help Add support for -p flag to print out PID and program name. +700 bytes of code. config NSLOOKUP bool "nslookup" default y help nslookup is a tool to query Internet name servers. config NTPD bool "ntpd" default y select PLATFORM_LINUX help The NTP client/server daemon. config FEATURE_NTPD_SERVER bool "Make ntpd usable as a NTP server" default y depends on NTPD help Make ntpd usable as a NTP server. If you disable this option ntpd will be usable only as a NTP client. config PSCAN bool "pscan" default y help Simple network port scanner. config ROUTE bool "route" default y select PLATFORM_LINUX help Route displays or manipulates the kernel's IP routing tables. config SLATTACH bool "slattach" default y select PLATFORM_LINUX help slattach is a small utility to attach network interfaces to serial lines. #config TC # bool "tc" # default y # help # show / manipulate traffic control settings # #config FEATURE_TC_INGRESS # def_bool n # depends on TC config TCPSVD bool "tcpsvd" default y help tcpsvd listens on a TCP port and runs a program for each new connection. config TELNET bool "telnet" default y help Telnet is an interface to the TELNET protocol, but is also commonly used to test other simple protocols. config FEATURE_TELNET_TTYPE bool "Pass TERM type to remote host" default y depends on TELNET help Setting this option will forward the TERM environment variable to the remote host you are connecting to. This is useful to make sure that things like ANSI colors and other control sequences behave. config FEATURE_TELNET_AUTOLOGIN bool "Pass USER type to remote host" default y depends on TELNET help Setting this option will forward the USER environment variable to the remote host you are connecting to. This is useful when you need to log into a machine without telling the username (autologin). This option enables `-a' and `-l USER' arguments. config TELNETD bool "telnetd" default y select FEATURE_SYSLOG help A daemon for the TELNET protocol, allowing you to log onto the host running the daemon. Please keep in mind that the TELNET protocol sends passwords in plain text. If you can't afford the space for an SSH daemon and you trust your network, you may say 'y' here. As a more secure alternative, you should seriously consider installing the very small Dropbear SSH daemon instead: http://matt.ucc.asn.au/dropbear/dropbear.html Note that for busybox telnetd to work you need several things: First of all, your kernel needs: UNIX98_PTYS=y DEVPTS_FS=y Next, you need a /dev/pts directory on your root filesystem: $ ls -ld /dev/pts drwxr-xr-x 2 root root 0 Sep 23 13:21 /dev/pts/ Next you need the pseudo terminal master multiplexer /dev/ptmx: $ ls -la /dev/ptmx crw-rw-rw- 1 root tty 5, 2 Sep 23 13:55 /dev/ptmx Any /dev/ttyp[0-9]* files you may have can be removed. Next, you need to mount the devpts filesystem on /dev/pts using: mount -t devpts devpts /dev/pts You need to be sure that busybox has LOGIN and FEATURE_SUID enabled. And finally, you should make certain that Busybox has been installed setuid root: chown root.root /bin/busybox chmod 4755 /bin/busybox with all that done, telnetd _should_ work.... config FEATURE_TELNETD_STANDALONE bool "Support standalone telnetd (not inetd only)" default y depends on TELNETD help Selecting this will make telnetd able to run standalone. config FEATURE_TELNETD_INETD_WAIT bool "Support -w SEC option (inetd wait mode)" default y depends on FEATURE_TELNETD_STANDALONE help This option allows you to run telnetd in "inet wait" mode. Example inetd.conf line (note "wait", not usual "nowait"): telnet stream tcp wait root /bin/telnetd telnetd -w10 In this example, inetd passes _listening_ socket_ as fd 0 to telnetd when connection appears. telnetd will wait for connections until all existing connections are closed, and no new connections appear during 10 seconds. Then it exits, and inetd continues to listen for new connections. This option is rarely used. "tcp nowait" is much more usual way of running tcp services, including telnetd. You most probably want to say N here. config TFTP bool "tftp" default y help This enables the Trivial File Transfer Protocol client program. TFTP is usually used for simple, small transfers such as a root image for a network-enabled bootloader. config TFTPD bool "tftpd" default y help This enables the Trivial File Transfer Protocol server program. It expects that stdin is a datagram socket and a packet is already pending on it. It will exit after one transfer. In other words: it should be run from inetd in nowait mode, or from udpsvd. Example: "udpsvd -E 0 69 tftpd DIR" comment "Common options for tftp/tftpd" depends on TFTP || TFTPD config FEATURE_TFTP_GET bool "Enable 'tftp get' and/or tftpd upload code" default y depends on TFTP || TFTPD help Add support for the GET command within the TFTP client. This allows a client to retrieve a file from a TFTP server. Also enable upload support in tftpd, if tftpd is selected. Note: this option does _not_ make tftpd capable of download (the usual operation people need from it)! config FEATURE_TFTP_PUT bool "Enable 'tftp put' and/or tftpd download code" default y depends on TFTP || TFTPD help Add support for the PUT command within the TFTP client. This allows a client to transfer a file to a TFTP server. Also enable download support in tftpd, if tftpd is selected. config FEATURE_TFTP_BLOCKSIZE bool "Enable 'blksize' and 'tsize' protocol options" default y depends on TFTP || TFTPD help Allow tftp to specify block size, and tftpd to understand "blksize" and "tsize" options. config FEATURE_TFTP_PROGRESS_BAR bool "Enable tftp progress meter" default y depends on TFTP && FEATURE_TFTP_BLOCKSIZE help Show progress bar. config TFTP_DEBUG bool "Enable debug" default n depends on TFTP || TFTPD help Make tftp[d] print debugging messages on stderr. This is useful if you are diagnosing a bug in tftp[d]. config TRACEROUTE bool "traceroute" default y select PLATFORM_LINUX help Utility to trace the route of IP packets. config TRACEROUTE6 bool "traceroute6" default y depends on FEATURE_IPV6 && TRACEROUTE help Utility to trace the route of IPv6 packets. config FEATURE_TRACEROUTE_VERBOSE bool "Enable verbose output" default y depends on TRACEROUTE help Add some verbosity to traceroute. This includes among other things hostnames and ICMP response types. config FEATURE_TRACEROUTE_SOURCE_ROUTE bool "Enable loose source route" default n depends on TRACEROUTE help Add option to specify a loose source route gateway (8 maximum). config FEATURE_TRACEROUTE_USE_ICMP bool "Use ICMP instead of UDP" default n depends on TRACEROUTE help Add option -I to use ICMP ECHO instead of UDP datagrams. config TUNCTL bool "tunctl" default y select PLATFORM_LINUX help tunctl creates or deletes tun devices. config FEATURE_TUNCTL_UG bool "Support owner:group assignment" default y depends on TUNCTL help Allow to specify owner and group of newly created interface. 340 bytes of pure bloat. Say no here. source networking/udhcp/Config.in config IFUPDOWN_UDHCPC_CMD_OPTIONS string "ifup udhcpc command line options" default "-R -n" depends on IFUPDOWN && UDHCPC help Command line options to pass to udhcpc from ifup. Intended to alter options not available in /etc/network/interfaces. (IE: --syslog --background etc...) config UDPSVD bool "udpsvd" default y help udpsvd listens on an UDP port and runs a program for each new connection. config VCONFIG bool "vconfig" default y select PLATFORM_LINUX help Creates, removes, and configures VLAN interfaces config WGET bool "wget" default y help wget is a utility for non-interactive download of files from HTTP and FTP servers. config FEATURE_WGET_STATUSBAR bool "Enable a nifty process meter (+2k)" default y depends on WGET help Enable the transfer progress bar for wget transfers. config FEATURE_WGET_AUTHENTICATION bool "Enable HTTP authentication" default y depends on WGET help Support authenticated HTTP transfers. config FEATURE_WGET_LONG_OPTIONS bool "Enable long options" default y depends on WGET && LONG_OPTS help Support long options for the wget applet. config FEATURE_WGET_TIMEOUT bool "Enable timeout option -T SEC" default y depends on WGET help Supports network read and connect timeouts for wget, so that wget will give up and timeout, through the -T command line option. Currently only connect and network data read timeout are supported (i.e., timeout is not applied to the DNS query). When FEATURE_WGET_LONG_OPTIONS is also enabled, the --timeout option will work in addition to -T. config WGET_DEFAULT_TIMEOUT int "Default wget timeout" default 900 range 1 2000 depends on FEATURE_WGET_TIMEOUT help The default time, in seconds, to wait before wget gives up. config ZCIP bool "zcip" default y select PLATFORM_LINUX select FEATURE_SYSLOG help ZCIP provides ZeroConf IPv4 address selection, according to RFC 3927. It's a daemon that allocates and defends a dynamically assigned address on the 169.254/16 network, requiring no system administrator. See http://www.zeroconf.org for further details, and "zcip.script" in the busybox examples. endmenu busybox-1.22.1/networking/nc_bloaty.c0000644000000000000000000007573612320365405016315 0ustar rootroot/* Based on netcat 1.10 RELEASE 960320 written by hobbit@avian.org. * Released into public domain by the author. * * Copyright (C) 2007 Denys Vlasenko. * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* Author's comments from nc 1.10: * ===================== * Netcat is entirely my own creation, although plenty of other code was used as * examples. It is freely given away to the Internet community in the hope that * it will be useful, with no restrictions except giving credit where it is due. * No GPLs, Berkeley copyrights or any of that nonsense. The author assumes NO * responsibility for how anyone uses it. If netcat makes you rich somehow and * you're feeling generous, mail me a check. If you are affiliated in any way * with Microsoft Network, get a life. Always ski in control. Comments, * questions, and patches to hobbit@avian.org. * ... * Netcat and the associated package is a product of Avian Research, and is freely * available in full source form with no restrictions save an obligation to give * credit where due. * ... * A damn useful little "backend" utility begun 950915 or thereabouts, * as *Hobbit*'s first real stab at some sockets programming. Something that * should have and indeed may have existed ten years ago, but never became a * standard Unix utility. IMHO, "nc" could take its place right next to cat, * cp, rm, mv, dd, ls, and all those other cryptic and Unix-like things. * ===================== * * Much of author's comments are still retained in the code. * * Functionality removed (rationale): * - miltiple-port ranges, randomized port scanning (use nmap) * - telnet support (use telnet) * - source routing * - multiple DNS checks * Functionalty which is different from nc 1.10: * - PROG in '-e PROG' can have ARGS (and options). * Because of this -e option must be last. //TODO: remove -e incompatibility? * - we don't redirect stderr to the network socket for the -e PROG. * (PROG can do it itself if needed, but sometimes it is NOT wanted!) * - numeric addresses are printed in (), not [] (IPv6 looks better), * port numbers are inside (): (1.2.3.4:5678) * - network read errors are reported on verbose levels > 1 * (nc 1.10 treats them as EOF) * - TCP connects from wrong ip/ports (if peer ip:port is specified * on the command line, but accept() says that it came from different addr) * are closed, but we don't exit - we continue to listen/accept. * Since bbox 1.22: * - nc exits when _both_ stdin and network are closed. * This makes these two commands: * echo "Yes" | nc 127.0.0.1 1234 * echo "no" | nc -lp 1234 * exchange their data _and exit_ instead of being stuck. */ /* done in nc.c: #include "libbb.h" */ //usage:#if ENABLE_NC_110_COMPAT //usage: //usage:#define nc_trivial_usage //usage: "[OPTIONS] HOST PORT - connect" //usage: IF_NC_SERVER("\n" //usage: "nc [OPTIONS] -l -p PORT [HOST] [PORT] - listen" //usage: ) //usage:#define nc_full_usage "\n\n" //usage: " -e PROG Run PROG after connect (must be last)" //usage: IF_NC_SERVER( //usage: "\n -l Listen mode, for inbound connects" //usage: "\n -lk With -e, provides persistent server" /* -ll does the same as -lk, but its our extension, while -k is BSD'd, * presumably more widely known. Therefore we advertise it, not -ll. * I would like to drop -ll support, but our "small" nc supports it, * and Rob uses it. */ //usage: ) //usage: "\n -p PORT Local port" //usage: "\n -s ADDR Local address" //usage: "\n -w SEC Timeout for connects and final net reads" //usage: IF_NC_EXTRA( //usage: "\n -i SEC Delay interval for lines sent" /* ", ports scanned" */ //usage: ) //usage: "\n -n Don't do DNS resolution" //usage: "\n -u UDP mode" //usage: "\n -v Verbose" //usage: IF_NC_EXTRA( //usage: "\n -o FILE Hex dump traffic" //usage: "\n -z Zero-I/O mode (scanning)" //usage: ) //usage:#endif /* "\n -r Randomize local and remote ports" */ /* "\n -g gateway Source-routing hop point[s], up to 8" */ /* "\n -G num Source-routing pointer: 4, 8, 12, ..." */ /* "\nport numbers can be individual or ranges: lo-hi [inclusive]" */ /* -e PROG can take ARGS too: "nc ... -e ls -l", but we don't document it * in help text: nc 1.10 does not allow that. We don't want to entice * users to use this incompatibility */ enum { SLEAZE_PORT = 31337, /* for UDP-scan RTT trick, change if ya want */ BIGSIZ = 8192, /* big buffers */ netfd = 3, ofd = 4, }; struct globals { /* global cmd flags: */ unsigned o_verbose; unsigned o_wait; #if ENABLE_NC_EXTRA unsigned o_interval; #endif /*int netfd;*/ /*int ofd;*/ /* hexdump output fd */ #if ENABLE_LFS #define SENT_N_RECV_M "sent %llu, rcvd %llu\n" unsigned long long wrote_out; /* total stdout bytes */ unsigned long long wrote_net; /* total net bytes */ #else #define SENT_N_RECV_M "sent %u, rcvd %u\n" unsigned wrote_out; /* total stdout bytes */ unsigned wrote_net; /* total net bytes */ #endif char *proggie0saved; /* ouraddr is never NULL and goes through three states as we progress: 1 - local address before bind (IP/port possibly zero) 2 - local address after bind (port is nonzero) 3 - local address after connect??/recv/accept (IP and port are nonzero) */ struct len_and_sockaddr *ouraddr; /* themaddr is NULL if no peer hostname[:port] specified on command line */ struct len_and_sockaddr *themaddr; /* remend is set after connect/recv/accept to the actual ip:port of peer */ struct len_and_sockaddr remend; jmp_buf jbuf; /* timer crud */ char bigbuf_in[BIGSIZ]; /* data buffers */ char bigbuf_net[BIGSIZ]; }; #define G (*ptr_to_globals) #define wrote_out (G.wrote_out ) #define wrote_net (G.wrote_net ) #define ouraddr (G.ouraddr ) #define themaddr (G.themaddr ) #define remend (G.remend ) #define jbuf (G.jbuf ) #define bigbuf_in (G.bigbuf_in ) #define bigbuf_net (G.bigbuf_net) #define o_verbose (G.o_verbose ) #define o_wait (G.o_wait ) #if ENABLE_NC_EXTRA #define o_interval (G.o_interval) #else #define o_interval 0 #endif #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ } while (0) /* Must match getopt32 call! */ enum { OPT_n = (1 << 0), OPT_p = (1 << 1), OPT_s = (1 << 2), OPT_u = (1 << 3), OPT_v = (1 << 4), OPT_w = (1 << 5), OPT_l = (1 << 6) * ENABLE_NC_SERVER, OPT_k = (1 << 7) * ENABLE_NC_SERVER, OPT_i = (1 << (6+2*ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA, OPT_o = (1 << (7+2*ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA, OPT_z = (1 << (8+2*ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA, }; #define o_nflag (option_mask32 & OPT_n) #define o_udpmode (option_mask32 & OPT_u) #if ENABLE_NC_EXTRA #define o_ofile (option_mask32 & OPT_o) #define o_zero (option_mask32 & OPT_z) #else #define o_ofile 0 #define o_zero 0 #endif /* Debug: squirt whatever message and sleep a bit so we can see it go by. */ /* Beware: writes to stdOUT... */ #if 0 #define Debug(...) do { printf(__VA_ARGS__); printf("\n"); fflush_all(); sleep(1); } while (0) #else #define Debug(...) do { } while (0) #endif #define holler_error(...) do { if (o_verbose) bb_error_msg(__VA_ARGS__); } while (0) #define holler_perror(...) do { if (o_verbose) bb_perror_msg(__VA_ARGS__); } while (0) /* catch: no-brainer interrupt handler */ static void catch(int sig) { if (o_verbose > 1) /* normally we don't care */ fprintf(stderr, SENT_N_RECV_M, wrote_net, wrote_out); fprintf(stderr, "punt!\n"); kill_myself_with_sig(sig); } /* unarm */ static void unarm(void) { signal(SIGALRM, SIG_IGN); alarm(0); } /* timeout and other signal handling cruft */ static void tmtravel(int sig UNUSED_PARAM) { unarm(); longjmp(jbuf, 1); } /* arm: set the timer. */ static void arm(unsigned secs) { signal(SIGALRM, tmtravel); alarm(secs); } /* findline: find the next newline in a buffer; return inclusive size of that "line", or the entire buffer size, so the caller knows how much to then write(). Not distinguishing \n vs \r\n for the nonce; it just works as is... */ static unsigned findline(char *buf, unsigned siz) { char * p; int x; if (!buf) /* various sanity checks... */ return 0; if (siz > BIGSIZ) return 0; x = siz; for (p = buf; x > 0; x--) { if (*p == '\n') { x = (int) (p - buf); x++; /* 'sokay if it points just past the end! */ Debug("findline returning %d", x); return x; } p++; } /* for */ Debug("findline returning whole thing: %d", siz); return siz; } /* findline */ /* doexec: fiddle all the file descriptors around, and hand off to another prog. Sort of like a one-off "poor man's inetd". This is the only section of code that would be security-critical, which is why it's ifdefed out by default. Use at your own hairy risk; if you leave shells lying around behind open listening ports you deserve to lose!! */ static int doexec(char **proggie) NORETURN; static int doexec(char **proggie) { if (G.proggie0saved) proggie[0] = G.proggie0saved; xmove_fd(netfd, 0); dup2(0, 1); /* dup2(0, 2); - do we *really* want this? NO! * exec'ed prog can do it yourself, if needed */ BB_EXECVP_or_die(proggie); } /* connect_w_timeout: return an fd for one of an open outbound TCP connection, a UDP stub-socket thingie, or an unconnected TCP or UDP socket to listen on. Examines various global o_blah flags to figure out what to do. lad can be NULL, then socket is not bound to any local ip[:port] */ static int connect_w_timeout(int fd) { int rr; /* wrap connect inside a timer, and hit it */ arm(o_wait); if (setjmp(jbuf) == 0) { rr = connect(fd, &themaddr->u.sa, themaddr->len); unarm(); } else { /* setjmp: connect failed... */ rr = -1; errno = ETIMEDOUT; /* fake it */ } return rr; } /* dolisten: listens for incoming and returns an open connection *from* someplace. If we were given host/port args, any connections from elsewhere are rejected. This in conjunction with local-address binding should limit things nicely... */ static void dolisten(int is_persistent, char **proggie) { int rr; if (!o_udpmode) xlisten(netfd, 1); /* TCP: gotta listen() before we can get */ /* Various things that follow temporarily trash bigbuf_net, which might contain a copy of any recvfrom()ed packet, but we'll read() another copy later. */ /* I can't believe I have to do all this to get my own goddamn bound address and port number. It should just get filled in during bind() or something. All this is only useful if we didn't say -p for listening, since if we said -p we *know* what port we're listening on. At any rate we won't bother with it all unless we wanted to see it, although listening quietly on a random unknown port is probably not very useful without "netstat". */ if (o_verbose) { char *addr; getsockname(netfd, &ouraddr->u.sa, &ouraddr->len); //if (rr < 0) // bb_perror_msg_and_die("getsockname after bind"); addr = xmalloc_sockaddr2dotted(&ouraddr->u.sa); fprintf(stderr, "listening on %s ...\n", addr); free(addr); } if (o_udpmode) { /* UDP is a speeeeecial case -- we have to do I/O *and* get the calling party's particulars all at once, listen() and accept() don't apply. At least in the BSD universe, however, recvfrom/PEEK is enough to tell us something came in, and we can set things up so straight read/write actually does work after all. Yow. YMMV on strange platforms! */ /* I'm not completely clear on how this works -- BSD seems to make UDP just magically work in a connect()ed context, but we'll undoubtedly run into systems this deal doesn't work on. For now, we apparently have to issue a connect() on our just-tickled socket so we can write() back. Again, why the fuck doesn't it just get filled in and taken care of?! This hack is anything but optimal. Basically, if you want your listener to also be able to send data back, you need this connect() line, which also has the side effect that now anything from a different source or even a different port on the other end won't show up and will cause ICMP errors. I guess that's what they meant by "connect". Let's try to remember what the "U" is *really* for, eh? */ /* If peer address is specified, connect to it */ remend.len = LSA_SIZEOF_SA; if (themaddr) { remend = *themaddr; xconnect(netfd, &themaddr->u.sa, themaddr->len); } /* peek first packet and remember peer addr */ arm(o_wait); /* might as well timeout this, too */ if (setjmp(jbuf) == 0) { /* do timeout for initial connect */ /* (*ouraddr) is prefilled with "default" address */ /* and here we block... */ rr = recv_from_to(netfd, NULL, 0, MSG_PEEK, /*was bigbuf_net, BIGSIZ*/ &remend.u.sa, &ouraddr->u.sa, ouraddr->len); if (rr < 0) bb_perror_msg_and_die("recvfrom"); unarm(); } else bb_error_msg_and_die("timeout"); /* Now we learned *to which IP* peer has connected, and we want to anchor our socket on it, so that our outbound packets will have correct local IP. Unfortunately, bind() on already bound socket will fail now (EINVAL): xbind(netfd, &ouraddr->u.sa, ouraddr->len); Need to read the packet, save data, close this socket and create new one, and bind() it. TODO */ if (!themaddr) xconnect(netfd, &remend.u.sa, ouraddr->len); } else { /* TCP */ another: arm(o_wait); /* wrap this in a timer, too; 0 = forever */ if (setjmp(jbuf) == 0) { again: remend.len = LSA_SIZEOF_SA; rr = accept(netfd, &remend.u.sa, &remend.len); if (rr < 0) bb_perror_msg_and_die("accept"); if (themaddr) { int sv_port, port, r; sv_port = get_nport(&remend.u.sa); /* save */ port = get_nport(&themaddr->u.sa); if (port == 0) { /* "nc -nl -p LPORT RHOST" (w/o RPORT!): * we should accept any remote port */ set_nport(&remend.u.sa, 0); /* blot out remote port# */ } r = memcmp(&remend.u.sa, &themaddr->u.sa, remend.len); set_nport(&remend.u.sa, sv_port); /* restore */ if (r != 0) { /* nc 1.10 bails out instead, and its error message * is not suppressed by o_verbose */ if (o_verbose) { char *remaddr = xmalloc_sockaddr2dotted(&remend.u.sa); bb_error_msg("connect from wrong ip/port %s ignored", remaddr); free(remaddr); } close(rr); goto again; } } unarm(); } else bb_error_msg_and_die("timeout"); if (is_persistent && proggie) { /* -l -k -e PROG */ signal(SIGCHLD, SIG_IGN); /* no zombies please */ if (xvfork() != 0) { /* parent: go back and accept more connections */ close(rr); goto another; } /* child */ signal(SIGCHLD, SIG_DFL); } xmove_fd(rr, netfd); /* dump the old socket, here's our new one */ /* find out what address the connection was *to* on our end, in case we're doing a listen-on-any on a multihomed machine. This allows one to offer different services via different alias addresses, such as the "virtual web site" hack. */ getsockname(netfd, &ouraddr->u.sa, &ouraddr->len); //if (rr < 0) // bb_perror_msg_and_die("getsockname after accept"); } if (o_verbose) { char *lcladdr, *remaddr, *remhostname; #if ENABLE_NC_EXTRA && defined(IP_OPTIONS) /* If we can, look for any IP options. Useful for testing the receiving end of such things, and is a good exercise in dealing with it. We do this before the connect message, to ensure that the connect msg is uniformly the LAST thing to emerge after all the intervening crud. Doesn't work for UDP on any machines I've tested, but feel free to surprise me. */ char optbuf[40]; socklen_t x = sizeof(optbuf); rr = getsockopt(netfd, IPPROTO_IP, IP_OPTIONS, optbuf, &x); if (rr >= 0 && x) { /* we've got options, lessee em... */ *bin2hex(bigbuf_net, optbuf, x) = '\0'; fprintf(stderr, "IP options: %s\n", bigbuf_net); } #endif /* now check out who it is. We don't care about mismatched DNS names here, but any ADDR and PORT we specified had better fucking well match the caller. Converting from addr to inet_ntoa and back again is a bit of a kludge, but gethostpoop wants a string and there's much gnarlier code out there already, so I don't feel bad. The *real* question is why BFD sockets wasn't designed to allow listens for connections *from* specific hosts/ports, instead of requiring the caller to accept the connection and then reject undesireable ones by closing. In other words, we need a TCP MSG_PEEK. */ /* bbox: removed most of it */ lcladdr = xmalloc_sockaddr2dotted(&ouraddr->u.sa); remaddr = xmalloc_sockaddr2dotted(&remend.u.sa); remhostname = o_nflag ? remaddr : xmalloc_sockaddr2host(&remend.u.sa); fprintf(stderr, "connect to %s from %s (%s)\n", lcladdr, remhostname, remaddr); free(lcladdr); free(remaddr); if (!o_nflag) free(remhostname); } if (proggie) doexec(proggie); } /* udptest: fire a couple of packets at a UDP target port, just to see if it's really there. On BSD kernels, ICMP host/port-unreachable errors get delivered to our socket as ECONNREFUSED write errors. On SV kernels, we lose; we'll have to collect and analyze raw ICMP ourselves a la satan's probe_udp_ports backend. Guess where one could swipe the appropriate code from... Use the time delay between writes if given, otherwise use the "tcp ping" trick for getting the RTT. [I got that idea from pluvius, and warped it.] Return either the original fd, or clean up and return -1. */ #if ENABLE_NC_EXTRA static int udptest(void) { int rr; rr = write(netfd, bigbuf_in, 1); if (rr != 1) bb_perror_msg("udptest first write"); if (o_wait) sleep(o_wait); // can be interrupted! while (t) nanosleep(&t)? else { /* use the tcp-ping trick: try connecting to a normally refused port, which causes us to block for the time that SYN gets there and RST gets back. Not completely reliable, but it *does* mostly work. */ /* Set a temporary connect timeout, so packet filtration doesnt cause us to hang forever, and hit it */ o_wait = 5; /* enough that we'll notice?? */ rr = xsocket(ouraddr->u.sa.sa_family, SOCK_STREAM, 0); set_nport(&themaddr->u.sa, htons(SLEAZE_PORT)); connect_w_timeout(rr); /* don't need to restore themaddr's port, it's not used anymore */ close(rr); o_wait = 0; /* restore */ } rr = write(netfd, bigbuf_in, 1); return (rr != 1); /* if rr == 1, return 0 (success) */ } #else int udptest(void); #endif /* oprint: Hexdump bytes shoveled either way to a running logfile, in the format: D offset - - - - --- 16 bytes --- - - - - # .... ascii ..... where "which" sets the direction indicator, D: 0 -- sent to network, or ">" 1 -- rcvd and printed to stdout, or "<" and "buf" and "n" are data-block and length. If the current block generates a partial line, so be it; we *want* that lockstep indication of who sent what when. Adapted from dgaudet's original example -- but must be ripping *fast*, since we don't want to be too disk-bound... */ #if ENABLE_NC_EXTRA static void oprint(int direction, unsigned char *p, unsigned bc) { unsigned obc; /* current "global" offset */ unsigned x; unsigned char *op; /* out hexdump ptr */ unsigned char *ap; /* out asc-dump ptr */ unsigned char stage[100]; if (bc == 0) return; obc = wrote_net; /* use the globals! */ if (direction == '<') obc = wrote_out; stage[0] = direction; stage[59] = '#'; /* preload separator */ stage[60] = ' '; do { /* for chunk-o-data ... */ x = 16; if (bc < 16) { /* memset(&stage[bc*3 + 11], ' ', 16*3 - bc*3); */ memset(&stage[11], ' ', 16*3); x = bc; } sprintf((char *)&stage[1], " %8.8x ", obc); /* xxx: still slow? */ bc -= x; /* fix current count */ obc += x; /* fix current offset */ op = &stage[11]; /* where hex starts */ ap = &stage[61]; /* where ascii starts */ do { /* for line of dump, however long ... */ *op++ = 0x20 | bb_hexdigits_upcase[*p >> 4]; *op++ = 0x20 | bb_hexdigits_upcase[*p & 0x0f]; *op++ = ' '; if ((*p > 31) && (*p < 127)) *ap = *p; /* printing */ else *ap = '.'; /* nonprinting, loose def */ ap++; p++; } while (--x); *ap++ = '\n'; /* finish the line */ xwrite(ofd, stage, ap - stage); } while (bc); } #else void oprint(int direction, unsigned char *p, unsigned bc); #endif /* readwrite: handle stdin/stdout/network I/O. Bwahaha!! -- the select loop from hell. In this instance, return what might become our exit status. */ static int readwrite(void) { int rr; char *zp = zp; /* gcc */ /* stdin buf ptr */ char *np = np; /* net-in buf ptr */ unsigned rzleft; unsigned rnleft; unsigned netretry; /* net-read retry counter */ unsigned fds_open; /* if you don't have all this FD_* macro hair in sys/types.h, you'll have to either find it or do your own bit-bashing: *ding1 |= (1 << fd), etc... */ fd_set ding1; /* for select loop */ fd_set ding2; FD_ZERO(&ding1); FD_SET(netfd, &ding1); FD_SET(STDIN_FILENO, &ding1); fds_open = 2; netretry = 2; rzleft = rnleft = 0; if (o_interval) sleep(o_interval); /* pause *before* sending stuff, too */ /* and now the big ol' select shoveling loop ... */ /* nc 1.10 has "while (FD_ISSET(netfd)" here */ while (fds_open) { unsigned wretry = 8200; /* net-write sanity counter */ ding2 = ding1; /* FD_COPY ain't portable... */ /* some systems, notably linux, crap into their select timers on return, so we create a expendable copy and give *that* to select. */ if (o_wait) { struct timeval tmp_timer; tmp_timer.tv_sec = o_wait; tmp_timer.tv_usec = 0; /* highest possible fd is netfd (3) */ rr = select(netfd+1, &ding2, NULL, NULL, &tmp_timer); } else rr = select(netfd+1, &ding2, NULL, NULL, NULL); if (rr < 0 && errno != EINTR) { /* might have gotten ^Zed, etc */ holler_perror("select"); close(netfd); return 1; } /* if we have a timeout AND stdin is closed AND we haven't heard anything from the net during that time, assume it's dead and close it too. */ if (rr == 0) { if (!FD_ISSET(STDIN_FILENO, &ding1)) { netretry--; /* we actually try a coupla times. */ if (!netretry) { if (o_verbose > 1) /* normally we don't care */ fprintf(stderr, "net timeout\n"); /*close(netfd); - redundant, exit will do it */ return 0; /* not an error! */ } } } /* select timeout */ /* xxx: should we check the exception fds too? The read fds seem to give us the right info, and none of the examples I found bothered. */ /* Ding!! Something arrived, go check all the incoming hoppers, net first */ if (FD_ISSET(netfd, &ding2)) { /* net: ding! */ rr = read(netfd, bigbuf_net, BIGSIZ); if (rr <= 0) { if (rr < 0 && o_verbose > 1) { /* nc 1.10 doesn't do this */ bb_perror_msg("net read"); } FD_CLR(netfd, &ding1); /* net closed */ fds_open--; rzleft = 0; /* can't write anymore: broken pipe */ } else { rnleft = rr; np = bigbuf_net; } Debug("got %d from the net, errno %d", rr, errno); } /* net:ding */ /* if we're in "slowly" mode there's probably still stuff in the stdin buffer, so don't read unless we really need MORE INPUT! MORE INPUT! */ if (rzleft) goto shovel; /* okay, suck more stdin */ if (FD_ISSET(STDIN_FILENO, &ding2)) { /* stdin: ding! */ rr = read(STDIN_FILENO, bigbuf_in, BIGSIZ); /* Considered making reads here smaller for UDP mode, but 8192-byte mobygrams are kinda fun and exercise the reassembler. */ if (rr <= 0) { /* at end, or fukt, or ... */ FD_CLR(STDIN_FILENO, &ding1); /* disable stdin */ /*close(STDIN_FILENO); - not really necessary */ /* Let peer know we have no more data */ /* nc 1.10 doesn't do this: */ shutdown(netfd, SHUT_WR); fds_open--; } else { rzleft = rr; zp = bigbuf_in; } } /* stdin:ding */ shovel: /* now that we've dingdonged all our thingdings, send off the results. Geez, why does this look an awful lot like the big loop in "rsh"? ... not sure if the order of this matters, but write net -> stdout first. */ if (rnleft) { rr = write(STDOUT_FILENO, np, rnleft); if (rr > 0) { if (o_ofile) /* log the stdout */ oprint('<', (unsigned char *)np, rr); np += rr; rnleft -= rr; wrote_out += rr; /* global count */ } Debug("wrote %d to stdout, errno %d", rr, errno); } /* rnleft */ if (rzleft) { if (o_interval) /* in "slowly" mode ?? */ rr = findline(zp, rzleft); else rr = rzleft; rr = write(netfd, zp, rr); /* one line, or the whole buffer */ if (rr > 0) { if (o_ofile) /* log what got sent */ oprint('>', (unsigned char *)zp, rr); zp += rr; rzleft -= rr; wrote_net += rr; /* global count */ } Debug("wrote %d to net, errno %d", rr, errno); } /* rzleft */ if (o_interval) { /* cycle between slow lines, or ... */ sleep(o_interval); continue; /* ...with hairy select loop... */ } if (rzleft || rnleft) { /* shovel that shit till they ain't */ wretry--; /* none left, and get another load */ /* net write retries sometimes happen on UDP connections */ if (!wretry) { /* is something hung? */ holler_error("too many output retries"); return 1; } goto shovel; } } /* while (fds_open) */ /* XXX: maybe want a more graceful shutdown() here, or screw around with linger times?? I suspect that I don't need to since I'm always doing blocking reads and writes and my own manual "last ditch" efforts to read the net again after a timeout. I haven't seen any screwups yet, but it's not like my test network is particularly busy... */ close(netfd); return 0; } /* readwrite */ /* main: now we pull it all together... */ int nc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int nc_main(int argc UNUSED_PARAM, char **argv) { char *str_p, *str_s; IF_NC_EXTRA(char *str_i, *str_o;) char *themdotted = themdotted; /* for compiler */ char **proggie; int x; unsigned cnt_l = 0; unsigned o_lport = 0; INIT_G(); /* catch a signal or two for cleanup */ bb_signals(0 + (1 << SIGINT) + (1 << SIGQUIT) + (1 << SIGTERM) , catch); /* and suppress others... */ bb_signals(0 #ifdef SIGURG + (1 << SIGURG) #endif + (1 << SIGPIPE) /* important! */ , SIG_IGN); proggie = argv; while (*++proggie) { if (strcmp(*proggie, "-e") == 0) { *proggie = NULL; proggie++; goto e_found; } /* -e PROG [ARGS] ? */ /* (aboriginal linux uses this form) */ if (proggie[0][0] == '-') { char *optpos = *proggie + 1; /* Skip all valid opts w/o params */ optpos = optpos + strspn(optpos, "nuv"IF_NC_SERVER("lk")IF_NC_EXTRA("z")); if (*optpos == 'e' && !optpos[1]) { *optpos = '\0'; proggie++; G.proggie0saved = *proggie; *proggie = NULL; /* terminate argv for getopt32 */ goto e_found; } } } proggie = NULL; e_found: // -g -G -t -r deleted, unimplemented -a deleted too opt_complementary = "?2:vv:ll:w+"; /* max 2 params; -v and -l are counters; -w N */ getopt32(argv, "np:s:uvw:" IF_NC_SERVER("lk") IF_NC_EXTRA("i:o:z"), &str_p, &str_s, &o_wait IF_NC_EXTRA(, &str_i, &str_o), &o_verbose IF_NC_SERVER(, &cnt_l)); argv += optind; #if ENABLE_NC_EXTRA if (option_mask32 & OPT_i) /* line-interval time */ o_interval = xatou_range(str_i, 1, 0xffff); #endif #if ENABLE_NC_SERVER //if (option_mask32 & OPT_l) /* listen mode */ if (option_mask32 & OPT_k) /* persistent server mode */ cnt_l = 2; #endif //if (option_mask32 & OPT_n) /* numeric-only, no DNS lookups */ //if (option_mask32 & OPT_o) /* hexdump log */ if (option_mask32 & OPT_p) { /* local source port */ o_lport = bb_lookup_port(str_p, o_udpmode ? "udp" : "tcp", 0); if (!o_lport) bb_error_msg_and_die("bad local port '%s'", str_p); } //if (option_mask32 & OPT_r) /* randomize various things */ //if (option_mask32 & OPT_u) /* use UDP */ //if (option_mask32 & OPT_v) /* verbose */ //if (option_mask32 & OPT_w) /* wait time */ //if (option_mask32 & OPT_z) /* little or no data xfer */ /* We manage our fd's so that they are never 0,1,2 */ /*bb_sanitize_stdio(); - not needed */ if (argv[0]) { themaddr = xhost2sockaddr(argv[0], argv[1] ? bb_lookup_port(argv[1], o_udpmode ? "udp" : "tcp", 0) : 0); } /* create & bind network socket */ x = (o_udpmode ? SOCK_DGRAM : SOCK_STREAM); if (option_mask32 & OPT_s) { /* local address */ /* if o_lport is still 0, then we will use random port */ ouraddr = xhost2sockaddr(str_s, o_lport); #ifdef BLOAT /* prevent spurious "UDP listen needs !0 port" */ o_lport = get_nport(ouraddr); o_lport = ntohs(o_lport); #endif x = xsocket(ouraddr->u.sa.sa_family, x, 0); } else { /* We try IPv6, then IPv4, unless addr family is * implicitly set by way of remote addr/port spec */ x = xsocket_type(&ouraddr, (themaddr ? themaddr->u.sa.sa_family : AF_UNSPEC), x); if (o_lport) set_nport(&ouraddr->u.sa, htons(o_lport)); } xmove_fd(x, netfd); setsockopt_reuseaddr(netfd); if (o_udpmode) socket_want_pktinfo(netfd); if (!ENABLE_FEATURE_UNIX_LOCAL || cnt_l != 0 /* listen */ || ouraddr->u.sa.sa_family != AF_UNIX ) { xbind(netfd, &ouraddr->u.sa, ouraddr->len); } #if 0 setsockopt(netfd, SOL_SOCKET, SO_RCVBUF, &o_rcvbuf, sizeof o_rcvbuf); setsockopt(netfd, SOL_SOCKET, SO_SNDBUF, &o_sndbuf, sizeof o_sndbuf); #endif #ifdef BLOAT if (OPT_l && (option_mask32 & (OPT_u|OPT_l)) == (OPT_u|OPT_l)) { /* apparently UDP can listen ON "port 0", but that's not useful */ if (!o_lport) bb_error_msg_and_die("UDP listen needs nonzero -p port"); } #endif if (proggie) { close(STDIN_FILENO); /* won't need stdin */ option_mask32 &= ~OPT_o; /* -o with -e is meaningless! */ } #if ENABLE_NC_EXTRA if (o_ofile) xmove_fd(xopen(str_o, O_WRONLY|O_CREAT|O_TRUNC), ofd); #endif if (cnt_l != 0) { dolisten((cnt_l - 1), proggie); /* dolisten does its own connect reporting */ x = readwrite(); /* it even works with UDP! */ } else { /* Outbound connects. Now we're more picky about args... */ if (!themaddr) bb_show_usage(); remend = *themaddr; if (o_verbose) themdotted = xmalloc_sockaddr2dotted(&themaddr->u.sa); x = connect_w_timeout(netfd); if (o_zero && x == 0 && o_udpmode) /* if UDP scanning... */ x = udptest(); if (x == 0) { /* Yow, are we OPEN YET?! */ if (o_verbose) fprintf(stderr, "%s (%s) open\n", argv[0], themdotted); if (proggie) /* exec is valid for outbound, too */ doexec(proggie); if (!o_zero) x = readwrite(); } else { /* connect or udptest wasn't successful */ x = 1; /* exit status */ /* if we're scanning at a "one -v" verbosity level, don't print refusals. Give it another -v if you want to see everything. */ if (o_verbose > 1 || (o_verbose && errno != ECONNREFUSED)) bb_perror_msg("%s (%s)", argv[0], themdotted); } } if (o_verbose > 1) /* normally we don't care */ fprintf(stderr, SENT_N_RECV_M, wrote_net, wrote_out); return x; } busybox-1.22.1/networking/ip.c0000644000000000000000000001374012263563520014742 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Authors: Alexey Kuznetsov, * * Changes: * Rani Assaf 980929: resolve addresses * Bernhard Reutner-Fischer rewrote to use index_in_substr_array */ /* would need to make the " | " optional depending on more than one selected: */ //usage:#define ip_trivial_usage //usage: "[OPTIONS] {" //usage: IF_FEATURE_IP_ADDRESS("address | ") //usage: IF_FEATURE_IP_ROUTE("route | ") //usage: IF_FEATURE_IP_LINK("link | ") //usage: IF_FEATURE_IP_TUNNEL("tunnel | ") //usage: IF_FEATURE_IP_RULE("rule") //usage: "} {COMMAND}" //usage:#define ip_full_usage "\n\n" //usage: "ip [OPTIONS] OBJECT {COMMAND}\n" //usage: "where OBJECT := {" //usage: IF_FEATURE_IP_ADDRESS("address | ") //usage: IF_FEATURE_IP_ROUTE("route | ") //usage: IF_FEATURE_IP_LINK("link | ") //usage: IF_FEATURE_IP_TUNNEL("tunnel | ") //usage: IF_FEATURE_IP_RULE("rule") //usage: "}\n" //usage: "OPTIONS := { -f[amily] { inet | inet6 | link } | -o[neline] }" //usage: //usage:#define ipaddr_trivial_usage //usage: "{ {add|del} IFADDR dev STRING | {show|flush}\n" //usage: " [dev STRING] [to PREFIX] }" //usage:#define ipaddr_full_usage "\n\n" //usage: "ipaddr {add|delete} IFADDR dev STRING\n" //usage: "ipaddr {show|flush} [dev STRING] [scope SCOPE-ID]\n" //usage: " [to PREFIX] [label PATTERN]\n" //usage: " IFADDR := PREFIX | ADDR peer PREFIX\n" //usage: " [broadcast ADDR] [anycast ADDR]\n" //usage: " [label STRING] [scope SCOPE-ID]\n" //usage: " SCOPE-ID := [host | link | global | NUMBER]" //usage: //usage:#define iplink_trivial_usage //usage: "{ set DEVICE { up | down | arp { on | off } | show [DEVICE] }" //usage:#define iplink_full_usage "\n\n" //usage: "iplink set DEVICE { up | down | arp | multicast { on | off } |\n" //usage: " dynamic { on | off } |\n" //usage: " mtu MTU }\n" //usage: "iplink show [DEVICE]" //usage: //usage:#define iproute_trivial_usage //usage: "{ list | flush | add | del | change | append |\n" //usage: " replace | test } ROUTE" //usage:#define iproute_full_usage "\n\n" //usage: "iproute { list | flush } SELECTOR\n" //usage: "iproute get ADDRESS [from ADDRESS iif STRING]\n" //usage: " [oif STRING] [tos TOS]\n" //usage: "iproute { add | del | change | append | replace | test } ROUTE\n" //usage: " SELECTOR := [root PREFIX] [match PREFIX] [proto RTPROTO]\n" //usage: " ROUTE := [TYPE] PREFIX [tos TOS] [proto RTPROTO] [metric METRIC]" //usage: //usage:#define iprule_trivial_usage //usage: "{[list | add | del] RULE}" //usage:#define iprule_full_usage "\n\n" //usage: "iprule [list | add | del] SELECTOR ACTION\n" //usage: " SELECTOR := [from PREFIX] [to PREFIX] [tos TOS] [fwmark FWMARK]\n" //usage: " [dev STRING] [pref NUMBER]\n" //usage: " ACTION := [table TABLE_ID] [nat ADDRESS]\n" //usage: " [prohibit | reject | unreachable]\n" //usage: " [realms [SRCREALM/]DSTREALM]\n" //usage: " TABLE_ID := [local | main | default | NUMBER]" //usage: //usage:#define iptunnel_trivial_usage //usage: "{ add | change | del | show } [NAME]\n" //usage: " [mode { ipip | gre | sit }]\n" //usage: " [remote ADDR] [local ADDR] [ttl TTL]" //usage:#define iptunnel_full_usage "\n\n" //usage: "iptunnel { add | change | del | show } [NAME]\n" //usage: " [mode { ipip | gre | sit }] [remote ADDR] [local ADDR]\n" //usage: " [[i|o]seq] [[i|o]key KEY] [[i|o]csum]\n" //usage: " [ttl TTL] [tos TOS] [[no]pmtudisc] [dev PHYS_DEV]" #include "libbb.h" #include "libiproute/utils.h" #include "libiproute/ip_common.h" #if ENABLE_FEATURE_IP_ADDRESS \ || ENABLE_FEATURE_IP_ROUTE \ || ENABLE_FEATURE_IP_LINK \ || ENABLE_FEATURE_IP_TUNNEL \ || ENABLE_FEATURE_IP_RULE static int FAST_FUNC ip_print_help(char **argv UNUSED_PARAM) { bb_show_usage(); } typedef int FAST_FUNC (*ip_func_ptr_t)(char**); static int ip_do(ip_func_ptr_t ip_func, char **argv) { argv = ip_parse_common_args(argv + 1); return ip_func(argv); } #if ENABLE_FEATURE_IP_ADDRESS int ipaddr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ipaddr_main(int argc UNUSED_PARAM, char **argv) { return ip_do(do_ipaddr, argv); } #endif #if ENABLE_FEATURE_IP_LINK int iplink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int iplink_main(int argc UNUSED_PARAM, char **argv) { return ip_do(do_iplink, argv); } #endif #if ENABLE_FEATURE_IP_ROUTE int iproute_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int iproute_main(int argc UNUSED_PARAM, char **argv) { return ip_do(do_iproute, argv); } #endif #if ENABLE_FEATURE_IP_RULE int iprule_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int iprule_main(int argc UNUSED_PARAM, char **argv) { return ip_do(do_iprule, argv); } #endif #if ENABLE_FEATURE_IP_TUNNEL int iptunnel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int iptunnel_main(int argc UNUSED_PARAM, char **argv) { return ip_do(do_iptunnel, argv); } #endif int ip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ip_main(int argc UNUSED_PARAM, char **argv) { static const char keywords[] ALIGN1 = IF_FEATURE_IP_ADDRESS("address\0") IF_FEATURE_IP_ROUTE("route\0") IF_FEATURE_IP_ROUTE("r\0") IF_FEATURE_IP_LINK("link\0") IF_FEATURE_IP_TUNNEL("tunnel\0") IF_FEATURE_IP_TUNNEL("tunl\0") IF_FEATURE_IP_RULE("rule\0") ; static const ip_func_ptr_t ip_func_ptrs[] = { ip_print_help, IF_FEATURE_IP_ADDRESS(do_ipaddr,) IF_FEATURE_IP_ROUTE(do_iproute,) IF_FEATURE_IP_ROUTE(do_iproute,) IF_FEATURE_IP_LINK(do_iplink,) IF_FEATURE_IP_TUNNEL(do_iptunnel,) IF_FEATURE_IP_TUNNEL(do_iptunnel,) IF_FEATURE_IP_RULE(do_iprule,) }; ip_func_ptr_t ip_func; int key; argv = ip_parse_common_args(argv + 1); key = *argv ? index_in_substrings(keywords, *argv++) : -1; ip_func = ip_func_ptrs[key + 1]; return ip_func(argv); } #endif /* any of ENABLE_FEATURE_IP_xxx is 1 */ busybox-1.22.1/networking/tunctl.c0000644000000000000000000000766212263563520015651 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * tun devices controller * * Copyright (C) 2008 by Vladimir Dronnikov * * Original code: * Jeff Dike * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define tunctl_trivial_usage //usage: "[-f device] ([-t name] | -d name)" IF_FEATURE_TUNCTL_UG(" [-u owner] [-g group] [-b]") //usage:#define tunctl_full_usage "\n\n" //usage: "Create or delete tun interfaces\n" //usage: "\n -f name tun device (/dev/net/tun)" //usage: "\n -t name Create iface 'name'" //usage: "\n -d name Delete iface 'name'" //usage: IF_FEATURE_TUNCTL_UG( //usage: "\n -u owner Set iface owner" //usage: "\n -g group Set iface group" //usage: "\n -b Brief output" //usage: ) //usage: //usage:#define tunctl_example_usage //usage: "# tunctl\n" //usage: "# tunctl -d tun0\n" #include #include #include #include "libbb.h" /* TUNSETGROUP appeared in 2.6.23 */ #ifndef TUNSETGROUP #define TUNSETGROUP _IOW('T', 206, int) #endif #define IOCTL(a, b, c) ioctl_or_perror_and_die(a, b, c, NULL) #if 1 int tunctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int tunctl_main(int argc UNUSED_PARAM, char **argv) { struct ifreq ifr; int fd; const char *opt_name = "tap%d"; const char *opt_device = "/dev/net/tun"; #if ENABLE_FEATURE_TUNCTL_UG const char *opt_user, *opt_group; long user = -1, group = -1; #endif unsigned opts; enum { OPT_f = 1 << 0, // control device name (/dev/net/tun) OPT_t = 1 << 1, // create named interface OPT_d = 1 << 2, // delete named interface #if ENABLE_FEATURE_TUNCTL_UG OPT_u = 1 << 3, // set new interface owner OPT_g = 1 << 4, // set new interface group OPT_b = 1 << 5, // brief output #endif }; opt_complementary = "=0:t--d:d--t"; // no arguments; t ^ d opts = getopt32(argv, "f:t:d:" IF_FEATURE_TUNCTL_UG("u:g:b"), &opt_device, &opt_name, &opt_name IF_FEATURE_TUNCTL_UG(, &opt_user, &opt_group)); // select device memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; strncpy_IFNAMSIZ(ifr.ifr_name, opt_name); // open device fd = xopen(opt_device, O_RDWR); IOCTL(fd, TUNSETIFF, (void *)&ifr); // delete? if (opts & OPT_d) { IOCTL(fd, TUNSETPERSIST, (void *)(uintptr_t)0); bb_info_msg("Set '%s' %spersistent", ifr.ifr_name, "non"); return EXIT_SUCCESS; } // create #if ENABLE_FEATURE_TUNCTL_UG if (opts & OPT_g) { group = xgroup2gid(opt_group); IOCTL(fd, TUNSETGROUP, (void *)(uintptr_t)group); } else user = geteuid(); if (opts & OPT_u) user = xuname2uid(opt_user); IOCTL(fd, TUNSETOWNER, (void *)(uintptr_t)user); #endif IOCTL(fd, TUNSETPERSIST, (void *)(uintptr_t)1); // show info #if ENABLE_FEATURE_TUNCTL_UG if (opts & OPT_b) { puts(ifr.ifr_name); } else { printf("Set '%s' %spersistent", ifr.ifr_name, ""); printf(" and owned by uid %ld", user); if (group != -1) printf(" gid %ld", group); bb_putchar('\n'); } #else puts(ifr.ifr_name); #endif return EXIT_SUCCESS; } #else /* -210 bytes: */ int tunctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int tunctl_main(int argc UNUSED_PARAM, char **argv) { struct ifreq ifr; int fd; const char *opt_name = "tap%d"; const char *opt_device = "/dev/net/tun"; unsigned opts; enum { OPT_f = 1 << 0, // control device name (/dev/net/tun) OPT_t = 1 << 1, // create named interface OPT_d = 1 << 2, // delete named interface }; opt_complementary = "=0:t--d:d--t"; // no arguments; t ^ d opts = getopt32(argv, "f:t:d:u:g:b", // u, g, b accepted and ignored &opt_device, &opt_name, &opt_name, NULL, NULL); // set interface name memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; strncpy_IFNAMSIZ(ifr.ifr_name, opt_name); // open device fd = xopen(opt_device, O_RDWR); IOCTL(fd, TUNSETIFF, (void *)&ifr); // create or delete interface IOCTL(fd, TUNSETPERSIST, (void *)(uintptr_t)(0 == (opts & OPT_d))); return EXIT_SUCCESS; } #endif busybox-1.22.1/networking/tc.c0000644000000000000000000003647012263563520014745 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Authors: Alexey Kuznetsov, * * Bernhard Reutner-Fischer adjusted for busybox */ //usage:#define tc_trivial_usage /* //usage: "[OPTIONS] OBJECT CMD [dev STRING]" */ //usage: "OBJECT CMD [dev STRING]" //usage:#define tc_full_usage "\n\n" //usage: "OBJECT: {qdisc|class|filter}\n" //usage: "CMD: {add|del|change|replace|show}\n" //usage: "\n" //usage: "qdisc [ handle QHANDLE ] [ root |"IF_FEATURE_TC_INGRESS(" ingress |")" parent CLASSID ]\n" /* //usage: "[ estimator INTERVAL TIME_CONSTANT ]\n" */ //usage: " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n" //usage: " QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }\n" //usage: "qdisc show [ dev STRING ]"IF_FEATURE_TC_INGRESS(" [ingress]")"\n" //usage: "class [ classid CLASSID ] [ root | parent CLASSID ]\n" //usage: " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n" //usage: "class show [ dev STRING ] [ root | parent CLASSID ]\n" //usage: "filter [ pref PRIO ] [ protocol PROTO ]\n" /* //usage: "\t[ estimator INTERVAL TIME_CONSTANT ]\n" */ //usage: " [ root | classid CLASSID ] [ handle FILTERID ]\n" //usage: " [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n" //usage: "filter show [ dev STRING ] [ root | parent CLASSID ]" #include "libbb.h" #include "libiproute/utils.h" #include "libiproute/ip_common.h" #include "libiproute/rt_names.h" #include /* for the TC_H_* macros */ #define parse_rtattr_nested(tb, max, rta) \ (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta))) /* nullifies tb on error */ #define __parse_rtattr_nested_compat(tb, max, rta, len) \ ({if ((RTA_PAYLOAD(rta) >= len) && \ (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr))) { \ rta = RTA_DATA(rta) + RTA_ALIGN(len); \ parse_rtattr_nested(tb, max, rta); \ } else \ memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); \ }) #define parse_rtattr_nested_compat(tb, max, rta, data, len) \ ({data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \ __parse_rtattr_nested_compat(tb, max, rta, len); }) #define show_details (0) /* not implemented. Does anyone need it? */ #define use_iec (0) /* not currently documented in the upstream manpage */ struct globals { int filter_ifindex; uint32_t filter_qdisc; uint32_t filter_parent; uint32_t filter_prio; uint32_t filter_proto; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) struct BUG_G_too_big { char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1]; }; #define filter_ifindex (G.filter_ifindex) #define filter_qdisc (G.filter_qdisc) #define filter_parent (G.filter_parent) #define filter_prio (G.filter_prio) #define filter_proto (G.filter_proto) #define INIT_G() do { } while (0) /* Allocates a buffer containing the name of a class id. * The caller must free the returned memory. */ static char* print_tc_classid(uint32_t cid) { #if 0 /* IMPOSSIBLE */ if (cid == TC_H_ROOT) return xasprintf("root"); else #endif if (cid == TC_H_UNSPEC) return xasprintf("none"); else if (TC_H_MAJ(cid) == 0) return xasprintf(":%x", TC_H_MIN(cid)); else if (TC_H_MIN(cid) == 0) return xasprintf("%x:", TC_H_MAJ(cid)>>16); else return xasprintf("%x:%x", TC_H_MAJ(cid)>>16, TC_H_MIN(cid)); } /* Get a qdisc handle. Return 0 on success, !0 otherwise. */ static int get_qdisc_handle(uint32_t *h, const char *str) { uint32_t maj; char *p; maj = TC_H_UNSPEC; if (!strcmp(str, "none")) goto ok; maj = strtoul(str, &p, 16); if (p == str) return 1; maj <<= 16; if (*p != ':' && *p != '\0') return 1; ok: *h = maj; return 0; } /* Get class ID. Return 0 on success, !0 otherwise. */ static int get_tc_classid(uint32_t *h, const char *str) { uint32_t maj, min; char *p; maj = TC_H_ROOT; if (!strcmp(str, "root")) goto ok; maj = TC_H_UNSPEC; if (!strcmp(str, "none")) goto ok; maj = strtoul(str, &p, 16); if (p == str) { if (*p != ':') return 1; maj = 0; } if (*p == ':') { if (maj >= (1<<16)) return 1; maj <<= 16; str = p + 1; min = strtoul(str, &p, 16); //FIXME: check for "" too? if (*p != '\0' || min >= (1<<16)) return 1; maj |= min; } else if (*p != 0) return 1; ok: *h = maj; return 0; } static void print_rate(char *buf, int len, uint32_t rate) { double tmp = (double)rate*8; if (use_iec) { if (tmp >= 1000.0*1024.0*1024.0) snprintf(buf, len, "%.0fMibit", tmp/1024.0*1024.0); else if (tmp >= 1000.0*1024) snprintf(buf, len, "%.0fKibit", tmp/1024); else snprintf(buf, len, "%.0fbit", tmp); } else { if (tmp >= 1000.0*1000000.0) snprintf(buf, len, "%.0fMbit", tmp/1000000.0); else if (tmp >= 1000.0 * 1000.0) snprintf(buf, len, "%.0fKbit", tmp/1000.0); else snprintf(buf, len, "%.0fbit", tmp); } } /* This is "pfifo_fast". */ static int prio_parse_opt(int argc, char **argv, struct nlmsghdr *n) { return 0; } static int prio_print_opt(struct rtattr *opt) { int i; struct tc_prio_qopt *qopt; struct rtattr *tb[TCA_PRIO_MAX+1]; if (opt == NULL) return 0; parse_rtattr_nested_compat(tb, TCA_PRIO_MAX, opt, qopt, sizeof(*qopt)); if (tb == NULL) return 0; printf("bands %u priomap ", qopt->bands); for (i=0; i<=TC_PRIO_MAX; i++) printf(" %d", qopt->priomap[i]); if (tb[TCA_PRIO_MQ]) printf(" multiqueue: o%s ", *(unsigned char *)RTA_DATA(tb[TCA_PRIO_MQ]) ? "n" : "ff"); return 0; } /* Class Based Queue */ static int cbq_parse_opt(int argc, char **argv, struct nlmsghdr *n) { return 0; } static int cbq_print_opt(struct rtattr *opt) { struct rtattr *tb[TCA_CBQ_MAX+1]; struct tc_ratespec *r = NULL; struct tc_cbq_lssopt *lss = NULL; struct tc_cbq_wrropt *wrr = NULL; struct tc_cbq_fopt *fopt = NULL; struct tc_cbq_ovl *ovl = NULL; const char *const error = "CBQ: too short %s opt"; char buf[64]; if (opt == NULL) goto done; parse_rtattr_nested(tb, TCA_CBQ_MAX, opt); if (tb[TCA_CBQ_RATE]) { if (RTA_PAYLOAD(tb[TCA_CBQ_RATE]) < sizeof(*r)) bb_error_msg(error, "rate"); else r = RTA_DATA(tb[TCA_CBQ_RATE]); } if (tb[TCA_CBQ_LSSOPT]) { if (RTA_PAYLOAD(tb[TCA_CBQ_LSSOPT]) < sizeof(*lss)) bb_error_msg(error, "lss"); else lss = RTA_DATA(tb[TCA_CBQ_LSSOPT]); } if (tb[TCA_CBQ_WRROPT]) { if (RTA_PAYLOAD(tb[TCA_CBQ_WRROPT]) < sizeof(*wrr)) bb_error_msg(error, "wrr"); else wrr = RTA_DATA(tb[TCA_CBQ_WRROPT]); } if (tb[TCA_CBQ_FOPT]) { if (RTA_PAYLOAD(tb[TCA_CBQ_FOPT]) < sizeof(*fopt)) bb_error_msg(error, "fopt"); else fopt = RTA_DATA(tb[TCA_CBQ_FOPT]); } if (tb[TCA_CBQ_OVL_STRATEGY]) { if (RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]) < sizeof(*ovl)) bb_error_msg("CBQ: too short overlimit strategy %u/%u", (unsigned) RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]), (unsigned) sizeof(*ovl)); else ovl = RTA_DATA(tb[TCA_CBQ_OVL_STRATEGY]); } if (r) { print_rate(buf, sizeof(buf), r->rate); printf("rate %s ", buf); if (show_details) { printf("cell %ub ", 1<cell_log); if (r->mpu) printf("mpu %ub ", r->mpu); if (r->overhead) printf("overhead %ub ", r->overhead); } } if (lss && lss->flags) { bool comma = false; bb_putchar('('); if (lss->flags&TCF_CBQ_LSS_BOUNDED) { printf("bounded"); comma = true; } if (lss->flags&TCF_CBQ_LSS_ISOLATED) { if (comma) bb_putchar(','); printf("isolated"); } printf(") "); } if (wrr) { if (wrr->priority != TC_CBQ_MAXPRIO) printf("prio %u", wrr->priority); else printf("prio no-transmit"); if (show_details) { printf("/%u ", wrr->cpriority); if (wrr->weight != 1) { print_rate(buf, sizeof(buf), wrr->weight); printf("weight %s ", buf); } if (wrr->allot) printf("allot %ub ", wrr->allot); } } done: return 0; } static int print_qdisc(const struct sockaddr_nl *who UNUSED_PARAM, struct nlmsghdr *hdr, void *arg UNUSED_PARAM) { struct tcmsg *msg = NLMSG_DATA(hdr); int len = hdr->nlmsg_len; struct rtattr * tb[TCA_MAX+1]; char *name; if (hdr->nlmsg_type != RTM_NEWQDISC && hdr->nlmsg_type != RTM_DELQDISC) { /* bb_error_msg("not a qdisc"); */ return 0; /* ??? mimic upstream; should perhaps return -1 */ } len -= NLMSG_LENGTH(sizeof(*msg)); if (len < 0) { /* bb_error_msg("wrong len %d", len); */ return -1; } /* not the desired interface? */ if (filter_ifindex && filter_ifindex != msg->tcm_ifindex) return 0; memset (tb, 0, sizeof(tb)); parse_rtattr(tb, TCA_MAX, TCA_RTA(msg), len); if (tb[TCA_KIND] == NULL) { /* bb_error_msg("%s: NULL kind", "qdisc"); */ return -1; } if (hdr->nlmsg_type == RTM_DELQDISC) printf("deleted "); name = (char*)RTA_DATA(tb[TCA_KIND]); printf("qdisc %s %x: ", name, msg->tcm_handle>>16); if (filter_ifindex == 0) printf("dev %s ", ll_index_to_name(msg->tcm_ifindex)); if (msg->tcm_parent == TC_H_ROOT) printf("root "); else if (msg->tcm_parent) { char *classid = print_tc_classid(msg->tcm_parent); printf("parent %s ", classid); if (ENABLE_FEATURE_CLEAN_UP) free(classid); } if (msg->tcm_info != 1) printf("refcnt %d ", msg->tcm_info); if (tb[TCA_OPTIONS]) { static const char _q_[] ALIGN1 = "pfifo_fast\0""cbq\0"; int qqq = index_in_strings(_q_, name); if (qqq == 0) { /* pfifo_fast aka prio */ prio_print_opt(tb[TCA_OPTIONS]); } else if (qqq == 1) { /* class based queuing */ cbq_print_opt(tb[TCA_OPTIONS]); } else bb_error_msg("unknown %s", name); } bb_putchar('\n'); return 0; } static int print_class(const struct sockaddr_nl *who UNUSED_PARAM, struct nlmsghdr *hdr, void *arg UNUSED_PARAM) { struct tcmsg *msg = NLMSG_DATA(hdr); int len = hdr->nlmsg_len; struct rtattr * tb[TCA_MAX+1]; char *name, *classid; /*XXX Eventually factor out common code */ if (hdr->nlmsg_type != RTM_NEWTCLASS && hdr->nlmsg_type != RTM_DELTCLASS) { /* bb_error_msg("not a class"); */ return 0; /* ??? mimic upstream; should perhaps return -1 */ } len -= NLMSG_LENGTH(sizeof(*msg)); if (len < 0) { /* bb_error_msg("wrong len %d", len); */ return -1; } /* not the desired interface? */ if (filter_qdisc && TC_H_MAJ(msg->tcm_handle^filter_qdisc)) return 0; memset (tb, 0, sizeof(tb)); parse_rtattr(tb, TCA_MAX, TCA_RTA(msg), len); if (tb[TCA_KIND] == NULL) { /* bb_error_msg("%s: NULL kind", "class"); */ return -1; } if (hdr->nlmsg_type == RTM_DELTCLASS) printf("deleted "); name = (char*)RTA_DATA(tb[TCA_KIND]); classid = !msg->tcm_handle ? NULL : print_tc_classid( filter_qdisc ? TC_H_MIN(msg->tcm_parent) : msg->tcm_parent); printf ("class %s %s", name, classid); if (ENABLE_FEATURE_CLEAN_UP) free(classid); if (filter_ifindex == 0) printf("dev %s ", ll_index_to_name(msg->tcm_ifindex)); if (msg->tcm_parent == TC_H_ROOT) printf("root "); else if (msg->tcm_parent) { classid = print_tc_classid(filter_qdisc ? TC_H_MIN(msg->tcm_parent) : msg->tcm_parent); printf("parent %s ", classid); if (ENABLE_FEATURE_CLEAN_UP) free(classid); } if (msg->tcm_info) printf("leaf %x ", msg->tcm_info >> 16); /* Do that get_qdisc_kind(RTA_DATA(tb[TCA_KIND])). */ if (tb[TCA_OPTIONS]) { static const char _q_[] ALIGN1 = "pfifo_fast\0""cbq\0"; int qqq = index_in_strings(_q_, name); if (qqq == 0) { /* pfifo_fast aka prio */ /* nothing. */ /*prio_print_opt(tb[TCA_OPTIONS]);*/ } else if (qqq == 1) { /* class based queuing */ /* cbq_print_copt() is identical to cbq_print_opt(). */ cbq_print_opt(tb[TCA_OPTIONS]); } else bb_error_msg("unknown %s", name); } bb_putchar('\n'); return 0; } static int print_filter(const struct sockaddr_nl *who UNUSED_PARAM, struct nlmsghdr *hdr, void *arg UNUSED_PARAM) { return 0; } int tc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int tc_main(int argc UNUSED_PARAM, char **argv) { static const char objects[] ALIGN1 = "qdisc\0""class\0""filter\0" ; enum { OBJ_qdisc = 0, OBJ_class, OBJ_filter }; static const char commands[] ALIGN1 = "add\0""delete\0""change\0" "link\0" /* only qdisc */ "replace\0" "show\0""list\0" ; static const char args[] ALIGN1 = "dev\0" /* qdisc, class, filter */ "root\0" /* class, filter */ "parent\0" /* class, filter */ "qdisc\0" /* class */ "handle\0" /* change: qdisc, class(classid) list: filter */ "classid\0" /* change: for class use "handle" */ "preference\0""priority\0""protocol\0" /* filter */ ; enum { CMD_add = 0, CMD_del, CMD_change, CMD_link, CMD_replace, CMD_show }; enum { ARG_dev = 0, ARG_root, ARG_parent, ARG_qdisc, ARG_handle, ARG_classid, ARG_pref, ARG_prio, ARG_proto}; struct rtnl_handle rth; struct tcmsg msg; int ret, obj, cmd, arg; char *dev = NULL; INIT_G(); if (!*++argv) bb_show_usage(); xrtnl_open(&rth); ret = EXIT_SUCCESS; obj = index_in_substrings(objects, *argv++); if (obj < OBJ_qdisc) bb_show_usage(); if (!*argv) cmd = CMD_show; /* list is the default */ else { cmd = index_in_substrings(commands, *argv); if (cmd < 0) bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name); argv++; } memset(&msg, 0, sizeof(msg)); msg.tcm_family = AF_UNSPEC; ll_init_map(&rth); while (*argv) { arg = index_in_substrings(args, *argv); if (arg == ARG_dev) { NEXT_ARG(); if (dev) duparg2("dev", *argv); dev = *argv++; msg.tcm_ifindex = xll_name_to_index(dev); if (cmd >= CMD_show) filter_ifindex = msg.tcm_ifindex; } else if ((arg == ARG_qdisc && obj == OBJ_class && cmd >= CMD_show) || (arg == ARG_handle && obj == OBJ_qdisc && cmd == CMD_change) ) { NEXT_ARG(); /* We don't care about duparg2("qdisc handle",*argv) for now */ if (get_qdisc_handle(&filter_qdisc, *argv)) invarg(*argv, "qdisc"); } else if (obj != OBJ_qdisc && (arg == ARG_root || arg == ARG_parent || (obj == OBJ_filter && arg >= ARG_pref) ) ) { /* nothing */ } else { invarg(*argv, "command"); } NEXT_ARG(); if (arg == ARG_root) { if (msg.tcm_parent) duparg("parent", *argv); msg.tcm_parent = TC_H_ROOT; if (obj == OBJ_filter) filter_parent = TC_H_ROOT; } else if (arg == ARG_parent) { uint32_t handle; if (msg.tcm_parent) duparg(*argv, "parent"); if (get_tc_classid(&handle, *argv)) invarg(*argv, "parent"); msg.tcm_parent = handle; if (obj == OBJ_filter) filter_parent = handle; } else if (arg == ARG_handle) { /* filter::list */ if (msg.tcm_handle) duparg(*argv, "handle"); /* reject LONG_MIN || LONG_MAX */ /* TODO: for fw slash = strchr(handle, '/'); if (slash != NULL) *slash = '\0'; */ msg.tcm_handle = get_u32(*argv, "handle"); /* if (slash) {if (get_u32(uint32_t &mask, slash+1, NULL)) inv mask; addattr32(n, MAX_MSG, TCA_FW_MASK, mask); */ } else if (arg == ARG_classid && obj == OBJ_class && cmd == CMD_change){ } else if (arg == ARG_pref || arg == ARG_prio) { /* filter::list */ if (filter_prio) duparg(*argv, "priority"); filter_prio = get_u32(*argv, "priority"); } else if (arg == ARG_proto) { /* filter::list */ uint16_t tmp; if (filter_proto) duparg(*argv, "protocol"); if (ll_proto_a2n(&tmp, *argv)) invarg(*argv, "protocol"); filter_proto = tmp; } } if (cmd >= CMD_show) { /* show or list */ if (obj == OBJ_filter) msg.tcm_info = TC_H_MAKE(filter_prio<<16, filter_proto); if (rtnl_dump_request(&rth, obj == OBJ_qdisc ? RTM_GETQDISC : obj == OBJ_class ? RTM_GETTCLASS : RTM_GETTFILTER, &msg, sizeof(msg)) < 0) bb_simple_perror_msg_and_die("can't send dump request"); xrtnl_dump_filter(&rth, obj == OBJ_qdisc ? print_qdisc : obj == OBJ_class ? print_class : print_filter, NULL); } if (ENABLE_FEATURE_CLEAN_UP) { rtnl_close(&rth); } return ret; } busybox-1.22.1/networking/slattach.c0000644000000000000000000001420712263563520016134 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Stripped down version of net-tools for busybox. * * Author: Ignacio Garcia Perez (iggarpe at gmail dot com) * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * There are some differences from the standard net-tools slattach: * * - The -l option is not supported. * * - The -F options allows disabling of RTS/CTS flow control. */ //usage:#define slattach_trivial_usage //usage: "[-cehmLF] [-s SPEED] [-p PROTOCOL] DEVICE" //usage:#define slattach_full_usage "\n\n" //usage: "Attach network interface(s) to serial line(s)\n" //usage: "\n -p PROT Set protocol (slip, cslip, slip6, clisp6 or adaptive)" //usage: "\n -s SPD Set line speed" //usage: "\n -e Exit after initializing device" //usage: "\n -h Exit when the carrier is lost" //usage: "\n -c PROG Run PROG when the line is hung up" //usage: "\n -m Do NOT initialize the line in raw 8 bits mode" //usage: "\n -L Enable 3-wire operation" //usage: "\n -F Disable RTS/CTS flow control" #include "libbb.h" #include "libiproute/utils.h" /* invarg() */ struct globals { int handle; int saved_disc; struct termios saved_state; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define handle (G.handle ) #define saved_disc (G.saved_disc ) #define saved_state (G.saved_state ) #define INIT_G() do { } while (0) /* * Save tty state and line discipline * * It is fine here to bail out on errors, since we haven modified anything yet */ static void save_state(void) { /* Save line status */ if (tcgetattr(handle, &saved_state) < 0) bb_perror_msg_and_die("get state"); /* Save line discipline */ xioctl(handle, TIOCGETD, &saved_disc); } static int set_termios_state_or_warn(struct termios *state) { int ret; ret = tcsetattr(handle, TCSANOW, state); if (ret < 0) { bb_perror_msg("set state"); return 1; /* used as exitcode */ } return 0; } /* * Restore state and line discipline for ALL managed ttys * * Restoring ALL managed ttys is the only way to have a single * hangup delay. * * Go on after errors: we want to restore as many controlled ttys * as possible. */ static void restore_state_and_exit(int exitcode) NORETURN; static void restore_state_and_exit(int exitcode) { struct termios state; /* Restore line discipline */ if (ioctl_or_warn(handle, TIOCSETD, &saved_disc) < 0) { exitcode = 1; } /* Hangup */ memcpy(&state, &saved_state, sizeof(state)); cfsetispeed(&state, B0); cfsetospeed(&state, B0); if (set_termios_state_or_warn(&state)) exitcode = 1; sleep(1); /* Restore line status */ if (set_termios_state_or_warn(&saved_state)) exit(EXIT_FAILURE); if (ENABLE_FEATURE_CLEAN_UP) close(handle); exit(exitcode); } /* * Set tty state, line discipline and encapsulation */ static void set_state(struct termios *state, int encap) { int disc; /* Set line status */ if (set_termios_state_or_warn(state)) goto bad; /* Set line discliple (N_SLIP always) */ disc = N_SLIP; if (ioctl_or_warn(handle, TIOCSETD, &disc) < 0) { goto bad; } /* Set encapsulation (SLIP, CSLIP, etc) */ if (ioctl_or_warn(handle, SIOCSIFENCAP, &encap) < 0) { bad: restore_state_and_exit(EXIT_FAILURE); } } static void sig_handler(int signo UNUSED_PARAM) { restore_state_and_exit(EXIT_SUCCESS); } int slattach_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int slattach_main(int argc UNUSED_PARAM, char **argv) { /* Line discipline code table */ static const char proto_names[] ALIGN1 = "slip\0" /* 0 */ "cslip\0" /* 1 */ "slip6\0" /* 2 */ "cslip6\0" /* 3 */ "adaptive\0" /* 8 */ ; int i, encap, opt; struct termios state; const char *proto = "cslip"; const char *extcmd; /* Command to execute after hangup */ const char *baud_str; int baud_code = -1; /* Line baud rate (system code) */ enum { OPT_p_proto = 1 << 0, OPT_s_baud = 1 << 1, OPT_c_extcmd = 1 << 2, OPT_e_quit = 1 << 3, OPT_h_watch = 1 << 4, OPT_m_nonraw = 1 << 5, OPT_L_local = 1 << 6, OPT_F_noflow = 1 << 7 }; INIT_G(); /* Parse command line options */ opt = getopt32(argv, "p:s:c:ehmLF", &proto, &baud_str, &extcmd); /*argc -= optind;*/ argv += optind; if (!*argv) bb_show_usage(); encap = index_in_strings(proto_names, proto); if (encap < 0) invarg(proto, "protocol"); if (encap > 3) encap = 8; /* We want to know if the baud rate is valid before we start touching the ttys */ if (opt & OPT_s_baud) { baud_code = tty_value_to_baud(xatoi(baud_str)); if (baud_code < 0) invarg(baud_str, "baud rate"); } /* Trap signals in order to restore tty states upon exit */ if (!(opt & OPT_e_quit)) { bb_signals(0 + (1 << SIGHUP) + (1 << SIGINT) + (1 << SIGQUIT) + (1 << SIGTERM) , sig_handler); } /* Open tty */ handle = open(*argv, O_RDWR | O_NDELAY); if (handle < 0) { char *buf = concat_path_file("/dev", *argv); handle = xopen(buf, O_RDWR | O_NDELAY); /* maybe if (ENABLE_FEATURE_CLEAN_UP) ?? */ free(buf); } /* Save current tty state */ save_state(); /* Configure tty */ memcpy(&state, &saved_state, sizeof(state)); if (!(opt & OPT_m_nonraw)) { /* raw not suppressed */ memset(&state.c_cc, 0, sizeof(state.c_cc)); state.c_cc[VMIN] = 1; state.c_iflag = IGNBRK | IGNPAR; state.c_oflag = 0; state.c_lflag = 0; state.c_cflag = CS8 | HUPCL | CREAD | ((opt & OPT_L_local) ? CLOCAL : 0) | ((opt & OPT_F_noflow) ? 0 : CRTSCTS); cfsetispeed(&state, cfgetispeed(&saved_state)); cfsetospeed(&state, cfgetospeed(&saved_state)); } if (opt & OPT_s_baud) { cfsetispeed(&state, baud_code); cfsetospeed(&state, baud_code); } set_state(&state, encap); /* Exit now if option -e was passed */ if (opt & OPT_e_quit) return 0; /* If we're not requested to watch, just keep descriptor open * until we are killed */ if (!(opt & OPT_h_watch)) while (1) sleep(24*60*60); /* Watch line for hangup */ while (1) { if (ioctl(handle, TIOCMGET, &i) < 0 || !(i & TIOCM_CAR)) goto no_carrier; sleep(15); } no_carrier: /* Execute command on hangup */ if (opt & OPT_c_extcmd) system(extcmd); /* Restore states and exit */ restore_state_and_exit(EXIT_SUCCESS); } busybox-1.22.1/networking/ifenslave.c0000644000000000000000000004376412263563520016317 0ustar rootroot/* Mode: C; * * Mini ifenslave implementation for busybox * Copyright (C) 2005 by Marc Leeman * * ifenslave.c: Configure network interfaces for parallel routing. * * This program controls the Linux implementation of running multiple * network interfaces in parallel. * * Author: Donald Becker * Copyright 1994-1996 Donald Becker * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation. * * The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O * Center of Excellence in Space Data and Information Sciences * Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 * * Changes : * - 2000/10/02 Willy Tarreau : * - few fixes. Master's MAC address is now correctly taken from * the first device when not previously set ; * - detach support : call BOND_RELEASE to detach an enslaved interface. * - give a mini-howto from command-line help : # ifenslave -h * * - 2001/02/16 Chad N. Tindel : * - Master is now brought down before setting the MAC address. In * the 2.4 kernel you can't change the MAC address while the device is * up because you get EBUSY. * * - 2001/09/13 Takao Indoh * - Added the ability to change the active interface on a mode 1 bond * at runtime. * * - 2001/10/23 Chad N. Tindel : * - No longer set the MAC address of the master. The bond device will * take care of this itself * - Try the SIOC*** versions of the bonding ioctls before using the * old versions * - 2002/02/18 Erik Habbinga : * - ifr2.ifr_flags was not initialized in the hwaddr_notset case, * SIOCGIFFLAGS now called before hwaddr_notset test * * - 2002/10/31 Tony Cureington : * - If the master does not have a hardware address when the first slave * is enslaved, the master is assigned the hardware address of that * slave - there is a comment in bonding.c stating "ifenslave takes * care of this now." This corrects the problem of slaves having * different hardware addresses in active-backup mode when * multiple interfaces are specified on a single ifenslave command * (ifenslave bond0 eth0 eth1). * * - 2003/03/18 - Tsippy Mendelson and * Shmulik Hen * - Moved setting the slave's mac address and openning it, from * the application to the driver. This enables support of modes * that need to use the unique mac address of each slave. * The driver also takes care of closing the slave and restoring its * original mac address upon release. * In addition, block possibility of enslaving before the master is up. * This prevents putting the system in an undefined state. * * - 2003/05/01 - Amir Noam * - Added ABI version control to restore compatibility between * new/old ifenslave and new/old bonding. * - Prevent adding an adapter that is already a slave. * Fixes the problem of stalling the transmission and leaving * the slave in a down state. * * - 2003/05/01 - Shmulik Hen * - Prevent enslaving if the bond device is down. * Fixes the problem of leaving the system in unstable state and * halting when trying to remove the module. * - Close socket on all abnormal exists. * - Add versioning scheme that follows that of the bonding driver. * current version is 1.0.0 as a base line. * * - 2003/05/22 - Jay Vosburgh * - ifenslave -c was broken; it's now fixed * - Fixed problem with routes vanishing from master during enslave * processing. * * - 2003/05/27 - Amir Noam * - Fix backward compatibility issues: * For drivers not using ABI versions, slave was set down while * it should be left up before enslaving. * Also, master was not set down and the default set_mac_address() * would fail and generate an error message in the system log. * - For opt_c: slave should not be set to the master's setting * while it is running. It was already set during enslave. To * simplify things, it is now handeled separately. * * - 2003/12/01 - Shmulik Hen * - Code cleanup and style changes * set version to 1.1.0 */ //usage:#define ifenslave_trivial_usage //usage: "[-cdf] MASTER_IFACE SLAVE_IFACE..." //usage:#define ifenslave_full_usage "\n\n" //usage: "Configure network interfaces for parallel routing\n" //usage: "\n -c,--change-active Change active slave" //usage: "\n -d,--detach Remove slave interface from bonding device" //usage: "\n -f,--force Force, even if interface is not Ethernet" /* //usage: "\n -r,--receive-slave Create a receive-only slave" */ //usage: //usage:#define ifenslave_example_usage //usage: "To create a bond device, simply follow these three steps:\n" //usage: "- ensure that the required drivers are properly loaded:\n" //usage: " # modprobe bonding ; modprobe <3c59x|eepro100|pcnet32|tulip|...>\n" //usage: "- assign an IP address to the bond device:\n" //usage: " # ifconfig bond0 netmask broadcast \n" //usage: "- attach all the interfaces you need to the bond device:\n" //usage: " # ifenslave bond0 eth0 eth1 eth2\n" //usage: " If bond0 didn't have a MAC address, it will take eth0's. Then, all\n" //usage: " interfaces attached AFTER this assignment will get the same MAC addr.\n\n" //usage: " To detach a dead interface without setting the bond device down:\n" //usage: " # ifenslave -d bond0 eth1\n\n" //usage: " To set the bond device down and automatically release all the slaves:\n" //usage: " # ifconfig bond0 down\n\n" //usage: " To change active slave:\n" //usage: " # ifenslave -c bond0 eth0\n" #include "libbb.h" /* #include - no. linux/if_bonding.h pulls in linux/if.h */ #include //#include - not needed? #include #include #include "fix_u32.h" /* hack, so we may include kernel's ethtool.h */ #include #ifndef BOND_ABI_VERSION # define BOND_ABI_VERSION 2 #endif #ifndef IFNAMSIZ # define IFNAMSIZ 16 #endif struct dev_data { struct ifreq mtu, flags, hwaddr; }; enum { skfd = 3 }; /* AF_INET socket for ioctl() calls. */ struct globals { unsigned abi_ver; /* userland - kernel ABI version */ smallint hwaddr_set; /* Master's hwaddr is set */ struct dev_data master; struct dev_data slave; }; #define G (*ptr_to_globals) #define abi_ver (G.abi_ver ) #define hwaddr_set (G.hwaddr_set) #define master (G.master ) #define slave (G.slave ) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ } while (0) /* NOINLINEs are placed where it results in smaller code (gcc 4.3.1) */ static int ioctl_on_skfd(unsigned request, struct ifreq *ifr) { return ioctl(skfd, request, ifr); } static int set_ifrname_and_do_ioctl(unsigned request, struct ifreq *ifr, const char *ifname) { strncpy_IFNAMSIZ(ifr->ifr_name, ifname); return ioctl_on_skfd(request, ifr); } static int get_if_settings(char *ifname, struct dev_data *dd) { int res; res = set_ifrname_and_do_ioctl(SIOCGIFMTU, &dd->mtu, ifname); res |= set_ifrname_and_do_ioctl(SIOCGIFFLAGS, &dd->flags, ifname); res |= set_ifrname_and_do_ioctl(SIOCGIFHWADDR, &dd->hwaddr, ifname); return res; } static int get_slave_flags(char *slave_ifname) { return set_ifrname_and_do_ioctl(SIOCGIFFLAGS, &slave.flags, slave_ifname); } static int set_hwaddr(char *ifname, struct sockaddr *hwaddr) { struct ifreq ifr; memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(*hwaddr)); return set_ifrname_and_do_ioctl(SIOCSIFHWADDR, &ifr, ifname); } static int set_mtu(char *ifname, int mtu) { struct ifreq ifr; ifr.ifr_mtu = mtu; return set_ifrname_and_do_ioctl(SIOCSIFMTU, &ifr, ifname); } static int set_if_flags(char *ifname, int flags) { struct ifreq ifr; ifr.ifr_flags = flags; return set_ifrname_and_do_ioctl(SIOCSIFFLAGS, &ifr, ifname); } static int set_if_up(char *ifname, int flags) { int res = set_if_flags(ifname, flags | IFF_UP); if (res) bb_perror_msg("%s: can't up", ifname); return res; } static int set_if_down(char *ifname, int flags) { int res = set_if_flags(ifname, flags & ~IFF_UP); if (res) bb_perror_msg("%s: can't down", ifname); return res; } static int clear_if_addr(char *ifname) { struct ifreq ifr; ifr.ifr_addr.sa_family = AF_INET; memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data)); return set_ifrname_and_do_ioctl(SIOCSIFADDR, &ifr, ifname); } static int set_if_addr(char *master_ifname, char *slave_ifname) { #if (SIOCGIFADDR | SIOCSIFADDR \ | SIOCGIFDSTADDR | SIOCSIFDSTADDR \ | SIOCGIFBRDADDR | SIOCSIFBRDADDR \ | SIOCGIFNETMASK | SIOCSIFNETMASK) <= 0xffff #define INT uint16_t #else #define INT int #endif static const struct { INT g_ioctl; INT s_ioctl; } ifra[] = { { SIOCGIFADDR, SIOCSIFADDR }, { SIOCGIFDSTADDR, SIOCSIFDSTADDR }, { SIOCGIFBRDADDR, SIOCSIFBRDADDR }, { SIOCGIFNETMASK, SIOCSIFNETMASK }, }; struct ifreq ifr; int res; unsigned i; for (i = 0; i < ARRAY_SIZE(ifra); i++) { res = set_ifrname_and_do_ioctl(ifra[i].g_ioctl, &ifr, master_ifname); if (res < 0) { ifr.ifr_addr.sa_family = AF_INET; memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data)); } res = set_ifrname_and_do_ioctl(ifra[i].s_ioctl, &ifr, slave_ifname); if (res < 0) return res; } return 0; } static void change_active(char *master_ifname, char *slave_ifname) { struct ifreq ifr; if (!(slave.flags.ifr_flags & IFF_SLAVE)) { bb_error_msg_and_die("%s is not a slave", slave_ifname); } strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname); if (set_ifrname_and_do_ioctl(SIOCBONDCHANGEACTIVE, &ifr, master_ifname) && ioctl_on_skfd(BOND_CHANGE_ACTIVE_OLD, &ifr) ) { bb_perror_msg_and_die( "master %s, slave %s: can't " "change active", master_ifname, slave_ifname); } } static NOINLINE int enslave(char *master_ifname, char *slave_ifname) { struct ifreq ifr; int res; if (slave.flags.ifr_flags & IFF_SLAVE) { bb_error_msg( "%s is already a slave", slave_ifname); return 1; } res = set_if_down(slave_ifname, slave.flags.ifr_flags); if (res) return res; if (abi_ver < 2) { /* Older bonding versions would panic if the slave has no IP * address, so get the IP setting from the master. */ res = set_if_addr(master_ifname, slave_ifname); if (res) { bb_perror_msg("%s: can't set address", slave_ifname); return res; } } else { res = clear_if_addr(slave_ifname); if (res) { bb_perror_msg("%s: can't clear address", slave_ifname); return res; } } if (master.mtu.ifr_mtu != slave.mtu.ifr_mtu) { res = set_mtu(slave_ifname, master.mtu.ifr_mtu); if (res) { bb_perror_msg("%s: can't set MTU", slave_ifname); return res; } } if (hwaddr_set) { /* Master already has an hwaddr * so set it's hwaddr to the slave */ if (abi_ver < 1) { /* The driver is using an old ABI, so * the application sets the slave's * hwaddr */ if (set_hwaddr(slave_ifname, &(master.hwaddr.ifr_hwaddr))) { bb_perror_msg("%s: can't set hw address", slave_ifname); goto undo_mtu; } /* For old ABI the application needs to bring the * slave back up */ if (set_if_up(slave_ifname, slave.flags.ifr_flags)) goto undo_slave_mac; } /* The driver is using a new ABI, * so the driver takes care of setting * the slave's hwaddr and bringing * it up again */ } else { /* No hwaddr for master yet, so * set the slave's hwaddr to it */ if (abi_ver < 1) { /* For old ABI, the master needs to be * down before setting it's hwaddr */ if (set_if_down(master_ifname, master.flags.ifr_flags)) goto undo_mtu; } if (set_hwaddr(master_ifname, &(slave.hwaddr.ifr_hwaddr))) { bb_error_msg("%s: can't set hw address", master_ifname); goto undo_mtu; } if (abi_ver < 1) { /* For old ABI, bring the master * back up */ if (set_if_up(master_ifname, master.flags.ifr_flags)) goto undo_master_mac; } hwaddr_set = 1; } /* Do the real thing */ strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname); if (set_ifrname_and_do_ioctl(SIOCBONDENSLAVE, &ifr, master_ifname) && ioctl_on_skfd(BOND_ENSLAVE_OLD, &ifr) ) { goto undo_master_mac; } return 0; /* rollback (best effort) */ undo_master_mac: set_hwaddr(master_ifname, &(master.hwaddr.ifr_hwaddr)); hwaddr_set = 0; goto undo_mtu; undo_slave_mac: set_hwaddr(slave_ifname, &(slave.hwaddr.ifr_hwaddr)); undo_mtu: set_mtu(slave_ifname, slave.mtu.ifr_mtu); return 1; } static int release(char *master_ifname, char *slave_ifname) { struct ifreq ifr; int res = 0; if (!(slave.flags.ifr_flags & IFF_SLAVE)) { bb_error_msg("%s is not a slave", slave_ifname); return 1; } strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname); if (set_ifrname_and_do_ioctl(SIOCBONDRELEASE, &ifr, master_ifname) < 0 && ioctl_on_skfd(BOND_RELEASE_OLD, &ifr) < 0 ) { return 1; } if (abi_ver < 1) { /* The driver is using an old ABI, so we'll set the interface * down to avoid any conflicts due to same MAC/IP */ res = set_if_down(slave_ifname, slave.flags.ifr_flags); } /* set to default mtu */ set_mtu(slave_ifname, 1500); return res; } static NOINLINE void get_drv_info(char *master_ifname) { struct ifreq ifr; struct ethtool_drvinfo info; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_data = (caddr_t)&info; info.cmd = ETHTOOL_GDRVINFO; /* both fields are 32 bytes long (long enough) */ strcpy(info.driver, "ifenslave"); strcpy(info.fw_version, utoa(BOND_ABI_VERSION)); if (set_ifrname_and_do_ioctl(SIOCETHTOOL, &ifr, master_ifname) < 0) { if (errno == EOPNOTSUPP) return; bb_perror_msg_and_die("%s: SIOCETHTOOL error", master_ifname); } abi_ver = bb_strtou(info.fw_version, NULL, 0); if (errno) bb_error_msg_and_die("%s: SIOCETHTOOL error", master_ifname); } int ifenslave_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ifenslave_main(int argc UNUSED_PARAM, char **argv) { char *master_ifname, *slave_ifname; int rv; int res; unsigned opt; enum { OPT_c = (1 << 0), OPT_d = (1 << 1), OPT_f = (1 << 2), }; #if ENABLE_LONG_OPTS static const char ifenslave_longopts[] ALIGN1 = "change-active\0" No_argument "c" "detach\0" No_argument "d" "force\0" No_argument "f" /* "all-interfaces\0" No_argument "a" */ ; applet_long_options = ifenslave_longopts; #endif INIT_G(); opt = getopt32(argv, "cdfa"); argv += optind; if (opt & (opt-1)) /* Only one option can be given */ bb_show_usage(); master_ifname = *argv++; /* No interface names - show all interfaces. */ if (!master_ifname) { display_interfaces(NULL); return EXIT_SUCCESS; } /* Open a basic socket */ xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), skfd); /* Exchange abi version with bonding module */ get_drv_info(master_ifname); slave_ifname = *argv++; if (!slave_ifname) { if (opt & (OPT_d|OPT_c)) { /* --change or --detach, and no slaves given - * show all interfaces. */ display_interfaces(slave_ifname /* == NULL */); return 2; /* why 2? */ } /* A single arg means show the * configuration for this interface */ display_interfaces(master_ifname); return EXIT_SUCCESS; } if (get_if_settings(master_ifname, &master)) { /* Probably a good reason not to go on */ bb_perror_msg_and_die("%s: can't get settings", master_ifname); } /* Check if master is indeed a master; * if not then fail any operation */ if (!(master.flags.ifr_flags & IFF_MASTER)) bb_error_msg_and_die("%s is not a master", master_ifname); /* Check if master is up; if not then fail any operation */ if (!(master.flags.ifr_flags & IFF_UP)) bb_error_msg_and_die("%s is not up", master_ifname); #ifdef WHY_BOTHER /* Neither -c[hange] nor -d[etach] -> it's "enslave" then; * and -f[orce] is not there too. Check that it's ethernet. */ if (!(opt & (OPT_d|OPT_c|OPT_f))) { /* The family '1' is ARPHRD_ETHER for ethernet. */ if (master.hwaddr.ifr_hwaddr.sa_family != 1) { bb_error_msg_and_die( "%s is not ethernet-like (-f overrides)", master_ifname); } } #endif /* Accepts only one slave */ if (opt & OPT_c) { /* Change active slave */ if (get_slave_flags(slave_ifname)) { bb_perror_msg_and_die( "%s: can't get flags", slave_ifname); } change_active(master_ifname, slave_ifname); return EXIT_SUCCESS; } /* Accepts multiple slaves */ res = 0; do { if (opt & OPT_d) { /* Detach a slave interface from the master */ rv = get_slave_flags(slave_ifname); if (rv) { /* Can't work with this slave, */ /* remember the error and skip it */ bb_perror_msg( "skipping %s: can't get flags", slave_ifname); res = rv; continue; } rv = release(master_ifname, slave_ifname); if (rv) { bb_perror_msg("can't release %s from %s", slave_ifname, master_ifname); res = rv; } } else { /* Attach a slave interface to the master */ rv = get_if_settings(slave_ifname, &slave); if (rv) { /* Can't work with this slave, */ /* remember the error and skip it */ bb_perror_msg( "skipping %s: can't get settings", slave_ifname); res = rv; continue; } rv = enslave(master_ifname, slave_ifname); if (rv) { bb_perror_msg("can't enslave %s to %s", slave_ifname, master_ifname); res = rv; } } } while ((slave_ifname = *argv++) != NULL); if (ENABLE_FEATURE_CLEAN_UP) { close(skfd); } return res; } busybox-1.22.1/networking/httpd.c0000644000000000000000000022006212263563520015452 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * httpd implementation for busybox * * Copyright (C) 2002,2003 Glenn Engel * Copyright (C) 2003-2006 Vladimir Oleynik * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * ***************************************************************************** * * Typical usage: * For non root user: * httpd -p 8080 -h $HOME/public_html * For daemon start from rc script with uid=0: * httpd -u www * which is equivalent to (assuming user www has uid 80): * httpd -p 80 -u 80 -h $PWD -c /etc/httpd.conf -r "Web Server Authentication" * * When an url starts with "/cgi-bin/" it is assumed to be a cgi script. * The server changes directory to the location of the script and executes it * after setting QUERY_STRING and other environment variables. * * If directory URL is given, no index.html is found and CGI support is enabled, * cgi-bin/index.cgi will be run. Directory to list is ../$QUERY_STRING. * See httpd_indexcgi.c for an example GCI code. * * Doc: * "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html * * The applet can also be invoked as an url arg decoder and html text encoder * as follows: * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World" * bar=`httpd -e ""` # encode as "<Hello World>" * Note that url encoding for arguments is not the same as html encoding for * presentation. -d decodes an url-encoded argument while -e encodes in html * for page display. * * httpd.conf has the following format: * * H:/serverroot # define the server root. It will override -h * A:172.20. # Allow address from 172.20.0.0/16 * A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127 * A:10.0.0.0/255.255.255.128 # Allow any address that previous set * A:127.0.0.1 # Allow local loopback connections * D:* # Deny from other IP connections * E404:/path/e404.html # /path/e404.html is the 404 (not found) error page * I:index.html # Show index.html when a directory is requested * * P:/url:[http://]hostname[:port]/new/path * # When /urlXXXXXX is requested, reverse proxy * # it to http://hostname[:port]/new/pathXXXXXX * * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/ * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/ * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ * /adm:root:* # or user root, pwd from /etc/passwd on urls starting with /adm/ * /wiki:*:* # or any user from /etc/passwd with according pwd on urls starting with /wiki/ * .au:audio/basic # additional mime type for audio.au files * *.php:/path/php # run xxx.php through an interpreter * * A/D may be as a/d or allow/deny - only first char matters. * Deny/Allow IP logic: * - Default is to allow all (Allow all (A:*) is a no-op). * - Deny rules take precedence over allow rules. * - "Deny all" rule (D:*) is applied last. * * Example: * 1. Allow only specified addresses * A:172.20 # Allow any address that begins with 172.20. * A:10.10. # Allow any address that begins with 10.10. * A:127.0.0.1 # Allow local loopback connections * D:* # Deny from other IP connections * * 2. Only deny specified addresses * D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255 * D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255 * A:* # (optional line added for clarity) * * If a sub directory contains config file, it is parsed and merged with * any existing settings as if it was appended to the original configuration. * * subdir paths are relative to the containing subdir and thus cannot * affect the parent rules. * * Note that since the sub dir is parsed in the forked thread servicing the * subdir http request, any merge is discarded when the process exits. As a * result, the subdir settings only have a lifetime of a single request. * * Custom error pages can contain an absolute path or be relative to * 'home_httpd'. Error pages are to be static files (no CGI or script). Error * page can only be defined in the root configuration file and are not taken * into account in local (directories) config files. * * If -c is not set, an attempt will be made to open the default * root configuration file. If -c is set and the file is not found, the * server exits with an error. * */ /* TODO: use TCP_CORK, parse_config() */ //usage:#define httpd_trivial_usage //usage: "[-ifv[v]]" //usage: " [-c CONFFILE]" //usage: " [-p [IP:]PORT]" //usage: IF_FEATURE_HTTPD_SETUID(" [-u USER[:GRP]]") //usage: IF_FEATURE_HTTPD_BASIC_AUTH(" [-r REALM]") //usage: " [-h HOME]\n" //usage: "or httpd -d/-e" IF_FEATURE_HTTPD_AUTH_MD5("/-m") " STRING" //usage:#define httpd_full_usage "\n\n" //usage: "Listen for incoming HTTP requests\n" //usage: "\n -i Inetd mode" //usage: "\n -f Don't daemonize" //usage: "\n -v[v] Verbose" //usage: "\n -p [IP:]PORT Bind to IP:PORT (default *:80)" //usage: IF_FEATURE_HTTPD_SETUID( //usage: "\n -u USER[:GRP] Set uid/gid after binding to port") //usage: IF_FEATURE_HTTPD_BASIC_AUTH( //usage: "\n -r REALM Authentication Realm for Basic Authentication") //usage: "\n -h HOME Home directory (default .)" //usage: "\n -c FILE Configuration file (default {/etc,HOME}/httpd.conf)" //usage: IF_FEATURE_HTTPD_AUTH_MD5( //usage: "\n -m STRING MD5 crypt STRING") //usage: "\n -e STRING HTML encode STRING" //usage: "\n -d STRING URL decode STRING" #include "libbb.h" #if ENABLE_PAM /* PAM may include . We may need to undefine bbox's stub define: */ # undef setlocale /* For some obscure reason, PAM is not in pam/xxx, but in security/xxx. * Apparently they like to confuse people. */ # include # include #endif #if ENABLE_FEATURE_HTTPD_USE_SENDFILE # include #endif /* amount of buffering in a pipe */ #ifndef PIPE_BUF # define PIPE_BUF 4096 #endif #define DEBUG 0 #define IOBUF_SIZE 8192 #if PIPE_BUF >= IOBUF_SIZE # error "PIPE_BUF >= IOBUF_SIZE" #endif #define HEADER_READ_TIMEOUT 60 static const char DEFAULT_PATH_HTTPD_CONF[] ALIGN1 = "/etc"; static const char HTTPD_CONF[] ALIGN1 = "httpd.conf"; static const char HTTP_200[] ALIGN1 = "HTTP/1.0 200 OK\r\n"; static const char index_html[] ALIGN1 = "index.html"; typedef struct has_next_ptr { struct has_next_ptr *next; } has_next_ptr; /* Must have "next" as a first member */ typedef struct Htaccess { struct Htaccess *next; char *after_colon; char before_colon[1]; /* really bigger, must be last */ } Htaccess; /* Must have "next" as a first member */ typedef struct Htaccess_IP { struct Htaccess_IP *next; unsigned ip; unsigned mask; int allow_deny; } Htaccess_IP; /* Must have "next" as a first member */ typedef struct Htaccess_Proxy { struct Htaccess_Proxy *next; char *url_from; char *host_port; char *url_to; } Htaccess_Proxy; enum { HTTP_OK = 200, HTTP_PARTIAL_CONTENT = 206, HTTP_MOVED_TEMPORARILY = 302, HTTP_BAD_REQUEST = 400, /* malformed syntax */ HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */ HTTP_NOT_FOUND = 404, HTTP_FORBIDDEN = 403, HTTP_REQUEST_TIMEOUT = 408, HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */ HTTP_INTERNAL_SERVER_ERROR = 500, HTTP_CONTINUE = 100, #if 0 /* future use */ HTTP_SWITCHING_PROTOCOLS = 101, HTTP_CREATED = 201, HTTP_ACCEPTED = 202, HTTP_NON_AUTHORITATIVE_INFO = 203, HTTP_NO_CONTENT = 204, HTTP_MULTIPLE_CHOICES = 300, HTTP_MOVED_PERMANENTLY = 301, HTTP_NOT_MODIFIED = 304, HTTP_PAYMENT_REQUIRED = 402, HTTP_BAD_GATEWAY = 502, HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */ #endif }; static const uint16_t http_response_type[] ALIGN2 = { HTTP_OK, #if ENABLE_FEATURE_HTTPD_RANGES HTTP_PARTIAL_CONTENT, #endif HTTP_MOVED_TEMPORARILY, HTTP_REQUEST_TIMEOUT, HTTP_NOT_IMPLEMENTED, #if ENABLE_FEATURE_HTTPD_BASIC_AUTH HTTP_UNAUTHORIZED, #endif HTTP_NOT_FOUND, HTTP_BAD_REQUEST, HTTP_FORBIDDEN, HTTP_INTERNAL_SERVER_ERROR, #if 0 /* not implemented */ HTTP_CREATED, HTTP_ACCEPTED, HTTP_NO_CONTENT, HTTP_MULTIPLE_CHOICES, HTTP_MOVED_PERMANENTLY, HTTP_NOT_MODIFIED, HTTP_BAD_GATEWAY, HTTP_SERVICE_UNAVAILABLE, #endif }; static const struct { const char *name; const char *info; } http_response[ARRAY_SIZE(http_response_type)] = { { "OK", NULL }, #if ENABLE_FEATURE_HTTPD_RANGES { "Partial Content", NULL }, #endif { "Found", NULL }, { "Request Timeout", "No request appeared within 60 seconds" }, { "Not Implemented", "The requested method is not recognized" }, #if ENABLE_FEATURE_HTTPD_BASIC_AUTH { "Unauthorized", "" }, #endif { "Not Found", "The requested URL was not found" }, { "Bad Request", "Unsupported method" }, { "Forbidden", "" }, { "Internal Server Error", "Internal Server Error" }, #if 0 /* not implemented */ { "Created" }, { "Accepted" }, { "No Content" }, { "Multiple Choices" }, { "Moved Permanently" }, { "Not Modified" }, { "Bad Gateway", "" }, { "Service Unavailable", "" }, #endif }; struct globals { int verbose; /* must be int (used by getopt32) */ smallint flg_deny_all; unsigned rmt_ip; /* used for IP-based allow/deny rules */ time_t last_mod; char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */ const char *bind_addr_or_port; const char *g_query; const char *opt_c_configFile; const char *home_httpd; const char *index_page; const char *found_mime_type; const char *found_moved_temporarily; Htaccess_IP *ip_a_d; /* config allow/deny lines */ IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;) IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;) IF_FEATURE_HTTPD_CGI(char *referer;) IF_FEATURE_HTTPD_CGI(char *user_agent;) IF_FEATURE_HTTPD_CGI(char *host;) IF_FEATURE_HTTPD_CGI(char *http_accept;) IF_FEATURE_HTTPD_CGI(char *http_accept_language;) off_t file_size; /* -1 - unknown */ #if ENABLE_FEATURE_HTTPD_RANGES off_t range_start; off_t range_end; off_t range_len; #endif #if ENABLE_FEATURE_HTTPD_BASIC_AUTH Htaccess *g_auth; /* config user:password lines */ #endif Htaccess *mime_a; /* config mime types */ #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR Htaccess *script_i; /* config script interpreters */ #endif char *iobuf; /* [IOBUF_SIZE] */ #define hdr_buf bb_common_bufsiz1 char *hdr_ptr; int hdr_cnt; #if ENABLE_FEATURE_HTTPD_ERROR_PAGES const char *http_error_page[ARRAY_SIZE(http_response_type)]; #endif #if ENABLE_FEATURE_HTTPD_PROXY Htaccess_Proxy *proxy; #endif #if ENABLE_FEATURE_HTTPD_GZIP /* client can handle gzip / we are going to send gzip */ smallint content_gzip; #endif }; #define G (*ptr_to_globals) #define verbose (G.verbose ) #define flg_deny_all (G.flg_deny_all ) #define rmt_ip (G.rmt_ip ) #define bind_addr_or_port (G.bind_addr_or_port) #define g_query (G.g_query ) #define opt_c_configFile (G.opt_c_configFile ) #define home_httpd (G.home_httpd ) #define index_page (G.index_page ) #define found_mime_type (G.found_mime_type ) #define found_moved_temporarily (G.found_moved_temporarily) #define last_mod (G.last_mod ) #define ip_a_d (G.ip_a_d ) #define g_realm (G.g_realm ) #define remoteuser (G.remoteuser ) #define referer (G.referer ) #define user_agent (G.user_agent ) #define host (G.host ) #define http_accept (G.http_accept ) #define http_accept_language (G.http_accept_language) #define file_size (G.file_size ) #if ENABLE_FEATURE_HTTPD_RANGES #define range_start (G.range_start ) #define range_end (G.range_end ) #define range_len (G.range_len ) #else enum { range_start = -1, range_end = MAXINT(off_t) - 1, range_len = MAXINT(off_t), }; #endif #define rmt_ip_str (G.rmt_ip_str ) #define g_auth (G.g_auth ) #define mime_a (G.mime_a ) #define script_i (G.script_i ) #define iobuf (G.iobuf ) #define hdr_ptr (G.hdr_ptr ) #define hdr_cnt (G.hdr_cnt ) #define http_error_page (G.http_error_page ) #define proxy (G.proxy ) #if ENABLE_FEATURE_HTTPD_GZIP # define content_gzip (G.content_gzip ) #else # define content_gzip 0 #endif #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \ IF_FEATURE_HTTPD_RANGES(range_start = -1;) \ bind_addr_or_port = "80"; \ index_page = index_html; \ file_size = -1; \ } while (0) #define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1) /* Prototypes */ enum { SEND_HEADERS = (1 << 0), SEND_BODY = (1 << 1), SEND_HEADERS_AND_BODY = SEND_HEADERS + SEND_BODY, }; static void send_file_and_exit(const char *url, int what) NORETURN; static void free_llist(has_next_ptr **pptr) { has_next_ptr *cur = *pptr; while (cur) { has_next_ptr *t = cur; cur = cur->next; free(t); } *pptr = NULL; } static ALWAYS_INLINE void free_Htaccess_list(Htaccess **pptr) { free_llist((has_next_ptr**)pptr); } static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr) { free_llist((has_next_ptr**)pptr); } /* Returns presumed mask width in bits or < 0 on error. * Updates strp, stores IP at provided pointer */ static int scan_ip(const char **strp, unsigned *ipp, unsigned char endc) { const char *p = *strp; int auto_mask = 8; unsigned ip = 0; int j; if (*p == '/') return -auto_mask; for (j = 0; j < 4; j++) { unsigned octet; if ((*p < '0' || *p > '9') && *p != '/' && *p) return -auto_mask; octet = 0; while (*p >= '0' && *p <= '9') { octet *= 10; octet += *p - '0'; if (octet > 255) return -auto_mask; p++; } if (*p == '.') p++; if (*p != '/' && *p) auto_mask += 8; ip = (ip << 8) | octet; } if (*p) { if (*p != endc) return -auto_mask; p++; if (*p == '\0') return -auto_mask; } *ipp = ip; *strp = p; return auto_mask; } /* Returns 0 on success. Stores IP and mask at provided pointers */ static int scan_ip_mask(const char *str, unsigned *ipp, unsigned *maskp) { int i; unsigned mask; char *p; i = scan_ip(&str, ipp, '/'); if (i < 0) return i; if (*str) { /* there is /xxx after dotted-IP address */ i = bb_strtou(str, &p, 10); if (*p == '.') { /* 'xxx' itself is dotted-IP mask, parse it */ /* (return 0 (success) only if it has N.N.N.N form) */ return scan_ip(&str, maskp, '\0') - 32; } if (*p) return -1; } if (i > 32) return -1; if (sizeof(unsigned) == 4 && i == 32) { /* mask >>= 32 below may not work */ mask = 0; } else { mask = 0xffffffff; mask >>= i; } /* i == 0 -> *maskp = 0x00000000 * i == 1 -> *maskp = 0x80000000 * i == 4 -> *maskp = 0xf0000000 * i == 31 -> *maskp = 0xfffffffe * i == 32 -> *maskp = 0xffffffff */ *maskp = (uint32_t)(~mask); return 0; } /* * Parse configuration file into in-memory linked list. * * Any previous IP rules are discarded. * If the flag argument is not SUBDIR_PARSE then all /path and mime rules * are also discarded. That is, previous settings are retained if flag is * SUBDIR_PARSE. * Error pages are only parsed on the main config file. * * path Path where to look for httpd.conf (without filename). * flag Type of the parse request. */ /* flag param: */ enum { FIRST_PARSE = 0, /* path will be "/etc" */ SIGNALED_PARSE = 1, /* path will be "/etc" */ SUBDIR_PARSE = 2, /* path will be derived from URL */ }; static void parse_conf(const char *path, int flag) { /* internally used extra flag state */ enum { TRY_CURDIR_PARSE = 3 }; FILE *f; const char *filename; char buf[160]; /* discard old rules */ free_Htaccess_IP_list(&ip_a_d); flg_deny_all = 0; /* retain previous auth and mime config only for subdir parse */ if (flag != SUBDIR_PARSE) { free_Htaccess_list(&mime_a); #if ENABLE_FEATURE_HTTPD_BASIC_AUTH free_Htaccess_list(&g_auth); #endif #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR free_Htaccess_list(&script_i); #endif } filename = opt_c_configFile; if (flag == SUBDIR_PARSE || filename == NULL) { filename = alloca(strlen(path) + sizeof(HTTPD_CONF) + 2); sprintf((char *)filename, "%s/%s", path, HTTPD_CONF); } while ((f = fopen_for_read(filename)) == NULL) { if (flag >= SUBDIR_PARSE) { /* SUBDIR or TRY_CURDIR */ /* config file not found, no changes to config */ return; } if (flag == FIRST_PARSE) { /* -c CONFFILE given, but CONFFILE doesn't exist? */ if (opt_c_configFile) bb_simple_perror_msg_and_die(opt_c_configFile); /* else: no -c, thus we looked at /etc/httpd.conf, * and it's not there. try ./httpd.conf: */ } flag = TRY_CURDIR_PARSE; filename = HTTPD_CONF; } #if ENABLE_FEATURE_HTTPD_BASIC_AUTH /* in "/file:user:pass" lines, we prepend path in subdirs */ if (flag != SUBDIR_PARSE) path = ""; #endif /* The lines can be: * * I:default_index_file * H:http_home * [AD]:IP[/mask] # allow/deny, * for wildcard * Ennn:error.html # error page for status nnn * P:/url:[http://]hostname[:port]/new/path # reverse proxy * .ext:mime/type # mime type * *.php:/path/php # run xxx.php through an interpreter * /file:user:pass # username and password */ while (fgets(buf, sizeof(buf), f) != NULL) { unsigned strlen_buf; unsigned char ch; char *after_colon; { /* remove all whitespace, and # comments */ char *p, *p0; p0 = buf; /* skip non-whitespace beginning. Often the whole line * is non-whitespace. We want this case to work fast, * without needless copying, therefore we don't merge * this operation into next while loop. */ while ((ch = *p0) != '\0' && ch != '\n' && ch != '#' && ch != ' ' && ch != '\t' ) { p0++; } p = p0; /* if we enter this loop, we have some whitespace. * discard it */ while (ch != '\0' && ch != '\n' && ch != '#') { if (ch != ' ' && ch != '\t') { *p++ = ch; } ch = *++p0; } *p = '\0'; strlen_buf = p - buf; if (strlen_buf == 0) continue; /* empty line */ } after_colon = strchr(buf, ':'); /* strange line? */ if (after_colon == NULL || *++after_colon == '\0') goto config_error; ch = (buf[0] & ~0x20); /* toupper if it's a letter */ if (ch == 'I') { if (index_page != index_html) free((char*)index_page); index_page = xstrdup(after_colon); continue; } /* do not allow jumping around using H in subdir's configs */ if (flag == FIRST_PARSE && ch == 'H') { home_httpd = xstrdup(after_colon); xchdir(home_httpd); continue; } if (ch == 'A' || ch == 'D') { Htaccess_IP *pip; if (*after_colon == '*') { if (ch == 'D') { /* memorize "deny all" */ flg_deny_all = 1; } /* skip assumed "A:*", it is a default anyway */ continue; } /* store "allow/deny IP/mask" line */ pip = xzalloc(sizeof(*pip)); if (scan_ip_mask(after_colon, &pip->ip, &pip->mask)) { /* IP{/mask} syntax error detected, protect all */ ch = 'D'; pip->mask = 0; } pip->allow_deny = ch; if (ch == 'D') { /* Deny:from_IP - prepend */ pip->next = ip_a_d; ip_a_d = pip; } else { /* A:from_IP - append (thus all D's precedes A's) */ Htaccess_IP *prev_IP = ip_a_d; if (prev_IP == NULL) { ip_a_d = pip; } else { while (prev_IP->next) prev_IP = prev_IP->next; prev_IP->next = pip; } } continue; } #if ENABLE_FEATURE_HTTPD_ERROR_PAGES if (flag == FIRST_PARSE && ch == 'E') { unsigned i; int status = atoi(buf + 1); /* error status code */ if (status < HTTP_CONTINUE) { goto config_error; } /* then error page; find matching status */ for (i = 0; i < ARRAY_SIZE(http_response_type); i++) { if (http_response_type[i] == status) { /* We chdir to home_httpd, thus no need to * concat_path_file(home_httpd, after_colon) * here */ http_error_page[i] = xstrdup(after_colon); break; } } continue; } #endif #if ENABLE_FEATURE_HTTPD_PROXY if (flag == FIRST_PARSE && ch == 'P') { /* P:/url:[http://]hostname[:port]/new/path */ char *url_from, *host_port, *url_to; Htaccess_Proxy *proxy_entry; url_from = after_colon; host_port = strchr(after_colon, ':'); if (host_port == NULL) { goto config_error; } *host_port++ = '\0'; if (strncmp(host_port, "http://", 7) == 0) host_port += 7; if (*host_port == '\0') { goto config_error; } url_to = strchr(host_port, '/'); if (url_to == NULL) { goto config_error; } *url_to = '\0'; proxy_entry = xzalloc(sizeof(*proxy_entry)); proxy_entry->url_from = xstrdup(url_from); proxy_entry->host_port = xstrdup(host_port); *url_to = '/'; proxy_entry->url_to = xstrdup(url_to); proxy_entry->next = proxy; proxy = proxy_entry; continue; } #endif /* the rest of directives are non-alphabetic, * must avoid using "toupper'ed" ch */ ch = buf[0]; if (ch == '.' /* ".ext:mime/type" */ #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR || (ch == '*' && buf[1] == '.') /* "*.php:/path/php" */ #endif ) { char *p; Htaccess *cur; cur = xzalloc(sizeof(*cur) /* includes space for NUL */ + strlen_buf); strcpy(cur->before_colon, buf); p = cur->before_colon + (after_colon - buf); p[-1] = '\0'; cur->after_colon = p; if (ch == '.') { /* .mime line: prepend to mime_a list */ cur->next = mime_a; mime_a = cur; } #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR else { /* script interpreter line: prepend to script_i list */ cur->next = script_i; script_i = cur; } #endif continue; } #if ENABLE_FEATURE_HTTPD_BASIC_AUTH if (ch == '/') { /* "/file:user:pass" */ char *p; Htaccess *cur; unsigned file_len; /* note: path is "" unless we are in SUBDIR parse, * otherwise it does NOT start with "/" */ cur = xzalloc(sizeof(*cur) /* includes space for NUL */ + 1 + strlen(path) + strlen_buf ); /* form "/path/file" */ sprintf(cur->before_colon, "/%s%.*s", path, (int) (after_colon - buf - 1), /* includes "/", but not ":" */ buf); /* canonicalize it */ p = bb_simplify_abs_path_inplace(cur->before_colon); file_len = p - cur->before_colon; /* add "user:pass" after NUL */ strcpy(++p, after_colon); cur->after_colon = p; /* insert cur into g_auth */ /* g_auth is sorted by decreased filename length */ { Htaccess *auth, **authp; authp = &g_auth; while ((auth = *authp) != NULL) { if (file_len >= strlen(auth->before_colon)) { /* insert cur before auth */ cur->next = auth; break; } authp = &auth->next; } *authp = cur; } continue; } #endif /* BASIC_AUTH */ /* the line is not recognized */ config_error: bb_error_msg("config error '%s' in '%s'", buf, filename); } /* while (fgets) */ fclose(f); } #if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR /* * Given a string, html-encode special characters. * This is used for the -e command line option to provide an easy way * for scripts to encode result data without confusing browsers. The * returned string pointer is memory allocated by malloc(). * * Returns a pointer to the encoded string (malloced). */ static char *encodeString(const char *string) { /* take the simple route and encode everything */ /* could possibly scan once to get length. */ int len = strlen(string); char *out = xmalloc(len * 6 + 1); char *p = out; char ch; while ((ch = *string++) != '\0') { /* very simple check for what to encode */ if (isalnum(ch)) *p++ = ch; else p += sprintf(p, "&#%d;", (unsigned char) ch); } *p = '\0'; return out; } #endif #if ENABLE_FEATURE_HTTPD_BASIC_AUTH /* * Decode a base64 data stream as per rfc1521. * Note that the rfc states that non base64 chars are to be ignored. * Since the decode always results in a shorter size than the input, * it is OK to pass the input arg as an output arg. * Parameter: a pointer to a base64 encoded string. * Decoded data is stored in-place. */ static void decodeBase64(char *Data) { const unsigned char *in = (const unsigned char *)Data; /* The decoded size will be at most 3/4 the size of the encoded */ unsigned ch = 0; int i = 0; while (*in) { int t = *in++; if (t >= '0' && t <= '9') t = t - '0' + 52; else if (t >= 'A' && t <= 'Z') t = t - 'A'; else if (t >= 'a' && t <= 'z') t = t - 'a' + 26; else if (t == '+') t = 62; else if (t == '/') t = 63; else if (t == '=') t = 0; else continue; ch = (ch << 6) | t; i++; if (i == 4) { *Data++ = (char) (ch >> 16); *Data++ = (char) (ch >> 8); *Data++ = (char) ch; i = 0; } } *Data = '\0'; } #endif /* * Create a listen server socket on the designated port. */ static int openServer(void) { unsigned n = bb_strtou(bind_addr_or_port, NULL, 10); if (!errno && n && n <= 0xffff) n = create_and_bind_stream_or_die(NULL, n); else n = create_and_bind_stream_or_die(bind_addr_or_port, 80); xlisten(n, 9); return n; } /* * Log the connection closure and exit. */ static void log_and_exit(void) NORETURN; static void log_and_exit(void) { /* Paranoia. IE said to be buggy. It may send some extra data * or be confused by us just exiting without SHUT_WR. Oh well. */ shutdown(1, SHUT_WR); /* Why?? (this also messes up stdin when user runs httpd -i from terminal) ndelay_on(0); while (read(STDIN_FILENO, iobuf, IOBUF_SIZE) > 0) continue; */ if (verbose > 2) bb_error_msg("closed"); _exit(xfunc_error_retval); } /* * Create and send HTTP response headers. * The arguments are combined and sent as one write operation. Note that * IE will puke big-time if the headers are not sent in one packet and the * second packet is delayed for any reason. * responseNum - the result code to send. */ static void send_headers(int responseNum) { static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT"; const char *responseString = ""; const char *infoString = NULL; const char *mime_type; #if ENABLE_FEATURE_HTTPD_ERROR_PAGES const char *error_page = NULL; #endif unsigned i; time_t timer = time(NULL); char tmp_str[80]; int len; for (i = 0; i < ARRAY_SIZE(http_response_type); i++) { if (http_response_type[i] == responseNum) { responseString = http_response[i].name; infoString = http_response[i].info; #if ENABLE_FEATURE_HTTPD_ERROR_PAGES error_page = http_error_page[i]; #endif break; } } /* error message is HTML */ mime_type = responseNum == HTTP_OK ? found_mime_type : "text/html"; if (verbose) bb_error_msg("response:%u", responseNum); /* emit the current date */ strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&timer)); len = sprintf(iobuf, "HTTP/1.0 %d %s\r\nContent-type: %s\r\n" "Date: %s\r\nConnection: close\r\n", responseNum, responseString, mime_type, tmp_str); #if ENABLE_FEATURE_HTTPD_BASIC_AUTH if (responseNum == HTTP_UNAUTHORIZED) { len += sprintf(iobuf + len, "WWW-Authenticate: Basic realm=\"%s\"\r\n", g_realm); } #endif if (responseNum == HTTP_MOVED_TEMPORARILY) { len += sprintf(iobuf + len, "Location: %s/%s%s\r\n", found_moved_temporarily, (g_query ? "?" : ""), (g_query ? g_query : "")); } #if ENABLE_FEATURE_HTTPD_ERROR_PAGES if (error_page && access(error_page, R_OK) == 0) { strcat(iobuf, "\r\n"); len += 2; if (DEBUG) fprintf(stderr, "headers: '%s'\n", iobuf); full_write(STDOUT_FILENO, iobuf, len); if (DEBUG) fprintf(stderr, "writing error page: '%s'\n", error_page); return send_file_and_exit(error_page, SEND_BODY); } #endif if (file_size != -1) { /* file */ strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod)); #if ENABLE_FEATURE_HTTPD_RANGES if (responseNum == HTTP_PARTIAL_CONTENT) { len += sprintf(iobuf + len, "Content-Range: bytes %"OFF_FMT"u-%"OFF_FMT"u/%"OFF_FMT"u\r\n", range_start, range_end, file_size); file_size = range_end - range_start + 1; } #endif len += sprintf(iobuf + len, #if ENABLE_FEATURE_HTTPD_RANGES "Accept-Ranges: bytes\r\n" #endif "Last-Modified: %s\r\n%s %"OFF_FMT"u\r\n", tmp_str, content_gzip ? "Transfer-length:" : "Content-length:", file_size ); } if (content_gzip) len += sprintf(iobuf + len, "Content-Encoding: gzip\r\n"); iobuf[len++] = '\r'; iobuf[len++] = '\n'; if (infoString) { len += sprintf(iobuf + len, "%d %s\n" "

%d %s

\n%s\n\n", responseNum, responseString, responseNum, responseString, infoString); } if (DEBUG) fprintf(stderr, "headers: '%s'\n", iobuf); if (full_write(STDOUT_FILENO, iobuf, len) != len) { if (verbose > 1) bb_perror_msg("error"); log_and_exit(); } } static void send_headers_and_exit(int responseNum) NORETURN; static void send_headers_and_exit(int responseNum) { IF_FEATURE_HTTPD_GZIP(content_gzip = 0;) send_headers(responseNum); log_and_exit(); } /* * Read from the socket until '\n' or EOF. '\r' chars are removed. * '\n' is replaced with NUL. * Return number of characters read or 0 if nothing is read * ('\r' and '\n' are not counted). * Data is returned in iobuf. */ static int get_line(void) { int count = 0; char c; alarm(HEADER_READ_TIMEOUT); while (1) { if (hdr_cnt <= 0) { hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf)); if (hdr_cnt <= 0) break; hdr_ptr = hdr_buf; } iobuf[count] = c = *hdr_ptr++; hdr_cnt--; if (c == '\r') continue; if (c == '\n') { iobuf[count] = '\0'; break; } if (count < (IOBUF_SIZE - 1)) /* check overflow */ count++; } return count; } #if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY /* gcc 4.2.1 fares better with NOINLINE */ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post_len) NORETURN; static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post_len) { enum { FROM_CGI = 1, TO_CGI = 2 }; /* indexes in pfd[] */ struct pollfd pfd[3]; int out_cnt; /* we buffer a bit of initial CGI output */ int count; /* iobuf is used for CGI -> network data, * hdr_buf is for network -> CGI data (POSTDATA) */ /* If CGI dies, we still want to correctly finish reading its output * and send it to the peer. So please no SIGPIPEs! */ signal(SIGPIPE, SIG_IGN); // We inconsistently handle a case when more POSTDATA from network // is coming than we expected. We may give *some part* of that // extra data to CGI. //if (hdr_cnt > post_len) { // /* We got more POSTDATA from network than we expected */ // hdr_cnt = post_len; //} post_len -= hdr_cnt; /* post_len - number of POST bytes not yet read from network */ /* NB: breaking out of this loop jumps to log_and_exit() */ out_cnt = 0; pfd[FROM_CGI].fd = fromCgi_rd; pfd[FROM_CGI].events = POLLIN; pfd[TO_CGI].fd = toCgi_wr; while (1) { /* Note: even pfd[0].events == 0 won't prevent * revents == POLLHUP|POLLERR reports from closed stdin. * Setting fd to -1 works: */ pfd[0].fd = -1; pfd[0].events = POLLIN; pfd[0].revents = 0; /* probably not needed, paranoia */ /* We always poll this fd, thus kernel always sets revents: */ /*pfd[FROM_CGI].events = POLLIN; - moved out of loop */ /*pfd[FROM_CGI].revents = 0; - not needed */ /* gcc-4.8.0 still doesnt fill two shorts with one insn :( */ /* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47059 */ /* hopefully one day it will... */ pfd[TO_CGI].events = POLLOUT; pfd[TO_CGI].revents = 0; /* needed! */ if (toCgi_wr && hdr_cnt <= 0) { if (post_len > 0) { /* Expect more POST data from network */ pfd[0].fd = 0; } else { /* post_len <= 0 && hdr_cnt <= 0: * no more POST data to CGI, * let CGI see EOF on CGI's stdin */ if (toCgi_wr != fromCgi_rd) close(toCgi_wr); toCgi_wr = 0; } } /* Now wait on the set of sockets */ count = safe_poll(pfd, hdr_cnt > 0 ? TO_CGI+1 : FROM_CGI+1, -1); if (count <= 0) { #if 0 if (safe_waitpid(pid, &status, WNOHANG) <= 0) { /* Weird. CGI didn't exit and no fd's * are ready, yet poll returned?! */ continue; } if (DEBUG && WIFEXITED(status)) bb_error_msg("CGI exited, status=%d", WEXITSTATUS(status)); if (DEBUG && WIFSIGNALED(status)) bb_error_msg("CGI killed, signal=%d", WTERMSIG(status)); #endif break; } if (pfd[TO_CGI].revents) { /* hdr_cnt > 0 here due to the way poll() called */ /* Have data from peer and can write to CGI */ count = safe_write(toCgi_wr, hdr_ptr, hdr_cnt); /* Doesn't happen, we dont use nonblocking IO here *if (count < 0 && errno == EAGAIN) { * ... *} else */ if (count > 0) { hdr_ptr += count; hdr_cnt -= count; } else { /* EOF/broken pipe to CGI, stop piping POST data */ hdr_cnt = post_len = 0; } } if (pfd[0].revents) { /* post_len > 0 && hdr_cnt == 0 here */ /* We expect data, prev data portion is eaten by CGI * and there *is* data to read from the peer * (POSTDATA) */ //count = post_len > (int)sizeof(hdr_buf) ? (int)sizeof(hdr_buf) : post_len; //count = safe_read(STDIN_FILENO, hdr_buf, count); count = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf)); if (count > 0) { hdr_cnt = count; hdr_ptr = hdr_buf; post_len -= count; } else { /* no more POST data can be read */ post_len = 0; } } if (pfd[FROM_CGI].revents) { /* There is something to read from CGI */ char *rbuf = iobuf; /* Are we still buffering CGI output? */ if (out_cnt >= 0) { /* HTTP_200[] has single "\r\n" at the end. * According to http://hoohoo.ncsa.uiuc.edu/cgi/out.html, * CGI scripts MUST send their own header terminated by * empty line, then data. That's why we have only one * pair here. We will output "200 OK" line * if needed, but CGI still has to provide blank line * between header and body */ /* Must use safe_read, not full_read, because * CGI may output a few first bytes and then wait * for POSTDATA without closing stdout. * With full_read we may wait here forever. */ count = safe_read(fromCgi_rd, rbuf + out_cnt, PIPE_BUF - 8); if (count <= 0) { /* eof (or error) and there was no "HTTP", * so write it, then write received data */ if (out_cnt) { full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1); full_write(STDOUT_FILENO, rbuf, out_cnt); } break; /* CGI stdout is closed, exiting */ } out_cnt += count; count = 0; /* "Status" header format is: "Status: 302 Redirected\r\n" */ if (out_cnt >= 8 && memcmp(rbuf, "Status: ", 8) == 0) { /* send "HTTP/1.0 " */ if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9) break; rbuf += 8; /* skip "Status: " */ count = out_cnt - 8; out_cnt = -1; /* buffering off */ } else if (out_cnt >= 4) { /* Did CGI add "HTTP"? */ if (memcmp(rbuf, HTTP_200, 4) != 0) { /* there is no "HTTP", do it ourself */ if (full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1) break; } /* Commented out: if (!strstr(rbuf, "ontent-")) { full_write(s, "Content-type: text/plain\r\n\r\n", 28); } * Counter-example of valid CGI without Content-type: * echo -en "HTTP/1.0 302 Found\r\n" * echo -en "Location: http://www.busybox.net\r\n" * echo -en "\r\n" */ count = out_cnt; out_cnt = -1; /* buffering off */ } } else { count = safe_read(fromCgi_rd, rbuf, PIPE_BUF); if (count <= 0) break; /* eof (or error) */ } if (full_write(STDOUT_FILENO, rbuf, count) != count) break; if (DEBUG) fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf); } /* if (pfd[FROM_CGI].revents) */ } /* while (1) */ log_and_exit(); } #endif #if ENABLE_FEATURE_HTTPD_CGI static void setenv1(const char *name, const char *value) { setenv(name, value ? value : "", 1); } /* * Spawn CGI script, forward CGI's stdin/out <=> network * * Environment variables are set up and the script is invoked with pipes * for stdin/stdout. If a POST is being done the script is fed the POST * data in addition to setting the QUERY_STRING variable (for GETs or POSTs). * * Parameters: * const char *url The requested URL (with leading /). * const char *orig_uri The original URI before rewriting (if any) * int post_len Length of the POST body. * const char *cookie For set HTTP_COOKIE. * const char *content_type For set CONTENT_TYPE. */ static void send_cgi_and_exit( const char *url, const char *orig_uri, const char *request, int post_len, const char *cookie, const char *content_type) NORETURN; static void send_cgi_and_exit( const char *url, const char *orig_uri, const char *request, int post_len, const char *cookie, const char *content_type) { struct fd_pair fromCgi; /* CGI -> httpd pipe */ struct fd_pair toCgi; /* httpd -> CGI pipe */ char *script, *last_slash; int pid; /* Make a copy. NB: caller guarantees: * url[0] == '/', url[1] != '/' */ url = xstrdup(url); /* * We are mucking with environment _first_ and then vfork/exec, * this allows us to use vfork safely. Parent doesn't care about * these environment changes anyway. */ /* Check for [dirs/]script.cgi/PATH_INFO */ last_slash = script = (char*)url; while ((script = strchr(script + 1, '/')) != NULL) { int dir; *script = '\0'; dir = is_directory(url + 1, /*followlinks:*/ 1); *script = '/'; if (!dir) { /* not directory, found script.cgi/PATH_INFO */ break; } /* is directory, find next '/' */ last_slash = script; } setenv1("PATH_INFO", script); /* set to /PATH_INFO or "" */ setenv1("REQUEST_METHOD", request); if (g_query) { putenv(xasprintf("%s=%s?%s", "REQUEST_URI", orig_uri, g_query)); } else { setenv1("REQUEST_URI", orig_uri); } if (script != NULL) *script = '\0'; /* cut off /PATH_INFO */ /* SCRIPT_FILENAME is required by PHP in CGI mode */ if (home_httpd[0] == '/') { char *fullpath = concat_path_file(home_httpd, url); setenv1("SCRIPT_FILENAME", fullpath); } /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */ setenv1("SCRIPT_NAME", url); /* http://hoohoo.ncsa.uiuc.edu/cgi/env.html: * QUERY_STRING: The information which follows the ? in the URL * which referenced this script. This is the query information. * It should not be decoded in any fashion. This variable * should always be set when there is query information, * regardless of command line decoding. */ /* (Older versions of bbox seem to do some decoding) */ setenv1("QUERY_STRING", g_query); putenv((char*)"SERVER_SOFTWARE=busybox httpd/"BB_VER); putenv((char*)"SERVER_PROTOCOL=HTTP/1.0"); putenv((char*)"GATEWAY_INTERFACE=CGI/1.1"); /* Having _separate_ variables for IP and port defeats * the purpose of having socket abstraction. Which "port" * are you using on Unix domain socket? * IOW - REMOTE_PEER="1.2.3.4:56" makes much more sense. * Oh well... */ { char *p = rmt_ip_str ? rmt_ip_str : (char*)""; char *cp = strrchr(p, ':'); if (ENABLE_FEATURE_IPV6 && cp && strchr(cp, ']')) cp = NULL; if (cp) *cp = '\0'; /* delete :PORT */ setenv1("REMOTE_ADDR", p); if (cp) { *cp = ':'; #if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV setenv1("REMOTE_PORT", cp + 1); #endif } } setenv1("HTTP_USER_AGENT", user_agent); if (http_accept) setenv1("HTTP_ACCEPT", http_accept); if (http_accept_language) setenv1("HTTP_ACCEPT_LANGUAGE", http_accept_language); if (post_len) putenv(xasprintf("CONTENT_LENGTH=%d", post_len)); if (cookie) setenv1("HTTP_COOKIE", cookie); if (content_type) setenv1("CONTENT_TYPE", content_type); #if ENABLE_FEATURE_HTTPD_BASIC_AUTH if (remoteuser) { setenv1("REMOTE_USER", remoteuser); putenv((char*)"AUTH_TYPE=Basic"); } #endif if (referer) setenv1("HTTP_REFERER", referer); setenv1("HTTP_HOST", host); /* set to "" if NULL */ /* setenv1("SERVER_NAME", safe_gethostname()); - don't do this, * just run "env SERVER_NAME=xyz httpd ..." instead */ xpiped_pair(fromCgi); xpiped_pair(toCgi); pid = vfork(); if (pid < 0) { /* TODO: log perror? */ log_and_exit(); } if (pid == 0) { /* Child process */ char *argv[3]; xfunc_error_retval = 242; /* NB: close _first_, then move fds! */ close(toCgi.wr); close(fromCgi.rd); xmove_fd(toCgi.rd, 0); /* replace stdin with the pipe */ xmove_fd(fromCgi.wr, 1); /* replace stdout with the pipe */ /* User seeing stderr output can be a security problem. * If CGI really wants that, it can always do dup itself. */ /* dup2(1, 2); */ /* Chdiring to script's dir */ script = last_slash; if (script != url) { /* paranoia */ *script = '\0'; if (chdir(url + 1) != 0) { bb_perror_msg("can't change directory to '%s'", url + 1); goto error_execing_cgi; } // not needed: *script = '/'; } script++; /* set argv[0] to name without path */ argv[0] = script; argv[1] = NULL; #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR { char *suffix = strrchr(script, '.'); if (suffix) { Htaccess *cur; for (cur = script_i; cur; cur = cur->next) { if (strcmp(cur->before_colon + 1, suffix) == 0) { /* found interpreter name */ argv[0] = cur->after_colon; argv[1] = script; argv[2] = NULL; break; } } } } #endif /* restore default signal dispositions for CGI process */ bb_signals(0 | (1 << SIGCHLD) | (1 << SIGPIPE) | (1 << SIGHUP) , SIG_DFL); /* _NOT_ execvp. We do not search PATH. argv[0] is a filename * without any dir components and will only match a file * in the current directory */ execv(argv[0], argv); if (verbose) bb_perror_msg("can't execute '%s'", argv[0]); error_execing_cgi: /* send to stdout * (we are CGI here, our stdout is pumped to the net) */ send_headers_and_exit(HTTP_NOT_FOUND); } /* end child */ /* Parent process */ /* Restore variables possibly changed by child */ xfunc_error_retval = 0; /* Pump data */ close(fromCgi.wr); close(toCgi.rd); cgi_io_loop_and_exit(fromCgi.rd, toCgi.wr, post_len); } #endif /* FEATURE_HTTPD_CGI */ /* * Send a file response to a HTTP request, and exit * * Parameters: * const char *url The requested URL (with leading /). * what What to send (headers/body/both). */ static NOINLINE void send_file_and_exit(const char *url, int what) { char *suffix; int fd; ssize_t count; if (content_gzip) { /* does .gz exist? Then use it instead */ char *gzurl = xasprintf("%s.gz", url); fd = open(gzurl, O_RDONLY); free(gzurl); if (fd != -1) { struct stat sb; fstat(fd, &sb); file_size = sb.st_size; last_mod = sb.st_mtime; } else { IF_FEATURE_HTTPD_GZIP(content_gzip = 0;) fd = open(url, O_RDONLY); } } else { fd = open(url, O_RDONLY); } if (fd < 0) { if (DEBUG) bb_perror_msg("can't open '%s'", url); /* Error pages are sent by using send_file_and_exit(SEND_BODY). * IOW: it is unsafe to call send_headers_and_exit * if what is SEND_BODY! Can recurse! */ if (what != SEND_BODY) send_headers_and_exit(HTTP_NOT_FOUND); log_and_exit(); } /* If you want to know about EPIPE below * (happens if you abort downloads from local httpd): */ signal(SIGPIPE, SIG_IGN); /* If not found, default is "application/octet-stream" */ found_mime_type = "application/octet-stream"; suffix = strrchr(url, '.'); if (suffix) { static const char suffixTable[] ALIGN1 = /* Shorter suffix must be first: * ".html.htm" will fail for ".htm" */ ".txt.h.c.cc.cpp\0" "text/plain\0" /* .htm line must be after .h line */ ".htm.html\0" "text/html\0" ".jpg.jpeg\0" "image/jpeg\0" ".gif\0" "image/gif\0" ".png\0" "image/png\0" /* .css line must be after .c line */ ".css\0" "text/css\0" ".wav\0" "audio/wav\0" ".avi\0" "video/x-msvideo\0" ".qt.mov\0" "video/quicktime\0" ".mpe.mpeg\0" "video/mpeg\0" ".mid.midi\0" "audio/midi\0" ".mp3\0" "audio/mpeg\0" #if 0 /* unpopular */ ".au\0" "audio/basic\0" ".pac\0" "application/x-ns-proxy-autoconfig\0" ".vrml.wrl\0" "model/vrml\0" #endif /* compiler adds another "\0" here */ ; Htaccess *cur; /* Examine built-in table */ const char *table = suffixTable; const char *table_next; for (; *table; table = table_next) { const char *try_suffix; const char *mime_type; mime_type = table + strlen(table) + 1; table_next = mime_type + strlen(mime_type) + 1; try_suffix = strstr(table, suffix); if (!try_suffix) continue; try_suffix += strlen(suffix); if (*try_suffix == '\0' || *try_suffix == '.') { found_mime_type = mime_type; break; } /* Example: strstr(table, ".av") != NULL, but it * does not match ".avi" after all and we end up here. * The table is arranged so that in this case we know * that it can't match anything in the following lines, * and we stop the search: */ break; } /* ...then user's table */ for (cur = mime_a; cur; cur = cur->next) { if (strcmp(cur->before_colon, suffix) == 0) { found_mime_type = cur->after_colon; break; } } } if (DEBUG) bb_error_msg("sending file '%s' content-type: %s", url, found_mime_type); #if ENABLE_FEATURE_HTTPD_RANGES if (what == SEND_BODY /* err pages and ranges don't mix */ || content_gzip /* we are sending compressed page: can't do ranges */ ///why? ) { range_start = -1; } range_len = MAXINT(off_t); if (range_start >= 0) { if (!range_end || range_end > file_size - 1) { range_end = file_size - 1; } if (range_end < range_start || lseek(fd, range_start, SEEK_SET) != range_start ) { lseek(fd, 0, SEEK_SET); range_start = -1; } else { range_len = range_end - range_start + 1; send_headers(HTTP_PARTIAL_CONTENT); what = SEND_BODY; } } #endif if (what & SEND_HEADERS) send_headers(HTTP_OK); #if ENABLE_FEATURE_HTTPD_USE_SENDFILE { off_t offset = range_start; while (1) { /* sz is rounded down to 64k */ ssize_t sz = MAXINT(ssize_t) - 0xffff; IF_FEATURE_HTTPD_RANGES(if (sz > range_len) sz = range_len;) count = sendfile(STDOUT_FILENO, fd, &offset, sz); if (count < 0) { if (offset == range_start) break; /* fall back to read/write loop */ goto fin; } IF_FEATURE_HTTPD_RANGES(range_len -= count;) if (count == 0 || range_len == 0) log_and_exit(); } } #endif while ((count = safe_read(fd, iobuf, IOBUF_SIZE)) > 0) { ssize_t n; IF_FEATURE_HTTPD_RANGES(if (count > range_len) count = range_len;) n = full_write(STDOUT_FILENO, iobuf, count); if (count != n) break; IF_FEATURE_HTTPD_RANGES(range_len -= count;) if (range_len == 0) break; } if (count < 0) { IF_FEATURE_HTTPD_USE_SENDFILE(fin:) if (verbose > 1) bb_perror_msg("error"); } log_and_exit(); } static int checkPermIP(void) { Htaccess_IP *cur; for (cur = ip_a_d; cur; cur = cur->next) { #if DEBUG fprintf(stderr, "checkPermIP: '%s' ? '%u.%u.%u.%u/%u.%u.%u.%u'\n", rmt_ip_str, (unsigned char)(cur->ip >> 24), (unsigned char)(cur->ip >> 16), (unsigned char)(cur->ip >> 8), (unsigned char)(cur->ip), (unsigned char)(cur->mask >> 24), (unsigned char)(cur->mask >> 16), (unsigned char)(cur->mask >> 8), (unsigned char)(cur->mask) ); #endif if ((rmt_ip & cur->mask) == cur->ip) return (cur->allow_deny == 'A'); /* A -> 1 */ } return !flg_deny_all; /* depends on whether we saw "D:*" */ } #if ENABLE_FEATURE_HTTPD_BASIC_AUTH # if ENABLE_PAM struct pam_userinfo { const char *name; const char *pw; }; static int pam_talker(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { int i; struct pam_userinfo *userinfo = (struct pam_userinfo *) appdata_ptr; struct pam_response *response; if (!resp || !msg || !userinfo) return PAM_CONV_ERR; /* allocate memory to store response */ response = xzalloc(num_msg * sizeof(*response)); /* copy values */ for (i = 0; i < num_msg; i++) { const char *s; switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON: s = userinfo->name; break; case PAM_PROMPT_ECHO_OFF: s = userinfo->pw; break; case PAM_ERROR_MSG: case PAM_TEXT_INFO: s = ""; break; default: free(response); return PAM_CONV_ERR; } response[i].resp = xstrdup(s); if (PAM_SUCCESS != 0) response[i].resp_retcode = PAM_SUCCESS; } *resp = response; return PAM_SUCCESS; } # endif /* * Config file entries are of the form "/::". * If config file has no prefix match for path, access is allowed. * * path The file path * user_and_passwd "user:passwd" to validate * * Returns 1 if user_and_passwd is OK. */ static int check_user_passwd(const char *path, char *user_and_passwd) { Htaccess *cur; const char *prev = NULL; for (cur = g_auth; cur; cur = cur->next) { const char *dir_prefix; size_t len; int r; dir_prefix = cur->before_colon; /* WHY? */ /* If already saw a match, don't accept other different matches */ if (prev && strcmp(prev, dir_prefix) != 0) continue; if (DEBUG) fprintf(stderr, "checkPerm: '%s' ? '%s'\n", dir_prefix, user_and_passwd); /* If it's not a prefix match, continue searching */ len = strlen(dir_prefix); if (len != 1 /* dir_prefix "/" matches all, don't need to check */ && (strncmp(dir_prefix, path, len) != 0 || (path[len] != '/' && path[len] != '\0') ) ) { continue; } /* Path match found */ prev = dir_prefix; if (ENABLE_FEATURE_HTTPD_AUTH_MD5) { char *colon_after_user; const char *passwd; # if ENABLE_FEATURE_SHADOWPASSWDS && !ENABLE_PAM char sp_buf[256]; # endif colon_after_user = strchr(user_and_passwd, ':'); if (!colon_after_user) goto bad_input; /* compare "user:" */ if (cur->after_colon[0] != '*' && strncmp(cur->after_colon, user_and_passwd, colon_after_user - user_and_passwd + 1) != 0 ) { continue; } /* this cfg entry is '*' or matches username from peer */ passwd = strchr(cur->after_colon, ':'); if (!passwd) goto bad_input; passwd++; if (passwd[0] == '*') { # if ENABLE_PAM struct pam_userinfo userinfo; struct pam_conv conv_info = { &pam_talker, (void *) &userinfo }; pam_handle_t *pamh; *colon_after_user = '\0'; userinfo.name = user_and_passwd; userinfo.pw = colon_after_user + 1; r = pam_start("httpd", user_and_passwd, &conv_info, &pamh) != PAM_SUCCESS; if (r == 0) { r = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS || pam_acct_mgmt(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS ; pam_end(pamh, PAM_SUCCESS); } *colon_after_user = ':'; goto end_check_passwd; # else # if ENABLE_FEATURE_SHADOWPASSWDS /* Using _r function to avoid pulling in static buffers */ struct spwd spw; # endif struct passwd *pw; *colon_after_user = '\0'; pw = getpwnam(user_and_passwd); *colon_after_user = ':'; if (!pw || !pw->pw_passwd) continue; passwd = pw->pw_passwd; # if ENABLE_FEATURE_SHADOWPASSWDS if ((passwd[0] == 'x' || passwd[0] == '*') && !passwd[1]) { /* getspnam_r may return 0 yet set result to NULL. * At least glibc 2.4 does this. Be extra paranoid here. */ struct spwd *result = NULL; r = getspnam_r(pw->pw_name, &spw, sp_buf, sizeof(sp_buf), &result); if (r == 0 && result) passwd = result->sp_pwdp; } # endif /* In this case, passwd is ALWAYS encrypted: * it came from /etc/passwd or /etc/shadow! */ goto check_encrypted; # endif /* ENABLE_PAM */ } /* Else: passwd is from httpd.conf, it is either plaintext or encrypted */ if (passwd[0] == '$' && isdigit(passwd[1])) { char *encrypted; # if !ENABLE_PAM check_encrypted: # endif /* encrypt pwd from peer and check match with local one */ encrypted = pw_encrypt( /* pwd (from peer): */ colon_after_user + 1, /* salt: */ passwd, /* cleanup: */ 0 ); r = strcmp(encrypted, passwd); free(encrypted); } else { /* local passwd is from httpd.conf and it's plaintext */ r = strcmp(colon_after_user + 1, passwd); } goto end_check_passwd; } bad_input: /* Comparing plaintext "user:pass" in one go */ r = strcmp(cur->after_colon, user_and_passwd); end_check_passwd: if (r == 0) { remoteuser = xstrndup(user_and_passwd, strchrnul(user_and_passwd, ':') - user_and_passwd ); return 1; /* Ok */ } } /* for */ /* 0(bad) if prev is set: matches were found but passwd was wrong */ return (prev == NULL); } #endif /* FEATURE_HTTPD_BASIC_AUTH */ #if ENABLE_FEATURE_HTTPD_PROXY static Htaccess_Proxy *find_proxy_entry(const char *url) { Htaccess_Proxy *p; for (p = proxy; p; p = p->next) { if (strncmp(url, p->url_from, strlen(p->url_from)) == 0) return p; } return NULL; } #endif /* * Handle timeouts */ static void send_REQUEST_TIMEOUT_and_exit(int sig) NORETURN; static void send_REQUEST_TIMEOUT_and_exit(int sig UNUSED_PARAM) { send_headers_and_exit(HTTP_REQUEST_TIMEOUT); } /* * Handle an incoming http request and exit. */ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) NORETURN; static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) { static const char request_GET[] ALIGN1 = "GET"; struct stat sb; char *urlcopy; char *urlp; char *tptr; #if ENABLE_FEATURE_HTTPD_CGI static const char request_HEAD[] ALIGN1 = "HEAD"; const char *prequest; char *cookie = NULL; char *content_type = NULL; unsigned long length = 0; #elif ENABLE_FEATURE_HTTPD_PROXY #define prequest request_GET unsigned long length = 0; #endif #if ENABLE_FEATURE_HTTPD_BASIC_AUTH smallint authorized = -1; #endif smallint ip_allowed; char http_major_version; #if ENABLE_FEATURE_HTTPD_PROXY char http_minor_version; char *header_buf = header_buf; /* for gcc */ char *header_ptr = header_ptr; Htaccess_Proxy *proxy_entry; #endif /* Allocation of iobuf is postponed until now * (IOW, server process doesn't need to waste 8k) */ iobuf = xmalloc(IOBUF_SIZE); rmt_ip = 0; if (fromAddr->u.sa.sa_family == AF_INET) { rmt_ip = ntohl(fromAddr->u.sin.sin_addr.s_addr); } #if ENABLE_FEATURE_IPV6 if (fromAddr->u.sa.sa_family == AF_INET6 && fromAddr->u.sin6.sin6_addr.s6_addr32[0] == 0 && fromAddr->u.sin6.sin6_addr.s6_addr32[1] == 0 && ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[2]) == 0xffff) rmt_ip = ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[3]); #endif if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) { /* NB: can be NULL (user runs httpd -i by hand?) */ rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr->u.sa); } if (verbose) { /* this trick makes -v logging much simpler */ if (rmt_ip_str) applet_name = rmt_ip_str; if (verbose > 2) bb_error_msg("connected"); } /* Install timeout handler. get_line() needs it. */ signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit); if (!get_line()) /* EOF or error or empty line */ send_headers_and_exit(HTTP_BAD_REQUEST); /* Determine type of request (GET/POST) */ // rfc2616: method and URI is separated by exactly one space //urlp = strpbrk(iobuf, " \t"); - no, tab isn't allowed urlp = strchr(iobuf, ' '); if (urlp == NULL) send_headers_and_exit(HTTP_BAD_REQUEST); *urlp++ = '\0'; #if ENABLE_FEATURE_HTTPD_CGI prequest = request_GET; if (strcasecmp(iobuf, prequest) != 0) { prequest = request_HEAD; if (strcasecmp(iobuf, prequest) != 0) { prequest = "POST"; if (strcasecmp(iobuf, prequest) != 0) send_headers_and_exit(HTTP_NOT_IMPLEMENTED); } } #else if (strcasecmp(iobuf, request_GET) != 0) send_headers_and_exit(HTTP_NOT_IMPLEMENTED); #endif // rfc2616: method and URI is separated by exactly one space //urlp = skip_whitespace(urlp); - should not be necessary if (urlp[0] != '/') send_headers_and_exit(HTTP_BAD_REQUEST); /* Find end of URL and parse HTTP version, if any */ http_major_version = '0'; IF_FEATURE_HTTPD_PROXY(http_minor_version = '0';) tptr = strchrnul(urlp, ' '); /* Is it " HTTP/"? */ if (tptr[0] && strncmp(tptr + 1, HTTP_200, 5) == 0) { http_major_version = tptr[6]; IF_FEATURE_HTTPD_PROXY(http_minor_version = tptr[8];) } *tptr = '\0'; /* Copy URL from after "GET "/"POST " to stack-allocated char[] */ urlcopy = alloca((tptr - urlp) + 2 + strlen(index_page)); /*if (urlcopy == NULL) * send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);*/ strcpy(urlcopy, urlp); /* NB: urlcopy ptr is never changed after this */ /* Extract url args if present */ /* g_query = NULL; - already is */ tptr = strchr(urlcopy, '?'); if (tptr) { *tptr++ = '\0'; g_query = tptr; } /* Decode URL escape sequences */ tptr = percent_decode_in_place(urlcopy, /*strict:*/ 1); if (tptr == NULL) send_headers_and_exit(HTTP_BAD_REQUEST); if (tptr == urlcopy + 1) { /* '/' or NUL is encoded */ send_headers_and_exit(HTTP_NOT_FOUND); } /* Canonicalize path */ /* Algorithm stolen from libbb bb_simplify_path(), * but don't strdup, retain trailing slash, protect root */ urlp = tptr = urlcopy; for (;;) { if (*urlp == '/') { /* skip duplicate (or initial) slash */ if (*tptr == '/') { goto next_char; } if (*tptr == '.') { if (tptr[1] == '.' && (tptr[2] == '/' || tptr[2] == '\0')) { /* "..": be careful */ /* protect root */ if (urlp == urlcopy) send_headers_and_exit(HTTP_BAD_REQUEST); /* omit previous dir */ while (*--urlp != '/') continue; /* skip to "./" or "." */ tptr++; } if (tptr[1] == '/' || tptr[1] == '\0') { /* skip extra "/./" */ goto next_char; } } } *++urlp = *tptr; if (*urlp == '\0') break; next_char: tptr++; } /* If URL is a directory, add '/' */ if (urlp[-1] != '/') { if (is_directory(urlcopy + 1, /*followlinks:*/ 1)) { found_moved_temporarily = urlcopy; } } /* Log it */ if (verbose > 1) bb_error_msg("url:%s", urlcopy); tptr = urlcopy; ip_allowed = checkPermIP(); while (ip_allowed && (tptr = strchr(tptr + 1, '/')) != NULL) { /* have path1/path2 */ *tptr = '\0'; if (is_directory(urlcopy + 1, /*followlinks:*/ 1)) { /* may have subdir config */ parse_conf(urlcopy + 1, SUBDIR_PARSE); ip_allowed = checkPermIP(); } *tptr = '/'; } #if ENABLE_FEATURE_HTTPD_PROXY proxy_entry = find_proxy_entry(urlcopy); if (proxy_entry) header_buf = header_ptr = xmalloc(IOBUF_SIZE); #endif if (http_major_version >= '0') { /* Request was with "... HTTP/nXXX", and n >= 0 */ /* Read until blank line */ while (1) { if (!get_line()) break; /* EOF or error or empty line */ if (DEBUG) bb_error_msg("header: '%s'", iobuf); #if ENABLE_FEATURE_HTTPD_PROXY /* We need 2 more bytes for yet another "\r\n" - * see near fdprintf(proxy_fd...) further below */ if (proxy_entry && (header_ptr - header_buf) < IOBUF_SIZE - 2) { int len = strlen(iobuf); if (len > IOBUF_SIZE - (header_ptr - header_buf) - 4) len = IOBUF_SIZE - (header_ptr - header_buf) - 4; memcpy(header_ptr, iobuf, len); header_ptr += len; header_ptr[0] = '\r'; header_ptr[1] = '\n'; header_ptr += 2; } #endif #if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY /* Try and do our best to parse more lines */ if ((STRNCASECMP(iobuf, "Content-length:") == 0)) { /* extra read only for POST */ if (prequest != request_GET # if ENABLE_FEATURE_HTTPD_CGI && prequest != request_HEAD # endif ) { tptr = skip_whitespace(iobuf + sizeof("Content-length:") - 1); if (!tptr[0]) send_headers_and_exit(HTTP_BAD_REQUEST); /* not using strtoul: it ignores leading minus! */ length = bb_strtou(tptr, NULL, 10); /* length is "ulong", but we need to pass it to int later */ if (errno || length > INT_MAX) send_headers_and_exit(HTTP_BAD_REQUEST); } } #endif #if ENABLE_FEATURE_HTTPD_CGI else if (STRNCASECMP(iobuf, "Cookie:") == 0) { cookie = xstrdup(skip_whitespace(iobuf + sizeof("Cookie:")-1)); } else if (STRNCASECMP(iobuf, "Content-Type:") == 0) { content_type = xstrdup(skip_whitespace(iobuf + sizeof("Content-Type:")-1)); } else if (STRNCASECMP(iobuf, "Referer:") == 0) { referer = xstrdup(skip_whitespace(iobuf + sizeof("Referer:")-1)); } else if (STRNCASECMP(iobuf, "User-Agent:") == 0) { user_agent = xstrdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1)); } else if (STRNCASECMP(iobuf, "Host:") == 0) { host = xstrdup(skip_whitespace(iobuf + sizeof("Host:")-1)); } else if (STRNCASECMP(iobuf, "Accept:") == 0) { http_accept = xstrdup(skip_whitespace(iobuf + sizeof("Accept:")-1)); } else if (STRNCASECMP(iobuf, "Accept-Language:") == 0) { http_accept_language = xstrdup(skip_whitespace(iobuf + sizeof("Accept-Language:")-1)); } #endif #if ENABLE_FEATURE_HTTPD_BASIC_AUTH if (STRNCASECMP(iobuf, "Authorization:") == 0) { /* We only allow Basic credentials. * It shows up as "Authorization: Basic :" where * ":" is base64 encoded. */ tptr = skip_whitespace(iobuf + sizeof("Authorization:")-1); if (STRNCASECMP(tptr, "Basic") != 0) continue; tptr += sizeof("Basic")-1; /* decodeBase64() skips whitespace itself */ decodeBase64(tptr); authorized = check_user_passwd(urlcopy, tptr); } #endif #if ENABLE_FEATURE_HTTPD_RANGES if (STRNCASECMP(iobuf, "Range:") == 0) { /* We know only bytes=NNN-[MMM] */ char *s = skip_whitespace(iobuf + sizeof("Range:")-1); if (strncmp(s, "bytes=", 6) == 0) { s += sizeof("bytes=")-1; range_start = BB_STRTOOFF(s, &s, 10); if (s[0] != '-' || range_start < 0) { range_start = -1; } else if (s[1]) { range_end = BB_STRTOOFF(s+1, NULL, 10); if (errno || range_end < range_start) range_start = -1; } } } #endif #if ENABLE_FEATURE_HTTPD_GZIP if (STRNCASECMP(iobuf, "Accept-Encoding:") == 0) { /* Note: we do not support "gzip;q=0" * method of _disabling_ gzip * delivery. No one uses that, though */ const char *s = strstr(iobuf, "gzip"); if (s) { // want more thorough checks? //if (s[-1] == ' ' // || s[-1] == ',' // || s[-1] == ':' //) { content_gzip = 1; //} } } #endif } /* while extra header reading */ } /* We are done reading headers, disable peer timeout */ alarm(0); if (strcmp(bb_basename(urlcopy), HTTPD_CONF) == 0 || !ip_allowed) { /* protect listing [/path]/httpd.conf or IP deny */ send_headers_and_exit(HTTP_FORBIDDEN); } #if ENABLE_FEATURE_HTTPD_BASIC_AUTH /* Case: no "Authorization:" was seen, but page might require passwd. * Check that with dummy user:pass */ if (authorized < 0) authorized = check_user_passwd(urlcopy, (char *) ""); if (!authorized) send_headers_and_exit(HTTP_UNAUTHORIZED); #endif if (found_moved_temporarily) { send_headers_and_exit(HTTP_MOVED_TEMPORARILY); } #if ENABLE_FEATURE_HTTPD_PROXY if (proxy_entry != NULL) { int proxy_fd; len_and_sockaddr *lsa; proxy_fd = socket(AF_INET, SOCK_STREAM, 0); if (proxy_fd < 0) send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); lsa = host2sockaddr(proxy_entry->host_port, 80); if (lsa == NULL) send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); if (connect(proxy_fd, &lsa->u.sa, lsa->len) < 0) send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); fdprintf(proxy_fd, "%s %s%s%s%s HTTP/%c.%c\r\n", prequest, /* GET or POST */ proxy_entry->url_to, /* url part 1 */ urlcopy + strlen(proxy_entry->url_from), /* url part 2 */ (g_query ? "?" : ""), /* "?" (maybe) */ (g_query ? g_query : ""), /* query string (maybe) */ http_major_version, http_minor_version); header_ptr[0] = '\r'; header_ptr[1] = '\n'; header_ptr += 2; write(proxy_fd, header_buf, header_ptr - header_buf); free(header_buf); /* on the order of 8k, free it */ cgi_io_loop_and_exit(proxy_fd, proxy_fd, length); } #endif tptr = urlcopy + 1; /* skip first '/' */ #if ENABLE_FEATURE_HTTPD_CGI if (strncmp(tptr, "cgi-bin/", 8) == 0) { if (tptr[8] == '\0') { /* protect listing "cgi-bin/" */ send_headers_and_exit(HTTP_FORBIDDEN); } send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type); } #endif if (urlp[-1] == '/') { /* When index_page string is appended to / URL, it overwrites * the query string. If we fall back to call /cgi-bin/index.cgi, * query string would be lost and not available to the CGI. * Work around it by making a deep copy. */ if (ENABLE_FEATURE_HTTPD_CGI) g_query = xstrdup(g_query); /* ok for NULL too */ strcpy(urlp, index_page); } if (stat(tptr, &sb) == 0) { #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR char *suffix = strrchr(tptr, '.'); if (suffix) { Htaccess *cur; for (cur = script_i; cur; cur = cur->next) { if (strcmp(cur->before_colon + 1, suffix) == 0) { send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type); } } } #endif file_size = sb.st_size; last_mod = sb.st_mtime; } #if ENABLE_FEATURE_HTTPD_CGI else if (urlp[-1] == '/') { /* It's a dir URL and there is no index.html * Try cgi-bin/index.cgi */ if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) { urlp[0] = '\0'; /* remove index_page */ send_cgi_and_exit("/cgi-bin/index.cgi", urlcopy, prequest, length, cookie, content_type); } } /* else fall through to send_file, it errors out if open fails: */ if (prequest != request_GET && prequest != request_HEAD) { /* POST for files does not make sense */ send_headers_and_exit(HTTP_NOT_IMPLEMENTED); } send_file_and_exit(tptr, (prequest != request_HEAD ? SEND_HEADERS_AND_BODY : SEND_HEADERS) ); #else send_file_and_exit(tptr, SEND_HEADERS_AND_BODY); #endif } /* * The main http server function. * Given a socket, listen for new connections and farm out * the processing as a [v]forked process. * Never returns. */ #if BB_MMU static void mini_httpd(int server_socket) NORETURN; static void mini_httpd(int server_socket) { /* NB: it's best to not use xfuncs in this loop before fork(). * Otherwise server may die on transient errors (temporary * out-of-memory condition, etc), which is Bad(tm). * Try to do any dangerous calls after fork. */ while (1) { int n; len_and_sockaddr fromAddr; /* Wait for connections... */ fromAddr.len = LSA_SIZEOF_SA; n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len); if (n < 0) continue; /* set the KEEPALIVE option to cull dead connections */ setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); if (fork() == 0) { /* child */ /* Do not reload config on HUP */ signal(SIGHUP, SIG_IGN); close(server_socket); xmove_fd(n, 0); xdup2(0, 1); handle_incoming_and_exit(&fromAddr); } /* parent, or fork failed */ close(n); } /* while (1) */ /* never reached */ } #else static void mini_httpd_nommu(int server_socket, int argc, char **argv) NORETURN; static void mini_httpd_nommu(int server_socket, int argc, char **argv) { char *argv_copy[argc + 2]; argv_copy[0] = argv[0]; argv_copy[1] = (char*)"-i"; memcpy(&argv_copy[2], &argv[1], argc * sizeof(argv[0])); /* NB: it's best to not use xfuncs in this loop before vfork(). * Otherwise server may die on transient errors (temporary * out-of-memory condition, etc), which is Bad(tm). * Try to do any dangerous calls after fork. */ while (1) { int n; len_and_sockaddr fromAddr; /* Wait for connections... */ fromAddr.len = LSA_SIZEOF_SA; n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len); if (n < 0) continue; /* set the KEEPALIVE option to cull dead connections */ setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); if (vfork() == 0) { /* child */ /* Do not reload config on HUP */ signal(SIGHUP, SIG_IGN); close(server_socket); xmove_fd(n, 0); xdup2(0, 1); /* Run a copy of ourself in inetd mode */ re_exec(argv_copy); } argv_copy[0][0] &= 0x7f; /* parent, or vfork failed */ close(n); } /* while (1) */ /* never reached */ } #endif /* * Process a HTTP connection on stdin/out. * Never returns. */ static void mini_httpd_inetd(void) NORETURN; static void mini_httpd_inetd(void) { len_and_sockaddr fromAddr; memset(&fromAddr, 0, sizeof(fromAddr)); fromAddr.len = LSA_SIZEOF_SA; /* NB: can fail if user runs it by hand and types in http cmds */ getpeername(0, &fromAddr.u.sa, &fromAddr.len); handle_incoming_and_exit(&fromAddr); } static void sighup_handler(int sig UNUSED_PARAM) { parse_conf(DEFAULT_PATH_HTTPD_CONF, SIGNALED_PARSE); } enum { c_opt_config_file = 0, d_opt_decode_url, h_opt_home_httpd, IF_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,) IF_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,) IF_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,) IF_FEATURE_HTTPD_SETUID( u_opt_setuid ,) p_opt_port , p_opt_inetd , p_opt_foreground, p_opt_verbose , OPT_CONFIG_FILE = 1 << c_opt_config_file, OPT_DECODE_URL = 1 << d_opt_decode_url, OPT_HOME_HTTPD = 1 << h_opt_home_httpd, OPT_ENCODE_URL = IF_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0, OPT_REALM = IF_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0, OPT_MD5 = IF_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0, OPT_SETUID = IF_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0, OPT_PORT = 1 << p_opt_port, OPT_INETD = 1 << p_opt_inetd, OPT_FOREGROUND = 1 << p_opt_foreground, OPT_VERBOSE = 1 << p_opt_verbose, }; int httpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int httpd_main(int argc UNUSED_PARAM, char **argv) { int server_socket = server_socket; /* for gcc */ unsigned opt; char *url_for_decode; IF_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;) IF_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;) IF_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;) IF_FEATURE_HTTPD_AUTH_MD5(const char *pass;) INIT_G(); #if ENABLE_LOCALE_SUPPORT /* Undo busybox.c: we want to speak English in http (dates etc) */ setlocale(LC_TIME, "C"); #endif home_httpd = xrealloc_getcwd_or_warn(NULL); /* -v counts, -i implies -f */ opt_complementary = "vv:if"; /* We do not "absolutize" path given by -h (home) opt. * If user gives relative path in -h, * $SCRIPT_FILENAME will not be set. */ opt = getopt32(argv, "c:d:h:" IF_FEATURE_HTTPD_ENCODE_URL_STR("e:") IF_FEATURE_HTTPD_BASIC_AUTH("r:") IF_FEATURE_HTTPD_AUTH_MD5("m:") IF_FEATURE_HTTPD_SETUID("u:") "p:ifv", &opt_c_configFile, &url_for_decode, &home_httpd IF_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode) IF_FEATURE_HTTPD_BASIC_AUTH(, &g_realm) IF_FEATURE_HTTPD_AUTH_MD5(, &pass) IF_FEATURE_HTTPD_SETUID(, &s_ugid) , &bind_addr_or_port , &verbose ); if (opt & OPT_DECODE_URL) { fputs(percent_decode_in_place(url_for_decode, /*strict:*/ 0), stdout); return 0; } #if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR if (opt & OPT_ENCODE_URL) { fputs(encodeString(url_for_encode), stdout); return 0; } #endif #if ENABLE_FEATURE_HTTPD_AUTH_MD5 if (opt & OPT_MD5) { char salt[sizeof("$1$XXXXXXXX")]; salt[0] = '$'; salt[1] = '1'; salt[2] = '$'; crypt_make_salt(salt + 3, 4); puts(pw_encrypt(pass, salt, /*cleanup:*/ 0)); return 0; } #endif #if ENABLE_FEATURE_HTTPD_SETUID if (opt & OPT_SETUID) { xget_uidgid(&ugid, s_ugid); } #endif #if !BB_MMU if (!(opt & OPT_FOREGROUND)) { bb_daemonize_or_rexec(0, argv); /* don't change current directory */ } #endif xchdir(home_httpd); if (!(opt & OPT_INETD)) { signal(SIGCHLD, SIG_IGN); server_socket = openServer(); #if ENABLE_FEATURE_HTTPD_SETUID /* drop privileges */ if (opt & OPT_SETUID) { if (ugid.gid != (gid_t)-1) { if (setgroups(1, &ugid.gid) == -1) bb_perror_msg_and_die("setgroups"); xsetgid(ugid.gid); } xsetuid(ugid.uid); } #endif } #if 0 /* User can do it himself: 'env - PATH="$PATH" httpd' * We don't do it because we don't want to screw users * which want to do * 'env - VAR1=val1 VAR2=val2 httpd' * and have VAR1 and VAR2 values visible in their CGIs. * Besides, it is also smaller. */ { char *p = getenv("PATH"); /* env strings themself are not freed, no need to xstrdup(p): */ clearenv(); if (p) putenv(p - 5); // if (!(opt & OPT_INETD)) // setenv_long("SERVER_PORT", ???); } #endif parse_conf(DEFAULT_PATH_HTTPD_CONF, FIRST_PARSE); if (!(opt & OPT_INETD)) signal(SIGHUP, sighup_handler); xfunc_error_retval = 0; if (opt & OPT_INETD) mini_httpd_inetd(); #if BB_MMU if (!(opt & OPT_FOREGROUND)) bb_daemonize(0); /* don't change current directory */ mini_httpd(server_socket); /* never returns */ #else mini_httpd_nommu(server_socket, argc, argv); /* never returns */ #endif /* return 0; */ } busybox-1.22.1/networking/arp.c0000644000000000000000000003023712263563520015114 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * arp.c - Manipulate the system ARP cache * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Author: Fred N. van Kempen, * Busybox port: Paul van Gool * * modified for getopt32 by Arne Bernin */ //usage:#define arp_trivial_usage //usage: "\n[-vn] [-H HWTYPE] [-i IF] -a [HOSTNAME]" //usage: "\n[-v] [-i IF] -d HOSTNAME [pub]" //usage: "\n[-v] [-H HWTYPE] [-i IF] -s HOSTNAME HWADDR [temp]" //usage: "\n[-v] [-H HWTYPE] [-i IF] -s HOSTNAME HWADDR [netmask MASK] pub" //usage: "\n[-v] [-H HWTYPE] [-i IF] -Ds HOSTNAME IFACE [netmask MASK] pub" //usage:#define arp_full_usage "\n\n" //usage: "Manipulate ARP cache\n" //usage: "\n -a Display (all) hosts" //usage: "\n -d Delete ARP entry" //usage: "\n -s Set new entry" //usage: "\n -v Verbose" //usage: "\n -n Don't resolve names" //usage: "\n -i IF Network interface" //usage: "\n -D Read HWADDR from IFACE" //usage: "\n -A,-p AF Protocol family" //usage: "\n -H HWTYPE Hardware address type" #include "libbb.h" #include "inet_common.h" #include #include #include #include #include #define DEBUG 0 #define DFLT_AF "inet" #define DFLT_HW "ether" enum { ARP_OPT_A = (1 << 0), ARP_OPT_p = (1 << 1), ARP_OPT_H = (1 << 2), ARP_OPT_t = (1 << 3), ARP_OPT_i = (1 << 4), ARP_OPT_a = (1 << 5), ARP_OPT_d = (1 << 6), ARP_OPT_n = (1 << 7), /* do not resolve addresses */ ARP_OPT_D = (1 << 8), /* HW-address is devicename */ ARP_OPT_s = (1 << 9), ARP_OPT_v = (1 << 10) * DEBUG, /* debugging output flag */ }; enum { sockfd = 3, /* active socket descriptor */ }; struct globals { const struct aftype *ap; /* current address family */ const struct hwtype *hw; /* current hardware type */ const char *device; /* current device */ smallint hw_set; /* flag if hw-type was set (-H) */ } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define ap (G.ap ) #define hw (G.hw ) #define device (G.device ) #define hw_set (G.hw_set ) #define INIT_G() do { \ device = ""; \ } while (0) static const char options[] ALIGN1 = "pub\0" "priv\0" "temp\0" "trail\0" "dontpub\0" "auto\0" "dev\0" "netmask\0"; /* Delete an entry from the ARP cache. */ /* Called only from main, once */ static int arp_del(char **args) { char *host; struct arpreq req; struct sockaddr sa; int flags = 0; int err; memset(&req, 0, sizeof(req)); /* Resolve the host name. */ host = *args; if (ap->input(host, &sa) < 0) { bb_herror_msg_and_die("%s", host); } /* If a host has more than one address, use the correct one! */ memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr)); if (hw_set) req.arp_ha.sa_family = hw->type; req.arp_flags = ATF_PERM; args++; while (*args != NULL) { switch (index_in_strings(options, *args)) { case 0: /* "pub" */ flags |= 1; args++; break; case 1: /* "priv" */ flags |= 2; args++; break; case 2: /* "temp" */ req.arp_flags &= ~ATF_PERM; args++; break; case 3: /* "trail" */ req.arp_flags |= ATF_USETRAILERS; args++; break; case 4: /* "dontpub" */ #ifdef HAVE_ATF_DONTPUB req.arp_flags |= ATF_DONTPUB; #else bb_error_msg("feature ATF_DONTPUB is not supported"); #endif args++; break; case 5: /* "auto" */ #ifdef HAVE_ATF_MAGIC req.arp_flags |= ATF_MAGIC; #else bb_error_msg("feature ATF_MAGIC is not supported"); #endif args++; break; case 6: /* "dev" */ if (*++args == NULL) bb_show_usage(); device = *args; args++; break; case 7: /* "netmask" */ if (*++args == NULL) bb_show_usage(); if (strcmp(*args, "255.255.255.255") != 0) { host = *args; if (ap->input(host, &sa) < 0) { bb_herror_msg_and_die("%s", host); } memcpy(&req.arp_netmask, &sa, sizeof(struct sockaddr)); req.arp_flags |= ATF_NETMASK; } args++; break; default: bb_show_usage(); break; } } if (flags == 0) flags = 3; strncpy(req.arp_dev, device, sizeof(req.arp_dev)); err = -1; /* Call the kernel. */ if (flags & 2) { if (option_mask32 & ARP_OPT_v) bb_error_msg("SIOCDARP(nopub)"); err = ioctl(sockfd, SIOCDARP, &req); if (err < 0) { if (errno == ENXIO) { if (flags & 1) goto nopub; printf("No ARP entry for %s\n", host); return -1; } bb_perror_msg_and_die("SIOCDARP(priv)"); } } if ((flags & 1) && err) { nopub: req.arp_flags |= ATF_PUBL; if (option_mask32 & ARP_OPT_v) bb_error_msg("SIOCDARP(pub)"); if (ioctl(sockfd, SIOCDARP, &req) < 0) { if (errno == ENXIO) { printf("No ARP entry for %s\n", host); return -1; } bb_perror_msg_and_die("SIOCDARP(pub)"); } } return 0; } /* Get the hardware address to a specified interface name */ static void arp_getdevhw(char *ifname, struct sockaddr *sa) { struct ifreq ifr; const struct hwtype *xhw; strcpy(ifr.ifr_name, ifname); ioctl_or_perror_and_die(sockfd, SIOCGIFHWADDR, &ifr, "can't get HW-Address for '%s'", ifname); if (hw_set && (ifr.ifr_hwaddr.sa_family != hw->type)) { bb_error_msg_and_die("protocol type mismatch"); } memcpy(sa, &(ifr.ifr_hwaddr), sizeof(struct sockaddr)); if (option_mask32 & ARP_OPT_v) { xhw = get_hwntype(ifr.ifr_hwaddr.sa_family); if (!xhw || !xhw->print) { xhw = get_hwntype(-1); } bb_error_msg("device '%s' has HW address %s '%s'", ifname, xhw->name, xhw->print((unsigned char *) &ifr.ifr_hwaddr.sa_data)); } } /* Set an entry in the ARP cache. */ /* Called only from main, once */ static int arp_set(char **args) { char *host; struct arpreq req; struct sockaddr sa; int flags; memset(&req, 0, sizeof(req)); host = *args++; if (ap->input(host, &sa) < 0) { bb_herror_msg_and_die("%s", host); } /* If a host has more than one address, use the correct one! */ memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr)); /* Fetch the hardware address. */ if (*args == NULL) { bb_error_msg_and_die("need hardware address"); } if (option_mask32 & ARP_OPT_D) { arp_getdevhw(*args++, &req.arp_ha); } else { if (hw->input(*args++, &req.arp_ha) < 0) { bb_error_msg_and_die("invalid hardware address"); } } /* Check out any modifiers. */ flags = ATF_PERM | ATF_COM; while (*args != NULL) { switch (index_in_strings(options, *args)) { case 0: /* "pub" */ flags |= ATF_PUBL; args++; break; case 1: /* "priv" */ flags &= ~ATF_PUBL; args++; break; case 2: /* "temp" */ flags &= ~ATF_PERM; args++; break; case 3: /* "trail" */ flags |= ATF_USETRAILERS; args++; break; case 4: /* "dontpub" */ #ifdef HAVE_ATF_DONTPUB flags |= ATF_DONTPUB; #else bb_error_msg("feature ATF_DONTPUB is not supported"); #endif args++; break; case 5: /* "auto" */ #ifdef HAVE_ATF_MAGIC flags |= ATF_MAGIC; #else bb_error_msg("feature ATF_MAGIC is not supported"); #endif args++; break; case 6: /* "dev" */ if (*++args == NULL) bb_show_usage(); device = *args; args++; break; case 7: /* "netmask" */ if (*++args == NULL) bb_show_usage(); if (strcmp(*args, "255.255.255.255") != 0) { host = *args; if (ap->input(host, &sa) < 0) { bb_herror_msg_and_die("%s", host); } memcpy(&req.arp_netmask, &sa, sizeof(struct sockaddr)); flags |= ATF_NETMASK; } args++; break; default: bb_show_usage(); break; } } /* Fill in the remainder of the request. */ req.arp_flags = flags; strncpy(req.arp_dev, device, sizeof(req.arp_dev)); /* Call the kernel. */ if (option_mask32 & ARP_OPT_v) bb_error_msg("SIOCSARP()"); xioctl(sockfd, SIOCSARP, &req); return 0; } /* Print the contents of an ARP request block. */ static void arp_disp(const char *name, char *ip, int type, int arp_flags, char *hwa, char *mask, char *dev) { static const int arp_masks[] = { ATF_PERM, ATF_PUBL, #ifdef HAVE_ATF_MAGIC ATF_MAGIC, #endif #ifdef HAVE_ATF_DONTPUB ATF_DONTPUB, #endif ATF_USETRAILERS, }; static const char arp_labels[] ALIGN1 = "PERM\0""PUP\0" #ifdef HAVE_ATF_MAGIC "AUTO\0" #endif #ifdef HAVE_ATF_DONTPUB "DONTPUB\0" #endif "TRAIL\0" ; const struct hwtype *xhw; xhw = get_hwntype(type); if (xhw == NULL) xhw = get_hwtype(DFLT_HW); printf("%s (%s) at ", name, ip); if (!(arp_flags & ATF_COM)) { if (arp_flags & ATF_PUBL) printf("* "); else printf(" "); } else { printf("%s [%s] ", hwa, xhw->name); } if (arp_flags & ATF_NETMASK) printf("netmask %s ", mask); print_flags_separated(arp_masks, arp_labels, arp_flags, " "); printf(" on %s\n", dev); } /* Display the contents of the ARP cache in the kernel. */ /* Called only from main, once */ static int arp_show(char *name) { const char *host; const char *hostname; FILE *fp; struct sockaddr sa; int type, flags; int num; unsigned entries = 0, shown = 0; char ip[128]; char hwa[128]; char mask[128]; char line[128]; char dev[128]; host = NULL; if (name != NULL) { /* Resolve the host name. */ if (ap->input(name, &sa) < 0) { bb_herror_msg_and_die("%s", name); } host = xstrdup(ap->sprint(&sa, 1)); } fp = xfopen_for_read("/proc/net/arp"); /* Bypass header -- read one line */ fgets(line, sizeof(line), fp); /* Read the ARP cache entries. */ while (fgets(line, sizeof(line), fp)) { mask[0] = '-'; mask[1] = '\0'; dev[0] = '-'; dev[1] = '\0'; /* All these strings can't overflow * because fgets above reads limited amount of data */ num = sscanf(line, "%s 0x%x 0x%x %s %s %s\n", ip, &type, &flags, hwa, mask, dev); if (num < 4) break; entries++; /* if the user specified hw-type differs, skip it */ if (hw_set && (type != hw->type)) continue; /* if the user specified address differs, skip it */ if (host && strcmp(ip, host) != 0) continue; /* if the user specified device differs, skip it */ if (device[0] && strcmp(dev, device) != 0) continue; shown++; /* This IS ugly but it works -be */ hostname = "?"; if (!(option_mask32 & ARP_OPT_n)) { if (ap->input(ip, &sa) < 0) hostname = ip; else hostname = ap->sprint(&sa, (option_mask32 & ARP_OPT_n) | 0x8000); if (strcmp(hostname, ip) == 0) hostname = "?"; } arp_disp(hostname, ip, type, flags, hwa, mask, dev); } if (option_mask32 & ARP_OPT_v) printf("Entries: %u\tSkipped: %u\tFound: %u\n", entries, entries - shown, shown); if (!shown) { if (hw_set || host || device[0]) printf("No match found in %u entries\n", entries); } if (ENABLE_FEATURE_CLEAN_UP) { free((char*)host); fclose(fp); } return 0; } int arp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int arp_main(int argc UNUSED_PARAM, char **argv) { const char *hw_type; const char *protocol; unsigned opts; INIT_G(); xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sockfd); ap = get_aftype(DFLT_AF); /* Defaults are always supported */ //if (!ap) // bb_error_msg_and_die("%s: %s not supported", DFLT_AF, "address family"); hw = get_hwtype(DFLT_HW); //if (!hw) // bb_error_msg_and_die("%s: %s not supported", DFLT_HW, "hardware type"); opts = getopt32(argv, "A:p:H:t:i:adnDsv", &protocol, &protocol, &hw_type, &hw_type, &device); argv += optind; if (opts & (ARP_OPT_A | ARP_OPT_p)) { ap = get_aftype(protocol); if (!ap) bb_error_msg_and_die("%s: unknown %s", protocol, "address family"); } if (opts & (ARP_OPT_H | ARP_OPT_t)) { hw = get_hwtype(hw_type); if (!hw) bb_error_msg_and_die("%s: unknown %s", hw_type, "hardware type"); hw_set = 1; } //if (opts & ARP_OPT_i)... -i if (ap->af != AF_INET) { bb_error_msg_and_die("%s: kernel only supports 'inet'", ap->name); } if (hw->alen <= 0) { bb_error_msg_and_die("%s: %s without ARP support", hw->name, "hardware type"); } /* Now see what we have to do here... */ if (opts & (ARP_OPT_d | ARP_OPT_s)) { if (argv[0] == NULL) bb_error_msg_and_die("need host name"); if (opts & ARP_OPT_s) return arp_set(argv); return arp_del(argv); } //if (opts & ARP_OPT_a) - default return arp_show(argv[0]); } busybox-1.22.1/networking/traceroute.c0000644000000000000000000011254212263563520016507 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000 * The Regents of the University of California. All rights reserved. * * Busybox port by Vladimir Oleynik (C) 2005 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ /* * traceroute6 * * Modified for NRL 4.4BSD IPv6 release. * 07/31/96 bgp * * Modified for Linux IPv6 by Pedro Roque * 31/07/1996 * * As ICMP error messages for IPv6 now include more than 8 bytes * UDP datagrams are now sent via an UDP socket instead of magic * RAW socket tricks. * * Converted to busybox applet by Leonid Lisovskiy * 2009-11-16 */ /* * traceroute host - trace the route ip packets follow going to "host". * * Attempt to trace the route an ip packet would follow to some * internet host. We find out intermediate hops by launching probe * packets with a small ttl (time to live) then listening for an * icmp "time exceeded" reply from a gateway. We start our probes * with a ttl of one and increase by one until we get an icmp "port * unreachable" (which means we got to "host") or hit a max (which * defaults to 30 hops & can be changed with the -m flag). Three * probes (change with -q flag) are sent at each ttl setting and a * line is printed showing the ttl, address of the gateway and * round trip time of each probe. If the probe answers come from * different gateways, the address of each responding system will * be printed. If there is no response within a 5 sec. timeout * interval (changed with the -w flag), a "*" is printed for that * probe. * * Probe packets are UDP format. We don't want the destination * host to process them so the destination port is set to an * unlikely value (if some clod on the destination is using that * value, it can be changed with the -p flag). * * A sample use might be: * * [yak 71]% traceroute nis.nsf.net. * traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms * * Note that lines 2 & 3 are the same. This is due to a buggy * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards * packets with a zero ttl. * * A more interesting example is: * * [yak 72]% traceroute allspice.lcs.mit.edu. * traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms * 12 * * * * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms * 14 * * * * 15 * * * * 16 * * * * 17 * * * * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms * * (I start to see why I'm having so much trouble with mail to * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away * either don't send ICMP "time exceeded" messages or send them * with a ttl too small to reach us. 14 - 17 are running the * MIT C Gateway code that doesn't send "time exceeded"s. God * only knows what's going on with 12. * * The silent gateway 12 in the above may be the result of a bug in * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3) * sends an unreachable message using whatever ttl remains in the * original datagram. Since, for gateways, the remaining ttl is * zero, the icmp "time exceeded" is guaranteed to not make it back * to us. The behavior of this bug is slightly more interesting * when it appears on the destination system: * * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms * 7 * * * * 8 * * * * 9 * * * * 10 * * * * 11 * * * * 12 * * * * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms ! * * Notice that there are 12 "gateways" (13 is the final * destination) and exactly the last half of them are "missing". * What's really happening is that rip (a Sun-3 running Sun OS3.5) * is using the ttl from our arriving datagram as the ttl in its * icmp reply. So, the reply will time out on the return path * (with no notice sent to anyone since icmp's aren't sent for * icmp's) until we probe with a ttl that's at least twice the path * length. I.e., rip is really only 7 hops away. A reply that * returns with a ttl of 1 is a clue this problem exists. * Traceroute prints a "!" after the time if the ttl is <= 1. * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or * non-standard (HPUX) software, expect to see this problem * frequently and/or take care picking the target host of your * probes. * * Other possible annotations after the time are !H, !N, !P (got a host, * network or protocol unreachable, respectively), !S or !F (source * route failed or fragmentation needed -- neither of these should * ever occur and the associated gateway is busted if you see one). If * almost all the probes result in some kind of unreachable, traceroute * will give up and exit. * * Notes * ----- * This program must be run by root or be setuid. (I suggest that * you *don't* make it setuid -- casual use could result in a lot * of unnecessary traffic on our poor, congested nets.) * * This program requires a kernel mod that does not appear in any * system available from Berkeley: A raw ip socket using proto * IPPROTO_RAW must interpret the data sent as an ip datagram (as * opposed to data to be wrapped in a ip datagram). See the README * file that came with the source to this program for a description * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE * MODIFIED TO RUN THIS PROGRAM. * * The udp port usage may appear bizarre (well, ok, it is bizarre). * The problem is that an icmp message only contains 8 bytes of * data from the original datagram. 8 bytes is the size of a udp * header so, if we want to associate replies with the original * datagram, the necessary information must be encoded into the * udp header (the ip id could be used but there's no way to * interlock with the kernel's assignment of ip id's and, anyway, * it would have taken a lot more kernel hacking to allow this * code to set the ip id). So, to allow two or more users to * use traceroute simultaneously, we use this task's pid as the * source port (the high bit is set to move the port number out * of the "likely" range). To keep track of which probe is being * replied to (so times and/or hop counts don't get confused by a * reply that was delayed in transit), we increment the destination * port number before each probe. * * Don't use this as a coding example. I was trying to find a * routing problem and this code sort-of popped out after 48 hours * without sleep. I was amazed it ever compiled, much less ran. * * I stole the idea for this program from Steve Deering. Since * the first release, I've learned that had I attended the right * IETF working group meetings, I also could have stolen it from Guy * Almes or Matt Mathis. I don't know (or care) who came up with * the idea first. I envy the originators' perspicacity and I'm * glad they didn't keep the idea a secret. * * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or * enhancements to the original distribution. * * I've hacked up a round-trip-route version of this that works by * sending a loose-source-routed udp datagram through the destination * back to yourself. Unfortunately, SO many gateways botch source * routing, the thing is almost worthless. Maybe one day... * * -- Van Jacobson (van@ee.lbl.gov) * Tue Dec 20 03:50:13 PST 1988 */ //usage:#define traceroute_trivial_usage //usage: "[-"IF_TRACEROUTE6("46")"FIldnrv] [-f 1ST_TTL] [-m MAXTTL] [-p PORT] [-q PROBES]\n" //usage: " [-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-g GATEWAY] [-i IFACE]\n" //usage: " [-z PAUSE_MSEC] HOST [BYTES]" //usage:#define traceroute_full_usage "\n\n" //usage: "Trace the route to HOST\n" //usage: IF_TRACEROUTE6( //usage: "\n -4,-6 Force IP or IPv6 name resolution" //usage: ) //usage: "\n -F Set the don't fragment bit" //usage: "\n -I Use ICMP ECHO instead of UDP datagrams" //usage: "\n -l Display the TTL value of the returned packet" //usage: "\n -d Set SO_DEBUG options to socket" //usage: "\n -n Print numeric addresses" //usage: "\n -r Bypass routing tables, send directly to HOST" //usage: "\n -v Verbose" //usage: "\n -m Max time-to-live (max number of hops)" //usage: "\n -p Base UDP port number used in probes" //usage: "\n (default 33434)" //usage: "\n -q Number of probes per TTL (default 3)" //usage: "\n -s IP address to use as the source address" //usage: "\n -t Type-of-service in probe packets (default 0)" //usage: "\n -w Time in seconds to wait for a response (default 3)" //usage: "\n -g Loose source route gateway (8 max)" //usage: //usage:#define traceroute6_trivial_usage //usage: "[-dnrv] [-m MAXTTL] [-p PORT] [-q PROBES]\n" //usage: " [-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-i IFACE]\n" //usage: " HOST [BYTES]" //usage:#define traceroute6_full_usage "\n\n" //usage: "Trace the route to HOST\n" //usage: "\n -d Set SO_DEBUG options to socket" //usage: "\n -n Print numeric addresses" //usage: "\n -r Bypass routing tables, send directly to HOST" //usage: "\n -v Verbose" //usage: "\n -m Max time-to-live (max number of hops)" //usage: "\n -p Base UDP port number used in probes" //usage: "\n (default is 33434)" //usage: "\n -q Number of probes per TTL (default 3)" //usage: "\n -s IP address to use as the source address" //usage: "\n -t Type-of-service in probe packets (default 0)" //usage: "\n -w Time in seconds to wait for a response (default 3)" #define TRACEROUTE_SO_DEBUG 0 /* TODO: undefs were uncommented - ??! we have config system for that! */ /* probably ok to remove altogether */ //#undef CONFIG_FEATURE_TRACEROUTE_VERBOSE //#define CONFIG_FEATURE_TRACEROUTE_VERBOSE //#undef CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE //#define CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE //#undef CONFIG_FEATURE_TRACEROUTE_USE_ICMP //#define CONFIG_FEATURE_TRACEROUTE_USE_ICMP #include #include #include #include #include #include #if ENABLE_FEATURE_IPV6 # include # include # ifndef SOL_IPV6 # define SOL_IPV6 IPPROTO_IPV6 # endif #endif #include "libbb.h" #include "inet_common.h" #ifndef IPPROTO_ICMP # define IPPROTO_ICMP 1 #endif #ifndef IPPROTO_IP # define IPPROTO_IP 0 #endif #define OPT_STRING \ "FIlnrdvxt:i:m:p:q:s:w:z:f:" \ IF_FEATURE_TRACEROUTE_SOURCE_ROUTE("g:") \ "4" IF_TRACEROUTE6("6") enum { OPT_DONT_FRAGMNT = (1 << 0), /* F */ OPT_USE_ICMP = (1 << 1) * ENABLE_FEATURE_TRACEROUTE_USE_ICMP, /* I */ OPT_TTL_FLAG = (1 << 2), /* l */ OPT_ADDR_NUM = (1 << 3), /* n */ OPT_BYPASS_ROUTE = (1 << 4), /* r */ OPT_DEBUG = (1 << 5), /* d */ OPT_VERBOSE = (1 << 6) * ENABLE_FEATURE_TRACEROUTE_VERBOSE, /* v */ OPT_IP_CHKSUM = (1 << 7), /* x */ OPT_TOS = (1 << 8), /* t */ OPT_DEVICE = (1 << 9), /* i */ OPT_MAX_TTL = (1 << 10), /* m */ OPT_PORT = (1 << 11), /* p */ OPT_NPROBES = (1 << 12), /* q */ OPT_SOURCE = (1 << 13), /* s */ OPT_WAITTIME = (1 << 14), /* w */ OPT_PAUSE_MS = (1 << 15), /* z */ OPT_FIRST_TTL = (1 << 16), /* f */ OPT_SOURCE_ROUTE = (1 << 17) * ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE, /* g */ OPT_IPV4 = (1 << (17+ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE)), /* 4 */ OPT_IPV6 = (1 << (18+ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE)) * ENABLE_TRACEROUTE6, /* 6 */ }; #define verbose (option_mask32 & OPT_VERBOSE) enum { SIZEOF_ICMP_HDR = 8, rcvsock = 3, /* receive (icmp) socket file descriptor */ sndsock = 4, /* send (udp/icmp) socket file descriptor */ }; /* Data section of the probe packet */ struct outdata_t { unsigned char seq; /* sequence number of this packet */ unsigned char ttl; /* ttl packet left with */ // UNUSED. Retaining to have the same packet size. struct timeval tv_UNUSED PACKED; /* time packet left */ }; #if ENABLE_TRACEROUTE6 struct outdata6_t { uint32_t ident6; uint32_t seq6; struct timeval tv_UNUSED PACKED; /* time packet left */ }; #endif struct globals { struct ip *outip; struct outdata_t *outdata; len_and_sockaddr *dest_lsa; int packlen; /* total length of packet */ int pmtu; /* Path MTU Discovery (RFC1191) */ uint32_t ident; uint16_t port; // 32768 + 666; /* start udp dest port # for probe packets */ int waittime; // 5; /* time to wait for response (in seconds) */ #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE int optlen; /* length of ip options */ #else #define optlen 0 #endif unsigned char recv_pkt[512]; /* last inbound (icmp) packet */ #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE /* Maximum number of gateways (include room for one noop) */ #define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(uint32_t))) /* loose source route gateway list (including room for final destination) */ uint32_t gwlist[NGATEWAYS + 1]; #endif }; #define G (*ptr_to_globals) #define outip (G.outip ) #define outdata (G.outdata ) #define dest_lsa (G.dest_lsa ) #define packlen (G.packlen ) #define pmtu (G.pmtu ) #define ident (G.ident ) #define port (G.port ) #define waittime (G.waittime ) #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE # define optlen (G.optlen ) #endif #define recv_pkt (G.recv_pkt ) #define gwlist (G.gwlist ) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ port = 32768 + 666; \ waittime = 5; \ } while (0) #define outicmp ((struct icmp *)(outip + 1)) #define outudp ((struct udphdr *)(outip + 1)) /* libbb candidate? tftp uses this idiom too */ static len_and_sockaddr* dup_sockaddr(const len_and_sockaddr *lsa) { len_and_sockaddr *new_lsa = xzalloc(LSA_LEN_SIZE + lsa->len); memcpy(new_lsa, lsa, LSA_LEN_SIZE + lsa->len); return new_lsa; } static int wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to, unsigned *timestamp_us, int *left_ms) { struct pollfd pfd[1]; int read_len = 0; pfd[0].fd = rcvsock; pfd[0].events = POLLIN; if (*left_ms >= 0 && safe_poll(pfd, 1, *left_ms) > 0) { unsigned t; read_len = recv_from_to(rcvsock, recv_pkt, sizeof(recv_pkt), /*flags:*/ MSG_DONTWAIT, &from_lsa->u.sa, to, from_lsa->len); t = monotonic_us(); *left_ms -= (t - *timestamp_us) / 1000; *timestamp_us = t; } return read_len; } static void send_probe(int seq, int ttl) { int len, res; void *out; /* Payload */ #if ENABLE_TRACEROUTE6 if (dest_lsa->u.sa.sa_family == AF_INET6) { struct outdata6_t *pkt = (struct outdata6_t *) outip; pkt->ident6 = htonl(ident); pkt->seq6 = htonl(seq); /*gettimeofday(&pkt->tv, &tz);*/ } else #endif { outdata->seq = seq; outdata->ttl = ttl; // UNUSED: was storing gettimeofday's result there, but never ever checked it /*memcpy(&outdata->tv, tp, sizeof(outdata->tv));*/ if (option_mask32 & OPT_USE_ICMP) { outicmp->icmp_seq = htons(seq); /* Always calculate checksum for icmp packets */ outicmp->icmp_cksum = 0; outicmp->icmp_cksum = inet_cksum((uint16_t *)outicmp, packlen - (sizeof(*outip) + optlen)); if (outicmp->icmp_cksum == 0) outicmp->icmp_cksum = 0xffff; } } //BUG! verbose is (x & OPT_VERBOSE), not a counter! #if 0 //ENABLE_FEATURE_TRACEROUTE_VERBOSE /* XXX undocumented debugging hack */ if (verbose > 1) { const uint16_t *sp; int nshorts, i; sp = (uint16_t *)outip; nshorts = (unsigned)packlen / sizeof(uint16_t); i = 0; printf("[ %d bytes", packlen); while (--nshorts >= 0) { if ((i++ % 8) == 0) printf("\n\t"); printf(" %04x", ntohs(*sp)); sp++; } if (packlen & 1) { if ((i % 8) == 0) printf("\n\t"); printf(" %02x", *(unsigned char *)sp); } printf("]\n"); } #endif #if ENABLE_TRACEROUTE6 if (dest_lsa->u.sa.sa_family == AF_INET6) { res = setsockopt(sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); if (res < 0) bb_perror_msg_and_die("setsockopt UNICAST_HOPS %d", ttl); out = outip; len = packlen; } else #endif { #if defined IP_TTL res = setsockopt(sndsock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); if (res < 0) bb_perror_msg_and_die("setsockopt ttl %d", ttl); #endif out = outicmp; len = packlen - sizeof(*outip); if (!(option_mask32 & OPT_USE_ICMP)) { out = outdata; len -= sizeof(*outudp); set_nport(&dest_lsa->u.sa, htons(port + seq)); } } res = xsendto(sndsock, out, len, &dest_lsa->u.sa, dest_lsa->len); if (res != len) bb_info_msg("sent %d octets, ret=%d", len, res); } #if ENABLE_FEATURE_TRACEROUTE_VERBOSE /* * Convert an ICMP "type" field to a printable string. */ static const char * pr_type(unsigned char t) { static const char *const ttab[] = { "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable", "Source Quench", "Redirect", "ICMP 6", "ICMP 7", "Echo", "Router Advert", "Router Solicit", "Time Exceeded", "Param Problem", "Timestamp", "Timestamp Reply", "Info Request", "Info Reply", "Mask Request", "Mask Reply" }; # if ENABLE_TRACEROUTE6 static const char *const ttab6[] = { [0] "Error", "Dest Unreachable", "Packet Too Big", "Time Exceeded", [4] "Param Problem", [8] "Echo Request", "Echo Reply", "Membership Query", "Membership Report", [12] "Membership Reduction", "Router Solicit", "Router Advert", "Neighbor Solicit", [16] "Neighbor Advert", "Redirect", }; if (dest_lsa->u.sa.sa_family == AF_INET6) { if (t < 5) return ttab6[t]; if (t < 128 || t > ND_REDIRECT) return "OUT-OF-RANGE"; return ttab6[(t & 63) + 8]; } # endif if (t >= ARRAY_SIZE(ttab)) return "OUT-OF-RANGE"; return ttab[t]; } #endif #if !ENABLE_FEATURE_TRACEROUTE_VERBOSE #define packet4_ok(read_len, from, seq) \ packet4_ok(read_len, seq) #endif static int packet4_ok(int read_len, const struct sockaddr_in *from, int seq) { const struct icmp *icp; unsigned char type, code; int hlen; const struct ip *ip; ip = (struct ip *) recv_pkt; hlen = ip->ip_hl << 2; if (read_len < hlen + ICMP_MINLEN) { #if ENABLE_FEATURE_TRACEROUTE_VERBOSE if (verbose) printf("packet too short (%d bytes) from %s\n", read_len, inet_ntoa(from->sin_addr)); #endif return 0; } read_len -= hlen; icp = (struct icmp *)(recv_pkt + hlen); type = icp->icmp_type; code = icp->icmp_code; /* Path MTU Discovery (RFC1191) */ pmtu = 0; if (code == ICMP_UNREACH_NEEDFRAG) pmtu = ntohs(icp->icmp_nextmtu); if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) || type == ICMP_UNREACH || type == ICMP_ECHOREPLY ) { const struct ip *hip; const struct udphdr *up; hip = &icp->icmp_ip; hlen = hip->ip_hl << 2; if (option_mask32 & OPT_USE_ICMP) { struct icmp *hicmp; /* XXX */ if (type == ICMP_ECHOREPLY && icp->icmp_id == htons(ident) && icp->icmp_seq == htons(seq) ) { return ICMP_UNREACH_PORT+1; } hicmp = (struct icmp *)((unsigned char *)hip + hlen); if (hlen + SIZEOF_ICMP_HDR <= read_len && hip->ip_p == IPPROTO_ICMP && hicmp->icmp_id == htons(ident) && hicmp->icmp_seq == htons(seq) ) { return (type == ICMP_TIMXCEED ? -1 : code + 1); } } else { up = (struct udphdr *)((char *)hip + hlen); if (hlen + 12 <= read_len && hip->ip_p == IPPROTO_UDP // Off: since we do not form the entire IP packet, // but defer it to kernel, we can't set source port, // and thus can't check it here in the reply /* && up->source == htons(ident) */ && up->dest == htons(port + seq) ) { return (type == ICMP_TIMXCEED ? -1 : code + 1); } } } #if ENABLE_FEATURE_TRACEROUTE_VERBOSE if (verbose) { int i; uint32_t *lp = (uint32_t *)&icp->icmp_ip; printf("\n%d bytes from %s to " "%s: icmp type %d (%s) code %d\n", read_len, inet_ntoa(from->sin_addr), inet_ntoa(ip->ip_dst), type, pr_type(type), icp->icmp_code); for (i = 4; i < read_len; i += sizeof(*lp)) printf("%2d: x%8.8x\n", i, *lp++); } #endif return 0; } #if ENABLE_TRACEROUTE6 # if !ENABLE_FEATURE_TRACEROUTE_VERBOSE #define packet_ok(read_len, from_lsa, to, seq) \ packet_ok(read_len, from_lsa, seq) # endif static int packet_ok(int read_len, len_and_sockaddr *from_lsa, struct sockaddr *to, int seq) { const struct icmp6_hdr *icp; unsigned char type, code; if (from_lsa->u.sa.sa_family == AF_INET) return packet4_ok(read_len, &from_lsa->u.sin, seq); icp = (struct icmp6_hdr *) recv_pkt; type = icp->icmp6_type; code = icp->icmp6_code; if ((type == ICMP6_TIME_EXCEEDED && code == ICMP6_TIME_EXCEED_TRANSIT) || type == ICMP6_DST_UNREACH ) { struct ip6_hdr *hip; struct udphdr *up; int nexthdr; hip = (struct ip6_hdr *)(icp + 1); up = (struct udphdr *) (hip + 1); nexthdr = hip->ip6_nxt; if (nexthdr == IPPROTO_FRAGMENT) { nexthdr = *(unsigned char*)up; up++; } if (nexthdr == IPPROTO_UDP) { struct outdata6_t *pkt; pkt = (struct outdata6_t *) (up + 1); if (ntohl(pkt->ident6) == ident && ntohl(pkt->seq6) == seq ) { return (type == ICMP6_TIME_EXCEEDED ? -1 : (code<<8)+1); } } } # if ENABLE_FEATURE_TRACEROUTE_VERBOSE if (verbose) { unsigned char *p; char pa1[MAXHOSTNAMELEN]; char pa2[MAXHOSTNAMELEN]; int i; p = (unsigned char *) (icp + 1); printf("\n%d bytes from %s to " "%s: icmp type %d (%s) code %d\n", read_len, inet_ntop(AF_INET6, &from_lsa->u.sin6.sin6_addr, pa1, sizeof(pa1)), inet_ntop(AF_INET6, &((struct sockaddr_in6*)to)->sin6_addr, pa2, sizeof(pa2)), type, pr_type(type), icp->icmp6_code); read_len -= sizeof(struct icmp6_hdr); for (i = 0; i < read_len; i++) { if (i % 16 == 0) printf("%04x:", i); if (i % 4 == 0) bb_putchar(' '); printf("%02x", p[i]); if ((i % 16 == 15) && (i + 1 < read_len)) bb_putchar('\n'); } bb_putchar('\n'); } # endif return 0; } #else /* !ENABLE_TRACEROUTE6 */ static ALWAYS_INLINE int packet_ok(int read_len, len_and_sockaddr *from_lsa IF_NOT_FEATURE_TRACEROUTE_VERBOSE(UNUSED_PARAM), struct sockaddr *to UNUSED_PARAM, int seq) { return packet4_ok(read_len, &from_lsa->u.sin, seq); } #endif /* * Construct an Internet address representation. * If the -n flag has been supplied, give * numeric value, otherwise try for symbolic name. */ static void print_inetname(const struct sockaddr *from) { char *ina = xmalloc_sockaddr2dotted_noport(from); if (option_mask32 & OPT_ADDR_NUM) { printf(" %s", ina); } else { char *n = NULL; if (from->sa_family != AF_INET || ((struct sockaddr_in*)from)->sin_addr.s_addr != INADDR_ANY ) { /* Try to reverse resolve if it is not 0.0.0.0 */ n = xmalloc_sockaddr2host_noport((struct sockaddr*)from); } printf(" %s (%s)", (n ? n : ina), ina); free(n); } free(ina); } static void print(int read_len, const struct sockaddr *from, const struct sockaddr *to) { print_inetname(from); if (verbose) { char *ina = xmalloc_sockaddr2dotted_noport(to); #if ENABLE_TRACEROUTE6 if (to->sa_family == AF_INET6) { read_len -= sizeof(struct ip6_hdr); } else #endif { struct ip *ip4packet = (struct ip*)recv_pkt; read_len -= ip4packet->ip_hl << 2; } printf(" %d bytes to %s", read_len, ina); free(ina); } } static void print_delta_ms(unsigned t1p, unsigned t2p) { unsigned tt = t2p - t1p; printf(" %u.%03u ms", tt / 1000, tt % 1000); } /* * Usage: [-dFIlnrvx] [-g gateway] [-i iface] [-f first_ttl] * [-m max_ttl] [ -p port] [-q nqueries] [-s src_addr] [-t tos] * [-w waittime] [-z pausemsecs] host [packetlen]" */ static int common_traceroute_main(int op, char **argv) { int minpacket; #ifdef IP_TOS int tos = 0; #endif int max_ttl = 30; int nprobes = 3; int first_ttl = 1; unsigned pausemsecs = 0; char *source; char *device; char *tos_str; char *max_ttl_str; char *port_str; char *nprobes_str; char *waittime_str; char *pausemsecs_str; char *first_ttl_str; char *dest_str; #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE llist_t *source_route_list = NULL; int lsrr = 0; #endif #if ENABLE_TRACEROUTE6 sa_family_t af; #else enum { af = AF_INET }; #endif int ttl; int seq; len_and_sockaddr *from_lsa; struct sockaddr *lastaddr; struct sockaddr *to; INIT_G(); /* minimum 1 arg */ opt_complementary = "-1:x-x" IF_FEATURE_TRACEROUTE_SOURCE_ROUTE(":g::"); op |= getopt32(argv, OPT_STRING , &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str , &source, &waittime_str, &pausemsecs_str, &first_ttl_str #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE , &source_route_list #endif ); argv += optind; #if 0 /* IGNORED */ if (op & OPT_IP_CHKSUM) bb_error_msg("warning: ip checksums disabled"); #endif #ifdef IP_TOS if (op & OPT_TOS) tos = xatou_range(tos_str, 0, 255); #endif if (op & OPT_MAX_TTL) max_ttl = xatou_range(max_ttl_str, 1, 255); if (op & OPT_PORT) port = xatou16(port_str); if (op & OPT_NPROBES) nprobes = xatou_range(nprobes_str, 1, INT_MAX); if (op & OPT_SOURCE) { /* * set the ip source address of the outbound * probe (e.g., on a multi-homed host). */ if (getuid() != 0) bb_error_msg_and_die(bb_msg_you_must_be_root); } if (op & OPT_WAITTIME) waittime = xatou_range(waittime_str, 1, 24 * 60 * 60); if (op & OPT_PAUSE_MS) pausemsecs = xatou_range(pausemsecs_str, 0, 60 * 60 * 1000); if (op & OPT_FIRST_TTL) first_ttl = xatou_range(first_ttl_str, 1, max_ttl); #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE if (source_route_list) { while (source_route_list) { len_and_sockaddr *lsa; if (lsrr >= NGATEWAYS) bb_error_msg_and_die("no more than %d gateways", NGATEWAYS); lsa = xhost_and_af2sockaddr(llist_pop(&source_route_list), 0, AF_INET); gwlist[lsrr] = lsa->u.sin.sin_addr.s_addr; free(lsa); ++lsrr; } optlen = (lsrr + 1) * sizeof(gwlist[0]); } #endif /* Process destination and optional packet size */ minpacket = sizeof(*outip) + SIZEOF_ICMP_HDR + sizeof(*outdata) + optlen; if (!(op & OPT_USE_ICMP)) minpacket += sizeof(*outudp) - SIZEOF_ICMP_HDR; #if ENABLE_TRACEROUTE6 af = AF_UNSPEC; if (op & OPT_IPV4) af = AF_INET; if (op & OPT_IPV6) af = AF_INET6; dest_lsa = xhost_and_af2sockaddr(argv[0], port, af); af = dest_lsa->u.sa.sa_family; if (af == AF_INET6) minpacket = sizeof(struct outdata6_t); #else dest_lsa = xhost2sockaddr(argv[0], port); #endif packlen = minpacket; if (argv[1]) packlen = xatoul_range(argv[1], minpacket, 32 * 1024); /* Ensure the socket fds won't be 0, 1 or 2 */ bb_sanitize_stdio(); #if ENABLE_TRACEROUTE6 if (af == AF_INET6) { xmove_fd(xsocket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6), rcvsock); # ifdef IPV6_RECVPKTINFO setsockopt(rcvsock, SOL_IPV6, IPV6_RECVPKTINFO, &const_int_1, sizeof(const_int_1)); setsockopt(rcvsock, SOL_IPV6, IPV6_2292PKTINFO, &const_int_1, sizeof(const_int_1)); # else setsockopt(rcvsock, SOL_IPV6, IPV6_PKTINFO, &const_int_1, sizeof(const_int_1)); # endif } else #endif { xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), rcvsock); } #if TRACEROUTE_SO_DEBUG if (op & OPT_DEBUG) setsockopt(rcvsock, SOL_SOCKET, SO_DEBUG, &const_int_1, sizeof(const_int_1)); #endif if (op & OPT_BYPASS_ROUTE) setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE, &const_int_1, sizeof(const_int_1)); #if ENABLE_TRACEROUTE6 if (af == AF_INET6) { static const int two = 2; if (setsockopt(rcvsock, SOL_RAW, IPV6_CHECKSUM, &two, sizeof(two)) < 0) bb_perror_msg_and_die("setsockopt RAW_CHECKSUM"); xmove_fd(xsocket(af, SOCK_DGRAM, 0), sndsock); } else #endif { if (op & OPT_USE_ICMP) xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), sndsock); else xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sndsock); #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE && defined IP_OPTIONS if (lsrr > 0) { unsigned char optlist[MAX_IPOPTLEN]; unsigned size; /* final hop */ gwlist[lsrr] = dest_lsa->u.sin.sin_addr.s_addr; ++lsrr; /* force 4 byte alignment */ optlist[0] = IPOPT_NOP; /* loose source route option */ optlist[1] = IPOPT_LSRR; size = lsrr * sizeof(gwlist[0]); optlist[2] = size + 3; /* pointer to LSRR addresses */ optlist[3] = IPOPT_MINOFF; memcpy(optlist + 4, gwlist, size); if (setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS, (char *)optlist, size + sizeof(gwlist[0])) < 0) { bb_perror_msg_and_die("IP_OPTIONS"); } } #endif } #ifdef SO_SNDBUF if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(packlen)) < 0) { bb_perror_msg_and_die("SO_SNDBUF"); } #endif #ifdef IP_TOS if ((op & OPT_TOS) && setsockopt(sndsock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) { bb_perror_msg_and_die("setsockopt tos %d", tos); } #endif #ifdef IP_DONTFRAG if (op & OPT_DONT_FRAGMNT) setsockopt(sndsock, IPPROTO_IP, IP_DONTFRAG, &const_int_1, sizeof(const_int_1)); #endif #if TRACEROUTE_SO_DEBUG if (op & OPT_DEBUG) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, &const_int_1, sizeof(const_int_1)); #endif if (op & OPT_BYPASS_ROUTE) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, &const_int_1, sizeof(const_int_1)); outip = xzalloc(packlen); ident = getpid(); if (af == AF_INET) { if (op & OPT_USE_ICMP) { ident |= 0x8000; outicmp->icmp_type = ICMP_ECHO; outicmp->icmp_id = htons(ident); outdata = (struct outdata_t *)((char *)outicmp + SIZEOF_ICMP_HDR); } else { outdata = (struct outdata_t *)(outudp + 1); } } if (op & OPT_DEVICE) /* hmm, do we need error check? */ setsockopt_bindtodevice(sndsock, device); if (op & OPT_SOURCE) { #if ENABLE_TRACEROUTE6 // TODO: need xdotted_and_af2sockaddr? len_and_sockaddr *source_lsa = xhost_and_af2sockaddr(source, 0, af); #else len_and_sockaddr *source_lsa = xdotted2sockaddr(source, 0); #endif /* Ping4 does this (why?) */ if (af == AF_INET) if (setsockopt(sndsock, IPPROTO_IP, IP_MULTICAST_IF, &source_lsa->u.sa, source_lsa->len)) bb_error_msg_and_die("can't set multicast source interface"); //TODO: we can query source port we bound to, // and check it in replies... if we care enough xbind(sndsock, &source_lsa->u.sa, source_lsa->len); free(source_lsa); } #if ENABLE_TRACEROUTE6 else if (af == AF_INET6) { //TODO: why we don't do it for IPv4? len_and_sockaddr *source_lsa; int probe_fd = xsocket(af, SOCK_DGRAM, 0); if (op & OPT_DEVICE) setsockopt_bindtodevice(probe_fd, device); set_nport(&dest_lsa->u.sa, htons(1025)); /* dummy connect. makes kernel pick source IP (and port) */ xconnect(probe_fd, &dest_lsa->u.sa, dest_lsa->len); set_nport(&dest_lsa->u.sa, htons(port)); /* read IP and port */ source_lsa = get_sock_lsa(probe_fd); if (source_lsa == NULL) bb_error_msg_and_die("can't get probe addr"); close(probe_fd); /* bind our sockets to this IP (but not port) */ set_nport(&source_lsa->u.sa, 0); xbind(sndsock, &source_lsa->u.sa, source_lsa->len); xbind(rcvsock, &source_lsa->u.sa, source_lsa->len); free(source_lsa); } #endif /* Revert to non-privileged user after opening sockets */ xsetgid(getgid()); xsetuid(getuid()); dest_str = xmalloc_sockaddr2dotted_noport(&dest_lsa->u.sa); printf("traceroute to %s (%s)", argv[0], dest_str); if (ENABLE_FEATURE_CLEAN_UP) { free(dest_str); } if (op & OPT_SOURCE) printf(" from %s", source); printf(", %d hops max, %d byte packets\n", max_ttl, packlen); from_lsa = dup_sockaddr(dest_lsa); lastaddr = xzalloc(dest_lsa->len); to = xzalloc(dest_lsa->len); seq = 0; for (ttl = first_ttl; ttl <= max_ttl; ++ttl) { int probe; int unreachable = 0; /* counter */ int gotlastaddr = 0; /* flags */ int got_there = 0; printf("%2d", ttl); for (probe = 0; probe < nprobes; ++probe) { int read_len; unsigned t1; unsigned t2; int left_ms; struct ip *ip; fflush_all(); if (probe != 0 && pausemsecs > 0) usleep(pausemsecs * 1000); send_probe(++seq, ttl); t2 = t1 = monotonic_us(); left_ms = waittime * 1000; while ((read_len = wait_for_reply(from_lsa, to, &t2, &left_ms)) != 0) { int icmp_code; /* Recv'ed a packet, or read error */ /* t2 = monotonic_us() - set by wait_for_reply */ if (read_len < 0) continue; icmp_code = packet_ok(read_len, from_lsa, to, seq); /* Skip short packet */ if (icmp_code == 0) continue; if (!gotlastaddr || (memcmp(lastaddr, &from_lsa->u.sa, from_lsa->len) != 0) ) { print(read_len, &from_lsa->u.sa, to); memcpy(lastaddr, &from_lsa->u.sa, from_lsa->len); gotlastaddr = 1; } print_delta_ms(t1, t2); ip = (struct ip *)recv_pkt; if (from_lsa->u.sa.sa_family == AF_INET) if (op & OPT_TTL_FLAG) printf(" (%d)", ip->ip_ttl); /* time exceeded in transit */ if (icmp_code == -1) break; icmp_code--; switch (icmp_code) { #if ENABLE_TRACEROUTE6 case ICMP6_DST_UNREACH_NOPORT << 8: got_there = 1; break; #endif case ICMP_UNREACH_PORT: if (ip->ip_ttl <= 1) printf(" !"); got_there = 1; break; case ICMP_UNREACH_NET: #if ENABLE_TRACEROUTE6 && (ICMP6_DST_UNREACH_NOROUTE != ICMP_UNREACH_NET) case ICMP6_DST_UNREACH_NOROUTE << 8: #endif printf(" !N"); ++unreachable; break; case ICMP_UNREACH_HOST: #if ENABLE_TRACEROUTE6 case ICMP6_DST_UNREACH_ADDR << 8: #endif printf(" !H"); ++unreachable; break; case ICMP_UNREACH_PROTOCOL: printf(" !P"); got_there = 1; break; case ICMP_UNREACH_NEEDFRAG: printf(" !F-%d", pmtu); ++unreachable; break; case ICMP_UNREACH_SRCFAIL: #if ENABLE_TRACEROUTE6 case ICMP6_DST_UNREACH_ADMIN << 8: #endif printf(" !S"); ++unreachable; break; case ICMP_UNREACH_FILTER_PROHIB: case ICMP_UNREACH_NET_PROHIB: /* misuse */ printf(" !A"); ++unreachable; break; case ICMP_UNREACH_HOST_PROHIB: printf(" !C"); ++unreachable; break; case ICMP_UNREACH_HOST_PRECEDENCE: printf(" !V"); ++unreachable; break; case ICMP_UNREACH_PRECEDENCE_CUTOFF: printf(" !C"); ++unreachable; break; case ICMP_UNREACH_NET_UNKNOWN: case ICMP_UNREACH_HOST_UNKNOWN: printf(" !U"); ++unreachable; break; case ICMP_UNREACH_ISOLATED: printf(" !I"); ++unreachable; break; case ICMP_UNREACH_TOSNET: case ICMP_UNREACH_TOSHOST: printf(" !T"); ++unreachable; break; default: printf(" !<%d>", icmp_code); ++unreachable; break; } break; } /* while (wait and read a packet) */ /* there was no packet at all? */ if (read_len == 0) printf(" *"); } /* for (nprobes) */ bb_putchar('\n'); if (got_there || (unreachable > 0 && unreachable >= nprobes - 1) ) { break; } } if (ENABLE_FEATURE_CLEAN_UP) { free(to); free(lastaddr); free(from_lsa); } return 0; } int traceroute_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int traceroute_main(int argc UNUSED_PARAM, char **argv) { return common_traceroute_main(0, argv); } #if ENABLE_TRACEROUTE6 int traceroute6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int traceroute6_main(int argc UNUSED_PARAM, char **argv) { return common_traceroute_main(OPT_IPV6, argv); } #endif busybox-1.22.1/networking/interface.c0000644000000000000000000006720112263563520016273 0ustar rootroot/* 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 * * Added print_bytes_scaled function to reduce code size. * Added some (potentially) missing defines. * Improved display support for -a and for a named interface. * * ----------------------------------------------------------- * * ifconfig This file contains an implementation of the command * that either displays or sets the characteristics of * one or more of the system's networking interfaces. * * * Author: Fred N. van Kempen, * and others. Copyright 1993 MicroWalt Corporation * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Patched to support 'add' and 'del' keywords for INET(4) addresses * by Mrs. Brisby * * {1.34} - 19980630 - Arnaldo Carvalho de Melo * - gettext instead of catgets for i18n * 10/1998 - Andi Kleen. Use interface list primitives. * 20001008 - Bernd Eckenfels, Patch from RH for setting mtu * (default AF was wrong) */ #include "libbb.h" #include "inet_common.h" #include #include #ifdef HAVE_NET_ETHERNET_H # include #endif #if ENABLE_FEATURE_HWIB /* #include */ # undef INFINIBAND_ALEN # define INFINIBAND_ALEN 20 #endif #if ENABLE_FEATURE_IPV6 # define HAVE_AFINET6 1 #else # undef HAVE_AFINET6 #endif #define _PATH_PROCNET_DEV "/proc/net/dev" #define _PATH_PROCNET_IFINET6 "/proc/net/if_inet6" #ifdef HAVE_AFINET6 # ifndef _LINUX_IN6_H /* * This is from linux/include/net/ipv6.h */ struct in6_ifreq { struct in6_addr ifr6_addr; uint32_t ifr6_prefixlen; unsigned int ifr6_ifindex; }; # endif #endif /* HAVE_AFINET6 */ /* Defines for glibc2.0 users. */ #ifndef SIOCSIFTXQLEN # define SIOCSIFTXQLEN 0x8943 # define SIOCGIFTXQLEN 0x8942 #endif /* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */ #ifndef ifr_qlen # define ifr_qlen ifr_ifru.ifru_mtu #endif #ifndef HAVE_TXQUEUELEN # define HAVE_TXQUEUELEN 1 #endif #ifndef IFF_DYNAMIC # define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */ #endif /* Display an Internet socket address. */ static const char* FAST_FUNC INET_sprint(struct sockaddr *sap, int numeric) { static char *buff; /* defaults to NULL */ free(buff); if (sap->sa_family == 0xFFFF || sap->sa_family == 0) return "[NONE SET]"; buff = INET_rresolve((struct sockaddr_in *) sap, numeric, 0xffffff00); return buff; } #ifdef UNUSED_AND_BUGGY static int INET_getsock(char *bufp, struct sockaddr *sap) { char *sp = bufp, *bp; unsigned int i; unsigned val; struct sockaddr_in *sock_in; sock_in = (struct sockaddr_in *) sap; sock_in->sin_family = AF_INET; sock_in->sin_port = 0; val = 0; bp = (char *) &val; for (i = 0; i < sizeof(sock_in->sin_addr.s_addr); i++) { *sp = toupper(*sp); if ((unsigned)(*sp - 'A') <= 5) bp[i] |= (int) (*sp - ('A' - 10)); else if (isdigit(*sp)) bp[i] |= (int) (*sp - '0'); else return -1; bp[i] <<= 4; sp++; *sp = toupper(*sp); if ((unsigned)(*sp - 'A') <= 5) bp[i] |= (int) (*sp - ('A' - 10)); else if (isdigit(*sp)) bp[i] |= (int) (*sp - '0'); else return -1; sp++; } sock_in->sin_addr.s_addr = htonl(val); return (sp - bufp); } #endif static int FAST_FUNC INET_input(/*int type,*/ const char *bufp, struct sockaddr *sap) { return INET_resolve(bufp, (struct sockaddr_in *) sap, 0); /* switch (type) { case 1: return (INET_getsock(bufp, sap)); case 256: return (INET_resolve(bufp, (struct sockaddr_in *) sap, 1)); default: return (INET_resolve(bufp, (struct sockaddr_in *) sap, 0)); } */ } static const struct aftype inet_aftype = { .name = "inet", .title = "DARPA Internet", .af = AF_INET, .alen = 4, .sprint = INET_sprint, .input = INET_input, }; #ifdef HAVE_AFINET6 /* Display an Internet socket address. */ /* dirty! struct sockaddr usually doesn't suffer for inet6 addresses, fst. */ static const char* FAST_FUNC INET6_sprint(struct sockaddr *sap, int numeric) { static char *buff; free(buff); if (sap->sa_family == 0xFFFF || sap->sa_family == 0) return "[NONE SET]"; buff = INET6_rresolve((struct sockaddr_in6 *) sap, numeric); return buff; } #ifdef UNUSED static int INET6_getsock(char *bufp, struct sockaddr *sap) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *) sap; sin6->sin6_family = AF_INET6; sin6->sin6_port = 0; if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0) return -1; return 16; /* ?;) */ } #endif static int FAST_FUNC INET6_input(/*int type,*/ const char *bufp, struct sockaddr *sap) { return INET6_resolve(bufp, (struct sockaddr_in6 *) sap); /* switch (type) { case 1: return (INET6_getsock(bufp, sap)); default: return (INET6_resolve(bufp, (struct sockaddr_in6 *) sap)); } */ } static const struct aftype inet6_aftype = { .name = "inet6", .title = "IPv6", .af = AF_INET6, .alen = sizeof(struct in6_addr), .sprint = INET6_sprint, .input = INET6_input, }; #endif /* HAVE_AFINET6 */ /* Display an UNSPEC address. */ static char* FAST_FUNC UNSPEC_print(unsigned char *ptr) { static char *buff; char *pos; unsigned int i; if (!buff) buff = xmalloc(sizeof(struct sockaddr) * 3 + 1); pos = buff; for (i = 0; i < sizeof(struct sockaddr); i++) { /* careful -- not every libc's sprintf returns # bytes written */ sprintf(pos, "%02X-", (*ptr++ & 0377)); pos += 3; } /* Erase trailing "-". Works as long as sizeof(struct sockaddr) != 0 */ *--pos = '\0'; return buff; } /* Display an UNSPEC socket address. */ static const char* FAST_FUNC UNSPEC_sprint(struct sockaddr *sap, int numeric UNUSED_PARAM) { if (sap->sa_family == 0xFFFF || sap->sa_family == 0) return "[NONE SET]"; return UNSPEC_print((unsigned char *)sap->sa_data); } static const struct aftype unspec_aftype = { .name = "unspec", .title = "UNSPEC", .af = AF_UNSPEC, .alen = 0, .print = UNSPEC_print, .sprint = UNSPEC_sprint, }; static const struct aftype *const aftypes[] = { &inet_aftype, #ifdef HAVE_AFINET6 &inet6_aftype, #endif &unspec_aftype, NULL }; /* Check our protocol family table for this family. */ const struct aftype* FAST_FUNC get_aftype(const char *name) { const struct aftype *const *afp; afp = aftypes; while (*afp != NULL) { if (!strcmp((*afp)->name, name)) return (*afp); afp++; } return NULL; } /* Check our protocol family table for this family. */ static const struct aftype *get_afntype(int af) { const struct aftype *const *afp; afp = aftypes; while (*afp != NULL) { if ((*afp)->af == af) return *afp; afp++; } return NULL; } struct user_net_device_stats { unsigned long long rx_packets; /* total packets received */ unsigned long long tx_packets; /* total packets transmitted */ unsigned long long rx_bytes; /* total bytes received */ unsigned long long tx_bytes; /* total bytes transmitted */ unsigned long rx_errors; /* bad packets received */ unsigned long tx_errors; /* packet transmit problems */ unsigned long rx_dropped; /* no space in linux buffers */ unsigned long tx_dropped; /* no space available in linux */ unsigned long rx_multicast; /* multicast packets received */ unsigned long rx_compressed; unsigned long tx_compressed; unsigned long collisions; /* detailed rx_errors: */ unsigned long rx_length_errors; unsigned long rx_over_errors; /* receiver ring buff overflow */ unsigned long rx_crc_errors; /* recved pkt with crc error */ unsigned long rx_frame_errors; /* recv'd frame alignment error */ unsigned long rx_fifo_errors; /* recv'r fifo overrun */ unsigned long rx_missed_errors; /* receiver missed packet */ /* detailed tx_errors */ unsigned long tx_aborted_errors; unsigned long tx_carrier_errors; unsigned long tx_fifo_errors; unsigned long tx_heartbeat_errors; unsigned long tx_window_errors; }; struct interface { struct interface *next, *prev; char name[IFNAMSIZ]; /* interface name */ short type; /* if type */ short flags; /* various flags */ int metric; /* routing metric */ int mtu; /* MTU value */ int tx_queue_len; /* transmit queue length */ struct ifmap map; /* hardware setup */ struct sockaddr addr; /* IP address */ struct sockaddr dstaddr; /* P-P IP address */ struct sockaddr broadaddr; /* IP broadcast address */ struct sockaddr netmask; /* IP network mask */ int has_ip; char hwaddr[32]; /* HW address */ int statistics_valid; struct user_net_device_stats stats; /* statistics */ int keepalive; /* keepalive value for SLIP */ int outfill; /* outfill value for SLIP */ }; smallint interface_opt_a; /* show all interfaces */ static struct interface *int_list, *int_last; #if 0 /* like strcmp(), but knows about numbers */ except that the freshly added calls to xatoul() brf on ethernet aliases with uClibc with e.g.: ife->name='lo' name='eth0:1' static int nstrcmp(const char *a, const char *b) { const char *a_ptr = a; const char *b_ptr = b; while (*a == *b) { if (*a == '\0') { return 0; } if (!isdigit(*a) && isdigit(*(a+1))) { a_ptr = a+1; b_ptr = b+1; } a++; b++; } if (isdigit(*a) && isdigit(*b)) { return xatoul(a_ptr) > xatoul(b_ptr) ? 1 : -1; } return *a - *b; } #endif static struct interface *add_interface(char *name) { struct interface *ife, **nextp, *new; for (ife = int_last; ife; ife = ife->prev) { int n = /*n*/strcmp(ife->name, name); if (n == 0) return ife; if (n < 0) break; } new = xzalloc(sizeof(*new)); strncpy_IFNAMSIZ(new->name, name); nextp = ife ? &ife->next : &int_list; new->prev = ife; new->next = *nextp; if (new->next) new->next->prev = new; else int_last = new; *nextp = new; return new; } static char *get_name(char *name, char *p) { /* Extract from nul-terminated p where p matches * : after leading whitespace. * If match is not made, set name empty and return unchanged p */ char *nameend; char *namestart = skip_whitespace(p); nameend = namestart; while (*nameend && *nameend != ':' && !isspace(*nameend)) nameend++; if (*nameend == ':') { if ((nameend - namestart) < IFNAMSIZ) { memcpy(name, namestart, nameend - namestart); name[nameend - namestart] = '\0'; p = nameend; } else { /* Interface name too large */ name[0] = '\0'; } } else { /* trailing ':' not found - return empty */ name[0] = '\0'; } return p + 1; } /* If scanf supports size qualifiers for %n conversions, then we can * use a modified fmt that simply stores the position in the fields * having no associated fields in the proc string. Of course, we need * to zero them again when we're done. But that is smaller than the * old approach of multiple scanf occurrences with large numbers of * args. */ /* static const char *const ss_fmt[] = { */ /* "%lln%llu%lu%lu%lu%lu%ln%ln%lln%llu%lu%lu%lu%lu%lu", */ /* "%llu%llu%lu%lu%lu%lu%ln%ln%llu%llu%lu%lu%lu%lu%lu", */ /* "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu" */ /* }; */ /* Lie about the size of the int pointed to for %n. */ #if INT_MAX == LONG_MAX static const char *const ss_fmt[] = { "%n%llu%u%u%u%u%n%n%n%llu%u%u%u%u%u", "%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u", "%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u" }; #else static const char *const ss_fmt[] = { "%n%llu%lu%lu%lu%lu%n%n%n%llu%lu%lu%lu%lu%lu", "%llu%llu%lu%lu%lu%lu%n%n%llu%llu%lu%lu%lu%lu%lu", "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu" }; #endif static void get_dev_fields(char *bp, struct interface *ife, int procnetdev_vsn) { memset(&ife->stats, 0, sizeof(struct user_net_device_stats)); sscanf(bp, ss_fmt[procnetdev_vsn], &ife->stats.rx_bytes, /* missing for 0 */ &ife->stats.rx_packets, &ife->stats.rx_errors, &ife->stats.rx_dropped, &ife->stats.rx_fifo_errors, &ife->stats.rx_frame_errors, &ife->stats.rx_compressed, /* missing for <= 1 */ &ife->stats.rx_multicast, /* missing for <= 1 */ &ife->stats.tx_bytes, /* missing for 0 */ &ife->stats.tx_packets, &ife->stats.tx_errors, &ife->stats.tx_dropped, &ife->stats.tx_fifo_errors, &ife->stats.collisions, &ife->stats.tx_carrier_errors, &ife->stats.tx_compressed /* missing for <= 1 */ ); if (procnetdev_vsn <= 1) { if (procnetdev_vsn == 0) { ife->stats.rx_bytes = 0; ife->stats.tx_bytes = 0; } ife->stats.rx_multicast = 0; ife->stats.rx_compressed = 0; ife->stats.tx_compressed = 0; } } static int procnetdev_version(char *buf) { if (strstr(buf, "compressed")) return 2; if (strstr(buf, "bytes")) return 1; return 0; } static int if_readconf(void) { int numreqs = 30; struct ifconf ifc; struct ifreq *ifr; int n, err = -1; int skfd; ifc.ifc_buf = NULL; /* SIOCGIFCONF currently seems to only work properly on AF_INET sockets (as of 2.1.128) */ skfd = socket(AF_INET, SOCK_DGRAM, 0); if (skfd < 0) { bb_perror_msg("error: no inet socket available"); return -1; } for (;;) { ifc.ifc_len = sizeof(struct ifreq) * numreqs; ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len); if (ioctl_or_warn(skfd, SIOCGIFCONF, &ifc) < 0) { goto out; } if (ifc.ifc_len == (int)(sizeof(struct ifreq) * numreqs)) { /* assume it overflowed and try again */ numreqs += 10; continue; } break; } ifr = ifc.ifc_req; for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) { add_interface(ifr->ifr_name); ifr++; } err = 0; out: close(skfd); free(ifc.ifc_buf); return err; } static int if_readlist_proc(char *target) { static smallint proc_read; FILE *fh; char buf[512]; struct interface *ife; int err, procnetdev_vsn; if (proc_read) return 0; if (!target) proc_read = 1; fh = fopen_or_warn(_PATH_PROCNET_DEV, "r"); if (!fh) { return if_readconf(); } fgets(buf, sizeof buf, fh); /* eat line */ fgets(buf, sizeof buf, fh); procnetdev_vsn = procnetdev_version(buf); err = 0; while (fgets(buf, sizeof buf, fh)) { char *s, name[128]; s = get_name(name, buf); ife = add_interface(name); get_dev_fields(s, ife, procnetdev_vsn); ife->statistics_valid = 1; if (target && !strcmp(target, name)) break; } if (ferror(fh)) { bb_perror_msg(_PATH_PROCNET_DEV); err = -1; proc_read = 0; } fclose(fh); return err; } static int if_readlist(void) { int err = if_readlist_proc(NULL); /* Needed in order to get ethN:M aliases */ if (!err) err = if_readconf(); return err; } /* Fetch the interface configuration from the kernel. */ static int if_fetch(struct interface *ife) { struct ifreq ifr; char *ifname = ife->name; int skfd; skfd = xsocket(AF_INET, SOCK_DGRAM, 0); strncpy_IFNAMSIZ(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) { close(skfd); return -1; } ife->flags = ifr.ifr_flags; strncpy_IFNAMSIZ(ifr.ifr_name, ifname); memset(ife->hwaddr, 0, 32); if (ioctl(skfd, SIOCGIFHWADDR, &ifr) >= 0) memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8); ife->type = ifr.ifr_hwaddr.sa_family; strncpy_IFNAMSIZ(ifr.ifr_name, ifname); ife->metric = 0; if (ioctl(skfd, SIOCGIFMETRIC, &ifr) >= 0) ife->metric = ifr.ifr_metric; strncpy_IFNAMSIZ(ifr.ifr_name, ifname); ife->mtu = 0; if (ioctl(skfd, SIOCGIFMTU, &ifr) >= 0) ife->mtu = ifr.ifr_mtu; memset(&ife->map, 0, sizeof(struct ifmap)); #ifdef SIOCGIFMAP strncpy_IFNAMSIZ(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFMAP, &ifr) == 0) ife->map = ifr.ifr_map; #endif #ifdef HAVE_TXQUEUELEN strncpy_IFNAMSIZ(ifr.ifr_name, ifname); ife->tx_queue_len = -1; /* unknown value */ if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) >= 0) ife->tx_queue_len = ifr.ifr_qlen; #else ife->tx_queue_len = -1; /* unknown value */ #endif strncpy_IFNAMSIZ(ifr.ifr_name, ifname); ifr.ifr_addr.sa_family = AF_INET; memset(&ife->addr, 0, sizeof(struct sockaddr)); if (ioctl(skfd, SIOCGIFADDR, &ifr) == 0) { ife->has_ip = 1; ife->addr = ifr.ifr_addr; strncpy_IFNAMSIZ(ifr.ifr_name, ifname); memset(&ife->dstaddr, 0, sizeof(struct sockaddr)); if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) >= 0) ife->dstaddr = ifr.ifr_dstaddr; strncpy_IFNAMSIZ(ifr.ifr_name, ifname); memset(&ife->broadaddr, 0, sizeof(struct sockaddr)); if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) >= 0) ife->broadaddr = ifr.ifr_broadaddr; strncpy_IFNAMSIZ(ifr.ifr_name, ifname); memset(&ife->netmask, 0, sizeof(struct sockaddr)); if (ioctl(skfd, SIOCGIFNETMASK, &ifr) >= 0) ife->netmask = ifr.ifr_netmask; } close(skfd); return 0; } static int do_if_fetch(struct interface *ife) { if (if_fetch(ife) < 0) { const char *errmsg; if (errno == ENODEV) { /* Give better error message for this case. */ errmsg = "Device not found"; } else { errmsg = strerror(errno); } bb_error_msg("%s: error fetching interface information: %s", ife->name, errmsg); return -1; } return 0; } static const struct hwtype unspec_hwtype = { .name = "unspec", .title = "UNSPEC", .type = -1, .print = UNSPEC_print }; static const struct hwtype loop_hwtype = { .name = "loop", .title = "Local Loopback", .type = ARPHRD_LOOPBACK }; /* Display an Ethernet address in readable format. */ static char* FAST_FUNC ether_print(unsigned char *ptr) { static char *buff; free(buff); buff = xasprintf("%02X:%02X:%02X:%02X:%02X:%02X", (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377), (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377) ); return buff; } static const struct hwtype ether_hwtype = { .name = "ether", .title = "Ethernet", .type = ARPHRD_ETHER, .alen = ETH_ALEN, .print = ether_print, .input = in_ether }; static const struct hwtype ppp_hwtype = { .name = "ppp", .title = "Point-to-Point Protocol", .type = ARPHRD_PPP }; #if ENABLE_FEATURE_IPV6 static const struct hwtype sit_hwtype = { .name = "sit", .title = "IPv6-in-IPv4", .type = ARPHRD_SIT, .print = UNSPEC_print, .suppress_null_addr = 1 }; #endif #if ENABLE_FEATURE_HWIB static const struct hwtype ib_hwtype = { .name = "infiniband", .title = "InfiniBand", .type = ARPHRD_INFINIBAND, .alen = INFINIBAND_ALEN, .print = UNSPEC_print, .input = in_ib, }; #endif static const struct hwtype *const hwtypes[] = { &loop_hwtype, ðer_hwtype, &ppp_hwtype, &unspec_hwtype, #if ENABLE_FEATURE_IPV6 &sit_hwtype, #endif #if ENABLE_FEATURE_HWIB &ib_hwtype, #endif NULL }; #ifdef IFF_PORTSEL static const char *const if_port_text[] = { /* Keep in step with */ "unknown", "10base2", "10baseT", "AUI", "100baseT", "100baseTX", "100baseFX", NULL }; #endif /* Check our hardware type table for this type. */ const struct hwtype* FAST_FUNC get_hwtype(const char *name) { const struct hwtype *const *hwp; hwp = hwtypes; while (*hwp != NULL) { if (!strcmp((*hwp)->name, name)) return (*hwp); hwp++; } return NULL; } /* Check our hardware type table for this type. */ const struct hwtype* FAST_FUNC get_hwntype(int type) { const struct hwtype *const *hwp; hwp = hwtypes; while (*hwp != NULL) { if ((*hwp)->type == type) return *hwp; hwp++; } return NULL; } /* return 1 if address is all zeros */ static int hw_null_address(const struct hwtype *hw, void *ap) { int i; unsigned char *address = (unsigned char *) ap; for (i = 0; i < hw->alen; i++) if (address[i]) return 0; return 1; } static const char TRext[] ALIGN1 = "\0\0\0Ki\0Mi\0Gi\0Ti"; static void print_bytes_scaled(unsigned long long ull, const char *end) { unsigned long long int_part; const char *ext; unsigned int frac_part; int i; frac_part = 0; ext = TRext; int_part = ull; i = 4; do { if (int_part >= 1024) { frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024; int_part /= 1024; ext += 3; /* KiB, MiB, GiB, TiB */ } --i; } while (i); printf("X bytes:%llu (%llu.%u %sB)%s", ull, int_part, frac_part, ext, end); } #ifdef HAVE_AFINET6 #define IPV6_ADDR_ANY 0x0000U #define IPV6_ADDR_UNICAST 0x0001U #define IPV6_ADDR_MULTICAST 0x0002U #define IPV6_ADDR_ANYCAST 0x0004U #define IPV6_ADDR_LOOPBACK 0x0010U #define IPV6_ADDR_LINKLOCAL 0x0020U #define IPV6_ADDR_SITELOCAL 0x0040U #define IPV6_ADDR_COMPATv4 0x0080U #define IPV6_ADDR_SCOPE_MASK 0x00f0U #define IPV6_ADDR_MAPPED 0x1000U #define IPV6_ADDR_RESERVED 0x2000U /* reserved address space */ static void ife_print6(struct interface *ptr) { FILE *f; char addr6[40], devname[21]; struct sockaddr_in6 sap; int plen, scope, dad_status, if_idx; char addr6p[8][5]; f = fopen_for_read(_PATH_PROCNET_IFINET6); if (f == NULL) return; while (fscanf (f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n", addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope, &dad_status, devname) != EOF ) { if (!strcmp(devname, ptr->name)) { sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s", addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7]); inet_pton(AF_INET6, addr6, (struct sockaddr *) &sap.sin6_addr); sap.sin6_family = AF_INET6; printf(" inet6 addr: %s/%d", INET6_sprint((struct sockaddr *) &sap, 1), plen); printf(" Scope:"); switch (scope & IPV6_ADDR_SCOPE_MASK) { case 0: puts("Global"); break; case IPV6_ADDR_LINKLOCAL: puts("Link"); break; case IPV6_ADDR_SITELOCAL: puts("Site"); break; case IPV6_ADDR_COMPATv4: puts("Compat"); break; case IPV6_ADDR_LOOPBACK: puts("Host"); break; default: puts("Unknown"); } } } fclose(f); } #else #define ife_print6(a) ((void)0) #endif static void ife_print(struct interface *ptr) { const struct aftype *ap; const struct hwtype *hw; int hf; int can_compress = 0; ap = get_afntype(ptr->addr.sa_family); if (ap == NULL) ap = get_afntype(0); hf = ptr->type; if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6) can_compress = 1; hw = get_hwntype(hf); if (hw == NULL) hw = get_hwntype(-1); printf("%-9s Link encap:%s ", ptr->name, hw->title); /* For some hardware types (eg Ash, ATM) we don't print the hardware address if it's null. */ if (hw->print != NULL && !(hw_null_address(hw, ptr->hwaddr) && hw->suppress_null_addr) ) { printf("HWaddr %s ", hw->print((unsigned char *)ptr->hwaddr)); } #ifdef IFF_PORTSEL if (ptr->flags & IFF_PORTSEL) { printf("Media:%s", if_port_text[ptr->map.port] /* [0] */); if (ptr->flags & IFF_AUTOMEDIA) printf("(auto)"); } #endif bb_putchar('\n'); if (ptr->has_ip) { printf(" %s addr:%s ", ap->name, ap->sprint(&ptr->addr, 1)); if (ptr->flags & IFF_POINTOPOINT) { printf(" P-t-P:%s ", ap->sprint(&ptr->dstaddr, 1)); } if (ptr->flags & IFF_BROADCAST) { printf(" Bcast:%s ", ap->sprint(&ptr->broadaddr, 1)); } printf(" Mask:%s\n", ap->sprint(&ptr->netmask, 1)); } ife_print6(ptr); printf(" "); /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */ if (ptr->flags == 0) { printf("[NO FLAGS] "); } else { static const char ife_print_flags_strs[] ALIGN1 = "UP\0" "BROADCAST\0" "DEBUG\0" "LOOPBACK\0" "POINTOPOINT\0" "NOTRAILERS\0" "RUNNING\0" "NOARP\0" "PROMISC\0" "ALLMULTI\0" "SLAVE\0" "MASTER\0" "MULTICAST\0" #ifdef HAVE_DYNAMIC "DYNAMIC\0" #endif ; static const unsigned short ife_print_flags_mask[] ALIGN2 = { IFF_UP, IFF_BROADCAST, IFF_DEBUG, IFF_LOOPBACK, IFF_POINTOPOINT, IFF_NOTRAILERS, IFF_RUNNING, IFF_NOARP, IFF_PROMISC, IFF_ALLMULTI, IFF_SLAVE, IFF_MASTER, IFF_MULTICAST #ifdef HAVE_DYNAMIC ,IFF_DYNAMIC #endif }; const unsigned short *mask = ife_print_flags_mask; const char *str = ife_print_flags_strs; do { if (ptr->flags & *mask) { printf("%s ", str); } mask++; str += strlen(str) + 1; } while (*str); } /* DONT FORGET TO ADD THE FLAGS IN ife_print_short */ printf(" MTU:%d Metric:%d", ptr->mtu, ptr->metric ? ptr->metric : 1); #ifdef SIOCSKEEPALIVE if (ptr->outfill || ptr->keepalive) printf(" Outfill:%d Keepalive:%d", ptr->outfill, ptr->keepalive); #endif bb_putchar('\n'); /* If needed, display the interface statistics. */ if (ptr->statistics_valid) { /* XXX: statistics are currently only printed for the primary address, * not for the aliases, although strictly speaking they're shared * by all addresses. */ printf(" "); printf("RX packets:%llu errors:%lu dropped:%lu overruns:%lu frame:%lu\n", ptr->stats.rx_packets, ptr->stats.rx_errors, ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors, ptr->stats.rx_frame_errors); if (can_compress) printf(" compressed:%lu\n", ptr->stats.rx_compressed); printf(" "); printf("TX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n", ptr->stats.tx_packets, ptr->stats.tx_errors, ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors, ptr->stats.tx_carrier_errors); printf(" collisions:%lu ", ptr->stats.collisions); if (can_compress) printf("compressed:%lu ", ptr->stats.tx_compressed); if (ptr->tx_queue_len != -1) printf("txqueuelen:%d ", ptr->tx_queue_len); printf("\n R"); print_bytes_scaled(ptr->stats.rx_bytes, " T"); print_bytes_scaled(ptr->stats.tx_bytes, "\n"); } if (ptr->map.irq || ptr->map.mem_start || ptr->map.dma || ptr->map.base_addr ) { printf(" "); if (ptr->map.irq) printf("Interrupt:%d ", ptr->map.irq); if (ptr->map.base_addr >= 0x100) /* Only print devices using it for I/O maps */ printf("Base address:0x%lx ", (unsigned long) ptr->map.base_addr); if (ptr->map.mem_start) { printf("Memory:%lx-%lx ", ptr->map.mem_start, ptr->map.mem_end); } if (ptr->map.dma) printf("DMA chan:%x ", ptr->map.dma); bb_putchar('\n'); } bb_putchar('\n'); } static int do_if_print(struct interface *ife) /*, int *opt_a)*/ { int res; res = do_if_fetch(ife); if (res >= 0) { if ((ife->flags & IFF_UP) || interface_opt_a) ife_print(ife); } return res; } static struct interface *lookup_interface(char *name) { struct interface *ife = NULL; if (if_readlist_proc(name) < 0) return NULL; ife = add_interface(name); return ife; } #ifdef UNUSED static int for_all_interfaces(int (*doit) (struct interface *, void *), void *cookie) { struct interface *ife; if (!int_list && (if_readlist() < 0)) return -1; for (ife = int_list; ife; ife = ife->next) { int err = doit(ife, cookie); if (err) return err; } return 0; } #endif /* for ipv4 add/del modes */ static int if_print(char *ifname) { struct interface *ife; int res; if (!ifname) { /*res = for_all_interfaces(do_if_print, &interface_opt_a);*/ if (!int_list && (if_readlist() < 0)) return -1; for (ife = int_list; ife; ife = ife->next) { int err = do_if_print(ife); /*, &interface_opt_a);*/ if (err) return err; } return 0; } ife = lookup_interface(ifname); res = do_if_fetch(ife); if (res >= 0) ife_print(ife); return res; } #if ENABLE_FEATURE_HWIB /* Input an Infiniband address and convert to binary. */ int FAST_FUNC in_ib(const char *bufp, struct sockaddr *sap) { sap->sa_family = ib_hwtype.type; //TODO: error check? hex2bin((char*)sap->sa_data, bufp, INFINIBAND_ALEN); # ifdef HWIB_DEBUG fprintf(stderr, "in_ib(%s): %s\n", bufp, UNSPEC_print(sap->sa_data)); # endif return 0; } #endif int FAST_FUNC display_interfaces(char *ifname) { int status; status = if_print(ifname); return (status < 0); /* status < 0 == 1 -- error */ } busybox-1.22.1/networking/ntpd.c0000644000000000000000000023330312267106071015274 0ustar rootroot/* * NTP client/server, based on OpenNTPD 3.9p1 * * Author: Adam Tkac * * Licensed under GPLv2, see file LICENSE in this source tree. * * Parts of OpenNTPD clock syncronization code is replaced by * code which is based on ntp-4.2.6, whuch carries the following * copyright notice: * *********************************************************************** * * * Copyright (c) University of Delaware 1992-2009 * * * * Permission to use, copy, modify, and distribute this software and * * its documentation for any purpose with or without fee is hereby * * granted, provided that the above copyright notice appears in all * * copies and that both the copyright notice and this permission * * notice appear in supporting documentation, and that the name * * University of Delaware not be used in advertising or publicity * * pertaining to distribution of the software without specific, * * written prior permission. The University of Delaware makes no * * representations about the suitability this software for any * * purpose. It is provided "as is" without express or implied * * warranty. * * * *********************************************************************** */ //usage:#define ntpd_trivial_usage //usage: "[-dnqNw"IF_FEATURE_NTPD_SERVER("l")"] [-S PROG] [-p PEER]..." //usage:#define ntpd_full_usage "\n\n" //usage: "NTP client/server\n" //usage: "\n -d Verbose" //usage: "\n -n Do not daemonize" //usage: "\n -q Quit after clock is set" //usage: "\n -N Run at high priority" //usage: "\n -w Do not set time (only query peers), implies -n" //usage: IF_FEATURE_NTPD_SERVER( //usage: "\n -l Run as server on port 123" //usage: ) //usage: "\n -S PROG Run PROG after stepping time, stratum change, and every 11 mins" //usage: "\n -p PEER Obtain time from PEER (may be repeated)" #include "libbb.h" #include #include /* For IPTOS_LOWDELAY definition */ #include /* setpriority */ #include #ifndef IPTOS_LOWDELAY # define IPTOS_LOWDELAY 0x10 #endif #ifndef IP_PKTINFO # error "Sorry, your kernel has to support IP_PKTINFO" #endif /* Verbosity control (max level of -dddd options accepted). * max 6 is very talkative (and bloated). 3 is non-bloated, * production level setting. */ #define MAX_VERBOSE 3 /* High-level description of the algorithm: * * We start running with very small poll_exp, BURSTPOLL, * in order to quickly accumulate INITIAL_SAMPLES datapoints * for each peer. Then, time is stepped if the offset is larger * than STEP_THRESHOLD, otherwise it isn't; anyway, we enlarge * poll_exp to MINPOLL and enter frequency measurement step: * we collect new datapoints but ignore them for WATCH_THRESHOLD * seconds. After WATCH_THRESHOLD seconds we look at accumulated * offset and estimate frequency drift. * * (frequency measurement step seems to not be strictly needed, * it is conditionally disabled with USING_INITIAL_FREQ_ESTIMATION * define set to 0) * * After this, we enter "steady state": we collect a datapoint, * we select the best peer, if this datapoint is not a new one * (IOW: if this datapoint isn't for selected peer), sleep * and collect another one; otherwise, use its offset to update * frequency drift, if offset is somewhat large, reduce poll_exp, * otherwise increase poll_exp. * * If offset is larger than STEP_THRESHOLD, which shouldn't normally * happen, we assume that something "bad" happened (computer * was hibernated, someone set totally wrong date, etc), * then the time is stepped, all datapoints are discarded, * and we go back to steady state. * * Made some changes to speed up re-syncing after our clock goes bad * (tested with suspending my laptop): * - if largish offset (>= STEP_THRESHOLD * 8 == 1 sec) is seen * from a peer, schedule next query for this peer soon * without drastically lowering poll interval for everybody. * This makes us collect enough data for step much faster: * e.g. at poll = 10 (1024 secs), step was done within 5 minutes * after first reply which indicated that our clock is 14 seconds off. * - on step, do not discard d_dispersion data of the existing datapoints, * do not clear reachable_bits. This prevents discarding first ~8 * datapoints after the step. */ #define RETRY_INTERVAL 5 /* on error, retry in N secs */ #define RESPONSE_INTERVAL 15 /* wait for reply up to N secs */ #define INITIAL_SAMPLES 4 /* how many samples do we want for init */ #define BAD_DELAY_GROWTH 4 /* drop packet if its delay grew by more than this */ /* Clock discipline parameters and constants */ /* Step threshold (sec). std ntpd uses 0.128. * Using exact power of 2 (1/8) results in smaller code */ #define STEP_THRESHOLD 0.125 #define WATCH_THRESHOLD 128 /* stepout threshold (sec). std ntpd uses 900 (11 mins (!)) */ /* NB: set WATCH_THRESHOLD to ~60 when debugging to save time) */ //UNUSED: #define PANIC_THRESHOLD 1000 /* panic threshold (sec) */ #define FREQ_TOLERANCE 0.000015 /* frequency tolerance (15 PPM) */ #define BURSTPOLL 0 /* initial poll */ #define MINPOLL 5 /* minimum poll interval. std ntpd uses 6 (6: 64 sec) */ /* If we got largish offset from a peer, cap next query interval * for this peer by this many seconds: */ #define BIGOFF_INTERVAL (1 << 6) /* If offset > discipline_jitter * POLLADJ_GATE, and poll interval is >= 2^BIGPOLL, * then it is decreased _at once_. (If < 2^BIGPOLL, it will be decreased _eventually_). */ #define BIGPOLL 10 /* 2^10 sec ~= 17 min */ #define MAXPOLL 12 /* maximum poll interval (12: 1.1h, 17: 36.4h). std ntpd uses 17 */ /* Actively lower poll when we see such big offsets. * With STEP_THRESHOLD = 0.125, it means we try to sync more aggressively * if offset increases over ~0.04 sec */ #define POLLDOWN_OFFSET (STEP_THRESHOLD / 3) #define MINDISP 0.01 /* minimum dispersion (sec) */ #define MAXDISP 16 /* maximum dispersion (sec) */ #define MAXSTRAT 16 /* maximum stratum (infinity metric) */ #define MAXDIST 1 /* distance threshold (sec) */ #define MIN_SELECTED 1 /* minimum intersection survivors */ #define MIN_CLUSTERED 3 /* minimum cluster survivors */ #define MAXDRIFT 0.000500 /* frequency drift we can correct (500 PPM) */ /* Poll-adjust threshold. * When we see that offset is small enough compared to discipline jitter, * we grow a counter: += MINPOLL. When counter goes over POLLADJ_LIMIT, * we poll_exp++. If offset isn't small, counter -= poll_exp*2, * and when it goes below -POLLADJ_LIMIT, we poll_exp--. * (Bumped from 30 to 40 since otherwise I often see poll_exp going *2* steps down) */ #define POLLADJ_LIMIT 40 /* If offset < discipline_jitter * POLLADJ_GATE, then we decide to increase * poll interval (we think we can't improve timekeeping * by staying at smaller poll). */ #define POLLADJ_GATE 4 #define TIMECONST_HACK_GATE 2 /* Compromise Allan intercept (sec). doc uses 1500, std ntpd uses 512 */ #define ALLAN 512 /* PLL loop gain */ #define PLL 65536 /* FLL loop gain [why it depends on MAXPOLL??] */ #define FLL (MAXPOLL + 1) /* Parameter averaging constant */ #define AVG 4 enum { NTP_VERSION = 4, NTP_MAXSTRATUM = 15, NTP_DIGESTSIZE = 16, NTP_MSGSIZE_NOAUTH = 48, NTP_MSGSIZE = (NTP_MSGSIZE_NOAUTH + 4 + NTP_DIGESTSIZE), /* Status Masks */ MODE_MASK = (7 << 0), VERSION_MASK = (7 << 3), VERSION_SHIFT = 3, LI_MASK = (3 << 6), /* Leap Second Codes (high order two bits of m_status) */ LI_NOWARNING = (0 << 6), /* no warning */ LI_PLUSSEC = (1 << 6), /* add a second (61 seconds) */ LI_MINUSSEC = (2 << 6), /* minus a second (59 seconds) */ LI_ALARM = (3 << 6), /* alarm condition */ /* Mode values */ MODE_RES0 = 0, /* reserved */ MODE_SYM_ACT = 1, /* symmetric active */ MODE_SYM_PAS = 2, /* symmetric passive */ MODE_CLIENT = 3, /* client */ MODE_SERVER = 4, /* server */ MODE_BROADCAST = 5, /* broadcast */ MODE_RES1 = 6, /* reserved for NTP control message */ MODE_RES2 = 7, /* reserved for private use */ }; //TODO: better base selection #define OFFSET_1900_1970 2208988800UL /* 1970 - 1900 in seconds */ #define NUM_DATAPOINTS 8 typedef struct { uint32_t int_partl; uint32_t fractionl; } l_fixedpt_t; typedef struct { uint16_t int_parts; uint16_t fractions; } s_fixedpt_t; typedef struct { uint8_t m_status; /* status of local clock and leap info */ uint8_t m_stratum; uint8_t m_ppoll; /* poll value */ int8_t m_precision_exp; s_fixedpt_t m_rootdelay; s_fixedpt_t m_rootdisp; uint32_t m_refid; l_fixedpt_t m_reftime; l_fixedpt_t m_orgtime; l_fixedpt_t m_rectime; l_fixedpt_t m_xmttime; uint32_t m_keyid; uint8_t m_digest[NTP_DIGESTSIZE]; } msg_t; typedef struct { double d_offset; double d_recv_time; double d_dispersion; } datapoint_t; typedef struct { len_and_sockaddr *p_lsa; char *p_dotted; int p_fd; int datapoint_idx; uint32_t lastpkt_refid; uint8_t lastpkt_status; uint8_t lastpkt_stratum; uint8_t reachable_bits; /* when to send new query (if p_fd == -1) * or when receive times out (if p_fd >= 0): */ double next_action_time; double p_xmttime; double lastpkt_recv_time; double lastpkt_delay; double lastpkt_rootdelay; double lastpkt_rootdisp; /* produced by filter algorithm: */ double filter_offset; double filter_dispersion; double filter_jitter; datapoint_t filter_datapoint[NUM_DATAPOINTS]; /* last sent packet: */ msg_t p_xmt_msg; } peer_t; #define USING_KERNEL_PLL_LOOP 1 #define USING_INITIAL_FREQ_ESTIMATION 0 enum { OPT_n = (1 << 0), OPT_q = (1 << 1), OPT_N = (1 << 2), OPT_x = (1 << 3), /* Insert new options above this line. */ /* Non-compat options: */ OPT_w = (1 << 4), OPT_p = (1 << 5), OPT_S = (1 << 6), OPT_l = (1 << 7) * ENABLE_FEATURE_NTPD_SERVER, /* We hijack some bits for other purposes */ OPT_qq = (1 << 31), }; struct globals { double cur_time; /* total round trip delay to currently selected reference clock */ double rootdelay; /* reference timestamp: time when the system clock was last set or corrected */ double reftime; /* total dispersion to currently selected reference clock */ double rootdisp; double last_script_run; char *script_name; llist_t *ntp_peers; #if ENABLE_FEATURE_NTPD_SERVER int listen_fd; # define G_listen_fd (G.listen_fd) #else # define G_listen_fd (-1) #endif unsigned verbose; unsigned peer_cnt; /* refid: 32-bit code identifying the particular server or reference clock * in stratum 0 packets this is a four-character ASCII string, * called the kiss code, used for debugging and monitoring * in stratum 1 packets this is a four-character ASCII string * assigned to the reference clock by IANA. Example: "GPS " * in stratum 2+ packets, it's IPv4 address or 4 first bytes * of MD5 hash of IPv6 */ uint32_t refid; uint8_t ntp_status; /* precision is defined as the larger of the resolution and time to * read the clock, in log2 units. For instance, the precision of a * mains-frequency clock incrementing at 60 Hz is 16 ms, even when the * system clock hardware representation is to the nanosecond. * * Delays, jitters of various kinds are clamped down to precision. * * If precision_sec is too large, discipline_jitter gets clamped to it * and if offset is smaller than discipline_jitter * POLLADJ_GATE, poll * interval grows even though we really can benefit from staying at * smaller one, collecting non-lagged datapoits and correcting offset. * (Lagged datapoits exist when poll_exp is large but we still have * systematic offset error - the time distance between datapoints * is significant and older datapoints have smaller offsets. * This makes our offset estimation a bit smaller than reality) * Due to this effect, setting G_precision_sec close to * STEP_THRESHOLD isn't such a good idea - offsets may grow * too big and we will step. I observed it with -6. * * OTOH, setting precision_sec far too small would result in futile * attempts to syncronize to an unachievable precision. * * -6 is 1/64 sec, -7 is 1/128 sec and so on. * -8 is 1/256 ~= 0.003906 (worked well for me --vda) * -9 is 1/512 ~= 0.001953 (let's try this for some time) */ #define G_precision_exp -9 /* * G_precision_exp is used only for construction outgoing packets. * It's ok to set G_precision_sec to a slightly different value * (One which is "nicer looking" in logs). * Exact value would be (1.0 / (1 << (- G_precision_exp))): */ #define G_precision_sec 0.002 uint8_t stratum; /* Bool. After set to 1, never goes back to 0: */ smallint initial_poll_complete; #define STATE_NSET 0 /* initial state, "nothing is set" */ //#define STATE_FSET 1 /* frequency set from file */ //#define STATE_SPIK 2 /* spike detected */ //#define STATE_FREQ 3 /* initial frequency */ #define STATE_SYNC 4 /* clock synchronized (normal operation) */ uint8_t discipline_state; // doc calls it c.state uint8_t poll_exp; // s.poll int polladj_count; // c.count long kernel_freq_drift; peer_t *last_update_peer; double last_update_offset; // c.last double last_update_recv_time; // s.t double discipline_jitter; // c.jitter /* Since we only compare it with ints, can simplify code * by not making this variable floating point: */ unsigned offset_to_jitter_ratio; //double cluster_offset; // s.offset //double cluster_jitter; // s.jitter #if !USING_KERNEL_PLL_LOOP double discipline_freq_drift; // c.freq /* Maybe conditionally calculate wander? it's used only for logging */ double discipline_wander; // c.wander #endif }; #define G (*ptr_to_globals) static const int const_IPTOS_LOWDELAY = IPTOS_LOWDELAY; #define VERB1 if (MAX_VERBOSE && G.verbose) #define VERB2 if (MAX_VERBOSE >= 2 && G.verbose >= 2) #define VERB3 if (MAX_VERBOSE >= 3 && G.verbose >= 3) #define VERB4 if (MAX_VERBOSE >= 4 && G.verbose >= 4) #define VERB5 if (MAX_VERBOSE >= 5 && G.verbose >= 5) #define VERB6 if (MAX_VERBOSE >= 6 && G.verbose >= 6) static double LOG2D(int a) { if (a < 0) return 1.0 / (1UL << -a); return 1UL << a; } static ALWAYS_INLINE double SQUARE(double x) { return x * x; } static ALWAYS_INLINE double MAXD(double a, double b) { if (a > b) return a; return b; } static ALWAYS_INLINE double MIND(double a, double b) { if (a < b) return a; return b; } static NOINLINE double my_SQRT(double X) { union { float f; int32_t i; } v; double invsqrt; double Xhalf = X * 0.5; /* Fast and good approximation to 1/sqrt(X), black magic */ v.f = X; /*v.i = 0x5f3759df - (v.i >> 1);*/ v.i = 0x5f375a86 - (v.i >> 1); /* - this constant is slightly better */ invsqrt = v.f; /* better than 0.2% accuracy */ /* Refining it using Newton's method: x1 = x0 - f(x0)/f'(x0) * f(x) = 1/(x*x) - X (f==0 when x = 1/sqrt(X)) * f'(x) = -2/(x*x*x) * f(x)/f'(x) = (X - 1/(x*x)) / (2/(x*x*x)) = X*x*x*x/2 - x/2 * x1 = x0 - (X*x0*x0*x0/2 - x0/2) = 1.5*x0 - X*x0*x0*x0/2 = x0*(1.5 - (X/2)*x0*x0) */ invsqrt = invsqrt * (1.5 - Xhalf * invsqrt * invsqrt); /* ~0.05% accuracy */ /* invsqrt = invsqrt * (1.5 - Xhalf * invsqrt * invsqrt); 2nd iter: ~0.0001% accuracy */ /* With 4 iterations, more than half results will be exact, * at 6th iterations result stabilizes with about 72% results exact. * We are well satisfied with 0.05% accuracy. */ return X * invsqrt; /* X * 1/sqrt(X) ~= sqrt(X) */ } static ALWAYS_INLINE double SQRT(double X) { /* If this arch doesn't use IEEE 754 floats, fall back to using libm */ if (sizeof(float) != 4) return sqrt(X); /* This avoids needing libm, saves about 0.5k on x86-32 */ return my_SQRT(X); } static double gettime1900d(void) { struct timeval tv; gettimeofday(&tv, NULL); /* never fails */ G.cur_time = tv.tv_sec + (1.0e-6 * tv.tv_usec) + OFFSET_1900_1970; return G.cur_time; } static void d_to_tv(double d, struct timeval *tv) { tv->tv_sec = (long)d; tv->tv_usec = (d - tv->tv_sec) * 1000000; } static double lfp_to_d(l_fixedpt_t lfp) { double ret; lfp.int_partl = ntohl(lfp.int_partl); lfp.fractionl = ntohl(lfp.fractionl); ret = (double)lfp.int_partl + ((double)lfp.fractionl / UINT_MAX); return ret; } static double sfp_to_d(s_fixedpt_t sfp) { double ret; sfp.int_parts = ntohs(sfp.int_parts); sfp.fractions = ntohs(sfp.fractions); ret = (double)sfp.int_parts + ((double)sfp.fractions / USHRT_MAX); return ret; } #if ENABLE_FEATURE_NTPD_SERVER static l_fixedpt_t d_to_lfp(double d) { l_fixedpt_t lfp; lfp.int_partl = (uint32_t)d; lfp.fractionl = (uint32_t)((d - lfp.int_partl) * UINT_MAX); lfp.int_partl = htonl(lfp.int_partl); lfp.fractionl = htonl(lfp.fractionl); return lfp; } static s_fixedpt_t d_to_sfp(double d) { s_fixedpt_t sfp; sfp.int_parts = (uint16_t)d; sfp.fractions = (uint16_t)((d - sfp.int_parts) * USHRT_MAX); sfp.int_parts = htons(sfp.int_parts); sfp.fractions = htons(sfp.fractions); return sfp; } #endif static double dispersion(const datapoint_t *dp) { return dp->d_dispersion + FREQ_TOLERANCE * (G.cur_time - dp->d_recv_time); } static double root_distance(peer_t *p) { /* The root synchronization distance is the maximum error due to * all causes of the local clock relative to the primary server. * It is defined as half the total delay plus total dispersion * plus peer jitter. */ return MAXD(MINDISP, p->lastpkt_rootdelay + p->lastpkt_delay) / 2 + p->lastpkt_rootdisp + p->filter_dispersion + FREQ_TOLERANCE * (G.cur_time - p->lastpkt_recv_time) + p->filter_jitter; } static void set_next(peer_t *p, unsigned t) { p->next_action_time = G.cur_time + t; } /* * Peer clock filter and its helpers */ static void filter_datapoints(peer_t *p) { int i, idx; double sum, wavg; datapoint_t *fdp; #if 0 /* Simulations have shown that use of *averaged* offset for p->filter_offset * is in fact worse than simply using last received one: with large poll intervals * (>= 2048) averaging code uses offset values which are outdated by hours, * and time/frequency correction goes totally wrong when fed essentially bogus offsets. */ int got_newest; double minoff, maxoff, w; double x = x; /* for compiler */ double oldest_off = oldest_off; double oldest_age = oldest_age; double newest_off = newest_off; double newest_age = newest_age; fdp = p->filter_datapoint; minoff = maxoff = fdp[0].d_offset; for (i = 1; i < NUM_DATAPOINTS; i++) { if (minoff > fdp[i].d_offset) minoff = fdp[i].d_offset; if (maxoff < fdp[i].d_offset) maxoff = fdp[i].d_offset; } idx = p->datapoint_idx; /* most recent datapoint's index */ /* Average offset: * Drop two outliers and take weighted average of the rest: * most_recent/2 + older1/4 + older2/8 ... + older5/32 + older6/32 * we use older6/32, not older6/64 since sum of weights should be 1: * 1/2 + 1/4 + 1/8 + 1/16 + 1/32 + 1/32 = 1 */ wavg = 0; w = 0.5; /* n-1 * --- dispersion(i) * filter_dispersion = \ ------------- * / (i+1) * --- 2 * i=0 */ got_newest = 0; sum = 0; for (i = 0; i < NUM_DATAPOINTS; i++) { VERB5 { bb_error_msg("datapoint[%d]: off:%f disp:%f(%f) age:%f%s", i, fdp[idx].d_offset, fdp[idx].d_dispersion, dispersion(&fdp[idx]), G.cur_time - fdp[idx].d_recv_time, (minoff == fdp[idx].d_offset || maxoff == fdp[idx].d_offset) ? " (outlier by offset)" : "" ); } sum += dispersion(&fdp[idx]) / (2 << i); if (minoff == fdp[idx].d_offset) { minoff -= 1; /* so that we don't match it ever again */ } else if (maxoff == fdp[idx].d_offset) { maxoff += 1; } else { oldest_off = fdp[idx].d_offset; oldest_age = G.cur_time - fdp[idx].d_recv_time; if (!got_newest) { got_newest = 1; newest_off = oldest_off; newest_age = oldest_age; } x = oldest_off * w; wavg += x; w /= 2; } idx = (idx - 1) & (NUM_DATAPOINTS - 1); } p->filter_dispersion = sum; wavg += x; /* add another older6/64 to form older6/32 */ /* Fix systematic underestimation with large poll intervals. * Imagine that we still have a bit of uncorrected drift, * and poll interval is big (say, 100 sec). Offsets form a progression: * 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 - 0.7 is most recent. * The algorithm above drops 0.0 and 0.7 as outliers, * and then we have this estimation, ~25% off from 0.7: * 0.1/32 + 0.2/32 + 0.3/16 + 0.4/8 + 0.5/4 + 0.6/2 = 0.503125 */ x = oldest_age - newest_age; if (x != 0) { x = newest_age / x; /* in above example, 100 / (600 - 100) */ if (x < 1) { /* paranoia check */ x = (newest_off - oldest_off) * x; /* 0.5 * 100/500 = 0.1 */ wavg += x; } } p->filter_offset = wavg; #else fdp = p->filter_datapoint; idx = p->datapoint_idx; /* most recent datapoint's index */ /* filter_offset: simply use the most recent value */ p->filter_offset = fdp[idx].d_offset; /* n-1 * --- dispersion(i) * filter_dispersion = \ ------------- * / (i+1) * --- 2 * i=0 */ wavg = 0; sum = 0; for (i = 0; i < NUM_DATAPOINTS; i++) { sum += dispersion(&fdp[idx]) / (2 << i); wavg += fdp[idx].d_offset; idx = (idx - 1) & (NUM_DATAPOINTS - 1); } wavg /= NUM_DATAPOINTS; p->filter_dispersion = sum; #endif /* +----- -----+ ^ 1/2 * | n-1 | * | --- | * | 1 \ 2 | * filter_jitter = | --- * / (avg-offset_j) | * | n --- | * | j=0 | * +----- -----+ * where n is the number of valid datapoints in the filter (n > 1); * if filter_jitter < precision then filter_jitter = precision */ sum = 0; for (i = 0; i < NUM_DATAPOINTS; i++) { sum += SQUARE(wavg - fdp[i].d_offset); } sum = SQRT(sum / NUM_DATAPOINTS); p->filter_jitter = sum > G_precision_sec ? sum : G_precision_sec; VERB4 bb_error_msg("filter offset:%+f disp:%f jitter:%f", p->filter_offset, p->filter_dispersion, p->filter_jitter); } static void reset_peer_stats(peer_t *p, double offset) { int i; bool small_ofs = fabs(offset) < 16 * STEP_THRESHOLD; /* Used to set p->filter_datapoint[i].d_dispersion = MAXDISP * and clear reachable bits, but this proved to be too agressive: * after step (tested with suspinding laptop for ~30 secs), * this caused all previous data to be considered invalid, * making us needing to collect full ~8 datapoins per peer * after step in order to start trusting them. * In turn, this was making poll interval decrease even after * step was done. (Poll interval decreases already before step * in this scenario, because we see large offsets and end up with * no good peer to select). */ for (i = 0; i < NUM_DATAPOINTS; i++) { if (small_ofs) { p->filter_datapoint[i].d_recv_time += offset; if (p->filter_datapoint[i].d_offset != 0) { p->filter_datapoint[i].d_offset -= offset; //bb_error_msg("p->filter_datapoint[%d].d_offset %f -> %f", // i, // p->filter_datapoint[i].d_offset + offset, // p->filter_datapoint[i].d_offset); } } else { p->filter_datapoint[i].d_recv_time = G.cur_time; p->filter_datapoint[i].d_offset = 0; /*p->filter_datapoint[i].d_dispersion = MAXDISP;*/ } } if (small_ofs) { p->lastpkt_recv_time += offset; } else { /*p->reachable_bits = 0;*/ p->lastpkt_recv_time = G.cur_time; } filter_datapoints(p); /* recalc p->filter_xxx */ VERB6 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time); } static void add_peers(char *s) { peer_t *p; p = xzalloc(sizeof(*p)); p->p_lsa = xhost2sockaddr(s, 123); p->p_dotted = xmalloc_sockaddr2dotted_noport(&p->p_lsa->u.sa); p->p_fd = -1; p->p_xmt_msg.m_status = MODE_CLIENT | (NTP_VERSION << 3); p->next_action_time = G.cur_time; /* = set_next(p, 0); */ reset_peer_stats(p, 16 * STEP_THRESHOLD); llist_add_to(&G.ntp_peers, p); G.peer_cnt++; } static int do_sendto(int fd, const struct sockaddr *from, const struct sockaddr *to, socklen_t addrlen, msg_t *msg, ssize_t len) { ssize_t ret; errno = 0; if (!from) { ret = sendto(fd, msg, len, MSG_DONTWAIT, to, addrlen); } else { ret = send_to_from(fd, msg, len, MSG_DONTWAIT, to, from, addrlen); } if (ret != len) { bb_perror_msg("send failed"); return -1; } return 0; } static void send_query_to_peer(peer_t *p) { /* Why do we need to bind()? * See what happens when we don't bind: * * socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3 * setsockopt(3, SOL_IP, IP_TOS, [16], 4) = 0 * gettimeofday({1259071266, 327885}, NULL) = 0 * sendto(3, "xxx", 48, MSG_DONTWAIT, {sa_family=AF_INET, sin_port=htons(123), sin_addr=inet_addr("10.34.32.125")}, 16) = 48 * ^^^ we sent it from some source port picked by kernel. * time(NULL) = 1259071266 * write(2, "ntpd: entering poll 15 secs\n", 28) = 28 * poll([{fd=3, events=POLLIN}], 1, 15000) = 1 ([{fd=3, revents=POLLIN}]) * recv(3, "yyy", 68, MSG_DONTWAIT) = 48 * ^^^ this recv will receive packets to any local port! * * Uncomment this and use strace to see it in action: */ #define PROBE_LOCAL_ADDR /* { len_and_sockaddr lsa; lsa.len = LSA_SIZEOF_SA; getsockname(p->query.fd, &lsa.u.sa, &lsa.len); } */ if (p->p_fd == -1) { int fd, family; len_and_sockaddr *local_lsa; family = p->p_lsa->u.sa.sa_family; p->p_fd = fd = xsocket_type(&local_lsa, family, SOCK_DGRAM); /* local_lsa has "null" address and port 0 now. * bind() ensures we have a *particular port* selected by kernel * and remembered in p->p_fd, thus later recv(p->p_fd) * receives only packets sent to this port. */ PROBE_LOCAL_ADDR xbind(fd, &local_lsa->u.sa, local_lsa->len); PROBE_LOCAL_ADDR #if ENABLE_FEATURE_IPV6 if (family == AF_INET) #endif setsockopt(fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY)); free(local_lsa); } /* Emit message _before_ attempted send. Think of a very short * roundtrip networks: we need to go back to recv loop ASAP, * to reduce delay. Printing messages after send works against that. */ VERB1 bb_error_msg("sending query to %s", p->p_dotted); /* * Send out a random 64-bit number as our transmit time. The NTP * server will copy said number into the originate field on the * response that it sends us. This is totally legal per the SNTP spec. * * The impact of this is two fold: we no longer send out the current * system time for the world to see (which may aid an attacker), and * it gives us a (not very secure) way of knowing that we're not * getting spoofed by an attacker that can't capture our traffic * but can spoof packets from the NTP server we're communicating with. * * Save the real transmit timestamp locally. */ p->p_xmt_msg.m_xmttime.int_partl = random(); p->p_xmt_msg.m_xmttime.fractionl = random(); p->p_xmttime = gettime1900d(); /* Were doing it only if sendto worked, but * loss of sync detection needs reachable_bits updated * even if sending fails *locally*: * "network is unreachable" because cable was pulled? * We still need to declare "unsync" if this condition persists. */ p->reachable_bits <<= 1; if (do_sendto(p->p_fd, /*from:*/ NULL, /*to:*/ &p->p_lsa->u.sa, /*addrlen:*/ p->p_lsa->len, &p->p_xmt_msg, NTP_MSGSIZE_NOAUTH) == -1 ) { close(p->p_fd); p->p_fd = -1; /* * We know that we sent nothing. * We can retry *soon* without fearing * that we are flooding the peer. */ set_next(p, RETRY_INTERVAL); return; } set_next(p, RESPONSE_INTERVAL); } /* Note that there is no provision to prevent several run_scripts * to be started in quick succession. In fact, it happens rather often * if initial syncronization results in a step. * You will see "step" and then "stratum" script runs, sometimes * as close as only 0.002 seconds apart. * Script should be ready to deal with this. */ static void run_script(const char *action, double offset) { char *argv[3]; char *env1, *env2, *env3, *env4; G.last_script_run = G.cur_time; if (!G.script_name) return; argv[0] = (char*) G.script_name; argv[1] = (char*) action; argv[2] = NULL; VERB1 bb_error_msg("executing '%s %s'", G.script_name, action); env1 = xasprintf("%s=%u", "stratum", G.stratum); putenv(env1); env2 = xasprintf("%s=%ld", "freq_drift_ppm", G.kernel_freq_drift); putenv(env2); env3 = xasprintf("%s=%u", "poll_interval", 1 << G.poll_exp); putenv(env3); env4 = xasprintf("%s=%f", "offset", offset); putenv(env4); /* Other items of potential interest: selected peer, * rootdelay, reftime, rootdisp, refid, ntp_status, * last_update_offset, last_update_recv_time, discipline_jitter, * how many peers have reachable_bits = 0? */ /* Don't want to wait: it may run hwclock --systohc, and that * may take some time (seconds): */ /*spawn_and_wait(argv);*/ spawn(argv); unsetenv("stratum"); unsetenv("freq_drift_ppm"); unsetenv("poll_interval"); unsetenv("offset"); free(env1); free(env2); free(env3); free(env4); } static NOINLINE void step_time(double offset) { llist_t *item; double dtime; struct timeval tvc, tvn; char buf[sizeof("yyyy-mm-dd hh:mm:ss") + /*paranoia:*/ 4]; time_t tval; gettimeofday(&tvc, NULL); /* never fails */ dtime = tvc.tv_sec + (1.0e-6 * tvc.tv_usec) + offset; d_to_tv(dtime, &tvn); if (settimeofday(&tvn, NULL) == -1) bb_perror_msg_and_die("settimeofday"); VERB2 { tval = tvc.tv_sec; strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &tval); bb_error_msg("current time is %s.%06u", buf, (unsigned)tvc.tv_usec); } tval = tvn.tv_sec; strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &tval); bb_error_msg("setting time to %s.%06u (offset %+fs)", buf, (unsigned)tvn.tv_usec, offset); /* Correct various fields which contain time-relative values: */ /* Globals: */ G.cur_time += offset; G.last_update_recv_time += offset; G.last_script_run += offset; /* p->lastpkt_recv_time, p->next_action_time and such: */ for (item = G.ntp_peers; item != NULL; item = item->link) { peer_t *pp = (peer_t *) item->data; reset_peer_stats(pp, offset); //bb_error_msg("offset:%+f pp->next_action_time:%f -> %f", // offset, pp->next_action_time, pp->next_action_time + offset); pp->next_action_time += offset; if (pp->p_fd >= 0) { /* We wait for reply from this peer too. * But due to step we are doing, reply's data is no longer * useful (in fact, it'll be bogus). Stop waiting for it. */ close(pp->p_fd); pp->p_fd = -1; set_next(pp, RETRY_INTERVAL); } } } /* * Selection and clustering, and their helpers */ typedef struct { peer_t *p; int type; double edge; double opt_rd; /* optimization */ } point_t; static int compare_point_edge(const void *aa, const void *bb) { const point_t *a = aa; const point_t *b = bb; if (a->edge < b->edge) { return -1; } return (a->edge > b->edge); } typedef struct { peer_t *p; double metric; } survivor_t; static int compare_survivor_metric(const void *aa, const void *bb) { const survivor_t *a = aa; const survivor_t *b = bb; if (a->metric < b->metric) { return -1; } return (a->metric > b->metric); } static int fit(peer_t *p, double rd) { if ((p->reachable_bits & (p->reachable_bits-1)) == 0) { /* One or zero bits in reachable_bits */ VERB4 bb_error_msg("peer %s unfit for selection: unreachable", p->p_dotted); return 0; } #if 0 /* we filter out such packets earlier */ if ((p->lastpkt_status & LI_ALARM) == LI_ALARM || p->lastpkt_stratum >= MAXSTRAT ) { VERB4 bb_error_msg("peer %s unfit for selection: bad status/stratum", p->p_dotted); return 0; } #endif /* rd is root_distance(p) */ if (rd > MAXDIST + FREQ_TOLERANCE * (1 << G.poll_exp)) { VERB4 bb_error_msg("peer %s unfit for selection: root distance too high", p->p_dotted); return 0; } //TODO // /* Do we have a loop? */ // if (p->refid == p->dstaddr || p->refid == s.refid) // return 0; return 1; } static peer_t* select_and_cluster(void) { peer_t *p; llist_t *item; int i, j; int size = 3 * G.peer_cnt; /* for selection algorithm */ point_t point[size]; unsigned num_points, num_candidates; double low, high; unsigned num_falsetickers; /* for cluster algorithm */ survivor_t survivor[size]; unsigned num_survivors; /* Selection */ num_points = 0; item = G.ntp_peers; if (G.initial_poll_complete) while (item != NULL) { double rd, offset; p = (peer_t *) item->data; rd = root_distance(p); offset = p->filter_offset; if (!fit(p, rd)) { item = item->link; continue; } VERB5 bb_error_msg("interval: [%f %f %f] %s", offset - rd, offset, offset + rd, p->p_dotted ); point[num_points].p = p; point[num_points].type = -1; point[num_points].edge = offset - rd; point[num_points].opt_rd = rd; num_points++; point[num_points].p = p; point[num_points].type = 0; point[num_points].edge = offset; point[num_points].opt_rd = rd; num_points++; point[num_points].p = p; point[num_points].type = 1; point[num_points].edge = offset + rd; point[num_points].opt_rd = rd; num_points++; item = item->link; } num_candidates = num_points / 3; if (num_candidates == 0) { VERB3 bb_error_msg("no valid datapoints%s", ", no peer selected"); return NULL; } //TODO: sorting does not seem to be done in reference code qsort(point, num_points, sizeof(point[0]), compare_point_edge); /* Start with the assumption that there are no falsetickers. * Attempt to find a nonempty intersection interval containing * the midpoints of all truechimers. * If a nonempty interval cannot be found, increase the number * of assumed falsetickers by one and try again. * If a nonempty interval is found and the number of falsetickers * is less than the number of truechimers, a majority has been found * and the midpoint of each truechimer represents * the candidates available to the cluster algorithm. */ num_falsetickers = 0; while (1) { int c; unsigned num_midpoints = 0; low = 1 << 9; high = - (1 << 9); c = 0; for (i = 0; i < num_points; i++) { /* We want to do: * if (point[i].type == -1) c++; * if (point[i].type == 1) c--; * and it's simpler to do it this way: */ c -= point[i].type; if (c >= num_candidates - num_falsetickers) { /* If it was c++ and it got big enough... */ low = point[i].edge; break; } if (point[i].type == 0) num_midpoints++; } c = 0; for (i = num_points-1; i >= 0; i--) { c += point[i].type; if (c >= num_candidates - num_falsetickers) { high = point[i].edge; break; } if (point[i].type == 0) num_midpoints++; } /* If the number of midpoints is greater than the number * of allowed falsetickers, the intersection contains at * least one truechimer with no midpoint - bad. * Also, interval should be nonempty. */ if (num_midpoints <= num_falsetickers && low < high) break; num_falsetickers++; if (num_falsetickers * 2 >= num_candidates) { VERB3 bb_error_msg("falsetickers:%d, candidates:%d%s", num_falsetickers, num_candidates, ", no peer selected"); return NULL; } } VERB4 bb_error_msg("selected interval: [%f, %f]; candidates:%d falsetickers:%d", low, high, num_candidates, num_falsetickers); /* Clustering */ /* Construct a list of survivors (p, metric) * from the chime list, where metric is dominated * first by stratum and then by root distance. * All other things being equal, this is the order of preference. */ num_survivors = 0; for (i = 0; i < num_points; i++) { if (point[i].edge < low || point[i].edge > high) continue; p = point[i].p; survivor[num_survivors].p = p; /* x.opt_rd == root_distance(p); */ survivor[num_survivors].metric = MAXDIST * p->lastpkt_stratum + point[i].opt_rd; VERB5 bb_error_msg("survivor[%d] metric:%f peer:%s", num_survivors, survivor[num_survivors].metric, p->p_dotted); num_survivors++; } /* There must be at least MIN_SELECTED survivors to satisfy the * correctness assertions. Ordinarily, the Byzantine criteria * require four survivors, but for the demonstration here, one * is acceptable. */ if (num_survivors < MIN_SELECTED) { VERB3 bb_error_msg("survivors:%d%s", num_survivors, ", no peer selected"); return NULL; } //looks like this is ONLY used by the fact that later we pick survivor[0]. //we can avoid sorting then, just find the minimum once! qsort(survivor, num_survivors, sizeof(survivor[0]), compare_survivor_metric); /* For each association p in turn, calculate the selection * jitter p->sjitter as the square root of the sum of squares * (p->offset - q->offset) over all q associations. The idea is * to repeatedly discard the survivor with maximum selection * jitter until a termination condition is met. */ while (1) { unsigned max_idx = max_idx; double max_selection_jitter = max_selection_jitter; double min_jitter = min_jitter; if (num_survivors <= MIN_CLUSTERED) { VERB4 bb_error_msg("num_survivors %d <= %d, not discarding more", num_survivors, MIN_CLUSTERED); break; } /* To make sure a few survivors are left * for the clustering algorithm to chew on, * we stop if the number of survivors * is less than or equal to MIN_CLUSTERED (3). */ for (i = 0; i < num_survivors; i++) { double selection_jitter_sq; p = survivor[i].p; if (i == 0 || p->filter_jitter < min_jitter) min_jitter = p->filter_jitter; selection_jitter_sq = 0; for (j = 0; j < num_survivors; j++) { peer_t *q = survivor[j].p; selection_jitter_sq += SQUARE(p->filter_offset - q->filter_offset); } if (i == 0 || selection_jitter_sq > max_selection_jitter) { max_selection_jitter = selection_jitter_sq; max_idx = i; } VERB6 bb_error_msg("survivor %d selection_jitter^2:%f", i, selection_jitter_sq); } max_selection_jitter = SQRT(max_selection_jitter / num_survivors); VERB5 bb_error_msg("max_selection_jitter (at %d):%f min_jitter:%f", max_idx, max_selection_jitter, min_jitter); /* If the maximum selection jitter is less than the * minimum peer jitter, then tossing out more survivors * will not lower the minimum peer jitter, so we might * as well stop. */ if (max_selection_jitter < min_jitter) { VERB4 bb_error_msg("max_selection_jitter:%f < min_jitter:%f, num_survivors:%d, not discarding more", max_selection_jitter, min_jitter, num_survivors); break; } /* Delete survivor[max_idx] from the list * and go around again. */ VERB6 bb_error_msg("dropping survivor %d", max_idx); num_survivors--; while (max_idx < num_survivors) { survivor[max_idx] = survivor[max_idx + 1]; max_idx++; } } if (0) { /* Combine the offsets of the clustering algorithm survivors * using a weighted average with weight determined by the root * distance. Compute the selection jitter as the weighted RMS * difference between the first survivor and the remaining * survivors. In some cases the inherent clock jitter can be * reduced by not using this algorithm, especially when frequent * clockhopping is involved. bbox: thus we don't do it. */ double x, y, z, w; y = z = w = 0; for (i = 0; i < num_survivors; i++) { p = survivor[i].p; x = root_distance(p); y += 1 / x; z += p->filter_offset / x; w += SQUARE(p->filter_offset - survivor[0].p->filter_offset) / x; } //G.cluster_offset = z / y; //G.cluster_jitter = SQRT(w / y); } /* Pick the best clock. If the old system peer is on the list * and at the same stratum as the first survivor on the list, * then don't do a clock hop. Otherwise, select the first * survivor on the list as the new system peer. */ p = survivor[0].p; if (G.last_update_peer && G.last_update_peer->lastpkt_stratum <= p->lastpkt_stratum ) { /* Starting from 1 is ok here */ for (i = 1; i < num_survivors; i++) { if (G.last_update_peer == survivor[i].p) { VERB5 bb_error_msg("keeping old synced peer"); p = G.last_update_peer; goto keep_old; } } } G.last_update_peer = p; keep_old: VERB4 bb_error_msg("selected peer %s filter_offset:%+f age:%f", p->p_dotted, p->filter_offset, G.cur_time - p->lastpkt_recv_time ); return p; } /* * Local clock discipline and its helpers */ static void set_new_values(int disc_state, double offset, double recv_time) { /* Enter new state and set state variables. Note we use the time * of the last clock filter sample, which must be earlier than * the current time. */ VERB4 bb_error_msg("disc_state=%d last update offset=%f recv_time=%f", disc_state, offset, recv_time); G.discipline_state = disc_state; G.last_update_offset = offset; G.last_update_recv_time = recv_time; } /* Return: -1: decrease poll interval, 0: leave as is, 1: increase */ static NOINLINE int update_local_clock(peer_t *p) { int rc; struct timex tmx; /* Note: can use G.cluster_offset instead: */ double offset = p->filter_offset; double recv_time = p->lastpkt_recv_time; double abs_offset; #if !USING_KERNEL_PLL_LOOP double freq_drift; #endif double since_last_update; double etemp, dtemp; abs_offset = fabs(offset); #if 0 /* If needed, -S script can do it by looking at $offset * env var and killing parent */ /* If the offset is too large, give up and go home */ if (abs_offset > PANIC_THRESHOLD) { bb_error_msg_and_die("offset %f far too big, exiting", offset); } #endif /* If this is an old update, for instance as the result * of a system peer change, avoid it. We never use * an old sample or the same sample twice. */ if (recv_time <= G.last_update_recv_time) { VERB3 bb_error_msg("update from %s: same or older datapoint, not using it", p->p_dotted); return 0; /* "leave poll interval as is" */ } /* Clock state machine transition function. This is where the * action is and defines how the system reacts to large time * and frequency errors. */ since_last_update = recv_time - G.reftime; #if !USING_KERNEL_PLL_LOOP freq_drift = 0; #endif #if USING_INITIAL_FREQ_ESTIMATION if (G.discipline_state == STATE_FREQ) { /* Ignore updates until the stepout threshold */ if (since_last_update < WATCH_THRESHOLD) { VERB4 bb_error_msg("measuring drift, datapoint ignored, %f sec remains", WATCH_THRESHOLD - since_last_update); return 0; /* "leave poll interval as is" */ } # if !USING_KERNEL_PLL_LOOP freq_drift = (offset - G.last_update_offset) / since_last_update; # endif } #endif /* There are two main regimes: when the * offset exceeds the step threshold and when it does not. */ if (abs_offset > STEP_THRESHOLD) { #if 0 double remains; // This "spike state" seems to be useless, peer selection already drops // occassional "bad" datapoints. If we are here, there were _many_ // large offsets. When a few first large offsets are seen, // we end up in "no valid datapoints, no peer selected" state. // Only when enough of them are seen (which means it's not a fluke), // we end up here. Looks like _our_ clock is off. switch (G.discipline_state) { case STATE_SYNC: /* The first outlyer: ignore it, switch to SPIK state */ VERB3 bb_error_msg("update from %s: offset:%+f, spike%s", p->p_dotted, offset, ""); G.discipline_state = STATE_SPIK; return -1; /* "decrease poll interval" */ case STATE_SPIK: /* Ignore succeeding outlyers until either an inlyer * is found or the stepout threshold is exceeded. */ remains = WATCH_THRESHOLD - since_last_update; if (remains > 0) { VERB3 bb_error_msg("update from %s: offset:%+f, spike%s", p->p_dotted, offset, ", datapoint ignored"); return -1; /* "decrease poll interval" */ } /* fall through: we need to step */ } /* switch */ #endif /* Step the time and clamp down the poll interval. * * In NSET state an initial frequency correction is * not available, usually because the frequency file has * not yet been written. Since the time is outside the * capture range, the clock is stepped. The frequency * will be set directly following the stepout interval. * * In FSET state the initial frequency has been set * from the frequency file. Since the time is outside * the capture range, the clock is stepped immediately, * rather than after the stepout interval. Guys get * nervous if it takes 17 minutes to set the clock for * the first time. * * In SPIK state the stepout threshold has expired and * the phase is still above the step threshold. Note * that a single spike greater than the step threshold * is always suppressed, even at the longer poll * intervals. */ VERB4 bb_error_msg("stepping time by %+f; poll_exp=MINPOLL", offset); step_time(offset); if (option_mask32 & OPT_q) { /* We were only asked to set time once. Done. */ exit(0); } G.polladj_count = 0; G.poll_exp = MINPOLL; G.stratum = MAXSTRAT; run_script("step", offset); recv_time += offset; #if USING_INITIAL_FREQ_ESTIMATION if (G.discipline_state == STATE_NSET) { set_new_values(STATE_FREQ, /*offset:*/ 0, recv_time); return 1; /* "ok to increase poll interval" */ } #endif abs_offset = offset = 0; set_new_values(STATE_SYNC, offset, recv_time); } else { /* abs_offset <= STEP_THRESHOLD */ if (G.poll_exp < MINPOLL && G.initial_poll_complete) { VERB4 bb_error_msg("small offset:%+f, disabling burst mode", offset); G.polladj_count = 0; G.poll_exp = MINPOLL; } /* Compute the clock jitter as the RMS of exponentially * weighted offset differences. Used by the poll adjust code. */ etemp = SQUARE(G.discipline_jitter); dtemp = SQUARE(offset - G.last_update_offset); G.discipline_jitter = SQRT(etemp + (dtemp - etemp) / AVG); switch (G.discipline_state) { case STATE_NSET: if (option_mask32 & OPT_q) { /* We were only asked to set time once. * The clock is precise enough, no need to step. */ exit(0); } #if USING_INITIAL_FREQ_ESTIMATION /* This is the first update received and the frequency * has not been initialized. The first thing to do * is directly measure the oscillator frequency. */ set_new_values(STATE_FREQ, offset, recv_time); #else set_new_values(STATE_SYNC, offset, recv_time); #endif VERB4 bb_error_msg("transitioning to FREQ, datapoint ignored"); return 0; /* "leave poll interval as is" */ #if 0 /* this is dead code for now */ case STATE_FSET: /* This is the first update and the frequency * has been initialized. Adjust the phase, but * don't adjust the frequency until the next update. */ set_new_values(STATE_SYNC, offset, recv_time); /* freq_drift remains 0 */ break; #endif #if USING_INITIAL_FREQ_ESTIMATION case STATE_FREQ: /* since_last_update >= WATCH_THRESHOLD, we waited enough. * Correct the phase and frequency and switch to SYNC state. * freq_drift was already estimated (see code above) */ set_new_values(STATE_SYNC, offset, recv_time); break; #endif default: #if !USING_KERNEL_PLL_LOOP /* Compute freq_drift due to PLL and FLL contributions. * * The FLL and PLL frequency gain constants * depend on the poll interval and Allan * intercept. The FLL is not used below one-half * the Allan intercept. Above that the loop gain * increases in steps to 1 / AVG. */ if ((1 << G.poll_exp) > ALLAN / 2) { etemp = FLL - G.poll_exp; if (etemp < AVG) etemp = AVG; freq_drift += (offset - G.last_update_offset) / (MAXD(since_last_update, ALLAN) * etemp); } /* For the PLL the integration interval * (numerator) is the minimum of the update * interval and poll interval. This allows * oversampling, but not undersampling. */ etemp = MIND(since_last_update, (1 << G.poll_exp)); dtemp = (4 * PLL) << G.poll_exp; freq_drift += offset * etemp / SQUARE(dtemp); #endif set_new_values(STATE_SYNC, offset, recv_time); break; } if (G.stratum != p->lastpkt_stratum + 1) { G.stratum = p->lastpkt_stratum + 1; run_script("stratum", offset); } } if (G.discipline_jitter < G_precision_sec) G.discipline_jitter = G_precision_sec; G.offset_to_jitter_ratio = abs_offset / G.discipline_jitter; G.reftime = G.cur_time; G.ntp_status = p->lastpkt_status; G.refid = p->lastpkt_refid; G.rootdelay = p->lastpkt_rootdelay + p->lastpkt_delay; dtemp = p->filter_jitter; // SQRT(SQUARE(p->filter_jitter) + SQUARE(G.cluster_jitter)); dtemp += MAXD(p->filter_dispersion + FREQ_TOLERANCE * (G.cur_time - p->lastpkt_recv_time) + abs_offset, MINDISP); G.rootdisp = p->lastpkt_rootdisp + dtemp; VERB4 bb_error_msg("updating leap/refid/reftime/rootdisp from peer %s", p->p_dotted); /* We are in STATE_SYNC now, but did not do adjtimex yet. * (Any other state does not reach this, they all return earlier) * By this time, freq_drift and offset are set * to values suitable for adjtimex. */ #if !USING_KERNEL_PLL_LOOP /* Calculate the new frequency drift and frequency stability (wander). * Compute the clock wander as the RMS of exponentially weighted * frequency differences. This is not used directly, but can, * along with the jitter, be a highly useful monitoring and * debugging tool. */ dtemp = G.discipline_freq_drift + freq_drift; G.discipline_freq_drift = MAXD(MIND(MAXDRIFT, dtemp), -MAXDRIFT); etemp = SQUARE(G.discipline_wander); dtemp = SQUARE(dtemp); G.discipline_wander = SQRT(etemp + (dtemp - etemp) / AVG); VERB4 bb_error_msg("discipline freq_drift=%.9f(int:%ld corr:%e) wander=%f", G.discipline_freq_drift, (long)(G.discipline_freq_drift * 65536e6), freq_drift, G.discipline_wander); #endif VERB4 { memset(&tmx, 0, sizeof(tmx)); if (adjtimex(&tmx) < 0) bb_perror_msg_and_die("adjtimex"); bb_error_msg("p adjtimex freq:%ld offset:%+ld status:0x%x tc:%ld", tmx.freq, tmx.offset, tmx.status, tmx.constant); } memset(&tmx, 0, sizeof(tmx)); #if 0 //doesn't work, offset remains 0 (!) in kernel: //ntpd: set adjtimex freq:1786097 tmx.offset:77487 //ntpd: prev adjtimex freq:1786097 tmx.offset:0 //ntpd: cur adjtimex freq:1786097 tmx.offset:0 tmx.modes = ADJ_FREQUENCY | ADJ_OFFSET; /* 65536 is one ppm */ tmx.freq = G.discipline_freq_drift * 65536e6; #endif tmx.modes = ADJ_OFFSET | ADJ_STATUS | ADJ_TIMECONST;// | ADJ_MAXERROR | ADJ_ESTERROR; tmx.offset = (offset * 1000000); /* usec */ tmx.status = STA_PLL; if (G.ntp_status & LI_PLUSSEC) tmx.status |= STA_INS; if (G.ntp_status & LI_MINUSSEC) tmx.status |= STA_DEL; tmx.constant = G.poll_exp - 4; /* EXPERIMENTAL. * The below if statement should be unnecessary, but... * It looks like Linux kernel's PLL is far too gentle in changing * tmx.freq in response to clock offset. Offset keeps growing * and eventually we fall back to smaller poll intervals. * We can make correction more agressive (about x2) by supplying * PLL time constant which is one less than the real one. * To be on a safe side, let's do it only if offset is significantly * larger than jitter. */ if (tmx.constant > 0 && G.offset_to_jitter_ratio >= TIMECONST_HACK_GATE) tmx.constant--; //tmx.esterror = (uint32_t)(clock_jitter * 1e6); //tmx.maxerror = (uint32_t)((sys_rootdelay / 2 + sys_rootdisp) * 1e6); rc = adjtimex(&tmx); if (rc < 0) bb_perror_msg_and_die("adjtimex"); /* NB: here kernel returns constant == G.poll_exp, not == G.poll_exp - 4. * Not sure why. Perhaps it is normal. */ VERB4 bb_error_msg("adjtimex:%d freq:%ld offset:%+ld status:0x%x", rc, tmx.freq, tmx.offset, tmx.status); G.kernel_freq_drift = tmx.freq / 65536; VERB2 bb_error_msg("update from:%s offset:%+f jitter:%f clock drift:%+.3fppm tc:%d", p->p_dotted, offset, G.discipline_jitter, (double)tmx.freq / 65536, (int)tmx.constant); return 1; /* "ok to increase poll interval" */ } /* * We've got a new reply packet from a peer, process it * (helpers first) */ static unsigned retry_interval(void) { /* Local problem, want to retry soon */ unsigned interval, r; interval = RETRY_INTERVAL; r = random(); interval += r % (unsigned)(RETRY_INTERVAL / 4); VERB4 bb_error_msg("chose retry interval:%u", interval); return interval; } static unsigned poll_interval(int exponent) { unsigned interval, r; exponent = G.poll_exp + exponent; if (exponent < 0) exponent = 0; interval = 1 << exponent; r = random(); interval += ((r & (interval-1)) >> 4) + ((r >> 8) & 1); /* + 1/16 of interval, max */ VERB4 bb_error_msg("chose poll interval:%u (poll_exp:%d exp:%d)", interval, G.poll_exp, exponent); return interval; } static NOINLINE void recv_and_process_peer_pkt(peer_t *p) { int rc; ssize_t size; msg_t msg; double T1, T2, T3, T4; double dv, offset; unsigned interval; datapoint_t *datapoint; peer_t *q; offset = 0; /* We can recvfrom here and check from.IP, but some multihomed * ntp servers reply from their *other IP*. * TODO: maybe we should check at least what we can: from.port == 123? */ size = recv(p->p_fd, &msg, sizeof(msg), MSG_DONTWAIT); if (size == -1) { bb_perror_msg("recv(%s) error", p->p_dotted); if (errno == EHOSTUNREACH || errno == EHOSTDOWN || errno == ENETUNREACH || errno == ENETDOWN || errno == ECONNREFUSED || errno == EADDRNOTAVAIL || errno == EAGAIN ) { //TODO: always do this? interval = retry_interval(); goto set_next_and_ret; } xfunc_die(); } if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) { bb_error_msg("malformed packet received from %s", p->p_dotted); return; } if (msg.m_orgtime.int_partl != p->p_xmt_msg.m_xmttime.int_partl || msg.m_orgtime.fractionl != p->p_xmt_msg.m_xmttime.fractionl ) { /* Somebody else's packet */ return; } /* We do not expect any more packets from this peer for now. * Closing the socket informs kernel about it. * We open a new socket when we send a new query. */ close(p->p_fd); p->p_fd = -1; if ((msg.m_status & LI_ALARM) == LI_ALARM || msg.m_stratum == 0 || msg.m_stratum > NTP_MAXSTRATUM ) { // TODO: stratum 0 responses may have commands in 32-bit m_refid field: // "DENY", "RSTR" - peer does not like us at all // "RATE" - peer is overloaded, reduce polling freq bb_error_msg("reply from %s: peer is unsynced", p->p_dotted); goto pick_normal_interval; } // /* Verify valid root distance */ // if (msg.m_rootdelay / 2 + msg.m_rootdisp >= MAXDISP || p->lastpkt_reftime > msg.m_xmt) // return; /* invalid header values */ p->lastpkt_status = msg.m_status; p->lastpkt_stratum = msg.m_stratum; p->lastpkt_rootdelay = sfp_to_d(msg.m_rootdelay); p->lastpkt_rootdisp = sfp_to_d(msg.m_rootdisp); p->lastpkt_refid = msg.m_refid; /* * From RFC 2030 (with a correction to the delay math): * * Timestamp Name ID When Generated * ------------------------------------------------------------ * Originate Timestamp T1 time request sent by client * Receive Timestamp T2 time request received by server * Transmit Timestamp T3 time reply sent by server * Destination Timestamp T4 time reply received by client * * The roundtrip delay and local clock offset are defined as * * delay = (T4 - T1) - (T3 - T2); offset = ((T2 - T1) + (T3 - T4)) / 2 */ T1 = p->p_xmttime; T2 = lfp_to_d(msg.m_rectime); T3 = lfp_to_d(msg.m_xmttime); T4 = G.cur_time; p->lastpkt_recv_time = T4; VERB6 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time); /* The delay calculation is a special case. In cases where the * server and client clocks are running at different rates and * with very fast networks, the delay can appear negative. In * order to avoid violating the Principle of Least Astonishment, * the delay is clamped not less than the system precision. */ dv = p->lastpkt_delay; p->lastpkt_delay = (T4 - T1) - (T3 - T2); if (p->lastpkt_delay < G_precision_sec) p->lastpkt_delay = G_precision_sec; /* * If this packet's delay is much bigger than the last one, * it's better to just ignore it than use its much less precise value. */ if (p->reachable_bits && p->lastpkt_delay > dv * BAD_DELAY_GROWTH) { bb_error_msg("reply from %s: delay %f is too high, ignoring", p->p_dotted, p->lastpkt_delay); goto pick_normal_interval; } p->datapoint_idx = p->reachable_bits ? (p->datapoint_idx + 1) % NUM_DATAPOINTS : 0; datapoint = &p->filter_datapoint[p->datapoint_idx]; datapoint->d_recv_time = T4; datapoint->d_offset = offset = ((T2 - T1) + (T3 - T4)) / 2; datapoint->d_dispersion = LOG2D(msg.m_precision_exp) + G_precision_sec; if (!p->reachable_bits) { /* 1st datapoint ever - replicate offset in every element */ int i; for (i = 0; i < NUM_DATAPOINTS; i++) { p->filter_datapoint[i].d_offset = offset; } } p->reachable_bits |= 1; if ((MAX_VERBOSE && G.verbose) || (option_mask32 & OPT_w)) { bb_error_msg("reply from %s: offset:%+f delay:%f status:0x%02x strat:%d refid:0x%08x rootdelay:%f reach:0x%02x", p->p_dotted, offset, p->lastpkt_delay, p->lastpkt_status, p->lastpkt_stratum, p->lastpkt_refid, p->lastpkt_rootdelay, p->reachable_bits /* not shown: m_ppoll, m_precision_exp, m_rootdisp, * m_reftime, m_orgtime, m_rectime, m_xmttime */ ); } /* Muck with statictics and update the clock */ filter_datapoints(p); q = select_and_cluster(); rc = -1; if (q) { rc = 0; if (!(option_mask32 & OPT_w)) { rc = update_local_clock(q); /* If drift is dangerously large, immediately * drop poll interval one step down. */ if (fabs(q->filter_offset) >= POLLDOWN_OFFSET) { VERB4 bb_error_msg("offset:%+f > POLLDOWN_OFFSET", q->filter_offset); goto poll_down; } } } /* else: no peer selected, rc = -1: we want to poll more often */ if (rc != 0) { /* Adjust the poll interval by comparing the current offset * with the clock jitter. If the offset is less than * the clock jitter times a constant, then the averaging interval * is increased, otherwise it is decreased. A bit of hysteresis * helps calm the dance. Works best using burst mode. */ if (rc > 0 && G.offset_to_jitter_ratio <= POLLADJ_GATE) { /* was += G.poll_exp but it is a bit * too optimistic for my taste at high poll_exp's */ G.polladj_count += MINPOLL; if (G.polladj_count > POLLADJ_LIMIT) { G.polladj_count = 0; if (G.poll_exp < MAXPOLL) { G.poll_exp++; VERB4 bb_error_msg("polladj: discipline_jitter:%f ++poll_exp=%d", G.discipline_jitter, G.poll_exp); } } else { VERB4 bb_error_msg("polladj: incr:%d", G.polladj_count); } } else { G.polladj_count -= G.poll_exp * 2; if (G.polladj_count < -POLLADJ_LIMIT || G.poll_exp >= BIGPOLL) { poll_down: G.polladj_count = 0; if (G.poll_exp > MINPOLL) { llist_t *item; G.poll_exp--; /* Correct p->next_action_time in each peer * which waits for sending, so that they send earlier. * Old pp->next_action_time are on the order * of t + (1 << old_poll_exp) + small_random, * we simply need to subtract ~half of that. */ for (item = G.ntp_peers; item != NULL; item = item->link) { peer_t *pp = (peer_t *) item->data; if (pp->p_fd < 0) pp->next_action_time -= (1 << G.poll_exp); } VERB4 bb_error_msg("polladj: discipline_jitter:%f --poll_exp=%d", G.discipline_jitter, G.poll_exp); } } else { VERB4 bb_error_msg("polladj: decr:%d", G.polladj_count); } } } /* Decide when to send new query for this peer */ pick_normal_interval: interval = poll_interval(0); if (fabs(offset) >= STEP_THRESHOLD * 8 && interval > BIGOFF_INTERVAL) { /* If we are synced, offsets are less than STEP_THRESHOLD, * or at the very least not much larger than it. * Now we see a largish one. * Either this peer is feeling bad, or packet got corrupted, * or _our_ clock is wrong now and _all_ peers will show similar * largish offsets too. * I observed this with laptop suspend stopping clock. * In any case, it makes sense to make next request soonish: * cases 1 and 2: get a better datapoint, * case 3: allows to resync faster. */ interval = BIGOFF_INTERVAL; } set_next_and_ret: set_next(p, interval); } #if ENABLE_FEATURE_NTPD_SERVER static NOINLINE void recv_and_process_client_pkt(void /*int fd*/) { ssize_t size; //uint8_t version; len_and_sockaddr *to; struct sockaddr *from; msg_t msg; uint8_t query_status; l_fixedpt_t query_xmttime; to = get_sock_lsa(G_listen_fd); from = xzalloc(to->len); size = recv_from_to(G_listen_fd, &msg, sizeof(msg), MSG_DONTWAIT, from, &to->u.sa, to->len); if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) { char *addr; if (size < 0) { if (errno == EAGAIN) goto bail; bb_perror_msg_and_die("recv"); } addr = xmalloc_sockaddr2dotted_noport(from); bb_error_msg("malformed packet received from %s: size %u", addr, (int)size); free(addr); goto bail; } query_status = msg.m_status; query_xmttime = msg.m_xmttime; /* Build a reply packet */ memset(&msg, 0, sizeof(msg)); msg.m_status = G.stratum < MAXSTRAT ? (G.ntp_status & LI_MASK) : LI_ALARM; msg.m_status |= (query_status & VERSION_MASK); msg.m_status |= ((query_status & MODE_MASK) == MODE_CLIENT) ? MODE_SERVER : MODE_SYM_PAS; msg.m_stratum = G.stratum; msg.m_ppoll = G.poll_exp; msg.m_precision_exp = G_precision_exp; /* this time was obtained between poll() and recv() */ msg.m_rectime = d_to_lfp(G.cur_time); msg.m_xmttime = d_to_lfp(gettime1900d()); /* this instant */ if (G.peer_cnt == 0) { /* we have no peers: "stratum 1 server" mode. reftime = our own time */ G.reftime = G.cur_time; } msg.m_reftime = d_to_lfp(G.reftime); msg.m_orgtime = query_xmttime; msg.m_rootdelay = d_to_sfp(G.rootdelay); //simple code does not do this, fix simple code! msg.m_rootdisp = d_to_sfp(G.rootdisp); //version = (query_status & VERSION_MASK); /* ... >> VERSION_SHIFT - done below instead */ msg.m_refid = G.refid; // (version > (3 << VERSION_SHIFT)) ? G.refid : G.refid3; /* We reply from the local address packet was sent to, * this makes to/from look swapped here: */ do_sendto(G_listen_fd, /*from:*/ &to->u.sa, /*to:*/ from, /*addrlen:*/ to->len, &msg, size); bail: free(to); free(from); } #endif /* Upstream ntpd's options: * * -4 Force DNS resolution of host names to the IPv4 namespace. * -6 Force DNS resolution of host names to the IPv6 namespace. * -a Require cryptographic authentication for broadcast client, * multicast client and symmetric passive associations. * This is the default. * -A Do not require cryptographic authentication for broadcast client, * multicast client and symmetric passive associations. * This is almost never a good idea. * -b Enable the client to synchronize to broadcast servers. * -c conffile * Specify the name and path of the configuration file, * default /etc/ntp.conf * -d Specify debugging mode. This option may occur more than once, * with each occurrence indicating greater detail of display. * -D level * Specify debugging level directly. * -f driftfile * Specify the name and path of the frequency file. * This is the same operation as the "driftfile FILE" * configuration command. * -g Normally, ntpd exits with a message to the system log * if the offset exceeds the panic threshold, which is 1000 s * by default. This option allows the time to be set to any value * without restriction; however, this can happen only once. * If the threshold is exceeded after that, ntpd will exit * with a message to the system log. This option can be used * with the -q and -x options. See the tinker command for other options. * -i jaildir * Chroot the server to the directory jaildir. This option also implies * that the server attempts to drop root privileges at startup * (otherwise, chroot gives very little additional security). * You may need to also specify a -u option. * -k keyfile * Specify the name and path of the symmetric key file, * default /etc/ntp/keys. This is the same operation * as the "keys FILE" configuration command. * -l logfile * Specify the name and path of the log file. The default * is the system log file. This is the same operation as * the "logfile FILE" configuration command. * -L Do not listen to virtual IPs. The default is to listen. * -n Don't fork. * -N To the extent permitted by the operating system, * run the ntpd at the highest priority. * -p pidfile * Specify the name and path of the file used to record the ntpd * process ID. This is the same operation as the "pidfile FILE" * configuration command. * -P priority * To the extent permitted by the operating system, * run the ntpd at the specified priority. * -q Exit the ntpd just after the first time the clock is set. * This behavior mimics that of the ntpdate program, which is * to be retired. The -g and -x options can be used with this option. * Note: The kernel time discipline is disabled with this option. * -r broadcastdelay * Specify the default propagation delay from the broadcast/multicast * server to this client. This is necessary only if the delay * cannot be computed automatically by the protocol. * -s statsdir * Specify the directory path for files created by the statistics * facility. This is the same operation as the "statsdir DIR" * configuration command. * -t key * Add a key number to the trusted key list. This option can occur * more than once. * -u user[:group] * Specify a user, and optionally a group, to switch to. * -v variable * -V variable * Add a system variable listed by default. * -x Normally, the time is slewed if the offset is less than the step * threshold, which is 128 ms by default, and stepped if above * the threshold. This option sets the threshold to 600 s, which is * well within the accuracy window to set the clock manually. * Note: since the slew rate of typical Unix kernels is limited * to 0.5 ms/s, each second of adjustment requires an amortization * interval of 2000 s. Thus, an adjustment as much as 600 s * will take almost 14 days to complete. This option can be used * with the -g and -q options. See the tinker command for other options. * Note: The kernel time discipline is disabled with this option. */ /* By doing init in a separate function we decrease stack usage * in main loop. */ static NOINLINE void ntp_init(char **argv) { unsigned opts; llist_t *peers; srandom(getpid()); if (getuid()) bb_error_msg_and_die(bb_msg_you_must_be_root); /* Set some globals */ G.stratum = MAXSTRAT; if (BURSTPOLL != 0) G.poll_exp = BURSTPOLL; /* speeds up initial sync */ G.last_script_run = G.reftime = G.last_update_recv_time = gettime1900d(); /* sets G.cur_time too */ /* Parse options */ peers = NULL; opt_complementary = "dd:p::wn"; /* d: counter; p: list; -w implies -n */ opts = getopt32(argv, "nqNx" /* compat */ "wp:S:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */ "d" /* compat */ "46aAbgL", /* compat, ignored */ &peers, &G.script_name, &G.verbose); if (!(opts & (OPT_p|OPT_l))) bb_show_usage(); // if (opts & OPT_x) /* disable stepping, only slew is allowed */ // G.time_was_stepped = 1; if (peers) { while (peers) add_peers(llist_pop(&peers)); } else { /* -l but no peers: "stratum 1 server" mode */ G.stratum = 1; } if (!(opts & OPT_n)) { bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO, argv); logmode = LOGMODE_NONE; } #if ENABLE_FEATURE_NTPD_SERVER G_listen_fd = -1; if (opts & OPT_l) { G_listen_fd = create_and_bind_dgram_or_die(NULL, 123); socket_want_pktinfo(G_listen_fd); setsockopt(G_listen_fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY)); } #endif /* I hesitate to set -20 prio. -15 should be high enough for timekeeping */ if (opts & OPT_N) setpriority(PRIO_PROCESS, 0, -15); /* If network is up, syncronization occurs in ~10 seconds. * We give "ntpd -q" 10 seconds to get first reply, * then another 50 seconds to finish syncing. * * I tested ntpd 4.2.6p1 and apparently it never exits * (will try forever), but it does not feel right. * The goal of -q is to act like ntpdate: set time * after a reasonably small period of polling, or fail. */ if (opts & OPT_q) { option_mask32 |= OPT_qq; alarm(10); } bb_signals(0 | (1 << SIGTERM) | (1 << SIGINT) | (1 << SIGALRM) , record_signo ); bb_signals(0 | (1 << SIGPIPE) | (1 << SIGCHLD) , SIG_IGN ); } int ntpd_main(int argc UNUSED_PARAM, char **argv) MAIN_EXTERNALLY_VISIBLE; int ntpd_main(int argc UNUSED_PARAM, char **argv) { #undef G struct globals G; struct pollfd *pfd; peer_t **idx2peer; unsigned cnt; memset(&G, 0, sizeof(G)); SET_PTR_TO_GLOBALS(&G); ntp_init(argv); /* If ENABLE_FEATURE_NTPD_SERVER, + 1 for listen_fd: */ cnt = G.peer_cnt + ENABLE_FEATURE_NTPD_SERVER; idx2peer = xzalloc(sizeof(idx2peer[0]) * cnt); pfd = xzalloc(sizeof(pfd[0]) * cnt); /* Countdown: we never sync before we sent INITIAL_SAMPLES+1 * packets to each peer. * NB: if some peer is not responding, we may end up sending * fewer packets to it and more to other peers. * NB2: sync usually happens using INITIAL_SAMPLES packets, * since last reply does not come back instantaneously. */ cnt = G.peer_cnt * (INITIAL_SAMPLES + 1); write_pidfile(CONFIG_PID_FILE_PATH "/ntpd.pid"); while (!bb_got_signal) { llist_t *item; unsigned i, j; int nfds, timeout; double nextaction; /* Nothing between here and poll() blocks for any significant time */ nextaction = G.cur_time + 3600; i = 0; #if ENABLE_FEATURE_NTPD_SERVER if (G_listen_fd != -1) { pfd[0].fd = G_listen_fd; pfd[0].events = POLLIN; i++; } #endif /* Pass over peer list, send requests, time out on receives */ for (item = G.ntp_peers; item != NULL; item = item->link) { peer_t *p = (peer_t *) item->data; if (p->next_action_time <= G.cur_time) { if (p->p_fd == -1) { /* Time to send new req */ if (--cnt == 0) { G.initial_poll_complete = 1; } send_query_to_peer(p); } else { /* Timed out waiting for reply */ close(p->p_fd); p->p_fd = -1; timeout = poll_interval(-2); /* -2: try a bit sooner */ bb_error_msg("timed out waiting for %s, reach 0x%02x, next query in %us", p->p_dotted, p->reachable_bits, timeout); set_next(p, timeout); } } if (p->next_action_time < nextaction) nextaction = p->next_action_time; if (p->p_fd >= 0) { /* Wait for reply from this peer */ pfd[i].fd = p->p_fd; pfd[i].events = POLLIN; idx2peer[i] = p; i++; } } timeout = nextaction - G.cur_time; if (timeout < 0) timeout = 0; timeout++; /* (nextaction - G.cur_time) rounds down, compensating */ /* Here we may block */ VERB2 { if (i > (ENABLE_FEATURE_NTPD_SERVER && G_listen_fd != -1)) { /* We wait for at least one reply. * Poll for it, without wasting time for message. * Since replies often come under 1 second, this also * reduces clutter in logs. */ nfds = poll(pfd, i, 1000); if (nfds != 0) goto did_poll; if (--timeout <= 0) goto did_poll; } bb_error_msg("poll:%us sockets:%u interval:%us", timeout, i, 1 << G.poll_exp); } nfds = poll(pfd, i, timeout * 1000); did_poll: gettime1900d(); /* sets G.cur_time */ if (nfds <= 0) { if (!bb_got_signal /* poll wasn't interrupted by a signal */ && G.cur_time - G.last_script_run > 11*60 ) { /* Useful for updating battery-backed RTC and such */ run_script("periodic", G.last_update_offset); gettime1900d(); /* sets G.cur_time */ } goto check_unsync; } /* Process any received packets */ j = 0; #if ENABLE_FEATURE_NTPD_SERVER if (G.listen_fd != -1) { if (pfd[0].revents /* & (POLLIN|POLLERR)*/) { nfds--; recv_and_process_client_pkt(/*G.listen_fd*/); gettime1900d(); /* sets G.cur_time */ } j = 1; } #endif for (; nfds != 0 && j < i; j++) { if (pfd[j].revents /* & (POLLIN|POLLERR)*/) { /* * At init, alarm was set to 10 sec. * Now we did get a reply. * Increase timeout to 50 seconds to finish syncing. */ if (option_mask32 & OPT_qq) { option_mask32 &= ~OPT_qq; alarm(50); } nfds--; recv_and_process_peer_pkt(idx2peer[j]); gettime1900d(); /* sets G.cur_time */ } } check_unsync: if (G.ntp_peers && G.stratum != MAXSTRAT) { for (item = G.ntp_peers; item != NULL; item = item->link) { peer_t *p = (peer_t *) item->data; if (p->reachable_bits) goto have_reachable_peer; } /* No peer responded for last 8 packets, panic */ G.polladj_count = 0; G.poll_exp = MINPOLL; G.stratum = MAXSTRAT; run_script("unsync", 0.0); have_reachable_peer: ; } } /* while (!bb_got_signal) */ remove_pidfile(CONFIG_PID_FILE_PATH "/ntpd.pid"); kill_myself_with_sig(bb_got_signal); } /*** openntpd-4.6 uses only adjtime, not adjtimex ***/ /*** ntp-4.2.6/ntpd/ntp_loopfilter.c - adjtimex usage ***/ #if 0 static double direct_freq(double fp_offset) { #ifdef KERNEL_PLL /* * If the kernel is enabled, we need the residual offset to * calculate the frequency correction. */ if (pll_control && kern_enable) { memset(&ntv, 0, sizeof(ntv)); ntp_adjtime(&ntv); #ifdef STA_NANO clock_offset = ntv.offset / 1e9; #else /* STA_NANO */ clock_offset = ntv.offset / 1e6; #endif /* STA_NANO */ drift_comp = FREQTOD(ntv.freq); } #endif /* KERNEL_PLL */ set_freq((fp_offset - clock_offset) / (current_time - clock_epoch) + drift_comp); wander_resid = 0; return drift_comp; } static void set_freq(double freq) /* frequency update */ { char tbuf[80]; drift_comp = freq; #ifdef KERNEL_PLL /* * If the kernel is enabled, update the kernel frequency. */ if (pll_control && kern_enable) { memset(&ntv, 0, sizeof(ntv)); ntv.modes = MOD_FREQUENCY; ntv.freq = DTOFREQ(drift_comp); ntp_adjtime(&ntv); snprintf(tbuf, sizeof(tbuf), "kernel %.3f PPM", drift_comp * 1e6); report_event(EVNT_FSET, NULL, tbuf); } else { snprintf(tbuf, sizeof(tbuf), "ntpd %.3f PPM", drift_comp * 1e6); report_event(EVNT_FSET, NULL, tbuf); } #else /* KERNEL_PLL */ snprintf(tbuf, sizeof(tbuf), "ntpd %.3f PPM", drift_comp * 1e6); report_event(EVNT_FSET, NULL, tbuf); #endif /* KERNEL_PLL */ } ... ... ... #ifdef KERNEL_PLL /* * This code segment works when clock adjustments are made using * precision time kernel support and the ntp_adjtime() system * call. This support is available in Solaris 2.6 and later, * Digital Unix 4.0 and later, FreeBSD, Linux and specially * modified kernels for HP-UX 9 and Ultrix 4. In the case of the * DECstation 5000/240 and Alpha AXP, additional kernel * modifications provide a true microsecond clock and nanosecond * clock, respectively. * * Important note: The kernel discipline is used only if the * step threshold is less than 0.5 s, as anything higher can * lead to overflow problems. This might occur if some misguided * lad set the step threshold to something ridiculous. */ if (pll_control && kern_enable) { #define MOD_BITS (MOD_OFFSET | MOD_MAXERROR | MOD_ESTERROR | MOD_STATUS | MOD_TIMECONST) /* * We initialize the structure for the ntp_adjtime() * system call. We have to convert everything to * microseconds or nanoseconds first. Do not update the * system variables if the ext_enable flag is set. In * this case, the external clock driver will update the * variables, which will be read later by the local * clock driver. Afterwards, remember the time and * frequency offsets for jitter and stability values and * to update the frequency file. */ memset(&ntv, 0, sizeof(ntv)); if (ext_enable) { ntv.modes = MOD_STATUS; } else { #ifdef STA_NANO ntv.modes = MOD_BITS | MOD_NANO; #else /* STA_NANO */ ntv.modes = MOD_BITS; #endif /* STA_NANO */ if (clock_offset < 0) dtemp = -.5; else dtemp = .5; #ifdef STA_NANO ntv.offset = (int32)(clock_offset * 1e9 + dtemp); ntv.constant = sys_poll; #else /* STA_NANO */ ntv.offset = (int32)(clock_offset * 1e6 + dtemp); ntv.constant = sys_poll - 4; #endif /* STA_NANO */ ntv.esterror = (u_int32)(clock_jitter * 1e6); ntv.maxerror = (u_int32)((sys_rootdelay / 2 + sys_rootdisp) * 1e6); ntv.status = STA_PLL; /* * Enable/disable the PPS if requested. */ if (pps_enable) { if (!(pll_status & STA_PPSTIME)) report_event(EVNT_KERN, NULL, "PPS enabled"); ntv.status |= STA_PPSTIME | STA_PPSFREQ; } else { if (pll_status & STA_PPSTIME) report_event(EVNT_KERN, NULL, "PPS disabled"); ntv.status &= ~(STA_PPSTIME | STA_PPSFREQ); } if (sys_leap == LEAP_ADDSECOND) ntv.status |= STA_INS; else if (sys_leap == LEAP_DELSECOND) ntv.status |= STA_DEL; } /* * Pass the stuff to the kernel. If it squeals, turn off * the pps. In any case, fetch the kernel offset, * frequency and jitter. */ if (ntp_adjtime(&ntv) == TIME_ERROR) { if (!(ntv.status & STA_PPSSIGNAL)) report_event(EVNT_KERN, NULL, "PPS no signal"); } pll_status = ntv.status; #ifdef STA_NANO clock_offset = ntv.offset / 1e9; #else /* STA_NANO */ clock_offset = ntv.offset / 1e6; #endif /* STA_NANO */ clock_frequency = FREQTOD(ntv.freq); /* * If the kernel PPS is lit, monitor its performance. */ if (ntv.status & STA_PPSTIME) { #ifdef STA_NANO clock_jitter = ntv.jitter / 1e9; #else /* STA_NANO */ clock_jitter = ntv.jitter / 1e6; #endif /* STA_NANO */ } #if defined(STA_NANO) && NTP_API == 4 /* * If the TAI changes, update the kernel TAI. */ if (loop_tai != sys_tai) { loop_tai = sys_tai; ntv.modes = MOD_TAI; ntv.constant = sys_tai; ntp_adjtime(&ntv); } #endif /* STA_NANO */ } #endif /* KERNEL_PLL */ #endif busybox-1.22.1/networking/dnsd.c0000644000000000000000000004427012263563520015264 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini DNS server implementation for busybox * * Copyright (C) 2005 Roberto A. Foglietta (me@roberto.foglietta.name) * Copyright (C) 2005 Odd Arild Olsen (oao at fibula dot no) * Copyright (C) 2003 Paul Sheer * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Odd Arild Olsen started out with the sheerdns [1] of Paul Sheer and rewrote * it into a shape which I believe is both easier to understand and maintain. * I also reused the input buffer for output and removed services he did not * need. [1] http://threading.2038bug.com/sheerdns/ * * Some bugfix and minor changes was applied by Roberto A. Foglietta who made * the first porting of oao' scdns to busybox also. */ //usage:#define dnsd_trivial_usage //usage: "[-dvs] [-c CONFFILE] [-t TTL_SEC] [-p PORT] [-i ADDR]" //usage:#define dnsd_full_usage "\n\n" //usage: "Small static DNS server daemon\n" //usage: "\n -c FILE Config file" //usage: "\n -t SEC TTL" //usage: "\n -p PORT Listen on PORT" //usage: "\n -i ADDR Listen on ADDR" //usage: "\n -d Daemonize" //usage: "\n -v Verbose" //usage: "\n -s Send successful replies only. Use this if you want" //usage: "\n to use /etc/resolv.conf with two nameserver lines:" //usage: "\n nameserver DNSD_SERVER" //usage: "\n nameserver NORMAL_DNS_SERVER" #include "libbb.h" #include //#define DEBUG 1 #define DEBUG 0 enum { /* can tweak this */ DEFAULT_TTL = 120, /* cannot get bigger packets than 512 per RFC1035. */ MAX_PACK_LEN = 512, IP_STRING_LEN = sizeof(".xxx.xxx.xxx.xxx"), MAX_NAME_LEN = IP_STRING_LEN - 1 + sizeof(".in-addr.arpa"), REQ_A = 1, REQ_PTR = 12, }; /* the message from client and first part of response msg */ struct dns_head { uint16_t id; uint16_t flags; uint16_t nquer; uint16_t nansw; uint16_t nauth; uint16_t nadd; }; /* Structure used to access type and class fields. * They are totally unaligned, but gcc 4.3.4 thinks that pointer of type uint16_t* * is 16-bit aligned and replaces 16-bit memcpy (in move_from_unaligned16 macro) * with aligned halfword access on arm920t! * Oh well. Slapping PACKED everywhere seems to help: */ struct type_and_class { uint16_t type PACKED; uint16_t class PACKED; } PACKED; /* element of known name, ip address and reversed ip address */ struct dns_entry { struct dns_entry *next; uint32_t ip; char rip[IP_STRING_LEN]; /* length decimal reversed IP */ char name[1]; }; #define OPT_verbose (option_mask32 & 1) #define OPT_silent (option_mask32 & 2) /* * Insert length of substrings instead of dots */ static void undot(char *rip) { int i = 0; int s = 0; while (rip[i]) i++; for (--i; i >= 0; i--) { if (rip[i] == '.') { rip[i] = s; s = 0; } else { s++; } } } /* * Read hostname/IP records from file */ static struct dns_entry *parse_conf_file(const char *fileconf) { char *token[2]; parser_t *parser; struct dns_entry *m, *conf_data; struct dns_entry **nextp; conf_data = NULL; nextp = &conf_data; parser = config_open(fileconf); while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) { struct in_addr ip; uint32_t v32; if (inet_aton(token[1], &ip) == 0) { bb_error_msg("error at line %u, skipping", parser->lineno); continue; } if (OPT_verbose) bb_error_msg("name:%s, ip:%s", token[0], token[1]); /* sizeof(*m) includes 1 byte for m->name[0] */ m = xzalloc(sizeof(*m) + strlen(token[0]) + 1); /*m->next = NULL;*/ *nextp = m; nextp = &m->next; m->name[0] = '.'; strcpy(m->name + 1, token[0]); undot(m->name); m->ip = ip.s_addr; /* in network order */ v32 = ntohl(m->ip); /* inverted order */ sprintf(m->rip, ".%u.%u.%u.%u", (uint8_t)(v32), (uint8_t)(v32 >> 8), (uint8_t)(v32 >> 16), (v32 >> 24) ); undot(m->rip); } config_close(parser); return conf_data; } /* * Look query up in dns records and return answer if found. */ static char *table_lookup(struct dns_entry *d, uint16_t type, char* query_string) { while (d) { unsigned len = d->name[0]; /* d->name[len] is the last (non NUL) char */ #if DEBUG char *p, *q; q = query_string + 1; p = d->name + 1; fprintf(stderr, "%d/%d p:%s q:%s %d\n", (int)strlen(p), len, p, q, (int)strlen(q) ); #endif if (type == htons(REQ_A)) { /* search by host name */ if (len != 1 || d->name[1] != '*') { /* we are lax, hope no name component is ever >64 so that length * (which will be represented as 'A','B'...) matches a lowercase letter. * Actually, I think false matches are hard to construct. * Example. * [31] len is represented as '1', [65] as 'A', [65+32] as 'a'. * [65] <65 same chars>[31]<31 same chars>NUL * [65+32]<65 same chars>1 <31 same chars>NUL * This example seems to be the minimal case when false match occurs. */ if (strcasecmp(d->name, query_string) != 0) goto next; } return (char *)&d->ip; #if DEBUG fprintf(stderr, "Found IP:%x\n", (int)d->ip); #endif return 0; } /* search by IP-address */ if ((len != 1 || d->name[1] != '*') /* we assume (do not check) that query_string * ends in ".in-addr.arpa" */ && strncmp(d->rip, query_string, strlen(d->rip)) == 0 ) { #if DEBUG fprintf(stderr, "Found name:%s\n", d->name); #endif return d->name; } next: d = d->next; } return NULL; } /* * Decode message and generate answer */ /* RFC 1035 ... Whenever an octet represents a numeric quantity, the left most bit in the diagram is the high order or most significant bit. That is, the bit labeled 0 is the most significant bit. ... 4.1.1. Header section format 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ID | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |QR| OPCODE |AA|TC|RD|RA| 0 0 0| RCODE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QDCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ANCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | NSCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ARCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ ID 16 bit random identifier assigned by querying peer. Used to match query/response. QR message is a query (0), or a response (1). OPCODE 0 standard query (QUERY) 1 inverse query (IQUERY) 2 server status request (STATUS) AA Authoritative Answer - this bit is valid in responses. Responding name server is an authority for the domain name in question section. Answer section may have multiple owner names because of aliases. The AA bit corresponds to the name which matches the query name, or the first owner name in the answer section. TC TrunCation - this message was truncated. RD Recursion Desired - this bit may be set in a query and is copied into the response. If RD is set, it directs the name server to pursue the query recursively. Recursive query support is optional. RA Recursion Available - this be is set or cleared in a response, and denotes whether recursive query support is available in the name server. RCODE Response code. 0 No error condition 1 Format error 2 Server failure - server was unable to process the query due to a problem with the name server. 3 Name Error - meaningful only for responses from an authoritative name server. The referenced domain name does not exist. 4 Not Implemented. 5 Refused. QDCOUNT number of entries in the question section. ANCOUNT number of records in the answer section. NSCOUNT number of records in the authority records section. ARCOUNT number of records in the additional records section. 4.1.2. Question section format The section contains QDCOUNT (usually 1) entries, each of this format: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ / QNAME / / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QTYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QCLASS | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ QNAME a domain name represented as a sequence of labels, where each label consists of a length octet followed by that number of octets. The domain name terminates with the zero length octet for the null label of the root. Note that this field may be an odd number of octets; no padding is used. QTYPE a two octet type of the query. 1 a host address [REQ_A const] 2 an authoritative name server 3 a mail destination (Obsolete - use MX) 4 a mail forwarder (Obsolete - use MX) 5 the canonical name for an alias 6 marks the start of a zone of authority 7 a mailbox domain name (EXPERIMENTAL) 8 a mail group member (EXPERIMENTAL) 9 a mail rename domain name (EXPERIMENTAL) 10 a null RR (EXPERIMENTAL) 11 a well known service description 12 a domain name pointer [REQ_PTR const] 13 host information 14 mailbox or mail list information 15 mail exchange 16 text strings 0x1c IPv6? 252 a request for a transfer of an entire zone 253 a request for mailbox-related records (MB, MG or MR) 254 a request for mail agent RRs (Obsolete - see MX) 255 a request for all records QCLASS a two octet code that specifies the class of the query. 1 the Internet (others are historic only) 255 any class 4.1.3. Resource Record format The answer, authority, and additional sections all share the same format: a variable number of resource records, where the number of records is specified in the corresponding count field in the header. Each resource record has this format: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ / / / NAME / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | CLASS | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TTL | | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | RDLENGTH | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| / RDATA / / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ NAME a domain name to which this resource record pertains. TYPE two octets containing one of the RR type codes. This field specifies the meaning of the data in the RDATA field. CLASS two octets which specify the class of the data in the RDATA field. TTL a 32 bit unsigned integer that specifies the time interval (in seconds) that the record may be cached. RDLENGTH a 16 bit integer, length in octets of the RDATA field. RDATA a variable length string of octets that describes the resource. The format of this information varies according to the TYPE and CLASS of the resource record. If the TYPE is A and the CLASS is IN, it's a 4 octet IP address. 4.1.4. Message compression In order to reduce the size of messages, domain names coan be compressed. An entire domain name or a list of labels at the end of a domain name is replaced with a pointer to a prior occurance of the same name. The pointer takes the form of a two octet sequence: +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 1| OFFSET | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The first two bits are ones. This allows a pointer to be distinguished from a label, since the label must begin with two zero bits because labels are restricted to 63 octets or less. The OFFSET field specifies an offset from the start of the message (i.e., the first octet of the ID field in the domain header). A zero offset specifies the first byte of the ID field, etc. Domain name in a message can be represented as either: - a sequence of labels ending in a zero octet - a pointer - a sequence of labels ending with a pointer */ static int process_packet(struct dns_entry *conf_data, uint32_t conf_ttl, uint8_t *buf) { struct dns_head *head; struct type_and_class *unaligned_type_class; const char *err_msg; char *query_string; char *answstr; uint8_t *answb; uint16_t outr_rlen; uint16_t outr_flags; uint16_t type; uint16_t class; int query_len; head = (struct dns_head *)buf; if (head->nquer == 0) { bb_error_msg("packet has 0 queries, ignored"); return 0; /* don't reply */ } if (head->flags & htons(0x8000)) { /* QR bit */ bb_error_msg("response packet, ignored"); return 0; /* don't reply */ } /* QR = 1 "response", RCODE = 4 "Not Implemented" */ outr_flags = htons(0x8000 | 4); err_msg = NULL; /* start of query string */ query_string = (void *)(head + 1); /* caller guarantees strlen is <= MAX_PACK_LEN */ query_len = strlen(query_string) + 1; /* may be unaligned! */ unaligned_type_class = (void *)(query_string + query_len); query_len += sizeof(*unaligned_type_class); /* where to append answer block */ answb = (void *)(unaligned_type_class + 1); /* OPCODE != 0 "standard query"? */ if ((head->flags & htons(0x7800)) != 0) { err_msg = "opcode != 0"; goto empty_packet; } move_from_unaligned16(class, &unaligned_type_class->class); if (class != htons(1)) { /* not class INET? */ err_msg = "class != 1"; goto empty_packet; } move_from_unaligned16(type, &unaligned_type_class->type); if (type != htons(REQ_A) && type != htons(REQ_PTR)) { /* we can't handle this query type */ //TODO: happens all the time with REQ_AAAA (0x1c) requests - implement those? err_msg = "type is !REQ_A and !REQ_PTR"; goto empty_packet; } /* look up the name */ answstr = table_lookup(conf_data, type, query_string); #if DEBUG /* Shows lengths instead of dots, unusable for !DEBUG */ bb_error_msg("'%s'->'%s'", query_string, answstr); #endif outr_rlen = 4; if (answstr && type == htons(REQ_PTR)) { /* returning a host name */ outr_rlen = strlen(answstr) + 1; } if (!answstr || (unsigned)(answb - buf) + query_len + 4 + 2 + outr_rlen > MAX_PACK_LEN ) { /* QR = 1 "response" * AA = 1 "Authoritative Answer" * RCODE = 3 "Name Error" */ err_msg = "name is not found"; outr_flags = htons(0x8000 | 0x0400 | 3); goto empty_packet; } /* Append answer Resource Record */ memcpy(answb, query_string, query_len); /* name, type, class */ answb += query_len; move_to_unaligned32((uint32_t *)answb, htonl(conf_ttl)); answb += 4; move_to_unaligned16((uint16_t *)answb, htons(outr_rlen)); answb += 2; memcpy(answb, answstr, outr_rlen); answb += outr_rlen; /* QR = 1 "response", * AA = 1 "Authoritative Answer", * TODO: need to set RA bit 0x80? One user says nslookup complains * "Got recursion not available from SERVER, trying next server" * "** server can't find HOSTNAME" * RCODE = 0 "success" */ if (OPT_verbose) bb_error_msg("returning positive reply"); outr_flags = htons(0x8000 | 0x0400 | 0); /* we have one answer */ head->nansw = htons(1); empty_packet: if ((outr_flags & htons(0xf)) != 0) { /* not a positive response */ if (OPT_verbose) { bb_error_msg("%s, %s", err_msg, OPT_silent ? "dropping query" : "sending error reply" ); } if (OPT_silent) return 0; } head->flags |= outr_flags; head->nauth = head->nadd = 0; head->nquer = htons(1); // why??? return answb - buf; } int dnsd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int dnsd_main(int argc UNUSED_PARAM, char **argv) { const char *listen_interface = "0.0.0.0"; const char *fileconf = "/etc/dnsd.conf"; struct dns_entry *conf_data; uint32_t conf_ttl = DEFAULT_TTL; char *sttl, *sport; len_and_sockaddr *lsa, *from, *to; unsigned lsa_size; int udps, opts; uint16_t port = 53; /* Ensure buf is 32bit aligned (we need 16bit, but 32bit can't hurt) */ uint8_t buf[MAX_PACK_LEN + 1] ALIGN4; opts = getopt32(argv, "vsi:c:t:p:d", &listen_interface, &fileconf, &sttl, &sport); //if (opts & (1 << 0)) // -v //if (opts & (1 << 1)) // -s //if (opts & (1 << 2)) // -i //if (opts & (1 << 3)) // -c if (opts & (1 << 4)) // -t conf_ttl = xatou_range(sttl, 1, 0xffffffff); if (opts & (1 << 5)) // -p port = xatou_range(sport, 1, 0xffff); if (opts & (1 << 6)) { // -d bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); openlog(applet_name, LOG_PID, LOG_DAEMON); logmode = LOGMODE_SYSLOG; } conf_data = parse_conf_file(fileconf); lsa = xdotted2sockaddr(listen_interface, port); udps = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0); xbind(udps, &lsa->u.sa, lsa->len); socket_want_pktinfo(udps); /* needed for recv_from_to to work */ lsa_size = LSA_LEN_SIZE + lsa->len; from = xzalloc(lsa_size); to = xzalloc(lsa_size); { char *p = xmalloc_sockaddr2dotted(&lsa->u.sa); bb_error_msg("accepting UDP packets on %s", p); free(p); } while (1) { int r; /* Try to get *DEST* address (to which of our addresses * this query was directed), and reply from the same address. * Or else we can exhibit usual UDP ugliness: * [ip1.multihomed.ip2] <= query to ip1 <= peer * [ip1.multihomed.ip2] => reply from ip2 => peer (confused) */ memcpy(to, lsa, lsa_size); r = recv_from_to(udps, buf, MAX_PACK_LEN + 1, 0, &from->u.sa, &to->u.sa, lsa->len); if (r < 12 || r > MAX_PACK_LEN) { bb_error_msg("packet size %d, ignored", r); continue; } if (OPT_verbose) bb_error_msg("got UDP packet"); buf[r] = '\0'; /* paranoia */ r = process_packet(conf_data, conf_ttl, buf); if (r <= 0) continue; send_to_from(udps, buf, r, 0, &from->u.sa, &to->u.sa, lsa->len); } return 0; } busybox-1.22.1/networking/inetd.c0000644000000000000000000014235012263563520015435 0ustar rootroot/* vi: set sw=4 ts=4: */ /* $Slackware: inetd.c 1.79s 2001/02/06 13:18:00 volkerdi Exp $ */ /* $OpenBSD: inetd.c,v 1.79 2001/01/30 08:30:57 deraadt Exp $ */ /* $NetBSD: inetd.c,v 1.11 1996/02/22 11:14:41 mycroft Exp $ */ /* Busybox port by Vladimir Oleynik (C) 2001-2005 */ /* IPv6 support, many bug fixes by Denys Vlasenko (c) 2008 */ /* * Copyright (c) 1983,1991 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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. */ /* Inetd - Internet super-server * * This program invokes configured services when a connection * from a peer is established or a datagram arrives. * Connection-oriented services are invoked each time a * connection is made, by creating a process. This process * is passed the connection as file descriptor 0 and is * expected to do a getpeername to find out peer's host * and port. * Datagram oriented services are invoked when a datagram * arrives; a process is created and passed a pending message * on file descriptor 0. peer's address can be obtained * using recvfrom. * * Inetd uses a configuration file which is read at startup * and, possibly, at some later time in response to a hangup signal. * The configuration file is "free format" with fields given in the * order shown below. Continuation lines for an entry must begin with * a space or tab. All fields must be present in each entry. * * service_name must be in /etc/services * socket_type stream/dgram/raw/rdm/seqpacket * protocol must be in /etc/protocols * (usually "tcp" or "udp") * wait/nowait[.max] single-threaded/multi-threaded, max # * user[.group] or user[:group] user/group to run daemon as * server_program full path name * server_program_arguments maximum of MAXARGS (20) * * For RPC services * service_name/version must be in /etc/rpc * socket_type stream/dgram/raw/rdm/seqpacket * rpc/protocol "rpc/tcp" etc * wait/nowait[.max] single-threaded/multi-threaded * user[.group] or user[:group] user to run daemon as * server_program full path name * server_program_arguments maximum of MAXARGS (20) * * For non-RPC services, the "service name" can be of the form * hostaddress:servicename, in which case the hostaddress is used * as the host portion of the address to listen on. If hostaddress * consists of a single '*' character, INADDR_ANY is used. * * A line can also consist of just * hostaddress: * where hostaddress is as in the preceding paragraph. Such a line must * have no further fields; the specified hostaddress is remembered and * used for all further lines that have no hostaddress specified, * until the next such line (or EOF). (This is why * is provided to * allow explicit specification of INADDR_ANY.) A line * *: * is implicitly in effect at the beginning of the file. * * The hostaddress specifier may (and often will) contain dots; * the service name must not. * * For RPC services, host-address specifiers are accepted and will * work to some extent; however, because of limitations in the * portmapper interface, it will not work to try to give more than * one line for any given RPC service, even if the host-address * specifiers are different. * * Comment lines are indicated by a '#' in column 1. */ /* inetd rules for passing file descriptors to children * (http://www.freebsd.org/cgi/man.cgi?query=inetd): * * The wait/nowait entry specifies whether the server that is invoked by * inetd will take over the socket associated with the service access point, * and thus whether inetd should wait for the server to exit before listen- * ing for new service requests. Datagram servers must use "wait", as * they are always invoked with the original datagram socket bound to the * specified service address. These servers must read at least one datagram * from the socket before exiting. If a datagram server connects to its * peer, freeing the socket so inetd can receive further messages on the * socket, it is said to be a "multi-threaded" server; it should read one * datagram from the socket and create a new socket connected to the peer. * It should fork, and the parent should then exit to allow inetd to check * for new service requests to spawn new servers. Datagram servers which * process all incoming datagrams on a socket and eventually time out are * said to be "single-threaded". The comsat(8), biff(1) and talkd(8) * utilities are both examples of the latter type of datagram server. The * tftpd(8) utility is an example of a multi-threaded datagram server. * * Servers using stream sockets generally are multi-threaded and use the * "nowait" entry. Connection requests for these services are accepted by * inetd, and the server is given only the newly-accepted socket connected * to a client of the service. Most stream-based services operate in this * manner. Stream-based servers that use "wait" are started with the lis- * tening service socket, and must accept at least one connection request * before exiting. Such a server would normally accept and process incoming * connection requests until a timeout. */ /* Despite of above doc saying that dgram services must use "wait", * "udp nowait" servers are implemented in busyboxed inetd. * IPv6 addresses are also implemented. However, they may look ugly - * ":::service..." means "address '::' (IPv6 wildcard addr)":"service"... * You have to put "tcp6"/"udp6" in protocol field to select IPv6. */ /* Here's the scoop concerning the user[:group] feature: * 1) group is not specified: * a) user = root: NO setuid() or setgid() is done * b) other: initgroups(name, primary group) * setgid(primary group as found in passwd) * setuid() * 2) group is specified: * a) user = root: setgid(specified group) * NO initgroups() * NO setuid() * b) other: initgroups(name, specified group) * setgid(specified group) * setuid() */ //usage:#define inetd_trivial_usage //usage: "[-fe] [-q N] [-R N] [CONFFILE]" //usage:#define inetd_full_usage "\n\n" //usage: "Listen for network connections and launch programs\n" //usage: "\n -f Run in foreground" //usage: "\n -e Log to stderr" //usage: "\n -q N Socket listen queue (default: 128)" //usage: "\n -R N Pause services after N connects/min" //usage: "\n (default: 0 - disabled)" #include #include /* setrlimit */ #include /* un.h may need this */ #include #include "libbb.h" #if ENABLE_FEATURE_INETD_RPC # if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) # error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support" # endif # include # include #endif #if !BB_MMU /* stream version of chargen is forking but not execing, * can't do that (easily) on NOMMU */ #undef ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN #define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN 0 #endif #define CNT_INTERVAL 60 /* servers in CNT_INTERVAL sec. */ #define RETRYTIME 60 /* retry after bind or server fail */ // TODO: explain, or get rid of setrlimit games #ifndef RLIMIT_NOFILE #define RLIMIT_NOFILE RLIMIT_OFILE #endif #ifndef OPEN_MAX #define OPEN_MAX 64 #endif /* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */ #define FD_MARGIN 8 #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD \ || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO \ || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN \ || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME \ || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME # define INETD_BUILTINS_ENABLED #endif typedef struct servtab_t { /* The most frequently referenced one: */ int se_fd; /* open descriptor */ /* NB: 'biggest fields last' saves on code size (~250 bytes) */ /* [addr:]service socktype proto wait user[:group] prog [args] */ char *se_local_hostname; /* addr to listen on */ char *se_service; /* "80" or "www" or "mount/2[-3]" */ /* socktype is in se_socktype */ /* "stream" "dgram" "raw" "rdm" "seqpacket" */ char *se_proto; /* "unix" or "[rpc/]tcp[6]" */ #if ENABLE_FEATURE_INETD_RPC int se_rpcprog; /* rpc program number */ int se_rpcver_lo; /* rpc program lowest version */ int se_rpcver_hi; /* rpc program highest version */ #define is_rpc_service(sep) ((sep)->se_rpcver_lo != 0) #else #define is_rpc_service(sep) 0 #endif pid_t se_wait; /* 0:"nowait", 1:"wait", >1:"wait" */ /* and waiting for this pid */ socktype_t se_socktype; /* SOCK_STREAM/DGRAM/RDM/... */ family_t se_family; /* AF_UNIX/INET[6] */ /* se_proto_no is used by RPC code only... hmm */ smallint se_proto_no; /* IPPROTO_TCP/UDP, n/a for AF_UNIX */ smallint se_checked; /* looked at during merge */ unsigned se_max; /* allowed instances per minute */ unsigned se_count; /* number started since se_time */ unsigned se_time; /* when we started counting */ char *se_user; /* user name to run as */ char *se_group; /* group name to run as, can be NULL */ #ifdef INETD_BUILTINS_ENABLED const struct builtin *se_builtin; /* if built-in, description */ #endif struct servtab_t *se_next; len_and_sockaddr *se_lsa; char *se_program; /* server program */ #define MAXARGV 20 char *se_argv[MAXARGV + 1]; /* program arguments */ } servtab_t; #ifdef INETD_BUILTINS_ENABLED /* Echo received data */ #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO static void FAST_FUNC echo_stream(int, servtab_t *); static void FAST_FUNC echo_dg(int, servtab_t *); #endif /* Internet /dev/null */ #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD static void FAST_FUNC discard_stream(int, servtab_t *); static void FAST_FUNC discard_dg(int, servtab_t *); #endif /* Return 32 bit time since 1900 */ #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME static void FAST_FUNC machtime_stream(int, servtab_t *); static void FAST_FUNC machtime_dg(int, servtab_t *); #endif /* Return human-readable time */ #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME static void FAST_FUNC daytime_stream(int, servtab_t *); static void FAST_FUNC daytime_dg(int, servtab_t *); #endif /* Familiar character generator */ #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN static void FAST_FUNC chargen_stream(int, servtab_t *); static void FAST_FUNC chargen_dg(int, servtab_t *); #endif struct builtin { /* NB: not necessarily NUL terminated */ char bi_service7[7]; /* internally provided service name */ uint8_t bi_fork; /* 1 if stream fn should run in child */ void (*bi_stream_fn)(int, servtab_t *) FAST_FUNC; void (*bi_dgram_fn)(int, servtab_t *) FAST_FUNC; }; static const struct builtin builtins[] = { #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO { "echo", 1, echo_stream, echo_dg }, #endif #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD { "discard", 1, discard_stream, discard_dg }, #endif #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN { "chargen", 1, chargen_stream, chargen_dg }, #endif #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME { "time", 0, machtime_stream, machtime_dg }, #endif #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME { "daytime", 0, daytime_stream, daytime_dg }, #endif }; #endif /* INETD_BUILTINS_ENABLED */ struct globals { rlim_t rlim_ofile_cur; struct rlimit rlim_ofile; servtab_t *serv_list; int global_queuelen; int maxsock; /* max fd# in allsock, -1: unknown */ /* whenever maxsock grows, prev_maxsock is set to new maxsock, * but if maxsock is set to -1, prev_maxsock is not changed */ int prev_maxsock; unsigned max_concurrency; smallint alarm_armed; uid_t real_uid; /* user ID who ran us */ const char *config_filename; parser_t *parser; char *default_local_hostname; #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN char *end_ring; char *ring_pos; char ring[128]; #endif fd_set allsock; /* Used in next_line(), and as scratch read buffer */ char line[256]; /* _at least_ 256, see LINE_SIZE */ } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) enum { LINE_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line) }; struct BUG_G_too_big { char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1]; }; #define rlim_ofile_cur (G.rlim_ofile_cur ) #define rlim_ofile (G.rlim_ofile ) #define serv_list (G.serv_list ) #define global_queuelen (G.global_queuelen) #define maxsock (G.maxsock ) #define prev_maxsock (G.prev_maxsock ) #define max_concurrency (G.max_concurrency) #define alarm_armed (G.alarm_armed ) #define real_uid (G.real_uid ) #define config_filename (G.config_filename) #define parser (G.parser ) #define default_local_hostname (G.default_local_hostname) #define first_ps_byte (G.first_ps_byte ) #define last_ps_byte (G.last_ps_byte ) #define end_ring (G.end_ring ) #define ring_pos (G.ring_pos ) #define ring (G.ring ) #define allsock (G.allsock ) #define line (G.line ) #define INIT_G() do { \ rlim_ofile_cur = OPEN_MAX; \ global_queuelen = 128; \ config_filename = "/etc/inetd.conf"; \ } while (0) #if 1 # define dbg(...) ((void)0) #else # define dbg(...) \ do { \ int dbg_fd = open("inetd_debug.log", O_WRONLY | O_CREAT | O_APPEND, 0666); \ if (dbg_fd >= 0) { \ fdprintf(dbg_fd, "%d: ", getpid()); \ fdprintf(dbg_fd, __VA_ARGS__); \ close(dbg_fd); \ } \ } while (0) #endif static void maybe_close(int fd) { if (fd >= 0) { close(fd); dbg("closed fd:%d\n", fd); } } // TODO: move to libbb? static len_and_sockaddr *xzalloc_lsa(int family) { len_and_sockaddr *lsa; int sz; sz = sizeof(struct sockaddr_in); if (family == AF_UNIX) sz = sizeof(struct sockaddr_un); #if ENABLE_FEATURE_IPV6 if (family == AF_INET6) sz = sizeof(struct sockaddr_in6); #endif lsa = xzalloc(LSA_LEN_SIZE + sz); lsa->len = sz; lsa->u.sa.sa_family = family; return lsa; } static void rearm_alarm(void) { if (!alarm_armed) { alarm_armed = 1; alarm(RETRYTIME); } } static void block_CHLD_HUP_ALRM(sigset_t *m) { sigemptyset(m); sigaddset(m, SIGCHLD); sigaddset(m, SIGHUP); sigaddset(m, SIGALRM); sigprocmask(SIG_BLOCK, m, m); /* old sigmask is stored in m */ } static void restore_sigmask(sigset_t *m) { sigprocmask(SIG_SETMASK, m, NULL); } #if ENABLE_FEATURE_INETD_RPC static void register_rpc(servtab_t *sep) { int n; struct sockaddr_in ir_sin; socklen_t size; size = sizeof(ir_sin); if (getsockname(sep->se_fd, (struct sockaddr *) &ir_sin, &size) < 0) { bb_perror_msg("getsockname"); return; } for (n = sep->se_rpcver_lo; n <= sep->se_rpcver_hi; n++) { pmap_unset(sep->se_rpcprog, n); if (!pmap_set(sep->se_rpcprog, n, sep->se_proto_no, ntohs(ir_sin.sin_port))) bb_perror_msg("%s %s: pmap_set(%u,%u,%u,%u)", sep->se_service, sep->se_proto, sep->se_rpcprog, n, sep->se_proto_no, ntohs(ir_sin.sin_port)); } } static void unregister_rpc(servtab_t *sep) { int n; for (n = sep->se_rpcver_lo; n <= sep->se_rpcver_hi; n++) { if (!pmap_unset(sep->se_rpcprog, n)) bb_perror_msg("pmap_unset(%u,%u)", sep->se_rpcprog, n); } } #endif /* FEATURE_INETD_RPC */ static void bump_nofile(void) { enum { FD_CHUNK = 32 }; struct rlimit rl; /* Never fails under Linux (except if you pass it bad arguments) */ getrlimit(RLIMIT_NOFILE, &rl); rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK); rl.rlim_cur = MIN(FD_SETSIZE, rl.rlim_cur + FD_CHUNK); if (rl.rlim_cur <= rlim_ofile_cur) { bb_error_msg("can't extend file limit, max = %d", (int) rl.rlim_cur); return; } if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { bb_perror_msg("setrlimit"); return; } rlim_ofile_cur = rl.rlim_cur; } static void remove_fd_from_set(int fd) { if (fd >= 0) { FD_CLR(fd, &allsock); dbg("stopped listening on fd:%d\n", fd); maxsock = -1; dbg("maxsock:%d\n", maxsock); } } static void add_fd_to_set(int fd) { if (fd >= 0) { FD_SET(fd, &allsock); dbg("started listening on fd:%d\n", fd); if (maxsock >= 0 && fd > maxsock) { prev_maxsock = maxsock = fd; dbg("maxsock:%d\n", maxsock); if ((rlim_t)fd > rlim_ofile_cur - FD_MARGIN) bump_nofile(); } } } static void recalculate_maxsock(void) { int fd = 0; /* We may have no services, in this case maxsock should still be >= 0 * (code elsewhere is not happy with maxsock == -1) */ maxsock = 0; while (fd <= prev_maxsock) { if (FD_ISSET(fd, &allsock)) maxsock = fd; fd++; } dbg("recalculated maxsock:%d\n", maxsock); prev_maxsock = maxsock; if ((rlim_t)maxsock > rlim_ofile_cur - FD_MARGIN) bump_nofile(); } static void prepare_socket_fd(servtab_t *sep) { int r, fd; fd = socket(sep->se_family, sep->se_socktype, 0); if (fd < 0) { bb_perror_msg("socket"); return; } setsockopt_reuseaddr(fd); #if ENABLE_FEATURE_INETD_RPC if (is_rpc_service(sep)) { struct passwd *pwd; /* zero out the port for all RPC services; let bind() * find one. */ set_nport(&sep->se_lsa->u.sa, 0); /* for RPC services, attempt to use a reserved port * if they are going to be running as root. */ if (real_uid == 0 && sep->se_family == AF_INET && (pwd = getpwnam(sep->se_user)) != NULL && pwd->pw_uid == 0 ) { r = bindresvport(fd, &sep->se_lsa->u.sin); } else { r = bind(fd, &sep->se_lsa->u.sa, sep->se_lsa->len); } if (r == 0) { int saveerrno = errno; /* update lsa with port# */ getsockname(fd, &sep->se_lsa->u.sa, &sep->se_lsa->len); errno = saveerrno; } } else #endif { if (sep->se_family == AF_UNIX) { struct sockaddr_un *sun; sun = (struct sockaddr_un*)&(sep->se_lsa->u.sa); unlink(sun->sun_path); } r = bind(fd, &sep->se_lsa->u.sa, sep->se_lsa->len); } if (r < 0) { bb_perror_msg("%s/%s: bind", sep->se_service, sep->se_proto); close(fd); rearm_alarm(); return; } if (sep->se_socktype == SOCK_STREAM) { listen(fd, global_queuelen); dbg("new sep->se_fd:%d (stream)\n", fd); } else { dbg("new sep->se_fd:%d (!stream)\n", fd); } add_fd_to_set(fd); sep->se_fd = fd; } static int reopen_config_file(void) { free(default_local_hostname); default_local_hostname = xstrdup("*"); if (parser != NULL) config_close(parser); parser = config_open(config_filename); return (parser != NULL); } static void close_config_file(void) { if (parser) { config_close(parser); parser = NULL; } } static void free_servtab_strings(servtab_t *cp) { int i; free(cp->se_local_hostname); free(cp->se_service); free(cp->se_proto); free(cp->se_user); free(cp->se_group); free(cp->se_lsa); /* not a string in fact */ free(cp->se_program); for (i = 0; i < MAXARGV; i++) free(cp->se_argv[i]); } static servtab_t *new_servtab(void) { servtab_t *newtab = xzalloc(sizeof(servtab_t)); newtab->se_fd = -1; /* paranoia */ return newtab; } static servtab_t *dup_servtab(servtab_t *sep) { servtab_t *newtab; int argc; newtab = new_servtab(); *newtab = *sep; /* struct copy */ /* deep-copying strings */ newtab->se_service = xstrdup(newtab->se_service); newtab->se_proto = xstrdup(newtab->se_proto); newtab->se_user = xstrdup(newtab->se_user); newtab->se_group = xstrdup(newtab->se_group); newtab->se_program = xstrdup(newtab->se_program); for (argc = 0; argc <= MAXARGV; argc++) newtab->se_argv[argc] = xstrdup(newtab->se_argv[argc]); /* NB: se_fd, se_hostaddr and se_next are always * overwrittend by callers, so we don't bother resetting them * to NULL/0/-1 etc */ return newtab; } /* gcc generates much more code if this is inlined */ static servtab_t *parse_one_line(void) { int argc; char *token[6+MAXARGV]; char *p, *arg; char *hostdelim; servtab_t *sep; servtab_t *nsep; new: sep = new_servtab(); more: argc = config_read(parser, token, 6+MAXARGV, 1, "# \t", PARSE_NORMAL); if (!argc) { free(sep); return NULL; } /* [host:]service socktype proto wait user[:group] prog [args] */ /* Check for "host:...." line */ arg = token[0]; hostdelim = strrchr(arg, ':'); if (hostdelim) { *hostdelim = '\0'; sep->se_local_hostname = xstrdup(arg); arg = hostdelim + 1; if (*arg == '\0' && argc == 1) { /* Line has just "host:", change the * default host for the following lines. */ free(default_local_hostname); default_local_hostname = sep->se_local_hostname; goto more; } } else sep->se_local_hostname = xstrdup(default_local_hostname); /* service socktype proto wait user[:group] prog [args] */ sep->se_service = xstrdup(arg); /* socktype proto wait user[:group] prog [args] */ if (argc < 6) { parse_err: bb_error_msg("parse error on line %u, line is ignored", parser->lineno); free_servtab_strings(sep); /* Just "goto more" can make sep to carry over e.g. * "rpc"-ness (by having se_rpcver_lo != 0). * We will be more paranoid: */ free(sep); goto new; } { static const int8_t SOCK_xxx[] ALIGN1 = { -1, SOCK_STREAM, SOCK_DGRAM, SOCK_RDM, SOCK_SEQPACKET, SOCK_RAW }; sep->se_socktype = SOCK_xxx[1 + index_in_strings( "stream""\0" "dgram""\0" "rdm""\0" "seqpacket""\0" "raw""\0" , token[1])]; } /* {unix,[rpc/]{tcp,udp}[6]} wait user[:group] prog [args] */ sep->se_proto = arg = xstrdup(token[2]); if (strcmp(arg, "unix") == 0) { sep->se_family = AF_UNIX; } else { char *six; sep->se_family = AF_INET; six = last_char_is(arg, '6'); if (six) { #if ENABLE_FEATURE_IPV6 *six = '\0'; sep->se_family = AF_INET6; #else bb_error_msg("%s: no support for IPv6", sep->se_proto); goto parse_err; #endif } if (strncmp(arg, "rpc/", 4) == 0) { #if ENABLE_FEATURE_INETD_RPC unsigned n; arg += 4; p = strchr(sep->se_service, '/'); if (p == NULL) { bb_error_msg("no rpc version: '%s'", sep->se_service); goto parse_err; } *p++ = '\0'; n = bb_strtou(p, &p, 10); if (n > INT_MAX) { bad_ver_spec: bb_error_msg("bad rpc version"); goto parse_err; } sep->se_rpcver_lo = sep->se_rpcver_hi = n; if (*p == '-') { p++; n = bb_strtou(p, &p, 10); if (n > INT_MAX || (int)n < sep->se_rpcver_lo) goto bad_ver_spec; sep->se_rpcver_hi = n; } if (*p != '\0') goto bad_ver_spec; #else bb_error_msg("no support for rpc services"); goto parse_err; #endif } /* we don't really need getprotobyname()! */ if (strcmp(arg, "tcp") == 0) sep->se_proto_no = IPPROTO_TCP; /* = 6 */ if (strcmp(arg, "udp") == 0) sep->se_proto_no = IPPROTO_UDP; /* = 17 */ if (six) *six = '6'; if (!sep->se_proto_no) /* not tcp/udp?? */ goto parse_err; } /* [no]wait[.max] user[:group] prog [args] */ arg = token[3]; sep->se_max = max_concurrency; p = strchr(arg, '.'); if (p) { *p++ = '\0'; sep->se_max = bb_strtou(p, NULL, 10); if (errno) goto parse_err; } sep->se_wait = (arg[0] != 'n' || arg[1] != 'o'); if (!sep->se_wait) /* "no" seen */ arg += 2; if (strcmp(arg, "wait") != 0) goto parse_err; /* user[:group] prog [args] */ sep->se_user = xstrdup(token[4]); arg = strchr(sep->se_user, '.'); if (arg == NULL) arg = strchr(sep->se_user, ':'); if (arg) { *arg++ = '\0'; sep->se_group = xstrdup(arg); } /* prog [args] */ sep->se_program = xstrdup(token[5]); #ifdef INETD_BUILTINS_ENABLED if (strcmp(sep->se_program, "internal") == 0 && strlen(sep->se_service) <= 7 && (sep->se_socktype == SOCK_STREAM || sep->se_socktype == SOCK_DGRAM) ) { unsigned i; for (i = 0; i < ARRAY_SIZE(builtins); i++) if (strncmp(builtins[i].bi_service7, sep->se_service, 7) == 0) goto found_bi; bb_error_msg("unknown internal service %s", sep->se_service); goto parse_err; found_bi: sep->se_builtin = &builtins[i]; /* stream builtins must be "nowait", dgram must be "wait" */ if (sep->se_wait != (sep->se_socktype == SOCK_DGRAM)) goto parse_err; } #endif argc = 0; while ((arg = token[6+argc]) != NULL && argc < MAXARGV) sep->se_argv[argc++] = xstrdup(arg); /* Some inetd.conf files have no argv's, not even argv[0]. * Fix them up. * (Technically, programs can be execed with argv[0] = NULL, * but many programs do not like that at all) */ if (argc == 0) sep->se_argv[0] = xstrdup(sep->se_program); /* catch mixups. " stream udp ..." == wtf */ if (sep->se_socktype == SOCK_STREAM) { if (sep->se_proto_no == IPPROTO_UDP) goto parse_err; } if (sep->se_socktype == SOCK_DGRAM) { if (sep->se_proto_no == IPPROTO_TCP) goto parse_err; } // bb_info_msg( // "ENTRY[%s][%s][%s][%d][%d][%d][%d][%d][%s][%s][%s]", // sep->se_local_hostname, sep->se_service, sep->se_proto, sep->se_wait, sep->se_proto_no, // sep->se_max, sep->se_count, sep->se_time, sep->se_user, sep->se_group, sep->se_program); /* check if the hostname specifier is a comma separated list * of hostnames. we'll make new entries for each address. */ while ((hostdelim = strrchr(sep->se_local_hostname, ',')) != NULL) { nsep = dup_servtab(sep); /* NUL terminate the hostname field of the existing entry, * and make a dup for the new entry. */ *hostdelim++ = '\0'; nsep->se_local_hostname = xstrdup(hostdelim); nsep->se_next = sep->se_next; sep->se_next = nsep; } /* was doing it here: */ /* DNS resolution, create copies for each IP address */ /* IPv6-ization destroyed it :( */ return sep; } static servtab_t *insert_in_servlist(servtab_t *cp) { servtab_t *sep; sigset_t omask; sep = new_servtab(); *sep = *cp; /* struct copy */ sep->se_fd = -1; #if ENABLE_FEATURE_INETD_RPC sep->se_rpcprog = -1; #endif block_CHLD_HUP_ALRM(&omask); sep->se_next = serv_list; serv_list = sep; restore_sigmask(&omask); return sep; } static int same_serv_addr_proto(servtab_t *old, servtab_t *new) { if (strcmp(old->se_local_hostname, new->se_local_hostname) != 0) return 0; if (strcmp(old->se_service, new->se_service) != 0) return 0; if (strcmp(old->se_proto, new->se_proto) != 0) return 0; return 1; } static void reread_config_file(int sig UNUSED_PARAM) { servtab_t *sep, *cp, **sepp; len_and_sockaddr *lsa; sigset_t omask; unsigned n; uint16_t port; int save_errno = errno; if (!reopen_config_file()) goto ret; for (sep = serv_list; sep; sep = sep->se_next) sep->se_checked = 0; goto first_line; while (1) { if (cp == NULL) { first_line: cp = parse_one_line(); if (cp == NULL) break; } for (sep = serv_list; sep; sep = sep->se_next) if (same_serv_addr_proto(sep, cp)) goto equal_servtab; /* not an "equal" servtab */ sep = insert_in_servlist(cp); goto after_check; equal_servtab: { int i; block_CHLD_HUP_ALRM(&omask); #if ENABLE_FEATURE_INETD_RPC if (is_rpc_service(sep)) unregister_rpc(sep); sep->se_rpcver_lo = cp->se_rpcver_lo; sep->se_rpcver_hi = cp->se_rpcver_hi; #endif if (cp->se_wait == 0) { /* New config says "nowait". If old one * was "wait", we currently may be waiting * for a child (and not accepting connects). * Stop waiting, start listening again. * (if it's not true, this op is harmless) */ add_fd_to_set(sep->se_fd); } sep->se_wait = cp->se_wait; sep->se_max = cp->se_max; /* string fields need more love - we don't want to leak them */ #define SWAP(type, a, b) do { type c = (type)a; a = (type)b; b = (type)c; } while (0) SWAP(char*, sep->se_user, cp->se_user); SWAP(char*, sep->se_group, cp->se_group); SWAP(char*, sep->se_program, cp->se_program); for (i = 0; i < MAXARGV; i++) SWAP(char*, sep->se_argv[i], cp->se_argv[i]); #undef SWAP restore_sigmask(&omask); free_servtab_strings(cp); } after_check: /* cp->string_fields are consumed by insert_in_servlist() * or freed at this point, cp itself is not yet freed. */ sep->se_checked = 1; /* create new len_and_sockaddr */ switch (sep->se_family) { struct sockaddr_un *sun; case AF_UNIX: lsa = xzalloc_lsa(AF_UNIX); sun = (struct sockaddr_un*)&lsa->u.sa; safe_strncpy(sun->sun_path, sep->se_service, sizeof(sun->sun_path)); break; default: /* case AF_INET, case AF_INET6 */ n = bb_strtou(sep->se_service, NULL, 10); #if ENABLE_FEATURE_INETD_RPC if (is_rpc_service(sep)) { sep->se_rpcprog = n; if (errno) { /* se_service is not numeric */ struct rpcent *rp = getrpcbyname(sep->se_service); if (rp == NULL) { bb_error_msg("%s: unknown rpc service", sep->se_service); goto next_cp; } sep->se_rpcprog = rp->r_number; } if (sep->se_fd == -1) prepare_socket_fd(sep); if (sep->se_fd != -1) register_rpc(sep); goto next_cp; } #endif /* what port to listen on? */ port = htons(n); if (errno || n > 0xffff) { /* se_service is not numeric */ char protoname[4]; struct servent *sp; /* can result only in "tcp" or "udp": */ safe_strncpy(protoname, sep->se_proto, 4); sp = getservbyname(sep->se_service, protoname); if (sp == NULL) { bb_error_msg("%s/%s: unknown service", sep->se_service, sep->se_proto); goto next_cp; } port = sp->s_port; } if (LONE_CHAR(sep->se_local_hostname, '*')) { lsa = xzalloc_lsa(sep->se_family); set_nport(&lsa->u.sa, port); } else { lsa = host_and_af2sockaddr(sep->se_local_hostname, ntohs(port), sep->se_family); if (!lsa) { bb_error_msg("%s/%s: unknown host '%s'", sep->se_service, sep->se_proto, sep->se_local_hostname); goto next_cp; } } break; } /* end of "switch (sep->se_family)" */ /* did lsa change? Then close/open */ if (sep->se_lsa == NULL || lsa->len != sep->se_lsa->len || memcmp(&lsa->u.sa, &sep->se_lsa->u.sa, lsa->len) != 0 ) { remove_fd_from_set(sep->se_fd); maybe_close(sep->se_fd); free(sep->se_lsa); sep->se_lsa = lsa; sep->se_fd = -1; } else { free(lsa); } if (sep->se_fd == -1) prepare_socket_fd(sep); next_cp: sep = cp->se_next; free(cp); cp = sep; } /* end of "while (1) parse lines" */ close_config_file(); /* Purge anything not looked at above - these are stale entries, * new config file doesnt have them. */ block_CHLD_HUP_ALRM(&omask); sepp = &serv_list; while ((sep = *sepp) != NULL) { if (sep->se_checked) { sepp = &sep->se_next; continue; } *sepp = sep->se_next; remove_fd_from_set(sep->se_fd); maybe_close(sep->se_fd); #if ENABLE_FEATURE_INETD_RPC if (is_rpc_service(sep)) unregister_rpc(sep); #endif if (sep->se_family == AF_UNIX) unlink(sep->se_service); free_servtab_strings(sep); free(sep); } restore_sigmask(&omask); ret: errno = save_errno; } static void reap_child(int sig UNUSED_PARAM) { pid_t pid; int status; servtab_t *sep; int save_errno = errno; for (;;) { pid = wait_any_nohang(&status); if (pid <= 0) break; for (sep = serv_list; sep; sep = sep->se_next) { if (sep->se_wait != pid) continue; /* One of our "wait" services */ if (WIFEXITED(status) && WEXITSTATUS(status)) bb_error_msg("%s: exit status %u", sep->se_program, WEXITSTATUS(status)); else if (WIFSIGNALED(status)) bb_error_msg("%s: exit signal %u", sep->se_program, WTERMSIG(status)); sep->se_wait = 1; add_fd_to_set(sep->se_fd); break; } } errno = save_errno; } static void retry_network_setup(int sig UNUSED_PARAM) { int save_errno = errno; servtab_t *sep; alarm_armed = 0; for (sep = serv_list; sep; sep = sep->se_next) { if (sep->se_fd == -1) { prepare_socket_fd(sep); #if ENABLE_FEATURE_INETD_RPC if (sep->se_fd != -1 && is_rpc_service(sep)) register_rpc(sep); #endif } } errno = save_errno; } static void clean_up_and_exit(int sig UNUSED_PARAM) { servtab_t *sep; /* XXX signal race walking sep list */ for (sep = serv_list; sep; sep = sep->se_next) { if (sep->se_fd == -1) continue; switch (sep->se_family) { case AF_UNIX: unlink(sep->se_service); break; default: /* case AF_INET, AF_INET6 */ #if ENABLE_FEATURE_INETD_RPC if (sep->se_wait == 1 && is_rpc_service(sep)) unregister_rpc(sep); /* XXX signal race */ #endif break; } if (ENABLE_FEATURE_CLEAN_UP) close(sep->se_fd); } remove_pidfile(CONFIG_PID_FILE_PATH "/inetd.pid"); exit(EXIT_SUCCESS); } int inetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int inetd_main(int argc UNUSED_PARAM, char **argv) { struct sigaction sa, saved_pipe_handler; servtab_t *sep, *sep2; struct passwd *pwd; struct group *grp = grp; /* for compiler */ int opt; pid_t pid; sigset_t omask; INIT_G(); real_uid = getuid(); if (real_uid != 0) /* run by non-root user */ config_filename = NULL; opt_complementary = "R+:q+"; /* -q N, -R N */ opt = getopt32(argv, "R:feq:", &max_concurrency, &global_queuelen); argv += optind; //argc -= optind; if (argv[0]) config_filename = argv[0]; if (config_filename == NULL) bb_error_msg_and_die("non-root must specify config file"); if (!(opt & 2)) bb_daemonize_or_rexec(0, argv - optind); else bb_sanitize_stdio(); if (!(opt & 4)) { /* LOG_NDELAY: connect to syslog daemon NOW. * Otherwise, we may open syslog socket * in vforked child, making opened fds and syslog() * internal state inconsistent. * This was observed to leak file descriptors. */ openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON); logmode = LOGMODE_SYSLOG; } if (real_uid == 0) { /* run by root, ensure groups vector gets trashed */ gid_t gid = getgid(); setgroups(1, &gid); } write_pidfile(CONFIG_PID_FILE_PATH "/inetd.pid"); /* never fails under Linux (except if you pass it bad arguments) */ getrlimit(RLIMIT_NOFILE, &rlim_ofile); rlim_ofile_cur = rlim_ofile.rlim_cur; if (rlim_ofile_cur == RLIM_INFINITY) /* ! */ rlim_ofile_cur = OPEN_MAX; memset(&sa, 0, sizeof(sa)); /*sigemptyset(&sa.sa_mask); - memset did it */ sigaddset(&sa.sa_mask, SIGALRM); sigaddset(&sa.sa_mask, SIGCHLD); sigaddset(&sa.sa_mask, SIGHUP); //FIXME: explain why no SA_RESTART //FIXME: retry_network_setup is unsafe to run in signal handler (many reasons)! sa.sa_handler = retry_network_setup; sigaction_set(SIGALRM, &sa); //FIXME: reread_config_file is unsafe to run in signal handler(many reasons)! sa.sa_handler = reread_config_file; sigaction_set(SIGHUP, &sa); //FIXME: reap_child is unsafe to run in signal handler (uses stdio)! sa.sa_handler = reap_child; sigaction_set(SIGCHLD, &sa); //FIXME: clean_up_and_exit is unsafe to run in signal handler (uses stdio)! sa.sa_handler = clean_up_and_exit; sigaction_set(SIGTERM, &sa); sa.sa_handler = clean_up_and_exit; sigaction_set(SIGINT, &sa); sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, &saved_pipe_handler); reread_config_file(SIGHUP); /* load config from file */ for (;;) { int ready_fd_cnt; int ctrl, accepted_fd, new_udp_fd; fd_set readable; if (maxsock < 0) recalculate_maxsock(); readable = allsock; /* struct copy */ /* if there are no fds to wait on, we will block * until signal wakes us up (maxsock == 0, but readable * never contains fds 0 and 1...) */ ready_fd_cnt = select(maxsock + 1, &readable, NULL, NULL, NULL); if (ready_fd_cnt < 0) { if (errno != EINTR) { bb_perror_msg("select"); sleep(1); } continue; } dbg("ready_fd_cnt:%d\n", ready_fd_cnt); for (sep = serv_list; ready_fd_cnt && sep; sep = sep->se_next) { if (sep->se_fd == -1 || !FD_ISSET(sep->se_fd, &readable)) continue; dbg("ready fd:%d\n", sep->se_fd); ready_fd_cnt--; ctrl = sep->se_fd; accepted_fd = -1; new_udp_fd = -1; if (!sep->se_wait) { if (sep->se_socktype == SOCK_STREAM) { ctrl = accepted_fd = accept(sep->se_fd, NULL, NULL); dbg("accepted_fd:%d\n", accepted_fd); if (ctrl < 0) { if (errno != EINTR) bb_perror_msg("accept (for %s)", sep->se_service); continue; } } /* "nowait" udp */ if (sep->se_socktype == SOCK_DGRAM && sep->se_family != AF_UNIX ) { /* How udp "nowait" works: * child peeks at (received and buffered by kernel) UDP packet, * performs connect() on the socket so that it is linked only * to this peer. But this also affects parent, because descriptors * are shared after fork() a-la dup(). When parent performs * select(), it will see this descriptor connected to the peer (!) * and still readable, will act on it and mess things up * (can create many copies of same child, etc). * Parent must create and use new socket instead. */ new_udp_fd = socket(sep->se_family, SOCK_DGRAM, 0); dbg("new_udp_fd:%d\n", new_udp_fd); if (new_udp_fd < 0) { /* error: eat packet, forget about it */ udp_err: recv(sep->se_fd, line, LINE_SIZE, MSG_DONTWAIT); continue; } setsockopt_reuseaddr(new_udp_fd); /* TODO: better do bind after fork in parent, * so that we don't have two wildcard bound sockets * even for a brief moment? */ if (bind(new_udp_fd, &sep->se_lsa->u.sa, sep->se_lsa->len) < 0) { dbg("bind(new_udp_fd) failed\n"); close(new_udp_fd); goto udp_err; } dbg("bind(new_udp_fd) succeeded\n"); } } block_CHLD_HUP_ALRM(&omask); pid = 0; #ifdef INETD_BUILTINS_ENABLED /* do we need to fork? */ if (sep->se_builtin == NULL || (sep->se_socktype == SOCK_STREAM && sep->se_builtin->bi_fork)) #endif { if (sep->se_max != 0) { if (++sep->se_count == 1) sep->se_time = monotonic_sec(); else if (sep->se_count >= sep->se_max) { unsigned now = monotonic_sec(); /* did we accumulate se_max connects too quickly? */ if (now - sep->se_time <= CNT_INTERVAL) { bb_error_msg("%s/%s: too many connections, pausing", sep->se_service, sep->se_proto); remove_fd_from_set(sep->se_fd); close(sep->se_fd); sep->se_fd = -1; sep->se_count = 0; rearm_alarm(); /* will revive it in RETRYTIME sec */ restore_sigmask(&omask); maybe_close(new_udp_fd); maybe_close(accepted_fd); continue; /* -> check next fd in fd set */ } sep->se_count = 0; } } /* on NOMMU, streamed chargen * builtin wouldn't work, but it is * not allowed on NOMMU (ifdefed out) */ #ifdef INETD_BUILTINS_ENABLED if (BB_MMU && sep->se_builtin) pid = fork(); else #endif pid = vfork(); if (pid < 0) { /* fork error */ bb_perror_msg("vfork"+1); sleep(1); restore_sigmask(&omask); maybe_close(new_udp_fd); maybe_close(accepted_fd); continue; /* -> check next fd in fd set */ } if (pid == 0) pid--; /* -1: "we did fork and we are child" */ } /* if pid == 0 here, we didn't fork */ if (pid > 0) { /* parent */ if (sep->se_wait) { /* wait: we passed socket to child, * will wait for child to terminate */ sep->se_wait = pid; remove_fd_from_set(sep->se_fd); } if (new_udp_fd >= 0) { /* udp nowait: child connected the socket, * we created and will use new, unconnected one */ xmove_fd(new_udp_fd, sep->se_fd); dbg("moved new_udp_fd:%d to sep->se_fd:%d\n", new_udp_fd, sep->se_fd); } restore_sigmask(&omask); maybe_close(accepted_fd); continue; /* -> check next fd in fd set */ } /* we are either child or didn't fork at all */ #ifdef INETD_BUILTINS_ENABLED if (sep->se_builtin) { if (pid) { /* "pid" is -1: we did fork */ close(sep->se_fd); /* listening socket */ dbg("closed sep->se_fd:%d\n", sep->se_fd); logmode = LOGMODE_NONE; /* make xwrite etc silent */ } restore_sigmask(&omask); if (sep->se_socktype == SOCK_STREAM) sep->se_builtin->bi_stream_fn(ctrl, sep); else sep->se_builtin->bi_dgram_fn(ctrl, sep); if (pid) /* we did fork */ _exit(EXIT_FAILURE); maybe_close(accepted_fd); continue; /* -> check next fd in fd set */ } #endif /* child */ setsid(); /* "nowait" udp */ if (new_udp_fd >= 0) { len_and_sockaddr *lsa; int r; close(new_udp_fd); dbg("closed new_udp_fd:%d\n", new_udp_fd); lsa = xzalloc_lsa(sep->se_family); /* peek at the packet and remember peer addr */ r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT, &lsa->u.sa, &lsa->len); if (r < 0) goto do_exit1; /* make this socket "connected" to peer addr: * only packets from this peer will be recv'ed, * and bare write()/send() will work on it */ connect(ctrl, &lsa->u.sa, lsa->len); dbg("connected ctrl:%d to remote peer\n", ctrl); free(lsa); } /* prepare env and exec program */ pwd = getpwnam(sep->se_user); if (pwd == NULL) { bb_error_msg("%s: no such %s", sep->se_user, "user"); goto do_exit1; } if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) { bb_error_msg("%s: no such %s", sep->se_group, "group"); goto do_exit1; } if (real_uid != 0 && real_uid != pwd->pw_uid) { /* a user running private inetd */ bb_error_msg("non-root must run services as himself"); goto do_exit1; } if (pwd->pw_uid != 0) { if (sep->se_group) pwd->pw_gid = grp->gr_gid; /* initgroups, setgid, setuid: */ change_identity(pwd); } else if (sep->se_group) { xsetgid(grp->gr_gid); setgroups(1, &grp->gr_gid); } if (rlim_ofile.rlim_cur != rlim_ofile_cur) if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) bb_perror_msg("setrlimit"); /* closelog(); - WRONG. we are after vfork, * this may confuse syslog() internal state. * Let's hope libc sets syslog fd to CLOEXEC... */ xmove_fd(ctrl, STDIN_FILENO); xdup2(STDIN_FILENO, STDOUT_FILENO); dbg("moved ctrl:%d to fd 0,1[,2]\n", ctrl); /* manpages of inetd I managed to find either say * that stderr is also redirected to the network, * or do not talk about redirection at all (!) */ if (!sep->se_wait) /* only for usual "tcp nowait" */ xdup2(STDIN_FILENO, STDERR_FILENO); /* NB: among others, this loop closes listening sockets * for nowait stream children */ for (sep2 = serv_list; sep2; sep2 = sep2->se_next) if (sep2->se_fd != ctrl) maybe_close(sep2->se_fd); sigaction_set(SIGPIPE, &saved_pipe_handler); restore_sigmask(&omask); dbg("execing:'%s'\n", sep->se_program); BB_EXECVP(sep->se_program, sep->se_argv); bb_perror_msg("can't execute '%s'", sep->se_program); do_exit1: /* eat packet in udp case */ if (sep->se_socktype != SOCK_STREAM) recv(0, line, LINE_SIZE, MSG_DONTWAIT); _exit(EXIT_FAILURE); } /* for (sep = servtab...) */ } /* for (;;) */ } #if !BB_MMU static const char *const cat_args[] = { "cat", NULL }; #endif /* * Internet services provided internally by inetd: */ #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO /* Echo service -- echo data back. */ /* ARGSUSED */ static void FAST_FUNC echo_stream(int s, servtab_t *sep UNUSED_PARAM) { #if BB_MMU while (1) { ssize_t sz = safe_read(s, line, LINE_SIZE); if (sz <= 0) break; xwrite(s, line, sz); } #else /* We are after vfork here! */ /* move network socket to stdin/stdout */ xmove_fd(s, STDIN_FILENO); xdup2(STDIN_FILENO, STDOUT_FILENO); /* no error messages please... */ close(STDERR_FILENO); xopen(bb_dev_null, O_WRONLY); BB_EXECVP("cat", (char**)cat_args); /* on failure we return to main, which does exit(EXIT_FAILURE) */ #endif } static void FAST_FUNC echo_dg(int s, servtab_t *sep) { enum { BUFSIZE = 12*1024 }; /* for jumbo sized packets! :) */ char *buf = xmalloc(BUFSIZE); /* too big for stack */ int sz; len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len); lsa->len = sep->se_lsa->len; /* dgram builtins are non-forking - DONT BLOCK! */ sz = recvfrom(s, buf, BUFSIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len); if (sz > 0) sendto(s, buf, sz, 0, &lsa->u.sa, lsa->len); free(buf); } #endif /* FEATURE_INETD_SUPPORT_BUILTIN_ECHO */ #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD /* Discard service -- ignore data. */ /* ARGSUSED */ static void FAST_FUNC discard_stream(int s, servtab_t *sep UNUSED_PARAM) { #if BB_MMU while (safe_read(s, line, LINE_SIZE) > 0) continue; #else /* We are after vfork here! */ /* move network socket to stdin */ xmove_fd(s, STDIN_FILENO); /* discard output */ close(STDOUT_FILENO); xopen(bb_dev_null, O_WRONLY); /* no error messages please... */ xdup2(STDOUT_FILENO, STDERR_FILENO); BB_EXECVP("cat", (char**)cat_args); /* on failure we return to main, which does exit(EXIT_FAILURE) */ #endif } /* ARGSUSED */ static void FAST_FUNC discard_dg(int s, servtab_t *sep UNUSED_PARAM) { /* dgram builtins are non-forking - DONT BLOCK! */ recv(s, line, LINE_SIZE, MSG_DONTWAIT); } #endif /* FEATURE_INETD_SUPPORT_BUILTIN_DISCARD */ #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN #define LINESIZ 72 static void init_ring(void) { int i; end_ring = ring; for (i = ' '; i < 127; i++) *end_ring++ = i; } /* Character generator. MMU arches only. */ /* ARGSUSED */ static void FAST_FUNC chargen_stream(int s, servtab_t *sep UNUSED_PARAM) { char *rs; int len; char text[LINESIZ + 2]; if (!end_ring) { init_ring(); rs = ring; } text[LINESIZ] = '\r'; text[LINESIZ + 1] = '\n'; rs = ring; for (;;) { len = end_ring - rs; if (len >= LINESIZ) memmove(text, rs, LINESIZ); else { memmove(text, rs, len); memmove(text + len, ring, LINESIZ - len); } if (++rs == end_ring) rs = ring; xwrite(s, text, sizeof(text)); } } /* ARGSUSED */ static void FAST_FUNC chargen_dg(int s, servtab_t *sep) { int len; char text[LINESIZ + 2]; len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len); /* Eat UDP packet which started it all */ /* dgram builtins are non-forking - DONT BLOCK! */ lsa->len = sep->se_lsa->len; if (recvfrom(s, text, sizeof(text), MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0) return; if (!end_ring) { init_ring(); ring_pos = ring; } len = end_ring - ring_pos; if (len >= LINESIZ) memmove(text, ring_pos, LINESIZ); else { memmove(text, ring_pos, len); memmove(text + len, ring, LINESIZ - len); } if (++ring_pos == end_ring) ring_pos = ring; text[LINESIZ] = '\r'; text[LINESIZ + 1] = '\n'; sendto(s, text, sizeof(text), 0, &lsa->u.sa, lsa->len); } #endif /* FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN */ #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME /* * Return a machine readable date and time, in the form of the * number of seconds since midnight, Jan 1, 1900. Since gettimeofday * returns the number of seconds since midnight, Jan 1, 1970, * we must add 2208988800 seconds to this figure to make up for * some seventy years Bell Labs was asleep. */ static uint32_t machtime(void) { struct timeval tv; gettimeofday(&tv, NULL); return htonl((uint32_t)(tv.tv_sec + 2208988800)); } /* ARGSUSED */ static void FAST_FUNC machtime_stream(int s, servtab_t *sep UNUSED_PARAM) { uint32_t result; result = machtime(); full_write(s, &result, sizeof(result)); } static void FAST_FUNC machtime_dg(int s, servtab_t *sep) { uint32_t result; len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len); lsa->len = sep->se_lsa->len; if (recvfrom(s, line, LINE_SIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0) return; result = machtime(); sendto(s, &result, sizeof(result), 0, &lsa->u.sa, lsa->len); } #endif /* FEATURE_INETD_SUPPORT_BUILTIN_TIME */ #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME /* Return human-readable time of day */ /* ARGSUSED */ static void FAST_FUNC daytime_stream(int s, servtab_t *sep UNUSED_PARAM) { time_t t; t = time(NULL); fdprintf(s, "%.24s\r\n", ctime(&t)); } static void FAST_FUNC daytime_dg(int s, servtab_t *sep) { time_t t; len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len); lsa->len = sep->se_lsa->len; if (recvfrom(s, line, LINE_SIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0) return; t = time(NULL); sprintf(line, "%.24s\r\n", ctime(&t)); sendto(s, line, strlen(line), 0, &lsa->u.sa, lsa->len); } #endif /* FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME */ busybox-1.22.1/networking/tcpudp.c0000644000000000000000000005037612263563520015637 0ustar rootroot/* Based on ipsvd utilities written by Gerrit Pape * which are released into public domain by the author. * Homepage: http://smarden.sunsite.dk/ipsvd/ * * Copyright (C) 2007 Denys Vlasenko. * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* Based on ipsvd-0.12.1. This tcpsvd accepts all options * which are supported by one from ipsvd-0.12.1, but not all are * functional. See help text at the end of this file for details. * * Code inside "#ifdef SSLSVD" is for sslsvd and is currently unused. * * Busybox version exports TCPLOCALADDR instead of * TCPLOCALIP + TCPLOCALPORT pair. ADDR more closely matches reality * (which is "struct sockaddr_XXX". Port is not a separate entity, * it's just a part of (AF_INET[6]) sockaddr!). * * TCPORIGDSTADDR is Busybox-specific addition. * * udp server is hacked up by reusing TCP code. It has the following * limitation inherent in Unix DGRAM sockets implementation: * - local IP address is retrieved (using recvmsg voodoo) but * child's socket is not bound to it (bind cannot be called on * already bound socket). Thus it still can emit outgoing packets * with wrong source IP... * - don't know how to retrieve ORIGDST for udp. */ //usage:#define tcpsvd_trivial_usage //usage: "[-hEv] [-c N] [-C N[:MSG]] [-b N] [-u USER] [-l NAME] IP PORT PROG" /* with not-implemented options: */ /* //usage: "[-hpEvv] [-c N] [-C N[:MSG]] [-b N] [-u USER] [-l NAME] [-i DIR|-x CDB] [-t SEC] IP PORT PROG" */ //usage:#define tcpsvd_full_usage "\n\n" //usage: "Create TCP socket, bind to IP:PORT and listen\n" //usage: "for incoming connection. Run PROG for each connection.\n" //usage: "\n IP IP to listen on, 0 = all" //usage: "\n PORT Port to listen on" //usage: "\n PROG ARGS Program to run" //usage: "\n -l NAME Local hostname (else looks up local hostname in DNS)" //usage: "\n -u USER[:GRP] Change to user/group after bind" //usage: "\n -c N Handle up to N connections simultaneously" //usage: "\n -b N Allow a backlog of approximately N TCP SYNs" //usage: "\n -C N[:MSG] Allow only up to N connections from the same IP" //usage: "\n New connections from this IP address are closed" //usage: "\n immediately. MSG is written to the peer before close" //usage: "\n -h Look up peer's hostname" //usage: "\n -E Don't set up environment variables" //usage: "\n -v Verbose" //usage: //usage:#define udpsvd_trivial_usage //usage: "[-hEv] [-c N] [-u USER] [-l NAME] IP PORT PROG" //usage:#define udpsvd_full_usage "\n\n" //usage: "Create UDP socket, bind to IP:PORT and wait\n" //usage: "for incoming packets. Run PROG for each packet,\n" //usage: "redirecting all further packets with same peer ip:port to it.\n" //usage: "\n IP IP to listen on, 0 = all" //usage: "\n PORT Port to listen on" //usage: "\n PROG ARGS Program to run" //usage: "\n -l NAME Local hostname (else looks up local hostname in DNS)" //usage: "\n -u USER[:GRP] Change to user/group after bind" //usage: "\n -c N Handle up to N connections simultaneously" //usage: "\n -h Look up peer's hostname" //usage: "\n -E Don't set up environment variables" //usage: "\n -v Verbose" #include "libbb.h" /* Wants etc, thus included after libbb.h: */ #ifdef __linux__ #include /* for __be32 etc */ #include #endif // TODO: move into this file: #include "tcpudp_perhost.h" #ifdef SSLSVD #include "matrixSsl.h" #include "ssl_io.h" #endif struct globals { unsigned verbose; unsigned max_per_host; unsigned cur_per_host; unsigned cnum; unsigned cmax; char **env_cur; char *env_var[1]; /* actually bigger */ } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define verbose (G.verbose ) #define max_per_host (G.max_per_host) #define cur_per_host (G.cur_per_host) #define cnum (G.cnum ) #define cmax (G.cmax ) #define env_cur (G.env_cur ) #define env_var (G.env_var ) #define INIT_G() do { \ cmax = 30; \ env_cur = &env_var[0]; \ } while (0) /* We have to be careful about leaking memory in repeated setenv's */ static void xsetenv_plain(const char *n, const char *v) { char *var = xasprintf("%s=%s", n, v); *env_cur++ = var; putenv(var); } static void xsetenv_proto(const char *proto, const char *n, const char *v) { char *var = xasprintf("%s%s=%s", proto, n, v); *env_cur++ = var; putenv(var); } static void undo_xsetenv(void) { char **pp = env_cur = &env_var[0]; while (*pp) { char *var = *pp; bb_unsetenv_and_free(var); *pp++ = NULL; } } static void sig_term_handler(int sig) { if (verbose) bb_error_msg("got signal %u, exit", sig); kill_myself_with_sig(sig); } /* Little bloated, but tries to give accurate info how child exited. * Makes easier to spot segfaulting children etc... */ static void print_waitstat(unsigned pid, int wstat) { unsigned e = 0; const char *cause = "?exit"; if (WIFEXITED(wstat)) { cause++; e = WEXITSTATUS(wstat); } else if (WIFSIGNALED(wstat)) { cause = "signal"; e = WTERMSIG(wstat); } bb_error_msg("end %d %s %d", pid, cause, e); } /* Must match getopt32 in main! */ enum { OPT_c = (1 << 0), OPT_C = (1 << 1), OPT_i = (1 << 2), OPT_x = (1 << 3), OPT_u = (1 << 4), OPT_l = (1 << 5), OPT_E = (1 << 6), OPT_b = (1 << 7), OPT_h = (1 << 8), OPT_p = (1 << 9), OPT_t = (1 << 10), OPT_v = (1 << 11), OPT_V = (1 << 12), OPT_U = (1 << 13), /* from here: sslsvd only */ OPT_slash = (1 << 14), OPT_Z = (1 << 15), OPT_K = (1 << 16), }; static void connection_status(void) { /* "only 1 client max" desn't need this */ if (cmax > 1) bb_error_msg("status %u/%u", cnum, cmax); } static void sig_child_handler(int sig UNUSED_PARAM) { int wstat; pid_t pid; while ((pid = wait_any_nohang(&wstat)) > 0) { if (max_per_host) ipsvd_perhost_remove(pid); if (cnum) cnum--; if (verbose) print_waitstat(pid, wstat); } if (verbose) connection_status(); } int tcpudpsvd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv) { char *str_C, *str_t; char *user; struct hcc *hccp; const char *instructs; char *msg_per_host = NULL; unsigned len_per_host = len_per_host; /* gcc */ #ifndef SSLSVD struct bb_uidgid_t ugid; #endif bool tcp; uint16_t local_port; char *preset_local_hostname = NULL; char *remote_hostname = remote_hostname; /* for compiler */ char *remote_addr = remote_addr; /* for compiler */ len_and_sockaddr *lsa; len_and_sockaddr local, remote; socklen_t sa_len; int pid; int sock; int conn; unsigned backlog = 20; unsigned opts; INIT_G(); tcp = (applet_name[0] == 't'); /* 3+ args, -i at most once, -p implies -h, -v is counter, -b N, -c N */ opt_complementary = "-3:i--i:ph:vv:b+:c+"; #ifdef SSLSVD opts = getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:", &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname, &backlog, &str_t, &ssluser, &root, &cert, &key, &verbose ); #else /* "+": stop on first non-option */ opts = getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v", &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname, &backlog, &str_t, &verbose ); #endif if (opts & OPT_C) { /* -C n[:message] */ max_per_host = bb_strtou(str_C, &str_C, 10); if (str_C[0]) { if (str_C[0] != ':') bb_show_usage(); msg_per_host = str_C + 1; len_per_host = strlen(msg_per_host); } } if (max_per_host > cmax) max_per_host = cmax; if (opts & OPT_u) { xget_uidgid(&ugid, user); } #ifdef SSLSVD if (opts & OPT_U) ssluser = optarg; if (opts & OPT_slash) root = optarg; if (opts & OPT_Z) cert = optarg; if (opts & OPT_K) key = optarg; #endif argv += optind; if (!argv[0][0] || LONE_CHAR(argv[0], '0')) argv[0] = (char*)"0.0.0.0"; /* Per-IP flood protection is not thought-out for UDP */ if (!tcp) max_per_host = 0; bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */ #ifdef SSLSVD sslser = user; client = 0; if ((getuid() == 0) && !(opts & OPT_u)) { xfunc_exitcode = 100; bb_error_msg_and_die(bb_msg_you_must_be_root); } if (opts & OPT_u) if (!uidgid_get(&sslugid, ssluser, 1)) { if (errno) { bb_perror_msg_and_die("can't get user/group: %s", ssluser); } bb_error_msg_and_die("unknown user/group %s", ssluser); } if (!cert) cert = "./cert.pem"; if (!key) key = cert; if (matrixSslOpen() < 0) fatal("can't initialize ssl"); if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) { if (client) fatal("can't read cert, key, or ca file"); fatal("can't read cert or key file"); } if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0) fatal("can't create ssl session"); #endif sig_block(SIGCHLD); signal(SIGCHLD, sig_child_handler); bb_signals(BB_FATAL_SIGS, sig_term_handler); signal(SIGPIPE, SIG_IGN); if (max_per_host) ipsvd_perhost_init(cmax); local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0); lsa = xhost2sockaddr(argv[0], local_port); argv += 2; sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0); setsockopt_reuseaddr(sock); sa_len = lsa->len; /* I presume sockaddr len stays the same */ xbind(sock, &lsa->u.sa, sa_len); if (tcp) { xlisten(sock, backlog); close_on_exec_on(sock); } else { /* udp: needed for recv_from_to to work: */ socket_want_pktinfo(sock); } /* ndelay_off(sock); - it is the default I think? */ #ifndef SSLSVD if (opts & OPT_u) { /* drop permissions */ xsetgid(ugid.gid); xsetuid(ugid.uid); } #endif if (verbose) { char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa); if (opts & OPT_u) bb_error_msg("listening on %s, starting, uid %u, gid %u", addr, (unsigned)ugid.uid, (unsigned)ugid.gid); else bb_error_msg("listening on %s, starting", addr); free(addr); } /* Main accept() loop */ again: hccp = NULL; while (cnum >= cmax) wait_for_any_sig(); /* expecting SIGCHLD */ /* Accept a connection to fd #0 */ again1: close(0); again2: sig_unblock(SIGCHLD); local.len = remote.len = sa_len; if (tcp) { conn = accept(sock, &remote.u.sa, &remote.len); } else { /* In case recv_from_to won't be able to recover local addr. * Also sets port - recv_from_to is unable to do it. */ local = *lsa; conn = recv_from_to(sock, NULL, 0, MSG_PEEK, &remote.u.sa, &local.u.sa, sa_len); } sig_block(SIGCHLD); if (conn < 0) { if (errno != EINTR) bb_perror_msg(tcp ? "accept" : "recv"); goto again2; } xmove_fd(tcp ? conn : sock, 0); if (max_per_host) { /* Drop connection immediately if cur_per_host > max_per_host * (minimizing load under SYN flood) */ remote_addr = xmalloc_sockaddr2dotted_noport(&remote.u.sa); cur_per_host = ipsvd_perhost_add(remote_addr, max_per_host, &hccp); if (cur_per_host > max_per_host) { /* ipsvd_perhost_add detected that max is exceeded * (and did not store ip in connection table) */ free(remote_addr); if (msg_per_host) { /* don't block or test for errors */ send(0, msg_per_host, len_per_host, MSG_DONTWAIT); } goto again1; } /* NB: remote_addr is not leaked, it is stored in conn table */ } if (!tcp) { /* Voodoo magic: making udp sockets each receive its own * packets is not trivial, and I still not sure * I do it 100% right. * 1) we have to do it before fork() * 2) order is important - is it right now? */ /* Open new non-connected UDP socket for further clients... */ sock = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0); setsockopt_reuseaddr(sock); /* Make plain write/send work for old socket by supplying default * destination address. This also restricts incoming packets * to ones coming from this remote IP. */ xconnect(0, &remote.u.sa, sa_len); /* hole? at this point we have no wildcard udp socket... * can this cause clients to get "port unreachable" icmp? * Yup, time window is very small, but it exists (is it?) */ /* ..."open new socket", continued */ xbind(sock, &lsa->u.sa, sa_len); socket_want_pktinfo(sock); /* Doesn't work: * we cannot replace fd #0 - we will lose pending packet * which is already buffered for us! And we cannot use fd #1 * instead - it will "intercept" all following packets, but child * does not expect data coming *from fd #1*! */ #if 0 /* Make it so that local addr is fixed to localp->u.sa * and we don't accidentally accept packets to other local IPs. */ /* NB: we possibly bind to the _very_ same_ address & port as the one * already bound in parent! This seems to work in Linux. * (otherwise we can move socket to fd #0 only if bind succeeds) */ close(0); set_nport(&localp->u.sa, htons(local_port)); xmove_fd(xsocket(localp->u.sa.sa_family, SOCK_DGRAM, 0), 0); setsockopt_reuseaddr(0); /* crucial */ xbind(0, &localp->u.sa, localp->len); #endif } pid = vfork(); if (pid == -1) { bb_perror_msg("vfork"); goto again; } if (pid != 0) { /* Parent */ cnum++; if (verbose) connection_status(); if (hccp) hccp->pid = pid; /* clean up changes done by vforked child */ undo_xsetenv(); goto again; } /* Child: prepare env, log, and exec prog */ { /* vfork alert! every xmalloc in this block should be freed! */ char *local_hostname = local_hostname; /* for compiler */ char *local_addr = NULL; char *free_me0 = NULL; char *free_me1 = NULL; char *free_me2 = NULL; if (verbose || !(opts & OPT_E)) { if (!max_per_host) /* remote_addr is not yet known */ free_me0 = remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa); if (opts & OPT_h) { free_me1 = remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa); if (!remote_hostname) { bb_error_msg("can't look up hostname for %s", remote_addr); remote_hostname = remote_addr; } } /* Find out local IP peer connected to. * Errors ignored (I'm not paranoid enough to imagine kernel * which doesn't know local IP). */ if (tcp) getsockname(0, &local.u.sa, &local.len); /* else: for UDP it is done earlier by parent */ local_addr = xmalloc_sockaddr2dotted(&local.u.sa); if (opts & OPT_h) { local_hostname = preset_local_hostname; if (!local_hostname) { free_me2 = local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa); if (!local_hostname) bb_error_msg_and_die("can't look up hostname for %s", local_addr); } /* else: local_hostname is not NULL, but is NOT malloced! */ } } if (verbose) { pid = getpid(); if (max_per_host) { bb_error_msg("concurrency %s %u/%u", remote_addr, cur_per_host, max_per_host); } bb_error_msg((opts & OPT_h) ? "start %u %s-%s (%s-%s)" : "start %u %s-%s", pid, local_addr, remote_addr, local_hostname, remote_hostname); } if (!(opts & OPT_E)) { /* setup ucspi env */ const char *proto = tcp ? "TCP" : "UDP"; #ifdef SO_ORIGINAL_DST /* Extract "original" destination addr:port * from Linux firewall. Useful when you redirect * an outbond connection to local handler, and it needs * to know where it originally tried to connect */ if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &local.u.sa, &local.len) == 0) { char *addr = xmalloc_sockaddr2dotted(&local.u.sa); xsetenv_plain("TCPORIGDSTADDR", addr); free(addr); } #endif xsetenv_plain("PROTO", proto); xsetenv_proto(proto, "LOCALADDR", local_addr); xsetenv_proto(proto, "REMOTEADDR", remote_addr); if (opts & OPT_h) { xsetenv_proto(proto, "LOCALHOST", local_hostname); xsetenv_proto(proto, "REMOTEHOST", remote_hostname); } //compat? xsetenv_proto(proto, "REMOTEINFO", ""); /* additional */ if (cur_per_host > 0) /* can not be true for udp */ xsetenv_plain("TCPCONCURRENCY", utoa(cur_per_host)); } free(local_addr); free(free_me0); free(free_me1); free(free_me2); } xdup2(0, 1); signal(SIGPIPE, SIG_DFL); /* this one was SIG_IGNed */ /* Non-ignored signals revert to SIG_DFL on exec anyway */ /*signal(SIGCHLD, SIG_DFL);*/ sig_unblock(SIGCHLD); #ifdef SSLSVD strcpy(id, utoa(pid)); ssl_io(0, argv); bb_perror_msg_and_die("can't execute '%s'", argv[0]); #else BB_EXECVP_or_die(argv); #endif } /* tcpsvd [-hpEvv] [-c n] [-C n:msg] [-b n] [-u user] [-l name] [-i dir|-x cdb] [ -t sec] host port prog tcpsvd creates a TCP/IP socket, binds it to the address host:port, and listens on the socket for incoming connections. On each incoming connection, tcpsvd conditionally runs a program, with standard input reading from the socket, and standard output writing to the socket, to handle this connection. tcpsvd keeps listening on the socket for new connections, and can handle multiple connections simultaneously. tcpsvd optionally checks for special instructions depending on the IP address or hostname of the client that initiated the connection, see ipsvd-instruct(5). host host either is a hostname, or a dotted-decimal IP address, or 0. If host is 0, tcpsvd accepts connections to any local IP address. * busybox accepts IPv6 addresses and host:port pairs too In this case second parameter is ignored port tcpsvd accepts connections to host:port. port may be a name from /etc/services or a number. prog prog consists of one or more arguments. For each connection, tcpsvd normally runs prog, with file descriptor 0 reading from the network, and file descriptor 1 writing to the network. By default it also sets up TCP-related environment variables, see tcp-environ(5) -i dir read instructions for handling new connections from the instructions directory dir. See ipsvd-instruct(5) for details. * ignored by busyboxed version -x cdb read instructions for handling new connections from the constant database cdb. The constant database normally is created from an instructions directory by running ipsvd-cdb(8). * ignored by busyboxed version -t sec timeout. This option only takes effect if the -i option is given. While checking the instructions directory, check the time of last access of the file that matches the clients address or hostname if any, discard and remove the file if it wasn't accessed within the last sec seconds; tcpsvd does not discard or remove a file if the user's write permission is not set, for those files the timeout is disabled. Default is 0, which means that the timeout is disabled. * ignored by busyboxed version -l name local hostname. Do not look up the local hostname in DNS, but use name as hostname. This option must be set if tcpsvd listens on port 53 to avoid loops. -u user[:group] drop permissions. Switch user ID to user's UID, and group ID to user's primary GID after creating and binding to the socket. If user is followed by a colon and a group name, the group ID is switched to the GID of group instead. All supplementary groups are removed. -c n concurrency. Handle up to n connections simultaneously. Default is 30. If there are n connections active, tcpsvd defers acceptance of a new connection until an active connection is closed. -C n[:msg] per host concurrency. Allow only up to n connections from the same IP address simultaneously. If there are n active connections from one IP address, new incoming connections from this IP address are closed immediately. If n is followed by :msg, the message msg is written to the client if possible, before closing the connection. By default msg is empty. See ipsvd-instruct(5) for supported escape sequences in msg. For each accepted connection, the current per host concurrency is available through the environment variable TCPCONCURRENCY. n and msg can be overwritten by ipsvd(7) instructions, see ipsvd-instruct(5). By default tcpsvd doesn't keep track of connections. -h Look up the client's hostname in DNS. -p paranoid. After looking up the client's hostname in DNS, look up the IP addresses in DNS for that hostname, and forget about the hostname if none of the addresses match the client's IP address. You should set this option if you use hostname based instructions. The -p option implies the -h option. * ignored by busyboxed version -b n backlog. Allow a backlog of approximately n TCP SYNs. On some systems n is silently limited. Default is 20. -E no special environment. Do not set up TCP-related environment variables. -v verbose. Print verbose messsages to standard output. -vv more verbose. Print more verbose messages to standard output. * no difference between -v and -vv in busyboxed version */ busybox-1.22.1/networking/isrv.c0000644000000000000000000001666612263563520015327 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Generic non-forking server infrastructure. * Intended to make writing telnetd-type servers easier. * * Copyright (C) 2007 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" #include "isrv.h" #define DEBUG 0 #if DEBUG #define DPRINTF(args...) bb_error_msg(args) #else #define DPRINTF(args...) ((void)0) #endif /* Helpers */ /* Opaque structure */ struct isrv_state_t { short *fd2peer; /* one per registered fd */ void **param_tbl; /* one per registered peer */ /* one per registered peer; doesn't exist if !timeout */ time_t *timeo_tbl; int (*new_peer)(isrv_state_t *state, int fd); time_t curtime; int timeout; int fd_count; int peer_count; int wr_count; fd_set rd; fd_set wr; }; #define FD2PEER (state->fd2peer) #define PARAM_TBL (state->param_tbl) #define TIMEO_TBL (state->timeo_tbl) #define CURTIME (state->curtime) #define TIMEOUT (state->timeout) #define FD_COUNT (state->fd_count) #define PEER_COUNT (state->peer_count) #define WR_COUNT (state->wr_count) /* callback */ void isrv_want_rd(isrv_state_t *state, int fd) { FD_SET(fd, &state->rd); } /* callback */ void isrv_want_wr(isrv_state_t *state, int fd) { if (!FD_ISSET(fd, &state->wr)) { WR_COUNT++; FD_SET(fd, &state->wr); } } /* callback */ void isrv_dont_want_rd(isrv_state_t *state, int fd) { FD_CLR(fd, &state->rd); } /* callback */ void isrv_dont_want_wr(isrv_state_t *state, int fd) { if (FD_ISSET(fd, &state->wr)) { WR_COUNT--; FD_CLR(fd, &state->wr); } } /* callback */ int isrv_register_fd(isrv_state_t *state, int peer, int fd) { int n; DPRINTF("register_fd(peer:%d,fd:%d)", peer, fd); if (FD_COUNT >= FD_SETSIZE) return -1; if (FD_COUNT <= fd) { n = FD_COUNT; FD_COUNT = fd + 1; DPRINTF("register_fd: FD_COUNT %d", FD_COUNT); FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0])); while (n < fd) FD2PEER[n++] = -1; } DPRINTF("register_fd: FD2PEER[%d] = %d", fd, peer); FD2PEER[fd] = peer; return 0; } /* callback */ void isrv_close_fd(isrv_state_t *state, int fd) { DPRINTF("close_fd(%d)", fd); close(fd); isrv_dont_want_rd(state, fd); if (WR_COUNT) isrv_dont_want_wr(state, fd); FD2PEER[fd] = -1; if (fd == FD_COUNT-1) { do fd--; while (fd >= 0 && FD2PEER[fd] == -1); FD_COUNT = fd + 1; DPRINTF("close_fd: FD_COUNT %d", FD_COUNT); FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0])); } } /* callback */ int isrv_register_peer(isrv_state_t *state, void *param) { int n; if (PEER_COUNT >= FD_SETSIZE) return -1; n = PEER_COUNT++; DPRINTF("register_peer: PEER_COUNT %d", PEER_COUNT); PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0])); PARAM_TBL[n] = param; if (TIMEOUT) { TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0])); TIMEO_TBL[n] = CURTIME; } return n; } static void remove_peer(isrv_state_t *state, int peer) { int movesize; int fd; DPRINTF("remove_peer(%d)", peer); fd = FD_COUNT - 1; while (fd >= 0) { if (FD2PEER[fd] == peer) { isrv_close_fd(state, fd); fd--; continue; } if (FD2PEER[fd] > peer) FD2PEER[fd]--; fd--; } PEER_COUNT--; DPRINTF("remove_peer: PEER_COUNT %d", PEER_COUNT); movesize = (PEER_COUNT - peer) * sizeof(void*); if (movesize > 0) { memcpy(&PARAM_TBL[peer], &PARAM_TBL[peer+1], movesize); if (TIMEOUT) memcpy(&TIMEO_TBL[peer], &TIMEO_TBL[peer+1], movesize); } PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0])); if (TIMEOUT) TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0])); } static void handle_accept(isrv_state_t *state, int fd) { int n, newfd; /* suppress gcc warning "cast from ptr to int of different size" */ fcntl(fd, F_SETFL, (int)(ptrdiff_t)(PARAM_TBL[0]) | O_NONBLOCK); newfd = accept(fd, NULL, 0); fcntl(fd, F_SETFL, (int)(ptrdiff_t)(PARAM_TBL[0])); if (newfd < 0) { if (errno == EAGAIN) return; /* Most probably someone gave us wrong fd type * (for example, non-socket). Don't want * to loop forever. */ bb_perror_msg_and_die("accept"); } DPRINTF("new_peer(%d)", newfd); n = state->new_peer(state, newfd); if (n) remove_peer(state, n); /* unsuccesful peer start */ } void BUG_sizeof_fd_set_is_strange(void); static void handle_fd_set(isrv_state_t *state, fd_set *fds, int (*h)(int, void **)) { enum { LONG_CNT = sizeof(fd_set) / sizeof(long) }; int fds_pos; int fd, peer; /* need to know value at _the beginning_ of this routine */ int fd_cnt = FD_COUNT; if (LONG_CNT * sizeof(long) != sizeof(fd_set)) BUG_sizeof_fd_set_is_strange(); fds_pos = 0; while (1) { /* Find next nonzero bit */ while (fds_pos < LONG_CNT) { if (((long*)fds)[fds_pos] == 0) { fds_pos++; continue; } /* Found non-zero word */ fd = fds_pos * sizeof(long)*8; /* word# -> bit# */ while (1) { if (FD_ISSET(fd, fds)) { FD_CLR(fd, fds); goto found_fd; } fd++; } } break; /* all words are zero */ found_fd: if (fd >= fd_cnt) { /* paranoia */ DPRINTF("handle_fd_set: fd > fd_cnt?? (%d > %d)", fd, fd_cnt); break; } DPRINTF("handle_fd_set: fd %d is active", fd); peer = FD2PEER[fd]; if (peer < 0) continue; /* peer is already gone */ if (peer == 0) { handle_accept(state, fd); continue; } DPRINTF("h(fd:%d)", fd); if (h(fd, &PARAM_TBL[peer])) { /* this peer is gone */ remove_peer(state, peer); } else if (TIMEOUT) { TIMEO_TBL[peer] = monotonic_sec(); } } } static void handle_timeout(isrv_state_t *state, int (*do_timeout)(void **)) { int n, peer; peer = PEER_COUNT-1; /* peer 0 is not checked */ while (peer > 0) { DPRINTF("peer %d: time diff %d", peer, (int)(CURTIME - TIMEO_TBL[peer])); if ((CURTIME - TIMEO_TBL[peer]) >= TIMEOUT) { DPRINTF("peer %d: do_timeout()", peer); n = do_timeout(&PARAM_TBL[peer]); if (n) remove_peer(state, peer); } peer--; } } /* Driver */ void isrv_run( int listen_fd, int (*new_peer)(isrv_state_t *state, int fd), int (*do_rd)(int fd, void **), int (*do_wr)(int fd, void **), int (*do_timeout)(void **), int timeout, int linger_timeout) { isrv_state_t *state = xzalloc(sizeof(*state)); state->new_peer = new_peer; state->timeout = timeout; /* register "peer" #0 - it will accept new connections */ isrv_register_peer(state, NULL); isrv_register_fd(state, /*peer:*/ 0, listen_fd); isrv_want_rd(state, listen_fd); /* remember flags to make blocking<->nonblocking switch faster */ /* (suppress gcc warning "cast from ptr to int of different size") */ PARAM_TBL[0] = (void*)(ptrdiff_t)(fcntl(listen_fd, F_GETFL)); while (1) { struct timeval tv; fd_set rd; fd_set wr; fd_set *wrp = NULL; int n; tv.tv_sec = timeout; if (PEER_COUNT <= 1) tv.tv_sec = linger_timeout; tv.tv_usec = 0; rd = state->rd; if (WR_COUNT) { wr = state->wr; wrp = ≀ } DPRINTF("run: select(FD_COUNT:%d,timeout:%d)...", FD_COUNT, (int)tv.tv_sec); n = select(FD_COUNT, &rd, wrp, NULL, tv.tv_sec ? &tv : NULL); DPRINTF("run: ...select:%d", n); if (n < 0) { if (errno != EINTR) bb_perror_msg("select"); continue; } if (n == 0 && linger_timeout && PEER_COUNT <= 1) break; if (timeout) { time_t t = monotonic_sec(); if (t != CURTIME) { CURTIME = t; handle_timeout(state, do_timeout); } } if (n > 0) { handle_fd_set(state, &rd, do_rd); if (wrp) handle_fd_set(state, wrp, do_wr); } } DPRINTF("run: bailout"); /* NB: accept socket is not closed. Caller is to decide what to do */ } busybox-1.22.1/networking/arping.c0000644000000000000000000002637012263563520015615 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Author: Alexey Kuznetsov * Busybox port: Nick Fedchik */ //usage:#define arping_trivial_usage //usage: "[-fqbDUA] [-c CNT] [-w TIMEOUT] [-I IFACE] [-s SRC_IP] DST_IP" //usage:#define arping_full_usage "\n\n" //usage: "Send ARP requests/replies\n" //usage: "\n -f Quit on first ARP reply" //usage: "\n -q Quiet" //usage: "\n -b Keep broadcasting, don't go unicast" //usage: "\n -D Duplicated address detection mode" //usage: "\n -U Unsolicited ARP mode, update your neighbors" //usage: "\n -A ARP answer mode, update your neighbors" //usage: "\n -c N Stop after sending N ARP requests" //usage: "\n -w TIMEOUT Time to wait for ARP reply, seconds" //usage: "\n -I IFACE Interface to use (default eth0)" //usage: "\n -s SRC_IP Sender IP address" //usage: "\n DST_IP Target IP address" #include #include #include #include #include "libbb.h" /* We don't expect to see 1000+ seconds delay, unsigned is enough */ #define MONOTONIC_US() ((unsigned)monotonic_us()) enum { DAD = 1, UNSOLICITED = 2, ADVERT = 4, QUIET = 8, QUIT_ON_REPLY = 16, BCAST_ONLY = 32, UNICASTING = 64 }; struct globals { struct in_addr src; struct in_addr dst; struct sockaddr_ll me; struct sockaddr_ll he; int sock_fd; int count; // = -1; unsigned last; unsigned timeout_us; unsigned start; unsigned sent; unsigned brd_sent; unsigned received; unsigned brd_recv; unsigned req_recv; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define src (G.src ) #define dst (G.dst ) #define me (G.me ) #define he (G.he ) #define sock_fd (G.sock_fd ) #define count (G.count ) #define last (G.last ) #define timeout_us (G.timeout_us) #define start (G.start ) #define sent (G.sent ) #define brd_sent (G.brd_sent ) #define received (G.received ) #define brd_recv (G.brd_recv ) #define req_recv (G.req_recv ) #define INIT_G() do { \ count = -1; \ } while (0) // If GNUisms are not available... //static void *mempcpy(void *_dst, const void *_src, int n) //{ // memcpy(_dst, _src, n); // return (char*)_dst + n; //} static int send_pack(struct in_addr *src_addr, struct in_addr *dst_addr, struct sockaddr_ll *ME, struct sockaddr_ll *HE) { int err; unsigned char buf[256]; struct arphdr *ah = (struct arphdr *) buf; unsigned char *p = (unsigned char *) (ah + 1); ah->ar_hrd = htons(ARPHRD_ETHER); ah->ar_pro = htons(ETH_P_IP); ah->ar_hln = ME->sll_halen; ah->ar_pln = 4; ah->ar_op = option_mask32 & ADVERT ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST); p = mempcpy(p, &ME->sll_addr, ah->ar_hln); p = mempcpy(p, src_addr, 4); if (option_mask32 & ADVERT) p = mempcpy(p, &ME->sll_addr, ah->ar_hln); else p = mempcpy(p, &HE->sll_addr, ah->ar_hln); p = mempcpy(p, dst_addr, 4); err = sendto(sock_fd, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE)); if (err == p - buf) { last = MONOTONIC_US(); sent++; if (!(option_mask32 & UNICASTING)) brd_sent++; } return err; } static void finish(void) NORETURN; static void finish(void) { if (!(option_mask32 & QUIET)) { printf("Sent %u probe(s) (%u broadcast(s))\n" "Received %u repl%s" " (%u request(s), %u broadcast(s))\n", sent, brd_sent, received, (received == 1) ? "ies" : "y", req_recv, brd_recv); } if (option_mask32 & DAD) exit(!!received); if (option_mask32 & UNSOLICITED) exit(EXIT_SUCCESS); exit(!received); } static void catcher(void) { unsigned now; now = MONOTONIC_US(); if (start == 0) start = now; if (count == 0 || (timeout_us && (now - start) > timeout_us)) finish(); /* count < 0 means "infinite count" */ if (count > 0) count--; if (last == 0 || (now - last) > 500000) { send_pack(&src, &dst, &me, &he); if (count == 0 && (option_mask32 & UNSOLICITED)) finish(); } alarm(1); } static bool recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM) { struct arphdr *ah = (struct arphdr *) buf; unsigned char *p = (unsigned char *) (ah + 1); struct in_addr src_ip, dst_ip; /* moves below assume in_addr is 4 bytes big, ensure that */ struct BUG_in_addr_must_be_4 { char BUG_in_addr_must_be_4[ sizeof(struct in_addr) == 4 ? 1 : -1 ]; char BUG_s_addr_must_be_4[ sizeof(src_ip.s_addr) == 4 ? 1 : -1 ]; }; /* Filter out wild packets */ if (FROM->sll_pkttype != PACKET_HOST && FROM->sll_pkttype != PACKET_BROADCAST && FROM->sll_pkttype != PACKET_MULTICAST) return false; /* Only these types are recognized */ if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY)) return false; /* ARPHRD check and this darned FDDI hack here :-( */ if (ah->ar_hrd != htons(FROM->sll_hatype) && (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER))) return false; /* Protocol must be IP. */ if (ah->ar_pro != htons(ETH_P_IP) || (ah->ar_pln != 4) || (ah->ar_hln != me.sll_halen) || (len < (int)(sizeof(*ah) + 2 * (4 + ah->ar_hln)))) return false; move_from_unaligned32(src_ip.s_addr, p + ah->ar_hln); move_from_unaligned32(dst_ip.s_addr, p + ah->ar_hln + 4 + ah->ar_hln); if (dst.s_addr != src_ip.s_addr) return false; if (!(option_mask32 & DAD)) { if ((src.s_addr != dst_ip.s_addr) || (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln))) return false; } else { /* DAD packet was: src_ip = 0 (or some src) src_hw = ME dst_ip = tested address dst_hw = We fail, if receive request/reply with: src_ip = tested_address src_hw != ME if src_ip in request was not zero, check also that it matches to dst_ip, otherwise dst_ip/dst_hw do not matter. */ if ((memcmp(p, &me.sll_addr, me.sll_halen) == 0) || (src.s_addr && src.s_addr != dst_ip.s_addr)) return false; } if (!(option_mask32 & QUIET)) { int s_printed = 0; printf("%scast re%s from %s [%s]", FROM->sll_pkttype == PACKET_HOST ? "Uni" : "Broad", ah->ar_op == htons(ARPOP_REPLY) ? "ply" : "quest", inet_ntoa(src_ip), ether_ntoa((struct ether_addr *) p)); if (dst_ip.s_addr != src.s_addr) { printf("for %s ", inet_ntoa(dst_ip)); s_printed = 1; } if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) { if (!s_printed) printf("for "); printf("[%s]", ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4)); } if (last) { unsigned diff = MONOTONIC_US() - last; printf(" %u.%03ums\n", diff / 1000, diff % 1000); } else { printf(" UNSOLICITED?\n"); } fflush_all(); } received++; if (FROM->sll_pkttype != PACKET_HOST) brd_recv++; if (ah->ar_op == htons(ARPOP_REQUEST)) req_recv++; if (option_mask32 & QUIT_ON_REPLY) finish(); if (!(option_mask32 & BCAST_ONLY)) { memcpy(he.sll_addr, p, me.sll_halen); option_mask32 |= UNICASTING; } return true; } int arping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int arping_main(int argc UNUSED_PARAM, char **argv) { const char *device = "eth0"; char *source = NULL; char *target; unsigned char *packet; char *err_str; INIT_G(); sock_fd = xsocket(AF_PACKET, SOCK_DGRAM, 0); // Drop suid root privileges // Need to remove SUID_NEVER from applets.h for this to work //xsetuid(getuid()); err_str = xasprintf("interface %s %%s", device); { unsigned opt; char *str_timeout; /* Dad also sets quit_on_reply. * Advert also sets unsolicited. */ opt_complementary = "=1:Df:AU:c+"; opt = getopt32(argv, "DUAqfbc:w:I:s:", &count, &str_timeout, &device, &source); if (opt & 0x80) /* -w: timeout */ timeout_us = xatou_range(str_timeout, 0, INT_MAX/2000000) * 1000000 + 500000; //if (opt & 0x200) /* -s: source */ option_mask32 &= 0x3f; /* set respective flags */ } target = argv[optind]; xfunc_error_retval = 2; { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy_IFNAMSIZ(ifr.ifr_name, device); /* We use ifr.ifr_name in error msg so that problem * with truncated name will be visible */ ioctl_or_perror_and_die(sock_fd, SIOCGIFINDEX, &ifr, err_str, "not found"); me.sll_ifindex = ifr.ifr_ifindex; xioctl(sock_fd, SIOCGIFFLAGS, (char *) &ifr); if (!(ifr.ifr_flags & IFF_UP)) { bb_error_msg_and_die(err_str, "is down"); } if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) { bb_error_msg(err_str, "is not ARPable"); return (option_mask32 & DAD ? 0 : 2); } } /* if (!inet_aton(target, &dst)) - not needed */ { len_and_sockaddr *lsa; lsa = xhost_and_af2sockaddr(target, 0, AF_INET); dst = lsa->u.sin.sin_addr; if (ENABLE_FEATURE_CLEAN_UP) free(lsa); } if (source && !inet_aton(source, &src)) { bb_error_msg_and_die("invalid source address %s", source); } if ((option_mask32 & (DAD|UNSOLICITED)) == UNSOLICITED && src.s_addr == 0) src = dst; if (!(option_mask32 & DAD) || src.s_addr) { struct sockaddr_in saddr; int probe_fd = xsocket(AF_INET, SOCK_DGRAM, 0); setsockopt_bindtodevice(probe_fd, device); memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; if (src.s_addr) { /* Check that this is indeed our IP */ saddr.sin_addr = src; xbind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)); } else { /* !(option_mask32 & DAD) case */ /* Find IP address on this iface */ socklen_t alen = sizeof(saddr); saddr.sin_port = htons(1025); saddr.sin_addr = dst; if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, &const_int_1, sizeof(const_int_1)) == -1) bb_perror_msg("setsockopt(SO_DONTROUTE)"); xconnect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)); getsockname(probe_fd, (struct sockaddr *) &saddr, &alen); //never happens: //if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) == -1) // bb_perror_msg_and_die("getsockname"); if (saddr.sin_family != AF_INET) bb_error_msg_and_die("no IP address configured"); src = saddr.sin_addr; } close(probe_fd); } me.sll_family = AF_PACKET; //me.sll_ifindex = ifindex; - done before me.sll_protocol = htons(ETH_P_ARP); xbind(sock_fd, (struct sockaddr *) &me, sizeof(me)); { socklen_t alen = sizeof(me); getsockname(sock_fd, (struct sockaddr *) &me, &alen); //never happens: //if (getsockname(sock_fd, (struct sockaddr *) &me, &alen) == -1) // bb_perror_msg_and_die("getsockname"); } if (me.sll_halen == 0) { bb_error_msg(err_str, "is not ARPable (no ll address)"); return (option_mask32 & DAD ? 0 : 2); } he = me; memset(he.sll_addr, -1, he.sll_halen); if (!(option_mask32 & QUIET)) { /* inet_ntoa uses static storage, can't use in same printf */ printf("ARPING to %s", inet_ntoa(dst)); printf(" from %s via %s\n", inet_ntoa(src), device); } signal_SA_RESTART_empty_mask(SIGINT, (void (*)(int))finish); signal_SA_RESTART_empty_mask(SIGALRM, (void (*)(int))catcher); catcher(); packet = xmalloc(4096); while (1) { sigset_t sset, osset; struct sockaddr_ll from; socklen_t alen = sizeof(from); int cc; cc = recvfrom(sock_fd, packet, 4096, 0, (struct sockaddr *) &from, &alen); if (cc < 0) { bb_perror_msg("recvfrom"); continue; } sigemptyset(&sset); sigaddset(&sset, SIGALRM); sigaddset(&sset, SIGINT); sigprocmask(SIG_BLOCK, &sset, &osset); recv_pack(packet, cc, &from); sigprocmask(SIG_SETMASK, &osset, NULL); } } busybox-1.22.1/networking/httpd_ssi.c0000644000000000000000000001017312263563520016330 0ustar rootroot/* * Copyright (c) 2009 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* * This program is a CGI application. It processes server-side includes: * * * Usage: put these lines in httpd.conf: * * *.html:/bin/httpd_ssi * *.htm:/bin/httpd_ssi */ /* Build a-la i486-linux-uclibc-gcc \ -static -static-libgcc \ -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 \ -Wall -Wshadow -Wwrite-strings -Wundef -Wstrict-prototypes -Werror \ -Wold-style-definition -Wdeclaration-after-statement -Wno-pointer-sign \ -Wmissing-prototypes -Wmissing-declarations \ -Os -fno-builtin-strlen -finline-limit=0 -fomit-frame-pointer \ -ffunction-sections -fdata-sections -fno-guess-branch-probability \ -funsigned-char \ -falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1 \ -march=i386 -mpreferred-stack-boundary=2 \ -Wl,-Map -Wl,link.map -Wl,--warn-common -Wl,--sort-common -Wl,--gc-sections \ httpd_ssi.c -o httpd_ssi */ /* Size (i386, static uclibc, approximate): * text data bss dec hex filename * 9487 160 68552 78199 13177 httpd_ssi * * Note: it wouldn't be too hard to get rid of stdio and strdup, * (especially that fgets() mangles NULs...) */ #include #include #include #include #include #include #include #include #include #include #include static char* skip_whitespace(char *s) { while (*s == ' ' || *s == '\t') ++s; return s; } static char line[64 * 1024]; static void process_includes(const char *filename) { int curdir_fd; char *end; FILE *fp = fopen(filename, "r"); if (!fp) exit(1); /* Ensure that nested includes are relative: * if we include a/1.htm and it includes b/2.htm, * we need to include a/b/2.htm, not b/2.htm */ curdir_fd = -1; end = strrchr(filename, '/'); if (end) { curdir_fd = open(".", O_RDONLY); /* *end = '\0' would mishandle "/file.htm" */ end[1] = '\0'; chdir(filename); } #define INCLUDE " | buf2 | --------------> | | +-------+ size2++ +------+ size2-- +----------+ size1: "how many bytes are buffered for pty between rdidx1 and wridx1?" size2: "how many bytes are buffered for socket between rdidx2 and wridx2?" Each session has got two buffers. Buffers are circular. If sizeN == 0, buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases rdidxN == wridxN. */ again: FD_ZERO(&rdfdset); FD_ZERO(&wrfdset); /* Select on the master socket, all telnet sockets and their * ptys if there is room in their session buffers. * NB: scalability problem: we recalculate entire bitmap * before each select. Can be a problem with 500+ connections. */ ts = G.sessions; while (ts) { struct tsession *next = ts->next; /* in case we free ts */ if (ts->shell_pid == -1) { /* Child died and we detected that */ free_session(ts); } else { if (ts->size1 > 0) /* can write to pty */ FD_SET(ts->ptyfd, &wrfdset); if (ts->size1 < BUFSIZE) /* can read from socket */ FD_SET(ts->sockfd_read, &rdfdset); if (ts->size2 > 0) /* can write to socket */ FD_SET(ts->sockfd_write, &wrfdset); if (ts->size2 < BUFSIZE) /* can read from pty */ FD_SET(ts->ptyfd, &rdfdset); } ts = next; } if (!IS_INETD) { FD_SET(master_fd, &rdfdset); /* This is needed because free_session() does not * take master_fd into account when it finds new * maxfd among remaining fd's */ if (master_fd > G.maxfd) G.maxfd = master_fd; } { struct timeval *tv_ptr = NULL; #if ENABLE_FEATURE_TELNETD_INETD_WAIT struct timeval tv; if ((opt & OPT_WAIT) && !G.sessions) { tv.tv_sec = sec_linger; tv.tv_usec = 0; tv_ptr = &tv; } #endif count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, tv_ptr); } if (count == 0) /* "telnetd -w SEC" timed out */ return 0; if (count < 0) goto again; /* EINTR or ENOMEM */ #if ENABLE_FEATURE_TELNETD_STANDALONE /* Check for and accept new sessions */ if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) { int fd; struct tsession *new_ts; fd = accept(master_fd, NULL, NULL); if (fd < 0) goto again; close_on_exec_on(fd); /* Create a new session and link it into active list */ new_ts = make_new_session(fd); if (new_ts) { new_ts->next = G.sessions; G.sessions = new_ts; } else { close(fd); } } #endif /* Then check for data tunneling */ ts = G.sessions; while (ts) { /* For all sessions... */ struct tsession *next = ts->next; /* in case we free ts */ if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) { int num_totty; unsigned char *ptr; /* Write to pty from buffer 1 */ ptr = remove_iacs(ts, &num_totty); count = safe_write(ts->ptyfd, ptr, num_totty); if (count < 0) { if (errno == EAGAIN) goto skip1; goto kill_session; } ts->size1 -= count; ts->wridx1 += count; if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */ ts->wridx1 = 0; } skip1: if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) { /* Write to socket from buffer 2 */ count = MIN(BUFSIZE - ts->wridx2, ts->size2); count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count); if (count < 0) { if (errno == EAGAIN) goto skip2; goto kill_session; } ts->size2 -= count; ts->wridx2 += count; if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */ ts->wridx2 = 0; } skip2: /* Should not be needed, but... remove_iacs is actually buggy * (it cannot process iacs which wrap around buffer's end)! * Since properly fixing it requires writing bigger code, * we rely instead on this code making it virtually impossible * to have wrapped iac (people don't type at 2k/second). * It also allows for bigger reads in common case. */ if (ts->size1 == 0) { ts->rdidx1 = 0; ts->wridx1 = 0; } if (ts->size2 == 0) { ts->rdidx2 = 0; ts->wridx2 = 0; } if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) { /* Read from socket to buffer 1 */ count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1); count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count); if (count <= 0) { if (count < 0 && errno == EAGAIN) goto skip3; goto kill_session; } /* Ignore trailing NUL if it is there */ if (!TS_BUF1(ts)[ts->rdidx1 + count - 1]) { --count; } ts->size1 += count; ts->rdidx1 += count; if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */ ts->rdidx1 = 0; } skip3: if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) { /* Read from pty to buffer 2 */ count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2); count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count); if (count <= 0) { if (count < 0 && errno == EAGAIN) goto skip4; goto kill_session; } ts->size2 += count; ts->rdidx2 += count; if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */ ts->rdidx2 = 0; } skip4: ts = next; continue; kill_session: if (ts->shell_pid > 0) update_utmp(ts->shell_pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL); free_session(ts); ts = next; } goto again; } busybox-1.22.1/networking/httpd_indexcgi.c0000644000000000000000000002336312263563520017331 0ustar rootroot/* * Copyright (c) 2007 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* * This program is a CGI application. It outputs directory index page. * Put it into cgi-bin/index.cgi and chmod 0755. */ /* Build a-la i486-linux-uclibc-gcc \ -static -static-libgcc \ -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 \ -Wall -Wshadow -Wwrite-strings -Wundef -Wstrict-prototypes -Werror \ -Wold-style-definition -Wdeclaration-after-statement -Wno-pointer-sign \ -Wmissing-prototypes -Wmissing-declarations \ -Os -fno-builtin-strlen -finline-limit=0 -fomit-frame-pointer \ -ffunction-sections -fdata-sections -fno-guess-branch-probability \ -funsigned-char \ -falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1 \ -march=i386 -mpreferred-stack-boundary=2 \ -Wl,-Map -Wl,link.map -Wl,--warn-common -Wl,--sort-common -Wl,--gc-sections \ httpd_indexcgi.c -o index.cgi */ /* We don't use printf, as it pulls in >12 kb of code from uclibc (i386). */ /* Currently malloc machinery is the biggest part of libc we pull in. */ /* We have only one realloc and one strdup, any idea how to do without? */ /* Size (i386, static uclibc, approximate): * text data bss dec hex filename * 13036 44 3052 16132 3f04 index.cgi * 2576 4 2048 4628 1214 index.cgi.o */ #define _GNU_SOURCE 1 /* for strchrnul */ #include #include #include #include #include #include #include #include #include #include /* Appearance of the table is controlled by style sheet *ONLY*, * formatting code uses to apply style * to elements. Edit stylesheet to your liking and recompile. */ #define STYLE_STR \ "" "\n"\ typedef struct dir_list_t { char *dl_name; mode_t dl_mode; off_t dl_size; time_t dl_mtime; } dir_list_t; static int compare_dl(dir_list_t *a, dir_list_t *b) { /* ".." is 'less than' any other dir entry */ if (strcmp(a->dl_name, "..") == 0) { return -1; } if (strcmp(b->dl_name, "..") == 0) { return 1; } if (S_ISDIR(a->dl_mode) != S_ISDIR(b->dl_mode)) { /* 1 if b is a dir (and thus a is 'after' b, a > b), * else -1 (a < b) */ return (S_ISDIR(b->dl_mode) != 0) ? 1 : -1; } return strcmp(a->dl_name, b->dl_name); } static char buffer[2*1024 > sizeof(STYLE_STR) ? 2*1024 : sizeof(STYLE_STR)]; static char *dst = buffer; enum { BUFFER_SIZE = sizeof(buffer), HEADROOM = 64, }; /* After this call, you have at least size + HEADROOM bytes available * ahead of dst */ static void guarantee(int size) { if (buffer + (BUFFER_SIZE-HEADROOM) - dst >= size) return; write(STDOUT_FILENO, buffer, dst - buffer); dst = buffer; } /* NB: formatters do not store terminating NUL! */ /* HEADROOM bytes are available after dst after this call */ static void fmt_str(/*char *dst,*/ const char *src) { unsigned len = strlen(src); guarantee(len); memcpy(dst, src, len); dst += len; } /* HEADROOM bytes after dst are available after this call */ static void fmt_url(/*char *dst,*/ const char *name) { while (*name) { unsigned c = *name++; guarantee(3); *dst = c; if ((c - '0') > 9 /* not a digit */ && ((c|0x20) - 'a') > ('z' - 'a') /* not A-Z or a-z */ && !strchr("._-+@", c) ) { *dst++ = '%'; *dst++ = "0123456789ABCDEF"[c >> 4]; *dst = "0123456789ABCDEF"[c & 0xf]; } dst++; } } /* HEADROOM bytes are available after dst after this call */ static void fmt_html(/*char *dst,*/ const char *name) { while (*name) { char c = *name++; if (c == '<') fmt_str("<"); else if (c == '>') fmt_str(">"); else if (c == '&') { fmt_str("&"); } else { guarantee(1); *dst++ = c; continue; } } } /* HEADROOM bytes are available after dst after this call */ static void fmt_ull(/*char *dst,*/ unsigned long long n) { char buf[sizeof(n)*3 + 2]; char *p; p = buf + sizeof(buf) - 1; *p = '\0'; do { *--p = (n % 10) + '0'; n /= 10; } while (n); fmt_str(/*dst,*/ p); } /* Does not call guarantee - eats into headroom instead */ static void fmt_02u(/*char *dst,*/ unsigned n) { /* n %= 100; - not needed, callers don't pass big n */ dst[0] = (n / 10) + '0'; dst[1] = (n % 10) + '0'; dst += 2; } /* Does not call guarantee - eats into headroom instead */ static void fmt_04u(/*char *dst,*/ unsigned n) { /* n %= 10000; - not needed, callers don't pass big n */ fmt_02u(n / 100); fmt_02u(n % 100); } int main(int argc, char *argv[]) { dir_list_t *dir_list; dir_list_t *cdir; unsigned dir_list_count; unsigned count_dirs; unsigned count_files; unsigned long long size_total; int odd; DIR *dirp; char *location; location = getenv("REQUEST_URI"); if (!location) return 1; /* drop URL arguments if any */ strchrnul(location, '?')[0] = '\0'; if (location[0] != '/' || strstr(location, "//") || strstr(location, "/../") || strcmp(strrchr(location, '/'), "/..") == 0 ) { return 1; } if (chdir("..") || (location[1] && chdir(location + 1)) ) { return 1; } dirp = opendir("."); if (!dirp) return 1; dir_list = NULL; dir_list_count = 0; while (1) { struct dirent *dp; struct stat sb; dp = readdir(dirp); if (!dp) break; if (dp->d_name[0] == '.' && !dp->d_name[1]) continue; if (stat(dp->d_name, &sb) != 0) continue; dir_list = realloc(dir_list, (dir_list_count + 1) * sizeof(dir_list[0])); dir_list[dir_list_count].dl_name = strdup(dp->d_name); dir_list[dir_list_count].dl_mode = sb.st_mode; dir_list[dir_list_count].dl_size = sb.st_size; dir_list[dir_list_count].dl_mtime = sb.st_mtime; dir_list_count++; } closedir(dirp); qsort(dir_list, dir_list_count, sizeof(dir_list[0]), (void*)compare_dl); fmt_str( "" /* Additional headers (currently none) */ "\r\n" /* Mandatory empty line after headers */ "Index of "); /* Guard against directories with &, > etc */ fmt_html(location); fmt_str( "\n" STYLE_STR "" "\n" "" "\n" "

Index of "); fmt_html(location); fmt_str( "

" "\n" "" "\n" "" "\n" "
NameSizeLast modified" "\n"); odd = 0; count_dirs = 0; count_files = 0; size_total = 0; cdir = dir_list; while (dir_list_count--) { struct tm *ptm; if (S_ISDIR(cdir->dl_mode)) { count_dirs++; } else if (S_ISREG(cdir->dl_mode)) { count_files++; size_total += cdir->dl_size; } else goto next; fmt_str("
"); fmt_html(cdir->dl_name); /* < etc */ if (S_ISDIR(cdir->dl_mode)) *dst++ = '/'; fmt_str(""); if (S_ISREG(cdir->dl_mode)) fmt_ull(cdir->dl_size); fmt_str(""); ptm = gmtime(&cdir->dl_mtime); fmt_04u(1900 + ptm->tm_year); *dst++ = '-'; fmt_02u(ptm->tm_mon + 1); *dst++ = '-'; fmt_02u(ptm->tm_mday); *dst++ = ' '; fmt_02u(ptm->tm_hour); *dst++ = ':'; fmt_02u(ptm->tm_min); *dst++ = ':'; fmt_02u(ptm->tm_sec); *dst++ = '\n'; odd = 1 - odd; next: cdir++; } fmt_str("
Files: "); fmt_ull(count_files); /* count_dirs - 1: we don't want to count ".." */ fmt_str(", directories: "); fmt_ull(count_dirs - 1); fmt_str(""); fmt_ull(size_total); fmt_str("\n"); /* "
" - why bother? */ guarantee(BUFFER_SIZE * 2); /* flush */ return 0; } busybox-1.22.1/networking/ether-wake.c0000644000000000000000000002170212263563520016363 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * ether-wake.c - Send a magic packet to wake up sleeping machines. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Author: Donald Becker, http://www.scyld.com/"; http://www.scyld.com/wakeonlan.html * Busybox port: Christian Volkmann * Used version of ether-wake.c: v1.09 11/12/2003 Donald Becker, http://www.scyld.com/"; */ /* full usage according Donald Becker * usage: ether-wake [-i ] [-p aa:bb:cc:dd[:ee:ff]] 00:11:22:33:44:55\n" * * This program generates and transmits a Wake-On-LAN (WOL)\n" * \"Magic Packet\", used for restarting machines that have been\n" * soft-powered-down (ACPI D3-warm state).\n" * It currently generates the standard AMD Magic Packet format, with\n" * an optional password appended.\n" * * The single required parameter is the Ethernet MAC (station) address\n" * of the machine to wake or a host ID with known NSS 'ethers' entry.\n" * The MAC address may be found with the 'arp' program while the target\n" * machine is awake.\n" * * Options:\n" * -b Send wake-up packet to the broadcast address.\n" * -D Increase the debug level.\n" * -i ifname Use interface IFNAME instead of the default 'eth0'.\n" * -p Append the four or six byte password PW to the packet.\n" * A password is only required for a few adapter types.\n" * The password may be specified in ethernet hex format\n" * or dotted decimal (Internet address)\n" * -p 00:22:44:66:88:aa\n" * -p 192.168.1.1\n"; * * * This program generates and transmits a Wake-On-LAN (WOL) "Magic Packet", * used for restarting machines that have been soft-powered-down * (ACPI D3-warm state). It currently generates the standard AMD Magic Packet * format, with an optional password appended. * * This software may be used and distributed according to the terms * of the GNU Public License, incorporated herein by reference. * Contact the author for use under other terms. * * This source file was originally part of the network tricks package, and * is now distributed to support the Scyld Beowulf system. * Copyright 1999-2003 Donald Becker and Scyld Computing Corporation. * * The author may be reached as becker@scyld, or C/O * Scyld Computing Corporation * 914 Bay Ridge Road, Suite 220 * Annapolis MD 21403 * * Notes: * On some systems dropping root capability allows the process to be * dumped, traced or debugged. * If someone traces this program, they get control of a raw socket. * Linux handles this safely, but beware when porting this program. * * An alternative to needing 'root' is using a UDP broadcast socket, however * doing so only works with adapters configured for unicast+broadcast Rx * filter. That configuration consumes more power. */ //usage:#define ether_wake_trivial_usage //usage: "[-b] [-i IFACE] [-p aa:bb:cc:dd[:ee:ff]/a.b.c.d] MAC" //usage:#define ether_wake_full_usage "\n\n" //usage: "Send a magic packet to wake up sleeping machines.\n" //usage: "MAC must be a station address (00:11:22:33:44:55) or\n" //usage: "a hostname with a known 'ethers' entry.\n" //usage: "\n -b Broadcast the packet" //usage: "\n -i IFACE Interface to use (default eth0)" //usage: "\n -p PASSWORD Append four or six byte PASSWORD to the packet" #include "libbb.h" #include #include #include /* Note: PF_INET, SOCK_DGRAM, IPPROTO_UDP would allow SIOCGIFHWADDR to * work as non-root, but we need SOCK_PACKET to specify the Ethernet * destination address. */ #ifdef PF_PACKET # define whereto_t sockaddr_ll # define make_socket() xsocket(PF_PACKET, SOCK_RAW, 0) #else # define whereto_t sockaddr # define make_socket() xsocket(AF_INET, SOCK_PACKET, SOCK_PACKET) #endif #ifdef DEBUG # define bb_debug_msg(fmt, args...) fprintf(stderr, fmt, ## args) void bb_debug_dump_packet(unsigned char *outpack, int pktsize) { int i; printf("packet dump:\n"); for (i = 0; i < pktsize; ++i) { printf("%2.2x ", outpack[i]); if (i % 20 == 19) bb_putchar('\n'); } printf("\n\n"); } #else # define bb_debug_msg(fmt, args...) ((void)0) # define bb_debug_dump_packet(outpack, pktsize) ((void)0) #endif /* Convert the host ID string to a MAC address. * The string may be a: * Host name * IP address string * MAC address string */ static void get_dest_addr(const char *hostid, struct ether_addr *eaddr) { struct ether_addr *eap; eap = ether_aton_r(hostid, eaddr); if (eap) { bb_debug_msg("The target station address is %s\n\n", ether_ntoa(eap)); #if !defined(__UCLIBC__) || UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 30) } else if (ether_hostton(hostid, eaddr) == 0) { bb_debug_msg("Station address for hostname %s is %s\n\n", hostid, ether_ntoa(eaddr)); #endif } else { bb_show_usage(); } } #define PKT_HEADER_SIZE (20 + 16*6) static int fill_pkt_header(unsigned char *pkt, struct ether_addr *eaddr, int broadcast) { int i; unsigned char *station_addr = eaddr->ether_addr_octet; memset(pkt, 0xff, 6); if (!broadcast) memcpy(pkt, station_addr, 6); pkt += 6; memcpy(pkt, station_addr, 6); /* 6 */ pkt += 6; *pkt++ = 0x08; /* 12 */ /* Or 0x0806 for ARP, 0x8035 for RARP */ *pkt++ = 0x42; /* 13 */ memset(pkt, 0xff, 6); /* 14 */ for (i = 0; i < 16; ++i) { pkt += 6; memcpy(pkt, station_addr, 6); /* 20,26,32,... */ } return PKT_HEADER_SIZE; /* length of packet */ } static int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd) { unsigned passwd[6]; int byte_cnt, i; /* handle MAC format */ byte_cnt = sscanf(ethoptarg, "%2x:%2x:%2x:%2x:%2x:%2x", &passwd[0], &passwd[1], &passwd[2], &passwd[3], &passwd[4], &passwd[5]); /* handle IP format */ // FIXME: why < 4?? should it be < 6? if (byte_cnt < 4) byte_cnt = sscanf(ethoptarg, "%u.%u.%u.%u", &passwd[0], &passwd[1], &passwd[2], &passwd[3]); if (byte_cnt < 4) { bb_error_msg("can't read Wake-On-LAN pass"); return 0; } // TODO: check invalid numbers >255?? for (i = 0; i < byte_cnt; ++i) wol_passwd[i] = passwd[i]; bb_debug_msg("password: %2.2x %2.2x %2.2x %2.2x (%d)\n\n", wol_passwd[0], wol_passwd[1], wol_passwd[2], wol_passwd[3], byte_cnt); return byte_cnt; } int ether_wake_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ether_wake_main(int argc UNUSED_PARAM, char **argv) { const char *ifname = "eth0"; char *pass; unsigned flags; unsigned char wol_passwd[6]; int wol_passwd_sz = 0; int s; /* Raw socket */ int pktsize; unsigned char outpack[PKT_HEADER_SIZE + 6 /* max passwd size */ + 16 /* paranoia */]; struct ether_addr eaddr; struct whereto_t whereto; /* who to wake up */ /* handle misc user options */ opt_complementary = "=1"; flags = getopt32(argv, "bi:p:", &ifname, &pass); if (flags & 4) /* -p */ wol_passwd_sz = get_wol_pw(pass, wol_passwd); flags &= 1; /* we further interested only in -b [bcast] flag */ /* create the raw socket */ s = make_socket(); /* now that we have a raw socket we can drop root */ /* xsetuid(getuid()); - but save on code size... */ /* look up the dest mac address */ get_dest_addr(argv[optind], &eaddr); /* fill out the header of the packet */ pktsize = fill_pkt_header(outpack, &eaddr, flags /* & 1 OPT_BROADCAST */); bb_debug_dump_packet(outpack, pktsize); /* Fill in the source address, if possible. */ #ifdef __linux__ { struct ifreq if_hwaddr; strncpy_IFNAMSIZ(if_hwaddr.ifr_name, ifname); ioctl_or_perror_and_die(s, SIOCGIFHWADDR, &if_hwaddr, "SIOCGIFHWADDR on %s failed", ifname); memcpy(outpack+6, if_hwaddr.ifr_hwaddr.sa_data, 6); # ifdef DEBUG { unsigned char *hwaddr = if_hwaddr.ifr_hwaddr.sa_data; printf("The hardware address (SIOCGIFHWADDR) of %s is type %d " "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n\n", ifname, if_hwaddr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]); } # endif } #endif /* __linux__ */ bb_debug_dump_packet(outpack, pktsize); /* append the password if specified */ if (wol_passwd_sz > 0) { memcpy(outpack+pktsize, wol_passwd, wol_passwd_sz); pktsize += wol_passwd_sz; } bb_debug_dump_packet(outpack, pktsize); /* This is necessary for broadcasts to work */ if (flags /* & 1 OPT_BROADCAST */) { if (setsockopt_broadcast(s) != 0) bb_perror_msg("SO_BROADCAST"); } #if defined(PF_PACKET) { struct ifreq ifr; strncpy_IFNAMSIZ(ifr.ifr_name, ifname); xioctl(s, SIOCGIFINDEX, &ifr); memset(&whereto, 0, sizeof(whereto)); whereto.sll_family = AF_PACKET; whereto.sll_ifindex = ifr.ifr_ifindex; /* The manual page incorrectly claims the address must be filled. We do so because the code may change to match the docs. */ whereto.sll_halen = ETH_ALEN; memcpy(whereto.sll_addr, outpack, ETH_ALEN); } #else whereto.sa_family = 0; strcpy(whereto.sa_data, ifname); #endif xsendto(s, outpack, pktsize, (struct sockaddr *)&whereto, sizeof(whereto)); if (ENABLE_FEATURE_CLEAN_UP) close(s); return EXIT_SUCCESS; } busybox-1.22.1/networking/route.c0000644000000000000000000004333412263563520015472 0ustar rootroot/* vi: set sw=4 ts=4: */ /* route * * Similar to the standard Unix route, but with only the necessary * parts for AF_INET and AF_INET6 * * Bjorn Wesen, Axis Communications AB * * Author of the original route: * Fred N. van Kempen, * (derived from FvK's 'route.c 1.70 01/04/94') * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * * displayroute() code added by Vladimir N. Oleynik * adjustments by Larry Doolittle * * IPV6 support added by Bart Visscher */ /* 2004/03/09 Manuel Novoa III * * Rewritten to fix several bugs, add additional error checking, and * remove ridiculous amounts of bloat. */ //usage:#define route_trivial_usage //usage: "[{add|del|delete}]" //usage:#define route_full_usage "\n\n" //usage: "Edit kernel routing tables\n" //usage: "\n -n Don't resolve names" //usage: "\n -e Display other/more information" //usage: "\n -A inet" IF_FEATURE_IPV6("{6}") " Select address family" #include #include #include "libbb.h" #include "inet_common.h" #ifndef RTF_UP /* Keep this in sync with /usr/src/linux/include/linux/route.h */ #define RTF_UP 0x0001 /* route usable */ #define RTF_GATEWAY 0x0002 /* destination is a gateway */ #define RTF_HOST 0x0004 /* host entry (net otherwise) */ #define RTF_REINSTATE 0x0008 /* reinstate route after tmout */ #define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */ #define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */ #define RTF_MTU 0x0040 /* specific MTU for this route */ #ifndef RTF_MSS #define RTF_MSS RTF_MTU /* Compatibility :-( */ #endif #define RTF_WINDOW 0x0080 /* per route window clamping */ #define RTF_IRTT 0x0100 /* Initial round trip time */ #define RTF_REJECT 0x0200 /* Reject route */ #endif #if defined(SIOCADDRTOLD) || defined(RTF_IRTT) /* route */ #define HAVE_NEW_ADDRT 1 #endif #if HAVE_NEW_ADDRT #define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr) #define full_mask(x) (x) #else #define mask_in_addr(x) ((x).rt_genmask) #define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr) #endif /* The RTACTION entries must agree with tbl_verb[] below! */ #define RTACTION_ADD 1 #define RTACTION_DEL 2 /* For the various tbl_*[] arrays, the 1st byte is the offset to * the next entry and the 2nd byte is return value. */ #define NET_FLAG 1 #define HOST_FLAG 2 /* We remap '-' to '#' to avoid problems with getopt. */ static const char tbl_hash_net_host[] ALIGN1 = "\007\001#net\0" /* "\010\002#host\0" */ "\007\002#host" /* Since last, we can save a byte. */ ; #define KW_TAKES_ARG 020 #define KW_SETS_FLAG 040 #define KW_IPVx_METRIC 020 #define KW_IPVx_NETMASK 021 #define KW_IPVx_GATEWAY 022 #define KW_IPVx_MSS 023 #define KW_IPVx_WINDOW 024 #define KW_IPVx_IRTT 025 #define KW_IPVx_DEVICE 026 #define KW_IPVx_FLAG_ONLY 040 #define KW_IPVx_REJECT 040 #define KW_IPVx_MOD 041 #define KW_IPVx_DYN 042 #define KW_IPVx_REINSTATE 043 static const char tbl_ipvx[] ALIGN1 = /* 020 is the "takes an arg" bit */ #if HAVE_NEW_ADDRT "\011\020metric\0" #endif "\012\021netmask\0" "\005\022gw\0" "\012\022gateway\0" "\006\023mss\0" "\011\024window\0" #ifdef RTF_IRTT "\007\025irtt\0" #endif "\006\026dev\0" "\011\026device\0" /* 040 is the "sets a flag" bit - MUST match flags_ipvx[] values below. */ #ifdef RTF_REJECT "\011\040reject\0" #endif "\006\041mod\0" "\006\042dyn\0" /* "\014\043reinstate\0" */ "\013\043reinstate" /* Since last, we can save a byte. */ ; static const int flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */ #ifdef RTF_REJECT RTF_REJECT, #endif RTF_MODIFIED, RTF_DYNAMIC, RTF_REINSTATE }; static int kw_lookup(const char *kwtbl, char ***pargs) { if (**pargs) { do { if (strcmp(kwtbl+2, **pargs) == 0) { /* Found a match. */ *pargs += 1; if (kwtbl[1] & KW_TAKES_ARG) { if (!**pargs) { /* No more args! */ bb_show_usage(); } *pargs += 1; /* Calling routine will use args[-1]. */ } return kwtbl[1]; } kwtbl += *kwtbl; } while (*kwtbl); } return 0; } /* Add or delete a route, depending on action. */ static NOINLINE void INET_setroute(int action, char **args) { /* char buffer instead of bona-fide struct avoids aliasing warning */ char rt_buf[sizeof(struct rtentry)]; struct rtentry *const rt = (void *)rt_buf; const char *netmask = NULL; int skfd, isnet, xflag; /* Grab the -net or -host options. Remember they were transformed. */ xflag = kw_lookup(tbl_hash_net_host, &args); /* If we did grab -net or -host, make sure we still have an arg left. */ if (*args == NULL) { bb_show_usage(); } /* Clean out the RTREQ structure. */ memset(rt, 0, sizeof(*rt)); { const char *target = *args++; char *prefix; /* recognize x.x.x.x/mask format. */ prefix = strchr(target, '/'); if (prefix) { int prefix_len; prefix_len = xatoul_range(prefix+1, 0, 32); mask_in_addr(*rt) = htonl( ~(0xffffffffUL >> prefix_len)); *prefix = '\0'; #if HAVE_NEW_ADDRT rt->rt_genmask.sa_family = AF_INET; #endif } else { /* Default netmask. */ netmask = "default"; } /* Prefer hostname lookup is -host flag (xflag==1) was given. */ isnet = INET_resolve(target, (struct sockaddr_in *) &rt->rt_dst, (xflag & HOST_FLAG)); if (isnet < 0) { bb_error_msg_and_die("resolving %s", target); } if (prefix) { /* do not destroy prefix for process args */ *prefix = '/'; } } if (xflag) { /* Reinit isnet if -net or -host was specified. */ isnet = (xflag & NET_FLAG); } /* Fill in the other fields. */ rt->rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST)); while (*args) { int k = kw_lookup(tbl_ipvx, &args); const char *args_m1 = args[-1]; if (k & KW_IPVx_FLAG_ONLY) { rt->rt_flags |= flags_ipvx[k & 3]; continue; } #if HAVE_NEW_ADDRT if (k == KW_IPVx_METRIC) { rt->rt_metric = xatoul(args_m1) + 1; continue; } #endif if (k == KW_IPVx_NETMASK) { struct sockaddr mask; if (mask_in_addr(*rt)) { bb_show_usage(); } netmask = args_m1; isnet = INET_resolve(netmask, (struct sockaddr_in *) &mask, 0); if (isnet < 0) { bb_error_msg_and_die("resolving %s", netmask); } rt->rt_genmask = full_mask(mask); continue; } if (k == KW_IPVx_GATEWAY) { if (rt->rt_flags & RTF_GATEWAY) { bb_show_usage(); } isnet = INET_resolve(args_m1, (struct sockaddr_in *) &rt->rt_gateway, 1); rt->rt_flags |= RTF_GATEWAY; if (isnet) { if (isnet < 0) { bb_error_msg_and_die("resolving %s", args_m1); } bb_error_msg_and_die("gateway %s is a NETWORK", args_m1); } continue; } if (k == KW_IPVx_MSS) { /* Check valid MSS bounds. */ rt->rt_flags |= RTF_MSS; rt->rt_mss = xatoul_range(args_m1, 64, 32768); continue; } if (k == KW_IPVx_WINDOW) { /* Check valid window bounds. */ rt->rt_flags |= RTF_WINDOW; rt->rt_window = xatoul_range(args_m1, 128, INT_MAX); continue; } #ifdef RTF_IRTT if (k == KW_IPVx_IRTT) { rt->rt_flags |= RTF_IRTT; rt->rt_irtt = xatoul(args_m1); rt->rt_irtt *= (sysconf(_SC_CLK_TCK) / 100); /* FIXME */ #if 0 /* FIXME: do we need to check anything of this? */ if (rt->rt_irtt < 1 || rt->rt_irtt > (120 * HZ)) { bb_error_msg_and_die("bad irtt"); } #endif continue; } #endif /* Device is special in that it can be the last arg specified * and doesn't requre the dev/device keyword in that case. */ if (!rt->rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) { /* Don't use args_m1 here since args may have changed! */ rt->rt_dev = args[-1]; continue; } /* Nothing matched. */ bb_show_usage(); } #ifdef RTF_REJECT if ((rt->rt_flags & RTF_REJECT) && !rt->rt_dev) { rt->rt_dev = (char*)"lo"; } #endif /* sanity checks.. */ if (mask_in_addr(*rt)) { uint32_t mask = mask_in_addr(*rt); mask = ~ntohl(mask); if ((rt->rt_flags & RTF_HOST) && mask != 0xffffffff) { bb_error_msg_and_die("netmask %.8x and host route conflict", (unsigned int) mask); } if (mask & (mask + 1)) { bb_error_msg_and_die("bogus netmask %s", netmask); } mask = ((struct sockaddr_in *) &rt->rt_dst)->sin_addr.s_addr; if (mask & ~(uint32_t)mask_in_addr(*rt)) { bb_error_msg_and_die("netmask and route address conflict"); } } /* Fill out netmask if still unset */ if ((action == RTACTION_ADD) && (rt->rt_flags & RTF_HOST)) { mask_in_addr(*rt) = 0xffffffff; } /* Create a socket to the INET kernel. */ skfd = xsocket(AF_INET, SOCK_DGRAM, 0); if (action == RTACTION_ADD) xioctl(skfd, SIOCADDRT, rt); else xioctl(skfd, SIOCDELRT, rt); if (ENABLE_FEATURE_CLEAN_UP) close(skfd); } #if ENABLE_FEATURE_IPV6 static NOINLINE void INET6_setroute(int action, char **args) { struct sockaddr_in6 sa6; struct in6_rtmsg rt; int prefix_len, skfd; const char *devname; /* We know args isn't NULL from the check in route_main. */ const char *target = *args++; if (strcmp(target, "default") == 0) { prefix_len = 0; memset(&sa6, 0, sizeof(sa6)); } else { char *cp; cp = strchr(target, '/'); /* Yes... const to non is ok. */ if (cp) { *cp = '\0'; prefix_len = xatoul_range(cp + 1, 0, 128); } else { prefix_len = 128; } if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) { bb_error_msg_and_die("resolving %s", target); } } /* Clean out the RTREQ structure. */ memset(&rt, 0, sizeof(rt)); memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr)); /* Fill in the other fields. */ rt.rtmsg_dst_len = prefix_len; rt.rtmsg_flags = ((prefix_len == 128) ? (RTF_UP|RTF_HOST) : RTF_UP); rt.rtmsg_metric = 1; devname = NULL; while (*args) { int k = kw_lookup(tbl_ipvx, &args); const char *args_m1 = args[-1]; if ((k == KW_IPVx_MOD) || (k == KW_IPVx_DYN)) { rt.rtmsg_flags |= flags_ipvx[k & 3]; continue; } if (k == KW_IPVx_METRIC) { rt.rtmsg_metric = xatoul(args_m1); continue; } if (k == KW_IPVx_GATEWAY) { if (rt.rtmsg_flags & RTF_GATEWAY) { bb_show_usage(); } if (INET6_resolve(args_m1, (struct sockaddr_in6 *) &sa6) < 0) { bb_error_msg_and_die("resolving %s", args_m1); } memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr)); rt.rtmsg_flags |= RTF_GATEWAY; continue; } /* Device is special in that it can be the last arg specified * and doesn't requre the dev/device keyword in that case. */ if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) { /* Don't use args_m1 here since args may have changed! */ devname = args[-1]; continue; } /* Nothing matched. */ bb_show_usage(); } /* Create a socket to the INET6 kernel. */ skfd = xsocket(AF_INET6, SOCK_DGRAM, 0); rt.rtmsg_ifindex = 0; if (devname) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy_IFNAMSIZ(ifr.ifr_name, devname); xioctl(skfd, SIOCGIFINDEX, &ifr); rt.rtmsg_ifindex = ifr.ifr_ifindex; } /* Tell the kernel to accept this route. */ if (action == RTACTION_ADD) xioctl(skfd, SIOCADDRT, &rt); else xioctl(skfd, SIOCDELRT, &rt); if (ENABLE_FEATURE_CLEAN_UP) close(skfd); } #endif static const unsigned flagvals[] = { /* Must agree with flagchars[]. */ RTF_GATEWAY, RTF_HOST, RTF_REINSTATE, RTF_DYNAMIC, RTF_MODIFIED, #if ENABLE_FEATURE_IPV6 RTF_DEFAULT, RTF_ADDRCONF, RTF_CACHE #endif }; #define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED) #define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE) /* Must agree with flagvals[]. */ static const char flagchars[] ALIGN1 = "GHRDM" #if ENABLE_FEATURE_IPV6 "DAC" #endif ; static void set_flags(char *flagstr, int flags) { int i; *flagstr++ = 'U'; for (i = 0; (*flagstr = flagchars[i]) != 0; i++) { if (flags & flagvals[i]) { ++flagstr; } } } /* also used in netstat */ void FAST_FUNC bb_displayroutes(int noresolve, int netstatfmt) { char devname[64], flags[16], *sdest, *sgw; unsigned long d, g, m; int flgs, ref, use, metric, mtu, win, ir; struct sockaddr_in s_addr; struct in_addr mask; FILE *fp = xfopen_for_read("/proc/net/route"); printf("Kernel IP routing table\n" "Destination Gateway Genmask Flags %s Iface\n", netstatfmt ? " MSS Window irtt" : "Metric Ref Use"); if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */ goto ERROR; /* Empty or missing line, or read error. */ } while (1) { int r; r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n", devname, &d, &g, &flgs, &ref, &use, &metric, &m, &mtu, &win, &ir); if (r != 11) { if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */ break; } ERROR: bb_error_msg_and_die("fscanf"); } if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */ continue; } set_flags(flags, (flgs & IPV4_MASK)); #ifdef RTF_REJECT if (flgs & RTF_REJECT) { flags[0] = '!'; } #endif memset(&s_addr, 0, sizeof(struct sockaddr_in)); s_addr.sin_family = AF_INET; s_addr.sin_addr.s_addr = d; sdest = INET_rresolve(&s_addr, (noresolve | 0x8000), m); /* 'default' instead of '*' */ s_addr.sin_addr.s_addr = g; sgw = INET_rresolve(&s_addr, (noresolve | 0x4000), m); /* Host instead of net */ mask.s_addr = m; /* "%15.15s" truncates hostnames, do we really want that? */ printf("%-15.15s %-15.15s %-16s%-6s", sdest, sgw, inet_ntoa(mask), flags); free(sdest); free(sgw); if (netstatfmt) { printf("%5d %-5d %6d %s\n", mtu, win, ir, devname); } else { printf("%-6d %-2d %7d %s\n", metric, ref, use, devname); } } fclose(fp); } #if ENABLE_FEATURE_IPV6 static void INET6_displayroutes(void) { char addr6[128], *naddr6; /* In addr6x, we store both 40-byte ':'-delimited ipv6 addresses. * We read the non-delimited strings into the tail of the buffer * using fscanf and then modify the buffer by shifting forward * while inserting ':'s and the nul terminator for the first string. * Hence the strings are at addr6x and addr6x+40. This generates * _much_ less code than the previous (upstream) approach. */ char addr6x[80]; char iface[16], flags[16]; int iflags, metric, refcnt, use, prefix_len, slen; struct sockaddr_in6 snaddr6; FILE *fp = xfopen_for_read("/proc/net/ipv6_route"); printf("Kernel IPv6 routing table\n%-44s%-40s" "Flags Metric Ref Use Iface\n", "Destination", "Next Hop"); while (1) { int r; r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n", addr6x+14, &prefix_len, &slen, addr6x+40+7, &metric, &use, &refcnt, &iflags, iface); if (r != 9) { if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */ break; } ERROR: bb_error_msg_and_die("fscanf"); } /* Do the addr6x shift-and-insert changes to ':'-delimit addresses. * For now, always do this to validate the proc route format, even * if the interface is down. */ { int i = 0; char *p = addr6x+14; do { if (!*p) { if (i == 40) { /* nul terminator for 1st address? */ addr6x[39] = 0; /* Fixup... need 0 instead of ':'. */ ++p; /* Skip and continue. */ continue; } goto ERROR; } addr6x[i++] = *p++; if (!((i+1) % 5)) { addr6x[i++] = ':'; } } while (i < 40+28+7); } if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */ continue; } set_flags(flags, (iflags & IPV6_MASK)); r = 0; while (1) { inet_pton(AF_INET6, addr6x + r, (struct sockaddr *) &snaddr6.sin6_addr); snaddr6.sin6_family = AF_INET6; naddr6 = INET6_rresolve((struct sockaddr_in6 *) &snaddr6, 0x0fff /* Apparently, upstream never resolves. */ ); if (!r) { /* 1st pass */ snprintf(addr6, sizeof(addr6), "%s/%d", naddr6, prefix_len); r += 40; free(naddr6); } else { /* 2nd pass */ /* Print the info. */ printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n", addr6, naddr6, flags, metric, refcnt, use, iface); free(naddr6); break; } } } fclose(fp); } #endif #define ROUTE_OPT_A 0x01 #define ROUTE_OPT_n 0x02 #define ROUTE_OPT_e 0x04 #define ROUTE_OPT_INET6 0x08 /* Not an actual option. See below. */ /* 1st byte is offset to next entry offset. 2nd byte is return value. */ /* 2nd byte matches RTACTION_* code */ static const char tbl_verb[] ALIGN1 = "\006\001add\0" "\006\002del\0" /* "\011\002delete\0" */ "\010\002delete" /* Since it's last, we can save a byte. */ ; int route_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int route_main(int argc UNUSED_PARAM, char **argv) { unsigned opt; int what; char *family; char **p; /* First, remap '-net' and '-host' to avoid getopt problems. */ p = argv; while (*++p) { if (strcmp(*p, "-net") == 0 || strcmp(*p, "-host") == 0) { p[0][0] = '#'; } } opt = getopt32(argv, "A:ne", &family); if ((opt & ROUTE_OPT_A) && strcmp(family, "inet") != 0) { #if ENABLE_FEATURE_IPV6 if (strcmp(family, "inet6") == 0) { opt |= ROUTE_OPT_INET6; /* Set flag for ipv6. */ } else #endif bb_show_usage(); } argv += optind; /* No more args means display the routing table. */ if (!*argv) { int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0; #if ENABLE_FEATURE_IPV6 if (opt & ROUTE_OPT_INET6) INET6_displayroutes(); else #endif bb_displayroutes(noresolve, opt & ROUTE_OPT_e); fflush_stdout_and_exit(EXIT_SUCCESS); } /* Check verb. At the moment, must be add, del, or delete. */ what = kw_lookup(tbl_verb, &argv); if (!what || !*argv) { /* Unknown verb or no more args. */ bb_show_usage(); } #if ENABLE_FEATURE_IPV6 if (opt & ROUTE_OPT_INET6) INET6_setroute(what, argv); else #endif INET_setroute(what, argv); return EXIT_SUCCESS; } busybox-1.22.1/networking/httpd_post_upload.txt0000644000000000000000000000311512263563520020456 0ustar rootrootPOST upload example: post_upload.htm ===============
File to upload:
post_upload.cgi =============== #!/bin/sh # POST upload format: # -----------------------------29995809218093749221856446032^M # Content-Disposition: form-data; name="file1"; filename="..."^M # Content-Type: application/octet-stream^M # ^M <--------- headers end with empty line # file contents # file contents # file contents # ^M <--------- extra empty line # -----------------------------29995809218093749221856446032--^M file=/tmp/$$-$RANDOM CR=`printf '\r'` # CGI output must start with at least empty line (or headers) printf '\r\n' IFS="$CR" read -r delim_line IFS="" while read -r line; do test x"$line" = x"" && break test x"$line" = x"$CR" && break done cat >"$file" # We need to delete the tail of "\r\ndelim_line--\r\n" tail_len=$((${#delim_line} + 6)) # Get and check file size filesize=`stat -c"%s" "$file"` test "$filesize" -lt "$tail_len" && exit 1 # Check that tail is correct dd if="$file" skip=$((filesize - tail_len)) bs=1 count=1000 >"$file.tail" 2>/dev/null printf "\r\n%s--\r\n" "$delim_line" >"$file.tail.expected" if ! diff -q "$file.tail" "$file.tail.expected" >/dev/null; then printf "\n\nMalformed file upload" exit 1 fi rm "$file.tail" rm "$file.tail.expected" # Truncate the file dd of="$file" seek=$((filesize - tail_len)) bs=1 count=0 >/dev/null 2>/dev/null printf "\n\nFile upload has been accepted" busybox-1.22.1/networking/nc.c0000644000000000000000000001767012263563520014740 0ustar rootroot/* vi: set sw=4 ts=4: */ /* nc: mini-netcat - built from the ground up for LRP * * Copyright (C) 1998, 1999 Charles P. Wright * Copyright (C) 1998 Dave Cinege * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" //config:config NC //config: bool "nc" //config: default y //config: help //config: A simple Unix utility which reads and writes data across network //config: connections. //config: //config:config NC_SERVER //config: bool "Netcat server options (-l)" //config: default y //config: depends on NC //config: help //config: Allow netcat to act as a server. //config: //config:config NC_EXTRA //config: bool "Netcat extensions (-eiw and -f FILE)" //config: default y //config: depends on NC //config: help //config: Add -e (support for executing the rest of the command line after //config: making or receiving a successful connection), -i (delay interval for //config: lines sent), -w (timeout for initial connection). //config: //config:config NC_110_COMPAT //config: bool "Netcat 1.10 compatibility (+2.5k)" //config: default n # off specially for Rob //config: depends on NC //config: help //config: This option makes nc closely follow original nc-1.10. //config: The code is about 2.5k bigger. It enables //config: -s ADDR, -n, -u, -v, -o FILE, -z options, but loses //config: busybox-specific extensions: -f FILE. #if ENABLE_NC_110_COMPAT # include "nc_bloaty.c" #else //usage:#if !ENABLE_NC_110_COMPAT //usage: //usage:#if ENABLE_NC_SERVER || ENABLE_NC_EXTRA //usage:#define NC_OPTIONS_STR "\n" //usage:#else //usage:#define NC_OPTIONS_STR //usage:#endif //usage: //usage:#define nc_trivial_usage //usage: IF_NC_EXTRA("[-iN] [-wN] ")IF_NC_SERVER("[-l] [-p PORT] ") //usage: "["IF_NC_EXTRA("-f FILE|")"IPADDR PORT]"IF_NC_EXTRA(" [-e PROG]") //usage:#define nc_full_usage "\n\n" //usage: "Open a pipe to IP:PORT" IF_NC_EXTRA(" or FILE") //usage: NC_OPTIONS_STR //usage: IF_NC_SERVER( //usage: "\n -l Listen mode, for inbound connects" //usage: IF_NC_EXTRA( //usage: "\n (use -ll with -e for persistent server)" //usage: ) //usage: "\n -p PORT Local port" //usage: ) //usage: IF_NC_EXTRA( //usage: "\n -w SEC Connect timeout" //usage: "\n -i SEC Delay interval for lines sent" //usage: "\n -f FILE Use file (ala /dev/ttyS0) instead of network" //usage: "\n -e PROG Run PROG after connect" //usage: ) //usage: //usage:#define nc_notes_usage "" //usage: IF_NC_EXTRA( //usage: "To use netcat as a terminal emulator on a serial port:\n\n" //usage: "$ stty 115200 -F /dev/ttyS0\n" //usage: "$ stty raw -echo -ctlecho && nc -f /dev/ttyS0\n" //usage: ) //usage: //usage:#define nc_example_usage //usage: "$ nc foobar.somedomain.com 25\n" //usage: "220 foobar ESMTP Exim 3.12 #1 Sat, 15 Apr 2000 00:03:02 -0600\n" //usage: "help\n" //usage: "214-Commands supported:\n" //usage: "214- HELO EHLO MAIL RCPT DATA AUTH\n" //usage: "214 NOOP QUIT RSET HELP\n" //usage: "quit\n" //usage: "221 foobar closing connection\n" //usage: //usage:#endif /* Lots of small differences in features * when compared to "standard" nc */ static void timeout(int signum UNUSED_PARAM) { bb_error_msg_and_die("timed out"); } int nc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int nc_main(int argc, char **argv) { /* sfd sits _here_ only because of "repeat" option (-l -l). */ int sfd = sfd; /* for gcc */ int cfd = 0; unsigned lport = 0; IF_NOT_NC_SERVER(const) unsigned do_listen = 0; IF_NOT_NC_EXTRA (const) unsigned wsecs = 0; IF_NOT_NC_EXTRA (const) unsigned delay = 0; IF_NOT_NC_EXTRA (const int execparam = 0;) IF_NC_EXTRA (char **execparam = NULL;) fd_set readfds, testfds; int opt; /* must be signed (getopt returns -1) */ if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) { /* getopt32 is _almost_ usable: ** it cannot handle "... -e PROG -prog-opt" */ while ((opt = getopt(argc, argv, "" IF_NC_SERVER("lp:") IF_NC_EXTRA("w:i:f:e:") )) > 0 ) { if (ENABLE_NC_SERVER && opt == 'l') IF_NC_SERVER(do_listen++); else if (ENABLE_NC_SERVER && opt == 'p') IF_NC_SERVER(lport = bb_lookup_port(optarg, "tcp", 0)); else if (ENABLE_NC_EXTRA && opt == 'w') IF_NC_EXTRA( wsecs = xatou(optarg)); else if (ENABLE_NC_EXTRA && opt == 'i') IF_NC_EXTRA( delay = xatou(optarg)); else if (ENABLE_NC_EXTRA && opt == 'f') IF_NC_EXTRA( cfd = xopen(optarg, O_RDWR)); else if (ENABLE_NC_EXTRA && opt == 'e' && optind <= argc) { /* We cannot just 'break'. We should let getopt finish. ** Or else we won't be able to find where ** 'host' and 'port' params are ** (think "nc -w 60 host port -e PROG"). */ IF_NC_EXTRA( char **p; // +2: one for progname (optarg) and one for NULL execparam = xzalloc(sizeof(char*) * (argc - optind + 2)); p = execparam; *p++ = optarg; while (optind < argc) { *p++ = argv[optind++]; } ) /* optind points to argv[argc] (NULL) now. ** FIXME: we assume that getopt will not count options ** possibly present on "-e PROG ARGS" and will not ** include them into final value of optind ** which is to be used ... */ } else bb_show_usage(); } argv += optind; /* ... here! */ argc -= optind; // -l and -f don't mix if (do_listen && cfd) bb_show_usage(); // File mode needs need zero arguments, listen mode needs zero or one, // client mode needs one or two if (cfd) { if (argc) bb_show_usage(); } else if (do_listen) { if (argc > 1) bb_show_usage(); } else { if (!argc || argc > 2) bb_show_usage(); } } else { if (argc != 3) bb_show_usage(); argc--; argv++; } if (wsecs) { signal(SIGALRM, timeout); alarm(wsecs); } if (!cfd) { if (do_listen) { sfd = create_and_bind_stream_or_die(argv[0], lport); xlisten(sfd, do_listen); /* can be > 1 */ #if 0 /* nc-1.10 does not do this (without -v) */ /* If we didn't specify a port number, * query and print it after listen() */ if (!lport) { len_and_sockaddr lsa; lsa.len = LSA_SIZEOF_SA; getsockname(sfd, &lsa.u.sa, &lsa.len); lport = get_nport(&lsa.u.sa); fdprintf(2, "%d\n", ntohs(lport)); } #endif close_on_exec_on(sfd); accept_again: cfd = accept(sfd, NULL, 0); if (cfd < 0) bb_perror_msg_and_die("accept"); if (!execparam) close(sfd); } else { cfd = create_and_connect_stream_or_die(argv[0], argv[1] ? bb_lookup_port(argv[1], "tcp", 0) : 0); } } if (wsecs) { alarm(0); /* Non-ignored signals revert to SIG_DFL on exec anyway */ /*signal(SIGALRM, SIG_DFL);*/ } /* -e given? */ if (execparam) { pid_t pid; /* With more than one -l, repeatedly act as server */ if (do_listen > 1 && (pid = xvfork()) != 0) { /* parent */ /* prevent zombies */ signal(SIGCHLD, SIG_IGN); close(cfd); goto accept_again; } /* child, or main thread if only one -l */ xmove_fd(cfd, 0); xdup2(0, 1); /*xdup2(0, 2); - original nc 1.10 does this, we don't */ IF_NC_EXTRA(BB_EXECVP(execparam[0], execparam);) IF_NC_EXTRA(bb_perror_msg_and_die("can't execute '%s'", execparam[0]);) } /* Select loop copying stdin to cfd, and cfd to stdout */ FD_ZERO(&readfds); FD_SET(cfd, &readfds); FD_SET(STDIN_FILENO, &readfds); for (;;) { int fd; int ofd; int nread; testfds = readfds; if (select(cfd + 1, &testfds, NULL, NULL, NULL) < 0) bb_perror_msg_and_die("select"); #define iobuf bb_common_bufsiz1 fd = STDIN_FILENO; while (1) { if (FD_ISSET(fd, &testfds)) { nread = safe_read(fd, iobuf, sizeof(iobuf)); if (fd == cfd) { if (nread < 1) exit(EXIT_SUCCESS); ofd = STDOUT_FILENO; } else { if (nread < 1) { /* Close outgoing half-connection so they get EOF, * but leave incoming alone so we can see response */ shutdown(cfd, SHUT_WR); FD_CLR(STDIN_FILENO, &readfds); } ofd = cfd; } xwrite(ofd, iobuf, nread); if (delay > 0) sleep(delay); } if (fd == cfd) break; fd = cfd; } } } #endif busybox-1.22.1/networking/ifconfig.c0000644000000000000000000003722112263563520016116 0ustar rootroot/* vi: set sw=4 ts=4: */ /* ifconfig * * Similar to the standard Unix ifconfig, but with only the necessary * parts for AF_INET, and without any printing of if info (for now). * * Bjorn Wesen, Axis Communications AB * * * Authors of the original ifconfig was: * Fred N. van Kempen, * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* * Heavily modified by Manuel Novoa III Mar 6, 2001 * * From initial port to busybox, removed most of the redundancy by * converting to a table-driven approach. Added several (optional) * args missing from initial port. * * Still missing: media, tunnel. * * 2002-04-20 * IPV6 support added by Bart Visscher */ //usage:#define ifconfig_trivial_usage //usage: IF_FEATURE_IFCONFIG_STATUS("[-a]") " interface [address]" //usage:#define ifconfig_full_usage "\n\n" //usage: "Configure a network interface\n" //usage: "\n" //usage: IF_FEATURE_IPV6( //usage: " [add ADDRESS[/PREFIXLEN]]\n") //usage: IF_FEATURE_IPV6( //usage: " [del ADDRESS[/PREFIXLEN]]\n") //usage: " [[-]broadcast [ADDRESS]] [[-]pointopoint [ADDRESS]]\n" //usage: " [netmask ADDRESS] [dstaddr ADDRESS]\n" //usage: IF_FEATURE_IFCONFIG_SLIP( //usage: " [outfill NN] [keepalive NN]\n") //usage: " " IF_FEATURE_IFCONFIG_HW("[hw ether" IF_FEATURE_HWIB("|infiniband")" ADDRESS] ") "[metric NN] [mtu NN]\n" //usage: " [[-]trailers] [[-]arp] [[-]allmulti]\n" //usage: " [multicast] [[-]promisc] [txqueuelen NN] [[-]dynamic]\n" //usage: IF_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ( //usage: " [mem_start NN] [io_addr NN] [irq NN]\n") //usage: " [up|down] ..." #include "libbb.h" #include "inet_common.h" #include #include #include #ifdef HAVE_NET_ETHERNET_H # include #endif #if ENABLE_FEATURE_IFCONFIG_SLIP # include #endif /* I don't know if this is needed for busybox or not. Anyone? */ #define QUESTIONABLE_ALIAS_CASE /* Defines for glibc2.0 users. */ #ifndef SIOCSIFTXQLEN # define SIOCSIFTXQLEN 0x8943 # define SIOCGIFTXQLEN 0x8942 #endif /* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */ #ifndef ifr_qlen # define ifr_qlen ifr_ifru.ifru_mtu #endif #ifndef IFF_DYNAMIC # define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */ #endif #if ENABLE_FEATURE_IPV6 struct in6_ifreq { struct in6_addr ifr6_addr; uint32_t ifr6_prefixlen; int ifr6_ifindex; }; #endif /* * Here are the bit masks for the "flags" member of struct options below. * N_ signifies no arg prefix; M_ signifies arg prefixed by '-'. * CLR clears the flag; SET sets the flag; ARG signifies (optional) arg. */ #define N_CLR 0x01 #define M_CLR 0x02 #define N_SET 0x04 #define M_SET 0x08 #define N_ARG 0x10 #define M_ARG 0x20 #define M_MASK (M_CLR | M_SET | M_ARG) #define N_MASK (N_CLR | N_SET | N_ARG) #define SET_MASK (N_SET | M_SET) #define CLR_MASK (N_CLR | M_CLR) #define SET_CLR_MASK (SET_MASK | CLR_MASK) #define ARG_MASK (M_ARG | N_ARG) /* * Here are the bit masks for the "arg_flags" member of struct options below. */ /* * cast type: * 00 int * 01 char * * 02 HOST_COPY in_ether * 03 HOST_COPY INET_resolve */ #define A_CAST_TYPE 0x03 /* * map type: * 00 not a map type (mem_start, io_addr, irq) * 04 memstart (unsigned long) * 08 io_addr (unsigned short) * 0C irq (unsigned char) */ #define A_MAP_TYPE 0x0C #define A_ARG_REQ 0x10 /* Set if an arg is required. */ #define A_NETMASK 0x20 /* Set if netmask (check for multiple sets). */ #define A_SET_AFTER 0x40 /* Set a flag at the end. */ #define A_COLON_CHK 0x80 /* Is this needed? See below. */ #if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS #define A_HOSTNAME 0x100 /* Set if it is ip addr. */ #define A_BROADCAST 0x200 /* Set if it is broadcast addr. */ #else #define A_HOSTNAME 0 #define A_BROADCAST 0 #endif /* * These defines are for dealing with the A_CAST_TYPE field. */ #define A_CAST_CHAR_PTR 0x01 #define A_CAST_RESOLVE 0x01 #define A_CAST_HOST_COPY 0x02 #define A_CAST_HOST_COPY_IN_ETHER A_CAST_HOST_COPY #define A_CAST_HOST_COPY_RESOLVE (A_CAST_HOST_COPY | A_CAST_RESOLVE) /* * These defines are for dealing with the A_MAP_TYPE field. */ #define A_MAP_ULONG 0x04 /* memstart */ #define A_MAP_USHORT 0x08 /* io_addr */ #define A_MAP_UCHAR 0x0C /* irq */ /* * Define the bit masks signifying which operations to perform for each arg. */ #define ARG_METRIC (A_ARG_REQ /*| A_CAST_INT*/) #define ARG_MTU (A_ARG_REQ /*| A_CAST_INT*/) #define ARG_TXQUEUELEN (A_ARG_REQ /*| A_CAST_INT*/) #define ARG_MEM_START (A_ARG_REQ | A_MAP_ULONG) #define ARG_IO_ADDR (A_ARG_REQ | A_MAP_ULONG) #define ARG_IRQ (A_ARG_REQ | A_MAP_UCHAR) #define ARG_DSTADDR (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE) #define ARG_NETMASK (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_NETMASK) #define ARG_BROADCAST (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_BROADCAST) #define ARG_HW (A_ARG_REQ | A_CAST_HOST_COPY_IN_ETHER) #define ARG_POINTOPOINT (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER) #define ARG_KEEPALIVE (A_ARG_REQ | A_CAST_CHAR_PTR) #define ARG_OUTFILL (A_ARG_REQ | A_CAST_CHAR_PTR) #define ARG_HOSTNAME (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_COLON_CHK | A_HOSTNAME) #define ARG_ADD_DEL (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER) struct arg1opt { const char *name; unsigned short selector; unsigned short ifr_offset; }; struct options { const char *name; #if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS const unsigned int flags:6; const unsigned int arg_flags:10; #else const unsigned char flags; const unsigned char arg_flags; #endif const unsigned short selector; }; #define ifreq_offsetof(x) offsetof(struct ifreq, x) /* * Set up the tables. Warning! They must have corresponding order! */ static const struct arg1opt Arg1Opt[] = { { "SIFMETRIC", SIOCSIFMETRIC, ifreq_offsetof(ifr_metric) }, { "SIFMTU", SIOCSIFMTU, ifreq_offsetof(ifr_mtu) }, { "SIFTXQLEN", SIOCSIFTXQLEN, ifreq_offsetof(ifr_qlen) }, { "SIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr) }, { "SIFNETMASK", SIOCSIFNETMASK, ifreq_offsetof(ifr_netmask) }, { "SIFBRDADDR", SIOCSIFBRDADDR, ifreq_offsetof(ifr_broadaddr) }, #if ENABLE_FEATURE_IFCONFIG_HW { "SIFHWADDR", SIOCSIFHWADDR, ifreq_offsetof(ifr_hwaddr) }, #endif { "SIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr) }, #ifdef SIOCSKEEPALIVE { "SKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data) }, #endif #ifdef SIOCSOUTFILL { "SOUTFILL", SIOCSOUTFILL, ifreq_offsetof(ifr_data) }, #endif #if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ { "SIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.mem_start) }, { "SIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.base_addr) }, { "SIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.irq) }, #endif #if ENABLE_FEATURE_IPV6 { "SIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr) }, /* IPv6 version ignores the offset */ { "DIFADDR", SIOCDIFADDR, ifreq_offsetof(ifr_addr) }, /* IPv6 version ignores the offset */ #endif /* Last entry is for unmatched (assumed to be hostname/address) arg. */ { "SIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr) }, }; static const struct options OptArray[] = { { "metric", N_ARG, ARG_METRIC, 0 }, { "mtu", N_ARG, ARG_MTU, 0 }, { "txqueuelen", N_ARG, ARG_TXQUEUELEN, 0 }, { "dstaddr", N_ARG, ARG_DSTADDR, 0 }, { "netmask", N_ARG, ARG_NETMASK, 0 }, { "broadcast", N_ARG | M_CLR, ARG_BROADCAST, IFF_BROADCAST }, #if ENABLE_FEATURE_IFCONFIG_HW { "hw", N_ARG, ARG_HW, 0 }, #endif { "pointopoint", N_ARG | M_CLR, ARG_POINTOPOINT, IFF_POINTOPOINT }, #ifdef SIOCSKEEPALIVE { "keepalive", N_ARG, ARG_KEEPALIVE, 0 }, #endif #ifdef SIOCSOUTFILL { "outfill", N_ARG, ARG_OUTFILL, 0 }, #endif #if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ { "mem_start", N_ARG, ARG_MEM_START, 0 }, { "io_addr", N_ARG, ARG_IO_ADDR, 0 }, { "irq", N_ARG, ARG_IRQ, 0 }, #endif #if ENABLE_FEATURE_IPV6 { "add", N_ARG, ARG_ADD_DEL, 0 }, { "del", N_ARG, ARG_ADD_DEL, 0 }, #endif { "arp", N_CLR | M_SET, 0, IFF_NOARP }, { "trailers", N_CLR | M_SET, 0, IFF_NOTRAILERS }, { "promisc", N_SET | M_CLR, 0, IFF_PROMISC }, { "multicast", N_SET | M_CLR, 0, IFF_MULTICAST }, { "allmulti", N_SET | M_CLR, 0, IFF_ALLMULTI }, { "dynamic", N_SET | M_CLR, 0, IFF_DYNAMIC }, { "up", N_SET, 0, (IFF_UP | IFF_RUNNING) }, { "down", N_CLR, 0, IFF_UP }, { NULL, 0, ARG_HOSTNAME, (IFF_UP | IFF_RUNNING) } }; int ifconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ifconfig_main(int argc UNUSED_PARAM, char **argv) { struct ifreq ifr; struct sockaddr_in sai; #if ENABLE_FEATURE_IFCONFIG_HW struct sockaddr sa; #endif const struct arg1opt *a1op; const struct options *op; int sockfd; /* socket fd we use to manipulate stuff with */ int selector; #if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS unsigned int mask; unsigned int did_flags; unsigned int sai_hostname, sai_netmask; #else unsigned char mask; unsigned char did_flags; #endif char *p; /*char host[128];*/ const char *host = NULL; /* make gcc happy */ did_flags = 0; #if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS sai_hostname = 0; sai_netmask = 0; #endif /* skip argv[0] */ ++argv; #if ENABLE_FEATURE_IFCONFIG_STATUS if (argv[0] && (argv[0][0] == '-' && argv[0][1] == 'a' && !argv[0][2])) { interface_opt_a = 1; ++argv; } #endif if (!argv[0] || !argv[1]) { /* one or no args */ #if ENABLE_FEATURE_IFCONFIG_STATUS return display_interfaces(argv[0] /* can be NULL */); #else bb_error_msg_and_die("no support for status display"); #endif } /* Create a channel to the NET kernel. */ sockfd = xsocket(AF_INET, SOCK_DGRAM, 0); /* get interface name */ strncpy_IFNAMSIZ(ifr.ifr_name, *argv); /* Process the remaining arguments. */ while (*++argv != NULL) { p = *argv; mask = N_MASK; if (*p == '-') { /* If the arg starts with '-'... */ ++p; /* advance past it and */ mask = M_MASK; /* set the appropriate mask. */ } for (op = OptArray; op->name; op++) { /* Find table entry. */ if (strcmp(p, op->name) == 0) { /* If name matches... */ mask &= op->flags; if (mask) /* set the mask and go. */ goto FOUND_ARG; /* If we get here, there was a valid arg with an */ /* invalid '-' prefix. */ bb_error_msg_and_die("bad: '%s'", p-1); } } /* We fell through, so treat as possible hostname. */ a1op = Arg1Opt + ARRAY_SIZE(Arg1Opt) - 1; mask = op->arg_flags; goto HOSTNAME; FOUND_ARG: if (mask & ARG_MASK) { mask = op->arg_flags; if (mask & A_NETMASK & did_flags) bb_show_usage(); a1op = Arg1Opt + (op - OptArray); if (*++argv == NULL) { if (mask & A_ARG_REQ) bb_show_usage(); --argv; mask &= A_SET_AFTER; /* just for broadcast */ } else { /* got an arg so process it */ HOSTNAME: did_flags |= (mask & (A_NETMASK|A_HOSTNAME)); if (mask & A_CAST_HOST_COPY) { #if ENABLE_FEATURE_IFCONFIG_HW if (mask & A_CAST_RESOLVE) { #endif host = *argv; if (strcmp(host, "inet") == 0) continue; /* compat stuff */ sai.sin_family = AF_INET; sai.sin_port = 0; if (strcmp(host, "default") == 0) { /* Default is special, meaning 0.0.0.0. */ sai.sin_addr.s_addr = INADDR_ANY; } #if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS else if ((host[0] == '+' && !host[1]) && (mask & A_BROADCAST) && (did_flags & (A_NETMASK|A_HOSTNAME)) == (A_NETMASK|A_HOSTNAME) ) { /* + is special, meaning broadcast is derived. */ sai.sin_addr.s_addr = (~sai_netmask) | (sai_hostname & sai_netmask); } #endif else { len_and_sockaddr *lsa; #if ENABLE_FEATURE_IPV6 char *prefix; int prefix_len = 0; prefix = strchr(host, '/'); if (prefix) { prefix_len = xatou_range(prefix + 1, 0, 128); *prefix = '\0'; } resolve: #endif lsa = xhost2sockaddr(host, 0); #if ENABLE_FEATURE_IPV6 if (lsa->u.sa.sa_family != AF_INET6 && prefix) { /* TODO: we do not support "ifconfig eth0 up 1.2.3.4/17". * For now, just make it fail instead of silently ignoring "/17" part: */ *prefix = '/'; goto resolve; } if (lsa->u.sa.sa_family == AF_INET6) { int sockfd6; struct in6_ifreq ifr6; sockfd6 = xsocket(AF_INET6, SOCK_DGRAM, 0); xioctl(sockfd6, SIOCGIFINDEX, &ifr); ifr6.ifr6_ifindex = ifr.ifr_ifindex; ifr6.ifr6_prefixlen = prefix_len; memcpy(&ifr6.ifr6_addr, &lsa->u.sin6.sin6_addr, sizeof(struct in6_addr)); ioctl_or_perror_and_die(sockfd6, a1op->selector, &ifr6, "SIOC%s", a1op->name); if (ENABLE_FEATURE_CLEAN_UP) free(lsa); continue; } #endif sai.sin_addr = lsa->u.sin.sin_addr; if (ENABLE_FEATURE_CLEAN_UP) free(lsa); } #if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS if (mask & A_HOSTNAME) sai_hostname = sai.sin_addr.s_addr; if (mask & A_NETMASK) sai_netmask = sai.sin_addr.s_addr; #endif p = (char *) &sai; #if ENABLE_FEATURE_IFCONFIG_HW } else { /* A_CAST_HOST_COPY_IN_ETHER */ /* This is the "hw" arg case. */ smalluint hw_class = index_in_substrings("ether\0" IF_FEATURE_HWIB("infiniband\0"), *argv) + 1; if (!hw_class || !*++argv) bb_show_usage(); host = *argv; if (hw_class == 1 ? in_ether(host, &sa) : in_ib(host, &sa)) bb_error_msg_and_die("invalid hw-addr %s", host); p = (char *) &sa; } #endif memcpy( ((char *)&ifr) + a1op->ifr_offset, p, sizeof(struct sockaddr)); } else { /* FIXME: error check?? */ unsigned long i = strtoul(*argv, NULL, 0); p = ((char *)&ifr) + a1op->ifr_offset; #if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ if (mask & A_MAP_TYPE) { xioctl(sockfd, SIOCGIFMAP, &ifr); if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR) *(unsigned char *) p = i; else if (mask & A_MAP_USHORT) *(unsigned short *) p = i; else *(unsigned long *) p = i; } else #endif if (mask & A_CAST_CHAR_PTR) *(caddr_t *) p = (caddr_t) i; else /* A_CAST_INT */ *(int *) p = i; } ioctl_or_perror_and_die(sockfd, a1op->selector, &ifr, "SIOC%s", a1op->name); #ifdef QUESTIONABLE_ALIAS_CASE if (mask & A_COLON_CHK) { /* * Don't do the set_flag() if the address is an alias with * a '-' at the end, since it's deleted already! - Roman * * Should really use regex.h here, not sure though how well * it'll go with the cross-platform support etc. */ char *ptr; short int found_colon = 0; for (ptr = ifr.ifr_name; *ptr; ptr++) if (*ptr == ':') found_colon++; if (found_colon && ptr[-1] == '-') continue; } #endif } if (!(mask & A_SET_AFTER)) continue; mask = N_SET; } /* if (mask & ARG_MASK) */ xioctl(sockfd, SIOCGIFFLAGS, &ifr); selector = op->selector; if (mask & SET_MASK) ifr.ifr_flags |= selector; else ifr.ifr_flags &= ~selector; xioctl(sockfd, SIOCSIFFLAGS, &ifr); } /* while () */ if (ENABLE_FEATURE_CLEAN_UP) close(sockfd); return 0; } busybox-1.22.1/networking/nslookup.c0000644000000000000000000001357112263563520016206 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini nslookup implementation for busybox * * Copyright (C) 1999,2000 by Lineo, inc. and John Beppu * Copyright (C) 1999,2000,2001 by John Beppu * * Correct default name server display and explicit name server option * added by Ben Zeckel June 2001 * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define nslookup_trivial_usage //usage: "[HOST] [SERVER]" //usage:#define nslookup_full_usage "\n\n" //usage: "Query the nameserver for the IP address of the given HOST\n" //usage: "optionally using a specified DNS server" //usage: //usage:#define nslookup_example_usage //usage: "$ nslookup localhost\n" //usage: "Server: default\n" //usage: "Address: default\n" //usage: "\n" //usage: "Name: debian\n" //usage: "Address: 127.0.0.1\n" #include #include "libbb.h" /* * I'm only implementing non-interactive mode; * I totally forgot nslookup even had an interactive mode. * * This applet is the only user of res_init(). Without it, * you may avoid pulling in _res global from libc. */ /* Examples of 'standard' nslookup output * $ nslookup yahoo.com * Server: 128.193.0.10 * Address: 128.193.0.10#53 * * Non-authoritative answer: * Name: yahoo.com * Address: 216.109.112.135 * Name: yahoo.com * Address: 66.94.234.13 * * $ nslookup 204.152.191.37 * Server: 128.193.4.20 * Address: 128.193.4.20#53 * * Non-authoritative answer: * 37.191.152.204.in-addr.arpa canonical name = 37.32-27.191.152.204.in-addr.arpa. * 37.32-27.191.152.204.in-addr.arpa name = zeus-pub2.kernel.org. * * Authoritative answers can be found from: * 32-27.191.152.204.in-addr.arpa nameserver = ns1.kernel.org. * 32-27.191.152.204.in-addr.arpa nameserver = ns2.kernel.org. * 32-27.191.152.204.in-addr.arpa nameserver = ns3.kernel.org. * ns1.kernel.org internet address = 140.211.167.34 * ns2.kernel.org internet address = 204.152.191.4 * ns3.kernel.org internet address = 204.152.191.36 */ static int print_host(const char *hostname, const char *header) { /* We can't use xhost2sockaddr() - we want to get ALL addresses, * not just one */ struct addrinfo *result = NULL; int rc; struct addrinfo hint; memset(&hint, 0 , sizeof(hint)); /* hint.ai_family = AF_UNSPEC; - zero anyway */ /* Needed. Or else we will get each address thrice (or more) * for each possible socket type (tcp,udp,raw...): */ hint.ai_socktype = SOCK_STREAM; // hint.ai_flags = AI_CANONNAME; rc = getaddrinfo(hostname, NULL /*service*/, &hint, &result); if (rc == 0) { struct addrinfo *cur = result; unsigned cnt = 0; printf("%-10s %s\n", header, hostname); // puts(cur->ai_canonname); ? while (cur) { char *dotted, *revhost; dotted = xmalloc_sockaddr2dotted_noport(cur->ai_addr); revhost = xmalloc_sockaddr2hostonly_noport(cur->ai_addr); printf("Address %u: %s%c", ++cnt, dotted, revhost ? ' ' : '\n'); if (revhost) { puts(revhost); if (ENABLE_FEATURE_CLEAN_UP) free(revhost); } if (ENABLE_FEATURE_CLEAN_UP) free(dotted); cur = cur->ai_next; } } else { #if ENABLE_VERBOSE_RESOLUTION_ERRORS bb_error_msg("can't resolve '%s': %s", hostname, gai_strerror(rc)); #else bb_error_msg("can't resolve '%s'", hostname); #endif } if (ENABLE_FEATURE_CLEAN_UP && result) freeaddrinfo(result); return (rc != 0); } /* lookup the default nameserver and display it */ static void server_print(void) { char *server; struct sockaddr *sa; #if ENABLE_FEATURE_IPV6 sa = (struct sockaddr*)_res._u._ext.nsaddrs[0]; if (!sa) #endif sa = (struct sockaddr*)&_res.nsaddr_list[0]; server = xmalloc_sockaddr2dotted_noport(sa); print_host(server, "Server:"); if (ENABLE_FEATURE_CLEAN_UP) free(server); bb_putchar('\n'); } /* alter the global _res nameserver structure to use an explicit dns server instead of what is in /etc/resolv.conf */ static void set_default_dns(const char *server) { len_and_sockaddr *lsa; if (!server) return; /* NB: this works even with, say, "[::1]:5353"! :) */ lsa = xhost2sockaddr(server, 53); if (lsa->u.sa.sa_family == AF_INET) { _res.nscount = 1; /* struct copy */ _res.nsaddr_list[0] = lsa->u.sin; } #if ENABLE_FEATURE_IPV6 /* Hoped libc can cope with IPv4 address there too. * No such luck, glibc 2.4 segfaults even with IPv6, * maybe I misunderstand how to make glibc use IPv6 addr? * (uclibc 0.9.31+ should work) */ if (lsa->u.sa.sa_family == AF_INET6) { // glibc neither SEGVs nor sends any dgrams with this // (strace shows no socket ops): //_res.nscount = 0; _res._u._ext.nscount = 1; /* store a pointer to part of malloc'ed lsa */ _res._u._ext.nsaddrs[0] = &lsa->u.sin6; /* must not free(lsa)! */ } #endif } int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int nslookup_main(int argc, char **argv) { /* We allow 1 or 2 arguments. * The first is the name to be looked up and the second is an * optional DNS server with which to do the lookup. * More than 3 arguments is an error to follow the pattern of the * standard nslookup */ if (!argv[1] || argv[1][0] == '-' || argc > 3) bb_show_usage(); /* initialize DNS structure _res used in printing the default * name server and in the explicit name server option feature. */ res_init(); /* rfc2133 says this enables IPv6 lookups */ /* (but it also says "may be enabled in /etc/resolv.conf") */ /*_res.options |= RES_USE_INET6;*/ set_default_dns(argv[2]); server_print(); /* getaddrinfo and friends are free to request a resolver * reinitialization. Just in case, set_default_dns() again * after getaddrinfo (in server_print). This reportedly helps * with bug 675 "nslookup does not properly use second argument" * at least on Debian Wheezy and Openwrt AA (eglibc based). */ set_default_dns(argv[2]); return print_host(argv[1], "Name:"); } busybox-1.22.1/libpwdgrp/0000755000000000000000000000000012320365362013762 5ustar rootrootbusybox-1.22.1/libpwdgrp/Kbuild.src0000644000000000000000000000033612263563520015711 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y := uidgid_get.o lib-$(CONFIG_USE_BB_PWD_GRP) += pwd_grp.o busybox-1.22.1/libpwdgrp/pwd_grp_internal.c0000644000000000000000000000273212263563520017472 0ustar rootroot/* vi: set sw=4 ts=4: */ /* Copyright (C) 2003 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* Nov 6, 2003 Initial version. * * NOTE: This implementation is quite strict about requiring all * field seperators. It also does not allow leading whitespace * except when processing the numeric fields. glibc is more * lenient. See the various glibc difference comments below. * * TODO: * Move to dynamic allocation of (currently statically allocated) * buffers; especially for the group-related functions since * large group member lists will cause error returns. */ #ifndef GETXXKEY_R_FUNC #error GETXXKEY_R_FUNC is not defined! #endif int GETXXKEY_R_FUNC(GETXXKEY_R_KEYTYPE key, GETXXKEY_R_ENTTYPE *__restrict resultbuf, char *__restrict buffer, size_t buflen, GETXXKEY_R_ENTTYPE **__restrict result) { FILE *stream; int rv; *result = NULL; stream = fopen_for_read(GETXXKEY_R_PATHNAME); if (!stream) return errno; while (1) { rv = bb__pgsreader(GETXXKEY_R_PARSER, resultbuf, buffer, buflen, stream); if (!rv) { if (GETXXKEY_R_TEST(resultbuf)) { /* found key? */ *result = resultbuf; break; } } else { if (rv == ENOENT) { /* EOF encountered */ rv = 0; } break; } } fclose(stream); return rv; } #undef GETXXKEY_R_FUNC #undef GETXXKEY_R_PARSER #undef GETXXKEY_R_ENTTYPE #undef GETXXKEY_R_TEST #undef GETXXKEY_R_KEYTYPE #undef GETXXKEY_R_PATHNAME busybox-1.22.1/libpwdgrp/pwd_grp.c0000644000000000000000000005743112263563520015604 0ustar rootroot/* vi: set sw=4 ts=4: */ /* Copyright (C) 2003 Manuel Novoa III * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* Nov 6, 2003 Initial version. * * NOTE: This implementation is quite strict about requiring all * field seperators. It also does not allow leading whitespace * except when processing the numeric fields. glibc is more * lenient. See the various glibc difference comments below. * * TODO: * Move to dynamic allocation of (currently statically allocated) * buffers; especially for the group-related functions since * large group member lists will cause error returns. */ #include "libbb.h" #include /**********************************************************************/ /* Sizes for statically allocated buffers. */ #define PWD_BUFFER_SIZE 256 #define GRP_BUFFER_SIZE 256 /**********************************************************************/ /* Prototypes for internal functions. */ static int bb__pgsreader( int FAST_FUNC (*parserfunc)(void *d, char *line), void *data, char *__restrict line_buff, size_t buflen, FILE *f); static int FAST_FUNC bb__parsepwent(void *pw, char *line); static int FAST_FUNC bb__parsegrent(void *gr, char *line); #if ENABLE_USE_BB_SHADOW static int FAST_FUNC bb__parsespent(void *sp, char *line); #endif /**********************************************************************/ /* We avoid having big global data. */ struct statics { /* Smaller things first */ /* It's ok to use one buffer for getpwuid and getpwnam. Manpage says: * "The return value may point to a static area, and may be overwritten * by subsequent calls to getpwent(), getpwnam(), or getpwuid()." */ struct passwd getpw_resultbuf; struct group getgr_resultbuf; char getpw_buffer[PWD_BUFFER_SIZE]; char getgr_buffer[GRP_BUFFER_SIZE]; #if 0 //ENABLE_USE_BB_SHADOW struct spwd getsp_resultbuf; char getsp_buffer[PWD_BUFFER_SIZE]; #endif // Not converted - too small to bother //pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER; //FILE *pwf /*= NULL*/; //FILE *grf /*= NULL*/; //FILE *spf /*= NULL*/; }; static struct statics *ptr_to_statics; static struct statics *get_S(void) { if (!ptr_to_statics) ptr_to_statics = xzalloc(sizeof(*ptr_to_statics)); return ptr_to_statics; } /* Always use in this order, get_S() must be called first */ #define RESULTBUF(name) &((S = get_S())->name##_resultbuf) #define BUFFER(name) (S->name##_buffer) /**********************************************************************/ /* For the various fget??ent_r funcs, return * * 0: success * ENOENT: end-of-file encountered * ERANGE: buflen too small * other error values possible. See bb__pgsreader. * * Also, *result == resultbuf on success and NULL on failure. * * NOTE: glibc difference - For the ENOENT case, glibc also sets errno. * We do not, as it really isn't an error if we reach the end-of-file. * Doing so is analogous to having fgetc() set errno on EOF. */ /**********************************************************************/ int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf, char *__restrict buffer, size_t buflen, struct passwd **__restrict result) { int rv; *result = NULL; rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, stream); if (!rv) { *result = resultbuf; } return rv; } int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf, char *__restrict buffer, size_t buflen, struct group **__restrict result) { int rv; *result = NULL; rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, stream); if (!rv) { *result = resultbuf; } return rv; } #if ENABLE_USE_BB_SHADOW #ifdef UNUSED_FOR_NOW int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf, char *__restrict buffer, size_t buflen, struct spwd **__restrict result) { int rv; *result = NULL; rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, stream); if (!rv) { *result = resultbuf; } return rv; } #endif #endif /**********************************************************************/ /* For the various fget??ent funcs, return NULL on failure and a * pointer to the appropriate struct (statically allocated) on success. * TODO: audit & stop using these in bbox, they pull in static buffers */ /**********************************************************************/ #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS struct passwd *fgetpwent(FILE *stream) { struct statics *S; struct passwd *resultbuf = RESULTBUF(getpw); char *buffer = BUFFER(getpw); struct passwd *result; fgetpwent_r(stream, resultbuf, buffer, sizeof(BUFFER(getpw)), &result); return result; } struct group *fgetgrent(FILE *stream) { struct statics *S; struct group *resultbuf = RESULTBUF(getgr); char *buffer = BUFFER(getgr); struct group *result; fgetgrent_r(stream, resultbuf, buffer, sizeof(BUFFER(getgr)), &result); return result; } #endif #if ENABLE_USE_BB_SHADOW #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS struct spwd *fgetspent(FILE *stream) { struct statics *S; struct spwd *resultbuf = RESULTBUF(getsp); char *buffer = BUFFER(getsp); struct spwd *result; fgetspent_r(stream, resultbuf, buffer, sizeof(BUFFER(getsp)), &result); return result; } #endif #ifdef UNUSED_FOR_NOW int sgetspent_r(const char *string, struct spwd *result_buf, char *buffer, size_t buflen, struct spwd **result) { int rv = ERANGE; *result = NULL; if (buflen < PWD_BUFFER_SIZE) { DO_ERANGE: errno = rv; goto DONE; } if (string != buffer) { if (strlen(string) >= buflen) { goto DO_ERANGE; } strcpy(buffer, string); } rv = bb__parsespent(result_buf, buffer); if (!rv) { *result = result_buf; } DONE: return rv; } #endif #endif /* ENABLE_USE_BB_SHADOW */ /**********************************************************************/ #define GETXXKEY_R_FUNC getpwnam_r #define GETXXKEY_R_PARSER bb__parsepwent #define GETXXKEY_R_ENTTYPE struct passwd #define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->pw_name, key)) #define GETXXKEY_R_KEYTYPE const char *__restrict #define GETXXKEY_R_PATHNAME _PATH_PASSWD #include "pwd_grp_internal.c" #define GETXXKEY_R_FUNC getgrnam_r #define GETXXKEY_R_PARSER bb__parsegrent #define GETXXKEY_R_ENTTYPE struct group #define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->gr_name, key)) #define GETXXKEY_R_KEYTYPE const char *__restrict #define GETXXKEY_R_PATHNAME _PATH_GROUP #include "pwd_grp_internal.c" #if ENABLE_USE_BB_SHADOW #define GETXXKEY_R_FUNC getspnam_r #define GETXXKEY_R_PARSER bb__parsespent #define GETXXKEY_R_ENTTYPE struct spwd #define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->sp_namp, key)) #define GETXXKEY_R_KEYTYPE const char *__restrict #define GETXXKEY_R_PATHNAME _PATH_SHADOW #include "pwd_grp_internal.c" #endif #define GETXXKEY_R_FUNC getpwuid_r #define GETXXKEY_R_PARSER bb__parsepwent #define GETXXKEY_R_ENTTYPE struct passwd #define GETXXKEY_R_TEST(ENT) ((ENT)->pw_uid == key) #define GETXXKEY_R_KEYTYPE uid_t #define GETXXKEY_R_PATHNAME _PATH_PASSWD #include "pwd_grp_internal.c" #define GETXXKEY_R_FUNC getgrgid_r #define GETXXKEY_R_PARSER bb__parsegrent #define GETXXKEY_R_ENTTYPE struct group #define GETXXKEY_R_TEST(ENT) ((ENT)->gr_gid == key) #define GETXXKEY_R_KEYTYPE gid_t #define GETXXKEY_R_PATHNAME _PATH_GROUP #include "pwd_grp_internal.c" /**********************************************************************/ /* TODO: audit & stop using these in bbox, they pull in static buffers */ /* This one has many users */ struct passwd *getpwuid(uid_t uid) { struct statics *S; struct passwd *resultbuf = RESULTBUF(getpw); char *buffer = BUFFER(getpw); struct passwd *result; getpwuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getpw)), &result); return result; } /* This one has many users */ struct group *getgrgid(gid_t gid) { struct statics *S; struct group *resultbuf = RESULTBUF(getgr); char *buffer = BUFFER(getgr); struct group *result; getgrgid_r(gid, resultbuf, buffer, sizeof(BUFFER(getgr)), &result); return result; } #if 0 //ENABLE_USE_BB_SHADOW /* This function is non-standard and is currently not built. It seems * to have been created as a reentrant version of the non-standard * functions getspuid. Why getspuid was added, I do not know. */ int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf, char *__restrict buffer, size_t buflen, struct spwd **__restrict result) { int rv; struct passwd *pp; struct passwd password; char pwd_buff[PWD_BUFFER_SIZE]; *result = NULL; rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp); if (!rv) { rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result); } return rv; } /* This function is non-standard and is currently not built. * Why it was added, I do not know. */ struct spwd *getspuid(uid_t uid) { struct statics *S; struct spwd *resultbuf = RESULTBUF(getsp); char *buffer = BUFFER(getsp); struct spwd *result; getspuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getsp)), &result); return result; } #endif /* This one has many users */ struct passwd *getpwnam(const char *name) { struct statics *S; struct passwd *resultbuf = RESULTBUF(getpw); char *buffer = BUFFER(getpw); struct passwd *result; getpwnam_r(name, resultbuf, buffer, sizeof(BUFFER(getpw)), &result); return result; } /* This one has many users */ struct group *getgrnam(const char *name) { struct statics *S; struct group *resultbuf = RESULTBUF(getgr); char *buffer = BUFFER(getgr); struct group *result; getgrnam_r(name, resultbuf, buffer, sizeof(BUFFER(getgr)), &result); return result; } #if 0 //ENABLE_USE_BB_SHADOW struct spwd *getspnam(const char *name) { struct statics *S; struct spwd *resultbuf = RESULTBUF(getsp); char *buffer = BUFFER(getsp); struct spwd *result; getspnam_r(name, resultbuf, buffer, sizeof(BUFFER(getsp)), &result); return result; } #endif /**********************************************************************/ /* FIXME: we don't have such CONFIG_xx - ?! */ #if defined CONFIG_USE_BB_THREADSAFE_SHADOW && defined PTHREAD_MUTEX_INITIALIZER static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER; # define LOCK pthread_mutex_lock(&mylock) # define UNLOCK pthread_mutex_unlock(&mylock); #else # define LOCK ((void) 0) # define UNLOCK ((void) 0) #endif static FILE *pwf /*= NULL*/; void setpwent(void) { LOCK; if (pwf) { rewind(pwf); } UNLOCK; } void endpwent(void) { LOCK; if (pwf) { fclose(pwf); pwf = NULL; } UNLOCK; } int getpwent_r(struct passwd *__restrict resultbuf, char *__restrict buffer, size_t buflen, struct passwd **__restrict result) { int rv; LOCK; *result = NULL; /* In case of error... */ if (!pwf) { pwf = fopen_for_read(_PATH_PASSWD); if (!pwf) { rv = errno; goto ERR; } close_on_exec_on(fileno(pwf)); } rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, pwf); if (!rv) { *result = resultbuf; } ERR: UNLOCK; return rv; } static FILE *grf /*= NULL*/; void setgrent(void) { LOCK; if (grf) { rewind(grf); } UNLOCK; } void endgrent(void) { LOCK; if (grf) { fclose(grf); grf = NULL; } UNLOCK; } int getgrent_r(struct group *__restrict resultbuf, char *__restrict buffer, size_t buflen, struct group **__restrict result) { int rv; LOCK; *result = NULL; /* In case of error... */ if (!grf) { grf = fopen_for_read(_PATH_GROUP); if (!grf) { rv = errno; goto ERR; } close_on_exec_on(fileno(grf)); } rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, grf); if (!rv) { *result = resultbuf; } ERR: UNLOCK; return rv; } #ifdef UNUSED_FOR_NOW #if ENABLE_USE_BB_SHADOW static FILE *spf /*= NULL*/; void setspent(void) { LOCK; if (spf) { rewind(spf); } UNLOCK; } void endspent(void) { LOCK; if (spf) { fclose(spf); spf = NULL; } UNLOCK; } int getspent_r(struct spwd *resultbuf, char *buffer, size_t buflen, struct spwd **result) { int rv; LOCK; *result = NULL; /* In case of error... */ if (!spf) { spf = fopen_for_read(_PATH_SHADOW); if (!spf) { rv = errno; goto ERR; } close_on_exec_on(fileno(spf)); } rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, spf); if (!rv) { *result = resultbuf; } ERR: UNLOCK; return rv; } #endif #endif /* UNUSED_FOR_NOW */ #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS struct passwd *getpwent(void) { static char line_buff[PWD_BUFFER_SIZE]; static struct passwd pwd; struct passwd *result; getpwent_r(&pwd, line_buff, sizeof(line_buff), &result); return result; } struct group *getgrent(void) { static char line_buff[GRP_BUFFER_SIZE]; static struct group gr; struct group *result; getgrent_r(&gr, line_buff, sizeof(line_buff), &result); return result; } #if ENABLE_USE_BB_SHADOW struct spwd *getspent(void) { static char line_buff[PWD_BUFFER_SIZE]; static struct spwd spwd; struct spwd *result; getspent_r(&spwd, line_buff, sizeof(line_buff), &result); return result; } struct spwd *sgetspent(const char *string) { static char line_buff[PWD_BUFFER_SIZE]; static struct spwd spwd; struct spwd *result; sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result); return result; } #endif #endif /* UNUSED_SINCE_WE_AVOID_STATIC_BUFS */ static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gid) { FILE *grfile; gid_t *group_list; int ngroups; struct group group; char buff[PWD_BUFFER_SIZE]; /* We alloc space for 8 gids at a time. */ group_list = xmalloc(8 * sizeof(group_list[0])); group_list[0] = gid; ngroups = 1; grfile = fopen_for_read(_PATH_GROUP); if (grfile) { while (!bb__pgsreader(bb__parsegrent, &group, buff, sizeof(buff), grfile)) { char **m; assert(group.gr_mem); /* Must have at least a NULL terminator. */ if (group.gr_gid == gid) continue; for (m = group.gr_mem; *m; m++) { if (strcmp(*m, user) != 0) continue; group_list = xrealloc_vector(group_list, /*8=2^3:*/ 3, ngroups); group_list[ngroups++] = group.gr_gid; break; } } fclose(grfile); } *ngroups_ptr = ngroups; return group_list; } int initgroups(const char *user, gid_t gid) { int ngroups; gid_t *group_list = getgrouplist_internal(&ngroups, user, gid); ngroups = setgroups(ngroups, group_list); free(group_list); return ngroups; } int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups) { int ngroups_old = *ngroups; gid_t *group_list = getgrouplist_internal(ngroups, user, gid); if (*ngroups <= ngroups_old) { ngroups_old = *ngroups; memcpy(groups, group_list, ngroups_old * sizeof(groups[0])); } else { ngroups_old = -1; } free(group_list); return ngroups_old; } #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS int putpwent(const struct passwd *__restrict p, FILE *__restrict f) { int rv = -1; #if 0 /* glibc does this check */ if (!p || !f) { errno = EINVAL; return rv; } #endif /* No extra thread locking is needed above what fprintf does. */ if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n", p->pw_name, p->pw_passwd, (unsigned long)(p->pw_uid), (unsigned long)(p->pw_gid), p->pw_gecos, p->pw_dir, p->pw_shell) >= 0 ) { rv = 0; } return rv; } int putgrent(const struct group *__restrict p, FILE *__restrict f) { int rv = -1; #if 0 /* glibc does this check */ if (!p || !f) { errno = EINVAL; return rv; } #endif if (fprintf(f, "%s:%s:%lu:", p->gr_name, p->gr_passwd, (unsigned long)(p->gr_gid)) >= 0 ) { static const char format[] ALIGN1 = ",%s"; char **m; const char *fmt; fmt = format + 1; assert(p->gr_mem); m = p->gr_mem; while (1) { if (!*m) { if (fputc('\n', f) >= 0) { rv = 0; } break; } if (fprintf(f, fmt, *m) < 0) { break; } m++; fmt = format; } } return rv; } #endif #if ENABLE_USE_BB_SHADOW #ifdef UNUSED_FOR_NOW static const unsigned char put_sp_off[] ALIGN1 = { offsetof(struct spwd, sp_lstchg), /* 2 - not a char ptr */ offsetof(struct spwd, sp_min), /* 3 - not a char ptr */ offsetof(struct spwd, sp_max), /* 4 - not a char ptr */ offsetof(struct spwd, sp_warn), /* 5 - not a char ptr */ offsetof(struct spwd, sp_inact), /* 6 - not a char ptr */ offsetof(struct spwd, sp_expire) /* 7 - not a char ptr */ }; int putspent(const struct spwd *p, FILE *stream) { const char *fmt; long x; int i; int rv = -1; /* Unlike putpwent and putgrent, glibc does not check the args. */ if (fprintf(stream, "%s:%s:", p->sp_namp, (p->sp_pwdp ? p->sp_pwdp : "")) < 0 ) { goto DO_UNLOCK; } for (i = 0; i < sizeof(put_sp_off); i++) { fmt = "%ld:"; x = *(long *)((char *)p + put_sp_off[i]); if (x == -1) { fmt += 3; } if (fprintf(stream, fmt, x) < 0) { goto DO_UNLOCK; } } if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) { goto DO_UNLOCK; } if (fputc('\n', stream) > 0) { rv = 0; } DO_UNLOCK: return rv; } #endif #endif /* USE_BB_SHADOW */ /**********************************************************************/ /* Internal functions */ /**********************************************************************/ static const unsigned char pw_off[] ALIGN1 = { offsetof(struct passwd, pw_name), /* 0 */ offsetof(struct passwd, pw_passwd), /* 1 */ offsetof(struct passwd, pw_uid), /* 2 - not a char ptr */ offsetof(struct passwd, pw_gid), /* 3 - not a char ptr */ offsetof(struct passwd, pw_gecos), /* 4 */ offsetof(struct passwd, pw_dir), /* 5 */ offsetof(struct passwd, pw_shell) /* 6 */ }; static int FAST_FUNC bb__parsepwent(void *data, char *line) { char *endptr; char *p; int i; i = 0; while (1) { p = (char *) data + pw_off[i]; if (i < 2 || i > 3) { *((char **) p) = line; if (i == 6) { return 0; } /* NOTE: glibc difference - glibc allows omission of * ':' seperators after the gid field if all remaining * entries are empty. We require all separators. */ line = strchr(line, ':'); if (!line) { break; } } else { unsigned long t = strtoul(line, &endptr, 10); /* Make sure we had at least one digit, and that the * failing char is the next field seperator ':'. See * glibc difference note above. */ /* TODO: Also check for leading whitespace? */ if ((endptr == line) || (*endptr != ':')) { break; } line = endptr; if (i & 1) { /* i == 3 -- gid */ *((gid_t *) p) = t; } else { /* i == 2 -- uid */ *((uid_t *) p) = t; } } *line++ = '\0'; i++; } /* while (1) */ return -1; } /**********************************************************************/ static const unsigned char gr_off[] ALIGN1 = { offsetof(struct group, gr_name), /* 0 */ offsetof(struct group, gr_passwd), /* 1 */ offsetof(struct group, gr_gid) /* 2 - not a char ptr */ }; static int FAST_FUNC bb__parsegrent(void *data, char *line) { char *endptr; char *p; int i; char **members; char *end_of_buf; end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */ i = 0; while (1) { p = (char *) data + gr_off[i]; if (i < 2) { *((char **) p) = line; line = strchr(line, ':'); if (!line) { break; } *line++ = '\0'; i++; } else { *((gid_t *) p) = strtoul(line, &endptr, 10); /* NOTE: glibc difference - glibc allows omission of the * trailing colon when there is no member list. We treat * this as an error. */ /* Make sure we had at least one digit, and that the * failing char is the next field seperator ':'. See * glibc difference note above. */ if ((endptr == line) || (*endptr != ':')) { break; } i = 1; /* Count terminating NULL ptr. */ p = endptr; if (p[1]) { /* We have a member list to process. */ /* Overwrite the last ':' with a ',' before counting. * This allows us to (1) test for initial ',' * and (2) adds one ',' so that the number of commas * equals the member count. */ *p = ','; do { /* NOTE: glibc difference - glibc allows and trims leading * (but not trailing) space. We treat this as an error. */ /* NOTE: glibc difference - glibc allows consecutive and * trailing commas, and ignores "empty string" users. We * treat this as an error. */ if (*p == ',') { ++i; *p = 0; /* nul-terminate each member string. */ if (!*++p || (*p == ',') || isspace(*p)) { goto ERR; } } } while (*++p); } /* Now align (p+1), rounding up. */ /* Assumes sizeof(char **) is a power of 2. */ members = (char **)( (((intptr_t) p) + sizeof(char **)) & ~((intptr_t)(sizeof(char **) - 1)) ); if (((char *)(members + i)) > end_of_buf) { /* No space. */ break; } ((struct group *) data)->gr_mem = members; if (--i) { p = endptr; /* Pointing to char prior to first member. */ while (1) { *members++ = ++p; if (!--i) break; while (*++p) continue; } } *members = NULL; return 0; } } /* while (1) */ ERR: return -1; } /**********************************************************************/ #if ENABLE_USE_BB_SHADOW static const unsigned char sp_off[] ALIGN1 = { offsetof(struct spwd, sp_namp), /* 0: char* */ offsetof(struct spwd, sp_pwdp), /* 1: char* */ offsetof(struct spwd, sp_lstchg), /* 2: long */ offsetof(struct spwd, sp_min), /* 3: long */ offsetof(struct spwd, sp_max), /* 4: long */ offsetof(struct spwd, sp_warn), /* 5: long */ offsetof(struct spwd, sp_inact), /* 6: long */ offsetof(struct spwd, sp_expire), /* 7: long */ offsetof(struct spwd, sp_flag) /* 8: unsigned long */ }; static int FAST_FUNC bb__parsespent(void *data, char *line) { char *endptr; char *p; int i; i = 0; while (1) { p = (char *) data + sp_off[i]; if (i < 2) { *((char **) p) = line; line = strchr(line, ':'); if (!line) { break; /* error */ } } else { *((long *) p) = strtoul(line, &endptr, 10); if (endptr == line) { *((long *) p) = -1L; } line = endptr; if (i == 8) { if (*line != '\0') { break; /* error */ } return 0; /* all ok */ } if (*line != ':') { break; /* error */ } } *line++ = '\0'; i++; } return EINVAL; } #endif /**********************************************************************/ /* Reads until EOF, or until it finds a line which fits in the buffer * and for which the parser function succeeds. * * Returns 0 on success and ENOENT for end-of-file (glibc convention). */ static int bb__pgsreader( int FAST_FUNC (*parserfunc)(void *d, char *line), void *data, char *__restrict line_buff, size_t buflen, FILE *f) { int skip; int rv = ERANGE; if (buflen < PWD_BUFFER_SIZE) { errno = rv; return rv; } skip = 0; while (1) { if (!fgets(line_buff, buflen, f)) { if (feof(f)) { rv = ENOENT; } break; } { int line_len = strlen(line_buff) - 1; if (line_len >= 0 && line_buff[line_len] == '\n') { line_buff[line_len] = '\0'; } else if (line_len + 2 == buflen) { /* A start (or continuation) of overlong line */ skip = 1; continue; } /* else: a last line in the file, and it has no '\n' */ } if (skip) { /* This "line" is a remainder of overlong line, ignore */ skip = 0; continue; } /* NOTE: glibc difference - glibc strips leading whitespace from * records. We do not allow leading whitespace. */ /* Skip empty lines, comment lines, and lines with leading * whitespace. */ if (line_buff[0] != '\0' && line_buff[0] != '#' && !isspace(line_buff[0])) { if (parserfunc == bb__parsegrent) { /* Do evil group hack: * The group entry parsing function needs to know where * the end of the buffer is so that it can construct the * group member ptr table. */ ((struct group *) data)->gr_name = line_buff + buflen; } if (parserfunc(data, line_buff) == 0) { rv = 0; break; } } } /* while (1) */ return rv; } busybox-1.22.1/libpwdgrp/uidgid_get.c0000644000000000000000000000752512263563520016245 0ustar rootroot/* Copyright (c) 2001-2006, Gerrit Pape 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. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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" /* Always sets uid and gid */ int FAST_FUNC get_uidgid(struct bb_uidgid_t *u, const char *ug, int numeric_ok) { struct passwd *pwd; struct group *gr; char *user, *group; unsigned n; user = (char*)ug; group = strchr(ug, ':'); if (group) { int sz = (++group) - ug; user = alloca(sz); /* copies sz-1 bytes, stores terminating '\0' */ safe_strncpy(user, ug, sz); } if (numeric_ok) { n = bb_strtou(user, NULL, 10); if (!errno) { u->uid = n; pwd = getpwuid(n); /* If we have e.g. "500" string without user */ /* with uid 500 in /etc/passwd, we set gid == uid */ u->gid = pwd ? pwd->pw_gid : n; goto skip; } } /* Either it is not numeric, or caller disallows numeric username */ pwd = getpwnam(user); if (!pwd) return 0; u->uid = pwd->pw_uid; u->gid = pwd->pw_gid; skip: if (group) { if (numeric_ok) { n = bb_strtou(group, NULL, 10); if (!errno) { u->gid = n; return 1; } } gr = getgrnam(group); if (!gr) return 0; u->gid = gr->gr_gid; } return 1; } void FAST_FUNC xget_uidgid(struct bb_uidgid_t *u, const char *ug) { if (!get_uidgid(u, ug, 1)) bb_error_msg_and_die("unknown user/group %s", ug); } /* chown-like: * "user" sets uid only, * ":group" sets gid only * "user:" sets uid and gid (to user's primary group id) * "user:group" sets uid and gid * ('unset' uid or gid retains the value it has on entry) */ void FAST_FUNC parse_chown_usergroup_or_die(struct bb_uidgid_t *u, char *user_group) { char *group; /* Check if there is a group name */ group = strchr(user_group, '.'); /* deprecated? */ if (!group) group = strchr(user_group, ':'); else *group = ':'; /* replace '.' with ':' */ /* Parse "user[:[group]]" */ if (!group) { /* "user" */ u->uid = get_ug_id(user_group, xuname2uid); } else if (group == user_group) { /* ":group" */ u->gid = get_ug_id(group + 1, xgroup2gid); } else { if (!group[1]) /* "user:" */ *group = '\0'; xget_uidgid(u, user_group); } } #if 0 #include int main() { unsigned u; struct bb_uidgid_t ug; u = get_uidgid(&ug, "apache", 0); printf("%u = %u:%u\n", u, ug.uid, ug.gid); ug.uid = ug.gid = 1111; u = get_uidgid(&ug, "apache", 0); printf("%u = %u:%u\n", u, ug.uid, ug.gid); ug.uid = ug.gid = 1111; u = get_uidgid(&ug, "apache:users", 0); printf("%u = %u:%u\n", u, ug.uid, ug.gid); ug.uid = ug.gid = 1111; u = get_uidgid(&ug, "apache:users", 0); printf("%u = %u:%u\n", u, ug.uid, ug.gid); return 0; } #endif busybox-1.22.1/findutils/0000755000000000000000000000000012320365362013771 5ustar rootrootbusybox-1.22.1/findutils/xargs.c0000644000000000000000000003476012263563520015275 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini xargs implementation for busybox * * (C) 2002,2003 by Vladimir Oleynik * * Special thanks * - Mark Whitley and Glenn McGrath for stimulus to rewrite :) * - Mike Rendell * and David MacKenzie . * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * xargs is described in the Single Unix Specification v3 at * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html */ //config:config XARGS //config: bool "xargs" //config: default y //config: help //config: xargs is used to execute a specified command for //config: every item from standard input. //config: //config:config FEATURE_XARGS_SUPPORT_CONFIRMATION //config: bool "Enable -p: prompt and confirmation" //config: default y //config: depends on XARGS //config: help //config: Support -p: prompt the user whether to run each command //config: line and read a line from the terminal. //config: //config:config FEATURE_XARGS_SUPPORT_QUOTES //config: bool "Enable single and double quotes and backslash" //config: default y //config: depends on XARGS //config: help //config: Support quoting in the input. //config: //config:config FEATURE_XARGS_SUPPORT_TERMOPT //config: bool "Enable -x: exit if -s or -n is exceeded" //config: default y //config: depends on XARGS //config: help //config: Support -x: exit if the command size (see the -s or -n option) //config: is exceeded. //config: //config:config FEATURE_XARGS_SUPPORT_ZERO_TERM //config: bool "Enable -0: NUL-terminated input" //config: default y //config: depends on XARGS //config: help //config: Support -0: input items are terminated by a NUL character //config: instead of whitespace, and the quotes and backslash //config: are not special. //applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, BB_DIR_USR_BIN, BB_SUID_DROP, xargs)) //kbuild:lib-$(CONFIG_XARGS) += xargs.o #include "libbb.h" /* This is a NOEXEC applet. Be very careful! */ //#define dbg_msg(...) bb_error_msg(__VA_ARGS__) #define dbg_msg(...) ((void)0) #ifdef TEST # ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION # define ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 1 # endif # ifndef ENABLE_FEATURE_XARGS_SUPPORT_QUOTES # define ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 1 # endif # ifndef ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT # define ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT 1 # endif # ifndef ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM # define ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 1 # endif #endif struct globals { char **args; const char *eof_str; int idx; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { \ G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \ } while (0) /* * This function has special algorithm. * Don't use fork and include to main! */ static int xargs_exec(void) { int status; status = spawn_and_wait(G.args); if (status < 0) { bb_simple_perror_msg(G.args[0]); return errno == ENOENT ? 127 : 126; } if (status == 255) { bb_error_msg("%s: exited with status 255; aborting", G.args[0]); return 124; } if (status >= 0x180) { bb_error_msg("%s: terminated by signal %d", G.args[0], status - 0x180); return 125; } if (status) return 123; return 0; } /* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space. * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13. */ #define ISSPACE(a) ({ unsigned char xargs__isspace = (a) - 9; xargs__isspace == (' ' - 9) || xargs__isspace <= (13 - 9); }) static void store_param(char *s) { /* Grow by 256 elements at once */ if (!(G.idx & 0xff)) { /* G.idx == N*256 */ /* Enlarge, make G.args[(N+1)*256 - 1] last valid idx */ G.args = xrealloc(G.args, sizeof(G.args[0]) * (G.idx + 0x100)); } G.args[G.idx++] = s; } /* process[0]_stdin: * Read characters into buf[n_max_chars+1], and when parameter delimiter * is seen, store the address of a new parameter to args[]. * If reading discovers that last chars do not form the complete * parameter, the pointer to the first such "tail character" is returned. * (buf has extra byte at the end to accomodate terminating NUL * of "tail characters" string). * Otherwise, the returned pointer points to NUL byte. * On entry, buf[] may contain some "seed chars" which are to become * the beginning of the first parameter. */ #if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf) { #define NORM 0 #define QUOTE 1 #define BACKSLASH 2 #define SPACE 4 char q = '\0'; /* quote char */ char state = NORM; char *s = buf; /* start of the word */ char *p = s + strlen(buf); /* end of the word */ buf += n_max_chars; /* past buffer's end */ /* "goto ret" is used instead of "break" to make control flow * more obvious: */ while (1) { int c = getchar(); if (c == EOF) { if (p != s) goto close_word; goto ret; } if (state == BACKSLASH) { state = NORM; goto set; } if (state == QUOTE) { if (c != q) goto set; q = '\0'; state = NORM; } else { /* if (state == NORM) */ if (ISSPACE(c)) { if (p != s) { close_word: state = SPACE; c = '\0'; goto set; } } else { if (c == '\\') { state = BACKSLASH; } else if (c == '\'' || c == '"') { q = c; state = QUOTE; } else { set: *p++ = c; } } } if (state == SPACE) { /* word's delimiter or EOF detected */ if (q) { bb_error_msg_and_die("unmatched %s quote", q == '\'' ? "single" : "double"); } /* A full word is loaded */ if (G.eof_str) { if (strcmp(s, G.eof_str) == 0) { while (getchar() != EOF) continue; p = s; goto ret; } } store_param(s); dbg_msg("args[]:'%s'", s); s = p; n_max_arg--; if (n_max_arg == 0) { goto ret; } state = NORM; } if (p == buf) { goto ret; } } ret: *p = '\0'; /* store_param(NULL) - caller will do it */ dbg_msg("return:'%s'", s); return s; } #else /* The variant does not support single quotes, double quotes or backslash */ static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf) { char *s = buf; /* start of the word */ char *p = s + strlen(buf); /* end of the word */ buf += n_max_chars; /* past buffer's end */ while (1) { int c = getchar(); if (c == EOF) { if (p == s) goto ret; } if (c == EOF || ISSPACE(c)) { if (p == s) continue; c = EOF; } *p++ = (c == EOF ? '\0' : c); if (c == EOF) { /* word's delimiter or EOF detected */ /* A full word is loaded */ if (G.eof_str) { if (strcmp(s, G.eof_str) == 0) { while (getchar() != EOF) continue; p = s; goto ret; } } store_param(s); dbg_msg("args[]:'%s'", s); s = p; n_max_arg--; if (n_max_arg == 0) { goto ret; } } if (p == buf) { goto ret; } } ret: *p = '\0'; /* store_param(NULL) - caller will do it */ dbg_msg("return:'%s'", s); return s; } #endif /* FEATURE_XARGS_SUPPORT_QUOTES */ #if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf) { char *s = buf; /* start of the word */ char *p = s + strlen(buf); /* end of the word */ buf += n_max_chars; /* past buffer's end */ while (1) { int c = getchar(); if (c == EOF) { if (p == s) goto ret; c = '\0'; } *p++ = c; if (c == '\0') { /* word's delimiter or EOF detected */ /* A full word is loaded */ store_param(s); dbg_msg("args[]:'%s'", s); s = p; n_max_arg--; if (n_max_arg == 0) { goto ret; } } if (p == buf) { goto ret; } } ret: *p = '\0'; /* store_param(NULL) - caller will do it */ dbg_msg("return:'%s'", s); return s; } #endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */ #if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION /* Prompt the user for a response, and if the user responds affirmatively, return true; otherwise, return false. Uses "/dev/tty", not stdin. */ static int xargs_ask_confirmation(void) { FILE *tty_stream; int c, savec; tty_stream = xfopen_for_read(CURRENT_TTY); fputs(" ?...", stderr); fflush_all(); c = savec = getc(tty_stream); while (c != EOF && c != '\n') c = getc(tty_stream); fclose(tty_stream); return (savec == 'y' || savec == 'Y'); } #else # define xargs_ask_confirmation() 1 #endif //usage:#define xargs_trivial_usage //usage: "[OPTIONS] [PROG ARGS]" //usage:#define xargs_full_usage "\n\n" //usage: "Run PROG on every item given by stdin\n" //usage: IF_FEATURE_XARGS_SUPPORT_CONFIRMATION( //usage: "\n -p Ask user whether to run each command" //usage: ) //usage: "\n -r Don't run command if input is empty" //usage: IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( //usage: "\n -0 Input is separated by NUL characters" //usage: ) //usage: "\n -t Print the command on stderr before execution" //usage: "\n -e[STR] STR stops input processing" //usage: "\n -n N Pass no more than N args to PROG" //usage: "\n -s N Pass command line of no more than N bytes" //usage: IF_FEATURE_XARGS_SUPPORT_TERMOPT( //usage: "\n -x Exit if size is exceeded" //usage: ) //usage:#define xargs_example_usage //usage: "$ ls | xargs gzip\n" //usage: "$ find . -name '*.c' -print | xargs rm\n" /* Correct regardless of combination of CONFIG_xxx */ enum { OPTBIT_VERBOSE = 0, OPTBIT_NO_EMPTY, OPTBIT_UPTO_NUMBER, OPTBIT_UPTO_SIZE, OPTBIT_EOF_STRING, OPTBIT_EOF_STRING1, IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,) IF_FEATURE_XARGS_SUPPORT_TERMOPT( OPTBIT_TERMINATE ,) IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( OPTBIT_ZEROTERM ,) OPT_VERBOSE = 1 << OPTBIT_VERBOSE , OPT_NO_EMPTY = 1 << OPTBIT_NO_EMPTY , OPT_UPTO_NUMBER = 1 << OPTBIT_UPTO_NUMBER, OPT_UPTO_SIZE = 1 << OPTBIT_UPTO_SIZE , OPT_EOF_STRING = 1 << OPTBIT_EOF_STRING , /* GNU: -e[] */ OPT_EOF_STRING1 = 1 << OPTBIT_EOF_STRING1, /* SUS: -E */ OPT_INTERACTIVE = IF_FEATURE_XARGS_SUPPORT_CONFIRMATION((1 << OPTBIT_INTERACTIVE)) + 0, OPT_TERMINATE = IF_FEATURE_XARGS_SUPPORT_TERMOPT( (1 << OPTBIT_TERMINATE )) + 0, OPT_ZEROTERM = IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( (1 << OPTBIT_ZEROTERM )) + 0, }; #define OPTION_STR "+trn:s:e::E:" \ IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \ IF_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \ IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int xargs_main(int argc, char **argv) { int i; int child_error = 0; char *max_args; char *max_chars; char *buf; unsigned opt; int n_max_chars; int n_max_arg; #if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin; #else #define read_args process_stdin #endif INIT_G(); #if ENABLE_DESKTOP && ENABLE_LONG_OPTS /* For example, Fedora's build system uses --no-run-if-empty */ applet_long_options = "no-run-if-empty\0" No_argument "r" ; #endif opt = getopt32(argv, OPTION_STR, &max_args, &max_chars, &G.eof_str, &G.eof_str); /* -E ""? You may wonder why not just omit -E? * This is used for portability: * old xargs was using "_" as default for -E / -e */ if ((opt & OPT_EOF_STRING1) && G.eof_str[0] == '\0') G.eof_str = NULL; if (opt & OPT_ZEROTERM) IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin); argv += optind; argc -= optind; if (!argv[0]) { /* default behavior is to echo all the filenames */ *--argv = (char*)"echo"; argc++; } /* -s NUM default. fileutils-4.4.2 uses 128k, but I heasitate * to use such a big value - first need to change code to use * growable buffer instead of fixed one. */ n_max_chars = 32 * 1024; /* Make smaller if system does not allow our default value. * The Open Group Base Specifications Issue 6: * "The xargs utility shall limit the command line length such that * when the command line is invoked, the combined argument * and environment lists (see the exec family of functions * in the System Interfaces volume of IEEE Std 1003.1-2001) * shall not exceed {ARG_MAX}-2048 bytes". */ { long arg_max = 0; #if defined _SC_ARG_MAX arg_max = sysconf(_SC_ARG_MAX) - 2048; #elif defined ARG_MAX arg_max = ARG_MAX - 2048; #endif if (arg_max > 0 && n_max_chars > arg_max) n_max_chars = arg_max; } if (opt & OPT_UPTO_SIZE) { n_max_chars = xatou_range(max_chars, 1, INT_MAX); } /* Account for prepended fixed arguments */ { size_t n_chars = 0; for (i = 0; argv[i]; i++) { n_chars += strlen(argv[i]) + 1; } n_max_chars -= n_chars; } /* Sanity check */ if (n_max_chars <= 0) { bb_error_msg_and_die("can't fit single argument within argument list size limit"); } buf = xzalloc(n_max_chars + 1); n_max_arg = n_max_chars; if (opt & OPT_UPTO_NUMBER) { n_max_arg = xatou_range(max_args, 1, INT_MAX); /* Not necessary, we use growable args[]: */ /* if (n_max_arg > n_max_chars) n_max_arg = n_max_chars */ } /* Allocate pointers for execvp */ /* We can statically allocate (argc + n_max_arg + 1) elements * and do not bother with resizing args[], but on 64-bit machines * this results in args[] vector which is ~8 times bigger * than n_max_chars! That is, with n_max_chars == 20k, * args[] will take 160k (!), which will most likely be * almost entirely unused. */ /* See store_param() for matching 256-step growth logic */ G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff)); /* Store the command to be executed, part 1 */ for (i = 0; argv[i]; i++) G.args[i] = argv[i]; while (1) { char *rem; G.idx = argc; rem = read_args(n_max_chars, n_max_arg, buf); store_param(NULL); if (!G.args[argc]) { if (*rem != '\0') bb_error_msg_and_die("argument line too long"); if (opt & OPT_NO_EMPTY) break; } opt |= OPT_NO_EMPTY; if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) { const char *fmt = " %s" + 1; char **args = G.args; for (i = 0; args[i]; i++) { fprintf(stderr, fmt, args[i]); fmt = " %s"; } if (!(opt & OPT_INTERACTIVE)) bb_putchar_stderr('\n'); } if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) { child_error = xargs_exec(); } if (child_error > 0 && child_error != 123) { break; } overlapping_strcpy(buf, rem); } /* while */ if (ENABLE_FEATURE_CLEAN_UP) { free(G.args); free(buf); } return child_error; } #ifdef TEST const char *applet_name = "debug stuff usage"; void bb_show_usage(void) { fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n", applet_name); exit(EXIT_FAILURE); } int main(int argc, char **argv) { return xargs_main(argc, argv); } #endif /* TEST */ busybox-1.22.1/findutils/Config.src0000644000000000000000000000022712263563520015712 0ustar rootroot# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Finding Utilities" INSERT endmenu busybox-1.22.1/findutils/find.c0000644000000000000000000011533312267106071015063 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini find implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen * * Reworked by David Douthitt and * Matt Kraai . * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* findutils-4.1.20: * * # find file.txt -exec 'echo {}' '{} {}' ';' * find: echo file.txt: No such file or directory * # find file.txt -exec 'echo' '{} {}' '; ' * find: missing argument to `-exec' * # find file.txt -exec 'echo {}' '{} {}' ';' junk * find: paths must precede expression * # find file.txt -exec 'echo {}' '{} {}' ';' junk ';' * find: paths must precede expression * # find file.txt -exec 'echo' '{} {}' ';' * file.txt file.txt * (strace: execve("/bin/echo", ["echo", "file.txt file.txt"], [ 30 vars ])) * # find file.txt -exec 'echo' '{} {}' ';' -print -exec pwd ';' * file.txt file.txt * file.txt * /tmp * # find -name '*.c' -o -name '*.h' * [shows files, *.c and *.h intermixed] * # find file.txt -name '*f*' -o -name '*t*' * file.txt * # find file.txt -name '*z*' -o -name '*t*' * file.txt * # find file.txt -name '*f*' -o -name '*z*' * file.txt * * # find t z -name '*t*' -print -o -name '*z*' * t * # find t z t z -name '*t*' -o -name '*z*' -print * z * z * # find t z t z '(' -name '*t*' -o -name '*z*' ')' -o -print * (no output) */ /* Testing script * ./busybox find "$@" | tee /tmp/bb_find * echo ================== * /path/to/gnu/find "$@" | tee /tmp/std_find * echo ================== * diff -u /tmp/std_find /tmp/bb_find && echo Identical */ //config:config FIND //config: bool "find" //config: default y //config: help //config: find is used to search your system to find specified files. //config: //config:config FEATURE_FIND_PRINT0 //config: bool "Enable -print0: NUL-terminated output" //config: default y //config: depends on FIND //config: help //config: Causes output names to be separated by a NUL character //config: rather than a newline. This allows names that contain //config: newlines and other whitespace to be more easily //config: interpreted by other programs. //config: //config:config FEATURE_FIND_MTIME //config: bool "Enable -mtime: modified time matching" //config: default y //config: depends on FIND //config: help //config: Allow searching based on the modification time of //config: files, in days. //config: //config:config FEATURE_FIND_MMIN //config: bool "Enable -mmin: modified time matching by minutes" //config: default y //config: depends on FIND //config: help //config: Allow searching based on the modification time of //config: files, in minutes. //config: //config:config FEATURE_FIND_PERM //config: bool "Enable -perm: permissions matching" //config: default y //config: depends on FIND //config: help //config: Enable searching based on file permissions. //config: //config:config FEATURE_FIND_TYPE //config: bool "Enable -type: file type matching (file/dir/link/...)" //config: default y //config: depends on FIND //config: help //config: Enable searching based on file type (file, //config: directory, socket, device, etc.). //config: //config:config FEATURE_FIND_XDEV //config: bool "Enable -xdev: 'stay in filesystem'" //config: default y //config: depends on FIND //config: help //config: This option allows find to restrict searches to a single filesystem. //config: //config:config FEATURE_FIND_MAXDEPTH //config: bool "Enable -mindepth N and -maxdepth N" //config: default y //config: depends on FIND //config: help //config: This option enables -mindepth N and -maxdepth N option. //config: //config:config FEATURE_FIND_NEWER //config: bool "Enable -newer: compare file modification times" //config: default y //config: depends on FIND //config: help //config: Support the 'find -newer' option for finding any files which have //config: modification time that is more recent than the specified FILE. //config: //config:config FEATURE_FIND_INUM //config: bool "Enable -inum: inode number matching" //config: default y //config: depends on FIND //config: help //config: Support the 'find -inum' option for searching by inode number. //config: //config:config FEATURE_FIND_EXEC //config: bool "Enable -exec: execute commands" //config: default y //config: depends on FIND //config: help //config: Support the 'find -exec' option for executing commands based upon //config: the files matched. //config: //config:config FEATURE_FIND_USER //config: bool "Enable -user: username/uid matching" //config: default y //config: depends on FIND //config: help //config: Support the 'find -user' option for searching by username or uid. //config: //config:config FEATURE_FIND_GROUP //config: bool "Enable -group: group/gid matching" //config: default y //config: depends on FIND //config: help //config: Support the 'find -group' option for searching by group name or gid. //config: //config:config FEATURE_FIND_NOT //config: bool "Enable the 'not' (!) operator" //config: default y //config: depends on FIND //config: help //config: Support the '!' operator to invert the test results. //config: If 'Enable full-blown desktop' is enabled, then will also support //config: the non-POSIX notation '-not'. //config: //config:config FEATURE_FIND_DEPTH //config: bool "Enable -depth" //config: default y //config: depends on FIND //config: help //config: Process each directory's contents before the directory itself. //config: //config:config FEATURE_FIND_PAREN //config: bool "Enable parens in options" //config: default y //config: depends on FIND //config: help //config: Enable usage of parens '(' to specify logical order of arguments. //config: //config:config FEATURE_FIND_SIZE //config: bool "Enable -size: file size matching" //config: default y //config: depends on FIND //config: help //config: Support the 'find -size' option for searching by file size. //config: //config:config FEATURE_FIND_PRUNE //config: bool "Enable -prune: exclude subdirectories" //config: default y //config: depends on FIND //config: help //config: If the file is a directory, dont descend into it. Useful for //config: exclusion .svn and CVS directories. //config: //config:config FEATURE_FIND_DELETE //config: bool "Enable -delete: delete files/dirs" //config: default y //config: depends on FIND && FEATURE_FIND_DEPTH //config: help //config: Support the 'find -delete' option for deleting files and directories. //config: WARNING: This option can do much harm if used wrong. Busybox will not //config: try to protect the user from doing stupid things. Use with care. //config: //config:config FEATURE_FIND_PATH //config: bool "Enable -path: match pathname with shell pattern" //config: default y //config: depends on FIND //config: help //config: The -path option matches whole pathname instead of just filename. //config: //config:config FEATURE_FIND_REGEX //config: bool "Enable -regex: match pathname with regex" //config: default y //config: depends on FIND //config: help //config: The -regex option matches whole pathname against regular expression. //config: //config:config FEATURE_FIND_CONTEXT //config: bool "Enable -context: security context matching" //config: default n //config: depends on FIND && SELINUX //config: help //config: Support the 'find -context' option for matching security context. //config: //config:config FEATURE_FIND_LINKS //config: bool "Enable -links: link count matching" //config: default y //config: depends on FIND //config: help //config: Support the 'find -links' option for matching number of links. //applet:IF_FIND(APPLET_NOEXEC(find, find, BB_DIR_USR_BIN, BB_SUID_DROP, find)) //kbuild:lib-$(CONFIG_FIND) += find.o //usage:#define find_trivial_usage //usage: "[-HL] [PATH]... [OPTIONS] [ACTIONS]" //usage:#define find_full_usage "\n\n" //usage: "Search for files and perform actions on them.\n" //usage: "First failed action stops processing of current file.\n" //usage: "Defaults: PATH is current directory, action is '-print'\n" //usage: "\n -L,-follow Follow symlinks" //usage: "\n -H ...on command line only" //usage: IF_FEATURE_FIND_XDEV( //usage: "\n -xdev Don't descend directories on other filesystems" //usage: ) //usage: IF_FEATURE_FIND_MAXDEPTH( //usage: "\n -maxdepth N Descend at most N levels. -maxdepth 0 applies" //usage: "\n actions to command line arguments only" //usage: "\n -mindepth N Don't act on first N levels" //usage: ) //usage: IF_FEATURE_FIND_DEPTH( //usage: "\n -depth Act on directory *after* traversing it" //usage: ) //usage: "\n" //usage: "\nActions:" //usage: IF_FEATURE_FIND_PAREN( //usage: "\n ( ACTIONS ) Group actions for -o / -a" //usage: ) //usage: IF_FEATURE_FIND_NOT( //usage: "\n ! ACT Invert ACT's success/failure" //usage: ) //usage: "\n ACT1 [-a] ACT2 If ACT1 fails, stop, else do ACT2" //usage: "\n ACT1 -o ACT2 If ACT1 succeeds, stop, else do ACT2" //usage: "\n Note: -a has higher priority than -o" //usage: "\n -name PATTERN Match file name (w/o directory name) to PATTERN" //usage: "\n -iname PATTERN Case insensitive -name" //usage: IF_FEATURE_FIND_PATH( //usage: "\n -path PATTERN Match path to PATTERN" //usage: "\n -ipath PATTERN Case insensitive -path" //usage: ) //usage: IF_FEATURE_FIND_REGEX( //usage: "\n -regex PATTERN Match path to regex PATTERN" //usage: ) //usage: IF_FEATURE_FIND_TYPE( //usage: "\n -type X File type is X (one of: f,d,l,b,c,...)" //usage: ) //usage: IF_FEATURE_FIND_PERM( //usage: "\n -perm MASK At least one mask bit (+MASK), all bits (-MASK)," //usage: "\n or exactly MASK bits are set in file's mode" //usage: ) //usage: IF_FEATURE_FIND_MTIME( //usage: "\n -mtime DAYS mtime is greater than (+N), less than (-N)," //usage: "\n or exactly N days in the past" //usage: ) //usage: IF_FEATURE_FIND_MMIN( //usage: "\n -mmin MINS mtime is greater than (+N), less than (-N)," //usage: "\n or exactly N minutes in the past" //usage: ) //usage: IF_FEATURE_FIND_NEWER( //usage: "\n -newer FILE mtime is more recent than FILE's" //usage: ) //usage: IF_FEATURE_FIND_INUM( //usage: "\n -inum N File has inode number N" //usage: ) //usage: IF_FEATURE_FIND_USER( //usage: "\n -user NAME/ID File is owned by given user" //usage: ) //usage: IF_FEATURE_FIND_GROUP( //usage: "\n -group NAME/ID File is owned by given group" //usage: ) //usage: IF_FEATURE_FIND_SIZE( //usage: "\n -size N[bck] File size is N (c:bytes,k:kbytes,b:512 bytes(def.))" //usage: "\n +/-N: file size is bigger/smaller than N" //usage: ) //usage: IF_FEATURE_FIND_LINKS( //usage: "\n -links N Number of links is greater than (+N), less than (-N)," //usage: "\n or exactly N" //usage: ) //usage: IF_FEATURE_FIND_CONTEXT( //usage: "\n -context CTX File has specified security context" //usage: ) //usage: IF_FEATURE_FIND_PRUNE( //usage: "\n -prune If current file is directory, don't descend into it" //usage: ) //usage: "\nIf none of the following actions is specified, -print is assumed" //usage: "\n -print Print file name" //usage: IF_FEATURE_FIND_PRINT0( //usage: "\n -print0 Print file name, NUL terminated" //usage: ) //usage: IF_FEATURE_FIND_EXEC( //usage: "\n -exec CMD ARG ; Run CMD with all instances of {} replaced by" //usage: "\n file name. Fails if CMD exits with nonzero" //usage: ) //usage: IF_FEATURE_FIND_DELETE( //usage: "\n -delete Delete current file/directory. Turns on -depth option" //usage: ) //usage: //usage:#define find_example_usage //usage: "$ find / -name passwd\n" //usage: "/etc/passwd\n" #include #include "libbb.h" #if ENABLE_FEATURE_FIND_REGEX # include "xregex.h" #endif /* GNUism: */ #ifndef FNM_CASEFOLD # define FNM_CASEFOLD 0 #endif #define dbg(...) ((void)0) /* #define dbg(...) bb_error_msg(__VA_ARGS__) */ /* This is a NOEXEC applet. Be very careful! */ typedef int (*action_fp)(const char *fileName, const struct stat *statbuf, void *) FAST_FUNC; typedef struct { action_fp f; #if ENABLE_FEATURE_FIND_NOT bool invert; #endif } action; #define ACTS(name, ...) typedef struct { action a; __VA_ARGS__ } action_##name; #define ACTF(name) \ static int FAST_FUNC func_##name(const char *fileName UNUSED_PARAM, \ const struct stat *statbuf UNUSED_PARAM, \ action_##name* ap UNUSED_PARAM) ACTS(print) ACTS(name, const char *pattern; bool iname;) IF_FEATURE_FIND_PATH( ACTS(path, const char *pattern; bool ipath;)) IF_FEATURE_FIND_REGEX( ACTS(regex, regex_t compiled_pattern;)) IF_FEATURE_FIND_PRINT0( ACTS(print0)) IF_FEATURE_FIND_TYPE( ACTS(type, int type_mask;)) IF_FEATURE_FIND_PERM( ACTS(perm, char perm_char; mode_t perm_mask;)) IF_FEATURE_FIND_MTIME( ACTS(mtime, char mtime_char; unsigned mtime_days;)) IF_FEATURE_FIND_MMIN( ACTS(mmin, char mmin_char; unsigned mmin_mins;)) IF_FEATURE_FIND_NEWER( ACTS(newer, time_t newer_mtime;)) IF_FEATURE_FIND_INUM( ACTS(inum, ino_t inode_num;)) IF_FEATURE_FIND_USER( ACTS(user, uid_t uid;)) IF_FEATURE_FIND_SIZE( ACTS(size, char size_char; off_t size;)) IF_FEATURE_FIND_CONTEXT(ACTS(context, security_context_t context;)) IF_FEATURE_FIND_PAREN( ACTS(paren, action ***subexpr;)) IF_FEATURE_FIND_PRUNE( ACTS(prune)) IF_FEATURE_FIND_DELETE( ACTS(delete)) IF_FEATURE_FIND_EXEC( ACTS(exec, char **exec_argv; unsigned *subst_count; int exec_argc;)) IF_FEATURE_FIND_GROUP( ACTS(group, gid_t gid;)) IF_FEATURE_FIND_LINKS( ACTS(links, char links_char; int links_count;)) struct globals { IF_FEATURE_FIND_XDEV(dev_t *xdev_dev;) IF_FEATURE_FIND_XDEV(int xdev_count;) #if ENABLE_FEATURE_FIND_MAXDEPTH int minmaxdepth[2]; #endif action ***actions; smallint need_print; smallint xdev_on; recurse_flags_t recurse_flags; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { \ struct G_sizecheck { \ char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \ }; \ /* we have to zero it out because of NOEXEC */ \ memset(&G, 0, sizeof(G)); \ IF_FEATURE_FIND_MAXDEPTH(G.minmaxdepth[1] = INT_MAX;) \ G.need_print = 1; \ G.recurse_flags = ACTION_RECURSE; \ } while (0) #if ENABLE_FEATURE_FIND_EXEC static unsigned count_subst(const char *str) { unsigned count = 0; while ((str = strstr(str, "{}")) != NULL) { count++; str++; } return count; } static char* subst(const char *src, unsigned count, const char* filename) { char *buf, *dst, *end; size_t flen = strlen(filename); /* we replace each '{}' with filename: growth by strlen-2 */ buf = dst = xmalloc(strlen(src) + count*(flen-2) + 1); while ((end = strstr(src, "{}"))) { memcpy(dst, src, end - src); dst += end - src; src = end + 2; memcpy(dst, filename, flen); dst += flen; } strcpy(dst, src); return buf; } #endif /* Return values of ACTFs ('action functions') are a bit mask: * bit 1=1: prune (use SKIP constant for setting it) * bit 0=1: matched successfully (TRUE) */ static int exec_actions(action ***appp, const char *fileName, const struct stat *statbuf) { int cur_group; int cur_action; int rc = 0; action **app, *ap; /* "action group" is a set of actions ANDed together. * groups are ORed together. * We simply evaluate each group until we find one in which all actions * succeed. */ /* -prune is special: if it is encountered, then we won't * descend into current directory. It doesn't matter whether * action group (in which -prune sits) will succeed or not: * find * -prune -name 'f*' -o -name 'm*' -- prunes every dir * find * -name 'f*' -o -prune -name 'm*' -- prunes all dirs * not starting with 'f' */ /* We invert TRUE bit (bit 0). Now 1 there means 'failure'. * and bitwise OR in "rc |= TRUE ^ ap->f()" will: * (1) make SKIP (-prune) bit stick; and (2) detect 'failure'. * On return, bit is restored. */ cur_group = -1; while ((app = appp[++cur_group]) != NULL) { rc &= ~TRUE; /* 'success' so far, clear TRUE bit */ cur_action = -1; while (1) { ap = app[++cur_action]; if (!ap) /* all actions in group were successful */ return rc ^ TRUE; /* restore TRUE bit */ rc |= TRUE ^ ap->f(fileName, statbuf, ap); #if ENABLE_FEATURE_FIND_NOT if (ap->invert) rc ^= TRUE; #endif dbg("grp %d action %d rc:0x%x", cur_group, cur_action, rc); if (rc & TRUE) /* current group failed, try next */ break; } } dbg("returning:0x%x", rc ^ TRUE); return rc ^ TRUE; /* restore TRUE bit */ } #if !FNM_CASEFOLD static char *strcpy_upcase(char *dst, const char *src) { char *d = dst; while (1) { unsigned char ch = *src++; if (ch >= 'a' && ch <= 'z') ch -= ('a' - 'A'); *d++ = ch; if (ch == '\0') break; } return dst; } #endif ACTF(name) { const char *tmp = bb_basename(fileName); if (tmp != fileName && *tmp == '\0') { /* "foo/bar/". Oh no... go back to 'b' */ tmp--; while (tmp != fileName && *--tmp != '/') continue; if (*tmp == '/') tmp++; } /* Was using FNM_PERIOD flag too, * but somewhere between 4.1.20 and 4.4.0 GNU find stopped using it. * find -name '*foo' should match .foo too: */ #if FNM_CASEFOLD return fnmatch(ap->pattern, tmp, (ap->iname ? FNM_CASEFOLD : 0)) == 0; #else if (ap->iname) tmp = strcpy_upcase(alloca(strlen(tmp) + 1), tmp); return fnmatch(ap->pattern, tmp, 0) == 0; #endif } #if ENABLE_FEATURE_FIND_PATH ACTF(path) { # if FNM_CASEFOLD return fnmatch(ap->pattern, fileName, (ap->ipath ? FNM_CASEFOLD : 0)) == 0; # else if (ap->ipath) fileName = strcpy_upcase(alloca(strlen(fileName) + 1), fileName); return fnmatch(ap->pattern, fileName, 0) == 0; # endif } #endif #if ENABLE_FEATURE_FIND_REGEX ACTF(regex) { regmatch_t match; if (regexec(&ap->compiled_pattern, fileName, 1, &match, 0 /*eflags*/)) return 0; /* no match */ if (match.rm_so) return 0; /* match doesn't start at pos 0 */ if (fileName[match.rm_eo]) return 0; /* match doesn't end exactly at end of pathname */ return 1; } #endif #if ENABLE_FEATURE_FIND_TYPE ACTF(type) { return ((statbuf->st_mode & S_IFMT) == ap->type_mask); } #endif #if ENABLE_FEATURE_FIND_PERM ACTF(perm) { /* -perm +mode: at least one of perm_mask bits are set */ if (ap->perm_char == '+') return (statbuf->st_mode & ap->perm_mask) != 0; /* -perm -mode: all of perm_mask are set */ if (ap->perm_char == '-') return (statbuf->st_mode & ap->perm_mask) == ap->perm_mask; /* -perm mode: file mode must match perm_mask */ return (statbuf->st_mode & 07777) == ap->perm_mask; } #endif #if ENABLE_FEATURE_FIND_MTIME ACTF(mtime) { time_t file_age = time(NULL) - statbuf->st_mtime; time_t mtime_secs = ap->mtime_days * 24*60*60; if (ap->mtime_char == '+') return file_age >= mtime_secs + 24*60*60; if (ap->mtime_char == '-') return file_age < mtime_secs; /* just numeric mtime */ return file_age >= mtime_secs && file_age < (mtime_secs + 24*60*60); } #endif #if ENABLE_FEATURE_FIND_MMIN ACTF(mmin) { time_t file_age = time(NULL) - statbuf->st_mtime; time_t mmin_secs = ap->mmin_mins * 60; if (ap->mmin_char == '+') return file_age >= mmin_secs + 60; if (ap->mmin_char == '-') return file_age < mmin_secs; /* just numeric mmin */ return file_age >= mmin_secs && file_age < (mmin_secs + 60); } #endif #if ENABLE_FEATURE_FIND_NEWER ACTF(newer) { return (ap->newer_mtime < statbuf->st_mtime); } #endif #if ENABLE_FEATURE_FIND_INUM ACTF(inum) { return (statbuf->st_ino == ap->inode_num); } #endif #if ENABLE_FEATURE_FIND_EXEC ACTF(exec) { int i, rc; #if ENABLE_USE_PORTABLE_CODE char **argv = alloca(sizeof(char*) * (ap->exec_argc + 1)); #else /* gcc 4.3.1 generates smaller code: */ char *argv[ap->exec_argc + 1]; #endif for (i = 0; i < ap->exec_argc; i++) argv[i] = subst(ap->exec_argv[i], ap->subst_count[i], fileName); argv[i] = NULL; /* terminate the list */ rc = spawn_and_wait(argv); if (rc < 0) bb_simple_perror_msg(argv[0]); i = 0; while (argv[i]) free(argv[i++]); return rc == 0; /* return 1 if exitcode 0 */ } #endif #if ENABLE_FEATURE_FIND_USER ACTF(user) { return (statbuf->st_uid == ap->uid); } #endif #if ENABLE_FEATURE_FIND_GROUP ACTF(group) { return (statbuf->st_gid == ap->gid); } #endif #if ENABLE_FEATURE_FIND_PRINT0 ACTF(print0) { printf("%s%c", fileName, '\0'); return TRUE; } #endif ACTF(print) { puts(fileName); return TRUE; } #if ENABLE_FEATURE_FIND_PAREN ACTF(paren) { return exec_actions(ap->subexpr, fileName, statbuf); } #endif #if ENABLE_FEATURE_FIND_SIZE ACTF(size) { if (ap->size_char == '+') return statbuf->st_size > ap->size; if (ap->size_char == '-') return statbuf->st_size < ap->size; return statbuf->st_size == ap->size; } #endif #if ENABLE_FEATURE_FIND_PRUNE /* * -prune: if -depth is not given, return true and do not descend * current dir; if -depth is given, return false with no effect. * Example: * find dir -name 'asm-*' -prune -o -name '*.[chS]' -print */ ACTF(prune) { return SKIP + TRUE; } #endif #if ENABLE_FEATURE_FIND_DELETE ACTF(delete) { int rc; if (S_ISDIR(statbuf->st_mode)) { rc = rmdir(fileName); } else { rc = unlink(fileName); } if (rc < 0) bb_simple_perror_msg(fileName); return TRUE; } #endif #if ENABLE_FEATURE_FIND_CONTEXT ACTF(context) { security_context_t con; int rc; if (G.recurse_flags & ACTION_FOLLOWLINKS) { rc = getfilecon(fileName, &con); } else { rc = lgetfilecon(fileName, &con); } if (rc < 0) return FALSE; rc = strcmp(ap->context, con); freecon(con); return rc == 0; } #endif #if ENABLE_FEATURE_FIND_LINKS ACTF(links) { switch(ap->links_char) { case '-' : return (statbuf->st_nlink < ap->links_count); case '+' : return (statbuf->st_nlink > ap->links_count); default: return (statbuf->st_nlink == ap->links_count); } } #endif static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void *userData UNUSED_PARAM, int depth IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM)) { int r; int same_fs = 1; #if ENABLE_FEATURE_FIND_XDEV if (S_ISDIR(statbuf->st_mode) && G.xdev_count) { int i; for (i = 0; i < G.xdev_count; i++) { if (G.xdev_dev[i] == statbuf->st_dev) goto found; } //bb_error_msg("'%s': not same fs", fileName); same_fs = 0; found: ; } #endif #if ENABLE_FEATURE_FIND_MAXDEPTH if (depth < G.minmaxdepth[0]) { if (same_fs) return TRUE; /* skip this, continue recursing */ return SKIP; /* stop recursing */ } if (depth > G.minmaxdepth[1]) return SKIP; /* stop recursing */ #endif r = exec_actions(G.actions, fileName, statbuf); /* Had no explicit -print[0] or -exec? then print */ if ((r & TRUE) && G.need_print) puts(fileName); #if ENABLE_FEATURE_FIND_MAXDEPTH if (S_ISDIR(statbuf->st_mode)) { if (depth == G.minmaxdepth[1]) return SKIP; } #endif /* -xdev stops on mountpoints, but AFTER mountpoit itself * is processed as usual */ if (!same_fs) { return SKIP; } /* Cannot return 0: our caller, recursive_action(), * will perror() and skip dirs (if called on dir) */ return (r & SKIP) ? SKIP : TRUE; } #if ENABLE_FEATURE_FIND_TYPE static int find_type(const char *type) { int mask = 0; if (*type == 'b') mask = S_IFBLK; else if (*type == 'c') mask = S_IFCHR; else if (*type == 'd') mask = S_IFDIR; else if (*type == 'p') mask = S_IFIFO; else if (*type == 'f') mask = S_IFREG; else if (*type == 'l') mask = S_IFLNK; else if (*type == 's') mask = S_IFSOCK; if (mask == 0 || type[1] != '\0') bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type"); return mask; } #endif #if ENABLE_FEATURE_FIND_PERM \ || ENABLE_FEATURE_FIND_MTIME || ENABLE_FEATURE_FIND_MMIN \ || ENABLE_FEATURE_FIND_SIZE || ENABLE_FEATURE_FIND_LINKS static const char* plus_minus_num(const char* str) { if (*str == '-' || *str == '+') str++; return str; } #endif /* Say no to GCCism */ #define USE_NESTED_FUNCTION 0 #if !USE_NESTED_FUNCTION struct pp_locals { action*** appp; unsigned cur_group; unsigned cur_action; IF_FEATURE_FIND_NOT( bool invert_flag; ) }; static action* alloc_action(struct pp_locals *ppl, int sizeof_struct, action_fp f) { action *ap = xzalloc(sizeof_struct); action **app; action ***group = &ppl->appp[ppl->cur_group]; *group = app = xrealloc(*group, (ppl->cur_action+2) * sizeof(ppl->appp[0][0])); app[ppl->cur_action++] = ap; app[ppl->cur_action] = NULL; ap->f = f; IF_FEATURE_FIND_NOT( ap->invert = ppl->invert_flag; ) IF_FEATURE_FIND_NOT( ppl->invert_flag = 0; ) return ap; } #endif static action*** parse_params(char **argv) { enum { OPT_FOLLOW , IF_FEATURE_FIND_XDEV( OPT_XDEV ,) IF_FEATURE_FIND_DEPTH( OPT_DEPTH ,) PARM_a , PARM_o , IF_FEATURE_FIND_NOT( PARM_char_not ,) #if ENABLE_DESKTOP PARM_and , PARM_or , IF_FEATURE_FIND_NOT( PARM_not ,) #endif PARM_print , IF_FEATURE_FIND_PRINT0( PARM_print0 ,) IF_FEATURE_FIND_PRUNE( PARM_prune ,) IF_FEATURE_FIND_DELETE( PARM_delete ,) IF_FEATURE_FIND_EXEC( PARM_exec ,) IF_FEATURE_FIND_PAREN( PARM_char_brace,) /* All options/actions starting from here require argument */ PARM_name , PARM_iname , IF_FEATURE_FIND_PATH( PARM_path ,) #if ENABLE_DESKTOP /* -wholename is a synonym for -path */ /* We support it because Linux kernel's "make tags" uses it */ IF_FEATURE_FIND_PATH( PARM_wholename ,) #endif IF_FEATURE_FIND_PATH( PARM_ipath ,) IF_FEATURE_FIND_REGEX( PARM_regex ,) IF_FEATURE_FIND_TYPE( PARM_type ,) IF_FEATURE_FIND_PERM( PARM_perm ,) IF_FEATURE_FIND_MTIME( PARM_mtime ,) IF_FEATURE_FIND_MMIN( PARM_mmin ,) IF_FEATURE_FIND_NEWER( PARM_newer ,) IF_FEATURE_FIND_INUM( PARM_inum ,) IF_FEATURE_FIND_USER( PARM_user ,) IF_FEATURE_FIND_GROUP( PARM_group ,) IF_FEATURE_FIND_SIZE( PARM_size ,) IF_FEATURE_FIND_CONTEXT(PARM_context ,) IF_FEATURE_FIND_LINKS( PARM_links ,) IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,OPT_MAXDEPTH,) }; static const char params[] ALIGN1 = "-follow\0" IF_FEATURE_FIND_XDEV( "-xdev\0" ) IF_FEATURE_FIND_DEPTH( "-depth\0" ) "-a\0" "-o\0" IF_FEATURE_FIND_NOT( "!\0" ) #if ENABLE_DESKTOP "-and\0" "-or\0" IF_FEATURE_FIND_NOT( "-not\0" ) #endif "-print\0" IF_FEATURE_FIND_PRINT0( "-print0\0" ) IF_FEATURE_FIND_PRUNE( "-prune\0" ) IF_FEATURE_FIND_DELETE( "-delete\0" ) IF_FEATURE_FIND_EXEC( "-exec\0" ) IF_FEATURE_FIND_PAREN( "(\0" ) /* All options/actions starting from here require argument */ "-name\0" "-iname\0" IF_FEATURE_FIND_PATH( "-path\0" ) #if ENABLE_DESKTOP IF_FEATURE_FIND_PATH( "-wholename\0") #endif IF_FEATURE_FIND_PATH( "-ipath\0" ) IF_FEATURE_FIND_REGEX( "-regex\0" ) IF_FEATURE_FIND_TYPE( "-type\0" ) IF_FEATURE_FIND_PERM( "-perm\0" ) IF_FEATURE_FIND_MTIME( "-mtime\0" ) IF_FEATURE_FIND_MMIN( "-mmin\0" ) IF_FEATURE_FIND_NEWER( "-newer\0" ) IF_FEATURE_FIND_INUM( "-inum\0" ) IF_FEATURE_FIND_USER( "-user\0" ) IF_FEATURE_FIND_GROUP( "-group\0" ) IF_FEATURE_FIND_SIZE( "-size\0" ) IF_FEATURE_FIND_CONTEXT("-context\0") IF_FEATURE_FIND_LINKS( "-links\0" ) IF_FEATURE_FIND_MAXDEPTH("-mindepth\0""-maxdepth\0") ; #if !USE_NESTED_FUNCTION struct pp_locals ppl; #define appp (ppl.appp ) #define cur_group (ppl.cur_group ) #define cur_action (ppl.cur_action ) #define invert_flag (ppl.invert_flag) #define ALLOC_ACTION(name) (action_##name*)alloc_action(&ppl, sizeof(action_##name), (action_fp) func_##name) #else action*** appp; unsigned cur_group; unsigned cur_action; IF_FEATURE_FIND_NOT( bool invert_flag; ) /* This is the only place in busybox where we use nested function. * So far more standard alternatives were bigger. */ /* Auto decl suppresses "func without a prototype" warning: */ auto action* alloc_action(int sizeof_struct, action_fp f); action* alloc_action(int sizeof_struct, action_fp f) { action *ap; appp[cur_group] = xrealloc(appp[cur_group], (cur_action+2) * sizeof(appp[0][0])); appp[cur_group][cur_action++] = ap = xzalloc(sizeof_struct); appp[cur_group][cur_action] = NULL; ap->f = f; IF_FEATURE_FIND_NOT( ap->invert = invert_flag; ) IF_FEATURE_FIND_NOT( invert_flag = 0; ) return ap; } #define ALLOC_ACTION(name) (action_##name*)alloc_action(sizeof(action_##name), (action_fp) func_##name) #endif cur_group = 0; cur_action = 0; IF_FEATURE_FIND_NOT( invert_flag = 0; ) appp = xzalloc(2 * sizeof(appp[0])); /* appp[0],[1] == NULL */ while (*argv) { const char *arg = argv[0]; int parm = index_in_strings(params, arg); const char *arg1 = argv[1]; dbg("arg:'%s' arg1:'%s' parm:%d PARM_type:%d", arg, arg1, parm, PARM_type); if (parm >= PARM_name) { /* All options/actions starting from -name require argument */ if (!arg1) bb_error_msg_and_die(bb_msg_requires_arg, arg); argv++; } /* We can use big switch() here, but on i386 * it doesn't give smaller code. Other arches? */ /* Options always return true. They always take effect * rather than being processed only when their place in the * expression is reached. */ /* Options */ if (parm == OPT_FOLLOW) { dbg("follow enabled: %d", __LINE__); G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK; } #if ENABLE_FEATURE_FIND_XDEV else if (parm == OPT_XDEV) { dbg("%d", __LINE__); G.xdev_on = 1; } #endif #if ENABLE_FEATURE_FIND_MAXDEPTH else if (parm == OPT_MINDEPTH || parm == OPT_MINDEPTH + 1) { dbg("%d", __LINE__); G.minmaxdepth[parm - OPT_MINDEPTH] = xatoi_positive(arg1); } #endif #if ENABLE_FEATURE_FIND_DEPTH else if (parm == OPT_DEPTH) { dbg("%d", __LINE__); G.recurse_flags |= ACTION_DEPTHFIRST; } #endif /* Actions are grouped by operators * ( expr ) Force precedence * ! expr True if expr is false * -not expr Same as ! expr * expr1 [-a[nd]] expr2 And; expr2 is not evaluated if expr1 is false * expr1 -o[r] expr2 Or; expr2 is not evaluated if expr1 is true * expr1 , expr2 List; both expr1 and expr2 are always evaluated * We implement: (), -a, -o */ /* Operators */ else if (parm == PARM_a IF_DESKTOP(|| parm == PARM_and)) { dbg("%d", __LINE__); /* no further special handling required */ } else if (parm == PARM_o IF_DESKTOP(|| parm == PARM_or)) { dbg("%d", __LINE__); /* start new OR group */ cur_group++; appp = xrealloc(appp, (cur_group+2) * sizeof(appp[0])); /*appp[cur_group] = NULL; - already NULL */ appp[cur_group+1] = NULL; cur_action = 0; } #if ENABLE_FEATURE_FIND_NOT else if (parm == PARM_char_not IF_DESKTOP(|| parm == PARM_not)) { /* also handles "find ! ! -name 'foo*'" */ invert_flag ^= 1; dbg("invert_flag:%d", invert_flag); } #endif /* Actions */ else if (parm == PARM_print) { dbg("%d", __LINE__); G.need_print = 0; (void) ALLOC_ACTION(print); } #if ENABLE_FEATURE_FIND_PRINT0 else if (parm == PARM_print0) { dbg("%d", __LINE__); G.need_print = 0; (void) ALLOC_ACTION(print0); } #endif #if ENABLE_FEATURE_FIND_PRUNE else if (parm == PARM_prune) { dbg("%d", __LINE__); (void) ALLOC_ACTION(prune); } #endif #if ENABLE_FEATURE_FIND_DELETE else if (parm == PARM_delete) { dbg("%d", __LINE__); G.need_print = 0; G.recurse_flags |= ACTION_DEPTHFIRST; (void) ALLOC_ACTION(delete); } #endif #if ENABLE_FEATURE_FIND_EXEC else if (parm == PARM_exec) { int i; action_exec *ap; dbg("%d", __LINE__); G.need_print = 0; ap = ALLOC_ACTION(exec); ap->exec_argv = ++argv; /* first arg after -exec */ /*ap->exec_argc = 0; - ALLOC_ACTION did it */ while (1) { if (!*argv) /* did not see ';' or '+' until end */ bb_error_msg_and_die(bb_msg_requires_arg, "-exec"); // find -exec echo Foo ">{}<" ";" // executes "echo Foo >FILENAME<", // find -exec echo Foo ">{}<" "+" // executes "echo Foo FILENAME1 FILENAME2 FILENAME3...". // TODO (so far we treat "+" just like ";") if ((argv[0][0] == ';' || argv[0][0] == '+') && argv[0][1] == '\0' ) { break; } argv++; ap->exec_argc++; } if (ap->exec_argc == 0) bb_error_msg_and_die(bb_msg_requires_arg, arg); ap->subst_count = xmalloc(ap->exec_argc * sizeof(int)); i = ap->exec_argc; while (i--) ap->subst_count[i] = count_subst(ap->exec_argv[i]); } #endif #if ENABLE_FEATURE_FIND_PAREN else if (parm == PARM_char_brace) { action_paren *ap; char **endarg; unsigned nested = 1; dbg("%d", __LINE__); endarg = argv; while (1) { if (!*++endarg) bb_error_msg_and_die("unpaired '('"); if (LONE_CHAR(*endarg, '(')) nested++; else if (LONE_CHAR(*endarg, ')') && !--nested) { *endarg = NULL; break; } } ap = ALLOC_ACTION(paren); ap->subexpr = parse_params(argv + 1); *endarg = (char*) ")"; /* restore NULLed parameter */ argv = endarg; } #endif else if (parm == PARM_name || parm == PARM_iname) { action_name *ap; dbg("%d", __LINE__); ap = ALLOC_ACTION(name); ap->pattern = arg1; ap->iname = (parm == PARM_iname); } #if ENABLE_FEATURE_FIND_PATH else if (parm == PARM_path IF_DESKTOP(|| parm == PARM_wholename) || parm == PARM_ipath) { action_path *ap; dbg("%d", __LINE__); ap = ALLOC_ACTION(path); ap->pattern = arg1; ap->ipath = (parm == PARM_ipath); } #endif #if ENABLE_FEATURE_FIND_REGEX else if (parm == PARM_regex) { action_regex *ap; dbg("%d", __LINE__); ap = ALLOC_ACTION(regex); xregcomp(&ap->compiled_pattern, arg1, 0 /*cflags*/); } #endif #if ENABLE_FEATURE_FIND_TYPE else if (parm == PARM_type) { action_type *ap; ap = ALLOC_ACTION(type); ap->type_mask = find_type(arg1); dbg("created:type mask:%x", ap->type_mask); } #endif #if ENABLE_FEATURE_FIND_PERM /* -perm BITS File's mode bits are exactly BITS (octal or symbolic). * Symbolic modes use mode 0 as a point of departure. * -perm -BITS All of the BITS are set in file's mode. * -perm +BITS At least one of the BITS is set in file's mode. */ else if (parm == PARM_perm) { action_perm *ap; dbg("%d", __LINE__); ap = ALLOC_ACTION(perm); ap->perm_char = arg1[0]; arg1 = plus_minus_num(arg1); /*ap->perm_mask = 0; - ALLOC_ACTION did it */ if (!bb_parse_mode(arg1, &ap->perm_mask)) bb_error_msg_and_die("invalid mode '%s'", arg1); } #endif #if ENABLE_FEATURE_FIND_MTIME else if (parm == PARM_mtime) { action_mtime *ap; dbg("%d", __LINE__); ap = ALLOC_ACTION(mtime); ap->mtime_char = arg1[0]; ap->mtime_days = xatoul(plus_minus_num(arg1)); } #endif #if ENABLE_FEATURE_FIND_MMIN else if (parm == PARM_mmin) { action_mmin *ap; dbg("%d", __LINE__); ap = ALLOC_ACTION(mmin); ap->mmin_char = arg1[0]; ap->mmin_mins = xatoul(plus_minus_num(arg1)); } #endif #if ENABLE_FEATURE_FIND_NEWER else if (parm == PARM_newer) { struct stat stat_newer; action_newer *ap; dbg("%d", __LINE__); ap = ALLOC_ACTION(newer); xstat(arg1, &stat_newer); ap->newer_mtime = stat_newer.st_mtime; } #endif #if ENABLE_FEATURE_FIND_INUM else if (parm == PARM_inum) { action_inum *ap; dbg("%d", __LINE__); ap = ALLOC_ACTION(inum); ap->inode_num = xatoul(arg1); } #endif #if ENABLE_FEATURE_FIND_USER else if (parm == PARM_user) { action_user *ap; dbg("%d", __LINE__); ap = ALLOC_ACTION(user); ap->uid = bb_strtou(arg1, NULL, 10); if (errno) ap->uid = xuname2uid(arg1); } #endif #if ENABLE_FEATURE_FIND_GROUP else if (parm == PARM_group) { action_group *ap; dbg("%d", __LINE__); ap = ALLOC_ACTION(group); ap->gid = bb_strtou(arg1, NULL, 10); if (errno) ap->gid = xgroup2gid(arg1); } #endif #if ENABLE_FEATURE_FIND_SIZE else if (parm == PARM_size) { /* -size n[bckw]: file uses n units of space * b (default): units are 512-byte blocks * c: 1 byte * k: kilobytes * w: 2-byte words */ #if ENABLE_LFS #define XATOU_SFX xatoull_sfx #else #define XATOU_SFX xatoul_sfx #endif static const struct suffix_mult find_suffixes[] = { { "c", 1 }, { "w", 2 }, { "", 512 }, { "b", 512 }, { "k", 1024 }, { "", 0 } }; action_size *ap; dbg("%d", __LINE__); ap = ALLOC_ACTION(size); ap->size_char = arg1[0]; ap->size = XATOU_SFX(plus_minus_num(arg1), find_suffixes); } #endif #if ENABLE_FEATURE_FIND_CONTEXT else if (parm == PARM_context) { action_context *ap; dbg("%d", __LINE__); ap = ALLOC_ACTION(context); /*ap->context = NULL; - ALLOC_ACTION did it */ /* SELinux headers erroneously declare non-const parameter */ if (selinux_raw_to_trans_context((char*)arg1, &ap->context)) bb_simple_perror_msg(arg1); } #endif #if ENABLE_FEATURE_FIND_LINKS else if (parm == PARM_links) { action_links *ap; dbg("%d", __LINE__); ap = ALLOC_ACTION(links); ap->links_char = arg1[0]; ap->links_count = xatoul(plus_minus_num(arg1)); } #endif else { bb_error_msg("unrecognized: %s", arg); bb_show_usage(); } argv++; } dbg("exiting %s", __func__); return appp; #undef ALLOC_ACTION #undef appp #undef cur_action #undef invert_flag } int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int find_main(int argc UNUSED_PARAM, char **argv) { int i, firstopt, status = EXIT_SUCCESS; char **past_HLP, *saved; INIT_G(); /* "find -type f" + getopt("+HLP") => disaster. * Need to avoid getopt running into a non-HLP option. * Do this by temporarily storing NULL there: */ past_HLP = argv; for (;;) { saved = *++past_HLP; if (!saved) break; if (saved[0] != '-') break; if (!saved[1]) break; /* it is "-" */ if ((saved+1)[strspn(saved+1, "HLP")] != '\0') break; } *past_HLP = NULL; /* "+": stop on first non-option */ i = getopt32(argv, "+HLP"); if (i & (1<<0)) G.recurse_flags |= ACTION_FOLLOWLINKS_L0 | ACTION_DANGLING_OK; if (i & (1<<1)) G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK; /* -P is default and is ignored */ argv = past_HLP; /* same result as "argv += optind;" */ *past_HLP = saved; for (firstopt = 0; argv[firstopt]; firstopt++) { if (argv[firstopt][0] == '-') break; if (ENABLE_FEATURE_FIND_NOT && LONE_CHAR(argv[firstopt], '!')) break; if (ENABLE_FEATURE_FIND_PAREN && LONE_CHAR(argv[firstopt], '(')) break; } if (firstopt == 0) { *--argv = (char*)"."; firstopt++; } G.actions = parse_params(&argv[firstopt]); argv[firstopt] = NULL; #if ENABLE_FEATURE_FIND_XDEV if (G.xdev_on) { struct stat stbuf; G.xdev_count = firstopt; G.xdev_dev = xzalloc(G.xdev_count * sizeof(G.xdev_dev[0])); for (i = 0; argv[i]; i++) { /* not xstat(): shouldn't bomb out on * "find not_exist exist -xdev" */ if (stat(argv[i], &stbuf) == 0) G.xdev_dev[i] = stbuf.st_dev; /* else G.xdev_dev[i] stays 0 and * won't match any real device dev_t */ } } #endif for (i = 0; argv[i]; i++) { if (!recursive_action(argv[i], G.recurse_flags,/* flags */ fileAction, /* file action */ fileAction, /* dir action */ NULL, /* user data */ 0) /* depth */ ) { status = EXIT_FAILURE; } } return status; } busybox-1.22.1/findutils/Kbuild.src0000644000000000000000000000025512263563520015720 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT busybox-1.22.1/findutils/grep.c0000644000000000000000000006163312267106071015103 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini grep implementation for busybox using libc regex. * * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley * Copyright (C) 1999,2000,2001 by Mark Whitley * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* BB_AUDIT SUSv3 defects - unsupported option -x "match whole line only". */ /* BB_AUDIT GNU defects - always acts as -a. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/grep.html */ /* * 2004,2006 (C) Vladimir Oleynik - * correction "-e pattern1 -e pattern2" logic and more optimizations. * precompiled regex * * (C) 2006 Jac Goudsmit added -o option */ //applet:IF_GREP(APPLET(grep, BB_DIR_BIN, BB_SUID_DROP)) //applet:IF_FEATURE_GREP_EGREP_ALIAS(APPLET_ODDNAME(egrep, grep, BB_DIR_BIN, BB_SUID_DROP, egrep)) //applet:IF_FEATURE_GREP_FGREP_ALIAS(APPLET_ODDNAME(fgrep, grep, BB_DIR_BIN, BB_SUID_DROP, fgrep)) //kbuild:lib-$(CONFIG_GREP) += grep.o //config:config GREP //config: bool "grep" //config: default y //config: help //config: grep is used to search files for a specified pattern. //config: //config:config FEATURE_GREP_EGREP_ALIAS //config: bool "Enable extended regular expressions (egrep & grep -E)" //config: default y //config: depends on GREP //config: help //config: Enabled support for extended regular expressions. Extended //config: regular expressions allow for alternation (foo|bar), grouping, //config: and various repetition operators. //config: //config:config FEATURE_GREP_FGREP_ALIAS //config: bool "Alias fgrep to grep -F" //config: default y //config: depends on GREP //config: help //config: fgrep sees the search pattern as a normal string rather than //config: regular expressions. //config: grep -F always works, this just creates the fgrep alias. //config: //config:config FEATURE_GREP_CONTEXT //config: bool "Enable before and after context flags (-A, -B and -C)" //config: default y //config: depends on GREP //config: help //config: Print the specified number of leading (-B) and/or trailing (-A) //config: context surrounding our matching lines. //config: Print the specified number of context lines (-C). #include "libbb.h" #include "xregex.h" /* options */ //usage:#define grep_trivial_usage //usage: "[-HhnlLoqvsriw" //usage: "F" //usage: IF_FEATURE_GREP_EGREP_ALIAS("E") //usage: IF_EXTRA_COMPAT("z") //usage: "] [-m N] " //usage: IF_FEATURE_GREP_CONTEXT("[-A/B/C N] ") //usage: "PATTERN/-e PATTERN.../-f FILE [FILE]..." //usage:#define grep_full_usage "\n\n" //usage: "Search for PATTERN in FILEs (or stdin)\n" //usage: "\n -H Add 'filename:' prefix" //usage: "\n -h Do not add 'filename:' prefix" //usage: "\n -n Add 'line_no:' prefix" //usage: "\n -l Show only names of files that match" //usage: "\n -L Show only names of files that don't match" //usage: "\n -c Show only count of matching lines" //usage: "\n -o Show only the matching part of line" //usage: "\n -q Quiet. Return 0 if PATTERN is found, 1 otherwise" //usage: "\n -v Select non-matching lines" //usage: "\n -s Suppress open and read errors" //usage: "\n -r Recurse" //usage: "\n -i Ignore case" //usage: "\n -w Match whole words only" //usage: "\n -x Match whole lines only" //usage: "\n -F PATTERN is a literal (not regexp)" //usage: IF_FEATURE_GREP_EGREP_ALIAS( //usage: "\n -E PATTERN is an extended regexp" //usage: ) //usage: IF_EXTRA_COMPAT( //usage: "\n -z Input is NUL terminated" //usage: ) //usage: "\n -m N Match up to N times per file" //usage: IF_FEATURE_GREP_CONTEXT( //usage: "\n -A N Print N lines of trailing context" //usage: "\n -B N Print N lines of leading context" //usage: "\n -C N Same as '-A N -B N'" //usage: ) //usage: "\n -e PTRN Pattern to match" //usage: "\n -f FILE Read pattern from file" //usage: //usage:#define grep_example_usage //usage: "$ grep root /etc/passwd\n" //usage: "root:x:0:0:root:/root:/bin/bash\n" //usage: "$ grep ^[rR]oo. /etc/passwd\n" //usage: "root:x:0:0:root:/root:/bin/bash\n" //usage: //usage:#define egrep_trivial_usage NOUSAGE_STR //usage:#define egrep_full_usage "" //usage:#define fgrep_trivial_usage NOUSAGE_STR //usage:#define fgrep_full_usage "" #define OPTSTR_GREP \ "lnqvscFiHhe:f:Lorm:wx" \ IF_FEATURE_GREP_CONTEXT("A:B:C:") \ IF_FEATURE_GREP_EGREP_ALIAS("E") \ IF_EXTRA_COMPAT("z") \ "aI" /* ignored: -a "assume all files to be text" */ /* ignored: -I "assume binary files have no matches" */ enum { OPTBIT_l, /* list matched file names only */ OPTBIT_n, /* print line# */ OPTBIT_q, /* quiet - exit(EXIT_SUCCESS) of first match */ OPTBIT_v, /* invert the match, to select non-matching lines */ OPTBIT_s, /* suppress errors about file open errors */ OPTBIT_c, /* count matches per file (suppresses normal output) */ OPTBIT_F, /* literal match */ OPTBIT_i, /* case-insensitive */ OPTBIT_H, /* force filename display */ OPTBIT_h, /* inhibit filename display */ OPTBIT_e, /* -e PATTERN */ OPTBIT_f, /* -f FILE_WITH_PATTERNS */ OPTBIT_L, /* list unmatched file names only */ OPTBIT_o, /* show only matching parts of lines */ OPTBIT_r, /* recurse dirs */ OPTBIT_m, /* -m MAX_MATCHES */ OPTBIT_w, /* -w whole word match */ OPTBIT_x, /* -x whole line match */ IF_FEATURE_GREP_CONTEXT( OPTBIT_A ,) /* -A NUM: after-match context */ IF_FEATURE_GREP_CONTEXT( OPTBIT_B ,) /* -B NUM: before-match context */ IF_FEATURE_GREP_CONTEXT( OPTBIT_C ,) /* -C NUM: -A and -B combined */ IF_FEATURE_GREP_EGREP_ALIAS(OPTBIT_E ,) /* extended regexp */ IF_EXTRA_COMPAT( OPTBIT_z ,) /* input is NUL terminated */ OPT_l = 1 << OPTBIT_l, OPT_n = 1 << OPTBIT_n, OPT_q = 1 << OPTBIT_q, OPT_v = 1 << OPTBIT_v, OPT_s = 1 << OPTBIT_s, OPT_c = 1 << OPTBIT_c, OPT_F = 1 << OPTBIT_F, OPT_i = 1 << OPTBIT_i, OPT_H = 1 << OPTBIT_H, OPT_h = 1 << OPTBIT_h, OPT_e = 1 << OPTBIT_e, OPT_f = 1 << OPTBIT_f, OPT_L = 1 << OPTBIT_L, OPT_o = 1 << OPTBIT_o, OPT_r = 1 << OPTBIT_r, OPT_m = 1 << OPTBIT_m, OPT_w = 1 << OPTBIT_w, OPT_x = 1 << OPTBIT_x, OPT_A = IF_FEATURE_GREP_CONTEXT( (1 << OPTBIT_A)) + 0, OPT_B = IF_FEATURE_GREP_CONTEXT( (1 << OPTBIT_B)) + 0, OPT_C = IF_FEATURE_GREP_CONTEXT( (1 << OPTBIT_C)) + 0, OPT_E = IF_FEATURE_GREP_EGREP_ALIAS((1 << OPTBIT_E)) + 0, OPT_z = IF_EXTRA_COMPAT( (1 << OPTBIT_z)) + 0, }; #define PRINT_FILES_WITH_MATCHES (option_mask32 & OPT_l) #define PRINT_LINE_NUM (option_mask32 & OPT_n) #define BE_QUIET (option_mask32 & OPT_q) #define SUPPRESS_ERR_MSGS (option_mask32 & OPT_s) #define PRINT_MATCH_COUNTS (option_mask32 & OPT_c) #define FGREP_FLAG (option_mask32 & OPT_F) #define PRINT_FILES_WITHOUT_MATCHES (option_mask32 & OPT_L) #define NUL_DELIMITED (option_mask32 & OPT_z) struct globals { int max_matches; #if !ENABLE_EXTRA_COMPAT int reflags; #else RE_TRANSLATE_TYPE case_fold; /* RE_TRANSLATE_TYPE is [[un]signed] char* */ #endif smalluint invert_search; smalluint print_filename; smalluint open_errors; #if ENABLE_FEATURE_GREP_CONTEXT smalluint did_print_line; int lines_before; int lines_after; char **before_buf; IF_EXTRA_COMPAT(size_t *before_buf_size;) int last_line_printed; #endif /* globals used internally */ llist_t *pattern_head; /* growable list of patterns to match */ const char *cur_file; /* the current file we are reading */ } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { \ struct G_sizecheck { \ char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \ }; \ } while (0) #define max_matches (G.max_matches ) #if !ENABLE_EXTRA_COMPAT # define reflags (G.reflags ) #else # define case_fold (G.case_fold ) /* http://www.delorie.com/gnu/docs/regex/regex_46.html */ # define reflags re_syntax_options # undef REG_NOSUB # undef REG_EXTENDED # undef REG_ICASE # define REG_NOSUB bug:is:here /* should not be used */ /* Just RE_SYNTAX_EGREP is not enough, need to enable {n[,[m]]} too */ # define REG_EXTENDED (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES) # define REG_ICASE bug:is:here /* should not be used */ #endif #define invert_search (G.invert_search ) #define print_filename (G.print_filename ) #define open_errors (G.open_errors ) #define did_print_line (G.did_print_line ) #define lines_before (G.lines_before ) #define lines_after (G.lines_after ) #define before_buf (G.before_buf ) #define before_buf_size (G.before_buf_size ) #define last_line_printed (G.last_line_printed ) #define pattern_head (G.pattern_head ) #define cur_file (G.cur_file ) typedef struct grep_list_data_t { char *pattern; /* for GNU regex, matched_range must be persistent across grep_file() calls */ #if !ENABLE_EXTRA_COMPAT regex_t compiled_regex; regmatch_t matched_range; #else struct re_pattern_buffer compiled_regex; struct re_registers matched_range; #endif #define ALLOCATED 1 #define COMPILED 2 int flg_mem_alocated_compiled; } grep_list_data_t; #if !ENABLE_EXTRA_COMPAT #define print_line(line, line_len, linenum, decoration) \ print_line(line, linenum, decoration) #endif static void print_line(const char *line, size_t line_len, int linenum, char decoration) { #if ENABLE_FEATURE_GREP_CONTEXT /* Happens when we go to next file, immediately hit match * and try to print prev context... from prev file! Don't do it */ if (linenum < 1) return; /* possibly print the little '--' separator */ if ((lines_before || lines_after) && did_print_line && last_line_printed != linenum - 1 ) { puts("--"); } /* guard against printing "--" before first line of first file */ did_print_line = 1; last_line_printed = linenum; #endif if (print_filename) printf("%s%c", cur_file, decoration); if (PRINT_LINE_NUM) printf("%i%c", linenum, decoration); /* Emulate weird GNU grep behavior with -ov */ if ((option_mask32 & (OPT_v|OPT_o)) != (OPT_v|OPT_o)) { #if !ENABLE_EXTRA_COMPAT puts(line); #else fwrite(line, 1, line_len, stdout); putchar(NUL_DELIMITED ? '\0' : '\n'); #endif } } #if ENABLE_EXTRA_COMPAT /* Unlike getline, this one removes trailing '\n' */ static ssize_t FAST_FUNC bb_getline(char **line_ptr, size_t *line_alloc_len, FILE *file) { ssize_t res_sz; char *line; int delim = (NUL_DELIMITED ? '\0' : '\n'); res_sz = getdelim(line_ptr, line_alloc_len, delim, file); line = *line_ptr; if (res_sz > 0) { if (line[res_sz - 1] == delim) line[--res_sz] = '\0'; } else { free(line); /* uclibc allocates a buffer even on EOF. WTF? */ } return res_sz; } #endif static int grep_file(FILE *file) { smalluint found; int linenum = 0; int nmatches = 0; #if !ENABLE_EXTRA_COMPAT char *line; #else char *line = NULL; ssize_t line_len; size_t line_alloc_len; # define rm_so start[0] # define rm_eo end[0] #endif #if ENABLE_FEATURE_GREP_CONTEXT int print_n_lines_after = 0; int curpos = 0; /* track where we are in the circular 'before' buffer */ int idx = 0; /* used for iteration through the circular buffer */ #else enum { print_n_lines_after = 0 }; #endif while ( #if !ENABLE_EXTRA_COMPAT (line = xmalloc_fgetline(file)) != NULL #else (line_len = bb_getline(&line, &line_alloc_len, file)) >= 0 #endif ) { llist_t *pattern_ptr = pattern_head; grep_list_data_t *gl = gl; /* for gcc */ linenum++; found = 0; while (pattern_ptr) { gl = (grep_list_data_t *)pattern_ptr->data; if (FGREP_FLAG) { char *match; char *str = line; opt_f_again: match = ((option_mask32 & OPT_i) ? strcasestr(str, gl->pattern) : strstr(str, gl->pattern) ); if (match) { if (option_mask32 & OPT_x) { if (match != str) goto opt_f_not_found; if (str[strlen(gl->pattern)] != '\0') goto opt_f_not_found; } else if (option_mask32 & OPT_w) { char c = (match != str) ? match[-1] : ' '; if (!isalnum(c) && c != '_') { c = match[strlen(gl->pattern)]; if (!c || (!isalnum(c) && c != '_')) goto opt_f_found; } str = match + 1; goto opt_f_again; } opt_f_found: found = 1; opt_f_not_found: ; } } else { #if ENABLE_EXTRA_COMPAT unsigned start_pos; #endif char *match_at; if (!(gl->flg_mem_alocated_compiled & COMPILED)) { gl->flg_mem_alocated_compiled |= COMPILED; #if !ENABLE_EXTRA_COMPAT xregcomp(&gl->compiled_regex, gl->pattern, reflags); #else memset(&gl->compiled_regex, 0, sizeof(gl->compiled_regex)); gl->compiled_regex.translate = case_fold; /* for -i */ if (re_compile_pattern(gl->pattern, strlen(gl->pattern), &gl->compiled_regex)) bb_error_msg_and_die("bad regex '%s'", gl->pattern); #endif } #if !ENABLE_EXTRA_COMPAT gl->matched_range.rm_so = 0; gl->matched_range.rm_eo = 0; #else start_pos = 0; #endif match_at = line; opt_w_again: //bb_error_msg("'%s' start_pos:%d line_len:%d", match_at, start_pos, line_len); if ( #if !ENABLE_EXTRA_COMPAT regexec(&gl->compiled_regex, match_at, 1, &gl->matched_range, 0) == 0 #else re_search(&gl->compiled_regex, match_at, line_len, start_pos, /*range:*/ line_len, &gl->matched_range) >= 0 #endif ) { if (option_mask32 & OPT_x) { found = (gl->matched_range.rm_so == 0 && match_at[gl->matched_range.rm_eo] == '\0'); } else if (!(option_mask32 & OPT_w)) { found = 1; } else { char c = ' '; if (gl->matched_range.rm_so) c = match_at[gl->matched_range.rm_so - 1]; if (!isalnum(c) && c != '_') { c = match_at[gl->matched_range.rm_eo]; if (!c || (!isalnum(c) && c != '_')) { found = 1; } else { /* * Why check gl->matched_range.rm_eo? * Zero-length match makes -w skip the line: * "echo foo | grep ^" prints "foo", * "echo foo | grep -w ^" prints nothing. * Without such check, we can loop forever. */ #if !ENABLE_EXTRA_COMPAT if (gl->matched_range.rm_eo != 0) { match_at += gl->matched_range.rm_eo; goto opt_w_again; } #else if (gl->matched_range.rm_eo > start_pos) { start_pos = gl->matched_range.rm_eo; goto opt_w_again; } #endif } } } } } /* If it's non-inverted search, we can stop * at first match */ if (found && !invert_search) goto do_found; pattern_ptr = pattern_ptr->link; } /* while (pattern_ptr) */ if (found ^ invert_search) { do_found: /* keep track of matches */ nmatches++; /* quiet/print (non)matching file names only? */ if (option_mask32 & (OPT_q|OPT_l|OPT_L)) { free(line); /* we don't need line anymore */ if (BE_QUIET) { /* manpage says about -q: * "exit immediately with zero status * if any match is found, * even if errors were detected" */ exit(EXIT_SUCCESS); } /* if we're just printing filenames, we stop after the first match */ if (PRINT_FILES_WITH_MATCHES) { puts(cur_file); /* fall through to "return 1" */ } /* OPT_L aka PRINT_FILES_WITHOUT_MATCHES: return early */ return 1; /* one match */ } #if ENABLE_FEATURE_GREP_CONTEXT /* Were we printing context and saw next (unwanted) match? */ if ((option_mask32 & OPT_m) && nmatches > max_matches) break; #endif /* print the matched line */ if (PRINT_MATCH_COUNTS == 0) { #if ENABLE_FEATURE_GREP_CONTEXT int prevpos = (curpos == 0) ? lines_before - 1 : curpos - 1; /* if we were told to print 'before' lines and there is at least * one line in the circular buffer, print them */ if (lines_before && before_buf[prevpos] != NULL) { int first_buf_entry_line_num = linenum - lines_before; /* advance to the first entry in the circular buffer, and * figure out the line number is of the first line in the * buffer */ idx = curpos; while (before_buf[idx] == NULL) { idx = (idx + 1) % lines_before; first_buf_entry_line_num++; } /* now print each line in the buffer, clearing them as we go */ while (before_buf[idx] != NULL) { print_line(before_buf[idx], before_buf_size[idx], first_buf_entry_line_num, '-'); free(before_buf[idx]); before_buf[idx] = NULL; idx = (idx + 1) % lines_before; first_buf_entry_line_num++; } } /* make a note that we need to print 'after' lines */ print_n_lines_after = lines_after; #endif if (option_mask32 & OPT_o) { if (FGREP_FLAG) { /* -Fo just prints the pattern * (unless -v: -Fov doesnt print anything at all) */ if (found) print_line(gl->pattern, strlen(gl->pattern), linenum, ':'); } else while (1) { unsigned start = gl->matched_range.rm_so; unsigned end = gl->matched_range.rm_eo; unsigned len = end - start; char old = line[end]; line[end] = '\0'; /* Empty match is not printed: try "echo test | grep -o ''" */ if (len != 0) print_line(line + start, len, linenum, ':'); if (old == '\0') break; line[end] = old; if (len == 0) end++; #if !ENABLE_EXTRA_COMPAT if (regexec(&gl->compiled_regex, line + end, 1, &gl->matched_range, REG_NOTBOL) != 0) break; gl->matched_range.rm_so += end; gl->matched_range.rm_eo += end; #else if (re_search(&gl->compiled_regex, line, line_len, end, line_len - end, &gl->matched_range) < 0) break; #endif } } else { print_line(line, line_len, linenum, ':'); } } } #if ENABLE_FEATURE_GREP_CONTEXT else { /* no match */ /* if we need to print some context lines after the last match, do so */ if (print_n_lines_after) { print_line(line, strlen(line), linenum, '-'); print_n_lines_after--; } else if (lines_before) { /* Add the line to the circular 'before' buffer */ free(before_buf[curpos]); before_buf[curpos] = line; IF_EXTRA_COMPAT(before_buf_size[curpos] = line_len;) curpos = (curpos + 1) % lines_before; /* avoid free(line) - we took the line */ line = NULL; } } #endif /* ENABLE_FEATURE_GREP_CONTEXT */ #if !ENABLE_EXTRA_COMPAT free(line); #endif /* Did we print all context after last requested match? */ if ((option_mask32 & OPT_m) && !print_n_lines_after && nmatches == max_matches ) { break; } } /* while (read line) */ /* special-case file post-processing for options where we don't print line * matches, just filenames and possibly match counts */ /* grep -c: print [filename:]count, even if count is zero */ if (PRINT_MATCH_COUNTS) { if (print_filename) printf("%s:", cur_file); printf("%d\n", nmatches); } /* grep -L: print just the filename */ if (PRINT_FILES_WITHOUT_MATCHES) { /* nmatches is zero, no need to check it: * we return 1 early if we detected a match * and PRINT_FILES_WITHOUT_MATCHES is set */ puts(cur_file); } return nmatches; } #if ENABLE_FEATURE_CLEAN_UP #define new_grep_list_data(p, m) add_grep_list_data(p, m) static char *add_grep_list_data(char *pattern, int flg_used_mem) #else #define new_grep_list_data(p, m) add_grep_list_data(p) static char *add_grep_list_data(char *pattern) #endif { grep_list_data_t *gl = xzalloc(sizeof(*gl)); gl->pattern = pattern; #if ENABLE_FEATURE_CLEAN_UP gl->flg_mem_alocated_compiled = flg_used_mem; #else /*gl->flg_mem_alocated_compiled = 0;*/ #endif return (char *)gl; } static void load_regexes_from_file(llist_t *fopt) { while (fopt) { char *line; FILE *fp; llist_t *cur = fopt; char *ffile = cur->data; fopt = cur->link; free(cur); fp = xfopen_stdin(ffile); while ((line = xmalloc_fgetline(fp)) != NULL) { llist_add_to(&pattern_head, new_grep_list_data(line, ALLOCATED)); } fclose_if_not_stdin(fp); } } static int FAST_FUNC file_action_grep(const char *filename, struct stat *statbuf UNUSED_PARAM, void* matched, int depth UNUSED_PARAM) { FILE *file = fopen_for_read(filename); if (file == NULL) { if (!SUPPRESS_ERR_MSGS) bb_simple_perror_msg(filename); open_errors = 1; return 0; } cur_file = filename; *(int*)matched += grep_file(file); fclose(file); return 1; } static int grep_dir(const char *dir) { int matched = 0; recursive_action(dir, /* recurse=yes */ ACTION_RECURSE | /* followLinks=no */ /* depthFirst=yes */ ACTION_DEPTHFIRST, /* fileAction= */ file_action_grep, /* dirAction= */ NULL, /* userData= */ &matched, /* depth= */ 0); return matched; } int grep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int grep_main(int argc UNUSED_PARAM, char **argv) { FILE *file; int matched; llist_t *fopt = NULL; /* do normal option parsing */ #if ENABLE_FEATURE_GREP_CONTEXT int Copt, opts; /* -H unsets -h; -C unsets -A,-B; -e,-f are lists; * -m,-A,-B,-C have numeric param */ opt_complementary = "H-h:C-AB:e::f::m+:A+:B+:C+"; opts = getopt32(argv, OPTSTR_GREP, &pattern_head, &fopt, &max_matches, &lines_after, &lines_before, &Copt); if (opts & OPT_C) { /* -C unsets prev -A and -B, but following -A or -B * may override it */ if (!(opts & OPT_A)) /* not overridden */ lines_after = Copt; if (!(opts & OPT_B)) /* not overridden */ lines_before = Copt; } /* sanity checks */ if (opts & (OPT_c|OPT_q|OPT_l|OPT_L)) { option_mask32 &= ~OPT_n; lines_before = 0; lines_after = 0; } else if (lines_before > 0) { if (lines_before > INT_MAX / sizeof(long long)) lines_before = INT_MAX / sizeof(long long); /* overflow in (lines_before * sizeof(x)) is prevented (above) */ before_buf = xzalloc(lines_before * sizeof(before_buf[0])); IF_EXTRA_COMPAT(before_buf_size = xzalloc(lines_before * sizeof(before_buf_size[0]));) } #else /* with auto sanity checks */ /* -H unsets -h; -c,-q or -l unset -n; -e,-f are lists; -m N */ opt_complementary = "H-h:c-n:q-n:l-n:e::f::m+"; getopt32(argv, OPTSTR_GREP, &pattern_head, &fopt, &max_matches); #endif invert_search = ((option_mask32 & OPT_v) != 0); /* 0 | 1 */ { /* convert char **argv to grep_list_data_t */ llist_t *cur; for (cur = pattern_head; cur; cur = cur->link) cur->data = new_grep_list_data(cur->data, 0); } if (option_mask32 & OPT_f) { load_regexes_from_file(fopt); if (!pattern_head) { /* -f EMPTY_FILE? */ /* GNU grep treats it as "nothing matches" */ llist_add_to(&pattern_head, new_grep_list_data((char*) "", 0)); invert_search ^= 1; } } if (ENABLE_FEATURE_GREP_FGREP_ALIAS && applet_name[0] == 'f') option_mask32 |= OPT_F; #if !ENABLE_EXTRA_COMPAT if (!(option_mask32 & (OPT_o | OPT_w | OPT_x))) reflags = REG_NOSUB; #endif if (ENABLE_FEATURE_GREP_EGREP_ALIAS && (applet_name[0] == 'e' || (option_mask32 & OPT_E)) ) { reflags |= REG_EXTENDED; } #if ENABLE_EXTRA_COMPAT else { reflags = RE_SYNTAX_GREP; } #endif if (option_mask32 & OPT_i) { #if !ENABLE_EXTRA_COMPAT reflags |= REG_ICASE; #else int i; case_fold = xmalloc(256); for (i = 0; i < 256; i++) case_fold[i] = (unsigned char)i; for (i = 'a'; i <= 'z'; i++) case_fold[i] = (unsigned char)(i - ('a' - 'A')); #endif } argv += optind; /* if we didn't get a pattern from -e and no command file was specified, * first parameter should be the pattern. no pattern, no worky */ if (pattern_head == NULL) { char *pattern; if (*argv == NULL) bb_show_usage(); pattern = new_grep_list_data(*argv++, 0); llist_add_to(&pattern_head, pattern); } /* argv[0..(argc-1)] should be names of file to grep through. If * there is more than one file to grep, we will print the filenames. */ if (argv[0] && argv[1]) print_filename = 1; /* -H / -h of course override */ if (option_mask32 & OPT_H) print_filename = 1; if (option_mask32 & OPT_h) print_filename = 0; /* If no files were specified, or '-' was specified, take input from * stdin. Otherwise, we grep through all the files specified. */ matched = 0; do { cur_file = *argv; file = stdin; if (!cur_file || LONE_DASH(cur_file)) { cur_file = "(standard input)"; } else { if (option_mask32 & OPT_r) { struct stat st; if (stat(cur_file, &st) == 0 && S_ISDIR(st.st_mode)) { if (!(option_mask32 & OPT_h)) print_filename = 1; matched += grep_dir(cur_file); goto grep_done; } } /* else: fopen(dir) will succeed, but reading won't */ file = fopen_for_read(cur_file); if (file == NULL) { if (!SUPPRESS_ERR_MSGS) bb_simple_perror_msg(cur_file); open_errors = 1; continue; } } matched += grep_file(file); fclose_if_not_stdin(file); grep_done: ; } while (*argv && *++argv); /* destroy all the elments in the pattern list */ if (ENABLE_FEATURE_CLEAN_UP) { while (pattern_head) { llist_t *pattern_head_ptr = pattern_head; grep_list_data_t *gl = (grep_list_data_t *)pattern_head_ptr->data; pattern_head = pattern_head->link; if (gl->flg_mem_alocated_compiled & ALLOCATED) free(gl->pattern); if (gl->flg_mem_alocated_compiled & COMPILED) regfree(&gl->compiled_regex); free(gl); free(pattern_head_ptr); } } /* 0 = success, 1 = failed, 2 = error */ if (open_errors) return 2; return !matched; /* invert return value: 0 = success, 1 = failed */ } busybox-1.22.1/e2fsprogs/0000755000000000000000000000000012320365362013702 5ustar rootrootbusybox-1.22.1/e2fsprogs/fsck.c0000644000000000000000000006052512263563520015006 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * fsck --- A generic, parallelizing front-end for the fsck program. * It will automatically try to run fsck programs in parallel if the * devices are on separate spindles. It is based on the same ideas as * the generic front end for fsck by David Engel and Fred van Kempen, * but it has been completely rewritten from scratch to support * parallel execution. * * Written by Theodore Ts'o, * * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994: * o Changed -t fstype to behave like with mount when -A (all file * systems) or -M (like mount) is specified. * o fsck looks if it can find the fsck.type program to decide * if it should ignore the fs type. This way more fsck programs * can be added without changing this front-end. * o -R flag skip root file system. * * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o. * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* All filesystem specific hooks have been removed. * If filesystem cannot be determined, we will execute * "fsck.auto". Currently this also happens if you specify * UUID=xxx or LABEL=xxx as an object to check. * Detection code for that is also probably has to be in fsck.auto. * * In other words, this is _really_ is just a driver program which * spawns actual fsck.something for each filesystem to check. * It doesn't guess filesystem types from on-disk format. */ //usage:#define fsck_trivial_usage //usage: "[-ANPRTV] [-C FD] [-t FSTYPE] [FS_OPTS] [BLOCKDEV]..." //usage:#define fsck_full_usage "\n\n" //usage: "Check and repair filesystems\n" //usage: "\n -A Walk /etc/fstab and check all filesystems" //usage: "\n -N Don't execute, just show what would be done" //usage: "\n -P With -A, check filesystems in parallel" //usage: "\n -R With -A, skip the root filesystem" //usage: "\n -T Don't show title on startup" //usage: "\n -V Verbose" //usage: "\n -C n Write status information to specified filedescriptor" //usage: "\n -t TYPE List of filesystem types to check" #include "libbb.h" /* "progress indicator" code is somewhat buggy and ext[23] specific. * We should be filesystem agnostic. IOW: there should be a well-defined * API for fsck.something, NOT ad-hoc hacks in generic fsck. */ #define DO_PROGRESS_INDICATOR 0 /* fsck 1.41.4 (27-Jan-2009) manpage says: * 0 - No errors * 1 - File system errors corrected * 2 - System should be rebooted * 4 - File system errors left uncorrected * 8 - Operational error * 16 - Usage or syntax error * 32 - Fsck canceled by user request * 128 - Shared library error */ #define EXIT_OK 0 #define EXIT_NONDESTRUCT 1 #define EXIT_DESTRUCT 2 #define EXIT_UNCORRECTED 4 #define EXIT_ERROR 8 #define EXIT_USAGE 16 #define FSCK_CANCELED 32 /* Aborted with a signal or ^C */ /* * Internal structure for mount table entries. */ struct fs_info { struct fs_info *next; char *device; char *mountpt; char *type; char *opts; int passno; int flags; }; #define FLAG_DONE 1 #define FLAG_PROGRESS 2 /* * Structure to allow exit codes to be stored */ struct fsck_instance { struct fsck_instance *next; int pid; int flags; #if DO_PROGRESS_INDICATOR time_t start_time; #endif char *prog; char *device; char *base_device; /* /dev/hda for /dev/hdaN etc */ }; static const char ignored_types[] ALIGN1 = "ignore\0" "iso9660\0" "nfs\0" "proc\0" "sw\0" "swap\0" "tmpfs\0" "devpts\0"; #if 0 static const char really_wanted[] ALIGN1 = "minix\0" "ext2\0" "ext3\0" "jfs\0" "reiserfs\0" "xiafs\0" "xfs\0"; #endif #define BASE_MD "/dev/md" static char **args; static int num_args; static int verbose; #define FS_TYPE_FLAG_NORMAL 0 #define FS_TYPE_FLAG_OPT 1 #define FS_TYPE_FLAG_NEGOPT 2 static char **fs_type_list; static uint8_t *fs_type_flag; static smallint fs_type_negated; static smallint noexecute; static smallint serialize; static smallint skip_root; /* static smallint like_mount; */ static smallint parallel_root; static smallint force_all_parallel; #if DO_PROGRESS_INDICATOR static smallint progress; static int progress_fd; #endif static int num_running; static int max_running; static char *fstype; static struct fs_info *filesys_info; static struct fs_info *filesys_last; static struct fsck_instance *instance_list; /* * Return the "base device" given a particular device; this is used to * assure that we only fsck one partition on a particular drive at any * one time. Otherwise, the disk heads will be seeking all over the * place. If the base device cannot be determined, return NULL. * * The base_device() function returns an allocated string which must * be freed. */ #if ENABLE_FEATURE_DEVFS /* * Required for the uber-silly devfs /dev/ide/host1/bus2/target3/lun3 * pathames. */ static const char *const devfs_hier[] = { "host", "bus", "target", "lun", NULL }; #endif static char *base_device(const char *device) { char *str, *cp; #if ENABLE_FEATURE_DEVFS const char *const *hier; const char *disk; int len; #endif str = xstrdup(device); /* Skip over "/dev/"; if it's not present, give up */ cp = skip_dev_pfx(str); if (cp == str) goto errout; /* * For md devices, we treat them all as if they were all * on one disk, since we don't know how to parallelize them. */ if (cp[0] == 'm' && cp[1] == 'd') { cp[2] = 0; return str; } /* Handle DAC 960 devices */ if (strncmp(cp, "rd/", 3) == 0) { cp += 3; if (cp[0] != 'c' || !isdigit(cp[1]) || cp[2] != 'd' || !isdigit(cp[3])) goto errout; cp[4] = 0; return str; } /* Now let's handle /dev/hd* and /dev/sd* devices.... */ if ((cp[0] == 'h' || cp[0] == 's') && cp[1] == 'd') { cp += 2; /* If there's a single number after /dev/hd, skip it */ if (isdigit(*cp)) cp++; /* What follows must be an alpha char, or give up */ if (!isalpha(*cp)) goto errout; cp[1] = 0; return str; } #if ENABLE_FEATURE_DEVFS /* Now let's handle devfs (ugh) names */ len = 0; if (strncmp(cp, "ide/", 4) == 0) len = 4; if (strncmp(cp, "scsi/", 5) == 0) len = 5; if (len) { cp += len; /* * Now we proceed down the expected devfs hierarchy. * i.e., .../host1/bus2/target3/lun4/... * If we don't find the expected token, followed by * some number of digits at each level, abort. */ for (hier = devfs_hier; *hier; hier++) { len = strlen(*hier); if (strncmp(cp, *hier, len) != 0) goto errout; cp += len; while (*cp != '/' && *cp != 0) { if (!isdigit(*cp)) goto errout; cp++; } cp++; } cp[-1] = 0; return str; } /* Now handle devfs /dev/disc or /dev/disk names */ disk = 0; if (strncmp(cp, "discs/", 6) == 0) disk = "disc"; else if (strncmp(cp, "disks/", 6) == 0) disk = "disk"; if (disk) { cp += 6; if (strncmp(cp, disk, 4) != 0) goto errout; cp += 4; while (*cp != '/' && *cp != 0) { if (!isdigit(*cp)) goto errout; cp++; } *cp = 0; return str; } #endif errout: free(str); return NULL; } static void free_instance(struct fsck_instance *p) { free(p->prog); free(p->device); free(p->base_device); free(p); } static struct fs_info *create_fs_device(const char *device, const char *mntpnt, const char *type, const char *opts, int passno) { struct fs_info *fs; fs = xzalloc(sizeof(*fs)); fs->device = xstrdup(device); fs->mountpt = xstrdup(mntpnt); if (strchr(type, ',')) type = (char *)"auto"; fs->type = xstrdup(type); fs->opts = xstrdup(opts ? opts : ""); fs->passno = passno < 0 ? 1 : passno; /*fs->flags = 0; */ /*fs->next = NULL; */ if (!filesys_info) filesys_info = fs; else filesys_last->next = fs; filesys_last = fs; return fs; } /* Load the filesystem database from /etc/fstab */ static void load_fs_info(const char *filename) { FILE *fstab; struct mntent mte; fstab = setmntent(filename, "r"); if (!fstab) { bb_perror_msg("can't read '%s'", filename); return; } // Loop through entries while (getmntent_r(fstab, &mte, bb_common_bufsiz1, COMMON_BUFSIZE)) { //bb_info_msg("CREATE[%s][%s][%s][%s][%d]", mte.mnt_fsname, mte.mnt_dir, // mte.mnt_type, mte.mnt_opts, // mte.mnt_passno); create_fs_device(mte.mnt_fsname, mte.mnt_dir, mte.mnt_type, mte.mnt_opts, mte.mnt_passno); } endmntent(fstab); } /* Lookup filesys in /etc/fstab and return the corresponding entry. */ static struct fs_info *lookup(char *filesys) { struct fs_info *fs; for (fs = filesys_info; fs; fs = fs->next) { if (strcmp(filesys, fs->device) == 0 || (fs->mountpt && strcmp(filesys, fs->mountpt) == 0) ) break; } return fs; } #if DO_PROGRESS_INDICATOR static int progress_active(void) { struct fsck_instance *inst; for (inst = instance_list; inst; inst = inst->next) { if (inst->flags & FLAG_DONE) continue; if (inst->flags & FLAG_PROGRESS) return 1; } return 0; } #endif /* * Send a signal to all outstanding fsck child processes */ static void kill_all_if_got_signal(void) { static smallint kill_sent; struct fsck_instance *inst; if (!bb_got_signal || kill_sent) return; for (inst = instance_list; inst; inst = inst->next) { if (inst->flags & FLAG_DONE) continue; kill(inst->pid, SIGTERM); } kill_sent = 1; } /* * Wait for one child process to exit; when it does, unlink it from * the list of executing child processes, free, and return its exit status. * If there is no exited child, return -1. */ static int wait_one(int flags) { int status; int sig; struct fsck_instance *inst, *prev; pid_t pid; if (!instance_list) return -1; /* if (noexecute) { already returned -1; } */ while (1) { pid = waitpid(-1, &status, flags); kill_all_if_got_signal(); if (pid == 0) /* flags == WNOHANG and no children exited */ return -1; if (pid < 0) { if (errno == EINTR) continue; if (errno == ECHILD) { /* paranoia */ bb_error_msg("wait: no more children"); return -1; } bb_perror_msg("wait"); continue; } prev = NULL; inst = instance_list; do { if (inst->pid == pid) goto child_died; prev = inst; inst = inst->next; } while (inst); } child_died: if (WIFEXITED(status)) status = WEXITSTATUS(status); else if (WIFSIGNALED(status)) { sig = WTERMSIG(status); status = EXIT_UNCORRECTED; if (sig != SIGINT) { printf("Warning: %s %s terminated " "by signal %d\n", inst->prog, inst->device, sig); status = EXIT_ERROR; } } else { printf("%s %s: status is %x, should never happen\n", inst->prog, inst->device, status); status = EXIT_ERROR; } #if DO_PROGRESS_INDICATOR if (progress && (inst->flags & FLAG_PROGRESS) && !progress_active()) { struct fsck_instance *inst2; for (inst2 = instance_list; inst2; inst2 = inst2->next) { if (inst2->flags & FLAG_DONE) continue; if (strcmp(inst2->type, "ext2") != 0 && strcmp(inst2->type, "ext3") != 0 ) { continue; } /* ext[23], we will send USR1 * (request to start displaying progress bar) * * If we've just started the fsck, wait a tiny * bit before sending the kill, to give it * time to set up the signal handler */ if (inst2->start_time >= time(NULL) - 1) sleep(1); kill(inst2->pid, SIGUSR1); inst2->flags |= FLAG_PROGRESS; break; } } #endif if (prev) prev->next = inst->next; else instance_list = inst->next; if (verbose > 1) printf("Finished with %s (exit status %d)\n", inst->device, status); num_running--; free_instance(inst); return status; } /* * Wait until all executing child processes have exited; return the * logical OR of all of their exit code values. */ #define FLAG_WAIT_ALL 0 #define FLAG_WAIT_ATLEAST_ONE WNOHANG static int wait_many(int flags) { int exit_status; int global_status = 0; int wait_flags = 0; while ((exit_status = wait_one(wait_flags)) != -1) { global_status |= exit_status; wait_flags |= flags; } return global_status; } /* * Execute a particular fsck program, and link it into the list of * child processes we are waiting for. */ static void execute(const char *type, const char *device, const char *mntpt /*, int interactive */) { int i; struct fsck_instance *inst; pid_t pid; args[0] = xasprintf("fsck.%s", type); #if DO_PROGRESS_INDICATOR if (progress && !progress_active()) { if (strcmp(type, "ext2") == 0 || strcmp(type, "ext3") == 0 ) { args[XXX] = xasprintf("-C%d", progress_fd); /* 1 */ inst->flags |= FLAG_PROGRESS; } } #endif args[num_args - 2] = (char*)device; /* args[num_args - 1] = NULL; - already is */ if (verbose || noexecute) { printf("[%s (%d) -- %s]", args[0], num_running, mntpt ? mntpt : device); for (i = 0; args[i]; i++) printf(" %s", args[i]); bb_putchar('\n'); } /* Fork and execute the correct program. */ pid = -1; if (!noexecute) { pid = spawn(args); if (pid < 0) bb_simple_perror_msg(args[0]); } #if DO_PROGRESS_INDICATOR free(args[XXX]); #endif /* No child, so don't record an instance */ if (pid <= 0) { free(args[0]); return; } inst = xzalloc(sizeof(*inst)); inst->pid = pid; inst->prog = args[0]; inst->device = xstrdup(device); inst->base_device = base_device(device); #if DO_PROGRESS_INDICATOR inst->start_time = time(NULL); #endif /* Add to the list of running fsck's. * (was adding to the end, but adding to the front is simpler...) */ inst->next = instance_list; instance_list = inst; } /* * Run the fsck program on a particular device * * If the type is specified using -t, and it isn't prefixed with "no" * (as in "noext2") and only one filesystem type is specified, then * use that type regardless of what is specified in /etc/fstab. * * If the type isn't specified by the user, then use either the type * specified in /etc/fstab, or "auto". */ static void fsck_device(struct fs_info *fs /*, int interactive */) { const char *type; if (strcmp(fs->type, "auto") != 0) { type = fs->type; if (verbose > 2) bb_info_msg("using filesystem type '%s' %s", type, "from fstab"); } else if (fstype && (fstype[0] != 'n' || fstype[1] != 'o') /* != "no" */ && strncmp(fstype, "opts=", 5) != 0 && strncmp(fstype, "loop", 4) != 0 && !strchr(fstype, ',') ) { type = fstype; if (verbose > 2) bb_info_msg("using filesystem type '%s' %s", type, "from -t"); } else { type = "auto"; if (verbose > 2) bb_info_msg("using filesystem type '%s' %s", type, "(default)"); } num_running++; execute(type, fs->device, fs->mountpt /*, interactive */); } /* * Returns TRUE if a partition on the same disk is already being * checked. */ static int device_already_active(char *device) { struct fsck_instance *inst; char *base; if (force_all_parallel) return 0; #ifdef BASE_MD /* Don't check a soft raid disk with any other disk */ if (instance_list && (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1) || !strncmp(device, BASE_MD, sizeof(BASE_MD)-1)) ) { return 1; } #endif base = base_device(device); /* * If we don't know the base device, assume that the device is * already active if there are any fsck instances running. */ if (!base) return (instance_list != NULL); for (inst = instance_list; inst; inst = inst->next) { if (!inst->base_device || !strcmp(base, inst->base_device)) { free(base); return 1; } } free(base); return 0; } /* * This function returns true if a particular option appears in a * comma-delimited options list */ static int opt_in_list(char *opt, char *optlist) { char *s; int len; if (!optlist) return 0; len = strlen(opt); s = optlist - 1; while (1) { s = strstr(s + 1, opt); if (!s) return 0; /* neither "opt.." nor "xxx,opt.."? */ if (s != optlist && s[-1] != ',') continue; /* neither "..opt" nor "..opt,xxx"? */ if (s[len] != '\0' && s[len] != ',') continue; return 1; } } /* See if the filesystem matches the criteria given by the -t option */ static int fs_match(struct fs_info *fs) { int n, ret, checked_type; char *cp; if (!fs_type_list) return 1; ret = 0; checked_type = 0; n = 0; while (1) { cp = fs_type_list[n]; if (!cp) break; switch (fs_type_flag[n]) { case FS_TYPE_FLAG_NORMAL: checked_type++; if (strcmp(cp, fs->type) == 0) ret = 1; break; case FS_TYPE_FLAG_NEGOPT: if (opt_in_list(cp, fs->opts)) return 0; break; case FS_TYPE_FLAG_OPT: if (!opt_in_list(cp, fs->opts)) return 0; break; } n++; } if (checked_type == 0) return 1; return (fs_type_negated ? !ret : ret); } /* Check if we should ignore this filesystem. */ static int ignore(struct fs_info *fs) { /* * If the pass number is 0, ignore it. */ if (fs->passno == 0) return 1; /* * If a specific fstype is specified, and it doesn't match, * ignore it. */ if (!fs_match(fs)) return 1; /* Are we ignoring this type? */ if (index_in_strings(ignored_types, fs->type) >= 0) return 1; /* We can and want to check this file system type. */ return 0; } /* Check all file systems, using the /etc/fstab table. */ static int check_all(void) { struct fs_info *fs; int status = EXIT_OK; smallint not_done_yet; smallint pass_done; int passno; if (verbose) puts("Checking all filesystems"); /* * Do an initial scan over the filesystem; mark filesystems * which should be ignored as done, and resolve any "auto" * filesystem types (done as a side-effect of calling ignore()). */ for (fs = filesys_info; fs; fs = fs->next) if (ignore(fs)) fs->flags |= FLAG_DONE; /* * Find and check the root filesystem. */ if (!parallel_root) { for (fs = filesys_info; fs; fs = fs->next) { if (LONE_CHAR(fs->mountpt, '/')) { if (!skip_root && !ignore(fs)) { fsck_device(fs /*, 1*/); status |= wait_many(FLAG_WAIT_ALL); if (status > EXIT_NONDESTRUCT) return status; } fs->flags |= FLAG_DONE; break; } } } /* * This is for the bone-headed user who has root * filesystem listed twice. * "Skip root" will skip _all_ root entries. */ if (skip_root) for (fs = filesys_info; fs; fs = fs->next) if (LONE_CHAR(fs->mountpt, '/')) fs->flags |= FLAG_DONE; not_done_yet = 1; passno = 1; while (not_done_yet) { not_done_yet = 0; pass_done = 1; for (fs = filesys_info; fs; fs = fs->next) { if (bb_got_signal) break; if (fs->flags & FLAG_DONE) continue; /* * If the filesystem's pass number is higher * than the current pass number, then we didn't * do it yet. */ if (fs->passno > passno) { not_done_yet = 1; continue; } /* * If a filesystem on a particular device has * already been spawned, then we need to defer * this to another pass. */ if (device_already_active(fs->device)) { pass_done = 0; continue; } /* * Spawn off the fsck process */ fsck_device(fs /*, serialize*/); fs->flags |= FLAG_DONE; /* * Only do one filesystem at a time, or if we * have a limit on the number of fsck's extant * at one time, apply that limit. */ if (serialize || (max_running && (num_running >= max_running)) ) { pass_done = 0; break; } } if (bb_got_signal) break; if (verbose > 1) printf("--waiting-- (pass %d)\n", passno); status |= wait_many(pass_done ? FLAG_WAIT_ALL : FLAG_WAIT_ATLEAST_ONE); if (pass_done) { if (verbose > 1) puts("----------------------------------"); passno++; } else not_done_yet = 1; } kill_all_if_got_signal(); status |= wait_many(FLAG_WAIT_ATLEAST_ONE); return status; } /* * Deal with the fsck -t argument. * Huh, for mount "-t novfat,nfs" means "neither vfat nor nfs"! * Why here we require "-t novfat,nonfs" ?? */ static void compile_fs_type(char *fs_type) { char *s; int num = 2; smallint negate; s = fs_type; while ((s = strchr(s, ','))) { num++; s++; } fs_type_list = xzalloc(num * sizeof(fs_type_list[0])); fs_type_flag = xzalloc(num * sizeof(fs_type_flag[0])); fs_type_negated = -1; /* not yet known is it negated or not */ num = 0; s = fs_type; while (1) { char *comma; negate = 0; if (s[0] == 'n' && s[1] == 'o') { /* "no.." */ s += 2; negate = 1; } else if (s[0] == '!') { s++; negate = 1; } if (strcmp(s, "loop") == 0) /* loop is really short-hand for opts=loop */ goto loop_special_case; if (strncmp(s, "opts=", 5) == 0) { s += 5; loop_special_case: fs_type_flag[num] = negate ? FS_TYPE_FLAG_NEGOPT : FS_TYPE_FLAG_OPT; } else { if (fs_type_negated == -1) fs_type_negated = negate; if (fs_type_negated != negate) bb_error_msg_and_die( "either all or none of the filesystem types passed to -t must be prefixed " "with 'no' or '!'"); } comma = strchr(s, ','); fs_type_list[num++] = comma ? xstrndup(s, comma-s) : xstrdup(s); if (!comma) break; s = comma + 1; } } static char **new_args(void) { args = xrealloc_vector(args, 2, num_args); return &args[num_args++]; } int fsck_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fsck_main(int argc UNUSED_PARAM, char **argv) { int i, status; /*int interactive;*/ struct fs_info *fs; const char *fstab; char *tmp; char **devices; int num_devices; smallint opts_for_fsck; smallint doall; smallint notitle; /* we want wait() to be interruptible */ signal_no_SA_RESTART_empty_mask(SIGINT, record_signo); signal_no_SA_RESTART_empty_mask(SIGTERM, record_signo); setbuf(stdout, NULL); opts_for_fsck = doall = notitle = 0; devices = NULL; num_devices = 0; new_args(); /* args[0] = NULL, will be replaced by fsck. */ /* instance_list = NULL; - in bss, so already zeroed */ while (*++argv) { int j; int optpos; char *options; char *arg = *argv; /* "/dev/blk" or "/path" or "UUID=xxx" or "LABEL=xxx" */ if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) { // FIXME: must check that arg is a blkdev, or resolve // "/path", "UUID=xxx" or "LABEL=xxx" into block device name // ("UUID=xxx"/"LABEL=xxx" can probably shifted to fsck.auto duties) devices = xrealloc_vector(devices, 2, num_devices); devices[num_devices++] = arg; continue; } if (arg[0] != '-' || opts_for_fsck) { *new_args() = arg; continue; } if (LONE_CHAR(arg + 1, '-')) { /* "--" ? */ opts_for_fsck = 1; continue; } optpos = 0; options = NULL; for (j = 1; arg[j]; j++) { switch (arg[j]) { case 'A': doall = 1; break; #if DO_PROGRESS_INDICATOR case 'C': progress = 1; if (arg[++j]) { /* -Cn */ progress_fd = xatoi_positive(&arg[j]); goto next_arg; } /* -C n */ if (!*++argv) bb_show_usage(); progress_fd = xatoi_positive(*argv); goto next_arg; #endif case 'V': verbose++; break; case 'N': noexecute = 1; break; case 'R': skip_root = 1; break; case 'T': notitle = 1; break; /* case 'M': like_mount = 1; break; */ case 'P': parallel_root = 1; break; case 's': serialize = 1; break; case 't': if (fstype) bb_show_usage(); if (arg[++j]) tmp = &arg[j]; else if (*++argv) tmp = *argv; else bb_show_usage(); fstype = xstrdup(tmp); compile_fs_type(fstype); goto next_arg; case '?': bb_show_usage(); break; default: optpos++; /* one extra for '\0' */ options = xrealloc(options, optpos + 2); options[optpos] = arg[j]; break; } } next_arg: if (optpos) { options[0] = '-'; options[optpos + 1] = '\0'; *new_args() = options; } } if (getenv("FSCK_FORCE_ALL_PARALLEL")) force_all_parallel = 1; tmp = getenv("FSCK_MAX_INST"); if (tmp) max_running = xatoi(tmp); new_args(); /* args[num_args - 2] will be replaced by */ new_args(); /* args[num_args - 1] is the last, NULL element */ if (!notitle) puts("fsck (busybox "BB_VER", "BB_BT")"); /* Even plain "fsck /dev/hda1" needs fstab to get fs type, * so we are scanning it anyway */ fstab = getenv("FSTAB_FILE"); if (!fstab) fstab = "/etc/fstab"; load_fs_info(fstab); /*interactive = (num_devices == 1) | serialize;*/ if (num_devices == 0) /*interactive =*/ serialize = doall = 1; if (doall) return check_all(); status = 0; for (i = 0; i < num_devices; i++) { if (bb_got_signal) { kill_all_if_got_signal(); break; } fs = lookup(devices[i]); if (!fs) fs = create_fs_device(devices[i], "", "auto", NULL, -1); fsck_device(fs /*, interactive */); if (serialize || (max_running && (num_running >= max_running)) ) { int exit_status = wait_one(0); if (exit_status >= 0) status |= exit_status; if (verbose > 1) puts("----------------------------------"); } } status |= wait_many(FLAG_WAIT_ALL); return status; } busybox-1.22.1/e2fsprogs/tune2fs.c0000644000000000000000000000530212263563520015436 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * tune2fs: utility to modify EXT2 filesystem * * Busybox'ed (2009) by Vladimir Dronnikov * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" #include #include "bb_e2fs_defs.h" // storage helpers char BUG_wrong_field_size(void); #define STORE_LE(field, value) \ do { \ if (sizeof(field) == 4) \ field = SWAP_LE32(value); \ else if (sizeof(field) == 2) \ field = SWAP_LE16(value); \ else if (sizeof(field) == 1) \ field = (value); \ else \ BUG_wrong_field_size(); \ } while (0) #define FETCH_LE32(field) \ (sizeof(field) == 4 ? SWAP_LE32(field) : BUG_wrong_field_size()) //usage:#define tune2fs_trivial_usage //usage: "[-c MAX_MOUNT_COUNT] " ////usage: "[-e errors-behavior] [-g group] " //usage: "[-i DAYS] " ////usage: "[-j] [-J journal-options] [-l] [-s sparse-flag] " ////usage: "[-m reserved-blocks-percent] [-o [^]mount-options[,...]] " ////usage: "[-r reserved-blocks-count] [-u user] " //usage: "[-C MOUNT_COUNT] " //usage: "[-L LABEL] " ////usage: "[-M last-mounted-dir] [-O [^]feature[,...]] " ////usage: "[-T last-check-time] [-U UUID] " //usage: "BLOCKDEV" //usage: //usage:#define tune2fs_full_usage "\n\n" //usage: "Adjust filesystem options on ext[23] filesystems" enum { OPT_L = 1 << 0, // label OPT_c = 1 << 1, // max mount count OPT_i = 1 << 2, // check interval OPT_C = 1 << 3, // current mount count }; int tune2fs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int tune2fs_main(int argc UNUSED_PARAM, char **argv) { unsigned opts; const char *label, *str_c, *str_i, *str_C; struct ext2_super_block *sb; int fd; opt_complementary = "=1"; opts = getopt32(argv, "L:c:i:C:", &label, &str_c, &str_i, &str_C); if (!opts) bb_show_usage(); argv += optind; // argv[0] -- device // read superblock fd = xopen(argv[0], O_RDWR); xlseek(fd, 1024, SEEK_SET); sb = xzalloc(1024); xread(fd, sb, 1024); // mangle superblock //STORE_LE(sb->s_wtime, time(NULL)); - why bother? if (opts & OPT_C) { int n = xatoi_range(str_C, 1, 0xfffe); STORE_LE(sb->s_mnt_count, (unsigned)n); } // set the label if (opts & OPT_L) safe_strncpy((char *)sb->s_volume_name, label, sizeof(sb->s_volume_name)); if (opts & OPT_c) { int n = xatoi_range(str_c, -1, 0xfffe); if (n == 0) n = -1; STORE_LE(sb->s_max_mnt_count, (unsigned)n); } if (opts & OPT_i) { unsigned n = xatou_range(str_i, 0, (unsigned)0xffffffff / (24*60*60)) * 24*60*60; STORE_LE(sb->s_checkinterval, n); } // write superblock xlseek(fd, 1024, SEEK_SET); xwrite(fd, sb, 1024); if (ENABLE_FEATURE_CLEAN_UP) { free(sb); } xclose(fd); return EXIT_SUCCESS; } busybox-1.22.1/e2fsprogs/README0000644000000000000000000000066212263563520014570 0ustar rootrootAuthors and contributors of original e2fsprogs: Remy Card Theodore Ts'o Stephen C. Tweedie Andreas Gruenbacher, Kaz Kylheku F.W. ten Wolde Jeremy Fitzhardinge M.J.E. Mol Miquel van Smoorenburg Uwe Ohse busybox-1.22.1/e2fsprogs/Config.src0000644000000000000000000000346012263563520015625 0ustar rootroot# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Linux Ext2 FS Progs" INSERT config CHATTR bool "chattr" default y help chattr changes the file attributes on a second extended file system. ### config E2FSCK ### bool "e2fsck" ### default y ### help ### e2fsck is used to check Linux second extended file systems (ext2fs). ### e2fsck also supports ext2 filesystems countaining a journal (ext3). ### The normal compat symlinks 'fsck.ext2' and 'fsck.ext3' are also ### provided. config FSCK bool "fsck" default y help fsck is used to check and optionally repair one or more filesystems. In actuality, fsck is simply a front-end for the various file system checkers (fsck.fstype) available under Linux. config LSATTR bool "lsattr" default y select PLATFORM_LINUX help lsattr lists the file attributes on a second extended file system. ### config MKE2FS ### bool "mke2fs" ### default y ### help ### mke2fs is used to create an ext2/ext3 filesystem. The normal compat ### symlinks 'mkfs.ext2' and 'mkfs.ext3' are also provided. config TUNE2FS bool "tune2fs" default n # off: it is too limited compared to upstream version help tune2fs allows the system administrator to adjust various tunable filesystem parameters on Linux ext2/ext3 filesystems. ### config E2LABEL ### bool "e2label" ### default y ### depends on TUNE2FS ### help ### e2label will display or change the filesystem label on the ext2 ### filesystem located on device. ### NB: this one is now provided by util-linux/volume_id/* ### config FINDFS ### bool "findfs" ### default y ### depends on TUNE2FS ### help ### findfs will search the disks in the system looking for a filesystem ### which has a label matching label or a UUID equal to uuid. endmenu busybox-1.22.1/e2fsprogs/Kbuild.src0000644000000000000000000000051212263563520015625 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT lib-$(CONFIG_CHATTR) += chattr.o e2fs_lib.o lib-$(CONFIG_LSATTR) += lsattr.o e2fs_lib.o lib-$(CONFIG_FSCK) += fsck.o lib-$(CONFIG_TUNE2FS) += tune2fs.o busybox-1.22.1/e2fsprogs/old_e2fsprogs/0000755000000000000000000000000012320365362016452 5ustar rootrootbusybox-1.22.1/e2fsprogs/old_e2fsprogs/fsck.c0000644000000000000000000006730512263563520017561 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * pfsck --- A generic, parallelizing front-end for the fsck program. * It will automatically try to run fsck programs in parallel if the * devices are on separate spindles. It is based on the same ideas as * the generic front end for fsck by David Engel and Fred van Kempen, * but it has been completely rewritten from scratch to support * parallel execution. * * Written by Theodore Ts'o, * * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994: * o Changed -t fstype to behave like with mount when -A (all file * systems) or -M (like mount) is specified. * o fsck looks if it can find the fsck.type program to decide * if it should ignore the fs type. This way more fsck programs * can be added without changing this front-end. * o -R flag skip root file system. * * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o. * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fsck.h" #include "blkid/blkid.h" #include "e2fsbb.h" #include "libbb.h" #ifndef _PATH_MNTTAB #define _PATH_MNTTAB "/etc/fstab" #endif /* * fsck.h */ #ifndef DEFAULT_FSTYPE #define DEFAULT_FSTYPE "ext2" #endif #define MAX_DEVICES 32 #define MAX_ARGS 32 /* * Internal structure for mount tabel entries. */ struct fs_info { char *device; char *mountpt; char *type; char *opts; int freq; int passno; int flags; struct fs_info *next; }; #define FLAG_DONE 1 #define FLAG_PROGRESS 2 /* * Structure to allow exit codes to be stored */ struct fsck_instance { int pid; int flags; int exit_status; time_t start_time; char * prog; char * type; char * device; char * base_device; struct fsck_instance *next; }; /* * base_device.c * * Return the "base device" given a particular device; this is used to * assure that we only fsck one partition on a particular drive at any * one time. Otherwise, the disk heads will be seeking all over the * place. If the base device cannot be determined, return NULL. * * The base_device() function returns an allocated string which must * be freed. * */ #ifdef CONFIG_FEATURE_DEVFS /* * Required for the uber-silly devfs /dev/ide/host1/bus2/target3/lun3 * pathames. */ static const char *const devfs_hier[] = { "host", "bus", "target", "lun", 0 }; #endif static char *base_device(const char *device) { char *str, *cp; #ifdef CONFIG_FEATURE_DEVFS const char *const *hier; const char *disk; int len; #endif cp = str = xstrdup(device); /* Skip over /dev/; if it's not present, give up. */ if (strncmp(cp, "/dev/", 5) != 0) goto errout; cp += 5; /* * For md devices, we treat them all as if they were all * on one disk, since we don't know how to parallelize them. */ if (cp[0] == 'm' && cp[1] == 'd') { *(cp+2) = 0; return str; } /* Handle DAC 960 devices */ if (strncmp(cp, "rd/", 3) == 0) { cp += 3; if (cp[0] != 'c' || cp[2] != 'd' || !isdigit(cp[1]) || !isdigit(cp[3])) goto errout; *(cp+4) = 0; return str; } /* Now let's handle /dev/hd* and /dev/sd* devices.... */ if ((cp[0] == 'h' || cp[0] == 's') && (cp[1] == 'd')) { cp += 2; /* If there's a single number after /dev/hd, skip it */ if (isdigit(*cp)) cp++; /* What follows must be an alpha char, or give up */ if (!isalpha(*cp)) goto errout; *(cp + 1) = 0; return str; } #ifdef CONFIG_FEATURE_DEVFS /* Now let's handle devfs (ugh) names */ len = 0; if (strncmp(cp, "ide/", 4) == 0) len = 4; if (strncmp(cp, "scsi/", 5) == 0) len = 5; if (len) { cp += len; /* * Now we proceed down the expected devfs hierarchy. * i.e., .../host1/bus2/target3/lun4/... * If we don't find the expected token, followed by * some number of digits at each level, abort. */ for (hier = devfs_hier; *hier; hier++) { len = strlen(*hier); if (strncmp(cp, *hier, len) != 0) goto errout; cp += len; while (*cp != '/' && *cp != 0) { if (!isdigit(*cp)) goto errout; cp++; } cp++; } *(cp - 1) = 0; return str; } /* Now handle devfs /dev/disc or /dev/disk names */ disk = 0; if (strncmp(cp, "discs/", 6) == 0) disk = "disc"; else if (strncmp(cp, "disks/", 6) == 0) disk = "disk"; if (disk) { cp += 6; if (strncmp(cp, disk, 4) != 0) goto errout; cp += 4; while (*cp != '/' && *cp != 0) { if (!isdigit(*cp)) goto errout; cp++; } *cp = 0; return str; } #endif errout: free(str); return NULL; } static const char *const ignored_types[] = { "ignore", "iso9660", "nfs", "proc", "sw", "swap", "tmpfs", "devpts", NULL }; static const char *const really_wanted[] = { "minix", "ext2", "ext3", "jfs", "reiserfs", "xiafs", "xfs", NULL }; #define BASE_MD "/dev/md" /* * Global variables for options */ static char *devices[MAX_DEVICES]; static char *args[MAX_ARGS]; static int num_devices, num_args; static int verbose; static int doall; static int noexecute; static int serialize; static int skip_root; static int like_mount; static int notitle; static int parallel_root; static int progress; static int progress_fd; static int force_all_parallel; static int num_running; static int max_running; static volatile int cancel_requested; static int kill_sent; static char *fstype; static struct fs_info *filesys_info, *filesys_last; static struct fsck_instance *instance_list; static char *fsck_path; static blkid_cache cache; static char *string_copy(const char *s) { char *ret; if (!s) return 0; ret = xstrdup(s); return ret; } static int string_to_int(const char *s) { long l; char *p; l = strtol(s, &p, 0); if (*p || l == LONG_MIN || l == LONG_MAX || l < 0 || l > INT_MAX) return -1; else return (int) l; } static char *skip_over_blank(char *cp) { while (*cp && isspace(*cp)) cp++; return cp; } static char *skip_over_word(char *cp) { while (*cp && !isspace(*cp)) cp++; return cp; } static void strip_line(char *line) { char *p; while (*line) { p = line + strlen(line) - 1; if ((*p == '\n') || (*p == '\r')) *p = 0; else break; } } static char *parse_word(char **buf) { char *word, *next; word = *buf; if (*word == 0) return 0; word = skip_over_blank(word); next = skip_over_word(word); if (*next) *next++ = 0; *buf = next; return word; } static void parse_escape(char *word) { char *q, c; const char *p; if (!word) return; strcpy_and_process_escape_sequences(word, word); } static void free_instance(struct fsck_instance *i) { if (i->prog) free(i->prog); if (i->device) free(i->device); if (i->base_device) free(i->base_device); free(i); } static struct fs_info *create_fs_device(const char *device, const char *mntpnt, const char *type, const char *opts, int freq, int passno) { struct fs_info *fs; fs = xmalloc(sizeof(struct fs_info)); fs->device = string_copy(device); fs->mountpt = string_copy(mntpnt); fs->type = string_copy(type); fs->opts = string_copy(opts ? opts : ""); fs->freq = freq; fs->passno = passno; fs->flags = 0; fs->next = NULL; if (!filesys_info) filesys_info = fs; else filesys_last->next = fs; filesys_last = fs; return fs; } static int parse_fstab_line(char *line, struct fs_info **ret_fs) { char *dev, *device, *mntpnt, *type, *opts, *freq, *passno, *cp; struct fs_info *fs; *ret_fs = 0; strip_line(line); if ((cp = strchr(line, '#'))) *cp = 0; /* Ignore everything after the comment char */ cp = line; device = parse_word(&cp); mntpnt = parse_word(&cp); type = parse_word(&cp); opts = parse_word(&cp); freq = parse_word(&cp); passno = parse_word(&cp); if (!device) return 0; /* Allow blank lines */ if (!mntpnt || !type) return -1; parse_escape(device); parse_escape(mntpnt); parse_escape(type); parse_escape(opts); parse_escape(freq); parse_escape(passno); dev = blkid_get_devname(cache, device, NULL); if (dev) device = dev; if (strchr(type, ',')) type = 0; fs = create_fs_device(device, mntpnt, type ? type : "auto", opts, freq ? atoi(freq) : -1, passno ? atoi(passno) : -1); if (dev) free(dev); if (!fs) return -1; *ret_fs = fs; return 0; } static void interpret_type(struct fs_info *fs) { char *t; if (strcmp(fs->type, "auto") != 0) return; t = blkid_get_tag_value(cache, "TYPE", fs->device); if (t) { free(fs->type); fs->type = t; } } /* * Load the filesystem database from /etc/fstab */ static void load_fs_info(const char *filename) { FILE *f; char buf[1024]; int lineno = 0; int old_fstab = 1; struct fs_info *fs; if ((f = fopen_or_warn(filename, "r")) == NULL) { return; } while (!feof(f)) { lineno++; if (!fgets(buf, sizeof(buf), f)) break; buf[sizeof(buf)-1] = 0; if (parse_fstab_line(buf, &fs) < 0) { bb_error_msg("WARNING: bad format " "on line %d of %s\n", lineno, filename); continue; } if (!fs) continue; if (fs->passno < 0) fs->passno = 0; else old_fstab = 0; } fclose(f); if (old_fstab) { fputs("\007\007\007" "WARNING: Your /etc/fstab does not contain the fsck passno\n" " field. I will kludge around things for you, but you\n" " should fix your /etc/fstab file as soon as you can.\n\n", stderr); for (fs = filesys_info; fs; fs = fs->next) { fs->passno = 1; } } } /* Lookup filesys in /etc/fstab and return the corresponding entry. */ static struct fs_info *lookup(char *filesys) { struct fs_info *fs; /* No filesys name given. */ if (filesys == NULL) return NULL; for (fs = filesys_info; fs; fs = fs->next) { if (!strcmp(filesys, fs->device) || (fs->mountpt && !strcmp(filesys, fs->mountpt))) break; } return fs; } /* Find fsck program for a given fs type. */ static char *find_fsck(char *type) { char *s; const char *tpl; char *p = string_copy(fsck_path); struct stat st; /* Are we looking for a program or just a type? */ tpl = (strncmp(type, "fsck.", 5) ? "%s/fsck.%s" : "%s/%s"); for (s = strtok(p, ":"); s; s = strtok(NULL, ":")) { s = xasprintf(tpl, s, type); if (stat(s, &st) == 0) break; free(s); } free(p); return s; } static int progress_active(void) { struct fsck_instance *inst; for (inst = instance_list; inst; inst = inst->next) { if (inst->flags & FLAG_DONE) continue; if (inst->flags & FLAG_PROGRESS) return 1; } return 0; } /* * Execute a particular fsck program, and link it into the list of * child processes we are waiting for. */ static int execute(const char *type, const char *device, const char *mntpt, int interactive) { char *s, *argv[80]; char *prog; int argc, i; struct fsck_instance *inst, *p; pid_t pid; inst = xzalloc(sizeof(struct fsck_instance)); prog = xasprintf("fsck.%s", type); argv[0] = prog; argc = 1; for (i=0; i flags |= FLAG_PROGRESS; } } argv[argc++] = string_copy(device); argv[argc] = 0; s = find_fsck(prog); if (s == NULL) { bb_error_msg("%s: not found", prog); return ENOENT; } if (verbose || noexecute) { printf("[%s (%d) -- %s] ", s, num_running, mntpt ? mntpt : device); for (i=0; i < argc; i++) printf("%s ", argv[i]); bb_putchar('\n'); } /* Fork and execute the correct program. */ if (noexecute) pid = -1; else if ((pid = fork()) < 0) { perror("vfork"+1); return errno; } else if (pid == 0) { if (!interactive) close(0); (void) execv(s, argv); bb_simple_perror_msg_and_die(argv[0]); } for (i = 1; i < argc; i++) free(argv[i]); free(s); inst->pid = pid; inst->prog = prog; inst->type = string_copy(type); inst->device = string_copy(device); inst->base_device = base_device(device); inst->start_time = time(0); inst->next = NULL; /* * Find the end of the list, so we add the instance on at the end. */ for (p = instance_list; p && p->next; p = p->next); if (p) p->next = inst; else instance_list = inst; return 0; } /* * Send a signal to all outstanding fsck child processes */ static int kill_all(int signum) { struct fsck_instance *inst; int n = 0; for (inst = instance_list; inst; inst = inst->next) { if (inst->flags & FLAG_DONE) continue; kill(inst->pid, signum); n++; } return n; } /* * Wait for one child process to exit; when it does, unlink it from * the list of executing child processes, and return it. */ static struct fsck_instance *wait_one(int flags) { int status; int sig; struct fsck_instance *inst, *inst2, *prev; pid_t pid; if (!instance_list) return NULL; if (noexecute) { inst = instance_list; prev = 0; #ifdef RANDOM_DEBUG while (inst->next && (random() & 1)) { prev = inst; inst = inst->next; } #endif inst->exit_status = 0; goto ret_inst; } /* * gcc -Wall fails saving throw against stupidity * (inst and prev are thought to be uninitialized variables) */ inst = prev = NULL; do { pid = waitpid(-1, &status, flags); if (cancel_requested && !kill_sent) { kill_all(SIGTERM); kill_sent++; } if ((pid == 0) && (flags & WNOHANG)) return NULL; if (pid < 0) { if ((errno == EINTR) || (errno == EAGAIN)) continue; if (errno == ECHILD) { bb_error_msg("wait: no more child process?!?"); return NULL; } perror("wait"); continue; } for (prev = 0, inst = instance_list; inst; prev = inst, inst = inst->next) { if (inst->pid == pid) break; } } while (!inst); if (WIFEXITED(status)) status = WEXITSTATUS(status); else if (WIFSIGNALED(status)) { sig = WTERMSIG(status); if (sig == SIGINT) { status = EXIT_UNCORRECTED; } else { printf("Warning... %s for device %s exited " "with signal %d.\n", inst->prog, inst->device, sig); status = EXIT_ERROR; } } else { printf("%s %s: status is %x, should never happen.\n", inst->prog, inst->device, status); status = EXIT_ERROR; } inst->exit_status = status; if (progress && (inst->flags & FLAG_PROGRESS) && !progress_active()) { for (inst2 = instance_list; inst2; inst2 = inst2->next) { if (inst2->flags & FLAG_DONE) continue; if (strcmp(inst2->type, "ext2") && strcmp(inst2->type, "ext3")) continue; /* * If we've just started the fsck, wait a tiny * bit before sending the kill, to give it * time to set up the signal handler */ if (inst2->start_time < time(0)+2) { if (fork() == 0) { sleep(1); kill(inst2->pid, SIGUSR1); exit(0); } } else kill(inst2->pid, SIGUSR1); inst2->flags |= FLAG_PROGRESS; break; } } ret_inst: if (prev) prev->next = inst->next; else instance_list = inst->next; if (verbose > 1) printf("Finished with %s (exit status %d)\n", inst->device, inst->exit_status); num_running--; return inst; } #define FLAG_WAIT_ALL 0 #define FLAG_WAIT_ATLEAST_ONE 1 /* * Wait until all executing child processes have exited; return the * logical OR of all of their exit code values. */ static int wait_many(int flags) { struct fsck_instance *inst; int global_status = 0; int wait_flags = 0; while ((inst = wait_one(wait_flags))) { global_status |= inst->exit_status; free_instance(inst); #ifdef RANDOM_DEBUG if (noexecute && (flags & WNOHANG) && !(random() % 3)) break; #endif if (flags & FLAG_WAIT_ATLEAST_ONE) wait_flags = WNOHANG; } return global_status; } /* * Run the fsck program on a particular device * * If the type is specified using -t, and it isn't prefixed with "no" * (as in "noext2") and only one filesystem type is specified, then * use that type regardless of what is specified in /etc/fstab. * * If the type isn't specified by the user, then use either the type * specified in /etc/fstab, or DEFAULT_FSTYPE. */ static void fsck_device(struct fs_info *fs, int interactive) { const char *type; int retval; interpret_type(fs); if (strcmp(fs->type, "auto") != 0) type = fs->type; else if (fstype && strncmp(fstype, "no", 2) && strncmp(fstype, "opts=", 5) && strncmp(fstype, "loop", 4) && !strchr(fstype, ',')) type = fstype; else type = DEFAULT_FSTYPE; num_running++; retval = execute(type, fs->device, fs->mountpt, interactive); if (retval) { bb_error_msg("error %d while executing fsck.%s for %s", retval, type, fs->device); num_running--; } } /* * Deal with the fsck -t argument. */ struct fs_type_compile { char **list; int *type; int negate; } fs_type_compiled; #define FS_TYPE_NORMAL 0 #define FS_TYPE_OPT 1 #define FS_TYPE_NEGOPT 2 static const char fs_type_syntax_error[] = "Either all or none of the filesystem types passed to -t must be prefixed\n" "with 'no' or '!'."; static void compile_fs_type(char *fs_type, struct fs_type_compile *cmp) { char *cp, *list, *s; int num = 2; int negate, first_negate = 1; if (fs_type) { for (cp=fs_type; *cp; cp++) { if (*cp == ',') num++; } } cmp->list = xzalloc(num * sizeof(char *)); cmp->type = xzalloc(num * sizeof(int)); cmp->negate = 0; if (!fs_type) return; list = string_copy(fs_type); num = 0; s = strtok(list, ","); while (s) { negate = 0; if (strncmp(s, "no", 2) == 0) { s += 2; negate = 1; } else if (*s == '!') { s++; negate = 1; } if (strcmp(s, "loop") == 0) /* loop is really short-hand for opts=loop */ goto loop_special_case; else if (strncmp(s, "opts=", 5) == 0) { s += 5; loop_special_case: cmp->type[num] = negate ? FS_TYPE_NEGOPT : FS_TYPE_OPT; } else { if (first_negate) { cmp->negate = negate; first_negate = 0; } if ((negate && !cmp->negate) || (!negate && cmp->negate)) { bb_error_msg_and_die("%s", fs_type_syntax_error); } } cmp->list[num++] = string_copy(s); s = strtok(NULL, ","); } free(list); } /* * This function returns true if a particular option appears in a * comma-delimited options list */ static int opt_in_list(char *opt, char *optlist) { char *list, *s; if (!optlist) return 0; list = string_copy(optlist); s = strtok(list, ","); while (s) { if (strcmp(s, opt) == 0) { free(list); return 1; } s = strtok(NULL, ","); } free(list); return 0; } /* See if the filesystem matches the criteria given by the -t option */ static int fs_match(struct fs_info *fs, struct fs_type_compile *cmp) { int n, ret = 0, checked_type = 0; char *cp; if (cmp->list == 0 || cmp->list[0] == 0) return 1; for (n=0; (cp = cmp->list[n]); n++) { switch (cmp->type[n]) { case FS_TYPE_NORMAL: checked_type++; if (strcmp(cp, fs->type) == 0) { ret = 1; } break; case FS_TYPE_NEGOPT: if (opt_in_list(cp, fs->opts)) return 0; break; case FS_TYPE_OPT: if (!opt_in_list(cp, fs->opts)) return 0; break; } } if (checked_type == 0) return 1; return (cmp->negate ? !ret : ret); } /* Check if we should ignore this filesystem. */ static int ignore(struct fs_info *fs) { int wanted; char *s; /* * If the pass number is 0, ignore it. */ if (fs->passno == 0) return 1; interpret_type(fs); /* * If a specific fstype is specified, and it doesn't match, * ignore it. */ if (!fs_match(fs, &fs_type_compiled)) return 1; /* Are we ignoring this type? */ if (index_in_str_array(ignored_types, fs->type) >= 0) return 1; /* Do we really really want to check this fs? */ wanted = index_in_str_array(really_wanted, fs->type) >= 0; /* See if the program is available. */ s = find_fsck(fs->type); if (s == NULL) { if (wanted) bb_error_msg("can't check %s: fsck.%s not found", fs->device, fs->type); return 1; } free(s); /* We can and want to check this file system type. */ return 0; } /* * Returns TRUE if a partition on the same disk is already being * checked. */ static int device_already_active(char *device) { struct fsck_instance *inst; char *base; if (force_all_parallel) return 0; #ifdef BASE_MD /* Don't check a soft raid disk with any other disk */ if (instance_list && (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1) || !strncmp(device, BASE_MD, sizeof(BASE_MD)-1))) return 1; #endif base = base_device(device); /* * If we don't know the base device, assume that the device is * already active if there are any fsck instances running. */ if (!base) return (instance_list != 0); for (inst = instance_list; inst; inst = inst->next) { if (!inst->base_device || !strcmp(base, inst->base_device)) { free(base); return 1; } } free(base); return 0; } /* Check all file systems, using the /etc/fstab table. */ static int check_all(void) { struct fs_info *fs = NULL; int status = EXIT_OK; int not_done_yet = 1; int passno = 1; int pass_done; if (verbose) fputs("Checking all file systems.\n", stdout); /* * Do an initial scan over the filesystem; mark filesystems * which should be ignored as done, and resolve any "auto" * filesystem types (done as a side-effect of calling ignore()). */ for (fs = filesys_info; fs; fs = fs->next) { if (ignore(fs)) fs->flags |= FLAG_DONE; } /* * Find and check the root filesystem. */ if (!parallel_root) { for (fs = filesys_info; fs; fs = fs->next) { if (LONE_CHAR(fs->mountpt, '/')) break; } if (fs) { if (!skip_root && !ignore(fs)) { fsck_device(fs, 1); status |= wait_many(FLAG_WAIT_ALL); if (status > EXIT_NONDESTRUCT) return status; } fs->flags |= FLAG_DONE; } } /* * This is for the bone-headed user who enters the root * filesystem twice. Skip root will skep all root entries. */ if (skip_root) for (fs = filesys_info; fs; fs = fs->next) if (LONE_CHAR(fs->mountpt, '/')) fs->flags |= FLAG_DONE; while (not_done_yet) { not_done_yet = 0; pass_done = 1; for (fs = filesys_info; fs; fs = fs->next) { if (cancel_requested) break; if (fs->flags & FLAG_DONE) continue; /* * If the filesystem's pass number is higher * than the current pass number, then we don't * do it yet. */ if (fs->passno > passno) { not_done_yet++; continue; } /* * If a filesystem on a particular device has * already been spawned, then we need to defer * this to another pass. */ if (device_already_active(fs->device)) { pass_done = 0; continue; } /* * Spawn off the fsck process */ fsck_device(fs, serialize); fs->flags |= FLAG_DONE; /* * Only do one filesystem at a time, or if we * have a limit on the number of fsck's extant * at one time, apply that limit. */ if (serialize || (max_running && (num_running >= max_running))) { pass_done = 0; break; } } if (cancel_requested) break; if (verbose > 1) printf("--waiting-- (pass %d)\n", passno); status |= wait_many(pass_done ? FLAG_WAIT_ALL : FLAG_WAIT_ATLEAST_ONE); if (pass_done) { if (verbose > 1) printf("----------------------------------\n"); passno++; } else not_done_yet++; } if (cancel_requested && !kill_sent) { kill_all(SIGTERM); kill_sent++; } status |= wait_many(FLAG_WAIT_ATLEAST_ONE); return status; } static void signal_cancel(int sig FSCK_ATTR((unused))) { cancel_requested++; } static void PRS(int argc, char **argv) { int i, j; char *arg, *dev, *tmp = NULL; char options[128]; int opt = 0; int opts_for_fsck = 0; struct sigaction sa; /* * Set up signal action */ memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = signal_cancel; sigaction(SIGINT, &sa, 0); sigaction(SIGTERM, &sa, 0); num_devices = 0; num_args = 0; instance_list = 0; for (i=1; i < argc; i++) { arg = argv[i]; if (!arg) continue; if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) { if (num_devices >= MAX_DEVICES) { bb_error_msg_and_die("too many devices"); } dev = blkid_get_devname(cache, arg, NULL); if (!dev && strchr(arg, '=')) { /* * Check to see if we failed because * /proc/partitions isn't found. */ if (access("/proc/partitions", R_OK) < 0) { bb_perror_msg_and_die("can't open /proc/partitions " "(is /proc mounted?)"); } /* * Check to see if this is because * we're not running as root */ if (geteuid()) bb_error_msg_and_die( "must be root to scan for matching filesystems: %s\n", arg); else bb_error_msg_and_die( "can't find matching filesystem: %s", arg); } devices[num_devices++] = dev ? dev : string_copy(arg); continue; } if (arg[0] != '-' || opts_for_fsck) { if (num_args >= MAX_ARGS) { bb_error_msg_and_die("too many arguments"); } args[num_args++] = string_copy(arg); continue; } for (j=1; arg[j]; j++) { if (opts_for_fsck) { options[++opt] = arg[j]; continue; } switch (arg[j]) { case 'A': doall++; break; case 'C': progress++; if (arg[j+1]) { progress_fd = string_to_int(arg+j+1); if (progress_fd < 0) progress_fd = 0; else goto next_arg; } else if ((i+1) < argc && argv[i+1][0] != '-') { progress_fd = string_to_int(argv[i]); if (progress_fd < 0) progress_fd = 0; else { goto next_arg; } } break; case 'V': verbose++; break; case 'N': noexecute++; break; case 'R': skip_root++; break; case 'T': notitle++; break; case 'M': like_mount++; break; case 'P': parallel_root++; break; case 's': serialize++; break; case 't': tmp = 0; if (fstype) bb_show_usage(); if (arg[j+1]) tmp = arg+j+1; else if ((i+1) < argc) tmp = argv[++i]; else bb_show_usage(); fstype = string_copy(tmp); compile_fs_type(fstype, &fs_type_compiled); goto next_arg; case '-': opts_for_fsck++; break; case '?': bb_show_usage(); break; default: options[++opt] = arg[j]; break; } } next_arg: if (opt) { options[0] = '-'; options[++opt] = '\0'; if (num_args >= MAX_ARGS) { bb_error_msg("too many arguments"); } args[num_args++] = string_copy(options); opt = 0; } } if (getenv("FSCK_FORCE_ALL_PARALLEL")) force_all_parallel++; if ((tmp = getenv("FSCK_MAX_INST"))) max_running = atoi(tmp); } int fsck_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fsck_main(int argc, char **argv) { int i, status = 0; int interactive = 0; const char *fstab; struct fs_info *fs; setvbuf(stdout, NULL, _IONBF, BUFSIZ); setvbuf(stderr, NULL, _IONBF, BUFSIZ); blkid_get_cache(&cache, NULL); PRS(argc, argv); if (!notitle) printf("fsck %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE); fstab = getenv("FSTAB_FILE"); if (!fstab) fstab = _PATH_MNTTAB; load_fs_info(fstab); fsck_path = e2fs_set_sbin_path(); if ((num_devices == 1) || (serialize)) interactive = 1; /* If -A was specified ("check all"), do that! */ if (doall) return check_all(); if (num_devices == 0) { serialize++; interactive++; return check_all(); } for (i = 0; i < num_devices; i++) { if (cancel_requested) { if (!kill_sent) { kill_all(SIGTERM); kill_sent++; } break; } fs = lookup(devices[i]); if (!fs) { fs = create_fs_device(devices[i], 0, "auto", 0, -1, -1); if (!fs) continue; } fsck_device(fs, interactive); if (serialize || (max_running && (num_running >= max_running))) { struct fsck_instance *inst; inst = wait_one(0); if (inst) { status |= inst->exit_status; free_instance(inst); } if (verbose > 1) printf("----------------------------------\n"); } } status |= wait_many(FLAG_WAIT_ALL); blkid_put_cache(cache); return status; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/tune2fs.c0000644000000000000000000004564612263563520020225 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * tune2fs.c - Change the file system parameters on an ext2 file system * * Copyright (C) 1992, 1993, 1994 Remy Card * Laboratoire MASI, Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o. * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* * History: * 93/06/01 - Creation * 93/10/31 - Added the -c option to change the maximal mount counts * 93/12/14 - Added -l flag to list contents of superblock * M.J.E. Mol (marcel@duteca.et.tudelft.nl) * F.W. ten Wolde (franky@duteca.et.tudelft.nl) * 93/12/29 - Added the -e option to change errors behavior * 94/02/27 - Ported to use the ext2fs library * 94/03/06 - Added the checks interval from Uwe Ohse (uwe@tirka.gun.de) */ #include #include #include #include #include #include #include #include #include "e2fsbb.h" #include "ext2fs/ext2_fs.h" #include "ext2fs/ext2fs.h" #include "uuid/uuid.h" #include "e2p/e2p.h" #include "ext2fs/kernel-jbd.h" #include "util.h" #include "blkid/blkid.h" #include "libbb.h" static char * device_name = NULL; static char * new_label, *new_last_mounted, *new_UUID; static char * io_options; static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag; static int m_flag, M_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag; static time_t last_check_time; static int print_label; static int max_mount_count, mount_count, mount_flags; static unsigned long interval, reserved_blocks; static unsigned reserved_ratio; static unsigned long resgid, resuid; static unsigned short errors; static int open_flag; static char *features_cmd; static char *mntopts_cmd; static int journal_size, journal_flags; static char *journal_device = NULL; static const char *please_fsck = "Please run e2fsck on the filesystem\n"; static __u32 ok_features[3] = { EXT3_FEATURE_COMPAT_HAS_JOURNAL | EXT2_FEATURE_COMPAT_DIR_INDEX, EXT2_FEATURE_INCOMPAT_FILETYPE, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER }; /* * Remove an external journal from the filesystem */ static void remove_journal_device(ext2_filsys fs) { char *journal_path; ext2_filsys jfs; char buf[1024]; journal_superblock_t *jsb; int i, nr_users; errcode_t retval; int commit_remove_journal = 0; io_manager io_ptr; if (f_flag) commit_remove_journal = 1; /* force removal even if error */ uuid_unparse(fs->super->s_journal_uuid, buf); journal_path = blkid_get_devname(NULL, "UUID", buf); if (!journal_path) { journal_path = ext2fs_find_block_device(fs->super->s_journal_dev); if (!journal_path) return; } io_ptr = unix_io_manager; retval = ext2fs_open(journal_path, EXT2_FLAG_RW| EXT2_FLAG_JOURNAL_DEV_OK, 0, fs->blocksize, io_ptr, &jfs); if (retval) { bb_error_msg("Failed to open external journal"); goto no_valid_journal; } if (!(jfs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { bb_error_msg("%s is not a journal device", journal_path); goto no_valid_journal; } /* Get the journal superblock */ if ((retval = io_channel_read_blk(jfs->io, 1, -1024, buf))) { bb_error_msg("Failed to read journal superblock"); goto no_valid_journal; } jsb = (journal_superblock_t *) buf; if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) { bb_error_msg("Journal superblock not found!"); goto no_valid_journal; } /* Find the filesystem UUID */ nr_users = ntohl(jsb->s_nr_users); for (i=0; i < nr_users; i++) { if (memcmp(fs->super->s_uuid, &jsb->s_users[i*16], 16) == 0) break; } if (i >= nr_users) { bb_error_msg("Filesystem's UUID not found on journal device"); commit_remove_journal = 1; goto no_valid_journal; } nr_users--; for (i=0; i < nr_users; i++) memcpy(&jsb->s_users[i*16], &jsb->s_users[(i+1)*16], 16); jsb->s_nr_users = htonl(nr_users); /* Write back the journal superblock */ if ((retval = io_channel_write_blk(jfs->io, 1, -1024, buf))) { bb_error_msg("Failed to write journal superblock"); goto no_valid_journal; } commit_remove_journal = 1; no_valid_journal: if (commit_remove_journal == 0) bb_error_msg_and_die("Journal NOT removed"); fs->super->s_journal_dev = 0; uuid_clear(fs->super->s_journal_uuid); ext2fs_mark_super_dirty(fs); puts("Journal removed"); free(journal_path); } /* Helper function for remove_journal_inode */ static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr, int blockcnt EXT2FS_ATTR((unused)), void *private EXT2FS_ATTR((unused))) { blk_t block; int group; block = *blocknr; ext2fs_unmark_block_bitmap(fs->block_map,block); group = ext2fs_group_of_blk(fs, block); fs->group_desc[group].bg_free_blocks_count++; fs->super->s_free_blocks_count++; return 0; } /* * Remove the journal inode from the filesystem */ static void remove_journal_inode(ext2_filsys fs) { struct ext2_inode inode; errcode_t retval; ino_t ino = fs->super->s_journal_inum; char *msg = "to read"; char *s = "journal inode"; retval = ext2fs_read_inode(fs, ino, &inode); if (retval) goto REMOVE_JOURNAL_INODE_ERROR; if (ino == EXT2_JOURNAL_INO) { retval = ext2fs_read_bitmaps(fs); if (retval) { msg = "to read bitmaps"; s = ""; goto REMOVE_JOURNAL_INODE_ERROR; } retval = ext2fs_block_iterate(fs, ino, 0, NULL, release_blocks_proc, NULL); if (retval) { msg = "clearing"; goto REMOVE_JOURNAL_INODE_ERROR; } memset(&inode, 0, sizeof(inode)); ext2fs_mark_bb_dirty(fs); fs->flags &= ~EXT2_FLAG_SUPER_ONLY; } else inode.i_flags &= ~EXT2_IMMUTABLE_FL; retval = ext2fs_write_inode(fs, ino, &inode); if (retval) { msg = "writing"; REMOVE_JOURNAL_INODE_ERROR: bb_error_msg_and_die("Failed %s %s", msg, s); } fs->super->s_journal_inum = 0; ext2fs_mark_super_dirty(fs); } /* * Update the default mount options */ static void update_mntopts(ext2_filsys fs, char *mntopts) { struct ext2_super_block *sb= fs->super; if (e2p_edit_mntopts(mntopts, &sb->s_default_mount_opts, ~0)) bb_error_msg_and_die("Invalid mount option set: %s", mntopts); ext2fs_mark_super_dirty(fs); } /* * Update the feature set as provided by the user. */ static void update_feature_set(ext2_filsys fs, char *features) { int sparse, old_sparse, filetype, old_filetype; int journal, old_journal, dxdir, old_dxdir; struct ext2_super_block *sb= fs->super; __u32 old_compat, old_incompat, old_ro_compat; old_compat = sb->s_feature_compat; old_ro_compat = sb->s_feature_ro_compat; old_incompat = sb->s_feature_incompat; old_sparse = sb->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; old_filetype = sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE; old_journal = sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL; old_dxdir = sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX; if (e2p_edit_feature(features, &sb->s_feature_compat, ok_features)) bb_error_msg_and_die("Invalid filesystem option set: %s", features); sparse = sb->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; filetype = sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE; journal = sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL; dxdir = sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX; if (old_journal && !journal) { if ((mount_flags & EXT2_MF_MOUNTED) && !(mount_flags & EXT2_MF_READONLY)) { bb_error_msg_and_die( "The has_journal flag may only be " "cleared when the filesystem is\n" "unmounted or mounted " "read-only"); } if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) { bb_error_msg_and_die( "The needs_recovery flag is set. " "%s before clearing the has_journal flag.", please_fsck); } if (sb->s_journal_inum) { remove_journal_inode(fs); } if (sb->s_journal_dev) { remove_journal_device(fs); } } if (journal && !old_journal) { /* * If adding a journal flag, let the create journal * code below handle creating setting the flag and * creating the journal. We supply a default size if * necessary. */ if (!journal_size) journal_size = -1; sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL; } if (dxdir && !old_dxdir) { if (!sb->s_def_hash_version) sb->s_def_hash_version = EXT2_HASH_TEA; if (uuid_is_null((unsigned char *) sb->s_hash_seed)) uuid_generate((unsigned char *) sb->s_hash_seed); } if (sb->s_rev_level == EXT2_GOOD_OLD_REV && (sb->s_feature_compat || sb->s_feature_ro_compat || sb->s_feature_incompat)) ext2fs_update_dynamic_rev(fs); if ((sparse != old_sparse) || (filetype != old_filetype)) { sb->s_state &= ~EXT2_VALID_FS; printf("\n%s\n", please_fsck); } if ((old_compat != sb->s_feature_compat) || (old_ro_compat != sb->s_feature_ro_compat) || (old_incompat != sb->s_feature_incompat)) ext2fs_mark_super_dirty(fs); } /* * Add a journal to the filesystem. */ static void add_journal(ext2_filsys fs) { if (fs->super->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) { bb_error_msg_and_die("The filesystem already has a journal"); } if (journal_device) { make_journal_device(journal_device, fs, 0, 0); } else if (journal_size) { make_journal_blocks(fs, journal_size, journal_flags, 0); /* * If the filesystem wasn't mounted, we need to force * the block group descriptors out. */ if ((mount_flags & EXT2_MF_MOUNTED) == 0) fs->flags &= ~EXT2_FLAG_SUPER_ONLY; } print_check_message(fs); } /* * Busybox stuff */ static char * x_blkid_get_devname(const char *token) { char * dev_name; if (!(dev_name = blkid_get_devname(NULL, token, NULL))) bb_error_msg_and_die("Unable to resolve '%s'", token); return dev_name; } #ifdef CONFIG_E2LABEL static void parse_e2label_options(int argc, char ** argv) { if ((argc < 2) || (argc > 3)) bb_show_usage(); io_options = strchr(argv[1], '?'); if (io_options) *io_options++ = 0; device_name = x_blkid_get_devname(argv[1]); if (argc == 3) { open_flag = EXT2_FLAG_RW | EXT2_FLAG_JOURNAL_DEV_OK; L_flag = 1; new_label = argv[2]; } else print_label++; } #else #define parse_e2label_options(x,y) #endif static time_t parse_time(char *str) { struct tm ts; if (strcmp(str, "now") == 0) { return time(0); } memset(&ts, 0, sizeof(ts)); #ifdef HAVE_STRPTIME strptime(str, "%Y%m%d%H%M%S", &ts); #else sscanf(str, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon, &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec); ts.tm_year -= 1900; ts.tm_mon -= 1; if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 || ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 || ts.tm_min > 59 || ts.tm_sec > 61) ts.tm_mday = 0; #endif if (ts.tm_mday == 0) { bb_error_msg_and_die("can't parse date/time specifier: %s", str); } return mktime(&ts); } static void parse_tune2fs_options(int argc, char **argv) { int c; char * tmp; printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE); while ((c = getopt(argc, argv, "c:e:fg:i:jlm:o:r:s:u:C:J:L:M:O:T:U:")) != EOF) switch (c) { case 'c': max_mount_count = xatou_range(optarg, 0, 16000); if (max_mount_count == 0) max_mount_count = -1; c_flag = 1; open_flag = EXT2_FLAG_RW; break; case 'C': mount_count = xatou_range(optarg, 0, 16000); C_flag = 1; open_flag = EXT2_FLAG_RW; break; case 'e': if (strcmp (optarg, "continue") == 0) errors = EXT2_ERRORS_CONTINUE; else if (strcmp (optarg, "remount-ro") == 0) errors = EXT2_ERRORS_RO; else if (strcmp (optarg, "panic") == 0) errors = EXT2_ERRORS_PANIC; else { bb_error_msg_and_die("bad error behavior - %s", optarg); } e_flag = 1; open_flag = EXT2_FLAG_RW; break; case 'f': /* Force */ f_flag = 1; break; case 'g': resgid = bb_strtoul(optarg, NULL, 10); if (errno) resgid = xgroup2gid(optarg); g_flag = 1; open_flag = EXT2_FLAG_RW; break; case 'i': interval = strtoul(optarg, &tmp, 0); switch (*tmp) { case 's': tmp++; break; case '\0': case 'd': case 'D': /* days */ interval *= 86400; if (*tmp != '\0') tmp++; break; case 'm': case 'M': /* months! */ interval *= 86400 * 30; tmp++; break; case 'w': case 'W': /* weeks */ interval *= 86400 * 7; tmp++; break; } if (*tmp || interval > (365 * 86400)) { bb_error_msg_and_die("bad interval - %s", optarg); } i_flag = 1; open_flag = EXT2_FLAG_RW; break; case 'j': if (!journal_size) journal_size = -1; open_flag = EXT2_FLAG_RW; break; case 'J': parse_journal_opts(&journal_device, &journal_flags, &journal_size, optarg); open_flag = EXT2_FLAG_RW; break; case 'l': l_flag = 1; break; case 'L': new_label = optarg; L_flag = 1; open_flag = EXT2_FLAG_RW | EXT2_FLAG_JOURNAL_DEV_OK; break; case 'm': reserved_ratio = xatou_range(optarg, 0, 50); m_flag = 1; open_flag = EXT2_FLAG_RW; break; case 'M': new_last_mounted = optarg; M_flag = 1; open_flag = EXT2_FLAG_RW; break; case 'o': if (mntopts_cmd) { bb_error_msg_and_die("-o may only be specified once"); } mntopts_cmd = optarg; open_flag = EXT2_FLAG_RW; break; case 'O': if (features_cmd) { bb_error_msg_and_die("-O may only be specified once"); } features_cmd = optarg; open_flag = EXT2_FLAG_RW; break; case 'r': reserved_blocks = xatoul(optarg); r_flag = 1; open_flag = EXT2_FLAG_RW; break; case 's': s_flag = atoi(optarg); open_flag = EXT2_FLAG_RW; break; case 'T': T_flag = 1; last_check_time = parse_time(optarg); open_flag = EXT2_FLAG_RW; break; case 'u': resuid = bb_strtoul(optarg, NULL, 10); if (errno) resuid = xuname2uid(optarg); u_flag = 1; open_flag = EXT2_FLAG_RW; break; case 'U': new_UUID = optarg; U_flag = 1; open_flag = EXT2_FLAG_RW | EXT2_FLAG_JOURNAL_DEV_OK; break; default: bb_show_usage(); } if (optind < argc - 1 || optind == argc) bb_show_usage(); if (!open_flag && !l_flag) bb_show_usage(); io_options = strchr(argv[optind], '?'); if (io_options) *io_options++ = 0; device_name = x_blkid_get_devname(argv[optind]); } static void tune2fs_clean_up(void) { if (ENABLE_FEATURE_CLEAN_UP && device_name) free(device_name); if (ENABLE_FEATURE_CLEAN_UP && journal_device) free(journal_device); } int tune2fs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int tune2fs_main(int argc, char **argv) { errcode_t retval; ext2_filsys fs; struct ext2_super_block *sb; io_manager io_ptr; if (ENABLE_FEATURE_CLEAN_UP) atexit(tune2fs_clean_up); if (ENABLE_E2LABEL && (applet_name[0] == 'e')) /* e2label */ parse_e2label_options(argc, argv); else parse_tune2fs_options(argc, argv); /* tune2fs */ io_ptr = unix_io_manager; retval = ext2fs_open2(device_name, io_options, open_flag, 0, 0, io_ptr, &fs); if (retval) bb_error_msg_and_die("No valid superblock on %s", device_name); sb = fs->super; if (print_label) { /* For e2label emulation */ printf("%.*s\n", (int) sizeof(sb->s_volume_name), sb->s_volume_name); return 0; } retval = ext2fs_check_if_mounted(device_name, &mount_flags); if (retval) bb_error_msg_and_die("can't determine if %s is mounted", device_name); /* Normally we only need to write out the superblock */ fs->flags |= EXT2_FLAG_SUPER_ONLY; if (c_flag) { sb->s_max_mnt_count = max_mount_count; ext2fs_mark_super_dirty(fs); printf("Setting maximal mount count to %d\n", max_mount_count); } if (C_flag) { sb->s_mnt_count = mount_count; ext2fs_mark_super_dirty(fs); printf("Setting current mount count to %d\n", mount_count); } if (e_flag) { sb->s_errors = errors; ext2fs_mark_super_dirty(fs); printf("Setting error behavior to %u\n", errors); } if (g_flag) { sb->s_def_resgid = resgid; ext2fs_mark_super_dirty(fs); printf("Setting reserved blocks gid to %lu\n", resgid); } if (i_flag) { sb->s_checkinterval = interval; ext2fs_mark_super_dirty(fs); printf("Setting interval between check %lu seconds\n", interval); } if (m_flag) { sb->s_r_blocks_count = (sb->s_blocks_count / 100) * reserved_ratio; ext2fs_mark_super_dirty(fs); printf("Setting reserved blocks percentage to %u (%u blocks)\n", reserved_ratio, sb->s_r_blocks_count); } if (r_flag) { if (reserved_blocks >= sb->s_blocks_count/2) bb_error_msg_and_die("reserved blocks count is too big (%lu)", reserved_blocks); sb->s_r_blocks_count = reserved_blocks; ext2fs_mark_super_dirty(fs); printf("Setting reserved blocks count to %lu\n", reserved_blocks); } if (s_flag == 1) { if (sb->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) bb_error_msg("\nThe filesystem already has sparse superblocks"); else { sb->s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; sb->s_state &= ~EXT2_VALID_FS; ext2fs_mark_super_dirty(fs); printf("\nSparse superblock flag set. %s", please_fsck); } } if (s_flag == 0) { if (!(sb->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) bb_error_msg("\nThe filesystem already has sparse superblocks disabled"); else { sb->s_feature_ro_compat &= ~EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; sb->s_state &= ~EXT2_VALID_FS; fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; ext2fs_mark_super_dirty(fs); printf("\nSparse superblock flag cleared. %s", please_fsck); } } if (T_flag) { sb->s_lastcheck = last_check_time; ext2fs_mark_super_dirty(fs); printf("Setting time filesystem last checked to %s\n", ctime(&last_check_time)); } if (u_flag) { sb->s_def_resuid = resuid; ext2fs_mark_super_dirty(fs); printf("Setting reserved blocks uid to %lu\n", resuid); } if (L_flag) { if (strlen(new_label) > sizeof(sb->s_volume_name)) bb_error_msg("Warning: label too long, truncating"); memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name)); safe_strncpy(sb->s_volume_name, new_label, sizeof(sb->s_volume_name)); ext2fs_mark_super_dirty(fs); } if (M_flag) { memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted)); safe_strncpy(sb->s_last_mounted, new_last_mounted, sizeof(sb->s_last_mounted)); ext2fs_mark_super_dirty(fs); } if (mntopts_cmd) update_mntopts(fs, mntopts_cmd); if (features_cmd) update_feature_set(fs, features_cmd); if (journal_size || journal_device) add_journal(fs); if (U_flag) { if ((strcasecmp(new_UUID, "null") == 0) || (strcasecmp(new_UUID, "clear") == 0)) { uuid_clear(sb->s_uuid); } else if (strcasecmp(new_UUID, "time") == 0) { uuid_generate_time(sb->s_uuid); } else if (strcasecmp(new_UUID, "random") == 0) { uuid_generate(sb->s_uuid); } else if (uuid_parse(new_UUID, sb->s_uuid)) { bb_error_msg_and_die("Invalid UUID format"); } ext2fs_mark_super_dirty(fs); } if (l_flag) list_super (sb); return (ext2fs_close (fs) ? 1 : 0); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/util.h0000644000000000000000000000154412263563520017606 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * util.h --- header file defining prototypes for helper functions * used by tune2fs and mke2fs * * Copyright 2000 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ extern void proceed_question(void); extern void check_plausibility(const char *device, int force); extern void parse_journal_opts(char **, int *, int *, const char *opts); extern void check_mount(const char *device, int force, const char *type); extern int figure_journal_size(int size, ext2_filsys fs); extern void print_check_message(ext2_filsys fs); extern void make_journal_device(char *journal_device, ext2_filsys fs, int quiet, int force); extern void make_journal_blocks(ext2_filsys fs, int journal_size, int journal_flags, int quiet); extern char *e2fs_set_sbin_path(void); busybox-1.22.1/e2fsprogs/old_e2fsprogs/README0000644000000000000000000000014212263563520017331 0ustar rootrootThis is a pretty straight rip from the e2fsprogs pkg. See README's in subdirs for specific info. busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/0000755000000000000000000000000012320365362017537 5ustar rootrootbusybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/list.c0000644000000000000000000000447112263563520020666 0ustar rootroot/* vi: set sw=4 ts=4: */ #include "list.h" /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ void __list_add(struct list_head * add, struct list_head * prev, struct list_head * next) { next->prev = add; add->next = next; add->prev = prev; prev->next = add; } /* * list_add - add a new entry * @add: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ void list_add(struct list_head *add, struct list_head *head) { __list_add(add, head, head->next); } /* * list_add_tail - add a new entry * @add: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ void list_add_tail(struct list_head *add, struct list_head *head) { __list_add(add, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } /* * list_del - deletes entry from list. * @entry: the element to delete from the list. * * list_empty() on @entry does not return true after this, @entry is * in an undefined state. */ void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); } /* * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. */ void list_del_init(struct list_head *entry) { __list_del(entry->prev, entry->next); INIT_LIST_HEAD(entry); } /* * list_empty - tests whether a list is empty * @head: the list to test. */ int list_empty(struct list_head *head) { return head->next == head; } /* * list_splice - join two lists * @list: the new list to add. * @head: the place to add it in the first list. */ void list_splice(struct list_head *list, struct list_head *head) { struct list_head *first = list->next; if (first != list) { struct list_head *last = list->prev; struct list_head *at = head->next; first->prev = head; head->next = first; last->next = at; at->prev = last; } } busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/probe.h0000644000000000000000000002136212263563520021025 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * probe.h - constants and on-disk structures for extracting device data * * Copyright (C) 1999 by Andries Brouwer * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o * Copyright (C) 2001 by Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #ifndef BLKID_PROBE_H #define BLKID_PROBE_H 1 #include struct blkid_magic; typedef int (*blkid_probe_t)(int fd, blkid_cache cache, blkid_dev dev, const struct blkid_magic *id, unsigned char *buf); struct blkid_magic { const char *bim_type; /* type name for this magic */ long bim_kboff; /* kilobyte offset of superblock */ unsigned bim_sboff; /* byte offset within superblock */ unsigned bim_len; /* length of magic */ const char *bim_magic; /* magic string */ blkid_probe_t bim_probe; /* probe function */ }; /* * Structures for each of the content types we want to extract information * from. We do not necessarily need the magic field here, because we have * already identified the content type before we get this far. It may still * be useful if there are probe functions which handle multiple content types. */ struct ext2_super_block { __u32 s_inodes_count; __u32 s_blocks_count; __u32 s_r_blocks_count; __u32 s_free_blocks_count; __u32 s_free_inodes_count; __u32 s_first_data_block; __u32 s_log_block_size; __u32 s_dummy3[7]; unsigned char s_magic[2]; __u16 s_state; __u32 s_dummy5[8]; __u32 s_feature_compat; __u32 s_feature_incompat; __u32 s_feature_ro_compat; unsigned char s_uuid[16]; char s_volume_name[16]; }; #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x00000004 #define EXT3_FEATURE_INCOMPAT_RECOVER 0x00000004 #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x00000008 struct xfs_super_block { unsigned char xs_magic[4]; __u32 xs_blocksize; __u64 xs_dblocks; __u64 xs_rblocks; __u32 xs_dummy1[2]; unsigned char xs_uuid[16]; __u32 xs_dummy2[15]; char xs_fname[12]; __u32 xs_dummy3[2]; __u64 xs_icount; __u64 xs_ifree; __u64 xs_fdblocks; }; struct reiserfs_super_block { __u32 rs_blocks_count; __u32 rs_free_blocks; __u32 rs_root_block; __u32 rs_journal_block; __u32 rs_journal_dev; __u32 rs_orig_journal_size; __u32 rs_dummy2[5]; __u16 rs_blocksize; __u16 rs_dummy3[3]; unsigned char rs_magic[12]; __u32 rs_dummy4[5]; unsigned char rs_uuid[16]; char rs_label[16]; }; struct jfs_super_block { unsigned char js_magic[4]; __u32 js_version; __u64 js_size; __u32 js_bsize; __u32 js_dummy1; __u32 js_pbsize; __u32 js_dummy2[27]; unsigned char js_uuid[16]; unsigned char js_label[16]; unsigned char js_loguuid[16]; }; struct romfs_super_block { unsigned char ros_magic[8]; __u32 ros_dummy1[2]; unsigned char ros_volume[16]; }; struct cramfs_super_block { __u8 magic[4]; __u32 size; __u32 flags; __u32 future; __u8 signature[16]; struct cramfs_info { __u32 crc; __u32 edition; __u32 blocks; __u32 files; } info; __u8 name[16]; }; struct swap_id_block { /* unsigned char sws_boot[1024]; */ __u32 sws_version; __u32 sws_lastpage; __u32 sws_nrbad; unsigned char sws_uuid[16]; char sws_volume[16]; unsigned char sws_pad[117]; __u32 sws_badpg; }; /* Yucky misaligned values */ struct vfat_super_block { /* 00*/ unsigned char vs_ignored[3]; /* 03*/ unsigned char vs_sysid[8]; /* 0b*/ unsigned char vs_sector_size[2]; /* 0d*/ __u8 vs_cluster_size; /* 0e*/ __u16 vs_reserved; /* 10*/ __u8 vs_fats; /* 11*/ unsigned char vs_dir_entries[2]; /* 13*/ unsigned char vs_sectors[2]; /* 15*/ unsigned char vs_media; /* 16*/ __u16 vs_fat_length; /* 18*/ __u16 vs_secs_track; /* 1a*/ __u16 vs_heads; /* 1c*/ __u32 vs_hidden; /* 20*/ __u32 vs_total_sect; /* 24*/ __u32 vs_fat32_length; /* 28*/ __u16 vs_flags; /* 2a*/ __u8 vs_version[2]; /* 2c*/ __u32 vs_root_cluster; /* 30*/ __u16 vs_insfo_sector; /* 32*/ __u16 vs_backup_boot; /* 34*/ __u16 vs_reserved2[6]; /* 40*/ unsigned char vs_unknown[3]; /* 43*/ unsigned char vs_serno[4]; /* 47*/ char vs_label[11]; /* 52*/ unsigned char vs_magic[8]; /* 5a*/ unsigned char vs_dummy2[164]; /*1fe*/ unsigned char vs_pmagic[2]; }; /* Yucky misaligned values */ struct msdos_super_block { /* 00*/ unsigned char ms_ignored[3]; /* 03*/ unsigned char ms_sysid[8]; /* 0b*/ unsigned char ms_sector_size[2]; /* 0d*/ __u8 ms_cluster_size; /* 0e*/ __u16 ms_reserved; /* 10*/ __u8 ms_fats; /* 11*/ unsigned char ms_dir_entries[2]; /* 13*/ unsigned char ms_sectors[2]; /* 15*/ unsigned char ms_media; /* 16*/ __u16 ms_fat_length; /* 18*/ __u16 ms_secs_track; /* 1a*/ __u16 ms_heads; /* 1c*/ __u32 ms_hidden; /* 20*/ __u32 ms_total_sect; /* 24*/ unsigned char ms_unknown[3]; /* 27*/ unsigned char ms_serno[4]; /* 2b*/ char ms_label[11]; /* 36*/ unsigned char ms_magic[8]; /* 3d*/ unsigned char ms_dummy2[192]; /*1fe*/ unsigned char ms_pmagic[2]; }; struct minix_super_block { __u16 ms_ninodes; __u16 ms_nzones; __u16 ms_imap_blocks; __u16 ms_zmap_blocks; __u16 ms_firstdatazone; __u16 ms_log_zone_size; __u32 ms_max_size; unsigned char ms_magic[2]; __u16 ms_state; __u32 ms_zones; }; struct mdp_superblock_s { __u32 md_magic; __u32 major_version; __u32 minor_version; __u32 patch_version; __u32 gvalid_words; __u32 set_uuid0; __u32 ctime; __u32 level; __u32 size; __u32 nr_disks; __u32 raid_disks; __u32 md_minor; __u32 not_persistent; __u32 set_uuid1; __u32 set_uuid2; __u32 set_uuid3; }; struct hfs_super_block { char h_magic[2]; char h_dummy[18]; __u32 h_blksize; }; struct ocfs_volume_header { unsigned char minor_version[4]; unsigned char major_version[4]; unsigned char signature[128]; char mount[128]; unsigned char mount_len[2]; }; struct ocfs_volume_label { unsigned char disk_lock[48]; char label[64]; unsigned char label_len[2]; unsigned char vol_id[16]; unsigned char vol_id_len[2]; }; #define ocfsmajor(o) ((__u32)o.major_version[0] \ + (((__u32) o.major_version[1]) << 8) \ + (((__u32) o.major_version[2]) << 16) \ + (((__u32) o.major_version[3]) << 24)) #define ocfslabellen(o) ((__u32)o.label_len[0] + (((__u32) o.label_len[1]) << 8)) #define ocfsmountlen(o) ((__u32)o.mount_len[0] + (((__u32) o.mount_len[1])<<8)) #define OCFS_MAGIC "OracleCFS" struct ocfs2_super_block { unsigned char signature[8]; unsigned char s_dummy1[184]; unsigned char s_dummy2[80]; char s_label[64]; unsigned char s_uuid[16]; }; #define OCFS2_MIN_BLOCKSIZE 512 #define OCFS2_MAX_BLOCKSIZE 4096 #define OCFS2_SUPER_BLOCK_BLKNO 2 #define OCFS2_SUPER_BLOCK_SIGNATURE "OCFSV2" struct oracle_asm_disk_label { char dummy[32]; char dl_tag[8]; char dl_id[24]; }; #define ORACLE_ASM_DISK_LABEL_MARKED "ORCLDISK" #define ORACLE_ASM_DISK_LABEL_OFFSET 32 #define ISODCL(from, to) (to - from + 1) struct iso_volume_descriptor { char type[ISODCL(1,1)]; /* 711 */ char id[ISODCL(2,6)]; char version[ISODCL(7,7)]; char data[ISODCL(8,2048)]; }; /* * Byte swap functions */ #ifdef __GNUC__ #define _INLINE_ static __inline__ #else /* For Watcom C */ #define _INLINE_ static inline #endif static __u16 blkid_swab16(__u16 val); static __u32 blkid_swab32(__u32 val); static __u64 blkid_swab64(__u64 val); #if ((defined __GNUC__) && \ (defined(__i386__) || defined(__i486__) || defined(__i586__))) #define _BLKID_HAVE_ASM_BITOPS_ _INLINE_ __u32 blkid_swab32(__u32 val) { #ifdef EXT2FS_REQUIRE_486 __asm__("bswap %0" : "=r" (val) : "0" (val)); #else __asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */ "rorl $16,%0\n\t" /* swap words */ "xchgb %b0,%h0" /* swap higher bytes */ :"=q" (val) : "0" (val)); #endif return val; } _INLINE_ __u16 blkid_swab16(__u16 val) { __asm__("xchgb %b0,%h0" /* swap bytes */ : "=q" (val) : "0" (val)); return val; } _INLINE_ __u64 blkid_swab64(__u64 val) { return blkid_swab32(val >> 32) | ( ((__u64)blkid_swab32((__u32)val)) << 32 ); } #endif #if !defined(_BLKID_HAVE_ASM_BITOPS_) _INLINE_ __u16 blkid_swab16(__u16 val) { return (val >> 8) | (val << 8); } _INLINE_ __u32 blkid_swab32(__u32 val) { return (val>>24) | ((val>>8) & 0xFF00) | ((val<<8) & 0xFF0000) | (val<<24); } _INLINE_ __u64 blkid_swab64(__u64 val) { return blkid_swab32(val >> 32) | ( ((__u64)blkid_swab32((__u32)val)) << 32 ); } #endif #if __BYTE_ORDER == __BIG_ENDIAN #define blkid_le16(x) blkid_swab16(x) #define blkid_le32(x) blkid_swab32(x) #define blkid_le64(x) blkid_swab64(x) #define blkid_be16(x) (x) #define blkid_be32(x) (x) #define blkid_be64(x) (x) #else #define blkid_le16(x) (x) #define blkid_le32(x) (x) #define blkid_le64(x) (x) #define blkid_be16(x) blkid_swab16(x) #define blkid_be32(x) blkid_swab32(x) #define blkid_be64(x) blkid_swab64(x) #endif #undef _INLINE_ #endif /* _BLKID_PROBE_H */ busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/blkidP.h0000644000000000000000000001247312263563520021126 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * blkidP.h - Internal interfaces for libblkid * * Copyright (C) 2001 Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #ifndef BLKID_BLKIDP_H #define BLKID_BLKIDP_H 1 #include #include #include "blkid.h" #include "list.h" #ifdef __GNUC__ #define __BLKID_ATTR(x) __attribute__(x) #else #define __BLKID_ATTR(x) #endif /* * This describes the attributes of a specific device. * We can traverse all of the tags by bid_tags (linking to the tag bit_names). * The bid_label and bid_uuid fields are shortcuts to the LABEL and UUID tag * values, if they exist. */ struct blkid_struct_dev { struct list_head bid_devs; /* All devices in the cache */ struct list_head bid_tags; /* All tags for this device */ blkid_cache bid_cache; /* Dev belongs to this cache */ char *bid_name; /* Device inode pathname */ char *bid_type; /* Preferred device TYPE */ int bid_pri; /* Device priority */ dev_t bid_devno; /* Device major/minor number */ time_t bid_time; /* Last update time of device */ unsigned int bid_flags; /* Device status bitflags */ char *bid_label; /* Shortcut to device LABEL */ char *bid_uuid; /* Shortcut to binary UUID */ }; #define BLKID_BID_FL_VERIFIED 0x0001 /* Device data validated from disk */ #define BLKID_BID_FL_INVALID 0x0004 /* Device is invalid */ /* * Each tag defines a NAME=value pair for a particular device. The tags * are linked via bit_names for a single device, so that traversing the * names list will get you a list of all tags associated with a device. * They are also linked via bit_values for all devices, so one can easily * search all tags with a given NAME for a specific value. */ struct blkid_struct_tag { struct list_head bit_tags; /* All tags for this device */ struct list_head bit_names; /* All tags with given NAME */ char *bit_name; /* NAME of tag (shared) */ char *bit_val; /* value of tag */ blkid_dev bit_dev; /* pointer to device */ }; typedef struct blkid_struct_tag *blkid_tag; /* * Minimum number of seconds between device probes, even when reading * from the cache. This is to avoid re-probing all devices which were * just probed by another program that does not share the cache. */ #define BLKID_PROBE_MIN 2 /* * Time in seconds an entry remains verified in the in-memory cache * before being reverified (in case of long-running processes that * keep a cache in memory and continue to use it for a long time). */ #define BLKID_PROBE_INTERVAL 200 /* This describes an entire blkid cache file and probed devices. * We can traverse all of the found devices via bic_list. * We can traverse all of the tag types by bic_tags, which hold empty tags * for each tag type. Those tags can be used as list_heads for iterating * through all devices with a specific tag type (e.g. LABEL). */ struct blkid_struct_cache { struct list_head bic_devs; /* List head of all devices */ struct list_head bic_tags; /* List head of all tag types */ time_t bic_time; /* Last probe time */ time_t bic_ftime; /* Mod time of the cachefile */ unsigned int bic_flags; /* Status flags of the cache */ char *bic_filename; /* filename of cache */ }; #define BLKID_BIC_FL_PROBED 0x0002 /* We probed /proc/partition devices */ #define BLKID_BIC_FL_CHANGED 0x0004 /* Cache has changed from disk */ extern char *blkid_strdup(const char *s); extern char *blkid_strndup(const char *s, const int length); #define BLKID_CACHE_FILE "/etc/blkid.tab" extern const char *blkid_devdirs[]; #define BLKID_ERR_IO 5 #define BLKID_ERR_PROC 9 #define BLKID_ERR_MEM 12 #define BLKID_ERR_CACHE 14 #define BLKID_ERR_DEV 19 #define BLKID_ERR_PARAM 22 #define BLKID_ERR_BIG 27 /* * Priority settings for different types of devices */ #define BLKID_PRI_EVMS 30 #define BLKID_PRI_LVM 20 #define BLKID_PRI_MD 10 #if defined(TEST_PROGRAM) && !defined(CONFIG_BLKID_DEBUG) #define CONFIG_BLKID_DEBUG #endif #define DEBUG_CACHE 0x0001 #define DEBUG_DUMP 0x0002 #define DEBUG_DEV 0x0004 #define DEBUG_DEVNAME 0x0008 #define DEBUG_DEVNO 0x0010 #define DEBUG_PROBE 0x0020 #define DEBUG_READ 0x0040 #define DEBUG_RESOLVE 0x0080 #define DEBUG_SAVE 0x0100 #define DEBUG_TAG 0x0200 #define DEBUG_INIT 0x8000 #define DEBUG_ALL 0xFFFF #ifdef CONFIG_BLKID_DEBUG #include extern int blkid_debug_mask; #define DBG(m,x) if ((m) & blkid_debug_mask) x; #else #define DBG(m,x) #endif #ifdef CONFIG_BLKID_DEBUG extern void blkid_debug_dump_dev(blkid_dev dev); extern void blkid_debug_dump_tag(blkid_tag tag); #endif /* lseek.c */ /* extern blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence); */ #ifdef CONFIG_LFS # define blkid_llseek lseek64 #else # define blkid_llseek lseek #endif /* read.c */ extern void blkid_read_cache(blkid_cache cache); /* save.c */ extern int blkid_flush_cache(blkid_cache cache); /* * Functions to create and find a specific tag type: tag.c */ extern void blkid_free_tag(blkid_tag tag); extern blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type); extern int blkid_set_tag(blkid_dev dev, const char *name, const char *value, const int vlength); /* * Functions to create and find a specific tag type: dev.c */ extern blkid_dev blkid_new_dev(void); extern void blkid_free_dev(blkid_dev dev); #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/devno.c0000644000000000000000000001107612263563520021025 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * devno.c - find a particular device by its device number (major/minor) * * Copyright (C) 2000, 2001, 2003 Theodore Ts'o * Copyright (C) 2001 Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #ifdef HAVE_SYS_TYPES_H #include #endif #include #include #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_SYS_MKDEV_H #include #endif #include "blkidP.h" struct dir_list { char *name; struct dir_list *next; }; char *blkid_strndup(const char *s, int length) { char *ret; if (!s) return NULL; if (!length) length = strlen(s); ret = xmalloc(length + 1); strncpy(ret, s, length); ret[length] = '\0'; return ret; } char *blkid_strdup(const char *s) { return blkid_strndup(s, 0); } /* * This function adds an entry to the directory list */ static void add_to_dirlist(const char *name, struct dir_list **list) { struct dir_list *dp; dp = xmalloc(sizeof(struct dir_list)); dp->name = blkid_strdup(name); dp->next = *list; *list = dp; } /* * This function frees a directory list */ static void free_dirlist(struct dir_list **list) { struct dir_list *dp, *next; for (dp = *list; dp; dp = next) { next = dp->next; free(dp->name); free(dp); } *list = NULL; } static void scan_dir(char *dir_name, dev_t devno, struct dir_list **list, char **devname) { DIR *dir; struct dirent *dp; char path[1024]; int dirlen; struct stat st; if ((dir = opendir(dir_name)) == NULL) return; dirlen = strlen(dir_name) + 2; while ((dp = readdir(dir)) != 0) { if (dirlen + strlen(dp->d_name) >= sizeof(path)) continue; if (dp->d_name[0] == '.' && ((dp->d_name[1] == 0) || ((dp->d_name[1] == '.') && (dp->d_name[2] == 0)))) continue; sprintf(path, "%s/%s", dir_name, dp->d_name); if (stat(path, &st) < 0) continue; if (S_ISDIR(st.st_mode)) add_to_dirlist(path, list); else if (S_ISBLK(st.st_mode) && st.st_rdev == devno) { *devname = blkid_strdup(path); DBG(DEBUG_DEVNO, printf("found 0x%llx at %s (%p)\n", devno, path, *devname)); break; } } closedir(dir); } /* Directories where we will try to search for device numbers */ const char *blkid_devdirs[] = { "/devices", "/devfs", "/dev", NULL }; /* * This function finds the pathname to a block device with a given * device number. It returns a pointer to allocated memory to the * pathname on success, and NULL on failure. */ char *blkid_devno_to_devname(dev_t devno) { struct dir_list *list = NULL, *new_list = NULL; char *devname = NULL; const char **dir; /* * Add the starting directories to search in reverse order of * importance, since we are using a stack... */ for (dir = blkid_devdirs; *dir; dir++) add_to_dirlist(*dir, &list); while (list) { struct dir_list *current = list; list = list->next; DBG(DEBUG_DEVNO, printf("directory %s\n", current->name)); scan_dir(current->name, devno, &new_list, &devname); free(current->name); free(current); if (devname) break; /* * If we're done checking at this level, descend to * the next level of subdirectories. (breadth-first) */ if (list == NULL) { list = new_list; new_list = NULL; } } free_dirlist(&list); free_dirlist(&new_list); if (!devname) { DBG(DEBUG_DEVNO, printf("blkid: cannot find devno 0x%04lx\n", (unsigned long) devno)); } else { DBG(DEBUG_DEVNO, printf("found devno 0x%04llx as %s\n", devno, devname)); } return devname; } #ifdef TEST_PROGRAM int main(int argc, char** argv) { char *devname, *tmp; int major, minor; dev_t devno; const char *errmsg = "Cannot parse %s: %s\n"; blkid_debug_mask = DEBUG_ALL; if ((argc != 2) && (argc != 3)) { fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n" "Resolve a device number to a device name\n", argv[0], argv[0]); exit(1); } if (argc == 2) { devno = strtoul(argv[1], &tmp, 0); if (*tmp) { fprintf(stderr, errmsg, "device number", argv[1]); exit(1); } } else { major = strtoul(argv[1], &tmp, 0); if (*tmp) { fprintf(stderr, errmsg, "major number", argv[1]); exit(1); } minor = strtoul(argv[2], &tmp, 0); if (*tmp) { fprintf(stderr, errmsg, "minor number", argv[2]); exit(1); } devno = makedev(major, minor); } printf("Looking for device 0x%04Lx\n", devno); devname = blkid_devno_to_devname(devno); free(devname); return 0; } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/Kbuild.src0000644000000000000000000000157212263563520021471 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. NEEDED-$(CONFIG_E2FSCK) = y NEEDED-$(CONFIG_FSCK) = y NEEDED-$(CONFIG_MKE2FS) = y NEEDED-$(CONFIG_TUNE2FS) = y lib-y:= INSERT lib-$(NEEDED-y) += cache.o dev.o devname.o devno.o blkid_getsize.o \ probe.o read.o resolve.o save.o tag.o list.o CFLAGS_dev.o := -include $(srctree)/include/busybox.h CFLAGS_devname.o := -include $(srctree)/include/busybox.h CFLAGS_devno.o := -include $(srctree)/include/busybox.h CFLAGS_blkid_getsize.o := -include $(srctree)/include/busybox.h CFLAGS_probe.o := -include $(srctree)/include/busybox.h CFLAGS_save.o := -include $(srctree)/include/busybox.h CFLAGS_tag.o := -include $(srctree)/include/busybox.h CFLAGS_list.o := -include $(srctree)/include/busybox.h busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/devname.c0000644000000000000000000002061412263563520021327 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * devname.c - get a dev by its device inode name * * Copyright (C) Andries Brouwer * Copyright (C) 1999, 2000, 2001, 2002, 2003 Theodore Ts'o * Copyright (C) 2001 Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #ifdef HAVE_SYS_TYPES_H #include #endif #include #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_SYS_MKDEV_H #include #endif #include #include "blkidP.h" /* * Find a dev struct in the cache by device name, if available. * * If there is no entry with the specified device name, and the create * flag is set, then create an empty device entry. */ blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags) { blkid_dev dev = NULL, tmp; struct list_head *p; if (!cache || !devname) return NULL; list_for_each(p, &cache->bic_devs) { tmp = list_entry(p, struct blkid_struct_dev, bid_devs); if (strcmp(tmp->bid_name, devname)) continue; DBG(DEBUG_DEVNAME, printf("found devname %s in cache\n", tmp->bid_name)); dev = tmp; break; } if (!dev && (flags & BLKID_DEV_CREATE)) { dev = blkid_new_dev(); if (!dev) return NULL; dev->bid_name = blkid_strdup(devname); dev->bid_cache = cache; list_add_tail(&dev->bid_devs, &cache->bic_devs); cache->bic_flags |= BLKID_BIC_FL_CHANGED; } if (flags & BLKID_DEV_VERIFY) dev = blkid_verify(cache, dev); return dev; } /* * Probe a single block device to add to the device cache. */ static void probe_one(blkid_cache cache, const char *ptname, dev_t devno, int pri) { blkid_dev dev = NULL; struct list_head *p; const char **dir; char *devname = NULL; /* See if we already have this device number in the cache. */ list_for_each(p, &cache->bic_devs) { blkid_dev tmp = list_entry(p, struct blkid_struct_dev, bid_devs); if (tmp->bid_devno == devno) { dev = blkid_verify(cache, tmp); break; } } if (dev && dev->bid_devno == devno) goto set_pri; /* * Take a quick look at /dev/ptname for the device number. We check * all of the likely device directories. If we don't find it, or if * the stat information doesn't check out, use blkid_devno_to_devname() * to find it via an exhaustive search for the device major/minor. */ for (dir = blkid_devdirs; *dir; dir++) { struct stat st; char device[256]; sprintf(device, "%s/%s", *dir, ptname); if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) && dev->bid_devno == devno) goto set_pri; if (stat(device, &st) == 0 && S_ISBLK(st.st_mode) && st.st_rdev == devno) { devname = blkid_strdup(device); break; } } if (!devname) { devname = blkid_devno_to_devname(devno); if (!devname) return; } dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL); free(devname); set_pri: if (!pri && !strncmp(ptname, "md", 2)) pri = BLKID_PRI_MD; if (dev) dev->bid_pri = pri; } #define PROC_PARTITIONS "/proc/partitions" #define VG_DIR "/proc/lvm/VGs" /* * This function initializes the UUID cache with devices from the LVM * proc hierarchy. We currently depend on the names of the LVM * hierarchy giving us the device structure in /dev. (XXX is this a * safe thing to do?) */ #ifdef VG_DIR #include static dev_t lvm_get_devno(const char *lvm_device) { FILE *lvf; char buf[1024]; int ma, mi; dev_t ret = 0; DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device)); if ((lvf = fopen_for_read(lvm_device)) == NULL) { DBG(DEBUG_DEVNAME, printf("%s: (%d) %s\n", lvm_device, errno, strerror(errno))); return 0; } while (fgets(buf, sizeof(buf), lvf)) { if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) { ret = makedev(ma, mi); break; } } fclose(lvf); return ret; } static void lvm_probe_all(blkid_cache cache) { DIR *vg_list; struct dirent *vg_iter; int vg_len = strlen(VG_DIR); dev_t dev; if ((vg_list = opendir(VG_DIR)) == NULL) return; DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR)); while ((vg_iter = readdir(vg_list)) != NULL) { DIR *lv_list; char *vdirname; char *vg_name; struct dirent *lv_iter; vg_name = vg_iter->d_name; if (LONE_CHAR(vg_name, '.') || !strcmp(vg_name, "..")) continue; vdirname = xmalloc(vg_len + strlen(vg_name) + 8); sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name); lv_list = opendir(vdirname); free(vdirname); if (lv_list == NULL) continue; while ((lv_iter = readdir(lv_list)) != NULL) { char *lv_name, *lvm_device; lv_name = lv_iter->d_name; if (LONE_CHAR(lv_name, '.') || !strcmp(lv_name, "..")) continue; lvm_device = xmalloc(vg_len + strlen(vg_name) + strlen(lv_name) + 8); sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name, lv_name); dev = lvm_get_devno(lvm_device); sprintf(lvm_device, "%s/%s", vg_name, lv_name); DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n", lvm_device, (unsigned int) dev)); probe_one(cache, lvm_device, dev, BLKID_PRI_LVM); free(lvm_device); } closedir(lv_list); } closedir(vg_list); } #endif #define PROC_EVMS_VOLUMES "/proc/evms/volumes" static int evms_probe_all(blkid_cache cache) { char line[100]; int ma, mi, sz, num = 0; FILE *procpt; char device[110]; procpt = fopen_for_read(PROC_EVMS_VOLUMES); if (!procpt) return 0; while (fgets(line, sizeof(line), procpt)) { if (sscanf(line, " %d %d %d %*s %*s %[^\n ]", &ma, &mi, &sz, device) != 4) continue; DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n", device, ma, mi)); probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS); num++; } fclose(procpt); return num; } /* * Read the device data for all available block devices in the system. */ int blkid_probe_all(blkid_cache cache) { FILE *proc; char line[1024]; char ptname0[128], ptname1[128], *ptname = NULL; char *ptnames[2]; dev_t devs[2]; int ma, mi; unsigned long long sz; int lens[2] = { 0, 0 }; int which = 0, last = 0; ptnames[0] = ptname0; ptnames[1] = ptname1; if (!cache) return -BLKID_ERR_PARAM; if (cache->bic_flags & BLKID_BIC_FL_PROBED && time(NULL) - cache->bic_time < BLKID_PROBE_INTERVAL) return 0; blkid_read_cache(cache); evms_probe_all(cache); #ifdef VG_DIR lvm_probe_all(cache); #endif proc = fopen_for_read(PROC_PARTITIONS); if (!proc) return -BLKID_ERR_PROC; while (fgets(line, sizeof(line), proc)) { last = which; which ^= 1; ptname = ptnames[which]; if (sscanf(line, " %d %d %llu %128[^\n ]", &ma, &mi, &sz, ptname) != 4) continue; devs[which] = makedev(ma, mi); DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname)); /* Skip whole disk devs unless they have no partitions * If we don't have a partition on this dev, also * check previous dev to see if it didn't have a partn. * heuristic: partition name ends in a digit. * * Skip extended partitions. * heuristic: size is 1 * * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs */ lens[which] = strlen(ptname); if (isdigit(ptname[lens[which] - 1])) { DBG(DEBUG_DEVNAME, printf("partition dev %s, devno 0x%04X\n", ptname, (unsigned int) devs[which])); if (sz > 1) probe_one(cache, ptname, devs[which], 0); lens[which] = 0; lens[last] = 0; } else if (lens[last] && strncmp(ptnames[last], ptname, lens[last])) { DBG(DEBUG_DEVNAME, printf("whole dev %s, devno 0x%04X\n", ptnames[last], (unsigned int) devs[last])); probe_one(cache, ptnames[last], devs[last], 0); lens[last] = 0; } } /* Handle the last device if it wasn't partitioned */ if (lens[which]) probe_one(cache, ptname, devs[which], 0); fclose(proc); cache->bic_time = time(NULL); cache->bic_flags |= BLKID_BIC_FL_PROBED; blkid_flush_cache(cache); return 0; } #ifdef TEST_PROGRAM int main(int argc, char **argv) { blkid_cache cache = NULL; int ret; blkid_debug_mask = DEBUG_ALL; if (argc != 1) { fprintf(stderr, "Usage: %s\n" "Probe all devices and exit\n", argv[0]); exit(1); } if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } if (blkid_probe_all(cache) < 0) printf("%s: error probing devices\n", argv[0]); blkid_put_cache(cache); return 0; } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/tag.c0000644000000000000000000002314612263563520020466 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * tag.c - allocation/initialization/free routines for tag structs * * Copyright (C) 2001 Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #include #include "blkidP.h" static blkid_tag blkid_new_tag(void) { blkid_tag tag; tag = xzalloc(sizeof(struct blkid_struct_tag)); INIT_LIST_HEAD(&tag->bit_tags); INIT_LIST_HEAD(&tag->bit_names); return tag; } #ifdef CONFIG_BLKID_DEBUG void blkid_debug_dump_tag(blkid_tag tag) { if (!tag) { printf(" tag: NULL\n"); return; } printf(" tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val); } #endif void blkid_free_tag(blkid_tag tag) { if (!tag) return; DBG(DEBUG_TAG, printf(" freeing tag %s=%s\n", tag->bit_name, tag->bit_val ? tag->bit_val : "(NULL)")); DBG(DEBUG_TAG, blkid_debug_dump_tag(tag)); list_del(&tag->bit_tags); /* list of tags for this device */ list_del(&tag->bit_names); /* list of tags with this type */ free(tag->bit_name); free(tag->bit_val); free(tag); } /* * Find the desired tag on a device. If value is NULL, then the * first such tag is returned, otherwise return only exact tag if found. */ blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type) { struct list_head *p; if (!dev || !type) return NULL; list_for_each(p, &dev->bid_tags) { blkid_tag tmp = list_entry(p, struct blkid_struct_tag, bit_tags); if (!strcmp(tmp->bit_name, type)) return tmp; } return NULL; } /* * Find the desired tag type in the cache. * We return the head tag for this tag type. */ static blkid_tag blkid_find_head_cache(blkid_cache cache, const char *type) { blkid_tag head = NULL, tmp; struct list_head *p; if (!cache || !type) return NULL; list_for_each(p, &cache->bic_tags) { tmp = list_entry(p, struct blkid_struct_tag, bit_tags); if (!strcmp(tmp->bit_name, type)) { DBG(DEBUG_TAG, printf(" found cache tag head %s\n", type)); head = tmp; break; } } return head; } /* * Set a tag on an existing device. * * If value is NULL, then delete the tagsfrom the device. */ int blkid_set_tag(blkid_dev dev, const char *name, const char *value, const int vlength) { blkid_tag t = 0, head = 0; char *val = NULL; if (!dev || !name) return -BLKID_ERR_PARAM; if (!(val = blkid_strndup(value, vlength)) && value) return -BLKID_ERR_MEM; t = blkid_find_tag_dev(dev, name); if (!value) { blkid_free_tag(t); } else if (t) { if (!strcmp(t->bit_val, val)) { /* Same thing, exit */ free(val); return 0; } free(t->bit_val); t->bit_val = val; } else { /* Existing tag not present, add to device */ if (!(t = blkid_new_tag())) goto errout; t->bit_name = blkid_strdup(name); t->bit_val = val; t->bit_dev = dev; list_add_tail(&t->bit_tags, &dev->bid_tags); if (dev->bid_cache) { head = blkid_find_head_cache(dev->bid_cache, t->bit_name); if (!head) { head = blkid_new_tag(); if (!head) goto errout; DBG(DEBUG_TAG, printf(" creating new cache tag head %s\n", name)); head->bit_name = blkid_strdup(name); if (!head->bit_name) goto errout; list_add_tail(&head->bit_tags, &dev->bid_cache->bic_tags); } list_add_tail(&t->bit_names, &head->bit_names); } } /* Link common tags directly to the device struct */ if (!strcmp(name, "TYPE")) dev->bid_type = val; else if (!strcmp(name, "LABEL")) dev->bid_label = val; else if (!strcmp(name, "UUID")) dev->bid_uuid = val; if (dev->bid_cache) dev->bid_cache->bic_flags |= BLKID_BIC_FL_CHANGED; return 0; errout: blkid_free_tag(t); if (!t) free(val); blkid_free_tag(head); return -BLKID_ERR_MEM; } /* * Parse a "NAME=value" string. This is slightly different than * parse_token, because that will end an unquoted value at a space, while * this will assume that an unquoted value is the rest of the token (e.g. * if we are passed an already quoted string from the command-line we don't * have to both quote and escape quote so that the quotes make it to * us). * * Returns 0 on success, and -1 on failure. */ int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val) { char *name, *value, *cp; DBG(DEBUG_TAG, printf("trying to parse '%s' as a tag\n", token)); if (!token || !(cp = strchr(token, '='))) return -1; name = blkid_strdup(token); if (!name) return -1; value = name + (cp - token); *value++ = '\0'; if (*value == '"' || *value == '\'') { char c = *value++; if (!(cp = strrchr(value, c))) goto errout; /* missing closing quote */ *cp = '\0'; } value = blkid_strdup(value); if (!value) goto errout; *ret_type = name; *ret_val = value; return 0; errout: free(name); return -1; } /* * Tag iteration routines for the public libblkid interface. * * These routines do not expose the list.h implementation, which are a * contamination of the namespace, and which force us to reveal far, far * too much of our internal implemenation. I'm not convinced I want * to keep list.h in the long term, anyway. It's fine for kernel * programming, but performance is not the #1 priority for this * library, and I really don't like the tradeoff of type-safety for * performance for this application. [tytso:20030125.2007EST] */ /* * This series of functions iterate over all tags in a device */ #define TAG_ITERATE_MAGIC 0x01a5284c struct blkid_struct_tag_iterate { int magic; blkid_dev dev; struct list_head *p; }; blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev) { blkid_tag_iterate iter; iter = xmalloc(sizeof(struct blkid_struct_tag_iterate)); iter->magic = TAG_ITERATE_MAGIC; iter->dev = dev; iter->p = dev->bid_tags.next; return iter; } /* * Return 0 on success, -1 on error */ extern int blkid_tag_next(blkid_tag_iterate iter, const char **type, const char **value) { blkid_tag tag; *type = 0; *value = 0; if (!iter || iter->magic != TAG_ITERATE_MAGIC || iter->p == &iter->dev->bid_tags) return -1; tag = list_entry(iter->p, struct blkid_struct_tag, bit_tags); *type = tag->bit_name; *value = tag->bit_val; iter->p = iter->p->next; return 0; } void blkid_tag_iterate_end(blkid_tag_iterate iter) { if (!iter || iter->magic != TAG_ITERATE_MAGIC) return; iter->magic = 0; free(iter); } /* * This function returns a device which matches a particular * type/value pair. If there is more than one device that matches the * search specification, it returns the one with the highest priority * value. This allows us to give preference to EVMS or LVM devices. * * XXX there should also be an interface which uses an iterator so we * can get all of the devices which match a type/value search parameter. */ extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache, const char *type, const char *value) { blkid_tag head; blkid_dev dev; int pri; struct list_head *p; if (!cache || !type || !value) return NULL; blkid_read_cache(cache); DBG(DEBUG_TAG, printf("looking for %s=%s in cache\n", type, value)); try_again: pri = -1; dev = 0; head = blkid_find_head_cache(cache, type); if (head) { list_for_each(p, &head->bit_names) { blkid_tag tmp = list_entry(p, struct blkid_struct_tag, bit_names); if (!strcmp(tmp->bit_val, value) && tmp->bit_dev->bid_pri > pri) { dev = tmp->bit_dev; pri = dev->bid_pri; } } } if (dev && !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) { dev = blkid_verify(cache, dev); if (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED)) goto try_again; } if (!dev && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) { if (blkid_probe_all(cache) < 0) return NULL; goto try_again; } return dev; } #ifdef TEST_PROGRAM #ifdef HAVE_GETOPT_H #include #else extern char *optarg; extern int optind; #endif void usage(char *prog) { fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask] device " "[type value]\n", prog); fprintf(stderr, "\tList all tags for a device and exit\n"); exit(1); } int main(int argc, char **argv) { blkid_tag_iterate iter; blkid_cache cache = NULL; blkid_dev dev; int c, ret, found; int flags = BLKID_DEV_FIND; char *tmp; char *file = NULL; char *devname = NULL; char *search_type = NULL; char *search_value = NULL; const char *type, *value; while ((c = getopt (argc, argv, "m:f:")) != EOF) switch (c) { case 'f': file = optarg; break; case 'm': blkid_debug_mask = strtoul (optarg, &tmp, 0); if (*tmp) { fprintf(stderr, "Invalid debug mask: %s\n", optarg); exit(1); } break; case '?': usage(argv[0]); } if (argc > optind) devname = argv[optind++]; if (argc > optind) search_type = argv[optind++]; if (argc > optind) search_value = argv[optind++]; if (!devname || (argc != optind)) usage(argv[0]); if ((ret = blkid_get_cache(&cache, file)) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } dev = blkid_get_dev(cache, devname, flags); if (!dev) { fprintf(stderr, "%s: cannot find device in blkid cache\n", devname); exit(1); } if (search_type) { found = blkid_dev_has_tag(dev, search_type, search_value); printf("Device %s: (%s, %s) %s\n", blkid_dev_devname(dev), search_type, search_value ? search_value : "NULL", found ? "FOUND" : "NOT FOUND"); return !found; } printf("Device %s...\n", blkid_dev_devname(dev)); iter = blkid_tag_iterate_begin(dev); while (blkid_tag_next(iter, &type, &value) == 0) { printf("\tTag %s has value %s\n", type, value); } blkid_tag_iterate_end(iter); blkid_put_cache(cache); return 0; } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/list.h0000644000000000000000000000442212263563520020667 0ustar rootroot/* vi: set sw=4 ts=4: */ #if !defined(_BLKID_LIST_H) && !defined(LIST_HEAD) #define BLKID_LIST_H 1 #ifdef __cplusplus extern "C" { #endif /* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) void __list_add(struct list_head * add, struct list_head * prev, struct list_head * next); void list_add(struct list_head *add, struct list_head *head); void list_add_tail(struct list_head *add, struct list_head *head); void __list_del(struct list_head * prev, struct list_head * next); void list_del(struct list_head *entry); void list_del_init(struct list_head *entry); int list_empty(struct list_head *head); void list_splice(struct list_head *list, struct list_head *head); /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) /** * list_for_each - iterate over elements in a list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * list_for_each_safe - iterate over elements in a list, but don't dereference * pos after the body is done (in case it is freed) * @pos: the &struct list_head to use as a loop counter. * @pnext: the &struct list_head to use as a pointer to the next item. * @head: the head for your list (not included in iteration). */ #define list_for_each_safe(pos, pnext, head) \ for (pos = (head)->next, pnext = pos->next; pos != (head); \ pos = pnext, pnext = pos->next) #ifdef __cplusplus } #endif #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/blkid.h0000644000000000000000000000601012263563520020774 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * blkid.h - Interface for libblkid, a library to identify block devices * * Copyright (C) 2001 Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #ifndef BLKID_BLKID_H #define BLKID_BLKID_H 1 #include #include #ifdef __cplusplus extern "C" { #endif #define BLKID_VERSION "1.0.0" #define BLKID_DATE "12-Feb-2003" typedef struct blkid_struct_dev *blkid_dev; typedef struct blkid_struct_cache *blkid_cache; typedef __s64 blkid_loff_t; typedef struct blkid_struct_tag_iterate *blkid_tag_iterate; typedef struct blkid_struct_dev_iterate *blkid_dev_iterate; /* * Flags for blkid_get_dev * * BLKID_DEV_CREATE Create an empty device structure if not found * in the cache. * BLKID_DEV_VERIFY Make sure the device structure corresponds * with reality. * BLKID_DEV_FIND Just look up a device entry, and return NULL * if it is not found. * BLKID_DEV_NORMAL Get a valid device structure, either from the * cache or by probing the device. */ #define BLKID_DEV_FIND 0x0000 #define BLKID_DEV_CREATE 0x0001 #define BLKID_DEV_VERIFY 0x0002 #define BLKID_DEV_NORMAL (BLKID_DEV_CREATE | BLKID_DEV_VERIFY) /* cache.c */ extern void blkid_put_cache(blkid_cache cache); extern int blkid_get_cache(blkid_cache *cache, const char *filename); /* dev.c */ extern const char *blkid_dev_devname(blkid_dev dev); extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache); extern int blkid_dev_set_search(blkid_dev_iterate iter, char *search_type, char *search_value); extern int blkid_dev_next(blkid_dev_iterate iterate, blkid_dev *dev); extern void blkid_dev_iterate_end(blkid_dev_iterate iterate); /* devno.c */ extern char *blkid_devno_to_devname(dev_t devno); /* devname.c */ extern int blkid_probe_all(blkid_cache cache); extern int blkid_probe_all_new(blkid_cache cache); extern blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags); /* getsize.c */ extern blkid_loff_t blkid_get_dev_size(int fd); /* probe.c */ int blkid_known_fstype(const char *fstype); extern blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev); /* read.c */ /* resolve.c */ extern char *blkid_get_tag_value(blkid_cache cache, const char *tagname, const char *devname); extern char *blkid_get_devname(blkid_cache cache, const char *token, const char *value); /* tag.c */ extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev); extern int blkid_tag_next(blkid_tag_iterate iterate, const char **type, const char **value); extern void blkid_tag_iterate_end(blkid_tag_iterate iterate); extern int blkid_dev_has_tag(blkid_dev dev, const char *type, const char *value); extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache, const char *type, const char *value); extern int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val); #ifdef __cplusplus } #endif #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/resolve.c0000644000000000000000000000574112263563520021373 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * resolve.c - resolve names and tags into specific devices * * Copyright (C) 2001, 2003 Theodore Ts'o. * Copyright (C) 2001 Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include "blkidP.h" #include "probe.h" /* * Find a tagname (e.g. LABEL or UUID) on a specific device. */ char *blkid_get_tag_value(blkid_cache cache, const char *tagname, const char *devname) { blkid_tag found; blkid_dev dev; blkid_cache c = cache; char *ret = NULL; DBG(DEBUG_RESOLVE, printf("looking for %s on %s\n", tagname, devname)); if (!devname) return NULL; if (!cache) { if (blkid_get_cache(&c, NULL) < 0) return NULL; } if ((dev = blkid_get_dev(c, devname, BLKID_DEV_NORMAL)) && (found = blkid_find_tag_dev(dev, tagname))) ret = blkid_strdup(found->bit_val); if (!cache) blkid_put_cache(c); return ret; } /* * Locate a device name from a token (NAME=value string), or (name, value) * pair. In the case of a token, value is ignored. If the "token" is not * of the form "NAME=value" and there is no value given, then it is assumed * to be the actual devname and a copy is returned. */ char *blkid_get_devname(blkid_cache cache, const char *token, const char *value) { blkid_dev dev; blkid_cache c = cache; char *t = NULL, *v = NULL; char *ret = NULL; if (!token) return NULL; if (!cache) { if (blkid_get_cache(&c, NULL) < 0) return NULL; } DBG(DEBUG_RESOLVE, printf("looking for %s%s%s %s\n", token, value ? "=" : "", value ? value : "", cache ? "in cache" : "from disk")); if (!value) { if (!strchr(token, '=')) return blkid_strdup(token); blkid_parse_tag_string(token, &t, &v); if (!t || !v) goto errout; token = t; value = v; } dev = blkid_find_dev_with_tag(c, token, value); if (!dev) goto errout; ret = blkid_strdup(blkid_dev_devname(dev)); errout: free(t); free(v); if (!cache) { blkid_put_cache(c); } return ret; } #ifdef TEST_PROGRAM int main(int argc, char **argv) { char *value; blkid_cache cache; blkid_debug_mask = DEBUG_ALL; if (argc != 2 && argc != 3) { fprintf(stderr, "Usage:\t%s tagname=value\n" "\t%s tagname devname\n" "Find which device holds a given token or\n" "Find what the value of a tag is in a device\n", argv[0], argv[0]); exit(1); } if (blkid_get_cache(&cache, bb_dev_null) < 0) { fprintf(stderr, "Can't get blkid cache\n"); exit(1); } if (argv[2]) { value = blkid_get_tag_value(cache, argv[1], argv[2]); printf("%s has tag %s=%s\n", argv[2], argv[1], value ? value : ""); } else { value = blkid_get_devname(cache, argv[1], NULL); printf("%s has tag %s\n", value ? value : "", argv[1]); } blkid_put_cache(cache); return value ? 0 : 1; } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/dev.c0000644000000000000000000001100712263563520020462 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * dev.c - allocation/initialization/free routines for dev * * Copyright (C) 2001 Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #include "blkidP.h" blkid_dev blkid_new_dev(void) { blkid_dev dev; dev = xzalloc(sizeof(struct blkid_struct_dev)); INIT_LIST_HEAD(&dev->bid_devs); INIT_LIST_HEAD(&dev->bid_tags); return dev; } void blkid_free_dev(blkid_dev dev) { if (!dev) return; DBG(DEBUG_DEV, printf(" freeing dev %s (%s)\n", dev->bid_name, dev->bid_type)); DBG(DEBUG_DEV, blkid_debug_dump_dev(dev)); list_del(&dev->bid_devs); while (!list_empty(&dev->bid_tags)) { blkid_tag tag = list_entry(dev->bid_tags.next, struct blkid_struct_tag, bit_tags); blkid_free_tag(tag); } if (dev->bid_name) free(dev->bid_name); free(dev); } /* * Given a blkid device, return its name */ const char *blkid_dev_devname(blkid_dev dev) { return dev->bid_name; } #ifdef CONFIG_BLKID_DEBUG void blkid_debug_dump_dev(blkid_dev dev) { struct list_head *p; if (!dev) { printf(" dev: NULL\n"); return; } printf(" dev: name = %s\n", dev->bid_name); printf(" dev: DEVNO=\"0x%0llx\"\n", dev->bid_devno); printf(" dev: TIME=\"%lu\"\n", dev->bid_time); printf(" dev: PRI=\"%d\"\n", dev->bid_pri); printf(" dev: flags = 0x%08X\n", dev->bid_flags); list_for_each(p, &dev->bid_tags) { blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags); if (tag) printf(" tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val); else printf(" tag: NULL\n"); } bb_putchar('\n'); } #endif /* * dev iteration routines for the public libblkid interface. * * These routines do not expose the list.h implementation, which are a * contamination of the namespace, and which force us to reveal far, far * too much of our internal implemenation. I'm not convinced I want * to keep list.h in the long term, anyway. It's fine for kernel * programming, but performance is not the #1 priority for this * library, and I really don't like the tradeoff of type-safety for * performance for this application. [tytso:20030125.2007EST] */ /* * This series of functions iterate over all devices in a blkid cache */ #define DEV_ITERATE_MAGIC 0x01a5284c struct blkid_struct_dev_iterate { int magic; blkid_cache cache; struct list_head *p; }; blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache) { blkid_dev_iterate iter; iter = xmalloc(sizeof(struct blkid_struct_dev_iterate)); iter->magic = DEV_ITERATE_MAGIC; iter->cache = cache; iter->p = cache->bic_devs.next; return iter; } /* * Return 0 on success, -1 on error */ extern int blkid_dev_next(blkid_dev_iterate iter, blkid_dev *dev) { *dev = 0; if (!iter || iter->magic != DEV_ITERATE_MAGIC || iter->p == &iter->cache->bic_devs) return -1; *dev = list_entry(iter->p, struct blkid_struct_dev, bid_devs); iter->p = iter->p->next; return 0; } void blkid_dev_iterate_end(blkid_dev_iterate iter) { if (!iter || iter->magic != DEV_ITERATE_MAGIC) return; iter->magic = 0; free(iter); } #ifdef TEST_PROGRAM #ifdef HAVE_GETOPT_H #include #else extern char *optarg; extern int optind; #endif void usage(char *prog) { fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask]\n", prog); fprintf(stderr, "\tList all devices and exit\n"); exit(1); } int main(int argc, char **argv) { blkid_dev_iterate iter; blkid_cache cache = NULL; blkid_dev dev; int c, ret; char *tmp; char *file = NULL; char *search_type = NULL; char *search_value = NULL; while ((c = getopt (argc, argv, "m:f:")) != EOF) switch (c) { case 'f': file = optarg; break; case 'm': blkid_debug_mask = strtoul (optarg, &tmp, 0); if (*tmp) { fprintf(stderr, "Invalid debug mask: %s\n", optarg); exit(1); } break; case '?': usage(argv[0]); } if (argc >= optind+2) { search_type = argv[optind]; search_value = argv[optind+1]; optind += 2; } if (argc != optind) usage(argv[0]); if ((ret = blkid_get_cache(&cache, file)) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } iter = blkid_dev_iterate_begin(cache); if (search_type) blkid_dev_set_search(iter, search_type, search_value); while (blkid_dev_next(iter, &dev) == 0) { printf("Device: %s\n", blkid_dev_devname(dev)); } blkid_dev_iterate_end(iter); blkid_put_cache(cache); return 0; } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/probe.c0000644000000000000000000004744712263563520021034 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * probe.c - identify a block device by its contents, and return a dev * struct with the details * * Copyright (C) 1999 by Andries Brouwer * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o * Copyright (C) 2001 by Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #include #include #include #include #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_MKDEV_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #include "blkidP.h" #include "../uuid/uuid.h" #include "probe.h" /* * This is a special case code to check for an MDRAID device. We do * this special since it requires checking for a superblock at the end * of the device. */ static int check_mdraid(int fd, unsigned char *ret_uuid) { struct mdp_superblock_s *md; blkid_loff_t offset; char buf[4096]; if (fd < 0) return -BLKID_ERR_PARAM; offset = (blkid_get_dev_size(fd) & ~((blkid_loff_t)65535)) - 65536; if (blkid_llseek(fd, offset, 0) < 0 || read(fd, buf, 4096) != 4096) return -BLKID_ERR_IO; /* Check for magic number */ if (memcmp("\251+N\374", buf, 4)) return -BLKID_ERR_PARAM; if (!ret_uuid) return 0; *ret_uuid = 0; /* The MD UUID is not contiguous in the superblock, make it so */ md = (struct mdp_superblock_s *)buf; if (md->set_uuid0 || md->set_uuid1 || md->set_uuid2 || md->set_uuid3) { memcpy(ret_uuid, &md->set_uuid0, 4); memcpy(ret_uuid, &md->set_uuid1, 12); } return 0; } static void set_uuid(blkid_dev dev, uuid_t uuid) { char str[37]; if (!uuid_is_null(uuid)) { uuid_unparse(uuid, str); blkid_set_tag(dev, "UUID", str, sizeof(str)); } } static void get_ext2_info(blkid_dev dev, unsigned char *buf) { struct ext2_super_block *es = (struct ext2_super_block *) buf; const char *label = NULL; DBG(DEBUG_PROBE, printf("ext2_sb.compat = %08X:%08X:%08X\n", blkid_le32(es->s_feature_compat), blkid_le32(es->s_feature_incompat), blkid_le32(es->s_feature_ro_compat))); if (strlen(es->s_volume_name)) label = es->s_volume_name; blkid_set_tag(dev, "LABEL", label, sizeof(es->s_volume_name)); set_uuid(dev, es->s_uuid); } static int probe_ext3(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, const struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct ext2_super_block *es; es = (struct ext2_super_block *)buf; /* Distinguish between jbd and ext2/3 fs */ if (blkid_le32(es->s_feature_incompat) & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) return -BLKID_ERR_PARAM; /* Distinguish between ext3 and ext2 */ if (!(blkid_le32(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) return -BLKID_ERR_PARAM; get_ext2_info(dev, buf); blkid_set_tag(dev, "SEC_TYPE", "ext2", sizeof("ext2")); return 0; } static int probe_ext2(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, const struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct ext2_super_block *es; es = (struct ext2_super_block *)buf; /* Distinguish between jbd and ext2/3 fs */ if (blkid_le32(es->s_feature_incompat) & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) return -BLKID_ERR_PARAM; get_ext2_info(dev, buf); return 0; } static int probe_jbd(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, const struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct ext2_super_block *es = (struct ext2_super_block *) buf; if (!(blkid_le32(es->s_feature_incompat) & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) return -BLKID_ERR_PARAM; get_ext2_info(dev, buf); return 0; } static int probe_vfat(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, const struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct vfat_super_block *vs; char serno[10]; const char *label = NULL; int label_len = 0; vs = (struct vfat_super_block *)buf; if (strncmp(vs->vs_label, "NO NAME", 7)) { char *end = vs->vs_label + sizeof(vs->vs_label) - 1; while (*end == ' ' && end >= vs->vs_label) --end; if (end >= vs->vs_label) { label = vs->vs_label; label_len = end - vs->vs_label + 1; } } /* We can't just print them as %04X, because they are unaligned */ sprintf(serno, "%02X%02X-%02X%02X", vs->vs_serno[3], vs->vs_serno[2], vs->vs_serno[1], vs->vs_serno[0]); blkid_set_tag(dev, "LABEL", label, label_len); blkid_set_tag(dev, "UUID", serno, sizeof(serno)); return 0; } static int probe_msdos(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, const struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct msdos_super_block *ms = (struct msdos_super_block *) buf; char serno[10]; const char *label = NULL; int label_len = 0; if (strncmp(ms->ms_label, "NO NAME", 7)) { char *end = ms->ms_label + sizeof(ms->ms_label) - 1; while (*end == ' ' && end >= ms->ms_label) --end; if (end >= ms->ms_label) { label = ms->ms_label; label_len = end - ms->ms_label + 1; } } /* We can't just print them as %04X, because they are unaligned */ sprintf(serno, "%02X%02X-%02X%02X", ms->ms_serno[3], ms->ms_serno[2], ms->ms_serno[1], ms->ms_serno[0]); blkid_set_tag(dev, "UUID", serno, 0); blkid_set_tag(dev, "LABEL", label, label_len); blkid_set_tag(dev, "SEC_TYPE", "msdos", sizeof("msdos")); return 0; } static int probe_xfs(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, const struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct xfs_super_block *xs; const char *label = NULL; xs = (struct xfs_super_block *)buf; if (strlen(xs->xs_fname)) label = xs->xs_fname; blkid_set_tag(dev, "LABEL", label, sizeof(xs->xs_fname)); set_uuid(dev, xs->xs_uuid); return 0; } static int probe_reiserfs(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, const struct blkid_magic *id, unsigned char *buf) { struct reiserfs_super_block *rs = (struct reiserfs_super_block *) buf; unsigned int blocksize; const char *label = NULL; blocksize = blkid_le16(rs->rs_blocksize); /* If the superblock is inside the journal, we have the wrong one */ if (id->bim_kboff/(blocksize>>10) > blkid_le32(rs->rs_journal_block)) return -BLKID_ERR_BIG; /* LABEL/UUID are only valid for later versions of Reiserfs v3.6. */ if (!strcmp(id->bim_magic, "ReIsEr2Fs") || !strcmp(id->bim_magic, "ReIsEr3Fs")) { if (strlen(rs->rs_label)) label = rs->rs_label; set_uuid(dev, rs->rs_uuid); } blkid_set_tag(dev, "LABEL", label, sizeof(rs->rs_label)); return 0; } static int probe_jfs(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, const struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct jfs_super_block *js; const char *label = NULL; js = (struct jfs_super_block *)buf; if (strlen((char *) js->js_label)) label = (char *) js->js_label; blkid_set_tag(dev, "LABEL", label, sizeof(js->js_label)); set_uuid(dev, js->js_uuid); return 0; } static int probe_romfs(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, const struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct romfs_super_block *ros; const char *label = NULL; ros = (struct romfs_super_block *)buf; if (strlen((char *) ros->ros_volume)) label = (char *) ros->ros_volume; blkid_set_tag(dev, "LABEL", label, 0); return 0; } static int probe_cramfs(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, const struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct cramfs_super_block *csb; const char *label = NULL; csb = (struct cramfs_super_block *)buf; if (strlen((char *) csb->name)) label = (char *) csb->name; blkid_set_tag(dev, "LABEL", label, 0); return 0; } static int probe_swap0(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, const struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf __BLKID_ATTR((unused))) { blkid_set_tag(dev, "UUID", 0, 0); blkid_set_tag(dev, "LABEL", 0, 0); return 0; } static int probe_swap1(int fd, blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, const struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf __BLKID_ATTR((unused))) { struct swap_id_block *sws; probe_swap0(fd, cache, dev, id, buf); /* * Version 1 swap headers are always located at offset of 1024 * bytes, although the swap signature itself is located at the * end of the page (which may vary depending on hardware * pagesize). */ if (lseek(fd, 1024, SEEK_SET) < 0) return 1; sws = xmalloc(1024); if (read(fd, sws, 1024) != 1024) { free(sws); return 1; } /* arbitrary sanity check.. is there any garbage down there? */ if (sws->sws_pad[32] == 0 && sws->sws_pad[33] == 0) { if (sws->sws_volume[0]) blkid_set_tag(dev, "LABEL", (const char*)sws->sws_volume, sizeof(sws->sws_volume)); if (sws->sws_uuid[0]) set_uuid(dev, sws->sws_uuid); } free(sws); return 0; } static const char * const udf_magic[] = { "BEA01", "BOOT2", "CD001", "CDW02", "NSR02", "NSR03", "TEA01", 0 }; static int probe_udf(int fd, blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev __BLKID_ATTR((unused)), const struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf __BLKID_ATTR((unused))) { int j, bs; struct iso_volume_descriptor isosb; const char *const *m; /* determine the block size by scanning in 2K increments (block sizes larger than 2K will be null padded) */ for (bs = 1; bs < 16; bs++) { lseek(fd, bs*2048+32768, SEEK_SET); if (read(fd, (char *)&isosb, sizeof(isosb)) != sizeof(isosb)) return 1; if (isosb.id[0]) break; } /* Scan up to another 64 blocks looking for additional VSD's */ for (j = 1; j < 64; j++) { if (j > 1) { lseek(fd, j*bs*2048+32768, SEEK_SET); if (read(fd, (char *)&isosb, sizeof(isosb)) != sizeof(isosb)) return 1; } /* If we find NSR0x then call it udf: NSR01 for UDF 1.00 NSR02 for UDF 1.50 NSR03 for UDF 2.00 */ if (!strncmp(isosb.id, "NSR0", 4)) return 0; for (m = udf_magic; *m; m++) if (!strncmp(*m, isosb.id, 5)) break; if (*m == 0) return 1; } return 1; } static int probe_ocfs(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, const struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct ocfs_volume_header ovh; struct ocfs_volume_label ovl; __u32 major; memcpy(&ovh, buf, sizeof(ovh)); memcpy(&ovl, buf+512, sizeof(ovl)); major = ocfsmajor(ovh); if (major == 1) blkid_set_tag(dev, "SEC_TYPE", "ocfs1", sizeof("ocfs1")); else if (major >= 9) blkid_set_tag(dev, "SEC_TYPE", "ntocfs", sizeof("ntocfs")); blkid_set_tag(dev, "LABEL", (const char*)ovl.label, ocfslabellen(ovl)); blkid_set_tag(dev, "MOUNT", (const char*)ovh.mount, ocfsmountlen(ovh)); set_uuid(dev, ovl.vol_id); return 0; } static int probe_ocfs2(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, const struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct ocfs2_super_block *osb; osb = (struct ocfs2_super_block *)buf; blkid_set_tag(dev, "LABEL", (const char*)osb->s_label, sizeof(osb->s_label)); set_uuid(dev, osb->s_uuid); return 0; } static int probe_oracleasm(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, const struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct oracle_asm_disk_label *dl; dl = (struct oracle_asm_disk_label *)buf; blkid_set_tag(dev, "LABEL", dl->dl_id, sizeof(dl->dl_id)); return 0; } /* * BLKID_BLK_OFFS is at least as large as the highest bim_kboff defined * in the type_array table below + bim_kbalign. * * When probing for a lot of magics, we handle everything in 1kB buffers so * that we don't have to worry about reading each combination of block sizes. */ #define BLKID_BLK_OFFS 64 /* currently reiserfs */ /* * Various filesystem magics that we can check for. Note that kboff and * sboff are in kilobytes and bytes respectively. All magics are in * byte strings so we don't worry about endian issues. */ static const struct blkid_magic type_array[] = { /* type kboff sboff len magic probe */ { "oracleasm", 0, 32, 8, "ORCLDISK", probe_oracleasm }, { "ntfs", 0, 3, 8, "NTFS ", 0 }, { "jbd", 1, 0x38, 2, "\123\357", probe_jbd }, { "ext3", 1, 0x38, 2, "\123\357", probe_ext3 }, { "ext2", 1, 0x38, 2, "\123\357", probe_ext2 }, { "reiserfs", 8, 0x34, 8, "ReIsErFs", probe_reiserfs }, { "reiserfs", 64, 0x34, 9, "ReIsEr2Fs", probe_reiserfs }, { "reiserfs", 64, 0x34, 9, "ReIsEr3Fs", probe_reiserfs }, { "reiserfs", 64, 0x34, 8, "ReIsErFs", probe_reiserfs }, { "reiserfs", 8, 20, 8, "ReIsErFs", probe_reiserfs }, { "vfat", 0, 0x52, 5, "MSWIN", probe_vfat }, { "vfat", 0, 0x52, 8, "FAT32 ", probe_vfat }, { "vfat", 0, 0x36, 5, "MSDOS", probe_msdos }, { "vfat", 0, 0x36, 8, "FAT16 ", probe_msdos }, { "vfat", 0, 0x36, 8, "FAT12 ", probe_msdos }, { "minix", 1, 0x10, 2, "\177\023", 0 }, { "minix", 1, 0x10, 2, "\217\023", 0 }, { "minix", 1, 0x10, 2, "\150\044", 0 }, { "minix", 1, 0x10, 2, "\170\044", 0 }, { "vxfs", 1, 0, 4, "\365\374\001\245", 0 }, { "xfs", 0, 0, 4, "XFSB", probe_xfs }, { "romfs", 0, 0, 8, "-rom1fs-", probe_romfs }, { "bfs", 0, 0, 4, "\316\372\173\033", 0 }, { "cramfs", 0, 0, 4, "E=\315\050", probe_cramfs }, { "qnx4", 0, 4, 6, "QNX4FS", 0 }, { "udf", 32, 1, 5, "BEA01", probe_udf }, { "udf", 32, 1, 5, "BOOT2", probe_udf }, { "udf", 32, 1, 5, "CD001", probe_udf }, { "udf", 32, 1, 5, "CDW02", probe_udf }, { "udf", 32, 1, 5, "NSR02", probe_udf }, { "udf", 32, 1, 5, "NSR03", probe_udf }, { "udf", 32, 1, 5, "TEA01", probe_udf }, { "iso9660", 32, 1, 5, "CD001", 0 }, { "iso9660", 32, 9, 5, "CDROM", 0 }, { "jfs", 32, 0, 4, "JFS1", probe_jfs }, { "hfs", 1, 0, 2, "BD", 0 }, { "ufs", 8, 0x55c, 4, "T\031\001\000", 0 }, { "hpfs", 8, 0, 4, "I\350\225\371", 0 }, { "sysv", 0, 0x3f8, 4, "\020~\030\375", 0 }, { "swap", 0, 0xff6, 10, "SWAP-SPACE", probe_swap0 }, { "swap", 0, 0xff6, 10, "SWAPSPACE2", probe_swap1 }, { "swap", 0, 0x1ff6, 10, "SWAP-SPACE", probe_swap0 }, { "swap", 0, 0x1ff6, 10, "SWAPSPACE2", probe_swap1 }, { "swap", 0, 0x3ff6, 10, "SWAP-SPACE", probe_swap0 }, { "swap", 0, 0x3ff6, 10, "SWAPSPACE2", probe_swap1 }, { "swap", 0, 0x7ff6, 10, "SWAP-SPACE", probe_swap0 }, { "swap", 0, 0x7ff6, 10, "SWAPSPACE2", probe_swap1 }, { "swap", 0, 0xfff6, 10, "SWAP-SPACE", probe_swap0 }, { "swap", 0, 0xfff6, 10, "SWAPSPACE2", probe_swap1 }, { "ocfs", 0, 8, 9, "OracleCFS", probe_ocfs }, { "ocfs2", 1, 0, 6, "OCFSV2", probe_ocfs2 }, { "ocfs2", 2, 0, 6, "OCFSV2", probe_ocfs2 }, { "ocfs2", 4, 0, 6, "OCFSV2", probe_ocfs2 }, { "ocfs2", 8, 0, 6, "OCFSV2", probe_ocfs2 }, { NULL, 0, 0, 0, NULL, NULL } }; /* * Verify that the data in dev is consistent with what is on the actual * block device (using the devname field only). Normally this will be * called when finding items in the cache, but for long running processes * is also desirable to revalidate an item before use. * * If we are unable to revalidate the data, we return the old data and * do not set the BLKID_BID_FL_VERIFIED flag on it. */ blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev) { const struct blkid_magic *id; unsigned char *bufs[BLKID_BLK_OFFS + 1], *buf; const char *type; struct stat st; time_t diff, now; int fd, idx; if (!dev) return NULL; now = time(NULL); diff = now - dev->bid_time; if ((now < dev->bid_time) || (diff < BLKID_PROBE_MIN) || (dev->bid_flags & BLKID_BID_FL_VERIFIED && diff < BLKID_PROBE_INTERVAL)) return dev; DBG(DEBUG_PROBE, printf("need to revalidate %s (time since last check %lu)\n", dev->bid_name, diff)); fd = open(dev->bid_name, O_RDONLY); if (fd < 0 || fstat(fd, &st) < 0 ) { if (fd >= 0) close(fd); if (errno == ENXIO || errno == ENODEV || errno == ENOENT) { blkid_free_dev(dev); return NULL; } /* We don't have read permission, just return cache data. */ DBG(DEBUG_PROBE, printf("returning unverified data for %s\n", dev->bid_name)); return dev; } memset(bufs, 0, sizeof(bufs)); /* * Iterate over the type array. If we already know the type, * then try that first. If it doesn't work, then blow away * the type information, and try again. * */ try_again: type = 0; if (!dev->bid_type || !strcmp(dev->bid_type, "mdraid")) { uuid_t uuid; if (check_mdraid(fd, uuid) == 0) { set_uuid(dev, uuid); type = "mdraid"; goto found_type; } } for (id = type_array; id->bim_type; id++) { if (dev->bid_type && strcmp(id->bim_type, dev->bid_type)) continue; idx = id->bim_kboff + (id->bim_sboff >> 10); if (idx > BLKID_BLK_OFFS || idx < 0) continue; buf = bufs[idx]; if (!buf) { if (lseek(fd, idx << 10, SEEK_SET) < 0) continue; buf = xmalloc(1024); if (read(fd, buf, 1024) != 1024) { free(buf); continue; } bufs[idx] = buf; } if (memcmp(id->bim_magic, buf + (id->bim_sboff&0x3ff), id->bim_len)) continue; if ((id->bim_probe == NULL) || (id->bim_probe(fd, cache, dev, id, buf) == 0)) { type = id->bim_type; goto found_type; } } if (!id->bim_type && dev->bid_type) { /* * Zap the device filesystem type and try again */ blkid_set_tag(dev, "TYPE", 0, 0); blkid_set_tag(dev, "SEC_TYPE", 0, 0); blkid_set_tag(dev, "LABEL", 0, 0); blkid_set_tag(dev, "UUID", 0, 0); goto try_again; } if (!dev->bid_type) { blkid_free_dev(dev); close(fd); return NULL; } found_type: if (dev && type) { dev->bid_devno = st.st_rdev; dev->bid_time = time(NULL); dev->bid_flags |= BLKID_BID_FL_VERIFIED; cache->bic_flags |= BLKID_BIC_FL_CHANGED; blkid_set_tag(dev, "TYPE", type, 0); DBG(DEBUG_PROBE, printf("%s: devno 0x%04llx, type %s\n", dev->bid_name, st.st_rdev, type)); } close(fd); return dev; } int blkid_known_fstype(const char *fstype) { const struct blkid_magic *id; for (id = type_array; id->bim_type; id++) { if (strcmp(fstype, id->bim_type) == 0) return 1; } return 0; } #ifdef TEST_PROGRAM int main(int argc, char **argv) { blkid_dev dev; blkid_cache cache; int ret; blkid_debug_mask = DEBUG_ALL; if (argc != 2) { fprintf(stderr, "Usage: %s device\n" "Probe a single device to determine type\n", argv[0]); exit(1); } if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } dev = blkid_get_dev(cache, argv[1], BLKID_DEV_NORMAL); if (!dev) { printf("%s: %s has an unsupported type\n", argv[0], argv[1]); return 1; } printf("%s is type %s\n", argv[1], dev->bid_type ? dev->bid_type : "(null)"); if (dev->bid_label) printf("\tlabel is '%s'\n", dev->bid_label); if (dev->bid_uuid) printf("\tuuid is %s\n", dev->bid_uuid); blkid_free_dev(dev); return 0; } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/read.c0000644000000000000000000002273512263563520020631 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * read.c - read the blkid cache from disk, to avoid scanning all devices * * Copyright (C) 2001, 2003 Theodore Y. Ts'o * Copyright (C) 2001 Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #include #include #include #include #include #include #include #include "blkidP.h" #include "../uuid/uuid.h" #ifdef HAVE_STRTOULL #define __USE_ISOC9X #define STRTOULL strtoull /* defined in stdlib.h if you try hard enough */ #else /* FIXME: need to support real strtoull here */ #define STRTOULL strtoul #endif #include #ifdef TEST_PROGRAM #define blkid_debug_dump_dev(dev) (debug_dump_dev(dev)) static void debug_dump_dev(blkid_dev dev); #endif /* * File format: * * ...]>device_name * * The following tags are required for each entry: * unique (within this file) ID number of this device * (ascii time_t) time this entry was last read from disk * (detected) type of filesystem/data for this partition * * The following tags may be present, depending on the device contents * (user supplied) label (volume name, etc) * (generated) universally unique identifier (serial no) */ static char *skip_over_blank(char *cp) { while (*cp && isspace(*cp)) cp++; return cp; } static char *skip_over_word(char *cp) { char ch; while ((ch = *cp)) { /* If we see a backslash, skip the next character */ if (ch == '\\') { cp++; if (*cp == '\0') break; cp++; continue; } if (isspace(ch) || ch == '<' || ch == '>') break; cp++; } return cp; } static char *strip_line(char *line) { char *p; line = skip_over_blank(line); p = line + strlen(line) - 1; while (*line) { if (isspace(*p)) *p-- = '\0'; else break; } return line; } /* * Start parsing a new line from the cache. * * line starts with " continue parsing line * line starts with " skip line * line starts with other, return -BLKID_ERR_CACHE -> error */ static int parse_start(char **cp) { char *p; p = strip_line(*cp); /* Skip comment or blank lines. We can't just NUL the first '#' char, * in case it is inside quotes, or escaped. */ if (*p == '\0' || *p == '#') return 0; if (!strncmp(p, "", 9)) { DBG(DEBUG_READ, printf("found device trailer %9s\n", *cp)); *cp += 9; return 0; } return -BLKID_ERR_CACHE; } /* * Allocate a new device struct with device name filled in. Will handle * finding the device on lines of the form: * devname * devnamebar */ static int parse_dev(blkid_cache cache, blkid_dev *dev, char **cp) { char *start, *tmp, *end, *name; int ret; if ((ret = parse_start(cp)) <= 0) return ret; start = tmp = strchr(*cp, '>'); if (!start) { DBG(DEBUG_READ, printf("blkid: short line parsing dev: %s\n", *cp)); return -BLKID_ERR_CACHE; } start = skip_over_blank(start + 1); end = skip_over_word(start); DBG(DEBUG_READ, printf("device should be %*s\n", end - start, start)); if (**cp == '>') *cp = end; else (*cp)++; *tmp = '\0'; if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0) { DBG(DEBUG_READ, printf("blkid: missing ending: %s\n", end)); } else if (tmp) *tmp = '\0'; if (end - start <= 1) { DBG(DEBUG_READ, printf("blkid: empty device name: %s\n", *cp)); return -BLKID_ERR_CACHE; } name = blkid_strndup(start, end-start); if (name == NULL) return -BLKID_ERR_MEM; DBG(DEBUG_READ, printf("found dev %s\n", name)); if (!(*dev = blkid_get_dev(cache, name, BLKID_DEV_CREATE))) return -BLKID_ERR_MEM; free(name); return 1; } /* * Extract a tag of the form NAME="value" from the line. */ static int parse_token(char **name, char **value, char **cp) { char *end; if (!name || !value || !cp) return -BLKID_ERR_PARAM; if (!(*value = strchr(*cp, '='))) return 0; **value = '\0'; *name = strip_line(*cp); *value = skip_over_blank(*value + 1); if (**value == '"') { end = strchr(*value + 1, '"'); if (!end) { DBG(DEBUG_READ, printf("unbalanced quotes at: %s\n", *value)); *cp = *value; return -BLKID_ERR_CACHE; } (*value)++; *end = '\0'; end++; } else { end = skip_over_word(*value); if (*end) { *end = '\0'; end++; } } *cp = end; return 1; } /* * Extract a tag of the form value from the line. */ /* static int parse_xml(char **name, char **value, char **cp) { char *end; if (!name || !value || !cp) return -BLKID_ERR_PARAM; *name = strip_line(*cp); if ((*name)[0] != '<' || (*name)[1] == '/') return 0; FIXME: finish this. } */ /* * Extract a tag from the line. * * Return 1 if a valid tag was found. * Return 0 if no tag found. * Return -ve error code. */ static int parse_tag(blkid_cache cache, blkid_dev dev, char **cp) { char *name; char *value; int ret; if (!cache || !dev) return -BLKID_ERR_PARAM; if ((ret = parse_token(&name, &value, cp)) <= 0 /* && (ret = parse_xml(&name, &value, cp)) <= 0 */) return ret; /* Some tags are stored directly in the device struct */ if (!strcmp(name, "DEVNO")) dev->bid_devno = STRTOULL(value, 0, 0); else if (!strcmp(name, "PRI")) dev->bid_pri = strtol(value, 0, 0); else if (!strcmp(name, "TIME")) /* FIXME: need to parse a long long eventually */ dev->bid_time = strtol(value, 0, 0); else ret = blkid_set_tag(dev, name, value, strlen(value)); DBG(DEBUG_READ, printf(" tag: %s=\"%s\"\n", name, value)); return ret < 0 ? ret : 1; } /* * Parse a single line of data, and return a newly allocated dev struct. * Add the new device to the cache struct, if one was read. * * Lines are of the form /dev/foo * * Returns -ve value on error. * Returns 0 otherwise. * If a valid device was read, *dev_p is non-NULL, otherwise it is NULL * (e.g. comment lines, unknown XML content, etc). */ static int blkid_parse_line(blkid_cache cache, blkid_dev *dev_p, char *cp) { blkid_dev dev; int ret; if (!cache || !dev_p) return -BLKID_ERR_PARAM; *dev_p = NULL; DBG(DEBUG_READ, printf("line: %s\n", cp)); if ((ret = parse_dev(cache, dev_p, &cp)) <= 0) return ret; dev = *dev_p; while ((ret = parse_tag(cache, dev, &cp)) > 0) { ; } if (dev->bid_type == NULL) { DBG(DEBUG_READ, printf("blkid: device %s has no TYPE\n",dev->bid_name)); blkid_free_dev(dev); } DBG(DEBUG_READ, blkid_debug_dump_dev(dev)); return ret; } /* * Parse the specified filename, and return the data in the supplied or * a newly allocated cache struct. If the file doesn't exist, return a * new empty cache struct. */ void blkid_read_cache(blkid_cache cache) { FILE *file; char buf[4096]; int fd, lineno = 0; struct stat st; if (!cache) return; /* * If the file doesn't exist, then we just return an empty * struct so that the cache can be populated. */ if ((fd = open(cache->bic_filename, O_RDONLY)) < 0) return; if (fstat(fd, &st) < 0) goto errout; if ((st.st_mtime == cache->bic_ftime) || (cache->bic_flags & BLKID_BIC_FL_CHANGED)) { DBG(DEBUG_CACHE, printf("skipping re-read of %s\n", cache->bic_filename)); goto errout; } DBG(DEBUG_CACHE, printf("reading cache file %s\n", cache->bic_filename)); file = xfdopen_for_read(fd); while (fgets(buf, sizeof(buf), file)) { blkid_dev dev; unsigned int end; lineno++; if (buf[0] == 0) continue; end = strlen(buf) - 1; /* Continue reading next line if it ends with a backslash */ while (end < sizeof(buf) - 2 && buf[end] == '\\' && fgets(buf + end, sizeof(buf) - end, file)) { end = strlen(buf) - 1; lineno++; } if (blkid_parse_line(cache, &dev, buf) < 0) { DBG(DEBUG_READ, printf("blkid: bad format on line %d\n", lineno)); continue; } } fclose(file); /* * Initially we do not need to write out the cache file. */ cache->bic_flags &= ~BLKID_BIC_FL_CHANGED; cache->bic_ftime = st.st_mtime; return; errout: close(fd); } #ifdef TEST_PROGRAM static void debug_dump_dev(blkid_dev dev) { struct list_head *p; if (!dev) { printf(" dev: NULL\n"); return; } printf(" dev: name = %s\n", dev->bid_name); printf(" dev: DEVNO=\"0x%0llx\"\n", dev->bid_devno); printf(" dev: TIME=\"%lu\"\n", dev->bid_time); printf(" dev: PRI=\"%d\"\n", dev->bid_pri); printf(" dev: flags = 0x%08X\n", dev->bid_flags); list_for_each(p, &dev->bid_tags) { blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags); if (tag) printf(" tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val); else printf(" tag: NULL\n"); } bb_putchar('\n'); } int main(int argc, char**argv) { blkid_cache cache = NULL; int ret; blkid_debug_mask = DEBUG_ALL; if (argc > 2) { fprintf(stderr, "Usage: %s [filename]\n" "Test parsing of the cache (filename)\n", argv[0]); exit(1); } if ((ret = blkid_get_cache(&cache, argv[1])) < 0) fprintf(stderr, "error %d reading cache file %s\n", ret, argv[1] ? argv[1] : BLKID_CACHE_FILE); blkid_put_cache(cache); return ret; } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c0000644000000000000000000000757412263563520022541 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * getsize.c --- get the size of a partition. * * Copyright (C) 1995, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ /* include this before sys/queues.h! */ #include "blkidP.h" #include #include #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_LINUX_FD_H #include #endif #ifdef HAVE_SYS_DISKLABEL_H #include #include #endif #ifdef HAVE_SYS_DISK_H #ifdef HAVE_SYS_QUEUE_H #include /* for LIST_HEAD */ #endif #include #endif #ifdef __linux__ #include #endif #if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) #define BLKGETSIZE _IO(0x12,96) /* return device size */ #endif #if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ #endif #ifdef APPLE_DARWIN #define BLKGETSIZE DKIOCGETBLOCKCOUNT32 #endif /* APPLE_DARWIN */ static int valid_offset(int fd, blkid_loff_t offset) { char ch; if (blkid_llseek(fd, offset, 0) < 0) return 0; if (read(fd, &ch, 1) < 1) return 0; return 1; } /* * Returns the number of blocks in a partition */ blkid_loff_t blkid_get_dev_size(int fd) { int valid_blkgetsize64 = 1; #ifdef __linux__ struct utsname ut; #endif unsigned long long size64; unsigned long size; blkid_loff_t high, low; #ifdef FDGETPRM struct floppy_struct this_floppy; #endif #ifdef HAVE_SYS_DISKLABEL_H int part = -1; struct disklabel lab; struct partition *pp; char ch; struct stat st; #endif /* HAVE_SYS_DISKLABEL_H */ #ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { if ((sizeof(blkid_loff_t) < sizeof(unsigned long long)) && (size64 << 9 > 0xFFFFFFFF)) return 0; /* EFBIG */ return (blkid_loff_t) size64 << 9; } #endif #ifdef BLKGETSIZE64 #ifdef __linux__ uname(&ut); if ((ut.release[0] == '2') && (ut.release[1] == '.') && (ut.release[2] < '6') && (ut.release[3] == '.')) valid_blkgetsize64 = 0; #endif if (valid_blkgetsize64 && ioctl(fd, BLKGETSIZE64, &size64) >= 0) { if ((sizeof(blkid_loff_t) < sizeof(unsigned long long)) && ((size64) > 0xFFFFFFFF)) return 0; /* EFBIG */ return size64; } #endif #ifdef BLKGETSIZE if (ioctl(fd, BLKGETSIZE, &size) >= 0) return (blkid_loff_t)size << 9; #endif #ifdef FDGETPRM if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) return (blkid_loff_t)this_floppy.size << 9; #endif #ifdef HAVE_SYS_DISKLABEL_H #if 0 /* * This should work in theory but I haven't tested it. Anyone * on a BSD system want to test this for me? In the meantime, * binary search mechanism should work just fine. */ if ((fstat(fd, &st) >= 0) && S_ISBLK(st.st_mode)) part = st.st_rdev & 7; if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { pp = &lab.d_partitions[part]; if (pp->p_size) return pp->p_size << 9; } #endif #endif /* HAVE_SYS_DISKLABEL_H */ /* * OK, we couldn't figure it out by using a specialized ioctl, * which is generally the best way. So do binary search to * find the size of the partition. */ low = 0; for (high = 1024; valid_offset(fd, high); high *= 2) low = high; while (low < high - 1) { const blkid_loff_t mid = (low + high) / 2; if (valid_offset(fd, mid)) low = mid; else high = mid; } return low + 1; } #ifdef TEST_PROGRAM int main(int argc, char **argv) { blkid_loff_t bytes; int fd; if (argc < 2) { fprintf(stderr, "Usage: %s device\n" "Determine the size of a device\n", argv[0]); return 1; } if ((fd = open(argv[1], O_RDONLY)) < 0) perror(argv[0]); bytes = blkid_get_dev_size(fd); printf("Device %s has %lld 1k blocks.\n", argv[1], bytes >> 10); return 0; } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/cache.c0000644000000000000000000000530612263563520020754 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * cache.c - allocation/initialization/free routines for cache * * Copyright (C) 2001 Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #include #include "blkidP.h" int blkid_debug_mask = 0; int blkid_get_cache(blkid_cache *ret_cache, const char *filename) { blkid_cache cache; #ifdef CONFIG_BLKID_DEBUG if (!(blkid_debug_mask & DEBUG_INIT)) { char *dstr = getenv("BLKID_DEBUG"); if (dstr) blkid_debug_mask = strtoul(dstr, 0, 0); blkid_debug_mask |= DEBUG_INIT; } #endif DBG(DEBUG_CACHE, printf("creating blkid cache (using %s)\n", filename ? filename : "default cache")); cache = xzalloc(sizeof(struct blkid_struct_cache)); INIT_LIST_HEAD(&cache->bic_devs); INIT_LIST_HEAD(&cache->bic_tags); if (filename && !strlen(filename)) filename = 0; if (!filename && (getuid() == geteuid())) filename = getenv("BLKID_FILE"); if (!filename) filename = BLKID_CACHE_FILE; cache->bic_filename = blkid_strdup(filename); blkid_read_cache(cache); *ret_cache = cache; return 0; } void blkid_put_cache(blkid_cache cache) { if (!cache) return; (void) blkid_flush_cache(cache); DBG(DEBUG_CACHE, printf("freeing cache struct\n")); /* DBG(DEBUG_CACHE, blkid_debug_dump_cache(cache)); */ while (!list_empty(&cache->bic_devs)) { blkid_dev dev = list_entry(cache->bic_devs.next, struct blkid_struct_dev, bid_devs); blkid_free_dev(dev); } while (!list_empty(&cache->bic_tags)) { blkid_tag tag = list_entry(cache->bic_tags.next, struct blkid_struct_tag, bit_tags); while (!list_empty(&tag->bit_names)) { blkid_tag bad = list_entry(tag->bit_names.next, struct blkid_struct_tag, bit_names); DBG(DEBUG_CACHE, printf("warning: unfreed tag %s=%s\n", bad->bit_name, bad->bit_val)); blkid_free_tag(bad); } blkid_free_tag(tag); } free(cache->bic_filename); free(cache); } #ifdef TEST_PROGRAM int main(int argc, char** argv) { blkid_cache cache = NULL; int ret; blkid_debug_mask = DEBUG_ALL; if ((argc > 2)) { fprintf(stderr, "Usage: %s [filename]\n", argv[0]); exit(1); } if ((ret = blkid_get_cache(&cache, argv[1])) < 0) { fprintf(stderr, "error %d parsing cache file %s\n", ret, argv[1] ? argv[1] : BLKID_CACHE_FILE); exit(1); } if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } if ((ret = blkid_probe_all(cache)) < 0) fprintf(stderr, "error probing devices\n"); blkid_put_cache(cache); return ret; } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/blkid/save.c0000644000000000000000000001012312263563520020640 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * save.c - write the cache struct to disk * * Copyright (C) 2001 by Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #include #include #include #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_MKDEV_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #include "blkidP.h" static int save_dev(blkid_dev dev, FILE *file) { struct list_head *p; if (!dev || dev->bid_name[0] != '/') return 0; DBG(DEBUG_SAVE, printf("device %s, type %s\n", dev->bid_name, dev->bid_type)); fprintf(file, "bid_devno, dev->bid_time); if (dev->bid_pri) fprintf(file, " PRI=\"%d\"", dev->bid_pri); list_for_each(p, &dev->bid_tags) { blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags); fprintf(file, " %s=\"%s\"", tag->bit_name,tag->bit_val); } fprintf(file, ">%s\n", dev->bid_name); return 0; } /* * Write out the cache struct to the cache file on disk. */ int blkid_flush_cache(blkid_cache cache) { struct list_head *p; char *tmp = NULL; const char *opened = NULL; const char *filename; FILE *file = NULL; int fd, ret = 0; struct stat st; if (!cache) return -BLKID_ERR_PARAM; if (list_empty(&cache->bic_devs) || !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) { DBG(DEBUG_SAVE, printf("skipping cache file write\n")); return 0; } filename = cache->bic_filename ? cache->bic_filename: BLKID_CACHE_FILE; /* If we can't write to the cache file, then don't even try */ if (((ret = stat(filename, &st)) < 0 && errno != ENOENT) || (ret == 0 && access(filename, W_OK) < 0)) { DBG(DEBUG_SAVE, printf("can't write to cache file %s\n", filename)); return 0; } /* * Try and create a temporary file in the same directory so * that in case of error we don't overwrite the cache file. * If the cache file doesn't yet exist, it isn't a regular * file (e.g. /dev/null or a socket), or we couldn't create * a temporary file then we open it directly. */ if (ret == 0 && S_ISREG(st.st_mode)) { tmp = xmalloc(strlen(filename) + 8); sprintf(tmp, "%s-XXXXXX", filename); fd = mkstemp(tmp); if (fd >= 0) { file = xfdopen_for_write(fd); opened = tmp; } fchmod(fd, 0644); } if (!file) { file = fopen_for_write(filename); opened = filename; } DBG(DEBUG_SAVE, printf("writing cache file %s (really %s)\n", filename, opened)); if (!file) { ret = errno; goto errout; } list_for_each(p, &cache->bic_devs) { blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs); if (!dev->bid_type) continue; if ((ret = save_dev(dev, file)) < 0) break; } if (ret >= 0) { cache->bic_flags &= ~BLKID_BIC_FL_CHANGED; ret = 1; } fclose(file); if (opened != filename) { if (ret < 0) { unlink(opened); DBG(DEBUG_SAVE, printf("unlinked temp cache %s\n", opened)); } else { char *backup; backup = xmalloc(strlen(filename) + 5); sprintf(backup, "%s.old", filename); unlink(backup); link(filename, backup); free(backup); rename(opened, filename); DBG(DEBUG_SAVE, printf("moved temp cache %s\n", opened)); } } errout: free(tmp); return ret; } #ifdef TEST_PROGRAM int main(int argc, char **argv) { blkid_cache cache = NULL; int ret; blkid_debug_mask = DEBUG_ALL; if (argc > 2) { fprintf(stderr, "Usage: %s [filename]\n" "Test loading/saving a cache (filename)\n", argv[0]); exit(1); } if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } if ((ret = blkid_probe_all(cache)) < 0) { fprintf(stderr, "error (%d) probing devices\n", ret); exit(1); } cache->bic_filename = blkid_strdup(argv[1]); if ((ret = blkid_flush_cache(cache)) < 0) { fprintf(stderr, "error (%d) saving cache\n", ret); exit(1); } blkid_put_cache(cache); return ret; } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2fsck.c0000644000000000000000000130614212263563520020004 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * e2fsck * * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. * Copyright (C) 2006 Garrett Kajmowicz * * Dictionary Abstract Data Type * Copyright (C) 1997 Kaz Kylheku * Free Software License: * All rights are reserved by the author, with the following exceptions: * Permission is granted to freely reproduce and distribute this software, * possibly in exchange for a fee, provided that this copyright notice appears * intact. Permission is also granted to adapt this software to produce * derivative works, as long as the modified versions carry this copyright * notice and additional notices stating that the work has been modified. * This source code may be translated into executable form and incorporated * into proprietary software; there is no requirement for such software to * contain a copyright notice related to this source. * * linux/fs/recovery and linux/fs/revoke * Written by Stephen C. Tweedie , 1999 * * Copyright 1999-2000 Red Hat Software --- All Rights Reserved * * Journal recovery routines for the generic filesystem journaling code; * part of the ext2fs journaling system. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* //usage:#define e2fsck_trivial_usage //usage: "[-panyrcdfvstDFSV] [-b superblock] [-B blocksize] " //usage: "[-I inode_buffer_blocks] [-P process_inode_size] " //usage: "[-l|-L bad_blocks_file] [-C fd] [-j external_journal] " //usage: "[-E extended-options] device" //usage:#define e2fsck_full_usage "\n\n" //usage: "Check ext2/ext3 file system\n" //usage: "\n -p Automatic repair (no questions)" //usage: "\n -n Make no changes to the filesystem" //usage: "\n -y Assume 'yes' to all questions" //usage: "\n -c Check for bad blocks and add them to the badblock list" //usage: "\n -f Force checking even if filesystem is marked clean" //usage: "\n -v Verbose" //usage: "\n -b superblock Use alternative superblock" //usage: "\n -B blocksize Force blocksize when looking for superblock" //usage: "\n -j journal Set location of the external journal" //usage: "\n -l file Add to badblocks list" //usage: "\n -L file Set badblocks list" */ #include "e2fsck.h" /*Put all of our defines here to clean things up*/ #define _(x) x #define N_(x) x /* * Procedure declarations */ static void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf); /* pass1.c */ static void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool); /* pass2.c */ static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir, ext2_ino_t ino, char *buf); /* pass3.c */ static int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t inode); static errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir, int num, int gauranteed_size); static ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix); static errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj); /* rehash.c */ static void e2fsck_rehash_directories(e2fsck_t ctx); /* util.c */ static void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size, const char *description); static int ask(e2fsck_t ctx, const char * string, int def); static void e2fsck_read_bitmaps(e2fsck_t ctx); static void preenhalt(e2fsck_t ctx); static void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino, struct ext2_inode * inode, const char * proc); static void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino, struct ext2_inode * inode, const char * proc); static blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name, io_manager manager); /* unix.c */ static void e2fsck_clear_progbar(e2fsck_t ctx); static int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent, unsigned int dpynum); /* * problem.h --- e2fsck problem error codes */ typedef __u32 problem_t; struct problem_context { errcode_t errcode; ext2_ino_t ino, ino2, dir; struct ext2_inode *inode; struct ext2_dir_entry *dirent; blk_t blk, blk2; e2_blkcnt_t blkcount; int group; __u64 num; const char *str; }; /* * Function declarations */ static int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx); static int end_problem_latch(e2fsck_t ctx, int mask); static int set_latch_flags(int mask, int setflags, int clearflags); static void clear_problem_context(struct problem_context *ctx); /* * Dictionary Abstract Data Type * Copyright (C) 1997 Kaz Kylheku * * dict.h v 1.22.2.6 2000/11/13 01:36:44 kaz * kazlib_1_20 */ #ifndef DICT_H #define DICT_H /* * Blurb for inclusion into C++ translation units */ typedef unsigned long dictcount_t; #define DICTCOUNT_T_MAX ULONG_MAX /* * The dictionary is implemented as a red-black tree */ typedef enum { dnode_red, dnode_black } dnode_color_t; typedef struct dnode_t { struct dnode_t *dict_left; struct dnode_t *dict_right; struct dnode_t *dict_parent; dnode_color_t dict_color; const void *dict_key; void *dict_data; } dnode_t; typedef int (*dict_comp_t)(const void *, const void *); typedef void (*dnode_free_t)(dnode_t *); typedef struct dict_t { dnode_t dict_nilnode; dictcount_t dict_nodecount; dictcount_t dict_maxcount; dict_comp_t dict_compare; dnode_free_t dict_freenode; int dict_dupes; } dict_t; typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *); typedef struct dict_load_t { dict_t *dict_dictptr; dnode_t dict_nilnode; } dict_load_t; #define dict_count(D) ((D)->dict_nodecount) #define dnode_get(N) ((N)->dict_data) #define dnode_getkey(N) ((N)->dict_key) #endif /* * Compatibility header file for e2fsck which should be included * instead of linux/jfs.h * * Copyright (C) 2000 Stephen C. Tweedie */ /* * Pull in the definition of the e2fsck context structure */ struct buffer_head { char b_data[8192]; e2fsck_t b_ctx; io_channel b_io; int b_size; blk_t b_blocknr; int b_dirty; int b_uptodate; int b_err; }; #define K_DEV_FS 1 #define K_DEV_JOURNAL 2 #define lock_buffer(bh) do {} while (0) #define unlock_buffer(bh) do {} while (0) #define buffer_req(bh) 1 #define do_readahead(journal, start) do {} while (0) static e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */ typedef struct { int object_length; } kmem_cache_t; #define kmem_cache_alloc(cache,flags) malloc((cache)->object_length) /* * We use the standard libext2fs portability tricks for inline * functions. */ static kmem_cache_t * do_cache_create(int len) { kmem_cache_t *new_cache; new_cache = xmalloc(sizeof(*new_cache)); new_cache->object_length = len; return new_cache; } static void do_cache_destroy(kmem_cache_t *cache) { free(cache); } /* * Dictionary Abstract Data Type */ /* * These macros provide short convenient names for structure members, * which are embellished with dict_ prefixes so that they are * properly confined to the documented namespace. It's legal for a * program which uses dict to define, for instance, a macro called ``parent''. * Such a macro would interfere with the dnode_t struct definition. * In general, highly portable and reusable C modules which expose their * structures need to confine structure member names to well-defined spaces. * The resulting identifiers aren't necessarily convenient to use, nor * readable, in the implementation, however! */ #define left dict_left #define right dict_right #define parent dict_parent #define color dict_color #define key dict_key #define data dict_data #define nilnode dict_nilnode #define maxcount dict_maxcount #define compare dict_compare #define dupes dict_dupes #define dict_root(D) ((D)->nilnode.left) #define dict_nil(D) (&(D)->nilnode) static void dnode_free(dnode_t *node); /* * Perform a ``left rotation'' adjustment on the tree. The given node P and * its right child C are rearranged so that the P instead becomes the left * child of C. The left subtree of C is inherited as the new right subtree * for P. The ordering of the keys within the tree is thus preserved. */ static void rotate_left(dnode_t *upper) { dnode_t *lower, *lowleft, *upparent; lower = upper->right; upper->right = lowleft = lower->left; lowleft->parent = upper; lower->parent = upparent = upper->parent; /* don't need to check for root node here because root->parent is the sentinel nil node, and root->parent->left points back to root */ if (upper == upparent->left) { upparent->left = lower; } else { assert (upper == upparent->right); upparent->right = lower; } lower->left = upper; upper->parent = lower; } /* * This operation is the ``mirror'' image of rotate_left. It is * the same procedure, but with left and right interchanged. */ static void rotate_right(dnode_t *upper) { dnode_t *lower, *lowright, *upparent; lower = upper->left; upper->left = lowright = lower->right; lowright->parent = upper; lower->parent = upparent = upper->parent; if (upper == upparent->right) { upparent->right = lower; } else { assert (upper == upparent->left); upparent->left = lower; } lower->right = upper; upper->parent = lower; } /* * Do a postorder traversal of the tree rooted at the specified * node and free everything under it. Used by dict_free(). */ static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil) { if (node == nil) return; free_nodes(dict, node->left, nil); free_nodes(dict, node->right, nil); dict->dict_freenode(node); } /* * Verify that the tree contains the given node. This is done by * traversing all of the nodes and comparing their pointers to the * given pointer. Returns 1 if the node is found, otherwise * returns zero. It is intended for debugging purposes. */ static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node) { if (root != nil) { return root == node || verify_dict_has_node(nil, root->left, node) || verify_dict_has_node(nil, root->right, node); } return 0; } /* * Select a different set of node allocator routines. */ static void dict_set_allocator(dict_t *dict, dnode_free_t fr) { assert(dict_count(dict) == 0); dict->dict_freenode = fr; } /* * Free all the nodes in the dictionary by using the dictionary's * installed free routine. The dictionary is emptied. */ static void dict_free_nodes(dict_t *dict) { dnode_t *nil = dict_nil(dict), *root = dict_root(dict); free_nodes(dict, root, nil); dict->dict_nodecount = 0; dict->nilnode.left = &dict->nilnode; dict->nilnode.right = &dict->nilnode; } /* * Initialize a user-supplied dictionary object. */ static dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp) { dict->compare = comp; dict->dict_freenode = dnode_free; dict->dict_nodecount = 0; dict->maxcount = maxcount; dict->nilnode.left = &dict->nilnode; dict->nilnode.right = &dict->nilnode; dict->nilnode.parent = &dict->nilnode; dict->nilnode.color = dnode_black; dict->dupes = 0; return dict; } /* * Locate a node in the dictionary having the given key. * If the node is not found, a null a pointer is returned (rather than * a pointer that dictionary's nil sentinel node), otherwise a pointer to the * located node is returned. */ static dnode_t *dict_lookup(dict_t *dict, const void *key) { dnode_t *root = dict_root(dict); dnode_t *nil = dict_nil(dict); dnode_t *saved; int result; /* simple binary search adapted for trees that contain duplicate keys */ while (root != nil) { result = dict->compare(key, root->key); if (result < 0) root = root->left; else if (result > 0) root = root->right; else { if (!dict->dupes) { /* no duplicates, return match */ return root; } else { /* could be dupes, find leftmost one */ do { saved = root; root = root->left; while (root != nil && dict->compare(key, root->key)) root = root->right; } while (root != nil); return saved; } } } return NULL; } /* * Insert a node into the dictionary. The node should have been * initialized with a data field. All other fields are ignored. * The behavior is undefined if the user attempts to insert into * a dictionary that is already full (for which the dict_isfull() * function returns true). */ static void dict_insert(dict_t *dict, dnode_t *node, const void *key) { dnode_t *where = dict_root(dict), *nil = dict_nil(dict); dnode_t *parent = nil, *uncle, *grandpa; int result = -1; node->key = key; /* basic binary tree insert */ while (where != nil) { parent = where; result = dict->compare(key, where->key); /* trap attempts at duplicate key insertion unless it's explicitly allowed */ assert(dict->dupes || result != 0); if (result < 0) where = where->left; else where = where->right; } assert(where == nil); if (result < 0) parent->left = node; else parent->right = node; node->parent = parent; node->left = nil; node->right = nil; dict->dict_nodecount++; /* red black adjustments */ node->color = dnode_red; while (parent->color == dnode_red) { grandpa = parent->parent; if (parent == grandpa->left) { uncle = grandpa->right; if (uncle->color == dnode_red) { /* red parent, red uncle */ parent->color = dnode_black; uncle->color = dnode_black; grandpa->color = dnode_red; node = grandpa; parent = grandpa->parent; } else { /* red parent, black uncle */ if (node == parent->right) { rotate_left(parent); parent = node; assert (grandpa == parent->parent); /* rotation between parent and child preserves grandpa */ } parent->color = dnode_black; grandpa->color = dnode_red; rotate_right(grandpa); break; } } else { /* symmetric cases: parent == parent->parent->right */ uncle = grandpa->left; if (uncle->color == dnode_red) { parent->color = dnode_black; uncle->color = dnode_black; grandpa->color = dnode_red; node = grandpa; parent = grandpa->parent; } else { if (node == parent->left) { rotate_right(parent); parent = node; assert (grandpa == parent->parent); } parent->color = dnode_black; grandpa->color = dnode_red; rotate_left(grandpa); break; } } } dict_root(dict)->color = dnode_black; } /* * Allocate a node using the dictionary's allocator routine, give it * the data item. */ static dnode_t *dnode_init(dnode_t *dnode, void *data) { dnode->data = data; dnode->parent = NULL; dnode->left = NULL; dnode->right = NULL; return dnode; } static int dict_alloc_insert(dict_t *dict, const void *key, void *data) { dnode_t *node = xmalloc(sizeof(dnode_t)); dnode_init(node, data); dict_insert(dict, node, key); return 1; } /* * Return the node with the lowest (leftmost) key. If the dictionary is empty * (that is, dict_isempty(dict) returns 1) a null pointer is returned. */ static dnode_t *dict_first(dict_t *dict) { dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left; if (root != nil) while ((left = root->left) != nil) root = left; return (root == nil) ? NULL : root; } /* * Return the given node's successor node---the node which has the * next key in the left to right ordering. If the node has * no successor, a null pointer is returned rather than a pointer to * the nil node. */ static dnode_t *dict_next(dict_t *dict, dnode_t *curr) { dnode_t *nil = dict_nil(dict), *parent, *left; if (curr->right != nil) { curr = curr->right; while ((left = curr->left) != nil) curr = left; return curr; } parent = curr->parent; while (parent != nil && curr == parent->right) { curr = parent; parent = curr->parent; } return (parent == nil) ? NULL : parent; } static void dnode_free(dnode_t *node) { free(node); } #undef left #undef right #undef parent #undef color #undef key #undef data #undef nilnode #undef maxcount #undef compare #undef dupes /* * dirinfo.c --- maintains the directory information table for e2fsck. */ /* * This subroutine is called during pass1 to create a directory info * entry. During pass1, the passed-in parent is 0; it will get filled * in during pass2. */ static void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent) { struct dir_info *dir; int i, j; ext2_ino_t num_dirs; errcode_t retval; unsigned long old_size; if (!ctx->dir_info) { ctx->dir_info_count = 0; retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs); if (retval) num_dirs = 1024; /* Guess */ ctx->dir_info_size = num_dirs + 10; ctx->dir_info = (struct dir_info *) e2fsck_allocate_memory(ctx, ctx->dir_info_size * sizeof (struct dir_info), "directory map"); } if (ctx->dir_info_count >= ctx->dir_info_size) { old_size = ctx->dir_info_size * sizeof(struct dir_info); ctx->dir_info_size += 10; retval = ext2fs_resize_mem(old_size, ctx->dir_info_size * sizeof(struct dir_info), &ctx->dir_info); if (retval) { ctx->dir_info_size -= 10; return; } } /* * Normally, add_dir_info is called with each inode in * sequential order; but once in a while (like when pass 3 * needs to recreate the root directory or lost+found * directory) it is called out of order. In those cases, we * need to move the dir_info entries down to make room, since * the dir_info array needs to be sorted by inode number for * get_dir_info()'s sake. */ if (ctx->dir_info_count && ctx->dir_info[ctx->dir_info_count-1].ino >= ino) { for (i = ctx->dir_info_count-1; i > 0; i--) if (ctx->dir_info[i-1].ino < ino) break; dir = &ctx->dir_info[i]; if (dir->ino != ino) for (j = ctx->dir_info_count++; j > i; j--) ctx->dir_info[j] = ctx->dir_info[j-1]; } else dir = &ctx->dir_info[ctx->dir_info_count++]; dir->ino = ino; dir->dotdot = parent; dir->parent = parent; } /* * get_dir_info() --- given an inode number, try to find the directory * information entry for it. */ static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino) { int low, high, mid; low = 0; high = ctx->dir_info_count-1; if (!ctx->dir_info) return 0; if (ino == ctx->dir_info[low].ino) return &ctx->dir_info[low]; if (ino == ctx->dir_info[high].ino) return &ctx->dir_info[high]; while (low < high) { mid = (low+high)/2; if (mid == low || mid == high) break; if (ino == ctx->dir_info[mid].ino) return &ctx->dir_info[mid]; if (ino < ctx->dir_info[mid].ino) high = mid; else low = mid; } return 0; } /* * Free the dir_info structure when it isn't needed any more. */ static void e2fsck_free_dir_info(e2fsck_t ctx) { ext2fs_free_mem(&ctx->dir_info); ctx->dir_info_size = 0; ctx->dir_info_count = 0; } /* * Return the count of number of directories in the dir_info structure */ static int e2fsck_get_num_dirinfo(e2fsck_t ctx) { return ctx->dir_info_count; } /* * A simple interator function */ static struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control) { if (*control >= ctx->dir_info_count) return 0; return ctx->dir_info + (*control)++; } /* * dirinfo.c --- maintains the directory information table for e2fsck. * */ #ifdef ENABLE_HTREE /* * This subroutine is called during pass1 to create a directory info * entry. During pass1, the passed-in parent is 0; it will get filled * in during pass2. */ static void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks) { struct dx_dir_info *dir; int i, j; errcode_t retval; unsigned long old_size; if (!ctx->dx_dir_info) { ctx->dx_dir_info_count = 0; ctx->dx_dir_info_size = 100; /* Guess */ ctx->dx_dir_info = (struct dx_dir_info *) e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size * sizeof (struct dx_dir_info), "directory map"); } if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) { old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info); ctx->dx_dir_info_size += 10; retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size * sizeof(struct dx_dir_info), &ctx->dx_dir_info); if (retval) { ctx->dx_dir_info_size -= 10; return; } } /* * Normally, add_dx_dir_info is called with each inode in * sequential order; but once in a while (like when pass 3 * needs to recreate the root directory or lost+found * directory) it is called out of order. In those cases, we * need to move the dx_dir_info entries down to make room, since * the dx_dir_info array needs to be sorted by inode number for * get_dx_dir_info()'s sake. */ if (ctx->dx_dir_info_count && ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) { for (i = ctx->dx_dir_info_count-1; i > 0; i--) if (ctx->dx_dir_info[i-1].ino < ino) break; dir = &ctx->dx_dir_info[i]; if (dir->ino != ino) for (j = ctx->dx_dir_info_count++; j > i; j--) ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1]; } else dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++]; dir->ino = ino; dir->numblocks = num_blocks; dir->hashversion = 0; dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks * sizeof (struct dx_dirblock_info), "dx_block info array"); } /* * get_dx_dir_info() --- given an inode number, try to find the directory * information entry for it. */ static struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino) { int low, high, mid; low = 0; high = ctx->dx_dir_info_count-1; if (!ctx->dx_dir_info) return 0; if (ino == ctx->dx_dir_info[low].ino) return &ctx->dx_dir_info[low]; if (ino == ctx->dx_dir_info[high].ino) return &ctx->dx_dir_info[high]; while (low < high) { mid = (low+high)/2; if (mid == low || mid == high) break; if (ino == ctx->dx_dir_info[mid].ino) return &ctx->dx_dir_info[mid]; if (ino < ctx->dx_dir_info[mid].ino) high = mid; else low = mid; } return 0; } /* * Free the dx_dir_info structure when it isn't needed any more. */ static void e2fsck_free_dx_dir_info(e2fsck_t ctx) { int i; struct dx_dir_info *dir; if (ctx->dx_dir_info) { dir = ctx->dx_dir_info; for (i=0; i < ctx->dx_dir_info_count; i++) { ext2fs_free_mem(&dir->dx_block); } ext2fs_free_mem(&ctx->dx_dir_info); } ctx->dx_dir_info_size = 0; ctx->dx_dir_info_count = 0; } /* * A simple interator function */ static struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control) { if (*control >= ctx->dx_dir_info_count) return 0; return ctx->dx_dir_info + (*control)++; } #endif /* ENABLE_HTREE */ /* * e2fsck.c - a consistency checker for the new extended file system. * */ /* * This function allocates an e2fsck context */ static errcode_t e2fsck_allocate_context(e2fsck_t *ret) { e2fsck_t context; errcode_t retval; retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &context); if (retval) return retval; memset(context, 0, sizeof(struct e2fsck_struct)); context->process_inode_size = 256; context->ext_attr_ver = 2; *ret = context; return 0; } struct ea_refcount_el { blk_t ea_blk; int ea_count; }; struct ea_refcount { blk_t count; blk_t size; blk_t cursor; struct ea_refcount_el *list; }; static void ea_refcount_free(ext2_refcount_t refcount) { if (!refcount) return; ext2fs_free_mem(&refcount->list); ext2fs_free_mem(&refcount); } /* * This function resets an e2fsck context; it is called when e2fsck * needs to be restarted. */ static errcode_t e2fsck_reset_context(e2fsck_t ctx) { ctx->flags = 0; ctx->lost_and_found = 0; ctx->bad_lost_and_found = 0; ext2fs_free_inode_bitmap(ctx->inode_used_map); ctx->inode_used_map = 0; ext2fs_free_inode_bitmap(ctx->inode_dir_map); ctx->inode_dir_map = 0; ext2fs_free_inode_bitmap(ctx->inode_reg_map); ctx->inode_reg_map = 0; ext2fs_free_block_bitmap(ctx->block_found_map); ctx->block_found_map = 0; ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0; if (ctx->journal_io) { if (ctx->fs && ctx->fs->io != ctx->journal_io) io_channel_close(ctx->journal_io); ctx->journal_io = 0; } if (ctx->fs) { ext2fs_free_dblist(ctx->fs->dblist); ctx->fs->dblist = 0; } e2fsck_free_dir_info(ctx); #ifdef ENABLE_HTREE e2fsck_free_dx_dir_info(ctx); #endif ea_refcount_free(ctx->refcount); ctx->refcount = 0; ea_refcount_free(ctx->refcount_extra); ctx->refcount_extra = 0; ext2fs_free_block_bitmap(ctx->block_dup_map); ctx->block_dup_map = 0; ext2fs_free_block_bitmap(ctx->block_ea_map); ctx->block_ea_map = 0; ext2fs_free_inode_bitmap(ctx->inode_bad_map); ctx->inode_bad_map = 0; ext2fs_free_inode_bitmap(ctx->inode_imagic_map); ctx->inode_imagic_map = 0; ext2fs_u32_list_free(ctx->dirs_to_hash); ctx->dirs_to_hash = 0; /* * Clear the array of invalid meta-data flags */ ext2fs_free_mem(&ctx->invalid_inode_bitmap_flag); ext2fs_free_mem(&ctx->invalid_block_bitmap_flag); ext2fs_free_mem(&ctx->invalid_inode_table_flag); /* Clear statistic counters */ ctx->fs_directory_count = 0; ctx->fs_regular_count = 0; ctx->fs_blockdev_count = 0; ctx->fs_chardev_count = 0; ctx->fs_links_count = 0; ctx->fs_symlinks_count = 0; ctx->fs_fast_symlinks_count = 0; ctx->fs_fifo_count = 0; ctx->fs_total_count = 0; ctx->fs_sockets_count = 0; ctx->fs_ind_count = 0; ctx->fs_dind_count = 0; ctx->fs_tind_count = 0; ctx->fs_fragmented = 0; ctx->large_files = 0; /* Reset the superblock to the user's requested value */ ctx->superblock = ctx->use_superblock; return 0; } static void e2fsck_free_context(e2fsck_t ctx) { if (!ctx) return; e2fsck_reset_context(ctx); if (ctx->blkid) blkid_put_cache(ctx->blkid); ext2fs_free_mem(&ctx); } /* * ea_refcount.c */ /* * The strategy we use for keeping track of EA refcounts is as * follows. We keep a sorted array of first EA blocks and its * reference counts. Once the refcount has dropped to zero, it is * removed from the array to save memory space. Once the EA block is * checked, its bit is set in the block_ea_map bitmap. */ static errcode_t ea_refcount_create(int size, ext2_refcount_t *ret) { ext2_refcount_t refcount; errcode_t retval; size_t bytes; retval = ext2fs_get_mem(sizeof(struct ea_refcount), &refcount); if (retval) return retval; memset(refcount, 0, sizeof(struct ea_refcount)); if (!size) size = 500; refcount->size = size; bytes = (size_t) (size * sizeof(struct ea_refcount_el)); #ifdef DEBUG printf("Refcount allocated %d entries, %lu bytes.\n", refcount->size, bytes); #endif retval = ext2fs_get_mem(bytes, &refcount->list); if (retval) goto errout; memset(refcount->list, 0, bytes); refcount->count = 0; refcount->cursor = 0; *ret = refcount; return 0; errout: ea_refcount_free(refcount); return retval; } /* * collapse_refcount() --- go through the refcount array, and get rid * of any count == zero entries */ static void refcount_collapse(ext2_refcount_t refcount) { unsigned int i, j; struct ea_refcount_el *list; list = refcount->list; for (i = 0, j = 0; i < refcount->count; i++) { if (list[i].ea_count) { if (i != j) list[j] = list[i]; j++; } } #if defined(DEBUG) || defined(TEST_PROGRAM) printf("Refcount_collapse: size was %d, now %d\n", refcount->count, j); #endif refcount->count = j; } /* * insert_refcount_el() --- Insert a new entry into the sorted list at a * specified position. */ static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount, blk_t blk, int pos) { struct ea_refcount_el *el; errcode_t retval; blk_t new_size = 0; int num; if (refcount->count >= refcount->size) { new_size = refcount->size + 100; #ifdef DEBUG printf("Reallocating refcount %d entries...\n", new_size); #endif retval = ext2fs_resize_mem((size_t) refcount->size * sizeof(struct ea_refcount_el), (size_t) new_size * sizeof(struct ea_refcount_el), &refcount->list); if (retval) return 0; refcount->size = new_size; } num = (int) refcount->count - pos; if (num < 0) return 0; /* should never happen */ if (num) { memmove(&refcount->list[pos+1], &refcount->list[pos], sizeof(struct ea_refcount_el) * num); } refcount->count++; el = &refcount->list[pos]; el->ea_count = 0; el->ea_blk = blk; return el; } /* * get_refcount_el() --- given an block number, try to find refcount * information in the sorted list. If the create flag is set, * and we can't find an entry, create one in the sorted list. */ static struct ea_refcount_el *get_refcount_el(ext2_refcount_t refcount, blk_t blk, int create) { float range; int low, high, mid; blk_t lowval, highval; if (!refcount || !refcount->list) return 0; retry: low = 0; high = (int) refcount->count-1; if (create && ((refcount->count == 0) || (blk > refcount->list[high].ea_blk))) { if (refcount->count >= refcount->size) refcount_collapse(refcount); return insert_refcount_el(refcount, blk, (unsigned) refcount->count); } if (refcount->count == 0) return 0; if (refcount->cursor >= refcount->count) refcount->cursor = 0; if (blk == refcount->list[refcount->cursor].ea_blk) return &refcount->list[refcount->cursor++]; #ifdef DEBUG printf("Non-cursor get_refcount_el: %u\n", blk); #endif while (low <= high) { if (low == high) mid = low; else { /* Interpolate for efficiency */ lowval = refcount->list[low].ea_blk; highval = refcount->list[high].ea_blk; if (blk < lowval) range = 0; else if (blk > highval) range = 1; else range = ((float) (blk - lowval)) / (highval - lowval); mid = low + ((int) (range * (high-low))); } if (blk == refcount->list[mid].ea_blk) { refcount->cursor = mid+1; return &refcount->list[mid]; } if (blk < refcount->list[mid].ea_blk) high = mid-1; else low = mid+1; } /* * If we need to create a new entry, it should be right at * low (where high will be left at low-1). */ if (create) { if (refcount->count >= refcount->size) { refcount_collapse(refcount); if (refcount->count < refcount->size) goto retry; } return insert_refcount_el(refcount, blk, low); } return 0; } static errcode_t ea_refcount_increment(ext2_refcount_t refcount, blk_t blk, int *ret) { struct ea_refcount_el *el; el = get_refcount_el(refcount, blk, 1); if (!el) return EXT2_ET_NO_MEMORY; el->ea_count++; if (ret) *ret = el->ea_count; return 0; } static errcode_t ea_refcount_decrement(ext2_refcount_t refcount, blk_t blk, int *ret) { struct ea_refcount_el *el; el = get_refcount_el(refcount, blk, 0); if (!el || el->ea_count == 0) return EXT2_ET_INVALID_ARGUMENT; el->ea_count--; if (ret) *ret = el->ea_count; return 0; } static errcode_t ea_refcount_store(ext2_refcount_t refcount, blk_t blk, int count) { struct ea_refcount_el *el; /* * Get the refcount element */ el = get_refcount_el(refcount, blk, count ? 1 : 0); if (!el) return count ? EXT2_ET_NO_MEMORY : 0; el->ea_count = count; return 0; } static inline void ea_refcount_intr_begin(ext2_refcount_t refcount) { refcount->cursor = 0; } static blk_t ea_refcount_intr_next(ext2_refcount_t refcount, int *ret) { struct ea_refcount_el *list; while (1) { if (refcount->cursor >= refcount->count) return 0; list = refcount->list; if (list[refcount->cursor].ea_count) { if (ret) *ret = list[refcount->cursor].ea_count; return list[refcount->cursor++].ea_blk; } refcount->cursor++; } } /* * ehandler.c --- handle bad block errors which come up during the * course of an e2fsck session. */ static const char *operation; static errcode_t e2fsck_handle_read_error(io_channel channel, unsigned long block, int count, void *data, size_t size FSCK_ATTR((unused)), int actual FSCK_ATTR((unused)), errcode_t error) { int i; char *p; ext2_filsys fs = (ext2_filsys) channel->app_data; e2fsck_t ctx; ctx = (e2fsck_t) fs->priv_data; /* * If more than one block was read, try reading each block * separately. We could use the actual bytes read to figure * out where to start, but we don't bother. */ if (count > 1) { p = (char *) data; for (i=0; i < count; i++, p += channel->block_size, block++) { error = io_channel_read_blk(channel, block, 1, p); if (error) return error; } return 0; } if (operation) printf(_("Error reading block %lu (%s) while %s. "), block, error_message(error), operation); else printf(_("Error reading block %lu (%s). "), block, error_message(error)); preenhalt(ctx); if (ask(ctx, _("Ignore error"), 1)) { if (ask(ctx, _("Force rewrite"), 1)) io_channel_write_blk(channel, block, 1, data); return 0; } return error; } static errcode_t e2fsck_handle_write_error(io_channel channel, unsigned long block, int count, const void *data, size_t size FSCK_ATTR((unused)), int actual FSCK_ATTR((unused)), errcode_t error) { int i; const char *p; ext2_filsys fs = (ext2_filsys) channel->app_data; e2fsck_t ctx; ctx = (e2fsck_t) fs->priv_data; /* * If more than one block was written, try writing each block * separately. We could use the actual bytes read to figure * out where to start, but we don't bother. */ if (count > 1) { p = (const char *) data; for (i=0; i < count; i++, p += channel->block_size, block++) { error = io_channel_write_blk(channel, block, 1, p); if (error) return error; } return 0; } if (operation) printf(_("Error writing block %lu (%s) while %s. "), block, error_message(error), operation); else printf(_("Error writing block %lu (%s). "), block, error_message(error)); preenhalt(ctx); if (ask(ctx, _("Ignore error"), 1)) return 0; return error; } static const char *ehandler_operation(const char *op) { const char *ret = operation; operation = op; return ret; } static void ehandler_init(io_channel channel) { channel->read_error = e2fsck_handle_read_error; channel->write_error = e2fsck_handle_write_error; } /* * journal.c --- code for handling the "ext3" journal * * Copyright (C) 2000 Andreas Dilger * Copyright (C) 2000 Theodore Ts'o * * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie * Copyright (C) 1999 Red Hat Software * * This file may be redistributed under the terms of the * GNU General Public License version 2 or at your discretion * any later version. */ /* * Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths. * This creates a larger static binary, and a smaller binary using * shared libraries. It's also probably slightly less CPU-efficient, * which is why it's not on by default. But, it's a good way of * testing the functions in inode_io.c and fileio.c. */ #undef USE_INODE_IO /* Kernel compatibility functions for handling the journal. These allow us * to use the recovery.c file virtually unchanged from the kernel, so we * don't have to do much to keep kernel and user recovery in sync. */ static int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys) { #ifdef USE_INODE_IO *phys = block; return 0; #else struct inode *inode = journal->j_inode; errcode_t retval; blk_t pblk; if (!inode) { *phys = block; return 0; } retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino, &inode->i_ext2, NULL, 0, block, &pblk); *phys = pblk; return retval; #endif } static struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize) { struct buffer_head *bh; bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer"); if (!bh) return NULL; bh->b_ctx = kdev->k_ctx; if (kdev->k_dev == K_DEV_FS) bh->b_io = kdev->k_ctx->fs->io; else bh->b_io = kdev->k_ctx->journal_io; bh->b_size = blocksize; bh->b_blocknr = blocknr; return bh; } static void sync_blockdev(kdev_t kdev) { io_channel io; if (kdev->k_dev == K_DEV_FS) io = kdev->k_ctx->fs->io; else io = kdev->k_ctx->journal_io; io_channel_flush(io); } static void ll_rw_block(int rw, int nr, struct buffer_head *bhp[]) { int retval; struct buffer_head *bh; for (; nr > 0; --nr) { bh = *bhp++; if (rw == READ && !bh->b_uptodate) { retval = io_channel_read_blk(bh->b_io, bh->b_blocknr, 1, bh->b_data); if (retval) { bb_error_msg("while reading block %lu", (unsigned long) bh->b_blocknr); bh->b_err = retval; continue; } bh->b_uptodate = 1; } else if (rw == WRITE && bh->b_dirty) { retval = io_channel_write_blk(bh->b_io, bh->b_blocknr, 1, bh->b_data); if (retval) { bb_error_msg("while writing block %lu", (unsigned long) bh->b_blocknr); bh->b_err = retval; continue; } bh->b_dirty = 0; bh->b_uptodate = 1; } } } static void mark_buffer_dirty(struct buffer_head *bh) { bh->b_dirty = 1; } static inline void mark_buffer_clean(struct buffer_head * bh) { bh->b_dirty = 0; } static void brelse(struct buffer_head *bh) { if (bh->b_dirty) ll_rw_block(WRITE, 1, &bh); ext2fs_free_mem(&bh); } static int buffer_uptodate(struct buffer_head *bh) { return bh->b_uptodate; } static inline void mark_buffer_uptodate(struct buffer_head *bh, int val) { bh->b_uptodate = val; } static void wait_on_buffer(struct buffer_head *bh) { if (!bh->b_uptodate) ll_rw_block(READ, 1, &bh); } static void e2fsck_clear_recover(e2fsck_t ctx, int error) { ctx->fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER; /* if we had an error doing journal recovery, we need a full fsck */ if (error) ctx->fs->super->s_state &= ~EXT2_VALID_FS; ext2fs_mark_super_dirty(ctx->fs); } static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal) { struct ext2_super_block *sb = ctx->fs->super; struct ext2_super_block jsuper; struct problem_context pctx; struct buffer_head *bh; struct inode *j_inode = NULL; struct kdev_s *dev_fs = NULL, *dev_journal; const char *journal_name = NULL; journal_t *journal = NULL; errcode_t retval = 0; io_manager io_ptr = 0; unsigned long start = 0; blk_t blk; int ext_journal = 0; int tried_backup_jnl = 0; int i; clear_problem_context(&pctx); journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal"); if (!journal) { return EXT2_ET_NO_MEMORY; } dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev"); if (!dev_fs) { retval = EXT2_ET_NO_MEMORY; goto errout; } dev_journal = dev_fs+1; dev_fs->k_ctx = dev_journal->k_ctx = ctx; dev_fs->k_dev = K_DEV_FS; dev_journal->k_dev = K_DEV_JOURNAL; journal->j_dev = dev_journal; journal->j_fs_dev = dev_fs; journal->j_inode = NULL; journal->j_blocksize = ctx->fs->blocksize; if (uuid_is_null(sb->s_journal_uuid)) { if (!sb->s_journal_inum) return EXT2_ET_BAD_INODE_NUM; j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode), "journal inode"); if (!j_inode) { retval = EXT2_ET_NO_MEMORY; goto errout; } j_inode->i_ctx = ctx; j_inode->i_ino = sb->s_journal_inum; if ((retval = ext2fs_read_inode(ctx->fs, sb->s_journal_inum, &j_inode->i_ext2))) { try_backup_journal: if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS || tried_backup_jnl) goto errout; memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode)); memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks, EXT2_N_BLOCKS*4); j_inode->i_ext2.i_size = sb->s_jnl_blocks[16]; j_inode->i_ext2.i_links_count = 1; j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600; tried_backup_jnl++; } if (!j_inode->i_ext2.i_links_count || !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) { retval = EXT2_ET_NO_JOURNAL; goto try_backup_journal; } if (j_inode->i_ext2.i_size / journal->j_blocksize < JFS_MIN_JOURNAL_BLOCKS) { retval = EXT2_ET_JOURNAL_TOO_SMALL; goto try_backup_journal; } for (i=0; i < EXT2_N_BLOCKS; i++) { blk = j_inode->i_ext2.i_block[i]; if (!blk) { if (i < EXT2_NDIR_BLOCKS) { retval = EXT2_ET_JOURNAL_TOO_SMALL; goto try_backup_journal; } continue; } if (blk < sb->s_first_data_block || blk >= sb->s_blocks_count) { retval = EXT2_ET_BAD_BLOCK_NUM; goto try_backup_journal; } } journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize; #ifdef USE_INODE_IO retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum, &j_inode->i_ext2, &journal_name); if (retval) goto errout; io_ptr = inode_io_manager; #else journal->j_inode = j_inode; ctx->journal_io = ctx->fs->io; if ((retval = journal_bmap(journal, 0, &start)) != 0) goto errout; #endif } else { ext_journal = 1; if (!ctx->journal_name) { char uuid[37]; uuid_unparse(sb->s_journal_uuid, uuid); ctx->journal_name = blkid_get_devname(ctx->blkid, "UUID", uuid); if (!ctx->journal_name) ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev); } journal_name = ctx->journal_name; if (!journal_name) { fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx); return EXT2_ET_LOAD_EXT_JOURNAL; } io_ptr = unix_io_manager; } #ifndef USE_INODE_IO if (ext_journal) #endif retval = io_ptr->open(journal_name, IO_FLAG_RW, &ctx->journal_io); if (retval) goto errout; io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize); if (ext_journal) { if (ctx->fs->blocksize == 1024) start = 1; bh = getblk(dev_journal, start, ctx->fs->blocksize); if (!bh) { retval = EXT2_ET_NO_MEMORY; goto errout; } ll_rw_block(READ, 1, &bh); if ((retval = bh->b_err) != 0) goto errout; memcpy(&jsuper, start ? bh->b_data : bh->b_data + 1024, sizeof(jsuper)); brelse(bh); #if BB_BIG_ENDIAN if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ext2fs_swap_super(&jsuper); #endif if (jsuper.s_magic != EXT2_SUPER_MAGIC || !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx); retval = EXT2_ET_LOAD_EXT_JOURNAL; goto errout; } /* Make sure the journal UUID is correct */ if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid, sizeof(jsuper.s_uuid))) { fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx); retval = EXT2_ET_LOAD_EXT_JOURNAL; goto errout; } journal->j_maxlen = jsuper.s_blocks_count; start++; } if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) { retval = EXT2_ET_NO_MEMORY; goto errout; } journal->j_sb_buffer = bh; journal->j_superblock = (journal_superblock_t *)bh->b_data; #ifdef USE_INODE_IO ext2fs_free_mem(&j_inode); #endif *ret_journal = journal; return 0; errout: ext2fs_free_mem(&dev_fs); ext2fs_free_mem(&j_inode); ext2fs_free_mem(&journal); return retval; } static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx, struct problem_context *pctx) { struct ext2_super_block *sb = ctx->fs->super; int recover = ctx->fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER; int has_journal = ctx->fs->super->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL; if (has_journal || sb->s_journal_inum) { /* The journal inode is bogus, remove and force full fsck */ pctx->ino = sb->s_journal_inum; if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) { if (has_journal && sb->s_journal_inum) printf("*** ext3 journal has been deleted - " "filesystem is now ext2 only ***\n\n"); sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL; sb->s_journal_inum = 0; ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */ e2fsck_clear_recover(ctx, 1); return 0; } return EXT2_ET_BAD_INODE_NUM; } else if (recover) { if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) { e2fsck_clear_recover(ctx, 1); return 0; } return EXT2_ET_UNSUPP_FEATURE; } return 0; } #define V1_SB_SIZE 0x0024 static void clear_v2_journal_fields(journal_t *journal) { e2fsck_t ctx = journal->j_dev->k_ctx; struct problem_context pctx; clear_problem_context(&pctx); if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx)) return; memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0, ctx->fs->blocksize-V1_SB_SIZE); mark_buffer_dirty(journal->j_sb_buffer); } static errcode_t e2fsck_journal_load(journal_t *journal) { e2fsck_t ctx = journal->j_dev->k_ctx; journal_superblock_t *jsb; struct buffer_head *jbh = journal->j_sb_buffer; struct problem_context pctx; clear_problem_context(&pctx); ll_rw_block(READ, 1, &jbh); if (jbh->b_err) { bb_error_msg(_("reading journal superblock")); return jbh->b_err; } jsb = journal->j_superblock; /* If we don't even have JFS_MAGIC, we probably have a wrong inode */ if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER)) return e2fsck_journal_fix_bad_inode(ctx, &pctx); switch (ntohl(jsb->s_header.h_blocktype)) { case JFS_SUPERBLOCK_V1: journal->j_format_version = 1; if (jsb->s_feature_compat || jsb->s_feature_incompat || jsb->s_feature_ro_compat || jsb->s_nr_users) clear_v2_journal_fields(journal); break; case JFS_SUPERBLOCK_V2: journal->j_format_version = 2; if (ntohl(jsb->s_nr_users) > 1 && uuid_is_null(ctx->fs->super->s_journal_uuid)) clear_v2_journal_fields(journal); if (ntohl(jsb->s_nr_users) > 1) { fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx); return EXT2_ET_JOURNAL_UNSUPP_VERSION; } break; /* * These should never appear in a journal super block, so if * they do, the journal is badly corrupted. */ case JFS_DESCRIPTOR_BLOCK: case JFS_COMMIT_BLOCK: case JFS_REVOKE_BLOCK: return EXT2_ET_CORRUPT_SUPERBLOCK; /* If we don't understand the superblock major type, but there * is a magic number, then it is likely to be a new format we * just don't understand, so leave it alone. */ default: return EXT2_ET_JOURNAL_UNSUPP_VERSION; } if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES)) return EXT2_ET_UNSUPP_FEATURE; if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES)) return EXT2_ET_RO_UNSUPP_FEATURE; /* We have now checked whether we know enough about the journal * format to be able to proceed safely, so any other checks that * fail we should attempt to recover from. */ if (jsb->s_blocksize != htonl(journal->j_blocksize)) { bb_error_msg(_("%s: no valid journal superblock found"), ctx->device_name); return EXT2_ET_CORRUPT_SUPERBLOCK; } if (ntohl(jsb->s_maxlen) < journal->j_maxlen) journal->j_maxlen = ntohl(jsb->s_maxlen); else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) { bb_error_msg(_("%s: journal too short"), ctx->device_name); return EXT2_ET_CORRUPT_SUPERBLOCK; } journal->j_tail_sequence = ntohl(jsb->s_sequence); journal->j_transaction_sequence = journal->j_tail_sequence; journal->j_tail = ntohl(jsb->s_start); journal->j_first = ntohl(jsb->s_first); journal->j_last = ntohl(jsb->s_maxlen); return 0; } static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb, journal_t *journal) { char *p; union { uuid_t uuid; __u32 val[4]; } u; __u32 new_seq = 0; int i; /* Leave a valid existing V1 superblock signature alone. * Anything unrecognizable we overwrite with a new V2 * signature. */ if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) || jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) { jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER); jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2); } /* Zero out everything else beyond the superblock header */ p = ((char *) jsb) + sizeof(journal_header_t); memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t)); jsb->s_blocksize = htonl(ctx->fs->blocksize); jsb->s_maxlen = htonl(journal->j_maxlen); jsb->s_first = htonl(1); /* Initialize the journal sequence number so that there is "no" * chance we will find old "valid" transactions in the journal. * This avoids the need to zero the whole journal (slow to do, * and risky when we are just recovering the filesystem). */ uuid_generate(u.uuid); for (i = 0; i < 4; i ++) new_seq ^= u.val[i]; jsb->s_sequence = htonl(new_seq); mark_buffer_dirty(journal->j_sb_buffer); ll_rw_block(WRITE, 1, &journal->j_sb_buffer); } static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx, journal_t *journal, struct problem_context *pctx) { struct ext2_super_block *sb = ctx->fs->super; int recover = ctx->fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER; if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) { if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) { e2fsck_journal_reset_super(ctx, journal->j_superblock, journal); journal->j_transaction_sequence = 1; e2fsck_clear_recover(ctx, recover); return 0; } return EXT2_ET_CORRUPT_SUPERBLOCK; } else if (e2fsck_journal_fix_bad_inode(ctx, pctx)) return EXT2_ET_CORRUPT_SUPERBLOCK; return 0; } static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal, int reset, int drop) { journal_superblock_t *jsb; if (drop) mark_buffer_clean(journal->j_sb_buffer); else if (!(ctx->options & E2F_OPT_READONLY)) { jsb = journal->j_superblock; jsb->s_sequence = htonl(journal->j_transaction_sequence); if (reset) jsb->s_start = 0; /* this marks the journal as empty */ mark_buffer_dirty(journal->j_sb_buffer); } brelse(journal->j_sb_buffer); if (ctx->journal_io) { if (ctx->fs && ctx->fs->io != ctx->journal_io) io_channel_close(ctx->journal_io); ctx->journal_io = 0; } #ifndef USE_INODE_IO ext2fs_free_mem(&journal->j_inode); #endif ext2fs_free_mem(&journal->j_fs_dev); ext2fs_free_mem(&journal); } /* * This function makes sure that the superblock fields regarding the * journal are consistent. */ static int e2fsck_check_ext3_journal(e2fsck_t ctx) { struct ext2_super_block *sb = ctx->fs->super; journal_t *journal; int recover = ctx->fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER; struct problem_context pctx; problem_t problem; int reset = 0, force_fsck = 0; int retval; /* If we don't have any journal features, don't do anything more */ if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && !recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 && uuid_is_null(sb->s_journal_uuid)) return 0; clear_problem_context(&pctx); pctx.num = sb->s_journal_inum; retval = e2fsck_get_journal(ctx, &journal); if (retval) { if ((retval == EXT2_ET_BAD_INODE_NUM) || (retval == EXT2_ET_BAD_BLOCK_NUM) || (retval == EXT2_ET_JOURNAL_TOO_SMALL) || (retval == EXT2_ET_NO_JOURNAL)) return e2fsck_journal_fix_bad_inode(ctx, &pctx); return retval; } retval = e2fsck_journal_load(journal); if (retval) { if ((retval == EXT2_ET_CORRUPT_SUPERBLOCK) || ((retval == EXT2_ET_UNSUPP_FEATURE) && (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_INCOMPAT, &pctx))) || ((retval == EXT2_ET_RO_UNSUPP_FEATURE) && (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_ROCOMPAT, &pctx))) || ((retval == EXT2_ET_JOURNAL_UNSUPP_VERSION) && (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_VERSION, &pctx)))) retval = e2fsck_journal_fix_corrupt_super(ctx, journal, &pctx); e2fsck_journal_release(ctx, journal, 0, 1); return retval; } /* * We want to make the flags consistent here. We will not leave with * needs_recovery set but has_journal clear. We can't get in a loop * with -y, -n, or -p, only if a user isn't making up their mind. */ no_has_journal: if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) { recover = sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER; pctx.str = "inode"; if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) { if (recover && !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx)) goto no_has_journal; /* * Need a full fsck if we are releasing a * journal stored on a reserved inode. */ force_fsck = recover || (sb->s_journal_inum < EXT2_FIRST_INODE(sb)); /* Clear all of the journal fields */ sb->s_journal_inum = 0; sb->s_journal_dev = 0; memset(sb->s_journal_uuid, 0, sizeof(sb->s_journal_uuid)); e2fsck_clear_recover(ctx, force_fsck); } else if (!(ctx->options & E2F_OPT_READONLY)) { sb->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; ext2fs_mark_super_dirty(ctx->fs); } } if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL && !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) && journal->j_superblock->s_start != 0) { /* Print status information */ fix_problem(ctx, PR_0_JOURNAL_RECOVERY_CLEAR, &pctx); if (ctx->superblock) problem = PR_0_JOURNAL_RUN_DEFAULT; else problem = PR_0_JOURNAL_RUN; if (fix_problem(ctx, problem, &pctx)) { ctx->options |= E2F_OPT_FORCE; sb->s_feature_incompat |= EXT3_FEATURE_INCOMPAT_RECOVER; ext2fs_mark_super_dirty(ctx->fs); } else if (fix_problem(ctx, PR_0_JOURNAL_RESET_JOURNAL, &pctx)) { reset = 1; sb->s_state &= ~EXT2_VALID_FS; ext2fs_mark_super_dirty(ctx->fs); } /* * If the user answers no to the above question, we * ignore the fact that journal apparently has data; * accidentally replaying over valid data would be far * worse than skipping a questionable recovery. * * XXX should we abort with a fatal error here? What * will the ext3 kernel code do if a filesystem with * !NEEDS_RECOVERY but with a non-zero * journal->j_superblock->s_start is mounted? */ } e2fsck_journal_release(ctx, journal, reset, 0); return retval; } static errcode_t recover_ext3_journal(e2fsck_t ctx) { journal_t *journal; int retval; journal_init_revoke_caches(); retval = e2fsck_get_journal(ctx, &journal); if (retval) return retval; retval = e2fsck_journal_load(journal); if (retval) goto errout; retval = journal_init_revoke(journal, 1024); if (retval) goto errout; retval = -journal_recover(journal); if (retval) goto errout; if (journal->j_superblock->s_errno) { ctx->fs->super->s_state |= EXT2_ERROR_FS; ext2fs_mark_super_dirty(ctx->fs); journal->j_superblock->s_errno = 0; mark_buffer_dirty(journal->j_sb_buffer); } errout: journal_destroy_revoke(journal); journal_destroy_revoke_caches(); e2fsck_journal_release(ctx, journal, 1, 0); return retval; } static int e2fsck_run_ext3_journal(e2fsck_t ctx) { io_manager io_ptr = ctx->fs->io->manager; int blocksize = ctx->fs->blocksize; errcode_t retval, recover_retval; printf(_("%s: recovering journal\n"), ctx->device_name); if (ctx->options & E2F_OPT_READONLY) { printf(_("%s: won't do journal recovery while read-only\n"), ctx->device_name); return EXT2_ET_FILE_RO; } if (ctx->fs->flags & EXT2_FLAG_DIRTY) ext2fs_flush(ctx->fs); /* Force out any modifications */ recover_retval = recover_ext3_journal(ctx); /* * Reload the filesystem context to get up-to-date data from disk * because journal recovery will change the filesystem under us. */ ext2fs_close(ctx->fs); retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW, ctx->superblock, blocksize, io_ptr, &ctx->fs); if (retval) { bb_error_msg(_("while trying to re-open %s"), ctx->device_name); bb_error_msg_and_die(0); } ctx->fs->priv_data = ctx; /* Set the superblock flags */ e2fsck_clear_recover(ctx, recover_retval); return recover_retval; } /* * This function will move the journal inode from a visible file in * the filesystem directory hierarchy to the reserved inode if necessary. */ static const char *const journal_names[] = { ".journal", "journal", ".journal.dat", "journal.dat", 0 }; static void e2fsck_move_ext3_journal(e2fsck_t ctx) { struct ext2_super_block *sb = ctx->fs->super; struct problem_context pctx; struct ext2_inode inode; ext2_filsys fs = ctx->fs; ext2_ino_t ino; errcode_t retval; const char *const * cpp; int group, mount_flags; clear_problem_context(&pctx); /* * If the filesystem is opened read-only, or there is no * journal, then do nothing. */ if ((ctx->options & E2F_OPT_READONLY) || (sb->s_journal_inum == 0) || !(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) return; /* * Read in the journal inode */ if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0) return; /* * If it's necessary to backup the journal inode, do so. */ if ((sb->s_jnl_backup_type == 0) || ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) && memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) { if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) { memcpy(sb->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4); sb->s_jnl_blocks[16] = inode.i_size; sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; ext2fs_mark_super_dirty(fs); fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; } } /* * If the journal is already the hidden inode, then do nothing */ if (sb->s_journal_inum == EXT2_JOURNAL_INO) return; /* * The journal inode had better have only one link and not be readable. */ if (inode.i_links_count != 1) return; /* * If the filesystem is mounted, or we can't tell whether * or not it's mounted, do nothing. */ retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags); if (retval || (mount_flags & EXT2_MF_MOUNTED)) return; /* * If we can't find the name of the journal inode, then do * nothing. */ for (cpp = journal_names; *cpp; cpp++) { retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp, strlen(*cpp), 0, &ino); if ((retval == 0) && (ino == sb->s_journal_inum)) break; } if (*cpp == 0) return; /* We need the inode bitmap to be loaded */ retval = ext2fs_read_bitmaps(fs); if (retval) return; pctx.str = *cpp; if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx)) return; /* * OK, we've done all the checks, let's actually move the * journal inode. Errors at this point mean we need to force * an ext2 filesystem check. */ if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0) goto err_out; if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0) goto err_out; sb->s_journal_inum = EXT2_JOURNAL_INO; ext2fs_mark_super_dirty(fs); fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; inode.i_links_count = 0; inode.i_dtime = time(NULL); if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0) goto err_out; group = ext2fs_group_of_ino(fs, ino); ext2fs_unmark_inode_bitmap(fs->inode_map, ino); ext2fs_mark_ib_dirty(fs); fs->group_desc[group].bg_free_inodes_count++; fs->super->s_free_inodes_count++; return; err_out: pctx.errcode = retval; fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx); fs->super->s_state &= ~EXT2_VALID_FS; ext2fs_mark_super_dirty(fs); } /* * message.c --- print e2fsck messages (with compression) * * print_e2fsck_message() prints a message to the user, using * compression techniques and expansions of abbreviations. * * The following % expansions are supported: * * %b block number * %B integer * %c block number * %Di ->ino inode number * %Dn ->name string * %Dr ->rec_len * %Dl ->name_len * %Dt ->filetype * %d inode number * %g integer * %i inode number * %Is -> i_size * %IS -> i_extra_isize * %Ib -> i_blocks * %Il -> i_links_count * %Im -> i_mode * %IM -> i_mtime * %IF -> i_faddr * %If -> i_file_acl * %Id -> i_dir_acl * %Iu -> i_uid * %Ig -> i_gid * %j inode number * %m * %N * %p ext2fs_get_pathname of directory * %P ext2fs_get_pathname of ->ino with as * the containing directory. (If dirent is NULL * then return the pathname of directory ) * %q ext2fs_get_pathname of directory * %Q ext2fs_get_pathname of directory with as * the containing directory. * %s miscellaneous string * %S backup superblock * %X hexadecimal format * * The following '@' expansions are supported: * * @a extended attribute * @A error allocating * @b block * @B bitmap * @c compress * @C conflicts with some other fs block * @D deleted * @d directory * @e entry * @E Entry '%Dn' in %p (%i) * @f filesystem * @F for @i %i (%Q) is * @g group * @h HTREE directory inode * @i inode * @I illegal * @j journal * @l lost+found * @L is a link * @m multiply-claimed * @n invalid * @o orphaned * @p problem in * @r root inode * @s should be * @S superblock * @u unattached * @v device * @z zero-length */ /* * This structure defines the abbreviations used by the text strings * below. The first character in the string is the index letter. An * abbreviation of the form '@' is expanded by looking up the index * letter in the table below. */ static const char *const abbrevs[] = { N_("aextended attribute"), N_("Aerror allocating"), N_("bblock"), N_("Bbitmap"), N_("ccompress"), N_("Cconflicts with some other fs @b"), N_("iinode"), N_("Iillegal"), N_("jjournal"), N_("Ddeleted"), N_("ddirectory"), N_("eentry"), N_("E@e '%Dn' in %p (%i)"), N_("ffilesystem"), N_("Ffor @i %i (%Q) is"), N_("ggroup"), N_("hHTREE @d @i"), N_("llost+found"), N_("Lis a link"), N_("mmultiply-claimed"), N_("ninvalid"), N_("oorphaned"), N_("pproblem in"), N_("rroot @i"), N_("sshould be"), N_("Ssuper@b"), N_("uunattached"), N_("vdevice"), N_("zzero-length"), "@@", 0 }; /* * Give more user friendly names to the "special" inodes. */ #define num_special_inodes 11 static const char *const special_inode_name[] = { N_(""), /* 0 */ N_(""), /* 1 */ "/", /* 2 */ N_(""), /* 3 */ N_(""), /* 4 */ N_(""), /* 5 */ N_(""), /* 6 */ N_(""), /* 7 */ N_(""), /* 8 */ N_(""), /* 9 */ N_(""), /* 10 */ }; /* * This function does "safe" printing. It will convert non-printable * ASCII characters using '^' and M- notation. */ static void safe_print(const char *cp, int len) { unsigned char ch; if (len < 0) len = strlen(cp); while (len--) { ch = *cp++; if (ch > 128) { fputs("M-", stdout); ch -= 128; } if ((ch < 32) || (ch == 0x7f)) { bb_putchar('^'); ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */ } bb_putchar(ch); } } /* * This function prints a pathname, using the ext2fs_get_pathname * function */ static void print_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino) { errcode_t retval; char *path; if (!dir && (ino < num_special_inodes)) { fputs(_(special_inode_name[ino]), stdout); return; } retval = ext2fs_get_pathname(fs, dir, ino, &path); if (retval) fputs("???", stdout); else { safe_print(path, -1); ext2fs_free_mem(&path); } } static void print_e2fsck_message(e2fsck_t ctx, const char *msg, struct problem_context *pctx, int first); /* * This function handles the '@' expansion. We allow recursive * expansion; an @ expression can contain further '@' and '%' * expressions. */ static void expand_at_expression(e2fsck_t ctx, char ch, struct problem_context *pctx, int *first) { const char *const *cpp; const char *str; /* Search for the abbreviation */ for (cpp = abbrevs; *cpp; cpp++) { if (ch == *cpp[0]) break; } if (*cpp) { str = _(*cpp) + 1; if (*first && islower(*str)) { *first = 0; bb_putchar(toupper(*str++)); } print_e2fsck_message(ctx, str, pctx, *first); } else printf("@%c", ch); } /* * This function expands '%IX' expressions */ static void expand_inode_expression(char ch, struct problem_context *ctx) { struct ext2_inode *inode; struct ext2_inode_large *large_inode; char * time_str; time_t t; int do_gmt = -1; if (!ctx || !ctx->inode) goto no_inode; inode = ctx->inode; large_inode = (struct ext2_inode_large *) inode; switch (ch) { case 's': if (LINUX_S_ISDIR(inode->i_mode)) printf("%u", inode->i_size); else { printf("%"PRIu64, (inode->i_size | ((uint64_t) inode->i_size_high << 32))); } break; case 'S': printf("%u", large_inode->i_extra_isize); break; case 'b': printf("%u", inode->i_blocks); break; case 'l': printf("%d", inode->i_links_count); break; case 'm': printf("0%o", inode->i_mode); break; case 'M': /* The diet libc doesn't respect the TZ environemnt variable */ if (do_gmt == -1) { time_str = getenv("TZ"); if (!time_str) time_str = ""; do_gmt = !strcmp(time_str, "GMT"); } t = inode->i_mtime; time_str = asctime(do_gmt ? gmtime(&t) : localtime(&t)); printf("%.24s", time_str); break; case 'F': printf("%u", inode->i_faddr); break; case 'f': printf("%u", inode->i_file_acl); break; case 'd': printf("%u", (LINUX_S_ISDIR(inode->i_mode) ? inode->i_dir_acl : 0)); break; case 'u': printf("%d", (inode->i_uid | (inode->osd2.linux2.l_i_uid_high << 16))); break; case 'g': printf("%d", (inode->i_gid | (inode->osd2.linux2.l_i_gid_high << 16))); break; default: no_inode: printf("%%I%c", ch); break; } } /* * This function expands '%dX' expressions */ static void expand_dirent_expression(char ch, struct problem_context *ctx) { struct ext2_dir_entry *dirent; int len; if (!ctx || !ctx->dirent) goto no_dirent; dirent = ctx->dirent; switch (ch) { case 'i': printf("%u", dirent->inode); break; case 'n': len = dirent->name_len & 0xFF; if (len > EXT2_NAME_LEN) len = EXT2_NAME_LEN; if (len > dirent->rec_len) len = dirent->rec_len; safe_print(dirent->name, len); break; case 'r': printf("%u", dirent->rec_len); break; case 'l': printf("%u", dirent->name_len & 0xFF); break; case 't': printf("%u", dirent->name_len >> 8); break; default: no_dirent: printf("%%D%c", ch); break; } } static void expand_percent_expression(ext2_filsys fs, char ch, struct problem_context *ctx) { if (!ctx) goto no_context; switch (ch) { case '%': bb_putchar('%'); break; case 'b': printf("%u", ctx->blk); break; case 'B': printf("%"PRIi64, ctx->blkcount); break; case 'c': printf("%u", ctx->blk2); break; case 'd': printf("%u", ctx->dir); break; case 'g': printf("%d", ctx->group); break; case 'i': printf("%u", ctx->ino); break; case 'j': printf("%u", ctx->ino2); break; case 'm': fputs(error_message(ctx->errcode), stdout); break; case 'N': printf("%"PRIi64, ctx->num); break; case 'p': print_pathname(fs, ctx->ino, 0); break; case 'P': print_pathname(fs, ctx->ino2, ctx->dirent ? ctx->dirent->inode : 0); break; case 'q': print_pathname(fs, ctx->dir, 0); break; case 'Q': print_pathname(fs, ctx->dir, ctx->ino); break; case 'S': printf("%d", get_backup_sb(NULL, fs, NULL, NULL)); break; case 's': fputs((ctx->str ? ctx->str : "NULL"), stdout); break; case 'X': printf("0x%"PRIi64, ctx->num); break; default: no_context: printf("%%%c", ch); break; } } static void print_e2fsck_message(e2fsck_t ctx, const char *msg, struct problem_context *pctx, int first) { ext2_filsys fs = ctx->fs; const char * cp; int i; e2fsck_clear_progbar(ctx); for (cp = msg; *cp; cp++) { if (cp[0] == '@') { cp++; expand_at_expression(ctx, *cp, pctx, &first); } else if (cp[0] == '%' && cp[1] == 'I') { cp += 2; expand_inode_expression(*cp, pctx); } else if (cp[0] == '%' && cp[1] == 'D') { cp += 2; expand_dirent_expression(*cp, pctx); } else if ((cp[0] == '%')) { cp++; expand_percent_expression(fs, *cp, pctx); } else { for (i=0; cp[i]; i++) if ((cp[i] == '@') || cp[i] == '%') break; printf("%.*s", i, cp); cp += i-1; } first = 0; } } /* * region.c --- code which manages allocations within a region. */ struct region_el { region_addr_t start; region_addr_t end; struct region_el *next; }; struct region_struct { region_addr_t min; region_addr_t max; struct region_el *allocated; }; static region_t region_create(region_addr_t min, region_addr_t max) { region_t region; region = xzalloc(sizeof(struct region_struct)); region->min = min; region->max = max; return region; } static void region_free(region_t region) { struct region_el *r, *next; for (r = region->allocated; r; r = next) { next = r->next; free(r); } memset(region, 0, sizeof(struct region_struct)); free(region); } static int region_allocate(region_t region, region_addr_t start, int n) { struct region_el *r, *new_region, *prev, *next; region_addr_t end; end = start+n; if ((start < region->min) || (end > region->max)) return -1; if (n == 0) return 1; /* * Search through the linked list. If we find that it * conflicts witih something that's already allocated, return * 1; if we can find an existing region which we can grow, do * so. Otherwise, stop when we find the appropriate place * insert a new region element into the linked list. */ for (r = region->allocated, prev=NULL; r; prev = r, r = r->next) { if (((start >= r->start) && (start < r->end)) || ((end > r->start) && (end <= r->end)) || ((start <= r->start) && (end >= r->end))) return 1; if (end == r->start) { r->start = start; return 0; } if (start == r->end) { if ((next = r->next)) { if (end > next->start) return 1; if (end == next->start) { r->end = next->end; r->next = next->next; free(next); return 0; } } r->end = end; return 0; } if (start < r->start) break; } /* * Insert a new region element structure into the linked list */ new_region = xmalloc(sizeof(struct region_el)); new_region->start = start; new_region->end = start + n; new_region->next = r; if (prev) prev->next = new_region; else region->allocated = new_region; return 0; } /* * pass1.c -- pass #1 of e2fsck: sequential scan of the inode table * * Pass 1 of e2fsck iterates over all the inodes in the filesystems, * and applies the following tests to each inode: * * - The mode field of the inode must be legal. * - The size and block count fields of the inode are correct. * - A data block must not be used by another inode * * Pass 1 also gathers the collects the following information: * * - A bitmap of which inodes are in use. (inode_used_map) * - A bitmap of which inodes are directories. (inode_dir_map) * - A bitmap of which inodes are regular files. (inode_reg_map) * - A bitmap of which inodes have bad fields. (inode_bad_map) * - A bitmap of which inodes are imagic inodes. (inode_imagic_map) * - A bitmap of which blocks are in use. (block_found_map) * - A bitmap of which blocks are in use by two inodes (block_dup_map) * - The data blocks of the directory inodes. (dir_map) * * Pass 1 is designed to stash away enough information so that the * other passes should not need to read in the inode information * during the normal course of a filesystem check. (Althogh if an * inconsistency is detected, other passes may need to read in an * inode to fix it.) * * Note that pass 1B will be invoked if there are any duplicate blocks * found. */ static int process_block(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_blk, int ref_offset, void *priv_data); static int process_bad_block(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_blk, int ref_offset, void *priv_data); static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, char *block_buf); static void mark_table_blocks(e2fsck_t ctx); static void alloc_imagic_map(e2fsck_t ctx); static void mark_inode_bad(e2fsck_t ctx, ino_t ino); static void handle_fs_bad_blocks(e2fsck_t ctx); static void process_inodes(e2fsck_t ctx, char *block_buf); static int process_inode_cmp(const void *a, const void *b); static errcode_t scan_callback(ext2_filsys fs, dgrp_t group, void * priv_data); static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount, char *block_buf, int adjust_sign); /* static char *describe_illegal_block(ext2_filsys fs, blk_t block); */ static void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino, struct ext2_inode * inode, int bufsize, const char *proc); struct process_block_struct_1 { ext2_ino_t ino; unsigned is_dir:1, is_reg:1, clear:1, suppress:1, fragmented:1, compressed:1, bbcheck:1; blk_t num_blocks; blk_t max_blocks; e2_blkcnt_t last_block; int num_illegal_blocks; blk_t previous_block; struct ext2_inode *inode; struct problem_context *pctx; ext2fs_block_bitmap fs_meta_blocks; e2fsck_t ctx; }; struct process_inode_block { ext2_ino_t ino; struct ext2_inode inode; }; struct scan_callback_struct { e2fsck_t ctx; char *block_buf; }; /* * For the inodes to process list. */ static struct process_inode_block *inodes_to_process; static int process_inode_count; static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE + 1]; /* * Free all memory allocated by pass1 in preparation for restarting * things. */ static void unwind_pass1(void) { ext2fs_free_mem(&inodes_to_process); } /* * Check to make sure a device inode is real. Returns 1 if the device * checks out, 0 if not. * * Note: this routine is now also used to check FIFO's and Sockets, * since they have the same requirement; the i_block fields should be * zero. */ static int e2fsck_pass1_check_device_inode(ext2_filsys fs, struct ext2_inode *inode) { int i; /* * If i_blocks is non-zero, or the index flag is set, then * this is a bogus device/fifo/socket */ if ((ext2fs_inode_data_blocks(fs, inode) != 0) || (inode->i_flags & EXT2_INDEX_FL)) return 0; /* * We should be able to do the test below all the time, but * because the kernel doesn't forcibly clear the device * inode's additional i_block fields, there are some rare * occasions when a legitimate device inode will have non-zero * additional i_block fields. So for now, we only complain * when the immutable flag is set, which should never happen * for devices. (And that's when the problem is caused, since * you can't set or clear immutable flags for devices.) Once * the kernel has been fixed we can change this... */ if (inode->i_flags & (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)) { for (i=4; i < EXT2_N_BLOCKS; i++) if (inode->i_block[i]) return 0; } return 1; } /* * Check to make sure a symlink inode is real. Returns 1 if the symlink * checks out, 0 if not. */ static int e2fsck_pass1_check_symlink(ext2_filsys fs, struct ext2_inode *inode, char *buf) { unsigned int len; int i; blk_t blocks; if ((inode->i_size_high || inode->i_size == 0) || (inode->i_flags & EXT2_INDEX_FL)) return 0; blocks = ext2fs_inode_data_blocks(fs, inode); if (blocks) { if ((inode->i_size >= fs->blocksize) || (blocks != fs->blocksize >> 9) || (inode->i_block[0] < fs->super->s_first_data_block) || (inode->i_block[0] >= fs->super->s_blocks_count)) return 0; for (i = 1; i < EXT2_N_BLOCKS; i++) if (inode->i_block[i]) return 0; if (io_channel_read_blk(fs->io, inode->i_block[0], 1, buf)) return 0; len = strnlen(buf, fs->blocksize); if (len == fs->blocksize) return 0; } else { if (inode->i_size >= sizeof(inode->i_block)) return 0; len = strnlen((char *)inode->i_block, sizeof(inode->i_block)); if (len == sizeof(inode->i_block)) return 0; } if (len != inode->i_size) return 0; return 1; } /* * If the immutable (or append-only) flag is set on the inode, offer * to clear it. */ #define BAD_SPECIAL_FLAGS (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL) static void check_immutable(e2fsck_t ctx, struct problem_context *pctx) { if (!(pctx->inode->i_flags & BAD_SPECIAL_FLAGS)) return; if (!fix_problem(ctx, PR_1_SET_IMMUTABLE, pctx)) return; pctx->inode->i_flags &= ~BAD_SPECIAL_FLAGS; e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1"); } /* * If device, fifo or socket, check size is zero -- if not offer to * clear it */ static void check_size(e2fsck_t ctx, struct problem_context *pctx) { struct ext2_inode *inode = pctx->inode; if ((inode->i_size == 0) && (inode->i_size_high == 0)) return; if (!fix_problem(ctx, PR_1_SET_NONZSIZE, pctx)) return; inode->i_size = 0; inode->i_size_high = 0; e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1"); } static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx) { struct ext2_super_block *sb = ctx->fs->super; struct ext2_inode_large *inode; struct ext2_ext_attr_entry *entry; char *start, *end; int storage_size, remain, offs; int problem = 0; inode = (struct ext2_inode_large *) pctx->inode; storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize; start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE + inode->i_extra_isize + sizeof(__u32); end = (char *) inode + EXT2_INODE_SIZE(ctx->fs->super); entry = (struct ext2_ext_attr_entry *) start; /* scan all entry's headers first */ /* take finish entry 0UL into account */ remain = storage_size - sizeof(__u32); offs = end - start; while (!EXT2_EXT_IS_LAST_ENTRY(entry)) { /* header eats this space */ remain -= sizeof(struct ext2_ext_attr_entry); /* is attribute name valid? */ if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain) { pctx->num = entry->e_name_len; problem = PR_1_ATTR_NAME_LEN; goto fix; } /* attribute len eats this space */ remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len); /* check value size */ if (entry->e_value_size == 0 || entry->e_value_size > remain) { pctx->num = entry->e_value_size; problem = PR_1_ATTR_VALUE_SIZE; goto fix; } /* check value placement */ if (entry->e_value_offs + EXT2_XATTR_SIZE(entry->e_value_size) != offs) { printf("(entry->e_value_offs + entry->e_value_size: %d, offs: %d)\n", entry->e_value_offs + entry->e_value_size, offs); pctx->num = entry->e_value_offs; problem = PR_1_ATTR_VALUE_OFFSET; goto fix; } /* e_value_block must be 0 in inode's ea */ if (entry->e_value_block != 0) { pctx->num = entry->e_value_block; problem = PR_1_ATTR_VALUE_BLOCK; goto fix; } /* e_hash must be 0 in inode's ea */ if (entry->e_hash != 0) { pctx->num = entry->e_hash; problem = PR_1_ATTR_HASH; goto fix; } remain -= entry->e_value_size; offs -= EXT2_XATTR_SIZE(entry->e_value_size); entry = EXT2_EXT_ATTR_NEXT(entry); } fix: /* * it seems like a corruption. it's very unlikely we could repair * EA(s) in automatic fashion -bzzz */ if (problem == 0 || !fix_problem(ctx, problem, pctx)) return; /* simple remove all possible EA(s) */ *((__u32 *)start) = 0UL; e2fsck_write_inode_full(ctx, pctx->ino, (struct ext2_inode *)inode, EXT2_INODE_SIZE(sb), "pass1"); } static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx) { struct ext2_super_block *sb = ctx->fs->super; struct ext2_inode_large *inode; __u32 *eamagic; int min, max; inode = (struct ext2_inode_large *) pctx->inode; if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) { /* this isn't large inode. so, nothing to check */ return; } /* i_extra_isize must cover i_extra_isize + i_pad1 at least */ min = sizeof(inode->i_extra_isize) + sizeof(inode->i_pad1); max = EXT2_INODE_SIZE(sb) - EXT2_GOOD_OLD_INODE_SIZE; /* * For now we will allow i_extra_isize to be 0, but really * implementations should never allow i_extra_isize to be 0 */ if (inode->i_extra_isize && (inode->i_extra_isize < min || inode->i_extra_isize > max)) { if (!fix_problem(ctx, PR_1_EXTRA_ISIZE, pctx)) return; inode->i_extra_isize = min; e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode, EXT2_INODE_SIZE(sb), "pass1"); return; } eamagic = (__u32 *) (((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE + inode->i_extra_isize); if (*eamagic == EXT2_EXT_ATTR_MAGIC) { /* it seems inode has an extended attribute(s) in body */ check_ea_in_inode(ctx, pctx); } } static void e2fsck_pass1(e2fsck_t ctx) { int i; __u64 max_sizes; ext2_filsys fs = ctx->fs; ext2_ino_t ino; struct ext2_inode *inode; ext2_inode_scan scan; char *block_buf; unsigned char frag, fsize; struct problem_context pctx; struct scan_callback_struct scan_struct; struct ext2_super_block *sb = ctx->fs->super; int imagic_fs; int busted_fs_time = 0; int inode_size; clear_problem_context(&pctx); if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_1_PASS_HEADER, &pctx); if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) && !(ctx->options & E2F_OPT_NO)) { if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50)) ctx->dirs_to_hash = 0; } /* Pass 1 */ #define EXT2_BPP(bits) (1ULL << ((bits) - 2)) for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) { max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i); max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i); max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i); max_sizes = (max_sizes * (1UL << i)) - 1; ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes; } #undef EXT2_BPP imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES); /* * Allocate bitmaps structures */ pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("in-use inode map"), &ctx->inode_used_map); if (pctx.errcode) { pctx.num = 1; fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("directory inode map"), &ctx->inode_dir_map); if (pctx.errcode) { pctx.num = 2; fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("regular file inode map"), &ctx->inode_reg_map); if (pctx.errcode) { pctx.num = 6; fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } pctx.errcode = ext2fs_allocate_block_bitmap(fs, _("in-use block map"), &ctx->block_found_map); if (pctx.errcode) { pctx.num = 1; fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0, &ctx->inode_link_info); if (pctx.errcode) { fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } inode_size = EXT2_INODE_SIZE(fs->super); inode = (struct ext2_inode *) e2fsck_allocate_memory(ctx, inode_size, "scratch inode"); inodes_to_process = (struct process_inode_block *) e2fsck_allocate_memory(ctx, (ctx->process_inode_size * sizeof(struct process_inode_block)), "array of inodes to process"); process_inode_count = 0; pctx.errcode = ext2fs_init_dblist(fs, 0); if (pctx.errcode) { fix_problem(ctx, PR_1_ALLOCATE_DBCOUNT, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } /* * If the last orphan field is set, clear it, since the pass1 * processing will automatically find and clear the orphans. * In the future, we may want to try using the last_orphan * linked list ourselves, but for now, we clear it so that the * ext3 mount code won't get confused. */ if (!(ctx->options & E2F_OPT_READONLY)) { if (fs->super->s_last_orphan) { fs->super->s_last_orphan = 0; ext2fs_mark_super_dirty(fs); } } mark_table_blocks(ctx); block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3, "block interate buffer"); e2fsck_use_inode_shortcuts(ctx, 1); ehandler_operation(_("doing inode scan")); pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks, &scan); if (pctx.errcode) { fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } ext2fs_inode_scan_flags(scan, EXT2_SF_SKIP_MISSING_ITABLE, 0); ctx->stashed_inode = inode; scan_struct.ctx = ctx; scan_struct.block_buf = block_buf; ext2fs_set_inode_callback(scan, scan_callback, &scan_struct); if (ctx->progress) if ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count)) return; if ((fs->super->s_wtime < fs->super->s_inodes_count) || (fs->super->s_mtime < fs->super->s_inodes_count)) busted_fs_time = 1; while (1) { pctx.errcode = ext2fs_get_next_inode_full(scan, &ino, inode, inode_size); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) { continue; } if (pctx.errcode) { fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } if (!ino) break; pctx.ino = ino; pctx.inode = inode; ctx->stashed_ino = ino; if (inode->i_links_count) { pctx.errcode = ext2fs_icount_store(ctx->inode_link_info, ino, inode->i_links_count); if (pctx.errcode) { pctx.num = inode->i_links_count; fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } } if (ino == EXT2_BAD_INO) { struct process_block_struct_1 pb; pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map, &pb.fs_meta_blocks); if (pctx.errcode) { pctx.num = 4; fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } pb.ino = EXT2_BAD_INO; pb.num_blocks = pb.last_block = 0; pb.num_illegal_blocks = 0; pb.suppress = 0; pb.clear = 0; pb.is_dir = 0; pb.is_reg = 0; pb.fragmented = 0; pb.bbcheck = 0; pb.inode = inode; pb.pctx = &pctx; pb.ctx = ctx; pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, process_bad_block, &pb); ext2fs_free_block_bitmap(pb.fs_meta_blocks); if (pctx.errcode) { fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } if (pb.bbcheck) if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK_PROMPT, &pctx)) { ctx->flags |= E2F_FLAG_ABORT; return; } ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino); clear_problem_context(&pctx); continue; } else if (ino == EXT2_ROOT_INO) { /* * Make sure the root inode is a directory; if * not, offer to clear it. It will be * regnerated in pass #3. */ if (!LINUX_S_ISDIR(inode->i_mode)) { if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) { inode->i_dtime = time(NULL); inode->i_links_count = 0; ext2fs_icount_store(ctx->inode_link_info, ino, 0); e2fsck_write_inode(ctx, ino, inode, "pass1"); } } /* * If dtime is set, offer to clear it. mke2fs * version 0.2b created filesystems with the * dtime field set for the root and lost+found * directories. We won't worry about * /lost+found, since that can be regenerated * easily. But we will fix the root directory * as a special case. */ if (inode->i_dtime && inode->i_links_count) { if (fix_problem(ctx, PR_1_ROOT_DTIME, &pctx)) { inode->i_dtime = 0; e2fsck_write_inode(ctx, ino, inode, "pass1"); } } } else if (ino == EXT2_JOURNAL_INO) { ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino); if (fs->super->s_journal_inum == EXT2_JOURNAL_INO) { if (!LINUX_S_ISREG(inode->i_mode) && fix_problem(ctx, PR_1_JOURNAL_BAD_MODE, &pctx)) { inode->i_mode = LINUX_S_IFREG; e2fsck_write_inode(ctx, ino, inode, "pass1"); } check_blocks(ctx, &pctx, block_buf); continue; } if ((inode->i_links_count || inode->i_blocks || inode->i_block[0]) && fix_problem(ctx, PR_1_JOURNAL_INODE_NOT_CLEAR, &pctx)) { memset(inode, 0, inode_size); ext2fs_icount_store(ctx->inode_link_info, ino, 0); e2fsck_write_inode_full(ctx, ino, inode, inode_size, "pass1"); } } else if (ino < EXT2_FIRST_INODE(fs->super)) { int problem = 0; ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino); if (ino == EXT2_BOOT_LOADER_INO) { if (LINUX_S_ISDIR(inode->i_mode)) problem = PR_1_RESERVED_BAD_MODE; } else if (ino == EXT2_RESIZE_INO) { if (inode->i_mode && !LINUX_S_ISREG(inode->i_mode)) problem = PR_1_RESERVED_BAD_MODE; } else { if (inode->i_mode != 0) problem = PR_1_RESERVED_BAD_MODE; } if (problem) { if (fix_problem(ctx, problem, &pctx)) { inode->i_mode = 0; e2fsck_write_inode(ctx, ino, inode, "pass1"); } } check_blocks(ctx, &pctx, block_buf); continue; } /* * Check for inodes who might have been part of the * orphaned list linked list. They should have gotten * dealt with by now, unless the list had somehow been * corrupted. * * FIXME: In the future, inodes which are still in use * (and which are therefore) pending truncation should * be handled specially. Right now we just clear the * dtime field, and the normal e2fsck handling of * inodes where i_size and the inode blocks are * inconsistent is to fix i_size, instead of releasing * the extra blocks. This won't catch the inodes that * was at the end of the orphan list, but it's better * than nothing. The right answer is that there * shouldn't be any bugs in the orphan list handling. :-) */ if (inode->i_dtime && !busted_fs_time && inode->i_dtime < ctx->fs->super->s_inodes_count) { if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) { inode->i_dtime = inode->i_links_count ? 0 : time(NULL); e2fsck_write_inode(ctx, ino, inode, "pass1"); } } /* * This code assumes that deleted inodes have * i_links_count set to 0. */ if (!inode->i_links_count) { if (!inode->i_dtime && inode->i_mode) { if (fix_problem(ctx, PR_1_ZERO_DTIME, &pctx)) { inode->i_dtime = time(NULL); e2fsck_write_inode(ctx, ino, inode, "pass1"); } } continue; } /* * n.b. 0.3c ext2fs code didn't clear i_links_count for * deleted files. Oops. * * Since all new ext2 implementations get this right, * we now assume that the case of non-zero * i_links_count and non-zero dtime means that we * should keep the file, not delete it. * */ if (inode->i_dtime) { if (fix_problem(ctx, PR_1_SET_DTIME, &pctx)) { inode->i_dtime = 0; e2fsck_write_inode(ctx, ino, inode, "pass1"); } } ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino); switch (fs->super->s_creator_os) { case EXT2_OS_LINUX: frag = inode->osd2.linux2.l_i_frag; fsize = inode->osd2.linux2.l_i_fsize; break; case EXT2_OS_HURD: frag = inode->osd2.hurd2.h_i_frag; fsize = inode->osd2.hurd2.h_i_fsize; break; case EXT2_OS_MASIX: frag = inode->osd2.masix2.m_i_frag; fsize = inode->osd2.masix2.m_i_fsize; break; default: frag = fsize = 0; } if (inode->i_faddr || frag || fsize || (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl)) mark_inode_bad(ctx, ino); if (inode->i_flags & EXT2_IMAGIC_FL) { if (imagic_fs) { if (!ctx->inode_imagic_map) alloc_imagic_map(ctx); ext2fs_mark_inode_bitmap(ctx->inode_imagic_map, ino); } else { if (fix_problem(ctx, PR_1_SET_IMAGIC, &pctx)) { inode->i_flags &= ~EXT2_IMAGIC_FL; e2fsck_write_inode(ctx, ino, inode, "pass1"); } } } check_inode_extra_space(ctx, &pctx); if (LINUX_S_ISDIR(inode->i_mode)) { ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino); e2fsck_add_dir_info(ctx, ino, 0); ctx->fs_directory_count++; } else if (LINUX_S_ISREG (inode->i_mode)) { ext2fs_mark_inode_bitmap(ctx->inode_reg_map, ino); ctx->fs_regular_count++; } else if (LINUX_S_ISCHR (inode->i_mode) && e2fsck_pass1_check_device_inode(fs, inode)) { check_immutable(ctx, &pctx); check_size(ctx, &pctx); ctx->fs_chardev_count++; } else if (LINUX_S_ISBLK (inode->i_mode) && e2fsck_pass1_check_device_inode(fs, inode)) { check_immutable(ctx, &pctx); check_size(ctx, &pctx); ctx->fs_blockdev_count++; } else if (LINUX_S_ISLNK (inode->i_mode) && e2fsck_pass1_check_symlink(fs, inode, block_buf)) { check_immutable(ctx, &pctx); ctx->fs_symlinks_count++; if (ext2fs_inode_data_blocks(fs, inode) == 0) { ctx->fs_fast_symlinks_count++; check_blocks(ctx, &pctx, block_buf); continue; } } else if (LINUX_S_ISFIFO (inode->i_mode) && e2fsck_pass1_check_device_inode(fs, inode)) { check_immutable(ctx, &pctx); check_size(ctx, &pctx); ctx->fs_fifo_count++; } else if ((LINUX_S_ISSOCK (inode->i_mode)) && e2fsck_pass1_check_device_inode(fs, inode)) { check_immutable(ctx, &pctx); check_size(ctx, &pctx); ctx->fs_sockets_count++; } else mark_inode_bad(ctx, ino); if (inode->i_block[EXT2_IND_BLOCK]) ctx->fs_ind_count++; if (inode->i_block[EXT2_DIND_BLOCK]) ctx->fs_dind_count++; if (inode->i_block[EXT2_TIND_BLOCK]) ctx->fs_tind_count++; if (inode->i_block[EXT2_IND_BLOCK] || inode->i_block[EXT2_DIND_BLOCK] || inode->i_block[EXT2_TIND_BLOCK] || inode->i_file_acl) { inodes_to_process[process_inode_count].ino = ino; inodes_to_process[process_inode_count].inode = *inode; process_inode_count++; } else check_blocks(ctx, &pctx, block_buf); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; if (process_inode_count >= ctx->process_inode_size) { process_inodes(ctx, block_buf); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; } } process_inodes(ctx, block_buf); ext2fs_close_inode_scan(scan); ehandler_operation(0); /* * If any extended attribute blocks' reference counts need to * be adjusted, either up (ctx->refcount_extra), or down * (ctx->refcount), then fix them. */ if (ctx->refcount) { adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1); ea_refcount_free(ctx->refcount); ctx->refcount = 0; } if (ctx->refcount_extra) { adjust_extattr_refcount(ctx, ctx->refcount_extra, block_buf, +1); ea_refcount_free(ctx->refcount_extra); ctx->refcount_extra = 0; } if (ctx->invalid_bitmaps) handle_fs_bad_blocks(ctx); /* We don't need the block_ea_map any more */ ext2fs_free_block_bitmap(ctx->block_ea_map); ctx->block_ea_map = 0; if (ctx->flags & E2F_FLAG_RESIZE_INODE) { ext2fs_block_bitmap save_bmap; save_bmap = fs->block_map; fs->block_map = ctx->block_found_map; clear_problem_context(&pctx); pctx.errcode = ext2fs_create_resize_inode(fs); if (pctx.errcode) { fix_problem(ctx, PR_1_RESIZE_INODE_CREATE, &pctx); /* Should never get here */ ctx->flags |= E2F_FLAG_ABORT; return; } e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode, "recreate inode"); inode->i_mtime = time(NULL); e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode, "recreate inode"); fs->block_map = save_bmap; ctx->flags &= ~E2F_FLAG_RESIZE_INODE; } if (ctx->flags & E2F_FLAG_RESTART) { /* * Only the master copy of the superblock and block * group descriptors are going to be written during a * restart, so set the superblock to be used to be the * master superblock. */ ctx->use_superblock = 0; unwind_pass1(); goto endit; } if (ctx->block_dup_map) { if (ctx->options & E2F_OPT_PREEN) { clear_problem_context(&pctx); fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx); } e2fsck_pass1_dupblocks(ctx, block_buf); } ext2fs_free_mem(&inodes_to_process); endit: e2fsck_use_inode_shortcuts(ctx, 0); ext2fs_free_mem(&block_buf); ext2fs_free_mem(&inode); } /* * When the inode_scan routines call this callback at the end of the * glock group, call process_inodes. */ static errcode_t scan_callback(ext2_filsys fs, dgrp_t group, void * priv_data) { struct scan_callback_struct *scan_struct; e2fsck_t ctx; scan_struct = (struct scan_callback_struct *) priv_data; ctx = scan_struct->ctx; process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf); if (ctx->progress) if ((ctx->progress)(ctx, 1, group+1, ctx->fs->group_desc_count)) return EXT2_ET_CANCEL_REQUESTED; return 0; } /* * Process the inodes in the "inodes to process" list. */ static void process_inodes(e2fsck_t ctx, char *block_buf) { int i; struct ext2_inode *old_stashed_inode; ext2_ino_t old_stashed_ino; const char *old_operation; char buf[80]; struct problem_context pctx; /* begin process_inodes */ if (process_inode_count == 0) return; old_operation = ehandler_operation(0); old_stashed_inode = ctx->stashed_inode; old_stashed_ino = ctx->stashed_ino; qsort(inodes_to_process, process_inode_count, sizeof(struct process_inode_block), process_inode_cmp); clear_problem_context(&pctx); for (i=0; i < process_inode_count; i++) { pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode; pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino; sprintf(buf, _("reading indirect blocks of inode %u"), pctx.ino); ehandler_operation(buf); check_blocks(ctx, &pctx, block_buf); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) break; } ctx->stashed_inode = old_stashed_inode; ctx->stashed_ino = old_stashed_ino; process_inode_count = 0; /* end process inodes */ ehandler_operation(old_operation); } static int process_inode_cmp(const void *a, const void *b) { const struct process_inode_block *ib_a = (const struct process_inode_block *) a; const struct process_inode_block *ib_b = (const struct process_inode_block *) b; int ret; ret = (ib_a->inode.i_block[EXT2_IND_BLOCK] - ib_b->inode.i_block[EXT2_IND_BLOCK]); if (ret == 0) ret = ib_a->inode.i_file_acl - ib_b->inode.i_file_acl; return ret; } /* * Mark an inode as being bad in some what */ static void mark_inode_bad(e2fsck_t ctx, ino_t ino) { struct problem_context pctx; if (!ctx->inode_bad_map) { clear_problem_context(&pctx); pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs, _("bad inode map"), &ctx->inode_bad_map); if (pctx.errcode) { pctx.num = 3; fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx); /* Should never get here */ ctx->flags |= E2F_FLAG_ABORT; return; } } ext2fs_mark_inode_bitmap(ctx->inode_bad_map, ino); } /* * This procedure will allocate the inode imagic table */ static void alloc_imagic_map(e2fsck_t ctx) { struct problem_context pctx; clear_problem_context(&pctx); pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs, _("imagic inode map"), &ctx->inode_imagic_map); if (pctx.errcode) { pctx.num = 5; fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx); /* Should never get here */ ctx->flags |= E2F_FLAG_ABORT; return; } } /* * Marks a block as in use, setting the dup_map if it's been set * already. Called by process_block and process_bad_block. * * WARNING: Assumes checks have already been done to make sure block * is valid. This is true in both process_block and process_bad_block. */ static void mark_block_used(e2fsck_t ctx, blk_t block) { struct problem_context pctx; clear_problem_context(&pctx); if (ext2fs_fast_test_block_bitmap(ctx->block_found_map, block)) { if (!ctx->block_dup_map) { pctx.errcode = ext2fs_allocate_block_bitmap(ctx->fs, _("multiply claimed block map"), &ctx->block_dup_map); if (pctx.errcode) { pctx.num = 3; fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx); /* Should never get here */ ctx->flags |= E2F_FLAG_ABORT; return; } } ext2fs_fast_mark_block_bitmap(ctx->block_dup_map, block); } else { ext2fs_fast_mark_block_bitmap(ctx->block_found_map, block); } } /* * Adjust the extended attribute block's reference counts at the end * of pass 1, either by subtracting out references for EA blocks that * are still referenced in ctx->refcount, or by adding references for * EA blocks that had extra references as accounted for in * ctx->refcount_extra. */ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount, char *block_buf, int adjust_sign) { struct ext2_ext_attr_header *header; struct problem_context pctx; ext2_filsys fs = ctx->fs; blk_t blk; __u32 should_be; int count; clear_problem_context(&pctx); ea_refcount_intr_begin(refcount); while (1) { if ((blk = ea_refcount_intr_next(refcount, &count)) == 0) break; pctx.blk = blk; pctx.errcode = ext2fs_read_ext_attr(fs, blk, block_buf); if (pctx.errcode) { fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx); return; } header = (struct ext2_ext_attr_header *) block_buf; pctx.blkcount = header->h_refcount; should_be = header->h_refcount + adjust_sign * count; pctx.num = should_be; if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) { header->h_refcount = should_be; pctx.errcode = ext2fs_write_ext_attr(fs, blk, block_buf); if (pctx.errcode) { fix_problem(ctx, PR_1_EXTATTR_WRITE, &pctx); continue; } } } } /* * Handle processing the extended attribute blocks */ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, char *block_buf) { ext2_filsys fs = ctx->fs; ext2_ino_t ino = pctx->ino; struct ext2_inode *inode = pctx->inode; blk_t blk; char * end; struct ext2_ext_attr_header *header; struct ext2_ext_attr_entry *entry; int count; region_t region; blk = inode->i_file_acl; if (blk == 0) return 0; /* * If the Extended attribute flag isn't set, then a non-zero * file acl means that the inode is corrupted. * * Or if the extended attribute block is an invalid block, * then the inode is also corrupted. */ if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) || (blk < fs->super->s_first_data_block) || (blk >= fs->super->s_blocks_count)) { mark_inode_bad(ctx, ino); return 0; } /* If ea bitmap hasn't been allocated, create it */ if (!ctx->block_ea_map) { pctx->errcode = ext2fs_allocate_block_bitmap(fs, _("ext attr block map"), &ctx->block_ea_map); if (pctx->errcode) { pctx->num = 2; fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, pctx); ctx->flags |= E2F_FLAG_ABORT; return 0; } } /* Create the EA refcount structure if necessary */ if (!ctx->refcount) { pctx->errcode = ea_refcount_create(0, &ctx->refcount); if (pctx->errcode) { pctx->num = 1; fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx); ctx->flags |= E2F_FLAG_ABORT; return 0; } } /* Have we seen this EA block before? */ if (ext2fs_fast_test_block_bitmap(ctx->block_ea_map, blk)) { if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0) return 1; /* Ooops, this EA was referenced more than it stated */ if (!ctx->refcount_extra) { pctx->errcode = ea_refcount_create(0, &ctx->refcount_extra); if (pctx->errcode) { pctx->num = 2; fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx); ctx->flags |= E2F_FLAG_ABORT; return 0; } } ea_refcount_increment(ctx->refcount_extra, blk, 0); return 1; } /* * OK, we haven't seen this EA block yet. So we need to * validate it */ pctx->blk = blk; pctx->errcode = ext2fs_read_ext_attr(fs, blk, block_buf); if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx)) goto clear_extattr; header = (struct ext2_ext_attr_header *) block_buf; pctx->blk = inode->i_file_acl; if (((ctx->ext_attr_ver == 1) && (header->h_magic != EXT2_EXT_ATTR_MAGIC_v1)) || ((ctx->ext_attr_ver == 2) && (header->h_magic != EXT2_EXT_ATTR_MAGIC))) { if (fix_problem(ctx, PR_1_BAD_EA_BLOCK, pctx)) goto clear_extattr; } if (header->h_blocks != 1) { if (fix_problem(ctx, PR_1_EA_MULTI_BLOCK, pctx)) goto clear_extattr; } region = region_create(0, fs->blocksize); if (!region) { fix_problem(ctx, PR_1_EA_ALLOC_REGION, pctx); ctx->flags |= E2F_FLAG_ABORT; return 0; } if (region_allocate(region, 0, sizeof(struct ext2_ext_attr_header))) { if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx)) goto clear_extattr; } entry = (struct ext2_ext_attr_entry *)(header+1); end = block_buf + fs->blocksize; while ((char *)entry < end && *(__u32 *)entry) { if (region_allocate(region, (char *)entry - (char *)header, EXT2_EXT_ATTR_LEN(entry->e_name_len))) { if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx)) goto clear_extattr; } if ((ctx->ext_attr_ver == 1 && (entry->e_name_len == 0 || entry->e_name_index != 0)) || (ctx->ext_attr_ver == 2 && entry->e_name_index == 0)) { if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx)) goto clear_extattr; } if (entry->e_value_block != 0) { if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx)) goto clear_extattr; } if (entry->e_value_size && region_allocate(region, entry->e_value_offs, EXT2_EXT_ATTR_SIZE(entry->e_value_size))) { if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx)) goto clear_extattr; } entry = EXT2_EXT_ATTR_NEXT(entry); } if (region_allocate(region, (char *)entry - (char *)header, 4)) { if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx)) goto clear_extattr; } region_free(region); count = header->h_refcount - 1; if (count) ea_refcount_store(ctx->refcount, blk, count); mark_block_used(ctx, blk); ext2fs_fast_mark_block_bitmap(ctx->block_ea_map, blk); return 1; clear_extattr: inode->i_file_acl = 0; e2fsck_write_inode(ctx, ino, inode, "check_ext_attr"); return 0; } /* Returns 1 if bad htree, 0 if OK */ static int handle_htree(e2fsck_t ctx, struct problem_context *pctx, ext2_ino_t ino FSCK_ATTR((unused)), struct ext2_inode *inode, char *block_buf) { struct ext2_dx_root_info *root; ext2_filsys fs = ctx->fs; errcode_t retval; blk_t blk; if ((!LINUX_S_ISDIR(inode->i_mode) && fix_problem(ctx, PR_1_HTREE_NODIR, pctx)) || (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) && fix_problem(ctx, PR_1_HTREE_SET, pctx))) return 1; blk = inode->i_block[0]; if (((blk == 0) || (blk < fs->super->s_first_data_block) || (blk >= fs->super->s_blocks_count)) && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx)) return 1; retval = io_channel_read_blk(fs->io, blk, 1, block_buf); if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx)) return 1; /* XXX should check that beginning matches a directory */ root = (struct ext2_dx_root_info *) (block_buf + 24); if ((root->reserved_zero || root->info_length < 8) && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx)) return 1; pctx->num = root->hash_version; if ((root->hash_version != EXT2_HASH_LEGACY) && (root->hash_version != EXT2_HASH_HALF_MD4) && (root->hash_version != EXT2_HASH_TEA) && fix_problem(ctx, PR_1_HTREE_HASHV, pctx)) return 1; if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) && fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx)) return 1; pctx->num = root->indirect_levels; if ((root->indirect_levels > 1) && fix_problem(ctx, PR_1_HTREE_DEPTH, pctx)) return 1; return 0; } /* * This subroutine is called on each inode to account for all of the * blocks used by that inode. */ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, char *block_buf) { ext2_filsys fs = ctx->fs; struct process_block_struct_1 pb; ext2_ino_t ino = pctx->ino; struct ext2_inode *inode = pctx->inode; int bad_size = 0; int dirty_inode = 0; __u64 size; pb.ino = ino; pb.num_blocks = 0; pb.last_block = -1; pb.num_illegal_blocks = 0; pb.suppress = 0; pb.clear = 0; pb.fragmented = 0; pb.compressed = 0; pb.previous_block = 0; pb.is_dir = LINUX_S_ISDIR(inode->i_mode); pb.is_reg = LINUX_S_ISREG(inode->i_mode); pb.max_blocks = 1 << (31 - fs->super->s_log_block_size); pb.inode = inode; pb.pctx = pctx; pb.ctx = ctx; pctx->ino = ino; pctx->errcode = 0; if (inode->i_flags & EXT2_COMPRBLK_FL) { if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION) pb.compressed = 1; else { if (fix_problem(ctx, PR_1_COMPR_SET, pctx)) { inode->i_flags &= ~EXT2_COMPRBLK_FL; dirty_inode++; } } } if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf)) pb.num_blocks++; if (ext2fs_inode_has_valid_blocks(inode)) pctx->errcode = ext2fs_block_iterate2(fs, ino, pb.is_dir ? BLOCK_FLAG_HOLE : 0, block_buf, process_block, &pb); end_problem_latch(ctx, PR_LATCH_BLOCK); end_problem_latch(ctx, PR_LATCH_TOOBIG); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) goto out; if (pctx->errcode) fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx); if (pb.fragmented && pb.num_blocks < fs->super->s_blocks_per_group) ctx->fs_fragmented++; if (pb.clear) { inode->i_links_count = 0; ext2fs_icount_store(ctx->inode_link_info, ino, 0); inode->i_dtime = time(NULL); dirty_inode++; ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino); ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino); ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino); /* * The inode was probably partially accounted for * before processing was aborted, so we need to * restart the pass 1 scan. */ ctx->flags |= E2F_FLAG_RESTART; goto out; } if (inode->i_flags & EXT2_INDEX_FL) { if (handle_htree(ctx, pctx, ino, inode, block_buf)) { inode->i_flags &= ~EXT2_INDEX_FL; dirty_inode++; } else { #ifdef ENABLE_HTREE e2fsck_add_dx_dir(ctx, ino, pb.last_block+1); #endif } } if (ctx->dirs_to_hash && pb.is_dir && !(inode->i_flags & EXT2_INDEX_FL) && ((inode->i_size / fs->blocksize) >= 3)) ext2fs_u32_list_add(ctx->dirs_to_hash, ino); if (!pb.num_blocks && pb.is_dir) { if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) { inode->i_links_count = 0; ext2fs_icount_store(ctx->inode_link_info, ino, 0); inode->i_dtime = time(NULL); dirty_inode++; ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino); ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino); ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino); ctx->fs_directory_count--; goto out; } } pb.num_blocks *= (fs->blocksize / 512); if (pb.is_dir) { int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super); if (nblock > (pb.last_block + 1)) bad_size = 1; else if (nblock < (pb.last_block + 1)) { if (((pb.last_block + 1) - nblock) > fs->super->s_prealloc_dir_blocks) bad_size = 2; } } else { size = EXT2_I_SIZE(inode); if ((pb.last_block >= 0) && (size < (__u64) pb.last_block * fs->blocksize)) bad_size = 3; else if (size > ext2_max_sizes[fs->super->s_log_block_size]) bad_size = 4; } /* i_size for symlinks is checked elsewhere */ if (bad_size && !LINUX_S_ISLNK(inode->i_mode)) { pctx->num = (pb.last_block+1) * fs->blocksize; if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) { inode->i_size = pctx->num; if (!LINUX_S_ISDIR(inode->i_mode)) inode->i_size_high = pctx->num >> 32; dirty_inode++; } pctx->num = 0; } if (LINUX_S_ISREG(inode->i_mode) && (inode->i_size_high || inode->i_size & 0x80000000UL)) ctx->large_files++; if (pb.num_blocks != inode->i_blocks) { pctx->num = pb.num_blocks; if (fix_problem(ctx, PR_1_BAD_I_BLOCKS, pctx)) { inode->i_blocks = pb.num_blocks; dirty_inode++; } pctx->num = 0; } out: if (dirty_inode) e2fsck_write_inode(ctx, ino, inode, "check_blocks"); } /* * This is a helper function for check_blocks(). */ static int process_block(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block FSCK_ATTR((unused)), int ref_offset FSCK_ATTR((unused)), void *priv_data) { struct process_block_struct_1 *p; struct problem_context *pctx; blk_t blk = *block_nr; int ret_code = 0; int problem = 0; e2fsck_t ctx; p = (struct process_block_struct_1 *) priv_data; pctx = p->pctx; ctx = p->ctx; if (p->compressed && (blk == EXT2FS_COMPRESSED_BLKADDR)) { /* todo: Check that the comprblk_fl is high, that the blkaddr pattern looks right (all non-holes up to first EXT2FS_COMPRESSED_BLKADDR, then all EXT2FS_COMPRESSED_BLKADDR up to end of cluster), that the feature_incompat bit is high, and that the inode is a regular file. If we're doing a "full check" (a concept introduced to e2fsck by e2compr, meaning that we look at data blocks as well as metadata) then call some library routine that checks the compressed data. I'll have to think about this, because one particularly important problem to be able to fix is to recalculate the cluster size if necessary. I think that perhaps we'd better do most/all e2compr-specific checks separately, after the non-e2compr checks. If not doing a full check, it may be useful to test that the personality is linux; e.g. if it isn't then perhaps this really is just an illegal block. */ return 0; } if (blk == 0) { if (p->is_dir == 0) { /* * Should never happen, since only directories * get called with BLOCK_FLAG_HOLE */ #ifdef DEBUG_E2FSCK printf("process_block() called with blk == 0, " "blockcnt=%d, inode %lu???\n", blockcnt, p->ino); #endif return 0; } if (blockcnt < 0) return 0; if (blockcnt * fs->blocksize < p->inode->i_size) { goto mark_dir; } return 0; } /* * Simplistic fragmentation check. We merely require that the * file be contiguous. (Which can never be true for really * big files that are greater than a block group.) */ if (!HOLE_BLKADDR(p->previous_block)) { if (p->previous_block+1 != blk) p->fragmented = 1; } p->previous_block = blk; if (p->is_dir && blockcnt > (1 << (21 - fs->super->s_log_block_size))) problem = PR_1_TOOBIG_DIR; if (p->is_reg && p->num_blocks+1 >= p->max_blocks) problem = PR_1_TOOBIG_REG; if (!p->is_dir && !p->is_reg && blockcnt > 0) problem = PR_1_TOOBIG_SYMLINK; if (blk < fs->super->s_first_data_block || blk >= fs->super->s_blocks_count) problem = PR_1_ILLEGAL_BLOCK_NUM; if (problem) { p->num_illegal_blocks++; if (!p->suppress && (p->num_illegal_blocks % 12) == 0) { if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) { p->clear = 1; return BLOCK_ABORT; } if (fix_problem(ctx, PR_1_SUPPRESS_MESSAGES, pctx)) { p->suppress = 1; set_latch_flags(PR_LATCH_BLOCK, PRL_SUPPRESS, 0); } } pctx->blk = blk; pctx->blkcount = blockcnt; if (fix_problem(ctx, problem, pctx)) { blk = *block_nr = 0; ret_code = BLOCK_CHANGED; goto mark_dir; } else return 0; } if (p->ino == EXT2_RESIZE_INO) { /* * The resize inode has already be sanity checked * during pass #0 (the superblock checks). All we * have to do is mark the double indirect block as * being in use; all of the other blocks are handled * by mark_table_blocks()). */ if (blockcnt == BLOCK_COUNT_DIND) mark_block_used(ctx, blk); } else mark_block_used(ctx, blk); p->num_blocks++; if (blockcnt >= 0) p->last_block = blockcnt; mark_dir: if (p->is_dir && (blockcnt >= 0)) { pctx->errcode = ext2fs_add_dir_block(fs->dblist, p->ino, blk, blockcnt); if (pctx->errcode) { pctx->blk = blk; pctx->num = blockcnt; fix_problem(ctx, PR_1_ADD_DBLOCK, pctx); /* Should never get here */ ctx->flags |= E2F_FLAG_ABORT; return BLOCK_ABORT; } } return ret_code; } static int process_bad_block(ext2_filsys fs FSCK_ATTR((unused)), blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block FSCK_ATTR((unused)), int ref_offset FSCK_ATTR((unused)), void *priv_data EXT2FS_ATTR((unused))) { /* * Note: This function processes blocks for the bad blocks * inode, which is never compressed. So we don't use HOLE_BLKADDR(). */ printf("Unrecoverable Error: Found %"PRIi64" bad blocks starting at block number: %u\n", blockcnt, *block_nr); return BLOCK_ERROR; } /* * This routine gets called at the end of pass 1 if bad blocks are * detected in the superblock, group descriptors, inode_bitmaps, or * block bitmaps. At this point, all of the blocks have been mapped * out, so we can try to allocate new block(s) to replace the bad * blocks. */ static void handle_fs_bad_blocks(e2fsck_t ctx) { printf("Bad blocks detected on your filesystem\n" "You should get your data off as the device will soon die\n"); } /* * This routine marks all blocks which are used by the superblock, * group descriptors, inode bitmaps, and block bitmaps. */ static void mark_table_blocks(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; blk_t block, b; dgrp_t i; int j; struct problem_context pctx; clear_problem_context(&pctx); block = fs->super->s_first_data_block; for (i = 0; i < fs->group_desc_count; i++) { pctx.group = i; ext2fs_reserve_super_and_bgd(fs, i, ctx->block_found_map); /* * Mark the blocks used for the inode table */ if (fs->group_desc[i].bg_inode_table) { for (j = 0, b = fs->group_desc[i].bg_inode_table; j < fs->inode_blocks_per_group; j++, b++) { if (ext2fs_test_block_bitmap(ctx->block_found_map, b)) { pctx.blk = b; if (fix_problem(ctx, PR_1_ITABLE_CONFLICT, &pctx)) { ctx->invalid_inode_table_flag[i]++; ctx->invalid_bitmaps++; } } else { ext2fs_mark_block_bitmap(ctx->block_found_map, b); } } } /* * Mark block used for the block bitmap */ if (fs->group_desc[i].bg_block_bitmap) { if (ext2fs_test_block_bitmap(ctx->block_found_map, fs->group_desc[i].bg_block_bitmap)) { pctx.blk = fs->group_desc[i].bg_block_bitmap; if (fix_problem(ctx, PR_1_BB_CONFLICT, &pctx)) { ctx->invalid_block_bitmap_flag[i]++; ctx->invalid_bitmaps++; } } else { ext2fs_mark_block_bitmap(ctx->block_found_map, fs->group_desc[i].bg_block_bitmap); } } /* * Mark block used for the inode bitmap */ if (fs->group_desc[i].bg_inode_bitmap) { if (ext2fs_test_block_bitmap(ctx->block_found_map, fs->group_desc[i].bg_inode_bitmap)) { pctx.blk = fs->group_desc[i].bg_inode_bitmap; if (fix_problem(ctx, PR_1_IB_CONFLICT, &pctx)) { ctx->invalid_inode_bitmap_flag[i]++; ctx->invalid_bitmaps++; } } else { ext2fs_mark_block_bitmap(ctx->block_found_map, fs->group_desc[i].bg_inode_bitmap); } } block += fs->super->s_blocks_per_group; } } /* * Thes subroutines short circuits ext2fs_get_blocks and * ext2fs_check_directory; we use them since we already have the inode * structure, so there's no point in letting the ext2fs library read * the inode again. */ static errcode_t pass1_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks) { e2fsck_t ctx = (e2fsck_t) fs->priv_data; int i; if ((ino != ctx->stashed_ino) || !ctx->stashed_inode) return EXT2_ET_CALLBACK_NOTHANDLED; for (i=0; i < EXT2_N_BLOCKS; i++) blocks[i] = ctx->stashed_inode->i_block[i]; return 0; } static errcode_t pass1_read_inode(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode) { e2fsck_t ctx = (e2fsck_t) fs->priv_data; if ((ino != ctx->stashed_ino) || !ctx->stashed_inode) return EXT2_ET_CALLBACK_NOTHANDLED; *inode = *ctx->stashed_inode; return 0; } static errcode_t pass1_write_inode(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode) { e2fsck_t ctx = (e2fsck_t) fs->priv_data; if ((ino == ctx->stashed_ino) && ctx->stashed_inode) *ctx->stashed_inode = *inode; return EXT2_ET_CALLBACK_NOTHANDLED; } static errcode_t pass1_check_directory(ext2_filsys fs, ext2_ino_t ino) { e2fsck_t ctx = (e2fsck_t) fs->priv_data; if ((ino != ctx->stashed_ino) || !ctx->stashed_inode) return EXT2_ET_CALLBACK_NOTHANDLED; if (!LINUX_S_ISDIR(ctx->stashed_inode->i_mode)) return EXT2_ET_NO_DIRECTORY; return 0; } void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool) { ext2_filsys fs = ctx->fs; if (bool) { fs->get_blocks = pass1_get_blocks; fs->check_directory = pass1_check_directory; fs->read_inode = pass1_read_inode; fs->write_inode = pass1_write_inode; ctx->stashed_ino = 0; } else { fs->get_blocks = 0; fs->check_directory = 0; fs->read_inode = 0; fs->write_inode = 0; } } /* * pass1b.c --- Pass #1b of e2fsck * * This file contains pass1B, pass1C, and pass1D of e2fsck. They are * only invoked if pass 1 discovered blocks which are in use by more * than one inode. * * Pass1B scans the data blocks of all the inodes again, generating a * complete list of duplicate blocks and which inodes have claimed * them. * * Pass1C does a tree-traversal of the filesystem, to determine the * parent directories of these inodes. This step is necessary so that * e2fsck can print out the pathnames of affected inodes. * * Pass1D is a reconciliation pass. For each inode with duplicate * blocks, the user is prompted if s/he would like to clone the file * (so that the file gets a fresh copy of the duplicated blocks) or * simply to delete the file. * */ /* Needed for architectures where sizeof(int) != sizeof(void *) */ #define INT_TO_VOIDPTR(val) ((void *)(intptr_t)(val)) #define VOIDPTR_TO_INT(ptr) ((int)(intptr_t)(ptr)) /* Define an extension to the ext2 library's block count information */ #define BLOCK_COUNT_EXTATTR (-5) struct block_el { blk_t block; struct block_el *next; }; struct inode_el { ext2_ino_t inode; struct inode_el *next; }; struct dup_block { int num_bad; struct inode_el *inode_list; }; /* * This structure stores information about a particular inode which * is sharing blocks with other inodes. This information is collected * to display to the user, so that the user knows what files he or she * is dealing with, when trying to decide how to resolve the conflict * of multiply-claimed blocks. */ struct dup_inode { ext2_ino_t dir; int num_dupblocks; struct ext2_inode inode; struct block_el *block_list; }; static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_blk, int ref_offset, void *priv_data); static void delete_file(e2fsck_t ctx, ext2_ino_t ino, struct dup_inode *dp, char *block_buf); static int clone_file(e2fsck_t ctx, ext2_ino_t ino, struct dup_inode *dp, char* block_buf); static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk); static void pass1b(e2fsck_t ctx, char *block_buf); static void pass1c(e2fsck_t ctx, char *block_buf); static void pass1d(e2fsck_t ctx, char *block_buf); static int dup_inode_count = 0; static dict_t blk_dict, ino_dict; static ext2fs_inode_bitmap inode_dup_map; static int dict_int_cmp(const void *a, const void *b) { intptr_t ia, ib; ia = (intptr_t)a; ib = (intptr_t)b; return (ia-ib); } /* * Add a duplicate block record */ static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk, struct ext2_inode *inode) { dnode_t *n; struct dup_block *db; struct dup_inode *di; struct block_el *blk_el; struct inode_el *ino_el; n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk)); if (n) db = (struct dup_block *) dnode_get(n); else { db = (struct dup_block *) e2fsck_allocate_memory(ctx, sizeof(struct dup_block), "duplicate block header"); db->num_bad = 0; db->inode_list = 0; dict_alloc_insert(&blk_dict, INT_TO_VOIDPTR(blk), db); } ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx, sizeof(struct inode_el), "inode element"); ino_el->inode = ino; ino_el->next = db->inode_list; db->inode_list = ino_el; db->num_bad++; n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino)); if (n) di = (struct dup_inode *) dnode_get(n); else { di = (struct dup_inode *) e2fsck_allocate_memory(ctx, sizeof(struct dup_inode), "duplicate inode header"); di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0; di->num_dupblocks = 0; di->block_list = 0; di->inode = *inode; dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di); } blk_el = (struct block_el *) e2fsck_allocate_memory(ctx, sizeof(struct block_el), "block element"); blk_el->block = blk; blk_el->next = di->block_list; di->block_list = blk_el; di->num_dupblocks++; } /* * Free a duplicate inode record */ static void inode_dnode_free(dnode_t *node) { struct dup_inode *di; struct block_el *p, *next; di = (struct dup_inode *) dnode_get(node); for (p = di->block_list; p; p = next) { next = p->next; free(p); } free(node); } /* * Free a duplicate block record */ static void block_dnode_free(dnode_t *node) { struct dup_block *db; struct inode_el *p, *next; db = (struct dup_block *) dnode_get(node); for (p = db->inode_list; p; p = next) { next = p->next; free(p); } free(node); } /* * Main procedure for handling duplicate blocks */ void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf) { ext2_filsys fs = ctx->fs; struct problem_context pctx; clear_problem_context(&pctx); pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("multiply claimed inode map"), &inode_dup_map); if (pctx.errcode) { fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp); dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp); dict_set_allocator(&ino_dict, inode_dnode_free); dict_set_allocator(&blk_dict, block_dnode_free); pass1b(ctx, block_buf); pass1c(ctx, block_buf); pass1d(ctx, block_buf); /* * Time to free all of the accumulated data structures that we * don't need anymore. */ dict_free_nodes(&ino_dict); dict_free_nodes(&blk_dict); } /* * Scan the inodes looking for inodes that contain duplicate blocks. */ struct process_block_struct_1b { e2fsck_t ctx; ext2_ino_t ino; int dup_blocks; struct ext2_inode *inode; struct problem_context *pctx; }; static void pass1b(e2fsck_t ctx, char *block_buf) { ext2_filsys fs = ctx->fs; ext2_ino_t ino; struct ext2_inode inode; ext2_inode_scan scan; struct process_block_struct_1b pb; struct problem_context pctx; clear_problem_context(&pctx); if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_1B_PASS_HEADER, &pctx); pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks, &scan); if (pctx.errcode) { fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } ctx->stashed_inode = &inode; pb.ctx = ctx; pb.pctx = &pctx; pctx.str = "pass1b"; while (1) { pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode); if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) continue; if (pctx.errcode) { fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } if (!ino) break; pctx.ino = ctx->stashed_ino = ino; if ((ino != EXT2_BAD_INO) && !ext2fs_test_inode_bitmap(ctx->inode_used_map, ino)) continue; pb.ino = ino; pb.dup_blocks = 0; pb.inode = &inode; if (ext2fs_inode_has_valid_blocks(&inode) || (ino == EXT2_BAD_INO)) pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, process_pass1b_block, &pb); if (inode.i_file_acl) process_pass1b_block(fs, &inode.i_file_acl, BLOCK_COUNT_EXTATTR, 0, 0, &pb); if (pb.dup_blocks) { end_problem_latch(ctx, PR_LATCH_DBLOCK); if (ino >= EXT2_FIRST_INODE(fs->super) || ino == EXT2_ROOT_INO) dup_inode_count++; } if (pctx.errcode) fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx); } ext2fs_close_inode_scan(scan); e2fsck_use_inode_shortcuts(ctx, 0); } static int process_pass1b_block(ext2_filsys fs FSCK_ATTR((unused)), blk_t *block_nr, e2_blkcnt_t blockcnt FSCK_ATTR((unused)), blk_t ref_blk FSCK_ATTR((unused)), int ref_offset FSCK_ATTR((unused)), void *priv_data) { struct process_block_struct_1b *p; e2fsck_t ctx; if (HOLE_BLKADDR(*block_nr)) return 0; p = (struct process_block_struct_1b *) priv_data; ctx = p->ctx; if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) return 0; /* OK, this is a duplicate block */ if (p->ino != EXT2_BAD_INO) { p->pctx->blk = *block_nr; fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx); } p->dup_blocks++; ext2fs_mark_inode_bitmap(inode_dup_map, p->ino); add_dupe(ctx, p->ino, *block_nr, p->inode); return 0; } /* * Pass 1c: Scan directories for inodes with duplicate blocks. This * is used so that we can print pathnames when prompting the user for * what to do. */ struct search_dir_struct { int count; ext2_ino_t first_inode; ext2_ino_t max_inode; }; static int search_dirent_proc(ext2_ino_t dir, int entry, struct ext2_dir_entry *dirent, int offset FSCK_ATTR((unused)), int blocksize FSCK_ATTR((unused)), char *buf FSCK_ATTR((unused)), void *priv_data) { struct search_dir_struct *sd; struct dup_inode *p; dnode_t *n; sd = (struct search_dir_struct *) priv_data; if (dirent->inode > sd->max_inode) /* Should abort this inode, but not everything */ return 0; if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) || !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode)) return 0; n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(dirent->inode)); if (!n) return 0; p = (struct dup_inode *) dnode_get(n); p->dir = dir; sd->count--; return sd->count ? 0 : DIRENT_ABORT; } static void pass1c(e2fsck_t ctx, char *block_buf) { ext2_filsys fs = ctx->fs; struct search_dir_struct sd; struct problem_context pctx; clear_problem_context(&pctx); if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_1C_PASS_HEADER, &pctx); /* * Search through all directories to translate inodes to names * (by searching for the containing directory for that inode.) */ sd.count = dup_inode_count; sd.first_inode = EXT2_FIRST_INODE(fs->super); sd.max_inode = fs->super->s_inodes_count; ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf, search_dirent_proc, &sd); } static void pass1d(e2fsck_t ctx, char *block_buf) { ext2_filsys fs = ctx->fs; struct dup_inode *p, *t; struct dup_block *q; ext2_ino_t *shared, ino; int shared_len; int i; int file_ok; int meta_data = 0; struct problem_context pctx; dnode_t *n, *m; struct block_el *s; struct inode_el *r; clear_problem_context(&pctx); if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_1D_PASS_HEADER, &pctx); e2fsck_read_bitmaps(ctx); pctx.num = dup_inode_count; /* dict_count(&ino_dict); */ fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx); shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx, sizeof(ext2_ino_t) * dict_count(&ino_dict), "Shared inode list"); for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) { p = (struct dup_inode *) dnode_get(n); shared_len = 0; file_ok = 1; ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n)); if (ino == EXT2_BAD_INO || ino == EXT2_RESIZE_INO) continue; /* * Find all of the inodes which share blocks with this * one. First we find all of the duplicate blocks * belonging to this inode, and then search each block * get the list of inodes, and merge them together. */ for (s = p->block_list; s; s = s->next) { m = dict_lookup(&blk_dict, INT_TO_VOIDPTR(s->block)); if (!m) continue; /* Should never happen... */ q = (struct dup_block *) dnode_get(m); if (q->num_bad > 1) file_ok = 0; if (check_if_fs_block(ctx, s->block)) { file_ok = 0; meta_data = 1; } /* * Add all inodes used by this block to the * shared[] --- which is a unique list, so * if an inode is already in shared[], don't * add it again. */ for (r = q->inode_list; r; r = r->next) { if (r->inode == ino) continue; for (i = 0; i < shared_len; i++) if (shared[i] == r->inode) break; if (i == shared_len) { shared[shared_len++] = r->inode; } } } /* * Report the inode that we are working on */ pctx.inode = &p->inode; pctx.ino = ino; pctx.dir = p->dir; pctx.blkcount = p->num_dupblocks; pctx.num = meta_data ? shared_len+1 : shared_len; fix_problem(ctx, PR_1D_DUP_FILE, &pctx); pctx.blkcount = 0; pctx.num = 0; if (meta_data) fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx); for (i = 0; i < shared_len; i++) { m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i])); if (!m) continue; /* should never happen */ t = (struct dup_inode *) dnode_get(m); /* * Report the inode that we are sharing with */ pctx.inode = &t->inode; pctx.ino = shared[i]; pctx.dir = t->dir; fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx); } if (file_ok) { fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx); continue; } if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) { pctx.errcode = clone_file(ctx, ino, p, block_buf); if (pctx.errcode) fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx); else continue; } if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx)) delete_file(ctx, ino, p, block_buf); else ext2fs_unmark_valid(fs); } ext2fs_free_mem(&shared); } /* * Drop the refcount on the dup_block structure, and clear the entry * in the block_dup_map if appropriate. */ static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p) { p->num_bad--; if (p->num_bad <= 0 || (p->num_bad == 1 && !check_if_fs_block(ctx, block))) ext2fs_unmark_block_bitmap(ctx->block_dup_map, block); } static int delete_file_block(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt FSCK_ATTR((unused)), blk_t ref_block FSCK_ATTR((unused)), int ref_offset FSCK_ATTR((unused)), void *priv_data) { struct process_block_struct_1b *pb; struct dup_block *p; dnode_t *n; e2fsck_t ctx; pb = (struct process_block_struct_1b *) priv_data; ctx = pb->ctx; if (HOLE_BLKADDR(*block_nr)) return 0; if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) { n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr)); if (n) { p = (struct dup_block *) dnode_get(n); decrement_badcount(ctx, *block_nr, p); } else bb_error_msg(_("internal error; can't find dup_blk for %d"), *block_nr); } else { ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr); ext2fs_block_alloc_stats(fs, *block_nr, -1); } return 0; } static void delete_file(e2fsck_t ctx, ext2_ino_t ino, struct dup_inode *dp, char* block_buf) { ext2_filsys fs = ctx->fs; struct process_block_struct_1b pb; struct ext2_inode inode; struct problem_context pctx; unsigned int count; clear_problem_context(&pctx); pctx.ino = pb.ino = ino; pb.dup_blocks = dp->num_dupblocks; pb.ctx = ctx; pctx.str = "delete_file"; e2fsck_read_inode(ctx, ino, &inode, "delete_file"); if (ext2fs_inode_has_valid_blocks(&inode)) pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, delete_file_block, &pb); if (pctx.errcode) fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx); ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino); ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino); if (ctx->inode_bad_map) ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino); ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode)); /* Inode may have changed by block_iterate, so reread it */ e2fsck_read_inode(ctx, ino, &inode, "delete_file"); inode.i_links_count = 0; inode.i_dtime = time(NULL); if (inode.i_file_acl && (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { count = 1; pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl, block_buf, -1, &count); if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { pctx.errcode = 0; count = 1; } if (pctx.errcode) { pctx.blk = inode.i_file_acl; fix_problem(ctx, PR_1B_ADJ_EA_REFCOUNT, &pctx); } /* * If the count is zero, then arrange to have the * block deleted. If the block is in the block_dup_map, * also call delete_file_block since it will take care * of keeping the accounting straight. */ if ((count == 0) || ext2fs_test_block_bitmap(ctx->block_dup_map, inode.i_file_acl)) delete_file_block(fs, &inode.i_file_acl, BLOCK_COUNT_EXTATTR, 0, 0, &pb); } e2fsck_write_inode(ctx, ino, &inode, "delete_file"); } struct clone_struct { errcode_t errcode; ext2_ino_t dir; char *buf; e2fsck_t ctx; }; static int clone_file_block(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block FSCK_ATTR((unused)), int ref_offset FSCK_ATTR((unused)), void *priv_data) { struct dup_block *p; blk_t new_block; errcode_t retval; struct clone_struct *cs = (struct clone_struct *) priv_data; dnode_t *n; e2fsck_t ctx; ctx = cs->ctx; if (HOLE_BLKADDR(*block_nr)) return 0; if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) { n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr)); if (n) { p = (struct dup_block *) dnode_get(n); retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &new_block); if (retval) { cs->errcode = retval; return BLOCK_ABORT; } if (cs->dir && (blockcnt >= 0)) { retval = ext2fs_set_dir_block(fs->dblist, cs->dir, new_block, blockcnt); if (retval) { cs->errcode = retval; return BLOCK_ABORT; } } retval = io_channel_read_blk(fs->io, *block_nr, 1, cs->buf); if (retval) { cs->errcode = retval; return BLOCK_ABORT; } retval = io_channel_write_blk(fs->io, new_block, 1, cs->buf); if (retval) { cs->errcode = retval; return BLOCK_ABORT; } decrement_badcount(ctx, *block_nr, p); *block_nr = new_block; ext2fs_mark_block_bitmap(ctx->block_found_map, new_block); ext2fs_mark_block_bitmap(fs->block_map, new_block); return BLOCK_CHANGED; } else bb_error_msg(_("internal error; can't find dup_blk for %d"), *block_nr); } return 0; } static int clone_file(e2fsck_t ctx, ext2_ino_t ino, struct dup_inode *dp, char* block_buf) { ext2_filsys fs = ctx->fs; errcode_t retval; struct clone_struct cs; struct problem_context pctx; blk_t blk; dnode_t *n; struct inode_el *ino_el; struct dup_block *db; struct dup_inode *di; clear_problem_context(&pctx); cs.errcode = 0; cs.dir = 0; cs.ctx = ctx; retval = ext2fs_get_mem(fs->blocksize, &cs.buf); if (retval) return retval; if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) cs.dir = ino; pctx.ino = ino; pctx.str = "clone_file"; if (ext2fs_inode_has_valid_blocks(&dp->inode)) pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, clone_file_block, &cs); ext2fs_mark_bb_dirty(fs); if (pctx.errcode) { fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx); retval = pctx.errcode; goto errout; } if (cs.errcode) { bb_error_msg(_("returned from clone_file_block")); retval = cs.errcode; goto errout; } /* The inode may have changed on disk, so we have to re-read it */ e2fsck_read_inode(ctx, ino, &dp->inode, "clone file EA"); blk = dp->inode.i_file_acl; if (blk && (clone_file_block(fs, &dp->inode.i_file_acl, BLOCK_COUNT_EXTATTR, 0, 0, &cs) == BLOCK_CHANGED)) { e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA"); /* * If we cloned the EA block, find all other inodes * which refered to that EA block, and modify * them to point to the new EA block. */ n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk)); db = (struct dup_block *) dnode_get(n); for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) { if (ino_el->inode == ino) continue; n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino_el->inode)); di = (struct dup_inode *) dnode_get(n); if (di->inode.i_file_acl == blk) { di->inode.i_file_acl = dp->inode.i_file_acl; e2fsck_write_inode(ctx, ino_el->inode, &di->inode, "clone file EA"); decrement_badcount(ctx, blk, db); } } } retval = 0; errout: ext2fs_free_mem(&cs.buf); return retval; } /* * This routine returns 1 if a block overlaps with one of the superblocks, * group descriptors, inode bitmaps, or block bitmaps. */ static int check_if_fs_block(e2fsck_t ctx, blk_t test_block) { ext2_filsys fs = ctx->fs; blk_t block; dgrp_t i; block = fs->super->s_first_data_block; for (i = 0; i < fs->group_desc_count; i++) { /* Check superblocks/block group descriptros */ if (ext2fs_bg_has_super(fs, i)) { if (test_block >= block && (test_block <= block + fs->desc_blocks)) return 1; } /* Check the inode table */ if ((fs->group_desc[i].bg_inode_table) && (test_block >= fs->group_desc[i].bg_inode_table) && (test_block < (fs->group_desc[i].bg_inode_table + fs->inode_blocks_per_group))) return 1; /* Check the bitmap blocks */ if ((test_block == fs->group_desc[i].bg_block_bitmap) || (test_block == fs->group_desc[i].bg_inode_bitmap)) return 1; block += fs->super->s_blocks_per_group; } return 0; } /* * pass2.c --- check directory structure * * Pass 2 of e2fsck iterates through all active directory inodes, and * applies to following tests to each directory entry in the directory * blocks in the inodes: * * - The length of the directory entry (rec_len) should be at * least 8 bytes, and no more than the remaining space * left in the directory block. * - The length of the name in the directory entry (name_len) * should be less than (rec_len - 8). * - The inode number in the directory entry should be within * legal bounds. * - The inode number should refer to a in-use inode. * - The first entry should be '.', and its inode should be * the inode of the directory. * - The second entry should be '..'. * * To minimize disk seek time, the directory blocks are processed in * sorted order of block numbers. * * Pass 2 also collects the following information: * - The inode numbers of the subdirectories for each directory. * * Pass 2 relies on the following information from previous passes: * - The directory information collected in pass 1. * - The inode_used_map bitmap * - The inode_bad_map bitmap * - The inode_dir_map bitmap * * Pass 2 frees the following data structures * - The inode_bad_map bitmap * - The inode_reg_map bitmap */ /* * Keeps track of how many times an inode is referenced. */ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf); static int check_dir_block(ext2_filsys fs, struct ext2_db_entry *dir_blocks_info, void *priv_data); static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *dir_blocks_info, struct problem_context *pctx); static int update_dir_block(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block, int ref_offset, void *priv_data); static void clear_htree(e2fsck_t ctx, ext2_ino_t ino); static int htree_depth(struct dx_dir_info *dx_dir, struct dx_dirblock_info *dx_db); static int special_dir_block_cmp(const void *a, const void *b); struct check_dir_struct { char *buf; struct problem_context pctx; int count, max; e2fsck_t ctx; }; static void e2fsck_pass2(e2fsck_t ctx) { struct ext2_super_block *sb = ctx->fs->super; struct problem_context pctx; ext2_filsys fs = ctx->fs; char *buf; struct dir_info *dir; struct check_dir_struct cd; struct dx_dir_info *dx_dir; struct dx_dirblock_info *dx_db, *dx_parent; int b; int i, depth; problem_t code; int bad_dir; clear_problem_context(&cd.pctx); /* Pass 2 */ if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx); cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT, 0, ctx->inode_link_info, &ctx->inode_count); if (cd.pctx.errcode) { fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx); ctx->flags |= E2F_FLAG_ABORT; return; } buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize, "directory scan buffer"); /* * Set up the parent pointer for the root directory, if * present. (If the root directory is not present, we will * create it in pass 3.) */ dir = e2fsck_get_dir_info(ctx, EXT2_ROOT_INO); if (dir) dir->parent = EXT2_ROOT_INO; cd.buf = buf; cd.ctx = ctx; cd.count = 1; cd.max = ext2fs_dblist_count(fs->dblist); if (ctx->progress) (void) (ctx->progress)(ctx, 2, 0, cd.max); if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ext2fs_dblist_sort(fs->dblist, special_dir_block_cmp); cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block, &cd); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; if (cd.pctx.errcode) { fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx); ctx->flags |= E2F_FLAG_ABORT; return; } #ifdef ENABLE_HTREE for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) { if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; if (dx_dir->numblocks == 0) continue; clear_problem_context(&pctx); bad_dir = 0; pctx.dir = dx_dir->ino; dx_db = dx_dir->dx_block; if (dx_db->flags & DX_FLAG_REFERENCED) dx_db->flags |= DX_FLAG_DUP_REF; else dx_db->flags |= DX_FLAG_REFERENCED; /* * Find all of the first and last leaf blocks, and * update their parent's min and max hash values */ for (b=0, dx_db = dx_dir->dx_block; b < dx_dir->numblocks; b++, dx_db++) { if ((dx_db->type != DX_DIRBLOCK_LEAF) || !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST))) continue; dx_parent = &dx_dir->dx_block[dx_db->parent]; /* * XXX Make sure dx_parent->min_hash > dx_db->min_hash */ if (dx_db->flags & DX_FLAG_FIRST) dx_parent->min_hash = dx_db->min_hash; /* * XXX Make sure dx_parent->max_hash < dx_db->max_hash */ if (dx_db->flags & DX_FLAG_LAST) dx_parent->max_hash = dx_db->max_hash; } for (b=0, dx_db = dx_dir->dx_block; b < dx_dir->numblocks; b++, dx_db++) { pctx.blkcount = b; pctx.group = dx_db->parent; code = 0; if (!(dx_db->flags & DX_FLAG_FIRST) && (dx_db->min_hash < dx_db->node_min_hash)) { pctx.blk = dx_db->min_hash; pctx.blk2 = dx_db->node_min_hash; code = PR_2_HTREE_MIN_HASH; fix_problem(ctx, code, &pctx); bad_dir++; } if (dx_db->type == DX_DIRBLOCK_LEAF) { depth = htree_depth(dx_dir, dx_db); if (depth != dx_dir->depth) { code = PR_2_HTREE_BAD_DEPTH; fix_problem(ctx, code, &pctx); bad_dir++; } } /* * This test doesn't apply for the root block * at block #0 */ if (b && (dx_db->max_hash > dx_db->node_max_hash)) { pctx.blk = dx_db->max_hash; pctx.blk2 = dx_db->node_max_hash; code = PR_2_HTREE_MAX_HASH; fix_problem(ctx, code, &pctx); bad_dir++; } if (!(dx_db->flags & DX_FLAG_REFERENCED)) { code = PR_2_HTREE_NOTREF; fix_problem(ctx, code, &pctx); bad_dir++; } else if (dx_db->flags & DX_FLAG_DUP_REF) { code = PR_2_HTREE_DUPREF; fix_problem(ctx, code, &pctx); bad_dir++; } if (code == 0) continue; } if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) { clear_htree(ctx, dx_dir->ino); dx_dir->numblocks = 0; } } #endif ext2fs_free_mem(&buf); ext2fs_free_dblist(fs->dblist); ext2fs_free_inode_bitmap(ctx->inode_bad_map); ctx->inode_bad_map = 0; ext2fs_free_inode_bitmap(ctx->inode_reg_map); ctx->inode_reg_map = 0; clear_problem_context(&pctx); if (ctx->large_files) { if (!(sb->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_LARGE_FILE) && fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) { sb->s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_LARGE_FILE; ext2fs_mark_super_dirty(fs); } if (sb->s_rev_level == EXT2_GOOD_OLD_REV && fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) { ext2fs_update_dynamic_rev(fs); ext2fs_mark_super_dirty(fs); } } else if (!ctx->large_files && (sb->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) { if (fs->flags & EXT2_FLAG_RW) { sb->s_feature_ro_compat &= ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE; ext2fs_mark_super_dirty(fs); } } } #define MAX_DEPTH 32000 static int htree_depth(struct dx_dir_info *dx_dir, struct dx_dirblock_info *dx_db) { int depth = 0; while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) { dx_db = &dx_dir->dx_block[dx_db->parent]; depth++; } return depth; } static int dict_de_cmp(const void *a, const void *b) { const struct ext2_dir_entry *de_a, *de_b; int a_len, b_len; de_a = (const struct ext2_dir_entry *) a; a_len = de_a->name_len & 0xFF; de_b = (const struct ext2_dir_entry *) b; b_len = de_b->name_len & 0xFF; if (a_len != b_len) return (a_len - b_len); return strncmp(de_a->name, de_b->name, a_len); } /* * This is special sort function that makes sure that directory blocks * with a dirblock of zero are sorted to the beginning of the list. * This guarantees that the root node of the htree directories are * processed first, so we know what hash version to use. */ static int special_dir_block_cmp(const void *a, const void *b) { const struct ext2_db_entry *db_a = (const struct ext2_db_entry *) a; const struct ext2_db_entry *db_b = (const struct ext2_db_entry *) b; if (db_a->blockcnt && !db_b->blockcnt) return 1; if (!db_a->blockcnt && db_b->blockcnt) return -1; if (db_a->blk != db_b->blk) return (int) (db_a->blk - db_b->blk); if (db_a->ino != db_b->ino) return (int) (db_a->ino - db_b->ino); return (int) (db_a->blockcnt - db_b->blockcnt); } /* * Make sure the first entry in the directory is '.', and that the * directory entry is sane. */ static int check_dot(e2fsck_t ctx, struct ext2_dir_entry *dirent, ext2_ino_t ino, struct problem_context *pctx) { struct ext2_dir_entry *nextdir; int status = 0; int created = 0; int new_len; int problem = 0; if (!dirent->inode) problem = PR_2_MISSING_DOT; else if (((dirent->name_len & 0xFF) != 1) || (dirent->name[0] != '.')) problem = PR_2_1ST_NOT_DOT; else if (dirent->name[1] != '\0') problem = PR_2_DOT_NULL_TERM; if (problem) { if (fix_problem(ctx, problem, pctx)) { if (dirent->rec_len < 12) dirent->rec_len = 12; dirent->inode = ino; dirent->name_len = 1; dirent->name[0] = '.'; dirent->name[1] = '\0'; status = 1; created = 1; } } if (dirent->inode != ino) { if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) { dirent->inode = ino; status = 1; } } if (dirent->rec_len > 12) { new_len = dirent->rec_len - 12; if (new_len > 12) { if (created || fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) { nextdir = (struct ext2_dir_entry *) ((char *) dirent + 12); dirent->rec_len = 12; nextdir->rec_len = new_len; nextdir->inode = 0; nextdir->name_len = 0; status = 1; } } } return status; } /* * Make sure the second entry in the directory is '..', and that the * directory entry is sane. We do not check the inode number of '..' * here; this gets done in pass 3. */ static int check_dotdot(e2fsck_t ctx, struct ext2_dir_entry *dirent, struct dir_info *dir, struct problem_context *pctx) { int problem = 0; if (!dirent->inode) problem = PR_2_MISSING_DOT_DOT; else if (((dirent->name_len & 0xFF) != 2) || (dirent->name[0] != '.') || (dirent->name[1] != '.')) problem = PR_2_2ND_NOT_DOT_DOT; else if (dirent->name[2] != '\0') problem = PR_2_DOT_DOT_NULL_TERM; if (problem) { if (fix_problem(ctx, problem, pctx)) { if (dirent->rec_len < 12) dirent->rec_len = 12; /* * Note: we don't have the parent inode just * yet, so we will fill it in with the root * inode. This will get fixed in pass 3. */ dirent->inode = EXT2_ROOT_INO; dirent->name_len = 2; dirent->name[0] = '.'; dirent->name[1] = '.'; dirent->name[2] = '\0'; return 1; } return 0; } dir->dotdot = dirent->inode; return 0; } /* * Check to make sure a directory entry doesn't contain any illegal * characters. */ static int check_name(e2fsck_t ctx, struct ext2_dir_entry *dirent, struct problem_context *pctx) { int i; int fixup = -1; int ret = 0; for ( i = 0; i < (dirent->name_len & 0xFF); i++) { if (dirent->name[i] == '/' || dirent->name[i] == '\0') { if (fixup < 0) { fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx); } if (fixup) { dirent->name[i] = '.'; ret = 1; } } } return ret; } /* * Check the directory filetype (if present) */ /* * Given a mode, return the ext2 file type */ static int ext2_file_type(unsigned int mode) { if (LINUX_S_ISREG(mode)) return EXT2_FT_REG_FILE; if (LINUX_S_ISDIR(mode)) return EXT2_FT_DIR; if (LINUX_S_ISCHR(mode)) return EXT2_FT_CHRDEV; if (LINUX_S_ISBLK(mode)) return EXT2_FT_BLKDEV; if (LINUX_S_ISLNK(mode)) return EXT2_FT_SYMLINK; if (LINUX_S_ISFIFO(mode)) return EXT2_FT_FIFO; if (LINUX_S_ISSOCK(mode)) return EXT2_FT_SOCK; return 0; } static int check_filetype(e2fsck_t ctx, struct ext2_dir_entry *dirent, struct problem_context *pctx) { int filetype = dirent->name_len >> 8; int should_be = EXT2_FT_UNKNOWN; struct ext2_inode inode; if (!(ctx->fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)) { if (filetype == 0 || !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx)) return 0; dirent->name_len = dirent->name_len & 0xFF; return 1; } if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode)) { should_be = EXT2_FT_DIR; } else if (ext2fs_test_inode_bitmap(ctx->inode_reg_map, dirent->inode)) { should_be = EXT2_FT_REG_FILE; } else if (ctx->inode_bad_map && ext2fs_test_inode_bitmap(ctx->inode_bad_map, dirent->inode)) should_be = 0; else { e2fsck_read_inode(ctx, dirent->inode, &inode, "check_filetype"); should_be = ext2_file_type(inode.i_mode); } if (filetype == should_be) return 0; pctx->num = should_be; if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE, pctx) == 0) return 0; dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8; return 1; } #ifdef ENABLE_HTREE static void parse_int_node(ext2_filsys fs, struct ext2_db_entry *db, struct check_dir_struct *cd, struct dx_dir_info *dx_dir, char *block_buf) { struct ext2_dx_root_info *root; struct ext2_dx_entry *ent; struct ext2_dx_countlimit *limit; struct dx_dirblock_info *dx_db; int i, expect_limit, count; blk_t blk; ext2_dirhash_t min_hash = 0xffffffff; ext2_dirhash_t max_hash = 0; ext2_dirhash_t hash = 0, prev_hash; if (db->blockcnt == 0) { root = (struct ext2_dx_root_info *) (block_buf + 24); ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length); } else { ent = (struct ext2_dx_entry *) (block_buf+8); } limit = (struct ext2_dx_countlimit *) ent; count = ext2fs_le16_to_cpu(limit->count); expect_limit = (fs->blocksize - ((char *) ent - block_buf)) / sizeof(struct ext2_dx_entry); if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) { cd->pctx.num = ext2fs_le16_to_cpu(limit->limit); if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx)) goto clear_and_exit; } if (count > expect_limit) { cd->pctx.num = count; if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx)) goto clear_and_exit; count = expect_limit; } for (i=0; i < count; i++) { prev_hash = hash; hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0; blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff; /* Check to make sure the block is valid */ if (blk > (blk_t) dx_dir->numblocks) { cd->pctx.blk = blk; if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK, &cd->pctx)) goto clear_and_exit; } if (hash < prev_hash && fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx)) goto clear_and_exit; dx_db = &dx_dir->dx_block[blk]; if (dx_db->flags & DX_FLAG_REFERENCED) { dx_db->flags |= DX_FLAG_DUP_REF; } else { dx_db->flags |= DX_FLAG_REFERENCED; dx_db->parent = db->blockcnt; } if (hash < min_hash) min_hash = hash; if (hash > max_hash) max_hash = hash; dx_db->node_min_hash = hash; if ((i+1) < count) dx_db->node_max_hash = ext2fs_le32_to_cpu(ent[i+1].hash) & ~1; else { dx_db->node_max_hash = 0xfffffffe; dx_db->flags |= DX_FLAG_LAST; } if (i == 0) dx_db->flags |= DX_FLAG_FIRST; } dx_db = &dx_dir->dx_block[db->blockcnt]; dx_db->min_hash = min_hash; dx_db->max_hash = max_hash; return; clear_and_exit: clear_htree(cd->ctx, cd->pctx.ino); dx_dir->numblocks = 0; } #endif /* ENABLE_HTREE */ /* * Given a busted directory, try to salvage it somehow. * */ static void salvage_directory(ext2_filsys fs, struct ext2_dir_entry *dirent, struct ext2_dir_entry *prev, unsigned int *offset) { char *cp = (char *) dirent; int left = fs->blocksize - *offset - dirent->rec_len; int name_len = dirent->name_len & 0xFF; /* * Special case of directory entry of size 8: copy what's left * of the directory block up to cover up the invalid hole. */ if ((left >= 12) && (dirent->rec_len == 8)) { memmove(cp, cp+8, left); memset(cp + left, 0, 8); return; } /* * If the directory entry overruns the end of the directory * block, and the name is small enough to fit, then adjust the * record length. */ if ((left < 0) && (name_len + 8 <= dirent->rec_len + left) && dirent->inode <= fs->super->s_inodes_count && strnlen(dirent->name, name_len) == name_len) { dirent->rec_len += left; return; } /* * If the directory entry is a multiple of four, so it is * valid, let the previous directory entry absorb the invalid * one. */ if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0) { prev->rec_len += dirent->rec_len; *offset += dirent->rec_len; return; } /* * Default salvage method --- kill all of the directory * entries for the rest of the block. We will either try to * absorb it into the previous directory entry, or create a * new empty directory entry the rest of the directory block. */ if (prev) { prev->rec_len += fs->blocksize - *offset; *offset = fs->blocksize; } else { dirent->rec_len = fs->blocksize - *offset; dirent->name_len = 0; dirent->inode = 0; } } static int check_dir_block(ext2_filsys fs, struct ext2_db_entry *db, void *priv_data) { struct dir_info *subdir, *dir; struct dx_dir_info *dx_dir; #ifdef ENABLE_HTREE struct dx_dirblock_info *dx_db = NULL; #endif /* ENABLE_HTREE */ struct ext2_dir_entry *dirent, *prev; ext2_dirhash_t hash; unsigned int offset = 0; int dir_modified = 0; int dot_state; blk_t block_nr = db->blk; ext2_ino_t ino = db->ino; __u16 links; struct check_dir_struct *cd; char *buf; e2fsck_t ctx; int problem; struct ext2_dx_root_info *root; struct ext2_dx_countlimit *limit; static dict_t de_dict; struct problem_context pctx; int dups_found = 0; cd = (struct check_dir_struct *) priv_data; buf = cd->buf; ctx = cd->ctx; if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return DIRENT_ABORT; if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max)) return DIRENT_ABORT; /* * Make sure the inode is still in use (could have been * deleted in the duplicate/bad blocks pass. */ if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))) return 0; cd->pctx.ino = ino; cd->pctx.blk = block_nr; cd->pctx.blkcount = db->blockcnt; cd->pctx.ino2 = 0; cd->pctx.dirent = 0; cd->pctx.num = 0; if (db->blk == 0) { if (allocate_dir_block(ctx, db, &cd->pctx)) return 0; block_nr = db->blk; } if (db->blockcnt) dot_state = 2; else dot_state = 0; if (ctx->dirs_to_hash && ext2fs_u32_list_test(ctx->dirs_to_hash, ino)) dups_found++; cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf); if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED) cd->pctx.errcode = 0; /* We'll handle this ourselves */ if (cd->pctx.errcode) { if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) { ctx->flags |= E2F_FLAG_ABORT; return DIRENT_ABORT; } memset(buf, 0, fs->blocksize); } #ifdef ENABLE_HTREE dx_dir = e2fsck_get_dx_dir_info(ctx, ino); if (dx_dir && dx_dir->numblocks) { if (db->blockcnt >= dx_dir->numblocks) { printf("XXX should never happen!!!\n"); abort(); } dx_db = &dx_dir->dx_block[db->blockcnt]; dx_db->type = DX_DIRBLOCK_LEAF; dx_db->phys = block_nr; dx_db->min_hash = ~0; dx_db->max_hash = 0; dirent = (struct ext2_dir_entry *) buf; limit = (struct ext2_dx_countlimit *) (buf+8); if (db->blockcnt == 0) { root = (struct ext2_dx_root_info *) (buf + 24); dx_db->type = DX_DIRBLOCK_ROOT; dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST; if ((root->reserved_zero || root->info_length < 8 || root->indirect_levels > 1) && fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) { clear_htree(ctx, ino); dx_dir->numblocks = 0; dx_db = 0; } dx_dir->hashversion = root->hash_version; dx_dir->depth = root->indirect_levels + 1; } else if ((dirent->inode == 0) && (dirent->rec_len == fs->blocksize) && (dirent->name_len == 0) && (ext2fs_le16_to_cpu(limit->limit) == ((fs->blocksize-8) / sizeof(struct ext2_dx_entry)))) dx_db->type = DX_DIRBLOCK_NODE; } #endif /* ENABLE_HTREE */ dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp); prev = 0; do { problem = 0; dirent = (struct ext2_dir_entry *) (buf + offset); cd->pctx.dirent = dirent; cd->pctx.num = offset; if (((offset + dirent->rec_len) > fs->blocksize) || (dirent->rec_len < 12) || ((dirent->rec_len % 4) != 0) || (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) { if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) { salvage_directory(fs, dirent, prev, &offset); dir_modified++; continue; } else goto abort_free_dict; } if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) { if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) { dirent->name_len = EXT2_NAME_LEN; dir_modified++; } } if (dot_state == 0) { if (check_dot(ctx, dirent, ino, &cd->pctx)) dir_modified++; } else if (dot_state == 1) { dir = e2fsck_get_dir_info(ctx, ino); if (!dir) { fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx); goto abort_free_dict; } if (check_dotdot(ctx, dirent, dir, &cd->pctx)) dir_modified++; } else if (dirent->inode == ino) { problem = PR_2_LINK_DOT; if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } } if (!dirent->inode) goto next; /* * Make sure the inode listed is a legal one. */ if (((dirent->inode != EXT2_ROOT_INO) && (dirent->inode < EXT2_FIRST_INODE(fs->super))) || (dirent->inode > fs->super->s_inodes_count)) { problem = PR_2_BAD_INO; } else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, dirent->inode))) { /* * If the inode is unused, offer to clear it. */ problem = PR_2_UNUSED_INODE; } else if ((dot_state > 1) && ((dirent->name_len & 0xFF) == 1) && (dirent->name[0] == '.')) { /* * If there's a '.' entry in anything other * than the first directory entry, it's a * duplicate entry that should be removed. */ problem = PR_2_DUP_DOT; } else if ((dot_state > 1) && ((dirent->name_len & 0xFF) == 2) && (dirent->name[0] == '.') && (dirent->name[1] == '.')) { /* * If there's a '..' entry in anything other * than the second directory entry, it's a * duplicate entry that should be removed. */ problem = PR_2_DUP_DOT_DOT; } else if ((dot_state > 1) && (dirent->inode == EXT2_ROOT_INO)) { /* * Don't allow links to the root directory. * We check this specially to make sure we * catch this error case even if the root * directory hasn't been created yet. */ problem = PR_2_LINK_ROOT; } else if ((dot_state > 1) && (dirent->name_len & 0xFF) == 0) { /* * Don't allow zero-length directory names. */ problem = PR_2_NULL_NAME; } if (problem) { if (fix_problem(ctx, problem, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } else { ext2fs_unmark_valid(fs); if (problem == PR_2_BAD_INO) goto next; } } /* * If the inode was marked as having bad fields in * pass1, process it and offer to fix/clear it. * (We wait until now so that we can display the * pathname to the user.) */ if (ctx->inode_bad_map && ext2fs_test_inode_bitmap(ctx->inode_bad_map, dirent->inode)) { if (e2fsck_process_bad_inode(ctx, ino, dirent->inode, buf + fs->blocksize)) { dirent->inode = 0; dir_modified++; goto next; } if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return DIRENT_ABORT; } if (check_name(ctx, dirent, &cd->pctx)) dir_modified++; if (check_filetype(ctx, dirent, &cd->pctx)) dir_modified++; #ifdef ENABLE_HTREE if (dx_db) { ext2fs_dirhash(dx_dir->hashversion, dirent->name, (dirent->name_len & 0xFF), fs->super->s_hash_seed, &hash, 0); if (hash < dx_db->min_hash) dx_db->min_hash = hash; if (hash > dx_db->max_hash) dx_db->max_hash = hash; } #endif /* * If this is a directory, then mark its parent in its * dir_info structure. If the parent field is already * filled in, then this directory has more than one * hard link. We assume the first link is correct, * and ask the user if he/she wants to clear this one. */ if ((dot_state > 1) && (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode))) { subdir = e2fsck_get_dir_info(ctx, dirent->inode); if (!subdir) { cd->pctx.ino = dirent->inode; fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx); goto abort_free_dict; } if (subdir->parent) { cd->pctx.ino2 = subdir->parent; if (fix_problem(ctx, PR_2_LINK_DIR, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } cd->pctx.ino2 = 0; } else subdir->parent = ino; } if (dups_found) { ; } else if (dict_lookup(&de_dict, dirent)) { clear_problem_context(&pctx); pctx.ino = ino; pctx.dirent = dirent; fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx); if (!ctx->dirs_to_hash) ext2fs_u32_list_create(&ctx->dirs_to_hash, 50); if (ctx->dirs_to_hash) ext2fs_u32_list_add(ctx->dirs_to_hash, ino); dups_found++; } else dict_alloc_insert(&de_dict, dirent, dirent); ext2fs_icount_increment(ctx->inode_count, dirent->inode, &links); if (links > 1) ctx->fs_links_count++; ctx->fs_total_count++; next: prev = dirent; offset += dirent->rec_len; dot_state++; } while (offset < fs->blocksize); #ifdef ENABLE_HTREE if (dx_db) { cd->pctx.dir = cd->pctx.ino; if ((dx_db->type == DX_DIRBLOCK_ROOT) || (dx_db->type == DX_DIRBLOCK_NODE)) parse_int_node(fs, db, cd, dx_dir, buf); } #endif /* ENABLE_HTREE */ if (offset != fs->blocksize) { cd->pctx.num = dirent->rec_len - fs->blocksize + offset; if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) { dirent->rec_len = cd->pctx.num; dir_modified++; } } if (dir_modified) { cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf); if (cd->pctx.errcode) { if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK, &cd->pctx)) goto abort_free_dict; } ext2fs_mark_changed(fs); } dict_free_nodes(&de_dict); return 0; abort_free_dict: dict_free_nodes(&de_dict); ctx->flags |= E2F_FLAG_ABORT; return DIRENT_ABORT; } /* * This function is called to deallocate a block, and is an interator * functioned called by deallocate inode via ext2fs_iterate_block(). */ static int deallocate_inode_block(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt FSCK_ATTR((unused)), blk_t ref_block FSCK_ATTR((unused)), int ref_offset FSCK_ATTR((unused)), void *priv_data) { e2fsck_t ctx = (e2fsck_t) priv_data; if (HOLE_BLKADDR(*block_nr)) return 0; if ((*block_nr < fs->super->s_first_data_block) || (*block_nr >= fs->super->s_blocks_count)) return 0; ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr); ext2fs_block_alloc_stats(fs, *block_nr, -1); return 0; } /* * This fuction deallocates an inode */ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) { ext2_filsys fs = ctx->fs; struct ext2_inode inode; struct problem_context pctx; __u32 count; ext2fs_icount_store(ctx->inode_link_info, ino, 0); e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode"); inode.i_links_count = 0; inode.i_dtime = time(NULL); e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode"); clear_problem_context(&pctx); pctx.ino = ino; /* * Fix up the bitmaps... */ e2fsck_read_bitmaps(ctx); ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino); ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino); if (ctx->inode_bad_map) ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino); ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode)); if (inode.i_file_acl && (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl, block_buf, -1, &count); if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { pctx.errcode = 0; count = 1; } if (pctx.errcode) { pctx.blk = inode.i_file_acl; fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } if (count == 0) { ext2fs_unmark_block_bitmap(ctx->block_found_map, inode.i_file_acl); ext2fs_block_alloc_stats(fs, inode.i_file_acl, -1); } inode.i_file_acl = 0; } if (!ext2fs_inode_has_valid_blocks(&inode)) return; if (LINUX_S_ISREG(inode.i_mode) && (inode.i_size_high || inode.i_size & 0x80000000UL)) ctx->large_files--; pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, deallocate_inode_block, ctx); if (pctx.errcode) { fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } } /* * This fuction clears the htree flag on an inode */ static void clear_htree(e2fsck_t ctx, ext2_ino_t ino) { struct ext2_inode inode; e2fsck_read_inode(ctx, ino, &inode, "clear_htree"); inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL; e2fsck_write_inode(ctx, ino, &inode, "clear_htree"); if (ctx->dirs_to_hash) ext2fs_u32_list_add(ctx->dirs_to_hash, ino); } static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir, ext2_ino_t ino, char *buf) { ext2_filsys fs = ctx->fs; struct ext2_inode inode; int inode_modified = 0; int not_fixed = 0; unsigned char *frag, *fsize; struct problem_context pctx; int problem = 0; e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode"); clear_problem_context(&pctx); pctx.ino = ino; pctx.dir = dir; pctx.inode = &inode; if (inode.i_file_acl && !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) && fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) { inode.i_file_acl = 0; #if BB_BIG_ENDIAN /* * This is a special kludge to deal with long symlinks * on big endian systems. i_blocks had already been * decremented earlier in pass 1, but since i_file_acl * hadn't yet been cleared, ext2fs_read_inode() * assumed that the file was short symlink and would * not have byte swapped i_block[0]. Hence, we have * to byte-swap it here. */ if (LINUX_S_ISLNK(inode.i_mode) && (fs->flags & EXT2_FLAG_SWAP_BYTES) && (inode.i_blocks == fs->blocksize >> 9)) inode.i_block[0] = ext2fs_swab32(inode.i_block[0]); #endif inode_modified++; } else not_fixed++; if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) && !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) && !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) && !(LINUX_S_ISSOCK(inode.i_mode))) problem = PR_2_BAD_MODE; else if (LINUX_S_ISCHR(inode.i_mode) && !e2fsck_pass1_check_device_inode(fs, &inode)) problem = PR_2_BAD_CHAR_DEV; else if (LINUX_S_ISBLK(inode.i_mode) && !e2fsck_pass1_check_device_inode(fs, &inode)) problem = PR_2_BAD_BLOCK_DEV; else if (LINUX_S_ISFIFO(inode.i_mode) && !e2fsck_pass1_check_device_inode(fs, &inode)) problem = PR_2_BAD_FIFO; else if (LINUX_S_ISSOCK(inode.i_mode) && !e2fsck_pass1_check_device_inode(fs, &inode)) problem = PR_2_BAD_SOCKET; else if (LINUX_S_ISLNK(inode.i_mode) && !e2fsck_pass1_check_symlink(fs, &inode, buf)) { problem = PR_2_INVALID_SYMLINK; } if (problem) { if (fix_problem(ctx, problem, &pctx)) { deallocate_inode(ctx, ino, 0); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return 0; return 1; } else not_fixed++; problem = 0; } if (inode.i_faddr) { if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) { inode.i_faddr = 0; inode_modified++; } else not_fixed++; } switch (fs->super->s_creator_os) { case EXT2_OS_LINUX: frag = &inode.osd2.linux2.l_i_frag; fsize = &inode.osd2.linux2.l_i_fsize; break; case EXT2_OS_HURD: frag = &inode.osd2.hurd2.h_i_frag; fsize = &inode.osd2.hurd2.h_i_fsize; break; case EXT2_OS_MASIX: frag = &inode.osd2.masix2.m_i_frag; fsize = &inode.osd2.masix2.m_i_fsize; break; default: frag = fsize = 0; } if (frag && *frag) { pctx.num = *frag; if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) { *frag = 0; inode_modified++; } else not_fixed++; pctx.num = 0; } if (fsize && *fsize) { pctx.num = *fsize; if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) { *fsize = 0; inode_modified++; } else not_fixed++; pctx.num = 0; } if (inode.i_file_acl && ((inode.i_file_acl < fs->super->s_first_data_block) || (inode.i_file_acl >= fs->super->s_blocks_count))) { if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) { inode.i_file_acl = 0; inode_modified++; } else not_fixed++; } if (inode.i_dir_acl && LINUX_S_ISDIR(inode.i_mode)) { if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) { inode.i_dir_acl = 0; inode_modified++; } else not_fixed++; } if (inode_modified) e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode"); if (!not_fixed) ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino); return 0; } /* * allocate_dir_block --- this function allocates a new directory * block for a particular inode; this is done if a directory has * a "hole" in it, or if a directory has a illegal block number * that was zeroed out and now needs to be replaced. */ static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *db, struct problem_context *pctx) { ext2_filsys fs = ctx->fs; blk_t blk; char *block; struct ext2_inode inode; if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0) return 1; /* * Read the inode and block bitmaps in; we'll be messing with * them. */ e2fsck_read_bitmaps(ctx); /* * First, find a free block */ pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk); if (pctx->errcode) { pctx->str = "ext2fs_new_block"; fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); return 1; } ext2fs_mark_block_bitmap(ctx->block_found_map, blk); ext2fs_mark_block_bitmap(fs->block_map, blk); ext2fs_mark_bb_dirty(fs); /* * Now let's create the actual data block for the inode */ if (db->blockcnt) pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block); else pctx->errcode = ext2fs_new_dir_block(fs, db->ino, EXT2_ROOT_INO, &block); if (pctx->errcode) { pctx->str = "ext2fs_new_dir_block"; fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); return 1; } pctx->errcode = ext2fs_write_dir_block(fs, blk, block); ext2fs_free_mem(&block); if (pctx->errcode) { pctx->str = "ext2fs_write_dir_block"; fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); return 1; } /* * Update the inode block count */ e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block"); inode.i_blocks += fs->blocksize / 512; if (inode.i_size < (db->blockcnt+1) * fs->blocksize) inode.i_size = (db->blockcnt+1) * fs->blocksize; e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block"); /* * Finally, update the block pointers for the inode */ db->blk = blk; pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE, 0, update_dir_block, db); if (pctx->errcode) { pctx->str = "ext2fs_block_iterate"; fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); return 1; } return 0; } /* * This is a helper function for allocate_dir_block(). */ static int update_dir_block(ext2_filsys fs FSCK_ATTR((unused)), blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block FSCK_ATTR((unused)), int ref_offset FSCK_ATTR((unused)), void *priv_data) { struct ext2_db_entry *db; db = (struct ext2_db_entry *) priv_data; if (db->blockcnt == (int) blockcnt) { *block_nr = db->blk; return BLOCK_CHANGED; } return 0; } /* * pass3.c -- pass #3 of e2fsck: Check for directory connectivity * * Pass #3 assures that all directories are connected to the * filesystem tree, using the following algorithm: * * First, the root directory is checked to make sure it exists; if * not, e2fsck will offer to create a new one. It is then marked as * "done". * * Then, pass3 interates over all directory inodes; for each directory * it attempts to trace up the filesystem tree, using dirinfo.parent * until it reaches a directory which has been marked "done". If it * cannot do so, then the directory must be disconnected, and e2fsck * will offer to reconnect it to /lost+found. While it is chasing * parent pointers up the filesystem tree, if pass3 sees a directory * twice, then it has detected a filesystem loop, and it will again * offer to reconnect the directory to /lost+found in to break the * filesystem loop. * * Pass 3 also contains the subroutine, e2fsck_reconnect_file() to * reconnect inodes to /lost+found; this subroutine is also used by * pass 4. e2fsck_reconnect_file() calls get_lost_and_found(), which * is responsible for creating /lost+found if it does not exist. * * Pass 3 frees the following data structures: * - The dirinfo directory information cache. */ static void check_root(e2fsck_t ctx); static int check_directory(e2fsck_t ctx, struct dir_info *dir, struct problem_context *pctx); static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent); static ext2fs_inode_bitmap inode_loop_detect; static ext2fs_inode_bitmap inode_done_map; static void e2fsck_pass3(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; int i; struct problem_context pctx; struct dir_info *dir; unsigned long maxdirs, count; clear_problem_context(&pctx); /* Pass 3 */ if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_3_PASS_HEADER, &pctx); /* * Allocate some bitmaps to do loop detection. */ pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"), &inode_done_map); if (pctx.errcode) { pctx.num = 2; fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; goto abort_exit; } check_root(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) goto abort_exit; ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO); maxdirs = e2fsck_get_num_dirinfo(ctx); count = 1; if (ctx->progress) if ((ctx->progress)(ctx, 3, 0, maxdirs)) goto abort_exit; for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) { if (ctx->flags & E2F_FLAG_SIGNAL_MASK) goto abort_exit; if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs)) goto abort_exit; if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino)) if (check_directory(ctx, dir, &pctx)) goto abort_exit; } /* * Force the creation of /lost+found if not present */ if ((ctx->flags & E2F_OPT_READONLY) == 0) e2fsck_get_lost_and_found(ctx, 1); /* * If there are any directories that need to be indexed or * optimized, do it here. */ e2fsck_rehash_directories(ctx); abort_exit: e2fsck_free_dir_info(ctx); ext2fs_free_inode_bitmap(inode_loop_detect); inode_loop_detect = 0; ext2fs_free_inode_bitmap(inode_done_map); inode_done_map = 0; } /* * This makes sure the root inode is present; if not, we ask if the * user wants us to create it. Not creating it is a fatal error. */ static void check_root(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; blk_t blk; struct ext2_inode inode; char * block; struct problem_context pctx; clear_problem_context(&pctx); if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) { /* * If the root inode is not a directory, die here. The * user must have answered 'no' in pass1 when we * offered to clear it. */ if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO))) { fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx); ctx->flags |= E2F_FLAG_ABORT; } return; } if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) { fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } e2fsck_read_bitmaps(ctx); /* * First, find a free block */ pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk); if (pctx.errcode) { pctx.str = "ext2fs_new_block"; fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } ext2fs_mark_block_bitmap(ctx->block_found_map, blk); ext2fs_mark_block_bitmap(fs->block_map, blk); ext2fs_mark_bb_dirty(fs); /* * Now let's create the actual data block for the inode */ pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, &block); if (pctx.errcode) { pctx.str = "ext2fs_new_dir_block"; fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } pctx.errcode = ext2fs_write_dir_block(fs, blk, block); if (pctx.errcode) { pctx.str = "ext2fs_write_dir_block"; fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } ext2fs_free_mem(&block); /* * Set up the inode structure */ memset(&inode, 0, sizeof(inode)); inode.i_mode = 040755; inode.i_size = fs->blocksize; inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL); inode.i_links_count = 2; inode.i_blocks = fs->blocksize / 512; inode.i_block[0] = blk; /* * Write out the inode. */ pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode); if (pctx.errcode) { pctx.str = "ext2fs_write_inode"; fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } /* * Miscellaneous bookkeeping... */ e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO); ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2); ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2); ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO); ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO); ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO); ext2fs_mark_ib_dirty(fs); } /* * This subroutine is responsible for making sure that a particular * directory is connected to the root; if it isn't we trace it up as * far as we can go, and then offer to connect the resulting parent to * the lost+found. We have to do loop detection; if we ever discover * a loop, we treat that as a disconnected directory and offer to * reparent it to lost+found. * * However, loop detection is expensive, because for very large * filesystems, the inode_loop_detect bitmap is huge, and clearing it * is non-trivial. Loops in filesystems are also a rare error case, * and we shouldn't optimize for error cases. So we try two passes of * the algorithm. The first time, we ignore loop detection and merely * increment a counter; if the counter exceeds some extreme threshold, * then we try again with the loop detection bitmap enabled. */ static int check_directory(e2fsck_t ctx, struct dir_info *dir, struct problem_context *pctx) { ext2_filsys fs = ctx->fs; struct dir_info *p = dir; int loop_pass = 0, parent_count = 0; if (!p) return 0; while (1) { /* * Mark this inode as being "done"; by the time we * return from this function, the inode we either be * verified as being connected to the directory tree, * or we will have offered to reconnect this to * lost+found. * * If it was marked done already, then we've reached a * parent we've already checked. */ if (ext2fs_mark_inode_bitmap(inode_done_map, p->ino)) break; /* * If this directory doesn't have a parent, or we've * seen the parent once already, then offer to * reparent it to lost+found */ if (!p->parent || (loop_pass && (ext2fs_test_inode_bitmap(inode_loop_detect, p->parent)))) { pctx->ino = p->ino; if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) { if (e2fsck_reconnect_file(ctx, pctx->ino)) ext2fs_unmark_valid(fs); else { p = e2fsck_get_dir_info(ctx, pctx->ino); p->parent = ctx->lost_and_found; fix_dotdot(ctx, p, ctx->lost_and_found); } } break; } p = e2fsck_get_dir_info(ctx, p->parent); if (!p) { fix_problem(ctx, PR_3_NO_DIRINFO, pctx); return 0; } if (loop_pass) { ext2fs_mark_inode_bitmap(inode_loop_detect, p->ino); } else if (parent_count++ > 2048) { /* * If we've run into a path depth that's * greater than 2048, try again with the inode * loop bitmap turned on and start from the * top. */ loop_pass = 1; if (inode_loop_detect) ext2fs_clear_inode_bitmap(inode_loop_detect); else { pctx->errcode = ext2fs_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), &inode_loop_detect); if (pctx->errcode) { pctx->num = 1; fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, pctx); ctx->flags |= E2F_FLAG_ABORT; return -1; } } p = dir; } } /* * Make sure that .. and the parent directory are the same; * offer to fix it if not. */ if (dir->parent != dir->dotdot) { pctx->ino = dir->ino; pctx->ino2 = dir->dotdot; pctx->dir = dir->parent; if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx)) fix_dotdot(ctx, dir, dir->parent); } return 0; } /* * This routine gets the lost_and_found inode, making it a directory * if necessary */ ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix) { ext2_filsys fs = ctx->fs; ext2_ino_t ino; blk_t blk; errcode_t retval; struct ext2_inode inode; char * block; static const char name[] = "lost+found"; struct problem_context pctx; struct dir_info *dirinfo; if (ctx->lost_and_found) return ctx->lost_and_found; clear_problem_context(&pctx); retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, sizeof(name)-1, 0, &ino); if (retval && !fix) return 0; if (!retval) { if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) { ctx->lost_and_found = ino; return ino; } /* Lost+found isn't a directory! */ if (!fix) return 0; pctx.ino = ino; if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx)) return 0; /* OK, unlink the old /lost+found file. */ pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0); if (pctx.errcode) { pctx.str = "ext2fs_unlink"; fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); return 0; } dirinfo = e2fsck_get_dir_info(ctx, ino); if (dirinfo) dirinfo->parent = 0; e2fsck_adjust_inode_count(ctx, ino, -1); } else if (retval != EXT2_ET_FILE_NOT_FOUND) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx); } if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0)) return 0; /* * Read the inode and block bitmaps in; we'll be messing with * them. */ e2fsck_read_bitmaps(ctx); /* * First, find a free block */ retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx); return 0; } ext2fs_mark_block_bitmap(ctx->block_found_map, blk); ext2fs_block_alloc_stats(fs, blk, +1); /* * Next find a free inode. */ retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700, ctx->inode_used_map, &ino); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx); return 0; } ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino); ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino); ext2fs_inode_alloc_stats2(fs, ino, +1, 1); /* * Now let's create the actual data block for the inode */ retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx); return 0; } retval = ext2fs_write_dir_block(fs, blk, block); ext2fs_free_mem(&block); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx); return 0; } /* * Set up the inode structure */ memset(&inode, 0, sizeof(inode)); inode.i_mode = 040700; inode.i_size = fs->blocksize; inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL); inode.i_links_count = 2; inode.i_blocks = fs->blocksize / 512; inode.i_block[0] = blk; /* * Next, write out the inode. */ pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode); if (pctx.errcode) { pctx.str = "ext2fs_write_inode"; fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); return 0; } /* * Finally, create the directory link */ pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR); if (pctx.errcode) { pctx.str = "ext2fs_link"; fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); return 0; } /* * Miscellaneous bookkeeping that needs to be kept straight. */ e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO); e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1); ext2fs_icount_store(ctx->inode_count, ino, 2); ext2fs_icount_store(ctx->inode_link_info, ino, 2); ctx->lost_and_found = ino; return ino; } /* * This routine will connect a file to lost+found */ int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino) { ext2_filsys fs = ctx->fs; errcode_t retval; char name[80]; struct problem_context pctx; struct ext2_inode inode; int file_type = 0; clear_problem_context(&pctx); pctx.ino = ino; if (!ctx->bad_lost_and_found && !ctx->lost_and_found) { if (e2fsck_get_lost_and_found(ctx, 1) == 0) ctx->bad_lost_and_found++; } if (ctx->bad_lost_and_found) { fix_problem(ctx, PR_3_NO_LPF, &pctx); return 1; } sprintf(name, "#%u", ino); if (ext2fs_read_inode(fs, ino, &inode) == 0) file_type = ext2_file_type(inode.i_mode); retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type); if (retval == EXT2_ET_DIR_NO_SPACE) { if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx)) return 1; retval = e2fsck_expand_directory(ctx, ctx->lost_and_found, 1, 0); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx); return 1; } retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type); } if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx); return 1; } e2fsck_adjust_inode_count(ctx, ino, 1); return 0; } /* * Utility routine to adjust the inode counts on an inode. */ errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj) { ext2_filsys fs = ctx->fs; errcode_t retval; struct ext2_inode inode; if (!ino) return 0; retval = ext2fs_read_inode(fs, ino, &inode); if (retval) return retval; if (adj == 1) { ext2fs_icount_increment(ctx->inode_count, ino, 0); if (inode.i_links_count == (__u16) ~0) return 0; ext2fs_icount_increment(ctx->inode_link_info, ino, 0); inode.i_links_count++; } else if (adj == -1) { ext2fs_icount_decrement(ctx->inode_count, ino, 0); if (inode.i_links_count == 0) return 0; ext2fs_icount_decrement(ctx->inode_link_info, ino, 0); inode.i_links_count--; } retval = ext2fs_write_inode(fs, ino, &inode); if (retval) return retval; return 0; } /* * Fix parent --- this routine fixes up the parent of a directory. */ struct fix_dotdot_struct { ext2_filsys fs; ext2_ino_t parent; int done; e2fsck_t ctx; }; static int fix_dotdot_proc(struct ext2_dir_entry *dirent, int offset FSCK_ATTR((unused)), int blocksize FSCK_ATTR((unused)), char *buf FSCK_ATTR((unused)), void *priv_data) { struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data; errcode_t retval; struct problem_context pctx; if ((dirent->name_len & 0xFF) != 2) return 0; if (strncmp(dirent->name, "..", 2)) return 0; clear_problem_context(&pctx); retval = e2fsck_adjust_inode_count(fp->ctx, dirent->inode, -1); if (retval) { pctx.errcode = retval; fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx); } retval = e2fsck_adjust_inode_count(fp->ctx, fp->parent, 1); if (retval) { pctx.errcode = retval; fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx); } dirent->inode = fp->parent; fp->done++; return DIRENT_ABORT | DIRENT_CHANGED; } static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent) { ext2_filsys fs = ctx->fs; errcode_t retval; struct fix_dotdot_struct fp; struct problem_context pctx; fp.fs = fs; fp.parent = parent; fp.done = 0; fp.ctx = ctx; retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY, 0, fix_dotdot_proc, &fp); if (retval || !fp.done) { clear_problem_context(&pctx); pctx.ino = dir->ino; pctx.errcode = retval; fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR : PR_3_FIX_PARENT_NOFIND, &pctx); ext2fs_unmark_valid(fs); } dir->dotdot = parent; } /* * These routines are responsible for expanding a /lost+found if it is * too small. */ struct expand_dir_struct { int num; int guaranteed_size; int newblocks; int last_block; errcode_t err; e2fsck_t ctx; }; static int expand_dir_proc(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_block FSCK_ATTR((unused)), int ref_offset FSCK_ATTR((unused)), void *priv_data) { struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data; blk_t new_blk; static blk_t last_blk = 0; char *block; errcode_t retval; e2fsck_t ctx; ctx = es->ctx; if (es->guaranteed_size && blockcnt >= es->guaranteed_size) return BLOCK_ABORT; if (blockcnt > 0) es->last_block = blockcnt; if (*blocknr) { last_blk = *blocknr; return 0; } retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map, &new_blk); if (retval) { es->err = retval; return BLOCK_ABORT; } if (blockcnt > 0) { retval = ext2fs_new_dir_block(fs, 0, 0, &block); if (retval) { es->err = retval; return BLOCK_ABORT; } es->num--; retval = ext2fs_write_dir_block(fs, new_blk, block); } else { retval = ext2fs_get_mem(fs->blocksize, &block); if (retval) { es->err = retval; return BLOCK_ABORT; } memset(block, 0, fs->blocksize); retval = io_channel_write_blk(fs->io, new_blk, 1, block); } if (retval) { es->err = retval; return BLOCK_ABORT; } ext2fs_free_mem(&block); *blocknr = new_blk; ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk); ext2fs_block_alloc_stats(fs, new_blk, +1); es->newblocks++; if (es->num == 0) return (BLOCK_CHANGED | BLOCK_ABORT); else return BLOCK_CHANGED; } errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir, int num, int guaranteed_size) { ext2_filsys fs = ctx->fs; errcode_t retval; struct expand_dir_struct es; struct ext2_inode inode; if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; /* * Read the inode and block bitmaps in; we'll be messing with * them. */ e2fsck_read_bitmaps(ctx); retval = ext2fs_check_directory(fs, dir); if (retval) return retval; es.num = num; es.guaranteed_size = guaranteed_size; es.last_block = 0; es.err = 0; es.newblocks = 0; es.ctx = ctx; retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND, 0, expand_dir_proc, &es); if (es.err) return es.err; /* * Update the size and block count fields in the inode. */ retval = ext2fs_read_inode(fs, dir, &inode); if (retval) return retval; inode.i_size = (es.last_block + 1) * fs->blocksize; inode.i_blocks += (fs->blocksize / 512) * es.newblocks; e2fsck_write_inode(ctx, dir, &inode, "expand_directory"); return 0; } /* * pass4.c -- pass #4 of e2fsck: Check reference counts * * Pass 4 frees the following data structures: * - A bitmap of which inodes are imagic inodes. (inode_imagic_map) */ /* * This routine is called when an inode is not connected to the * directory tree. * * This subroutine returns 1 then the caller shouldn't bother with the * rest of the pass 4 tests. */ static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i) { ext2_filsys fs = ctx->fs; struct ext2_inode inode; struct problem_context pctx; e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode"); clear_problem_context(&pctx); pctx.ino = i; pctx.inode = &inode; /* * Offer to delete any zero-length files that does not have * blocks. If there is an EA block, it might have useful * information, so we won't prompt to delete it, but let it be * reconnected to lost+found. */ if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) || LINUX_S_ISDIR(inode.i_mode))) { if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) { ext2fs_icount_store(ctx->inode_link_info, i, 0); inode.i_links_count = 0; inode.i_dtime = time(NULL); e2fsck_write_inode(ctx, i, &inode, "disconnect_inode"); /* * Fix up the bitmaps... */ e2fsck_read_bitmaps(ctx); ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i); ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i); ext2fs_inode_alloc_stats2(fs, i, -1, LINUX_S_ISDIR(inode.i_mode)); return 0; } } /* * Prompt to reconnect. */ if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) { if (e2fsck_reconnect_file(ctx, i)) ext2fs_unmark_valid(fs); } else { /* * If we don't attach the inode, then skip the * i_links_test since there's no point in trying to * force i_links_count to zero. */ ext2fs_unmark_valid(fs); return 1; } return 0; } static void e2fsck_pass4(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; ext2_ino_t i; struct ext2_inode inode; struct problem_context pctx; __u16 link_count, link_counted; char *buf = NULL; int group, maxgroup; /* Pass 4 */ clear_problem_context(&pctx); if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_4_PASS_HEADER, &pctx); group = 0; maxgroup = fs->group_desc_count; if (ctx->progress) if ((ctx->progress)(ctx, 4, 0, maxgroup)) return; for (i=1; i <= fs->super->s_inodes_count; i++) { if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; if ((i % fs->super->s_inodes_per_group) == 0) { group++; if (ctx->progress) if ((ctx->progress)(ctx, 4, group, maxgroup)) return; } if (i == EXT2_BAD_INO || (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super))) continue; if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) || (ctx->inode_imagic_map && ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i))) continue; ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count); ext2fs_icount_fetch(ctx->inode_count, i, &link_counted); if (link_counted == 0) { if (!buf) buf = e2fsck_allocate_memory(ctx, fs->blocksize, "bad_inode buffer"); if (e2fsck_process_bad_inode(ctx, 0, i, buf)) continue; if (disconnect_inode(ctx, i)) continue; ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count); ext2fs_icount_fetch(ctx->inode_count, i, &link_counted); } if (link_counted != link_count) { e2fsck_read_inode(ctx, i, &inode, "pass4"); pctx.ino = i; pctx.inode = &inode; if (link_count != inode.i_links_count) { pctx.num = link_count; fix_problem(ctx, PR_4_INCONSISTENT_COUNT, &pctx); } pctx.num = link_counted; if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) { inode.i_links_count = link_counted; e2fsck_write_inode(ctx, i, &inode, "pass4"); } } } ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0; ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0; ext2fs_free_inode_bitmap(ctx->inode_imagic_map); ctx->inode_imagic_map = 0; ext2fs_free_mem(&buf); } /* * pass5.c --- check block and inode bitmaps against on-disk bitmaps */ #define NO_BLK ((blk_t) -1) static void print_bitmap_problem(e2fsck_t ctx, int problem, struct problem_context *pctx) { switch (problem) { case PR_5_BLOCK_UNUSED: if (pctx->blk == pctx->blk2) pctx->blk2 = 0; else problem = PR_5_BLOCK_RANGE_UNUSED; break; case PR_5_BLOCK_USED: if (pctx->blk == pctx->blk2) pctx->blk2 = 0; else problem = PR_5_BLOCK_RANGE_USED; break; case PR_5_INODE_UNUSED: if (pctx->ino == pctx->ino2) pctx->ino2 = 0; else problem = PR_5_INODE_RANGE_UNUSED; break; case PR_5_INODE_USED: if (pctx->ino == pctx->ino2) pctx->ino2 = 0; else problem = PR_5_INODE_RANGE_USED; break; } fix_problem(ctx, problem, pctx); pctx->blk = pctx->blk2 = NO_BLK; pctx->ino = pctx->ino2 = 0; } static void check_block_bitmaps(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; blk_t i; int *free_array; int group = 0; unsigned int blocks = 0; unsigned int free_blocks = 0; int group_free = 0; int actual, bitmap; struct problem_context pctx; int problem, save_problem, fixit, had_problem; errcode_t retval; clear_problem_context(&pctx); free_array = (int *) e2fsck_allocate_memory(ctx, fs->group_desc_count * sizeof(int), "free block count array"); if ((fs->super->s_first_data_block < ext2fs_get_block_bitmap_start(ctx->block_found_map)) || (fs->super->s_blocks_count-1 > ext2fs_get_block_bitmap_end(ctx->block_found_map))) { pctx.num = 1; pctx.blk = fs->super->s_first_data_block; pctx.blk2 = fs->super->s_blocks_count -1; pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map); pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map); fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* fatal */ return; } if ((fs->super->s_first_data_block < ext2fs_get_block_bitmap_start(fs->block_map)) || (fs->super->s_blocks_count-1 > ext2fs_get_block_bitmap_end(fs->block_map))) { pctx.num = 2; pctx.blk = fs->super->s_first_data_block; pctx.blk2 = fs->super->s_blocks_count -1; pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map); pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map); fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* fatal */ return; } redo_counts: had_problem = 0; save_problem = 0; pctx.blk = pctx.blk2 = NO_BLK; for (i = fs->super->s_first_data_block; i < fs->super->s_blocks_count; i++) { actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i); bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i); if (actual == bitmap) goto do_counts; if (!actual && bitmap) { /* * Block not used, but marked in use in the bitmap. */ problem = PR_5_BLOCK_UNUSED; } else { /* * Block used, but not marked in use in the bitmap. */ problem = PR_5_BLOCK_USED; } if (pctx.blk == NO_BLK) { pctx.blk = pctx.blk2 = i; save_problem = problem; } else { if ((problem == save_problem) && (pctx.blk2 == i-1)) pctx.blk2++; else { print_bitmap_problem(ctx, save_problem, &pctx); pctx.blk = pctx.blk2 = i; save_problem = problem; } } ctx->flags |= E2F_FLAG_PROG_SUPPRESS; had_problem++; do_counts: if (!bitmap) { group_free++; free_blocks++; } blocks ++; if ((blocks == fs->super->s_blocks_per_group) || (i == fs->super->s_blocks_count-1)) { free_array[group] = group_free; group ++; blocks = 0; group_free = 0; if (ctx->progress) if ((ctx->progress)(ctx, 5, group, fs->group_desc_count*2)) return; } } if (pctx.blk != NO_BLK) print_bitmap_problem(ctx, save_problem, &pctx); if (had_problem) fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP); else fixit = -1; ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS; if (fixit == 1) { ext2fs_free_block_bitmap(fs->block_map); retval = ext2fs_copy_bitmap(ctx->block_found_map, &fs->block_map); if (retval) { clear_problem_context(&pctx); fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } ext2fs_set_bitmap_padding(fs->block_map); ext2fs_mark_bb_dirty(fs); /* Redo the counts */ blocks = 0; free_blocks = 0; group_free = 0; group = 0; memset(free_array, 0, fs->group_desc_count * sizeof(int)); goto redo_counts; } else if (fixit == 0) ext2fs_unmark_valid(fs); for (i = 0; i < fs->group_desc_count; i++) { if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) { pctx.group = i; pctx.blk = fs->group_desc[i].bg_free_blocks_count; pctx.blk2 = free_array[i]; if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP, &pctx)) { fs->group_desc[i].bg_free_blocks_count = free_array[i]; ext2fs_mark_super_dirty(fs); } else ext2fs_unmark_valid(fs); } } if (free_blocks != fs->super->s_free_blocks_count) { pctx.group = 0; pctx.blk = fs->super->s_free_blocks_count; pctx.blk2 = free_blocks; if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) { fs->super->s_free_blocks_count = free_blocks; ext2fs_mark_super_dirty(fs); } else ext2fs_unmark_valid(fs); } ext2fs_free_mem(&free_array); } static void check_inode_bitmaps(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; ext2_ino_t i; unsigned int free_inodes = 0; int group_free = 0; int dirs_count = 0; int group = 0; unsigned int inodes = 0; int *free_array; int *dir_array; int actual, bitmap; errcode_t retval; struct problem_context pctx; int problem, save_problem, fixit, had_problem; clear_problem_context(&pctx); free_array = (int *) e2fsck_allocate_memory(ctx, fs->group_desc_count * sizeof(int), "free inode count array"); dir_array = (int *) e2fsck_allocate_memory(ctx, fs->group_desc_count * sizeof(int), "directory count array"); if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) || (fs->super->s_inodes_count > ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) { pctx.num = 3; pctx.blk = 1; pctx.blk2 = fs->super->s_inodes_count; pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map); pctx.ino2 = ext2fs_get_inode_bitmap_end(ctx->inode_used_map); fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* fatal */ return; } if ((1 < ext2fs_get_inode_bitmap_start(fs->inode_map)) || (fs->super->s_inodes_count > ext2fs_get_inode_bitmap_end(fs->inode_map))) { pctx.num = 4; pctx.blk = 1; pctx.blk2 = fs->super->s_inodes_count; pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map); pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map); fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* fatal */ return; } redo_counts: had_problem = 0; save_problem = 0; pctx.ino = pctx.ino2 = 0; for (i = 1; i <= fs->super->s_inodes_count; i++) { actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i); bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i); if (actual == bitmap) goto do_counts; if (!actual && bitmap) { /* * Inode wasn't used, but marked in bitmap */ problem = PR_5_INODE_UNUSED; } else /* if (actual && !bitmap) */ { /* * Inode used, but not in bitmap */ problem = PR_5_INODE_USED; } if (pctx.ino == 0) { pctx.ino = pctx.ino2 = i; save_problem = problem; } else { if ((problem == save_problem) && (pctx.ino2 == i-1)) pctx.ino2++; else { print_bitmap_problem(ctx, save_problem, &pctx); pctx.ino = pctx.ino2 = i; save_problem = problem; } } ctx->flags |= E2F_FLAG_PROG_SUPPRESS; had_problem++; do_counts: if (!bitmap) { group_free++; free_inodes++; } else { if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i)) dirs_count++; } inodes++; if ((inodes == fs->super->s_inodes_per_group) || (i == fs->super->s_inodes_count)) { free_array[group] = group_free; dir_array[group] = dirs_count; group ++; inodes = 0; group_free = 0; dirs_count = 0; if (ctx->progress) if ((ctx->progress)(ctx, 5, group + fs->group_desc_count, fs->group_desc_count*2)) return; } } if (pctx.ino) print_bitmap_problem(ctx, save_problem, &pctx); if (had_problem) fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP); else fixit = -1; ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS; if (fixit == 1) { ext2fs_free_inode_bitmap(fs->inode_map); retval = ext2fs_copy_bitmap(ctx->inode_used_map, &fs->inode_map); if (retval) { clear_problem_context(&pctx); fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } ext2fs_set_bitmap_padding(fs->inode_map); ext2fs_mark_ib_dirty(fs); /* redo counts */ inodes = 0; free_inodes = 0; group_free = 0; dirs_count = 0; group = 0; memset(free_array, 0, fs->group_desc_count * sizeof(int)); memset(dir_array, 0, fs->group_desc_count * sizeof(int)); goto redo_counts; } else if (fixit == 0) ext2fs_unmark_valid(fs); for (i = 0; i < fs->group_desc_count; i++) { if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) { pctx.group = i; pctx.ino = fs->group_desc[i].bg_free_inodes_count; pctx.ino2 = free_array[i]; if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP, &pctx)) { fs->group_desc[i].bg_free_inodes_count = free_array[i]; ext2fs_mark_super_dirty(fs); } else ext2fs_unmark_valid(fs); } if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) { pctx.group = i; pctx.ino = fs->group_desc[i].bg_used_dirs_count; pctx.ino2 = dir_array[i]; if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP, &pctx)) { fs->group_desc[i].bg_used_dirs_count = dir_array[i]; ext2fs_mark_super_dirty(fs); } else ext2fs_unmark_valid(fs); } } if (free_inodes != fs->super->s_free_inodes_count) { pctx.group = -1; pctx.ino = fs->super->s_free_inodes_count; pctx.ino2 = free_inodes; if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) { fs->super->s_free_inodes_count = free_inodes; ext2fs_mark_super_dirty(fs); } else ext2fs_unmark_valid(fs); } ext2fs_free_mem(&free_array); ext2fs_free_mem(&dir_array); } static void check_inode_end(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; ext2_ino_t end, save_inodes_count, i; struct problem_context pctx; clear_problem_context(&pctx); end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count; pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end, &save_inodes_count); if (pctx.errcode) { pctx.num = 1; fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* fatal */ return; } if (save_inodes_count == end) return; for (i = save_inodes_count + 1; i <= end; i++) { if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) { if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) { for (i = save_inodes_count + 1; i <= end; i++) ext2fs_mark_inode_bitmap(fs->inode_map, i); ext2fs_mark_ib_dirty(fs); } else ext2fs_unmark_valid(fs); break; } } pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, save_inodes_count, 0); if (pctx.errcode) { pctx.num = 2; fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* fatal */ return; } } static void check_block_end(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; blk_t end, save_blocks_count, i; struct problem_context pctx; clear_problem_context(&pctx); end = fs->block_map->start + (EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count) - 1; pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, end, &save_blocks_count); if (pctx.errcode) { pctx.num = 3; fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* fatal */ return; } if (save_blocks_count == end) return; for (i = save_blocks_count + 1; i <= end; i++) { if (!ext2fs_test_block_bitmap(fs->block_map, i)) { if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) { for (i = save_blocks_count + 1; i <= end; i++) ext2fs_mark_block_bitmap(fs->block_map, i); ext2fs_mark_bb_dirty(fs); } else ext2fs_unmark_valid(fs); break; } } pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, save_blocks_count, 0); if (pctx.errcode) { pctx.num = 4; fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* fatal */ return; } } static void e2fsck_pass5(e2fsck_t ctx) { struct problem_context pctx; /* Pass 5 */ clear_problem_context(&pctx); if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_5_PASS_HEADER, &pctx); if (ctx->progress) if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2)) return; e2fsck_read_bitmaps(ctx); check_block_bitmaps(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; check_inode_bitmaps(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; check_inode_end(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; check_block_end(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; ext2fs_free_inode_bitmap(ctx->inode_used_map); ctx->inode_used_map = 0; ext2fs_free_inode_bitmap(ctx->inode_dir_map); ctx->inode_dir_map = 0; ext2fs_free_block_bitmap(ctx->block_found_map); ctx->block_found_map = 0; } /* * problem.c --- report filesystem problems to the user */ #define PR_PREEN_OK 0x000001 /* Don't need to do preenhalt */ #define PR_NO_OK 0x000002 /* If user answers no, don't make fs invalid */ #define PR_NO_DEFAULT 0x000004 /* Default to no */ #define PR_MSG_ONLY 0x000008 /* Print message only */ /* Bit positions 0x000ff0 are reserved for the PR_LATCH flags */ #define PR_FATAL 0x001000 /* Fatal error */ #define PR_AFTER_CODE 0x002000 /* After asking the first question, */ /* ask another */ #define PR_PREEN_NOMSG 0x004000 /* Don't print a message if we're preening */ #define PR_NOCOLLATE 0x008000 /* Don't collate answers for this latch */ #define PR_NO_NOMSG 0x010000 /* Don't print a message if e2fsck -n */ #define PR_PREEN_NO 0x020000 /* Use No as an answer if preening */ #define PR_PREEN_NOHDR 0x040000 /* Don't print the preen header */ #define PROMPT_NONE 0 #define PROMPT_FIX 1 #define PROMPT_CLEAR 2 #define PROMPT_RELOCATE 3 #define PROMPT_ALLOCATE 4 #define PROMPT_EXPAND 5 #define PROMPT_CONNECT 6 #define PROMPT_CREATE 7 #define PROMPT_SALVAGE 8 #define PROMPT_TRUNCATE 9 #define PROMPT_CLEAR_INODE 10 #define PROMPT_ABORT 11 #define PROMPT_SPLIT 12 #define PROMPT_CONTINUE 13 #define PROMPT_CLONE 14 #define PROMPT_DELETE 15 #define PROMPT_SUPPRESS 16 #define PROMPT_UNLINK 17 #define PROMPT_CLEAR_HTREE 18 #define PROMPT_RECREATE 19 #define PROMPT_NULL 20 struct e2fsck_problem { problem_t e2p_code; const char * e2p_description; char prompt; int flags; problem_t second_code; }; struct latch_descr { int latch_code; problem_t question; problem_t end_message; int flags; }; /* * These are the prompts which are used to ask the user if they want * to fix a problem. */ static const char *const prompt[] = { N_("(no prompt)"), /* 0 */ N_("Fix"), /* 1 */ N_("Clear"), /* 2 */ N_("Relocate"), /* 3 */ N_("Allocate"), /* 4 */ N_("Expand"), /* 5 */ N_("Connect to /lost+found"), /* 6 */ N_("Create"), /* 7 */ N_("Salvage"), /* 8 */ N_("Truncate"), /* 9 */ N_("Clear inode"), /* 10 */ N_("Abort"), /* 11 */ N_("Split"), /* 12 */ N_("Continue"), /* 13 */ N_("Clone multiply-claimed blocks"), /* 14 */ N_("Delete file"), /* 15 */ N_("Suppress messages"),/* 16 */ N_("Unlink"), /* 17 */ N_("Clear HTree index"),/* 18 */ N_("Recreate"), /* 19 */ "", /* 20 */ }; /* * These messages are printed when we are preen mode and we will be * automatically fixing the problem. */ static const char *const preen_msg[] = { N_("(NONE)"), /* 0 */ N_("FIXED"), /* 1 */ N_("CLEARED"), /* 2 */ N_("RELOCATED"), /* 3 */ N_("ALLOCATED"), /* 4 */ N_("EXPANDED"), /* 5 */ N_("RECONNECTED"), /* 6 */ N_("CREATED"), /* 7 */ N_("SALVAGED"), /* 8 */ N_("TRUNCATED"), /* 9 */ N_("INODE CLEARED"), /* 10 */ N_("ABORTED"), /* 11 */ N_("SPLIT"), /* 12 */ N_("CONTINUING"), /* 13 */ N_("MULTIPLY-CLAIMED BLOCKS CLONED"), /* 14 */ N_("FILE DELETED"), /* 15 */ N_("SUPPRESSED"), /* 16 */ N_("UNLINKED"), /* 17 */ N_("HTREE INDEX CLEARED"),/* 18 */ N_("WILL RECREATE"), /* 19 */ "", /* 20 */ }; static const struct e2fsck_problem problem_table[] = { /* Pre-Pass 1 errors */ /* Block bitmap not in group */ { PR_0_BB_NOT_GROUP, N_("@b @B for @g %g is not in @g. (@b %b)\n"), PROMPT_RELOCATE, PR_LATCH_RELOC }, /* Inode bitmap not in group */ { PR_0_IB_NOT_GROUP, N_("@i @B for @g %g is not in @g. (@b %b)\n"), PROMPT_RELOCATE, PR_LATCH_RELOC }, /* Inode table not in group */ { PR_0_ITABLE_NOT_GROUP, N_("@i table for @g %g is not in @g. (@b %b)\n" "WARNING: SEVERE DATA LOSS POSSIBLE.\n"), PROMPT_RELOCATE, PR_LATCH_RELOC }, /* Superblock corrupt */ { PR_0_SB_CORRUPT, N_("\nThe @S could not be read or does not describe a correct ext2\n" "@f. If the @v is valid and it really contains an ext2\n" "@f (and not swap or ufs or something else), then the @S\n" "is corrupt, and you might try running e2fsck with an alternate @S:\n" " e2fsck -b %S <@v>\n\n"), PROMPT_NONE, PR_FATAL }, /* Filesystem size is wrong */ { PR_0_FS_SIZE_WRONG, N_("The @f size (according to the @S) is %b @bs\n" "The physical size of the @v is %c @bs\n" "Either the @S or the partition table is likely to be corrupt!\n"), PROMPT_ABORT, 0 }, /* Fragments not supported */ { PR_0_NO_FRAGMENTS, N_("@S @b_size = %b, fragsize = %c.\n" "This version of e2fsck does not support fragment sizes different\n" "from the @b size.\n"), PROMPT_NONE, PR_FATAL }, /* Bad blocks_per_group */ { PR_0_BLOCKS_PER_GROUP, N_("@S @bs_per_group = %b, should have been %c\n"), PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT }, /* Bad first_data_block */ { PR_0_FIRST_DATA_BLOCK, N_("@S first_data_@b = %b, should have been %c\n"), PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT }, /* Adding UUID to filesystem */ { PR_0_ADD_UUID, N_("@f did not have a UUID; generating one.\n\n"), PROMPT_NONE, 0 }, /* Relocate hint */ { PR_0_RELOCATE_HINT, N_("Note: if several inode or block bitmap blocks or part\n" "of the inode table require relocation, you may wish to try\n" "running e2fsck with the '-b %S' option first. The problem\n" "may lie only with the primary block group descriptors, and\n" "the backup block group descriptors may be OK.\n\n"), PROMPT_NONE, PR_PREEN_OK | PR_NOCOLLATE }, /* Miscellaneous superblock corruption */ { PR_0_MISC_CORRUPT_SUPER, N_("Corruption found in @S. (%s = %N).\n"), PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT }, /* Error determing physical device size of filesystem */ { PR_0_GETSIZE_ERROR, N_("Error determining size of the physical @v: %m\n"), PROMPT_NONE, PR_FATAL }, /* Inode count in superblock is incorrect */ { PR_0_INODE_COUNT_WRONG, N_("@i count in @S is %i, @s %j.\n"), PROMPT_FIX, 0 }, { PR_0_HURD_CLEAR_FILETYPE, N_("The Hurd does not support the filetype feature.\n"), PROMPT_CLEAR, 0 }, /* Journal inode is invalid */ { PR_0_JOURNAL_BAD_INODE, N_("@S has an @n ext3 @j (@i %i).\n"), PROMPT_CLEAR, PR_PREEN_OK }, /* The external journal has (unsupported) multiple filesystems */ { PR_0_JOURNAL_UNSUPP_MULTIFS, N_("External @j has multiple @f users (unsupported).\n"), PROMPT_NONE, PR_FATAL }, /* Can't find external journal */ { PR_0_CANT_FIND_JOURNAL, N_("Can't find external @j\n"), PROMPT_NONE, PR_FATAL }, /* External journal has bad superblock */ { PR_0_EXT_JOURNAL_BAD_SUPER, N_("External @j has bad @S\n"), PROMPT_NONE, PR_FATAL }, /* Superblock has a bad journal UUID */ { PR_0_JOURNAL_BAD_UUID, N_("External @j does not support this @f\n"), PROMPT_NONE, PR_FATAL }, /* Journal has an unknown superblock type */ { PR_0_JOURNAL_UNSUPP_SUPER, N_("Ext3 @j @S is unknown type %N (unsupported).\n" "It is likely that your copy of e2fsck is old and/or doesn't " "support this @j format.\n" "It is also possible the @j @S is corrupt.\n"), PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_SUPER }, /* Journal superblock is corrupt */ { PR_0_JOURNAL_BAD_SUPER, N_("Ext3 @j @S is corrupt.\n"), PROMPT_FIX, PR_PREEN_OK }, /* Superblock flag should be cleared */ { PR_0_JOURNAL_HAS_JOURNAL, N_("@S doesn't have has_@j flag, but has ext3 @j %s.\n"), PROMPT_CLEAR, PR_PREEN_OK }, /* Superblock flag is incorrect */ { PR_0_JOURNAL_RECOVER_SET, N_("@S has ext3 needs_recovery flag set, but no @j.\n"), PROMPT_CLEAR, PR_PREEN_OK }, /* Journal has data, but recovery flag is clear */ { PR_0_JOURNAL_RECOVERY_CLEAR, N_("ext3 recovery flag is clear, but @j has data.\n"), PROMPT_NONE, 0 }, /* Ask if we should clear the journal */ { PR_0_JOURNAL_RESET_JOURNAL, N_("Clear @j"), PROMPT_NULL, PR_PREEN_NOMSG }, /* Ask if we should run the journal anyway */ { PR_0_JOURNAL_RUN, N_("Run @j anyway"), PROMPT_NULL, 0 }, /* Run the journal by default */ { PR_0_JOURNAL_RUN_DEFAULT, N_("Recovery flag not set in backup @S, so running @j anyway.\n"), PROMPT_NONE, 0 }, /* Clearing orphan inode */ { PR_0_ORPHAN_CLEAR_INODE, N_("%s @o @i %i (uid=%Iu, gid=%Ig, mode=%Im, size=%Is)\n"), PROMPT_NONE, 0 }, /* Illegal block found in orphaned inode */ { PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, N_("@I @b #%B (%b) found in @o @i %i.\n"), PROMPT_NONE, 0 }, /* Already cleared block found in orphaned inode */ { PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, N_("Already cleared @b #%B (%b) found in @o @i %i.\n"), PROMPT_NONE, 0 }, /* Illegal orphan inode in superblock */ { PR_0_ORPHAN_ILLEGAL_HEAD_INODE, N_("@I @o @i %i in @S.\n"), PROMPT_NONE, 0 }, /* Illegal inode in orphaned inode list */ { PR_0_ORPHAN_ILLEGAL_INODE, N_("@I @i %i in @o @i list.\n"), PROMPT_NONE, 0 }, /* Filesystem revision is 0, but feature flags are set */ { PR_0_FS_REV_LEVEL, N_("@f has feature flag(s) set, but is a revision 0 @f. "), PROMPT_FIX, PR_PREEN_OK | PR_NO_OK }, /* Journal superblock has an unknown read-only feature flag set */ { PR_0_JOURNAL_UNSUPP_ROCOMPAT, N_("Ext3 @j @S has an unknown read-only feature flag set.\n"), PROMPT_ABORT, 0 }, /* Journal superblock has an unknown incompatible feature flag set */ { PR_0_JOURNAL_UNSUPP_INCOMPAT, N_("Ext3 @j @S has an unknown incompatible feature flag set.\n"), PROMPT_ABORT, 0 }, /* Journal has unsupported version number */ { PR_0_JOURNAL_UNSUPP_VERSION, N_("@j version not supported by this e2fsck.\n"), PROMPT_ABORT, 0 }, /* Moving journal to hidden file */ { PR_0_MOVE_JOURNAL, N_("Moving @j from /%s to hidden @i.\n\n"), PROMPT_NONE, 0 }, /* Error moving journal to hidden file */ { PR_0_ERR_MOVE_JOURNAL, N_("Error moving @j: %m\n\n"), PROMPT_NONE, 0 }, /* Clearing V2 journal superblock */ { PR_0_CLEAR_V2_JOURNAL, N_("Found @n V2 @j @S fields (from V1 @j).\n" "Clearing fields beyond the V1 @j @S...\n\n"), PROMPT_NONE, 0 }, /* Backup journal inode blocks */ { PR_0_BACKUP_JNL, N_("Backing up @j @i @b information.\n\n"), PROMPT_NONE, 0 }, /* Reserved blocks w/o resize_inode */ { PR_0_NONZERO_RESERVED_GDT_BLOCKS, N_("@f does not have resize_@i enabled, but s_reserved_gdt_@bs\n" "is %N; @s zero. "), PROMPT_FIX, 0 }, /* Resize_inode not enabled, but resize inode is non-zero */ { PR_0_CLEAR_RESIZE_INODE, N_("Resize_@i not enabled, but the resize @i is non-zero. "), PROMPT_CLEAR, 0 }, /* Resize inode invalid */ { PR_0_RESIZE_INODE_INVALID, N_("Resize @i not valid. "), PROMPT_RECREATE, 0 }, /* Pass 1 errors */ /* Pass 1: Checking inodes, blocks, and sizes */ { PR_1_PASS_HEADER, N_("Pass 1: Checking @is, @bs, and sizes\n"), PROMPT_NONE, 0 }, /* Root directory is not an inode */ { PR_1_ROOT_NO_DIR, N_("@r is not a @d. "), PROMPT_CLEAR, 0 }, /* Root directory has dtime set */ { PR_1_ROOT_DTIME, N_("@r has dtime set (probably due to old mke2fs). "), PROMPT_FIX, PR_PREEN_OK }, /* Reserved inode has bad mode */ { PR_1_RESERVED_BAD_MODE, N_("Reserved @i %i (%Q) has @n mode. "), PROMPT_CLEAR, PR_PREEN_OK }, /* Deleted inode has zero dtime */ { PR_1_ZERO_DTIME, N_("@D @i %i has zero dtime. "), PROMPT_FIX, PR_PREEN_OK }, /* Inode in use, but dtime set */ { PR_1_SET_DTIME, N_("@i %i is in use, but has dtime set. "), PROMPT_FIX, PR_PREEN_OK }, /* Zero-length directory */ { PR_1_ZERO_LENGTH_DIR, N_("@i %i is a @z @d. "), PROMPT_CLEAR, PR_PREEN_OK }, /* Block bitmap conflicts with some other fs block */ { PR_1_BB_CONFLICT, N_("@g %g's @b @B at %b @C.\n"), PROMPT_RELOCATE, 0 }, /* Inode bitmap conflicts with some other fs block */ { PR_1_IB_CONFLICT, N_("@g %g's @i @B at %b @C.\n"), PROMPT_RELOCATE, 0 }, /* Inode table conflicts with some other fs block */ { PR_1_ITABLE_CONFLICT, N_("@g %g's @i table at %b @C.\n"), PROMPT_RELOCATE, 0 }, /* Block bitmap is on a bad block */ { PR_1_BB_BAD_BLOCK, N_("@g %g's @b @B (%b) is bad. "), PROMPT_RELOCATE, 0 }, /* Inode bitmap is on a bad block */ { PR_1_IB_BAD_BLOCK, N_("@g %g's @i @B (%b) is bad. "), PROMPT_RELOCATE, 0 }, /* Inode has incorrect i_size */ { PR_1_BAD_I_SIZE, N_("@i %i, i_size is %Is, @s %N. "), PROMPT_FIX, PR_PREEN_OK }, /* Inode has incorrect i_blocks */ { PR_1_BAD_I_BLOCKS, N_("@i %i, i_@bs is %Ib, @s %N. "), PROMPT_FIX, PR_PREEN_OK }, /* Illegal blocknumber in inode */ { PR_1_ILLEGAL_BLOCK_NUM, N_("@I @b #%B (%b) in @i %i. "), PROMPT_CLEAR, PR_LATCH_BLOCK }, /* Block number overlaps fs metadata */ { PR_1_BLOCK_OVERLAPS_METADATA, N_("@b #%B (%b) overlaps @f metadata in @i %i. "), PROMPT_CLEAR, PR_LATCH_BLOCK }, /* Inode has illegal blocks (latch question) */ { PR_1_INODE_BLOCK_LATCH, N_("@i %i has illegal @b(s). "), PROMPT_CLEAR, 0 }, /* Too many bad blocks in inode */ { PR_1_TOO_MANY_BAD_BLOCKS, N_("Too many illegal @bs in @i %i.\n"), PROMPT_CLEAR_INODE, PR_NO_OK }, /* Illegal block number in bad block inode */ { PR_1_BB_ILLEGAL_BLOCK_NUM, N_("@I @b #%B (%b) in bad @b @i. "), PROMPT_CLEAR, PR_LATCH_BBLOCK }, /* Bad block inode has illegal blocks (latch question) */ { PR_1_INODE_BBLOCK_LATCH, N_("Bad @b @i has illegal @b(s). "), PROMPT_CLEAR, 0 }, /* Duplicate or bad blocks in use! */ { PR_1_DUP_BLOCKS_PREENSTOP, N_("Duplicate or bad @b in use!\n"), PROMPT_NONE, 0 }, /* Bad block used as bad block indirect block */ { PR_1_BBINODE_BAD_METABLOCK, N_("Bad @b %b used as bad @b @i indirect @b. "), PROMPT_CLEAR, PR_LATCH_BBLOCK }, /* Inconsistency can't be fixed prompt */ { PR_1_BBINODE_BAD_METABLOCK_PROMPT, N_("\nThe bad @b @i has probably been corrupted. You probably\n" "should stop now and run ""e2fsck -c"" to scan for bad blocks\n" "in the @f.\n"), PROMPT_CONTINUE, PR_PREEN_NOMSG }, /* Bad primary block */ { PR_1_BAD_PRIMARY_BLOCK, N_("\nIf the @b is really bad, the @f cannot be fixed.\n"), PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK_PROMPT }, /* Bad primary block prompt */ { PR_1_BAD_PRIMARY_BLOCK_PROMPT, N_("You can remove this @b from the bad @b list and hope\n" "that the @b is really OK. But there are no guarantees.\n\n"), PROMPT_CLEAR, PR_PREEN_NOMSG }, /* Bad primary superblock */ { PR_1_BAD_PRIMARY_SUPERBLOCK, N_("The primary @S (%b) is on the bad @b list.\n"), PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK }, /* Bad primary block group descriptors */ { PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR, N_("Block %b in the primary @g descriptors " "is on the bad @b list\n"), PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK }, /* Bad superblock in group */ { PR_1_BAD_SUPERBLOCK, N_("Warning: Group %g's @S (%b) is bad.\n"), PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG }, /* Bad block group descriptors in group */ { PR_1_BAD_GROUP_DESCRIPTORS, N_("Warning: Group %g's copy of the @g descriptors has a bad " "@b (%b).\n"), PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG }, /* Block claimed for no reason */ { PR_1_PROGERR_CLAIMED_BLOCK, N_("Programming error? @b #%b claimed for no reason in " "process_bad_@b.\n"), PROMPT_NONE, PR_PREEN_OK }, /* Error allocating blocks for relocating metadata */ { PR_1_RELOC_BLOCK_ALLOCATE, N_("@A %N contiguous @b(s) in @b @g %g for %s: %m\n"), PROMPT_NONE, PR_PREEN_OK }, /* Error allocating block buffer during relocation process */ { PR_1_RELOC_MEMORY_ALLOCATE, N_("@A @b buffer for relocating %s\n"), PROMPT_NONE, PR_PREEN_OK }, /* Relocating metadata group information from X to Y */ { PR_1_RELOC_FROM_TO, N_("Relocating @g %g's %s from %b to %c...\n"), PROMPT_NONE, PR_PREEN_OK }, /* Relocating metatdata group information to X */ { PR_1_RELOC_TO, N_("Relocating @g %g's %s to %c...\n"), /* xgettext:no-c-format */ PROMPT_NONE, PR_PREEN_OK }, /* Block read error during relocation process */ { PR_1_RELOC_READ_ERR, N_("Warning: could not read @b %b of %s: %m\n"), PROMPT_NONE, PR_PREEN_OK }, /* Block write error during relocation process */ { PR_1_RELOC_WRITE_ERR, N_("Warning: could not write @b %b for %s: %m\n"), PROMPT_NONE, PR_PREEN_OK }, /* Error allocating inode bitmap */ { PR_1_ALLOCATE_IBITMAP_ERROR, N_("@A @i @B (%N): %m\n"), PROMPT_NONE, PR_FATAL }, /* Error allocating block bitmap */ { PR_1_ALLOCATE_BBITMAP_ERROR, N_("@A @b @B (%N): %m\n"), PROMPT_NONE, PR_FATAL }, /* Error allocating icount structure */ { PR_1_ALLOCATE_ICOUNT, N_("@A icount link information: %m\n"), PROMPT_NONE, PR_FATAL }, /* Error allocating dbcount */ { PR_1_ALLOCATE_DBCOUNT, N_("@A @d @b array: %m\n"), PROMPT_NONE, PR_FATAL }, /* Error while scanning inodes */ { PR_1_ISCAN_ERROR, N_("Error while scanning @is (%i): %m\n"), PROMPT_NONE, PR_FATAL }, /* Error while iterating over blocks */ { PR_1_BLOCK_ITERATE, N_("Error while iterating over @bs in @i %i: %m\n"), PROMPT_NONE, PR_FATAL }, /* Error while storing inode count information */ { PR_1_ICOUNT_STORE, N_("Error storing @i count information (@i=%i, count=%N): %m\n"), PROMPT_NONE, PR_FATAL }, /* Error while storing directory block information */ { PR_1_ADD_DBLOCK, N_("Error storing @d @b information " "(@i=%i, @b=%b, num=%N): %m\n"), PROMPT_NONE, PR_FATAL }, /* Error while reading inode (for clearing) */ { PR_1_READ_INODE, N_("Error reading @i %i: %m\n"), PROMPT_NONE, PR_FATAL }, /* Suppress messages prompt */ { PR_1_SUPPRESS_MESSAGES, "", PROMPT_SUPPRESS, PR_NO_OK }, /* Imagic flag set on an inode when filesystem doesn't support it */ { PR_1_SET_IMAGIC, N_("@i %i has imagic flag set. "), PROMPT_CLEAR, 0 }, /* Immutable flag set on a device or socket inode */ { PR_1_SET_IMMUTABLE, N_("Special (@v/socket/fifo/symlink) file (@i %i) has immutable\n" "or append-only flag set. "), PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK }, /* Compression flag set on an inode when filesystem doesn't support it */ { PR_1_COMPR_SET, N_("@i %i has @cion flag set on @f without @cion support. "), PROMPT_CLEAR, 0 }, /* Non-zero size for device, fifo or socket inode */ { PR_1_SET_NONZSIZE, N_("Special (@v/socket/fifo) @i %i has non-zero size. "), PROMPT_FIX, PR_PREEN_OK }, /* Filesystem revision is 0, but feature flags are set */ { PR_1_FS_REV_LEVEL, N_("@f has feature flag(s) set, but is a revision 0 @f. "), PROMPT_FIX, PR_PREEN_OK | PR_NO_OK }, /* Journal inode is not in use, but contains data */ { PR_1_JOURNAL_INODE_NOT_CLEAR, N_("@j @i is not in use, but contains data. "), PROMPT_CLEAR, PR_PREEN_OK }, /* Journal has bad mode */ { PR_1_JOURNAL_BAD_MODE, N_("@j is not regular file. "), PROMPT_FIX, PR_PREEN_OK }, /* Deal with inodes that were part of orphan linked list */ { PR_1_LOW_DTIME, N_("@i %i was part of the @o @i list. "), PROMPT_FIX, PR_LATCH_LOW_DTIME, 0 }, /* Deal with inodes that were part of corrupted orphan linked list (latch question) */ { PR_1_ORPHAN_LIST_REFUGEES, N_("@is that were part of a corrupted orphan linked list found. "), PROMPT_FIX, 0 }, /* Error allocating refcount structure */ { PR_1_ALLOCATE_REFCOUNT, N_("@A refcount structure (%N): %m\n"), PROMPT_NONE, PR_FATAL }, /* Error reading extended attribute block */ { PR_1_READ_EA_BLOCK, N_("Error reading @a @b %b for @i %i. "), PROMPT_CLEAR, 0 }, /* Invalid extended attribute block */ { PR_1_BAD_EA_BLOCK, N_("@i %i has a bad @a @b %b. "), PROMPT_CLEAR, 0 }, /* Error reading Extended Attribute block while fixing refcount */ { PR_1_EXTATTR_READ_ABORT, N_("Error reading @a @b %b (%m). "), PROMPT_ABORT, 0 }, /* Extended attribute reference count incorrect */ { PR_1_EXTATTR_REFCOUNT, N_("@a @b %b has reference count %B, @s %N. "), PROMPT_FIX, 0 }, /* Error writing Extended Attribute block while fixing refcount */ { PR_1_EXTATTR_WRITE, N_("Error writing @a @b %b (%m). "), PROMPT_ABORT, 0 }, /* Multiple EA blocks not supported */ { PR_1_EA_MULTI_BLOCK, N_("@a @b %b has h_@bs > 1. "), PROMPT_CLEAR, 0}, /* Error allocating EA region allocation structure */ { PR_1_EA_ALLOC_REGION, N_("@A @a @b %b. "), PROMPT_ABORT, 0}, /* Error EA allocation collision */ { PR_1_EA_ALLOC_COLLISION, N_("@a @b %b is corrupt (allocation collision). "), PROMPT_CLEAR, 0}, /* Bad extended attribute name */ { PR_1_EA_BAD_NAME, N_("@a @b %b is corrupt (@n name). "), PROMPT_CLEAR, 0}, /* Bad extended attribute value */ { PR_1_EA_BAD_VALUE, N_("@a @b %b is corrupt (@n value). "), PROMPT_CLEAR, 0}, /* Inode too big (latch question) */ { PR_1_INODE_TOOBIG, N_("@i %i is too big. "), PROMPT_TRUNCATE, 0 }, /* Directory too big */ { PR_1_TOOBIG_DIR, N_("@b #%B (%b) causes @d to be too big. "), PROMPT_CLEAR, PR_LATCH_TOOBIG }, /* Regular file too big */ { PR_1_TOOBIG_REG, N_("@b #%B (%b) causes file to be too big. "), PROMPT_CLEAR, PR_LATCH_TOOBIG }, /* Symlink too big */ { PR_1_TOOBIG_SYMLINK, N_("@b #%B (%b) causes symlink to be too big. "), PROMPT_CLEAR, PR_LATCH_TOOBIG }, /* INDEX_FL flag set on a non-HTREE filesystem */ { PR_1_HTREE_SET, N_("@i %i has INDEX_FL flag set on @f without htree support.\n"), PROMPT_CLEAR_HTREE, PR_PREEN_OK }, /* INDEX_FL flag set on a non-directory */ { PR_1_HTREE_NODIR, N_("@i %i has INDEX_FL flag set but is not a @d.\n"), PROMPT_CLEAR_HTREE, PR_PREEN_OK }, /* Invalid root node in HTREE directory */ { PR_1_HTREE_BADROOT, N_("@h %i has an @n root node.\n"), PROMPT_CLEAR_HTREE, PR_PREEN_OK }, /* Unsupported hash version in HTREE directory */ { PR_1_HTREE_HASHV, N_("@h %i has an unsupported hash version (%N)\n"), PROMPT_CLEAR_HTREE, PR_PREEN_OK }, /* Incompatible flag in HTREE root node */ { PR_1_HTREE_INCOMPAT, N_("@h %i uses an incompatible htree root node flag.\n"), PROMPT_CLEAR_HTREE, PR_PREEN_OK }, /* HTREE too deep */ { PR_1_HTREE_DEPTH, N_("@h %i has a tree depth (%N) which is too big\n"), PROMPT_CLEAR_HTREE, PR_PREEN_OK }, /* Bad block has indirect block that conflicts with filesystem block */ { PR_1_BB_FS_BLOCK, N_("Bad @b @i has an indirect @b (%b) that conflicts with\n" "@f metadata. "), PROMPT_CLEAR, PR_LATCH_BBLOCK }, /* Resize inode failed */ { PR_1_RESIZE_INODE_CREATE, N_("Resize @i (re)creation failed: %m."), PROMPT_ABORT, 0 }, /* invalid inode->i_extra_isize */ { PR_1_EXTRA_ISIZE, N_("@i %i has a extra size (%IS) which is @n\n"), PROMPT_FIX, PR_PREEN_OK }, /* invalid ea entry->e_name_len */ { PR_1_ATTR_NAME_LEN, N_("@a in @i %i has a namelen (%N) which is @n\n"), PROMPT_CLEAR, PR_PREEN_OK }, /* invalid ea entry->e_value_size */ { PR_1_ATTR_VALUE_SIZE, N_("@a in @i %i has a value size (%N) which is @n\n"), PROMPT_CLEAR, PR_PREEN_OK }, /* invalid ea entry->e_value_offs */ { PR_1_ATTR_VALUE_OFFSET, N_("@a in @i %i has a value offset (%N) which is @n\n"), PROMPT_CLEAR, PR_PREEN_OK }, /* invalid ea entry->e_value_block */ { PR_1_ATTR_VALUE_BLOCK, N_("@a in @i %i has a value @b (%N) which is @n (must be 0)\n"), PROMPT_CLEAR, PR_PREEN_OK }, /* invalid ea entry->e_hash */ { PR_1_ATTR_HASH, N_("@a in @i %i has a hash (%N) which is @n (must be 0)\n"), PROMPT_CLEAR, PR_PREEN_OK }, /* Pass 1b errors */ /* Pass 1B: Rescan for duplicate/bad blocks */ { PR_1B_PASS_HEADER, N_("\nRunning additional passes to resolve @bs claimed by more than one @i...\n" "Pass 1B: Rescanning for @m @bs\n"), PROMPT_NONE, 0 }, /* Duplicate/bad block(s) header */ { PR_1B_DUP_BLOCK_HEADER, N_("@m @b(s) in @i %i:"), PROMPT_NONE, 0 }, /* Duplicate/bad block(s) in inode */ { PR_1B_DUP_BLOCK, " %b", PROMPT_NONE, PR_LATCH_DBLOCK | PR_PREEN_NOHDR }, /* Duplicate/bad block(s) end */ { PR_1B_DUP_BLOCK_END, "\n", PROMPT_NONE, PR_PREEN_NOHDR }, /* Error while scanning inodes */ { PR_1B_ISCAN_ERROR, N_("Error while scanning inodes (%i): %m\n"), PROMPT_NONE, PR_FATAL }, /* Error allocating inode bitmap */ { PR_1B_ALLOCATE_IBITMAP_ERROR, N_("@A @i @B (@i_dup_map): %m\n"), PROMPT_NONE, PR_FATAL }, /* Error while iterating over blocks */ { PR_1B_BLOCK_ITERATE, N_("Error while iterating over @bs in @i %i (%s): %m\n"), PROMPT_NONE, 0 }, /* Error adjusting EA refcount */ { PR_1B_ADJ_EA_REFCOUNT, N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"), PROMPT_NONE, 0 }, /* Pass 1C: Scan directories for inodes with multiply-claimed blocks. */ { PR_1C_PASS_HEADER, N_("Pass 1C: Scanning directories for @is with @m @bs.\n"), PROMPT_NONE, 0 }, /* Pass 1D: Reconciling multiply-claimed blocks */ { PR_1D_PASS_HEADER, N_("Pass 1D: Reconciling @m @bs\n"), PROMPT_NONE, 0 }, /* File has duplicate blocks */ { PR_1D_DUP_FILE, N_("File %Q (@i #%i, mod time %IM)\n" " has %B @m @b(s), shared with %N file(s):\n"), PROMPT_NONE, 0 }, /* List of files sharing duplicate blocks */ { PR_1D_DUP_FILE_LIST, N_("\t%Q (@i #%i, mod time %IM)\n"), PROMPT_NONE, 0 }, /* File sharing blocks with filesystem metadata */ { PR_1D_SHARE_METADATA, N_("\t<@f metadata>\n"), PROMPT_NONE, 0 }, /* Report of how many duplicate/bad inodes */ { PR_1D_NUM_DUP_INODES, N_("(There are %N @is containing @m @bs.)\n\n"), PROMPT_NONE, 0 }, /* Duplicated blocks already reassigned or cloned. */ { PR_1D_DUP_BLOCKS_DEALT, N_("@m @bs already reassigned or cloned.\n\n"), PROMPT_NONE, 0 }, /* Clone duplicate/bad blocks? */ { PR_1D_CLONE_QUESTION, "", PROMPT_CLONE, PR_NO_OK }, /* Delete file? */ { PR_1D_DELETE_QUESTION, "", PROMPT_DELETE, 0 }, /* Couldn't clone file (error) */ { PR_1D_CLONE_ERROR, N_("Couldn't clone file: %m\n"), PROMPT_NONE, 0 }, /* Pass 2 errors */ /* Pass 2: Checking directory structure */ { PR_2_PASS_HEADER, N_("Pass 2: Checking @d structure\n"), PROMPT_NONE, 0 }, /* Bad inode number for '.' */ { PR_2_BAD_INODE_DOT, N_("@n @i number for '.' in @d @i %i.\n"), PROMPT_FIX, 0 }, /* Directory entry has bad inode number */ { PR_2_BAD_INO, N_("@E has @n @i #: %Di.\n"), PROMPT_CLEAR, 0 }, /* Directory entry has deleted or unused inode */ { PR_2_UNUSED_INODE, N_("@E has @D/unused @i %Di. "), PROMPT_CLEAR, PR_PREEN_OK }, /* Directry entry is link to '.' */ { PR_2_LINK_DOT, N_("@E @L to '.' "), PROMPT_CLEAR, 0 }, /* Directory entry points to inode now located in a bad block */ { PR_2_BB_INODE, N_("@E points to @i (%Di) located in a bad @b.\n"), PROMPT_CLEAR, 0 }, /* Directory entry contains a link to a directory */ { PR_2_LINK_DIR, N_("@E @L to @d %P (%Di).\n"), PROMPT_CLEAR, 0 }, /* Directory entry contains a link to the root directry */ { PR_2_LINK_ROOT, N_("@E @L to the @r.\n"), PROMPT_CLEAR, 0 }, /* Directory entry has illegal characters in its name */ { PR_2_BAD_NAME, N_("@E has illegal characters in its name.\n"), PROMPT_FIX, 0 }, /* Missing '.' in directory inode */ { PR_2_MISSING_DOT, N_("Missing '.' in @d @i %i.\n"), PROMPT_FIX, 0 }, /* Missing '..' in directory inode */ { PR_2_MISSING_DOT_DOT, N_("Missing '..' in @d @i %i.\n"), PROMPT_FIX, 0 }, /* First entry in directory inode doesn't contain '.' */ { PR_2_1ST_NOT_DOT, N_("First @e '%Dn' (@i=%Di) in @d @i %i (%p) @s '.'\n"), PROMPT_FIX, 0 }, /* Second entry in directory inode doesn't contain '..' */ { PR_2_2ND_NOT_DOT_DOT, N_("Second @e '%Dn' (@i=%Di) in @d @i %i @s '..'\n"), PROMPT_FIX, 0 }, /* i_faddr should be zero */ { PR_2_FADDR_ZERO, N_("i_faddr @F %IF, @s zero.\n"), PROMPT_CLEAR, 0 }, /* i_file_acl should be zero */ { PR_2_FILE_ACL_ZERO, N_("i_file_acl @F %If, @s zero.\n"), PROMPT_CLEAR, 0 }, /* i_dir_acl should be zero */ { PR_2_DIR_ACL_ZERO, N_("i_dir_acl @F %Id, @s zero.\n"), PROMPT_CLEAR, 0 }, /* i_frag should be zero */ { PR_2_FRAG_ZERO, N_("i_frag @F %N, @s zero.\n"), PROMPT_CLEAR, 0 }, /* i_fsize should be zero */ { PR_2_FSIZE_ZERO, N_("i_fsize @F %N, @s zero.\n"), PROMPT_CLEAR, 0 }, /* inode has bad mode */ { PR_2_BAD_MODE, N_("@i %i (%Q) has @n mode (%Im).\n"), PROMPT_CLEAR, 0 }, /* directory corrupted */ { PR_2_DIR_CORRUPTED, N_("@d @i %i, @b %B, offset %N: @d corrupted\n"), PROMPT_SALVAGE, 0 }, /* filename too long */ { PR_2_FILENAME_LONG, N_("@d @i %i, @b %B, offset %N: filename too long\n"), PROMPT_TRUNCATE, 0 }, /* Directory inode has a missing block (hole) */ { PR_2_DIRECTORY_HOLE, N_("@d @i %i has an unallocated @b #%B. "), PROMPT_ALLOCATE, 0 }, /* '.' is not NULL terminated */ { PR_2_DOT_NULL_TERM, N_("'.' @d @e in @d @i %i is not NULL terminated\n"), PROMPT_FIX, 0 }, /* '..' is not NULL terminated */ { PR_2_DOT_DOT_NULL_TERM, N_("'..' @d @e in @d @i %i is not NULL terminated\n"), PROMPT_FIX, 0 }, /* Illegal character device inode */ { PR_2_BAD_CHAR_DEV, N_("@i %i (%Q) is an @I character @v.\n"), PROMPT_CLEAR, 0 }, /* Illegal block device inode */ { PR_2_BAD_BLOCK_DEV, N_("@i %i (%Q) is an @I @b @v.\n"), PROMPT_CLEAR, 0 }, /* Duplicate '.' entry */ { PR_2_DUP_DOT, N_("@E is duplicate '.' @e.\n"), PROMPT_FIX, 0 }, /* Duplicate '..' entry */ { PR_2_DUP_DOT_DOT, N_("@E is duplicate '..' @e.\n"), PROMPT_FIX, 0 }, /* Internal error: couldn't find dir_info */ { PR_2_NO_DIRINFO, N_("Internal error: cannot find dir_info for %i.\n"), PROMPT_NONE, PR_FATAL }, /* Final rec_len is wrong */ { PR_2_FINAL_RECLEN, N_("@E has rec_len of %Dr, @s %N.\n"), PROMPT_FIX, 0 }, /* Error allocating icount structure */ { PR_2_ALLOCATE_ICOUNT, N_("@A icount structure: %m\n"), PROMPT_NONE, PR_FATAL }, /* Error iterating over directory blocks */ { PR_2_DBLIST_ITERATE, N_("Error iterating over @d @bs: %m\n"), PROMPT_NONE, PR_FATAL }, /* Error reading directory block */ { PR_2_READ_DIRBLOCK, N_("Error reading @d @b %b (@i %i): %m\n"), PROMPT_CONTINUE, 0 }, /* Error writing directory block */ { PR_2_WRITE_DIRBLOCK, N_("Error writing @d @b %b (@i %i): %m\n"), PROMPT_CONTINUE, 0 }, /* Error allocating new directory block */ { PR_2_ALLOC_DIRBOCK, N_("@A new @d @b for @i %i (%s): %m\n"), PROMPT_NONE, 0 }, /* Error deallocating inode */ { PR_2_DEALLOC_INODE, N_("Error deallocating @i %i: %m\n"), PROMPT_NONE, PR_FATAL }, /* Directory entry for '.' is big. Split? */ { PR_2_SPLIT_DOT, N_("@d @e for '.' is big. "), PROMPT_SPLIT, PR_NO_OK }, /* Illegal FIFO inode */ { PR_2_BAD_FIFO, N_("@i %i (%Q) is an @I FIFO.\n"), PROMPT_CLEAR, 0 }, /* Illegal socket inode */ { PR_2_BAD_SOCKET, N_("@i %i (%Q) is an @I socket.\n"), PROMPT_CLEAR, 0 }, /* Directory filetype not set */ { PR_2_SET_FILETYPE, N_("Setting filetype for @E to %N.\n"), PROMPT_NONE, PR_PREEN_OK | PR_NO_OK | PR_NO_NOMSG }, /* Directory filetype incorrect */ { PR_2_BAD_FILETYPE, N_("@E has an incorrect filetype (was %Dt, @s %N).\n"), PROMPT_FIX, 0 }, /* Directory filetype set on filesystem */ { PR_2_CLEAR_FILETYPE, N_("@E has filetype set.\n"), PROMPT_CLEAR, PR_PREEN_OK }, /* Directory filename is null */ { PR_2_NULL_NAME, N_("@E has a @z name.\n"), PROMPT_CLEAR, 0 }, /* Invalid symlink */ { PR_2_INVALID_SYMLINK, N_("Symlink %Q (@i #%i) is @n.\n"), PROMPT_CLEAR, 0 }, /* i_file_acl (extended attribute block) is bad */ { PR_2_FILE_ACL_BAD, N_("@a @b @F @n (%If).\n"), PROMPT_CLEAR, 0 }, /* Filesystem contains large files, but has no such flag in sb */ { PR_2_FEATURE_LARGE_FILES, N_("@f contains large files, but lacks LARGE_FILE flag in @S.\n"), PROMPT_FIX, 0 }, /* Node in HTREE directory not referenced */ { PR_2_HTREE_NOTREF, N_("@p @h %d: node (%B) not referenced\n"), PROMPT_NONE, 0 }, /* Node in HTREE directory referenced twice */ { PR_2_HTREE_DUPREF, N_("@p @h %d: node (%B) referenced twice\n"), PROMPT_NONE, 0 }, /* Node in HTREE directory has bad min hash */ { PR_2_HTREE_MIN_HASH, N_("@p @h %d: node (%B) has bad min hash\n"), PROMPT_NONE, 0 }, /* Node in HTREE directory has bad max hash */ { PR_2_HTREE_MAX_HASH, N_("@p @h %d: node (%B) has bad max hash\n"), PROMPT_NONE, 0 }, /* Clear invalid HTREE directory */ { PR_2_HTREE_CLEAR, N_("@n @h %d (%q). "), PROMPT_CLEAR, 0 }, /* Bad block in htree interior node */ { PR_2_HTREE_BADBLK, N_("@p @h %d (%q): bad @b number %b.\n"), PROMPT_CLEAR_HTREE, 0 }, /* Error adjusting EA refcount */ { PR_2_ADJ_EA_REFCOUNT, N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"), PROMPT_NONE, PR_FATAL }, /* Invalid HTREE root node */ { PR_2_HTREE_BAD_ROOT, N_("@p @h %d: root node is @n\n"), PROMPT_CLEAR_HTREE, PR_PREEN_OK }, /* Invalid HTREE limit */ { PR_2_HTREE_BAD_LIMIT, N_("@p @h %d: node (%B) has @n limit (%N)\n"), PROMPT_CLEAR_HTREE, PR_PREEN_OK }, /* Invalid HTREE count */ { PR_2_HTREE_BAD_COUNT, N_("@p @h %d: node (%B) has @n count (%N)\n"), PROMPT_CLEAR_HTREE, PR_PREEN_OK }, /* HTREE interior node has out-of-order hashes in table */ { PR_2_HTREE_HASH_ORDER, N_("@p @h %d: node (%B) has an unordered hash table\n"), PROMPT_CLEAR_HTREE, PR_PREEN_OK }, /* Node in HTREE directory has invalid depth */ { PR_2_HTREE_BAD_DEPTH, N_("@p @h %d: node (%B) has @n depth\n"), PROMPT_NONE, 0 }, /* Duplicate directory entry found */ { PR_2_DUPLICATE_DIRENT, N_("Duplicate @E found. "), PROMPT_CLEAR, 0 }, /* Non-unique filename found */ { PR_2_NON_UNIQUE_FILE, /* xgettext: no-c-format */ N_("@E has a non-unique filename.\nRename to %s"), PROMPT_NULL, 0 }, /* Duplicate directory entry found */ { PR_2_REPORT_DUP_DIRENT, N_("Duplicate @e '%Dn' found.\n\tMarking %p (%i) to be rebuilt.\n\n"), PROMPT_NONE, 0 }, /* Pass 3 errors */ /* Pass 3: Checking directory connectivity */ { PR_3_PASS_HEADER, N_("Pass 3: Checking @d connectivity\n"), PROMPT_NONE, 0 }, /* Root inode not allocated */ { PR_3_NO_ROOT_INODE, N_("@r not allocated. "), PROMPT_ALLOCATE, 0 }, /* No room in lost+found */ { PR_3_EXPAND_LF_DIR, N_("No room in @l @d. "), PROMPT_EXPAND, 0 }, /* Unconnected directory inode */ { PR_3_UNCONNECTED_DIR, N_("Unconnected @d @i %i (%p)\n"), PROMPT_CONNECT, 0 }, /* /lost+found not found */ { PR_3_NO_LF_DIR, N_("/@l not found. "), PROMPT_CREATE, PR_PREEN_OK }, /* .. entry is incorrect */ { PR_3_BAD_DOT_DOT, N_("'..' in %Q (%i) is %P (%j), @s %q (%d).\n"), PROMPT_FIX, 0 }, /* Bad or non-existent /lost+found. Cannot reconnect */ { PR_3_NO_LPF, N_("Bad or non-existent /@l. Cannot reconnect.\n"), PROMPT_NONE, 0 }, /* Could not expand /lost+found */ { PR_3_CANT_EXPAND_LPF, N_("Could not expand /@l: %m\n"), PROMPT_NONE, 0 }, /* Could not reconnect inode */ { PR_3_CANT_RECONNECT, N_("Could not reconnect %i: %m\n"), PROMPT_NONE, 0 }, /* Error while trying to find /lost+found */ { PR_3_ERR_FIND_LPF, N_("Error while trying to find /@l: %m\n"), PROMPT_NONE, 0 }, /* Error in ext2fs_new_block while creating /lost+found */ { PR_3_ERR_LPF_NEW_BLOCK, N_("ext2fs_new_@b: %m while trying to create /@l @d\n"), PROMPT_NONE, 0 }, /* Error in ext2fs_new_inode while creating /lost+found */ { PR_3_ERR_LPF_NEW_INODE, N_("ext2fs_new_@i: %m while trying to create /@l @d\n"), PROMPT_NONE, 0 }, /* Error in ext2fs_new_dir_block while creating /lost+found */ { PR_3_ERR_LPF_NEW_DIR_BLOCK, N_("ext2fs_new_dir_@b: %m while creating new @d @b\n"), PROMPT_NONE, 0 }, /* Error while writing directory block for /lost+found */ { PR_3_ERR_LPF_WRITE_BLOCK, N_("ext2fs_write_dir_@b: %m while writing the @d @b for /@l\n"), PROMPT_NONE, 0 }, /* Error while adjusting inode count */ { PR_3_ADJUST_INODE, N_("Error while adjusting @i count on @i %i\n"), PROMPT_NONE, 0 }, /* Couldn't fix parent directory -- error */ { PR_3_FIX_PARENT_ERR, N_("Couldn't fix parent of @i %i: %m\n\n"), PROMPT_NONE, 0 }, /* Couldn't fix parent directory -- couldn't find it */ { PR_3_FIX_PARENT_NOFIND, N_("Couldn't fix parent of @i %i: Couldn't find parent @d @e\n\n"), PROMPT_NONE, 0 }, /* Error allocating inode bitmap */ { PR_3_ALLOCATE_IBITMAP_ERROR, N_("@A @i @B (%N): %m\n"), PROMPT_NONE, PR_FATAL }, /* Error creating root directory */ { PR_3_CREATE_ROOT_ERROR, N_("Error creating root @d (%s): %m\n"), PROMPT_NONE, PR_FATAL }, /* Error creating lost and found directory */ { PR_3_CREATE_LPF_ERROR, N_("Error creating /@l @d (%s): %m\n"), PROMPT_NONE, PR_FATAL }, /* Root inode is not directory; aborting */ { PR_3_ROOT_NOT_DIR_ABORT, N_("@r is not a @d; aborting.\n"), PROMPT_NONE, PR_FATAL }, /* Cannot proceed without a root inode. */ { PR_3_NO_ROOT_INODE_ABORT, N_("can't proceed without a @r.\n"), PROMPT_NONE, PR_FATAL }, /* Internal error: couldn't find dir_info */ { PR_3_NO_DIRINFO, N_("Internal error: cannot find dir_info for %i.\n"), PROMPT_NONE, PR_FATAL }, /* Lost+found not a directory */ { PR_3_LPF_NOTDIR, N_("/@l is not a @d (ino=%i)\n"), PROMPT_UNLINK, 0 }, /* Pass 3A Directory Optimization */ /* Pass 3A: Optimizing directories */ { PR_3A_PASS_HEADER, N_("Pass 3A: Optimizing directories\n"), PROMPT_NONE, PR_PREEN_NOMSG }, /* Error iterating over directories */ { PR_3A_OPTIMIZE_ITER, N_("Failed to create dirs_to_hash iterator: %m"), PROMPT_NONE, 0 }, /* Error rehash directory */ { PR_3A_OPTIMIZE_DIR_ERR, N_("Failed to optimize directory %q (%d): %m"), PROMPT_NONE, 0 }, /* Rehashing dir header */ { PR_3A_OPTIMIZE_DIR_HEADER, N_("Optimizing directories: "), PROMPT_NONE, PR_MSG_ONLY }, /* Rehashing directory %d */ { PR_3A_OPTIMIZE_DIR, " %d", PROMPT_NONE, PR_LATCH_OPTIMIZE_DIR | PR_PREEN_NOHDR}, /* Rehashing dir end */ { PR_3A_OPTIMIZE_DIR_END, "\n", PROMPT_NONE, PR_PREEN_NOHDR }, /* Pass 4 errors */ /* Pass 4: Checking reference counts */ { PR_4_PASS_HEADER, N_("Pass 4: Checking reference counts\n"), PROMPT_NONE, 0 }, /* Unattached zero-length inode */ { PR_4_ZERO_LEN_INODE, N_("@u @z @i %i. "), PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK }, /* Unattached inode */ { PR_4_UNATTACHED_INODE, N_("@u @i %i\n"), PROMPT_CONNECT, 0 }, /* Inode ref count wrong */ { PR_4_BAD_REF_COUNT, N_("@i %i ref count is %Il, @s %N. "), PROMPT_FIX, PR_PREEN_OK }, { PR_4_INCONSISTENT_COUNT, N_("WARNING: PROGRAMMING BUG IN E2FSCK!\n" "\tOR SOME BONEHEAD (YOU) IS CHECKING A MOUNTED (LIVE) FILESYSTEM.\n" "@i_link_info[%i] is %N, @i.i_links_count is %Il. " "They @s the same!\n"), PROMPT_NONE, 0 }, /* Pass 5 errors */ /* Pass 5: Checking group summary information */ { PR_5_PASS_HEADER, N_("Pass 5: Checking @g summary information\n"), PROMPT_NONE, 0 }, /* Padding at end of inode bitmap is not set. */ { PR_5_INODE_BMAP_PADDING, N_("Padding at end of @i @B is not set. "), PROMPT_FIX, PR_PREEN_OK }, /* Padding at end of block bitmap is not set. */ { PR_5_BLOCK_BMAP_PADDING, N_("Padding at end of @b @B is not set. "), PROMPT_FIX, PR_PREEN_OK }, /* Block bitmap differences header */ { PR_5_BLOCK_BITMAP_HEADER, N_("@b @B differences: "), PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG}, /* Block not used, but marked in bitmap */ { PR_5_BLOCK_UNUSED, " -%b", PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, /* Block used, but not marked used in bitmap */ { PR_5_BLOCK_USED, " +%b", PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, /* Block bitmap differences end */ { PR_5_BLOCK_BITMAP_END, "\n", PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, /* Inode bitmap differences header */ { PR_5_INODE_BITMAP_HEADER, N_("@i @B differences: "), PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG }, /* Inode not used, but marked in bitmap */ { PR_5_INODE_UNUSED, " -%i", PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, /* Inode used, but not marked used in bitmap */ { PR_5_INODE_USED, " +%i", PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, /* Inode bitmap differences end */ { PR_5_INODE_BITMAP_END, "\n", PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, /* Free inodes count for group wrong */ { PR_5_FREE_INODE_COUNT_GROUP, N_("Free @is count wrong for @g #%g (%i, counted=%j).\n"), PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, /* Directories count for group wrong */ { PR_5_FREE_DIR_COUNT_GROUP, N_("Directories count wrong for @g #%g (%i, counted=%j).\n"), PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, /* Free inodes count wrong */ { PR_5_FREE_INODE_COUNT, N_("Free @is count wrong (%i, counted=%j).\n"), PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, /* Free blocks count for group wrong */ { PR_5_FREE_BLOCK_COUNT_GROUP, N_("Free @bs count wrong for @g #%g (%b, counted=%c).\n"), PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, /* Free blocks count wrong */ { PR_5_FREE_BLOCK_COUNT, N_("Free @bs count wrong (%b, counted=%c).\n"), PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, /* Programming error: bitmap endpoints don't match */ { PR_5_BMAP_ENDPOINTS, N_("PROGRAMMING ERROR: @f (#%N) @B endpoints (%b, %c) don't " "match calculated @B endpoints (%i, %j)\n"), PROMPT_NONE, PR_FATAL }, /* Internal error: fudging end of bitmap */ { PR_5_FUDGE_BITMAP_ERROR, N_("Internal error: fudging end of bitmap (%N)\n"), PROMPT_NONE, PR_FATAL }, /* Error copying in replacement inode bitmap */ { PR_5_COPY_IBITMAP_ERROR, N_("Error copying in replacement @i @B: %m\n"), PROMPT_NONE, PR_FATAL }, /* Error copying in replacement block bitmap */ { PR_5_COPY_BBITMAP_ERROR, N_("Error copying in replacement @b @B: %m\n"), PROMPT_NONE, PR_FATAL }, /* Block range not used, but marked in bitmap */ { PR_5_BLOCK_RANGE_UNUSED, " -(%b--%c)", PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, /* Block range used, but not marked used in bitmap */ { PR_5_BLOCK_RANGE_USED, " +(%b--%c)", PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, /* Inode range not used, but marked in bitmap */ { PR_5_INODE_RANGE_UNUSED, " -(%i--%j)", PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, /* Inode range used, but not marked used in bitmap */ { PR_5_INODE_RANGE_USED, " +(%i--%j)", PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, { 0 } }; /* * This is the latch flags register. It allows several problems to be * "latched" together. This means that the user has to answer but one * question for the set of problems, and all of the associated * problems will be either fixed or not fixed. */ static struct latch_descr pr_latch_info[] = { { PR_LATCH_BLOCK, PR_1_INODE_BLOCK_LATCH, 0 }, { PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 }, { PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END }, { PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END }, { PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 }, { PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END }, { PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 }, { PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 }, { PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END }, { -1, 0, 0 }, }; static const struct e2fsck_problem *find_problem(problem_t code) { int i; for (i=0; problem_table[i].e2p_code; i++) { if (problem_table[i].e2p_code == code) return &problem_table[i]; } return 0; } static struct latch_descr *find_latch(int code) { int i; for (i=0; pr_latch_info[i].latch_code >= 0; i++) { if (pr_latch_info[i].latch_code == code) return &pr_latch_info[i]; } return 0; } int end_problem_latch(e2fsck_t ctx, int mask) { struct latch_descr *ldesc; struct problem_context pctx; int answer = -1; ldesc = find_latch(mask); if (ldesc->end_message && (ldesc->flags & PRL_LATCHED)) { clear_problem_context(&pctx); answer = fix_problem(ctx, ldesc->end_message, &pctx); } ldesc->flags &= ~(PRL_VARIABLE); return answer; } int set_latch_flags(int mask, int setflags, int clearflags) { struct latch_descr *ldesc; ldesc = find_latch(mask); if (!ldesc) return -1; ldesc->flags |= setflags; ldesc->flags &= ~clearflags; return 0; } void clear_problem_context(struct problem_context *ctx) { memset(ctx, 0, sizeof(struct problem_context)); ctx->blkcount = -1; ctx->group = -1; } int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx) { ext2_filsys fs = ctx->fs; const struct e2fsck_problem *ptr; struct latch_descr *ldesc = NULL; const char *message; int def_yn, answer, ans; int print_answer = 0; int suppress = 0; ptr = find_problem(code); if (!ptr) { printf(_("Unhandled error code (0x%x)!\n"), code); return 0; } def_yn = 1; if ((ptr->flags & PR_NO_DEFAULT) || ((ptr->flags & PR_PREEN_NO) && (ctx->options & E2F_OPT_PREEN)) || (ctx->options & E2F_OPT_NO)) def_yn= 0; /* * Do special latch processing. This is where we ask the * latch question, if it exists */ if (ptr->flags & PR_LATCH_MASK) { ldesc = find_latch(ptr->flags & PR_LATCH_MASK); if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) { ans = fix_problem(ctx, ldesc->question, pctx); if (ans == 1) ldesc->flags |= PRL_YES; if (ans == 0) ldesc->flags |= PRL_NO; ldesc->flags |= PRL_LATCHED; } if (ldesc->flags & PRL_SUPPRESS) suppress++; } if ((ptr->flags & PR_PREEN_NOMSG) && (ctx->options & E2F_OPT_PREEN)) suppress++; if ((ptr->flags & PR_NO_NOMSG) && (ctx->options & E2F_OPT_NO)) suppress++; if (!suppress) { message = ptr->e2p_description; if ((ctx->options & E2F_OPT_PREEN) && !(ptr->flags & PR_PREEN_NOHDR)) { printf("%s: ", ctx->device_name ? ctx->device_name : ctx->filesystem_name); } if (*message) print_e2fsck_message(ctx, _(message), pctx, 1); } if (!(ptr->flags & PR_PREEN_OK) && (ptr->prompt != PROMPT_NONE)) preenhalt(ctx); if (ptr->flags & PR_FATAL) bb_error_msg_and_die(0); if (ptr->prompt == PROMPT_NONE) { if (ptr->flags & PR_NOCOLLATE) answer = -1; else answer = def_yn; } else { if (ctx->options & E2F_OPT_PREEN) { answer = def_yn; if (!(ptr->flags & PR_PREEN_NOMSG)) print_answer = 1; } else if ((ptr->flags & PR_LATCH_MASK) && (ldesc->flags & (PRL_YES | PRL_NO))) { if (!suppress) print_answer = 1; if (ldesc->flags & PRL_YES) answer = 1; else answer = 0; } else answer = ask(ctx, _(prompt[(int) ptr->prompt]), def_yn); if (!answer && !(ptr->flags & PR_NO_OK)) ext2fs_unmark_valid(fs); if (print_answer) printf("%s.\n", answer ? _(preen_msg[(int) ptr->prompt]) : _("IGNORED")); } if ((ptr->prompt == PROMPT_ABORT) && answer) bb_error_msg_and_die(0); if (ptr->flags & PR_AFTER_CODE) answer = fix_problem(ctx, ptr->second_code, pctx); return answer; } /* * linux/fs/recovery.c * * Written by Stephen C. Tweedie , 1999 */ /* * Maintain information about the progress of the recovery job, so that * the different passes can carry information between them. */ struct recovery_info { tid_t start_transaction; tid_t end_transaction; int nr_replays; int nr_revokes; int nr_revoke_hits; }; enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY}; static int do_one_pass(journal_t *journal, struct recovery_info *info, enum passtype pass); static int scan_revoke_records(journal_t *, struct buffer_head *, tid_t, struct recovery_info *); /* * Read a block from the journal */ static int jread(struct buffer_head **bhp, journal_t *journal, unsigned int offset) { int err; unsigned long blocknr; struct buffer_head *bh; *bhp = NULL; err = journal_bmap(journal, offset, &blocknr); if (err) { printf("JBD: bad block at offset %u\n", offset); return err; } bh = getblk(journal->j_dev, blocknr, journal->j_blocksize); if (!bh) return -ENOMEM; if (!buffer_uptodate(bh)) { /* If this is a brand new buffer, start readahead. Otherwise, we assume we are already reading it. */ if (!buffer_req(bh)) do_readahead(journal, offset); wait_on_buffer(bh); } if (!buffer_uptodate(bh)) { printf("JBD: Failed to read block at offset %u\n", offset); brelse(bh); return -EIO; } *bhp = bh; return 0; } /* * Count the number of in-use tags in a journal descriptor block. */ static int count_tags(struct buffer_head *bh, int size) { char * tagp; journal_block_tag_t * tag; int nr = 0; tagp = &bh->b_data[sizeof(journal_header_t)]; while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) { tag = (journal_block_tag_t *) tagp; nr++; tagp += sizeof(journal_block_tag_t); if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID))) tagp += 16; if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG)) break; } return nr; } /* Make sure we wrap around the log correctly! */ #define wrap(journal, var) \ do { \ if (var >= (journal)->j_last) \ var -= ((journal)->j_last - (journal)->j_first); \ } while (0) /** * int journal_recover(journal_t *journal) - recovers a on-disk journal * @journal: the journal to recover * * The primary function for recovering the log contents when mounting a * journaled device. * * Recovery is done in three passes. In the first pass, we look for the * end of the log. In the second, we assemble the list of revoke * blocks. In the third and final pass, we replay any un-revoked blocks * in the log. */ int journal_recover(journal_t *journal) { int err; journal_superblock_t * sb; struct recovery_info info; memset(&info, 0, sizeof(info)); sb = journal->j_superblock; /* * The journal superblock's s_start field (the current log head) * is always zero if, and only if, the journal was cleanly * unmounted. */ if (!sb->s_start) { journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1; return 0; } err = do_one_pass(journal, &info, PASS_SCAN); if (!err) err = do_one_pass(journal, &info, PASS_REVOKE); if (!err) err = do_one_pass(journal, &info, PASS_REPLAY); /* Restart the log at the next transaction ID, thus invalidating * any existing commit records in the log. */ journal->j_transaction_sequence = ++info.end_transaction; journal_clear_revoke(journal); sync_blockdev(journal->j_fs_dev); return err; } static int do_one_pass(journal_t *journal, struct recovery_info *info, enum passtype pass) { unsigned int first_commit_ID, next_commit_ID; unsigned long next_log_block; int err, success = 0; journal_superblock_t * sb; journal_header_t * tmp; struct buffer_head * bh; unsigned int sequence; int blocktype; /* Precompute the maximum metadata descriptors in a descriptor block */ int MAX_BLOCKS_PER_DESC; MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t)) / sizeof(journal_block_tag_t)); /* * First thing is to establish what we expect to find in the log * (in terms of transaction IDs), and where (in terms of log * block offsets): query the superblock. */ sb = journal->j_superblock; next_commit_ID = ntohl(sb->s_sequence); next_log_block = ntohl(sb->s_start); first_commit_ID = next_commit_ID; if (pass == PASS_SCAN) info->start_transaction = first_commit_ID; /* * Now we walk through the log, transaction by transaction, * making sure that each transaction has a commit block in the * expected place. Each complete transaction gets replayed back * into the main filesystem. */ while (1) { int flags; char * tagp; journal_block_tag_t * tag; struct buffer_head * obh; struct buffer_head * nbh; /* If we already know where to stop the log traversal, * check right now that we haven't gone past the end of * the log. */ if (pass != PASS_SCAN) if (tid_geq(next_commit_ID, info->end_transaction)) break; /* Skip over each chunk of the transaction looking * either the next descriptor block or the final commit * record. */ err = jread(&bh, journal, next_log_block); if (err) goto failed; next_log_block++; wrap(journal, next_log_block); /* What kind of buffer is it? * * If it is a descriptor block, check that it has the * expected sequence number. Otherwise, we're all done * here. */ tmp = (journal_header_t *)bh->b_data; if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) { brelse(bh); break; } blocktype = ntohl(tmp->h_blocktype); sequence = ntohl(tmp->h_sequence); if (sequence != next_commit_ID) { brelse(bh); break; } /* OK, we have a valid descriptor block which matches * all of the sequence number checks. What are we going * to do with it? That depends on the pass... */ switch (blocktype) { case JFS_DESCRIPTOR_BLOCK: /* If it is a valid descriptor block, replay it * in pass REPLAY; otherwise, just skip over the * blocks it describes. */ if (pass != PASS_REPLAY) { next_log_block += count_tags(bh, journal->j_blocksize); wrap(journal, next_log_block); brelse(bh); continue; } /* A descriptor block: we can now write all of * the data blocks. Yay, useful work is finally * getting done here! */ tagp = &bh->b_data[sizeof(journal_header_t)]; while ((tagp - bh->b_data +sizeof(journal_block_tag_t)) <= journal->j_blocksize) { unsigned long io_block; tag = (journal_block_tag_t *) tagp; flags = ntohl(tag->t_flags); io_block = next_log_block++; wrap(journal, next_log_block); err = jread(&obh, journal, io_block); if (err) { /* Recover what we can, but * report failure at the end. */ success = err; printf("JBD: IO error %d recovering " "block %ld in log\n", err, io_block); } else { unsigned long blocknr; blocknr = ntohl(tag->t_blocknr); /* If the block has been * revoked, then we're all done * here. */ if (journal_test_revoke (journal, blocknr, next_commit_ID)) { brelse(obh); ++info->nr_revoke_hits; goto skip_write; } /* Find a buffer for the new * data being restored */ nbh = getblk(journal->j_fs_dev, blocknr, journal->j_blocksize); if (nbh == NULL) { printf("JBD: Out of memory " "during recovery.\n"); err = -ENOMEM; brelse(bh); brelse(obh); goto failed; } lock_buffer(nbh); memcpy(nbh->b_data, obh->b_data, journal->j_blocksize); if (flags & JFS_FLAG_ESCAPE) { *((unsigned int *)bh->b_data) = htonl(JFS_MAGIC_NUMBER); } mark_buffer_uptodate(nbh, 1); mark_buffer_dirty(nbh); ++info->nr_replays; /* ll_rw_block(WRITE, 1, &nbh); */ unlock_buffer(nbh); brelse(obh); brelse(nbh); } skip_write: tagp += sizeof(journal_block_tag_t); if (!(flags & JFS_FLAG_SAME_UUID)) tagp += 16; if (flags & JFS_FLAG_LAST_TAG) break; } brelse(bh); continue; case JFS_COMMIT_BLOCK: /* Found an expected commit block: not much to * do other than move on to the next sequence * number. */ brelse(bh); next_commit_ID++; continue; case JFS_REVOKE_BLOCK: /* If we aren't in the REVOKE pass, then we can * just skip over this block. */ if (pass != PASS_REVOKE) { brelse(bh); continue; } err = scan_revoke_records(journal, bh, next_commit_ID, info); brelse(bh); if (err) goto failed; continue; default: goto done; } } done: /* * We broke out of the log scan loop: either we came to the * known end of the log or we found an unexpected block in the * log. If the latter happened, then we know that the "current" * transaction marks the end of the valid log. */ if (pass == PASS_SCAN) info->end_transaction = next_commit_ID; else { /* It's really bad news if different passes end up at * different places (but possible due to IO errors). */ if (info->end_transaction != next_commit_ID) { printf("JBD: recovery pass %d ended at " "transaction %u, expected %u\n", pass, next_commit_ID, info->end_transaction); if (!success) success = -EIO; } } return success; failed: return err; } /* Scan a revoke record, marking all blocks mentioned as revoked. */ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh, tid_t sequence, struct recovery_info *info) { journal_revoke_header_t *header; int offset, max; header = (journal_revoke_header_t *) bh->b_data; offset = sizeof(journal_revoke_header_t); max = ntohl(header->r_count); while (offset < max) { unsigned long blocknr; int err; blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset))); offset += 4; err = journal_set_revoke(journal, blocknr, sequence); if (err) return err; ++info->nr_revokes; } return 0; } /* * rehash.c --- rebuild hash tree directories * * This algorithm is designed for simplicity of implementation and to * pack the directory as much as possible. It however requires twice * as much memory as the size of the directory. The maximum size * directory supported using a 4k blocksize is roughly a gigabyte, and * so there may very well be problems with machines that don't have * virtual memory, and obscenely large directories. * * An alternate algorithm which is much more disk intensive could be * written, and probably will need to be written in the future. The * design goals of such an algorithm are: (a) use (roughly) constant * amounts of memory, no matter how large the directory, (b) the * directory must be safe at all times, even if e2fsck is interrupted * in the middle, (c) we must use minimal amounts of extra disk * blocks. This pretty much requires an incremental approach, where * we are reading from one part of the directory, and inserting into * the front half. So the algorithm will have to keep track of a * moving block boundary between the new tree and the old tree, and * files will need to be moved from the old directory and inserted * into the new tree. If the new directory requires space which isn't * yet available, blocks from the beginning part of the old directory * may need to be moved to the end of the directory to make room for * the new tree: * * -------------------------------------------------------- * | new tree | | old tree | * -------------------------------------------------------- * ^ ptr ^ptr * tail new head old * * This is going to be a pain in the tuckus to implement, and will * require a lot more disk accesses. So I'm going to skip it for now; * it's only really going to be an issue for really, really big * filesystems (when we reach the level of tens of millions of files * in a single directory). It will probably be easier to simply * require that e2fsck use VM first. */ struct fill_dir_struct { char *buf; struct ext2_inode *inode; int err; e2fsck_t ctx; struct hash_entry *harray; int max_array, num_array; int dir_size; int compress; ino_t parent; }; struct hash_entry { ext2_dirhash_t hash; ext2_dirhash_t minor_hash; struct ext2_dir_entry *dir; }; struct out_dir { int num; int max; char *buf; ext2_dirhash_t *hashes; }; static int fill_dir_block(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block FSCK_ATTR((unused)), int ref_offset FSCK_ATTR((unused)), void *priv_data) { struct fill_dir_struct *fd = (struct fill_dir_struct *) priv_data; struct hash_entry *new_array, *ent; struct ext2_dir_entry *dirent; char *dir; unsigned int offset, dir_offset; if (blockcnt < 0) return 0; offset = blockcnt * fs->blocksize; if (offset + fs->blocksize > fd->inode->i_size) { fd->err = EXT2_ET_DIR_CORRUPTED; return BLOCK_ABORT; } dir = (fd->buf+offset); if (HOLE_BLKADDR(*block_nr)) { memset(dir, 0, fs->blocksize); dirent = (struct ext2_dir_entry *) dir; dirent->rec_len = fs->blocksize; } else { fd->err = ext2fs_read_dir_block(fs, *block_nr, dir); if (fd->err) return BLOCK_ABORT; } /* While the directory block is "hot", index it. */ dir_offset = 0; while (dir_offset < fs->blocksize) { dirent = (struct ext2_dir_entry *) (dir + dir_offset); if (((dir_offset + dirent->rec_len) > fs->blocksize) || (dirent->rec_len < 8) || ((dirent->rec_len % 4) != 0) || (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) { fd->err = EXT2_ET_DIR_CORRUPTED; return BLOCK_ABORT; } dir_offset += dirent->rec_len; if (dirent->inode == 0) continue; if (!fd->compress && ((dirent->name_len&0xFF) == 1) && (dirent->name[0] == '.')) continue; if (!fd->compress && ((dirent->name_len&0xFF) == 2) && (dirent->name[0] == '.') && (dirent->name[1] == '.')) { fd->parent = dirent->inode; continue; } if (fd->num_array >= fd->max_array) { new_array = xrealloc(fd->harray, sizeof(struct hash_entry) * (fd->max_array+500)); fd->harray = new_array; fd->max_array += 500; } ent = fd->harray + fd->num_array++; ent->dir = dirent; fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF); if (fd->compress) ent->hash = ent->minor_hash = 0; else { fd->err = ext2fs_dirhash(fs->super->s_def_hash_version, dirent->name, dirent->name_len & 0xFF, fs->super->s_hash_seed, &ent->hash, &ent->minor_hash); if (fd->err) return BLOCK_ABORT; } } return 0; } /* Used for sorting the hash entry */ static int name_cmp(const void *a, const void *b) { const struct hash_entry *he_a = (const struct hash_entry *) a; const struct hash_entry *he_b = (const struct hash_entry *) b; int ret; int min_len; min_len = he_a->dir->name_len; if (min_len > he_b->dir->name_len) min_len = he_b->dir->name_len; ret = strncmp(he_a->dir->name, he_b->dir->name, min_len); if (ret == 0) { if (he_a->dir->name_len > he_b->dir->name_len) ret = 1; else if (he_a->dir->name_len < he_b->dir->name_len) ret = -1; else ret = he_b->dir->inode - he_a->dir->inode; } return ret; } /* Used for sorting the hash entry */ static int hash_cmp(const void *a, const void *b) { const struct hash_entry *he_a = (const struct hash_entry *) a; const struct hash_entry *he_b = (const struct hash_entry *) b; int ret; if (he_a->hash > he_b->hash) ret = 1; else if (he_a->hash < he_b->hash) ret = -1; else { if (he_a->minor_hash > he_b->minor_hash) ret = 1; else if (he_a->minor_hash < he_b->minor_hash) ret = -1; else ret = name_cmp(a, b); } return ret; } static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir, int blocks) { void *new_mem; if (outdir->max) { new_mem = xrealloc(outdir->buf, blocks * fs->blocksize); outdir->buf = new_mem; new_mem = xrealloc(outdir->hashes, blocks * sizeof(ext2_dirhash_t)); outdir->hashes = new_mem; } else { outdir->buf = xmalloc(blocks * fs->blocksize); outdir->hashes = xmalloc(blocks * sizeof(ext2_dirhash_t)); outdir->num = 0; } outdir->max = blocks; return 0; } static void free_out_dir(struct out_dir *outdir) { free(outdir->buf); free(outdir->hashes); outdir->max = 0; outdir->num =0; } static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir, char ** ret) { errcode_t retval; if (outdir->num >= outdir->max) { retval = alloc_size_dir(fs, outdir, outdir->max + 50); if (retval) return retval; } *ret = outdir->buf + (outdir->num++ * fs->blocksize); memset(*ret, 0, fs->blocksize); return 0; } /* * This function is used to make a unique filename. We do this by * appending ~0, and then incrementing the number. However, we cannot * expand the length of the filename beyond the padding available in * the directory entry. */ static void mutate_name(char *str, __u16 *len) { int i; __u16 l = *len & 0xFF, h = *len & 0xff00; /* * First check to see if it looks the name has been mutated * already */ for (i = l-1; i > 0; i--) { if (!isdigit(str[i])) break; } if ((i == l-1) || (str[i] != '~')) { if (((l-1) & 3) < 2) l += 2; else l = (l+3) & ~3; str[l-2] = '~'; str[l-1] = '0'; *len = l | h; return; } for (i = l-1; i >= 0; i--) { if (isdigit(str[i])) { if (str[i] == '9') str[i] = '0'; else { str[i]++; return; } continue; } if (i == 1) { if (str[0] == 'z') str[0] = 'A'; else if (str[0] == 'Z') { str[0] = '~'; str[1] = '0'; } else str[0]++; } else if (i > 0) { str[i] = '1'; str[i-1] = '~'; } else { if (str[0] == '~') str[0] = 'a'; else str[0]++; } break; } } static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs, ext2_ino_t ino, struct fill_dir_struct *fd) { struct problem_context pctx; struct hash_entry *ent, *prev; int i, j; int fixed = 0; char new_name[256]; __u16 new_len; clear_problem_context(&pctx); pctx.ino = ino; for (i=1; i < fd->num_array; i++) { ent = fd->harray + i; prev = ent - 1; if (!ent->dir->inode || ((ent->dir->name_len & 0xFF) != (prev->dir->name_len & 0xFF)) || (strncmp(ent->dir->name, prev->dir->name, ent->dir->name_len & 0xFF))) continue; pctx.dirent = ent->dir; if ((ent->dir->inode == prev->dir->inode) && fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) { e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1); ent->dir->inode = 0; fixed++; continue; } memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF); new_len = ent->dir->name_len; mutate_name(new_name, &new_len); for (j=0; j < fd->num_array; j++) { if ((i==j) || ((ent->dir->name_len & 0xFF) != (fd->harray[j].dir->name_len & 0xFF)) || (strncmp(new_name, fd->harray[j].dir->name, new_len & 0xFF))) continue; mutate_name(new_name, &new_len); j = -1; } new_name[new_len & 0xFF] = 0; pctx.str = new_name; if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) { memcpy(ent->dir->name, new_name, new_len & 0xFF); ent->dir->name_len = new_len; ext2fs_dirhash(fs->super->s_def_hash_version, ent->dir->name, ent->dir->name_len & 0xFF, fs->super->s_hash_seed, &ent->hash, &ent->minor_hash); fixed++; } } return fixed; } static errcode_t copy_dir_entries(ext2_filsys fs, struct fill_dir_struct *fd, struct out_dir *outdir) { errcode_t retval; char *block_start; struct hash_entry *ent; struct ext2_dir_entry *dirent; int i, rec_len, left; ext2_dirhash_t prev_hash; int offset; outdir->max = 0; retval = alloc_size_dir(fs, outdir, (fd->dir_size / fs->blocksize) + 2); if (retval) return retval; outdir->num = fd->compress ? 0 : 1; offset = 0; outdir->hashes[0] = 0; prev_hash = 1; if ((retval = get_next_block(fs, outdir, &block_start))) return retval; dirent = (struct ext2_dir_entry *) block_start; left = fs->blocksize; for (i=0; i < fd->num_array; i++) { ent = fd->harray + i; if (ent->dir->inode == 0) continue; rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF); if (rec_len > left) { if (left) dirent->rec_len += left; if ((retval = get_next_block(fs, outdir, &block_start))) return retval; offset = 0; } left = fs->blocksize - offset; dirent = (struct ext2_dir_entry *) (block_start + offset); if (offset == 0) { if (ent->hash == prev_hash) outdir->hashes[outdir->num-1] = ent->hash | 1; else outdir->hashes[outdir->num-1] = ent->hash; } dirent->inode = ent->dir->inode; dirent->name_len = ent->dir->name_len; dirent->rec_len = rec_len; memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF); offset += rec_len; left -= rec_len; if (left < 12) { dirent->rec_len += left; offset += left; left = 0; } prev_hash = ent->hash; } if (left) dirent->rec_len += left; return 0; } static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf, ext2_ino_t ino, ext2_ino_t parent) { struct ext2_dir_entry *dir; struct ext2_dx_root_info *root; struct ext2_dx_countlimit *limits; int filetype = 0; if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) filetype = EXT2_FT_DIR << 8; memset(buf, 0, fs->blocksize); dir = (struct ext2_dir_entry *) buf; dir->inode = ino; dir->name[0] = '.'; dir->name_len = 1 | filetype; dir->rec_len = 12; dir = (struct ext2_dir_entry *) (buf + 12); dir->inode = parent; dir->name[0] = '.'; dir->name[1] = '.'; dir->name_len = 2 | filetype; dir->rec_len = fs->blocksize - 12; root = (struct ext2_dx_root_info *) (buf+24); root->reserved_zero = 0; root->hash_version = fs->super->s_def_hash_version; root->info_length = 8; root->indirect_levels = 0; root->unused_flags = 0; limits = (struct ext2_dx_countlimit *) (buf+32); limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry); limits->count = 0; return root; } static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf) { struct ext2_dir_entry *dir; struct ext2_dx_countlimit *limits; memset(buf, 0, fs->blocksize); dir = (struct ext2_dir_entry *) buf; dir->inode = 0; dir->rec_len = fs->blocksize; limits = (struct ext2_dx_countlimit *) (buf+8); limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry); limits->count = 0; return (struct ext2_dx_entry *) limits; } /* * This function takes the leaf nodes which have been written in * outdir, and populates the root node and any necessary interior nodes. */ static errcode_t calculate_tree(ext2_filsys fs, struct out_dir *outdir, ext2_ino_t ino, ext2_ino_t parent) { struct ext2_dx_root_info *root_info; struct ext2_dx_entry *root, *dx_ent = NULL; struct ext2_dx_countlimit *root_limit, *limit; errcode_t retval; char * block_start; int i, c1, c2, nblks; int limit_offset, root_offset; root_info = set_root_node(fs, outdir->buf, ino, parent); root_offset = limit_offset = ((char *) root_info - outdir->buf) + root_info->info_length; root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset); c1 = root_limit->limit; nblks = outdir->num; /* Write out the pointer blocks */ if (nblks-1 <= c1) { /* Just write out the root block, and we're done */ root = (struct ext2_dx_entry *) (outdir->buf + root_offset); for (i=1; i < nblks; i++) { root->block = ext2fs_cpu_to_le32(i); if (i != 1) root->hash = ext2fs_cpu_to_le32(outdir->hashes[i]); root++; c1--; } } else { c2 = 0; limit = 0; root_info->indirect_levels = 1; for (i=1; i < nblks; i++) { if (c1 == 0) return ENOSPC; if (c2 == 0) { if (limit) limit->limit = limit->count = ext2fs_cpu_to_le16(limit->limit); root = (struct ext2_dx_entry *) (outdir->buf + root_offset); root->block = ext2fs_cpu_to_le32(outdir->num); if (i != 1) root->hash = ext2fs_cpu_to_le32(outdir->hashes[i]); if ((retval = get_next_block(fs, outdir, &block_start))) return retval; dx_ent = set_int_node(fs, block_start); limit = (struct ext2_dx_countlimit *) dx_ent; c2 = limit->limit; root_offset += sizeof(struct ext2_dx_entry); c1--; } dx_ent->block = ext2fs_cpu_to_le32(i); if (c2 != limit->limit) dx_ent->hash = ext2fs_cpu_to_le32(outdir->hashes[i]); dx_ent++; c2--; } limit->count = ext2fs_cpu_to_le16(limit->limit - c2); limit->limit = ext2fs_cpu_to_le16(limit->limit); } root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset); root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1); root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit); return 0; } struct write_dir_struct { struct out_dir *outdir; errcode_t err; e2fsck_t ctx; int cleared; }; /* * Helper function which writes out a directory block. */ static int write_dir_block(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block FSCK_ATTR((unused)), int ref_offset FSCK_ATTR((unused)), void *priv_data) { struct write_dir_struct *wd = (struct write_dir_struct *) priv_data; blk_t blk; char *dir; if (*block_nr == 0) return 0; if (blockcnt >= wd->outdir->num) { e2fsck_read_bitmaps(wd->ctx); blk = *block_nr; ext2fs_unmark_block_bitmap(wd->ctx->block_found_map, blk); ext2fs_block_alloc_stats(fs, blk, -1); *block_nr = 0; wd->cleared++; return BLOCK_CHANGED; } if (blockcnt < 0) return 0; dir = wd->outdir->buf + (blockcnt * fs->blocksize); wd->err = ext2fs_write_dir_block(fs, *block_nr, dir); if (wd->err) return BLOCK_ABORT; return 0; } static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs, struct out_dir *outdir, ext2_ino_t ino, int compress) { struct write_dir_struct wd; errcode_t retval; struct ext2_inode inode; retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num); if (retval) return retval; wd.outdir = outdir; wd.err = 0; wd.ctx = ctx; wd.cleared = 0; retval = ext2fs_block_iterate2(fs, ino, 0, 0, write_dir_block, &wd); if (retval) return retval; if (wd.err) return wd.err; e2fsck_read_inode(ctx, ino, &inode, "rehash_dir"); if (compress) inode.i_flags &= ~EXT2_INDEX_FL; else inode.i_flags |= EXT2_INDEX_FL; inode.i_size = outdir->num * fs->blocksize; inode.i_blocks -= (fs->blocksize / 512) * wd.cleared; e2fsck_write_inode(ctx, ino, &inode, "rehash_dir"); return 0; } static errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino) { ext2_filsys fs = ctx->fs; errcode_t retval; struct ext2_inode inode; char *dir_buf = NULL; struct fill_dir_struct fd; struct out_dir outdir; outdir.max = outdir.num = 0; outdir.buf = 0; outdir.hashes = 0; e2fsck_read_inode(ctx, ino, &inode, "rehash_dir"); retval = ENOMEM; fd.harray = 0; dir_buf = xmalloc(inode.i_size); fd.max_array = inode.i_size / 32; fd.num_array = 0; fd.harray = xmalloc(fd.max_array * sizeof(struct hash_entry)); fd.ctx = ctx; fd.buf = dir_buf; fd.inode = &inode; fd.err = 0; fd.dir_size = 0; fd.compress = 0; if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) || (inode.i_size / fs->blocksize) < 2) fd.compress = 1; fd.parent = 0; /* Read in the entire directory into memory */ retval = ext2fs_block_iterate2(fs, ino, 0, 0, fill_dir_block, &fd); if (fd.err) { retval = fd.err; goto errout; } /* Sort the list */ resort: if (fd.compress) qsort(fd.harray+2, fd.num_array-2, sizeof(struct hash_entry), name_cmp); else qsort(fd.harray, fd.num_array, sizeof(struct hash_entry), hash_cmp); /* * Look for duplicates */ if (duplicate_search_and_fix(ctx, fs, ino, &fd)) goto resort; if (ctx->options & E2F_OPT_NO) { retval = 0; goto errout; } /* * Copy the directory entries. In a htree directory these * will become the leaf nodes. */ retval = copy_dir_entries(fs, &fd, &outdir); if (retval) goto errout; free(dir_buf); dir_buf = 0; if (!fd.compress) { /* Calculate the interior nodes */ retval = calculate_tree(fs, &outdir, ino, fd.parent); if (retval) goto errout; } retval = write_directory(ctx, fs, &outdir, ino, fd.compress); errout: free(dir_buf); free(fd.harray); free_out_dir(&outdir); return retval; } void e2fsck_rehash_directories(e2fsck_t ctx) { struct problem_context pctx; struct dir_info *dir; ext2_u32_iterate iter; ext2_ino_t ino; errcode_t retval; int i, cur, max, all_dirs, dir_index, first = 1; all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS; if (!ctx->dirs_to_hash && !all_dirs) return; e2fsck_get_lost_and_found(ctx, 0); clear_problem_context(&pctx); dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX; cur = 0; if (all_dirs) { i = 0; max = e2fsck_get_num_dirinfo(ctx); } else { retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash, &iter); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx); return; } max = ext2fs_u32_list_count(ctx->dirs_to_hash); } while (1) { if (all_dirs) { if ((dir = e2fsck_dir_info_iter(ctx, &i)) == 0) break; ino = dir->ino; } else { if (!ext2fs_u32_list_iterate(iter, &ino)) break; } if (ino == ctx->lost_and_found) continue; pctx.dir = ino; if (first) { fix_problem(ctx, PR_3A_PASS_HEADER, &pctx); first = 0; } pctx.errcode = e2fsck_rehash_dir(ctx, ino); if (pctx.errcode) { end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR); fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx); } if (ctx->progress && !ctx->progress_fd) e2fsck_simple_progress(ctx, "Rebuilding directory", 100.0 * (float) (++cur) / (float) max, ino); } end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR); if (!all_dirs) ext2fs_u32_list_iterate_end(iter); ext2fs_u32_list_free(ctx->dirs_to_hash); ctx->dirs_to_hash = 0; } /* * linux/fs/revoke.c * * Journal revoke routines for the generic filesystem journaling code; * part of the ext2fs journaling system. * * Revoke is the mechanism used to prevent old log records for deleted * metadata from being replayed on top of newer data using the same * blocks. The revoke mechanism is used in two separate places: * * + Commit: during commit we write the entire list of the current * transaction's revoked blocks to the journal * * + Recovery: during recovery we record the transaction ID of all * revoked blocks. If there are multiple revoke records in the log * for a single block, only the last one counts, and if there is a log * entry for a block beyond the last revoke, then that log entry still * gets replayed. * * We can get interactions between revokes and new log data within a * single transaction: * * Block is revoked and then journaled: * The desired end result is the journaling of the new block, so we * cancel the revoke before the transaction commits. * * Block is journaled and then revoked: * The revoke must take precedence over the write of the block, so we * need either to cancel the journal entry or to write the revoke * later in the log than the log block. In this case, we choose the * latter: journaling a block cancels any revoke record for that block * in the current transaction, so any revoke for that block in the * transaction must have happened after the block was journaled and so * the revoke must take precedence. * * Block is revoked and then written as data: * The data write is allowed to succeed, but the revoke is _not_ * cancelled. We still need to prevent old log records from * overwriting the new data. We don't even need to clear the revoke * bit here. * * Revoke information on buffers is a tri-state value: * * RevokeValid clear: no cached revoke status, need to look it up * RevokeValid set, Revoked clear: * buffer has not been revoked, and cancel_revoke * need do nothing. * RevokeValid set, Revoked set: * buffer has been revoked. */ static kmem_cache_t *revoke_record_cache; static kmem_cache_t *revoke_table_cache; /* Each revoke record represents one single revoked block. During journal replay, this involves recording the transaction ID of the last transaction to revoke this block. */ struct jbd_revoke_record_s { struct list_head hash; tid_t sequence; /* Used for recovery only */ unsigned long blocknr; }; /* The revoke table is just a simple hash table of revoke records. */ struct jbd_revoke_table_s { /* It is conceivable that we might want a larger hash table * for recovery. Must be a power of two. */ int hash_size; int hash_shift; struct list_head *hash_table; }; /* Utility functions to maintain the revoke table */ /* Borrowed from buffer.c: this is a tried and tested block hash function */ static int hash(journal_t *journal, unsigned long block) { struct jbd_revoke_table_s *table = journal->j_revoke; int hash_shift = table->hash_shift; return ((block << (hash_shift - 6)) ^ (block >> 13) ^ (block << (hash_shift - 12))) & (table->hash_size - 1); } static int insert_revoke_hash(journal_t *journal, unsigned long blocknr, tid_t seq) { struct list_head *hash_list; struct jbd_revoke_record_s *record; record = kmem_cache_alloc(revoke_record_cache, GFP_NOFS); if (!record) goto oom; record->sequence = seq; record->blocknr = blocknr; hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)]; list_add(&record->hash, hash_list); return 0; oom: return -ENOMEM; } /* Find a revoke record in the journal's hash table. */ static struct jbd_revoke_record_s *find_revoke_record(journal_t *journal, unsigned long blocknr) { struct list_head *hash_list; struct jbd_revoke_record_s *record; hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)]; record = (struct jbd_revoke_record_s *) hash_list->next; while (&(record->hash) != hash_list) { if (record->blocknr == blocknr) return record; record = (struct jbd_revoke_record_s *) record->hash.next; } return NULL; } int journal_init_revoke_caches(void) { revoke_record_cache = do_cache_create(sizeof(struct jbd_revoke_record_s)); if (revoke_record_cache == 0) return -ENOMEM; revoke_table_cache = do_cache_create(sizeof(struct jbd_revoke_table_s)); if (revoke_table_cache == 0) { do_cache_destroy(revoke_record_cache); revoke_record_cache = NULL; return -ENOMEM; } return 0; } void journal_destroy_revoke_caches(void) { do_cache_destroy(revoke_record_cache); revoke_record_cache = 0; do_cache_destroy(revoke_table_cache); revoke_table_cache = 0; } /* Initialise the revoke table for a given journal to a given size. */ int journal_init_revoke(journal_t *journal, int hash_size) { int shift, tmp; journal->j_revoke = kmem_cache_alloc(revoke_table_cache, GFP_KERNEL); if (!journal->j_revoke) return -ENOMEM; /* Check that the hash_size is a power of two */ journal->j_revoke->hash_size = hash_size; shift = 0; tmp = hash_size; while ((tmp >>= 1UL) != 0UL) shift++; journal->j_revoke->hash_shift = shift; journal->j_revoke->hash_table = xmalloc(hash_size * sizeof(struct list_head)); for (tmp = 0; tmp < hash_size; tmp++) INIT_LIST_HEAD(&journal->j_revoke->hash_table[tmp]); return 0; } /* Destoy a journal's revoke table. The table must already be empty! */ void journal_destroy_revoke(journal_t *journal) { struct jbd_revoke_table_s *table; struct list_head *hash_list; int i; table = journal->j_revoke; if (!table) return; for (i=0; ihash_size; i++) { hash_list = &table->hash_table[i]; } free(table->hash_table); free(table); journal->j_revoke = NULL; } /* * Revoke support for recovery. * * Recovery needs to be able to: * * record all revoke records, including the tid of the latest instance * of each revoke in the journal * * check whether a given block in a given transaction should be replayed * (ie. has not been revoked by a revoke record in that or a subsequent * transaction) * * empty the revoke table after recovery. */ /* * First, setting revoke records. We create a new revoke record for * every block ever revoked in the log as we scan it for recovery, and * we update the existing records if we find multiple revokes for a * single block. */ int journal_set_revoke(journal_t *journal, unsigned long blocknr, tid_t sequence) { struct jbd_revoke_record_s *record; record = find_revoke_record(journal, blocknr); if (record) { /* If we have multiple occurences, only record the * latest sequence number in the hashed record */ if (tid_gt(sequence, record->sequence)) record->sequence = sequence; return 0; } return insert_revoke_hash(journal, blocknr, sequence); } /* * Test revoke records. For a given block referenced in the log, has * that block been revoked? A revoke record with a given transaction * sequence number revokes all blocks in that transaction and earlier * ones, but later transactions still need replayed. */ int journal_test_revoke(journal_t *journal, unsigned long blocknr, tid_t sequence) { struct jbd_revoke_record_s *record; record = find_revoke_record(journal, blocknr); if (!record) return 0; if (tid_gt(sequence, record->sequence)) return 0; return 1; } /* * Finally, once recovery is over, we need to clear the revoke table so * that it can be reused by the running filesystem. */ void journal_clear_revoke(journal_t *journal) { int i; struct list_head *hash_list; struct jbd_revoke_record_s *record; struct jbd_revoke_table_s *revoke_var; revoke_var = journal->j_revoke; for (i = 0; i < revoke_var->hash_size; i++) { hash_list = &revoke_var->hash_table[i]; while (!list_empty(hash_list)) { record = (struct jbd_revoke_record_s*) hash_list->next; list_del(&record->hash); free(record); } } } /* * e2fsck.c - superblock checks */ #define MIN_CHECK 1 #define MAX_CHECK 2 static void check_super_value(e2fsck_t ctx, const char *descr, unsigned long value, int flags, unsigned long min_val, unsigned long max_val) { struct problem_context pctx; if (((flags & MIN_CHECK) && (value < min_val)) || ((flags & MAX_CHECK) && (value > max_val))) { clear_problem_context(&pctx); pctx.num = value; pctx.str = descr; fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* never get here! */ } } /* * This routine may get stubbed out in special compilations of the * e2fsck code.. */ #ifndef EXT2_SPECIAL_DEVICE_SIZE static errcode_t e2fsck_get_device_size(e2fsck_t ctx) { return (ext2fs_get_device_size(ctx->filesystem_name, EXT2_BLOCK_SIZE(ctx->fs->super), &ctx->num_blocks)); } #endif /* * helper function to release an inode */ struct process_block_struct { e2fsck_t ctx; char *buf; struct problem_context *pctx; int truncating; int truncate_offset; e2_blkcnt_t truncate_block; int truncated_blocks; int abort; errcode_t errcode; }; static int release_inode_block(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_blk FSCK_ATTR((unused)), int ref_offset FSCK_ATTR((unused)), void *priv_data) { struct process_block_struct *pb; e2fsck_t ctx; struct problem_context *pctx; blk_t blk = *block_nr; int retval = 0; pb = (struct process_block_struct *) priv_data; ctx = pb->ctx; pctx = pb->pctx; pctx->blk = blk; pctx->blkcount = blockcnt; if (HOLE_BLKADDR(blk)) return 0; if ((blk < fs->super->s_first_data_block) || (blk >= fs->super->s_blocks_count)) { fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx); return_abort: pb->abort = 1; return BLOCK_ABORT; } if (!ext2fs_test_block_bitmap(fs->block_map, blk)) { fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx); goto return_abort; } /* * If we are deleting an orphan, then we leave the fields alone. * If we are truncating an orphan, then update the inode fields * and clean up any partial block data. */ if (pb->truncating) { /* * We only remove indirect blocks if they are * completely empty. */ if (blockcnt < 0) { int i, limit; blk_t *bp; pb->errcode = io_channel_read_blk(fs->io, blk, 1, pb->buf); if (pb->errcode) goto return_abort; limit = fs->blocksize >> 2; for (i = 0, bp = (blk_t *) pb->buf; i < limit; i++, bp++) if (*bp) return 0; } /* * We don't remove direct blocks until we've reached * the truncation block. */ if (blockcnt >= 0 && blockcnt < pb->truncate_block) return 0; /* * If part of the last block needs truncating, we do * it here. */ if ((blockcnt == pb->truncate_block) && pb->truncate_offset) { pb->errcode = io_channel_read_blk(fs->io, blk, 1, pb->buf); if (pb->errcode) goto return_abort; memset(pb->buf + pb->truncate_offset, 0, fs->blocksize - pb->truncate_offset); pb->errcode = io_channel_write_blk(fs->io, blk, 1, pb->buf); if (pb->errcode) goto return_abort; } pb->truncated_blocks++; *block_nr = 0; retval |= BLOCK_CHANGED; } ext2fs_block_alloc_stats(fs, blk, -1); return retval; } /* * This function releases an inode. Returns 1 if an inconsistency was * found. If the inode has a link count, then it is being truncated and * not deleted. */ static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, struct ext2_inode *inode, char *block_buf, struct problem_context *pctx) { struct process_block_struct pb; ext2_filsys fs = ctx->fs; errcode_t retval; __u32 count; if (!ext2fs_inode_has_valid_blocks(inode)) return 0; pb.buf = block_buf + 3 * ctx->fs->blocksize; pb.ctx = ctx; pb.abort = 0; pb.errcode = 0; pb.pctx = pctx; if (inode->i_links_count) { pb.truncating = 1; pb.truncate_block = (e2_blkcnt_t) ((((long long)inode->i_size_high << 32) + inode->i_size + fs->blocksize - 1) / fs->blocksize); pb.truncate_offset = inode->i_size % fs->blocksize; } else { pb.truncating = 0; pb.truncate_block = 0; pb.truncate_offset = 0; } pb.truncated_blocks = 0; retval = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE, block_buf, release_inode_block, &pb); if (retval) { bb_error_msg(_("while calling ext2fs_block_iterate for inode %d"), ino); return 1; } if (pb.abort) return 1; /* Refresh the inode since ext2fs_block_iterate may have changed it */ e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks"); if (pb.truncated_blocks) inode->i_blocks -= pb.truncated_blocks * (fs->blocksize / 512); if (inode->i_file_acl) { retval = ext2fs_adjust_ea_refcount(fs, inode->i_file_acl, block_buf, -1, &count); if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) { retval = 0; count = 1; } if (retval) { bb_error_msg(_("while calling ext2fs_adjust_ea_refocunt for inode %d"), ino); return 1; } if (count == 0) ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1); inode->i_file_acl = 0; } return 0; } /* * This function releases all of the orphan inodes. It returns 1 if * it hit some error, and 0 on success. */ static int release_orphan_inodes(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; ext2_ino_t ino, next_ino; struct ext2_inode inode; struct problem_context pctx; char *block_buf; if ((ino = fs->super->s_last_orphan) == 0) return 0; /* * Win or lose, we won't be using the head of the orphan inode * list again. */ fs->super->s_last_orphan = 0; ext2fs_mark_super_dirty(fs); /* * If the filesystem contains errors, don't run the orphan * list, since the orphan list can't be trusted; and we're * going to be running a full e2fsck run anyway... */ if (fs->super->s_state & EXT2_ERROR_FS) return 0; if ((ino < EXT2_FIRST_INODE(fs->super)) || (ino > fs->super->s_inodes_count)) { clear_problem_context(&pctx); pctx.ino = ino; fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx); return 1; } block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4, "block iterate buffer"); e2fsck_read_bitmaps(ctx); while (ino) { e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes"); clear_problem_context(&pctx); pctx.ino = ino; pctx.inode = &inode; pctx.str = inode.i_links_count ? _("Truncating") : _("Clearing"); fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx); next_ino = inode.i_dtime; if (next_ino && ((next_ino < EXT2_FIRST_INODE(fs->super)) || (next_ino > fs->super->s_inodes_count))) { pctx.ino = next_ino; fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx); goto return_abort; } if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx)) goto return_abort; if (!inode.i_links_count) { ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode)); inode.i_dtime = time(NULL); } else { inode.i_dtime = 0; } e2fsck_write_inode(ctx, ino, &inode, "delete_file"); ino = next_ino; } ext2fs_free_mem(&block_buf); return 0; return_abort: ext2fs_free_mem(&block_buf); return 1; } /* * Check the resize inode to make sure it is sane. We check both for * the case where on-line resizing is not enabled (in which case the * resize inode should be cleared) as well as the case where on-line * resizing is enabled. */ static void check_resize_inode(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; struct ext2_inode inode; struct problem_context pctx; int i, j, gdt_off, ind_off; blk_t blk, pblk, expect; __u32 *dind_buf = NULL, *ind_buf; errcode_t retval; clear_problem_context(&pctx); /* * If the resize inode feature isn't set, then * s_reserved_gdt_blocks must be zero. */ if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INO)) { if (fs->super->s_reserved_gdt_blocks) { pctx.num = fs->super->s_reserved_gdt_blocks; if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS, &pctx)) { fs->super->s_reserved_gdt_blocks = 0; ext2fs_mark_super_dirty(fs); } } } /* Read the resize inode */ pctx.ino = EXT2_RESIZE_INO; retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode); if (retval) { if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INO) ctx->flags |= E2F_FLAG_RESIZE_INODE; return; } /* * If the resize inode feature isn't set, check to make sure * the resize inode is cleared; then we're done. */ if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INO)) { for (i=0; i < EXT2_N_BLOCKS; i++) { if (inode.i_block[i]) break; } if ((i < EXT2_N_BLOCKS) && fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) { memset(&inode, 0, sizeof(inode)); e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode, "clear_resize"); } return; } /* * The resize inode feature is enabled; check to make sure the * only block in use is the double indirect block */ blk = inode.i_block[EXT2_DIND_BLOCK]; for (i=0; i < EXT2_N_BLOCKS; i++) { if (i != EXT2_DIND_BLOCK && inode.i_block[i]) break; } if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count || !(inode.i_mode & LINUX_S_IFREG) || (blk < fs->super->s_first_data_block || blk >= fs->super->s_blocks_count)) { resize_inode_invalid: if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) { memset(&inode, 0, sizeof(inode)); e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode, "clear_resize"); ctx->flags |= E2F_FLAG_RESIZE_INODE; } if (!(ctx->options & E2F_OPT_READONLY)) { fs->super->s_state &= ~EXT2_VALID_FS; ext2fs_mark_super_dirty(fs); } goto cleanup; } dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2, "resize dind buffer"); ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize); retval = ext2fs_read_ind_block(fs, blk, dind_buf); if (retval) goto resize_inode_invalid; gdt_off = fs->desc_blocks; pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks; for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4; i++, gdt_off++, pblk++) { gdt_off %= fs->blocksize/4; if (dind_buf[gdt_off] != pblk) goto resize_inode_invalid; retval = ext2fs_read_ind_block(fs, pblk, ind_buf); if (retval) goto resize_inode_invalid; ind_off = 0; for (j = 1; j < fs->group_desc_count; j++) { if (!ext2fs_bg_has_super(fs, j)) continue; expect = pblk + (j * fs->super->s_blocks_per_group); if (ind_buf[ind_off] != expect) goto resize_inode_invalid; ind_off++; } } cleanup: ext2fs_free_mem(&dind_buf); } static void check_super_block(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; blk_t first_block, last_block; struct ext2_super_block *sb = fs->super; struct ext2_group_desc *gd; blk_t blocks_per_group = fs->super->s_blocks_per_group; blk_t bpg_max; int inodes_per_block; int ipg_max; int inode_size; dgrp_t i; blk_t should_be; struct problem_context pctx; __u32 free_blocks = 0, free_inodes = 0; inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super); ipg_max = inodes_per_block * (blocks_per_group - 4); if (ipg_max > EXT2_MAX_INODES_PER_GROUP(sb)) ipg_max = EXT2_MAX_INODES_PER_GROUP(sb); bpg_max = 8 * EXT2_BLOCK_SIZE(sb); if (bpg_max > EXT2_MAX_BLOCKS_PER_GROUP(sb)) bpg_max = EXT2_MAX_BLOCKS_PER_GROUP(sb); ctx->invalid_inode_bitmap_flag = (int *) e2fsck_allocate_memory(ctx, sizeof(int) * fs->group_desc_count, "invalid_inode_bitmap"); ctx->invalid_block_bitmap_flag = (int *) e2fsck_allocate_memory(ctx, sizeof(int) * fs->group_desc_count, "invalid_block_bitmap"); ctx->invalid_inode_table_flag = (int *) e2fsck_allocate_memory(ctx, sizeof(int) * fs->group_desc_count, "invalid_inode_table"); clear_problem_context(&pctx); /* * Verify the super block constants... */ check_super_value(ctx, "inodes_count", sb->s_inodes_count, MIN_CHECK, 1, 0); check_super_value(ctx, "blocks_count", sb->s_blocks_count, MIN_CHECK, 1, 0); check_super_value(ctx, "first_data_block", sb->s_first_data_block, MAX_CHECK, 0, sb->s_blocks_count); check_super_value(ctx, "log_block_size", sb->s_log_block_size, MIN_CHECK | MAX_CHECK, 0, EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE); check_super_value(ctx, "log_frag_size", sb->s_log_frag_size, MIN_CHECK | MAX_CHECK, 0, sb->s_log_block_size); check_super_value(ctx, "frags_per_group", sb->s_frags_per_group, MIN_CHECK | MAX_CHECK, sb->s_blocks_per_group, bpg_max); check_super_value(ctx, "blocks_per_group", sb->s_blocks_per_group, MIN_CHECK | MAX_CHECK, 8, bpg_max); check_super_value(ctx, "inodes_per_group", sb->s_inodes_per_group, MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max); check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count, MAX_CHECK, 0, sb->s_blocks_count / 2); check_super_value(ctx, "reserved_gdt_blocks", sb->s_reserved_gdt_blocks, MAX_CHECK, 0, fs->blocksize/4); inode_size = EXT2_INODE_SIZE(sb); check_super_value(ctx, "inode_size", inode_size, MIN_CHECK | MAX_CHECK, EXT2_GOOD_OLD_INODE_SIZE, fs->blocksize); if (inode_size & (inode_size - 1)) { pctx.num = inode_size; pctx.str = "inode_size"; fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* never get here! */ return; } if (!ctx->num_blocks) { pctx.errcode = e2fsck_get_device_size(ctx); if (pctx.errcode && pctx.errcode != EXT2_ET_UNIMPLEMENTED) { fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } if ((pctx.errcode != EXT2_ET_UNIMPLEMENTED) && (ctx->num_blocks < sb->s_blocks_count)) { pctx.blk = sb->s_blocks_count; pctx.blk2 = ctx->num_blocks; if (fix_problem(ctx, PR_0_FS_SIZE_WRONG, &pctx)) { ctx->flags |= E2F_FLAG_ABORT; return; } } } if (sb->s_log_block_size != (__u32) sb->s_log_frag_size) { pctx.blk = EXT2_BLOCK_SIZE(sb); pctx.blk2 = EXT2_FRAG_SIZE(sb); fix_problem(ctx, PR_0_NO_FRAGMENTS, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } should_be = sb->s_frags_per_group >> (sb->s_log_block_size - sb->s_log_frag_size); if (sb->s_blocks_per_group != should_be) { pctx.blk = sb->s_blocks_per_group; pctx.blk2 = should_be; fix_problem(ctx, PR_0_BLOCKS_PER_GROUP, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } should_be = (sb->s_log_block_size == 0) ? 1 : 0; if (sb->s_first_data_block != should_be) { pctx.blk = sb->s_first_data_block; pctx.blk2 = should_be; fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } should_be = sb->s_inodes_per_group * fs->group_desc_count; if (sb->s_inodes_count != should_be) { pctx.ino = sb->s_inodes_count; pctx.ino2 = should_be; if (fix_problem(ctx, PR_0_INODE_COUNT_WRONG, &pctx)) { sb->s_inodes_count = should_be; ext2fs_mark_super_dirty(fs); } } /* * Verify the group descriptors.... */ first_block = sb->s_first_data_block; last_block = first_block + blocks_per_group; for (i = 0, gd=fs->group_desc; i < fs->group_desc_count; i++, gd++) { pctx.group = i; if (i == fs->group_desc_count - 1) last_block = sb->s_blocks_count; if ((gd->bg_block_bitmap < first_block) || (gd->bg_block_bitmap >= last_block)) { pctx.blk = gd->bg_block_bitmap; if (fix_problem(ctx, PR_0_BB_NOT_GROUP, &pctx)) gd->bg_block_bitmap = 0; } if (gd->bg_block_bitmap == 0) { ctx->invalid_block_bitmap_flag[i]++; ctx->invalid_bitmaps++; } if ((gd->bg_inode_bitmap < first_block) || (gd->bg_inode_bitmap >= last_block)) { pctx.blk = gd->bg_inode_bitmap; if (fix_problem(ctx, PR_0_IB_NOT_GROUP, &pctx)) gd->bg_inode_bitmap = 0; } if (gd->bg_inode_bitmap == 0) { ctx->invalid_inode_bitmap_flag[i]++; ctx->invalid_bitmaps++; } if ((gd->bg_inode_table < first_block) || ((gd->bg_inode_table + fs->inode_blocks_per_group - 1) >= last_block)) { pctx.blk = gd->bg_inode_table; if (fix_problem(ctx, PR_0_ITABLE_NOT_GROUP, &pctx)) gd->bg_inode_table = 0; } if (gd->bg_inode_table == 0) { ctx->invalid_inode_table_flag[i]++; ctx->invalid_bitmaps++; } free_blocks += gd->bg_free_blocks_count; free_inodes += gd->bg_free_inodes_count; first_block += sb->s_blocks_per_group; last_block += sb->s_blocks_per_group; if ((gd->bg_free_blocks_count > sb->s_blocks_per_group) || (gd->bg_free_inodes_count > sb->s_inodes_per_group) || (gd->bg_used_dirs_count > sb->s_inodes_per_group)) ext2fs_unmark_valid(fs); } /* * Update the global counts from the block group counts. This * is needed for an experimental patch which eliminates * locking the entire filesystem when allocating blocks or * inodes; if the filesystem is not unmounted cleanly, the * global counts may not be accurate. */ if ((free_blocks != sb->s_free_blocks_count) || (free_inodes != sb->s_free_inodes_count)) { if (ctx->options & E2F_OPT_READONLY) ext2fs_unmark_valid(fs); else { sb->s_free_blocks_count = free_blocks; sb->s_free_inodes_count = free_inodes; ext2fs_mark_super_dirty(fs); } } if ((sb->s_free_blocks_count > sb->s_blocks_count) || (sb->s_free_inodes_count > sb->s_inodes_count)) ext2fs_unmark_valid(fs); /* * If we have invalid bitmaps, set the error state of the * filesystem. */ if (ctx->invalid_bitmaps && !(ctx->options & E2F_OPT_READONLY)) { sb->s_state &= ~EXT2_VALID_FS; ext2fs_mark_super_dirty(fs); } clear_problem_context(&pctx); /* * If the UUID field isn't assigned, assign it. */ if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) { if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) { uuid_generate(sb->s_uuid); ext2fs_mark_super_dirty(fs); fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; } } /* FIXME - HURD support? * For the Hurd, check to see if the filetype option is set, * since it doesn't support it. */ if (!(ctx->options & E2F_OPT_READONLY) && fs->super->s_creator_os == EXT2_OS_HURD && (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)) { if (fix_problem(ctx, PR_0_HURD_CLEAR_FILETYPE, &pctx)) { fs->super->s_feature_incompat &= ~EXT2_FEATURE_INCOMPAT_FILETYPE; ext2fs_mark_super_dirty(fs); } } /* * If we have any of the compatibility flags set, we need to have a * revision 1 filesystem. Most kernels will not check the flags on * a rev 0 filesystem and we may have corruption issues because of * the incompatible changes to the filesystem. */ if (!(ctx->options & E2F_OPT_READONLY) && fs->super->s_rev_level == EXT2_GOOD_OLD_REV && (fs->super->s_feature_compat || fs->super->s_feature_ro_compat || fs->super->s_feature_incompat) && fix_problem(ctx, PR_0_FS_REV_LEVEL, &pctx)) { ext2fs_update_dynamic_rev(fs); ext2fs_mark_super_dirty(fs); } check_resize_inode(ctx); /* * Clean up any orphan inodes, if present. */ if (!(ctx->options & E2F_OPT_READONLY) && release_orphan_inodes(ctx)) { fs->super->s_state &= ~EXT2_VALID_FS; ext2fs_mark_super_dirty(fs); } /* * Move the ext3 journal file, if necessary. */ e2fsck_move_ext3_journal(ctx); } /* * swapfs.c --- byte-swap an ext2 filesystem */ #ifdef ENABLE_SWAPFS struct swap_block_struct { ext2_ino_t ino; int isdir; errcode_t errcode; char *dir_buf; struct ext2_inode *inode; }; /* * This is a helper function for block_iterate. We mark all of the * indirect and direct blocks as changed, so that block_iterate will * write them out. */ static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt, void *priv_data) { errcode_t retval; struct swap_block_struct *sb = (struct swap_block_struct *) priv_data; if (sb->isdir && (blockcnt >= 0) && *block_nr) { retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf); if (retval) { sb->errcode = retval; return BLOCK_ABORT; } retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf); if (retval) { sb->errcode = retval; return BLOCK_ABORT; } } if (blockcnt >= 0) { if (blockcnt < EXT2_NDIR_BLOCKS) return 0; return BLOCK_CHANGED; } if (blockcnt == BLOCK_COUNT_IND) { if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK]) return 0; return BLOCK_CHANGED; } if (blockcnt == BLOCK_COUNT_DIND) { if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK]) return 0; return BLOCK_CHANGED; } if (blockcnt == BLOCK_COUNT_TIND) { if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK]) return 0; return BLOCK_CHANGED; } return BLOCK_CHANGED; } /* * This function is responsible for byte-swapping all of the indirect, * block pointers. It is also responsible for byte-swapping directories. */ static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf, struct ext2_inode *inode) { errcode_t retval; struct swap_block_struct sb; sb.ino = ino; sb.inode = inode; sb.dir_buf = block_buf + ctx->fs->blocksize*3; sb.errcode = 0; sb.isdir = 0; if (LINUX_S_ISDIR(inode->i_mode)) sb.isdir = 1; retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf, swap_block, &sb); if (retval) { bb_error_msg(_("while calling ext2fs_block_iterate")); ctx->flags |= E2F_FLAG_ABORT; return; } if (sb.errcode) { bb_error_msg(_("while calling iterator function")); ctx->flags |= E2F_FLAG_ABORT; return; } } static void swap_inodes(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; dgrp_t group; unsigned int i; ext2_ino_t ino = 1; char *buf, *block_buf; errcode_t retval; struct ext2_inode * inode; e2fsck_use_inode_shortcuts(ctx, 1); retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group, &buf); if (retval) { bb_error_msg(_("while allocating inode buffer")); ctx->flags |= E2F_FLAG_ABORT; return; } block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4, "block interate buffer"); for (group = 0; group < fs->group_desc_count; group++) { retval = io_channel_read_blk(fs->io, fs->group_desc[group].bg_inode_table, fs->inode_blocks_per_group, buf); if (retval) { bb_error_msg(_("while reading inode table (group %d)"), group); ctx->flags |= E2F_FLAG_ABORT; return; } inode = (struct ext2_inode *) buf; for (i=0; i < fs->super->s_inodes_per_group; i++, ino++, inode++) { ctx->stashed_ino = ino; ctx->stashed_inode = inode; if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ) ext2fs_swap_inode(fs, inode, inode, 0); /* * Skip deleted files. */ if (inode->i_links_count == 0) continue; if (LINUX_S_ISDIR(inode->i_mode) || ((inode->i_block[EXT2_IND_BLOCK] || inode->i_block[EXT2_DIND_BLOCK] || inode->i_block[EXT2_TIND_BLOCK]) && ext2fs_inode_has_valid_blocks(inode))) swap_inode_blocks(ctx, ino, block_buf, inode); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE) ext2fs_swap_inode(fs, inode, inode, 1); } retval = io_channel_write_blk(fs->io, fs->group_desc[group].bg_inode_table, fs->inode_blocks_per_group, buf); if (retval) { bb_error_msg(_("while writing inode table (group %d)"), group); ctx->flags |= E2F_FLAG_ABORT; return; } } ext2fs_free_mem(&buf); ext2fs_free_mem(&block_buf); e2fsck_use_inode_shortcuts(ctx, 0); ext2fs_flush_icache(fs); } #if defined(__powerpc__) && BB_BIG_ENDIAN /* * On the PowerPC, the big-endian variant of the ext2 filesystem * has its bitmaps stored as 32-bit words with bit 0 as the LSB * of each word. Thus a bitmap with only bit 0 set would be, as * a string of bytes, 00 00 00 01 00 ... * To cope with this, we byte-reverse each word of a bitmap if * we have a big-endian filesystem, that is, if we are *not* * byte-swapping other word-sized numbers. */ #define EXT2_BIG_ENDIAN_BITMAPS #endif #ifdef EXT2_BIG_ENDIAN_BITMAPS static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap) { __u32 *p = (__u32 *) bmap->bitmap; int n, nbytes = (bmap->end - bmap->start + 7) / 8; for (n = nbytes / sizeof(__u32); n > 0; --n, ++p) *p = ext2fs_swab32(*p); } #endif #ifdef ENABLE_SWAPFS static void swap_filesys(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; if (!(ctx->options & E2F_OPT_PREEN)) printf(_("Pass 0: Doing byte-swap of filesystem\n")); /* Byte swap */ if (fs->super->s_mnt_count) { fprintf(stderr, _("%s: the filesystem must be freshly " "checked using fsck\n" "and not mounted before trying to " "byte-swap it.\n"), ctx->device_name); ctx->flags |= E2F_FLAG_ABORT; return; } if (fs->flags & EXT2_FLAG_SWAP_BYTES) { fs->flags &= ~(EXT2_FLAG_SWAP_BYTES| EXT2_FLAG_SWAP_BYTES_WRITE); fs->flags |= EXT2_FLAG_SWAP_BYTES_READ; } else { fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ; fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE; } swap_inodes(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE) fs->flags |= EXT2_FLAG_SWAP_BYTES; fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ| EXT2_FLAG_SWAP_BYTES_WRITE); #ifdef EXT2_BIG_ENDIAN_BITMAPS e2fsck_read_bitmaps(ctx); ext2fs_swap_bitmap(fs->inode_map); ext2fs_swap_bitmap(fs->block_map); fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY; #endif fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; ext2fs_flush(fs); fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; } #endif /* ENABLE_SWAPFS */ #endif /* * util.c --- miscellaneous utilities */ void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size, const char *description) { return xzalloc(size); } static char *string_copy(const char *str, int len) { char *ret; if (!str) return NULL; if (!len) len = strlen(str); ret = xmalloc(len+1); strncpy(ret, str, len); ret[len] = 0; return ret; } #ifndef HAVE_CONIO_H static int read_a_char(void) { char c; int r; int fail = 0; while (1) { if (e2fsck_global_ctx && (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) { return 3; } r = read(0, &c, 1); if (r == 1) return c; if (fail++ > 100) break; } return EOF; } #endif static int ask_yn(const char * string, int def) { int c; const char *defstr; static const char short_yes[] = "yY"; static const char short_no[] = "nN"; #ifdef HAVE_TERMIOS_H struct termios termios, tmp; tcgetattr (0, &termios); tmp = termios; tmp.c_lflag &= ~(ICANON | ECHO); tmp.c_cc[VMIN] = 1; tmp.c_cc[VTIME] = 0; tcsetattr_stdin_TCSANOW(&tmp); #endif if (def == 1) defstr = ""; else if (def == 0) defstr = ""; else defstr = " (y/n)"; printf("%s%s? ", string, defstr); while (1) { fflush (stdout); if ((c = read_a_char()) == EOF) break; if (c == 3) { #ifdef HAVE_TERMIOS_H tcsetattr_stdin_TCSANOW(&termios); #endif if (e2fsck_global_ctx && e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) { puts("\n"); longjmp(e2fsck_global_ctx->abort_loc, 1); } puts(_("cancelled!\n")); return 0; } if (strchr(short_yes, (char) c)) { def = 1; break; } else if (strchr(short_no, (char) c)) { def = 0; break; } else if ((c == ' ' || c == '\n') && (def != -1)) break; } if (def) puts("yes\n"); else puts ("no\n"); #ifdef HAVE_TERMIOS_H tcsetattr_stdin_TCSANOW(&termios); #endif return def; } int ask (e2fsck_t ctx, const char * string, int def) { if (ctx->options & E2F_OPT_NO) { printf(_("%s? no\n\n"), string); return 0; } if (ctx->options & E2F_OPT_YES) { printf(_("%s? yes\n\n"), string); return 1; } if (ctx->options & E2F_OPT_PREEN) { printf("%s? %s\n\n", string, def ? _("yes") : _("no")); return def; } return ask_yn(string, def); } void e2fsck_read_bitmaps(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; errcode_t retval; if (ctx->invalid_bitmaps) { bb_error_msg(_("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"), ctx->device_name); bb_error_msg_and_die(0); } ehandler_operation(_("reading inode and block bitmaps")); retval = ext2fs_read_bitmaps(fs); ehandler_operation(0); if (retval) { bb_error_msg(_("while retrying to read bitmaps for %s"), ctx->device_name); bb_error_msg_and_die(0); } } static void e2fsck_write_bitmaps(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; errcode_t retval; if (ext2fs_test_bb_dirty(fs)) { ehandler_operation(_("writing block bitmaps")); retval = ext2fs_write_block_bitmap(fs); ehandler_operation(0); if (retval) { bb_error_msg(_("while retrying to write block bitmaps for %s"), ctx->device_name); bb_error_msg_and_die(0); } } if (ext2fs_test_ib_dirty(fs)) { ehandler_operation(_("writing inode bitmaps")); retval = ext2fs_write_inode_bitmap(fs); ehandler_operation(0); if (retval) { bb_error_msg(_("while retrying to write inode bitmaps for %s"), ctx->device_name); bb_error_msg_and_die(0); } } } void preenhalt(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; if (!(ctx->options & E2F_OPT_PREEN)) return; fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; " "RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"), ctx->device_name); if (fs != NULL) { fs->super->s_state |= EXT2_ERROR_FS; ext2fs_mark_super_dirty(fs); ext2fs_close(fs); } exit(EXIT_UNCORRECTED); } void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino, struct ext2_inode * inode, const char *proc) { int retval; retval = ext2fs_read_inode(ctx->fs, ino, inode); if (retval) { bb_error_msg(_("while reading inode %ld in %s"), ino, proc); bb_error_msg_and_die(0); } } extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino, struct ext2_inode * inode, int bufsize, const char *proc) { int retval; retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize); if (retval) { bb_error_msg(_("while writing inode %ld in %s"), ino, proc); bb_error_msg_and_die(0); } } extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino, struct ext2_inode * inode, const char *proc) { int retval; retval = ext2fs_write_inode(ctx->fs, ino, inode); if (retval) { bb_error_msg(_("while writing inode %ld in %s"), ino, proc); bb_error_msg_and_die(0); } } blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name, io_manager manager) { struct ext2_super_block *sb; io_channel io = NULL; void *buf = NULL; int blocksize; blk_t superblock, ret_sb = 8193; if (fs && fs->super) { ret_sb = (fs->super->s_blocks_per_group + fs->super->s_first_data_block); if (ctx) { ctx->superblock = ret_sb; ctx->blocksize = fs->blocksize; } return ret_sb; } if (ctx) { if (ctx->blocksize) { ret_sb = ctx->blocksize * 8; if (ctx->blocksize == 1024) ret_sb++; ctx->superblock = ret_sb; return ret_sb; } ctx->superblock = ret_sb; ctx->blocksize = 1024; } if (!name || !manager) goto cleanup; if (manager->open(name, 0, &io) != 0) goto cleanup; if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf)) goto cleanup; sb = (struct ext2_super_block *) buf; for (blocksize = EXT2_MIN_BLOCK_SIZE; blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) { superblock = blocksize*8; if (blocksize == 1024) superblock++; io_channel_set_blksize(io, blocksize); if (io_channel_read_blk(io, superblock, -SUPERBLOCK_SIZE, buf)) continue; #if BB_BIG_ENDIAN if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ext2fs_swap_super(sb); #endif if (sb->s_magic == EXT2_SUPER_MAGIC) { ret_sb = superblock; if (ctx) { ctx->superblock = superblock; ctx->blocksize = blocksize; } break; } } cleanup: if (io) io_channel_close(io); ext2fs_free_mem(&buf); return ret_sb; } /* * This function runs through the e2fsck passes and calls them all, * returning restart, abort, or cancel as necessary... */ typedef void (*pass_t)(e2fsck_t ctx); static const pass_t e2fsck_passes[] = { e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4, e2fsck_pass5, 0 }; #define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART) static int e2fsck_run(e2fsck_t ctx) { int i; pass_t e2fsck_pass; if (setjmp(ctx->abort_loc)) { ctx->flags &= ~E2F_FLAG_SETJMP_OK; return (ctx->flags & E2F_FLAG_RUN_RETURN); } ctx->flags |= E2F_FLAG_SETJMP_OK; for (i=0; (e2fsck_pass = e2fsck_passes[i]); i++) { if (ctx->flags & E2F_FLAG_RUN_RETURN) break; e2fsck_pass(ctx); if (ctx->progress) (void) (ctx->progress)(ctx, 0, 0, 0); } ctx->flags &= ~E2F_FLAG_SETJMP_OK; if (ctx->flags & E2F_FLAG_RUN_RETURN) return (ctx->flags & E2F_FLAG_RUN_RETURN); return 0; } /* * unix.c - The unix-specific code for e2fsck */ /* Command line options */ static int swapfs; #ifdef ENABLE_SWAPFS static int normalize_swapfs; #endif static int cflag; /* check disk */ static int show_version_only; static int verbose; #define P_E2(singular, plural, n) n, ((n) == 1 ? singular : plural) static void show_stats(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; int inodes, inodes_used, blocks, blocks_used; int dir_links; int num_files, num_links; int frag_percent; dir_links = 2 * ctx->fs_directory_count - 1; num_files = ctx->fs_total_count - dir_links; num_links = ctx->fs_links_count - dir_links; inodes = fs->super->s_inodes_count; inodes_used = (fs->super->s_inodes_count - fs->super->s_free_inodes_count); blocks = fs->super->s_blocks_count; blocks_used = (fs->super->s_blocks_count - fs->super->s_free_blocks_count); frag_percent = (10000 * ctx->fs_fragmented) / inodes_used; frag_percent = (frag_percent + 5) / 10; if (!verbose) { printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n", ctx->device_name, inodes_used, inodes, frag_percent / 10, frag_percent % 10, blocks_used, blocks); return; } printf("\n%8d inode%s used (%d%%)\n", P_E2("", "s", inodes_used), 100 * inodes_used / inodes); printf("%8d non-contiguous inode%s (%0d.%d%%)\n", P_E2("", "s", ctx->fs_fragmented), frag_percent / 10, frag_percent % 10); printf(_(" # of inodes with ind/dind/tind blocks: %d/%d/%d\n"), ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count); printf("%8d block%s used (%d%%)\n", P_E2("", "s", blocks_used), (int) ((long long) 100 * blocks_used / blocks)); printf("%8d large file%s\n", P_E2("", "s", ctx->large_files)); printf("\n%8d regular file%s\n", P_E2("", "s", ctx->fs_regular_count)); printf("%8d director%s\n", P_E2("y", "ies", ctx->fs_directory_count)); printf("%8d character device file%s\n", P_E2("", "s", ctx->fs_chardev_count)); printf("%8d block device file%s\n", P_E2("", "s", ctx->fs_blockdev_count)); printf("%8d fifo%s\n", P_E2("", "s", ctx->fs_fifo_count)); printf("%8d link%s\n", P_E2("", "s", ctx->fs_links_count - dir_links)); printf("%8d symbolic link%s", P_E2("", "s", ctx->fs_symlinks_count)); printf(" (%d fast symbolic link%s)\n", P_E2("", "s", ctx->fs_fast_symlinks_count)); printf("%8d socket%s--------\n\n", P_E2("", "s", ctx->fs_sockets_count)); printf("%8d file%s\n", P_E2("", "s", ctx->fs_total_count - dir_links)); } static void check_mount(e2fsck_t ctx) { errcode_t retval; int cont; retval = ext2fs_check_if_mounted(ctx->filesystem_name, &ctx->mount_flags); if (retval) { bb_error_msg(_("while determining whether %s is mounted"), ctx->filesystem_name); return; } /* * If the filesystem isn't mounted, or it's the root filesystem * and it's mounted read-only, then everything's fine. */ if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) || ((ctx->mount_flags & EXT2_MF_ISROOT) && (ctx->mount_flags & EXT2_MF_READONLY))) return; if (ctx->options & E2F_OPT_READONLY) { printf(_("Warning! %s is mounted.\n"), ctx->filesystem_name); return; } printf(_("%s is mounted. "), ctx->filesystem_name); if (!ctx->interactive) bb_error_msg_and_die(_("can't continue, aborting")); printf(_("\n\n\007\007\007\007WARNING!!! " "Running e2fsck on a mounted filesystem may cause\n" "SEVERE filesystem damage.\007\007\007\n\n")); cont = ask_yn(_("Do you really want to continue"), -1); if (!cont) { printf(_("check aborted.\n")); exit(0); } } static int is_on_batt(void) { FILE *f; DIR *d; char tmp[80], tmp2[80], fname[80]; unsigned int acflag; struct dirent* de; f = fopen_for_read("/proc/apm"); if (f) { if (fscanf(f, "%s %s %s %x", tmp, tmp, tmp, &acflag) != 4) acflag = 1; fclose(f); return (acflag != 1); } d = opendir("/proc/acpi/ac_adapter"); if (d) { while ((de=readdir(d)) != NULL) { if (!strncmp(".", de->d_name, 1)) continue; snprintf(fname, 80, "/proc/acpi/ac_adapter/%s/state", de->d_name); f = fopen_for_read(fname); if (!f) continue; if (fscanf(f, "%s %s", tmp2, tmp) != 2) tmp[0] = 0; fclose(f); if (strncmp(tmp, "off-line", 8) == 0) { closedir(d); return 1; } } closedir(d); } return 0; } /* * This routine checks to see if a filesystem can be skipped; if so, * it will exit with EXIT_OK. Under some conditions it will print a * message explaining why a check is being forced. */ static void check_if_skip(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; const char *reason = NULL; unsigned int reason_arg = 0; long next_check; int batt = is_on_batt(); time_t now = time(NULL); if ((ctx->options & E2F_OPT_FORCE) || cflag || swapfs) return; if ((fs->super->s_state & EXT2_ERROR_FS) || !ext2fs_test_valid(fs)) reason = _(" contains a file system with errors"); else if ((fs->super->s_state & EXT2_VALID_FS) == 0) reason = _(" was not cleanly unmounted"); else if ((fs->super->s_max_mnt_count > 0) && (fs->super->s_mnt_count >= (unsigned) fs->super->s_max_mnt_count)) { reason = _(" has been mounted %u times without being checked"); reason_arg = fs->super->s_mnt_count; if (batt && (fs->super->s_mnt_count < (unsigned) fs->super->s_max_mnt_count*2)) reason = 0; } else if (fs->super->s_checkinterval && ((now - fs->super->s_lastcheck) >= fs->super->s_checkinterval)) { reason = _(" has gone %u days without being checked"); reason_arg = (now - fs->super->s_lastcheck)/(3600*24); if (batt && ((now - fs->super->s_lastcheck) < fs->super->s_checkinterval*2)) reason = 0; } if (reason) { fputs(ctx->device_name, stdout); printf(reason, reason_arg); fputs(_(", check forced.\n"), stdout); return; } printf(_("%s: clean, %d/%d files, %d/%d blocks"), ctx->device_name, fs->super->s_inodes_count - fs->super->s_free_inodes_count, fs->super->s_inodes_count, fs->super->s_blocks_count - fs->super->s_free_blocks_count, fs->super->s_blocks_count); next_check = 100000; if (fs->super->s_max_mnt_count > 0) { next_check = fs->super->s_max_mnt_count - fs->super->s_mnt_count; if (next_check <= 0) next_check = 1; } if (fs->super->s_checkinterval && ((now - fs->super->s_lastcheck) >= fs->super->s_checkinterval)) next_check = 1; if (next_check <= 5) { if (next_check == 1) fputs(_(" (check after next mount)"), stdout); else printf(_(" (check in %ld mounts)"), next_check); } bb_putchar('\n'); ext2fs_close(fs); ctx->fs = NULL; e2fsck_free_context(ctx); exit(EXIT_OK); } /* * For completion notice */ struct percent_tbl { int max_pass; int table[32]; }; static const struct percent_tbl e2fsck_tbl = { 5, { 0, 70, 90, 92, 95, 100 } }; static char bar[128], spaces[128]; static float calc_percent(const struct percent_tbl *tbl, int pass, int curr, int max) { float percent; if (pass <= 0) return 0.0; if (pass > tbl->max_pass || max == 0) return 100.0; percent = ((float) curr) / ((float) max); return ((percent * (tbl->table[pass] - tbl->table[pass-1])) + tbl->table[pass-1]); } void e2fsck_clear_progbar(e2fsck_t ctx) { if (!(ctx->flags & E2F_FLAG_PROG_BAR)) return; printf("%s%s\r%s", ctx->start_meta, spaces + (sizeof(spaces) - 80), ctx->stop_meta); fflush(stdout); ctx->flags &= ~E2F_FLAG_PROG_BAR; } int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent, unsigned int dpynum) { static const char spinner[] = "\\|/-"; int i; unsigned int tick; struct timeval tv; int dpywidth; int fixed_percent; if (ctx->flags & E2F_FLAG_PROG_SUPPRESS) return 0; /* * Calculate the new progress position. If the * percentage hasn't changed, then we skip out right * away. */ fixed_percent = (int) ((10 * percent) + 0.5); if (ctx->progress_last_percent == fixed_percent) return 0; ctx->progress_last_percent = fixed_percent; /* * If we've already updated the spinner once within * the last 1/8th of a second, no point doing it * again. */ gettimeofday(&tv, NULL); tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8)); if ((tick == ctx->progress_last_time) && (fixed_percent != 0) && (fixed_percent != 1000)) return 0; ctx->progress_last_time = tick; /* * Advance the spinner, and note that the progress bar * will be on the screen */ ctx->progress_pos = (ctx->progress_pos+1) & 3; ctx->flags |= E2F_FLAG_PROG_BAR; dpywidth = 66 - strlen(label); dpywidth = 8 * (dpywidth / 8); if (dpynum) dpywidth -= 8; i = ((percent * dpywidth) + 50) / 100; printf("%s%s: |%s%s", ctx->start_meta, label, bar + (sizeof(bar) - (i+1)), spaces + (sizeof(spaces) - (dpywidth - i + 1))); if (fixed_percent == 1000) bb_putchar('|'); else bb_putchar(spinner[ctx->progress_pos & 3]); printf(" %4.1f%% ", percent); if (dpynum) printf("%u\r", dpynum); else fputs(" \r", stdout); fputs(ctx->stop_meta, stdout); if (fixed_percent == 1000) e2fsck_clear_progbar(ctx); fflush(stdout); return 0; } static int e2fsck_update_progress(e2fsck_t ctx, int pass, unsigned long cur, unsigned long max) { char buf[80]; float percent; if (pass == 0) return 0; if (ctx->progress_fd) { sprintf(buf, "%d %lu %lu\n", pass, cur, max); xwrite_str(ctx->progress_fd, buf); } else { percent = calc_percent(&e2fsck_tbl, pass, cur, max); e2fsck_simple_progress(ctx, ctx->device_name, percent, 0); } return 0; } static void reserve_stdio_fds(void) { int fd; while (1) { fd = open(bb_dev_null, O_RDWR); if (fd > 2) break; if (fd < 0) { fprintf(stderr, _("ERROR: Cannot open " "/dev/null (%s)\n"), strerror(errno)); break; } } close(fd); } static void signal_progress_on(int sig FSCK_ATTR((unused))) { e2fsck_t ctx = e2fsck_global_ctx; if (!ctx) return; ctx->progress = e2fsck_update_progress; ctx->progress_fd = 0; } static void signal_progress_off(int sig FSCK_ATTR((unused))) { e2fsck_t ctx = e2fsck_global_ctx; if (!ctx) return; e2fsck_clear_progbar(ctx); ctx->progress = 0; } static void signal_cancel(int sig FSCK_ATTR((unused))) { e2fsck_t ctx = e2fsck_global_ctx; if (!ctx) exit(FSCK_CANCELED); ctx->flags |= E2F_FLAG_CANCEL; } static void parse_extended_opts(e2fsck_t ctx, const char *opts) { char *buf, *token, *next, *p, *arg; int ea_ver; int extended_usage = 0; buf = string_copy(opts, 0); for (token = buf; token && *token; token = next) { p = strchr(token, ','); next = 0; if (p) { *p = 0; next = p+1; } arg = strchr(token, '='); if (arg) { *arg = 0; arg++; } if (strcmp(token, "ea_ver") == 0) { if (!arg) { extended_usage++; continue; } ea_ver = strtoul(arg, &p, 0); if (*p || ((ea_ver != 1) && (ea_ver != 2))) { fprintf(stderr, _("Invalid EA version.\n")); extended_usage++; continue; } ctx->ext_attr_ver = ea_ver; } else { fprintf(stderr, _("Unknown extended option: %s\n"), token); extended_usage++; } } if (extended_usage) { bb_error_msg_and_die( "Extended options are separated by commas, " "and may take an argument which\n" "is set off by an equals ('=') sign. " "Valid extended options are:\n" "\tea_ver=\n\n"); } } static errcode_t PRS(int argc, char **argv, e2fsck_t *ret_ctx) { int flush = 0; int c, fd; e2fsck_t ctx; errcode_t retval; struct sigaction sa; char *extended_opts = NULL; retval = e2fsck_allocate_context(&ctx); if (retval) return retval; *ret_ctx = ctx; setvbuf(stdout, NULL, _IONBF, BUFSIZ); setvbuf(stderr, NULL, _IONBF, BUFSIZ); if (isatty(0) && isatty(1)) { ctx->interactive = 1; } else { ctx->start_meta[0] = '\001'; ctx->stop_meta[0] = '\002'; } memset(bar, '=', sizeof(bar)-1); memset(spaces, ' ', sizeof(spaces)-1); blkid_get_cache(&ctx->blkid, NULL); if (argc && *argv) ctx->program_name = *argv; else ctx->program_name = "e2fsck"; while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF) switch (c) { case 'C': ctx->progress = e2fsck_update_progress; ctx->progress_fd = atoi(optarg); if (!ctx->progress_fd) break; /* Validate the file descriptor to avoid disasters */ fd = dup(ctx->progress_fd); if (fd < 0) { fprintf(stderr, _("Error validating file descriptor %d: %s\n"), ctx->progress_fd, error_message(errno)); bb_error_msg_and_die(_("Invalid completion information file descriptor")); } else close(fd); break; case 'D': ctx->options |= E2F_OPT_COMPRESS_DIRS; break; case 'E': extended_opts = optarg; break; case 'p': case 'a': if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) { conflict_opt: bb_error_msg_and_die(_("only one the options -p/-a, -n or -y may be specified")); } ctx->options |= E2F_OPT_PREEN; break; case 'n': if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN)) goto conflict_opt; ctx->options |= E2F_OPT_NO; break; case 'y': if (ctx->options & (E2F_OPT_PREEN|E2F_OPT_NO)) goto conflict_opt; ctx->options |= E2F_OPT_YES; break; case 't': /* FIXME - This needs to go away in a future path - will change binary */ fprintf(stderr, _("The -t option is not " "supported on this version of e2fsck.\n")); break; case 'c': if (cflag++) ctx->options |= E2F_OPT_WRITECHECK; ctx->options |= E2F_OPT_CHECKBLOCKS; break; case 'r': /* What we do by default, anyway! */ break; case 'b': ctx->use_superblock = atoi(optarg); ctx->flags |= E2F_FLAG_SB_SPECIFIED; break; case 'B': ctx->blocksize = atoi(optarg); break; case 'I': ctx->inode_buffer_blocks = atoi(optarg); break; case 'j': ctx->journal_name = string_copy(optarg, 0); break; case 'P': ctx->process_inode_size = atoi(optarg); break; case 'd': ctx->options |= E2F_OPT_DEBUG; break; case 'f': ctx->options |= E2F_OPT_FORCE; break; case 'F': flush = 1; break; case 'v': verbose = 1; break; case 'V': show_version_only = 1; break; case 'N': ctx->device_name = optarg; break; #ifdef ENABLE_SWAPFS case 's': normalize_swapfs = 1; case 'S': swapfs = 1; break; #else case 's': case 'S': fprintf(stderr, _("Byte-swapping filesystems " "not compiled in this version " "of e2fsck\n")); exit(1); #endif default: bb_show_usage(); } if (show_version_only) return 0; if (optind != argc - 1) bb_show_usage(); if ((ctx->options & E2F_OPT_NO) && !cflag && !swapfs && !(ctx->options & E2F_OPT_COMPRESS_DIRS)) ctx->options |= E2F_OPT_READONLY; ctx->io_options = strchr(argv[optind], '?'); if (ctx->io_options) *ctx->io_options++ = 0; ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0); if (!ctx->filesystem_name) { bb_error_msg(_("Unable to resolve '%s'"), argv[optind]); bb_error_msg_and_die(0); } if (extended_opts) parse_extended_opts(ctx, extended_opts); if (flush) { fd = open(ctx->filesystem_name, O_RDONLY, 0); if (fd < 0) { bb_error_msg(_("while opening %s for flushing"), ctx->filesystem_name); bb_error_msg_and_die(0); } if ((retval = ext2fs_sync_device(fd, 1))) { bb_error_msg(_("while trying to flush %s"), ctx->filesystem_name); bb_error_msg_and_die(0); } close(fd); } #ifdef ENABLE_SWAPFS if (swapfs && cflag) { fprintf(stderr, _("Incompatible options not " "allowed when byte-swapping.\n")); exit(EXIT_USAGE); } #endif /* * Set up signal action */ memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = signal_cancel; sigaction(SIGINT, &sa, 0); sigaction(SIGTERM, &sa, 0); #ifdef SA_RESTART sa.sa_flags = SA_RESTART; #endif e2fsck_global_ctx = ctx; sa.sa_handler = signal_progress_on; sigaction(SIGUSR1, &sa, 0); sa.sa_handler = signal_progress_off; sigaction(SIGUSR2, &sa, 0); /* Update our PATH to include /sbin if we need to run badblocks */ if (cflag) e2fs_set_sbin_path(); return 0; } static const char my_ver_string[] = E2FSPROGS_VERSION; static const char my_ver_date[] = E2FSPROGS_DATE; int e2fsck_main (int argc, char **argv); int e2fsck_main (int argc, char **argv) { errcode_t retval; int exit_value = EXIT_OK; ext2_filsys fs = 0; io_manager io_ptr; struct ext2_super_block *sb; const char *lib_ver_date; int my_ver, lib_ver; e2fsck_t ctx; struct problem_context pctx; int flags, run_result; clear_problem_context(&pctx); my_ver = ext2fs_parse_version_string(my_ver_string); lib_ver = ext2fs_get_library_version(0, &lib_ver_date); if (my_ver > lib_ver) { fprintf( stderr, _("Error: ext2fs library version " "out of date!\n")); show_version_only++; } retval = PRS(argc, argv, &ctx); if (retval) { bb_error_msg(_("while trying to initialize program")); exit(EXIT_ERROR); } reserve_stdio_fds(); if (!(ctx->options & E2F_OPT_PREEN) || show_version_only) fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string, my_ver_date); if (show_version_only) { fprintf(stderr, _("\tUsing %s, %s\n"), error_message(EXT2_ET_BASE), lib_ver_date); exit(EXIT_OK); } check_mount(ctx); if (!(ctx->options & E2F_OPT_PREEN) && !(ctx->options & E2F_OPT_NO) && !(ctx->options & E2F_OPT_YES)) { if (!ctx->interactive) bb_error_msg_and_die(_("need terminal for interactive repairs")); } ctx->superblock = ctx->use_superblock; restart: #ifdef CONFIG_TESTIO_DEBUG io_ptr = test_io_manager; test_io_backing_manager = unix_io_manager; #else io_ptr = unix_io_manager; #endif flags = 0; if ((ctx->options & E2F_OPT_READONLY) == 0) flags |= EXT2_FLAG_RW; if (ctx->superblock && ctx->blocksize) { retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options, flags, ctx->superblock, ctx->blocksize, io_ptr, &fs); } else if (ctx->superblock) { int blocksize; for (blocksize = EXT2_MIN_BLOCK_SIZE; blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) { retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options, flags, ctx->superblock, blocksize, io_ptr, &fs); if (!retval) break; } } else retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options, flags, 0, 0, io_ptr, &fs); if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) && !(ctx->flags & E2F_FLAG_SB_SPECIFIED) && ((retval == EXT2_ET_BAD_MAGIC) || ((retval == 0) && ext2fs_check_desc(fs)))) { if (!fs || (fs->group_desc_count > 1)) { printf(_("%s trying backup blocks...\n"), retval ? _("Couldn't find ext2 superblock,") : _("Group descriptors look bad...")); get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr); if (fs) ext2fs_close(fs); goto restart; } } if (retval) { bb_error_msg(_("while trying to open %s"), ctx->filesystem_name); if (retval == EXT2_ET_REV_TOO_HIGH) { printf(_("The filesystem revision is apparently " "too high for this version of e2fsck.\n" "(Or the filesystem superblock " "is corrupt)\n\n")); fix_problem(ctx, PR_0_SB_CORRUPT, &pctx); } else if (retval == EXT2_ET_SHORT_READ) printf(_("Could this be a zero-length partition?\n")); else if ((retval == EPERM) || (retval == EACCES)) printf(_("You must have %s access to the " "filesystem or be root\n"), (ctx->options & E2F_OPT_READONLY) ? "r/o" : "r/w"); else if (retval == ENXIO) printf(_("Possibly non-existent or swap device?\n")); #ifdef EROFS else if (retval == EROFS) printf(_("Disk write-protected; use the -n option " "to do a read-only\n" "check of the device.\n")); #endif else fix_problem(ctx, PR_0_SB_CORRUPT, &pctx); bb_error_msg_and_die(0); } ctx->fs = fs; fs->priv_data = ctx; sb = fs->super; if (sb->s_rev_level > E2FSCK_CURRENT_REV) { bb_error_msg(_("while trying to open %s"), ctx->filesystem_name); get_newer: bb_error_msg_and_die(_("Get a newer version of e2fsck!")); } /* * Set the device name, which is used whenever we print error * or informational messages to the user. */ if (ctx->device_name == 0 && (sb->s_volume_name[0] != 0)) { ctx->device_name = string_copy(sb->s_volume_name, sizeof(sb->s_volume_name)); } if (ctx->device_name == 0) ctx->device_name = ctx->filesystem_name; /* * Make sure the ext3 superblock fields are consistent. */ retval = e2fsck_check_ext3_journal(ctx); if (retval) { bb_error_msg(_("while checking ext3 journal for %s"), ctx->device_name); bb_error_msg_and_die(0); } /* * Check to see if we need to do ext3-style recovery. If so, * do it, and then restart the fsck. */ if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) { if (ctx->options & E2F_OPT_READONLY) { printf(_("Warning: skipping journal recovery " "because doing a read-only filesystem " "check.\n")); io_channel_flush(ctx->fs->io); } else { if (ctx->flags & E2F_FLAG_RESTARTED) { /* * Whoops, we attempted to run the * journal twice. This should never * happen, unless the hardware or * device driver is being bogus. */ bb_error_msg(_("can't set superblock flags on %s"), ctx->device_name); bb_error_msg_and_die(0); } retval = e2fsck_run_ext3_journal(ctx); if (retval) { bb_error_msg(_("while recovering ext3 journal of %s"), ctx->device_name); bb_error_msg_and_die(0); } ext2fs_close(ctx->fs); ctx->fs = 0; ctx->flags |= E2F_FLAG_RESTARTED; goto restart; } } /* * Check for compatibility with the feature sets. We need to * be more stringent than ext2fs_open(). */ if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) || (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) { bb_error_msg("(%s)", ctx->device_name); goto get_newer; } if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { bb_error_msg("(%s)", ctx->device_name); goto get_newer; } #ifdef ENABLE_COMPRESSION /* FIXME - do we support this at all? */ if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION) bb_error_msg(_("warning: compression support is experimental")); #endif #ifndef ENABLE_HTREE if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) { bb_error_msg(_("E2fsck not compiled with HTREE support,\n\t" "but filesystem %s has HTREE directories."), ctx->device_name); goto get_newer; } #endif /* * If the user specified a specific superblock, presumably the * master superblock has been trashed. So we mark the * superblock as dirty, so it can be written out. */ if (ctx->superblock && !(ctx->options & E2F_OPT_READONLY)) ext2fs_mark_super_dirty(fs); /* * We only update the master superblock because (a) paranoia; * we don't want to corrupt the backup superblocks, and (b) we * don't need to update the mount count and last checked * fields in the backup superblock (the kernel doesn't * update the backup superblocks anyway). */ fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; ehandler_init(fs->io); if (ctx->superblock) set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0); ext2fs_mark_valid(fs); check_super_block(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) bb_error_msg_and_die(0); check_if_skip(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) bb_error_msg_and_die(0); #ifdef ENABLE_SWAPFS #ifdef WORDS_BIGENDIAN #define NATIVE_FLAG EXT2_FLAG_SWAP_BYTES #else #define NATIVE_FLAG 0 #endif if (normalize_swapfs) { if ((fs->flags & EXT2_FLAG_SWAP_BYTES) == NATIVE_FLAG) { fprintf(stderr, _("%s: Filesystem byte order " "already normalized.\n"), ctx->device_name); bb_error_msg_and_die(0); } } if (swapfs) { swap_filesys(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) bb_error_msg_and_die(0); } #endif /* * Mark the system as valid, 'til proven otherwise */ ext2fs_mark_valid(fs); retval = ext2fs_read_bb_inode(fs, &fs->badblocks); if (retval) { bb_error_msg(_("while reading bad blocks inode")); preenhalt(ctx); printf(_("This doesn't bode well," " but we'll try to go on...\n")); } run_result = e2fsck_run(ctx); e2fsck_clear_progbar(ctx); if (run_result == E2F_FLAG_RESTART) { printf(_("Restarting e2fsck from the beginning...\n")); retval = e2fsck_reset_context(ctx); if (retval) { bb_error_msg(_("while resetting context")); bb_error_msg_and_die(0); } ext2fs_close(fs); goto restart; } if (run_result & E2F_FLAG_CANCEL) { printf(_("%s: e2fsck canceled.\n"), ctx->device_name ? ctx->device_name : ctx->filesystem_name); exit_value |= FSCK_CANCELED; } if (run_result & E2F_FLAG_ABORT) bb_error_msg_and_die(_("aborted")); /* Cleanup */ if (ext2fs_test_changed(fs)) { exit_value |= EXIT_NONDESTRUCT; if (!(ctx->options & E2F_OPT_PREEN)) printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"), ctx->device_name); if (ctx->mount_flags & EXT2_MF_ISROOT) { printf(_("%s: ***** REBOOT LINUX *****\n"), ctx->device_name); exit_value |= EXIT_DESTRUCT; } } if (!ext2fs_test_valid(fs)) { printf(_("\n%s: ********** WARNING: Filesystem still has " "errors **********\n\n"), ctx->device_name); exit_value |= EXIT_UNCORRECTED; exit_value &= ~EXIT_NONDESTRUCT; } if (exit_value & FSCK_CANCELED) exit_value &= ~EXIT_NONDESTRUCT; else { show_stats(ctx); if (!(ctx->options & E2F_OPT_READONLY)) { if (ext2fs_test_valid(fs)) { if (!(sb->s_state & EXT2_VALID_FS)) exit_value |= EXIT_NONDESTRUCT; sb->s_state = EXT2_VALID_FS; } else sb->s_state &= ~EXT2_VALID_FS; sb->s_mnt_count = 0; sb->s_lastcheck = time(NULL); ext2fs_mark_super_dirty(fs); } } e2fsck_write_bitmaps(ctx); ext2fs_close(fs); ctx->fs = NULL; free(ctx->filesystem_name); free(ctx->journal_name); e2fsck_free_context(ctx); return exit_value; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/Config.src0000644000000000000000000000311712263563520020374 0ustar rootroot# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Linux Ext2 FS Progs" INSERT config CHATTR bool "chattr" default n help chattr changes the file attributes on a second extended file system. config E2FSCK bool "e2fsck" default n help e2fsck is used to check Linux second extended file systems (ext2fs). e2fsck also supports ext2 filesystems countaining a journal (ext3). The normal compat symlinks 'fsck.ext2' and 'fsck.ext3' are also provided. config FSCK bool "fsck" default n help fsck is used to check and optionally repair one or more filesystems. In actuality, fsck is simply a front-end for the various file system checkers (fsck.fstype) available under Linux. config LSATTR bool "lsattr" default n help lsattr lists the file attributes on a second extended file system. config MKE2FS bool "mke2fs" default n help mke2fs is used to create an ext2/ext3 filesystem. The normal compat symlinks 'mkfs.ext2' and 'mkfs.ext3' are also provided. config TUNE2FS bool "tune2fs" default n help tune2fs allows the system administrator to adjust various tunable filesystem parameters on Linux ext2/ext3 filesystems. config E2LABEL bool "e2label" default n depends on TUNE2FS help e2label will display or change the filesystem label on the ext2 filesystem located on device. config FINDFS bool "findfs" default n depends on TUNE2FS help findfs will search the disks in the system looking for a filesystem which has a label matching label or a UUID equal to uuid. endmenu busybox-1.22.1/e2fsprogs/old_e2fsprogs/mke2fs.c0000644000000000000000000010327012263563520020012 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * mke2fs.c - Make a ext2fs filesystem. * * Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, * 2003, 2004, 2005 by Theodore Ts'o. * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* Usage: mke2fs [options] device * * The device may be a block device or a image of one, but this isn't * enforced (but it's not much fun on a character device :-). */ #include #include #include #include #include #include #include #include #include #include #include #include #include "e2fsbb.h" #include "ext2fs/ext2_fs.h" #include "uuid/uuid.h" #include "e2p/e2p.h" #include "ext2fs/ext2fs.h" #include "util.h" #define STRIDE_LENGTH 8 #ifndef __sparc__ #define ZAP_BOOTBLOCK #endif static const char * device_name; /* Command line options */ static int cflag; static int quiet; static int super_only; static int force; static int noaction; static int journal_size; static int journal_flags; static const char *bad_blocks_filename; static __u32 fs_stride; static struct ext2_super_block param; static char *creator_os; static char *volume_label; static char *mount_dir; static char *journal_device = NULL; static int sync_kludge; /* Set using the MKE2FS_SYNC env. option */ static int sys_page_size = 4096; static int linux_version_code = 0; static int int_log2(int arg) { int l = 0; arg >>= 1; while (arg) { l++; arg >>= 1; } return l; } static int int_log10(unsigned int arg) { int l; for (l = 0; arg; l++) arg = arg / 10; return l; } /* * This function sets the default parameters for a filesystem * * The type is specified by the user. The size is the maximum size * (in megabytes) for which a set of parameters applies, with a size * of zero meaning that it is the default parameter for the type. * Note that order is important in the table below. */ #define DEF_MAX_BLOCKSIZE -1 static const char default_str[] = "default"; struct mke2fs_defaults { const char *type; int size; int blocksize; int inode_ratio; }; static const struct mke2fs_defaults settings[] = { { default_str, 0, 4096, 8192 }, { default_str, 512, 1024, 4096 }, { default_str, 3, 1024, 8192 }, { "journal", 0, 4096, 8192 }, { "news", 0, 4096, 4096 }, { "largefile", 0, 4096, 1024 * 1024 }, { "largefile4", 0, 4096, 4096 * 1024 }, { 0, 0, 0, 0}, }; static void set_fs_defaults(const char *fs_type, struct ext2_super_block *super, int blocksize, int sector_size, int *inode_ratio) { int megs; int ratio = 0; const struct mke2fs_defaults *p; int use_bsize = 1024; megs = super->s_blocks_count * (EXT2_BLOCK_SIZE(super) / 1024) / 1024; if (inode_ratio) ratio = *inode_ratio; if (!fs_type) fs_type = default_str; for (p = settings; p->type; p++) { if ((strcmp(p->type, fs_type) != 0) && (strcmp(p->type, default_str) != 0)) continue; if ((p->size != 0) && (megs > p->size)) continue; if (ratio == 0) *inode_ratio = p->inode_ratio < blocksize ? blocksize : p->inode_ratio; use_bsize = p->blocksize; } if (blocksize <= 0) { if (use_bsize == DEF_MAX_BLOCKSIZE) { use_bsize = sys_page_size; if ((linux_version_code < (2*65536 + 6*256)) && (use_bsize > 4096)) use_bsize = 4096; } if (sector_size && use_bsize < sector_size) use_bsize = sector_size; if ((blocksize < 0) && (use_bsize < (-blocksize))) use_bsize = -blocksize; blocksize = use_bsize; super->s_blocks_count /= blocksize / 1024; } super->s_log_frag_size = super->s_log_block_size = int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE); } /* * Helper function for read_bb_file and test_disk */ static void invalid_block(ext2_filsys fs EXT2FS_ATTR((unused)), blk_t blk) { bb_error_msg("Bad block %u out of range; ignored", blk); } /* * Busybox stuff */ static void mke2fs_error_msg_and_die(int retval, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); static void mke2fs_error_msg_and_die(int retval, const char *fmt, ...) { va_list ap; if (retval) { va_start(ap, fmt); fprintf(stderr, "\nCould not "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); exit(EXIT_FAILURE); } } static void mke2fs_verbose(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); static void mke2fs_verbose(const char *fmt, ...) { va_list ap; if (!quiet) { va_start(ap, fmt); vfprintf(stdout, fmt, ap); fflush(stdout); va_end(ap); } } static void mke2fs_verbose_done(void) { mke2fs_verbose("done\n"); } static void mke2fs_warning_msg(int retval, char *fmt, ... ) __attribute__ ((format (printf, 2, 3))); static void mke2fs_warning_msg(int retval, char *fmt, ... ) { va_list ap; if (retval) { va_start(ap, fmt); fprintf(stderr, "\nWarning: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); } } /* * Reads the bad blocks list from a file */ static void read_bb_file(ext2_filsys fs, badblocks_list *bb_list, const char *bad_blocks_file) { FILE *f; errcode_t retval; f = xfopen_for_read(bad_blocks_file); retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block); fclose (f); mke2fs_error_msg_and_die(retval, "read bad blocks from list"); } /* * Runs the badblocks program to test the disk */ static void test_disk(ext2_filsys fs, badblocks_list *bb_list) { FILE *f; errcode_t retval; char buf[1024]; sprintf(buf, "badblocks -b %u %s%s%s %d", fs->blocksize, quiet ? "" : "-s ", (cflag > 1) ? "-w " : "", fs->device_name, fs->super->s_blocks_count); mke2fs_verbose("Running command: %s\n", buf); f = popen(buf, "r"); if (!f) { bb_perror_msg_and_die("can't run '%s'", buf); } retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block); pclose(f); mke2fs_error_msg_and_die(retval, "read bad blocks from program"); } static void handle_bad_blocks(ext2_filsys fs, badblocks_list bb_list) { dgrp_t i; blk_t j; unsigned must_be_good; blk_t blk; badblocks_iterate bb_iter; errcode_t retval; blk_t group_block; int group; int group_bad; if (!bb_list) return; /* * The primary superblock and group descriptors *must* be * good; if not, abort. */ must_be_good = fs->super->s_first_data_block + 1 + fs->desc_blocks; for (i = fs->super->s_first_data_block; i <= must_be_good; i++) { if (ext2fs_badblocks_list_test(bb_list, i)) { bb_error_msg_and_die( "Block %d in primary superblock/group descriptor area bad\n" "Blocks %d through %d must be good in order to build a filesystem\n" "Aborting ...", i, fs->super->s_first_data_block, must_be_good); } } /* * See if any of the bad blocks are showing up in the backup * superblocks and/or group descriptors. If so, issue a * warning and adjust the block counts appropriately. */ group_block = fs->super->s_first_data_block + fs->super->s_blocks_per_group; for (i = 1; i < fs->group_desc_count; i++) { group_bad = 0; for (j=0; j < fs->desc_blocks+1; j++) { if (ext2fs_badblocks_list_test(bb_list, group_block + j)) { mke2fs_warning_msg(!group_bad, "the backup superblock/group descriptors at block %d contain\n" "bad blocks\n", group_block); group_bad++; group = ext2fs_group_of_blk(fs, group_block+j); fs->group_desc[group].bg_free_blocks_count++; fs->super->s_free_blocks_count++; } } group_block += fs->super->s_blocks_per_group; } /* * Mark all the bad blocks as used... */ retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter); mke2fs_error_msg_and_die(retval, "mark bad blocks as used"); while (ext2fs_badblocks_list_iterate(bb_iter, &blk)) ext2fs_mark_block_bitmap(fs->block_map, blk); ext2fs_badblocks_list_iterate_end(bb_iter); } /* * These functions implement a generalized progress meter. */ struct progress_struct { char format[20]; char backup[80]; __u32 max; int skip_progress; }; static void progress_init(struct progress_struct *progress, const char *label,__u32 max) { int i; memset(progress, 0, sizeof(struct progress_struct)); if (quiet) return; /* * Figure out how many digits we need */ i = int_log10(max); sprintf(progress->format, "%%%dd/%%%dld", i, i); memset(progress->backup, '\b', sizeof(progress->backup)-1); progress->backup[sizeof(progress->backup)-1] = 0; if ((2*i)+1 < (int) sizeof(progress->backup)) progress->backup[(2*i)+1] = 0; progress->max = max; progress->skip_progress = 0; if (getenv("MKE2FS_SKIP_PROGRESS")) progress->skip_progress++; fputs(label, stdout); fflush(stdout); } static void progress_update(struct progress_struct *progress, __u32 val) { if ((progress->format[0] == 0) || progress->skip_progress) return; printf(progress->format, val, progress->max); fputs(progress->backup, stdout); } static void progress_close(struct progress_struct *progress) { if (progress->format[0] == 0) return; printf("%-28s\n", "done"); } /* * Helper function which zeros out _num_ blocks starting at _blk_. In * case of an error, the details of the error is returned via _ret_blk_ * and _ret_count_ if they are non-NULL pointers. Returns 0 on * success, and an error code on an error. * * As a special case, if the first argument is NULL, then it will * attempt to free the static zeroizing buffer. (This is to keep * programs that check for memory leaks happy.) */ static errcode_t zero_blocks(ext2_filsys fs, blk_t blk, int num, struct progress_struct *progress, blk_t *ret_blk, int *ret_count) { int j, count, next_update; static char *buf; errcode_t retval; /* If fs is null, clean up the static buffer and return */ if (!fs) { if (buf) { free(buf); buf = 0; } return 0; } /* Allocate the zeroizing buffer if necessary */ if (!buf) { buf = xzalloc(fs->blocksize * STRIDE_LENGTH); } /* OK, do the write loop */ next_update = 0; for (j=0; j < num; j += STRIDE_LENGTH, blk += STRIDE_LENGTH) { count = num - j; if (count > STRIDE_LENGTH) count = STRIDE_LENGTH; retval = io_channel_write_blk(fs->io, blk, count, buf); if (retval) { if (ret_count) *ret_count = count; if (ret_blk) *ret_blk = blk; return retval; } if (progress && j > next_update) { next_update += num / 100; progress_update(progress, blk); } } return 0; } static void write_inode_tables(ext2_filsys fs) { errcode_t retval; blk_t blk; dgrp_t i; int num; struct progress_struct progress; if (quiet) memset(&progress, 0, sizeof(progress)); else progress_init(&progress, "Writing inode tables: ", fs->group_desc_count); for (i = 0; i < fs->group_desc_count; i++) { progress_update(&progress, i); blk = fs->group_desc[i].bg_inode_table; num = fs->inode_blocks_per_group; retval = zero_blocks(fs, blk, num, 0, &blk, &num); mke2fs_error_msg_and_die(retval, "write %d blocks in inode table starting at %d.", num, blk); if (sync_kludge) { if (sync_kludge == 1) sync(); else if ((i % sync_kludge) == 0) sync(); } } zero_blocks(0, 0, 0, 0, 0, 0); progress_close(&progress); } static void create_root_dir(ext2_filsys fs) { errcode_t retval; struct ext2_inode inode; retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, 0); mke2fs_error_msg_and_die(retval, "create root dir"); if (geteuid()) { retval = ext2fs_read_inode(fs, EXT2_ROOT_INO, &inode); mke2fs_error_msg_and_die(retval, "read root inode"); inode.i_uid = getuid(); if (inode.i_uid) inode.i_gid = getgid(); retval = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode); mke2fs_error_msg_and_die(retval, "set root inode ownership"); } } static void create_lost_and_found(ext2_filsys fs) { errcode_t retval; ext2_ino_t ino; const char *name = "lost+found"; int i = 1; char *msg = "create"; int lpf_size = 0; fs->umask = 077; retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, 0, name); if (retval) { goto CREATE_LOST_AND_FOUND_ERROR; } retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, strlen(name), 0, &ino); if (retval) { msg = "lookup"; goto CREATE_LOST_AND_FOUND_ERROR; } for (; i < EXT2_NDIR_BLOCKS; i++) { if ((lpf_size += fs->blocksize) >= 16*1024) break; retval = ext2fs_expand_dir(fs, ino); msg = "expand"; CREATE_LOST_AND_FOUND_ERROR: mke2fs_error_msg_and_die(retval, "%s %s", msg, name); } } static void create_bad_block_inode(ext2_filsys fs, badblocks_list bb_list) { errcode_t retval; ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_BAD_INO); fs->group_desc[0].bg_free_inodes_count--; fs->super->s_free_inodes_count--; retval = ext2fs_update_bb_inode(fs, bb_list); mke2fs_error_msg_and_die(retval, "set bad block inode"); } static void reserve_inodes(ext2_filsys fs) { ext2_ino_t i; int group; for (i = EXT2_ROOT_INO + 1; i < EXT2_FIRST_INODE(fs->super); i++) { ext2fs_mark_inode_bitmap(fs->inode_map, i); group = ext2fs_group_of_ino(fs, i); fs->group_desc[group].bg_free_inodes_count--; fs->super->s_free_inodes_count--; } ext2fs_mark_ib_dirty(fs); } #define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */ #define BSD_MAGICDISK (0x57455682UL) /* The disk magic number reversed */ #define BSD_LABEL_OFFSET 64 static void zap_sector(ext2_filsys fs, int sect, int nsect) { char *buf; char *fmt = "could not %s %d"; int retval; unsigned int *magic; buf = xmalloc(512*nsect); if (sect == 0) { /* Check for a BSD disklabel, and don't erase it if so */ retval = io_channel_read_blk(fs->io, 0, -512, buf); if (retval) mke2fs_warning_msg(retval, fmt, "read block", 0); else { magic = (unsigned int *) (buf + BSD_LABEL_OFFSET); if ((*magic == BSD_DISKMAGIC) || (*magic == BSD_MAGICDISK)) return; } } memset(buf, 0, 512*nsect); io_channel_set_blksize(fs->io, 512); retval = io_channel_write_blk(fs->io, sect, -512*nsect, buf); io_channel_set_blksize(fs->io, fs->blocksize); free(buf); mke2fs_warning_msg(retval, fmt, "erase sector", sect); } static void create_journal_dev(ext2_filsys fs) { struct progress_struct progress; errcode_t retval; char *buf; char *fmt = "%s journal superblock"; blk_t blk; int count; retval = ext2fs_create_journal_superblock(fs, fs->super->s_blocks_count, 0, &buf); mke2fs_error_msg_and_die(retval, fmt, "init"); if (quiet) memset(&progress, 0, sizeof(progress)); else progress_init(&progress, "Zeroing journal device: ", fs->super->s_blocks_count); retval = zero_blocks(fs, 0, fs->super->s_blocks_count, &progress, &blk, &count); mke2fs_error_msg_and_die(retval, "zero journal device (block %u, count %d)", blk, count); zero_blocks(0, 0, 0, 0, 0, 0); retval = io_channel_write_blk(fs->io, fs->super->s_first_data_block+1, 1, buf); mke2fs_error_msg_and_die(retval, fmt, "write"); progress_close(&progress); } static void show_stats(ext2_filsys fs) { struct ext2_super_block *s = fs->super; char *os; blk_t group_block; dgrp_t i; int need, col_left; mke2fs_warning_msg((param.s_blocks_count != s->s_blocks_count), "%d blocks unused\n", param.s_blocks_count - s->s_blocks_count); os = e2p_os2string(fs->super->s_creator_os); printf( "Filesystem label=%.*s\n" "OS type: %s\n" "Block size=%u (log=%u)\n" "Fragment size=%u (log=%u)\n" "%u inodes, %u blocks\n" "%u blocks (%2.2f%%) reserved for the super user\n" "First data block=%u\n", (int) sizeof(s->s_volume_name), s->s_volume_name, os, fs->blocksize, s->s_log_block_size, fs->fragsize, s->s_log_frag_size, s->s_inodes_count, s->s_blocks_count, s->s_r_blocks_count, 100.0 * s->s_r_blocks_count / s->s_blocks_count, s->s_first_data_block); free(os); if (s->s_reserved_gdt_blocks) { printf("Maximum filesystem blocks=%lu\n", (s->s_reserved_gdt_blocks + fs->desc_blocks) * (fs->blocksize / sizeof(struct ext2_group_desc)) * s->s_blocks_per_group); } printf( "%u block group%s\n" "%u blocks per group, %u fragments per group\n" "%u inodes per group\n", fs->group_desc_count, (fs->group_desc_count > 1) ? "s" : "", s->s_blocks_per_group, s->s_frags_per_group, s->s_inodes_per_group); if (fs->group_desc_count == 1) { bb_putchar('\n'); return; } printf("Superblock backups stored on blocks: "); group_block = s->s_first_data_block; col_left = 0; for (i = 1; i < fs->group_desc_count; i++) { group_block += s->s_blocks_per_group; if (!ext2fs_bg_has_super(fs, i)) continue; if (i != 1) printf(", "); need = int_log10(group_block) + 2; if (need > col_left) { printf("\n\t"); col_left = 72; } col_left -= need; printf("%u", group_block); } puts("\n"); } /* * Set the S_CREATOR_OS field. Return true if OS is known, * otherwise, 0. */ static int set_os(struct ext2_super_block *sb, char *os) { if (isdigit (*os)) { sb->s_creator_os = atoi(os); return 1; } if ((sb->s_creator_os = e2p_string2os(os)) >= 0) { return 1; } else if (!strcasecmp("GNU", os)) { sb->s_creator_os = EXT2_OS_HURD; return 1; } return 0; } static void parse_extended_opts(struct ext2_super_block *sb_param, const char *opts) { char *buf, *token, *next, *p, *arg; int r_usage = 0; buf = xstrdup(opts); for (token = buf; token && *token; token = next) { p = strchr(token, ','); next = 0; if (p) { *p = 0; next = p+1; } arg = strchr(token, '='); if (arg) { *arg = 0; arg++; } if (strcmp(token, "stride") == 0) { if (!arg) { r_usage++; continue; } fs_stride = strtoul(arg, &p, 0); if (*p || (fs_stride == 0)) { bb_error_msg("Invalid stride parameter: %s", arg); r_usage++; continue; } } else if (!strcmp(token, "resize")) { unsigned long resize, bpg, rsv_groups; unsigned long group_desc_count, desc_blocks; unsigned int gdpb, blocksize; int rsv_gdb; if (!arg) { r_usage++; continue; } resize = parse_num_blocks(arg, sb_param->s_log_block_size); if (resize == 0) { bb_error_msg("Invalid resize parameter: %s", arg); r_usage++; continue; } if (resize <= sb_param->s_blocks_count) { bb_error_msg("The resize maximum must be greater " "than the filesystem size"); r_usage++; continue; } blocksize = EXT2_BLOCK_SIZE(sb_param); bpg = sb_param->s_blocks_per_group; if (!bpg) bpg = blocksize * 8; gdpb = blocksize / sizeof(struct ext2_group_desc); group_desc_count = (sb_param->s_blocks_count + bpg - 1) / bpg; desc_blocks = (group_desc_count + gdpb - 1) / gdpb; rsv_groups = (resize + bpg - 1) / bpg; rsv_gdb = (rsv_groups + gdpb - 1) / gdpb - desc_blocks; if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb_param)) rsv_gdb = EXT2_ADDR_PER_BLOCK(sb_param); if (rsv_gdb > 0) { sb_param->s_feature_compat |= EXT2_FEATURE_COMPAT_RESIZE_INO; sb_param->s_reserved_gdt_blocks = rsv_gdb; } } else r_usage++; } if (r_usage) { bb_error_msg_and_die( "\nBad options specified.\n\n" "Extended options are separated by commas, " "and may take an argument which\n" "\tis set off by an equals ('=') sign.\n\n" "Valid extended options are:\n" "\tstride=\n" "\tresize=\n"); } } static __u32 ok_features[3] = { EXT3_FEATURE_COMPAT_HAS_JOURNAL | EXT2_FEATURE_COMPAT_RESIZE_INO | EXT2_FEATURE_COMPAT_DIR_INDEX, /* Compat */ EXT2_FEATURE_INCOMPAT_FILETYPE| /* Incompat */ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV| EXT2_FEATURE_INCOMPAT_META_BG, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER /* R/O compat */ }; static int PRS(int argc, char **argv) { int c; int size; char * tmp; int blocksize = 0; int inode_ratio = 0; int inode_size = 0; int reserved_ratio = 5; int sector_size = 0; int show_version_only = 0; ext2_ino_t num_inodes = 0; errcode_t retval; char * extended_opts = NULL; const char * fs_type = NULL; blk_t dev_size; long sysval; /* Update our PATH to include /sbin */ e2fs_set_sbin_path(); tmp = getenv("MKE2FS_SYNC"); if (tmp) sync_kludge = atoi(tmp); /* Determine the system page size if possible */ #if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE)) #define _SC_PAGESIZE _SC_PAGE_SIZE #endif #ifdef _SC_PAGESIZE sysval = sysconf(_SC_PAGESIZE); if (sysval > 0) sys_page_size = sysval; #endif /* _SC_PAGESIZE */ setbuf(stdout, NULL); setbuf(stderr, NULL); memset(¶m, 0, sizeof(struct ext2_super_block)); param.s_rev_level = 1; /* Create revision 1 filesystems now */ param.s_feature_incompat |= EXT2_FEATURE_INCOMPAT_FILETYPE; param.s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; #ifdef __linux__ linux_version_code = get_linux_version_code(); if (linux_version_code && linux_version_code < KERNEL_VERSION(2,2,0)) { param.s_rev_level = 0; param.s_feature_incompat = 0; param.s_feature_compat = 0; param.s_feature_ro_compat = 0; } #endif /* If called as mkfs.ext3, create a journal inode */ if (last_char_is(applet_name, '3')) journal_size = -1; while ((c = getopt (argc, argv, "b:cE:f:g:i:jl:m:no:qr:R:s:tvI:J:ST:FL:M:N:O:V")) != EOF) { switch (c) { case 'b': blocksize = xatou_range(optarg, EXT2_MIN_BLOCK_SIZE, EXT2_MAX_BLOCK_SIZE); mke2fs_warning_msg((blocksize > 4096), "blocksize %d not usable on most systems", blocksize); param.s_log_block_size = int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE); break; case 'c': /* Check for bad blocks */ case 't': /* deprecated */ cflag++; break; case 'f': size = xatou_range(optarg, EXT2_MIN_BLOCK_SIZE, EXT2_MAX_BLOCK_SIZE); param.s_log_frag_size = int_log2(size >> EXT2_MIN_BLOCK_LOG_SIZE); mke2fs_warning_msg(1, "fragments not supported. Ignoring -f option"); break; case 'g': param.s_blocks_per_group = xatou32(optarg); if ((param.s_blocks_per_group % 8) != 0) { bb_error_msg_and_die("blocks per group must be multiple of 8"); } break; case 'i': /* Huh? is "* 1024" correct? */ inode_ratio = xatou_range(optarg, EXT2_MIN_BLOCK_SIZE, EXT2_MAX_BLOCK_SIZE * 1024); break; case 'J': parse_journal_opts(&journal_device, &journal_flags, &journal_size, optarg); break; case 'j': param.s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; if (!journal_size) journal_size = -1; break; case 'l': bad_blocks_filename = optarg; break; case 'm': reserved_ratio = xatou_range(optarg, 0, 50); break; case 'n': noaction++; break; case 'o': creator_os = optarg; break; case 'r': param.s_rev_level = xatoi_positive(optarg); if (param.s_rev_level == EXT2_GOOD_OLD_REV) { param.s_feature_incompat = 0; param.s_feature_compat = 0; param.s_feature_ro_compat = 0; } break; case 's': /* deprecated */ if (xatou(optarg)) param.s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; else param.s_feature_ro_compat &= ~EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; break; #ifdef EXT2_DYNAMIC_REV case 'I': inode_size = xatoi_positive(optarg); break; #endif case 'N': num_inodes = xatoi_positive(optarg); break; case 'v': quiet = 0; break; case 'q': quiet = 1; break; case 'F': force = 1; break; case 'L': volume_label = optarg; break; case 'M': mount_dir = optarg; break; case 'O': if (!strcmp(optarg, "none")) { param.s_feature_compat = 0; param.s_feature_incompat = 0; param.s_feature_ro_compat = 0; break; } if (e2p_edit_feature(optarg, ¶m.s_feature_compat, ok_features)) { bb_error_msg_and_die("Invalid filesystem option set: %s", optarg); } break; case 'E': case 'R': extended_opts = optarg; break; case 'S': super_only = 1; break; case 'T': fs_type = optarg; break; case 'V': /* Print version number and exit */ show_version_only = 1; quiet = 0; break; default: bb_show_usage(); } } if ((optind == argc) /*&& !show_version_only*/) bb_show_usage(); device_name = argv[optind++]; mke2fs_verbose("mke2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE); if (show_version_only) { return 0; } /* * If there's no blocksize specified and there is a journal * device, use it to figure out the blocksize */ if (blocksize <= 0 && journal_device) { ext2_filsys jfs; io_manager io_ptr; #ifdef CONFIG_TESTIO_DEBUG io_ptr = test_io_manager; test_io_backing_manager = unix_io_manager; #else io_ptr = unix_io_manager; #endif retval = ext2fs_open(journal_device, EXT2_FLAG_JOURNAL_DEV_OK, 0, 0, io_ptr, &jfs); mke2fs_error_msg_and_die(retval, "open journal device %s", journal_device); if ((blocksize < 0) && (jfs->blocksize < (unsigned) (-blocksize))) { bb_error_msg_and_die( "Journal dev blocksize (%d) smaller than " "minimum blocksize %d\n", jfs->blocksize, -blocksize); } blocksize = jfs->blocksize; param.s_log_block_size = int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE); ext2fs_close(jfs); } if (blocksize > sys_page_size) { mke2fs_warning_msg(1, "%d-byte blocks too big for system (max %d)", blocksize, sys_page_size); if (!force) { proceed_question(); } bb_error_msg("Forced to continue"); } mke2fs_warning_msg(((blocksize > 4096) && (param.s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)), "some 2.4 kernels do not support " "blocksizes greater than 4096 using ext3.\n" "Use -b 4096 if this is an issue for you\n"); if (optind < argc) { param.s_blocks_count = parse_num_blocks(argv[optind++], param.s_log_block_size); mke2fs_error_msg_and_die(!param.s_blocks_count, "invalid blocks count - %s", argv[optind - 1]); } if (optind < argc) bb_show_usage(); if (param.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { if (!fs_type) fs_type = "journal"; reserved_ratio = 0; param.s_feature_incompat = EXT3_FEATURE_INCOMPAT_JOURNAL_DEV; param.s_feature_compat = 0; param.s_feature_ro_compat = 0; } if (param.s_rev_level == EXT2_GOOD_OLD_REV && (param.s_feature_compat || param.s_feature_ro_compat || param.s_feature_incompat)) param.s_rev_level = 1; /* Create a revision 1 filesystem */ check_plausibility(device_name , force); check_mount(device_name, force, "filesystem"); param.s_log_frag_size = param.s_log_block_size; if (noaction && param.s_blocks_count) { dev_size = param.s_blocks_count; retval = 0; } else { retry: retval = ext2fs_get_device_size(device_name, EXT2_BLOCK_SIZE(¶m), &dev_size); if ((retval == EFBIG) && (blocksize == 0) && (param.s_log_block_size == 0)) { param.s_log_block_size = 2; blocksize = 4096; goto retry; } } mke2fs_error_msg_and_die((retval && (retval != EXT2_ET_UNIMPLEMENTED)),"determine filesystem size"); if (!param.s_blocks_count) { if (retval == EXT2_ET_UNIMPLEMENTED) { mke2fs_error_msg_and_die(1, "determine device size; you " "must specify\nthe size of the " "filesystem"); } else { if (dev_size == 0) { bb_error_msg_and_die( "Device size reported to be zero. " "Invalid partition specified, or\n\t" "partition table wasn't reread " "after running fdisk, due to\n\t" "a modified partition being busy " "and in use. You may need to reboot\n\t" "to re-read your partition table.\n" ); } param.s_blocks_count = dev_size; if (sys_page_size > EXT2_BLOCK_SIZE(¶m)) param.s_blocks_count &= ~((sys_page_size / EXT2_BLOCK_SIZE(¶m))-1); } } else if (!force && (param.s_blocks_count > dev_size)) { bb_error_msg("Filesystem larger than apparent device size"); proceed_question(); } /* * If the user asked for HAS_JOURNAL, then make sure a journal * gets created. */ if ((param.s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && !journal_size) journal_size = -1; /* Set first meta blockgroup via an environment variable */ /* (this is mostly for debugging purposes) */ if ((param.s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) && ((tmp = getenv("MKE2FS_FIRST_META_BG")))) param.s_first_meta_bg = atoi(tmp); /* Get the hardware sector size, if available */ retval = ext2fs_get_device_sectsize(device_name, §or_size); mke2fs_error_msg_and_die(retval, "determine hardware sector size"); if ((tmp = getenv("MKE2FS_DEVICE_SECTSIZE")) != NULL) sector_size = atoi(tmp); set_fs_defaults(fs_type, ¶m, blocksize, sector_size, &inode_ratio); blocksize = EXT2_BLOCK_SIZE(¶m); if (extended_opts) parse_extended_opts(¶m, extended_opts); /* Since sparse_super is the default, we would only have a problem * here if it was explicitly disabled. */ if ((param.s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INO) && !(param.s_feature_ro_compat&EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { bb_error_msg_and_die("reserved online resize blocks not supported " "on non-sparse filesystem"); } if (param.s_blocks_per_group) { if (param.s_blocks_per_group < 256 || param.s_blocks_per_group > 8 * (unsigned) blocksize) { bb_error_msg_and_die("blocks per group count out of range"); } } if (!force && param.s_blocks_count >= (1 << 31)) { bb_error_msg_and_die("Filesystem too large. No more than 2**31-1 blocks\n" "\t (8TB using a blocksize of 4k) are currently supported."); } if (inode_size) { if (inode_size < EXT2_GOOD_OLD_INODE_SIZE || inode_size > EXT2_BLOCK_SIZE(¶m) || inode_size & (inode_size - 1)) { bb_error_msg_and_die("invalid inode size %d (min %d/max %d)", inode_size, EXT2_GOOD_OLD_INODE_SIZE, blocksize); } mke2fs_warning_msg((inode_size != EXT2_GOOD_OLD_INODE_SIZE), "%d-byte inodes not usable on most systems", inode_size); param.s_inode_size = inode_size; } /* * Calculate number of inodes based on the inode ratio */ param.s_inodes_count = num_inodes ? num_inodes : ((__u64) param.s_blocks_count * blocksize) / inode_ratio; /* * Calculate number of blocks to reserve */ param.s_r_blocks_count = (param.s_blocks_count * reserved_ratio) / 100; return 1; } static void mke2fs_clean_up(void) { if (ENABLE_FEATURE_CLEAN_UP && journal_device) free(journal_device); } int mke2fs_main (int argc, char **argv); int mke2fs_main (int argc, char **argv) { errcode_t retval; ext2_filsys fs; badblocks_list bb_list = 0; unsigned int i; int val; io_manager io_ptr; if (ENABLE_FEATURE_CLEAN_UP) atexit(mke2fs_clean_up); if (!PRS(argc, argv)) return 0; #ifdef CONFIG_TESTIO_DEBUG io_ptr = test_io_manager; test_io_backing_manager = unix_io_manager; #else io_ptr = unix_io_manager; #endif /* * Initialize the superblock.... */ retval = ext2fs_initialize(device_name, 0, ¶m, io_ptr, &fs); mke2fs_error_msg_and_die(retval, "set up superblock"); /* * Wipe out the old on-disk superblock */ if (!noaction) zap_sector(fs, 2, 6); /* * Generate a UUID for it... */ uuid_generate(fs->super->s_uuid); /* * Initialize the directory index variables */ fs->super->s_def_hash_version = EXT2_HASH_TEA; uuid_generate((unsigned char *) fs->super->s_hash_seed); /* * Add "jitter" to the superblock's check interval so that we * don't check all the filesystems at the same time. We use a * kludgy hack of using the UUID to derive a random jitter value. */ for (i = 0, val = 0; i < sizeof(fs->super->s_uuid); i++) val += fs->super->s_uuid[i]; fs->super->s_max_mnt_count += val % EXT2_DFL_MAX_MNT_COUNT; /* * Override the creator OS, if applicable */ if (creator_os && !set_os(fs->super, creator_os)) { bb_error_msg_and_die("unknown os - %s", creator_os); } /* * For the Hurd, we will turn off filetype since it doesn't * support it. */ if (fs->super->s_creator_os == EXT2_OS_HURD) fs->super->s_feature_incompat &= ~EXT2_FEATURE_INCOMPAT_FILETYPE; /* * Set the volume label... */ if (volume_label) { snprintf(fs->super->s_volume_name, sizeof(fs->super->s_volume_name), "%s", volume_label); } /* * Set the last mount directory */ if (mount_dir) { snprintf(fs->super->s_last_mounted, sizeof(fs->super->s_last_mounted), "%s", mount_dir); } if (!quiet || noaction) show_stats(fs); if (noaction) return 0; if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { create_journal_dev(fs); return (ext2fs_close(fs) ? 1 : 0); } if (bad_blocks_filename) read_bb_file(fs, &bb_list, bad_blocks_filename); if (cflag) test_disk(fs, &bb_list); handle_bad_blocks(fs, bb_list); fs->stride = fs_stride; retval = ext2fs_allocate_tables(fs); mke2fs_error_msg_and_die(retval, "allocate filesystem tables"); if (super_only) { fs->super->s_state |= EXT2_ERROR_FS; fs->flags &= ~(EXT2_FLAG_IB_DIRTY|EXT2_FLAG_BB_DIRTY); } else { /* rsv must be a power of two (64kB is MD RAID sb alignment) */ unsigned int rsv = 65536 / fs->blocksize; unsigned long blocks = fs->super->s_blocks_count; unsigned long start; blk_t ret_blk; #ifdef ZAP_BOOTBLOCK zap_sector(fs, 0, 2); #endif /* * Wipe out any old MD RAID (or other) metadata at the end * of the device. This will also verify that the device is * as large as we think. Be careful with very small devices. */ start = (blocks & ~(rsv - 1)); if (start > rsv) start -= rsv; if (start > 0) retval = zero_blocks(fs, start, blocks - start, NULL, &ret_blk, NULL); mke2fs_warning_msg(retval, "can't zero block %u at end of filesystem", ret_blk); write_inode_tables(fs); create_root_dir(fs); create_lost_and_found(fs); reserve_inodes(fs); create_bad_block_inode(fs, bb_list); if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INO) { retval = ext2fs_create_resize_inode(fs); mke2fs_error_msg_and_die(retval, "reserve blocks for online resize"); } } if (journal_device) { make_journal_device(journal_device, fs, quiet, force); } else if (journal_size) { make_journal_blocks(fs, journal_size, journal_flags, quiet); } mke2fs_verbose("Writing superblocks and filesystem accounting information: "); retval = ext2fs_flush(fs); mke2fs_warning_msg(retval, "had trouble writing out superblocks"); mke2fs_verbose_done(); if (!quiet && !getenv("MKE2FS_SKIP_CHECK_MSG")) print_check_message(fs); val = ext2fs_close(fs); return (retval || val) ? 1 : 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/Kbuild.src0000644000000000000000000000073112263563520020400 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT lib-$(CONFIG_CHATTR) += chattr.o lib-$(CONFIG_E2FSCK) += e2fsck.o util.o lib-$(CONFIG_FSCK) += fsck.o util.o lib-$(CONFIG_LSATTR) += lsattr.o lib-$(CONFIG_MKE2FS) += mke2fs.o util.o lib-$(CONFIG_TUNE2FS) += tune2fs.o util.o CFLAGS += -include $(srctree)/e2fsprogs/e2fsbb.h busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2fsbb.h0000644000000000000000000000164212263563520017773 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * File: e2fsbb.h * * Redefine a bunch of e2fsprogs stuff to use busybox routines * instead. This makes upgrade between e2fsprogs versions easy. */ #ifndef E2FSBB_H #define E2FSBB_H 1 #include "libbb.h" /* version we've last synced against */ #define E2FSPROGS_VERSION "1.38" #define E2FSPROGS_DATE "30-Jun-2005" typedef long errcode_t; #define ERRCODE_RANGE 8 #define error_message(code) strerror((int) (code & ((1< #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct read_bb_record { ext2_badblocks_list bb_list; errcode_t err; }; /* * Helper function for ext2fs_read_bb_inode() */ #ifdef __TURBOC__ # pragma argsused #endif static int mark_bad_block(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct read_bb_record *rb = (struct read_bb_record *) priv_data; if (blockcnt < 0) return 0; if ((*block_nr < fs->super->s_first_data_block) || (*block_nr >= fs->super->s_blocks_count)) return 0; /* Ignore illegal blocks */ rb->err = ext2fs_badblocks_list_add(rb->bb_list, *block_nr); if (rb->err) return BLOCK_ABORT; return 0; } /* * Reads the current bad blocks from the bad blocks inode. */ errcode_t ext2fs_read_bb_inode(ext2_filsys fs, ext2_badblocks_list *bb_list) { errcode_t retval; struct read_bb_record rb; struct ext2_inode inode; blk_t numblocks; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!*bb_list) { retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode); if (retval) return retval; if (inode.i_blocks < 500) numblocks = (inode.i_blocks / (fs->blocksize / 512)) + 20; else numblocks = 500; retval = ext2fs_badblocks_list_create(bb_list, numblocks); if (retval) return retval; } rb.bb_list = *bb_list; rb.err = 0; retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, 0, 0, mark_bad_block, &rb); if (retval) return retval; return rb.err; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/brel.h0000644000000000000000000000410212263563520020761 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * brel.h * * Copyright (C) 1996, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ struct ext2_block_relocate_entry { blk_t new; __s16 offset; __u16 flags; union { blk_t block_ref; ext2_ino_t inode_ref; } owner; }; #define RELOCATE_TYPE_REF 0x0007 #define RELOCATE_BLOCK_REF 0x0001 #define RELOCATE_INODE_REF 0x0002 typedef struct ext2_block_relocation_table *ext2_brel; struct ext2_block_relocation_table { __u32 magic; char *name; blk_t current; void *priv_data; /* * Add a block relocation entry. */ errcode_t (*put)(ext2_brel brel, blk_t old, struct ext2_block_relocate_entry *ent); /* * Get a block relocation entry. */ errcode_t (*get)(ext2_brel brel, blk_t old, struct ext2_block_relocate_entry *ent); /* * Initialize for iterating over the block relocation entries. */ errcode_t (*start_iter)(ext2_brel brel); /* * The iterator function for the inode relocation entries. * Returns an inode number of 0 when out of entries. */ errcode_t (*next)(ext2_brel brel, blk_t *old, struct ext2_block_relocate_entry *ent); /* * Move the inode relocation table from one block number to * another. */ errcode_t (*move)(ext2_brel brel, blk_t old, blk_t new); /* * Remove a block relocation entry. */ errcode_t (*delete)(ext2_brel brel, blk_t old); /* * Free the block relocation table. */ errcode_t (*free)(ext2_brel brel); }; errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block, ext2_brel *brel); #define ext2fs_brel_put(brel, old, ent) ((brel)->put((brel), old, ent)) #define ext2fs_brel_get(brel, old, ent) ((brel)->get((brel), old, ent)) #define ext2fs_brel_start_iter(brel) ((brel)->start_iter((brel))) #define ext2fs_brel_next(brel, old, ent) ((brel)->next((brel), old, ent)) #define ext2fs_brel_move(brel, old, new) ((brel)->move((brel), old, new)) #define ext2fs_brel_delete(brel, old) ((brel)->delete((brel), old)) #define ext2fs_brel_free(brel) ((brel)->free((brel))) busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/block.c0000644000000000000000000002525512263563520021136 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * block.c --- iterate over all blocks in an inode * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct block_context { ext2_filsys fs; int (*func)(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t bcount, blk_t ref_blk, int ref_offset, void *priv_data); e2_blkcnt_t bcount; int bsize; int flags; errcode_t errcode; char *ind_buf; char *dind_buf; char *tind_buf; void *priv_data; }; static int block_iterate_ind(blk_t *ind_block, blk_t ref_block, int ref_offset, struct block_context *ctx) { int ret = 0, changed = 0; int i, flags, limit, offset; blk_t *block_nr; limit = ctx->fs->blocksize >> 2; if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) ret = (*ctx->func)(ctx->fs, ind_block, BLOCK_COUNT_IND, ref_block, ref_offset, ctx->priv_data); if (!*ind_block || (ret & BLOCK_ABORT)) { ctx->bcount += limit; return ret; } if (*ind_block >= ctx->fs->super->s_blocks_count || *ind_block < ctx->fs->super->s_first_data_block) { ctx->errcode = EXT2_ET_BAD_IND_BLOCK; ret |= BLOCK_ERROR; return ret; } ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block, ctx->ind_buf); if (ctx->errcode) { ret |= BLOCK_ERROR; return ret; } block_nr = (blk_t *) ctx->ind_buf; offset = 0; if (ctx->flags & BLOCK_FLAG_APPEND) { for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount, *ind_block, offset, ctx->priv_data); changed |= flags; if (flags & BLOCK_ABORT) { ret |= BLOCK_ABORT; break; } offset += sizeof(blk_t); } } else { for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { if (*block_nr == 0) continue; flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount, *ind_block, offset, ctx->priv_data); changed |= flags; if (flags & BLOCK_ABORT) { ret |= BLOCK_ABORT; break; } offset += sizeof(blk_t); } } if (changed & BLOCK_CHANGED) { ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block, ctx->ind_buf); if (ctx->errcode) ret |= BLOCK_ERROR | BLOCK_ABORT; } if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && !(ret & BLOCK_ABORT)) ret |= (*ctx->func)(ctx->fs, ind_block, BLOCK_COUNT_IND, ref_block, ref_offset, ctx->priv_data); return ret; } static int block_iterate_dind(blk_t *dind_block, blk_t ref_block, int ref_offset, struct block_context *ctx) { int ret = 0, changed = 0; int i, flags, limit, offset; blk_t *block_nr; limit = ctx->fs->blocksize >> 2; if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | BLOCK_FLAG_DATA_ONLY))) ret = (*ctx->func)(ctx->fs, dind_block, BLOCK_COUNT_DIND, ref_block, ref_offset, ctx->priv_data); if (!*dind_block || (ret & BLOCK_ABORT)) { ctx->bcount += limit*limit; return ret; } if (*dind_block >= ctx->fs->super->s_blocks_count || *dind_block < ctx->fs->super->s_first_data_block) { ctx->errcode = EXT2_ET_BAD_DIND_BLOCK; ret |= BLOCK_ERROR; return ret; } ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block, ctx->dind_buf); if (ctx->errcode) { ret |= BLOCK_ERROR; return ret; } block_nr = (blk_t *) ctx->dind_buf; offset = 0; if (ctx->flags & BLOCK_FLAG_APPEND) { for (i = 0; i < limit; i++, block_nr++) { flags = block_iterate_ind(block_nr, *dind_block, offset, ctx); changed |= flags; if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); break; } offset += sizeof(blk_t); } } else { for (i = 0; i < limit; i++, block_nr++) { if (*block_nr == 0) { ctx->bcount += limit; continue; } flags = block_iterate_ind(block_nr, *dind_block, offset, ctx); changed |= flags; if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); break; } offset += sizeof(blk_t); } } if (changed & BLOCK_CHANGED) { ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block, ctx->dind_buf); if (ctx->errcode) ret |= BLOCK_ERROR | BLOCK_ABORT; } if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && !(ret & BLOCK_ABORT)) ret |= (*ctx->func)(ctx->fs, dind_block, BLOCK_COUNT_DIND, ref_block, ref_offset, ctx->priv_data); return ret; } static int block_iterate_tind(blk_t *tind_block, blk_t ref_block, int ref_offset, struct block_context *ctx) { int ret = 0, changed = 0; int i, flags, limit, offset; blk_t *block_nr; limit = ctx->fs->blocksize >> 2; if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | BLOCK_FLAG_DATA_ONLY))) ret = (*ctx->func)(ctx->fs, tind_block, BLOCK_COUNT_TIND, ref_block, ref_offset, ctx->priv_data); if (!*tind_block || (ret & BLOCK_ABORT)) { ctx->bcount += limit*limit*limit; return ret; } if (*tind_block >= ctx->fs->super->s_blocks_count || *tind_block < ctx->fs->super->s_first_data_block) { ctx->errcode = EXT2_ET_BAD_TIND_BLOCK; ret |= BLOCK_ERROR; return ret; } ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block, ctx->tind_buf); if (ctx->errcode) { ret |= BLOCK_ERROR; return ret; } block_nr = (blk_t *) ctx->tind_buf; offset = 0; if (ctx->flags & BLOCK_FLAG_APPEND) { for (i = 0; i < limit; i++, block_nr++) { flags = block_iterate_dind(block_nr, *tind_block, offset, ctx); changed |= flags; if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); break; } offset += sizeof(blk_t); } } else { for (i = 0; i < limit; i++, block_nr++) { if (*block_nr == 0) { ctx->bcount += limit*limit; continue; } flags = block_iterate_dind(block_nr, *tind_block, offset, ctx); changed |= flags; if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); break; } offset += sizeof(blk_t); } } if (changed & BLOCK_CHANGED) { ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block, ctx->tind_buf); if (ctx->errcode) ret |= BLOCK_ERROR | BLOCK_ABORT; } if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && !(ret & BLOCK_ABORT)) ret |= (*ctx->func)(ctx->fs, tind_block, BLOCK_COUNT_TIND, ref_block, ref_offset, ctx->priv_data); return ret; } errcode_t ext2fs_block_iterate2(ext2_filsys fs, ext2_ino_t ino, int flags, char *block_buf, int (*func)(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_blk, int ref_offset, void *priv_data), void *priv_data) { int i; int got_inode = 0; int ret = 0; blk_t blocks[EXT2_N_BLOCKS]; /* directory data blocks */ struct ext2_inode inode; errcode_t retval; struct block_context ctx; int limit; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); /* * Check to see if we need to limit large files */ if (flags & BLOCK_FLAG_NO_LARGE) { ctx.errcode = ext2fs_read_inode(fs, ino, &inode); if (ctx.errcode) return ctx.errcode; got_inode = 1; if (!LINUX_S_ISDIR(inode.i_mode) && (inode.i_size_high != 0)) return EXT2_ET_FILE_TOO_BIG; } retval = ext2fs_get_blocks(fs, ino, blocks); if (retval) return retval; limit = fs->blocksize >> 2; ctx.fs = fs; ctx.func = func; ctx.priv_data = priv_data; ctx.flags = flags; ctx.bcount = 0; if (block_buf) { ctx.ind_buf = block_buf; } else { retval = ext2fs_get_mem(fs->blocksize * 3, &ctx.ind_buf); if (retval) return retval; } ctx.dind_buf = ctx.ind_buf + fs->blocksize; ctx.tind_buf = ctx.dind_buf + fs->blocksize; /* * Iterate over the HURD translator block (if present) */ if ((fs->super->s_creator_os == EXT2_OS_HURD) && !(flags & BLOCK_FLAG_DATA_ONLY)) { ctx.errcode = ext2fs_read_inode(fs, ino, &inode); if (ctx.errcode) goto abort_exit; got_inode = 1; if (inode.osd1.hurd1.h_i_translator) { ret |= (*ctx.func)(fs, &inode.osd1.hurd1.h_i_translator, BLOCK_COUNT_TRANSLATOR, 0, 0, priv_data); if (ret & BLOCK_ABORT) goto abort_exit; } } /* * Iterate over normal data blocks */ for (i = 0; i < EXT2_NDIR_BLOCKS; i++, ctx.bcount++) { if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) { ret |= (*ctx.func)(fs, &blocks[i], ctx.bcount, 0, i, priv_data); if (ret & BLOCK_ABORT) goto abort_exit; } } if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) { ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK, 0, EXT2_IND_BLOCK, &ctx); if (ret & BLOCK_ABORT) goto abort_exit; } else ctx.bcount += limit; if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) { ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK, 0, EXT2_DIND_BLOCK, &ctx); if (ret & BLOCK_ABORT) goto abort_exit; } else ctx.bcount += limit * limit; if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) { ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK, 0, EXT2_TIND_BLOCK, &ctx); if (ret & BLOCK_ABORT) goto abort_exit; } abort_exit: if (ret & BLOCK_CHANGED) { if (!got_inode) { retval = ext2fs_read_inode(fs, ino, &inode); if (retval) return retval; } for (i=0; i < EXT2_N_BLOCKS; i++) inode.i_block[i] = blocks[i]; retval = ext2fs_write_inode(fs, ino, &inode); if (retval) return retval; } if (!block_buf) ext2fs_free_mem(&ctx.ind_buf); return (ret & BLOCK_ERROR) ? ctx.errcode : 0; } /* * Emulate the old ext2fs_block_iterate function! */ struct xlate { int (*func)(ext2_filsys fs, blk_t *blocknr, int bcount, void *priv_data); void *real_private; }; #ifdef __TURBOC__ # pragma argsused #endif static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct xlate *xl = (struct xlate *) priv_data; return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private); } errcode_t ext2fs_block_iterate(ext2_filsys fs, ext2_ino_t ino, int flags, char *block_buf, int (*func)(ext2_filsys fs, blk_t *blocknr, int blockcnt, void *priv_data), void *priv_data) { struct xlate xl; xl.real_private = priv_data; xl.func = func; return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags, block_buf, xlate_func, &xl); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/dblist_dir.c0000644000000000000000000000303612263563520022154 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * dblist_dir.c --- iterate by directory entry * * Copyright 1997 by Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% * */ #include #if HAVE_UNISTD_H #include #endif #include #include #include "ext2_fs.h" #include "ext2fsP.h" static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info, void *priv_data); errcode_t ext2fs_dblist_dir_iterate(ext2_dblist dblist, int flags, char *block_buf, int (*func)(ext2_ino_t dir, int entry, struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data), void *priv_data) { errcode_t retval; struct dir_context ctx; EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); ctx.dir = 0; ctx.flags = flags; if (block_buf) ctx.buf = block_buf; else { retval = ext2fs_get_mem(dblist->fs->blocksize, &ctx.buf); if (retval) return retval; } ctx.func = func; ctx.priv_data = priv_data; ctx.errcode = 0; retval = ext2fs_dblist_iterate(dblist, db_dir_proc, &ctx); if (!block_buf) ext2fs_free_mem(&ctx.buf); if (retval) return retval; return ctx.errcode; } static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info, void *priv_data) { struct dir_context *ctx; ctx = (struct dir_context *) priv_data; ctx->dir = db_info->ino; return ext2fs_process_dir_block(fs, &db_info->blk, db_info->blockcnt, 0, 0, priv_data); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c0000644000000000000000000001502012263563520021616 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * inode_io.c --- This is allows an inode in an ext2 filesystem image * to be accessed via the I/O manager interface. * * Copyright (C) 2002 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #include "ext2_fs.h" #include "ext2fs.h" /* * For checking structure magic numbers... */ #define EXT2_CHECK_MAGIC(struct, code) \ if ((struct)->magic != (code)) return (code) struct inode_private_data { int magic; char name[32]; ext2_file_t file; ext2_filsys fs; ext2_ino_t ino; struct ext2_inode inode; int flags; struct inode_private_data *next; }; #define CHANNEL_HAS_INODE 0x8000 static struct inode_private_data *top_intern; static int ino_unique = 0; static errcode_t inode_open(const char *name, int flags, io_channel *channel); static errcode_t inode_close(io_channel channel); static errcode_t inode_set_blksize(io_channel channel, int blksize); static errcode_t inode_read_blk(io_channel channel, unsigned long block, int count, void *data); static errcode_t inode_write_blk(io_channel channel, unsigned long block, int count, const void *data); static errcode_t inode_flush(io_channel channel); static errcode_t inode_write_byte(io_channel channel, unsigned long offset, int size, const void *data); static struct struct_io_manager struct_inode_manager = { EXT2_ET_MAGIC_IO_MANAGER, "Inode I/O Manager", inode_open, inode_close, inode_set_blksize, inode_read_blk, inode_write_blk, inode_flush, inode_write_byte }; io_manager inode_io_manager = &struct_inode_manager; errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, char **name) { struct inode_private_data *data; errcode_t retval; if ((retval = ext2fs_get_mem(sizeof(struct inode_private_data), &data))) return retval; data->magic = EXT2_ET_MAGIC_INODE_IO_CHANNEL; sprintf(data->name, "%u:%d", ino, ino_unique++); data->file = 0; data->fs = fs; data->ino = ino; data->flags = 0; if (inode) { memcpy(&data->inode, inode, sizeof(struct ext2_inode)); data->flags |= CHANNEL_HAS_INODE; } data->next = top_intern; top_intern = data; *name = data->name; return 0; } errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino, char **name) { return ext2fs_inode_io_intern2(fs, ino, NULL, name); } static errcode_t inode_open(const char *name, int flags, io_channel *channel) { io_channel io = NULL; struct inode_private_data *prev, *data = NULL; errcode_t retval; int open_flags; if (name == 0) return EXT2_ET_BAD_DEVICE_NAME; for (data = top_intern, prev = NULL; data; prev = data, data = data->next) if (strcmp(name, data->name) == 0) break; if (!data) return ENOENT; if (prev) prev->next = data->next; else top_intern = data->next; retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); if (retval) goto cleanup; memset(io, 0, sizeof(struct struct_io_channel)); io->magic = EXT2_ET_MAGIC_IO_CHANNEL; io->manager = inode_io_manager; retval = ext2fs_get_mem(strlen(name)+1, &io->name); if (retval) goto cleanup; strcpy(io->name, name); io->private_data = data; io->block_size = 1024; io->read_error = 0; io->write_error = 0; io->refcount = 1; open_flags = (flags & IO_FLAG_RW) ? EXT2_FILE_WRITE : 0; retval = ext2fs_file_open2(data->fs, data->ino, (data->flags & CHANNEL_HAS_INODE) ? &data->inode : 0, open_flags, &data->file); if (retval) goto cleanup; *channel = io; return 0; cleanup: if (data) { ext2fs_free_mem(&data); } if (io) ext2fs_free_mem(&io); return retval; } static errcode_t inode_close(io_channel channel) { struct inode_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct inode_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); if (--channel->refcount > 0) return 0; retval = ext2fs_file_close(data->file); ext2fs_free_mem(&channel->private_data); if (channel->name) ext2fs_free_mem(&channel->name); ext2fs_free_mem(&channel); return retval; } static errcode_t inode_set_blksize(io_channel channel, int blksize) { struct inode_private_data *data; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct inode_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); channel->block_size = blksize; return 0; } static errcode_t inode_read_blk(io_channel channel, unsigned long block, int count, void *buf) { struct inode_private_data *data; errcode_t retval; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct inode_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); if ((retval = ext2fs_file_lseek(data->file, block * channel->block_size, EXT2_SEEK_SET, 0))) return retval; count = (count < 0) ? -count : (count * channel->block_size); return ext2fs_file_read(data->file, buf, count, 0); } static errcode_t inode_write_blk(io_channel channel, unsigned long block, int count, const void *buf) { struct inode_private_data *data; errcode_t retval; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct inode_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); if ((retval = ext2fs_file_lseek(data->file, block * channel->block_size, EXT2_SEEK_SET, 0))) return retval; count = (count < 0) ? -count : (count * channel->block_size); return ext2fs_file_write(data->file, buf, count, 0); } static errcode_t inode_write_byte(io_channel channel, unsigned long offset, int size, const void *buf) { struct inode_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct inode_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); if ((retval = ext2fs_file_lseek(data->file, offset, EXT2_SEEK_SET, 0))) return retval; return ext2fs_file_write(data->file, buf, size, 0); } /* * Flush data buffers to disk. */ static errcode_t inode_flush(io_channel channel) { struct inode_private_data *data; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct inode_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); return ext2fs_file_flush(data->file); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/inline.c0000644000000000000000000000123112263563520021306 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * inline.c --- Includes the inlined functions defined in the header * files as standalone functions, in case the application program * is compiled with inlining turned off. * * Copyright (C) 1993, 1994 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #define INCLUDE_INLINE_FUNCS #include "ext2fs.h" busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/ext2_io.h0000644000000000000000000000612712263563520021417 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * io.h --- the I/O manager abstraction * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #ifndef EXT2FS_EXT2_IO_H #define EXT2FS_EXT2_IO_H 1 /* * ext2_loff_t is defined here since unix_io.c needs it. */ #if defined(__GNUC__) || defined(HAS_LONG_LONG) typedef long long ext2_loff_t; #else typedef long ext2_loff_t; #endif /* llseek.c */ /* ext2_loff_t ext2fs_llseek (int, ext2_loff_t, int); */ #ifdef CONFIG_LFS # define ext2fs_llseek lseek64 #else # define ext2fs_llseek lseek #endif typedef struct struct_io_manager *io_manager; typedef struct struct_io_channel *io_channel; #define CHANNEL_FLAGS_WRITETHROUGH 0x01 struct struct_io_channel { errcode_t magic; io_manager manager; char *name; int block_size; errcode_t (*read_error)(io_channel channel, unsigned long block, int count, void *data, size_t size, int actual_bytes_read, errcode_t error); errcode_t (*write_error)(io_channel channel, unsigned long block, int count, const void *data, size_t size, int actual_bytes_written, errcode_t error); int refcount; int flags; int reserved[14]; void *private_data; void *app_data; }; struct struct_io_manager { errcode_t magic; const char *name; errcode_t (*open)(const char *name, int flags, io_channel *channel); errcode_t (*close)(io_channel channel); errcode_t (*set_blksize)(io_channel channel, int blksize); errcode_t (*read_blk)(io_channel channel, unsigned long block, int count, void *data); errcode_t (*write_blk)(io_channel channel, unsigned long block, int count, const void *data); errcode_t (*flush)(io_channel channel); errcode_t (*write_byte)(io_channel channel, unsigned long offset, int count, const void *data); errcode_t (*set_option)(io_channel channel, const char *option, const char *arg); int reserved[14]; }; #define IO_FLAG_RW 1 /* * Convenience functions.... */ #define io_channel_close(c) ((c)->manager->close((c))) #define io_channel_set_blksize(c,s) ((c)->manager->set_blksize((c),s)) #define io_channel_read_blk(c,b,n,d) ((c)->manager->read_blk((c),b,n,d)) #define io_channel_write_blk(c,b,n,d) ((c)->manager->write_blk((c),b,n,d)) #define io_channel_flush(c) ((c)->manager->flush((c))) #define io_channel_bumpcount(c) ((c)->refcount++) /* io_manager.c */ extern errcode_t io_channel_set_options(io_channel channel, const char *options); extern errcode_t io_channel_write_byte(io_channel channel, unsigned long offset, int count, const void *data); /* unix_io.c */ extern io_manager unix_io_manager; /* test_io.c */ extern io_manager test_io_manager, test_io_backing_manager; extern void (*test_io_cb_read_blk) (unsigned long block, int count, errcode_t err); extern void (*test_io_cb_write_blk) (unsigned long block, int count, errcode_t err); extern void (*test_io_cb_set_blksize) (int blksize, errcode_t err); #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/namei.c0000644000000000000000000001155112263563520021127 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * namei.c --- ext2fs directory lookup operations * * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif /* #define NAMEI_DEBUG */ #include "ext2_fs.h" #include "ext2fs.h" static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base, const char *pathname, size_t pathlen, int follow, int link_count, char *buf, ext2_ino_t *res_inode); static errcode_t follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir, ext2_ino_t inode, int link_count, char *buf, ext2_ino_t *res_inode) { char *pathname; char *buffer = NULL; errcode_t retval; struct ext2_inode ei; #ifdef NAMEI_DEBUG printf("follow_link: root=%lu, dir=%lu, inode=%lu, lc=%d\n", root, dir, inode, link_count); #endif retval = ext2fs_read_inode (fs, inode, &ei); if (retval) return retval; if (!LINUX_S_ISLNK (ei.i_mode)) { *res_inode = inode; return 0; } if (link_count++ > 5) { return EXT2_ET_SYMLINK_LOOP; } if (ext2fs_inode_data_blocks(fs, &ei)) { retval = ext2fs_get_mem(fs->blocksize, &buffer); if (retval) return retval; retval = io_channel_read_blk(fs->io, ei.i_block[0], 1, buffer); if (retval) { ext2fs_free_mem(&buffer); return retval; } pathname = buffer; } else pathname = (char *)&(ei.i_block[0]); retval = open_namei(fs, root, dir, pathname, ei.i_size, 1, link_count, buf, res_inode); ext2fs_free_mem(&buffer); return retval; } /* * This routine interprets a pathname in the context of the current * directory and the root directory, and returns the inode of the * containing directory, and a pointer to the filename of the file * (pointing into the pathname) and the length of the filename. */ static errcode_t dir_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir, const char *pathname, int pathlen, int link_count, char *buf, const char **name, int *namelen, ext2_ino_t *res_inode) { char c; const char *thisname; int len; ext2_ino_t inode; errcode_t retval; if ((c = *pathname) == '/') { dir = root; pathname++; pathlen--; } while (1) { thisname = pathname; for (len=0; --pathlen >= 0;len++) { c = *(pathname++); if (c == '/') break; } if (pathlen < 0) break; retval = ext2fs_lookup (fs, dir, thisname, len, buf, &inode); if (retval) return retval; retval = follow_link (fs, root, dir, inode, link_count, buf, &dir); if (retval) return retval; } *name = thisname; *namelen = len; *res_inode = dir; return 0; } static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base, const char *pathname, size_t pathlen, int follow, int link_count, char *buf, ext2_ino_t *res_inode) { const char *basename; int namelen; ext2_ino_t dir, inode; errcode_t retval; #ifdef NAMEI_DEBUG printf("open_namei: root=%lu, dir=%lu, path=%*s, lc=%d\n", root, base, pathlen, pathname, link_count); #endif retval = dir_namei(fs, root, base, pathname, pathlen, link_count, buf, &basename, &namelen, &dir); if (retval) return retval; if (!namelen) { /* special case: '/usr/' etc */ *res_inode=dir; return 0; } retval = ext2fs_lookup (fs, dir, basename, namelen, buf, &inode); if (retval) return retval; if (follow) { retval = follow_link(fs, root, dir, inode, link_count, buf, &inode); if (retval) return retval; } #ifdef NAMEI_DEBUG printf("open_namei: (link_count=%d) returns %lu\n", link_count, inode); #endif *res_inode = inode; return 0; } errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, const char *name, ext2_ino_t *inode) { char *buf; errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; retval = open_namei(fs, root, cwd, name, strlen(name), 0, 0, buf, inode); ext2fs_free_mem(&buf); return retval; } errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, const char *name, ext2_ino_t *inode) { char *buf; errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; retval = open_namei(fs, root, cwd, name, strlen(name), 1, 0, buf, inode); ext2fs_free_mem(&buf); return retval; } errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, ext2_ino_t inode, ext2_ino_t *res_inode) { char *buf; errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; retval = follow_link(fs, root, cwd, inode, 0, buf, res_inode); ext2fs_free_mem(&buf); return retval; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/valid_blk.c0000644000000000000000000000251612263563520021766 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * valid_blk.c --- does the inode have valid blocks? * * Copyright 1997 by Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% * */ #include #if HAVE_UNISTD_H #include #endif #include #include #include "ext2_fs.h" #include "ext2fs.h" /* * This function returns 1 if the inode's block entries actually * contain block entries. */ int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode) { /* * Only directories, regular files, and some symbolic links * have valid block entries. */ if (!LINUX_S_ISDIR(inode->i_mode) && !LINUX_S_ISREG(inode->i_mode) && !LINUX_S_ISLNK(inode->i_mode)) return 0; /* * If the symbolic link is a "fast symlink", then the symlink * target is stored in the block entries. */ if (LINUX_S_ISLNK (inode->i_mode)) { if (inode->i_file_acl == 0) { /* With no EA block, we can rely on i_blocks */ if (inode->i_blocks == 0) return 0; } else { /* With an EA block, life gets more tricky */ if (inode->i_size >= EXT2_N_BLOCKS*4) return 1; /* definitely using i_block[] */ if (inode->i_size > 4 && inode->i_block[1] == 0) return 1; /* definitely using i_block[] */ return 0; /* Probably a fast symlink */ } } return 1; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/alloc_stats.c0000644000000000000000000000244012263563520022343 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * alloc_stats.c --- Update allocation statistics for ext2fs * * Copyright (C) 2001 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% * */ #include #include "ext2_fs.h" #include "ext2fs.h" void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino, int inuse, int isdir) { int group = ext2fs_group_of_ino(fs, ino); if (inuse > 0) ext2fs_mark_inode_bitmap(fs->inode_map, ino); else ext2fs_unmark_inode_bitmap(fs->inode_map, ino); fs->group_desc[group].bg_free_inodes_count -= inuse; if (isdir) fs->group_desc[group].bg_used_dirs_count += inuse; fs->super->s_free_inodes_count -= inuse; ext2fs_mark_super_dirty(fs); ext2fs_mark_ib_dirty(fs); } void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse) { ext2fs_inode_alloc_stats2(fs, ino, inuse, 0); } void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse) { int group = ext2fs_group_of_blk(fs, blk); if (inuse > 0) ext2fs_mark_block_bitmap(fs->block_map, blk); else ext2fs_unmark_block_bitmap(fs->block_map, blk); fs->group_desc[group].bg_free_blocks_count -= inuse; fs->super->s_free_blocks_count -= inuse; ext2fs_mark_super_dirty(fs); ext2fs_mark_bb_dirty(fs); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/write_bb_file.c0000644000000000000000000000136512263563520022634 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * write_bb_file.c --- write a list of bad blocks to a FILE * * * Copyright (C) 1994, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include "ext2_fs.h" #include "ext2fs.h" errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list, unsigned int flags EXT2FS_ATTR((unused)), FILE *f) { badblocks_iterate bb_iter; blk_t blk; errcode_t retval; retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter); if (retval) return retval; while (ext2fs_badblocks_list_iterate(bb_iter, &blk)) { fprintf(f, "%d\n", blk); } ext2fs_badblocks_list_iterate_end(bb_iter); return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/initialize.c0000644000000000000000000002672012263563520022203 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * initialize.c --- initialize a filesystem handle given superblock * parameters. Used by mke2fs when initializing a filesystem. * * Copyright (C) 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #if defined(__linux__) && defined(EXT2_OS_LINUX) #define CREATOR_OS EXT2_OS_LINUX #else #if defined(__GNU__) && defined(EXT2_OS_HURD) #define CREATOR_OS EXT2_OS_HURD #else #if defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) #define CREATOR_OS EXT2_OS_FREEBSD #else #if defined(LITES) && defined(EXT2_OS_LITES) #define CREATOR_OS EXT2_OS_LITES #else #define CREATOR_OS EXT2_OS_LINUX /* by default */ #endif /* defined(LITES) && defined(EXT2_OS_LITES) */ #endif /* defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) */ #endif /* defined(__GNU__) && defined(EXT2_OS_HURD) */ #endif /* defined(__linux__) && defined(EXT2_OS_LINUX) */ /* * Note we override the kernel include file's idea of what the default * check interval (never) should be. It's a good idea to check at * least *occasionally*, specially since servers will never rarely get * to reboot, since Linux is so robust these days. :-) * * 180 days (six months) seems like a good value. */ #ifdef EXT2_DFL_CHECKINTERVAL #undef EXT2_DFL_CHECKINTERVAL #endif #define EXT2_DFL_CHECKINTERVAL (86400L * 180L) /* * Calculate the number of GDT blocks to reserve for online filesystem growth. * The absolute maximum number of GDT blocks we can reserve is determined by * the number of block pointers that can fit into a single block. */ static int calc_reserved_gdt_blocks(ext2_filsys fs) { struct ext2_super_block *sb = fs->super; unsigned long bpg = sb->s_blocks_per_group; unsigned int gdpb = fs->blocksize / sizeof(struct ext2_group_desc); unsigned long max_blocks = 0xffffffff; unsigned long rsv_groups; int rsv_gdb; /* We set it at 1024x the current filesystem size, or * the upper block count limit (2^32), whichever is lower. */ if (sb->s_blocks_count < max_blocks / 1024) max_blocks = sb->s_blocks_count * 1024; rsv_groups = (max_blocks - sb->s_first_data_block + bpg - 1) / bpg; rsv_gdb = (rsv_groups + gdpb - 1) / gdpb - fs->desc_blocks; if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb)) rsv_gdb = EXT2_ADDR_PER_BLOCK(sb); #ifdef RES_GDT_DEBUG printf("max_blocks %lu, rsv_groups = %lu, rsv_gdb = %u\n", max_blocks, rsv_groups, rsv_gdb); #endif return rsv_gdb; } errcode_t ext2fs_initialize(const char *name, int flags, struct ext2_super_block *param, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; struct ext2_super_block *super; int frags_per_block; unsigned int rem; unsigned int overhead = 0; blk_t group_block; unsigned int ipg; dgrp_t i; blk_t numblocks; int rsv_gdt; char *buf; if (!param || !param->s_blocks_count) return EXT2_ET_INVALID_ARGUMENT; retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); if (retval) return retval; memset(fs, 0, sizeof(struct struct_ext2_filsys)); fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; fs->flags = flags | EXT2_FLAG_RW; fs->umask = 022; #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; #endif retval = manager->open(name, IO_FLAG_RW, &fs->io); if (retval) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super); if (retval) goto cleanup; fs->super = super; memset(super, 0, SUPERBLOCK_SIZE); #define set_field(field, default) (super->field = param->field ? \ param->field : (default)) super->s_magic = EXT2_SUPER_MAGIC; super->s_state = EXT2_VALID_FS; set_field(s_log_block_size, 0); /* default blocksize: 1024 bytes */ set_field(s_log_frag_size, 0); /* default fragsize: 1024 bytes */ set_field(s_first_data_block, super->s_log_block_size ? 0 : 1); set_field(s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT); set_field(s_errors, EXT2_ERRORS_DEFAULT); set_field(s_feature_compat, 0); set_field(s_feature_incompat, 0); set_field(s_feature_ro_compat, 0); set_field(s_first_meta_bg, 0); if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { retval = EXT2_ET_RO_UNSUPP_FEATURE; goto cleanup; } set_field(s_rev_level, EXT2_GOOD_OLD_REV); if (super->s_rev_level >= EXT2_DYNAMIC_REV) { set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO); set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE); } set_field(s_checkinterval, EXT2_DFL_CHECKINTERVAL); super->s_mkfs_time = super->s_lastcheck = time(NULL); super->s_creator_os = CREATOR_OS; fs->blocksize = EXT2_BLOCK_SIZE(super); fs->fragsize = EXT2_FRAG_SIZE(super); frags_per_block = fs->blocksize / fs->fragsize; /* default: (fs->blocksize*8) blocks/group, up to 2^16 (GDT limit) */ set_field(s_blocks_per_group, fs->blocksize * 8); if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super)) super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super); super->s_frags_per_group = super->s_blocks_per_group * frags_per_block; super->s_blocks_count = param->s_blocks_count; super->s_r_blocks_count = param->s_r_blocks_count; if (super->s_r_blocks_count >= param->s_blocks_count) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } /* * If we're creating an external journal device, we don't need * to bother with the rest. */ if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { fs->group_desc_count = 0; ext2fs_mark_super_dirty(fs); *ret_fs = fs; return 0; } retry: fs->group_desc_count = (super->s_blocks_count - super->s_first_data_block + EXT2_BLOCKS_PER_GROUP(super) - 1) / EXT2_BLOCKS_PER_GROUP(super); if (fs->group_desc_count == 0) { retval = EXT2_ET_TOOSMALL; goto cleanup; } fs->desc_blocks = (fs->group_desc_count + EXT2_DESC_PER_BLOCK(super) - 1) / EXT2_DESC_PER_BLOCK(super); i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize; set_field(s_inodes_count, super->s_blocks_count / i); /* * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so * that we have enough inodes for the filesystem(!) */ if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1) super->s_inodes_count = EXT2_FIRST_INODE(super)+1; /* * There should be at least as many inodes as the user * requested. Figure out how many inodes per group that * should be. But make sure that we don't allocate more than * one bitmap's worth of inodes each group. */ ipg = (super->s_inodes_count + fs->group_desc_count - 1) / fs->group_desc_count; if (ipg > fs->blocksize * 8) { if (super->s_blocks_per_group >= 256) { /* Try again with slightly different parameters */ super->s_blocks_per_group -= 8; super->s_blocks_count = param->s_blocks_count; super->s_frags_per_group = super->s_blocks_per_group * frags_per_block; goto retry; } else return EXT2_ET_TOO_MANY_INODES; } if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super)) ipg = EXT2_MAX_INODES_PER_GROUP(super); super->s_inodes_per_group = ipg; if (super->s_inodes_count > ipg * fs->group_desc_count) super->s_inodes_count = ipg * fs->group_desc_count; /* * Make sure the number of inodes per group completely fills * the inode table blocks in the descriptor. If not, add some * additional inodes/group. Waste not, want not... */ fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); super->s_inodes_per_group = ((fs->inode_blocks_per_group * EXT2_BLOCK_SIZE(super)) / EXT2_INODE_SIZE(super)); /* * Finally, make sure the number of inodes per group is a * multiple of 8. This is needed to simplify the bitmap * splicing code. */ super->s_inodes_per_group &= ~7; fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); /* * adjust inode count to reflect the adjusted inodes_per_group */ super->s_inodes_count = super->s_inodes_per_group * fs->group_desc_count; super->s_free_inodes_count = super->s_inodes_count; /* * check the number of reserved group descriptor table blocks */ if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INO) rsv_gdt = calc_reserved_gdt_blocks(fs); else rsv_gdt = 0; set_field(s_reserved_gdt_blocks, rsv_gdt); if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) { retval = EXT2_ET_RES_GDT_BLOCKS; goto cleanup; } /* * Overhead is the number of bookkeeping blocks per group. It * includes the superblock backup, the group descriptor * backups, the inode bitmap, the block bitmap, and the inode * table. */ overhead = (int) (2 + fs->inode_blocks_per_group); if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1)) overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks; /* This can only happen if the user requested too many inodes */ if (overhead > super->s_blocks_per_group) return EXT2_ET_TOO_MANY_INODES; /* * See if the last group is big enough to support the * necessary data structures. If not, we need to get rid of * it. */ rem = ((super->s_blocks_count - super->s_first_data_block) % super->s_blocks_per_group); if ((fs->group_desc_count == 1) && rem && (rem < overhead)) return EXT2_ET_TOOSMALL; if (rem && (rem < overhead+50)) { super->s_blocks_count -= rem; goto retry; } /* * At this point we know how big the filesystem will be. So * we can do any and all allocations that depend on the block * count. */ retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) goto cleanup; sprintf(buf, "block bitmap for %s", fs->device_name); retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); if (retval) goto cleanup; sprintf(buf, "inode bitmap for %s", fs->device_name); retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); if (retval) goto cleanup; ext2fs_free_mem(&buf); retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize, &fs->group_desc); if (retval) goto cleanup; memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize); /* * Reserve the superblock and group descriptors for each * group, and fill in the correct group statistics for group. * Note that although the block bitmap, inode bitmap, and * inode table have not been allocated (and in fact won't be * by this routine), they are accounted for nevertheless. */ group_block = super->s_first_data_block; super->s_free_blocks_count = 0; for (i = 0; i < fs->group_desc_count; i++) { numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map); super->s_free_blocks_count += numblocks; fs->group_desc[i].bg_free_blocks_count = numblocks; fs->group_desc[i].bg_free_inodes_count = fs->super->s_inodes_per_group; fs->group_desc[i].bg_used_dirs_count = 0; group_block += super->s_blocks_per_group; } ext2fs_mark_super_dirty(fs); ext2fs_mark_bb_dirty(fs); ext2fs_mark_ib_dirty(fs); io_channel_set_blksize(fs->io, fs->blocksize); *ret_fs = fs; return 0; cleanup: ext2fs_free(fs); return retval; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/bmove.c0000644000000000000000000000674412263563520021156 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * bmove.c --- Move blocks around to make way for a particular * filesystem structure. * * Copyright (C) 1997 Theodore Ts'o. This file may be redistributed * under the terms of the GNU Public License. */ #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fsP.h" struct process_block_struct { ext2_ino_t ino; struct ext2_inode * inode; ext2fs_block_bitmap reserve; ext2fs_block_bitmap alloc_map; errcode_t error; char *buf; int add_dir; int flags; }; static int process_block(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block, int ref_offset, void *priv_data) { struct process_block_struct *pb; errcode_t retval; int ret; blk_t block, orig; pb = (struct process_block_struct *) priv_data; block = orig = *block_nr; ret = 0; /* * Let's see if this is one which we need to relocate */ if (ext2fs_test_block_bitmap(pb->reserve, block)) { do { if (++block >= fs->super->s_blocks_count) block = fs->super->s_first_data_block; if (block == orig) { pb->error = EXT2_ET_BLOCK_ALLOC_FAIL; return BLOCK_ABORT; } } while (ext2fs_test_block_bitmap(pb->reserve, block) || ext2fs_test_block_bitmap(pb->alloc_map, block)); retval = io_channel_read_blk(fs->io, orig, 1, pb->buf); if (retval) { pb->error = retval; return BLOCK_ABORT; } retval = io_channel_write_blk(fs->io, block, 1, pb->buf); if (retval) { pb->error = retval; return BLOCK_ABORT; } *block_nr = block; ext2fs_mark_block_bitmap(pb->alloc_map, block); ret = BLOCK_CHANGED; if (pb->flags & EXT2_BMOVE_DEBUG) printf("ino=%ld, blockcnt=%lld, %d->%d\n", pb->ino, blockcnt, orig, block); } if (pb->add_dir) { retval = ext2fs_add_dir_block(fs->dblist, pb->ino, block, (int) blockcnt); if (retval) { pb->error = retval; ret |= BLOCK_ABORT; } } return ret; } errcode_t ext2fs_move_blocks(ext2_filsys fs, ext2fs_block_bitmap reserve, ext2fs_block_bitmap alloc_map, int flags) { ext2_ino_t ino; struct ext2_inode inode; errcode_t retval; struct process_block_struct pb; ext2_inode_scan scan; char *block_buf; retval = ext2fs_open_inode_scan(fs, 0, &scan); if (retval) return retval; pb.reserve = reserve; pb.error = 0; pb.alloc_map = alloc_map ? alloc_map : fs->block_map; pb.flags = flags; retval = ext2fs_get_mem(fs->blocksize * 4, &block_buf); if (retval) return retval; pb.buf = block_buf + fs->blocksize * 3; /* * If GET_DBLIST is set in the flags field, then we should * gather directory block information while we're doing the * block move. */ if (flags & EXT2_BMOVE_GET_DBLIST) { ext2fs_free_dblist(fs->dblist); fs->dblist = NULL; retval = ext2fs_init_dblist(fs, 0); if (retval) return retval; } retval = ext2fs_get_next_inode(scan, &ino, &inode); if (retval) return retval; while (ino) { if ((inode.i_links_count == 0) || !ext2fs_inode_has_valid_blocks(&inode)) goto next; pb.ino = ino; pb.inode = &inode; pb.add_dir = (LINUX_S_ISDIR(inode.i_mode) && flags & EXT2_BMOVE_GET_DBLIST); retval = ext2fs_block_iterate2(fs, ino, 0, block_buf, process_block, &pb); if (retval) return retval; if (pb.error) return pb.error; next: retval = ext2fs_get_next_inode(scan, &ino, &inode); if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) goto next; } return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c0000644000000000000000000002066512263563520021347 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * swapfs.c --- swap ext2 filesystem data structures * * Copyright (C) 1995, 1996, 2002 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #include #include #include "ext2_fs.h" #include "ext2fs.h" #include "ext2_ext_attr.h" #if BB_BIG_ENDIAN void ext2fs_swap_super(struct ext2_super_block * sb) { int i; sb->s_inodes_count = ext2fs_swab32(sb->s_inodes_count); sb->s_blocks_count = ext2fs_swab32(sb->s_blocks_count); sb->s_r_blocks_count = ext2fs_swab32(sb->s_r_blocks_count); sb->s_free_blocks_count = ext2fs_swab32(sb->s_free_blocks_count); sb->s_free_inodes_count = ext2fs_swab32(sb->s_free_inodes_count); sb->s_first_data_block = ext2fs_swab32(sb->s_first_data_block); sb->s_log_block_size = ext2fs_swab32(sb->s_log_block_size); sb->s_log_frag_size = ext2fs_swab32(sb->s_log_frag_size); sb->s_blocks_per_group = ext2fs_swab32(sb->s_blocks_per_group); sb->s_frags_per_group = ext2fs_swab32(sb->s_frags_per_group); sb->s_inodes_per_group = ext2fs_swab32(sb->s_inodes_per_group); sb->s_mtime = ext2fs_swab32(sb->s_mtime); sb->s_wtime = ext2fs_swab32(sb->s_wtime); sb->s_mnt_count = ext2fs_swab16(sb->s_mnt_count); sb->s_max_mnt_count = ext2fs_swab16(sb->s_max_mnt_count); sb->s_magic = ext2fs_swab16(sb->s_magic); sb->s_state = ext2fs_swab16(sb->s_state); sb->s_errors = ext2fs_swab16(sb->s_errors); sb->s_minor_rev_level = ext2fs_swab16(sb->s_minor_rev_level); sb->s_lastcheck = ext2fs_swab32(sb->s_lastcheck); sb->s_checkinterval = ext2fs_swab32(sb->s_checkinterval); sb->s_creator_os = ext2fs_swab32(sb->s_creator_os); sb->s_rev_level = ext2fs_swab32(sb->s_rev_level); sb->s_def_resuid = ext2fs_swab16(sb->s_def_resuid); sb->s_def_resgid = ext2fs_swab16(sb->s_def_resgid); sb->s_first_ino = ext2fs_swab32(sb->s_first_ino); sb->s_inode_size = ext2fs_swab16(sb->s_inode_size); sb->s_block_group_nr = ext2fs_swab16(sb->s_block_group_nr); sb->s_feature_compat = ext2fs_swab32(sb->s_feature_compat); sb->s_feature_incompat = ext2fs_swab32(sb->s_feature_incompat); sb->s_feature_ro_compat = ext2fs_swab32(sb->s_feature_ro_compat); sb->s_algorithm_usage_bitmap = ext2fs_swab32(sb->s_algorithm_usage_bitmap); sb->s_reserved_gdt_blocks = ext2fs_swab16(sb->s_reserved_gdt_blocks); sb->s_journal_inum = ext2fs_swab32(sb->s_journal_inum); sb->s_journal_dev = ext2fs_swab32(sb->s_journal_dev); sb->s_last_orphan = ext2fs_swab32(sb->s_last_orphan); sb->s_default_mount_opts = ext2fs_swab32(sb->s_default_mount_opts); sb->s_first_meta_bg = ext2fs_swab32(sb->s_first_meta_bg); sb->s_mkfs_time = ext2fs_swab32(sb->s_mkfs_time); for (i=0; i < 4; i++) sb->s_hash_seed[i] = ext2fs_swab32(sb->s_hash_seed[i]); for (i=0; i < 17; i++) sb->s_jnl_blocks[i] = ext2fs_swab32(sb->s_jnl_blocks[i]); } void ext2fs_swap_group_desc(struct ext2_group_desc *gdp) { gdp->bg_block_bitmap = ext2fs_swab32(gdp->bg_block_bitmap); gdp->bg_inode_bitmap = ext2fs_swab32(gdp->bg_inode_bitmap); gdp->bg_inode_table = ext2fs_swab32(gdp->bg_inode_table); gdp->bg_free_blocks_count = ext2fs_swab16(gdp->bg_free_blocks_count); gdp->bg_free_inodes_count = ext2fs_swab16(gdp->bg_free_inodes_count); gdp->bg_used_dirs_count = ext2fs_swab16(gdp->bg_used_dirs_count); } void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header) { struct ext2_ext_attr_header *from_header = (struct ext2_ext_attr_header *)from; struct ext2_ext_attr_header *to_header = (struct ext2_ext_attr_header *)to; struct ext2_ext_attr_entry *from_entry, *to_entry; char *from_end = (char *)from_header + bufsize; int n; if (to_header != from_header) memcpy(to_header, from_header, bufsize); from_entry = (struct ext2_ext_attr_entry *)from_header; to_entry = (struct ext2_ext_attr_entry *)to_header; if (has_header) { to_header->h_magic = ext2fs_swab32(from_header->h_magic); to_header->h_blocks = ext2fs_swab32(from_header->h_blocks); to_header->h_refcount = ext2fs_swab32(from_header->h_refcount); for (n=0; n<4; n++) to_header->h_reserved[n] = ext2fs_swab32(from_header->h_reserved[n]); from_entry = (struct ext2_ext_attr_entry *)(from_header+1); to_entry = (struct ext2_ext_attr_entry *)(to_header+1); } while ((char *)from_entry < from_end && *(__u32 *)from_entry) { to_entry->e_value_offs = ext2fs_swab16(from_entry->e_value_offs); to_entry->e_value_block = ext2fs_swab32(from_entry->e_value_block); to_entry->e_value_size = ext2fs_swab32(from_entry->e_value_size); from_entry = EXT2_EXT_ATTR_NEXT(from_entry); to_entry = EXT2_EXT_ATTR_NEXT(to_entry); } } void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t, struct ext2_inode_large *f, int hostorder, int bufsize) { unsigned i; int islnk = 0; __u32 *eaf, *eat; if (hostorder && LINUX_S_ISLNK(f->i_mode)) islnk = 1; t->i_mode = ext2fs_swab16(f->i_mode); if (!hostorder && LINUX_S_ISLNK(t->i_mode)) islnk = 1; t->i_uid = ext2fs_swab16(f->i_uid); t->i_size = ext2fs_swab32(f->i_size); t->i_atime = ext2fs_swab32(f->i_atime); t->i_ctime = ext2fs_swab32(f->i_ctime); t->i_mtime = ext2fs_swab32(f->i_mtime); t->i_dtime = ext2fs_swab32(f->i_dtime); t->i_gid = ext2fs_swab16(f->i_gid); t->i_links_count = ext2fs_swab16(f->i_links_count); t->i_blocks = ext2fs_swab32(f->i_blocks); t->i_flags = ext2fs_swab32(f->i_flags); t->i_file_acl = ext2fs_swab32(f->i_file_acl); t->i_dir_acl = ext2fs_swab32(f->i_dir_acl); if (!islnk || ext2fs_inode_data_blocks(fs, (struct ext2_inode *)t)) { for (i = 0; i < EXT2_N_BLOCKS; i++) t->i_block[i] = ext2fs_swab32(f->i_block[i]); } else if (t != f) { for (i = 0; i < EXT2_N_BLOCKS; i++) t->i_block[i] = f->i_block[i]; } t->i_generation = ext2fs_swab32(f->i_generation); t->i_faddr = ext2fs_swab32(f->i_faddr); switch (fs->super->s_creator_os) { case EXT2_OS_LINUX: t->osd1.linux1.l_i_reserved1 = ext2fs_swab32(f->osd1.linux1.l_i_reserved1); t->osd2.linux2.l_i_frag = f->osd2.linux2.l_i_frag; t->osd2.linux2.l_i_fsize = f->osd2.linux2.l_i_fsize; t->osd2.linux2.i_pad1 = ext2fs_swab16(f->osd2.linux2.i_pad1); t->osd2.linux2.l_i_uid_high = ext2fs_swab16 (f->osd2.linux2.l_i_uid_high); t->osd2.linux2.l_i_gid_high = ext2fs_swab16 (f->osd2.linux2.l_i_gid_high); t->osd2.linux2.l_i_reserved2 = ext2fs_swab32(f->osd2.linux2.l_i_reserved2); break; case EXT2_OS_HURD: t->osd1.hurd1.h_i_translator = ext2fs_swab32 (f->osd1.hurd1.h_i_translator); t->osd2.hurd2.h_i_frag = f->osd2.hurd2.h_i_frag; t->osd2.hurd2.h_i_fsize = f->osd2.hurd2.h_i_fsize; t->osd2.hurd2.h_i_mode_high = ext2fs_swab16 (f->osd2.hurd2.h_i_mode_high); t->osd2.hurd2.h_i_uid_high = ext2fs_swab16 (f->osd2.hurd2.h_i_uid_high); t->osd2.hurd2.h_i_gid_high = ext2fs_swab16 (f->osd2.hurd2.h_i_gid_high); t->osd2.hurd2.h_i_author = ext2fs_swab32 (f->osd2.hurd2.h_i_author); break; case EXT2_OS_MASIX: t->osd1.masix1.m_i_reserved1 = ext2fs_swab32(f->osd1.masix1.m_i_reserved1); t->osd2.masix2.m_i_frag = f->osd2.masix2.m_i_frag; t->osd2.masix2.m_i_fsize = f->osd2.masix2.m_i_fsize; t->osd2.masix2.m_pad1 = ext2fs_swab16(f->osd2.masix2.m_pad1); t->osd2.masix2.m_i_reserved2[0] = ext2fs_swab32(f->osd2.masix2.m_i_reserved2[0]); t->osd2.masix2.m_i_reserved2[1] = ext2fs_swab32(f->osd2.masix2.m_i_reserved2[1]); break; } if (bufsize < (int) (sizeof(struct ext2_inode) + sizeof(__u16))) return; /* no i_extra_isize field */ t->i_extra_isize = ext2fs_swab16(f->i_extra_isize); if (t->i_extra_isize > EXT2_INODE_SIZE(fs->super) - sizeof(struct ext2_inode)) { /* this is error case: i_extra_size is too large */ return; } i = sizeof(struct ext2_inode) + t->i_extra_isize + sizeof(__u32); if (bufsize < (int) i) return; /* no space for EA magic */ eaf = (__u32 *) (((char *) f) + sizeof(struct ext2_inode) + f->i_extra_isize); if (ext2fs_swab32(*eaf) != EXT2_EXT_ATTR_MAGIC) return; /* it seems no magic here */ eat = (__u32 *) (((char *) t) + sizeof(struct ext2_inode) + f->i_extra_isize); *eat = ext2fs_swab32(*eaf); /* convert EA(s) */ ext2fs_swap_ext_attr((char *) (eat + 1), (char *) (eaf + 1), bufsize - sizeof(struct ext2_inode) - t->i_extra_isize - sizeof(__u32), 0); } void ext2fs_swap_inode(ext2_filsys fs, struct ext2_inode *t, struct ext2_inode *f, int hostorder) { ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) t, (struct ext2_inode_large *) f, hostorder, sizeof(struct ext2_inode)); } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/badblocks.c0000644000000000000000000001456512263563520021772 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * badblocks.c --- routines to manipulate the bad block structure * * Copyright (C) 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fsP.h" /* * Helper function for making a badblocks list */ static errcode_t make_u32_list(int size, int num, __u32 *list, ext2_u32_list *ret) { ext2_u32_list bb; errcode_t retval; retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_list), &bb); if (retval) return retval; memset(bb, 0, sizeof(struct ext2_struct_u32_list)); bb->magic = EXT2_ET_MAGIC_BADBLOCKS_LIST; bb->size = size ? size : 10; bb->num = num; retval = ext2fs_get_mem(bb->size * sizeof(blk_t), &bb->list); if (!bb->list) { ext2fs_free_mem(&bb); return retval; } if (list) memcpy(bb->list, list, bb->size * sizeof(blk_t)); else memset(bb->list, 0, bb->size * sizeof(blk_t)); *ret = bb; return 0; } /* * This procedure creates an empty u32 list. */ errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size) { return make_u32_list(size, 0, 0, ret); } /* * This procedure creates an empty badblocks list. */ errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, int size) { return make_u32_list(size, 0, 0, (ext2_badblocks_list *) ret); } /* * This procedure copies a badblocks list */ errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest) { errcode_t retval; retval = make_u32_list(src->size, src->num, src->list, dest); if (retval) return retval; (*dest)->badblocks_flags = src->badblocks_flags; return 0; } errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src, ext2_badblocks_list *dest) { return ext2fs_u32_copy((ext2_u32_list) src, (ext2_u32_list *) dest); } /* * This procedure frees a badblocks list. * * (note: moved to closefs.c) */ /* * This procedure adds a block to a badblocks list. */ errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk) { errcode_t retval; int i, j; unsigned long old_size; EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST); if (bb->num >= bb->size) { old_size = bb->size * sizeof(__u32); bb->size += 100; retval = ext2fs_resize_mem(old_size, bb->size * sizeof(__u32), &bb->list); if (retval) { bb->size -= 100; return retval; } } /* * Add special case code for appending to the end of the list */ i = bb->num-1; if ((bb->num != 0) && (bb->list[i] == blk)) return 0; if ((bb->num == 0) || (bb->list[i] < blk)) { bb->list[bb->num++] = blk; return 0; } j = bb->num; for (i=0; i < bb->num; i++) { if (bb->list[i] == blk) return 0; if (bb->list[i] > blk) { j = i; break; } } for (i=bb->num; i > j; i--) bb->list[i] = bb->list[i-1]; bb->list[j] = blk; bb->num++; return 0; } errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, blk_t blk) { return ext2fs_u32_list_add((ext2_u32_list) bb, (__u32) blk); } /* * This procedure finds a particular block is on a badblocks * list. */ int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk) { int low, high, mid; if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) return -1; if (bb->num == 0) return -1; low = 0; high = bb->num-1; if (blk == bb->list[low]) return low; if (blk == bb->list[high]) return high; while (low < high) { mid = (low+high)/2; if (mid == low || mid == high) break; if (blk == bb->list[mid]) return mid; if (blk < bb->list[mid]) high = mid; else low = mid; } return -1; } /* * This procedure tests to see if a particular block is on a badblocks * list. */ int ext2fs_u32_list_test(ext2_u32_list bb, __u32 blk) { if (ext2fs_u32_list_find(bb, blk) < 0) return 0; else return 1; } int ext2fs_badblocks_list_test(ext2_badblocks_list bb, blk_t blk) { return ext2fs_u32_list_test((ext2_u32_list) bb, (__u32) blk); } /* * Remove a block from the badblock list */ int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk) { int remloc, i; if (bb->num == 0) return -1; remloc = ext2fs_u32_list_find(bb, blk); if (remloc < 0) return -1; for (i = remloc; i < bb->num - 1; i++) bb->list[i] = bb->list[i+1]; bb->num--; return 0; } void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk) { ext2fs_u32_list_del(bb, blk); } errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb, ext2_u32_iterate *ret) { ext2_u32_iterate iter; errcode_t retval; EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST); retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_iterate), &iter); if (retval) return retval; iter->magic = EXT2_ET_MAGIC_BADBLOCKS_ITERATE; iter->bb = bb; iter->ptr = 0; *ret = iter; return 0; } errcode_t ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb, ext2_badblocks_iterate *ret) { return ext2fs_u32_list_iterate_begin((ext2_u32_list) bb, (ext2_u32_iterate *) ret); } int ext2fs_u32_list_iterate(ext2_u32_iterate iter, __u32 *blk) { ext2_u32_list bb; if (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE) return 0; bb = iter->bb; if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) return 0; if (iter->ptr < bb->num) { *blk = bb->list[iter->ptr++]; return 1; } *blk = 0; return 0; } int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, blk_t *blk) { return ext2fs_u32_list_iterate((ext2_u32_iterate) iter, (__u32 *) blk); } void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter) { if (!iter || (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE)) return; iter->bb = 0; ext2fs_free_mem(&iter); } void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter) { ext2fs_u32_list_iterate_end((ext2_u32_iterate) iter); } int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2) { EXT2_CHECK_MAGIC(bb1, EXT2_ET_MAGIC_BADBLOCKS_LIST); EXT2_CHECK_MAGIC(bb2, EXT2_ET_MAGIC_BADBLOCKS_LIST); if (bb1->num != bb2->num) return 0; if (memcmp(bb1->list, bb2->list, bb1->num * sizeof(blk_t)) != 0) return 0; return 1; } int ext2fs_badblocks_equal(ext2_badblocks_list bb1, ext2_badblocks_list bb2) { return ext2fs_u32_list_equal((ext2_u32_list) bb1, (ext2_u32_list) bb2); } int ext2fs_u32_list_count(ext2_u32_list bb) { return bb->num; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c0000644000000000000000000000610012263563520021621 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * dirblock.c --- directory block routines. * * Copyright (C) 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include #include #include "ext2_fs.h" #include "ext2fs.h" errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, void *buf, int flags EXT2FS_ATTR((unused))) { errcode_t retval; char *p, *end; struct ext2_dir_entry *dirent; unsigned int name_len, rec_len; #if BB_BIG_ENDIAN unsigned int do_swap; #endif retval = io_channel_read_blk(fs->io, block, 1, buf); if (retval) return retval; #if BB_BIG_ENDIAN do_swap = (fs->flags & (EXT2_FLAG_SWAP_BYTES| EXT2_FLAG_SWAP_BYTES_READ)) != 0; #endif p = (char *) buf; end = (char *) buf + fs->blocksize; while (p < end-8) { dirent = (struct ext2_dir_entry *) p; #if BB_BIG_ENDIAN if (do_swap) { dirent->inode = ext2fs_swab32(dirent->inode); dirent->rec_len = ext2fs_swab16(dirent->rec_len); dirent->name_len = ext2fs_swab16(dirent->name_len); } #endif name_len = dirent->name_len; #ifdef WORDS_BIGENDIAN if (flags & EXT2_DIRBLOCK_V2_STRUCT) dirent->name_len = ext2fs_swab16(dirent->name_len); #endif rec_len = dirent->rec_len; if ((rec_len < 8) || (rec_len % 4)) { rec_len = 8; retval = EXT2_ET_DIR_CORRUPTED; } if (((name_len & 0xFF) + 8) > dirent->rec_len) retval = EXT2_ET_DIR_CORRUPTED; p += rec_len; } return retval; } errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block, void *buf) { return ext2fs_read_dir_block2(fs, block, buf, 0); } errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block, void *inbuf, int flags EXT2FS_ATTR((unused))) { #if BB_BIG_ENDIAN int do_swap = 0; errcode_t retval; char *p, *end; char *buf = NULL; struct ext2_dir_entry *dirent; if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) do_swap = 1; #ifndef WORDS_BIGENDIAN if (!do_swap) return io_channel_write_blk(fs->io, block, 1, (char *) inbuf); #endif retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; memcpy(buf, inbuf, fs->blocksize); p = buf; end = buf + fs->blocksize; while (p < end) { dirent = (struct ext2_dir_entry *) p; if ((dirent->rec_len < 8) || (dirent->rec_len % 4)) { ext2fs_free_mem(&buf); return EXT2_ET_DIR_CORRUPTED; } p += dirent->rec_len; if (do_swap) { dirent->inode = ext2fs_swab32(dirent->inode); dirent->rec_len = ext2fs_swab16(dirent->rec_len); dirent->name_len = ext2fs_swab16(dirent->name_len); } #ifdef WORDS_BIGENDIAN if (flags & EXT2_DIRBLOCK_V2_STRUCT) dirent->name_len = ext2fs_swab16(dirent->name_len); #endif } retval = io_channel_write_blk(fs->io, block, 1, buf); ext2fs_free_mem(&buf); return retval; #else return io_channel_write_blk(fs->io, block, 1, (char *) inbuf); #endif } errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block, void *inbuf) { return ext2fs_write_dir_block2(fs, block, inbuf, 0); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/brel_ma.c0000644000000000000000000001013512263563520021434 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * brel_ma.c * * Copyright (C) 1996, 1997 Theodore Ts'o. * * TODO: rewrite to not use a direct array!!! (Fortunately this * module isn't really used yet.) * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #include "brel.h" static errcode_t bma_put(ext2_brel brel, blk_t old, struct ext2_block_relocate_entry *ent); static errcode_t bma_get(ext2_brel brel, blk_t old, struct ext2_block_relocate_entry *ent); static errcode_t bma_start_iter(ext2_brel brel); static errcode_t bma_next(ext2_brel brel, blk_t *old, struct ext2_block_relocate_entry *ent); static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new); static errcode_t bma_delete(ext2_brel brel, blk_t old); static errcode_t bma_free(ext2_brel brel); struct brel_ma { __u32 magic; blk_t max_block; struct ext2_block_relocate_entry *entries; }; errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block, ext2_brel *new_brel) { ext2_brel brel = 0; errcode_t retval; struct brel_ma *ma = 0; size_t size; *new_brel = 0; /* * Allocate memory structures */ retval = ext2fs_get_mem(sizeof(struct ext2_block_relocation_table), &brel); if (retval) goto errout; memset(brel, 0, sizeof(struct ext2_block_relocation_table)); retval = ext2fs_get_mem(strlen(name)+1, &brel->name); if (retval) goto errout; strcpy(brel->name, name); retval = ext2fs_get_mem(sizeof(struct brel_ma), &ma); if (retval) goto errout; memset(ma, 0, sizeof(struct brel_ma)); brel->priv_data = ma; size = (size_t) (sizeof(struct ext2_block_relocate_entry) * (max_block+1)); retval = ext2fs_get_mem(size, &ma->entries); if (retval) goto errout; memset(ma->entries, 0, size); ma->max_block = max_block; /* * Fill in the brel data structure */ brel->put = bma_put; brel->get = bma_get; brel->start_iter = bma_start_iter; brel->next = bma_next; brel->move = bma_move; brel->delete = bma_delete; brel->free = bma_free; *new_brel = brel; return 0; errout: bma_free(brel); return retval; } static errcode_t bma_put(ext2_brel brel, blk_t old, struct ext2_block_relocate_entry *ent) { struct brel_ma *ma; ma = brel->priv_data; if (old > ma->max_block) return EXT2_ET_INVALID_ARGUMENT; ma->entries[(unsigned)old] = *ent; return 0; } static errcode_t bma_get(ext2_brel brel, blk_t old, struct ext2_block_relocate_entry *ent) { struct brel_ma *ma; ma = brel->priv_data; if (old > ma->max_block) return EXT2_ET_INVALID_ARGUMENT; if (ma->entries[(unsigned)old].new == 0) return ENOENT; *ent = ma->entries[old]; return 0; } static errcode_t bma_start_iter(ext2_brel brel) { brel->current = 0; return 0; } static errcode_t bma_next(ext2_brel brel, blk_t *old, struct ext2_block_relocate_entry *ent) { struct brel_ma *ma; ma = brel->priv_data; while (++brel->current < ma->max_block) { if (ma->entries[(unsigned)brel->current].new == 0) continue; *old = brel->current; *ent = ma->entries[(unsigned)brel->current]; return 0; } *old = 0; return 0; } static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new) { struct brel_ma *ma; ma = brel->priv_data; if ((old > ma->max_block) || (new > ma->max_block)) return EXT2_ET_INVALID_ARGUMENT; if (ma->entries[(unsigned)old].new == 0) return ENOENT; ma->entries[(unsigned)new] = ma->entries[old]; ma->entries[(unsigned)old].new = 0; return 0; } static errcode_t bma_delete(ext2_brel brel, blk_t old) { struct brel_ma *ma; ma = brel->priv_data; if (old > ma->max_block) return EXT2_ET_INVALID_ARGUMENT; if (ma->entries[(unsigned)old].new == 0) return ENOENT; ma->entries[(unsigned)old].new = 0; return 0; } static errcode_t bma_free(ext2_brel brel) { struct brel_ma *ma; if (!brel) return 0; ma = brel->priv_data; if (ma) { ext2fs_free_mem(&ma->entries); ext2fs_free_mem(&ma); } ext2fs_free_mem(&brel->name); ext2fs_free_mem(&brel); return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/flushb.c0000644000000000000000000000370512263563520021323 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * flushb.c --- Hides system-dependent information for both syncing a * device to disk and to flush any buffers from disk cache. * * Copyright (C) 2000 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #if HAVE_ERRNO_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_IOCTL_H #include #endif #if HAVE_SYS_MOUNT_H #include #include /* This may define BLKFLSBUF */ #endif #include "ext2_fs.h" #include "ext2fs.h" /* * For Linux, define BLKFLSBUF and FDFLUSH if necessary, since * not all portable header file does so for us. This really should be * fixed in the glibc header files. (Recent glibcs appear to define * BLKFLSBUF in sys/mount.h, but FDFLUSH still doesn't seem to be * defined anywhere portable.) Until then.... */ #ifdef __linux__ #ifndef BLKFLSBUF #define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */ #endif #ifndef FDFLUSH #define FDFLUSH _IO(2,0x4b) /* flush floppy disk */ #endif #endif /* * This function will sync a device/file, and optionally attempt to * flush the buffer cache. The latter is basically only useful for * system benchmarks and for torturing systems in burn-in tests. :) */ errcode_t ext2fs_sync_device(int fd, int flushb) { /* * We always sync the device in case we're running on old * kernels for which we can lose data if we don't. (There * still is a race condition for those kernels, but this * reduces it greatly.) */ if (fsync (fd) == -1) return errno; if (flushb) { #ifdef BLKFLSBUF if (ioctl (fd, BLKFLSBUF, 0) == 0) return 0; #else #ifdef __GNUC__ # warning BLKFLSBUF not defined #endif /* __GNUC__ */ #endif #ifdef FDFLUSH ioctl (fd, FDFLUSH, 0); /* In case this is a floppy */ #else #ifdef __GNUC__ # warning FDFLUSH not defined #endif /* __GNUC__ */ #endif } return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/bb_compat.c0000644000000000000000000000241212263563520021760 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * bb_compat.c --- compatibility badblocks routines * * Copyright (C) 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fsP.h" errcode_t badblocks_list_create(badblocks_list *ret, int size) { return ext2fs_badblocks_list_create(ret, size); } void badblocks_list_free(badblocks_list bb) { ext2fs_badblocks_list_free(bb); } errcode_t badblocks_list_add(badblocks_list bb, blk_t blk) { return ext2fs_badblocks_list_add(bb, blk); } int badblocks_list_test(badblocks_list bb, blk_t blk) { return ext2fs_badblocks_list_test(bb, blk); } errcode_t badblocks_list_iterate_begin(badblocks_list bb, badblocks_iterate *ret) { return ext2fs_badblocks_list_iterate_begin(bb, ret); } int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk) { return ext2fs_badblocks_list_iterate(iter, blk); } void badblocks_list_iterate_end(badblocks_iterate iter) { ext2fs_badblocks_list_iterate_end(iter); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/bitops.h0000644000000000000000000001024512263563520021342 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * bitops.h --- Bitmap frobbing code. The byte swapping routines are * also included here. * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% * * i386 bitops operations taken from , Copyright 1992, * Linus Torvalds. */ #include extern int ext2fs_set_bit(unsigned int nr,void * addr); extern int ext2fs_clear_bit(unsigned int nr, void * addr); extern int ext2fs_test_bit(unsigned int nr, const void * addr); extern __u16 ext2fs_swab16(__u16 val); extern __u32 ext2fs_swab32(__u32 val); #ifdef WORDS_BIGENDIAN #define ext2fs_cpu_to_le32(x) ext2fs_swab32((x)) #define ext2fs_le32_to_cpu(x) ext2fs_swab32((x)) #define ext2fs_cpu_to_le16(x) ext2fs_swab16((x)) #define ext2fs_le16_to_cpu(x) ext2fs_swab16((x)) #define ext2fs_cpu_to_be32(x) ((__u32)(x)) #define ext2fs_be32_to_cpu(x) ((__u32)(x)) #define ext2fs_cpu_to_be16(x) ((__u16)(x)) #define ext2fs_be16_to_cpu(x) ((__u16)(x)) #else #define ext2fs_cpu_to_le32(x) ((__u32)(x)) #define ext2fs_le32_to_cpu(x) ((__u32)(x)) #define ext2fs_cpu_to_le16(x) ((__u16)(x)) #define ext2fs_le16_to_cpu(x) ((__u16)(x)) #define ext2fs_cpu_to_be32(x) ext2fs_swab32((x)) #define ext2fs_be32_to_cpu(x) ext2fs_swab32((x)) #define ext2fs_cpu_to_be16(x) ext2fs_swab16((x)) #define ext2fs_be16_to_cpu(x) ext2fs_swab16((x)) #endif /* * EXT2FS bitmap manipulation routines. */ /* Support for sending warning messages from the inline subroutines */ extern const char *ext2fs_block_string; extern const char *ext2fs_inode_string; extern const char *ext2fs_mark_string; extern const char *ext2fs_unmark_string; extern const char *ext2fs_test_string; extern void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg, const char *description); extern void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap, int code, unsigned long arg); extern int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); extern int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); extern int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); extern int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); extern int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); extern int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); extern void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); extern void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); extern int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); extern void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); extern void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); extern int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); extern blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap); extern ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap); extern blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap); extern ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap); extern void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num); extern void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num); extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num); extern void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num); extern void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num); extern int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num); extern void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map); /* These two routines moved to gen_bitmap.c */ extern int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap, __u32 bitno); extern int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap, blk_t bitno); busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/freefs.c0000644000000000000000000000520112263563520021303 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * freefs.c --- free an ext2 filesystem * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fsP.h" static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache); void ext2fs_free(ext2_filsys fs) { if (!fs || (fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS)) return; if (fs->image_io != fs->io) { if (fs->image_io) io_channel_close(fs->image_io); } if (fs->io) { io_channel_close(fs->io); } ext2fs_free_mem(&fs->device_name); ext2fs_free_mem(&fs->super); ext2fs_free_mem(&fs->orig_super); ext2fs_free_mem(&fs->group_desc); ext2fs_free_block_bitmap(fs->block_map); ext2fs_free_inode_bitmap(fs->inode_map); ext2fs_badblocks_list_free(fs->badblocks); fs->badblocks = 0; ext2fs_free_dblist(fs->dblist); if (fs->icache) ext2fs_free_inode_cache(fs->icache); fs->magic = 0; ext2fs_free_mem(&fs); } void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap) { if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_GENERIC_BITMAP)) return; bitmap->magic = 0; ext2fs_free_mem(&bitmap->description); ext2fs_free_mem(&bitmap->bitmap); ext2fs_free_mem(&bitmap); } void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap) { if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_INODE_BITMAP)) return; bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP; ext2fs_free_generic_bitmap(bitmap); } void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap) { if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_BLOCK_BITMAP)) return; bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP; ext2fs_free_generic_bitmap(bitmap); } /* * Free the inode cache structure */ static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache) { if (--icache->refcount) return; ext2fs_free_mem(&icache->buffer); ext2fs_free_mem(&icache->cache); icache->buffer_blk = 0; ext2fs_free_mem(&icache); } /* * This procedure frees a badblocks list. */ void ext2fs_u32_list_free(ext2_u32_list bb) { if (!bb || bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) return; ext2fs_free_mem(&bb->list); ext2fs_free_mem(&bb); } void ext2fs_badblocks_list_free(ext2_badblocks_list bb) { ext2fs_u32_list_free((ext2_u32_list) bb); } /* * Free a directory block list */ void ext2fs_free_dblist(ext2_dblist dblist) { if (!dblist || (dblist->magic != EXT2_ET_MAGIC_DBLIST)) return; ext2fs_free_mem(&dblist->list); if (dblist->fs && dblist->fs->dblist == dblist) dblist->fs->dblist = 0; dblist->magic = 0; ext2fs_free_mem(&dblist); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/ext2_err.h0000644000000000000000000001465112263563520021601 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * ext2_err.h: * This file is automatically generated; please do not edit it. */ #define EXT2_ET_BASE (2133571328L) #define EXT2_ET_MAGIC_EXT2FS_FILSYS (2133571329L) #define EXT2_ET_MAGIC_BADBLOCKS_LIST (2133571330L) #define EXT2_ET_MAGIC_BADBLOCKS_ITERATE (2133571331L) #define EXT2_ET_MAGIC_INODE_SCAN (2133571332L) #define EXT2_ET_MAGIC_IO_CHANNEL (2133571333L) #define EXT2_ET_MAGIC_UNIX_IO_CHANNEL (2133571334L) #define EXT2_ET_MAGIC_IO_MANAGER (2133571335L) #define EXT2_ET_MAGIC_BLOCK_BITMAP (2133571336L) #define EXT2_ET_MAGIC_INODE_BITMAP (2133571337L) #define EXT2_ET_MAGIC_GENERIC_BITMAP (2133571338L) #define EXT2_ET_MAGIC_TEST_IO_CHANNEL (2133571339L) #define EXT2_ET_MAGIC_DBLIST (2133571340L) #define EXT2_ET_MAGIC_ICOUNT (2133571341L) #define EXT2_ET_MAGIC_PQ_IO_CHANNEL (2133571342L) #define EXT2_ET_MAGIC_EXT2_FILE (2133571343L) #define EXT2_ET_MAGIC_E2IMAGE (2133571344L) #define EXT2_ET_MAGIC_INODE_IO_CHANNEL (2133571345L) #define EXT2_ET_MAGIC_RESERVED_9 (2133571346L) #define EXT2_ET_BAD_MAGIC (2133571347L) #define EXT2_ET_REV_TOO_HIGH (2133571348L) #define EXT2_ET_RO_FILSYS (2133571349L) #define EXT2_ET_GDESC_READ (2133571350L) #define EXT2_ET_GDESC_WRITE (2133571351L) #define EXT2_ET_GDESC_BAD_BLOCK_MAP (2133571352L) #define EXT2_ET_GDESC_BAD_INODE_MAP (2133571353L) #define EXT2_ET_GDESC_BAD_INODE_TABLE (2133571354L) #define EXT2_ET_INODE_BITMAP_WRITE (2133571355L) #define EXT2_ET_INODE_BITMAP_READ (2133571356L) #define EXT2_ET_BLOCK_BITMAP_WRITE (2133571357L) #define EXT2_ET_BLOCK_BITMAP_READ (2133571358L) #define EXT2_ET_INODE_TABLE_WRITE (2133571359L) #define EXT2_ET_INODE_TABLE_READ (2133571360L) #define EXT2_ET_NEXT_INODE_READ (2133571361L) #define EXT2_ET_UNEXPECTED_BLOCK_SIZE (2133571362L) #define EXT2_ET_DIR_CORRUPTED (2133571363L) #define EXT2_ET_SHORT_READ (2133571364L) #define EXT2_ET_SHORT_WRITE (2133571365L) #define EXT2_ET_DIR_NO_SPACE (2133571366L) #define EXT2_ET_NO_INODE_BITMAP (2133571367L) #define EXT2_ET_NO_BLOCK_BITMAP (2133571368L) #define EXT2_ET_BAD_INODE_NUM (2133571369L) #define EXT2_ET_BAD_BLOCK_NUM (2133571370L) #define EXT2_ET_EXPAND_DIR_ERR (2133571371L) #define EXT2_ET_TOOSMALL (2133571372L) #define EXT2_ET_BAD_BLOCK_MARK (2133571373L) #define EXT2_ET_BAD_BLOCK_UNMARK (2133571374L) #define EXT2_ET_BAD_BLOCK_TEST (2133571375L) #define EXT2_ET_BAD_INODE_MARK (2133571376L) #define EXT2_ET_BAD_INODE_UNMARK (2133571377L) #define EXT2_ET_BAD_INODE_TEST (2133571378L) #define EXT2_ET_FUDGE_BLOCK_BITMAP_END (2133571379L) #define EXT2_ET_FUDGE_INODE_BITMAP_END (2133571380L) #define EXT2_ET_BAD_IND_BLOCK (2133571381L) #define EXT2_ET_BAD_DIND_BLOCK (2133571382L) #define EXT2_ET_BAD_TIND_BLOCK (2133571383L) #define EXT2_ET_NEQ_BLOCK_BITMAP (2133571384L) #define EXT2_ET_NEQ_INODE_BITMAP (2133571385L) #define EXT2_ET_BAD_DEVICE_NAME (2133571386L) #define EXT2_ET_MISSING_INODE_TABLE (2133571387L) #define EXT2_ET_CORRUPT_SUPERBLOCK (2133571388L) #define EXT2_ET_BAD_GENERIC_MARK (2133571389L) #define EXT2_ET_BAD_GENERIC_UNMARK (2133571390L) #define EXT2_ET_BAD_GENERIC_TEST (2133571391L) #define EXT2_ET_SYMLINK_LOOP (2133571392L) #define EXT2_ET_CALLBACK_NOTHANDLED (2133571393L) #define EXT2_ET_BAD_BLOCK_IN_INODE_TABLE (2133571394L) #define EXT2_ET_UNSUPP_FEATURE (2133571395L) #define EXT2_ET_RO_UNSUPP_FEATURE (2133571396L) #define EXT2_ET_LLSEEK_FAILED (2133571397L) #define EXT2_ET_NO_MEMORY (2133571398L) #define EXT2_ET_INVALID_ARGUMENT (2133571399L) #define EXT2_ET_BLOCK_ALLOC_FAIL (2133571400L) #define EXT2_ET_INODE_ALLOC_FAIL (2133571401L) #define EXT2_ET_NO_DIRECTORY (2133571402L) #define EXT2_ET_TOO_MANY_REFS (2133571403L) #define EXT2_ET_FILE_NOT_FOUND (2133571404L) #define EXT2_ET_FILE_RO (2133571405L) #define EXT2_ET_DB_NOT_FOUND (2133571406L) #define EXT2_ET_DIR_EXISTS (2133571407L) #define EXT2_ET_UNIMPLEMENTED (2133571408L) #define EXT2_ET_CANCEL_REQUESTED (2133571409L) #define EXT2_ET_FILE_TOO_BIG (2133571410L) #define EXT2_ET_JOURNAL_NOT_BLOCK (2133571411L) #define EXT2_ET_NO_JOURNAL_SB (2133571412L) #define EXT2_ET_JOURNAL_TOO_SMALL (2133571413L) #define EXT2_ET_JOURNAL_UNSUPP_VERSION (2133571414L) #define EXT2_ET_LOAD_EXT_JOURNAL (2133571415L) #define EXT2_ET_NO_JOURNAL (2133571416L) #define EXT2_ET_DIRHASH_UNSUPP (2133571417L) #define EXT2_ET_BAD_EA_BLOCK_NUM (2133571418L) #define EXT2_ET_TOO_MANY_INODES (2133571419L) #define EXT2_ET_NOT_IMAGE_FILE (2133571420L) #define EXT2_ET_RES_GDT_BLOCKS (2133571421L) #define EXT2_ET_RESIZE_INODE_CORRUPT (2133571422L) #define EXT2_ET_SET_BMAP_NO_IND (2133571423L) #if 0 extern const struct error_table et_ext2_error_table; extern void initialize_ext2_error_table(void); /* For compatibility with Heimdal */ extern void initialize_ext2_error_table_r(struct et_list **list); #define ERROR_TABLE_BASE_ext2 (2133571328L) /* for compatibility with older versions... */ #define init_ext2_err_tbl initialize_ext2_error_table #define ext2_err_base ERROR_TABLE_BASE_ext2 #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c0000644000000000000000000000545412263563520021151 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * mkdir.c --- make a directory in the filesystem * * Copyright (C) 1994, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #ifndef EXT2_FT_DIR #define EXT2_FT_DIR 2 #endif errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum, const char *name) { errcode_t retval; struct ext2_inode parent_inode, inode; ext2_ino_t ino = inum; ext2_ino_t scratch_ino; blk_t blk; char *block = NULL; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); /* * Allocate an inode, if necessary */ if (!ino) { retval = ext2fs_new_inode(fs, parent, LINUX_S_IFDIR | 0755, 0, &ino); if (retval) goto cleanup; } /* * Allocate a data block for the directory */ retval = ext2fs_new_block(fs, 0, 0, &blk); if (retval) goto cleanup; /* * Create a scratch template for the directory */ retval = ext2fs_new_dir_block(fs, ino, parent, &block); if (retval) goto cleanup; /* * Get the parent's inode, if necessary */ if (parent != ino) { retval = ext2fs_read_inode(fs, parent, &parent_inode); if (retval) goto cleanup; } else memset(&parent_inode, 0, sizeof(parent_inode)); /* * Create the inode structure.... */ memset(&inode, 0, sizeof(struct ext2_inode)); inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask); inode.i_uid = inode.i_gid = 0; inode.i_blocks = fs->blocksize / 512; inode.i_block[0] = blk; inode.i_links_count = 2; inode.i_ctime = inode.i_atime = inode.i_mtime = time(NULL); inode.i_size = fs->blocksize; /* * Write out the inode and inode data block */ retval = ext2fs_write_dir_block(fs, blk, block); if (retval) goto cleanup; retval = ext2fs_write_new_inode(fs, ino, &inode); if (retval) goto cleanup; /* * Link the directory into the filesystem hierarchy */ if (name) { retval = ext2fs_lookup(fs, parent, name, strlen(name), 0, &scratch_ino); if (!retval) { retval = EXT2_ET_DIR_EXISTS; name = 0; goto cleanup; } if (retval != EXT2_ET_FILE_NOT_FOUND) goto cleanup; retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_DIR); if (retval) goto cleanup; } /* * Update parent inode's counts */ if (parent != ino) { parent_inode.i_links_count++; retval = ext2fs_write_inode(fs, parent, &parent_inode); if (retval) goto cleanup; } /* * Update accounting.... */ ext2fs_block_alloc_stats(fs, blk, +1); ext2fs_inode_alloc_stats2(fs, ino, +1, 1); cleanup: ext2fs_free_mem(&block); return retval; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c0000644000000000000000000000533112263563520022461 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * alloc_tables.c --- Allocate tables for a newly initialized * filesystem. Used by mke2fs when initializing a filesystem * * Copyright (C) 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group, ext2fs_block_bitmap bmap) { errcode_t retval; blk_t group_blk, start_blk, last_blk, new_blk, blk; int j; group_blk = fs->super->s_first_data_block + (group * fs->super->s_blocks_per_group); last_blk = group_blk + fs->super->s_blocks_per_group; if (last_blk >= fs->super->s_blocks_count) last_blk = fs->super->s_blocks_count - 1; if (!bmap) bmap = fs->block_map; /* * Allocate the block and inode bitmaps, if necessary */ if (fs->stride) { start_blk = group_blk + fs->inode_blocks_per_group; start_blk += ((fs->stride * group) % (last_blk - start_blk)); if (start_blk > last_blk) start_blk = group_blk; } else start_blk = group_blk; if (!fs->group_desc[group].bg_block_bitmap) { retval = ext2fs_get_free_blocks(fs, start_blk, last_blk, 1, bmap, &new_blk); if (retval == EXT2_ET_BLOCK_ALLOC_FAIL) retval = ext2fs_get_free_blocks(fs, group_blk, last_blk, 1, bmap, &new_blk); if (retval) return retval; ext2fs_mark_block_bitmap(bmap, new_blk); fs->group_desc[group].bg_block_bitmap = new_blk; } if (!fs->group_desc[group].bg_inode_bitmap) { retval = ext2fs_get_free_blocks(fs, start_blk, last_blk, 1, bmap, &new_blk); if (retval == EXT2_ET_BLOCK_ALLOC_FAIL) retval = ext2fs_get_free_blocks(fs, group_blk, last_blk, 1, bmap, &new_blk); if (retval) return retval; ext2fs_mark_block_bitmap(bmap, new_blk); fs->group_desc[group].bg_inode_bitmap = new_blk; } /* * Allocate the inode table */ if (!fs->group_desc[group].bg_inode_table) { retval = ext2fs_get_free_blocks(fs, group_blk, last_blk, fs->inode_blocks_per_group, bmap, &new_blk); if (retval) return retval; for (j=0, blk = new_blk; j < fs->inode_blocks_per_group; j++, blk++) ext2fs_mark_block_bitmap(bmap, blk); fs->group_desc[group].bg_inode_table = new_blk; } return 0; } errcode_t ext2fs_allocate_tables(ext2_filsys fs) { errcode_t retval; dgrp_t i; for (i = 0; i < fs->group_desc_count; i++) { retval = ext2fs_allocate_group_table(fs, i, fs->block_map); if (retval) return retval; } return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c0000644000000000000000000000451512263563520022020 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * rs_bitmap.c --- routine for changing the size of a bitmap * * Copyright (C) 1996, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" errcode_t ext2fs_resize_generic_bitmap(__u32 new_end, __u32 new_real_end, ext2fs_generic_bitmap bmap) { errcode_t retval; size_t size, new_size; __u32 bitno; if (!bmap) return EXT2_ET_INVALID_ARGUMENT; EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_GENERIC_BITMAP); /* * If we're expanding the bitmap, make sure all of the new * parts of the bitmap are zero. */ if (new_end > bmap->end) { bitno = bmap->real_end; if (bitno > new_end) bitno = new_end; for (; bitno > bmap->end; bitno--) ext2fs_clear_bit(bitno - bmap->start, bmap->bitmap); } if (new_real_end == bmap->real_end) { bmap->end = new_end; return 0; } size = ((bmap->real_end - bmap->start) / 8) + 1; new_size = ((new_real_end - bmap->start) / 8) + 1; if (size != new_size) { retval = ext2fs_resize_mem(size, new_size, &bmap->bitmap); if (retval) return retval; } if (new_size > size) memset(bmap->bitmap + size, 0, new_size - size); bmap->end = new_end; bmap->real_end = new_real_end; return 0; } errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end, ext2fs_inode_bitmap bmap) { errcode_t retval; if (!bmap) return EXT2_ET_INVALID_ARGUMENT; EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_INODE_BITMAP); bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP; retval = ext2fs_resize_generic_bitmap(new_end, new_real_end, bmap); bmap->magic = EXT2_ET_MAGIC_INODE_BITMAP; return retval; } errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end, ext2fs_block_bitmap bmap) { errcode_t retval; if (!bmap) return EXT2_ET_INVALID_ARGUMENT; EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_BLOCK_BITMAP); bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP; retval = ext2fs_resize_generic_bitmap(new_end, new_real_end, bmap); bmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP; return retval; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/bmap.c0000644000000000000000000001415112263563520020754 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * bmap.c --- logical to physical block mapping * * Copyright (C) 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, char *block_buf, int bmap_flags, blk_t block, blk_t *phys_blk); #define inode_bmap(inode, nr) ((inode)->i_block[(nr)]) static errcode_t block_ind_bmap(ext2_filsys fs, int flags, blk_t ind, char *block_buf, int *blocks_alloc, blk_t nr, blk_t *ret_blk) { errcode_t retval; blk_t b; if (!ind) { if (flags & BMAP_SET) return EXT2_ET_SET_BMAP_NO_IND; *ret_blk = 0; return 0; } retval = io_channel_read_blk(fs->io, ind, 1, block_buf); if (retval) return retval; if (flags & BMAP_SET) { b = *ret_blk; #if BB_BIG_ENDIAN if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) b = ext2fs_swab32(b); #endif ((blk_t *) block_buf)[nr] = b; return io_channel_write_blk(fs->io, ind, 1, block_buf); } b = ((blk_t *) block_buf)[nr]; #if BB_BIG_ENDIAN if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) b = ext2fs_swab32(b); #endif if (!b && (flags & BMAP_ALLOC)) { b = nr ? ((blk_t *) block_buf)[nr-1] : 0; retval = ext2fs_alloc_block(fs, b, block_buf + fs->blocksize, &b); if (retval) return retval; #if BB_BIG_ENDIAN if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) ((blk_t *) block_buf)[nr] = ext2fs_swab32(b); else #endif ((blk_t *) block_buf)[nr] = b; retval = io_channel_write_blk(fs->io, ind, 1, block_buf); if (retval) return retval; (*blocks_alloc)++; } *ret_blk = b; return 0; } static errcode_t block_dind_bmap(ext2_filsys fs, int flags, blk_t dind, char *block_buf, int *blocks_alloc, blk_t nr, blk_t *ret_blk) { blk_t b; errcode_t retval; blk_t addr_per_block; addr_per_block = (blk_t) fs->blocksize >> 2; retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf, blocks_alloc, nr / addr_per_block, &b); if (retval) return retval; retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, nr % addr_per_block, ret_blk); return retval; } static errcode_t block_tind_bmap(ext2_filsys fs, int flags, blk_t tind, char *block_buf, int *blocks_alloc, blk_t nr, blk_t *ret_blk) { blk_t b; errcode_t retval; blk_t addr_per_block; addr_per_block = (blk_t) fs->blocksize >> 2; retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf, blocks_alloc, nr / addr_per_block, &b); if (retval) return retval; retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, nr % addr_per_block, ret_blk); return retval; } errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, char *block_buf, int bmap_flags, blk_t block, blk_t *phys_blk) { struct ext2_inode inode_buf; blk_t addr_per_block; blk_t b; char *buf = NULL; errcode_t retval = 0; int blocks_alloc = 0, inode_dirty = 0; if (!(bmap_flags & BMAP_SET)) *phys_blk = 0; /* Read inode structure if necessary */ if (!inode) { retval = ext2fs_read_inode(fs, ino, &inode_buf); if (retval) return retval; inode = &inode_buf; } addr_per_block = (blk_t) fs->blocksize >> 2; if (!block_buf) { retval = ext2fs_get_mem(fs->blocksize * 2, &buf); if (retval) return retval; block_buf = buf; } if (block < EXT2_NDIR_BLOCKS) { if (bmap_flags & BMAP_SET) { b = *phys_blk; #if BB_BIG_ENDIAN if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) b = ext2fs_swab32(b); #endif inode_bmap(inode, block) = b; inode_dirty++; goto done; } *phys_blk = inode_bmap(inode, block); b = block ? inode_bmap(inode, block-1) : 0; if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { retval = ext2fs_alloc_block(fs, b, block_buf, &b); if (retval) goto done; inode_bmap(inode, block) = b; blocks_alloc++; *phys_blk = b; } goto done; } /* Indirect block */ block -= EXT2_NDIR_BLOCKS; if (block < addr_per_block) { b = inode_bmap(inode, EXT2_IND_BLOCK); if (!b) { if (!(bmap_flags & BMAP_ALLOC)) { if (bmap_flags & BMAP_SET) retval = EXT2_ET_SET_BMAP_NO_IND; goto done; } b = inode_bmap(inode, EXT2_IND_BLOCK-1); retval = ext2fs_alloc_block(fs, b, block_buf, &b); if (retval) goto done; inode_bmap(inode, EXT2_IND_BLOCK) = b; blocks_alloc++; } retval = block_ind_bmap(fs, bmap_flags, b, block_buf, &blocks_alloc, block, phys_blk); goto done; } /* Doubly indirect block */ block -= addr_per_block; if (block < addr_per_block * addr_per_block) { b = inode_bmap(inode, EXT2_DIND_BLOCK); if (!b) { if (!(bmap_flags & BMAP_ALLOC)) { if (bmap_flags & BMAP_SET) retval = EXT2_ET_SET_BMAP_NO_IND; goto done; } b = inode_bmap(inode, EXT2_IND_BLOCK); retval = ext2fs_alloc_block(fs, b, block_buf, &b); if (retval) goto done; inode_bmap(inode, EXT2_DIND_BLOCK) = b; blocks_alloc++; } retval = block_dind_bmap(fs, bmap_flags, b, block_buf, &blocks_alloc, block, phys_blk); goto done; } /* Triply indirect block */ block -= addr_per_block * addr_per_block; b = inode_bmap(inode, EXT2_TIND_BLOCK); if (!b) { if (!(bmap_flags & BMAP_ALLOC)) { if (bmap_flags & BMAP_SET) retval = EXT2_ET_SET_BMAP_NO_IND; goto done; } b = inode_bmap(inode, EXT2_DIND_BLOCK); retval = ext2fs_alloc_block(fs, b, block_buf, &b); if (retval) goto done; inode_bmap(inode, EXT2_TIND_BLOCK) = b; blocks_alloc++; } retval = block_tind_bmap(fs, bmap_flags, b, block_buf, &blocks_alloc, block, phys_blk); done: ext2fs_free_mem(&buf); if ((retval == 0) && (blocks_alloc || inode_dirty)) { inode->i_blocks += (blocks_alloc * fs->blocksize) / 512; retval = ext2fs_write_inode(fs, ino, inode); } return retval; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/Kbuild.src0000644000000000000000000000202612263563520021612 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. NEEDED-$(CONFIG_E2FSCK) = y NEEDED-$(CONFIG_FSCK) = y NEEDED-$(CONFIG_MKE2FS) = y NEEDED-$(CONFIG_TUNE2FS) = y lib-y:= INSERT lib-$(NEEDED-y) += gen_bitmap.o bitops.o ismounted.o mkjournal.o unix_io.o \ rw_bitmaps.o initialize.o bitmaps.o block.o \ ind_block.o inode.o freefs.o alloc_stats.o closefs.o \ openfs.o io_manager.o finddev.o read_bb.o alloc.o badblocks.o \ getsize.o getsectsize.o alloc_tables.o read_bb_file.o mkdir.o \ bb_inode.o newdir.o alloc_sb.o lookup.o dirblock.o expanddir.o \ dir_iterate.o link.o res_gdt.o icount.o get_pathname.o dblist.o \ dirhash.o version.o flushb.o unlink.o check_desc.o valid_blk.o \ ext_attr.o bmap.o dblist_dir.o ext2fs_inline.o swapfs.o CFLAGS += -include $(srctree)/e2fsprogs/e2fsbb.h busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/bitops.c0000644000000000000000000000366612263563520021346 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * bitops.c --- Bitmap frobbing code. See bitops.h for the inlined * routines. * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #ifndef _EXT2_HAVE_ASM_BITOPS_ /* * For the benefit of those who are trying to port Linux to another * architecture, here are some C-language equivalents. You should * recode these in the native assmebly language, if at all possible. * * C language equivalents written by Theodore Ts'o, 9/26/92. * Modified by Pete A. Zaitcev 7/14/95 to be portable to big endian * systems, as well as non-32 bit systems. */ int ext2fs_set_bit(unsigned int nr,void * addr) { int mask, retval; unsigned char *ADDR = (unsigned char *) addr; ADDR += nr >> 3; mask = 1 << (nr & 0x07); retval = mask & *ADDR; *ADDR |= mask; return retval; } int ext2fs_clear_bit(unsigned int nr, void * addr) { int mask, retval; unsigned char *ADDR = (unsigned char *) addr; ADDR += nr >> 3; mask = 1 << (nr & 0x07); retval = mask & *ADDR; *ADDR &= ~mask; return retval; } int ext2fs_test_bit(unsigned int nr, const void * addr) { int mask; const unsigned char *ADDR = (const unsigned char *) addr; ADDR += nr >> 3; mask = 1 << (nr & 0x07); return (mask & *ADDR); } #endif /* !_EXT2_HAVE_ASM_BITOPS_ */ void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg, const char *description) { #ifndef OMIT_COM_ERR if (description) bb_error_msg("#%lu for %s", arg, description); else bb_error_msg("#%lu", arg); #endif } void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap, int code, unsigned long arg) { #ifndef OMIT_COM_ERR if (bitmap->description) bb_error_msg("#%lu for %s", arg, bitmap->description); else bb_error_msg("#%lu", arg); #endif } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/irel.h0000644000000000000000000000644512263563520021004 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * irel.h * * Copyright (C) 1996, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ struct ext2_inode_reference { blk_t block; __u16 offset; }; struct ext2_inode_relocate_entry { ext2_ino_t new; ext2_ino_t orig; __u16 flags; __u16 max_refs; }; typedef struct ext2_inode_relocation_table *ext2_irel; struct ext2_inode_relocation_table { __u32 magic; char *name; ext2_ino_t current; void *priv_data; /* * Add an inode relocation entry. */ errcode_t (*put)(ext2_irel irel, ext2_ino_t old, struct ext2_inode_relocate_entry *ent); /* * Get an inode relocation entry. */ errcode_t (*get)(ext2_irel irel, ext2_ino_t old, struct ext2_inode_relocate_entry *ent); /* * Get an inode relocation entry by its original inode number */ errcode_t (*get_by_orig)(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, struct ext2_inode_relocate_entry *ent); /* * Initialize for iterating over the inode relocation entries. */ errcode_t (*start_iter)(ext2_irel irel); /* * The iterator function for the inode relocation entries. * Returns an inode number of 0 when out of entries. */ errcode_t (*next)(ext2_irel irel, ext2_ino_t *old, struct ext2_inode_relocate_entry *ent); /* * Add an inode reference (i.e., note the fact that a * particular block/offset contains a reference to an inode) */ errcode_t (*add_ref)(ext2_irel irel, ext2_ino_t ino, struct ext2_inode_reference *ref); /* * Initialize for iterating over the inode references for a * particular inode. */ errcode_t (*start_iter_ref)(ext2_irel irel, ext2_ino_t ino); /* * The iterator function for the inode references for an * inode. The references for only one inode can be interator * over at a time, as the iterator state is stored in ext2_irel. */ errcode_t (*next_ref)(ext2_irel irel, struct ext2_inode_reference *ref); /* * Move the inode relocation table from one inode number to * another. Note that the inode references also must move. */ errcode_t (*move)(ext2_irel irel, ext2_ino_t old, ext2_ino_t new); /* * Remove an inode relocation entry, along with all of the * inode references. */ errcode_t (*delete)(ext2_irel irel, ext2_ino_t old); /* * Free the inode relocation table. */ errcode_t (*free)(ext2_irel irel); }; errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode, ext2_irel *irel); #define ext2fs_irel_put(irel, old, ent) ((irel)->put((irel), old, ent)) #define ext2fs_irel_get(irel, old, ent) ((irel)->get((irel), old, ent)) #define ext2fs_irel_get_by_orig(irel, orig, old, ent) \ ((irel)->get_by_orig((irel), orig, old, ent)) #define ext2fs_irel_start_iter(irel) ((irel)->start_iter((irel))) #define ext2fs_irel_next(irel, old, ent) ((irel)->next((irel), old, ent)) #define ext2fs_irel_add_ref(irel, ino, ref) ((irel)->add_ref((irel), ino, ref)) #define ext2fs_irel_start_iter_ref(irel, ino) ((irel)->start_iter_ref((irel), ino)) #define ext2fs_irel_next_ref(irel, ref) ((irel)->next_ref((irel), ref)) #define ext2fs_irel_move(irel, old, new) ((irel)->move((irel), old, new)) #define ext2fs_irel_delete(irel, old) ((irel)->delete((irel), old)) #define ext2fs_irel_free(irel) ((irel)->free((irel))) busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/closefs.c0000644000000000000000000002235112263563520021474 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * closefs.c --- close an ext2 filesystem * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include #include #include "ext2_fs.h" #include "ext2fsP.h" static int test_root(int a, int b) { if (a == 0) return 1; while (1) { if (a == 1) return 1; if (a % b) return 0; a = a / b; } } int ext2fs_bg_has_super(ext2_filsys fs, int group_block) { if (!(fs->super->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) return 1; if (test_root(group_block, 3) || (test_root(group_block, 5)) || test_root(group_block, 7)) return 1; return 0; } int ext2fs_super_and_bgd_loc(ext2_filsys fs, dgrp_t group, blk_t *ret_super_blk, blk_t *ret_old_desc_blk, blk_t *ret_new_desc_blk, int *ret_meta_bg) { blk_t group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0; unsigned int meta_bg, meta_bg_size; int numblocks, has_super; int old_desc_blocks; group_block = fs->super->s_first_data_block + (group * fs->super->s_blocks_per_group); if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) old_desc_blocks = fs->super->s_first_meta_bg; else old_desc_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks; if (group == fs->group_desc_count-1) { numblocks = (fs->super->s_blocks_count - fs->super->s_first_data_block) % fs->super->s_blocks_per_group; if (!numblocks) numblocks = fs->super->s_blocks_per_group; } else numblocks = fs->super->s_blocks_per_group; has_super = ext2fs_bg_has_super(fs, group); if (has_super) { super_blk = group_block; numblocks--; } meta_bg_size = (fs->blocksize / sizeof (struct ext2_group_desc)); meta_bg = group / meta_bg_size; if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || (meta_bg < fs->super->s_first_meta_bg)) { if (has_super) { old_desc_blk = group_block + 1; numblocks -= old_desc_blocks; } } else { if (((group % meta_bg_size) == 0) || ((group % meta_bg_size) == 1) || ((group % meta_bg_size) == (meta_bg_size-1))) { if (has_super) has_super = 1; new_desc_blk = group_block + has_super; numblocks--; } } numblocks -= 2 + fs->inode_blocks_per_group; if (ret_super_blk) *ret_super_blk = super_blk; if (ret_old_desc_blk) *ret_old_desc_blk = old_desc_blk; if (ret_new_desc_blk) *ret_new_desc_blk = new_desc_blk; if (ret_meta_bg) *ret_meta_bg = meta_bg; return numblocks; } /* * This function forces out the primary superblock. We need to only * write out those fields which we have changed, since if the * filesystem is mounted, it may have changed some of the other * fields. * * It takes as input a superblock which has already been byte swapped * (if necessary). * */ static errcode_t write_primary_superblock(ext2_filsys fs, struct ext2_super_block *super) { __u16 *old_super, *new_super; int check_idx, write_idx, size; errcode_t retval; if (!fs->io->manager->write_byte || !fs->orig_super) { io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); retval = io_channel_write_blk(fs->io, 1, -SUPERBLOCK_SIZE, super); io_channel_set_blksize(fs->io, fs->blocksize); return retval; } old_super = (__u16 *) fs->orig_super; new_super = (__u16 *) super; for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) { if (old_super[check_idx] == new_super[check_idx]) continue; write_idx = check_idx; for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++) if (old_super[check_idx] == new_super[check_idx]) break; size = 2 * (check_idx - write_idx); retval = io_channel_write_byte(fs->io, SUPERBLOCK_OFFSET + (2 * write_idx), size, new_super + write_idx); if (retval) return retval; } memcpy(fs->orig_super, super, SUPERBLOCK_SIZE); return 0; } /* * Updates the revision to EXT2_DYNAMIC_REV */ void ext2fs_update_dynamic_rev(ext2_filsys fs) { struct ext2_super_block *sb = fs->super; if (sb->s_rev_level > EXT2_GOOD_OLD_REV) return; sb->s_rev_level = EXT2_DYNAMIC_REV; sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; /* s_uuid is handled by e2fsck already */ /* other fields should be left alone */ } static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group, blk_t group_block, struct ext2_super_block *super_shadow) { dgrp_t sgrp = group; if (sgrp > ((1 << 16) - 1)) sgrp = (1 << 16) - 1; #if BB_BIG_ENDIAN if (fs->flags & EXT2_FLAG_SWAP_BYTES) super_shadow->s_block_group_nr = ext2fs_swab16(sgrp); else #endif fs->super->s_block_group_nr = sgrp; return io_channel_write_blk(fs->io, group_block, -SUPERBLOCK_SIZE, super_shadow); } errcode_t ext2fs_flush(ext2_filsys fs) { dgrp_t i; blk_t group_block; errcode_t retval; unsigned long fs_state; struct ext2_super_block *super_shadow = NULL; struct ext2_group_desc *group_shadow = NULL; char *group_ptr; int old_desc_blocks; #if BB_BIG_ENDIAN dgrp_t j; struct ext2_group_desc *s, *t; #endif EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); fs_state = fs->super->s_state; fs->super->s_wtime = time(NULL); fs->super->s_block_group_nr = 0; #if BB_BIG_ENDIAN if (fs->flags & EXT2_FLAG_SWAP_BYTES) { retval = EXT2_ET_NO_MEMORY; retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow); if (retval) goto errout; retval = ext2fs_get_mem((size_t)(fs->blocksize * fs->desc_blocks), &group_shadow); if (retval) goto errout; memset(group_shadow, 0, (size_t) fs->blocksize * fs->desc_blocks); /* swap the group descriptors */ for (j=0, s=fs->group_desc, t=group_shadow; j < fs->group_desc_count; j++, t++, s++) { *t = *s; ext2fs_swap_group_desc(t); } } else { super_shadow = fs->super; group_shadow = fs->group_desc; } #else super_shadow = fs->super; group_shadow = fs->group_desc; #endif /* * If this is an external journal device, don't write out the * block group descriptors or any of the backup superblocks */ if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) goto write_primary_superblock_only; /* * Set the state of the FS to be non-valid. (The state has * already been backed up earlier, and will be restored after * we write out the backup superblocks.) */ fs->super->s_state &= ~EXT2_VALID_FS; #if BB_BIG_ENDIAN if (fs->flags & EXT2_FLAG_SWAP_BYTES) { *super_shadow = *fs->super; ext2fs_swap_super(super_shadow); } #endif /* * Write out the master group descriptors, and the backup * superblocks and group descriptors. */ group_block = fs->super->s_first_data_block; group_ptr = (char *) group_shadow; if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) old_desc_blocks = fs->super->s_first_meta_bg; else old_desc_blocks = fs->desc_blocks; for (i = 0; i < fs->group_desc_count; i++) { blk_t super_blk, old_desc_blk, new_desc_blk; int meta_bg; ext2fs_super_and_bgd_loc(fs, i, &super_blk, &old_desc_blk, &new_desc_blk, &meta_bg); if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) { retval = write_backup_super(fs, i, super_blk, super_shadow); if (retval) goto errout; } if (fs->flags & EXT2_FLAG_SUPER_ONLY) continue; if ((old_desc_blk) && (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) { retval = io_channel_write_blk(fs->io, old_desc_blk, old_desc_blocks, group_ptr); if (retval) goto errout; } if (new_desc_blk) { retval = io_channel_write_blk(fs->io, new_desc_blk, 1, group_ptr + (meta_bg*fs->blocksize)); if (retval) goto errout; } } fs->super->s_block_group_nr = 0; fs->super->s_state = fs_state; #if BB_BIG_ENDIAN if (fs->flags & EXT2_FLAG_SWAP_BYTES) { *super_shadow = *fs->super; ext2fs_swap_super(super_shadow); } #endif /* * If the write_bitmaps() function is present, call it to * flush the bitmaps. This is done this way so that a simple * program that doesn't mess with the bitmaps doesn't need to * drag in the bitmaps.c code. */ if (fs->write_bitmaps) { retval = fs->write_bitmaps(fs); if (retval) goto errout; } write_primary_superblock_only: /* * Write out master superblock. This has to be done * separately, since it is located at a fixed location * (SUPERBLOCK_OFFSET). We flush all other pending changes * out to disk first, just to avoid a race condition with an * insy-tinsy window.... */ retval = io_channel_flush(fs->io); retval = write_primary_superblock(fs, super_shadow); if (retval) goto errout; fs->flags &= ~EXT2_FLAG_DIRTY; retval = io_channel_flush(fs->io); errout: fs->super->s_state = fs_state; if (fs->flags & EXT2_FLAG_SWAP_BYTES) { if (super_shadow) ext2fs_free_mem(&super_shadow); if (group_shadow) ext2fs_free_mem(&group_shadow); } return retval; } errcode_t ext2fs_close(ext2_filsys fs) { errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (fs->flags & EXT2_FLAG_DIRTY) { retval = ext2fs_flush(fs); if (retval) return retval; } if (fs->write_bitmaps) { retval = fs->write_bitmaps(fs); if (retval) return retval; } ext2fs_free(fs); return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/kernel-jbd.h0000644000000000000000000001437212263563520022064 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * linux/include/linux/jbd.h * * Written by Stephen C. Tweedie * * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your * option, any later version, incorporated herein by reference. * * Definitions for transaction data structures for the buffer cache * filesystem journaling support. */ #ifndef LINUX_JBD_H #define LINUX_JBD_H 1 #include #include #include "ext2fs.h" /* * Standard header for all descriptor blocks: */ typedef struct journal_header_s { __u32 h_magic; __u32 h_blocktype; __u32 h_sequence; } journal_header_t; /* * This is the global e2fsck structure. */ typedef struct e2fsck_struct *e2fsck_t; struct inode { e2fsck_t i_ctx; ext2_ino_t i_ino; struct ext2_inode i_ext2; }; /* * The journal superblock. All fields are in big-endian byte order. */ typedef struct journal_superblock_s { /* 0x0000 */ journal_header_t s_header; /* 0x000C */ /* Static information describing the journal */ __u32 s_blocksize; /* journal device blocksize */ __u32 s_maxlen; /* total blocks in journal file */ __u32 s_first; /* first block of log information */ /* 0x0018 */ /* Dynamic information describing the current state of the log */ __u32 s_sequence; /* first commit ID expected in log */ __u32 s_start; /* blocknr of start of log */ /* 0x0020 */ /* Error value, as set by journal_abort(). */ __s32 s_errno; /* 0x0024 */ /* Remaining fields are only valid in a version-2 superblock */ __u32 s_feature_compat; /* compatible feature set */ __u32 s_feature_incompat; /* incompatible feature set */ __u32 s_feature_ro_compat; /* readonly-compatible feature set */ /* 0x0030 */ __u8 s_uuid[16]; /* 128-bit uuid for journal */ /* 0x0040 */ __u32 s_nr_users; /* Nr of filesystems sharing log */ __u32 s_dynsuper; /* Blocknr of dynamic superblock copy*/ /* 0x0048 */ __u32 s_max_transaction; /* Limit of journal blocks per trans.*/ __u32 s_max_trans_data; /* Limit of data blocks per trans. */ /* 0x0050 */ __u32 s_padding[44]; /* 0x0100 */ __u8 s_users[16*48]; /* ids of all fs'es sharing the log */ /* 0x0400 */ } journal_superblock_t; extern int journal_blocks_per_page(struct inode *inode); extern int jbd_blocks_per_page(struct inode *inode); #define JFS_MIN_JOURNAL_BLOCKS 1024 /* * Internal structures used by the logging mechanism: */ #define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */ /* * Descriptor block types: */ #define JFS_DESCRIPTOR_BLOCK 1 #define JFS_COMMIT_BLOCK 2 #define JFS_SUPERBLOCK_V1 3 #define JFS_SUPERBLOCK_V2 4 #define JFS_REVOKE_BLOCK 5 /* * The block tag: used to describe a single buffer in the journal */ typedef struct journal_block_tag_s { __u32 t_blocknr; /* The on-disk block number */ __u32 t_flags; /* See below */ } journal_block_tag_t; /* * The revoke descriptor: used on disk to describe a series of blocks to * be revoked from the log */ typedef struct journal_revoke_header_s { journal_header_t r_header; int r_count; /* Count of bytes used in the block */ } journal_revoke_header_t; /* Definitions for the journal tag flags word: */ #define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */ #define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */ #define JFS_FLAG_DELETED 4 /* block deleted by this transaction */ #define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */ #define JFS_HAS_COMPAT_FEATURE(j,mask) \ ((j)->j_format_version >= 2 && \ ((j)->j_superblock->s_feature_compat & cpu_to_be32((mask)))) #define JFS_HAS_RO_COMPAT_FEATURE(j,mask) \ ((j)->j_format_version >= 2 && \ ((j)->j_superblock->s_feature_ro_compat & cpu_to_be32((mask)))) #define JFS_HAS_INCOMPAT_FEATURE(j,mask) \ ((j)->j_format_version >= 2 && \ ((j)->j_superblock->s_feature_incompat & cpu_to_be32((mask)))) #define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001 /* Features known to this kernel version: */ #define JFS_KNOWN_COMPAT_FEATURES 0 #define JFS_KNOWN_ROCOMPAT_FEATURES 0 #define JFS_KNOWN_INCOMPAT_FEATURES JFS_FEATURE_INCOMPAT_REVOKE /* Comparison functions for transaction IDs: perform comparisons using * modulo arithmetic so that they work over sequence number wraps. */ /* * Definitions which augment the buffer_head layer */ /* journaling buffer types */ #define BJ_None 0 /* Not journaled */ #define BJ_SyncData 1 /* Normal data: flush before commit */ #define BJ_AsyncData 2 /* writepage data: wait on it before commit */ #define BJ_Metadata 3 /* Normal journaled metadata */ #define BJ_Forget 4 /* Buffer superceded by this transaction */ #define BJ_IO 5 /* Buffer is for temporary IO use */ #define BJ_Shadow 6 /* Buffer contents being shadowed to the log */ #define BJ_LogCtl 7 /* Buffer contains log descriptors */ #define BJ_Reserved 8 /* Buffer is reserved for access by journal */ #define BJ_Types 9 struct kdev_s { e2fsck_t k_ctx; int k_dev; }; typedef struct kdev_s *kdev_t; typedef unsigned int tid_t; struct journal_s { unsigned long j_flags; int j_errno; struct buffer_head * j_sb_buffer; struct journal_superblock_s *j_superblock; int j_format_version; unsigned long j_head; unsigned long j_tail; unsigned long j_free; unsigned long j_first, j_last; kdev_t j_dev; kdev_t j_fs_dev; int j_blocksize; unsigned int j_blk_offset; unsigned int j_maxlen; struct inode * j_inode; tid_t j_tail_sequence; tid_t j_transaction_sequence; __u8 j_uuid[16]; struct jbd_revoke_table_s *j_revoke; }; typedef struct journal_s journal_t; extern int journal_recover (journal_t *journal); extern int journal_skip_recovery (journal_t *); /* Primary revoke support */ extern int journal_init_revoke(journal_t *, int); extern void journal_destroy_revoke_caches(void); extern int journal_init_revoke_caches(void); /* Recovery revoke support */ extern int journal_set_revoke(journal_t *, unsigned long, tid_t); extern int journal_test_revoke(journal_t *, unsigned long, tid_t); extern void journal_clear_revoke(journal_t *); extern void journal_brelse_array(struct buffer_head *b[], int n); extern void journal_destroy_revoke(journal_t *); #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c0000644000000000000000000000404712263563520021161 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * dupfs.c --- duplicate a ext2 filesystem handle * * Copyright (C) 1997, 1998, 2001, 2003, 2005 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include #include #include "ext2_fs.h" #include "ext2fsP.h" errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest) { ext2_filsys fs; errcode_t retval; EXT2_CHECK_MAGIC(src, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); if (retval) return retval; *fs = *src; fs->device_name = 0; fs->super = 0; fs->orig_super = 0; fs->group_desc = 0; fs->inode_map = 0; fs->block_map = 0; fs->badblocks = 0; fs->dblist = 0; io_channel_bumpcount(fs->io); if (fs->icache) fs->icache->refcount++; retval = ext2fs_get_mem(strlen(src->device_name)+1, &fs->device_name); if (retval) goto errout; strcpy(fs->device_name, src->device_name); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super); if (retval) goto errout; memcpy(fs->super, src->super, SUPERBLOCK_SIZE); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); if (retval) goto errout; memcpy(fs->orig_super, src->orig_super, SUPERBLOCK_SIZE); retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize, &fs->group_desc); if (retval) goto errout; memcpy(fs->group_desc, src->group_desc, (size_t) fs->desc_blocks * fs->blocksize); if (src->inode_map) { retval = ext2fs_copy_bitmap(src->inode_map, &fs->inode_map); if (retval) goto errout; } if (src->block_map) { retval = ext2fs_copy_bitmap(src->block_map, &fs->block_map); if (retval) goto errout; } if (src->badblocks) { retval = ext2fs_badblocks_copy(src->badblocks, &fs->badblocks); if (retval) goto errout; } if (src->dblist) { retval = ext2fs_copy_dblist(src->dblist, &fs->dblist); if (retval) goto errout; } *dest = fs; return 0; errout: ext2fs_free(fs); return retval; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/imager.c0000644000000000000000000001620112263563520021277 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * image.c --- writes out the critical parts of the filesystem as a * flat file. * * Copyright (C) 2000 Theodore Ts'o. * * Note: this uses the POSIX IO interfaces, unlike most of the other * functions in this library. So sue me. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #ifndef HAVE_TYPE_SSIZE_T typedef int ssize_t; #endif /* * This function returns 1 if the specified block is all zeros */ static int check_zero_block(char *buf, int blocksize) { char *cp = buf; int left = blocksize; while (left > 0) { if (*cp++) return 0; left--; } return 1; } /* * Write the inode table out as a single block. */ #define BUF_BLOCKS 32 errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags) { unsigned int group, left, c, d; char *buf, *cp; blk_t blk; ssize_t actual; errcode_t retval; buf = xmalloc(fs->blocksize * BUF_BLOCKS); for (group = 0; group < fs->group_desc_count; group++) { blk = fs->group_desc[(unsigned)group].bg_inode_table; if (!blk) return EXT2_ET_MISSING_INODE_TABLE; left = fs->inode_blocks_per_group; while (left) { c = BUF_BLOCKS; if (c > left) c = left; retval = io_channel_read_blk(fs->io, blk, c, buf); if (retval) goto errout; cp = buf; while (c) { if (!(flags & IMAGER_FLAG_SPARSEWRITE)) { d = c; goto skip_sparse; } /* Skip zero blocks */ if (check_zero_block(cp, fs->blocksize)) { c--; blk++; left--; cp += fs->blocksize; lseek(fd, fs->blocksize, SEEK_CUR); continue; } /* Find non-zero blocks */ for (d=1; d < c; d++) { if (check_zero_block(cp + d*fs->blocksize, fs->blocksize)) break; } skip_sparse: actual = write(fd, cp, fs->blocksize * d); if (actual == -1) { retval = errno; goto errout; } if (actual != (ssize_t) (fs->blocksize * d)) { retval = EXT2_ET_SHORT_WRITE; goto errout; } blk += d; left -= d; cp += fs->blocksize * d; c -= d; } } } retval = 0; errout: free(buf); return retval; } /* * Read in the inode table and stuff it into place */ errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, int flags EXT2FS_ATTR((unused))) { unsigned int group, c, left; char *buf; blk_t blk; ssize_t actual; errcode_t retval; buf = xmalloc(fs->blocksize * BUF_BLOCKS); for (group = 0; group < fs->group_desc_count; group++) { blk = fs->group_desc[(unsigned)group].bg_inode_table; if (!blk) { retval = EXT2_ET_MISSING_INODE_TABLE; goto errout; } left = fs->inode_blocks_per_group; while (left) { c = BUF_BLOCKS; if (c > left) c = left; actual = read(fd, buf, fs->blocksize * c); if (actual == -1) { retval = errno; goto errout; } if (actual != (ssize_t) (fs->blocksize * c)) { retval = EXT2_ET_SHORT_READ; goto errout; } retval = io_channel_write_blk(fs->io, blk, c, buf); if (retval) goto errout; blk += c; left -= c; } } retval = ext2fs_flush_icache(fs); errout: free(buf); return retval; } /* * Write out superblock and group descriptors */ errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, int flags EXT2FS_ATTR((unused))) { char *buf, *cp; ssize_t actual; errcode_t retval; buf = xmalloc(fs->blocksize); /* * Write out the superblock */ memset(buf, 0, fs->blocksize); memcpy(buf, fs->super, SUPERBLOCK_SIZE); actual = write(fd, buf, fs->blocksize); if (actual == -1) { retval = errno; goto errout; } if (actual != (ssize_t) fs->blocksize) { retval = EXT2_ET_SHORT_WRITE; goto errout; } /* * Now write out the block group descriptors */ cp = (char *) fs->group_desc; actual = write(fd, cp, fs->blocksize * fs->desc_blocks); if (actual == -1) { retval = errno; goto errout; } if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) { retval = EXT2_ET_SHORT_WRITE; goto errout; } retval = 0; errout: free(buf); return retval; } /* * Read the superblock and group descriptors and overwrite them. */ errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, int flags EXT2FS_ATTR((unused))) { char *buf; ssize_t actual, size; errcode_t retval; size = fs->blocksize * (fs->group_desc_count + 1); buf = xmalloc(size); /* * Read it all in. */ actual = read(fd, buf, size); if (actual == -1) { retval = errno; goto errout; } if (actual != size) { retval = EXT2_ET_SHORT_READ; goto errout; } /* * Now copy in the superblock and group descriptors */ memcpy(fs->super, buf, SUPERBLOCK_SIZE); memcpy(fs->group_desc, buf + fs->blocksize, fs->blocksize * fs->group_desc_count); retval = 0; errout: free(buf); return retval; } /* * Write the block/inode bitmaps. */ errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags) { char *ptr; int c, size; char zero_buf[1024]; ssize_t actual; errcode_t retval; if (flags & IMAGER_FLAG_INODEMAP) { if (!fs->inode_map) { retval = ext2fs_read_inode_bitmap(fs); if (retval) return retval; } ptr = fs->inode_map->bitmap; size = (EXT2_INODES_PER_GROUP(fs->super) / 8); } else { if (!fs->block_map) { retval = ext2fs_read_block_bitmap(fs); if (retval) return retval; } ptr = fs->block_map->bitmap; size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; } size = size * fs->group_desc_count; actual = write(fd, ptr, size); if (actual == -1) { retval = errno; goto errout; } if (actual != size) { retval = EXT2_ET_SHORT_WRITE; goto errout; } size = size % fs->blocksize; memset(zero_buf, 0, sizeof(zero_buf)); if (size) { size = fs->blocksize - size; while (size) { c = size; if (c > (int) sizeof(zero_buf)) c = sizeof(zero_buf); actual = write(fd, zero_buf, c); if (actual == -1) { retval = errno; goto errout; } if (actual != c) { retval = EXT2_ET_SHORT_WRITE; goto errout; } size -= c; } } retval = 0; errout: return retval; } /* * Read the block/inode bitmaps. */ errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags) { char *ptr, *buf = NULL; int size; ssize_t actual; errcode_t retval; if (flags & IMAGER_FLAG_INODEMAP) { if (!fs->inode_map) { retval = ext2fs_read_inode_bitmap(fs); if (retval) return retval; } ptr = fs->inode_map->bitmap; size = (EXT2_INODES_PER_GROUP(fs->super) / 8); } else { if (!fs->block_map) { retval = ext2fs_read_block_bitmap(fs); if (retval) return retval; } ptr = fs->block_map->bitmap; size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; } size = size * fs->group_desc_count; buf = xmalloc(size); actual = read(fd, buf, size); if (actual == -1) { retval = errno; goto errout; } if (actual != size) { retval = EXT2_ET_SHORT_WRITE; goto errout; } memcpy(ptr, buf, size); retval = 0; errout: free(buf); return retval; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/ext2_types.h0000644000000000000000000000006212263563520022144 0ustar rootroot/* vi: set sw=4 ts=4: */ #include busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/link.c0000644000000000000000000000614512263563520020776 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * link.c --- create links in a ext2fs directory * * Copyright (C) 1993, 1994 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct link_struct { const char *name; int namelen; ext2_ino_t inode; int flags; int done; struct ext2_super_block *sb; }; static int link_proc(struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data) { struct link_struct *ls = (struct link_struct *) priv_data; struct ext2_dir_entry *next; int rec_len, min_rec_len; int ret = 0; rec_len = EXT2_DIR_REC_LEN(ls->namelen); /* * See if the following directory entry (if any) is unused; * if so, absorb it into this one. */ next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len); if ((offset + dirent->rec_len < blocksize - 8) && (next->inode == 0) && (offset + dirent->rec_len + next->rec_len <= blocksize)) { dirent->rec_len += next->rec_len; ret = DIRENT_CHANGED; } /* * If the directory entry is used, see if we can split the * directory entry to make room for the new name. If so, * truncate it and return. */ if (dirent->inode) { min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF); if (dirent->rec_len < (min_rec_len + rec_len)) return ret; rec_len = dirent->rec_len - min_rec_len; dirent->rec_len = min_rec_len; next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len); next->inode = 0; next->name_len = 0; next->rec_len = rec_len; return DIRENT_CHANGED; } /* * If we get this far, then the directory entry is not used. * See if we can fit the request entry in. If so, do it. */ if (dirent->rec_len < rec_len) return ret; dirent->inode = ls->inode; dirent->name_len = ls->namelen; strncpy(dirent->name, ls->name, ls->namelen); if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) dirent->name_len |= (ls->flags & 0x7) << 8; ls->done++; return DIRENT_ABORT|DIRENT_CHANGED; } /* * Note: the low 3 bits of the flags field are used as the directory * entry filetype. */ #ifdef __TURBOC__ # pragma argsused #endif errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name, ext2_ino_t ino, int flags) { errcode_t retval; struct link_struct ls; struct ext2_inode inode; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; ls.name = name; ls.namelen = name ? strlen(name) : 0; ls.inode = ino; ls.flags = flags; ls.done = 0; ls.sb = fs->super; retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY, 0, link_proc, &ls); if (retval) return retval; if (!ls.done) return EXT2_ET_DIR_NO_SPACE; if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0) return retval; if (inode.i_flags & EXT2_INDEX_FL) { inode.i_flags &= ~EXT2_INDEX_FL; if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0) return retval; } return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h0000644000000000000000000000360112263563520022634 0ustar rootroot/* vi: set sw=4 ts=4: */ /* File: linux/ext2_ext_attr.h On-disk format of extended attributes for the ext2 filesystem. (C) 2000 Andreas Gruenbacher, */ /* Magic value in attribute blocks */ #define EXT2_EXT_ATTR_MAGIC_v1 0xEA010000 #define EXT2_EXT_ATTR_MAGIC 0xEA020000 /* Maximum number of references to one attribute block */ #define EXT2_EXT_ATTR_REFCOUNT_MAX 1024 struct ext2_ext_attr_header { __u32 h_magic; /* magic number for identification */ __u32 h_refcount; /* reference count */ __u32 h_blocks; /* number of disk blocks used */ __u32 h_hash; /* hash value of all attributes */ __u32 h_reserved[4]; /* zero right now */ }; struct ext2_ext_attr_entry { __u8 e_name_len; /* length of name */ __u8 e_name_index; /* attribute name index */ __u16 e_value_offs; /* offset in disk block of value */ __u32 e_value_block; /* disk block attribute is stored on (n/i) */ __u32 e_value_size; /* size of attribute value */ __u32 e_hash; /* hash value of name and value */ }; #define EXT2_EXT_ATTR_PAD_BITS 2 #define EXT2_EXT_ATTR_PAD (1<e_name_len)) ) #define EXT2_EXT_ATTR_SIZE(size) \ (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND) #define EXT2_EXT_IS_LAST_ENTRY(entry) (*((__u32 *)(entry)) == 0UL) #define EXT2_EXT_ATTR_NAME(entry) \ (((char *) (entry)) + sizeof(struct ext2_ext_attr_entry)) #define EXT2_XATTR_LEN(name_len) \ (((name_len) + EXT2_EXT_ATTR_ROUND + \ sizeof(struct ext2_xattr_entry)) & ~EXT2_EXT_ATTR_ROUND) #define EXT2_XATTR_SIZE(size) \ (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND) busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/alloc_sb.c0000644000000000000000000000257212263563520021617 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * alloc_sb.c --- Allocate the superblock and block group descriptors for a * newly initialized filesystem. Used by mke2fs when initializing a filesystem * * Copyright (C) 1994, 1995, 1996, 2003 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" int ext2fs_reserve_super_and_bgd(ext2_filsys fs, dgrp_t group, ext2fs_block_bitmap bmap) { blk_t super_blk, old_desc_blk, new_desc_blk; int j, old_desc_blocks, num_blocks; num_blocks = ext2fs_super_and_bgd_loc(fs, group, &super_blk, &old_desc_blk, &new_desc_blk, 0); if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) old_desc_blocks = fs->super->s_first_meta_bg; else old_desc_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks; if (super_blk || (group == 0)) ext2fs_mark_block_bitmap(bmap, super_blk); if (old_desc_blk) { for (j=0; j < old_desc_blocks; j++) ext2fs_mark_block_bitmap(bmap, old_desc_blk + j); } if (new_desc_blk) ext2fs_mark_block_bitmap(bmap, new_desc_blk); return num_blocks; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/gen_bitmap.c0000644000000000000000000000207512263563520022144 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * gen_bitmap.c --- Generic bitmap routines that used to be inlined. * * Copyright (C) 2001 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap, __u32 bitno) { if ((bitno < bitmap->start) || (bitno > bitmap->end)) { ext2fs_warn_bitmap2(bitmap, EXT2FS_MARK_ERROR, bitno); return 0; } return ext2fs_set_bit(bitno - bitmap->start, bitmap->bitmap); } int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap, blk_t bitno) { if ((bitno < bitmap->start) || (bitno > bitmap->end)) { ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, bitno); return 0; } return ext2fs_clear_bit(bitno - bitmap->start, bitmap->bitmap); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/test_io.c0000644000000000000000000002443312263563520021507 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * test_io.c --- This is the Test I/O interface. * * Copyright (C) 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" /* * For checking structure magic numbers... */ #define EXT2_CHECK_MAGIC(struct, code) \ if ((struct)->magic != (code)) return (code) struct test_private_data { int magic; io_channel real; int flags; FILE *outfile; unsigned long block; int read_abort_count, write_abort_count; void (*read_blk)(unsigned long block, int count, errcode_t err); void (*write_blk)(unsigned long block, int count, errcode_t err); void (*set_blksize)(int blksize, errcode_t err); void (*write_byte)(unsigned long block, int count, errcode_t err); }; static errcode_t test_open(const char *name, int flags, io_channel *channel); static errcode_t test_close(io_channel channel); static errcode_t test_set_blksize(io_channel channel, int blksize); static errcode_t test_read_blk(io_channel channel, unsigned long block, int count, void *data); static errcode_t test_write_blk(io_channel channel, unsigned long block, int count, const void *data); static errcode_t test_flush(io_channel channel); static errcode_t test_write_byte(io_channel channel, unsigned long offset, int count, const void *buf); static errcode_t test_set_option(io_channel channel, const char *option, const char *arg); static struct struct_io_manager struct_test_manager = { EXT2_ET_MAGIC_IO_MANAGER, "Test I/O Manager", test_open, test_close, test_set_blksize, test_read_blk, test_write_blk, test_flush, test_write_byte, test_set_option }; io_manager test_io_manager = &struct_test_manager; /* * These global variable can be set by the test program as * necessary *before* calling test_open */ io_manager test_io_backing_manager = 0; void (*test_io_cb_read_blk) (unsigned long block, int count, errcode_t err) = 0; void (*test_io_cb_write_blk) (unsigned long block, int count, errcode_t err) = 0; void (*test_io_cb_set_blksize) (int blksize, errcode_t err) = 0; void (*test_io_cb_write_byte) (unsigned long block, int count, errcode_t err) = 0; /* * Test flags */ #define TEST_FLAG_READ 0x01 #define TEST_FLAG_WRITE 0x02 #define TEST_FLAG_SET_BLKSIZE 0x04 #define TEST_FLAG_FLUSH 0x08 #define TEST_FLAG_DUMP 0x10 #define TEST_FLAG_SET_OPTION 0x20 static void test_dump_block(io_channel channel, struct test_private_data *data, unsigned long block, const void *buf) { const unsigned char *cp; FILE *f = data->outfile; int i; unsigned long cksum = 0; for (i=0, cp = buf; i < channel->block_size; i++, cp++) { cksum += *cp; } fprintf(f, "Contents of block %lu, checksum %08lu:\n", block, cksum); for (i=0, cp = buf; i < channel->block_size; i++, cp++) { if ((i % 16) == 0) fprintf(f, "%04x: ", i); fprintf(f, "%02x%c", *cp, ((i % 16) == 15) ? '\n' : ' '); } } static void test_abort(io_channel channel, unsigned long block) { struct test_private_data *data; FILE *f; data = (struct test_private_data *) channel->private_data; f = data->outfile; test_flush(channel); fprintf(f, "Aborting due to I/O to block %lu\n", block); fflush(f); abort(); } static errcode_t test_open(const char *name, int flags, io_channel *channel) { io_channel io = NULL; struct test_private_data *data = NULL; errcode_t retval; char *value; if (name == 0) return EXT2_ET_BAD_DEVICE_NAME; retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); if (retval) return retval; memset(io, 0, sizeof(struct struct_io_channel)); io->magic = EXT2_ET_MAGIC_IO_CHANNEL; retval = ext2fs_get_mem(sizeof(struct test_private_data), &data); if (retval) { retval = EXT2_ET_NO_MEMORY; goto cleanup; } io->manager = test_io_manager; retval = ext2fs_get_mem(strlen(name)+1, &io->name); if (retval) goto cleanup; strcpy(io->name, name); io->private_data = data; io->block_size = 1024; io->read_error = 0; io->write_error = 0; io->refcount = 1; memset(data, 0, sizeof(struct test_private_data)); data->magic = EXT2_ET_MAGIC_TEST_IO_CHANNEL; if (test_io_backing_manager) { retval = test_io_backing_manager->open(name, flags, &data->real); if (retval) goto cleanup; } else data->real = 0; data->read_blk = test_io_cb_read_blk; data->write_blk = test_io_cb_write_blk; data->set_blksize = test_io_cb_set_blksize; data->write_byte = test_io_cb_write_byte; data->outfile = NULL; if ((value = getenv("TEST_IO_LOGFILE")) != NULL) data->outfile = fopen_for_write(value); if (!data->outfile) data->outfile = stderr; data->flags = 0; if ((value = getenv("TEST_IO_FLAGS")) != NULL) data->flags = strtoul(value, NULL, 0); data->block = 0; if ((value = getenv("TEST_IO_BLOCK")) != NULL) data->block = strtoul(value, NULL, 0); data->read_abort_count = 0; if ((value = getenv("TEST_IO_READ_ABORT")) != NULL) data->read_abort_count = strtoul(value, NULL, 0); data->write_abort_count = 0; if ((value = getenv("TEST_IO_WRITE_ABORT")) != NULL) data->write_abort_count = strtoul(value, NULL, 0); *channel = io; return 0; cleanup: ext2fs_free_mem(&io); ext2fs_free_mem(&data); return retval; } static errcode_t test_close(io_channel channel) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (--channel->refcount > 0) return 0; if (data->real) retval = io_channel_close(data->real); if (data->outfile && data->outfile != stderr) fclose(data->outfile); ext2fs_free_mem(&channel->private_data); ext2fs_free_mem(&channel->name); ext2fs_free_mem(&channel); return retval; } static errcode_t test_set_blksize(io_channel channel, int blksize) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real) retval = io_channel_set_blksize(data->real, blksize); if (data->set_blksize) data->set_blksize(blksize, retval); if (data->flags & TEST_FLAG_SET_BLKSIZE) fprintf(data->outfile, "Test_io: set_blksize(%d) returned %s\n", blksize, retval ? error_message(retval) : "OK"); channel->block_size = blksize; return retval; } static errcode_t test_read_blk(io_channel channel, unsigned long block, int count, void *buf) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real) retval = io_channel_read_blk(data->real, block, count, buf); if (data->read_blk) data->read_blk(block, count, retval); if (data->flags & TEST_FLAG_READ) fprintf(data->outfile, "Test_io: read_blk(%lu, %d) returned %s\n", block, count, retval ? error_message(retval) : "OK"); if (data->block && data->block == block) { if (data->flags & TEST_FLAG_DUMP) test_dump_block(channel, data, block, buf); if (--data->read_abort_count == 0) test_abort(channel, block); } return retval; } static errcode_t test_write_blk(io_channel channel, unsigned long block, int count, const void *buf) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real) retval = io_channel_write_blk(data->real, block, count, buf); if (data->write_blk) data->write_blk(block, count, retval); if (data->flags & TEST_FLAG_WRITE) fprintf(data->outfile, "Test_io: write_blk(%lu, %d) returned %s\n", block, count, retval ? error_message(retval) : "OK"); if (data->block && data->block == block) { if (data->flags & TEST_FLAG_DUMP) test_dump_block(channel, data, block, buf); if (--data->write_abort_count == 0) test_abort(channel, block); } return retval; } static errcode_t test_write_byte(io_channel channel, unsigned long offset, int count, const void *buf) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real && data->real->manager->write_byte) retval = io_channel_write_byte(data->real, offset, count, buf); if (data->write_byte) data->write_byte(offset, count, retval); if (data->flags & TEST_FLAG_WRITE) fprintf(data->outfile, "Test_io: write_byte(%lu, %d) returned %s\n", offset, count, retval ? error_message(retval) : "OK"); return retval; } /* * Flush data buffers to disk. */ static errcode_t test_flush(io_channel channel) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real) retval = io_channel_flush(data->real); if (data->flags & TEST_FLAG_FLUSH) fprintf(data->outfile, "Test_io: flush() returned %s\n", retval ? error_message(retval) : "OK"); return retval; } static errcode_t test_set_option(io_channel channel, const char *option, const char *arg) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->flags & TEST_FLAG_SET_OPTION) fprintf(data->outfile, "Test_io: set_option(%s, %s) ", option, arg); if (data->real && data->real->manager->set_option) { retval = (data->real->manager->set_option)(data->real, option, arg); if (data->flags & TEST_FLAG_SET_OPTION) fprintf(data->outfile, "returned %s\n", retval ? error_message(retval) : "OK"); } else { if (data->flags & TEST_FLAG_SET_OPTION) fprintf(data->outfile, "not implemented\n"); } return retval; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c0000644000000000000000000001413112263563520021574 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * bb_inode.c --- routines to update the bad block inode. * * WARNING: This routine modifies a lot of state in the filesystem; if * this routine returns an error, the bad block inode may be in an * inconsistent state. * * Copyright (C) 1994, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct set_badblock_record { ext2_badblocks_iterate bb_iter; int bad_block_count; blk_t *ind_blocks; int max_ind_blocks; int ind_blocks_size; int ind_blocks_ptr; char *block_buf; errcode_t err; }; static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block, int ref_offset, void *priv_data); static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block, int ref_offset, void *priv_data); /* * Given a bad blocks bitmap, update the bad blocks inode to reflect * the map. */ errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list) { errcode_t retval; struct set_badblock_record rec; struct ext2_inode inode; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!fs->block_map) return EXT2_ET_NO_BLOCK_BITMAP; rec.bad_block_count = 0; rec.ind_blocks_size = rec.ind_blocks_ptr = 0; rec.max_ind_blocks = 10; retval = ext2fs_get_mem(rec.max_ind_blocks * sizeof(blk_t), &rec.ind_blocks); if (retval) return retval; memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t)); retval = ext2fs_get_mem(fs->blocksize, &rec.block_buf); if (retval) goto cleanup; memset(rec.block_buf, 0, fs->blocksize); rec.err = 0; /* * First clear the old bad blocks (while saving the indirect blocks) */ retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, BLOCK_FLAG_DEPTH_TRAVERSE, 0, clear_bad_block_proc, &rec); if (retval) goto cleanup; if (rec.err) { retval = rec.err; goto cleanup; } /* * Now set the bad blocks! * * First, mark the bad blocks as used. This prevents a bad * block from being used as an indirecto block for the bad * block inode (!). */ if (bb_list) { retval = ext2fs_badblocks_list_iterate_begin(bb_list, &rec.bb_iter); if (retval) goto cleanup; retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, BLOCK_FLAG_APPEND, 0, set_bad_block_proc, &rec); ext2fs_badblocks_list_iterate_end(rec.bb_iter); if (retval) goto cleanup; if (rec.err) { retval = rec.err; goto cleanup; } } /* * Update the bad block inode's mod time and block count * field. */ retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode); if (retval) goto cleanup; inode.i_atime = inode.i_mtime = time(NULL); if (!inode.i_ctime) inode.i_ctime = time(NULL); inode.i_blocks = rec.bad_block_count * (fs->blocksize / 512); inode.i_size = rec.bad_block_count * fs->blocksize; retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode); if (retval) goto cleanup; cleanup: ext2fs_free_mem(&rec.ind_blocks); ext2fs_free_mem(&rec.block_buf); return retval; } /* * Helper function for update_bb_inode() * * Clear the bad blocks in the bad block inode, while saving the * indirect blocks. */ #ifdef __TURBOC__ # pragma argsused #endif static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct set_badblock_record *rec = (struct set_badblock_record *) priv_data; errcode_t retval; unsigned long old_size; if (!*block_nr) return 0; /* * If the block number is outrageous, clear it and ignore it. */ if (*block_nr >= fs->super->s_blocks_count || *block_nr < fs->super->s_first_data_block) { *block_nr = 0; return BLOCK_CHANGED; } if (blockcnt < 0) { if (rec->ind_blocks_size >= rec->max_ind_blocks) { old_size = rec->max_ind_blocks * sizeof(blk_t); rec->max_ind_blocks += 10; retval = ext2fs_resize_mem(old_size, rec->max_ind_blocks * sizeof(blk_t), &rec->ind_blocks); if (retval) { rec->max_ind_blocks -= 10; rec->err = retval; return BLOCK_ABORT; } } rec->ind_blocks[rec->ind_blocks_size++] = *block_nr; } /* * Mark the block as unused, and update accounting information */ ext2fs_block_alloc_stats(fs, *block_nr, -1); *block_nr = 0; return BLOCK_CHANGED; } /* * Helper function for update_bb_inode() * * Set the block list in the bad block inode, using the supplied bitmap. */ #ifdef __TURBOC__ #pragma argsused #endif static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct set_badblock_record *rec = (struct set_badblock_record *) priv_data; errcode_t retval; blk_t blk; if (blockcnt >= 0) { /* * Get the next bad block. */ if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk)) return BLOCK_ABORT; rec->bad_block_count++; } else { /* * An indirect block; fetch a block from the * previously used indirect block list. The block * most be not marked as used; if so, get another one. * If we run out of reserved indirect blocks, allocate * a new one. */ retry: if (rec->ind_blocks_ptr < rec->ind_blocks_size) { blk = rec->ind_blocks[rec->ind_blocks_ptr++]; if (ext2fs_test_block_bitmap(fs->block_map, blk)) goto retry; } else { retval = ext2fs_new_block(fs, 0, 0, &blk); if (retval) { rec->err = retval; return BLOCK_ABORT; } } retval = io_channel_write_blk(fs->io, blk, 1, rec->block_buf); if (retval) { rec->err = retval; return BLOCK_ABORT; } } /* * Update block counts */ ext2fs_block_alloc_stats(fs, blk, +1); *block_nr = blk; return BLOCK_CHANGED; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/expanddir.c0000644000000000000000000000507612263563520022021 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * expand.c --- expand an ext2fs directory * * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct expand_dir_struct { int done; int newblocks; errcode_t err; }; static int expand_dir_proc(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data; blk_t new_blk; static blk_t last_blk = 0; char *block; errcode_t retval; if (*blocknr) { last_blk = *blocknr; return 0; } retval = ext2fs_new_block(fs, last_blk, 0, &new_blk); if (retval) { es->err = retval; return BLOCK_ABORT; } if (blockcnt > 0) { retval = ext2fs_new_dir_block(fs, 0, 0, &block); if (retval) { es->err = retval; return BLOCK_ABORT; } es->done = 1; retval = ext2fs_write_dir_block(fs, new_blk, block); } else { retval = ext2fs_get_mem(fs->blocksize, &block); if (retval) { es->err = retval; return BLOCK_ABORT; } memset(block, 0, fs->blocksize); retval = io_channel_write_blk(fs->io, new_blk, 1, block); } if (retval) { es->err = retval; return BLOCK_ABORT; } ext2fs_free_mem(&block); *blocknr = new_blk; ext2fs_block_alloc_stats(fs, new_blk, +1); es->newblocks++; if (es->done) return (BLOCK_CHANGED | BLOCK_ABORT); else return BLOCK_CHANGED; } errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir) { errcode_t retval; struct expand_dir_struct es; struct ext2_inode inode; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; if (!fs->block_map) return EXT2_ET_NO_BLOCK_BITMAP; retval = ext2fs_check_directory(fs, dir); if (retval) return retval; es.done = 0; es.err = 0; es.newblocks = 0; retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND, 0, expand_dir_proc, &es); if (es.err) return es.err; if (!es.done) return EXT2_ET_EXPAND_DIR_ERR; /* * Update the size and block count fields in the inode. */ retval = ext2fs_read_inode(fs, dir, &inode); if (retval) return retval; inode.i_size += fs->blocksize; inode.i_blocks += (fs->blocksize / 512) * es.newblocks; retval = ext2fs_write_inode(fs, dir, &inode); if (retval) return retval; return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/io_manager.c0000644000000000000000000000246212263563520022140 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * io_manager.c --- the I/O manager abstraction */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" errcode_t io_channel_set_options(io_channel channel, const char *opts) { errcode_t retval = 0; char *next, *ptr, *options, *arg; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); if (!opts) return 0; if (!channel->manager->set_option) return EXT2_ET_INVALID_ARGUMENT; options = malloc(strlen(opts)+1); if (!options) return EXT2_ET_NO_MEMORY; strcpy(options, opts); ptr = options; while (ptr && *ptr) { next = strchr(ptr, '&'); if (next) *next++ = 0; arg = strchr(ptr, '='); if (arg) *arg++ = 0; retval = (channel->manager->set_option)(channel, ptr, arg); if (retval) break; ptr = next; } free(options); return retval; } errcode_t io_channel_write_byte(io_channel channel, unsigned long offset, int count, const void *data) { EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); if (channel->manager->write_byte) return channel->manager->write_byte(channel, offset, count, data); return EXT2_ET_UNIMPLEMENTED; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/e2image.h0000644000000000000000000000251212263563520021351 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * e2image.h --- header file describing the ext2 image format * * Copyright (C) 2000 Theodore Ts'o. * * Note: this uses the POSIX IO interfaces, unlike most of the other * functions in this library. So sue me. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ struct ext2_image_hdr { __u32 magic_number; /* This must be EXT2_ET_MAGIC_E2IMAGE */ char magic_descriptor[16]; /* "Ext2 Image 1.0", w/ null padding */ char fs_hostname[64];/* Hostname of machine of image */ char fs_netaddr[32]; /* Network address */ __u32 fs_netaddr_type;/* 0 = IPV4, 1 = IPV6, etc. */ __u32 fs_device; /* Device number of image */ char fs_device_name[64]; /* Device name */ char fs_uuid[16]; /* UUID of filesystem */ __u32 fs_blocksize; /* Block size of the filesystem */ __u32 fs_reserved[8]; __u32 image_device; /* Device number of image file */ __u32 image_inode; /* Inode number of image file */ __u32 image_time; /* Time of image creation */ __u32 image_reserved[8]; __u32 offset_super; /* Byte offset of the sb and descriptors */ __u32 offset_inode; /* Byte offset of the inode table */ __u32 offset_inodemap; /* Byte offset of the inode bitmaps */ __u32 offset_blockmap; /* Byte offset of the inode bitmaps */ __u32 offset_reserved[8]; }; busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c0000644000000000000000000000276612263563520021772 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * ind_block.c --- indirect block I/O routines * * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf) { errcode_t retval; #if BB_BIG_ENDIAN blk_t *block_nr; int i; int limit = fs->blocksize >> 2; #endif if ((fs->flags & EXT2_FLAG_IMAGE_FILE) && (fs->io != fs->image_io)) memset(buf, 0, fs->blocksize); else { retval = io_channel_read_blk(fs->io, blk, 1, buf); if (retval) return retval; } #if BB_BIG_ENDIAN if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_READ)) { block_nr = (blk_t *) buf; for (i = 0; i < limit; i++, block_nr++) *block_nr = ext2fs_swab32(*block_nr); } #endif return 0; } errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf) { #if BB_BIG_ENDIAN blk_t *block_nr; int i; int limit = fs->blocksize >> 2; #endif if (fs->flags & EXT2_FLAG_IMAGE_FILE) return 0; #if BB_BIG_ENDIAN if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_WRITE)) { block_nr = (blk_t *) buf; for (i = 0; i < limit; i++, block_nr++) *block_nr = ext2fs_swab32(*block_nr); } #endif return io_channel_write_blk(fs->io, blk, 1, buf); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h0000644000000000000000000007274312263563520021270 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * ext2fs.h --- ext2fs * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #ifndef EXT2FS_EXT2FS_H #define EXT2FS_EXT2FS_H 1 #define EXT2FS_ATTR(x) #ifdef __cplusplus extern "C" { #endif /* * Where the master copy of the superblock is located, and how big * superblocks are supposed to be. We define SUPERBLOCK_SIZE because * the size of the superblock structure is not necessarily trustworthy * (some versions have the padding set up so that the superblock is * 1032 bytes long). */ #define SUPERBLOCK_OFFSET 1024 #define SUPERBLOCK_SIZE 1024 /* * The last ext2fs revision level that this version of the library is * able to support. */ #define EXT2_LIB_CURRENT_REV EXT2_DYNAMIC_REV #ifdef HAVE_SYS_TYPES_H #include #endif #include #include #include "ext2_types.h" #include "ext2_fs.h" typedef __u32 ext2_ino_t; typedef __u32 blk_t; typedef __u32 dgrp_t; typedef __u32 ext2_off_t; typedef __s64 e2_blkcnt_t; typedef __u32 ext2_dirhash_t; #include "ext2_io.h" #include "ext2_err.h" typedef struct struct_ext2_filsys *ext2_filsys; struct ext2fs_struct_generic_bitmap { errcode_t magic; ext2_filsys fs; __u32 start, end; __u32 real_end; char * description; char * bitmap; errcode_t base_error_code; __u32 reserved[7]; }; #define EXT2FS_MARK_ERROR 0 #define EXT2FS_UNMARK_ERROR 1 #define EXT2FS_TEST_ERROR 2 typedef struct ext2fs_struct_generic_bitmap *ext2fs_generic_bitmap; typedef struct ext2fs_struct_generic_bitmap *ext2fs_inode_bitmap; typedef struct ext2fs_struct_generic_bitmap *ext2fs_block_bitmap; #define EXT2_FIRST_INODE(s) EXT2_FIRST_INO(s) /* * badblocks list definitions */ typedef struct ext2_struct_u32_list *ext2_badblocks_list; typedef struct ext2_struct_u32_iterate *ext2_badblocks_iterate; typedef struct ext2_struct_u32_list *ext2_u32_list; typedef struct ext2_struct_u32_iterate *ext2_u32_iterate; /* old */ typedef struct ext2_struct_u32_list *badblocks_list; typedef struct ext2_struct_u32_iterate *badblocks_iterate; #define BADBLOCKS_FLAG_DIRTY 1 /* * ext2_dblist structure and abstractions (see dblist.c) */ struct ext2_db_entry { ext2_ino_t ino; blk_t blk; int blockcnt; }; typedef struct ext2_struct_dblist *ext2_dblist; #define DBLIST_ABORT 1 /* * ext2_fileio definitions */ #define EXT2_FILE_WRITE 0x0001 #define EXT2_FILE_CREATE 0x0002 #define EXT2_FILE_MASK 0x00FF #define EXT2_FILE_BUF_DIRTY 0x4000 #define EXT2_FILE_BUF_VALID 0x2000 typedef struct ext2_file *ext2_file_t; #define EXT2_SEEK_SET 0 #define EXT2_SEEK_CUR 1 #define EXT2_SEEK_END 2 /* * Flags for the ext2_filsys structure and for ext2fs_open() */ #define EXT2_FLAG_RW 0x01 #define EXT2_FLAG_CHANGED 0x02 #define EXT2_FLAG_DIRTY 0x04 #define EXT2_FLAG_VALID 0x08 #define EXT2_FLAG_IB_DIRTY 0x10 #define EXT2_FLAG_BB_DIRTY 0x20 #define EXT2_FLAG_SWAP_BYTES 0x40 #define EXT2_FLAG_SWAP_BYTES_READ 0x80 #define EXT2_FLAG_SWAP_BYTES_WRITE 0x100 #define EXT2_FLAG_MASTER_SB_ONLY 0x200 #define EXT2_FLAG_FORCE 0x400 #define EXT2_FLAG_SUPER_ONLY 0x800 #define EXT2_FLAG_JOURNAL_DEV_OK 0x1000 #define EXT2_FLAG_IMAGE_FILE 0x2000 /* * Special flag in the ext2 inode i_flag field that means that this is * a new inode. (So that ext2_write_inode() can clear extra fields.) */ #define EXT2_NEW_INODE_FL 0x80000000 /* * Flags for mkjournal * * EXT2_MKJOURNAL_V1_SUPER Make a (deprecated) V1 journal superblock */ #define EXT2_MKJOURNAL_V1_SUPER 0x0000001 struct struct_ext2_filsys { errcode_t magic; io_channel io; int flags; char * device_name; struct ext2_super_block * super; unsigned int blocksize; int fragsize; dgrp_t group_desc_count; unsigned long desc_blocks; struct ext2_group_desc * group_desc; int inode_blocks_per_group; ext2fs_inode_bitmap inode_map; ext2fs_block_bitmap block_map; errcode_t (*get_blocks)(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks); errcode_t (*check_directory)(ext2_filsys fs, ext2_ino_t ino); errcode_t (*write_bitmaps)(ext2_filsys fs); errcode_t (*read_inode)(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode); errcode_t (*write_inode)(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode); ext2_badblocks_list badblocks; ext2_dblist dblist; __u32 stride; /* for mke2fs */ struct ext2_super_block * orig_super; struct ext2_image_hdr * image_header; __u32 umask; /* * Reserved for future expansion */ __u32 reserved[8]; /* * Reserved for the use of the calling application. */ void * priv_data; /* * Inode cache */ struct ext2_inode_cache *icache; io_channel image_io; }; #include "bitops.h" /* * Return flags for the block iterator functions */ #define BLOCK_CHANGED 1 #define BLOCK_ABORT 2 #define BLOCK_ERROR 4 /* * Block interate flags * * BLOCK_FLAG_APPEND, or BLOCK_FLAG_HOLE, indicates that the interator * function should be called on blocks where the block number is zero. * This is used by ext2fs_expand_dir() to be able to add a new block * to an inode. It can also be used for programs that want to be able * to deal with files that contain "holes". * * BLOCK_FLAG_TRAVERSE indicates that the iterator function for the * indirect, doubly indirect, etc. blocks should be called after all * of the blocks containined in the indirect blocks are processed. * This is useful if you are going to be deallocating blocks from an * inode. * * BLOCK_FLAG_DATA_ONLY indicates that the iterator function should be * called for data blocks only. * * BLOCK_FLAG_NO_LARGE is for internal use only. It informs * ext2fs_block_iterate2 that large files won't be accepted. */ #define BLOCK_FLAG_APPEND 1 #define BLOCK_FLAG_HOLE 1 #define BLOCK_FLAG_DEPTH_TRAVERSE 2 #define BLOCK_FLAG_DATA_ONLY 4 #define BLOCK_FLAG_NO_LARGE 0x1000 /* * Magic "block count" return values for the block iterator function. */ #define BLOCK_COUNT_IND (-1) #define BLOCK_COUNT_DIND (-2) #define BLOCK_COUNT_TIND (-3) #define BLOCK_COUNT_TRANSLATOR (-4) #if 0 /* * Flags for ext2fs_move_blocks */ #define EXT2_BMOVE_GET_DBLIST 0x0001 #define EXT2_BMOVE_DEBUG 0x0002 #endif /* * Flags for directory block reading and writing functions */ #define EXT2_DIRBLOCK_V2_STRUCT 0x0001 /* * Return flags for the directory iterator functions */ #define DIRENT_CHANGED 1 #define DIRENT_ABORT 2 #define DIRENT_ERROR 3 /* * Directory iterator flags */ #define DIRENT_FLAG_INCLUDE_EMPTY 1 #define DIRENT_FLAG_INCLUDE_REMOVED 2 #define DIRENT_DOT_FILE 1 #define DIRENT_DOT_DOT_FILE 2 #define DIRENT_OTHER_FILE 3 #define DIRENT_DELETED_FILE 4 /* * Inode scan definitions */ typedef struct ext2_struct_inode_scan *ext2_inode_scan; /* * ext2fs_scan flags */ #define EXT2_SF_CHK_BADBLOCKS 0x0001 #define EXT2_SF_BAD_INODE_BLK 0x0002 #define EXT2_SF_BAD_EXTRA_BYTES 0x0004 #define EXT2_SF_SKIP_MISSING_ITABLE 0x0008 /* * ext2fs_check_if_mounted flags */ #define EXT2_MF_MOUNTED 1 #define EXT2_MF_ISROOT 2 #define EXT2_MF_READONLY 4 #define EXT2_MF_SWAP 8 #define EXT2_MF_BUSY 16 /* * Ext2/linux mode flags. We define them here so that we don't need * to depend on the OS's sys/stat.h, since we may be compiling on a * non-Linux system. */ #define LINUX_S_IFMT 00170000 #define LINUX_S_IFSOCK 0140000 #define LINUX_S_IFLNK 0120000 #define LINUX_S_IFREG 0100000 #define LINUX_S_IFBLK 0060000 #define LINUX_S_IFDIR 0040000 #define LINUX_S_IFCHR 0020000 #define LINUX_S_IFIFO 0010000 #define LINUX_S_ISUID 0004000 #define LINUX_S_ISGID 0002000 #define LINUX_S_ISVTX 0001000 #define LINUX_S_IRWXU 00700 #define LINUX_S_IRUSR 00400 #define LINUX_S_IWUSR 00200 #define LINUX_S_IXUSR 00100 #define LINUX_S_IRWXG 00070 #define LINUX_S_IRGRP 00040 #define LINUX_S_IWGRP 00020 #define LINUX_S_IXGRP 00010 #define LINUX_S_IRWXO 00007 #define LINUX_S_IROTH 00004 #define LINUX_S_IWOTH 00002 #define LINUX_S_IXOTH 00001 #define LINUX_S_ISLNK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFLNK) #define LINUX_S_ISREG(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFREG) #define LINUX_S_ISDIR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFDIR) #define LINUX_S_ISCHR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFCHR) #define LINUX_S_ISBLK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFBLK) #define LINUX_S_ISFIFO(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFIFO) #define LINUX_S_ISSOCK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFSOCK) /* * ext2 size of an inode */ #define EXT2_I_SIZE(i) ((i)->i_size | ((__u64) (i)->i_size_high << 32)) /* * ext2_icount_t abstraction */ #define EXT2_ICOUNT_OPT_INCREMENT 0x01 typedef struct ext2_icount *ext2_icount_t; /* * Flags for ext2fs_bmap */ #define BMAP_ALLOC 0x0001 #define BMAP_SET 0x0002 /* * Flags for imager.c functions */ #define IMAGER_FLAG_INODEMAP 1 #define IMAGER_FLAG_SPARSEWRITE 2 /* * For checking structure magic numbers... */ #define EXT2_CHECK_MAGIC(struct, code) \ if ((struct)->magic != (code)) return (code) /* * For ext2 compression support */ #define EXT2FS_COMPRESSED_BLKADDR ((blk_t) 0xffffffff) #define HOLE_BLKADDR(_b) ((_b) == 0 || (_b) == EXT2FS_COMPRESSED_BLKADDR) /* * Features supported by this version of the library */ #define EXT2_LIB_FEATURE_COMPAT_SUPP (EXT2_FEATURE_COMPAT_DIR_PREALLOC|\ EXT2_FEATURE_COMPAT_IMAGIC_INODES|\ EXT3_FEATURE_COMPAT_HAS_JOURNAL|\ EXT2_FEATURE_COMPAT_RESIZE_INO|\ EXT2_FEATURE_COMPAT_DIR_INDEX|\ EXT2_FEATURE_COMPAT_EXT_ATTR) /* This #ifdef is temporary until compression is fully supported */ #ifdef ENABLE_COMPRESSION #ifndef I_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL /* If the below warning bugs you, then have `CPPFLAGS=-DI_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL' in your environment at configure time. */ #warning "Compression support is experimental" #endif #define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ EXT2_FEATURE_INCOMPAT_COMPRESSION|\ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\ EXT2_FEATURE_INCOMPAT_META_BG|\ EXT3_FEATURE_INCOMPAT_RECOVER) #else #define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\ EXT2_FEATURE_INCOMPAT_META_BG|\ EXT3_FEATURE_INCOMPAT_RECOVER) #endif #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\ EXT2_FEATURE_RO_COMPAT_LARGE_FILE) /* * function prototypes */ /* alloc.c */ extern errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode, ext2fs_inode_bitmap map, ext2_ino_t *ret); extern errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal, ext2fs_block_bitmap map, blk_t *ret); extern errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish, int num, ext2fs_block_bitmap map, blk_t *ret); extern errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal, char *block_buf, blk_t *ret); /* alloc_sb.c */ extern int ext2fs_reserve_super_and_bgd(ext2_filsys fs, dgrp_t group, ext2fs_block_bitmap bmap); /* alloc_stats.c */ void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse); void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino, int inuse, int isdir); void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse); /* alloc_tables.c */ extern errcode_t ext2fs_allocate_tables(ext2_filsys fs); extern errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group, ext2fs_block_bitmap bmap); /* badblocks.c */ extern errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size); extern errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk); extern int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk); extern int ext2fs_u32_list_test(ext2_u32_list bb, blk_t blk); extern errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb, ext2_u32_iterate *ret); extern int ext2fs_u32_list_iterate(ext2_u32_iterate iter, blk_t *blk); extern void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter); extern errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest); extern int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2); extern errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, int size); extern errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, blk_t blk); extern int ext2fs_badblocks_list_test(ext2_badblocks_list bb, blk_t blk); extern int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk); extern void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk); extern errcode_t ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb, ext2_badblocks_iterate *ret); extern int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, blk_t *blk); extern void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter); extern errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src, ext2_badblocks_list *dest); extern int ext2fs_badblocks_equal(ext2_badblocks_list bb1, ext2_badblocks_list bb2); extern int ext2fs_u32_list_count(ext2_u32_list bb); /* bb_compat */ extern errcode_t badblocks_list_create(badblocks_list *ret, int size); extern errcode_t badblocks_list_add(badblocks_list bb, blk_t blk); extern int badblocks_list_test(badblocks_list bb, blk_t blk); extern errcode_t badblocks_list_iterate_begin(badblocks_list bb, badblocks_iterate *ret); extern int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk); extern void badblocks_list_iterate_end(badblocks_iterate iter); extern void badblocks_list_free(badblocks_list bb); /* bb_inode.c */ extern errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list); /* bitmaps.c */ extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs); extern errcode_t ext2fs_write_block_bitmap (ext2_filsys fs); extern errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs); extern errcode_t ext2fs_read_block_bitmap(ext2_filsys fs); extern errcode_t ext2fs_allocate_generic_bitmap(__u32 start, __u32 end, __u32 real_end, const char *descr, ext2fs_generic_bitmap *ret); extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs, const char *descr, ext2fs_block_bitmap *ret); extern errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs, const char *descr, ext2fs_inode_bitmap *ret); extern errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap, ext2_ino_t end, ext2_ino_t *oend); extern errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap, blk_t end, blk_t *oend); extern void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap); extern void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap); extern errcode_t ext2fs_read_bitmaps(ext2_filsys fs); extern errcode_t ext2fs_write_bitmaps(ext2_filsys fs); /* block.c */ extern errcode_t ext2fs_block_iterate(ext2_filsys fs, ext2_ino_t ino, int flags, char *block_buf, int (*func)(ext2_filsys fs, blk_t *blocknr, int blockcnt, void *priv_data), void *priv_data); errcode_t ext2fs_block_iterate2(ext2_filsys fs, ext2_ino_t ino, int flags, char *block_buf, int (*func)(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_blk, int ref_offset, void *priv_data), void *priv_data); /* bmap.c */ extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, char *block_buf, int bmap_flags, blk_t block, blk_t *phys_blk); #if 0 /* bmove.c */ extern errcode_t ext2fs_move_blocks(ext2_filsys fs, ext2fs_block_bitmap reserve, ext2fs_block_bitmap alloc_map, int flags); #endif /* check_desc.c */ extern errcode_t ext2fs_check_desc(ext2_filsys fs); /* closefs.c */ extern errcode_t ext2fs_close(ext2_filsys fs); extern errcode_t ext2fs_flush(ext2_filsys fs); extern int ext2fs_bg_has_super(ext2_filsys fs, int group_block); extern int ext2fs_super_and_bgd_loc(ext2_filsys fs, dgrp_t group, blk_t *ret_super_blk, blk_t *ret_old_desc_blk, blk_t *ret_new_desc_blk, int *ret_meta_bg); extern void ext2fs_update_dynamic_rev(ext2_filsys fs); /* cmp_bitmaps.c */ extern errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1, ext2fs_block_bitmap bm2); extern errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1, ext2fs_inode_bitmap bm2); /* dblist.c */ extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs); extern errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist); extern errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, int blockcnt); extern void ext2fs_dblist_sort(ext2_dblist dblist, int (*sortfunc)(const void *, const void *)); extern errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, int (*func)(ext2_filsys fs, struct ext2_db_entry *db_info, void *priv_data), void *priv_data); extern errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, int blockcnt); extern errcode_t ext2fs_copy_dblist(ext2_dblist src, ext2_dblist *dest); extern int ext2fs_dblist_count(ext2_dblist dblist); /* dblist_dir.c */ extern errcode_t ext2fs_dblist_dir_iterate(ext2_dblist dblist, int flags, char *block_buf, int (*func)(ext2_ino_t dir, int entry, struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data), void *priv_data); /* dirblock.c */ extern errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block, void *buf); extern errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, void *buf, int flags); extern errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block, void *buf); extern errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block, void *buf, int flags); /* dirhash.c */ extern errcode_t ext2fs_dirhash(int version, const char *name, int len, const __u32 *seed, ext2_dirhash_t *ret_hash, ext2_dirhash_t *ret_minor_hash); /* dir_iterate.c */ extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, ext2_ino_t dir, int flags, char *block_buf, int (*func)(struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data), void *priv_data); extern errcode_t ext2fs_dir_iterate2(ext2_filsys fs, ext2_ino_t dir, int flags, char *block_buf, int (*func)(ext2_ino_t dir, int entry, struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data), void *priv_data); /* dupfs.c */ extern errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest); /* expanddir.c */ extern errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir); /* ext_attr.c */ extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf); extern errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *buf); extern errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, char *block_buf, int adjust, __u32 *newcount); /* fileio.c */ extern errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, int flags, ext2_file_t *ret); extern errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino, int flags, ext2_file_t *ret); extern ext2_filsys ext2fs_file_get_fs(ext2_file_t file); extern errcode_t ext2fs_file_close(ext2_file_t file); extern errcode_t ext2fs_file_flush(ext2_file_t file); extern errcode_t ext2fs_file_read(ext2_file_t file, void *buf, unsigned int wanted, unsigned int *got); extern errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, unsigned int nbytes, unsigned int *written); extern errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset, int whence, __u64 *ret_pos); extern errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset, int whence, ext2_off_t *ret_pos); errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size); extern ext2_off_t ext2fs_file_get_size(ext2_file_t file); extern errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size); /* finddev.c */ extern char *ext2fs_find_block_device(dev_t device); /* flushb.c */ extern errcode_t ext2fs_sync_device(int fd, int flushb); /* freefs.c */ extern void ext2fs_free(ext2_filsys fs); extern void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap); extern void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap); extern void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap); extern void ext2fs_free_dblist(ext2_dblist dblist); extern void ext2fs_badblocks_list_free(ext2_badblocks_list bb); extern void ext2fs_u32_list_free(ext2_u32_list bb); /* getsize.c */ extern errcode_t ext2fs_get_device_size(const char *file, int blocksize, blk_t *retblocks); /* getsectsize.c */ errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize); /* imager.c */ extern errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags); extern errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, int flags); extern errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, int flags); extern errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, int flags); extern errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags); extern errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags); /* ind_block.c */ errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf); errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf); /* initialize.c */ extern errcode_t ext2fs_initialize(const char *name, int flags, struct ext2_super_block *param, io_manager manager, ext2_filsys *ret_fs); /* icount.c */ extern void ext2fs_free_icount(ext2_icount_t icount); extern errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size, ext2_icount_t hint, ext2_icount_t *ret); extern errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, unsigned int size, ext2_icount_t *ret); extern errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret); extern errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret); extern errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret); extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, __u16 count); extern ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount); errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *); /* inode.c */ extern errcode_t ext2fs_flush_icache(ext2_filsys fs); extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino, struct ext2_inode *inode, int bufsize); extern errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, ext2_inode_scan *ret_scan); extern void ext2fs_close_inode_scan(ext2_inode_scan scan); extern errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino, struct ext2_inode *inode); extern errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan, int group); extern void ext2fs_set_inode_callback (ext2_inode_scan scan, errcode_t (*done_group)(ext2_filsys fs, dgrp_t group, void * priv_data), void *done_group_data); extern int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags, int clear_flags); extern errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode, int bufsize); extern errcode_t ext2fs_read_inode (ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode); extern errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode, int bufsize); extern errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode); extern errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode); extern errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks); extern errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino); /* inode_io.c */ extern io_manager inode_io_manager; extern errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino, char **name); extern errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, char **name); /* ismounted.c */ extern errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags); extern errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags, char *mtpt, int mtlen); /* namei.c */ extern errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name, int namelen, char *buf, ext2_ino_t *inode); extern errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, const char *name, ext2_ino_t *inode); errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, const char *name, ext2_ino_t *inode); extern errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, ext2_ino_t inode, ext2_ino_t *res_inode); /* native.c */ int ext2fs_native_flag(void); /* newdir.c */ extern errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, ext2_ino_t parent_ino, char **block); /* mkdir.c */ extern errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum, const char *name); /* mkjournal.c */ extern errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, __u32 size, int flags, char **ret_jsb); extern errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev); extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags); /* openfs.c */ extern errcode_t ext2fs_open(const char *name, int flags, int superblock, unsigned int block_size, io_manager manager, ext2_filsys *ret_fs); extern errcode_t ext2fs_open2(const char *name, const char *io_options, int flags, int superblock, unsigned int block_size, io_manager manager, ext2_filsys *ret_fs); extern blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i); errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io); errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io); errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io); /* get_pathname.c */ extern errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino, char **name); /* link.c */ errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name, ext2_ino_t ino, int flags); errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, const char *name, ext2_ino_t ino, int flags); /* read_bb.c */ extern errcode_t ext2fs_read_bb_inode(ext2_filsys fs, ext2_badblocks_list *bb_list); /* read_bb_file.c */ extern errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f, ext2_badblocks_list *bb_list, void *priv_data, void (*invalid)(ext2_filsys fs, blk_t blk, char *badstr, void *priv_data)); extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, ext2_badblocks_list *bb_list, void (*invalid)(ext2_filsys fs, blk_t blk)); /* res_gdt.c */ extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs); /* rs_bitmap.c */ extern errcode_t ext2fs_resize_generic_bitmap(__u32 new_end, __u32 new_real_end, ext2fs_generic_bitmap bmap); extern errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end, ext2fs_inode_bitmap bmap); extern errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end, ext2fs_block_bitmap bmap); extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src, ext2fs_generic_bitmap *dest); /* swapfs.c */ extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header); extern void ext2fs_swap_super(struct ext2_super_block * super); extern void ext2fs_swap_group_desc(struct ext2_group_desc *gdp); extern void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t, struct ext2_inode_large *f, int hostorder, int bufsize); extern void ext2fs_swap_inode(ext2_filsys fs,struct ext2_inode *t, struct ext2_inode *f, int hostorder); /* valid_blk.c */ extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode); /* version.c */ extern int ext2fs_parse_version_string(const char *ver_string); extern int ext2fs_get_library_version(const char **ver_string, const char **date_string); /* write_bb_file.c */ extern errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list, unsigned int flags, FILE *f); /* inline functions */ extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr); extern errcode_t ext2fs_free_mem(void *ptr); extern errcode_t ext2fs_resize_mem(unsigned long old_size, unsigned long size, void *ptr); extern void ext2fs_mark_super_dirty(ext2_filsys fs); extern void ext2fs_mark_changed(ext2_filsys fs); extern int ext2fs_test_changed(ext2_filsys fs); extern void ext2fs_mark_valid(ext2_filsys fs); extern void ext2fs_unmark_valid(ext2_filsys fs); extern int ext2fs_test_valid(ext2_filsys fs); extern void ext2fs_mark_ib_dirty(ext2_filsys fs); extern void ext2fs_mark_bb_dirty(ext2_filsys fs); extern int ext2fs_test_ib_dirty(ext2_filsys fs); extern int ext2fs_test_bb_dirty(ext2_filsys fs); extern int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk); extern int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino); extern blk_t ext2fs_inode_data_blocks(ext2_filsys fs, struct ext2_inode *inode); #ifdef __cplusplus } #endif #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/lookup.c0000644000000000000000000000263612263563520021353 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * lookup.c --- ext2fs directory lookup operations * * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct lookup_struct { const char *name; int len; ext2_ino_t *inode; int found; }; #ifdef __TURBOC__ # pragma argsused #endif static int lookup_proc(struct ext2_dir_entry *dirent, int offset EXT2FS_ATTR((unused)), int blocksize EXT2FS_ATTR((unused)), char *buf EXT2FS_ATTR((unused)), void *priv_data) { struct lookup_struct *ls = (struct lookup_struct *) priv_data; if (ls->len != (dirent->name_len & 0xFF)) return 0; if (strncmp(ls->name, dirent->name, (dirent->name_len & 0xFF))) return 0; *ls->inode = dirent->inode; ls->found++; return DIRENT_ABORT; } errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name, int namelen, char *buf, ext2_ino_t *inode) { errcode_t retval; struct lookup_struct ls; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); ls.name = name; ls.len = namelen; ls.inode = inode; ls.found = 0; retval = ext2fs_dir_iterate(fs, dir, 0, buf, lookup_proc, &ls); if (retval) return retval; return (ls.found) ? 0 : EXT2_ET_FILE_NOT_FOUND; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c0000644000000000000000000000400312263563520022405 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * read_bb_file.c --- read a list of bad blocks from a FILE * * * Copyright (C) 1994, 1995, 2000 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" /* * Reads a list of bad blocks from a FILE * */ errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f, ext2_badblocks_list *bb_list, void *priv_data, void (*invalid)(ext2_filsys fs, blk_t blk, char *badstr, void *priv_data)) { errcode_t retval; blk_t blockno; int count; char buf[128]; if (fs) EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!*bb_list) { retval = ext2fs_badblocks_list_create(bb_list, 10); if (retval) return retval; } while (!feof (f)) { if (fgets(buf, sizeof(buf), f) == NULL) break; count = sscanf(buf, "%u", &blockno); if (count <= 0) continue; if (fs && ((blockno < fs->super->s_first_data_block) || (blockno >= fs->super->s_blocks_count))) { if (invalid) (invalid)(fs, blockno, buf, priv_data); continue; } retval = ext2fs_badblocks_list_add(*bb_list, blockno); if (retval) return retval; } return 0; } static void call_compat_invalid(ext2_filsys fs, blk_t blk, char *badstr EXT2FS_ATTR((unused)), void *priv_data) { void (*invalid)(ext2_filsys, blk_t); invalid = (void (*)(ext2_filsys, blk_t)) priv_data; if (invalid) invalid(fs, blk); } /* * Reads a list of bad blocks from a FILE * */ errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, ext2_badblocks_list *bb_list, void (*invalid)(ext2_filsys fs, blk_t blk)) { return ext2fs_read_bb_FILE2(fs, f, bb_list, (void *) invalid, call_compat_invalid); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/fileio.c0000644000000000000000000001726512263563520021315 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * fileio.c --- Simple file I/O routines * * Copyright (C) 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct ext2_file { errcode_t magic; ext2_filsys fs; ext2_ino_t ino; struct ext2_inode inode; int flags; __u64 pos; blk_t blockno; blk_t physblock; char *buf; }; #define BMAP_BUFFER (file->buf + fs->blocksize) errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, int flags, ext2_file_t *ret) { ext2_file_t file; errcode_t retval; /* * Don't let caller create or open a file for writing if the * filesystem is read-only. */ if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) && !(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; retval = ext2fs_get_mem(sizeof(struct ext2_file), &file); if (retval) return retval; memset(file, 0, sizeof(struct ext2_file)); file->magic = EXT2_ET_MAGIC_EXT2_FILE; file->fs = fs; file->ino = ino; file->flags = flags & EXT2_FILE_MASK; if (inode) { memcpy(&file->inode, inode, sizeof(struct ext2_inode)); } else { retval = ext2fs_read_inode(fs, ino, &file->inode); if (retval) goto fail; } retval = ext2fs_get_mem(fs->blocksize * 3, &file->buf); if (retval) goto fail; *ret = file; return 0; fail: ext2fs_free_mem(&file->buf); ext2fs_free_mem(&file); return retval; } errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino, int flags, ext2_file_t *ret) { return ext2fs_file_open2(fs, ino, NULL, flags, ret); } /* * This function returns the filesystem handle of a file from the structure */ ext2_filsys ext2fs_file_get_fs(ext2_file_t file) { if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) return 0; return file->fs; } /* * This function flushes the dirty block buffer out to disk if * necessary. */ errcode_t ext2fs_file_flush(ext2_file_t file) { errcode_t retval; ext2_filsys fs; EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); fs = file->fs; if (!(file->flags & EXT2_FILE_BUF_VALID) || !(file->flags & EXT2_FILE_BUF_DIRTY)) return 0; /* * OK, the physical block hasn't been allocated yet. * Allocate it. */ if (!file->physblock) { retval = ext2fs_bmap(fs, file->ino, &file->inode, BMAP_BUFFER, file->ino ? BMAP_ALLOC : 0, file->blockno, &file->physblock); if (retval) return retval; } retval = io_channel_write_blk(fs->io, file->physblock, 1, file->buf); if (retval) return retval; file->flags &= ~EXT2_FILE_BUF_DIRTY; return retval; } /* * This function synchronizes the file's block buffer and the current * file position, possibly invalidating block buffer if necessary */ static errcode_t sync_buffer_position(ext2_file_t file) { blk_t b; errcode_t retval; b = file->pos / file->fs->blocksize; if (b != file->blockno) { retval = ext2fs_file_flush(file); if (retval) return retval; file->flags &= ~EXT2_FILE_BUF_VALID; } file->blockno = b; return 0; } /* * This function loads the file's block buffer with valid data from * the disk as necessary. * * If dontfill is true, then skip initializing the buffer since we're * going to be replacing its entire contents anyway. If set, then the * function basically only sets file->physblock and EXT2_FILE_BUF_VALID */ #define DONTFILL 1 static errcode_t load_buffer(ext2_file_t file, int dontfill) { ext2_filsys fs = file->fs; errcode_t retval; if (!(file->flags & EXT2_FILE_BUF_VALID)) { retval = ext2fs_bmap(fs, file->ino, &file->inode, BMAP_BUFFER, 0, file->blockno, &file->physblock); if (retval) return retval; if (!dontfill) { if (file->physblock) { retval = io_channel_read_blk(fs->io, file->physblock, 1, file->buf); if (retval) return retval; } else memset(file->buf, 0, fs->blocksize); } file->flags |= EXT2_FILE_BUF_VALID; } return 0; } errcode_t ext2fs_file_close(ext2_file_t file) { errcode_t retval; EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); retval = ext2fs_file_flush(file); ext2fs_free_mem(&file->buf); ext2fs_free_mem(&file); return retval; } errcode_t ext2fs_file_read(ext2_file_t file, void *buf, unsigned int wanted, unsigned int *got) { ext2_filsys fs; errcode_t retval = 0; unsigned int start, c, count = 0; __u64 left; char *ptr = (char *) buf; EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); fs = file->fs; while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) { retval = sync_buffer_position(file); if (retval) goto fail; retval = load_buffer(file, 0); if (retval) goto fail; start = file->pos % fs->blocksize; c = fs->blocksize - start; if (c > wanted) c = wanted; left = EXT2_I_SIZE(&file->inode) - file->pos; if (c > left) c = left; memcpy(ptr, file->buf+start, c); file->pos += c; ptr += c; count += c; wanted -= c; } fail: if (got) *got = count; return retval; } errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, unsigned int nbytes, unsigned int *written) { ext2_filsys fs; errcode_t retval = 0; unsigned int start, c, count = 0; const char *ptr = (const char *) buf; EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); fs = file->fs; if (!(file->flags & EXT2_FILE_WRITE)) return EXT2_ET_FILE_RO; while (nbytes > 0) { retval = sync_buffer_position(file); if (retval) goto fail; start = file->pos % fs->blocksize; c = fs->blocksize - start; if (c > nbytes) c = nbytes; /* * We only need to do a read-modify-update cycle if * we're doing a partial write. */ retval = load_buffer(file, (c == fs->blocksize)); if (retval) goto fail; file->flags |= EXT2_FILE_BUF_DIRTY; memcpy(file->buf+start, ptr, c); file->pos += c; ptr += c; count += c; nbytes -= c; } fail: if (written) *written = count; return retval; } errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset, int whence, __u64 *ret_pos) { EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); if (whence == EXT2_SEEK_SET) file->pos = offset; else if (whence == EXT2_SEEK_CUR) file->pos += offset; else if (whence == EXT2_SEEK_END) file->pos = EXT2_I_SIZE(&file->inode) + offset; else return EXT2_ET_INVALID_ARGUMENT; if (ret_pos) *ret_pos = file->pos; return 0; } errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset, int whence, ext2_off_t *ret_pos) { __u64 loffset, ret_loffset; errcode_t retval; loffset = offset; retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset); if (ret_pos) *ret_pos = (ext2_off_t) ret_loffset; return retval; } /* * This function returns the size of the file, according to the inode */ errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size) { if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) return EXT2_ET_MAGIC_EXT2_FILE; *ret_size = EXT2_I_SIZE(&file->inode); return 0; } /* * This function returns the size of the file, according to the inode */ ext2_off_t ext2fs_file_get_size(ext2_file_t file) { __u64 size; if (ext2fs_file_get_lsize(file, &size)) return 0; if ((size >> 32) != 0) return 0; return size; } /* * This function sets the size of the file, truncating it if necessary * * XXX still need to call truncate */ errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size) { errcode_t retval; EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); file->inode.i_size = size; file->inode.i_size_high = 0; if (file->ino) { retval = ext2fs_write_inode(file->fs, file->ino, &file->inode); if (retval) return retval; } /* * XXX truncate inode if necessary */ return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c0000644000000000000000000004162512263563520021515 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * unix_io.c --- This is the Unix (well, really POSIX) implementation * of the I/O manager. * * Implements a one-block write-through cache. * * Includes support for Windows NT support under Cygwin. * * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #include #ifdef __linux__ #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include #include "ext2_fs.h" #include "ext2fs.h" /* * For checking structure magic numbers... */ #define EXT2_CHECK_MAGIC(struct, code) \ if ((struct)->magic != (code)) return (code) struct unix_cache { char *buf; unsigned long block; int access_time; unsigned dirty:1; unsigned in_use:1; }; #define CACHE_SIZE 8 #define WRITE_DIRECT_SIZE 4 /* Must be smaller than CACHE_SIZE */ #define READ_DIRECT_SIZE 4 /* Should be smaller than CACHE_SIZE */ struct unix_private_data { int magic; int dev; int flags; int access_time; ext2_loff_t offset; struct unix_cache cache[CACHE_SIZE]; }; static errcode_t unix_open(const char *name, int flags, io_channel *channel); static errcode_t unix_close(io_channel channel); static errcode_t unix_set_blksize(io_channel channel, int blksize); static errcode_t unix_read_blk(io_channel channel, unsigned long block, int count, void *data); static errcode_t unix_write_blk(io_channel channel, unsigned long block, int count, const void *data); static errcode_t unix_flush(io_channel channel); static errcode_t unix_write_byte(io_channel channel, unsigned long offset, int size, const void *data); static errcode_t unix_set_option(io_channel channel, const char *option, const char *arg); static void reuse_cache(io_channel channel, struct unix_private_data *data, struct unix_cache *cache, unsigned long block); /* __FreeBSD_kernel__ is defined by GNU/kFreeBSD - the FreeBSD kernel * does not know buffered block devices - everything is raw. */ #if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #define NEED_BOUNCE_BUFFER #else #undef NEED_BOUNCE_BUFFER #endif static struct struct_io_manager struct_unix_manager = { EXT2_ET_MAGIC_IO_MANAGER, "Unix I/O Manager", unix_open, unix_close, unix_set_blksize, unix_read_blk, unix_write_blk, unix_flush, #ifdef NEED_BOUNCE_BUFFER 0, #else unix_write_byte, #endif unix_set_option }; io_manager unix_io_manager = &struct_unix_manager; /* * Here are the raw I/O functions */ #ifndef NEED_BOUNCE_BUFFER static errcode_t raw_read_blk(io_channel channel, struct unix_private_data *data, unsigned long block, int count, void *buf) { errcode_t retval; ssize_t size; ext2_loff_t location; int actual = 0; size = (count < 0) ? -count : count * channel->block_size; location = ((ext2_loff_t) block * channel->block_size) + data->offset; if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; goto error_out; } actual = read(data->dev, buf, size); if (actual != size) { if (actual < 0) actual = 0; retval = EXT2_ET_SHORT_READ; goto error_out; } return 0; error_out: memset((char *) buf+actual, 0, size-actual); if (channel->read_error) retval = (channel->read_error)(channel, block, count, buf, size, actual, retval); return retval; } #else /* NEED_BOUNCE_BUFFER */ /* * Windows and FreeBSD block devices only allow sector alignment IO in offset and size */ static errcode_t raw_read_blk(io_channel channel, struct unix_private_data *data, unsigned long block, int count, void *buf) { errcode_t retval; size_t size, alignsize, fragment; ext2_loff_t location; int total = 0, actual; #define BLOCKALIGN 512 char sector[BLOCKALIGN]; size = (count < 0) ? -count : count * channel->block_size; location = ((ext2_loff_t) block * channel->block_size) + data->offset; #ifdef DEBUG printf("count=%d, size=%d, block=%d, blk_size=%d, location=%lx\n", count, size, block, channel->block_size, location); #endif if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; goto error_out; } fragment = size % BLOCKALIGN; alignsize = size - fragment; if (alignsize) { actual = read(data->dev, buf, alignsize); if (actual != alignsize) goto short_read; } if (fragment) { actual = read(data->dev, sector, BLOCKALIGN); if (actual != BLOCKALIGN) goto short_read; memcpy(buf+alignsize, sector, fragment); } return 0; short_read: if (actual>0) total += actual; retval = EXT2_ET_SHORT_READ; error_out: memset((char *) buf+total, 0, size-actual); if (channel->read_error) retval = (channel->read_error)(channel, block, count, buf, size, actual, retval); return retval; } #endif static errcode_t raw_write_blk(io_channel channel, struct unix_private_data *data, unsigned long block, int count, const void *buf) { ssize_t size; ext2_loff_t location; int actual = 0; errcode_t retval; if (count == 1) size = channel->block_size; else { if (count < 0) size = -count; else size = count * channel->block_size; } location = ((ext2_loff_t) block * channel->block_size) + data->offset; if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; goto error_out; } actual = write(data->dev, buf, size); if (actual != size) { retval = EXT2_ET_SHORT_WRITE; goto error_out; } return 0; error_out: if (channel->write_error) retval = (channel->write_error)(channel, block, count, buf, size, actual, retval); return retval; } /* * Here we implement the cache functions */ /* Allocate the cache buffers */ static errcode_t alloc_cache(io_channel channel, struct unix_private_data *data) { errcode_t retval; struct unix_cache *cache; int i; data->access_time = 0; for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { cache->block = 0; cache->access_time = 0; cache->dirty = 0; cache->in_use = 0; if ((retval = ext2fs_get_mem(channel->block_size, &cache->buf))) return retval; } return 0; } /* Free the cache buffers */ static void free_cache(struct unix_private_data *data) { struct unix_cache *cache; int i; data->access_time = 0; for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { cache->block = 0; cache->access_time = 0; cache->dirty = 0; cache->in_use = 0; ext2fs_free_mem(&cache->buf); cache->buf = 0; } } #ifndef NO_IO_CACHE /* * Try to find a block in the cache. If the block is not found, and * eldest is a non-zero pointer, then fill in eldest with the cache * entry to that should be reused. */ static struct unix_cache *find_cached_block(struct unix_private_data *data, unsigned long block, struct unix_cache **eldest) { struct unix_cache *cache, *unused_cache, *oldest_cache; int i; unused_cache = oldest_cache = 0; for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { if (!cache->in_use) { if (!unused_cache) unused_cache = cache; continue; } if (cache->block == block) { cache->access_time = ++data->access_time; return cache; } if (!oldest_cache || (cache->access_time < oldest_cache->access_time)) oldest_cache = cache; } if (eldest) *eldest = (unused_cache) ? unused_cache : oldest_cache; return 0; } /* * Reuse a particular cache entry for another block. */ static void reuse_cache(io_channel channel, struct unix_private_data *data, struct unix_cache *cache, unsigned long block) { if (cache->dirty && cache->in_use) raw_write_blk(channel, data, cache->block, 1, cache->buf); cache->in_use = 1; cache->dirty = 0; cache->block = block; cache->access_time = ++data->access_time; } /* * Flush all of the blocks in the cache */ static errcode_t flush_cached_blocks(io_channel channel, struct unix_private_data *data, int invalidate) { struct unix_cache *cache; errcode_t retval, retval2; int i; retval2 = 0; for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { if (!cache->in_use) continue; if (invalidate) cache->in_use = 0; if (!cache->dirty) continue; retval = raw_write_blk(channel, data, cache->block, 1, cache->buf); if (retval) retval2 = retval; else cache->dirty = 0; } return retval2; } #endif /* NO_IO_CACHE */ static errcode_t unix_open(const char *name, int flags, io_channel *channel) { io_channel io = NULL; struct unix_private_data *data = NULL; errcode_t retval; int open_flags; struct stat st; #ifdef __linux__ struct utsname ut; #endif if (name == 0) return EXT2_ET_BAD_DEVICE_NAME; retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); if (retval) return retval; memset(io, 0, sizeof(struct struct_io_channel)); io->magic = EXT2_ET_MAGIC_IO_CHANNEL; retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data); if (retval) goto cleanup; io->manager = unix_io_manager; retval = ext2fs_get_mem(strlen(name)+1, &io->name); if (retval) goto cleanup; strcpy(io->name, name); io->private_data = data; io->block_size = 1024; io->read_error = 0; io->write_error = 0; io->refcount = 1; memset(data, 0, sizeof(struct unix_private_data)); data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL; if ((retval = alloc_cache(io, data))) goto cleanup; open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY; #ifdef CONFIG_LFS data->dev = open64(io->name, open_flags); #else data->dev = open(io->name, open_flags); #endif if (data->dev < 0) { retval = errno; goto cleanup; } #ifdef __linux__ #undef RLIM_INFINITY #if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4))) #define RLIM_INFINITY ((unsigned long)(~0UL>>1)) #else #define RLIM_INFINITY (~0UL) #endif /* * Work around a bug in 2.4.10-2.4.18 kernels where writes to * block devices are wrongly getting hit by the filesize * limit. This workaround isn't perfect, since it won't work * if glibc wasn't built against 2.2 header files. (Sigh.) * */ if ((flags & IO_FLAG_RW) && (uname(&ut) == 0) && ((ut.release[0] == '2') && (ut.release[1] == '.') && (ut.release[2] == '4') && (ut.release[3] == '.') && (ut.release[4] == '1') && (ut.release[5] >= '0') && (ut.release[5] < '8')) && (fstat(data->dev, &st) == 0) && (S_ISBLK(st.st_mode))) { struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY; setrlimit(RLIMIT_FSIZE, &rlim); getrlimit(RLIMIT_FSIZE, &rlim); if (((unsigned long) rlim.rlim_cur) < ((unsigned long) rlim.rlim_max)) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_FSIZE, &rlim); } } #endif *channel = io; return 0; cleanup: if (data) { free_cache(data); ext2fs_free_mem(&data); } ext2fs_free_mem(&io); return retval; } static errcode_t unix_close(io_channel channel) { struct unix_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct unix_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); if (--channel->refcount > 0) return 0; #ifndef NO_IO_CACHE retval = flush_cached_blocks(channel, data, 0); #endif if (close(data->dev) < 0) retval = errno; free_cache(data); ext2fs_free_mem(&channel->private_data); ext2fs_free_mem(&channel->name); ext2fs_free_mem(&channel); return retval; } static errcode_t unix_set_blksize(io_channel channel, int blksize) { struct unix_private_data *data; errcode_t retval; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct unix_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); if (channel->block_size != blksize) { #ifndef NO_IO_CACHE if ((retval = flush_cached_blocks(channel, data, 0))) return retval; #endif channel->block_size = blksize; free_cache(data); if ((retval = alloc_cache(channel, data))) return retval; } return 0; } static errcode_t unix_read_blk(io_channel channel, unsigned long block, int count, void *buf) { struct unix_private_data *data; struct unix_cache *cache, *reuse[READ_DIRECT_SIZE]; errcode_t retval; char *cp; int i, j; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct unix_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); #ifdef NO_IO_CACHE return raw_read_blk(channel, data, block, count, buf); #else /* * If we're doing an odd-sized read or a very large read, * flush out the cache and then do a direct read. */ if (count < 0 || count > WRITE_DIRECT_SIZE) { if ((retval = flush_cached_blocks(channel, data, 0))) return retval; return raw_read_blk(channel, data, block, count, buf); } cp = buf; while (count > 0) { /* If it's in the cache, use it! */ if ((cache = find_cached_block(data, block, &reuse[0]))) { #ifdef DEBUG printf("Using cached block %lu\n", block); #endif memcpy(cp, cache->buf, channel->block_size); count--; block++; cp += channel->block_size; continue; } /* * Find the number of uncached blocks so we can do a * single read request */ for (i=1; i < count; i++) if (find_cached_block(data, block+i, &reuse[i])) break; #ifdef DEBUG printf("Reading %d blocks starting at %lu\n", i, block); #endif if ((retval = raw_read_blk(channel, data, block, i, cp))) return retval; /* Save the results in the cache */ for (j=0; j < i; j++) { count--; cache = reuse[j]; reuse_cache(channel, data, cache, block++); memcpy(cache->buf, cp, channel->block_size); cp += channel->block_size; } } return 0; #endif /* NO_IO_CACHE */ } static errcode_t unix_write_blk(io_channel channel, unsigned long block, int count, const void *buf) { struct unix_private_data *data; struct unix_cache *cache, *reuse; errcode_t retval = 0; const char *cp; int writethrough; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct unix_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); #ifdef NO_IO_CACHE return raw_write_blk(channel, data, block, count, buf); #else /* * If we're doing an odd-sized write or a very large write, * flush out the cache completely and then do a direct write. */ if (count < 0 || count > WRITE_DIRECT_SIZE) { if ((retval = flush_cached_blocks(channel, data, 1))) return retval; return raw_write_blk(channel, data, block, count, buf); } /* * For a moderate-sized multi-block write, first force a write * if we're in write-through cache mode, and then fill the * cache with the blocks. */ writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH; if (writethrough) retval = raw_write_blk(channel, data, block, count, buf); cp = buf; while (count > 0) { cache = find_cached_block(data, block, &reuse); if (!cache) { cache = reuse; reuse_cache(channel, data, cache, block); } memcpy(cache->buf, cp, channel->block_size); cache->dirty = !writethrough; count--; block++; cp += channel->block_size; } return retval; #endif /* NO_IO_CACHE */ } static errcode_t unix_write_byte(io_channel channel, unsigned long offset, int size, const void *buf) { struct unix_private_data *data; errcode_t retval = 0; ssize_t actual; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct unix_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); #ifndef NO_IO_CACHE /* * Flush out the cache completely */ if ((retval = flush_cached_blocks(channel, data, 1))) return retval; #endif if (lseek(data->dev, offset + data->offset, SEEK_SET) < 0) return errno; actual = write(data->dev, buf, size); if (actual != size) return EXT2_ET_SHORT_WRITE; return 0; } /* * Flush data buffers to disk. */ static errcode_t unix_flush(io_channel channel) { struct unix_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct unix_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); #ifndef NO_IO_CACHE retval = flush_cached_blocks(channel, data, 0); #endif fsync(data->dev); return retval; } static errcode_t unix_set_option(io_channel channel, const char *option, const char *arg) { struct unix_private_data *data; unsigned long tmp; char *end; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct unix_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); if (!strcmp(option, "offset")) { if (!arg) return EXT2_ET_INVALID_ARGUMENT; tmp = strtoul(arg, &end, 0); if (*end) return EXT2_ET_INVALID_ARGUMENT; data->offset = tmp; return 0; } return EXT2_ET_INVALID_ARGUMENT; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/irel_ma.c0000644000000000000000000002114712263563520021450 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * irel_ma.c * * Copyright (C) 1996, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #include "irel.h" static errcode_t ima_put(ext2_irel irel, ext2_ino_t old, struct ext2_inode_relocate_entry *ent); static errcode_t ima_get(ext2_irel irel, ext2_ino_t old, struct ext2_inode_relocate_entry *ent); static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, struct ext2_inode_relocate_entry *ent); static errcode_t ima_start_iter(ext2_irel irel); static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old, struct ext2_inode_relocate_entry *ent); static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino, struct ext2_inode_reference *ref); static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino); static errcode_t ima_next_ref(ext2_irel irel, struct ext2_inode_reference *ref); static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new); static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old); static errcode_t ima_free(ext2_irel irel); /* * This data structure stores the array of inode references; there is * a structure for each inode. */ struct inode_reference_entry { __u16 num; struct ext2_inode_reference *refs; }; struct irel_ma { __u32 magic; ext2_ino_t max_inode; ext2_ino_t ref_current; int ref_iter; ext2_ino_t *orig_map; struct ext2_inode_relocate_entry *entries; struct inode_reference_entry *ref_entries; }; errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode, ext2_irel *new_irel) { ext2_irel irel = 0; errcode_t retval; struct irel_ma *ma = 0; size_t size; *new_irel = 0; /* * Allocate memory structures */ retval = ext2fs_get_mem(sizeof(struct ext2_inode_relocation_table), &irel); if (retval) goto errout; memset(irel, 0, sizeof(struct ext2_inode_relocation_table)); retval = ext2fs_get_mem(strlen(name)+1, &irel->name); if (retval) goto errout; strcpy(irel->name, name); retval = ext2fs_get_mem(sizeof(struct irel_ma), &ma); if (retval) goto errout; memset(ma, 0, sizeof(struct irel_ma)); irel->priv_data = ma; size = (size_t) (sizeof(ext2_ino_t) * (max_inode+1)); retval = ext2fs_get_mem(size, &ma->orig_map); if (retval) goto errout; memset(ma->orig_map, 0, size); size = (size_t) (sizeof(struct ext2_inode_relocate_entry) * (max_inode+1)); retval = ext2fs_get_mem(size, &ma->entries); if (retval) goto errout; memset(ma->entries, 0, size); size = (size_t) (sizeof(struct inode_reference_entry) * (max_inode+1)); retval = ext2fs_get_mem(size, &ma->ref_entries); if (retval) goto errout; memset(ma->ref_entries, 0, size); ma->max_inode = max_inode; /* * Fill in the irel data structure */ irel->put = ima_put; irel->get = ima_get; irel->get_by_orig = ima_get_by_orig; irel->start_iter = ima_start_iter; irel->next = ima_next; irel->add_ref = ima_add_ref; irel->start_iter_ref = ima_start_iter_ref; irel->next_ref = ima_next_ref; irel->move = ima_move; irel->delete = ima_delete; irel->free = ima_free; *new_irel = irel; return 0; errout: ima_free(irel); return retval; } static errcode_t ima_put(ext2_irel irel, ext2_ino_t old, struct ext2_inode_relocate_entry *ent) { struct inode_reference_entry *ref_ent; struct irel_ma *ma; errcode_t retval; size_t size, old_size; ma = irel->priv_data; if (old > ma->max_inode) return EXT2_ET_INVALID_ARGUMENT; /* * Force the orig field to the correct value; the application * program shouldn't be messing with this field. */ if (ma->entries[(unsigned) old].new == 0) ent->orig = old; else ent->orig = ma->entries[(unsigned) old].orig; /* * If max_refs has changed, reallocate the refs array */ ref_ent = ma->ref_entries + (unsigned) old; if (ref_ent->refs && ent->max_refs != ma->entries[(unsigned) old].max_refs) { size = (sizeof(struct ext2_inode_reference) * ent->max_refs); old_size = (sizeof(struct ext2_inode_reference) * ma->entries[(unsigned) old].max_refs); retval = ext2fs_resize_mem(old_size, size, &ref_ent->refs); if (retval) return retval; } ma->entries[(unsigned) old] = *ent; ma->orig_map[(unsigned) ent->orig] = old; return 0; } static errcode_t ima_get(ext2_irel irel, ext2_ino_t old, struct ext2_inode_relocate_entry *ent) { struct irel_ma *ma; ma = irel->priv_data; if (old > ma->max_inode) return EXT2_ET_INVALID_ARGUMENT; if (ma->entries[(unsigned) old].new == 0) return ENOENT; *ent = ma->entries[(unsigned) old]; return 0; } static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, struct ext2_inode_relocate_entry *ent) { struct irel_ma *ma; ext2_ino_t ino; ma = irel->priv_data; if (orig > ma->max_inode) return EXT2_ET_INVALID_ARGUMENT; ino = ma->orig_map[(unsigned) orig]; if (ino == 0) return ENOENT; *old = ino; *ent = ma->entries[(unsigned) ino]; return 0; } static errcode_t ima_start_iter(ext2_irel irel) { irel->current = 0; return 0; } static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old, struct ext2_inode_relocate_entry *ent) { struct irel_ma *ma; ma = irel->priv_data; while (++irel->current < ma->max_inode) { if (ma->entries[(unsigned) irel->current].new == 0) continue; *old = irel->current; *ent = ma->entries[(unsigned) irel->current]; return 0; } *old = 0; return 0; } static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino, struct ext2_inode_reference *ref) { struct irel_ma *ma; size_t size; struct inode_reference_entry *ref_ent; struct ext2_inode_relocate_entry *ent; errcode_t retval; ma = irel->priv_data; if (ino > ma->max_inode) return EXT2_ET_INVALID_ARGUMENT; ref_ent = ma->ref_entries + (unsigned) ino; ent = ma->entries + (unsigned) ino; /* * If the inode reference array doesn't exist, create it. */ if (ref_ent->refs == 0) { size = (size_t) ((sizeof(struct ext2_inode_reference) * ent->max_refs)); retval = ext2fs_get_mem(size, &ref_ent->refs); if (retval) return retval; memset(ref_ent->refs, 0, size); ref_ent->num = 0; } if (ref_ent->num >= ent->max_refs) return EXT2_ET_TOO_MANY_REFS; ref_ent->refs[(unsigned) ref_ent->num++] = *ref; return 0; } static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino) { struct irel_ma *ma; ma = irel->priv_data; if (ino > ma->max_inode) return EXT2_ET_INVALID_ARGUMENT; if (ma->entries[(unsigned) ino].new == 0) return ENOENT; ma->ref_current = ino; ma->ref_iter = 0; return 0; } static errcode_t ima_next_ref(ext2_irel irel, struct ext2_inode_reference *ref) { struct irel_ma *ma; struct inode_reference_entry *ref_ent; ma = irel->priv_data; ref_ent = ma->ref_entries + ma->ref_current; if ((ref_ent->refs == NULL) || (ma->ref_iter >= ref_ent->num)) { ref->block = 0; ref->offset = 0; return 0; } *ref = ref_ent->refs[ma->ref_iter++]; return 0; } static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new) { struct irel_ma *ma; ma = irel->priv_data; if ((old > ma->max_inode) || (new > ma->max_inode)) return EXT2_ET_INVALID_ARGUMENT; if (ma->entries[(unsigned) old].new == 0) return ENOENT; ma->entries[(unsigned) new] = ma->entries[(unsigned) old]; ext2fs_free_mem(&ma->ref_entries[(unsigned) new].refs); ma->ref_entries[(unsigned) new] = ma->ref_entries[(unsigned) old]; ma->entries[(unsigned) old].new = 0; ma->ref_entries[(unsigned) old].num = 0; ma->ref_entries[(unsigned) old].refs = 0; ma->orig_map[ma->entries[new].orig] = new; return 0; } static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old) { struct irel_ma *ma; ma = irel->priv_data; if (old > ma->max_inode) return EXT2_ET_INVALID_ARGUMENT; if (ma->entries[(unsigned) old].new == 0) return ENOENT; ma->entries[old].new = 0; ext2fs_free_mem(&ma->ref_entries[(unsigned) old].refs); ma->orig_map[ma->entries[(unsigned) old].orig] = 0; ma->ref_entries[(unsigned) old].num = 0; ma->ref_entries[(unsigned) old].refs = 0; return 0; } static errcode_t ima_free(ext2_irel irel) { struct irel_ma *ma; ext2_ino_t ino; if (!irel) return 0; ma = irel->priv_data; if (ma) { ext2fs_free_mem(&ma->orig_map); ext2fs_free_mem(&ma->entries); if (ma->ref_entries) { for (ino = 0; ino <= ma->max_inode; ino++) { ext2fs_free_mem(&ma->ref_entries[(unsigned) ino].refs); } ext2fs_free_mem(&ma->ref_entries); } ext2fs_free_mem(&ma); } ext2fs_free_mem(&irel->name); ext2fs_free_mem(&irel); return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c0000644000000000000000000001415212263563520021510 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * getsize.c --- get the size of a partition. * * Copyright (C) 1995, 1995 Theodore Ts'o. * Copyright (C) 2003 VMware, Inc. * * Windows version of ext2fs_get_device_size by Chris Li, VMware. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_LINUX_FD_H #include #endif #ifdef HAVE_SYS_DISKLABEL_H #include #endif #ifdef HAVE_SYS_DISK_H #ifdef HAVE_SYS_QUEUE_H #include /* for LIST_HEAD */ #endif #include #endif #ifdef __linux__ #include #endif #if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) #define BLKGETSIZE _IO(0x12,96) /* return device size */ #endif #if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ #endif #ifdef APPLE_DARWIN #define BLKGETSIZE DKIOCGETBLOCKCOUNT32 #endif /* APPLE_DARWIN */ #include "ext2_fs.h" #include "ext2fs.h" #if defined(__CYGWIN__) || defined(WIN32) #include #include #if (_WIN32_WINNT >= 0x0500) #define HAVE_GET_FILE_SIZE_EX 1 #endif errcode_t ext2fs_get_device_size(const char *file, int blocksize, blk_t *retblocks) { HANDLE dev; PARTITION_INFORMATION pi; DISK_GEOMETRY gi; DWORD retbytes; #ifdef HAVE_GET_FILE_SIZE_EX LARGE_INTEGER filesize; #else DWORD filesize; #endif /* HAVE_GET_FILE_SIZE_EX */ dev = CreateFile(file, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE , NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (dev == INVALID_HANDLE_VALUE) return EBADF; if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, &pi, sizeof(PARTITION_INFORMATION), &pi, sizeof(PARTITION_INFORMATION), &retbytes, NULL)) { *retblocks = pi.PartitionLength.QuadPart / blocksize; } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, &gi, sizeof(DISK_GEOMETRY), &gi, sizeof(DISK_GEOMETRY), &retbytes, NULL)) { *retblocks = gi.BytesPerSector * gi.SectorsPerTrack * gi.TracksPerCylinder * gi.Cylinders.QuadPart / blocksize; #ifdef HAVE_GET_FILE_SIZE_EX } else if (GetFileSizeEx(dev, &filesize)) { *retblocks = filesize.QuadPart / blocksize; } #else } else { filesize = GetFileSize(dev, NULL); if (INVALID_FILE_SIZE != filesize) { *retblocks = filesize / blocksize; } } #endif /* HAVE_GET_FILE_SIZE_EX */ CloseHandle(dev); return 0; } #else static int valid_offset (int fd, ext2_loff_t offset) { char ch; if (ext2fs_llseek (fd, offset, 0) < 0) return 0; if (read (fd, &ch, 1) < 1) return 0; return 1; } /* * Returns the number of blocks in a partition */ errcode_t ext2fs_get_device_size(const char *file, int blocksize, blk_t *retblocks) { int fd; int valid_blkgetsize64 = 1; #ifdef __linux__ struct utsname ut; #endif unsigned long long size64; unsigned long size; ext2_loff_t high, low; #ifdef FDGETPRM struct floppy_struct this_floppy; #endif #ifdef HAVE_SYS_DISKLABEL_H int part; struct disklabel lab; struct partition *pp; char ch; #endif /* HAVE_SYS_DISKLABEL_H */ #ifdef CONFIG_LFS fd = open64(file, O_RDONLY); #else fd = open(file, O_RDONLY); #endif if (fd < 0) return errno; #ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { if ((sizeof(*retblocks) < sizeof(unsigned long long)) && ((size64 / (blocksize / 512)) > 0xFFFFFFFF)) return EFBIG; close(fd); *retblocks = size64 / (blocksize / 512); return 0; } #endif #ifdef BLKGETSIZE64 #ifdef __linux__ uname(&ut); if ((ut.release[0] == '2') && (ut.release[1] == '.') && (ut.release[2] < '6') && (ut.release[3] == '.')) valid_blkgetsize64 = 0; #endif if (valid_blkgetsize64 && ioctl(fd, BLKGETSIZE64, &size64) >= 0) { if ((sizeof(*retblocks) < sizeof(unsigned long long)) && ((size64 / blocksize) > 0xFFFFFFFF)) return EFBIG; close(fd); *retblocks = size64 / blocksize; return 0; } #endif #ifdef BLKGETSIZE if (ioctl(fd, BLKGETSIZE, &size) >= 0) { close(fd); *retblocks = size / (blocksize / 512); return 0; } #endif #ifdef FDGETPRM if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { close(fd); *retblocks = this_floppy.size / (blocksize / 512); return 0; } #endif #ifdef HAVE_SYS_DISKLABEL_H #if defined(DIOCGMEDIASIZE) { off_t ms; u_int bs; if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) { *retblocks = ms / blocksize; return 0; } } #elif defined(DIOCGDINFO) /* old disklabel interface */ part = strlen(file) - 1; if (part >= 0) { ch = file[part]; if (isdigit(ch)) part = 0; else if (ch >= 'a' && ch <= 'h') part = ch - 'a'; else part = -1; } if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { pp = &lab.d_partitions[part]; if (pp->p_size) { close(fd); *retblocks = pp->p_size / (blocksize / 512); return 0; } } #endif /* defined(DIOCG*) */ #endif /* HAVE_SYS_DISKLABEL_H */ /* * OK, we couldn't figure it out by using a specialized ioctl, * which is generally the best way. So do binary search to * find the size of the partition. */ low = 0; for (high = 1024; valid_offset (fd, high); high *= 2) low = high; while (low < high - 1) { const ext2_loff_t mid = (low + high) / 2; if (valid_offset (fd, mid)) low = mid; else high = mid; } valid_offset (fd, 0); close(fd); size64 = low + 1; if ((sizeof(*retblocks) < sizeof(unsigned long long)) && ((size64 / blocksize) > 0xFFFFFFFF)) return EFBIG; *retblocks = size64 / blocksize; return 0; } #endif /* WIN32 */ #ifdef DEBUG int main(int argc, char **argv) { blk_t blocks; int retval; if (argc < 2) { fprintf(stderr, "Usage: %s device\n", argv[0]); exit(1); } retval = ext2fs_get_device_size(argv[1], 1024, &blocks); if (retval) { com_err(argv[0], retval, "while calling ext2fs_get_device_size"); exit(1); } printf("Device %s has %d 1k blocks.\n", argv[1], blocks); exit(0); } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h0000644000000000000000000000303012263563520021446 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * jfs_dat.h --- stripped down header file which only contains the JFS * on-disk data structures */ #define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */ /* * On-disk structures */ /* * Descriptor block types: */ #define JFS_DESCRIPTOR_BLOCK 1 #define JFS_COMMIT_BLOCK 2 #define JFS_SUPERBLOCK 3 /* * Standard header for all descriptor blocks: */ typedef struct journal_header_s { __u32 h_magic; __u32 h_blocktype; __u32 h_sequence; } journal_header_t; /* * The block tag: used to describe a single buffer in the journal */ typedef struct journal_block_tag_s { __u32 t_blocknr; /* The on-disk block number */ __u32 t_flags; /* See below */ } journal_block_tag_t; /* Definitions for the journal tag flags word: */ #define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */ #define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */ #define JFS_FLAG_DELETED 4 /* block deleted by this transaction */ #define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */ /* * The journal superblock */ typedef struct journal_superblock_s { journal_header_t s_header; /* Static information describing the journal */ __u32 s_blocksize; /* journal device blocksize */ __u32 s_maxlen; /* total blocks in journal file */ __u32 s_first; /* first block of log information */ /* Dynamic information describing the current state of the log */ __u32 s_sequence; /* first commit ID expected in log */ __u32 s_start; /* blocknr of start of log */ } journal_superblock_t; busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/kernel-list.h0000644000000000000000000000473312263563520022300 0ustar rootroot/* vi: set sw=4 ts=4: */ #ifndef LINUX_LIST_H #define LINUX_LIST_H 1 /* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = { &name, &name } #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) #if (!defined(__GNUC__) && !defined(__WATCOMC__)) #define __inline__ #endif /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static __inline__ void __list_add(struct list_head * new, struct list_head * prev, struct list_head * next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /* * Insert a new entry after the specified head.. */ static __inline__ void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } /* * Insert a new entry at the tail */ static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static __inline__ void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } static __inline__ void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); } static __inline__ int list_empty(struct list_head *head) { return head->next == head; } /* * Splice in "list" into "head" */ static __inline__ void list_splice(struct list_head *list, struct list_head *head) { struct list_head *first = list->next; if (first != list) { struct list_head *last = list->prev; struct list_head *at = head->next; first->prev = head; head->next = first; last->next = at; at->prev = last; } } #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c0000644000000000000000000001633312263563520022612 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * ext2fs.h --- ext2fs * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include "ext2fs.h" #include "bitops.h" #include /* * Allocate memory */ errcode_t ext2fs_get_mem(unsigned long size, void *ptr) { void **pp = (void **)ptr; *pp = malloc(size); if (!*pp) return EXT2_ET_NO_MEMORY; return 0; } /* * Free memory */ errcode_t ext2fs_free_mem(void *ptr) { void **pp = (void **)ptr; free(*pp); *pp = 0; return 0; } /* * Resize memory */ errcode_t ext2fs_resize_mem(unsigned long EXT2FS_ATTR((unused)) old_size, unsigned long size, void *ptr) { void *p; /* Use "memcpy" for pointer assignments here to avoid problems * with C99 strict type aliasing rules. */ memcpy(&p, ptr, sizeof (p)); p = xrealloc(p, size); memcpy(ptr, &p, sizeof (p)); return 0; } /* * Mark a filesystem superblock as dirty */ void ext2fs_mark_super_dirty(ext2_filsys fs) { fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_CHANGED; } /* * Mark a filesystem as changed */ void ext2fs_mark_changed(ext2_filsys fs) { fs->flags |= EXT2_FLAG_CHANGED; } /* * Check to see if a filesystem has changed */ int ext2fs_test_changed(ext2_filsys fs) { return (fs->flags & EXT2_FLAG_CHANGED); } /* * Mark a filesystem as valid */ void ext2fs_mark_valid(ext2_filsys fs) { fs->flags |= EXT2_FLAG_VALID; } /* * Mark a filesystem as NOT valid */ void ext2fs_unmark_valid(ext2_filsys fs) { fs->flags &= ~EXT2_FLAG_VALID; } /* * Check to see if a filesystem is valid */ int ext2fs_test_valid(ext2_filsys fs) { return (fs->flags & EXT2_FLAG_VALID); } /* * Mark the inode bitmap as dirty */ void ext2fs_mark_ib_dirty(ext2_filsys fs) { fs->flags |= EXT2_FLAG_IB_DIRTY | EXT2_FLAG_CHANGED; } /* * Mark the block bitmap as dirty */ void ext2fs_mark_bb_dirty(ext2_filsys fs) { fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_CHANGED; } /* * Check to see if a filesystem's inode bitmap is dirty */ int ext2fs_test_ib_dirty(ext2_filsys fs) { return (fs->flags & EXT2_FLAG_IB_DIRTY); } /* * Check to see if a filesystem's block bitmap is dirty */ int ext2fs_test_bb_dirty(ext2_filsys fs) { return (fs->flags & EXT2_FLAG_BB_DIRTY); } /* * Return the group # of a block */ int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk) { return (blk - fs->super->s_first_data_block) / fs->super->s_blocks_per_group; } /* * Return the group # of an inode number */ int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino) { return (ino - 1) / fs->super->s_inodes_per_group; } blk_t ext2fs_inode_data_blocks(ext2_filsys fs, struct ext2_inode *inode) { return inode->i_blocks - (inode->i_file_acl ? fs->blocksize >> 9 : 0); } __u16 ext2fs_swab16(__u16 val) { return (val >> 8) | (val << 8); } __u32 ext2fs_swab32(__u32 val) { return ((val>>24) | ((val>>8)&0xFF00) | ((val<<8)&0xFF0000) | (val<<24)); } int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap, blk_t bitno); int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap, blk_t bitno) { if ((bitno < bitmap->start) || (bitno > bitmap->end)) { ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, bitno); return 0; } return ext2fs_test_bit(bitno - bitmap->start, bitmap->bitmap); } int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block) { return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, block); } int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block) { return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, block); } int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block) { return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, block); } int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode) { return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, inode); } int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode) { return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, inode); } int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode) { return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, inode); } void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block) { ext2fs_set_bit(block - bitmap->start, bitmap->bitmap); } void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block) { ext2fs_clear_bit(block - bitmap->start, bitmap->bitmap); } int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block) { return ext2fs_test_bit(block - bitmap->start, bitmap->bitmap); } void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode) { ext2fs_set_bit(inode - bitmap->start, bitmap->bitmap); } void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode) { ext2fs_clear_bit(inode - bitmap->start, bitmap->bitmap); } int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode) { return ext2fs_test_bit(inode - bitmap->start, bitmap->bitmap); } blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap) { return bitmap->start; } ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap) { return bitmap->start; } blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap) { return bitmap->end; } ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap) { return bitmap->end; } int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num) { int i; if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST, block, bitmap->description); return 0; } for (i=0; i < num; i++) { if (ext2fs_fast_test_block_bitmap(bitmap, block+i)) return 0; } return 1; } int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num) { int i; for (i=0; i < num; i++) { if (ext2fs_fast_test_block_bitmap(bitmap, block+i)) return 0; } return 1; } void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num) { int i; if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block, bitmap->description); return; } for (i=0; i < num; i++) ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap); } void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num) { int i; for (i=0; i < num; i++) ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap); } void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num) { int i; if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block, bitmap->description); return; } for (i=0; i < num; i++) ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap); } void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num) { int i; for (i=0; i < num; i++) ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/newdir.c0000644000000000000000000000272412263563520021330 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * newdir.c --- create a new directory block * * Copyright (C) 1994, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #ifndef EXT2_FT_DIR #define EXT2_FT_DIR 2 #endif /* * Create new directory block */ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, ext2_ino_t parent_ino, char **block) { struct ext2_dir_entry *dir = NULL; errcode_t retval; char *buf; int rec_len; int filetype = 0; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; memset(buf, 0, fs->blocksize); dir = (struct ext2_dir_entry *) buf; dir->rec_len = fs->blocksize; if (dir_ino) { if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) filetype = EXT2_FT_DIR << 8; /* * Set up entry for '.' */ dir->inode = dir_ino; dir->name_len = 1 | filetype; dir->name[0] = '.'; rec_len = dir->rec_len - EXT2_DIR_REC_LEN(1); dir->rec_len = EXT2_DIR_REC_LEN(1); /* * Set up entry for '..' */ dir = (struct ext2_dir_entry *) (buf + dir->rec_len); dir->rec_len = rec_len; dir->inode = parent_ino; dir->name_len = 2 | filetype; dir->name[0] = '.'; dir->name[1] = '.'; } *block = buf; return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c0000644000000000000000000001224012263563520022325 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * dir_iterate.c --- ext2fs directory iteration operations * * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include "ext2_fs.h" #include "ext2fsP.h" /* * This function checks to see whether or not a potential deleted * directory entry looks valid. What we do is check the deleted entry * and each successive entry to make sure that they all look valid and * that the last deleted entry ends at the beginning of the next * undeleted entry. Returns 1 if the deleted entry looks valid, zero * if not valid. */ static int ext2fs_validate_entry(char *buf, int offset, int final_offset) { struct ext2_dir_entry *dirent; while (offset < final_offset) { dirent = (struct ext2_dir_entry *)(buf + offset); offset += dirent->rec_len; if ((dirent->rec_len < 8) || ((dirent->rec_len % 4) != 0) || (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) return 0; } return (offset == final_offset); } errcode_t ext2fs_dir_iterate2(ext2_filsys fs, ext2_ino_t dir, int flags, char *block_buf, int (*func)(ext2_ino_t dir, int entry, struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data), void *priv_data) { struct dir_context ctx; errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_check_directory(fs, dir); if (retval) return retval; ctx.dir = dir; ctx.flags = flags; if (block_buf) ctx.buf = block_buf; else { retval = ext2fs_get_mem(fs->blocksize, &ctx.buf); if (retval) return retval; } ctx.func = func; ctx.priv_data = priv_data; ctx.errcode = 0; retval = ext2fs_block_iterate2(fs, dir, 0, 0, ext2fs_process_dir_block, &ctx); if (!block_buf) ext2fs_free_mem(&ctx.buf); if (retval) return retval; return ctx.errcode; } struct xlate { int (*func)(struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data); void *real_private; }; static int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)), int entry EXT2FS_ATTR((unused)), struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data) { struct xlate *xl = (struct xlate *) priv_data; return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private); } extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, ext2_ino_t dir, int flags, char *block_buf, int (*func)(struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data), void *priv_data) { struct xlate xl; xl.real_private = priv_data; xl.func = func; return ext2fs_dir_iterate2(fs, dir, flags, block_buf, xlate_func, &xl); } /* * Helper function which is private to this module. Used by * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate() */ int ext2fs_process_dir_block(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct dir_context *ctx = (struct dir_context *) priv_data; unsigned int offset = 0; unsigned int next_real_entry = 0; int ret = 0; int changed = 0; int do_abort = 0; int entry, size; struct ext2_dir_entry *dirent; if (blockcnt < 0) return 0; entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE; ctx->errcode = ext2fs_read_dir_block(fs, *blocknr, ctx->buf); if (ctx->errcode) return BLOCK_ABORT; while (offset < fs->blocksize) { dirent = (struct ext2_dir_entry *) (ctx->buf + offset); if (((offset + dirent->rec_len) > fs->blocksize) || (dirent->rec_len < 8) || ((dirent->rec_len % 4) != 0) || (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) { ctx->errcode = EXT2_ET_DIR_CORRUPTED; return BLOCK_ABORT; } if (!dirent->inode && !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) goto next; ret = (ctx->func)(ctx->dir, (next_real_entry > offset) ? DIRENT_DELETED_FILE : entry, dirent, offset, fs->blocksize, ctx->buf, ctx->priv_data); if (entry < DIRENT_OTHER_FILE) entry++; if (ret & DIRENT_CHANGED) changed++; if (ret & DIRENT_ABORT) { do_abort++; break; } next: if (next_real_entry == offset) next_real_entry += dirent->rec_len; if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) { size = ((dirent->name_len & 0xFF) + 11) & ~3; if (dirent->rec_len != size) { unsigned int final_offset; final_offset = offset + dirent->rec_len; offset += size; while (offset < final_offset && !ext2fs_validate_entry(ctx->buf, offset, final_offset)) offset += 4; continue; } } offset += dirent->rec_len; } if (changed) { ctx->errcode = ext2fs_write_dir_block(fs, *blocknr, ctx->buf); if (ctx->errcode) return BLOCK_ABORT; } if (do_abort) return BLOCK_ABORT; return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c0000644000000000000000000002311112263563520022033 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * mkjournal.c --- make a journal for a filesystem * * Copyright (C) 2000 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_IOCTL_H #include #endif #if HAVE_NETINET_IN_H #include #endif #include "ext2_fs.h" #include "../e2p/e2p.h" #include "../e2fsck.h" #include "ext2fs.h" #include "kernel-jbd.h" /* * This function automatically sets up the journal superblock and * returns it as an allocated block. */ errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, __u32 size, int flags, char **ret_jsb) { errcode_t retval; journal_superblock_t *jsb; if (size < 1024) return EXT2_ET_JOURNAL_TOO_SMALL; if ((retval = ext2fs_get_mem(fs->blocksize, &jsb))) return retval; memset (jsb, 0, fs->blocksize); jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER); if (flags & EXT2_MKJOURNAL_V1_SUPER) jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V1); else jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2); jsb->s_blocksize = htonl(fs->blocksize); jsb->s_maxlen = htonl(size); jsb->s_nr_users = htonl(1); jsb->s_first = htonl(1); jsb->s_sequence = htonl(1); memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid)); /* * If we're creating an external journal device, we need to * adjust these fields. */ if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { jsb->s_nr_users = 0; if (fs->blocksize == 1024) jsb->s_first = htonl(3); else jsb->s_first = htonl(2); } *ret_jsb = (char *) jsb; return 0; } /* * This function writes a journal using POSIX routines. It is used * for creating external journals and creating journals on live * filesystems. */ static errcode_t write_journal_file(ext2_filsys fs, char *filename, blk_t size, int flags) { errcode_t retval; char *buf = NULL; int fd, ret_size; blk_t i; if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) return retval; /* Open the device or journal file */ if ((fd = open(filename, O_WRONLY)) < 0) { retval = errno; goto errout; } /* Write the superblock out */ retval = EXT2_ET_SHORT_WRITE; ret_size = write(fd, buf, fs->blocksize); if (ret_size < 0) { retval = errno; goto errout; } if (ret_size != (int) fs->blocksize) goto errout; memset(buf, 0, fs->blocksize); for (i = 1; i < size; i++) { ret_size = write(fd, buf, fs->blocksize); if (ret_size < 0) { retval = errno; goto errout; } if (ret_size != (int) fs->blocksize) goto errout; } close(fd); retval = 0; errout: ext2fs_free_mem(&buf); return retval; } /* * Helper function for creating the journal using direct I/O routines */ struct mkjournal_struct { int num_blocks; int newblocks; char *buf; errcode_t err; }; static int mkjournal_proc(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct mkjournal_struct *es = (struct mkjournal_struct *) priv_data; blk_t new_blk; static blk_t last_blk = 0; errcode_t retval; if (*blocknr) { last_blk = *blocknr; return 0; } retval = ext2fs_new_block(fs, last_blk, 0, &new_blk); if (retval) { es->err = retval; return BLOCK_ABORT; } if (blockcnt > 0) es->num_blocks--; es->newblocks++; retval = io_channel_write_blk(fs->io, new_blk, 1, es->buf); if (blockcnt == 0) memset(es->buf, 0, fs->blocksize); if (retval) { es->err = retval; return BLOCK_ABORT; } *blocknr = new_blk; last_blk = new_blk; ext2fs_block_alloc_stats(fs, new_blk, +1); if (es->num_blocks == 0) return (BLOCK_CHANGED | BLOCK_ABORT); else return BLOCK_CHANGED; } /* * This function creates a journal using direct I/O routines. */ static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino, blk_t size, int flags) { char *buf; errcode_t retval; struct ext2_inode inode; struct mkjournal_struct es; if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) return retval; if ((retval = ext2fs_read_bitmaps(fs))) return retval; if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) return retval; if (inode.i_blocks > 0) return EEXIST; es.num_blocks = size; es.newblocks = 0; es.buf = buf; es.err = 0; retval = ext2fs_block_iterate2(fs, journal_ino, BLOCK_FLAG_APPEND, 0, mkjournal_proc, &es); if (es.err) { retval = es.err; goto errout; } if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) goto errout; inode.i_size += fs->blocksize * size; inode.i_blocks += (fs->blocksize / 512) * es.newblocks; inode.i_mtime = inode.i_ctime = time(NULL); inode.i_links_count = 1; inode.i_mode = LINUX_S_IFREG | 0600; if ((retval = ext2fs_write_inode(fs, journal_ino, &inode))) goto errout; retval = 0; memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4); fs->super->s_jnl_blocks[16] = inode.i_size; fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; ext2fs_mark_super_dirty(fs); errout: ext2fs_free_mem(&buf); return retval; } /* * This function adds a journal device to a filesystem */ errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev) { struct stat st; errcode_t retval; char buf[1024]; journal_superblock_t *jsb; int start; __u32 i, nr_users; /* Make sure the device exists and is a block device */ if (stat(journal_dev->device_name, &st) < 0) return errno; if (!S_ISBLK(st.st_mode)) return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */ /* Get the journal superblock */ start = 1; if (journal_dev->blocksize == 1024) start++; if ((retval = io_channel_read_blk(journal_dev->io, start, -1024, buf))) return retval; jsb = (journal_superblock_t *) buf; if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) return EXT2_ET_NO_JOURNAL_SB; if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize) return EXT2_ET_UNEXPECTED_BLOCK_SIZE; /* Check and see if this filesystem has already been added */ nr_users = ntohl(jsb->s_nr_users); for (i=0; i < nr_users; i++) { if (memcmp(fs->super->s_uuid, &jsb->s_users[i*16], 16) == 0) break; } if (i >= nr_users) { memcpy(&jsb->s_users[nr_users*16], fs->super->s_uuid, 16); jsb->s_nr_users = htonl(nr_users+1); } /* Writeback the journal superblock */ if ((retval = io_channel_write_blk(journal_dev->io, start, -1024, buf))) return retval; fs->super->s_journal_inum = 0; fs->super->s_journal_dev = st.st_rdev; memcpy(fs->super->s_journal_uuid, jsb->s_uuid, sizeof(fs->super->s_journal_uuid)); fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; ext2fs_mark_super_dirty(fs); return 0; } /* * This function adds a journal inode to a filesystem, using either * POSIX routines if the filesystem is mounted, or using direct I/O * functions if it is not. */ errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags) { errcode_t retval; ext2_ino_t journal_ino; struct stat st; char jfile[1024]; int fd, mount_flags, f; retval = ext2fs_check_mount_point(fs->device_name, &mount_flags, jfile, sizeof(jfile)-10); if (retval) return retval; if (mount_flags & EXT2_MF_MOUNTED) { strcat(jfile, "/.journal"); /* * If .../.journal already exists, make sure any * immutable or append-only flags are cleared. */ #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) (void) chflags (jfile, 0); #else #if HAVE_EXT2_IOCTLS fd = open(jfile, O_RDONLY); if (fd >= 0) { f = 0; ioctl(fd, EXT2_IOC_SETFLAGS, &f); close(fd); } #endif #endif /* Create the journal file */ if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0) return errno; if ((retval = write_journal_file(fs, jfile, size, flags))) goto errout; /* Get inode number of the journal file */ if (fstat(fd, &st) < 0) goto errout; #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE); #else #if HAVE_EXT2_IOCTLS f = EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL; retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f); #endif #endif if (retval) goto errout; close(fd); journal_ino = st.st_ino; } else { journal_ino = EXT2_JOURNAL_INO; if ((retval = write_journal_inode(fs, journal_ino, size, flags))) return retval; } fs->super->s_journal_inum = journal_ino; fs->super->s_journal_dev = 0; memset(fs->super->s_journal_uuid, 0, sizeof(fs->super->s_journal_uuid)); fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; ext2fs_mark_super_dirty(fs); return 0; errout: close(fd); return retval; } #ifdef DEBUG main(int argc, char **argv) { errcode_t retval; char *device_name; ext2_filsys fs; if (argc < 2) { fprintf(stderr, "Usage: %s filesystem\n", argv[0]); exit(1); } device_name = argv[1]; retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0, unix_io_manager, &fs); if (retval) { com_err(argv[0], retval, "while opening %s", device_name); exit(1); } retval = ext2fs_add_journal_inode(fs, 1024); if (retval) { com_err(argv[0], retval, "while adding journal to %s", device_name); exit(1); } retval = ext2fs_flush(fs); if (retval) { printf("Warning, had trouble writing out superblocks.\n"); } ext2fs_close(fs); exit(0); } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c0000644000000000000000000000333412263563520022334 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * cmp_bitmaps.c --- routines to compare inode and block bitmaps. * * Copyright (C) 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1, ext2fs_block_bitmap bm2) { blk_t i; EXT2_CHECK_MAGIC(bm1, EXT2_ET_MAGIC_BLOCK_BITMAP); EXT2_CHECK_MAGIC(bm2, EXT2_ET_MAGIC_BLOCK_BITMAP); if ((bm1->start != bm2->start) || (bm1->end != bm2->end) || (memcmp(bm1->bitmap, bm2->bitmap, (size_t) (bm1->end - bm1->start)/8))) return EXT2_ET_NEQ_BLOCK_BITMAP; for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++) if (ext2fs_fast_test_block_bitmap(bm1, i) != ext2fs_fast_test_block_bitmap(bm2, i)) return EXT2_ET_NEQ_BLOCK_BITMAP; return 0; } errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1, ext2fs_inode_bitmap bm2) { ext2_ino_t i; EXT2_CHECK_MAGIC(bm1, EXT2_ET_MAGIC_INODE_BITMAP); EXT2_CHECK_MAGIC(bm2, EXT2_ET_MAGIC_INODE_BITMAP); if ((bm1->start != bm2->start) || (bm1->end != bm2->end) || (memcmp(bm1->bitmap, bm2->bitmap, (size_t) (bm1->end - bm1->start)/8))) return EXT2_ET_NEQ_INODE_BITMAP; for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++) if (ext2fs_fast_test_inode_bitmap(bm1, i) != ext2fs_fast_test_inode_bitmap(bm2, i)) return EXT2_ET_NEQ_INODE_BITMAP; return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/finddev.c0000644000000000000000000001005712263563520021455 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * finddev.c -- this routine attempts to find a particular device in * /dev * * Copyright (C) 2000 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #include #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_SYS_MKDEV_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct dir_list { char *name; struct dir_list *next; }; /* * This function adds an entry to the directory list */ static void add_to_dirlist(const char *name, struct dir_list **list) { struct dir_list *dp; dp = xmalloc(sizeof(struct dir_list)); dp->name = xmalloc(strlen(name)+1); strcpy(dp->name, name); dp->next = *list; *list = dp; } /* * This function frees a directory list */ static void free_dirlist(struct dir_list **list) { struct dir_list *dp, *next; for (dp = *list; dp; dp = next) { next = dp->next; free(dp->name); free(dp); } *list = 0; } static int scan_dir(char *dir_name, dev_t device, struct dir_list **list, char **ret_path) { DIR *dir; struct dirent *dp; char path[1024], *cp; int dirlen; struct stat st; dirlen = strlen(dir_name); if ((dir = opendir(dir_name)) == NULL) return errno; dp = readdir(dir); while (dp) { if (dirlen + strlen(dp->d_name) + 2 >= sizeof(path)) goto skip_to_next; if (dp->d_name[0] == '.' && ((dp->d_name[1] == 0) || ((dp->d_name[1] == '.') && (dp->d_name[2] == 0)))) goto skip_to_next; sprintf(path, "%s/%s", dir_name, dp->d_name); if (stat(path, &st) < 0) goto skip_to_next; if (S_ISDIR(st.st_mode)) add_to_dirlist(path, list); if (S_ISBLK(st.st_mode) && st.st_rdev == device) { cp = xmalloc(strlen(path)+1); strcpy(cp, path); *ret_path = cp; goto success; } skip_to_next: dp = readdir(dir); } success: closedir(dir); return 0; } /* * This function finds the pathname to a block device with a given * device number. It returns a pointer to allocated memory to the * pathname on success, and NULL on failure. */ char *ext2fs_find_block_device(dev_t device) { struct dir_list *list = NULL, *new_list = NULL; struct dir_list *current; char *ret_path = NULL; /* * Add the starting directories to search... */ add_to_dirlist("/devices", &list); add_to_dirlist("/devfs", &list); add_to_dirlist("/dev", &list); while (list) { current = list; list = list->next; #ifdef DEBUG printf("Scanning directory %s\n", current->name); #endif scan_dir(current->name, device, &new_list, &ret_path); free(current->name); free(current); if (ret_path) break; /* * If we're done checking at this level, descend to * the next level of subdirectories. (breadth-first) */ if (list == 0) { list = new_list; new_list = 0; } } free_dirlist(&list); free_dirlist(&new_list); return ret_path; } #ifdef DEBUG int main(int argc, char** argv) { char *devname, *tmp; int major, minor; dev_t device; const char *errmsg = "Cannot parse %s: %s\n"; if ((argc != 2) && (argc != 3)) { fprintf(stderr, "Usage: %s device_number\n", argv[0]); fprintf(stderr, "\t: %s major minor\n", argv[0]); exit(1); } if (argc == 2) { device = strtoul(argv[1], &tmp, 0); if (*tmp) { fprintf(stderr, errmsg, "device number", argv[1]); exit(1); } } else { major = strtoul(argv[1], &tmp, 0); if (*tmp) { fprintf(stderr, errmsg, "major number", argv[1]); exit(1); } minor = strtoul(argv[2], &tmp, 0); if (*tmp) { fprintf(stderr, errmsg, "minor number", argv[2]); exit(1); } device = makedev(major, minor); printf("Looking for device 0x%04x (%d:%d)\n", device, major, minor); } devname = ext2fs_find_block_device(device); if (devname) { printf("Found device! %s\n", devname); free(devname); } else { printf("Cannot find device.\n"); } return 0; } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h0000644000000000000000000004340312263563520021416 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * linux/include/linux/ext2_fs.h * * Copyright (C) 1992, 1993, 1994, 1995 * Remy Card (card@masi.ibp.fr) * Laboratoire MASI - Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * from * * linux/include/linux/minix_fs.h * * Copyright (C) 1991, 1992 Linus Torvalds */ #ifndef LINUX_EXT2_FS_H #define LINUX_EXT2_FS_H 1 #include "ext2_types.h" /* Changed from linux/types.h */ /* * Special inode numbers */ #define EXT2_BAD_INO 1 /* Bad blocks inode */ #define EXT2_ROOT_INO 2 /* Root inode */ #define EXT2_ACL_IDX_INO 3 /* ACL inode */ #define EXT2_ACL_DATA_INO 4 /* ACL inode */ #define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ #define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ #define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */ #define EXT2_JOURNAL_INO 8 /* Journal inode */ /* First non-reserved inode for old ext2 filesystems */ #define EXT2_GOOD_OLD_FIRST_INO 11 /* * The second extended file system magic number */ #define EXT2_SUPER_MAGIC 0xEF53 /* Assume that user mode programs are passing in an ext2fs superblock, not * a kernel struct super_block. This will allow us to call the feature-test * macros from user land. */ #define EXT2_SB(sb) (sb) /* * Maximal count of links to a file */ #define EXT2_LINK_MAX 32000 /* * Macro-instructions used to manage several block sizes */ #define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */ #define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */ #define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE) #define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE) #define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) #define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) #define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size) #define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino) #define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(__u32)) /* * Macro-instructions used to manage fragments */ #define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE #define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE #define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE # define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size) # define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s)) /* * ACL structures */ struct ext2_acl_header /* Header of Access Control Lists */ { __u32 aclh_size; __u32 aclh_file_count; __u32 aclh_acle_count; __u32 aclh_first_acle; }; struct ext2_acl_entry /* Access Control List Entry */ { __u32 acle_size; __u16 acle_perms; /* Access permissions */ __u16 acle_type; /* Type of entry */ __u16 acle_tag; /* User or group identity */ __u16 acle_pad1; __u32 acle_next; /* Pointer on next entry for the */ /* same inode or on next free entry */ }; /* * Structure of a blocks group descriptor */ struct ext2_group_desc { __u32 bg_block_bitmap; /* Blocks bitmap block */ __u32 bg_inode_bitmap; /* Inodes bitmap block */ __u32 bg_inode_table; /* Inodes table block */ __u16 bg_free_blocks_count; /* Free blocks count */ __u16 bg_free_inodes_count; /* Free inodes count */ __u16 bg_used_dirs_count; /* Directories count */ __u16 bg_pad; __u32 bg_reserved[3]; }; /* * Data structures used by the directory indexing feature * * Note: all of the multibyte integer fields are little endian. */ /* * Note: dx_root_info is laid out so that if it should somehow get * overlaid by a dirent the two low bits of the hash version will be * zero. Therefore, the hash version mod 4 should never be 0. * Sincerely, the paranoia department. */ struct ext2_dx_root_info { __u32 reserved_zero; __u8 hash_version; /* 0 now, 1 at release */ __u8 info_length; /* 8 */ __u8 indirect_levels; __u8 unused_flags; }; #define EXT2_HASH_LEGACY 0 #define EXT2_HASH_HALF_MD4 1 #define EXT2_HASH_TEA 2 #define EXT2_HASH_FLAG_INCOMPAT 0x1 struct ext2_dx_entry { __u32 hash; __u32 block; }; struct ext2_dx_countlimit { __u16 limit; __u16 count; }; /* * Macro-instructions used to manage group descriptors */ #define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group) #define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group) #define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s)) /* limits imposed by 16-bit value gd_free_{blocks,inode}_count */ #define EXT2_MAX_BLOCKS_PER_GROUP(s) ((1 << 16) - 8) #define EXT2_MAX_INODES_PER_GROUP(s) ((1 << 16) - EXT2_INODES_PER_BLOCK(s)) #define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) /* * Constants relative to the data blocks */ #define EXT2_NDIR_BLOCKS 12 #define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS #define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) #define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) #define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) /* * Inode flags */ #define EXT2_SECRM_FL 0x00000001 /* Secure deletion */ #define EXT2_UNRM_FL 0x00000002 /* Undelete */ #define EXT2_COMPR_FL 0x00000004 /* Compress file */ #define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */ #define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ #define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ #define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ #define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ /* Reserved for compression usage... */ #define EXT2_DIRTY_FL 0x00000100 #define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ #define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */ #define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ /* End compression flags --- maybe not all used */ #define EXT2_BTREE_FL 0x00001000 /* btree format dir */ #define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */ #define EXT2_IMAGIC_FL 0x00002000 #define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */ #define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */ #define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */ #define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ #define EXT3_EXTENTS_FL 0x00080000 /* Inode uses extents */ #define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ #define EXT2_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */ #define EXT2_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */ /* * ioctl commands */ #define EXT2_IOC_GETFLAGS _IOR('f', 1, long) #define EXT2_IOC_SETFLAGS _IOW('f', 2, long) #define EXT2_IOC_GETVERSION _IOR('v', 1, long) #define EXT2_IOC_SETVERSION _IOW('v', 2, long) /* * Structure of an inode on the disk */ struct ext2_inode { __u16 i_mode; /* File mode */ __u16 i_uid; /* Low 16 bits of Owner Uid */ __u32 i_size; /* Size in bytes */ __u32 i_atime; /* Access time */ __u32 i_ctime; /* Creation time */ __u32 i_mtime; /* Modification time */ __u32 i_dtime; /* Deletion Time */ __u16 i_gid; /* Low 16 bits of Group Id */ __u16 i_links_count; /* Links count */ __u32 i_blocks; /* Blocks count */ __u32 i_flags; /* File flags */ union { struct { __u32 l_i_reserved1; } linux1; struct { __u32 h_i_translator; } hurd1; struct { __u32 m_i_reserved1; } masix1; } osd1; /* OS dependent 1 */ __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ __u32 i_generation; /* File version (for NFS) */ __u32 i_file_acl; /* File ACL */ __u32 i_dir_acl; /* Directory ACL */ __u32 i_faddr; /* Fragment address */ union { struct { __u8 l_i_frag; /* Fragment number */ __u8 l_i_fsize; /* Fragment size */ __u16 i_pad1; __u16 l_i_uid_high; /* these 2 fields */ __u16 l_i_gid_high; /* were reserved2[0] */ __u32 l_i_reserved2; } linux2; struct { __u8 h_i_frag; /* Fragment number */ __u8 h_i_fsize; /* Fragment size */ __u16 h_i_mode_high; __u16 h_i_uid_high; __u16 h_i_gid_high; __u32 h_i_author; } hurd2; struct { __u8 m_i_frag; /* Fragment number */ __u8 m_i_fsize; /* Fragment size */ __u16 m_pad1; __u32 m_i_reserved2[2]; } masix2; } osd2; /* OS dependent 2 */ }; /* * Permanent part of an large inode on the disk */ struct ext2_inode_large { __u16 i_mode; /* File mode */ __u16 i_uid; /* Low 16 bits of Owner Uid */ __u32 i_size; /* Size in bytes */ __u32 i_atime; /* Access time */ __u32 i_ctime; /* Creation time */ __u32 i_mtime; /* Modification time */ __u32 i_dtime; /* Deletion Time */ __u16 i_gid; /* Low 16 bits of Group Id */ __u16 i_links_count; /* Links count */ __u32 i_blocks; /* Blocks count */ __u32 i_flags; /* File flags */ union { struct { __u32 l_i_reserved1; } linux1; struct { __u32 h_i_translator; } hurd1; struct { __u32 m_i_reserved1; } masix1; } osd1; /* OS dependent 1 */ __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ __u32 i_generation; /* File version (for NFS) */ __u32 i_file_acl; /* File ACL */ __u32 i_dir_acl; /* Directory ACL */ __u32 i_faddr; /* Fragment address */ union { struct { __u8 l_i_frag; /* Fragment number */ __u8 l_i_fsize; /* Fragment size */ __u16 i_pad1; __u16 l_i_uid_high; /* these 2 fields */ __u16 l_i_gid_high; /* were reserved2[0] */ __u32 l_i_reserved2; } linux2; struct { __u8 h_i_frag; /* Fragment number */ __u8 h_i_fsize; /* Fragment size */ __u16 h_i_mode_high; __u16 h_i_uid_high; __u16 h_i_gid_high; __u32 h_i_author; } hurd2; struct { __u8 m_i_frag; /* Fragment number */ __u8 m_i_fsize; /* Fragment size */ __u16 m_pad1; __u32 m_i_reserved2[2]; } masix2; } osd2; /* OS dependent 2 */ __u16 i_extra_isize; __u16 i_pad1; }; #define i_size_high i_dir_acl /* * File system states */ #define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ #define EXT2_ERROR_FS 0x0002 /* Errors detected */ /* * Mount flags */ #define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */ #define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ #define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ #define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ #define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ #define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ #define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ #define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ #define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt #define set_opt(o, opt) o |= EXT2_MOUNT_##opt #define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \ EXT2_MOUNT_##opt) /* * Maximal mount counts between two filesystem checks */ #define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */ #define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */ /* * Behaviour when detecting errors */ #define EXT2_ERRORS_CONTINUE 1 /* Continue execution */ #define EXT2_ERRORS_RO 2 /* Remount fs read-only */ #define EXT2_ERRORS_PANIC 3 /* Panic */ #define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE /* * Structure of the super block */ struct ext2_super_block { __u32 s_inodes_count; /* Inodes count */ __u32 s_blocks_count; /* Blocks count */ __u32 s_r_blocks_count; /* Reserved blocks count */ __u32 s_free_blocks_count; /* Free blocks count */ __u32 s_free_inodes_count; /* Free inodes count */ __u32 s_first_data_block; /* First Data Block */ __u32 s_log_block_size; /* Block size */ __s32 s_log_frag_size; /* Fragment size */ __u32 s_blocks_per_group; /* # Blocks per group */ __u32 s_frags_per_group; /* # Fragments per group */ __u32 s_inodes_per_group; /* # Inodes per group */ __u32 s_mtime; /* Mount time */ __u32 s_wtime; /* Write time */ __u16 s_mnt_count; /* Mount count */ __s16 s_max_mnt_count; /* Maximal mount count */ __u16 s_magic; /* Magic signature */ __u16 s_state; /* File system state */ __u16 s_errors; /* Behaviour when detecting errors */ __u16 s_minor_rev_level; /* minor revision level */ __u32 s_lastcheck; /* time of last check */ __u32 s_checkinterval; /* max. time between checks */ __u32 s_creator_os; /* OS */ __u32 s_rev_level; /* Revision level */ __u16 s_def_resuid; /* Default uid for reserved blocks */ __u16 s_def_resgid; /* Default gid for reserved blocks */ /* * These fields are for EXT2_DYNAMIC_REV superblocks only. * * Note: the difference between the compatible feature set and * the incompatible feature set is that if there is a bit set * in the incompatible feature set that the kernel doesn't * know about, it should refuse to mount the filesystem. * * e2fsck's requirements are more strict; if it doesn't know * about a feature in either the compatible or incompatible * feature set, it must abort and not try to meddle with * things it doesn't understand... */ __u32 s_first_ino; /* First non-reserved inode */ __u16 s_inode_size; /* size of inode structure */ __u16 s_block_group_nr; /* block group # of this superblock */ __u32 s_feature_compat; /* compatible feature set */ __u32 s_feature_incompat; /* incompatible feature set */ __u32 s_feature_ro_compat; /* readonly-compatible feature set */ __u8 s_uuid[16]; /* 128-bit uuid for volume */ char s_volume_name[16]; /* volume name */ char s_last_mounted[64]; /* directory where last mounted */ __u32 s_algorithm_usage_bitmap; /* For compression */ /* * Performance hints. Directory preallocation should only * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on. */ __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ __u16 s_reserved_gdt_blocks; /* Per group table for online growth */ /* * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set. */ __u8 s_journal_uuid[16]; /* uuid of journal superblock */ __u32 s_journal_inum; /* inode number of journal file */ __u32 s_journal_dev; /* device number of journal file */ __u32 s_last_orphan; /* start of list of inodes to delete */ __u32 s_hash_seed[4]; /* HTREE hash seed */ __u8 s_def_hash_version; /* Default hash version to use */ __u8 s_jnl_backup_type; /* Default type of journal backup */ __u16 s_reserved_word_pad; __u32 s_default_mount_opts; __u32 s_first_meta_bg; /* First metablock group */ __u32 s_mkfs_time; /* When the filesystem was created */ __u32 s_jnl_blocks[17]; /* Backup of the journal inode */ __u32 s_reserved[172]; /* Padding to the end of the block */ }; /* * Codes for operating systems */ #define EXT2_OS_LINUX 0 #define EXT2_OS_HURD 1 #define EXT2_OS_MASIX 2 #define EXT2_OS_FREEBSD 3 #define EXT2_OS_LITES 4 /* * Revision levels */ #define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ #define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ #define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV #define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV #define EXT2_GOOD_OLD_INODE_SIZE 128 /* * Journal inode backup types */ #define EXT3_JNL_BACKUP_BLOCKS 1 /* * Feature set definitions */ #define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ ( EXT2_SB(sb)->s_feature_compat & (mask) ) #define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ ( EXT2_SB(sb)->s_feature_ro_compat & (mask) ) #define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ ( EXT2_SB(sb)->s_feature_incompat & (mask) ) #define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 #define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 #define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 #define EXT2_FEATURE_COMPAT_RESIZE_INO 0x0010 #define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 /* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */ #define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 #define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 #define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */ #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */ #define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 #define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040 #define EXT2_FEATURE_COMPAT_SUPP 0 #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE) #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ EXT2_FEATURE_RO_COMPAT_BTREE_DIR) /* * Default values for user and/or group using reserved blocks */ #define EXT2_DEF_RESUID 0 #define EXT2_DEF_RESGID 0 /* * Default mount options */ #define EXT2_DEFM_DEBUG 0x0001 #define EXT2_DEFM_BSDGROUPS 0x0002 #define EXT2_DEFM_XATTR_USER 0x0004 #define EXT2_DEFM_ACL 0x0008 #define EXT2_DEFM_UID16 0x0010 #define EXT3_DEFM_JMODE 0x0060 #define EXT3_DEFM_JMODE_DATA 0x0020 #define EXT3_DEFM_JMODE_ORDERED 0x0040 #define EXT3_DEFM_JMODE_WBACK 0x0060 /* * Structure of a directory entry */ #define EXT2_NAME_LEN 255 struct ext2_dir_entry { __u32 inode; /* Inode number */ __u16 rec_len; /* Directory entry length */ __u16 name_len; /* Name length */ char name[EXT2_NAME_LEN]; /* File name */ }; /* * The new version of the directory entry. Since EXT2 structures are * stored in intel byte order, and the name_len field could never be * bigger than 255 chars, it's safe to reclaim the extra byte for the * file_type field. */ struct ext2_dir_entry_2 { __u32 inode; /* Inode number */ __u16 rec_len; /* Directory entry length */ __u8 name_len; /* Name length */ __u8 file_type; char name[EXT2_NAME_LEN]; /* File name */ }; /* * Ext2 directory file types. Only the low 3 bits are used. The * other bits are reserved for now. */ #define EXT2_FT_UNKNOWN 0 #define EXT2_FT_REG_FILE 1 #define EXT2_FT_DIR 2 #define EXT2_FT_CHRDEV 3 #define EXT2_FT_BLKDEV 4 #define EXT2_FT_FIFO 5 #define EXT2_FT_SOCK 6 #define EXT2_FT_SYMLINK 7 #define EXT2_FT_MAX 8 /* * EXT2_DIR_PAD defines the directory entries boundaries * * NOTE: It must be a multiple of 4 */ #define EXT2_DIR_PAD 4 #define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) #define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ ~EXT2_DIR_ROUND) #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/version.c0000644000000000000000000000173312263563520021524 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * version.c --- Return the version of the ext2 library * * Copyright (C) 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #if HAVE_UNISTD_H #include #endif #include #include #include #include "ext2_fs.h" #include "ext2fs.h" static const char *lib_version = E2FSPROGS_VERSION; static const char *lib_date = E2FSPROGS_DATE; int ext2fs_parse_version_string(const char *ver_string) { const char *cp; int version = 0; for (cp = ver_string; *cp; cp++) { if (*cp == '.') continue; if (!isdigit(*cp)) break; version = (version * 10) + (*cp - '0'); } return version; } int ext2fs_get_library_version(const char **ver_string, const char **date_string) { if (ver_string) *ver_string = lib_version; if (date_string) *date_string = lib_date; return ext2fs_parse_version_string(lib_version); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/icount.c0000644000000000000000000002601112263563520021334 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * icount.c --- an efficient inode count abstraction * * Copyright (C) 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #if HAVE_UNISTD_H #include #endif #include #include #include "ext2_fs.h" #include "ext2fs.h" /* * The data storage strategy used by icount relies on the observation * that most inode counts are either zero (for non-allocated inodes), * one (for most files), and only a few that are two or more * (directories and files that are linked to more than one directory). * * Also, e2fsck tends to load the icount data sequentially. * * So, we use an inode bitmap to indicate which inodes have a count of * one, and then use a sorted list to store the counts for inodes * which are greater than one. * * We also use an optional bitmap to indicate which inodes are already * in the sorted list, to speed up the use of this abstraction by * e2fsck's pass 2. Pass 2 increments inode counts as it finds them, * so this extra bitmap avoids searching the sorted list to see if a * particular inode is on the sorted list already. */ struct ext2_icount_el { ext2_ino_t ino; __u16 count; }; struct ext2_icount { errcode_t magic; ext2fs_inode_bitmap single; ext2fs_inode_bitmap multiple; ext2_ino_t count; ext2_ino_t size; ext2_ino_t num_inodes; ext2_ino_t cursor; struct ext2_icount_el *list; }; void ext2fs_free_icount(ext2_icount_t icount) { if (!icount) return; icount->magic = 0; ext2fs_free_mem(&icount->list); ext2fs_free_inode_bitmap(icount->single); ext2fs_free_inode_bitmap(icount->multiple); ext2fs_free_mem(&icount); } errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size, ext2_icount_t hint, ext2_icount_t *ret) { ext2_icount_t icount; errcode_t retval; size_t bytes; ext2_ino_t i; if (hint) { EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT); if (hint->size > size) size = (size_t) hint->size; } retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount); if (retval) return retval; memset(icount, 0, sizeof(struct ext2_icount)); retval = ext2fs_allocate_inode_bitmap(fs, 0, &icount->single); if (retval) goto errout; if (flags & EXT2_ICOUNT_OPT_INCREMENT) { retval = ext2fs_allocate_inode_bitmap(fs, 0, &icount->multiple); if (retval) goto errout; } else icount->multiple = 0; if (size) { icount->size = size; } else { /* * Figure out how many special case inode counts we will * have. We know we will need one for each directory; * we also need to reserve some extra room for file links */ retval = ext2fs_get_num_dirs(fs, &icount->size); if (retval) goto errout; icount->size += fs->super->s_inodes_count / 50; } bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el)); retval = ext2fs_get_mem(bytes, &icount->list); if (retval) goto errout; memset(icount->list, 0, bytes); icount->magic = EXT2_ET_MAGIC_ICOUNT; icount->count = 0; icount->cursor = 0; icount->num_inodes = fs->super->s_inodes_count; /* * Populate the sorted list with those entries which were * found in the hint icount (since those are ones which will * likely need to be in the sorted list this time around). */ if (hint) { for (i=0; i < hint->count; i++) icount->list[i].ino = hint->list[i].ino; icount->count = hint->count; } *ret = icount; return 0; errout: ext2fs_free_icount(icount); return retval; } errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, unsigned int size, ext2_icount_t *ret) { return ext2fs_create_icount2(fs, flags, size, 0, ret); } /* * insert_icount_el() --- Insert a new entry into the sorted list at a * specified position. */ static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount, ext2_ino_t ino, int pos) { struct ext2_icount_el *el; errcode_t retval; ext2_ino_t new_size = 0; int num; if (icount->count >= icount->size) { if (icount->count) { new_size = icount->list[(unsigned)icount->count-1].ino; new_size = (ext2_ino_t) (icount->count * ((float) icount->num_inodes / new_size)); } if (new_size < (icount->size + 100)) new_size = icount->size + 100; retval = ext2fs_resize_mem((size_t) icount->size * sizeof(struct ext2_icount_el), (size_t) new_size * sizeof(struct ext2_icount_el), &icount->list); if (retval) return 0; icount->size = new_size; } num = (int) icount->count - pos; if (num < 0) return 0; /* should never happen */ if (num) { memmove(&icount->list[pos+1], &icount->list[pos], sizeof(struct ext2_icount_el) * num); } icount->count++; el = &icount->list[pos]; el->count = 0; el->ino = ino; return el; } /* * get_icount_el() --- given an inode number, try to find icount * information in the sorted list. If the create flag is set, * and we can't find an entry, create one in the sorted list. */ static struct ext2_icount_el *get_icount_el(ext2_icount_t icount, ext2_ino_t ino, int create) { float range; int low, high, mid; ext2_ino_t lowval, highval; if (!icount || !icount->list) return 0; if (create && ((icount->count == 0) || (ino > icount->list[(unsigned)icount->count-1].ino))) { return insert_icount_el(icount, ino, (unsigned) icount->count); } if (icount->count == 0) return 0; if (icount->cursor >= icount->count) icount->cursor = 0; if (ino == icount->list[icount->cursor].ino) return &icount->list[icount->cursor++]; low = 0; high = (int) icount->count-1; while (low <= high) { if (low == high) mid = low; else { /* Interpolate for efficiency */ lowval = icount->list[low].ino; highval = icount->list[high].ino; if (ino < lowval) range = 0; else if (ino > highval) range = 1; else range = ((float) (ino - lowval)) / (highval - lowval); mid = low + ((int) (range * (high-low))); } if (ino == icount->list[mid].ino) { icount->cursor = mid+1; return &icount->list[mid]; } if (ino < icount->list[mid].ino) high = mid-1; else low = mid+1; } /* * If we need to create a new entry, it should be right at * low (where high will be left at low-1). */ if (create) return insert_icount_el(icount, ino, low); return 0; } errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out) { errcode_t ret = 0; unsigned int i; const char *bad = "bad icount"; EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); if (icount->count > icount->size) { fprintf(out, "%s: count > size\n", bad); return EXT2_ET_INVALID_ARGUMENT; } for (i=1; i < icount->count; i++) { if (icount->list[i-1].ino >= icount->list[i].ino) { fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n", bad, i-1, icount->list[i-1].ino, i, icount->list[i].ino); ret = EXT2_ET_INVALID_ARGUMENT; } } return ret; } errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret) { struct ext2_icount_el *el; EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); if (!ino || (ino > icount->num_inodes)) return EXT2_ET_INVALID_ARGUMENT; if (ext2fs_test_inode_bitmap(icount->single, ino)) { *ret = 1; return 0; } if (icount->multiple && !ext2fs_test_inode_bitmap(icount->multiple, ino)) { *ret = 0; return 0; } el = get_icount_el(icount, ino, 0); if (!el) { *ret = 0; return 0; } *ret = el->count; return 0; } errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret) { struct ext2_icount_el *el; EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); if (!ino || (ino > icount->num_inodes)) return EXT2_ET_INVALID_ARGUMENT; if (ext2fs_test_inode_bitmap(icount->single, ino)) { /* * If the existing count is 1, then we know there is * no entry in the list. */ el = get_icount_el(icount, ino, 1); if (!el) return EXT2_ET_NO_MEMORY; ext2fs_unmark_inode_bitmap(icount->single, ino); el->count = 2; } else if (icount->multiple) { /* * The count is either zero or greater than 1; if the * inode is set in icount->multiple, then there should * be an entry in the list, so find it using * get_icount_el(). */ if (ext2fs_test_inode_bitmap(icount->multiple, ino)) { el = get_icount_el(icount, ino, 1); if (!el) return EXT2_ET_NO_MEMORY; el->count++; } else { /* * The count was zero; mark the single bitmap * and return. */ zero_count: ext2fs_mark_inode_bitmap(icount->single, ino); if (ret) *ret = 1; return 0; } } else { /* * The count is either zero or greater than 1; try to * find an entry in the list to determine which. */ el = get_icount_el(icount, ino, 0); if (!el) { /* No entry means the count was zero */ goto zero_count; } el = get_icount_el(icount, ino, 1); if (!el) return EXT2_ET_NO_MEMORY; el->count++; } if (icount->multiple) ext2fs_mark_inode_bitmap(icount->multiple, ino); if (ret) *ret = el->count; return 0; } errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret) { struct ext2_icount_el *el; if (!ino || (ino > icount->num_inodes)) return EXT2_ET_INVALID_ARGUMENT; EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); if (ext2fs_test_inode_bitmap(icount->single, ino)) { ext2fs_unmark_inode_bitmap(icount->single, ino); if (icount->multiple) ext2fs_unmark_inode_bitmap(icount->multiple, ino); else { el = get_icount_el(icount, ino, 0); if (el) el->count = 0; } if (ret) *ret = 0; return 0; } if (icount->multiple && !ext2fs_test_inode_bitmap(icount->multiple, ino)) return EXT2_ET_INVALID_ARGUMENT; el = get_icount_el(icount, ino, 0); if (!el || el->count == 0) return EXT2_ET_INVALID_ARGUMENT; el->count--; if (el->count == 1) ext2fs_mark_inode_bitmap(icount->single, ino); if ((el->count == 0) && icount->multiple) ext2fs_unmark_inode_bitmap(icount->multiple, ino); if (ret) *ret = el->count; return 0; } errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, __u16 count) { struct ext2_icount_el *el; if (!ino || (ino > icount->num_inodes)) return EXT2_ET_INVALID_ARGUMENT; EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); if (count == 1) { ext2fs_mark_inode_bitmap(icount->single, ino); if (icount->multiple) ext2fs_unmark_inode_bitmap(icount->multiple, ino); return 0; } if (count == 0) { ext2fs_unmark_inode_bitmap(icount->single, ino); if (icount->multiple) { /* * If the icount->multiple bitmap is enabled, * we can just clear both bitmaps and we're done */ ext2fs_unmark_inode_bitmap(icount->multiple, ino); } else { el = get_icount_el(icount, ino, 0); if (el) el->count = 0; } return 0; } /* * Get the icount element */ el = get_icount_el(icount, ino, 1); if (!el) return EXT2_ET_NO_MEMORY; el->count = count; ext2fs_unmark_inode_bitmap(icount->single, ino); if (icount->multiple) ext2fs_mark_inode_bitmap(icount->multiple, ino); return 0; } ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount) { if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT) return 0; return icount->size; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/inode.c0000644000000000000000000004632112263563520021137 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * inode.c --- utility routines to read and write inodes * * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fsP.h" #include "e2image.h" struct ext2_struct_inode_scan { errcode_t magic; ext2_filsys fs; ext2_ino_t current_inode; blk_t current_block; dgrp_t current_group; ext2_ino_t inodes_left; blk_t blocks_left; dgrp_t groups_left; blk_t inode_buffer_blocks; char * inode_buffer; int inode_size; char * ptr; int bytes_left; char *temp_buffer; errcode_t (*done_group)(ext2_filsys fs, dgrp_t group, void * priv_data); void * done_group_data; int bad_block_ptr; int scan_flags; int reserved[6]; }; /* * This routine flushes the icache, if it exists. */ errcode_t ext2fs_flush_icache(ext2_filsys fs) { int i; if (!fs->icache) return 0; for (i=0; i < fs->icache->cache_size; i++) fs->icache->cache[i].ino = 0; fs->icache->buffer_blk = 0; return 0; } static errcode_t create_icache(ext2_filsys fs) { errcode_t retval; if (fs->icache) return 0; retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache), &fs->icache); if (retval) return retval; memset(fs->icache, 0, sizeof(struct ext2_inode_cache)); retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer); if (retval) { ext2fs_free_mem(&fs->icache); return retval; } fs->icache->buffer_blk = 0; fs->icache->cache_last = -1; fs->icache->cache_size = 4; fs->icache->refcount = 1; retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache_ent) * fs->icache->cache_size, &fs->icache->cache); if (retval) { ext2fs_free_mem(&fs->icache->buffer); ext2fs_free_mem(&fs->icache); return retval; } ext2fs_flush_icache(fs); return 0; } errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, ext2_inode_scan *ret_scan) { ext2_inode_scan scan; errcode_t retval; errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks); EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); /* * If fs->badblocks isn't set, then set it --- since the inode * scanning functions require it. */ if (fs->badblocks == 0) { /* * Temporarly save fs->get_blocks and set it to zero, * for compatibility with old e2fsck's. */ save_get_blocks = fs->get_blocks; fs->get_blocks = 0; retval = ext2fs_read_bb_inode(fs, &fs->badblocks); if (retval) { ext2fs_badblocks_list_free(fs->badblocks); fs->badblocks = 0; } fs->get_blocks = save_get_blocks; } retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan), &scan); if (retval) return retval; memset(scan, 0, sizeof(struct ext2_struct_inode_scan)); scan->magic = EXT2_ET_MAGIC_INODE_SCAN; scan->fs = fs; scan->inode_size = EXT2_INODE_SIZE(fs->super); scan->bytes_left = 0; scan->current_group = 0; scan->groups_left = fs->group_desc_count - 1; scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8; scan->current_block = scan->fs-> group_desc[scan->current_group].bg_inode_table; scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super); scan->blocks_left = scan->fs->inode_blocks_per_group; retval = ext2fs_get_mem((size_t) (scan->inode_buffer_blocks * fs->blocksize), &scan->inode_buffer); scan->done_group = 0; scan->done_group_data = 0; scan->bad_block_ptr = 0; if (retval) { ext2fs_free_mem(&scan); return retval; } retval = ext2fs_get_mem(scan->inode_size, &scan->temp_buffer); if (retval) { ext2fs_free_mem(&scan->inode_buffer); ext2fs_free_mem(&scan); return retval; } if (scan->fs->badblocks && scan->fs->badblocks->num) scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS; *ret_scan = scan; return 0; } void ext2fs_close_inode_scan(ext2_inode_scan scan) { if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) return; ext2fs_free_mem(&scan->inode_buffer); scan->inode_buffer = NULL; ext2fs_free_mem(&scan->temp_buffer); scan->temp_buffer = NULL; ext2fs_free_mem(&scan); } void ext2fs_set_inode_callback(ext2_inode_scan scan, errcode_t (*done_group)(ext2_filsys fs, dgrp_t group, void * priv_data), void *done_group_data) { if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) return; scan->done_group = done_group; scan->done_group_data = done_group_data; } int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags, int clear_flags) { int old_flags; if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) return 0; old_flags = scan->scan_flags; scan->scan_flags &= ~clear_flags; scan->scan_flags |= set_flags; return old_flags; } /* * This function is called by ext2fs_get_next_inode when it needs to * get ready to read in a new blockgroup. */ static errcode_t get_next_blockgroup(ext2_inode_scan scan) { scan->current_group++; scan->groups_left--; scan->current_block = scan->fs-> group_desc[scan->current_group].bg_inode_table; scan->current_inode = scan->current_group * EXT2_INODES_PER_GROUP(scan->fs->super); scan->bytes_left = 0; scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super); scan->blocks_left = scan->fs->inode_blocks_per_group; return 0; } errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan, int group) { scan->current_group = group - 1; scan->groups_left = scan->fs->group_desc_count - group; return get_next_blockgroup(scan); } /* * This function is called by get_next_blocks() to check for bad * blocks in the inode table. * * This function assumes that badblocks_list->list is sorted in * increasing order. */ static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan, blk_t *num_blocks) { blk_t blk = scan->current_block; badblocks_list bb = scan->fs->badblocks; /* * If the inode table is missing, then obviously there are no * bad blocks. :-) */ if (blk == 0) return 0; /* * If the current block is greater than the bad block listed * in the bad block list, then advance the pointer until this * is no longer the case. If we run out of bad blocks, then * we don't need to do any more checking! */ while (blk > bb->list[scan->bad_block_ptr]) { if (++scan->bad_block_ptr >= bb->num) { scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS; return 0; } } /* * If the current block is equal to the bad block listed in * the bad block list, then handle that one block specially. * (We could try to handle runs of bad blocks, but that * only increases CPU efficiency by a small amount, at the * expense of a huge expense of code complexity, and for an * uncommon case at that.) */ if (blk == bb->list[scan->bad_block_ptr]) { scan->scan_flags |= EXT2_SF_BAD_INODE_BLK; *num_blocks = 1; if (++scan->bad_block_ptr >= bb->num) scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS; return 0; } /* * If there is a bad block in the range that we're about to * read in, adjust the number of blocks to read so that we we * don't read in the bad block. (Then the next block to read * will be the bad block, which is handled in the above case.) */ if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr]) *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk); return 0; } /* * This function is called by ext2fs_get_next_inode when it needs to * read in more blocks from the current blockgroup's inode table. */ static errcode_t get_next_blocks(ext2_inode_scan scan) { blk_t num_blocks; errcode_t retval; /* * Figure out how many blocks to read; we read at most * inode_buffer_blocks, and perhaps less if there aren't that * many blocks left to read. */ num_blocks = scan->inode_buffer_blocks; if (num_blocks > scan->blocks_left) num_blocks = scan->blocks_left; /* * If the past block "read" was a bad block, then mark the * left-over extra bytes as also being bad. */ if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) { if (scan->bytes_left) scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES; scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK; } /* * Do inode bad block processing, if necessary. */ if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) { retval = check_for_inode_bad_blocks(scan, &num_blocks); if (retval) return retval; } if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) || (scan->current_block == 0)) { memset(scan->inode_buffer, 0, (size_t) num_blocks * scan->fs->blocksize); } else { retval = io_channel_read_blk(scan->fs->io, scan->current_block, (int) num_blocks, scan->inode_buffer); if (retval) return EXT2_ET_NEXT_INODE_READ; } scan->ptr = scan->inode_buffer; scan->bytes_left = num_blocks * scan->fs->blocksize; scan->blocks_left -= num_blocks; if (scan->current_block) scan->current_block += num_blocks; return 0; } errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino, struct ext2_inode *inode, int bufsize) { errcode_t retval; int extra_bytes = 0; EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN); /* * Do we need to start reading a new block group? */ if (scan->inodes_left <= 0) { force_new_group: if (scan->done_group) { retval = (scan->done_group) (scan->fs, scan->current_group, scan->done_group_data); if (retval) return retval; } if (scan->groups_left <= 0) { *ino = 0; return 0; } retval = get_next_blockgroup(scan); if (retval) return retval; } /* * This is done outside the above if statement so that the * check can be done for block group #0. */ if (scan->current_block == 0) { if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) { goto force_new_group; } else return EXT2_ET_MISSING_INODE_TABLE; } /* * Have we run out of space in the inode buffer? If so, we * need to read in more blocks. */ if (scan->bytes_left < scan->inode_size) { memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left); extra_bytes = scan->bytes_left; retval = get_next_blocks(scan); if (retval) return retval; #if 0 /* * XXX test Need check for used inode somehow. * (Note: this is hard.) */ if (is_empty_scan(scan)) goto force_new_group; #endif } retval = 0; if (extra_bytes) { memcpy(scan->temp_buffer+extra_bytes, scan->ptr, scan->inode_size - extra_bytes); scan->ptr += scan->inode_size - extra_bytes; scan->bytes_left -= scan->inode_size - extra_bytes; #if BB_BIG_ENDIAN if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) || (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) ext2fs_swap_inode_full(scan->fs, (struct ext2_inode_large *) inode, (struct ext2_inode_large *) scan->temp_buffer, 0, bufsize); else #endif *inode = *((struct ext2_inode *) scan->temp_buffer); if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES) retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES; } else { #if BB_BIG_ENDIAN if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) || (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) ext2fs_swap_inode_full(scan->fs, (struct ext2_inode_large *) inode, (struct ext2_inode_large *) scan->ptr, 0, bufsize); else #endif memcpy(inode, scan->ptr, bufsize); scan->ptr += scan->inode_size; scan->bytes_left -= scan->inode_size; if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; } scan->inodes_left--; scan->current_inode++; *ino = scan->current_inode; return retval; } errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino, struct ext2_inode *inode) { return ext2fs_get_next_inode_full(scan, ino, inode, sizeof(struct ext2_inode)); } /* * Functions to read and write a single inode. */ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode, int bufsize) { unsigned long group, block, block_nr, offset; char *ptr; errcode_t retval; int clen, i, inodes_per_block, length; io_channel io; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); /* Check to see if user has an override function */ if (fs->read_inode) { retval = (fs->read_inode)(fs, ino, inode); if (retval != EXT2_ET_CALLBACK_NOTHANDLED) return retval; } /* Create inode cache if not present */ if (!fs->icache) { retval = create_icache(fs); if (retval) return retval; } /* Check to see if it's in the inode cache */ if (bufsize == sizeof(struct ext2_inode)) { /* only old good inode can be retrieve from the cache */ for (i=0; i < fs->icache->cache_size; i++) { if (fs->icache->cache[i].ino == ino) { *inode = fs->icache->cache[i].inode; return 0; } } } if ((ino == 0) || (ino > fs->super->s_inodes_count)) return EXT2_ET_BAD_INODE_NUM; if (fs->flags & EXT2_FLAG_IMAGE_FILE) { inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super); block_nr = fs->image_header->offset_inode / fs->blocksize; block_nr += (ino - 1) / inodes_per_block; offset = ((ino - 1) % inodes_per_block) * EXT2_INODE_SIZE(fs->super); io = fs->image_io; } else { group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * EXT2_INODE_SIZE(fs->super); block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); if (!fs->group_desc[(unsigned)group].bg_inode_table) return EXT2_ET_MISSING_INODE_TABLE; block_nr = fs->group_desc[(unsigned)group].bg_inode_table + block; io = fs->io; } offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); length = EXT2_INODE_SIZE(fs->super); if (bufsize < length) length = bufsize; ptr = (char *) inode; while (length) { clen = length; if ((offset + length) > fs->blocksize) clen = fs->blocksize - offset; if (block_nr != fs->icache->buffer_blk) { retval = io_channel_read_blk(io, block_nr, 1, fs->icache->buffer); if (retval) return retval; fs->icache->buffer_blk = block_nr; } memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset, clen); offset = 0; length -= clen; ptr += clen; block_nr++; } #if BB_BIG_ENDIAN if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode, (struct ext2_inode_large *) inode, 0, length); #endif /* Update the inode cache */ fs->icache->cache_last = (fs->icache->cache_last + 1) % fs->icache->cache_size; fs->icache->cache[fs->icache->cache_last].ino = ino; fs->icache->cache[fs->icache->cache_last].inode = *inode; return 0; } errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode) { return ext2fs_read_inode_full(fs, ino, inode, sizeof(struct ext2_inode)); } errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode, int bufsize) { unsigned long group, block, block_nr, offset; errcode_t retval = 0; struct ext2_inode_large temp_inode, *w_inode; char *ptr; int clen, i, length; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); /* Check to see if user provided an override function */ if (fs->write_inode) { retval = (fs->write_inode)(fs, ino, inode); if (retval != EXT2_ET_CALLBACK_NOTHANDLED) return retval; } /* Check to see if the inode cache needs to be updated */ if (fs->icache) { for (i=0; i < fs->icache->cache_size; i++) { if (fs->icache->cache[i].ino == ino) { fs->icache->cache[i].inode = *inode; break; } } } else { retval = create_icache(fs); if (retval) return retval; } if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; if ((ino == 0) || (ino > fs->super->s_inodes_count)) return EXT2_ET_BAD_INODE_NUM; length = bufsize; if (length < EXT2_INODE_SIZE(fs->super)) length = EXT2_INODE_SIZE(fs->super); if (length > (int) sizeof(struct ext2_inode_large)) { w_inode = xmalloc(length); } else w_inode = &temp_inode; memset(w_inode, 0, length); #if BB_BIG_ENDIAN if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) ext2fs_swap_inode_full(fs, w_inode, (struct ext2_inode_large *) inode, 1, bufsize); else #endif memcpy(w_inode, inode, bufsize); group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * EXT2_INODE_SIZE(fs->super); block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); if (!fs->group_desc[(unsigned) group].bg_inode_table) return EXT2_ET_MISSING_INODE_TABLE; block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block; offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); length = EXT2_INODE_SIZE(fs->super); if (length > bufsize) length = bufsize; ptr = (char *) w_inode; while (length) { clen = length; if ((offset + length) > fs->blocksize) clen = fs->blocksize - offset; if (fs->icache->buffer_blk != block_nr) { retval = io_channel_read_blk(fs->io, block_nr, 1, fs->icache->buffer); if (retval) goto errout; fs->icache->buffer_blk = block_nr; } memcpy((char *) fs->icache->buffer + (unsigned) offset, ptr, clen); retval = io_channel_write_blk(fs->io, block_nr, 1, fs->icache->buffer); if (retval) goto errout; offset = 0; ptr += clen; length -= clen; block_nr++; } fs->flags |= EXT2_FLAG_CHANGED; errout: if (w_inode && w_inode != &temp_inode) free(w_inode); return retval; } errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode) { return ext2fs_write_inode_full(fs, ino, inode, sizeof(struct ext2_inode)); } /* * This function should be called when writing a new inode. It makes * sure that extra part of large inodes is initialized properly. */ errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode) { struct ext2_inode *buf; int size = EXT2_INODE_SIZE(fs->super); struct ext2_inode_large *large_inode; if (size == sizeof(struct ext2_inode)) return ext2fs_write_inode_full(fs, ino, inode, sizeof(struct ext2_inode)); buf = xmalloc(size); memset(buf, 0, size); *buf = *inode; large_inode = (struct ext2_inode_large *) buf; large_inode->i_extra_isize = sizeof(struct ext2_inode_large) - EXT2_GOOD_OLD_INODE_SIZE; return ext2fs_write_inode_full(fs, ino, buf, size); } errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks) { struct ext2_inode inode; int i; errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (ino > fs->super->s_inodes_count) return EXT2_ET_BAD_INODE_NUM; if (fs->get_blocks) { if (!(*fs->get_blocks)(fs, ino, blocks)) return 0; } retval = ext2fs_read_inode(fs, ino, &inode); if (retval) return retval; for (i=0; i < EXT2_N_BLOCKS; i++) blocks[i] = inode.i_block[i]; return 0; } errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino) { struct ext2_inode inode; errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (ino > fs->super->s_inodes_count) return EXT2_ET_BAD_INODE_NUM; if (fs->check_directory) { retval = (fs->check_directory)(fs, ino); if (retval != EXT2_ET_CALLBACK_NOTHANDLED) return retval; } retval = ext2fs_read_inode(fs, ino, &inode); if (retval) return retval; if (!LINUX_S_ISDIR(inode.i_mode)) return EXT2_ET_NO_DIRECTORY; return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/bitmaps.c0000644000000000000000000001151212263563520021472 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * bitmaps.c --- routines to read, write, and manipulate the inode and * block bitmaps. * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" static errcode_t make_bitmap(__u32 start, __u32 end, __u32 real_end, const char *descr, char *init_map, ext2fs_generic_bitmap *ret) { ext2fs_generic_bitmap bitmap; errcode_t retval; size_t size; retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap), &bitmap); if (retval) return retval; bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP; bitmap->fs = NULL; bitmap->start = start; bitmap->end = end; bitmap->real_end = real_end; bitmap->base_error_code = EXT2_ET_BAD_GENERIC_MARK; if (descr) { retval = ext2fs_get_mem(strlen(descr)+1, &bitmap->description); if (retval) { ext2fs_free_mem(&bitmap); return retval; } strcpy(bitmap->description, descr); } else bitmap->description = 0; size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1); retval = ext2fs_get_mem(size, &bitmap->bitmap); if (retval) { ext2fs_free_mem(&bitmap->description); ext2fs_free_mem(&bitmap); return retval; } if (init_map) memcpy(bitmap->bitmap, init_map, size); else memset(bitmap->bitmap, 0, size); *ret = bitmap; return 0; } errcode_t ext2fs_allocate_generic_bitmap(__u32 start, __u32 end, __u32 real_end, const char *descr, ext2fs_generic_bitmap *ret) { return make_bitmap(start, end, real_end, descr, 0, ret); } errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src, ext2fs_generic_bitmap *dest) { errcode_t retval; ext2fs_generic_bitmap new_map; retval = make_bitmap(src->start, src->end, src->real_end, src->description, src->bitmap, &new_map); if (retval) return retval; new_map->magic = src->magic; new_map->fs = src->fs; new_map->base_error_code = src->base_error_code; *dest = new_map; return 0; } void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map) { __u32 i, j; for (i=map->end+1, j = i - map->start; i <= map->real_end; i++, j++) ext2fs_set_bit(j, map->bitmap); } errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs, const char *descr, ext2fs_inode_bitmap *ret) { ext2fs_inode_bitmap bitmap; errcode_t retval; __u32 start, end, real_end; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); fs->write_bitmaps = ext2fs_write_bitmaps; start = 1; end = fs->super->s_inodes_count; real_end = (EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count); retval = ext2fs_allocate_generic_bitmap(start, end, real_end, descr, &bitmap); if (retval) return retval; bitmap->magic = EXT2_ET_MAGIC_INODE_BITMAP; bitmap->fs = fs; bitmap->base_error_code = EXT2_ET_BAD_INODE_MARK; *ret = bitmap; return 0; } errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs, const char *descr, ext2fs_block_bitmap *ret) { ext2fs_block_bitmap bitmap; errcode_t retval; __u32 start, end, real_end; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); fs->write_bitmaps = ext2fs_write_bitmaps; start = fs->super->s_first_data_block; end = fs->super->s_blocks_count-1; real_end = (EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count)-1 + start; retval = ext2fs_allocate_generic_bitmap(start, end, real_end, descr, &bitmap); if (retval) return retval; bitmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP; bitmap->fs = fs; bitmap->base_error_code = EXT2_ET_BAD_BLOCK_MARK; *ret = bitmap; return 0; } errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap, ext2_ino_t end, ext2_ino_t *oend) { EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_INODE_BITMAP); if (end > bitmap->real_end) return EXT2_ET_FUDGE_INODE_BITMAP_END; if (oend) *oend = bitmap->end; bitmap->end = end; return 0; } errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap, blk_t end, blk_t *oend) { EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_BLOCK_BITMAP); if (end > bitmap->real_end) return EXT2_ET_FUDGE_BLOCK_BITMAP_END; if (oend) *oend = bitmap->end; bitmap->end = end; return 0; } void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap) { if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_INODE_BITMAP)) return; memset(bitmap->bitmap, 0, (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1)); } void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap) { if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_BLOCK_BITMAP)) return; memset(bitmap->bitmap, 0, (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1)); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/getsectsize.c0000644000000000000000000000204212263563520022362 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * getsectsize.c --- get the sector size of a device. * * Copyright (C) 1995, 1995 Theodore Ts'o. * Copyright (C) 2003 VMware, Inc. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #ifdef HAVE_LINUX_FD_H #include #include #endif #if defined(__linux__) && defined(_IO) && !defined(BLKSSZGET) #define BLKSSZGET _IO(0x12,104)/* get block device sector size */ #endif #include "ext2_fs.h" #include "ext2fs.h" /* * Returns the number of blocks in a partition */ errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize) { int fd; #ifdef CONFIG_LFS fd = open64(file, O_RDONLY); #else fd = open(file, O_RDONLY); #endif if (fd < 0) return errno; #ifdef BLKSSZGET if (ioctl(fd, BLKSSZGET, sectsize) >= 0) { close(fd); return 0; } #endif *sectsize = 0; close(fd); return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c0000644000000000000000000000650112263563520022471 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * get_pathname.c --- do directry/inode -> name translation * * Copyright (C) 1993, 1994, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% * * ext2fs_get_pathname(fs, dir, ino, name) * * This function translates takes two inode numbers into a * string, placing the result in . is the containing * directory inode, and is the inode number itself. If * is zero, then ext2fs_get_pathname will return pathname * of the directory . * */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct get_pathname_struct { ext2_ino_t search_ino; ext2_ino_t parent; char *name; errcode_t errcode; }; #ifdef __TURBOC__ # pragma argsused #endif static int get_pathname_proc(struct ext2_dir_entry *dirent, int offset EXT2FS_ATTR((unused)), int blocksize EXT2FS_ATTR((unused)), char *buf EXT2FS_ATTR((unused)), void *priv_data) { struct get_pathname_struct *gp; errcode_t retval; gp = (struct get_pathname_struct *) priv_data; if (((dirent->name_len & 0xFF) == 2) && !strncmp(dirent->name, "..", 2)) gp->parent = dirent->inode; if (dirent->inode == gp->search_ino) { retval = ext2fs_get_mem((dirent->name_len & 0xFF) + 1, &gp->name); if (retval) { gp->errcode = retval; return DIRENT_ABORT; } strncpy(gp->name, dirent->name, (dirent->name_len & 0xFF)); gp->name[dirent->name_len & 0xFF] = '\0'; return DIRENT_ABORT; } return 0; } static errcode_t ext2fs_get_pathname_int(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino, int maxdepth, char *buf, char **name) { struct get_pathname_struct gp; char *parent_name, *ret; errcode_t retval; if (dir == ino) { retval = ext2fs_get_mem(2, name); if (retval) return retval; strcpy(*name, (dir == EXT2_ROOT_INO) ? "/" : "."); return 0; } if (!dir || (maxdepth < 0)) { retval = ext2fs_get_mem(4, name); if (retval) return retval; strcpy(*name, "..."); return 0; } gp.search_ino = ino; gp.parent = 0; gp.name = 0; gp.errcode = 0; retval = ext2fs_dir_iterate(fs, dir, 0, buf, get_pathname_proc, &gp); if (retval) goto cleanup; if (gp.errcode) { retval = gp.errcode; goto cleanup; } retval = ext2fs_get_pathname_int(fs, gp.parent, dir, maxdepth-1, buf, &parent_name); if (retval) goto cleanup; if (!ino) { *name = parent_name; return 0; } if (gp.name) retval = ext2fs_get_mem(strlen(parent_name)+strlen(gp.name)+2, &ret); else retval = ext2fs_get_mem(strlen(parent_name)+5, &ret); if (retval) goto cleanup; ret[0] = 0; if (parent_name[1]) strcat(ret, parent_name); strcat(ret, "/"); if (gp.name) strcat(ret, gp.name); else strcat(ret, "???"); *name = ret; ext2fs_free_mem(&parent_name); retval = 0; cleanup: ext2fs_free_mem(&gp.name); return retval; } errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino, char **name) { char *buf; errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; if (dir == ino) ino = 0; retval = ext2fs_get_pathname_int(fs, dir, ino, 32, buf, name); ext2fs_free_mem(&buf); return retval; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c0000644000000000000000000001654412263563520022214 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * rw_bitmaps.c --- routines to read and write the inode and block bitmaps. * * Copyright (C) 1993, 1994, 1994, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #include "e2image.h" #if defined(__powerpc__) && BB_BIG_ENDIAN /* * On the PowerPC, the big-endian variant of the ext2 filesystem * has its bitmaps stored as 32-bit words with bit 0 as the LSB * of each word. Thus a bitmap with only bit 0 set would be, as * a string of bytes, 00 00 00 01 00 ... * To cope with this, we byte-reverse each word of a bitmap if * we have a big-endian filesystem, that is, if we are *not* * byte-swapping other word-sized numbers. */ #define EXT2_BIG_ENDIAN_BITMAPS #endif #ifdef EXT2_BIG_ENDIAN_BITMAPS static void ext2fs_swap_bitmap(ext2_filsys fs, char *bitmap, int nbytes) { __u32 *p = (__u32 *) bitmap; int n; for (n = nbytes / sizeof(__u32); n > 0; --n, ++p) *p = ext2fs_swab32(*p); } #endif errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs) { dgrp_t i; size_t nbytes; errcode_t retval; char * inode_bitmap = fs->inode_map->bitmap; char * bitmap_block = NULL; blk_t blk; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; if (!inode_bitmap) return 0; nbytes = (size_t) ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8); retval = ext2fs_get_mem(fs->blocksize, &bitmap_block); if (retval) return retval; memset(bitmap_block, 0xff, fs->blocksize); for (i = 0; i < fs->group_desc_count; i++) { memcpy(bitmap_block, inode_bitmap, nbytes); blk = fs->group_desc[i].bg_inode_bitmap; if (blk) { #ifdef EXT2_BIG_ENDIAN_BITMAPS if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) || (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))) ext2fs_swap_bitmap(fs, bitmap_block, nbytes); #endif retval = io_channel_write_blk(fs->io, blk, 1, bitmap_block); if (retval) return EXT2_ET_INODE_BITMAP_WRITE; } inode_bitmap += nbytes; } fs->flags &= ~EXT2_FLAG_IB_DIRTY; ext2fs_free_mem(&bitmap_block); return 0; } errcode_t ext2fs_write_block_bitmap (ext2_filsys fs) { dgrp_t i; unsigned int j; int nbytes; unsigned int nbits; errcode_t retval; char * block_bitmap = fs->block_map->bitmap; char * bitmap_block = NULL; blk_t blk; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; if (!block_bitmap) return 0; nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; retval = ext2fs_get_mem(fs->blocksize, &bitmap_block); if (retval) return retval; memset(bitmap_block, 0xff, fs->blocksize); for (i = 0; i < fs->group_desc_count; i++) { memcpy(bitmap_block, block_bitmap, nbytes); if (i == fs->group_desc_count - 1) { /* Force bitmap padding for the last group */ nbits = ((fs->super->s_blocks_count - fs->super->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(fs->super)); if (nbits) for (j = nbits; j < fs->blocksize * 8; j++) ext2fs_set_bit(j, bitmap_block); } blk = fs->group_desc[i].bg_block_bitmap; if (blk) { #ifdef EXT2_BIG_ENDIAN_BITMAPS if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) || (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))) ext2fs_swap_bitmap(fs, bitmap_block, nbytes); #endif retval = io_channel_write_blk(fs->io, blk, 1, bitmap_block); if (retval) return EXT2_ET_BLOCK_BITMAP_WRITE; } block_bitmap += nbytes; } fs->flags &= ~EXT2_FLAG_BB_DIRTY; ext2fs_free_mem(&bitmap_block); return 0; } static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) { dgrp_t i; char *block_bitmap = NULL, *inode_bitmap = NULL; char *buf; errcode_t retval; int block_nbytes = (int) EXT2_BLOCKS_PER_GROUP(fs->super) / 8; int inode_nbytes = (int) EXT2_INODES_PER_GROUP(fs->super) / 8; blk_t blk; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); fs->write_bitmaps = ext2fs_write_bitmaps; retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) return retval; if (do_block) { ext2fs_free_block_bitmap(fs->block_map); sprintf(buf, "block bitmap for %s", fs->device_name); retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); if (retval) goto cleanup; block_bitmap = fs->block_map->bitmap; } if (do_inode) { ext2fs_free_inode_bitmap(fs->inode_map); sprintf(buf, "inode bitmap for %s", fs->device_name); retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); if (retval) goto cleanup; inode_bitmap = fs->inode_map->bitmap; } ext2fs_free_mem(&buf); if (fs->flags & EXT2_FLAG_IMAGE_FILE) { if (inode_bitmap) { blk = (fs->image_header->offset_inodemap / fs->blocksize); retval = io_channel_read_blk(fs->image_io, blk, -(inode_nbytes * fs->group_desc_count), inode_bitmap); if (retval) goto cleanup; } if (block_bitmap) { blk = (fs->image_header->offset_blockmap / fs->blocksize); retval = io_channel_read_blk(fs->image_io, blk, -(block_nbytes * fs->group_desc_count), block_bitmap); if (retval) goto cleanup; } return 0; } for (i = 0; i < fs->group_desc_count; i++) { if (block_bitmap) { blk = fs->group_desc[i].bg_block_bitmap; if (blk) { retval = io_channel_read_blk(fs->io, blk, -block_nbytes, block_bitmap); if (retval) { retval = EXT2_ET_BLOCK_BITMAP_READ; goto cleanup; } #ifdef EXT2_BIG_ENDIAN_BITMAPS if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) || (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))) ext2fs_swap_bitmap(fs, block_bitmap, block_nbytes); #endif } else memset(block_bitmap, 0, block_nbytes); block_bitmap += block_nbytes; } if (inode_bitmap) { blk = fs->group_desc[i].bg_inode_bitmap; if (blk) { retval = io_channel_read_blk(fs->io, blk, -inode_nbytes, inode_bitmap); if (retval) { retval = EXT2_ET_INODE_BITMAP_READ; goto cleanup; } #ifdef EXT2_BIG_ENDIAN_BITMAPS if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) || (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))) ext2fs_swap_bitmap(fs, inode_bitmap, inode_nbytes); #endif } else memset(inode_bitmap, 0, inode_nbytes); inode_bitmap += inode_nbytes; } } return 0; cleanup: if (do_block) { ext2fs_free_mem(&fs->block_map); } if (do_inode) { ext2fs_free_mem(&fs->inode_map); } ext2fs_free_mem(&buf); return retval; } errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs) { return read_bitmaps(fs, 1, 0); } errcode_t ext2fs_read_block_bitmap(ext2_filsys fs) { return read_bitmaps(fs, 0, 1); } errcode_t ext2fs_read_bitmaps(ext2_filsys fs) { EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (fs->inode_map && fs->block_map) return 0; return read_bitmaps(fs, !fs->inode_map, !fs->block_map); } errcode_t ext2fs_write_bitmaps(ext2_filsys fs) { errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (fs->block_map && ext2fs_test_bb_dirty(fs)) { retval = ext2fs_write_block_bitmap(fs); if (retval) return retval; } if (fs->inode_map && ext2fs_test_ib_dirty(fs)) { retval = ext2fs_write_inode_bitmap(fs); if (retval) return retval; } return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/openfs.c0000644000000000000000000002135412263563520021332 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * openfs.c --- open an ext2 filesystem * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #include "e2image.h" blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i) { int bg; int has_super = 0; int ret_blk; if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || (i < fs->super->s_first_meta_bg)) return (group_block + i + 1); bg = (fs->blocksize / sizeof (struct ext2_group_desc)) * i; if (ext2fs_bg_has_super(fs, bg)) has_super = 1; ret_blk = (fs->super->s_first_data_block + has_super + (bg * fs->super->s_blocks_per_group)); /* * If group_block is not the normal value, we're trying to use * the backup group descriptors and superblock --- so use the * alternate location of the second block group in the * metablock group. Ideally we should be testing each bg * descriptor block individually for correctness, but we don't * have the infrastructure in place to do that. */ if (group_block != fs->super->s_first_data_block && ((ret_blk + fs->super->s_blocks_per_group) < fs->super->s_blocks_count)) ret_blk += fs->super->s_blocks_per_group; return ret_blk; } errcode_t ext2fs_open(const char *name, int flags, int superblock, unsigned int block_size, io_manager manager, ext2_filsys *ret_fs) { return ext2fs_open2(name, 0, flags, superblock, block_size, manager, ret_fs); } /* * Note: if superblock is non-zero, block-size must also be non-zero. * Superblock and block_size can be zero to use the default size. * * Valid flags for ext2fs_open() * * EXT2_FLAG_RW - Open the filesystem for read/write. * EXT2_FLAG_FORCE - Open the filesystem even if some of the * features aren't supported. * EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device */ errcode_t ext2fs_open2(const char *name, const char *io_options, int flags, int superblock, unsigned int block_size, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; unsigned long i; int groups_per_block, blocks_per_group; blk_t group_block, blk; char *dest, *cp; #if BB_BIG_ENDIAN int j; struct ext2_group_desc *gdp; #endif EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER); retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); if (retval) return retval; memset(fs, 0, sizeof(struct struct_ext2_filsys)); fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; fs->flags = flags; fs->umask = 022; retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); cp = strchr(fs->device_name, '?'); if (!io_options && cp) { *cp++ = 0; io_options = cp; } retval = manager->open(fs->device_name, (flags & EXT2_FLAG_RW) ? IO_FLAG_RW : 0, &fs->io); if (retval) goto cleanup; if (io_options && (retval = io_channel_set_options(fs->io, io_options))) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super); if (retval) goto cleanup; if (flags & EXT2_FLAG_IMAGE_FILE) { retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr), &fs->image_header); if (retval) goto cleanup; retval = io_channel_read_blk(fs->io, 0, -(int)sizeof(struct ext2_image_hdr), fs->image_header); if (retval) goto cleanup; if (fs->image_header->magic_number != EXT2_ET_MAGIC_E2IMAGE) return EXT2_ET_MAGIC_E2IMAGE; superblock = 1; block_size = fs->image_header->fs_blocksize; } /* * If the user specifies a specific block # for the * superblock, then he/she must also specify the block size! * Otherwise, read the master superblock located at offset * SUPERBLOCK_OFFSET from the start of the partition. * * Note: we only save a backup copy of the superblock if we * are reading the superblock from the primary superblock location. */ if (superblock) { if (!block_size) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } io_channel_set_blksize(fs->io, block_size); group_block = superblock; fs->orig_super = 0; } else { io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); superblock = 1; group_block = 0; retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); if (retval) goto cleanup; } retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE, fs->super); if (retval) goto cleanup; if (fs->orig_super) memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE); #if BB_BIG_ENDIAN if ((fs->super->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) || (fs->flags & EXT2_FLAG_SWAP_BYTES)) { fs->flags |= EXT2_FLAG_SWAP_BYTES; ext2fs_swap_super(fs->super); } #endif if (fs->super->s_magic != EXT2_SUPER_MAGIC) { retval = EXT2_ET_BAD_MAGIC; goto cleanup; } if (fs->super->s_rev_level > EXT2_LIB_CURRENT_REV) { retval = EXT2_ET_REV_TOO_HIGH; goto cleanup; } /* * Check for feature set incompatibility */ if (!(flags & EXT2_FLAG_FORCE)) { if (fs->super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } if ((flags & EXT2_FLAG_RW) && (fs->super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) { retval = EXT2_ET_RO_UNSUPP_FEATURE; goto cleanup; } if (!(flags & EXT2_FLAG_JOURNAL_DEV_OK) && (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } } fs->blocksize = EXT2_BLOCK_SIZE(fs->super); if (fs->blocksize == 0) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->fragsize = EXT2_FRAG_SIZE(fs->super); fs->inode_blocks_per_group = ((fs->super->s_inodes_per_group * EXT2_INODE_SIZE(fs->super) + EXT2_BLOCK_SIZE(fs->super) - 1) / EXT2_BLOCK_SIZE(fs->super)); if (block_size) { if (block_size != fs->blocksize) { retval = EXT2_ET_UNEXPECTED_BLOCK_SIZE; goto cleanup; } } /* * Set the blocksize to the filesystem's blocksize. */ io_channel_set_blksize(fs->io, fs->blocksize); /* * If this is an external journal device, don't try to read * the group descriptors, because they're not there. */ if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { fs->group_desc_count = 0; *ret_fs = fs; return 0; } /* * Read group descriptors */ blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super); if (blocks_per_group == 0 || blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(fs->super) || fs->inode_blocks_per_group > EXT2_MAX_INODES_PER_GROUP(fs->super)) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->group_desc_count = (fs->super->s_blocks_count - fs->super->s_first_data_block + blocks_per_group - 1) / blocks_per_group; fs->desc_blocks = (fs->group_desc_count + EXT2_DESC_PER_BLOCK(fs->super) - 1) / EXT2_DESC_PER_BLOCK(fs->super); retval = ext2fs_get_mem(fs->desc_blocks * fs->blocksize, &fs->group_desc); if (retval) goto cleanup; if (!group_block) group_block = fs->super->s_first_data_block; dest = (char *) fs->group_desc; groups_per_block = fs->blocksize / sizeof(struct ext2_group_desc); for (i = 0; i < fs->desc_blocks; i++) { blk = ext2fs_descriptor_block_loc(fs, group_block, i); retval = io_channel_read_blk(fs->io, blk, 1, dest); if (retval) goto cleanup; #if BB_BIG_ENDIAN if (fs->flags & EXT2_FLAG_SWAP_BYTES) { gdp = (struct ext2_group_desc *) dest; for (j=0; j < groups_per_block; j++) ext2fs_swap_group_desc(gdp++); } #endif dest += fs->blocksize; } *ret_fs = fs; return 0; cleanup: ext2fs_free(fs); return retval; } /* * Set/get the filesystem data I/O channel. * * These functions are only valid if EXT2_FLAG_IMAGE_FILE is true. */ errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io) { if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) return EXT2_ET_NOT_IMAGE_FILE; if (old_io) { *old_io = (fs->image_io == fs->io) ? 0 : fs->io; } return 0; } errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io) { if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) return EXT2_ET_NOT_IMAGE_FILE; fs->io = new_io ? new_io : fs->image_io; return 0; } errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io) { if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) return EXT2_ET_NOT_IMAGE_FILE; fs->io = fs->image_io = new_io; fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_RW | EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY; fs->flags &= ~EXT2_FLAG_IMAGE_FILE; return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/sparse.c0000644000000000000000000000316512263563520021335 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * sparse.c --- find the groups in an ext2 filesystem with metadata backups * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * Copyright (C) 2002 Andreas Dilger. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include "ext2_fs.h" #include "ext2fsP.h" static int test_root(int a, int b) { if (a == 0) return 1; while (1) { if (a == 1) return 1; if (a % b) return 0; a = a / b; } } int ext2fs_bg_has_super(ext2_filsys fs, int group_block) { if (!(fs->super->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) return 1; if (test_root(group_block, 3) || (test_root(group_block, 5)) || test_root(group_block, 7)) return 1; return 0; } /* * Iterate through the groups which hold BACKUP superblock/GDT copies in an * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before * calling this for the first time. In a sparse filesystem it will be the * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ... * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ... */ unsigned int ext2fs_list_backups(ext2_filsys fs, unsigned int *three, unsigned int *five, unsigned int *seven) { unsigned int *min = three; int mult = 3; unsigned int ret; if (!(fs->super->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { ret = *min; *min += 1; return ret; } if (*five < *min) { min = five; mult = 5; } if (*seven < *min) { min = seven; mult = 7; } ret = *min; *min *= mult; return ret; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/ext_attr.c0000644000000000000000000000434112263563520021667 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * ext_attr.c --- extended attribute blocks * * Copyright (C) 2001 Andreas Gruenbacher, * * Copyright (C) 2002 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #include #include #include "ext2_fs.h" #include "ext2_ext_attr.h" #include "ext2fs.h" errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf) { errcode_t retval; retval = io_channel_read_blk(fs->io, block, 1, buf); if (retval) return retval; #if BB_BIG_ENDIAN if ((fs->flags & (EXT2_FLAG_SWAP_BYTES| EXT2_FLAG_SWAP_BYTES_READ)) != 0) ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1); #endif return 0; } errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf) { errcode_t retval; char *write_buf; char *buf = NULL; if (BB_BIG_ENDIAN && ((fs->flags & EXT2_FLAG_SWAP_BYTES) || (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))) { retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; write_buf = buf; ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1); } else write_buf = (char *) inbuf; retval = io_channel_write_blk(fs->io, block, 1, write_buf); if (buf) ext2fs_free_mem(&buf); if (!retval) ext2fs_mark_changed(fs); return retval; } /* * This function adjusts the reference count of the EA block. */ errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, char *block_buf, int adjust, __u32 *newcount) { errcode_t retval; struct ext2_ext_attr_header *header; char *buf = NULL; if ((blk >= fs->super->s_blocks_count) || (blk < fs->super->s_first_data_block)) return EXT2_ET_BAD_EA_BLOCK_NUM; if (!block_buf) { retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; block_buf = buf; } retval = ext2fs_read_ext_attr(fs, blk, block_buf); if (retval) goto errout; header = (struct ext2_ext_attr_header *) block_buf; header->h_refcount += adjust; if (newcount) *newcount = header->h_refcount; retval = ext2fs_write_ext_attr(fs, blk, block_buf); if (retval) goto errout; errout: if (buf) ext2fs_free_mem(&buf); return retval; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/check_desc.c0000644000000000000000000000313512263563520022110 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * check_desc.c --- Check the group descriptors of an ext2 filesystem * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" /* * This routine sanity checks the group descriptors */ errcode_t ext2fs_check_desc(ext2_filsys fs) { dgrp_t i; blk_t block = fs->super->s_first_data_block; blk_t next; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); for (i = 0; i < fs->group_desc_count; i++) { next = block + fs->super->s_blocks_per_group; /* * Check to make sure block bitmap for group is * located within the group. */ if (fs->group_desc[i].bg_block_bitmap < block || fs->group_desc[i].bg_block_bitmap >= next) return EXT2_ET_GDESC_BAD_BLOCK_MAP; /* * Check to make sure inode bitmap for group is * located within the group */ if (fs->group_desc[i].bg_inode_bitmap < block || fs->group_desc[i].bg_inode_bitmap >= next) return EXT2_ET_GDESC_BAD_INODE_MAP; /* * Check to make sure inode table for group is located * within the group */ if (fs->group_desc[i].bg_inode_table < block || ((fs->group_desc[i].bg_inode_table + fs->inode_blocks_per_group) >= next)) return EXT2_ET_GDESC_BAD_INODE_TABLE; block = next; } return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/alloc.c0000644000000000000000000000714012263563520021127 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * alloc.c --- allocate new inodes, blocks for ext2fs * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% * */ #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" /* * Right now, just search forward from the parent directory's block * group to find the next free inode. * * Should have a special policy for directories. */ errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode EXT2FS_ATTR((unused)), ext2fs_inode_bitmap map, ext2_ino_t *ret) { ext2_ino_t dir_group = 0; ext2_ino_t i; ext2_ino_t start_inode; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!map) map = fs->inode_map; if (!map) return EXT2_ET_NO_INODE_BITMAP; if (dir > 0) dir_group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->super); start_inode = (dir_group * EXT2_INODES_PER_GROUP(fs->super)) + 1; if (start_inode < EXT2_FIRST_INODE(fs->super)) start_inode = EXT2_FIRST_INODE(fs->super); i = start_inode; do { if (!ext2fs_fast_test_inode_bitmap(map, i)) break; i++; if (i > fs->super->s_inodes_count) i = EXT2_FIRST_INODE(fs->super); } while (i != start_inode); if (ext2fs_test_inode_bitmap(map, i)) return EXT2_ET_INODE_ALLOC_FAIL; *ret = i; return 0; } /* * Stupid algorithm --- we now just search forward starting from the * goal. Should put in a smarter one someday.... */ errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal, ext2fs_block_bitmap map, blk_t *ret) { blk_t i; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!map) map = fs->block_map; if (!map) return EXT2_ET_NO_BLOCK_BITMAP; if (!goal || (goal >= fs->super->s_blocks_count)) goal = fs->super->s_first_data_block; i = goal; do { if (!ext2fs_fast_test_block_bitmap(map, i)) { *ret = i; return 0; } i++; if (i >= fs->super->s_blocks_count) i = fs->super->s_first_data_block; } while (i != goal); return EXT2_ET_BLOCK_ALLOC_FAIL; } /* * This function zeros out the allocated block, and updates all of the * appropriate filesystem records. */ errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal, char *block_buf, blk_t *ret) { errcode_t retval; blk_t block; char *buf = NULL; if (!block_buf) { retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; block_buf = buf; } memset(block_buf, 0, fs->blocksize); if (!fs->block_map) { retval = ext2fs_read_block_bitmap(fs); if (retval) goto fail; } retval = ext2fs_new_block(fs, goal, 0, &block); if (retval) goto fail; retval = io_channel_write_blk(fs->io, block, 1, block_buf); if (retval) goto fail; ext2fs_block_alloc_stats(fs, block, +1); *ret = block; return 0; fail: if (buf) ext2fs_free_mem(&buf); return retval; } errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish, int num, ext2fs_block_bitmap map, blk_t *ret) { blk_t b = start; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!map) map = fs->block_map; if (!map) return EXT2_ET_NO_BLOCK_BITMAP; if (!b) b = fs->super->s_first_data_block; if (!finish) finish = start; if (!num) num = 1; do { if (b+num-1 > fs->super->s_blocks_count) b = fs->super->s_first_data_block; if (ext2fs_fast_test_block_bitmap_range(map, b, num)) { *ret = b; return 0; } b++; } while (b != finish); return EXT2_ET_BLOCK_ALLOC_FAIL; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h0000644000000000000000000000267312263563520021403 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * ext2fsP.h --- private header file for ext2 library * * Copyright (C) 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include "ext2fs.h" /* * Badblocks list */ struct ext2_struct_u32_list { int magic; int num; int size; __u32 *list; int badblocks_flags; }; struct ext2_struct_u32_iterate { int magic; ext2_u32_list bb; int ptr; }; /* * Directory block iterator definition */ struct ext2_struct_dblist { int magic; ext2_filsys fs; ext2_ino_t size; ext2_ino_t count; int sorted; struct ext2_db_entry * list; }; /* * For directory iterators */ struct dir_context { ext2_ino_t dir; int flags; char *buf; int (*func)(ext2_ino_t dir, int entry, struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data); void *priv_data; errcode_t errcode; }; /* * Inode cache structure */ struct ext2_inode_cache { void * buffer; blk_t buffer_blk; int cache_last; int cache_size; int refcount; struct ext2_inode_cache_ent *cache; }; struct ext2_inode_cache_ent { ext2_ino_t ino; struct ext2_inode inode; }; /* Function prototypes */ extern int ext2fs_process_dir_block(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_block, int ref_offset, void *priv_data); busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c0000644000000000000000000002064312263563520022047 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * ismounted.c --- Check to see if the filesystem was mounted * * Copyright (C) 1995,1996,1997,1998,1999,2000 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #ifdef HAVE_LINUX_FD_H #include #endif #ifdef HAVE_MNTENT_H #include #endif #ifdef HAVE_GETMNTINFO #include #include #include #endif /* HAVE_GETMNTINFO */ #include #include #include "ext2_fs.h" #include "ext2fs.h" #ifdef HAVE_MNTENT_H /* * Helper function which checks a file in /etc/mtab format to see if a * filesystem is mounted. Returns an error if the file doesn't exist * or can't be opened. */ static errcode_t check_mntent_file(const char *mtab_file, const char *file, int *mount_flags, char *mtpt, int mtlen) { struct mntent *mnt; struct stat st_buf; errcode_t retval = 0; dev_t file_dev=0, file_rdev=0; ino_t file_ino=0; FILE *f; int fd; *mount_flags = 0; if ((f = setmntent (mtab_file, "r")) == NULL) return errno; if (stat(file, &st_buf) == 0) { if (S_ISBLK(st_buf.st_mode)) { #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ file_rdev = st_buf.st_rdev; #endif /* __GNU__ */ } else { file_dev = st_buf.st_dev; file_ino = st_buf.st_ino; } } while ((mnt = getmntent (f)) != NULL) { if (strcmp(file, mnt->mnt_fsname) == 0) break; if (stat(mnt->mnt_fsname, &st_buf) == 0) { if (S_ISBLK(st_buf.st_mode)) { #ifndef __GNU__ if (file_rdev && (file_rdev == st_buf.st_rdev)) break; #endif /* __GNU__ */ } else { if (file_dev && ((file_dev == st_buf.st_dev) && (file_ino == st_buf.st_ino))) break; } } } if (mnt == 0) { #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ /* * Do an extra check to see if this is the root device. We * can't trust /etc/mtab, and /proc/mounts will only list * /dev/root for the root filesystem. Argh. Instead we * check if the given device has the same major/minor number * as the device that the root directory is on. */ if (file_rdev && stat("/", &st_buf) == 0) { if (st_buf.st_dev == file_rdev) { *mount_flags = EXT2_MF_MOUNTED; if (mtpt) strncpy(mtpt, "/", mtlen); goto is_root; } } #endif /* __GNU__ */ goto errout; } #ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */ /* Validate the entry in case /etc/mtab is out of date */ /* * We need to be paranoid, because some broken distributions * (read: Slackware) don't initialize /etc/mtab before checking * all of the non-root filesystems on the disk. */ if (stat(mnt->mnt_dir, &st_buf) < 0) { retval = errno; if (retval == ENOENT) { #ifdef DEBUG printf("Bogus entry in %s! (%s does not exist)\n", mtab_file, mnt->mnt_dir); #endif /* DEBUG */ retval = 0; } goto errout; } if (file_rdev && (st_buf.st_dev != file_rdev)) { #ifdef DEBUG printf("Bogus entry in %s! (%s not mounted on %s)\n", mtab_file, file, mnt->mnt_dir); #endif /* DEBUG */ goto errout; } #endif /* __GNU__ */ *mount_flags = EXT2_MF_MOUNTED; #ifdef MNTOPT_RO /* Check to see if the ro option is set */ if (hasmntopt(mnt, MNTOPT_RO)) *mount_flags |= EXT2_MF_READONLY; #endif if (mtpt) strncpy(mtpt, mnt->mnt_dir, mtlen); /* * Check to see if we're referring to the root filesystem. * If so, do a manual check to see if we can open /etc/mtab * read/write, since if the root is mounted read/only, the * contents of /etc/mtab may not be accurate. */ if (LONE_CHAR(mnt->mnt_dir, '/')) { is_root: #define TEST_FILE "/.ismount-test-file" *mount_flags |= EXT2_MF_ISROOT; fd = open(TEST_FILE, O_RDWR|O_CREAT); if (fd < 0) { if (errno == EROFS) *mount_flags |= EXT2_MF_READONLY; } else close(fd); (void) unlink(TEST_FILE); } retval = 0; errout: endmntent (f); return retval; } static errcode_t check_mntent(const char *file, int *mount_flags, char *mtpt, int mtlen) { errcode_t retval; #ifdef DEBUG retval = check_mntent_file("/tmp/mtab", file, mount_flags, mtpt, mtlen); if (retval == 0) return 0; #endif /* DEBUG */ #ifdef __linux__ retval = check_mntent_file("/proc/mounts", file, mount_flags, mtpt, mtlen); if (retval == 0 && (*mount_flags != 0)) return 0; #endif /* __linux__ */ #if defined(MOUNTED) || defined(_PATH_MOUNTED) #ifndef MOUNTED #define MOUNTED _PATH_MOUNTED #endif /* MOUNTED */ retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen); return retval; #else *mount_flags = 0; return 0; #endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */ } #else #if defined(HAVE_GETMNTINFO) static errcode_t check_getmntinfo(const char *file, int *mount_flags, char *mtpt, int mtlen) { struct statfs *mp; int len, n; const char *s1; char *s2; n = getmntinfo(&mp, MNT_NOWAIT); if (n == 0) return errno; len = sizeof(_PATH_DEV) - 1; s1 = file; if (strncmp(_PATH_DEV, s1, len) == 0) s1 += len; *mount_flags = 0; while (--n >= 0) { s2 = mp->f_mntfromname; if (strncmp(_PATH_DEV, s2, len) == 0) { s2 += len - 1; *s2 = 'r'; } if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) { *mount_flags = EXT2_MF_MOUNTED; break; } ++mp; } if (mtpt) strncpy(mtpt, mp->f_mntonname, mtlen); return 0; } #endif /* HAVE_GETMNTINFO */ #endif /* HAVE_MNTENT_H */ /* * Check to see if we're dealing with the swap device. */ static int is_swap_device(const char *file) { FILE *f; char buf[1024], *cp; dev_t file_dev; struct stat st_buf; int ret = 0; file_dev = 0; #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ if ((stat(file, &st_buf) == 0) && S_ISBLK(st_buf.st_mode)) file_dev = st_buf.st_rdev; #endif /* __GNU__ */ if (!(f = fopen_for_read("/proc/swaps"))) return 0; /* Skip the first line */ fgets(buf, sizeof(buf), f); while (!feof(f)) { if (!fgets(buf, sizeof(buf), f)) break; if ((cp = strchr(buf, ' ')) != NULL) *cp = 0; if ((cp = strchr(buf, '\t')) != NULL) *cp = 0; if (strcmp(buf, file) == 0) { ret++; break; } #ifndef __GNU__ if (file_dev && (stat(buf, &st_buf) == 0) && S_ISBLK(st_buf.st_mode) && file_dev == st_buf.st_rdev) { ret++; break; } #endif /* __GNU__ */ } fclose(f); return ret; } /* * ext2fs_check_mount_point() returns 1 if the device is mounted, 0 * otherwise. If mtpt is non-NULL, the directory where the device is * mounted is copied to where mtpt is pointing, up to mtlen * characters. */ #ifdef __TURBOC__ # pragma argsused #endif errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags, char *mtpt, int mtlen) { if (is_swap_device(device)) { *mount_flags = EXT2_MF_MOUNTED | EXT2_MF_SWAP; strncpy(mtpt, "", mtlen); return 0; } #ifdef HAVE_MNTENT_H return check_mntent(device, mount_flags, mtpt, mtlen); #else #ifdef HAVE_GETMNTINFO return check_getmntinfo(device, mount_flags, mtpt, mtlen); #else #ifdef __GNUC__ #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!" #endif *mount_flags = 0; return 0; #endif /* HAVE_GETMNTINFO */ #endif /* HAVE_MNTENT_H */ } /* * ext2fs_check_if_mounted() sets the mount_flags EXT2_MF_MOUNTED, * EXT2_MF_READONLY, and EXT2_MF_ROOT * */ errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags) { return ext2fs_check_mount_point(file, mount_flags, NULL, 0); } #ifdef DEBUG int main(int argc, char **argv) { int retval, mount_flags; char mntpt[80]; if (argc < 2) { fprintf(stderr, "Usage: %s device\n", argv[0]); exit(1); } mntpt[0] = 0; retval = ext2fs_check_mount_point(argv[1], &mount_flags, mntpt, sizeof(mntpt)); if (retval) { com_err(argv[0], retval, "while calling ext2fs_check_if_mounted"); exit(1); } printf("Device %s reports flags %02x\n", argv[1], mount_flags); if (mount_flags & EXT2_MF_BUSY) printf("\t%s is apparently in use.\n", argv[1]); if (mount_flags & EXT2_MF_MOUNTED) printf("\t%s is mounted.\n", argv[1]); if (mount_flags & EXT2_MF_SWAP) printf("\t%s is a swap device.\n", argv[1]); if (mount_flags & EXT2_MF_READONLY) printf("\t%s is read-only.\n", argv[1]); if (mount_flags & EXT2_MF_ISROOT) printf("\t%s is the root filesystem.\n", argv[1]); if (mntpt[0]) printf("\t%s is mounted on %s.\n", argv[1], mntpt); exit(0); } #endif /* DEBUG */ busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c0000644000000000000000000001341512263563520021466 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * res_gdt.c --- reserve blocks for growing the group descriptor table * during online resizing. * * Copyright (C) 2002 Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #include #include "ext2_fs.h" #include "ext2fs.h" /* * Iterate through the groups which hold BACKUP superblock/GDT copies in an * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before * calling this for the first time. In a sparse filesystem it will be the * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ... * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ... */ static unsigned int list_backups(ext2_filsys fs, unsigned int *three, unsigned int *five, unsigned int *seven) { unsigned int *min = three; int mult = 3; unsigned int ret; if (!(fs->super->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { ret = *min; *min += 1; return ret; } if (*five < *min) { min = five; mult = 5; } if (*seven < *min) { min = seven; mult = 7; } ret = *min; *min *= mult; return ret; } /* * This code assumes that the reserved blocks have already been marked in-use * during ext2fs_initialize(), so that they are not allocated for other * uses before we can add them to the resize inode (which has to come * after the creation of the inode table). */ errcode_t ext2fs_create_resize_inode(ext2_filsys fs) { errcode_t retval, retval2; struct ext2_super_block *sb; struct ext2_inode inode; __u32 *dindir_buf, *gdt_buf; int rsv_add; unsigned long long apb, inode_size; blk_t dindir_blk, rsv_off, gdt_off, gdt_blk; int dindir_dirty = 0, inode_dirty = 0; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); sb = fs->super; retval = ext2fs_get_mem(2 * fs->blocksize, (void *)&dindir_buf); if (retval) goto out_free; gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize); retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode); if (retval) goto out_free; /* Maximum possible file size (we donly use the dindirect blocks) */ apb = EXT2_ADDR_PER_BLOCK(sb); rsv_add = fs->blocksize / 512; if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) { #ifdef RES_GDT_DEBUG printf("reading GDT dindir %u\n", dindir_blk); #endif retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf); if (retval) goto out_inode; } else { blk_t goal = 3 + sb->s_reserved_gdt_blocks + fs->desc_blocks + fs->inode_blocks_per_group; retval = ext2fs_alloc_block(fs, goal, 0, &dindir_blk); if (retval) goto out_free; inode.i_mode = LINUX_S_IFREG | 0600; inode.i_links_count = 1; inode.i_block[EXT2_DIND_BLOCK] = dindir_blk; inode.i_blocks = rsv_add; memset(dindir_buf, 0, fs->blocksize); #ifdef RES_GDT_DEBUG printf("allocated GDT dindir %u\n", dindir_blk); #endif dindir_dirty = inode_dirty = 1; inode_size = apb*apb + apb + EXT2_NDIR_BLOCKS; inode_size *= fs->blocksize; inode.i_size = inode_size & 0xFFFFFFFF; inode.i_size_high = (inode_size >> 32) & 0xFFFFFFFF; if (inode.i_size_high) { sb->s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_LARGE_FILE; } inode.i_ctime = time(NULL); } for (rsv_off = 0, gdt_off = fs->desc_blocks, gdt_blk = sb->s_first_data_block + 1 + fs->desc_blocks; rsv_off < sb->s_reserved_gdt_blocks; rsv_off++, gdt_off++, gdt_blk++) { unsigned int three = 1, five = 5, seven = 7; unsigned int grp, last = 0; int gdt_dirty = 0; gdt_off %= apb; if (!dindir_buf[gdt_off]) { /* FIXME XXX XXX blk_t new_blk; retval = ext2fs_new_block(fs, gdt_blk, 0, &new_blk); if (retval) goto out_free; if (new_blk != gdt_blk) { // XXX free block retval = -1; // XXX } */ gdt_dirty = dindir_dirty = inode_dirty = 1; memset(gdt_buf, 0, fs->blocksize); dindir_buf[gdt_off] = gdt_blk; inode.i_blocks += rsv_add; #ifdef RES_GDT_DEBUG printf("added primary GDT block %u at %u[%u]\n", gdt_blk, dindir_blk, gdt_off); #endif } else if (dindir_buf[gdt_off] == gdt_blk) { #ifdef RES_GDT_DEBUG printf("reading primary GDT block %u\n", gdt_blk); #endif retval = ext2fs_read_ind_block(fs, gdt_blk, gdt_buf); if (retval) goto out_dindir; } else { #ifdef RES_GDT_DEBUG printf("bad primary GDT %u != %u at %u[%u]\n", dindir_buf[gdt_off], gdt_blk,dindir_blk,gdt_off); #endif retval = EXT2_ET_RESIZE_INODE_CORRUPT; goto out_dindir; } while ((grp = list_backups(fs, &three, &five, &seven)) < fs->group_desc_count) { blk_t expect = gdt_blk + grp * sb->s_blocks_per_group; if (!gdt_buf[last]) { #ifdef RES_GDT_DEBUG printf("added backup GDT %u grp %u@%u[%u]\n", expect, grp, gdt_blk, last); #endif gdt_buf[last] = expect; inode.i_blocks += rsv_add; gdt_dirty = inode_dirty = 1; } else if (gdt_buf[last] != expect) { #ifdef RES_GDT_DEBUG printf("bad backup GDT %u != %u at %u[%u]\n", gdt_buf[last], expect, gdt_blk, last); #endif retval = EXT2_ET_RESIZE_INODE_CORRUPT; goto out_dindir; } last++; } if (gdt_dirty) { #ifdef RES_GDT_DEBUG printf("writing primary GDT block %u\n", gdt_blk); #endif retval = ext2fs_write_ind_block(fs, gdt_blk, gdt_buf); if (retval) goto out_dindir; } } out_dindir: if (dindir_dirty) { retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf); if (!retval) retval = retval2; } out_inode: #ifdef RES_GDT_DEBUG printf("inode.i_blocks = %u, i_size = %u\n", inode.i_blocks, inode.i_size); #endif if (inode_dirty) { inode.i_atime = inode.i_mtime = time(NULL); retval2 = ext2fs_write_inode(fs, EXT2_RESIZE_INO, &inode); if (!retval) retval = retval2; } out_free: ext2fs_free_mem((void *)&dindir_buf); return retval; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/dirhash.c0000644000000000000000000001246412263563520021464 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * dirhash.c -- Calculate the hash of a directory entry * * Copyright (c) 2001 Daniel Phillips * * Copyright (c) 2002 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #include "ext2_fs.h" #include "ext2fs.h" /* * Keyed 32-bit hash function using TEA in a Davis-Meyer function * H0 = Key * Hi = E Mi(Hi-1) + Hi-1 * * (see Applied Cryptography, 2nd edition, p448). * * Jeremy Fitzhardinge 1998 * * This code is made available under the terms of the GPL */ #define DELTA 0x9E3779B9 static void TEA_transform(__u32 buf[4], __u32 const in[]) { __u32 sum = 0; __u32 b0 = buf[0], b1 = buf[1]; __u32 a = in[0], b = in[1], c = in[2], d = in[3]; int n = 16; do { sum += DELTA; b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b); b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d); } while (--n); buf[0] += b0; buf[1] += b1; } /* F, G and H are basic MD4 functions: selection, majority, parity */ #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) #define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) /* * The generic round function. The application is so specific that * we don't bother protecting all the arguments with parens, as is generally * good macro practice, in favor of extra legibility. * Rotation is separate from addition to prevent recomputation */ #define ROUND(f, a, b, c, d, x, s) \ (a += f(b, c, d) + x, a = (a << s) | (a >> (32-s))) #define K1 0 #define K2 013240474631UL #define K3 015666365641UL /* * Basic cut-down MD4 transform. Returns only 32 bits of result. */ static void halfMD4Transform (__u32 buf[4], __u32 const in[]) { __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; /* Round 1 */ ROUND(F, a, b, c, d, in[0] + K1, 3); ROUND(F, d, a, b, c, in[1] + K1, 7); ROUND(F, c, d, a, b, in[2] + K1, 11); ROUND(F, b, c, d, a, in[3] + K1, 19); ROUND(F, a, b, c, d, in[4] + K1, 3); ROUND(F, d, a, b, c, in[5] + K1, 7); ROUND(F, c, d, a, b, in[6] + K1, 11); ROUND(F, b, c, d, a, in[7] + K1, 19); /* Round 2 */ ROUND(G, a, b, c, d, in[1] + K2, 3); ROUND(G, d, a, b, c, in[3] + K2, 5); ROUND(G, c, d, a, b, in[5] + K2, 9); ROUND(G, b, c, d, a, in[7] + K2, 13); ROUND(G, a, b, c, d, in[0] + K2, 3); ROUND(G, d, a, b, c, in[2] + K2, 5); ROUND(G, c, d, a, b, in[4] + K2, 9); ROUND(G, b, c, d, a, in[6] + K2, 13); /* Round 3 */ ROUND(H, a, b, c, d, in[3] + K3, 3); ROUND(H, d, a, b, c, in[7] + K3, 9); ROUND(H, c, d, a, b, in[2] + K3, 11); ROUND(H, b, c, d, a, in[6] + K3, 15); ROUND(H, a, b, c, d, in[1] + K3, 3); ROUND(H, d, a, b, c, in[5] + K3, 9); ROUND(H, c, d, a, b, in[0] + K3, 11); ROUND(H, b, c, d, a, in[4] + K3, 15); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } #undef ROUND #undef F #undef G #undef H #undef K1 #undef K2 #undef K3 /* The old legacy hash */ static ext2_dirhash_t dx_hack_hash (const char *name, int len) { __u32 hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9; while (len--) { __u32 hash = hash1 + (hash0 ^ (*name++ * 7152373)); if (hash & 0x80000000) hash -= 0x7fffffff; hash1 = hash0; hash0 = hash; } return (hash0 << 1); } static void str2hashbuf(const char *msg, int len, __u32 *buf, int num) { __u32 pad, val; int i; pad = (__u32)len | ((__u32)len << 8); pad |= pad << 16; val = pad; if (len > num*4) len = num * 4; for (i=0; i < len; i++) { if ((i % 4) == 0) val = pad; val = msg[i] + (val << 8); if ((i % 4) == 3) { *buf++ = val; val = pad; num--; } } if (--num >= 0) *buf++ = val; while (--num >= 0) *buf++ = pad; } /* * Returns the hash of a filename. If len is 0 and name is NULL, then * this function can be used to test whether or not a hash version is * supported. * * The seed is an 4 longword (32 bits) "secret" which can be used to * uniquify a hash. If the seed is all zero's, then some default seed * may be used. * * A particular hash version specifies whether or not the seed is * represented, and whether or not the returned hash is 32 bits or 64 * bits. 32 bit hashes will return 0 for the minor hash. */ errcode_t ext2fs_dirhash(int version, const char *name, int len, const __u32 *seed, ext2_dirhash_t *ret_hash, ext2_dirhash_t *ret_minor_hash) { __u32 hash; __u32 minor_hash = 0; const char *p; int i; __u32 in[8], buf[4]; /* Initialize the default seed for the hash checksum functions */ buf[0] = 0x67452301; buf[1] = 0xefcdab89; buf[2] = 0x98badcfe; buf[3] = 0x10325476; /* Check to see if the seed is all zero's */ if (seed) { for (i=0; i < 4; i++) { if (seed[i]) break; } if (i < 4) memcpy(buf, seed, sizeof(buf)); } switch (version) { case EXT2_HASH_LEGACY: hash = dx_hack_hash(name, len); break; case EXT2_HASH_HALF_MD4: p = name; while (len > 0) { str2hashbuf(p, len, in, 8); halfMD4Transform(buf, in); len -= 32; p += 32; } minor_hash = buf[2]; hash = buf[1]; break; case EXT2_HASH_TEA: p = name; while (len > 0) { str2hashbuf(p, len, in, 4); TEA_transform(buf, in); len -= 16; p += 16; } hash = buf[0]; minor_hash = buf[1]; break; default: *ret_hash = 0; return EXT2_ET_DIRHASH_UNSUPP; } *ret_hash = hash & ~1; if (ret_minor_hash) *ret_minor_hash = minor_hash; return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/unlink.c0000644000000000000000000000366412263563520021344 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * unlink.c --- delete links in a ext2fs directory * * Copyright (C) 1993, 1994, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct link_struct { const char *name; int namelen; ext2_ino_t inode; int flags; struct ext2_dir_entry *prev; int done; }; #ifdef __TURBOC__ # pragma argsused #endif static int unlink_proc(struct ext2_dir_entry *dirent, int offset EXT2FS_ATTR((unused)), int blocksize EXT2FS_ATTR((unused)), char *buf EXT2FS_ATTR((unused)), void *priv_data) { struct link_struct *ls = (struct link_struct *) priv_data; struct ext2_dir_entry *prev; prev = ls->prev; ls->prev = dirent; if (ls->name) { if ((dirent->name_len & 0xFF) != ls->namelen) return 0; if (strncmp(ls->name, dirent->name, dirent->name_len & 0xFF)) return 0; } if (ls->inode) { if (dirent->inode != ls->inode) return 0; } else { if (!dirent->inode) return 0; } if (prev) prev->rec_len += dirent->rec_len; else dirent->inode = 0; ls->done++; return DIRENT_ABORT|DIRENT_CHANGED; } #ifdef __TURBOC__ #pragma argsused #endif errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, const char *name, ext2_ino_t ino, int flags EXT2FS_ATTR((unused))) { errcode_t retval; struct link_struct ls; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!name && !ino) return EXT2_ET_INVALID_ARGUMENT; if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; ls.name = name; ls.namelen = name ? strlen(name) : 0; ls.inode = ino; ls.flags = 0; ls.done = 0; ls.prev = 0; retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY, 0, unlink_proc, &ls); if (retval) return retval; return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/ext2fs/dblist.c0000644000000000000000000001273012263563520021317 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * dblist.c -- directory block list functions * * Copyright 1997 by Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% * */ #include #if HAVE_UNISTD_H #include #endif #include #include #include "ext2_fs.h" #include "ext2fsP.h" static int dir_block_cmp(const void *a, const void *b); /* * Returns the number of directories in the filesystem as reported by * the group descriptors. Of course, the group descriptors could be * wrong! */ errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs) { dgrp_t i; ext2_ino_t num_dirs, max_dirs; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); num_dirs = 0; max_dirs = fs->super->s_inodes_per_group; for (i = 0; i < fs->group_desc_count; i++) { if (fs->group_desc[i].bg_used_dirs_count > max_dirs) num_dirs += max_dirs / 8; else num_dirs += fs->group_desc[i].bg_used_dirs_count; } if (num_dirs > fs->super->s_inodes_count) num_dirs = fs->super->s_inodes_count; *ret_num_dirs = num_dirs; return 0; } /* * helper function for making a new directory block list (for * initialize and copy). */ static errcode_t make_dblist(ext2_filsys fs, ext2_ino_t size, ext2_ino_t count, struct ext2_db_entry *list, ext2_dblist *ret_dblist) { ext2_dblist dblist; errcode_t retval; size_t len; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if ((ret_dblist == 0) && fs->dblist && (fs->dblist->magic == EXT2_ET_MAGIC_DBLIST)) return 0; retval = ext2fs_get_mem(sizeof(struct ext2_struct_dblist), &dblist); if (retval) return retval; memset(dblist, 0, sizeof(struct ext2_struct_dblist)); dblist->magic = EXT2_ET_MAGIC_DBLIST; dblist->fs = fs; if (size) dblist->size = size; else { retval = ext2fs_get_num_dirs(fs, &dblist->size); if (retval) goto cleanup; dblist->size = (dblist->size * 2) + 12; } len = (size_t) sizeof(struct ext2_db_entry) * dblist->size; dblist->count = count; retval = ext2fs_get_mem(len, &dblist->list); if (retval) goto cleanup; if (list) memcpy(dblist->list, list, len); else memset(dblist->list, 0, len); if (ret_dblist) *ret_dblist = dblist; else fs->dblist = dblist; return 0; cleanup: ext2fs_free_mem(&dblist); return retval; } /* * Initialize a directory block list */ errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist) { ext2_dblist dblist; errcode_t retval; retval = make_dblist(fs, 0, 0, 0, &dblist); if (retval) return retval; dblist->sorted = 1; if (ret_dblist) *ret_dblist = dblist; else fs->dblist = dblist; return 0; } /* * Copy a directory block list */ errcode_t ext2fs_copy_dblist(ext2_dblist src, ext2_dblist *dest) { ext2_dblist dblist; errcode_t retval; retval = make_dblist(src->fs, src->size, src->count, src->list, &dblist); if (retval) return retval; dblist->sorted = src->sorted; *dest = dblist; return 0; } /* * Close a directory block list * * (moved to closefs.c) */ /* * Add a directory block to the directory block list */ errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, int blockcnt) { struct ext2_db_entry *new_entry; errcode_t retval; unsigned long old_size; EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); if (dblist->count >= dblist->size) { old_size = dblist->size * sizeof(struct ext2_db_entry); dblist->size += 100; retval = ext2fs_resize_mem(old_size, (size_t) dblist->size * sizeof(struct ext2_db_entry), &dblist->list); if (retval) { dblist->size -= 100; return retval; } } new_entry = dblist->list + ( (int) dblist->count++); new_entry->blk = blk; new_entry->ino = ino; new_entry->blockcnt = blockcnt; dblist->sorted = 0; return 0; } /* * Change the directory block to the directory block list */ errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, int blockcnt) { dgrp_t i; EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); for (i=0; i < dblist->count; i++) { if ((dblist->list[i].ino != ino) || (dblist->list[i].blockcnt != blockcnt)) continue; dblist->list[i].blk = blk; dblist->sorted = 0; return 0; } return EXT2_ET_DB_NOT_FOUND; } void ext2fs_dblist_sort(ext2_dblist dblist, int (*sortfunc)(const void *, const void *)) { if (!sortfunc) sortfunc = dir_block_cmp; qsort(dblist->list, (size_t) dblist->count, sizeof(struct ext2_db_entry), sortfunc); dblist->sorted = 1; } /* * This function iterates over the directory block list */ errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, int (*func)(ext2_filsys fs, struct ext2_db_entry *db_info, void *priv_data), void *priv_data) { ext2_ino_t i; int ret; EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); if (!dblist->sorted) ext2fs_dblist_sort(dblist, 0); for (i=0; i < dblist->count; i++) { ret = (*func)(dblist->fs, &dblist->list[(int)i], priv_data); if (ret & DBLIST_ABORT) return 0; } return 0; } static int dir_block_cmp(const void *a, const void *b) { const struct ext2_db_entry *db_a = (const struct ext2_db_entry *) a; const struct ext2_db_entry *db_b = (const struct ext2_db_entry *) b; if (db_a->blk != db_b->blk) return (int) (db_a->blk - db_b->blk); if (db_a->ino != db_b->ino) return (int) (db_a->ino - db_b->ino); return (int) (db_a->blockcnt - db_b->blockcnt); } int ext2fs_dblist_count(ext2_dblist dblist) { return (int) dblist->count; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/uuid/0000755000000000000000000000000012320365361017417 5ustar rootrootbusybox-1.22.1/e2fsprogs/old_e2fsprogs/uuid/unparse.c0000644000000000000000000000476712263563520021261 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * unparse.c -- convert a UUID to string * * Copyright (C) 1996, 1997 Theodore Ts'o. * * %Begin-Header% * 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, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 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. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * %End-Header% */ #include #include "uuidP.h" static const char *fmt_lower = "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"; static const char *fmt_upper = "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"; #ifdef UUID_UNPARSE_DEFAULT_UPPER #define FMT_DEFAULT fmt_upper #else #define FMT_DEFAULT fmt_lower #endif static void uuid_unparse_x(const uuid_t uu, char *out, const char *fmt) { struct uuid uuid; uuid_unpack(uu, &uuid); sprintf(out, fmt, uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, uuid.node[0], uuid.node[1], uuid.node[2], uuid.node[3], uuid.node[4], uuid.node[5]); } void uuid_unparse_lower(const uuid_t uu, char *out) { uuid_unparse_x(uu, out, fmt_lower); } void uuid_unparse_upper(const uuid_t uu, char *out) { uuid_unparse_x(uu, out, fmt_upper); } void uuid_unparse(const uuid_t uu, char *out) { uuid_unparse_x(uu, out, FMT_DEFAULT); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/uuid/compare.c0000644000000000000000000000422012263563520021212 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * compare.c --- compare whether or not two UUID's are the same * * Returns 0 if the two UUID's are different, and 1 if they are the same. * * Copyright (C) 1996, 1997 Theodore Ts'o. * * %Begin-Header% * 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, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 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. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * %End-Header% */ #include "uuidP.h" #include #define UUCMP(u1,u2) if (u1 != u2) return (u1 < u2) ? -1 : 1; int uuid_compare(const uuid_t uu1, const uuid_t uu2) { struct uuid uuid1, uuid2; uuid_unpack(uu1, &uuid1); uuid_unpack(uu2, &uuid2); UUCMP(uuid1.time_low, uuid2.time_low); UUCMP(uuid1.time_mid, uuid2.time_mid); UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version); UUCMP(uuid1.clock_seq, uuid2.clock_seq); return memcmp(uuid1.node, uuid2.node, 6); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/uuid/parse.c0000644000000000000000000000461512263563520020706 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * parse.c --- UUID parsing * * Copyright (C) 1996, 1997 Theodore Ts'o. * * %Begin-Header% * 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, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 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. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * %End-Header% */ #include #include #include #include #include "uuidP.h" int uuid_parse(const char *in, uuid_t uu) { struct uuid uuid; int i; const char *cp; char buf[3]; if (strlen(in) != 36) return -1; for (i=0, cp = in; i <= 36; i++,cp++) { if ((i == 8) || (i == 13) || (i == 18) || (i == 23)) { if (*cp == '-') continue; else return -1; } if (i== 36) if (*cp == 0) continue; if (!isxdigit(*cp)) return -1; } uuid.time_low = strtoul(in, NULL, 16); uuid.time_mid = strtoul(in+9, NULL, 16); uuid.time_hi_and_version = strtoul(in+14, NULL, 16); uuid.clock_seq = strtoul(in+19, NULL, 16); cp = in+24; buf[2] = 0; for (i=0; i < 6; i++) { buf[0] = *cp++; buf[1] = *cp++; uuid.node[i] = strtoul(buf, NULL, 16); } uuid_pack(&uuid, uu); return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/uuid/Kbuild.src0000644000000000000000000000061012263563520021342 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. NEEDED-$(CONFIG_E2FSCK) = y NEEDED-$(CONFIG_FSCK) = y NEEDED-$(CONFIG_MKE2FS) = y NEEDED-$(CONFIG_TUNE2FS) = y lib-y:= INSERT lib-$(NEEDED-y) += compare.o gen_uuid.o pack.o parse.o unpack.o unparse.o \ uuid_time.o busybox-1.22.1/e2fsprogs/old_e2fsprogs/uuid/uuid_time.c0000644000000000000000000001017112263563520021552 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * uuid_time.c --- Interpret the time field from a uuid. This program * violates the UUID abstraction barrier by reaching into the guts * of a UUID and interpreting it. * * Copyright (C) 1998, 1999 Theodore Ts'o. * * %Begin-Header% * 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, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 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. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * %End-Header% */ #include #include #include #include #include #include "uuidP.h" time_t uuid_time(const uuid_t uu, struct timeval *ret_tv) { struct uuid uuid; uint32_t high; struct timeval tv; unsigned long long clock_reg; uuid_unpack(uu, &uuid); high = uuid.time_mid | ((uuid.time_hi_and_version & 0xFFF) << 16); clock_reg = uuid.time_low | ((unsigned long long) high << 32); clock_reg -= (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000; tv.tv_sec = clock_reg / 10000000; tv.tv_usec = (clock_reg % 10000000) / 10; if (ret_tv) *ret_tv = tv; return tv.tv_sec; } int uuid_type(const uuid_t uu) { struct uuid uuid; uuid_unpack(uu, &uuid); return ((uuid.time_hi_and_version >> 12) & 0xF); } int uuid_variant(const uuid_t uu) { struct uuid uuid; int var; uuid_unpack(uu, &uuid); var = uuid.clock_seq; if ((var & 0x8000) == 0) return UUID_VARIANT_NCS; if ((var & 0x4000) == 0) return UUID_VARIANT_DCE; if ((var & 0x2000) == 0) return UUID_VARIANT_MICROSOFT; return UUID_VARIANT_OTHER; } #ifdef DEBUG static const char *variant_string(int variant) { switch (variant) { case UUID_VARIANT_NCS: return "NCS"; case UUID_VARIANT_DCE: return "DCE"; case UUID_VARIANT_MICROSOFT: return "Microsoft"; default: return "Other"; } } int main(int argc, char **argv) { uuid_t buf; time_t time_reg; struct timeval tv; int type, variant; if (argc != 2) { fprintf(stderr, "Usage: %s uuid\n", argv[0]); exit(1); } if (uuid_parse(argv[1], buf)) { fprintf(stderr, "Invalid UUID: %s\n", argv[1]); exit(1); } variant = uuid_variant(buf); type = uuid_type(buf); time_reg = uuid_time(buf, &tv); printf("UUID variant is %d (%s)\n", variant, variant_string(variant)); if (variant != UUID_VARIANT_DCE) { printf("Warning: This program only knows how to interpret " "DCE UUIDs.\n\tThe rest of the output is likely " "to be incorrect!!\n"); } printf("UUID type is %d", type); switch (type) { case 1: printf(" (time based)\n"); break; case 2: printf(" (DCE)\n"); break; case 3: printf(" (name-based)\n"); break; case 4: printf(" (random)\n"); break; default: bb_putchar('\n'); } if (type != 1) { printf("Warning: not a time-based UUID, so UUID time " "decoding will likely not work!\n"); } printf("UUID time is: (%ld, %ld): %s\n", tv.tv_sec, tv.tv_usec, ctime(&time_reg)); return 0; } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/uuid/uuid.h0000644000000000000000000000654512263563520020553 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Public include file for the UUID library * * Copyright (C) 1996, 1997, 1998 Theodore Ts'o. * * %Begin-Header% * 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, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 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. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * %End-Header% */ #ifndef UUID_UUID_H #define UUID_UUID_H 1 #include #include typedef unsigned char uuid_t[16]; /* UUID Variant definitions */ #define UUID_VARIANT_NCS 0 #define UUID_VARIANT_DCE 1 #define UUID_VARIANT_MICROSOFT 2 #define UUID_VARIANT_OTHER 3 /* UUID Type definitions */ #define UUID_TYPE_DCE_TIME 1 #define UUID_TYPE_DCE_RANDOM 4 /* Allow UUID constants to be defined */ #ifdef __GNUC__ #define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ static const uuid_t name UNUSED_PARAM = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} #else #define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ static const uuid_t name = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} #endif #ifdef __cplusplus extern "C" { #endif /* clear.c */ /*void uuid_clear(uuid_t uu);*/ #define uuid_clear(uu) memset(uu, 0, sizeof(uu)) /* compare.c */ int uuid_compare(const uuid_t uu1, const uuid_t uu2); /* copy.c */ /*void uuid_copy(uuid_t dst, const uuid_t src);*/ #define uuid_copy(dst,src) memcpy(dst, src, sizeof(dst)) /* gen_uuid.c */ void uuid_generate(uuid_t out); void uuid_generate_random(uuid_t out); void uuid_generate_time(uuid_t out); /* isnull.c */ /*int uuid_is_null(const uuid_t uu);*/ #define uuid_is_null(uu) (!memcmp(uu, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", sizeof(uu))) /* parse.c */ int uuid_parse(const char *in, uuid_t uu); /* unparse.c */ void uuid_unparse(const uuid_t uu, char *out); void uuid_unparse_lower(const uuid_t uu, char *out); void uuid_unparse_upper(const uuid_t uu, char *out); /* uuid_time.c */ time_t uuid_time(const uuid_t uu, struct timeval *ret_tv); int uuid_type(const uuid_t uu); int uuid_variant(const uuid_t uu); #ifdef __cplusplus } #endif #endif /* _UUID_UUID_H */ busybox-1.22.1/e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c0000644000000000000000000001710512263563520021371 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * gen_uuid.c --- generate a DCE-compatible uuid * * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o. * * %Begin-Header% * 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, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 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. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * %End-Header% */ #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_IOCTL_H #include #endif #include #ifdef HAVE_SYS_SOCKIO_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NET_IF_DL_H #include #endif #include "uuidP.h" #ifdef HAVE_SRANDOM #define srand(x) srandom(x) #define rand() random() #endif static int get_random_fd(void) { struct timeval tv; static int fd = -2; int i; if (fd == -2) { gettimeofday(&tv, 0); fd = open("/dev/urandom", O_RDONLY); if (fd == -1) fd = open("/dev/random", O_RDONLY | O_NONBLOCK); srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec); } /* Crank the random number generator a few times */ gettimeofday(&tv, 0); for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--) rand(); return fd; } /* * Generate a series of random bytes. Use /dev/urandom if possible, * and if not, use srandom/random. */ static void get_random_bytes(void *buf, int nbytes) { int i, n = nbytes, fd = get_random_fd(); int lose_counter = 0; unsigned char *cp = (unsigned char *) buf; if (fd >= 0) { while (n > 0) { i = read(fd, cp, n); if (i <= 0) { if (lose_counter++ > 16) break; continue; } n -= i; cp += i; lose_counter = 0; } } /* * We do this all the time, but this is the only source of * randomness if /dev/random/urandom is out to lunch. */ for (cp = buf, i = 0; i < nbytes; i++) *cp++ ^= (rand() >> 7) & 0xFF; } /* * Get the ethernet hardware address, if we can find it... */ static int get_node_id(unsigned char *node_id) { #ifdef HAVE_NET_IF_H int sd; struct ifreq ifr, *ifrp; struct ifconf ifc; char buf[1024]; int n, i; unsigned char *a; #ifdef HAVE_NET_IF_DL_H struct sockaddr_dl *sdlp; #endif /* * BSD 4.4 defines the size of an ifreq to be * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len * However, under earlier systems, sa_len isn't present, so the size is * just sizeof(struct ifreq) */ #ifdef HAVE_SA_LEN #ifndef max #define max(a,b) ((a) > (b) ? (a) : (b)) #endif #define ifreq_size(i) max(sizeof(struct ifreq),\ sizeof((i).ifr_name)+(i).ifr_addr.sa_len) #else #define ifreq_size(i) sizeof(struct ifreq) #endif /* HAVE_SA_LEN*/ sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (sd < 0) { return -1; } memset(buf, 0, sizeof(buf)); ifc.ifc_len = sizeof(buf); ifc.ifc_buf = buf; if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) { close(sd); return -1; } n = ifc.ifc_len; for (i = 0; i < n; i+= ifreq_size(*ifrp) ) { ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i); strncpy_IFNAMSIZ(ifr.ifr_name, ifrp->ifr_name); #ifdef SIOCGIFHWADDR if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) continue; a = (unsigned char *) &ifr.ifr_hwaddr.sa_data; #else #ifdef SIOCGENADDR if (ioctl(sd, SIOCGENADDR, &ifr) < 0) continue; a = (unsigned char *) ifr.ifr_enaddr; #else #ifdef HAVE_NET_IF_DL_H sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr; if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6)) continue; a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen]; #else /* * XXX we don't have a way of getting the hardware * address */ close(sd); return 0; #endif /* HAVE_NET_IF_DL_H */ #endif /* SIOCGENADDR */ #endif /* SIOCGIFHWADDR */ if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) continue; if (node_id) { memcpy(node_id, a, 6); close(sd); return 1; } } close(sd); #endif return 0; } /* Assume that the gettimeofday() has microsecond granularity */ #define MAX_ADJUSTMENT 10 static int get_clock(uint32_t *clock_high, uint32_t *clock_low, uint16_t *ret_clock_seq) { static int adjustment = 0; static struct timeval last = {0, 0}; static uint16_t clock_seq; struct timeval tv; unsigned long long clock_reg; try_again: gettimeofday(&tv, 0); if ((last.tv_sec == 0) && (last.tv_usec == 0)) { get_random_bytes(&clock_seq, sizeof(clock_seq)); clock_seq &= 0x3FFF; last = tv; last.tv_sec--; } if ((tv.tv_sec < last.tv_sec) || ((tv.tv_sec == last.tv_sec) && (tv.tv_usec < last.tv_usec))) { clock_seq = (clock_seq+1) & 0x3FFF; adjustment = 0; last = tv; } else if ((tv.tv_sec == last.tv_sec) && (tv.tv_usec == last.tv_usec)) { if (adjustment >= MAX_ADJUSTMENT) goto try_again; adjustment++; } else { adjustment = 0; last = tv; } clock_reg = tv.tv_usec*10 + adjustment; clock_reg += ((unsigned long long) tv.tv_sec)*10000000; clock_reg += (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000; *clock_high = clock_reg >> 32; *clock_low = clock_reg; *ret_clock_seq = clock_seq; return 0; } void uuid_generate_time(uuid_t out) { static unsigned char node_id[6]; static int has_init = 0; struct uuid uu; uint32_t clock_mid; if (!has_init) { if (get_node_id(node_id) <= 0) { get_random_bytes(node_id, 6); /* * Set multicast bit, to prevent conflicts * with IEEE 802 addresses obtained from * network cards */ node_id[0] |= 0x01; } has_init = 1; } get_clock(&clock_mid, &uu.time_low, &uu.clock_seq); uu.clock_seq |= 0x8000; uu.time_mid = (uint16_t) clock_mid; uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000; memcpy(uu.node, node_id, 6); uuid_pack(&uu, out); } void uuid_generate_random(uuid_t out) { uuid_t buf; struct uuid uu; get_random_bytes(buf, sizeof(buf)); uuid_unpack(buf, &uu); uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000; uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000; uuid_pack(&uu, out); } /* * This is the generic front-end to uuid_generate_random and * uuid_generate_time. It uses uuid_generate_random only if * /dev/urandom is available, since otherwise we won't have * high-quality randomness. */ void uuid_generate(uuid_t out) { if (get_random_fd() >= 0) uuid_generate_random(out); else uuid_generate_time(out); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/uuid/uuidP.h0000644000000000000000000000402112263563520020656 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * uuid.h -- private header file for uuids * * Copyright (C) 1996, 1997 Theodore Ts'o. * * %Begin-Header% * 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, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 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. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * %End-Header% */ #include #include #include "uuid.h" /* * Offset between 15-Oct-1582 and 1-Jan-70 */ #define TIME_OFFSET_HIGH 0x01B21DD2 #define TIME_OFFSET_LOW 0x13814000 struct uuid { uint32_t time_low; uint16_t time_mid; uint16_t time_hi_and_version; uint16_t clock_seq; uint8_t node[6]; }; /* * prototypes */ void uuid_pack(const struct uuid *uu, uuid_t ptr); void uuid_unpack(const uuid_t in, struct uuid *uu); busybox-1.22.1/e2fsprogs/old_e2fsprogs/uuid/unpack.c0000644000000000000000000000410012263563520021042 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Internal routine for unpacking UUID * * Copyright (C) 1996, 1997 Theodore Ts'o. * * %Begin-Header% * 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, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 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. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * %End-Header% */ #include #include "uuidP.h" void uuid_unpack(const uuid_t in, struct uuid *uu) { const uint8_t *ptr = in; uint32_t tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; tmp = (tmp << 8) | *ptr++; tmp = (tmp << 8) | *ptr++; uu->time_low = tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; uu->time_mid = tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; uu->time_hi_and_version = tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; uu->clock_seq = tmp; memcpy(uu->node, ptr, 6); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/uuid/pack.c0000644000000000000000000000433512263563520020511 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Internal routine for packing UUID's * * Copyright (C) 1996, 1997 Theodore Ts'o. * * %Begin-Header% * 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, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 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. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * %End-Header% */ #include #include "uuidP.h" void uuid_pack(const struct uuid *uu, uuid_t ptr) { uint32_t tmp; unsigned char *out = ptr; tmp = uu->time_low; out[3] = (unsigned char) tmp; tmp >>= 8; out[2] = (unsigned char) tmp; tmp >>= 8; out[1] = (unsigned char) tmp; tmp >>= 8; out[0] = (unsigned char) tmp; tmp = uu->time_mid; out[5] = (unsigned char) tmp; tmp >>= 8; out[4] = (unsigned char) tmp; tmp = uu->time_hi_and_version; out[7] = (unsigned char) tmp; tmp >>= 8; out[6] = (unsigned char) tmp; tmp = uu->clock_seq; out[9] = (unsigned char) tmp; tmp >>= 8; out[8] = (unsigned char) tmp; memcpy(out+10, uu->node, 6); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2fsck.h0000644000000000000000000007607512263563520020021 0ustar rootroot/* vi: set sw=4 ts=4: */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ext2fs/kernel-list.h" #include #include /* * Now pull in the real linux/jfs.h definitions. */ #include "ext2fs/kernel-jbd.h" #include "fsck.h" #include "ext2fs/ext2_fs.h" #include "blkid/blkid.h" #include "ext2fs/ext2_ext_attr.h" #include "uuid/uuid.h" #include "libbb.h" #ifdef HAVE_CONIO_H #undef HAVE_TERMIOS_H #include #define read_a_char() getch() #else #ifdef HAVE_TERMIOS_H #include #endif #endif /* * The last ext2fs revision level that this version of e2fsck is able to * support */ #define E2FSCK_CURRENT_REV 1 /* Used by the region allocation code */ typedef __u32 region_addr_t; typedef struct region_struct *region_t; struct dx_dirblock_info { int type; blk_t phys; int flags; blk_t parent; ext2_dirhash_t min_hash; ext2_dirhash_t max_hash; ext2_dirhash_t node_min_hash; ext2_dirhash_t node_max_hash; }; /* These defines are used in the type field of dx_dirblock_info */ #define DX_DIRBLOCK_ROOT 1 #define DX_DIRBLOCK_LEAF 2 #define DX_DIRBLOCK_NODE 3 /* The following defines are used in the 'flags' field of a dx_dirblock_info */ #define DX_FLAG_REFERENCED 1 #define DX_FLAG_DUP_REF 2 #define DX_FLAG_FIRST 4 #define DX_FLAG_LAST 8 /* * E2fsck options */ #define E2F_OPT_READONLY 0x0001 #define E2F_OPT_PREEN 0x0002 #define E2F_OPT_YES 0x0004 #define E2F_OPT_NO 0x0008 #define E2F_OPT_TIME 0x0010 #define E2F_OPT_CHECKBLOCKS 0x0040 #define E2F_OPT_DEBUG 0x0080 #define E2F_OPT_FORCE 0x0100 #define E2F_OPT_WRITECHECK 0x0200 #define E2F_OPT_COMPRESS_DIRS 0x0400 /* * E2fsck flags */ #define E2F_FLAG_ABORT 0x0001 /* Abort signaled */ #define E2F_FLAG_CANCEL 0x0002 /* Cancel signaled */ #define E2F_FLAG_SIGNAL_MASK 0x0003 #define E2F_FLAG_RESTART 0x0004 /* Restart signaled */ #define E2F_FLAG_SETJMP_OK 0x0010 /* Setjmp valid for abort */ #define E2F_FLAG_PROG_BAR 0x0020 /* Progress bar on screen */ #define E2F_FLAG_PROG_SUPPRESS 0x0040 /* Progress suspended */ #define E2F_FLAG_JOURNAL_INODE 0x0080 /* Create a new ext3 journal inode */ #define E2F_FLAG_SB_SPECIFIED 0x0100 /* The superblock was explicitly * specified by the user */ #define E2F_FLAG_RESTARTED 0x0200 /* E2fsck has been restarted */ #define E2F_FLAG_RESIZE_INODE 0x0400 /* Request to recreate resize inode */ /*Don't know where these come from*/ #define READ 0 #define WRITE 1 #define cpu_to_be32(n) htonl(n) #define be32_to_cpu(n) ntohl(n) /* * We define a set of "latch groups"; these are problems which are * handled as a set. The user answers once for a particular latch * group. */ #define PR_LATCH_MASK 0x0ff0 /* Latch mask */ #define PR_LATCH_BLOCK 0x0010 /* Latch for illegal blocks (pass 1) */ #define PR_LATCH_BBLOCK 0x0020 /* Latch for bad block inode blocks (pass 1) */ #define PR_LATCH_IBITMAP 0x0030 /* Latch for pass 5 inode bitmap proc. */ #define PR_LATCH_BBITMAP 0x0040 /* Latch for pass 5 inode bitmap proc. */ #define PR_LATCH_RELOC 0x0050 /* Latch for superblock relocate hint */ #define PR_LATCH_DBLOCK 0x0060 /* Latch for pass 1b dup block headers */ #define PR_LATCH_LOW_DTIME 0x0070 /* Latch for pass1 orphaned list refugees */ #define PR_LATCH_TOOBIG 0x0080 /* Latch for file to big errors */ #define PR_LATCH_OPTIMIZE_DIR 0x0090 /* Latch for optimize directories */ #define PR_LATCH(x) ((((x) & PR_LATCH_MASK) >> 4) - 1) /* * Latch group descriptor flags */ #define PRL_YES 0x0001 /* Answer yes */ #define PRL_NO 0x0002 /* Answer no */ #define PRL_LATCHED 0x0004 /* The latch group is latched */ #define PRL_SUPPRESS 0x0008 /* Suppress all latch group questions */ #define PRL_VARIABLE 0x000f /* All the flags that need to be reset */ /* * Pre-Pass 1 errors */ #define PR_0_BB_NOT_GROUP 0x000001 /* Block bitmap not in group */ #define PR_0_IB_NOT_GROUP 0x000002 /* Inode bitmap not in group */ #define PR_0_ITABLE_NOT_GROUP 0x000003 /* Inode table not in group */ #define PR_0_SB_CORRUPT 0x000004 /* Superblock corrupt */ #define PR_0_FS_SIZE_WRONG 0x000005 /* Filesystem size is wrong */ #define PR_0_NO_FRAGMENTS 0x000006 /* Fragments not supported */ #define PR_0_BLOCKS_PER_GROUP 0x000007 /* Bad blocks_per_group */ #define PR_0_FIRST_DATA_BLOCK 0x000008 /* Bad first_data_block */ #define PR_0_ADD_UUID 0x000009 /* Adding UUID to filesystem */ #define PR_0_RELOCATE_HINT 0x00000A /* Relocate hint */ #define PR_0_MISC_CORRUPT_SUPER 0x00000B /* Miscellaneous superblock corruption */ #define PR_0_GETSIZE_ERROR 0x00000C /* Error determing physical device size of filesystem */ #define PR_0_INODE_COUNT_WRONG 0x00000D /* Inode count in the superblock incorrect */ #define PR_0_HURD_CLEAR_FILETYPE 0x00000E /* The Hurd does not support the filetype feature */ #define PR_0_JOURNAL_BAD_INODE 0x00000F /* The Hurd does not support the filetype feature */ #define PR_0_JOURNAL_UNSUPP_MULTIFS 0x000010 /* The external journal has multiple filesystems (which we can't handle yet) */ #define PR_0_CANT_FIND_JOURNAL 0x000011 /* Can't find external journal */ #define PR_0_EXT_JOURNAL_BAD_SUPER 0x000012/* External journal has bad superblock */ #define PR_0_JOURNAL_BAD_UUID 0x000013 /* Superblock has a bad journal UUID */ #define PR_0_JOURNAL_UNSUPP_SUPER 0x000014 /* Journal has an unknown superblock type */ #define PR_0_JOURNAL_BAD_SUPER 0x000015 /* Journal superblock is corrupt */ #define PR_0_JOURNAL_HAS_JOURNAL 0x000016 /* Journal superblock is corrupt */ #define PR_0_JOURNAL_RECOVER_SET 0x000017 /* Superblock has recovery flag set but no journal */ #define PR_0_JOURNAL_RECOVERY_CLEAR 0x000018 /* Journal has data, but recovery flag is clear */ #define PR_0_JOURNAL_RESET_JOURNAL 0x000019 /* Ask if we should clear the journal */ #define PR_0_FS_REV_LEVEL 0x00001A /* Filesystem revision is 0, but feature flags are set */ #define PR_0_ORPHAN_CLEAR_INODE 0x000020 /* Clearing orphan inode */ #define PR_0_ORPHAN_ILLEGAL_BLOCK_NUM 0x000021 /* Illegal block found in orphaned inode */ #define PR_0_ORPHAN_ALREADY_CLEARED_BLOCK 0x000022 /* Already cleared block found in orphaned inode */ #define PR_0_ORPHAN_ILLEGAL_HEAD_INODE 0x000023 /* Illegal orphan inode in superblock */ #define PR_0_ORPHAN_ILLEGAL_INODE 0x000024 /* Illegal inode in orphaned inode list */ #define PR_0_JOURNAL_UNSUPP_ROCOMPAT 0x000025 /* Journal has unsupported read-only feature - abort */ #define PR_0_JOURNAL_UNSUPP_INCOMPAT 0x000026 /* Journal has unsupported incompatible feature - abort */ #define PR_0_JOURNAL_UNSUPP_VERSION 0x000027 /* Journal has unsupported version number */ #define PR_0_MOVE_JOURNAL 0x000028 /* Moving journal to hidden file */ #define PR_0_ERR_MOVE_JOURNAL 0x000029 /* Error moving journal */ #define PR_0_CLEAR_V2_JOURNAL 0x00002A /* Clearing V2 journal superblock */ #define PR_0_JOURNAL_RUN 0x00002B /* Run journal anyway */ #define PR_0_JOURNAL_RUN_DEFAULT 0x00002C /* Run journal anyway by default */ #define PR_0_BACKUP_JNL 0x00002D /* Backup journal inode blocks */ #define PR_0_NONZERO_RESERVED_GDT_BLOCKS 0x00002E /* Reserved blocks w/o resize_inode */ #define PR_0_CLEAR_RESIZE_INODE 0x00002F /* Resize_inode not enabled, but resize inode is non-zero */ #define PR_0_RESIZE_INODE_INVALID 0x000030 /* Resize inode invalid */ /* * Pass 1 errors */ #define PR_1_PASS_HEADER 0x010000 /* Pass 1: Checking inodes, blocks, and sizes */ #define PR_1_ROOT_NO_DIR 0x010001 /* Root directory is not an inode */ #define PR_1_ROOT_DTIME 0x010002 /* Root directory has dtime set */ #define PR_1_RESERVED_BAD_MODE 0x010003 /* Reserved inode has bad mode */ #define PR_1_ZERO_DTIME 0x010004 /* Deleted inode has zero dtime */ #define PR_1_SET_DTIME 0x010005 /* Inode in use, but dtime set */ #define PR_1_ZERO_LENGTH_DIR 0x010006 /* Zero-length directory */ #define PR_1_BB_CONFLICT 0x010007 /* Block bitmap conflicts with some other fs block */ #define PR_1_IB_CONFLICT 0x010008 /* Inode bitmap conflicts with some other fs block */ #define PR_1_ITABLE_CONFLICT 0x010009 /* Inode table conflicts with some other fs block */ #define PR_1_BB_BAD_BLOCK 0x01000A /* Block bitmap is on a bad block */ #define PR_1_IB_BAD_BLOCK 0x01000B /* Inode bitmap is on a bad block */ #define PR_1_BAD_I_SIZE 0x01000C /* Inode has incorrect i_size */ #define PR_1_BAD_I_BLOCKS 0x01000D /* Inode has incorrect i_blocks */ #define PR_1_ILLEGAL_BLOCK_NUM 0x01000E /* Illegal block number in inode */ #define PR_1_BLOCK_OVERLAPS_METADATA 0x01000F /* Block number overlaps fs metadata */ #define PR_1_INODE_BLOCK_LATCH 0x010010 /* Inode has illegal blocks (latch question) */ #define PR_1_TOO_MANY_BAD_BLOCKS 0x010011 /* Too many bad blocks in inode */ #define PR_1_BB_ILLEGAL_BLOCK_NUM 0x010012 /* Illegal block number in bad block inode */ #define PR_1_INODE_BBLOCK_LATCH 0x010013 /* Bad block inode has illegal blocks (latch question) */ #define PR_1_DUP_BLOCKS_PREENSTOP 0x010014 /* Duplicate or bad blocks in use! */ #define PR_1_BBINODE_BAD_METABLOCK 0x010015 /* Bad block used as bad block indirect block */ #define PR_1_BBINODE_BAD_METABLOCK_PROMPT 0x010016 /* Inconsistency can't be fixed prompt */ #define PR_1_BAD_PRIMARY_BLOCK 0x010017 /* Bad primary block */ #define PR_1_BAD_PRIMARY_BLOCK_PROMPT 0x010018 /* Bad primary block prompt */ #define PR_1_BAD_PRIMARY_SUPERBLOCK 0x010019 /* Bad primary superblock */ #define PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR 0x01001A /* Bad primary block group descriptors */ #define PR_1_BAD_SUPERBLOCK 0x01001B /* Bad superblock in group */ #define PR_1_BAD_GROUP_DESCRIPTORS 0x01001C /* Bad block group descriptors in group */ #define PR_1_PROGERR_CLAIMED_BLOCK 0x01001D /* Block claimed for no reason */ #define PR_1_RELOC_BLOCK_ALLOCATE 0x01001E /* Error allocating blocks for relocating metadata */ #define PR_1_RELOC_MEMORY_ALLOCATE 0x01001F /* Error allocating block buffer during relocation process */ #define PR_1_RELOC_FROM_TO 0x010020 /* Relocating metadata group information from X to Y */ #define PR_1_RELOC_TO 0x010021 /* Relocating metatdata group information to X */ #define PR_1_RELOC_READ_ERR 0x010022 /* Block read error during relocation process */ #define PR_1_RELOC_WRITE_ERR 0x010023 /* Block write error during relocation process */ #define PR_1_ALLOCATE_IBITMAP_ERROR 0x010024 /* Error allocating inode bitmap */ #define PR_1_ALLOCATE_BBITMAP_ERROR 0x010025 /* Error allocating block bitmap */ #define PR_1_ALLOCATE_ICOUNT 0x010026 /* Error allocating icount structure */ #define PR_1_ALLOCATE_DBCOUNT 0x010027 /* Error allocating dbcount */ #define PR_1_ISCAN_ERROR 0x010028 /* Error while scanning inodes */ #define PR_1_BLOCK_ITERATE 0x010029 /* Error while iterating over blocks */ #define PR_1_ICOUNT_STORE 0x01002A /* Error while storing inode count information */ #define PR_1_ADD_DBLOCK 0x01002B /* Error while storing directory block information */ #define PR_1_READ_INODE 0x01002C /* Error while reading inode (for clearing) */ #define PR_1_SUPPRESS_MESSAGES 0x01002D /* Suppress messages prompt */ #define PR_1_SET_IMAGIC 0x01002F /* Imagic flag set on an inode when filesystem doesn't support it */ #define PR_1_SET_IMMUTABLE 0x010030 /* Immutable flag set on a device or socket inode */ #define PR_1_COMPR_SET 0x010031 /* Compression flag set on a non-compressed filesystem */ #define PR_1_SET_NONZSIZE 0x010032 /* Non-zero size on device, fifo or socket inode */ #define PR_1_FS_REV_LEVEL 0x010033 /* Filesystem revision is 0, but feature flags are set */ #define PR_1_JOURNAL_INODE_NOT_CLEAR 0x010034 /* Journal inode not in use, needs clearing */ #define PR_1_JOURNAL_BAD_MODE 0x010035 /* Journal inode has wrong mode */ #define PR_1_LOW_DTIME 0x010036 /* Inode that was part of orphan linked list */ #define PR_1_ORPHAN_LIST_REFUGEES 0x010037 /* Latch question which asks how to deal with low dtime inodes */ #define PR_1_ALLOCATE_REFCOUNT 0x010038 /* Error allocating refcount structure */ #define PR_1_READ_EA_BLOCK 0x010039 /* Error reading Extended Attribute block */ #define PR_1_BAD_EA_BLOCK 0x01003A /* Invalid Extended Attribute block */ #define PR_1_EXTATTR_READ_ABORT 0x01003B /* Error reading Extended Attribute block while fixing refcount -- abort */ #define PR_1_EXTATTR_REFCOUNT 0x01003C /* Extended attribute reference count incorrect */ #define PR_1_EXTATTR_WRITE 0x01003D /* Error writing Extended Attribute block while fixing refcount */ #define PR_1_EA_MULTI_BLOCK 0x01003E /* Multiple EA blocks not supported */ #define PR_1_EA_ALLOC_REGION 0x01003F /* Error allocating EA region allocation structure */ #define PR_1_EA_ALLOC_COLLISION 0x010040 /* Error EA allocation collision */ #define PR_1_EA_BAD_NAME 0x010041 /* Bad extended attribute name */ #define PR_1_EA_BAD_VALUE 0x010042 /* Bad extended attribute value */ #define PR_1_INODE_TOOBIG 0x010043 /* Inode too big (latch question) */ #define PR_1_TOOBIG_DIR 0x010044 /* Directory too big */ #define PR_1_TOOBIG_REG 0x010045 /* Regular file too big */ #define PR_1_TOOBIG_SYMLINK 0x010046 /* Symlink too big */ #define PR_1_HTREE_SET 0x010047 /* INDEX_FL flag set on a non-HTREE filesystem */ #define PR_1_HTREE_NODIR 0x010048 /* INDEX_FL flag set on a non-directory */ #define PR_1_HTREE_BADROOT 0x010049 /* Invalid root node in HTREE directory */ #define PR_1_HTREE_HASHV 0x01004A /* Unsupported hash version in HTREE directory */ #define PR_1_HTREE_INCOMPAT 0x01004B /* Incompatible flag in HTREE root node */ #define PR_1_HTREE_DEPTH 0x01004C /* HTREE too deep */ #define PR_1_BB_FS_BLOCK 0x01004D /* Bad block has indirect block that conflicts with filesystem block */ #define PR_1_RESIZE_INODE_CREATE 0x01004E /* Resize inode failed */ #define PR_1_EXTRA_ISIZE 0x01004F /* inode->i_size is too long */ #define PR_1_ATTR_NAME_LEN 0x010050 /* attribute name is too long */ #define PR_1_ATTR_VALUE_OFFSET 0x010051 /* wrong EA value offset */ #define PR_1_ATTR_VALUE_BLOCK 0x010052 /* wrong EA blocknumber */ #define PR_1_ATTR_VALUE_SIZE 0x010053 /* wrong EA value size */ #define PR_1_ATTR_HASH 0x010054 /* wrong EA hash value */ /* * Pass 1b errors */ #define PR_1B_PASS_HEADER 0x011000 /* Pass 1B: Rescan for duplicate/bad blocks */ #define PR_1B_DUP_BLOCK_HEADER 0x011001 /* Duplicate/bad block(s) header */ #define PR_1B_DUP_BLOCK 0x011002 /* Duplicate/bad block(s) in inode */ #define PR_1B_DUP_BLOCK_END 0x011003 /* Duplicate/bad block(s) end */ #define PR_1B_ISCAN_ERROR 0x011004 /* Error while scanning inodes */ #define PR_1B_ALLOCATE_IBITMAP_ERROR 0x011005 /* Error allocating inode bitmap */ #define PR_1B_BLOCK_ITERATE 0x0110006 /* Error while iterating over blocks */ #define PR_1B_ADJ_EA_REFCOUNT 0x0110007 /* Error adjusting EA refcount */ #define PR_1C_PASS_HEADER 0x012000 /* Pass 1C: Scan directories for inodes with dup blocks. */ #define PR_1D_PASS_HEADER 0x013000 /* Pass 1D: Reconciling duplicate blocks */ #define PR_1D_DUP_FILE 0x013001 /* File has duplicate blocks */ #define PR_1D_DUP_FILE_LIST 0x013002 /* List of files sharing duplicate blocks */ #define PR_1D_SHARE_METADATA 0x013003 /* File sharing blocks with filesystem metadata */ #define PR_1D_NUM_DUP_INODES 0x013004 /* Report of how many duplicate/bad inodes */ #define PR_1D_DUP_BLOCKS_DEALT 0x013005 /* Duplicated blocks already reassigned or cloned. */ #define PR_1D_CLONE_QUESTION 0x013006 /* Clone duplicate/bad blocks? */ #define PR_1D_DELETE_QUESTION 0x013007 /* Delete file? */ #define PR_1D_CLONE_ERROR 0x013008 /* Couldn't clone file (error) */ /* * Pass 2 errors */ #define PR_2_PASS_HEADER 0x020000 /* Pass 2: Checking directory structure */ #define PR_2_BAD_INODE_DOT 0x020001 /* Bad inode number for '.' */ #define PR_2_BAD_INO 0x020002 /* Directory entry has bad inode number */ #define PR_2_UNUSED_INODE 0x020003 /* Directory entry has deleted or unused inode */ #define PR_2_LINK_DOT 0x020004 /* Directry entry is link to '.' */ #define PR_2_BB_INODE 0x020005 /* Directory entry points to inode now located in a bad block */ #define PR_2_LINK_DIR 0x020006 /* Directory entry contains a link to a directory */ #define PR_2_LINK_ROOT 0x020007 /* Directory entry contains a link to the root directry */ #define PR_2_BAD_NAME 0x020008 /* Directory entry has illegal characters in its name */ #define PR_2_MISSING_DOT 0x020009 /* Missing '.' in directory inode */ #define PR_2_MISSING_DOT_DOT 0x02000A /* Missing '..' in directory inode */ #define PR_2_1ST_NOT_DOT 0x02000B /* First entry in directory inode doesn't contain '.' */ #define PR_2_2ND_NOT_DOT_DOT 0x02000C /* Second entry in directory inode doesn't contain '..' */ #define PR_2_FADDR_ZERO 0x02000D /* i_faddr should be zero */ #define PR_2_FILE_ACL_ZERO 0x02000E /* i_file_acl should be zero */ #define PR_2_DIR_ACL_ZERO 0x02000F /* i_dir_acl should be zero */ #define PR_2_FRAG_ZERO 0x020010 /* i_frag should be zero */ #define PR_2_FSIZE_ZERO 0x020011 /* i_fsize should be zero */ #define PR_2_BAD_MODE 0x020012 /* inode has bad mode */ #define PR_2_DIR_CORRUPTED 0x020013 /* directory corrupted */ #define PR_2_FILENAME_LONG 0x020014 /* filename too long */ #define PR_2_DIRECTORY_HOLE 0x020015 /* Directory inode has a missing block (hole) */ #define PR_2_DOT_NULL_TERM 0x020016 /* '.' is not NULL terminated */ #define PR_2_DOT_DOT_NULL_TERM 0x020017 /* '..' is not NULL terminated */ #define PR_2_BAD_CHAR_DEV 0x020018 /* Illegal character device in inode */ #define PR_2_BAD_BLOCK_DEV 0x020019 /* Illegal block device in inode */ #define PR_2_DUP_DOT 0x02001A /* Duplicate '.' entry */ #define PR_2_DUP_DOT_DOT 0x02001B /* Duplicate '..' entry */ #define PR_2_NO_DIRINFO 0x02001C /* Internal error: couldn't find dir_info */ #define PR_2_FINAL_RECLEN 0x02001D /* Final rec_len is wrong */ #define PR_2_ALLOCATE_ICOUNT 0x02001E /* Error allocating icount structure */ #define PR_2_DBLIST_ITERATE 0x02001F /* Error iterating over directory blocks */ #define PR_2_READ_DIRBLOCK 0x020020 /* Error reading directory block */ #define PR_2_WRITE_DIRBLOCK 0x020021 /* Error writing directory block */ #define PR_2_ALLOC_DIRBOCK 0x020022 /* Error allocating new directory block */ #define PR_2_DEALLOC_INODE 0x020023 /* Error deallocating inode */ #define PR_2_SPLIT_DOT 0x020024 /* Directory entry for '.' is big. Split? */ #define PR_2_BAD_FIFO 0x020025 /* Illegal FIFO */ #define PR_2_BAD_SOCKET 0x020026 /* Illegal socket */ #define PR_2_SET_FILETYPE 0x020027 /* Directory filetype not set */ #define PR_2_BAD_FILETYPE 0x020028 /* Directory filetype incorrect */ #define PR_2_CLEAR_FILETYPE 0x020029 /* Directory filetype set when it shouldn't be */ #define PR_2_NULL_NAME 0x020030 /* Directory filename can't be zero-length */ #define PR_2_INVALID_SYMLINK 0x020031 /* Invalid symlink */ #define PR_2_FILE_ACL_BAD 0x020032 /* i_file_acl (extended attribute) is bad */ #define PR_2_FEATURE_LARGE_FILES 0x020033 /* Filesystem contains large files, but has no such flag in sb */ #define PR_2_HTREE_NOTREF 0x020034 /* Node in HTREE directory not referenced */ #define PR_2_HTREE_DUPREF 0x020035 /* Node in HTREE directory referenced twice */ #define PR_2_HTREE_MIN_HASH 0x020036 /* Node in HTREE directory has bad min hash */ #define PR_2_HTREE_MAX_HASH 0x020037 /* Node in HTREE directory has bad max hash */ #define PR_2_HTREE_CLEAR 0x020038 /* Clear invalid HTREE directory */ #define PR_2_HTREE_BADBLK 0x02003A /* Bad block in htree interior node */ #define PR_2_ADJ_EA_REFCOUNT 0x02003B /* Error adjusting EA refcount */ #define PR_2_HTREE_BAD_ROOT 0x02003C /* Invalid HTREE root node */ #define PR_2_HTREE_BAD_LIMIT 0x02003D /* Invalid HTREE limit */ #define PR_2_HTREE_BAD_COUNT 0x02003E /* Invalid HTREE count */ #define PR_2_HTREE_HASH_ORDER 0x02003F /* HTREE interior node has out-of-order hashes in table */ #define PR_2_HTREE_BAD_DEPTH 0x020040 /* Node in HTREE directory has bad depth */ #define PR_2_DUPLICATE_DIRENT 0x020041 /* Duplicate directory entry found */ #define PR_2_NON_UNIQUE_FILE 0x020042 /* Non-unique filename found */ #define PR_2_REPORT_DUP_DIRENT 0x020043 /* Duplicate directory entry found */ /* * Pass 3 errors */ #define PR_3_PASS_HEADER 0x030000 /* Pass 3: Checking directory connectivity */ #define PR_3_NO_ROOT_INODE 0x030001 /* Root inode not allocated */ #define PR_3_EXPAND_LF_DIR 0x030002 /* No room in lost+found */ #define PR_3_UNCONNECTED_DIR 0x030003 /* Unconnected directory inode */ #define PR_3_NO_LF_DIR 0x030004 /* /lost+found not found */ #define PR_3_BAD_DOT_DOT 0x030005 /* .. entry is incorrect */ #define PR_3_NO_LPF 0x030006 /* Bad or non-existent /lost+found. Cannot reconnect */ #define PR_3_CANT_EXPAND_LPF 0x030007 /* Could not expand /lost+found */ #define PR_3_CANT_RECONNECT 0x030008 /* Could not reconnect inode */ #define PR_3_ERR_FIND_LPF 0x030009 /* Error while trying to find /lost+found */ #define PR_3_ERR_LPF_NEW_BLOCK 0x03000A /* Error in ext2fs_new_block while creating /lost+found */ #define PR_3_ERR_LPF_NEW_INODE 0x03000B /* Error in ext2fs_new_inode while creating /lost+found */ #define PR_3_ERR_LPF_NEW_DIR_BLOCK 0x03000C /* Error in ext2fs_new_dir_block while creating /lost+found */ #define PR_3_ERR_LPF_WRITE_BLOCK 0x03000D /* Error while writing directory block for /lost+found */ #define PR_3_ADJUST_INODE 0x03000E /* Error while adjusting inode count */ #define PR_3_FIX_PARENT_ERR 0x03000F /* Couldn't fix parent directory -- error */ #define PR_3_FIX_PARENT_NOFIND 0x030010 /* Couldn't fix parent directory -- couldn't find it */ #define PR_3_ALLOCATE_IBITMAP_ERROR 0x030011 /* Error allocating inode bitmap */ #define PR_3_CREATE_ROOT_ERROR 0x030012 /* Error creating root directory */ #define PR_3_CREATE_LPF_ERROR 0x030013 /* Error creating lost and found directory */ #define PR_3_ROOT_NOT_DIR_ABORT 0x030014 /* Root inode is not directory; aborting */ #define PR_3_NO_ROOT_INODE_ABORT 0x030015 /* Cannot proceed without a root inode. */ #define PR_3_NO_DIRINFO 0x030016 /* Internal error: couldn't find dir_info */ #define PR_3_LPF_NOTDIR 0x030017 /* Lost+found is not a directory */ /* * Pass 3a --- rehashing diretories */ #define PR_3A_PASS_HEADER 0x031000 /* Pass 3a: Reindexing directories */ #define PR_3A_OPTIMIZE_ITER 0x031001 /* Error iterating over directories */ #define PR_3A_OPTIMIZE_DIR_ERR 0x031002 /* Error rehash directory */ #define PR_3A_OPTIMIZE_DIR_HEADER 0x031003 /* Rehashing dir header */ #define PR_3A_OPTIMIZE_DIR 0x031004 /* Rehashing directory %d */ #define PR_3A_OPTIMIZE_DIR_END 0x031005 /* Rehashing dir end */ /* * Pass 4 errors */ #define PR_4_PASS_HEADER 0x040000 /* Pass 4: Checking reference counts */ #define PR_4_ZERO_LEN_INODE 0x040001 /* Unattached zero-length inode */ #define PR_4_UNATTACHED_INODE 0x040002 /* Unattached inode */ #define PR_4_BAD_REF_COUNT 0x040003 /* Inode ref count wrong */ #define PR_4_INCONSISTENT_COUNT 0x040004 /* Inconsistent inode count information cached */ /* * Pass 5 errors */ #define PR_5_PASS_HEADER 0x050000 /* Pass 5: Checking group summary information */ #define PR_5_INODE_BMAP_PADDING 0x050001 /* Padding at end of inode bitmap is not set. */ #define PR_5_BLOCK_BMAP_PADDING 0x050002 /* Padding at end of block bitmap is not set. */ #define PR_5_BLOCK_BITMAP_HEADER 0x050003 /* Block bitmap differences header */ #define PR_5_BLOCK_UNUSED 0x050004 /* Block not used, but marked in bitmap */ #define PR_5_BLOCK_USED 0x050005 /* Block used, but not marked used in bitmap */ #define PR_5_BLOCK_BITMAP_END 0x050006 /* Block bitmap differences end */ #define PR_5_INODE_BITMAP_HEADER 0x050007 /* Inode bitmap differences header */ #define PR_5_INODE_UNUSED 0x050008 /* Inode not used, but marked in bitmap */ #define PR_5_INODE_USED 0x050009 /* Inode used, but not marked used in bitmap */ #define PR_5_INODE_BITMAP_END 0x05000A /* Inode bitmap differences end */ #define PR_5_FREE_INODE_COUNT_GROUP 0x05000B /* Free inodes count for group wrong */ #define PR_5_FREE_DIR_COUNT_GROUP 0x05000C /* Directories count for group wrong */ #define PR_5_FREE_INODE_COUNT 0x05000D /* Free inodes count wrong */ #define PR_5_FREE_BLOCK_COUNT_GROUP 0x05000E /* Free blocks count for group wrong */ #define PR_5_FREE_BLOCK_COUNT 0x05000F /* Free blocks count wrong */ #define PR_5_BMAP_ENDPOINTS 0x050010 /* Programming error: bitmap endpoints don't match */ #define PR_5_FUDGE_BITMAP_ERROR 0x050011 /* Internal error: fudging end of bitmap */ #define PR_5_COPY_IBITMAP_ERROR 0x050012 /* Error copying in replacement inode bitmap */ #define PR_5_COPY_BBITMAP_ERROR 0x050013 /* Error copying in replacement block bitmap */ #define PR_5_BLOCK_RANGE_UNUSED 0x050014 /* Block range not used, but marked in bitmap */ #define PR_5_BLOCK_RANGE_USED 0x050015 /* Block range used, but not marked used in bitmap */ #define PR_5_INODE_RANGE_UNUSED 0x050016 /* Inode range not used, but marked in bitmap */ #define PR_5_INODE_RANGE_USED 0x050017 /* Inode rangeused, but not marked used in bitmap */ /* * The directory information structure; stores directory information * collected in earlier passes, to avoid disk i/o in fetching the * directory information. */ struct dir_info { ext2_ino_t ino; /* Inode number */ ext2_ino_t dotdot; /* Parent according to '..' */ ext2_ino_t parent; /* Parent according to treewalk */ }; /* * The indexed directory information structure; stores information for * directories which contain a hash tree index. */ struct dx_dir_info { ext2_ino_t ino; /* Inode number */ int numblocks; /* number of blocks */ int hashversion; short depth; /* depth of tree */ struct dx_dirblock_info *dx_block; /* Array of size numblocks */ }; /* * Define the extended attribute refcount structure */ typedef struct ea_refcount *ext2_refcount_t; struct e2fsck_struct { ext2_filsys fs; const char *program_name; char *filesystem_name; char *device_name; char *io_options; int flags; /* E2fsck internal flags */ int options; blk_t use_superblock; /* sb requested by user */ blk_t superblock; /* sb used to open fs */ int blocksize; /* blocksize */ blk_t num_blocks; /* Total number of blocks */ int mount_flags; blkid_cache blkid; /* blkid cache */ jmp_buf abort_loc; unsigned long abort_code; int (*progress)(e2fsck_t ctx, int pass, unsigned long cur, unsigned long max); ext2fs_inode_bitmap inode_used_map; /* Inodes which are in use */ ext2fs_inode_bitmap inode_bad_map; /* Inodes which are bad somehow */ ext2fs_inode_bitmap inode_dir_map; /* Inodes which are directories */ ext2fs_inode_bitmap inode_imagic_map; /* AFS inodes */ ext2fs_inode_bitmap inode_reg_map; /* Inodes which are regular files*/ ext2fs_block_bitmap block_found_map; /* Blocks which are in use */ ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */ ext2fs_block_bitmap block_ea_map; /* Blocks which are used by EA's */ /* * Inode count arrays */ ext2_icount_t inode_count; ext2_icount_t inode_link_info; ext2_refcount_t refcount; ext2_refcount_t refcount_extra; /* * Array of flags indicating whether an inode bitmap, block * bitmap, or inode table is invalid */ int *invalid_inode_bitmap_flag; int *invalid_block_bitmap_flag; int *invalid_inode_table_flag; int invalid_bitmaps; /* There are invalid bitmaps/itable */ /* * Block buffer */ char *block_buf; /* * For pass1_check_directory and pass1_get_blocks */ ext2_ino_t stashed_ino; struct ext2_inode *stashed_inode; /* * Location of the lost and found directory */ ext2_ino_t lost_and_found; int bad_lost_and_found; /* * Directory information */ int dir_info_count; int dir_info_size; struct dir_info *dir_info; /* * Indexed directory information */ int dx_dir_info_count; int dx_dir_info_size; struct dx_dir_info *dx_dir_info; /* * Directories to hash */ ext2_u32_list dirs_to_hash; /* * Tuning parameters */ int process_inode_size; int inode_buffer_blocks; /* * ext3 journal support */ io_channel journal_io; char *journal_name; /* * How we display the progress update (for unix) */ int progress_fd; int progress_pos; int progress_last_percent; unsigned int progress_last_time; int interactive; /* Are we connected directly to a tty? */ char start_meta[2], stop_meta[2]; /* File counts */ int fs_directory_count; int fs_regular_count; int fs_blockdev_count; int fs_chardev_count; int fs_links_count; int fs_symlinks_count; int fs_fast_symlinks_count; int fs_fifo_count; int fs_total_count; int fs_sockets_count; int fs_ind_count; int fs_dind_count; int fs_tind_count; int fs_fragmented; int large_files; int fs_ext_attr_inodes; int fs_ext_attr_blocks; int ext_attr_ver; /* * For the use of callers of the e2fsck functions; not used by * e2fsck functions themselves. */ void *priv_data; }; #define tid_gt(x, y) ((x - y) > 0) static inline int tid_geq(tid_t x, tid_t y) { int difference = (x - y); return (difference >= 0); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/lsattr.c0000644000000000000000000000541012263563520020131 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * lsattr.c - List file attributes on an ext2 file system * * Copyright (C) 1993, 1994 Remy Card * Laboratoire MASI, Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * This file can be redistributed under the terms of the GNU General * Public License */ /* * History: * 93/10/30 - Creation * 93/11/13 - Replace stat() calls by lstat() to avoid loops * 94/02/27 - Integrated in Ted's distribution * 98/12/29 - Display version info only when -V specified (G M Sipe) */ #include #include #include #include #include #include #include #include #include #include #include #include "ext2fs/ext2_fs.h" #include "e2fsbb.h" #include "e2p/e2p.h" #define OPT_RECUR 1 #define OPT_ALL 2 #define OPT_DIRS_OPT 4 #define OPT_PF_LONG 8 #define OPT_GENERATION 16 static int flags; static void list_attributes(const char *name) { unsigned long fsflags; unsigned long generation; if (fgetflags(name, &fsflags) == -1) goto read_err; if (flags & OPT_GENERATION) { if (fgetversion(name, &generation) == -1) goto read_err; printf("%5lu ", generation); } if (flags & OPT_PF_LONG) { printf("%-28s ", name); print_e2flags(stdout, fsflags, PFOPT_LONG); bb_putchar('\n'); } else { print_e2flags(stdout, fsflags, 0); printf(" %s\n", name); } return; read_err: bb_perror_msg("reading %s", name); } static int lsattr_dir_proc(const char *, struct dirent *, void *); static void lsattr_args(const char *name) { struct stat st; if (lstat(name, &st) == -1) { bb_perror_msg("stating %s", name); } else { if (S_ISDIR(st.st_mode) && !(flags & OPT_DIRS_OPT)) iterate_on_dir(name, lsattr_dir_proc, NULL); else list_attributes(name); } } static int lsattr_dir_proc(const char *dir_name, struct dirent *de, void *private) { struct stat st; char *path; path = concat_path_file(dir_name, de->d_name); if (lstat(path, &st) == -1) bb_simple_perror_msg(path); else { if (de->d_name[0] != '.' || (flags & OPT_ALL)) { list_attributes(path); if (S_ISDIR(st.st_mode) && (flags & OPT_RECUR) && (de->d_name[0] != '.' && (de->d_name[1] != '\0' || (de->d_name[1] != '.' && de->d_name[2] != '\0')))) { printf("\n%s:\n", path); iterate_on_dir(path, lsattr_dir_proc, NULL); bb_putchar('\n'); } } } free(path); return 0; } int lsattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int lsattr_main(int argc, char **argv) { int i; flags = getopt32(argv, "Radlv"); if (optind > argc - 1) lsattr_args("."); else for (i = optind; i < argc; i++) lsattr_args(argv[i]); return EXIT_SUCCESS; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/util.c0000644000000000000000000001544612263563520017607 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * util.c --- helper functions used by tune2fs and mke2fs * * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o. * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include #include #include #include #include #include "e2fsbb.h" #include "e2p/e2p.h" #include "ext2fs/ext2_fs.h" #include "ext2fs/ext2fs.h" #include "blkid/blkid.h" #include "util.h" void proceed_question(void) { fputs("Proceed anyway? (y,n) ", stdout); if (bb_ask_confirmation() == 0) exit(1); } void check_plausibility(const char *device, int force) { int val; struct stat s; val = stat(device, &s); if (force) return; if (val == -1) bb_perror_msg_and_die("can't stat '%s'", device); if (!S_ISBLK(s.st_mode)) { printf("%s is not a block special device.\n", device); proceed_question(); return; } #ifdef HAVE_LINUX_MAJOR_H #ifndef MAJOR #define MAJOR(dev) ((dev)>>8) #define MINOR(dev) ((dev) & 0xff) #endif #ifndef SCSI_BLK_MAJOR #ifdef SCSI_DISK0_MAJOR #ifdef SCSI_DISK8_MAJOR #define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR || \ ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) || \ ((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR)) #else #define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR || \ ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR)) #endif /* defined(SCSI_DISK8_MAJOR) */ #define SCSI_BLK_MAJOR(M) (SCSI_DISK_MAJOR((M)) || (M) == SCSI_CDROM_MAJOR) #else #define SCSI_BLK_MAJOR(M) ((M) == SCSI_DISK_MAJOR || (M) == SCSI_CDROM_MAJOR) #endif /* defined(SCSI_DISK0_MAJOR) */ #endif /* defined(SCSI_BLK_MAJOR) */ if (((MAJOR(s.st_rdev) == HD_MAJOR && MINOR(s.st_rdev)%64 == 0) || (SCSI_BLK_MAJOR(MAJOR(s.st_rdev)) && MINOR(s.st_rdev)%16 == 0))) { printf("%s is entire device, not just one partition!\n", device); proceed_question(); } #endif } void check_mount(const char *device, int force, const char *type) { errcode_t retval; int mount_flags; retval = ext2fs_check_if_mounted(device, &mount_flags); if (retval) { bb_error_msg("can't determine if %s is mounted", device); return; } if (mount_flags & EXT2_MF_MOUNTED) { bb_error_msg("%s is mounted !", device); force_check: if (force) bb_error_msg("badblocks forced anyways"); else bb_error_msg_and_die("it's not safe to run badblocks!"); } if (mount_flags & EXT2_MF_BUSY) { bb_error_msg("%s is apparently in use by the system", device); goto force_check; } } void parse_journal_opts(char **journal_device, int *journal_flags, int *journal_size, const char *opts) { char *buf, *token, *next, *p, *arg; int journal_usage = 0; buf = xstrdup(opts); for (token = buf; token && *token; token = next) { p = strchr(token, ','); next = 0; if (p) { *p = 0; next = p+1; } arg = strchr(token, '='); if (arg) { *arg = 0; arg++; } if (strcmp(token, "device") == 0) { *journal_device = blkid_get_devname(NULL, arg, NULL); if (!*journal_device) { journal_usage++; continue; } } else if (strcmp(token, "size") == 0) { if (!arg) { journal_usage++; continue; } (*journal_size) = strtoul(arg, &p, 0); if (*p) journal_usage++; } else if (strcmp(token, "v1_superblock") == 0) { (*journal_flags) |= EXT2_MKJOURNAL_V1_SUPER; continue; } else journal_usage++; } if (journal_usage) bb_error_msg_and_die( "\nBad journal options specified.\n\n" "Journal options are separated by commas, " "and may take an argument which\n" "\tis set off by an equals ('=') sign.\n\n" "Valid journal options are:\n" "\tsize=\n" "\tdevice=\n\n" "The journal size must be between " "1024 and 102400 filesystem blocks.\n\n"); } /* * Determine the number of journal blocks to use, either via * user-specified # of megabytes, or via some intelligently selected * defaults. * * Find a reasonable journal file size (in blocks) given the number of blocks * in the filesystem. For very small filesystems, it is not reasonable to * have a journal that fills more than half of the filesystem. */ int figure_journal_size(int size, ext2_filsys fs) { blk_t j_blocks; if (fs->super->s_blocks_count < 2048) { bb_error_msg("Filesystem too small for a journal"); return 0; } if (size >= 0) { j_blocks = size * 1024 / (fs->blocksize / 1024); if (j_blocks < 1024 || j_blocks > 102400) bb_error_msg_and_die("\nThe requested journal " "size is %d blocks;\n it must be " "between 1024 and 102400 blocks; Aborting", j_blocks); if (j_blocks > fs->super->s_free_blocks_count) bb_error_msg_and_die("Journal size too big for filesystem"); return j_blocks; } if (fs->super->s_blocks_count < 32768) j_blocks = 1024; else if (fs->super->s_blocks_count < 256*1024) j_blocks = 4096; else if (fs->super->s_blocks_count < 512*1024) j_blocks = 8192; else if (fs->super->s_blocks_count < 1024*1024) j_blocks = 16384; else j_blocks = 32768; return j_blocks; } void print_check_message(ext2_filsys fs) { printf("This filesystem will be automatically " "checked every %d mounts or\n" "%g days, whichever comes first. " "Use tune2fs -c or -i to override.\n", fs->super->s_max_mnt_count, (double)fs->super->s_checkinterval / (3600 * 24)); } void make_journal_device(char *journal_device, ext2_filsys fs, int quiet, int force) { errcode_t retval; ext2_filsys jfs; io_manager io_ptr; check_plausibility(journal_device, force); check_mount(journal_device, force, "journal"); io_ptr = unix_io_manager; retval = ext2fs_open(journal_device, EXT2_FLAG_RW| EXT2_FLAG_JOURNAL_DEV_OK, 0, fs->blocksize, io_ptr, &jfs); if (retval) bb_error_msg_and_die("can't journal device %s", journal_device); if (!quiet) printf("Adding journal to device %s: ", journal_device); fflush(stdout); retval = ext2fs_add_journal_device(fs, jfs); if (retval) bb_error_msg_and_die("\nFailed to add journal to device %s", journal_device); if (!quiet) puts("done"); ext2fs_close(jfs); } void make_journal_blocks(ext2_filsys fs, int journal_size, int journal_flags, int quiet) { unsigned long journal_blocks; errcode_t retval; journal_blocks = figure_journal_size(journal_size, fs); if (!journal_blocks) { fs->super->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL; return; } if (!quiet) printf("Creating journal (%lu blocks): ", journal_blocks); fflush(stdout); retval = ext2fs_add_journal_inode(fs, journal_blocks, journal_flags); if (retval) bb_error_msg_and_die("can't create journal"); if (!quiet) puts("done"); } char *e2fs_set_sbin_path(void) { char *oldpath = getenv("PATH"); /* Update our PATH to include /sbin */ #define PATH_SET "/sbin" if (oldpath) oldpath = xasprintf("%s:%s", PATH_SET, oldpath); else oldpath = PATH_SET; putenv(oldpath); return oldpath; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/chattr.c0000644000000000000000000001164012263563520020107 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * chattr.c - Change file attributes on an ext2 file system * * Copyright (C) 1993, 1994 Remy Card * Laboratoire MASI, Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * This file can be redistributed under the terms of the GNU General * Public License */ /* * History: * 93/10/30 - Creation * 93/11/13 - Replace stat() calls by lstat() to avoid loops * 94/02/27 - Integrated in Ted's distribution * 98/12/29 - Ignore symlinks when working recursively (G M Sipe) * 98/12/29 - Display version info only when -V specified (G M Sipe) */ #include #include #include #include #include #include #include #include #include #include #include "ext2fs/ext2_fs.h" #ifdef __GNUC__ # define EXT2FS_ATTR(x) __attribute__(x) #else # define EXT2FS_ATTR(x) #endif #include "e2fsbb.h" #include "e2p/e2p.h" #define OPT_ADD 1 #define OPT_REM 2 #define OPT_SET 4 #define OPT_SET_VER 8 static int flags; static int recursive; static unsigned long version; static unsigned long af; static unsigned long rf; static unsigned long sf; struct flags_char { unsigned long flag; char optchar; }; static const struct flags_char flags_array[] = { { EXT2_NOATIME_FL, 'A' }, { EXT2_SYNC_FL, 'S' }, { EXT2_DIRSYNC_FL, 'D' }, { EXT2_APPEND_FL, 'a' }, { EXT2_COMPR_FL, 'c' }, { EXT2_NODUMP_FL, 'd' }, { EXT2_IMMUTABLE_FL, 'i' }, { EXT3_JOURNAL_DATA_FL, 'j' }, { EXT2_SECRM_FL, 's' }, { EXT2_UNRM_FL, 'u' }, { EXT2_NOTAIL_FL, 't' }, { EXT2_TOPDIR_FL, 'T' }, { 0, 0 } }; static unsigned long get_flag(char c) { const struct flags_char *fp; for (fp = flags_array; fp->flag; fp++) if (fp->optchar == c) return fp->flag; bb_show_usage(); return 0; } static int decode_arg(char *arg) { unsigned long *fl; char opt = *arg++; if (opt == '-') { flags |= OPT_REM; fl = &rf; } else if (opt == '+') { flags |= OPT_ADD; fl = ⁡ } else if (opt == '=') { flags |= OPT_SET; fl = &sf; } else return EOF; for (; *arg; ++arg) (*fl) |= get_flag(*arg); return 1; } static int chattr_dir_proc(const char *, struct dirent *, void *); static void change_attributes(const char * name) { unsigned long fsflags; struct stat st; if (lstat(name, &st) == -1) { bb_error_msg("stat %s failed", name); return; } if (S_ISLNK(st.st_mode) && recursive) return; /* Don't try to open device files, fifos etc. We probably * ought to display an error if the file was explicitly given * on the command line (whether or not recursive was * requested). */ if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode)) return; if (flags & OPT_SET_VER) if (fsetversion(name, version) == -1) bb_error_msg("setting version on %s", name); if (flags & OPT_SET) { fsflags = sf; } else { if (fgetflags(name, &fsflags) == -1) { bb_error_msg("reading flags on %s", name); goto skip_setflags; } if (flags & OPT_REM) fsflags &= ~rf; if (flags & OPT_ADD) fsflags |= af; if (!S_ISDIR(st.st_mode)) fsflags &= ~EXT2_DIRSYNC_FL; } if (fsetflags(name, fsflags) == -1) bb_error_msg("setting flags on %s", name); skip_setflags: if (S_ISDIR(st.st_mode) && recursive) iterate_on_dir(name, chattr_dir_proc, NULL); } static int chattr_dir_proc(const char *dir_name, struct dirent *de, void *private EXT2FS_ATTR((unused))) { /*if (strcmp(de->d_name, ".") || strcmp(de->d_name, "..")) {*/ if (de->d_name[0] == '.' && (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])) ) { char *path = concat_subpath_file(dir_name, de->d_name); if (path) { change_attributes(path); free(path); } } return 0; } int chattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int chattr_main(int argc, char **argv) { int i; char *arg; /* parse the args */ for (i = 1; i < argc; ++i) { arg = argv[i]; /* take care of -R and -v */ if (arg[0] == '-') { if (arg[1] == 'R' && arg[2] == '\0') { recursive = 1; continue; } else if (arg[1] == 'v' && arg[2] == '\0') { char *tmp; ++i; if (i >= argc) bb_show_usage(); version = strtol(argv[i], &tmp, 0); if (*tmp) bb_error_msg_and_die("bad version '%s'", arg); flags |= OPT_SET_VER; continue; } } if (decode_arg(arg) == EOF) break; } /* run sanity checks on all the arguments given us */ if (i >= argc) bb_show_usage(); if ((flags & OPT_SET) && ((flags & OPT_ADD) || (flags & OPT_REM))) bb_error_msg_and_die("= is incompatible with - and +"); if ((rf & af) != 0) bb_error_msg_and_die("Can't set and unset a flag"); if (!flags) bb_error_msg_and_die("Must use '-v', =, - or +"); /* now run chattr on all the files passed to us */ while (i < argc) change_attributes(argv[i++]); return EXIT_SUCCESS; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/0000755000000000000000000000000012320365361017137 5ustar rootrootbusybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/mntopts.c0000644000000000000000000000503212263563520021012 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * mountopts.c --- convert between default mount options and strings * * Copyright (C) 2002 Theodore Ts'o * * This file can be redistributed under the terms of the GNU Library General * Public License * */ #include #include #include #include #include #include "e2p.h" struct mntopt { unsigned int mask; const char *string; }; static const struct mntopt mntopt_list[] = { { EXT2_DEFM_DEBUG, "debug" }, { EXT2_DEFM_BSDGROUPS, "bsdgroups" }, { EXT2_DEFM_XATTR_USER, "user_xattr" }, { EXT2_DEFM_ACL, "acl" }, { EXT2_DEFM_UID16, "uid16" }, { EXT3_DEFM_JMODE_DATA, "journal_data" }, { EXT3_DEFM_JMODE_ORDERED, "journal_data_ordered" }, { EXT3_DEFM_JMODE_WBACK, "journal_data_writeback" }, { 0, 0 }, }; const char *e2p_mntopt2string(unsigned int mask) { const struct mntopt *f; static char buf[20]; int fnum; for (f = mntopt_list; f->string; f++) { if (mask == f->mask) return f->string; } for (fnum = 0; mask >>= 1; fnum++); sprintf(buf, "MNTOPT_%d", fnum); return buf; } int e2p_string2mntopt(char *string, unsigned int *mask) { const struct mntopt *f; char *eptr; int num; for (f = mntopt_list; f->string; f++) { if (!strcasecmp(string, f->string)) { *mask = f->mask; return 0; } } if (strncasecmp(string, "MNTOPT_", 8)) return 1; if (string[8] == 0) return 1; num = strtol(string+8, &eptr, 10); if (num > 32 || num < 0) return 1; if (*eptr) return 1; *mask = 1 << num; return 0; } static char *skip_over_blanks(char *cp) { while (*cp && isspace(*cp)) cp++; return cp; } static char *skip_over_word(char *cp) { while (*cp && !isspace(*cp) && *cp != ',') cp++; return cp; } /* * Edit a mntopt set array as requested by the user. The ok * parameter, if non-zero, allows the application to limit what * mntopts the user is allowed to set or clear using this function. */ int e2p_edit_mntopts(const char *str, __u32 *mntopts, __u32 ok) { char *cp, *buf, *next; int neg; unsigned int mask; buf = xstrdup(str); cp = buf; while (cp && *cp) { neg = 0; cp = skip_over_blanks(cp); next = skip_over_word(cp); if (*next == 0) next = 0; else *next = 0; switch (*cp) { case '-': case '^': neg++; case '+': cp++; break; } if (e2p_string2mntopt(cp, &mask)) return 1; if (ok && !(ok & mask)) return 1; if (mask & EXT3_DEFM_JMODE) *mntopts &= ~EXT3_DEFM_JMODE; if (neg) *mntopts &= ~mask; else *mntopts |= mask; cp = next ? next+1 : 0; } return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/ps.c0000644000000000000000000000116412263563520017732 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * ps.c - Print filesystem state * * Copyright (C) 1993, 1994 Remy Card * Laboratoire MASI, Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * This file can be redistributed under the terms of the GNU Library General * Public License */ /* * History: * 93/12/22 - Creation */ #include #include "e2p.h" void print_fs_state(FILE *f, unsigned short state) { fprintf(f, (state & EXT2_VALID_FS ? " clean" : " not clean")); if (state & EXT2_ERROR_FS) fprintf(f, " with errors"); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/Kbuild.src0000644000000000000000000000070112263563520021063 0ustar rootroot# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen # # Licensed under GPLv2, see file LICENSE in this source tree. NEEDED-$(CONFIG_CHATTR) = y NEEDED-$(CONFIG_LSATTR) = y NEEDED-$(CONFIG_MKE2FS) = y NEEDED-$(CONFIG_TUNE2FS) = y lib-y:= INSERT lib-$(NEEDED-y) += fgetsetflags.o fgetsetversion.o pf.o iod.o mntopts.o \ feature.o ls.o uuid.o pe.o ostype.o ps.o hashstr.o \ parse_num.o busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/pe.c0000644000000000000000000000151012263563520017707 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * pe.c - Print a second extended filesystem errors behavior * * Copyright (C) 1992, 1993, 1994 Remy Card * Laboratoire MASI, Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * This file can be redistributed under the terms of the GNU Library General * Public License */ /* * History: * 94/01/09 - Creation */ #include #include "e2p.h" void print_fs_errors(FILE *f, unsigned short errors) { char *disp = NULL; switch (errors) { case EXT2_ERRORS_CONTINUE: disp = "Continue"; break; case EXT2_ERRORS_RO: disp = "Remount read-only"; break; case EXT2_ERRORS_PANIC: disp = "Panic"; break; default: disp = "Unknown (continue)"; } fprintf(f, disp); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/fgetsetflags.c0000644000000000000000000000274012263563520021767 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * fgetflags.c - Get a file flags on an ext2 file system * fsetflags.c - Set a file flags on an ext2 file system * * Copyright (C) 1993, 1994 Remy Card * Laboratoire MASI, Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * This file can be redistributed under the terms of the GNU Library General * Public License */ /* * History: * 93/10/30 - Creation */ #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #ifdef HAVE_EXT2_IOCTLS #include #include #endif #include "e2p.h" #ifdef O_LARGEFILE #define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE) #else #define OPEN_FLAGS (O_RDONLY|O_NONBLOCK) #endif int fgetsetflags (const char * name, unsigned long * get_flags, unsigned long set_flags) { #ifdef HAVE_EXT2_IOCTLS struct stat buf; int fd, r, f, save_errno = 0; if (!stat(name, &buf) && !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) { goto notsupp; } fd = open (name, OPEN_FLAGS); if (fd == -1) return -1; if (!get_flags) { f = (int) set_flags; r = ioctl (fd, EXT2_IOC_SETFLAGS, &f); } else { r = ioctl (fd, EXT2_IOC_GETFLAGS, &f); *get_flags = f; } if (r == -1) save_errno = errno; close (fd); if (save_errno) errno = save_errno; return r; notsupp: #endif /* HAVE_EXT2_IOCTLS */ errno = EOPNOTSUPP; return -1; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/pf.c0000644000000000000000000000362612263563520017722 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * pf.c - Print file attributes on an ext2 file system * * Copyright (C) 1993, 1994 Remy Card * Laboratoire MASI, Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * This file can be redistributed under the terms of the GNU Library General * Public License */ /* * History: * 93/10/30 - Creation */ #include #include "e2p.h" struct flags_name { unsigned long flag; const char *short_name; const char *long_name; }; static const struct flags_name flags_array[] = { { EXT2_SECRM_FL, "s", "Secure_Deletion" }, { EXT2_UNRM_FL, "u" , "Undelete" }, { EXT2_SYNC_FL, "S", "Synchronous_Updates" }, { EXT2_DIRSYNC_FL, "D", "Synchronous_Directory_Updates" }, { EXT2_IMMUTABLE_FL, "i", "Immutable" }, { EXT2_APPEND_FL, "a", "Append_Only" }, { EXT2_NODUMP_FL, "d", "No_Dump" }, { EXT2_NOATIME_FL, "A", "No_Atime" }, { EXT2_COMPR_FL, "c", "Compression_Requested" }, #ifdef ENABLE_COMPRESSION { EXT2_COMPRBLK_FL, "B", "Compressed_File" }, { EXT2_DIRTY_FL, "Z", "Compressed_Dirty_File" }, { EXT2_NOCOMPR_FL, "X", "Compression_Raw_Access" }, { EXT2_ECOMPR_FL, "E", "Compression_Error" }, #endif { EXT3_JOURNAL_DATA_FL, "j", "Journaled_Data" }, { EXT2_INDEX_FL, "I", "Indexed_direcctory" }, { EXT2_NOTAIL_FL, "t", "No_Tailmerging" }, { EXT2_TOPDIR_FL, "T", "Top_of_Directory_Hierarchies" }, { 0, NULL, NULL } }; void print_flags (FILE *f, unsigned long flags, unsigned options) { int long_opt = (options & PFOPT_LONG); const struct flags_name *fp; int first = 1; for (fp = flags_array; fp->flag != 0; fp++) { if (flags & fp->flag) { if (long_opt) { if (first) first = 0; else fputs(", ", f); fputs(fp->long_name, f); } else fputs(fp->short_name, f); } else { if (!long_opt) fputs("-", f); } } if (long_opt && first) fputs("---", f); } busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/parse_num.c0000644000000000000000000000205012263563520021274 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * parse_num.c - Parse the number of blocks * * Copyright (C) 2004,2005 Theodore Ts'o * * This file can be redistributed under the terms of the GNU Library General * Public License */ #include "e2p.h" #include unsigned long parse_num_blocks(const char *arg, int log_block_size) { char *p; unsigned long long num; num = strtoull(arg, &p, 0); if (p[0] && p[1]) return 0; switch (*p) { /* Using fall-through logic */ case 'T': case 't': num <<= 10; case 'G': case 'g': num <<= 10; case 'M': case 'm': num <<= 10; case 'K': case 'k': num >>= log_block_size; break; case 's': num >>= 1; break; case '\0': break; default: return 0; } return num; } #ifdef DEBUG #include #include main(int argc, char **argv) { unsigned long num; int log_block_size = 0; if (argc != 2) { fprintf(stderr, "Usage: %s arg\n", argv[0]); exit(1); } num = parse_num_blocks(argv[1], log_block_size); printf("Parsed number: %lu\n", num); exit(0); } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/uuid.c0000644000000000000000000000260312263563520020255 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * uuid.c -- utility routines for manipulating UUID's. */ #include #include #include "../ext2fs/ext2_types.h" #include "e2p.h" struct uuid { __u32 time_low; __u16 time_mid; __u16 time_hi_and_version; __u16 clock_seq; __u8 node[6]; }; /* Returns 1 if the uuid is the NULL uuid */ int e2p_is_null_uuid(void *uu) { __u8 *cp; int i; for (i=0, cp = uu; i < 16; i++) if (*cp) return 0; return 1; } static void e2p_unpack_uuid(void *in, struct uuid *uu) { __u8 *ptr = in; __u32 tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; tmp = (tmp << 8) | *ptr++; tmp = (tmp << 8) | *ptr++; uu->time_low = tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; uu->time_mid = tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; uu->time_hi_and_version = tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; uu->clock_seq = tmp; memcpy(uu->node, ptr, 6); } void e2p_uuid_to_str(void *uu, char *out) { struct uuid uuid; e2p_unpack_uuid(uu, &uuid); sprintf(out, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, uuid.node[0], uuid.node[1], uuid.node[2], uuid.node[3], uuid.node[4], uuid.node[5]); } const char *e2p_uuid2str(void *uu) { static char buf[80]; if (e2p_is_null_uuid(uu)) return ""; e2p_uuid_to_str(uu, buf); return buf; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/fgetsetversion.c0000644000000000000000000000314512263563520022360 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * fgetversion.c - Get a file version on an ext2 file system * fsetversion.c - Set a file version on an ext2 file system * * * Copyright (C) 1993, 1994 Remy Card * Laboratoire MASI, Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * This file can be redistributed under the terms of the GNU Library General * Public License */ /* * History: * 93/10/30 - Creation */ #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #include "e2p.h" #ifdef O_LARGEFILE #define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE) #else #define OPEN_FLAGS (O_RDONLY|O_NONBLOCK) #endif /* To do fsetversion: unsigned long *ptr_version must be set to NULL. and unsigned long version must be set to a value To do fgetversion: unsigned long *ptr_version must NOT be set to NULL and unsigned long version is ignored. TITO. */ int fgetsetversion (const char * name, unsigned long * get_version, unsigned long set_version) { #ifdef HAVE_EXT2_IOCTLS int fd, r, ver, save_errno = 0; fd = open (name, OPEN_FLAGS); if (fd == -1) return -1; if (!get_version) { ver = (int) set_version; r = ioctl (fd, EXT2_IOC_SETVERSION, &ver); } else { r = ioctl (fd, EXT2_IOC_GETVERSION, &ver); *get_version = ver; } if (r == -1) save_errno = errno; close (fd); if (save_errno) errno = save_errno; return r; #else /* ! HAVE_EXT2_IOCTLS */ errno = EOPNOTSUPP; return -1; #endif /* ! HAVE_EXT2_IOCTLS */ } busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/ostype.c0000644000000000000000000000216112263563520020631 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * getostype.c - Get the Filesystem OS type * * Copyright (C) 2004,2005 Theodore Ts'o * * This file can be redistributed under the terms of the GNU Library General * Public License */ #include "e2p.h" #include #include static const char *const os_tab[] = { "Linux", "Hurd", "Masix", "FreeBSD", "Lites", 0 }; /* * Convert an os_type to a string */ char *e2p_os2string(int os_type) { const char *os; char *ret; if (os_type <= EXT2_OS_LITES) os = os_tab[os_type]; else os = "(unknown os)"; ret = xstrdup(os); return ret; } /* * Convert an os_type to a string */ int e2p_string2os(char *str) { const char *const *cpp; int i = 0; for (cpp = os_tab; *cpp; cpp++, i++) { if (!strcasecmp(str, *cpp)) return i; } return -1; } #ifdef TEST_PROGRAM int main(int argc, char **argv) { char *s; int i, os; for (i=0; i <= EXT2_OS_LITES; i++) { s = e2p_os2string(i); os = e2p_string2os(s); printf("%d: %s (%d)\n", i, s, os); if (i != os) { fprintf(stderr, "Failure!\n"); exit(1); } } exit(0); } #endif busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/e2p.h0000644000000000000000000000474512263563520020013 0ustar rootroot/* vi: set sw=4 ts=4: */ #include "libbb.h" #include /* Needed by dirent.h on netbsd */ #include #include #include "../ext2fs/ext2_fs.h" #define E2P_FEATURE_COMPAT 0 #define E2P_FEATURE_INCOMPAT 1 #define E2P_FEATURE_RO_INCOMPAT 2 #ifndef EXT3_FEATURE_INCOMPAT_EXTENTS #define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040 #endif /* `options' for print_e2flags() */ #define PFOPT_LONG 1 /* Must be 1 for compatibility with `int long_format'. */ /*int fgetversion (const char * name, unsigned long * version);*/ /*int fsetversion (const char * name, unsigned long version);*/ int fgetsetversion(const char * name, unsigned long * get_version, unsigned long set_version); #define fgetversion(name, version) fgetsetversion(name, version, 0) #define fsetversion(name, version) fgetsetversion(name, NULL, version) /*int fgetflags (const char * name, unsigned long * flags);*/ /*int fsetflags (const char * name, unsigned long flags);*/ int fgetsetflags(const char * name, unsigned long * get_flags, unsigned long set_flags); #define fgetflags(name, flags) fgetsetflags(name, flags, 0) #define fsetflags(name, flags) fgetsetflags(name, NULL, flags) int getflags (int fd, unsigned long * flags); int getversion (int fd, unsigned long * version); int iterate_on_dir (const char * dir_name, int (*func) (const char *, struct dirent *, void *), void * private); /*void list_super(struct ext2_super_block * s);*/ void list_super2(struct ext2_super_block * s, FILE *f); #define list_super(s) list_super2(s, stdout) void print_fs_errors (FILE *f, unsigned short errors); void print_flags (FILE *f, unsigned long flags, unsigned options); void print_fs_state (FILE *f, unsigned short state); int setflags (int fd, unsigned long flags); int setversion (int fd, unsigned long version); const char *e2p_feature2string(int compat, unsigned int mask); int e2p_string2feature(char *string, int *compat, unsigned int *mask); int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array); int e2p_is_null_uuid(void *uu); void e2p_uuid_to_str(void *uu, char *out); const char *e2p_uuid2str(void *uu); const char *e2p_hash2string(int num); int e2p_string2hash(char *string); const char *e2p_mntopt2string(unsigned int mask); int e2p_string2mntopt(char *string, unsigned int *mask); int e2p_edit_mntopts(const char *str, __u32 *mntopts, __u32 ok); unsigned long parse_num_blocks(const char *arg, int log_block_size); char *e2p_os2string(int os_type); int e2p_string2os(char *str); busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/iod.c0000644000000000000000000000215212263563520020061 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * iod.c - Iterate a function on each entry of a directory * * Copyright (C) 1993, 1994 Remy Card * Laboratoire MASI, Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * This file can be redistributed under the terms of the GNU Library General * Public License */ /* * History: * 93/10/30 - Creation */ #include "e2p.h" #include #include #include int iterate_on_dir (const char * dir_name, int (*func) (const char *, struct dirent *, void *), void * private) { DIR * dir; struct dirent *de, *dep; int max_len, len; max_len = PATH_MAX + sizeof(struct dirent); de = xmalloc(max_len+1); memset(de, 0, max_len+1); dir = opendir (dir_name); if (dir == NULL) { free(de); return -1; } while ((dep = readdir (dir))) { len = sizeof(struct dirent); if (len < dep->d_reclen) len = dep->d_reclen; if (len > max_len) len = max_len; memcpy(de, dep, len); (*func) (dir_name, de, private); } free(de); closedir(dir); return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/ls.c0000644000000000000000000001636212263563520017734 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * ls.c - List the contents of an ext2fs superblock * * Copyright (C) 1992, 1993, 1994 Remy Card * Laboratoire MASI, Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * Copyright (C) 1995, 1996, 1997 Theodore Ts'o * * This file can be redistributed under the terms of the GNU Library General * Public License */ #include #include #include #include #include #include #include #include "e2p.h" static void print_user(unsigned short uid, FILE *f) { struct passwd *pw = getpwuid(uid); fprintf(f, "%u (user %s)\n", uid, (pw == NULL ? "unknown" : pw->pw_name)); } static void print_group(unsigned short gid, FILE *f) { struct group *gr = getgrgid(gid); fprintf(f, "%u (group %s)\n", gid, (gr == NULL ? "unknown" : gr->gr_name)); } #define MONTH_INT (86400 * 30) #define WEEK_INT (86400 * 7) #define DAY_INT (86400) #define HOUR_INT (60 * 60) #define MINUTE_INT (60) static const char *interval_string(unsigned int secs) { static char buf[256], tmp[80]; int hr, min, num; buf[0] = 0; if (secs == 0) return ""; if (secs >= MONTH_INT) { num = secs / MONTH_INT; secs -= num*MONTH_INT; sprintf(buf, "%d month%s", num, (num>1) ? "s" : ""); } if (secs >= WEEK_INT) { num = secs / WEEK_INT; secs -= num*WEEK_INT; sprintf(tmp, "%s%d week%s", buf[0] ? ", " : "", num, (num>1) ? "s" : ""); strcat(buf, tmp); } if (secs >= DAY_INT) { num = secs / DAY_INT; secs -= num*DAY_INT; sprintf(tmp, "%s%d day%s", buf[0] ? ", " : "", num, (num>1) ? "s" : ""); strcat(buf, tmp); } if (secs > 0) { hr = secs / HOUR_INT; secs -= hr*HOUR_INT; min = secs / MINUTE_INT; secs -= min*MINUTE_INT; sprintf(tmp, "%s%d:%02d:%02d", buf[0] ? ", " : "", hr, min, secs); strcat(buf, tmp); } return buf; } static void print_features(struct ext2_super_block * s, FILE *f) { #ifdef EXT2_DYNAMIC_REV int i, j, printed=0; __u32 *mask = &s->s_feature_compat, m; fprintf(f, "Filesystem features: "); for (i=0; i <3; i++,mask++) { for (j=0,m=1; j < 32; j++, m<<=1) { if (*mask & m) { fprintf(f, " %s", e2p_feature2string(i, m)); printed++; } } } if (printed == 0) fprintf(f, " (none)"); fprintf(f, "\n"); #endif } static void print_mntopts(struct ext2_super_block * s, FILE *f) { #ifdef EXT2_DYNAMIC_REV int i, printed=0; __u32 mask = s->s_default_mount_opts, m; fprintf(f, "Default mount options: "); if (mask & EXT3_DEFM_JMODE) { fprintf(f, " %s", e2p_mntopt2string(mask & EXT3_DEFM_JMODE)); printed++; } for (i=0,m=1; i < 32; i++, m<<=1) { if (m & EXT3_DEFM_JMODE) continue; if (mask & m) { fprintf(f, " %s", e2p_mntopt2string(m)); printed++; } } if (printed == 0) fprintf(f, " (none)"); fprintf(f, "\n"); #endif } #ifndef EXT2_INODE_SIZE #define EXT2_INODE_SIZE(s) sizeof(struct ext2_inode) #endif #ifndef EXT2_GOOD_OLD_REV #define EXT2_GOOD_OLD_REV 0 #endif void list_super2(struct ext2_super_block * sb, FILE *f) { int inode_blocks_per_group; char buf[80], *str; time_t tm; inode_blocks_per_group = (((sb->s_inodes_per_group * EXT2_INODE_SIZE(sb)) + EXT2_BLOCK_SIZE(sb) - 1) / EXT2_BLOCK_SIZE(sb)); if (sb->s_volume_name[0]) { memset(buf, 0, sizeof(buf)); strncpy(buf, sb->s_volume_name, sizeof(sb->s_volume_name)); } else strcpy(buf, ""); fprintf(f, "Filesystem volume name: %s\n", buf); if (sb->s_last_mounted[0]) { memset(buf, 0, sizeof(buf)); strncpy(buf, sb->s_last_mounted, sizeof(sb->s_last_mounted)); } else strcpy(buf, ""); fprintf(f, "Last mounted on: %s\n" "Filesystem UUID: %s\n" "Filesystem magic number: 0x%04X\n" "Filesystem revision #: %d", buf, e2p_uuid2str(sb->s_uuid), sb->s_magic, sb->s_rev_level); if (sb->s_rev_level == EXT2_GOOD_OLD_REV) { fprintf(f, " (original)\n"); #ifdef EXT2_DYNAMIC_REV } else if (sb->s_rev_level == EXT2_DYNAMIC_REV) { fprintf(f, " (dynamic)\n"); #endif } else fprintf(f, " (unknown)\n"); print_features(sb, f); print_mntopts(sb, f); fprintf(f, "Filesystem state: "); print_fs_state (f, sb->s_state); fprintf(f, "\nErrors behavior: "); print_fs_errors(f, sb->s_errors); str = e2p_os2string(sb->s_creator_os); fprintf(f, "\n" "Filesystem OS type: %s\n" "Inode count: %u\n" "Block count: %u\n" "Reserved block count: %u\n" "Free blocks: %u\n" "Free inodes: %u\n" "First block: %u\n" "Block size: %u\n" "Fragment size: %u\n", str, sb->s_inodes_count, sb->s_blocks_count, sb->s_r_blocks_count, sb->s_free_blocks_count, sb->s_free_inodes_count, sb->s_first_data_block, EXT2_BLOCK_SIZE(sb), EXT2_FRAG_SIZE(sb)); free(str); if (sb->s_reserved_gdt_blocks) fprintf(f, "Reserved GDT blocks: %u\n", sb->s_reserved_gdt_blocks); fprintf(f, "Blocks per group: %u\n" "Fragments per group: %u\n" "Inodes per group: %u\n" "Inode blocks per group: %u\n", sb->s_blocks_per_group, sb->s_frags_per_group, sb->s_inodes_per_group, inode_blocks_per_group); if (sb->s_first_meta_bg) fprintf(f, "First meta block group: %u\n", sb->s_first_meta_bg); if (sb->s_mkfs_time) { tm = sb->s_mkfs_time; fprintf(f, "Filesystem created: %s", ctime(&tm)); } tm = sb->s_mtime; fprintf(f, "Last mount time: %s", sb->s_mtime ? ctime(&tm) : "n/a\n"); tm = sb->s_wtime; fprintf(f, "Last write time: %s" "Mount count: %u\n" "Maximum mount count: %d\n", ctime(&tm), sb->s_mnt_count, sb->s_max_mnt_count); tm = sb->s_lastcheck; fprintf(f, "Last checked: %s" "Check interval: %u (%s)\n", ctime(&tm), sb->s_checkinterval, interval_string(sb->s_checkinterval)); if (sb->s_checkinterval) { time_t next; next = sb->s_lastcheck + sb->s_checkinterval; fprintf(f, "Next check after: %s", ctime(&next)); } fprintf(f, "Reserved blocks uid: "); print_user(sb->s_def_resuid, f); fprintf(f, "Reserved blocks gid: "); print_group(sb->s_def_resgid, f); if (sb->s_rev_level >= EXT2_DYNAMIC_REV) { fprintf(f, "First inode: %d\n" "Inode size: %d\n", sb->s_first_ino, sb->s_inode_size); } if (!e2p_is_null_uuid(sb->s_journal_uuid)) fprintf(f, "Journal UUID: %s\n", e2p_uuid2str(sb->s_journal_uuid)); if (sb->s_journal_inum) fprintf(f, "Journal inode: %u\n", sb->s_journal_inum); if (sb->s_journal_dev) fprintf(f, "Journal device: 0x%04x\n", sb->s_journal_dev); if (sb->s_last_orphan) fprintf(f, "First orphan inode: %u\n", sb->s_last_orphan); if ((sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) || sb->s_def_hash_version) fprintf(f, "Default directory hash: %s\n", e2p_hash2string(sb->s_def_hash_version)); if (!e2p_is_null_uuid(sb->s_hash_seed)) fprintf(f, "Directory Hash Seed: %s\n", e2p_uuid2str(sb->s_hash_seed)); if (sb->s_jnl_backup_type) { fprintf(f, "Journal backup: "); if (sb->s_jnl_backup_type == 1) fprintf(f, "inode blocks\n"); else fprintf(f, "type %u\n", sb->s_jnl_backup_type); } } busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/feature.c0000644000000000000000000000762312263563520020751 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * feature.c --- convert between features and strings * * Copyright (C) 1999 Theodore Ts'o * * This file can be redistributed under the terms of the GNU Library General * Public License * */ #include #include #include #include #include #include "e2p.h" struct feature { int compat; unsigned int mask; const char *string; }; static const struct feature feature_list[] = { { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_PREALLOC, "dir_prealloc" }, { E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL, "has_journal" }, { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_IMAGIC_INODES, "imagic_inodes" }, { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXT_ATTR, "ext_attr" }, { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX, "dir_index" }, { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_RESIZE_INO, "resize_inode" }, { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER, "sparse_super" }, { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_LARGE_FILE, "large_file" }, { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION, "compression" }, { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_FILETYPE, "filetype" }, { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_RECOVER, "needs_recovery" }, { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_JOURNAL_DEV, "journal_dev" }, { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_EXTENTS, "extents" }, { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_META_BG, "meta_bg" }, { 0, 0, 0 }, }; const char *e2p_feature2string(int compat, unsigned int mask) { const struct feature *f; static char buf[20]; char fchar; int fnum; for (f = feature_list; f->string; f++) { if ((compat == f->compat) && (mask == f->mask)) return f->string; } switch (compat) { case E2P_FEATURE_COMPAT: fchar = 'C'; break; case E2P_FEATURE_INCOMPAT: fchar = 'I'; break; case E2P_FEATURE_RO_INCOMPAT: fchar = 'R'; break; default: fchar = '?'; break; } for (fnum = 0; mask >>= 1; fnum++); sprintf(buf, "FEATURE_%c%d", fchar, fnum); return buf; } int e2p_string2feature(char *string, int *compat_type, unsigned int *mask) { const struct feature *f; char *eptr; int num; for (f = feature_list; f->string; f++) { if (!strcasecmp(string, f->string)) { *compat_type = f->compat; *mask = f->mask; return 0; } } if (strncasecmp(string, "FEATURE_", 8)) return 1; switch (string[8]) { case 'c': case 'C': *compat_type = E2P_FEATURE_COMPAT; break; case 'i': case 'I': *compat_type = E2P_FEATURE_INCOMPAT; break; case 'r': case 'R': *compat_type = E2P_FEATURE_RO_INCOMPAT; break; default: return 1; } if (string[9] == 0) return 1; num = strtol(string+9, &eptr, 10); if (num > 32 || num < 0) return 1; if (*eptr) return 1; *mask = 1 << num; return 0; } static inline char *skip_over_blanks(char *cp) { while (*cp && isspace(*cp)) cp++; return cp; } static inline char *skip_over_word(char *cp) { while (*cp && !isspace(*cp) && *cp != ',') cp++; return cp; } /* * Edit a feature set array as requested by the user. The ok_array, * if set, allows the application to limit what features the user is * allowed to set or clear using this function. */ int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array) { char *cp, *buf, *next; int neg; unsigned int mask; int compat_type; buf = xstrdup(str); cp = buf; while (cp && *cp) { neg = 0; cp = skip_over_blanks(cp); next = skip_over_word(cp); if (*next == 0) next = 0; else *next = 0; switch (*cp) { case '-': case '^': neg++; case '+': cp++; break; } if (e2p_string2feature(cp, &compat_type, &mask)) return 1; if (ok_array && !(ok_array[compat_type] & mask)) return 1; if (neg) compat_array[compat_type] &= ~mask; else compat_array[compat_type] |= mask; cp = next ? next+1 : 0; } return 0; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/e2p/hashstr.c0000644000000000000000000000232112263563520020760 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * feature.c --- convert between features and strings * * Copyright (C) 1999 Theodore Ts'o * * This file can be redistributed under the terms of the GNU Library General * Public License * */ #include #include #include #include #include #include "e2p.h" struct hash { int num; const char *string; }; static const struct hash hash_list[] = { { EXT2_HASH_LEGACY, "legacy" }, { EXT2_HASH_HALF_MD4, "half_md4" }, { EXT2_HASH_TEA, "tea" }, { 0, 0 }, }; const char *e2p_hash2string(int num) { const struct hash *p; static char buf[20]; for (p = hash_list; p->string; p++) { if (num == p->num) return p->string; } sprintf(buf, "HASHALG_%d", num); return buf; } /* * Returns the hash algorithm, or -1 on error */ int e2p_string2hash(char *string) { const struct hash *p; char *eptr; int num; for (p = hash_list; p->string; p++) { if (!strcasecmp(string, p->string)) { return p->num; } } if (strncasecmp(string, "HASHALG_", 8)) return -1; if (string[8] == 0) return -1; num = strtol(string+8, &eptr, 10); if (num > 255 || num < 0) return -1; if (*eptr) return -1; return num; } busybox-1.22.1/e2fsprogs/old_e2fsprogs/fsck.h0000644000000000000000000000053712263563520017560 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * fsck.h */ #define FSCK_ATTR(x) __attribute__(x) #define EXIT_OK 0 #define EXIT_NONDESTRUCT 1 #define EXIT_DESTRUCT 2 #define EXIT_UNCORRECTED 4 #define EXIT_ERROR 8 #define EXIT_USAGE 16 #define FSCK_CANCELED 32 /* Aborted with a signal or ^C */ extern char *e2fs_set_sbin_path(void); busybox-1.22.1/e2fsprogs/lsattr.c0000644000000000000000000000546712263563520015375 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * lsattr.c - List file attributes on an ext2 file system * * Copyright (C) 1993, 1994 Remy Card * Laboratoire MASI, Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * This file can be redistributed under the terms of the GNU General * Public License */ /* * History: * 93/10/30 - Creation * 93/11/13 - Replace stat() calls by lstat() to avoid loops * 94/02/27 - Integrated in Ted's distribution * 98/12/29 - Display version info only when -V specified (G M Sipe) */ //usage:#define lsattr_trivial_usage //usage: "[-Radlv] [FILE]..." //usage:#define lsattr_full_usage "\n\n" //usage: "List file attributes on an ext2 fs\n" //usage: "\n -R Recurse" //usage: "\n -a Don't hide entries starting with ." //usage: "\n -d List directory entries instead of contents" //usage: "\n -l List long flag names" //usage: "\n -v List the file's version/generation number" #include "libbb.h" #include "e2fs_lib.h" enum { OPT_RECUR = 0x1, OPT_ALL = 0x2, OPT_DIRS_OPT = 0x4, OPT_PF_LONG = 0x8, OPT_GENERATION = 0x10, }; static void list_attributes(const char *name) { unsigned long fsflags; unsigned long generation; if (fgetflags(name, &fsflags) != 0) goto read_err; if (option_mask32 & OPT_GENERATION) { if (fgetversion(name, &generation) != 0) goto read_err; printf("%5lu ", generation); } if (option_mask32 & OPT_PF_LONG) { printf("%-28s ", name); print_e2flags(stdout, fsflags, PFOPT_LONG); bb_putchar('\n'); } else { print_e2flags(stdout, fsflags, 0); printf(" %s\n", name); } return; read_err: bb_perror_msg("reading %s", name); } static int FAST_FUNC lsattr_dir_proc(const char *dir_name, struct dirent *de, void *private UNUSED_PARAM) { struct stat st; char *path; path = concat_path_file(dir_name, de->d_name); if (lstat(path, &st) != 0) bb_perror_msg("stat %s", path); else if (de->d_name[0] != '.' || (option_mask32 & OPT_ALL)) { list_attributes(path); if (S_ISDIR(st.st_mode) && (option_mask32 & OPT_RECUR) && !DOT_OR_DOTDOT(de->d_name) ) { printf("\n%s:\n", path); iterate_on_dir(path, lsattr_dir_proc, NULL); bb_putchar('\n'); } } free(path); return 0; } static void lsattr_args(const char *name) { struct stat st; if (lstat(name, &st) == -1) { bb_perror_msg("stat %s", name); } else if (S_ISDIR(st.st_mode) && !(option_mask32 & OPT_DIRS_OPT)) { iterate_on_dir(name, lsattr_dir_proc, NULL); } else { list_attributes(name); } } int lsattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int lsattr_main(int argc UNUSED_PARAM, char **argv) { getopt32(argv, "Radlv"); argv += optind; if (!*argv) *--argv = (char*)"."; do lsattr_args(*argv++); while (*argv); return EXIT_SUCCESS; } busybox-1.22.1/e2fsprogs/e2fs_lib.c0000644000000000000000000001012412263563520015533 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * See README for additional information * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" #include "e2fs_lib.h" #define HAVE_EXT2_IOCTLS 1 #if INT_MAX == LONG_MAX #define IF_LONG_IS_SAME(...) __VA_ARGS__ #define IF_LONG_IS_WIDER(...) #else #define IF_LONG_IS_SAME(...) #define IF_LONG_IS_WIDER(...) __VA_ARGS__ #endif static void close_silently(int fd) { int e = errno; close(fd); errno = e; } /* Iterate a function on each entry of a directory */ int iterate_on_dir(const char *dir_name, int FAST_FUNC (*func)(const char *, struct dirent *, void *), void *private) { DIR *dir; struct dirent *de; dir = opendir(dir_name); if (dir == NULL) { return -1; } while ((de = readdir(dir)) != NULL) { func(dir_name, de, private); } closedir(dir); return 0; } /* Get/set a file version on an ext2 file system */ int fgetsetversion(const char *name, unsigned long *get_version, unsigned long set_version) { #if HAVE_EXT2_IOCTLS int fd, r; IF_LONG_IS_WIDER(int ver;) fd = open(name, O_RDONLY | O_NONBLOCK); if (fd == -1) return -1; if (!get_version) { IF_LONG_IS_WIDER( ver = (int) set_version; r = ioctl(fd, EXT2_IOC_SETVERSION, &ver); ) IF_LONG_IS_SAME( r = ioctl(fd, EXT2_IOC_SETVERSION, (void*)&set_version); ) } else { IF_LONG_IS_WIDER( r = ioctl(fd, EXT2_IOC_GETVERSION, &ver); *get_version = ver; ) IF_LONG_IS_SAME( r = ioctl(fd, EXT2_IOC_GETVERSION, (void*)get_version); ) } close_silently(fd); return r; #else /* ! HAVE_EXT2_IOCTLS */ errno = EOPNOTSUPP; return -1; #endif /* ! HAVE_EXT2_IOCTLS */ } /* Get/set a file flags on an ext2 file system */ int fgetsetflags(const char *name, unsigned long *get_flags, unsigned long set_flags) { #if HAVE_EXT2_IOCTLS struct stat buf; int fd, r; IF_LONG_IS_WIDER(int f;) if (stat(name, &buf) == 0 /* stat is ok */ && !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode) ) { goto notsupp; } fd = open(name, O_RDONLY | O_NONBLOCK); /* neither read nor write asked for */ if (fd == -1) return -1; if (!get_flags) { IF_LONG_IS_WIDER( f = (int) set_flags; r = ioctl(fd, EXT2_IOC_SETFLAGS, &f); ) IF_LONG_IS_SAME( r = ioctl(fd, EXT2_IOC_SETFLAGS, (void*)&set_flags); ) } else { IF_LONG_IS_WIDER( r = ioctl(fd, EXT2_IOC_GETFLAGS, &f); *get_flags = f; ) IF_LONG_IS_SAME( r = ioctl(fd, EXT2_IOC_GETFLAGS, (void*)get_flags); ) } close_silently(fd); return r; notsupp: #endif /* HAVE_EXT2_IOCTLS */ errno = EOPNOTSUPP; return -1; } /* Print file attributes on an ext2 file system */ const uint32_t e2attr_flags_value[] = { #ifdef ENABLE_COMPRESSION EXT2_COMPRBLK_FL, EXT2_DIRTY_FL, EXT2_NOCOMPR_FL, EXT2_ECOMPR_FL, #endif EXT2_INDEX_FL, EXT2_SECRM_FL, EXT2_UNRM_FL, EXT2_SYNC_FL, EXT2_DIRSYNC_FL, EXT2_IMMUTABLE_FL, EXT2_APPEND_FL, EXT2_NODUMP_FL, EXT2_NOATIME_FL, EXT2_COMPR_FL, EXT3_JOURNAL_DATA_FL, EXT2_NOTAIL_FL, EXT2_TOPDIR_FL }; const char e2attr_flags_sname[] = #ifdef ENABLE_COMPRESSION "BZXE" #endif "I" "suSDiadAcjtT"; static const char e2attr_flags_lname[] = #ifdef ENABLE_COMPRESSION "Compressed_File" "\0" "Compressed_Dirty_File" "\0" "Compression_Raw_Access" "\0" "Compression_Error" "\0" #endif "Indexed_directory" "\0" "Secure_Deletion" "\0" "Undelete" "\0" "Synchronous_Updates" "\0" "Synchronous_Directory_Updates" "\0" "Immutable" "\0" "Append_Only" "\0" "No_Dump" "\0" "No_Atime" "\0" "Compression_Requested" "\0" "Journaled_Data" "\0" "No_Tailmerging" "\0" "Top_of_Directory_Hierarchies" "\0" /* Another trailing NUL is added by compiler */; void print_e2flags(FILE *f, unsigned long flags, unsigned options) { const uint32_t *fv; const char *fn; fv = e2attr_flags_value; if (options & PFOPT_LONG) { int first = 1; fn = e2attr_flags_lname; do { if (flags & *fv) { if (!first) fputs(", ", f); fputs(fn, f); first = 0; } fv++; fn += strlen(fn) + 1; } while (*fn); if (first) fputs("---", f); } else { fn = e2attr_flags_sname; do { char c = '-'; if (flags & *fv) c = *fn; fputc(c, f); fv++; fn++; } while (*fn); } } busybox-1.22.1/e2fsprogs/chattr.c0000644000000000000000000001206712263563520015343 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * chattr.c - Change file attributes on an ext2 file system * * Copyright (C) 1993, 1994 Remy Card * Laboratoire MASI, Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * This file can be redistributed under the terms of the GNU General * Public License */ /* * History: * 93/10/30 - Creation * 93/11/13 - Replace stat() calls by lstat() to avoid loops * 94/02/27 - Integrated in Ted's distribution * 98/12/29 - Ignore symlinks when working recursively (G M Sipe) * 98/12/29 - Display version info only when -V specified (G M Sipe) */ //usage:#define chattr_trivial_usage //usage: "[-R] [-+=AacDdijsStTu] [-v VERSION] [FILE]..." //usage:#define chattr_full_usage "\n\n" //usage: "Change file attributes on an ext2 fs\n" //usage: "\nModifiers:" //usage: "\n - Remove attributes" //usage: "\n + Add attributes" //usage: "\n = Set attributes" //usage: "\nAttributes:" //usage: "\n A Don't track atime" //usage: "\n a Append mode only" //usage: "\n c Enable compress" //usage: "\n D Write dir contents synchronously" //usage: "\n d Don't backup with dump" //usage: "\n i Cannot be modified (immutable)" //usage: "\n j Write all data to journal first" //usage: "\n s Zero disk storage when deleted" //usage: "\n S Write file contents synchronously" //usage: "\n t Disable tail-merging of partial blocks with other files" //usage: "\n u Allow file to be undeleted" //usage: "\n -R Recurse" //usage: "\n -v Set the file's version/generation number" #include "libbb.h" #include "e2fs_lib.h" #define OPT_ADD 1 #define OPT_REM 2 #define OPT_SET 4 #define OPT_SET_VER 8 struct globals { unsigned long version; unsigned long af; unsigned long rf; smallint flags; smallint recursive; }; static unsigned long get_flag(char c) { const char *fp = strchr(e2attr_flags_sname_chattr, c); if (fp) return e2attr_flags_value_chattr[fp - e2attr_flags_sname_chattr]; bb_show_usage(); } static int decode_arg(const char *arg, struct globals *gp) { unsigned long *fl; char opt = *arg++; fl = &gp->af; if (opt == '-') { gp->flags |= OPT_REM; fl = &gp->rf; } else if (opt == '+') { gp->flags |= OPT_ADD; } else if (opt == '=') { gp->flags |= OPT_SET; } else return 0; while (*arg) *fl |= get_flag(*arg++); return 1; } static void change_attributes(const char *name, struct globals *gp); static int FAST_FUNC chattr_dir_proc(const char *dir_name, struct dirent *de, void *gp) { char *path = concat_subpath_file(dir_name, de->d_name); /* path is NULL if de->d_name is "." or "..", else... */ if (path) { change_attributes(path, gp); free(path); } return 0; } static void change_attributes(const char *name, struct globals *gp) { unsigned long fsflags; struct stat st; if (lstat(name, &st) != 0) { bb_perror_msg("stat %s", name); return; } if (S_ISLNK(st.st_mode) && gp->recursive) return; /* Don't try to open device files, fifos etc. We probably * ought to display an error if the file was explicitly given * on the command line (whether or not recursive was * requested). */ if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode)) return; if (gp->flags & OPT_SET_VER) if (fsetversion(name, gp->version) != 0) bb_perror_msg("setting version on %s", name); if (gp->flags & OPT_SET) { fsflags = gp->af; } else { if (fgetflags(name, &fsflags) != 0) { bb_perror_msg("reading flags on %s", name); goto skip_setflags; } /*if (gp->flags & OPT_REM) - not needed, rf is zero otherwise */ fsflags &= ~gp->rf; /*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */ fsflags |= gp->af; /* What is this? And why it's not done for SET case? */ if (!S_ISDIR(st.st_mode)) fsflags &= ~EXT2_DIRSYNC_FL; } if (fsetflags(name, fsflags) != 0) bb_perror_msg("setting flags on %s", name); skip_setflags: if (gp->recursive && S_ISDIR(st.st_mode)) iterate_on_dir(name, chattr_dir_proc, gp); } int chattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int chattr_main(int argc UNUSED_PARAM, char **argv) { struct globals g; char *arg; memset(&g, 0, sizeof(g)); /* parse the args */ while ((arg = *++argv)) { /* take care of -R and -v */ if (arg[0] == '-' && (arg[1] == 'R' || arg[1] == 'v') && !arg[2] ) { if (arg[1] == 'R') { g.recursive = 1; continue; } /* arg[1] == 'v' */ if (!*++argv) bb_show_usage(); g.version = xatoul(*argv); g.flags |= OPT_SET_VER; continue; } if (!decode_arg(arg, &g)) break; } /* run sanity checks on all the arguments given us */ if (!*argv) bb_show_usage(); if ((g.flags & OPT_SET) && (g.flags & (OPT_ADD|OPT_REM))) bb_error_msg_and_die("= is incompatible with - and +"); if (g.rf & g.af) bb_error_msg_and_die("can't set and unset a flag"); if (!g.flags) bb_error_msg_and_die("must use '-v', =, - or +"); /* now run chattr on all the files passed to us */ do change_attributes(*argv, &g); while (*++argv); return EXIT_SUCCESS; } busybox-1.22.1/e2fsprogs/e2fs_lib.h0000644000000000000000000000326412263563520015547 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * See README for additional information * * This file can be redistributed under the terms of the GNU Library General * Public License */ /* Constants and structures */ #include "bb_e2fs_defs.h" PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* Iterate a function on each entry of a directory */ int iterate_on_dir(const char *dir_name, int FAST_FUNC (*func)(const char *, struct dirent *, void *), void *private); /* Get/set a file version on an ext2 file system */ int fgetsetversion(const char *name, unsigned long *get_version, unsigned long set_version); #define fgetversion(name, version) fgetsetversion(name, version, 0) #define fsetversion(name, version) fgetsetversion(name, NULL, version) /* Get/set a file flags on an ext2 file system */ int fgetsetflags(const char *name, unsigned long *get_flags, unsigned long set_flags); #define fgetflags(name, flags) fgetsetflags(name, flags, 0) #define fsetflags(name, flags) fgetsetflags(name, NULL, flags) /* Must be 1 for compatibility with `int long_format'. */ #define PFOPT_LONG 1 /* Print file attributes on an ext2 file system */ void print_e2flags(FILE *f, unsigned long flags, unsigned options); extern const uint32_t e2attr_flags_value[]; extern const char e2attr_flags_sname[]; /* If you plan to ENABLE_COMPRESSION, see e2fs_lib.c and chattr.c - */ /* make sure that chattr doesn't accept bad options! */ #ifdef ENABLE_COMPRESSION #define e2attr_flags_value_chattr (&e2attr_flags_value[5]) #define e2attr_flags_sname_chattr (&e2attr_flags_sname[5]) #else #define e2attr_flags_value_chattr (&e2attr_flags_value[1]) #define e2attr_flags_sname_chattr (&e2attr_flags_sname[1]) #endif POP_SAVED_FUNCTION_VISIBILITY busybox-1.22.1/scripts/0000755000000000000000000000000012320365361013456 5ustar rootrootbusybox-1.22.1/scripts/memusage0000755000000000000000000000041412263563520015211 0ustar rootroot#!/bin/sh busybox=../busybox i=4000 echo "Before we started $i copies of '$busybox sleep 10':" $busybox nmeter '%t %[pn] %m' | head -3 while test $i != 0; do $busybox sleep 10 & i=$((i-1)) done sleep 1 echo "After:" $busybox nmeter '%t %[pn] %m' | head -3 busybox-1.22.1/scripts/Makefile.clean0000644000000000000000000000625212263563520016207 0ustar rootroot# ========================================================================== # Cleaning up # ========================================================================== src := $(obj) PHONY := __clean __clean: # Shorthand for $(Q)$(MAKE) scripts/Makefile.clean obj=dir # Usage: # $(Q)$(MAKE) $(clean)=dir clean := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.clean obj # The filename Kbuild has precedence over Makefile kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) -include $(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, $(kbuild-dir)/Makefile) # Figure out what we need to build from the various variables # ========================================================================== __subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y))) subdir-y += $(__subdir-y) __subdir-m := $(patsubst %/,%,$(filter %/, $(obj-m))) subdir-m += $(__subdir-m) __subdir-n := $(patsubst %/,%,$(filter %/, $(obj-n))) subdir-n += $(__subdir-n) __subdir- := $(patsubst %/,%,$(filter %/, $(obj-))) subdir- += $(__subdir-) # Subdirectories we need to descend into subdir-ym := $(sort $(subdir-y) $(subdir-m)) subdir-ymn := $(sort $(subdir-ym) $(subdir-n) $(subdir-)) # Add subdir path subdir-ymn := $(addprefix $(obj)/,$(subdir-ymn)) # build a list of files to remove, usually releative to the current # directory __clean-files := $(extra-y) $(EXTRA_TARGETS) $(always) \ $(targets) $(clean-files) \ $(host-progs) \ $(hostprogs-y) $(hostprogs-m) $(hostprogs-) # as clean-files is given relative to the current directory, this adds # a $(obj) prefix, except for absolute paths __clean-files := $(wildcard \ $(addprefix $(obj)/, $(filter-out /%, $(__clean-files))) \ $(filter /%, $(__clean-files))) # as clean-dirs is given relative to the current directory, this adds # a $(obj) prefix, except for absolute paths __clean-dirs := $(wildcard \ $(addprefix $(obj)/, $(filter-out /%, $(clean-dirs))) \ $(filter /%, $(clean-dirs))) # ========================================================================== quiet_cmd_clean = CLEAN $(obj) cmd_clean = rm -f $(__clean-files) quiet_cmd_cleandir = CLEAN $(__clean-dirs) cmd_cleandir = rm -rf $(__clean-dirs) __clean: $(subdir-ymn) ifneq ($(strip $(__clean-files)),) +$(call cmd,clean) endif ifneq ($(strip $(__clean-dirs)),) +$(call cmd,cleandir) endif ifneq ($(strip $(clean-rule)),) +$(clean-rule) endif @: # =========================================================================== # Generic stuff # =========================================================================== # Descending # --------------------------------------------------------------------------- PHONY += $(subdir-ymn) $(subdir-ymn): $(Q)$(MAKE) $(clean)=$@ # If quiet is set, only print short version of command cmd = @$(if $($(quiet)cmd_$(1)),echo ' $($(quiet)cmd_$(1))' &&) $(cmd_$(1)) # Declare the contents of the .PHONY variable as phony. We keep that # information in a variable se we can use it in if_changed and friends. .PHONY: $(PHONY) busybox-1.22.1/scripts/sample_pmap0000755000000000000000000000030112263563520015677 0ustar rootroot#!/bin/sh busybox=../busybox $busybox sleep 10 & pid=$! sleep 1 echo "Memory map of '$busybox sleep 10':" size $busybox pmap $pid | env - grep "^[0-9a-f][0-9a-f]* " | sort -r -t " " -k2,999 busybox-1.22.1/scripts/bb_release0000755000000000000000000000202112263563520015465 0ustar rootroot#!/bin/sh # Create signed release tarballs and signature files from current svn. # Since you don't have my gpg key, this doesn't do you much good, # but if I get hit by a bus the next maintainer might find this useful. # Run this in an empty directory. The VERSION= line can get confused # otherwise. #svn co svn://busybox.net/trunk/busybox cd busybox || { echo "cd busybox failed"; exit 1; } make release || { echo "make release failed"; exit 1; } cd .. VERSION=`ls busybox-*.tar.gz | sed 's/busybox-\(.*\)\.tar\.gz/\1/'` zcat busybox-$VERSION.tar.gz | bzip2 > busybox-$VERSION.tar.bz2 test -f busybox-$VERSION.tar.gz || { echo "no busybox-$VERSION.tar.gz"; exit 1; } test -f busybox-$VERSION.tar.bz2 || { echo "no busybox-$VERSION.tar.bz2"; exit 1; } signit() { echo "$1 released `date -r $1 -R` MD5: `md5sum $1` SHA1: `sha1sum $1` To verify this signature, you can obtain my public key from http://busybox.net/~vda/vda_pubkey.gpg " | gpg --clearsign > "$1.sign" } signit busybox-$VERSION.tar.gz signit busybox-$VERSION.tar.bz2 busybox-1.22.1/scripts/checkstack.pl0000755000000000000000000000767512263563520016143 0ustar rootroot#!/usr/bin/perl # Stolen from Linux kernel :) # Check the stack usage of functions # # Copyright Joern Engel # Inspired by Linus Torvalds # Original idea maybe from Keith Owens # s390 port and big speedup by Arnd Bergmann # Mips port by Juan Quintela # IA64 port via Andreas Dilger # Arm port by Holger Schurig # sh64 port by Paul Mundt # Random bits by Matt Mackall # M68k port by Geert Uytterhoeven and Andreas Schwab # # Usage: # objdump -d vmlinux | checkstack.pl [arch] # # TODO : Port to all architectures (one regex per arch) # check for arch # # $re is used for two matches: # $& (whole re) matches the complete objdump line with the stack growth # $1 (first bracket) matches the size of the stack growth # # use anything else and feel the pain ;) my (@stack, $re, $x, $xs); { my $arch = shift; if ($arch eq "") { $arch = `uname -m`; } $x = "[0-9a-f]"; # hex character $xs = "[0-9a-f ]"; # hex character or space if ($arch eq 'arm') { #c0008ffc: e24dd064 sub sp, sp, #100 ; 0x64 $re = qr/.*sub.*sp, sp, #(([0-9]{2}|[3-9])[0-9]{2})/o; } elsif ($arch eq 'blackfin') { # 52: 00 e8 03 00 LINK 0xc; $re = qr/.*LINK (0x$x{1,5});$/o; } elsif ($arch =~ /^i[3456]86$/) { #c0105234: 81 ec ac 05 00 00 sub $0x5ac,%esp $re = qr/^.*[as][du][db] \$(0x$x{1,8}),\%esp$/o; } elsif ($arch eq 'x86_64') { # 2f60: 48 81 ec e8 05 00 00 sub $0x5e8,%rsp $re = qr/^.*[as][du][db] \$(0x$x{1,8}),\%rsp$/o; } elsif ($arch eq 'ia64') { #e0000000044011fc: 01 0f fc 8c adds r12=-384,r12 $re = qr/.*adds.*r12=-(([0-9]{2}|[3-9])[0-9]{2}),r12/o; } elsif ($arch eq 'm68k') { # 2b6c: 4e56 fb70 linkw %fp,#-1168 # 1df770: defc ffe4 addaw #-28,%sp $re = qr/.*(?:linkw %fp,|addaw )#-([0-9]{1,4})(?:,%sp)?$/o; } elsif ($arch eq 'mips64') { #8800402c: 67bdfff0 daddiu sp,sp,-16 $re = qr/.*daddiu.*sp,sp,-(([0-9]{2}|[3-9])[0-9]{2})/o; } elsif ($arch eq 'mips') { #88003254: 27bdffe0 addiu sp,sp,-32 $re = qr/.*addiu.*sp,sp,-(([0-9]{2}|[3-9])[0-9]{2})/o; } elsif ($arch eq 'ppc') { #c00029f4: 94 21 ff 30 stwu r1,-208(r1) $re = qr/.*stwu.*r1,-($x{1,8})\(r1\)/o; } elsif ($arch eq 'ppc64') { #XXX $re = qr/.*stdu.*r1,-($x{1,8})\(r1\)/o; } elsif ($arch eq 'powerpc') { $re = qr/.*st[dw]u.*r1,-($x{1,8})\(r1\)/o; } elsif ($arch =~ /^s390x?$/) { # 11160: a7 fb ff 60 aghi %r15,-160 $re = qr/.*ag?hi.*\%r15,-(([0-9]{2}|[3-9])[0-9]{2})/o; } elsif ($arch =~ /^sh64$/) { #XXX: we only check for the immediate case presently, # though we will want to check for the movi/sub # pair for larger users. -- PFM. #a00048e0: d4fc40f0 addi.l r15,-240,r15 $re = qr/.*addi\.l.*r15,-(([0-9]{2}|[3-9])[0-9]{2}),r15/o; } else { print("wrong or unknown architecture\n"); exit } } sub bysize($) { my ($asize, $bsize); ($asize = $a) =~ s/.*: *(.*)$/$1/; ($bsize = $b) =~ s/.*: *(.*)$/$1/; $bsize <=> $asize } # # main() # my $funcre = qr/^$x* <(.*)>:$/; my $func; my $file, $lastslash; while (my $line = ) { if ($line =~ m/$funcre/) { $func = $1; } elsif ($line =~ m/(.*):\s*file format/) { $file = $1; $file =~ s/\.ko//; $lastslash = rindex($file, "/"); if ($lastslash != -1) { $file = substr($file, $lastslash + 1); } } elsif ($line =~ m/$re/) { my $size = $1; $size = hex($size) if ($size =~ /^0x/); if ($size > 0xf0000000) { $size = - $size; $size += 0x80000000; $size += 0x80000000; } next if ($size > 0x10000000); next if $line !~ m/^($xs*)/; my $addr = $1; $addr =~ s/ /0/g; $addr = "0x$addr"; # bbox: was: my $intro = "$addr $func [$file]:"; my $intro = "$func [$file]:"; my $padlen = 56 - length($intro); while ($padlen > 0) { $intro .= ' '; $padlen -= 8; } next if ($size < 100); push @stack, "$intro$size\n"; } } print sort bysize @stack; busybox-1.22.1/scripts/gen_build_files.sh0000755000000000000000000000471512267106022017133 0ustar rootroot#!/bin/sh # Note: was using sed OPTS CMD -- FILES # but users complain that many sed implementations # are misinterpreting --. test $# -ge 2 || { echo "Syntax: $0 SRCTREE OBJTREE"; exit 1; } # cd to objtree cd -- "$2" || { echo "Syntax: $0 SRCTREE OBJTREE"; exit 1; } # In separate objtree build, include/ might not exist yet mkdir include 2>/dev/null srctree="$1" status() { printf ' %-8s%s\n' "$1" "$2"; } gen() { status "GEN" "$@"; } chk() { status "CHK" "$@"; } generate() { # NB: data to be inserted at INSERT line is coming on stdin local src="$1" dst="$2" header="$3" #chk "${dst}" { # Need to use printf: different shells have inconsistent # rules re handling of "\n" in echo params. printf "%s\n" "${header}" # print everything up to INSERT line sed -n '/^INSERT$/ q; p' "${src}" # copy stdin to stdout cat # print everything after INSERT line sed -n '/^INSERT$/ { :l; n; p; bl }' "${src}" } >"${dst}.tmp" if ! cmp -s "${dst}" "${dst}.tmp"; then gen "${dst}" mv "${dst}.tmp" "${dst}" else rm -f "${dst}.tmp" fi } # (Re)generate include/applets.h sed -n 's@^//applet:@@p' "$srctree"/*/*.c "$srctree"/*/*/*.c \ | generate \ "$srctree/include/applets.src.h" \ "include/applets.h" \ "/* DO NOT EDIT. This file is generated from applets.src.h */" # (Re)generate include/usage.h # We add line continuation backslash after each line, # and insert empty line before each line which doesn't start # with space or tab sed -n -e 's@^//usage:\([ \t].*\)$@\1 \\@p' -e 's@^//usage:\([^ \t].*\)$@\n\1 \\@p' \ "$srctree"/*/*.c "$srctree"/*/*/*.c \ | generate \ "$srctree/include/usage.src.h" \ "include/usage.h" \ "/* DO NOT EDIT. This file is generated from usage.src.h */" # (Re)generate */Kbuild and */Config.in # We skip .dotdirs - makes git/svn/etc users happier { cd -- "$srctree" && find . -type d -not '(' -name '.?*' -prune ')'; } \ | while read -r d; do d="${d#./}" src="$srctree/$d/Kbuild.src" dst="$d/Kbuild" if test -f "$src"; then mkdir -p -- "$d" 2>/dev/null sed -n 's@^//kbuild:@@p' "$srctree/$d"/*.c \ | generate \ "${src}" "${dst}" \ "# DO NOT EDIT. This file is generated from Kbuild.src" fi src="$srctree/$d/Config.src" dst="$d/Config.in" if test -f "$src"; then mkdir -p -- "$d" 2>/dev/null sed -n 's@^//config:@@p' "$srctree/$d"/*.c \ | generate \ "${src}" "${dst}" \ "# DO NOT EDIT. This file is generated from Config.src" fi done # Last read failed. This is normal. Don't exit with its error code: exit 0 busybox-1.22.1/scripts/Kbuild.src0000644000000000000000000000037012263563520015404 0ustar rootroot### # scripts contains sources for various helper programs used throughout # the kernel for the build process. # --------------------------------------------------------------------------- # Let clean descend into subdirs subdir- += basic kconfig busybox-1.22.1/scripts/basic/0000755000000000000000000000000012320365361014537 5ustar rootrootbusybox-1.22.1/scripts/basic/fixdep.c0000644000000000000000000002333412267106022016164 0ustar rootroot/* * "Optimize" a list of dependencies as spit out by gcc -MD * for the kernel build * =========================================================================== * * Author Kai Germaschewski * Copyright 2002 by Kai Germaschewski * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * * * Introduction: * * gcc produces a very nice and correct list of dependencies which * tells make when to remake a file. * * To use this list as-is however has the drawback that virtually * every file in the kernel includes which then again * includes * * If the user re-runs make *config, linux/autoconf.h will be * regenerated. make notices that and will rebuild every file which * includes autoconf.h, i.e. basically all files. This is extremely * annoying if the user just changed CONFIG_HIS_DRIVER from n to m. * * So we play the same trick that "mkdep" played before. We replace * the dependency on linux/autoconf.h by a dependency on every config * option which is mentioned in any of the listed prequisites. * * To be exact, split-include populates a tree in include/config/, * e.g. include/config/his/driver.h, which contains the #define/#undef * for the CONFIG_HIS_DRIVER option. * * So if the user changes his CONFIG_HIS_DRIVER option, only the objects * which depend on "include/linux/config/his/driver.h" will be rebuilt, * so most likely only his driver ;-) * * The idea above dates, by the way, back to Michael E Chastain, AFAIK. * * So to get dependencies right, there are two issues: * o if any of the files the compiler read changed, we need to rebuild * o if the command line given to the compile the file changed, we * better rebuild as well. * * The former is handled by using the -MD output, the later by saving * the command line used to compile the old object and comparing it * to the one we would now use. * * Again, also this idea is pretty old and has been discussed on * kbuild-devel a long time ago. I don't have a sensibly working * internet connection right now, so I rather don't mention names * without double checking. * * This code here has been based partially based on mkdep.c, which * says the following about its history: * * Copyright abandoned, Michael Chastain, . * This is a C version of syncdep.pl by Werner Almesberger. * * * It is invoked as * * fixdep * * and will read the dependency file * * The transformed dependency snipped is written to stdout. * * It first generates a line * * cmd_ = * * and then basically copies the ..d file to stdout, in the * process filtering out the dependency on linux/autoconf.h and adding * dependencies on include/config/my/option.h for every * CONFIG_MY_OPTION encountered in any of the prequisites. * * It will also filter out all the dependencies on *.ver. We need * to make sure that the generated version checksum are globally up * to date before even starting the recursive build, so it's too late * at this point anyway. * * The algorithm to grep for "CONFIG_..." is bit unusual, but should * be fast ;-) We don't even try to really parse the header files, but * merely grep, i.e. if CONFIG_FOO is mentioned in a comment, it will * be picked up as well. It's not a problem with respect to * correctness, since that can only give too many dependencies, thus * we cannot miss a rebuild. Since people tend to not mention totally * unrelated CONFIG_ options all over the place, it's not an * efficiency problem either. * * (Note: it'd be easy to port over the complete mkdep state machine, * but I don't think the added complexity is worth it) */ /* * Note 2: if somebody writes HELLO_CONFIG_BOOM in a file, it will depend onto * CONFIG_BOOM. This could seem a bug (not too hard to fix), but please do not * fix it! Some UserModeLinux files (look at arch/um/) call CONFIG_BOOM as * UML_CONFIG_BOOM, to avoid conflicts with /usr/include/linux/autoconf.h, * through arch/um/include/uml-config.h; this fixdep "bug" makes sure that * those files will have correct dependencies. */ #include #include #include #include #include #include #include #include #include #include #include #include /* bbox: not needed #define INT_CONF ntohl(0x434f4e46) #define INT_ONFI ntohl(0x4f4e4649) #define INT_NFIG ntohl(0x4e464947) #define INT_FIG_ ntohl(0x4649475f) */ char *target; char *depfile; char *cmdline; void usage(void) { fprintf(stderr, "Usage: fixdep \n"); exit(1); } /* * Print out the commandline prefixed with cmd_ := */ void print_cmdline(void) { printf("cmd_%s := %s\n\n", target, cmdline); } char * str_config = NULL; int size_config = 0; int len_config = 0; /* * Grow the configuration string to a desired length. * Usually the first growth is plenty. */ void grow_config(int len) { while (len_config + len > size_config) { if (size_config == 0) size_config = 2048; str_config = realloc(str_config, size_config *= 2); if (str_config == NULL) { perror("fixdep:malloc"); exit(1); } } } /* * Lookup a value in the configuration string. */ int is_defined_config(const char * name, int len) { const char * pconfig; const char * plast = str_config + len_config - len; for ( pconfig = str_config + 1; pconfig < plast; pconfig++ ) { if (pconfig[ -1] == '\n' && pconfig[len] == '\n' && !memcmp(pconfig, name, len)) return 1; } return 0; } /* * Add a new value to the configuration string. */ void define_config(const char * name, int len) { grow_config(len + 1); memcpy(str_config+len_config, name, len); len_config += len; str_config[len_config++] = '\n'; } /* * Clear the set of configuration strings. */ void clear_config(void) { len_config = 0; define_config("", 0); } /* * Record the use of a CONFIG_* word. */ void use_config(char *m, int slen) { char *s = alloca(slen+1); char *p; if (is_defined_config(m, slen)) return; define_config(m, slen); memcpy(s, m, slen); s[slen] = 0; for (p = s; p < s + slen; p++) { if (*p == '_') *p = '/'; else *p = tolower((int)*p); } printf(" $(wildcard include/config/%s.h) \\\n", s); } void parse_config_file(char *map, size_t len) { /* modified for bbox */ char *end_3 = map + len - 3; /* 3 == length of "IF_" */ char *end_7 = map + len - 7; char *p = map; char *q; int off; for (; p <= end_3; p++) { /* Find next identifier's beginning */ if (!(isalnum(*p) || *p == '_')) continue; /* Check it */ if (p < end_7 && p[6] == '_') { if (!memcmp(p, "CONFIG", 6)) goto conf7; if (!memcmp(p, "ENABLE", 6)) goto conf7; if (!memcmp(p, "IF_NOT", 6)) goto conf7; } /* we have at least 3 chars because of p <= end_3 */ /*if (!memcmp(p, "IF_", 3)) ...*/ if (p[0] == 'I' && p[1] == 'F' && p[2] == '_') { off = 3; goto conf; } /* This identifier is not interesting, skip it */ while (p <= end_3 && (isalnum(*p) || *p == '_')) p++; continue; conf7: off = 7; conf: p += off; for (q = p; q < end_3+3; q++) { if (!(isalnum(*q) || *q == '_')) break; } if (q != p) { use_config(p, q-p); } } } /* test is s ends in sub */ int strrcmp(char *s, char *sub) { int slen = strlen(s); int sublen = strlen(sub); if (sublen > slen) return 1; return memcmp(s + slen - sublen, sub, sublen); } void do_config_file(char *filename) { struct stat st; int fd; void *map; fd = open(filename, O_RDONLY); if (fd < 0) { fprintf(stderr, "fixdep: "); perror(filename); exit(2); } fstat(fd, &st); if (st.st_size == 0) { close(fd); return; } map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if ((long) map == -1) { perror("fixdep: mmap"); close(fd); return; } parse_config_file(map, st.st_size); munmap(map, st.st_size); close(fd); } void parse_dep_file(void *map, size_t len) { char *m = map; char *end = m + len; char *p; char *s = alloca(len); p = memchr(m, ':', len); if (!p) { fprintf(stderr, "fixdep: parse error\n"); exit(1); } memcpy(s, m, p-m); s[p-m] = 0; printf("deps_%s := \\\n", target); m = p+1; clear_config(); while (m < end) { while (m < end && (*m == ' ' || *m == '\\' || *m == '\n' || *m == '\r')) m++; p = m; while (p < end && *p != ' ') p++; if (p == end) { do p--; while (!isalnum(*p)); p++; } memcpy(s, m, p-m); s[p-m] = 0; if (strrcmp(s, "include/autoconf.h") && strrcmp(s, "arch/um/include/uml-config.h") && strrcmp(s, ".ver")) { printf(" %s \\\n", s); do_config_file(s); } m = p + 1; } printf("\n%s: $(deps_%s)\n\n", target, target); printf("$(deps_%s):\n", target); } void print_deps(void) { struct stat st; int fd; void *map; fd = open(depfile, O_RDONLY); if (fd < 0) { fprintf(stderr, "fixdep: "); perror(depfile); exit(2); } fstat(fd, &st); if (st.st_size == 0) { fprintf(stderr,"fixdep: %s is empty\n",depfile); close(fd); return; } map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if ((long) map == -1) { perror("fixdep: mmap"); close(fd); return; } parse_dep_file(map, st.st_size); munmap(map, st.st_size); close(fd); } void traps(void) { /* bbox: not needed static char test[] __attribute__((aligned(sizeof(int)))) = "CONF"; if (*(int *)test != INT_CONF) { fprintf(stderr, "fixdep: sizeof(int) != 4 or wrong endianess? %#x\n", *(int *)test); exit(2); } */ } int main(int argc, char **argv) { traps(); if (argc != 4) usage(); depfile = argv[1]; target = argv[2]; cmdline = argv[3]; print_cmdline(); print_deps(); return 0; } busybox-1.22.1/scripts/basic/docproc.c0000644000000000000000000002306612267106022016340 0ustar rootroot/* * docproc is a simple preprocessor for the template files * used as placeholders for the kernel internal documentation. * docproc is used for documentation-frontend and * dependency-generator. * The two usages have in common that they require * some knowledge of the .tmpl syntax, therefore they * are kept together. * * documentation-frontend * Scans the template file and call kernel-doc for * all occurrences of ![EIF]file * Beforehand each referenced file are scanned for * any exported sympols "EXPORT_SYMBOL()" statements. * This is used to create proper -function and * -nofunction arguments in calls to kernel-doc. * Usage: docproc doc file.tmpl * * dependency-generator: * Scans the template file and list all files * referenced in a format recognized by make. * Usage: docproc depend file.tmpl * Writes dependency information to stdout * in the following format: * file.tmpl src.c src2.c * The filenames are obtained from the following constructs: * !Efilename * !Ifilename * !Dfilename * !Ffilename * */ #include #include #include #include #include #include #include #include #include /* exitstatus is used to keep track of any failing calls to kernel-doc, * but execution continues. */ int exitstatus = 0; typedef void DFL(char *); DFL *defaultline; typedef void FILEONLY(char * file); FILEONLY *internalfunctions; FILEONLY *externalfunctions; FILEONLY *symbolsonly; typedef void FILELINE(char * file, char * line); FILELINE * singlefunctions; FILELINE * entity_system; #define MAXLINESZ 2048 #define MAXFILES 250 #define KERNELDOCPATH "scripts/" #define KERNELDOC "kernel-doc" #define DOCBOOK "-docbook" #define FUNCTION "-function" #define NOFUNCTION "-nofunction" void usage (void) { fprintf(stderr, "Usage: docproc {doc|depend} file\n"); fprintf(stderr, "Input is read from file.tmpl. Output is sent to stdout\n"); fprintf(stderr, "doc: frontend when generating kernel documentation\n"); fprintf(stderr, "depend: generate list of files referenced within file\n"); } /* * Execute kernel-doc with parameters givin in svec */ void exec_kernel_doc(char **svec) { pid_t pid; int ret; char *real_filename; int rflen; /* Make sure output generated so far are flushed */ fflush(stdout); switch(pid=fork()) { case -1: perror("vfork"+1); exit(1); case 0: rflen = strlen(getenv("SRCTREE")); rflen += strlen(KERNELDOCPATH KERNELDOC); real_filename = alloca(rflen + 1); strcpy(real_filename, getenv("SRCTREE")); strcat(real_filename, KERNELDOCPATH KERNELDOC); execvp(real_filename, svec); fprintf(stderr, "exec "); perror(real_filename); exit(1); default: waitpid(pid, &ret ,0); } if (WIFEXITED(ret)) exitstatus |= WEXITSTATUS(ret); else exitstatus = 0xff; } /* Types used to create list of all exported symbols in a number of files */ struct symbols { char *name; }; struct symfile { char *filename; struct symbols *symbollist; int symbolcnt; }; struct symfile symfilelist[MAXFILES]; int symfilecnt = 0; void add_new_symbol(struct symfile *sym, char * symname) { sym->symbollist = realloc(sym->symbollist, (sym->symbolcnt + 1) * sizeof(char *)); sym->symbollist[sym->symbolcnt++].name = strdup(symname); } /* Add a filename to the list */ struct symfile * add_new_file(char * filename) { symfilelist[symfilecnt++].filename = strdup(filename); return &symfilelist[symfilecnt - 1]; } /* Check if file already are present in the list */ struct symfile * filename_exist(char * filename) { int i; for (i=0; i < symfilecnt; i++) if (strcmp(symfilelist[i].filename, filename) == 0) return &symfilelist[i]; return NULL; } /* * List all files referenced within the template file. * Files are separated by tabs. */ void adddep(char * file) { printf("\t%s", file); } void adddep2(char * file, char * line) { line = line; adddep(file); } void noaction(char * line) { line = line; } void noaction2(char * file, char * line) { file = file; line = line; } /* Echo the line without further action */ void printline(char * line) { printf("%s", line); } /* * Find all symbols exported with EXPORT_SYMBOL and EXPORT_SYMBOL_GPL * in filename. * All symbols located are stored in symfilelist. */ void find_export_symbols(char * filename) { FILE * fp; struct symfile *sym; char line[MAXLINESZ]; if (filename_exist(filename) == NULL) { int rflen = strlen(getenv("SRCTREE")) + strlen(filename); char *real_filename = alloca(rflen + 1); strcpy(real_filename, getenv("SRCTREE")); strcat(real_filename, filename); sym = add_new_file(filename); fp = fopen(real_filename, "r"); if (fp == NULL) { fprintf(stderr, "docproc: "); perror(real_filename); } while (fgets(line, MAXLINESZ, fp)) { char *p; char *e; if (((p = strstr(line, "EXPORT_SYMBOL_GPL")) != 0) || ((p = strstr(line, "EXPORT_SYMBOL")) != 0)) { /* Skip EXPORT_SYMBOL{_GPL} */ while (isalnum(*p) || *p == '_') p++; /* Remove paranteses and additional ws */ while (isspace(*p)) p++; if (*p != '(') continue; /* Syntax error? */ else p++; while (isspace(*p)) p++; e = p; while (isalnum(*e) || *e == '_') e++; *e = '\0'; add_new_symbol(sym, p); } } fclose(fp); } } /* * Document all external or internal functions in a file. * Call kernel-doc with following parameters: * kernel-doc -docbook -nofunction function_name1 filename * function names are obtained from all the src files * by find_export_symbols. * intfunc uses -nofunction * extfunc uses -function */ void docfunctions(char * filename, char * type) { int i,j; int symcnt = 0; int idx = 0; char **vec; for (i=0; i <= symfilecnt; i++) symcnt += symfilelist[i].symbolcnt; vec = malloc((2 + 2 * symcnt + 2) * sizeof(char*)); if (vec == NULL) { perror("docproc: "); exit(1); } vec[idx++] = KERNELDOC; vec[idx++] = DOCBOOK; for (i=0; i < symfilecnt; i++) { struct symfile * sym = &symfilelist[i]; for (j=0; j < sym->symbolcnt; j++) { vec[idx++] = type; vec[idx++] = sym->symbollist[j].name; } } vec[idx++] = filename; vec[idx] = NULL; printf("\n", filename); exec_kernel_doc(vec); fflush(stdout); free(vec); } void intfunc(char * filename) { docfunctions(filename, NOFUNCTION); } void extfunc(char * filename) { docfunctions(filename, FUNCTION); } /* * Document specific function(s) in a file. * Call kernel-doc with the following parameters: * kernel-doc -docbook -function function1 [-function function2] */ void singfunc(char * filename, char * line) { char *vec[200]; /* Enough for specific functions */ int i, idx = 0; int startofsym = 1; vec[idx++] = KERNELDOC; vec[idx++] = DOCBOOK; /* Split line up in individual parameters preceeded by FUNCTION */ for (i=0; line[i]; i++) { if (isspace(line[i])) { line[i] = '\0'; startofsym = 1; continue; } if (startofsym) { startofsym = 0; vec[idx++] = FUNCTION; vec[idx++] = &line[i]; } } vec[idx++] = filename; vec[idx] = NULL; exec_kernel_doc(vec); } /* * Parse file, calling action specific functions for: * 1) Lines containing !E * 2) Lines containing !I * 3) Lines containing !D * 4) Lines containing !F * 5) Default lines - lines not matching the above */ void parse_file(FILE *infile) { char line[MAXLINESZ]; char * s; while (fgets(line, MAXLINESZ, infile)) { if (line[0] == '!') { s = line + 2; switch (line[1]) { case 'E': while (*s && !isspace(*s)) s++; *s = '\0'; externalfunctions(line+2); break; case 'I': while (*s && !isspace(*s)) s++; *s = '\0'; internalfunctions(line+2); break; case 'D': while (*s && !isspace(*s)) s++; *s = '\0'; symbolsonly(line+2); break; case 'F': /* filename */ while (*s && !isspace(*s)) s++; *s++ = '\0'; /* function names */ while (isspace(*s)) s++; singlefunctions(line +2, s); break; default: defaultline(line); } } else { defaultline(line); } } fflush(stdout); } int main(int argc, char **argv) { FILE * infile; if (argc != 3) { usage(); exit(1); } /* Open file, exit on error */ infile = fopen(argv[2], "r"); if (infile == NULL) { fprintf(stderr, "docproc: "); perror(argv[2]); exit(2); } if (strcmp("doc", argv[1]) == 0) { /* Need to do this in two passes. * First pass is used to collect all symbols exported * in the various files. * Second pass generate the documentation. * This is required because function are declared * and exported in different files :-(( */ /* Collect symbols */ defaultline = noaction; internalfunctions = find_export_symbols; externalfunctions = find_export_symbols; symbolsonly = find_export_symbols; singlefunctions = noaction2; parse_file(infile); /* Rewind to start from beginning of file again */ fseek(infile, 0, SEEK_SET); defaultline = printline; internalfunctions = intfunc; externalfunctions = extfunc; symbolsonly = printline; singlefunctions = singfunc; parse_file(infile); } else if (strcmp("depend", argv[1]) == 0) { /* Create first part of dependency chain * file.tmpl */ printf("%s\t", argv[2]); defaultline = noaction; internalfunctions = adddep; externalfunctions = adddep; symbolsonly = adddep; singlefunctions = adddep2; parse_file(infile); printf("\n"); } else { fprintf(stderr, "Unknown option: %s\n", argv[1]); exit(1); } fclose(infile); fflush(stdout); return exitstatus; } busybox-1.22.1/scripts/basic/split-include.c0000644000000000000000000001302112263563520017457 0ustar rootroot/* * split-include.c * * Copyright abandoned, Michael Chastain, . * This is a C version of syncdep.pl by Werner Almesberger. * * This program takes autoconf.h as input and outputs a directory full * of one-line include files, merging onto the old values. * * Think of the configuration options as key-value pairs. Then there * are five cases: * * key old value new value action * * KEY-1 VALUE-1 VALUE-1 leave file alone * KEY-2 VALUE-2A VALUE-2B write VALUE-2B into file * KEY-3 - VALUE-3 write VALUE-3 into file * KEY-4 VALUE-4 - write an empty file * KEY-5 (empty) - leave old empty file alone */ #include #include #include #include #include #include #include #include #include #define ERROR_EXIT(strExit) \ { \ const int errnoSave = errno; \ fprintf(stderr, "%s: ", str_my_name); \ errno = errnoSave; \ perror((strExit)); \ exit(1); \ } int main(int argc, const char * argv []) { const char * str_my_name; const char * str_file_autoconf; const char * str_dir_config; FILE * fp_config; FILE * fp_target; FILE * fp_find; int buffer_size; char * line; char * old_line; char * list_target; char * ptarget; struct stat stat_buf; /* Check arg count. */ if (argc != 3) { fprintf(stderr, "%s: wrong number of arguments.\n", argv[0]); exit(1); } str_my_name = argv[0]; str_file_autoconf = argv[1]; str_dir_config = argv[2]; /* Find a buffer size. */ if (stat(str_file_autoconf, &stat_buf) != 0) ERROR_EXIT(str_file_autoconf); buffer_size = 2 * stat_buf.st_size + 4096; /* Allocate buffers. */ if ( (line = malloc(buffer_size)) == NULL || (old_line = malloc(buffer_size)) == NULL || (list_target = malloc(buffer_size)) == NULL ) ERROR_EXIT(str_file_autoconf); /* Open autoconfig file. */ if ((fp_config = fopen(str_file_autoconf, "r")) == NULL) ERROR_EXIT(str_file_autoconf); /* Make output directory if needed. */ if (stat(str_dir_config, &stat_buf) != 0) { if (mkdir(str_dir_config, 0755) != 0) ERROR_EXIT(str_dir_config); } /* Change to output directory. */ if (chdir(str_dir_config) != 0) ERROR_EXIT(str_dir_config); /* Put initial separator into target list. */ ptarget = list_target; *ptarget++ = '\n'; /* Read config lines. */ while (fgets(line, buffer_size, fp_config)) { const char * str_config; int is_same; int itarget; if (line[0] != '#') continue; if ((str_config = strstr(line, " CONFIG_")) == NULL) continue; /* We found #define CONFIG_foo or #undef CONFIG_foo. * Make the output file name. */ str_config += sizeof(" CONFIG_") - 1; for (itarget = 0; !isspace(str_config[itarget]); itarget++) { int c = (unsigned char) str_config[itarget]; if (isupper(c)) c = tolower(c); if (c == '_') c = '/'; ptarget[itarget] = c; } ptarget[itarget++] = '.'; ptarget[itarget++] = 'h'; ptarget[itarget++] = '\0'; /* Check for existing file. */ is_same = 0; if ((fp_target = fopen(ptarget, "r")) != NULL) { fgets(old_line, buffer_size, fp_target); if (fclose(fp_target) != 0) ERROR_EXIT(ptarget); if (!strcmp(line, old_line)) is_same = 1; } if (!is_same) { /* Auto-create directories. */ int islash; for (islash = 0; islash < itarget; islash++) { if (ptarget[islash] == '/') { ptarget[islash] = '\0'; if (stat(ptarget, &stat_buf) != 0 && mkdir(ptarget, 0755) != 0) ERROR_EXIT( ptarget ); ptarget[islash] = '/'; } } /* Write the file. */ if ((fp_target = fopen(ptarget, "w")) == NULL) ERROR_EXIT(ptarget); fputs(line, fp_target); if (ferror(fp_target) || fclose(fp_target) != 0) ERROR_EXIT(ptarget); } /* Update target list */ ptarget += itarget; *(ptarget-1) = '\n'; } /* * Close autoconfig file. * Terminate the target list. */ if (fclose(fp_config) != 0) ERROR_EXIT(str_file_autoconf); *ptarget = '\0'; /* * Fix up existing files which have no new value. * This is Case 4 and Case 5. * * I re-read the tree and filter it against list_target. * This is crude. But it avoids data copies. Also, list_target * is compact and contiguous, so it easily fits into cache. * * Notice that list_target contains strings separated by \n, * with a \n before the first string and after the last. * fgets gives the incoming names a terminating \n. * So by having an initial \n, strstr will find exact matches. */ fp_find = popen("find * -type f -name \"*.h\" -print", "r"); if (fp_find == 0) ERROR_EXIT( "find" ); line[0] = '\n'; while (fgets(line+1, buffer_size, fp_find)) { if (strstr(list_target, line) == NULL) { /* * This is an old file with no CONFIG_* flag in autoconf.h. */ /* First strip the \n. */ line[strlen(line)-1] = '\0'; /* Grab size. */ if (stat(line+1, &stat_buf) != 0) ERROR_EXIT(line); /* If file is not empty, make it empty and give it a fresh date. */ if (stat_buf.st_size != 0) { if ((fp_target = fopen(line+1, "w")) == NULL) ERROR_EXIT(line); if (fclose(fp_target) != 0) ERROR_EXIT(line); } } } if (pclose(fp_find) != 0) ERROR_EXIT("find"); return 0; } busybox-1.22.1/scripts/basic/Makefile0000644000000000000000000000153312263563520016204 0ustar rootroot### # Makefile.basic list the most basic programs used during the build process. # The programs listed herein is what is needed to do the basic stuff, # such as splitting .config and fix dependency file. # This initial step is needed to avoid files to be recompiled # when busybox configuration changes (which is what happens when # .config is included by main Makefile. # --------------------------------------------------------------------------- # fixdep: Used to generate dependency information during build process # split-include: Divide all config symbols up in a number of files in # include/config/... # docproc: Used in Documentation/docbook hostprogs-y := fixdep split-include docproc always := $(hostprogs-y) # fixdep is needed to compile other host programs $(addprefix $(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep busybox-1.22.1/scripts/trylink0000755000000000000000000002142312263563520015105 0ustar rootroot#!/bin/sh debug=false # Linker flags used: # # Informational: # --warn-common # -Map $EXE.map # --verbose # # Optimizations: # --sort-common reduces padding # --sort-section alignment reduces padding # --gc-sections throws out unused sections, # does not work for shared libs # -On Not used, maybe useful? # # List of files to link: # $l_list == --start-group -llib1 -llib2 --end-group # --start-group $O_FILES $A_FILES --end-group # # Shared library link: # -shared self-explanatory # -fPIC position-independent code # --enable-new-dtags ? # -z,combreloc ? # -soname="libbusybox.so.$BB_VER" # --undefined=lbb_main Seed name to start pulling from # (otherwise we'll need --whole-archive) # -static Not used, but may be useful! manpage: # "... This option can be used with -shared. # Doing so means that a shared library # is being created but that all of the library's # external references must be resolved by pulling # in entries from static libraries." try() { printf "%s\n" "Output of:" >$EXE.out printf "%s\n" "$*" >>$EXE.out printf "%s\n" "==========" >>$EXE.out $debug && echo "Trying: $*" $@ >>$EXE.out 2>&1 return $? } check_cc() { local tempname="/tmp/temp.$$.$RANDOM" # Can use "-o /dev/null", but older gcc tend to *unlink it* on failure! :( # "-xc": C language. "/dev/null" is an empty source file. if $CC $1 -shared -xc /dev/null -o "$tempname".o >/dev/null 2>&1; then echo "$1"; else echo "$2"; fi rm "$tempname".o 2>/dev/null } check_libc_is_glibc() { local tempname="/tmp/temp.$$.$RANDOM" echo "\ #include /* Apparently uclibc defines __GLIBC__ (compat trick?). Oh well. */ #if defined(__GLIBC__) && !defined(__UCLIBC__) syntax error here #endif " >"$tempname".c if $CC "$tempname".c -c -o "$tempname".o >/dev/null 2>&1; then echo "$2"; else echo "$1"; fi rm "$tempname".c "$tempname".o 2>/dev/null } EXE="$1" CC="$2" CFLAGS="$3" LDFLAGS="$4" O_FILES="$5" A_FILES="$6" LDLIBS="$7" # The --sort-section option is not supported by older versions of ld SORT_SECTION=`check_cc "-Wl,--sort-section,alignment" ""` START_GROUP="-Wl,--start-group" END_GROUP="-Wl,--end-group" INFO_OPTS="-Wl,--warn-common -Wl,-Map,$EXE.map -Wl,--verbose" # gold may not support --sort-common (yet) SORT_COMMON=`check_cc "-Wl,--sort-common" ""` # Static linking against glibc produces buggy executables # (glibc does not cope well with ld --gc-sections). # See sources.redhat.com/bugzilla/show_bug.cgi?id=3400 # Note that glibc is unsuitable for static linking anyway. # We are removing -Wl,--gc-sections from link command line. GC_SECTIONS=`( . ./.config if test x"$CONFIG_STATIC" = x"y"; then check_libc_is_glibc "" "-Wl,--gc-sections" else echo "-Wl,--gc-sections" fi )` # The --gc-sections option is not supported by older versions of ld if test -n "$GC_SECTIONS"; then GC_SECTIONS=`check_cc "$GC_SECTIONS" ""` fi # Sanitize lib list (dups, extra spaces etc) LDLIBS=`echo "$LDLIBS" | xargs -n1 | sort | uniq | xargs` # First link with all libs. If it fails, bail out echo "Trying libraries: $LDLIBS" # "lib1 lib2 lib3" -> "-llib1 -llib2 -llib3" l_list=`echo " $LDLIBS " | sed -e 's: \([^- ][^ ]*\): -l\1:g'` test "x$l_list" != "x" && l_list="$START_GROUP $l_list $END_GROUP" try $CC $CFLAGS $LDFLAGS \ -o $EXE \ $SORT_COMMON \ $SORT_SECTION \ $GC_SECTIONS \ $START_GROUP $O_FILES $A_FILES $END_GROUP \ $l_list \ || { echo "Failed: $l_list" cat $EXE.out exit 1 } # Now try to remove each lib and build without it. # Stop when no lib can be removed. while test "$LDLIBS"; do $debug && echo "Trying libraries: $LDLIBS" all_needed=true last_needed=false for one in $LDLIBS; do without_one=`echo " $LDLIBS " | sed "s/ $one / /g" | xargs` # "lib1 lib2 lib3" -> "-llib1 -llib2 -llib3" l_list=`echo " $without_one " | sed -e 's: \([^- ][^ ]*\): -l\1:g'` test x"$l_list" != x"" && l_list="$START_GROUP $l_list $END_GROUP" $debug && echo "Trying -l options: '$l_list'" try $CC $CFLAGS $LDFLAGS \ -o $EXE \ $SORT_COMMON \ $SORT_SECTION \ $GC_SECTIONS \ $START_GROUP $O_FILES $A_FILES $END_GROUP \ $l_list if test $? = 0; then echo " Library $one is not needed, excluding it" LDLIBS="$without_one" all_needed=false last_needed=false else echo " Library $one is needed, can't exclude it (yet)" last_needed=true fi done # All libs were needed, can't remove any $all_needed && break # Optimization: was the last tried lib needed? if $last_needed; then # Was it the only one lib left? Don't test again then. { echo "$LDLIBS" | grep -q ' '; } || break fi done # Make the binary with final, minimal list of libs echo "Final link with: ${LDLIBS:-}" l_list=`echo " $LDLIBS " | sed -e 's: \([^- ][^ ]*\): -l\1:g'` test "x$l_list" != "x" && l_list="$START_GROUP $l_list $END_GROUP" # --verbose gives us gobs of info to stdout (e.g. linker script used) if ! test -f busybox_ldscript; then try $CC $CFLAGS $LDFLAGS \ -o $EXE \ $SORT_COMMON \ $SORT_SECTION \ $GC_SECTIONS \ $START_GROUP $O_FILES $A_FILES $END_GROUP \ $l_list \ $INFO_OPTS \ || { cat $EXE.out exit 1 } else echo "Custom linker script 'busybox_ldscript' found, using it" # Add SORT_BY_ALIGNMENT to linker script (found in $EXE.out): # .rodata : { *(.rodata SORT_BY_ALIGNMENT(.rodata.*) .gnu.linkonce.r.*) } # *(.data SORT_BY_ALIGNMENT(.data.*) .gnu.linkonce.d.*) # *(.bss SORT_BY_ALIGNMENT(.bss.*) .gnu.linkonce.b.*) # This will eliminate most of the padding (~3kb). # Hmm, "ld --sort-section alignment" should do it too. try $CC $CFLAGS $LDFLAGS \ -o $EXE \ $SORT_COMMON \ $SORT_SECTION \ $GC_SECTIONS \ -Wl,-T,busybox_ldscript \ $START_GROUP $O_FILES $A_FILES $END_GROUP \ $l_list \ $INFO_OPTS \ || { cat $EXE.out exit 1 } fi . ./.config sharedlib_dir="0_lib" if test "$CONFIG_BUILD_LIBBUSYBOX" = y; then mkdir "$sharedlib_dir" 2>/dev/null test -d "$sharedlib_dir" || { echo "Cannot make directory $sharedlib_dir" exit 1 } ln -s "libbusybox.so.$BB_VER" "$sharedlib_dir"/libbusybox.so 2>/dev/null EXE="$sharedlib_dir/libbusybox.so.${BB_VER}_unstripped" try $CC $CFLAGS $LDFLAGS \ -o $EXE \ -shared -fPIC \ -Wl,--enable-new-dtags \ -Wl,-z,combreloc \ -Wl,-soname="libbusybox.so.$BB_VER" \ -Wl,--undefined=lbb_main \ $SORT_COMMON \ $SORT_SECTION \ $START_GROUP $A_FILES $END_GROUP \ $l_list \ $INFO_OPTS \ || { echo "Linking $EXE failed" cat $EXE.out exit 1 } $STRIP -s --remove-section=.note --remove-section=.comment $EXE -o "$sharedlib_dir/libbusybox.so.$BB_VER" chmod a+x "$sharedlib_dir/libbusybox.so.$BB_VER" echo "libbusybox: $sharedlib_dir/libbusybox.so.$BB_VER" fi if test "$CONFIG_FEATURE_SHARED_BUSYBOX" = y; then EXE="$sharedlib_dir/busybox_unstripped" try $CC $CFLAGS $LDFLAGS \ -o $EXE \ $SORT_COMMON \ $SORT_SECTION \ $GC_SECTIONS \ $START_GROUP $O_FILES $END_GROUP \ -L"$sharedlib_dir" -lbusybox \ $l_list \ $INFO_OPTS \ || { echo "Linking $EXE failed" cat $EXE.out exit 1 } $STRIP -s --remove-section=.note --remove-section=.comment $EXE -o "$sharedlib_dir/busybox" echo "busybox linked against libbusybox: $sharedlib_dir/busybox" fi if test "$CONFIG_FEATURE_INDIVIDUAL" = y; then echo "Linking individual applets against libbusybox (see $sharedlib_dir/*)" gcc -DNAME_MAIN_CNAME -E -include include/autoconf.h include/applets.h \ | grep -v "^#" \ | grep -v "^$" \ > applet_lst.tmp while read name main junk; do echo "\ void lbb_prepare(const char *applet, char **argv); int $main(int argc, char **argv); int main(int argc, char **argv) { lbb_prepare(\"$name\", argv); return $main(argc, argv); } " >"$sharedlib_dir/applet.c" EXE="$sharedlib_dir/$name" try $CC $CFLAGS $LDFLAGS "$sharedlib_dir/applet.c" \ -o $EXE \ $SORT_COMMON \ $SORT_SECTION \ $GC_SECTIONS \ -L"$sharedlib_dir" -lbusybox \ -Wl,--warn-common \ || { echo "Linking $EXE failed" cat $EXE.out exit 1 } rm -- "$sharedlib_dir/applet.c" $EXE.out $STRIP -s --remove-section=.note --remove-section=.comment $EXE done /dev/null exit 0 # or else we may confuse make busybox-1.22.1/scripts/mkdiff_obj0000755000000000000000000000202112263563520015474 0ustar rootroot#!/bin/sh usage() { echo "Usage: ${0##*/} DIR1 DIR2" echo echo "Compares all object files recursivelty found in DIR1 and DIR2." echo "Prints diff of their disassembly." echo exit $1 } filter() { # sed removes " address: " prefixes which mess up diff sed $'s/^\\(\t*\\)[ ]*[0-9a-f][0-9a-f]*:[ \t]*/\\1/' \ | sed 's/__GI_//g' } test -d "$1" || usage 1 test -d "$2" || usage 1 { ( cd "$1" || exit 1 find -name '*.o' # -o -name '*.os' # -o -name '*.so' ) ( cd "$2" || exit 1 find -name '*.o' # -o -name '*.os' # -o -name '*.so' ) } | sed 's:^\./::' | sort | uniq | \ ( while IFS='' read -r oname; do if ! test -f "$1/$oname"; then echo "Only $2/$oname" continue fi if ! test -f "$2/$oname"; then echo "Only $1/$oname" continue fi diff -q -- "$1/$oname" "$2/$oname" >/dev/null && continue (cd "$1" && { size "$oname"; objdump -dr "$oname" | filter; } >"$oname.disasm") (cd "$2" && { size "$oname"; objdump -dr "$oname" | filter; } >"$oname.disasm") diff -u -- "$1/$oname.disasm" "$2/$oname.disasm" done ) busybox-1.22.1/scripts/Makefile.lib0000644000000000000000000001532212263563520015671 0ustar rootroot# Backward compatibility - to be removed... extra-y += $(EXTRA_TARGETS) # Figure out what we need to build from the various variables # =========================================================================== # When an object is listed to be built compiled-in and modular, # only build the compiled-in version obj-m := $(filter-out $(obj-y),$(obj-m)) # Libraries are always collected in one lib file. # Filter out objects already built-in lib-y := $(filter-out $(obj-y), $(sort $(lib-y) $(lib-m))) # Handle objects in subdirs # --------------------------------------------------------------------------- # o if we encounter foo/ in $(obj-y), replace it by foo/built-in.o # and add the directory to the list of dirs to descend into: $(subdir-y) # o if we encounter foo/ in $(obj-m), remove it from $(obj-m) # and add the directory to the list of dirs to descend into: $(subdir-m) __subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y))) subdir-y += $(__subdir-y) __subdir-m := $(patsubst %/,%,$(filter %/, $(obj-m))) subdir-m += $(__subdir-m) obj-y := $(patsubst %/, %/built-in.o, $(obj-y)) obj-m := $(filter-out %/, $(obj-m)) # Subdirectories we need to descend into subdir-ym := $(sort $(subdir-y) $(subdir-m)) # if $(foo-objs) exists, foo.o is a composite object multi-used-y := $(sort $(foreach m,$(obj-y), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))), $(m)))) multi-used-m := $(sort $(foreach m,$(obj-m), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))), $(m)))) multi-used := $(multi-used-y) $(multi-used-m) single-used-m := $(sort $(filter-out $(multi-used-m),$(obj-m))) # Build list of the parts of our composite objects, our composite # objects depend on those (obviously) multi-objs-y := $(foreach m, $(multi-used-y), $($(m:.o=-objs)) $($(m:.o=-y))) multi-objs-m := $(foreach m, $(multi-used-m), $($(m:.o=-objs)) $($(m:.o=-y))) multi-objs := $(multi-objs-y) $(multi-objs-m) # $(subdir-obj-y) is the list of objects in $(obj-y) which do not live # in the local directory subdir-obj-y := $(foreach o,$(obj-y),$(if $(filter-out $(o),$(notdir $(o))),$(o))) # $(obj-dirs) is a list of directories that contain object files obj-dirs := $(dir $(multi-objs) $(subdir-obj-y)) # Replace multi-part objects by their individual parts, look at local dir only real-objs-y := $(foreach m, $(filter-out $(subdir-obj-y), $(obj-y)), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m))) $(extra-y) real-objs-m := $(foreach m, $(obj-m), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m))) # Add subdir path extra-y := $(addprefix $(obj)/,$(extra-y)) always := $(addprefix $(obj)/,$(always)) targets := $(addprefix $(obj)/,$(targets)) obj-y := $(addprefix $(obj)/,$(obj-y)) obj-m := $(addprefix $(obj)/,$(obj-m)) lib-y := $(addprefix $(obj)/,$(lib-y)) subdir-obj-y := $(addprefix $(obj)/,$(subdir-obj-y)) real-objs-y := $(addprefix $(obj)/,$(real-objs-y)) real-objs-m := $(addprefix $(obj)/,$(real-objs-m)) single-used-m := $(addprefix $(obj)/,$(single-used-m)) multi-used-y := $(addprefix $(obj)/,$(multi-used-y)) multi-used-m := $(addprefix $(obj)/,$(multi-used-m)) multi-objs-y := $(addprefix $(obj)/,$(multi-objs-y)) multi-objs-m := $(addprefix $(obj)/,$(multi-objs-m)) subdir-ym := $(addprefix $(obj)/,$(subdir-ym)) obj-dirs := $(addprefix $(obj)/,$(obj-dirs)) # These flags are needed for modversions and compiling, so we define them here # already # $(modname_flags) #defines KBUILD_MODNAME as the name of the module it will # end up in (or would, if it gets compiled in) # Note: It's possible that one object gets potentially linked into more # than one module. In that case KBUILD_MODNAME will be set to foo_bar, # where foo and bar are the name of the modules. name-fix = $(subst $(comma),_,$(subst -,_,$1)) basename_flags = -D"KBUILD_BASENAME=KBUILD_STR($(call name-fix,$(*F)))" modname_flags = $(if $(filter 1,$(words $(modname))),\ -D"KBUILD_MODNAME=KBUILD_STR($(call name-fix,$(modname)))") _c_flags = $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$(*F).o) _a_flags = $(AFLAGS) $(EXTRA_AFLAGS) $(AFLAGS_$(*F).o) _cpp_flags = $(CPPFLAGS) $(EXTRA_CPPFLAGS) $(CPPFLAGS_$(@F)) # If building the kernel in a separate objtree expand all occurrences # of -Idir to -I$(srctree)/dir except for absolute paths (starting with '/'). ifeq ($(KBUILD_SRC),) __c_flags = $(_c_flags) __a_flags = $(_a_flags) __cpp_flags = $(_cpp_flags) else # -I$(obj) locates generated .h files # $(call addtree,-I$(obj)) locates .h files in srctree, from generated .c files # and locates generated .h files # FIXME: Replace both with specific CFLAGS* statements in the makefiles __c_flags = $(call addtree,-I$(obj)) $(call flags,_c_flags) __a_flags = $(call flags,_a_flags) __cpp_flags = $(call flags,_cpp_flags) endif c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(CPPFLAGS) \ $(__c_flags) $(modkern_cflags) \ -D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags) a_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(CPPFLAGS) \ $(__a_flags) $(modkern_aflags) cpp_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(__cpp_flags) # Seems to be a wrong thing to do. LDFLAGS contains gcc's flags, # yet ld_flags is fed to ld. #ld_flags = $(LDFLAGS) $(EXTRA_LDFLAGS) # Remove the -Wl, prefix from linker options normally passed through gcc ld_flags = $(filter-out -Wl$(comma)%,$(LDFLAGS) $(EXTRA_LDFLAGS)) # Finds the multi-part object the current object will be linked into modname-multi = $(sort $(foreach m,$(multi-used),\ $(if $(filter $(subst $(obj)/,,$*.o), $($(m:.o=-objs)) $($(m:.o=-y))),$(m:.o=)))) # Shipped files # =========================================================================== quiet_cmd_shipped = SHIPPED $@ cmd_shipped = cat $< > $@ $(obj)/%:: $(src)/%_shipped $(call cmd,shipped) # Commands useful for building a boot image # =========================================================================== # # Use as following: # # target: source(s) FORCE # $(if_changed,ld/objcopy/gzip) # # and add target to EXTRA_TARGETS so that we know we have to # read in the saved command line # Linking # --------------------------------------------------------------------------- # TODO: LDFLAGS usually is supposed to contain gcc's flags, not ld's. # but here we feed them to ld! quiet_cmd_ld = LD $@ cmd_ld = $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) $(LDFLAGS_$(@F)) \ $(filter-out FORCE,$^) -o $@ # Objcopy # --------------------------------------------------------------------------- quiet_cmd_objcopy = OBJCOPY $@ cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@ # Gzip # --------------------------------------------------------------------------- quiet_cmd_gzip = GZIP $@ cmd_gzip = gzip -f -9 < $< > $@ busybox-1.22.1/scripts/gcc-version.sh0000755000000000000000000000043612263563520016242 0ustar rootroot#!/bin/sh # # gcc-version gcc-command # # Prints the gcc version of `gcc-command' in a canonical 4-digit form # such as `0295' for gcc-2.95, `0303' for gcc-3.3, etc. # compiler="$*" MAJ_MIN=$(echo __GNUC__ __GNUC_MINOR__ | $compiler -E -xc - | tail -n 1) printf '%02d%02d\n' $MAJ_MIN busybox-1.22.1/scripts/bloat-o-meter0000755000000000000000000001054712263563520016065 0ustar rootroot#!/usr/bin/env python # # Copyright 2004 Matt Mackall # # Inspired by perl Bloat-O-Meter (c) 1997 by Andi Kleen # # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. import sys, os def usage(): sys.stderr.write("usage: %s [-t] file1 file2 [-- ]\n" % sys.argv[0]) sys.stderr.write("\t-t\tShow time spent on parsing/processing\n") sys.stderr.write("\t--\tPass additional parameters to readelf\n") sys.exit(1) f1, f2 = (None, None) flag_timing, dashes = (False, False) for f in sys.argv[1:]: if f.startswith("-"): if f == "--": # sym_args dashes = True break if f == "-t": # timings flag_timing = True else: if not os.path.exists(f): sys.stderr.write("Error: file '%s' does not exist\n" % f) usage() if f1 is None: f1 = f elif f2 is None: f2 = f else: usage() if flag_timing: import time if f1 is None or f2 is None: usage() sym_args = " ".join(sys.argv[3 + flag_timing + dashes:]) def getsizes(file): sym, alias, lut = {}, {}, {} for l in os.popen("readelf -W -s %s %s" % (sym_args, file)).readlines(): l = l.strip() if not (len(l) and l[0].isdigit() and len(l.split()) == 8): continue num, value, size, typ, bind, vis, ndx, name = l.split() if ndx == "UND": continue # skip undefined if typ in ["SECTION", "FILES"]: continue # skip sections and files if "." in name: name = "static." + name.split(".")[0] value = int(value, 16) size = int(size, 16) if size.startswith('0x') else int(size) if vis != "DEFAULT" and bind != "GLOBAL": # see if it is an alias alias[(value, size)] = {"name" : name} else: sym[name] = {"addr" : value, "size": size} lut[(value, size)] = 0 for addr, sz in iter(alias.keys()): # If the non-GLOBAL sym has an implementation elsewhere then # it's an alias, disregard it. if not (addr, sz) in lut: # If this non-GLOBAL sym does not have an implementation at # another address, then treat it as a normal symbol. sym[alias[(addr, sz)]["name"]] = {"addr" : addr, "size": sz} for l in os.popen("readelf -W -S " + file).readlines(): x = l.split() if len(x)<6: continue # Should take these into account too! #if x[1] not in [".text", ".rodata", ".symtab", ".strtab"]: continue if x[1] not in [".rodata"]: continue sym[x[1]] = {"addr" : int(x[3], 16), "size" : int(x[5], 16)} return sym if flag_timing: start_t1 = int(time.time() * 1e9) old = getsizes(f1) if flag_timing: end_t1 = int(time.time() * 1e9) start_t2 = int(time.time() * 1e9) new = getsizes(f2) if flag_timing: end_t2 = int(time.time() * 1e9) start_t3 = int(time.time() * 1e9) grow, shrink, add, remove, up, down = 0, 0, 0, 0, 0, 0 delta, common = [], {} for name in iter(old.keys()): if name in new: common[name] = 1 for name in old: if name not in common: remove += 1 sz = old[name]["size"] down += sz delta.append((-sz, name)) for name in new: if name not in common: add += 1 sz = new[name]["size"] up += sz delta.append((sz, name)) for name in common: d = new[name].get("size", 0) - old[name].get("size", 0) if d>0: grow, up = grow+1, up+d elif d<0: shrink, down = shrink+1, down-d else: continue delta.append((d, name)) delta.sort() delta.reverse() if flag_timing: end_t3 = int(time.time() * 1e9) print("%-48s %7s %7s %+7s" % ("function", "old", "new", "delta")) for d, n in delta: if d: old_sz = old.get(n, {}).get("size", "-") new_sz = new.get(n, {}).get("size", "-") print("%-48s %7s %7s %+7d" % (n, old_sz, new_sz, d)) print("-"*78) total="(add/remove: %s/%s grow/shrink: %s/%s up/down: %s/%s)%%sTotal: %s bytes"\ % (add, remove, grow, shrink, up, -down, up-down) print(total % (" "*(80-len(total)))) if flag_timing: print("\n%d/%d; %d Parse origin/new; processing nsecs" % (end_t1-start_t1, end_t2-start_t2, end_t3-start_t3)) print("total nsecs: %d" % (end_t3-start_t1)) busybox-1.22.1/scripts/find_stray_empty_lines0000755000000000000000000000105212263563520020157 0ustar rootroot#!/bin/sh grep -n -B1 -r $'^\t*}$' . | grep -A1 '.[ch]-[0-9]*-$' grep -n -A1 -r $'^\t*{$' . | grep -B1 '.[ch]-[0-9]*-$' # or (less surefire ones): grep -n -B1 -r $'^\t*}' . | grep -A1 '.[ch]-[0-9]*-$' grep -n -A1 -r $'^\t*{' . | grep -B1 '.[ch]-[0-9]*-$' # find trailing empty lines find -type f | while read file; do test x"$file" = x"" && continue tail -n1 $file | while read lastline do #echo "|$file|$lastline" if test x"$lastline" = x""; then echo "$file" fi done done busybox-1.22.1/scripts/cleanup_printf2puts0000755000000000000000000000047012263563520017417 0ustar rootroot#!/bin/sh # Processes current directory recursively: # printf("abc\n") -> puts("abc"). Beware of fprintf etc... # BTW, gcc 4.1.2 already does the same! Can't believe it... grep -lr 'printf\([^%%]*\\n"\)' . | grep '.[ch]$' | xargs -n1 \ sed -e 's/\([^A-Za-z0-9_]\)printf(\( *"[^%]*\)\\n")/\1puts(\2")/' -i busybox-1.22.1/scripts/find_bad_common_bufsiz0000755000000000000000000000051312263563520020066 0ustar rootroot#!/bin/sh # This script finds applets with multiple uses of bb_common_bufsiz1 # (== possible bugs). # Currently (2007-06-04) reports 3 false positives: # ./coreutils/diff.c:7 # ./loginutils/getty.c:2 # ./util-linux/mount.c:5 find -name '*.c' \ | while read name; do grep -Hc bb_common_bufsiz1 "$name" done | grep -v ':[01]$' busybox-1.22.1/scripts/randomtest0000755000000000000000000000545012263563520015573 0ustar rootroot#!/bin/sh # If not specified in environment... if ! test "$LIBC"; then # Select which libc to build against LIBC="glibc" LIBC="uclibc" fi # x86 32-bit: #CROSS_COMPILER_PREFIX="i486-linux-uclibc-" # My system has strange prefix for x86 64-bit uclibc: #CROSS_COMPILER_PREFIX="x86_64-pc-linux-gnu-" if test $# -lt 2 || ! test -d "$1" || test -e "$2"; then echo "Usage: $0 SRC_DIR TMP_DIR" echo echo "SRC_DIR will be copied to TMP_DIR directory." echo "Then a random build will be performed." echo echo "Useful variables:" echo "\$LIBC, \$CROSS_COMPILER_PREFIX, \$MAKEOPTS" exit 1 fi cp -dpr -- "$1" "$2" || { echo "copy error"; exit 1; } cd -- "$2" || { echo "cd $dir error"; exit 1; } # Generate random config make randconfig >/dev/null || { echo "randconfig error"; exit 1; } # Tweak resulting config cat .config \ | grep -v CONFIG_DEBUG_PESSIMIZE \ | grep -v CONFIG_WERROR \ | grep -v CONFIG_CROSS_COMPILER_PREFIX \ | grep -v CONFIG_SELINUX \ | grep -v CONFIG_EFENCE \ | grep -v CONFIG_DMALLOC \ \ | grep -v CONFIG_RFKILL \ >.config.new mv .config.new .config echo '# CONFIG_DEBUG_PESSIMIZE is not set' >>.config echo '# CONFIG_WERROR is not set' >>.config echo "CONFIG_CROSS_COMPILER_PREFIX=\"${CROSS_COMPILER_PREFIX}\"" >>.config echo '# CONFIG_SELINUX is not set' >>.config echo '# CONFIG_EFENCE is not set' >>.config echo '# CONFIG_DMALLOC is not set' >>.config echo '# CONFIG_RFKILL is not set' >>.config # If glibc, don't build static if test x"$LIBC" = x"glibc"; then cat .config \ | grep -v CONFIG_STATIC \ >.config.new mv .config.new .config echo '# CONFIG_STATIC is not set' >>.config fi # If uclibc, build static, and remove some things # likely to not work on uclibc. if test x"$LIBC" = x"uclibc"; then cat .config \ | grep -v CONFIG_STATIC \ | grep -v CONFIG_BUILD_LIBBUSYBOX \ | grep -v CONFIG_PIE \ \ | grep -v CONFIG_FEATURE_TOUCH_NODEREF \ | grep -v CONFIG_FEATURE_2_4_MODULES \ >.config.new mv .config.new .config echo 'CONFIG_STATIC=y' >>.config echo '# CONFIG_BUILD_LIBBUSYBOX is not set' >>.config echo '# CONFIG_PIE is not set' >>.config echo '# CONFIG_FEATURE_2_4_MODULES is not set' >>.config echo '# CONFIG_FEATURE_TOUCH_NODEREF is not set' >>.config fi # If STATIC, remove some things. # PAM with static linking is probably pointless # (but I need to try - now I don't have libpam.a on my system, only libpam.so) if grep -q "^CONFIG_STATIC=y" .config; then cat .config \ | grep -v CONFIG_PAM \ >.config.new mv .config.new .config echo '# CONFIG_PAM is not set' >>.config fi # Regenerate .config with default answers for yanked-off options # (most of default answers are "no"). { yes "" | make oldconfig >/dev/null; } || { echo "oldconfig error"; exit 1; } # Build! nice -n 10 make $MAKEOPTS 2>&1 | tee make.log # Return exitcode 1 if busybox executable does not exist test -x busybox busybox-1.22.1/scripts/showasm0000755000000000000000000000121712263563520015071 0ustar rootroot#!/bin/sh # Copyright 2006 Rob Landley # Licensed under GPLv2 or later, see file LICENSE in this source tree. # Dumb little utility function to print out the assembly dump of a single # function, or list the functions so dumpable in an executable. You'd think # there would be a way to get objdump to do this, but I can't find it. [ $# -lt 1 ] || [ $# -gt 2 ] && { echo "usage: showasm file function"; exit 1; } [ ! -f $1 ] && { echo "File $1 not found"; exit 1; } if [ $# -eq 1 ] then objdump -d $1 | sed -n -e 's/^[0-9a-fA-F]* <\(.*\)>:$/\1/p' exit 0 fi objdump -d $1 | sed -n -e '/./{H;$!d}' -e "x;/^.[0-9a-fA-F]* <$2>:/p" busybox-1.22.1/scripts/Kbuild.include0000644000000000000000000001251312263563520016242 0ustar rootroot#### # kbuild: Generic definitions # Convinient variables comma := , squote := ' empty := space := $(empty) $(empty) ### # The temporary file to save gcc -MD generated dependencies must not # contain a comma depfile = $(subst $(comma),_,$(@D)/.$(@F).d) ### # Escape single quote for use in echo statements escsq = $(subst $(squote),'\$(squote)',$1) ### # filechk is used to check if the content of a generated file is updated. # Sample usage: # define filechk_sample # echo $KERNELRELEASE # endef # version.h : Makefile # $(call filechk,sample) # The rule defined shall write to stdout the content of the new file. # The existing file will be compared with the new one. # - If no file exist it is created # - If the content differ the new file is used # - If they are equal no change, and no timestamp update # - stdin is piped in from the first prerequisite ($<) so one has # to specify a valid file as first prerequisite (often the kbuild file) define filechk $(Q)set -e; \ echo ' CHK $@'; \ mkdir -p $(dir $@); \ $(filechk_$(1)) < $< > $@.tmp; \ if [ -r $@ ] && cmp -s $@ $@.tmp; then \ rm -f $@.tmp; \ else \ echo ' UPD $@'; \ mv -f $@.tmp $@; \ fi endef ###### # gcc support functions # See documentation in Documentation/kbuild/makefiles.txt # as-option # Usage: cflags-y += $(call as-option, -Wa$(comma)-isa=foo,) as-option = $(shell if $(CC) $(CFLAGS) $(1) -Wa,-Z -c -o /dev/null \ -xassembler /dev/null > /dev/null 2>&1; then echo "$(1)"; \ else echo "$(2)"; fi ;) # cc-option # Usage: cflags-y += $(call cc-option, -march=winchip-c6, -march=i586) cc-option = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \ > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;) # hostcc-option # Usage: hostcflags-y += $(call hostcc-option, -march=winchip-c6, -march=i586) hostcc-option = $(shell if $(HOSTCC) $(HOSTCFLAGS) $(1) -S -o /dev/null -xc /dev/null \ > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;) # cc-option-yn # Usage: flag := $(call cc-option-yn, -march=winchip-c6) cc-option-yn = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \ > /dev/null 2>&1; then echo "y"; else echo "n"; fi;) # cc-option-align # Prefix align with either -falign or -malign cc-option-align = $(subst -functions=0,,\ $(call cc-option,-falign-functions=0,-malign-functions=0)) # cc-version # Usage gcc-ver := $(call cc-version, $(CC)) cc-version = $(shell PATH="$(PATH)" $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh \ $(if $(1), $(1), $(CC))) # cc-ifversion # Usage: EXTRA_CFLAGS += $(call cc-ifversion, -lt, 0402, -O1) cc-ifversion = $(shell if [ $(call cc-version, $(CC)) $(1) $(2) ]; then \ echo $(3); fi;) ### # Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj= # Usage: # $(Q)$(MAKE) $(build)=dir build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj # Prefix -I with $(srctree) if it is not an absolute path addtree = $(if $(filter-out -I/%,$(1)),$(patsubst -I%,-I$(srctree)/%,$(1))) $(1) # Find all -I options and call addtree flags = $(foreach o,$($(1)),$(if $(filter -I%,$(o)),$(call addtree,$(o)),$(o))) # If quiet is set, only print short version of command cmd = @$(echo-cmd) $(cmd_$(1)) # Add $(obj)/ for paths that is not absolute objectify = $(foreach o,$(1),$(if $(filter /%,$(o)),$(o),$(obj)/$(o))) ### # if_changed - execute command if any prerequisite is newer than # target, or command line has changed # if_changed_dep - as if_changed, but uses fixdep to reveal dependencies # including used config symbols # if_changed_rule - as if_changed but execute rule instead # See Documentation/kbuild/makefiles.txt for more info ifneq ($(KBUILD_NOCMDDEP),1) # Check if both arguments has same arguments. Result in empty string if equal # User may override this check using make KBUILD_NOCMDDEP=1 arg-check = $(strip $(filter-out $(1), $(2)) $(filter-out $(2), $(1)) ) endif # echo command. Short version is $(quiet) equals quiet, otherwise full command echo-cmd = $(if $($(quiet)cmd_$(1)), \ echo ' $(call escsq,$($(quiet)cmd_$(1)))';) make-cmd = $(subst \#,\\\#,$(subst $$,$$$$,$(call escsq,$(cmd_$(1))))) # function to only execute the passed command if necessary # >'< substitution is for echo to work, >$< substitution to preserve $ when reloading .cmd file # note: when using inline perl scripts [perl -e '...$$t=1;...'] in $(cmd_xxx) double $$ your perl vars # if_changed = $(if $(strip $(filter-out $(PHONY),$?) \ $(call arg-check, $(cmd_$(1)), $(cmd_$@)) ), \ @set -e; \ $(echo-cmd) $(cmd_$(1)); \ echo 'cmd_$@ := $(make-cmd)' > $(@D)/.$(@F).cmd) # execute the command and also postprocess generated .d dependencies # file if_changed_dep = $(if $(strip $(filter-out $(PHONY),$?) \ $(filter-out FORCE $(wildcard $^),$^) \ $(call arg-check, $(cmd_$(1)), $(cmd_$@)) ), \ @set -e; \ $(echo-cmd) $(cmd_$(1)); \ scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(@D)/.$(@F).tmp; \ rm -f $(depfile); \ mv -f $(@D)/.$(@F).tmp $(@D)/.$(@F).cmd) # Usage: $(call if_changed_rule,foo) # will check if $(cmd_foo) changed, or any of the prequisites changed, # and if so will execute $(rule_foo) if_changed_rule = $(if $(strip $(filter-out $(PHONY),$?) \ $(call arg-check, $(cmd_$(1)), $(cmd_$@)) ),\ @set -e; \ $(rule_$(1))) busybox-1.22.1/scripts/randomtest.loop0000755000000000000000000000202512263563520016536 0ustar rootroot#!/bin/sh run_testsuite=true test -d "$1" || { echo "'$1' is not a directory"; exit 1; } test -x "$1/scripts/randomtest" || { echo "No scripts/randomtest in '$1'"; exit 1; } export LIBC="uclibc" export CROSS_COMPILER_PREFIX="i686-" export MAKEOPTS="-j9" cnt=0 fail=0 while sleep 1; do echo "Passes: $cnt Failures: $fail" dir="test.$$" while test -e "$dir" -o -e "failed.$dir"; do dir="test.$$.$RANDOM" done echo "Running randconfig test in $dir..." if ! "$1/scripts/randomtest" "$1" "$dir" >/dev/null; then mv -- "$dir" "failed.$dir" echo "Failed build in: failed.$dir" exit 1 # you may comment this out... let fail++ continue fi if $run_testsuite; then ( cd -- "$dir/testsuite" || exit 1 echo "Running testsuite in $dir..." SKIP_KNOWN_BUGS=1 SKIP_INTERNET_TESTS=1 ./runtest -v >runtest.log 2>&1 ) if test $? != 0; then echo "Failed runtest in $dir" exit 1 # you may comment this out... let fail++ continue fi tail -n10 -- "$dir/testsuite/runtest.log" fi rm -rf -- "$dir" let cnt++ done busybox-1.22.1/scripts/test_make_O0000755000000000000000000000046012263563520015641 0ustar rootroot#!/bin/sh b=`basename $PWD` test "${b#busybox}" != "$b" || { echo "Must be run in busybox tree"; exit 1; } rm -rf ../testdir_make_O.$$ mkdir ../testdir_make_O.$$ odir=`cd ../testdir_make_O.$$ && pwd` test -d "$odir" || exit 1 make O="$odir" $MAKEOPTS "$@" defconfig busybox 2>&1 | tee test_make_O.log busybox-1.22.1/scripts/Makefile.IMA0000644000000000000000000001425612263563520015536 0ustar rootroot# This is completely unsupported. # # Uasge: make -f scripts/Makefile.IMA # # Fix COMBINED_COMPILE upstream (in the Kbuild) and propagate # the changes back srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR)) objtree := $(CURDIR) src := $(srctree) obj := $(objtree) # Make generated files DUMMY := $(shell $(Q)$(srctree)/scripts/gen_build_files.sh $(srctree) $(objtree) >&2) # Look for make include files relative to root of src MAKEFLAGS += --include-dir=$(srctree) default: busybox include .config # Cross compiling and selecting different set of gcc/bin-utils ifeq ($(CROSS_COMPILE),) CROSS_COMPILE := $(subst ",,$(CONFIG_CROSS_COMPILER_PREFIX)) endif ifneq ($(CROSS_COMPILE),) SUBARCH := $(shell echo $(CROSS_COMPILE) | cut -d- -f1) else SUBARCH := $(shell uname -m) endif SUBARCH := $(shell echo $(SUBARCH) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ -e s/arm.*/arm/ -e s/sa110/arm/ \ -e s/s390x/s390/ -e s/parisc64/parisc/ \ -e s/ppc.*/powerpc/ -e s/mips.*/mips/ ) ARCH ?= $(SUBARCH) ifndef HOSTCC HOSTCC = cc endif AS = $(CROSS_COMPILE)as CC = $(CROSS_COMPILE)gcc LD = $(CC) -nostdlib CPP = $(CC) -E AR = $(CROSS_COMPILE)ar NM = $(CROSS_COMPILE)nm STRIP = $(CROSS_COMPILE)strip OBJCOPY = $(CROSS_COMPILE)objcopy OBJDUMP = $(CROSS_COMPILE)objdump CFLAGS := $(CFLAGS) CPPFLAGS += -D"KBUILD_STR(s)=\#s" #-Q # We need some generic definitions include $(srctree)/scripts/Kbuild.include include Makefile.flags -include $(srctree)/arch/$(ARCH)/Makefile ifdef CONFIG_FEATURE_COMPRESS_USAGE usage_stuff = include/usage_compressed.h endif ifndef BB_VER BB_VER:="" endif WHOLE_PROGRAM:=$(call cc-option,-fwhole-program,) # pull in the config stuff lib-all-y := applets/applets.o lib-y:= include procps/Kbuild lib-all-y += $(patsubst %,procps/%,$(sort $(lib-y))) lib-y:= include networking/Kbuild lib-all-y += $(patsubst %,networking/%,$(sort $(lib-y))) lib-y:= include networking/udhcp/Kbuild lib-all-y += $(patsubst %,networking/udhcp/%,$(sort $(lib-y))) lib-y:= include networking/libiproute/Kbuild lib-all-y += $(patsubst %,networking/libiproute/%,$(sort $(lib-y))) lib-y:= include loginutils/Kbuild lib-all-y += $(patsubst %,loginutils/%,$(sort $(lib-y))) lib-y:= include archival/Kbuild lib-all-y += $(patsubst %,archival/%,$(sort $(lib-y))) lib-y:= include archival/libarchive/Kbuild lib-all-y += $(patsubst %,archival/libarchive/%,$(sort $(lib-y))) lib-y:= include applets/Kbuild lib-all-y += $(patsubst %,applets/%,$(sort $(lib-y))) lib-y:= include e2fsprogs/Kbuild lib-all-y += $(patsubst %,e2fsprogs/%,$(sort $(lib-y))) lib-y:= #include e2fsprogs/old_e2fsprogs/Kbuild #lib-all-y += $(patsubst %,e2fsprogs/old_e2fsprogs/%,$(sort $(lib-y))) #lib-y:= #include e2fsprogs/old_e2fsprogs/ext2fs/Kbuild #lib-all-y += $(patsubst %,e2fsprogs/old_e2fsprogs/ext2fs/%,$(sort $(lib-y))) #lib-y:= #include e2fsprogs/old_e2fsprogs/blkid/Kbuild #lib-all-y += $(patsubst %,e2fsprogs/old_e2fsprogs/blkid/%,$(sort $(lib-y))) #lib-y:= #include e2fsprogs/old_e2fsprogs/uuid/Kbuild #lib-all-y += $(patsubst %,e2fsprogs/old_e2fsprogs/uuid/%,$(sort $(lib-y))) #lib-y:= #include e2fsprogs/old_e2fsprogs/e2p/Kbuild #lib-all-y += $(patsubst %,e2fsprogs/old_e2fsprogs/e2p/%,$(sort $(lib-y))) #lib-y:= include debianutils/Kbuild lib-all-y += $(patsubst %,debianutils/%,$(sort $(lib-y))) lib-y:= include runit/Kbuild lib-all-y += $(patsubst %,runit/%,$(sort $(lib-y))) lib-y:= include modutils/Kbuild lib-all-y += $(patsubst %,modutils/%,$(sort $(lib-y))) lib-y:= include miscutils/Kbuild lib-all-y += $(patsubst %,miscutils/%,$(sort $(lib-y))) lib-y:= include mailutils/Kbuild lib-all-y += $(patsubst %,mailutils/%,$(sort $(lib-y))) lib-y:= include coreutils/libcoreutils/Kbuild lib-all-y += $(patsubst %,coreutils/libcoreutils/%,$(sort $(lib-y))) lib-y:= include coreutils/Kbuild lib-all-y += $(patsubst %,coreutils/%,$(sort $(lib-y))) lib-y:= include sysklogd/Kbuild lib-all-y += $(patsubst %,sysklogd/%,$(sort $(lib-y))) lib-y:= include shell/Kbuild lib-all-y += $(patsubst %,shell/%,$(sort $(lib-y))) lib-y:= include console-tools/Kbuild lib-all-y += $(patsubst %,console-tools/%,$(sort $(lib-y))) lib-y:= include findutils/Kbuild lib-all-y += $(patsubst %,findutils/%,$(sort $(lib-y))) lib-y:= include util-linux/Kbuild lib-all-y += $(patsubst %,util-linux/%,$(sort $(lib-y))) lib-y:= include util-linux/volume_id/Kbuild lib-all-y += $(patsubst %,util-linux/volume_id/%,$(sort $(lib-y))) lib-y:= include init/Kbuild lib-all-y += $(patsubst %,init/%,$(sort $(lib-y))) lib-y:= include libpwdgrp/Kbuild lib-all-y += $(patsubst %,libpwdgrp/%,$(sort $(lib-y))) lib-y:= include editors/Kbuild lib-all-y += $(patsubst %,editors/%,$(sort $(lib-y))) lib-y:= include printutils/Kbuild lib-all-y += $(patsubst %,printutils/%,$(sort $(lib-y))) lib-y:= include selinux/Kbuild lib-all-y += $(patsubst %,selinux/%,$(sort $(lib-y))) lib-y:= include scripts/Kbuild lib-all-y += $(patsubst %,scripts/%,$(sort $(lib-y))) lib-y:= include libbb/Kbuild lib-all-y += $(patsubst %,libbb/%,$(sort $(lib-y))) lib-y:= comma:=, busybox_unstripped.o: $(usage_stuff) include/applet_tables.h include/NUM_APPLETS.h include/autoconf.h $(CC) $(CPPFLAGS) $(CFLAGS) $(EXTRA_CFLAGS) \ $(patsubst %,-Wl$(comma)%,$(LDFLAGS) $(EXTRA_LDFLAGS)) \ -DGCC_COMBINE=1 \ --combine $(WHOLE_PROGRAM) \ -funit-at-a-time -Wno-error -std=gnu99 \ -c -o busybox_unstripped.o \ $(lib-all-y:.o=.c) busybox: busybox_unstripped.o $(srctree)/scripts/trylink \ busybox_unstripped \ "$(CC) $(CFLAGS_busybox)" \ "$(CFLAGS)" \ "$(LDFLAGS)" \ "busybox_unstripped.o" \ "" \ "crypt m" cp -f $(@)_unstripped $@ -$(STRIP) -s -R .note -R .comment -R .version $@ # If .config is newer than include/autoconf.h, someone tinkered # with it and forgot to run make oldconfig. include/autoconf.h: .config $(MAKE) -f $(srctree)/Makefile silentoldconfig # Override rules for host compile applets/usage: include/autoconf.h $(HOSTCC) -Wall -O2 -I$(srctree)/include -o applets/usage applets/usage.c applets/applet_tables: include/autoconf.h $(HOSTCC) -Wall -O2 -I$(srctree)/include -o applets/applet_tables applets/applet_tables.c busybox-1.22.1/scripts/mkmakefile0000755000000000000000000000143212263563520015514 0ustar rootroot#!/bin/sh # Generates a small Makefile used in the root of the output # directory, to allow make to be started from there. # The Makefile also allow for more convinient build of external modules # Usage # $1 - Kernel src directory # $2 - Output directory # $3 - version # $4 - patchlevel test ! -r $2/Makefile -o -O $2/Makefile || exit 0 echo " GEN $2/Makefile" cat << EOF > $2/Makefile # Automatically generated by $0: don't edit VERSION = $3 PATCHLEVEL = $4 KERNELSRC := $1 KERNELOUTPUT := $2 MAKEFLAGS += --no-print-directory .PHONY: all \$(MAKECMDGOALS) all: \$(MAKE) -C \$(KERNELSRC) O=\$(KERNELOUTPUT) Makefile:; \$(filter-out all Makefile,\$(MAKECMDGOALS)): \$(MAKE) -C \$(KERNELSRC) O=\$(KERNELOUTPUT) \$@ %/: \$(MAKE) -C \$(KERNELSRC) O=\$(KERNELOUTPUT) \$@ EOF busybox-1.22.1/scripts/Makefile.host0000644000000000000000000001344012263563520016077 0ustar rootroot# ========================================================================== # Building binaries on the host system # Binaries are used during the compilation of the kernel, for example # to preprocess a data file. # # Both C and C++ is supported, but preferred language is C for such utilities. # # Samle syntax (see Documentation/kbuild/makefile.txt for reference) # hostprogs-y := bin2hex # Will compile bin2hex.c and create an executable named bin2hex # # hostprogs-y := lxdialog # lxdialog-objs := checklist.o lxdialog.o # Will compile lxdialog.c and checklist.c, and then link the executable # lxdialog, based on checklist.o and lxdialog.o # # hostprogs-y := qconf # qconf-cxxobjs := qconf.o # qconf-objs := menu.o # Will compile qconf as a C++ program, and menu as a C program. # They are linked as C++ code to the executable qconf # hostprogs-y := conf # conf-objs := conf.o libkconfig.so # libkconfig-objs := expr.o type.o # Will create a shared library named libkconfig.so that consist of # expr.o and type.o (they are both compiled as C code and the object file # are made as position independent code). # conf.c is compiled as a c program, and conf.o is linked together with # libkconfig.so as the executable conf. # Note: Shared libraries consisting of C++ files are not supported __hostprogs := $(sort $(hostprogs-y)$(hostprogs-m)) # hostprogs-y := tools/build may have been specified. Retreive directory obj-dirs += $(foreach f,$(__hostprogs), $(if $(dir $(f)),$(dir $(f)))) obj-dirs := $(strip $(sort $(filter-out ./,$(obj-dirs)))) # C code # Executables compiled from a single .c file host-csingle := $(foreach m,$(__hostprogs),$(if $($(m)-objs),,$(m))) # C executables linked based on several .o files host-cmulti := $(foreach m,$(__hostprogs),\ $(if $($(m)-cxxobjs),,$(if $($(m)-objs),$(m)))) # Object (.o) files compiled from .c files host-cobjs := $(sort $(foreach m,$(__hostprogs),$($(m)-objs))) # C++ code # C++ executables compiled from at least on .cc file # and zero or more .c files host-cxxmulti := $(foreach m,$(__hostprogs),$(if $($(m)-cxxobjs),$(m))) # C++ Object (.o) files compiled from .cc files host-cxxobjs := $(sort $(foreach m,$(host-cxxmulti),$($(m)-cxxobjs))) # Shared libaries (only .c supported) # Shared libraries (.so) - all .so files referenced in "xxx-objs" host-cshlib := $(sort $(filter %.so, $(host-cobjs))) # Remove .so files from "xxx-objs" host-cobjs := $(filter-out %.so,$(host-cobjs)) #Object (.o) files used by the shared libaries host-cshobjs := $(sort $(foreach m,$(host-cshlib),$($(m:.so=-objs)))) __hostprogs := $(addprefix $(obj)/,$(__hostprogs)) host-csingle := $(addprefix $(obj)/,$(host-csingle)) host-cmulti := $(addprefix $(obj)/,$(host-cmulti)) host-cobjs := $(addprefix $(obj)/,$(host-cobjs)) host-cxxmulti := $(addprefix $(obj)/,$(host-cxxmulti)) host-cxxobjs := $(addprefix $(obj)/,$(host-cxxobjs)) host-cshlib := $(addprefix $(obj)/,$(host-cshlib)) host-cshobjs := $(addprefix $(obj)/,$(host-cshobjs)) obj-dirs := $(addprefix $(obj)/,$(obj-dirs)) ##### # Handle options to gcc. Support building with separate output directory _hostc_flags = $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) $(HOSTCFLAGS_$(*F).o) _hostcxx_flags = $(HOSTCXXFLAGS) $(HOST_EXTRACXXFLAGS) $(HOSTCXXFLAGS_$(*F).o) ifeq ($(KBUILD_SRC),) __hostc_flags = $(_hostc_flags) __hostcxx_flags = $(_hostcxx_flags) else __hostc_flags = -I$(obj) $(call flags,_hostc_flags) __hostcxx_flags = -I$(obj) $(call flags,_hostcxx_flags) endif hostc_flags = -Wp,-MD,$(depfile) $(__hostc_flags) hostcxx_flags = -Wp,-MD,$(depfile) $(__hostcxx_flags) ##### # Compile programs on the host # Create executable from a single .c file # host-csingle -> Executable quiet_cmd_host-csingle = HOSTCC $@ cmd_host-csingle = $(HOSTCC) $(hostc_flags) -o $@ $< \ $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F)) $(host-csingle): %: %.c FORCE $(call if_changed_dep,host-csingle) # Link an executable based on list of .o files, all plain c # host-cmulti -> executable quiet_cmd_host-cmulti = HOSTLD $@ cmd_host-cmulti = $(HOSTCC) $(HOSTLDFLAGS) -o $@ \ $(addprefix $(obj)/,$($(@F)-objs)) \ $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F)) $(host-cmulti): %: $(host-cobjs) $(host-cshlib) FORCE $(call if_changed,host-cmulti) # Create .o file from a single .c file # host-cobjs -> .o quiet_cmd_host-cobjs = HOSTCC $@ cmd_host-cobjs = $(HOSTCC) $(hostc_flags) -c -o $@ $< $(host-cobjs): %.o: %.c FORCE $(call if_changed_dep,host-cobjs) # Link an executable based on list of .o files, a mixture of .c and .cc # host-cxxmulti -> executable quiet_cmd_host-cxxmulti = HOSTLD $@ cmd_host-cxxmulti = $(HOSTCXX) $(HOSTLDFLAGS) -o $@ \ $(foreach o,objs cxxobjs,\ $(addprefix $(obj)/,$($(@F)-$(o)))) \ $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F)) $(host-cxxmulti): %: $(host-cobjs) $(host-cxxobjs) $(host-cshlib) FORCE $(call if_changed,host-cxxmulti) # Create .o file from a single .cc (C++) file quiet_cmd_host-cxxobjs = HOSTCXX $@ cmd_host-cxxobjs = $(HOSTCXX) $(hostcxx_flags) -c -o $@ $< $(host-cxxobjs): %.o: %.cc FORCE $(call if_changed_dep,host-cxxobjs) # Compile .c file, create position independent .o file # host-cshobjs -> .o quiet_cmd_host-cshobjs = HOSTCC -fPIC $@ cmd_host-cshobjs = $(HOSTCC) $(hostc_flags) -fPIC -c -o $@ $< $(host-cshobjs): %.o: %.c FORCE $(call if_changed_dep,host-cshobjs) # Link a shared library, based on position independent .o files # *.o -> .so shared library (host-cshlib) quiet_cmd_host-cshlib = HOSTLLD -shared $@ cmd_host-cshlib = $(HOSTCC) $(HOSTLDFLAGS) -shared -o $@ \ $(addprefix $(obj)/,$($(@F:.so=-objs))) \ $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F)) $(host-cshlib): %: $(host-cshobjs) FORCE $(call if_changed,host-cshlib) targets += $(host-csingle) $(host-cmulti) $(host-cobjs)\ $(host-cxxmulti) $(host-cxxobjs) $(host-cshlib) $(host-cshobjs) busybox-1.22.1/scripts/echo.c0000644000000000000000000001347612263563520014556 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * echo implementation for busybox - used as a helper for testsuite/* * on systems lacking "echo -en" * * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Original copyright notice is retained at the end of this file. */ /* BB_AUDIT SUSv3 compliant -- unless configured as fancy echo. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/echo.html */ /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) * * Because of behavioral differences, implemented configurable SUSv3 * or 'fancy' gnu-ish behaviors. Also, reduced size and fixed bugs. * 1) In handling '\c' escape, the previous version only suppressed the * trailing newline. SUSv3 specifies _no_ output after '\c'. * 2) SUSv3 specifies that octal escapes are of the form \0{#{#{#}}}. * The previous version did not allow 4-digit octals. */ #include #include #include #define WANT_HEX_ESCAPES 1 /* Usual "this only works for ascii compatible encodings" disclaimer. */ #undef _tolower #define _tolower(X) ((X)|((char) 0x20)) static 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 char)(*q) - '0'; #ifdef WANT_HEX_ESCAPES if (d >= 10) { d = (unsigned char)(_tolower(*q)) - 'a' + 10; } #endif if (d >= base) { #ifdef WANT_HEX_ESCAPES if ((base == 16) && (!--num_digits)) { /* return '\\'; */ --q; } #endif break; } r = n * base + d; if (r > UCHAR_MAX) { break; } n = r; ++q; } while (++num_digits < 3); if (num_digits == 0) { /* mnemonic escape sequence? */ p = charmap; do { if (*p == *q) { q++; break; } } while (*++p); n = *(p + (sizeof(charmap)/2)); } *ptr = q; return (char) n; } int main(int argc, char **argv) { const char *arg; const char *p; char nflag = 1; char eflag = 0; /* We must check that stdout is not closed. */ if (dup2(1, 1) != 1) return -1; while (1) { arg = *++argv; if (!arg) goto newline_ret; if (*arg != '-') break; /* If it appears that we are handling options, then make sure * that all of the options specified are actually valid. * Otherwise, the string should just be echoed. */ p = arg + 1; if (!*p) /* A single '-', so echo it. */ goto just_echo; do { if (!strrchr("neE", *p)) goto just_echo; } while (*++p); /* All of the options in this arg are valid, so handle them. */ p = arg + 1; do { if (*p == 'n') nflag = 0; if (*p == 'e') eflag = '\\'; } while (*++p); } just_echo: while (1) { /* arg is already == *argv and isn't NULL */ int c; if (!eflag) { /* optimization for very common case */ fputs(arg, stdout); } else while ((c = *arg++)) { if (c == eflag) { /* Check for escape seq. */ if (*arg == 'c') { /* '\c' means cancel newline and * ignore all subsequent chars. */ goto ret; } { /* Since SUSv3 mandates a first digit of 0, 4-digit octals * of the form \0### are accepted. */ if (*arg == '0') { /* NB: don't turn "...\0" into "...\" */ if (arg[1] && ((unsigned char)(arg[1]) - '0') < 8) { arg++; } } /* bb_process_escape_sequence handles NUL correctly * ("...\" case. */ c = bb_process_escape_sequence(&arg); } } putchar(c); } arg = *++argv; if (!arg) break; putchar(' '); } newline_ret: if (nflag) { putchar('\n'); } ret: return fflush(NULL); } /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * 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. * * California, Berkeley and its contributors. * 4. 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. * * @(#)echo.c 8.1 (Berkeley) 5/31/93 */ busybox-1.22.1/scripts/fix_ws.sh0000755000000000000000000000452112263563520015321 0ustar rootroot#!/bin/bash # Whitespace fixer # Usage: fix_ws [dir]... temp="/tmp/fix_ws.$$.$RANDOM" # Using $'xxx' bashism begin8sp_tab=$'s/^ /\t/' beginchar7sp_chartab=$'s/^\\([^ \t]\\) /\\1\t/' tab8sp_tabtab=$'s/\t /\t\t/g' tab8sptab_tabtabtab=$'s/\t \t/\t\t\t/g' begin17sptab_tab=$'s/^ \\{1,7\\}\t/\t/' tab17sptab_tabtab=$'s/\t \\{1,7\\}\t/\t\t/g' trailingws_=$'s/[ \t]*$//' #>fix_ws.diff find "$@" -type f \ | while read name; do test "YES" = "${name/*.bz2/YES}" && continue test "YES" = "${name/*.gz/YES}" && continue test "YES" = "${name/*.png/YES}" && continue test "YES" = "${name/*.gif/YES}" && continue test "YES" = "${name/*.jpg/YES}" && continue test "YES" = "${name/*.diff/YES}" && continue test "YES" = "${name/*.patch/YES}" && continue # shell testsuite entries are not to be touched too test "YES" = "${name/*.right/YES}" && continue if test "YES" = "${name/*.[chsS]/YES}" \ -o "YES" = "${name/*.sh/YES}" \ -o "YES" = "${name/*.txt/YES}" \ -o "YES" = "${name/*.html/YES}" \ -o "YES" = "${name/*.htm/YES}" \ -o "YES" = "${name/*Config.in*/YES}" \ ; then # More aggressive whitespace fixes for known file types echo "Formatting: $name" >&2 cat "$name" \ | sed -e "$tab8sptab_tabtabtab" -e "$tab8sptab_tabtabtab" \ -e "$tab8sptab_tabtabtab" -e "$tab8sptab_tabtabtab" \ | sed "$begin17sptab_tab" \ | sed -e "$tab17sptab_tabtab" -e "$tab17sptab_tabtab" \ -e "$tab17sptab_tabtab" -e "$tab17sptab_tabtab" \ -e "$tab17sptab_tabtab" -e "$tab17sptab_tabtab" \ | sed "$trailingws_" elif test "YES" = "${name/*Makefile*/YES}" \ -o "YES" = "${name/*Kbuild*/YES}" \ ; then # For Makefiles, never convert "1-7spaces+tab" into "tabtab" echo "Makefile: $name" >&2 cat "$name" \ | sed -e "$tab8sptab_tabtabtab" -e "$tab8sptab_tabtabtab" \ -e "$tab8sptab_tabtabtab" -e "$tab8sptab_tabtabtab" \ | sed -e "$tab17sptab_tabtab" -e "$tab17sptab_tabtab" \ -e "$tab17sptab_tabtab" -e "$tab17sptab_tabtab" \ -e "$tab17sptab_tabtab" -e "$tab17sptab_tabtab" \ | sed "$trailingws_" else # Only remove trailing WS for the rest echo "Removing trailing whitespace: $name" >&2 cat "$name" \ | sed "$trailingws_" fi >"$temp" # diff -u "$temp" "$name" >>fix_ws.diff # Conserve mode/symlink: cat "$temp" >"$name" done rm "$temp" 2>/dev/null busybox-1.22.1/scripts/objsizes0000755000000000000000000000110212263563520015231 0ustar rootroot#!/bin/sh t_text=0 t_data=0 t_bss=0 printf "%9s %11s %9s %9s %s\n" "text+data" "text+rodata" rwdata bss filename find -name '*.o' | grep -v '^\./scripts/' | grep -vF built-in.o \ | sed 's:^\./::' | xargs "${CROSS_COMPILE}size" | grep '^ *[0-9]' \ | { while read text data bss dec hex filename; do t_text=$((t_text+text)) t_data=$((t_data+data)) t_bss=$((t_bss+bss)) printf "%9d %11d %9d %9d %s\n" $((text+data)) $text $data $bss "$filename" done printf "%9d %11d %9d %9d %s\n" $((t_text+t_data)) $t_text $t_data $t_bss "TOTAL" } | env -uLANG -uLC_ALL sort -r busybox-1.22.1/scripts/kconfig/0000755000000000000000000000000012320365361015076 5ustar rootrootbusybox-1.22.1/scripts/kconfig/conf.c0000644000000000000000000002627012263563520016201 0ustar rootroot/* * Copyright (C) 2002 Roman Zippel * Released under the terms of the GNU GPL v2.0. */ #define _XOPEN_SOURCE 700 #include #include #include #include #include #include #include #define LKC_DIRECT_LINK #include "lkc.h" static void conf(struct menu *menu); static void check_conf(struct menu *menu); enum { ask_all, ask_new, ask_silent, set_default, set_yes, set_mod, set_no, set_random } input_mode = ask_all; char *defconfig_file; static int indent = 1; static int valid_stdin = 1; static int conf_cnt; static char line[128]; static struct menu *rootEntry; static char nohelp_text[] = N_("Sorry, no help available for this option yet.\n"); static void strip(char *str) { char *p = str; int l; while ((isspace(*p))) p++; l = strlen(p); if (p != str) memmove(str, p, l + 1); if (!l) return; p = str + l - 1; while ((isspace(*p))) *p-- = 0; } static void check_stdin(void) { if (!valid_stdin && input_mode == ask_silent) { printf(_("aborted!\n\n")); printf(_("Console input/output is redirected. ")); printf(_("Run 'make oldconfig' to update configuration.\n\n")); exit(1); } } static void conf_askvalue(struct symbol *sym, const char *def) { enum symbol_type type = sym_get_type(sym); tristate val; if (!sym_has_value(sym)) printf("(NEW) "); line[0] = '\n'; line[1] = 0; if (!sym_is_changable(sym)) { printf("%s\n", def); line[0] = '\n'; line[1] = 0; return; } switch (input_mode) { case set_no: case set_mod: case set_yes: case set_random: if (sym_has_value(sym)) { printf("%s\n", def); return; } break; case ask_new: case ask_silent: if (sym_has_value(sym)) { printf("%s\n", def); return; } check_stdin(); case ask_all: fflush(stdout); fgets(line, 128, stdin); return; case set_default: printf("%s\n", def); return; default: break; } switch (type) { case S_INT: case S_HEX: case S_STRING: printf("%s\n", def); return; default: ; } switch (input_mode) { case set_yes: if (sym_tristate_within_range(sym, yes)) { line[0] = 'y'; line[1] = '\n'; line[2] = 0; break; } case set_mod: if (type == S_TRISTATE) { if (sym_tristate_within_range(sym, mod)) { line[0] = 'm'; line[1] = '\n'; line[2] = 0; break; } } else { if (sym_tristate_within_range(sym, yes)) { line[0] = 'y'; line[1] = '\n'; line[2] = 0; break; } } case set_no: if (sym_tristate_within_range(sym, no)) { line[0] = 'n'; line[1] = '\n'; line[2] = 0; break; } case set_random: do { val = (tristate)(random() % 3); } while (!sym_tristate_within_range(sym, val)); switch (val) { case no: line[0] = 'n'; break; case mod: line[0] = 'm'; break; case yes: line[0] = 'y'; break; } line[1] = '\n'; line[2] = 0; break; default: break; } printf("%s", line); } int conf_string(struct menu *menu) { struct symbol *sym = menu->sym; const char *def; while (1) { printf("%*s%s ", indent - 1, "", menu->prompt->text); printf("(%s) ", sym->name); def = sym_get_string_value(sym); if (sym_get_string_value(sym)) printf("[%s] ", def); conf_askvalue(sym, def); switch (line[0]) { case '\n': break; case '?': /* print help */ if (line[1] == '\n') { printf("\n%s\n", menu->sym->help ? menu->sym->help : nohelp_text); def = NULL; break; } default: line[strlen(line)-1] = 0; def = line; } if (def && sym_set_string_value(sym, def)) return 0; } } static int conf_sym(struct menu *menu) { struct symbol *sym = menu->sym; tristate oldval, newval; const char *help; while (1) { printf("%*s%s ", indent - 1, "", menu->prompt->text); if (sym->name) printf("(%s) ", sym->name); putchar('['); oldval = sym_get_tristate_value(sym); switch (oldval) { case no: putchar('N'); break; case mod: putchar('M'); break; case yes: putchar('Y'); break; } if (oldval != no && sym_tristate_within_range(sym, no)) printf("/n"); if (oldval != mod && sym_tristate_within_range(sym, mod)) printf("/m"); if (oldval != yes && sym_tristate_within_range(sym, yes)) printf("/y"); if (sym->help) printf("/?"); printf("] "); conf_askvalue(sym, sym_get_string_value(sym)); strip(line); switch (line[0]) { case 'n': case 'N': newval = no; if (!line[1] || !strcmp(&line[1], "o")) break; continue; case 'm': case 'M': newval = mod; if (!line[1]) break; continue; case 'y': case 'Y': newval = yes; if (!line[1] || !strcmp(&line[1], "es")) break; continue; case 0: newval = oldval; break; case '?': goto help; default: continue; } if (sym_set_tristate_value(sym, newval)) return 0; help: help = nohelp_text; if (sym->help) help = sym->help; printf("\n%s\n", help); } } static int conf_choice(struct menu *menu) { struct symbol *sym, *def_sym; struct menu *child; bool is_new; sym = menu->sym; is_new = !sym_has_value(sym); if (sym_is_changable(sym)) { conf_sym(menu); sym_calc_value(sym); switch (sym_get_tristate_value(sym)) { case no: return 1; case mod: return 0; case yes: break; } } else { switch (sym_get_tristate_value(sym)) { case no: return 1; case mod: printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu)); return 0; case yes: break; } } while (1) { int cnt, def; printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu)); def_sym = sym_get_choice_value(sym); cnt = def = 0; line[0] = 0; for (child = menu->list; child; child = child->next) { if (!menu_is_visible(child)) continue; if (!child->sym) { printf("%*c %s\n", indent, '*', menu_get_prompt(child)); continue; } cnt++; if (child->sym == def_sym) { def = cnt; printf("%*c", indent, '>'); } else printf("%*c", indent, ' '); printf(" %d. %s", cnt, menu_get_prompt(child)); if (child->sym->name) printf(" (%s)", child->sym->name); if (!sym_has_value(child->sym)) printf(" (NEW)"); printf("\n"); } printf("%*schoice", indent - 1, ""); if (cnt == 1) { printf("[1]: 1\n"); goto conf_childs; } printf("[1-%d", cnt); if (sym->help) printf("?"); printf("]: "); switch (input_mode) { case ask_new: case ask_silent: if (!is_new) { cnt = def; printf("%d\n", cnt); break; } check_stdin(); case ask_all: fflush(stdout); fgets(line, 128, stdin); strip(line); if (line[0] == '?') { printf("\n%s\n", menu->sym->help ? menu->sym->help : nohelp_text); continue; } if (!line[0]) cnt = def; else if (isdigit(line[0])) cnt = atoi(line); else continue; break; case set_random: def = (random() % cnt) + 1; case set_default: case set_yes: case set_mod: case set_no: cnt = def; printf("%d\n", cnt); break; } conf_childs: for (child = menu->list; child; child = child->next) { if (!child->sym || !menu_is_visible(child)) continue; if (!--cnt) break; } if (!child) continue; if (strlen(line) > 0 && line[strlen(line) - 1] == '?') { printf("\n%s\n", child->sym->help ? child->sym->help : nohelp_text); continue; } sym_set_choice_value(sym, child->sym); if (child->list) { indent += 2; conf(child->list); indent -= 2; } return 1; } } static void conf(struct menu *menu) { struct symbol *sym; struct property *prop; struct menu *child; if (!menu_is_visible(menu)) return; sym = menu->sym; prop = menu->prompt; if (prop) { const char *prompt; switch (prop->type) { case P_MENU: if (input_mode == ask_silent && rootEntry != menu) { check_conf(menu); return; } case P_COMMENT: prompt = menu_get_prompt(menu); if (prompt) printf("%*c\n%*c %s\n%*c\n", indent, '*', indent, '*', prompt, indent, '*'); default: ; } } if (!sym) goto conf_childs; if (sym_is_choice(sym)) { conf_choice(menu); if (sym->curr.tri != mod) return; goto conf_childs; } switch (sym->type) { case S_INT: case S_HEX: case S_STRING: conf_string(menu); break; default: conf_sym(menu); break; } conf_childs: if (sym) indent += 2; for (child = menu->list; child; child = child->next) conf(child); if (sym) indent -= 2; } static void check_conf(struct menu *menu) { struct symbol *sym; struct menu *child; if (!menu_is_visible(menu)) return; sym = menu->sym; if (sym && !sym_has_value(sym)) { if (sym_is_changable(sym) || (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) { if (!conf_cnt++) printf(_("*\n* Restart config...\n*\n")); rootEntry = menu_get_parent_menu(menu); conf(rootEntry); } } for (child = menu->list; child; child = child->next) check_conf(child); } int main(int ac, char **av) { int i = 1; const char *name; struct stat tmpstat; if (ac > i && av[i][0] == '-') { switch (av[i++][1]) { case 'o': input_mode = ask_new; break; case 's': input_mode = ask_silent; valid_stdin = isatty(0); //bbox: && isatty(1) && isatty(2); break; case 'd': input_mode = set_default; break; case 'D': input_mode = set_default; defconfig_file = av[i++]; if (!defconfig_file) { printf(_("%s: No default config file specified\n"), av[0]); exit(1); } break; case 'n': input_mode = set_no; break; case 'm': input_mode = set_mod; break; case 'y': input_mode = set_yes; break; case 'r': input_mode = set_random; srandom(time(NULL)); break; case 'h': case '?': fprintf(stderr, "See README for usage info\n"); exit(0); } } name = av[i]; if (!name) { printf(_("%s: Kconfig file missing\n"), av[0]); } conf_parse(name); //zconfdump(stdout); switch (input_mode) { case set_default: if (!defconfig_file) defconfig_file = conf_get_default_confname(); if (conf_read(defconfig_file)) { printf("***\n" "*** Can't find default configuration \"%s\"!\n" "***\n", defconfig_file); exit(1); } break; case ask_silent: if (stat(".config", &tmpstat)) { printf(_("***\n" "*** You have not yet configured busybox!\n" "***\n" "*** Please run some configurator (e.g. \"make oldconfig\" or\n" "*** \"make menuconfig\" or \"make defconfig\").\n" "***\n")); exit(1); } case ask_all: case ask_new: conf_read(NULL); break; case set_no: case set_mod: case set_yes: case set_random: name = getenv("KCONFIG_ALLCONFIG"); if (name && !stat(name, &tmpstat)) { conf_read_simple(name); break; } switch (input_mode) { case set_no: name = "allno.config"; break; case set_mod: name = "allmod.config"; break; case set_yes: name = "allyes.config"; break; case set_random: name = "allrandom.config"; break; default: break; } if (!stat(name, &tmpstat)) conf_read_simple(name); else if (!stat("all.config", &tmpstat)) conf_read_simple("all.config"); break; default: break; } if (input_mode != ask_silent) { rootEntry = &rootmenu; conf(&rootmenu); if (input_mode == ask_all) { input_mode = ask_silent; valid_stdin = 1; } } do { conf_cnt = 0; check_conf(&rootmenu); } while (conf_cnt); if (conf_write(NULL)) { fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n")); return 1; } return 0; } busybox-1.22.1/scripts/kconfig/lkc_proto.h0000644000000000000000000000327012263563520017250 0ustar rootroot /* confdata.c */ P(conf_parse,void,(const char *name)); P(conf_read,int,(const char *name)); P(conf_read_simple,int,(const char *name)); P(conf_write,int,(const char *name)); /* menu.c */ P(rootmenu,struct menu,); P(menu_is_visible,bool,(struct menu *menu)); P(menu_get_prompt,const char *,(struct menu *menu)); P(menu_get_root_menu,struct menu *,(struct menu *menu)); P(menu_get_parent_menu,struct menu *,(struct menu *menu)); /* symbol.c */ P(symbol_hash,struct symbol *,[SYMBOL_HASHSIZE]); P(sym_change_count,int,); P(sym_lookup,struct symbol *,(const char *name, int isconst)); P(sym_find,struct symbol *,(const char *name)); P(sym_re_search,struct symbol **,(const char *pattern)); P(sym_type_name,const char *,(enum symbol_type type)); P(sym_calc_value,void,(struct symbol *sym)); P(sym_get_type,enum symbol_type,(struct symbol *sym)); P(sym_tristate_within_range,bool,(struct symbol *sym,tristate tri)); P(sym_set_tristate_value,bool,(struct symbol *sym,tristate tri)); P(sym_toggle_tristate_value,tristate,(struct symbol *sym)); P(sym_string_valid,bool,(struct symbol *sym, const char *newval)); P(sym_string_within_range,bool,(struct symbol *sym, const char *str)); P(sym_set_string_value,bool,(struct symbol *sym, const char *newval)); P(sym_is_changable,bool,(struct symbol *sym)); P(sym_get_choice_prop,struct property *,(struct symbol *sym)); P(sym_get_default_prop,struct property *,(struct symbol *sym)); P(sym_get_string_value,const char *,(struct symbol *sym)); P(prop_get_type_name,const char *,(enum prop_type type)); /* expr.c */ P(expr_compare_type,int,(enum expr_type t1, enum expr_type t2)); P(expr_print,void,(struct expr *e, void (*fn)(void *, const char *), void *data, int prevtoken)); busybox-1.22.1/scripts/kconfig/gconf.c0000644000000000000000000012114112263563520016341 0ustar rootroot/* Hey EMACS -*- linux-c -*- */ /* * * Copyright (C) 2002-2003 Romain Lievin * Released under the terms of the GNU GPL v2.0. * */ #ifdef HAVE_CONFIG_H # include #endif #include "lkc.h" #include "images.c" #include #include #include #include #include #include #include #include #include //#define DEBUG enum { SINGLE_VIEW, SPLIT_VIEW, FULL_VIEW }; static gint view_mode = FULL_VIEW; static gboolean show_name = TRUE; static gboolean show_range = TRUE; static gboolean show_value = TRUE; static gboolean show_all = FALSE; static gboolean show_debug = FALSE; static gboolean resizeable = FALSE; static gboolean config_changed = FALSE; static char nohelp_text[] = N_("Sorry, no help available for this option yet.\n"); GtkWidget *main_wnd = NULL; GtkWidget *tree1_w = NULL; // left frame GtkWidget *tree2_w = NULL; // right frame GtkWidget *text_w = NULL; GtkWidget *hpaned = NULL; GtkWidget *vpaned = NULL; GtkWidget *back_btn = NULL; GtkTextTag *tag1, *tag2; GdkColor color; GtkTreeStore *tree1, *tree2, *tree; GtkTreeModel *model1, *model2; static GtkTreeIter *parents[256]; static gint indent; static struct menu *current; // current node for SINGLE view static struct menu *browsed; // browsed node for SPLIT view enum { COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE, COL_MENU, COL_COLOR, COL_EDIT, COL_PIXBUF, COL_PIXVIS, COL_BTNVIS, COL_BTNACT, COL_BTNINC, COL_BTNRAD, COL_NUMBER }; static void display_list(void); static void display_tree(struct menu *menu); static void display_tree_part(void); static void update_tree(struct menu *src, GtkTreeIter * dst); static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row); static gchar **fill_row(struct menu *menu); /* Helping/Debugging Functions */ const char *dbg_print_stype(int val) { static char buf[256]; memset(buf, 0, 256); if (val == S_UNKNOWN) strcpy(buf, "unknown"); if (val == S_BOOLEAN) strcpy(buf, "boolean"); if (val == S_TRISTATE) strcpy(buf, "tristate"); if (val == S_INT) strcpy(buf, "int"); if (val == S_HEX) strcpy(buf, "hex"); if (val == S_STRING) strcpy(buf, "string"); if (val == S_OTHER) strcpy(buf, "other"); #ifdef DEBUG printf("%s", buf); #endif return buf; } const char *dbg_print_flags(int val) { static char buf[256]; memset(buf, 0, 256); if (val & SYMBOL_YES) strcat(buf, "yes/"); if (val & SYMBOL_MOD) strcat(buf, "mod/"); if (val & SYMBOL_NO) strcat(buf, "no/"); if (val & SYMBOL_CONST) strcat(buf, "const/"); if (val & SYMBOL_CHECK) strcat(buf, "check/"); if (val & SYMBOL_CHOICE) strcat(buf, "choice/"); if (val & SYMBOL_CHOICEVAL) strcat(buf, "choiceval/"); if (val & SYMBOL_PRINTED) strcat(buf, "printed/"); if (val & SYMBOL_VALID) strcat(buf, "valid/"); if (val & SYMBOL_OPTIONAL) strcat(buf, "optional/"); if (val & SYMBOL_WRITE) strcat(buf, "write/"); if (val & SYMBOL_CHANGED) strcat(buf, "changed/"); if (val & SYMBOL_NEW) strcat(buf, "new/"); if (val & SYMBOL_AUTO) strcat(buf, "auto/"); buf[strlen(buf) - 1] = '\0'; #ifdef DEBUG printf("%s", buf); #endif return buf; } const char *dbg_print_ptype(int val) { static char buf[256]; memset(buf, 0, 256); if (val == P_UNKNOWN) strcpy(buf, "unknown"); if (val == P_PROMPT) strcpy(buf, "prompt"); if (val == P_COMMENT) strcpy(buf, "comment"); if (val == P_MENU) strcpy(buf, "menu"); if (val == P_DEFAULT) strcpy(buf, "default"); if (val == P_CHOICE) strcpy(buf, "choice"); #ifdef DEBUG printf("%s", buf); #endif return buf; } void replace_button_icon(GladeXML * xml, GdkDrawable * window, GtkStyle * style, gchar * btn_name, gchar ** xpm) { GdkPixmap *pixmap; GdkBitmap *mask; GtkToolButton *button; GtkWidget *image; pixmap = gdk_pixmap_create_from_xpm_d(window, &mask, &style->bg[GTK_STATE_NORMAL], xpm); button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name)); image = gtk_image_new_from_pixmap(pixmap, mask); gtk_widget_show(image); gtk_tool_button_set_icon_widget(button, image); } /* Main Window Initialization */ void init_main_window(const gchar * glade_file) { GladeXML *xml; GtkWidget *widget; GtkTextBuffer *txtbuf; char title[256]; GtkStyle *style; xml = glade_xml_new(glade_file, "window1", NULL); if (!xml) g_error(_("GUI loading failed !\n")); glade_xml_signal_autoconnect(xml); main_wnd = glade_xml_get_widget(xml, "window1"); hpaned = glade_xml_get_widget(xml, "hpaned1"); vpaned = glade_xml_get_widget(xml, "vpaned1"); tree1_w = glade_xml_get_widget(xml, "treeview1"); tree2_w = glade_xml_get_widget(xml, "treeview2"); text_w = glade_xml_get_widget(xml, "textview3"); back_btn = glade_xml_get_widget(xml, "button1"); gtk_widget_set_sensitive(back_btn, FALSE); widget = glade_xml_get_widget(xml, "show_name1"); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_name); widget = glade_xml_get_widget(xml, "show_range1"); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_range); widget = glade_xml_get_widget(xml, "show_data1"); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_value); style = gtk_widget_get_style(main_wnd); widget = glade_xml_get_widget(xml, "toolbar1"); #if 0 /* Use stock Gtk icons instead */ replace_button_icon(xml, main_wnd->window, style, "button1", (gchar **) xpm_back); replace_button_icon(xml, main_wnd->window, style, "button2", (gchar **) xpm_load); replace_button_icon(xml, main_wnd->window, style, "button3", (gchar **) xpm_save); #endif replace_button_icon(xml, main_wnd->window, style, "button4", (gchar **) xpm_single_view); replace_button_icon(xml, main_wnd->window, style, "button5", (gchar **) xpm_split_view); replace_button_icon(xml, main_wnd->window, style, "button6", (gchar **) xpm_tree_view); #if 0 switch (view_mode) { case SINGLE_VIEW: widget = glade_xml_get_widget(xml, "button4"); g_signal_emit_by_name(widget, "clicked"); break; case SPLIT_VIEW: widget = glade_xml_get_widget(xml, "button5"); g_signal_emit_by_name(widget, "clicked"); break; case FULL_VIEW: widget = glade_xml_get_widget(xml, "button6"); g_signal_emit_by_name(widget, "clicked"); break; } #endif txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1", "foreground", "red", "weight", PANGO_WEIGHT_BOLD, NULL); tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2", /*"style", PANGO_STYLE_OBLIQUE, */ NULL); sprintf(title, _("BusyBox %s Configuration"), getenv("KERNELVERSION")); gtk_window_set_title(GTK_WINDOW(main_wnd), title); gtk_widget_show(main_wnd); } void init_tree_model(void) { gint i; tree = tree2 = gtk_tree_store_new(COL_NUMBER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_COLOR, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); model2 = GTK_TREE_MODEL(tree2); for (parents[0] = NULL, i = 1; i < 256; i++) parents[i] = (GtkTreeIter *) g_malloc(sizeof(GtkTreeIter)); tree1 = gtk_tree_store_new(COL_NUMBER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_COLOR, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); model1 = GTK_TREE_MODEL(tree1); } void init_left_tree(void) { GtkTreeView *view = GTK_TREE_VIEW(tree1_w); GtkCellRenderer *renderer; GtkTreeSelection *sel; GtkTreeViewColumn *column; gtk_tree_view_set_model(view, model1); gtk_tree_view_set_headers_visible(view, TRUE); gtk_tree_view_set_rules_hint(view, FALSE); column = gtk_tree_view_column_new(); gtk_tree_view_append_column(view, column); gtk_tree_view_column_set_title(column, _("Options")); renderer = gtk_cell_renderer_toggle_new(); gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), renderer, FALSE); gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), renderer, "active", COL_BTNACT, "inconsistent", COL_BTNINC, "visible", COL_BTNVIS, "radio", COL_BTNRAD, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), renderer, FALSE); gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), renderer, "text", COL_OPTION, "foreground-gdk", COL_COLOR, NULL); sel = gtk_tree_view_get_selection(view); gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); gtk_widget_realize(tree1_w); } static void renderer_edited(GtkCellRendererText * cell, const gchar * path_string, const gchar * new_text, gpointer user_data); static void renderer_toggled(GtkCellRendererToggle * cellrenderertoggle, gchar * arg1, gpointer user_data); void init_right_tree(void) { GtkTreeView *view = GTK_TREE_VIEW(tree2_w); GtkCellRenderer *renderer; GtkTreeSelection *sel; GtkTreeViewColumn *column; gint i; gtk_tree_view_set_model(view, model2); gtk_tree_view_set_headers_visible(view, TRUE); gtk_tree_view_set_rules_hint(view, FALSE); column = gtk_tree_view_column_new(); gtk_tree_view_append_column(view, column); gtk_tree_view_column_set_title(column, _("Options")); renderer = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), renderer, FALSE); gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), renderer, "pixbuf", COL_PIXBUF, "visible", COL_PIXVIS, NULL); renderer = gtk_cell_renderer_toggle_new(); gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), renderer, FALSE); gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), renderer, "active", COL_BTNACT, "inconsistent", COL_BTNINC, "visible", COL_BTNVIS, "radio", COL_BTNRAD, NULL); /*g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(renderer_toggled), NULL); */ renderer = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), renderer, FALSE); gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), renderer, "text", COL_OPTION, "foreground-gdk", COL_COLOR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(view, -1, _("Name"), renderer, "text", COL_NAME, "foreground-gdk", COL_COLOR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(view, -1, "N", renderer, "text", COL_NO, "foreground-gdk", COL_COLOR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(view, -1, "M", renderer, "text", COL_MOD, "foreground-gdk", COL_COLOR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(view, -1, "Y", renderer, "text", COL_YES, "foreground-gdk", COL_COLOR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(view, -1, _("Value"), renderer, "text", COL_VALUE, "editable", COL_EDIT, "foreground-gdk", COL_COLOR, NULL); g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(renderer_edited), NULL); column = gtk_tree_view_get_column(view, COL_NAME); gtk_tree_view_column_set_visible(column, show_name); column = gtk_tree_view_get_column(view, COL_NO); gtk_tree_view_column_set_visible(column, show_range); column = gtk_tree_view_get_column(view, COL_MOD); gtk_tree_view_column_set_visible(column, show_range); column = gtk_tree_view_get_column(view, COL_YES); gtk_tree_view_column_set_visible(column, show_range); column = gtk_tree_view_get_column(view, COL_VALUE); gtk_tree_view_column_set_visible(column, show_value); if (resizeable) { for (i = 0; i < COL_VALUE; i++) { column = gtk_tree_view_get_column(view, i); gtk_tree_view_column_set_resizable(column, TRUE); } } sel = gtk_tree_view_get_selection(view); gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); } /* Utility Functions */ static void text_insert_help(struct menu *menu) { GtkTextBuffer *buffer; GtkTextIter start, end; const char *prompt = menu_get_prompt(menu); gchar *name; const char *help = _(nohelp_text); if (!menu->sym) help = ""; else if (menu->sym->help) help = _(menu->sym->help); if (menu->sym && menu->sym->name) name = g_strdup_printf(_(menu->sym->name)); else name = g_strdup(""); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); gtk_text_buffer_get_bounds(buffer, &start, &end); gtk_text_buffer_delete(buffer, &start, &end); gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15); gtk_text_buffer_get_end_iter(buffer, &end); gtk_text_buffer_insert_with_tags(buffer, &end, prompt, -1, tag1, NULL); gtk_text_buffer_insert_at_cursor(buffer, " ", 1); gtk_text_buffer_get_end_iter(buffer, &end); gtk_text_buffer_insert_with_tags(buffer, &end, name, -1, tag1, NULL); gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2); gtk_text_buffer_get_end_iter(buffer, &end); gtk_text_buffer_insert_with_tags(buffer, &end, help, -1, tag2, NULL); } static void text_insert_msg(const char *title, const char *message) { GtkTextBuffer *buffer; GtkTextIter start, end; const char *msg = message; buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); gtk_text_buffer_get_bounds(buffer, &start, &end); gtk_text_buffer_delete(buffer, &start, &end); gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15); gtk_text_buffer_get_end_iter(buffer, &end); gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1, NULL); gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2); gtk_text_buffer_get_end_iter(buffer, &end); gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2, NULL); } /* Main Windows Callbacks */ void on_save1_activate(GtkMenuItem * menuitem, gpointer user_data); gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event, gpointer user_data) { GtkWidget *dialog, *label; gint result; if (config_changed == FALSE) return FALSE; dialog = gtk_dialog_new_with_buttons(_("Warning !"), GTK_WINDOW(main_wnd), (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_STOCK_OK, GTK_RESPONSE_YES, GTK_STOCK_NO, GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL); label = gtk_label_new(_("\nSave configuration ?\n")); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); gtk_widget_show(label); result = gtk_dialog_run(GTK_DIALOG(dialog)); switch (result) { case GTK_RESPONSE_YES: on_save1_activate(NULL, NULL); return FALSE; case GTK_RESPONSE_NO: return FALSE; case GTK_RESPONSE_CANCEL: case GTK_RESPONSE_DELETE_EVENT: default: gtk_widget_destroy(dialog); return TRUE; } return FALSE; } void on_window1_destroy(GtkObject * object, gpointer user_data) { gtk_main_quit(); } void on_window1_size_request(GtkWidget * widget, GtkRequisition * requisition, gpointer user_data) { static gint old_h; gint w, h; if (widget->window == NULL) gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); else gdk_window_get_size(widget->window, &w, &h); if (h == old_h) return; old_h = h; gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3); } /* Menu & Toolbar Callbacks */ static void load_filename(GtkFileSelection * file_selector, gpointer user_data) { const gchar *fn; fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION (user_data)); if (conf_read(fn)) text_insert_msg(_("Error"), _("Unable to load configuration !")); else display_tree(&rootmenu); } void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data) { GtkWidget *fs; fs = gtk_file_selection_new(_("Load file...")); g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked", G_CALLBACK(load_filename), (gpointer) fs); g_signal_connect_swapped(GTK_OBJECT (GTK_FILE_SELECTION(fs)->ok_button), "clicked", G_CALLBACK(gtk_widget_destroy), (gpointer) fs); g_signal_connect_swapped(GTK_OBJECT (GTK_FILE_SELECTION(fs)->cancel_button), "clicked", G_CALLBACK(gtk_widget_destroy), (gpointer) fs); gtk_widget_show(fs); } void on_save1_activate(GtkMenuItem * menuitem, gpointer user_data) { if (conf_write(NULL)) text_insert_msg(_("Error"), _("Unable to save configuration !")); config_changed = FALSE; } static void store_filename(GtkFileSelection * file_selector, gpointer user_data) { const gchar *fn; fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION (user_data)); if (conf_write(fn)) text_insert_msg(_("Error"), _("Unable to save configuration !")); gtk_widget_destroy(GTK_WIDGET(user_data)); } void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data) { GtkWidget *fs; fs = gtk_file_selection_new(_("Save file as...")); g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked", G_CALLBACK(store_filename), (gpointer) fs); g_signal_connect_swapped(GTK_OBJECT (GTK_FILE_SELECTION(fs)->ok_button), "clicked", G_CALLBACK(gtk_widget_destroy), (gpointer) fs); g_signal_connect_swapped(GTK_OBJECT (GTK_FILE_SELECTION(fs)->cancel_button), "clicked", G_CALLBACK(gtk_widget_destroy), (gpointer) fs); gtk_widget_show(fs); } void on_quit1_activate(GtkMenuItem * menuitem, gpointer user_data) { if (!on_window1_delete_event(NULL, NULL, NULL)) gtk_widget_destroy(GTK_WIDGET(main_wnd)); } void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data) { GtkTreeViewColumn *col; show_name = GTK_CHECK_MENU_ITEM(menuitem)->active; col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NAME); if (col) gtk_tree_view_column_set_visible(col, show_name); } void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data) { GtkTreeViewColumn *col; show_range = GTK_CHECK_MENU_ITEM(menuitem)->active; col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NO); if (col) gtk_tree_view_column_set_visible(col, show_range); col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_MOD); if (col) gtk_tree_view_column_set_visible(col, show_range); col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_YES); if (col) gtk_tree_view_column_set_visible(col, show_range); } void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data) { GtkTreeViewColumn *col; show_value = GTK_CHECK_MENU_ITEM(menuitem)->active; col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_VALUE); if (col) gtk_tree_view_column_set_visible(col, show_value); } void on_show_all_options1_activate(GtkMenuItem * menuitem, gpointer user_data) { show_all = GTK_CHECK_MENU_ITEM(menuitem)->active; gtk_tree_store_clear(tree2); display_tree(&rootmenu); // instead of update_tree to speed-up } void on_show_debug_info1_activate(GtkMenuItem * menuitem, gpointer user_data) { show_debug = GTK_CHECK_MENU_ITEM(menuitem)->active; update_tree(&rootmenu, NULL); } void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data) { GtkWidget *dialog; const gchar *intro_text = _( "Welcome to gkc, the GTK+ graphical configuration tool.\n" "For each option, a blank box indicates the feature is disabled, a\n" "check indicates it is enabled, and a dot indicates that it is to\n" "be compiled as a module. Clicking on the box will cycle through the three states.\n" "\n" "If you do not see an option (e.g., a device driver) that you\n" "believe should be present, try turning on Show All Options\n" "under the Options menu.\n" "Although there is no cross reference yet to help you figure out\n" "what other options must be enabled to support the option you\n" "are interested in, you can still view the help of a grayed-out\n" "option.\n" "\n" "Toggling Show Debug Info under the Options menu will show\n" "the dependencies, which you can then match by examining other options."); dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, intro_text); g_signal_connect_swapped(GTK_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), GTK_OBJECT(dialog)); gtk_widget_show_all(dialog); } void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data) { GtkWidget *dialog; const gchar *about_text = _("gkc is copyright (c) 2002 Romain Lievin .\n" "Based on the source code from Roman Zippel.\n"); dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, about_text); g_signal_connect_swapped(GTK_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), GTK_OBJECT(dialog)); gtk_widget_show_all(dialog); } void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data) { GtkWidget *dialog; const gchar *license_text = _("gkc is released under the terms of the GNU GPL v2.\n" "For more information, please see the source code or\n" "visit http://www.fsf.org/licenses/licenses.html\n"); dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, license_text); g_signal_connect_swapped(GTK_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), GTK_OBJECT(dialog)); gtk_widget_show_all(dialog); } void on_back_clicked(GtkButton * button, gpointer user_data) { enum prop_type ptype; current = current->parent; ptype = current->prompt ? current->prompt->type : P_UNKNOWN; if (ptype != P_MENU) current = current->parent; display_tree_part(); if (current == &rootmenu) gtk_widget_set_sensitive(back_btn, FALSE); } void on_load_clicked(GtkButton * button, gpointer user_data) { on_load1_activate(NULL, user_data); } void on_save_clicked(GtkButton * button, gpointer user_data) { on_save1_activate(NULL, user_data); } void on_single_clicked(GtkButton * button, gpointer user_data) { view_mode = SINGLE_VIEW; gtk_paned_set_position(GTK_PANED(hpaned), 0); gtk_widget_hide(tree1_w); current = &rootmenu; display_tree_part(); } void on_split_clicked(GtkButton * button, gpointer user_data) { gint w, h; view_mode = SPLIT_VIEW; gtk_widget_show(tree1_w); gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); gtk_paned_set_position(GTK_PANED(hpaned), w / 2); if (tree2) gtk_tree_store_clear(tree2); display_list(); /* Disable back btn, like in full mode. */ gtk_widget_set_sensitive(back_btn, FALSE); } void on_full_clicked(GtkButton * button, gpointer user_data) { view_mode = FULL_VIEW; gtk_paned_set_position(GTK_PANED(hpaned), 0); gtk_widget_hide(tree1_w); if (tree2) gtk_tree_store_clear(tree2); display_tree(&rootmenu); gtk_widget_set_sensitive(back_btn, FALSE); } void on_collapse_clicked(GtkButton * button, gpointer user_data) { gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w)); } void on_expand_clicked(GtkButton * button, gpointer user_data) { gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); } /* CTree Callbacks */ /* Change hex/int/string value in the cell */ static void renderer_edited(GtkCellRendererText * cell, const gchar * path_string, const gchar * new_text, gpointer user_data) { GtkTreePath *path = gtk_tree_path_new_from_string(path_string); GtkTreeIter iter; const char *old_def, *new_def; struct menu *menu; struct symbol *sym; if (!gtk_tree_model_get_iter(model2, &iter, path)) return; gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); sym = menu->sym; gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1); new_def = new_text; sym_set_string_value(sym, new_def); config_changed = TRUE; update_tree(&rootmenu, NULL); gtk_tree_path_free(path); } /* Change the value of a symbol and update the tree */ static void change_sym_value(struct menu *menu, gint col) { struct symbol *sym = menu->sym; tristate oldval, newval; if (!sym) return; if (col == COL_NO) newval = no; else if (col == COL_MOD) newval = mod; else if (col == COL_YES) newval = yes; else return; switch (sym_get_type(sym)) { case S_BOOLEAN: case S_TRISTATE: oldval = sym_get_tristate_value(sym); if (!sym_tristate_within_range(sym, newval)) newval = yes; sym_set_tristate_value(sym, newval); config_changed = TRUE; if (view_mode == FULL_VIEW) update_tree(&rootmenu, NULL); else if (view_mode == SPLIT_VIEW) { update_tree(browsed, NULL); display_list(); } else if (view_mode == SINGLE_VIEW) display_tree_part(); //fixme: keep exp/coll break; case S_INT: case S_HEX: case S_STRING: default: break; } } static void toggle_sym_value(struct menu *menu) { if (!menu->sym) return; sym_toggle_tristate_value(menu->sym); if (view_mode == FULL_VIEW) update_tree(&rootmenu, NULL); else if (view_mode == SPLIT_VIEW) { update_tree(browsed, NULL); display_list(); } else if (view_mode == SINGLE_VIEW) display_tree_part(); //fixme: keep exp/coll } static void renderer_toggled(GtkCellRendererToggle * cell, gchar * path_string, gpointer user_data) { GtkTreePath *path, *sel_path = NULL; GtkTreeIter iter, sel_iter; GtkTreeSelection *sel; struct menu *menu; path = gtk_tree_path_new_from_string(path_string); if (!gtk_tree_model_get_iter(model2, &iter, path)) return; sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree2_w)); if (gtk_tree_selection_get_selected(sel, NULL, &sel_iter)) sel_path = gtk_tree_model_get_path(model2, &sel_iter); if (!sel_path) goto out1; if (gtk_tree_path_compare(path, sel_path)) goto out2; gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); toggle_sym_value(menu); out2: gtk_tree_path_free(sel_path); out1: gtk_tree_path_free(path); } static gint column2index(GtkTreeViewColumn * column) { gint i; for (i = 0; i < COL_NUMBER; i++) { GtkTreeViewColumn *col; col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), i); if (col == column) return i; } return -1; } /* User click: update choice (full) or goes down (single) */ gboolean on_treeview2_button_press_event(GtkWidget * widget, GdkEventButton * event, gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); GtkTreePath *path; GtkTreeViewColumn *column; GtkTreeIter iter; struct menu *menu; gint col; #if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK gint tx = (gint) event->x; gint ty = (gint) event->y; gint cx, cy; gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, &cy); #else gtk_tree_view_get_cursor(view, &path, &column); #endif if (path == NULL) return FALSE; if (!gtk_tree_model_get_iter(model2, &iter, path)) return FALSE; gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); col = column2index(column); if (event->type == GDK_2BUTTON_PRESS) { enum prop_type ptype; ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) { // goes down into menu current = menu; display_tree_part(); gtk_widget_set_sensitive(back_btn, TRUE); } else if ((col == COL_OPTION)) { toggle_sym_value(menu); gtk_tree_view_expand_row(view, path, TRUE); } } else { if (col == COL_VALUE) { toggle_sym_value(menu); gtk_tree_view_expand_row(view, path, TRUE); } else if (col == COL_NO || col == COL_MOD || col == COL_YES) { change_sym_value(menu, col); gtk_tree_view_expand_row(view, path, TRUE); } } return FALSE; } /* Key pressed: update choice */ gboolean on_treeview2_key_press_event(GtkWidget * widget, GdkEventKey * event, gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); GtkTreePath *path; GtkTreeViewColumn *column; GtkTreeIter iter; struct menu *menu; gint col; gtk_tree_view_get_cursor(view, &path, &column); if (path == NULL) return FALSE; if (event->keyval == GDK_space) { if (gtk_tree_view_row_expanded(view, path)) gtk_tree_view_collapse_row(view, path); else gtk_tree_view_expand_row(view, path, FALSE); return TRUE; } if (event->keyval == GDK_KP_Enter) { } if (widget == tree1_w) return FALSE; gtk_tree_model_get_iter(model2, &iter, path); gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); if (!strcasecmp(event->string, "n")) col = COL_NO; else if (!strcasecmp(event->string, "m")) col = COL_MOD; else if (!strcasecmp(event->string, "y")) col = COL_YES; else col = -1; change_sym_value(menu, col); return FALSE; } /* Row selection changed: update help */ void on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data) { GtkTreeSelection *selection; GtkTreeIter iter; struct menu *menu; selection = gtk_tree_view_get_selection(treeview); if (gtk_tree_selection_get_selected(selection, &model2, &iter)) { gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); text_insert_help(menu); } } /* User click: display sub-tree in the right frame. */ gboolean on_treeview1_button_press_event(GtkWidget * widget, GdkEventButton * event, gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); GtkTreePath *path; GtkTreeViewColumn *column; GtkTreeIter iter; struct menu *menu; gint tx = (gint) event->x; gint ty = (gint) event->y; gint cx, cy; gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, &cy); if (path == NULL) return FALSE; gtk_tree_model_get_iter(model1, &iter, path); gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1); if (event->type == GDK_2BUTTON_PRESS) { toggle_sym_value(menu); current = menu; display_tree_part(); } else { browsed = menu; display_tree_part(); } gtk_widget_realize(tree2_w); gtk_tree_view_set_cursor(view, path, NULL, FALSE); gtk_widget_grab_focus(tree2_w); return FALSE; } /* Fill a row of strings */ static gchar **fill_row(struct menu *menu) { static gchar *row[COL_NUMBER]; struct symbol *sym = menu->sym; const char *def; int stype; tristate val; enum prop_type ptype; int i; for (i = COL_OPTION; i <= COL_COLOR; i++) g_free(row[i]); memset(row, 0, sizeof(row)); row[COL_OPTION] = g_strdup_printf("%s %s", menu_get_prompt(menu), sym ? (sym-> flags & SYMBOL_NEW ? "(NEW)" : "") : ""); if (show_all && !menu_is_visible(menu)) row[COL_COLOR] = g_strdup("DarkGray"); else row[COL_COLOR] = g_strdup("Black"); ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; switch (ptype) { case P_MENU: row[COL_PIXBUF] = (gchar *) xpm_menu; if (view_mode == SINGLE_VIEW) row[COL_PIXVIS] = GINT_TO_POINTER(TRUE); row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); break; case P_COMMENT: row[COL_PIXBUF] = (gchar *) xpm_void; row[COL_PIXVIS] = GINT_TO_POINTER(FALSE); row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); break; default: row[COL_PIXBUF] = (gchar *) xpm_void; row[COL_PIXVIS] = GINT_TO_POINTER(FALSE); row[COL_BTNVIS] = GINT_TO_POINTER(TRUE); break; } if (!sym) return row; row[COL_NAME] = g_strdup(sym->name); sym_calc_value(sym); sym->flags &= ~SYMBOL_CHANGED; if (sym_is_choice(sym)) { // parse childs for getting final value struct menu *child; struct symbol *def_sym = sym_get_choice_value(sym); struct menu *def_menu = NULL; row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); for (child = menu->list; child; child = child->next) { if (menu_is_visible(child) && child->sym == def_sym) def_menu = child; } if (def_menu) row[COL_VALUE] = g_strdup(menu_get_prompt(def_menu)); } if (sym->flags & SYMBOL_CHOICEVAL) row[COL_BTNRAD] = GINT_TO_POINTER(TRUE); stype = sym_get_type(sym); switch (stype) { case S_BOOLEAN: if (GPOINTER_TO_INT(row[COL_PIXVIS]) == FALSE) row[COL_BTNVIS] = GINT_TO_POINTER(TRUE); if (sym_is_choice(sym)) break; case S_TRISTATE: val = sym_get_tristate_value(sym); switch (val) { case no: row[COL_NO] = g_strdup("N"); row[COL_VALUE] = g_strdup("N"); row[COL_BTNACT] = GINT_TO_POINTER(FALSE); row[COL_BTNINC] = GINT_TO_POINTER(FALSE); break; case mod: row[COL_MOD] = g_strdup("M"); row[COL_VALUE] = g_strdup("M"); row[COL_BTNINC] = GINT_TO_POINTER(TRUE); break; case yes: row[COL_YES] = g_strdup("Y"); row[COL_VALUE] = g_strdup("Y"); row[COL_BTNACT] = GINT_TO_POINTER(TRUE); row[COL_BTNINC] = GINT_TO_POINTER(FALSE); break; } if (val != no && sym_tristate_within_range(sym, no)) row[COL_NO] = g_strdup("_"); if (val != mod && sym_tristate_within_range(sym, mod)) row[COL_MOD] = g_strdup("_"); if (val != yes && sym_tristate_within_range(sym, yes)) row[COL_YES] = g_strdup("_"); break; case S_INT: case S_HEX: case S_STRING: def = sym_get_string_value(sym); row[COL_VALUE] = g_strdup(def); row[COL_EDIT] = GINT_TO_POINTER(TRUE); row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); break; } return row; } /* Set the node content with a row of strings */ static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row) { GdkColor color; gboolean success; GdkPixbuf *pix; pix = gdk_pixbuf_new_from_xpm_data((const char **) row[COL_PIXBUF]); gdk_color_parse(row[COL_COLOR], &color); gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1, FALSE, FALSE, &success); gtk_tree_store_set(tree, node, COL_OPTION, row[COL_OPTION], COL_NAME, row[COL_NAME], COL_NO, row[COL_NO], COL_MOD, row[COL_MOD], COL_YES, row[COL_YES], COL_VALUE, row[COL_VALUE], COL_MENU, (gpointer) menu, COL_COLOR, &color, COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]), COL_PIXBUF, pix, COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]), COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]), COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]), COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]), COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]), -1); g_object_unref(pix); } /* Add a node to the tree */ static void place_node(struct menu *menu, char **row) { GtkTreeIter *parent = parents[indent - 1]; GtkTreeIter *node = parents[indent]; gtk_tree_store_append(tree, node, parent); set_node(node, menu, row); } /* Find a node in the GTK+ tree */ static GtkTreeIter found; /* * Find a menu in the GtkTree starting at parent. */ GtkTreeIter *gtktree_iter_find_node(GtkTreeIter * parent, struct menu *tofind) { GtkTreeIter iter; GtkTreeIter *child = &iter; gboolean valid; GtkTreeIter *ret; valid = gtk_tree_model_iter_children(model2, child, parent); while (valid) { struct menu *menu; gtk_tree_model_get(model2, child, 6, &menu, -1); if (menu == tofind) { memcpy(&found, child, sizeof(GtkTreeIter)); return &found; } ret = gtktree_iter_find_node(child, tofind); if (ret) return ret; valid = gtk_tree_model_iter_next(model2, child); } return NULL; } /* * Update the tree by adding/removing entries * Does not change other nodes */ static void update_tree(struct menu *src, GtkTreeIter * dst) { struct menu *child1; GtkTreeIter iter, tmp; GtkTreeIter *child2 = &iter; gboolean valid; GtkTreeIter *sibling; struct symbol *sym; struct property *prop; struct menu *menu1, *menu2; if (src == &rootmenu) indent = 1; valid = gtk_tree_model_iter_children(model2, child2, dst); for (child1 = src->list; child1; child1 = child1->next) { prop = child1->prompt; sym = child1->sym; reparse: menu1 = child1; if (valid) gtk_tree_model_get(model2, child2, COL_MENU, &menu2, -1); else menu2 = NULL; // force adding of a first child #ifdef DEBUG printf("%*c%s | %s\n", indent, ' ', menu1 ? menu_get_prompt(menu1) : "nil", menu2 ? menu_get_prompt(menu2) : "nil"); #endif if (!menu_is_visible(child1) && !show_all) { // remove node if (gtktree_iter_find_node(dst, menu1) != NULL) { memcpy(&tmp, child2, sizeof(GtkTreeIter)); valid = gtk_tree_model_iter_next(model2, child2); gtk_tree_store_remove(tree2, &tmp); if (!valid) return; // next parent else goto reparse; // next child } else continue; } if (menu1 != menu2) { if (gtktree_iter_find_node(dst, menu1) == NULL) { // add node if (!valid && !menu2) sibling = NULL; else sibling = child2; gtk_tree_store_insert_before(tree2, child2, dst, sibling); set_node(child2, menu1, fill_row(menu1)); if (menu2 == NULL) valid = TRUE; } else { // remove node memcpy(&tmp, child2, sizeof(GtkTreeIter)); valid = gtk_tree_model_iter_next(model2, child2); gtk_tree_store_remove(tree2, &tmp); if (!valid) return; // next parent else goto reparse; // next child } } else if (sym && (sym->flags & SYMBOL_CHANGED)) { set_node(child2, menu1, fill_row(menu1)); } indent++; update_tree(child1, child2); indent--; valid = gtk_tree_model_iter_next(model2, child2); } } /* Display the whole tree (single/split/full view) */ static void display_tree(struct menu *menu) { struct symbol *sym; struct property *prop; struct menu *child; enum prop_type ptype; if (menu == &rootmenu) { indent = 1; current = &rootmenu; } for (child = menu->list; child; child = child->next) { prop = child->prompt; sym = child->sym; ptype = prop ? prop->type : P_UNKNOWN; if (sym) sym->flags &= ~SYMBOL_CHANGED; if ((view_mode == SPLIT_VIEW) && !(child->flags & MENU_ROOT) && (tree == tree1)) continue; if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT) && (tree == tree2)) continue; if (menu_is_visible(child) || show_all) place_node(child, fill_row(child)); #ifdef DEBUG printf("%*c%s: ", indent, ' ', menu_get_prompt(child)); printf("%s", child->flags & MENU_ROOT ? "rootmenu | " : ""); dbg_print_ptype(ptype); printf(" | "); if (sym) { dbg_print_stype(sym->type); printf(" | "); dbg_print_flags(sym->flags); printf("\n"); } else printf("\n"); #endif if ((view_mode != FULL_VIEW) && (ptype == P_MENU) && (tree == tree2)) continue; /* if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT)) || (view_mode == FULL_VIEW) || (view_mode == SPLIT_VIEW))*/ if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT)) || (view_mode == FULL_VIEW) || (view_mode == SPLIT_VIEW)) { indent++; display_tree(child); indent--; } } } /* Display a part of the tree starting at current node (single/split view) */ static void display_tree_part(void) { if (tree2) gtk_tree_store_clear(tree2); if (view_mode == SINGLE_VIEW) display_tree(current); else if (view_mode == SPLIT_VIEW) display_tree(browsed); gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); } /* Display the list in the left frame (split view) */ static void display_list(void) { if (tree1) gtk_tree_store_clear(tree1); tree = tree1; display_tree(&rootmenu); gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w)); tree = tree2; } void fixup_rootmenu(struct menu *menu) { struct menu *child; static int menu_cnt = 0; menu->flags |= MENU_ROOT; for (child = menu->list; child; child = child->next) { if (child->prompt && child->prompt->type == P_MENU) { menu_cnt++; fixup_rootmenu(child); menu_cnt--; } else if (!menu_cnt) fixup_rootmenu(child); } } /* Main */ int main(int ac, char *av[]) { const char *name; char *env; gchar *glade_file; #ifndef LKC_DIRECT_LINK kconfig_load(); #endif bindtextdomain(PACKAGE, LOCALEDIR); bind_textdomain_codeset(PACKAGE, "UTF-8"); textdomain(PACKAGE); /* GTK stuffs */ gtk_set_locale(); gtk_init(&ac, &av); glade_init(); //add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps"); //add_pixmap_directory (PACKAGE_SOURCE_DIR "/pixmaps"); /* Determine GUI path */ env = getenv(SRCTREE); if (env) glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL); else if (av[0][0] == '/') glade_file = g_strconcat(av[0], ".glade", NULL); else glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL); /* Load the interface and connect signals */ init_main_window(glade_file); init_tree_model(); init_left_tree(); init_right_tree(); /* Conf stuffs */ if (ac > 1 && av[1][0] == '-') { switch (av[1][1]) { case 'a': //showAll = 1; break; case 'h': case '?': printf("%s \n", av[0]); exit(0); } name = av[2]; } else name = av[1]; conf_parse(name); fixup_rootmenu(&rootmenu); conf_read(NULL); switch (view_mode) { case SINGLE_VIEW: display_tree_part(); break; case SPLIT_VIEW: display_list(); break; case FULL_VIEW: display_tree(&rootmenu); break; } gtk_main(); return 0; } busybox-1.22.1/scripts/kconfig/mconf.c0000644000000000000000000006615012263563520016357 0ustar rootroot/* * Copyright (C) 2002 Roman Zippel * Released under the terms of the GNU GPL v2.0. * * Introduced single menu mode (show all sub-menus in one large tree). * 2002-11-06 Petr Baudis * * i18n, 2005, Arnaldo Carvalho de Melo */ #define _XOPEN_SOURCE 700 /* On Darwin, this may be needed to get SIGWINCH: */ #define _DARWIN_C_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include /* for strcasecmp */ #include #include #include #define LKC_DIRECT_LINK #include "lkc.h" static char menu_backtitle[128]; static const char mconf_readme[] = N_( "Overview\n" "--------\n" "Some features may be built directly into busybox.\n" "Some may be made into standalone applets. Some features\n" "may be completely removed altogether. There are also certain\n" "parameters which are not really features, but must be\n" "entered in as decimal or hexadecimal numbers or possibly text.\n" "\n" "Menu items beginning with [*], or [ ] represent features\n" "configured to be built in, modularized or removed respectively.\n" "Pointed brackets <> represent module capable features.\n" "\n" "To change any of these features, highlight it with the cursor\n" "keys and press to build it in, to make it a module or\n" " to removed it. You may also press the to cycle\n" "through the available options (ie. Y->N->M->Y).\n" "\n" "Some additional keyboard hints:\n" "\n" "Menus\n" "----------\n" "o Use the Up/Down arrow keys (cursor keys) to highlight the item\n" " you wish to change or submenu wish to select and press .\n" " Submenus are designated by \"--->\".\n" "\n" " Shortcut: Press the option's highlighted letter (hotkey).\n" " Pressing a hotkey more than once will sequence\n" " through all visible items which use that hotkey.\n" "\n" " You may also use the and keys to scroll\n" " unseen options into view.\n" "\n" "o To exit a menu use the cursor keys to highlight the button\n" " and press .\n" "\n" " Shortcut: Press or or if there is no hotkey\n" " using those letters. You may press a single , but\n" " there is a delayed response which you may find annoying.\n" "\n" " Also, the and cursor keys will cycle between and\n" " \n" "\n" "\n" "Data Entry\n" "-----------\n" "o Enter the requested information and press \n" " If you are entering hexadecimal values, it is not necessary to\n" " add the '0x' prefix to the entry.\n" "\n" "o For help, use the or cursor keys to highlight the help option\n" " and press . You can try as well.\n" "\n" "\n" "Text Box (Help Window)\n" "--------\n" "o Use the cursor keys to scroll up/down/left/right. The VI editor\n" " keys h,j,k,l function here as do and for those\n" " who are familiar with less and lynx.\n" "\n" "o Press , , or to exit.\n" "\n" "\n" "Alternate Configuration Files\n" "-----------------------------\n" "Menuconfig supports the use of alternate configuration files for\n" "those who, for various reasons, find it necessary to switch\n" "between different configurations.\n" "\n" "At the end of the main menu you will find two options. One is\n" "for saving the current configuration to a file of your choosing.\n" "The other option is for loading a previously saved alternate\n" "configuration.\n" "\n" "Even if you don't use alternate configuration files, but you\n" "find during a Menuconfig session that you have completely messed\n" "up your settings, you may use the \"Load Alternate...\" option to\n" "restore your previously saved settings from \".config\" without\n" "restarting Menuconfig.\n" "\n" "Other information\n" "-----------------\n" "If you use Menuconfig in an XTERM window make sure you have your\n" "$TERM variable set to point to a xterm definition which supports color.\n" "Otherwise, Menuconfig will look rather bad. Menuconfig will not\n" "display correctly in a RXVT window because rxvt displays only one\n" "intensity of color, bright.\n" "\n" "Menuconfig will display larger menus on screens or xterms which are\n" "set to display more than the standard 25 row by 80 column geometry.\n" "In order for this to work, the \"stty size\" command must be able to\n" "display the screen's current row and column geometry. I STRONGLY\n" "RECOMMEND that you make sure you do NOT have the shell variables\n" "LINES and COLUMNS exported into your environment. Some distributions\n" "export those variables via /etc/profile. Some ncurses programs can\n" "become confused when those variables (LINES & COLUMNS) don't reflect\n" "the true screen size.\n" "\n" "Optional personality available\n" "------------------------------\n" "If you prefer to have all of the options listed in a single\n" "menu, rather than the default multimenu hierarchy, run the menuconfig\n" "with MENUCONFIG_MODE environment variable set to single_menu. Example:\n" "\n" "make MENUCONFIG_MODE=single_menu menuconfig\n" "\n" " will then unroll the appropriate category, or enfold it if it\n" "is already unrolled.\n" "\n" "Note that this mode can eventually be a little more CPU expensive\n" "(especially with a larger number of unrolled categories) than the\n" "default mode.\n"), menu_instructions[] = N_( "Arrow keys navigate the menu. " " selects submenus --->. " "Highlighted letters are hotkeys. " "Pressing includes, excludes, modularizes features. " "Press to exit, for Help, for Search. " "Legend: [*] built-in [ ] excluded module < > module capable"), radiolist_instructions[] = N_( "Use the arrow keys to navigate this window or " "press the hotkey of the item you wish to select " "followed by the . " "Press for additional information about this option."), inputbox_instructions_int[] = N_( "Please enter a decimal value. " "Fractions will not be accepted. " "Use the key to move from the input field to the buttons below it."), inputbox_instructions_hex[] = N_( "Please enter a hexadecimal value. " "Use the key to move from the input field to the buttons below it."), inputbox_instructions_string[] = N_( "Please enter a string value. " "Use the key to move from the input field to the buttons below it."), setmod_text[] = N_( "This feature depends on another which has been configured as a module.\n" "As a result, this feature will be built as a module."), nohelp_text[] = N_( "There is no help available for this option.\n"), load_config_text[] = N_( "Enter the name of the configuration file you wish to load. " "Accept the name shown to restore the configuration you " "last retrieved. Leave blank to abort."), load_config_help[] = N_( "\n" "For various reasons, one may wish to keep several different\n" "configurations available on a single machine.\n" "\n" "If you have saved a previous configuration in a file other than\n" "default, entering the name of the file here will allow you\n" "to modify that configuration.\n" "\n" "If you are uncertain, then you have probably never used alternate\n" "configuration files. You should therefor leave this blank to abort.\n"), save_config_text[] = N_( "Enter a filename to which this configuration should be saved " "as an alternate. Leave blank to abort."), save_config_help[] = N_( "\n" "For various reasons, one may wish to keep different\n" "configurations available on a single machine.\n" "\n" "Entering a file name here will allow you to later retrieve, modify\n" "and use the current configuration as an alternate to whatever\n" "configuration options you have selected at that time.\n" "\n" "If you are uncertain what all this means then you should probably\n" "leave this blank.\n"), search_help[] = N_( "\n" "Search for CONFIG_ symbols and display their relations.\n" "Regular expressions are allowed.\n" "Example: search for \"^FOO\"\n" "Result:\n" "-----------------------------------------------------------------\n" "Symbol: FOO [=m]\n" "Prompt: Foo bus is used to drive the bar HW\n" "Defined at drivers/pci/Kconfig:47\n" "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n" "Location:\n" " -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n" " -> PCI support (PCI [=y])\n" " -> PCI access mode ( [=y])\n" "Selects: LIBCRC32\n" "Selected by: BAR\n" "-----------------------------------------------------------------\n" "o The line 'Prompt:' shows the text used in the menu structure for\n" " this CONFIG_ symbol\n" "o The 'Defined at' line tell at what file / line number the symbol\n" " is defined\n" "o The 'Depends on:' line tell what symbols needs to be defined for\n" " this symbol to be visible in the menu (selectable)\n" "o The 'Location:' lines tell where in the menu structure this symbol\n" " is located\n" " A location followed by a [=y] indicate that this is a selectable\n" " menu item - and current value is displayed inside brackets.\n" "o The 'Selects:' line tell what symbol will be automatically\n" " selected if this symbol is selected (y or m)\n" "o The 'Selected by' line tell what symbol has selected this symbol\n" "\n" "Only relevant lines are shown.\n" "\n\n" "Search examples:\n" "Examples: USB => find all CONFIG_ symbols containing USB\n" " ^USB => find all CONFIG_ symbols starting with USB\n" " USB$ => find all CONFIG_ symbols ending with USB\n" "\n"); static char buf[4096*10], *bufptr = buf; static char input_buf[4096]; static const char filename[] = ".config"; static char *args[1024], **argptr = args; static int indent; static struct termios ios_org; static int rows = 0, cols = 0; static struct menu *current_menu; static int child_count; static int do_resize; static int single_menu_mode; static void conf(struct menu *menu); static void conf_choice(struct menu *menu); static void conf_string(struct menu *menu); static void conf_load(void); static void conf_save(void); static void show_textbox(const char *title, const char *text, int r, int c); static void show_helptext(const char *title, const char *text); static void show_help(struct menu *menu); static void show_file(const char *filename, const char *title, int r, int c); static void cprint_init(void); static int cprint1(const char *fmt, ...); static void cprint_done(void); static int cprint(const char *fmt, ...); static void init_wsize(void) { struct winsize ws; char *env; if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) { rows = ws.ws_row; cols = ws.ws_col; } if (!rows) { env = getenv("LINES"); if (env) rows = atoi(env); if (!rows) rows = 24; } if (!cols) { env = getenv("COLUMNS"); if (env) cols = atoi(env); if (!cols) cols = 80; } if (rows < 19 || cols < 80) { fprintf(stderr, N_("Your display is too small to run Menuconfig!\n")); fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n")); exit(1); } rows -= 4; cols -= 5; } static void cprint_init(void) { bufptr = buf; argptr = args; memset(args, 0, sizeof(args)); indent = 0; child_count = 0; cprint("./scripts/kconfig/lxdialog/lxdialog"); cprint("--backtitle"); cprint(menu_backtitle); } static int cprint1(const char *fmt, ...) { va_list ap; int res; if (!*argptr) *argptr = bufptr; va_start(ap, fmt); res = vsprintf(bufptr, fmt, ap); va_end(ap); bufptr += res; return res; } static void cprint_done(void) { *bufptr++ = 0; argptr++; } static int cprint(const char *fmt, ...) { va_list ap; int res; *argptr++ = bufptr; va_start(ap, fmt); res = vsprintf(bufptr, fmt, ap); va_end(ap); bufptr += res; *bufptr++ = 0; return res; } static void get_prompt_str(struct gstr *r, struct property *prop) { int i, j; struct menu *submenu[8], *menu; str_printf(r, "Prompt: %s\n", prop->text); str_printf(r, " Defined at %s:%d\n", prop->menu->file->name, prop->menu->lineno); if (!expr_is_yes(prop->visible.expr)) { str_append(r, " Depends on: "); expr_gstr_print(prop->visible.expr, r); str_append(r, "\n"); } menu = prop->menu->parent; for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) submenu[i++] = menu; if (i > 0) { str_printf(r, " Location:\n"); for (j = 4; --i >= 0; j += 2) { menu = submenu[i]; str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu)); if (menu->sym) { str_printf(r, " (%s [=%s])", menu->sym->name ? menu->sym->name : "", sym_get_string_value(menu->sym)); } str_append(r, "\n"); } } } static void get_symbol_str(struct gstr *r, struct symbol *sym) { bool hit; struct property *prop; str_printf(r, "Symbol: %s [=%s]\n", sym->name, sym_get_string_value(sym)); for_all_prompts(sym, prop) get_prompt_str(r, prop); hit = false; for_all_properties(sym, prop, P_SELECT) { if (!hit) { str_append(r, " Selects: "); hit = true; } else str_printf(r, " && "); expr_gstr_print(prop->expr, r); } if (hit) str_append(r, "\n"); if (sym->rev_dep.expr) { str_append(r, " Selected by: "); expr_gstr_print(sym->rev_dep.expr, r); str_append(r, "\n"); } str_append(r, "\n\n"); } static struct gstr get_relations_str(struct symbol **sym_arr) { struct symbol *sym; struct gstr res = str_new(); int i; for (i = 0; sym_arr && (sym = sym_arr[i]); i++) get_symbol_str(&res, sym); if (!i) str_append(&res, "No matches found.\n"); return res; } pid_t pid; #ifdef SIGWINCH static void winch_handler(int sig) { if (!do_resize) { kill(pid, SIGINT); do_resize = 1; } } #endif static int exec_conf(void) { int pipefd[2], stat, size; sigset_t sset, osset; sigemptyset(&sset); sigaddset(&sset, SIGINT); sigprocmask(SIG_BLOCK, &sset, &osset); signal(SIGINT, SIG_DFL); #ifdef SIGWINCH { struct sigaction sa; sa.sa_handler = winch_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGWINCH, &sa, NULL); } #endif *argptr++ = NULL; pipe(pipefd); pid = fork(); if (pid == 0) { sigprocmask(SIG_SETMASK, &osset, NULL); dup2(pipefd[1], 2); close(pipefd[0]); close(pipefd[1]); execv(args[0], args); _exit(EXIT_FAILURE); } close(pipefd[1]); bufptr = input_buf; while (1) { size = input_buf + sizeof(input_buf) - bufptr; size = read(pipefd[0], bufptr, size); if (size <= 0) { if (size < 0) { if (errno == EINTR || errno == EAGAIN) continue; perror("read"); } break; } bufptr += size; } *bufptr++ = 0; close(pipefd[0]); waitpid(pid, &stat, 0); if (do_resize) { init_wsize(); do_resize = 0; sigprocmask(SIG_SETMASK, &osset, NULL); return -1; } if (WIFSIGNALED(stat)) { printf("\finterrupted(%d)\n", WTERMSIG(stat)); exit(1); } #if 0 printf("\fexit state: %d\nexit data: '%s'\n", WEXITSTATUS(stat), input_buf); sleep(1); #endif sigpending(&sset); if (sigismember(&sset, SIGINT)) { printf("\finterrupted\n"); exit(1); } sigprocmask(SIG_SETMASK, &osset, NULL); return WEXITSTATUS(stat); } static void search_conf(void) { struct symbol **sym_arr; int stat; struct gstr res; again: cprint_init(); cprint("--title"); cprint(_("Search Configuration Parameter")); cprint("--inputbox"); cprint(_("Enter CONFIG_ (sub)string to search for (omit CONFIG_)")); cprint("10"); cprint("75"); cprint(""); stat = exec_conf(); if (stat < 0) goto again; switch (stat) { case 0: break; case 1: show_helptext(_("Search Configuration"), search_help); goto again; default: return; } sym_arr = sym_re_search(input_buf); res = get_relations_str(sym_arr); free(sym_arr); show_textbox(_("Search Results"), str_get(&res), 0, 0); str_free(&res); } static void build_conf(struct menu *menu) { struct symbol *sym; struct property *prop; struct menu *child; int type, tmp, doint = 2; tristate val; char ch; if (!menu_is_visible(menu)) return; sym = menu->sym; prop = menu->prompt; if (!sym) { if (prop && menu != current_menu) { const char *prompt = menu_get_prompt(menu); switch (prop->type) { case P_MENU: child_count++; cprint("m%p", menu); if (single_menu_mode) { cprint1("%s%*c%s", menu->data ? "-->" : "++>", indent + 1, ' ', prompt); } else cprint1(" %*c%s --->", indent + 1, ' ', prompt); cprint_done(); if (single_menu_mode && menu->data) goto conf_childs; return; default: if (prompt) { child_count++; cprint(":%p", menu); cprint("---%*c%s", indent + 1, ' ', prompt); } } } else doint = 0; goto conf_childs; } type = sym_get_type(sym); if (sym_is_choice(sym)) { struct symbol *def_sym = sym_get_choice_value(sym); struct menu *def_menu = NULL; child_count++; for (child = menu->list; child; child = child->next) { if (menu_is_visible(child) && child->sym == def_sym) def_menu = child; } val = sym_get_tristate_value(sym); if (sym_is_changable(sym)) { cprint("t%p", menu); switch (type) { case S_BOOLEAN: cprint1("[%c]", val == no ? ' ' : '*'); break; case S_TRISTATE: switch (val) { case yes: ch = '*'; break; case mod: ch = 'M'; break; default: ch = ' '; break; } cprint1("<%c>", ch); break; } } else { cprint("%c%p", def_menu ? 't' : ':', menu); cprint1(" "); } cprint1("%*c%s", indent + 1, ' ', menu_get_prompt(menu)); if (val == yes) { if (def_menu) { cprint1(" (%s)", menu_get_prompt(def_menu)); cprint1(" --->"); cprint_done(); if (def_menu->list) { indent += 2; build_conf(def_menu); indent -= 2; } } else cprint_done(); return; } cprint_done(); } else { if (menu == current_menu) { cprint(":%p", menu); cprint("---%*c%s", indent + 1, ' ', menu_get_prompt(menu)); goto conf_childs; } child_count++; val = sym_get_tristate_value(sym); if (sym_is_choice_value(sym) && val == yes) { cprint(":%p", menu); cprint1(" "); } else { switch (type) { case S_BOOLEAN: cprint("t%p", menu); if (sym_is_changable(sym)) cprint1("[%c]", val == no ? ' ' : '*'); else cprint1("---"); break; case S_TRISTATE: cprint("t%p", menu); switch (val) { case yes: ch = '*'; break; case mod: ch = 'M'; break; default: ch = ' '; break; } if (sym_is_changable(sym)) cprint1("<%c>", ch); else cprint1("---"); break; default: cprint("s%p", menu); tmp = cprint1("(%s)", sym_get_string_value(sym)); tmp = indent - tmp + 4; if (tmp < 0) tmp = 0; cprint1("%*c%s%s", tmp, ' ', menu_get_prompt(menu), (sym_has_value(sym) || !sym_is_changable(sym)) ? "" : " (NEW)"); cprint_done(); goto conf_childs; } } cprint1("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu), (sym_has_value(sym) || !sym_is_changable(sym)) ? "" : " (NEW)"); if (menu->prompt->type == P_MENU) { cprint1(" --->"); cprint_done(); return; } cprint_done(); } conf_childs: indent += doint; for (child = menu->list; child; child = child->next) build_conf(child); indent -= doint; } static void conf(struct menu *menu) { struct menu *submenu; const char *prompt = menu_get_prompt(menu); struct symbol *sym; char active_entry[40]; int stat, type, i; unlink("lxdialog.scrltmp"); active_entry[0] = 0; while (1) { cprint_init(); cprint("--title"); cprint("%s", prompt ? prompt : _("Main Menu")); cprint("--menu"); cprint(_(menu_instructions)); cprint("%d", rows); cprint("%d", cols); cprint("%d", rows - 10); cprint("%s", active_entry); current_menu = menu; build_conf(menu); if (!child_count) break; if (menu == &rootmenu) { cprint(":"); cprint("--- "); cprint("L"); cprint(_(" Load an Alternate Configuration File")); cprint("S"); cprint(_(" Save Configuration to an Alternate File")); } stat = exec_conf(); if (stat < 0) continue; if (stat == 1 || stat == 255) break; type = input_buf[0]; if (!type) continue; for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++) ; if (i >= sizeof(active_entry)) i = sizeof(active_entry) - 1; input_buf[i] = 0; strcpy(active_entry, input_buf); sym = NULL; submenu = NULL; if (sscanf(input_buf + 1, "%p", &submenu) == 1) sym = submenu->sym; switch (stat) { case 0: switch (type) { case 'm': if (single_menu_mode) submenu->data = (void *) (long) !submenu->data; else conf(submenu); break; case 't': if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes) conf_choice(submenu); else if (submenu->prompt->type == P_MENU) conf(submenu); break; case 's': conf_string(submenu); break; case 'L': conf_load(); break; case 'S': conf_save(); break; } break; case 2: if (sym) show_help(submenu); else show_helptext("README", _(mconf_readme)); break; case 3: if (type == 't') { if (sym_set_tristate_value(sym, yes)) break; if (sym_set_tristate_value(sym, mod)) show_textbox(NULL, setmod_text, 6, 74); } break; case 4: if (type == 't') sym_set_tristate_value(sym, no); break; case 5: if (type == 't') sym_set_tristate_value(sym, mod); break; case 6: if (type == 't') sym_toggle_tristate_value(sym); else if (type == 'm') conf(submenu); break; case 7: search_conf(); break; } } } static void show_textbox(const char *title, const char *text, int r, int c) { int fd; fd = creat(".help.tmp", 0777); write(fd, text, strlen(text)); close(fd); show_file(".help.tmp", title, r, c); unlink(".help.tmp"); } static void show_helptext(const char *title, const char *text) { show_textbox(title, text, 0, 0); } static void show_help(struct menu *menu) { struct gstr help = str_new(); struct symbol *sym = menu->sym; if (sym->help) { if (sym->name) { str_printf(&help, "CONFIG_%s:\n\n", sym->name); str_append(&help, _(sym->help)); str_append(&help, "\n"); } } else { str_append(&help, nohelp_text); } get_symbol_str(&help, sym); show_helptext(menu_get_prompt(menu), str_get(&help)); str_free(&help); } static void show_file(const char *filename, const char *title, int r, int c) { do { cprint_init(); if (title) { cprint("--title"); cprint("%s", title); } cprint("--textbox"); cprint("%s", filename); cprint("%d", r ? r : rows); cprint("%d", c ? c : cols); } while (exec_conf() < 0); } static void conf_choice(struct menu *menu) { const char *prompt = menu_get_prompt(menu); struct menu *child; struct symbol *active; int stat; active = sym_get_choice_value(menu->sym); while (1) { cprint_init(); cprint("--title"); cprint("%s", prompt ? prompt : _("Main Menu")); cprint("--radiolist"); cprint(_(radiolist_instructions)); cprint("15"); cprint("70"); cprint("6"); current_menu = menu; for (child = menu->list; child; child = child->next) { if (!menu_is_visible(child)) continue; cprint("%p", child); cprint("%s", menu_get_prompt(child)); if (child->sym == sym_get_choice_value(menu->sym)) cprint("ON"); else if (child->sym == active) cprint("SELECTED"); else cprint("OFF"); } stat = exec_conf(); switch (stat) { case 0: if (sscanf(input_buf, "%p", &child) != 1) break; sym_set_tristate_value(child->sym, yes); return; case 1: if (sscanf(input_buf, "%p", &child) == 1) { show_help(child); active = child->sym; } else show_help(menu); break; case 255: return; } } } static void conf_string(struct menu *menu) { const char *prompt = menu_get_prompt(menu); int stat; while (1) { cprint_init(); cprint("--title"); cprint("%s", prompt ? prompt : _("Main Menu")); cprint("--inputbox"); switch (sym_get_type(menu->sym)) { case S_INT: cprint(_(inputbox_instructions_int)); break; case S_HEX: cprint(_(inputbox_instructions_hex)); break; case S_STRING: cprint(_(inputbox_instructions_string)); break; default: /* panic? */; } cprint("10"); cprint("75"); cprint("%s", sym_get_string_value(menu->sym)); stat = exec_conf(); switch (stat) { case 0: if (sym_set_string_value(menu->sym, input_buf)) return; show_textbox(NULL, _("You have made an invalid entry."), 5, 43); break; case 1: show_help(menu); break; case 255: return; } } } static void conf_load(void) { int stat; while (1) { cprint_init(); cprint("--inputbox"); cprint(load_config_text); cprint("11"); cprint("55"); cprint("%s", filename); stat = exec_conf(); switch(stat) { case 0: if (!input_buf[0]) return; if (!conf_read(input_buf)) return; show_textbox(NULL, _("File does not exist!"), 5, 38); break; case 1: show_helptext(_("Load Alternate Configuration"), load_config_help); break; case 255: return; } } } static void conf_save(void) { int stat; while (1) { cprint_init(); cprint("--inputbox"); cprint(save_config_text); cprint("11"); cprint("55"); cprint("%s", filename); stat = exec_conf(); switch(stat) { case 0: if (!input_buf[0]) return; if (!conf_write(input_buf)) return; show_textbox(NULL, _("Can't create file! Probably a nonexistent directory."), 5, 60); break; case 1: show_helptext(_("Save Alternate Configuration"), save_config_help); break; case 255: return; } } } static void conf_cleanup(void) { tcsetattr(1, TCSAFLUSH, &ios_org); unlink(".help.tmp"); unlink("lxdialog.scrltmp"); } int main(int ac, char **av) { struct symbol *sym; char *mode; int stat; setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); conf_parse(av[1]); conf_read(NULL); sym = sym_lookup("KERNELVERSION", 0); sym_calc_value(sym); sprintf(menu_backtitle, _("BusyBox %s Configuration"), sym_get_string_value(sym)); mode = getenv("MENUCONFIG_MODE"); if (mode) { if (!strcasecmp(mode, "single_menu")) single_menu_mode = 1; } tcgetattr(1, &ios_org); atexit(conf_cleanup); init_wsize(); conf(&rootmenu); do { cprint_init(); cprint("--yesno"); cprint(_("Do you wish to save your new configuration?")); cprint("5"); cprint("60"); stat = exec_conf(); } while (stat < 0); if (stat == 0) { if (conf_write(NULL)) { fprintf(stderr, _("\n\n" "Error during writing of the configuration.\n" "Your configuration changes were NOT saved." "\n\n")); return 1; } printf(_("\n\n" "*** End of configuration.\n" "*** Execute 'make' to build the project or try 'make help'." "\n\n")); } else { fprintf(stderr, _("\n\n" "Your configuration changes were NOT saved." "\n\n")); } return 0; } busybox-1.22.1/scripts/kconfig/images.c0000644000000000000000000001464512263563520016524 0ustar rootroot/* * Copyright (C) 2002 Roman Zippel * Released under the terms of the GNU GPL v2.0. */ static const char *xpm_load[] = { "22 22 5 1", ". c None", "# c #000000", "c c #838100", "a c #ffff00", "b c #ffffff", "......................", "......................", "......................", "............####....#.", "...........#....##.##.", "..................###.", ".................####.", ".####...........#####.", "#abab##########.......", "#babababababab#.......", "#ababababababa#.......", "#babababababab#.......", "#ababab###############", "#babab##cccccccccccc##", "#abab##cccccccccccc##.", "#bab##cccccccccccc##..", "#ab##cccccccccccc##...", "#b##cccccccccccc##....", "###cccccccccccc##.....", "##cccccccccccc##......", "###############.......", "......................"}; static const char *xpm_save[] = { "22 22 5 1", ". c None", "# c #000000", "a c #838100", "b c #c5c2c5", "c c #cdb6d5", "......................", ".####################.", ".#aa#bbbbbbbbbbbb#bb#.", ".#aa#bbbbbbbbbbbb#bb#.", ".#aa#bbbbbbbbbcbb####.", ".#aa#bbbccbbbbbbb#aa#.", ".#aa#bbbccbbbbbbb#aa#.", ".#aa#bbbbbbbbbbbb#aa#.", ".#aa#bbbbbbbbbbbb#aa#.", ".#aa#bbbbbbbbbbbb#aa#.", ".#aa#bbbbbbbbbbbb#aa#.", ".#aaa############aaa#.", ".#aaaaaaaaaaaaaaaaaa#.", ".#aaaaaaaaaaaaaaaaaa#.", ".#aaa#############aa#.", ".#aaa#########bbb#aa#.", ".#aaa#########bbb#aa#.", ".#aaa#########bbb#aa#.", ".#aaa#########bbb#aa#.", ".#aaa#########bbb#aa#.", "..##################..", "......................"}; static const char *xpm_back[] = { "22 22 3 1", ". c None", "# c #000083", "a c #838183", "......................", "......................", "......................", "......................", "......................", "...........######a....", "..#......##########...", "..##...####......##a..", "..###.###.........##..", "..######..........##..", "..#####...........##..", "..######..........##..", "..#######.........##..", "..########.......##a..", "...............a###...", "...............###....", "......................", "......................", "......................", "......................", "......................", "......................"}; static const char *xpm_tree_view[] = { "22 22 2 1", ". c None", "# c #000000", "......................", "......................", "......#...............", "......#...............", "......#...............", "......#...............", "......#...............", "......########........", "......#...............", "......#...............", "......#...............", "......#...............", "......#...............", "......########........", "......#...............", "......#...............", "......#...............", "......#...............", "......#...............", "......########........", "......................", "......................"}; static const char *xpm_single_view[] = { "22 22 2 1", ". c None", "# c #000000", "......................", "......................", "..........#...........", "..........#...........", "..........#...........", "..........#...........", "..........#...........", "..........#...........", "..........#...........", "..........#...........", "..........#...........", "..........#...........", "..........#...........", "..........#...........", "..........#...........", "..........#...........", "..........#...........", "..........#...........", "..........#...........", "..........#...........", "......................", "......................"}; static const char *xpm_split_view[] = { "22 22 2 1", ". c None", "# c #000000", "......................", "......................", "......#......#........", "......#......#........", "......#......#........", "......#......#........", "......#......#........", "......#......#........", "......#......#........", "......#......#........", "......#......#........", "......#......#........", "......#......#........", "......#......#........", "......#......#........", "......#......#........", "......#......#........", "......#......#........", "......#......#........", "......#......#........", "......................", "......................"}; static const char *xpm_symbol_no[] = { "12 12 2 1", " c white", ". c black", " ", " .......... ", " . . ", " . . ", " . . ", " . . ", " . . ", " . . ", " . . ", " . . ", " .......... ", " "}; static const char *xpm_symbol_mod[] = { "12 12 2 1", " c white", ". c black", " ", " .......... ", " . . ", " . . ", " . .. . ", " . .... . ", " . .... . ", " . .. . ", " . . ", " . . ", " .......... ", " "}; static const char *xpm_symbol_yes[] = { "12 12 2 1", " c white", ". c black", " ", " .......... ", " . . ", " . . ", " . . . ", " . .. . ", " . . .. . ", " . .... . ", " . .. . ", " . . ", " .......... ", " "}; static const char *xpm_choice_no[] = { "12 12 2 1", " c white", ". c black", " ", " .... ", " .. .. ", " . . ", " . . ", " . . ", " . . ", " . . ", " . . ", " .. .. ", " .... ", " "}; static const char *xpm_choice_yes[] = { "12 12 2 1", " c white", ". c black", " ", " .... ", " .. .. ", " . . ", " . .. . ", " . .... . ", " . .... . ", " . .. . ", " . . ", " .. .. ", " .... ", " "}; static const char *xpm_menu[] = { "12 12 2 1", " c white", ". c black", " ", " .......... ", " . . ", " . .. . ", " . .... . ", " . ...... . ", " . ...... . ", " . .... . ", " . .. . ", " . . ", " .......... ", " "}; static const char *xpm_menu_inv[] = { "12 12 2 1", " c white", ". c black", " ", " .......... ", " .......... ", " .. ...... ", " .. .... ", " .. .. ", " .. .. ", " .. .... ", " .. ...... ", " .......... ", " .......... ", " "}; static const char *xpm_menuback[] = { "12 12 2 1", " c white", ". c black", " ", " .......... ", " . . ", " . .. . ", " . .... . ", " . ...... . ", " . ...... . ", " . .... . ", " . .. . ", " . . ", " .......... ", " "}; static const char *xpm_void[] = { "12 12 2 1", " c white", ". c black", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "}; busybox-1.22.1/scripts/kconfig/kxgettext.c0000644000000000000000000000770112263563520017301 0ustar rootroot/* * Arnaldo Carvalho de Melo , 2005 * * Released under the terms of the GNU GPL v2.0 */ #include #include #define LKC_DIRECT_LINK #include "lkc.h" static char *escape(const char* text, char *bf, int len) { char *bfp = bf; int multiline = strchr(text, '\n') != NULL; int eol = 0; int textlen = strlen(text); if ((textlen > 0) && (text[textlen-1] == '\n')) eol = 1; *bfp++ = '"'; --len; if (multiline) { *bfp++ = '"'; *bfp++ = '\n'; *bfp++ = '"'; len -= 3; } while (*text != '\0' && len > 1) { if (*text == '"') *bfp++ = '\\'; else if (*text == '\n') { *bfp++ = '\\'; *bfp++ = 'n'; *bfp++ = '"'; *bfp++ = '\n'; *bfp++ = '"'; len -= 5; ++text; goto next; } *bfp++ = *text++; next: --len; } if (multiline && eol) bfp -= 3; *bfp++ = '"'; *bfp = '\0'; return bf; } struct file_line { struct file_line *next; char* file; int lineno; }; static struct file_line *file_line__new(char *file, int lineno) { struct file_line *self = malloc(sizeof(*self)); if (self == NULL) goto out; self->file = file; self->lineno = lineno; self->next = NULL; out: return self; } struct message { const char *msg; const char *option; struct message *next; struct file_line *files; }; static struct message *message__list; static struct message *message__new(const char *msg, char *option, char *file, int lineno) { struct message *self = malloc(sizeof(*self)); if (self == NULL) goto out; self->files = file_line__new(file, lineno); if (self->files == NULL) goto out_fail; self->msg = strdup(msg); if (self->msg == NULL) goto out_fail_msg; self->option = option; self->next = NULL; out: return self; out_fail_msg: free(self->files); out_fail: free(self); self = NULL; goto out; } static struct message *mesage__find(const char *msg) { struct message *m = message__list; while (m != NULL) { if (strcmp(m->msg, msg) == 0) break; m = m->next; } return m; } static int message__add_file_line(struct message *self, char *file, int lineno) { int rc = -1; struct file_line *fl = file_line__new(file, lineno); if (fl == NULL) goto out; fl->next = self->files; self->files = fl; rc = 0; out: return rc; } static int message__add(const char *msg, char *option, char *file, int lineno) { int rc = 0; char bf[16384]; char *escaped = escape(msg, bf, sizeof(bf)); struct message *m = mesage__find(escaped); if (m != NULL) rc = message__add_file_line(m, file, lineno); else { m = message__new(escaped, option, file, lineno); if (m != NULL) { m->next = message__list; message__list = m; } else rc = -1; } return rc; } void menu_build_message_list(struct menu *menu) { struct menu *child; message__add(menu_get_prompt(menu), NULL, menu->file == NULL ? "Root Menu" : menu->file->name, menu->lineno); if (menu->sym != NULL && menu->sym->help != NULL) message__add(menu->sym->help, menu->sym->name, menu->file == NULL ? "Root Menu" : menu->file->name, menu->lineno); for (child = menu->list; child != NULL; child = child->next) if (child->prompt != NULL) menu_build_message_list(child); } static void message__print_file_lineno(struct message *self) { struct file_line *fl = self->files; putchar('\n'); if (self->option != NULL) printf("# %s:00000\n", self->option); printf("#: %s:%d", fl->file, fl->lineno); fl = fl->next; while (fl != NULL) { printf(", %s:%d", fl->file, fl->lineno); fl = fl->next; } putchar('\n'); } static void message__print_gettext_msgid_msgstr(struct message *self) { message__print_file_lineno(self); printf("msgid %s\n" "msgstr \"\"\n", self->msg); } void menu__xgettext(void) { struct message *m = message__list; while (m != NULL) { message__print_gettext_msgid_msgstr(m); m = m->next; } } int main(int ac, char **av) { conf_parse(av[1]); menu_build_message_list(menu_get_root_menu(NULL)); menu__xgettext(); return 0; } busybox-1.22.1/scripts/kconfig/zconf.tab.c_shipped0000644000000000000000000016555312263563520020664 0ustar rootroot/* A Bison parser, made by GNU Bison 2.0. */ /* Skeleton parser for Yacc-like parsing with Bison, Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* As a special exception, when this file is copied by Bison into a Bison output file, you may use that output file without restriction. This special exception was added by the Free Software Foundation in version 1.24 of Bison. */ /* Written by Richard Stallman by simplifying the original so called ``semantic'' parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 0 /* Using locations. */ #define YYLSP_NEEDED 0 /* Substitute the variable and function names. */ #define yyparse zconfparse #define yylex zconflex #define yyerror zconferror #define yylval zconflval #define yychar zconfchar #define yydebug zconfdebug #define yynerrs zconfnerrs /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { T_MAINMENU = 258, T_MENU = 259, T_ENDMENU = 260, T_SOURCE = 261, T_CHOICE = 262, T_ENDCHOICE = 263, T_COMMENT = 264, T_CONFIG = 265, T_MENUCONFIG = 266, T_HELP = 267, T_HELPTEXT = 268, T_IF = 269, T_ENDIF = 270, T_DEPENDS = 271, T_REQUIRES = 272, T_OPTIONAL = 273, T_PROMPT = 274, T_TYPE = 275, T_DEFAULT = 276, T_SELECT = 277, T_RANGE = 278, T_ON = 279, T_WORD = 280, T_WORD_QUOTE = 281, T_UNEQUAL = 282, T_CLOSE_PAREN = 283, T_OPEN_PAREN = 284, T_EOL = 285, T_OR = 286, T_AND = 287, T_EQUAL = 288, T_NOT = 289 }; #endif #define T_MAINMENU 258 #define T_MENU 259 #define T_ENDMENU 260 #define T_SOURCE 261 #define T_CHOICE 262 #define T_ENDCHOICE 263 #define T_COMMENT 264 #define T_CONFIG 265 #define T_MENUCONFIG 266 #define T_HELP 267 #define T_HELPTEXT 268 #define T_IF 269 #define T_ENDIF 270 #define T_DEPENDS 271 #define T_REQUIRES 272 #define T_OPTIONAL 273 #define T_PROMPT 274 #define T_TYPE 275 #define T_DEFAULT 276 #define T_SELECT 277 #define T_RANGE 278 #define T_ON 279 #define T_WORD 280 #define T_WORD_QUOTE 281 #define T_UNEQUAL 282 #define T_CLOSE_PAREN 283 #define T_OPEN_PAREN 284 #define T_EOL 285 #define T_OR 286 #define T_AND 287 #define T_EQUAL 288 #define T_NOT 289 /* Copy the first part of user declarations. */ /* * Copyright (C) 2002 Roman Zippel * Released under the terms of the GNU GPL v2.0. */ #include #include #include #include #include #include #define LKC_DIRECT_LINK #include "lkc.h" #include "zconf.hash.c" #define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) #define PRINTD 0x0001 #define DEBUG_PARSE 0x0002 int cdebug = PRINTD; extern int zconflex(void); static void zconfprint(const char *err, ...); static void zconf_error(const char *err, ...); static void zconferror(const char *err); static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken); struct symbol *symbol_hash[257]; static struct menu *current_menu, *current_entry; #define YYDEBUG 0 #if YYDEBUG #define YYERROR_VERBOSE #endif /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 0 #endif #if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) typedef union YYSTYPE { char *string; struct file *file; struct symbol *symbol; struct expr *expr; struct menu *menu; struct kconf_id *id; } YYSTYPE; /* Line 190 of yacc.c. */ # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 #endif /* Copy the second part of user declarations. */ /* Line 213 of yacc.c. */ #if ! defined (yyoverflow) || YYERROR_VERBOSE # ifndef YYFREE # define YYFREE free # endif # ifndef YYMALLOC # define YYMALLOC malloc # endif /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # else # define YYSTACK_ALLOC alloca # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) # else # if defined (__STDC__) || defined (__cplusplus) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # endif # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # endif #endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ #if (! defined (yyoverflow) \ && (! defined (__cplusplus) \ || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { short int yyss; YYSTYPE yyvs; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (short int) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY # if defined (__GNUC__) && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ register YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ while (0) # endif # endif /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack, Stack, yysize); \ Stack = &yyptr->Stack; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (0) #endif #if defined (__STDC__) || defined (__cplusplus) typedef signed char yysigned_char; #else typedef short int yysigned_char; #endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 3 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 264 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 35 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 42 /* YYNRULES -- Number of rules. */ #define YYNRULES 104 /* YYNRULES -- Number of states. */ #define YYNSTATES 175 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 289 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ static const unsigned char yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ static const unsigned short int yyprhs[] = { 0, 0, 3, 5, 6, 9, 12, 15, 20, 23, 28, 33, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 67, 70, 74, 77, 81, 84, 85, 88, 91, 94, 97, 100, 104, 109, 114, 119, 125, 128, 131, 133, 137, 138, 141, 144, 147, 150, 153, 158, 162, 165, 170, 171, 174, 178, 180, 184, 185, 188, 191, 194, 198, 201, 203, 207, 208, 211, 214, 217, 221, 225, 228, 231, 234, 235, 238, 241, 244, 249, 253, 257, 258, 261, 263, 265, 268, 271, 274, 276, 279, 280, 283, 285, 289, 293, 297, 300, 304, 308, 310 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yysigned_char yyrhs[] = { 36, 0, -1, 37, -1, -1, 37, 39, -1, 37, 50, -1, 37, 61, -1, 37, 3, 71, 73, -1, 37, 72, -1, 37, 25, 1, 30, -1, 37, 38, 1, 30, -1, 37, 1, 30, -1, 16, -1, 19, -1, 20, -1, 22, -1, 18, -1, 23, -1, 21, -1, 30, -1, 56, -1, 65, -1, 42, -1, 44, -1, 63, -1, 25, 1, 30, -1, 1, 30, -1, 10, 25, 30, -1, 41, 45, -1, 11, 25, 30, -1, 43, 45, -1, -1, 45, 46, -1, 45, 69, -1, 45, 67, -1, 45, 40, -1, 45, 30, -1, 20, 70, 30, -1, 19, 71, 74, 30, -1, 21, 75, 74, 30, -1, 22, 25, 74, 30, -1, 23, 76, 76, 74, 30, -1, 7, 30, -1, 47, 51, -1, 72, -1, 48, 53, 49, -1, -1, 51, 52, -1, 51, 69, -1, 51, 67, -1, 51, 30, -1, 51, 40, -1, 19, 71, 74, 30, -1, 20, 70, 30, -1, 18, 30, -1, 21, 25, 74, 30, -1, -1, 53, 39, -1, 14, 75, 73, -1, 72, -1, 54, 57, 55, -1, -1, 57, 39, -1, 57, 61, -1, 57, 50, -1, 4, 71, 30, -1, 58, 68, -1, 72, -1, 59, 62, 60, -1, -1, 62, 39, -1, 62, 61, -1, 62, 50, -1, 6, 71, 30, -1, 9, 71, 30, -1, 64, 68, -1, 12, 30, -1, 66, 13, -1, -1, 68, 69, -1, 68, 30, -1, 68, 40, -1, 16, 24, 75, 30, -1, 16, 75, 30, -1, 17, 75, 30, -1, -1, 71, 74, -1, 25, -1, 26, -1, 5, 30, -1, 8, 30, -1, 15, 30, -1, 30, -1, 73, 30, -1, -1, 14, 75, -1, 76, -1, 76, 33, 76, -1, 76, 27, 76, -1, 29, 75, 28, -1, 34, 75, -1, 75, 31, 75, -1, 75, 32, 75, -1, 25, -1, 26, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const unsigned short int yyrline[] = { 0, 103, 103, 105, 107, 108, 109, 110, 111, 112, 113, 117, 121, 121, 121, 121, 121, 121, 121, 125, 126, 127, 128, 129, 130, 134, 135, 141, 149, 155, 163, 173, 175, 176, 177, 178, 179, 182, 190, 196, 206, 212, 220, 229, 234, 242, 245, 247, 248, 249, 250, 251, 254, 260, 271, 277, 287, 289, 294, 302, 310, 313, 315, 316, 317, 322, 329, 334, 342, 345, 347, 348, 349, 352, 360, 367, 374, 380, 387, 389, 390, 391, 394, 399, 404, 412, 414, 419, 420, 423, 424, 425, 429, 430, 433, 434, 437, 438, 439, 440, 441, 442, 443, 446, 447 }; #endif #if YYDEBUG || YYERROR_VERBOSE /* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "T_MAINMENU", "T_MENU", "T_ENDMENU", "T_SOURCE", "T_CHOICE", "T_ENDCHOICE", "T_COMMENT", "T_CONFIG", "T_MENUCONFIG", "T_HELP", "T_HELPTEXT", "T_IF", "T_ENDIF", "T_DEPENDS", "T_REQUIRES", "T_OPTIONAL", "T_PROMPT", "T_TYPE", "T_DEFAULT", "T_SELECT", "T_RANGE", "T_ON", "T_WORD", "T_WORD_QUOTE", "T_UNEQUAL", "T_CLOSE_PAREN", "T_OPEN_PAREN", "T_EOL", "T_OR", "T_AND", "T_EQUAL", "T_NOT", "$accept", "input", "stmt_list", "option_name", "common_stmt", "option_error", "config_entry_start", "config_stmt", "menuconfig_entry_start", "menuconfig_stmt", "config_option_list", "config_option", "choice", "choice_entry", "choice_end", "choice_stmt", "choice_option_list", "choice_option", "choice_block", "if_entry", "if_end", "if_stmt", "if_block", "menu", "menu_entry", "menu_end", "menu_stmt", "menu_block", "source_stmt", "comment", "comment_stmt", "help_start", "help", "depends_list", "depends", "prompt_stmt_opt", "prompt", "end", "nl", "if_expr", "expr", "symbol", 0 }; #endif # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ static const unsigned short int yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const unsigned char yyr1[] = { 0, 35, 36, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 40, 40, 41, 42, 43, 44, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 47, 48, 49, 50, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 53, 53, 54, 55, 56, 57, 57, 57, 57, 58, 59, 60, 61, 62, 62, 62, 62, 63, 64, 65, 66, 67, 68, 68, 68, 68, 69, 69, 69, 70, 70, 71, 71, 72, 72, 72, 73, 73, 74, 74, 75, 75, 75, 75, 75, 75, 75, 76, 76 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const unsigned char yyr2[] = { 0, 2, 1, 0, 2, 2, 2, 4, 2, 4, 4, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 3, 2, 3, 2, 0, 2, 2, 2, 2, 2, 3, 4, 4, 4, 5, 2, 2, 1, 3, 0, 2, 2, 2, 2, 2, 4, 3, 2, 4, 0, 2, 3, 1, 3, 0, 2, 2, 2, 3, 2, 1, 3, 0, 2, 2, 2, 3, 3, 2, 2, 2, 0, 2, 2, 2, 4, 3, 3, 0, 2, 1, 1, 2, 2, 2, 1, 2, 0, 2, 1, 3, 3, 3, 2, 3, 3, 1, 1 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const unsigned char yydefact[] = { 3, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 16, 13, 14, 18, 15, 17, 0, 19, 0, 4, 31, 22, 31, 23, 46, 56, 5, 61, 20, 78, 69, 6, 24, 78, 21, 8, 11, 87, 88, 0, 0, 89, 0, 42, 90, 0, 0, 0, 103, 104, 0, 0, 0, 96, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 7, 65, 73, 74, 27, 29, 0, 100, 0, 0, 58, 0, 0, 9, 10, 0, 0, 0, 0, 0, 85, 0, 0, 0, 0, 36, 35, 32, 0, 34, 33, 0, 0, 85, 0, 50, 51, 47, 49, 48, 57, 45, 44, 62, 64, 60, 63, 59, 80, 81, 79, 70, 72, 68, 71, 67, 93, 99, 101, 102, 98, 97, 26, 76, 0, 0, 0, 94, 0, 94, 94, 94, 0, 0, 77, 54, 94, 0, 94, 0, 83, 84, 0, 0, 37, 86, 0, 0, 94, 25, 0, 53, 0, 82, 95, 38, 39, 40, 0, 52, 55, 41 }; /* YYDEFGOTO[NTERM-NUM]. */ static const short int yydefgoto[] = { -1, 1, 2, 25, 26, 99, 27, 28, 29, 30, 64, 100, 31, 32, 114, 33, 66, 110, 67, 34, 118, 35, 68, 36, 37, 126, 38, 70, 39, 40, 41, 101, 102, 69, 103, 141, 142, 42, 73, 156, 59, 60 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ #define YYPACT_NINF -78 static const short int yypact[] = { -78, 2, 159, -78, -21, 0, 0, -12, 0, 1, 4, 0, 27, 38, 60, 58, -78, -78, -78, -78, -78, -78, -78, 100, -78, 104, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, 86, 113, -78, 114, -78, -78, 125, 127, 128, -78, -78, 60, 60, 210, 65, -78, 141, 142, 39, 103, 182, 200, 6, 66, 6, 131, -78, 146, -78, -78, -78, -78, -78, 196, -78, 60, 60, 146, 40, 40, -78, -78, 155, 156, -2, 60, 0, 0, 60, 105, 40, 194, -78, -78, -78, 206, -78, -78, 183, 0, 0, 195, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, 197, -78, -78, -78, -78, -78, 60, 213, 216, 212, 203, 212, 190, 212, 40, 208, -78, -78, 212, 222, 212, 219, -78, -78, 60, 223, -78, -78, 224, 225, 212, -78, 226, -78, 227, -78, 47, -78, -78, -78, 228, -78, -78, -78 }; /* YYPGOTO[NTERM-NUM]. */ static const short int yypgoto[] = { -78, -78, -78, -78, 164, -36, -78, -78, -78, -78, 230, -78, -78, -78, -78, 29, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, 59, -78, -78, -78, -78, -78, 198, 220, 24, 157, -5, 169, 202, 74, -53, -77 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -76 static const short int yytable[] = { 46, 47, 3, 49, 79, 80, 52, 133, 134, 43, 6, 7, 8, 9, 10, 11, 12, 13, 48, 145, 14, 15, 137, 55, 56, 44, 45, 57, 131, 132, 109, 50, 58, 122, 51, 122, 24, 138, 139, -28, 88, 143, -28, -28, -28, -28, -28, -28, -28, -28, -28, 89, 53, -28, -28, 90, 91, -28, 92, 93, 94, 95, 96, 54, 97, 55, 56, 88, 161, 98, -66, -66, -66, -66, -66, -66, -66, -66, 81, 82, -66, -66, 90, 91, 152, 55, 56, 140, 61, 57, 112, 97, 84, 123, 58, 123, 121, 117, 85, 125, 149, 62, 167, -30, 88, 63, -30, -30, -30, -30, -30, -30, -30, -30, -30, 89, 72, -30, -30, 90, 91, -30, 92, 93, 94, 95, 96, 119, 97, 127, 144, -75, 88, 98, -75, -75, -75, -75, -75, -75, -75, -75, -75, 74, 75, -75, -75, 90, 91, -75, -75, -75, -75, -75, -75, 76, 97, 77, 78, -2, 4, 121, 5, 6, 7, 8, 9, 10, 11, 12, 13, 86, 87, 14, 15, 16, 129, 17, 18, 19, 20, 21, 22, 88, 23, 135, 136, -43, -43, 24, -43, -43, -43, -43, 89, 146, -43, -43, 90, 91, 104, 105, 106, 107, 155, 7, 8, 97, 10, 11, 12, 13, 108, 148, 14, 15, 158, 159, 160, 147, 151, 81, 82, 163, 130, 165, 155, 81, 82, 82, 24, 113, 116, 157, 124, 171, 115, 120, 162, 128, 72, 81, 82, 153, 81, 82, 154, 81, 82, 166, 81, 82, 164, 168, 169, 170, 172, 173, 174, 65, 71, 83, 0, 150, 111 }; static const short int yycheck[] = { 5, 6, 0, 8, 57, 58, 11, 84, 85, 30, 4, 5, 6, 7, 8, 9, 10, 11, 30, 96, 14, 15, 24, 25, 26, 25, 26, 29, 81, 82, 66, 30, 34, 69, 30, 71, 30, 90, 91, 0, 1, 94, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 25, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 25, 25, 26, 1, 145, 30, 4, 5, 6, 7, 8, 9, 10, 11, 31, 32, 14, 15, 16, 17, 137, 25, 26, 92, 30, 29, 66, 25, 27, 69, 34, 71, 30, 68, 33, 70, 105, 1, 155, 0, 1, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 30, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 68, 25, 70, 25, 0, 1, 30, 3, 4, 5, 6, 7, 8, 9, 10, 11, 30, 30, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 30, 25, 30, 30, 0, 1, 30, 3, 4, 5, 6, 7, 8, 9, 10, 11, 30, 30, 14, 15, 16, 30, 18, 19, 20, 21, 22, 23, 1, 25, 30, 30, 5, 6, 30, 8, 9, 10, 11, 12, 1, 14, 15, 16, 17, 18, 19, 20, 21, 14, 5, 6, 25, 8, 9, 10, 11, 30, 30, 14, 15, 142, 143, 144, 13, 25, 31, 32, 149, 28, 151, 14, 31, 32, 32, 30, 67, 68, 30, 70, 161, 67, 68, 30, 70, 30, 31, 32, 30, 31, 32, 30, 31, 32, 30, 31, 32, 30, 30, 30, 30, 30, 30, 30, 29, 40, 59, -1, 106, 66 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const unsigned char yystos[] = { 0, 36, 37, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 18, 19, 20, 21, 22, 23, 25, 30, 38, 39, 41, 42, 43, 44, 47, 48, 50, 54, 56, 58, 59, 61, 63, 64, 65, 72, 30, 25, 26, 71, 71, 30, 71, 30, 30, 71, 25, 25, 25, 26, 29, 34, 75, 76, 30, 1, 1, 45, 45, 51, 53, 57, 68, 62, 68, 30, 73, 30, 30, 30, 30, 30, 75, 75, 31, 32, 73, 27, 33, 30, 30, 1, 12, 16, 17, 19, 20, 21, 22, 23, 25, 30, 40, 46, 66, 67, 69, 18, 19, 20, 21, 30, 40, 52, 67, 69, 39, 49, 72, 39, 50, 55, 61, 72, 30, 40, 69, 39, 50, 60, 61, 72, 30, 28, 75, 75, 76, 76, 30, 30, 24, 75, 75, 71, 70, 71, 75, 25, 76, 1, 13, 30, 71, 70, 25, 75, 30, 30, 14, 74, 30, 74, 74, 74, 76, 30, 74, 30, 74, 30, 75, 30, 30, 30, 74, 30, 30, 30 }; #if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) # define YYSIZE_T __SIZE_TYPE__ #endif #if ! defined (YYSIZE_T) && defined (size_t) # define YYSIZE_T size_t #endif #if ! defined (YYSIZE_T) # if defined (__STDC__) || defined (__cplusplus) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # endif #endif #if ! defined (YYSIZE_T) # define YYSIZE_T unsigned int #endif #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { \ yychar = (Token); \ yylval = (Value); \ yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK; \ goto yybackup; \ } \ else \ { \ yyerror ("syntax error: cannot back up");\ YYERROR; \ } \ while (0) #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (N) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (0) #endif /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if YYLTYPE_IS_TRIVIAL # define YY_LOCATION_PRINT(File, Loc) \ fprintf (File, "%d.%d-%d.%d", \ (Loc).first_line, (Loc).first_column, \ (Loc).last_line, (Loc).last_column) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #endif /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM # define YYLEX yylex (YYLEX_PARAM) #else # define YYLEX yylex () #endif /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (0) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yysymprint (stderr, \ Type, Value); \ YYFPRINTF (stderr, "\n"); \ } \ } while (0) /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ #if defined (__STDC__) || defined (__cplusplus) static void yy_stack_print (short int *bottom, short int *top) #else static void yy_stack_print (bottom, top) short int *bottom; short int *top; #endif { YYFPRINTF (stderr, "Stack now"); for (/* Nothing. */; bottom <= top; ++bottom) YYFPRINTF (stderr, " %d", *bottom); YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ #if defined (__STDC__) || defined (__cplusplus) static void yy_reduce_print (int yyrule) #else static void yy_reduce_print (yyrule) int yyrule; #endif { int yyi; unsigned int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ", yyrule - 1, yylno); /* Print the symbols being reduced, and their result. */ for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]); YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]); } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (Rule); \ } while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined (__GLIBC__) && defined (_STRING_H) # define yystrlen strlen # else /* Return the length of YYSTR. */ static YYSIZE_T # if defined (__STDC__) || defined (__cplusplus) yystrlen (const char *yystr) # else yystrlen (yystr) const char *yystr; # endif { register const char *yys = yystr; while (*yys++ != '\0') continue; return yys - yystr - 1; } # endif # endif # ifndef yystpcpy # if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ static char * # if defined (__STDC__) || defined (__cplusplus) yystpcpy (char *yydest, const char *yysrc) # else yystpcpy (yydest, yysrc) char *yydest; const char *yysrc; # endif { register char *yyd = yydest; register const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif #endif /* !YYERROR_VERBOSE */ #if YYDEBUG /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ #if defined (__STDC__) || defined (__cplusplus) static void yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) #else static void yysymprint (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE *yyvaluep; #endif { /* Pacify ``unused variable'' warnings. */ (void) yyvaluep; if (yytype < YYNTOKENS) YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); else YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # endif switch (yytype) { default: break; } YYFPRINTF (yyoutput, ")"); } #endif /* ! YYDEBUG */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ #if defined (__STDC__) || defined (__cplusplus) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) #else static void yydestruct (yymsg, yytype, yyvaluep) const char *yymsg; int yytype; YYSTYPE *yyvaluep; #endif { /* Pacify ``unused variable'' warnings. */ (void) yyvaluep; if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); switch (yytype) { case 48: /* choice_entry */ { fprintf(stderr, "%s:%d: missing end statement for this entry\n", (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno); if (current_menu == (yyvaluep->menu)) menu_end_menu(); }; break; case 54: /* if_entry */ { fprintf(stderr, "%s:%d: missing end statement for this entry\n", (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno); if (current_menu == (yyvaluep->menu)) menu_end_menu(); }; break; case 59: /* menu_entry */ { fprintf(stderr, "%s:%d: missing end statement for this entry\n", (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno); if (current_menu == (yyvaluep->menu)) menu_end_menu(); }; break; default: break; } } /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM # if defined (__STDC__) || defined (__cplusplus) int yyparse (void *YYPARSE_PARAM); # else int yyparse (); # endif #else /* ! YYPARSE_PARAM */ #if defined (__STDC__) || defined (__cplusplus) int yyparse (void); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /* The look-ahead symbol. */ int yychar; /* The semantic value of the look-ahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /*----------. | yyparse. | `----------*/ #ifdef YYPARSE_PARAM # if defined (__STDC__) || defined (__cplusplus) int yyparse (void *YYPARSE_PARAM) # else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; # endif #else /* ! YYPARSE_PARAM */ #if defined (__STDC__) || defined (__cplusplus) int yyparse (void) #else int yyparse () #endif #endif { register int yystate; register int yyn; int yyresult; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* Look-ahead token as an internal (translated) token number. */ int yytoken = 0; /* Three stacks and their tools: `yyss': related to states, `yyvs': related to semantic values, `yyls': related to locations. Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ short int yyssa[YYINITDEPTH]; short int *yyss = yyssa; register short int *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs = yyvsa; register YYSTYPE *yyvsp; #define YYPOPSTACK (yyvsp--, yyssp--) YYSIZE_T yystacksize = YYINITDEPTH; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; /* When reducing, the number of symbols on the RHS of the reduced rule. */ int yylen; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss; yyvsp = yyvs; yyvsp[0] = yylval; goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. so pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; short int *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow ("parser stack overflow", &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyoverflowlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyoverflowlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { short int *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyoverflowlab; YYSTACK_RELOCATE (yyss); YYSTACK_RELOCATE (yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. */ /* Read a look-ahead token if we need one and don't already have one. */ /* yyresume: */ /* First try to decide what to do without reference to look-ahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; /* Not known => get a look-ahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yyn == 0 || yyn == YYTABLE_NINF) goto yyerrlab; yyn = -yyn; goto yyreduce; } if (yyn == YYFINAL) YYACCEPT; /* Shift the look-ahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the token being shifted unless it is eof. */ if (yychar != YYEOF) yychar = YYEMPTY; *++yyvsp = yylval; /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; yystate = yyn; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: `$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; YY_REDUCE_PRINT (yyn); switch (yyn) { case 8: { zconf_error("unexpected end statement"); ;} break; case 9: { zconf_error("unknown statement \"%s\"", (yyvsp[-2].string)); ;} break; case 10: { zconf_error("unexpected option \"%s\"", kconf_id_strings + (yyvsp[-2].id)->name); ;} break; case 11: { zconf_error("invalid statement"); ;} break; case 25: { zconf_error("unknown option \"%s\"", (yyvsp[-2].string)); ;} break; case 26: { zconf_error("invalid option"); ;} break; case 27: { struct symbol *sym = sym_lookup((yyvsp[-1].string), 0); sym->flags |= SYMBOL_OPTIONAL; menu_add_entry(sym); printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string)); ;} break; case 28: { menu_end_entry(); printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); ;} break; case 29: { struct symbol *sym = sym_lookup((yyvsp[-1].string), 0); sym->flags |= SYMBOL_OPTIONAL; menu_add_entry(sym); printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string)); ;} break; case 30: { if (current_entry->prompt) current_entry->prompt->type = P_MENU; else zconfprint("warning: menuconfig statement without prompt"); menu_end_entry(); printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); ;} break; case 37: { menu_set_type((yyvsp[-2].id)->stype); printd(DEBUG_PARSE, "%s:%d:type(%u)\n", zconf_curname(), zconf_lineno(), (yyvsp[-2].id)->stype); ;} break; case 38: { menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr)); printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); ;} break; case 39: { menu_add_expr(P_DEFAULT, (yyvsp[-2].expr), (yyvsp[-1].expr)); if ((yyvsp[-3].id)->stype != S_UNKNOWN) menu_set_type((yyvsp[-3].id)->stype); printd(DEBUG_PARSE, "%s:%d:default(%u)\n", zconf_curname(), zconf_lineno(), (yyvsp[-3].id)->stype); ;} break; case 40: { menu_add_symbol(P_SELECT, sym_lookup((yyvsp[-2].string), 0), (yyvsp[-1].expr)); printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno()); ;} break; case 41: { menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,(yyvsp[-3].symbol), (yyvsp[-2].symbol)), (yyvsp[-1].expr)); printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); ;} break; case 42: { struct symbol *sym = sym_lookup(NULL, 0); sym->flags |= SYMBOL_CHOICE; menu_add_entry(sym); menu_add_expr(P_CHOICE, NULL, NULL); printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno()); ;} break; case 43: { (yyval.menu) = menu_add_menu(); ;} break; case 44: { if (zconf_endtoken((yyvsp[0].id), T_CHOICE, T_ENDCHOICE)) { menu_end_menu(); printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno()); } ;} break; case 52: { menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr)); printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); ;} break; case 53: { if ((yyvsp[-2].id)->stype == S_BOOLEAN || (yyvsp[-2].id)->stype == S_TRISTATE) { menu_set_type((yyvsp[-2].id)->stype); printd(DEBUG_PARSE, "%s:%d:type(%u)\n", zconf_curname(), zconf_lineno(), (yyvsp[-2].id)->stype); } else YYERROR; ;} break; case 54: { current_entry->sym->flags |= SYMBOL_OPTIONAL; printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno()); ;} break; case 55: { if ((yyvsp[-3].id)->stype == S_UNKNOWN) { menu_add_symbol(P_DEFAULT, sym_lookup((yyvsp[-2].string), 0), (yyvsp[-1].expr)); printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno()); } else YYERROR; ;} break; case 58: { printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); menu_add_entry(NULL); menu_add_dep((yyvsp[-1].expr)); (yyval.menu) = menu_add_menu(); ;} break; case 59: { if (zconf_endtoken((yyvsp[0].id), T_IF, T_ENDIF)) { menu_end_menu(); printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno()); } ;} break; case 65: { menu_add_entry(NULL); menu_add_prompt(P_MENU, (yyvsp[-1].string), NULL); printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno()); ;} break; case 66: { (yyval.menu) = menu_add_menu(); ;} break; case 67: { if (zconf_endtoken((yyvsp[0].id), T_MENU, T_ENDMENU)) { menu_end_menu(); printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno()); } ;} break; case 73: { printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string)); zconf_nextfile((yyvsp[-1].string)); ;} break; case 74: { menu_add_entry(NULL); menu_add_prompt(P_COMMENT, (yyvsp[-1].string), NULL); printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno()); ;} break; case 75: { menu_end_entry(); ;} break; case 76: { printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); zconf_starthelp(); ;} break; case 77: { current_entry->sym->help = (yyvsp[0].string); ;} break; case 82: { menu_add_dep((yyvsp[-1].expr)); printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno()); ;} break; case 83: { menu_add_dep((yyvsp[-1].expr)); printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno()); ;} break; case 84: { menu_add_dep((yyvsp[-1].expr)); printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno()); ;} break; case 86: { menu_add_prompt(P_PROMPT, (yyvsp[-1].string), (yyvsp[0].expr)); ;} break; case 89: { (yyval.id) = (yyvsp[-1].id); ;} break; case 90: { (yyval.id) = (yyvsp[-1].id); ;} break; case 91: { (yyval.id) = (yyvsp[-1].id); ;} break; case 94: { (yyval.expr) = NULL; ;} break; case 95: { (yyval.expr) = (yyvsp[0].expr); ;} break; case 96: { (yyval.expr) = expr_alloc_symbol((yyvsp[0].symbol)); ;} break; case 97: { (yyval.expr) = expr_alloc_comp(E_EQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); ;} break; case 98: { (yyval.expr) = expr_alloc_comp(E_UNEQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); ;} break; case 99: { (yyval.expr) = (yyvsp[-1].expr); ;} break; case 100: { (yyval.expr) = expr_alloc_one(E_NOT, (yyvsp[0].expr)); ;} break; case 101: { (yyval.expr) = expr_alloc_two(E_OR, (yyvsp[-2].expr), (yyvsp[0].expr)); ;} break; case 102: { (yyval.expr) = expr_alloc_two(E_AND, (yyvsp[-2].expr), (yyvsp[0].expr)); ;} break; case 103: { (yyval.symbol) = sym_lookup((yyvsp[0].string), 0); free((yyvsp[0].string)); ;} break; case 104: { (yyval.symbol) = sym_lookup((yyvsp[0].string), 1); free((yyvsp[0].string)); ;} break; } /* Line 1037 of yacc.c. */ yyvsp -= yylen; yyssp -= yylen; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*------------------------------------. | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if YYERROR_VERBOSE yyn = yypact[yystate]; if (YYPACT_NINF < yyn && yyn < YYLAST) { YYSIZE_T yysize = 0; int yytype = YYTRANSLATE (yychar); const char* yyprefix; char *yymsg; int yyx; /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yycount = 0; yyprefix = ", expecting "; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]); yycount += 1; if (yycount == 5) { yysize = 0; break; } } yysize += (sizeof ("syntax error, unexpected ") + yystrlen (yytname[yytype])); yymsg = (char *) YYSTACK_ALLOC (yysize); if (yymsg != 0) { char *yyp = yystpcpy (yymsg, "syntax error, unexpected "); yyp = yystpcpy (yyp, yytname[yytype]); if (yycount < 5) { yyprefix = ", expecting "; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { yyp = yystpcpy (yyp, yyprefix); yyp = yystpcpy (yyp, yytname[yyx]); yyprefix = " or "; } } yyerror (yymsg); YYSTACK_FREE (yymsg); } else yyerror ("syntax error; also virtual memory exhausted"); } else #endif /* YYERROR_VERBOSE */ yyerror ("syntax error"); } if (yyerrstatus == 3) { /* If just tried and failed to reuse look-ahead token after an error, discard it. */ if (yychar <= YYEOF) { /* If at end of input, pop the error token, then the rest of the stack, then return failure. */ if (yychar == YYEOF) for (;;) { YYPOPSTACK; if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[*yyssp], yyvsp); } } else { yydestruct ("Error: discarding", yytoken, &yylval); yychar = YYEMPTY; } } /* Else will try to reuse look-ahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: #ifdef __GNUC__ /* Pacify GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (0) goto yyerrorlab; #endif yyvsp -= yylen; yyssp -= yylen; yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[yystate], yyvsp); YYPOPSTACK; yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } if (yyn == YYFINAL) YYACCEPT; *++yyvsp = yylval; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yydestruct ("Error: discarding lookahead", yytoken, &yylval); yychar = YYEMPTY; yyresult = 1; goto yyreturn; #ifndef yyoverflow /*----------------------------------------------. | yyoverflowlab -- parser overflow comes here. | `----------------------------------------------*/ yyoverflowlab: yyerror ("parser stack overflow"); yyresult = 2; /* Fall through. */ #endif yyreturn: #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif return yyresult; } void conf_parse(const char *name) { struct symbol *sym; int i; zconf_initscan(name); sym_init(); menu_init(); modules_sym = sym_lookup("MODULES", 0); rootmenu.prompt = menu_add_prompt(P_MENU, "Busybox Configuration", NULL); #if YYDEBUG if (getenv("ZCONF_DEBUG")) zconfdebug = 1; #endif zconfparse(); if (zconfnerrs) exit(1); menu_finalize(&rootmenu); for_all_symbols(i, sym) { sym_check_deps(sym); } sym_change_count = 1; } const char *zconf_tokenname(int token) { switch (token) { case T_MENU: return "menu"; case T_ENDMENU: return "endmenu"; case T_CHOICE: return "choice"; case T_ENDCHOICE: return "endchoice"; case T_IF: return "if"; case T_ENDIF: return "endif"; case T_DEPENDS: return "depends"; } return ""; } static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken) { if (id->token != endtoken) { zconf_error("unexpected '%s' within %s block", kconf_id_strings + id->name, zconf_tokenname(starttoken)); zconfnerrs++; return false; } if (current_menu->file != current_file) { zconf_error("'%s' in different file than '%s'", kconf_id_strings + id->name, zconf_tokenname(starttoken)); fprintf(stderr, "%s:%d: location of the '%s'\n", current_menu->file->name, current_menu->lineno, zconf_tokenname(starttoken)); zconfnerrs++; return false; } return true; } static void zconfprint(const char *err, ...) { va_list ap; fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); va_start(ap, err); vfprintf(stderr, err, ap); va_end(ap); fprintf(stderr, "\n"); } static void zconf_error(const char *err, ...) { va_list ap; zconfnerrs++; fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); va_start(ap, err); vfprintf(stderr, err, ap); va_end(ap); fprintf(stderr, "\n"); } static void zconferror(const char *err) { #if YYDEBUG fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err); #endif } void print_quoted_string(FILE *out, const char *str) { const char *p; int len; putc('"', out); while ((p = strchr(str, '"'))) { len = p - str; if (len) fprintf(out, "%.*s", len, str); fputs("\\\"", out); str = p + 1; } fputs(str, out); putc('"', out); } void print_symbol(FILE *out, struct menu *menu) { struct symbol *sym = menu->sym; struct property *prop; if (sym_is_choice(sym)) fprintf(out, "choice\n"); else fprintf(out, "config %s\n", sym->name); switch (sym->type) { case S_BOOLEAN: fputs(" boolean\n", out); break; case S_TRISTATE: fputs(" tristate\n", out); break; case S_STRING: fputs(" string\n", out); break; case S_INT: fputs(" integer\n", out); break; case S_HEX: fputs(" hex\n", out); break; default: fputs(" ???\n", out); break; } for (prop = sym->prop; prop; prop = prop->next) { if (prop->menu != menu) continue; switch (prop->type) { case P_PROMPT: fputs(" prompt ", out); print_quoted_string(out, prop->text); if (!expr_is_yes(prop->visible.expr)) { fputs(" if ", out); expr_fprint(prop->visible.expr, out); } fputc('\n', out); break; case P_DEFAULT: fputs( " default ", out); expr_fprint(prop->expr, out); if (!expr_is_yes(prop->visible.expr)) { fputs(" if ", out); expr_fprint(prop->visible.expr, out); } fputc('\n', out); break; case P_CHOICE: fputs(" #choice value\n", out); break; default: fprintf(out, " unknown prop %d!\n", prop->type); break; } } if (sym->help) { int len = strlen(sym->help); while (sym->help[--len] == '\n') sym->help[len] = 0; fprintf(out, " help\n%s\n", sym->help); } fputc('\n', out); } void zconfdump(FILE *out) { struct property *prop; struct symbol *sym; struct menu *menu; menu = rootmenu.list; while (menu) { if ((sym = menu->sym)) print_symbol(out, menu); else if ((prop = menu->prompt)) { switch (prop->type) { case P_COMMENT: fputs("\ncomment ", out); print_quoted_string(out, prop->text); fputs("\n", out); break; case P_MENU: fputs("\nmenu ", out); print_quoted_string(out, prop->text); fputs("\n", out); break; default: ; } if (!expr_is_yes(prop->visible.expr)) { fputs(" depends ", out); expr_fprint(prop->visible.expr, out); fputc('\n', out); } fputs("\n", out); } if (menu->list) menu = menu->list; else if (menu->next) menu = menu->next; else while ((menu = menu->parent)) { if (menu->prompt && menu->prompt->type == P_MENU) fputs("\nendmenu\n", out); if (menu->next) { menu = menu->next; break; } } } } #include "lex.zconf.c" #include "util.c" #include "confdata.c" #include "expr.c" #include "symbol.c" #include "menu.c" busybox-1.22.1/scripts/kconfig/lex.zconf.c_shipped0000644000000000000000000015346412263563520020704 0ustar rootroot #line 3 "scripts/kconfig/lex.zconf.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 31 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; #endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ #if __STDC__ #define YY_USE_CONST #endif /* __STDC__ */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN (yy_start) = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START (((yy_start) - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE zconfrestart(zconfin ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #define YY_BUF_SIZE 16384 #endif #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif extern int zconfleng; extern FILE *zconfin, *zconfout; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up zconftext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = (yy_hold_char); \ YY_RESTORE_YY_MORE_OFFSET \ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up zconftext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, (yytext_ptr) ) /* The following is because we cannot portably get our hands on size_t * (without autoconf's help, which isn't available because we want * flex-generated scanners to compile on their own). */ #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef unsigned int yy_size_t; #endif #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via zconfrestart()), so that the user can continue scanning by * just pointing zconfin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* Stack of input buffers. */ static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] /* yy_hold_char holds the character lost when zconftext is formed. */ static char yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ int zconfleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = (char *) 0; static int yy_init = 1; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* Flag which is used to allow zconfwrap()'s to do buffer switches * instead of setting up a fresh zconfin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; void zconfrestart (FILE *input_file ); void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer ); YY_BUFFER_STATE zconf_create_buffer (FILE *file,int size ); void zconf_delete_buffer (YY_BUFFER_STATE b ); void zconf_flush_buffer (YY_BUFFER_STATE b ); void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer ); void zconfpop_buffer_state (void ); static void zconfensure_buffer_stack (void ); static void zconf_load_buffer_state (void ); static void zconf_init_buffer (YY_BUFFER_STATE b,FILE *file ); #define YY_FLUSH_BUFFER zconf_flush_buffer(YY_CURRENT_BUFFER ) YY_BUFFER_STATE zconf_scan_buffer (char *base,yy_size_t size ); YY_BUFFER_STATE zconf_scan_string (yyconst char *yy_str ); YY_BUFFER_STATE zconf_scan_bytes (yyconst char *bytes,int len ); void *zconfalloc (yy_size_t ); void *zconfrealloc (void *,yy_size_t ); void zconffree (void * ); #define yy_new_buffer zconf_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ zconfensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ zconf_create_buffer(zconfin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ zconfensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ zconf_create_buffer(zconfin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define zconfwrap() 1 #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; FILE *zconfin = (FILE *) 0, *zconfout = (FILE *) 0; typedef int yy_state_type; extern int zconflineno; int zconflineno = 1; extern char *zconftext; #define yytext_ptr zconftext static yyconst flex_int16_t yy_nxt[][17] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 11, 12, 13, 14, 12, 12, 15, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12 }, { 11, 12, 13, 14, 12, 12, 15, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12 }, { 11, 16, 16, 17, 16, 16, 16, 16, 16, 16, 16, 16, 16, 18, 16, 16, 16 }, { 11, 16, 16, 17, 16, 16, 16, 16, 16, 16, 16, 16, 16, 18, 16, 16, 16 }, { 11, 19, 20, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19 }, { 11, 19, 20, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19 }, { 11, 22, 22, 23, 22, 24, 22, 22, 24, 22, 22, 22, 22, 22, 22, 25, 22 }, { 11, 22, 22, 23, 22, 24, 22, 22, 24, 22, 22, 22, 22, 22, 22, 25, 22 }, { 11, 26, 26, 27, 28, 29, 30, 31, 29, 32, 33, 34, 35, 35, 36, 37, 38 }, { 11, 26, 26, 27, 28, 29, 30, 31, 29, 32, 33, 34, 35, 35, 36, 37, 38 }, { -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11 }, { 11, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12 }, { 11, -13, 39, 40, -13, -13, 41, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13 }, { 11, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14 }, { 11, 42, 42, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 }, { 11, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16 }, { 11, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17 }, { 11, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, 44, -18, -18, -18 }, { 11, 45, 45, -19, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45 }, { 11, -20, 46, 47, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20 }, { 11, 48, -21, -21, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48 }, { 11, 49, 49, 50, 49, -22, 49, 49, -22, 49, 49, 49, 49, 49, 49, -22, 49 }, { 11, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23 }, { 11, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24 }, { 11, 51, 51, 52, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51 }, { 11, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26 }, { 11, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27 }, { 11, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, 53, -28, -28 }, { 11, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29 }, { 11, 54, 54, -30, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54 }, { 11, -31, -31, -31, -31, -31, -31, 55, -31, -31, -31, -31, -31, -31, -31, -31, -31 }, { 11, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32 }, { 11, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33 }, { 11, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, 56, 57, 57, -34, -34, -34 }, { 11, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, 57, 57, 57, -35, -35, -35 }, { 11, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36 }, { 11, -37, -37, 58, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37 }, { 11, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, 59 }, { 11, -39, 39, 40, -39, -39, 41, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39 }, { 11, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40 }, { 11, 42, 42, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 }, { 11, 42, 42, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 }, { 11, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43 }, { 11, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, 44, -44, -44, -44 }, { 11, 45, 45, -45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45 }, { 11, -46, 46, 47, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46 }, { 11, 48, -47, -47, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48 }, { 11, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48 }, { 11, 49, 49, 50, 49, -49, 49, 49, -49, 49, 49, 49, 49, 49, 49, -49, 49 }, { 11, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50 }, { 11, -51, -51, 52, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51 }, { 11, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52 }, { 11, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53 }, { 11, 54, 54, -54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54 }, { 11, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55 }, { 11, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, 60, 57, 57, -56, -56, -56 }, { 11, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, 57, 57, 57, -57, -57, -57 }, { 11, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58 }, { 11, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59 }, { 11, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, 57, 57, 57, -60, -60, -60 }, } ; static yy_state_type yy_get_previous_state (void ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); static int yy_get_next_buffer (void ); static void yy_fatal_error (yyconst char msg[] ); /* Done after the current pattern has been matched and before the * corresponding action - sets up zconftext. */ #define YY_DO_BEFORE_ACTION \ (yytext_ptr) = yy_bp; \ zconfleng = (size_t) (yy_cp - yy_bp); \ (yy_hold_char) = *yy_cp; \ *yy_cp = '\0'; \ (yy_c_buf_p) = yy_cp; #define YY_NUM_RULES 33 #define YY_END_OF_BUFFER 34 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[61] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 5, 4, 2, 3, 7, 8, 6, 32, 29, 31, 24, 28, 27, 26, 22, 17, 13, 16, 20, 22, 11, 12, 19, 19, 14, 22, 22, 4, 2, 3, 3, 1, 6, 32, 29, 31, 30, 24, 23, 26, 25, 15, 20, 9, 19, 19, 21, 10, 18 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 5, 6, 1, 1, 7, 8, 9, 10, 1, 1, 1, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, 14, 1, 1, 1, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 1, 15, 1, 1, 13, 1, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 1, 16, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; extern int zconf_flex_debug; int zconf_flex_debug = 0; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET char *zconftext; /* * Copyright (C) 2002 Roman Zippel * Released under the terms of the GNU GPL v2.0. */ #include #include #include #include #include #define LKC_DIRECT_LINK #include "lkc.h" #define START_STRSIZE 16 static struct { struct file *file; int lineno; } current_pos; static char *text; static int text_size, text_asize; struct buffer { struct buffer *parent; YY_BUFFER_STATE state; }; struct buffer *current_buf; static int last_ts, first_ts; static void zconf_endhelp(void); static void zconf_endfile(void); void new_string(void) { text = malloc(START_STRSIZE); text_asize = START_STRSIZE; text_size = 0; *text = 0; } void append_string(const char *str, int size) { int new_size = text_size + size + 1; if (size > 70) { fprintf (stderr, "%s:%d error: Overlong line\n", current_file->name, current_file->lineno); } if (new_size > text_asize) { new_size += START_STRSIZE - 1; new_size &= -START_STRSIZE; text = realloc(text, new_size); text_asize = new_size; } memcpy(text + text_size, str, size); text_size += size; text[text_size] = 0; } void alloc_string(const char *str, int size) { text = malloc(size + 1); memcpy(text, str, size); text[size] = 0; } #define INITIAL 0 #define COMMAND 1 #define HELP 2 #define STRING 3 #define PARAM 4 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int zconfwrap (void ); #else extern int zconfwrap (void ); #endif #endif static void yyunput (int c,char *buf_ptr ); #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ); #endif //bbox: suppressing "defined but not used" warning #define YY_NO_INPUT 1 #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void ); #else static int input (void ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO (void) fwrite( zconftext, zconfleng, 1, zconfout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ errno=0; \ while ( (result = read( fileno(zconfin), (char *) buf, max_size )) < 0 ) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(zconfin); \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int zconflex (void); #define YY_DECL int zconflex (void) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after zconftext and zconfleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; int str = 0; int ts, i; if ( (yy_init) ) { (yy_init) = 0; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! (yy_start) ) (yy_start) = 1; /* first start state */ if ( ! zconfin ) zconfin = stdin; if ( ! zconfout ) zconfout = stdout; if ( ! YY_CURRENT_BUFFER ) { zconfensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = zconf_create_buffer(zconfin,YY_BUF_SIZE ); } zconf_load_buffer_state( ); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = (yy_c_buf_p); /* Support of zconftext. */ *yy_cp = (yy_hold_char); /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = (yy_start); yy_match: while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 ) ++yy_cp; yy_current_state = -yy_current_state; yy_find_action: yy_act = yy_accept[yy_current_state]; YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 1: /* rule 1 can match eol */ case 2: /* rule 2 can match eol */ YY_RULE_SETUP { current_file->lineno++; return T_EOL; } YY_BREAK case 3: YY_RULE_SETUP YY_BREAK case 4: YY_RULE_SETUP { BEGIN(COMMAND); } YY_BREAK case 5: YY_RULE_SETUP { unput(zconftext[0]); BEGIN(COMMAND); } YY_BREAK case 6: YY_RULE_SETUP { struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng); BEGIN(PARAM); current_pos.file = current_file; current_pos.lineno = current_file->lineno; if (id && id->flags & TF_COMMAND) { zconflval.id = id; return id->token; } alloc_string(zconftext, zconfleng); zconflval.string = text; return T_WORD; } YY_BREAK case 7: YY_RULE_SETUP YY_BREAK case 8: /* rule 8 can match eol */ YY_RULE_SETUP { BEGIN(INITIAL); current_file->lineno++; return T_EOL; } YY_BREAK case 9: YY_RULE_SETUP return T_AND; YY_BREAK case 10: YY_RULE_SETUP return T_OR; YY_BREAK case 11: YY_RULE_SETUP return T_OPEN_PAREN; YY_BREAK case 12: YY_RULE_SETUP return T_CLOSE_PAREN; YY_BREAK case 13: YY_RULE_SETUP return T_NOT; YY_BREAK case 14: YY_RULE_SETUP return T_EQUAL; YY_BREAK case 15: YY_RULE_SETUP return T_UNEQUAL; YY_BREAK case 16: YY_RULE_SETUP { str = zconftext[0]; new_string(); BEGIN(STRING); } YY_BREAK case 17: /* rule 17 can match eol */ YY_RULE_SETUP BEGIN(INITIAL); current_file->lineno++; return T_EOL; YY_BREAK case 18: YY_RULE_SETUP /* ignore */ YY_BREAK case 19: YY_RULE_SETUP { struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng); if (id && id->flags & TF_PARAM) { zconflval.id = id; return id->token; } alloc_string(zconftext, zconfleng); zconflval.string = text; return T_WORD; } YY_BREAK case 20: YY_RULE_SETUP /* comment */ YY_BREAK case 21: /* rule 21 can match eol */ YY_RULE_SETUP current_file->lineno++; YY_BREAK case 22: YY_RULE_SETUP YY_BREAK case YY_STATE_EOF(PARAM): { BEGIN(INITIAL); } YY_BREAK case 23: /* rule 23 can match eol */ *yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ (yy_c_buf_p) = yy_cp -= 1; YY_DO_BEFORE_ACTION; /* set up zconftext again */ YY_RULE_SETUP { append_string(zconftext, zconfleng); zconflval.string = text; return T_WORD_QUOTE; } YY_BREAK case 24: YY_RULE_SETUP { append_string(zconftext, zconfleng); } YY_BREAK case 25: /* rule 25 can match eol */ *yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ (yy_c_buf_p) = yy_cp -= 1; YY_DO_BEFORE_ACTION; /* set up zconftext again */ YY_RULE_SETUP { append_string(zconftext + 1, zconfleng - 1); zconflval.string = text; return T_WORD_QUOTE; } YY_BREAK case 26: YY_RULE_SETUP { append_string(zconftext + 1, zconfleng - 1); } YY_BREAK case 27: YY_RULE_SETUP { if (str == zconftext[0]) { BEGIN(PARAM); zconflval.string = text; return T_WORD_QUOTE; } else append_string(zconftext, 1); } YY_BREAK case 28: /* rule 28 can match eol */ YY_RULE_SETUP { printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); current_file->lineno++; BEGIN(INITIAL); return T_EOL; } YY_BREAK case YY_STATE_EOF(STRING): { BEGIN(INITIAL); } YY_BREAK case 29: YY_RULE_SETUP { ts = 0; for (i = 0; i < zconfleng; i++) { if (zconftext[i] == '\t') ts = (ts & ~7) + 8; else ts++; } last_ts = ts; if (first_ts) { if (ts < first_ts) { zconf_endhelp(); return T_HELPTEXT; } ts -= first_ts; while (ts > 8) { append_string(" ", 8); ts -= 8; } append_string(" ", ts); } } YY_BREAK case 30: /* rule 30 can match eol */ *yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ (yy_c_buf_p) = yy_cp -= 1; YY_DO_BEFORE_ACTION; /* set up zconftext again */ YY_RULE_SETUP { current_file->lineno++; zconf_endhelp(); return T_HELPTEXT; } YY_BREAK case 31: /* rule 31 can match eol */ YY_RULE_SETUP { current_file->lineno++; append_string("\n", 1); } YY_BREAK case 32: YY_RULE_SETUP { append_string(zconftext, zconfleng); if (!first_ts) first_ts = last_ts; } YY_BREAK case YY_STATE_EOF(HELP): { zconf_endhelp(); return T_HELPTEXT; } YY_BREAK case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(COMMAND): { if (current_file) { zconf_endfile(); return T_EOL; } fclose(zconfin); yyterminate(); } YY_BREAK case 33: YY_RULE_SETUP YY_FATAL_ERROR( "flex scanner jammed" ); YY_BREAK case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = (yy_hold_char); YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed zconfin at a new source and called * zconflex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = zconfin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) { /* This was really a NUL. */ yy_state_type yy_next_state; (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = (yytext_ptr) + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++(yy_c_buf_p); yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = (yy_c_buf_p); goto yy_find_action; } } else switch ( yy_get_next_buffer( ) ) { case EOB_ACT_END_OF_FILE: { (yy_did_buffer_switch_on_eof) = 0; if ( zconfwrap( ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * zconftext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: (yy_c_buf_p) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of zconflex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (void) { register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = (yytext_ptr); register int number_to_move, i; int ret_val; if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; else { size_t num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = (int) ((yy_c_buf_p) - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ zconfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), (yy_n_chars), num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } if ( (yy_n_chars) == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; zconfrestart(zconfin ); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; (yy_n_chars) += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (void) { register yy_state_type yy_current_state; register char *yy_cp; yy_current_state = (yy_start); for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) { yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) { register int yy_is_jam; yy_current_state = yy_nxt[yy_current_state][1]; yy_is_jam = (yy_current_state <= 0); return yy_is_jam ? 0 : yy_current_state; } static void yyunput (int c, register char * yy_bp ) { register char *yy_cp; yy_cp = (yy_c_buf_p); /* undo effects of setting up zconftext */ *yy_cp = (yy_hold_char); if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) { /* need to shift things up to make room */ /* +2 for EOB chars. */ register int number_to_move = (yy_n_chars) + 2; register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; register char *source = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) *--dest = *--source; yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); } *--yy_cp = (char) c; (yytext_ptr) = yy_bp; (yy_hold_char) = *yy_cp; (yy_c_buf_p) = yy_cp; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void) #else static int input (void) #endif { int c; *(yy_c_buf_p) = (yy_hold_char); if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) /* This was really a NUL. */ *(yy_c_buf_p) = '\0'; else { /* need more input */ int offset = (yy_c_buf_p) - (yytext_ptr); ++(yy_c_buf_p); switch ( yy_get_next_buffer( ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ zconfrestart(zconfin ); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( zconfwrap( ) ) return EOF; if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(); #else return input(); #endif } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + offset; break; } } } c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ *(yy_c_buf_p) = '\0'; /* preserve zconftext */ (yy_hold_char) = *++(yy_c_buf_p); return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * * @note This function does not reset the start condition to @c INITIAL . */ void zconfrestart (FILE * input_file ) { if ( ! YY_CURRENT_BUFFER ){ zconfensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = zconf_create_buffer(zconfin,YY_BUF_SIZE ); } zconf_init_buffer(YY_CURRENT_BUFFER,input_file ); zconf_load_buffer_state( ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * */ void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer ) { /* TODO. We should be able to replace this entire function body * with * zconfpop_buffer_state(); * zconfpush_buffer_state(new_buffer); */ zconfensure_buffer_stack (); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } YY_CURRENT_BUFFER_LVALUE = new_buffer; zconf_load_buffer_state( ); /* We don't actually know whether we did this switch during * EOF (zconfwrap()) processing, but the only time this flag * is looked at is after zconfwrap() is called, so it's safe * to go ahead and always set it. */ (yy_did_buffer_switch_on_eof) = 1; } static void zconf_load_buffer_state (void) { (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; zconfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; (yy_hold_char) = *(yy_c_buf_p); } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * * @return the allocated buffer state. */ YY_BUFFER_STATE zconf_create_buffer (FILE * file, int size ) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) zconfalloc(b->yy_buf_size + 2 ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" ); b->yy_is_our_buffer = 1; zconf_init_buffer(b,file ); return b; } /** Destroy the buffer. * @param b a buffer created with zconf_create_buffer() * */ void zconf_delete_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) zconffree((void *) b->yy_ch_buf ); zconffree((void *) b ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a zconfrestart() or at EOF. */ static void zconf_init_buffer (YY_BUFFER_STATE b, FILE * file ) { int oerrno = errno; zconf_flush_buffer(b ); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then zconf_init_buffer was _probably_ * called from zconfrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * */ void zconf_flush_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) zconf_load_buffer_state( ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * */ void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer ) { if (new_buffer == NULL) return; zconfensure_buffer_stack(); /* This block is copied from zconf_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) (yy_buffer_stack_top)++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from zconf_switch_to_buffer. */ zconf_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * */ void zconfpop_buffer_state (void) { if (!YY_CURRENT_BUFFER) return; zconf_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; if ((yy_buffer_stack_top) > 0) --(yy_buffer_stack_top); if (YY_CURRENT_BUFFER) { zconf_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void zconfensure_buffer_stack (void) { int num_to_alloc; if (!(yy_buffer_stack)) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; (yy_buffer_stack) = (struct yy_buffer_state**)zconfalloc (num_to_alloc * sizeof(struct yy_buffer_state*) ); memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; (yy_buffer_stack_top) = 0; return; } if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = (yy_buffer_stack_max) + grow_size; (yy_buffer_stack) = (struct yy_buffer_state**)zconfrealloc ((yy_buffer_stack), num_to_alloc * sizeof(struct yy_buffer_state*) ); /* zero only the new slots.*/ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE zconf_scan_buffer (char * base, yy_size_t size ) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; zconf_switch_to_buffer(b ); return b; } /** Setup the input buffer state to scan a string. The next call to zconflex() will * scan from a @e copy of @a str. * @param yy_str a NUL-terminated string to scan * * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * zconf_scan_bytes() instead. */ YY_BUFFER_STATE zconf_scan_string (yyconst char * yy_str ) { return zconf_scan_bytes(yy_str,strlen(yy_str) ); } /** Setup the input buffer state to scan the given bytes. The next call to zconflex() will * scan from a @e copy of @a bytes. * @param bytes the byte buffer to scan * @param len the number of bytes in the buffer pointed to by @a bytes. * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE zconf_scan_bytes (yyconst char * bytes, int len ) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = len + 2; buf = (char *) zconfalloc(n ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_bytes()" ); for ( i = 0; i < len; ++i ) buf[i] = bytes[i]; buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; b = zconf_scan_buffer(buf,n ); if ( ! b ) YY_FATAL_ERROR( "bad buffer in zconf_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg ) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up zconftext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ zconftext[zconfleng] = (yy_hold_char); \ (yy_c_buf_p) = zconftext + yyless_macro_arg; \ (yy_hold_char) = *(yy_c_buf_p); \ *(yy_c_buf_p) = '\0'; \ zconfleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the current line number. * */ int zconfget_lineno (void) { return zconflineno; } /** Get the input stream. * */ FILE *zconfget_in (void) { return zconfin; } /** Get the output stream. * */ FILE *zconfget_out (void) { return zconfout; } /** Get the length of the current token. * */ int zconfget_leng (void) { return zconfleng; } /** Get the current token. * */ char *zconfget_text (void) { return zconftext; } /** Set the current line number. * @param line_number * */ void zconfset_lineno (int line_number ) { zconflineno = line_number; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * * @see zconf_switch_to_buffer */ void zconfset_in (FILE * in_str ) { zconfin = in_str ; } void zconfset_out (FILE * out_str ) { zconfout = out_str ; } int zconfget_debug (void) { return zconf_flex_debug; } void zconfset_debug (int bdebug ) { zconf_flex_debug = bdebug ; } /* zconflex_destroy is for both reentrant and non-reentrant scanners. */ int zconflex_destroy (void) { /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ zconf_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; zconfpop_buffer_state(); } /* Destroy the stack itself. */ zconffree((yy_buffer_stack) ); (yy_buffer_stack) = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s ) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *zconfalloc (yy_size_t size ) { return (void *) malloc( size ); } void *zconfrealloc (void * ptr, yy_size_t size ) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } void zconffree (void * ptr ) { free( (char *) ptr ); /* see zconfrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #undef YY_NEW_FILE #undef YY_FLUSH_BUFFER #undef yy_set_bol #undef yy_new_buffer #undef yy_set_interactive #undef yytext_ptr #undef YY_DO_BEFORE_ACTION #ifdef YY_DECL_IS_OURS #undef YY_DECL_IS_OURS #undef YY_DECL #endif void zconf_starthelp(void) { new_string(); last_ts = first_ts = 0; BEGIN(HELP); } static void zconf_endhelp(void) { zconflval.string = text; BEGIN(INITIAL); } /* * Try to open specified file with following names: * ./name * $(srctree)/name * The latter is used when srctree is separate from objtree * when compiling the kernel. * Return NULL if file is not found. */ FILE *zconf_fopen(const char *name) { char *env; FILE *f; f = fopen(name, "r"); if (!f && name[0] != '/') { env = getenv(SRCTREE); if (env) { char *fullname = alloca(strlen(env) + strlen(name) + 2); sprintf(fullname, "%s/%s", env, name); f = fopen(fullname, "r"); } } return f; } void zconf_initscan(const char *name) { zconfin = zconf_fopen(name); if (!zconfin) { printf("can't find file %s\n", name); exit(1); } current_buf = malloc(sizeof(*current_buf)); memset(current_buf, 0, sizeof(*current_buf)); current_file = file_lookup(name); current_file->lineno = 1; current_file->flags = FILE_BUSY; } void zconf_nextfile(const char *name) { struct file *file = file_lookup(name); struct buffer *buf = malloc(sizeof(*buf)); memset(buf, 0, sizeof(*buf)); current_buf->state = YY_CURRENT_BUFFER; zconfin = zconf_fopen(name); if (!zconfin) { printf("%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), name); exit(1); } zconf_switch_to_buffer(zconf_create_buffer(zconfin,YY_BUF_SIZE)); buf->parent = current_buf; current_buf = buf; if (file->flags & FILE_BUSY) { printf("recursive scan (%s)?\n", name); exit(1); } if (file->flags & FILE_SCANNED) { printf("file %s already scanned?\n", name); exit(1); } file->flags |= FILE_BUSY; file->lineno = 1; file->parent = current_file; current_file = file; } static void zconf_endfile(void) { struct buffer *parent; current_file->flags |= FILE_SCANNED; current_file->flags &= ~FILE_BUSY; current_file = current_file->parent; parent = current_buf->parent; if (parent) { fclose(zconfin); zconf_delete_buffer(YY_CURRENT_BUFFER); zconf_switch_to_buffer(parent->state); } free(current_buf); current_buf = parent; } int zconf_lineno(void) { return current_pos.lineno; } char *zconf_curname(void) { return current_pos.file ? current_pos.file->name : ""; } busybox-1.22.1/scripts/kconfig/menu.c0000644000000000000000000002356012263563520016217 0ustar rootroot/* * Copyright (C) 2002 Roman Zippel * Released under the terms of the GNU GPL v2.0. */ #include #include #define LKC_DIRECT_LINK #include "lkc.h" struct menu rootmenu; static struct menu **last_entry_ptr; struct file *file_list; struct file *current_file; static void menu_warn(struct menu *menu, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); } static void prop_warn(struct property *prop, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); } void menu_init(void) { current_entry = current_menu = &rootmenu; last_entry_ptr = &rootmenu.list; } void menu_add_entry(struct symbol *sym) { struct menu *menu; menu = malloc(sizeof(*menu)); memset(menu, 0, sizeof(*menu)); menu->sym = sym; menu->parent = current_menu; menu->file = current_file; menu->lineno = zconf_lineno(); *last_entry_ptr = menu; last_entry_ptr = &menu->next; current_entry = menu; } void menu_end_entry(void) { } struct menu *menu_add_menu(void) { menu_end_entry(); last_entry_ptr = ¤t_entry->list; return current_menu = current_entry; } void menu_end_menu(void) { last_entry_ptr = ¤t_menu->next; current_menu = current_menu->parent; } struct expr *menu_check_dep(struct expr *e) { if (!e) return e; switch (e->type) { case E_NOT: e->left.expr = menu_check_dep(e->left.expr); break; case E_OR: case E_AND: e->left.expr = menu_check_dep(e->left.expr); e->right.expr = menu_check_dep(e->right.expr); break; case E_SYMBOL: /* change 'm' into 'm' && MODULES */ if (e->left.sym == &symbol_mod) return expr_alloc_and(e, expr_alloc_symbol(modules_sym)); break; default: break; } return e; } void menu_add_dep(struct expr *dep) { current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep)); } void menu_set_type(int type) { struct symbol *sym = current_entry->sym; if (sym->type == type) return; if (sym->type == S_UNKNOWN) { sym->type = type; return; } menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'\n", sym->name ? sym->name : "", sym_type_name(sym->type), sym_type_name(type)); } struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep) { struct property *prop = prop_alloc(type, current_entry->sym); prop->menu = current_entry; prop->text = prompt; prop->expr = expr; prop->visible.expr = menu_check_dep(dep); if (prompt) { if (current_entry->prompt) menu_warn(current_entry, "prompt redefined\n"); current_entry->prompt = prop; } return prop; } struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep) { return menu_add_prop(type, prompt, NULL, dep); } void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep) { menu_add_prop(type, NULL, expr, dep); } void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep) { menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep); } static int menu_range_valid_sym(struct symbol *sym, struct symbol *sym2) { return sym2->type == S_INT || sym2->type == S_HEX || (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name)); } void sym_check_prop(struct symbol *sym) { struct property *prop; struct symbol *sym2; for (prop = sym->prop; prop; prop = prop->next) { switch (prop->type) { case P_DEFAULT: if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) && prop->expr->type != E_SYMBOL) prop_warn(prop, "default for config symbol '%'" " must be a single symbol", sym->name); break; case P_SELECT: sym2 = prop_get_symbol(prop); if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE) prop_warn(prop, "config symbol '%s' uses select, but is " "not boolean or tristate", sym->name); else if (sym2->type == S_UNKNOWN) prop_warn(prop, "'select' used by config symbol '%s' " "refer to undefined symbol '%s'", sym->name, sym2->name); else if (sym2->type != S_BOOLEAN && sym2->type != S_TRISTATE) prop_warn(prop, "'%s' has wrong type. 'select' only " "accept arguments of boolean and " "tristate type", sym2->name); break; case P_RANGE: if (sym->type != S_INT && sym->type != S_HEX) prop_warn(prop, "range is only allowed " "for int or hex symbols"); if (!menu_range_valid_sym(sym, prop->expr->left.sym) || !menu_range_valid_sym(sym, prop->expr->right.sym)) prop_warn(prop, "range is invalid"); break; default: ; } } } void menu_finalize(struct menu *parent) { struct menu *menu, *last_menu; struct symbol *sym; struct property *prop; struct expr *parentdep, *basedep, *dep, *dep2, **ep; sym = parent->sym; if (parent->list) { if (sym && sym_is_choice(sym)) { /* find the first choice value and find out choice type */ for (menu = parent->list; menu; menu = menu->next) { if (menu->sym) { current_entry = parent; menu_set_type(menu->sym->type); current_entry = menu; menu_set_type(sym->type); break; } } parentdep = expr_alloc_symbol(sym); } else if (parent->prompt) parentdep = parent->prompt->visible.expr; else parentdep = parent->dep; for (menu = parent->list; menu; menu = menu->next) { basedep = expr_transform(menu->dep); basedep = expr_alloc_and(expr_copy(parentdep), basedep); basedep = expr_eliminate_dups(basedep); menu->dep = basedep; if (menu->sym) prop = menu->sym->prop; else prop = menu->prompt; for (; prop; prop = prop->next) { if (prop->menu != menu) continue; dep = expr_transform(prop->visible.expr); dep = expr_alloc_and(expr_copy(basedep), dep); dep = expr_eliminate_dups(dep); if (menu->sym && menu->sym->type != S_TRISTATE) dep = expr_trans_bool(dep); prop->visible.expr = dep; if (prop->type == P_SELECT) { struct symbol *es = prop_get_symbol(prop); es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); } } } for (menu = parent->list; menu; menu = menu->next) menu_finalize(menu); } else if (sym) { basedep = parent->prompt ? parent->prompt->visible.expr : NULL; basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); basedep = expr_eliminate_dups(expr_transform(basedep)); last_menu = NULL; for (menu = parent->next; menu; menu = menu->next) { dep = menu->prompt ? menu->prompt->visible.expr : menu->dep; if (!expr_contains_symbol(dep, sym)) break; if (expr_depends_symbol(dep, sym)) goto next; dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); dep = expr_eliminate_dups(expr_transform(dep)); dep2 = expr_copy(basedep); expr_eliminate_eq(&dep, &dep2); expr_free(dep); if (!expr_is_yes(dep2)) { expr_free(dep2); break; } expr_free(dep2); next: menu_finalize(menu); menu->parent = parent; last_menu = menu; } if (last_menu) { parent->list = parent->next; parent->next = last_menu->next; last_menu->next = NULL; } } for (menu = parent->list; menu; menu = menu->next) { if (sym && sym_is_choice(sym) && menu->sym) { menu->sym->flags |= SYMBOL_CHOICEVAL; if (!menu->prompt) menu_warn(menu, "choice value must have a prompt"); for (prop = menu->sym->prop; prop; prop = prop->next) { if (prop->type == P_PROMPT && prop->menu != menu) { prop_warn(prop, "choice values " "currently only support a " "single prompt"); } if (prop->type == P_DEFAULT) prop_warn(prop, "defaults for choice " "values not supported"); } current_entry = menu; menu_set_type(sym->type); menu_add_symbol(P_CHOICE, sym, NULL); prop = sym_get_choice_prop(sym); for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) ; *ep = expr_alloc_one(E_CHOICE, NULL); (*ep)->right.sym = menu->sym; } if (menu->list && (!menu->prompt || !menu->prompt->text)) { for (last_menu = menu->list; ; last_menu = last_menu->next) { last_menu->parent = parent; if (!last_menu->next) break; } last_menu->next = menu->next; menu->next = menu->list; menu->list = NULL; } } if (sym && !(sym->flags & SYMBOL_WARNED)) { if (sym->type == S_UNKNOWN) menu_warn(parent, "config symbol defined " "without type\n"); if (sym_is_choice(sym) && !parent->prompt) menu_warn(parent, "choice must have a prompt\n"); /* Check properties connected to this symbol */ sym_check_prop(sym); sym->flags |= SYMBOL_WARNED; } if (sym && !sym_is_optional(sym) && parent->prompt) { sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, expr_alloc_and(parent->prompt->visible.expr, expr_alloc_symbol(&symbol_mod))); } } bool menu_is_visible(struct menu *menu) { struct menu *child; struct symbol *sym; tristate visible; if (!menu->prompt) return false; sym = menu->sym; if (sym) { sym_calc_value(sym); visible = menu->prompt->visible.tri; } else visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr); if (visible != no) return true; if (!sym || sym_get_tristate_value(menu->sym) == no) return false; for (child = menu->list; child; child = child->next) if (menu_is_visible(child)) return true; return false; } const char *menu_get_prompt(struct menu *menu) { if (menu->prompt) return _(menu->prompt->text); else if (menu->sym) return _(menu->sym->name); return NULL; } struct menu *menu_get_root_menu(struct menu *menu) { return &rootmenu; } struct menu *menu_get_parent_menu(struct menu *menu) { enum prop_type type; for (; menu != &rootmenu; menu = menu->parent) { type = menu->prompt ? menu->prompt->type : 0; if (type == P_MENU) break; } return menu; } busybox-1.22.1/scripts/kconfig/zconf.gperf0000644000000000000000000000230612263563520017246 0ustar rootroot%language=ANSI-C %define hash-function-name kconf_id_hash %define lookup-function-name kconf_id_lookup %define string-pool-name kconf_id_strings %compare-strncmp %enum %pic %struct-type struct kconf_id; %% mainmenu, T_MAINMENU, TF_COMMAND menu, T_MENU, TF_COMMAND endmenu, T_ENDMENU, TF_COMMAND source, T_SOURCE, TF_COMMAND choice, T_CHOICE, TF_COMMAND endchoice, T_ENDCHOICE, TF_COMMAND comment, T_COMMENT, TF_COMMAND config, T_CONFIG, TF_COMMAND menuconfig, T_MENUCONFIG, TF_COMMAND help, T_HELP, TF_COMMAND if, T_IF, TF_COMMAND|TF_PARAM endif, T_ENDIF, TF_COMMAND depends, T_DEPENDS, TF_COMMAND requires, T_REQUIRES, TF_COMMAND optional, T_OPTIONAL, TF_COMMAND default, T_DEFAULT, TF_COMMAND, S_UNKNOWN prompt, T_PROMPT, TF_COMMAND tristate, T_TYPE, TF_COMMAND, S_TRISTATE def_tristate, T_DEFAULT, TF_COMMAND, S_TRISTATE bool, T_TYPE, TF_COMMAND, S_BOOLEAN boolean, T_TYPE, TF_COMMAND, S_BOOLEAN def_bool, T_DEFAULT, TF_COMMAND, S_BOOLEAN def_boolean, T_DEFAULT, TF_COMMAND, S_BOOLEAN int, T_TYPE, TF_COMMAND, S_INT hex, T_TYPE, TF_COMMAND, S_HEX string, T_TYPE, TF_COMMAND, S_STRING select, T_SELECT, TF_COMMAND enable, T_SELECT, TF_COMMAND range, T_RANGE, TF_COMMAND on, T_ON, TF_PARAM %% busybox-1.22.1/scripts/kconfig/qconf.h0000644000000000000000000001315012263563520016360 0ustar rootroot/* * Copyright (C) 2002 Roman Zippel * Released under the terms of the GNU GPL v2.0. */ #include #if QT_VERSION >= 300 #include #else class QSettings { }; #endif class ConfigList; class ConfigItem; class ConfigLineEdit; class ConfigMainWindow; class ConfigSettings : public QSettings { public: ConfigSettings(); #if QT_VERSION >= 300 void readListSettings(); QValueList readSizes(const QString& key, bool *ok); bool writeSizes(const QString& key, const QValueList& value); #endif bool showAll; bool showName; bool showRange; bool showData; }; class ConfigView : public QVBox { Q_OBJECT typedef class QVBox Parent; public: ConfigView(QWidget* parent, ConfigMainWindow* cview, ConfigSettings* configSettings); ~ConfigView(void); static void updateList(ConfigItem* item); static void updateListAll(void); public: ConfigList* list; ConfigLineEdit* lineEdit; static ConfigView* viewList; ConfigView* nextView; }; enum colIdx { promptColIdx, nameColIdx, noColIdx, modColIdx, yesColIdx, dataColIdx, colNr }; enum listMode { singleMode, menuMode, symbolMode, fullMode }; class ConfigList : public QListView { Q_OBJECT typedef class QListView Parent; public: ConfigList(ConfigView* p, ConfigMainWindow* cview, ConfigSettings *configSettings); void reinit(void); ConfigView* parent(void) const { return (ConfigView*)Parent::parent(); } protected: ConfigMainWindow* cview; void keyPressEvent(QKeyEvent *e); void contentsMousePressEvent(QMouseEvent *e); void contentsMouseReleaseEvent(QMouseEvent *e); void contentsMouseMoveEvent(QMouseEvent *e); void contentsMouseDoubleClickEvent(QMouseEvent *e); void focusInEvent(QFocusEvent *e); public slots: void setRootMenu(struct menu *menu); void updateList(ConfigItem *item); void setValue(ConfigItem* item, tristate val); void changeValue(ConfigItem* item); void updateSelection(void); signals: void menuSelected(struct menu *menu); void parentSelected(void); void gotFocus(void); public: void updateListAll(void) { updateAll = true; updateList(NULL); updateAll = false; } ConfigList* listView() { return this; } ConfigItem* firstChild() const { return (ConfigItem *)Parent::firstChild(); } int mapIdx(colIdx idx) { return colMap[idx]; } void addColumn(colIdx idx, const QString& label) { colMap[idx] = Parent::addColumn(label); colRevMap[colMap[idx]] = idx; } void removeColumn(colIdx idx) { int col = colMap[idx]; if (col >= 0) { Parent::removeColumn(col); colRevMap[col] = colMap[idx] = -1; } } void setAllOpen(bool open); void setParentMenu(void); template void updateMenuList(P*, struct menu*); bool updateAll; QPixmap symbolYesPix, symbolModPix, symbolNoPix; QPixmap choiceYesPix, choiceNoPix; QPixmap menuPix, menuInvPix, menuBackPix, voidPix; bool showAll, showName, showRange, showData; enum listMode mode; struct menu *rootEntry; QColorGroup disabledColorGroup; QColorGroup inactivedColorGroup; private: int colMap[colNr]; int colRevMap[colNr]; }; class ConfigItem : public QListViewItem { typedef class QListViewItem Parent; public: ConfigItem(QListView *parent, ConfigItem *after, struct menu *m, bool v) : Parent(parent, after), menu(m), visible(v), goParent(false) { init(); } ConfigItem(ConfigItem *parent, ConfigItem *after, struct menu *m, bool v) : Parent(parent, after), menu(m), visible(v), goParent(false) { init(); } ConfigItem(QListView *parent, ConfigItem *after, bool v) : Parent(parent, after), menu(0), visible(v), goParent(true) { init(); } ~ConfigItem(void); void init(void); #if QT_VERSION >= 300 void okRename(int col); #endif void updateMenu(void); void testUpdateMenu(bool v); ConfigList* listView() const { return (ConfigList*)Parent::listView(); } ConfigItem* firstChild() const { return (ConfigItem *)Parent::firstChild(); } ConfigItem* nextSibling() const { return (ConfigItem *)Parent::nextSibling(); } void setText(colIdx idx, const QString& text) { Parent::setText(listView()->mapIdx(idx), text); } QString text(colIdx idx) const { return Parent::text(listView()->mapIdx(idx)); } void setPixmap(colIdx idx, const QPixmap& pm) { Parent::setPixmap(listView()->mapIdx(idx), pm); } const QPixmap* pixmap(colIdx idx) const { return Parent::pixmap(listView()->mapIdx(idx)); } void paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align); ConfigItem* nextItem; struct menu *menu; bool visible; bool goParent; }; class ConfigLineEdit : public QLineEdit { Q_OBJECT typedef class QLineEdit Parent; public: ConfigLineEdit(ConfigView* parent) : Parent(parent) { } ConfigView* parent(void) const { return (ConfigView*)Parent::parent(); } void show(ConfigItem *i); void keyPressEvent(QKeyEvent *e); public: ConfigItem *item; }; class ConfigMainWindow : public QMainWindow { Q_OBJECT public: ConfigMainWindow(void); public slots: void setHelp(QListViewItem* item); void changeMenu(struct menu *); void listFocusChanged(void); void goBack(void); void loadConfig(void); void saveConfig(void); void saveConfigAs(void); void showSingleView(void); void showSplitView(void); void showFullView(void); void setShowAll(bool); void setShowDebug(bool); void setShowRange(bool); void setShowName(bool); void setShowData(bool); void showIntro(void); void showAbout(void); void saveSettings(void); protected: void closeEvent(QCloseEvent *e); ConfigView *menuView; ConfigList *menuList; ConfigView *configView; ConfigList *configList; QTextView *helpText; QToolBar *toolBar; QAction *backAction; QSplitter* split1; QSplitter* split2; bool showDebug; }; busybox-1.22.1/scripts/kconfig/kconfig_load.c0000644000000000000000000000115712263563520017670 0ustar rootroot#include #include #include #include "lkc.h" #define P(name,type,arg) type (*name ## _p) arg #include "lkc_proto.h" #undef P void kconfig_load(void) { void *handle; char *error; handle = dlopen("./libkconfig.so", RTLD_LAZY); if (!handle) { handle = dlopen("./scripts/kconfig/libkconfig.so", RTLD_LAZY); if (!handle) { fprintf(stderr, "%s\n", dlerror()); exit(1); } } #define P(name,type,arg) \ { \ name ## _p = dlsym(handle, #name); \ if ((error = dlerror())) { \ fprintf(stderr, "%s\n", error); \ exit(1); \ } \ } #include "lkc_proto.h" #undef P } busybox-1.22.1/scripts/kconfig/zconf.l0000644000000000000000000001436512263563520016406 0ustar rootroot%option backup nostdinit noyywrap never-interactive full ecs %option 8bit backup nodefault perf-report perf-report %x COMMAND HELP STRING PARAM %{ /* * Copyright (C) 2002 Roman Zippel * Released under the terms of the GNU GPL v2.0. */ #include #include #include #include #include #define LKC_DIRECT_LINK #include "lkc.h" #define START_STRSIZE 16 static struct { struct file *file; int lineno; } current_pos; static char *text; static int text_size, text_asize; struct buffer { struct buffer *parent; YY_BUFFER_STATE state; }; struct buffer *current_buf; static int last_ts, first_ts; static void zconf_endhelp(void); static void zconf_endfile(void); void new_string(void) { text = malloc(START_STRSIZE); text_asize = START_STRSIZE; text_size = 0; *text = 0; } void append_string(const char *str, int size) { int new_size = text_size + size + 1; if (size > 70) { fprintf (stderr, "%s:%d error: Overlong line\n", current_file->name, current_file->lineno); } if (new_size > text_asize) { new_size += START_STRSIZE - 1; new_size &= -START_STRSIZE; text = realloc(text, new_size); text_asize = new_size; } memcpy(text + text_size, str, size); text_size += size; text[text_size] = 0; } void alloc_string(const char *str, int size) { text = malloc(size + 1); memcpy(text, str, size); text[size] = 0; } %} ws [ \n\t] n [A-Za-z0-9_] %% int str = 0; int ts, i; [ \t]*#.*\n | [ \t]*\n { current_file->lineno++; return T_EOL; } [ \t]*#.* [ \t]+ { BEGIN(COMMAND); } . { unput(yytext[0]); BEGIN(COMMAND); } { {n}+ { struct kconf_id *id = kconf_id_lookup(yytext, yyleng); BEGIN(PARAM); current_pos.file = current_file; current_pos.lineno = current_file->lineno; if (id && id->flags & TF_COMMAND) { zconflval.id = id; return id->token; } alloc_string(yytext, yyleng); zconflval.string = text; return T_WORD; } . \n { BEGIN(INITIAL); current_file->lineno++; return T_EOL; } } { "&&" return T_AND; "||" return T_OR; "(" return T_OPEN_PAREN; ")" return T_CLOSE_PAREN; "!" return T_NOT; "=" return T_EQUAL; "!=" return T_UNEQUAL; \"|\' { str = yytext[0]; new_string(); BEGIN(STRING); } \n BEGIN(INITIAL); current_file->lineno++; return T_EOL; --- /* ignore */ ({n}|[-/.])+ { struct kconf_id *id = kconf_id_lookup(yytext, yyleng); if (id && id->flags & TF_PARAM) { zconflval.id = id; return id->token; } alloc_string(yytext, yyleng); zconflval.string = text; return T_WORD; } #.* /* comment */ \\\n current_file->lineno++; . <> { BEGIN(INITIAL); } } { [^'"\\\n]+/\n { append_string(yytext, yyleng); zconflval.string = text; return T_WORD_QUOTE; } [^'"\\\n]+ { append_string(yytext, yyleng); } \\.?/\n { append_string(yytext + 1, yyleng - 1); zconflval.string = text; return T_WORD_QUOTE; } \\.? { append_string(yytext + 1, yyleng - 1); } \'|\" { if (str == yytext[0]) { BEGIN(PARAM); zconflval.string = text; return T_WORD_QUOTE; } else append_string(yytext, 1); } \n { printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); current_file->lineno++; BEGIN(INITIAL); return T_EOL; } <> { BEGIN(INITIAL); } } { [ \t]+ { ts = 0; for (i = 0; i < yyleng; i++) { if (yytext[i] == '\t') ts = (ts & ~7) + 8; else ts++; } last_ts = ts; if (first_ts) { if (ts < first_ts) { zconf_endhelp(); return T_HELPTEXT; } ts -= first_ts; while (ts > 8) { append_string(" ", 8); ts -= 8; } append_string(" ", ts); } } [ \t]*\n/[^ \t\n] { current_file->lineno++; zconf_endhelp(); return T_HELPTEXT; } [ \t]*\n { current_file->lineno++; append_string("\n", 1); } [^ \t\n].* { append_string(yytext, yyleng); if (!first_ts) first_ts = last_ts; } <> { zconf_endhelp(); return T_HELPTEXT; } } <> { if (current_file) { zconf_endfile(); return T_EOL; } fclose(yyin); yyterminate(); } %% void zconf_starthelp(void) { new_string(); last_ts = first_ts = 0; BEGIN(HELP); } static void zconf_endhelp(void) { zconflval.string = text; BEGIN(INITIAL); } /* * Try to open specified file with following names: * ./name * $(srctree)/name * The latter is used when srctree is separate from objtree * when compiling the kernel. * Return NULL if file is not found. */ FILE *zconf_fopen(const char *name) { char *env; FILE *f; f = fopen(name, "r"); if (!f && name[0] != '/') { env = getenv(SRCTREE); if (env) { char *fullname = alloca(strlen(env) + strlen(name) + 2); sprintf(fullname, "%s/%s", env, name); f = fopen(fullname, "r"); } } return f; } void zconf_initscan(const char *name) { yyin = zconf_fopen(name); if (!yyin) { printf("can't find file %s\n", name); exit(1); } current_buf = malloc(sizeof(*current_buf)); memset(current_buf, 0, sizeof(*current_buf)); current_file = file_lookup(name); current_file->lineno = 1; current_file->flags = FILE_BUSY; } void zconf_nextfile(const char *name) { struct file *file = file_lookup(name); struct buffer *buf = malloc(sizeof(*buf)); memset(buf, 0, sizeof(*buf)); current_buf->state = YY_CURRENT_BUFFER; yyin = zconf_fopen(name); if (!yyin) { printf("%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), name); exit(1); } yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); buf->parent = current_buf; current_buf = buf; if (file->flags & FILE_BUSY) { printf("recursive scan (%s)?\n", name); exit(1); } if (file->flags & FILE_SCANNED) { printf("file %s already scanned?\n", name); exit(1); } file->flags |= FILE_BUSY; file->lineno = 1; file->parent = current_file; current_file = file; } static void zconf_endfile(void) { struct buffer *parent; current_file->flags |= FILE_SCANNED; current_file->flags &= ~FILE_BUSY; current_file = current_file->parent; parent = current_buf->parent; if (parent) { fclose(yyin); yy_delete_buffer(YY_CURRENT_BUFFER); yy_switch_to_buffer(parent->state); } free(current_buf); current_buf = parent; } int zconf_lineno(void) { return current_pos.lineno; } char *zconf_curname(void) { return current_pos.file ? current_pos.file->name : ""; } busybox-1.22.1/scripts/kconfig/lkc.h0000644000000000000000000000660312263563520016030 0ustar rootroot/* * Copyright (C) 2002 Roman Zippel * Released under the terms of the GNU GPL v2.0. */ #ifndef LKC_H #define LKC_H #include "expr.h" #ifndef KBUILD_NO_NLS # include #else # define gettext(Msgid) ((const char *) (Msgid)) # define textdomain(Domainname) ((const char *) (Domainname)) # define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname)) #endif #ifdef __cplusplus extern "C" { #endif #ifdef LKC_DIRECT_LINK #define P(name,type,arg) extern type name arg #else #include "lkc_defs.h" #define P(name,type,arg) extern type (*name ## _p) arg #endif #include "lkc_proto.h" #undef P #define SRCTREE "srctree" #define PACKAGE "linux" #define LOCALEDIR "/usr/share/locale" #define _(text) gettext(text) #define N_(text) (text) #define TF_COMMAND 0x0001 #define TF_PARAM 0x0002 struct kconf_id { int name; int token; unsigned int flags; enum symbol_type stype; }; int zconfparse(void); void zconfdump(FILE *out); extern int zconfdebug; void zconf_starthelp(void); FILE *zconf_fopen(const char *name); void zconf_initscan(const char *name); void zconf_nextfile(const char *name); int zconf_lineno(void); char *zconf_curname(void); /* confdata.c */ extern const char conf_def_filename[]; char *conf_get_default_confname(void); /* kconfig_load.c */ void kconfig_load(void); /* menu.c */ void menu_init(void); struct menu *menu_add_menu(void); void menu_end_menu(void); void menu_add_entry(struct symbol *sym); void menu_end_entry(void); void menu_add_dep(struct expr *dep); struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep); struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep); void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep); void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep); void menu_finalize(struct menu *parent); void menu_set_type(int type); /* util.c */ struct file *file_lookup(const char *name); int file_write_dep(const char *name); struct gstr { size_t len; char *s; }; struct gstr str_new(void); struct gstr str_assign(const char *s); void str_free(struct gstr *gs); void str_append(struct gstr *gs, const char *s); void str_printf(struct gstr *gs, const char *fmt, ...); const char *str_get(struct gstr *gs); /* symbol.c */ void sym_init(void); void sym_clear_all_valid(void); void sym_set_changed(struct symbol *sym); struct symbol *sym_check_deps(struct symbol *sym); struct property *prop_alloc(enum prop_type type, struct symbol *sym); struct symbol *prop_get_symbol(struct property *prop); static inline tristate sym_get_tristate_value(struct symbol *sym) { return sym->curr.tri; } static inline struct symbol *sym_get_choice_value(struct symbol *sym) { return (struct symbol *)sym->curr.val; } static inline bool sym_set_choice_value(struct symbol *ch, struct symbol *chval) { return sym_set_tristate_value(chval, yes); } static inline bool sym_is_choice(struct symbol *sym) { return sym->flags & SYMBOL_CHOICE ? true : false; } static inline bool sym_is_choice_value(struct symbol *sym) { return sym->flags & SYMBOL_CHOICEVAL ? true : false; } static inline bool sym_is_optional(struct symbol *sym) { return sym->flags & SYMBOL_OPTIONAL ? true : false; } static inline bool sym_has_value(struct symbol *sym) { return sym->flags & SYMBOL_NEW ? false : true; } #ifdef __cplusplus } #endif #endif /* LKC_H */ busybox-1.22.1/scripts/kconfig/symbol.c0000644000000000000000000004254312263563520016562 0ustar rootroot/* * Copyright (C) 2002 Roman Zippel * Released under the terms of the GNU GPL v2.0. */ #include #include #include #include #include #define LKC_DIRECT_LINK #include "lkc.h" struct symbol symbol_yes = { .name = "y", .curr = { "y", yes }, .flags = SYMBOL_YES|SYMBOL_VALID, }, symbol_mod = { .name = "m", .curr = { "m", mod }, .flags = SYMBOL_MOD|SYMBOL_VALID, }, symbol_no = { .name = "n", .curr = { "n", no }, .flags = SYMBOL_NO|SYMBOL_VALID, }, symbol_empty = { .name = "", .curr = { "", no }, .flags = SYMBOL_VALID, }; int sym_change_count; struct symbol *modules_sym; tristate modules_val; void sym_add_default(struct symbol *sym, const char *def) { struct property *prop = prop_alloc(P_DEFAULT, sym); prop->expr = expr_alloc_symbol(sym_lookup(def, 1)); } void sym_init(void) { struct symbol *sym; struct utsname uts; char *p; static bool inited = false; if (inited) return; inited = true; uname(&uts); sym = sym_lookup("ARCH", 0); sym->type = S_STRING; sym->flags |= SYMBOL_AUTO; p = getenv("ARCH"); if (p) sym_add_default(sym, p); sym = sym_lookup("KERNELVERSION", 0); sym->type = S_STRING; sym->flags |= SYMBOL_AUTO; p = getenv("KERNELVERSION"); if (p) sym_add_default(sym, p); sym = sym_lookup("UNAME_RELEASE", 0); sym->type = S_STRING; sym->flags |= SYMBOL_AUTO; sym_add_default(sym, uts.release); } enum symbol_type sym_get_type(struct symbol *sym) { enum symbol_type type = sym->type; if (type == S_TRISTATE) { if (sym_is_choice_value(sym) && sym->visible == yes) type = S_BOOLEAN; else if (modules_val == no) type = S_BOOLEAN; } return type; } const char *sym_type_name(enum symbol_type type) { switch (type) { case S_BOOLEAN: return "boolean"; case S_TRISTATE: return "tristate"; case S_INT: return "integer"; case S_HEX: return "hex"; case S_STRING: return "string"; case S_UNKNOWN: return "unknown"; case S_OTHER: break; } return "???"; } struct property *sym_get_choice_prop(struct symbol *sym) { struct property *prop; for_all_choices(sym, prop) return prop; return NULL; } struct property *sym_get_default_prop(struct symbol *sym) { struct property *prop; for_all_defaults(sym, prop) { prop->visible.tri = expr_calc_value(prop->visible.expr); if (prop->visible.tri != no) return prop; } return NULL; } struct property *sym_get_range_prop(struct symbol *sym) { struct property *prop; for_all_properties(sym, prop, P_RANGE) { prop->visible.tri = expr_calc_value(prop->visible.expr); if (prop->visible.tri != no) return prop; } return NULL; } static int sym_get_range_val(struct symbol *sym, int base) { sym_calc_value(sym); switch (sym->type) { case S_INT: base = 10; break; case S_HEX: base = 16; break; default: break; } return strtol(sym->curr.val, NULL, base); } static void sym_validate_range(struct symbol *sym) { struct property *prop; int base, val, val2; char str[64]; switch (sym->type) { case S_INT: base = 10; break; case S_HEX: base = 16; break; default: return; } prop = sym_get_range_prop(sym); if (!prop) return; val = strtol(sym->curr.val, NULL, base); val2 = sym_get_range_val(prop->expr->left.sym, base); if (val >= val2) { val2 = sym_get_range_val(prop->expr->right.sym, base); if (val <= val2) return; } if (sym->type == S_INT) sprintf(str, "%d", val2); else sprintf(str, "0x%x", val2); sym->curr.val = strdup(str); } static void sym_calc_visibility(struct symbol *sym) { struct property *prop; tristate tri; /* any prompt visible? */ tri = no; for_all_prompts(sym, prop) { prop->visible.tri = expr_calc_value(prop->visible.expr); tri = E_OR(tri, prop->visible.tri); } if (tri == mod && (sym->type != S_TRISTATE || modules_val == no)) tri = yes; if (sym->visible != tri) { sym->visible = tri; sym_set_changed(sym); } if (sym_is_choice_value(sym)) return; tri = no; if (sym->rev_dep.expr) tri = expr_calc_value(sym->rev_dep.expr); if (tri == mod && sym_get_type(sym) == S_BOOLEAN) tri = yes; if (sym->rev_dep.tri != tri) { sym->rev_dep.tri = tri; sym_set_changed(sym); } } static struct symbol *sym_calc_choice(struct symbol *sym) { struct symbol *def_sym; struct property *prop; struct expr *e; /* is the user choice visible? */ def_sym = sym->user.val; if (def_sym) { sym_calc_visibility(def_sym); if (def_sym->visible != no) return def_sym; } /* any of the defaults visible? */ for_all_defaults(sym, prop) { prop->visible.tri = expr_calc_value(prop->visible.expr); if (prop->visible.tri == no) continue; def_sym = prop_get_symbol(prop); sym_calc_visibility(def_sym); if (def_sym->visible != no) return def_sym; } /* just get the first visible value */ prop = sym_get_choice_prop(sym); for (e = prop->expr; e; e = e->left.expr) { def_sym = e->right.sym; sym_calc_visibility(def_sym); if (def_sym->visible != no) return def_sym; } /* no choice? reset tristate value */ sym->curr.tri = no; return NULL; } void sym_calc_value(struct symbol *sym) { struct symbol_value newval, oldval; struct property *prop; struct expr *e; if (!sym) return; if (sym->flags & SYMBOL_VALID) return; sym->flags |= SYMBOL_VALID; oldval = sym->curr; switch (sym->type) { case S_INT: case S_HEX: case S_STRING: newval = symbol_empty.curr; break; case S_BOOLEAN: case S_TRISTATE: newval = symbol_no.curr; break; default: sym->curr.val = sym->name; sym->curr.tri = no; return; } if (!sym_is_choice_value(sym)) sym->flags &= ~SYMBOL_WRITE; sym_calc_visibility(sym); /* set default if recursively called */ sym->curr = newval; switch (sym_get_type(sym)) { case S_BOOLEAN: case S_TRISTATE: if (sym_is_choice_value(sym) && sym->visible == yes) { prop = sym_get_choice_prop(sym); newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no; } else if (E_OR(sym->visible, sym->rev_dep.tri) != no) { sym->flags |= SYMBOL_WRITE; if (sym_has_value(sym)) newval.tri = sym->user.tri; else if (!sym_is_choice(sym)) { prop = sym_get_default_prop(sym); if (prop) newval.tri = expr_calc_value(prop->expr); } newval.tri = E_OR(E_AND(newval.tri, sym->visible), sym->rev_dep.tri); } else if (!sym_is_choice(sym)) { prop = sym_get_default_prop(sym); if (prop) { sym->flags |= SYMBOL_WRITE; newval.tri = expr_calc_value(prop->expr); } } if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN) newval.tri = yes; break; case S_STRING: case S_HEX: case S_INT: if (sym->visible != no) { sym->flags |= SYMBOL_WRITE; if (sym_has_value(sym)) { newval.val = sym->user.val; break; } } prop = sym_get_default_prop(sym); if (prop) { struct symbol *ds = prop_get_symbol(prop); if (ds) { sym->flags |= SYMBOL_WRITE; sym_calc_value(ds); newval.val = ds->curr.val; } } break; default: ; } sym->curr = newval; if (sym_is_choice(sym) && newval.tri == yes) sym->curr.val = sym_calc_choice(sym); sym_validate_range(sym); if (memcmp(&oldval, &sym->curr, sizeof(oldval))) sym_set_changed(sym); if (modules_sym == sym) modules_val = modules_sym->curr.tri; if (sym_is_choice(sym)) { int flags = sym->flags & (SYMBOL_CHANGED | SYMBOL_WRITE); prop = sym_get_choice_prop(sym); for (e = prop->expr; e; e = e->left.expr) { e->right.sym->flags |= flags; if (flags & SYMBOL_CHANGED) sym_set_changed(e->right.sym); } } } void sym_clear_all_valid(void) { struct symbol *sym; int i; for_all_symbols(i, sym) sym->flags &= ~SYMBOL_VALID; sym_change_count++; if (modules_sym) sym_calc_value(modules_sym); } void sym_set_changed(struct symbol *sym) { struct property *prop; sym->flags |= SYMBOL_CHANGED; for (prop = sym->prop; prop; prop = prop->next) { if (prop->menu) prop->menu->flags |= MENU_CHANGED; } } void sym_set_all_changed(void) { struct symbol *sym; int i; for_all_symbols(i, sym) sym_set_changed(sym); } bool sym_tristate_within_range(struct symbol *sym, tristate val) { int type = sym_get_type(sym); if (sym->visible == no) return false; if (type != S_BOOLEAN && type != S_TRISTATE) return false; if (type == S_BOOLEAN && val == mod) return false; if (sym->visible <= sym->rev_dep.tri) return false; if (sym_is_choice_value(sym) && sym->visible == yes) return val == yes; return val >= sym->rev_dep.tri && val <= sym->visible; } bool sym_set_tristate_value(struct symbol *sym, tristate val) { tristate oldval = sym_get_tristate_value(sym); if (oldval != val && !sym_tristate_within_range(sym, val)) return false; if (sym->flags & SYMBOL_NEW) { sym->flags &= ~SYMBOL_NEW; sym_set_changed(sym); } /* * setting a choice value also resets the new flag of the choice * symbol and all other choice values. */ if (sym_is_choice_value(sym) && val == yes) { struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); struct property *prop; struct expr *e; cs->user.val = sym; cs->flags &= ~SYMBOL_NEW; prop = sym_get_choice_prop(cs); for (e = prop->expr; e; e = e->left.expr) { if (e->right.sym->visible != no) e->right.sym->flags &= ~SYMBOL_NEW; } } sym->user.tri = val; if (oldval != val) { sym_clear_all_valid(); if (sym == modules_sym) sym_set_all_changed(); } return true; } tristate sym_toggle_tristate_value(struct symbol *sym) { tristate oldval, newval; oldval = newval = sym_get_tristate_value(sym); do { switch (newval) { case no: newval = mod; break; case mod: newval = yes; break; case yes: newval = no; break; } if (sym_set_tristate_value(sym, newval)) break; } while (oldval != newval); return newval; } bool sym_string_valid(struct symbol *sym, const char *str) { signed char ch; switch (sym->type) { case S_STRING: return true; case S_INT: ch = *str++; if (ch == '-') ch = *str++; if (!isdigit(ch)) return false; if (ch == '0' && *str != 0) return false; while ((ch = *str++)) { if (!isdigit(ch)) return false; } return true; case S_HEX: if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) str += 2; ch = *str++; do { if (!isxdigit(ch)) return false; } while ((ch = *str++)); return true; case S_BOOLEAN: case S_TRISTATE: switch (str[0]) { case 'y': case 'Y': case 'm': case 'M': case 'n': case 'N': return true; } return false; default: return false; } } bool sym_string_within_range(struct symbol *sym, const char *str) { struct property *prop; int val; switch (sym->type) { case S_STRING: return sym_string_valid(sym, str); case S_INT: if (!sym_string_valid(sym, str)) return false; prop = sym_get_range_prop(sym); if (!prop) return true; val = strtol(str, NULL, 10); return val >= sym_get_range_val(prop->expr->left.sym, 10) && val <= sym_get_range_val(prop->expr->right.sym, 10); case S_HEX: if (!sym_string_valid(sym, str)) return false; prop = sym_get_range_prop(sym); if (!prop) return true; val = strtol(str, NULL, 16); return val >= sym_get_range_val(prop->expr->left.sym, 16) && val <= sym_get_range_val(prop->expr->right.sym, 16); case S_BOOLEAN: case S_TRISTATE: switch (str[0]) { case 'y': case 'Y': return sym_tristate_within_range(sym, yes); case 'm': case 'M': return sym_tristate_within_range(sym, mod); case 'n': case 'N': return sym_tristate_within_range(sym, no); } return false; default: return false; } } bool sym_set_string_value(struct symbol *sym, const char *newval) { const char *oldval; char *val; int size; switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: switch (newval[0]) { case 'y': case 'Y': return sym_set_tristate_value(sym, yes); case 'm': case 'M': return sym_set_tristate_value(sym, mod); case 'n': case 'N': return sym_set_tristate_value(sym, no); } return false; default: ; } if (!sym_string_within_range(sym, newval)) return false; if (sym->flags & SYMBOL_NEW) { sym->flags &= ~SYMBOL_NEW; sym_set_changed(sym); } oldval = sym->user.val; size = strlen(newval) + 1; if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) { size += 2; sym->user.val = val = malloc(size); *val++ = '0'; *val++ = 'x'; } else if (!oldval || strcmp(oldval, newval)) sym->user.val = val = malloc(size); else return true; strcpy(val, newval); free((void *)oldval); sym_clear_all_valid(); return true; } const char *sym_get_string_value(struct symbol *sym) { tristate val; switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: val = sym_get_tristate_value(sym); switch (val) { case no: return "n"; case mod: return "m"; case yes: return "y"; } break; default: ; } return (const char *)sym->curr.val; } bool sym_is_changable(struct symbol *sym) { return sym->visible > sym->rev_dep.tri; } struct symbol *sym_lookup(const char *name, int isconst) { struct symbol *symbol; const char *ptr; char *new_name; int hash = 0; if (name) { if (name[0] && !name[1]) { switch (name[0]) { case 'y': return &symbol_yes; case 'm': return &symbol_mod; case 'n': return &symbol_no; } } for (ptr = name; *ptr; ptr++) hash += *ptr; hash &= 0xff; for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { if (!strcmp(symbol->name, name)) { if ((isconst && symbol->flags & SYMBOL_CONST) || (!isconst && !(symbol->flags & SYMBOL_CONST))) return symbol; } } new_name = strdup(name); } else { new_name = NULL; hash = 256; } symbol = malloc(sizeof(*symbol)); memset(symbol, 0, sizeof(*symbol)); symbol->name = new_name; symbol->type = S_UNKNOWN; symbol->flags = SYMBOL_NEW; if (isconst) symbol->flags |= SYMBOL_CONST; symbol->next = symbol_hash[hash]; symbol_hash[hash] = symbol; return symbol; } struct symbol *sym_find(const char *name) { struct symbol *symbol = NULL; const char *ptr; int hash = 0; if (!name) return NULL; if (name[0] && !name[1]) { switch (name[0]) { case 'y': return &symbol_yes; case 'm': return &symbol_mod; case 'n': return &symbol_no; } } for (ptr = name; *ptr; ptr++) hash += *ptr; hash &= 0xff; for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { if (!strcmp(symbol->name, name) && !(symbol->flags & SYMBOL_CONST)) break; } return symbol; } struct symbol **sym_re_search(const char *pattern) { struct symbol *sym, **sym_arr = NULL; int i, cnt, size; regex_t re; cnt = size = 0; /* Skip if empty */ if (strlen(pattern) == 0) return NULL; if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB|REG_ICASE)) return NULL; for_all_symbols(i, sym) { if (sym->flags & SYMBOL_CONST || !sym->name) continue; if (regexec(&re, sym->name, 0, NULL, 0)) continue; if (cnt + 1 >= size) { void *tmp = sym_arr; size += 16; sym_arr = realloc(sym_arr, size * sizeof(struct symbol *)); if (!sym_arr) { free(tmp); return NULL; } } sym_arr[cnt++] = sym; } if (sym_arr) sym_arr[cnt] = NULL; regfree(&re); return sym_arr; } struct symbol *sym_check_deps(struct symbol *sym); static struct symbol *sym_check_expr_deps(struct expr *e) { struct symbol *sym; if (!e) return NULL; switch (e->type) { case E_OR: case E_AND: sym = sym_check_expr_deps(e->left.expr); if (sym) return sym; return sym_check_expr_deps(e->right.expr); case E_NOT: return sym_check_expr_deps(e->left.expr); case E_EQUAL: case E_UNEQUAL: sym = sym_check_deps(e->left.sym); if (sym) return sym; return sym_check_deps(e->right.sym); case E_SYMBOL: return sym_check_deps(e->left.sym); default: break; } printf("Oops! How to check %d?\n", e->type); return NULL; } struct symbol *sym_check_deps(struct symbol *sym) { struct symbol *sym2; struct property *prop; if (sym->flags & SYMBOL_CHECK) { printf("Warning! Found recursive dependency: %s", sym->name); return sym; } if (sym->flags & SYMBOL_CHECKED) return NULL; sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); sym2 = sym_check_expr_deps(sym->rev_dep.expr); if (sym2) goto out; for (prop = sym->prop; prop; prop = prop->next) { if (prop->type == P_CHOICE || prop->type == P_SELECT) continue; sym2 = sym_check_expr_deps(prop->visible.expr); if (sym2) goto out; if (prop->type != P_DEFAULT || sym_is_choice(sym)) continue; sym2 = sym_check_expr_deps(prop->expr); if (sym2) goto out; } out: if (sym2) { printf(" %s", sym->name); if (sym2 == sym) { printf("\n"); sym2 = NULL; } } sym->flags &= ~SYMBOL_CHECK; return sym2; } struct property *prop_alloc(enum prop_type type, struct symbol *sym) { struct property *prop; struct property **propp; prop = malloc(sizeof(*prop)); memset(prop, 0, sizeof(*prop)); prop->type = type; prop->sym = sym; prop->file = current_file; prop->lineno = zconf_lineno(); /* append property to the prop list of symbol */ if (sym) { for (propp = &sym->prop; *propp; propp = &(*propp)->next) ; *propp = prop; } return prop; } struct symbol *prop_get_symbol(struct property *prop) { if (prop->expr && (prop->expr->type == E_SYMBOL || prop->expr->type == E_CHOICE)) return prop->expr->left.sym; return NULL; } const char *prop_get_type_name(enum prop_type type) { switch (type) { case P_PROMPT: return "prompt"; case P_COMMENT: return "comment"; case P_MENU: return "menu"; case P_DEFAULT: return "default"; case P_CHOICE: return "choice"; case P_SELECT: return "select"; case P_RANGE: return "range"; case P_UNKNOWN: break; } return "unknown"; } busybox-1.22.1/scripts/kconfig/gconf.glade0000644000000000000000000006077612263563520017213 0ustar rootroot True Gtk Kernel Configurator GTK_WINDOW_TOPLEVEL GTK_WIN_POS_NONE False 640 480 True False True False False GDK_WINDOW_TYPE_HINT_NORMAL GDK_GRAVITY_NORTH_WEST True False 0 True True _File True True Load a config file _Load True True gtk-open 1 0.5 0.5 0 0 True Save the config in .config _Save True True gtk-save 1 0.5 0.5 0 0 True Save the config in a file Save _as True True gtk-save-as 1 0.5 0.5 0 0 True True _Quit True True gtk-quit 1 0.5 0.5 0 0 True _Options True True Show name Show _name True False True Show range (Y/M/N) Show _range True False True Show value of the option Show _data True False True True Show all options Show all _options True False True Show masked options Show _debug info True False True _Help True True _Introduction True True gtk-dialog-question 1 0.5 0.5 0 0 True _About True True gtk-properties 1 0.5 0.5 0 0 True _License True True gtk-justify-fill 1 0.5 0.5 0 0 0 False False True GTK_SHADOW_OUT GTK_POS_LEFT GTK_POS_TOP True GTK_ORIENTATION_HORIZONTAL GTK_TOOLBAR_BOTH True True True Goes up of one level (single view) Back True gtk-undo True True False False True True True True False True False False True Load a config file Load True gtk-open True True False False True True Save a config file Save True gtk-save True True False False True True True True False True False False True Single view Single True gtk-missing-image True True False False True True Split view Split True gtk-missing-image True True False False True True Full view Full True gtk-missing-image True True False False True True True True False True False False True Collapse the whole tree in the right frame Collapse True gtk-remove True True False False True True Expand the whole tree in the right frame Expand True gtk-add True True False False True 0 False False 1 True True 0 True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_IN GTK_CORNER_TOP_LEFT True True True False False True True False True True 0 True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_IN GTK_CORNER_TOP_LEFT True True True True False False True True False True GTK_POLICY_NEVER GTK_POLICY_AUTOMATIC GTK_SHADOW_IN GTK_CORNER_TOP_LEFT True True False False True GTK_JUSTIFY_LEFT GTK_WRAP_WORD True 0 0 0 0 0 0 Sorry, no help available for this option yet. True True True True 0 True True busybox-1.22.1/scripts/kconfig/qconf.cc0000644000000000000000000010324112263563520016517 0ustar rootroot/* * Copyright (C) 2002 Roman Zippel * Released under the terms of the GNU GPL v2.0. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lkc.h" #include "qconf.h" #include "qconf.moc" #include "images.c" #ifdef _ # undef _ # define _ qgettext #endif static QApplication *configApp; static inline QString qgettext(const char* str) { return QString::fromLocal8Bit(gettext(str)); } static inline QString qgettext(const QString& str) { return QString::fromLocal8Bit(gettext(str.latin1())); } ConfigSettings::ConfigSettings() : showAll(false), showName(false), showRange(false), showData(false) { } #if QT_VERSION >= 300 /** * Reads the list column settings from the application settings. */ void ConfigSettings::readListSettings() { showAll = readBoolEntry("/kconfig/qconf/showAll", false); showName = readBoolEntry("/kconfig/qconf/showName", false); showRange = readBoolEntry("/kconfig/qconf/showRange", false); showData = readBoolEntry("/kconfig/qconf/showData", false); } /** * Reads a list of integer values from the application settings. */ QValueList ConfigSettings::readSizes(const QString& key, bool *ok) { QValueList result; QStringList entryList = readListEntry(key, ok); if (ok) { QStringList::Iterator it; for (it = entryList.begin(); it != entryList.end(); ++it) result.push_back((*it).toInt()); } return result; } /** * Writes a list of integer values to the application settings. */ bool ConfigSettings::writeSizes(const QString& key, const QValueList& value) { QStringList stringList; QValueList::ConstIterator it; for (it = value.begin(); it != value.end(); ++it) stringList.push_back(QString::number(*it)); return writeEntry(key, stringList); } #endif /* * update all the children of a menu entry * removes/adds the entries from the parent widget as necessary * * parent: either the menu list widget or a menu entry widget * menu: entry to be updated */ template void ConfigList::updateMenuList(P* parent, struct menu* menu) { struct menu* child; ConfigItem* item; ConfigItem* last; bool visible; enum prop_type type; if (!menu) { while ((item = parent->firstChild())) delete item; return; } last = parent->firstChild(); if (last && !last->goParent) last = 0; for (child = menu->list; child; child = child->next) { item = last ? last->nextSibling() : parent->firstChild(); type = child->prompt ? child->prompt->type : P_UNKNOWN; switch (mode) { case menuMode: if (!(child->flags & MENU_ROOT)) goto hide; break; case symbolMode: if (child->flags & MENU_ROOT) goto hide; break; default: break; } visible = menu_is_visible(child); if (showAll || visible) { if (!item || item->menu != child) item = new ConfigItem(parent, last, child, visible); else item->testUpdateMenu(visible); if (mode == fullMode || mode == menuMode || type != P_MENU) updateMenuList(item, child); else updateMenuList(item, 0); last = item; continue; } hide: if (item && item->menu == child) { last = parent->firstChild(); if (last == item) last = 0; else while (last->nextSibling() != item) last = last->nextSibling(); delete item; } } } #if QT_VERSION >= 300 /* * set the new data * TODO check the value */ void ConfigItem::okRename(int col) { Parent::okRename(col); sym_set_string_value(menu->sym, text(dataColIdx).latin1()); } #endif /* * update the displayed of a menu entry */ void ConfigItem::updateMenu(void) { ConfigList* list; struct symbol* sym; struct property *prop; QString prompt; int type; tristate expr; list = listView(); if (goParent) { setPixmap(promptColIdx, list->menuBackPix); prompt = ".."; goto set_prompt; } sym = menu->sym; prop = menu->prompt; prompt = QString::fromLocal8Bit(menu_get_prompt(menu)); if (prop) switch (prop->type) { case P_MENU: if (list->mode == singleMode || list->mode == symbolMode) { /* a menuconfig entry is displayed differently * depending whether it's at the view root or a child. */ if (sym && list->rootEntry == menu) break; setPixmap(promptColIdx, list->menuPix); } else { if (sym) break; setPixmap(promptColIdx, 0); } goto set_prompt; case P_COMMENT: setPixmap(promptColIdx, 0); goto set_prompt; default: ; } if (!sym) goto set_prompt; setText(nameColIdx, QString::fromLocal8Bit(sym->name)); type = sym_get_type(sym); switch (type) { case S_BOOLEAN: case S_TRISTATE: char ch; if (!sym_is_changable(sym) && !list->showAll) { setPixmap(promptColIdx, 0); setText(noColIdx, QString::null); setText(modColIdx, QString::null); setText(yesColIdx, QString::null); break; } expr = sym_get_tristate_value(sym); switch (expr) { case yes: if (sym_is_choice_value(sym) && type == S_BOOLEAN) setPixmap(promptColIdx, list->choiceYesPix); else setPixmap(promptColIdx, list->symbolYesPix); setText(yesColIdx, "Y"); ch = 'Y'; break; case mod: setPixmap(promptColIdx, list->symbolModPix); setText(modColIdx, "M"); ch = 'M'; break; default: if (sym_is_choice_value(sym) && type == S_BOOLEAN) setPixmap(promptColIdx, list->choiceNoPix); else setPixmap(promptColIdx, list->symbolNoPix); setText(noColIdx, "N"); ch = 'N'; break; } if (expr != no) setText(noColIdx, sym_tristate_within_range(sym, no) ? "_" : 0); if (expr != mod) setText(modColIdx, sym_tristate_within_range(sym, mod) ? "_" : 0); if (expr != yes) setText(yesColIdx, sym_tristate_within_range(sym, yes) ? "_" : 0); setText(dataColIdx, QChar(ch)); break; case S_INT: case S_HEX: case S_STRING: const char* data; data = sym_get_string_value(sym); #if QT_VERSION >= 300 int i = list->mapIdx(dataColIdx); if (i >= 0) setRenameEnabled(i, TRUE); #endif setText(dataColIdx, data); if (type == S_STRING) prompt = QString("%1: %2").arg(prompt).arg(data); else prompt = QString("(%2) %1").arg(prompt).arg(data); break; } if (!sym_has_value(sym) && visible) prompt += " (NEW)"; set_prompt: setText(promptColIdx, prompt); } void ConfigItem::testUpdateMenu(bool v) { ConfigItem* i; visible = v; if (!menu) return; sym_calc_value(menu->sym); if (menu->flags & MENU_CHANGED) { /* the menu entry changed, so update all list items */ menu->flags &= ~MENU_CHANGED; for (i = (ConfigItem*)menu->data; i; i = i->nextItem) i->updateMenu(); } else if (listView()->updateAll) updateMenu(); } void ConfigItem::paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align) { ConfigList* list = listView(); if (visible) { if (isSelected() && !list->hasFocus() && list->mode == menuMode) Parent::paintCell(p, list->inactivedColorGroup, column, width, align); else Parent::paintCell(p, cg, column, width, align); } else Parent::paintCell(p, list->disabledColorGroup, column, width, align); } /* * construct a menu entry */ void ConfigItem::init(void) { if (menu) { ConfigList* list = listView(); nextItem = (ConfigItem*)menu->data; menu->data = this; if (list->mode != fullMode) setOpen(TRUE); sym_calc_value(menu->sym); } updateMenu(); } /* * destruct a menu entry */ ConfigItem::~ConfigItem(void) { if (menu) { ConfigItem** ip = (ConfigItem**)&menu->data; for (; *ip; ip = &(*ip)->nextItem) { if (*ip == this) { *ip = nextItem; break; } } } } void ConfigLineEdit::show(ConfigItem* i) { item = i; if (sym_get_string_value(item->menu->sym)) setText(QString::fromLocal8Bit(sym_get_string_value(item->menu->sym))); else setText(QString::null); Parent::show(); setFocus(); } void ConfigLineEdit::keyPressEvent(QKeyEvent* e) { switch (e->key()) { case Key_Escape: break; case Key_Return: case Key_Enter: sym_set_string_value(item->menu->sym, text().latin1()); parent()->updateList(item); break; default: Parent::keyPressEvent(e); return; } e->accept(); parent()->list->setFocus(); hide(); } ConfigList::ConfigList(ConfigView* p, ConfigMainWindow* cv, ConfigSettings* configSettings) : Parent(p), cview(cv), updateAll(false), symbolYesPix(xpm_symbol_yes), symbolModPix(xpm_symbol_mod), symbolNoPix(xpm_symbol_no), choiceYesPix(xpm_choice_yes), choiceNoPix(xpm_choice_no), menuPix(xpm_menu), menuInvPix(xpm_menu_inv), menuBackPix(xpm_menuback), voidPix(xpm_void), showAll(false), showName(false), showRange(false), showData(false), rootEntry(0) { int i; setSorting(-1); setRootIsDecorated(TRUE); disabledColorGroup = palette().active(); disabledColorGroup.setColor(QColorGroup::Text, palette().disabled().text()); inactivedColorGroup = palette().active(); inactivedColorGroup.setColor(QColorGroup::Highlight, palette().disabled().highlight()); connect(this, SIGNAL(selectionChanged(void)), SLOT(updateSelection(void))); if (configSettings) { showAll = configSettings->showAll; showName = configSettings->showName; showRange = configSettings->showRange; showData = configSettings->showData; } for (i = 0; i < colNr; i++) colMap[i] = colRevMap[i] = -1; addColumn(promptColIdx, "Option"); reinit(); } void ConfigList::reinit(void) { removeColumn(dataColIdx); removeColumn(yesColIdx); removeColumn(modColIdx); removeColumn(noColIdx); removeColumn(nameColIdx); if (showName) addColumn(nameColIdx, "Name"); if (showRange) { addColumn(noColIdx, "N"); addColumn(modColIdx, "M"); addColumn(yesColIdx, "Y"); } if (showData) addColumn(dataColIdx, "Value"); updateListAll(); } void ConfigList::updateSelection(void) { struct menu *menu; enum prop_type type; ConfigItem* item = (ConfigItem*)selectedItem(); if (!item) return; cview->setHelp(item); menu = item->menu; if (!menu) return; type = menu->prompt ? menu->prompt->type : P_UNKNOWN; if (mode == menuMode && type == P_MENU) emit menuSelected(menu); } void ConfigList::updateList(ConfigItem* item) { ConfigItem* last = 0; if (!rootEntry) goto update; if (rootEntry != &rootmenu && (mode == singleMode || (mode == symbolMode && rootEntry->parent != &rootmenu))) { item = firstChild(); if (!item) item = new ConfigItem(this, 0, true); last = item; } if ((mode == singleMode || (mode == symbolMode && !(rootEntry->flags & MENU_ROOT))) && rootEntry->sym && rootEntry->prompt) { item = last ? last->nextSibling() : firstChild(); if (!item) item = new ConfigItem(this, last, rootEntry, true); else item->testUpdateMenu(true); updateMenuList(item, rootEntry); triggerUpdate(); return; } update: updateMenuList(this, rootEntry); triggerUpdate(); } void ConfigList::setAllOpen(bool open) { QListViewItemIterator it(this); for (; it.current(); it++) it.current()->setOpen(open); } void ConfigList::setValue(ConfigItem* item, tristate val) { struct symbol* sym; int type; tristate oldval; sym = item->menu ? item->menu->sym : 0; if (!sym) return; type = sym_get_type(sym); switch (type) { case S_BOOLEAN: case S_TRISTATE: oldval = sym_get_tristate_value(sym); if (!sym_set_tristate_value(sym, val)) return; if (oldval == no && item->menu->list) item->setOpen(TRUE); parent()->updateList(item); break; } } void ConfigList::changeValue(ConfigItem* item) { struct symbol* sym; struct menu* menu; int type, oldexpr, newexpr; menu = item->menu; if (!menu) return; sym = menu->sym; if (!sym) { if (item->menu->list) item->setOpen(!item->isOpen()); return; } type = sym_get_type(sym); switch (type) { case S_BOOLEAN: case S_TRISTATE: oldexpr = sym_get_tristate_value(sym); newexpr = sym_toggle_tristate_value(sym); if (item->menu->list) { if (oldexpr == newexpr) item->setOpen(!item->isOpen()); else if (oldexpr == no) item->setOpen(TRUE); } if (oldexpr != newexpr) parent()->updateList(item); break; case S_INT: case S_HEX: case S_STRING: #if QT_VERSION >= 300 if (colMap[dataColIdx] >= 0) item->startRename(colMap[dataColIdx]); else #endif parent()->lineEdit->show(item); break; } } void ConfigList::setRootMenu(struct menu *menu) { enum prop_type type; if (rootEntry == menu) return; type = menu && menu->prompt ? menu->prompt->type : P_UNKNOWN; if (type != P_MENU) return; updateMenuList(this, 0); rootEntry = menu; updateListAll(); setSelected(currentItem(), hasFocus()); } void ConfigList::setParentMenu(void) { ConfigItem* item; struct menu *oldroot; oldroot = rootEntry; if (rootEntry == &rootmenu) return; setRootMenu(menu_get_parent_menu(rootEntry->parent)); QListViewItemIterator it(this); for (; (item = (ConfigItem*)it.current()); it++) { if (item->menu == oldroot) { setCurrentItem(item); ensureItemVisible(item); break; } } } void ConfigList::keyPressEvent(QKeyEvent* ev) { QListViewItem* i = currentItem(); ConfigItem* item; struct menu *menu; enum prop_type type; if (ev->key() == Key_Escape && mode != fullMode) { emit parentSelected(); ev->accept(); return; } if (!i) { Parent::keyPressEvent(ev); return; } item = (ConfigItem*)i; switch (ev->key()) { case Key_Return: case Key_Enter: if (item->goParent) { emit parentSelected(); break; } menu = item->menu; if (!menu) break; type = menu->prompt ? menu->prompt->type : P_UNKNOWN; if (type == P_MENU && rootEntry != menu && mode != fullMode && mode != menuMode) { emit menuSelected(menu); break; } case Key_Space: changeValue(item); break; case Key_N: setValue(item, no); break; case Key_M: setValue(item, mod); break; case Key_Y: setValue(item, yes); break; default: Parent::keyPressEvent(ev); return; } ev->accept(); } void ConfigList::contentsMousePressEvent(QMouseEvent* e) { //QPoint p(contentsToViewport(e->pos())); //printf("contentsMousePressEvent: %d,%d\n", p.x(), p.y()); Parent::contentsMousePressEvent(e); } void ConfigList::contentsMouseReleaseEvent(QMouseEvent* e) { QPoint p(contentsToViewport(e->pos())); ConfigItem* item = (ConfigItem*)itemAt(p); struct menu *menu; enum prop_type ptype; const QPixmap* pm; int idx, x; if (!item) goto skip; menu = item->menu; x = header()->offset() + p.x(); idx = colRevMap[header()->sectionAt(x)]; switch (idx) { case promptColIdx: pm = item->pixmap(promptColIdx); if (pm) { int off = header()->sectionPos(0) + itemMargin() + treeStepSize() * (item->depth() + (rootIsDecorated() ? 1 : 0)); if (x >= off && x < off + pm->width()) { if (item->goParent) { emit parentSelected(); break; } else if (!menu) break; ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; if (ptype == P_MENU && rootEntry != menu && mode != fullMode && mode != menuMode) emit menuSelected(menu); else changeValue(item); } } break; case noColIdx: setValue(item, no); break; case modColIdx: setValue(item, mod); break; case yesColIdx: setValue(item, yes); break; case dataColIdx: changeValue(item); break; } skip: //printf("contentsMouseReleaseEvent: %d,%d\n", p.x(), p.y()); Parent::contentsMouseReleaseEvent(e); } void ConfigList::contentsMouseMoveEvent(QMouseEvent* e) { //QPoint p(contentsToViewport(e->pos())); //printf("contentsMouseMoveEvent: %d,%d\n", p.x(), p.y()); Parent::contentsMouseMoveEvent(e); } void ConfigList::contentsMouseDoubleClickEvent(QMouseEvent* e) { QPoint p(contentsToViewport(e->pos())); ConfigItem* item = (ConfigItem*)itemAt(p); struct menu *menu; enum prop_type ptype; if (!item) goto skip; if (item->goParent) { emit parentSelected(); goto skip; } menu = item->menu; if (!menu) goto skip; ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; if (ptype == P_MENU && (mode == singleMode || mode == symbolMode)) emit menuSelected(menu); else if (menu->sym) changeValue(item); skip: //printf("contentsMouseDoubleClickEvent: %d,%d\n", p.x(), p.y()); Parent::contentsMouseDoubleClickEvent(e); } void ConfigList::focusInEvent(QFocusEvent *e) { Parent::focusInEvent(e); QListViewItem* item = currentItem(); if (!item) return; setSelected(item, TRUE); emit gotFocus(); } ConfigView* ConfigView::viewList; ConfigView::ConfigView(QWidget* parent, ConfigMainWindow* cview, ConfigSettings *configSettings) : Parent(parent) { list = new ConfigList(this, cview, configSettings); lineEdit = new ConfigLineEdit(this); lineEdit->hide(); this->nextView = viewList; viewList = this; } ConfigView::~ConfigView(void) { ConfigView** vp; for (vp = &viewList; *vp; vp = &(*vp)->nextView) { if (*vp == this) { *vp = nextView; break; } } } void ConfigView::updateList(ConfigItem* item) { ConfigView* v; for (v = viewList; v; v = v->nextView) v->list->updateList(item); } void ConfigView::updateListAll(void) { ConfigView* v; for (v = viewList; v; v = v->nextView) v->list->updateListAll(); } /* * Construct the complete config widget */ ConfigMainWindow::ConfigMainWindow(void) { QMenuBar* menu; bool ok; int x, y, width, height; QWidget *d = configApp->desktop(); ConfigSettings* configSettings = new ConfigSettings(); #if QT_VERSION >= 300 width = configSettings->readNumEntry("/kconfig/qconf/window width", d->width() - 64); height = configSettings->readNumEntry("/kconfig/qconf/window height", d->height() - 64); resize(width, height); x = configSettings->readNumEntry("/kconfig/qconf/window x", 0, &ok); if (ok) y = configSettings->readNumEntry("/kconfig/qconf/window y", 0, &ok); if (ok) move(x, y); showDebug = configSettings->readBoolEntry("/kconfig/qconf/showDebug", false); // read list settings into configSettings, will be used later for ConfigList setup configSettings->readListSettings(); #else width = d->width() - 64; height = d->height() - 64; resize(width, height); showDebug = false; #endif split1 = new QSplitter(this); split1->setOrientation(QSplitter::Horizontal); setCentralWidget(split1); menuView = new ConfigView(split1, this, configSettings); menuList = menuView->list; split2 = new QSplitter(split1); split2->setOrientation(QSplitter::Vertical); // create config tree configView = new ConfigView(split2, this, configSettings); configList = configView->list; helpText = new QTextView(split2); helpText->setTextFormat(Qt::RichText); setTabOrder(configList, helpText); configList->setFocus(); menu = menuBar(); toolBar = new QToolBar("Tools", this); backAction = new QAction("Back", QPixmap(xpm_back), "Back", 0, this); connect(backAction, SIGNAL(activated()), SLOT(goBack())); backAction->setEnabled(FALSE); QAction *quitAction = new QAction("Quit", "&Quit", CTRL+Key_Q, this); connect(quitAction, SIGNAL(activated()), SLOT(close())); QAction *loadAction = new QAction("Load", QPixmap(xpm_load), "&Load", CTRL+Key_L, this); connect(loadAction, SIGNAL(activated()), SLOT(loadConfig())); QAction *saveAction = new QAction("Save", QPixmap(xpm_save), "&Save", CTRL+Key_S, this); connect(saveAction, SIGNAL(activated()), SLOT(saveConfig())); QAction *saveAsAction = new QAction("Save As...", "Save &As...", 0, this); connect(saveAsAction, SIGNAL(activated()), SLOT(saveConfigAs())); QAction *singleViewAction = new QAction("Single View", QPixmap(xpm_single_view), "Split View", 0, this); connect(singleViewAction, SIGNAL(activated()), SLOT(showSingleView())); QAction *splitViewAction = new QAction("Split View", QPixmap(xpm_split_view), "Split View", 0, this); connect(splitViewAction, SIGNAL(activated()), SLOT(showSplitView())); QAction *fullViewAction = new QAction("Full View", QPixmap(xpm_tree_view), "Full View", 0, this); connect(fullViewAction, SIGNAL(activated()), SLOT(showFullView())); QAction *showNameAction = new QAction(NULL, "Show Name", 0, this); showNameAction->setToggleAction(TRUE); showNameAction->setOn(configList->showName); connect(showNameAction, SIGNAL(toggled(bool)), SLOT(setShowName(bool))); QAction *showRangeAction = new QAction(NULL, "Show Range", 0, this); showRangeAction->setToggleAction(TRUE); showRangeAction->setOn(configList->showRange); connect(showRangeAction, SIGNAL(toggled(bool)), SLOT(setShowRange(bool))); QAction *showDataAction = new QAction(NULL, "Show Data", 0, this); showDataAction->setToggleAction(TRUE); showDataAction->setOn(configList->showData); connect(showDataAction, SIGNAL(toggled(bool)), SLOT(setShowData(bool))); QAction *showAllAction = new QAction(NULL, "Show All Options", 0, this); showAllAction->setToggleAction(TRUE); showAllAction->setOn(configList->showAll); connect(showAllAction, SIGNAL(toggled(bool)), SLOT(setShowAll(bool))); QAction *showDebugAction = new QAction(NULL, "Show Debug Info", 0, this); showDebugAction->setToggleAction(TRUE); showDebugAction->setOn(showDebug); connect(showDebugAction, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool))); QAction *showIntroAction = new QAction(NULL, "Introduction", 0, this); connect(showIntroAction, SIGNAL(activated()), SLOT(showIntro())); QAction *showAboutAction = new QAction(NULL, "About", 0, this); connect(showAboutAction, SIGNAL(activated()), SLOT(showAbout())); // init tool bar backAction->addTo(toolBar); toolBar->addSeparator(); loadAction->addTo(toolBar); saveAction->addTo(toolBar); toolBar->addSeparator(); singleViewAction->addTo(toolBar); splitViewAction->addTo(toolBar); fullViewAction->addTo(toolBar); // create config menu QPopupMenu* config = new QPopupMenu(this); menu->insertItem("&File", config); loadAction->addTo(config); saveAction->addTo(config); saveAsAction->addTo(config); config->insertSeparator(); quitAction->addTo(config); // create options menu QPopupMenu* optionMenu = new QPopupMenu(this); menu->insertItem("&Option", optionMenu); showNameAction->addTo(optionMenu); showRangeAction->addTo(optionMenu); showDataAction->addTo(optionMenu); optionMenu->insertSeparator(); showAllAction->addTo(optionMenu); showDebugAction->addTo(optionMenu); // create help menu QPopupMenu* helpMenu = new QPopupMenu(this); menu->insertSeparator(); menu->insertItem("&Help", helpMenu); showIntroAction->addTo(helpMenu); showAboutAction->addTo(helpMenu); connect(configList, SIGNAL(menuSelected(struct menu *)), SLOT(changeMenu(struct menu *))); connect(configList, SIGNAL(parentSelected()), SLOT(goBack())); connect(menuList, SIGNAL(menuSelected(struct menu *)), SLOT(changeMenu(struct menu *))); connect(configList, SIGNAL(gotFocus(void)), SLOT(listFocusChanged(void))); connect(menuList, SIGNAL(gotFocus(void)), SLOT(listFocusChanged(void))); #if QT_VERSION >= 300 QString listMode = configSettings->readEntry("/kconfig/qconf/listMode", "symbol"); if (listMode == "single") showSingleView(); else if (listMode == "full") showFullView(); else /*if (listMode == "split")*/ showSplitView(); // UI setup done, restore splitter positions QValueList sizes = configSettings->readSizes("/kconfig/qconf/split1", &ok); if (ok) split1->setSizes(sizes); sizes = configSettings->readSizes("/kconfig/qconf/split2", &ok); if (ok) split2->setSizes(sizes); #else showSplitView(); #endif delete configSettings; } static QString print_filter(const QString &str) { QRegExp re("[<>&\"\\n]"); QString res = str; for (int i = 0; (i = res.find(re, i)) >= 0;) { switch (res[i].latin1()) { case '<': res.replace(i, 1, "<"); i += 4; break; case '>': res.replace(i, 1, ">"); i += 4; break; case '&': res.replace(i, 1, "&"); i += 5; break; case '"': res.replace(i, 1, """); i += 6; break; case '\n': res.replace(i, 1, "
"); i += 4; break; } } return res; } static void expr_print_help(void *data, const char *str) { reinterpret_cast(data)->append(print_filter(str)); } /* * display a new help entry as soon as a new menu entry is selected */ void ConfigMainWindow::setHelp(QListViewItem* item) { struct symbol* sym; struct menu* menu = 0; configList->parent()->lineEdit->hide(); if (item) menu = ((ConfigItem*)item)->menu; if (!menu) { helpText->setText(QString::null); return; } QString head, debug, help; menu = ((ConfigItem*)item)->menu; sym = menu->sym; if (sym) { if (menu->prompt) { head += ""; head += print_filter(_(menu->prompt->text)); head += ""; if (sym->name) { head += " ("; head += print_filter(_(sym->name)); head += ")"; } } else if (sym->name) { head += ""; head += print_filter(_(sym->name)); head += ""; } head += "

"; if (showDebug) { debug += "type: "; debug += print_filter(sym_type_name(sym->type)); if (sym_is_choice(sym)) debug += " (choice)"; debug += "
"; if (sym->rev_dep.expr) { debug += "reverse dep: "; expr_print(sym->rev_dep.expr, expr_print_help, &debug, E_NONE); debug += "
"; } for (struct property *prop = sym->prop; prop; prop = prop->next) { switch (prop->type) { case P_PROMPT: case P_MENU: debug += "prompt: "; debug += print_filter(_(prop->text)); debug += "
"; break; case P_DEFAULT: debug += "default: "; expr_print(prop->expr, expr_print_help, &debug, E_NONE); debug += "
"; break; case P_CHOICE: if (sym_is_choice(sym)) { debug += "choice: "; expr_print(prop->expr, expr_print_help, &debug, E_NONE); debug += "
"; } break; case P_SELECT: debug += "select: "; expr_print(prop->expr, expr_print_help, &debug, E_NONE); debug += "
"; break; case P_RANGE: debug += "range: "; expr_print(prop->expr, expr_print_help, &debug, E_NONE); debug += "
"; break; default: debug += "unknown property: "; debug += prop_get_type_name(prop->type); debug += "
"; } if (prop->visible.expr) { debug += "    dep: "; expr_print(prop->visible.expr, expr_print_help, &debug, E_NONE); debug += "
"; } } debug += "
"; } help = print_filter(_(sym->help)); } else if (menu->prompt) { head += ""; head += print_filter(_(menu->prompt->text)); head += "

"; if (showDebug) { if (menu->prompt->visible.expr) { debug += "  dep: "; expr_print(menu->prompt->visible.expr, expr_print_help, &debug, E_NONE); debug += "

"; } } } if (showDebug) debug += QString().sprintf("defined at %s:%d

", menu->file->name, menu->lineno); helpText->setText(head + debug + help); } void ConfigMainWindow::loadConfig(void) { QString s = QFileDialog::getOpenFileName(".config", NULL, this); if (s.isNull()) return; if (conf_read(QFile::encodeName(s))) QMessageBox::information(this, "qconf", "Unable to load configuration!"); ConfigView::updateListAll(); } void ConfigMainWindow::saveConfig(void) { if (conf_write(NULL)) QMessageBox::information(this, "qconf", "Unable to save configuration!"); } void ConfigMainWindow::saveConfigAs(void) { QString s = QFileDialog::getSaveFileName(".config", NULL, this); if (s.isNull()) return; if (conf_write(QFile::encodeName(s))) QMessageBox::information(this, "qconf", "Unable to save configuration!"); } void ConfigMainWindow::changeMenu(struct menu *menu) { configList->setRootMenu(menu); backAction->setEnabled(TRUE); } void ConfigMainWindow::listFocusChanged(void) { if (menuList->hasFocus()) { if (menuList->mode == menuMode) configList->clearSelection(); setHelp(menuList->selectedItem()); } else if (configList->hasFocus()) { setHelp(configList->selectedItem()); } } void ConfigMainWindow::goBack(void) { ConfigItem* item; configList->setParentMenu(); if (configList->rootEntry == &rootmenu) backAction->setEnabled(FALSE); item = (ConfigItem*)menuList->selectedItem(); while (item) { if (item->menu == configList->rootEntry) { menuList->setSelected(item, TRUE); break; } item = (ConfigItem*)item->parent(); } } void ConfigMainWindow::showSingleView(void) { menuView->hide(); menuList->setRootMenu(0); configList->mode = singleMode; if (configList->rootEntry == &rootmenu) configList->updateListAll(); else configList->setRootMenu(&rootmenu); configList->setAllOpen(TRUE); configList->setFocus(); } void ConfigMainWindow::showSplitView(void) { configList->mode = symbolMode; if (configList->rootEntry == &rootmenu) configList->updateListAll(); else configList->setRootMenu(&rootmenu); configList->setAllOpen(TRUE); configApp->processEvents(); menuList->mode = menuMode; menuList->setRootMenu(&rootmenu); menuList->setAllOpen(TRUE); menuView->show(); menuList->setFocus(); } void ConfigMainWindow::showFullView(void) { menuView->hide(); menuList->setRootMenu(0); configList->mode = fullMode; if (configList->rootEntry == &rootmenu) configList->updateListAll(); else configList->setRootMenu(&rootmenu); configList->setAllOpen(FALSE); configList->setFocus(); } void ConfigMainWindow::setShowAll(bool b) { if (configList->showAll == b) return; configList->showAll = b; configList->updateListAll(); menuList->showAll = b; menuList->updateListAll(); } void ConfigMainWindow::setShowDebug(bool b) { if (showDebug == b) return; showDebug = b; } void ConfigMainWindow::setShowName(bool b) { if (configList->showName == b) return; configList->showName = b; configList->reinit(); menuList->showName = b; menuList->reinit(); } void ConfigMainWindow::setShowRange(bool b) { if (configList->showRange == b) return; configList->showRange = b; configList->reinit(); menuList->showRange = b; menuList->reinit(); } void ConfigMainWindow::setShowData(bool b) { if (configList->showData == b) return; configList->showData = b; configList->reinit(); menuList->showData = b; menuList->reinit(); } /* * ask for saving configuration before quitting * TODO ask only when something changed */ void ConfigMainWindow::closeEvent(QCloseEvent* e) { if (!sym_change_count) { e->accept(); return; } QMessageBox mb("qconf", "Save configuration?", QMessageBox::Warning, QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape); mb.setButtonText(QMessageBox::Yes, "&Save Changes"); mb.setButtonText(QMessageBox::No, "&Discard Changes"); mb.setButtonText(QMessageBox::Cancel, "Cancel Exit"); switch (mb.exec()) { case QMessageBox::Yes: conf_write(NULL); case QMessageBox::No: e->accept(); break; case QMessageBox::Cancel: e->ignore(); break; } } void ConfigMainWindow::showIntro(void) { static char str[] = "Welcome to the qconf graphical configuration tool.\n\n" "For each option, a blank box indicates the feature is disabled, a check\n" "indicates it is enabled, and a dot indicates that it is to be compiled\n" "as a module. Clicking on the box will cycle through the three states.\n\n" "If you do not see an option (e.g., a device driver) that you believe\n" "should be present, try turning on Show All Options under the Options menu.\n" "Although there is no cross reference yet to help you figure out what other\n" "options must be enabled to support the option you are interested in, you can\n" "still view the help of a grayed-out option.\n\n" "Toggling Show Debug Info under the Options menu will show the dependencies,\n" "which you can then match by examining other options.\n\n"; QMessageBox::information(this, "qconf", str); } void ConfigMainWindow::showAbout(void) { static char str[] = "qconf is Copyright (C) 2002 Roman Zippel .\n"; QMessageBox::information(this, "qconf", str); } void ConfigMainWindow::saveSettings(void) { #if QT_VERSION >= 300 ConfigSettings *configSettings = new ConfigSettings; configSettings->writeEntry("/kconfig/qconf/window x", pos().x()); configSettings->writeEntry("/kconfig/qconf/window y", pos().y()); configSettings->writeEntry("/kconfig/qconf/window width", size().width()); configSettings->writeEntry("/kconfig/qconf/window height", size().height()); configSettings->writeEntry("/kconfig/qconf/showName", configList->showName); configSettings->writeEntry("/kconfig/qconf/showRange", configList->showRange); configSettings->writeEntry("/kconfig/qconf/showData", configList->showData); configSettings->writeEntry("/kconfig/qconf/showAll", configList->showAll); configSettings->writeEntry("/kconfig/qconf/showDebug", showDebug); QString entry; switch(configList->mode) { case singleMode : entry = "single"; break; case symbolMode : entry = "split"; break; case fullMode : entry = "full"; break; } configSettings->writeEntry("/kconfig/qconf/listMode", entry); configSettings->writeSizes("/kconfig/qconf/split1", split1->sizes()); configSettings->writeSizes("/kconfig/qconf/split2", split2->sizes()); delete configSettings; #endif } void fixup_rootmenu(struct menu *menu) { struct menu *child; static int menu_cnt = 0; menu->flags |= MENU_ROOT; for (child = menu->list; child; child = child->next) { if (child->prompt && child->prompt->type == P_MENU) { menu_cnt++; fixup_rootmenu(child); menu_cnt--; } else if (!menu_cnt) fixup_rootmenu(child); } } static const char *progname; static void usage(void) { printf("%s \n", progname); exit(0); } int main(int ac, char** av) { ConfigMainWindow* v; const char *name; bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #ifndef LKC_DIRECT_LINK kconfig_load(); #endif progname = av[0]; configApp = new QApplication(ac, av); if (ac > 1 && av[1][0] == '-') { switch (av[1][1]) { case 'h': case '?': usage(); } name = av[2]; } else name = av[1]; if (!name) usage(); conf_parse(name); fixup_rootmenu(&rootmenu); conf_read(NULL); //zconfdump(stdout); v = new ConfigMainWindow(); //zconfdump(stdout); v->show(); configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit())); configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings())); configApp->exec(); return 0; } busybox-1.22.1/scripts/kconfig/lxdialog/0000755000000000000000000000000012320365361016701 5ustar rootrootbusybox-1.22.1/scripts/kconfig/lxdialog/lxdialog.c0000644000000000000000000001240112263563520020651 0ustar rootroot/* * dialog - Display simple dialog boxes from shell scripts * * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "dialog.h" static void Usage(const char *name); typedef int (jumperFn) (const char *title, int argc, const char *const *argv); struct Mode { char *name; int argmin, argmax, argmod; jumperFn *jumper; }; jumperFn j_menu, j_radiolist, j_yesno, j_textbox, j_inputbox; jumperFn j_msgbox, j_infobox; static struct Mode modes[] = { {"--menu", 9, 0, 3, j_menu}, {"--radiolist", 9, 0, 3, j_radiolist}, {"--yesno", 5, 5, 1, j_yesno}, {"--textbox", 5, 5, 1, j_textbox}, {"--inputbox", 5, 6, 1, j_inputbox}, {"--msgbox", 5, 5, 1, j_msgbox}, {"--infobox", 5, 5, 1, j_infobox}, {NULL, 0, 0, 0, NULL} }; static struct Mode *modePtr; #ifdef LOCALE #include #endif int main(int argc, const char *const *argv) { int offset = 0, opt_clear = 0, end_common_opts = 0, retval; const char *title = NULL; #ifdef LOCALE (void)setlocale(LC_ALL, ""); #endif #ifdef TRACE trace(TRACE_CALLS | TRACE_UPDATE); #endif if (argc < 2) { Usage(argv[0]); exit(-1); } while (offset < argc - 1 && !end_common_opts) { /* Common options */ if (!strcmp(argv[offset + 1], "--title")) { if (argc - offset < 3 || title != NULL) { Usage(argv[0]); exit(-1); } else { title = argv[offset + 2]; offset += 2; } } else if (!strcmp(argv[offset + 1], "--backtitle")) { if (backtitle != NULL) { Usage(argv[0]); exit(-1); } else { backtitle = argv[offset + 2]; offset += 2; } } else if (!strcmp(argv[offset + 1], "--clear")) { if (opt_clear) { /* Hey, "--clear" can't appear twice! */ Usage(argv[0]); exit(-1); } else if (argc == 2) { /* we only want to clear the screen */ init_dialog(); refresh(); /* init_dialog() will clear the screen for us */ end_dialog(); return 0; } else { opt_clear = 1; offset++; } } else /* no more common options */ end_common_opts = 1; } if (argc - 1 == offset) { /* no more options */ Usage(argv[0]); exit(-1); } /* use a table to look for the requested mode, to avoid code duplication */ for (modePtr = modes; modePtr->name; modePtr++) /* look for the mode */ if (!strcmp(argv[offset + 1], modePtr->name)) break; if (!modePtr->name) Usage(argv[0]); if (argc - offset < modePtr->argmin) Usage(argv[0]); if (modePtr->argmax && argc - offset > modePtr->argmax) Usage(argv[0]); init_dialog(); retval = (*(modePtr->jumper)) (title, argc - offset, argv + offset); if (opt_clear) { /* clear screen before exit */ attr_clear(stdscr, LINES, COLS, screen_attr); refresh(); } end_dialog(); exit(retval); } /* * Print program usage */ static void Usage(const char *name) { fprintf(stderr, "\ \ndialog, by Savio Lam (lam836@cs.cuhk.hk).\ \n patched by Stuart Herbert (S.Herbert@shef.ac.uk)\ \n modified/gutted for use as a Linux kernel config tool by \ \n William Roadcap (roadcapw@cfw.com)\ \n\ \n* Display dialog boxes from shell scripts *\ \n\ \nUsage: %s --clear\ \n %s [--title ] [--backtitle <backtitle>] --clear <Box options>\ \n\ \nBox options:\ \n\ \n --menu <text> <height> <width> <menu height> <tag1> <item1>...\ \n --radiolist <text> <height> <width> <list height> <tag1> <item1> <status1>...\ \n --textbox <file> <height> <width>\ \n --inputbox <text> <height> <width> [<init>]\ \n --yesno <text> <height> <width>\ \n", name, name); exit(-1); } /* * These are the program jumpers */ int j_menu(const char *t, int ac, const char *const *av) { return dialog_menu(t, av[2], atoi(av[3]), atoi(av[4]), atoi(av[5]), av[6], (ac - 6) / 2, av + 7); } int j_radiolist(const char *t, int ac, const char *const *av) { return dialog_checklist(t, av[2], atoi(av[3]), atoi(av[4]), atoi(av[5]), (ac - 6) / 3, av + 6); } int j_textbox(const char *t, int ac, const char *const *av) { return dialog_textbox(t, av[2], atoi(av[3]), atoi(av[4])); } int j_yesno(const char *t, int ac, const char *const *av) { return dialog_yesno(t, av[2], atoi(av[3]), atoi(av[4])); } int j_inputbox(const char *t, int ac, const char *const *av) { int ret = dialog_inputbox(t, av[2], atoi(av[3]), atoi(av[4]), ac == 6 ? av[5] : (char *)NULL); if (ret == 0) fprintf(stderr, dialog_input_result); return ret; } int j_msgbox(const char *t, int ac, const char *const *av) { return dialog_msgbox(t, av[2], atoi(av[3]), atoi(av[4]), 1); } int j_infobox(const char *t, int ac, const char *const *av) { return dialog_msgbox(t, av[2], atoi(av[3]), atoi(av[4]), 0); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/lxdialog/inputbox.c��������������������������������������������������0000644�0000000�0000000�00000013134�12263563520�020722� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * inputbox.c -- implements the input box * * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "dialog.h" char dialog_input_result[MAX_LEN + 1]; /* * Print the termination buttons */ static void print_buttons(WINDOW * dialog, int height, int width, int selected) { int x = width / 2 - 11; int y = height - 2; print_button(dialog, " Ok ", y, x, selected == 0); print_button(dialog, " Help ", y, x + 14, selected == 1); wmove(dialog, y, x + 1 + 14 * selected); wrefresh(dialog); } /* * Display a dialog box for inputing a string */ int dialog_inputbox(const char *title, const char *prompt, int height, int width, const char *init) { int i, x, y, box_y, box_x, box_width; int input_x = 0, scroll = 0, key = 0, button = -1; char *instr = dialog_input_result; WINDOW *dialog; /* center dialog box on screen */ x = (COLS - width) / 2; y = (LINES - height) / 2; draw_shadow(stdscr, y, x, height, width); dialog = newwin(height, width, y, x); keypad(dialog, TRUE); draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); wattrset(dialog, border_attr); mvwaddch(dialog, height - 3, 0, ACS_LTEE); for (i = 0; i < width - 2; i++) waddch(dialog, ACS_HLINE); wattrset(dialog, dialog_attr); waddch(dialog, ACS_RTEE); print_title(dialog, title, width); wattrset(dialog, dialog_attr); print_autowrap(dialog, prompt, width - 2, 1, 3); /* Draw the input field box */ box_width = width - 6; getyx(dialog, y, x); box_y = y + 2; box_x = (width - box_width) / 2; draw_box(dialog, y + 1, box_x - 1, 3, box_width + 2, border_attr, dialog_attr); print_buttons(dialog, height, width, 0); /* Set up the initial value */ wmove(dialog, box_y, box_x); wattrset(dialog, inputbox_attr); if (!init) instr[0] = '\0'; else strcpy(instr, init); input_x = strlen(instr); if (input_x >= box_width) { scroll = input_x - box_width + 1; input_x = box_width - 1; for (i = 0; i < box_width - 1; i++) waddch(dialog, instr[scroll + i]); } else { waddstr(dialog, instr); } wmove(dialog, box_y, box_x + input_x); wrefresh(dialog); while (key != ESC) { key = wgetch(dialog); if (button == -1) { /* Input box selected */ switch (key) { case TAB: case KEY_UP: case KEY_DOWN: break; case KEY_LEFT: continue; case KEY_RIGHT: continue; case KEY_BACKSPACE: case 127: if (input_x || scroll) { wattrset(dialog, inputbox_attr); if (!input_x) { scroll = scroll < box_width - 1 ? 0 : scroll - (box_width - 1); wmove(dialog, box_y, box_x); for (i = 0; i < box_width; i++) waddch(dialog, instr[scroll + input_x + i] ? instr[scroll + input_x + i] : ' '); input_x = strlen(instr) - scroll; } else input_x--; instr[scroll + input_x] = '\0'; mvwaddch(dialog, box_y, input_x + box_x, ' '); wmove(dialog, box_y, input_x + box_x); wrefresh(dialog); } continue; default: if (key < 0x100 && isprint(key)) { if (scroll + input_x < MAX_LEN) { wattrset(dialog, inputbox_attr); instr[scroll + input_x] = key; instr[scroll + input_x + 1] = '\0'; if (input_x == box_width - 1) { scroll++; wmove(dialog, box_y, box_x); for (i = 0; i < box_width - 1; i++) waddch(dialog, instr [scroll + i]); } else { wmove(dialog, box_y, input_x++ + box_x); waddch(dialog, key); } wrefresh(dialog); } else flash(); /* Alarm user about overflow */ continue; } } } switch (key) { case 'O': case 'o': delwin(dialog); return 0; case 'H': case 'h': delwin(dialog); return 1; case KEY_UP: case KEY_LEFT: switch (button) { case -1: button = 1; /* Indicates "Cancel" button is selected */ print_buttons(dialog, height, width, 1); break; case 0: button = -1; /* Indicates input box is selected */ print_buttons(dialog, height, width, 0); wmove(dialog, box_y, box_x + input_x); wrefresh(dialog); break; case 1: button = 0; /* Indicates "OK" button is selected */ print_buttons(dialog, height, width, 0); break; } break; case TAB: case KEY_DOWN: case KEY_RIGHT: switch (button) { case -1: button = 0; /* Indicates "OK" button is selected */ print_buttons(dialog, height, width, 0); break; case 0: button = 1; /* Indicates "Cancel" button is selected */ print_buttons(dialog, height, width, 1); break; case 1: button = -1; /* Indicates input box is selected */ print_buttons(dialog, height, width, 0); wmove(dialog, box_y, box_x + input_x); wrefresh(dialog); break; } break; case ' ': case '\n': delwin(dialog); return (button == -1 ? 0 : button); case 'X': case 'x': key = ESC; case ESC: break; } } delwin(dialog); return -1; /* ESC pressed */ } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/lxdialog/msgbox.c����������������������������������������������������0000644�0000000�0000000�00000004112�12263563520�020345� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * msgbox.c -- implements the message box and info box * * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "dialog.h" /* * Display a message box. Program will pause and display an "OK" button * if the parameter 'pause' is non-zero. */ int dialog_msgbox(const char *title, const char *prompt, int height, int width, int pause) { int i, x, y, key = 0; WINDOW *dialog; /* center dialog box on screen */ x = (COLS - width) / 2; y = (LINES - height) / 2; draw_shadow(stdscr, y, x, height, width); dialog = newwin(height, width, y, x); keypad(dialog, TRUE); draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); print_title(dialog, title, width); wattrset(dialog, dialog_attr); print_autowrap(dialog, prompt, width - 2, 1, 2); if (pause) { wattrset(dialog, border_attr); mvwaddch(dialog, height - 3, 0, ACS_LTEE); for (i = 0; i < width - 2; i++) waddch(dialog, ACS_HLINE); wattrset(dialog, dialog_attr); waddch(dialog, ACS_RTEE); print_button(dialog, " Ok ", height - 2, width / 2 - 4, TRUE); wrefresh(dialog); while (key != ESC && key != '\n' && key != ' ' && key != 'O' && key != 'o' && key != 'X' && key != 'x') key = wgetch(dialog); } else { key = '\n'; wrefresh(dialog); } delwin(dialog); return key == ESC ? -1 : 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/lxdialog/dialog.h����������������������������������������������������0000644�0000000�0000000�00000013366�12263563520�020325� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * dialog.h -- common declarations for all dialog modules * * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <ctype.h> #include <stdlib.h> #include <string.h> #ifdef __sun__ #define CURS_MACROS #endif #include CURSES_LOC /* * Colors in ncurses 1.9.9e do not work properly since foreground and * background colors are OR'd rather than separately masked. This version * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible * with standard curses. The simplest fix (to make this work with standard * curses) uses the wbkgdset() function, not used in the original hack. * Turn it off if we're building with 1.9.9e, since it just confuses things. */ #if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE) #define OLD_NCURSES 1 #undef wbkgdset #define wbkgdset(w,p) /*nothing */ #else #define OLD_NCURSES 0 #endif #define TR(params) _tracef params #define ESC 27 #define TAB 9 #define MAX_LEN 2048 #define BUF_SIZE (10*1024) #define MIN(x,y) (x < y ? x : y) #define MAX(x,y) (x > y ? x : y) #ifndef ACS_ULCORNER #define ACS_ULCORNER '+' #endif #ifndef ACS_LLCORNER #define ACS_LLCORNER '+' #endif #ifndef ACS_URCORNER #define ACS_URCORNER '+' #endif #ifndef ACS_LRCORNER #define ACS_LRCORNER '+' #endif #ifndef ACS_HLINE #define ACS_HLINE '-' #endif #ifndef ACS_VLINE #define ACS_VLINE '|' #endif #ifndef ACS_LTEE #define ACS_LTEE '+' #endif #ifndef ACS_RTEE #define ACS_RTEE '+' #endif #ifndef ACS_UARROW #define ACS_UARROW '^' #endif #ifndef ACS_DARROW #define ACS_DARROW 'v' #endif /* * Attribute names */ #define screen_attr attributes[0] #define shadow_attr attributes[1] #define dialog_attr attributes[2] #define title_attr attributes[3] #define border_attr attributes[4] #define button_active_attr attributes[5] #define button_inactive_attr attributes[6] #define button_key_active_attr attributes[7] #define button_key_inactive_attr attributes[8] #define button_label_active_attr attributes[9] #define button_label_inactive_attr attributes[10] #define inputbox_attr attributes[11] #define inputbox_border_attr attributes[12] #define searchbox_attr attributes[13] #define searchbox_title_attr attributes[14] #define searchbox_border_attr attributes[15] #define position_indicator_attr attributes[16] #define menubox_attr attributes[17] #define menubox_border_attr attributes[18] #define item_attr attributes[19] #define item_selected_attr attributes[20] #define tag_attr attributes[21] #define tag_selected_attr attributes[22] #define tag_key_attr attributes[23] #define tag_key_selected_attr attributes[24] #define check_attr attributes[25] #define check_selected_attr attributes[26] #define uarrow_attr attributes[27] #define darrow_attr attributes[28] /* number of attributes */ #define ATTRIBUTE_COUNT 29 /* * Global variables */ extern bool use_colors; extern bool use_shadow; extern chtype attributes[]; extern const char *backtitle; /* * Function prototypes */ extern void create_rc(const char *filename); extern int parse_rc(void); void init_dialog(void); void end_dialog(void); void attr_clear(WINDOW * win, int height, int width, chtype attr); void dialog_clear(void); void color_setup(void); void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x); void print_button(WINDOW * win, const char *label, int y, int x, int selected); void print_title(WINDOW *dialog, const char *title, int width); void draw_box(WINDOW * win, int y, int x, int height, int width, chtype box, chtype border); void draw_shadow(WINDOW * win, int y, int x, int height, int width); int first_alpha(const char *string, const char *exempt); int dialog_yesno(const char *title, const char *prompt, int height, int width); int dialog_msgbox(const char *title, const char *prompt, int height, int width, int pause); int dialog_textbox(const char *title, const char *file, int height, int width); int dialog_menu(const char *title, const char *prompt, int height, int width, int menu_height, const char *choice, int item_no, const char *const *items); int dialog_checklist(const char *title, const char *prompt, int height, int width, int list_height, int item_no, const char *const *items); extern char dialog_input_result[]; int dialog_inputbox(const char *title, const char *prompt, int height, int width, const char *init); /* * This is the base for fictitious keys, which activate * the buttons. * * Mouse-generated keys are the following: * -- the first 32 are used as numbers, in addition to '0'-'9' * -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o') * -- uppercase chars are used to invoke the button (M_EVENT + 'O') */ #define M_EVENT (KEY_MAX+1) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/lxdialog/menubox.c���������������������������������������������������0000644�0000000�0000000�00000025627�12263563520�020541� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * menubox.c -- implements the menu box * * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Changes by Clifford Wolf (god@clifford.at) * * [ 1998-06-13 ] * * *) A bugfix for the Page-Down problem * * *) Formerly when I used Page Down and Page Up, the cursor would be set * to the first position in the menu box. Now lxdialog is a bit * smarter and works more like other menu systems (just have a look at * it). * * *) Formerly if I selected something my scrolling would be broken because * lxdialog is re-invoked by the Menuconfig shell script, can't * remember the last scrolling position, and just sets it so that the * cursor is at the bottom of the box. Now it writes the temporary file * lxdialog.scrltmp which contains this information. The file is * deleted by lxdialog if the user leaves a submenu or enters a new * one, but it would be nice if Menuconfig could make another "rm -f" * just to be sure. Just try it out - you will recognise a difference! * * [ 1998-06-14 ] * * *) Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files * and menus change their size on the fly. * * *) If for some reason the last scrolling position is not saved by * lxdialog, it sets the scrolling so that the selected item is in the * middle of the menu box, not at the bottom. * * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net) * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus. * This fixes a bug in Menuconfig where using ' ' to descend into menus * would leave mis-synchronized lxdialog.scrltmp files lying around, * fscanf would read in 'scroll', and eventually that value would get used. */ #include "dialog.h" static int menu_width, item_x; /* * Print menu item */ static void do_print_item(WINDOW * win, const char *item, int choice, int selected, int hotkey) { int j; char *menu_item = malloc(menu_width + 1); strncpy(menu_item, item, menu_width - item_x); menu_item[menu_width] = 0; j = first_alpha(menu_item, "YyNnMmHh"); /* Clear 'residue' of last item */ wattrset(win, menubox_attr); wmove(win, choice, 0); #if OLD_NCURSES { int i; for (i = 0; i < menu_width; i++) waddch(win, ' '); } #else wclrtoeol(win); #endif wattrset(win, selected ? item_selected_attr : item_attr); mvwaddstr(win, choice, item_x, menu_item); if (hotkey) { wattrset(win, selected ? tag_key_selected_attr : tag_key_attr); mvwaddch(win, choice, item_x + j, menu_item[j]); } if (selected) { wmove(win, choice, item_x + 1); } free(menu_item); wrefresh(win); } #define print_item(index, choice, selected) \ do {\ int hotkey = (items[(index) * 2][0] != ':'); \ do_print_item(menu, items[(index) * 2 + 1], choice, selected, hotkey); \ } while (0) /* * Print the scroll indicators. */ static void print_arrows(WINDOW * win, int item_no, int scroll, int y, int x, int height) { int cur_y, cur_x; getyx(win, cur_y, cur_x); wmove(win, y, x); if (scroll > 0) { wattrset(win, uarrow_attr); waddch(win, ACS_UARROW); waddstr(win, "(-)"); } else { wattrset(win, menubox_attr); waddch(win, ACS_HLINE); waddch(win, ACS_HLINE); waddch(win, ACS_HLINE); waddch(win, ACS_HLINE); } y = y + height + 1; wmove(win, y, x); wrefresh(win); if ((height < item_no) && (scroll + height < item_no)) { wattrset(win, darrow_attr); waddch(win, ACS_DARROW); waddstr(win, "(+)"); } else { wattrset(win, menubox_border_attr); waddch(win, ACS_HLINE); waddch(win, ACS_HLINE); waddch(win, ACS_HLINE); waddch(win, ACS_HLINE); } wmove(win, cur_y, cur_x); wrefresh(win); } /* * Display the termination buttons. */ static void print_buttons(WINDOW * win, int height, int width, int selected) { int x = width / 2 - 16; int y = height - 2; print_button(win, "Select", y, x, selected == 0); print_button(win, " Exit ", y, x + 12, selected == 1); print_button(win, " Help ", y, x + 24, selected == 2); wmove(win, y, x + 1 + 12 * selected); wrefresh(win); } /* scroll up n lines (n may be negative) */ static void do_scroll(WINDOW *win, int *scroll, int n) { /* Scroll menu up */ scrollok(win, TRUE); wscrl(win, n); scrollok(win, FALSE); *scroll = *scroll + n; wrefresh(win); } /* * Display a menu for choosing among a number of options */ int dialog_menu(const char *title, const char *prompt, int height, int width, int menu_height, const char *current, int item_no, const char *const *items) { int i, j, x, y, box_x, box_y; int key = 0, button = 0, scroll = 0, choice = 0; int first_item = 0, max_choice; WINDOW *dialog, *menu; FILE *f; max_choice = MIN(menu_height, item_no); /* center dialog box on screen */ x = (COLS - width) / 2; y = (LINES - height) / 2; draw_shadow(stdscr, y, x, height, width); dialog = newwin(height, width, y, x); keypad(dialog, TRUE); draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); wattrset(dialog, border_attr); mvwaddch(dialog, height - 3, 0, ACS_LTEE); for (i = 0; i < width - 2; i++) waddch(dialog, ACS_HLINE); wattrset(dialog, dialog_attr); wbkgdset(dialog, dialog_attr & A_COLOR); waddch(dialog, ACS_RTEE); print_title(dialog, title, width); wattrset(dialog, dialog_attr); print_autowrap(dialog, prompt, width - 2, 1, 3); menu_width = width - 6; box_y = height - menu_height - 5; box_x = (width - menu_width) / 2 - 1; /* create new window for the menu */ menu = subwin(dialog, menu_height, menu_width, y + box_y + 1, x + box_x + 1); keypad(menu, TRUE); /* draw a box around the menu items */ draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2, menubox_border_attr, menubox_attr); item_x = (menu_width - 70) / 2; /* Set choice to default item */ for (i = 0; i < item_no; i++) if (strcmp(current, items[i * 2]) == 0) choice = i; /* get the scroll info from the temp file */ if ((f = fopen("lxdialog.scrltmp", "r")) != NULL) { if ((fscanf(f, "%d\n", &scroll) == 1) && (scroll <= choice) && (scroll + max_choice > choice) && (scroll >= 0) && (scroll + max_choice <= item_no)) { first_item = scroll; choice = choice - scroll; fclose(f); } else { scroll = 0; remove("lxdialog.scrltmp"); fclose(f); f = NULL; } } if ((choice >= max_choice) || (f == NULL && choice >= max_choice / 2)) { if (choice >= item_no - max_choice / 2) scroll = first_item = item_no - max_choice; else scroll = first_item = choice - max_choice / 2; choice = choice - scroll; } /* Print the menu */ for (i = 0; i < max_choice; i++) { print_item(first_item + i, i, i == choice); } wnoutrefresh(menu); print_arrows(dialog, item_no, scroll, box_y, box_x + item_x + 1, menu_height); print_buttons(dialog, height, width, 0); wmove(menu, choice, item_x + 1); wrefresh(menu); while (key != ESC) { key = wgetch(menu); if (key < 256 && isalpha(key)) key = tolower(key); if (strchr("ynmh", key)) i = max_choice; else { for (i = choice + 1; i < max_choice; i++) { j = first_alpha(items[(scroll + i) * 2 + 1], "YyNnMmHh"); if (key == tolower(items[(scroll + i) * 2 + 1][j])) break; } if (i == max_choice) for (i = 0; i < max_choice; i++) { j = first_alpha(items [(scroll + i) * 2 + 1], "YyNnMmHh"); if (key == tolower(items[(scroll + i) * 2 + 1][j])) break; } } if (i < max_choice || key == KEY_UP || key == KEY_DOWN || key == '-' || key == '+' || key == KEY_PPAGE || key == KEY_NPAGE) { /* Remove highligt of current item */ print_item(scroll + choice, choice, FALSE); if (key == KEY_UP || key == '-') { if (choice < 2 && scroll) { /* Scroll menu down */ do_scroll(menu, &scroll, -1); print_item(scroll, 0, FALSE); } else choice = MAX(choice - 1, 0); } else if (key == KEY_DOWN || key == '+') { print_item(scroll+choice, choice, FALSE); if ((choice > max_choice - 3) && (scroll + max_choice < item_no)) { /* Scroll menu up */ do_scroll(menu, &scroll, 1); print_item(scroll+max_choice - 1, max_choice - 1, FALSE); } else choice = MIN(choice + 1, max_choice - 1); } else if (key == KEY_PPAGE) { scrollok(menu, TRUE); for (i = 0; (i < max_choice); i++) { if (scroll > 0) { do_scroll(menu, &scroll, -1); print_item(scroll, 0, FALSE); } else { if (choice > 0) choice--; } } } else if (key == KEY_NPAGE) { for (i = 0; (i < max_choice); i++) { if (scroll + max_choice < item_no) { do_scroll(menu, &scroll, 1); print_item(scroll+max_choice-1, max_choice - 1, FALSE); } else { if (choice + 1 < max_choice) choice++; } } } else choice = i; print_item(scroll + choice, choice, TRUE); print_arrows(dialog, item_no, scroll, box_y, box_x + item_x + 1, menu_height); wnoutrefresh(dialog); wrefresh(menu); continue; /* wait for another key press */ } switch (key) { case KEY_LEFT: case TAB: case KEY_RIGHT: button = ((key == KEY_LEFT ? --button : ++button) < 0) ? 2 : (button > 2 ? 0 : button); print_buttons(dialog, height, width, button); wrefresh(menu); break; case ' ': case 's': case 'y': case 'n': case 'm': case '/': /* save scroll info */ if ((f = fopen("lxdialog.scrltmp", "w")) != NULL) { fprintf(f, "%d\n", scroll); fclose(f); } delwin(dialog); fprintf(stderr, "%s\n", items[(scroll + choice) * 2]); switch (key) { case 's': return 3; case 'y': return 3; case 'n': return 4; case 'm': return 5; case ' ': return 6; case '/': return 7; } return 0; case 'h': case '?': button = 2; case '\n': delwin(dialog); if (button == 2) fprintf(stderr, "%s \"%s\"\n", items[(scroll + choice) * 2], items[(scroll + choice) * 2 + 1] + first_alpha(items [(scroll + choice) * 2 + 1], "")); else fprintf(stderr, "%s\n", items[(scroll + choice) * 2]); remove("lxdialog.scrltmp"); return button; case 'e': case 'x': key = ESC; case ESC: break; } } delwin(dialog); remove("lxdialog.scrltmp"); return -1; /* ESC pressed */ } ���������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/lxdialog/checklist.c�������������������������������������������������0000644�0000000�0000000�00000021227�12263563520�021025� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * checklist.c -- implements the checklist box * * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) * Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension * Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "dialog.h" static int list_width, check_x, item_x; /* * Print list item */ static void print_item(WINDOW * win, const char *item, int status, int choice, int selected) { int i; /* Clear 'residue' of last item */ wattrset(win, menubox_attr); wmove(win, choice, 0); for (i = 0; i < list_width; i++) waddch(win, ' '); wmove(win, choice, check_x); wattrset(win, selected ? check_selected_attr : check_attr); wprintw(win, "(%c)", status ? 'X' : ' '); wattrset(win, selected ? tag_selected_attr : tag_attr); mvwaddch(win, choice, item_x, item[0]); wattrset(win, selected ? item_selected_attr : item_attr); waddstr(win, (char *)item + 1); if (selected) { wmove(win, choice, check_x + 1); wrefresh(win); } } /* * Print the scroll indicators. */ static void print_arrows(WINDOW * win, int choice, int item_no, int scroll, int y, int x, int height) { wmove(win, y, x); if (scroll > 0) { wattrset(win, uarrow_attr); waddch(win, ACS_UARROW); waddstr(win, "(-)"); } else { wattrset(win, menubox_attr); waddch(win, ACS_HLINE); waddch(win, ACS_HLINE); waddch(win, ACS_HLINE); waddch(win, ACS_HLINE); } y = y + height + 1; wmove(win, y, x); if ((height < item_no) && (scroll + choice < item_no - 1)) { wattrset(win, darrow_attr); waddch(win, ACS_DARROW); waddstr(win, "(+)"); } else { wattrset(win, menubox_border_attr); waddch(win, ACS_HLINE); waddch(win, ACS_HLINE); waddch(win, ACS_HLINE); waddch(win, ACS_HLINE); } } /* * Display the termination buttons */ static void print_buttons(WINDOW * dialog, int height, int width, int selected) { int x = width / 2 - 11; int y = height - 2; print_button(dialog, "Select", y, x, selected == 0); print_button(dialog, " Help ", y, x + 14, selected == 1); wmove(dialog, y, x + 1 + 14 * selected); wrefresh(dialog); } /* * Display a dialog box with a list of options that can be turned on or off * in the style of radiolist (only one option turned on at a time). */ int dialog_checklist(const char *title, const char *prompt, int height, int width, int list_height, int item_no, const char *const *items) { int i, x, y, box_x, box_y; int key = 0, button = 0, choice = 0, scroll = 0, max_choice, *status; WINDOW *dialog, *list; /* Allocate space for storing item on/off status */ if ((status = malloc(sizeof(int) * item_no)) == NULL) { endwin(); fprintf(stderr, "\nCan't allocate memory in dialog_checklist().\n"); exit(-1); } /* Initializes status */ for (i = 0; i < item_no; i++) { status[i] = !strcasecmp(items[i * 3 + 2], "on"); if ((!choice && status[i]) || !strcasecmp(items[i * 3 + 2], "selected")) choice = i + 1; } if (choice) choice--; max_choice = MIN(list_height, item_no); /* center dialog box on screen */ x = (COLS - width) / 2; y = (LINES - height) / 2; draw_shadow(stdscr, y, x, height, width); dialog = newwin(height, width, y, x); keypad(dialog, TRUE); draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); wattrset(dialog, border_attr); mvwaddch(dialog, height - 3, 0, ACS_LTEE); for (i = 0; i < width - 2; i++) waddch(dialog, ACS_HLINE); wattrset(dialog, dialog_attr); waddch(dialog, ACS_RTEE); print_title(dialog, title, width); wattrset(dialog, dialog_attr); print_autowrap(dialog, prompt, width - 2, 1, 3); list_width = width - 6; box_y = height - list_height - 5; box_x = (width - list_width) / 2 - 1; /* create new window for the list */ list = subwin(dialog, list_height, list_width, y + box_y + 1, x + box_x + 1); keypad(list, TRUE); /* draw a box around the list items */ draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2, menubox_border_attr, menubox_attr); /* Find length of longest item in order to center checklist */ check_x = 0; for (i = 0; i < item_no; i++) check_x = MAX(check_x, +strlen(items[i * 3 + 1]) + 4); check_x = (list_width - check_x) / 2; item_x = check_x + 4; if (choice >= list_height) { scroll = choice - list_height + 1; choice -= scroll; } /* Print the list */ for (i = 0; i < max_choice; i++) { print_item(list, items[(scroll + i) * 3 + 1], status[i + scroll], i, i == choice); } print_arrows(dialog, choice, item_no, scroll, box_y, box_x + check_x + 5, list_height); print_buttons(dialog, height, width, 0); wnoutrefresh(dialog); wnoutrefresh(list); doupdate(); while (key != ESC) { key = wgetch(dialog); for (i = 0; i < max_choice; i++) if (toupper(key) == toupper(items[(scroll + i) * 3 + 1][0])) break; if (i < max_choice || key == KEY_UP || key == KEY_DOWN || key == '+' || key == '-') { if (key == KEY_UP || key == '-') { if (!choice) { if (!scroll) continue; /* Scroll list down */ if (list_height > 1) { /* De-highlight current first item */ print_item(list, items[scroll * 3 + 1], status[scroll], 0, FALSE); scrollok(list, TRUE); wscrl(list, -1); scrollok(list, FALSE); } scroll--; print_item(list, items[scroll * 3 + 1], status[scroll], 0, TRUE); print_arrows(dialog, choice, item_no, scroll, box_y, box_x + check_x + 5, list_height); wnoutrefresh(dialog); wrefresh(list); continue; /* wait for another key press */ } else i = choice - 1; } else if (key == KEY_DOWN || key == '+') { if (choice == max_choice - 1) { if (scroll + choice >= item_no - 1) continue; /* Scroll list up */ if (list_height > 1) { /* De-highlight current last item before scrolling up */ print_item(list, items[(scroll + max_choice - 1) * 3 + 1], status[scroll + max_choice - 1], max_choice - 1, FALSE); scrollok(list, TRUE); wscrl(list, 1); scrollok(list, FALSE); } scroll++; print_item(list, items[(scroll + max_choice - 1) * 3 + 1], status[scroll + max_choice - 1], max_choice - 1, TRUE); print_arrows(dialog, choice, item_no, scroll, box_y, box_x + check_x + 5, list_height); wnoutrefresh(dialog); wrefresh(list); continue; /* wait for another key press */ } else i = choice + 1; } if (i != choice) { /* De-highlight current item */ print_item(list, items[(scroll + choice) * 3 + 1], status[scroll + choice], choice, FALSE); /* Highlight new item */ choice = i; print_item(list, items[(scroll + choice) * 3 + 1], status[scroll + choice], choice, TRUE); wnoutrefresh(dialog); wrefresh(list); } continue; /* wait for another key press */ } switch (key) { case 'H': case 'h': case '?': fprintf(stderr, "%s", items[(scroll + choice) * 3]); delwin(dialog); free(status); return 1; case TAB: case KEY_LEFT: case KEY_RIGHT: button = ((key == KEY_LEFT ? --button : ++button) < 0) ? 1 : (button > 1 ? 0 : button); print_buttons(dialog, height, width, button); wrefresh(dialog); break; case 'S': case 's': case ' ': case '\n': if (!button) { if (!status[scroll + choice]) { for (i = 0; i < item_no; i++) status[i] = 0; status[scroll + choice] = 1; for (i = 0; i < max_choice; i++) print_item(list, items[(scroll + i) * 3 + 1], status[scroll + i], i, i == choice); } wnoutrefresh(dialog); wrefresh(list); for (i = 0; i < item_no; i++) if (status[i]) fprintf(stderr, "%s", items[i * 3]); } else fprintf(stderr, "%s", items[(scroll + choice) * 3]); delwin(dialog); free(status); return button; case 'X': case 'x': key = ESC; case ESC: break; } /* Now, update everything... */ doupdate(); } delwin(dialog); free(status); return -1; /* ESC pressed */ } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/lxdialog/Makefile����������������������������������������������������0000644�0000000�0000000�00000001325�12263563520�020345� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Makefile to build lxdialog package # check-lxdialog := $(srctree)/$(src)/check-lxdialog.sh # Use reursively expanded variables so we do not call gcc unless # we really need to do so. (Do not call gcc as part of make mrproper) HOST_EXTRACFLAGS = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ccflags) HOST_LOADLIBES = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ldflags $(HOSTCC)) HOST_EXTRACFLAGS += -DLOCALE PHONY += dochecklxdialog $(obj)/dochecklxdialog: $(Q)$(CONFIG_SHELL) $(check-lxdialog) -check $(HOSTCC) $(HOST_EXTRACFLAGS) $(HOST_LOADLIBES) hostprogs-y := lxdialog always := $(hostprogs-y) dochecklxdialog lxdialog-objs := checklist.o menubox.o textbox.o yesno.o inputbox.o \ util.o lxdialog.o msgbox.o �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/lxdialog/colors.h����������������������������������������������������0000644�0000000�0000000�00000012220�12263563520�020353� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * colors.h -- color attribute definitions * * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Default color definitions * * *_FG = foreground * *_BG = background * *_HL = highlight? */ #define SCREEN_FG COLOR_CYAN #define SCREEN_BG COLOR_BLUE #define SCREEN_HL TRUE #define SHADOW_FG COLOR_BLACK #define SHADOW_BG COLOR_BLACK #define SHADOW_HL TRUE #define DIALOG_FG COLOR_BLACK #define DIALOG_BG COLOR_WHITE #define DIALOG_HL FALSE #define TITLE_FG COLOR_YELLOW #define TITLE_BG COLOR_WHITE #define TITLE_HL TRUE #define BORDER_FG COLOR_WHITE #define BORDER_BG COLOR_WHITE #define BORDER_HL TRUE #define BUTTON_ACTIVE_FG COLOR_WHITE #define BUTTON_ACTIVE_BG COLOR_BLUE #define BUTTON_ACTIVE_HL TRUE #define BUTTON_INACTIVE_FG COLOR_BLACK #define BUTTON_INACTIVE_BG COLOR_WHITE #define BUTTON_INACTIVE_HL FALSE #define BUTTON_KEY_ACTIVE_FG COLOR_WHITE #define BUTTON_KEY_ACTIVE_BG COLOR_BLUE #define BUTTON_KEY_ACTIVE_HL TRUE #define BUTTON_KEY_INACTIVE_FG COLOR_RED #define BUTTON_KEY_INACTIVE_BG COLOR_WHITE #define BUTTON_KEY_INACTIVE_HL FALSE #define BUTTON_LABEL_ACTIVE_FG COLOR_YELLOW #define BUTTON_LABEL_ACTIVE_BG COLOR_BLUE #define BUTTON_LABEL_ACTIVE_HL TRUE #define BUTTON_LABEL_INACTIVE_FG COLOR_BLACK #define BUTTON_LABEL_INACTIVE_BG COLOR_WHITE #define BUTTON_LABEL_INACTIVE_HL TRUE #define INPUTBOX_FG COLOR_BLACK #define INPUTBOX_BG COLOR_WHITE #define INPUTBOX_HL FALSE #define INPUTBOX_BORDER_FG COLOR_BLACK #define INPUTBOX_BORDER_BG COLOR_WHITE #define INPUTBOX_BORDER_HL FALSE #define SEARCHBOX_FG COLOR_BLACK #define SEARCHBOX_BG COLOR_WHITE #define SEARCHBOX_HL FALSE #define SEARCHBOX_TITLE_FG COLOR_YELLOW #define SEARCHBOX_TITLE_BG COLOR_WHITE #define SEARCHBOX_TITLE_HL TRUE #define SEARCHBOX_BORDER_FG COLOR_WHITE #define SEARCHBOX_BORDER_BG COLOR_WHITE #define SEARCHBOX_BORDER_HL TRUE #define POSITION_INDICATOR_FG COLOR_YELLOW #define POSITION_INDICATOR_BG COLOR_WHITE #define POSITION_INDICATOR_HL TRUE #define MENUBOX_FG COLOR_BLACK #define MENUBOX_BG COLOR_WHITE #define MENUBOX_HL FALSE #define MENUBOX_BORDER_FG COLOR_WHITE #define MENUBOX_BORDER_BG COLOR_WHITE #define MENUBOX_BORDER_HL TRUE #define ITEM_FG COLOR_BLACK #define ITEM_BG COLOR_WHITE #define ITEM_HL FALSE #define ITEM_SELECTED_FG COLOR_WHITE #define ITEM_SELECTED_BG COLOR_BLUE #define ITEM_SELECTED_HL TRUE #define TAG_FG COLOR_YELLOW #define TAG_BG COLOR_WHITE #define TAG_HL TRUE #define TAG_SELECTED_FG COLOR_YELLOW #define TAG_SELECTED_BG COLOR_BLUE #define TAG_SELECTED_HL TRUE #define TAG_KEY_FG COLOR_YELLOW #define TAG_KEY_BG COLOR_WHITE #define TAG_KEY_HL TRUE #define TAG_KEY_SELECTED_FG COLOR_YELLOW #define TAG_KEY_SELECTED_BG COLOR_BLUE #define TAG_KEY_SELECTED_HL TRUE #define CHECK_FG COLOR_BLACK #define CHECK_BG COLOR_WHITE #define CHECK_HL FALSE #define CHECK_SELECTED_FG COLOR_WHITE #define CHECK_SELECTED_BG COLOR_BLUE #define CHECK_SELECTED_HL TRUE #define UARROW_FG COLOR_GREEN #define UARROW_BG COLOR_WHITE #define UARROW_HL TRUE #define DARROW_FG COLOR_GREEN #define DARROW_BG COLOR_WHITE #define DARROW_HL TRUE /* End of default color definitions */ #define C_ATTR(x,y) ((x ? A_BOLD : 0) | COLOR_PAIR((y))) #define COLOR_NAME_LEN 10 #define COLOR_COUNT 8 /* * Global variables */ extern int color_table[][3]; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/lxdialog/util.c������������������������������������������������������0000644�0000000�0000000�00000022453�12263563520�020033� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * util.c * * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "dialog.h" /* use colors by default? */ bool use_colors = 1; const char *backtitle = NULL; /* * Attribute values, default is for mono display */ chtype attributes[] = { A_NORMAL, /* screen_attr */ A_NORMAL, /* shadow_attr */ A_NORMAL, /* dialog_attr */ A_BOLD, /* title_attr */ A_NORMAL, /* border_attr */ A_REVERSE, /* button_active_attr */ A_DIM, /* button_inactive_attr */ A_REVERSE, /* button_key_active_attr */ A_BOLD, /* button_key_inactive_attr */ A_REVERSE, /* button_label_active_attr */ A_NORMAL, /* button_label_inactive_attr */ A_NORMAL, /* inputbox_attr */ A_NORMAL, /* inputbox_border_attr */ A_NORMAL, /* searchbox_attr */ A_BOLD, /* searchbox_title_attr */ A_NORMAL, /* searchbox_border_attr */ A_BOLD, /* position_indicator_attr */ A_NORMAL, /* menubox_attr */ A_NORMAL, /* menubox_border_attr */ A_NORMAL, /* item_attr */ A_REVERSE, /* item_selected_attr */ A_BOLD, /* tag_attr */ A_REVERSE, /* tag_selected_attr */ A_BOLD, /* tag_key_attr */ A_REVERSE, /* tag_key_selected_attr */ A_BOLD, /* check_attr */ A_REVERSE, /* check_selected_attr */ A_BOLD, /* uarrow_attr */ A_BOLD /* darrow_attr */ }; #include "colors.h" /* * Table of color values */ int color_table[][3] = { {SCREEN_FG, SCREEN_BG, SCREEN_HL}, {SHADOW_FG, SHADOW_BG, SHADOW_HL}, {DIALOG_FG, DIALOG_BG, DIALOG_HL}, {TITLE_FG, TITLE_BG, TITLE_HL}, {BORDER_FG, BORDER_BG, BORDER_HL}, {BUTTON_ACTIVE_FG, BUTTON_ACTIVE_BG, BUTTON_ACTIVE_HL}, {BUTTON_INACTIVE_FG, BUTTON_INACTIVE_BG, BUTTON_INACTIVE_HL}, {BUTTON_KEY_ACTIVE_FG, BUTTON_KEY_ACTIVE_BG, BUTTON_KEY_ACTIVE_HL}, {BUTTON_KEY_INACTIVE_FG, BUTTON_KEY_INACTIVE_BG, BUTTON_KEY_INACTIVE_HL}, {BUTTON_LABEL_ACTIVE_FG, BUTTON_LABEL_ACTIVE_BG, BUTTON_LABEL_ACTIVE_HL}, {BUTTON_LABEL_INACTIVE_FG, BUTTON_LABEL_INACTIVE_BG, BUTTON_LABEL_INACTIVE_HL}, {INPUTBOX_FG, INPUTBOX_BG, INPUTBOX_HL}, {INPUTBOX_BORDER_FG, INPUTBOX_BORDER_BG, INPUTBOX_BORDER_HL}, {SEARCHBOX_FG, SEARCHBOX_BG, SEARCHBOX_HL}, {SEARCHBOX_TITLE_FG, SEARCHBOX_TITLE_BG, SEARCHBOX_TITLE_HL}, {SEARCHBOX_BORDER_FG, SEARCHBOX_BORDER_BG, SEARCHBOX_BORDER_HL}, {POSITION_INDICATOR_FG, POSITION_INDICATOR_BG, POSITION_INDICATOR_HL}, {MENUBOX_FG, MENUBOX_BG, MENUBOX_HL}, {MENUBOX_BORDER_FG, MENUBOX_BORDER_BG, MENUBOX_BORDER_HL}, {ITEM_FG, ITEM_BG, ITEM_HL}, {ITEM_SELECTED_FG, ITEM_SELECTED_BG, ITEM_SELECTED_HL}, {TAG_FG, TAG_BG, TAG_HL}, {TAG_SELECTED_FG, TAG_SELECTED_BG, TAG_SELECTED_HL}, {TAG_KEY_FG, TAG_KEY_BG, TAG_KEY_HL}, {TAG_KEY_SELECTED_FG, TAG_KEY_SELECTED_BG, TAG_KEY_SELECTED_HL}, {CHECK_FG, CHECK_BG, CHECK_HL}, {CHECK_SELECTED_FG, CHECK_SELECTED_BG, CHECK_SELECTED_HL}, {UARROW_FG, UARROW_BG, UARROW_HL}, {DARROW_FG, DARROW_BG, DARROW_HL}, }; /* color_table */ /* * Set window to attribute 'attr' */ void attr_clear(WINDOW * win, int height, int width, chtype attr) { int i, j; wattrset(win, attr); for (i = 0; i < height; i++) { wmove(win, i, 0); for (j = 0; j < width; j++) waddch(win, ' '); } touchwin(win); } void dialog_clear(void) { attr_clear(stdscr, LINES, COLS, screen_attr); /* Display background title if it exists ... - SLH */ if (backtitle != NULL) { int i; wattrset(stdscr, screen_attr); mvwaddstr(stdscr, 0, 1, (char *)backtitle); wmove(stdscr, 1, 1); for (i = 1; i < COLS - 1; i++) waddch(stdscr, ACS_HLINE); } wnoutrefresh(stdscr); } /* * Do some initialization for dialog */ void init_dialog(void) { initscr(); /* Init curses */ keypad(stdscr, TRUE); cbreak(); noecho(); if (use_colors) /* Set up colors */ color_setup(); dialog_clear(); } /* * Setup for color display */ void color_setup(void) { int i; if (has_colors()) { /* Terminal supports color? */ start_color(); /* Initialize color pairs */ for (i = 0; i < ATTRIBUTE_COUNT; i++) init_pair(i + 1, color_table[i][0], color_table[i][1]); /* Setup color attributes */ for (i = 0; i < ATTRIBUTE_COUNT; i++) attributes[i] = C_ATTR(color_table[i][2], i + 1); } } /* * End using dialog functions. */ void end_dialog(void) { endwin(); } /* Print the title of the dialog. Center the title and truncate * tile if wider than dialog (- 2 chars). **/ void print_title(WINDOW *dialog, const char *title, int width) { if (title) { int tlen = MIN(width - 2, strlen(title)); wattrset(dialog, title_attr); mvwaddch(dialog, 0, (width - tlen) / 2 - 1, ' '); mvwaddnstr(dialog, 0, (width - tlen)/2, title, tlen); waddch(dialog, ' '); } } /* * Print a string of text in a window, automatically wrap around to the * next line if the string is too long to fit on one line. Newline * characters '\n' are replaced by spaces. We start on a new line * if there is no room for at least 4 nonblanks following a double-space. */ void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x) { int newl, cur_x, cur_y; int i, prompt_len, room, wlen; char tempstr[MAX_LEN + 1], *word, *sp, *sp2; strcpy(tempstr, prompt); prompt_len = strlen(tempstr); /* * Remove newlines */ for (i = 0; i < prompt_len; i++) { if (tempstr[i] == '\n') tempstr[i] = ' '; } if (prompt_len <= width - x * 2) { /* If prompt is short */ wmove(win, y, (width - prompt_len) / 2); waddstr(win, tempstr); } else { cur_x = x; cur_y = y; newl = 1; word = tempstr; while (word && *word) { sp = strchr(word, ' '); if (sp) *sp++ = 0; /* Wrap to next line if either the word does not fit, or it is the first word of a new sentence, and it is short, and the next word does not fit. */ room = width - cur_x; wlen = strlen(word); if (wlen > room || (newl && wlen < 4 && sp && wlen + 1 + strlen(sp) > room && (!(sp2 = strchr(sp, ' ')) || wlen + 1 + (sp2 - sp) > room))) { cur_y++; cur_x = x; } wmove(win, cur_y, cur_x); waddstr(win, word); getyx(win, cur_y, cur_x); cur_x++; if (sp && *sp == ' ') { cur_x++; /* double space */ while (*++sp == ' ') ; newl = 1; } else newl = 0; word = sp; } } } /* * Print a button */ void print_button(WINDOW * win, const char *label, int y, int x, int selected) { int i, temp; wmove(win, y, x); wattrset(win, selected ? button_active_attr : button_inactive_attr); waddstr(win, "<"); temp = strspn(label, " "); label += temp; wattrset(win, selected ? button_label_active_attr : button_label_inactive_attr); for (i = 0; i < temp; i++) waddch(win, ' '); wattrset(win, selected ? button_key_active_attr : button_key_inactive_attr); waddch(win, label[0]); wattrset(win, selected ? button_label_active_attr : button_label_inactive_attr); waddstr(win, (char *)label + 1); wattrset(win, selected ? button_active_attr : button_inactive_attr); waddstr(win, ">"); wmove(win, y, x + temp + 1); } /* * Draw a rectangular box with line drawing characters */ void draw_box(WINDOW * win, int y, int x, int height, int width, chtype box, chtype border) { int i, j; wattrset(win, 0); for (i = 0; i < height; i++) { wmove(win, y + i, x); for (j = 0; j < width; j++) if (!i && !j) waddch(win, border | ACS_ULCORNER); else if (i == height - 1 && !j) waddch(win, border | ACS_LLCORNER); else if (!i && j == width - 1) waddch(win, box | ACS_URCORNER); else if (i == height - 1 && j == width - 1) waddch(win, box | ACS_LRCORNER); else if (!i) waddch(win, border | ACS_HLINE); else if (i == height - 1) waddch(win, box | ACS_HLINE); else if (!j) waddch(win, border | ACS_VLINE); else if (j == width - 1) waddch(win, box | ACS_VLINE); else waddch(win, box | ' '); } } /* * Draw shadows along the right and bottom edge to give a more 3D look * to the boxes */ void draw_shadow(WINDOW * win, int y, int x, int height, int width) { int i; if (has_colors()) { /* Whether terminal supports color? */ wattrset(win, shadow_attr); wmove(win, y + height, x + 2); for (i = 0; i < width; i++) waddch(win, winch(win) & A_CHARTEXT); for (i = y + 1; i < y + height + 1; i++) { wmove(win, i, x + width); waddch(win, winch(win) & A_CHARTEXT); waddch(win, winch(win) & A_CHARTEXT); } wnoutrefresh(win); } } /* * Return the position of the first alphabetic character in a string. */ int first_alpha(const char *string, const char *exempt) { int i, in_paren = 0, c; for (i = 0; i < strlen(string); i++) { c = tolower(string[i]); if (strchr("<[(", c)) ++in_paren; if (strchr(">])", c) && in_paren > 0) --in_paren; if ((!in_paren) && isalpha(c) && strchr(exempt, c) == 0) return i; } return 0; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/lxdialog/yesno.c�����������������������������������������������������0000644�0000000�0000000�00000005060�12263563520�020206� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * yesno.c -- implements the yes/no box * * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "dialog.h" /* * Display termination buttons */ static void print_buttons(WINDOW * dialog, int height, int width, int selected) { int x = width / 2 - 10; int y = height - 2; print_button(dialog, " Yes ", y, x, selected == 0); print_button(dialog, " No ", y, x + 13, selected == 1); wmove(dialog, y, x + 1 + 13 * selected); wrefresh(dialog); } /* * Display a dialog box with two buttons - Yes and No */ int dialog_yesno(const char *title, const char *prompt, int height, int width) { int i, x, y, key = 0, button = 0; WINDOW *dialog; /* center dialog box on screen */ x = (COLS - width) / 2; y = (LINES - height) / 2; draw_shadow(stdscr, y, x, height, width); dialog = newwin(height, width, y, x); keypad(dialog, TRUE); draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); wattrset(dialog, border_attr); mvwaddch(dialog, height - 3, 0, ACS_LTEE); for (i = 0; i < width - 2; i++) waddch(dialog, ACS_HLINE); wattrset(dialog, dialog_attr); waddch(dialog, ACS_RTEE); print_title(dialog, title, width); wattrset(dialog, dialog_attr); print_autowrap(dialog, prompt, width - 2, 1, 3); print_buttons(dialog, height, width, 0); while (key != ESC) { key = wgetch(dialog); switch (key) { case 'Y': case 'y': delwin(dialog); return 0; case 'N': case 'n': delwin(dialog); return 1; case TAB: case KEY_LEFT: case KEY_RIGHT: button = ((key == KEY_LEFT ? --button : ++button) < 0) ? 1 : (button > 1 ? 0 : button); print_buttons(dialog, height, width, button); wrefresh(dialog); break; case ' ': case '\n': delwin(dialog); return button; case ESC: break; } } delwin(dialog); return -1; /* ESC pressed */ } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/lxdialog/check-lxdialog.sh�������������������������������������������0000644�0000000�0000000�00000003532�12263563520�022121� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Check ncurses compatibility # What library to link ldflags() { for ext in so a dylib ; do for lib in ncursesw ncurses curses ; do $cc -print-file-name=lib${lib}.${ext} | grep -q / if [ $? -eq 0 ]; then echo "-l${lib}" exit fi done done exit 1 } # Where is ncurses.h? ccflags() { if [ -f /usr/include/ncursesw/ncurses.h ]; then echo '-I/usr/include/ncursesw -DCURSES_LOC="<ncurses.h>"' elif [ -f /usr/include/ncursesw/curses.h ]; then echo '-I/usr/include/ncursesw -DCURSES_LOC="<ncursesw/curses.h>"' elif [ -f /usr/include/ncurses/ncurses.h ]; then echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"' elif [ -f /usr/include/ncurses/curses.h ]; then echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"' elif [ -f /usr/include/ncurses.h ]; then echo '-DCURSES_LOC="<ncurses.h>"' else echo '-DCURSES_LOC="<curses.h>"' fi } # Temp file, try to clean up after us tmp=.lxdialog.tmp trap "rm -f $tmp" 0 1 2 3 15 # Check if we can link to ncurses check() { $cc -xc - -o $tmp 2>/dev/null <<'EOF' #include CURSES_LOC main() {} EOF if [ $? != 0 ]; then echo " *** Unable to find the ncurses libraries or the" 1>&2 echo " *** required header files." 1>&2 echo " *** 'make menuconfig' requires the ncurses libraries." 1>&2 echo " *** " 1>&2 echo " *** Install ncurses (ncurses-devel) and try again." 1>&2 echo " *** " 1>&2 exit 1 fi } usage() { printf "Usage: $0 [-check compiler options|-ccflags|-ldflags compiler options]\n" } if [ $# -eq 0 ]; then usage exit 1 fi cc="" case "$1" in "-check") shift cc="$@" check ;; "-ccflags") ccflags ;; "-ldflags") shift cc="$@" ldflags ;; "*") usage exit 1 ;; esac ����������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/lxdialog/textbox.c���������������������������������������������������0000644�0000000�0000000�00000034773�12263563520�020563� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * textbox.c -- implements the text box * * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "dialog.h" static void back_lines(int n); static void print_page(WINDOW * win, int height, int width); static void print_line(WINDOW * win, int row, int width); static char *get_line(void); static void print_position(WINDOW * win, int height, int width); static int hscroll, fd, file_size, bytes_read; static int begin_reached = 1, end_reached, page_length; static char *buf, *page; /* * Display text from a file in a dialog box. */ int dialog_textbox(const char *title, const char *file, int height, int width) { int i, x, y, cur_x, cur_y, fpos, key = 0; int passed_end; WINDOW *dialog, *text; /* Open input file for reading */ if ((fd = open(file, O_RDONLY)) == -1) { endwin(); fprintf(stderr, "\nCan't open input file in dialog_textbox().\n"); exit(-1); } /* Get file size. Actually, 'file_size' is the real file size - 1, since it's only the last byte offset from the beginning */ if ((file_size = lseek(fd, 0, SEEK_END)) == -1) { endwin(); fprintf(stderr, "\nError getting file size in dialog_textbox().\n"); exit(-1); } /* Restore file pointer to beginning of file after getting file size */ if (lseek(fd, 0, SEEK_SET) == -1) { endwin(); fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n"); exit(-1); } /* Allocate space for read buffer */ if ((buf = malloc(BUF_SIZE + 1)) == NULL) { endwin(); fprintf(stderr, "\nCan't allocate memory in dialog_textbox().\n"); exit(-1); } if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) { endwin(); fprintf(stderr, "\nError reading file in dialog_textbox().\n"); exit(-1); } buf[bytes_read] = '\0'; /* mark end of valid data */ page = buf; /* page is pointer to start of page to be displayed */ /* center dialog box on screen */ x = (COLS - width) / 2; y = (LINES - height) / 2; draw_shadow(stdscr, y, x, height, width); dialog = newwin(height, width, y, x); keypad(dialog, TRUE); /* Create window for text region, used for scrolling text */ text = subwin(dialog, height - 4, width - 2, y + 1, x + 1); wattrset(text, dialog_attr); wbkgdset(text, dialog_attr & A_COLOR); keypad(text, TRUE); /* register the new window, along with its borders */ draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); wattrset(dialog, border_attr); mvwaddch(dialog, height - 3, 0, ACS_LTEE); for (i = 0; i < width - 2; i++) waddch(dialog, ACS_HLINE); wattrset(dialog, dialog_attr); wbkgdset(dialog, dialog_attr & A_COLOR); waddch(dialog, ACS_RTEE); print_title(dialog, title, width); print_button(dialog, " Exit ", height - 2, width / 2 - 4, TRUE); wnoutrefresh(dialog); getyx(dialog, cur_y, cur_x); /* Save cursor position */ /* Print first page of text */ attr_clear(text, height - 4, width - 2, dialog_attr); print_page(text, height - 4, width - 2); print_position(dialog, height, width); wmove(dialog, cur_y, cur_x); /* Restore cursor position */ wrefresh(dialog); while ((key != ESC) && (key != '\n')) { key = wgetch(dialog); switch (key) { case 'E': /* Exit */ case 'e': case 'X': case 'x': delwin(dialog); free(buf); close(fd); return 0; case 'g': /* First page */ case KEY_HOME: if (!begin_reached) { begin_reached = 1; /* First page not in buffer? */ if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { endwin(); fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n"); exit(-1); } if (fpos > bytes_read) { /* Yes, we have to read it in */ if (lseek(fd, 0, SEEK_SET) == -1) { endwin(); fprintf(stderr, "\nError moving file pointer in " "dialog_textbox().\n"); exit(-1); } if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) { endwin(); fprintf(stderr, "\nError reading file in dialog_textbox().\n"); exit(-1); } buf[bytes_read] = '\0'; } page = buf; print_page(text, height - 4, width - 2); print_position(dialog, height, width); wmove(dialog, cur_y, cur_x); /* Restore cursor position */ wrefresh(dialog); } break; case 'G': /* Last page */ case KEY_END: end_reached = 1; /* Last page not in buffer? */ if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { endwin(); fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n"); exit(-1); } if (fpos < file_size) { /* Yes, we have to read it in */ if (lseek(fd, -BUF_SIZE, SEEK_END) == -1) { endwin(); fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n"); exit(-1); } if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) { endwin(); fprintf(stderr, "\nError reading file in dialog_textbox().\n"); exit(-1); } buf[bytes_read] = '\0'; } page = buf + bytes_read; back_lines(height - 4); print_page(text, height - 4, width - 2); print_position(dialog, height, width); wmove(dialog, cur_y, cur_x); /* Restore cursor position */ wrefresh(dialog); break; case 'K': /* Previous line */ case 'k': case KEY_UP: if (!begin_reached) { back_lines(page_length + 1); /* We don't call print_page() here but use scrolling to ensure faster screen update. However, 'end_reached' and 'page_length' should still be updated, and 'page' should point to start of next page. This is done by calling get_line() in the following 'for' loop. */ scrollok(text, TRUE); wscrl(text, -1); /* Scroll text region down one line */ scrollok(text, FALSE); page_length = 0; passed_end = 0; for (i = 0; i < height - 4; i++) { if (!i) { /* print first line of page */ print_line(text, 0, width - 2); wnoutrefresh(text); } else /* Called to update 'end_reached' and 'page' */ get_line(); if (!passed_end) page_length++; if (end_reached && !passed_end) passed_end = 1; } print_position(dialog, height, width); wmove(dialog, cur_y, cur_x); /* Restore cursor position */ wrefresh(dialog); } break; case 'B': /* Previous page */ case 'b': case KEY_PPAGE: if (begin_reached) break; back_lines(page_length + height - 4); print_page(text, height - 4, width - 2); print_position(dialog, height, width); wmove(dialog, cur_y, cur_x); wrefresh(dialog); break; case 'J': /* Next line */ case 'j': case KEY_DOWN: if (!end_reached) { begin_reached = 0; scrollok(text, TRUE); scroll(text); /* Scroll text region up one line */ scrollok(text, FALSE); print_line(text, height - 5, width - 2); wnoutrefresh(text); print_position(dialog, height, width); wmove(dialog, cur_y, cur_x); /* Restore cursor position */ wrefresh(dialog); } break; case KEY_NPAGE: /* Next page */ case ' ': if (end_reached) break; begin_reached = 0; print_page(text, height - 4, width - 2); print_position(dialog, height, width); wmove(dialog, cur_y, cur_x); wrefresh(dialog); break; case '0': /* Beginning of line */ case 'H': /* Scroll left */ case 'h': case KEY_LEFT: if (hscroll <= 0) break; if (key == '0') hscroll = 0; else hscroll--; /* Reprint current page to scroll horizontally */ back_lines(page_length); print_page(text, height - 4, width - 2); wmove(dialog, cur_y, cur_x); wrefresh(dialog); break; case 'L': /* Scroll right */ case 'l': case KEY_RIGHT: if (hscroll >= MAX_LEN) break; hscroll++; /* Reprint current page to scroll horizontally */ back_lines(page_length); print_page(text, height - 4, width - 2); wmove(dialog, cur_y, cur_x); wrefresh(dialog); break; case ESC: break; } } delwin(dialog); free(buf); close(fd); return -1; /* ESC pressed */ } /* * Go back 'n' lines in text file. Called by dialog_textbox(). * 'page' will be updated to point to the desired line in 'buf'. */ static void back_lines(int n) { int i, fpos; begin_reached = 0; /* We have to distinguish between end_reached and !end_reached since at end of file, the line is not ended by a '\n'. The code inside 'if' basically does a '--page' to move one character backward so as to skip '\n' of the previous line */ if (!end_reached) { /* Either beginning of buffer or beginning of file reached? */ if (page == buf) { if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { endwin(); fprintf(stderr, "\nError moving file pointer in " "back_lines().\n"); exit(-1); } if (fpos > bytes_read) { /* Not beginning of file yet */ /* We've reached beginning of buffer, but not beginning of file yet, so read previous part of file into buffer. Note that we only move backward for BUF_SIZE/2 bytes, but not BUF_SIZE bytes to avoid re-reading again in print_page() later */ /* Really possible to move backward BUF_SIZE/2 bytes? */ if (fpos < BUF_SIZE / 2 + bytes_read) { /* No, move less then */ if (lseek(fd, 0, SEEK_SET) == -1) { endwin(); fprintf(stderr, "\nError moving file pointer in " "back_lines().\n"); exit(-1); } page = buf + fpos - bytes_read; } else { /* Move backward BUF_SIZE/2 bytes */ if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR) == -1) { endwin(); fprintf(stderr, "\nError moving file pointer " "in back_lines().\n"); exit(-1); } page = buf + BUF_SIZE / 2; } if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) { endwin(); fprintf(stderr, "\nError reading file in back_lines().\n"); exit(-1); } buf[bytes_read] = '\0'; } else { /* Beginning of file reached */ begin_reached = 1; return; } } if (*(--page) != '\n') { /* '--page' here */ /* Something's wrong... */ endwin(); fprintf(stderr, "\nInternal error in back_lines().\n"); exit(-1); } } /* Go back 'n' lines */ for (i = 0; i < n; i++) do { if (page == buf) { if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { endwin(); fprintf(stderr, "\nError moving file pointer in back_lines().\n"); exit(-1); } if (fpos > bytes_read) { /* Really possible to move backward BUF_SIZE/2 bytes? */ if (fpos < BUF_SIZE / 2 + bytes_read) { /* No, move less then */ if (lseek(fd, 0, SEEK_SET) == -1) { endwin(); fprintf(stderr, "\nError moving file pointer " "in back_lines().\n"); exit(-1); } page = buf + fpos - bytes_read; } else { /* Move backward BUF_SIZE/2 bytes */ if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR) == -1) { endwin(); fprintf(stderr, "\nError moving file pointer" " in back_lines().\n"); exit(-1); } page = buf + BUF_SIZE / 2; } if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) { endwin(); fprintf(stderr, "\nError reading file in " "back_lines().\n"); exit(-1); } buf[bytes_read] = '\0'; } else { /* Beginning of file reached */ begin_reached = 1; return; } } } while (*(--page) != '\n'); page++; } /* * Print a new page of text. Called by dialog_textbox(). */ static void print_page(WINDOW * win, int height, int width) { int i, passed_end = 0; page_length = 0; for (i = 0; i < height; i++) { print_line(win, i, width); if (!passed_end) page_length++; if (end_reached && !passed_end) passed_end = 1; } wnoutrefresh(win); } /* * Print a new line of text. Called by dialog_textbox() and print_page(). */ static void print_line(WINDOW * win, int row, int width) { char *line; line = get_line(); line += MIN(strlen(line), hscroll); /* Scroll horizontally */ wmove(win, row, 0); /* move cursor to correct line */ waddch(win, ' '); waddnstr(win, line, MIN(strlen(line), width - 2)); /* Clear 'residue' of previous line */ #if OLD_NCURSES { int i; int y, x; getyx(win, y, x); for (i = 0; i < width - x; i++) waddch(win, ' '); } #else wclrtoeol(win); #endif } /* * Return current line of text. Called by dialog_textbox() and print_line(). * 'page' should point to start of current line before calling, and will be * updated to point to start of next line. */ static char *get_line(void) { int i = 0, fpos; static char line[MAX_LEN + 1]; end_reached = 0; while (*page != '\n') { if (*page == '\0') { /* Either end of file or end of buffer reached */ if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { endwin(); fprintf(stderr, "\nError moving file pointer in " "get_line().\n"); exit(-1); } if (fpos < file_size) { /* Not end of file yet */ /* We've reached end of buffer, but not end of file yet, so read next part of file into buffer */ if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) { endwin(); fprintf(stderr, "\nError reading file in get_line().\n"); exit(-1); } buf[bytes_read] = '\0'; page = buf; } else { if (!end_reached) end_reached = 1; break; } } else if (i < MAX_LEN) line[i++] = *(page++); else { /* Truncate lines longer than MAX_LEN characters */ if (i == MAX_LEN) line[i++] = '\0'; page++; } } if (i <= MAX_LEN) line[i] = '\0'; if (!end_reached) page++; /* move pass '\n' */ return line; } /* * Print current position */ static void print_position(WINDOW * win, int height, int width) { int fpos, percent; if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { endwin(); fprintf(stderr, "\nError moving file pointer in print_position().\n"); exit(-1); } wattrset(win, position_indicator_attr); wbkgdset(win, position_indicator_attr & A_COLOR); percent = !file_size ? 100 : ((fpos - bytes_read + page - buf) * 100) / file_size; wmove(win, height - 3, width - 9); wprintw(win, "(%3d%%)", percent); } �����busybox-1.22.1/scripts/kconfig/lxdialog/BIG.FAT.WARNING���������������������������������������������0000644�0000000�0000000�00000000414�12263563520�020744� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This is NOT the official version of dialog. This version has been significantly modified from the original. It was used by the Linux kernel configuration script, and subsequently adapted for busybox. Please do not bother Savio Lam with questions about this program. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/Makefile�������������������������������������������������������������0000644�0000000�0000000�00000020574�12263563520�016551� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# =========================================================================== # Kernel configuration targets # These targets are used from top-level makefile PHONY += oldconfig xconfig gconfig menuconfig config silentoldconfig update-po-config xconfig: $(obj)/qconf $< Config.in gconfig: $(obj)/gconf $< Config.in menuconfig: $(obj)/mconf $(Q)$(MAKE) $(build)=scripts/kconfig/lxdialog $< Config.in config: $(obj)/conf $< Config.in # Mtime granularity problem. # It was observed that these commands: # make allnoconfig; sed -i -e '/CONFIG_TRUE/s/.*/CONFIG_TRUE=y/' .config; make # sometimes produce busybox with "true" applet still disabled. # This is caused by .config updated by sed having mtime which is still # equal to (not bigger than) include/autoconf.h's mtime, # and thus 2nd make does not regenerate include/autoconf.h. # Waiting for 1 second after non-interactive "make XXXXconfig" # prevents this from happening. # # We'd like to detect whether filesystem we are on has coarse mtimes, # but can't do it yet, bbox ls hasn't got --full-time. #MTIME_IS_COARSE:=@ls --full-time -ld | grep -F .000 >/dev/null MTIME_IS_COARSE:=@true oldconfig: $(obj)/conf $< -o Config.in $(MTIME_IS_COARSE) && sleep 1 silentoldconfig: $(obj)/conf $< -s Config.in $(MTIME_IS_COARSE) && sleep 1 update-po-config: $(obj)/kxgettext xgettext --default-domain=linux \ --add-comments --keyword=_ --keyword=N_ \ --files-from=scripts/kconfig/POTFILES.in \ --output scripts/kconfig/config.pot $(Q)ln -fs Kconfig_i386 arch/um/Kconfig_arch $(Q)for i in `ls arch/`; \ do \ scripts/kconfig/kxgettext arch/$$i/Kconfig \ | msguniq -o scripts/kconfig/linux_$${i}.pot; \ done $(Q)msgcat scripts/kconfig/config.pot \ `find scripts/kconfig/ -type f -name linux_*.pot` \ --output scripts/kconfig/linux_raw.pot $(Q)msguniq --sort-by-file scripts/kconfig/linux_raw.pot \ --output scripts/kconfig/linux.pot $(Q)rm -f arch/um/Kconfig_arch $(Q)rm -f scripts/kconfig/linux_*.pot scripts/kconfig/config.pot PHONY += randconfig allyesconfig allnoconfig allmodconfig defconfig randconfig: $(obj)/conf $< -r Config.in $(MTIME_IS_COARSE) && sleep 1 allyesconfig: $(obj)/conf $< -y Config.in $(MTIME_IS_COARSE) && sleep 1 allnoconfig: $(obj)/conf $< -n Config.in $(MTIME_IS_COARSE) && sleep 1 allmodconfig: $(obj)/conf $< -m Config.in $(MTIME_IS_COARSE) && sleep 1 defconfig: $(obj)/conf ifeq ($(KBUILD_DEFCONFIG),) $< -d Config.in else @echo *** Default configuration is based on '$(KBUILD_DEFCONFIG)' $(Q)$< -D $(KBUILD_DEFCONFIG) Config.in endif $(MTIME_IS_COARSE) && sleep 1 %_defconfig: $(obj)/conf $(Q)$< -D configs/$@ Config.in $(MTIME_IS_COARSE) && sleep 1 # Help text used by make help help: @echo ' config - Update current config utilising a line-oriented program' @echo ' menuconfig - Update current config utilising a menu based program' @echo ' xconfig - Update current config utilising a QT based front-end' @echo ' gconfig - Update current config utilising a GTK based front-end' @echo ' oldconfig - Update current config utilising a provided .config as base' @echo ' randconfig - New config with random answer to all options' @echo ' defconfig - New config with default answer to all options' @echo ' allmodconfig - New config selecting modules when possible' @echo ' allyesconfig - New config where all options are accepted with yes' @echo ' allnoconfig - New config where all options are answered with no' # =========================================================================== # Shared Makefile for the various kconfig executables: # conf: Used for defconfig, oldconfig and related targets # mconf: Used for the mconfig target. # Utilizes the lxdialog package # qconf: Used for the xconfig target # Based on QT which needs to be installed to compile it # gconf: Used for the gconfig target # Based on GTK which needs to be installed to compile it # object files used by all kconfig flavours hostprogs-y := conf mconf qconf gconf kxgettext conf-objs := conf.o zconf.tab.o mconf-objs := mconf.o zconf.tab.o kxgettext-objs := kxgettext.o zconf.tab.o ifeq ($(MAKECMDGOALS),xconfig) qconf-target := 1 endif ifeq ($(MAKECMDGOALS),gconfig) gconf-target := 1 endif ifeq ($(qconf-target),1) qconf-cxxobjs := qconf.o qconf-objs := kconfig_load.o zconf.tab.o endif ifeq ($(gconf-target),1) gconf-objs := gconf.o kconfig_load.o zconf.tab.o endif clean-files := lkc_defs.h qconf.moc .tmp_qtcheck \ .tmp_gtkcheck zconf.tab.c lex.zconf.c zconf.hash.c subdir- += lxdialog # Add environment specific flags HOST_EXTRACFLAGS += $(shell $(CONFIG_SHELL) $(srctree)/$(src)/check.sh $(HOSTCC) $(HOSTCFLAGS)) # generated files seem to need this to find local include files HOSTCFLAGS_lex.zconf.o := -I$(src) HOSTCFLAGS_zconf.tab.o := -I$(src) HOSTLOADLIBES_qconf = $(KC_QT_LIBS) -ldl HOSTCXXFLAGS_qconf.o = $(KC_QT_CFLAGS) -D LKC_DIRECT_LINK HOSTLOADLIBES_gconf = `pkg-config --libs gtk+-2.0 gmodule-2.0 libglade-2.0` -ldl HOSTCFLAGS_gconf.o = `pkg-config --cflags gtk+-2.0 gmodule-2.0 libglade-2.0` \ -D LKC_DIRECT_LINK $(obj)/qconf.o: $(obj)/.tmp_qtcheck ifeq ($(qconf-target),1) $(obj)/.tmp_qtcheck: $(src)/Makefile -include $(obj)/.tmp_qtcheck # QT needs some extra effort... $(obj)/.tmp_qtcheck: @set -e; echo " CHECK qt"; dir=""; pkg=""; \ pkg-config --exists qt 2> /dev/null && pkg=qt; \ pkg-config --exists qt-mt 2> /dev/null && pkg=qt-mt; \ if [ -n "$$pkg" ]; then \ cflags="\$$(shell pkg-config $$pkg --cflags)"; \ libs="\$$(shell pkg-config $$pkg --libs)"; \ moc="\$$(shell pkg-config $$pkg --variable=prefix)/bin/moc"; \ dir="$$(pkg-config $$pkg --variable=prefix)"; \ else \ for d in $$QTDIR /usr/share/qt* /usr/lib/qt*; do \ if [ -f $$d/include/qconfig.h ]; then dir=$$d; break; fi; \ done; \ if [ -z "$$dir" ]; then \ echo "*"; \ echo "* Unable to find the QT installation. Please make sure that"; \ echo "* the QT development package is correctly installed and"; \ echo "* either install pkg-config or set the QTDIR environment"; \ echo "* variable to the correct location."; \ echo "*"; \ false; \ fi; \ libpath=$$dir/lib; lib=qt; osdir=""; \ $(HOSTCXX) -print-multi-os-directory > /dev/null 2>&1 && \ osdir=x$$($(HOSTCXX) -print-multi-os-directory); \ test -d $$libpath/$$osdir && libpath=$$libpath/$$osdir; \ test -f $$libpath/libqt-mt.so && lib=qt-mt; \ cflags="-I$$dir/include"; \ libs="-L$$libpath -Wl,-rpath,$$libpath -l$$lib"; \ moc="$$dir/bin/moc"; \ fi; \ if [ ! -x $$dir/bin/moc -a -x /usr/bin/moc ]; then \ echo "*"; \ echo "* Unable to find $$dir/bin/moc, using /usr/bin/moc instead."; \ echo "*"; \ moc="/usr/bin/moc"; \ fi; \ echo "KC_QT_CFLAGS=$$cflags" > $@; \ echo "KC_QT_LIBS=$$libs" >> $@; \ echo "KC_QT_MOC=$$moc" >> $@ endif $(obj)/gconf.o: $(obj)/.tmp_gtkcheck ifeq ($(gconf-target),1) -include $(obj)/.tmp_gtkcheck # GTK needs some extra effort, too... $(obj)/.tmp_gtkcheck: @if `pkg-config --exists gtk+-2.0 gmodule-2.0 libglade-2.0`; then \ if `pkg-config --atleast-version=2.0.0 gtk+-2.0`; then \ touch $@; \ else \ echo "*"; \ echo "* GTK+ is present but version >= 2.0.0 is required."; \ echo "*"; \ false; \ fi \ else \ echo "*"; \ echo "* Unable to find the GTK+ installation. Please make sure that"; \ echo "* the GTK+ 2.0 development package is correctly installed..."; \ echo "* You need gtk+-2.0, glib-2.0 and libglade-2.0."; \ echo "*"; \ false; \ fi endif $(obj)/zconf.tab.o: $(obj)/lex.zconf.c $(obj)/zconf.hash.c $(obj)/kconfig_load.o: $(obj)/lkc_defs.h $(obj)/qconf.o: $(obj)/qconf.moc $(obj)/lkc_defs.h $(obj)/gconf.o: $(obj)/lkc_defs.h $(obj)/%.moc: $(src)/%.h $(KC_QT_MOC) -i $< -o $@ $(obj)/lkc_defs.h: $(src)/lkc_proto.h sed < $< > $@ 's/P(\([^,]*\),.*/#define \1 (\*\1_p)/' ### # The following requires flex/bison/gperf # By default we use the _shipped versions, uncomment the following line if # you are modifying the flex/bison src. # LKC_GENPARSER := 1 ifdef LKC_GENPARSER $(obj)/zconf.tab.c: $(src)/zconf.y $(obj)/lex.zconf.c: $(src)/zconf.l $(obj)/zconf.hash.c: $(src)/zconf.gperf %.tab.c: %.y bison -l -b $* -p $(notdir $*) $< cp $@ $@_shipped lex.%.c: %.l flex -L -P$(notdir $*) -o$@ $< cp $@ $@_shipped %.hash.c: %.gperf gperf < $< > $@ cp $@ $@_shipped endif ������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/expr.h���������������������������������������������������������������0000644�0000000�0000000�00000011421�12263563520�016227� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> * Released under the terms of the GNU GPL v2.0. */ #ifndef EXPR_H #define EXPR_H #ifdef __cplusplus extern "C" { #endif #include <stdio.h> #ifndef __cplusplus #include <stdbool.h> #endif struct file { struct file *next; struct file *parent; char *name; int lineno; int flags; }; #define FILE_BUSY 0x0001 #define FILE_SCANNED 0x0002 #define FILE_PRINTED 0x0004 typedef enum tristate { no, mod, yes } tristate; enum expr_type { E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_CHOICE, E_SYMBOL, E_RANGE }; union expr_data { struct expr *expr; struct symbol *sym; }; struct expr { enum expr_type type; union expr_data left, right; }; #define E_OR(dep1, dep2) (((dep1)>(dep2))?(dep1):(dep2)) #define E_AND(dep1, dep2) (((dep1)<(dep2))?(dep1):(dep2)) #define E_NOT(dep) (2-(dep)) struct expr_value { struct expr *expr; tristate tri; }; struct symbol_value { void *val; tristate tri; }; enum symbol_type { S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING, S_OTHER }; struct symbol { struct symbol *next; char *name; char *help; enum symbol_type type; struct symbol_value curr, user; tristate visible; int flags; struct property *prop; struct expr *dep, *dep2; struct expr_value rev_dep; }; #define for_all_symbols(i, sym) for (i = 0; i < 257; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) if (sym->type != S_OTHER) #define SYMBOL_YES 0x0001 #define SYMBOL_MOD 0x0002 #define SYMBOL_NO 0x0004 #define SYMBOL_CONST 0x0007 #define SYMBOL_CHECK 0x0008 #define SYMBOL_CHOICE 0x0010 #define SYMBOL_CHOICEVAL 0x0020 #define SYMBOL_PRINTED 0x0040 #define SYMBOL_VALID 0x0080 #define SYMBOL_OPTIONAL 0x0100 #define SYMBOL_WRITE 0x0200 #define SYMBOL_CHANGED 0x0400 #define SYMBOL_NEW 0x0800 #define SYMBOL_AUTO 0x1000 #define SYMBOL_CHECKED 0x2000 #define SYMBOL_WARNED 0x8000 #define SYMBOL_MAXLENGTH 256 #define SYMBOL_HASHSIZE 257 #define SYMBOL_HASHMASK 0xff enum prop_type { P_UNKNOWN, P_PROMPT, P_COMMENT, P_MENU, P_DEFAULT, P_CHOICE, P_SELECT, P_RANGE }; struct property { struct property *next; struct symbol *sym; enum prop_type type; const char *text; struct expr_value visible; struct expr *expr; struct menu *menu; struct file *file; int lineno; }; #define for_all_properties(sym, st, tok) \ for (st = sym->prop; st; st = st->next) \ if (st->type == (tok)) #define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT) #define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE) #define for_all_prompts(sym, st) \ for (st = sym->prop; st; st = st->next) \ if (st->text) struct menu { struct menu *next; struct menu *parent; struct menu *list; struct symbol *sym; struct property *prompt; struct expr *dep; unsigned int flags; //char *help; struct file *file; int lineno; void *data; }; #define MENU_CHANGED 0x0001 #define MENU_ROOT 0x0002 #ifndef SWIG extern struct file *file_list; extern struct file *current_file; struct file *lookup_file(const char *name); extern struct symbol symbol_yes, symbol_no, symbol_mod; extern struct symbol *modules_sym; extern int cdebug; struct expr *expr_alloc_symbol(struct symbol *sym); struct expr *expr_alloc_one(enum expr_type type, struct expr *ce); struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2); struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2); struct expr *expr_alloc_and(struct expr *e1, struct expr *e2); struct expr *expr_alloc_or(struct expr *e1, struct expr *e2); struct expr *expr_copy(struct expr *org); void expr_free(struct expr *e); int expr_eq(struct expr *e1, struct expr *e2); void expr_eliminate_eq(struct expr **ep1, struct expr **ep2); tristate expr_calc_value(struct expr *e); struct expr *expr_eliminate_yn(struct expr *e); struct expr *expr_trans_bool(struct expr *e); struct expr *expr_eliminate_dups(struct expr *e); struct expr *expr_transform(struct expr *e); int expr_contains_symbol(struct expr *dep, struct symbol *sym); bool expr_depends_symbol(struct expr *dep, struct symbol *sym); struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2); struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2); void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2); struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym); void expr_fprint(struct expr *e, FILE *out); struct gstr; /* forward */ void expr_gstr_print(struct expr *e, struct gstr *gs); static inline int expr_is_yes(struct expr *e) { return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes); } static inline int expr_is_no(struct expr *e) { return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no); } #endif #ifdef __cplusplus } #endif #endif /* EXPR_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/util.c���������������������������������������������������������������0000644�0000000�0000000�00000004327�12263563520�016230� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2002-2005 Roman Zippel <zippel@linux-m68k.org> * Copyright (C) 2002-2005 Sam Ravnborg <sam@ravnborg.org> * * Released under the terms of the GNU GPL v2.0. */ #include <string.h> #include "lkc.h" /* file already present in list? If not add it */ struct file *file_lookup(const char *name) { struct file *file; for (file = file_list; file; file = file->next) { if (!strcmp(name, file->name)) return file; } file = malloc(sizeof(*file)); memset(file, 0, sizeof(*file)); file->name = strdup(name); file->next = file_list; file_list = file; return file; } /* write a dependency file as used by kbuild to track dependencies */ int file_write_dep(const char *name) { struct file *file; FILE *out; if (!name) name = ".kconfig.d"; out = fopen("..config.tmp", "w"); if (!out) return 1; fprintf(out, "deps_config := \\\n"); for (file = file_list; file; file = file->next) { if (file->next) fprintf(out, "\t%s \\\n", file->name); else fprintf(out, "\t%s\n", file->name); } fprintf(out, "\n" ".config include/autoconf.h: $(deps_config)\n" "\n" "include/autoconf.h: .config\n" /* bbox */ "\n" "$(deps_config):\n"); fclose(out); rename("..config.tmp", name); return 0; } /* Allocate initial growable string */ struct gstr str_new(void) { struct gstr gs; gs.s = malloc(sizeof(char) * 64); gs.len = 16; strcpy(gs.s, "\0"); return gs; } /* Allocate and assign growable string */ struct gstr str_assign(const char *s) { struct gstr gs; gs.s = strdup(s); gs.len = strlen(s) + 1; return gs; } /* Free storage for growable string */ void str_free(struct gstr *gs) { if (gs->s) free(gs->s); gs->s = NULL; gs->len = 0; } /* Append to growable string */ void str_append(struct gstr *gs, const char *s) { size_t l = strlen(gs->s) + strlen(s) + 1; if (l > gs->len) { gs->s = realloc(gs->s, l); gs->len = l; } strcat(gs->s, s); } /* Append printf formatted string to growable string */ void str_printf(struct gstr *gs, const char *fmt, ...) { va_list ap; char s[10000]; /* big enough... */ va_start(ap, fmt); vsnprintf(s, sizeof(s), fmt, ap); str_append(gs, s); va_end(ap); } /* Retrieve value of growable string */ const char *str_get(struct gstr *gs) { return gs->s; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/confdata.c�����������������������������������������������������������0000644�0000000�0000000�00000033564�12263563520�017037� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> * Released under the terms of the GNU GPL v2.0. */ #include <sys/stat.h> #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> #define LKC_DIRECT_LINK #include "lkc.h" static void conf_warning(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); static const char *conf_filename; static int conf_lineno, conf_warnings, conf_unsaved; const char conf_def_filename[] = ".config"; const char conf_defname[] = "/dev/null"; //bbox const char *conf_confnames[] = { conf_def_filename, conf_defname, NULL, }; static void conf_warning(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); conf_warnings++; } static char *conf_expand_value(const char *in) { struct symbol *sym; const char *src; static char res_value[SYMBOL_MAXLENGTH]; char *dst, name[SYMBOL_MAXLENGTH]; res_value[0] = 0; dst = name; while ((src = strchr(in, '$'))) { strncat(res_value, in, src - in); src++; dst = name; while (isalnum(*src) || *src == '_') *dst++ = *src++; *dst = 0; sym = sym_lookup(name, 0); sym_calc_value(sym); strcat(res_value, sym_get_string_value(sym)); in = src; } strcat(res_value, in); return res_value; } char *conf_get_default_confname(void) { struct stat buf; static char *fullname = NULL; char *env, *name; name = conf_expand_value(conf_defname); env = getenv(SRCTREE); if (env) { fullname = realloc(fullname, strlen(env) + strlen(name) + 2); sprintf(fullname, "%s/%s", env, name); if (!stat(fullname, &buf)) return fullname; } return name; } int conf_read_simple(const char *name) { FILE *in = NULL; char line[1024]; char *p, *p2; struct symbol *sym; int i; if (name) { in = zconf_fopen(name); } else { const char **names = conf_confnames; while ((name = *names++)) { name = conf_expand_value(name); in = zconf_fopen(name); if (in) { printf(_("#\n" "# using defaults found in %s\n" "#\n"), name); break; } } } if (!in) return 1; conf_filename = name; conf_lineno = 0; conf_warnings = 0; conf_unsaved = 0; for_all_symbols(i, sym) { sym->flags |= SYMBOL_NEW | SYMBOL_CHANGED; if (sym_is_choice(sym)) sym->flags &= ~SYMBOL_NEW; sym->flags &= ~SYMBOL_VALID; switch (sym->type) { case S_INT: case S_HEX: case S_STRING: if (sym->user.val) free(sym->user.val); default: sym->user.val = NULL; sym->user.tri = no; } } while (fgets(line, sizeof(line), in)) { conf_lineno++; sym = NULL; switch (line[0]) { case '#': if (memcmp(line + 2, "CONFIG_", 7)) continue; p = strchr(line + 9, ' '); if (!p) continue; *p++ = 0; if (strncmp(p, "is not set", 10)) continue; sym = sym_find(line + 9); if (!sym) { conf_warning("trying to assign nonexistent symbol %s", line + 9); break; } else if (!(sym->flags & SYMBOL_NEW)) { conf_warning("trying to reassign symbol %s", sym->name); break; } switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: sym->user.tri = no; sym->flags &= ~SYMBOL_NEW; break; default: ; } break; case 'C': if (memcmp(line, "CONFIG_", 7)) { conf_warning("unexpected data"); continue; } p = strchr(line + 7, '='); if (!p) continue; *p++ = 0; p2 = strchr(p, '\n'); if (p2) *p2 = 0; sym = sym_find(line + 7); if (!sym) { conf_warning("trying to assign nonexistent symbol %s", line + 7); break; } else if (!(sym->flags & SYMBOL_NEW)) { conf_warning("trying to reassign symbol %s", sym->name); break; } switch (sym->type) { case S_TRISTATE: if (p[0] == 'm') { sym->user.tri = mod; sym->flags &= ~SYMBOL_NEW; break; } case S_BOOLEAN: if (p[0] == 'y') { sym->user.tri = yes; sym->flags &= ~SYMBOL_NEW; break; } if (p[0] == 'n') { sym->user.tri = no; sym->flags &= ~SYMBOL_NEW; break; } conf_warning("symbol value '%s' invalid for %s", p, sym->name); break; case S_STRING: if (*p++ != '"') break; for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) { if (*p2 == '"') { *p2 = 0; break; } memmove(p2, p2 + 1, strlen(p2)); } if (!p2) { conf_warning("invalid string found"); continue; } case S_INT: case S_HEX: if (sym_string_valid(sym, p)) { sym->user.val = strdup(p); sym->flags &= ~SYMBOL_NEW; } else { if (p[0]) /* bbox */ conf_warning("symbol value '%s' invalid for %s", p, sym->name); continue; } break; default: ; } break; case '\n': break; default: conf_warning("unexpected data"); continue; } if (sym && sym_is_choice_value(sym)) { struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); switch (sym->user.tri) { case no: break; case mod: if (cs->user.tri == yes) { conf_warning("%s creates inconsistent choice state", sym->name); cs->flags |= SYMBOL_NEW; } break; case yes: if (cs->user.tri != no) { conf_warning("%s creates inconsistent choice state", sym->name); cs->flags |= SYMBOL_NEW; } else cs->user.val = sym; break; } cs->user.tri = E_OR(cs->user.tri, sym->user.tri); } } fclose(in); if (modules_sym) sym_calc_value(modules_sym); return 0; } int conf_read(const char *name) { struct symbol *sym; struct property *prop; struct expr *e; int i; if (conf_read_simple(name)) return 1; for_all_symbols(i, sym) { sym_calc_value(sym); if (sym_is_choice(sym) || (sym->flags & SYMBOL_AUTO)) goto sym_ok; if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) { /* check that calculated value agrees with saved value */ switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: if (sym->user.tri != sym_get_tristate_value(sym)) break; if (!sym_is_choice(sym)) goto sym_ok; default: if (!strcmp(sym->curr.val, sym->user.val)) goto sym_ok; break; } } else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE)) /* no previous value and not saved */ goto sym_ok; conf_unsaved++; /* maybe print value in verbose mode... */ sym_ok: if (sym_has_value(sym) && !sym_is_choice_value(sym)) { if (sym->visible == no) sym->flags |= SYMBOL_NEW; switch (sym->type) { case S_STRING: case S_INT: case S_HEX: if (!sym_string_within_range(sym, sym->user.val)) { sym->flags |= SYMBOL_NEW; sym->flags &= ~SYMBOL_VALID; } default: break; } } if (!sym_is_choice(sym)) continue; prop = sym_get_choice_prop(sym); for (e = prop->expr; e; e = e->left.expr) if (e->right.sym->visible != no) sym->flags |= e->right.sym->flags & SYMBOL_NEW; } sym_change_count = conf_warnings || conf_unsaved; return 0; } int conf_write(const char *name) { FILE *out, *out_h; struct symbol *sym; struct menu *menu; const char *basename; char dirname[128], tmpname[128], newname[128]; int type, l; const char *str; time_t now; int use_timestamp = 1; char *env; dirname[0] = 0; if (name && name[0]) { struct stat st; char *slash; if (!stat(name, &st) && S_ISDIR(st.st_mode)) { strcpy(dirname, name); strcat(dirname, "/"); basename = conf_def_filename; } else if ((slash = strrchr(name, '/'))) { int size = slash - name + 1; memcpy(dirname, name, size); dirname[size] = 0; if (slash[1]) basename = slash + 1; else basename = conf_def_filename; } else basename = name; } else basename = conf_def_filename; sprintf(newname, "%s.tmpconfig.%d", dirname, (int)getpid()); out = fopen(newname, "w"); if (!out) return 1; out_h = NULL; if (!name) { out_h = fopen(".tmpconfig.h", "w"); if (!out_h) return 1; file_write_dep(NULL); } sym = sym_lookup("KERNELVERSION", 0); sym_calc_value(sym); time(&now); env = getenv("KCONFIG_NOTIMESTAMP"); if (env && *env) use_timestamp = 0; fprintf(out, _("#\n" "# Automatically generated make config: don't edit\n" "# Busybox version: %s\n" "%s%s" "#\n"), sym_get_string_value(sym), use_timestamp ? "# " : "", use_timestamp ? ctime(&now) : ""); if (out_h) { char buf[sizeof("#define AUTOCONF_TIMESTAMP " "\"YYYY-MM-DD HH:MM:SS some_timezone\"\n")]; buf[0] = '\0'; if (use_timestamp) { size_t ret = \ strftime(buf, sizeof(buf), "#define AUTOCONF_TIMESTAMP " "\"%Y-%m-%d %H:%M:%S %Z\"\n", localtime(&now)); /* if user has Factory timezone or some other odd install, the * %Z above will overflow the string leaving us with undefined * results ... so let's try again without the timezone. */ if (ret == 0) strftime(buf, sizeof(buf), "#define AUTOCONF_TIMESTAMP " "\"%Y-%m-%d %H:%M:%S\"\n", localtime(&now)); } else { /* bbox */ strcpy(buf, "#define AUTOCONF_TIMESTAMP \"\"\n"); } fprintf(out_h, "/*\n" " * Automatically generated C config: don't edit\n" " * Busybox version: %s\n" " */\n" "%s" "\n", sym_get_string_value(sym), buf); } if (!sym_change_count) sym_clear_all_valid(); menu = rootmenu.list; while (menu) { sym = menu->sym; if (!sym) { if (!menu_is_visible(menu)) goto next; str = menu_get_prompt(menu); fprintf(out, "\n" "#\n" "# %s\n" "#\n", str); if (out_h) fprintf(out_h, "\n" "/*\n" " * %s\n" " */\n", str); } else if (!(sym->flags & SYMBOL_CHOICE)) { sym_calc_value(sym); /* bbox: we want to see all syms if (!(sym->flags & SYMBOL_WRITE)) goto next; */ sym->flags &= ~SYMBOL_WRITE; type = sym->type; if (type == S_TRISTATE) { sym_calc_value(modules_sym); if (modules_sym->curr.tri == no) type = S_BOOLEAN; } switch (type) { case S_BOOLEAN: case S_TRISTATE: switch (sym_get_tristate_value(sym)) { case no: fprintf(out, "# CONFIG_%s is not set\n", sym->name); if (out_h) { fprintf(out_h, "#undef CONFIG_%s\n", sym->name); /* bbox */ fprintf(out_h, "#define ENABLE_%s 0\n", sym->name); fprintf(out_h, "#define IF_%s(...)\n", sym->name); fprintf(out_h, "#define IF_NOT_%s(...) __VA_ARGS__\n", sym->name); } break; case mod: fprintf(out, "CONFIG_%s=m\n", sym->name); if (out_h) fprintf(out_h, "#define CONFIG_%s_MODULE 1\n", sym->name); break; case yes: fprintf(out, "CONFIG_%s=y\n", sym->name); if (out_h) { fprintf(out_h, "#define CONFIG_%s 1\n", sym->name); /* bbox */ fprintf(out_h, "#define ENABLE_%s 1\n", sym->name); fprintf(out_h, "#ifdef MAKE_SUID\n"); fprintf(out_h, "# define IF_%s(...) __VA_ARGS__ \"CONFIG_%s\"\n", sym->name, sym->name); fprintf(out_h, "#else\n"); fprintf(out_h, "# define IF_%s(...) __VA_ARGS__\n", sym->name); fprintf(out_h, "#endif\n"); fprintf(out_h, "#define IF_NOT_%s(...)\n", sym->name); } break; } break; case S_STRING: // fix me str = sym_get_string_value(sym); fprintf(out, "CONFIG_%s=\"", sym->name); if (out_h) fprintf(out_h, "#define CONFIG_%s \"", sym->name); do { l = strcspn(str, "\"\\"); if (l) { fwrite(str, l, 1, out); if (out_h) fwrite(str, l, 1, out_h); } str += l; while (*str == '\\' || *str == '"') { fprintf(out, "\\%c", *str); if (out_h) fprintf(out_h, "\\%c", *str); str++; } } while (*str); fputs("\"\n", out); if (out_h) { fputs("\"\n", out_h); /* bbox */ fprintf(out_h, "#define ENABLE_%s 1\n", sym->name); fprintf(out_h, "#ifdef MAKE_SUID\n"); fprintf(out_h, "# define IF_%s(...) __VA_ARGS__ \"CONFIG_%s\"\n", sym->name, sym->name); fprintf(out_h, "#else\n"); fprintf(out_h, "# define IF_%s(...) __VA_ARGS__\n", sym->name); fprintf(out_h, "#endif\n"); fprintf(out_h, "#define IF_NOT_%s(...)\n", sym->name); } break; case S_HEX: str = sym_get_string_value(sym); if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) { fprintf(out, "CONFIG_%s=%s\n", sym->name, str); if (out_h) { fprintf(out_h, "#define CONFIG_%s 0x%s\n", sym->name, str); /* bbox */ fprintf(out_h, "#define ENABLE_%s 1\n", sym->name); fprintf(out_h, "#ifdef MAKE_SUID\n"); fprintf(out_h, "# define IF_%s(...) __VA_ARGS__ \"CONFIG_%s\"\n", sym->name, sym->name); fprintf(out_h, "#else\n"); fprintf(out_h, "# define IF_%s(...) __VA_ARGS__\n", sym->name); fprintf(out_h, "#endif\n"); fprintf(out_h, "#define IF_NOT_%s(...)\n", sym->name); } break; } case S_INT: str = sym_get_string_value(sym); if (!str[0]) str = "0"; fprintf(out, "CONFIG_%s=%s\n", sym->name, str); if (out_h) { fprintf(out_h, "#define CONFIG_%s %s\n", sym->name, str); /* bbox */ fprintf(out_h, "#define ENABLE_%s 1\n", sym->name); fprintf(out_h, "#ifdef MAKE_SUID\n"); fprintf(out_h, "# define IF_%s(...) __VA_ARGS__ \"CONFIG_%s\"\n", sym->name, sym->name); fprintf(out_h, "#else\n"); fprintf(out_h, "# define IF_%s(...) __VA_ARGS__\n", sym->name); fprintf(out_h, "#endif\n"); fprintf(out_h, "#define IF_NOT_%s(...)\n", sym->name); } break; } } next: if (menu->list) { menu = menu->list; continue; } if (menu->next) menu = menu->next; else while ((menu = menu->parent)) { if (menu->next) { menu = menu->next; break; } } } fclose(out); if (out_h) { fclose(out_h); rename(".tmpconfig.h", "include/autoconf.h"); } if (!name || basename != conf_def_filename) { if (!name) name = conf_def_filename; sprintf(tmpname, "%s.old", name); rename(name, tmpname); } sprintf(tmpname, "%s%s", dirname, basename); if (rename(newname, tmpname)) return 1; sym_change_count = 0; return 0; } ��������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/check.sh�������������������������������������������������������������0000755�0000000�0000000�00000000325�12263563520�016515� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Needed for systems without gettext $* -xc -o /dev/null - > /dev/null 2>&1 << EOF #include <libintl.h> int main() { gettext(""); return 0; } EOF if [ ! "$?" -eq "0" ]; then echo -DKBUILD_NO_NLS; fi �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/zconf.y��������������������������������������������������������������0000644�0000000�0000000�00000033566�12263563520�016427� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%{ /* * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> * Released under the terms of the GNU GPL v2.0. */ #include <ctype.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #define LKC_DIRECT_LINK #include "lkc.h" #define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) #define PRINTD 0x0001 #define DEBUG_PARSE 0x0002 int cdebug = PRINTD; extern int zconflex(void); static void zconfprint(const char *err, ...); static void zconf_error(const char *err, ...); static void zconferror(const char *err); static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken); struct symbol *symbol_hash[257]; static struct menu *current_menu, *current_entry; #define YYDEBUG 0 #if YYDEBUG #define YYERROR_VERBOSE #endif %} %expect 26 %union { char *string; struct file *file; struct symbol *symbol; struct expr *expr; struct menu *menu; struct kconf_id *id; } %token <id>T_MAINMENU %token <id>T_MENU %token <id>T_ENDMENU %token <id>T_SOURCE %token <id>T_CHOICE %token <id>T_ENDCHOICE %token <id>T_COMMENT %token <id>T_CONFIG %token <id>T_MENUCONFIG %token <id>T_HELP %token <string> T_HELPTEXT %token <id>T_IF %token <id>T_ENDIF %token <id>T_DEPENDS %token <id>T_REQUIRES %token <id>T_OPTIONAL %token <id>T_PROMPT %token <id>T_TYPE %token <id>T_DEFAULT %token <id>T_SELECT %token <id>T_RANGE %token <id>T_ON %token <string> T_WORD %token <string> T_WORD_QUOTE %token T_UNEQUAL %token T_CLOSE_PAREN %token T_OPEN_PAREN %token T_EOL %left T_OR %left T_AND %left T_EQUAL T_UNEQUAL %nonassoc T_NOT %type <string> prompt %type <symbol> symbol %type <expr> expr %type <expr> if_expr %type <id> end %type <id> option_name %type <menu> if_entry menu_entry choice_entry %destructor { fprintf(stderr, "%s:%d: missing end statement for this entry\n", $$->file->name, $$->lineno); if (current_menu == $$) menu_end_menu(); } if_entry menu_entry choice_entry %{ #include "zconf.hash.c" %} %% input: stmt_list; stmt_list: /* empty */ | stmt_list common_stmt | stmt_list choice_stmt | stmt_list menu_stmt | stmt_list T_MAINMENU prompt nl | stmt_list end { zconf_error("unexpected end statement"); } | stmt_list T_WORD error T_EOL { zconf_error("unknown statement \"%s\"", $2); } | stmt_list option_name error T_EOL { zconf_error("unexpected option \"%s\"", kconf_id_strings + $2->name); } | stmt_list error T_EOL { zconf_error("invalid statement"); } ; option_name: T_DEPENDS | T_PROMPT | T_TYPE | T_SELECT | T_OPTIONAL | T_RANGE | T_DEFAULT ; common_stmt: T_EOL | if_stmt | comment_stmt | config_stmt | menuconfig_stmt | source_stmt ; option_error: T_WORD error T_EOL { zconf_error("unknown option \"%s\"", $1); } | error T_EOL { zconf_error("invalid option"); } ; /* config/menuconfig entry */ config_entry_start: T_CONFIG T_WORD T_EOL { struct symbol *sym = sym_lookup($2, 0); sym->flags |= SYMBOL_OPTIONAL; menu_add_entry(sym); printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2); }; config_stmt: config_entry_start config_option_list { menu_end_entry(); printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); }; menuconfig_entry_start: T_MENUCONFIG T_WORD T_EOL { struct symbol *sym = sym_lookup($2, 0); sym->flags |= SYMBOL_OPTIONAL; menu_add_entry(sym); printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2); }; menuconfig_stmt: menuconfig_entry_start config_option_list { if (current_entry->prompt) current_entry->prompt->type = P_MENU; else zconfprint("warning: menuconfig statement without prompt"); menu_end_entry(); printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); }; config_option_list: /* empty */ | config_option_list config_option | config_option_list depends | config_option_list help | config_option_list option_error | config_option_list T_EOL ; config_option: T_TYPE prompt_stmt_opt T_EOL { menu_set_type($1->stype); printd(DEBUG_PARSE, "%s:%d:type(%u)\n", zconf_curname(), zconf_lineno(), $1->stype); }; config_option: T_PROMPT prompt if_expr T_EOL { menu_add_prompt(P_PROMPT, $2, $3); printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); }; config_option: T_DEFAULT expr if_expr T_EOL { menu_add_expr(P_DEFAULT, $2, $3); if ($1->stype != S_UNKNOWN) menu_set_type($1->stype); printd(DEBUG_PARSE, "%s:%d:default(%u)\n", zconf_curname(), zconf_lineno(), $1->stype); }; config_option: T_SELECT T_WORD if_expr T_EOL { menu_add_symbol(P_SELECT, sym_lookup($2, 0), $3); printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno()); }; config_option: T_RANGE symbol symbol if_expr T_EOL { menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4); printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); }; /* choice entry */ choice: T_CHOICE T_EOL { struct symbol *sym = sym_lookup(NULL, 0); sym->flags |= SYMBOL_CHOICE; menu_add_entry(sym); menu_add_expr(P_CHOICE, NULL, NULL); printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno()); }; choice_entry: choice choice_option_list { $$ = menu_add_menu(); }; choice_end: end { if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) { menu_end_menu(); printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno()); } }; choice_stmt: choice_entry choice_block choice_end ; choice_option_list: /* empty */ | choice_option_list choice_option | choice_option_list depends | choice_option_list help | choice_option_list T_EOL | choice_option_list option_error ; choice_option: T_PROMPT prompt if_expr T_EOL { menu_add_prompt(P_PROMPT, $2, $3); printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); }; choice_option: T_TYPE prompt_stmt_opt T_EOL { if ($1->stype == S_BOOLEAN || $1->stype == S_TRISTATE) { menu_set_type($1->stype); printd(DEBUG_PARSE, "%s:%d:type(%u)\n", zconf_curname(), zconf_lineno(), $1->stype); } else YYERROR; }; choice_option: T_OPTIONAL T_EOL { current_entry->sym->flags |= SYMBOL_OPTIONAL; printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno()); }; choice_option: T_DEFAULT T_WORD if_expr T_EOL { if ($1->stype == S_UNKNOWN) { menu_add_symbol(P_DEFAULT, sym_lookup($2, 0), $3); printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno()); } else YYERROR; }; choice_block: /* empty */ | choice_block common_stmt ; /* if entry */ if_entry: T_IF expr nl { printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); menu_add_entry(NULL); menu_add_dep($2); $$ = menu_add_menu(); }; if_end: end { if (zconf_endtoken($1, T_IF, T_ENDIF)) { menu_end_menu(); printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno()); } }; if_stmt: if_entry if_block if_end ; if_block: /* empty */ | if_block common_stmt | if_block menu_stmt | if_block choice_stmt ; /* menu entry */ menu: T_MENU prompt T_EOL { menu_add_entry(NULL); menu_add_prompt(P_MENU, $2, NULL); printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno()); }; menu_entry: menu depends_list { $$ = menu_add_menu(); }; menu_end: end { if (zconf_endtoken($1, T_MENU, T_ENDMENU)) { menu_end_menu(); printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno()); } }; menu_stmt: menu_entry menu_block menu_end ; menu_block: /* empty */ | menu_block common_stmt | menu_block menu_stmt | menu_block choice_stmt ; source_stmt: T_SOURCE prompt T_EOL { printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2); zconf_nextfile($2); }; /* comment entry */ comment: T_COMMENT prompt T_EOL { menu_add_entry(NULL); menu_add_prompt(P_COMMENT, $2, NULL); printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno()); }; comment_stmt: comment depends_list { menu_end_entry(); }; /* help option */ help_start: T_HELP T_EOL { printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); zconf_starthelp(); }; help: help_start T_HELPTEXT { current_entry->sym->help = $2; }; /* depends option */ depends_list: /* empty */ | depends_list depends | depends_list T_EOL | depends_list option_error ; depends: T_DEPENDS T_ON expr T_EOL { menu_add_dep($3); printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno()); } | T_DEPENDS expr T_EOL { menu_add_dep($2); printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno()); } | T_REQUIRES expr T_EOL { menu_add_dep($2); printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno()); }; /* prompt statement */ prompt_stmt_opt: /* empty */ | prompt if_expr { menu_add_prompt(P_PROMPT, $1, $2); }; prompt: T_WORD | T_WORD_QUOTE ; end: T_ENDMENU T_EOL { $$ = $1; } | T_ENDCHOICE T_EOL { $$ = $1; } | T_ENDIF T_EOL { $$ = $1; } ; nl: T_EOL | nl T_EOL ; if_expr: /* empty */ { $$ = NULL; } | T_IF expr { $$ = $2; } ; expr: symbol { $$ = expr_alloc_symbol($1); } | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); } | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); } | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; } | T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); } | expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); } | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); } ; symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); } | T_WORD_QUOTE { $$ = sym_lookup($1, 1); free($1); } ; %% void conf_parse(const char *name) { struct symbol *sym; int i; zconf_initscan(name); sym_init(); menu_init(); modules_sym = sym_lookup("MODULES", 0); rootmenu.prompt = menu_add_prompt(P_MENU, "Busybox Configuration", NULL); #if YYDEBUG if (getenv("ZCONF_DEBUG")) zconfdebug = 1; #endif zconfparse(); if (zconfnerrs) exit(1); menu_finalize(&rootmenu); for_all_symbols(i, sym) { sym_check_deps(sym); } sym_change_count = 1; } const char *zconf_tokenname(int token) { switch (token) { case T_MENU: return "menu"; case T_ENDMENU: return "endmenu"; case T_CHOICE: return "choice"; case T_ENDCHOICE: return "endchoice"; case T_IF: return "if"; case T_ENDIF: return "endif"; case T_DEPENDS: return "depends"; } return "<token>"; } static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken) { if (id->token != endtoken) { zconf_error("unexpected '%s' within %s block", kconf_id_strings + id->name, zconf_tokenname(starttoken)); zconfnerrs++; return false; } if (current_menu->file != current_file) { zconf_error("'%s' in different file than '%s'", kconf_id_strings + id->name, zconf_tokenname(starttoken)); fprintf(stderr, "%s:%d: location of the '%s'\n", current_menu->file->name, current_menu->lineno, zconf_tokenname(starttoken)); zconfnerrs++; return false; } return true; } static void zconfprint(const char *err, ...) { va_list ap; fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); va_start(ap, err); vfprintf(stderr, err, ap); va_end(ap); fprintf(stderr, "\n"); } static void zconf_error(const char *err, ...) { va_list ap; zconfnerrs++; fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); va_start(ap, err); vfprintf(stderr, err, ap); va_end(ap); fprintf(stderr, "\n"); } static void zconferror(const char *err) { #if YYDEBUG fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err); #endif } void print_quoted_string(FILE *out, const char *str) { const char *p; int len; putc('"', out); while ((p = strchr(str, '"'))) { len = p - str; if (len) fprintf(out, "%.*s", len, str); fputs("\\\"", out); str = p + 1; } fputs(str, out); putc('"', out); } void print_symbol(FILE *out, struct menu *menu) { struct symbol *sym = menu->sym; struct property *prop; if (sym_is_choice(sym)) fprintf(out, "choice\n"); else fprintf(out, "config %s\n", sym->name); switch (sym->type) { case S_BOOLEAN: fputs(" boolean\n", out); break; case S_TRISTATE: fputs(" tristate\n", out); break; case S_STRING: fputs(" string\n", out); break; case S_INT: fputs(" integer\n", out); break; case S_HEX: fputs(" hex\n", out); break; default: fputs(" ???\n", out); break; } for (prop = sym->prop; prop; prop = prop->next) { if (prop->menu != menu) continue; switch (prop->type) { case P_PROMPT: fputs(" prompt ", out); print_quoted_string(out, prop->text); if (!expr_is_yes(prop->visible.expr)) { fputs(" if ", out); expr_fprint(prop->visible.expr, out); } fputc('\n', out); break; case P_DEFAULT: fputs( " default ", out); expr_fprint(prop->expr, out); if (!expr_is_yes(prop->visible.expr)) { fputs(" if ", out); expr_fprint(prop->visible.expr, out); } fputc('\n', out); break; case P_CHOICE: fputs(" #choice value\n", out); break; default: fprintf(out, " unknown prop %d!\n", prop->type); break; } } if (sym->help) { int len = strlen(sym->help); while (sym->help[--len] == '\n') sym->help[len] = 0; fprintf(out, " help\n%s\n", sym->help); } fputc('\n', out); } void zconfdump(FILE *out) { struct property *prop; struct symbol *sym; struct menu *menu; menu = rootmenu.list; while (menu) { if ((sym = menu->sym)) print_symbol(out, menu); else if ((prop = menu->prompt)) { switch (prop->type) { case P_COMMENT: fputs("\ncomment ", out); print_quoted_string(out, prop->text); fputs("\n", out); break; case P_MENU: fputs("\nmenu ", out); print_quoted_string(out, prop->text); fputs("\n", out); break; default: ; } if (!expr_is_yes(prop->visible.expr)) { fputs(" depends ", out); expr_fprint(prop->visible.expr, out); fputc('\n', out); } fputs("\n", out); } if (menu->list) menu = menu->list; else if (menu->next) menu = menu->next; else while ((menu = menu->parent)) { if (menu->prompt && menu->prompt->type == P_MENU) fputs("\nendmenu\n", out); if (menu->next) { menu = menu->next; break; } } } } #include "lex.zconf.c" #include "util.c" #include "confdata.c" #include "expr.c" #include "symbol.c" #include "menu.c" ������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/expr.c���������������������������������������������������������������0000644�0000000�0000000�00000061623�12263563520�016233� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> * Released under the terms of the GNU GPL v2.0. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #define LKC_DIRECT_LINK #include "lkc.h" #define DEBUG_EXPR 0 struct expr *expr_alloc_symbol(struct symbol *sym) { struct expr *e = malloc(sizeof(*e)); memset(e, 0, sizeof(*e)); e->type = E_SYMBOL; e->left.sym = sym; return e; } struct expr *expr_alloc_one(enum expr_type type, struct expr *ce) { struct expr *e = malloc(sizeof(*e)); memset(e, 0, sizeof(*e)); e->type = type; e->left.expr = ce; return e; } struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2) { struct expr *e = malloc(sizeof(*e)); memset(e, 0, sizeof(*e)); e->type = type; e->left.expr = e1; e->right.expr = e2; return e; } struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2) { struct expr *e = malloc(sizeof(*e)); memset(e, 0, sizeof(*e)); e->type = type; e->left.sym = s1; e->right.sym = s2; return e; } struct expr *expr_alloc_and(struct expr *e1, struct expr *e2) { if (!e1) return e2; return e2 ? expr_alloc_two(E_AND, e1, e2) : e1; } struct expr *expr_alloc_or(struct expr *e1, struct expr *e2) { if (!e1) return e2; return e2 ? expr_alloc_two(E_OR, e1, e2) : e1; } struct expr *expr_copy(struct expr *org) { struct expr *e; if (!org) return NULL; e = malloc(sizeof(*org)); memcpy(e, org, sizeof(*org)); switch (org->type) { case E_SYMBOL: e->left = org->left; break; case E_NOT: e->left.expr = expr_copy(org->left.expr); break; case E_EQUAL: case E_UNEQUAL: e->left.sym = org->left.sym; e->right.sym = org->right.sym; break; case E_AND: case E_OR: case E_CHOICE: e->left.expr = expr_copy(org->left.expr); e->right.expr = expr_copy(org->right.expr); break; default: printf("can't copy type %d\n", e->type); free(e); e = NULL; break; } return e; } void expr_free(struct expr *e) { if (!e) return; switch (e->type) { case E_SYMBOL: break; case E_NOT: expr_free(e->left.expr); return; case E_EQUAL: case E_UNEQUAL: break; case E_OR: case E_AND: expr_free(e->left.expr); expr_free(e->right.expr); break; default: printf("how to free type %d?\n", e->type); break; } free(e); } static int trans_count; #define e1 (*ep1) #define e2 (*ep2) static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2) { if (e1->type == type) { __expr_eliminate_eq(type, &e1->left.expr, &e2); __expr_eliminate_eq(type, &e1->right.expr, &e2); return; } if (e2->type == type) { __expr_eliminate_eq(type, &e1, &e2->left.expr); __expr_eliminate_eq(type, &e1, &e2->right.expr); return; } if (e1->type == E_SYMBOL && e2->type == E_SYMBOL && e1->left.sym == e2->left.sym && (e1->left.sym->flags & (SYMBOL_YES|SYMBOL_NO))) return; if (!expr_eq(e1, e2)) return; trans_count++; expr_free(e1); expr_free(e2); switch (type) { case E_OR: e1 = expr_alloc_symbol(&symbol_no); e2 = expr_alloc_symbol(&symbol_no); break; case E_AND: e1 = expr_alloc_symbol(&symbol_yes); e2 = expr_alloc_symbol(&symbol_yes); break; default: ; } } void expr_eliminate_eq(struct expr **ep1, struct expr **ep2) { if (!e1 || !e2) return; switch (e1->type) { case E_OR: case E_AND: __expr_eliminate_eq(e1->type, ep1, ep2); default: ; } if (e1->type != e2->type) switch (e2->type) { case E_OR: case E_AND: __expr_eliminate_eq(e2->type, ep1, ep2); default: ; } e1 = expr_eliminate_yn(e1); e2 = expr_eliminate_yn(e2); } #undef e1 #undef e2 int expr_eq(struct expr *e1, struct expr *e2) { int res, old_count; if (e1->type != e2->type) return 0; switch (e1->type) { case E_EQUAL: case E_UNEQUAL: return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym; case E_SYMBOL: return e1->left.sym == e2->left.sym; case E_NOT: return expr_eq(e1->left.expr, e2->left.expr); case E_AND: case E_OR: e1 = expr_copy(e1); e2 = expr_copy(e2); old_count = trans_count; expr_eliminate_eq(&e1, &e2); res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL && e1->left.sym == e2->left.sym); expr_free(e1); expr_free(e2); trans_count = old_count; return res; case E_CHOICE: case E_RANGE: case E_NONE: /* panic */; } if (DEBUG_EXPR) { expr_fprint(e1, stdout); printf(" = "); expr_fprint(e2, stdout); printf(" ?\n"); } return 0; } struct expr *expr_eliminate_yn(struct expr *e) { struct expr *tmp; if (e) switch (e->type) { case E_AND: e->left.expr = expr_eliminate_yn(e->left.expr); e->right.expr = expr_eliminate_yn(e->right.expr); if (e->left.expr->type == E_SYMBOL) { if (e->left.expr->left.sym == &symbol_no) { expr_free(e->left.expr); expr_free(e->right.expr); e->type = E_SYMBOL; e->left.sym = &symbol_no; e->right.expr = NULL; return e; } else if (e->left.expr->left.sym == &symbol_yes) { free(e->left.expr); tmp = e->right.expr; *e = *(e->right.expr); free(tmp); return e; } } if (e->right.expr->type == E_SYMBOL) { if (e->right.expr->left.sym == &symbol_no) { expr_free(e->left.expr); expr_free(e->right.expr); e->type = E_SYMBOL; e->left.sym = &symbol_no; e->right.expr = NULL; return e; } else if (e->right.expr->left.sym == &symbol_yes) { free(e->right.expr); tmp = e->left.expr; *e = *(e->left.expr); free(tmp); return e; } } break; case E_OR: e->left.expr = expr_eliminate_yn(e->left.expr); e->right.expr = expr_eliminate_yn(e->right.expr); if (e->left.expr->type == E_SYMBOL) { if (e->left.expr->left.sym == &symbol_no) { free(e->left.expr); tmp = e->right.expr; *e = *(e->right.expr); free(tmp); return e; } else if (e->left.expr->left.sym == &symbol_yes) { expr_free(e->left.expr); expr_free(e->right.expr); e->type = E_SYMBOL; e->left.sym = &symbol_yes; e->right.expr = NULL; return e; } } if (e->right.expr->type == E_SYMBOL) { if (e->right.expr->left.sym == &symbol_no) { free(e->right.expr); tmp = e->left.expr; *e = *(e->left.expr); free(tmp); return e; } else if (e->right.expr->left.sym == &symbol_yes) { expr_free(e->left.expr); expr_free(e->right.expr); e->type = E_SYMBOL; e->left.sym = &symbol_yes; e->right.expr = NULL; return e; } } break; default: ; } return e; } /* * bool FOO!=n => FOO */ struct expr *expr_trans_bool(struct expr *e) { if (!e) return NULL; switch (e->type) { case E_AND: case E_OR: case E_NOT: e->left.expr = expr_trans_bool(e->left.expr); e->right.expr = expr_trans_bool(e->right.expr); break; case E_UNEQUAL: // FOO!=n -> FOO if (e->left.sym->type == S_TRISTATE) { if (e->right.sym == &symbol_no) { e->type = E_SYMBOL; e->right.sym = NULL; } } break; default: ; } return e; } /* * e1 || e2 -> ? */ struct expr *expr_join_or(struct expr *e1, struct expr *e2) { struct expr *tmp; struct symbol *sym1, *sym2; if (expr_eq(e1, e2)) return expr_copy(e1); if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) return NULL; if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) return NULL; if (e1->type == E_NOT) { tmp = e1->left.expr; if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) return NULL; sym1 = tmp->left.sym; } else sym1 = e1->left.sym; if (e2->type == E_NOT) { if (e2->left.expr->type != E_SYMBOL) return NULL; sym2 = e2->left.expr->left.sym; } else sym2 = e2->left.sym; if (sym1 != sym2) return NULL; if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) return NULL; if (sym1->type == S_TRISTATE) { if (e1->type == E_EQUAL && e2->type == E_EQUAL && ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) { // (a='y') || (a='m') -> (a!='n') return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no); } if (e1->type == E_EQUAL && e2->type == E_EQUAL && ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) { // (a='y') || (a='n') -> (a!='m') return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod); } if (e1->type == E_EQUAL && e2->type == E_EQUAL && ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) { // (a='m') || (a='n') -> (a!='y') return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes); } } if (sym1->type == S_BOOLEAN && sym1 == sym2) { if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) || (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL)) return expr_alloc_symbol(&symbol_yes); } if (DEBUG_EXPR) { printf("optimize ("); expr_fprint(e1, stdout); printf(") || ("); expr_fprint(e2, stdout); printf(")?\n"); } return NULL; } struct expr *expr_join_and(struct expr *e1, struct expr *e2) { struct expr *tmp; struct symbol *sym1, *sym2; if (expr_eq(e1, e2)) return expr_copy(e1); if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) return NULL; if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) return NULL; if (e1->type == E_NOT) { tmp = e1->left.expr; if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) return NULL; sym1 = tmp->left.sym; } else sym1 = e1->left.sym; if (e2->type == E_NOT) { if (e2->left.expr->type != E_SYMBOL) return NULL; sym2 = e2->left.expr->left.sym; } else sym2 = e2->left.sym; if (sym1 != sym2) return NULL; if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) return NULL; if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) || (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes)) // (a) && (a='y') -> (a='y') return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) || (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no)) // (a) && (a!='n') -> (a) return expr_alloc_symbol(sym1); if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) || (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod)) // (a) && (a!='m') -> (a='y') return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); if (sym1->type == S_TRISTATE) { if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) { // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' sym2 = e1->right.sym; if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) : expr_alloc_symbol(&symbol_no); } if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) { // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' sym2 = e2->right.sym; if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) : expr_alloc_symbol(&symbol_no); } if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) // (a!='y') && (a!='n') -> (a='m') return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod); if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) // (a!='y') && (a!='m') -> (a='n') return expr_alloc_comp(E_EQUAL, sym1, &symbol_no); if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) // (a!='m') && (a!='n') -> (a='m') return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) || (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) || (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) || (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes)) return NULL; } if (DEBUG_EXPR) { printf("optimize ("); expr_fprint(e1, stdout); printf(") && ("); expr_fprint(e2, stdout); printf(")?\n"); } return NULL; } static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2) { #define e1 (*ep1) #define e2 (*ep2) struct expr *tmp; if (e1->type == type) { expr_eliminate_dups1(type, &e1->left.expr, &e2); expr_eliminate_dups1(type, &e1->right.expr, &e2); return; } if (e2->type == type) { expr_eliminate_dups1(type, &e1, &e2->left.expr); expr_eliminate_dups1(type, &e1, &e2->right.expr); return; } if (e1 == e2) return; switch (e1->type) { case E_OR: case E_AND: expr_eliminate_dups1(e1->type, &e1, &e1); default: ; } switch (type) { case E_OR: tmp = expr_join_or(e1, e2); if (tmp) { expr_free(e1); expr_free(e2); e1 = expr_alloc_symbol(&symbol_no); e2 = tmp; trans_count++; } break; case E_AND: tmp = expr_join_and(e1, e2); if (tmp) { expr_free(e1); expr_free(e2); e1 = expr_alloc_symbol(&symbol_yes); e2 = tmp; trans_count++; } break; default: ; } #undef e1 #undef e2 } static void expr_eliminate_dups2(enum expr_type type, struct expr **ep1, struct expr **ep2) { #define e1 (*ep1) #define e2 (*ep2) struct expr *tmp, *tmp1, *tmp2; if (e1->type == type) { expr_eliminate_dups2(type, &e1->left.expr, &e2); expr_eliminate_dups2(type, &e1->right.expr, &e2); return; } if (e2->type == type) { expr_eliminate_dups2(type, &e1, &e2->left.expr); expr_eliminate_dups2(type, &e1, &e2->right.expr); } if (e1 == e2) return; switch (e1->type) { case E_OR: expr_eliminate_dups2(e1->type, &e1, &e1); // (FOO || BAR) && (!FOO && !BAR) -> n tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1))); tmp2 = expr_copy(e2); tmp = expr_extract_eq_and(&tmp1, &tmp2); if (expr_is_yes(tmp1)) { expr_free(e1); e1 = expr_alloc_symbol(&symbol_no); trans_count++; } expr_free(tmp2); expr_free(tmp1); expr_free(tmp); break; case E_AND: expr_eliminate_dups2(e1->type, &e1, &e1); // (FOO && BAR) || (!FOO || !BAR) -> y tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1))); tmp2 = expr_copy(e2); tmp = expr_extract_eq_or(&tmp1, &tmp2); if (expr_is_no(tmp1)) { expr_free(e1); e1 = expr_alloc_symbol(&symbol_yes); trans_count++; } expr_free(tmp2); expr_free(tmp1); expr_free(tmp); break; default: ; } #undef e1 #undef e2 } struct expr *expr_eliminate_dups(struct expr *e) { int oldcount; if (!e) return e; oldcount = trans_count; while (1) { trans_count = 0; switch (e->type) { case E_OR: case E_AND: expr_eliminate_dups1(e->type, &e, &e); expr_eliminate_dups2(e->type, &e, &e); default: ; } if (!trans_count) break; e = expr_eliminate_yn(e); } trans_count = oldcount; return e; } struct expr *expr_transform(struct expr *e) { struct expr *tmp; if (!e) return NULL; switch (e->type) { case E_EQUAL: case E_UNEQUAL: case E_SYMBOL: case E_CHOICE: break; default: e->left.expr = expr_transform(e->left.expr); e->right.expr = expr_transform(e->right.expr); } switch (e->type) { case E_EQUAL: if (e->left.sym->type != S_BOOLEAN) break; if (e->right.sym == &symbol_no) { e->type = E_NOT; e->left.expr = expr_alloc_symbol(e->left.sym); e->right.sym = NULL; break; } if (e->right.sym == &symbol_mod) { printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name); e->type = E_SYMBOL; e->left.sym = &symbol_no; e->right.sym = NULL; break; } if (e->right.sym == &symbol_yes) { e->type = E_SYMBOL; e->right.sym = NULL; break; } break; case E_UNEQUAL: if (e->left.sym->type != S_BOOLEAN) break; if (e->right.sym == &symbol_no) { e->type = E_SYMBOL; e->right.sym = NULL; break; } if (e->right.sym == &symbol_mod) { printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name); e->type = E_SYMBOL; e->left.sym = &symbol_yes; e->right.sym = NULL; break; } if (e->right.sym == &symbol_yes) { e->type = E_NOT; e->left.expr = expr_alloc_symbol(e->left.sym); e->right.sym = NULL; break; } break; case E_NOT: switch (e->left.expr->type) { case E_NOT: // !!a -> a tmp = e->left.expr->left.expr; free(e->left.expr); free(e); e = tmp; e = expr_transform(e); break; case E_EQUAL: case E_UNEQUAL: // !a='x' -> a!='x' tmp = e->left.expr; free(e); e = tmp; e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL; break; case E_OR: // !(a || b) -> !a && !b tmp = e->left.expr; e->type = E_AND; e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); tmp->type = E_NOT; tmp->right.expr = NULL; e = expr_transform(e); break; case E_AND: // !(a && b) -> !a || !b tmp = e->left.expr; e->type = E_OR; e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); tmp->type = E_NOT; tmp->right.expr = NULL; e = expr_transform(e); break; case E_SYMBOL: if (e->left.expr->left.sym == &symbol_yes) { // !'y' -> 'n' tmp = e->left.expr; free(e); e = tmp; e->type = E_SYMBOL; e->left.sym = &symbol_no; break; } if (e->left.expr->left.sym == &symbol_mod) { // !'m' -> 'm' tmp = e->left.expr; free(e); e = tmp; e->type = E_SYMBOL; e->left.sym = &symbol_mod; break; } if (e->left.expr->left.sym == &symbol_no) { // !'n' -> 'y' tmp = e->left.expr; free(e); e = tmp; e->type = E_SYMBOL; e->left.sym = &symbol_yes; break; } break; default: ; } break; default: ; } return e; } int expr_contains_symbol(struct expr *dep, struct symbol *sym) { if (!dep) return 0; switch (dep->type) { case E_AND: case E_OR: return expr_contains_symbol(dep->left.expr, sym) || expr_contains_symbol(dep->right.expr, sym); case E_SYMBOL: return dep->left.sym == sym; case E_EQUAL: case E_UNEQUAL: return dep->left.sym == sym || dep->right.sym == sym; case E_NOT: return expr_contains_symbol(dep->left.expr, sym); default: ; } return 0; } bool expr_depends_symbol(struct expr *dep, struct symbol *sym) { if (!dep) return false; switch (dep->type) { case E_AND: return expr_depends_symbol(dep->left.expr, sym) || expr_depends_symbol(dep->right.expr, sym); case E_SYMBOL: return dep->left.sym == sym; case E_EQUAL: if (dep->left.sym == sym) { if (dep->right.sym == &symbol_yes || dep->right.sym == &symbol_mod) return true; } break; case E_UNEQUAL: if (dep->left.sym == sym) { if (dep->right.sym == &symbol_no) return true; } break; default: ; } return false; } struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2) { struct expr *tmp = NULL; expr_extract_eq(E_AND, &tmp, ep1, ep2); if (tmp) { *ep1 = expr_eliminate_yn(*ep1); *ep2 = expr_eliminate_yn(*ep2); } return tmp; } struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2) { struct expr *tmp = NULL; expr_extract_eq(E_OR, &tmp, ep1, ep2); if (tmp) { *ep1 = expr_eliminate_yn(*ep1); *ep2 = expr_eliminate_yn(*ep2); } return tmp; } void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2) { #define e1 (*ep1) #define e2 (*ep2) if (e1->type == type) { expr_extract_eq(type, ep, &e1->left.expr, &e2); expr_extract_eq(type, ep, &e1->right.expr, &e2); return; } if (e2->type == type) { expr_extract_eq(type, ep, ep1, &e2->left.expr); expr_extract_eq(type, ep, ep1, &e2->right.expr); return; } if (expr_eq(e1, e2)) { *ep = *ep ? expr_alloc_two(type, *ep, e1) : e1; expr_free(e2); if (type == E_AND) { e1 = expr_alloc_symbol(&symbol_yes); e2 = expr_alloc_symbol(&symbol_yes); } else if (type == E_OR) { e1 = expr_alloc_symbol(&symbol_no); e2 = expr_alloc_symbol(&symbol_no); } } #undef e1 #undef e2 } struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym) { struct expr *e1, *e2; if (!e) { e = expr_alloc_symbol(sym); if (type == E_UNEQUAL) e = expr_alloc_one(E_NOT, e); return e; } switch (e->type) { case E_AND: e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); if (sym == &symbol_yes) e = expr_alloc_two(E_AND, e1, e2); if (sym == &symbol_no) e = expr_alloc_two(E_OR, e1, e2); if (type == E_UNEQUAL) e = expr_alloc_one(E_NOT, e); return e; case E_OR: e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); if (sym == &symbol_yes) e = expr_alloc_two(E_OR, e1, e2); if (sym == &symbol_no) e = expr_alloc_two(E_AND, e1, e2); if (type == E_UNEQUAL) e = expr_alloc_one(E_NOT, e); return e; case E_NOT: return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym); case E_UNEQUAL: case E_EQUAL: if (type == E_EQUAL) { if (sym == &symbol_yes) return expr_copy(e); if (sym == &symbol_mod) return expr_alloc_symbol(&symbol_no); if (sym == &symbol_no) return expr_alloc_one(E_NOT, expr_copy(e)); } else { if (sym == &symbol_yes) return expr_alloc_one(E_NOT, expr_copy(e)); if (sym == &symbol_mod) return expr_alloc_symbol(&symbol_yes); if (sym == &symbol_no) return expr_copy(e); } break; case E_SYMBOL: return expr_alloc_comp(type, e->left.sym, sym); case E_CHOICE: case E_RANGE: case E_NONE: /* panic */; } return NULL; } tristate expr_calc_value(struct expr *e) { tristate val1, val2; const char *str1, *str2; if (!e) return yes; switch (e->type) { case E_SYMBOL: sym_calc_value(e->left.sym); return e->left.sym->curr.tri; case E_AND: val1 = expr_calc_value(e->left.expr); val2 = expr_calc_value(e->right.expr); return E_AND(val1, val2); case E_OR: val1 = expr_calc_value(e->left.expr); val2 = expr_calc_value(e->right.expr); return E_OR(val1, val2); case E_NOT: val1 = expr_calc_value(e->left.expr); return E_NOT(val1); case E_EQUAL: sym_calc_value(e->left.sym); sym_calc_value(e->right.sym); str1 = sym_get_string_value(e->left.sym); str2 = sym_get_string_value(e->right.sym); return !strcmp(str1, str2) ? yes : no; case E_UNEQUAL: sym_calc_value(e->left.sym); sym_calc_value(e->right.sym); str1 = sym_get_string_value(e->left.sym); str2 = sym_get_string_value(e->right.sym); return !strcmp(str1, str2) ? no : yes; default: printf("expr_calc_value: %d?\n", e->type); return no; } } int expr_compare_type(enum expr_type t1, enum expr_type t2) { #if 0 return 1; #else if (t1 == t2) return 0; switch (t1) { case E_EQUAL: case E_UNEQUAL: if (t2 == E_NOT) return 1; case E_NOT: if (t2 == E_AND) return 1; case E_AND: if (t2 == E_OR) return 1; case E_OR: if (t2 == E_CHOICE) return 1; case E_CHOICE: if (t2 == 0) return 1; default: return -1; } printf("[%dgt%d?]", t1, t2); return 0; #endif } void expr_print(struct expr *e, void (*fn)(void *, const char *), void *data, int prevtoken) { if (!e) { fn(data, "y"); return; } if (expr_compare_type(prevtoken, e->type) > 0) fn(data, "("); switch (e->type) { case E_SYMBOL: if (e->left.sym->name) fn(data, e->left.sym->name); else fn(data, "<choice>"); break; case E_NOT: fn(data, "!"); expr_print(e->left.expr, fn, data, E_NOT); break; case E_EQUAL: fn(data, e->left.sym->name); fn(data, "="); fn(data, e->right.sym->name); break; case E_UNEQUAL: fn(data, e->left.sym->name); fn(data, "!="); fn(data, e->right.sym->name); break; case E_OR: expr_print(e->left.expr, fn, data, E_OR); fn(data, " || "); expr_print(e->right.expr, fn, data, E_OR); break; case E_AND: expr_print(e->left.expr, fn, data, E_AND); fn(data, " && "); expr_print(e->right.expr, fn, data, E_AND); break; case E_CHOICE: fn(data, e->right.sym->name); if (e->left.expr) { fn(data, " ^ "); expr_print(e->left.expr, fn, data, E_CHOICE); } break; case E_RANGE: fn(data, "["); fn(data, e->left.sym->name); fn(data, " "); fn(data, e->right.sym->name); fn(data, "]"); break; default: { char buf[32]; sprintf(buf, "<unknown type %d>", e->type); fn(data, buf); break; } } if (expr_compare_type(prevtoken, e->type) > 0) fn(data, ")"); } static void expr_print_file_helper(void *data, const char *str) { fwrite(str, strlen(str), 1, data); } void expr_fprint(struct expr *e, FILE *out) { expr_print(e, expr_print_file_helper, out, E_NONE); } static void expr_print_gstr_helper(void *data, const char *str) { str_append((struct gstr*)data, str); } void expr_gstr_print(struct expr *e, struct gstr *gs) { expr_print(e, expr_print_gstr_helper, gs, E_NONE); } �������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/zconf.hash.c_shipped�������������������������������������������������0000644�0000000�0000000�00000023132�12263563520�021023� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ANSI-C code produced by gperf version 3.0.1 */ /* Command-line: gperf */ /* Computed positions: -k'1,3' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ #error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>." #endif struct kconf_id; /* maximum key range = 45, duplicates = 0 */ #ifdef __GNUC__ __inline #else #ifdef __cplusplus inline #endif #endif static unsigned int kconf_id_hash (register const char *str, register unsigned int len) { static unsigned char asso_values[] = { 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 25, 10, 15, 0, 0, 5, 47, 0, 0, 47, 47, 0, 10, 0, 20, 20, 20, 5, 0, 0, 20, 47, 47, 20, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47 }; register int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[2]]; /*FALLTHROUGH*/ case 2: case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval; } struct kconf_id_strings_t { char kconf_id_strings_str2[sizeof("if")]; char kconf_id_strings_str3[sizeof("int")]; char kconf_id_strings_str4[sizeof("help")]; char kconf_id_strings_str5[sizeof("endif")]; char kconf_id_strings_str6[sizeof("select")]; char kconf_id_strings_str7[sizeof("endmenu")]; char kconf_id_strings_str8[sizeof("tristate")]; char kconf_id_strings_str9[sizeof("endchoice")]; char kconf_id_strings_str10[sizeof("range")]; char kconf_id_strings_str11[sizeof("string")]; char kconf_id_strings_str12[sizeof("default")]; char kconf_id_strings_str13[sizeof("def_bool")]; char kconf_id_strings_str14[sizeof("menu")]; char kconf_id_strings_str16[sizeof("def_boolean")]; char kconf_id_strings_str17[sizeof("def_tristate")]; char kconf_id_strings_str18[sizeof("mainmenu")]; char kconf_id_strings_str20[sizeof("menuconfig")]; char kconf_id_strings_str21[sizeof("config")]; char kconf_id_strings_str22[sizeof("on")]; char kconf_id_strings_str23[sizeof("hex")]; char kconf_id_strings_str26[sizeof("source")]; char kconf_id_strings_str27[sizeof("depends")]; char kconf_id_strings_str28[sizeof("optional")]; char kconf_id_strings_str31[sizeof("enable")]; char kconf_id_strings_str32[sizeof("comment")]; char kconf_id_strings_str33[sizeof("requires")]; char kconf_id_strings_str34[sizeof("bool")]; char kconf_id_strings_str37[sizeof("boolean")]; char kconf_id_strings_str41[sizeof("choice")]; char kconf_id_strings_str46[sizeof("prompt")]; }; static struct kconf_id_strings_t kconf_id_strings_contents = { "if", "int", "help", "endif", "select", "endmenu", "tristate", "endchoice", "range", "string", "default", "def_bool", "menu", "def_boolean", "def_tristate", "mainmenu", "menuconfig", "config", "on", "hex", "source", "depends", "optional", "enable", "comment", "requires", "bool", "boolean", "choice", "prompt" }; #define kconf_id_strings ((const char *) &kconf_id_strings_contents) #ifdef __GNUC__ __inline #endif struct kconf_id * kconf_id_lookup (register const char *str, register unsigned int len) { enum { TOTAL_KEYWORDS = 30, MIN_WORD_LENGTH = 2, MAX_WORD_LENGTH = 12, MIN_HASH_VALUE = 2, MAX_HASH_VALUE = 46 }; static struct kconf_id wordlist[] = { {-1}, {-1}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str2, T_IF, TF_COMMAND|TF_PARAM}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str3, T_TYPE, TF_COMMAND, S_INT}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str4, T_HELP, TF_COMMAND}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str5, T_ENDIF, TF_COMMAND}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str6, T_SELECT, TF_COMMAND}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str7, T_ENDMENU, TF_COMMAND}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str8, T_TYPE, TF_COMMAND, S_TRISTATE}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str9, T_ENDCHOICE, TF_COMMAND}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str10, T_RANGE, TF_COMMAND}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str11, T_TYPE, TF_COMMAND, S_STRING}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str12, T_DEFAULT, TF_COMMAND, S_UNKNOWN}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str13, T_DEFAULT, TF_COMMAND, S_BOOLEAN}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str14, T_MENU, TF_COMMAND}, {-1}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str16, T_DEFAULT, TF_COMMAND, S_BOOLEAN}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str17, T_DEFAULT, TF_COMMAND, S_TRISTATE}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str18, T_MAINMENU, TF_COMMAND}, {-1}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str20, T_MENUCONFIG, TF_COMMAND}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str21, T_CONFIG, TF_COMMAND}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str22, T_ON, TF_PARAM}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str23, T_TYPE, TF_COMMAND, S_HEX}, {-1}, {-1}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str26, T_SOURCE, TF_COMMAND}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str27, T_DEPENDS, TF_COMMAND}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str28, T_OPTIONAL, TF_COMMAND}, {-1}, {-1}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str31, T_SELECT, TF_COMMAND}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str32, T_COMMENT, TF_COMMAND}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str33, T_REQUIRES, TF_COMMAND}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str34, T_TYPE, TF_COMMAND, S_BOOLEAN}, {-1}, {-1}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str37, T_TYPE, TF_COMMAND, S_BOOLEAN}, {-1}, {-1}, {-1}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str41, T_CHOICE, TF_COMMAND}, {-1}, {-1}, {-1}, {-1}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str46, T_PROMPT, TF_COMMAND} }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register int key = kconf_id_hash (str, len); if (key <= MAX_HASH_VALUE && key >= 0) { register int o = wordlist[key].name; if (o >= 0) { register const char *s = o + kconf_id_strings; if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0') return &wordlist[key]; } } } return 0; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/kconfig/POTFILES.in����������������������������������������������������������0000644�0000000�0000000�00000000173�12263563520�016657� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������scripts/kconfig/mconf.c scripts/kconfig/conf.c scripts/kconfig/confdata.c scripts/kconfig/gconf.c scripts/kconfig/qconf.cc �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/checkhelp.awk����������������������������������������������������������������0000755�0000000�0000000�00000001655�12263563520�016125� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/awk -f # AWK script to check for missing help entries for config options # # Copyright (C) 2006 Bernhard Reutner-Fischer # # This file is distributed under the terms and conditions of the # MIT/X public licenses. See http://opensource.org/licenses/mit-license.html # and notice http://www.gnu.org/licenses/license-list.html#X11License /^choice/ { is_choice = 1; } /^endchoice/ { is_choice = 0; } /^config/ { pos++; conf[pos] = $2; file[pos] = FILENAME; if (is_choice) { help[pos] = 1; # do not warn about 'choice' config entries. } else { help[pos] = 0; } } /^[ \t]*help[ \t]*$/ { help[pos] = 1; } /^[ \t]*bool[ \t]*$/ { help[pos] = 1; # ignore options which are not selectable } BEGIN { pos = -1; is_choice = 0; } END { for (i = 0; i <= pos; i++) { # printf("%s: help for #%i '%s' == %i\n", file[i], i, conf[i], help[i]); if (help[i] == 0) { printf("%s: No helptext for '%s'\n", file[i], conf[i]); } } } �����������������������������������������������������������������������������������busybox-1.22.1/scripts/find_stray_common_vars�������������������������������������������������������0000755�0000000�0000000�00000000474�12263563520�020161� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Common variables are elusive, they don't show up in size output! # This script will show all commons in *.o, sorted by size find ! -path './scripts/*' -a ! -name built-in.o -a -name '*.o' \ | while read name; do b=`basename "$name"` nm "$name" | sed "s/^/$b: /" done | grep -i ' c ' | sort -k2 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/Makefile.build���������������������������������������������������������������0000644�0000000�0000000�00000024247�12263563520�016230� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# ========================================================================== # Building # ========================================================================== src := $(obj) PHONY := __build __build: # Read .config if it exist, otherwise ignore -include .config include scripts/Kbuild.include # The filename Kbuild has precedence over Makefile # bbox: we also try to include Kbuild file in obj tree first kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) include $(if $(wildcard $(src)/Kbuild), $(src)/Kbuild, \ $(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, \ $(kbuild-dir)/Makefile \ ) \ ) include scripts/Makefile.lib ifdef host-progs ifneq ($(hostprogs-y),$(host-progs)) $(warning kbuild: $(obj)/Makefile - Usage of host-progs is deprecated. Please replace with hostprogs-y!) hostprogs-y += $(host-progs) endif endif # Do not include host rules unles needed ifneq ($(hostprogs-y)$(hostprogs-m),) include scripts/Makefile.host endif ifneq ($(KBUILD_SRC),) # Create output directory if not already present _dummy := $(shell [ -d $(obj) ] || mkdir -p $(obj)) # Create directories for object files if directory does not exist # Needed when obj-y := dir/file.o syntax is used _dummy := $(foreach d,$(obj-dirs), $(shell [ -d $(d) ] || mkdir -p $(d))) endif ifdef EXTRA_TARGETS $(warning kbuild: $(obj)/Makefile - Usage of EXTRA_TARGETS is obsolete in 2.6. Please fix!) endif ifdef build-targets $(warning kbuild: $(obj)/Makefile - Usage of build-targets is obsolete in 2.6. Please fix!) endif ifdef export-objs $(warning kbuild: $(obj)/Makefile - Usage of export-objs is obsolete in 2.6. Please fix!) endif ifdef O_TARGET $(warning kbuild: $(obj)/Makefile - Usage of O_TARGET := $(O_TARGET) is obsolete in 2.6. Please fix!) endif ifdef L_TARGET $(error kbuild: $(obj)/Makefile - Use of L_TARGET is replaced by lib-y in 2.6. Please fix!) endif ifdef list-multi $(warning kbuild: $(obj)/Makefile - list-multi := $(list-multi) is obsolete in 2.6. Please fix!) endif ifndef obj $(warning kbuild: Makefile.build is included improperly) endif # =========================================================================== ifneq ($(strip $(lib-y) $(lib-m) $(lib-n) $(lib-)),) lib-target := $(obj)/lib.a endif ifneq ($(strip $(obj-y) $(obj-m) $(obj-n) $(obj-) $(lib-target)),) builtin-target := $(obj)/built-in.o endif # We keep a list of all modules in $(MODVERDIR) __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \ $(if $(KBUILD_MODULES),$(obj-m)) \ $(subdir-ym) $(always) @: # Linus' kernel sanity checking tool ifneq ($(KBUILD_CHECKSRC),0) ifeq ($(KBUILD_CHECKSRC),2) quiet_cmd_force_checksrc = CHECK $< cmd_force_checksrc = $(CHECK) $(CHECKFLAGS) $(c_flags) $< ; else quiet_cmd_checksrc = CHECK $< cmd_checksrc = $(CHECK) $(CHECKFLAGS) $(c_flags) $< ; endif endif # Compile C sources (.c) # --------------------------------------------------------------------------- # Default is built-in, unless we know otherwise modkern_cflags := $(CFLAGS_KERNEL) quiet_modtag := $(empty) $(empty) $(real-objs-m) : modkern_cflags := $(CFLAGS_MODULE) $(real-objs-m:.o=.i) : modkern_cflags := $(CFLAGS_MODULE) $(real-objs-m:.o=.s) : modkern_cflags := $(CFLAGS_MODULE) $(real-objs-m:.o=.lst): modkern_cflags := $(CFLAGS_MODULE) $(real-objs-m) : quiet_modtag := [M] $(real-objs-m:.o=.i) : quiet_modtag := [M] $(real-objs-m:.o=.s) : quiet_modtag := [M] $(real-objs-m:.o=.lst): quiet_modtag := [M] $(obj-m) : quiet_modtag := [M] # Default for not multi-part modules modname = $(*F) $(multi-objs-m) : modname = $(modname-multi) $(multi-objs-m:.o=.i) : modname = $(modname-multi) $(multi-objs-m:.o=.s) : modname = $(modname-multi) $(multi-objs-m:.o=.lst) : modname = $(modname-multi) $(multi-objs-y) : modname = $(modname-multi) $(multi-objs-y:.o=.i) : modname = $(modname-multi) $(multi-objs-y:.o=.s) : modname = $(modname-multi) $(multi-objs-y:.o=.lst) : modname = $(modname-multi) quiet_cmd_cc_s_c = CC $(quiet_modtag) $@ cmd_cc_s_c = $(CC) $(c_flags) -fverbose-asm -S -o $@ $< %.s: %.c FORCE $(call if_changed_dep,cc_s_c) quiet_cmd_cc_i_c = CPP $(quiet_modtag) $@ cmd_cc_i_c = $(CPP) $(c_flags) -o $@ $< %.i: %.c FORCE $(call if_changed_dep,cc_i_c) # C (.c) files # The C file is compiled and updated dependency information is generated. # (See cmd_cc_o_c + relevant part of rule_cc_o_c) quiet_cmd_cc_o_c = CC $(quiet_modtag) $@ ifndef CONFIG_MODVERSIONS cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< else # When module versioning is enabled the following steps are executed: # o compile a .tmp_<file>.o from <file>.c # o if .tmp_<file>.o doesn't contain a __ksymtab version, i.e. does # not export symbols, we just rename .tmp_<file>.o to <file>.o and # are done. # o otherwise, we calculate symbol versions using the good old # genksyms on the preprocessed source and postprocess them in a way # that they are usable as a linker script # o generate <file>.o from .tmp_<file>.o using the linker to # replace the unresolved symbols __crc_exported_symbol with # the actual value of the checksum generated by genksyms cmd_cc_o_c = $(CC) $(c_flags) -c -o $(@D)/.tmp_$(@F) $< cmd_modversions = \ if $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then \ $(CPP) -D__GENKSYMS__ $(c_flags) $< \ | $(GENKSYMS) -a $(ARCH) \ > $(@D)/.tmp_$(@F:.o=.ver); \ \ $(LD) $(LDFLAGS) -r -o $@ $(@D)/.tmp_$(@F) \ -T $(@D)/.tmp_$(@F:.o=.ver); \ rm -f $(@D)/.tmp_$(@F) $(@D)/.tmp_$(@F:.o=.ver); \ else \ mv -f $(@D)/.tmp_$(@F) $@; \ fi; endif define rule_cc_o_c $(call echo-cmd,checksrc) $(cmd_checksrc) \ $(call echo-cmd,cc_o_c) $(cmd_cc_o_c); \ $(cmd_modversions) \ scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' > $(@D)/.$(@F).tmp; \ rm -f $(depfile); \ mv -f $(@D)/.$(@F).tmp $(@D)/.$(@F).cmd endef # Built-in and composite module parts %.o: %.c FORCE $(call cmd,force_checksrc) $(call if_changed_rule,cc_o_c) # Single-part modules are special since we need to mark them in $(MODVERDIR) $(single-used-m): %.o: %.c FORCE $(call cmd,force_checksrc) $(call if_changed_rule,cc_o_c) @{ echo $(@:.o=.ko); echo $@; } > $(MODVERDIR)/$(@F:.o=.mod) quiet_cmd_cc_lst_c = MKLST $@ cmd_cc_lst_c = $(CC) $(c_flags) -g -c -o $*.o $< && \ $(CONFIG_SHELL) $(srctree)/scripts/makelst $*.o \ System.map $(OBJDUMP) > $@ %.lst: %.c FORCE $(call if_changed_dep,cc_lst_c) # Compile assembler sources (.S) # --------------------------------------------------------------------------- modkern_aflags := $(AFLAGS_KERNEL) $(real-objs-m) : modkern_aflags := $(AFLAGS_MODULE) $(real-objs-m:.o=.s): modkern_aflags := $(AFLAGS_MODULE) quiet_cmd_as_s_S = CPP $(quiet_modtag) $@ cmd_as_s_S = $(CPP) $(a_flags) -o $@ $< %.s: %.S FORCE $(call if_changed_dep,as_s_S) quiet_cmd_as_o_S = AS $(quiet_modtag) $@ cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< %.o: %.S FORCE $(call if_changed_dep,as_o_S) targets += $(real-objs-y) $(real-objs-m) $(lib-y) targets += $(extra-y) $(MAKECMDGOALS) $(always) # Linker scripts preprocessor (.lds.S -> .lds) # --------------------------------------------------------------------------- quiet_cmd_cpp_lds_S = LDS $@ cmd_cpp_lds_S = $(CPP) $(cpp_flags) -D__ASSEMBLY__ -o $@ $< %.lds: %.lds.S FORCE $(call if_changed_dep,cpp_lds_S) # Build the compiled-in targets # --------------------------------------------------------------------------- # To build objects in subdirs, we need to descend into the directories $(sort $(subdir-obj-y)): $(subdir-ym) ; # # Rule to compile a set of .o files into one .o file # ifdef builtin-target quiet_cmd_link_o_target = LD $@ # If the list of objects to link is empty, just create an empty built-in.o cmd_link_o_target = $(if $(strip $(obj-y)),\ $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^),\ rm -f $@; $(AR) rcs $@) $(builtin-target): $(obj-y) FORCE $(call if_changed,link_o_target) targets += $(builtin-target) endif # builtin-target # # Rule to compile a set of .o files into one .a file # ifdef lib-target quiet_cmd_link_l_target = AR $@ cmd_link_l_target = rm -f $@; $(AR) $(EXTRA_ARFLAGS) rcs $@ $(lib-y) $(lib-target): $(lib-y) FORCE $(call if_changed,link_l_target) targets += $(lib-target) endif # # Rule to link composite objects # # Composite objects are specified in kbuild makefile as follows: # <composite-object>-objs := <list of .o files> # or # <composite-object>-y := <list of .o files> link_multi_deps = \ $(filter $(addprefix $(obj)/, \ $($(subst $(obj)/,,$(@:.o=-objs))) \ $($(subst $(obj)/,,$(@:.o=-y)))), $^) quiet_cmd_link_multi-y = LD $@ cmd_link_multi-y = $(LD) $(ld_flags) -r -o $@ $(link_multi_deps) quiet_cmd_link_multi-m = LD [M] $@ cmd_link_multi-m = $(LD) $(ld_flags) $(LDFLAGS_MODULE) -o $@ $(link_multi_deps) # We would rather have a list of rules like # foo.o: $(foo-objs) # but that's not so easy, so we rather make all composite objects depend # on the set of all their parts $(multi-used-y) : %.o: $(multi-objs-y) FORCE $(call if_changed,link_multi-y) $(multi-used-m) : %.o: $(multi-objs-m) FORCE $(call if_changed,link_multi-m) @{ echo $(@:.o=.ko); echo $(link_multi_deps); } > $(MODVERDIR)/$(@F:.o=.mod) targets += $(multi-used-y) $(multi-used-m) # Descending # --------------------------------------------------------------------------- PHONY += $(subdir-ym) $(subdir-ym): $(Q)$(MAKE) $(build)=$@ # Add FORCE to the prequisites of a target to force it to be always rebuilt. # --------------------------------------------------------------------------- PHONY += FORCE FORCE: # Read all saved command lines and dependencies for the $(targets) we # may be building above, using $(if_changed{,_dep}). As an # optimization, we don't need to read them if the target does not # exist, we will rebuild anyway in that case. targets := $(wildcard $(sort $(targets))) cmd_files := $(wildcard $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd)) ifneq ($(cmd_files),) include $(cmd_files) endif # Declare the contents of the .PHONY variable as phony. We keep that # information in a variable se we can use it in if_changed and friends. .PHONY: $(PHONY) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/mkdiff_obj_bloat�������������������������������������������������������������0000755�0000000�0000000�00000001152�12263563520�016661� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh test -d "$1" || exit 1 test -d "$2" || exit 1 { ( cd "$1" || exit 1 find -name '*.o' -o -name '*.os' # -o -name '*.so' ) ( cd "$2" || exit 1 find -name '*.o' -o -name '*.os' # -o -name '*.so' ) } | sed 's:^\./::' | sort | uniq | \ tee LST | \ ( IFS='' while read -r oname; do if ! test -f "$1/$oname"; then echo "Only $2/$oname" continue fi if ! test -f "$2/$oname"; then echo "Only $1/$oname" continue fi $1/scripts/bloat-o-meter $1/$oname $2/$oname | grep 'otal: 0 byte' >/dev/null && continue $1/scripts/bloat-o-meter $1/$oname $2/$oname size $1/$oname $2/$oname echo done ) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/mkconfigs��������������������������������������������������������������������0000755�0000000�0000000�00000004233�12263563520�015371� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # # Copyright (C) 2002 Khalid Aziz <khalid_aziz at hp.com> # Copyright (C) 2002 Randy Dunlap <rddunlap at osdl.org> # Copyright (C) 2002 Al Stone <ahs3 at fc.hp.com> # Copyright (C) 2002 Hewlett-Packard Company # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Busybox version by Matteo Croce <3297627799 at wind.it> # # Rules to generate bbconfigopts.h from .config: # - Retain lines that begin with "CONFIG_" # - Retain lines that begin with "# CONFIG_" # - lines that use double-quotes must \\-escape-quote them config=.config { echo "\ #ifndef _BBCONFIGOPTS_H #define _BBCONFIGOPTS_H /* * busybox configuration settings. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * This file is generated automatically by scripts/mkconfigs. * Do not edit. */ static const char bbconfig_config[] ALIGN1 =" grep -e '^# CONFIG_' -e '^CONFIG_' "$config" \ | sed -e 's/\"/\\\"/g' -e 's/^/"/' -e 's/$/\\n"/' echo ";" echo "#endif" } >"$1" { echo "\ #ifndef _BBCONFIGOPTS_BZ2_H #define _BBCONFIGOPTS_BZ2_H /* * busybox configuration settings. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * This file is generated automatically by scripts/mkconfigs. * Do not edit. */ static const char bbconfig_config_bz2[] ALIGN1 = {" grep -e '^# CONFIG_' -e '^CONFIG_' "$config" \ | bzip2 -1 | dd bs=2 skip=1 2>/dev/null \ | od -v -b \ | sed -e 's/^[^ ]*//' \ -e 's/ //g' \ -e '/^$/d' \ -e 's/\(...\)/0\1,/g' echo "};" echo "#endif" } >"$2" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/scripts/test_make_clean��������������������������������������������������������������0000755�0000000�0000000�00000000471�12263563520�016527� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh b=`basename $PWD` test "${b#busybox}" != "$b" || { echo "Must be run in busybox tree"; exit 1; } cd .. cp -pPR "$b" busybox.$$.test_tree cd busybox.$$.test_tree make defconfig make $MAKEOPTS make clean cd .. diff -urp "$b" busybox.$$.test_tree >busybox.$$.test_tree.diff cat busybox.$$.test_tree.diff �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/����������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�013605� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/����������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�016115� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/dhcp_if_pinger/�������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�021055� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/dhcp_if_pinger/run����������������������������������������������0000755�0000000�0000000�00000001073�12263563520�021613� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh delay=67 if=${PWD##*/dhcp_} if=${if%%_pinger} if test -f "$0.log"; then tail -999 "$0.log" >"$0.log.new" mv "$0.log.new" "$0.log" fi test -f "/var/service/dhcp_$if/dhcp_$if.out" || exec env - sleep "$delay" . "/var/service/dhcp_$if/dhcp_$if.out" test x"$router" != x"" || exec env - sleep "$delay" #echo "`date '+%Y-%m-%d %H:%M:%S'` Testing ping -c3 $router" >>"$0.log" ping -c3 "$router" && exec env - sleep "$delay" echo "`date '+%Y-%m-%d %H:%M:%S'` Restarting /var/service/dhcp_$if" >>"$0.log" sv t "/var/service/dhcp_$if" exec env - sleep "$delay" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/README����������������������������������������������������������0000644�0000000�0000000�00000005630�12263563520�017004� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������In many cases, network configuration makes it necessary to run several daemons: dhcp, zeroconf, ppp, openvpn and such. They need to be controlled, and in many cases you also want to babysit them. runsvdir is a good tool for this. examples/var_service directory provides a few examples. It is meant to be used this way: copy it somewhere (say, /var/service) and run something like env - PATH=... <other vars=...> runsvdir /var/service & from one of system startup scripts. (Google "man runsvdir" and "man runsv" for more info about these tools). Some existing examples: var_service/dhcp_if - controls a udhcpc instance which provides dhpc-assigned IP address on interface named "if". Copy/rename this directory as needed to run udhcpc on other interfaces (var_service/dhcp_if/run script uses _foo suffix of the parent directory as interface name). When IP address is obtained or lost, var_service/dhcp_if/dhcp_handler is run. It saves new config data to /var/run/service/fw/dhcp_if.ipconf and (re)starts /var/service/fw service. This example can be used as a template for other dynamic network link services (ppp/vpn/zcip). var_service/ifplugd_if - watches link status of interface if. Downs and ups /var/service/dhcp_if service accordingly. In effect, it allows you to unplug/plug-to-different-network and have your IP properly re-negotiated at once. var_service/dhcp_if_pinger - Uses var_service/dhcp_if's data (/var/service/dhcp_if/dhcp_if.out file) to determine router IP. Pings it. If ping fails, restarts /var/service/dhcp_if service. Basically, an example of watchdog service for networks which are not reliable and need babysitting. var_service/fw - A *one-shot* service which reconfigures network based on current known state of ALL interfaces. Uses conf/*.ipconf (static config) and /var/run/service/fw/*.ipconf (dynamic config from dhcp/ppp/vpn/etc) to determine what to do. One-shot-ness of this service means that it shuts itself off after single run. IOW: it is not a constantly running daemon sort of thing. It starts, it configures the network, it shuts down, all done (unlike infamous NetworkManagers which sit in RAM forever, doing hell knows what). However, any dhcp/ppp/vpn or similar service can restart it anytime when it senses the change in network configuration. This even works while fw service runs: if dhcp signals fw to (re)start while fw runs, fw will not stop after its execution, but will re-execute once, picking up dhcp's new configuration. This is achieved very simply by having # Make ourself one-shot sv o . at the very beginning of fw/run script, not at the end. Therefore, any "sv u /var/run/service/fw" command by any other script "undoes" o(ne-shot) command if fw still runs, thus runsv will rerun it; or start it in a normal way if fw is not running. System administrators are expected to edit fw/run script, since network configuration needs are likely to be very complex and different for non-trivial installations. ��������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ftpd/�����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�017052� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ftpd/run��������������������������������������������������������0000755�0000000�0000000�00000000317�12263563520�017610� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh #exec >/dev/null exec 2>&1 exec </dev/null user=www user=root exec \ env - PATH="$PATH" \ softlimit \ tcpsvd \ -vE -l 0 -c 40 \ 0.0.0.0 21 \ setuidgid "$user" \ ftpd -vv -t10 /pub/ftpd_root �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ftpd/w_log������������������������������������������������������0000755�0000000�0000000�00000000174�12263563520�020114� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cd log/logdir || exit 1 watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))' ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ftpd/log/�������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�017633� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ftpd/log/run����������������������������������������������������0000755�0000000�0000000�00000000547�12263563520�020376� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh user=logger logdir="/var/log/service/`(cd ..;basename $PWD)`" mkdir -p "$logdir" 2>/dev/null chown -R "$user": "$logdir" chmod -R go-rwxst,u+rwX "$logdir" rm logdir ln -s "$logdir" logdir # make this dir accessible to logger chmod a+rX . exec >/dev/null exec 2>&1 exec \ env - PATH="$PATH" \ softlimit \ setuidgid "$user" \ svlogd -tt "$logdir" ���������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ftpd/p_log������������������������������������������������������0000755�0000000�0000000�00000000073�12263563520�020103� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cd log/logdir || exit 1 cat @* current | $PAGER ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/dhcp_if/��������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�017511� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/dhcp_if/convert2ipconf������������������������������������������0000755�0000000�0000000�00000001710�12263563520�022402� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # convert: # dhcptype=5 # serverid=172.16.42.102 # lease=97200 # interface=eth0 # ip=172.16.42.177 # subnet=255.255.255.0 # mask=24 # broadcast=172.16.22.255 # router=172.16.42.98 # dns=10.34.32.125 10.32.63.5 10.34.255.7 10.11.255.27 # domain=lab.example.com example.com # ntpsrv=10.34.32.125 10.34.255.7 # into: #let cfg=cfg+1 #if[$cfg]=...; ip[$cfg]=...; ipmask[$cfg]=.../...; gw[$cfg]=...; net[$cfg]=... dns[$cfg]=... exec >/dev/null #exec >"$0.out" # debug exec 2>&1 test "$interface" || exit 1 test "$ip" || exit 1 { echo "let cfg=cfg+1" test "$interface" && echo "if[\$cfg]='$interface'" test "$ip" && echo "ip[\$cfg]='$ip'" test "$ip" && test "$mask" \ && echo "ipmask[\$cfg]='$ip/$mask'" test "$router" && echo "gw[\$cfg]='$router'" test "$dns" && echo "dns[\$cfg]='$dns'" # TODO: I never saw a dhcp server which correctly announces # which subnet(s) is/are available thru advertised router # Assume 0/0 echo "net[\$cfg]='0/0'" } >"$1" ��������������������������������������������������������busybox-1.22.1/examples/var_service/dhcp_if/dhcp_handler��������������������������������������������0000755�0000000�0000000�00000004271�12263563520�022061� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # executed by udhcpc # parameters: $1 and environment # $1 is: # # deconfig: udhcpc starts, or lease is lost. # Environment example: interface=eth0 # # bound: lease is obtained. Environment example: # dhcptype=5 # serverid=172.16.42.102 # lease=97200 # interface=eth0 # ip=172.16.42.177 # subnet=255.255.255.0 # mask=24 # broadcast=172.16.22.255 # router=172.16.42.98 # dns=10.34.32.125 10.32.63.5 10.34.255.7 10.11.255.27 # domain=lab.example.com example.com # ntpsrv=10.34.32.125 10.34.255.7 # # renew: lease is renewed. Environment is similar to "bound". # The IP address does not change, however, the other DHCP paramaters, # such as the default gateway, subnet mask, and dns server may change. # # nak: udhcpc received a NAK message. # Environment example: interface=eth0 # # leasefail: udhcpc cannot obtain a lease (DHCP server not responding, etc). # Environment example: interface=eth0 # TODO: put $domain into /etc/resolv.conf (thru /var/service/fw) service=${PWD##*/} file_ipconf="$service.ipconf" file_ntpconf="$service.ntpconf" dir_ipconf="/var/run/service/fw" dir_ntpconf="/var/run/service/ntp" exec >/dev/null #exec >>"$0.out" #debug exec 2>&1 echo "`date`: Params: $*" if test x"$1" != x"bound" && test x"$1" != x"renew" ; then # Reconfigure network with this interface disabled echo "Deconfiguring" rm "$service.out" rm "$file_ipconf" rm "$file_ntpconf" rm "$dir_ipconf/$file_ipconf" rm "$dir_ntpconf/$file_ntpconf" sv u /var/service/fw exit fi # Bound: we've got the lease #env >"$service.out" # debug ./convert2ipconf "$file_ipconf" # Reconfigure routing and firewall if needed diff --brief "$file_ipconf" "$dir_ipconf/$file_ipconf" >/dev/null 2>&1 if test $? != 0; then echo "Reconfiguring fw" mkdir -p "$dir_ipconf" 2>/dev/null cp "$file_ipconf" "$dir_ipconf/$file_ipconf" sv u /var/service/fw fi if test -d /var/service/ntp; then ./convert2ntpconf "$file_ntpconf" # Reconfigure ntp server addresses if needed diff --brief "$file_ntpconf" "$dir_ntpconf/$file_ntpconf" >/dev/null 2>&1 if test $? != 0; then echo "Reconfiguring ntp" mkdir -p "$dir_ntpconf" 2>/dev/null cp "$file_ntpconf" "$dir_ntpconf/$file_ntpconf" sv t /var/service/ntp sv u /var/service/ntp fi fi ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/dhcp_if/run�����������������������������������������������������0000755�0000000�0000000�00000000513�12263563520�020245� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh exec 2>&1 exec </dev/null pwd="$PWD" if="${PWD##*/dhcp_}" echo "* Upping iface $if" ip link set dev "$if" up echo "* Starting udhcpc" exec \ env - PATH="$PATH" \ softlimit \ setuidgid root \ udhcpc -vv \ --hostname=null \ --foreground \ --interface="$if" \ --pidfile="$pwd/udhcpc.pid" \ --script="$pwd/dhcp_handler" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/dhcp_if/w_log���������������������������������������������������0000755�0000000�0000000�00000000174�12263563520�020553� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cd log/logdir || exit 1 watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))' ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/dhcp_if/log/����������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�020272� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/dhcp_if/log/run�������������������������������������������������0000755�0000000�0000000�00000000547�12263563520�021035� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh user=logger logdir="/var/log/service/`(cd ..;basename $PWD)`" mkdir -p "$logdir" 2>/dev/null chown -R "$user": "$logdir" chmod -R go-rwxst,u+rwX "$logdir" rm logdir ln -s "$logdir" logdir # make this dir accessible to logger chmod a+rX . exec >/dev/null exec 2>&1 exec \ env - PATH="$PATH" \ softlimit \ setuidgid "$user" \ svlogd -tt "$logdir" ���������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/dhcp_if/convert2ntpconf�����������������������������������������0000755�0000000�0000000�00000001043�12263563520�022572� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # convert: # dhcptype=5 # serverid=172.16.42.102 # lease=97200 # interface=eth0 # ip=172.16.42.177 # subnet=255.255.255.0 # mask=24 # broadcast=172.16.22.255 # router=172.16.42.98 # dns=10.34.32.125 10.32.63.5 10.34.255.7 10.11.255.27 # domain=lab.example.com example.com # ntpsrv=10.34.32.125 10.34.255.7 # into: #let cfg=cfg+1 #ntpip[$cfg]=... exec >/dev/null #exec >"$0.out" # debug exec 2>&1 test "$interface" || exit 1 test "$ip" || exit 1 { for n in $ntpsrv; do echo "let cfg=cfg+1" echo "ntpip[\$cfg]='$n'"; done } >"$1" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/dhcp_if/p_log���������������������������������������������������0000755�0000000�0000000�00000000073�12263563520�020542� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cd log/logdir || exit 1 cat @* current | $PAGER ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/inetd/����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�017220� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/inetd/run�������������������������������������������������������0000755�0000000�0000000�00000000520�12263563520�017752� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh #exec >/dev/null exec 2>&1 exec </dev/null echo "* Starting inetd [$$]" exec \ env - PATH="$PATH" \ softlimit \ inetd -f -e "$PWD/inetd.conf" # inetd [-f] [-q len] [conf] # -f Run in foreground # -e Log to stderr (default is syslog) # -q N Set the size of the socket listen queue to N # (default: 128) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/inetd/w_log�����������������������������������������������������0000755�0000000�0000000�00000000174�12263563520�020262� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cd log/logdir || exit 1 watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))' ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/inetd/log/������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�020001� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/inetd/log/run���������������������������������������������������0000755�0000000�0000000�00000000547�12263563520�020544� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh user=logger logdir="/var/log/service/`(cd ..;basename $PWD)`" mkdir -p "$logdir" 2>/dev/null chown -R "$user": "$logdir" chmod -R go-rwxst,u+rwX "$logdir" rm logdir ln -s "$logdir" logdir # make this dir accessible to logger chmod a+rX . exec >/dev/null exec 2>&1 exec \ env - PATH="$PATH" \ softlimit \ setuidgid "$user" \ svlogd -tt "$logdir" ���������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/inetd/inetd.conf������������������������������������������������0000644�0000000�0000000�00000001404�12263563520�021174� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# [ADDR:]service_name must be in /etc/services, or port number # socket_type stream/dgram/raw/rdm/seqpacket # protocol tcp/udp # wait/nowait[.max] wait is usually for udp, nowait for tcp # max: max copies to run # user[.group] or user[:group] user and group to run under # binary program to run # arg0 arg1 arg2... arguments, INCLUDING program name (arg0) # serv socket pro w/nw user binary args # IPv6 555 dgram udp6 wait root echo echo Hello IPv6 udp world # ...with ADDR prefix: ::1:444 stream tcp6 nowait root echo echo Hello IPv6 localhost # Rarely seen case: tcp *wait* service telnet stream tcp wait root telnetd telnetd -w10 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/inetd/p_log�����������������������������������������������������0000755�0000000�0000000�00000000073�12263563520�020251� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cd log/logdir || exit 1 cat @* current | $PAGER ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/fw/�������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�016531� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/fw/stat���������������������������������������������������������0000755�0000000�0000000�00000000353�12263563520�017436� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo; echo "* Firewall:" { echo '---FILTER--'; iptables -v -L -x -n; echo '---NAT-----'; iptables -t nat -v -L -x -n; echo '---MANGLE--'; iptables -t mangle -v -L -x -n; } \ | grep -v '^$' | grep -Fv 'bytes target' | $PAGER �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/fw/run����������������������������������������������������������0000755�0000000�0000000�00000013660�12263563520�017274� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # (using bashism: arrays) service="${PWD##*/}" rundir="/var/run/service/$service" user=root extif=if ext_open_tcp="21 22 80" # space-separated # Make ourself one-shot sv o . # Debug #date '+%Y-%m-%d %H:%M:%S' >>"$0.log" ### filter This is the default table (if no -t option is passed). It contains ### the built-in chains INPUT (for packets coming into the box itself), ### FORWARD (for packets being routed through the box), and OUTPUT (for ### locally-generated packets). ### ### nat This table is consulted when a packet that creates a new connection ### is encountered. It consists of three built-ins: PREROUTING (for ### altering packets as soon as they come in), OUTPUT (for altering ### locally-generated packets before routing), and POSTROUTING (for ### altering packets as they are about to go out). ### ### mangle It had two built-in chains: PREROUTING (for altering incoming ### packets before routing) and OUTPUT (for altering locally-generated ### packets before routing). Recently three other built-in ### chains are added: INPUT (for packets coming into the box ### itself), FORWARD (for altering packets being routed through the ### box), and POSTROUTING (for altering packets as they are about to go ### out). ### ### ...iface... ...iface... ### | ^ ### v | ### -mangle,NAT- -mangle,filter- -mangle,NAT-- ### |PREROUTING|-->[Routing]-->|FORWARD |-->|POSTROUTING| ### ------------ | ^ --------------- ------------- ### | | ^ ### | +--if NATed------------+ | ### v | | ### -mangle,filter- -mangle,NAT,filter- ### |INPUT | +->[Routing]->|OUTPUT | ### --------------- | ------------------- ### | | ### v | ### ... Local Process... doit() { echo "# $*" "$@" } #exec >/dev/null exec >"$0.out" exec 2>&1 exec </dev/null umask 077 # Make sure rundir/ exists mkdir -p "$rundir" 2>/dev/null chown -R "$user:" "$rundir" chmod -R a=rX "$rundir" rm -rf rundir 2>/dev/null ln -s "$rundir" rundir # Timestamping date '+%Y-%m-%d %H:%M:%S' echo; echo "* Reading IP config" cfg=-1 # static cfg dhcp,zeroconf etc for ipconf in conf/*.ipconf "$rundir"/*.ipconf; do if test -f "$ipconf"; then echo "+ $ipconf" . "$ipconf" fi done echo; echo "* Configuring hardware" #doit ethtool -s if autoneg off speed 100 duplex full #doit ethtool -K if rx off tx off sg off tso off echo; echo "* Resetting address and routing info" doit ip a f dev lo i=0; while test "${if[$i]}"; do doit ip a f dev "${if[$i]}" doit ip r f dev "${if[$i]}" root 0/0 let i++; done echo; echo "* Configuring addresses" doit ip a a dev lo 127.0.0.1/8 scope host doit ip a a dev lo ::1/128 scope host i=0; while test "${if[$i]}"; do if test "${ipmask[$i]}"; then doit ip a a dev "${if[$i]}" "${ipmask[$i]}" brd + doit ip l set dev "${if[$i]}" up fi let i++; done echo; echo "* Configuring routes" i=0; while test "${if[$i]}"; do if test "${net[$i]}" && test "${gw[$i]}"; then doit ip r a "${net[$i]}" via "${gw[$i]}" fi let i++; done echo; echo "* Recreating /etc/* files reflecting new network configuration:" for i in etc/*; do n=`basename "$i"` echo "+ $n" (. "$i") >"/etc/$n" chmod 644 "/etc/$n" done # Usage: new_chain <chain> [<table>] new_chain() { local t="" test x"$2" != x"" && t="-t $2" doit iptables $t -N $1 ipt="iptables $t -A $1" } echo; echo "* Reset iptables" doit iptables --flush doit iptables --delete-chain doit iptables --zero doit iptables -t nat --flush doit iptables -t nat --delete-chain doit iptables -t nat --zero doit iptables -t mangle --flush doit iptables -t mangle --delete-chain doit iptables -t mangle --zero echo; echo "* Configure iptables" doit modprobe nf_nat_ftp doit modprobe nf_nat_tftp doit modprobe nf_conntrack_ftp doit modprobe nf_conntrack_tftp # *** nat *** # INCOMING TRAFFIC ipt="iptables -t nat -A PREROUTING" # nothing here # LOCALLY ORIGINATED TRAFFIC ipt="iptables -t nat -A OUTPUT" # nothing here # OUTGOING TRAFFIC ipt="iptables -t nat -A POSTROUTING" # Masquerade boxes on my private net doit $ipt -s 192.168.0.0/24 -o $extif -j MASQUERADE # *** mangle *** ### DEBUG ### ipt="iptables -t mangle -A PREROUTING" ### doit $ipt -s 192.168.0.0/24 -j RETURN ### ipt="iptables -t mangle -A FORWARD" ### doit $ipt -s 192.168.0.0/24 -j RETURN ### ipt="iptables -t mangle -A POSTROUTING" ### doit $ipt -s 192.168.0.0/24 -j RETURN # nothing here # *** filter *** # new_chain iext filter #doit $ipt -s 203.177.104.72 -j DROP # Some idiot probes my ssh #doit $ipt -d 203.177.104.72 -j DROP # Some idiot probes my ssh doit $ipt -m state --state ESTABLISHED,RELATED -j RETURN # FTP data etc is ok if test "$ext_open_tcp"; then portlist="${ext_open_tcp// /,}" doit $ipt -p tcp -m multiport --dports $portlist -j RETURN fi doit $ipt -p tcp -j REJECT # Anything else isn't ok. REJECT = irc opens faster # (it probes proxy ports, DROP will incur timeout delays) ipt="iptables -t filter -A INPUT" doit $ipt -i $extif -j iext echo; echo "* Enabling forwarding" echo 1 >/proc/sys/net/ipv4/ip_forward echo "/proc/sys/net/ipv4/ip_forward: `cat /proc/sys/net/ipv4/ip_forward`" # Signal everybody that firewall is up date '+%Y-%m-%d %H:%M:%S' >"$rundir/up" # Ok, spew out gobs of info and disable ourself echo; echo "* IP:" ip a l echo; echo "* Routing:" ip r l echo; echo "* Firewall:" { echo '---FILTER--'; iptables -v -L -x -n; echo '---NAT-----'; iptables -t nat -v -L -x -n; echo '---MANGLE--'; iptables -t mangle -v -L -x -n; } \ | grep -v '^$' | grep -Fv 'bytes target' echo echo "* End of firewall configuration" ��������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/fw/conf/��������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�017456� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/fw/conf/lo.ipconf�����������������������������������������������0000644�0000000�0000000�00000000304�12263563520�021270� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # Mostly redundant except when you need dns[]=your_static_dns_srv # let cfg=cfg+1 if[$cfg]=lo ip[$cfg]=127.0.0.1 ipmask[$cfg]=127.0.0.1/8 gw[$cfg]='' net[$cfg]='' #dns[$cfg]=127.0.0.1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/fw/conf/11.22.33.44.ipconf--������������������������������������0000644�0000000�0000000�00000000301�12263563520�022122� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # If we have simple static address... # let cfg=cfg+1 if[$cfg]=if ip[$cfg]=11.22.33.44 ipmask[$cfg]=11.22.33.44/24 gw[$cfg]=11.22.33.1 net[$cfg]=0/0 dns[$cfg]='11.22.33.2 11.22.33.3' �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/fw/conf/192.168.0.1.ipconf��������������������������������������0000644�0000000�0000000�00000000312�12263563520�022002� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # A small network with no routers # (maybe *we* are their router) # let cfg=cfg+1 if[$cfg]=if ip[$cfg]=192.168.0.1 ipmask[$cfg]=192.168.0.1/24 ### gw[$cfg]= ### net[$cfg]=0/0 ### dns[$cfg]='' ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/fw/etc/���������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�017304� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/fw/etc/resolv.conf����������������������������������������������0000644�0000000�0000000�00000001375�12263563520�021476� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash domain=`(. /boot.conf; echo "$DNSDOMAINNAME") 2>/dev/null` echo "# This file is automagically regenerated with each boot" echo test "$domain" && echo "domain $domain" test "$domain" && echo "search $domain" echo echo "# Note that nslookup can choke on DNS server which itself" echo "# does NOT have domain name. Other things can work fine." echo # # If we run DNS cache: # echo "nameserver 127.0.0.1" # exit prio=0 i=0; while test "${if[$i]}"; do test x"${dns_prio[$i]}" != x"" \ && test "${dns_prio[$i]}" -gt "$prio" \ && prio="${dns_prio[$i]}" let i++; done i=0; while test "${if[$i]}"; do for d in ${dns[$i]}; do p="${dns_prio[$i]}" test x"$p" == x"" && p=0 test x"$p" == x"$prio" || continue echo "nameserver $d" done let i++; done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/fw/etc/hosts����������������������������������������������������0000644�0000000�0000000�00000000677�12263563520�020404� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo "\ # This file is automagically regenerated # Note! /etc/nsswitch.conf may override this! # For loopbacking 127.0.0.1 localhost # Our local IPs" hostname=`hostname` test "$hostname" || hostname=localhost domain=`(. /boot.conf; echo "$DNSDOMAINNAME")` test "$domain" && hostname="$hostname $hostname.$domain" ip -o a l \ | grep -F 'inet ' \ | sed -e 's/^.*inet //' -e 's:[ /].*$: '"$hostname"':' echo echo "# End of /etc/hosts" �����������������������������������������������������������������busybox-1.22.1/examples/var_service/nmeter/���������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�017407� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/nmeter/run������������������������������������������������������0000755�0000000�0000000�00000000576�12263563520�020154� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh exec >/dev/null exec 2>&1 exec </dev/null # Since per-process /proc/net/ (-> /proc/self/net/) appeared, # we need to be root user="root" tty="/dev/tty9" cmd="nmeter '%t %c x %x p%p f %f b %b m %m if%[nif]'" chmod -R a+X . # or else env will moan chown "$user": "$tty" # devfs made happy eval exec \ env - PATH="$PATH" \ setuidgid "$user" \ <"$tty" >"$tty" 2>&1 \ $cmd ����������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/getty_tty1/�����������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�020232� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/getty_tty1/run��������������������������������������������������0000755�0000000�0000000�00000000757�12263563520�021000� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh exec >/dev/null exec 2>&1 exec </dev/null user=root baud=38400 delay=3 export TERM=linux tty="/dev/${PWD##*/getty_}" if ! test -e "$tty"; then exec env - sleep 32000 fi sleep "$delay" chown "$user" "$tty" # - devfs made happy exec <"$tty" >"$tty" 2>&1 # using . in order to be able to set env (TERM etc) in cfg test -x ./cfg && . ./cfg exec \ env - "TERM=$TERM" PATH="$PATH" LOGIN_PRE_SUID_SCRIPT="$PWD/login.sh" \ softlimit \ setuidgid "$user" \ getty "$baud" "$tty" "$TERM" �����������������busybox-1.22.1/examples/var_service/getty_tty1/ru_koi8r.keymap��������������������������������������0000644�0000000�0000000�00000046572�12263563520�023225� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������keymaps 0,1, 2,3, 4,6, 8,10, 12,14 # # This one is for generating koi8r Russian chars # Cyr/Lat switches: RightAlt, Shift+Ctrl, Ctrl+Shift # (last one does not work for dark and obscure reasons 8( ) # # plain,shift, plain,shift, ctrl,ctrl alt,alt ctrlalt,ctrlalt # lat-------- cyr-------- lat cyr lat cyr lat cyr # #Shift 1 #AltGr (cyr) 2 #Control 4 #Alt 8 #ShiftL 16 #ShiftR 32 #CtrlL 64 #CtrlR 128 #============== plain ========= shift========== plain cyr ===== shift cyr ===== ctrl ================== ctrl cyr ============== alt =================== alt cyr =============== ctrlalt =============== ctrlalt cyr =========== keycode 1 = Escape Escape Escape Escape Escape Escape Meta_Escape Meta_Escape SAK SAK keycode 2 = one exclam one exclam exclam exclam Meta_one Meta_one keycode 3 = two at two at nul nul Meta_two Meta_two keycode 4 = three numbersign three numbersign three three Meta_three Meta_three keycode 5 = four dollar four dollar Control_backslash Control_backslash Meta_four Meta_four keycode 6 = five percent five percent Control_bracketright Control_bracketright Meta_five Meta_five keycode 7 = six asciicircum six asciicircum Control_asciicircum Control_asciicircum Meta_six Meta_six keycode 8 = seven ampersand seven ampersand Control_underscore Control_underscore Meta_seven Meta_seven keycode 9 = eight asterisk eight asterisk eight eight Meta_eight Meta_eight keycode 10 = nine parenleft nine parenleft nine nine Meta_nine Meta_nine keycode 11 = zero parenright zero parenright zero zero Meta_zero Meta_zero keycode 12 = minus underscore minus underscore Control_underscore Control_underscore Meta_minus Meta_minus keycode 13 = equal plus equal plus equal equal Meta_equal Meta_equal keycode 14 = Delete Delete Delete Delete BackSpace BackSpace Meta_Delete Meta_Delete keycode 15 = Tab Tab Tab Tab Tab Tab Meta_Tab Meta_Tab keycode 16 = q Q 202 234 Control_q Control_q Meta_q Meta_q Meta_Control_q Meta_Control_q keycode 17 = w W 195 227 Control_w Control_w Meta_w Meta_w Meta_Control_w Meta_Control_w keycode 18 = e E 213 245 Control_e Control_e Meta_e Meta_e Meta_Control_e Meta_Control_e keycode 19 = r R 203 235 Control_r Control_r Meta_r Meta_r Meta_Control_r Meta_Control_r keycode 20 = t T 197 229 Control_t Control_t Meta_t Meta_t Meta_Control_t Meta_Control_t #============== plain ========= shift========== plain cyr ===== shift cyr ===== ctrl ================== ctrl cyr ============== alt =================== alt cyr =============== ctrlalt =============== ctrlalt cyr =========== keycode 21 = y Y 206 238 Control_y Control_y Meta_y Meta_y Meta_Control_y Meta_Control_y keycode 22 = u U 199 231 Control_u Control_u Meta_u Meta_u Meta_Control_u Meta_Control_u keycode 23 = i I 219 251 Control_i Control_i Meta_i Meta_i Meta_Control_i Meta_Control_i keycode 24 = o O 221 253 Control_o Control_o Meta_o Meta_o Meta_Control_o Meta_Control_o keycode 25 = p P 218 250 Control_p Control_p Meta_p Meta_p Meta_Control_p Meta_Control_p keycode 26 = bracketleft braceleft 200 232 Escape Escape Meta_bracketleft Meta_bracketleft keycode 27 = bracketright braceright 223 255 Control_bracketright Control_bracketright keycode 28 = Return # Shift+Ctrl - Cyrillic keycode 29 = Control AltGr_Lock Control AltGr_Lock Control Control Control Control Control Control keycode 30 = a A 198 230 Control_a Control_a Meta_a Meta_a Meta_Control_a Meta_Control_a keycode 31 = s S 217 249 Control_s Control_s Meta_s Meta_s Meta_Control_s Meta_Control_s keycode 32 = d D 215 247 Control_d Control_d Meta_d Meta_d Meta_Control_d Meta_Control_d keycode 33 = f F 193 225 Control_f Control_f Meta_f Meta_f Meta_Control_f Meta_Control_f keycode 34 = g G 208 240 Control_g Control_g Meta_g Meta_g Meta_Control_g Meta_Control_g keycode 35 = h H 210 242 Control_h Control_h Meta_h Meta_h Meta_Control_h Meta_Control_h keycode 36 = j J 207 239 Control_j Control_j Meta_j Meta_j Meta_Control_j Meta_Control_j keycode 37 = k K 204 236 Control_k Control_k Meta_k Meta_k Meta_Control_k Meta_Control_k keycode 38 = l L 196 228 Control_l Control_l Meta_l Meta_l Meta_Control_l Meta_Control_l keycode 39 = semicolon colon 214 246 semicolon semicolon Meta_semicolon Meta_semicolon keycode 40 = apostrophe quotedbl 220 252 Control_g Control_g Meta_apostrophe Meta_apostrophe #============== plain ========= shift========== plain cyr ===== shift cyr ===== ctrl ================== ctrl cyr ============== alt =================== alt cyr =============== ctrlalt =============== ctrlalt cyr =========== keycode 41 = grave asciitilde grave asciitilde nul nul Meta_grave Meta_grave keycode 42 = Shift keycode 43 = backslash bar backslash bar Control_backslash Control_backslash Meta_backslash Meta_backslash keycode 44 = z Z 209 241 Control_z Control_z Meta_z Meta_z Meta_Control_z Meta_Control_z keycode 45 = x X 222 254 Control_x Control_x Meta_x Meta_x Meta_Control_x Meta_Control_x keycode 46 = c C 211 243 Control_c Control_c Meta_c Meta_c Meta_Control_c Meta_Control_c keycode 47 = v V 205 237 Control_v Control_v Meta_v Meta_v Meta_Control_v Meta_Control_v keycode 48 = b B 201 233 Control_b Control_b Meta_b Meta_b Meta_Control_b Meta_Control_b keycode 49 = n N 212 244 Control_n Control_n Meta_n Meta_n Meta_Control_n Meta_Control_n keycode 50 = m M 216 248 Control_m Control_m Meta_m Meta_m Meta_Control_m Meta_Control_m keycode 51 = comma less 194 226 comma comma Meta_comma Meta_comma keycode 52 = period greater 192 224 Compose Compose Meta_period Meta_period keycode 53 = slash question slash question Delete Delete Meta_slash Meta_slash Meta_question Meta_question # Ctrl+Shift - Cyrillic (not working???) keycode 54 = Shift Shift Shift Shift AltGr_Lock AltGr_Lock Shift Shift Shift Shift keycode 55 = KP_Multiply keycode 56 = Alt keycode 57 = space space space space nul nul Meta_space Meta_space keycode 58 = Caps_Lock keycode 59 = F1 F11 F1 F11 F1 F1 Console_1 Console_1 Console_1 Console_1 keycode 60 = F2 F12 F2 F12 F2 F2 Console_2 Console_2 Console_2 Console_2 #============== plain ========= shift========== plain cyr ===== shift cyr ===== ctrl ================== ctrl cyr ============== alt =================== alt cyr =============== ctrlalt =============== ctrlalt cyr =========== keycode 61 = F3 F13 F3 F13 F3 F3 Console_3 Console_3 Console_3 Console_3 keycode 62 = F4 F14 F4 F14 F4 F4 Console_4 Console_4 Console_4 Console_4 keycode 63 = F5 F15 F5 F15 F5 F5 Console_5 Console_5 Console_5 Console_5 keycode 64 = F6 F16 F6 F16 F6 F6 Console_6 Console_6 Console_6 Console_6 keycode 65 = F7 F17 F7 F17 F7 F7 Console_7 Console_7 Console_7 Console_7 keycode 66 = F8 F18 F8 F18 F8 F8 Console_8 Console_8 Console_8 Console_8 keycode 67 = F9 F19 F9 F19 F9 F9 Console_9 Console_9 Console_9 Console_9 keycode 68 = F10 F20 F10 F20 F10 F10 Console_10 Console_10 Console_10 Console_10 keycode 69 = Num_Lock Bare_Num_Lock Num_Lock Bare_Num_Lock keycode 70 = Scroll_Lock Show_Memory Scroll_Lock Show_Memory Show_State Show_State keycode 71 = KP_7 KP_7 KP_7 KP_7 KP_7 KP_7 Ascii_7 Ascii_7 keycode 72 = KP_8 KP_8 KP_8 KP_8 KP_8 KP_8 Ascii_8 Ascii_8 keycode 73 = KP_9 KP_9 KP_9 KP_9 KP_9 KP_9 Ascii_9 Ascii_9 keycode 74 = KP_Subtract KP_Subtract KP_Subtract KP_Subtract KP_Subtract KP_Subtract KP_Subtract KP_Subtract keycode 75 = KP_4 KP_4 KP_4 KP_4 KP_4 KP_4 Ascii_4 Ascii_4 keycode 76 = KP_5 KP_5 KP_5 KP_5 KP_5 KP_5 Ascii_5 Ascii_5 keycode 77 = KP_6 KP_6 KP_6 KP_6 KP_6 KP_6 Ascii_6 Ascii_6 keycode 78 = KP_Add KP_Add KP_Add KP_Add KP_Add KP_Add KP_Add KP_Add keycode 79 = KP_1 KP_1 KP_1 KP_1 KP_1 KP_1 Ascii_1 Ascii_1 keycode 80 = KP_2 KP_2 KP_2 KP_2 KP_2 KP_2 Ascii_2 Ascii_2 #============== plain ========= shift========== plain cyr ===== shift cyr ===== ctrl ================== ctrl cyr ============== alt =================== alt cyr =============== ctrlalt =============== ctrlalt cyr =========== keycode 81 = KP_3 KP_3 KP_3 KP_3 KP_3 KP_3 Ascii_3 Ascii_3 keycode 82 = KP_0 KP_0 KP_0 KP_0 KP_0 KP_0 Ascii_0 Ascii_0 keycode 83 = KP_Period KP_Period KP_Period KP_Period KP_Period KP_Period KP_Period KP_Period Boot Boot keycode 84 = Last_Console keycode 85 = keycode 86 = less greater less greater less less Meta_less Meta_less keycode 87 = F11 F11 F11 F11 F11 F11 Console_11 Console_11 Console_11 Console_11 keycode 88 = F12 F12 F12 F12 F12 F12 Console_12 Console_12 Console_12 Console_12 keycode 89 = keycode 90 = keycode 91 = keycode 92 = keycode 93 = keycode 94 = keycode 95 = keycode 96 = KP_Enter keycode 97 = Control keycode 98 = KP_Divide keycode 99 = Control_backslash # Right Alt - Cyrillic keycode 100 = AltGr_Lock #============== plain ========= shift========== plain cyr ===== shift cyr ===== ctrl ================== ctrl cyr ============== alt =================== alt cyr =============== ctrlalt =============== ctrlalt cyr =========== keycode 101 = Break keycode 102 = Find keycode 103 = Up keycode 104 = Prior Scroll_Backward Prior Scroll_Backward Prior Prior Prior Prior Prior Prior keycode 105 = Left Left Left Left Left Left Left Left Decr_Console Decr_Console keycode 106 = Right Right Right Right Right Right Right Right Incr_Console Incr_Console keycode 107 = Select keycode 108 = Down keycode 109 = Next Scroll_Forward Next Scroll_Forward Next Next Next Next Next Next keycode 110 = Insert keycode 111 = Remove Remove Remove Remove Remove Remove Remove Remove Boot Boot keycode 112 = Macro keycode 113 = F13 keycode 114 = F14 keycode 115 = Help keycode 116 = Do keycode 117 = F17 keycode 118 = KP_MinPlus keycode 119 = Pause keycode 120 = #============== plain ========= shift========== plain cyr ===== shift cyr ===== ctrl ================== ctrl cyr ============== alt =================== alt cyr =============== ctrlalt =============== ctrlalt cyr =========== keycode 121 = keycode 122 = keycode 123 = keycode 124 = keycode 125 = keycode 126 = keycode 127 = string F1 = "\033[[A" string F2 = "\033[[B" string F3 = "\033[[C" string F4 = "\033[[D" string F5 = "\033[[E" string F6 = "\033[17~" string F7 = "\033[18~" string F8 = "\033[19~" string F9 = "\033[20~" string F10 = "\033[21~" string F11 = "\033[23~" string F12 = "\033[24~" string F13 = "\033[25~" string F14 = "\033[26~" string F15 = "\033[28~" string F16 = "\033[29~" string F17 = "\033[31~" string F18 = "\033[32~" string F19 = "\033[33~" string F20 = "\033[34~" string Find = "\033[1~" string Insert = "\033[2~" string Remove = "\033[3~" string Select = "\033[4~" string Prior = "\033[5~" string Next = "\033[6~" string Macro = "\033[M" string Pause = "\033[P" ��������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/getty_tty1/login.sh���������������������������������������������0000755�0000000�0000000�00000000563�12263563520�021710� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh ttyname=`tty` ttybase="${ttyname%%[0123456789]*}" # strip numeric tail if test "$ttybase" = "/dev/tty"; then tail="${ttyname:8}" echo "* Setting terminal device's owner to $LOGIN_UID:$LOGIN_GID" chown "$LOGIN_UID:$LOGIN_GID" "/dev/vcs$tail" "/dev/vcsa$tail" fi # We can do this also, but login does it itself # chown "$LOGIN_UID:$LOGIN_GID" "$ttyname" ���������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/getty_tty1/alt08x16+unimap.fnt����������������������������������0000644�0000000�0000000�00000012004�12263563520�023517� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������6��`` ������~¥½™~������~ÿÛÿÿÿÃçÿ~������6w>>����>>>>����kkk����>*>���������<<������ÿÿÿÿÿÿçÃÃçÿÿÿÿÿÿ�����<BBBB<�����ÿÿÿÿÿý½½½Ãÿÿÿÿÿ��8DDD8������""">������0p`������!>"""&nìÀ�������kwk������@`px|~|xp`@�����>~>�����>>�����"""""""�""������?IIII9 ������" """�������������ÿÿÿ�����>>������>���������>�������� þÿþ ���������0ÿ0�����������@@@~�����������$fÿf$���������>>��������>>�����������������������<<<������ff"D��������������llþlllþll�����|ÖÖpÖÖ|������qÛv 0nÛŽ������8lll8{ÎÌÎ{������������������ 000000 ������0 0���������f<ÿ<f�����������~�������������������������þ������������������������� 0`À€������|ÆÆÎÖÖæÆÆ|������8x~������|Æ 0`ÀÆþ������|Æ<Æ|������ <lÌþ ������þÀÀÀüÆ|������8`ÀÀüÆÆÆÆ|������þÆ 0000������|ÆÆÆ|ÆÆÆÆ|������|ÆÆÆ~ x����������������������������� 0`0 ���������~��~����������`0  0`������|ÆÆ ��������|ÆÆÞÞÞÜÀ|������8lÆÆþÆÆÆÆ������üfff|ffffü������<fÂÀÀÀÀÂf<������ølfffffflø������þfbhxh`bfþ������þfbhxh```ð������<fÂÀÀÞÆÆf:������ÆÆÆÆþÆÆÆÆÆ������<<������ ÌÌÌx������æfflxxlffæ������ð``````bfþ������ÃçÿÿÛÛÛÃÃÃ������ÆæöþÞÎÆÆÆÆ������|ÆÆÆÆÆÆÆÆ|������üfff|````ð������|ÆÆÆÆÆÆÖÞ| ����üfff|lfffæ������|ÆÆ`8 ÆÆ|������ÿÛ™<������ÆÆÆÆÆÆÆÆÆ|������ÃÃÃÃÃÃÃf<������ÃÃÃÃÛÛÛÿff������ÃÃf<<fÃÃ������ÃÃÃf<<������ÿÆ 0`ÁÃÿ������<00000000<�������€À`0 ������< <����8lÆ�������������������������ÿ�������������������x |ÌÌÌv������à``xlffff|���������|ÆÀÀÀÆ|������ <lÌÌÌÌv���������|ÆþÀÀÆ|������8ld`ð````ð���������vÌÌÌÌÌ| Ìx���à``lvffffæ�������8<�������ff<���à``flxxlfæ������8<���������îÿÛÛÛÛÛ���������Üffffff���������|ÆÆÆÆÆ|���������Üfffff|``ð������vÌÌÌÌÌ| ������Üvf```ð���������|Æ`8 Æ|������00|00006���������ÌÌÌÌÌÌv���������ÃÃÃÃf<���������ÃÃÃÛÛÿf���������Ãf<<fÃ���������ÆÆÆÆÆÆ~ ø������þÌ0`Æþ������p�������������pp������vÜ����������������8lÆÆÆþ�������"BBB~BBB������~@@@|BBBB|������|BBB|BBBB|������~@@@@@@@@@������ $DDDDDDþ‚�����~@@@x@@@@~������’’’’T8T’’’������<BB BB<������BBBFJRbBBB�����BBBFJRbBBB������BDHP`PHDBB������"""""""B������‚ƪ’’’’‚‚‚������BBBB~BBBBB������<BBBBBBBB<������~BBBBBBBBB������|BBBBB|@@@������<BB@@@@BB<������þ������BBBBBB>B<������>IIIII>������BBB$$BBBB������„„„„„„„„„þ�����BBBBBB>������‚‚’’’’’’’þ������‚‚’’’’’’’ÿ�����à <""""<������‚‚‚‚òŠŠŠŠò������@@@@|BBBB|������<BBBB<������œ¢¢¢â¢¢¢¢œ������>BBBB> "B���������<>BBB>������<@@|BBBB<���������|BB|BB|���������~@@@@@@���������$DDDDþ‚��������<BB~@B<���������’’T8T’’���������<BB<���������BBFJRbB��������BBFJRbB���������BDHpHDB���������""""B���������‚ƪ’’‚‚���������BBB~BBB���������<BBBBB<���������~BBBBBB����‚A‚A‚A‚AI’I’I’I’I’I’I’I’ÙÊS›ÙÊS›ÙÊS›ÙÊS›øøø6666666ö66666666�������þ66666666������øø666666öö66666666666666666666666������þö6666666666666öþ�������6666666þ��������øø��������������ø��������ÿ���������������ÿ�������ÿ��������ÿ666666676666666666666670?�������������?076666666666666÷�ÿ�������������ÿ�÷66666666666667076666666������ÿ�ÿ�������666666÷�÷6666666ÿ�ÿ�������6666666ÿ��������������ÿ�ÿ�������ÿ666666666666666?����������������������������?666666666666666ÿ66666666ÿÿø���������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ��������ÿÿÿÿÿÿÿÿððððððððððððððððÿÿÿÿÿÿÿÿ�������������|BBBBB|@@@������<B@@@B<���������þ���������BBBBBB><�����|’’’’’|������‚D((D‚���������„„„„„„þ��������BBB"���������‚’’’’’þ���������‚’’’’’ÿ��������à <"""<���������‚‚òŠŠŠò���������@@|BBB|���������<BB<���������œ¢¢â¢¢œ���������>BB>"B����$�~@@@x@@@@~�������$�<BB~@B<������<BB@p@@BB<���������<B@p@B<����(�88�������(�08�����BBBBBB>B<��������BBBBBB><��""������������������������������������������d ������’•ÕÒ°·�����8D8@|�������������>>>>>>��������ð€à€€������ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ �ÿÿ �ÿÿ �ÿÿ �ÿÿ �ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ�ÿÿ �ÿÿ!�ÿÿ"�ÿÿ#�ÿÿ$�ÿÿ%�ÿÿ&�ÿÿ'�ÿÿ(�ÿÿ)�ÿÿ*�ÿÿ+�ÿÿ,�ÿÿ-�ÿÿ.�ÿÿ/�ÿÿ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�ÿÿ[�ÿÿ\�ÿÿ]�ÿÿ^�ÿÿ_�ÿÿ`�ÿÿ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ÿÿ:ÿÿ;ÿÿ<ÿÿ=ÿÿ>ÿÿ?ÿÿ‘%ÿÿ’%ÿÿ“%ÿÿ%ÿÿ$%ÿÿa%ÿÿb%ÿÿV%ÿÿU%ÿÿc%ÿÿQ%ÿÿW%ÿÿ]%ÿÿ\%ÿÿ[%ÿÿ%ÿÿ%ÿÿ4%ÿÿ,%ÿÿ%ÿÿ�%ÿÿ<%ÿÿ^%ÿÿ_%ÿÿZ%ÿÿT%ÿÿi%ÿÿf%ÿÿ`%ÿÿP%ÿÿl%ÿÿg%ÿÿh%ÿÿd%ÿÿe%ÿÿY%ÿÿX%ÿÿR%ÿÿS%ÿÿk%ÿÿj%ÿÿ%ÿÿ %ÿÿˆ%ÿÿ„%ÿÿŒ%ÿÿ%ÿÿ€%ÿÿ@ÿÿAÿÿBÿÿCÿÿDÿÿEÿÿFÿÿGÿÿHÿÿIÿÿJÿÿKÿÿLÿÿMÿÿNÿÿOÿÿÿÿQÿÿÿÿTÿÿÿÿWÿÿÿÿ^ÿÿ°�ÿÿ"ÿÿ·�ÿÿ"ÿÿ!ÿÿ¤�ÿÿ %ÿÿ �ÿÿ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/getty_tty1/cfg��������������������������������������������������0000755�0000000�0000000�00000001134�12263563520�020721� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh if test x"$TERM" = x"" -o x"$TERM" = x"unknown"; then TERM="linux" echo "* Setting TERM='$TERM'" fi export TERM ttyname=`tty` ttybase="${ttyname%%[0123456789]*}" # strip numeric tail if test x"$ttybase" = x"/dev/vc/" -o x"$ttybase" = x"/dev/tty"; then echo "* Activating Cyrillic KOI8-R -> CP866 font map" echo -ne "\033(K" >"$ttyname" echo "* Loading screen font" setfont \ -C "$ttyname" \ -m "$PWD/koi8r_to_uni.trans" \ "$PWD/alt08x16+unimap.fnt" \ || echo "! setfont failure" echo "* Loading keymap" loadkeys "$PWD/ru_koi8r.keymap" \ || echo "! loadkeys failure" fi ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/getty_tty1/koi8r_to_uni.trans�����������������������������������0000644�0000000�0000000�00000023571�12263563520�023727� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0x00 U+0000 # NULL (NUL) 0x01 U+0001 # START OF HEADING (SOH) 0x02 U+0002 # START OF TEXT (STX) 0x03 U+0003 # END OF TEXT (ETX) 0x04 U+0004 # END OF TRANSMISSION (EOT) 0x05 U+0005 # ENQUIRY (ENQ) 0x06 U+0006 # ACKNOWLEDGE (ACK) 0x07 U+0007 # BELL (BEL) 0x08 U+0008 # BACKSPACE (BS) 0x09 U+0009 # CHARACTER TABULATION (HT) 0x0a U+000A # LINE FEED (LF) 0x0b U+000B # LINE TABULATION (VT) 0x0c U+000C # FORM FEED (FF) 0x0d U+000D # CARRIAGE RETURN (CR) 0x0e U+000E # SHIFT OUT (SO) 0x0f U+000F # SHIFT IN (SI) 0x10 U+0010 # DATALINK ESCAPE (DLE) 0x11 U+0011 # DEVICE CONTROL ONE (DC1) 0x12 U+0012 # DEVICE CONTROL TWO (DC2) 0x13 U+0013 # DEVICE CONTROL THREE (DC3) 0x14 U+0014 # DEVICE CONTROL FOUR (DC4) 0x15 U+0015 # NEGATIVE ACKNOWLEDGE (NAK) 0x16 U+0016 # SYNCHRONOUS IDLE (SYN) 0x17 U+0017 # END OF TRANSMISSION BLOCK (ETB) 0x18 U+0018 # CANCEL (CAN) 0x19 U+0019 # END OF MEDIUM (EM) 0x1a U+001A # SUBSTITUTE (SUB) 0x1b U+001B # ESCAPE (ESC) 0x1c U+001C # FILE SEPARATOR (IS4) 0x1d U+001D # GROUP SEPARATOR (IS3) 0x1e U+001E # RECORD SEPARATOR (IS2) 0x1f U+001F # UNIT SEPARATOR (IS1) 0x20 U+0020 # SPACE 0x21 U+0021 # EXCLAMATION MARK 0x22 U+0022 # QUOTATION MARK 0x23 U+0023 # NUMBER SIGN 0x24 U+0024 # DOLLAR SIGN 0x25 U+0025 # PERCENT SIGN 0x26 U+0026 # AMPERSAND 0x27 U+0027 # APOSTROPHE 0x28 U+0028 # LEFT PARENTHESIS 0x29 U+0029 # RIGHT PARENTHESIS 0x2a U+002A # ASTERISK 0x2b U+002B # PLUS SIGN 0x2c U+002C # COMMA 0x2d U+002D # HYPHEN-MINUS 0x2e U+002E # FULL STOP 0x2f U+002F # SOLIDUS 0x30 U+0030 # DIGIT ZERO 0x31 U+0031 # DIGIT ONE 0x32 U+0032 # DIGIT TWO 0x33 U+0033 # DIGIT THREE 0x34 U+0034 # DIGIT FOUR 0x35 U+0035 # DIGIT FIVE 0x36 U+0036 # DIGIT SIX 0x37 U+0037 # DIGIT SEVEN 0x38 U+0038 # DIGIT EIGHT 0x39 U+0039 # DIGIT NINE 0x3a U+003A # COLON 0x3b U+003B # SEMICOLON 0x3c U+003C # LESS-THAN SIGN 0x3d U+003D # EQUALS SIGN 0x3e U+003E # GREATER-THAN SIGN 0x3f U+003F # QUESTION MARK 0x40 U+0040 # COMMERCIAL AT 0x41 U+0041 # LATIN CAPITAL LETTER A 0x42 U+0042 # LATIN CAPITAL LETTER B 0x43 U+0043 # LATIN CAPITAL LETTER C 0x44 U+0044 # LATIN CAPITAL LETTER D 0x45 U+0045 # LATIN CAPITAL LETTER E 0x46 U+0046 # LATIN CAPITAL LETTER F 0x47 U+0047 # LATIN CAPITAL LETTER G 0x48 U+0048 # LATIN CAPITAL LETTER H 0x49 U+0049 # LATIN CAPITAL LETTER I 0x4a U+004A # LATIN CAPITAL LETTER J 0x4b U+004B # LATIN CAPITAL LETTER K 0x4c U+004C # LATIN CAPITAL LETTER L 0x4d U+004D # LATIN CAPITAL LETTER M 0x4e U+004E # LATIN CAPITAL LETTER N 0x4f U+004F # LATIN CAPITAL LETTER O 0x50 U+0050 # LATIN CAPITAL LETTER P 0x51 U+0051 # LATIN CAPITAL LETTER Q 0x52 U+0052 # LATIN CAPITAL LETTER R 0x53 U+0053 # LATIN CAPITAL LETTER S 0x54 U+0054 # LATIN CAPITAL LETTER T 0x55 U+0055 # LATIN CAPITAL LETTER U 0x56 U+0056 # LATIN CAPITAL LETTER V 0x57 U+0057 # LATIN CAPITAL LETTER W 0x58 U+0058 # LATIN CAPITAL LETTER X 0x59 U+0059 # LATIN CAPITAL LETTER Y 0x5a U+005A # LATIN CAPITAL LETTER Z 0x5b U+005B # LEFT SQUARE BRACKET 0x5c U+005C # REVERSE SOLIDUS 0x5d U+005D # RIGHT SQUARE BRACKET 0x5e U+005E # CIRCUMFLEX ACCENT 0x5f U+005F # LOW LINE 0x60 U+0060 # GRAVE ACCENT 0x61 U+0061 # LATIN SMALL LETTER A 0x62 U+0062 # LATIN SMALL LETTER B 0x63 U+0063 # LATIN SMALL LETTER C 0x64 U+0064 # LATIN SMALL LETTER D 0x65 U+0065 # LATIN SMALL LETTER E 0x66 U+0066 # LATIN SMALL LETTER F 0x67 U+0067 # LATIN SMALL LETTER G 0x68 U+0068 # LATIN SMALL LETTER H 0x69 U+0069 # LATIN SMALL LETTER I 0x6a U+006A # LATIN SMALL LETTER J 0x6b U+006B # LATIN SMALL LETTER K 0x6c U+006C # LATIN SMALL LETTER L 0x6d U+006D # LATIN SMALL LETTER M 0x6e U+006E # LATIN SMALL LETTER N 0x6f U+006F # LATIN SMALL LETTER O 0x70 U+0070 # LATIN SMALL LETTER P 0x71 U+0071 # LATIN SMALL LETTER Q 0x72 U+0072 # LATIN SMALL LETTER R 0x73 U+0073 # LATIN SMALL LETTER S 0x74 U+0074 # LATIN SMALL LETTER T 0x75 U+0075 # LATIN SMALL LETTER U 0x76 U+0076 # LATIN SMALL LETTER V 0x77 U+0077 # LATIN SMALL LETTER W 0x78 U+0078 # LATIN SMALL LETTER X 0x79 U+0079 # LATIN SMALL LETTER Y 0x7a U+007A # LATIN SMALL LETTER Z 0x7b U+007B # LEFT CURLY BRACKET 0x7c U+007C # VERTICAL LINE 0x7d U+007D # RIGHT CURLY BRACKET 0x7e U+007E # TILDE 0x7f U+007F # DELETE (DEL) 0x80 U+2500 # BOX DRAWINGS LIGHT HORIZONTAL 0x81 U+2502 # BOX DRAWINGS LIGHT VERTICAL 0x82 U+250C # BOX DRAWINGS LIGHT DOWN AND RIGHT 0x83 U+2510 # BOX DRAWINGS LIGHT DOWN AND LEFT 0x84 U+2514 # BOX DRAWINGS LIGHT UP AND RIGHT 0x85 U+2518 # BOX DRAWINGS LIGHT UP AND LEFT 0x86 U+251C # BOX DRAWINGS LIGHT VERTICAL AND RIGHT 0x87 U+2524 # BOX DRAWINGS LIGHT VERTICAL AND LEFT 0x88 U+252C # BOX DRAWINGS LIGHT DOWN AND HORIZONTAL 0x89 U+2534 # BOX DRAWINGS LIGHT UP AND HORIZONTAL 0x8a U+253C # BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL 0x8b U+2580 # UPPER HALF BLOCK 0x8c U+2584 # LOWER HALF BLOCK 0x8d U+2588 # FULL BLOCK 0x8e U+258C # LEFT HALF BLOCK 0x8f U+2590 # RIGHT HALF BLOCK 0x90 U+2591 # LIGHT SHADE 0x91 U+2592 # MEDIUM SHADE 0x92 U+2593 # DARK SHADE 0x93 U+2320 # TOP HALF INTEGRAL 0x94 U+25A0 # BLACK SQUARE 0x95 U+2219 # BULLET OPERATOR 0x96 U+221A # SQUARE ROOT 0x97 U+2248 # ALMOST EQUAL TO 0x98 U+2264 # LESS-THAN OR EQUAL TO 0x99 U+2265 # GREATER-THAN OR EQUAL TO 0x9a U+00A0 # NO-BREAK SPACE 0x9b U+2321 # BOTTOM HALF INTEGRAL 0x9c U+00B0 # DEGREE SIGN 0x9d U+00B2 # SUPERSCRIPT TWO 0x9e U+00B7 # MIDDLE DOT 0x9f U+00F7 # DIVISION SIGN 0xa0 U+2550 # BOX DRAWINGS DOUBLE HORIZONTAL 0xa1 U+2551 # BOX DRAWINGS DOUBLE VERTICAL 0xa2 U+2552 # BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE 0xa3 U+0451 # CYRILLIC SMALL LETTER IO 0xa4 U+2553 # BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE 0xa5 U+2554 # BOX DRAWINGS DOUBLE DOWN AND RIGHT 0xa6 U+2555 # BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE 0xa7 U+2556 # BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE 0xa8 U+2557 # BOX DRAWINGS DOUBLE DOWN AND LEFT 0xa9 U+2558 # BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE 0xaa U+2559 # BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE 0xab U+255A # BOX DRAWINGS DOUBLE UP AND RIGHT 0xac U+255B # BOX DRAWINGS UP SINGLE AND LEFT DOUBLE 0xad U+255C # BOX DRAWINGS UP DOUBLE AND LEFT SINGLE 0xae U+255D # BOX DRAWINGS DOUBLE UP AND LEFT 0xaf U+255E # BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE 0xb0 U+255F # BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE 0xb1 U+2560 # BOX DRAWINGS DOUBLE VERTICAL AND RIGHT 0xb2 U+2561 # BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE 0xb3 U+0401 # CYRILLIC CAPITAL LETTER IO 0xb4 U+2562 # BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE 0xb5 U+2563 # BOX DRAWINGS DOUBLE VERTICAL AND LEFT 0xb6 U+2564 # BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE 0xb7 U+2565 # BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE 0xb8 U+2566 # BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL 0xb9 U+2567 # BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE 0xba U+2568 # BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE 0xbb U+2569 # BOX DRAWINGS DOUBLE UP AND HORIZONTAL 0xbc U+256A # BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE 0xbd U+256B # BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE 0xbe U+256C # BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL 0xbf U+00A9 # COPYRIGHT SIGN 0xc0 U+044E # CYRILLIC SMALL LETTER YU 0xc1 U+0430 # CYRILLIC SMALL LETTER A 0xc2 U+0431 # CYRILLIC SMALL LETTER BE 0xc3 U+0446 # CYRILLIC SMALL LETTER TSE 0xc4 U+0434 # CYRILLIC SMALL LETTER DE 0xc5 U+0435 # CYRILLIC SMALL LETTER IE 0xc6 U+0444 # CYRILLIC SMALL LETTER EF 0xc7 U+0433 # CYRILLIC SMALL LETTER GHE 0xc8 U+0445 # CYRILLIC SMALL LETTER HA 0xc9 U+0438 # CYRILLIC SMALL LETTER I 0xca U+0439 # CYRILLIC SMALL LETTER SHORT I 0xcb U+043A # CYRILLIC SMALL LETTER KA 0xcc U+043B # CYRILLIC SMALL LETTER EL 0xcd U+043C # CYRILLIC SMALL LETTER EM 0xce U+043D # CYRILLIC SMALL LETTER EN 0xcf U+043E # CYRILLIC SMALL LETTER O 0xd0 U+043F # CYRILLIC SMALL LETTER PE 0xd1 U+044F # CYRILLIC SMALL LETTER YA 0xd2 U+0440 # CYRILLIC SMALL LETTER ER 0xd3 U+0441 # CYRILLIC SMALL LETTER ES 0xd4 U+0442 # CYRILLIC SMALL LETTER TE 0xd5 U+0443 # CYRILLIC SMALL LETTER U 0xd6 U+0436 # CYRILLIC SMALL LETTER ZHE 0xd7 U+0432 # CYRILLIC SMALL LETTER VE 0xd8 U+044C # CYRILLIC SMALL LETTER SOFT SIGN 0xd9 U+044B # CYRILLIC SMALL LETTER YERU 0xda U+0437 # CYRILLIC SMALL LETTER ZE 0xdb U+0448 # CYRILLIC SMALL LETTER SHA 0xdc U+044D # CYRILLIC SMALL LETTER E 0xdd U+0449 # CYRILLIC SMALL LETTER SHCHA 0xde U+0447 # CYRILLIC SMALL LETTER CHE 0xdf U+044A # CYRILLIC SMALL LETTER HARD SIGN 0xe0 U+042E # CYRILLIC CAPITAL LETTER YU 0xe1 U+0410 # CYRILLIC CAPITAL LETTER A 0xe2 U+0411 # CYRILLIC CAPITAL LETTER BE 0xe3 U+0426 # CYRILLIC CAPITAL LETTER TSE 0xe4 U+0414 # CYRILLIC CAPITAL LETTER DE 0xe5 U+0415 # CYRILLIC CAPITAL LETTER IE 0xe6 U+0424 # CYRILLIC CAPITAL LETTER EF 0xe7 U+0413 # CYRILLIC CAPITAL LETTER GHE 0xe8 U+0425 # CYRILLIC CAPITAL LETTER HA 0xe9 U+0418 # CYRILLIC CAPITAL LETTER I 0xea U+0419 # CYRILLIC CAPITAL LETTER SHORT I 0xeb U+041A # CYRILLIC CAPITAL LETTER KA 0xec U+041B # CYRILLIC CAPITAL LETTER EL 0xed U+041C # CYRILLIC CAPITAL LETTER EM 0xee U+041D # CYRILLIC CAPITAL LETTER EN 0xef U+041E # CYRILLIC CAPITAL LETTER O 0xf0 U+041F # CYRILLIC CAPITAL LETTER PE 0xf1 U+042F # CYRILLIC CAPITAL LETTER YA 0xf2 U+0420 # CYRILLIC CAPITAL LETTER ER 0xf3 U+0421 # CYRILLIC CAPITAL LETTER ES 0xf4 U+0422 # CYRILLIC CAPITAL LETTER TE 0xf5 U+0423 # CYRILLIC CAPITAL LETTER U 0xf6 U+0416 # CYRILLIC CAPITAL LETTER ZHE 0xf7 U+0412 # CYRILLIC CAPITAL LETTER VE 0xf8 U+042C # CYRILLIC CAPITAL LETTER SOFT SIGN 0xf9 U+042B # CYRILLIC CAPITAL LETTER YERU 0xfa U+0417 # CYRILLIC CAPITAL LETTER ZE 0xfb U+0428 # CYRILLIC CAPITAL LETTER SHA 0xfc U+042D # CYRILLIC CAPITAL LETTER E 0xfd U+0429 # CYRILLIC CAPITAL LETTER SHCHA 0xfe U+0427 # CYRILLIC CAPITAL LETTER CHE 0xff U+042A # CYRILLIC CAPITAL LETTER HARD SIGN ���������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ifplugd_if/�����������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�020225� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ifplugd_if/run��������������������������������������������������0000755�0000000�0000000�00000002306�12263563520�020763� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh #exec >/dev/null exec 2>&1 exec </dev/null pwd="$PWD" if="${PWD##*/ifplugd_}" echo "* Starting ifplugd on $if [$$]" exec \ env - PATH="$PATH" \ softlimit \ setuidgid root \ ifplugd -apqns -t3 -u8 -d8 -i "$if" -r "$pwd/ifplugd_handler" # We use -t3 to wake ifplugd up less often. # If after three tests (3*3=9 > 8) link state seen to be different, # the handler will be called. # IOW: short link losses will be ignored, longer ones # will trigger DHCP reconfiguration and such (see handler code). #-a Do not up interface automatically #-p Dont run script on daemon startup #-q Dont run script on daemon quit #-n Do not daemonize #-s Do not log to syslog #-t SECS Poll time in seconds #-u SECS Delay before running script after link up #-d SECS Delay after link down #-i IFACE Interface #-r PROG Script to run #-f/-F Treat link detection error as link down/link up (otherwise exit on error) #-M Monitor creation/destruction of interface (otherwise it must exist) #-x ARG Extra argument for script #-I Dont exit on nonzero exit code from script #-l Run script on startup even if no cable is detected #-m MODE API mode (mii, priv, ethtool, wlan, auto) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ifplugd_if/w_log������������������������������������������������0000755�0000000�0000000�00000000174�12263563520�021267� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cd log/logdir || exit 1 watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))' ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ifplugd_if/log/�������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�021006� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ifplugd_if/log/run����������������������������������������������0000755�0000000�0000000�00000000547�12263563520�021551� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh user=logger logdir="/var/log/service/`(cd ..;basename $PWD)`" mkdir -p "$logdir" 2>/dev/null chown -R "$user": "$logdir" chmod -R go-rwxst,u+rwX "$logdir" rm logdir ln -s "$logdir" logdir # make this dir accessible to logger chmod a+rX . exec >/dev/null exec 2>&1 exec \ env - PATH="$PATH" \ softlimit \ setuidgid "$user" \ svlogd -tt "$logdir" ���������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ifplugd_if/ifplugd_handler��������������������������������������0000755�0000000�0000000�00000000453�12263563520�023307� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # parameters: # $1: interface # $2: state if test -d "/var/service/dhcp_$1"; then if test x"$2" = x"down"; then echo "Downing /var/service/dhcp_$1" sv d "/var/service/dhcp_$1" fi if test x"$2" = x"up"; then echo "Upping /var/service/dhcp_$1" sv u "/var/service/dhcp_$1" fi fi ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ifplugd_if/p_log������������������������������������������������0000755�0000000�0000000�00000000073�12263563520�021256� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cd log/logdir || exit 1 cat @* current | $PAGER ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/tftpd/����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�017236� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/tftpd/run�������������������������������������������������������0000755�0000000�0000000�00000000305�12263563520�017771� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh #exec >/dev/null exec 2>&1 exec </dev/null user=root # for bind to port 69 exec \ env - \ softlimit \ setuidgid "$user" \ udpsvd -v -c 10 -l localhost \ 0 69 \ tftpd /pub/tftpd_root ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/tftpd/w_log�����������������������������������������������������0000755�0000000�0000000�00000000174�12263563520�020300� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cd log/logdir || exit 1 watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))' ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/tftpd/log/������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�020017� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/tftpd/log/run���������������������������������������������������0000755�0000000�0000000�00000000547�12263563520�020562� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh user=logger logdir="/var/log/service/`(cd ..;basename $PWD)`" mkdir -p "$logdir" 2>/dev/null chown -R "$user": "$logdir" chmod -R go-rwxst,u+rwX "$logdir" rm logdir ln -s "$logdir" logdir # make this dir accessible to logger chmod a+rX . exec >/dev/null exec 2>&1 exec \ env - PATH="$PATH" \ softlimit \ setuidgid "$user" \ svlogd -tt "$logdir" ���������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/tftpd/p_log�����������������������������������������������������0000755�0000000�0000000�00000000073�12263563520�020267� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cd log/logdir || exit 1 cat @* current | $PAGER ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/httpd/����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�017240� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/httpd/run�������������������������������������������������������0000755�0000000�0000000�00000000376�12263563520�020003� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh #exec >/dev/null exec 2>&1 exec </dev/null user=www user=root echo "* Starting tcpsvd for httpd [$$]" exec \ env - PATH="$PATH" \ softlimit \ tcpsvd \ -v -E -l localhost -c 5 \ 0 88 \ setuidgid "$user" \ httpd -vvv -i -h /pub/httpd_root ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/httpd/w_log�����������������������������������������������������0000755�0000000�0000000�00000000174�12263563520�020302� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cd log/logdir || exit 1 watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))' ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/httpd/log/������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�020021� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/httpd/log/run���������������������������������������������������0000755�0000000�0000000�00000000547�12263563520�020564� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh user=logger logdir="/var/log/service/`(cd ..;basename $PWD)`" mkdir -p "$logdir" 2>/dev/null chown -R "$user": "$logdir" chmod -R go-rwxst,u+rwX "$logdir" rm logdir ln -s "$logdir" logdir # make this dir accessible to logger chmod a+rX . exec >/dev/null exec 2>&1 exec \ env - PATH="$PATH" \ softlimit \ setuidgid "$user" \ svlogd -tt "$logdir" ���������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/httpd/p_log�����������������������������������������������������0000755�0000000�0000000�00000000073�12263563520�020271� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cd log/logdir || exit 1 cat @* current | $PAGER ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/gpm/������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�016700� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/gpm/run���������������������������������������������������������0000755�0000000�0000000�00000000326�12263563520�017436� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh exec >/dev/null exec 2>&1 exec </dev/null user=root options="-D -2 -m /dev/psaux -t ps2" #options="-D -2 -m /dev/ttyS0 -t bare" exec \ env - PATH="$PATH" \ softlimit \ setuidgid "$user" \ gpm $options ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ntpd/�����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�017062� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ntpd/run��������������������������������������������������������0000755�0000000�0000000�00000002213�12263563520�017615� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # (using bashism (arrays) in dhcp config) #exec >/dev/null exec 2>&1 exec </dev/null user=root pool="us.pool.ntp.org" # replace "us" with your country code service="${PWD##*/}" rundir="/var/run/service/$service" default_p_opt="-p 0.$pool -p 1.$pool -p 2.$pool -p 3.$pool" # Make sure rundir/ exists mkdir -p "$rundir" 2>/dev/null chown -R "$user:" "$rundir" chmod -R a=rX "$rundir" rm -rf rundir 2>/dev/null ln -s "$rundir" rundir echo "* Checking network" test -f /var/run/service/fw/up || exec sleep 7 sleep 5 # to let it settle # Grab config from dhcp cfg=-1 for f in rundir/*.ntpconf; do test -f "$f" || continue . "$f" done # Select peers p_opt="" cfg=0 while test x"${ntpip[$cfg]}" != x""; do p_opt="$p_opt -p ${ntpip[$cfg]}" let cfg=cfg+1 done test x"$p_opt" == x"" && p_opt="$default_p_opt" if test x"$p_opt" == x""; then echo "* No NTP peers configured, stopping" sv o . exec sleep 1 fi # Let others know that we are up date '+%Y-%m-%d %H:%M:%S %Z' >rundir/up # Go go go echo "* Starting ntpd[$$]" exec \ env - PATH="$PATH" \ softlimit \ setuidgid "$user" \ ntpd -ddnNl -S ./ntp.script $p_opt �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ntpd/w_log������������������������������������������������������0000755�0000000�0000000�00000000174�12263563520�020124� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cd log/logdir || exit 1 watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))' ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ntpd/log/�������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�017643� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ntpd/log/run����������������������������������������������������0000755�0000000�0000000�00000000547�12263563520�020406� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh user=logger logdir="/var/log/service/`(cd ..;basename $PWD)`" mkdir -p "$logdir" 2>/dev/null chown -R "$user": "$logdir" chmod -R go-rwxst,u+rwX "$logdir" rm logdir ln -s "$logdir" logdir # make this dir accessible to logger chmod a+rX . exec >/dev/null exec 2>&1 exec \ env - PATH="$PATH" \ softlimit \ setuidgid "$user" \ svlogd -tt "$logdir" ���������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ntpd/ntp.script�������������������������������������������������0000755�0000000�0000000�00000002003�12263563520�021112� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Note that there is no provision to prevent several copies of the script # to be run in quick succession. In fact, it happens rather often # if initial syncronization results in a step. # You will see "step" and then "stratum" script runs, sometimes # as close as only 0.002 seconds apart. # # Script should be ready to deal with this. dt=`date '+%Y-%m-%d %H:%M:%S'` if test x"$stratum" != x"" \ && test x"$poll_interval" != x"" \ && test 4 -ge "$stratum" \ && test 128 -le "$poll_interval" \ ; then echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$" echo "$dt: $1"\ "freq_drift_ppm=$freq_drift_ppm"\ "offset=$offset"\ "stratum=$stratum"\ "poll_interval=$poll_interval,"\ "setting hardware clock"\ >>"$0.log.$$" mv -- "$0.log.$$" "$0.log" exec hwclock --systohc fi echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$" echo "$dt: $1"\ "freq_drift_ppm=$freq_drift_ppm"\ "offset=$offset"\ "stratum=$stratum"\ "poll_interval=$poll_interval"\ >>"$0.log.$$" mv -- "$0.log.$$" "$0.log" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/var_service/ntpd/p_log������������������������������������������������������0000755�0000000�0000000�00000000073�12263563520�020113� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cd log/logdir || exit 1 cat @* current | $PAGER ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/inittab���������������������������������������������������������������������0000644�0000000�0000000�00000005773�12263563520�015201� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# /etc/inittab init(8) configuration for BusyBox # # Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> # # # Note, BusyBox init doesn't support runlevels. The runlevels field is # completely ignored by BusyBox init. If you want runlevels, use sysvinit. # # # Format for each entry: <id>:<runlevels>:<action>:<process> # # <id>: WARNING: This field has a non-traditional meaning for BusyBox init! # # The id field is used by BusyBox init to specify the controlling tty for # the specified process to run on. The contents of this field are # appended to "/dev/" and used as-is. There is no need for this field to # be unique, although if it isn't you may have strange results. If this # field is left blank, then the init's stdin/out will be used. # # <runlevels>: The runlevels field is completely ignored. # # <action>: Valid actions include: sysinit, respawn, askfirst, wait, once, # restart, ctrlaltdel, and shutdown. # # Note: askfirst acts just like respawn, but before running the specified # process it displays the line "Please press Enter to activate this # console." and then waits for the user to press enter before starting # the specified process. # # Note: unrecognized actions (like initdefault) will cause init to emit # an error message, and then go along with its business. # # <process>: Specifies the process to be executed and it's command line. # # Note: BusyBox init works just fine without an inittab. If no inittab is # found, it has the following default behavior: # ::sysinit:/etc/init.d/rcS # ::askfirst:/bin/sh # ::ctrlaltdel:/sbin/reboot # ::shutdown:/sbin/swapoff -a # ::shutdown:/bin/umount -a -r # ::restart:/sbin/init # tty2::askfirst:/bin/sh # tty3::askfirst:/bin/sh # tty4::askfirst:/bin/sh # # Boot-time system configuration/initialization script. # This is run first except when booting in single-user mode. # ::sysinit:/etc/init.d/rcS # /bin/sh invocations on selected ttys # # Note below that we prefix the shell commands with a "-" to indicate to the # shell that it is supposed to be a login shell. Normally this is handled by # login, but since we are bypassing login in this case, BusyBox lets you do # this yourself... # # Start an "askfirst" shell on the console (whatever that may be) ::askfirst:-/bin/sh # Start an "askfirst" shell on /dev/tty2-4 tty2::askfirst:-/bin/sh tty3::askfirst:-/bin/sh tty4::askfirst:-/bin/sh # /sbin/getty invocations for selected ttys tty4::respawn:/sbin/getty 38400 tty5 tty5::respawn:/sbin/getty 38400 tty6 # Example of how to put a getty on a serial line (for a terminal) #::respawn:/sbin/getty -L ttyS0 9600 vt100 #::respawn:/sbin/getty -L ttyS1 9600 vt100 # # Example how to put a getty on a modem line. #::respawn:/sbin/getty 57600 ttyS2 # Stuff to do when restarting the init process ::restart:/sbin/init # Stuff to do before rebooting ::ctrlaltdel:/sbin/reboot ::shutdown:/bin/umount -a -r ::shutdown:/sbin/swapoff -a �����busybox-1.22.1/examples/dnsd.conf�������������������������������������������������������������������0000644�0000000�0000000�00000000023�12263563520�015402� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������thebox 192.168.1.5 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/depmod����������������������������������������������������������������������0000755�0000000�0000000�00000002424�12263563520�015010� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # # Simple depmod, use to generate modprobe.conf # # Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com> # # Licensed under GPLv2, see file LICENSE in this source tree. # local BASE="${1:-/usr/lib/modules}" find "$BASE" -name '*.ko.gz' | while read I ; do N=`basename "$I" '.ko.gz'` echo -n "@$N" zcat "$I" | strings | grep '^depends=' | sed -e 's/^depends=$//' -e 's/^depends=/,/' -e 's/,/ @/g' done | awk ' { # modules which has no dependencies are resolved if ( NF == 1 ) { res[$1] = ""; next } # others have to be resolved based on those which already resolved i = $1; $1 = ""; deps[i] = $0; ++ndeps } END { # resolve implicit dependencies while ( ndeps ) for (mod in deps) { if ( index(deps[mod], "@") > 0 ) { $0 = deps[mod] for ( i=1; i<=NF; ++i ) { if ( substr($i,1,1) == "@" ) { if ( $i in res ) { $i = res[$i] " " substr($i,2) } } } deps[mod] = $0 } else { res[mod] = deps[mod] delete deps[mod] --ndeps } } # output dependencies in modules.dep format for ( mod in res ) { $0 = res[mod] s = "" delete a for ( i=1; i<=NF; ++i ) { if ( ! ($i in a) ) { a[$i] = $i s = " ," $i s } } print "," substr(mod,2) ":" s } } ' | sort | sed -r -e "s!,([^,: ]*)!/usr/lib/modules/\\1.ko.gz!g" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/bootfloppy/�����������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�016002� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/bootfloppy/mkrootfs.sh������������������������������������������������������0000755�0000000�0000000�00000004437�12263563520�020220� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # # mkrootfs.sh - creates a root file system # # TODO: need to add checks here to verify that busybox, uClibc and bzImage # exist # command-line settable variables BUSYBOX_DIR=.. UCLIBC_DIR=../../uClibc TARGET_DIR=./loop FSSIZE=4000 CLEANUP=1 MKFS='mkfs.ext2 -F' # don't-touch variables BASE_DIR=`pwd` while getopts 'b:u:s:t:Cm' opt do case $opt in b) BUSYBOX_DIR=$OPTARG ;; u) UCLIBC_DIR=$OPTARG ;; t) TARGET_DIR=$OPTARG ;; s) FSSIZE=$OPTARG ;; C) CLEANUP=0 ;; m) MKFS='mkfs.minix' ;; *) echo "usage: `basename $0` [-bu]" echo " -b DIR path to busybox direcory (default ..)" echo " -u DIR path to uClibc direcory (default ../../uClibc)" echo " -t DIR path to target direcory (default ./loop)" echo " -s SIZE size of root filesystem in Kbytes (default 4000)" echo " -C don't perform cleanup (umount target dir, gzip rootfs, etc.)" echo " (this allows you to 'chroot loop/ /bin/sh' to test it)" echo " -m use minix filesystem (default is ext2)" exit 1 ;; esac done # clean up from any previous work mount | grep -q loop [ $? -eq 0 ] && umount $TARGET_DIR [ -d $TARGET_DIR ] && rm -rf $TARGET_DIR/ [ -f rootfs ] && rm -f rootfs [ -f rootfs.gz ] && rm -f rootfs.gz # prepare root file system and mount as loopback dd if=/dev/zero of=rootfs bs=1k count=$FSSIZE $MKFS -i 2000 rootfs mkdir $TARGET_DIR mount -o loop,exec rootfs $TARGET_DIR # must be root # install uClibc mkdir -p $TARGET_DIR/lib cd $UCLIBC_DIR make INSTALL_DIR= cp -a libc.so* $BASE_DIR/$TARGET_DIR/lib cp -a uClibc*.so $BASE_DIR/$TARGET_DIR/lib cp -a ld.so-1/d-link/ld-linux-uclibc.so* $BASE_DIR/$TARGET_DIR/lib cp -a ld.so-1/libdl/libdl.so* $BASE_DIR/$TARGET_DIR/lib cp -a crypt/libcrypt.so* $BASE_DIR/$TARGET_DIR/lib cd $BASE_DIR # install busybox and components cd $BUSYBOX_DIR make distclean make CC=$BASE_DIR/$UCLIBC_DIR/extra/gcc-uClibc/i386-uclibc-gcc make CONFIG_PREFIX=$BASE_DIR/$TARGET_DIR install cd $BASE_DIR # make files in /dev mkdir $TARGET_DIR/dev ./mkdevs.sh $TARGET_DIR/dev # make files in /etc cp -a etc $TARGET_DIR ln -s /proc/mounts $TARGET_DIR/etc/mtab # other miscellaneous setup mkdir $TARGET_DIR/initrd mkdir $TARGET_DIR/proc # Done. Maybe do cleanup. if [ $CLEANUP -eq 1 ] then umount $TARGET_DIR rmdir $TARGET_DIR gzip -9 rootfs fi ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/bootfloppy/quickstart.txt���������������������������������������������������0000644�0000000�0000000�00000001076�12263563520�020744� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Quickstart on making the Busybox boot-floppy: 1) Download Busybox and uClibc from CVS or tarballs. Make sure they share a common parent directory. (i.e. busybox/ and uclibc/ are both right off of /tmp, or wherever.) 2) Build a Linux kernel. Make sure you include support for initrd. 3) Put a floppy in the drive. Make sure it is a floppy you don't care about because the contents will be overwritten. 4) As root, type ./mksyslinux.sh path/to/linux/kernel from this directory. Wait patiently while the magic happens. 5) Boot up on the floppy. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/bootfloppy/mkdevs.sh��������������������������������������������������������0000755�0000000�0000000�00000002022�12263563520�017631� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # # makedev.sh - creates device files for a busybox boot floppy image # we do our work in the dev/ directory if [ -z "$1" ]; then echo "usage: `basename $0` path/to/dev/dir" exit 1 fi cd $1 # miscellaneous one-of-a-kind stuff mknod console c 5 1 mknod full c 1 7 mknod kmem c 1 2 mknod mem c 1 1 mknod null c 1 3 mknod port c 1 4 mknod random c 1 8 mknod urandom c 1 9 mknod zero c 1 5 ln -s /proc/kcore core # IDE HD devs # note: not going to bother creating all concievable partitions; you can do # that yourself as you need 'em. mknod hda b 3 0 mknod hdb b 3 64 mknod hdc b 22 0 mknod hdd b 22 64 # loop devs for i in `seq 0 7`; do mknod loop$i b 7 $i done # ram devs for i in `seq 0 9`; do mknod ram$i b 1 $i done ln -s ram1 ram # ttys mknod tty c 5 0 for i in `seq 0 9`; do mknod tty$i c 4 $i done # virtual console screen devs for i in `seq 0 9`; do mknod vcs$i b 7 $i done ln -s vcs0 vcs # virtual console screen w/ attributes devs for i in `seq 0 9`; do mknod vcsa$i b 7 $((128 + i)) done ln -s vcsa0 vcsa ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/bootfloppy/display.txt������������������������������������������������������0000644�0000000�0000000�00000000207�12263563520�020212� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� This boot floppy is made with Busybox, uClibc, and the Linux kernel. Hit RETURN to boot or enter boot parameters at the prompt below. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/bootfloppy/etc/�������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�016555� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/bootfloppy/etc/profile������������������������������������������������������0000644�0000000�0000000�00000000205�12263563520�020140� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# /etc/profile: system-wide .profile file for the Bourne shells echo echo -n "Processing /etc/profile... " # no-op echo "Done" echo �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/bootfloppy/etc/inittab������������������������������������������������������0000644�0000000�0000000�00000000144�12263563520�020134� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������::sysinit:/etc/init.d/rcS ::respawn:-/bin/sh tty2::askfirst:-/bin/sh ::ctrlaltdel:/bin/umount -a -r ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/bootfloppy/etc/init.d/������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�017742� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/bootfloppy/etc/init.d/rcS���������������������������������������������������0000755�0000000�0000000�00000000032�12263563520�020415� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#! /bin/sh /bin/mount -a ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/bootfloppy/etc/fstab��������������������������������������������������������0000644�0000000�0000000�00000000041�12263563520�017575� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������proc /proc proc defaults 0 0 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/bootfloppy/mksyslinux.sh����������������������������������������������������0000755�0000000�0000000�00000002237�12263563520�020576� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # # Formats a floppy to use Syslinux dummy="" # need to have mtools installed if [ -z `which mformat` -o -z `which mcopy` ]; then echo "You must have the mtools package installed to run this script" exit 1 fi # need an arg for the location of the kernel if [ -z "$1" ]; then echo "usage: `basename $0` path/to/linux/kernel" exit 1 fi # need to have a root file system built if [ ! -f rootfs.gz ]; then echo "You need to have a rootfs built first." echo "Hit RETURN to make one now or Control-C to quit." read dummy ./mkrootfs.sh fi # prepare the floppy echo "Please insert a blank floppy in the drive and press RETURN to format" echo "(WARNING: All data will be erased! Hit Control-C to abort)" read dummy echo "Formatting the floppy..." mformat a: echo "Making it bootable with Syslinux..." syslinux -s /dev/fd0 echo "Copying Syslinux configuration files..." mcopy syslinux.cfg display.txt a: echo "Copying root filesystem file..." mcopy rootfs.gz a: # XXX: maybe check for "no space on device" errors here echo "Copying linux kernel..." mcopy $1 a:linux # XXX: maybe check for "no space on device" errors here too echo "Finished: boot floppy created" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/bootfloppy/syslinux.cfg�����������������������������������������������������0000644�0000000�0000000�00000000170�12263563520�020362� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������display display.txt default linux timeout 10 prompt 1 label linux kernel linux append initrd=rootfs.gz root=/dev/ram0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/bootfloppy/bootfloppy.txt���������������������������������������������������0000644�0000000�0000000�00000011006�12263563520�020741� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Building a Busybox Boot Floppy ============================== This document describes how to buid a boot floppy using the following components: - Linux Kernel (http://www.kernel.org) - uClibc: C library (http://www.uclibc.org/) - Busybox: Unix utilities (http://busybox.net/) - Syslinux: bootloader (http://syslinux.zytor.com) It is based heavily on a paper presented by Erik Andersen at the 2001 Embedded Systems Conference. Building The Software Components -------------------------------- Detailed instructions on how to build Busybox, uClibc, or a working Linux kernel are beyond the scope of this document. The following guidelines will help though: - Stock Busybox from CVS or a tarball will work with no modifications to any files. Just extract and go. - Ditto uClibc. - Your Linux kernel must include support for initrd or else the floppy won't be able to mount it's root file system. If you require further information on building Busybox uClibc or Linux, please refer to the web pages and documentation for those individual programs. Making a Root File System ------------------------- The following steps will create a root file system. - Create an empty file that you can format as a filesystem: dd if=/dev/zero of=rootfs bs=1k count=4000 - Set up the rootfs file we just created to be used as a loop device (may not be necessary) losetup /dev/loop0 rootfs - Format the rootfs file with a filesystem: mkfs.ext2 -F -i 2000 rootfs - Mount the file on a mountpoint so we can place files in it: mkdir loop mount -o loop rootfs loop/ (you will probably need to be root to do this) - Copy on the C library, the dynamic linking library, and other necessary libraries. For this example, we copy the following files from the uClibc tree: mkdir loop/lib (chdir to uClibc directory) cp -a libc.so* uClibc*.so \ ld.so-1/d-link/ld-linux-uclibc.so* \ ld.so-1/libdl/libdl.so* \ crypt/libcrypt.so* \ (path to)loop/lib - Install the Busybox binary and accompanying symlinks: (chdir to busybox directory) make CONFIG_PREFIX=(path to)loop/ install - Make device files in /dev: This can be done by running the 'mkdevs.sh' script. If you want the gory details, you can read the script. - Make necessary files in /etc: For this, just cp -a the etc/ directory onto rootfs. Again, if you want all the details, you can just look at the files in the dir. - Unmount the rootfs from the mountpoint: umount loop - Compress it: gzip -9 rootfs Making a SYSLINUX boot floppy ----------------------------- The following steps will create the boot floppy. Note: You will need to have the mtools package installed beforehand. - Insert a floppy in the drive and format it with an MSDOS filesystem: mformat a: (if the system doesn't know what device 'a:' is, look at /etc/mtools.conf) - Run syslinux on the floppy: syslinux -s /dev/fd0 (the -s stands for "safe, slow, and stupid" and should work better with buggy BIOSes; it can be omitted) - Put on a syslinux.cfg file: mcopy syslinux.cfg a: (more on syslinux.cfg below) - Copy the root file system you made onto the MSDOS formatted floppy mcopy rootfs.gz a: - Build a linux kernel and copy it onto the disk with the filename 'linux' mcopy bzImage a:linux Sample syslinux.cfg ~~~~~~~~~~~~~~~~~~~ The following simple syslinux.cfg file should work. You can tweak it if you like. ----begin-syslinux.cfg--------------- DEFAULT linux APPEND initrd=rootfs.gz root=/dev/ram0 TIMEOUT 10 PROMPT 1 ----end-syslinux.cfg--------------- Some changes you could make to syslinux.cfg: - This value is the number seconds it will wait before booting. You can set the timeout to 0 (or omit) to boot instantly, or you can set it as high as 10 to wait awhile. - PROMPT can be set to 0 to disable the 'boot:' prompt. - you can add this line to display the contents of a file as a welcome message: DISPLAY display.txt Additional Resources -------------------- Other useful information on making a Linux bootfloppy is available at the following URLs: http://www.linuxdoc.org/HOWTO/Bootdisk-HOWTO/index.html http://www.linux-embedded.com/howto/Embedded-Linux-Howto.html http://linux-embedded.org/howto/LFS-HOWTO.html http://linux-embedded.org/pmhowto.html http://recycle.lbl.gov/~ldoolitt/embedded/ (Larry Doolittle's stuff) Possible TODOs -------------- The following features that we might want to add later: - support for additional filesystems besides ext2, i.e. minix - different libc, static vs dynamic loading - maybe using an alternate bootloader ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/mdev.conf�������������������������������������������������������������������0000644�0000000�0000000�00000001570�12263563520�015415� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # This is a sample mdev.conf # # Provide user, group, and mode information for devices. If a regex matches # the device name provided by sysfs, use the appropriate user:group and mode # instead of the default 0:0 660. # # Syntax: # [-]devicename_regex user:group mode [=path]|[>path]|[!] [@|$|*cmd args...] # [-]$ENVVAR=regex user:group mode [=path]|[>path]|[!] [@|$|*cmd args...] # [-]@maj,min[-min2] user:group mode [=path]|[>path]|[!] [@|$|*cmd args...] # # [-]: do not stop on this match, continue reading mdev.conf # =: move, >: move and create a symlink # !: do not create device node # @|$|*: run@cmd if $ACTION=add, $cmd if $ACTION=remove, *cmd in all cases null 0:0 666 zero 0:0 666 urandom 0:0 444 kmem 0:9 000 mem 0:9 640 port 0:9 640 console 0:5 600 ptmx 0:5 660 tty[0-9]* 0:5 660 ttyS[0-9]* 0:20 640 fd[0-9]* 0:11 660 sd[a-z]* 0:6 660 hd[a-z]* 0:6 660 ����������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/mdev_fat.conf���������������������������������������������������������������0000644�0000000�0000000�00000012760�12263563520�016252� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # This is a sample mdev.conf # # Provide user, group, and mode information for devices. If a regex matches # the device name provided by sysfs, use the appropriate user:group and mode # instead of the default 0:0 660. # # Syntax: # [-][ENVVAR=regex;]...devicename_regex user:group mode [=path]|[>path]|[!] [@|$|*cmd args...] # [-][ENVVAR=regex;]...@maj,min[-min2] user:group mode [=path]|[>path]|[!] [@|$|*cmd args...] # [-]$ENVVAR=regex user:group mode [=path]|[>path]|[!] [@|$|*cmd args...] # # [-]: do not stop on this match, continue reading mdev.conf # =: move, >: move and create a symlink # !: do not create device node # @|$|*: run cmd if $ACTION=remove, @cmd if $ACTION=add, *cmd in all cases # support module loading on hotplug $MODALIAS=.* root:root 660 @modprobe "$MODALIAS" # null may already exist; therefore ownership has to be changed with command null root:root 666 @chmod 666 $MDEV zero root:root 666 full root:root 666 random root:root 444 urandom root:root 444 hwrandom root:root 444 grsec root:root 660 kmem root:root 640 mem root:root 640 port root:root 640 # console may already exist; therefore ownership has to be changed with command console root:tty 600 @chmod 600 $MDEV ptmx root:tty 666 pty.* root:tty 660 # Typical devices tty root:tty 666 tty[0-9]* root:tty 660 vcsa*[0-9]* root:tty 660 ttyS[0-9]* root:uucp 660 # block devices ram([0-9]*) root:disk 660 >rd/%1 loop([0-9]+) root:disk 660 >loop/%1 sd[a-z].* root:disk 660 */lib/mdev/usbdisk_link hd[a-z][0-9]* root:disk 660 */lib/mdev/ide_links md[0-9]* root:disk 660 sr[0-9]* root:cdrom 660 @ln -sf $MDEV cdrom fd[0-9]* root:floppy 660 # net devices SUBSYSTEM=net;.* root:root 600 @nameif tun[0-9]* root:root 600 =net/ tap[0-9]* root:root 600 =net/ # alsa sound devices and audio stuff pcm.* root:audio 660 =snd/ control.* root:audio 660 =snd/ midi.* root:audio 660 =snd/ seq root:audio 660 =snd/ timer root:audio 660 =snd/ adsp root:audio 660 >sound/ audio root:audio 660 >sound/ dsp root:audio 660 >sound/ mixer root:audio 660 >sound/ sequencer.* root:audio 660 >sound/ # Less typical devices # raid controllers cciss!(.*) root:disk 660 =cciss/%1 ida!(.*) root:disk 660 =ida/%1 rd!(.*) root:disk 660 =rd/%1 ttyLTM[0-9] root:dialout 660 @ln -sf $MDEV modem ttySHSF[0-9] root:dialout 660 @ln -sf $MDEV modem slamr root:dialout 660 @ln -sf $MDEV slamr0 slusb root:dialout 660 @ln -sf $MDEV slusb0 fuse root:root 666 # dri device card[0-9] root:video 660 =dri/ # misc stuff agpgart root:root 660 >misc/ psaux root:root 660 >misc/ rtc root:root 664 >misc/ # input stuff event[0-9]+ root:root 640 =input/ mice root:root 640 =input/ mouse[0-9] root:root 640 =input/ ts[0-9] root:root 600 =input/ # v4l stuff vbi[0-9] root:video 660 >v4l/ video[0-9] root:video 660 >v4l/ # dvb stuff dvb.* root:video 660 */lib/mdev/dvbdev # load drivers for usb devices usbdev[0-9].[0-9] root:root 660 */lib/mdev/usbdev usbdev[0-9].[0-9]_.* root:root 660 # zaptel devices zap(.*) root:dialout 660 =zap/%1 dahdi!(.*) root:dialout 660 =dahdi/%1 # HTC Android # Attaching it via USB cable causes a flurry of activity: # # mdev[1271]: 48.639459 ACTION:add SUBSYSTEM:usb DEVNAME:bus/usb/001/009 DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5 # mdev[1271]: mknod bus/usb/001/009 (189,8) 20660 0:0 # mdev[1272]: 48.642354 ACTION:add SUBSYSTEM:usb DEVNAME:(null) DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0 # mdev[1272]: running: modprobe "$MODALIAS" # mdev[1273]: 48.650078 ACTION:add SUBSYSTEM:scsi DEVNAME:(null) DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host11 # mdev[1274]: 48.651297 ACTION:add SUBSYSTEM:scsi_host DEVNAME:(null) DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host11/scsi_host/host11 # mdev[1275]: 49.649422 ACTION:add SUBSYSTEM:scsi DEVNAME:(null) DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host11/target11:0:0 # mdev[1276]: 49.650703 ACTION:add SUBSYSTEM:scsi DEVNAME:(null) DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host11/target11:0:0/11:0:0:0 # mdev[1276]: running: modprobe "$MODALIAS" # modprobe: module scsi:t-0x00 not found in modules.dep # mdev[1277]: 49.658002 ACTION:add SUBSYSTEM:scsi_disk DEVNAME:(null) DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host11/target11:0:0/11:0:0:0/scsi_disk/11:0:0:0 # mdev[1278]: 49.659244 ACTION:add SUBSYSTEM:scsi_device DEVNAME:(null) DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host11/target11:0:0/11:0:0:0/scsi_device/11:0:0: # mdev[1279]: 49.660535 ACTION:add SUBSYSTEM:bsg DEVNAME:bsg/11:0:0:0 DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host11/target11:0:0/11:0:0:0/bsg/11:0:0:0 # mdev[1279]: mknod bsg/11:0:0:0 (253,1) 20660 0:0 # mdev[1280]: 49.663516 ACTION:add SUBSYSTEM:bdi DEVNAME:(null) DEVPATH:/devices/virtual/bdi/8:16 # mdev[1281]: 49.664748 ACTION:add SUBSYSTEM:block DEVNAME:sdb DEVPATH:/block/sdb # mdev[1281]: mknod sdb (8,16) 60660 0:0 # mdev[1282]: 49.677597 ACTION:change SUBSYSTEM:block DEVNAME:sdb DEVPATH:/block/sdb # mdev[1283]: 50.692936 ACTION:change SUBSYSTEM:block DEVNAME:sdb DEVPATH:/block/sdb # # We are hooking to the last events. To avoid having two scripts running, # we check for DISK_MEDIA_CHANGE=1 (prev to last event has it, # and it's the _only_ difference in env for these two events as of kernel 3.7.7) # Unfortunately, there is no event for "user pressed [Turn USB storage] btn"! # Script merely backgrounds and tries to rescan partition table for 1 minute: ACTION=change;SUBSYSTEM=block;DISK_MEDIA_CHANGE=1;.* 0:0 660 */etc/mdev.conf.change_blockdev.sh ����������������busybox-1.22.1/examples/zcip.script�����������������������������������������������������������������0000755�0000000�0000000�00000001263�12263563520�016010� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # only for use as a "zcip" callback script if [ "x$interface" = x ] then exit 1 fi # zcip should start on boot/resume and various media changes case "$1" in init) # for now, zcip requires the link to be already up, # and it drops links when they go down. that isn't # the most robust model... exit 0 ;; config) if [ "x$ip" = x ] then exit 1 fi # remember $ip for $interface, to use on restart if [ "x$ip" != x -a -w "$ip.$interface" ] then echo $ip > "$ip.$interface" fi exec ip address add dev $interface \ scope link local "$ip/16" broadcast + ;; deconfig) if [ x$ip = x ] then exit 1 fi exec ip address del dev $interface local $ip ;; esac exit 1 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/linux-2.6.30_proc_self_exe.patch��������������������������������������������0000644�0000000�0000000�00000001706�12263563520�021415� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This patch makes /proc/self/exe executable even if proc is not mounted. It is especially useful on NOMMU arches. diff -urp ../linux-2.6.30.org/fs/exec.c linux-2.6.30/fs/exec.c --- ../linux-2.6.30.org/fs/exec.c 2009-06-10 05:05:27.000000000 +0200 +++ linux-2.6.30/fs/exec.c 2009-06-25 00:20:13.000000000 +0200 @@ -652,9 +652,25 @@ struct file *open_exec(const char *name) file = do_filp_open(AT_FDCWD, name, O_LARGEFILE | O_RDONLY | FMODE_EXEC, 0, MAY_EXEC | MAY_OPEN); - if (IS_ERR(file)) - goto out; + if (IS_ERR(file)) { + if ((PTR_ERR(file) == -ENOENT || PTR_ERR(file) == -EACCES) + && strcmp(name, "/proc/self/exe") == 0 + ) { + struct file *sv = file; + struct mm_struct *mm; + mm = get_task_mm(current); + if (!mm) + goto out; + file = get_mm_exe_file(mm); + mmput(mm); + if (file) + goto ok; + file = sv; + } + goto out; + } +ok: err = -EACCES; if (!S_ISREG(file->f_path.dentry->d_inode->i_mode)) goto exit; ����������������������������������������������������������busybox-1.22.1/examples/android-build���������������������������������������������������������������0000755�0000000�0000000�00000002673�12263563520�016263� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Build Busybox against Android's bionic # Originally by Dan Fandrich # # Configure with "make android_defconfig" # # This file has been tested on Android Froyo (the lack of ttyname_r in # the android libc must be patched around) and Gingerbread. # Point this to the Android root directory; it's used in the defconfig CFLAGS export A="$HOME/android" # Android product being built P=zoom2 # Toolchain version in use by this version of Android GCCVER=4.4.3 export PATH="$A/prebuilt/linux-x86/toolchain/arm-eabi-$GCCVER/bin:$PATH" # Set the linker flags; compiler flags are in the defconfig file if grep "^CONFIG_STATIC=y" .config >/dev/null ; then # Static linking LDFLAGS="-static -Xlinker -z -Xlinker muldefs -nostdlib $A/out/target/product/$P/obj/lib/crtbegin_static.o $A/out/target/product/$P/obj/lib/crtend_android.o -L$A/out/target/product/$P/obj/lib -L$A/out/target/product/$P/obj/STATIC_LIBRARIES/libm_intermediates -L$A/out/target/product/$P/obj/STATIC_LIBRARIES/libc_intermediates" LDLIBS="m c gcc" else # Dynamic linking LDFLAGS="-Xlinker -z -Xlinker muldefs -nostdlib -Bdynamic -Xlinker -T$A/build/core/armelf.x -Xlinker -dynamic-linker -Xlinker /system/bin/linker -Xlinker -z -Xlinker nocopyreloc -Xlinker --no-undefined $A/out/target/product/$P/obj/lib/crtbegin_dynamic.o $A/out/target/product/$P/obj/lib/crtend_android.o -L$A/out/target/product/$P/obj/lib" LDLIBS="dl m c gcc" fi make EXTRA_LDFLAGS="$LDFLAGS" LDLIBS="$LDLIBS" "$@" ���������������������������������������������������������������������busybox-1.22.1/examples/undeb�����������������������������������������������������������������������0000755�0000000�0000000�00000002402�12263563520�014631� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # # This should work with the GNU version of tar and gzip! # This should work with the bash or ash shell! # Requires the programs (ar, tar, gzip, and the pager more or less). # usage() { echo "Usage: undeb -c package.deb <Print control file info>" echo " undeb -l package.deb <List contents of deb package>" echo " undeb -x package.deb /foo/boo <Extract deb package to this directory," echo " put . for current directory>" exit } deb=$2 exist() { if [ "$deb" = "" ]; then usage elif [ ! -s "$deb" ]; then echo "Can't find $deb!" exit fi } if [ "$1" = "" ]; then usage elif [ "$1" = "-l" ]; then exist type more >/dev/null 2>&1 && pager=more type less >/dev/null 2>&1 && pager=less [ "$pager" = "" ] && echo "No pager found!" && exit (ar -p $deb control.tar.gz | tar -xzO *control ; echo -e "\nPress enter to scroll, q to Quit!\n" ; ar -p $deb data.tar.gz | tar -tzv) | $pager exit elif [ "$1" = "-c" ]; then exist ar -p $deb control.tar.gz | tar -xzO *control exit elif [ "$1" = "-x" ]; then exist if [ "$3" = "" ]; then usage elif [ ! -d "$3" ]; then echo "No such directory $3!" exit fi ar -p $deb data.tar.gz | tar -xzvpf - -C $3 || exit echo echo "Extracted $deb to $3!" exit else usage fi ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/inetd.conf������������������������������������������������������������������0000644�0000000�0000000�00000005550�12263563520�015567� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# /etc/inetd.conf: see inetd(8) for further informations. # # Internet server configuration database # # # If you want to disable an entry so it isn't touched during # package updates just comment it out with a single '#' character. # # If you make changes to this file, either reboot your machine or # send the inetd process a HUP signal: # Do a "ps x" as root and look up the pid of inetd. Then do a # kill -HUP <pid of inetd> # inetd will re-read this file whenever it gets that signal. # <service_name> <sock_type> <proto> <flags> <user> <server_path> <args> # #:INTERNAL: Internal services # It is generally considered safer to keep these off. echo stream tcp nowait root internal echo dgram udp wait root internal #discard stream tcp nowait root internal #discard dgram udp wait root internal daytime stream tcp nowait root internal daytime dgram udp wait root internal #chargen stream tcp nowait root internal #chargen dgram udp wait root internal time stream tcp nowait root internal time dgram udp wait root internal # These are standard services. # #ftp stream tcp nowait root /usr/sbin/tcpd in.ftpd #telnet stream tcp nowait root /sbin/telnetd /sbin/telnetd #nntp stream tcp nowait root tcpd in.nntpd #smtp stream tcp nowait root tcpd sendmail -v # # Shell, login, exec and talk are BSD protocols. # # If you run an ntalk daemon (such as netkit-ntalk) on the old talk # port, that is, "talk" as opposed to "ntalk", it won't work and may # cause certain broken talk clients to malfunction. # # The talkd from netkit-ntalk 0.12 and higher, however, can speak the # old talk protocol and can be used safely. # #shell stream tcp nowait root /usr/sbin/tcpd in.rshd -L #login stream tcp nowait root /usr/sbin/tcpd in.rlogind -L #exec stream tcp nowait root /usr/sbin/tcpd in.rexecd #talk dgram udp wait root /usr/sbin/tcpd in.talkd #ntalk dgram udp wait root /usr/sbin/tcpd in.talkd # # Pop et al # Leave these off unless you're using them. #pop2 stream tcp nowait root /usr/sbin/tcpd in.pop2d #pop3 stream tcp nowait root /usr/sbin/tcpd in.pop3d # # The Internet UUCP service. # uucp stream tcp nowait uucp /usr/sbin/tcpd /usr/lib/uucp/uucico -l # # Tftp service is provided primarily for booting. Most sites # run this only on machines acting as "boot servers." If you don't # need it, don't use it. # #tftp dgram udp wait nobody /usr/sbin/tcpd in.tftpd #bootps dgram udp wait root /usr/sbin/in.bootpd in.bootpd # # Finger, systat and netstat give out user information which may be # valuable to potential "system crackers." Many sites choose to disable # some or all of these services to improve security. # #finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd -w #systat stream tcp nowait nobody /usr/sbin/tcpd /bin/ps -auwwx #netstat stream tcp nowait root /bin/netstat /bin/netstat -a #ident stream tcp nowait root /usr/sbin/in.identd in.identd ��������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/udhcp/����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�014710� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/udhcp/udhcpd.conf�����������������������������������������������������������0000644�0000000�0000000�00000010052�12263563520�017027� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Sample udhcpd configuration file (/etc/udhcpd.conf) # Values shown are defaults # The start and end of the IP lease block start 192.168.0.20 end 192.168.0.254 # The interface that udhcpd will use interface eth0 # The maximum number of leases (includes addresses reserved # by OFFER's, DECLINE's, and ARP conflicts). Will be corrected # if it's bigger than IP lease block, but it ok to make it # smaller than lease block. #max_leases 254 # The amount of time that an IP will be reserved (leased to nobody) # if a DHCP decline message is received (seconds) #decline_time 3600 # The amount of time that an IP will be reserved # if an ARP conflict occurs (seconds) #conflict_time 3600 # How long an offered address is reserved (seconds) #offer_time 60 # If client asks for lease below this value, it will be rounded up # to this value (seconds) #min_lease 60 # The location of the pid file #pidfile /var/run/udhcpd.pid # The location of the leases file #lease_file /var/lib/misc/udhcpd.leases # The time period at which udhcpd will write out leases file. # If this is 0, udhcpd will never automatically write leases file. # Specified in seconds. #auto_time 7200 # Every time udhcpd writes a leases file, the below script will be called #notify_file # default: no script #notify_file dumpleases # useful for debugging # The following are bootp specific options # next server to use in bootstrap #siaddr 192.168.0.22 # default: 0.0.0.0 (none) # tftp server name #sname zorak # default: none # tftp file to download (e.g. kernel image) #boot_file /var/nfs_root # default: none # Static leases map #static_lease 00:60:08:11:CE:4E 192.168.0.54 #static_lease 00:60:08:11:CE:3E 192.168.0.44 # The remainder of options are DHCP options and can be specified with the # keyword 'opt' or 'option'. If an option can take multiple items, such # as the dns option, they can be listed on the same line, or multiple # lines. # Examples: opt dns 192.168.10.2 192.168.10.10 option subnet 255.255.255.0 opt router 192.168.10.2 opt wins 192.168.10.10 option dns 129.219.13.81 # appended to above DNS servers for a total of 3 option domain local option lease 864000 # default: 10 days option msstaticroutes 10.0.0.0/8 10.127.0.1 # single static route option staticroutes 10.0.0.0/8 10.127.0.1, 10.11.12.0/24 10.11.12.1 # Arbitrary option in hex form: option 0x08 01020304 # option 8: "cookie server IP addr: 1.2.3.4" # Currently supported options (for more info, see options.c): #opt lease NUM #opt subnet IP #opt broadcast IP #opt router IP_LIST #opt ipttl NUM #opt mtu NUM #opt hostname STRING # client's hostname #opt domain STRING # client's domain suffix #opt search STRING_LIST # search domains #opt nisdomain STRING #opt timezone NUM # (localtime - UTC_time) in seconds. signed #opt tftp STRING # tftp server name #opt bootfile STRING # tftp file to download (e.g. kernel image) #opt bootsize NUM # size of that file #opt rootpath STRING # (NFS) path to mount as root fs #opt wpad STRING #opt serverid IP # default: server's IP #opt message STRING # error message (udhcpd sends it on success too) #opt vlanid NUM # 802.1P VLAN ID #opt vlanpriority NUM # 802.1Q VLAN priority # Options specifying server(s) #opt dns IP_LIST #opt wins IP_LIST #opt nissrv IP_LIST #opt ntpsrv IP_LIST #opt lprsrv IP_LIST #opt swapsrv IP # Options specifying routes #opt routes IP_PAIR_LIST #opt staticroutes STATIC_ROUTES # RFC 3442 classless static route option #opt msstaticroutes STATIC_ROUTES # same, using MS option number # Obsolete options, no longer supported #opt logsrv IP_LIST # 704/UDP log server (not syslog!) #opt namesrv IP_LIST # IEN 116 name server, obsolete (August 1979!!!) #opt cookiesrv IP_LIST # RFC 865 "quote of the day" server, rarely (never?) used #opt timesrv IP_LIST # RFC 868 time server, rarely (never?) used # TODO: in development #opt userclass STRING # RFC 3004. set of LASCII strings. "I am a printer" etc #opt sipserv STRING LIST # RFC 3361. flag byte, then: 0: domain names, 1: IP addrs ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/udhcp/simple.script���������������������������������������������������������0000755�0000000�0000000�00000002405�12263563520�017436� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # udhcpc script edited by Tim Riker <Tim@Rikers.org> RESOLV_CONF="/etc/resolv.conf" [ -n "$1" ] || { echo "Error: should be called from udhcpc"; exit 1; } NETMASK="" [ -n "$subnet" ] && NETMASK="netmask $subnet" BROADCAST="broadcast +" [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" case "$1" in deconfig) echo "Setting IP address 0.0.0.0 on $interface" ifconfig $interface 0.0.0.0 ;; renew|bound) echo "Setting IP address $ip on $interface" ifconfig $interface $ip $NETMASK $BROADCAST if [ -n "$router" ] ; then echo "Deleting routers" while route del default gw 0.0.0.0 dev $interface ; do : done metric=0 for i in $router ; do echo "Adding router $i" route add default gw $i dev $interface metric $metric : $(( metric += 1 )) done fi echo "Recreating $RESOLV_CONF" # If the file is a symlink somewhere (like /etc/resolv.conf # pointing to /run/resolv.conf), make sure things work. realconf=$(readlink -f "$RESOLV_CONF" 2>/dev/null || echo "$RESOLV_CONF") tmpfile="$realconf-$$" > "$tmpfile" [ -n "$domain" ] && echo "search $domain" >> "$tmpfile" for i in $dns ; do echo " Adding DNS server $i" echo "nameserver $i" >> "$tmpfile" done mv "$tmpfile" "$realconf" ;; esac exit 0 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/udhcp/sample.nak������������������������������������������������������������0000755�0000000�0000000�00000000104�12263563520�016665� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Sample udhcpc nak script echo Received a NAK: $message ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/udhcp/sample.deconfig�������������������������������������������������������0000755�0000000�0000000�00000000115�12263563520�017674� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Sample udhcpc deconfig script /sbin/ifconfig $interface 0.0.0.0 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/udhcp/sample.renew����������������������������������������������������������0000755�0000000�0000000�00000001154�12267106022�017234� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Sample udhcpc bound script RESOLV_CONF="/etc/udhcpc/resolv.conf" [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" [ -n "$subnet" ] && NETMASK="netmask $subnet" /sbin/ifconfig $interface $ip $BROADCAST $NETMASK if [ -n "$router" ] then echo "deleting routers" while /sbin/route del default gw 0.0.0.0 dev $interface do : done metric=0 for i in $router do /sbin/route add default gw $i dev $interface metric $((metric++)) done fi echo -n > $RESOLV_CONF [ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF for i in $dns do echo adding dns $i echo nameserver $i >> $RESOLV_CONF done ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/udhcp/sample.bound����������������������������������������������������������0000755�0000000�0000000�00000001154�12267106022�017223� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Sample udhcpc renew script RESOLV_CONF="/etc/udhcpc/resolv.conf" [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" [ -n "$subnet" ] && NETMASK="netmask $subnet" /sbin/ifconfig $interface $ip $BROADCAST $NETMASK if [ -n "$router" ] then echo "deleting routers" while /sbin/route del default gw 0.0.0.0 dev $interface do : done metric=0 for i in $router do /sbin/route add default gw $i dev $interface metric $((metric++)) done fi echo -n > $RESOLV_CONF [ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF for i in $dns do echo adding dns $i echo nameserver $i >> $RESOLV_CONF done ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/udhcp/sample.script���������������������������������������������������������0000755�0000000�0000000�00000000417�12263563520�017427� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Currently, we only dispatch according to command. However, a more # elaborate system might dispatch by command and interface or do some # common initialization first, especially if more dhcp event notifications # are added. exec /usr/share/udhcpc/sample.$1 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/depmod.pl�������������������������������������������������������������������0000755�0000000�0000000�00000023070�12263563520�015422� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # vi: set sw=4 ts=4: # Copyright (c) 2001 David Schleef <ds@schleef.org> # Copyright (c) 2001 Erik Andersen <andersen@codepoet.org> # Copyright (c) 2001 Stuart Hughes <seh@zee2.com> # Copyright (c) 2002 Steven J. Hill <shill@broadcom.com> # Copyright (c) 2006 Freescale Semiconductor, Inc <stuarth@freescale.com> # # History: # March 2006: Stuart Hughes <stuarth@freescale.com>. # Significant updates, including implementing the '-F' option # and adding support for 2.6 kernels. # This program is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. use Getopt::Long qw(:config no_auto_abbrev no_ignore_case); use File::Find; use strict; # Set up some default values my $kdir=""; my $basedir=""; my $kernel=""; my $kernelsyms=""; my $symprefix=""; my $all=0; my $quick=0; my $errsyms=0; my $stdout=0; my $verbose=0; my $help=0; my $nm = $ENV{'NM'} || "nm"; # more globals my (@liblist) = (); my $exp = {}; my $dep = {}; my $mod = {}; my $usage = <<TXT; $0 -b basedir { -k <vmlinux> | -F <System.map> } [options]... Where: -h --help : Show this help screen -b --basedir : Modules base directory (e.g /lib/modules/<2.x.y>) -k --kernel : Kernel binary for the target (e.g. vmlinux) -F --kernelsyms : Kernel symbol file (e.g. System.map) -n --stdout : Write to stdout instead of <basedir>/modules.dep -v --verbose : Print out lots of debugging stuff -P --symbol-prefix : Symbol prefix -a --all : Probe all modules (default/only thing supported) -e --errsyms : Report any symbols not supplied by modules/kernel TXT # get command-line options GetOptions( "help|h" => \$help, "basedir|b=s" => \$basedir, "kernel|k=s" => \$kernel, "kernelsyms|F=s" => \$kernelsyms, "stdout|n" => \$stdout, "verbose|v" => \$verbose, "symbol-prefix|P=s" => \$symprefix, "all|a" => \$all, # unsupported options "quick|A" => \$quick, # ignored options (for historical usage) "quiet|q", "root|r", "unresolved-error|u" ); die $usage if $help; die $usage unless $basedir && ( $kernel || $kernelsyms ); die "can't use both -k and -F\n\n$usage" if $kernel && $kernelsyms; die "sorry, -A/--quick is not supported" if $quick; die "--errsyms requires --kernelsyms" if $errsyms && !$kernelsyms; # Strip any trailing or multiple slashes from basedir $basedir =~ s-/+$--g; # The base directory should contain /lib/modules somewhere if($basedir !~ m-/lib/modules-) { warn "WARNING: base directory does not match ..../lib/modules\n"; } # if no kernel version is contained in the basedir, try to find one if($basedir !~ m-/lib/modules/\d\.\d-) { opendir(BD, $basedir) or die "can't open basedir $basedir : $!\n"; foreach ( readdir(BD) ) { next if /^\.\.?$/; next unless -d "$basedir/$_"; warn "dir = $_\n" if $verbose; if( /^\d\.\d/ ) { $kdir = $_; warn("Guessed module directory as $basedir/$kdir\n"); last; } } closedir(BD); die "Cannot find a kernel version under $basedir\n" unless $kdir; $basedir = "$basedir/$kdir"; } # Find the list of .o or .ko files living under $basedir warn "**** Locating all modules\n" if $verbose; find sub { my $file; if ( -f $_ && ! -d $_ ) { $file = $File::Find::name; if ( $file =~ /\.k?o$/ ) { push(@liblist, $file); warn "$file\n" if $verbose; } } }, $basedir; warn "**** Finished locating modules\n" if $verbose; foreach my $obj ( @liblist ){ # turn the input file name into a target tag name my ($tgtname) = $obj =~ m-(/lib/modules/.*)$-; warn "\nMODULE = $tgtname\n" if $verbose; # get a list of symbols my @output=`$nm $obj`; build_ref_tables($tgtname, \@output, $exp, $dep); } # vmlinux is a special name that is only used to resolve symbols my $tgtname = 'vmlinux'; my @output = $kernelsyms ? `cat $kernelsyms` : `$nm $kernel`; warn "\nMODULE = $tgtname\n" if $verbose; build_ref_tables($tgtname, \@output, $exp, $dep); # resolve the dependencies for each module # reduce dependencies: remove unresolvable and resolved from vmlinux/System.map # remove duplicates foreach my $module (keys %$dep) { warn "reducing module: $module\n" if $verbose; $mod->{$module} = {}; foreach (@{$dep->{$module}}) { if( $exp->{$_} ) { warn "resolved symbol $_ in file $exp->{$_}\n" if $verbose; next if $exp->{$_} =~ /vmlinux/; $mod->{$module}{$exp->{$_}} = 1; } else { warn "unresolved symbol $_ in file $module\n"; } } } # build a complete dependency list for each module and make sure it # is kept in order proper order my $mod2 = {}; sub maybe_unshift { my ($array, $ele) = @_; # chop off the leading path /lib/modules/<kver>/ as modprobe # will handle relative paths just fine $ele =~ s:^/lib/modules/[^/]*/::; foreach (@{$array}) { if ($_ eq $ele) { return; } } unshift (@{$array}, $ele); } sub add_mod_deps { my ($depth, $mod, $mod2, $module, $this_module) = @_; $depth .= " "; warn "${depth}loading deps of module: $this_module\n" if $verbose; if (length($depth) > 50) { die "too much recursion (circular dependencies in modules?)"; } foreach my $md (keys %{$mod->{$this_module}}) { add_mod_deps ($depth, $mod, $mod2, $module, $md); warn "${depth} outputting $md\n" if $verbose; maybe_unshift (\@{$$mod2->{$module}}, $md); } if (!%{$mod->{$this_module}}) { warn "${depth} no deps\n" if $verbose; } } foreach my $module (keys %$mod) { warn "filling out module: $module\n" if $verbose; @{$mod2->{$module}} = (); add_mod_deps ("", $mod, \$mod2, $module, $module); } # figure out where the output should go if ($stdout == 0) { warn "writing $basedir/modules.dep\n" if $verbose; open(STDOUT, ">$basedir/modules.dep") or die "cannot open $basedir/modules.dep: $!"; } my $kseries = $basedir =~ m,/2\.4\.[^/]*, ? '2.4' : 'others'; foreach my $module ( keys %$mod ) { if($kseries eq '2.4') { print "$module:\t"; my @sorted = sort bydep keys %{$mod->{$module}}; print join(" \\\n\t",@sorted); print "\n\n"; } else { my $shortmod = $module; $shortmod =~ s:^/lib/modules/[^/]*/::; print "$shortmod:"; my @sorted = @{$mod2->{$module}}; printf " " if @sorted; print join(" ",@sorted); print "\n"; } } sub build_ref_tables { my ($name, $sym_ar, $exp, $dep) = @_; my $ksymtab = grep m/ ${symprefix}__ksymtab/, @$sym_ar; # gather the exported symbols if($ksymtab){ # explicitly exported foreach ( @$sym_ar ) { / ${symprefix}__ksymtab_(.*)$/ and do { my $sym = ${symprefix} . $1; warn "sym = $sym\n" if $verbose; $exp->{$sym} = $name; }; } } else { # exporting all symbols foreach ( @$sym_ar ) { / [ABCDGRSTW] (.*)$/ and do { warn "syma = $1\n" if $verbose; $exp->{$1} = $name; }; } } # this takes makes sure modules with no dependencies get listed push @{$dep->{$name}}, $symprefix . 'printk' unless $name eq 'vmlinux'; # gather the unresolved symbols foreach ( @$sym_ar ) { !/ ${symprefix}__this_module/ && / U (.*)$/ and do { warn "und = $1\n" if $verbose; push @{$dep->{$name}}, $1; }; } } sub bydep { foreach my $f ( keys %{$mod->{$b}} ) { if($f eq $a) { return 1; } } return -1; } __END__ =head1 NAME depmod.pl - a cross platform script to generate kernel module dependency lists (modules.conf) which can then be used by modprobe on the target platform. It supports Linux 2.4 and 2.6 styles of modules.conf (auto-detected) =head1 SYNOPSIS depmod.pl [OPTION]... [basedir]... Example: depmod.pl -F linux/System.map -b target/lib/modules/2.6.11 =head1 DESCRIPTION The purpose of this script is to automagically generate a list of of kernel module dependencies. This script produces dependency lists that should be identical to the depmod program from the modutils package. Unlike the depmod binary, however, depmod.pl is designed to be run on your host system, not on your target system. This script was written by David Schleef <ds@schleef.org> to be used in conjunction with the BusyBox modprobe applet. =head1 OPTIONS =over 4 =item B<-h --help> This displays the help message. =item B<-b --basedir> The base directory uner which the target's modules will be found. This defaults to the /lib/modules directory. If you don't specify the kernel version, this script will search for one under the specified based directory and use the first thing that looks like a kernel version. =item B<-k --kernel> Kernel binary for the target (vmlinux). You must either supply a kernel binary or a kernel symbol file (using the -F option). =item B<-F --kernelsyms> Kernel symbol file for the target (System.map). =item B<-n --stdout> Write to stdout instead of modules.dep kernel binary for the target (using the -k option). =item B<--verbose> Verbose (debug) output =back =head1 COPYRIGHT AND LICENSE Copyright (c) 2001 David Schleef <ds@schleef.org> Copyright (c) 2001 Erik Andersen <andersen@codepoet.org> Copyright (c) 2001 Stuart Hughes <seh@zee2.com> Copyright (c) 2002 Steven J. Hill <shill@broadcom.com> Copyright (c) 2006 Freescale Semiconductor, Inc <stuarth@freescale.com> This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 AUTHOR David Schleef <ds@schleef.org> =cut ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/busybox.spec����������������������������������������������������������������0000644�0000000�0000000�00000007644�12263563520�016172� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Summary: Statically linked binary providing simplified versions of system commands Name: busybox Version: 1.15.1 Release: 1%{?dist} Epoch: 1 License: GPLv2 Group: System Environment/Shells Source: http://www.busybox.net/downloads/%{name}-%{version}.tar.bz2 Source1: busybox-static.config Source2: busybox-petitboot.config Source3: http://www.uclibc.org/downloads/uClibc-0.9.30.1.tar.bz2 Source4: uClibc.config Patch16: busybox-1.10.1-hwclock.patch # patch to avoid conflicts with getline() from stdio.h, already present in upstream VCS Patch22: uClibc-0.9.30.1-getline.patch Obsoletes: busybox-anaconda URL: http://www.busybox.net BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: libselinux-devel >= 1.27.7-2 BuildRequires: libsepol-devel BuildRequires: libselinux-static BuildRequires: libsepol-static BuildRequires: glibc-static %define debug_package %{nil} %package petitboot Group: System Environment/Shells Summary: Version of busybox configured for use with petitboot %description Busybox is a single binary which includes versions of a large number of system commands, including a shell. This package can be very useful for recovering from certain types of system failures, particularly those involving broken shared libraries. %description petitboot Busybox is a single binary which includes versions of a large number of system commands, including a shell. The version contained in this package is a minimal configuration intended for use with the Petitboot bootloader used on PlayStation 3. The busybox package provides a binary better suited to normal use. %prep %setup -q -a3 %patch16 -b .ia64 -p1 cat %{SOURCE4} >uClibc-0.9.30.1/.config1 %patch22 -b .getline -p1 %build # create static busybox - the executable is kept as busybox-static # We use uclibc instead of system glibc, uclibc is several times # smaller, this is important for static build. # Build uclibc first. cd uClibc-0.9.30.1 # fixme: mkdir kernel-include cp -a /usr/include/asm kernel-include cp -a /usr/include/asm-generic kernel-include cp -a /usr/include/linux kernel-include # uclibc can't be built on ppc64,s390,ia64, we set $arch to "" in this case arch=`uname -m | sed -e 's/i.86/i386/' -e 's/ppc/powerpc/' -e 's/ppc64//' -e 's/powerpc64//' -e 's/ia64//' -e 's/s390.*//'` echo "TARGET_$arch=y" >.config echo "TARGET_ARCH=\"$arch\"" >>.config cat .config1 >>.config if test "$arch"; then yes "" | make oldconfig; fi if test "$arch"; then cat .config; fi if test "$arch"; then make V=1; fi if test "$arch"; then make install; fi if test "$arch"; then make install_kernel_headers; fi cd .. # we are back in busybox-NN.MM dir now cp %{SOURCE1} .config # set all new options to defaults yes "" | make oldconfig # gcc needs to be convinced to use neither system headers, nor libs, # nor startfiles (i.e. crtXXX.o files) if test "$arch"; then \ mv .config .config1 && \ grep -v ^CONFIG_SELINUX .config1 >.config && \ yes "" | make oldconfig && \ cat .config && \ make V=1 \ EXTRA_CFLAGS="-isystem uClibc-0.9.30.1/installed/include" \ CFLAGS_busybox="-static -nostartfiles -LuClibc-0.9.30.1/installed/lib uClibc-0.9.30.1/installed/lib/crt1.o uClibc-0.9.30.1/installed/lib/crti.o uClibc-0.9.30.1/installed/lib/crtn.o"; \ else \ cat .config && \ make V=1 CC="gcc $RPM_OPT_FLAGS"; \ fi cp busybox busybox.static # create busybox optimized for petitboot make clean # copy new configuration file cp %{SOURCE2} .config # set all new options to defaults yes "" | make oldconfig make V=1 CC="%__cc $RPM_OPT_FLAGS" cp busybox busybox.petitboot %install rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT/sbin install -m 755 busybox.static $RPM_BUILD_ROOT/sbin/busybox install -m 755 busybox.petitboot $RPM_BUILD_ROOT/sbin/busybox.petitboot %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) %doc LICENSE docs/busybox.net/*.html /sbin/busybox %files petitboot %defattr(-,root,root,-) %doc LICENSE /sbin/busybox.petitboot %changelog ��������������������������������������������������������������������������������������������busybox-1.22.1/examples/mk2knr.pl�������������������������������������������������������������������0000755�0000000�0000000�00000004637�12263563520�015366� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # # @(#) mk2knr.pl - generates a perl script that converts lexemes to K&R-style # # How to use this script: # - In the busybox directory type 'examples/mk2knr.pl files-to-convert' # - Review the 'convertme.pl' script generated and remove / edit any of the # substitutions in there (please especially check for false positives) # - Type './convertme.pl same-files-as-before' # - Compile and see if it works # # BUGS: This script does not ignore strings inside comments or strings inside # quotes (it probably should). # set this to something else if you want $convertme = 'convertme.pl'; # internal-use variables (don't touch) $convert = 0; %converted = (); # if no files were specified, print usage die "usage: $0 file.c | file.h\n" if scalar(@ARGV) == 0; # prepare the "convert me" file open(CM, ">$convertme") or die "convertme.pl $!"; print CM "#!/usr/bin/perl -p -i\n\n"; # process each file passed on the cmd line while (<>) { # if the line says "getopt" in it anywhere, we don't want to muck with it # because option lists tend to include strings like "cxtzvOf:" which get # matched by the "check for mixed case" regexps below next if /getopt/; # tokenize the string into just the variables while (/([a-zA-Z_][a-zA-Z0-9_]*)/g) { $var = $1; # ignore the word "BusyBox" next if ($var =~ /BusyBox/); # this checks for javaStyle or szHungarianNotation $convert++ if ($var =~ /^[a-z]+[A-Z][a-z]+/); # this checks for PascalStyle $convert++ if ($var =~ /^[A-Z][a-z]+[A-Z][a-z]+/); # if we want to add more checks, we can add 'em here, but the above # checks catch "just enough" and not too much, so prolly not. if ($convert) { $convert = 0; # skip ahead if we've already dealt with this one next if ($converted{$var}); # record that we've dealt with this var $converted{$var} = 1; print CM "s/\\b$var\\b/"; # more to come in just a minute # change the first letter to lower-case $var = lcfirst($var); # put underscores before all remaining upper-case letters $var =~ s/([A-Z])/_$1/g; # now change the remaining characters to lower-case $var = lc($var); print CM "$var/g;\n"; } } } # tidy up and make the $convertme script executable close(CM); chmod 0755, $convertme; # print a helpful help message print "Done. Scheduled name changes are in $convertme.\n"; print "Please review/modify it and then type ./$convertme to do the search & replace.\n"; �������������������������������������������������������������������������������������������������busybox-1.22.1/examples/unrpm�����������������������������������������������������������������������0000755�0000000�0000000�00000002067�12263563520�014704� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # # This should work with the GNU version of cpio and gzip! # This should work with the bash or ash shell! # Requires the programs (cpio, gzip, and the pager more or less). # usage() { echo "Usage: unrpm -l package.rpm <List contents of rpm package>" echo " unrpm -x package.rpm /foo/boo <Extract rpm package to this directory," echo " put . for current directory>" exit } rpm=$2 exist() { if [ "$rpm" = "" ]; then usage elif [ ! -s "$rpm" ]; then echo "Can't find $rpm!" exit fi } if [ "$1" = "" ]; then usage elif [ "$1" = "-l" ]; then exist type more >/dev/null 2>&1 && pager=more type less >/dev/null 2>&1 && pager=less [ "$pager" = "" ] && echo "No pager found!" && exit (echo -e "\nPress enter to scroll, q to Quit!\n" ; rpm2cpio $rpm | cpio -tv --quiet) | $pager exit elif [ "$1" = "-x" ]; then exist if [ "$3" = "" ]; then usage elif [ ! -d "$3" ]; then echo "No such directory $3!" exit fi rpm2cpio $rpm | (umask 0 ; cd $3 ; cpio -idmuv) || exit echo echo "Extracted $rpm to $3!" exit else usage fi �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/devfsd.conf�����������������������������������������������������������������0000644�0000000�0000000�00000012366�12263563520�015742� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Sample /etc/devfsd.conf configuration file. # Richard Gooch <rgooch@atnf.csiro.au> 17-FEB-2002 # # adapted for busybox devfsd implementation by Tito <farmatito@tiscali.it> # # Enable full compatibility mode for old device names. You may comment these # out if you don't use the old device names. Make sure you know what you're # doing! REGISTER .* MKOLDCOMPAT UNREGISTER .* RMOLDCOMPAT # You may comment out the above and uncomment the following if you've # configured your system to use the original "new" devfs names or the really # new names #REGISTER ^vc/ MKOLDCOMPAT #UNREGISTER ^vc/ RMOLDCOMPAT #REGISTER ^pty/ MKOLDCOMPAT #UNREGISTER ^pty/ RMOLDCOMPAT #REGISTER ^misc/ MKOLDCOMPAT #UNREGISTER ^misc/ RMOLDCOMPAT # You may comment these out if you don't use the original "new" names REGISTER .* MKNEWCOMPAT UNREGISTER .* RMNEWCOMPAT # Enable module autoloading. You may comment this out if you don't use # autoloading # Supported by busybox when CONFIG_DEVFSD_MODLOAD is set. # This actually doesn't work with busybox modutils but needs # the real modutils' modprobe LOOKUP .* MODLOAD # Uncomment the following if you want to set the group to "tty" for the # pseudo-tty devices. This is necessary so that mesg(1) can later be used to # enable/disable talk requests and wall(1) messages. REGISTER ^pty/s.* PERMISSIONS -1.tty 0600 #REGISTER ^pts/.* PERMISSIONS -1.tty 0600 # Restoring /dev/log on startup would trigger the minilogd/initlog deadlock # (minilogd falsely assuming syslogd has been started). REGISTER ^log$ IGNORE CREATE ^log$ IGNORE CHANGE ^log$ IGNORE DELETE ^log$ IGNORE # # Uncomment this if you want permissions to be saved and restored # Do not do this for pseudo-terminal devices REGISTER ^pt[sy] IGNORE CREATE ^pt[sy] IGNORE CHANGE ^pt[sy] IGNORE DELETE ^pt[sy] IGNORE REGISTER .* COPY /lib/dev-state/$devname $devpath CREATE .* COPY $devpath /lib/dev-state/$devname CHANGE .* COPY $devpath /lib/dev-state/$devname #DELETE .* CFUNCTION GLOBAL unlink /lib/dev-state/$devname # Busybox DELETE .* EXECUTE /bin/rm -f /lib/dev-state/$devname RESTORE /lib/dev-state # # Uncomment this if you want the old /dev/cdrom symlink #REGISTER ^cdroms/cdrom0$ CFUNCTION GLOBAL mksymlink $devname cdrom #UNREGISTER ^cdroms/cdrom0$ CFUNCTION GLOBAL unlink cdrom # busybox REGISTER ^cdroms/cdrom0$ EXECUTE /bin/ln -sf $devname cdrom UNREGISTER ^cdroms/cdrom0$ EXECUTE /bin/rm -f cdrom #REGISTER ^v4l/video0$ CFUNCTION GLOBAL mksymlink v4l/video0 video #UNREGISTER ^v4l/video0$ CFUNCTION GLOBAL unlink video #REGISTER ^radio0$ CFUNCTION GLOBAL mksymlink radio0 radio #UNREGISTER ^radio0$ CFUNCTION GLOBAL unlink radio # Busybox REGISTER ^v4l/video0$ EXECUTE /bin/ln -sf v4l/video0 video UNREGISTER ^v4l/video0$ EXECUTE /bin/rm -f video REGISTER ^radio0$ EXECUTE /bin/ln -sf radio0 radio UNREGISTER ^radio0$ EXECUTE /bin/rm -f radio # ALSA stuff #LOOKUP snd MODLOAD ACTION snd # Uncomment this to let PAM manage devfs # Not supported by busybox #REGISTER .* CFUNCTION /lib/security/pam_console_apply_devfsd.so pam_console_apply_single $devpath # Uncomment this to manage USB mouse # Not supported by busybox #REGISTER ^input/mouse0$ CFUNCTION GLOBAL mksymlink $devname usbmouse #UNREGISTER ^input/mouse0$ CFUNCTION GLOBAL unlink usbmouse # Busybox #REGISTER ^input/mouse0$ EXECUTE /bin/ln -sf $devname usbmouse #UNREGISTER ^input/mouse0$ EXECUTE /bin/rm -f usbmouse # Not supported by busybox #REGISTER ^input/mice$ CFUNCTION GLOBAL mksymlink $devname usbmouse #UNREGISTER ^input/mice$ CFUNCTION GLOBAL unlink usbmouse # Busybox REGISTER ^input/mice$ EXECUTE /bin/ln -sf $devname usbmouse UNREGISTER ^input/mice$ EXECUTE /bin/rm -f usbmouse # If you have removable media and want to force media revalidation when looking # up new or old compatibility names, uncomment the following lines # SCSI NEWCOMPAT /dev/sd/* names LOOKUP ^(sd/c[0-9]+b[0-9]+t[0-9]+u[0-9]+)p[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1 # SCSI OLDCOMPAT /dev/sd?? names LOOKUP ^(sd[a-z]+)[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1 # IDE NEWCOMPAT /dev/ide/hd/* names LOOKUP ^(ide/hd/c[0-9]+b[0-9]+t[0-9]+u[0-9]+)p[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1 # IDE OLDCOMPAT /dev/hd?? names LOOKUP ^(hd[a-z])[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1 # IDE-SCSI NEWCOMPAT /dev/sd/* names #LOOKUP ^(sd/c[0-9]+b[0-9]+t[0-9]+u[0-9]+)p[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1 #SCSI OLDCOMPAT /dev/scd? names LOOKUP ^(scd+)[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1 REGISTER ^dvb/card[0-9]+/[^/]+$ PERMISSIONS root.video 0660 # Not supported by busybox #REGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ CFUNCTION GLOBAL mksymlink /dev/$devname ost/\2\1 #UNREGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ CFUNCTION GLOBAL unlink ost/\2\1 # Busybox REGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ EXECUTE /bin/ln -sf /dev/$devname ost/\2\1 UNREGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ EXECUTE /bin/rm -f ost/\2\1 # Include package-generated files from /etc/devfs/conf.d # Supported by busybox # INCLUDE /etc/devfs/conf.d/ INCLUDE /etc/devfs/busybox/ # Busybox: just for testing #INCLUDE /etc/devfs/nothing/ #INCLUDE /etc/devfs/nothing/nothing #OPTIONAL_INCLUDE /etc/devfs/nothing/ #OPTIONAL_INCLUDE /etc/devfs/nothing/nothing ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/examples/mdev.conf.change_blockdev.sh������������������������������������������������0000755�0000000�0000000�00000001157�12263563520�021127� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Seconds to try to reread partition table cnt=60 exec </dev/null exec >"/tmp/${0##*/}.$$.out" exec 2>&1 ( echo "Running: $0" echo "Env:" env | sort while sleep 1; test $cnt != 0; do echo "Trying to reread partition table on $DEVNAME ($cnt)" : $((cnt--)) # If device node doesn't exist, it means the device was removed. # Stop trying. test -e "$DEVNAME" || { echo "$DEVNAME doesn't exist, aborting"; exit 1; } #echo "$DEVNAME exists" if blockdev --rereadpt "$DEVNAME"; then echo "blockdev --rereadpt succeeded" exit 0 fi echo "blockdev --rereadpt failed, exit code: $?" done echo "Timed out" ) & �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/printutils/��������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�014204� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/printutils/lpr.c���������������������������������������������������������������������0000644�0000000�0000000�00000017534�12263563520�015162� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * bare bones version of lpr & lpq: BSD printing utilities * * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com> * * Original idea and code: * Walter Harms <WHarms@bfs.de> * * Licensed under GPLv2, see file LICENSE in this source tree. * * See RFC 1179 for protocol description. */ //usage:#define lpr_trivial_usage //usage: "-P queue[@host[:port]] -U USERNAME -J TITLE -Vmh [FILE]..." /* -C CLASS exists too, not shown. * CLASS is supposed to be printed on banner page, if one is requested */ //usage:#define lpr_full_usage "\n\n" //usage: " -P lp service to connect to (else uses $PRINTER)" //usage: "\n -m Send mail on completion" //usage: "\n -h Print banner page too" //usage: "\n -V Verbose" //usage: //usage:#define lpq_trivial_usage //usage: "[-P queue[@host[:port]]] [-U USERNAME] [-d JOBID]... [-fs]" //usage:#define lpq_full_usage "\n\n" //usage: " -P lp service to connect to (else uses $PRINTER)" //usage: "\n -d Delete jobs" //usage: "\n -f Force any waiting job to be printed" //usage: "\n -s Short display" #include "libbb.h" /* * LPD returns binary 0 on success. * Otherwise it returns error message. */ static void get_response_or_say_and_die(int fd, const char *errmsg) { ssize_t sz; char buf[128]; buf[0] = ' '; sz = safe_read(fd, buf, 1); if ('\0' != buf[0]) { // request has failed // try to make sure last char is '\n', but do not add // superfluous one sz = full_read(fd, buf + 1, 126); bb_error_msg("error while %s%s", errmsg, (sz > 0 ? ". Server said:" : "")); if (sz > 0) { // sz = (bytes in buf) - 1 if (buf[sz] != '\n') buf[++sz] = '\n'; safe_write(STDERR_FILENO, buf, sz + 1); } xfunc_die(); } } int lpqr_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; int lpqr_main(int argc UNUSED_PARAM, char *argv[]) { enum { OPT_P = 1 << 0, // -P queue[@host[:port]]. If no -P is given use $PRINTER, then "lp@localhost:515" OPT_U = 1 << 1, // -U username LPR_V = 1 << 2, // -V: be verbose LPR_h = 1 << 3, // -h: want banner printed LPR_C = 1 << 4, // -C class: job "class" (? supposedly printed on banner) LPR_J = 1 << 5, // -J title: the job title for the banner page LPR_m = 1 << 6, // -m: send mail back to user LPQ_SHORT_FMT = 1 << 2, // -s: short listing format LPQ_DELETE = 1 << 3, // -d: delete job(s) LPQ_FORCE = 1 << 4, // -f: force waiting job(s) to be printed }; char tempfile[sizeof("/tmp/lprXXXXXX")]; const char *job_title; const char *printer_class = ""; // printer class, max 32 char const char *queue; // name of printer queue const char *server = "localhost"; // server[:port] of printer queue char *hostname; // N.B. IMHO getenv("USER") can be way easily spoofed! const char *user = xuid2uname(getuid()); unsigned job; unsigned opts; int fd; queue = getenv("PRINTER"); if (!queue) queue = "lp"; // parse options // TODO: set opt_complementary: s,d,f are mutually exclusive opts = getopt32(argv, (/*lp*/'r' == applet_name[2]) ? "P:U:VhC:J:m" : "P:U:sdf" , &queue, &user , &printer_class, &job_title ); argv += optind; { // queue name is to the left of '@' char *s = strchr(queue, '@'); if (s) { // server name is to the right of '@' *s = '\0'; server = s + 1; } } // do connect fd = create_and_connect_stream_or_die(server, 515); // // LPQ ------------------------ // if (/*lp*/'q' == applet_name[2]) { char cmd; // force printing of every job still in queue if (opts & LPQ_FORCE) { cmd = 1; goto command; // delete job(s) } else if (opts & LPQ_DELETE) { fdprintf(fd, "\x5" "%s %s", queue, user); while (*argv) { fdprintf(fd, " %s", *argv++); } bb_putchar('\n'); // dump current jobs status // N.B. periodical polling should be achieved // via "watch -n delay lpq" // They say it's the UNIX-way :) } else { cmd = (opts & LPQ_SHORT_FMT) ? 3 : 4; command: fdprintf(fd, "%c" "%s\n", cmd, queue); bb_copyfd_eof(fd, STDOUT_FILENO); } return EXIT_SUCCESS; } // // LPR ------------------------ // if (opts & LPR_V) bb_error_msg("connected to server"); job = getpid() % 1000; hostname = safe_gethostname(); // no files given on command line? -> use stdin if (!*argv) *--argv = (char *)"-"; fdprintf(fd, "\x2" "%s\n", queue); get_response_or_say_and_die(fd, "setting queue"); // process files do { unsigned cflen; int dfd; struct stat st; char *c; char *remote_filename; char *controlfile; // if data file is stdin, we need to dump it first if (LONE_DASH(*argv)) { strcpy(tempfile, "/tmp/lprXXXXXX"); dfd = xmkstemp(tempfile); bb_copyfd_eof(STDIN_FILENO, dfd); xlseek(dfd, 0, SEEK_SET); *argv = (char*)bb_msg_standard_input; } else { dfd = xopen(*argv, O_RDONLY); } st.st_size = 0; /* paranoia: fstat may theoretically fail */ fstat(dfd, &st); /* Apparently, some servers are buggy and won't accept 0-sized jobs. * Standard lpr works around it by refusing to send such jobs: */ if (st.st_size == 0) { bb_error_msg("nothing to print"); continue; } /* "The name ... should start with ASCII "cfA", * followed by a three digit job number, followed * by the host name which has constructed the file." * We supply 'c' or 'd' as needed for control/data file. */ remote_filename = xasprintf("fA%03u%s", job, hostname); // create control file // TODO: all lines but 2 last are constants! How we can use this fact? controlfile = xasprintf( "H" "%.32s\n" "P" "%.32s\n" /* H HOST, P USER */ "C" "%.32s\n" /* C CLASS - printed on banner page (if L cmd is also given) */ "J" "%.99s\n" /* J JOBNAME */ /* "class name for banner page and job name * for banner page commands must precede L command" */ "L" "%.32s\n" /* L USER - print banner page, with given user's name */ "M" "%.32s\n" /* M WHOM_TO_MAIL */ "l" "d%.31s\n" /* l DATA_FILE_NAME ("dfAxxx") */ , hostname, user , printer_class /* can be "" */ , ((opts & LPR_J) ? job_title : *argv) , (opts & LPR_h) ? user : "" , (opts & LPR_m) ? user : "" , remote_filename ); // delete possible "\nX\n" (that is, one-char) patterns c = controlfile; while ((c = strchr(c, '\n')) != NULL) { if (c[1] && c[2] == '\n') { overlapping_strcpy(c, c+2); } else { c++; } } // send control file if (opts & LPR_V) bb_error_msg("sending control file"); /* "Acknowledgement processing must occur as usual * after the command is sent." */ cflen = (unsigned)strlen(controlfile); fdprintf(fd, "\x2" "%u c%s\n", cflen, remote_filename); get_response_or_say_and_die(fd, "sending control file"); /* "Once all of the contents have * been delivered, an octet of zero bits is sent as * an indication that the file being sent is complete. * A second level of acknowledgement processing * must occur at this point." */ full_write(fd, controlfile, cflen + 1); /* writes NUL byte too */ get_response_or_say_and_die(fd, "sending control file"); // send data file, with name "dfaXXX" if (opts & LPR_V) bb_error_msg("sending data file"); fdprintf(fd, "\x3" "%"OFF_FMT"u d%s\n", st.st_size, remote_filename); get_response_or_say_and_die(fd, "sending data file"); if (bb_copyfd_size(dfd, fd, st.st_size) != st.st_size) { // We're screwed. We sent less bytes than we advertised. bb_error_msg_and_die("local file changed size?!"); } write(fd, "", 1); // send ACK get_response_or_say_and_die(fd, "sending data file"); // delete temporary file if we dumped stdin if (*argv == (char*)bb_msg_standard_input) unlink(tempfile); // cleanup close(fd); free(remote_filename); free(controlfile); // say job accepted if (opts & LPR_V) bb_error_msg("job accepted"); // next, please! job = (job + 1) % 1000; } while (*++argv); return EXIT_SUCCESS; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/printutils/Config.src����������������������������������������������������������������0000644�0000000�0000000�00000000672�12263563520�016132� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Print Utilities" INSERT config LPD bool "lpd" default y help lpd is a print spooling daemon. config LPR bool "lpr" default y help lpr sends files (or standard input) to a print spooling daemon. config LPQ bool "lpq" default y help lpq is a print spool queue examination and manipulation program. endmenu ����������������������������������������������������������������������busybox-1.22.1/printutils/Kbuild.src����������������������������������������������������������������0000644�0000000�0000000�00000000263�12263563520�016133� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Makefile for busybox # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y := lib-$(CONFIG_LPD) += lpd.o lib-$(CONFIG_LPR) += lpr.o lib-$(CONFIG_LPQ) += lpr.o ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/printutils/lpd.c���������������������������������������������������������������������0000644�0000000�0000000�00000021211�12263563520�015127� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * micro lpd * * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* * A typical usage of BB lpd looks as follows: * # tcpsvd -E 0 515 lpd [SPOOLDIR] [HELPER-PROG [ARGS...]] * * This starts TCP listener on port 515 (default for LP protocol). * When a client connection is made (via lpr) lpd first changes its * working directory to SPOOLDIR (current dir is the default). * * SPOOLDIR is the spool directory which contains printing queues * and should have the following structure: * * SPOOLDIR/ * <queue1> * ... * <queueN> * * <queueX> can be of two types: * A. a printer character device, an ordinary file or a link to such; * B. a directory. * * In case A lpd just dumps the data it receives from client (lpr) to the * end of queue file/device. This is non-spooling mode. * * In case B lpd enters spooling mode. It reliably saves client data along * with control info in two unique files under the queue directory. These * files are named dfAXXXHHHH and cfAXXXHHHH, where XXX is the job number * and HHHH is the client hostname. Unless a printing helper application * is specified lpd is done at this point. * * NB: file names are produced by peer! They actually may be anything at all. * lpd only sanitizes them (by removing most non-alphanumerics). * * If HELPER-PROG (with optional arguments) is specified then lpd continues * to process client data: * 1. it reads and parses control file (cfA...). The parse process * results in setting environment variables whose values were passed * in control file; when parsing is complete, lpd deletes control file. * 2. it spawns specified helper application. It is then * the helper application who is responsible for both actual printing * and deleting of processed data file. * * A good lpr passes control files which when parsed provides the following * variables: * $H = host which issues the job * $P = user who prints * $C = class of printing (what is printed on banner page) * $J = the name of the job * $L = print banner page * $M = the user to whom a mail should be sent if a problem occurs * * We specifically filter out and NOT provide: * $l = name of datafile ("dfAxxx") - file whose content are to be printed * * lpd provides $DATAFILE instead - the ACTUAL name * of the datafile under which it was saved. * $l would be not reliable (you would be at mercy of remote peer). * * Thus, a typical helper can be something like this: * #!/bin/sh * cat ./"$DATAFILE" >/dev/lp0 * mv -f ./"$DATAFILE" save/ */ //usage:#define lpd_trivial_usage //usage: "SPOOLDIR [HELPER [ARGS]]" //usage:#define lpd_full_usage "\n\n" //usage: "SPOOLDIR must contain (symlinks to) device nodes or directories" //usage: "\nwith names matching print queue names. In the first case, jobs are" //usage: "\nsent directly to the device. Otherwise each job is stored in queue" //usage: "\ndirectory and HELPER program is called. Name of file to print" //usage: "\nis passed in $DATAFILE variable." //usage: "\nExample:" //usage: "\n tcpsvd -E 0 515 softlimit -m 999999 lpd /var/spool ./print" #include "libbb.h" // strip argument of bad chars static char *sane(char *str) { char *s = str; char *p = s; while (*s) { if (isalnum(*s) || '-' == *s || '_' == *s) { *p++ = *s; } s++; } *p = '\0'; return str; } static char *xmalloc_read_stdin(void) { // SECURITY: size_t max = 4 * 1024; // more than enough for commands! return xmalloc_reads(STDIN_FILENO, &max); } int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; int lpd_main(int argc UNUSED_PARAM, char *argv[]) { int spooling = spooling; // for compiler char *s, *queue; char *filenames[2]; // goto spool directory if (*++argv) xchdir(*argv++); // error messages of xfuncs will be sent over network xdup2(STDOUT_FILENO, STDERR_FILENO); // nullify ctrl/data filenames memset(filenames, 0, sizeof(filenames)); // read command s = queue = xmalloc_read_stdin(); // we understand only "receive job" command if (2 != *queue) { unsupported_cmd: printf("Command %02x %s\n", (unsigned char)s[0], "is not supported"); goto err_exit; } // parse command: "2 | QUEUE_NAME | '\n'" queue++; // protect against "/../" attacks // *strchrnul(queue, '\n') = '\0'; - redundant, sane() will do if (!*sane(queue)) return EXIT_FAILURE; // queue is a directory -> chdir to it and enter spooling mode spooling = chdir(queue) + 1; // 0: cannot chdir, 1: done // we don't free(s), we might need "queue" var later while (1) { char *fname; int fd; // int is easier than ssize_t: can use xatoi_positive, // and can correctly display error returns (-1) int expected_len, real_len; // signal OK safe_write(STDOUT_FILENO, "", 1); // get subcommand // valid s must be of form: "SUBCMD | LEN | space | FNAME" // N.B. we bail out on any error s = xmalloc_read_stdin(); if (!s) { // (probably) EOF char *p, *q, var[2]; // non-spooling mode or no spool helper specified if (!spooling || !*argv) return EXIT_SUCCESS; // the only non-error exit // spooling mode but we didn't see both ctrlfile & datafile if (spooling != 7) goto err_exit; // reject job // spooling mode and spool helper specified -> exec spool helper // (we exit 127 if helper cannot be executed) var[1] = '\0'; // read and delete ctrlfile q = xmalloc_xopen_read_close(filenames[0], NULL); unlink(filenames[0]); // provide datafile name // we can use leaky setenv since we are about to exec or exit xsetenv("DATAFILE", filenames[1]); // parse control file by "\n" while ((p = strchr(q, '\n')) != NULL && isalpha(*q)) { *p++ = '\0'; // q is a line of <SYM><VALUE>, // we are setting environment string <SYM>=<VALUE>. // Ignoring "l<datafile>", exporting others: if (*q != 'l') { var[0] = *q++; xsetenv(var, q); } q = p; // next line } // helper should not talk over network. // this call reopens stdio fds to "/dev/null" // (no daemonization is done) bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO | DAEMON_ONLY_SANITIZE, NULL); BB_EXECVP_or_die(argv); } // validate input. // we understand only "control file" or "data file" cmds if (2 != s[0] && 3 != s[0]) goto unsupported_cmd; if (spooling & (1 << (s[0]-1))) { printf("Duplicated subcommand\n"); goto err_exit; } // get filename *strchrnul(s, '\n') = '\0'; fname = strchr(s, ' '); if (!fname) { // bad_fname: printf("No or bad filename\n"); goto err_exit; } *fname++ = '\0'; // // s[0]==2: ctrlfile, must start with 'c' // // s[0]==3: datafile, must start with 'd' // if (fname[0] != s[0] + ('c'-2)) // goto bad_fname; // get length expected_len = bb_strtou(s + 1, NULL, 10); if (errno || expected_len < 0) { printf("Bad length\n"); goto err_exit; } if (2 == s[0] && expected_len > 16 * 1024) { // SECURITY: // ctrlfile can't be big (we want to read it back later!) printf("File is too big\n"); goto err_exit; } // open the file if (spooling) { // spooling mode: dump both files // job in flight has mode 0200 "only writable" sane(fname); fd = open3_or_warn(fname, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0200); if (fd < 0) goto err_exit; filenames[s[0] - 2] = xstrdup(fname); } else { // non-spooling mode: // 2: control file (ignoring), 3: data file fd = -1; if (3 == s[0]) fd = xopen(queue, O_RDWR | O_APPEND); } // signal OK safe_write(STDOUT_FILENO, "", 1); // copy the file real_len = bb_copyfd_size(STDIN_FILENO, fd, expected_len); if (real_len != expected_len) { printf("Expected %d but got %d bytes\n", expected_len, real_len); goto err_exit; } // get EOF indicator, see whether it is NUL (ok) // (and don't trash s[0]!) if (safe_read(STDIN_FILENO, &s[1], 1) != 1 || s[1] != 0) { // don't send error msg to peer - it obviously // doesn't follow the protocol, so probably // it can't understand us either goto err_exit; } if (spooling) { // chmod completely downloaded file as "readable+writable" fchmod(fd, 0600); // accumulate dump state // N.B. after all files are dumped spooling should be 1+2+4==7 spooling |= (1 << (s[0]-1)); // bit 1: ctrlfile; bit 2: datafile } free(s); close(fd); // NB: can do close(-1). Who cares? // NB: don't do "signal OK" write here, it will be done // at the top of the loop } // while (1) err_exit: // don't keep corrupted files if (spooling) { #define i spooling for (i = 2; --i >= 0; ) if (filenames[i]) unlink(filenames[i]); } return EXIT_FAILURE; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/TODO���������������������������������������������������������������������������������0000644�0000000�0000000�00000026500�12263563520�012465� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Busybox TODO Harvest patches from http://git.openembedded.org/cgit.cgi/openembedded/tree/recipes/busybox/ https://dev.openwrt.org/browser/trunk/package/busybox/patches/ Stuff that needs to be done. This is organized by who plans to get around to doing it eventually, but that doesn't mean they "own" the item. If you want to do one of these bounce an email off the person it's listed under to see if they have any suggestions how they plan to go about it, and to minimize conflicts between your work and theirs. But otherwise, all of these are fair game. Rob Landley suggested this: Implement bb_realpath() that can handle NULL on non-glibc. sh The command shell situation is a mess. We have two different shells that don't really share any code, and the "standalone shell" doesn't work all that well (especially not in a chroot environment), due to apps not being reentrant. Do a SUSv3 audit Look at the full Single Unix Specification version 3 (available online at "http://www.opengroup.org/onlinepubs/009695399/nfindex.html") and figure out which of our apps are compliant, and what we're missing that we might actually care about. Even better would be some kind of automated compliance test harness that exercises each command line option and the various corner cases. Internationalization How much internationalization should we do? The low hanging fruit is UTF-8 character set support. We should do this. See TODO_unicode file. We also have lots of hardwired english text messages. Consolidating this into some kind of message table not only makes translation easier, but also allows us to consolidate redundant (or close) strings. We probably don't want to be bloated with locale support. (Not unless we can cleanly export it from our underlying C library without having to concern ourselves with it directly. Perhaps a few specific things like a config option for "date" are low hanging fruit here?) What level should things happen at? How much do we care about internationalizing the text console when X11 and xterms are so much better at it? (There's some infrastructure here we don't implement: The "unicode_start" and "unicode_stop" shell scripts need "vt-is-UTF8" and a --unicode option to loadkeys. That implies a real loadkeys/dumpkeys implementation to replace loadkmap/dumpkmap. Plus messing with console font loading. Is it worth it, or do we just say "use X"?) Individual compilation of applets. It would be nice if busybox had the option to compile to individual applets, for people who want an alternate implementation less bloated than the gnu utils (or simply with less political baggage), but without it being one big executable. Turning libbb into a real dll is another possibility, especially if libbb could export some of the other library interfaces we've already more or less got the code for (like zlib). buildroot - Make a "dogfood" option Busybox 1.1 will be capable of replacing most gnu packages for real world use, such as developing software or in a live CD. It needs wider testing. Busybox should now be able to replace bzip2, coreutils, e2fsprogs, file, findutils, gawk, grep, inetutils, less, modutils, net-tools, patch, procps, sed, shadow, sysklogd, sysvinit, tar, util-linux, and vim. The resulting system should be self-hosting (I.E. able to rebuild itself from source code). This means it would need (at least) binutils, gcc, and make, or equivalents. It would be a good "eating our own dogfood" test if buildroot had the option of using a "make allyesconfig" busybox instead of the all of the above packages. Anything that's wrong with the resulting system, we can fix. (It would be nice to be able to upgrade busybox to be able to replace bash and diffutils as well, but we're not there yet.) One example of an existing system that does this already is Firmware Linux: http://www.landley.net/code/firmware initramfs Busybox should have a sample initramfs build script. This depends on shell, mdev, and switch_root. mkdep Write a mkdep that doesn't segfault if there's a directory it doesn't have permission to read, isn't based on manually editing the output of lexx and yacc, doesn't make such a mess under include/config, etc. Group globals into unions of structures. Go through and turn all the global and static variables into structures, and have all those structures be in a big union shared between processes, so busybox uses less bss. (This is a big win on nommu machines.) See sed.c and mdev.c for examples. Go through bugs.busybox.net and close out all of that somehow. This one's open to everybody, but I'll wind up doing it... Bernhard Reutner-Fischer <busybox@busybox.net> suggests to look at these: New debug options: -Wlarger-than-127 Cleanup any big users Collate BUFSIZ IOBUF_SIZE MY_BUF_SIZE PIPE_PROGRESS_SIZE BUFSIZE PIPESIZE make bb_common_bufsiz1 configurable, size wise. make pipesize configurable, size wise. Use bb_common_bufsiz1 throughout applets! As yet unclaimed: ---- diff Make sure we handle empty files properly: From the patch man page: you can remove a file by sending out a context diff that compares the file to be deleted with an empty file dated the Epoch. The file will be removed unless patch is conforming to POSIX and the -E or --remove-empty-files option is not given. --- patch Should have simple fuzz factor support to apply patches at an offset which shouldn't take up too much space. And while we're at it, a new patch filename quoting format is apparently coming soon: http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2 Architectural issues: bb_close() with fsync() We should have a bb_close() in place of normal close, with a CONFIG_ option to not just check the return value of close() for an error, but fsync(). Close can't reliably report anything useful because if write() accepted the data then it either went out to the network or it's in cache or a pipe buffer. Either way, there's no guarantee it'll make it to its final destination before close() gets called, so there's no guarantee that any error will be reported. You need to call fsync() if you care about errors that occur after write(), but that can have a big performance impact. So make it a config option. --- Unify archivers Lots of archivers have the same general infrastructure. The directory traversal code should be factored out, and the guts of each archiver could be some setup code and a series of callbacks for "add this file", "add this directory", "add this symlink" and so on. This could clean up tar and zip, and make it cheaper to add cpio and ar write support, and possibly even cheaply add things like mkisofs or mksquashfs someday, if they become relevant. --- Text buffer support. Several existing applets (sort, vi, less...) read a whole file into memory and act on it. Use open_read_close(). --- Memory Allocation We have a CONFIG_BUFFER mechanism that lets us select whether to do memory allocation on the stack or the heap. Unfortunately, we're not using it much. We need to audit our memory allocations and turn a lot of malloc/free calls into RESERVE_CONFIG_BUFFER/RELEASE_CONFIG_BUFFER. For a start, see e.g. make EXTRA_CFLAGS=-Wlarger-than-64 And while we're at it, many of the CONFIG_FEATURE_CLEAN_UP #ifdefs will be optimized out by the compiler in the stack allocation case (since there's no free for an alloca()), and this means that various cleanup loops that just call free might also be optimized out by the compiler if written right, so we can yank those #ifdefs too, and generally clean up the code. --- FEATURE_CLEAN_UP This is more an unresolved issue than a to-do item. More thought is needed. Normally we rely on exit() to free memory, close files and unmap segments for us. This makes most calls to free(), close(), and unmap() optional in busybox applets that don't intend to run for very long, and optional stuff can be omitted to save size. The idea was raised that we could simulate fork/exit with setjmp/longjmp for _really_ brainless embedded systems, or speed up the standalone shell by not forking. Doing so would require a reliable FEATURE_CLEAN_UP. Unfortunately, this isn't as easy as it sounds. The problem is, lots of things exit(), sometimes unexpectedly (xmalloc()) and sometimes reliably (bb_perror_msg_and_die() or show_usage()). This jumps out of the normal flow control and bypasses any cleanup code we put at the end of our applets. It's possible to add hooks to libbb functions like xmalloc() and xopen() to add their entries to a linked list, which could be traversed and freed/closed automatically. (This would need to be able to free just the entries after a checkpoint to be usable for a forkless standalone shell. You don't want to free the shell's own resources.) Right now, FEATURE_CLEAN_UP is more or less a debugging aid, to make things like valgrind happy. It's also documentation of _what_ we're trusting exit() to clean up for us. But new infrastructure to auto-free stuff would render the existing FEATURE_CLEAN_UP code redundant. For right now, exit() handles it just fine. Minor stuff: watchdog.c could autodetect the timer duration via: if(!ioctl (fd, WDIOC_GETTIMEOUT, &tmo)) timer_duration = 1 + (tmo / 2); Unfortunately, that needs linux/watchdog.h and that contains unfiltered kernel types on some distros, which breaks the build. --- use bb_error_msg where appropriate: See egrep "(printf.*\([[:space:]]*(stderr|2)|[^_]write.*\([[:space:]]*(stderr|2))" --- use bb_perror_msg where appropriate: See egrep "[^_]perror" --- possible code duplication ingroup() and is_a_group_member() --- Move __get_hz() to a better place and (re)use it in route.c, ash.c --- See grep -r strtod Alot of duplication that wants cleanup. --- unify progress_meter. wget, flash_eraseall, pipe_progress, fbsplash, setfiles. --- support start-stop-daemon -d <chdir-path> --- vdprintf() -> similar sized functionality --- (TODO list after discussion 11.05.2009) * shrink tc/brctl/ip tc/brctl seem like fairly large things to try and tackle in your timeframe, and i think people have posted attempts in the past. Adding additional options to ip though seems reasonable. * add tests for some applets * implement POSIX utilities and audit them for POSIX conformance. then audit them for GNU conformance. then document all your findings in a new doc/conformance.txt file while perhaps implementing some of the missing features. you can find the latest POSIX documentation (1003.1-2008) here: http://www.opengroup.org/onlinepubs/9699919799/ and the complete list of all utilities that POSIX covers: http://www.opengroup.org/onlinepubs/9699919799/idx/utilities.html The first step would to generate a file/matrix what is already archived (also IPV6) * implement 'at' * rpcbind (former portmap) or equivalent so that we don't have to use -o nolock on nfs mounts * check IPV6 compliance * generate a mini example using kernel+busybox only (+libc) for example * more support for advanced linux 2.6.x features, see: iotop most likely there is more * even more support for statistics: mpstat, iostat, powertop.... ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/.indent.pro��������������������������������������������������������������������������0000644�0000000�0000000�00000001547�12263563520�014062� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������--blank-lines-after-declarations --blank-lines-after-procedures --break-before-boolean-operator --no-blank-lines-after-commas --braces-on-if-line --braces-on-struct-decl-line --comment-indentation25 --declaration-comment-column25 --no-comment-delimiters-on-blank-lines --cuddle-else --continuation-indentation4 --case-indentation0 --else-endif-column33 --space-after-cast --line-comments-indentation0 --declaration-indentation1 --dont-format-first-column-comments --dont-format-comments --honour-newlines --indent-level4 /* changed from 0 to 4 */ --parameter-indentation4 --line-length78 /* changed from 75 */ --continue-at-parentheses --no-space-after-function-call-names --dont-break-procedure-type --dont-star-comments --leave-optional-blank-lines --dont-space-special-semicolon --tab-size4 /* additions by Mark */ --case-brace-indentation0 --leave-preprocessor-space ���������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/Makefile�����������������������������������������������������������������������������0000644�0000000�0000000�00000124236�12267106140�013435� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������VERSION = 1 PATCHLEVEL = 22 SUBLEVEL = 1 EXTRAVERSION = NAME = Unnamed # *DOCUMENTATION* # To see a list of typical targets execute "make help" # More info can be located in ./README # Comments in this file are targeted only to the developer, do not # expect to learn how to build the kernel reading this file. # Do not print "Entering directory ..." MAKEFLAGS += --no-print-directory # We are using a recursive build, so we need to do a little thinking # to get the ordering right. # # Most importantly: sub-Makefiles should only ever modify files in # their own directory. If in some directory we have a dependency on # a file in another dir (which doesn't happen often, but it's often # unavoidable when linking the built-in.o targets which finally # turn into busybox), we will call a sub make in that other dir, and # after that we are sure that everything which is in that other dir # is now up to date. # # The only cases where we need to modify files which have global # effects are thus separated out and done before the recursive # descending is started. They are now explicitly listed as the # prepare rule. # To put more focus on warnings, be less verbose as default # Use 'make V=1' to see the full commands ifdef V ifeq ("$(origin V)", "command line") KBUILD_VERBOSE = $(V) endif endif ifndef KBUILD_VERBOSE KBUILD_VERBOSE = 0 endif # Call sparse as part of compilation of C files # Use 'make C=1' to enable sparse checking ifdef C ifeq ("$(origin C)", "command line") KBUILD_CHECKSRC = $(C) endif endif ifndef KBUILD_CHECKSRC KBUILD_CHECKSRC = 0 endif # Use make M=dir to specify directory of external module to build # Old syntax make ... SUBDIRS=$PWD is still supported # Setting the environment variable KBUILD_EXTMOD take precedence ifdef SUBDIRS KBUILD_EXTMOD ?= $(SUBDIRS) endif ifdef M ifeq ("$(origin M)", "command line") KBUILD_EXTMOD := $(M) endif endif # kbuild supports saving output files in a separate directory. # To locate output files in a separate directory two syntaxes are supported. # In both cases the working directory must be the root of the kernel src. # 1) O= # Use "make O=dir/to/store/output/files/" # # 2) Set KBUILD_OUTPUT # Set the environment variable KBUILD_OUTPUT to point to the directory # where the output files shall be placed. # export KBUILD_OUTPUT=dir/to/store/output/files/ # make # # The O= assignment takes precedence over the KBUILD_OUTPUT environment # variable. # KBUILD_SRC is set on invocation of make in OBJ directory # KBUILD_SRC is not intended to be used by the regular user (for now) ifeq ($(KBUILD_SRC),) # OK, Make called in directory where kernel src resides # Do we want to locate output files in a separate directory? ifdef O ifeq ("$(origin O)", "command line") KBUILD_OUTPUT := $(O) endif endif # That's our default target when none is given on the command line PHONY := _all _all: ifneq ($(KBUILD_OUTPUT),) # Invoke a second make in the output directory, passing relevant variables # check that the output directory actually exists saved-output := $(KBUILD_OUTPUT) KBUILD_OUTPUT := $(shell cd $(KBUILD_OUTPUT) && /bin/pwd) $(if $(KBUILD_OUTPUT),, \ $(error output directory "$(saved-output)" does not exist)) PHONY += $(MAKECMDGOALS) $(filter-out _all,$(MAKECMDGOALS)) _all: $(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) \ KBUILD_SRC=$(CURDIR) \ KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile $@ # Leave processing to above invocation of make skip-makefile := 1 endif # ifneq ($(KBUILD_OUTPUT),) endif # ifeq ($(KBUILD_SRC),) # We process the rest of the Makefile if this is the final invocation of make ifeq ($(skip-makefile),) # If building an external module we do not care about the all: rule # but instead _all depend on modules PHONY += all ifeq ($(KBUILD_EXTMOD),) _all: all else _all: modules endif srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR)) TOPDIR := $(srctree) # FIXME - TOPDIR is obsolete, use srctree/objtree objtree := $(CURDIR) src := $(srctree) obj := $(objtree) VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD)) export srctree objtree VPATH TOPDIR # Cross compiling and selecting different set of gcc/bin-utils # --------------------------------------------------------------------------- # # When performing cross compilation for other architectures ARCH shall be set # to the target architecture. (See arch/* for the possibilities). # ARCH can be set during invocation of make: # make ARCH=ia64 # Another way is to have ARCH set in the environment. # The default ARCH is the host where make is executed. # CROSS_COMPILE specify the prefix used for all executables used # during compilation. Only gcc and related bin-utils executables # are prefixed with $(CROSS_COMPILE). # CROSS_COMPILE can be set on the command line # make CROSS_COMPILE=ia64-linux- # Alternatively CROSS_COMPILE can be set in the environment. # Default value for CROSS_COMPILE is not to prefix executables # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile CROSS_COMPILE ?= # bbox: we may have CONFIG_CROSS_COMPILER_PREFIX in .config, # and it has not been included yet... thus using an awkward syntax. ifeq ($(CROSS_COMPILE),) CROSS_COMPILE := $(shell grep ^CONFIG_CROSS_COMPILER_PREFIX .config 2>/dev/null) CROSS_COMPILE := $(subst CONFIG_CROSS_COMPILER_PREFIX=,,$(CROSS_COMPILE)) CROSS_COMPILE := $(subst ",,$(CROSS_COMPILE)) #") endif # SUBARCH tells the usermode build what the underlying arch is. That is set # first, and if a usermode build is happening, the "ARCH=um" on the command # line overrides the setting of ARCH below. If a native build is happening, # then ARCH is assigned, getting whatever value it gets normally, and # SUBARCH is subsequently ignored. ifneq ($(CROSS_COMPILE),) SUBARCH := $(shell echo $(CROSS_COMPILE) | cut -d- -f1) else SUBARCH := $(shell uname -m) endif SUBARCH := $(shell echo $(SUBARCH) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ -e s/arm.*/arm/ -e s/sa110/arm/ \ -e s/s390x/s390/ -e s/parisc64/parisc/ \ -e s/ppc.*/powerpc/ -e s/mips.*/mips/ ) ARCH ?= $(SUBARCH) # Architecture as present in compile.h UTS_MACHINE := $(ARCH) # SHELL used by kbuild CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ else if [ -x /bin/bash ]; then echo /bin/bash; \ else echo sh; fi ; fi) # Decide whether to build built-in, modular, or both. # Normally, just do built-in. KBUILD_MODULES := KBUILD_BUILTIN := 1 # If we have only "make modules", don't compile built-in objects. # When we're building modules with modversions, we need to consider # the built-in objects during the descend as well, in order to # make sure the checksums are uptodate before we record them. ifeq ($(MAKECMDGOALS),modules) KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1) endif # If we have "make <whatever> modules", compile modules # in addition to whatever we do anyway. # Just "make" or "make all" shall build modules as well ifneq ($(filter all _all modules,$(MAKECMDGOALS)),) KBUILD_MODULES := 1 endif ifeq ($(MAKECMDGOALS),) KBUILD_MODULES := 1 endif export KBUILD_MODULES KBUILD_BUILTIN export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD # Beautify output # --------------------------------------------------------------------------- # # Normally, we echo the whole command before executing it. By making # that echo $($(quiet)$(cmd)), we now have the possibility to set # $(quiet) to choose other forms of output instead, e.g. # # quiet_cmd_cc_o_c = Compiling $(RELDIR)/$@ # cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< # # If $(quiet) is empty, the whole command will be printed. # If it is set to "quiet_", only the short version will be printed. # If it is set to "silent_", nothing wil be printed at all, since # the variable $(silent_cmd_cc_o_c) doesn't exist. # # A simple variant is to prefix commands with $(Q) - that's useful # for commands that shall be hidden in non-verbose mode. # # $(Q)ln $@ :< # # If KBUILD_VERBOSE equals 0 then the above command will be hidden. # If KBUILD_VERBOSE equals 1 then the above command is displayed. ifeq ($(KBUILD_VERBOSE),1) quiet = Q = else quiet=quiet_ Q = @ endif # If the user is running make -s (silent mode), suppress echoing of # commands ifneq ($(findstring s,$(MAKEFLAGS)),) quiet=silent_ endif export quiet Q KBUILD_VERBOSE # Look for make include files relative to root of kernel src MAKEFLAGS += --include-dir=$(srctree) HOSTCC = gcc HOSTCXX = g++ HOSTCFLAGS := HOSTCXXFLAGS := # We need some generic definitions include $(srctree)/scripts/Kbuild.include HOSTCFLAGS += $(call hostcc-option,-Wall -Wstrict-prototypes -O2 -fomit-frame-pointer,) HOSTCXXFLAGS += -O2 # For maximum performance (+ possibly random breakage, uncomment # the following) MAKEFLAGS += -rR # Make variables (CC, etc...) AS = $(CROSS_COMPILE)as CC = $(CROSS_COMPILE)gcc LD = $(CC) -nostdlib CPP = $(CC) -E AR = $(CROSS_COMPILE)ar NM = $(CROSS_COMPILE)nm STRIP = $(CROSS_COMPILE)strip OBJCOPY = $(CROSS_COMPILE)objcopy OBJDUMP = $(CROSS_COMPILE)objdump PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config AWK = awk GENKSYMS = scripts/genksyms/genksyms DEPMOD = /sbin/depmod KALLSYMS = scripts/kallsyms PERL = perl CHECK = sparse CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise $(CF) MODFLAGS = -DMODULE CFLAGS_MODULE = $(MODFLAGS) AFLAGS_MODULE = $(MODFLAGS) LDFLAGS_MODULE = -r CFLAGS_KERNEL = AFLAGS_KERNEL = # Use LINUXINCLUDE when you must reference the include/ directory. # Needed to be compatible with the O= option CFLAGS := $(CFLAGS) # Added only to final link stage of busybox binary CFLAGS_busybox := $(CFLAGS_busybox) CPPFLAGS := $(CPPFLAGS) AFLAGS := $(AFLAGS) LDFLAGS := $(LDFLAGS) LDLIBS := # Read KERNELRELEASE from .kernelrelease (if it exists) KERNELRELEASE = $(shell cat .kernelrelease 2> /dev/null) KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION \ ARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \ CPP AR NM STRIP OBJCOPY OBJDUMP MAKE AWK GENKSYMS PERL UTS_MACHINE \ HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS export CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS export CFLAGS CFLAGS_KERNEL CFLAGS_MODULE export AFLAGS AFLAGS_KERNEL AFLAGS_MODULE export FLTFLAGS # When compiling out-of-tree modules, put MODVERDIR in the module # tree rather than in the kernel tree. The kernel tree might # even be read-only. export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions # Files to ignore in find ... statements RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS -o -name .pc -o -name .hg -o -name .git \) -prune -o export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn --exclude CVS --exclude .pc --exclude .hg --exclude .git # =========================================================================== # Rules shared between *config targets and build targets # Basic helpers built in scripts/ PHONY += scripts_basic scripts_basic: $(Q)$(MAKE) $(build)=scripts/basic # To avoid any implicit rule to kick in, define an empty command. scripts/basic/%: scripts_basic ; # This target generates Kbuild's and Config.in's from *.c files PHONY += gen_build_files gen_build_files: $(wildcard $(srctree)/*/*.c) $(wildcard $(srctree)/*/*/*.c) $(Q)$(srctree)/scripts/gen_build_files.sh $(srctree) $(objtree) # bbox: we have helpers in applets/ # we depend on scripts_basic, since scripts/basic/fixdep # must be built before any other host prog PHONY += applets_dir applets_dir: scripts_basic gen_build_files $(Q)$(MAKE) $(build)=applets applets/%: applets_dir ; PHONY += outputmakefile # outputmakefile generates a Makefile in the output directory, if using a # separate output directory. This allows convenient use of make in the # output directory. outputmakefile: ifneq ($(KBUILD_SRC),) $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \ $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL) endif # To make sure we do not include .config for any of the *config targets # catch them early, and hand them over to scripts/kconfig/Makefile # It is allowed to specify more targets when calling make, including # mixing *config targets and build targets. # For example 'make oldconfig all'. # Detect when mixed targets is specified, and make a second invocation # of make so .config is not included in this case either (for *config). no-dot-config-targets := clean mrproper distclean \ cscope TAGS tags help %docs #bbox# check% is removed from above config-targets := 0 mixed-targets := 0 dot-config := 1 ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),) ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),) dot-config := 0 endif endif ifeq ($(KBUILD_EXTMOD),) ifneq ($(filter config %config,$(MAKECMDGOALS)),) config-targets := 1 ifneq ($(filter-out config %config,$(MAKECMDGOALS)),) mixed-targets := 1 endif endif endif ifeq ($(mixed-targets),1) # =========================================================================== # We're called with mixed targets (*config and build targets). # Handle them one by one. %:: FORCE $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@ else ifeq ($(config-targets),1) # =========================================================================== # *config targets only - make sure prerequisites are updated, and descend # in scripts/kconfig to make the *config target # Read arch specific Makefile to set KBUILD_DEFCONFIG as needed. # KBUILD_DEFCONFIG may point out an alternative default configuration # used for 'make defconfig' -include $(srctree)/arch/$(ARCH)/Makefile export KBUILD_DEFCONFIG config: scripts_basic outputmakefile gen_build_files FORCE $(Q)mkdir -p include $(Q)$(MAKE) $(build)=scripts/kconfig $@ $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= .kernelrelease %config: scripts_basic outputmakefile gen_build_files FORCE $(Q)mkdir -p include $(Q)$(MAKE) $(build)=scripts/kconfig $@ $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= .kernelrelease else # =========================================================================== # Build targets only - this includes busybox, arch specific targets, clean # targets and others. In general all targets except *config targets. ifeq ($(KBUILD_EXTMOD),) # Additional helpers built in scripts/ # Carefully list dependencies so we do not try to build scripts twice # in parallel PHONY += scripts scripts: gen_build_files scripts_basic include/config/MARKER $(Q)$(MAKE) $(build)=$(@) scripts_basic: include/autoconf.h # Objects we will link into busybox / subdirs we need to visit core-y := \ applets/ \ libs-y := \ archival/ \ archival/libarchive/ \ console-tools/ \ coreutils/ \ coreutils/libcoreutils/ \ debianutils/ \ e2fsprogs/ \ editors/ \ findutils/ \ init/ \ libbb/ \ libpwdgrp/ \ loginutils/ \ mailutils/ \ miscutils/ \ modutils/ \ networking/ \ networking/libiproute/ \ networking/udhcp/ \ printutils/ \ procps/ \ runit/ \ selinux/ \ shell/ \ sysklogd/ \ util-linux/ \ util-linux/volume_id/ \ endif # KBUILD_EXTMOD ifeq ($(dot-config),1) # In this section, we need .config # Read in dependencies to all Kconfig* files, make sure to run # oldconfig if changes are detected. -include .kconfig.d -include .config # If .config needs to be updated, it will be done via the dependency # that autoconf has on .config. # To avoid any implicit rule to kick in, define an empty command .config .kconfig.d: ; # Now we can define CFLAGS etc according to .config include $(srctree)/Makefile.flags # If .config is newer than include/autoconf.h, someone tinkered # with it and forgot to run make oldconfig. # If kconfig.d is missing then we are probarly in a cleaned tree so # we execute the config step to be sure to catch updated Kconfig files include/autoconf.h: .kconfig.d .config $(wildcard $(srctree)/*/*.c) $(wildcard $(srctree)/*/*/*.c) | gen_build_files $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig include/usage.h: gen_build_files else # Dummy target needed, because used as prerequisite include/autoconf.h: ; endif # The all: target is the default when no target is given on the # command line. # This allow a user to issue only 'make' to build a kernel including modules # Defaults busybox but it is usually overridden in the arch makefile all: busybox doc -include $(srctree)/arch/$(ARCH)/Makefile # arch Makefile may override CC so keep this after arch Makefile is included #bbox# NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) CHECKFLAGS += $(NOSTDINC_FLAGS) # Default kernel image to build when no specific target is given. # KBUILD_IMAGE may be overruled on the commandline or # set in the environment # Also any assignments in arch/$(ARCH)/Makefile take precedence over # this default value export KBUILD_IMAGE ?= busybox # # INSTALL_PATH specifies where to place the updated kernel and system map # images. Default is /boot, but you can set it to other values export INSTALL_PATH ?= /boot # # INSTALL_MOD_PATH specifies a prefix to MODLIB for module directory # relocations required by build roots. This is not defined in the # makefile but the arguement can be passed to make if needed. # MODLIB = $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE) export MODLIB ifeq ($(KBUILD_EXTMOD),) busybox-dirs := $(patsubst %/,%,$(filter %/, $(core-y) $(core-m) $(libs-y) $(libs-m))) busybox-alldirs := $(sort $(busybox-dirs) $(patsubst %/,%,$(filter %/, \ $(core-n) $(core-) $(libs-n) $(libs-) \ ))) core-y := $(patsubst %/, %/built-in.o, $(core-y)) libs-y1 := $(patsubst %/, %/lib.a, $(libs-y)) libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y)) libs-y := $(libs-y1) $(libs-y2) # Build busybox # --------------------------------------------------------------------------- # busybox is build from the objects selected by $(busybox-init) and # $(busybox-main). Most are built-in.o files from top-level directories # in the kernel tree, others are specified in arch/$(ARCH)Makefile. # Ordering when linking is important, and $(busybox-init) must be first. # # busybox # ^ # | # +-< $(busybox-init) # | +--< init/version.o + more # | # +--< $(busybox-main) # | +--< driver/built-in.o mm/built-in.o + more # | # +-< kallsyms.o (see description in CONFIG_KALLSYMS section) # # busybox version (uname -v) cannot be updated during normal # descending-into-subdirs phase since we do not yet know if we need to # update busybox. # Therefore this step is delayed until just before final link of busybox - # except in the kallsyms case where it is done just before adding the # symbols to the kernel. # # System.map is generated to document addresses of all kernel symbols busybox-all := $(core-y) $(libs-y) # Rule to link busybox - also used during CONFIG_KALLSYMS # May be overridden by arch/$(ARCH)/Makefile quiet_cmd_busybox__ ?= LINK $@ cmd_busybox__ ?= $(srctree)/scripts/trylink \ "$@" \ "$(CC)" \ "$(CFLAGS) $(CFLAGS_busybox)" \ "$(LDFLAGS) $(EXTRA_LDFLAGS)" \ "$(core-y)" \ "$(libs-y)" \ "$(LDLIBS)" # Generate System.map quiet_cmd_sysmap = SYSMAP cmd_sysmap = $(CONFIG_SHELL) $(srctree)/scripts/mksysmap # Link of busybox # If CONFIG_KALLSYMS is set .version is already updated # Generate System.map and verify that the content is consistent # Use + in front of the busybox_version rule to silent warning with make -j2 # First command is ':' to allow us to use + in front of the rule define rule_busybox__ : $(call cmd,busybox__) $(Q)echo 'cmd_$@ := $(cmd_busybox__)' > $(@D)/.$(@F).cmd endef ifdef CONFIG_KALLSYMS # Generate section listing all symbols and add it into busybox $(kallsyms.o) # It's a three stage process: # o .tmp_busybox1 has all symbols and sections, but __kallsyms is # empty # Running kallsyms on that gives us .tmp_kallsyms1.o with # the right size - busybox version (uname -v) is updated during this step # o .tmp_busybox2 now has a __kallsyms section of the right size, # but due to the added section, some addresses have shifted. # From here, we generate a correct .tmp_kallsyms2.o # o The correct .tmp_kallsyms2.o is linked into the final busybox. # o Verify that the System.map from busybox matches the map from # .tmp_busybox2, just in case we did not generate kallsyms correctly. # o If CONFIG_KALLSYMS_EXTRA_PASS is set, do an extra pass using # .tmp_busybox3 and .tmp_kallsyms3.o. This is only meant as a # temporary bypass to allow the kernel to be built while the # maintainers work out what went wrong with kallsyms. ifdef CONFIG_KALLSYMS_EXTRA_PASS last_kallsyms := 3 else last_kallsyms := 2 endif kallsyms.o := .tmp_kallsyms$(last_kallsyms).o define verify_kallsyms $(Q)$(if $($(quiet)cmd_sysmap), \ echo ' $($(quiet)cmd_sysmap) .tmp_System.map' &&) \ $(cmd_sysmap) .tmp_busybox$(last_kallsyms) .tmp_System.map $(Q)cmp -s System.map .tmp_System.map || \ (echo Inconsistent kallsyms data; \ echo Try setting CONFIG_KALLSYMS_EXTRA_PASS; \ rm .tmp_kallsyms* ; /bin/false ) endef # Update busybox version before link # Use + in front of this rule to silent warning about make -j1 # First command is ':' to allow us to use + in front of this rule cmd_ksym_ld = $(cmd_busybox__) define rule_ksym_ld : +$(call cmd,busybox_version) $(call cmd,busybox__) $(Q)echo 'cmd_$@ := $(cmd_busybox__)' > $(@D)/.$(@F).cmd endef # Generate .S file with all kernel symbols quiet_cmd_kallsyms = KSYM $@ cmd_kallsyms = $(NM) -n $< | $(KALLSYMS) \ $(if $(CONFIG_KALLSYMS_ALL),--all-symbols) > $@ .tmp_kallsyms1.o .tmp_kallsyms2.o .tmp_kallsyms3.o: %.o: %.S scripts FORCE $(call if_changed_dep,as_o_S) .tmp_kallsyms%.S: .tmp_busybox% $(KALLSYMS) $(call cmd,kallsyms) # .tmp_busybox1 must be complete except kallsyms, so update busybox version .tmp_busybox1: $(busybox-lds) $(busybox-all) FORCE $(call if_changed_rule,ksym_ld) .tmp_busybox2: $(busybox-lds) $(busybox-all) .tmp_kallsyms1.o FORCE $(call if_changed,busybox__) .tmp_busybox3: $(busybox-lds) $(busybox-all) .tmp_kallsyms2.o FORCE $(call if_changed,busybox__) # Needs to visit scripts/ before $(KALLSYMS) can be used. $(KALLSYMS): scripts ; # Generate some data for debugging strange kallsyms problems debug_kallsyms: .tmp_map$(last_kallsyms) .tmp_map%: .tmp_busybox% FORCE ($(OBJDUMP) -h $< | $(AWK) '/^ +[0-9]/{print $$4 " 0 " $$2}'; $(NM) $<) | sort > $@ .tmp_map3: .tmp_map2 .tmp_map2: .tmp_map1 endif # ifdef CONFIG_KALLSYMS # busybox image - including updated kernel symbols busybox_unstripped: $(busybox-all) FORCE $(call if_changed_rule,busybox__) $(Q)rm -f .old_version busybox: busybox_unstripped ifeq ($(SKIP_STRIP),y) $(Q)cp $< $@ else $(Q)$(STRIP) -s --remove-section=.note --remove-section=.comment \ busybox_unstripped -o $@ # strip is confused by PIE executable and does not set exec bits $(Q)chmod a+x $@ endif # The actual objects are generated when descending, # make sure no implicit rule kicks in $(sort $(busybox-all)): $(busybox-dirs) ; # Handle descending into subdirectories listed in $(busybox-dirs) # Preset locale variables to speed up the build process. Limit locale # tweaks to this spot to avoid wrong language settings when running # make menuconfig etc. # Error messages still appears in the original language PHONY += $(busybox-dirs) $(busybox-dirs): prepare scripts $(Q)$(MAKE) $(build)=$@ # Build the kernel release string # The KERNELRELEASE is stored in a file named .kernelrelease # to be used when executing for example make install or make modules_install # # Take the contents of any files called localversion* and the config # variable CONFIG_LOCALVERSION and append them to KERNELRELEASE. # LOCALVERSION from the command line override all of this nullstring := space := $(nullstring) # end of line ___localver = $(objtree)/localversion* $(srctree)/localversion* __localver = $(sort $(wildcard $(___localver))) # skip backup files (containing '~') _localver = $(foreach f, $(__localver), $(if $(findstring ~, $(f)),,$(f))) localver = $(subst $(space),, \ $(shell cat /dev/null $(_localver)) \ $(patsubst "%",%,$(CONFIG_LOCALVERSION))) # If CONFIG_LOCALVERSION_AUTO is set scripts/setlocalversion is called # and if the SCM is know a tag from the SCM is appended. # The appended tag is determinded by the SCM used. # # Currently, only git is supported. # Other SCMs can edit scripts/setlocalversion and add the appropriate # checks as needed. ifdef _BB_DISABLED_CONFIG_LOCALVERSION_AUTO _localver-auto = $(shell $(CONFIG_SHELL) \ $(srctree)/scripts/setlocalversion $(srctree)) localver-auto = $(LOCALVERSION)$(_localver-auto) endif localver-full = $(localver)$(localver-auto) # Store (new) KERNELRELASE string in .kernelrelease kernelrelease = $(KERNELVERSION)$(localver-full) .kernelrelease: FORCE $(Q)rm -f $@ $(Q)echo $(kernelrelease) > $@ # Things we need to do before we recursively start building the kernel # or the modules are listed in "prepare". # A multi level approach is used. prepareN is processed before prepareN-1. # archprepare is used in arch Makefiles and when processed asm symlink, # version.h and scripts_basic is processed / created. # Listed in dependency order PHONY += prepare archprepare prepare0 prepare1 prepare2 prepare3 # prepare-all is deprecated, use prepare as valid replacement PHONY += prepare-all # prepare3 is used to check if we are building in a separate output directory, # and if so do: # 1) Check that make has not been executed in the kernel src $(srctree) # 2) Create the include2 directory, used for the second asm symlink prepare3: .kernelrelease ifneq ($(KBUILD_SRC),) @echo ' Using $(srctree) as source for busybox' $(Q)if [ -f $(srctree)/.config ]; then \ echo " $(srctree) is not clean, please run 'make mrproper'";\ echo " in the '$(srctree)' directory.";\ /bin/false; \ fi; $(Q)if [ ! -d include2 ]; then mkdir -p include2; fi; $(Q)ln -fsn $(srctree)/include/asm-$(ARCH) include2/asm endif # prepare2 creates a makefile if using a separate output directory prepare2: prepare3 outputmakefile prepare1: prepare2 include/config/MARKER ifneq ($(KBUILD_MODULES),) $(Q)mkdir -p $(MODVERDIR) $(Q)rm -f $(MODVERDIR)/* endif archprepare: prepare1 scripts_basic applets_dir prepare0: archprepare FORCE $(Q)$(MAKE) $(build)=. # All the preparing.. prepare prepare-all: prepare0 # Leave this as default for preprocessing busybox.lds.S, which is now # done in arch/$(ARCH)/kernel/Makefile export CPPFLAGS_busybox.lds += -P -C -U$(ARCH) # FIXME: The asm symlink changes when $(ARCH) changes. That's # hard to detect, but I suppose "make mrproper" is a good idea # before switching between archs anyway. #bbox# include/asm: #bbox# @echo ' SYMLINK $@ -> include/asm-$(ARCH)' #bbox# $(Q)if [ ! -d include ]; then mkdir -p include; fi; #bbox# @ln -fsn asm-$(ARCH) $@ # Split autoconf.h into include/linux/config/* quiet_cmd_gen_bbconfigopts = GEN include/bbconfigopts.h cmd_gen_bbconfigopts = $(srctree)/scripts/mkconfigs include/bbconfigopts.h include/bbconfigopts_bz2.h quiet_cmd_split_autoconf = SPLIT include/autoconf.h -> include/config/* cmd_split_autoconf = scripts/basic/split-include include/autoconf.h include/config #bbox# piggybacked generation of few .h files include/config/MARKER: scripts/basic/split-include include/autoconf.h $(call cmd,split_autoconf) $(call cmd,gen_bbconfigopts) @touch $@ # Generate some files # --------------------------------------------------------------------------- # KERNELRELEASE can change from a few different places, meaning version.h # needs to be updated, so this check is forced on all builds uts_len := 64 define filechk_version.h if [ `echo -n "$(KERNELRELEASE)" | wc -c ` -gt $(uts_len) ]; then \ echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2; \ exit 1; \ fi; \ (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\"; \ echo \#define LINUX_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)`; \ echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))'; \ ) endef # --------------------------------------------------------------------------- PHONY += depend dep depend dep: @echo '*** Warning: make $@ is unnecessary now.' # --------------------------------------------------------------------------- # Modules ifdef _BB_DISABLED_CONFIG_MODULES # By default, build modules as well all: modules # Build modules PHONY += modules modules: $(busybox-dirs) $(if $(KBUILD_BUILTIN),busybox) @echo ' Building modules, stage 2.'; $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost # Target to prepare building external modules PHONY += modules_prepare modules_prepare: prepare scripts # Target to install modules PHONY += modules_install modules_install: _modinst_ _modinst_post PHONY += _modinst_ _modinst_: @if [ -z "`$(DEPMOD) -V 2>/dev/null | grep module-init-tools`" ]; then \ echo "Warning: you may need to install module-init-tools"; \ echo "See http://www.codemonkey.org.uk/docs/post-halloween-2.6.txt";\ sleep 1; \ fi @rm -rf $(MODLIB)/kernel @rm -f $(MODLIB)/source @mkdir -p $(MODLIB)/kernel @ln -s $(srctree) $(MODLIB)/source @if [ ! $(objtree) -ef $(MODLIB)/build ]; then \ rm -f $(MODLIB)/build ; \ ln -s $(objtree) $(MODLIB)/build ; \ fi $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modinst # If System.map exists, run depmod. This deliberately does not have a # dependency on System.map since that would run the dependency tree on # busybox. This depmod is only for convenience to give the initial # boot a modules.dep even before / is mounted read-write. However the # boot script depmod is the master version. ifeq "$(strip $(INSTALL_MOD_PATH))" "" depmod_opts := else depmod_opts := -b $(INSTALL_MOD_PATH) -r endif PHONY += _modinst_post _modinst_post: _modinst_ if [ -r System.map -a -x $(DEPMOD) ]; then $(DEPMOD) -ae -F System.map $(depmod_opts) $(KERNELRELEASE); fi else # CONFIG_MODULES # Modules not configured # --------------------------------------------------------------------------- modules modules_install: FORCE @echo @echo "The present busybox configuration has modules disabled." @echo "Type 'make config' and enable loadable module support." @echo "Then build a kernel with module support enabled." @echo @exit 1 endif # CONFIG_MODULES ### # Cleaning is done on three levels. # make clean Delete most generated files # Leave enough to build external modules # make mrproper Delete the current configuration, and all generated files # make distclean Remove editor backup files, patch leftover files and the like # Directories & files removed with 'make clean' CLEAN_DIRS += $(MODVERDIR) _install 0_lib CLEAN_FILES += busybox busybox_unstripped* busybox.links \ System.map .kernelrelease \ .tmp_kallsyms* .tmp_version .tmp_busybox* .tmp_System.map # Directories & files removed with 'make mrproper' MRPROPER_DIRS += include/config include2 MRPROPER_FILES += .config .config.old include/asm .version .old_version \ include/NUM_APPLETS.h \ include/autoconf.h \ include/bbconfigopts.h \ include/bbconfigopts_bz2.h \ include/usage_compressed.h \ include/applet_tables.h \ include/applets.h \ include/usage.h \ applets/usage \ .kernelrelease Module.symvers tags TAGS cscope* \ busybox_old # clean - Delete most, but leave enough to build external modules # clean: rm-dirs := $(CLEAN_DIRS) clean: rm-files := $(CLEAN_FILES) clean-dirs := $(addprefix _clean_,$(srctree) $(busybox-alldirs)) PHONY += $(clean-dirs) clean archclean $(clean-dirs): $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@) clean: archclean $(clean-dirs) $(call cmd,rmdirs) $(call cmd,rmfiles) @find . $(RCS_FIND_IGNORE) \ \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \ -type f -print | xargs rm -f PHONY += doc-clean doc-clean: rm-files := docs/busybox.pod \ docs/BusyBox.html docs/busybox.1 docs/BusyBox.txt doc-clean: $(call cmd,rmfiles) # mrproper - Delete all generated files, including .config # mrproper: rm-dirs := $(wildcard $(MRPROPER_DIRS)) mrproper: rm-files := $(wildcard $(MRPROPER_FILES)) mrproper-dirs := $(addprefix _mrproper_,scripts) PHONY += $(mrproper-dirs) mrproper archmrproper $(mrproper-dirs): $(Q)$(MAKE) $(clean)=$(patsubst _mrproper_%,%,$@) mrproper: clean archmrproper $(mrproper-dirs) $(call cmd,rmdirs) $(call cmd,rmfiles) @find . -name Config.src | sed 's/.src$$/.in/' | xargs -r rm -f @find . -name Kbuild.src | sed 's/.src$$//' | xargs -r rm -f # distclean # PHONY += distclean distclean: mrproper @find $(srctree) $(RCS_FIND_IGNORE) \ \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ -o -name '.*.rej' -o -name '*.tmp' -o -size 0 \ -o -name '*%' -o -name '.*.cmd' -o -name 'core' \) \ -type f -print | xargs rm -f # Packaging of the kernel to various formats # --------------------------------------------------------------------------- # rpm target kept for backward compatibility package-dir := $(srctree)/scripts/package %pkg: FORCE $(Q)$(MAKE) $(build)=$(package-dir) $@ rpm: FORCE $(Q)$(MAKE) $(build)=$(package-dir) $@ # Brief documentation of the typical targets used # --------------------------------------------------------------------------- boards := $(wildcard $(srctree)/configs/*_defconfig) boards := $(notdir $(boards)) -include $(srctree)/Makefile.help # Documentation targets # --------------------------------------------------------------------------- %docs: scripts_basic FORCE $(Q)$(MAKE) $(build)=Documentation/DocBook $@ else # KBUILD_EXTMOD ### # External module support. # When building external modules the kernel used as basis is considered # read-only, and no consistency checks are made and the make # system is not used on the basis kernel. If updates are required # in the basis kernel ordinary make commands (without M=...) must # be used. # # The following are the only valid targets when building external # modules. # make M=dir clean Delete all automatically generated files # make M=dir modules Make all modules in specified dir # make M=dir Same as 'make M=dir modules' # make M=dir modules_install # Install the modules build in the module directory # Assumes install directory is already created # We are always building modules KBUILD_MODULES := 1 PHONY += crmodverdir crmodverdir: $(Q)mkdir -p $(MODVERDIR) $(Q)rm -f $(MODVERDIR)/* PHONY += $(objtree)/Module.symvers $(objtree)/Module.symvers: @test -e $(objtree)/Module.symvers || ( \ echo; \ echo " WARNING: Symbol version dump $(objtree)/Module.symvers"; \ echo " is missing; modules will have no dependencies and modversions."; \ echo ) module-dirs := $(addprefix _module_,$(KBUILD_EXTMOD)) PHONY += $(module-dirs) modules $(module-dirs): crmodverdir $(objtree)/Module.symvers $(Q)$(MAKE) $(build)=$(patsubst _module_%,%,$@) modules: $(module-dirs) @echo ' Building modules, stage 2.'; $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost PHONY += modules_install modules_install: _emodinst_ _emodinst_post install-dir := $(if $(INSTALL_MOD_DIR),$(INSTALL_MOD_DIR),extra) PHONY += _emodinst_ _emodinst_: $(Q)mkdir -p $(MODLIB)/$(install-dir) $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modinst # Run depmod only is we have System.map and depmod is executable quiet_cmd_depmod = DEPMOD $(KERNELRELEASE) cmd_depmod = if [ -r System.map -a -x $(DEPMOD) ]; then \ $(DEPMOD) -ae -F System.map \ $(if $(strip $(INSTALL_MOD_PATH)), \ -b $(INSTALL_MOD_PATH) -r) \ $(KERNELRELEASE); \ fi PHONY += _emodinst_post _emodinst_post: _emodinst_ $(call cmd,depmod) clean-dirs := $(addprefix _clean_,$(KBUILD_EXTMOD)) PHONY += $(clean-dirs) clean $(clean-dirs): $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@) clean: rm-dirs := $(MODVERDIR) clean: $(clean-dirs) $(call cmd,rmdirs) @find $(KBUILD_EXTMOD) $(RCS_FIND_IGNORE) \ \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \ -type f -print | xargs rm -f # Dummies... PHONY += prepare scripts prepare: ; scripts: ; endif # KBUILD_EXTMOD # Generate tags for editors # --------------------------------------------------------------------------- #We want __srctree to totally vanish out when KBUILD_OUTPUT is not set #(which is the most common case IMHO) to avoid unneeded clutter in the big tags file. #Adding $(srctree) adds about 20M on i386 to the size of the output file! ifeq ($(src),$(obj)) __srctree = else __srctree = $(srctree)/ endif ifeq ($(ALLSOURCE_ARCHS),) ifeq ($(ARCH),um) ALLINCLUDE_ARCHS := $(ARCH) $(SUBARCH) else ALLINCLUDE_ARCHS := $(ARCH) endif else #Allow user to specify only ALLSOURCE_PATHS on the command line, keeping existing behaviour. ALLINCLUDE_ARCHS := $(ALLSOURCE_ARCHS) endif ALLSOURCE_ARCHS := $(ARCH) define all-sources ( find $(__srctree) $(RCS_FIND_IGNORE) \ \( -name include -o -name arch \) -prune -o \ -name '*.[chS]' -print; \ for ARCH in $(ALLSOURCE_ARCHS) ; do \ find $(__srctree)arch/$${ARCH} $(RCS_FIND_IGNORE) \ -name '*.[chS]' -print; \ done ; \ find $(__srctree)security/selinux/include $(RCS_FIND_IGNORE) \ -name '*.[chS]' -print; \ find $(__srctree)include $(RCS_FIND_IGNORE) \ \( -name config -o -name 'asm-*' \) -prune \ -o -name '*.[chS]' -print; \ for ARCH in $(ALLINCLUDE_ARCHS) ; do \ find $(__srctree)include/asm-$${ARCH} $(RCS_FIND_IGNORE) \ -name '*.[chS]' -print; \ done ; \ find $(__srctree)include/asm-generic $(RCS_FIND_IGNORE) \ -name '*.[chS]' -print ) endef quiet_cmd_cscope-file = FILELST cscope.files cmd_cscope-file = (echo \-k; echo \-q; $(all-sources)) > cscope.files quiet_cmd_cscope = MAKE cscope.out cmd_cscope = cscope -b cscope: FORCE $(call cmd,cscope-file) $(call cmd,cscope) quiet_cmd_TAGS = MAKE $@ define cmd_TAGS rm -f $@; \ ETAGSF=`etags --version | grep -i exuberant >/dev/null && \ echo "-I __initdata,__exitdata,__acquires,__releases \ -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL \ --extra=+f --c-kinds=+px"`; \ $(all-sources) | xargs etags $$ETAGSF -a endef TAGS: FORCE $(call cmd,TAGS) quiet_cmd_tags = MAKE $@ define cmd_tags rm -f $@; \ CTAGSF=`ctags --version | grep -i exuberant >/dev/null && \ echo "-I __initdata,__exitdata,__acquires,__releases \ -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL \ --extra=+f --c-kinds=+px"`; \ $(all-sources) | xargs ctags $$CTAGSF -a endef tags: FORCE $(call cmd,tags) # Scripts to check various things for consistency # --------------------------------------------------------------------------- includecheck: find * $(RCS_FIND_IGNORE) \ -name '*.[hcS]' -type f -print | sort \ | xargs $(PERL) -w scripts/checkincludes.pl versioncheck: find * $(RCS_FIND_IGNORE) \ -name '*.[hcS]' -type f -print | sort \ | xargs $(PERL) -w scripts/checkversion.pl namespacecheck: $(PERL) $(srctree)/scripts/namespace.pl endif #ifeq ($(config-targets),1) endif #ifeq ($(mixed-targets),1) PHONY += checkstack checkstack: $(OBJDUMP) -d busybox $$(find . -name '*.ko') | \ $(PERL) $(src)/scripts/checkstack.pl $(ARCH) kernelrelease: $(if $(wildcard .kernelrelease), $(Q)echo $(KERNELRELEASE), \ $(error kernelrelease not valid - run 'make *config' to update it)) kernelversion: @echo $(KERNELVERSION) # Single targets # --------------------------------------------------------------------------- # Single targets are compatible with: # - build whith mixed source and output # - build with separate output dir 'make O=...' # - external modules # # target-dir => where to store outputfile # build-dir => directory in kernel source tree to use ifeq ($(KBUILD_EXTMOD),) build-dir = $(patsubst %/,%,$(dir $@)) target-dir = $(dir $@) else zap-slash=$(filter-out .,$(patsubst %/,%,$(dir $@))) build-dir = $(KBUILD_EXTMOD)$(if $(zap-slash),/$(zap-slash)) target-dir = $(if $(KBUILD_EXTMOD),$(dir $<),$(dir $@)) endif %.s: %.c prepare scripts FORCE $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) %.i: %.c prepare scripts FORCE $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) %.o: %.c prepare scripts FORCE $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) %.lst: %.c prepare scripts FORCE $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) %.s: %.S prepare scripts FORCE $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) %.o: %.S prepare scripts FORCE $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) # Modules %/: prepare scripts FORCE $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ $(build)=$(build-dir) /: prepare scripts FORCE $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ $(build)=$(build-dir) %.ko: prepare scripts FORCE $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ $(build)=$(build-dir) $(@:.ko=.o) $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost # FIXME Should go into a make.lib or something # =========================================================================== quiet_cmd_rmdirs = $(if $(wildcard $(rm-dirs)),CLEAN $(wildcard $(rm-dirs))) cmd_rmdirs = rm -rf $(rm-dirs) quiet_cmd_rmfiles = $(if $(wildcard $(rm-files)),CLEAN $(wildcard $(rm-files))) cmd_rmfiles = rm -f $(rm-files) a_flags = -Wp,-MD,$(depfile) $(AFLAGS) $(AFLAGS_KERNEL) \ $(NOSTDINC_FLAGS) $(CPPFLAGS) \ $(modkern_aflags) $(EXTRA_AFLAGS) $(AFLAGS_$(*F).o) quiet_cmd_as_o_S = AS $@ cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< # read all saved command lines targets := $(wildcard $(sort $(targets))) cmd_files := $(wildcard .*.cmd $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd)) ifneq ($(cmd_files),) $(cmd_files): ; # Do not try to update included dependency files include $(cmd_files) endif # Shorthand for $(Q)$(MAKE) -f scripts/Makefile.clean obj=dir # Usage: # $(Q)$(MAKE) $(clean)=dir clean := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.clean obj endif # skip-makefile PHONY += FORCE FORCE: -include $(srctree)/Makefile.custom # Declare the contents of the .PHONY variable as phony. We keep that # information in a variable se we can use it in if_changed and friends. .PHONY: $(PHONY) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/sysklogd/����������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�013626� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/sysklogd/syslogd.c�������������������������������������������������������������������0000644�0000000�0000000�00000067043�12263563520�015473� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Mini syslogd implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> * * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org> * * "circular buffer" Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com> * * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001 * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define syslogd_trivial_usage //usage: "[OPTIONS]" //usage:#define syslogd_full_usage "\n\n" //usage: "System logging utility\n" //usage: IF_NOT_FEATURE_SYSLOGD_CFG( //usage: "(this version of syslogd ignores /etc/syslog.conf)\n" //usage: ) //usage: "\n -n Run in foreground" //usage: "\n -O FILE Log to FILE (default:/var/log/messages)" //usage: "\n -l N Log only messages more urgent than prio N (1-8)" //usage: "\n -S Smaller output" //usage: IF_FEATURE_ROTATE_LOGFILE( //usage: "\n -s SIZE Max size (KB) before rotation (default:200KB, 0=off)" //usage: "\n -b N N rotated logs to keep (default:1, max=99, 0=purge)" //usage: ) //usage: IF_FEATURE_REMOTE_LOG( //usage: "\n -R HOST[:PORT] Log to HOST:PORT (default PORT:514)" //usage: "\n -L Log locally and via network (default is network only if -R)" //usage: ) //usage: IF_FEATURE_SYSLOGD_DUP( //usage: "\n -D Drop duplicates" //usage: ) //usage: IF_FEATURE_IPC_SYSLOG( /* NB: -Csize shouldn't have space (because size is optional) */ //usage: "\n -C[size_kb] Log to shared mem buffer (use logread to read it)" //usage: ) //usage: IF_FEATURE_SYSLOGD_CFG( //usage: "\n -f FILE Use FILE as config (default:/etc/syslog.conf)" //usage: ) /* //usage: "\n -m MIN Minutes between MARK lines (default:20, 0=off)" */ //usage: IF_FEATURE_KMSG_SYSLOG( //usage: "\n -K Log to kernel printk buffer (use dmesg to read it)" //usage: ) //usage: //usage:#define syslogd_example_usage //usage: "$ syslogd -R masterlog:514\n" //usage: "$ syslogd -R 192.168.1.1:601\n" /* * Done in syslogd_and_logger.c: #include "libbb.h" #define SYSLOG_NAMES #define SYSLOG_NAMES_CONST #include <syslog.h> */ #ifndef _PATH_LOG #define _PATH_LOG "/dev/log" #endif #include <sys/un.h> #include <sys/uio.h> #if ENABLE_FEATURE_REMOTE_LOG #include <netinet/in.h> #endif #if ENABLE_FEATURE_IPC_SYSLOG #include <sys/ipc.h> #include <sys/sem.h> #include <sys/shm.h> #endif #define DEBUG 0 /* MARK code is not very useful, is bloat, and broken: * can deadlock if alarmed to make MARK while writing to IPC buffer * (semaphores are down but do_mark routine tries to down them again) */ #undef SYSLOGD_MARK /* Write locking does not seem to be useful either */ #undef SYSLOGD_WRLOCK enum { MAX_READ = CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE, DNS_WAIT_SEC = 2 * 60, }; /* Semaphore operation structures */ struct shbuf_ds { int32_t size; /* size of data - 1 */ int32_t tail; /* end of message list */ char data[1]; /* data/messages */ }; #if ENABLE_FEATURE_REMOTE_LOG typedef struct { int remoteFD; unsigned last_dns_resolve; len_and_sockaddr *remoteAddr; const char *remoteHostname; } remoteHost_t; #endif typedef struct logFile_t { const char *path; int fd; #if ENABLE_FEATURE_ROTATE_LOGFILE unsigned size; uint8_t isRegular; #endif } logFile_t; #if ENABLE_FEATURE_SYSLOGD_CFG typedef struct logRule_t { uint8_t enabled_facility_priomap[LOG_NFACILITIES]; struct logFile_t *file; struct logRule_t *next; } logRule_t; #endif /* Allows us to have smaller initializer. Ugly. */ #define GLOBALS \ logFile_t logFile; \ /* interval between marks in seconds */ \ /*int markInterval;*/ \ /* level of messages to be logged */ \ int logLevel; \ IF_FEATURE_ROTATE_LOGFILE( \ /* max size of file before rotation */ \ unsigned logFileSize; \ /* number of rotated message files */ \ unsigned logFileRotate; \ ) \ IF_FEATURE_IPC_SYSLOG( \ int shmid; /* ipc shared memory id */ \ int s_semid; /* ipc semaphore id */ \ int shm_size; \ struct sembuf SMwup[1]; \ struct sembuf SMwdn[3]; \ ) \ IF_FEATURE_SYSLOGD_CFG( \ logRule_t *log_rules; \ ) \ IF_FEATURE_KMSG_SYSLOG( \ int kmsgfd; \ int primask; \ ) struct init_globals { GLOBALS }; struct globals { GLOBALS #if ENABLE_FEATURE_REMOTE_LOG llist_t *remoteHosts; #endif #if ENABLE_FEATURE_IPC_SYSLOG struct shbuf_ds *shbuf; #endif time_t last_log_time; /* localhost's name. We print only first 64 chars */ char *hostname; /* We recv into recvbuf... */ char recvbuf[MAX_READ * (1 + ENABLE_FEATURE_SYSLOGD_DUP)]; /* ...then copy to parsebuf, escaping control chars */ /* (can grow x2 max) */ char parsebuf[MAX_READ*2]; /* ...then sprintf into printbuf, adding timestamp (15 chars), * host (64), fac.prio (20) to the message */ /* (growth by: 15 + 64 + 20 + delims = ~110) */ char printbuf[MAX_READ*2 + 128]; }; static const struct init_globals init_data = { .logFile = { .path = "/var/log/messages", .fd = -1, }, #ifdef SYSLOGD_MARK .markInterval = 20 * 60, #endif .logLevel = 8, #if ENABLE_FEATURE_ROTATE_LOGFILE .logFileSize = 200 * 1024, .logFileRotate = 1, #endif #if ENABLE_FEATURE_IPC_SYSLOG .shmid = -1, .s_semid = -1, .shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024), /* default shm size */ .SMwup = { {1, -1, IPC_NOWAIT} }, .SMwdn = { {0, 0}, {1, 0}, {1, +1} }, #endif }; #define G (*ptr_to_globals) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(memcpy(xzalloc(sizeof(G)), &init_data, sizeof(init_data))); \ } while (0) /* Options */ enum { OPTBIT_mark = 0, // -m OPTBIT_nofork, // -n OPTBIT_outfile, // -O OPTBIT_loglevel, // -l OPTBIT_small, // -S IF_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize ,) // -s IF_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt ,) // -b IF_FEATURE_REMOTE_LOG( OPTBIT_remotelog ,) // -R IF_FEATURE_REMOTE_LOG( OPTBIT_locallog ,) // -L IF_FEATURE_IPC_SYSLOG( OPTBIT_circularlog,) // -C IF_FEATURE_SYSLOGD_DUP( OPTBIT_dup ,) // -D IF_FEATURE_SYSLOGD_CFG( OPTBIT_cfg ,) // -f IF_FEATURE_KMSG_SYSLOG( OPTBIT_kmsg ,) // -K OPT_mark = 1 << OPTBIT_mark , OPT_nofork = 1 << OPTBIT_nofork , OPT_outfile = 1 << OPTBIT_outfile , OPT_loglevel = 1 << OPTBIT_loglevel, OPT_small = 1 << OPTBIT_small , OPT_filesize = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize )) + 0, OPT_rotatecnt = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt )) + 0, OPT_remotelog = IF_FEATURE_REMOTE_LOG( (1 << OPTBIT_remotelog )) + 0, OPT_locallog = IF_FEATURE_REMOTE_LOG( (1 << OPTBIT_locallog )) + 0, OPT_circularlog = IF_FEATURE_IPC_SYSLOG( (1 << OPTBIT_circularlog)) + 0, OPT_dup = IF_FEATURE_SYSLOGD_DUP( (1 << OPTBIT_dup )) + 0, OPT_cfg = IF_FEATURE_SYSLOGD_CFG( (1 << OPTBIT_cfg )) + 0, OPT_kmsg = IF_FEATURE_KMSG_SYSLOG( (1 << OPTBIT_kmsg )) + 0, }; #define OPTION_STR "m:nO:l:S" \ IF_FEATURE_ROTATE_LOGFILE("s:" ) \ IF_FEATURE_ROTATE_LOGFILE("b:" ) \ IF_FEATURE_REMOTE_LOG( "R:" ) \ IF_FEATURE_REMOTE_LOG( "L" ) \ IF_FEATURE_IPC_SYSLOG( "C::") \ IF_FEATURE_SYSLOGD_DUP( "D" ) \ IF_FEATURE_SYSLOGD_CFG( "f:" ) \ IF_FEATURE_KMSG_SYSLOG( "K" ) #define OPTION_DECL *opt_m, *opt_l \ IF_FEATURE_ROTATE_LOGFILE(,*opt_s) \ IF_FEATURE_ROTATE_LOGFILE(,*opt_b) \ IF_FEATURE_IPC_SYSLOG( ,*opt_C = NULL) \ IF_FEATURE_SYSLOGD_CFG( ,*opt_f = NULL) #define OPTION_PARAM &opt_m, &(G.logFile.path), &opt_l \ IF_FEATURE_ROTATE_LOGFILE(,&opt_s) \ IF_FEATURE_ROTATE_LOGFILE(,&opt_b) \ IF_FEATURE_REMOTE_LOG( ,&remoteAddrList) \ IF_FEATURE_IPC_SYSLOG( ,&opt_C) \ IF_FEATURE_SYSLOGD_CFG( ,&opt_f) #if ENABLE_FEATURE_SYSLOGD_CFG static const CODE* find_by_name(char *name, const CODE* c_set) { for (; c_set->c_name; c_set++) { if (strcmp(name, c_set->c_name) == 0) return c_set; } return NULL; } #endif static const CODE* find_by_val(int val, const CODE* c_set) { for (; c_set->c_name; c_set++) { if (c_set->c_val == val) return c_set; } return NULL; } #if ENABLE_FEATURE_SYSLOGD_CFG static void parse_syslogdcfg(const char *file) { char *t; logRule_t **pp_rule; /* tok[0] set of selectors */ /* tok[1] file name */ /* tok[2] has to be NULL */ char *tok[3]; parser_t *parser; parser = config_open2(file ? file : "/etc/syslog.conf", file ? xfopen_for_read : fopen_for_read); if (!parser) /* didn't find default /etc/syslog.conf */ /* proceed as if we built busybox without config support */ return; /* use ptr to ptr to avoid checking whether head was initialized */ pp_rule = &G.log_rules; /* iterate through lines of config, skipping comments */ while (config_read(parser, tok, 3, 2, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) { char *cur_selector; logRule_t *cur_rule; /* unexpected trailing token? */ if (tok[2]) goto cfgerr; cur_rule = *pp_rule = xzalloc(sizeof(*cur_rule)); cur_selector = tok[0]; /* iterate through selectors: "kern.info;kern.!err;..." */ do { const CODE *code; char *next_selector; uint8_t negated_prio; /* "kern.!err" */ uint8_t single_prio; /* "kern.=err" */ uint32_t facmap; /* bitmap of enabled facilities */ uint8_t primap; /* bitmap of enabled priorities */ unsigned i; next_selector = strchr(cur_selector, ';'); if (next_selector) *next_selector++ = '\0'; t = strchr(cur_selector, '.'); if (!t) goto cfgerr; *t++ = '\0'; /* separate facility from priority */ negated_prio = 0; single_prio = 0; if (*t == '!') { negated_prio = 1; ++t; } if (*t == '=') { single_prio = 1; ++t; } /* parse priority */ if (*t == '*') primap = 0xff; /* all 8 log levels enabled */ else { uint8_t priority; code = find_by_name(t, prioritynames); if (!code) goto cfgerr; primap = 0; priority = code->c_val; if (priority == INTERNAL_NOPRI) { /* ensure we take "enabled_facility_priomap[fac] &= 0" branch below */ negated_prio = 1; } else { priority = 1 << priority; do { primap |= priority; if (single_prio) break; priority >>= 1; } while (priority); if (negated_prio) primap = ~primap; } } /* parse facility */ if (*cur_selector == '*') facmap = (1<<LOG_NFACILITIES) - 1; else { char *next_facility; facmap = 0; t = cur_selector; /* iterate through facilities: "kern,daemon.<priospec>" */ do { next_facility = strchr(t, ','); if (next_facility) *next_facility++ = '\0'; code = find_by_name(t, facilitynames); if (!code) goto cfgerr; /* "mark" is not a real facility, skip it */ if (code->c_val != INTERNAL_MARK) facmap |= 1<<(LOG_FAC(code->c_val)); t = next_facility; } while (t); } /* merge result with previous selectors */ for (i = 0; i < LOG_NFACILITIES; ++i) { if (!(facmap & (1<<i))) continue; if (negated_prio) cur_rule->enabled_facility_priomap[i] &= primap; else cur_rule->enabled_facility_priomap[i] |= primap; } cur_selector = next_selector; } while (cur_selector); /* check whether current file name was mentioned in previous rules or * as global logfile (G.logFile). */ if (strcmp(G.logFile.path, tok[1]) == 0) { cur_rule->file = &G.logFile; goto found; } /* temporarily use cur_rule as iterator, but *pp_rule still points * to currently processing rule entry. * NOTE: *pp_rule points to the current (and last in the list) rule. */ for (cur_rule = G.log_rules; cur_rule != *pp_rule; cur_rule = cur_rule->next) { if (strcmp(cur_rule->file->path, tok[1]) == 0) { /* found - reuse the same file structure */ (*pp_rule)->file = cur_rule->file; cur_rule = *pp_rule; goto found; } } cur_rule->file = xzalloc(sizeof(*cur_rule->file)); cur_rule->file->fd = -1; cur_rule->file->path = xstrdup(tok[1]); found: pp_rule = &cur_rule->next; } config_close(parser); return; cfgerr: bb_error_msg_and_die("error in '%s' at line %d", file ? file : "/etc/syslog.conf", parser->lineno); } #endif /* circular buffer variables/structures */ #if ENABLE_FEATURE_IPC_SYSLOG #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4 #error Sorry, you must set the syslogd buffer size to at least 4KB. #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE #endif /* our shared key (syslogd.c and logread.c must be in sync) */ enum { KEY_ID = 0x414e4547 }; /* "GENA" */ static void ipcsyslog_cleanup(void) { if (G.shmid != -1) { shmdt(G.shbuf); } if (G.shmid != -1) { shmctl(G.shmid, IPC_RMID, NULL); } if (G.s_semid != -1) { semctl(G.s_semid, 0, IPC_RMID, 0); } } static void ipcsyslog_init(void) { if (DEBUG) printf("shmget(%x, %d,...)\n", (int)KEY_ID, G.shm_size); G.shmid = shmget(KEY_ID, G.shm_size, IPC_CREAT | 0644); if (G.shmid == -1) { bb_perror_msg_and_die("shmget"); } G.shbuf = shmat(G.shmid, NULL, 0); if (G.shbuf == (void*) -1L) { /* shmat has bizarre error return */ bb_perror_msg_and_die("shmat"); } memset(G.shbuf, 0, G.shm_size); G.shbuf->size = G.shm_size - offsetof(struct shbuf_ds, data) - 1; /*G.shbuf->tail = 0;*/ /* we'll trust the OS to set initial semval to 0 (let's hope) */ G.s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023); if (G.s_semid == -1) { if (errno == EEXIST) { G.s_semid = semget(KEY_ID, 2, 0); if (G.s_semid != -1) return; } bb_perror_msg_and_die("semget"); } } /* Write message to shared mem buffer */ static void log_to_shmem(const char *msg) { int old_tail, new_tail; int len; if (semop(G.s_semid, G.SMwdn, 3) == -1) { bb_perror_msg_and_die("SMwdn"); } /* Circular Buffer Algorithm: * -------------------------- * tail == position where to store next syslog message. * tail's max value is (shbuf->size - 1) * Last byte of buffer is never used and remains NUL. */ len = strlen(msg) + 1; /* length with NUL included */ again: old_tail = G.shbuf->tail; new_tail = old_tail + len; if (new_tail < G.shbuf->size) { /* store message, set new tail */ memcpy(G.shbuf->data + old_tail, msg, len); G.shbuf->tail = new_tail; } else { /* k == available buffer space ahead of old tail */ int k = G.shbuf->size - old_tail; /* copy what fits to the end of buffer, and repeat */ memcpy(G.shbuf->data + old_tail, msg, k); msg += k; len -= k; G.shbuf->tail = 0; goto again; } if (semop(G.s_semid, G.SMwup, 1) == -1) { bb_perror_msg_and_die("SMwup"); } if (DEBUG) printf("tail:%d\n", G.shbuf->tail); } #else static void ipcsyslog_cleanup(void) {} static void ipcsyslog_init(void) {} void log_to_shmem(const char *msg); #endif /* FEATURE_IPC_SYSLOG */ #if ENABLE_FEATURE_KMSG_SYSLOG static void kmsg_init(void) { G.kmsgfd = xopen("/dev/kmsg", O_WRONLY); /* * kernel < 3.5 expects single char printk KERN_* priority prefix, * from 3.5 onwards the full syslog facility/priority format is supported */ if (get_linux_version_code() < KERNEL_VERSION(3,5,0)) G.primask = LOG_PRIMASK; else G.primask = -1; } static void kmsg_cleanup(void) { if (ENABLE_FEATURE_CLEAN_UP) close(G.kmsgfd); } /* Write message to /dev/kmsg */ static void log_to_kmsg(int pri, const char *msg) { /* * kernel < 3.5 expects single char printk KERN_* priority prefix, * from 3.5 onwards the full syslog facility/priority format is supported */ pri &= G.primask; write(G.kmsgfd, G.printbuf, sprintf(G.printbuf, "<%d>%s\n", pri, msg)); } #else static void kmsg_init(void) {} static void kmsg_cleanup(void) {} static void log_to_kmsg(int pri UNUSED_PARAM, const char *msg UNUSED_PARAM) {} #endif /* FEATURE_KMSG_SYSLOG */ /* Print a message to the log file. */ static void log_locally(time_t now, char *msg, logFile_t *log_file) { #ifdef SYSLOGD_WRLOCK struct flock fl; #endif int len = strlen(msg); if (log_file->fd >= 0) { /* Reopen log file every second. This allows admin * to delete the file and not worry about restarting us. * This costs almost nothing since it happens * _at most_ once a second. */ if (!now) now = time(NULL); if (G.last_log_time != now) { G.last_log_time = now; close(log_file->fd); goto reopen; } } else { reopen: log_file->fd = open(log_file->path, O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND | O_NONBLOCK, 0666); if (log_file->fd < 0) { /* cannot open logfile? - print to /dev/console then */ int fd = device_open(DEV_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK); if (fd < 0) fd = 2; /* then stderr, dammit */ full_write(fd, msg, len); if (fd != 2) close(fd); return; } #if ENABLE_FEATURE_ROTATE_LOGFILE { struct stat statf; log_file->isRegular = (fstat(log_file->fd, &statf) == 0 && S_ISREG(statf.st_mode)); /* bug (mostly harmless): can wrap around if file > 4gb */ log_file->size = statf.st_size; } #endif } #ifdef SYSLOGD_WRLOCK fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 1; fl.l_type = F_WRLCK; fcntl(log_file->fd, F_SETLKW, &fl); #endif #if ENABLE_FEATURE_ROTATE_LOGFILE if (G.logFileSize && log_file->isRegular && log_file->size > G.logFileSize) { if (G.logFileRotate) { /* always 0..99 */ int i = strlen(log_file->path) + 3 + 1; char oldFile[i]; char newFile[i]; i = G.logFileRotate - 1; /* rename: f.8 -> f.9; f.7 -> f.8; ... */ while (1) { sprintf(newFile, "%s.%d", log_file->path, i); if (i == 0) break; sprintf(oldFile, "%s.%d", log_file->path, --i); /* ignore errors - file might be missing */ rename(oldFile, newFile); } /* newFile == "f.0" now */ rename(log_file->path, newFile); /* Incredibly, if F and F.0 are hardlinks, POSIX * _demands_ that rename returns 0 but does not * remove F!!! * (hardlinked F/F.0 pair was observed after * power failure during rename()). * Ensure old file is gone: */ unlink(log_file->path); #ifdef SYSLOGD_WRLOCK fl.l_type = F_UNLCK; fcntl(log_file->fd, F_SETLKW, &fl); #endif close(log_file->fd); goto reopen; } ftruncate(log_file->fd, 0); } log_file->size += #endif full_write(log_file->fd, msg, len); #ifdef SYSLOGD_WRLOCK fl.l_type = F_UNLCK; fcntl(log_file->fd, F_SETLKW, &fl); #endif } static void parse_fac_prio_20(int pri, char *res20) { const CODE *c_pri, *c_fac; c_fac = find_by_val(LOG_FAC(pri) << 3, facilitynames); if (c_fac) { c_pri = find_by_val(LOG_PRI(pri), prioritynames); if (c_pri) { snprintf(res20, 20, "%s.%s", c_fac->c_name, c_pri->c_name); return; } } snprintf(res20, 20, "<%d>", pri); } /* len parameter is used only for "is there a timestamp?" check. * NB: some callers cheat and supply len==0 when they know * that there is no timestamp, short-circuiting the test. */ static void timestamp_and_log(int pri, char *msg, int len) { char *timestamp; time_t now; /* Jan 18 00:11:22 msg... */ /* 01234567890123456 */ if (len < 16 || msg[3] != ' ' || msg[6] != ' ' || msg[9] != ':' || msg[12] != ':' || msg[15] != ' ' ) { time(&now); timestamp = ctime(&now) + 4; /* skip day of week */ } else { now = 0; timestamp = msg; msg += 16; } timestamp[15] = '\0'; if (option_mask32 & OPT_kmsg) { log_to_kmsg(pri, msg); return; } if (option_mask32 & OPT_small) sprintf(G.printbuf, "%s %s\n", timestamp, msg); else { char res[20]; parse_fac_prio_20(pri, res); sprintf(G.printbuf, "%s %.64s %s %s\n", timestamp, G.hostname, res, msg); } /* Log message locally (to file or shared mem) */ #if ENABLE_FEATURE_SYSLOGD_CFG { bool match = 0; logRule_t *rule; uint8_t facility = LOG_FAC(pri); uint8_t prio_bit = 1 << LOG_PRI(pri); for (rule = G.log_rules; rule; rule = rule->next) { if (rule->enabled_facility_priomap[facility] & prio_bit) { log_locally(now, G.printbuf, rule->file); match = 1; } } if (match) return; } #endif if (LOG_PRI(pri) < G.logLevel) { #if ENABLE_FEATURE_IPC_SYSLOG if ((option_mask32 & OPT_circularlog) && G.shbuf) { log_to_shmem(G.printbuf); return; } #endif log_locally(now, G.printbuf, &G.logFile); } } static void timestamp_and_log_internal(const char *msg) { /* -L, or no -R */ if (ENABLE_FEATURE_REMOTE_LOG && !(option_mask32 & OPT_locallog)) return; timestamp_and_log(LOG_SYSLOG | LOG_INFO, (char*)msg, 0); } /* tmpbuf[len] is a NUL byte (set by caller), but there can be other, * embedded NULs. Split messages on each of these NULs, parse prio, * escape control chars and log each locally. */ static void split_escape_and_log(char *tmpbuf, int len) { char *p = tmpbuf; tmpbuf += len; while (p < tmpbuf) { char c; char *q = G.parsebuf; int pri = (LOG_USER | LOG_NOTICE); if (*p == '<') { /* Parse the magic priority number */ pri = bb_strtou(p + 1, &p, 10); if (*p == '>') p++; if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) pri = (LOG_USER | LOG_NOTICE); } while ((c = *p++)) { if (c == '\n') c = ' '; if (!(c & ~0x1f) && c != '\t') { *q++ = '^'; c += '@'; /* ^@, ^A, ^B... */ } *q++ = c; } *q = '\0'; /* Now log it */ timestamp_and_log(pri, G.parsebuf, q - G.parsebuf); } } #ifdef SYSLOGD_MARK static void do_mark(int sig) { if (G.markInterval) { timestamp_and_log_internal("-- MARK --"); alarm(G.markInterval); } } #endif /* Don't inline: prevent struct sockaddr_un to take up space on stack * permanently */ static NOINLINE int create_socket(void) { struct sockaddr_un sunx; int sock_fd; char *dev_log_name; #if ENABLE_FEATURE_SYSTEMD if (sd_listen_fds() == 1) return SD_LISTEN_FDS_START; #endif memset(&sunx, 0, sizeof(sunx)); sunx.sun_family = AF_UNIX; /* Unlink old /dev/log or object it points to. */ /* (if it exists, bind will fail) */ strcpy(sunx.sun_path, _PATH_LOG); dev_log_name = xmalloc_follow_symlinks(_PATH_LOG); if (dev_log_name) { safe_strncpy(sunx.sun_path, dev_log_name, sizeof(sunx.sun_path)); free(dev_log_name); } unlink(sunx.sun_path); sock_fd = xsocket(AF_UNIX, SOCK_DGRAM, 0); xbind(sock_fd, (struct sockaddr *) &sunx, sizeof(sunx)); chmod(_PATH_LOG, 0666); return sock_fd; } #if ENABLE_FEATURE_REMOTE_LOG static int try_to_resolve_remote(remoteHost_t *rh) { if (!rh->remoteAddr) { unsigned now = monotonic_sec(); /* Don't resolve name too often - DNS timeouts can be big */ if ((now - rh->last_dns_resolve) < DNS_WAIT_SEC) return -1; rh->last_dns_resolve = now; rh->remoteAddr = host2sockaddr(rh->remoteHostname, 514); if (!rh->remoteAddr) return -1; } return xsocket(rh->remoteAddr->u.sa.sa_family, SOCK_DGRAM, 0); } #endif static void do_syslogd(void) NORETURN; static void do_syslogd(void) { int sock_fd; #if ENABLE_FEATURE_REMOTE_LOG llist_t *item; #endif #if ENABLE_FEATURE_SYSLOGD_DUP int last_sz = -1; char *last_buf; char *recvbuf = G.recvbuf; #else #define recvbuf (G.recvbuf) #endif /* Set up signal handlers (so that they interrupt read()) */ signal_no_SA_RESTART_empty_mask(SIGTERM, record_signo); signal_no_SA_RESTART_empty_mask(SIGINT, record_signo); //signal_no_SA_RESTART_empty_mask(SIGQUIT, record_signo); signal(SIGHUP, SIG_IGN); #ifdef SYSLOGD_MARK signal(SIGALRM, do_mark); alarm(G.markInterval); #endif sock_fd = create_socket(); if (option_mask32 & OPT_circularlog) ipcsyslog_init(); if (option_mask32 & OPT_kmsg) kmsg_init(); timestamp_and_log_internal("syslogd started: BusyBox v" BB_VER); while (!bb_got_signal) { ssize_t sz; #if ENABLE_FEATURE_SYSLOGD_DUP last_buf = recvbuf; if (recvbuf == G.recvbuf) recvbuf = G.recvbuf + MAX_READ; else recvbuf = G.recvbuf; #endif read_again: sz = read(sock_fd, recvbuf, MAX_READ - 1); if (sz < 0) { if (!bb_got_signal) bb_perror_msg("read from %s", _PATH_LOG); break; } /* Drop trailing '\n' and NULs (typically there is one NUL) */ while (1) { if (sz == 0) goto read_again; /* man 3 syslog says: "A trailing newline is added when needed". * However, neither glibc nor uclibc do this: * syslog(prio, "test") sends "test\0" to /dev/log, * syslog(prio, "test\n") sends "test\n\0". * IOW: newline is passed verbatim! * I take it to mean that it's syslogd's job * to make those look identical in the log files. */ if (recvbuf[sz-1] != '\0' && recvbuf[sz-1] != '\n') break; sz--; } #if ENABLE_FEATURE_SYSLOGD_DUP if ((option_mask32 & OPT_dup) && (sz == last_sz)) if (memcmp(last_buf, recvbuf, sz) == 0) continue; last_sz = sz; #endif #if ENABLE_FEATURE_REMOTE_LOG /* Stock syslogd sends it '\n'-terminated * over network, mimic that */ recvbuf[sz] = '\n'; /* We are not modifying log messages in any way before send */ /* Remote site cannot trust _us_ anyway and need to do validation again */ for (item = G.remoteHosts; item != NULL; item = item->link) { remoteHost_t *rh = (remoteHost_t *)item->data; if (rh->remoteFD == -1) { rh->remoteFD = try_to_resolve_remote(rh); if (rh->remoteFD == -1) continue; } /* Send message to remote logger. * On some errors, close and set remoteFD to -1 * so that DNS resolution is retried. */ if (sendto(rh->remoteFD, recvbuf, sz+1, MSG_DONTWAIT | MSG_NOSIGNAL, &(rh->remoteAddr->u.sa), rh->remoteAddr->len) == -1 ) { switch (errno) { case ECONNRESET: case ENOTCONN: /* paranoia */ case EPIPE: close(rh->remoteFD); rh->remoteFD = -1; free(rh->remoteAddr); rh->remoteAddr = NULL; } } } #endif if (!ENABLE_FEATURE_REMOTE_LOG || (option_mask32 & OPT_locallog)) { recvbuf[sz] = '\0'; /* ensure it *is* NUL terminated */ split_escape_and_log(recvbuf, sz); } } /* while (!bb_got_signal) */ timestamp_and_log_internal("syslogd exiting"); puts("syslogd exiting"); remove_pidfile(CONFIG_PID_FILE_PATH "/syslogd.pid"); ipcsyslog_cleanup(); if (option_mask32 & OPT_kmsg) kmsg_cleanup(); kill_myself_with_sig(bb_got_signal); #undef recvbuf } int syslogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int syslogd_main(int argc UNUSED_PARAM, char **argv) { int opts; char OPTION_DECL; #if ENABLE_FEATURE_REMOTE_LOG llist_t *remoteAddrList = NULL; #endif INIT_G(); /* No non-option params, -R can occur multiple times */ opt_complementary = "=0" IF_FEATURE_REMOTE_LOG(":R::"); opts = getopt32(argv, OPTION_STR, OPTION_PARAM); #if ENABLE_FEATURE_REMOTE_LOG while (remoteAddrList) { remoteHost_t *rh = xzalloc(sizeof(*rh)); rh->remoteHostname = llist_pop(&remoteAddrList); rh->remoteFD = -1; rh->last_dns_resolve = monotonic_sec() - DNS_WAIT_SEC - 1; llist_add_to(&G.remoteHosts, rh); } #endif #ifdef SYSLOGD_MARK if (opts & OPT_mark) // -m G.markInterval = xatou_range(opt_m, 0, INT_MAX/60) * 60; #endif //if (opts & OPT_nofork) // -n //if (opts & OPT_outfile) // -O if (opts & OPT_loglevel) // -l G.logLevel = xatou_range(opt_l, 1, 8); //if (opts & OPT_small) // -S #if ENABLE_FEATURE_ROTATE_LOGFILE if (opts & OPT_filesize) // -s G.logFileSize = xatou_range(opt_s, 0, INT_MAX/1024) * 1024; if (opts & OPT_rotatecnt) // -b G.logFileRotate = xatou_range(opt_b, 0, 99); #endif #if ENABLE_FEATURE_IPC_SYSLOG if (opt_C) // -Cn G.shm_size = xatoul_range(opt_C, 4, INT_MAX/1024) * 1024; #endif /* If they have not specified remote logging, then log locally */ if (ENABLE_FEATURE_REMOTE_LOG && !(opts & OPT_remotelog)) // -R option_mask32 |= OPT_locallog; #if ENABLE_FEATURE_SYSLOGD_CFG parse_syslogdcfg(opt_f); #endif /* Store away localhost's name before the fork */ G.hostname = safe_gethostname(); *strchrnul(G.hostname, '.') = '\0'; if (!(opts & OPT_nofork)) { bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); } //umask(0); - why?? write_pidfile(CONFIG_PID_FILE_PATH "/syslogd.pid"); do_syslogd(); /* return EXIT_SUCCESS; */ } /* Clean up. Needed because we are included from syslogd_and_logger.c */ #undef DEBUG #undef SYSLOGD_MARK #undef SYSLOGD_WRLOCK #undef G #undef GLOBALS #undef INIT_G #undef OPTION_STR #undef OPTION_DECL #undef OPTION_PARAM ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/sysklogd/Config.src������������������������������������������������������������������0000644�0000000�0000000�00000012031�12263563520�015544� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "System Logging Utilities" INSERT config SYSLOGD bool "syslogd" default y help The syslogd utility is used to record logs of all the significant events that occur on a system. Every message that is logged records the date and time of the event, and will generally also record the name of the application that generated the message. When used in conjunction with klogd, messages from the Linux kernel can also be recorded. This is terribly useful, especially for finding what happened when something goes wrong. And something almost always will go wrong if you wait long enough.... config FEATURE_ROTATE_LOGFILE bool "Rotate message files" default y depends on SYSLOGD help This enables syslogd to rotate the message files on his own. No need to use an external rotatescript. config FEATURE_REMOTE_LOG bool "Remote Log support" default y depends on SYSLOGD help When you enable this feature, the syslogd utility can be used to send system log messages to another system connected via a network. This allows the remote machine to log all the system messages, which can be terribly useful for reducing the number of serial cables you use. It can also be a very good security measure to prevent system logs from being tampered with by an intruder. config FEATURE_SYSLOGD_DUP bool "Support -D (drop dups) option" default y depends on SYSLOGD help Option -D instructs syslogd to drop consecutive messages which are totally the same. config FEATURE_SYSLOGD_CFG bool "Support syslog.conf" default y depends on SYSLOGD help Supports restricted syslogd config. See docs/syslog.conf.txt config FEATURE_SYSLOGD_READ_BUFFER_SIZE int "Read buffer size in bytes" default 256 range 256 20000 depends on SYSLOGD help This option sets the size of the syslog read buffer. Actual memory usage increases around five times the change done here. config FEATURE_IPC_SYSLOG bool "Circular Buffer support" default y depends on SYSLOGD help When you enable this feature, the syslogd utility will use a circular buffer to record system log messages. When the buffer is filled it will continue to overwrite the oldest messages. This can be very useful for systems with little or no permanent storage, since otherwise system logs can eventually fill up your entire filesystem, which may cause your system to break badly. config FEATURE_IPC_SYSLOG_BUFFER_SIZE int "Circular buffer size in Kbytes (minimum 4KB)" default 16 range 4 2147483647 depends on FEATURE_IPC_SYSLOG help This option sets the size of the circular buffer used to record system log messages. config LOGREAD bool "logread" default y depends on FEATURE_IPC_SYSLOG help If you enabled Circular Buffer support, you almost certainly want to enable this feature as well. This utility will allow you to read the messages that are stored in the syslogd circular buffer. config FEATURE_LOGREAD_REDUCED_LOCKING bool "Double buffering" default y depends on LOGREAD help 'logread' ouput to slow serial terminals can have side effects on syslog because of the semaphore. This option make logread to double buffer copy from circular buffer, minimizing semaphore contention at some minor memory expense. config FEATURE_KMSG_SYSLOG bool "Linux kernel printk buffer support" default y depends on SYSLOGD select PLATFORM_LINUX help When you enable this feature, the syslogd utility will write system log message to the Linux kernel's printk buffer. This can be used as a smaller alternative to the syslogd IPC support, as klogd and logread aren't needed. NOTICE: Syslog facilities in log entries needs kernel 3.5+. config KLOGD bool "klogd" default y help klogd is a utility which intercepts and logs all messages from the Linux kernel and sends the messages out to the 'syslogd' utility so they can be logged. If you wish to record the messages produced by the kernel, you should enable this option. comment "klogd should not be used together with syslog to kernel printk buffer" depends on KLOGD && FEATURE_KMSG_SYSLOG config FEATURE_KLOGD_KLOGCTL bool "Use the klogctl() interface" default y depends on KLOGD select PLATFORM_LINUX help The klogd applet supports two interfaces for reading kernel messages. Linux provides the klogctl() interface which allows reading messages from the kernel ring buffer independently from the file system. If you answer 'N' here, klogd will use the more portable approach of reading them from /proc or a device node. However, this method requires the file to be available. If in doubt, say 'Y'. config LOGGER bool "logger" default y select FEATURE_SYSLOG help The logger utility allows you to send arbitrary text messages to the system log (i.e. the 'syslogd' utility) so they can be logged. This is generally used to help locate problems that occur within programs and scripts. endmenu �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/sysklogd/Kbuild.src������������������������������������������������������������������0000644�0000000�0000000�00000000516�12263563520�015556� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org> # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT lib-$(CONFIG_KLOGD) += klogd.o lib-$(CONFIG_LOGGER) += syslogd_and_logger.o lib-$(CONFIG_LOGREAD) += logread.o lib-$(CONFIG_SYSLOGD) += syslogd_and_logger.o ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/sysklogd/logread.c�������������������������������������������������������������������0000644�0000000�0000000�00000011715�12263563520�015417� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * circular buffer syslog implementation for busybox * * Copyright (C) 2000 by Gennady Feldman <gfeldman@gena01.com> * * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001 * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define logread_trivial_usage //usage: "[-f]" //usage:#define logread_full_usage "\n\n" //usage: "Show messages in syslogd's circular buffer\n" //usage: "\n -f Output data as log grows" #include "libbb.h" #include <sys/ipc.h> #include <sys/sem.h> #include <sys/shm.h> #define DEBUG 0 /* our shared key (syslogd.c and logread.c must be in sync) */ enum { KEY_ID = 0x414e4547 }; /* "GENA" */ struct shbuf_ds { int32_t size; // size of data - 1 int32_t tail; // end of message list char data[1]; // messages }; static const struct sembuf init_sem[3] = { {0, -1, IPC_NOWAIT | SEM_UNDO}, {1, 0}, {0, +1, SEM_UNDO} }; struct globals { struct sembuf SMrup[1]; // {0, -1, IPC_NOWAIT | SEM_UNDO}, struct sembuf SMrdn[2]; // {1, 0}, {0, +1, SEM_UNDO} struct shbuf_ds *shbuf; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define SMrup (G.SMrup) #define SMrdn (G.SMrdn) #define shbuf (G.shbuf) #define INIT_G() do { \ memcpy(SMrup, init_sem, sizeof(init_sem)); \ } while (0) #if 0 static void error_exit(const char *str) NORETURN; static void error_exit(const char *str) { /* Release all acquired resources */ shmdt(shbuf); bb_perror_msg_and_die(str); } #else /* On Linux, shmdt is not mandatory on exit */ # define error_exit(str) bb_perror_msg_and_die(str) #endif /* * sem_up - up()'s a semaphore. */ static void sem_up(int semid) { if (semop(semid, SMrup, 1) == -1) error_exit("semop[SMrup]"); } static void interrupted(int sig) { /* shmdt(shbuf); - on Linux, shmdt is not mandatory on exit */ kill_myself_with_sig(sig); } int logread_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int logread_main(int argc UNUSED_PARAM, char **argv) { unsigned cur; int log_semid; /* ipc semaphore id */ int log_shmid; /* ipc shared memory id */ smallint follow = getopt32(argv, "f"); INIT_G(); log_shmid = shmget(KEY_ID, 0, 0); if (log_shmid == -1) bb_perror_msg_and_die("can't %s syslogd buffer", "find"); /* Attach shared memory to our char* */ shbuf = shmat(log_shmid, NULL, SHM_RDONLY); if (shbuf == NULL) bb_perror_msg_and_die("can't %s syslogd buffer", "access"); log_semid = semget(KEY_ID, 0, 0); if (log_semid == -1) error_exit("can't get access to semaphores for syslogd buffer"); bb_signals(BB_FATAL_SIGS, interrupted); /* Suppose atomic memory read */ /* Max possible value for tail is shbuf->size - 1 */ cur = shbuf->tail; /* Loop for logread -f, one pass if there was no -f */ do { unsigned shbuf_size; unsigned shbuf_tail; const char *shbuf_data; #if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING int i; int len_first_part; int len_total = len_total; /* for gcc */ char *copy = copy; /* for gcc */ #endif if (semop(log_semid, SMrdn, 2) == -1) error_exit("semop[SMrdn]"); /* Copy the info, helps gcc to realize that it doesn't change */ shbuf_size = shbuf->size; shbuf_tail = shbuf->tail; shbuf_data = shbuf->data; /* pointer! */ if (DEBUG) printf("cur:%u tail:%u size:%u\n", cur, shbuf_tail, shbuf_size); if (!follow) { /* advance to oldest complete message */ /* find NUL */ cur += strlen(shbuf_data + cur); if (cur >= shbuf_size) { /* last byte in buffer? */ cur = strnlen(shbuf_data, shbuf_tail); if (cur == shbuf_tail) goto unlock; /* no complete messages */ } /* advance to first byte of the message */ cur++; if (cur >= shbuf_size) /* last byte in buffer? */ cur = 0; } else { /* logread -f */ if (cur == shbuf_tail) { sem_up(log_semid); fflush_all(); sleep(1); /* TODO: replace me with a sleep_on */ continue; } } /* Read from cur to tail */ #if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING len_first_part = len_total = shbuf_tail - cur; if (len_total < 0) { /* message wraps: */ /* [SECOND PART.........FIRST PART] */ /* ^data ^tail ^cur ^size */ len_total += shbuf_size; } copy = xmalloc(len_total + 1); if (len_first_part < 0) { /* message wraps (see above) */ len_first_part = shbuf_size - cur; memcpy(copy + len_first_part, shbuf_data, shbuf_tail); } memcpy(copy, shbuf_data + cur, len_first_part); copy[len_total] = '\0'; cur = shbuf_tail; #else while (cur != shbuf_tail) { fputs(shbuf_data + cur, stdout); cur += strlen(shbuf_data + cur) + 1; if (cur >= shbuf_size) cur = 0; } #endif unlock: /* release the lock on the log chain */ sem_up(log_semid); #if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING for (i = 0; i < len_total; i += strlen(copy + i) + 1) { fputs(copy + i, stdout); } free(copy); #endif fflush_all(); } while (follow); /* shmdt(shbuf); - on Linux, shmdt is not mandatory on exit */ fflush_stdout_and_exit(EXIT_SUCCESS); } ���������������������������������������������������busybox-1.22.1/sysklogd/syslogd_and_logger.c��������������������������������������������������������0000644�0000000�0000000�00000002254�12263563520�017645� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * prioritynames[] and facilitynames[] * * Copyright (C) 2008 by Denys Vlasenko <vda.linux@gmail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" #define SYSLOG_NAMES #define SYSLOG_NAMES_CONST #include <syslog.h> #if 0 /* For the record: with SYSLOG_NAMES <syslog.h> defines * (not declares) the following: */ typedef struct _code { /*const*/ char *c_name; int c_val; } CODE; /*const*/ CODE prioritynames[] = { { "alert", LOG_ALERT }, ... { NULL, -1 } }; /* same for facilitynames[] */ /* This MUST occur only once per entire executable, * therefore we can't just do it in syslogd.c and logger.c - * there will be two copies of it. * * We cannot even do it in separate file and then just reference * prioritynames[] from syslogd.c and logger.c - bare <syslog.h> * will not emit extern decls for prioritynames[]! Attempts to * emit "matching" struct _code declaration defeat the whole purpose * of <syslog.h>. * * For now, syslogd.c and logger.c are simply compiled into * one object file. */ #endif #if ENABLE_SYSLOGD #include "syslogd.c" #endif #if ENABLE_LOGGER #include "logger.c" #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/sysklogd/klogd.c���������������������������������������������������������������������0000644�0000000�0000000�00000015473�12263563520�015107� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Mini klogd implementation for busybox * * Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>. * Changes: Made this a standalone busybox module which uses standalone * syslog() client interface. * * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> * * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org> * * "circular buffer" Copyright (C) 2000 by Gennady Feldman <gfeldman@gena01.com> * * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001 * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define klogd_trivial_usage //usage: "[-c N] [-n]" //usage:#define klogd_full_usage "\n\n" //usage: "Kernel logger\n" //usage: "\n -c N Print to console messages more urgent than prio N (1-8)" //usage: "\n -n Run in foreground" #include "libbb.h" #include <syslog.h> /* The Linux-specific klogctl(3) interface does not rely on the filesystem and * allows us to change the console loglevel. Alternatively, we read the * messages from _PATH_KLOG. */ #if ENABLE_FEATURE_KLOGD_KLOGCTL # include <sys/klog.h> static void klogd_open(void) { /* "Open the log. Currently a NOP" */ klogctl(1, NULL, 0); } static void klogd_setloglevel(int lvl) { /* "printk() prints a message on the console only if it has a loglevel * less than console_loglevel". Here we set console_loglevel = lvl. */ klogctl(8, NULL, lvl); } static int klogd_read(char *bufp, int len) { return klogctl(2, bufp, len); } # define READ_ERROR "klogctl(2) error" static void klogd_close(void) { /* FYI: cmd 7 is equivalent to setting console_loglevel to 7 * via klogctl(8, NULL, 7). */ klogctl(7, NULL, 0); /* "7 -- Enable printk's to console" */ klogctl(0, NULL, 0); /* "0 -- Close the log. Currently a NOP" */ } #else # include <paths.h> # ifndef _PATH_KLOG # ifdef __GNU__ # define _PATH_KLOG "/dev/klog" # else # error "your system's _PATH_KLOG is unknown" # endif # endif # define PATH_PRINTK "/proc/sys/kernel/printk" enum { klogfd = 3 }; static void klogd_open(void) { int fd = xopen(_PATH_KLOG, O_RDONLY); xmove_fd(fd, klogfd); } static void klogd_setloglevel(int lvl) { FILE *fp = fopen_or_warn(PATH_PRINTK, "w"); if (fp) { /* This changes only first value: * "messages with a higher priority than this * [that is, with numerically lower value] * will be printed to the console". * The other three values in this pseudo-file aren't changed. */ fprintf(fp, "%u\n", lvl); fclose(fp); } } static int klogd_read(char *bufp, int len) { return read(klogfd, bufp, len); } # define READ_ERROR "read error" static void klogd_close(void) { klogd_setloglevel(7); if (ENABLE_FEATURE_CLEAN_UP) close(klogfd); } #endif #define log_buffer bb_common_bufsiz1 enum { KLOGD_LOGBUF_SIZE = sizeof(log_buffer), OPT_LEVEL = (1 << 0), OPT_FOREGROUND = (1 << 1), }; /* TODO: glibc openlog(LOG_KERN) reverts to LOG_USER instead, * because that's how they interpret word "default" * in the openlog() manpage: * LOG_USER (default) * generic user-level messages * and the fact that LOG_KERN is a constant 0. * glibc interprets it as "0 in openlog() call means 'use default'". * I think it means "if openlog wasn't called before syslog() is called, * use default". * Convincing glibc maintainers otherwise is, as usual, nearly impossible. * Should we open-code syslog() here to use correct facility? */ int klogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int klogd_main(int argc UNUSED_PARAM, char **argv) { int i = 0; char *opt_c; int opt; int used; opt = getopt32(argv, "c:n", &opt_c); if (opt & OPT_LEVEL) { /* Valid levels are between 1 and 8 */ i = xatou_range(opt_c, 1, 8); } if (!(opt & OPT_FOREGROUND)) { bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); } logmode = LOGMODE_SYSLOG; /* klogd_open() before openlog(), since it might use fixed fd 3, * and openlog() also may use the same fd 3 if we swap them: */ klogd_open(); openlog("kernel", 0, LOG_KERN); /* * glibc problem: for some reason, glibc changes LOG_KERN to LOG_USER * above. The logic behind this is that standard * http://pubs.opengroup.org/onlinepubs/9699919799/functions/syslog.html * says the following about openlog and syslog: * "LOG_USER * Messages generated by arbitrary processes. * This is the default facility identifier if none is specified." * * I believe glibc misinterpreted this text as "if openlog's * third parameter is 0 (=LOG_KERN), treat it as LOG_USER". * Whereas it was meant to say "if *syslog* is called with facility * 0 in its 1st parameter without prior call to openlog, then perform * implicit openlog(LOG_USER)". * * As a result of this, eh, feature, standard klogd was forced * to open-code its own openlog and syslog implementation (!). * * Note that prohibiting openlog(LOG_KERN) on libc level does not * add any security: any process can open a socket to "/dev/log" * and write a string "<0>Voila, a LOG_KERN + LOG_EMERG message" * * Google code search tells me there is no widespread use of * openlog("foo", 0, 0), thus fixing glibc won't break userspace. * * The bug against glibc was filed: * bugzilla.redhat.com/show_bug.cgi?id=547000 */ if (i) klogd_setloglevel(i); signal(SIGHUP, SIG_IGN); /* We want klogd_read to not be restarted, thus _norestart: */ bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo); syslog(LOG_NOTICE, "klogd started: %s", bb_banner); write_pidfile(CONFIG_PID_FILE_PATH "/klogd.pid"); used = 0; while (!bb_got_signal) { int n; int priority; char *start; /* "2 -- Read from the log." */ start = log_buffer + used; n = klogd_read(start, KLOGD_LOGBUF_SIZE-1 - used); if (n < 0) { if (errno == EINTR) continue; bb_perror_msg(READ_ERROR); break; } start[n] = '\0'; /* Process each newline-terminated line in the buffer */ start = log_buffer; while (1) { char *newline = strchrnul(start, '\n'); if (*newline == '\0') { /* This line is incomplete */ /* move it to the front of the buffer */ overlapping_strcpy(log_buffer, start); used = newline - start; if (used < KLOGD_LOGBUF_SIZE-1) { /* buffer isn't full */ break; } /* buffer is full, log it anyway */ used = 0; newline = NULL; } else { *newline++ = '\0'; } /* Extract the priority */ priority = LOG_INFO; if (*start == '<') { start++; if (*start) priority = strtoul(start, &start, 10); if (*start == '>') start++; } /* Log (only non-empty lines) */ if (*start) syslog(priority, "%s", start); if (!newline) break; start = newline; } } klogd_close(); syslog(LOG_NOTICE, "klogd: exiting"); remove_pidfile(CONFIG_PID_FILE_PATH "/klogd.pid"); if (bb_got_signal) kill_myself_with_sig(bb_got_signal); return EXIT_FAILURE; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/sysklogd/logger.c��������������������������������������������������������������������0000644�0000000�0000000�00000011566�12263563520�015265� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Mini logger implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define logger_trivial_usage //usage: "[OPTIONS] [MESSAGE]" //usage:#define logger_full_usage "\n\n" //usage: "Write MESSAGE (or stdin) to syslog\n" //usage: "\n -s Log to stderr as well as the system log" //usage: "\n -t TAG Log using the specified tag (defaults to user name)" //usage: "\n -p PRIO Priority (numeric or facility.level pair)" //usage: //usage:#define logger_example_usage //usage: "$ logger \"hello\"\n" /* * Done in syslogd_and_logger.c: #include "libbb.h" #define SYSLOG_NAMES #define SYSLOG_NAMES_CONST #include <syslog.h> */ /* Decode a symbolic name to a numeric value * this function is based on code * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Original copyright notice is retained at the end of this file. */ static int decode(char *name, const CODE *codetab) { const CODE *c; if (isdigit(*name)) return atoi(name); for (c = codetab; c->c_name; c++) { if (!strcasecmp(name, c->c_name)) { return c->c_val; } } return -1; } /* Decode a symbolic name to a numeric value * this function is based on code * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Original copyright notice is retained at the end of this file. */ static int pencode(char *s) { char *save; int lev, fac = LOG_USER; for (save = s; *s && *s != '.'; ++s) ; if (*s) { *s = '\0'; fac = decode(save, facilitynames); if (fac < 0) bb_error_msg_and_die("unknown %s name: %s", "facility", save); *s++ = '.'; } else { s = save; } lev = decode(s, prioritynames); if (lev < 0) bb_error_msg_and_die("unknown %s name: %s", "priority", save); return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK)); } #define strbuf bb_common_bufsiz1 int logger_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int logger_main(int argc UNUSED_PARAM, char **argv) { char *str_p, *str_t; int opt; int i = 0; /* Fill out the name string early (may be overwritten later) */ str_t = uid2uname_utoa(geteuid()); /* Parse any options */ opt = getopt32(argv, "p:st:", &str_p, &str_t); if (opt & 0x2) /* -s */ i |= LOG_PERROR; //if (opt & 0x4) /* -t */ openlog(str_t, i, 0); i = LOG_USER | LOG_NOTICE; if (opt & 0x1) /* -p */ i = pencode(str_p); argv += optind; if (!argv[0]) { while (fgets(strbuf, COMMON_BUFSIZE, stdin)) { if (strbuf[0] && NOT_LONE_CHAR(strbuf, '\n') ) { /* Neither "" nor "\n" */ syslog(i, "%s", strbuf); } } } else { char *message = NULL; int len = 0; int pos = 0; do { len += strlen(*argv) + 1; message = xrealloc(message, len + 1); sprintf(message + pos, " %s", *argv), pos = len; } while (*++argv); syslog(i, "%s", message + 1); /* skip leading " " */ } closelog(); return EXIT_SUCCESS; } /* Clean up. Needed because we are included from syslogd_and_logger.c */ #undef strbuf /*- * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * This is the original license statement for the decode and pencode functions. * * 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. BSD Advertising Clause omitted per the July 22, 1999 licensing change * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change * * 4. 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. */ ������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/arch/��������������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�012704� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/arch/i386/���������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�013375� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/arch/i386/Makefile�������������������������������������������������������������������0000644�0000000�0000000�00000000577�12263563520�015051� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# ========================================================================== # Build system # ========================================================================== # -mpreferred-stack-boundary=2 is essential in preventing gcc 4.2.x # from aligning stack to 16 bytes. (Which is gcc's way of supporting SSE). CFLAGS += $(call cc-option,-march=i386 -mpreferred-stack-boundary=2,) ���������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/INSTALL������������������������������������������������������������������������������0000644�0000000�0000000�00000013744�12263563520�013034� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Building: ========= The BusyBox build process is similar to the Linux kernel build: make menuconfig # This creates a file called ".config" make # This creates the "busybox" executable make install # or make CONFIG_PREFIX=/path/from/root install The full list of configuration and install options is available by typing: make help Quick Start: ============ The easy way to try out BusyBox for the first time, without having to install it, is to enable all features and then use "standalone shell" mode with a blank command $PATH. To enable all features, use "make defconfig", which produces the largest general-purpose configuration. It's allyesconfig minus debugging options, optional packaging choices, and a few special-purpose features requiring extra configuration to use. Then enable "standalone shell" feature: make defconfig make menuconfig # select Busybox Settings # then General Configuration # then exec prefers applets # exit back to top level menu # select Shells # then Standalone shell # exit back to top level menu # exit and save new configuration # OR # use these commands to modify .config directly: sed -e 's/.*FEATURE_PREFER_APPLETS.*/CONFIG_FEATURE_PREFER_APPLETS=y/' -i .config sed -e 's/.*FEATURE_SH_STANDALONE.*/CONFIG_FEATURE_SH_STANDALONE=y/' -i .config make PATH= ./busybox ash Standalone shell mode causes busybox's built-in command shell to run any built-in busybox applets directly, without looking for external programs by that name. Supplying an empty command path (as above) means the only commands busybox can find are the built-in ones. Note that the standalone shell requires CONFIG_BUSYBOX_EXEC_PATH to be set appropriately, depending on whether or not /proc/self/exe is available. If you do not have /proc, then point that config option to the location of your busybox binary, usually /bin/busybox. Another solution is to patch the kernel (see examples/linux-*_proc_self_exe.patch) to make exec("/proc/self/exe") always work. Configuring Busybox: ==================== Busybox is optimized for size, but enabling the full set of functionality still results in a fairly large executable -- more than 1 megabyte when statically linked. To save space, busybox can be configured with only the set of applets needed for each environment. The minimal configuration, with all applets disabled, produces a 4k executable. (It's useless, but very small.) The manual configurator "make menuconfig" modifies the existing configuration. (For systems without ncurses, try "make config" instead.) The two most interesting starting configurations are "make allnoconfig" (to start with everything disabled and add just what you need), and "make defconfig" (to start with everything enabled and remove what you don't need). If menuconfig is run without an existing configuration, make defconfig will run first to create a known starting point. Other starting configurations (mostly used for testing purposes) include "make allbareconfig" (enables all applets but disables all optional features), "make allyesconfig" (enables absolutely everything including debug features), and "make randconfig" (produce a random configuration). The configs/ directory contains a number of additional configuration files ending in _defconfig which are useful in specific cases. "make help" will list them. Configuring BusyBox produces a file ".config", which can be saved for future use. Run "make oldconfig" to bring a .config file from an older version of busybox up to date. Installing Busybox: =================== Busybox is a single executable that can behave like many different commands, and BusyBox uses the name it was invoked under to determine the desired behavior. (Try "mv busybox ls" and then "./ls -l".) Installing busybox consists of creating symlinks (or hardlinks) to the busybox binary for each applet enabled in busybox, and making sure these symlinks are in the shell's command $PATH. Running "make install" creates these symlinks, or "make install-hardlinks" creates hardlinks instead (useful on systems with a limited number of inodes). This install process uses the file "busybox.links" (created by make), which contains the list of enabled applets and the path at which to install them. Installing links to busybox is not always necessary. The special applet name "busybox" (or with any optional suffix, such as "busybox-static") uses the first argument to determine which applet to behave as, for example "./busybox cat LICENSE". (Running the busybox applet with no arguments gives a list of all enabled applets.) The standalone shell can also call busybox applets without links to busybox under other names in the filesystem. You can also configure a standalone install capability into the busybox base applet, and then install such links at runtime with one of "busybox --install" (for hardlinks) or "busybox --install -s" (for symlinks). If you enabled the busybox shared library feature (libbusybox.so) and want to run tests without installing, set your LD_LIBRARY_PATH accordingly when running the executable: LD_LIBRARY_PATH=`pwd` ./busybox Building out-of-tree: ===================== By default, the BusyBox build puts its temporary files in the source tree. Building from a read-only source tree, or building multiple configurations from the same source directory, requires the ability to put the temporary files somewhere else. To build out of tree, cd to an empty directory and configure busybox from there: make KBUILD_SRC=/path/to/source -f /path/to/source/Makefile defconfig make make install Alternately, use the O=$BUILDPATH option (with an absolute path) during the configuration step, as in: make O=/some/empty/directory allyesconfig cd /some/empty/directory make make CONFIG_PREFIX=. install More Information: ================= Se also the busybox FAQ, under the questions "How can I get started using BusyBox" and "How do I build a BusyBox-based system?" The BusyBox FAQ is available from http://www.busybox.net/FAQ.html ����������������������������busybox-1.22.1/testsuite/���������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�014020� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/patch.tests����������������������������������������������������������������0000755�0000000�0000000�00000006047�12263563520�016220� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2008 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "test name" "command(s)" "expected result" "file input" "stdin" testing "patch with old_file == new_file" \ 'patch 2>&1; echo $?; cat input' \ "\ patching file input 0 qwe asd zxc " \ "\ qwe zxc " \ "\ --- input Jan 01 01:01:01 2000 +++ input Jan 01 01:01:01 2000 @@ -1,2 +1,3 @@ qwe +asd zxc " \ testing "patch with nonexistent old_file" \ 'patch 2>&1; echo $?; cat input' \ "\ patching file input 0 qwe asd zxc " \ "\ qwe zxc " \ "\ --- input.doesnt_exist Jan 01 01:01:01 2000 +++ input Jan 01 01:01:01 2000 @@ -1,2 +1,3 @@ qwe +asd zxc " \ testing "patch -R with nonexistent old_file" \ 'patch -R 2>&1; echo $?; cat input' \ "\ patching file input 0 qwe zxc " \ "\ qwe asd zxc " \ "\ --- input.doesnt_exist Jan 01 01:01:01 2000 +++ input Jan 01 01:01:01 2000 @@ -1,2 +1,3 @@ qwe +asd zxc " \ testing "patch detects already applied hunk" \ 'patch 2>&1; echo $?; cat input' \ "\ Possibly reversed hunk 1 at 4 Hunk 1 FAILED 1/1. abc +def 123 patching file input 1 abc def 123 " \ "\ abc def 123 " \ "\ --- input.old Jan 01 01:01:01 2000 +++ input Jan 01 01:01:01 2000 @@ -1,2 +1,3 @@ abc +def 123 " \ testing "patch detects already applied hunk at the EOF" \ 'patch 2>&1; echo $?; cat input' \ "\ Possibly reversed hunk 1 at 4 Hunk 1 FAILED 1/1. abc 123 +456 patching file input 1 abc 123 456 " \ "\ abc 123 456 " \ "\ --- input.old Jan 01 01:01:01 2000 +++ input Jan 01 01:01:01 2000 @@ -1,2 +1,3 @@ abc 123 +456 " \ # testing "test name" "command(s)" "expected result" "file input" "stdin" testing "patch -N ignores already applied hunk" \ 'patch -N 2>&1; echo $?; cat input' \ "\ patching file input 0 abc def 123 " \ "\ abc def 123 " \ "\ --- input +++ input @@ -1,2 +1,3 @@ abc +def 123 " \ # testing "test name" "command(s)" "expected result" "file input" "stdin" testing "patch FILE PATCH" \ 'cat >a.patch; patch input a.patch 2>&1; echo $?; cat input; rm a.patch' \ "\ patching file input 0 abc def 123 " \ "\ abc 123 " \ "\ --- foo.old +++ foo @@ -1,2 +1,3 @@ abc +def 123 " \ # testing "test name" "command(s)" "expected result" "file input" "stdin" testing "patch at the beginning" \ 'patch 2>&1; cat input' \ "\ patching file input 111changed 444 555 666 777 888 999 " \ "\ 111 222 333 444 555 666 777 888 999 " \ "\ --- input +++ input @@ -1,6 +1,4 @@ -111 -222 -333 +111changed 444 555 666 " \ # testing "test name" "command(s)" "expected result" "file input" "stdin" testing "patch creates new file" \ 'patch 2>&1; echo $?; cat testfile; rm testfile' \ "\ creating testfile 0 qwerty " "" "\ --- /dev/null +++ testfile @@ -0,0 +1 @@ +qwerty " # testing "test name" "command(s)" "expected result" "file input" "stdin" testing "patch understands ...dir///dir..." \ 'patch -p1 2>&1; echo $?' \ "\ patching file dir2///file patch: can't open 'dir2///file': No such file or directory 1 " "" "\ --- bogus_dir///dir2///file +++ bogus_dir///dir2///file @@ -1,2 +1,3 @@ qwe +asd zxc " rm input.orig 2>/dev/null exit $FAILCOUNT �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/unexpand/������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�015642� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/unexpand/unexpand-works-like-GNU�������������������������������������������0000644�0000000�0000000�00000002322�12263563520�022125� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rm -f foo bar echo " y" | unexpand ../../busybox > foo echo " y" | busybox unexpand ../../busybox > bar set +e test ! -f foo -a -f bar if [ $? = 0 ] ; then set -e diff -q foo bar fi rm -f foo bar echo " y" | unexpand ../../busybox > foo echo " y" | busybox unexpand ../../busybox > bar set +e test ! -f foo -a -f bar if [ $? = 0 ] ; then set -e diff -q foo bar fi echo " y y" | unexpand ../../busybox > foo echo " y y" | busybox unexpand ../../busybox > bar set +e test ! -f foo -a -f bar if [ $? = 0 ] ; then set -e diff -q foo bar fi rm -f foo bar echo " y y" | unexpand ../../busybox > foo echo " y y" | busybox unexpand ../../busybox > bar set +e test ! -f foo -a -f bar if [ $? = 0 ] ; then set -e diff -q foo bar fi echo " y y" | unexpand -a ../../busybox > foo echo " y y" | busybox unexpand -a ../../busybox > bar set +e test ! -f foo -a -f bar if [ $? = 0 ] ; then set -e diff -q foo bar fi rm -f foo bar echo " y y" | unexpand -a ../../busybox > foo echo " y y" | busybox unexpand -a ../../busybox > bar set +e test ! -f foo -a -f bar if [ $? = 0 ] ; then set -e diff -q foo bar fi ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/sha256sum.tests������������������������������������������������������������0000755�0000000�0000000�00000000147�12263563520�016651� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh . ./md5sum.tests sha256sum 8e1d3ed57ebc130f0f72508446559eeae06451ae6d61b1e8ce46370cfb8963c3 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/diff.tests�����������������������������������������������������������������0000755�0000000�0000000�00000007150�12263563520�016025� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2008 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "test name" "commands" "expected result" "file input" "stdin" # diff outputs date/time in the header, which should not be analysed # NB: sed has tab character in s command! TRIM_TAB="sed 's/ .*//'" testing "diff of stdin" \ "diff -u - input | $TRIM_TAB" \ "\ --- - +++ input @@ -1 +1,3 @@ +qwe asd +zxc " \ "qwe\nasd\nzxc\n" \ "asd\n" testing "diff of stdin, no newline in the file" \ "diff -u - input | $TRIM_TAB" \ "\ --- - +++ input @@ -1 +1,3 @@ +qwe asd +zxc \\ No newline at end of file " \ "qwe\nasd\nzxc" \ "asd\n" # we also test that stdin is in fact NOT read testing "diff of stdin, twice" \ 'diff - -; echo $?; wc -c' \ "0\n5\n" \ "" \ "stdin" testing "diff of empty file against nonempty one" \ "diff -u - input | $TRIM_TAB" \ "\ --- - +++ input @@ -0,0 +1 @@ +a " \ "a\n" \ "" testing "diff -b treats EOF as whitespace" \ 'diff -ub - input; echo $?' \ "0\n" \ "abc" \ "abc " testing "diff -b treats all spaces as equal" \ 'diff -ub - input; echo $?' \ "0\n" \ "a \t c\n" \ "a\t \tc\n" testing "diff -B ignores changes whose lines are all blank" \ 'diff -uB - input; echo $?' \ "0\n" \ "a\n" \ "\na\n\n" testing "diff -B does not ignore changes whose lines are not all blank" \ "diff -uB - input | $TRIM_TAB" \ "\ --- - +++ input @@ -1,3 +1 @@ - -b - +a " \ "a\n" \ "\nb\n\n" testing "diff always takes context from old file" \ "diff -ub - input | $TRIM_TAB" \ "\ --- - +++ input @@ -1 +1,3 @@ +abc a c +def " \ "abc\na c\ndef\n" \ "a c\n" # testing "test name" "commands" "expected result" "file input" "stdin" # clean up rm -rf diff1 diff2 mkdir diff1 diff2 diff2/subdir echo qwe >diff1/- echo asd >diff2/subdir/- optional FEATURE_DIFF_DIR testing "diff diff1 diff2/subdir" \ "diff -ur diff1 diff2/subdir | $TRIM_TAB" \ "\ --- diff1/- +++ diff2/subdir/- @@ -1 +1 @@ -qwe +asd " \ "" "" SKIP= # using directory structure from prev test... optional FEATURE_DIFF_DIR testing "diff dir dir2/file/-" \ "diff -ur diff1 diff2/subdir/- | $TRIM_TAB" \ "\ --- diff1/- +++ diff2/subdir/- @@ -1 +1 @@ -qwe +asd " \ "" "" SKIP= # using directory structure from prev test... mkdir diff1/test mkfifo diff2/subdir/test optional FEATURE_DIFF_DIR testing "diff of dir and fifo" \ "diff -ur diff1 diff2/subdir | $TRIM_TAB" \ "\ --- diff1/- +++ diff2/subdir/- @@ -1 +1 @@ -qwe +asd Only in diff2/subdir: test " \ "" "" SKIP= # using directory structure from prev test... rmdir diff1/test echo >diff1/test optional FEATURE_DIFF_DIR testing "diff of file and fifo" \ "diff -ur diff1 diff2/subdir | $TRIM_TAB" \ "\ --- diff1/- +++ diff2/subdir/- @@ -1 +1 @@ -qwe +asd File diff2/subdir/test is not a regular file or directory and was skipped " \ "" "" SKIP= # using directory structure from prev test... mkfifo diff1/test2 optional FEATURE_DIFF_DIR testing "diff -rN does not read non-regular files" \ "diff -urN diff1 diff2/subdir | $TRIM_TAB" \ "\ --- diff1/- +++ diff2/subdir/- @@ -1 +1 @@ -qwe +asd File diff2/subdir/test is not a regular file or directory and was skipped File diff1/test2 is not a regular file or directory and was skipped " \ "" "" SKIP= # clean up rm -rf diff1 diff2 # NOT using directory structure from prev test... mkdir diff1 diff2 echo qwe >diff1/- echo rty >diff2/- optional FEATURE_DIFF_DIR testing "diff diff1 diff2/" \ "diff -ur diff1 diff2/ | $TRIM_TAB; diff -ur .///diff1 diff2//// | $TRIM_TAB" \ "\ --- diff1/- +++ diff2/- @@ -1 +1 @@ -qwe +rty --- .///diff1/- +++ diff2////- @@ -1 +1 @@ -qwe +rty " \ "" "" SKIP= # clean up rm -rf diff1 diff2 exit $FAILCOUNT ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ash.tests������������������������������������������������������������������0000755�0000000�0000000�00000006123�12263563520�015667� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # # These are not ash tests, we use ash as a way to test lineedit! # # Copyright 2010 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh test -f "$bindir/.config" && . "$bindir/.config" test x"CONFIG_SCRIPT" = x"y" || exit 0 test x"CONFIG_HEXDUMP" = x"y" || exit 0 test x"CONFIG_FEATURE_DEVPTS" = x"y" || exit 0 # testing "test name" "options" "expected result" "file input" "stdin" if test x"$CONFIG_UNICODE_PRESERVE_BROKEN" = x"y"; then testing "One byte which is not valid unicode char followed by valid input" \ "script -q -c 'ash' /dev/null >/dev/null; cat ash.output" \ "\ 00000000 ff 2d 0a |.-.| 00000003 " \ "" \ "echo \xff- | hexdump -C >ash.output; exit; exit; exit; exit\n" testing "30 bytes which are not valid unicode chars followed by valid input" \ "script -q -c 'ash' /dev/null >/dev/null; cat ash.output" \ "\ 00000000 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| 00000010 ff ff ff ff ff ff ff ff ff ff ff ff ff ff 2d 0a |..............-.| 00000020 " \ "" \ "echo \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff- | hexdump -C >ash.output; exit; exit; exit; exit\n" else testing "One byte which is not valid unicode char followed by valid input" \ "script -q -c 'ash' /dev/null >/dev/null; cat ash.output" \ "\ 00000000 3f 2d 0a |?-.| 00000003 " \ "" \ "echo \xff- | hexdump -C >ash.output; exit; exit; exit; exit\n" testing "30 bytes which are not valid unicode chars followed by valid input" \ "script -q -c 'ash' /dev/null >/dev/null; cat ash.output" \ "\ 00000000 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f |????????????????| 00000010 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 2d 0a |??????????????-.| 00000020 " \ "" \ "echo \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff- | hexdump -C >ash.output; exit; exit; exit; exit\n" fi # Not sure this behavior is perfect: we lose all invalid input which precedes # arrow keys and such. In this example, \xff\xff are lost testing "2 bytes which are not valid unicode chars followed by left arrow key" \ "script -q -c 'ash' /dev/null >/dev/null; cat ash.output" \ "\ 00000000 3d 2d 0a |=-.| 00000003 " \ "" \ "echo =+\xff\xff\x1b\x5b\x44- | hexdump -C >ash.output; exit; exit; exit; exit\n" # ash should see "echo \xff\n",pause -> execute it as "echo ?" (which is # not checked by the test), then read and execute the rest: "echo A | ..." # The bug was that ash was eating the beginning of "echo A" despite the pause. testing "Invalid unicode chars followed by a pause do not eat next chars" \ "{ $ECHO -ne 'echo \xff\n'; sleep 1; $ECHO -ne 'echo A | hexdump -C >ash.output; exit; exit; exit; exit\n'; } \ | script -q -c 'ash' /dev/null >/dev/null; cat ash.output" \ "\ 00000000 41 0a |A.| 00000002 " \ "" "" rm ash.output exit $FAILCOUNT ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/xargs/���������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�015144� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/xargs/xargs-works����������������������������������������������������������0000644�0000000�0000000�00000000264�12263563520�017363� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ -n "$d" ] || d=.. find "$d" -name \*works -type f | xargs md5sum > logfile.gnu find "$d" -name \*works -type f | busybox xargs md5sum > logfile.bb diff -u logfile.gnu logfile.bb ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/gzip/����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�014771� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/gzip/gzip-accepts-single-minus���������������������������������������������0000644�0000000�0000000�00000000045�12263563520�021717� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo foo | busybox gzip - >/dev/null �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/gzip/gzip-accepts-multiple-files�������������������������������������������0000644�0000000�0000000�00000000077�12263563520�022245� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo bar busybox gzip foo bar test -f foo.gz -a -f bar.gz �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/gzip/gzip-removes-original-file��������������������������������������������0000644�0000000�0000000�00000000051�12263563520�022061� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo busybox gzip foo test ! -f foo ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mkdir/���������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�015126� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mkdir/mkdir-makes-parent-directories���������������������������������������0000644�0000000�0000000�00000000063�12263563520�023060� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox mkdir -p foo/bar test -d foo -a -d foo/bar �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mkdir/mkdir-makes-a-directory����������������������������������������������0000644�0000000�0000000�00000000036�12263563520�021477� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox mkdir foo test -d foo ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/false/���������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�015112� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/false/false-is-silent������������������������������������������������������0000644�0000000�0000000�00000000045�12263563520�020036� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox false 2>&1 | cmp - /dev/null �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/false/false-returns-failure������������������������������������������������0000644�0000000�0000000�00000000020�12263563520�021247� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������! busybox false ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/start-stop-daemon.tests����������������������������������������������������0000755�0000000�0000000�00000000653�12263563520�020477� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2008 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "test name" "cmd" "expected result" "file input" "stdin" testing "start-stop-daemon -x without -a" \ 'start-stop-daemon -S -x true 2>&1; echo $?' \ "0\n" \ "" "" testing "start-stop-daemon -a without -x" \ 'start-stop-daemon -S -a false 2>&1; echo $?' \ "1\n" \ "" "" exit $FAILCOUNT �������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�014422� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-dir-create-dir�������������������������������������������������������0000644�0000000�0000000�00000000076�12263563520�017546� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mkdir bar touch bar/baz busybox cp -R bar foo test -f foo/baz ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-copies-small-file����������������������������������������������������0000644�0000000�0000000�00000000061�12263563520�020252� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo I WANT > foo busybox cp foo bar cmp foo bar �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-RHL-does_not_preserve-links������������������������������������������0000644�0000000�0000000�00000000144�12263563520�022235� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mkdir a >a/file ln -s file a/link busybox cp -RHL a b test ! -L b/link #sh </dev/tty >/dev/tty 2>&1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-preserves-source-file������������������������������������������������0000644�0000000�0000000�00000000051�12263563520�021175� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo busybox cp foo bar test -f foo ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-dev-file�������������������������������������������������������������0000644�0000000�0000000�00000000045�12263563520�016442� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox cp /dev/null foo test -f foo �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-follows-links��������������������������������������������������������0000644�0000000�0000000�00000000067�12263563520�017556� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo ln -s foo bar busybox cp bar baz test -f baz �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-preserves-hard-links�������������������������������������������������0000644�0000000�0000000�00000000177�12263563520�021025� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_PRESERVE_HARDLINKS touch foo ln foo bar mkdir baz busybox cp -d foo bar baz test baz/foo -ef baz/bar �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-does-not-copy-unreadable-file����������������������������������������0000644�0000000�0000000�00000000263�12263563520�022466� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo chmod a-r foo set +e if test `id -u` = 0; then # run as user with nonzero uid setuidgid 1 busybox cp foo bar else busybox cp foo bar fi set -e test ! -f bar ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-d-files-to-dir�������������������������������������������������������0000644�0000000�0000000�00000000410�12263563520�017462� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo file number one > file1 echo file number two > file2 touch file3 ln -s file2 link1 mkdir there busybox cp -d file1 file2 file3 link1 there test -f there/file1 test -f there/file2 test ! -s there/file3 test -L there/link1 test xfile2 = x`readlink there/link1` ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-a-preserves-links����������������������������������������������������0000644�0000000�0000000�00000000126�12263563520�020321� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo ln -s foo bar busybox cp -a bar baz test -L baz test xfoo = x`readlink baz` ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-dir-existing-dir�����������������������������������������������������0000644�0000000�0000000�00000000114�12263563520�020126� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mkdir bar touch bar/baz mkdir foo busybox cp -R bar foo test -f foo/bar/baz ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-copies-large-file����������������������������������������������������0000644�0000000�0000000�00000000123�12263563520�020233� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dd if=/dev/zero of=foo seek=10k count=1 2>/dev/null busybox cp foo bar cmp foo bar ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-copies-empty-file����������������������������������������������������0000644�0000000�0000000�00000000051�12263563520�020277� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo busybox cp foo bar cmp foo bar ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-files-to-dir���������������������������������������������������������0000644�0000000�0000000�00000000373�12263563520�017251� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo file number one > file1 echo file number two > file2 touch file3 ln -s file2 link1 mkdir there busybox cp file1 file2 file3 link1 there test -f there/file1 test -f there/file2 test ! -s there/file3 test -f there/link1 cmp there/file2 there/link1 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-a-files-to-dir�������������������������������������������������������0000644�0000000�0000000�00000000625�12263563520�017467� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo file number one > file1 echo file number two > file2 ln -s file2 link1 mkdir dir1 # why??? #TZ=UTC0 touch -d '2000-01-30 05:24:08' dir1/file3 mkdir there busybox cp -a file1 file2 link1 dir1 there test -f there/file1 test -f there/file2 test ! -s there/dir1/file3 test -L there/link1 test xfile2 = x`readlink there/link1` test ! dir1/file3 -ot there/dir1/file3 test ! dir1/file3 -nt there/dir1/file3 �����������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-preserves-links������������������������������������������������������0000644�0000000�0000000�00000000126�12263563520�020103� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo ln -s foo bar busybox cp -d bar baz test -L baz test xfoo = x`readlink baz` ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp/cp-parents��������������������������������������������������������������0000644�0000000�0000000�00000000247�12263563520�016427� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_CP_LONG_OPTIONS mkdir -p foo/bar/baz touch foo/bar/baz/file mkdir dir busybox cp --parents foo/bar/baz/file dir test -f dir/foo/bar/baz/file ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/touch/���������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�015142� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/touch/touch-creates-file���������������������������������������������������0000644�0000000�0000000�00000000036�12263563520�020552� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox touch foo test -f foo ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/touch/touch-touches-files-after-non-existent-file��������������������������0000644�0000000�0000000�00000000127�12263563520�025427� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch -t 198001010000 bar busybox touch -c foo bar test x"`find bar -mtime -1`" = xbar �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/touch/touch-does-not-create-file�������������������������������������������0000644�0000000�0000000�00000000043�12263563520�022113� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox touch -c foo test ! -f foo ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cpio.tests�����������������������������������������������������������������0000755�0000000�0000000�00000007046�12263563520�016053� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2008 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh umask 022 # ls -ln shows date. Need to remove that, it's variable. # sed: coalesce spaces # cut: remove date # grep: remove "total NNN" lines FILTER_LS="sed 's/ */ /g' | cut -d' ' -f 1-5,9- | grep -v '^total '" # newc cpio archive of directory cpio.testdir with empty x and y hardlinks hexdump="\ \x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x64\x1e\x91\x8c\x00\x00\ \x48\x7f\x80\x4c\x48\x08\x00\x28\x01\xff\xe0\x3f\x24\x14\x00\x0e\ \x20\xdc\x60\x20\x00\x92\x11\xea\xa0\x1a\x00\x00\x00\x03\x20\x8a\ \x93\xd4\x9a\x68\x1a\x0d\x1e\x91\xa1\xa0\x06\x98\xe3\x5c\x2f\xd9\ \x26\xa1\x25\x24\x20\xed\x47\xc7\x21\x40\x2b\x6e\xf2\xe6\xfe\x98\ \x13\x68\xa8\xbd\x82\xb2\x4f\x26\x02\x24\x16\x5b\x22\x16\x72\x74\ \x15\xcd\xc1\xa6\x9e\xa6\x5e\x6c\x16\x37\x35\x01\x99\xc4\x81\x21\ \x29\x28\x4b\x69\x51\xa9\x3c\x1a\x9b\x0a\xe1\xe4\xb4\xaf\x85\x73\ \xba\x23\x10\x59\xe8\xb3\xe1\xa1\x63\x05\x8c\x4f\xc5\xdc\x91\x4e\ \x14\x24\x19\x07\xa4\x63\x00" user=$(id -u) group=$(id -g) rm -rf cpio.testdir cpio.testdir2 2>/dev/null # testing "test name" "command" "expected result" "file input" "stdin" optional FEATURE_LS_SORTFILES FEATURE_LS_TIMESTAMPS testing "cpio extracts zero-sized hardlinks" \ "$ECHO -ne '$hexdump' | bzcat | cpio -i 2>&1; echo \$?; ls -ln cpio.testdir | $FILTER_LS" \ "\ 1 blocks 0 -rw-r--r-- 2 $user $group 0 x -rw-r--r-- 2 $user $group 0 y " "" "" SKIP= test x"$SKIP_KNOWN_BUGS" = x"" && { # Currently fails. Numerous buglets: "1 blocks" versus "1 block", # does not list cpio.testdir/x and cpio.testdir/y testing "cpio lists hardlinks" \ "$ECHO -ne '$hexdump' | bzcat | cpio -t 2>&1; echo \$?" \ "\ cpio.testdir cpio.testdir/x cpio.testdir/y 1 blocks 0 " "" "" } # More complex case rm -rf cpio.testdir cpio.testdir2 2>/dev/null mkdir cpio.testdir touch cpio.testdir/solo touch cpio.testdir/empty echo x >cpio.testdir/nonempty ln cpio.testdir/empty cpio.testdir/empty1 ln cpio.testdir/nonempty cpio.testdir/nonempty1 mkdir cpio.testdir2 optional FEATURE_CPIO_O LONG_OPTS FEATURE_LS_SORTFILES FEATURE_LS_TIMESTAMPS testing "cpio extracts zero-sized hardlinks 2" \ "find cpio.testdir | cpio -H newc --create | (cd cpio.testdir2 && cpio -i 2>&1); echo \$?; ls -ln cpio.testdir2/cpio.testdir | $FILTER_LS" \ "\ 2 blocks 0 -rw-r--r-- 2 $user $group 0 empty -rw-r--r-- 2 $user $group 0 empty1 -rw-r--r-- 2 $user $group 2 nonempty -rw-r--r-- 2 $user $group 2 nonempty1 -rw-r--r-- 1 $user $group 0 solo " "" "" SKIP= # Was trying to create "/usr/bin", correct is "usr/bin". rm -rf cpio.testdir optional FEATURE_CPIO_P testing "cpio -p with absolute paths" \ "echo /usr/bin | cpio -dp cpio.testdir 2>&1; echo \$?; ls cpio.testdir" \ "\ 1 blocks 0 usr " "" "" SKIP= # chown on a link was affecting file, dropping its suid/sgid bits rm -rf cpio.testdir optional FEATURE_CPIO_O FEATURE_STAT_FORMAT mkdir cpio.testdir touch cpio.testdir/file chmod 6755 cpio.testdir/file # sets suid/sgid bits ln -sf file cpio.testdir/link testing "cpio restores suid/sgid bits" \ "cd cpio.testdir && { echo file; echo link; } | cpio -ovHnewc >pack.cpio && rm ???? && cpio -idmvu <pack.cpio 2>/dev/null; stat -c '%a %n' file" \ "\ file link 6755 file " "" "" SKIP= # avoid 'not created: newer or same age file exists' message for directories rm -rf cpio.testdir cpio.testdir2 2>/dev/null mkdir cpio.testdir testing "cpio extracts in existing directory" \ "$ECHO -ne '$hexdump' | bzcat | cpio -id 2>&1; echo \$?" \ "\ 1 blocks 0 " "" "" SKIP= # Clean up rm -rf cpio.testdir cpio.testdir2 2>/dev/null exit $FAILCOUNT ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ar.tests�������������������������������������������������������������������0000755�0000000�0000000�00000001337�12263563520�015520� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2010 Nokia Corporation # Written by Alexander Shishkin # Licensed under GPLv2 or later, see file LICENSE in this source tree. . ./testing.sh # testing "test name" "command(s)" "expected result" "file input" "stdin" optional FEATURE_AR_CREATE rm test.a 2>/dev/null testing "ar creates archives" \ "ar rc test.a README && ar p test.a README | md5sum" \ "$(md5sum <README)\n" \ "" \ "" rm test.a testing "ar replaces things in archives" \ "echo 'blah!' >file1 && echo 'blast!' >file2 && ar cr test.a README file1 file2 && mv file2 file1 && ar cr test.a file1 && ar p test.a file1" \ "blast!\n" \ "" \ "" rm test.a file1 file1 2>/dev/null exit $FAILCOUNT �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/umlwrapper.sh��������������������������������������������������������������0000755�0000000�0000000�00000000532�12263563520�016560� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Wrapper for User Mode Linux emulation environment RUNFILE="$(pwd)/${1}.testroot" if [ -z "$RUNFILE" ] || [ ! -x "$RUNFILE" ] then echo "Can't run '$RUNFILE'" exit 1 fi shift if [ -z $(which linux) ] then echo "No User Mode Linux." exit 1; fi linux rootfstype=hostfs rw init="$RUNFILE" TESTDIR=`pwd` PATH="$PATH" $* quiet ����������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/xargs.tests����������������������������������������������������������������0000755�0000000�0000000�00000001771�12263563520�016244� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2008 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "test name" "command" "expected result" "file input" "stdin" testing "xargs -E _ stops on underscore" \ "xargs -E _" \ "a\n" \ "" "a\n_\nb\n" testing "xargs -E ''" \ "xargs -E ''" \ "a _ b\n" \ "" "a\n_\nb\n" testing "xargs -e without param" \ "xargs -e" \ "a _ b\n" \ "" "a\n_\nb\n" testing "xargs does not stop on underscore ('new' GNU behavior)" \ "xargs" \ "a _ b\n" \ "" "a\n_\nb\n" testing "xargs -s7 can take one-char input" \ "xargs -s7 echo" \ "a\n" \ "" "a\n" testing "xargs -sNUM test 1" \ "xargs -ts25 echo 2>&1 >/dev/null" \ "echo 1 2 3 4 5 6 7 8 9 0\n""echo 1 2 3 4 5 6 7 8 9\n""echo 00\n" \ "" "1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 00\n" testing "xargs -sNUM test 2" \ "xargs -ts25 echo 1 2>&1 >/dev/null" \ "echo 1 2 3 4 5 6 7 8 9 0\n""echo 1 2 3 4 5 6 7 8 9\n""echo 1 00\n" \ "" "2 3 4 5 6 7 8 9 0 2 3 4 5 6 7 8 9 00\n" exit $FAILCOUNT �������busybox-1.22.1/testsuite/README���������������������������������������������������������������������0000644�0000000�0000000�00000004004�12263563520�014701� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������To run the test suite, change to this directory and run "./runtest". It will run all of the test cases, and list those with unexpected outcomes. Adding the -v option will cause it to show expected outcomes as well. To only run the test cases for particular applets: ./runtest <applet1> <applet2>... Set SKIP_KNOWN_BUGS environment variable to any non-empty value to exclude tests which are known to fail. Set SKIP_INTERNET_TESTS to exclude tests which require working internet connection. Common causes of false positives: For busybox built against uclibc, /etc/TZ does not exist or does not match host system timezone setting. For glibc based host systems, timezone settings are in /etc/localtime. LANG and LC_xxx environment variables set to non-C locale. Developer's notes: The test cases for an applet reside in the subdirectory of the applet name. The name of the test case should be the assertion that is tested. The test case should be a shell fragment that returns successfully if the test case passes, and unsuccessfully otherwise. If the test case relies on a certain feature, it should include the string "FEATURE: " followed by the name of the feature in a comment. If it is always expected to fail, it should include the string "XFAIL" in a comment. For the entire testsuite, the copyright is as follows: Copyright (C) 2001, 2002 Matt Kraai This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/date/����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365361�014735� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/date/date-works-1����������������������������������������������������������0000644�0000000�0000000�00000005003�12263563520�017077� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������unset LANG unset LANGUAGE unset LC_TIME unset LC_ALL dt=`busybox date -d 1:2 +%T` test x"$dt" = x"01:02:00" dt=`busybox date -d 1:2:3 +%T` test x"$dt" = x"01:02:03" host_date=/bin/date # date (GNU coreutils) 6.10 reports: # date: invalid date '1.2-3:4' # busybox 1.11.0.svn date reports: # date: invalid date '1/2 3:4' # TODO: (1) compare with strings, not "host date" # (2) implement d/m[/y] hh:mm[:ss] fmt in date applet #hdt=`$host_date -d '1/2 3:4'` #dt=`busybox date -d 1.2-3:4` #test x"$hdt" = x"$dt" #hdt=`$host_date -d '1/2 3:4:5'` #dt=`busybox date -d 1.2-3:4:5` #test x"$hdt" = x"$dt" #hdt=`$host_date -d '1/2/1999 3:4'` #dt=`busybox date -d 1999.1.2-3:4` #test x"$hdt" = x"$dt" #hdt=`$host_date -d '1/2/1999 3:4:5'` #dt=`busybox date -d 1999.1.2-3:4:5` #test x"$hdt" = x"$dt" hdt=`$host_date -d '1999-1-2 3:4:5'` dt=`busybox date -d '1999-1-2 3:4:5'` test x"$hdt" = x"$dt" # Avoiding using week day in this evaluation, as it's mostly different every year # date (GNU coreutils) 6.10 reports: # date: invalid date '01231133' #dt=`busybox date -d 01231133 +%c` #dt=`echo "$dt" | cut -b5-19` #test x"$dt" = x"Jan 23 11:33:00" # date (GNU coreutils) 6.10 reports: # date: invalid date '012311332000' dt=`busybox date -d 200001231133 +%c` test x"$dt" = x"Sun Jan 23 11:33:00 2000" # date (GNU coreutils) 6.10 reports: # date: invalid date '012311332000' dt=`busybox date -d 200001231133.30 +%c` test x"$dt" = x"Sun Jan 23 11:33:30 2000" lcbbd="LC_ALL=C busybox date" wd=$(eval $lcbbd +%a) # weekday name mn=$(eval $lcbbd +%b) # month name dm=$(eval $lcbbd +%e) # day of month, space padded h=$(eval $lcbbd +%H) # hour, zero padded m=$(eval $lcbbd +%M) # minute, zero padded s=$(eval $lcbbd +%S) # second, zero padded z=$(eval $lcbbd -u +%Z) # time zone abbreviation y=$(eval $lcbbd +%Y) # year res=OK case $wd in Sun) ;; Mon) ;; Tue) ;; Wed) ;; Thu) ;; Fri) ;; Sat) ;; *) res=BAD ;; esac case $mn in Jan) ;; Feb) ;; Mar) ;; Apr) ;; May) ;; Jun) ;; Jul) ;; Aug) ;; Sep) ;; Oct) ;; Nov) ;; Dec) ;; *) res=BAD ;; esac dm=${dm# *} [ $dm -ge 1 ] && [ $dm -le 31 ] || res=BAD h=${h#0} [ $h -ge 0 ] && [ $h -le 23 ] || res=BAD m=${m#0} [ $m -ge 0 ] && [ $m -le 59 ] || res=BAD s=${s#0} [ $s -ge 0 ] && [ $s -le 59 ] || res=BAD [ $z = UTC ] || res=BAD [ $y -ge 1970 ] || res=BAD test x"$res" = xOK # This should error out (by showing usage text). Testing for that dt=`busybox date -d 012311332000.30 %+c 2>&1 | head -n 1` test x"${dt##BusyBox * multi-call binary*}" = x"" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/date/date-format-works�����������������������������������������������������0000644�0000000�0000000�00000000403�12263563520�020226� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# TODO: gnu date doesn't accept '2000.11.22-11:22:33' format, # but accepts '2000-11-22 11:22:33'. We must follow. test x"01/01/99" = x"`busybox date -d 1999.01.01-11:22:33 '+%d/%m/%y'`" test x"22/11/00" = x"`busybox date -d 2000.11.22-11:22:33 '+%d/%m/%y'`" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/date/date-R-works����������������������������������������������������������0000644�0000000�0000000�00000001176�12263563520�017147� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# When different date's use time() and clock_gettime(), # seconds transition may not happen at _exactly_ the same moment. # Therefore we try it several times. test x"`date -R`" = x"`busybox date -R`" && exit 0 || true test x"`date -R`" = x"`busybox date -R`" && exit 0 || true test x"`date -R`" = x"`busybox date -R`" && exit 0 || true test x"`date -R`" = x"`busybox date -R`" && exit 0 || true test x"`date -R`" = x"`busybox date -R`" && exit 0 || true test x"`date -R`" = x"`busybox date -R`" && exit 0 || true test x"`date -R`" = x"`busybox date -R`" && exit 0 || true test x"`date -R`" = x"`busybox date -R`" && exit 0 || true false ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/date/date-works������������������������������������������������������������0000644�0000000�0000000�00000002376�12263563520�016753� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dt=`busybox date` # Expected format: Fri Apr 25 03:47:55 CEST 2008 dt=`echo "$dt" | sed 's/^[^ ][^ ][^ ] [^ ][^ ][^ ] [ 0123][0-9] [012][0-9]:[0-5][0-9]:[0-6][0-9] [A-Z][A-Z]* [012][0-9][0-9][0-9]$/OK/'` test x"$dt" = x"OK" dt=`busybox date -d 1:2` dt=`echo "$dt" | cut -b12-19` test x"$dt" = x"01:02:00" dt=`busybox date -d 1:2:3` dt=`echo "$dt" | cut -b12-19` test x"$dt" = x"01:02:03" dt=`busybox date -d 1.2-3:4` dt=`echo "$dt" | cut -b5-19` test x"$dt" = x"Jan 2 03:04:00" dt=`busybox date -d 1.2-3:4:5` dt=`echo "$dt" | cut -b5-19` test x"$dt" = x"Jan 2 03:04:05" dt=`busybox date -d 1999.1.2-3:4` dt=`echo "$dt" | cut -b1-19` test x"$dt" = x"Sat Jan 2 03:04:00" dt=`busybox date -d 1999.1.2-3:4:5` dt=`echo "$dt" | cut -b1-19` test x"$dt" = x"Sat Jan 2 03:04:05" dt=`busybox date -d '1999-1-2 3:4:5'` dt=`echo "$dt" | cut -b1-19` test x"$dt" = x"Sat Jan 2 03:04:05" # date (GNU coreutils) 8.17 doesn't accept 01231133 either: # date: invalid date '01231133' #dt=`busybox date -d 01231133` #dt=`echo "$dt" | cut -b5-19` #test x"$dt" = x"Jan 23 11:33:00" dt=`busybox date -d 200001231133` dt=`echo "$dt" | cut -b1-19` test x"$dt" = x"Sun Jan 23 11:33:00" dt=`busybox date -d 200001231133.30` dt=`echo "$dt" | cut -b1-19` test x"$dt" = x"Sun Jan 23 11:33:30" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/date/date-@-works����������������������������������������������������������0000644�0000000�0000000�00000001112�12263563520�017113� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Tests for time_t value (unix time format) # Just before DST switched off test x"Sun Oct 31 03:59:59 EEST 2010" = x"`TZ=EET-2EEST,M3.5.0/3,M10.5.0/4 busybox date -d @1288486799`" # Just after DST switched off test x"Sun Oct 31 03:00:01 EET 2010" = x"`TZ=EET-2EEST,M3.5.0/3,M10.5.0/4 busybox date -d @1288486801`" # Just before DST switched on test x"Sun Mar 28 02:59:59 EET 2010" = x"`TZ=EET-2EEST,M3.5.0/3,M10.5.0/4 busybox date -d @1269737999`" # Just after DST switched on test x"Sun Mar 28 04:00:01 EEST 2010" = x"`TZ=EET-2EEST,M3.5.0/3,M10.5.0/4 busybox date -d @1269738001`" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/date/date-u-works����������������������������������������������������������0000644�0000000�0000000�00000000142�12263563520�017202� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x"Sat Jan 1 11:22:33 UTC 2000" = x"`TZ=CET-1CEST-2 busybox date -u -d 2000.01.01-11:22:33`" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/parse.tests����������������������������������������������������������������0000755�0000000�0000000�00000006565�12263563520�016240� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2008 by Denys Vlasenko <vda.linux@googlemail.com> # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh COLLAPSE=$(( 0x00010000)) TRIM=$(( 0x00020000)) GREEDY=$(( 0x00040000)) MIN_DIE=$(( 0x00100000)) KEEP_COPY=$((0x00200000)) ESCAPE=$(( 0x00400000)) NORMAL=$(( COLLAPSE | TRIM | GREEDY)) # testing "description" "command" "result" "infile" "stdin" testing "parse mdev.conf" \ "parse -n 4 -m 3 -f $((NORMAL)) -" \ "[sda][0:0][644][@echo @echo TEST]\n" \ "-" \ " sda 0:0 644 @echo @echo TEST # echo trap\n" testing "parse notrim" \ "parse -n 4 -m 3 -f $((NORMAL - TRIM - COLLAPSE)) -" \ "[][sda][0:0][644 @echo @echo TEST ]\n" \ "-" \ " sda 0:0 644 @echo @echo TEST \n" FILE=__parse cat >$FILE <<EOF # # Device Point System Options #_______________________________________________________________ /dev/hdb3 / ext2 defaults 1 0 /dev/hdb1 /dosc hpfs ro 1 0 /dev/fd0 /dosa vfat rw,user,noauto,nohide 0 0 /dev/fd1 /dosb vfat rw,user,noauto,nohide 0 0 # /dev/cdrom /cdrom iso9660 ro,user,noauto,nohide 0 0 /dev/hdb5 /redhat ext2 rw,root,noauto,nohide 0 0 #sssd /dev/hdb6 /win2home ntfs rw,root,noauto,nohide 0 0# ssdsd /dev/hdb7 /win2skul ntfs rw,root,noauto,nohide none 0 0 none /dev/pts devpts gid=5,mode=620 0 0 none /proc proc defaults 0 0 EOF cat >$FILE.res <<EOF [/dev/hdb3][/][ext2][defaults][1][0] [/dev/hdb1][/dosc][hpfs][ro][1][0] [/dev/fd0][/dosa][vfat][rw,user,noauto,nohide][0][0] [/dev/fd1][/dosb][vfat][rw,user,noauto,nohide][0][0] [/dev/cdrom][/cdrom][iso9660][ro,user,noauto,nohide][0][0] [/dev/hdb5][/redhat][ext2][rw,root,noauto,nohide][0][0] [/dev/hdb6][/win2home][ntfs][rw,root,noauto,nohide][0][0] [/dev/hdb7][/win2skul][ntfs][rw,root,noauto,nohide][none][0 0] [none][/dev/pts][devpts][gid=5,mode=620][0][0] [none][/proc][proc][defaults][0][0] EOF testing "parse polluted fstab" \ "parse -n 6 -m 6 $FILE" \ "`cat $FILE.res`\n" \ "" \ "" cp ../examples/inittab $FILE cat >$FILE.res <<EOF [][][sysinit][/etc/init.d/rcS] [][][askfirst][-/bin/sh] [tty2][][askfirst][-/bin/sh] [tty3][][askfirst][-/bin/sh] [tty4][][askfirst][-/bin/sh] [tty4][][respawn][/sbin/getty 38400 tty5] [tty5][][respawn][/sbin/getty 38400 tty6] [][][restart][/sbin/init] [][][ctrlaltdel][/sbin/reboot] [][][shutdown][/bin/umount -a -r] [][][shutdown][/sbin/swapoff -a] EOF testing "parse inittab from examples" \ "parse -n 4 -m 4 -f $((NORMAL - TRIM - COLLAPSE)) -d'#:' $FILE" \ "`cat $FILE.res`\n" \ "" \ "" cp ../examples/udhcp/udhcpd.conf $FILE cat >$FILE.res <<EOF [start][192.168.0.20] [end][192.168.0.254] [interface][eth0] [opt][dns][192.168.10.2][192.168.10.10] [option][subnet][255.255.255.0] [opt][router][192.168.10.2] [opt][wins][192.168.10.10] [option][dns][129.219.13.81] [option][domain][local] [option][lease][864000] [option][0x08][01020304] EOF testing "parse udhcpd.conf from examples" \ "parse -n 127 $FILE" \ "`cat $FILE.res`\n" \ "" \ "" rm -f $FILE $FILE.res exit $FAILCOUNT �������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/all_sourcecode.tests�������������������������������������������������������0000755�0000000�0000000�00000005217�12263563520�020102� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Tests for the sourcecode base itself. # Copyright 2006 by Mike Frysinger <vapier@gentoo.org> # Licensed under GPLv2, see file LICENSE in this source tree. [ -n "$srcdir" ] || srcdir=$(pwd) . ./testing.sh # # if we don't have the sourcecode available, let's just bail # [ -s "$srcdir/../Makefile" ] || exit 0 [ -s "$srcdir/../include/applets.h" ] || exit 0 # # make sure all usage strings are properly escaped. oftentimes people miss # an escape sequence so we end up with: # #define foo_usage \ # " this line is ok" \ # " as is this line" # " but this one is broken as the \ is missing from above" # ${CROSS_COMPILE}cpp -dD -P $srcdir/../include/usage.h \ | sed -e '/^#define/d' -e '/^$/d' > src.usage.escaped testing "Usage strings escaped" "cat src.usage.escaped" "" "" "" rm -f src.usage.escaped # # verify the applet order is correct in applets.h, otherwise # applets won't be called properly. # sed -n -e 's:^//::' -e '/^IF_[A-Z]*(APPLET/{s:,.*::;s:.*(::;s:"::g;p}' \ $srcdir/../include/applets.h > applet.order.current LC_ALL=C sort applet.order.current > applet.order.correct testing "Applet order" "diff -u applet.order.current applet.order.correct" "" "" "" rm -f applet.order.current applet.order.correct # # check for misc common typos # find $srcdir/../ \ '(' -type d -a '(' -name .svn -o -name testsuite ')' -prune ')' \ -o '(' -type f -a -print0 ')' | xargs -0 \ grep -I \ -e '\<compatability\>' \ -e '\<compatable\>' \ -e '\<fordeground\>' \ -e '\<depency\>' -e '\<dependancy\>' -e '\<dependancies\>' \ -e '\<defalt\>' \ -e '\<remaing\>' \ -e '\<queueing\>' \ -e '\<detatch\>' \ -e '\<sempahore\>' \ -e '\<reprenstative\>' \ -e '\<overriden\>' \ -e '\<readed\>' \ -e '\<formated\>' \ -e '\<algorithic\>' \ -e '\<deamon\>' \ -e '\<derefernce\>' \ -e '\<acomadate\>' \ | sed -e "s:^$srcdir/\.\./::g" > src.typos testing "Common typos" "cat src.typos" "" "" "" rm -f src.typos # # don't allow obsolete functions # find $srcdir/.. '(' -name '*.c' -o -name '*.h' ')' -print0 | xargs -0 \ grep -E -e '\<(bcmp|bcopy|bzero|getwd|index|mktemp|rindex|utime|sigblock|siggetmask|sigsetmask)\>[[:space:]]*\(' \ | sed -e "s:^$srcdir/\.\./::g" > src.obsolete.funcs testing "Obsolete function usage" "cat src.obsolete.funcs" "" "" "" rm -f src.obsolete.funcs # # don't allow obsolete headers # find $srcdir/.. '(' -name '*.c' -o -name '*.h' ')' -print0 | xargs -0 \ grep -E -e '\<(malloc|memory|sys/(errno|fcntl|signal|stropts|termios|unistd))\.h\>' \ | sed -e "s:^$srcdir/\.\./::g" > src.obsolete.headers testing "Obsolete headers" "cat src.obsolete.headers" "" "" "" rm -f src.obsolete.headers exit $FAILCOUNT ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/sha1sum.tests��������������������������������������������������������������0000755�0000000�0000000�00000000115�12263563520�016470� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh . ./md5sum.tests sha1sum d41337e834377140ae7f98460d71d908598ef04f ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/bunzip2/�������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�015410� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/bunzip2/bunzip2-reads-from-standard-input����������������������������������0000644�0000000�0000000�00000000104�12263563520�023713� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo foo | bzip2 | busybox bunzip2 > output echo foo | cmp - output ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/bunzip2/bzcat-does-not-remove-compressed-file������������������������������0000644�0000000�0000000�00000000100�12263563520�024531� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo foo | bzip2 >foo.bz2 busybox bzcat foo.bz2 test -f foo.bz2 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/bunzip2/bunzip2-removes-compressed-file������������������������������������0000644�0000000�0000000�00000000104�12263563520�023460� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo foo | bzip2 >foo.bz2 busybox bunzip2 foo.bz2 test ! -f foo.bz2 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/taskset.tests��������������������������������������������������������������0000755�0000000�0000000�00000001072�12263563520�016570� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2006 Bernhard Reutner-Fischer # Licensed under GPLv2 or later, see file LICENSE in this source tree. . ./testing.sh a="taskset" # testing "test name" "opts" "expected result" "file inp" "stdin" testing "taskset (get from pid 1)" "$a -p 1 >/dev/null;echo \$?" "0\n" "" "" testing "taskset (invalid pid)" "$a -p 0 >/dev/null 2>&1;echo \$?" "1\n" "" "" testing "taskset (set_aff, needs CAP_SYS_NICE)" \ "$a 0x1 $SHELL -c '$a -p \$\$ | grep \"current affinity mask: 1\" >/dev/null'; echo \$?" \ "0\n" "" "" unset a exit $FAILCOUNT ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/uniq.tests�����������������������������������������������������������������0000755�0000000�0000000�00000004750�12263563520�016074� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # SUSv3 compliant uniq tests. # Copyright 2005 by Rob Landley <rob@landley.net> # Licensed under GPLv2, see file LICENSE in this source tree. # AUDIT: Full SUSv3 coverage (except internationalization). . ./testing.sh # testing "test name" "options" "expected result" "file input" "stdin" # file input will be file called "input" # test can create a file "actual" instead of writing to stdout # Test exit status testing "uniq (exit with error)" "uniq nonexistent 2> /dev/null || echo yes" \ "yes\n" "" "" testing "uniq (exit success)" "uniq /dev/null && echo yes" "yes\n" "" "" # Test various data sources and destinations testing "uniq (default to stdin)" "uniq" "one\ntwo\nthree\n" "" \ "one\ntwo\ntwo\nthree\nthree\nthree\n" testing "uniq - (specify stdin)" "uniq -" "one\ntwo\nthree\n" "" \ "one\ntwo\ntwo\nthree\nthree\nthree\n" testing "uniq input (specify file)" "uniq input" "one\ntwo\nthree\n" \ "one\ntwo\ntwo\nthree\nthree\nthree\n" "" testing "uniq input outfile (two files)" "uniq input actual > /dev/null" \ "one\ntwo\nthree\n" "one\ntwo\ntwo\nthree\nthree\nthree\n" "" testing "uniq (stdin) outfile" "uniq - actual" \ "one\ntwo\nthree\n" "" "one\ntwo\ntwo\nthree\nthree\nthree\n" # Note: SUSv3 doesn't seem to require support for "-" output, but we do anyway. testing "uniq input - (specify stdout)" "uniq input -" \ "one\ntwo\nthree\n" "one\ntwo\ntwo\nthree\nthree\nthree\n" "" #-f skip fields #-s skip chars #-c occurrences #-d dups only #-u #-w max chars # Test various command line options # Leading whitespace is a minor technical violation of the spec, # but since gnu does it... testing "uniq -c (occurrence count)" "uniq -c | sed 's/^[ \t]*//'" \ "1 one\n2 two\n3 three\n" "" \ "one\ntwo\ntwo\nthree\nthree\nthree\n" testing "uniq -d (dups only)" "uniq -d" "two\nthree\n" "" \ "one\ntwo\ntwo\nthree\nthree\nthree\n" testing "uniq -f -s (skip fields and chars)" "uniq -f2 -s 3" \ "cc dd ee8 aa bb cc9 " "" \ "cc dd ee8 bb cc dd8 aa bb cc9 " testing "uniq -w (compare max characters)" "uniq -w 2" \ "cc1 " "" \ "cc1 cc2 cc3 " testing "uniq -s -w (skip fields and compare max chars)" \ "uniq -s 2 -w 2" \ "aaccaa " "" \ "aaccaa aaccbb bbccaa " # -d is "Suppress the writing fo lines that are not repeated in the input." # -u is "Suppress the writing of lines that are repeated in the input." # Therefore, together this means they should produce no output. testing "uniq -u and -d produce no output" "uniq -d -u" "" "" \ "one\ntwo\ntwo\nthree\nthree\nthree\n" exit $FAILCOUNT ������������������������busybox-1.22.1/testsuite/tr/������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014444� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tr/tr-rejects-wrong-class��������������������������������������������������0000644�0000000�0000000�00000001635�12263563520�020717� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_TR_CLASSES echo t12esting | tr -d '[[:alpha:]]' > logfile.gnu echo t12esting | tr -d '[:alpha:]' >> logfile.gnu echo t12esting | tr -d '[[:alpha:]' >> logfile.gnu echo t12esting | tr -d '[[:alpha:' >> logfile.gnu echo t12esting | tr -d '[[:alpha' >> logfile.gnu echo t12esting | tr -d '[:alpha:]' >> logfile.gnu echo t12esting | tr -d '[:alpha:' >> logfile.gnu echo t12esting | tr -d '[:alpha' >> logfile.gnu echo t12esting | busybox tr -d '[[:alpha:]]' > logfile.bb echo t12esting | busybox tr -d '[:alpha:]' >> logfile.bb echo t12esting | busybox tr -d '[[:alpha:]' >> logfile.bb echo t12esting | busybox tr -d '[[:alpha:' >> logfile.bb echo t12esting | busybox tr -d '[[:alpha' >> logfile.bb echo t12esting | busybox tr -d '[:alpha:]' >> logfile.bb echo t12esting | busybox tr -d '[:alpha:' >> logfile.bb echo t12esting | busybox tr -d '[:alpha' >> logfile.bb diff -u logfile.gnu logfile.bb ���������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tr/tr-d-alnum-works��������������������������������������������������������0000644�0000000�0000000�00000000257�12263563520�017522� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_TR_CLASSES echo testing | tr -d '[[:alnum:]]' > logfile.gnu echo testing | busybox tr -d '[[:alnum:]]' > logfile.bb diff -u logfile.gnu logfile.bb �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tr/tr-works����������������������������������������������������������������0000644�0000000�0000000�00000001231�12263563520�016160� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_TR_CLASSES run_tr () { $ECHO -n "echo '$1' | tr '$2' '$3': " echo "$1" | $bb tr "$2" "$3" echo } tr_test () { run_tr "cbaab" abc zyx run_tr "TESTING A B C" '[A-Z]' '[a-z]' run_tr "abc[]" "a[b" AXB run_tr abc '[:alpha:]' A-ZA-Z run_tr abc56 '[:alnum:]' A-ZA-Zxxxxxxxxxx run_tr 012 '[:digit:]' abcdefghi run_tr abc56 '[:lower:]' '[:upper:]' run_tr " " '[:space:]' 12345 run_tr " " '[:blank:]' 12 run_tr 'a b' '[= =]' X run_tr "[:" '[:' ab run_tr " .,:" '[:punct:]' 12 run_tr " .,:" '[:cntrl:]' 12 } bb= tr_test > logfile.gnu bb=busybox tr_test > logfile.bb diff -u logfile.gnu logfile.bb �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tr/tr-d-works��������������������������������������������������������������0000644�0000000�0000000�00000000171�12263563520�016403� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo testing | tr -d aeiou > logfile.gnu echo testing | busybox tr -d aeiou > logfile.bb diff -u logfile.gnu logfile.bb �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tr/tr-non-gnu��������������������������������������������������������������0000644�0000000�0000000�00000000073�12263563520�016377� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo fdhrnzvfu bffvsentr | busybox tr '[a-z]' '[n-z][a-m]' ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tail.tests�����������������������������������������������������������������0000755�0000000�0000000�00000001214�12263563520�016041� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2009 by Denys Vlasenko <vda.linux@googlemail.com> # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "test name" "command" "expected result" "file input" "stdin" # file input will be file called "input" # test can create a file "actual" instead of writing to stdout testing "tail: +N with N > file length" \ "tail -c +55 2>&1; echo \$?" \ "0\n" \ "" "qw" testing "tail: -c +N with largish N" \ " dd if=/dev/zero bs=16k count=1 2>/dev/null | tail -c +8200 | wc -c; dd if=/dev/zero bs=16k count=1 2>/dev/null | tail -c +8208 | wc -c; " \ "8185\n8177\n" \ "" "" exit $FAILCOUNT ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/�����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014605� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar-demands-at-least-one-ctx�������������������������������������������0000644�0000000�0000000�00000000020�12263563520�022006� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������! busybox tar v ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar-archives-multiple-files��������������������������������������������0000644�0000000�0000000�00000000202�12263563520�022047� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_TAR_CREATE touch foo bar busybox tar cf foo.tar foo bar rm foo bar tar xf foo.tar test -f foo -a -f bar ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar_with_prefix_fields�������������������������������������������������0000644�0000000�0000000�00000046606�12263563520�021274� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_TAR_UNAME_GNAME # FEATURE: CONFIG_DESKTOP tar1_bz2() { $ECHO -ne "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x12\xd1\x86\x30\x00\x0c" $ECHO -ne "\xb8\x7f\x80\xff\x50\x08\xa0\x5e\xff\xff\xfd\x7f\xff\xff\xee\xff" $ECHO -ne "\xff\xff\xfa\x00\x08\x60\x0f\xc5\x3e\xf4\xdc\x00\x00\x59\x25\xbd" $ECHO -ne "\xb8\x7a\x02\xb5\x82\x78\x25\xb0\x89\x54\x10\x11\x44\x8b\x36\x36" $ECHO -ne "\xc8\x97\x0d\x34\x9a\x21\xa9\x36\xa9\xed\x32\x02\x8d\xa6\x81\x8a" $ECHO -ne "\x79\x13\x4d\x1a\x03\x10\x69\xa0\xd3\x40\x64\x0f\x44\x68\x3d\x41" $ECHO -ne "\x2a\x7a\x20\x09\xa1\x34\x9a\x09\xa4\xc8\xf5\x4f\x46\xa6\x86\x32" $ECHO -ne "\x4c\x08\x00\x00\xd0\x06\x9a\x00\xd3\xd4\x11\x49\xa7\xb5\x20\x1a" $ECHO -ne "\x7a\x80\x00\x00\x00\x00\xd0\x68\x00\x00\x00\x00\x00\x49\xa8\x89" $ECHO -ne "\xa9\x31\x4f\x22\xa7\xea\x9b\x61\x53\xf4\x93\xf2\xa3\x47\xa4\xd3" $ECHO -ne "\x41\xea\x06\x41\xa0\x0d\x00\x00\x00\x1e\xa0\x70\x34\xd3\x4d\x06" $ECHO -ne "\x86\x86\x86\x46\x80\x64\x01\xa1\xa0\x34\xd1\x90\x00\x03\x09\x88" $ECHO -ne "\x0d\x04\x89\x08\x00\x82\x7a\x4c\x42\x64\xc9\x3d\x1a\x29\xe9\xa2" $ECHO -ne "\x3d\x46\x9e\x46\x9a\x13\x26\x9e\x53\x10\x01\x91\xea\x68\x19\xf0" $ECHO -ne "\x73\xf2\xe0\xd1\x3c\x80\x01\xb1\x48\x44\x08\x9a\xba\xf3\x9e\x87" $ECHO -ne "\xec\xc4\x4b\x02\x92\x80\x75\x00\x56\x42\x88\x10\x68\xcc\x06\x22" $ECHO -ne "\x7c\x2b\xa7\xc8\x21\x91\x13\xe5\x72\xc0\xe6\x0c\x03\x10\xf2\x89" $ECHO -ne "\x9c\x67\x6b\xc3\xe6\xae\x98\x85\x0a\x7f\x25\x2e\x3d\x84\x5b\xeb" $ECHO -ne "\xf3\xff\xb3\x52\xf7\x6e\xf6\x92\xd6\x33\x5f\x4f\xd1\x3d\xb7\xc4" $ECHO -ne "\x0d\x50\x02\x49\x01\xaf\xd0\x69\xbb\xd3\xe9\x63\x0a\x68\x36\x92" $ECHO -ne "\xf2\x03\x1d\xf2\xe2\x35\xbc\x73\xd4\x44\xf6\xa0\xe0\x31\xd7\x7d" $ECHO -ne "\x56\x96\xcb\x52\xfc\x79\xe0\xeb\xf7\x34\xd8\xda\x18\x72\x30\x94" $ECHO -ne "\x53\x45\xf5\x54\x56\x6c\x0b\x50\xa0\xbc\xbd\xcc\xd8\x21\xab\x7b" $ECHO -ne "\xa8\xa4\xe4\x78\x25\x73\xbf\x4b\x30\x38\x71\xe9\x3c\x14\x5d\xa3" $ECHO -ne "\x12\x04\x6b\x37\x9d\xe5\xce\xa5\xd9\xd1\xa5\x69\x09\x08\xc4\x48" $ECHO -ne "\x4b\x34\x58\x81\x15\x18\x88\xac\x11\x51\x88\x35\x0d\xd3\x13\x18" $ECHO -ne "\x67\x73\x20\x5c\x28\x03\x26\xcd\x6d\x20\x90\xba\xa4\x12\xb3\x08" $ECHO -ne "\x27\x74\x6a\x99\xdf\xb1\x20\x3d\x85\xe7\x5f\xab\x0e\x2e\xdc\x23" $ECHO -ne "\x99\xe1\xef\x34\x68\xcd\xa9\xb0\xbf\xda\xec\x81\xdd\x66\xca\x21" $ECHO -ne "\x13\x47\xd7\xca\x48\xcf\xeb\x25\xbb\x79\x6d\x40\xd0\xe4\x69\x3c" $ECHO -ne "\x8f\x09\x1e\x7b\xaa\x4b\x91\x39\xac\xd6\xd2\x0c\x85\x1d\xf7\x70" $ECHO -ne "\x1f\x1e\x58\xbb\x22\x11\x29\x39\x14\x4d\x58\x81\x9f\xd7\x1e\x22" $ECHO -ne "\x21\x91\x0a\x40\xd1\x87\x29\x99\x93\xf4\xf3\x25\x48\xbb\xb4\x24" $ECHO -ne "\x2a\x1c\xa7\x28\xc1\x68\x08\x25\x00\xaa\x3d\xee\xae\xc1\xe1\x4f" $ECHO -ne "\xe6\x9a\x26\x6b\xcf\xb1\x3e\xb9\x85\x04\xf4\xef\xff\x7a\x2f\x2a" $ECHO -ne "\x04\x08\xe0\x4c\xb8\xbd\x8b\x81\xbf\xa2\xbe\x82\x52\x9b\x40\x63" $ECHO -ne "\xe6\xf3\xb3\xe4\xe6\xe5\x94\x4a\xdd\xc3\x1b\xaf\x61\xf3\xbf\x5b" $ECHO -ne "\x6d\xaa\xaa\x27\xe8\x50\x8d\x23\x97\xa4\xbd\xc3\xd2\xe6\xb5\x66" $ECHO -ne "\x9a\x1a\x8e\x45\x2a\xed\x0b\x79\xb8\x89\x38\x4a\x04\x85\x0d\x1e" $ECHO -ne "\x2b\x77\x51\x91\x5f\x9f\xe0\x2a\x49\x56\xd3\xa1\xde\xf6\xd7\x88" $ECHO -ne "\x5a\x61\xe5\x04\x54\xdf\xa3\x92\xeb\xbf\x75\x39\xce\xfa\xf5\xde" $ECHO -ne "\x30\xd7\x56\xd1\x7d\x2c\xdf\xda\x3e\x1c\xc8\xc2\x93\x61\x21\x20" $ECHO -ne "\xb2\x22\x6d\xbe\x39\x52\x64\xf6\xb3\x91\x21\x86\xdb\x67\x72\x8f" $ECHO -ne "\x49\xad\xe4\x93\x39\x5c\x34\x8f\x58\xdb\x58\xd3\x3c\x1e\x4c\x6c" $ECHO -ne "\xbb\x70\x6f\x42\xcf\x9e\xbf\xb1\xcb\xa9\x8d\x05\xe7\xea\xea\xd7" $ECHO -ne "\x3c\x67\x31\x69\x44\x33\xa4\x92\x9c\x65\xa4\x89\x5a\xae\xcf\xc9" $ECHO -ne "\x55\x43\x62\x6d\xbf\x05\x3c\xd1\x0f\x01\x4a\xb5\x1d\xbb\x2c\xfb" $ECHO -ne "\xa6\xb7\xb3\xb1\x1d\x66\xd3\xeb\x22\xd0\xb5\x5a\x4b\xc4\x47\x47" $ECHO -ne "\x5a\x49\x85\x18\xbc\x15\x39\x3b\x92\xee\x51\x98\x33\x34\x5d\xb5" $ECHO -ne "\xbb\x8b\x94\x8c\xde\x8e\x3f\x3d\x09\x4f\xba\xd3\xf6\x79\x74\x8e" $ECHO -ne "\x82\x0d\x56\x85\xa2\xc7\xc6\xa6\x89\x29\x26\xa3\x53\x5e\x52\xf5" $ECHO -ne "\x56\x74\x8b\x17\x82\xed\x7a\x8b\x68\x61\xa5\xc9\x7c\xde\x9f\x68" $ECHO -ne "\x27\x4d\xea\x65\x68\x6f\x7d\x5e\x88\x73\x87\x6c\x92\xf2\xa9\x15" $ECHO -ne "\x4e\xee\x4d\x41\xbb\x98\x5d\x8a\xaf\xcb\x11\x7b\x2a\xce\xf4\x1e" $ECHO -ne "\x3a\x28\x48\x14\xfe\x7f\x09\x45\x48\xf1\x5b\xc1\xcb\xcd\x91\xba" $ECHO -ne "\x3b\xe2\x7d\x57\x85\x66\x68\xec\x51\x82\x97\x88\xeb\x94\x3b\x78" $ECHO -ne "\x6c\xf4\xf1\x3e\x38\x8d\x22\x16\xab\x3b\x13\xb3\x1b\x39\x94\x0e" $ECHO -ne "\xa8\x26\xb7\x8d\xe9\x7d\x66\x23\x4b\x65\x07\xb7\x2b\xc9\x96\xb6" $ECHO -ne "\x99\x12\x22\xbc\x90\xda\x51\xbc\xfd\x97\xa5\x7d\xbc\x12\xa6\x72" $ECHO -ne "\xd3\xe3\x8c\xc7\x58\xe1\xf8\x28\xf4\x46\x49\x14\xd0\x9d\xb6\xed" $ECHO -ne "\xce\x99\xc6\xbc\xed\xa3\xab\xa0\x8c\x9d\xce\x1a\x1a\xc2\xe6\x77" $ECHO -ne "\xba\xae\xba\xd6\xc9\xb2\xd1\x65\x24\x7b\x0d\xd4\xf2\xac\x28\xc3" $ECHO -ne "\x1c\xbe\x4a\x54\xe3\x0f\x8d\xad\xb2\x37\x9e\x1f\x81\x72\x2d\xab" $ECHO -ne "\x8f\xb1\xcd\xf7\xb4\x51\x2f\x1d\xf8\xad\x77\x14\x37\xd2\x1a\x9a" $ECHO -ne "\xc0\xf2\x48\xc6\x4c\x8d\xd3\x8d\xf1\xd9\x2e\x2c\xdd\x7a\x98\x3c" $ECHO -ne "\x24\x76\xb9\x9d\x27\xcd\x71\x7d\x6c\xc7\x1f\x0a\x74\x8a\x6e\x54" $ECHO -ne "\xec\x5a\xa1\x77\x60\x80\xef\x00\xa4\x5f\x9e\x8b\x2f\x02\x72\x9c" $ECHO -ne "\x46\xd8\x79\x92\x4c\x8f\x4e\x37\xed\x0c\x58\xab\x44\xee\x1d\xd1" $ECHO -ne "\xa1\xb0\xa5\x1f\xaf\xb0\x39\x01\x26\xb2\x4a\x20\x68\x4a\x18\x23" $ECHO -ne "\xc3\x03\x84\x22\x18\xdb\x6d\x83\x60\xc1\x12\x09\x21\x84\x22\x48" $ECHO -ne "\x7f\x1e\x17\xf5\xbe\xce\x4c\x4f\x9f\x9f\xee\xf4\xfe\xef\x9a\x34" $ECHO -ne "\x91\x8f\x36\x1d\xbc\x73\xd7\xeb\xc8\x2e\x81\x25\xfa\x18\x76\x35" $ECHO -ne "\x1f\x16\xdb\x20\x4b\x74\x6d\x94\x4e\xe5\x36\xed\xf5\x5d\x59\xaf" $ECHO -ne "\x46\x70\xea\x03\xac\x50\xbb\x26\xab\x39\x9a\x4b\x6b\x09\x8c\x6d" $ECHO -ne "\x34\xcf\xed\xaa\xf7\x56\x40\xf2\xab\x07\xca\x22\x71\x97\xc7\x35" $ECHO -ne "\xe8\x06\x90\x7b\xec\xc3\x9f\xa4\xde\xd9\xdb\x43\xf1\xd5\x06\x58" $ECHO -ne "\x72\x9e\x1f\x08\xb6\xc2\x05\x0d\x25\xfe\x7a\x85\xe5\x10\x12\x68" $ECHO -ne "\x18\x7e\x8c\xa0\xfa\xb4\xc4\xc7\x4e\xa9\xf2\x13\xd7\xc2\x52\xb5" $ECHO -ne "\xe3\x72\x37\x31\x1e\x4f\x99\xfd\xac\x97\x08\x88\x71\x88\xeb\x1a" $ECHO -ne "\xf9\xa1\x10\x9c\x44\x08\x56\x4a\x77\xaa\x0f\x19\x5f\x5f\xb3\x95" $ECHO -ne "\xee\x9b\x9f\x5b\xb5\xc9\x0a\xf4\x28\x16\x25\x34\x6c\x72\xda\x92" $ECHO -ne "\xb4\x2c\xbd\x5e\xb1\xe8\xe5\x0f\x68\xf3\x44\x8a\xd5\xfa\x73\x5c" $ECHO -ne "\x89\x2e\x99\x7d\xed\xe3\x5b\x3f\x48\x97\xeb\xb6\x76\x5c\xa5\x9d" $ECHO -ne "\xef\x12\x1e\x42\x89\x52\xad\x28\x90\xe5\x2b\x88\xa0\x4f\x11\x92" $ECHO -ne "\xcd\xcc\x63\x40\x1a\xc7\x10\x0c\x2f\xcd\x01\xf2\x07\x38\xac\x14" $ECHO -ne "\xe5\x90\xc0\x30\x21\xe2\xe3\x72\x0e\x3e\x04\xc8\x9e\xa7\x00\xdb" $ECHO -ne "\x91\xdd\x9d\x80\xa4\x69\x2a\x48\x37\x97\xa4\x26\x5d\xae\x84\x1e" $ECHO -ne "\x88\xf4\x83\x04\x24\xc9\x1f\x94\x61\x25\xf9\x82\xdd\xed\x2d\x96" $ECHO -ne "\xad\x06\x45\xdd\x88\xd7\x50\x40\x14\xdc\x7c\xdb\x0f\x53\x96\x27" $ECHO -ne "\xcb\x67\xac\xa6\xc1\x15\x2f\xc3\xdb\x2c\xca\x94\xb3\xf3\xd1\x6a" $ECHO -ne "\xba\x34\x83\xd1\xcc\x40\x3e\x76\xa1\x69\x7f\x49\x33\xdc\xa7\x3c" $ECHO -ne "\x6a\x67\x15\xab\xdb\x52\xa0\xb8\xa6\x1e\xce\xe3\xaf\xf4\xa2\x62" $ECHO -ne "\x35\x0f\x03\x40\x8e\x20\x12\x9c\xb6\x34\x71\x3a\x15\x5d\xe5\x34" $ECHO -ne "\xa8\xd4\x05\x99\x6b\x9a\xb6\x41\x0b\x78\xc4\xd8\xd9\x7a\x65\xdc" $ECHO -ne "\xdb\xe3\x42\xd5\x66\xf9\xb4\x83\x7e\xc0\xf4\x01\xc4\xcc\x3b\x0e" $ECHO -ne "\x15\xdc\x15\xc2\x3e\x04\x2f\xfc\x6b\x72\xeb\xf6\xaa\x16\x20\xde" $ECHO -ne "\xd3\x3a\xb1\x10\xc6\x3c\xe8\x2b\xb8\xea\xda\x19\x6e\x36\xaa\xa4" $ECHO -ne "\x23\x6d\xa0\x40\xd1\x5a\x0b\x7e\xa4\xd5\x2d\xcb\xa9\x15\x35\xba" $ECHO -ne "\x93\x92\x45\x41\xb0\x1a\xd1\x13\x31\xb6\x44\x98\x78\x28\x15\xe4" $ECHO -ne "\xae\xba\x58\xd1\x75\x36\x34\x1a\xd8\x28\xf1\x4a\x4c\xbc\x1b\xa8" $ECHO -ne "\xf7\x57\x92\xbc\xe2\xb5\xda\xb6\xa6\x1d\x83\x37\x96\x43\x20\x84" $ECHO -ne "\xcb\xb6\xd9\x3f\xeb\xfa\xa0\xfe\x9a\x7d\xee\x47\x98\xc4\xe7\xc4" $ECHO -ne "\xbd\xc6\xf0\x6d\xb2\x26\x10\x1e\x78\xef\xf3\x28\x3e\x35\xe6\xe4" $ECHO -ne "\xe6\xf3\x0f\x26\x34\x13\x85\xd0\xcf\x55\x0f\x8b\xd7\xe9\xf4\xdf" $ECHO -ne "\x70\x68\xc0\xb5\x30\x3c\xb1\x01\xe8\x28\xae\x80\x26\x01\x8b\x15" $ECHO -ne "\x0f\x80\x48\x18\x4b\xe2\xed\x59\x92\x31\xcf\xd2\x8f\x42\xbf\xee" $ECHO -ne "\xbd\x07\x91\x24\xc6\x66\x5e\x8c\x9a\x48\x63\xe7\xac\x8a\x1e\xc5" $ECHO -ne "\x69\x16\x8d\xac\x67\xdc\x75\x75\x82\xca\x19\x28\x36\x4d\x10\xf9" $ECHO -ne "\x41\xcb\x15\x05\x64\xc7\xb0\xc3\x64\xf3\x48\x71\x60\xf2\xbd\xcc" $ECHO -ne "\x37\xb1\x36\xbc\xa7\x2e\x6b\x20\x11\x51\x42\xe1\x8a\x29\xac\x44" $ECHO -ne "\x8f\x63\x56\x23\xd4\xd4\x07\xb4\x60\xa4\xb8\xcd\xee\x49\xa5\x42" $ECHO -ne "\xcc\x52\x00\x6f\xdc\x44\x20\x57\x7d\x36\xd7\x48\x1a\x22\x2c\xd0" $ECHO -ne "\x19\x43\x51\x5e\x1c\x8c\x5f\x70\xc2\x6b\xcf\xea\xd4\x97\x61\x72" $ECHO -ne "\x33\xc3\x9a\xd4\x06\xf1\x8a\x9a\xfe\x21\x83\x0b\xea\xf1\xfa\x2c" $ECHO -ne "\x52\x23\x2c\xb8\xc1\xe6\xc8\x9d\x9c\x5f\x8f\xf2\x4a\x86\x76\x92" $ECHO -ne "\x78\x0f\x7d\x9d\x09\x38\xce\xe1\x9a\xf3\x60\xed\x65\x0b\x1a\x68" $ECHO -ne "\xa6\x52\x39\x18\x1e\x45\xe3\x5d\xe0\x7d\xfb\xc6\xcc\x44\x18\x93" $ECHO -ne "\xe9\x71\xa8\x18\x0d\x74\x48\x8a\x18\x0b\x61\xbf\xe1\xa9\x0e\x4c" $ECHO -ne "\xad\x1b\xaf\x1a\x37\x39\x92\x4d\xcc\x96\x87\x46\x0d\x83\x06\x33" $ECHO -ne "\x53\x35\xd9\x2c\x36\x98\x28\x1c\x52\xb1\x89\x55\x56\xcc\x37\x20" $ECHO -ne "\x89\x84\x0e\x3d\x27\x2f\xc6\xfa\x78\x04\xe1\xd5\xc6\x90\x49\x16" $ECHO -ne "\xfe\x0a\x16\x6f\x11\x54\x42\x22\xa1\x90\x2d\x19\x91\x28\x05\xf2" $ECHO -ne "\x30\x6c\x14\x16\xd6\x8a\xce\xf6\xcd\x7c\x64\x76\x42\xe9\x28\xe9" $ECHO -ne "\x1c\xd1\xb8\x9e\xcd\x53\xb2\x6b\x8d\x57\x57\x2a\xb8\x59\x58\x8c" $ECHO -ne "\xd3\x12\x57\xa6\xe3\x48\x70\xf5\x55\x0f\x76\xb5\x27\x08\xd1\xa0" $ECHO -ne "\xf8\x60\x09\xa1\xf2\x30\x43\x4a\x30\x46\xf7\x96\x19\xe9\x3a\x44" $ECHO -ne "\xc0\xd8\xa8\x51\xae\x50\x92\x81\x81\xda\x10\xd3\x18\x62\x94\xd0" $ECHO -ne "\x9e\x54\x0b\x22\xcc\xd0\xfe\x0c\x36\x44\x4d\x4d\x40\x5c\xa8\x35" $ECHO -ne "\xb6\x53\x9c\x36\x9c\x5a\x0e\x0e\xb0\x5c\x29\x2a\x35\x66\xaa\x3a" $ECHO -ne "\xcb\x23\x7b\xbb\xc8\x60\xbc\xb4\x28\xf4\x6e\xfe\x86\xfc\x16\x85" $ECHO -ne "\x0c\xe0\x1d\xcf\xfd\x12\x28\xc6\x60\xd0\xe6\x2f\x76\xf0\x1a\x5b" $ECHO -ne "\xfa\xa6\xc6\xea\x58\xbb\x26\x37\x84\xdd\x85\xd5\x37\x82\x76\xd9" $ECHO -ne "\x14\x7a\xca\xed\x13\x72\xc3\xe1\xb9\x69\x45\xd4\xec\x44\x94\x26" $ECHO -ne "\x8e\x0b\x90\xb6\x8b\x1f\x1e\x01\x96\x5a\xb9\x51\xa6\x27\xa2\x9b" $ECHO -ne "\x38\xd9\x25\x32\x9b\x54\xfc\x45\xd1\xa8\x59\x35\x1a\xb0\xb2\x1a" $ECHO -ne "\xc8\x88\x15\x42\x98\x50\x99\x12\x9e\xf5\x59\xb2\x5c\xc5\xa7\x34" $ECHO -ne "\x35\xca\xb3\xed\xdc\xc9\x9f\x3e\x77\x8f\x6c\xde\xc8\x41\x6a\xc5" $ECHO -ne "\x24\x85\x04\xa1\x2f\xe3\x47\x8c\x47\xd4\xdb\x74\x8c\xb6\x4c\xef" $ECHO -ne "\xed\xad\x9f\x86\x31\xd8\xc8\x07\xc5\x11\x1c\x39\x3a\xf8\x75\x73" $ECHO -ne "\xae\x78\x7d\x1d\x36\x5b\xd1\x23\x5d\x84\x17\x5d\x4b\xac\xd3\x70" $ECHO -ne "\x8a\x83\x48\x48\x83\x7b\x5c\x99\x9e\x56\xbb\xfc\x0c\x4b\x04\xcf" $ECHO -ne "\x83\x5d\xf8\x31\x2c\xc4\x5c\xa1\x68\x6a\x56\xe1\x7f\xbe\xd6\x59" $ECHO -ne "\x6c\x55\xb0\x63\x41\xeb\x88\x69\xb6\x9b\x50\xc4\x31\xea\xb0\xd7" $ECHO -ne "\xe2\xfb\x7b\xeb\xbb\x52\xc4\x97\x23\xe9\x16\x29\x18\x50\x4d\x0e" $ECHO -ne "\x68\x62\xfb\x3f\xd9\x07\xb9\x89\x4d\x58\x7c\x32\x6d\x12\x3e\x9b" $ECHO -ne "\x3a\x14\xee\xac\x3c\x8d\x09\x62\x30\x8e\xe0\x86\x84\xb9\xf3\x0d" $ECHO -ne "\xf8\xad\x42\xa6\xbb\x7d\xd1\xf2\xf3\xc0\xe2\x32\xc4\x40\xaa\x8a" $ECHO -ne "\x2a\xe9\xa9\x45\x83\x23\xf6\x90\x05\x24\x59\x22\x84\x50\x82\xc0" $ECHO -ne "\x58\x41\x42\x18\x91\x3d\xd8\x80\xb1\x26\x68\xb2\xa8\xc0\x21\x14" $ECHO -ne "\x18\xdf\x3a\x86\x25\x85\x56\xab\x20\x38\xcd\xdc\x98\x6e\x07\xc4" $ECHO -ne "\x6b\x16\x55\xe0\x41\xe0\x41\xda\x29\x62\x8d\xba\xce\xa2\xcb\xfc" $ECHO -ne "\x70\x78\x99\xf9\x16\x0b\x5a\x0c\xc5\xad\x18\xeb\xf0\xb5\xc9\x25" $ECHO -ne "\x82\x16\xe0\x5d\xc1\xc4\xc6\xf0\x84\x6a\x45\x7d\xdb\x28\x46\xab" $ECHO -ne "\xef\x32\xc9\x49\x50\x51\x60\x77\x1c\xfd\x58\x9c\x01\x3b\x7a\xfa" $ECHO -ne "\x49\x47\x3e\x87\x1c\x39\xa6\x6a\xa4\xb7\x39\x93\xac\xac\xb0\x39" $ECHO -ne "\x2f\xbc\xab\x9b\x52\x96\x24\x46\xc1\x95\xe4\x31\x89\x37\x18\xc8" $ECHO -ne "\x2c\x22\x32\x2a\x8f\xb6\x58\x77\x57\x77\x2f\x09\xd0\x7c\xed\x74" $ECHO -ne "\xaa\x7c\x86\x25\x45\x0c\x43\x4d\x31\xb0\x63\x40\xcf\x86\xfe\x75" $ECHO -ne "\x76\xe0\xee\x99\xb5\x71\xe2\x4e\xe5\xc1\xf9\x2e\x48\xe2\xa6\x1b" $ECHO -ne "\x28\xa5\xa3\xbe\xff\x37\xd1\xdd\x66\xa2\xe8\xd3\x88\x4d\x13\xd5" $ECHO -ne "\x68\x51\x27\x41\xc3\x6c\x1b\x48\x67\x6a\xdf\x25\x2a\x40\xa1\x87" $ECHO -ne "\x1d\x54\xb7\xe3\x91\xc2\x6b\x5b\xb9\x8c\xd5\x10\x11\x10\x16\xab" $ECHO -ne "\x6b\xbe\x65\x6b\x73\xa7\x35\xa1\x09\x60\x60\xed\x96\x39\xc9\x40" $ECHO -ne "\x5d\xdc\xee\x60\x49\x0c\x68\x18\x34\xb2\x6f\x2a\x95\x14\x29\x95" $ECHO -ne "\x5b\x59\xd2\x1f\x63\x2a\xbe\xfd\xae\x09\x5c\xee\x11\xb5\x29\x36" $ECHO -ne "\xca\xdf\x28\x8c\x65\x42\x46\x74\x0c\x39\x68\x30\xac\x2c\x2f\xd0" $ECHO -ne "\x9b\xb3\x92\x19\x90\xa1\x07\xcc\xf6\xde\x64\x5f\x6f\xd7\xb6\xcc" $ECHO -ne "\xe0\x70\x0f\x0b\xd2\x0e\x77\xa1\x70\xe3\x56\x90\x4b\x28\x58\xd0" $ECHO -ne "\xd1\xe1\x9d\x18\x98\xba\x6b\x36\x54\xa9\x54\x09\x63\x49\x18\x55" $ECHO -ne "\x60\xba\x11\xb1\x0a\x14\x45\x1f\xae\x08\x50\x09\x33\x00\xa2\xb2" $ECHO -ne "\x71\x81\x75\x89\xb7\xb9\x0c\x73\xc0\x4c\x32\x89\x72\xac\xa9\xa3" $ECHO -ne "\x47\x5f\x7d\x4e\x1b\x4d\xb9\xea\x84\x45\x00\x37\x3c\xb3\x7b\xf8" $ECHO -ne "\xe7\x0f\xaa\x33\x1a\x9b\xc2\x0c\x35\x8a\xd4\x04\x46\x42\xcb\xab" $ECHO -ne "\xaa\xc7\xe5\xc9\x20\x6e\x21\xa6\x8c\xed\x61\x86\x42\x87\x03\x25" $ECHO -ne "\xde\x2c\x4a\x85\xcb\xb4\x36\xc9\xd4\x72\x60\x62\xc2\x19\xd0\x30" $ECHO -ne "\x16\x6d\x58\x61\x62\x16\xe8\xd2\x0e\xd0\xf3\xdb\x53\x37\x07\x37" $ECHO -ne "\x40\xc3\xe5\x5b\x9d\x16\x45\x60\x8e\xfb\x12\xc4\x5f\x9f\xdd\xe1" $ECHO -ne "\x45\x5d\x45\x36\x21\xa0\xc0\xb8\x11\x98\x0f\x64\x98\x67\x1c\x11" $ECHO -ne "\xa9\xa1\x65\x10\xb9\x22\x12\x91\x10\x9b\x10\x6f\x95\x2e\x34\x91" $ECHO -ne "\x64\x82\xa4\x05\x02\xfc\x4a\x9f\x9c\x4d\x6c\x8d\x67\x26\x90\x63" $ECHO -ne "\x04\x12\x6f\x0e\x55\x3c\x8e\xf2\x8d\xb4\x6b\x3d\xac\xcf\x84\x2e" $ECHO -ne "\x60\x0f\x40\x62\x88\x3a\xcf\xbd\xea\xad\x40\x4c\x29\xe1\xb5\xb6" $ECHO -ne "\x3e\x15\x86\xd5\xbe\xad\x27\xde\x2b\x32\xef\xcf\x97\x88\x8b\x17" $ECHO -ne "\x80\x43\x0e\x20\x79\x3b\x36\x73\xb8\xad\x12\x0e\x87\x59\x5f\xd3" $ECHO -ne "\x3c\x8c\x84\xc8\x54\x2b\x94\xc9\x2e\x36\x8b\x32\x48\xf1\xe3\x08" $ECHO -ne "\xf0\x36\xc0\xb8\xc2\xa2\xa9\xe2\x52\x02\xf1\x9b\xcb\xde\xcc\xb5" $ECHO -ne "\x5a\x6c\x05\x06\x31\x44\x41\x88\xa3\x05\x04\x16\x0d\x4a\x85\x20" $ECHO -ne "\x79\xda\x89\x82\x1d\x5f\x5a\x11\x88\x89\x06\x05\xf5\xf4\xed\x75" $ECHO -ne "\x62\x39\x37\x69\x11\x32\x3e\x8d\xe4\x60\x62\x52\xc9\xad\x82\x9a" $ECHO -ne "\x9a\x2f\x06\x41\x26\xb4\x48\x70\x39\x2b\x8a\xb1\x5a\x53\xc6\x48" $ECHO -ne "\x57\x17\xf5\xd8\x5a\xc6\x19\x83\x06\x0b\x9b\x04\xb8\xf5\xaf\x23" $ECHO -ne "\x45\x87\x48\x50\x6d\x16\xea\xb4\x20\xb8\x49\x92\x6b\x0c\x76\x14" $ECHO -ne "\x48\x53\xa1\x29\x74\xf6\xd7\x49\x44\x39\xba\xbd\x63\xa6\xf2\x81" $ECHO -ne "\x8f\x5b\x5e\x46\x0a\x34\x95\x31\xc0\xdd\x60\x50\xd6\x0a\xa6\x29" $ECHO -ne "\x3d\x36\x3a\xc7\xb8\xcf\x25\x5e\xf7\x82\x55\x88\xc2\x8b\x30\xd2" $ECHO -ne "\x97\x90\x49\x94\xde\xe5\xaa\xeb\x42\x8c\x94\x2a\xa0\x0d\x9b\xb5" $ECHO -ne "\x59\xbe\xcb\x35\xa3\x37\x54\x76\x35\x98\xcb\x1f\x13\x3f\x4a\xd1" $ECHO -ne "\x45\x87\x67\xed\x66\x02\x06\x49\x1c\x59\x51\x1f\x4d\x85\x03\x46" $ECHO -ne "\x65\x86\x4e\x4c\x6a\xd2\x24\x31\x5b\x6a\x3b\x19\x49\xe1\x83\x14" $ECHO -ne "\xc1\xf0\x56\x61\x93\x8b\x33\x13\x54\x6f\x78\x4c\xa0\x85\xf0\xb3" $ECHO -ne "\x17\xaa\xf2\x67\x02\x0c\x31\xed\x5e\x98\x02\x28\xc4\xe5\x87\xa2" $ECHO -ne "\x70\xd1\xd5\x9c\xf8\xec\x19\x88\xa9\x0d\x0e\x4a\xe3\x47\x83\x6e" $ECHO -ne "\xf7\x70\x3e\xa4\xa9\xc0\x4c\xfa\x2b\x3b\xd7\x6f\x96\xc3\x6d\x6d" $ECHO -ne "\x71\x2a\x8d\x62\xf1\xd3\xdd\xb0\x33\xd4\x67\x19\x0d\x30\x85\x3a" $ECHO -ne "\x06\x04\x85\x00\x48\x5d\x53\x35\xa0\x31\x56\x84\x82\xa5\xac\x22" $ECHO -ne "\x02\x44\xaf\x6d\xc0\x61\x59\x23\x96\x72\x5a\x81\x9e\x0c\xe5\x79" $ECHO -ne "\xda\xd0\x42\x5c\x89\xa5\x00\xec\x56\x41\x64\x8a\x11\x60\x79\xb1" $ECHO -ne "\xed\x55\x16\x54\xe6\x51\x03\x34\x14\x60\x31\xd2\xf0\x0b\xce\xf2" $ECHO -ne "\x4e\x4c\x45\x8c\xeb\x2a\x82\x3a\xa8\x52\xce\x8f\x4e\xf1\x89\xea" $ECHO -ne "\x44\x91\x66\xdd\x6b\x49\xa3\x83\x0b\x19\x0e\x66\x5f\x02\x22\x58" $ECHO -ne "\xe7\xc0\xa8\xce\x55\x48\xa6\x04\xf3\x03\xac\x62\xb2\xc0\xaa\xa0" $ECHO -ne "\x09\xae\x5b\x96\xd0\xdd\xa9\x1f\xfb\x2d\x3d\xf5\x02\xe1\x86\x02" $ECHO -ne "\x3e\xda\xd0\x5d\xba\x16\x39\xcd\x75\xa2\x47\x26\x74\x25\xa8\x5e" $ECHO -ne "\xf3\x36\x0c\x37\x19\x17\x06\x66\xd0\x0b\x42\x41\x0a\xa0\xde\x93" $ECHO -ne "\xd7\xb4\x9f\xfb\xc7\x4f\x65\x54\xda\xb8\x8b\x23\xde\x9c\x57\xcf" $ECHO -ne "\x2d\x2a\x12\xda\xcc\xf6\x73\x83\x02\x4c\x0e\x42\x88\xda\x27\xb9" $ECHO -ne "\xcb\x04\xb6\x07\x26\x78\xa1\xa1\x09\xa3\x6a\x86\xbd\x9d\xd4\xf9" $ECHO -ne "\xc0\x81\xa6\x49\xa9\x72\xeb\x56\xbd\xf9\xea\x89\x4f\xae\x72\x28" $ECHO -ne "\xb6\x57\x35\xbe\x94\xad\xc0\xff\x1e\xf2\x35\x24\xa0\x45\xd5\x09" $ECHO -ne "\xc0\xe0\x10\xd0\x17\x90\xe2\xff\x8b\xb9\x22\x9c\x28\x48\x09\x68" $ECHO -ne "\xc3\x18\x00" } tar2_bz2() { $ECHO -ne "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x16\x08\xfd\x60\x00\x00" $ECHO -ne "\x01\xff\x80\x48\x80\x00\xa0\x40\x03\xff\xc0\x72\x00\x89\x40\xff" $ECHO -ne "\xe7\xdf\xc0\x20\x00\x92\x11\x53\xf5\x13\xd3\x53\x7a\x41\xa8\xf2" $ECHO -ne "\x9a\x60\x21\x9a\x40\xf4\x9b\xd2\x0d\x09\xa5\x3c\xa6\x4f\x44\xf5" $ECHO -ne "\x34\x34\xd1\xb5\x01\xa0\x3d\x41\xa4\xe1\xeb\x4c\x5a\x01\x47\x3b" $ECHO -ne "\xd1\x67\x1a\x4c\x3b\x21\x84\x23\x2c\x5c\xf7\xe0\xbd\x2a\xa4\xea" $ECHO -ne "\xdc\xdb\x71\x80\x26\x98\x21\x0e\x76\x21\x30\xce\xe4\xad\x8c\xb5" $ECHO -ne "\x68\x62\x35\xa1\xfd\x8e\x7b\x51\x70\x96\xb1\x2c\xa2\x99\x6c\xa1" $ECHO -ne "\xc2\xcd\xea\xa7\x5e\x6b\x91\x4f\x73\x96\xe4\x48\x3c\xe7\x8c\x0f" $ECHO -ne "\x03\x64\x5b\x7a\x43\xc1\x68\x86\x41\x83\x46\x0b\xba\xaa\x6a\x9b" $ECHO -ne "\x59\x34\xf1\x1c\x08\x69\x1d\x41\xfb\x4a\x96\x1b\x14\x9e\x32\x89" $ECHO -ne "\x69\x5f\x63\x9a\x22\xe4\x96\x34\xff\x12\x20\xd0\x25\x70\xdc\x5d" $ECHO -ne "\xc9\x14\xe1\x42\x40\x58\x23\xf5\x80" } # NB: tar emits "tar: short read" on stderr because these test tars are # also lacking proper terminating zeroed blocks. But exitcode is 0. # This is intended. export TZ=UTC-1 # Case 1: long name, with path in prefix field res1='-rw-r--r-- fm3/users 9869 2007-03-12 10:44:54 VirtualBox-1.5.6_OSE/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/ipcITransactionService.idl' t=`tar1_bz2 | bunzip2 | busybox tar tvf -` test x"$res1" = x"$t" t=`tar1_bz2 | bunzip2 | busybox tar tv` test x"$res1" = x"$t" # Case 2: long dir name, with ENTIRE path in prefix field (name = "") res2='drwxr-xr-x fm3/users 0 2008-02-19 16:33:20 VirtualBox-1.5.6_OSE/src/VBox/Additions/linux/x11include/4.3/programs/Xserver/hw/xfree86/xf24_32bpp/' t=`tar2_bz2 | bunzip2 | busybox tar tvf -` test x"$res2" = x"$t" t=`tar2_bz2 | bunzip2 | busybox tar tv` test x"$res2" = x"$t" ��������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar-complains-about-missing-file���������������������������������������0000644�0000000�0000000�00000000072�12263563520�023000� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo tar cf foo.tar foo ! busybox tar xf foo.tar bar ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar_with_link_with_size������������������������������������������������0000644�0000000�0000000�00000003231�12263563520�021456� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_TAR_UNAME_GNAME # This tarball contains a softlink with size field != 0. # If not ignored, it makes hext header to be skipped # and data to be read as a header. # GNU tar 1.15.1 has a bug here: tf won't work, but xf will. tar1_bz2() { $ECHO -ne "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x14\x44\xe3\xdd\x00\x00" $ECHO -ne "\x9a\xfb\x90\xca\x18\x00\xc0\x40\x03\xff\x80\x08\x00\x7b\xe3\xff" $ECHO -ne "\x80\x04\x00\x00\x08\x30\x00\xd6\xb3\x09\x45\x19\x0d\x0d\x41\x84" $ECHO -ne "\x1a\x68\xd0\x7a\x99\x90\x4a\x0a\x6d\x4c\xa3\x20\x7a\x41\xa0\x00" $ECHO -ne "\x00\x55\x25\x34\x1a\x34\xd0\x00\x64\x64\x1a\x32\x3f\x76\x3c\x1c" $ECHO -ne "\xd3\x3c\xa0\x84\x9b\x88\x05\x70\x90\xbb\x18\x28\x39\x29\xb3\x30" $ECHO -ne "\xa8\x0a\x21\x70\x0c\x01\x32\x3b\xbe\xde\xd7\x13\x2e\xbd\x2a\x9c" $ECHO -ne "\xa8\x42\x2a\x91\x15\xe2\xa1\xcd\x24\x37\x9c\x91\xaa\xc7\x14\xdb" $ECHO -ne "\x4c\x08\xaa\xaf\x12\xeb\x6c\x37\x96\xb0\xa4\x25\x0c\xb4\x4b\xc5" $ECHO -ne "\x52\x70\x3b\x25\x4c\x0e\x46\x67\x51\x54\x89\x13\x13\xf0\xa8\xe9" $ECHO -ne "\x68\x4e\x8c\x81\xfc\x79\xe0\xb0\xd8\x79\x34\x94\x71\xa2\x0c\xbe" $ECHO -ne "\x93\x61\x82\x95\x10\x88\xd1\xa6\x69\xaa\x38\x9c\xb6\xc2\xb2\x94" $ECHO -ne "\x90\xc3\x82\x29\xe8\x8c\xb8\x95\x83\x32\x40\x61\x11\x11\xd3\xaa" $ECHO -ne "\x3f\x8b\xb9\x22\x9c\x28\x48\x0a\x22\x71\xee\x80" } res1="\ lrwxrwxrwx user/group 0 2008-07-19 15:02:37 firmware-372/sources/native/bin/chroot-setup.sh -> qemu-setup.sh -rwxr-xr-x user/group 512 2008-07-19 15:02:37 firmware-372/sources/native/bin/qemu-setup.sh" export TZ=UTC-2 t=`tar1_bz2 | bunzip2 | busybox tar tvf -` test x"$res1" = x"$t" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar-extracts-from-standard-input���������������������������������������0000644�0000000�0000000�00000000114�12263563520�023045� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo tar cf foo.tar foo rm foo cat foo.tar | busybox tar x test -f foo ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar-handles-exclude-and-extract-lists����������������������������������0000644�0000000�0000000�00000000354�12263563520�023733� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_TAR_FROM # FEATURE: CONFIG_FEATURE_TAR_CREATE touch foo bar baz tar cf foo.tar foo bar baz echo foo >foo.exclude rm foo bar baz busybox tar xf foo.tar foo bar -X foo.exclude test ! -f foo -a -f bar -a ! -f baz ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar-extracts-multiple-files��������������������������������������������0000644�0000000�0000000�00000000140�12263563520�022101� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo bar tar cf foo.tar foo bar rm foo bar busybox tar -xf foo.tar test -f foo test -f bar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar-extracts-all-subdirs�����������������������������������������������0000644�0000000�0000000�00000000475�12263563520�021402� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_TAR_CREATE mkdir -p foo/{1,2,3} mkdir -p foo/1/{10,11} mkdir -p foo/1/10/{100,101,102} tar cf foo.tar -C foo . rm -rf foo/* busybox tar xf foo.tar -C foo ./1/10 find foo | sort >logfile.bb rm -rf foo/* tar xf foo.tar -C foo ./1/10 find foo | sort >logfile.gnu diff -u logfile.gnu logfile.bb ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar-handles-multiple-X-options�����������������������������������������0000644�0000000�0000000�00000000373�12263563520�022470� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_TAR_FROM # FEATURE: CONFIG_FEATURE_TAR_CREATE touch foo touch bar tar cf foo.tar foo bar echo foo > foo.exclude echo bar > bar.exclude rm foo bar busybox tar xf foo.tar -X foo.exclude -X bar.exclude test ! -f foo -a ! -f bar ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar-demands-at-most-one-ctx��������������������������������������������0000644�0000000�0000000�00000000021�12263563520�021661� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������! busybox tar tx ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar-extracts-file������������������������������������������������������0000644�0000000�0000000�00000000107�12263563520�020070� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo tar cf foo.tar foo rm foo busybox tar xf foo.tar test -f foo ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar-handles-cz-options�������������������������������������������������0000644�0000000�0000000�00000000207�12263563520�021040� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_TAR_CREATE # FEATURE: CONFIG_FEATURE_SEAMLESS_GZ touch foo busybox tar czf foo.tar.gz foo gzip -d foo.tar.gz �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar-handles-nested-exclude���������������������������������������������0000644�0000000�0000000�00000000344�12263563520�021646� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_TAR_FROM # FEATURE: CONFIG_FEATURE_TAR_CREATE mkdir foo touch foo/bar tar cf foo.tar foo rm -rf foo echo foo/bar >foobar.exclude busybox tar xf foo.tar foo -X foobar.exclude test -d foo -a ! -f foo/bar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar-extracts-to-standard-output����������������������������������������0000644�0000000�0000000�00000000113�12263563520�022724� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo foo > foo tar cf foo.tar foo cat foo.tar | busybox tar Ox | cmp foo - �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list�������������������0000644�0000000�0000000�00000000241�12263563520�026574� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_TAR_FROM # FEATURE: CONFIG_FEATURE_TAR_CREATE touch foo tar cf foo.tar foo echo foo >foo.exclude busybox tar xf foo.tar -X foo.exclude ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/basename/������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�015572� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/basename/basename-works����������������������������������������������������0000644�0000000�0000000�00000000067�12263563520�020442� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x$(basename $(pwd)) = x$(busybox basename $(pwd)) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/basename/basename-does-not-remove-identical-extension����������������������0000644�0000000�0000000�00000000050�12263563520�026254� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test xfoo = x`busybox basename foo foo` ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/unexpand.tests�������������������������������������������������������������0000755�0000000�0000000�00000002115�12263563520�016733� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2008 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh test -f "$bindir/.config" && . "$bindir/.config" # testing "test name" "options" "expected result" "file input" "stdin" testing "unexpand case 1" "unexpand" \ "\t12345678\n" "" " 12345678\n" \ testing "unexpand case 2" "unexpand" \ "\t 12345678\n" "" " 12345678\n" \ testing "unexpand case 3" "unexpand" \ "\t 12345678\n" "" " 12345678\n" \ testing "unexpand case 4" "unexpand" \ "\t12345678\n" "" " \t12345678\n" \ testing "unexpand case 5" "unexpand" \ "\t12345678\n" "" " \t12345678\n" \ testing "unexpand case 6" "unexpand" \ "\t12345678\n" "" " \t12345678\n" \ testing "unexpand case 7" "unexpand" \ "123\t 45678\n" "" "123 \t 45678\n" \ testing "unexpand case 8" "unexpand" \ "a b\n" "" "a b\n" \ test x"$CONFIG_UNICODE_SUPPORT" = x"y" \ && test x"$CONFIG_UNICODE_USING_LOCALE" != x"y" \ && testing "unexpand with unicode characher 0x394" "unexpand" \ "1ΔΔΔ5\t99999\n" "" "1ΔΔΔ5 99999\n" exit $FAILCOUNT ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ln/������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014430� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ln/ln-preserves-soft-links�������������������������������������������������0000644�0000000�0000000�00000000205�12263563520�021070� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo file number one > file1 echo file number two > link1 set +e busybox ln -s file1 link1 if [ $? != 0 ] ; then exit 0; fi exit 1; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ln/ln-creates-hard-links���������������������������������������������������0000644�0000000�0000000�00000000120�12263563520�020437� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo file number one > file1 busybox ln file1 link1 test -f file1 test -f link1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ln/ln-force-creates-hard-links���������������������������������������������0000644�0000000�0000000�00000000160�12263563520�021537� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo file number one > file1 echo file number two > link1 busybox ln -f file1 link1 test -f file1 test -f link1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ln/ln-preserves-hard-links�������������������������������������������������0000644�0000000�0000000�00000000202�12263563520�021030� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo file number one > file1 echo file number two > link1 set +e busybox ln file1 link1 if [ $? != 0 ] ; then exit 0; fi exit 1; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ln/ln-creates-soft-links���������������������������������������������������0000644�0000000�0000000�00000000145�12263563520�020503� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo file number one > file1 busybox ln -s file1 link1 test -L link1 test xfile1 = x`readlink link1` ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ln/ln-force-creates-soft-links���������������������������������������������0000644�0000000�0000000�00000000205�12263563520�021574� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo file number one > file1 echo file number two > link1 busybox ln -f -s file1 link1 test -L link1 test xfile1 = x`readlink link1` �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/md5sum/��������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�015231� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/md5sum/md5sum-verifies-non-binary-file�������������������������������������0000644�0000000�0000000�00000000137�12263563520�023174� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_MD5_SHA1_SUM_CHECK touch foo md5sum foo > bar busybox md5sum -c bar ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/sed.tests������������������������������������������������������������������0000755�0000000�0000000�00000025625�12263563520�015677� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # SUSv3 compliant sed tests. # Copyright 2005 by Rob Landley <rob@landley.net> # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "description" "commands" "result" "infile" "stdin" # Corner cases testing "sed no files (stdin)" 'sed ""' "hello\n" "" "hello\n" testing "sed explicit stdin" 'sed "" -' "hello\n" "" "hello\n" testing "sed handles empty lines" "sed -e 's/\$/@/'" "@\n" "" "\n" testing "sed stdin twice" 'sed "" - -' "hello" "" "hello" # Trailing EOF. # Match $, at end of each file or all files? # -e corner cases # without -e # multiple -e # interact with a # -eee arg1 arg2 arg3 # -f corner cases # -e -f -e # -n corner cases # no newline at EOF? # -r corner cases # Just make sure it works. # -i corner cases: # sed -i - # permissions # -i on a symlink # on a directory # With $ last-line test # Continue with \ # End of script with trailing \ # command list testing "sed accepts blanks before command" "sed -e '1 d'" "" "" "" testing "sed accepts newlines in -e" "sed -e 'i\ 1 a\ 3'" "1\n2\n3\n" "" "2\n" testing "sed accepts multiple -e" "sed -e 'i\' -e '1' -e 'a\' -e '3'" \ "1\n2\n3\n" "" "2\n" # substitutions testing "sed -n" "sed -n -e s/foo/bar/ -e s/bar/baz/" "" "" "foo\n" testing "sed with empty match" "sed 's/z*//g'" "string\n" "" "string\n" testing "sed s//p" "sed -e s/foo/bar/p -e s/bar/baz/p" "bar\nbaz\nbaz\n" \ "" "foo\n" testing "sed -n s//p" "sed -ne s/abc/def/p" "def\n" "" "abc\n" testing "sed s//g (exhaustive)" "sed -e 's/[[:space:]]*/,/g'" ",1,2,3,4,5,\n" \ "" "12345\n" testing "sed s arbitrary delimiter" "sed -e 's woo boing '" "boing\n" "" "woo\n" testing "sed s chains" "sed -e s/foo/bar/ -e s/bar/baz/" "baz\n" "" "foo\n" testing "sed s chains2" "sed -e s/foo/bar/ -e s/baz/nee/" "bar\n" "" "foo\n" testing "sed s [delimiter]" "sed -e 's@[@]@@'" "onetwo" "" "one@two" testing "sed s with \\t (GNU ext)" "sed 's/\t/ /'" "one two" "" "one\ttwo" # branch testing "sed b (branch)" "sed -e 'b one;p;: one'" "foo\n" "" "foo\n" testing "sed b (branch with no label jumps to end)" "sed -e 'b;p'" \ "foo\n" "" "foo\n" # test and branch testing "sed t (test/branch)" "sed -e 's/a/1/;t one;p;: one;p'" \ "1\n1\nb\nb\nb\nc\nc\nc\n" "" "a\nb\nc\n" testing "sed t (test/branch clears test bit)" "sed -e 's/a/b/;:loop;t loop'" \ "b\nb\nc\n" "" "a\nb\nc\n" testing "sed T (!test/branch)" "sed -e 's/a/1/;T notone;p;: notone;p'" \ "1\n1\n1\nb\nb\nc\nc\n" "" "a\nb\nc\n" test x"$SKIP_KNOWN_BUGS" = x"" && { # Normal sed end-of-script doesn't print "c" because n flushed the pattern # space. If n hits EOF, pattern space is empty when script ends. # Query: how does this interact with no newline at EOF? testing "sed n (flushes pattern space, terminates early)" "sed -e 'n;p'" \ "a\nb\nb\nc\n" "" "a\nb\nc\n" } # non-GNU sed: N does _not_ flush pattern space, therefore c is eaten @ script end # GNU sed: N flushes pattern space, therefore c is printed too @ script end testing "sed N (flushes pattern space (GNU behavior))" "sed -e 'N;p'" \ "a\nb\na\nb\nc\n" "" "a\nb\nc\n" testing "sed N test2" "sed ':a;N;s/\n/ /;ta'" \ "a b c\n" "" "a\nb\nc\n" testing "sed N test3" "sed 'N;s/\n/ /'" \ "a b\nc\n" "" "a\nb\nc\n" testing "sed address match newline" 'sed "/b/N;/b\\nc/i woo"' \ "a\nwoo\nb\nc\nd\n" "" "a\nb\nc\nd\n" # Multiple lines in pattern space testing "sed N (stops at end of input) and P (prints to first newline only)" \ "sed -n 'N;P;p'" "a\na\nb\n" "" "a\nb\nc\n" # Hold space testing "sed G (append hold space to pattern space)" 'sed G' "a\n\nb\n\nc\n\n" \ "" "a\nb\nc\n" #testing "sed g/G (swap/append hold and patter space)" #testing "sed g (swap hold/pattern space)" testing "sed d ends script iteration" \ "sed -e '/ook/d;s/ook/ping/p;i woot'" "" "" "ook\n" testing "sed d ends script iteration (2)" \ "sed -e '/ook/d;a\' -e 'bang'" "woot\nbang\n" "" "ook\nwoot\n" # Multiple files, with varying newlines and NUL bytes test x"$SKIP_KNOWN_BUGS" = x"" && { testing "sed embedded NUL" "sed -e 's/woo/bang/'" "\0bang\0woo\0" "" \ "\0woo\0woo\0" } testing "sed embedded NUL g" "sed -e 's/woo/bang/g'" "bang\0bang\0" "" \ "woo\0woo\0" test x"$SKIP_KNOWN_BUGS" = x"" && { $ECHO -e "/woo/a he\0llo" > sed.commands testing "sed NUL in command" "sed -f sed.commands" "woo\nhe\0llo\n" "" "woo" rm sed.commands } # sed has funky behavior with newlines at the end of file. Test lots of # corner cases with the optional newline appending behavior. testing "sed normal newlines" "sed -e 's/woo/bang/' input -" "bang\nbang\n" \ "woo\n" "woo\n" testing "sed leave off trailing newline" "sed -e 's/woo/bang/' input -" \ "bang\nbang" "woo\n" "woo" testing "sed autoinsert newline" "sed -e 's/woo/bang/' input -" "bang\nbang" \ "woo" "woo" testing "sed empty file plus cat" "sed -e 's/nohit//' input -" "one\ntwo" \ "" "one\ntwo" testing "sed cat plus empty file" "sed -e 's/nohit//' input -" "one\ntwo" \ "one\ntwo" "" test x"$SKIP_KNOWN_BUGS" = x"" && { testing "sed append autoinserts newline" "sed -e '/woot/a woo' -" \ "woot\nwoo\n" "" "woot" } testing "sed insert doesn't autoinsert newline" "sed -e '/woot/i woo' -" \ "woo\nwoot" "" "woot" testing "sed print autoinsert newlines" "sed -e 'p' -" "one\none" "" "one" testing "sed print autoinsert newlines two files" "sed -e 'p' input -" \ "one\none\ntwo\ntwo" "one" "two" testing "sed noprint, no match, no newline" "sed -ne 's/woo/bang/' input" \ "" "no\n" "" testing "sed selective matches with one nl" "sed -ne 's/woo/bang/p' input -" \ "a bang\nc bang\n" "a woo\nb no" "c woo\nd no" testing "sed selective matches insert newline" \ "sed -ne 's/woo/bang/p' input -" "a bang\nb bang\nd bang" \ "a woo\nb woo" "c no\nd woo" testing "sed selective matches noinsert newline" \ "sed -ne 's/woo/bang/p' input -" "a bang\nb bang" "a woo\nb woo" \ "c no\nd no" testing "sed clusternewline" \ "sed -e '/one/a 111' -e '/two/i 222' -e p input -" \ "one\none\n111\n222\ntwo\ntwo" "one" "two" testing "sed subst+write" \ "sed -e 's/i/z/' -e 'woutputw' input -; $ECHO -n X; cat outputw" \ "thzngy\nagaznXthzngy\nagazn" "thingy" "again" rm outputw testing "sed trailing NUL" \ "sed 's/i/z/' input -" \ "a\0b\0\nc" "a\0b\0" "c" testing "sed escaped newline in command" \ "sed 's/a/z\\ z/' input" \ "z\nz" "a" "" # Test end-of-file matching behavior testing "sed match EOF" "sed -e '"'$p'"'" "hello\nthere\nthere" "" \ "hello\nthere" testing "sed match EOF two files" "sed -e '"'$p'"' input -" \ "one\ntwo\nthree\nfour\nfour" "one\ntwo" "three\nfour" # sed match EOF inline: gnu sed 4.1.5 outputs this: #00000000 6f 6e 65 0a 6f 6f 6b 0a 6f 6f 6b 0a 74 77 6f 0a |one.ook.ook.two.| #00000010 0a 74 68 72 65 65 0a 6f 6f 6b 0a 6f 6f 6b 0a 66 |.three.ook.ook.f| #00000020 6f 75 72 |our| # which looks buggy to me. $ECHO -ne "three\nfour" > input2 testing "sed match EOF inline" \ "sed -e '"'$i ook'"' -i input input2 && cat input input2" \ "one\nook\ntwothree\nook\nfour" "one\ntwo" "" rm input2 # Test lie-to-autoconf testing "sed lie-to-autoconf" "sed --version | grep -o 'GNU sed version '" \ "GNU sed version \n" "" "" # Jump to nonexistent label test x"$SKIP_KNOWN_BUGS" = x"" && { # Incompatibility: illegal jump is not detected if input is "" # (that is, no lines at all). GNU sed 4.1.5 complains even in this case testing "sed nonexistent label" "sed -e 'b walrus' 2>/dev/null || echo yes" \ "yes\n" "" "" } testing "sed backref from empty s uses range regex" \ "sed -e '/woot/s//eep \0 eep/'" "eep woot eep" "" "woot" testing "sed backref from empty s uses range regex with newline" \ "sed -e '/woot/s//eep \0 eep/'" "eep woot eep\n" "" "woot\n" # -i with no filename touch ./- # Detect gnu failure mode here. testing "sed -i with no arg [GNUFAIL]" "sed -e '' -i 2> /dev/null || echo yes" \ "yes\n" "" "" rm ./- # Clean up testing "sed s/xxx/[/" "sed -e 's/xxx/[/'" "[\n" "" "xxx\n" # Ponder this a bit more, why "woo not found" from gnu version? #testing "sed doesn't substitute in deleted line" \ # "sed -e '/ook/d;s/ook//;t woo;a bang;'" "bang" "" "ook\n" # This makes both seds very unhappy. Why? #testing "sed -g (exhaustive)" "sed -e 's/[[:space:]]*/,/g'" ",1,2,3,4,5," \ # "" "12345" # testing "description" "commands" "result" "infile" "stdin" testing "sed n command must reset 'substituted' bit" \ "sed 's/1/x/;T;n;: next;s/3/y/;t quit;n;b next;: quit;q'" \ "0\nx\n2\ny\n" "" "0\n1\n2\n3\n" testing "sed d does not break n,m matching" \ "sed -n '1d;1,3p'" \ "second\nthird\n" "" "first\nsecond\nthird\nfourth\n" testing "sed d does not break n,regex matching" \ "sed -n '1d;1,/hir/p'" \ "second\nthird\n" "" "first\nsecond\nthird\nfourth\n" testing "sed d does not break n,regex matching #2" \ "sed -n '1,5d;1,/hir/p'" \ "second2\nthird2\n" "" \ "first\nsecond\nthird\nfourth\n""first2\nsecond2\nthird2\nfourth2\n" testing "sed 2d;2,1p (gnu compat)" \ "sed -n '2d;2,1p'" \ "third\n" "" \ "first\nsecond\nthird\nfourth\n" # Regex means: "match / at BOL or nothing, then one or more not-slashes". # The bug was that second slash in /usr/lib was treated as "at BOL" too. testing "sed beginning (^) matches only once" \ "sed 's,\(^/\|\)[^/][^/]*,>\0<,g'" \ ">/usr</>lib<\n" "" \ "/usr/lib\n" testing "sed c" \ "sed 'crepl'" \ "repl\nrepl\n" "" \ "first\nsecond\n" testing "sed nested {}s" \ "sed '/asd/ { p; /s/ { s/s/c/ }; p; q }'" \ "qwe\nasd\nacd\nacd\n" "" \ "qwe\nasd\nzxc\n" testing "sed a cmd ended by double backslash" \ "sed -e '/| one /a \\ | three \\\\' -e '/| one-/a \\ | three-* \\\\'" \ ' | one \\ | three \\ | two \\ ' '' \ ' | one \\ | two \\ ' # first three lines are deleted; 4th line is matched and printed by "2,3" and by "4" ranges testing "sed with N skipping lines past ranges on next cmds" \ "sed -n '1{N;N;d};1p;2,3p;3p;4p'" \ "4\n4\n" "" "1\n2\n3\n4\n" testing "sed -i with address modifies all files, not only first" \ "cp input input2; sed -i -e '1s/foo/bar/' input input2 && cat input input2; rm input2" \ "bar\nbar\n" "foo\n" "" testing "sed understands \r" \ "sed 's/r/\r/'" \ "\rrr\n" "" "rrr\n" testing "sed -i finishes ranges correctly" \ "sed '1,2d' -i input; echo \$?; cat input" \ "0\n3\n4\n" "1\n2\n3\n4\n" "" testing "sed zero chars match/replace advances correctly 1" \ "sed 's/l*/@/g'" \ "@h@e@o@\n" "" "helllo\n" testing "sed zero chars match/replace advances correctly 2" \ "sed 's [^ .]* x g'" \ "x x.x\n" "" " a.b\n" testing "sed zero chars match/replace logic must not falsely trigger here 1" \ "sed 's/a/A/g'" \ "_AAA1AA\n" "" "_aaa1aa\n" testing "sed zero chars match/replace logic must not falsely trigger here 2" \ "sed 's/ *$/_/g'" \ "qwerty_\n" "" "qwerty\n" testing "sed /\$_in_regex/ should not match newlines, only end-of-line" \ "sed ': testcont; /\\\\$/{ =; N; b testcont }'" \ "\ this is a regular line 2 line with \\ continuation more regular lines 5 line with \\ continuation " \ "" "\ this is a regular line line with \\ continuation more regular lines line with \\ continuation " # testing "description" "commands" "result" "infile" "stdin" exit $FAILCOUNT �����������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/gunzip.tests���������������������������������������������������������������0000755�0000000�0000000�00000000035�12263563520�016424� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh . ./bunzip2.tests ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tr.tests�������������������������������������������������������������������0000755�0000000�0000000�00000001544�12263563520�015543� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2009 by Denys Vlasenko <vda.linux@googlemail.com> # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "description" "arguments" "result" "infile" "stdin" testing "tr does not treat [] in [a-z] as special" \ "tr '[q-z]' '_Q-Z+'" \ "_QWe+" "" "[qwe]" testing "tr understands 0-9A-F" \ "tr -cd '[0-9A-F]'" \ "19AF" "" "19AFH\n" optional FEATURE_TR_CLASSES testing "tr understands [:xdigit:]" \ "tr -cd '[:xdigit:]'" \ "19AF" "" "19AFH\n" SKIP= optional FEATURE_TR_CLASSES testing "tr does not stop after [:digit:]" \ "tr '[:digit:]y-z' 111111111123" \ "111abcx23\n" "" "789abcxyz\n" SKIP= optional FEATURE_TR_CLASSES testing "tr has correct xdigit sequence" \ "tr '[:xdigit:]Gg' 1111111151242222333330xX" \ "#1111111151242222x333330X\n" "" \ "#0123456789ABCDEFGabcdefg\n" SKIP= exit $FAILCOUNT ������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/echo/����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014735� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/echo/echo-prints-slash_041�������������������������������������������������0000644�0000000�0000000�00000000161�12263563520�020611� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_FANCY_ECHO test "`busybox echo -ne '\041z' | od -t x1 | head -n 1`" = "0000000 21 7a" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/echo/echo-prints-newline���������������������������������������������������0000644�0000000�0000000�00000000047�12263563520�020557� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test `busybox echo word | wc -c` -eq 5 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/echo/echo-prints-argument��������������������������������������������������0000644�0000000�0000000�00000000044�12263563520�020735� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test xfubar = x`busybox echo fubar` ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/echo/echo-prints-non-opts��������������������������������������������������0000644�0000000�0000000�00000000121�12263563520�020664� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test "`busybox echo -neEZ | od -t x1 | head -n 1`" = "0000000 2d 6e 65 45 5a 0a" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/echo/echo-prints-slash_00041�����������������������������������������������0000644�0000000�0000000�00000000166�12263563520�020756� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_FANCY_ECHO test "`busybox echo -ne '\00041z' | od -t x1 | head -n 1`" = "0000000 04 31 7a" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/echo/echo-does-not-print-newline�������������������������������������������0000644�0000000�0000000�00000000120�12263563520�022112� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_FANCY_ECHO test `busybox echo -n word | wc -c` -eq 4 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/echo/echo-prints-slash-zero������������������������������������������������0000644�0000000�0000000�00000000176�12263563520�021210� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_FANCY_ECHO test "`busybox echo -e -n 'msg\n\0' | od -t x1 | head -n 1`" = "0000000 6d 73 67 0a 00" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/echo/echo-prints-slash_0041������������������������������������������������0000644�0000000�0000000�00000000162�12263563520�020672� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_FANCY_ECHO test "`busybox echo -ne '\0041z' | od -t x1 | head -n 1`" = "0000000 21 7a" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/echo/echo-prints-slash_41��������������������������������������������������0000644�0000000�0000000�00000000160�12263563520�020530� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_FANCY_ECHO test "`busybox echo -ne '\41z' | od -t x1 | head -n 1`" = "0000000 21 7a" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/echo/echo-prints-dash������������������������������������������������������0000644�0000000�0000000�00000000101�12263563520�020024� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test "`busybox echo - | od -t x1 | head -n 1`" = "0000000 2d 0a" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/echo/echo-prints-arguments�������������������������������������������������0000644�0000000�0000000�00000000052�12263563520�021117� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test "`busybox echo foo bar`" = "foo bar" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cal.tests������������������������������������������������������������������0000755�0000000�0000000�00000001662�12263563520�015656� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2010 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh test -f "$bindir/.config" && . "$bindir/.config" # testing "test name" "command" "expected result" "file input" "stdin" testing "cal 2000" "cal 1 2000" "\ January 2000 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 " "" "" test x"$CONFIG_LOCALE_SUPPORT" = x"y" \ && test x"$CONFIG_UNICODE_SUPPORT" = x"y" \ && test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"0" \ && test x"$CONFIG_UNICODE_WIDE_WCHARS" = x"y" \ && test x"$CONFIG_STATIC" != x"y" \ && test x"$CONFIG_CROSS_COMPILER_PREFIX" = x"" \ && testing "unicode cal 2000" "LANG=zh_TW.utf8 cal 1 2000" "\ 一月 2000 æ—¥ 一 二 三 å›› 五 å…­ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 " "" "" exit $FAILCOUNT ������������������������������������������������������������������������������busybox-1.22.1/testsuite/fold.tests�����������������������������������������������������������������0000755�0000000�0000000�00000005267�12263563520�016050� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2009 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh test -f "$bindir/.config" && . "$bindir/.config" # testing "test name" "options" "expected result" "file input" "stdin" testing "fold -s" "fold -w 7 -s" \ "123456\n\t\nasdf" \ "" \ "123456\tasdf" \ testing "fold -w1" "fold -w1" \ "q\nq\n \nw\n \ne\ne\ne\n \nr\n \nt\nt\nt\nt\n \ny" \ "" \ "qq w eee r tttt y" \ testing "fold with NULs" "fold -sw22" \ "\ The NUL is here:>\0< \n\ and another one is \n\ here:>\0< - they must \n\ be preserved " \ "" \ "The NUL is here:>\0< and another one \ is here:>\0< - they must be preserved " \ # The text was taken from English and Ukrainian wikipedia pages test x"$CONFIG_UNICODE_SUPPORT" = x"y" \ && test x"$CONFIG_UNICODE_USING_LOCALE" != x"y" \ && testing "fold -sw66 with unicode input" "fold -sw66" \ "\ The Andromeda Galaxy (pronounced /ænˈdrÉ’mÉ™dÉ™/, also known as \n\ Messier 31, M31, or NGC224; often referred to as the Great \n\ Andromeda Nebula in older texts) is a spiral galaxy approximately \n\ 2,500,000 light-years (1.58×10^11 AU) away in the constellation \n\ Andromeda. It is the nearest spiral galaxy to our own, the Milky \n\ Way.\n\ Галактика або ТуманніÑÑ‚ÑŒ Ðндромеди (також відома Ñк M31 за \n\ каталогом МеÑÑьє та NGC224 за Ðовим загальним каталогом) — \n\ Ñпіральна галактика, що знаходитьÑÑ Ð½Ð° відÑтані приблизно у 2,5 \n\ мільйони Ñвітлових років від нашої планети у Ñузір'Ñ— Ðндромеди. \n\ Ðа початку ХХІ ÑÑ‚. в центрі галактики виÑвлено чорну дірку." \ "" \ "\ The Andromeda Galaxy (pronounced /ænˈdrÉ’mÉ™dÉ™/, also known as \ Messier 31, M31, or NGC224; often referred to as the Great \ Andromeda Nebula in older texts) is a spiral galaxy approximately \ 2,500,000 light-years (1.58×10^11 AU) away in the constellation \ Andromeda. It is the nearest spiral galaxy to our own, the Milky \ Way. Галактика або ТуманніÑÑ‚ÑŒ Ðндромеди (також відома Ñк M31 за \ каталогом МеÑÑьє та NGC224 за Ðовим загальним каталогом) — \ Ñпіральна галактика, що знаходитьÑÑ Ð½Ð° відÑтані приблизно у 2,5 \ мільйони Ñвітлових років від нашої планети у Ñузір'Ñ— Ðндромеди. \ Ðа початку ХХІ ÑÑ‚. в центрі галактики виÑвлено чорну дірку." exit $FAILCOUNT �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/hostname/������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�015635� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/hostname/hostname-d-works��������������������������������������������������0000644�0000000�0000000�00000000122�12263563520�020761� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������f=$(busybox hostname -f). d=$(busybox hostname -d) test x"${f#*.}" = x"$d${d:+.}" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/hostname/hostname-works����������������������������������������������������0000644�0000000�0000000�00000000051�12263563520�020541� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x$(hostname) = x$(busybox hostname) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/hostname/hostname-s-works��������������������������������������������������0000644�0000000�0000000�00000000057�12263563520�021007� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x$(hostname -s) = x$(busybox hostname -s) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/hostname/hostname-i-works��������������������������������������������������0000644�0000000�0000000�00000000300�12263563520�020764� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x"$SKIP_KNOWN_BUGS" != x"" && exit # Observed bug: # # ./busybox hostname -i # 127.0.0.1 # # hostname -i # 127.0.0.1 10.0.0.2 10.32.10.45 test x$(hostname -i) = x$(busybox hostname -i) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/comm.tests�����������������������������������������������������������������0000755�0000000�0000000�00000002054�12263563520�016046� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2008 by Denys Vlasenko <vda.linux@googlemail.com> # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "description" "command" "result" "infile" "stdin" testing "comm test 1" "comm input -" "\t123\n""456\n""abc\n""\tdef\n" "456\nabc\n" "123\ndef\n" testing "comm test 2" "comm - input" "123\n""\t456\n""\tabc\n""def\n" "456\nabc\n" "123\ndef\n" testing "comm test 3" "comm input -" "abc\n""\tdef\n""xyz\n" "abc\nxyz\n" "def\n" testing "comm test 4" "comm - input" "\tabc\n""def\n""\txyz\n" "abc\nxyz\n" "def\n" testing "comm test 5" "comm input -" "123\n""abc\n""\tdef\n" "123\nabc\n" "def\n" testing "comm test 6" "comm - input" "\t123\n""\tabc\n""def\n" "123\nabc\n" "def\n" testing "comm unterminated line 1" "comm input -" "abc\n""\tdef\n" "abc" "def" testing "comm unterminated line 2" "comm - input" "\tabc\n""def\n" "abc" "def" exit $FAILCOUNT ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/readlink.tests�������������������������������������������������������������0000755�0000000�0000000�00000001721�12263563520�016704� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Readlink tests. # Copyright 2006 by Natanael Copa <n@tanael.org> # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh TESTDIR=readlink_testdir TESTFILE="$TESTDIR/testfile" TESTLINK="testlink" FAILLINK="$TESTDIR/$TESTDIR/testlink" # create the dir and test files mkdir -p "./$TESTDIR" touch "./$TESTFILE" ln -s "./$TESTFILE" "./$TESTLINK" testing "readlink on a file" "readlink ./$TESTFILE" "" "" "" testing "readlink on a link" "readlink ./$TESTLINK" "./$TESTFILE\n" "" "" optional FEATURE_READLINK_FOLLOW testing "readlink -f on a file" "readlink -f ./$TESTFILE" "$PWD/$TESTFILE\n" "" "" testing "readlink -f on a link" "readlink -f ./$TESTLINK" "$PWD/$TESTFILE\n" "" "" testing "readlink -f on an invalid link" "readlink -f ./$FAILLINK" "" "" "" testing "readlink -f on a wierd dir" "readlink -f $TESTDIR/../$TESTFILE" "$PWD/$TESTFILE\n" "" "" # clean up rm -r "$TESTLINK" "$TESTDIR" exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255)) �����������������������������������������������busybox-1.22.1/testsuite/grep.tests�����������������������������������������������������������������0000755�0000000�0000000�00000012432�12267106071�016047� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2005 by Rob Landley <rob@landley.net> # Licensed under GPLv2, see file LICENSE in this source tree. # AUDIT: . ./testing.sh # testing "test name" "commands" "expected result" "file input" "stdin" # file input will be file called "input" # test can create a file "actual" instead of writing to stdout # Test exit status testing "grep (exit with error)" "grep nonexistent 2> /dev/null ; echo \$?" \ "1\n" "" "" testing "grep (exit success)" "grep grep $0 > /dev/null 2>&1 ; echo \$?" "0\n" \ "" "" # Test various data sources and destinations testing "grep (default to stdin)" "grep two" "two\n" "" \ "one\ntwo\nthree\nthree\nthree\n" testing "grep - (specify stdin)" "grep two -" "two\n" "" \ "one\ntwo\nthree\nthree\nthree\n" testing "grep input (specify file)" "grep two input" "two\n" \ "one\ntwo\nthree\nthree\nthree\n" "" # GNU grep 2.5.3 outputs a new line character after the located string # even if there is no new line character in the input testing "grep (no newline at EOL)" "grep bug input" "bug\n" "bug" "" >empty testing "grep two files" "grep two input empty 2>/dev/null" \ "input:two\n" "one\ntwo\nthree\nthree\nthree\n" "" rm empty testing "grep - infile (specify stdin and file)" "grep two - input" \ "(standard input):two\ninput:two\n" "one\ntwo\nthree\n" \ "one\ntwo\ntoo\nthree\nthree\n" # Check if we see the correct return value if both stdin and non-existing file # are given. testing "grep - nofile (specify stdin and nonexisting file)" \ "grep two - nonexistent 2> /dev/null ; echo \$?" \ "(standard input):two\n(standard input):two\n2\n" \ "" "one\ntwo\ntwo\nthree\nthree\nthree\n" testing "grep -q - nofile (specify stdin and nonexisting file, no match)" \ "grep -q nomatch - nonexistent 2> /dev/null ; echo \$?" \ "2\n" "" "one\ntwo\ntwo\nthree\nthree\nthree\n" # SUSv3: If the -q option is specified, the exit status shall be zero # if an input line is selected, even if an error was detected. testing "grep -q - nofile (specify stdin and nonexisting file, match)" \ "grep -q two - nonexistent ; echo \$?" \ "0\n" "" "one\ntwo\ntwo\nthree\nthree\nthree\n" # Test various command line options # -s no error messages testing "grep -s nofile (nonexisting file, no match)" \ "grep -s nomatch nonexistent ; echo \$?" "2\n" "" "" testing "grep -s nofile - (stdin and nonexisting file, match)" \ "grep -s domatch nonexistent - ; echo \$?" \ "(standard input):domatch\n2\n" "" "nomatch\ndomatch\nend\n" optional EXTRA_COMPAT testing "grep handles NUL in files" "grep -a foo input" "\0foo\n" "\0foo\n\n" "" testing "grep handles NUL on stdin" "grep -a foo" "\0foo\n" "" "\0foo\n\n" testing "grep matches NUL" "grep . input > /dev/null 2>&1 ; echo \$?" \ "0\n" "\0\n" "" SKIP= # -e regex testing "grep handles multiple regexps" "grep -e one -e two input ; echo \$?" \ "one\ntwo\n0\n" "one\ntwo\n" "" testing "grep -F handles multiple expessions" "grep -F -e one -e two input ; echo \$?" \ "one\ntwo\n0\n" "one\ntwo\n" "" testing "grep -F handles -i" "grep -F -i foo input ; echo \$?" \ "FOO\n0\n" "FOO\n" "" # -f file/- testing "grep can read regexps from stdin" "grep -f - input ; echo \$?" \ "two\nthree\n0\n" "tw\ntwo\nthree\n" "tw.\nthr\n" # -x (whole line match) testing "grep -x (full match)" "grep -x foo input ; echo \$?" \ "foo\n0\n" "foo\n" "" testing "grep -x (partial match 1)" "grep -x foo input ; echo \$?" \ "1\n" "foo bar\n" "" testing "grep -x (partial match 2)" "grep -x foo input ; echo \$?" \ "1\n" "bar foo\n" "" testing "grep -x -F (full match)" "grep -x -F foo input ; echo \$?" \ "foo\n0\n" "foo\n" "" testing "grep -x -F (partial match 1)" "grep -x -F foo input ; echo \$?" \ "1\n" "foo bar\n" "" testing "grep -x -F (partial match 2)" "grep -x -F foo input ; echo \$?" \ "1\n" "bar foo\n" "" optional FEATURE_GREP_EGREP_ALIAS testing "grep -E supports extended regexps" "grep -E fo+" "foo\n" "" \ "b\ar\nfoo\nbaz" testing "grep is also egrep" "egrep foo" "foo\n" "" "foo\nbar\n" testing "egrep is not case insensitive" \ "egrep foo ; [ \$? -ne 0 ] && echo yes" "yes\n" "" "FOO\n" testing "grep -E -o prints all matches" \ "grep -E -o '([[:xdigit:]]{2}[:-]){5}[[:xdigit:]]{2}'" \ "00:19:3E:00:AA:5E\n00:1D:60:3D:3A:FB\n00:22:43:49:FB:AA\n" \ "" "00:19:3E:00:AA:5E 00:1D:60:3D:3A:FB 00:22:43:49:FB:AA\n" SKIP= testing "grep -o does not loop forever" \ 'grep -o "[^/]*$"' \ "test\n" \ "" "/var/test\n" testing "grep -o does not loop forever on zero-length match" \ 'grep -o "" | head -n1' \ "" \ "" "test\n" testing "grep -f EMPTY_FILE" \ "grep -f input" \ "" \ "" \ "test\n" testing "grep -v -f EMPTY_FILE" \ "grep -v -f input" \ "test\n" \ "" \ "test\n" testing "grep -Fw matches only words" \ "grep -Fw foo input" \ "" \ "foop\n" \ "" testing "grep -Fw doesn't stop on 1st mismatch" \ "grep -Fw foo input" \ "foop foo\n" \ "foop foo\n" \ "" testing "grep -w doesn't stop on 1st mismatch" \ "grep -w foo input" \ "foop foo\n" \ "foop foo\n" \ "" testing "grep -w ^str doesn't match str not at the beginning" \ "grep -w ^str input" \ "" \ "strstr\n" \ "" testing "grep -w ^ doesn't hang" \ "grep -w ^ input" \ "" \ "anything\n" \ "" # testing "test name" "commands" "expected result" "file input" "stdin" # file input will be file called "input" # test can create a file "actual" instead of writing to stdout exit $FAILCOUNT ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/uuencode.tests�������������������������������������������������������������0000755�0000000�0000000�00000006532�12263563520�016727� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # unit test for uuencode to test functionality. # Copyright 2006 by Erik Hovland <erik@hovland.org> # Licensed under GPLv2, see file LICENSE in this source tree. # AUDIT: Unit tests for uuencode . ./testing.sh # testing "test name" "command(s)" "expected result" "file input" "stdin" # file input will be file called "input" # test can create a file "actual" instead of writing to stdout # Test setup of standard input umask 0 testing "uuencode sets standard input mode correctly" \ "uuencode foo </dev/null | head -n 1 | grep -q 666 && echo yes" "yes\n" "" "" umask 022 testing "uuencode correct encoding" "uuencode bb_uuenc_test.out" \ "begin 644 bb_uuenc_test.out\nM5&AE(&9A<W0@9W)E>2!F;W@@:G5M<&5D(&]V97(@=&AE(&QA>GD@8G)O=VX@\n%9&]G+@H\`\n\`\nend\n" \ "" "The fast grey fox jumped over the lazy brown dog.\n" testing "uuencode correct base64 encoding" "uuencode -m bb_uuenc_test.out" \ "begin-base64 644 bb_uuenc_test.out\nVGhlIGZhc3QgZ3JleSBmb3gganVtcGVkIG92ZXIgdGhlIGxhenkgYnJvd24g\nZG9nLgo=\n====\n" \ "" "The fast grey fox jumped over the lazy brown dog.\n" testing "uuencode empty file" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin 644 FILE ` end ' "" "" testing "uuencode -m empty file" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin-base64 644 FILE ==== ' "" "" testing "uuencode file 'A'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin 644 FILE !00`` ` end A' "" "A" testing "uuencode -m file 'A'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin-base64 644 FILE QQ== ==== A' "" "A" testing "uuencode file 'AB'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin 644 FILE "04(` ` end AB' "" "AB" testing "uuencode -m file 'AB'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin-base64 644 FILE QUI= ==== AB' "" "AB" testing "uuencode file 'ABC'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin 644 FILE #04)# ` end ABC' "" "ABC" testing "uuencode -m file 'ABC'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin-base64 644 FILE QUJD ==== ABC' "" "ABC" testing "uuencode file 'ABCD'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin 644 FILE $04)#1``` ` end ABCD' "" "ABCD" testing "uuencode -m file 'ABCD'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin-base64 644 FILE QUJDRA== ==== ABCD' "" "ABCD" testing "uuencode file 'ABCDE'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin 644 FILE %04)#1$4` ` end ABCDE' "" "ABCDE" testing "uuencode -m file 'ABCDE'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin-base64 644 FILE QUJDREU= ==== ABCDE' "" "ABCDE" testing "uuencode file 'ABCDEF'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin 644 FILE &04)#1$5& ` end ABCDEF' "" "ABCDEF" testing "uuencode -m file 'ABCDEF'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin-base64 644 FILE QUJDREVG ==== ABCDEF' "" "ABCDEF" testing "uuencode file 'A<NUL><0xff>Z'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin 644 FILE $00#_6@`` ` end A\x0\xffZ' "" "A\x0\xffZ" testing "uuencode -m file 'A<NUL><0xff>Z'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \ 'begin-base64 644 FILE QQD/Wg== ==== A\x0\xffZ' "" "A\x0\xffZ" exit $FAILCOUNT ����������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cut.tests������������������������������������������������������������������0000755�0000000�0000000�00000000773�12263563520�015714� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2007 by Denys Vlasenko <vda.linux@googlemail.com> # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "test name" "options" "expected result" "file input" "stdin" # file input will be file called "input" # test can create a file "actual" instead of writing to stdout testing "cut '-' (stdin) and multi file handling" \ "cut -d' ' -f2 - input" \ "over\n""quick\n" \ "the quick brown fox\n" \ "jumps over the lazy dog\n" \ exit $FAILCOUNT �����busybox-1.22.1/testsuite/bzcat.tests����������������������������������������������������������������0000755�0000000�0000000�00000003461�12263563520�016221� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh FAILCOUNT=0 ext=bz2 bb="busybox " unset LC_ALL unset LC_MESSAGES unset LANG unset LANGUAGE hello_gz() { # Gzipped "HELLO\n" #_________________________ vvv vvv vvv vvv - mtime $ECHO -ne "\x1f\x8b\x08\x00\x85\x1d\xef\x45\x02\x03\xf3\x70\xf5\xf1\xf1\xe7" $ECHO -ne "\x02\x00\x6e\xd7\xac\xfd\x06\x00\x00\x00" } hello_bz2() { # Bzipped "HELLO\n" $ECHO -ne "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x5b\xb8\xe8\xa3\x00\x00" $ECHO -ne "\x01\x44\x00\x00\x10\x02\x44\xa0\x00\x30\xcd\x00\xc3\x46\x29\x97" $ECHO -ne "\x17\x72\x45\x38\x50\x90\x5b\xb8\xe8\xa3" } prep() { rm -f t* hello_$ext >t1.$ext hello_$ext >t2.$ext } check() { eval $2 >t_actual 2>&1 if $ECHO -ne "$expected" | cmp - t_actual; then echo "PASS: $1" else echo "FAIL: $1" FAILCOUNT=$((FAILCOUNT + 1)) fi } mkdir testdir 2>/dev/null ( cd testdir || { echo "cannot cd testdir!"; exit 1; } expected="HELLO\nok\n" prep; check "bzcat: dont delete src" "${bb}bzcat t2.bz2; test -f t2.bz2 && echo ok" ) rm -rf testdir # Copyright 2011 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "test name" "command" "expected result" "file input" "stdin" # "input" file is bzipped file with "a\n" data testing "bzcat can print many files" \ "$ECHO -ne '$hexdump' | bzcat input input; echo \$?" \ "\ a a 0 " "\ \x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x63\x3e\xd6\xe2\x00\x00\ \x00\xc1\x00\x00\x10\x20\x00\x20\x00\x21\x00\x82\xb1\x77\x24\x53\ \x85\x09\x06\x33\xed\x6e\x20\ " "" # "input" file is bzipped zero byte file testing "bzcat can handle compressed zero-length bzip2 files" \ "$ECHO -ne '$hexdump' | bzcat input input; echo \$?" \ "0\n" \ "\x42\x5a\x68\x39\x17\x72\x45\x38\x50\x90\x00\x00\x00\x00" "" exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255)) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cmp/�����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014576� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cmp/cmp-detects-difference�������������������������������������������������0000644�0000000�0000000�00000000145�12263563520�021025� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo foo >foo echo bar >bar set +e busybox cmp -s foo bar if [ $? != 0 ] ; then exit 0; fi exit 1; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/uncompress.tests�����������������������������������������������������������0000755�0000000�0000000�00000000712�12263563520�017310� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2011 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "test name" "commands" "expected result" "file input" "stdin" testing "uncompress < \x1f\x9d\x90 \x01 x N" \ 'uncompress 2>&1 1>/dev/null; echo $?' \ "\ uncompress: corrupted data 1 " \ "" "\ \x1f\x9d\x90\ \01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\ " exit $FAILCOUNT ������������������������������������������������������busybox-1.22.1/testsuite/mount.tests����������������������������������������������������������������0000755�0000000�0000000�00000006141�12263563520�016256� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2007 by Denys Vlasenko <vda.linux@googlemail.com> # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh test -f "$bindir/.config" && . "$bindir/.config" test "`id -u`" = 0 || { echo "SKIPPED: mount (must be root to test this)" exit 0 } if test x"$CONFIG_MKFS_MINIX" != x"y" \ || test x"$CONFIG_FEATURE_MINIX2" != x"y" \ || test x"$CONFIG_FEATURE_MOUNT_LOOP" != x"y" \ || test x"$CONFIG_FEATURE_MOUNT_FLAGS" != x"y" \ || test x"$CONFIG_FEATURE_DEVFS" = x"y" \ ; then echo "SKIPPED: mount" exit 0 fi testdir="$PWD/mount.testdir" dd if=/dev/zero of=mount.image1m count=1 bs=1M 2>/dev/null || { echo "dd error"; exit 1; } mkfs.minix -v mount.image1m >/dev/null 2>&1 || { echo "mkfs.minix error"; exit 1; } modprobe minix 2>/dev/null mkdir "$testdir" 2>/dev/null umount -d "$testdir" 2>/dev/null # testing "test name" "command" "expected result" "file input" "stdin" # file input will be file called "input" # test can create a file "actual" instead of writing to stdout testing "mount -o remount,mand" \ "mount -o loop mount.image1m $testdir "\ "&& grep -Fc $testdir </proc/mounts "\ "&& mount -o remount,mand $testdir "\ "&& grep -F $testdir </proc/mounts | grep -c '[, ]mand[, ]'"\ "|| grep -F $testdir </proc/mounts" \ "1\n""1\n" \ "" "" umount -d "$testdir" rmdir "$testdir" rm mount.image1m # Bug: mount.shared1 directory shows no files (has to show files a and b) optional FEATURE_LS_RECURSIVE FEATURE_LS_SORTFILES testing "mount bind+rshared" "\ mkdir -p mount.dir mount.shared1 mount.shared2 touch mount.dir/a mount.dir/b mount --bind mount.shared1 mount.shared1 2>&1 mount --make-rshared mount.shared1 2>&1 mount --bind mount.shared2 mount.shared2 2>&1 mount --make-rshared mount.shared2 2>&1 mount --bind mount.shared2 mount.shared1 2>&1 mount --bind mount.dir mount.shared2 2>&1 ls -R mount.dir mount.shared1 mount.shared2 2>&1 umount mount.dir mount.shared1 mount.shared2 2>/dev/null umount mount.dir mount.shared1 mount.shared2 2>/dev/null umount mount.dir mount.shared1 mount.shared2 2>/dev/null rm -f mount.dir/a mount.dir/b mount.dir/c rmdir mount.dir mount.shared1 mount.shared2 " \ "\ mount.dir: a b mount.shared1: a b mount.shared2: a b " \ "" "" SKIP= testing "mount RO loop" "\ exec 2>&1 umount -d mount.dir 2>/dev/null rmdir mount.dir 2>/dev/null mkdir -p mount.dir ( cd mount.dir || { echo 'cd error'; exit 1; } mkdir z1 z2 || { echo 'mkdir error'; exit 1; } mount -t tmpfs tmpfs z1 || { echo 'mount tmpfs error'; exit 1; } dd if=/dev/zero of=z1/e2img count=10 bs=1M 2>/dev/null || { echo 'dd error'; exit 1; } mke2fs -F z1/e2img 2>/dev/null >&2 || { echo 'mke2fs error'; exit 1; } mount -r -o loop -t ext2 z1/e2img z2 || { echo 'mount -r -o loop error'; exit 1; } mount -o remount,ro z1 || { echo 'mount -o remount,ro error'; exit 1; } ) umount -d mount.dir/z2 ##losetup -d /dev/loop* umount -d mount.dir/z1 rm -rf mount.dir echo DONE " \ "DONE\n" "" "" exit $FAILCOUNT �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/which/���������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�015121� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/which/which-uses-default-path����������������������������������������������0000644�0000000�0000000�00000000115�12263563520�021500� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������BUSYBOX=$(command -pv busybox) SAVED_PATH=$PATH unset PATH $BUSYBOX which ls ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/test.tests�����������������������������������������������������������������0000755�0000000�0000000�00000003211�12263563520�016066� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2007 by Denys Vlasenko <vda.linux@googlemail.com> # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "test name" "command" "expected result" "file input" "stdin" # file input will be file called "input" # test can create a file "actual" instead of writing to stdout # Need to call 'busybox test', otherwise shell builtin is used testing "test: should be false (1)" \ "busybox test; echo \$?" \ "1\n" \ "" "" testing "test '': should be false (1)" \ "busybox test ''; echo \$?" \ "1\n" \ "" "" testing "test !: should be true (0)" \ "busybox test !; echo \$?" \ "0\n" \ "" "" testing "test a: should be true (0)" \ "busybox test a; echo \$?" \ "0\n" \ "" "" testing "test --help: should be true (0)" \ "busybox test --help; echo \$?" \ "0\n" \ "" "" testing "test -f: should be true (0)" \ "busybox test -f; echo \$?" \ "0\n" \ "" "" testing "test ! -f: should be false (1)" \ "busybox test ! -f; echo \$?" \ "1\n" \ "" "" testing "test a = a: should be true (0)" \ "busybox test a = a; echo \$?" \ "0\n" \ "" "" testing "test -lt = -gt: should be false (1)" \ "busybox test -lt = -gt; echo \$?" \ "1\n" \ "" "" testing "test a -a !: should be true (0)" \ "busybox test a -a !; echo \$?" \ "0\n" \ "" "" testing "test -f = a -o b: should be true (0)" \ "busybox test -f = a -o b; echo \$?" \ "0\n" \ "" "" testing "test ! a = b -a ! c = c: should be false (1)" \ "busybox test ! a = b -a ! c = c; echo \$?" \ "1\n" \ "" "" testing "test ! a = b -a ! c = d: should be true (0)" \ "busybox test ! a = b -a ! c = d; echo \$?" \ "0\n" \ "" "" exit $FAILCOUNT ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/du/������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014427� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/du/du-s-works��������������������������������������������������������������0000644�0000000�0000000�00000000276�12263563520�016376� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K d=/bin du -s "$d" > logfile.gnu busybox du -s "$d" > logfile.bb cmp logfile.gnu logfile.bb && exit 0 diff -u logfile.gnu logfile.bb exit 1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/du/du-l-works��������������������������������������������������������������0000644�0000000�0000000�00000000614�12263563520�016363� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K mkdir du.testdir cd du.testdir dd if=/dev/zero of=file1 bs=1k count=64 2>/dev/null ln file1 file1.1 dd if=/dev/zero of=file2 bs=1k count=16 2>/dev/null test x"`busybox du -l .`" = x"144 ." \ -o x"`busybox du -l .`" = x"146 ." \ -o x"`busybox du -l .`" = x"148 ." \ -o x"`busybox du -l .`" = x"152 ." \ -o x"`busybox du -l .`" = x"156 ." ��������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/du/du-h-works��������������������������������������������������������������0000644�0000000�0000000�00000000210�12263563520�016347� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_HUMAN_READABLE dd if=/dev/zero of=file bs=1M count=1 2>/dev/null test x"`busybox du -h file`" = x"1.0M file" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/du/du-k-works��������������������������������������������������������������0000644�0000000�0000000�00000000722�12263563520�016362� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mkdir du.testdir cd du.testdir dd if=/dev/zero of=file1 bs=1k count=64 2>/dev/null dd if=/dev/zero of=file2 bs=1k count=16 2>/dev/null # ext4 on images <512M gives 81kb # ext3 on images <512M gives 83kb # a bsd system reportedly gives 82kb test x"`busybox du -k .`" = x"80 ." \ -o x"`busybox du -k .`" = x"81 ." \ -o x"`busybox du -k .`" = x"82 ." \ -o x"`busybox du -k .`" = x"83 ." \ -o x"`busybox du -k .`" = x"84 ." \ -o x"`busybox du -k .`" = x"88 ." ����������������������������������������������busybox-1.22.1/testsuite/du/du-m-works��������������������������������������������������������������0000644�0000000�0000000�00000000177�12263563520�016370� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_HUMAN_READABLE dd if=/dev/zero of=file bs=1M count=1 2>/dev/null test x"`busybox du -m .`" = x"1 ." �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/du/du-works����������������������������������������������������������������0000644�0000000�0000000�00000000270�12263563520�016130� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K d=/bin du "$d" > logfile.gnu busybox du "$d" > logfile.bb cmp logfile.gnu logfile.bb && exit 0 diff -u logfile.gnu logfile.bb exit 1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/pidof.tests����������������������������������������������������������������0000755�0000000�0000000�00000002050�12263563520�016210� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # pidof tests. # Copyright 2005 by Bernhard Reutner-Fischer # Licensed under GPLv2, see file LICENSE in this source tree. # AUDIT: . ./testing.sh # testing "test name" "options" "expected result" "file input" "stdin" testing "pidof (exit with error)" \ "pidof veryunlikelyoccuringbinaryname ; echo \$?" "1\n" "" "" testing "pidof (exit with success)" "pidof pidof > /dev/null; echo \$?" \ "0\n" "" "" # We can get away with this because it says #!/bin/sh up top. testing "pidof this" "pidof pidof.tests | grep -o -w $$" "$$\n" "" "" optional FEATURE_PIDOF_SINGLE testing "pidof -s" "pidof -s init" "1\n" "" "" SKIP= optional FEATURE_PIDOF_OMIT FEATURE_PIDOF_SINGLE # This test fails now because process name matching logic has changed, # but new logic is not "wrong" either... see find_pid_by_name.c comments #testing "pidof -o %PPID" "pidof -o %PPID pidof.tests | grep -o -w $$" "" "" "" testing "pidof -o %PPID NOP" "pidof -o %PPID -s init" "1\n" "" "" testing "pidof -o init" "pidof -o 1 init | grep -o -w 1" "" "" "" SKIP= exit $FAILCOUNT ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/bunzip2.tests��������������������������������������������������������������0000755�0000000�0000000�00000107010�12263563520�016502� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Used by both gunzip and bunzip2 tests FAILCOUNT=0 if test "${0##*/}" = "gunzip.tests"; then unpack=gunzip ext=gz elif test "${0##*/}" = "bunzip2.tests"; then unpack=bunzip2 ext=bz2 else echo "WTF? argv0='$0'" exit 1 fi bb="busybox " unset LC_ALL unset LC_MESSAGES unset LANG unset LANGUAGE hello_gz() { # Gzipped "HELLO\n" #_________________________ vvv vvv vvv vvv - mtime $ECHO -ne "\x1f\x8b\x08\x00\x85\x1d\xef\x45\x02\x03\xf3\x70\xf5\xf1\xf1\xe7" $ECHO -ne "\x02\x00\x6e\xd7\xac\xfd\x06\x00\x00\x00" } hello_bz2() { # Bzipped "HELLO\n" $ECHO -ne "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x5b\xb8\xe8\xa3\x00\x00" $ECHO -ne "\x01\x44\x00\x00\x10\x02\x44\xa0\x00\x30\xcd\x00\xc3\x46\x29\x97" $ECHO -ne "\x17\x72\x45\x38\x50\x90\x5b\xb8\xe8\xa3" } # We had bunzip2 error on this .bz2 file (fixed in rev 22521) test1_bz2() { $ECHO -ne "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\xbf\x4b\x95\xe7\x00\x15" $ECHO -ne "\xa1\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" $ECHO -ne "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xef\xff\xff\xfb\xff\xff\xff" $ECHO -ne "\xff\xff\xff\xe0\x16\xe6\x37\xb7\x77\xb0\xfb\x22\xb5\x81\x40\xf5" $ECHO -ne "\xa7\x69\xa4\x47\xab\x61\x4d\x6a\x3d\xef\x75\x7b\x22\xaf\x71\x9b" $ECHO -ne "\xb3\x5a\x93\xbb\x78\x00\x79\xbd\xc0\x07\xae\x1b\xdb\xc5\xc6\x6b" $ECHO -ne "\x7a\x7b\xd3\xd7\x38\xbb\x70\x5e\xf0\xd1\x08\x9a\x64\x26\x53\x68" $ECHO -ne "\x03\x53\x4d\x32\x30\xd2\x61\x31\x13\x68\xd1\xa6\x4c\x41\x89\x84" $ECHO -ne "\x31\x4f\x40\xd2\x79\x14\xf0\xa3\xda\x1a\x00\x65\x4f\x53\xd9\x28" $ECHO -ne "\xd3\xf4\x4c\x13\x26\x4c\x4c\x64\x34\x9a\x6a\x7a\x99\x3c\x12\x78" $ECHO -ne "\xd2\x68\xd4\xda\x6a\x79\x32\x64\xd1\xa8\x1b\x13\x01\x4f\x29\xea" $ECHO -ne "\x6c\xa3\xd2\x07\x94\x06\xa6\x44\x01\x4f\x11\xa3\x4d\x13\x4d\x31" $ECHO -ne "\x32\x4c\x98\x0c\x9a\xa6\xda\x29\x3d\xa4\xf1\x24\xfd\x1a\xa3\x7a" $ECHO -ne "\x93\xf4\xa7\xb5\x3d\x51\xe2\x47\x94\xf2\x8f\x29\xb2\x9b\x29\xe9" $ECHO -ne "\x34\x79\x4f\x46\x9e\x84\x6a\x69\xea\x69\xa7\xa9\xb5\x03\x27\xa8" $ECHO -ne "\xf1\x40\x32\x7a\x13\x10\x00\x3d\x41\x90\x00\xd0\x1e\x84\x0d\x1b" $ECHO -ne "\x53\x41\xa3\x21\x93\xd0\x83\x53\x10\x99\x34\x24\xf5\x32\x99\x34" $ECHO -ne "\xd2\x7a\x86\xca\x6c\x28\xda\x6d\x29\xa6\x4d\x31\x0c\xd4\x7a\x69" $ECHO -ne "\x1e\x93\x23\xca\x1e\x93\x4d\x03\x26\x9a\x68\x01\xa0\xc9\xa0\x1a" $ECHO -ne "\x00\x34\x00\x00\x69\xa0\xf4\x80\x0d\x00\x00\x34\x06\x86\x80\x34" $ECHO -ne "\x00\x00\x00\x34\x00\x48\x88\x84\x53\x68\x4f\x45\x3d\x51\xfa\x4d" $ECHO -ne "\x4c\xda\x9a\x8d\xb5\x4c\xd4\xf2\x35\x1b\x51\xb4\xd3\x14\xf5\x0f" $ECHO -ne "\x50\xf5\x0f\x24\xd3\x32\x23\xd4\xcd\x21\xa6\xd4\xd0\xd0\x69\xa0" $ECHO -ne "\xf4\x8d\x3d\x43\xd3\x51\xea\x6c\x90\xd0\x68\xf4\x40\x00\x07\xa8" $ECHO -ne "\x19\x3d\x47\xa8\x1e\xa0\xd0\x34\x00\x0d\x1a\x06\x80\x01\xe9\x00" $ECHO -ne "\x64\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" $ECHO -ne "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" $ECHO -ne "\x00\x00\x48\x92\x1a\x46\x9a\x02\x9e\x24\xc5\x4f\xc9\x91\x4f\x29" $ECHO -ne "\xec\xa9\xea\x34\xd3\xd2\x68\x68\x00\xf4\xd4\xf5\x19\x00\x34\x00" $ECHO -ne "\x06\x4d\x00\xd0\x1a\x1a\x03\x20\x01\xa0\x00\xf5\x00\x0d\x06\x9b" $ECHO -ne "\x50\x36\xa3\x35\x00\x03\x40\x03\x40\x68\xf5\x34\x00\x00\x00\x71" $ECHO -ne "\xe4\x3c\x0c\x0f\x0f\x3d\x23\x9a\x7f\x31\x80\xae\x1a\xe1\x09\x03" $ECHO -ne "\x2c\x61\x63\x18\xfc\x1a\x28\x0e\x9a\x85\xc3\x86\x96\x11\x94\x88" $ECHO -ne "\x0f\x92\x11\x8a\x1a\xb4\x61\x8c\x38\x30\xf3\xa9\xfb\xbe\xcd\x8d" $ECHO -ne "\xc4\xb7\x7a\x52\xad\x92\x9f\xde\xe6\x75\x74\xb7\xbb\x0b\xf5\x4c" $ECHO -ne "\x97\xd9\x49\xc8\x63\x9b\xa6\xba\x95\xe8\x46\x70\x11\x71\x55\x67" $ECHO -ne "\x17\xe3\xab\x91\x7d\xbe\x0d\x56\x78\x61\x98\x41\x28\x2c\xb5\xa3" $ECHO -ne "\xd1\xb8\x76\x80\xc8\xad\x9b\x79\x6f\x8a\x7a\x84\x44\x35\x24\x64" $ECHO -ne "\xa0\xde\x18\x80\x11\x14\xcd\xb0\xc6\xe4\x31\x49\x71\x53\xaf\x5d" $ECHO -ne "\x20\xc9\x59\xdc\xa2\x53\x06\xf0\x4f\xc8\x09\x21\x92\x6b\xf2\x37" $ECHO -ne "\x32\xcb\x75\x91\x75\x41\xc0\xe8\x47\x02\xb3\x8e\x0b\x17\x47\x42" $ECHO -ne "\x79\x21\x3c\xec\x6c\xb0\x84\x24\x96\x45\x4a\x8e\xbb\xbe\xc2\xe0" $ECHO -ne "\x16\x85\x76\x43\x26\xd5\xd0\x58\xdd\xf7\x83\x65\x44\x8f\xbe\x6c" $ECHO -ne "\x72\xe1\x5b\x1a\x0e\x3a\xbb\x51\xcf\xbd\x9b\x3a\xd0\xd5\x39\x5b" $ECHO -ne "\x23\x7d\xc9\x0b\x08\x0a\x23\x7b\x3a\x00\x67\xa1\x76\xa5\x19\xab" $ECHO -ne "\x48\xbd\x54\xaa\x8f\xaf\xb6\xe8\xd5\x91\x0f\x3e\x4b\x3a\x8d\xc9" $ECHO -ne "\x48\x02\xc2\x6b\xfc\xef\x0a\x9b\xf1\x67\xd0\x45\x48\x45\x05\xc0" $ECHO -ne "\x07\xc4\x47\x96\x6e\x79\xac\x31\x49\xcf\x1f\xa8\x3b\xdc\x1d\x44" $ECHO -ne "\x83\x84\x04\x49\x9f\x25\x01\x4b\x41\x80\x14\x1b\x9d\xaf\xfc\xb5" $ECHO -ne "\x46\x22\xca\x96\x4e\xd5\xe3\x08\x7d\xab\x17\x0b\x65\xcd\xa7\xd4" $ECHO -ne "\x5e\xd1\x8a\x27\xc8\x60\xe9\x17\xa3\xc9\xb4\x26\xf8\x58\xb2\x4a" $ECHO -ne "\x15\x52\x8e\x15\x20\x72\xb2\x0e\x4f\x64\xcb\x2b\xa3\xd9\xf0\xa6" $ECHO -ne "\x6f\x0b\x50\xed\x4e\xa4\x28\xe8\xe0\x05\x2d\x15\x26\x2c\x3d\x9f" $ECHO -ne "\x87\xc4\xd1\xd3\x69\xe2\x1c\xea\x41\x99\xbd\x43\x59\x9f\x06\x57" $ECHO -ne "\x06\xb4\x72\xe7\x45\x2c\xd2\xcf\x5f\x66\xa5\xeb\x58\x6a\xc0\x37" $ECHO -ne "\x82\x81\xf6\xdc\x1c\x35\x5b\xc6\xf1\x92\x4e\xe0\xe2\xd2\x12\x82" $ECHO -ne "\x92\x97\x14\xe5\xac\x49\x9f\xfd\x16\x46\xc6\xc2\xf1\x48\x86\x05" $ECHO -ne "\xe6\x74\xac\x9a\x52\x49\x64\x45\x25\x43\x08\x06\x03\x63\x49\x91" $ECHO -ne "\x9a\x92\x96\x5b\xe3\x2f\x11\x51\xcd\xe3\xa3\x9f\x3e\x8f\x9f\xab" $ECHO -ne "\x92\xbc\x6d\x36\xa3\xd1\x71\xa4\x4a\xb6\xe1\x49\xb8\xdc\x2c\x90" $ECHO -ne "\xb2\xd6\xfb\xa6\xc6\xd9\xa4\x5b\x9b\xe5\xd6\x59\xb3\x76\x37\xeb" $ECHO -ne "\xa6\x3e\xf0\x4c\x98\x1d\x73\x04\x01\x06\x8c\x10\x00\x3b\x2b\x04" $ECHO -ne "\x55\xf4\xfd\xec\x9d\xad\xc7\x2b\xbd\xe3\xda\x4b\xee\x28\x5d\x7a" $ECHO -ne "\xbe\xa6\xb9\xe0\x81\x15\xa6\x09\x26\x52\x60\xcc\x03\x30\x66\x60" $ECHO -ne "\xb9\xd0\x79\xfd\xb6\xb3\x85\xac\xd1\xc4\x4c\xbf\x80\x20\x44\x45" $ECHO -ne "\x7f\x72\x27\xff\x14\xc2\xc0\x81\x02\xab\x32\x20\x43\x46\x06\x7f" $ECHO -ne "\xb7\xc2\xb9\xf6\x39\x7b\x0b\x0c\xcb\xe7\x6e\x03\xe3\x20\x46\x82" $ECHO -ne "\x4a\x01\x23\xbb\xb0\x0c\xb5\x6f\xf7\xfb\xfc\xf5\xf2\x3c\x8e\x7e" $ECHO -ne "\xcb\x77\x6f\x7e\xc3\x71\x7c\x44\x3f\xbc\x3c\x54\xb8\x40\x27\x78" $ECHO -ne "\x63\x4d\x83\x22\x6a\x0a\x00\x0e\x8d\xa5\xfa\x5e\xe5\x89\x55\xa4" $ECHO -ne "\x18\x60\xc2\xa6\xd6\x17\x98\x23\xf0\x07\x44\x45\x18\xa4\x68\xd9" $ECHO -ne "\xcc\x0d\xe3\x81\x06\x09\x0c\x17\xaf\x52\xad\x85\x83\x5d\x09\x30" $ECHO -ne "\x0d\xa9\xb3\xe6\x45\x85\x9e\x26\x47\xab\x7d\x14\xe1\x8a\xb9\xfe" $ECHO -ne "\x0a\x0f\x8d\x68\x73\x73\x5c\xa3\x0b\xb5\x29\x0c\xd8\xde\xc2\x30" $ECHO -ne "\xbe\x61\xce\xbd\x4d\x3a\x03\xef\xe9\x45\xef\xeb\x07\xe8\x6b\x7d" $ECHO -ne "\xd2\xf4\x92\x8f\x91\xa1\x6e\x85\x2b\x73\xaf\x01\x8a\xd2\x0f\x52" $ECHO -ne "\xed\x65\x9f\xe6\x15\x47\xb2\x71\xd3\xbc\xee\xde\xff\x10\xfa\x4d" $ECHO -ne "\x7f\x9d\x5a\x4b\x13\x4a\x92\xd6\x85\xb2\xef\xe1\xbb\x92\x80\x4a" $ECHO -ne "\x45\x70\xa0\x4e\xe6\xf3\x39\x9a\xf6\x55\xee\x80\xc5\xa0\xff\x9d" $ECHO -ne "\xb6\x66\xe6\xcc\x81\xb2\xdc\xd6\x39\xb7\x06\x2c\xd6\x3b\x27\x0e" $ECHO -ne "\x5d\x01\x92\x5c\xf3\xbe\x3d\x46\x8a\x46\xa4\xd4\x03\x21\x86\x8e" $ECHO -ne "\x68\x05\x3b\xf0\x66\x69\x4c\x61\xf0\x39\x1c\x9d\xe2\x74\x3b\x5f" $ECHO -ne "\xd7\x87\xdc\xd3\xeb\x59\x50\xb6\x6d\x75\xc9\x5b\xdc\x4d\xb7\x29" $ECHO -ne "\x0c\x64\x9c\x5c\x22\xd1\x44\xd7\x01\x68\x0a\x26\x25\x7d\x6a\x76" $ECHO -ne "\x1c\x1b\xbf\x7a\xa5\xeb\x42\x8f\x2f\x93\xa3\xc1\xca\xe3\x9f\x46" $ECHO -ne "\xfd\x77\x07\x27\x2d\xaf\xbb\x1a\x13\x5b\x86\x94\x00\x90\x86\xc1" $ECHO -ne "\x24\x8d\x86\x22\x56\xbe\x06\xe1\xa1\x44\x4c\x36\xe2\x22\x08\x21" $ECHO -ne "\xb2\x20\x6d\xb6\xdb\x6c\x6e\x26\x26\x06\xc0\x26\x09\x94\x09\x75" $ECHO -ne "\x2c\x10\x4b\x44\xb0\x1b\x44\x26\x11\x58\x10\xdf\x2c\xc1\x55\x8a" $ECHO -ne "\xad\xb2\xa3\x08\x67\x34\xe5\x83\x95\x0a\x08\x82\xc1\x8a\x06\xdd" $ECHO -ne "\xb1\x32\x14\xa5\x27\x78\xca\xb6\xd1\x57\x5a\xc9\x2a\x06\x05\x29" $ECHO -ne "\x0c\x88\x28\xd2\x86\xa5\xa9\x69\x51\x81\x46\xa1\xa4\x81\xb1\x8d" $ECHO -ne "\xb1\xb0\x01\x83\x49\x4b\x15\x1a\x6e\x13\x24\x68\x54\x60\x4b\x4e" $ECHO -ne "\x21\x39\x82\x1c\x8d\x02\x6d\x23\xc3\x30\x25\x83\x69\x05\x11\x05" $ECHO -ne "\x4d\x24\x04\x4e\x0c\x53\x81\x25\xce\x34\x10\xd0\x04\xd4\x98\xa1" $ECHO -ne "\x21\x0b\x7e\xc4\x09\x11\x30\x82\x8f\x68\xc4\x13\x48\x0a\x30\x17" $ECHO -ne "\x4f\xaf\x80\x52\xd0\x36\x22\xd6\x43\x48\x15\xf6\xa1\x82\x84\xdc" $ECHO -ne "\x44\x34\x07\x52\xc4\x2c\x56\xb7\xaf\xa8\x3b\xb1\x08\x4b\x6b\x6c" $ECHO -ne "\x24\x05\xce\x1a\x10\xe2\x02\x31\x22\x25\xb8\x23\x65\xd0\x52\x9b" $ECHO -ne "\x4a\xcb\x64\xae\xbd\xe8\xda\x30\xb4\x25\x79\xa4\xbc\xe6\xe0\xf3" $ECHO -ne "\xde\x82\x23\x84\xce\xe5\xb9\xc9\xe9\xeb\x69\x78\x2f\xbc\x76\x6d" $ECHO -ne "\x58\x86\xc4\xa5\x82\xfa\xad\x61\x75\x62\x0c\xb6\x9b\x00\xdf\x30" $ECHO -ne "\x4a\xd6\x83\xaa\x60\x8a\x33\x7c\xd2\x12\xf5\x6c\x48\x52\xc5\x85" $ECHO -ne "\xe2\x6f\x37\x73\xc7\xbc\xad\xea\x27\x27\xa8\xef\xf7\xef\x59\x17" $ECHO -ne "\x65\xb6\xe1\xd8\xdd\xb5\x93\x42\xd0\x29\x5a\x18\x76\x08\xdb\xe5" $ECHO -ne "\x38\xf9\xa8\xe4\xa1\xa2\xd4\x40\xa0\xfd\x45\x18\x4b\x3c\xa6\x85" $ECHO -ne "\x02\x94\x8c\x88\xa9\x71\x87\x40\x96\x4d\x23\x26\xf4\x17\x44\xb8" $ECHO -ne "\x78\x1e\x71\xe3\x3b\xee\xc6\x4b\x87\x88\xfd\x2b\xb5\x8b\x1b\x53" $ECHO -ne "\x0b\xab\xd6\x47\x23\xa7\xcf\x78\x3a\x25\xd7\x3c\x77\xb3\x8e\x00" $ECHO -ne "\x37\x83\x11\xbb\x68\xf5\xed\x0a\x1a\x4d\xa3\x90\x68\xea\xed\x49" $ECHO -ne "\x8d\xb6\x80\x69\x83\x67\xcf\x65\x5a\xec\xda\x12\xe6\x5a\x47\x5a" $ECHO -ne "\x3c\x63\x50\x41\x73\x40\x83\xc7\x69\xbc\x46\xa7\xb1\xed\x79\x3c" $ECHO -ne "\xfe\xdf\x27\x4b\xbe\xeb\x68\xec\x83\x00\x6b\x7b\x08\x4a\x6e\x0c" $ECHO -ne "\x2d\x16\xba\x1a\x96\xa1\x03\xc3\x63\x9e\x7a\xce\x8b\xe2\xae\x51" $ECHO -ne "\xfb\x7d\xed\x5d\xfb\xbc\xed\x04\x6f\x1f\x21\xfc\x69\x3c\xb1\xaa" $ECHO -ne "\xdf\xbf\xa0\xab\xc3\xcc\x6a\xbf\xe7\x96\xbe\x36\xb3\x23\x32\x1c" $ECHO -ne "\xb5\x18\x44\x54\x51\x81\xb4\x63\xc7\x99\x84\x06\xcb\xaf\x5b\x05" $ECHO -ne "\x4f\x82\xf5\x93\xb4\xc3\xcf\xdb\x65\xb8\x8d\xae\xa1\xc2\xf0\xdf" $ECHO -ne "\xa7\xe5\xf3\x37\xd2\x57\x73\x0d\x89\xb8\x21\x10\x9a\x43\xe9\xe0" $ECHO -ne "\x09\x1a\x40\x49\xa0\xcc\x03\x30\x46\x66\x66\x0c\x12\x48\x89\xff" $ECHO -ne "\x57\xe8\xd2\x7c\x3e\x8d\x9e\x46\x7f\x97\xfc\x3b\x12\x95\xd2\xdf" $ECHO -ne "\x2f\xb1\xc8\x7d\x61\xdb\xb2\x8a\xdd\xbf\xf3\x7e\x08\xcc\xad\x16" $ECHO -ne "\xbe\x45\x13\xf2\x7f\x14\x5a\x79\x2e\xb5\xbb\x78\x0c\x22\xc6\x10" $ECHO -ne "\x31\xce\x9c\x6b\x1d\x48\x11\x16\x4c\xdf\x98\x12\xf3\x41\x05\x81" $ECHO -ne "\xd3\x24\x94\x92\x37\x51\x5d\xdc\x51\x08\xd3\x73\xba\x89\x42\x3f" $ECHO -ne "\xcb\x5c\x4c\xb2\x16\xcb\x04\xcd\x86\xb2\x05\x8a\xc3\x56\xc8\x83" $ECHO -ne "\x0b\x2e\x90\x31\x86\x5c\x68\xb9\x8d\xbc\xbf\xf2\xe2\xd2\xb0\x0b" $ECHO -ne "\x76\x2b\x3d\x79\xba\x3f\x9b\xe3\x8e\xc4\xf5\xed\xe0\xf7\xdd\xdb" $ECHO -ne "\x97\x5f\x9a\xb3\xfc\x50\xbf\x89\xf4\x7a\x38\xa3\x44\x0c\x50\x5d" $ECHO -ne "\x7c\xbb\x65\x47\xf1\x33\xd6\x67\xa4\xe0\xf0\x68\x58\xe9\x6c\x40" $ECHO -ne "\x02\x6b\x01\x20\x40\x84\x89\x80\x08\xcc\x52\xa0\x20\x81\x98\x16" $ECHO -ne "\xa1\x90\xf8\xcd\xbe\x1e\xc7\x6b\x1d\xb5\x81\x6b\x04\xdb\x4c\x43" $ECHO -ne "\x1a\xbc\xd4\x0d\xb6\x0d\xb3\x82\xc8\xc7\xf0\x13\xa8\xc5\x20\xd5" $ECHO -ne "\xbd\xb4\xc0\x5a\xdd\xe8\xd1\x31\x4f\xad\x88\x63\x30\x44\x0d\xd5" $ECHO -ne "\xc6\x56\x96\x28\xe2\xe8\xa8\xa9\x10\xdb\x1a\xa3\x21\xa6\xc5\xe6" $ECHO -ne "\xf5\xb2\xa4\x6d\x8d\xb4\x31\xb5\xc3\xec\x3e\x8f\xd0\xeb\x35\xce" $ECHO -ne "\xdb\x02\x9c\x4e\x96\xcd\x40\x14\xcd\x97\xb9\x0a\xe3\x09\xf5\x49" $ECHO -ne "\xfe\x1e\xc7\xc5\x57\xb9\x96\xe9\xf5\x8a\x56\xdf\x63\xda\x8a\xea" $ECHO -ne "\x41\x97\x74\x7b\xa6\x57\x99\x8d\xb0\x78\x60\xe4\x04\xd7\xe4\xbf" $ECHO -ne "\x89\x71\xa5\xc8\x93\x42\x02\x53\x7a\x6a\x9d\x99\xc9\xd3\x2b\x87" $ECHO -ne "\x75\xb2\x8f\x19\x86\x28\x2b\xc3\x2b\x67\x95\x72\xfb\x13\x39\xb5" $ECHO -ne "\xca\x8c\x43\x88\x1c\xdc\x47\xb6\xdb\x05\xaf\x8e\x31\x54\xb8\xbd" $ECHO -ne "\x98\x8b\x1d\x1f\x17\x87\x9d\x6d\x05\xca\xa8\x90\x49\x10\xbb\x67" $ECHO -ne "\x2f\x92\x61\x43\xfe\xe2\xd6\x18\x6d\x2a\xc0\x14\x96\x9a\x2a\x65" $ECHO -ne "\x48\x04\xc7\x2d\x76\xa6\x1f\xc5\x79\x36\x28\x69\x6f\x09\xb6\x90" $ECHO -ne "\xc3\x55\x6d\x98\xf0\xbd\xce\xb1\x37\xf4\xc4\x90\x1c\xdf\x5a\x27" $ECHO -ne "\xbc\x24\x38\x52\x75\xc0\xee\xc9\x05\x5a\xd7\x2b\x61\xfd\xba\xfb" $ECHO -ne "\xea\x9f\x65\x39\x9f\xe7\xc9\xc3\x0e\xa9\x3a\x20\x50\x87\xb6\x08" $ECHO -ne "\xc7\x80\x92\xe2\x60\x21\xd2\x2d\x51\x12\xf8\x46\x60\xbd\xf4\x65" $ECHO -ne "\xd5\x7b\x1a\xa7\x79\xb7\x73\x79\xe9\x0d\x60\x34\xc3\xb0\x58\xc8" $ECHO -ne "\xcc\x42\x7b\xb0\x56\x8c\xde\x66\x72\x23\xc2\x59\xe6\x9f\x83\x6a" $ECHO -ne "\xef\x4a\x9e\x1e\xf3\xd5\xde\x52\x32\x14\x8a\x2d\x0b\xf0\x1e\x5b" $ECHO -ne "\x7c\x4a\x34\x4d\x72\x4f\x1d\x8f\x97\xe8\xc9\xcd\xe2\xb9\x03\x36" $ECHO -ne "\x9f\x89\x97\xc3\x19\x8d\x8d\x84\x41\x0c\x03\x34\x18\x41\x20\x10" $ECHO -ne "\x26\x4c\x10\x18\x50\x5e\xd7\x93\x1f\x31\xf7\x54\xb3\x43\x4d\xd7" $ECHO -ne "\x48\x69\xcf\x7d\x29\x2f\x7f\x8f\x11\xf2\x4c\x3f\xcd\xe7\xa2\xe1" $ECHO -ne "\x09\x9a\x1a\x6c\xc6\xf3\xcf\x33\xe5\xb5\x8f\x6e\x41\xf1\x80\x07" $ECHO -ne "\x4d\x7f\xbe\x1b\x37\xdd\xe3\x64\xb8\xa2\x59\x90\x2c\xa2\xbe\xf4" $ECHO -ne "\x82\x2a\x80\x46\x4d\x1a\x8c\x88\x5a\x18\x30\x64\x0a\x5a\x57\x37" $ECHO -ne "\x63\xe9\x6d\x8a\x6d\x5f\x88\x5e\x6d\x41\x33\x60\xfd\x45\x90\x7e" $ECHO -ne "\x15\xaa\x95\x6f\xbd\xfc\xe9\x0b\x34\xe4\x3b\xa8\x41\x78\x1c\x55" $ECHO -ne "\x62\x5d\xb2\x19\xdd\xeb\x69\xeb\xef\xe1\xbf\x7b\xeb\x62\x23\x8a" $ECHO -ne "\x61\x14\x9f\x22\x53\x08\x6a\x31\xba\x30\x24\x1e\x54\x83\xae\xbd" $ECHO -ne "\x87\xa1\x71\xf0\x3c\x7d\x94\xa1\x2c\xea\xff\x84\x76\x77\xd2\xc9" $ECHO -ne "\x9f\x2f\x9c\xc7\x83\x3f\x89\x5d\x1b\x5c\xc3\x0f\xfa\xd2\x93\x32" $ECHO -ne "\xfc\xed\xa6\x26\x86\x98\x1b\x05\x10\x20\x27\x4c\x95\x3f\x6d\x94" $ECHO -ne "\x82\x5a\xa8\x68\x72\xae\xd7\xae\xdb\xaf\x26\xb6\x5a\x89\x30\xe7" $ECHO -ne "\xd0\x5a\x7c\xc6\x66\xfa\xc3\x85\x7d\x26\xee\xb3\x34\xc2\xac\x70" $ECHO -ne "\x88\x03\x15\xc6\xee\x55\x45\xde\x1c\x10\x01\xe6\x3b\x36\x26\x11" $ECHO -ne "\xbe\xec\x54\xea\xd8\x20\x1d\x35\x00\xd1\x8c\x00\xe8\xf5\x21\x97" $ECHO -ne "\x26\x06\x69\x87\x55\xa3\xc8\xf6\x58\xcc\x60\x12\x30\x0b\x8a\x50" $ECHO -ne "\x01\x57\x30\xdc\x9a\x01\xd4\xa4\xcd\xd6\x69\x23\x0f\xc3\xb8\x85" $ECHO -ne "\x12\xbb\x8e\xdf\xc5\xf1\xf3\x7c\xc9\x7a\x24\x25\x07\x9c\x86\x97" $ECHO -ne "\x68\xb5\xad\x0b\xba\x2e\xe8\x6f\x7f\xa1\xed\x4f\x0c\x34\x7b\xc8" $ECHO -ne "\x84\x10\x08\x2a\xcc\x19\x59\xbd\xbc\xe4\x3d\xa8\xd9\x35\xaf\x8b" $ECHO -ne "\xa7\x0a\xad\x42\xe8\x02\x90\xe6\x8e\x76\x5d\x0f\x3b\x87\xb8\xe4" $ECHO -ne "\x65\x4e\x5f\x0d\xe8\x26\xaf\x2a\x94\x9a\x2e\x21\x9a\x19\xb9\xa0" $ECHO -ne "\x8d\x26\x78\xa1\x4b\x6e\xf6\xd7\x29\x66\xdb\x49\x09\xa0\xca\x4d" $ECHO -ne "\x32\xb0\x31\xf5\x73\xe1\x67\xce\xe0\x5a\x79\x84\xa4\x22\xd4\xc9" $ECHO -ne "\x43\x59\x08\xa8\xd5\x5e\x8c\x72\x61\x70\x9a\xa6\x42\xc0\x42\x22" $ECHO -ne "\x2d\xd0\xbe\xb1\x49\x6e\x36\xbb\x8d\x8f\x03\x9b\xb4\xdb\x5a\x77" $ECHO -ne "\x3e\x29\x91\xc6\x73\x88\xef\x8c\xf7\xde\xe2\x2b\xc2\xce\xcd\x8c" $ECHO -ne "\x92\x60\x96\x29\x89\x99\x62\x99\x81\x36\x9b\x50\xc8\x70\xd6\x8d" $ECHO -ne "\xaf\x6b\x30\xba\xc7\x7a\xca\x4c\x56\x66\x66\x2d\xc7\xa5\xf7\x63" $ECHO -ne "\xa4\x55\x8d\xd4\x92\xdb\x2b\x6b\xb1\xa1\x96\x99\xd9\x25\xdb\x14" $ECHO -ne "\x1c\x49\x04\x67\x25\x45\x0a\x50\x1d\x20\xd8\x8d\xcf\xe7\x03\x20" $ECHO -ne "\xf0\xd7\xc0\xcc\x84\x20\x68\x4a\x63\x41\xa4\x6c\x32\x08\xa2\x37" $ECHO -ne "\x03\x6b\x42\x12\xbe\xa9\x4e\x9b\x97\x16\x92\x48\x56\x32\xae\x2c" $ECHO -ne "\x10\xc6\x31\x14\x8c\xcc\xd6\x23\x09\xf4\x64\x15\x9e\xf1\x35\x75" $ECHO -ne "\x98\x3a\x0c\x12\x29\xaa\xb7\x2b\x82\xe0\xf9\xcd\x05\xed\xb8\xbe" $ECHO -ne "\xb6\xf5\x6f\xcc\x41\xbc\x3a\x81\x39\x3b\x03\xe8\xb2\xab\xb6\xaa" $ECHO -ne "\xed\xa8\x58\xdf\xca\x06\xba\x64\x7b\xc4\xef\xec\x23\x38\x77\xec" $ECHO -ne "\xcf\xd7\xd2\xeb\x75\x3d\x26\xe2\xfa\x66\x49\x0b\x4a\xdc\xe3\x48" $ECHO -ne "\x64\x33\xc4\xb3\x93\xda\xdd\x3c\x08\x83\x7d\x91\x78\xe5\x61\x57" $ECHO -ne "\x67\x37\x73\xe1\x05\xbb\x96\x3e\x26\xc7\x6c\x44\xb5\xfb\x80\xb2" $ECHO -ne "\xd9\xa0\x99\x6b\xbf\x74\x62\xb7\xf7\x14\xec\x07\x12\xfc\xe6\x1b" $ECHO -ne "\xf1\x1d\x24\xfe\xd0\xb9\x61\x76\x56\xa0\xa5\x8c\x63\xce\x96\x5d" $ECHO -ne "\x65\x4f\xae\xcc\x7d\x86\x2d\xd7\x74\x96\x67\xb7\x5c\x94\xa6\x30" $ECHO -ne "\xbd\xb3\x84\x56\x93\x1e\x44\xc5\x43\x5f\x65\x9d\x1a\x92\xb1\x9a" $ECHO -ne "\x4c\x46\x1f\xd2\x64\x54\xb6\x4e\x7e\xb2\x71\x75\xf6\xce\xac\xdc" $ECHO -ne "\x5a\xa1\xd4\xf1\xf5\x71\x6a\x93\x50\xd2\x8b\xb2\xb1\x7f\xaf\x20" $ECHO -ne "\xd2\xc9\xce\xeb\xfb\x1d\x4a\xff\x26\x89\xa2\x60\xed\x8a\xeb\xa7" $ECHO -ne "\x6e\x92\xea\xb7\xef\x7a\xcc\xd9\x4b\xbb\x3e\xad\xc6\x7a\xfa\xbb" $ECHO -ne "\xe0\x25\x0c\x0f\xe2\x14\xf9\x2e\x0b\x5f\xd4\xbd\x8f\x5a\xae\xb6" $ECHO -ne "\xca\xc1\x5a\x89\x4c\x74\x36\xd3\x32\xab\x87\xa7\x7d\x57\x7f\x45" $ECHO -ne "\x1a\x1d\x45\xcc\xc8\xf1\x36\x8c\x4d\x6e\xc9\x01\xb8\x7a\x99\xdc" $ECHO -ne "\x4d\x9a\xa1\xc3\x7a\x81\xac\xa9\x40\x20\xc1\x18\xc7\x1e\x0d\xeb" $ECHO -ne "\xf7\x53\x9b\xcb\xe2\x64\x4e\x17\x1c\x6a\xd7\x74\x6b\xe4\x4b\xe7" $ECHO -ne "\x5f\x06\x31\xac\xe7\x5c\x64\x93\x05\x69\x13\x1a\x34\x52\x3e\x1a" $ECHO -ne "\xc8\xf6\xed\xde\x5e\x79\xf4\xe2\x04\xc3\xb6\xb3\x49\xdc\x7a\xe3" $ECHO -ne "\x52\x12\x1b\x32\xd9\xe2\x5c\x95\x5f\x69\x01\xde\x77\x16\x34\xf7" $ECHO -ne "\xda\x43\x2c\x56\x77\x21\xcc\x86\xc4\x4a\x14\xb8\x29\x28\x0a\xf1" $ECHO -ne "\x79\x8a\x9e\x94\x86\x6c\x6a\x6c\x0f\x15\xe6\xb4\x57\x92\xfc\x1f" $ECHO -ne "\x6d\x98\xbf\x2f\x0b\x97\xb3\x4b\xec\xe6\xd3\xf7\x94\xe4\x2c\xf3" $ECHO -ne "\x20\xfa\x42\x69\xd9\xb6\xb2\x96\x31\x09\x6b\xcb\xd2\x92\x14\x40" $ECHO -ne "\x69\x75\x9a\x83\x49\x44\xed\xe0\xdd\xa3\x3d\x09\xe0\xe4\xcf\x4f" $ECHO -ne "\x3b\x12\x84\xb6\x47\x2b\x1b\x7e\xc2\xac\x8d\xf6\x1c\x74\x26\xb0" $ECHO -ne "\x2a\x27\xf6\x03\xe3\xf9\xe3\xbb\x4a\x1a\x6f\xa2\xf4\x10\xb0\xe5" $ECHO -ne "\x70\x9a\x9b\xdf\xac\x42\x6a\xdc\x80\xc3\x80\x0c\x84\x26\xf0\x23" $ECHO -ne "\xd6\x5b\xcb\x8b\x3b\xe1\x65\xfe\xba\xad\x85\xe9\x1d\x88\xa4\xf8" $ECHO -ne "\x05\xb8\x58\x0b\xb1\x13\xa8\xd8\xea\xfd\x07\x68\xee\x6b\x5c\x88" $ECHO -ne "\x17\x49\x89\x0e\xa5\x7a\xe6\xa0\x9c\x3a\x06\x2d\x71\x84\x2c\xd2" $ECHO -ne "\x1b\x07\xfd\x43\x9b\x48\x9b\xae\x60\x54\x5d\xd3\x2b\xf1\xc0\x0d" $ECHO -ne "\x49\x01\x64\x34\x36\x77\x2f\x0e\xe7\x72\x35\x48\x2f\x05\xaa\xd5" $ECHO -ne "\xb4\x98\x77\xa3\x19\xa2\xf4\xb8\x11\xa7\xa6\x24\x91\xac\x1e\x09" $ECHO -ne "\x38\x04\xc6\xff\x0b\x7d\x36\xc2\xcb\xb8\x9c\x7e\x7b\x49\x8c\x4e" $ECHO -ne "\xbb\x37\x19\x18\x83\xc5\x23\x03\x6c\xcb\x51\x24\xe5\x42\x85\xc7" $ECHO -ne "\x73\x13\x2c\xc8\x22\x28\x50\x83\xbc\x3a\x8e\x60\xac\xb1\xda\x18" $ECHO -ne "\x24\x6d\x64\xd0\xa9\x65\xcd\xd6\x5a\xa7\xaa\xc6\xed\x32\x76\x7b" $ECHO -ne "\x07\x90\xb4\x7b\x5d\x16\x88\x9b\xd7\x5e\x0a\xb7\xbf\xbf\xc4\x5d" $ECHO -ne "\x1c\xbd\x39\xf3\x17\xae\x50\xaa\xc7\xa4\xe9\xad\xa5\xac\x04\xd9" $ECHO -ne "\xa4\x27\x5f\x79\x75\x29\x10\x69\x75\xe9\x06\x53\x7c\x66\x8b\x83" $ECHO -ne "\xf7\x7c\xfd\xcd\x16\xc3\x8c\x8e\x51\x6f\xcb\x68\x0a\x9c\x39\x39" $ECHO -ne "\xb9\x0b\x6a\x16\xc5\x4a\x22\xc0\x31\x09\x22\x28\xa0\x65\x69\x05" $ECHO -ne "\x30\x90\xc1\x18\x22\x05\x9e\xad\xa9\xc3\x54\x3e\x27\xa9\xc4\x41" $ECHO -ne "\x2c\x39\x03\xd2\x8e\x3f\x91\x9a\x4c\xc8\x68\x14\xe4\x1c\xa6\x5f" $ECHO -ne "\x0b\x57\x27\x09\x8a\x7d\xff\x47\x63\xa7\x5a\x29\x82\xa0\x3a\x28" $ECHO -ne "\x30\x9a\x2b\xf3\x69\x63\x18\xcd\xe2\x32\x66\x3c\xd7\x79\xdd\x12" $ECHO -ne "\x86\x34\xc6\x9e\x75\x05\x87\x39\x23\x72\x97\x71\x27\x64\xcd\xd9" $ECHO -ne "\xa6\x2e\x61\xd2\x37\xe4\xae\xc6\xc9\x81\xc0\x2e\x9f\xc6\xf9\x7f" $ECHO -ne "\xd9\x5e\xd0\xa9\x09\x97\x35\xa2\xe3\x4f\xe9\x19\x7c\xa5\xc7\x4d" $ECHO -ne "\x2d\x92\xec\xd6\xef\xda\x55\xf3\xa2\x95\x17\x1b\xce\xbe\x6b\x74" $ECHO -ne "\x70\xee\xdb\xa8\x42\x26\xb1\xcc\xc1\x31\x0a\x67\x92\x13\x9d\x9c" $ECHO -ne "\x12\x18\xa4\x08\x4d\x4d\xfc\x7c\xeb\x59\x6b\x22\x03\xaa\x97\xc3" $ECHO -ne "\x27\xa5\x21\x35\x68\xd2\x57\x54\xca\x58\x38\x82\xc5\x05\xa0\x71" $ECHO -ne "\x01\x1b\xce\x57\x1e\x20\xbf\x89\x96\x2a\x31\x8e\x6e\xaf\x7f\x35" $ECHO -ne "\x08\x10\xd9\x0e\x8a\x78\xb0\x48\x98\xa4\x64\x14\xa2\xcf\x23\x2d" $ECHO -ne "\x0a\x7b\x84\xe5\xfd\x29\x49\x15\x3d\x75\x39\xfd\xaa\xd6\xa4\xb9" $ECHO -ne "\x05\x12\x57\x31\x04\xdc\x26\x34\x16\x3f\xa7\x03\x32\x1d\x4b\x1d" $ECHO -ne "\x78\xdc\x9b\x79\x96\x9a\x87\x6e\xb4\x80\xaa\x01\x19\x33\x92\xb0" $ECHO -ne "\x16\xc9\x94\x9c\xe7\xa5\x63\xe6\x18\x13\xb2\x34\xbd\x98\x41\xd6" $ECHO -ne "\xa4\xc8\xb9\x6e\x06\x9c\x72\xf8\x49\xab\xd5\x47\x9e\xa1\xe6\xde" $ECHO -ne "\x62\xd0\xec\xaf\xbf\x1b\x8a\xaf\x63\xa0\x29\xbe\x3d\x87\xa0\x22" $ECHO -ne "\xce\x46\x4e\x18\x30\x7b\x3c\x3d\x86\xe1\x9e\xb6\x59\xef\x1c\x43" $ECHO -ne "\x65\xd0\x3d\x53\xd0\x41\x20\x40\xb7\x2b\xb1\xdd\x52\x2c\xdd\x68" $ECHO -ne "\x44\xc1\xbe\x40\x72\x61\xd7\x25\x5d\xf5\x69\xce\x3a\x3b\x2e\x9b" $ECHO -ne "\x13\x19\x79\x1a\xf0\xee\xb0\xe7\x17\x44\x45\xe8\x2d\x59\x50\xbc" $ECHO -ne "\x40\x67\x66\x12\x20\xcc\x43\x8a\x9c\x1d\xde\xac\x2d\x00\x76\xb2" $ECHO -ne "\x98\x8a\xa9\xde\x1c\xb6\x8b\x32\x19\x67\x1c\x67\x95\x41\x40\x60" $ECHO -ne "\xf3\x13\x44\xb8\xc5\x18\xa7\xca\xdd\x8c\x5a\x8f\x72\x69\xf1\x31" $ECHO -ne "\xa9\xd2\xeb\xac\x3e\x2f\xdc\xc7\xe0\x00\x78\x5d\x72\xff\x01\x95" $ECHO -ne "\x86\x4a\x90\x2b\xf8\x10\xc5\xc2\xd1\x9d\x7a\xc3\x65\xb1\xfd\x2d" $ECHO -ne "\x09\x0b\xcd\xdf\x03\x80\x3e\x44\x81\x65\x49\x4f\x50\x7e\x1f\x75" $ECHO -ne "\x97\xc6\x05\xda\x5a\xe9\xf6\xee\xe5\x66\xcc\x5e\x17\xe2\x8c\xb2" $ECHO -ne "\x06\x5b\xdd\x41\x0d\x26\xcc\x87\x0d\x37\x2e\x2d\x35\xe0\x5d\x93" $ECHO -ne "\xc5\xdf\x2d\xb4\xa2\xb1\x1b\x0e\x9b\xe6\x76\xb4\x28\x69\x5c\xe9" $ECHO -ne "\x4e\x27\x6f\x52\xcb\x4d\xb3\xc8\xaa\xea\xd3\x1a\x57\x00\xdf\x20" $ECHO -ne "\x2d\x42\xea\x6a\x18\x0a\xac\xae\x9a\x32\x08\x23\x99\xb7\xd8\xe5" $ECHO -ne "\x75\x3a\x65\x8b\x2f\xaa\x4f\x7b\x68\xd5\x66\x76\xf4\xec\x3d\xdb" $ECHO -ne "\xe9\x37\xdb\x69\x40\x6d\x35\x4f\x77\xfa\x8f\x07\x60\xac\x8e\x3b" $ECHO -ne "\x89\x46\x3c\x16\xd4\x4b\x6e\x71\x4f\x00\x10\x22\x14\x12\xca\x72" $ECHO -ne "\xe0\x6c\x54\x2f\x0e\x32\x8c\xba\x53\xad\x51\x48\xaf\xee\xb2\xca" $ECHO -ne "\x93\x4a\x46\x24\x1f\x09\x83\x69\x1c\x3f\x72\x50\x70\xff\x10\x74" $ECHO -ne "\x21\xef\x4a\x08\x38\x25\x4c\x54\xb6\x34\x83\x64\x99\x22\x0f\x02" $ECHO -ne "\x49\x58\x50\x74\xa3\xbe\xc2\x17\x05\xa7\x60\x55\xc4\x21\x52\x0c" $ECHO -ne "\x57\xee\x0f\x64\x6a\xa9\x73\x25\xa1\x2a\x94\x1d\x00\xca\x65\xc4" $ECHO -ne "\x39\xfc\x53\xa8\xe7\x4c\x07\x44\x5f\x29\x19\x98\x08\x16\x53\x1a" $ECHO -ne "\xba\xee\x8e\x2e\x16\x97\x66\x5b\x7c\xb5\x63\x2d\x31\x18\xdb\x64" $ECHO -ne "\xc5\x69\x15\xa9\xe8\x23\x5f\x92\xdb\x75\x60\x90\x6a\xbf\xb5\xba" $ECHO -ne "\xe5\xa5\x70\xce\x26\xd0\xc1\x63\xcb\x0e\x21\x67\x1e\x8e\x20\x32" $ECHO -ne "\xa1\x2d\x51\xfc\x32\xa0\xc9\xd0\x32\x91\x9a\xda\x45\x73\x2e\x97" $ECHO -ne "\x09\x17\x0c\xea\xe4\x89\x94\xe8\xad\x64\xd6\x78\x02\x07\x79\x06" $ECHO -ne "\xa4\x01\xce\xd0\xcc\x33\x20\x8d\xc9\x2d\x67\xdf\x85\x06\xb5\x21" $ECHO -ne "\x74\x61\x49\x99\x98\xec\x28\x06\xc4\xbd\x25\xb5\x62\x2d\xb0\xba" $ECHO -ne "\x5f\x4c\xc4\x33\x85\x42\x58\x11\xd4\xff\x27\x21\x3c\x57\x9e\xd9" $ECHO -ne "\xc4\xb1\x6d\x8d\x4a\x8c\x8a\x80\x6c\x1e\x16\x5f\xc1\xc4\x68\x4a" $ECHO -ne "\xca\x20\xb1\x40\x10\x1b\x1b\x6c\xf7\x82\xf8\xd4\x35\x29\x10\x76" $ECHO -ne "\x7d\x3a\x4d\x4d\x49\x9b\x62\x65\x66\xd4\xda\x81\x24\xca\x4a\x48" $ECHO -ne "\x48\x2f\x83\x48\xd1\x09\xdf\x2f\x17\x8b\xc5\x37\x89\x94\x15\xb1" $ECHO -ne "\x36\x58\xcd\x80\xb4\x19\xc5\xc6\xda\xda\x16\x95\x82\x14\xc5\x19" $ECHO -ne "\x61\x6e\xb5\xcc\x27\xb5\xf3\xdb\xef\x6e\x44\x37\xbf\xdc\x11\xf9" $ECHO -ne "\xa0\xf2\x78\x30\x85\xc0\xc0\x07\x67\x02\x66\x56\x7c\x76\xee\x7a" $ECHO -ne "\x97\x6e\x02\x5e\x08\xc0\x35\x02\x4a\x87\x39\x4c\xd6\xc4\xe0\x99" $ECHO -ne "\xcd\xd9\xda\x2c\x49\x18\x5c\x22\xb6\x51\x4b\xa0\x58\x8b\x7a\x55" $ECHO -ne "\x61\xdc\xa5\x21\x83\x1d\x47\x9c\x0b\xf4\x74\xba\x08\x85\xe4\xc8" $ECHO -ne "\x80\xbe\x80\x46\xfc\x46\x85\x60\x64\xa6\xc4\xc1\xae\x69\x67\x0b" $ECHO -ne "\x8e\xac\xa2\xc0\xf4\x6b\x6f\x7a\x9e\x00\xdd\x4d\x59\x57\x4a\x78" $ECHO -ne "\x08\x64\x08\x84\x80\x50\x34\xb1\x3b\xc7\x71\x3f\x3e\x1c\x1d\x4e" $ECHO -ne "\x4e\xa9\xb0\x32\x02\x10\x8e\x88\x71\xed\x87\x2c\x32\x4d\x57\x05" $ECHO -ne "\xf1\xba\xa0\xf9\x61\x30\x4b\x18\x65\x6e\x38\xf9\x41\xdd\xf1\x48" $ECHO -ne "\x63\x38\x50\x10\xc1\xac\x1b\xf2\x5b\xaa\x15\xf4\x89\x0e\xe9\x77" $ECHO -ne "\x80\x56\x50\x18\x81\x71\xd8\xdb\x0d\x6a\xce\xd2\xb6\x76\xbd\x35" $ECHO -ne "\xf0\x96\xe1\x06\x8b\x09\xab\x83\x21\x10\x10\x30\x68\x30\xad\xe0" $ECHO -ne "\xc2\x62\xa2\x99\x0b\x92\x17\x19\xab\xe3\x7a\xd1\x90\xae\x5c\x2b" $ECHO -ne "\x6e\xbe\x31\xec\x72\x78\x03\x7a\x85\x70\xe0\x67\x36\xe0\xdb\x63" $ECHO -ne "\x6e\xed\x26\x94\xcc\x9b\x4e\xa8\x23\x57\x56\xe1\x49\x61\x31\x5e" $ECHO -ne "\xc8\x2b\x81\x05\x23\x18\xdb\x68\x34\x0b\x6c\xf1\xfc\xc7\xdd\xdf" $ECHO -ne "\x1a\x39\xf8\xf6\x72\xb9\x4d\xc9\x80\xbf\x23\x93\x24\x76\xdd\x6d" $ECHO -ne "\x0a\x8f\x18\xe1\x81\x8f\x48\x7b\x48\x2e\xd0\xb5\xd0\xcb\xa1\x46" $ECHO -ne "\xae\x1c\x26\x02\xd2\xe0\xf4\x56\x8c\x8a\x01\x97\x4e\x5f\xd1\xde" $ECHO -ne "\x9a\x10\x31\x0d\x4c\xbc\x40\x06\xc5\x04\x92\x91\x88\x81\x58\x5d" $ECHO -ne "\x55\x13\xab\x4f\xaa\xbd\xee\xa0\x6a\x80\xb2\x83\xd0\x46\x31\xa0" $ECHO -ne "\xbc\x2c\xf9\x0d\xad\xe2\x62\xb0\xac\xa4\x91\x84\xb8\x31\x99\xb9" $ECHO -ne "\x45\xb3\x47\x1e\xc2\x96\xc9\x9d\xcc\xd3\xcc\x71\xc4\xf3\x9a\x92" $ECHO -ne "\x2b\xac\xc3\x8c\xe1\xdc\x40\x66\x64\xe8\x24\x35\x50\x26\x68\x0b" $ECHO -ne "\x79\x96\x81\xb6\x36\xc7\xa4\x82\x0d\x32\x65\xc3\x4c\x61\x49\x32" $ECHO -ne "\x09\x14\x22\xac\x37\x69\x34\xb4\x6c\xdd\xbc\x95\x54\x6b\x59\x53" $ECHO -ne "\xc6\x50\x32\x09\x99\x14\x8c\x18\x74\xcc\x05\x86\x7a\x06\x48\x50" $ECHO -ne "\x6e\xe0\xaa\x41\xbb\xb0\xbc\x19\xaa\x2c\x12\x9c\xcd\xa5\x1c\x6d" $ECHO -ne "\x19\x0a\x62\x02\xfe\xd3\x4a\xcc\x7c\x6a\xa5\x72\x06\x35\xfb\x8d" $ECHO -ne "\xf9\xab\x1e\x0b\x29\x73\x70\xb5\xe8\xf6\x54\xb6\x4c\xc8\xea\x30" $ECHO -ne "\x8c\xaf\xd0\xd3\xb0\x20\x59\x80\x61\x40\xc8\x19\x99\x6d\x97\xb3" $ECHO -ne "\xca\x66\x1e\x16\x3d\xa7\x74\xa6\x58\xf0\xd4\x00\x67\xdc\xbb\x8a" $ECHO -ne "\x4a\x7b\x75\xa4\x6e\x89\xc4\x44\x44\x3d\x72\xb4\x52\x8a\xc0\xc2" $ECHO -ne "\x11\x40\x22\x9a\x14\x09\x66\xc2\x03\xcc\x04\x86\x02\x03\xa6\x8a" $ECHO -ne "\xab\x60\xe0\xe8\xdc\x2b\x5d\x0d\x73\xb5\x8f\x74\xc6\xce\xdb\xb5" $ECHO -ne "\xa8\xe7\x95\x3f\x8b\xaf\xb9\x87\xbc\x63\xab\x84\xea\x93\x1e\x9d" $ECHO -ne "\xb4\xe0\x83\xc8\x4a\xc9\xc7\xb9\xc7\xf2\xc6\x25\x10\x58\xc0\x21" $ECHO -ne "\x64\xa1\x08\xd3\x10\x2f\x94\x40\x5a\x56\x17\xa1\x0f\xa6\xfb\xda" $ECHO -ne "\xd3\x12\x42\x31\x71\x09\xa5\x2e\x8b\xd1\x69\x5c\x99\x5b\x09\x52" $ECHO -ne "\xc6\x9b\x5a\x18\x0c\x06\x47\x42\x8a\xc3\xad\xef\x9a\xe9\x9d\xf6" $ECHO -ne "\x2b\x81\x72\x48\x05\x20\x16\x10\xa3\xc3\xc5\xd2\x71\x0e\xca\x04" $ECHO -ne "\x17\xef\xdf\x39\x64\x26\x4c\x9f\x22\xb4\x13\x1c\x3d\xe7\x55\x40" $ECHO -ne "\x2e\xd1\x91\x28\xc8\x1c\x68\x69\x65\x97\x13\x75\xfe\x5b\x5c\xb1" $ECHO -ne "\x9b\x5a\xf7\xd2\x02\xb2\x0b\x41\x36\x67\xe7\xa9\x10\x80\xd0\x5c" $ECHO -ne "\x64\x08\x67\xda\x56\x36\x53\x4a\xa8\xca\x16\x88\xc5\x79\xdd\x3e" $ECHO -ne "\x87\x71\x13\x39\xae\xfd\x2a\x93\x6e\xbb\x96\x02\x39\xea\xda\x5a" $ECHO -ne "\x87\xb8\xfa\x54\x2c\x49\xa3\xa0\xbb\xa5\xc4\x10\x5c\xd2\x10\x10" $ECHO -ne "\x0c\x88\xb2\xd4\xf4\x67\x6a\x93\x5b\xbb\x20\x06\xdc\x75\x7f\x3a" $ECHO -ne "\x9b\xa3\xb0\x76\x98\xd9\x77\x32\x97\xa5\xdc\x64\xa4\x7b\xa5\xae" $ECHO -ne "\xaa\x15\x2d\x59\x0c\xc1\x7a\x40\xd2\xc2\xbb\x45\x10\xe1\x9a\x46" $ECHO -ne "\x52\x91\xe4\x24\x21\x9c\x46\xee\x05\x57\x44\x5e\x41\xad\x5a\x08" $ECHO -ne "\x46\x0b\xa0\xdf\xb4\x59\x7a\xe4\x41\xa3\x0a\x59\x5e\x2b\x17\x20" $ECHO -ne "\x19\x02\x6c\xe6\xe2\x48\x85\x99\xb3\xba\xfc\x9c\xe3\xcd\xf9\x31" $ECHO -ne "\x5b\xf1\x86\x64\x9d\x8f\x93\x24\xa3\x29\x38\x94\xcb\x1e\x71\x87" $ECHO -ne "\x54\xf2\x27\x22\x4e\x57\x26\x9a\x82\xb5\x6e\x6c\x1c\xad\x2d\x2b" $ECHO -ne "\x22\x62\x0a\xd0\x23\x5d\x5a\x75\x15\xae\xa0\x26\x04\x21\x6d\x2c" $ECHO -ne "\xfe\x06\xd9\x60\x61\x20\x8e\xea\xef\xba\x59\x03\x64\xda\xe5\xb2" $ECHO -ne "\x30\xc0\x9c\xdc\xcf\x11\x77\xe9\x23\x54\x33\xb8\xe9\x05\xab\x4c" $ECHO -ne "\x5b\xb5\x4b\x2d\x03\x0c\x51\xc5\x80\x11\x51\xac\xeb\x8d\x4c\x25" $ECHO -ne "\x21\x98\x79\xb0\x38\x99\x9c\xbc\xe2\x96\xe9\x4a\xd0\xad\x56\x6a" $ECHO -ne "\x65\xe1\xd4\x90\x12\x4a\xa5\x48\x06\xc6\x48\x31\xac\xaf\x21\x0a" $ECHO -ne "\x56\xa2\x90\x12\xd7\x53\xa8\x48\x03\x75\x2e\x36\x14\xf4\x50\x89" $ECHO -ne "\x7c\x49\x4e\x4a\x2a\xbc\x46\xc3\x2d\x16\x4e\x42\x25\x28\xd4\xf2" $ECHO -ne "\x01\x47\xa3\x5a\xd1\x6a\x0c\x1c\x35\x9c\x52\xc8\x2d\xeb\xab\x15" $ECHO -ne "\x2a\x99\x51\x45\x22\x9c\x77\xaf\x85\x77\xbc\x84\xb2\xf5\x99\xe9" $ECHO -ne "\xd8\xd9\xc1\x90\x83\x02\xeb\xde\x8f\x91\x82\xa3\x0c\x73\x1a\x05" $ECHO -ne "\x6b\x9c\x98\x28\xc5\x56\x55\xe9\xf3\x74\x24\x81\x48\xaf\x21\xb4" $ECHO -ne "\x84\x2d\x6b\x45\x88\xc2\x90\xb1\x12\x12\x60\xc8\x6a\xec\x61\x33" $ECHO -ne "\xa2\xc3\xb3\x58\xbf\x29\x1c\x48\x97\x7a\xea\x20\x65\x03\x7f\x22" $ECHO -ne "\x45\xa5\xdd\x03\x5c\x52\xdc\x30\x85\xde\xd9\x47\x5e\xeb\x31\x65" $ECHO -ne "\x0b\xf3\x13\x80\xae\x3e\x07\x52\x2a\x47\xb9\x7b\x7c\xa8\x41\x79" $ECHO -ne "\x95\x2e\xcf\x3d\x60\x08\xe6\x26\x00\xd1\x82\x60\x70\x45\xa1\x4a" $ECHO -ne "\x48\xa2\x18\x76\x35\xb5\xe8\x6c\x0d\x42\xd2\xba\x2c\x13\x5b\x25" $ECHO -ne "\x27\xd4\x8d\x73\x7a\xf8\xd9\xfe\xf3\xd3\x81\x73\x83\xe8\x65\x60" $ECHO -ne "\xf3\x5b\x18\x07\x15\x04\x60\x67\x51\xca\xab\x91\x85\xdc\x61\x3a" $ECHO -ne "\xe9\x72\xc2\xd1\xa4\x68\xe2\x00\xc8\x0c\x5e\x82\xa0\x0b\x82\x16" $ECHO -ne "\x40\x82\xb2\x98\x7b\x76\xf8\x5b\xb5\xf6\x8d\xfb\xc9\x36\x8c\x1e" $ECHO -ne "\xa6\xc9\xb5\x95\xd8\x36\x28\x36\xee\xa9\xa2\x72\x66\x58\xf5\x90" $ECHO -ne "\x02\x95\xee\xa8\xfc\x79\xfa\x6f\x66\xf6\x47\xd6\x4f\x10\x95\x86" $ECHO -ne "\x54\x0d\xa0\x22\x26\x01\xbe\x63\xc5\xf1\xac\x36\x4a\xe1\x0f\x6b" $ECHO -ne "\xe7\xba\x4f\x20\x17\xc0\xf9\x02\x5d\xc4\xc0\xaf\xf0\x1f\x88\x54" $ECHO -ne "\xe4\x6e\xc0\xa2\xbb\x59\x6f\x82\x21\xbb\x50\x9c\x4e\x92\x83\x24" $ECHO -ne "\x23\xf8\x28\x4c\x45\x79\x88\x71\x3b\x05\x79\x98\x4f\xa1\x00\x2d" $ECHO -ne "\x6e\x68\x08\x46\x3b\xfb\xf0\x5c\x22\xf3\x42\x40\x7d\x5e\x67\x3f" $ECHO -ne "\xdf\xff\x2e\x73\x0d\x31\xb5\xc0\x78\x4c\x0f\x85\x0f\xdb\xe6\x2b" $ECHO -ne "\x86\x0f\xb3\x8f\x9d\x1a\xe6\xe2\xdb\x32\x68\xfb\x5a\x79\x0a\xf4" $ECHO -ne "\x25\x28\x01\x39\xd2\x7c\x05\x7b\x0b\x18\xbb\x9c\x68\x31\x8d\xb0" $ECHO -ne "\xfc\x69\x2d\x17\x2c\x33\x62\xa6\x24\x6f\x0e\xcc\xdc\xff\x7b\xdf" $ECHO -ne "\x78\xd3\x88\x5c\x4a\xbd\xeb\x14\xda\xe0\x0e\xd3\xf3\x5b\xd2\xaa" $ECHO -ne "\xd1\x64\x82\x42\x0c\xfe\x54\x90\x40\xeb\x75\x13\x63\x68\xb5\x52" $ECHO -ne "\x0a\xd9\x3b\xc5\xd0\x14\xe1\xd2\x35\xab\x2b\x8b\xed\x21\xf3\xe1" $ECHO -ne "\xcb\xed\xbc\x3e\x64\x81\x63\x5f\xaa\xf3\xb8\x05\x10\x29\xe1\x67" $ECHO -ne "\xae\x0a\x16\xbb\x71\x05\x02\x46\xba\xef\x30\xda\xe7\x3a\x18\x4b" $ECHO -ne "\xcb\x0e\x97\xd1\xe9\xf5\x7a\x73\xc1\x60\x91\xaf\x63\x4a\xc8\x57" $ECHO -ne "\xa6\xb0\x09\xb4\x95\x98\x87\xa9\xc4\x5e\x04\xe7\xef\xbf\xe5\x8c" $ECHO -ne "\x39\x8e\xe2\xdd\x9a\xc0\xbf\x37\x9c\x38\xa3\xad\xc6\x14\x86\x3f" $ECHO -ne "\x7d\xc1\x9d\xb5\xbb\x68\x58\x4c\x14\x03\xf1\x36\xd4\xf2\xce\xb6" $ECHO -ne "\xbc\x9d\xb3\x31\x55\x68\x0c\x5f\xd0\x30\xe2\x05\xae\x68\x4e\x11" $ECHO -ne "\x5a\x28\x15\x68\x8f\xac\xa3\x1b\x89\xad\x48\x89\x08\x08\xa3\x3b" $ECHO -ne "\x26\x5e\x32\x1d\x6d\x73\x2b\x20\x90\x08\x4a\x12\xb0\x1f\xf1\xa3" $ECHO -ne "\x37\xf8\xec\x30\x21\x06\xf3\xbb\x3b\x46\xf9\xaa\xeb\x1a\x31\x33" $ECHO -ne "\x2b\xa5\x87\x4d\x4d\xbc\x13\xda\xa9\xa0\x4c\xca\x0b\x46\xa6\x09" $ECHO -ne "\x41\x73\xbb\x3f\x71\x9f\x6a\x88\x41\x02\x20\x3f\x54\x6d\x42\xc8" $ECHO -ne "\x70\x6e\x64\xd0\x50\x88\x83\xc4\x33\x78\x6d\x04\xb6\x43\xed\x5e" $ECHO -ne "\x72\x71\x6b\xd5\x65\x80\xcf\x66\x46\x29\x10\xdb\x79\xd1\xa9\x95" $ECHO -ne "\xb7\x9d\xf4\xa9\x93\xb4\x5a\x00\xc6\xb2\xad\xbf\x7e\xaa\xe6\x8d" $ECHO -ne "\x8c\xc0\x6b\xf5\xc2\xad\x00\xfb\x08\x63\xe2\xb5\xb1\xe0\x76\xac" $ECHO -ne "\x0a\xe4\x72\xb5\x72\xbc\x24\x6b\xef\x62\x0f\xaa\xb9\x28\xd2\x3c" $ECHO -ne "\xf6\x3e\x26\x20\x8b\xcc\xd2\x61\xcd\xd4\xc7\xed\x39\xa8\x39\x4e" $ECHO -ne "\x7e\x05\x97\xeb\x29\x24\x3a\xb2\xc9\xbd\xad\xcf\xf0\x22\xbc\xdd" $ECHO -ne "\xb8\x8c\x2e\x6a\xbf\x4f\x67\x2f\xfc\x07\xd0\x53\x0c\x54\x30\x35" $ECHO -ne "\xf8\xf1\x76\x45\x26\x5a\x86\x11\x1e\xeb\x58\xc0\x2d\x6c\x3d\x87" $ECHO -ne "\xa6\xca\x8e\x89\x4f\x75\x88\xf9\xb9\x9a\x99\x8b\x68\x22\xe2\x52" $ECHO -ne "\x4d\x46\x8d\x44\x31\x2b\xc1\xb0\x19\xa7\x90\xfb\x95\xda\x19\x2f" $ECHO -ne "\x6e\x0d\xe2\xc1\x85\xd0\x1f\x9b\xd3\xae\x33\xe3\x55\xa4\x77\xf2" $ECHO -ne "\xf1\xd7\xa8\xf0\x57\x30\xc4\x3b\xe6\x55\x97\xf9\xe3\x89\x82\xda" $ECHO -ne "\xae\x02\x45\xb1\x86\x8c\x84\x4c\xb2\xcf\x82\xdb\x4e\x04\x45\xcc" $ECHO -ne "\x19\x53\x9e\x2f\x95\xa9\xc7\xa8\x08\x17\x61\xc1\x8c\x26\x7f\x9b" $ECHO -ne "\x07\x8c\xe7\x77\x2d\x12\xd2\xcd\xc6\x97\xcf\x29\x3a\x1e\xac\x2b" $ECHO -ne "\x69\xb9\xb4\x61\xee\xeb\xb3\xae\x60\x18\xa0\x3a\xe5\xc0\xb9\x58" $ECHO -ne "\x38\x4d\x32\x57\x81\x89\x99\x29\x73\xdd\x47\x43\x2f\x1e\x39\xc6" $ECHO -ne "\x06\xbf\x7f\x64\x9e\x91\xc3\x9f\x18\x1b\xba\xf8\xb5\x29\x5d\xe3" $ECHO -ne "\x46\x7e\xb5\x1a\xfd\x9b\xb0\x1b\x85\x06\xc3\xc5\x09\xdb\x82\xd0" $ECHO -ne "\xd1\xff\xe1\x0f\xeb\x37\x1d\xce\x65\x6d\x26\x55\xe0\x20\x00\xc4" $ECHO -ne "\x36\x2f\xba\x86\x26\xc6\x7b\xa4\xe9\xb1\x41\x20\x04\x11\xeb\x24" $ECHO -ne "\x3c\x72\xbf\xd3\xc5\xb3\xbd\xce\x14\x45\x2d\x50\x01\x00\x26\x39" $ECHO -ne "\x3c\x85\x17\x0e\x42\x66\x8a\x1c\x94\xa8\x90\x02\xc4\x42\xd8\xd1" $ECHO -ne "\xcc\x94\x7a\x25\xad\xfd\x8d\xa4\x0e\xe0\xcb\x92\x5e\x6f\x14\x2b" $ECHO -ne "\x29\xbd\xc0\x81\x20\x3f\x0b\x2c\x7a\x2c\xe7\xba\x6d\x99\x14\xbe" $ECHO -ne "\xd5\x39\xc8\x6f\x2e\xbd\x79\x59\x19\x75\xb6\xf5\x4f\x12\xf6\x8e" $ECHO -ne "\x40\xa0\x00\x8b\x12\xe8\xfb\xb7\x27\xaa\xd3\x36\x0c\xfc\xe1\x40" $ECHO -ne "\x01\xff\x8b\xb9\x22\x9c\x28\x48\x5f\xa5\xca\xf3\x80" } pbzip_4m_zeros() { $ECHO -ne "\x42\x5a\x68\x31\x31\x41\x59\x26\x53\x59\x63\xe3\xec\xa2\x00\x06" $ECHO -ne "\xe4\xc1\x00\xc0\x00\x02\x00\x00\x08\x20\x00\x30\xcc\x09\xaa\x69" $ECHO -ne "\x94\xa1\x36\xa9\x28\x4f\x17\x72\x45\x38\x50\x90\x63\xe3\xec\xa2" $ECHO -ne "\x42\x5a\x68\x31\x31\x41\x59\x26\x53\x59\x63\xe3\xec\xa2\x00\x06" $ECHO -ne "\xe4\xc1\x00\xc0\x00\x02\x00\x00\x08\x20\x00\x30\xcc\x09\xaa\x69" $ECHO -ne "\x94\xa1\x36\xa9\x28\x4f\x17\x72\x45\x38\x50\x90\x63\xe3\xec\xa2" $ECHO -ne "\x42\x5a\x68\x31\x31\x41\x59\x26\x53\x59\x63\xe3\xec\xa2\x00\x06" $ECHO -ne "\xe4\xc1\x00\xc0\x00\x02\x00\x00\x08\x20\x00\x30\xcc\x09\xaa\x69" $ECHO -ne "\x94\xa1\x36\xa9\x28\x4f\x17\x72\x45\x38\x50\x90\x63\xe3\xec\xa2" $ECHO -ne "\x42\x5a\x68\x31\x31\x41\x59\x26\x53\x59\x63\xe3\xec\xa2\x00\x06" $ECHO -ne "\xe4\xc1\x00\xc0\x00\x02\x00\x00\x08\x20\x00\x30\xcc\x09\xaa\x69" $ECHO -ne "\x94\xa1\x36\xa9\x28\x4f\x17\x72\x45\x38\x50\x90\x63\xe3\xec\xa2" $ECHO -ne "\x42\x5a\x68\x31\x31\x41\x59\x26\x53\x59\xc9\xb5\x21\xef\x00\x04" $ECHO -ne "\x8d\x40\x20\xc0\x00\x01\x00\x00\x08\x20\x00\x30\xcc\x05\x29\xa6" $ECHO -ne "\x4a\x11\xb1\x4a\x11\xe2\xee\x48\xa7\x0a\x12\x19\x36\xa4\x3d\xe0" } prep() { rm -f t* hello_$ext >t1.$ext hello_$ext >t2.$ext } check() { eval $2 >t_actual 2>&1 if $ECHO -ne "$expected" | cmp - t_actual; then echo "PASS: $1" else echo "FAIL: $1" FAILCOUNT=$((FAILCOUNT + 1)) fi } mkdir testdir 2>/dev/null ( cd testdir || { echo "cannot cd testdir!"; exit 1; } expected="$unpack: z: No such file or directory 1 HELLO " prep; check "$unpack: doesnt exist" "${bb}$unpack z t1.$ext; echo \$?; cat t1" expected="$unpack: t.zz: unknown suffix - ignored 1 HELLO " prep; >t.zz; check "$unpack: unknown suffix" "${bb}$unpack t.zz t1.$ext; echo \$?; cat t1" # In this case file "t1" exists, and we skip t1.gz and unpack t2.gz expected="$unpack: can't open 't1': File exists 1 HELLO " prep; >t1; check "$unpack: already exists" "${bb}$unpack t1.$ext t2.$ext; echo \$?; cat t1 t2" # From old testsuite expected="HELLO\n0\n" prep; check "$unpack: stream unpack" "cat t1.$ext | ${bb}$unpack; echo \$?" expected="ok\n" prep; check "$unpack: delete src" "${bb}$unpack t2.$ext; test ! -f t2.$ext && echo ok" ) rm -rf testdir # This test is only for bunzip2 if test "${0##*/}" = "bunzip2.tests"; then if test1_bz2 | ${bb}bunzip2 >/dev/null \ && test "`test1_bz2 | ${bb}bunzip2 | md5sum`" = "61bbeee4be9c6f110a71447f584fda7b -" then echo "PASS: $unpack: test_bz2 file" else echo "FAIL: $unpack: test_bz2 file" FAILCOUNT=$((FAILCOUNT + 1)) fi if pbzip_4m_zeros | ${bb}bunzip2 >/dev/null \ && test "`pbzip_4m_zeros | ${bb}bunzip2 | md5sum`" = "b5cfa9d6c8febd618f91ac2843d50a1c -" then echo "PASS: $unpack: pbzip_4m_zeros file" else echo "FAIL: $unpack: pbzip_4m_zeros file" FAILCOUNT=$((FAILCOUNT + 1)) fi fi exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255)) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cp.tests�������������������������������������������������������������������0000755�0000000�0000000�00000021727�12263563520�015525� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2010 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # Opening quote in "omitting directory 'dir'" message: sq='`' # GNU cp: ` sq="'" # bbox cp: ' rm -rf cp.testdir >/dev/null mkdir cp.testdir mkdir cp.testdir/dir > cp.testdir/dir/file ln -s file cp.testdir/dir/file_symlink > cp.testdir/file ln -s file cp.testdir/file_symlink ln -s dir cp.testdir/dir_symlink # testing "test name" "command" "expected result" "file input" "stdin" rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1 testing "cp" '\ cd cp.testdir || exit 1; cp * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1 test ! -L file && test -f file || echo BAD: file test ! -L file_symlink && test -f file_symlink || echo BAD: file_symlink test ! -L dir && test ! -e dir || echo BAD: dir test ! -L dir_symlink && test ! -e dir_symlink || echo BAD: dir_symlink ' "\ cp: omitting directory ${sq}dir' cp: omitting directory ${sq}dir_symlink' 1 " "" "" rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1 testing "cp -d" '\ cd cp.testdir || exit 1; cp -d * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1 test ! -L file && test -f file || echo BAD: file test -L file_symlink && test -f file_symlink || echo BAD: file_symlink test ! -L dir && test ! -e dir || echo BAD: dir test -L dir_symlink && test ! -e dir_symlink || echo BAD: dir_symlink ' "\ cp: omitting directory ${sq}dir' 1 " "" "" rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1 testing "cp -P" '\ cd cp.testdir || exit 1; cp -P * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1 test ! -L file && test -f file || echo BAD: file test -L file_symlink && test -f file_symlink || echo BAD: file_symlink test ! -L dir && test ! -e dir || echo BAD: dir test -L dir_symlink && test ! -e dir_symlink || echo BAD: dir_symlink ' "\ cp: omitting directory ${sq}dir' 1 " "" "" rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1 testing "cp -L" '\ cd cp.testdir || exit 1; cp -L * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1 test ! -L file && test -f file || echo BAD: file test ! -L file_symlink && test -f file_symlink || echo BAD: file_symlink test ! -L dir && test ! -e dir || echo BAD: dir test ! -L dir_symlink && test ! -e dir_symlink || echo BAD: dir_symlink ' "\ cp: omitting directory ${sq}dir' cp: omitting directory ${sq}dir_symlink' 1 " "" "" rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1 testing "cp -H" '\ cd cp.testdir || exit 1; cp -H * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1 test ! -L file && test -f file || echo BAD: file test ! -L file_symlink && test -f file_symlink || echo BAD: file_symlink test ! -L dir && test ! -e dir || echo BAD: dir test ! -L dir_symlink && test ! -e dir_symlink || echo BAD: dir_symlink ' "\ cp: omitting directory ${sq}dir' cp: omitting directory ${sq}dir_symlink' 1 " "" "" rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1 testing "cp -R" '\ cd cp.testdir || exit 1; cp -R * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1 test ! -L file && test -f file || echo BAD: file test -L file_symlink && test -f file_symlink || echo BAD: file_symlink test ! -L dir && test -d dir || echo BAD: dir test -L dir_symlink && test -d dir_symlink || echo BAD: dir_symlink test ! -L dir/file && test -f dir/file || echo BAD: dir/file test -L dir/file_symlink && test -f dir/file_symlink || echo BAD: dir/file_symlink ' "\ 0 " "" "" rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1 testing "cp -Rd" '\ cd cp.testdir || exit 1; cp -Rd * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1 test ! -L file && test -f file || echo BAD: file test -L file_symlink && test -f file_symlink || echo BAD: file_symlink test ! -L dir && test -d dir || echo BAD: dir test -L dir_symlink && test -d dir_symlink || echo BAD: dir_symlink test ! -L dir/file && test -f dir/file || echo BAD: dir/file test -L dir/file_symlink && test -f dir/file_symlink || echo BAD: dir/file_symlink ' "\ 0 " "" "" rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1 testing "cp -RP" '\ cd cp.testdir || exit 1; cp -RP * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1 test ! -L file && test -f file || echo BAD: file test -L file_symlink && test -f file_symlink || echo BAD: file_symlink test ! -L dir && test -d dir || echo BAD: dir test -L dir_symlink && test -d dir_symlink || echo BAD: dir_symlink test ! -L dir/file && test -f dir/file || echo BAD: dir/file test -L dir/file_symlink && test -f dir/file_symlink || echo BAD: dir/file_symlink ' "\ 0 " "" "" rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1 testing "cp -RL" '\ cd cp.testdir || exit 1; cp -RL * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1 test ! -L file && test -f file || echo BAD: file test ! -L file_symlink && test -f file_symlink || echo BAD: file_symlink test ! -L dir && test -d dir || echo BAD: dir test ! -L dir_symlink && test -d dir_symlink || echo BAD: dir_symlink test ! -L dir/file && test -f dir/file || echo BAD: dir/file test ! -L dir/file_symlink && test -f dir/file_symlink || echo BAD: dir/file_symlink ' "\ 0 " "" "" rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1 # GNU coreutils 7.2 says: # cp: will not create hard link `../cp.testdir2/dir_symlink' to directory `../cp.testdir2/dir' test x"$SKIP_KNOWN_BUGS" = x"" && \ testing "cp -RH" '\ cd cp.testdir || exit 1; cp -RH * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1 test ! -L file && test -f file || echo BAD: file test ! -L file_symlink && test -f file_symlink || echo BAD: file_symlink test ! -L dir && test -d dir || echo BAD: dir test ! -L dir_symlink && test -d dir_symlink || echo BAD: dir_symlink test ! -L dir/file && test -f dir/file || echo BAD: dir/file test -L dir/file_symlink && test -f dir/file_symlink || echo BAD: dir/file_symlink ' "\ 0 " "" "" rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1 # GNU coreutils 7.2 says: # cp: will not create hard link `../cp.testdir2/dir_symlink' to directory `../cp.testdir2/dir' test x"$SKIP_KNOWN_BUGS" = x"" && \ testing "cp -RHP" '\ cd cp.testdir || exit 1; cp -RHP * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1 test ! -L file && test -f file || echo BAD: file test ! -L file_symlink && test -f file_symlink || echo BAD: file_symlink test ! -L dir && test -d dir || echo BAD: dir test ! -L dir_symlink && test -d dir_symlink || echo BAD: dir_symlink test ! -L dir/file && test -f dir/file || echo BAD: dir/file test -L dir/file_symlink && test -f dir/file_symlink || echo BAD: dir/file_symlink ' "\ 0 " "" "" rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1 testing "cp -RHL" '\ cd cp.testdir || exit 1; cp -RHL * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1 test ! -L file && test -f file || echo BAD: file test ! -L file_symlink && test -f file_symlink || echo BAD: file_symlink test ! -L dir && test -d dir || echo BAD: dir test ! -L dir_symlink && test -d dir_symlink || echo BAD: dir_symlink test ! -L dir/file && test -f dir/file || echo BAD: dir/file test ! -L dir/file_symlink && test -f dir/file_symlink || echo BAD: dir/file_symlink ' "\ 0 " "" "" rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1 # Wow! "cp -RLH" is not the same as "cp -RHL" (prev test)! # GNU coreutils 7.2 says: # cp: will not create hard link `../cp.testdir2/dir_symlink' to directory `../cp.testdir2/dir' test x"$SKIP_KNOWN_BUGS" = x"" && \ testing "cp -RLH" '\ cd cp.testdir || exit 1; cp -RLH * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1 test ! -L file && test -f file || echo BAD: file test ! -L file_symlink && test -f file_symlink || echo BAD: file_symlink test ! -L dir && test -d dir || echo BAD: dir test ! -L dir_symlink && test -d dir_symlink || echo BAD: dir_symlink test ! -L dir/file && test -f dir/file || echo BAD: dir/file test ! -L dir/file_symlink && test -f dir/file_symlink || echo BAD: dir/file_symlink ' "\ 0 " "" "" # Clean up rm -rf cp.testdir cp.testdir2 2>/dev/null exit $FAILCOUNT �����������������������������������������busybox-1.22.1/testsuite/awk_t1.tar.bz2�������������������������������������������������������������0000644�0000000�0000000�00000037123�12263563520�016423� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������BZh91AY&SYÑ·du�(h†øp�Dÿÿÿÿÿÿÿÿÿÿÿ����`Q~ðÜKí¨º-Ö½»Ø{UËîâP(”•*&±B i©ëéòû^望/£©îã•}»³„ÅK»ÝçÔ}»æÀáè=¾û»;Þêûæå¨ûå¾y÷¾ï®>ùÜû…jZûto°� 7Þn¾·»:»Ï{ïxøúökèÛî9©Ëz÷´×ÇŸxú»¾úôîØÓG«ß}÷Ó_n|}îsé×ÑæÞö™Xª<·×o}¯¾›îàz;ݽçKÛƒäzÒë·5쮾íO(Þöïo^žƒ9ç{zžûï{ׯ}÷Õ{Ž¦ˆ Ð#@òi£CTðTÿMTöSLOE4ýLž Äj~¤¦!F„Ñ©ª1¨õ3Ph=Aê 4hh mOP��H$I¥=i=IôSÕ(=C!ê�4d €��� H€MM4Ð 2ié©´›P €�z€��I h€ Ñ¥4Í5M6‰˜&ž¤ýDÓÒ4ÚG €h£A!! “@MdzO4§¦DSF€�����?sû„¯3i^¢yü<uÇì]-5r\ùHXM±€´!° %!TÓI ST÷ýëü§ZÅbK­Š Šª&jR–hŠ(ª*†¨˜¢ h(¦Z¦I!„™.ßþlÙ¤{Õ¤_åIMÿi]Å‘S'­’dTyd€”Ñ ’ŒL O?Ç¿’ýâÏ8Ó·Oûk+Œ?Cåg¶¢J£…™qáëù,¹kNSî%M;Wù“–ðÖ*$‡Â~º¤ÊYŽßÉÌ(1´A ŽG™»Ü£|Zì%9›8Ÿï8rþ¸:²ü·ðwú¾$«xƒ·Y'}*=jü%îå?áøUìSÚûE>´óxÿ‡Ó˜ùüòö›/q1“¥_?Ë,Û£®²Ìϼ‚ÚÛñí{|®™:¸Ûëz§7”¦s¼ÓÖ®`‡B6Y £ãa]Ä¢YÁÂÍ]Oú\r£¦yYgv‰vHÎœ”Ä—ˆµHÚnacÝH%gö•Ë4vd4«²ʸ‹Çm6;«R”Cç¼Ë6"I*ï´¾%ßJÙ.W½ùãëÄÛ{g ’C¾€ÏÝWÏ~ˆQHZí‚­’µà|&Ã~œ='ëÕðí_íߟ&«NM<À:0ú]a4všø]~µÒÈÁoâÛÒ&w=îvO茶F›1̧Ñ9½Àë†Éè™Ø>u³Ã) Êëeöögx„ "Ñ,gyb&ˆE:•šyÓ­Žì¿Ä¼ôîžÃ‡–Ê·o/í‡þ­«©áøl{eøq~m_Q-‰¿¾¿kèýŸ»ðýëñù}ìpÓµiº"óøuuYÌîº~.=ó.\è|—5Ähåù‘¤¥d§Ø›–=ÐQ‘ßûMcË:ë#D`‹•Hs)ì)·</~6Yta·|©wD®º%—wSQЛ÷?}wýoÔü¬ñ1¦í»ï.kŸvIÈC£8ZsÕøãï˜ÃÁ_.’ãÞdK™W4ˆÒ‰þŸ›jE­åû¦…¯æ~‹=ZãÓ»Üóöéåèt/Nø’wÎ »…csÍëä¹»Xušó}½/”’´¶X´­öQY¾¶‹«€r;gq!*D’S4K!«,%p‰Š‰‚o$3 9›n0m66Õå2œl–9ýzr_>ý÷¹oÐív"Õ„0: ÉL³—De¥x×Ý®!G°¦Ìu¦ûÇá%^4ÇÜü©XÚm)8Ðã4ž}0]JDQï²&Z „n!÷@ö‡N%ѸO—ð?!b{~ƒùM‚ê( 0S2á#4î9í_°üävn?÷Âw):¿6ÿ)-h·ó>½üDE¿$ ,‰I c†Üëò=\=;¼ÉÏ)™Ãó&gíü]æÈ”Šœmr?ÏíG—æùÎnMfʈOáN¿‚íEƒOÙaØMò*íù!¤ÌLôC¦*‚ i1X¥Gò ìÉCoOÓ§HÝy^ŸQ®„¦fÑ©ïM¥în†ÏCë†öˆb¾Îcå S Âr;<gÈZ`¹T}Ü[û!—¤PöýPsvy¨yY·òªV¾ÕºUèê²…E…øn•Êí[‡Õäìã–‡ŠßÄ=ðfø³­Éƒ•f>»¢†ºqä&ܦŽ]0þ…+?Ô³nòþr.UE½__$/ d¾1†ýÕ÷…>¼~ŒÛ¼èͺÒq;)pàéooö7ÎÞð-4‘Iæ‚‹Ü­ù⎠oÞöMBƒÏöoèTÐ02¦Œ©N'SðŽÝÿ8ÑØè@M„Žkøù5Ô¿àW^iµ˜`6€É R5"Ð…"ÁR 4ÒÊ”íxÄõ¦>°jÇŸeÔ¦¬Æ2�¼a“@ìÛ…€ï�¾aÔƒÑçæþ=´Áã—qÍ¥¶w@! p*‰*÷E{&+ Ja!+löŸº/<sïF²màçd!‰�›J¦¹#5„VõW ŽH›$PŽ85£ÇÙ'wáæLê•pÚúÏ-‡ívYGT¸sqÆy«AûUÀ²)¹ÎSîrŠ,ÍŠ ‡;¹í Péîu[ËÕï—`j@À•aq™´b«S (}|ÈX' Œ·í®6ê¢æbCÝe}+›·I²–$LœbæØãÍïö?Óí¶ßÏHcé·Cê›I9Ã)q¥G§"|cu•†s®0Îðî߃½\¥³Ukâ«ã©ª:r£ë&¹_?ìW«µ»ñŠÛÇÓ¯Íe¬í#ìp''¾Çw‚m¶Ýhæh½:o؈޸#N‰­~¿ ¶¿9Ãnó#üû;;>»ú×ðïæÞØ&tpM¥i87¿VÎâ¼&W™ùbïK úÝ­ƒ"\IæçEÆv_y)'Ðý^£L¨b Ȇx+H=2q4ÜýXõvK:’lX›—Y\²ž’zBr"ø8[Z›sÞWuü3Öeñä]HÈï%i åÄöÌ­ifçÅeÁìÂÕá£Ê÷ïæ!Ù#â§Ó~È–Smæ^¯¼²ŒUWBqx>Qç¿¡Îiº$|¶ÊRâg‰*\g ";ñ4¥hµ®X]Í´ýÄ_üí½’@¨ì$Íád½'?Õ¹eª¦7:üˆÒsNO†Ò'Åû!úz0”„ó³;§¶ý2—0Z—ÈÓ{vß·oÆð_ܯíéwßÌ^ýºrpü#÷§‡óÆ /®•Fב™¢¾P68¾Q&¾‚©gKÕ_¾8ŸÉŽ”=<Øî‹•P¹å fÙ”›–ÒüúcºÈäÿu½íº]†yž©©¹5„ô[÷À¾ ‹Hùí}Ê0;ë¿Ns%&×¾wÃëyŪŚ(v ¸&ºJâ9§G‘,,^܉ÜU“iè‹sŸ Œ0áÙ™¦ÈÙ"îš[øÏíêñ ©†Áíý½c­+¦©8æ£Â šHF‰ò¨O÷ÃW¼ýÊĨHHNÏ~²yN² ·c^¡”âżŸ)DÐø,kÏHæ}ùdF1 ų¶Â²:œ]uÆ₲Ù<u«zç¢{S™ž»L«I´”`úù(ô§Z~3êm“ýNÓ~ÛÙïÞÅ“È}i]E\|ÜÀ÷i†-5÷]°á}™¬çÓ…'C ÆÜ忹·;'ðÏlït_ǢDŽMÄ(í0ÅáüÂw„ÔƒšãI-_ðʯ¬ŠS~¨ËJDî29Od³¥þˆ|Niì÷nÎd¶2•œá-oÕaA.¾ë›Cá¼ÀFL’k¦A)Ä°ŸW`r2æ39ÜáˆyñÑÑß±ÞßÏ­^Qíg®óÞ×/½¿S5¦ªPBé‰UC™3Ÿw|ãé©Iã31YàLÓ^QFS淽¤iÏ ºk£ØݳZT…)X}WÇMYyó˜Ú¹—ûž½°ìˆÊ> &Ò,ÂEgëàc®v}Þ:g0­oÂó÷b«Ø§GQâ[ÞêMmqÑ'&òàs’xlÓ úSÊl¡.mVf \všÒšˆ|íï×…iYIßÖC¢½ ôà÷ÒúÛyc6öN°¬1–x럱*ß-ìl íT³•÷èžÏ}oM9jvVê›+Zô‚ÄY‚ÙÞ;'|òÄ–9´¬â»!BÎפݕÚCš>“¿‡ åKqXqs«ÑeE²e­Ib>œ\îN²ZuÄhÏhñN¬OÒ÷µ†—¾õ\}îØmp^ÚSw¼JÊoo%ge¿ .eŒßeµÜ—ÅE —{;w¢1ˆ‡Ä?OƒÃÚ³êÑÔúþ“±Ó¸(ºHsÛ6Áu«·ÙnRŸ+9ø;ª~¨~ˆ¹×sQ$Aì¢tî¨È³çÌ3_àÿcŽuôã'öׯЅ¿Bׇ<=ÌþíéQÉc±ô\)ããÜö}æ1|¸Ÿ­õ ý–3sÃ$v‘eÈP.Ñ \½ØãÌãÔ€L=Ö"O¾ä¯DU¾êÝ5DV‘6ø€˜ÄBvxQî±»!%kÙRù4'ä¡øÎ!YCà.Ÿã6Ô­_8I¬[{>ý¼T†q¬owY¾bcÎ<jÆVÉÑÂ×OD| {¹—ÃæøÙãGgfoÃ×׌jŸ¢¯‹)ßØ™Ry£¨ro×Ö=½‚µ†?<wÚR°C ‹BL½>¹dÓßôÊZ.Á“<g—çã¤gÖ#i ßâÔ8¾q~Iç´U ×ò¿{Îd¼‰K+L =_) 8Ø¢Iˆ ää›G»•í‚›ô¦Â½J/¬DÅÐa&Ÿ]GÇ!çQÍÈžæH؆ùwCWÍ|P>8šŸ÷׿ÍðbO߆Y2ëXƒ¯ë^ƒèû%á·æ¶kÉ;dšÅ°É’iŸÅ°Pr‚MË+fÕT 8—ÝP¨¾xU žTm $ÚAÿ,K;¿©½6}ô„ÏÓñ/ù캺 °2¾øG÷|hõëaáƨs›Çä|ª•(ÝÅŸ—Ý÷cžM ltìuø;0&@&i¤¢™(Ú|Ö" "%¦">„?ô‡è J)ªZm×bbت˜ð);#þ<_ÒMÑÅØ\žB.Ì™ÓÎ Ø'È›l…ØÙè3¦?Q|Z\-(€!=åCA‰DÒåÇZë‚£Œ¨Ý®¦q˜1Q,aúC!Ìö<é€VÃeKt *‡§êê9šSå&5C4»„á…Ó ½^Ä}U°°õ¢~$f†.ª¥²Óàî\T½Žá®wöìRS¾~þ3Yñ“}‰)çõbD¡þ±‘s˜#xŸÓøu×V+nÙ]w¨wÍfFˆµ7O EvÒ6M×µß~’®¿=IžÏ½‹Ô\xSVeÄû‚¢²j»Ú˜ã ¥ü¶ûÙ‡%güýÿ†SŹ²ç“Ï-»?M:¢7ØGL…Ç…VÒ†^…|?Ìûú×;¼r®Êº±úé%ÿ{÷úÞžó‰àôØýƒŠþebÞŸ¼ù çðÇÊîçàóTãöÎ:žš>å¸Î“zçâê³—‡áÑ ›†¡µ[”²âe¾SŸü9ÏO½œü›²Ãн8lÖSLºÓ5oô|IKW¿9F_b÷çüÏÝäXÆž(>š�K QWB#ª/Ä@ÄŠ*üþÒ€¸ú¦…¦ {Ço}ߢ!Àcêï÷vè¹7o”‚8'§Ð;×Å°Qâ<Û<ßêûññä.—ƒ_Ëm €Ø¸•šÚË«ûþû6þ³êÏs æw‰7¥`I‡jÍJ'vÉÞŒüçÉ-ÅŠÙ}æöËúú†Ü{דáø„ÚR¹±ÿÃ]µ™É@|™ÕJo¶žË8¢²bιy×g3ÙÛôì"ÍKè´è®¥öÁ}Óž~Ÿ¡ô¦ß<tÝ×´ËtŒ÷à½z®?ûâ•{ú46ν±’ŸÛÐv|<gô_ j?c§œ­$棜êdWXÊâRúþiÉt°fÍ›•yÕàL$i'Z'h&<e™ïÅøüÅ~È» ú–5T)<Ü:pE˜X[r<äU2DO¶·˜#X² O¢®|º ,ÀpŽ§@›J™ãYï--©¬wØM'áb'õOŒdä–¯Aì®ø6£w³Ëïk><E<¨B]ñŸØyh×›Úx£nVé+2Ebz¶­|×Uïé¿ _JÖñ×ÉâGù?¿3ÉžEÇŒðË(Å~ovÞ•ðí–ÑýkñTÍï${10#±Ú!yõéÎxmÞ fŽ>=ќÖ‹ÃnØJTwåYâa56£´Ä”]8€P–v[•”¸æ·$,Zl¥¯ŒâE†fóüÉC|Ol==ZF$øÁؽ_¾Ë¥ØY¯Ñg¯õÈ•Ÿ •Ñ ¥w„›çkÝcÞ¯<®¦ßpÿŠ|u‘$ä ~ùÿÎ@??ãúÿrH1S€«•þCî_Ÿb7ûÐïR}¢Q}L”:HH=½•-¡º:oâ‹Áû»&ÿ«Ò«ý„„&H;ÛLΚL.éíGn/â?—» Z|ÕY‡vC„ÔÏè+R=O[ï-<h¸£[|Ž('9/‹yD¾ô#igÛ[[ëò«|_ 5#\6ÐÂ×x®Yb‡žë—§ùîUÉž.¼gÄû߆b›±×”¦A4ü­ÔÇ+T¨î.))ZsŽtLJËw»âõX¼FŒV œ/È°çoú>uº€³/Íg븢?¢?ºåÀþ:m`‘t9ÆÔßg»¯„þσôË¿7àܯ†#”©JùÙ‘ø†l’›„ˆ Ë�,À,Á6üSç^Ý·©mÿ†z)ùwÛ–(溒eãPa*”z¯Ù‡ÓÌTaD#£2ÜK¶_3t1ælw¯fÿ>>ÿpæ§÷¯‘ ö2,ŸÖµO›ð»Â•£~ óÙîo´‚…Åçêý†|¬C÷ñS<©ø~zrs•ãû0íþ'ÚrÖ\WQ˜h~ÊŽVÖT[Àø"`„E Ÿ ‚`=§3}õ P¢Æ)LÓS EDESKBRP 3 4Æ%(˜ï;\mrã‡_[ægsç?Êk¡`ÜÜé»Ì÷.Á)ŠªëÞÇij!twšÍ8>\ùò4ŸÊãOÞ¸ófð´ãöÁ¨2 jß«ÄгŠ…³|ðüi¤ Å þ‰šA¡ùÒ…Î׶�žRråj¦8H–½6L¤œ£³D]Jl55L0“ z=Ìæ4æWynfɲȵ4ŸM:&Æi™–9ë‚ÒÛ¬«Æ4§PpÄ.Õpú‹‚ìGeñô0üÎnÁ„Û)§7.úŠöÛÆaC—7Œø×t^5ìÇ5íïœ ‹@ǧë$!Ó]„!^!»{K …Ì]Dß¿¡NëÍÎþBlë¶ík¡ðdº Z.°ÂÀ tÌÆØÚO|sð«Y‚NÈBlc›½R¯ËŠš3%ûþK&wlc±Ý ‘Óó5Ç,ʪ¨MõTß=ütpÇáÛu³ËЀQž§&.NvCÞ‘çÜS}Ú–¨Þ&`_ÿ±ôÛŘ¿–vvô7yŒoÓu9Û^oºàÕºØjW át7|OŨNÜDV D @‘T÷å'È¥þyÓÔq.å‚}G“…þBÖïW<äɱÄ|Ì))&Ç ÏRáóðâUÏöpâ™x°‡Äg«(Q·…IºE�òÒÒ�0¨Á¤ÏŒÇO„ª¦ÞÆÄöô”ñ bÚ ÜhðjDsÙÆ_)rH·TŽ¤Õ¦ÙP$~rõè»,’K¿¥6Ð:J¸8Òãë|=÷*ùx=ÇTw7é†\p‡i=Àw¹65Á‘Ú r=yJ9;G2OŸD*~¹çÿŽ÷~ó«` LÎ3“éÄ«&¸4†fµhB�Žvmg’$J(\¾¡ Í<‘yÈ´€bùJÒ™+JêÉ•Ç×…‡Éäx‡³Ôîtºµñþ ûGû^%È~(|õJ¥²3,'Ó2ýëç™è,Ýè¶E¿Â×öù6dyt˜xPmÂï«æü âîC‘|›%ö £õŽ5PÃf2ûoµ¦›»ê<?§áˆPÈ¥>vöü¾ùçrMðyØÀ±m ÖÁ|´k†Ö´>†”]"Ã;‚T(${°²HMssñ;›Åú<rnØ$~öôJpýCM†÷XÝþ«,ºuªé$XX¿óSËkàš«Õæ‰H|pº?Å^E^wWø×ï²ÚÊà X·\ˆ–»cRV' |eé”.&-9Íÿ¦P³qº3ù¿_ðÝu„3³¤Ó›A5Ii’gš&ŽÖú–þ¦ù ô½!êGz“¯Ü½ŠyÆi J•ûÑ8×÷̆J¾Þ(!•G–§ø‹7–r†áØ 4h›Š;Ä^‰=k^%åò”ÆﱚKA¬0ZNÜ&"…ç O[“ »Ÿ&yãºçoWqÕäãÒoZvX&"»1^8w,Fb"|¸Ð­éwò–zÖL<N³ÜG|ªãÇFN±ÃG.[Tr`Áíß<Þ"Þ¥¦åÒXC‰"ÝŽÑñ<g™ÂìKÊ‘kùó=S,»þó}_IaO¨ƒPjC%Œ°yÂeKfgUX†¿i|¦YZ�Ì|2zÏí¿«ø‘ºóñ°&‘DÀúÃ#ŽN²Œð‹—I¬V@i×ô"°>ŠÖtÍÌÂIÊ’/h—äq"û£DC‹apO«J^'?ªÑM<p©!ŒÓîv‹LM ˜ÅB}•‘uA‹¶ÀÉÈ9m¬ÆbZÕC×–-ÉÀ ÉC™»s•!)ïämä!sÄÓ²ŽU~×¥“ÇEFzœ`Ì-O|*J—WpƒÍôœQÓMA>ÞËF„Þ¯LuÌUO¸"uø -Ç»¿¨a·¯–lG„˜N´QGHg ÆBaƒ½*$´Û.'Ó³‚$ôÁ™€C\¬’Y÷a¯a'Br@”IEÙÛ•ÖÁSÊ.1ND9b.Œ­bЬ¹š>DLEæêÐÐÇ=‹ŒS-ISÌ?‰rìnàGïŽxblÐàY°tⲈ–™µ5rÃ>ÊpTOGc’C¢Z®–†„H&à%àžg^Göz@ñbž”ø`æ³+†zï[;<·eî|ÀŒ h9n-îoíÓˆxŠù3Sï=!Êsp|ä"¡Sæ†s?ëÓ¨º-“ìsŽÃÌÉå² ÓdLŒ‘‘5k¨:ÏC´K¢\´½"¤¯™ÎÝ— 5€Ð͉;ÞÅ \B>%Nß'†4ä"íQ STÊPªGÈcŽ2iµ›6tµ©Y1¶ÎýüºB1%ßÇ:h+¬né­h‰ ®÷ðÇ`[šoQDÔà¢ØªèÄ’+¦n™™0\i¤@*<5¨ý]˜ÏYž×&‘ˆÓ2éÞ-V Ì1§D¢Ê\Q—¡«{³’!Ôˆšm§©ÌròÈÆÖRVˆZ4!d› qâü'}Ùº~RÁÍg«Fë±4 àx#û“‚j·SMÿ¿GݱŒ{{<Èá4 Ü…Ñ»—‘½(nRðŠŸiÓÓFÏÜ,O3 ì¡Ä¼¡,‹È�ºÆãr$nL>ßéûŸ§7fqùÑ3‹v„q  %QKa#®Æ¨¤!Ù3ŽO¡ ûÊôŽb Ôø2tBK-‘™î‰D 8G×Yø§á¼jÊmÃä`¦i-ÙÑX¡XQ`õ|fÙJƒ¤D©~}¿IqÏ<ÂZ1÷æa™‹á™5‘B蛣šØ9œÊ”émWØ áì%,/ÞÞÎyG‚”Ñú¯Ýó]NHnÛ·ÉîúôÌ^Û䓾ַ—vdƒ9ìº ^Lø$°8àÐÁߣÂwÔrpäre(¥ra}ðâ’4à<ûwÕE,¨¶yº ¾§I¤Q�€D B„û­Šùa´›N'¨ò<ý+fÙ\+# Á£(Ì% (‚’ ȃ,¨ðá€á3ƒo6I™{Œ7ùÆyŒmy4Œz‘IÚ€yµ5õšXkFUl(ÅI"8°8Â9‘툢‘;ñM$ËŽûꉕîøâ1`z�Þl»ØxúñyuépÌ㪊ùÓyòzÚuQ‘„ã†fe(E…¡'œQf؆ª"r ¢†l”¤ÈÉ" ’¡ÂÈ WPž‹”¦é 0ù�w—gfæúIØ‘ÁvšPÁÙ0E¡t2wÙ›F®;l|†ÊÌ ÎŒftÆŠå‚LÓ XØÛl¤}FEƒ$Ì2$áæ¡Ûf ‚ncx²©ƒP-½Z1âkÓÑÝ"U¢4c#5‘*Ó7 ;aZF; CÚ5Ð$ ä­o†£„ajIdâe|»@`êPR®¥òÖ7®J¯« ¶ÃÜMYŽ(K¬”ÌŽ¨ûÎG#_^QJwp(hq)VÝlA›*„ÂÃ/›ýbìØA»Ì§+Ã3âà¶; L‰E±AÝ3òíºŒô^o‚$â#‹Û¤E¨";,8³‚Ù¥ŽŠ÷(¯u®Òw—KèôpìéÐ.ðááè#Y43YÆ bL1� »Ô4oH‡mÜô†q.$Ê™©ˆ™%ŠÎåsÜ]dÌ–±!P¶X÷g…€a-T¶^ëÞÛlên”/†Ñx­¦#ÃÖøXp²bƒ†ÔQG¤’9DÈ™Œ³cžqs¥ËÝBÆ78ã›Î¦0bn³®Ù3®_ÒS™5ÏSÑÇá%ŸÏv52ùRaÊeVÍhƒ'OÎjg†co×4)æá‚Á›6äD×63IÛ í²k úgÃ#Ʊ—ã¹â:¥º×Í7¸|¹}aÛÄç=´<à¥þ˜$sT.Søpð»HøÜ;ïškÉoCë|ø;ôÕÑÑÒ}oat ‚ãÝùéÉ´Æò´VùxFg¬O‡Ó.ÕÅ“ƒÐïDöï­õËòfšN¹íß4vXOé«ë\Ù„`Wh¸ªßƒ©Æ› •Û’××gïž^e–8n|7 :.øívvfÆ\®3AÆŽ:ç’æÎçpWiˆÂ0ûr–f7nwƒ§ÉÜà®üõpƒ./³SÒÊdT/´YÃJÌŒ+Ã#;Ȳó3 ®})ƒ™csB“E—§!e­“he Ó¡~ÖüZ"«Ó8ôäI„Œ»9:[ñ4ðªë{ã¿<|j0Õ sk>L³bŽjázé:zíª^»âH×WBÍ—¶“¤T=ò"1˜ãPúÄE˜³•L­}©6FZ$~”±C´ÑÜZÚ“Íhƒ¾É©dBL¥˜ýFÎÔZZå—vŽ\ÄóMŠ­´´Êì:Ga[ðòù¬vWžVÌ7T¶³’Øt¼ÒÑ[œm.¨Ma0Ú íÀèõ4çÝø2ÎnÂúÂõi�K•~p±Îº}> §|U÷;¤€%°Ï�ÖJÙ`°Û*MkCd[I¬\ …{%Åöôב†£‚„mîÊ{ÌSnâQ'O)–_~Zg¢˜€Í¡Ÿ¦s±A±õ§qB¾“šKER‰¦šb¾Ÿ ž_µ¾N Q[*æØx‡qé“OZiTðØÇW„vÇ„;èø¢è²Ÿ:än&ˆt*¼[$6hr¥ª‰aó蔳4nó½—h’>@h—½ÚÙ·drQªyÁøcðDËG°Ð]¬½;¢ ÆÛî`6D/´ ,ÀÙz—b(~š€yª`@¦SNÄŽáËØ;¤Zü ™Ÿ}n£Ïh¸v¸&÷”u{ñð0?<C_q²qà‘Œ n»,¨¥A±æyê áúÉìiÞ<™<Ì�¯„âE1è°¢#\8há}€íÚKÏ„¢jWoXc£hyí<Ø{‡f�NG$á ‡>‘Ù6î3œòÂüÊèªÃ�"‰`퇙Ûeði»A“ÃsÐûÓ.°Wk‡‰¿‘€[ɵQx¢ôY F.)TÓdð 4–ûoŸ�ȵqéèý9ª«Z¥%žv‘˜š~“Þ5:eàÈz1Vpõ»Š3d‡)7ºbëñ „`¿?iLC¾L©‘ ÊæKP'‘• ±2ç3²&A8g2+œsƒ©||1D÷²k33C‘Bä)ÓJœ3[Ï@ÄÈr¸5¸¼Í 탹vï]‚íÉ2©‡ `‰„Ñà) ÚÉ”¯™•4 óOåaGÏU||øÍVŠöՅ˽ؠû–ÃZÓNWËX,=õzÝþ£:¬æ{^<QÉ3Þ0ë‡ê9ƒ‚IZµx‹ù¼ªƒpP®'1h­?:â´åvëŽs<yxÏ/ˆ}ÁÇ’é›”ñ« çg.* ÊpG»t/­k Öƒ$»z3Ö ¨à8Ò„àK²„Ƈv±rB»W Kæ!êALPMDÐì¨} þü ¿·“ xâ×€nFÊåþ%ê,MÅA‹Çi¦)ØÏW¦BˆbâÀçc0çùúŽnrª1$2ã@o„fÖ¦Š$†‡åƒ$Ú\ˆ¼ "tA”Ä‘ aŽüf^.xÒvírÿxÏ[@éÂvíºêÀ]ᶊ^°äׂîá<LDôÑÂz¶Î?“ò^cŊܹUé 6¦ÅÃyN7QLGÞ¦Kg²6såá¸nžÞ¹IĶCŠg Øâ|T~Š3)ç.³òb㢠Š`†$sXèŠo‡Âƒ;lwÛBȹàhÃŒ]Z¦·I¢åb‘MÄ£i"‹ž2L ÔËA[ÐÑX¡´MAp,Ï‹8&êù{ œD³ RÓ®Êm£m¬ñnv—…T\©|Ë+=Âô“Ú­åÜ'3¥¼õ3¶µÎ,M)¤‘00m„ð“RÓÍcH7Gœ�ûH€ê@,††1 šhÃc6¶4bì44´¥ ÞG"%¡¡Ô@™ˆ ÓJDH4òÚ˜0õ¶KF›FÁ­”8¡NmQ6 ‹^&C Fì.YàœPÖõB´EÖ€ûóª&I‹ˆ€l<Ѹ—‘±ÙÍ™ˆd–%üüâI㙎LäàOþc¤au˜!14¤$9ÁB'+! _—ˆ’$„$Q3˜å¶ ‚¨b&ˆ¡0ÁÆ&o5j‚†XŠ§0 rÌ.îˆ=öql5÷bdë…iˆ ÞoˆhAs„ ƒ€a;¼. 9d…Ræð)bØL²÷ì_ƒ¤£Ý$HŒc%ô(ˆ¾±¶ÇÓÎÃZÖbÁ}1˜djXÊå±õS¹Ñüi ˆ* Û-l!)õžÑ çAƲŽ =åÁ‚b‚©hÇL ÔA­$°;~‚Æ3¤!b45wV Ðâ¿ ê&çx9û¯GU| ÃW7[COWVÖ9n÷³Ñ±­b†ö&3{°§°ª™'pS¼çÞñ‚5@g9DL ¹Òh†vcô”^GSºF½¼ˆ( (¤•‚«©ÒèÇšÁçóNîšêì×£šrQ hA<¢wÝ£qÖ'výõ'$”^wù{rÍð,S? ÇJJ3€ÑÑÓ@3AB Q‰â‘`Ñ|f;€a‡ƒžwÚ; G`ô!†g™#@Sù"i»èKbæK4Ûó^8ñ]`iÆ~ eÌÞYÌ4Ýä¼À9®OlÎmÛXõ‰ùW¶g/}<é~JÉôw7¬K·ƒŽ)F™}·G]øƒ9çlãÜzÕõç3h9Öâò>`áòuWCâŸSNš* „ĵ‚I @°NnãËÂÖŽžyÁO"°HJs€wy±}nJvÖ¦ùM0½RiOÊ* ^¦p¨¦A2Ñ-X¾Mry!*ØdšÑOfðÚ:ðãjØñÛ•¥z—3†sâî÷”b ¨Š ¤$•HhX€¢¨"Z)¤Š&–˜…™Z $$Sœ,g<²»Mà Áôi‚Uw{ÃÞˆ|N®t1~>$d™.RJS 3øÜV>òôÃ0:C·âNƒ0Í÷ĽdÜ]:õ ÁÂŒ5œ6$\e9hàò;àr1Þ¨í.½œ$ ëuðlfG â*é«£¨‘ÍSoX`VÀ𤌒 &ÞâíÜÆHÓ x +Á6e|\‘òç•‚Ìi´ŸÝzT)€Øës CŽ›Úk:jK½=HÅæ&D ŽämfňIQS90Ž'´.ÅaÚòB#™˜ÉÕB=á"7Í A2Zò–ÝȘRƒ×Éöþ?5]/%Ô0#[hšÅCOcl©Èp°[° í n?¢Ô$ÂD飂)m²a³&¥ a-#lbk.|öa8&̲¹kÃTÓCHc$&˜A½E ‘µH£²q˜Ëc.³ïm š¶‘M6êøág¥íÛ¹ÁÎûÕr~ƒåPç�ÄöUÀg4ٴǺ{Æûé‹ñG¦*ïÑ4»Ln:7˜ÝÓŽÑPS÷ðx¾¥¾°h®R}ÎXo;ɹ9°àŠæE©·—#i<„²e^…½Ùh²d,DŒŠ†/;ï9w:1›®”·}!Õ´Û÷lõôé7X:ôzt9h£(gA¬Æ*±6Æ›JÿF»5ölÀ± 1|FäÒxa(•}ЀµµÊ|hN>Âþ¡÷ǕƈœiËвê§]k“¥Ð‘ŠõV¿”™­eóEÈdx*Ú’Ä3f6]¶9åa…Mà1DLJÔm&AYí ¢>KØ,¢¥,`¹ % ûÑŽˆ –åzŸo#])ªip-ºH‚aï=~ss«Í€ÙpÖ¦“¢C¼dO0/ʇUQQ@¼ Ð�c”<5F�,€È!Œ½=‹Ë–Þ˜²¾S*/‡Ø´ÏÆ¥+~U•k”b\‰˜RìßC°P@4iÕqàÖ8H‚3™¹«uÇLØÞÙWĪY'!¢dŸÄÔàw‰»Ø[ŒG .…ÉATßD§d›Mxb¼éŽÁc²'šqÐM÷Œg>1íÑmÜðä!–R¥'d3 CÈî i‰0Ý’)$•˜ëR”-Äë×·}”øq÷y÷ÊË®°ÉŠPd®fPUdœG3ˆu©¤¾ò40)\͉pâ!¹Îù}ó•/jì;ë–}‡#ðøækk©-Ê/MB §Ð ÐÇ?zN‘Ñy��”3Pm…”6;è†iéŠb4)‚f®ÜþZ.-ÌÃb¦†ÄÁ°«5‹~ óO–Ó!uÅC2ŽÍl<ö�>¦†÷Ú–Ä`ï#HhEÜOFÊ</y30j�n$ Q0˜VˆÆÛ€’r¡ÍÂ`Ì.«–Ò¡·`ÓLkRV6QW¬n°*Xâ$Ä™ˆT³2 ­VQŒj"jËYÆà ’&õ(ÄØèíLtš¡NEPb &¹»›CÉ©b"tRÕŒÕâ i7 ÜmJ4F'”9Î$kN³–±˜Ã,0³¦YTk†rÎ(eD2)ÖF3¦ˆ¦‘n0‹$f៿ñû‹‚ØãQµ£ fôD,ÓüÙx;!¼œdh9û• %ÔÆÍØUkñlÈa`P<1-@wjýÁGŸdV|wÆÑ Œß¬<É9¦9#ŒÆ’5 ‚ÛD{‘ªÉ (äˆNŒPMfìrQ•Ã 0TLïþIPh ™§M§€bn:¶ÀÜ7˜œ$0šÉ(yË A RRHÉÑ?cU]ÝûÞXå8I—¯9³"XY$E"T¡F$ åÂ2È2$¡À2q)÷Àd$¶ÈâXxóçãsä;³I ‹¬ÙO²@œHM_²-d².ã¢QÝ ÉözSñ¾ÚôÍÈ´š‘ê…QT¢Tª_Î8ä3+þS³ï0·‘(ú>ß•òîÂ?i„<­¥4rÁÓìÅi[l#xq†ªŠ¸ãëxïÄ7B ÓPl” 5;â!‹Ji ¥¼‰%6²Û$¶µ\iÂ'[! [CÍ VL T&´B,äl3àÙ °¿ÁÄÚ)‚¢†Š;®Ç~Pæoˆaß27 9«ÁH-ÍÉl•6«‘äuŸ7mrÿDé¢ÐÐ 2;3$¼¬z3—ÎȪCã¥ùÚPÚW#l § ÑÝ›¼„ºûi„Ð ã†Ó~á´QipФ† óàxF@Y#C Ç33"E:íìЗÊ4e’^!Å?R{ ¼3¹k¤Ó‡ÙgÃï^]>RÆ‘¶¿³2€ô¶è)Kÿs÷³±03ïá€8Œˆ*Å$4b9 xîrÄÉ OÀh í,€®` (P ä™}˜5 12……Ëâ@oܶ´Q ˜d²Å ëÆïf£ÌÞpBãÊùïF‡<µoozñâ¾™F•¢/Y6 ájŒÆÆÂÎïÍyÀ*"(aäÀ TR2XŠe'G¡Ü}šÛŒpSâÏ)/»îõu(2J\¥2S g©†:rGPèú[c–¥â~m>Gpc¯‡y’4˜Ói°ÆF%îÉd>àÃ}–PH<!™ßѨö¬úš>…ôgƒwr’HrÈÖ:Ú ¢a7vE4 íÓr(˜ÌAЗÜä—„OH>L‚‹†‘‚)BÒ¨F`<&0˜,EbBÚ è[SeûGç=(jãȇ éi9&G¤»¹áL˜l^ Q¤ØˆÀƒlŸÆ€˜y:´Æý ‚뜡ç kp-eÌzx³ÙZ¹?†FòñöiîÐõàf_È¢šh#Ö( ©Üc‘ ® ¼Ò„1§’K†zÓÊU $€2Zøí#˜E÷CïŽëa%ªšZ¥böiK2–·0Aû¶cÞ6`Æ®œ_[i¥)àòÓ éÃŒdš¹}ÒðeÅ#!W$;hpÏDCݶ39A˜à”y…ûS`j sº.jvÛ‡MSu”µ ˆ£Ö¶‘QRoUàp{A¤>Xú!BæÜxÇÁ"ÔTã&ó§´vPa‡v‘ÌL;Ÿ'¬H(¢i5Ǧs daïðº<ÖÊ‘¶b’BeU÷¿:¬^Y=ù v}lËÏ <ã(+VÕZ¾Þùz÷ ‘8aÂ4Š~{ LÒÔ“»XüNm¤Š VºÉ[}#jЕ5 €8r±íV© ê;äœÓ· (`!™&àî«ñ…xÇ#VV4/F©"DhccQ[aDPë2&¦›1À£ (¢Hò,Ú2)vœJ Ö&1V¨ÍP@áli² ´eEE6± õG€ H êR çnf°° Ìƈ¤µã4Úª„¾´<ŽKÜ’ö'€ñ%!äH2²Í×.påÌì>#ÎOÄêžÂÄšø¹Ý´IH!(r(zaEZŒXš ŽfòiÒ±ÃÐC§I„ #L‘G4;Ø•¾i´•2%9¼ JØ®µw !h“wOc{ÇMÒ,wp6žÛ·AÚ•ëž,[:-Î/’yOaY±v3!l[ ÒóÃÐâ6HUhúÑÊ»±!u)phɶîmÔ«&«¥Ø¥ê†°§cÔj]«ð 6ØŽrg!Í H ¹ÊŽ–¸_¼F„Å‚>.¾Ïz¯뛡â_2æ øXãmŽ>—…ƒ'Çe¸$„¼#* )�2[†@:`”ÌÄs 0l„ótó¸ã„C$bšE#°ku)ëâÆÇÛõ÷Ž¬PÒ�(DH7WÓË‘òàÏ—ãÑX6NÑ9ÄTO•„}œÂ*˜¨Û,�†ÖŒTÄ4mðpª>ý¥Ô�ðCØ0õû¨t[–r•¿/OÁäß[Tç§ÖØWêÉcs‰å™¹…ŽjǞυ¬x[šöë„o@åü¸ÅþD·:ÂI [§éTûÌžjq¸Õó—hÃœòÎØýÎ`Þ(I¡PºFHv˜Ý›åí4 Ù˜’[Ó××¾[œÚ¼^¤Ç‹ãֵجk«XÏ”¨‚üK£ñÞcwŸ·Ï´øåti³]¼Tµ£¤]{^stóÉI›Ô+õñpS¬ÄÉ š2ÓŒš[̕îùÈžÎæ)&YÙš‡6JIòÙuoT‹êII âç±CÁMý±B̪°ÜR™Ú¥ß¿iËàÍvHÅ´p^'àå¹~¨7«F8Jˆo+Žy‹T’+ S}5égyr®uЗÅrš6÷šæî©ýê¸X-ÞpÐ:)ríßSu¬”óLn´‘»p(¦ÜžÇîg¢F¥äþª…ÈxÁè„î.ØŸ…¼»Þ ^²w®¢ìƒ€ª”ÇÂÍr­ˆÊÒ(þJZUœ@ק\É'ïOL7—Ìz¡/gæV-öÌáØô™2Œ.0ð…HÊ©¼ÀÑŒàÇH‰°o{}!‡8ÕúSëÙuÖ96+õÊx³»XJ–ß׺ʶ-¬ ­Æ9Ä$B „äxmήvfö›â?J’2Kci唹A•æY&:­j‰§6úSgNÞ ìïñíkÁNg¨,2c àªÌIä°]Yù®ˆâx7]txdñM–¨4N¤ Ø´xtä.â:CŠ™‹1G@w]Ǽ `YZ\¢`õxfc,…û+_ýÙ þuƒ^bŬyÖ|²Q—M’2 ã ¹¢nëC-êë–FSªß}ÖÒÆÌ1Ù™+ª†2ÇÝ3ºNêôîÚB!$èþÑ›‘<ÉõÆE’Ðìq¹t³·ˆ°ÈDBCZEHˆãmÓ 8›Ûa[jÃŒÌäÖºþÁ&fèBª‚W3…™f¬|ã7à'#·žëÇsý1ÓŽM=®Ášñ°—®f`wѸjÜMÀd¨ªB¨€j™}R·ˆP!ìŒÈ%:R}§¢±5ä]ؾf)b¡»!R9ñÄk¿%ìœÒà qZàÙo`U%U%Ç?&¢ ;oo’^p¡»I›ÁEF´kX2™­|Tôï†6aXCCÈϺc„Ap°ðßAÄVÉ&€ØdÁf`èôY+;3ͯ1¾ |¤F1ƒòz­à¯ …F†Ì ²™Äe>VìÁˆP<z·Á6¬èvúõqüeÆê v ¸u8ùc}!N”gìëõ(flgò—œ›VN‘À­©$•� i�ˆV&œ4Õ[+?¢S2ûx˜–iŒ<dqƒ¦ã€#J4fŽº1ab’¶äoÓcg ­æê F7˜6‡gV™pk4\DÌÞH››ÃaDa)IÓ ˜‡s2–_KZ¼a&m ¡<ñ¼]l|*‰e°Nª(r“Т÷ŒÌF ¶œ»‡ª3Œ› æºÛ´!K¦Lq8P.ÔMsÅhs¸åS02·Â’±žpèÅÉÌ•²õ—¶ÌÌì—­•~²ÓÀqlÉL¦Ô™‡6äÅunC<Ñ üHðŽí¹ÛVrž;€™€8ÖµšÍ°Ù›‰z’Œ³YH†ªÕë!DË™‰/ßAæ©G}‹“ÀbMœËÞó´õÉ~¤ C8btï]§*2 ÉcoL¡ˆ¾|qpi#MŶ Á–Â0¶ª2X*¡ŒùjjtIæÌù!²&ÂHÅ9J*Â@Pµ§[ÑÓß`ÏB4¶pÓ†-¦ò‘íXôڸ錻¸k‰SªaY×|´ä”¤i’VƒC©¨Ñ×Ec‚šZ}ðYÆ[ÝßÜ Cç¼[Æ™âÑ}ú¸Ê°è1~6L¬ƒ,ᣩ®:*3¨Øc.cäH‚#6¸e!ã3€ˆÓHë é\]Ü‘Il²xy›H9cêÂVli­I„SÄæL¸màÜÉÄï³0ÍÁ¬=»B’÷{Ñ<Ûm‡ ÐìG…æZƼa25pvíæF68jj×$&•CaXƆ±¶ÃuT›48`º¡È:,1€íclÛ‘F†äƒƒ# ­RØÓFdæ614EÉ×7sm)M�µRÄÝ{}#ÐxðãÖxMP4„DÀ8ó074o/7)‰Ÿ®'.%EQ.F‘¨Hä¸Î'h¹ï· j«ü.mêéª2õ×î!\¾ââð1‡r4—ë´°À4’ƒGgß´7jéõ¶µ”€2,ñã¼^-7OH“ža:f®2!¡ç ³ –f÷óÈH 37° ¹9 ƒ¤­×œ¨WÅ-fÆZBºE½¨ûO46ÛZ«ºx{šé­‚�꯭éE¡êÌXÄ ³:†¡…�¿µóŽÖ ‹@õO`úÊø$‡Ïq’Ðûž”%'1ŒßÝ™±)õ¡M)ϹALö�Ä6NAóït®IUHÈ&˜æF©å9�óŠ«íéîz±×)°ê'DM»´r0-œÍ3e!„!qÕLüI„^¨|$ÑÃOŠâ¥X£àãöAóŠ4Ô8joPõ¡1Š—wYØÂÂ< >²š’me¨²ë ˜Nó+ûz¼ßÁÏ $A¨1±J†3(¡5cèm€ÉsA£ë8;â|:¨ *>ôQ¼õ “n7ˆgOå 7þžªM½–®»G Á>Ÿ0Ή’¾¢Çl³qŒ;¢£@Üdg=8$¦¶|áPÓB‘‘Û!ä5†Ã1 ’ñ“ƨZ{©ŒUD˘Y+Èj¦GÍö"+žÎ~]5¼¸Ç†òr ’-·ÏÌ>v¹ÜT d[w›7y—�Ñ:.ƒ1‰‡CKQw •ðƒr§ æ>¸›žÃœ: ¸s*·£«=?[Àõ® €þ¿­zÞÜ¡\µ™…‚ä@{F&æ?´O¾C!=ÉßÜyý?}¶¼º“+ñ1B.Ã2ùýú2Â|I’Üþ%7µ˜“98ù—FL3½îó.¬yrõOZ*O—Ë•Xï ¦Ù¶}Ì>©ƒ‘€{`®adøt1PÔé ÞÄÖ¸×.‹­V/Ko_DÄ"Hgwº¾(%oÛÓÉËMHŒ‘lÛé//ÿÅÜ‘N$4mÙ@���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/sum.tests������������������������������������������������������������������0000755�0000000�0000000�00000001503�12263563520�015715� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # unit test for sum. # Copyright 2007 by Bernhard Reutner-Fischer # Licensed under GPLv2 or later, see file LICENSE in this source tree. # AUDIT: Unit tests for sum . ./testing.sh # testing "test name" "options" "expected result" "file input" "stdin" # file input will be file called "input" # test can create a file "actual" instead of writing to stdout testing "sum -r file doesn't print file's name" \ "sum -r $0 | grep -c $0 && echo wrongly_printed_filename || echo yes" \ "0\nyes\n" "" "" testing "sum -r file file does print both names" \ "sum -r $0 $0 | grep -c $0 && echo yes || echo wrongly_omitted_filename" \ "2\nyes\n" "" "" testing "sum -s file does print file's name" \ "sum -s $0 | grep -c $0 && echo yes || echo wrongly_omitted_filename" \ "1\nyes\n" "" "" exit $FAILCOUNT ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/wc/������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014430� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/wc/wc-counts-words���������������������������������������������������������0000644�0000000�0000000�00000000067�12263563520�017440� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test `echo i\'m a little teapot | busybox wc -w` -eq 4 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/wc/wc-counts-all�����������������������������������������������������������0000644�0000000�0000000�00000000171�12263563520�017046� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# 1 line, 4 words, 20 chars. test "`echo i\'m a little teapot | busybox wc | sed 's/ */ /g' | sed 's/^ //'`" = '1 4 20' �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/wc/wc-prints-longest-line-length�������������������������������������������0000644�0000000�0000000�00000000070�12263563520�022157� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test `echo i\'m a little teapot | busybox wc -L` -eq 19 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/wc/wc-counts-characters����������������������������������������������������0000644�0000000�0000000�00000000070�12263563520�020413� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test `echo i\'m a little teapot | busybox wc -c` -eq 20 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/wc/wc-counts-lines���������������������������������������������������������0000644�0000000�0000000�00000000067�12263563520�017414� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test `echo i\'m a little teapot | busybox wc -l` -eq 1 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/TODO�����������������������������������������������������������������������0000644�0000000�0000000�00000001727�12263563520�014522� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This testsuite is quite obviously a work in progress. As such, there are a number of good extensions. If you are looking for something to do, feel free to tackle one or more of the following: Moving to the new format. The old way was "lots of little tests files in a directory", which doesn't interact well with source control systems. The new test format is command.tests files that use testing.sh. Every busybox applet needs a corresponding applet.tests. Full SUSv3 test suite. Let's make the Linux Test Project jealous, shall we? Don't just audit programs for standards compliance, _prove_ it with a regression test harness. http://www.opengroup.org/onlinepubs/009695399/utilities/ Some tests need root access. It's hard to test things like mount or init as a normal user. Possibly User Mode Linux could be used for this, or perhaps Erik's buildroot. libbb unit testing Being able to test the functions of libbb individually may help to prevent regressions. �����������������������������������������busybox-1.22.1/testsuite/uptime/��������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�015322� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/uptime/uptime-works��������������������������������������������������������0000644�0000000�0000000�00000000017�12263563520�017715� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox uptime �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tee/�����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014574� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tee/tee-tees-input���������������������������������������������������������0000644�0000000�0000000�00000000153�12263563520�017372� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo i\'m a little teapot >foo echo i\'m a little teapot | busybox tee bar >baz cmp foo bar && cmp foo baz ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tee/tee-appends-input������������������������������������������������������0000644�0000000�0000000�00000000220�12263563520�020057� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo i\'m a little teapot >foo cp foo bar echo i\'m a little teapot >>foo echo i\'m a little teapot | busybox tee -a bar >/dev/null cmp foo bar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/expand/��������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�015276� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/expand/expand-works-like-GNU�����������������������������������������������0000644�0000000�0000000�00000000670�12263563520�021223� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_UNEXPAND rm -f foo bar $ECHO -e "\ty" | expand -t 3 ../../busybox > foo $ECHO -e "\ty" | busybox unexpand -t 3 ../../busybox > bar set +e test ! -f foo -a -f bar if [ $? = 0 ] ; then set -e diff -q foo bar fi rm -f foo bar $ECHO -e "\ty\tx" | expand -it 3 ../../busybox > foo $ECHO -e "\ty\tx" | busybox unexpand -it 3 ../../busybox > bar set +e test ! -f foo -a -f bar if [ $? = 0 ] ; then set -e diff -q foo bar fi ������������������������������������������������������������������������busybox-1.22.1/testsuite/pwd/�����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014611� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/pwd/pwd-prints-working-directory�������������������������������������������0000644�0000000�0000000�00000000035�12263563520�022325� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test $(pwd) = $(busybox pwd) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/awk.tests������������������������������������������������������������������0000755�0000000�0000000�00000015046�12263563520�015702� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2007 by Denys Vlasenko <vda.linux@googlemail.com> # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "description" "command" "result" "infile" "stdin" testing "awk -F case 0" "awk -F '[#]' '{ print NF }'" "" "" "" testing "awk -F case 1" "awk -F '[#]' '{ print NF }'" "0\n" "" "\n" testing "awk -F case 2" "awk -F '[#]' '{ print NF }'" "2\n" "" "#\n" testing "awk -F case 3" "awk -F '[#]' '{ print NF }'" "3\n" "" "#abc#\n" testing "awk -F case 4" "awk -F '[#]' '{ print NF }'" "3\n" "" "#abc#zz\n" testing "awk -F case 5" "awk -F '[#]' '{ print NF }'" "4\n" "" "#abc##zz\n" testing "awk -F case 6" "awk -F '[#]' '{ print NF }'" "4\n" "" "z#abc##zz\n" testing "awk -F case 7" "awk -F '[#]' '{ print NF }'" "5\n" "" "z##abc##zz\n" # conditions and operators testing "awk if operator == " "awk 'BEGIN{if(23==23) print \"foo\"}'" "foo\n" "" "" testing "awk if operator != " "awk 'BEGIN{if(23!=23) print \"bar\"}'" "" "" "" testing "awk if operator >= " "awk 'BEGIN{if(23>=23) print \"foo\"}'" "foo\n" "" "" testing "awk if operator < " "awk 'BEGIN{if(2 < 13) print \"foo\"}'" "foo\n" "" "" testing "awk if string == " "awk 'BEGIN{if(\"a\"==\"ab\") print \"bar\"}'" "" "" "" # 4294967295 = 0xffffffff testing "awk bitwise op" "awk '{ print or(4294967295,1) }'" "4294967295\n" "" "\n" # we were testing for a non-empty body when deciding if a function was # defined or not. The testcase below caused: # awk: cmd. line:8: Call to undefined function prg=' function empty_fun(count) { # empty } END { i=1 print "L" i "\n" empty_fun(i + i + ++i) print "L" i "\n" }' testing "awk handles empty function f(arg){}" \ "awk '$prg'" \ "L1\n\nL2\n\n" \ "" "" prg=' function outer_fun() { return 1 } END { i=1 print "L" i "\n" i += outer_fun() print "L" i "\n" }' testing "awk properly handles function from other scope" \ "awk '$prg'" \ "L1\n\nL2\n\n" \ "" "" prg=' END { i=1 print "L" i "\n" i + trigger_error_fun() print "L" i "\n" }' testing "awk properly handles undefined function" \ "awk '$prg' 2>&1" \ "L1\n\nawk: cmd. line:5: Call to undefined function\n" \ "" "" optional DESKTOP testing "awk hex const 1" "awk '{ print or(0xffffffff,1) }'" "4294967295\n" "" "\n" testing "awk hex const 2" "awk '{ print or(0x80000000,1) }'" "2147483649\n" "" "\n" testing "awk oct const" "awk '{ print or(01234,1) }'" "669\n" "" "\n" SKIP= # check that "hex/oct integer" heuristic doesn't kick in on 00NN.NNN testing "awk floating const with leading zeroes" \ "awk '{ printf \"%f %f\n\", \"000.123\", \"009.123\" }'" \ "0.123000 9.123000\n" \ "" "\n" # long field seps requiring regex testing "awk long field sep" "awk -F-- '{ print NF, length(\$NF), \$NF }'" \ "2 0 \n3 0 \n4 0 \n5 0 \n" \ "" \ "a--\na--b--\na--b--c--\na--b--c--d--" testing "awk -F handles escapes" "awk -F'\\x21' '{print \$1}'" \ "a\n" \ "" \ "a!b\n" # '@(samp|code|file)\{' is an invalid extended regex (unmatched '{'), # but gawk 3.1.5 does not bail out on it. testing "awk gsub falls back to non-extended-regex" \ "awk 'gsub(\"@(samp|code|file)\{\",\"\");'; echo \$?" "0\n" "" "Hi\n" optional TAR BUNZIP2 FEATURE_SEAMLESS_BZ2 test x"$SKIP" != x"1" && tar xjf awk_t1.tar.bz2 testing "awk 'gcc build bug'" \ "awk -f awk_t1_opt-functions.awk -f awk_t1_opth-gen.awk <awk_t1_input | md5sum" \ "f842e256461a5ab1ec60b58d16f1114f -\n" \ "" "" rm -rf awk_t1_* 2>/dev/null SKIP= Q='":"' testing "awk NF in BEGIN" \ "awk 'BEGIN { print ${Q} NF ${Q} \$0 ${Q} \$1 ${Q} \$2 ${Q} }'" \ ":0::::\n" \ "" "" prg=' function b(tmp) { tmp = 0; print "" tmp; #this line causes the bug return tmp; } function c(tmpc) { tmpc = b(); return tmpc; } BEGIN { print (c() ? "string" : "number"); }' testing "awk string cast (bug 725)" \ "awk '$prg'" \ "0\nnumber\n" \ "" "" testing "awk handles whitespace before array subscript" \ "awk 'BEGIN { arr [3] = 1; print arr [3] }'" "1\n" "" "" # GNU awk 3.1.5's "print ERRNO" prints "No such file or directory" instead of "2", # do we need to emulate that as well? testing "awk handles non-existing file correctly" \ "awk 'BEGIN { getline line <\"doesnt_exist\"; print ERRNO; ERRNO=0; close(\"doesnt_exist\"); print ERRNO; print \"Ok\" }'" \ "2\n0\nOk\n" "" "" prg=' BEGIN { u["a"]=1 u["b"]=1 u["c"]=1 v["d"]=1 v["e"]=1 v["f"]=1 for (l in u) { print "outer1", l; for (l in v) { print " inner", l; } print "outer2", l; } print "end", l; l="a" exit; }' testing "awk nested loops with the same variable" \ "awk '$prg'" \ "\ outer1 a inner d inner e inner f outer2 f outer1 b inner d inner e inner f outer2 f outer1 c inner d inner e inner f outer2 f end f " \ "" "" prg=' BEGIN { u["a"]=1 u["b"]=1 u["c"]=1 v["d"]=1 v["e"]=1 v["f"]=1 for (l in u) { print "outer1", l; for (l in v) { print " inner", l; break; } print "outer2", l; } print "end", l; l="a" exit; }' # It's not just buggy, it enters infinite loop. Thus disabled false && test x"$SKIP_KNOWN_BUGS" = x"" && testing "awk nested loops with the same variable and break" \ "awk '$prg'" \ "\ outer1 a inner d outer2 d outer1 b inner d outer2 d outer1 c inner d outer2 d end d " \ "" "" prg=' function f() { for (l in v) { print " inner", l; return; } } BEGIN { u["a"]=1 u["b"]=1 u["c"]=1 v["d"]=1 v["e"]=1 v["f"]=1 for (l in u) { print "outer1", l; f(); print "outer2", l; } print "end", l; l="a" exit; }' # It's not just buggy, it enters infinite loop. Thus disabled false && test x"$SKIP_KNOWN_BUGS" = x"" && testing "awk nested loops with the same variable and return" \ "awk '$prg'" \ "\ outer1 a inner d outer2 d outer1 b inner d outer2 d outer1 c inner d outer2 d end d " \ "" "" testing "awk handles empty ()" \ "awk 'BEGIN {print()}' 2>&1" "awk: cmd. line:1: Empty sequence\n" "" "" testing "awk FS assignment" "awk '{FS=\":\"; print \$1}'" \ "a:b\ne\n" \ "" \ "a:b c:d\ne:f g:h" optional FEATURE_AWK_LIBM testing "awk large integer" \ "awk 'BEGIN{n=(2^31)-1; print n, int(n), n%1, ++n, int(n), n%1}'" \ "2147483647 2147483647 0 2147483648 2147483648 0\n" \ "" "" SKIP= testing "awk length(array)" \ "awk 'BEGIN{ A[1]=2; A[\"qwe\"]=\"asd\"; print length(A)}'" \ "2\n" \ "" "" testing "awk -f and ARGC" \ "awk -f - input" \ "re\n2\n" \ "do re mi\n" \ '{print $2; print ARGC;}' \ optional FEATURE_AWK_GNU_EXTENSIONS testing "awk -e and ARGC" \ "awk -e '{print \$2; print ARGC;}' input" \ "re\n2\n" \ "do re mi\n" \ "" SKIP= # testing "description" "command" "result" "infile" "stdin" exit $FAILCOUNT ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tar.tests������������������������������������������������������������������0000755�0000000�0000000�00000013451�12263563520�015704� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2009 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh unset LANG unset LANGUAGE unset LC_COLLATE unset LC_ALL umask 022 rm -rf tar.tempdir 2>/dev/null mkdir tar.tempdir && cd tar.tempdir || exit 1 # testing "test name" "script" "expected result" "file input" "stdin" testing "Empty file is not a tarball" '\ tar xvf - 2>&1; echo $? ' "\ tar: short read 1 " \ "" "" SKIP= optional FEATURE_SEAMLESS_GZ # In NOMMU case, "invalid magic" message comes from gunzip child process. # Otherwise, it comes from tar. # Need to fix output up to avoid false positive. testing "Empty file is not a tarball.tar.gz" '\ { tar xvzf - 2>&1; echo $?; } | grep -Fv "invalid magic" ' "\ tar: short read 1 " \ "" "" SKIP= testing "Two zeroed blocks is a ('truncated') empty tarball" '\ dd if=/dev/zero bs=512 count=2 2>/dev/null | tar xvf - 2>&1; echo $? ' "\ 0 " \ "" "" SKIP= testing "Twenty zeroed blocks is an empty tarball" '\ dd if=/dev/zero bs=512 count=20 2>/dev/null | tar xvf - 2>&1; echo $? ' "\ 0 " \ "" "" SKIP= optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES testing "tar hardlinks and repeated files" '\ rm -rf input_* test.tar 2>/dev/null >input_hard1 ln input_hard1 input_hard2 mkdir input_dir >input_dir/file chmod -R 644 * chmod 755 input_dir tar cf test.tar input input_dir/ input_hard1 input_hard2 input_hard1 input_dir/ input tar tvf test.tar | sed "s/.*[0-9] input/input/" tar xf test.tar 2>&1 echo Ok: $? ls -l . input_dir/* | grep input_ | sed "s/\\(^[^ ]*\\) .* input/\\1 input/" ' "\ input input_dir/ input_dir/file input_hard1 input_hard2 -> input_hard1 input_hard1 -> input_hard1 input_dir/ input_dir/file input Ok: 0 -rw-r--r-- input_dir/file drwxr-xr-x input_dir -rw-r--r-- input_hard1 -rw-r--r-- input_hard2 " \ "" "" SKIP= optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES testing "tar hardlinks mode" '\ rm -rf input_* test.tar 2>/dev/null >input_hard1 chmod 741 input_hard1 ln input_hard1 input_hard2 mkdir input_dir ln input_hard1 input_dir ln input_hard2 input_dir chmod 550 input_dir # On some filesystems, input_dir/input_hard2 is returned by readdir # BEFORE input_dir/input_hard1! Thats why we cant just "tar cf ... input_*": tar cf test.tar input_dir/input_hard* input_hard* tar tvf test.tar | sed "s/.*[0-9] input/input/" chmod 770 input_dir rm -rf input_* tar xf test.tar 2>&1 echo Ok: $? ls -l . input_dir/* | grep "input.*hard" | sed "s/\\(^[^ ]*\\) .* input/\\1 input/" ' "\ input_dir/input_hard1 input_dir/input_hard2 -> input_dir/input_hard1 input_hard1 -> input_dir/input_hard1 input_hard2 -> input_dir/input_hard1 Ok: 0 -rwxr----x input_dir/input_hard1 -rwxr----x input_dir/input_hard2 -rwxr----x input_hard1 -rwxr----x input_hard2 " \ "" "" SKIP= optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES testing "tar symlinks mode" '\ rm -rf input_* test.tar 2>/dev/null >input_file chmod 741 input_file ln -s input_file input_soft mkdir input_dir ln input_file input_dir ln input_soft input_dir chmod 550 input_dir tar cf test.tar input_dir/* input_[fs]* tar tvf test.tar | sed "s/.*[0-9] input/input/" | sort chmod 770 input_dir rm -rf input_* tar xf test.tar 2>&1 echo Ok: $? ls -l . input_dir/* | grep "input_[fs]" | sed "s/\\(^[^ ]*\\) .* input/\\1 input/" ' "\ input_dir/input_file input_dir/input_soft -> input_file input_file -> input_dir/input_file input_soft -> input_dir/input_soft Ok: 0 -rwxr----x input_dir/input_file lrwxrwxrwx input_file -rwxr----x input_file lrwxrwxrwx input_file " \ "" "" SKIP= optional FEATURE_TAR_CREATE FEATURE_TAR_LONG_OPTIONS testing "tar --overwrite" "\ rm -rf input_* test.tar 2>/dev/null ln input input_hard tar cf test.tar input_hard echo WRONG >input # --overwrite opens 'input_hard' without unlinking, # thus 'input_hard' still linked to 'input' and we write 'Ok' into it tar xf test.tar --overwrite 2>&1 && cat input " "\ Ok " \ "Ok\n" "" SKIP= test x"$SKIP_KNOWN_BUGS" = x"" && { # Needs to be run under non-root for meaningful test optional FEATURE_TAR_CREATE testing "tar writing into read-only dir" '\ rm -rf input_* test.tar 2>/dev/null mkdir input_dir >input_dir/input_file chmod 550 input_dir tar cf test.tar input_dir tar tvf test.tar | sed "s/.*[0-9] input/input/" chmod 770 input_dir rm -rf input_* tar xf test.tar 2>&1 echo Ok: $? ls -l input_dir/* . | grep input_ | sed "s/\\(^[^ ]*\\) .* input/\\1 input/" chmod 770 input_dir ' "\ input_dir/ input_dir/input_file Ok: 0 -rw-r--r-- input_dir/input_file dr-xr-x--- input_dir " \ "" "" SKIP= } # Had a bug where on extract autodetect first "switched off" -z # and then failed to recognize .tgz extension optional FEATURE_TAR_CREATE FEATURE_SEAMLESS_GZ testing "tar extract tgz" "\ dd count=1 bs=1M if=/dev/zero of=F0 2>/dev/null tar -czf F0.tgz F0 rm F0 tar -xzvf F0.tgz && echo Ok rm F0 || echo BAD " "\ F0 Ok " \ "" "" SKIP= # Do we detect XZ-compressed data (even w/o .tar.xz or txz extension)? # (the uuencoded hello_world.txz contains one empty file named "hello_world") optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_XZ testing "tar extract txz" "\ uudecode -o input && tar tf input && echo Ok " "\ hello_world Ok " \ "" "\ begin-base64 644 hello_world.txz /Td6WFoAAATm1rRGAgAhARYAAAB0L+Wj4AX/AEldADQZSe6ODIZQ3rSQ8kAJ SnMPTX+XWGKW3Yu/Rwqg4Ik5wqgQKgVH97J8yA8IvZ4ahaCQogUNHRkXibr2 Q615wcb2G7fJU49AhWAAAAAAUA8gu9DyXfAAAWWADAAAAB5FXGCxxGf7AgAA AAAEWVo= ==== " SKIP= # On extract, everything up to and including last ".." component is stripped optional FEATURE_TAR_CREATE testing "tar strips /../ on extract" "\ rm -rf input_* test.tar 2>/dev/null mkdir input_dir echo Ok >input_dir/file tar cf test.tar ./../tar.tempdir/input_dir/../input_dir 2>&1 rm -rf input_* 2>/dev/null tar -vxf test.tar 2>&1 cat input_dir/file 2>&1 " "\ tar: removing leading './../tar.tempdir/input_dir/../' from member names input_dir/ input_dir/file Ok " \ "" "" SKIP= cd .. && rm -rf tar.tempdir || exit 1 exit $FAILCOUNT �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/makedevs.device_table.txt��������������������������������������������������0000644�0000000�0000000�00000013467�12263563520�021003� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# When building a target filesystem, it is desirable to not have to # become root and then run 'mknod' a thousand times. Using a device # table you can create device nodes and directories "on the fly". # # This is a sample device table file for use with genext2fs. You can # do all sorts of interesting things with a device table file. For # example, if you want to adjust the permissions on a particular file # you can just add an entry like: # /sbin/foobar f 2755 0 0 - - - - - # and (assuming the file /sbin/foobar exists) it will be made setuid # root (regardless of what its permissions are on the host filesystem). # Furthermore, you can use a single table entry to create a many device # minors. For example, if I wanted to create /dev/hda and /dev/hda[0-15] # I could just use the following two table entries: # /dev/hda b 640 0 0 3 0 0 0 - # /dev/hda b 640 0 0 3 1 1 1 15 # # Device table entries take the form of: # <name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count> # where name is the file name, type can be one of: # f A regular file # d Directory # c Character special device file # b Block special device file # p Fifo (named pipe) # uid is the user id for the target file, gid is the group id for the # target file. The rest of the entries (major, minor, etc) apply only # to device special files. # Have fun # -Erik Andersen <andersen@codepoet.org> # #<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count> /dev d 755 0 0 - - - - - /dev/pts d 755 0 0 - - - - - /dev/shm d 755 0 0 - - - - - /tmp d 1777 0 0 - - - - - /etc d 755 0 0 - - - - - /home/default d 2755 1000 1000 - - - - - #<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count> ###/bin/busybox f 4755 0 0 - - - - - ###/etc/shadow f 600 0 0 - - - - - ###/etc/passwd f 644 0 0 - - - - - /etc/network/if-up.d d 755 0 0 - - - - - /etc/network/if-pre-up.d d 755 0 0 - - - - - /etc/network/if-down.d d 755 0 0 - - - - - /etc/network/if-post-down.d d 755 0 0 - - - - - ###/usr/share/udhcpc/default.script f 755 0 0 - - - - - # uncomment this to allow starting x as non-root #/usr/X11R6/bin/Xfbdev f 4755 0 0 - - - - - # Normal system devices # <name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count> /dev/mem c 640 0 0 1 1 0 0 - /dev/kmem c 640 0 0 1 2 0 0 - /dev/null c 666 0 0 1 3 0 0 - /dev/zero c 666 0 0 1 5 0 0 - /dev/random c 666 0 0 1 8 0 0 - /dev/urandom c 666 0 0 1 9 0 0 - /dev/ram b 640 0 0 1 1 0 0 - /dev/ram b 640 0 0 1 0 0 1 4 /dev/loop b 640 0 0 7 0 0 1 2 /dev/rtc c 640 0 0 10 135 - - - /dev/console c 666 0 0 5 1 - - - /dev/tty c 666 0 0 5 0 - - - /dev/tty c 666 0 0 4 0 0 1 8 /dev/ttyp c 666 0 0 3 0 0 1 10 /dev/ptyp c 666 0 0 2 0 0 1 10 /dev/ptmx c 666 0 0 5 2 - - - /dev/ttyP c 666 0 0 57 0 0 1 4 /dev/ttyS c 666 0 0 4 64 0 1 4 /dev/fb c 640 0 5 29 0 0 32 4 #/dev/ttySA c 666 0 0 204 5 0 1 3 /dev/psaux c 666 0 0 10 1 0 0 - #/dev/ppp c 666 0 0 108 0 - - - # Input stuff /dev/input d 755 0 0 - - - - - /dev/input/mice c 640 0 0 13 63 0 0 - /dev/input/mouse c 660 0 0 13 32 0 1 4 /dev/input/event c 660 0 0 13 64 0 1 4 #/dev/input/js c 660 0 0 13 0 0 1 4 # MTD stuff /dev/mtd c 640 0 0 90 0 0 2 4 /dev/mtdblock b 640 0 0 31 0 0 1 4 #Tun/tap driver /dev/net d 755 0 0 - - - - - /dev/net/tun c 660 0 0 10 200 - - - # Audio stuff #/dev/audio c 666 0 29 14 4 - - - #/dev/audio1 c 666 0 29 14 20 - - - #/dev/dsp c 666 0 29 14 3 - - - #/dev/dsp1 c 666 0 29 14 19 - - - #/dev/sndstat c 666 0 29 14 6 - - - # User-mode Linux stuff #/dev/ubda b 640 0 0 98 0 0 0 - #/dev/ubda b 640 0 0 98 1 1 1 15 # IDE Devices /dev/hda b 640 0 0 3 0 0 0 - /dev/hda b 640 0 0 3 1 1 1 15 /dev/hdb b 640 0 0 3 64 0 0 - /dev/hdb b 640 0 0 3 65 1 1 15 #/dev/hdc b 640 0 0 22 0 0 0 - #/dev/hdc b 640 0 0 22 1 1 1 15 #/dev/hdd b 640 0 0 22 64 0 0 - #/dev/hdd b 640 0 0 22 65 1 1 15 #/dev/hde b 640 0 0 33 0 0 0 - #/dev/hde b 640 0 0 33 1 1 1 15 #/dev/hdf b 640 0 0 33 64 0 0 - #/dev/hdf b 640 0 0 33 65 1 1 15 #/dev/hdg b 640 0 0 34 0 0 0 - #/dev/hdg b 640 0 0 34 1 1 1 15 #/dev/hdh b 640 0 0 34 64 0 0 - #/dev/hdh b 640 0 0 34 65 1 1 15 # SCSI Devices #/dev/sda b 640 0 0 8 0 0 0 - #/dev/sda b 640 0 0 8 1 1 1 15 #/dev/sdb b 640 0 0 8 16 0 0 - #/dev/sdb b 640 0 0 8 17 1 1 15 #/dev/sdc b 640 0 0 8 32 0 0 - #/dev/sdc b 640 0 0 8 33 1 1 15 #/dev/sdd b 640 0 0 8 48 0 0 - #/dev/sdd b 640 0 0 8 49 1 1 15 #/dev/sde b 640 0 0 8 64 0 0 - #/dev/sde b 640 0 0 8 65 1 1 15 #/dev/sdf b 640 0 0 8 80 0 0 - #/dev/sdf b 640 0 0 8 81 1 1 15 #/dev/sdg b 640 0 0 8 96 0 0 - #/dev/sdg b 640 0 0 8 97 1 1 15 #/dev/sdh b 640 0 0 8 112 0 0 - #/dev/sdh b 640 0 0 8 113 1 1 15 #/dev/sg c 640 0 0 21 0 0 1 15 #/dev/scd b 640 0 0 11 0 0 1 15 #/dev/st c 640 0 0 9 0 0 1 8 #/dev/nst c 640 0 0 9 128 0 1 8 #/dev/st c 640 0 0 9 32 1 1 4 #/dev/st c 640 0 0 9 64 1 1 4 #/dev/st c 640 0 0 9 96 1 1 4 # Floppy disk devices #/dev/fd b 640 0 0 2 0 0 1 2 #/dev/fd0d360 b 640 0 0 2 4 0 0 - #/dev/fd1d360 b 640 0 0 2 5 0 0 - #/dev/fd0h1200 b 640 0 0 2 8 0 0 - #/dev/fd1h1200 b 640 0 0 2 9 0 0 - #/dev/fd0u1440 b 640 0 0 2 28 0 0 - #/dev/fd1u1440 b 640 0 0 2 29 0 0 - #/dev/fd0u2880 b 640 0 0 2 32 0 0 - #/dev/fd1u2880 b 640 0 0 2 33 0 0 - # All the proprietary cdrom devices in the world #/dev/aztcd b 640 0 0 29 0 0 0 - #/dev/bpcd b 640 0 0 41 0 0 0 - #/dev/capi20 c 640 0 0 68 0 0 1 2 #/dev/cdu31a b 640 0 0 15 0 0 0 - #/dev/cdu535 b 640 0 0 24 0 0 0 - #/dev/cm206cd b 640 0 0 32 0 0 0 - #/dev/sjcd b 640 0 0 18 0 0 0 - #/dev/sonycd b 640 0 0 15 0 0 0 - #/dev/gscd b 640 0 0 16 0 0 0 - #/dev/sbpcd b 640 0 0 25 0 0 0 - #/dev/sbpcd b 640 0 0 25 0 0 1 4 #/dev/mcd b 640 0 0 23 0 0 0 - #/dev/optcd b 640 0 0 17 0 0 0 - ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/msh/�����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014606� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/msh/msh-supports-underscores-in-variable-names�����������������������������0000644�0000000�0000000�00000000073�12263563520�025043� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test "`busybox msh -c 'FOO_BAR=foo; echo $FOO_BAR'`" = foo ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ls/������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014435� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ls/ls-h-works��������������������������������������������������������������0000644�0000000�0000000�00000000306�12263563520�016371� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_LS_SORTFILES CONFIG_FEATURE_HUMAN_READABLE [ -n "$d" ] || d=.. LC_ALL=C ls -h "$d" > logfile.gnu LC_ALL=C busybox ls -h "$d" > logfile.bb diff -ubw logfile.gnu logfile.bb ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ls/ls-l-works��������������������������������������������������������������0000644�0000000�0000000�00000000323�12263563520�016374� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x"$SKIP_KNOWN_BUGS" != x"" && exit # busybox does not emit "total NNN" line [ -n "$d" ] || d=.. LC_ALL=C ls -l "$d" > logfile.gnu LC_ALL=C busybox ls -l "$d" > logfile.bb diff -ubw logfile.gnu logfile.bb �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ls/ls-1-works��������������������������������������������������������������0000644�0000000�0000000�00000000250�12263563520�016300� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_LS_SORTFILES [ -n "$d" ] || d=.. LC_ALL=C ls -1 "$d" > logfile.gnu LC_ALL=C busybox ls -1 "$d" > logfile.bb diff -ubw logfile.gnu logfile.bb ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ls/ls-s-works��������������������������������������������������������������0000644�0000000�0000000�00000000325�12263563520�016405� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x"$SKIP_KNOWN_BUGS" != x"" && exit # busybox does not emit "total NNN" line [ -n "$d" ] || d=.. LC_ALL=C ls -1s "$d" > logfile.gnu LC_ALL=C busybox ls -1s "$d" > logfile.bb diff -ubw logfile.gnu logfile.bb �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/busybox.tests��������������������������������������������������������������0000755�0000000�0000000�00000002341�12263563520�016605� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Tests for busybox applet itself. # Copyright 2005 by Rob Landley <rob@landley.net> # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh HELPDUMP=`true | busybox 2>&1 | cat` # We need to test under calling the binary under other names. optional FEATURE_VERBOSE_USAGE testing "busybox --help busybox" "true | busybox --help busybox 2>&1 | cat" "$HELPDUMP\n\n" "" "" SKIP= ln -s `which busybox` busybox-suffix for i in busybox ./busybox-suffix do # The gratuitous "\n"s are due to a shell idiosyncrasy: # environment variables seem to strip trailing whitespace. testing "" "$i" "$HELPDUMP\n\n" "" "" testing "$i unknown" "$i unknown 2>&1" \ "unknown: applet not found\n" "" "" testing "$i --help" "$i --help 2>&1" "$HELPDUMP\n\n" "" "" optional FEATURE_VERBOSE_USAGE CAT testing "" "$i cat" "moo" "" "moo" testing "$i --help cat" "$i --help cat 2>&1 | grep print" \ "Concatenate FILEs and print them to stdout\n" "" "" SKIP= testing "$i --help unknown" "$i --help unknown 2>&1" \ "unknown: applet not found\n" "" "" done rm busybox-suffix ln -s `which busybox` unknown testing "busybox as unknown name" "./unknown 2>&1" \ "unknown: applet not found\n" "" "" rm unknown exit $FAILCOUNT �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/rmdir/���������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�015134� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/rmdir/rmdir-removes-parent-directories�������������������������������������0000644�0000000�0000000�00000000070�12263563520�023454� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mkdir -p foo/bar busybox rmdir -p foo/bar test ! -d foo ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/strings/�������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�015510� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/strings/strings-works-like-GNU���������������������������������������������0000644�0000000�0000000�00000000246�12263563520�021646� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rm -f foo bar strings -af ../../busybox > foo busybox strings -af ../../busybox > bar set +e test ! -f foo -a -f bar if [ $? = 0 ] ; then set -e diff -q foo bar fi ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tail/����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014750� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tail/tail-works������������������������������������������������������������0000644�0000000�0000000�00000000244�12263563520�016773� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_INCLUDE_SUSv2 $ECHO -ne "abc\ndef\n123\n" >input $ECHO -ne "def\n123\n" >logfile.ok busybox tail -2 input > logfile.bb cmp logfile.ok logfile.bb ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/tail/tail-n-works����������������������������������������������������������0000644�0000000�0000000�00000000205�12263563520�017223� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$ECHO -ne "abc\ndef\n123\n" >input $ECHO -ne "def\n123\n" >logfile.ok busybox tail -n 2 input > logfile.bb cmp logfile.ok logfile.bb �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/true/����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014776� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/true/true-is-silent��������������������������������������������������������0000644�0000000�0000000�00000000044�12263563520�017607� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox true 2>&1 | cmp - /dev/null ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/true/true-returns-success��������������������������������������������������0000644�0000000�0000000�00000000015�12263563520�021046� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox true �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/rx.tests�������������������������������������������������������������������0000755�0000000�0000000�00000001524�12263563520�015545� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2009 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "test name" "options" "expected result" "file input" "stdin" # Simple one-block file transfer # rx => 'C' # rx <= SOH <blockno> <255-blockno> <128 byte padded with x1A> <crc> <crc> # rx => ACK # rx <= EOT # rx => ACK testing "rx" \ "rx rx.OUTFILE | hexdump -vC && cat rx.OUTFILE" \ "\ 00000000 43 06 06 |C..|\n\ 00000003\n\ ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????" \ "" "\01\01\0376\ ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????\ \x1A\x1A\x1A\x1A\x1A\x4B\xB0\04" rm -f rx.OUTFILE 2>/dev/null exit $FAILCOUNT ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/find/����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014737� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/find/find-supports-minus-xdev����������������������������������������������0000644�0000000�0000000�00000000112�12263563520�021572� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_FIND_XDEV busybox find . -xdev >/dev/null 2>&1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/head/����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014720� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/head/head-works������������������������������������������������������������0000644�0000000�0000000�00000000164�12263563520�016714� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ -n "$d" ] || d=.. head "$d/README" > logfile.gnu busybox head "$d/README" > logfile.bb cmp logfile.gnu logfile.bb ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/head/head-n-works����������������������������������������������������������0000644�0000000�0000000�00000000176�12263563520�017152� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ -n "$d" ] || d=.. head -n 2 "$d/README" > logfile.gnu busybox head -n 2 "$d/README" > logfile.bb cmp logfile.gnu logfile.bb ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/rm/������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014435� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/rm/rm-removes-file���������������������������������������������������������0000644�0000000�0000000�00000000047�12263563520�017376� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo busybox rm foo test ! -f foo �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ls.mk_uni_tests������������������������������������������������������������0000644�0000000�0000000�00000021336�12263563520�017074� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# DO NOT EDIT THIS FILE! MOST TEXT EDITORS WILL DAMAGE IT! >'0001_1__Some_correct_UTF-8_text___________________________________________|' >'0002_2__Boundary_condition_test_cases_____________________________________|' >'0003_2.1__First_possible_sequence_of_a_certain_length_____________________|' >'0004_2.1.2__2_bytes__U-00000080_:________"€"______________________________|' >'0005_2.1.3__3_bytes__U-00000800_:________"à €"______________________________|' >'0006_2.1.4__4_bytes__U-00010000_:________"ð€€"______________________________|' >'0007_2.1.5__5_bytes__U-00200000_:________"øˆ€€€"______________________________|' >'0008_2.1.6__6_bytes__U-04000000_:________"ü„€€€€"______________________________|' >'0009_2.2__Last_possible_sequence_of_a_certain_length______________________|' >'0010_2.2.1__1_byte___U-0000007F_:________""______________________________|' >'0011_2.2.2__2_bytes__U-000007FF_:________"ß¿"______________________________|' >'0012_2.2.3__3_bytes__U-0000FFFF_:________"ï¿¿"______________________________|' >'0013_2.2.4__4_bytes__U-001FFFFF_:________"÷¿¿¿"______________________________|' >'0014_2.2.5__5_bytes__U-03FFFFFF_:________"û¿¿¿¿"______________________________|' >'0015_2.2.6__6_bytes__U-7FFFFFFF_:________"ý¿¿¿¿¿"______________________________|' >'0016_2.3__Other_boundary_conditions_______________________________________|' >'0017_2.3.1__U-0000D7FF_=_ed_9f_bf_=_"퟿"___________________________________|' >'0018_2.3.2__U-0000E000_=_ee_80_80_=_""___________________________________|' >'0019_2.3.3__U-0000FFFD_=_ef_bf_bd_=_"�"___________________________________|' >'0020_2.3.4__U-0010FFFF_=_f4_8f_bf_bf_=_"ô¿¿"________________________________|' >'0021_2.3.5__U-00110000_=_f4_90_80_80_=_"ô€€"________________________________|' >'0022_3__Malformed_sequences_______________________________________________|' >'0023_3.1__Unexpected_continuation_bytes___________________________________|' >'0024_3.1.1__First_continuation_byte_0x80:_"€"_____________________________|' >'0025_3.1.2__Last__continuation_byte_0xbf:_"¿"_____________________________|' >'0026_3.1.3__2_continuation_bytes:_"€¿"____________________________________|' >'0027_3.1.4__3_continuation_bytes:_"€¿€"___________________________________|' >'0028_3.1.5__4_continuation_bytes:_"€¿€¿"__________________________________|' >'0029_3.1.6__5_continuation_bytes:_"€¿€¿€"_________________________________|' >'0030_3.1.7__6_continuation_bytes:_"€¿€¿€¿"________________________________|' >'0031_3.1.8__7_continuation_bytes:_"€¿€¿€¿€"_______________________________|' >'0032_3.1.9__Sequence_of_all_64_possible_continuation_bytes__0x80-0xbf_:___|' >'0033____"€‚ƒ„…†‡ˆ‰Š‹ŒŽ_________________________________________________|' >'0034_____‘’“”•–—˜™š›œžŸ_________________________________________________|' >'0035_____ ¡¢£¤¥¦§¨©ª«¬­®¯_________________________________________________|' >'0036_____°±²³´µ¶·¸¹º»¼½¾¿"________________________________________________|' >'0037_3.2__Lonely_start_characters_________________________________________|' >'0038_3.2.1__All_32_first_bytes_of_2-byte_sequences__0xc0-0xdf_,___________|' >'0039________each_followed_by_a_space_character:___________________________|' >'0040____"À_Á_Â_Ã_Ä_Å_Æ_Ç_È_É_Ê_Ë_Ì_Í_Î_Ï__________________________________|' >'0041_____Ð_Ñ_Ò_Ó_Ô_Õ_Ö_×_Ø_Ù_Ú_Û_Ü_Ý_Þ_ß_"________________________________|' >'0042_3.2.2__All_16_first_bytes_of_3-byte_sequences__0xe0-0xef_,___________|' >'0043________each_followed_by_a_space_character:___________________________|' >'0044____"à_á_â_ã_ä_å_æ_ç_è_é_ê_ë_ì_í_î_ï_"________________________________|' >'0045_3.2.3__All_8_first_bytes_of_4-byte_sequences__0xf0-0xf7_,____________|' >'0046________each_followed_by_a_space_character:___________________________|' >'0047____"ð_ñ_ò_ó_ô_õ_ö_÷_"________________________________________________|' >'0048_3.2.4__All_4_first_bytes_of_5-byte_sequences__0xf8-0xfb_,____________|' >'0049________each_followed_by_a_space_character:___________________________|' >'0050____"ø_ù_ú_û_"________________________________________________________|' >'0051_3.2.5__All_2_first_bytes_of_6-byte_sequences__0xfc-0xfd_,____________|' >'0052________each_followed_by_a_space_character:___________________________|' >'0053____"ü_ý_"____________________________________________________________|' >'0054_3.3__Sequences_with_last_continuation_byte_missing___________________|' >'0055_3.3.1__2-byte_sequence_with_last_byte_missing__U+0000_:_____"À"______|' >'0056_3.3.2__3-byte_sequence_with_last_byte_missing__U+0000_:_____"à€"______|' >'0057_3.3.3__4-byte_sequence_with_last_byte_missing__U+0000_:_____"ð€€"______|' >'0058_3.3.4__5-byte_sequence_with_last_byte_missing__U+0000_:_____"ø€€€"______|' >'0059_3.3.5__6-byte_sequence_with_last_byte_missing__U+0000_:_____"ü€€€€"______|' >'0060_3.3.6__2-byte_sequence_with_last_byte_missing__U-000007FF_:_"ß"______|' >'0061_3.3.7__3-byte_sequence_with_last_byte_missing__U-0000FFFF_:_"ï¿"______|' >'0062_3.3.8__4-byte_sequence_with_last_byte_missing__U-001FFFFF_:_"÷¿¿"______|' >'0063_3.3.9__5-byte_sequence_with_last_byte_missing__U-03FFFFFF_:_"û¿¿¿"______|' >'0064_3.3.10_6-byte_sequence_with_last_byte_missing__U-7FFFFFFF_:_"ý¿¿¿¿"______|' >'0065_3.4__Concatenation_of_incomplete_sequences___________________________|' >'0066____"Àà€ð€€ø€€€ü€€€€ßï¿÷¿¿û¿¿¿ý¿¿¿¿"______________________________________________________|' >'0067_3.5__Impossible_bytes________________________________________________|' >'0068_3.5.1__fe_=_"þ"______________________________________________________|' >'0069_3.5.2__ff_=_"ÿ"______________________________________________________|' >'0070_3.5.3__fe_fe_ff_ff_=_"þþÿÿ"__________________________________________|' >'0071_4__Overlong_sequences________________________________________________|' >'0072_4.1__Examples_of_an_overlong_ASCII_character_________________________|' >'0073_4.1.1_U+002F_=_c0_af_____________=_"À¯"_______________________________|' >'0074_4.1.2_U+002F_=_e0_80_af__________=_"à€¯"_______________________________|' >'0075_4.1.3_U+002F_=_f0_80_80_af_______=_"ð€€¯"_______________________________|' >'0076_4.1.4_U+002F_=_f8_80_80_80_af____=_"ø€€€¯"_______________________________|' >'0077_4.1.5_U+002F_=_fc_80_80_80_80_af_=_"ü€€€€¯"_______________________________|' >'0078_4.2__Maximum_overlong_sequences______________________________________|' >'0079_4.2.1__U-0000007F_=_c1_bf_____________=_"Á¿"__________________________|' >'0080_4.2.2__U-000007FF_=_e0_9f_bf__________=_"àŸ¿"__________________________|' >'0081_4.2.3__U-0000FFFF_=_f0_8f_bf_bf_______=_"ð¿¿"__________________________|' >'0082_4.2.4__U-001FFFFF_=_f8_87_bf_bf_bf____=_"ø‡¿¿¿"__________________________|' >'0083_4.2.5__U-03FFFFFF_=_fc_83_bf_bf_bf_bf_=_"üƒ¿¿¿¿"__________________________|' >'0084_4.3__Overlong_representation_of_the_NUL_character____________________|' >'0085_4.3.1__U+0000_=_c0_80_____________=_"À€"______________________________|' >'0086_4.3.2__U+0000_=_e0_80_80__________=_"à€€"______________________________|' >'0087_4.3.3__U+0000_=_f0_80_80_80_______=_"ð€€€"______________________________|' >'0088_4.3.4__U+0000_=_f8_80_80_80_80____=_"ø€€€€"______________________________|' >'0089_4.3.5__U+0000_=_fc_80_80_80_80_80_=_"ü€€€€€"______________________________|' >'0090_5__Illegal_code_positions____________________________________________|' >'0091_5.1_Single_UTF-16_surrogates_________________________________________|' >'0092_5.1.1__U+D800_=_ed_a0_80_=_"í €"_______________________________________|' >'0093_5.1.2__U+DB7F_=_ed_ad_bf_=_"í­¿"_______________________________________|' >'0094_5.1.3__U+DB80_=_ed_ae_80_=_"í®€"_______________________________________|' >'0095_5.1.4__U+DBFF_=_ed_af_bf_=_"í¯¿"_______________________________________|' >'0096_5.1.5__U+DC00_=_ed_b0_80_=_"í°€"_______________________________________|' >'0097_5.1.6__U+DF80_=_ed_be_80_=_"í¾€"_______________________________________|' >'0098_5.1.7__U+DFFF_=_ed_bf_bf_=_"í¿¿"_______________________________________|' >'0099_5.2_Paired_UTF-16_surrogates_________________________________________|' >'0100_5.2.1__U+D800_U+DC00_=_ed_a0_80_ed_b0_80_=_"𐀀"______________________|' >'0101_5.2.2__U+D800_U+DFFF_=_ed_a0_80_ed_bf_bf_=_"𐏿"______________________|' >'0102_5.2.3__U+DB7F_U+DC00_=_ed_ad_bf_ed_b0_80_=_"í­¿í°€"______________________|' >'0103_5.2.4__U+DB7F_U+DFFF_=_ed_ad_bf_ed_bf_bf_=_"í­¿í¿¿"______________________|' >'0104_5.2.5__U+DB80_U+DC00_=_ed_ae_80_ed_b0_80_=_"󰀀"______________________|' >'0105_5.2.6__U+DB80_U+DFFF_=_ed_ae_80_ed_bf_bf_=_"󰏿"______________________|' >'0106_5.2.7__U+DBFF_U+DC00_=_ed_af_bf_ed_b0_80_=_"􏰀"______________________|' >'0107_5.2.8__U+DBFF_U+DFFF_=_ed_af_bf_ed_bf_bf_=_"􏿿"______________________|' >'0108_5.3_Other_illegal_code_positions_____________________________________|' >'0109_5.3.1__U+FFFE_=_ef_bf_be_=_"￾"_______________________________________|' >'0110_5.3.2__U+FFFF_=_ef_bf_bf_=_"ï¿¿"_______________________________________|' ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/runtest��������������������������������������������������������������������0000755�0000000�0000000�00000007671�12263563520�015470� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Usage: # runtest [applet1] [applet2...] . ./testing.sh total_failed=0 # Run one old-style test. # Tests are stored in applet/testcase shell scripts. # They are run using "sh -x -e applet/testcase". # Option -e will make testcase stop on the first failed command. run_applet_testcase() { local applet="$1" local testcase="$2" local status=0 local uc_applet=$(echo "$applet" | tr a-z A-Z) local testname="$testcase" testname="${testname##*/}" # take basename if grep "^# CONFIG_$uc_applet is not set$" "$bindir/.config" >/dev/null; then echo "UNTESTED: $testname" return 0 fi if grep "^# FEATURE: " "$testcase" >/dev/null; then local feature=$(sed -ne 's/^# FEATURE: //p' "$testcase") for f in $feature; do if grep "^# $f is not set$" "$bindir/.config" >/dev/null; then echo "UNTESTED: $testname" return 0 fi done fi rm -rf ".tmpdir.$applet" mkdir -p ".tmpdir.$applet" cd ".tmpdir.$applet" || return 1 # echo "Running testcase $testcase" d="$tsdir" \ sh -x -e "$testcase" >"$testname.stdout.txt" 2>&1 || status=$? if [ $status -ne 0 ]; then echo "FAIL: $testname" if [ x"$VERBOSE" != x ]; then cat "$testname.stdout.txt" fi else echo "PASS: $testname" fi cd .. rm -rf ".tmpdir.$applet" return $status } # Run all old-style tests for given applet run_oldstyle_applet_tests() { local applet="$1" local status=0 for testcase in "$tsdir/$applet"/*; do # switch on basename of $testcase case "${testcase##*/}" in .*) continue ;; # .svn, .git etc *~) continue ;; # backup files "CVS") continue ;; \#*) continue ;; # CVS merge residues *.mine) continue ;; # svn-produced junk *.r[0-9]*) continue ;; # svn-produced junk esac run_applet_testcase "$applet" "$testcase" || { status=1 total_failed=$((total_failed + 1)) } done return $status } lcwd=$(pwd) [ x"$tsdir" != x"" ] || tsdir="$lcwd" [ x"$bindir" != x"" ] || bindir="${lcwd%/*}" # one directory up from $lcwd PATH="$bindir:$PATH" export bindir # some tests need to look at $bindir/.config if [ x"$VERBOSE" = x ]; then export VERBOSE= fi if [ x"$1" = x"-v" ]; then export VERBOSE=1 shift fi implemented=$( printf "busybox " # always implemented "$bindir/busybox" 2>&1 | while read line; do if [ x"$line" = x"Currently defined functions:" ]; then xargs | sed 's/,//g' break fi done ) applets="$implemented" if [ $# -ne 0 ]; then applets="$@" fi # Populate a directory with links to all busybox applets LINKSDIR="$bindir/runtest-tempdir-links" # Comment this line out if you have put a different binary in $LINKSDIR # (say, a "standard" tool's binary) in order to run tests against it: rm -rf "$LINKSDIR" 2>/dev/null mkdir "$LINKSDIR" 2>/dev/null for i in $implemented; do # Note: if $LINKSDIR/applet exists, we do not overwrite it. # Useful if one wants to run tests against a standard utility, # not an applet. ln -s "$bindir/busybox" "$LINKSDIR/$i" 2>/dev/null done # Set up option flags so tests can be selective. export OPTIONFLAGS=:$( sed -nr 's/^CONFIG_//p' "$bindir/.config" | sed 's/=.*//' | xargs | sed 's/ /:/g' ): status=0 for applet in $applets; do # Any old-style tests for this applet? if [ -d "$tsdir/$applet" ]; then run_oldstyle_applet_tests "$applet" || status=1 fi # Is this a new-style test? if [ -f "$applet.tests" ]; then if [ ! -e "$LINKSDIR/$applet" ]; then # (avoiding bash'ism "${applet:0:4}") if ! echo "$applet" | grep "^all_" >/dev/null; then echo "SKIPPED: $applet (not built)" continue fi fi # echo "Running test $tsdir/$applet.tests" PATH="$LINKSDIR:$tsdir:$bindir:$PATH" \ "$tsdir/$applet.tests" rc=$? total_failed=$((total_failed + rc)) test $rc -ne 0 && status=1 fi done # Leaving the dir makes it somewhat easier to run failed test by hand #rm -rf "$LINKSDIR" if [ $status -ne 0 ] && [ x"$VERBOSE" = x ]; then echo "$total_failed failure(s) detected; running with -v (verbose) will give more info" fi exit $status �����������������������������������������������������������������������busybox-1.22.1/testsuite/sha512sum.tests������������������������������������������������������������0000755�0000000�0000000�00000000247�12263563520�016645� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh . ./md5sum.tests sha512sum fe413e0f177324d1353893ca0772ceba83fd41512ba63895a0eebb703ef9feac2fb4e92b2cb430b3bda41b46b0cb4ea8307190a5cc795157cfb680a9cd635d0f ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/expand.tests���������������������������������������������������������������0000755�0000000�0000000�00000001131�12263563520�016365� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2008 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh test -f "$bindir/.config" && . "$bindir/.config" # testing "test name" "options" "expected result" "file input" "stdin" testing "expand" \ "expand" \ " 12345678 12345678\n" \ "" \ "\t12345678\t12345678\n" test x"$CONFIG_UNICODE_SUPPORT" = x"y" \ && test x"$CONFIG_UNICODE_USING_LOCALE" != x"y" \ && testing "expand with unicode characher 0x394" \ "expand" \ "Δ 12345ΔΔΔ 12345678\n" \ "" \ "Δ\t12345ΔΔΔ\t12345678\n" exit $FAILCOUNT ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/gunzip/��������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�015333� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/gunzip/gunzip-reads-from-standard-input������������������������������������0000644�0000000�0000000�00000000102�12263563520�023557� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo foo | gzip | busybox gunzip > output echo foo | cmp - output ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/id/������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014413� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/id/id-u-works��������������������������������������������������������������0000644�0000000�0000000�00000000043�12263563520�016340� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x$(id -u) = x$(busybox id -u) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/id/id-un-works�������������������������������������������������������������0000644�0000000�0000000�00000000045�12263563520�016520� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x$(id -un) = x$(busybox id -un) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/id/id-ur-works�������������������������������������������������������������0000644�0000000�0000000�00000000045�12263563520�016524� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x$(id -ur) = x$(busybox id -ur) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/id/id-g-works��������������������������������������������������������������0000644�0000000�0000000�00000000043�12263563520�016322� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x$(id -g) = x$(busybox id -g) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/od.tests�������������������������������������������������������������������0000755�0000000�0000000�00000001160�12263563520�015512� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2008 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "test name" "commands" "expected result" "file input" "stdin" optional DESKTOP testing "od -b" \ "od -b" \ "\ 0000000 110 105 114 114 117 0000005 " \ "" "HELLO" SKIP= optional DESKTOP LONG_OPTS testing "od -b --traditional" \ "od -b --traditional" \ "\ 0000000 110 105 114 114 117 0000005 " \ "" "HELLO" SKIP= optional DESKTOP LONG_OPTS testing "od -b --traditional FILE" \ "od -b --traditional input" \ "\ 0000000 110 105 114 114 117 0000005 " \ "HELLO" "" SKIP= exit $FAILCOUNT ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mv/������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014441� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mv/mv-moves-file�����������������������������������������������������������0000644�0000000�0000000�00000000065�12263563520�017057� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo busybox mv foo bar test ! -f foo -a -f bar ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mv/mv-refuses-mv-dir-to-subdir���������������������������������������������0000644�0000000�0000000�00000000713�12263563520�021567� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo file number one > file1 echo file number two > file2 ln -s file2 link1 mkdir dir1 TZ=UTC0 touch -d '2000-01-30 05:24:08' dir1/file3 mkdir there busybox mv file1 file2 link1 dir1 there test -f there/file1 test -f there/file2 test -f there/dir1/file3 test -L there/link1 test xfile2 = x`readlink there/link1` test ! -e file1 test ! -e file2 test ! -e link1 test ! -e dir1/file3 set +e busybox mv there there/dir1 if [ $? != 0 ] ; then exit 0; fi exit 1; �����������������������������������������������������busybox-1.22.1/testsuite/mv/mv-moves-empty-file�����������������������������������������������������0000644�0000000�0000000�00000000067�12263563520�020215� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo busybox mv foo bar test ! -e foo test -f bar �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mv/mv-moves-unreadable-files�����������������������������������������������0000644�0000000�0000000�00000000105�12263563520�021335� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo chmod a-r foo busybox mv foo bar test ! -e foo test -f bar �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mv/mv-moves-symlinks�������������������������������������������������������0000644�0000000�0000000�00000000121�12263563520�020002� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo ln -s foo bar busybox mv bar baz test -f foo test ! -e bar test -L baz �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mv/mv-files-to-dir���������������������������������������������������������0000644�0000000�0000000�00000000575�12263563520�017315� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo file number one > file1 echo file number two > file2 ln -s file2 link1 mkdir dir1 TZ=UTC0 touch -d '2000-01-30 05:24:08' dir1/file3 mkdir there busybox mv file1 file2 link1 dir1 there test -f there/file1 test -f there/file2 test -f there/dir1/file3 test -L there/link1 test xfile2 = x`readlink there/link1` test ! -e file1 test ! -e file2 test ! -e link1 test ! -e dir1/file3 �����������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mv/mv-moves-small-file�����������������������������������������������������0000644�0000000�0000000�00000000077�12263563520�020170� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo I WANT > foo busybox mv foo bar test ! -e foo test -f bar �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mv/mv-follows-links��������������������������������������������������������0000644�0000000�0000000�00000000067�12263563520�017616� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo ln -s foo bar busybox mv bar baz test -f baz �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mv/mv-moves-hardlinks������������������������������������������������������0000644�0000000�0000000�00000000100�12263563520�020105� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo ln foo bar busybox mv bar baz test ! -f bar -a -f baz ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mv/mv-moves-large-file�����������������������������������������������������0000644�0000000�0000000�00000000141�12263563520�020142� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dd if=/dev/zero of=foo seek=10k count=1 2>/dev/null busybox mv foo bar test ! -e foo test -f bar �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mv/mv-removes-source-file��������������������������������������������������0000644�0000000�0000000�00000000067�12263563520�020706� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo busybox mv foo bar test ! -e foo test -f bar �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mv/mv-preserves-links������������������������������������������������������0000644�0000000�0000000�00000000123�12263563520�020140� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������touch foo ln -s foo bar busybox mv bar baz test -L baz test xfoo = x`readlink baz` ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mv/mv-preserves-hard-links�������������������������������������������������0000644�0000000�0000000�00000000174�12263563520�021062� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# FEATURE: CONFIG_FEATURE_PRESERVE_HARDLINKS touch foo ln foo bar mkdir baz busybox mv foo bar baz test baz/foo -ef baz/bar ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/ls.tests�������������������������������������������������������������������0000755�0000000�0000000�00000043676�12263563520�015550� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2010 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh test -f "$bindir/.config" && . "$bindir/.config" rm -rf ls.testdir 2>/dev/null mkdir ls.testdir || exit 1 # testing "test name" "command" "expected result" "file input" "stdin" # With Unicode provided by libc locale, I'm not sure this test can pass. # I suspect we might fail to skip exactly correct number of bytes # over broked unicode sequences. test x"$CONFIG_UNICODE_SUPPORT" = x"y" \ && test x"$CONFIG_UNICODE_USING_LOCALE" != x"y" \ && test x"$CONFIG_SUBST_WCHAR" = x"63" \ && test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"767" \ && test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \ && testing "ls unicode test with codepoints limited to 767" \ "(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1 ls.testdir" \ '0001_1__Some_correct_UTF-8_text___________________________________________| 0002_2__Boundary_condition_test_cases_____________________________________| 0003_2.1__First_possible_sequence_of_a_certain_length_____________________| 0004_2.1.2__2_bytes__U-00000080_:________"?"______________________________| 0005_2.1.3__3_bytes__U-00000800_:________"?"______________________________| 0006_2.1.4__4_bytes__U-00010000_:________"?"______________________________| 0007_2.1.5__5_bytes__U-00200000_:________"?"______________________________| 0008_2.1.6__6_bytes__U-04000000_:________"?"______________________________| 0009_2.2__Last_possible_sequence_of_a_certain_length______________________| 0010_2.2.1__1_byte___U-0000007F_:________"?"______________________________| 0011_2.2.2__2_bytes__U-000007FF_:________"?"______________________________| 0012_2.2.3__3_bytes__U-0000FFFF_:________"?"______________________________| 0013_2.2.4__4_bytes__U-001FFFFF_:________"?"______________________________| 0014_2.2.5__5_bytes__U-03FFFFFF_:________"?"______________________________| 0015_2.2.6__6_bytes__U-7FFFFFFF_:________"?"______________________________| 0016_2.3__Other_boundary_conditions_______________________________________| 0017_2.3.1__U-0000D7FF_=_ed_9f_bf_=_"?"___________________________________| 0018_2.3.2__U-0000E000_=_ee_80_80_=_"?"___________________________________| 0019_2.3.3__U-0000FFFD_=_ef_bf_bd_=_"?"___________________________________| 0020_2.3.4__U-0010FFFF_=_f4_8f_bf_bf_=_"?"________________________________| 0021_2.3.5__U-00110000_=_f4_90_80_80_=_"?"________________________________| 0022_3__Malformed_sequences_______________________________________________| 0023_3.1__Unexpected_continuation_bytes___________________________________| 0024_3.1.1__First_continuation_byte_0x80:_"?"_____________________________| 0025_3.1.2__Last__continuation_byte_0xbf:_"?"_____________________________| 0026_3.1.3__2_continuation_bytes:_"??"____________________________________| 0027_3.1.4__3_continuation_bytes:_"???"___________________________________| 0028_3.1.5__4_continuation_bytes:_"????"__________________________________| 0029_3.1.6__5_continuation_bytes:_"?????"_________________________________| 0030_3.1.7__6_continuation_bytes:_"??????"________________________________| 0031_3.1.8__7_continuation_bytes:_"???????"_______________________________| 0032_3.1.9__Sequence_of_all_64_possible_continuation_bytes__0x80-0xbf_:___| 0033____"????????????????_________________________________________________| 0034_____????????????????_________________________________________________| 0035_____????????????????_________________________________________________| 0036_____????????????????"________________________________________________| 0037_3.2__Lonely_start_characters_________________________________________| 0038_3.2.1__All_32_first_bytes_of_2-byte_sequences__0xc0-0xdf_,___________| 0039________each_followed_by_a_space_character:___________________________| 0040____"?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?__________________________________| 0041_____?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_"________________________________| 0042_3.2.2__All_16_first_bytes_of_3-byte_sequences__0xe0-0xef_,___________| 0043________each_followed_by_a_space_character:___________________________| 0044____"?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_"________________________________| 0045_3.2.3__All_8_first_bytes_of_4-byte_sequences__0xf0-0xf7_,____________| 0046________each_followed_by_a_space_character:___________________________| 0047____"?_?_?_?_?_?_?_?_"________________________________________________| 0048_3.2.4__All_4_first_bytes_of_5-byte_sequences__0xf8-0xfb_,____________| 0049________each_followed_by_a_space_character:___________________________| 0050____"?_?_?_?_"________________________________________________________| 0051_3.2.5__All_2_first_bytes_of_6-byte_sequences__0xfc-0xfd_,____________| 0052________each_followed_by_a_space_character:___________________________| 0053____"?_?_"____________________________________________________________| 0054_3.3__Sequences_with_last_continuation_byte_missing___________________| 0055_3.3.1__2-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______| 0056_3.3.2__3-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______| 0057_3.3.3__4-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______| 0058_3.3.4__5-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______| 0059_3.3.5__6-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______| 0060_3.3.6__2-byte_sequence_with_last_byte_missing__U-000007FF_:_"?"______| 0061_3.3.7__3-byte_sequence_with_last_byte_missing__U-0000FFFF_:_"?"______| 0062_3.3.8__4-byte_sequence_with_last_byte_missing__U-001FFFFF_:_"?"______| 0063_3.3.9__5-byte_sequence_with_last_byte_missing__U-03FFFFFF_:_"?"______| 0064_3.3.10_6-byte_sequence_with_last_byte_missing__U-7FFFFFFF_:_"?"______| 0065_3.4__Concatenation_of_incomplete_sequences___________________________| 0066____"??????????"______________________________________________________| 0067_3.5__Impossible_bytes________________________________________________| 0068_3.5.1__fe_=_"?"______________________________________________________| 0069_3.5.2__ff_=_"?"______________________________________________________| 0070_3.5.3__fe_fe_ff_ff_=_"????"__________________________________________| 0071_4__Overlong_sequences________________________________________________| 0072_4.1__Examples_of_an_overlong_ASCII_character_________________________| 0073_4.1.1_U+002F_=_c0_af_____________=_"?"_______________________________| 0074_4.1.2_U+002F_=_e0_80_af__________=_"?"_______________________________| 0075_4.1.3_U+002F_=_f0_80_80_af_______=_"?"_______________________________| 0076_4.1.4_U+002F_=_f8_80_80_80_af____=_"?"_______________________________| 0077_4.1.5_U+002F_=_fc_80_80_80_80_af_=_"?"_______________________________| 0078_4.2__Maximum_overlong_sequences______________________________________| 0079_4.2.1__U-0000007F_=_c1_bf_____________=_"?"__________________________| 0080_4.2.2__U-000007FF_=_e0_9f_bf__________=_"?"__________________________| 0081_4.2.3__U-0000FFFF_=_f0_8f_bf_bf_______=_"?"__________________________| 0082_4.2.4__U-001FFFFF_=_f8_87_bf_bf_bf____=_"?"__________________________| 0083_4.2.5__U-03FFFFFF_=_fc_83_bf_bf_bf_bf_=_"?"__________________________| 0084_4.3__Overlong_representation_of_the_NUL_character____________________| 0085_4.3.1__U+0000_=_c0_80_____________=_"?"______________________________| 0086_4.3.2__U+0000_=_e0_80_80__________=_"?"______________________________| 0087_4.3.3__U+0000_=_f0_80_80_80_______=_"?"______________________________| 0088_4.3.4__U+0000_=_f8_80_80_80_80____=_"?"______________________________| 0089_4.3.5__U+0000_=_fc_80_80_80_80_80_=_"?"______________________________| 0090_5__Illegal_code_positions____________________________________________| 0091_5.1_Single_UTF-16_surrogates_________________________________________| 0092_5.1.1__U+D800_=_ed_a0_80_=_"?"_______________________________________| 0093_5.1.2__U+DB7F_=_ed_ad_bf_=_"?"_______________________________________| 0094_5.1.3__U+DB80_=_ed_ae_80_=_"?"_______________________________________| 0095_5.1.4__U+DBFF_=_ed_af_bf_=_"?"_______________________________________| 0096_5.1.5__U+DC00_=_ed_b0_80_=_"?"_______________________________________| 0097_5.1.6__U+DF80_=_ed_be_80_=_"?"_______________________________________| 0098_5.1.7__U+DFFF_=_ed_bf_bf_=_"?"_______________________________________| 0099_5.2_Paired_UTF-16_surrogates_________________________________________| 0100_5.2.1__U+D800_U+DC00_=_ed_a0_80_ed_b0_80_=_"??"______________________| 0101_5.2.2__U+D800_U+DFFF_=_ed_a0_80_ed_bf_bf_=_"??"______________________| 0102_5.2.3__U+DB7F_U+DC00_=_ed_ad_bf_ed_b0_80_=_"??"______________________| 0103_5.2.4__U+DB7F_U+DFFF_=_ed_ad_bf_ed_bf_bf_=_"??"______________________| 0104_5.2.5__U+DB80_U+DC00_=_ed_ae_80_ed_b0_80_=_"??"______________________| 0105_5.2.6__U+DB80_U+DFFF_=_ed_ae_80_ed_bf_bf_=_"??"______________________| 0106_5.2.7__U+DBFF_U+DC00_=_ed_af_bf_ed_b0_80_=_"??"______________________| 0107_5.2.8__U+DBFF_U+DFFF_=_ed_af_bf_ed_bf_bf_=_"??"______________________| 0108_5.3_Other_illegal_code_positions_____________________________________| 0109_5.3.1__U+FFFE_=_ef_bf_be_=_"?"_______________________________________| 0110_5.3.2__U+FFFF_=_ef_bf_bf_=_"?"_______________________________________| ' "" "" # Currently fails on "0080_4.2.2__U-000007FF_=_e0_9f_bf" line test x"$CONFIG_UNICODE_SUPPORT" = x"y" \ && test x"$CONFIG_UNICODE_USING_LOCALE" != x"y" \ && test x"$CONFIG_SUBST_WCHAR" = x"63" \ && test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"0" \ && testing "ls unicode test with unlimited codepoints" \ "(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1 ls.testdir" \ '0001_1__Some_correct_UTF-8_text___________________________________________| 0002_2__Boundary_condition_test_cases_____________________________________| 0003_2.1__First_possible_sequence_of_a_certain_length_____________________| 0004_2.1.2__2_bytes__U-00000080_:________"?"______________________________| 0005_2.1.3__3_bytes__U-00000800_:________"à €"______________________________| 0006_2.1.4__4_bytes__U-00010000_:________"ð€€"______________________________| 0007_2.1.5__5_bytes__U-00200000_:________"?"______________________________| 0008_2.1.6__6_bytes__U-04000000_:________"?"______________________________| 0009_2.2__Last_possible_sequence_of_a_certain_length______________________| 0010_2.2.1__1_byte___U-0000007F_:________"?"______________________________| 0011_2.2.2__2_bytes__U-000007FF_:________"ß¿"______________________________| 0012_2.2.3__3_bytes__U-0000FFFF_:________"?"______________________________| 0013_2.2.4__4_bytes__U-001FFFFF_:________"?"______________________________| 0014_2.2.5__5_bytes__U-03FFFFFF_:________"?"______________________________| 0015_2.2.6__6_bytes__U-7FFFFFFF_:________"?"______________________________| 0016_2.3__Other_boundary_conditions_______________________________________| 0017_2.3.1__U-0000D7FF_=_ed_9f_bf_=_"퟿"___________________________________| 0018_2.3.2__U-0000E000_=_ee_80_80_=_"?"___________________________________| 0019_2.3.3__U-0000FFFD_=_ef_bf_bd_=_"�"___________________________________| 0020_2.3.4__U-0010FFFF_=_f4_8f_bf_bf_=_"?"________________________________| 0021_2.3.5__U-00110000_=_f4_90_80_80_=_"?"________________________________| 0022_3__Malformed_sequences_______________________________________________| 0023_3.1__Unexpected_continuation_bytes___________________________________| 0024_3.1.1__First_continuation_byte_0x80:_"?"_____________________________| 0025_3.1.2__Last__continuation_byte_0xbf:_"?"_____________________________| 0026_3.1.3__2_continuation_bytes:_"??"____________________________________| 0027_3.1.4__3_continuation_bytes:_"???"___________________________________| 0028_3.1.5__4_continuation_bytes:_"????"__________________________________| 0029_3.1.6__5_continuation_bytes:_"?????"_________________________________| 0030_3.1.7__6_continuation_bytes:_"??????"________________________________| 0031_3.1.8__7_continuation_bytes:_"???????"_______________________________| 0032_3.1.9__Sequence_of_all_64_possible_continuation_bytes__0x80-0xbf_:___| 0033____"????????????????_________________________________________________| 0034_____????????????????_________________________________________________| 0035_____????????????????_________________________________________________| 0036_____????????????????"________________________________________________| 0037_3.2__Lonely_start_characters_________________________________________| 0038_3.2.1__All_32_first_bytes_of_2-byte_sequences__0xc0-0xdf_,___________| 0039________each_followed_by_a_space_character:___________________________| 0040____"?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?__________________________________| 0041_____?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_"________________________________| 0042_3.2.2__All_16_first_bytes_of_3-byte_sequences__0xe0-0xef_,___________| 0043________each_followed_by_a_space_character:___________________________| 0044____"?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_"________________________________| 0045_3.2.3__All_8_first_bytes_of_4-byte_sequences__0xf0-0xf7_,____________| 0046________each_followed_by_a_space_character:___________________________| 0047____"?_?_?_?_?_?_?_?_"________________________________________________| 0048_3.2.4__All_4_first_bytes_of_5-byte_sequences__0xf8-0xfb_,____________| 0049________each_followed_by_a_space_character:___________________________| 0050____"?_?_?_?_"________________________________________________________| 0051_3.2.5__All_2_first_bytes_of_6-byte_sequences__0xfc-0xfd_,____________| 0052________each_followed_by_a_space_character:___________________________| 0053____"?_?_"____________________________________________________________| 0054_3.3__Sequences_with_last_continuation_byte_missing___________________| 0055_3.3.1__2-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______| 0056_3.3.2__3-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______| 0057_3.3.3__4-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______| 0058_3.3.4__5-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______| 0059_3.3.5__6-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______| 0060_3.3.6__2-byte_sequence_with_last_byte_missing__U-000007FF_:_"?"______| 0061_3.3.7__3-byte_sequence_with_last_byte_missing__U-0000FFFF_:_"?"______| 0062_3.3.8__4-byte_sequence_with_last_byte_missing__U-001FFFFF_:_"?"______| 0063_3.3.9__5-byte_sequence_with_last_byte_missing__U-03FFFFFF_:_"?"______| 0064_3.3.10_6-byte_sequence_with_last_byte_missing__U-7FFFFFFF_:_"?"______| 0065_3.4__Concatenation_of_incomplete_sequences___________________________| 0066____"??????????"______________________________________________________| 0067_3.5__Impossible_bytes________________________________________________| 0068_3.5.1__fe_=_"?"______________________________________________________| 0069_3.5.2__ff_=_"?"______________________________________________________| 0070_3.5.3__fe_fe_ff_ff_=_"????"__________________________________________| 0071_4__Overlong_sequences________________________________________________| 0072_4.1__Examples_of_an_overlong_ASCII_character_________________________| 0073_4.1.1_U+002F_=_c0_af_____________=_"?"_______________________________| 0074_4.1.2_U+002F_=_e0_80_af__________=_"?"_______________________________| 0075_4.1.3_U+002F_=_f0_80_80_af_______=_"?"_______________________________| 0076_4.1.4_U+002F_=_f8_80_80_80_af____=_"?"_______________________________| 0077_4.1.5_U+002F_=_fc_80_80_80_80_af_=_"?"_______________________________| 0078_4.2__Maximum_overlong_sequences______________________________________| 0079_4.2.1__U-0000007F_=_c1_bf_____________=_"?"__________________________| 0080_4.2.2__U-000007FF_=_e0_9f_bf__________=_"?"__________________________| 0081_4.2.3__U-0000FFFF_=_f0_8f_bf_bf_______=_"?"__________________________| 0082_4.2.4__U-001FFFFF_=_f8_87_bf_bf_bf____=_"?"__________________________| 0083_4.2.5__U-03FFFFFF_=_fc_83_bf_bf_bf_bf_=_"?"__________________________| 0084_4.3__Overlong_representation_of_the_NUL_character____________________| 0085_4.3.1__U+0000_=_c0_80_____________=_"?"______________________________| 0086_4.3.2__U+0000_=_e0_80_80__________=_"?"______________________________| 0087_4.3.3__U+0000_=_f0_80_80_80_______=_"?"______________________________| 0088_4.3.4__U+0000_=_f8_80_80_80_80____=_"?"______________________________| 0089_4.3.5__U+0000_=_fc_80_80_80_80_80_=_"?"______________________________| 0090_5__Illegal_code_positions____________________________________________| 0091_5.1_Single_UTF-16_surrogates_________________________________________| 0092_5.1.1__U+D800_=_ed_a0_80_=_"?"_______________________________________| 0093_5.1.2__U+DB7F_=_ed_ad_bf_=_"?"_______________________________________| 0094_5.1.3__U+DB80_=_ed_ae_80_=_"?"_______________________________________| 0095_5.1.4__U+DBFF_=_ed_af_bf_=_"?"_______________________________________| 0096_5.1.5__U+DC00_=_ed_b0_80_=_"?"_______________________________________| 0097_5.1.6__U+DF80_=_ed_be_80_=_"?"_______________________________________| 0098_5.1.7__U+DFFF_=_ed_bf_bf_=_"?"_______________________________________| 0099_5.2_Paired_UTF-16_surrogates_________________________________________| 0100_5.2.1__U+D800_U+DC00_=_ed_a0_80_ed_b0_80_=_"??"______________________| 0101_5.2.2__U+D800_U+DFFF_=_ed_a0_80_ed_bf_bf_=_"??"______________________| 0102_5.2.3__U+DB7F_U+DC00_=_ed_ad_bf_ed_b0_80_=_"??"______________________| 0103_5.2.4__U+DB7F_U+DFFF_=_ed_ad_bf_ed_bf_bf_=_"??"______________________| 0104_5.2.5__U+DB80_U+DC00_=_ed_ae_80_ed_b0_80_=_"??"______________________| 0105_5.2.6__U+DB80_U+DFFF_=_ed_ae_80_ed_bf_bf_=_"??"______________________| 0106_5.2.7__U+DBFF_U+DC00_=_ed_af_bf_ed_b0_80_=_"??"______________________| 0107_5.2.8__U+DBFF_U+DFFF_=_ed_af_bf_ed_bf_bf_=_"??"______________________| 0108_5.3_Other_illegal_code_positions_____________________________________| 0109_5.3.1__U+FFFE_=_ef_bf_be_=_"?"_______________________________________| 0110_5.3.2__U+FFFF_=_ef_bf_bf_=_"?"_______________________________________| ' "" "" rm -rf ls.testdir 2>/dev/null mkdir ls.testdir || exit 1 # testing "test name" "command" "expected result" "file input" "stdin" test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \ && testing "ls symlink_to_dir" \ "touch ls.testdir/A ls.testdir/B; ln -s ls.testdir ls.link; ls ls.link; ls -1 ls.link/; ls -1 ls.link; rm -f ls.link" \ "A\nB\nA\nB\nA\nB\n" \ "" "" # Clean up rm -rf ls.testdir 2>/dev/null exit $FAILCOUNT ������������������������������������������������������������������busybox-1.22.1/testsuite/cut/�����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014612� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cut/cut-cuts-an-unclosed-range���������������������������������������������0000644�0000000�0000000�00000000053�12263563520�021606� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test $(echo abcd | busybox cut -c 3-) = cd �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cut/cut-cuts-an-open-range�������������������������������������������������0000644�0000000�0000000�00000000054�12263563520�020734� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test $(echo abcd | busybox cut -c -3) = abc ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cut/cut-cuts-a-character���������������������������������������������������0000644�0000000�0000000�00000000051�12263563520�020454� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test $(echo abcd | busybox cut -c 3) = c ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cut/cut-cuts-a-field�������������������������������������������������������0000644�0000000�0000000�00000000066�12263563520�017611� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test $($ECHO -e "f1\tf2\tf3" | busybox cut -f 2) = f2 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cut/cut-cuts-a-closed-range������������������������������������������������0000644�0000000�0000000�00000000054�12263563520�021066� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test $(echo abcd | busybox cut -c 1-2) = ab ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/md5sum.tests���������������������������������������������������������������0000755�0000000�0000000�00000002023�12263563520�016321� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Used by {ms5,shaN}sum # We pipe texts 0...999 bytes long, {md5,shaN}sum them, # then {md5,shaN}sum the resulting list. # Then we compare the result with expected result. # # Here are the expected results: # efe30c482e0b687e0cca0612f42ca29b # d41337e834377140ae7f98460d71d908598ef04f # 8e1d3ed57ebc130f0f72508446559eeae06451ae6d61b1e8ce46370cfb8963c3 # fe413e0f177324d1353893ca0772ceba83fd41512ba63895a0eebb703ef9feac2fb4e92b2cb430b3bda41b46b0cb4ea8307190a5cc795157cfb680a9cd635d0f if ! test "$1"; then set -- md5sum efe30c482e0b687e0cca0612f42ca29b fi sum="$1" expected="$2" test -f "$bindir/.config" && . "$bindir/.config" test x"$CONFIG_FEATURE_FANCY_HEAD" != x"y" \ && { echo "SKIPPED: $sum"; exit 0; } text="The quick brown fox jumps over the lazy dog" text=`yes "$text" | head -c 9999` result=`( n=0 while test $n -le 999; do echo "$text" | head -c $n | "$sum" n=$(($n+1)) done | "$sum" )` if test x"$result" = x"$expected -"; then echo "PASS: $sum" exit 0 fi echo "FAIL: $sum (r:$result exp:$expected)" exit 1 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mdev.tests�����������������������������������������������������������������0000755�0000000�0000000�00000020370�12263563520�016047� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2008 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # ls -ln is showing date. Need to remove that, it's variable # sed: (1) "maj, min" -> "maj,min" (2) coalesce spaces # cut: remove date FILTER_LS="grep -v '^total ' | sed -e 's/, */,/g' -e 's/ */ /g' | cut -d' ' -f 1-5,9-" # cut: remove size+date FILTER_LS2="grep -v '^total ' | sed -e 's/, */,/g' -e 's/ */ /g' | cut -d' ' -f 1-4,9-" # testing "test name" "commands" "expected result" "file input" "stdin" rm -rf mdev.testdir mkdir mdev.testdir # We need mdev executable to be in chroot jail! # (will still fail with dynamically linked one, though...) cp ../busybox mdev.testdir/mdev mkdir mdev.testdir/bin cp ../busybox mdev.testdir/bin/sh 2>/dev/null # for testing cmd feature mkdir mdev.testdir/etc mkdir mdev.testdir/dev mkdir -p mdev.testdir/sys/block/sda echo "8:0" >mdev.testdir/sys/block/sda/dev # env - PATH=$PATH: on some systems chroot binary won't otherwise be found optional STATIC FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME testing "mdev add /block/sda" \ "env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1; ls -ln mdev.testdir/dev | $FILTER_LS" \ "\ brw-rw---- 1 0 0 8,0 sda " \ "" "" SKIP= # continuing to use directory structure from prev test optional STATIC FEATURE_MDEV_CONF FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME testing "mdev deletes /block/sda" \ "env - PATH=$PATH ACTION=remove DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1; ls -ln mdev.testdir/dev | $FILTER_LS" \ "\ " \ "" "" SKIP= # continuing to use directory structure from prev test rm -rf mdev.testdir/dev/* echo ".* 1:1 666" >mdev.testdir/etc/mdev.conf echo "sda 2:2 444" >>mdev.testdir/etc/mdev.conf optional STATIC FEATURE_MDEV_CONF FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME testing "mdev stops on first rule" \ "env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1; ls -ln mdev.testdir/dev | $FILTER_LS" \ "\ brw-rw-rw- 1 1 1 8,0 sda " \ "" "" SKIP= # continuing to use directory structure from prev test rm -rf mdev.testdir/dev/* echo "-.* 1:1 666" >mdev.testdir/etc/mdev.conf echo "sda 2:2 444" >>mdev.testdir/etc/mdev.conf optional STATIC FEATURE_MDEV_CONF FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME testing "mdev does not stop on dash-rule" \ "env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1; ls -ln mdev.testdir/dev | $FILTER_LS" \ "\ br--r--r-- 1 2 2 8,0 sda " \ "" "" SKIP= # continuing to use directory structure from prev test rm -rf mdev.testdir/dev/* echo "\$MODALIAS=qw 1:1 666" >mdev.testdir/etc/mdev.conf echo "\$MODALIAS=qw. 2:2 444" >>mdev.testdir/etc/mdev.conf echo "\$MODALIAS=qw. 3:3 400" >>mdev.testdir/etc/mdev.conf optional STATIC FEATURE_MDEV_CONF FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME testing "mdev \$ENVVAR=regex match" \ "env - PATH=$PATH ACTION=add DEVPATH=/block/sda MODALIAS=qwe chroot mdev.testdir /mdev 2>&1; ls -ln mdev.testdir/dev | $FILTER_LS" \ "\ br--r--r-- 1 2 2 8,0 sda " \ "" "" SKIP= # continuing to use directory structure from prev test rm -rf mdev.testdir/dev/* echo "sda 0:0 444 >disk/scsiA" >mdev.testdir/etc/mdev.conf optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_RENAME FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME testing "mdev move/symlink rule '>bar/baz'" \ "env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1; ls -lnR mdev.testdir/dev | $FILTER_LS2" \ "\ mdev.testdir/dev: drwxr-xr-x 2 0 0 disk lrwxrwxrwx 1 0 0 sda -> disk/scsiA mdev.testdir/dev/disk: br--r--r-- 1 0 0 scsiA " \ "" "" SKIP= # continuing to use directory structure from prev test rm -rf mdev.testdir/dev/* echo "sda 0:0 444 >disk/" >mdev.testdir/etc/mdev.conf optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_RENAME FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME testing "mdev move/symlink rule '>bar/'" \ "env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1; ls -lnR mdev.testdir/dev | $FILTER_LS2" \ "\ mdev.testdir/dev: drwxr-xr-x 2 0 0 disk lrwxrwxrwx 1 0 0 sda -> disk/sda mdev.testdir/dev/disk: br--r--r-- 1 0 0 sda " \ "" "" SKIP= # continuing to use directory structure from prev test rm -rf mdev.testdir/dev/* echo "sda 0:0 444 =disk/sd/a" >mdev.testdir/etc/mdev.conf optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_RENAME FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME testing "mdev move rule '=bar/baz/fname'" \ "env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1; ls -lnR mdev.testdir/dev | $FILTER_LS2" \ "\ mdev.testdir/dev: drwxr-xr-x 3 0 0 disk mdev.testdir/dev/disk: drwxr-xr-x 2 0 0 sd mdev.testdir/dev/disk/sd: br--r--r-- 1 0 0 a " \ "" "" SKIP= # continuing to use directory structure from prev test rm -rf mdev.testdir/dev/* # here we complicate things by having non-matching group 1 and using %0 echo "s([0-9])*d([a-z]+) 0:0 644 >sd/%2_%0" >mdev.testdir/etc/mdev.conf optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_RENAME FEATURE_MDEV_RENAME_REGEXP FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME FEATURE_LS_SORTFILES testing "mdev regexp substring match + replace" \ "env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1; ls -lnR mdev.testdir/dev | $FILTER_LS2" \ "\ mdev.testdir/dev: drwxr-xr-x 2 0 0 sd lrwxrwxrwx 1 0 0 sda -> sd/a_sda mdev.testdir/dev/sd: brw-r--r-- 1 0 0 a_sda " \ "" "" SKIP= # continuing to use directory structure from prev test rm -rf mdev.testdir/dev/* echo "sda 0:0 644 @echo @echo TEST" >mdev.testdir/etc/mdev.conf optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_EXEC FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME FEATURE_SH_IS_ASH testing "mdev command" \ "env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1; ls -lnR mdev.testdir/dev | $FILTER_LS" \ "\ @echo TEST mdev.testdir/dev: brw-r--r-- 1 0 0 8,0 sda " \ "" "" SKIP= # continuing to use directory structure from prev test rm -rf mdev.testdir/dev/* echo "sda 0:0 644 =block/ @echo @echo TEST:\$MDEV" >mdev.testdir/etc/mdev.conf optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_RENAME FEATURE_MDEV_EXEC FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME FEATURE_SH_IS_ASH testing "mdev move and command" \ "env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1; ls -lnR mdev.testdir/dev | $FILTER_LS2" \ "\ @echo TEST:block/sda mdev.testdir/dev: drwxr-xr-x 2 0 0 block mdev.testdir/dev/block: brw-r--r-- 1 0 0 sda " \ "" "" SKIP= # continuing to use directory structure from prev test rm -rf mdev.testdir/dev/* echo "@8,0 0:1 644" >mdev.testdir/etc/mdev.conf optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_RENAME FEATURE_MDEV_RENAME_REGEXP FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME testing "mdev #maj,min and no explicit uid" \ "env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1; ls -lnR mdev.testdir/dev | $FILTER_LS" \ "\ mdev.testdir/dev: brw-r--r-- 1 0 1 8,0 sda " \ "" "" SKIP= # continuing to use directory structure from prev test rm -rf mdev.testdir/dev/* mkdir -p mdev.testdir/sys/class/tty/capi echo "191:0" >mdev.testdir/sys/class/tty/capi/dev mkdir -p mdev.testdir/sys/class/tty/capi1 echo "191:1" >mdev.testdir/sys/class/tty/capi1/dev mkdir -p mdev.testdir/sys/class/tty/capi20 echo "191:20" >mdev.testdir/sys/class/tty/capi20/dev echo "capi 0:0 0660 =capi20" >mdev.testdir/etc/mdev.conf echo "capi([0-9]) 0:0 0660 =capi20.0%1" >>mdev.testdir/etc/mdev.conf echo "capi([0-9]*) 0:0 0660 =capi20.%1" >>mdev.testdir/etc/mdev.conf # mdev invocation with DEVPATH=/class/tty/capi20 was deleting /dev/capi20 optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_RENAME FEATURE_MDEV_RENAME_REGEXP FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME FEATURE_LS_SORTFILES testing "move rule does not delete node with name == device_name" \ "\ env - PATH=$PATH ACTION=add DEVPATH=/class/tty/capi chroot mdev.testdir /mdev 2>&1; env - PATH=$PATH ACTION=add DEVPATH=/class/tty/capi1 chroot mdev.testdir /mdev 2>&1; env - PATH=$PATH ACTION=add DEVPATH=/class/tty/capi20 chroot mdev.testdir /mdev 2>&1; ls -lnR mdev.testdir/dev | $FILTER_LS" \ "\ mdev.testdir/dev: crw-rw---- 1 0 0 191,0 capi20 crw-rw---- 1 0 0 191,1 capi20.01 crw-rw---- 1 0 0 191,20 capi20.20 " \ "" "" SKIP= # clean up rm -rf mdev.testdir exit $FAILCOUNT ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cat/�����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014566� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cat/cat-prints-a-file������������������������������������������������������0000644�0000000�0000000�00000000063�12263563520�017733� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo I WANT > foo busybox cat foo >bar cmp foo bar �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/cat/cat-prints-a-file-and-standard-input�����������������������������������0000644�0000000�0000000�00000000152�12263563520�023425� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo I WANT > foo echo SOMETHING | busybox cat foo - >bar cat >baz <<EOF I WANT SOMETHING EOF cmp bar baz ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/wget/����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014765� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/wget/wget-retrieves-google-index�������������������������������������������0000644�0000000�0000000�00000000147�12263563520�022251� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x"$SKIP_INTERNET_TESTS" != x"" && exit busybox wget -q -O foo http://www.google.com/ test -s foo �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/wget/wget--O-overrides--P��������������������������������������������������0000644�0000000�0000000�00000000206�12263563520�020403� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x"$SKIP_INTERNET_TESTS" != x"" && exit mkdir foo busybox wget -q -O index.html -P foo http://www.google.com/ test -s index.html ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/wget/wget-supports--P������������������������������������������������������0000644�0000000�0000000�00000000174�12263563520�020033� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x"$SKIP_INTERNET_TESTS" != x"" && exit mkdir foo busybox wget -q -P foo http://www.google.com/ test -s foo/index.html ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/wget/wget-handles-empty-path�����������������������������������������������0000644�0000000�0000000�00000000120�12263563520�021355� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x"$SKIP_INTERNET_TESTS" != x"" && exit busybox wget http://www.google.com ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mount.testroot�������������������������������������������������������������0000755�0000000�0000000�00000012307�12263563520�017000� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # SUSv3 compliant mount and umount tests. # Copyright 2005 by Rob Landley <rob@landley.net> # Licensed under GPLv2, see file LICENSE in this source tree. if [ -z "$TESTDIR" ] then echo 'Need $TESTDIR' exit 1 fi cd "$TESTDIR" . testing.sh # If we aren't PID 1, barf. #if [ $$ -ne 1 ] #then # echo "SKIPPED: mount test requires emulation environment" # exit 0 #fi # Run tests within the chroot environment. dochroot bash rm ls ln cat ps mknod mkdir dd grep cmp diff tail \ mkfs.ext2 mkfs.vfat mount umount losetup wc << EOF #!/bin/bash . /testing.sh mknod /dev/loop0 b 7 0 mknod /dev/loop1 b 7 1 # We need /proc to do much. Make sure we can mount it explicitly. testing "mount no proc [GNUFAIL]" "mount 2> /dev/null || echo yes" "yes\n" "" "" testing "mount /proc" "mount -t proc /proc /proc && ls -d /proc/1" \ "/proc/1\n" "" "" # Make sure the last thing in the list is /proc testing "mount list1" "mount | tail -n 1" "/proc on /proc type proc (rw)\n" \ "" "" # Create an ext2 image mkdir -p images/{ext2.dir,vfat.dir,test1,test2,test3} dd if=/dev/zero of=images/ext2.img bs=1M count=1 2> /dev/null mkfs.ext2 -F -b 1024 images/ext2.img > /dev/null 2> /dev/null dd if=/dev/zero of=images/vfat.img bs=1M count=1 2> /dev/null mkfs.vfat images/vfat.img > /dev/null # Test mount it testing "mount vfat image (explicitly autodetect type)" \ "mount -t auto images/vfat.img images/vfat.dir && mount | tail -n 1 | grep -o 'vfat.dir type vfat'" \ "vfat.dir type vfat\n" "" "" testing "mount ext2 image (autodetect type)" \ "mount images/ext2.img images/ext2.dir 2> /dev/null && mount | tail -n 1" \ "/dev/loop1 on /images/ext2.dir type ext2 (rw)\n" "" "" testing "mount remount ext2 image noatime" \ "mount -o remount,noatime images/ext2.dir && mount | tail -n 1" \ "/dev/loop1 on /images/ext2.dir type ext2 (rw,noatime)\n" "" "" testing "mount remount ext2 image ro remembers noatime" \ "mount -o remount,ro images/ext2.dir && mount | tail -n 1" \ "/dev/loop1 on /images/ext2.dir type ext2 (ro,noatime)\n" "" "" umount -d images/vfat.dir umount -d images/ext2.dir testing "mount umount freed loop device" \ "mount images/ext2.img images/ext2.dir && mount | tail -n 1" \ "/dev/loop0 on /images/ext2.dir type ext2 (rw)\n" "" "" testing "mount block device" \ "mount -t ext2 /dev/loop0 images/test1 && mount | tail -n 1" \ "/dev/loop0 on /images/test1 type ext2 (rw)\n" "" "" umount -d images/ext2.dir images/test1 testing "mount remount nonexistent directory" \ "mount -o remount,noatime images/ext2.dir 2> /dev/null || echo yes" \ "yes\n" "" "" # Fun with mount -a testing "mount -a no fstab" "mount -a 2>/dev/null || echo yes" "yes\n" "" "" umount /proc # The first field is space delimited, the rest tabs. cat > /etc/fstab << FSTAB /proc /proc proc defaults 0 0 # Autodetect loop, and provide flags with commas in them. /images/ext2.img /images/ext2.dir ext2 noatime,nodev 0 0 # autodetect filesystem, flags without commas. /images/vfat.img /images/vfat.dir auto ro 0 0 # A block device /dev/loop2 /images/test1 auto defaults 0 0 # tmpfs, filesystem specific flag. walrus /images/test2 tmpfs size=42 0 0 # Autodetect a bind mount. /images/test2 /images/test3 auto defaults 0 0 FSTAB # Put something on loop2. mknod /dev/loop2 b 7 2 cat images/ext2.img > images/ext2-2.img losetup /dev/loop2 images/ext2-2.img testing "mount -a" "mount -a && echo hello > /images/test2/abc && cat /images/test3/abc && (mount | wc -l)" "hello\n8\n" "" "" testing "umount -a" "umount -a && ls /proc" "" "" "" #/bin/bash < /dev/tty > /dev/tty 2> /dev/tty mknod /dev/console c 5 1 /bin/bash < /dev/console > /dev/console 2> /dev/console EOF exit 0 # Run some tests losetup nonexistent device (should return error 2) losetup unbound loop device (should return error 1) losetup bind file to loop device losetup bound loop device (display) (should return error 0) losetup filename (error) losetup nofile (file not found) losetup -d losetup bind with offset losetup -f (print first loop device) losetup -f filename (associate file with first loop device) losetup -o (past end of file) -f filename mount -a with multiple entries in fstab with duplicate entries in fstab with relative paths in fstab with user entries in fstab mount -o async,sync,atime,noatime,dev,nodev,exec,noexec,loop,suid,nosuid,remount,ro,rw,bind,move mount -r mount -o rw -r mount -w -o ro mount -t auto mount with relative path in fstab mount block device mount char device mount file (autoloop) mount directory (autobind) testing "umount with no /proc" testing "umount curdir" # The basic tests. These should work even with the small busybox. testing "sort" "input" "a\nb\nc\n" "c\na\nb\n" "" testing "sort #2" "input" "010\n1\n3\n" "3\n1\n010\n" "" testing "sort stdin" "" "a\nb\nc\n" "" "b\na\nc\n" testing "sort numeric" "-n input" "1\n3\n010\n" "3\n1\n010\n" "" testing "sort reverse" "-r input" "wook\nwalrus\npoint\npabst\naargh\n" \ "point\nwook\npabst\naargh\nwalrus\n" "" optional FEATURE_MOUNT_LOOP testing "umount -D" optional FEATURE_MTAB_SUPPORT optional FEATURE_MOUNT_NFS # No idea what to test here. optional UMOUNT optional FEATURE_UMOUNT_ALL testing "umount -a" testing "umount -r" testing "umount -l" testing "umount -f" exit $FAILCOUNT �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/seq.tests������������������������������������������������������������������0000755�0000000�0000000�00000004235�12263563520�015706� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # SUSv3 compliant seq tests. # Copyright 2006 by Rob Landley <rob@landley.net> # Licensed under GPLv2, see file LICENSE in this source tree. # AUDIT: Full SUSv3 coverage (except internationalization). . ./testing.sh # testing "test name" "options" "expected result" "file input" "stdin" # file input will be file called "input" # test can create a file "actual" instead of writing to stdout # Test exit status testing "seq (exit with error)" "seq 2> /dev/null || echo yes" "yes\n" "" "" testing "seq (exit with error)" "seq 1 2 3 4 2> /dev/null || echo yes" \ "yes\n" "" "" testing "seq one argument" "seq 3" "1\n2\n3\n" "" "" testing "seq two arguments" "seq 5 7" "5\n6\n7\n" "" "" testing "seq two arguments reversed" "seq 7 5" "" "" "" testing "seq two arguments equal" "seq 3 3" "3\n" "" "" testing "seq two arguments equal, arbitrary negative step" "seq 1 -15 1" \ "1\n" "" "" testing "seq two arguments equal, arbitrary positive step" "seq 1 +15 1" \ "1\n" "" "" testing "seq count up by 2" "seq 4 2 8" "4\n6\n8\n" "" "" testing "seq count down by 2" "seq 8 -2 4" "8\n6\n4\n" "" "" testing "seq count wrong way #1" "seq 4 -2 8" "" "" "" testing "seq count wrong way #2" "seq 8 2 4" "" "" "" testing "seq count by .3" "seq 3 .3 4" "3.0\n3.3\n3.6\n3.9\n" "" "" testing "seq count by .30" "seq 3 .30 4" "3.00\n3.30\n3.60\n3.90\n" "" "" testing "seq count by .30 to 4.000" "seq 3 .30 4.000" "3.00\n3.30\n3.60\n3.90\n" "" "" testing "seq count by -.9" "seq .7 -.9 -2.2" "0.7\n-0.2\n-1.1\n-2.0\n" "" "" testing "seq count by zero" "seq 4 0 8 | head -n 10" "4\n4\n4\n4\n4\n4\n4\n4\n4\n4\n" "" "" testing "seq one argument with padding" "seq -w 003" "001\n002\n003\n" "" "" testing "seq two arguments with padding" "seq -w 005 7" "005\n006\n007\n" "" "" testing "seq count down by 3 with padding" "seq -w 8 -3 04" "08\n05\n" "" "" # Looks like a bug in coreutils 6.10: it uses width one less than needed # These tests contain the expected "fixed" output testing "seq count by .3 with padding 1" "seq -w 09 .3 11" "09.0\n09.3\n09.6\n09.9\n10.2\n10.5\n10.8\n" "" "" testing "seq count by .3 with padding 2" "seq -w 03 .3 0004" "0003.0\n0003.3\n0003.6\n0003.9\n" "" "" exit $FAILCOUNT �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/sort.tests�����������������������������������������������������������������0000755�0000000�0000000�00000005024�12267106022�016074� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # SUSv3 compliant sort tests. # Copyright 2005 by Rob Landley <rob@landley.net> # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # The basic tests. These should work even with the small busybox. testing "sort" "sort input" "a\nb\nc\n" "c\na\nb\n" "" testing "sort #2" "sort input" "010\n1\n3\n" "3\n1\n010\n" "" testing "sort stdin" "sort" "a\nb\nc\n" "" "b\na\nc\n" testing "sort numeric" "sort -n input" "1\n3\n010\n" "3\n1\n010\n" "" testing "sort reverse" "sort -r input" "wook\nwalrus\npoint\npabst\naargh\n" \ "point\nwook\npabst\naargh\nwalrus\n" "" # These tests require the full option set. optional FEATURE_SORT_BIG # Longish chunk of data re-used by the next few tests data="42 1 3 woot 42 1 010 zoology egg 1 2 papyrus 7 3 42 soup 999 3 0 algebra " # testing "description" "command(s)" "result" "infile" "stdin" # Sorting with keys testing "sort one key" "sort -k4,4 input" \ "999 3 0 algebra egg 1 2 papyrus 7 3 42 soup 42 1 3 woot 42 1 010 zoology " "$data" "" testing "sort key range with numeric option" "sort -k2,3n input" \ "42 1 010 zoology 42 1 3 woot egg 1 2 papyrus 7 3 42 soup 999 3 0 algebra " "$data" "" test x"$SKIP_KNOWN_BUGS" = x"" && { # Busybox is definitely doing these wrong. FIXME testing "sort key range with numeric option and global reverse" \ "sort -k2,3n -r input" \ "egg 1 2 papyrus 42 1 3 woot 42 1 010 zoology 999 3 0 algebra 7 3 42 soup " "$data" "" testing "sort key range with multiple options" "sort -k2,3rn input" \ "7 3 42 soup 999 3 0 algebra 42 1 010 zoology 42 1 3 woot egg 1 2 papyrus " "$data" "" } testing "sort key range with two -k options" "sort -k 2,2n -k 1,1r input" "\ d 2 b 2 c 3 " "\ c 3 b 2 d 2 " "" testing "sort with non-default leading delim 1" "sort -n -k2 -t/ input" "\ /a/2 /b/1 " "\ /a/2 /b/1 " "" testing "sort with non-default leading delim 2" "sort -n -k3 -t/ input" "\ /b/1 /a/2 " "\ /b/1 /a/2 " "" testing "sort with non-default leading delim 3" "sort -n -k3 -t/ input" "\ //a/2 //b/1 " "\ //a/2 //b/1 " "" testing "sort -u should consider field only when discarding" "sort -u -k2 input" "\ a c " "\ a c b c " "" testing "sort -z outputs NUL terminated lines" "sort -z input" "\ one\0three\0two\0\ " "\ one\0two\0three\0\ " "" testing "sort key doesn't strip leading blanks, disables fallback global sort" \ "sort -n -k2 -t ' '" " a \n 1 \n 2 \n" "" " 2 \n 1 \n a \n" testing "sort file in place" \ "sort -o input input && cat input" "\ 111 222 " "\ 222 111 " "" # testing "description" "command(s)" "result" "infile" "stdin" exit $FAILCOUNT ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/dirname/�������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�015436� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/dirname/dirname-handles-multiple-slashes�����������������������������������0000644�0000000�0000000�00000000060�12263563520�023705� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test $(busybox dirname foo/bar///baz) = foo/bar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/dirname/dirname-handles-empty-path�����������������������������������������0000644�0000000�0000000�00000000037�12263563520�022506� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test $(busybox dirname '') = . �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/dirname/dirname-handles-root�����������������������������������������������0000644�0000000�0000000�00000000036�12263563520�021400� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test $(busybox dirname /) = / ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/dirname/dirname-handles-relative-path��������������������������������������0000644�0000000�0000000�00000000056�12263563520�023164� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test $(busybox dirname foo/bar/baz) = foo/bar ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/dirname/dirname-works������������������������������������������������������0000644�0000000�0000000�00000000065�12263563520�020150� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test x$(dirname $(pwd)) = x$(busybox dirname $(pwd)) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/dirname/dirname-handles-single-component�����������������������������������0000644�0000000�0000000�00000000040�12263563520�023671� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test $(busybox dirname foo) = . ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/dirname/dirname-handles-absolute-path��������������������������������������0000644�0000000�0000000�00000000060�12263563520�023162� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test $(busybox dirname /foo/bar/baz) = /foo/bar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/sha3sum.tests��������������������������������������������������������������0000755�0000000�0000000�00000000245�12263563520�016476� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh . ./md5sum.tests sha3sum c29d77bc548fa2b20a04c861400a5360879c52156e2a54a3415b99a9a3123e1d5f36714a24eca8c1f05a8e2d8ba859c930d41141f64a255c6794436fc99c486a �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/unzip.tests����������������������������������������������������������������0000755�0000000�0000000�00000001430�12263563520�016255� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Tests for unzip. # Copyright 2006 Rob Landley <rob@landley.net> # Copyright 2006 Glenn McGrath # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "test name" "options" "expected result" "file input" "stdin" # file input will be file called "input" # test can create a file "actual" instead of writing to stdout # Create a scratch directory mkdir temp cd temp # Create test file to work with. mkdir foo touch foo/bar zip foo.zip foo foo/bar > /dev/null rm -f foo/bar rmdir foo # Test that unzipping just foo doesn't create bar. testing "unzip (subdir only)" "unzip -q foo.zip foo/ && test -d foo && test ! -f foo/bar && echo yes" "yes\n" "" "" rmdir foo rm foo.zip # Clean up scratch directory. cd .. rm -rf temp exit $FAILCOUNT ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/dd/������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014406� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/dd/dd-prints-count-to-standard-error���������������������������������������0000644�0000000�0000000�00000000105�12263563520�022730� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo I WANT | busybox dd of=foo >/dev/null 2>bar grep -q records bar �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/dd/dd-copies-from-standard-input-to-standard-output������������������������0000644�0000000�0000000�00000000072�12263563520�025653� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test "$(echo I WANT | busybox dd 2>/dev/null)" = "I WANT" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/dd/dd-reports-write-errors�������������������������������������������������0000644�0000000�0000000�00000000112�12263563520�021054� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox dd if="$0" of=/dev/full 2>/dev/null || status=$? test $status = 1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/dd/dd-accepts-if�����������������������������������������������������������0000644�0000000�0000000�00000000104�12263563520�016733� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo I WANT >foo test "$(busybox dd if=foo 2>/dev/null)" = "I WANT" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/dd/dd-accepts-of�����������������������������������������������������������0000644�0000000�0000000�00000000104�12263563520�016741� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo I WANT | busybox dd of=foo 2>/dev/null echo I WANT | cmp foo - ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/makedevs.tests�������������������������������������������������������������0000755�0000000�0000000�00000014366�12263563520�016723� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2008 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh test x"`id -u`" = x"0" || { echo "SKIPPED: makedevs (must be root to test this)" exit 0 } unset LANG unset LC_COLLATE unset LC_ALL # ls -ln is showing date. Need to remove that, it's variable # sed: (1) "maj, min" -> "maj,min" (2) coalesce spaces # cut: remove date FILTER_LS="sed -e 's/, */,/g' -e 's/ */ /g' | cut -d' ' -f 1-5,9-" # cut: remove size+date FILTER_LS2="sed -e 's/, */,/g' -e 's/ */ /g' | cut -d' ' -f 1-4,9-" # testing "test name" "options" "expected result" "file input" "stdin" rm -rf makedevs.testdir mkdir makedevs.testdir optional FEATURE_MAKEDEVS_TABLE FEATURE_FIND_NOT FEATURE_FIND_TYPE FEATURE_LS_RECURSIVE FEATURE_LS_SORTFILES testing "makedevs -d ../makedevs.device_table.txt ." \ "(cd makedevs.testdir && makedevs -d ../makedevs.device_table.txt . 2>&1); find makedevs.testdir ! -type d | sort | xargs ls -lnR | $FILTER_LS" \ "\ rootdir=. table='../makedevs.device_table.txt' crw-rw-rw- 1 0 0 5,1 makedevs.testdir/dev/console crw-r----- 1 0 5 29,0 makedevs.testdir/dev/fb0 crw-r----- 1 0 5 29,32 makedevs.testdir/dev/fb1 crw-r----- 1 0 5 29,64 makedevs.testdir/dev/fb2 crw-r----- 1 0 5 29,96 makedevs.testdir/dev/fb3 brw-r----- 1 0 0 3,0 makedevs.testdir/dev/hda brw-r----- 1 0 0 3,1 makedevs.testdir/dev/hda1 brw-r----- 1 0 0 3,10 makedevs.testdir/dev/hda10 brw-r----- 1 0 0 3,11 makedevs.testdir/dev/hda11 brw-r----- 1 0 0 3,12 makedevs.testdir/dev/hda12 brw-r----- 1 0 0 3,13 makedevs.testdir/dev/hda13 brw-r----- 1 0 0 3,14 makedevs.testdir/dev/hda14 brw-r----- 1 0 0 3,15 makedevs.testdir/dev/hda15 brw-r----- 1 0 0 3,2 makedevs.testdir/dev/hda2 brw-r----- 1 0 0 3,3 makedevs.testdir/dev/hda3 brw-r----- 1 0 0 3,4 makedevs.testdir/dev/hda4 brw-r----- 1 0 0 3,5 makedevs.testdir/dev/hda5 brw-r----- 1 0 0 3,6 makedevs.testdir/dev/hda6 brw-r----- 1 0 0 3,7 makedevs.testdir/dev/hda7 brw-r----- 1 0 0 3,8 makedevs.testdir/dev/hda8 brw-r----- 1 0 0 3,9 makedevs.testdir/dev/hda9 brw-r----- 1 0 0 3,64 makedevs.testdir/dev/hdb brw-r----- 1 0 0 3,65 makedevs.testdir/dev/hdb1 brw-r----- 1 0 0 3,74 makedevs.testdir/dev/hdb10 brw-r----- 1 0 0 3,75 makedevs.testdir/dev/hdb11 brw-r----- 1 0 0 3,76 makedevs.testdir/dev/hdb12 brw-r----- 1 0 0 3,77 makedevs.testdir/dev/hdb13 brw-r----- 1 0 0 3,78 makedevs.testdir/dev/hdb14 brw-r----- 1 0 0 3,79 makedevs.testdir/dev/hdb15 brw-r----- 1 0 0 3,66 makedevs.testdir/dev/hdb2 brw-r----- 1 0 0 3,67 makedevs.testdir/dev/hdb3 brw-r----- 1 0 0 3,68 makedevs.testdir/dev/hdb4 brw-r----- 1 0 0 3,69 makedevs.testdir/dev/hdb5 brw-r----- 1 0 0 3,70 makedevs.testdir/dev/hdb6 brw-r----- 1 0 0 3,71 makedevs.testdir/dev/hdb7 brw-r----- 1 0 0 3,72 makedevs.testdir/dev/hdb8 brw-r----- 1 0 0 3,73 makedevs.testdir/dev/hdb9 crw-rw---- 1 0 0 13,64 makedevs.testdir/dev/input/event0 crw-rw---- 1 0 0 13,65 makedevs.testdir/dev/input/event1 crw-rw---- 1 0 0 13,66 makedevs.testdir/dev/input/event2 crw-rw---- 1 0 0 13,67 makedevs.testdir/dev/input/event3 crw-r----- 1 0 0 13,63 makedevs.testdir/dev/input/mice crw-rw---- 1 0 0 13,32 makedevs.testdir/dev/input/mouse0 crw-rw---- 1 0 0 13,33 makedevs.testdir/dev/input/mouse1 crw-rw---- 1 0 0 13,34 makedevs.testdir/dev/input/mouse2 crw-rw---- 1 0 0 13,35 makedevs.testdir/dev/input/mouse3 crw-r----- 1 0 0 1,2 makedevs.testdir/dev/kmem brw-r----- 1 0 0 7,0 makedevs.testdir/dev/loop0 brw-r----- 1 0 0 7,1 makedevs.testdir/dev/loop1 crw-r----- 1 0 0 1,1 makedevs.testdir/dev/mem crw-r----- 1 0 0 90,0 makedevs.testdir/dev/mtd0 crw-r----- 1 0 0 90,2 makedevs.testdir/dev/mtd1 crw-r----- 1 0 0 90,4 makedevs.testdir/dev/mtd2 crw-r----- 1 0 0 90,6 makedevs.testdir/dev/mtd3 brw-r----- 1 0 0 31,0 makedevs.testdir/dev/mtdblock0 brw-r----- 1 0 0 31,1 makedevs.testdir/dev/mtdblock1 brw-r----- 1 0 0 31,2 makedevs.testdir/dev/mtdblock2 brw-r----- 1 0 0 31,3 makedevs.testdir/dev/mtdblock3 crw-rw---- 1 0 0 10,200 makedevs.testdir/dev/net/tun crw-rw-rw- 1 0 0 1,3 makedevs.testdir/dev/null crw-rw-rw- 1 0 0 10,1 makedevs.testdir/dev/psaux crw-rw-rw- 1 0 0 5,2 makedevs.testdir/dev/ptmx crw-rw-rw- 1 0 0 2,0 makedevs.testdir/dev/ptyp0 crw-rw-rw- 1 0 0 2,1 makedevs.testdir/dev/ptyp1 crw-rw-rw- 1 0 0 2,2 makedevs.testdir/dev/ptyp2 crw-rw-rw- 1 0 0 2,3 makedevs.testdir/dev/ptyp3 crw-rw-rw- 1 0 0 2,4 makedevs.testdir/dev/ptyp4 crw-rw-rw- 1 0 0 2,5 makedevs.testdir/dev/ptyp5 crw-rw-rw- 1 0 0 2,6 makedevs.testdir/dev/ptyp6 crw-rw-rw- 1 0 0 2,7 makedevs.testdir/dev/ptyp7 crw-rw-rw- 1 0 0 2,8 makedevs.testdir/dev/ptyp8 crw-rw-rw- 1 0 0 2,9 makedevs.testdir/dev/ptyp9 brw-r----- 1 0 0 1,1 makedevs.testdir/dev/ram brw-r----- 1 0 0 1,0 makedevs.testdir/dev/ram0 brw-r----- 1 0 0 1,1 makedevs.testdir/dev/ram1 brw-r----- 1 0 0 1,2 makedevs.testdir/dev/ram2 brw-r----- 1 0 0 1,3 makedevs.testdir/dev/ram3 crw-rw-rw- 1 0 0 1,8 makedevs.testdir/dev/random crw-r----- 1 0 0 10,135 makedevs.testdir/dev/rtc crw-rw-rw- 1 0 0 5,0 makedevs.testdir/dev/tty crw-rw-rw- 1 0 0 4,0 makedevs.testdir/dev/tty0 crw-rw-rw- 1 0 0 4,1 makedevs.testdir/dev/tty1 crw-rw-rw- 1 0 0 4,2 makedevs.testdir/dev/tty2 crw-rw-rw- 1 0 0 4,3 makedevs.testdir/dev/tty3 crw-rw-rw- 1 0 0 4,4 makedevs.testdir/dev/tty4 crw-rw-rw- 1 0 0 4,5 makedevs.testdir/dev/tty5 crw-rw-rw- 1 0 0 4,6 makedevs.testdir/dev/tty6 crw-rw-rw- 1 0 0 4,7 makedevs.testdir/dev/tty7 crw-rw-rw- 1 0 0 57,0 makedevs.testdir/dev/ttyP0 crw-rw-rw- 1 0 0 57,1 makedevs.testdir/dev/ttyP1 crw-rw-rw- 1 0 0 57,2 makedevs.testdir/dev/ttyP2 crw-rw-rw- 1 0 0 57,3 makedevs.testdir/dev/ttyP3 crw-rw-rw- 1 0 0 4,64 makedevs.testdir/dev/ttyS0 crw-rw-rw- 1 0 0 4,65 makedevs.testdir/dev/ttyS1 crw-rw-rw- 1 0 0 4,66 makedevs.testdir/dev/ttyS2 crw-rw-rw- 1 0 0 4,67 makedevs.testdir/dev/ttyS3 crw-rw-rw- 1 0 0 3,0 makedevs.testdir/dev/ttyp0 crw-rw-rw- 1 0 0 3,1 makedevs.testdir/dev/ttyp1 crw-rw-rw- 1 0 0 3,2 makedevs.testdir/dev/ttyp2 crw-rw-rw- 1 0 0 3,3 makedevs.testdir/dev/ttyp3 crw-rw-rw- 1 0 0 3,4 makedevs.testdir/dev/ttyp4 crw-rw-rw- 1 0 0 3,5 makedevs.testdir/dev/ttyp5 crw-rw-rw- 1 0 0 3,6 makedevs.testdir/dev/ttyp6 crw-rw-rw- 1 0 0 3,7 makedevs.testdir/dev/ttyp7 crw-rw-rw- 1 0 0 3,8 makedevs.testdir/dev/ttyp8 crw-rw-rw- 1 0 0 3,9 makedevs.testdir/dev/ttyp9 crw-rw-rw- 1 0 0 1,9 makedevs.testdir/dev/urandom crw-rw-rw- 1 0 0 1,5 makedevs.testdir/dev/zero " \ "" "" SKIP= # clean up rm -rf makedevs.testdir exit $FAILCOUNT ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/printf.tests���������������������������������������������������������������0000755�0000000�0000000�00000005455�12263563520�016425� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright 2008 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # Need this in order to not execute shell builtin bb="busybox " # testing "test name" "command" "expected result" "file input" "stdin" testing "printf produces no further output 1" \ "${bb}printf '\c' foo" \ "" \ "" "" testing "printf produces no further output 2" \ "${bb}printf '%s\c' foo bar" \ "foo" \ "" "" testing "printf repeatedly uses pattern for each argv" \ "${bb}printf '%s\n' foo \$HOME" \ "foo\n$HOME\n" \ "" "" testing "printf understands %b escaped_string" \ "${bb}printf '%b' 'a\tb' 'c\\d\n' 2>&1; echo \$?" \ "a\tbc\\d\n""0\n" \ "" "" testing "printf understands %d '\"x' \"'y\" \"'zTAIL\"" \ "${bb}printf '%d\n' '\"x' \"'y\" \"'zTAIL\" 2>&1; echo \$?" \ "120\n""121\n""122\n""0\n" \ "" "" testing "printf understands %s '\"x' \"'y\" \"'zTAIL\"" \ "${bb}printf '%s\n' '\"x' \"'y\" \"'zTAIL\" 2>&1; echo \$?" \ "\"x\n""'y\n""'zTAIL\n""0\n" \ "" "" testing "printf understands %23.12f" \ "${bb}printf '|%23.12f|\n' 5.25 2>&1; echo \$?" \ "| 5.250000000000|\n""0\n" \ "" "" testing "printf understands %*.*f" \ "${bb}printf '|%*.*f|\n' 23 12 5.25 2>&1; echo \$?" \ "| 5.250000000000|\n""0\n" \ "" "" testing "printf understands %*f with negative width" \ "${bb}printf '|%*f|\n' -23 5.25 2>&1; echo \$?" \ "|5.250000 |\n""0\n" \ "" "" testing "printf understands %.*f with negative precision" \ "${bb}printf '|%.*f|\n' -12 5.25 2>&1; echo \$?" \ "|5.250000|\n""0\n" \ "" "" testing "printf understands %*.*f with negative width/precision" \ "${bb}printf '|%*.*f|\n' -23 -12 5.25 2>&1; echo \$?" \ "|5.250000 |\n""0\n" \ "" "" testing "printf understands %zd" \ "${bb}printf '%zd\n' -5 2>&1; echo \$?" \ "-5\n""0\n" \ "" "" testing "printf understands %ld" \ "${bb}printf '%ld\n' -5 2>&1; echo \$?" \ "-5\n""0\n" \ "" "" testing "printf understands %Ld" \ "${bb}printf '%Ld\n' -5 2>&1; echo \$?" \ "-5\n""0\n" \ "" "" # "FIXED" now to act compatibly ## We are "more correct" here than bash/coreutils: they happily print -2 ## as if it is a huge unsigned number #testing "printf handles %u -N" \ # "${bb}printf '%u\n' 1 -2 3 2>&1; echo \$?" \ # "1\n""printf: -2: invalid number\n""0\n""3\n""0\n" \ # "" "" testing "printf handles %d bad_input" \ "${bb}printf '%d\n' 1 - 2 bad 3 123bad 4 2>&1; echo \$?" \ "1\n""printf: invalid number '-'\n""0\n"\ "2\n""printf: invalid number 'bad'\n""0\n"\ "3\n""printf: invalid number '123bad'\n""0\n"\ "4\n""1\n" \ "" "" testing "printf aborts on bare %" \ "${bb}printf '%' a b c 2>&1; echo \$?" \ "printf: %: invalid format\n""1\n" \ "" "" testing "printf aborts on %r" \ "${bb}printf '%r' a b c 2>&1; echo \$?" \ "printf: %r: invalid format\n""1\n" \ "" "" exit $FAILCOUNT �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/hostid/��������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�015311� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/hostid/hostid-works��������������������������������������������������������0000644�0000000�0000000�00000000154�12263563520�017675� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������h=x$(busybox hostid) # Is $h a sequence of hex numbers? case "$h" in x*[!0-9a-f]*) false;; *) true;; esac ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/mkfs.minix.tests�����������������������������������������������������������0000755�0000000�0000000�00000001271�12263563520�017176� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # mkfs.minix tests. # Copyright 2007 by Denys Vlasenko # Licensed under GPLv2, see file LICENSE in this source tree. . ./testing.sh # testing "test name" "options" "expected result" "file input" "stdin" # '\n' produces 10 on little endian, but not on big endian cr=`echo | od -i | sed 's/ *$//g;s/.* //g;2d'` if [ x"$cr" = x"10" ]; then hash=4f35f7afeba07d56055bed1f29ae20b7 else hash=5adbc1b3ccd20ca5d0ab5bc1e13ac3fc fi testing "mkfs.minix" \ "dd if=/dev/zero of=input bs=1k count=1024 2>/dev/null; mkfs.minix input; md5sum <input" \ "352 inodes\n"\ "1024 blocks\n"\ "Firstdatazone=15 (15)\n"\ "Zonesize=1024\n"\ "Maxsize=268966912\n"\ "$hash -\n" \ "" \ "" exit $FAILCOUNT ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/expr/����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014775� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/expr/expr-big��������������������������������������������������������������0000644�0000000�0000000�00000000712�12263563520�016441� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# busybox expr # 3*1000*1000*1000 overflows 32-bit signed int res=`busybox expr 0 '<' 3000000000` [ x"$res" = x1 ] || exit 1 # 9223372036854775807 = 2^31-1 res=`busybox expr 0 '<' 9223372036854775807` [ x"$res" = x1 ] || exit 1 # coreutils fails this one! res=`busybox expr -9223372036854775800 '<' 9223372036854775807` [ x"$res" = x1 ] || exit 1 # This one works only by chance # res=`busybox expr 0 '<' 9223372036854775808` # [ x"$res" = x1 ] || exit 1 ������������������������������������������������������busybox-1.22.1/testsuite/expr/expr-works������������������������������������������������������������0000644�0000000�0000000�00000001407�12263563520�017047� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# busybox expr busybox expr 1 \| 1 busybox expr 1 \| 0 busybox expr 0 \| 1 busybox expr 1 \& 1 busybox expr 0 \< 1 busybox expr 1 \> 0 busybox expr 0 \<= 1 busybox expr 1 \<= 1 busybox expr 1 \>= 0 busybox expr 1 \>= 1 busybox expr 1 + 2 busybox expr 2 - 1 busybox expr 2 \* 3 busybox expr 12 / 2 busybox expr 12 % 5 set +e busybox expr 0 \| 0 if [ $? != 1 ] ; then exit 1; fi; busybox expr 1 \& 0 if [ $? != 1 ] ; then exit 1; fi; busybox expr 0 \& 1 if [ $? != 1 ] ; then exit 1; fi; busybox expr 0 \& 0 if [ $? != 1 ] ; then exit 1; fi; busybox expr 1 \< 0 if [ $? != 1 ] ; then exit 1; fi; busybox expr 0 \> 1 if [ $? != 1 ] ; then exit 1; fi; busybox expr 1 \<= 0 if [ $? != 1 ] ; then exit 1; fi; busybox expr 0 \>= 1 if [ $? != 1 ] ; then exit 1; fi; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/testsuite/testing.sh�����������������������������������������������������������������0000644�0000000�0000000�00000007606�12263563520�016045� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Simple test harness infrastructure for BusyBox # # Copyright 2005 by Rob Landley # # License is GPLv2, see LICENSE in the busybox tarball for full license text. # This file defines two functions, "testing" and "optional" # and a couple more... # The following environment variables may be set to enable optional behavior # in "testing": # VERBOSE - Print the diff -u of each failed test case. # DEBUG - Enable command tracing. # SKIP - do not perform this test (this is set by "optional") # # The "testing" function takes five arguments: # $1) Test description # $2) Command(s) to run. May have pipes, redirects, etc # $3) Expected result on stdout # $4) Data to be written to file "input" # $5) Data to be written to stdin # # The exit value of testing is the exit value of $2 it ran. # # The environment variable "FAILCOUNT" contains a cumulative total of the # number of failed tests. # The "optional" function is used to skip certain tests, ala: # optional FEATURE_THINGY # # The "optional" function checks the environment variable "OPTIONFLAGS", # which is either empty (in which case it always clears SKIP) or # else contains a colon-separated list of features (in which case the function # clears SKIP if the flag was found, or sets it to 1 if the flag was not found). export FAILCOUNT=0 export SKIP= # Helper for helpers. Oh my... test x"$ECHO" != x"" || { ECHO="echo" test x"`echo -ne`" = x"" || { # Compile and use a replacement 'echo' which understands -e -n ECHO="$PWD/echo-ne" test -x "$ECHO" || { gcc -Os -o "$ECHO" ../scripts/echo.c || exit 1 } } export ECHO } # Helper functions optional() { SKIP= while test "$1"; do case "${OPTIONFLAGS}" in *:$1:*) ;; *) SKIP=1; return ;; esac shift done } # The testing function testing() { NAME="$1" [ -n "$1" ] || NAME="$2" if [ $# -ne 5 ] then echo "Test $NAME has wrong number of arguments: $# (must be 5)" >&2 exit 1 fi [ -z "$DEBUG" ] || set -x if [ -n "$SKIP" ] then echo "SKIPPED: $NAME" return 0 fi $ECHO -ne "$3" > expected $ECHO -ne "$4" > input [ -z "$VERBOSE" ] || echo ====================== [ -z "$VERBOSE" ] || echo "echo -ne '$4' >input" [ -z "$VERBOSE" ] || echo "echo -ne '$5' | $2" $ECHO -ne "$5" | eval "$2" > actual RETVAL=$? if cmp expected actual >/dev/null 2>/dev/null then echo "PASS: $NAME" else FAILCOUNT=$(($FAILCOUNT + 1)) echo "FAIL: $NAME" [ -z "$VERBOSE" ] || diff -u expected actual fi rm -f input expected actual [ -z "$DEBUG" ] || set +x return $RETVAL } # Recursively grab an executable and all the libraries needed to run it. # Source paths beginning with / will be copied into destpath, otherwise # the file is assumed to already be there and only its library dependencies # are copied. mkchroot() { [ $# -lt 2 ] && return $ECHO -n . dest=$1 shift for i in "$@" do #bashism: [ "${i:0:1}" == "/" ] || i=$(which $i) i=$(which $i) # no-op for /bin/prog [ -f "$dest/$i" ] && continue if [ -e "$i" ] then d=`echo "$i" | grep -o '.*/'` && mkdir -p "$dest/$d" && cat "$i" > "$dest/$i" && chmod +x "$dest/$i" else echo "Not found: $i" fi mkchroot "$dest" $(ldd "$i" | egrep -o '/.* ') done } # Set up a chroot environment and run commands within it. # Needed commands listed on command line # Script fed to stdin. dochroot() { mkdir tmpdir4chroot mount -t ramfs tmpdir4chroot tmpdir4chroot mkdir -p tmpdir4chroot/{etc,sys,proc,tmp,dev} cp -L testing.sh tmpdir4chroot # Copy utilities from command line arguments $ECHO -n "Setup chroot" mkchroot tmpdir4chroot $* echo mknod tmpdir4chroot/dev/tty c 5 0 mknod tmpdir4chroot/dev/null c 1 3 mknod tmpdir4chroot/dev/zero c 1 5 # Copy script from stdin cat > tmpdir4chroot/test.sh chmod +x tmpdir4chroot/test.sh chroot tmpdir4chroot /test.sh umount -l tmpdir4chroot rmdir tmpdir4chroot } ��������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/AUTHORS������������������������������������������������������������������������������0000644�0000000�0000000�00000012063�12263563520�013044� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������List of the authors of code contained in BusyBox. If you have code in BusyBox, you should be listed here. If you should be listed, or the description of what you have done needs more detail, or is incorrect, _please_ let me know. -Erik ----------- Peter Willis <psyphreak@phreaker.net> eject Emanuele Aina <emanuele.aina@tiscali.it> run-parts Erik Andersen <andersen@codepoet.org> Tons of new stuff, major rewrite of most of the core apps, tons of new apps as noted in header files. Lots of tedious effort writing these boring docs that nobody is going to actually read. Laurence Anderson <l.d.anderson@warwick.ac.uk> rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm Jeff Angielski <jeff@theptrgroup.com> ftpput, ftpget Enrik Berkhan <Enrik.Berkhan@inka.de> setconsole Jim Bauer <jfbauer@nfr.com> modprobe shell dependency Edward Betts <edward@debian.org> expr, hostid, logname, whoami John Beppu <beppu@codepoet.org> du, nslookup, sort David Brownell <dbrownell@users.sourceforge.net> zcip Brian Candler <B.Candler@pobox.com> tiny-ls(ls) Randolph Chung <tausq@debian.org> fbset, ping, hostname Dave Cinege <dcinege@psychosis.com> more(v2), makedevs, dutmp, modularization, auto links file, various fixes, Linux Router Project maintenance Jordan Crouse <jordan@cosmicpenguin.net> ipcalc Magnus Damm <damm@opensource.se> tftp client insmod powerpc support Larry Doolittle <ldoolitt@recycle.lbl.gov> pristine source directory compilation, lots of patches and fixes. Glenn Engel <glenne@engel.org> httpd Gennady Feldman <gfeldman@gena01.com> Sysklogd (single threaded syslogd, IPC Circular buffer support, logread), various fixes. Robert Griebl <sandman@handhelds.org> modprobe, hwclock, suid/sgid handling, tinylogin integration many bugfixes and enhancements Karl M. Hegbloom <karlheg@debian.org> cp_mv.c, the test suite, various fixes to utility.c, &c. Daniel Jacobowitz <dan@debian.org> mktemp.c Matt Kraai <kraai@alumni.cmu.edu> documentation, bugfixes, test suite Rob Landley <rob@landley.net> Became busybox maintainer in 2006. sed (major rewrite in 2003, and I now maintain the thing) bunzip2 (complete from-scratch rewrite, then mjn3 optimized the result) sort (more or less from scratch rewrite in 2004, I now maintain it) mount (rewrite in 2005, I maintain the new one) Stephan Linz <linz@li-pro.net> ipcalc, Red Hat equivalence John Lombardo <john@deltanet.com> tr Glenn McGrath <glenn.l.mcgrath@gmail.com> Common unarchiving code and unarchiving applets, ifupdown, ftpgetput, nameif, sed, patch, fold, install, uudecode. Various bugfixes, review and apply numerous patches. Manuel Novoa III <mjn3@codepoet.org> cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes, mesg, vconfig, nice, renice, make_directory, parse_mode, dirname, mode_string, get_last_path_component, simplify_path, and a number trivial libbb routines also bug fixes, partial rewrites, and size optimizations in ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir, mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable, interface, dutmp, ifconfig, route Vladimir Oleynik <dzo@simtreas.ru> cmdedit; bb_mkdep, xargs(current), httpd(current); ports: ash, crond, fdisk (initial, unmaintained now), inetd, stty, traceroute, top; locale, various fixes and irreconcilable critic of everything not perfect. Bruce Perens <bruce@pixar.com> Original author of BusyBox in 1995, 1996. Some of his code can still be found hiding here and there... Rodney Radford <rradford@mindspring.com> ipcs, ipcrm Tim Riker <Tim@Rikers.org> bug fixes, member of fan club Kent Robotti <robotti@metconnect.com> reset, tons and tons of bug reports and patches. Chip Rosenthal <chip@unicom.com>, <crosenth@covad.com> wget - Contributed by permission of Covad Communications Pavel Roskin <proski@gnu.org> Lots of bugs fixes and patches. Gyepi Sam <gyepi@praxis-sw.com> Remote logging feature for syslogd Rob Sullivan <cogito.ergo.cogito@gmail.com> comm Linus Torvalds mkswap, fsck.minix, mkfs.minix Mark Whitley <markw@codepoet.org> grep, sed, cut, xargs(previous), style-guide, new-applet-HOWTO, bug fixes, etc. Charles P. Wright <cpwright@villagenet.com> gzip, mini-netcat(nc) Enrique Zanardi <ezanardi@ull.es> tarcat (since removed), loadkmap, various fixes, Debian maintenance Tito Ragusa <farmatito@tiscali.it> devfsd and size optimizations in strings, openvt, chvt, deallocvt, hdparm, fdformat, lsattr, chattr, id and eject. Paul Fox <pgf@foxharp.boston.ma.us> vi editing mode for ash, various other patches/fixes Roberto A. Foglietta <me@roberto.foglietta.name> port: dnsd Bernhard Reutner-Fischer <rep.dot.nop@gmail.com> misc Mike Frysinger <vapier@gentoo.org> initial e2fsprogs, printenv, setarch, sum, misc Jie Zhang <jie.zhang@analog.com> fixed two bugs in msh and hush (exitcode of killed processes) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/Makefile.custom����������������������������������������������������������������������0000644�0000000�0000000�00000013637�12263563520�014755� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# ========================================================================== # Build system # ========================================================================== busybox.links: $(srctree)/applets/busybox.mkll $(objtree)/include/autoconf.h include/applets.h $(Q)-$(SHELL) $^ > $@ busybox.cfg.suid: $(srctree)/applets/busybox.mksuid $(objtree)/include/autoconf.h include/applets.h $(Q)-SUID="yes" $(SHELL) $^ > $@ busybox.cfg.nosuid: $(srctree)/applets/busybox.mksuid $(objtree)/include/autoconf.h include/applets.h $(Q)-SUID="DROP" $(SHELL) $^ > $@ .PHONY: install ifeq ($(CONFIG_INSTALL_APPLET_SYMLINKS),y) INSTALL_OPTS:= --symlinks endif ifeq ($(CONFIG_INSTALL_APPLET_HARDLINKS),y) INSTALL_OPTS:= --hardlinks endif ifeq ($(CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS),y) ifeq ($(CONFIG_INSTALL_SH_APPLET_SYMLINK),y) INSTALL_OPTS:= --sw-sh-sym endif ifeq ($(CONFIG_INSTALL_SH_APPLET_HARDLINK),y) INSTALL_OPTS:= --sw-sh-hard endif ifeq ($(CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER),y) INSTALL_OPTS:= --scriptwrapper endif endif install: $(srctree)/applets/install.sh busybox busybox.links $(Q)DO_INSTALL_LIBS="$(strip $(LIBBUSYBOX_SONAME) $(DO_INSTALL_LIBS))" \ $(SHELL) $< $(CONFIG_PREFIX) $(INSTALL_OPTS) ifeq ($(strip $(CONFIG_FEATURE_SUID)),y) @echo @echo @echo -------------------------------------------------- @echo You will probably need to make your busybox binary @echo setuid root to ensure all configured applets will @echo work properly. @echo -------------------------------------------------- @echo endif uninstall: busybox.links rm -f $(CONFIG_PREFIX)/bin/busybox for i in `cat busybox.links` ; do rm -f $(CONFIG_PREFIX)$$i; done ifneq ($(strip $(DO_INSTALL_LIBS)),n) for i in $(LIBBUSYBOX_SONAME) $(DO_INSTALL_LIBS); do \ rm -f $(CONFIG_PREFIX)$$i; \ done endif # Not very elegant: copies testsuite to objdir... # (cp -pPR is POSIX-compliant (cp -dpR or cp -a would not be)) .PHONY: check .PHONY: test check test: busybox busybox.links test -d $(objtree)/testsuite || cp -pPR $(srctree)/testsuite $(objtree) bindir=$(objtree) srcdir=$(srctree)/testsuite \ $(SHELL) -c "cd $(objtree)/testsuite && $(srctree)/testsuite/runtest $(if $(KBUILD_VERBOSE:0=),-v)" .PHONY: release release: distclean cd ..; \ rm -r -f busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION); \ cp -pPR busybox busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) && { \ find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type d \ -name .svn \ -print \ -exec rm -r -f {} \; ; \ find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type d \ -name .git \ -print \ -exec rm -r -f {} \; ; \ find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type f \ -name .gitignore \ -print \ -exec rm -f {} \; ; \ find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type f \ -name .\#* \ -print \ -exec rm -f {} \; ; \ tar -czf busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION).tar.gz \ busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ ; } .PHONY: checkhelp checkhelp: $(Q)$(srctree)/scripts/checkhelp.awk \ $(patsubst %,$(srctree)/%,$(wildcard $(patsubst %,%/Config.in,$(busybox-dirs) ./))) .PHONY: sizes sizes: busybox_unstripped $(NM) --size-sort $(<) .PHONY: bloatcheck bloatcheck: busybox_old busybox_unstripped @$(srctree)/scripts/bloat-o-meter busybox_old busybox_unstripped @$(CROSS_COMPILE)size busybox_old busybox_unstripped .PHONY: baseline baseline: busybox_unstripped @mv busybox_unstripped busybox_old .PHONY: objsizes objsizes: busybox_unstripped $(srctree)/scripts/objsizes .PHONY: stksizes stksizes: busybox_unstripped $(CROSS_COMPILE)objdump -d busybox_unstripped | $(srctree)/scripts/checkstack.pl $(ARCH) | uniq .PHONY: bigdata bigdata: busybox_unstripped $(CROSS_COMPILE)nm --size-sort busybox_unstripped | grep -vi ' [trw] ' # Documentation Targets .PHONY: doc doc: docs/busybox.pod docs/BusyBox.txt docs/busybox.1 docs/BusyBox.html # FIXME: Doesn't belong here cmd_doc = quiet_cmd_doc = $(Q)echo " DOC $(@F)" silent_cmd_doc = disp_doc = $($(quiet)cmd_doc) # sed adds newlines after "Options:" etc, # this is needed in order to get good BusyBox.{1,txt,html} docs/busybox.pod: $(srctree)/docs/busybox_header.pod \ include/usage.h \ $(srctree)/docs/busybox_footer.pod \ applets/usage_pod $(disp_doc) $(Q)-mkdir -p docs $(Q)-( \ cat $(srctree)/docs/busybox_header.pod; \ echo; \ applets/usage_pod | sed 's/^[A-Za-z][A-Za-z ]*[a-z]:$$/&\n/'; \ cat $(srctree)/docs/busybox_footer.pod; \ ) > docs/busybox.pod docs/BusyBox.txt: docs/busybox.pod $(disp_doc) $(Q)-mkdir -p docs $(Q)-pod2text $< > $@ docs/busybox.1: docs/busybox.pod $(disp_doc) $(Q)-mkdir -p docs $(Q)-pod2man --center=busybox --release="version $(KERNELVERSION)" $< > $@ docs/BusyBox.html: docs/busybox.net/BusyBox.html $(disp_doc) $(Q)-mkdir -p docs $(Q)-rm -f docs/BusyBox.html $(Q)-cp docs/busybox.net/BusyBox.html docs/BusyBox.html docs/busybox.net/BusyBox.html: docs/busybox.pod $(Q)-mkdir -p docs/busybox.net $(Q)-pod2html --noindex $< > $@ $(Q)-rm -f pod2htm* # documentation, cross-reference # Modern distributions already ship synopsis packages (e.g. debian) # If you have an old distribution go to http://synopsis.fresco.org/ syn_tgt = $(wildcard $(patsubst %,%/*.c,$(busybox-alldirs))) syn = $(patsubst %.c, %.syn, $(syn_tgt)) comma:= , brace_open:= ( brace_close:= ) SYN_CPPFLAGS := $(strip $(CPPFLAGS) $(EXTRA_CPPFLAGS)) SYN_CPPFLAGS := $(subst $(brace_open),\$(brace_open),$(SYN_CPPFLAGS)) SYN_CPPFLAGS := $(subst $(brace_close),\$(brace_close),$(SYN_CPPFLAGS)) #SYN_CPPFLAGS := $(subst ",\",$(SYN_CPPFLAGS)) #") #SYN_CPPFLAGS := [$(patsubst %,'%'$(comma),$(SYN_CPPFLAGS))''] %.syn: %.c synopsis -p C -l Comments.SSDFilter,Comments.Previous -Wp,preprocess=True,cppflags="'$(SYN_CPPFLAGS)'" -o $@ $< .PHONY: html html: $(syn) synopsis -f HTML -Wf,title="'BusyBox Documentation'" -o $@ $^ -include $(srctree)/Makefile.local �������������������������������������������������������������������������������������������������busybox-1.22.1/runit/�������������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�013127� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/runit/runit_lib.h��������������������������������������������������������������������0000644�0000000�0000000�00000003527�12263563520�015302� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2001-2006, Gerrit Pape 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. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. */ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* * runsv / supervise / sv stuff */ typedef struct svstatus_t { uint64_t time_be64 PACKED; uint32_t time_nsec_be32 PACKED; uint32_t pid_le32 PACKED; uint8_t paused; uint8_t want; /* 'u' or 'd' */ uint8_t got_term; uint8_t run_or_finish; } svstatus_t; struct ERR_svstatus_must_be_20_bytes { char ERR_svstatus_must_be_20_bytes[sizeof(svstatus_t) == 20 ? 1 : -1]; }; POP_SAVED_FUNCTION_VISIBILITY �������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/runit/Config.src���������������������������������������������������������������������0000644�0000000�0000000�00000003652�12263563520�015057� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Runit Utilities" INSERT config RUNSV bool "runsv" default y help runsv starts and monitors a service and optionally an appendant log service. config RUNSVDIR bool "runsvdir" default y help runsvdir starts a runsv process for each subdirectory, or symlink to a directory, in the services directory dir, up to a limit of 1000 subdirectories, and restarts a runsv process if it terminates. config FEATURE_RUNSVDIR_LOG bool "Enable scrolling argument log" depends on RUNSVDIR default n help Enable feature where second parameter of runsvdir holds last error message (viewable via top/ps). Otherwise (feature is off or no parameter), error messages go to stderr only. config SV bool "sv" default y help sv reports the current status and controls the state of services monitored by the runsv supervisor. config SV_DEFAULT_SERVICE_DIR string "Default directory for services" default "/var/service" depends on SV help Default directory for services. Defaults to "/var/service" config SVLOGD bool "svlogd" default y help svlogd continuously reads log data from its standard input, optionally filters log messages, and writes the data to one or more automatically rotated logs. config CHPST bool "chpst" default y help chpst changes the process state according to the given options, and execs specified program. config SETUIDGID bool "setuidgid" default y help Sets soft resource limits as specified by options config ENVUIDGID bool "envuidgid" default y help Sets $UID to account's uid and $GID to account's gid config ENVDIR bool "envdir" default y help Sets various environment variables as specified by files in the given directory config SOFTLIMIT bool "softlimit" default y help Sets soft resource limits as specified by options endmenu ��������������������������������������������������������������������������������������busybox-1.22.1/runit/svlogd.c�����������������������������������������������������������������������0000644�0000000�0000000�00000076316�12263563520�014612� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2001-2006, Gerrit Pape 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. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. */ /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */ /* TODO: depends on runit_lib.c - review and reduce/eliminate */ /* Config files On startup, and after receiving a HUP signal, svlogd checks for each log directory log if the configuration file log/config exists, and if so, reads the file line by line and adjusts configuration for log as follows: If the line is empty, or starts with a #, it is ignored. A line of the form ssize sets the maximum file size of current when svlogd should rotate the current log file to size bytes. Default is 1000000. If size is zero, svlogd doesnt rotate log files You should set size to at least (2 * len). nnum sets the number of old log files svlogd should maintain to num. If svlogd sees more that num old log files in log after log file rotation, it deletes the oldest one. Default is 10. If num is zero, svlogd doesnt remove old log files. Nmin sets the minimum number of old log files svlogd should maintain to min. min must be less than num. If min is set, and svlogd cannot write to current because the filesystem is full, and it sees more than min old log files, it deletes the oldest one. ttimeout sets the maximum age of the current log file when svlogd should rotate the current log file to timeout seconds. If current is timeout seconds old, and is not empty, svlogd forces log file rotation. !processor tells svlogd to feed each recent log file through processor (see above) on log file rotation. By default log files are not processed. ua.b.c.d[:port] tells svlogd to transmit the first len characters of selected log messages to the IP address a.b.c.d, port number port. If port isnt set, the default port for syslog is used (514). len can be set through the -l option, see below. If svlogd has trouble sending udp packets, it writes error messages to the log directory. Attention: logging through udp is unreliable, and should be used in private networks only. Ua.b.c.d[:port] is the same as the u line above, but the log messages are no longer written to the log directory, but transmitted through udp only. Error messages from svlogd concerning sending udp packages still go to the log directory. pprefix tells svlogd to prefix each line to be written to the log directory, to standard error, or through UDP, with prefix. If a line starts with a -, +, e, or E, svlogd matches the first len characters of each log message against pattern and acts accordingly: -pattern the log message is deselected. +pattern the log message is selected. epattern the log message is selected to be printed to standard error. Epattern the log message is deselected to be printed to standard error. Initially each line is selected to be written to log/current. Deselected log messages are discarded from log. Initially each line is deselected to be written to standard err. Log messages selected for standard error are written to standard error. Pattern Matching svlogd matches a log message against the string pattern as follows: pattern is applied to the log message one character by one, starting with the first. A character not a star (*) and not a plus (+) matches itself. A plus matches the next character in pattern in the log message one or more times. A star before the end of pattern matches any string in the log message that does not include the next character in pattern. A star at the end of pattern matches any string. Timestamps optionally added by svlogd are not considered part of the log message. An svlogd pattern is not a regular expression. For example consider a log message like this 2005-12-18_09:13:50.97618 tcpsvd: info: pid 1977 from 10.4.1.14 The following pattern doesnt match -*pid* because the first star matches up to the first p in tcpsvd, and then the match fails because i is not s. To match this log message, you can use a pattern like this instead -*: *: pid * */ //usage:#define svlogd_trivial_usage //usage: "[-ttv] [-r C] [-R CHARS] [-l MATCHLEN] [-b BUFLEN] DIR..." //usage:#define svlogd_full_usage "\n\n" //usage: "Continuously read log data from stdin and write to rotated log files in DIRs" //usage: "\n" //usage: "\n""DIR/config file modifies behavior:" //usage: "\n""sSIZE - when to rotate logs" //usage: "\n""nNUM - number of files to retain" /*usage: "\n""NNUM - min number files to retain" - confusing */ /*usage: "\n""tSEC - rotate file if it get SEC seconds old" - confusing */ //usage: "\n""!PROG - process rotated log with PROG" /*usage: "\n""uIPADDR - send log over UDP" - unsupported */ /*usage: "\n""UIPADDR - send log over UDP and DONT log" - unsupported */ /*usage: "\n""pPFX - prefix each line with PFX" - unsupported */ //usage: "\n""+,-PATTERN - (de)select line for logging" //usage: "\n""E,ePATTERN - (de)select line for stderr" #include <sys/file.h> #include "libbb.h" #include "runit_lib.h" #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0) #define FMT_PTIME 30 struct logdir { ////char *btmp; /* pattern list to match, in "aa\0bb\0\cc\0\0" form */ char *inst; char *processor; char *name; unsigned size; unsigned sizemax; unsigned nmax; unsigned nmin; unsigned rotate_period; int ppid; int fddir; int fdcur; FILE* filecur; //// int fdlock; unsigned next_rotate; char fnsave[FMT_PTIME]; char match; char matcherr; }; struct globals { struct logdir *dir; unsigned verbose; int linemax; ////int buflen; int linelen; int fdwdir; char **fndir; int wstat; unsigned nearest_rotate; void* (*memRchr)(const void *, int, size_t); char *shell; smallint exitasap; smallint rotateasap; smallint reopenasap; smallint linecomplete; smallint tmaxflag; char repl; const char *replace; int fl_flag_0; unsigned dirn; sigset_t blocked_sigset; }; #define G (*ptr_to_globals) #define dir (G.dir ) #define verbose (G.verbose ) #define linemax (G.linemax ) #define buflen (G.buflen ) #define linelen (G.linelen ) #define fndir (G.fndir ) #define fdwdir (G.fdwdir ) #define wstat (G.wstat ) #define memRchr (G.memRchr ) #define nearest_rotate (G.nearest_rotate) #define exitasap (G.exitasap ) #define rotateasap (G.rotateasap ) #define reopenasap (G.reopenasap ) #define linecomplete (G.linecomplete ) #define tmaxflag (G.tmaxflag ) #define repl (G.repl ) #define replace (G.replace ) #define blocked_sigset (G.blocked_sigset) #define fl_flag_0 (G.fl_flag_0 ) #define dirn (G.dirn ) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ linemax = 1000; \ /*buflen = 1024;*/ \ linecomplete = 1; \ replace = ""; \ } while (0) #define line bb_common_bufsiz1 #define FATAL "fatal: " #define WARNING "warning: " #define PAUSE "pausing: " #define INFO "info: " static void fatalx(const char *m0) { bb_error_msg_and_die(FATAL"%s", m0); } static void warn(const char *m0) { bb_perror_msg(WARNING"%s", m0); } static void warn2(const char *m0, const char *m1) { bb_perror_msg(WARNING"%s: %s", m0, m1); } static void warnx(const char *m0, const char *m1) { bb_error_msg(WARNING"%s: %s", m0, m1); } static void pause_nomem(void) { bb_error_msg(PAUSE"out of memory"); sleep(3); } static void pause1cannot(const char *m0) { bb_perror_msg(PAUSE"can't %s", m0); sleep(3); } static void pause2cannot(const char *m0, const char *m1) { bb_perror_msg(PAUSE"can't %s %s", m0, m1); sleep(3); } static char* wstrdup(const char *str) { char *s; while (!(s = strdup(str))) pause_nomem(); return s; } static unsigned pmatch(const char *p, const char *s, unsigned len) { for (;;) { char c = *p++; if (!c) return !len; switch (c) { case '*': c = *p; if (!c) return 1; for (;;) { if (!len) return 0; if (*s == c) break; ++s; --len; } continue; case '+': c = *p++; if (c != *s) return 0; for (;;) { if (!len) return 1; if (*s != c) break; ++s; --len; } continue; /* case '?': if (*p == '?') { if (*s != '?') return 0; ++p; } ++s; --len; continue; */ default: if (!len) return 0; if (*s != c) return 0; ++s; --len; continue; } } return 0; } /*** ex fmt_ptime.[ch] ***/ /* NUL terminated */ static void fmt_time_human_30nul(char *s) { struct tm *ptm; struct timeval tv; gettimeofday(&tv, NULL); ptm = gmtime(&tv.tv_sec); sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000", (unsigned)(1900 + ptm->tm_year), (unsigned)(ptm->tm_mon + 1), (unsigned)(ptm->tm_mday), (unsigned)(ptm->tm_hour), (unsigned)(ptm->tm_min), (unsigned)(ptm->tm_sec), (unsigned)(tv.tv_usec) ); /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */ /* 5 + 3 + 3 + 3 + 3 + 3 + 9 = */ /* 20 (up to '.' inclusive) + 9 (not including '\0') */ } /* NOT terminated! */ static void fmt_time_bernstein_25(char *s) { uint32_t pack[3]; struct timeval tv; unsigned sec_hi; gettimeofday(&tv, NULL); sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32; tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec; tv.tv_usec *= 1000; /* Network order is big-endian: most significant byte first. * This is exactly what we want here */ pack[0] = htonl(sec_hi); pack[1] = htonl(tv.tv_sec); pack[2] = htonl(tv.tv_usec); *s++ = '@'; bin2hex(s, (char*)pack, 12); } static void processorstart(struct logdir *ld) { char sv_ch; int pid; if (!ld->processor) return; if (ld->ppid) { warnx("processor already running", ld->name); return; } /* vfork'ed child trashes this byte, save... */ sv_ch = ld->fnsave[26]; if (!G.shell) G.shell = xstrdup(get_shell_name()); while ((pid = vfork()) == -1) pause2cannot("vfork for processor", ld->name); if (!pid) { int fd; /* child */ /* Non-ignored signals revert to SIG_DFL on exec anyway */ /*bb_signals(0 + (1 << SIGTERM) + (1 << SIGALRM) + (1 << SIGHUP) , SIG_DFL);*/ sig_unblock(SIGTERM); sig_unblock(SIGALRM); sig_unblock(SIGHUP); if (verbose) bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave); fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY); xmove_fd(fd, 0); ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */ fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT); xmove_fd(fd, 1); fd = open("state", O_RDONLY|O_NDELAY); if (fd == -1) { if (errno != ENOENT) bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name); close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT)); fd = xopen("state", O_RDONLY|O_NDELAY); } xmove_fd(fd, 4); fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT); xmove_fd(fd, 5); execl(G.shell, G.shell, "-c", ld->processor, (char*) NULL); bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name); } ld->fnsave[26] = sv_ch; /* ...restore */ ld->ppid = pid; } static unsigned processorstop(struct logdir *ld) { char f[28]; if (ld->ppid) { sig_unblock(SIGHUP); while (safe_waitpid(ld->ppid, &wstat, 0) == -1) pause2cannot("wait for processor", ld->name); sig_block(SIGHUP); ld->ppid = 0; } if (ld->fddir == -1) return 1; while (fchdir(ld->fddir) == -1) pause2cannot("change directory, want processor", ld->name); if (WEXITSTATUS(wstat) != 0) { warnx("processor failed, restart", ld->name); ld->fnsave[26] = 't'; unlink(ld->fnsave); ld->fnsave[26] = 'u'; processorstart(ld); while (fchdir(fdwdir) == -1) pause1cannot("change to initial working directory"); return ld->processor ? 0 : 1; } ld->fnsave[26] = 't'; memcpy(f, ld->fnsave, 26); f[26] = 's'; f[27] = '\0'; while (rename(ld->fnsave, f) == -1) pause2cannot("rename processed", ld->name); while (chmod(f, 0744) == -1) pause2cannot("set mode of processed", ld->name); ld->fnsave[26] = 'u'; if (unlink(ld->fnsave) == -1) bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave); while (rename("newstate", "state") == -1) pause2cannot("rename state", ld->name); if (verbose) bb_error_msg(INFO"processed: %s/%s", ld->name, f); while (fchdir(fdwdir) == -1) pause1cannot("change to initial working directory"); return 1; } static void rmoldest(struct logdir *ld) { DIR *d; struct dirent *f; char oldest[FMT_PTIME]; int n = 0; oldest[0] = 'A'; oldest[1] = oldest[27] = 0; while (!(d = opendir("."))) pause2cannot("open directory, want rotate", ld->name); errno = 0; while ((f = readdir(d))) { if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) { if (f->d_name[26] == 't') { if (unlink(f->d_name) == -1) warn2("can't unlink processor leftover", f->d_name); } else { ++n; if (strcmp(f->d_name, oldest) < 0) memcpy(oldest, f->d_name, 27); } errno = 0; } } if (errno) warn2("can't read directory", ld->name); closedir(d); if (ld->nmax && (n > ld->nmax)) { if (verbose) bb_error_msg(INFO"delete: %s/%s", ld->name, oldest); if ((*oldest == '@') && (unlink(oldest) == -1)) warn2("can't unlink oldest logfile", ld->name); } } static unsigned rotate(struct logdir *ld) { struct stat st; unsigned now; if (ld->fddir == -1) { ld->rotate_period = 0; return 0; } if (ld->ppid) while (!processorstop(ld)) continue; while (fchdir(ld->fddir) == -1) pause2cannot("change directory, want rotate", ld->name); /* create new filename */ ld->fnsave[25] = '.'; ld->fnsave[26] = 's'; if (ld->processor) ld->fnsave[26] = 'u'; ld->fnsave[27] = '\0'; do { fmt_time_bernstein_25(ld->fnsave); errno = 0; stat(ld->fnsave, &st); } while (errno != ENOENT); now = monotonic_sec(); if (ld->rotate_period && LESS(ld->next_rotate, now)) { ld->next_rotate = now + ld->rotate_period; if (LESS(ld->next_rotate, nearest_rotate)) nearest_rotate = ld->next_rotate; } if (ld->size > 0) { while (fflush(ld->filecur) || fsync(ld->fdcur) == -1) pause2cannot("fsync current logfile", ld->name); while (fchmod(ld->fdcur, 0744) == -1) pause2cannot("set mode of current", ld->name); ////close(ld->fdcur); fclose(ld->filecur); if (verbose) { bb_error_msg(INFO"rename: %s/current %s %u", ld->name, ld->fnsave, ld->size); } while (rename("current", ld->fnsave) == -1) pause2cannot("rename current", ld->name); while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1) pause2cannot("create new current", ld->name); while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) //// pause2cannot("create new current", ld->name); /* very unlikely */ setvbuf(ld->filecur, NULL, _IOFBF, linelen); //// close_on_exec_on(ld->fdcur); ld->size = 0; while (fchmod(ld->fdcur, 0644) == -1) pause2cannot("set mode of current", ld->name); rmoldest(ld); processorstart(ld); } while (fchdir(fdwdir) == -1) pause1cannot("change to initial working directory"); return 1; } static int buffer_pwrite(int n, char *s, unsigned len) { int i; struct logdir *ld = &dir[n]; if (ld->sizemax) { if (ld->size >= ld->sizemax) rotate(ld); if (len > (ld->sizemax - ld->size)) len = ld->sizemax - ld->size; } while (1) { ////i = full_write(ld->fdcur, s, len); ////if (i != -1) break; i = fwrite(s, 1, len, ld->filecur); if (i == len) break; if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) { DIR *d; struct dirent *f; char oldest[FMT_PTIME]; int j = 0; while (fchdir(ld->fddir) == -1) pause2cannot("change directory, want remove old logfile", ld->name); oldest[0] = 'A'; oldest[1] = oldest[27] = '\0'; while (!(d = opendir("."))) pause2cannot("open directory, want remove old logfile", ld->name); errno = 0; while ((f = readdir(d))) if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) { ++j; if (strcmp(f->d_name, oldest) < 0) memcpy(oldest, f->d_name, 27); } if (errno) warn2("can't read directory, want remove old logfile", ld->name); closedir(d); errno = ENOSPC; if (j > ld->nmin) { if (*oldest == '@') { bb_error_msg(WARNING"out of disk space, delete: %s/%s", ld->name, oldest); errno = 0; if (unlink(oldest) == -1) { warn2("can't unlink oldest logfile", ld->name); errno = ENOSPC; } while (fchdir(fdwdir) == -1) pause1cannot("change to initial working directory"); } } } if (errno) pause2cannot("write to current", ld->name); } ld->size += i; if (ld->sizemax) if (s[i-1] == '\n') if (ld->size >= (ld->sizemax - linemax)) rotate(ld); return i; } static void logdir_close(struct logdir *ld) { if (ld->fddir == -1) return; if (verbose) bb_error_msg(INFO"close: %s", ld->name); close(ld->fddir); ld->fddir = -1; if (ld->fdcur == -1) return; /* impossible */ while (fflush(ld->filecur) || fsync(ld->fdcur) == -1) pause2cannot("fsync current logfile", ld->name); while (fchmod(ld->fdcur, 0744) == -1) pause2cannot("set mode of current", ld->name); ////close(ld->fdcur); fclose(ld->filecur); ld->fdcur = -1; if (ld->fdlock == -1) return; /* impossible */ close(ld->fdlock); ld->fdlock = -1; free(ld->processor); ld->processor = NULL; } static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn) { char buf[128]; unsigned now; char *new, *s, *np; int i; struct stat st; now = monotonic_sec(); ld->fddir = open(fn, O_RDONLY|O_NDELAY); if (ld->fddir == -1) { warn2("can't open log directory", (char*)fn); return 0; } close_on_exec_on(ld->fddir); if (fchdir(ld->fddir) == -1) { logdir_close(ld); warn2("can't change directory", (char*)fn); return 0; } ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600); if ((ld->fdlock == -1) || (flock(ld->fdlock, LOCK_EX | LOCK_NB) == -1) ) { logdir_close(ld); warn2("can't lock directory", (char*)fn); while (fchdir(fdwdir) == -1) pause1cannot("change to initial working directory"); return 0; } close_on_exec_on(ld->fdlock); ld->size = 0; ld->sizemax = 1000000; ld->nmax = ld->nmin = 10; ld->rotate_period = 0; ld->name = (char*)fn; ld->ppid = 0; ld->match = '+'; free(ld->inst); ld->inst = NULL; free(ld->processor); ld->processor = NULL; /* read config */ i = open_read_close("config", buf, sizeof(buf) - 1); if (i < 0 && errno != ENOENT) bb_perror_msg(WARNING"%s/config", ld->name); if (i > 0) { buf[i] = '\0'; if (verbose) bb_error_msg(INFO"read: %s/config", ld->name); s = buf; while (s) { np = strchr(s, '\n'); if (np) *np++ = '\0'; switch (s[0]) { case '+': case '-': case 'e': case 'E': /* Filtering requires one-line buffering, * resetting the "find newline" function * accordingly */ memRchr = memchr; /* Add '\n'-terminated line to ld->inst */ while (1) { int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s); if (l >= 0 && new) break; pause_nomem(); } free(ld->inst); ld->inst = new; break; case 's': { ld->sizemax = xatou_sfx(&s[1], km_suffixes); break; } case 'n': ld->nmax = xatoi_positive(&s[1]); break; case 'N': ld->nmin = xatoi_positive(&s[1]); break; case 't': { static const struct suffix_mult mh_suffixes[] = { { "m", 60 }, { "h", 60*60 }, /*{ "d", 24*60*60 },*/ { "", 0 } }; ld->rotate_period = xatou_sfx(&s[1], mh_suffixes); if (ld->rotate_period) { ld->next_rotate = now + ld->rotate_period; if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate)) nearest_rotate = ld->next_rotate; tmaxflag = 1; } break; } case '!': if (s[1]) { free(ld->processor); ld->processor = wstrdup(s); } break; } s = np; } /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */ s = ld->inst; while (s) { np = strchr(s, '\n'); if (np) *np++ = '\0'; s = np; } } /* open current */ i = stat("current", &st); if (i != -1) { if (st.st_size && !(st.st_mode & S_IXUSR)) { ld->fnsave[25] = '.'; ld->fnsave[26] = 'u'; ld->fnsave[27] = '\0'; do { fmt_time_bernstein_25(ld->fnsave); errno = 0; stat(ld->fnsave, &st); } while (errno != ENOENT); while (rename("current", ld->fnsave) == -1) pause2cannot("rename current", ld->name); rmoldest(ld); i = -1; } else { /* st.st_size can be not just bigger, but WIDER! * This code is safe: if st.st_size > 4GB, we select * ld->sizemax (because it's "unsigned") */ ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size; } } else { if (errno != ENOENT) { logdir_close(ld); warn2("can't stat current", ld->name); while (fchdir(fdwdir) == -1) pause1cannot("change to initial working directory"); return 0; } } while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1) pause2cannot("open current", ld->name); while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) pause2cannot("open current", ld->name); //// setvbuf(ld->filecur, NULL, _IOFBF, linelen); //// close_on_exec_on(ld->fdcur); while (fchmod(ld->fdcur, 0644) == -1) pause2cannot("set mode of current", ld->name); if (verbose) { if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name); else bb_error_msg(INFO"new: %s/current", ld->name); } while (fchdir(fdwdir) == -1) pause1cannot("change to initial working directory"); return 1; } static void logdirs_reopen(void) { int l; int ok = 0; tmaxflag = 0; for (l = 0; l < dirn; ++l) { logdir_close(&dir[l]); if (logdir_open(&dir[l], fndir[l])) ok = 1; } if (!ok) fatalx("no functional log directories"); } /* Will look good in libbb one day */ static ssize_t ndelay_read(int fd, void *buf, size_t count) { if (!(fl_flag_0 & O_NONBLOCK)) fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK); count = safe_read(fd, buf, count); if (!(fl_flag_0 & O_NONBLOCK)) fcntl(fd, F_SETFL, fl_flag_0); return count; } /* Used for reading stdin */ static int buffer_pread(/*int fd, */char *s, unsigned len) { unsigned now; struct pollfd input; int i; input.fd = STDIN_FILENO; input.events = POLLIN; do { if (rotateasap) { for (i = 0; i < dirn; ++i) rotate(dir + i); rotateasap = 0; } if (exitasap) { if (linecomplete) return 0; len = 1; } if (reopenasap) { logdirs_reopen(); reopenasap = 0; } now = monotonic_sec(); nearest_rotate = now + (45 * 60 + 45); for (i = 0; i < dirn; ++i) { if (dir[i].rotate_period) { if (LESS(dir[i].next_rotate, now)) rotate(dir + i); if (LESS(dir[i].next_rotate, nearest_rotate)) nearest_rotate = dir[i].next_rotate; } } sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL); i = nearest_rotate - now; if (i > 1000000) i = 1000000; if (i <= 0) i = 1; poll(&input, 1, i * 1000); sigprocmask(SIG_BLOCK, &blocked_sigset, NULL); i = ndelay_read(STDIN_FILENO, s, len); if (i >= 0) break; if (errno == EINTR) continue; if (errno != EAGAIN) { warn("can't read standard input"); break; } /* else: EAGAIN - normal, repeat silently */ } while (!exitasap); if (i > 0) { int cnt; linecomplete = (s[i-1] == '\n'); if (!repl) return i; cnt = i; while (--cnt >= 0) { char ch = *s; if (ch != '\n') { if (ch < 32 || ch > 126) *s = repl; else { int j; for (j = 0; replace[j]; ++j) { if (ch == replace[j]) { *s = repl; break; } } } } s++; } } return i; } static void sig_term_handler(int sig_no UNUSED_PARAM) { if (verbose) bb_error_msg(INFO"sig%s received", "term"); exitasap = 1; } static void sig_child_handler(int sig_no UNUSED_PARAM) { pid_t pid; int l; if (verbose) bb_error_msg(INFO"sig%s received", "child"); while ((pid = wait_any_nohang(&wstat)) > 0) { for (l = 0; l < dirn; ++l) { if (dir[l].ppid == pid) { dir[l].ppid = 0; processorstop(&dir[l]); break; } } } } static void sig_alarm_handler(int sig_no UNUSED_PARAM) { if (verbose) bb_error_msg(INFO"sig%s received", "alarm"); rotateasap = 1; } static void sig_hangup_handler(int sig_no UNUSED_PARAM) { if (verbose) bb_error_msg(INFO"sig%s received", "hangup"); reopenasap = 1; } static void logmatch(struct logdir *ld) { char *s; ld->match = '+'; ld->matcherr = 'E'; s = ld->inst; while (s && s[0]) { switch (s[0]) { case '+': case '-': if (pmatch(s+1, line, linelen)) ld->match = s[0]; break; case 'e': case 'E': if (pmatch(s+1, line, linelen)) ld->matcherr = s[0]; break; } s += strlen(s) + 1; } } int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int svlogd_main(int argc, char **argv) { char *r, *l, *b; ssize_t stdin_cnt = 0; int i; unsigned opt; unsigned timestamp = 0; INIT_G(); opt_complementary = "tt:vv"; opt = getopt32(argv, "r:R:l:b:tv", &r, &replace, &l, &b, ×tamp, &verbose); if (opt & 1) { // -r repl = r[0]; if (!repl || r[1]) bb_show_usage(); } if (opt & 2) if (!repl) repl = '_'; // -R if (opt & 4) { // -l linemax = xatou_range(l, 0, BUFSIZ-26); if (linemax == 0) linemax = BUFSIZ-26; if (linemax < 256) linemax = 256; } ////if (opt & 8) { // -b //// buflen = xatoi_positive(b); //// if (buflen == 0) buflen = 1024; ////} //if (opt & 0x10) timestamp++; // -t //if (opt & 0x20) verbose++; // -v //if (timestamp > 2) timestamp = 2; argv += optind; argc -= optind; dirn = argc; if (dirn <= 0) bb_show_usage(); ////if (buflen <= linemax) bb_show_usage(); fdwdir = xopen(".", O_RDONLY|O_NDELAY); close_on_exec_on(fdwdir); dir = xzalloc(dirn * sizeof(dir[0])); for (i = 0; i < dirn; ++i) { dir[i].fddir = -1; dir[i].fdcur = -1; ////dir[i].btmp = xmalloc(buflen); /*dir[i].ppid = 0;*/ } /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */ fndir = argv; /* We cannot set NONBLOCK on fd #0 permanently - this setting * _isn't_ per-process! It is shared among all other processes * with the same stdin */ fl_flag_0 = fcntl(0, F_GETFL); sigemptyset(&blocked_sigset); sigaddset(&blocked_sigset, SIGTERM); sigaddset(&blocked_sigset, SIGCHLD); sigaddset(&blocked_sigset, SIGALRM); sigaddset(&blocked_sigset, SIGHUP); sigprocmask(SIG_BLOCK, &blocked_sigset, NULL); bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler); bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler); bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler); bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler); /* Without timestamps, we don't have to print each line * separately, so we can look for _last_ newline, not first, * thus batching writes. If filtering is enabled in config, * logdirs_reopen resets it to memchr. */ memRchr = (timestamp ? memchr : memrchr); logdirs_reopen(); setvbuf(stderr, NULL, _IOFBF, linelen); /* Each iteration processes one or more lines */ while (1) { char stamp[FMT_PTIME]; char *lineptr; char *printptr; char *np; int printlen; char ch; lineptr = line; if (timestamp) lineptr += 26; /* lineptr[0..linemax-1] - buffer for stdin */ /* (possibly has some unprocessed data from prev loop) */ /* Refill the buffer if needed */ np = memRchr(lineptr, '\n', stdin_cnt); if (!np && !exitasap) { i = linemax - stdin_cnt; /* avail. bytes at tail */ if (i >= 128) { i = buffer_pread(/*0, */lineptr + stdin_cnt, i); if (i <= 0) /* EOF or error on stdin */ exitasap = 1; else { np = memRchr(lineptr + stdin_cnt, '\n', i); stdin_cnt += i; } } } if (stdin_cnt <= 0 && exitasap) break; /* Search for '\n' (in fact, np already holds the result) */ linelen = stdin_cnt; if (np) { print_to_nl: /* NB: starting from here lineptr may point * farther out into line[] */ linelen = np - lineptr + 1; } /* linelen == no of chars incl. '\n' (or == stdin_cnt) */ ch = lineptr[linelen-1]; /* Biggest performance hit was coming from the fact * that we did not buffer writes. We were reading many lines * in one read() above, but wrote one line per write(). * We are using stdio to fix that */ /* write out lineptr[0..linelen-1] to each log destination * (or lineptr[-26..linelen-1] if timestamping) */ printlen = linelen; printptr = lineptr; if (timestamp) { if (timestamp == 1) fmt_time_bernstein_25(stamp); else /* 2: */ fmt_time_human_30nul(stamp); printlen += 26; printptr -= 26; memcpy(printptr, stamp, 25); printptr[25] = ' '; } for (i = 0; i < dirn; ++i) { struct logdir *ld = &dir[i]; if (ld->fddir == -1) continue; if (ld->inst) logmatch(ld); if (ld->matcherr == 'e') { /* runit-1.8.0 compat: if timestamping, do it on stderr too */ ////full_write(STDERR_FILENO, printptr, printlen); fwrite(printptr, 1, printlen, stderr); } if (ld->match != '+') continue; buffer_pwrite(i, printptr, printlen); } /* If we didn't see '\n' (long input line), */ /* read/write repeatedly until we see it */ while (ch != '\n') { /* lineptr is emptied now, safe to use as buffer */ stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax); if (stdin_cnt <= 0) { /* EOF or error on stdin */ exitasap = 1; lineptr[0] = ch = '\n'; linelen = 1; stdin_cnt = 1; } else { linelen = stdin_cnt; np = memRchr(lineptr, '\n', stdin_cnt); if (np) linelen = np - lineptr + 1; ch = lineptr[linelen-1]; } /* linelen == no of chars incl. '\n' (or == stdin_cnt) */ for (i = 0; i < dirn; ++i) { if (dir[i].fddir == -1) continue; if (dir[i].matcherr == 'e') { ////full_write(STDERR_FILENO, lineptr, linelen); fwrite(lineptr, 1, linelen, stderr); } if (dir[i].match != '+') continue; buffer_pwrite(i, lineptr, linelen); } } stdin_cnt -= linelen; if (stdin_cnt > 0) { lineptr += linelen; /* If we see another '\n', we don't need to read * next piece of input: can print what we have */ np = memRchr(lineptr, '\n', stdin_cnt); if (np) goto print_to_nl; /* Move unprocessed data to the front of line */ memmove((timestamp ? line+26 : line), lineptr, stdin_cnt); } fflush_all();//// } for (i = 0; i < dirn; ++i) { if (dir[i].ppid) while (!processorstop(&dir[i])) continue; logdir_close(&dir[i]); } return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/runit/Kbuild.src���������������������������������������������������������������������0000644�0000000�0000000�00000000725�12263563520�015062� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org> # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT lib-$(CONFIG_RUNSV) += runsv.o lib-$(CONFIG_RUNSVDIR) += runsvdir.o lib-$(CONFIG_SV) += sv.o lib-$(CONFIG_SVLOGD) += svlogd.o lib-$(CONFIG_CHPST) += chpst.o lib-$(CONFIG_ENVDIR) += chpst.o lib-$(CONFIG_ENVUIDGID) += chpst.o lib-$(CONFIG_SETUIDGID) += chpst.o lib-$(CONFIG_SOFTLIMIT) += chpst.o �������������������������������������������busybox-1.22.1/runit/sv.c���������������������������������������������������������������������������0000644�0000000�0000000�00000040676�12263563520�013744� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2001-2006, Gerrit Pape 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. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. */ /* Taken from http://smarden.sunsite.dk/runit/sv.8.html: sv - control and manage services monitored by runsv sv [-v] [-w sec] command services /etc/init.d/service [-w sec] command The sv program reports the current status and controls the state of services monitored by the runsv(8) supervisor. services consists of one or more arguments, each argument naming a directory service used by runsv(8). If service doesn't start with a dot or slash, it is searched in the default services directory /var/service/, otherwise relative to the current directory. command is one of up, down, status, once, pause, cont, hup, alarm, interrupt, 1, 2, term, kill, or exit, or start, stop, restart, shutdown, force-stop, force-reload, force-restart, force-shutdown. The sv program can be sym-linked to /etc/init.d/ to provide an LSB init script interface. The service to be controlled then is specified by the base name of the "init script". status Report the current status of the service, and the appendant log service if available, to standard output. up If the service is not running, start it. If the service stops, restart it. down If the service is running, send it the TERM signal, and the CONT signal. If ./run exits, start ./finish if it exists. After it stops, do not restart service. once If the service is not running, start it. Do not restart it if it stops. pause cont hup alarm interrupt quit 1 2 term kill If the service is running, send it the STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, or KILL signal respectively. exit If the service is running, send it the TERM signal, and the CONT signal. Do not restart the service. If the service is down, and no log service exists, runsv(8) exits. If the service is down and a log service exists, send the TERM signal to the log service. If the log service is down, runsv(8) exits. This command is ignored if it is given to an appendant log service. sv actually looks only at the first character of above commands. Commands compatible to LSB init script actions: status Same as status. start Same as up, but wait up to 7 seconds for the command to take effect. Then report the status or timeout. If the script ./check exists in the service directory, sv runs this script to check whether the service is up and available; it's considered to be available if ./check exits with 0. stop Same as down, but wait up to 7 seconds for the service to become down. Then report the status or timeout. restart Send the commands term, cont, and up to the service, and wait up to 7 seconds for the service to restart. Then report the status or timeout. If the script ./check exists in the service directory, sv runs this script to check whether the service is up and available again; it's considered to be available if ./check exits with 0. shutdown Same as exit, but wait up to 7 seconds for the runsv(8) process to terminate. Then report the status or timeout. force-stop Same as down, but wait up to 7 seconds for the service to become down. Then report the status, and on timeout send the service the kill command. force-reload Send the service the term and cont commands, and wait up to 7 seconds for the service to restart. Then report the status, and on timeout send the service the kill command. force-restart Send the service the term, cont and up commands, and wait up to 7 seconds for the service to restart. Then report the status, and on timeout send the service the kill command. If the script ./check exists in the service directory, sv runs this script to check whether the service is up and available again; it's considered to be available if ./check exits with 0. force-shutdown Same as exit, but wait up to 7 seconds for the runsv(8) process to terminate. Then report the status, and on timeout send the service the kill command. Additional Commands check Check for the service to be in the state that's been requested. Wait up to 7 seconds for the service to reach the requested state, then report the status or timeout. If the requested state of the service is up, and the script ./check exists in the service directory, sv runs this script to check whether the service is up and running; it's considered to be up if ./check exits with 0. Options -v wait up to 7 seconds for the command to take effect. Then report the status or timeout. -w sec Override the default timeout of 7 seconds with sec seconds. Implies -v. Environment SVDIR The environment variable $SVDIR overrides the default services directory /var/service. SVWAIT The environment variable $SVWAIT overrides the default 7 seconds to wait for a command to take effect. It is overridden by the -w option. Exit Codes sv exits 0, if the command was successfully sent to all services, and, if it was told to wait, the command has taken effect to all services. For each service that caused an error (e.g. the directory is not controlled by a runsv(8) process, or sv timed out while waiting), sv increases the exit code by one and exits non zero. The maximum is 99. sv exits 100 on error. */ /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */ /* TODO: depends on runit_lib.c - review and reduce/eliminate */ //usage:#define sv_trivial_usage //usage: "[-v] [-w SEC] CMD SERVICE_DIR..." //usage:#define sv_full_usage "\n\n" //usage: "Control services monitored by runsv supervisor.\n" //usage: "Commands (only first character is enough):\n" //usage: "\n" //usage: "status: query service status\n" //usage: "up: if service isn't running, start it. If service stops, restart it\n" //usage: "once: like 'up', but if service stops, don't restart it\n" //usage: "down: send TERM and CONT signals. If ./run exits, start ./finish\n" //usage: " if it exists. After it stops, don't restart service\n" //usage: "exit: send TERM and CONT signals to service and log service. If they exit,\n" //usage: " runsv exits too\n" //usage: "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n" //usage: "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service" #include <sys/file.h> #include "libbb.h" #include "runit_lib.h" struct globals { const char *acts; char **service; unsigned rc; /* "Bernstein" time format: unix + 0x400000000000000aULL */ uint64_t tstart, tnow; svstatus_t svstatus; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define acts (G.acts ) #define service (G.service ) #define rc (G.rc ) #define tstart (G.tstart ) #define tnow (G.tnow ) #define svstatus (G.svstatus ) #define INIT_G() do { } while (0) #define str_equal(s,t) (!strcmp((s), (t))) static void fatal_cannot(const char *m1) NORETURN; static void fatal_cannot(const char *m1) { bb_perror_msg("fatal: can't %s", m1); _exit(151); } static void out(const char *p, const char *m1) { printf("%s%s: %s", p, *service, m1); if (errno) { printf(": %s", strerror(errno)); } bb_putchar('\n'); /* will also flush the output */ } #define WARN "warning: " #define OK "ok: " static void fail(const char *m1) { ++rc; out("fail: ", m1); } static void failx(const char *m1) { errno = 0; fail(m1); } static void warn(const char *m1) { ++rc; /* "warning: <service>: <m1>\n" */ out("warning: ", m1); } static void ok(const char *m1) { errno = 0; out(OK, m1); } static int svstatus_get(void) { int fd, r; fd = open("supervise/ok", O_WRONLY|O_NDELAY); if (fd == -1) { if (errno == ENODEV) { *acts == 'x' ? ok("runsv not running") : failx("runsv not running"); return 0; } warn("can't open supervise/ok"); return -1; } close(fd); fd = open("supervise/status", O_RDONLY|O_NDELAY); if (fd == -1) { warn("can't open supervise/status"); return -1; } r = read(fd, &svstatus, 20); close(fd); switch (r) { case 20: break; case -1: warn("can't read supervise/status"); return -1; default: errno = 0; warn("can't read supervise/status: bad format"); return -1; } return 1; } static unsigned svstatus_print(const char *m) { int diff; int pid; int normallyup = 0; struct stat s; uint64_t timestamp; if (stat("down", &s) == -1) { if (errno != ENOENT) { bb_perror_msg(WARN"can't stat %s/down", *service); return 0; } normallyup = 1; } pid = SWAP_LE32(svstatus.pid_le32); timestamp = SWAP_BE64(svstatus.time_be64); if (pid) { switch (svstatus.run_or_finish) { case 1: printf("run: "); break; case 2: printf("finish: "); break; } printf("%s: (pid %d) ", m, pid); } else { printf("down: %s: ", m); } diff = tnow - timestamp; printf("%us", (diff < 0 ? 0 : diff)); if (pid) { if (!normallyup) printf(", normally down"); if (svstatus.paused) printf(", paused"); if (svstatus.want == 'd') printf(", want down"); if (svstatus.got_term) printf(", got TERM"); } else { if (normallyup) printf(", normally up"); if (svstatus.want == 'u') printf(", want up"); } return pid ? 1 : 2; } static int status(const char *unused UNUSED_PARAM) { int r; if (svstatus_get() <= 0) return 0; r = svstatus_print(*service); if (chdir("log") == -1) { if (errno != ENOENT) { printf("; log: "WARN"can't change to log service directory: %s", strerror(errno)); } } else if (svstatus_get()) { printf("; "); svstatus_print("log"); } bb_putchar('\n'); /* will also flush the output */ return r; } static int checkscript(void) { char *prog[2]; struct stat s; int pid, w; if (stat("check", &s) == -1) { if (errno == ENOENT) return 1; bb_perror_msg(WARN"can't stat %s/check", *service); return 0; } /* if (!(s.st_mode & S_IXUSR)) return 1; */ prog[0] = (char*)"./check"; prog[1] = NULL; pid = spawn(prog); if (pid <= 0) { bb_perror_msg(WARN"can't %s child %s/check", "run", *service); return 0; } while (safe_waitpid(pid, &w, 0) == -1) { bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service); return 0; } return WEXITSTATUS(w) == 0; } static int check(const char *a) { int r; unsigned pid_le32; uint64_t timestamp; r = svstatus_get(); if (r == -1) return -1; if (r == 0) { if (*a == 'x') return 1; return -1; } pid_le32 = svstatus.pid_le32; switch (*a) { case 'x': return 0; case 'u': if (!pid_le32 || svstatus.run_or_finish != 1) return 0; if (!checkscript()) return 0; break; case 'd': if (pid_le32) return 0; break; case 'c': if (pid_le32 && !checkscript()) return 0; break; case 't': if (!pid_le32 && svstatus.want == 'd') break; timestamp = SWAP_BE64(svstatus.time_be64); if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript()) return 0; break; case 'o': timestamp = SWAP_BE64(svstatus.time_be64); if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd')) return 0; } printf(OK); svstatus_print(*service); bb_putchar('\n'); /* will also flush the output */ return 1; } static int control(const char *a) { int fd, r, l; /* Is it an optimization? It causes problems with "sv o SRV; ...; sv d SRV" ('d' is not passed to SRV because its .want == 'd'): if (svstatus_get() <= 0) return -1; if (svstatus.want == *a) return 0; */ fd = open("supervise/control", O_WRONLY|O_NDELAY); if (fd == -1) { if (errno != ENODEV) warn("can't open supervise/control"); else *a == 'x' ? ok("runsv not running") : failx("runsv not running"); return -1; } l = strlen(a); r = write(fd, a, l); close(fd); if (r != l) { warn("can't write to supervise/control"); return -1; } return 1; } int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int sv_main(int argc UNUSED_PARAM, char **argv) { char *x; char *action; const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR; unsigned waitsec = 7; smallint kll = 0; int verbose = 0; int (*act)(const char*); int (*cbk)(const char*); int curdir; INIT_G(); xfunc_error_retval = 100; x = getenv("SVDIR"); if (x) varservice = x; x = getenv("SVWAIT"); if (x) waitsec = xatou(x); opt_complementary = "w+:vv"; /* -w N, -v is a counter */ getopt32(argv, "w:v", &waitsec, &verbose); argv += optind; action = *argv++; if (!action || !*argv) bb_show_usage(); tnow = time(NULL) + 0x400000000000000aULL; tstart = tnow; curdir = open(".", O_RDONLY|O_NDELAY); if (curdir == -1) fatal_cannot("open current directory"); act = &control; acts = "s"; cbk = ✓ switch (*action) { case 'x': case 'e': acts = "x"; if (!verbose) cbk = NULL; break; case 'X': case 'E': acts = "x"; kll = 1; break; case 'D': acts = "d"; kll = 1; break; case 'T': acts = "tc"; kll = 1; break; case 'c': if (str_equal(action, "check")) { act = NULL; acts = "c"; break; } case 'u': case 'd': case 'o': case 't': case 'p': case 'h': case 'a': case 'i': case 'k': case 'q': case '1': case '2': action[1] = '\0'; acts = action; if (!verbose) cbk = NULL; break; case 's': if (str_equal(action, "shutdown")) { acts = "x"; break; } if (str_equal(action, "start")) { acts = "u"; break; } if (str_equal(action, "stop")) { acts = "d"; break; } /* "status" */ act = &status; cbk = NULL; break; case 'r': if (str_equal(action, "restart")) { acts = "tcu"; break; } bb_show_usage(); case 'f': if (str_equal(action, "force-reload")) { acts = "tc"; kll = 1; break; } if (str_equal(action, "force-restart")) { acts = "tcu"; kll = 1; break; } if (str_equal(action, "force-shutdown")) { acts = "x"; kll = 1; break; } if (str_equal(action, "force-stop")) { acts = "d"; kll = 1; break; } default: bb_show_usage(); } service = argv; while ((x = *service) != NULL) { if (x[0] != '/' && x[0] != '.') { if (chdir(varservice) == -1) goto chdir_failed_0; } if (chdir(x) == -1) { chdir_failed_0: fail("can't change to service directory"); goto nullify_service_0; } if (act && (act(acts) == -1)) { nullify_service_0: *service = (char*) -1L; /* "dead" */ } if (fchdir(curdir) == -1) fatal_cannot("change to original directory"); service++; } if (cbk) while (1) { int want_exit; int diff; diff = tnow - tstart; service = argv; want_exit = 1; while ((x = *service) != NULL) { if (x == (char*) -1L) /* "dead" */ goto next; if (x[0] != '/' && x[0] != '.') { if (chdir(varservice) == -1) goto chdir_failed; } if (chdir(x) == -1) { chdir_failed: fail("can't change to service directory"); goto nullify_service; } if (cbk(acts) != 0) goto nullify_service; want_exit = 0; if (diff >= waitsec) { printf(kll ? "kill: " : "timeout: "); if (svstatus_get() > 0) { svstatus_print(x); ++rc; } bb_putchar('\n'); /* will also flush the output */ if (kll) control("k"); nullify_service: *service = (char*) -1L; /* "dead" */ } if (fchdir(curdir) == -1) fatal_cannot("change to original directory"); next: service++; } if (want_exit) break; usleep(420000); tnow = time(NULL) + 0x400000000000000aULL; } return rc > 99 ? 99 : rc; } ������������������������������������������������������������������busybox-1.22.1/runit/runsvdir.c���������������������������������������������������������������������0000644�0000000�0000000�00000023707�12263563520�015164� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2001-2006, Gerrit Pape 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. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. */ /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */ /* TODO: depends on runit_lib.c - review and reduce/eliminate */ //usage:#define runsvdir_trivial_usage //usage: "[-P] [-s SCRIPT] DIR" //usage:#define runsvdir_full_usage "\n\n" //usage: "Start a runsv process for each subdirectory. If it exits, restart it.\n" //usage: "\n -P Put each runsv in a new session" //usage: "\n -s SCRIPT Run SCRIPT <signo> after signal is processed" #include <sys/file.h> #include "libbb.h" #include "runit_lib.h" #define MAXSERVICES 1000 /* Should be not needed - all dirs are on same FS, right? */ #define CHECK_DEVNO_TOO 0 struct service { #if CHECK_DEVNO_TOO dev_t dev; #endif ino_t ino; pid_t pid; smallint isgone; }; struct globals { struct service *sv; char *svdir; int svnum; #if ENABLE_FEATURE_RUNSVDIR_LOG char *rplog; int rploglen; struct fd_pair logpipe; struct pollfd pfd[1]; unsigned stamplog; #endif } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define sv (G.sv ) #define svdir (G.svdir ) #define svnum (G.svnum ) #define rplog (G.rplog ) #define rploglen (G.rploglen ) #define logpipe (G.logpipe ) #define pfd (G.pfd ) #define stamplog (G.stamplog ) #define INIT_G() do { } while (0) static void fatal2_cannot(const char *m1, const char *m2) { bb_perror_msg_and_die("%s: fatal: can't %s%s", svdir, m1, m2); /* was exiting 100 */ } static void warn3x(const char *m1, const char *m2, const char *m3) { bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3); } static void warn2_cannot(const char *m1, const char *m2) { warn3x("can't ", m1, m2); } #if ENABLE_FEATURE_RUNSVDIR_LOG static void warnx(const char *m1) { warn3x(m1, "", ""); } #endif /* inlining + vfork -> bigger code */ static NOINLINE pid_t runsv(const char *name) { pid_t pid; /* If we got signaled, stop spawning children at once! */ if (bb_got_signal) return 0; pid = vfork(); if (pid == -1) { warn2_cannot("vfork", ""); return 0; } if (pid == 0) { /* child */ if (option_mask32 & 1) /* -P option? */ setsid(); /* man execv: * "Signals set to be caught by the calling process image * shall be set to the default action in the new process image." * Therefore, we do not need this: */ #if 0 bb_signals(0 | (1 << SIGHUP) | (1 << SIGTERM) , SIG_DFL); #endif execlp("runsv", "runsv", name, (char *) NULL); fatal2_cannot("start runsv ", name); } return pid; } /* gcc 4.3.0 does better with NOINLINE */ static NOINLINE int do_rescan(void) { DIR *dir; struct dirent *d; int i; struct stat s; int need_rescan = 0; dir = opendir("."); if (!dir) { warn2_cannot("open directory ", svdir); return 1; /* need to rescan again soon */ } for (i = 0; i < svnum; i++) sv[i].isgone = 1; while (1) { errno = 0; d = readdir(dir); if (!d) break; if (d->d_name[0] == '.') continue; if (stat(d->d_name, &s) == -1) { warn2_cannot("stat ", d->d_name); continue; } if (!S_ISDIR(s.st_mode)) continue; /* Do we have this service listed already? */ for (i = 0; i < svnum; i++) { if ((sv[i].ino == s.st_ino) #if CHECK_DEVNO_TOO && (sv[i].dev == s.st_dev) #endif ) { if (sv[i].pid == 0) /* restart if it has died */ goto run_ith_sv; sv[i].isgone = 0; /* "we still see you" */ goto next_dentry; } } { /* Not found, make new service */ struct service *svnew = realloc(sv, (i+1) * sizeof(*sv)); if (!svnew) { warn2_cannot("start runsv ", d->d_name); need_rescan = 1; continue; } sv = svnew; svnum++; #if CHECK_DEVNO_TOO sv[i].dev = s.st_dev; #endif sv[i].ino = s.st_ino; run_ith_sv: sv[i].pid = runsv(d->d_name); sv[i].isgone = 0; } next_dentry: ; } i = errno; closedir(dir); if (i) { /* readdir failed */ warn2_cannot("read directory ", svdir); return 1; /* need to rescan again soon */ } /* Send SIGTERM to runsv whose directories * were no longer found (-> must have been removed) */ for (i = 0; i < svnum; i++) { if (!sv[i].isgone) continue; if (sv[i].pid) kill(sv[i].pid, SIGTERM); svnum--; sv[i] = sv[svnum]; i--; /* so that we don't skip new sv[i] (bug was here!) */ } return need_rescan; } int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int runsvdir_main(int argc UNUSED_PARAM, char **argv) { struct stat s; dev_t last_dev = last_dev; /* for gcc */ ino_t last_ino = last_ino; /* for gcc */ time_t last_mtime = 0; int wstat; int curdir; pid_t pid; unsigned deadline; unsigned now; unsigned stampcheck; int i; int need_rescan = 1; char *opt_s_argv[3]; INIT_G(); opt_complementary = "-1"; opt_s_argv[0] = NULL; opt_s_argv[2] = NULL; getopt32(argv, "Ps:", &opt_s_argv[0]); argv += optind; bb_signals(0 | (1 << SIGTERM) | (1 << SIGHUP) /* For busybox's init, SIGTERM == reboot, * SIGUSR1 == halt * SIGUSR2 == poweroff * so we need to intercept SIGUSRn too. * Note that we do not implement actual reboot * (killall(TERM) + umount, etc), we just pause * respawing and avoid exiting (-> making kernel oops). * The user is responsible for the rest. */ | (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0) , record_signo); svdir = *argv++; #if ENABLE_FEATURE_RUNSVDIR_LOG /* setup log */ if (*argv) { rplog = *argv; rploglen = strlen(rplog); if (rploglen < 7) { warnx("log must have at least seven characters"); } else if (piped_pair(logpipe)) { warnx("can't create pipe for log"); } else { close_on_exec_on(logpipe.rd); close_on_exec_on(logpipe.wr); ndelay_on(logpipe.rd); ndelay_on(logpipe.wr); if (dup2(logpipe.wr, 2) == -1) { warnx("can't set filedescriptor for log"); } else { pfd[0].fd = logpipe.rd; pfd[0].events = POLLIN; stamplog = monotonic_sec(); goto run; } } rplog = NULL; warnx("log service disabled"); } run: #endif curdir = open(".", O_RDONLY|O_NDELAY); if (curdir == -1) fatal2_cannot("open current directory", ""); close_on_exec_on(curdir); stampcheck = monotonic_sec(); for (;;) { /* collect children */ for (;;) { pid = wait_any_nohang(&wstat); if (pid <= 0) break; for (i = 0; i < svnum; i++) { if (pid == sv[i].pid) { /* runsv has died */ sv[i].pid = 0; need_rescan = 1; } } } now = monotonic_sec(); if ((int)(now - stampcheck) >= 0) { /* wait at least a second */ stampcheck = now + 1; if (stat(svdir, &s) != -1) { if (need_rescan || s.st_mtime != last_mtime || s.st_ino != last_ino || s.st_dev != last_dev ) { /* svdir modified */ if (chdir(svdir) != -1) { last_mtime = s.st_mtime; last_dev = s.st_dev; last_ino = s.st_ino; /* if the svdir changed this very second, wait until the * next second, because we won't be able to detect more * changes within this second */ while (time(NULL) == last_mtime) usleep(100000); need_rescan = do_rescan(); while (fchdir(curdir) == -1) { warn2_cannot("change directory, pausing", ""); sleep(5); } } else { warn2_cannot("change directory to ", svdir); } } } else { warn2_cannot("stat ", svdir); } } #if ENABLE_FEATURE_RUNSVDIR_LOG if (rplog) { if ((int)(now - stamplog) >= 0) { write(logpipe.wr, ".", 1); stamplog = now + 900; } } pfd[0].revents = 0; #endif deadline = (need_rescan ? 1 : 5); sig_block(SIGCHLD); #if ENABLE_FEATURE_RUNSVDIR_LOG if (rplog) poll(pfd, 1, deadline*1000); else #endif sleep(deadline); sig_unblock(SIGCHLD); #if ENABLE_FEATURE_RUNSVDIR_LOG if (pfd[0].revents & POLLIN) { char ch; while (read(logpipe.rd, &ch, 1) > 0) { if (ch < ' ') ch = ' '; for (i = 6; i < rploglen; i++) rplog[i-1] = rplog[i]; rplog[rploglen-1] = ch; } } #endif if (!bb_got_signal) continue; /* -s SCRIPT: useful if we are init. * In this case typically script never returns, * it halts/powers off/reboots the system. */ if (opt_s_argv[0]) { /* Single parameter: signal# */ opt_s_argv[1] = utoa(bb_got_signal); pid = spawn(opt_s_argv); if (pid > 0) { /* Remembering to wait for _any_ children, * not just pid */ while (wait(NULL) != pid) continue; } } if (bb_got_signal == SIGHUP) { for (i = 0; i < svnum; i++) if (sv[i].pid) kill(sv[i].pid, SIGTERM); } /* SIGHUP or SIGTERM (or SIGUSRn if we are init) */ /* Exit unless we are init */ if (getpid() != 1) return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS; /* init continues to monitor services forever */ bb_got_signal = 0; } /* for (;;) */ } ���������������������������������������������������������busybox-1.22.1/runit/chpst.c������������������������������������������������������������������������0000644�0000000�0000000�00000030526�12263563520�014426� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2001-2006, Gerrit Pape 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. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. */ /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */ /* Dependencies on runit_lib.c removed */ //usage:#define chpst_trivial_usage //usage: "[-vP012] [-u USER[:GRP]] [-U USER[:GRP]] [-e DIR]\n" //usage: " [-/ DIR] [-n NICE] [-m BYTES] [-d BYTES] [-o N]\n" //usage: " [-p N] [-f BYTES] [-c BYTES] PROG ARGS" //usage:#define chpst_full_usage "\n\n" //usage: "Change the process state, run PROG\n" //usage: "\n -u USER[:GRP] Set uid and gid" //usage: "\n -U USER[:GRP] Set $UID and $GID in environment" //usage: "\n -e DIR Set environment variables as specified by files" //usage: "\n in DIR: file=1st_line_of_file" //usage: "\n -/ DIR Chroot to DIR" //usage: "\n -n NICE Add NICE to nice value" //usage: "\n -m BYTES Same as -d BYTES -s BYTES -l BYTES" //usage: "\n -d BYTES Limit data segment" //usage: "\n -o N Limit number of open files per process" //usage: "\n -p N Limit number of processes per uid" //usage: "\n -f BYTES Limit output file sizes" //usage: "\n -c BYTES Limit core file size" //usage: "\n -v Verbose" //usage: "\n -P Create new process group" //usage: "\n -0 Close stdin" //usage: "\n -1 Close stdout" //usage: "\n -2 Close stderr" //usage: //usage:#define envdir_trivial_usage //usage: "DIR PROG ARGS" //usage:#define envdir_full_usage "\n\n" //usage: "Set various environment variables as specified by files\n" //usage: "in the directory DIR, run PROG" //usage: //usage:#define envuidgid_trivial_usage //usage: "USER PROG ARGS" //usage:#define envuidgid_full_usage "\n\n" //usage: "Set $UID to USER's uid and $GID to USER's gid, run PROG" //usage: //usage:#define setuidgid_trivial_usage //usage: "USER PROG ARGS" //usage:#define setuidgid_full_usage "\n\n" //usage: "Set uid and gid to USER's uid and gid, drop supplementary group ids,\n" //usage: "run PROG" //usage: //usage:#define softlimit_trivial_usage //usage: "[-a BYTES] [-m BYTES] [-d BYTES] [-s BYTES] [-l BYTES]\n" //usage: " [-f BYTES] [-c BYTES] [-r BYTES] [-o N] [-p N] [-t N]\n" //usage: " PROG ARGS" //usage:#define softlimit_full_usage "\n\n" //usage: "Set soft resource limits, then run PROG\n" //usage: "\n -a BYTES Limit total size of all segments" //usage: "\n -m BYTES Same as -d BYTES -s BYTES -l BYTES -a BYTES" //usage: "\n -d BYTES Limit data segment" //usage: "\n -s BYTES Limit stack segment" //usage: "\n -l BYTES Limit locked memory size" //usage: "\n -o N Limit number of open files per process" //usage: "\n -p N Limit number of processes per uid" //usage: "\nOptions controlling file sizes:" //usage: "\n -f BYTES Limit output file sizes" //usage: "\n -c BYTES Limit core file size" //usage: "\nEfficiency opts:" //usage: "\n -r BYTES Limit resident set size" //usage: "\n -t N Limit CPU time, process receives" //usage: "\n a SIGXCPU after N seconds" #include "libbb.h" #include <sys/resource.h> /* getrlimit */ /* Five applets here: chpst, envdir, envuidgid, setuidgid, softlimit. Only softlimit and chpst are taking options: # common -o N Limit number of open files per process -p N Limit number of processes per uid -m BYTES Same as -d BYTES -s BYTES -l BYTES [-a BYTES] -d BYTES Limit data segment -f BYTES Limit output file sizes -c BYTES Limit core file size # softlimit -a BYTES Limit total size of all segments -s BYTES Limit stack segment -l BYTES Limit locked memory size -r BYTES Limit resident set size -t N Limit CPU time # chpst -u USER[:GRP] Set uid and gid -U USER[:GRP] Set $UID and $GID in environment -e DIR Set environment variables as specified by files in DIR -/ DIR Chroot to DIR -n NICE Add NICE to nice value -v Verbose -P Create new process group -0 -1 -2 Close fd 0,1,2 Even though we accept all these options for both softlimit and chpst, they are not to be advertised on their help texts. We have enough problems with feature creep in other people's software, don't want to add our own. envdir, envuidgid, setuidgid take no options, but they reuse code which handles -e, -U and -u. */ enum { OPT_a = (1 << 0) * ENABLE_SOFTLIMIT, OPT_c = (1 << 1) * (ENABLE_SOFTLIMIT || ENABLE_CHPST), OPT_d = (1 << 2) * (ENABLE_SOFTLIMIT || ENABLE_CHPST), OPT_f = (1 << 3) * (ENABLE_SOFTLIMIT || ENABLE_CHPST), OPT_l = (1 << 4) * ENABLE_SOFTLIMIT, OPT_m = (1 << 5) * (ENABLE_SOFTLIMIT || ENABLE_CHPST), OPT_o = (1 << 6) * (ENABLE_SOFTLIMIT || ENABLE_CHPST), OPT_p = (1 << 7) * (ENABLE_SOFTLIMIT || ENABLE_CHPST), OPT_r = (1 << 8) * ENABLE_SOFTLIMIT, OPT_s = (1 << 9) * ENABLE_SOFTLIMIT, OPT_t = (1 << 10) * ENABLE_SOFTLIMIT, OPT_u = (1 << 11) * (ENABLE_CHPST || ENABLE_SETUIDGID), OPT_U = (1 << 12) * (ENABLE_CHPST || ENABLE_ENVUIDGID), OPT_e = (1 << 13) * (ENABLE_CHPST || ENABLE_ENVDIR), OPT_root = (1 << 14) * ENABLE_CHPST, OPT_n = (1 << 15) * ENABLE_CHPST, OPT_v = (1 << 16) * ENABLE_CHPST, OPT_P = (1 << 17) * ENABLE_CHPST, OPT_0 = (1 << 18) * ENABLE_CHPST, OPT_1 = (1 << 19) * ENABLE_CHPST, OPT_2 = (1 << 20) * ENABLE_CHPST, }; /* TODO: use recursive_action? */ static NOINLINE void edir(const char *directory_name) { int wdir; DIR *dir; struct dirent *d; int fd; wdir = xopen(".", O_RDONLY | O_NDELAY); xchdir(directory_name); dir = xopendir("."); for (;;) { char buf[256]; char *tail; int size; errno = 0; d = readdir(dir); if (!d) { if (errno) bb_perror_msg_and_die("readdir %s", directory_name); break; } if (d->d_name[0] == '.') continue; fd = open(d->d_name, O_RDONLY | O_NDELAY); if (fd < 0) { if ((errno == EISDIR) && directory_name) { if (option_mask32 & OPT_v) bb_perror_msg("warning: %s/%s is a directory", directory_name, d->d_name); continue; } bb_perror_msg_and_die("open %s/%s", directory_name, d->d_name); } size = full_read(fd, buf, sizeof(buf)-1); close(fd); if (size < 0) bb_perror_msg_and_die("read %s/%s", directory_name, d->d_name); if (size == 0) { unsetenv(d->d_name); continue; } buf[size] = '\n'; tail = strchr(buf, '\n'); /* skip trailing whitespace */ while (1) { *tail = '\0'; tail--; if (tail < buf || !isspace(*tail)) break; } xsetenv(d->d_name, buf); } closedir(dir); if (fchdir(wdir) == -1) bb_perror_msg_and_die("fchdir"); close(wdir); } static void limit(int what, long l) { struct rlimit r; /* Never fails under Linux (except if you pass it bad arguments) */ getrlimit(what, &r); if ((l < 0) || (l > r.rlim_max)) r.rlim_cur = r.rlim_max; else r.rlim_cur = l; if (setrlimit(what, &r) == -1) bb_perror_msg_and_die("setrlimit"); } int chpst_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int chpst_main(int argc UNUSED_PARAM, char **argv) { struct bb_uidgid_t ugid; char *set_user = set_user; /* for compiler */ char *env_dir = env_dir; char *root; char *nicestr; unsigned limita; unsigned limitc; unsigned limitd; unsigned limitf; unsigned limitl; unsigned limitm; unsigned limito; unsigned limitp; unsigned limitr; unsigned limits; unsigned limitt; unsigned opt; if ((ENABLE_CHPST && applet_name[0] == 'c') || (ENABLE_SOFTLIMIT && applet_name[1] == 'o') ) { // FIXME: can we live with int-sized limits? // can we live with 40000 days? // if yes -> getopt converts strings to numbers for us opt_complementary = "-1:a+:c+:d+:f+:l+:m+:o+:p+:r+:s+:t+"; opt = getopt32(argv, "+a:c:d:f:l:m:o:p:r:s:t:u:U:e:" IF_CHPST("/:n:vP012"), &limita, &limitc, &limitd, &limitf, &limitl, &limitm, &limito, &limitp, &limitr, &limits, &limitt, &set_user, &set_user, &env_dir IF_CHPST(, &root, &nicestr)); argv += optind; if (opt & OPT_m) { // -m means -asld limita = limits = limitl = limitd = limitm; opt |= (OPT_s | OPT_l | OPT_a | OPT_d); } } else { option_mask32 = opt = 0; argv++; if (!*argv) bb_show_usage(); } // envdir? if (ENABLE_ENVDIR && applet_name[3] == 'd') { env_dir = *argv++; opt |= OPT_e; } // setuidgid? if (ENABLE_SETUIDGID && applet_name[1] == 'e') { set_user = *argv++; opt |= OPT_u; } // envuidgid? if (ENABLE_ENVUIDGID && applet_name[0] == 'e' && applet_name[3] == 'u') { set_user = *argv++; opt |= OPT_U; } // we must have PROG [ARGS] if (!*argv) bb_show_usage(); // set limits if (opt & OPT_d) { #ifdef RLIMIT_DATA limit(RLIMIT_DATA, limitd); #else if (opt & OPT_v) bb_error_msg("system does not support RLIMIT_%s", "DATA"); #endif } if (opt & OPT_s) { #ifdef RLIMIT_STACK limit(RLIMIT_STACK, limits); #else if (opt & OPT_v) bb_error_msg("system does not support RLIMIT_%s", "STACK"); #endif } if (opt & OPT_l) { #ifdef RLIMIT_MEMLOCK limit(RLIMIT_MEMLOCK, limitl); #else if (opt & OPT_v) bb_error_msg("system does not support RLIMIT_%s", "MEMLOCK"); #endif } if (opt & OPT_a) { #ifdef RLIMIT_VMEM limit(RLIMIT_VMEM, limita); #else #ifdef RLIMIT_AS limit(RLIMIT_AS, limita); #else if (opt & OPT_v) bb_error_msg("system does not support RLIMIT_%s", "VMEM"); #endif #endif } if (opt & OPT_o) { #ifdef RLIMIT_NOFILE limit(RLIMIT_NOFILE, limito); #else #ifdef RLIMIT_OFILE limit(RLIMIT_OFILE, limito); #else if (opt & OPT_v) bb_error_msg("system does not support RLIMIT_%s", "NOFILE"); #endif #endif } if (opt & OPT_p) { #ifdef RLIMIT_NPROC limit(RLIMIT_NPROC, limitp); #else if (opt & OPT_v) bb_error_msg("system does not support RLIMIT_%s", "NPROC"); #endif } if (opt & OPT_f) { #ifdef RLIMIT_FSIZE limit(RLIMIT_FSIZE, limitf); #else if (opt & OPT_v) bb_error_msg("system does not support RLIMIT_%s", "FSIZE"); #endif } if (opt & OPT_c) { #ifdef RLIMIT_CORE limit(RLIMIT_CORE, limitc); #else if (opt & OPT_v) bb_error_msg("system does not support RLIMIT_%s", "CORE"); #endif } if (opt & OPT_r) { #ifdef RLIMIT_RSS limit(RLIMIT_RSS, limitr); #else if (opt & OPT_v) bb_error_msg("system does not support RLIMIT_%s", "RSS"); #endif } if (opt & OPT_t) { #ifdef RLIMIT_CPU limit(RLIMIT_CPU, limitt); #else if (opt & OPT_v) bb_error_msg("system does not support RLIMIT_%s", "CPU"); #endif } if (opt & OPT_P) setsid(); if (opt & OPT_e) edir(env_dir); if (opt & (OPT_u|OPT_U)) xget_uidgid(&ugid, set_user); // chrooted jail must have /etc/passwd if we move this after chroot. // OTOH chroot fails for non-roots. // Solution: cache uid/gid before chroot, apply uid/gid after. if (opt & OPT_U) { xsetenv("GID", utoa(ugid.gid)); xsetenv("UID", utoa(ugid.uid)); } if (opt & OPT_root) { xchroot(root); } if (opt & OPT_u) { if (setgroups(1, &ugid.gid) == -1) bb_perror_msg_and_die("setgroups"); xsetgid(ugid.gid); xsetuid(ugid.uid); } if (opt & OPT_n) { errno = 0; if (nice(xatoi(nicestr)) == -1) bb_perror_msg_and_die("nice"); } if (opt & OPT_0) close(STDIN_FILENO); if (opt & OPT_1) close(STDOUT_FILENO); if (opt & OPT_2) close(STDERR_FILENO); BB_EXECVP_or_die(argv); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/runit/runsv.c������������������������������������������������������������������������0000644�0000000�0000000�00000040111�12263563520�014451� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2001-2006, Gerrit Pape 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. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. */ /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */ /* TODO: depends on runit_lib.c - review and reduce/eliminate */ //usage:#define runsv_trivial_usage //usage: "DIR" //usage:#define runsv_full_usage "\n\n" //usage: "Start and monitor a service and optionally an appendant log service" #include <sys/file.h> #include "libbb.h" #include "runit_lib.h" #if ENABLE_MONOTONIC_SYSCALL #include <sys/syscall.h> /* libc has incredibly messy way of doing this, * typically requiring -lrt. We just skip all this mess */ static void gettimeofday_ns(struct timespec *ts) { syscall(__NR_clock_gettime, CLOCK_REALTIME, ts); } #else static void gettimeofday_ns(struct timespec *ts) { if (sizeof(struct timeval) == sizeof(struct timespec) && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec) ) { /* Cheat */ gettimeofday((void*)ts, NULL); ts->tv_nsec *= 1000; } else { extern void BUG_need_to_implement_gettimeofday_ns(void); BUG_need_to_implement_gettimeofday_ns(); } } #endif /* Compare possibly overflowing unsigned counters */ #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0) /* state */ #define S_DOWN 0 #define S_RUN 1 #define S_FINISH 2 /* ctrl */ #define C_NOOP 0 #define C_TERM 1 #define C_PAUSE 2 /* want */ #define W_UP 0 #define W_DOWN 1 #define W_EXIT 2 struct svdir { int pid; smallint state; smallint ctrl; smallint sd_want; smallint islog; struct timespec start; int fdlock; int fdcontrol; int fdcontrolwrite; int wstat; }; struct globals { smallint haslog; smallint sigterm; smallint pidchanged; struct fd_pair selfpipe; struct fd_pair logpipe; char *dir; struct svdir svd[2]; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define haslog (G.haslog ) #define sigterm (G.sigterm ) #define pidchanged (G.pidchanged ) #define selfpipe (G.selfpipe ) #define logpipe (G.logpipe ) #define dir (G.dir ) #define svd (G.svd ) #define INIT_G() do { \ pidchanged = 1; \ } while (0) static void fatal2_cannot(const char *m1, const char *m2) { bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2); /* was exiting 111 */ } static void fatal_cannot(const char *m) { fatal2_cannot(m, ""); /* was exiting 111 */ } static void fatal2x_cannot(const char *m1, const char *m2) { bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2); /* was exiting 111 */ } static void warn_cannot(const char *m) { bb_perror_msg("%s: warning: cannot %s", dir, m); } static void s_child(int sig_no UNUSED_PARAM) { write(selfpipe.wr, "", 1); } static void s_term(int sig_no UNUSED_PARAM) { sigterm = 1; write(selfpipe.wr, "", 1); /* XXX */ } static int open_trunc_or_warn(const char *name) { /* Why O_NDELAY? */ int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644); if (fd < 0) bb_perror_msg("%s: warning: cannot open %s", dir, name); return fd; } static void update_status(struct svdir *s) { ssize_t sz; int fd; svstatus_t status; /* pid */ if (pidchanged) { fd = open_trunc_or_warn("supervise/pid.new"); if (fd < 0) return; if (s->pid) { char spid[sizeof(int)*3 + 2]; int size = sprintf(spid, "%u\n", (unsigned)s->pid); write(fd, spid, size); } close(fd); if (rename_or_warn("supervise/pid.new", s->islog ? "log/supervise/pid" : "log/supervise/pid"+4)) return; pidchanged = 0; } /* stat */ fd = open_trunc_or_warn("supervise/stat.new"); if (fd < -1) return; { char stat_buf[sizeof("finish, paused, got TERM, want down\n")]; char *p = stat_buf; switch (s->state) { case S_DOWN: p = stpcpy(p, "down"); break; case S_RUN: p = stpcpy(p, "run"); break; case S_FINISH: p = stpcpy(p, "finish"); break; } if (s->ctrl & C_PAUSE) p = stpcpy(p, ", paused"); if (s->ctrl & C_TERM) p = stpcpy(p, ", got TERM"); if (s->state != S_DOWN) switch (s->sd_want) { case W_DOWN: p = stpcpy(p, ", want down"); break; case W_EXIT: p = stpcpy(p, ", want exit"); break; } *p++ = '\n'; write(fd, stat_buf, p - stat_buf); close(fd); } rename_or_warn("supervise/stat.new", s->islog ? "log/supervise/stat" : "log/supervise/stat"+4); /* supervise compatibility */ memset(&status, 0, sizeof(status)); status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL); status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec); status.pid_le32 = SWAP_LE32(s->pid); if (s->ctrl & C_PAUSE) status.paused = 1; if (s->sd_want == W_UP) status.want = 'u'; else status.want = 'd'; if (s->ctrl & C_TERM) status.got_term = 1; status.run_or_finish = s->state; fd = open_trunc_or_warn("supervise/status.new"); if (fd < 0) return; sz = write(fd, &status, sizeof(status)); close(fd); if (sz != sizeof(status)) { warn_cannot("write supervise/status.new"); unlink("supervise/status.new"); return; } rename_or_warn("supervise/status.new", s->islog ? "log/supervise/status" : "log/supervise/status"+4); } static unsigned custom(struct svdir *s, char c) { pid_t pid; int w; char a[10]; struct stat st; if (s->islog) return 0; strcpy(a, "control/?"); a[8] = c; /* replace '?' */ if (stat(a, &st) == 0) { if (st.st_mode & S_IXUSR) { pid = vfork(); if (pid == -1) { warn_cannot("vfork for control/?"); return 0; } if (pid == 0) { /* child */ if (haslog && dup2(logpipe.wr, 1) == -1) warn_cannot("setup stdout for control/?"); execl(a, a, (char *) NULL); fatal_cannot("run control/?"); } /* parent */ if (safe_waitpid(pid, &w, 0) == -1) { warn_cannot("wait for child control/?"); return 0; } return WEXITSTATUS(w) == 0; } } else { if (errno != ENOENT) warn_cannot("stat control/?"); } return 0; } static void stopservice(struct svdir *s) { if (s->pid && !custom(s, 't')) { kill(s->pid, SIGTERM); s->ctrl |= C_TERM; update_status(s); } if (s->sd_want == W_DOWN) { kill(s->pid, SIGCONT); custom(s, 'd'); return; } if (s->sd_want == W_EXIT) { kill(s->pid, SIGCONT); custom(s, 'x'); } } static void startservice(struct svdir *s) { int p; const char *arg[4]; char exitcode[sizeof(int)*3 + 2]; if (s->state == S_FINISH) { /* Two arguments are given to ./finish. The first one is ./run exit code, * or -1 if ./run didnt exit normally. The second one is * the least significant byte of the exit status as determined by waitpid; * for instance it is 0 if ./run exited normally, and the signal number * if ./run was terminated by a signal. If runsv cannot start ./run * for some reason, the exit code is 111 and the status is 0. */ arg[0] = "./finish"; arg[1] = "-1"; if (WIFEXITED(s->wstat)) { *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0'; arg[1] = exitcode; } //arg[2] = "0"; //if (WIFSIGNALED(s->wstat)) { arg[2] = utoa(WTERMSIG(s->wstat)); //} arg[3] = NULL; } else { arg[0] = "./run"; arg[1] = NULL; custom(s, 'u'); } if (s->pid != 0) stopservice(s); /* should never happen */ while ((p = vfork()) == -1) { warn_cannot("vfork, sleeping"); sleep(5); } if (p == 0) { /* child */ if (haslog) { /* NB: bug alert! right order is close, then dup2 */ if (s->islog) { xchdir("./log"); close(logpipe.wr); xdup2(logpipe.rd, 0); } else { close(logpipe.rd); xdup2(logpipe.wr, 1); } } /* Non-ignored signals revert to SIG_DFL on exec anyway */ /*bb_signals(0 + (1 << SIGCHLD) + (1 << SIGTERM) , SIG_DFL);*/ sig_unblock(SIGCHLD); sig_unblock(SIGTERM); execv(arg[0], (char**) arg); fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]); } /* parent */ if (s->state != S_FINISH) { gettimeofday_ns(&s->start); s->state = S_RUN; } s->pid = p; pidchanged = 1; s->ctrl = C_NOOP; update_status(s); } static int ctrl(struct svdir *s, char c) { int sig; switch (c) { case 'd': /* down */ s->sd_want = W_DOWN; update_status(s); if (s->pid && s->state != S_FINISH) stopservice(s); break; case 'u': /* up */ s->sd_want = W_UP; update_status(s); if (s->pid == 0) startservice(s); break; case 'x': /* exit */ if (s->islog) break; s->sd_want = W_EXIT; update_status(s); /* FALLTHROUGH */ case 't': /* sig term */ if (s->pid && s->state != S_FINISH) stopservice(s); break; case 'k': /* sig kill */ if (s->pid && !custom(s, c)) kill(s->pid, SIGKILL); s->state = S_DOWN; break; case 'p': /* sig pause */ if (s->pid && !custom(s, c)) kill(s->pid, SIGSTOP); s->ctrl |= C_PAUSE; update_status(s); break; case 'c': /* sig cont */ if (s->pid && !custom(s, c)) kill(s->pid, SIGCONT); s->ctrl &= ~C_PAUSE; update_status(s); break; case 'o': /* once */ s->sd_want = W_DOWN; update_status(s); if (!s->pid) startservice(s); break; case 'a': /* sig alarm */ sig = SIGALRM; goto sendsig; case 'h': /* sig hup */ sig = SIGHUP; goto sendsig; case 'i': /* sig int */ sig = SIGINT; goto sendsig; case 'q': /* sig quit */ sig = SIGQUIT; goto sendsig; case '1': /* sig usr1 */ sig = SIGUSR1; goto sendsig; case '2': /* sig usr2 */ sig = SIGUSR2; goto sendsig; } return 1; sendsig: if (s->pid && !custom(s, c)) kill(s->pid, sig); return 1; } int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int runsv_main(int argc UNUSED_PARAM, char **argv) { struct stat s; int fd; int r; char buf[256]; INIT_G(); dir = single_argv(argv); xpiped_pair(selfpipe); close_on_exec_on(selfpipe.rd); close_on_exec_on(selfpipe.wr); ndelay_on(selfpipe.rd); ndelay_on(selfpipe.wr); sig_block(SIGCHLD); bb_signals_recursive_norestart(1 << SIGCHLD, s_child); sig_block(SIGTERM); bb_signals_recursive_norestart(1 << SIGTERM, s_term); xchdir(dir); /* bss: svd[0].pid = 0; */ if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */ if (C_NOOP) svd[0].ctrl = C_NOOP; if (W_UP) svd[0].sd_want = W_UP; /* bss: svd[0].islog = 0; */ /* bss: svd[1].pid = 0; */ gettimeofday_ns(&svd[0].start); if (stat("down", &s) != -1) svd[0].sd_want = W_DOWN; if (stat("log", &s) == -1) { if (errno != ENOENT) warn_cannot("stat ./log"); } else { if (!S_ISDIR(s.st_mode)) { errno = 0; warn_cannot("stat log/down: log is not a directory"); } else { haslog = 1; svd[1].state = S_DOWN; svd[1].ctrl = C_NOOP; svd[1].sd_want = W_UP; svd[1].islog = 1; gettimeofday_ns(&svd[1].start); if (stat("log/down", &s) != -1) svd[1].sd_want = W_DOWN; xpiped_pair(logpipe); close_on_exec_on(logpipe.rd); close_on_exec_on(logpipe.wr); } } if (mkdir("supervise", 0700) == -1) { r = readlink("supervise", buf, sizeof(buf)); if (r != -1) { if (r == sizeof(buf)) fatal2x_cannot("readlink ./supervise", ": name too long"); buf[r] = 0; mkdir(buf, 0700); } else { if ((errno != ENOENT) && (errno != EINVAL)) fatal_cannot("readlink ./supervise"); } } svd[0].fdlock = xopen3("log/supervise/lock"+4, O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600); if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1) fatal_cannot("lock supervise/lock"); close_on_exec_on(svd[0].fdlock); if (haslog) { if (mkdir("log/supervise", 0700) == -1) { r = readlink("log/supervise", buf, 256); if (r != -1) { if (r == 256) fatal2x_cannot("readlink ./log/supervise", ": name too long"); buf[r] = 0; fd = xopen(".", O_RDONLY|O_NDELAY); xchdir("./log"); mkdir(buf, 0700); if (fchdir(fd) == -1) fatal_cannot("change back to service directory"); close(fd); } else { if ((errno != ENOENT) && (errno != EINVAL)) fatal_cannot("readlink ./log/supervise"); } } svd[1].fdlock = xopen3("log/supervise/lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600); if (flock(svd[1].fdlock, LOCK_EX) == -1) fatal_cannot("lock log/supervise/lock"); close_on_exec_on(svd[1].fdlock); } mkfifo("log/supervise/control"+4, 0600); svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY); close_on_exec_on(svd[0].fdcontrol); svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY); close_on_exec_on(svd[0].fdcontrolwrite); update_status(&svd[0]); if (haslog) { mkfifo("log/supervise/control", 0600); svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY); close_on_exec_on(svd[1].fdcontrol); svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY); close_on_exec_on(svd[1].fdcontrolwrite); update_status(&svd[1]); } mkfifo("log/supervise/ok"+4, 0600); fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY); close_on_exec_on(fd); if (haslog) { mkfifo("log/supervise/ok", 0600); fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY); close_on_exec_on(fd); } for (;;) { struct pollfd x[3]; unsigned deadline; char ch; if (haslog) if (!svd[1].pid && svd[1].sd_want == W_UP) startservice(&svd[1]); if (!svd[0].pid) if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH) startservice(&svd[0]); x[0].fd = selfpipe.rd; x[0].events = POLLIN; x[1].fd = svd[0].fdcontrol; x[1].events = POLLIN; /* x[2] is used only if haslog == 1 */ x[2].fd = svd[1].fdcontrol; x[2].events = POLLIN; sig_unblock(SIGTERM); sig_unblock(SIGCHLD); poll(x, 2 + haslog, 3600*1000); sig_block(SIGTERM); sig_block(SIGCHLD); while (read(selfpipe.rd, &ch, 1) == 1) continue; for (;;) { pid_t child; int wstat; child = wait_any_nohang(&wstat); if (!child) break; if ((child == -1) && (errno != EINTR)) break; if (child == svd[0].pid) { svd[0].wstat = wstat; svd[0].pid = 0; pidchanged = 1; svd[0].ctrl &= ~C_TERM; if (svd[0].state != S_FINISH) { fd = open("finish", O_RDONLY|O_NDELAY); if (fd != -1) { close(fd); svd[0].state = S_FINISH; update_status(&svd[0]); continue; } } svd[0].state = S_DOWN; deadline = svd[0].start.tv_sec + 1; gettimeofday_ns(&svd[0].start); update_status(&svd[0]); if (LESS(svd[0].start.tv_sec, deadline)) sleep(1); } if (haslog) { if (child == svd[1].pid) { svd[0].wstat = wstat; svd[1].pid = 0; pidchanged = 1; svd[1].state = S_DOWN; svd[1].ctrl &= ~C_TERM; deadline = svd[1].start.tv_sec + 1; gettimeofday_ns(&svd[1].start); update_status(&svd[1]); if (LESS(svd[1].start.tv_sec, deadline)) sleep(1); } } } /* for (;;) */ if (read(svd[0].fdcontrol, &ch, 1) == 1) ctrl(&svd[0], ch); if (haslog) if (read(svd[1].fdcontrol, &ch, 1) == 1) ctrl(&svd[1], ch); if (sigterm) { ctrl(&svd[0], 'x'); sigterm = 0; } if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) { if (svd[1].pid == 0) _exit(EXIT_SUCCESS); if (svd[1].sd_want != W_EXIT) { svd[1].sd_want = W_EXIT; /* stopservice(&svd[1]); */ update_status(&svd[1]); close(logpipe.wr); close(logpipe.rd); } } } /* for (;;) */ /* not reached */ return 0; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/��������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�014100� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/scriptreplay.c������������������������������������������������������������0000644�0000000�0000000�00000002076�12263563520�016776� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * scriptreplay - play back typescripts, using timing information * * pascal.bellard@ads-lu.com * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * */ //usage:#define scriptreplay_trivial_usage //usage: "timingfile [typescript [divisor]]" //usage:#define scriptreplay_full_usage "\n\n" //usage: "Play back typescripts, using timing information" #include "libbb.h" int scriptreplay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int scriptreplay_main(int argc UNUSED_PARAM, char **argv) { const char *script = "typescript"; double delay, factor = 1000000.0; int fd; unsigned long count; FILE *tfp; if (!argv[1]) bb_show_usage(); if (argv[2]) { script = argv[2]; if (argv[3]) factor /= atof(argv[3]); } tfp = xfopen_for_read(argv[1]); fd = xopen(script, O_RDONLY); while (fscanf(tfp, "%lf %lu\n", &delay, &count) == 2) { usleep(delay * factor); bb_copyfd_exact_size(fd, STDOUT_FILENO, count); } if (ENABLE_FEATURE_CLEAN_UP) { close(fd); fclose(tfp); } return EXIT_SUCCESS; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/fsck_minix.c��������������������������������������������������������������0000644�0000000�0000000�00000101473�12263563520�016410� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * fsck.c - a file system consistency checker for Linux. * * (C) 1991, 1992 Linus Torvalds. * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* * 09.11.91 - made the first rudimentary functions * * 10.11.91 - updated, does checking, no repairs yet. * Sent out to the mailing-list for testing. * * 14.11.91 - Testing seems to have gone well. Added some * correction-code, and changed some functions. * * 15.11.91 - More correction code. Hopefully it notices most * cases now, and tries to do something about them. * * 16.11.91 - More corrections (thanks to Mika Jalava). Most * things seem to work now. Yeah, sure. * * 19.04.92 - Had to start over again from this old version, as a * kernel bug ate my enhanced fsck in february. * * 28.02.93 - added support for different directory entry sizes.. * * Sat Mar 6 18:59:42 1993, faith@cs.unc.edu: Output namelen with * superblock information * * Sat Oct 9 11:17:11 1993, faith@cs.unc.edu: make exit status conform * to that required by fsutil * * Mon Jan 3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu) * Added support for file system valid flag. Also * added program_version variable and output of * program name and version number when program * is executed. * * 30.10.94 - added support for v2 filesystem * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de) * * 10.12.94 - added test to prevent checking of mounted fs adapted * from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck * program. (Daniel Quinlan, quinlan@yggdrasil.com) * * 01.07.96 - Fixed the v2 fs stuff to use the right #defines and such * for modern libcs (janl@math.uio.no, Nicolai Langfeldt) * * 02.07.96 - Added C bit fiddling routines from rmk@ecs.soton.ac.uk * (Russell King). He made them for ARM. It would seem * that the ARM is powerful enough to do this in C whereas * i386 and m64k must use assembly to get it fast >:-) * This should make minix fsck system-independent. * (janl@math.uio.no, Nicolai Langfeldt) * * 04.11.96 - Added minor fixes from Andreas Schwab to avoid compiler * warnings. Added mc68k bitops from * Joerg Dorchain <dorchain@mpi-sb.mpg.de>. * * 06.11.96 - Added v2 code submitted by Joerg Dorchain, but written by * Andreas Schwab. * * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org> * - added Native Language Support * * * I've had no time to add comments - hopefully the function names * are comments enough. As with all file system checkers, this assumes * the file system is quiescent - don't use it on a mounted device * unless you can be sure nobody is writing to it (and remember that the * kernel can write to it when it searches for files). * * Usage: fsck [-larvsm] device * -l for a listing of all the filenames * -a for automatic repairs (not implemented) * -r for repairs (interactive) (not implemented) * -v for verbose (tells how many files) * -s for superblock info * -m for minix-like "mode not cleared" warnings * -f force filesystem check even if filesystem marked as valid * * The device may be a block device or a image of one, but this isn't * enforced (but it's not much fun on a character device :-). */ //usage:#define fsck_minix_trivial_usage //usage: "[-larvsmf] BLOCKDEV" //usage:#define fsck_minix_full_usage "\n\n" //usage: "Check MINIX filesystem\n" //usage: "\n -l List all filenames" //usage: "\n -r Perform interactive repairs" //usage: "\n -a Perform automatic repairs" //usage: "\n -v Verbose" //usage: "\n -s Output superblock information" //usage: "\n -m Show \"mode not cleared\" warnings" //usage: "\n -f Force file system check" #include <mntent.h> #include "libbb.h" #include "minix.h" #ifndef BLKGETSIZE #define BLKGETSIZE _IO(0x12,96) /* return device size */ #endif struct BUG_bad_inode_size { char BUG_bad_inode1_size[(INODE_SIZE1 * MINIX1_INODES_PER_BLOCK != BLOCK_SIZE) ? -1 : 1]; #if ENABLE_FEATURE_MINIX2 char BUG_bad_inode2_size[(INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE) ? -1 : 1]; #endif }; enum { #ifdef UNUSED MINIX1_LINK_MAX = 250, MINIX2_LINK_MAX = 65530, MINIX_I_MAP_SLOTS = 8, MINIX_Z_MAP_SLOTS = 64, MINIX_V1 = 0x0001, /* original minix fs */ MINIX_V2 = 0x0002, /* minix V2 fs */ #endif MINIX_NAME_MAX = 255, /* # chars in a file name */ }; #if !ENABLE_FEATURE_MINIX2 enum { version2 = 0 }; #endif enum { MAX_DEPTH = 32 }; enum { dev_fd = 3 }; struct globals { #if ENABLE_FEATURE_MINIX2 smallint version2; #endif smallint changed; /* is filesystem modified? */ smallint errors_uncorrected; /* flag if some error was not corrected */ smallint termios_set; smallint dirsize; smallint namelen; const char *device_name; int directory, regular, blockdev, chardev, links, symlinks, total; char *inode_buffer; char *inode_map; char *zone_map; unsigned char *inode_count; unsigned char *zone_count; /* File-name data */ int name_depth; char *name_component[MAX_DEPTH+1]; /* Bigger stuff */ struct termios sv_termios; char superblock_buffer[BLOCK_SIZE]; char add_zone_ind_blk[BLOCK_SIZE]; char add_zone_dind_blk[BLOCK_SIZE]; IF_FEATURE_MINIX2(char add_zone_tind_blk[BLOCK_SIZE];) char check_file_blk[BLOCK_SIZE]; /* File-name data */ char current_name[MAX_DEPTH * MINIX_NAME_MAX]; }; #define G (*ptr_to_globals) #if ENABLE_FEATURE_MINIX2 #define version2 (G.version2 ) #endif #define changed (G.changed ) #define errors_uncorrected (G.errors_uncorrected ) #define termios_set (G.termios_set ) #define dirsize (G.dirsize ) #define namelen (G.namelen ) #define device_name (G.device_name ) #define directory (G.directory ) #define regular (G.regular ) #define blockdev (G.blockdev ) #define chardev (G.chardev ) #define links (G.links ) #define symlinks (G.symlinks ) #define total (G.total ) #define inode_buffer (G.inode_buffer ) #define inode_map (G.inode_map ) #define zone_map (G.zone_map ) #define inode_count (G.inode_count ) #define zone_count (G.zone_count ) #define name_depth (G.name_depth ) #define name_component (G.name_component ) #define sv_termios (G.sv_termios ) #define superblock_buffer (G.superblock_buffer ) #define add_zone_ind_blk (G.add_zone_ind_blk ) #define add_zone_dind_blk (G.add_zone_dind_blk ) #define add_zone_tind_blk (G.add_zone_tind_blk ) #define check_file_blk (G.check_file_blk ) #define current_name (G.current_name ) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ dirsize = 16; \ namelen = 14; \ current_name[0] = '/'; \ /*current_name[1] = '\0';*/ \ name_component[0] = ¤t_name[0]; \ } while (0) #define OPTION_STR "larvsmf" enum { OPT_l = (1 << 0), OPT_a = (1 << 1), OPT_r = (1 << 2), OPT_v = (1 << 3), OPT_s = (1 << 4), OPT_w = (1 << 5), OPT_f = (1 << 6), }; #define OPT_list (option_mask32 & OPT_l) #define OPT_automatic (option_mask32 & OPT_a) #define OPT_repair (option_mask32 & OPT_r) #define OPT_verbose (option_mask32 & OPT_v) #define OPT_show (option_mask32 & OPT_s) #define OPT_warn_mode (option_mask32 & OPT_w) #define OPT_force (option_mask32 & OPT_f) /* non-automatic repairs requested? */ #define OPT_manual ((option_mask32 & (OPT_a|OPT_r)) == OPT_r) #define Inode1 (((struct minix1_inode *) inode_buffer)-1) #define Inode2 (((struct minix2_inode *) inode_buffer)-1) #define Super (*(struct minix_superblock *)(superblock_buffer)) #if ENABLE_FEATURE_MINIX2 # define ZONES ((unsigned)(version2 ? Super.s_zones : Super.s_nzones)) #else # define ZONES ((unsigned)(Super.s_nzones)) #endif #define INODES ((unsigned)Super.s_ninodes) #define IMAPS ((unsigned)Super.s_imap_blocks) #define ZMAPS ((unsigned)Super.s_zmap_blocks) #define FIRSTZONE ((unsigned)Super.s_firstdatazone) #define ZONESIZE ((unsigned)Super.s_log_zone_size) #define MAXSIZE ((unsigned)Super.s_max_size) #define MAGIC (Super.s_magic) /* gcc likes this more (code is smaller) than macro variant */ static ALWAYS_INLINE unsigned div_roundup(unsigned size, unsigned n) { return (size + n-1) / n; } #if !ENABLE_FEATURE_MINIX2 #define INODE_BLOCKS div_roundup(INODES, MINIX1_INODES_PER_BLOCK) #else #define INODE_BLOCKS div_roundup(INODES, \ (version2 ? MINIX2_INODES_PER_BLOCK : MINIX1_INODES_PER_BLOCK)) #endif #define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE) #define NORM_FIRSTZONE (2 + IMAPS + ZMAPS + INODE_BLOCKS) /* Before you ask "where they come from?": */ /* setbit/clrbit are supplied by sys/param.h */ static int minix_bit(const char *a, unsigned i) { return (a[i >> 3] & (1<<(i & 7))); } static void minix_setbit(char *a, unsigned i) { setbit(a, i); changed = 1; } static void minix_clrbit(char *a, unsigned i) { clrbit(a, i); changed = 1; } /* Note: do not assume 0/1, it is 0/nonzero */ #define zone_in_use(x) (minix_bit(zone_map,(x)-FIRSTZONE+1)) #define inode_in_use(x) (minix_bit(inode_map,(x))) #define mark_inode(x) (minix_setbit(inode_map,(x))) #define unmark_inode(x) (minix_clrbit(inode_map,(x))) #define mark_zone(x) (minix_setbit(zone_map,(x)-FIRSTZONE+1)) #define unmark_zone(x) (minix_clrbit(zone_map,(x)-FIRSTZONE+1)) static void recursive_check(unsigned ino); #if ENABLE_FEATURE_MINIX2 static void recursive_check2(unsigned ino); #endif static void die(const char *str) NORETURN; static void die(const char *str) { if (termios_set) tcsetattr_stdin_TCSANOW(&sv_termios); bb_error_msg_and_die("%s", str); } static void push_filename(const char *name) { // /dir/dir/dir/file // ^ ^ ^ // [0] [1] [2] <-name_component[i] if (name_depth < MAX_DEPTH) { int len; char *p = name_component[name_depth]; *p++ = '/'; len = sprintf(p, "%.*s", namelen, name); name_component[name_depth + 1] = p + len; } name_depth++; } static void pop_filename(void) { name_depth--; if (name_depth < MAX_DEPTH) { *name_component[name_depth] = '\0'; if (!name_depth) { current_name[0] = '/'; current_name[1] = '\0'; } } } static int ask(const char *string, int def) { int c; if (!OPT_repair) { bb_putchar('\n'); errors_uncorrected = 1; return 0; } if (OPT_automatic) { bb_putchar('\n'); if (!def) errors_uncorrected = 1; return def; } printf(def ? "%s (y/n)? " : "%s (n/y)? ", string); for (;;) { fflush_all(); c = getchar(); if (c == EOF) { if (!def) errors_uncorrected = 1; return def; } if (c == '\n') break; c |= 0x20; /* tolower */ if (c == 'y') { def = 1; break; } if (c == 'n') { def = 0; break; } } if (def) printf("y\n"); else { printf("n\n"); errors_uncorrected = 1; } return def; } /* * Make certain that we aren't checking a filesystem that is on a * mounted partition. Code adapted from e2fsck, Copyright (C) 1993, * 1994 Theodore Ts'o. Also licensed under GPL. */ static void check_mount(void) { if (find_mount_point(device_name, 0)) { int cont; #if ENABLE_FEATURE_MTAB_SUPPORT /* * If the root is mounted read-only, then /etc/mtab is * probably not correct; so we won't issue a warning based on * it. */ int fd = open(bb_path_mtab_file, O_RDWR); if (fd < 0 && errno == EROFS) return; close(fd); #endif printf("%s is mounted. ", device_name); cont = 0; if (isatty(0) && isatty(1)) cont = ask("Do you really want to continue", 0); if (!cont) { printf("Check aborted\n"); exit(EXIT_SUCCESS); } } } /* * check_zone_nr checks to see that *nr is a valid zone nr. If it * isn't, it will possibly be repaired. Check_zone_nr sets *corrected * if an error was corrected, and returns the zone (0 for no zone * or a bad zone-number). */ static int check_zone_nr2(uint32_t *nr, smallint *corrected) { const char *msg; if (!*nr) return 0; if (*nr < FIRSTZONE) msg = "< FIRSTZONE"; else if (*nr >= ZONES) msg = ">= ZONES"; else return *nr; printf("Zone nr %s in file '%s'. ", msg, current_name); if (ask("Remove block", 1)) { *nr = 0; *corrected = 1; } return 0; } static int check_zone_nr(uint16_t *nr, smallint *corrected) { uint32_t nr32 = *nr; int r = check_zone_nr2(&nr32, corrected); *nr = (uint16_t)nr32; return r; } /* * read-block reads block nr into the buffer at addr. */ static void read_block(unsigned nr, void *addr) { if (!nr) { memset(addr, 0, BLOCK_SIZE); return; } xlseek(dev_fd, BLOCK_SIZE * nr, SEEK_SET); if (BLOCK_SIZE != full_read(dev_fd, addr, BLOCK_SIZE)) { printf("%s: bad block %u in file '%s'\n", bb_msg_read_error, nr, current_name); errors_uncorrected = 1; memset(addr, 0, BLOCK_SIZE); } } /* * write_block writes block nr to disk. */ static void write_block(unsigned nr, void *addr) { if (!nr) return; if (nr < FIRSTZONE || nr >= ZONES) { printf("Internal error: trying to write bad block\n" "Write request ignored\n"); errors_uncorrected = 1; return; } xlseek(dev_fd, BLOCK_SIZE * nr, SEEK_SET); if (BLOCK_SIZE != full_write(dev_fd, addr, BLOCK_SIZE)) { printf("%s: bad block %u in file '%s'\n", bb_msg_write_error, nr, current_name); errors_uncorrected = 1; } } /* * map_block calculates the absolute block nr of a block in a file. * It sets 'changed' if the inode has needed changing, and re-writes * any indirect blocks with errors. */ static int map_block(struct minix1_inode *inode, unsigned blknr) { uint16_t ind[BLOCK_SIZE >> 1]; int block, result; smallint blk_chg; if (blknr < 7) return check_zone_nr(inode->i_zone + blknr, &changed); blknr -= 7; if (blknr < 512) { block = check_zone_nr(inode->i_zone + 7, &changed); goto common; } blknr -= 512; block = check_zone_nr(inode->i_zone + 8, &changed); read_block(block, ind); /* double indirect */ blk_chg = 0; result = check_zone_nr(&ind[blknr / 512], &blk_chg); if (blk_chg) write_block(block, ind); block = result; common: read_block(block, ind); blk_chg = 0; result = check_zone_nr(&ind[blknr % 512], &blk_chg); if (blk_chg) write_block(block, ind); return result; } #if ENABLE_FEATURE_MINIX2 static int map_block2(struct minix2_inode *inode, unsigned blknr) { uint32_t ind[BLOCK_SIZE >> 2]; int block, result; smallint blk_chg; if (blknr < 7) return check_zone_nr2(inode->i_zone + blknr, &changed); blknr -= 7; if (blknr < 256) { block = check_zone_nr2(inode->i_zone + 7, &changed); goto common2; } blknr -= 256; if (blknr < 256 * 256) { block = check_zone_nr2(inode->i_zone + 8, &changed); goto common1; } blknr -= 256 * 256; block = check_zone_nr2(inode->i_zone + 9, &changed); read_block(block, ind); /* triple indirect */ blk_chg = 0; result = check_zone_nr2(&ind[blknr / (256 * 256)], &blk_chg); if (blk_chg) write_block(block, ind); block = result; common1: read_block(block, ind); /* double indirect */ blk_chg = 0; result = check_zone_nr2(&ind[(blknr / 256) % 256], &blk_chg); if (blk_chg) write_block(block, ind); block = result; common2: read_block(block, ind); blk_chg = 0; result = check_zone_nr2(&ind[blknr % 256], &blk_chg); if (blk_chg) write_block(block, ind); return result; } #endif static void write_superblock(void) { /* * Set the state of the filesystem based on whether or not there * are uncorrected errors. The filesystem valid flag is * unconditionally set if we get this far. */ Super.s_state |= MINIX_VALID_FS | MINIX_ERROR_FS; if (!errors_uncorrected) Super.s_state &= ~MINIX_ERROR_FS; xlseek(dev_fd, BLOCK_SIZE, SEEK_SET); if (BLOCK_SIZE != full_write(dev_fd, superblock_buffer, BLOCK_SIZE)) die("can't write superblock"); } static void write_tables(void) { write_superblock(); if (IMAPS * BLOCK_SIZE != write(dev_fd, inode_map, IMAPS * BLOCK_SIZE)) die("can't write inode map"); if (ZMAPS * BLOCK_SIZE != write(dev_fd, zone_map, ZMAPS * BLOCK_SIZE)) die("can't write zone map"); if (INODE_BUFFER_SIZE != write(dev_fd, inode_buffer, INODE_BUFFER_SIZE)) die("can't write inodes"); } static void get_dirsize(void) { int block; char blk[BLOCK_SIZE]; int size; #if ENABLE_FEATURE_MINIX2 if (version2) block = Inode2[MINIX_ROOT_INO].i_zone[0]; else #endif block = Inode1[MINIX_ROOT_INO].i_zone[0]; read_block(block, blk); for (size = 16; size < BLOCK_SIZE; size <<= 1) { if (strcmp(blk + size + 2, "..") == 0) { dirsize = size; namelen = size - 2; return; } } /* use defaults */ } static void read_superblock(void) { xlseek(dev_fd, BLOCK_SIZE, SEEK_SET); if (BLOCK_SIZE != full_read(dev_fd, superblock_buffer, BLOCK_SIZE)) die("can't read superblock"); /* already initialized to: namelen = 14; dirsize = 16; version2 = 0; */ if (MAGIC == MINIX1_SUPER_MAGIC) { } else if (MAGIC == MINIX1_SUPER_MAGIC2) { namelen = 30; dirsize = 32; #if ENABLE_FEATURE_MINIX2 } else if (MAGIC == MINIX2_SUPER_MAGIC) { version2 = 1; } else if (MAGIC == MINIX2_SUPER_MAGIC2) { namelen = 30; dirsize = 32; version2 = 1; #endif } else die("bad magic number in superblock"); if (ZONESIZE != 0 || BLOCK_SIZE != 1024) die("only 1k blocks/zones supported"); if (IMAPS * BLOCK_SIZE * 8 < INODES + 1) die("bad s_imap_blocks field in superblock"); if (ZMAPS * BLOCK_SIZE * 8 < ZONES - FIRSTZONE + 1) die("bad s_zmap_blocks field in superblock"); } static void read_tables(void) { inode_map = xzalloc(IMAPS * BLOCK_SIZE); zone_map = xzalloc(ZMAPS * BLOCK_SIZE); inode_buffer = xmalloc(INODE_BUFFER_SIZE); inode_count = xmalloc(INODES + 1); zone_count = xmalloc(ZONES); if (IMAPS * BLOCK_SIZE != read(dev_fd, inode_map, IMAPS * BLOCK_SIZE)) die("can't read inode map"); if (ZMAPS * BLOCK_SIZE != read(dev_fd, zone_map, ZMAPS * BLOCK_SIZE)) die("can't read zone map"); if (INODE_BUFFER_SIZE != read(dev_fd, inode_buffer, INODE_BUFFER_SIZE)) die("can't read inodes"); if (NORM_FIRSTZONE != FIRSTZONE) { printf("warning: firstzone!=norm_firstzone\n"); errors_uncorrected = 1; } get_dirsize(); if (OPT_show) { printf("%u inodes\n" "%u blocks\n" "Firstdatazone=%u (%u)\n" "Zonesize=%u\n" "Maxsize=%u\n" "Filesystem state=%u\n" "namelen=%u\n\n", INODES, ZONES, FIRSTZONE, NORM_FIRSTZONE, BLOCK_SIZE << ZONESIZE, MAXSIZE, Super.s_state, namelen); } } static void get_inode_common(unsigned nr, uint16_t i_mode) { total++; if (!inode_count[nr]) { if (!inode_in_use(nr)) { printf("Inode %u is marked as 'unused', but it is used " "for file '%s'\n", nr, current_name); if (OPT_repair) { if (ask("Mark as 'in use'", 1)) mark_inode(nr); else errors_uncorrected = 1; } } if (S_ISDIR(i_mode)) directory++; else if (S_ISREG(i_mode)) regular++; else if (S_ISCHR(i_mode)) chardev++; else if (S_ISBLK(i_mode)) blockdev++; else if (S_ISLNK(i_mode)) symlinks++; else if (S_ISSOCK(i_mode)); else if (S_ISFIFO(i_mode)); else { printf("%s has mode %05o\n", current_name, i_mode); } } else links++; if (!++inode_count[nr]) { printf("Warning: inode count too big\n"); inode_count[nr]--; errors_uncorrected = 1; } } static struct minix1_inode *get_inode(unsigned nr) { struct minix1_inode *inode; if (!nr || nr > INODES) return NULL; inode = Inode1 + nr; get_inode_common(nr, inode->i_mode); return inode; } #if ENABLE_FEATURE_MINIX2 static struct minix2_inode *get_inode2(unsigned nr) { struct minix2_inode *inode; if (!nr || nr > INODES) return NULL; inode = Inode2 + nr; get_inode_common(nr, inode->i_mode); return inode; } #endif static void check_root(void) { struct minix1_inode *inode = Inode1 + MINIX_ROOT_INO; if (!inode || !S_ISDIR(inode->i_mode)) die("root inode isn't a directory"); } #if ENABLE_FEATURE_MINIX2 static void check_root2(void) { struct minix2_inode *inode = Inode2 + MINIX_ROOT_INO; if (!inode || !S_ISDIR(inode->i_mode)) die("root inode isn't a directory"); } #else void check_root2(void); #endif static int add_zone_common(int block, smallint *corrected) { if (!block) return 0; if (zone_count[block]) { printf("Already used block is reused in file '%s'. ", current_name); if (ask("Clear", 1)) { block = 0; *corrected = 1; return -1; /* "please zero out *znr" */ } } if (!zone_in_use(block)) { printf("Block %d in file '%s' is marked as 'unused'. ", block, current_name); if (ask("Correct", 1)) mark_zone(block); } if (!++zone_count[block]) zone_count[block]--; return block; } static int add_zone(uint16_t *znr, smallint *corrected) { int block; block = check_zone_nr(znr, corrected); block = add_zone_common(block, corrected); if (block == -1) { *znr = 0; block = 0; } return block; } #if ENABLE_FEATURE_MINIX2 static int add_zone2(uint32_t *znr, smallint *corrected) { int block; block = check_zone_nr2(znr, corrected); block = add_zone_common(block, corrected); if (block == -1) { *znr = 0; block = 0; } return block; } #endif static void add_zone_ind(uint16_t *znr, smallint *corrected) { int i; int block; smallint chg_blk = 0; block = add_zone(znr, corrected); if (!block) return; read_block(block, add_zone_ind_blk); for (i = 0; i < (BLOCK_SIZE >> 1); i++) add_zone(i + (uint16_t *) add_zone_ind_blk, &chg_blk); if (chg_blk) write_block(block, add_zone_ind_blk); } #if ENABLE_FEATURE_MINIX2 static void add_zone_ind2(uint32_t *znr, smallint *corrected) { int i; int block; smallint chg_blk = 0; block = add_zone2(znr, corrected); if (!block) return; read_block(block, add_zone_ind_blk); for (i = 0; i < BLOCK_SIZE >> 2; i++) add_zone2(i + (uint32_t *) add_zone_ind_blk, &chg_blk); if (chg_blk) write_block(block, add_zone_ind_blk); } #endif static void add_zone_dind(uint16_t *znr, smallint *corrected) { int i; int block; smallint chg_blk = 0; block = add_zone(znr, corrected); if (!block) return; read_block(block, add_zone_dind_blk); for (i = 0; i < (BLOCK_SIZE >> 1); i++) add_zone_ind(i + (uint16_t *) add_zone_dind_blk, &chg_blk); if (chg_blk) write_block(block, add_zone_dind_blk); } #if ENABLE_FEATURE_MINIX2 static void add_zone_dind2(uint32_t *znr, smallint *corrected) { int i; int block; smallint chg_blk = 0; block = add_zone2(znr, corrected); if (!block) return; read_block(block, add_zone_dind_blk); for (i = 0; i < BLOCK_SIZE >> 2; i++) add_zone_ind2(i + (uint32_t *) add_zone_dind_blk, &chg_blk); if (chg_blk) write_block(block, add_zone_dind_blk); } static void add_zone_tind2(uint32_t *znr, smallint *corrected) { int i; int block; smallint chg_blk = 0; block = add_zone2(znr, corrected); if (!block) return; read_block(block, add_zone_tind_blk); for (i = 0; i < BLOCK_SIZE >> 2; i++) add_zone_dind2(i + (uint32_t *) add_zone_tind_blk, &chg_blk); if (chg_blk) write_block(block, add_zone_tind_blk); } #endif static void check_zones(unsigned i) { struct minix1_inode *inode; if (!i || i > INODES) return; if (inode_count[i] > 1) /* have we counted this file already? */ return; inode = Inode1 + i; if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode) ) { return; } for (i = 0; i < 7; i++) add_zone(i + inode->i_zone, &changed); add_zone_ind(7 + inode->i_zone, &changed); add_zone_dind(8 + inode->i_zone, &changed); } #if ENABLE_FEATURE_MINIX2 static void check_zones2(unsigned i) { struct minix2_inode *inode; if (!i || i > INODES) return; if (inode_count[i] > 1) /* have we counted this file already? */ return; inode = Inode2 + i; if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) return; for (i = 0; i < 7; i++) add_zone2(i + inode->i_zone, &changed); add_zone_ind2(7 + inode->i_zone, &changed); add_zone_dind2(8 + inode->i_zone, &changed); add_zone_tind2(9 + inode->i_zone, &changed); } #endif static void check_file(struct minix1_inode *dir, unsigned offset) { struct minix1_inode *inode; int ino; char *name; int block; block = map_block(dir, offset / BLOCK_SIZE); read_block(block, check_file_blk); name = check_file_blk + (offset % BLOCK_SIZE) + 2; ino = *(uint16_t *) (name - 2); if (ino > INODES) { printf("%s contains a bad inode number for file '%.*s'. ", current_name, namelen, name); if (ask("Remove", 1)) { *(uint16_t *) (name - 2) = 0; write_block(block, check_file_blk); } ino = 0; } push_filename(name); inode = get_inode(ino); pop_filename(); if (!offset) { if (inode && LONE_CHAR(name, '.')) return; printf("%s: bad directory: '.' isn't first\n", current_name); errors_uncorrected = 1; } if (offset == dirsize) { if (inode && strcmp("..", name) == 0) return; printf("%s: bad directory: '..' isn't second\n", current_name); errors_uncorrected = 1; } if (!inode) return; push_filename(name); if (OPT_list) { if (OPT_verbose) printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks); printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : ""); } check_zones(ino); if (inode && S_ISDIR(inode->i_mode)) recursive_check(ino); pop_filename(); } #if ENABLE_FEATURE_MINIX2 static void check_file2(struct minix2_inode *dir, unsigned offset) { struct minix2_inode *inode; int ino; char *name; int block; block = map_block2(dir, offset / BLOCK_SIZE); read_block(block, check_file_blk); name = check_file_blk + (offset % BLOCK_SIZE) + 2; ino = *(uint16_t *) (name - 2); if (ino > INODES) { printf("%s contains a bad inode number for file '%.*s'. ", current_name, namelen, name); if (ask("Remove", 1)) { *(uint16_t *) (name - 2) = 0; write_block(block, check_file_blk); } ino = 0; } push_filename(name); inode = get_inode2(ino); pop_filename(); if (!offset) { if (inode && LONE_CHAR(name, '.')) return; printf("%s: bad directory: '.' isn't first\n", current_name); errors_uncorrected = 1; } if (offset == dirsize) { if (inode && strcmp("..", name) == 0) return; printf("%s: bad directory: '..' isn't second\n", current_name); errors_uncorrected = 1; } if (!inode) return; push_filename(name); if (OPT_list) { if (OPT_verbose) printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks); printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : ""); } check_zones2(ino); if (inode && S_ISDIR(inode->i_mode)) recursive_check2(ino); pop_filename(); } #endif static void recursive_check(unsigned ino) { struct minix1_inode *dir; unsigned offset; dir = Inode1 + ino; if (!S_ISDIR(dir->i_mode)) die("internal error"); if (dir->i_size < 2 * dirsize) { printf("%s: bad directory: size<32", current_name); errors_uncorrected = 1; } for (offset = 0; offset < dir->i_size; offset += dirsize) check_file(dir, offset); } #if ENABLE_FEATURE_MINIX2 static void recursive_check2(unsigned ino) { struct minix2_inode *dir; unsigned offset; dir = Inode2 + ino; if (!S_ISDIR(dir->i_mode)) die("internal error"); if (dir->i_size < 2 * dirsize) { printf("%s: bad directory: size<32", current_name); errors_uncorrected = 1; } for (offset = 0; offset < dir->i_size; offset += dirsize) check_file2(dir, offset); } #endif static int bad_zone(int i) { char buffer[BLOCK_SIZE]; xlseek(dev_fd, BLOCK_SIZE * i, SEEK_SET); return (BLOCK_SIZE != full_read(dev_fd, buffer, BLOCK_SIZE)); } static void check_counts(void) { int i; for (i = 1; i <= INODES; i++) { if (OPT_warn_mode && Inode1[i].i_mode && !inode_in_use(i)) { printf("Inode %d has non-zero mode. ", i); if (ask("Clear", 1)) { Inode1[i].i_mode = 0; changed = 1; } } if (!inode_count[i]) { if (!inode_in_use(i)) continue; printf("Unused inode %d is marked as 'used' in the bitmap. ", i); if (ask("Clear", 1)) unmark_inode(i); continue; } if (!inode_in_use(i)) { printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i); if (ask("Set", 1)) mark_inode(i); } if (Inode1[i].i_nlinks != inode_count[i]) { printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ", i, Inode1[i].i_mode, Inode1[i].i_nlinks, inode_count[i]); if (ask("Set i_nlinks to count", 1)) { Inode1[i].i_nlinks = inode_count[i]; changed = 1; } } } for (i = FIRSTZONE; i < ZONES; i++) { if ((zone_in_use(i) != 0) == zone_count[i]) continue; if (!zone_count[i]) { if (bad_zone(i)) continue; printf("Zone %d is marked 'in use', but no file uses it. ", i); if (ask("Unmark", 1)) unmark_zone(i); continue; } printf("Zone %d: %sin use, counted=%d\n", i, zone_in_use(i) ? "" : "not ", zone_count[i]); } } #if ENABLE_FEATURE_MINIX2 static void check_counts2(void) { int i; for (i = 1; i <= INODES; i++) { if (OPT_warn_mode && Inode2[i].i_mode && !inode_in_use(i)) { printf("Inode %d has non-zero mode. ", i); if (ask("Clear", 1)) { Inode2[i].i_mode = 0; changed = 1; } } if (!inode_count[i]) { if (!inode_in_use(i)) continue; printf("Unused inode %d is marked as 'used' in the bitmap. ", i); if (ask("Clear", 1)) unmark_inode(i); continue; } if (!inode_in_use(i)) { printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i); if (ask("Set", 1)) mark_inode(i); } if (Inode2[i].i_nlinks != inode_count[i]) { printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ", i, Inode2[i].i_mode, Inode2[i].i_nlinks, inode_count[i]); if (ask("Set i_nlinks to count", 1)) { Inode2[i].i_nlinks = inode_count[i]; changed = 1; } } } for (i = FIRSTZONE; i < ZONES; i++) { if ((zone_in_use(i) != 0) == zone_count[i]) continue; if (!zone_count[i]) { if (bad_zone(i)) continue; printf("Zone %d is marked 'in use', but no file uses it. ", i); if (ask("Unmark", 1)) unmark_zone(i); continue; } printf("Zone %d: %sin use, counted=%d\n", i, zone_in_use(i) ? "" : "not ", zone_count[i]); } } #endif static void check(void) { memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count)); memset(zone_count, 0, ZONES * sizeof(*zone_count)); check_zones(MINIX_ROOT_INO); recursive_check(MINIX_ROOT_INO); check_counts(); } #if ENABLE_FEATURE_MINIX2 static void check2(void) { memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count)); memset(zone_count, 0, ZONES * sizeof(*zone_count)); check_zones2(MINIX_ROOT_INO); recursive_check2(MINIX_ROOT_INO); check_counts2(); } #else void check2(void); #endif int fsck_minix_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fsck_minix_main(int argc UNUSED_PARAM, char **argv) { struct termios tmp; int retcode = 0; xfunc_error_retval = 8; INIT_G(); opt_complementary = "=1:ar"; /* one argument; -a assumes -r */ getopt32(argv, OPTION_STR); argv += optind; device_name = argv[0]; check_mount(); /* trying to check a mounted filesystem? */ if (OPT_manual) { if (!isatty(0) || !isatty(1)) die("need terminal for interactive repairs"); } xmove_fd(xopen(device_name, OPT_repair ? O_RDWR : O_RDONLY), dev_fd); /*sync(); paranoia? */ read_superblock(); /* * Determine whether or not we should continue with the checking. * This is based on the status of the filesystem valid and error * flags and whether or not the -f switch was specified on the * command line. */ printf("%s: %s\n", applet_name, bb_banner); if (!(Super.s_state & MINIX_ERROR_FS) && (Super.s_state & MINIX_VALID_FS) && !OPT_force ) { if (OPT_repair) printf("%s is clean, check is skipped\n", device_name); return 0; } else if (OPT_force) printf("Forcing filesystem check on %s\n", device_name); else if (OPT_repair) printf("Filesystem on %s is dirty, needs checking\n", device_name); read_tables(); if (OPT_manual) { tcgetattr(0, &sv_termios); tmp = sv_termios; tmp.c_lflag &= ~(ICANON | ECHO); tcsetattr_stdin_TCSANOW(&tmp); termios_set = 1; } if (version2) { check_root2(); check2(); } else { check_root(); check(); } if (OPT_verbose) { int i, free_cnt; for (i = 1, free_cnt = 0; i <= INODES; i++) if (!inode_in_use(i)) free_cnt++; printf("\n%6u inodes used (%u%%)\n", (INODES - free_cnt), 100 * (INODES - free_cnt) / INODES); for (i = FIRSTZONE, free_cnt = 0; i < ZONES; i++) if (!zone_in_use(i)) free_cnt++; printf("%6u zones used (%u%%)\n\n" "%6u regular files\n" "%6u directories\n" "%6u character device files\n" "%6u block device files\n" "%6u links\n" "%6u symbolic links\n" "------\n" "%6u files\n", (ZONES - free_cnt), 100 * (ZONES - free_cnt) / ZONES, regular, directory, chardev, blockdev, links - 2 * directory + 1, symlinks, total - 2 * directory + 1); } if (changed) { write_tables(); printf("FILE SYSTEM HAS BEEN CHANGED\n"); sync(); } else if (OPT_repair) write_superblock(); if (OPT_manual) tcsetattr_stdin_TCSANOW(&sv_termios); if (changed) retcode += 3; if (errors_uncorrected) retcode += 4; return retcode; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/umount.c������������������������������������������������������������������0000644�0000000�0000000�00000014420�12263563520�015600� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Mini umount implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> * Copyright (C) 2005 by Rob Landley <rob@landley.net> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define umount_trivial_usage //usage: "[OPTIONS] FILESYSTEM|DIRECTORY" //usage:#define umount_full_usage "\n\n" //usage: "Unmount file systems\n" //usage: IF_FEATURE_UMOUNT_ALL( //usage: "\n -a Unmount all file systems" IF_FEATURE_MTAB_SUPPORT(" in /etc/mtab") //usage: ) //usage: IF_FEATURE_MTAB_SUPPORT( //usage: "\n -n Don't erase /etc/mtab entries" //usage: ) //usage: "\n -r Try to remount devices as read-only if mount is busy" //usage: "\n -l Lazy umount (detach filesystem)" //usage: "\n -f Force umount (i.e., unreachable NFS server)" //usage: IF_FEATURE_MOUNT_LOOP( //usage: "\n -D Don't free loop device even if it has been used" //usage: ) //usage: //usage:#define umount_example_usage //usage: "$ umount /dev/hdc1\n" #include <mntent.h> #include <sys/mount.h> #include "libbb.h" #if defined(__dietlibc__) // TODO: This does not belong here. /* 16.12.2006, Sampo Kellomaki (sampo@iki.fi) * dietlibc-0.30 does not have implementation of getmntent_r() */ static struct mntent *getmntent_r(FILE* stream, struct mntent* result, char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM) { struct mntent* ment = getmntent(stream); return memcpy(result, ment, sizeof(*ment)); } #endif /* Ignored: -v -t -i * bbox always acts as if -d is present. * -D can be used to suppress it (bbox extension). * Rationale: * (1) util-linux's umount does it if "loop=..." is seen in /etc/mtab: * thus, on many systems, bare umount _does_ drop loop devices. * (2) many users request this feature. */ #define OPTION_STRING "fldDnra" "vt:i" #define OPT_FORCE (1 << 0) // Same as MNT_FORCE #define OPT_LAZY (1 << 1) // Same as MNT_DETACH //#define OPT_FREE_LOOP (1 << 2) // -d is assumed always present #define OPT_DONT_FREE_LOOP (1 << 3) #define OPT_NO_MTAB (1 << 4) #define OPT_REMOUNT (1 << 5) #define OPT_ALL (ENABLE_FEATURE_UMOUNT_ALL ? (1 << 6) : 0) int umount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int umount_main(int argc UNUSED_PARAM, char **argv) { int doForce; struct mntent me; FILE *fp; char *fstype = NULL; int status = EXIT_SUCCESS; unsigned opt; struct mtab_list { char *dir; char *device; struct mtab_list *next; } *mtl, *m; opt = getopt32(argv, OPTION_STRING, &fstype); //argc -= optind; argv += optind; // MNT_FORCE and MNT_DETACH (from linux/fs.h) must match // OPT_FORCE and OPT_LAZY, otherwise this trick won't work: doForce = MAX((opt & OPT_FORCE), (opt & OPT_LAZY)); /* Get a list of mount points from mtab. We read them all in now mostly * for umount -a (so we don't have to worry about the list changing while * we iterate over it, or about getting stuck in a loop on the same failing * entry. Notice that this also naturally reverses the list so that -a * umounts the most recent entries first. */ m = mtl = NULL; // If we're umounting all, then m points to the start of the list and // the argument list should be empty (which will match all). fp = setmntent(bb_path_mtab_file, "r"); if (!fp) { if (opt & OPT_ALL) bb_error_msg_and_die("can't open '%s'", bb_path_mtab_file); } else { while (getmntent_r(fp, &me, bb_common_bufsiz1, sizeof(bb_common_bufsiz1))) { /* Match fstype if passed */ if (!match_fstype(&me, fstype)) continue; m = xzalloc(sizeof(*m)); m->next = mtl; m->device = xstrdup(me.mnt_fsname); m->dir = xstrdup(me.mnt_dir); mtl = m; } endmntent(fp); } // If we're not umounting all, we need at least one argument. if (!(opt & OPT_ALL) && !fstype) { if (!argv[0]) bb_show_usage(); m = NULL; } // Loop through everything we're supposed to umount, and do so. for (;;) { int curstat; char *zapit = *argv; char *path; // Do we already know what to umount this time through the loop? if (m) path = xstrdup(m->dir); // For umount -a, end of mtab means time to exit. else if (opt & OPT_ALL) break; // Use command line argument (and look it up in mtab list) else { if (!zapit) break; argv++; path = xmalloc_realpath(zapit); if (path) { for (m = mtl; m; m = m->next) if (strcmp(path, m->dir) == 0 || strcmp(path, m->device) == 0) break; } } // If we couldn't find this sucker in /etc/mtab, punt by passing our // command line argument straight to the umount syscall. Otherwise, // umount the directory even if we were given the block device. if (m) zapit = m->dir; // Let's ask the thing nicely to unmount. curstat = umount(zapit); // Force the unmount, if necessary. if (curstat && doForce) curstat = umount2(zapit, doForce); // If still can't umount, maybe remount read-only? if (curstat) { if ((opt & OPT_REMOUNT) && errno == EBUSY && m) { // Note! Even if we succeed here, later we should not // free loop device or erase mtab entry! const char *msg = "%s busy - remounted read-only"; curstat = mount(m->device, zapit, NULL, MS_REMOUNT|MS_RDONLY, NULL); if (curstat) { msg = "can't remount %s read-only"; status = EXIT_FAILURE; } bb_error_msg(msg, m->device); } else { status = EXIT_FAILURE; bb_perror_msg("can't %sumount %s", (doForce ? "forcibly " : ""), zapit); } } else { // De-allocate the loop device. This ioctl should be ignored on // any non-loop block devices. if (ENABLE_FEATURE_MOUNT_LOOP && !(opt & OPT_DONT_FREE_LOOP) && m) del_loop(m->device); if (ENABLE_FEATURE_MTAB_SUPPORT && !(opt & OPT_NO_MTAB) && m) erase_mtab(m->dir); } // Find next matching mtab entry for -a or umount /dev // Note this means that "umount /dev/blah" will unmount all instances // of /dev/blah, not just the most recent. if (m) { while ((m = m->next) != NULL) // NB: if m is non-NULL, path is non-NULL as well if ((opt & OPT_ALL) || strcmp(path, m->device) == 0) break; } free(path); } // Free mtab list if necessary if (ENABLE_FEATURE_CLEAN_UP) { while (mtl) { m = mtl->next; free(mtl->device); free(mtl->dir); free(mtl); mtl = m; } } return status; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/mkfs_ext2_test.sh���������������������������������������������������������0000755�0000000�0000000�00000005231�12263563520�017405� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Disabling features we do not match exactly: system_mke2fs='/sbin/mke2fs -O ^resize_inode' bbox_mke2fs='./busybox mke2fs' gen_image() { # params: mke2fs_invocation image_name >$2 dd seek=$((kilobytes-1)) bs=1K count=1 </dev/zero of=$2 >/dev/null 2>&1 || exit 1 $1 -F $2 $kilobytes >$2.raw_out 2>&1 || return 1 cat $2.raw_out \ | grep -v '^mke2fs [0-9]*\.[0-9]*\.[0-9]* ' \ | grep -v '^Maximum filesystem' \ | grep -v '^Writing inode tables' \ | grep -v '^Writing superblocks and filesystem accounting information' \ | grep -v '^This filesystem will be automatically checked every' \ | grep -v '^180 days, whichever comes first' \ | sed 's/blocks* unused./blocks unused/' \ | sed 's/block groups*/block groups/' \ | sed 's/ *$//' \ | sed 's/blocks (.*%) reserved/blocks reserved/' \ | grep -v '^$' \ >$2.out } test_mke2fs() { echo Testing $kilobytes gen_image "$system_mke2fs" image_std || return 1 gen_image "$bbox_mke2fs" image_bb || return 1 diff -ua image_bb.out image_std.out >image.out.diff || { cat image.out.diff return 1 } e2fsck -f -n image_bb >image_bb_e2fsck.out 2>&1 || { echo "e2fsck error on image_bb" cat image_bb_e2fsck.out exit 1 } } # -:bbox +:standard # kilobytes=60 is the minimal allowed size kilobytes=60 while true; do test_mke2fs || exit 1 : $((kilobytes++)) test $kilobytes = 200 && break done # Transition from one block group to two # fails in [8378..8410] range unless -O ^resize_inode kilobytes=$((1 * 8*1024 - 50)) while true; do test_mke2fs || exit 1 : $((kilobytes++)) test $kilobytes = $((1 * 8*1024 + 300)) && break done # Transition from 2 block groups to 3 # works kilobytes=$((2 * 8*1024 - 50)) while true; do test_mke2fs || exit 1 : $((kilobytes++)) test $kilobytes = $((2 * 8*1024 + 400)) && break done # Transition from 3 block groups to 4 # fails in [24825..24922] range unless -O ^resize_inode kilobytes=$((3 * 8*1024 - 50)) while true; do test_mke2fs || exit 1 : $((kilobytes++)) test $kilobytes = $((3 * 8*1024 + 500)) && break done # Transition from 4 block groups to 5 # works kilobytes=$((4 * 8*1024 - 50)) while true; do test_mke2fs || exit 1 : $((kilobytes++)) test $kilobytes = $((4 * 8*1024 + 600)) && break done # Transition from 5 block groups to 6 # fails in [41230..41391] range unless -O ^resize_inode kilobytes=$((5 * 8*1024 - 50)) while true; do test_mke2fs || exit 1 : $((kilobytes++)) test $kilobytes = $((5 * 8*1024 + 700)) && break done exit # Random sizes while true; do kilobytes=$(( (RANDOM*RANDOM) % 5000000 + 60)) test_mke2fs || exit 1 done �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/fdisk.c�������������������������������������������������������������������0000644�0000000�0000000�00000232722�12263563520�015360� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* fdisk.c -- Partition table manipulator for Linux. * * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk) * Copyright (C) 2001,2002 Vladimir Oleynik <dzo@simtreas.ru> (initial bb port) * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* Looks like someone forgot to add this to config system */ //usage:#ifndef ENABLE_FEATURE_FDISK_BLKSIZE //usage:# define ENABLE_FEATURE_FDISK_BLKSIZE 0 //usage:# define IF_FEATURE_FDISK_BLKSIZE(a) //usage:#endif //usage: //usage:#define fdisk_trivial_usage //usage: "[-ul" IF_FEATURE_FDISK_BLKSIZE("s") "] " //usage: "[-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SSZ] DISK" //usage:#define fdisk_full_usage "\n\n" //usage: "Change partition table\n" //usage: "\n -u Start and End are in sectors (instead of cylinders)" //usage: "\n -l Show partition table for each DISK, then exit" //usage: IF_FEATURE_FDISK_BLKSIZE( //usage: "\n -s Show partition sizes in kb for each DISK, then exit" //usage: ) //usage: "\n -b 2048 (for certain MO disks) use 2048-byte sectors" //usage: "\n -C CYLINDERS Set number of cylinders/heads/sectors" //usage: "\n -H HEADS" //usage: "\n -S SECTORS" #ifndef _LARGEFILE64_SOURCE /* For lseek64 */ # define _LARGEFILE64_SOURCE #endif #include <assert.h> /* assert */ #include <sys/mount.h> #if !defined(BLKSSZGET) # define BLKSSZGET _IO(0x12, 104) #endif #if !defined(BLKGETSIZE64) # define BLKGETSIZE64 _IOR(0x12,114,size_t) #endif #include "libbb.h" #if BB_LITTLE_ENDIAN # define inline_if_little_endian ALWAYS_INLINE #else # define inline_if_little_endian /* nothing */ #endif /* Looks like someone forgot to add this to config system */ #ifndef ENABLE_FEATURE_FDISK_BLKSIZE # define ENABLE_FEATURE_FDISK_BLKSIZE 0 # define IF_FEATURE_FDISK_BLKSIZE(a) #endif #define DEFAULT_SECTOR_SIZE 512 #define DEFAULT_SECTOR_SIZE_STR "512" #define MAX_SECTOR_SIZE 2048 #define SECTOR_SIZE 512 /* still used in osf/sgi/sun code */ #define MAXIMUM_PARTS 60 #define ACTIVE_FLAG 0x80 #define EXTENDED 0x05 #define WIN98_EXTENDED 0x0f #define LINUX_PARTITION 0x81 #define LINUX_SWAP 0x82 #define LINUX_NATIVE 0x83 #define LINUX_EXTENDED 0x85 #define LINUX_LVM 0x8e #define LINUX_RAID 0xfd enum { OPT_b = 1 << 0, OPT_C = 1 << 1, OPT_H = 1 << 2, OPT_l = 1 << 3, OPT_S = 1 << 4, OPT_u = 1 << 5, OPT_s = (1 << 6) * ENABLE_FEATURE_FDISK_BLKSIZE, }; typedef unsigned long long ullong; /* Used for sector numbers. Partition formats we know * do not support more than 2^32 sectors */ typedef uint32_t sector_t; #if UINT_MAX == 4294967295 # define SECT_FMT "" #elif ULONG_MAX == 4294967295 # define SECT_FMT "l" #else # error Cant detect sizeof(uint32_t) #endif struct hd_geometry { unsigned char heads; unsigned char sectors; unsigned short cylinders; unsigned long start; }; #define HDIO_GETGEO 0x0301 /* get device geometry */ static const char msg_building_new_label[] ALIGN1 = "Building a new %s. Changes will remain in memory only,\n" "until you decide to write them. After that the previous content\n" "won't be recoverable.\n\n"; static const char msg_part_already_defined[] ALIGN1 = "Partition %u is already defined, delete it before re-adding\n"; struct partition { unsigned char boot_ind; /* 0x80 - active */ unsigned char head; /* starting head */ unsigned char sector; /* starting sector */ unsigned char cyl; /* starting cylinder */ unsigned char sys_ind; /* what partition type */ unsigned char end_head; /* end head */ unsigned char end_sector; /* end sector */ unsigned char end_cyl; /* end cylinder */ unsigned char start4[4]; /* starting sector counting from 0 */ unsigned char size4[4]; /* nr of sectors in partition */ } PACKED; /* * per partition table entry data * * The four primary partitions have the same sectorbuffer (MBRbuffer) * and have NULL ext_pointer. * Each logical partition table entry has two pointers, one for the * partition and one link to the next one. */ struct pte { struct partition *part_table; /* points into sectorbuffer */ struct partition *ext_pointer; /* points into sectorbuffer */ sector_t offset_from_dev_start; /* disk sector number */ char *sectorbuffer; /* disk sector contents */ #if ENABLE_FEATURE_FDISK_WRITABLE char changed; /* boolean */ #endif }; #define unable_to_open "can't open '%s'" #define unable_to_read "can't read from %s" #define unable_to_seek "can't seek on %s" enum label_type { LABEL_DOS, LABEL_SUN, LABEL_SGI, LABEL_AIX, LABEL_OSF, LABEL_GPT }; #define LABEL_IS_DOS (LABEL_DOS == current_label_type) #if ENABLE_FEATURE_SUN_LABEL #define LABEL_IS_SUN (LABEL_SUN == current_label_type) #define STATIC_SUN static #else #define LABEL_IS_SUN 0 #define STATIC_SUN extern #endif #if ENABLE_FEATURE_SGI_LABEL #define LABEL_IS_SGI (LABEL_SGI == current_label_type) #define STATIC_SGI static #else #define LABEL_IS_SGI 0 #define STATIC_SGI extern #endif #if ENABLE_FEATURE_AIX_LABEL #define LABEL_IS_AIX (LABEL_AIX == current_label_type) #define STATIC_AIX static #else #define LABEL_IS_AIX 0 #define STATIC_AIX extern #endif #if ENABLE_FEATURE_OSF_LABEL #define LABEL_IS_OSF (LABEL_OSF == current_label_type) #define STATIC_OSF static #else #define LABEL_IS_OSF 0 #define STATIC_OSF extern #endif #if ENABLE_FEATURE_GPT_LABEL #define LABEL_IS_GPT (LABEL_GPT == current_label_type) #define STATIC_GPT static #else #define LABEL_IS_GPT 0 #define STATIC_GPT extern #endif enum action { OPEN_MAIN, TRY_ONLY, CREATE_EMPTY_DOS, CREATE_EMPTY_SUN }; static void update_units(void); #if ENABLE_FEATURE_FDISK_WRITABLE static void change_units(void); static void reread_partition_table(int leave); static void delete_partition(int i); static unsigned get_partition(int warn, unsigned max); static void list_types(const char *const *sys); static sector_t read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char *mesg); #endif static const char *partition_type(unsigned char type); static void get_geometry(void); static void read_pte(struct pte *pe, sector_t offset); #if ENABLE_FEATURE_SUN_LABEL || ENABLE_FEATURE_FDISK_WRITABLE static int get_boot(enum action what); #else static int get_boot(void); #endif #define PLURAL 0 #define SINGULAR 1 static sector_t get_start_sect(const struct partition *p); static sector_t get_nr_sects(const struct partition *p); /* DOS partition types */ static const char *const i386_sys_types[] = { "\x00" "Empty", "\x01" "FAT12", "\x04" "FAT16 <32M", "\x05" "Extended", /* DOS 3.3+ extended partition */ "\x06" "FAT16", /* DOS 16-bit >=32M */ "\x07" "HPFS/NTFS", /* OS/2 IFS, eg, HPFS or NTFS or QNX */ "\x0a" "OS/2 Boot Manager",/* OS/2 Boot Manager */ "\x0b" "Win95 FAT32", "\x0c" "Win95 FAT32 (LBA)",/* LBA really is 'Extended Int 13h' */ "\x0e" "Win95 FAT16 (LBA)", "\x0f" "Win95 Ext'd (LBA)", "\x11" "Hidden FAT12", "\x12" "Compaq diagnostics", "\x14" "Hidden FAT16 <32M", "\x16" "Hidden FAT16", "\x17" "Hidden HPFS/NTFS", "\x1b" "Hidden Win95 FAT32", "\x1c" "Hidden W95 FAT32 (LBA)", "\x1e" "Hidden W95 FAT16 (LBA)", "\x3c" "Part.Magic recovery", "\x41" "PPC PReP Boot", "\x42" "SFS", "\x63" "GNU HURD or SysV", /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */ "\x80" "Old Minix", /* Minix 1.4a and earlier */ "\x81" "Minix / old Linux",/* Minix 1.4b and later */ "\x82" "Linux swap", /* also Solaris */ "\x83" "Linux", "\x84" "OS/2 hidden C: drive", "\x85" "Linux extended", "\x86" "NTFS volume set", "\x87" "NTFS volume set", "\x8e" "Linux LVM", "\x9f" "BSD/OS", /* BSDI */ "\xa0" "Thinkpad hibernation", "\xa5" "FreeBSD", /* various BSD flavours */ "\xa6" "OpenBSD", "\xa8" "Darwin UFS", "\xa9" "NetBSD", "\xab" "Darwin boot", "\xb7" "BSDI fs", "\xb8" "BSDI swap", "\xbe" "Solaris boot", "\xeb" "BeOS fs", "\xee" "EFI GPT", /* Intel EFI GUID Partition Table */ "\xef" "EFI (FAT-12/16/32)", /* Intel EFI System Partition */ "\xf0" "Linux/PA-RISC boot", /* Linux/PA-RISC boot loader */ "\xf2" "DOS secondary", /* DOS 3.3+ secondary */ "\xfd" "Linux raid autodetect", /* New (2.2.x) raid partition with autodetect using persistent superblock */ #if 0 /* ENABLE_WEIRD_PARTITION_TYPES */ "\x02" "XENIX root", "\x03" "XENIX usr", "\x08" "AIX", /* AIX boot (AIX -- PS/2 port) or SplitDrive */ "\x09" "AIX bootable", /* AIX data or Coherent */ "\x10" "OPUS", "\x18" "AST SmartSleep", "\x24" "NEC DOS", "\x39" "Plan 9", "\x40" "Venix 80286", "\x4d" "QNX4.x", "\x4e" "QNX4.x 2nd part", "\x4f" "QNX4.x 3rd part", "\x50" "OnTrack DM", "\x51" "OnTrack DM6 Aux1", /* (or Novell) */ "\x52" "CP/M", /* CP/M or Microport SysV/AT */ "\x53" "OnTrack DM6 Aux3", "\x54" "OnTrackDM6", "\x55" "EZ-Drive", "\x56" "Golden Bow", "\x5c" "Priam Edisk", "\x61" "SpeedStor", "\x64" "Novell Netware 286", "\x65" "Novell Netware 386", "\x70" "DiskSecure Multi-Boot", "\x75" "PC/IX", "\x93" "Amoeba", "\x94" "Amoeba BBT", /* (bad block table) */ "\xa7" "NeXTSTEP", "\xbb" "Boot Wizard hidden", "\xc1" "DRDOS/sec (FAT-12)", "\xc4" "DRDOS/sec (FAT-16 < 32M)", "\xc6" "DRDOS/sec (FAT-16)", "\xc7" "Syrinx", "\xda" "Non-FS data", "\xdb" "CP/M / CTOS / ...",/* CP/M or Concurrent CP/M or Concurrent DOS or CTOS */ "\xde" "Dell Utility", /* Dell PowerEdge Server utilities */ "\xdf" "BootIt", /* BootIt EMBRM */ "\xe1" "DOS access", /* DOS access or SpeedStor 12-bit FAT extended partition */ "\xe3" "DOS R/O", /* DOS R/O or SpeedStor */ "\xe4" "SpeedStor", /* SpeedStor 16-bit FAT extended partition < 1024 cyl. */ "\xf1" "SpeedStor", "\xf4" "SpeedStor", /* SpeedStor large partition */ "\xfe" "LANstep", /* SpeedStor >1024 cyl. or LANstep */ "\xff" "BBT", /* Xenix Bad Block Table */ #endif NULL }; enum { dev_fd = 3 /* the disk */ }; /* Globals */ struct globals { char *line_ptr; const char *disk_device; int g_partitions; // = 4; /* maximum partition + 1 */ unsigned units_per_sector; // = 1; unsigned sector_size; // = DEFAULT_SECTOR_SIZE; unsigned user_set_sector_size; unsigned sector_offset; // = 1; unsigned g_heads, g_sectors, g_cylinders; smallint /* enum label_type */ current_label_type; smallint display_in_cyl_units; // = 1; #if ENABLE_FEATURE_OSF_LABEL smallint possibly_osf_label; #endif smallint listing; /* no aborts for fdisk -l */ smallint dos_compatible_flag; // = 1; #if ENABLE_FEATURE_FDISK_WRITABLE //int dos_changed; smallint nowarn; /* no warnings for fdisk -l/-s */ #endif int ext_index; /* the prime extended partition */ unsigned user_cylinders, user_heads, user_sectors; unsigned pt_heads, pt_sectors; unsigned kern_heads, kern_sectors; sector_t extended_offset; /* offset of link pointers */ sector_t total_number_of_sectors; jmp_buf listingbuf; char line_buffer[80]; char partname_buffer[80]; /* Raw disk label. For DOS-type partition tables the MBR, * with descriptions of the primary partitions. */ char MBRbuffer[MAX_SECTOR_SIZE]; /* Partition tables */ struct pte ptes[MAXIMUM_PARTS]; }; #define G (*ptr_to_globals) #define line_ptr (G.line_ptr ) #define disk_device (G.disk_device ) #define g_partitions (G.g_partitions ) #define units_per_sector (G.units_per_sector ) #define sector_size (G.sector_size ) #define user_set_sector_size (G.user_set_sector_size) #define sector_offset (G.sector_offset ) #define g_heads (G.g_heads ) #define g_sectors (G.g_sectors ) #define g_cylinders (G.g_cylinders ) #define current_label_type (G.current_label_type ) #define display_in_cyl_units (G.display_in_cyl_units) #define possibly_osf_label (G.possibly_osf_label ) #define listing (G.listing ) #define dos_compatible_flag (G.dos_compatible_flag ) #define nowarn (G.nowarn ) #define ext_index (G.ext_index ) #define user_cylinders (G.user_cylinders ) #define user_heads (G.user_heads ) #define user_sectors (G.user_sectors ) #define pt_heads (G.pt_heads ) #define pt_sectors (G.pt_sectors ) #define kern_heads (G.kern_heads ) #define kern_sectors (G.kern_sectors ) #define extended_offset (G.extended_offset ) #define total_number_of_sectors (G.total_number_of_sectors) #define listingbuf (G.listingbuf ) #define line_buffer (G.line_buffer ) #define partname_buffer (G.partname_buffer) #define MBRbuffer (G.MBRbuffer ) #define ptes (G.ptes ) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ sector_size = DEFAULT_SECTOR_SIZE; \ sector_offset = 1; \ g_partitions = 4; \ display_in_cyl_units = 1; \ units_per_sector = 1; \ dos_compatible_flag = 1; \ } while (0) /* TODO: move to libbb? */ /* TODO: return unsigned long long, FEATURE_FDISK_BLKSIZE _can_ handle * disks > 2^32 sectors */ static sector_t bb_BLKGETSIZE_sectors(int fd) { uint64_t v64; unsigned long longsectors; if (ioctl(fd, BLKGETSIZE64, &v64) == 0) { /* Got bytes, convert to 512 byte sectors */ v64 >>= 9; if (v64 != (sector_t)v64) { ret_trunc: /* Not only DOS, but all other partition tables * we support can't record more than 32 bit * sector counts or offsets */ bb_error_msg("device has more than 2^32 sectors, can't use all of them"); v64 = (uint32_t)-1L; } return v64; } /* Needs temp of type long */ if (ioctl(fd, BLKGETSIZE, &longsectors)) { /* Perhaps this is a disk image */ off_t sz = lseek(fd, 0, SEEK_END); longsectors = 0; if (sz > 0) longsectors = (uoff_t)sz / sector_size; lseek(fd, 0, SEEK_SET); } if (sizeof(long) > sizeof(sector_t) && longsectors != (sector_t)longsectors ) { goto ret_trunc; } return longsectors; } #define IS_EXTENDED(i) \ ((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED) #define cround(n) (display_in_cyl_units ? ((n)/units_per_sector)+1 : (n)) #define scround(x) (((x)+units_per_sector-1)/units_per_sector) #define pt_offset(b, n) \ ((struct partition *)((b) + 0x1be + (n) * sizeof(struct partition))) #define sector(s) ((s) & 0x3f) #define cylinder(s, c) ((c) | (((s) & 0xc0) << 2)) #define hsc2sector(h,s,c) \ (sector(s) - 1 + sectors * ((h) + heads * cylinder(s,c))) static void close_dev_fd(void) { /* Not really closing, but making sure it is open, and to harmless place */ xmove_fd(xopen(bb_dev_null, O_RDONLY), dev_fd); } /* * Return partition name - uses static storage */ static const char * partname(const char *dev, int pno, int lth) { const char *p; int w, wp; int bufsiz; char *bufp; bufp = partname_buffer; bufsiz = sizeof(partname_buffer); w = strlen(dev); p = ""; if (isdigit(dev[w-1])) p = "p"; /* devfs kludge - note: fdisk partition names are not supposed to equal kernel names, so there is no reason to do this */ if (strcmp(dev + w - 4, "disc") == 0) { w -= 4; p = "part"; } wp = strlen(p); if (lth) { snprintf(bufp, bufsiz, "%*.*s%s%-2u", lth-wp-2, w, dev, p, pno); } else { snprintf(bufp, bufsiz, "%.*s%s%-2u", w, dev, p, pno); } return bufp; } static ALWAYS_INLINE struct partition * get_part_table(int i) { return ptes[i].part_table; } static const char * str_units(int n) { /* n==1: use singular */ if (n == 1) return display_in_cyl_units ? "cylinder" : "sector"; return display_in_cyl_units ? "cylinders" : "sectors"; } static int valid_part_table_flag(const char *mbuffer) { return (mbuffer[510] == 0x55 && (uint8_t)mbuffer[511] == 0xaa); } static void fdisk_fatal(const char *why) { if (listing) { close_dev_fd(); longjmp(listingbuf, 1); } bb_error_msg_and_die(why, disk_device); } static void seek_sector(sector_t secno) { #if ENABLE_FDISK_SUPPORT_LARGE_DISKS off64_t off = (off64_t)secno * sector_size; if (lseek64(dev_fd, off, SEEK_SET) == (off64_t) -1) fdisk_fatal(unable_to_seek); #else uint64_t off = (uint64_t)secno * sector_size; if (off > MAXINT(off_t) || lseek(dev_fd, (off_t)off, SEEK_SET) == (off_t) -1 ) { fdisk_fatal(unable_to_seek); } #endif } #if ENABLE_FEATURE_FDISK_WRITABLE /* Read line; return 0 or first printable char */ static int read_line(const char *prompt) { int sz; sz = read_line_input(NULL, prompt, line_buffer, sizeof(line_buffer), /*timeout*/ -1); if (sz <= 0) exit(EXIT_SUCCESS); /* Ctrl-D or Ctrl-C */ if (line_buffer[sz-1] == '\n') line_buffer[--sz] = '\0'; line_ptr = line_buffer; while (*line_ptr != '\0' && (unsigned char)*line_ptr <= ' ') line_ptr++; return *line_ptr; } static void set_all_unchanged(void) { int i; for (i = 0; i < MAXIMUM_PARTS; i++) ptes[i].changed = 0; } static ALWAYS_INLINE void set_changed(int i) { ptes[i].changed = 1; } static ALWAYS_INLINE void write_part_table_flag(char *b) { b[510] = 0x55; b[511] = 0xaa; } static char read_nonempty(const char *mesg) { while (!read_line(mesg)) continue; return *line_ptr; } static char read_maybe_empty(const char *mesg) { if (!read_line(mesg)) { line_ptr = line_buffer; line_ptr[0] = '\n'; line_ptr[1] = '\0'; } return line_ptr[0]; } static int read_hex(const char *const *sys) { unsigned long v; while (1) { read_nonempty("Hex code (type L to list codes): "); if ((line_ptr[0] | 0x20) == 'l') { list_types(sys); continue; } v = bb_strtoul(line_ptr, NULL, 16); if (v <= 0xff) return v; } } static void write_sector(sector_t secno, const void *buf) { seek_sector(secno); xwrite(dev_fd, buf, sector_size); } #endif /* FEATURE_FDISK_WRITABLE */ #include "fdisk_aix.c" struct sun_partition { unsigned char info[128]; /* Informative text string */ unsigned char spare0[14]; struct sun_info { unsigned char spare1; unsigned char id; unsigned char spare2; unsigned char flags; } infos[8]; unsigned char spare1[246]; /* Boot information etc. */ unsigned short rspeed; /* Disk rotational speed */ unsigned short pcylcount; /* Physical cylinder count */ unsigned short sparecyl; /* extra sects per cylinder */ unsigned char spare2[4]; /* More magic... */ unsigned short ilfact; /* Interleave factor */ unsigned short ncyl; /* Data cylinder count */ unsigned short nacyl; /* Alt. cylinder count */ unsigned short ntrks; /* Tracks per cylinder */ unsigned short nsect; /* Sectors per track */ unsigned char spare3[4]; /* Even more magic... */ struct sun_partinfo { uint32_t start_cylinder; uint32_t num_sectors; } partitions[8]; unsigned short magic; /* Magic number */ unsigned short csum; /* Label xor'd checksum */ } FIX_ALIASING; typedef struct sun_partition sun_partition; #define sunlabel ((sun_partition *)MBRbuffer) STATIC_OSF void bsd_select(void); STATIC_OSF void xbsd_print_disklabel(int); #include "fdisk_osf.c" STATIC_GPT void gpt_list_table(int xtra); #include "fdisk_gpt.c" #if ENABLE_FEATURE_SGI_LABEL || ENABLE_FEATURE_SUN_LABEL static uint16_t fdisk_swap16(uint16_t x) { return (x << 8) | (x >> 8); } static uint32_t fdisk_swap32(uint32_t x) { return (x << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | (x >> 24); } #endif STATIC_SGI const char *const sgi_sys_types[]; STATIC_SGI unsigned sgi_get_num_sectors(int i); STATIC_SGI int sgi_get_sysid(int i); STATIC_SGI void sgi_delete_partition(int i); STATIC_SGI void sgi_change_sysid(int i, int sys); STATIC_SGI void sgi_list_table(int xtra); #if ENABLE_FEATURE_FDISK_ADVANCED STATIC_SGI void sgi_set_xcyl(void); #endif STATIC_SGI int verify_sgi(int verbose); STATIC_SGI void sgi_add_partition(int n, int sys); STATIC_SGI void sgi_set_swappartition(int i); STATIC_SGI const char *sgi_get_bootfile(void); STATIC_SGI void sgi_set_bootfile(const char* aFile); STATIC_SGI void create_sgiinfo(void); STATIC_SGI void sgi_write_table(void); STATIC_SGI void sgi_set_bootpartition(int i); #include "fdisk_sgi.c" STATIC_SUN const char *const sun_sys_types[]; STATIC_SUN void sun_delete_partition(int i); STATIC_SUN void sun_change_sysid(int i, int sys); STATIC_SUN void sun_list_table(int xtra); STATIC_SUN void add_sun_partition(int n, int sys); #if ENABLE_FEATURE_FDISK_ADVANCED STATIC_SUN void sun_set_alt_cyl(void); STATIC_SUN void sun_set_ncyl(int cyl); STATIC_SUN void sun_set_xcyl(void); STATIC_SUN void sun_set_ilfact(void); STATIC_SUN void sun_set_rspeed(void); STATIC_SUN void sun_set_pcylcount(void); #endif STATIC_SUN void toggle_sunflags(int i, unsigned char mask); STATIC_SUN void verify_sun(void); STATIC_SUN void sun_write_table(void); #include "fdisk_sun.c" static inline_if_little_endian unsigned read4_little_endian(const unsigned char *cp) { uint32_t v; move_from_unaligned32(v, cp); return SWAP_LE32(v); } static sector_t get_start_sect(const struct partition *p) { return read4_little_endian(p->start4); } static sector_t get_nr_sects(const struct partition *p) { return read4_little_endian(p->size4); } #if ENABLE_FEATURE_FDISK_WRITABLE /* start_sect and nr_sects are stored little endian on all machines */ /* moreover, they are not aligned correctly */ static inline_if_little_endian void store4_little_endian(unsigned char *cp, unsigned val) { uint32_t v = SWAP_LE32(val); move_to_unaligned32(cp, v); } static void set_start_sect(struct partition *p, unsigned start_sect) { store4_little_endian(p->start4, start_sect); } static void set_nr_sects(struct partition *p, unsigned nr_sects) { store4_little_endian(p->size4, nr_sects); } #endif /* Allocate a buffer and read a partition table sector */ static void read_pte(struct pte *pe, sector_t offset) { pe->offset_from_dev_start = offset; pe->sectorbuffer = xzalloc(sector_size); seek_sector(offset); /* xread would make us abort - bad for fdisk -l */ if (full_read(dev_fd, pe->sectorbuffer, sector_size) != sector_size) fdisk_fatal(unable_to_read); #if ENABLE_FEATURE_FDISK_WRITABLE pe->changed = 0; #endif pe->part_table = pe->ext_pointer = NULL; } static sector_t get_partition_start_from_dev_start(const struct pte *pe) { return pe->offset_from_dev_start + get_start_sect(pe->part_table); } #if ENABLE_FEATURE_FDISK_WRITABLE /* * Avoid warning about DOS partitions when no DOS partition was changed. * Here a heuristic "is probably dos partition". * We might also do the opposite and warn in all cases except * for "is probably nondos partition". */ #ifdef UNUSED static int is_dos_partition(int t) { return (t == 1 || t == 4 || t == 6 || t == 0x0b || t == 0x0c || t == 0x0e || t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 || t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 || t == 0xc1 || t == 0xc4 || t == 0xc6); } #endif static void menu(void) { puts("Command Action"); if (LABEL_IS_SUN) { puts("a\ttoggle a read only flag"); /* sun */ puts("b\tedit bsd disklabel"); puts("c\ttoggle the mountable flag"); /* sun */ puts("d\tdelete a partition"); puts("l\tlist known partition types"); puts("n\tadd a new partition"); puts("o\tcreate a new empty DOS partition table"); puts("p\tprint the partition table"); puts("q\tquit without saving changes"); puts("s\tcreate a new empty Sun disklabel"); /* sun */ puts("t\tchange a partition's system id"); puts("u\tchange display/entry units"); puts("v\tverify the partition table"); puts("w\twrite table to disk and exit"); #if ENABLE_FEATURE_FDISK_ADVANCED puts("x\textra functionality (experts only)"); #endif } else if (LABEL_IS_SGI) { puts("a\tselect bootable partition"); /* sgi flavour */ puts("b\tedit bootfile entry"); /* sgi */ puts("c\tselect sgi swap partition"); /* sgi flavour */ puts("d\tdelete a partition"); puts("l\tlist known partition types"); puts("n\tadd a new partition"); puts("o\tcreate a new empty DOS partition table"); puts("p\tprint the partition table"); puts("q\tquit without saving changes"); puts("s\tcreate a new empty Sun disklabel"); /* sun */ puts("t\tchange a partition's system id"); puts("u\tchange display/entry units"); puts("v\tverify the partition table"); puts("w\twrite table to disk and exit"); } else if (LABEL_IS_AIX) { puts("o\tcreate a new empty DOS partition table"); puts("q\tquit without saving changes"); puts("s\tcreate a new empty Sun disklabel"); /* sun */ } else if (LABEL_IS_GPT) { puts("o\tcreate a new empty DOS partition table"); puts("p\tprint the partition table"); puts("q\tquit without saving changes"); puts("s\tcreate a new empty Sun disklabel"); /* sun */ } else { puts("a\ttoggle a bootable flag"); puts("b\tedit bsd disklabel"); puts("c\ttoggle the dos compatibility flag"); puts("d\tdelete a partition"); puts("l\tlist known partition types"); puts("n\tadd a new partition"); puts("o\tcreate a new empty DOS partition table"); puts("p\tprint the partition table"); puts("q\tquit without saving changes"); puts("s\tcreate a new empty Sun disklabel"); /* sun */ puts("t\tchange a partition's system id"); puts("u\tchange display/entry units"); puts("v\tverify the partition table"); puts("w\twrite table to disk and exit"); #if ENABLE_FEATURE_FDISK_ADVANCED puts("x\textra functionality (experts only)"); #endif } } #endif /* FEATURE_FDISK_WRITABLE */ #if ENABLE_FEATURE_FDISK_ADVANCED static void xmenu(void) { puts("Command Action"); if (LABEL_IS_SUN) { puts("a\tchange number of alternate cylinders"); /*sun*/ puts("c\tchange number of cylinders"); puts("d\tprint the raw data in the partition table"); puts("e\tchange number of extra sectors per cylinder");/*sun*/ puts("h\tchange number of heads"); puts("i\tchange interleave factor"); /*sun*/ puts("o\tchange rotation speed (rpm)"); /*sun*/ puts("p\tprint the partition table"); puts("q\tquit without saving changes"); puts("r\treturn to main menu"); puts("s\tchange number of sectors/track"); puts("v\tverify the partition table"); puts("w\twrite table to disk and exit"); puts("y\tchange number of physical cylinders"); /*sun*/ } else if (LABEL_IS_SGI) { puts("b\tmove beginning of data in a partition"); /* !sun */ puts("c\tchange number of cylinders"); puts("d\tprint the raw data in the partition table"); puts("e\tlist extended partitions"); /* !sun */ puts("g\tcreate an IRIX (SGI) partition table");/* sgi */ puts("h\tchange number of heads"); puts("p\tprint the partition table"); puts("q\tquit without saving changes"); puts("r\treturn to main menu"); puts("s\tchange number of sectors/track"); puts("v\tverify the partition table"); puts("w\twrite table to disk and exit"); } else if (LABEL_IS_AIX) { puts("b\tmove beginning of data in a partition"); /* !sun */ puts("c\tchange number of cylinders"); puts("d\tprint the raw data in the partition table"); puts("e\tlist extended partitions"); /* !sun */ puts("g\tcreate an IRIX (SGI) partition table");/* sgi */ puts("h\tchange number of heads"); puts("p\tprint the partition table"); puts("q\tquit without saving changes"); puts("r\treturn to main menu"); puts("s\tchange number of sectors/track"); puts("v\tverify the partition table"); puts("w\twrite table to disk and exit"); } else { puts("b\tmove beginning of data in a partition"); /* !sun */ puts("c\tchange number of cylinders"); puts("d\tprint the raw data in the partition table"); puts("e\tlist extended partitions"); /* !sun */ puts("f\tfix partition order"); /* !sun, !aix, !sgi */ #if ENABLE_FEATURE_SGI_LABEL puts("g\tcreate an IRIX (SGI) partition table");/* sgi */ #endif puts("h\tchange number of heads"); puts("p\tprint the partition table"); puts("q\tquit without saving changes"); puts("r\treturn to main menu"); puts("s\tchange number of sectors/track"); puts("v\tverify the partition table"); puts("w\twrite table to disk and exit"); } } #endif /* ADVANCED mode */ #if ENABLE_FEATURE_FDISK_WRITABLE static const char *const * get_sys_types(void) { return ( LABEL_IS_SUN ? sun_sys_types : LABEL_IS_SGI ? sgi_sys_types : i386_sys_types); } #else #define get_sys_types() i386_sys_types #endif static const char * partition_type(unsigned char type) { int i; const char *const *types = get_sys_types(); for (i = 0; types[i]; i++) if ((unsigned char)types[i][0] == type) return types[i] + 1; return "Unknown"; } static int is_cleared_partition(const struct partition *p) { /* We consider partition "cleared" only if it has only zeros */ const char *cp = (const char *)p; int cnt = sizeof(*p); char bits = 0; while (--cnt >= 0) bits |= *cp++; return (bits == 0); } static void clear_partition(struct partition *p) { if (p) memset(p, 0, sizeof(*p)); } #if ENABLE_FEATURE_FDISK_WRITABLE static int get_sysid(int i) { return LABEL_IS_SUN ? sunlabel->infos[i].id : (LABEL_IS_SGI ? sgi_get_sysid(i) : ptes[i].part_table->sys_ind); } static void list_types(const char *const *sys) { enum { COLS = 3 }; unsigned last[COLS]; unsigned done, next, size; int i; for (size = 0; sys[size]; size++) continue; done = 0; for (i = COLS-1; i >= 0; i--) { done += (size + i - done) / (i + 1); last[COLS-1 - i] = done; } i = done = next = 0; do { printf("%c%2x %-22.22s", i ? ' ' : '\n', (unsigned char)sys[next][0], sys[next] + 1); next = last[i++] + done; if (i >= COLS || next >= last[i]) { i = 0; next = ++done; } } while (done < last[0]); bb_putchar('\n'); } #define set_hsc(h, s, c, sector) do \ { \ s = sector % g_sectors + 1; \ sector /= g_sectors; \ h = sector % g_heads; \ sector /= g_heads; \ c = sector & 0xff; \ s |= (sector >> 2) & 0xc0; \ } while (0) static void set_hsc_start_end(struct partition *p, sector_t start, sector_t stop) { if (dos_compatible_flag && (start / (g_sectors * g_heads) > 1023)) start = g_heads * g_sectors * 1024 - 1; set_hsc(p->head, p->sector, p->cyl, start); if (dos_compatible_flag && (stop / (g_sectors * g_heads) > 1023)) stop = g_heads * g_sectors * 1024 - 1; set_hsc(p->end_head, p->end_sector, p->end_cyl, stop); } static void set_partition(int i, int doext, sector_t start, sector_t stop, int sysid) { struct partition *p; sector_t offset; if (doext) { p = ptes[i].ext_pointer; offset = extended_offset; } else { p = ptes[i].part_table; offset = ptes[i].offset_from_dev_start; } p->boot_ind = 0; p->sys_ind = sysid; set_start_sect(p, start - offset); set_nr_sects(p, stop - start + 1); set_hsc_start_end(p, start, stop); ptes[i].changed = 1; } #endif static int warn_geometry(void) { if (g_heads && g_sectors && g_cylinders) return 0; printf("Unknown value(s) for:"); if (!g_heads) printf(" heads"); if (!g_sectors) printf(" sectors"); if (!g_cylinders) printf(" cylinders"); printf( #if ENABLE_FEATURE_FDISK_WRITABLE " (settable in the extra functions menu)" #endif "\n"); return 1; } static void update_units(void) { int cyl_units = g_heads * g_sectors; if (display_in_cyl_units && cyl_units) units_per_sector = cyl_units; else units_per_sector = 1; /* in sectors */ } #if ENABLE_FEATURE_FDISK_WRITABLE static void warn_cylinders(void) { if (LABEL_IS_DOS && g_cylinders > 1024 && !nowarn) printf("\n" "The number of cylinders for this disk is set to %u.\n" "There is nothing wrong with that, but this is larger than 1024,\n" "and could in certain setups cause problems with:\n" "1) software that runs at boot time (e.g., old versions of LILO)\n" "2) booting and partitioning software from other OSs\n" " (e.g., DOS FDISK, OS/2 FDISK)\n", g_cylinders); } #endif static void read_extended(int ext) { int i; struct pte *pex; struct partition *p, *q; ext_index = ext; pex = &ptes[ext]; pex->ext_pointer = pex->part_table; p = pex->part_table; if (!get_start_sect(p)) { printf("Bad offset in primary extended partition\n"); return; } while (IS_EXTENDED(p->sys_ind)) { struct pte *pe = &ptes[g_partitions]; if (g_partitions >= MAXIMUM_PARTS) { /* This is not a Linux restriction, but this program uses arrays of size MAXIMUM_PARTS. Do not try to 'improve' this test. */ struct pte *pre = &ptes[g_partitions - 1]; #if ENABLE_FEATURE_FDISK_WRITABLE printf("Warning: deleting partitions after %u\n", g_partitions); pre->changed = 1; #endif clear_partition(pre->ext_pointer); return; } read_pte(pe, extended_offset + get_start_sect(p)); if (!extended_offset) extended_offset = get_start_sect(p); q = p = pt_offset(pe->sectorbuffer, 0); for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) { if (IS_EXTENDED(p->sys_ind)) { if (pe->ext_pointer) printf("Warning: extra link " "pointer in partition table" " %u\n", g_partitions + 1); else pe->ext_pointer = p; } else if (p->sys_ind) { if (pe->part_table) printf("Warning: ignoring extra " "data in partition table" " %u\n", g_partitions + 1); else pe->part_table = p; } } /* very strange code here... */ if (!pe->part_table) { if (q != pe->ext_pointer) pe->part_table = q; else pe->part_table = q + 1; } if (!pe->ext_pointer) { if (q != pe->part_table) pe->ext_pointer = q; else pe->ext_pointer = q + 1; } p = pe->ext_pointer; g_partitions++; } #if ENABLE_FEATURE_FDISK_WRITABLE /* remove empty links */ remove: for (i = 4; i < g_partitions; i++) { struct pte *pe = &ptes[i]; if (!get_nr_sects(pe->part_table) && (g_partitions > 5 || ptes[4].part_table->sys_ind) ) { printf("Omitting empty partition (%u)\n", i+1); delete_partition(i); goto remove; /* numbering changed */ } } #endif } #if ENABLE_FEATURE_FDISK_WRITABLE static void create_doslabel(void) { printf(msg_building_new_label, "DOS disklabel"); current_label_type = LABEL_DOS; #if ENABLE_FEATURE_OSF_LABEL possibly_osf_label = 0; #endif g_partitions = 4; memset(&MBRbuffer[510 - 4*16], 0, 4*16); write_part_table_flag(MBRbuffer); extended_offset = 0; set_all_unchanged(); set_changed(0); get_boot(CREATE_EMPTY_DOS); } #endif static void get_sectorsize(void) { if (!user_set_sector_size) { int arg; if (ioctl(dev_fd, BLKSSZGET, &arg) == 0) sector_size = arg; if (sector_size != DEFAULT_SECTOR_SIZE) printf("Note: sector size is %u " "(not " DEFAULT_SECTOR_SIZE_STR ")\n", sector_size); } } static void get_kernel_geometry(void) { struct hd_geometry geometry; if (!ioctl(dev_fd, HDIO_GETGEO, &geometry)) { kern_heads = geometry.heads; kern_sectors = geometry.sectors; /* never use geometry.cylinders - it is truncated */ } } static void get_partition_table_geometry(void) { const unsigned char *bufp = (const unsigned char *)MBRbuffer; struct partition *p; int i, h, s, hh, ss; int first = 1; int bad = 0; if (!(valid_part_table_flag((char*)bufp))) return; hh = ss = 0; for (i = 0; i < 4; i++) { p = pt_offset(bufp, i); if (p->sys_ind != 0) { h = p->end_head + 1; s = (p->end_sector & 077); if (first) { hh = h; ss = s; first = 0; } else if (hh != h || ss != s) bad = 1; } } if (!first && !bad) { pt_heads = hh; pt_sectors = ss; } } static void get_geometry(void) { int sec_fac; get_sectorsize(); sec_fac = sector_size / 512; #if ENABLE_FEATURE_SUN_LABEL guess_device_type(); #endif g_heads = g_cylinders = g_sectors = 0; kern_heads = kern_sectors = 0; pt_heads = pt_sectors = 0; get_kernel_geometry(); get_partition_table_geometry(); g_heads = user_heads ? user_heads : pt_heads ? pt_heads : kern_heads ? kern_heads : 255; g_sectors = user_sectors ? user_sectors : pt_sectors ? pt_sectors : kern_sectors ? kern_sectors : 63; total_number_of_sectors = bb_BLKGETSIZE_sectors(dev_fd); sector_offset = 1; if (dos_compatible_flag) sector_offset = g_sectors; g_cylinders = total_number_of_sectors / (g_heads * g_sectors * sec_fac); if (!g_cylinders) g_cylinders = user_cylinders; } /* * Opens disk_device and optionally reads MBR. * If what == OPEN_MAIN: * Open device, read MBR. Abort program on short read. Create empty * disklabel if the on-disk structure is invalid (WRITABLE mode). * If what == TRY_ONLY: * Open device, read MBR. Return an error if anything is out of place. * Do not create an empty disklabel. This is used for the "list" * operations: "fdisk -l /dev/sda" and "fdisk -l" (all devices). * If what == CREATE_EMPTY_*: * This means that get_boot() was called recursively from create_*label(). * Do not re-open the device; just set up the ptes array and print * geometry warnings. * * Returns: * -1: no 0xaa55 flag present (possibly entire disk BSD) * 0: found or created label * 1: I/O error */ #if ENABLE_FEATURE_SUN_LABEL || ENABLE_FEATURE_FDISK_WRITABLE static int get_boot(enum action what) #else static int get_boot(void) #define get_boot(what) get_boot() #endif { int i, fd; g_partitions = 4; for (i = 0; i < 4; i++) { struct pte *pe = &ptes[i]; pe->part_table = pt_offset(MBRbuffer, i); pe->ext_pointer = NULL; pe->offset_from_dev_start = 0; pe->sectorbuffer = MBRbuffer; #if ENABLE_FEATURE_FDISK_WRITABLE pe->changed = (what == CREATE_EMPTY_DOS); #endif } #if ENABLE_FEATURE_FDISK_WRITABLE // ALERT! highly idiotic design! // We end up here when we call get_boot() recursively // via get_boot() [table is bad] -> create_doslabel() -> get_boot(CREATE_EMPTY_DOS). // or get_boot() [table is bad] -> create_sunlabel() -> get_boot(CREATE_EMPTY_SUN). // (just factor out re-init of ptes[0,1,2,3] in a separate fn instead?) // So skip opening device _again_... if (what == CREATE_EMPTY_DOS IF_FEATURE_SUN_LABEL(|| what == CREATE_EMPTY_SUN)) goto created_table; fd = open(disk_device, (option_mask32 & OPT_l) ? O_RDONLY : O_RDWR); if (fd < 0) { fd = open(disk_device, O_RDONLY); if (fd < 0) { if (what == TRY_ONLY) return 1; fdisk_fatal(unable_to_open); } printf("'%s' is opened for read only\n", disk_device); } xmove_fd(fd, dev_fd); if (512 != full_read(dev_fd, MBRbuffer, 512)) { if (what == TRY_ONLY) { close_dev_fd(); return 1; } fdisk_fatal(unable_to_read); } #else fd = open(disk_device, O_RDONLY); if (fd < 0) return 1; if (512 != full_read(fd, MBRbuffer, 512)) { close(fd); return 1; } xmove_fd(fd, dev_fd); #endif get_geometry(); update_units(); #if ENABLE_FEATURE_SUN_LABEL if (check_sun_label()) return 0; #endif #if ENABLE_FEATURE_SGI_LABEL if (check_sgi_label()) return 0; #endif #if ENABLE_FEATURE_AIX_LABEL if (check_aix_label()) return 0; #endif #if ENABLE_FEATURE_GPT_LABEL if (check_gpt_label()) return 0; #endif #if ENABLE_FEATURE_OSF_LABEL if (check_osf_label()) { possibly_osf_label = 1; if (!valid_part_table_flag(MBRbuffer)) { current_label_type = LABEL_OSF; return 0; } printf("This disk has both DOS and BSD magic.\n" "Give the 'b' command to go to BSD mode.\n"); } #endif #if !ENABLE_FEATURE_FDISK_WRITABLE if (!valid_part_table_flag(MBRbuffer)) return -1; #else if (!valid_part_table_flag(MBRbuffer)) { if (what == OPEN_MAIN) { printf("Device contains neither a valid DOS " "partition table, nor Sun, SGI, OSF or GPT " "disklabel\n"); #ifdef __sparc__ IF_FEATURE_SUN_LABEL(create_sunlabel();) #else create_doslabel(); #endif return 0; } /* TRY_ONLY: */ return -1; } created_table: #endif /* FEATURE_FDISK_WRITABLE */ IF_FEATURE_FDISK_WRITABLE(warn_cylinders();) warn_geometry(); for (i = 0; i < 4; i++) { if (IS_EXTENDED(ptes[i].part_table->sys_ind)) { if (g_partitions != 4) printf("Ignoring extra extended " "partition %u\n", i + 1); else read_extended(i); } } for (i = 3; i < g_partitions; i++) { struct pte *pe = &ptes[i]; if (!valid_part_table_flag(pe->sectorbuffer)) { printf("Warning: invalid flag 0x%02x,0x%02x of partition " "table %u will be corrected by w(rite)\n", pe->sectorbuffer[510], pe->sectorbuffer[511], i + 1); IF_FEATURE_FDISK_WRITABLE(pe->changed = 1;) } } return 0; } #if ENABLE_FEATURE_FDISK_WRITABLE /* * Print the message MESG, then read an integer between LOW and HIGH (inclusive). * If the user hits Enter, DFLT is returned. * Answers like +10 are interpreted as offsets from BASE. * * There is no default if DFLT is not between LOW and HIGH. */ static sector_t read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char *mesg) { sector_t value; int default_ok = 1; const char *fmt = "%s (%u-%u, default %u): "; if (dflt < low || dflt > high) { fmt = "%s (%u-%u): "; default_ok = 0; } while (1) { int use_default = default_ok; /* ask question and read answer */ do { printf(fmt, mesg, low, high, dflt); read_maybe_empty(""); } while (*line_ptr != '\n' && !isdigit(*line_ptr) && *line_ptr != '-' && *line_ptr != '+'); if (*line_ptr == '+' || *line_ptr == '-') { int minus = (*line_ptr == '-'); int absolute = 0; value = atoi(line_ptr + 1); /* (1) if 2nd char is digit, use_default = 0. * (2) move line_ptr to first non-digit. */ while (isdigit(*++line_ptr)) use_default = 0; switch (*line_ptr) { case 'c': case 'C': if (!display_in_cyl_units) value *= g_heads * g_sectors; break; case 'K': absolute = 1024; break; case 'k': absolute = 1000; break; case 'm': case 'M': absolute = 1000000; break; case 'g': case 'G': absolute = 1000000000; break; default: break; } if (absolute) { ullong bytes; unsigned long unit; bytes = (ullong) value * absolute; unit = sector_size * units_per_sector; bytes += unit/2; /* round */ bytes /= unit; value = bytes; } if (minus) value = -value; value += base; } else { value = atoi(line_ptr); while (isdigit(*line_ptr)) { line_ptr++; use_default = 0; } } if (use_default) { value = dflt; printf("Using default value %u\n", value); } if (value >= low && value <= high) break; printf("Value is out of range\n"); } return value; } static unsigned get_partition(int warn, unsigned max) { struct pte *pe; unsigned i; i = read_int(1, 0, max, 0, "Partition number") - 1; pe = &ptes[i]; if (warn) { if ((!LABEL_IS_SUN && !LABEL_IS_SGI && !pe->part_table->sys_ind) || (LABEL_IS_SUN && (!sunlabel->partitions[i].num_sectors || !sunlabel->infos[i].id)) || (LABEL_IS_SGI && !sgi_get_num_sectors(i)) ) { printf("Warning: partition %u has empty type\n", i+1); } } return i; } static int get_existing_partition(int warn, unsigned max) { int pno = -1; unsigned i; for (i = 0; i < max; i++) { struct pte *pe = &ptes[i]; struct partition *p = pe->part_table; if (p && !is_cleared_partition(p)) { if (pno >= 0) goto not_unique; pno = i; } } if (pno >= 0) { printf("Selected partition %u\n", pno+1); return pno; } printf("No partition is defined yet!\n"); return -1; not_unique: return get_partition(warn, max); } static int get_nonexisting_partition(int warn, unsigned max) { int pno = -1; unsigned i; for (i = 0; i < max; i++) { struct pte *pe = &ptes[i]; struct partition *p = pe->part_table; if (p && is_cleared_partition(p)) { if (pno >= 0) goto not_unique; pno = i; } } if (pno >= 0) { printf("Selected partition %u\n", pno+1); return pno; } printf("All primary partitions have been defined already!\n"); return -1; not_unique: return get_partition(warn, max); } static void change_units(void) { display_in_cyl_units = !display_in_cyl_units; update_units(); printf("Changing display/entry units to %s\n", str_units(PLURAL)); } static void toggle_active(int i) { struct pte *pe = &ptes[i]; struct partition *p = pe->part_table; if (IS_EXTENDED(p->sys_ind) && !p->boot_ind) printf("WARNING: Partition %u is an extended partition\n", i + 1); p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG); pe->changed = 1; } static void toggle_dos_compatibility_flag(void) { dos_compatible_flag = 1 - dos_compatible_flag; if (dos_compatible_flag) { sector_offset = g_sectors; printf("DOS Compatibility flag is set\n"); } else { sector_offset = 1; printf("DOS Compatibility flag is not set\n"); } } static void delete_partition(int i) { struct pte *pe = &ptes[i]; struct partition *p = pe->part_table; struct partition *q = pe->ext_pointer; /* Note that for the fifth partition (i == 4) we don't actually * decrement partitions. */ if (warn_geometry()) return; /* C/H/S not set */ pe->changed = 1; if (LABEL_IS_SUN) { sun_delete_partition(i); return; } if (LABEL_IS_SGI) { sgi_delete_partition(i); return; } if (i < 4) { if (IS_EXTENDED(p->sys_ind) && i == ext_index) { g_partitions = 4; ptes[ext_index].ext_pointer = NULL; extended_offset = 0; } clear_partition(p); return; } if (!q->sys_ind && i > 4) { /* the last one in the chain - just delete */ --g_partitions; --i; clear_partition(ptes[i].ext_pointer); ptes[i].changed = 1; } else { /* not the last one - further ones will be moved down */ if (i > 4) { /* delete this link in the chain */ p = ptes[i-1].ext_pointer; *p = *q; set_start_sect(p, get_start_sect(q)); set_nr_sects(p, get_nr_sects(q)); ptes[i-1].changed = 1; } else if (g_partitions > 5) { /* 5 will be moved to 4 */ /* the first logical in a longer chain */ pe = &ptes[5]; if (pe->part_table) /* prevent SEGFAULT */ set_start_sect(pe->part_table, get_partition_start_from_dev_start(pe) - extended_offset); pe->offset_from_dev_start = extended_offset; pe->changed = 1; } if (g_partitions > 5) { g_partitions--; while (i < g_partitions) { ptes[i] = ptes[i+1]; i++; } } else { /* the only logical: clear only */ clear_partition(ptes[i].part_table); } } } static void change_sysid(void) { int i, sys, origsys; struct partition *p; /* If sgi_label then don't use get_existing_partition, let the user select a partition, since get_existing_partition() only works for Linux like partition tables. */ if (!LABEL_IS_SGI) { i = get_existing_partition(0, g_partitions); } else { i = get_partition(0, g_partitions); } if (i == -1) return; p = ptes[i].part_table; origsys = sys = get_sysid(i); /* if changing types T to 0 is allowed, then the reverse change must be allowed, too */ if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN && !get_nr_sects(p)) { printf("Partition %u does not exist yet!\n", i + 1); return; } while (1) { sys = read_hex(get_sys_types()); if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN) { printf("Type 0 means free space to many systems\n" "(but not to Linux). Having partitions of\n" "type 0 is probably unwise.\n"); /* break; */ } if (!LABEL_IS_SUN && !LABEL_IS_SGI) { if (IS_EXTENDED(sys) != IS_EXTENDED(p->sys_ind)) { printf("You cannot change a partition into" " an extended one or vice versa\n"); break; } } if (sys < 256) { #if ENABLE_FEATURE_SUN_LABEL if (LABEL_IS_SUN && i == 2 && sys != SUN_WHOLE_DISK) printf("Consider leaving partition 3 " "as Whole disk (5),\n" "as SunOS/Solaris expects it and " "even Linux likes it\n\n"); #endif #if ENABLE_FEATURE_SGI_LABEL if (LABEL_IS_SGI && ( (i == 10 && sys != SGI_ENTIRE_DISK) || (i == 8 && sys != 0) ) ) { printf("Consider leaving partition 9 " "as volume header (0),\nand " "partition 11 as entire volume (6)" "as IRIX expects it\n\n"); } #endif if (sys == origsys) break; if (LABEL_IS_SUN) { sun_change_sysid(i, sys); } else if (LABEL_IS_SGI) { sgi_change_sysid(i, sys); } else p->sys_ind = sys; printf("Changed system type of partition %u " "to %x (%s)\n", i + 1, sys, partition_type(sys)); ptes[i].changed = 1; //if (is_dos_partition(origsys) || is_dos_partition(sys)) // dos_changed = 1; break; } } } #endif /* FEATURE_FDISK_WRITABLE */ /* check_consistency() and linear2chs() added Sat Mar 6 12:28:16 1993, * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross, * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S. * Lubkin Oct. 1991). */ static void linear2chs(unsigned ls, unsigned *c, unsigned *h, unsigned *s) { int spc = g_heads * g_sectors; *c = ls / spc; ls = ls % spc; *h = ls / g_sectors; *s = ls % g_sectors + 1; /* sectors count from 1 */ } static void check_consistency(const struct partition *p, int partition) { unsigned pbc, pbh, pbs; /* physical beginning c, h, s */ unsigned pec, peh, pes; /* physical ending c, h, s */ unsigned lbc, lbh, lbs; /* logical beginning c, h, s */ unsigned lec, leh, les; /* logical ending c, h, s */ if (!g_heads || !g_sectors || (partition >= 4)) return; /* do not check extended partitions */ /* physical beginning c, h, s */ pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300); pbh = p->head; pbs = p->sector & 0x3f; /* physical ending c, h, s */ pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300); peh = p->end_head; pes = p->end_sector & 0x3f; /* compute logical beginning (c, h, s) */ linear2chs(get_start_sect(p), &lbc, &lbh, &lbs); /* compute logical ending (c, h, s) */ linear2chs(get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les); /* Same physical / logical beginning? */ if (g_cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) { printf("Partition %u has different physical/logical " "beginnings (non-Linux?):\n", partition + 1); printf(" phys=(%u, %u, %u) ", pbc, pbh, pbs); printf("logical=(%u, %u, %u)\n", lbc, lbh, lbs); } /* Same physical / logical ending? */ if (g_cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) { printf("Partition %u has different physical/logical " "endings:\n", partition + 1); printf(" phys=(%u, %u, %u) ", pec, peh, pes); printf("logical=(%u, %u, %u)\n", lec, leh, les); } /* Ending on cylinder boundary? */ if (peh != (g_heads - 1) || pes != g_sectors) { printf("Partition %u does not end on cylinder boundary\n", partition + 1); } } static void list_disk_geometry(void) { ullong bytes = ((ullong)total_number_of_sectors << 9); long megabytes = bytes / 1000000; if (megabytes < 10000) printf("\nDisk %s: %lu MB, %llu bytes\n", disk_device, megabytes, bytes); else printf("\nDisk %s: %lu.%lu GB, %llu bytes\n", disk_device, megabytes/1000, (megabytes/100)%10, bytes); printf("%u heads, %u sectors/track, %u cylinders", g_heads, g_sectors, g_cylinders); if (units_per_sector == 1) printf(", total %"SECT_FMT"u sectors", total_number_of_sectors / (sector_size/512)); printf("\nUnits = %s of %u * %u = %u bytes\n\n", str_units(PLURAL), units_per_sector, sector_size, units_per_sector * sector_size); } /* * Check whether partition entries are ordered by their starting positions. * Return 0 if OK. Return i if partition i should have been earlier. * Two separate checks: primary and logical partitions. */ static int wrong_p_order(int *prev) { const struct pte *pe; const struct partition *p; sector_t last_p_start_pos = 0, p_start_pos; unsigned i, last_i = 0; for (i = 0; i < g_partitions; i++) { if (i == 4) { last_i = 4; last_p_start_pos = 0; } pe = &ptes[i]; p = pe->part_table; if (p->sys_ind) { p_start_pos = get_partition_start_from_dev_start(pe); if (last_p_start_pos > p_start_pos) { if (prev) *prev = last_i; return i; } last_p_start_pos = p_start_pos; last_i = i; } } return 0; } #if ENABLE_FEATURE_FDISK_ADVANCED /* * Fix the chain of logicals. * extended_offset is unchanged, the set of sectors used is unchanged * The chain is sorted so that sectors increase, and so that * starting sectors increase. * * After this it may still be that cfdisk doesnt like the table. * (This is because cfdisk considers expanded parts, from link to * end of partition, and these may still overlap.) * Now * sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda * may help. */ static void fix_chain_of_logicals(void) { int j, oj, ojj, sj, sjj; struct partition *pj,*pjj,tmp; /* Stage 1: sort sectors but leave sector of part 4 */ /* (Its sector is the global extended_offset.) */ stage1: for (j = 5; j < g_partitions - 1; j++) { oj = ptes[j].offset_from_dev_start; ojj = ptes[j+1].offset_from_dev_start; if (oj > ojj) { ptes[j].offset_from_dev_start = ojj; ptes[j+1].offset_from_dev_start = oj; pj = ptes[j].part_table; set_start_sect(pj, get_start_sect(pj)+oj-ojj); pjj = ptes[j+1].part_table; set_start_sect(pjj, get_start_sect(pjj)+ojj-oj); set_start_sect(ptes[j-1].ext_pointer, ojj-extended_offset); set_start_sect(ptes[j].ext_pointer, oj-extended_offset); goto stage1; } } /* Stage 2: sort starting sectors */ stage2: for (j = 4; j < g_partitions - 1; j++) { pj = ptes[j].part_table; pjj = ptes[j+1].part_table; sj = get_start_sect(pj); sjj = get_start_sect(pjj); oj = ptes[j].offset_from_dev_start; ojj = ptes[j+1].offset_from_dev_start; if (oj+sj > ojj+sjj) { tmp = *pj; *pj = *pjj; *pjj = tmp; set_start_sect(pj, ojj+sjj-oj); set_start_sect(pjj, oj+sj-ojj); goto stage2; } } /* Probably something was changed */ for (j = 4; j < g_partitions; j++) ptes[j].changed = 1; } static void fix_partition_table_order(void) { struct pte *pei, *pek; int i,k; if (!wrong_p_order(NULL)) { printf("Ordering is already correct\n\n"); return; } while ((i = wrong_p_order(&k)) != 0 && i < 4) { /* partition i should have come earlier, move it */ /* We have to move data in the MBR */ struct partition *pi, *pk, *pe, pbuf; pei = &ptes[i]; pek = &ptes[k]; pe = pei->ext_pointer; pei->ext_pointer = pek->ext_pointer; pek->ext_pointer = pe; pi = pei->part_table; pk = pek->part_table; memmove(&pbuf, pi, sizeof(struct partition)); memmove(pi, pk, sizeof(struct partition)); memmove(pk, &pbuf, sizeof(struct partition)); pei->changed = pek->changed = 1; } if (i) fix_chain_of_logicals(); printf("Done.\n"); } #endif static void list_table(int xtra) { const struct partition *p; int i, w; if (LABEL_IS_SUN) { sun_list_table(xtra); return; } if (LABEL_IS_SGI) { sgi_list_table(xtra); return; } if (LABEL_IS_GPT) { gpt_list_table(xtra); return; } list_disk_geometry(); if (LABEL_IS_OSF) { xbsd_print_disklabel(xtra); return; } /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3, but if the device name ends in a digit, say /dev/foo1, then the partition is called /dev/foo1p3. */ w = strlen(disk_device); if (w && isdigit(disk_device[w-1])) w++; if (w < 5) w = 5; // 1 12345678901 12345678901 12345678901 12 printf("%*s Boot Start End Blocks Id System\n", w+1, "Device"); for (i = 0; i < g_partitions; i++) { const struct pte *pe = &ptes[i]; sector_t psects; sector_t pblocks; unsigned podd; p = pe->part_table; if (!p || is_cleared_partition(p)) continue; psects = get_nr_sects(p); pblocks = psects; podd = 0; if (sector_size < 1024) { pblocks /= (1024 / sector_size); podd = psects % (1024 / sector_size); } if (sector_size > 1024) pblocks *= (sector_size / 1024); printf("%s %c %11"SECT_FMT"u %11"SECT_FMT"u %11"SECT_FMT"u%c %2x %s\n", partname(disk_device, i+1, w+2), !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG /* boot flag */ ? '*' : '?', cround(get_partition_start_from_dev_start(pe)), /* start */ cround(get_partition_start_from_dev_start(pe) + psects /* end */ - (psects ? 1 : 0)), pblocks, podd ? '+' : ' ', /* odd flag on end */ p->sys_ind, /* type id */ partition_type(p->sys_ind)); /* type name */ check_consistency(p, i); } /* Is partition table in disk order? It need not be, but... */ /* partition table entries are not checked for correct order * if this is a sgi, sun or aix labeled disk... */ if (LABEL_IS_DOS && wrong_p_order(NULL)) { /* FIXME */ printf("\nPartition table entries are not in disk order\n"); } } #if ENABLE_FEATURE_FDISK_ADVANCED static void x_list_table(int extend) { const struct pte *pe; const struct partition *p; int i; printf("\nDisk %s: %u heads, %u sectors, %u cylinders\n\n", disk_device, g_heads, g_sectors, g_cylinders); printf("Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID\n"); for (i = 0; i < g_partitions; i++) { pe = &ptes[i]; p = (extend ? pe->ext_pointer : pe->part_table); if (p != NULL) { printf("%2u %02x%4u%4u%5u%4u%4u%5u%11"SECT_FMT"u%11"SECT_FMT"u %02x\n", i + 1, p->boot_ind, p->head, sector(p->sector), cylinder(p->sector, p->cyl), p->end_head, sector(p->end_sector), cylinder(p->end_sector, p->end_cyl), get_start_sect(p), get_nr_sects(p), p->sys_ind); if (p->sys_ind) check_consistency(p, i); } } } #endif #if ENABLE_FEATURE_FDISK_WRITABLE static void fill_bounds(sector_t *first, sector_t *last) { unsigned i; const struct pte *pe = &ptes[0]; const struct partition *p; for (i = 0; i < g_partitions; pe++,i++) { p = pe->part_table; if (!p->sys_ind || IS_EXTENDED(p->sys_ind)) { first[i] = 0xffffffff; last[i] = 0; } else { first[i] = get_partition_start_from_dev_start(pe); last[i] = first[i] + get_nr_sects(p) - 1; } } } static void check(int n, unsigned h, unsigned s, unsigned c, sector_t start) { sector_t total, real_s, real_c; real_s = sector(s) - 1; real_c = cylinder(s, c); total = (real_c * g_sectors + real_s) * g_heads + h; if (!total) printf("Partition %u contains sector 0\n", n); if (h >= g_heads) printf("Partition %u: head %u greater than maximum %u\n", n, h + 1, g_heads); if (real_s >= g_sectors) printf("Partition %u: sector %u greater than " "maximum %u\n", n, s, g_sectors); if (real_c >= g_cylinders) printf("Partition %u: cylinder %"SECT_FMT"u greater than " "maximum %u\n", n, real_c + 1, g_cylinders); if (g_cylinders <= 1024 && start != total) printf("Partition %u: previous sectors %"SECT_FMT"u disagrees with " "total %"SECT_FMT"u\n", n, start, total); } static void verify(void) { int i, j; sector_t total = 1; sector_t first[g_partitions], last[g_partitions]; struct partition *p; if (warn_geometry()) return; if (LABEL_IS_SUN) { verify_sun(); return; } if (LABEL_IS_SGI) { verify_sgi(1); return; } fill_bounds(first, last); for (i = 0; i < g_partitions; i++) { struct pte *pe = &ptes[i]; p = pe->part_table; if (p->sys_ind && !IS_EXTENDED(p->sys_ind)) { check_consistency(p, i); if (get_partition_start_from_dev_start(pe) < first[i]) printf("Warning: bad start-of-data in " "partition %u\n", i + 1); check(i + 1, p->end_head, p->end_sector, p->end_cyl, last[i]); total += last[i] + 1 - first[i]; for (j = 0; j < i; j++) { if ((first[i] >= first[j] && first[i] <= last[j]) || ((last[i] <= last[j] && last[i] >= first[j]))) { printf("Warning: partition %u overlaps " "partition %u\n", j + 1, i + 1); total += first[i] >= first[j] ? first[i] : first[j]; total -= last[i] <= last[j] ? last[i] : last[j]; } } } } if (extended_offset) { struct pte *pex = &ptes[ext_index]; sector_t e_last = get_start_sect(pex->part_table) + get_nr_sects(pex->part_table) - 1; for (i = 4; i < g_partitions; i++) { total++; p = ptes[i].part_table; if (!p->sys_ind) { if (i != 4 || i + 1 < g_partitions) printf("Warning: partition %u " "is empty\n", i + 1); } else if (first[i] < extended_offset || last[i] > e_last) { printf("Logical partition %u not entirely in " "partition %u\n", i + 1, ext_index + 1); } } } if (total > g_heads * g_sectors * g_cylinders) printf("Total allocated sectors %u greater than the maximum " "%u\n", total, g_heads * g_sectors * g_cylinders); else { total = g_heads * g_sectors * g_cylinders - total; if (total != 0) printf("%"SECT_FMT"u unallocated sectors\n", total); } } static void add_partition(int n, int sys) { char mesg[256]; /* 48 does not suffice in Japanese */ int i, num_read = 0; struct partition *p = ptes[n].part_table; struct partition *q = ptes[ext_index].part_table; sector_t limit, temp; sector_t start, stop = 0; sector_t first[g_partitions], last[g_partitions]; if (p && p->sys_ind) { printf(msg_part_already_defined, n + 1); return; } fill_bounds(first, last); if (n < 4) { start = sector_offset; if (display_in_cyl_units || !total_number_of_sectors) limit = (sector_t) g_heads * g_sectors * g_cylinders - 1; else limit = total_number_of_sectors - 1; if (extended_offset) { first[ext_index] = extended_offset; last[ext_index] = get_start_sect(q) + get_nr_sects(q) - 1; } } else { start = extended_offset + sector_offset; limit = get_start_sect(q) + get_nr_sects(q) - 1; } if (display_in_cyl_units) for (i = 0; i < g_partitions; i++) first[i] = (cround(first[i]) - 1) * units_per_sector; snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR)); do { temp = start; for (i = 0; i < g_partitions; i++) { int lastplusoff; if (start == ptes[i].offset_from_dev_start) start += sector_offset; lastplusoff = last[i] + ((n < 4) ? 0 : sector_offset); if (start >= first[i] && start <= lastplusoff) start = lastplusoff + 1; } if (start > limit) break; if (start >= temp+units_per_sector && num_read) { printf("Sector %"SECT_FMT"u is already allocated\n", temp); temp = start; num_read = 0; } if (!num_read && start == temp) { sector_t saved_start; saved_start = start; start = read_int(cround(saved_start), cround(saved_start), cround(limit), 0, mesg); if (display_in_cyl_units) { start = (start - 1) * units_per_sector; if (start < saved_start) start = saved_start; } num_read = 1; } } while (start != temp || !num_read); if (n > 4) { /* NOT for fifth partition */ struct pte *pe = &ptes[n]; pe->offset_from_dev_start = start - sector_offset; if (pe->offset_from_dev_start == extended_offset) { /* must be corrected */ pe->offset_from_dev_start++; if (sector_offset == 1) start++; } } for (i = 0; i < g_partitions; i++) { struct pte *pe = &ptes[i]; if (start < pe->offset_from_dev_start && limit >= pe->offset_from_dev_start) limit = pe->offset_from_dev_start - 1; if (start < first[i] && limit >= first[i]) limit = first[i] - 1; } if (start > limit) { printf("No free sectors available\n"); if (n > 4) g_partitions--; return; } if (cround(start) == cround(limit)) { stop = limit; } else { snprintf(mesg, sizeof(mesg), "Last %s or +size or +sizeM or +sizeK", str_units(SINGULAR)); stop = read_int(cround(start), cround(limit), cround(limit), cround(start), mesg); if (display_in_cyl_units) { stop = stop * units_per_sector - 1; if (stop >limit) stop = limit; } } set_partition(n, 0, start, stop, sys); if (n > 4) set_partition(n - 1, 1, ptes[n].offset_from_dev_start, stop, EXTENDED); if (IS_EXTENDED(sys)) { struct pte *pe4 = &ptes[4]; struct pte *pen = &ptes[n]; ext_index = n; pen->ext_pointer = p; pe4->offset_from_dev_start = extended_offset = start; pe4->sectorbuffer = xzalloc(sector_size); pe4->part_table = pt_offset(pe4->sectorbuffer, 0); pe4->ext_pointer = pe4->part_table + 1; pe4->changed = 1; g_partitions = 5; } } static void add_logical(void) { if (g_partitions > 5 || ptes[4].part_table->sys_ind) { struct pte *pe = &ptes[g_partitions]; pe->sectorbuffer = xzalloc(sector_size); pe->part_table = pt_offset(pe->sectorbuffer, 0); pe->ext_pointer = pe->part_table + 1; pe->offset_from_dev_start = 0; pe->changed = 1; g_partitions++; } add_partition(g_partitions - 1, LINUX_NATIVE); } static void new_partition(void) { int i, free_primary = 0; if (warn_geometry()) return; if (LABEL_IS_SUN) { add_sun_partition(get_partition(0, g_partitions), LINUX_NATIVE); return; } if (LABEL_IS_SGI) { sgi_add_partition(get_partition(0, g_partitions), LINUX_NATIVE); return; } if (LABEL_IS_AIX) { printf("Sorry - this fdisk cannot handle AIX disk labels.\n" "If you want to add DOS-type partitions, create a new empty DOS partition\n" "table first (use 'o'). This will destroy the present disk contents.\n"); return; } for (i = 0; i < 4; i++) free_primary += !ptes[i].part_table->sys_ind; if (!free_primary && g_partitions >= MAXIMUM_PARTS) { printf("The maximum number of partitions has been created\n"); return; } if (!free_primary) { if (extended_offset) add_logical(); else printf("You must delete some partition and add " "an extended partition first\n"); } else { char c, line[80]; snprintf(line, sizeof(line), "Command action\n" " %s\n" " p primary partition (1-4)\n", (extended_offset ? "l logical (5 or over)" : "e extended")); while (1) { c = read_nonempty(line); if ((c | 0x20) == 'p') { i = get_nonexisting_partition(0, 4); if (i >= 0) add_partition(i, LINUX_NATIVE); return; } if (c == 'l' && extended_offset) { add_logical(); return; } if (c == 'e' && !extended_offset) { i = get_nonexisting_partition(0, 4); if (i >= 0) add_partition(i, EXTENDED); return; } printf("Invalid partition number " "for type '%c'\n", c); } } } static void reread_partition_table(int leave) { int i; printf("Calling ioctl() to re-read partition table\n"); sync(); /* Users with slow external USB disks on a 320MHz ARM system (year 2011) * report that sleep is needed, otherwise BLKRRPART may fail with -EIO: */ sleep(1); i = ioctl_or_perror(dev_fd, BLKRRPART, NULL, "WARNING: rereading partition table " "failed, kernel still uses old table"); #if 0 if (dos_changed) printf( "\nWARNING: If you have created or modified any DOS 6.x\n" "partitions, please see the fdisk manual page for additional\n" "information\n"); #endif if (leave) { if (ENABLE_FEATURE_CLEAN_UP) close_dev_fd(); exit(i != 0); } } static void write_table(void) { int i; if (LABEL_IS_DOS) { for (i = 0; i < 3; i++) if (ptes[i].changed) ptes[3].changed = 1; for (i = 3; i < g_partitions; i++) { struct pte *pe = &ptes[i]; if (pe->changed) { write_part_table_flag(pe->sectorbuffer); write_sector(pe->offset_from_dev_start, pe->sectorbuffer); } } } else if (LABEL_IS_SGI) { /* no test on change? the printf below might be mistaken */ sgi_write_table(); } else if (LABEL_IS_SUN) { for (i = 0; i < 8; i++) { if (ptes[i].changed) { sun_write_table(); break; } } } printf("The partition table has been altered.\n"); reread_partition_table(1); } #endif /* FEATURE_FDISK_WRITABLE */ #if ENABLE_FEATURE_FDISK_ADVANCED #define MAX_PER_LINE 16 static void print_buffer(char *pbuffer) { int i,l; for (i = 0, l = 0; i < sector_size; i++, l++) { if (l == 0) printf("0x%03X:", i); printf(" %02X", (unsigned char) pbuffer[i]); if (l == MAX_PER_LINE - 1) { bb_putchar('\n'); l = -1; } } if (l > 0) bb_putchar('\n'); bb_putchar('\n'); } static void print_raw(void) { int i; printf("Device: %s\n", disk_device); if (LABEL_IS_SGI || LABEL_IS_SUN) print_buffer(MBRbuffer); else { for (i = 3; i < g_partitions; i++) print_buffer(ptes[i].sectorbuffer); } } static void move_begin(unsigned i) { struct pte *pe = &ptes[i]; struct partition *p = pe->part_table; sector_t new, first, nr_sects; if (warn_geometry()) return; nr_sects = get_nr_sects(p); if (!p->sys_ind || !nr_sects || IS_EXTENDED(p->sys_ind)) { printf("Partition %u has no data area\n", i + 1); return; } first = get_partition_start_from_dev_start(pe); /* == pe->offset_from_dev_start + get_start_sect(p) */ new = read_int(0 /*was:first*/, first, first + nr_sects - 1, first, "New beginning of data"); if (new != first) { sector_t new_relative = new - pe->offset_from_dev_start; nr_sects += (get_start_sect(p) - new_relative); set_start_sect(p, new_relative); set_nr_sects(p, nr_sects); read_nonempty("Recalculate C/H/S values? (Y/N): "); if ((line_ptr[0] | 0x20) == 'y') set_hsc_start_end(p, new, new + nr_sects - 1); pe->changed = 1; } } static void xselect(void) { char c; while (1) { bb_putchar('\n'); c = 0x20 | read_nonempty("Expert command (m for help): "); switch (c) { case 'a': if (LABEL_IS_SUN) sun_set_alt_cyl(); break; case 'b': if (LABEL_IS_DOS) move_begin(get_partition(0, g_partitions)); break; case 'c': user_cylinders = g_cylinders = read_int(1, g_cylinders, 1048576, 0, "Number of cylinders"); if (LABEL_IS_SUN) sun_set_ncyl(g_cylinders); if (LABEL_IS_DOS) warn_cylinders(); break; case 'd': print_raw(); break; case 'e': if (LABEL_IS_SGI) sgi_set_xcyl(); else if (LABEL_IS_SUN) sun_set_xcyl(); else if (LABEL_IS_DOS) x_list_table(1); break; case 'f': if (LABEL_IS_DOS) fix_partition_table_order(); break; case 'g': #if ENABLE_FEATURE_SGI_LABEL create_sgilabel(); #endif break; case 'h': user_heads = g_heads = read_int(1, g_heads, 256, 0, "Number of heads"); update_units(); break; case 'i': if (LABEL_IS_SUN) sun_set_ilfact(); break; case 'o': if (LABEL_IS_SUN) sun_set_rspeed(); break; case 'p': if (LABEL_IS_SUN) list_table(1); else x_list_table(0); break; case 'q': if (ENABLE_FEATURE_CLEAN_UP) close_dev_fd(); bb_putchar('\n'); exit(EXIT_SUCCESS); case 'r': return; case 's': user_sectors = g_sectors = read_int(1, g_sectors, 63, 0, "Number of sectors"); if (dos_compatible_flag) { sector_offset = g_sectors; printf("Warning: setting sector offset for DOS " "compatiblity\n"); } update_units(); break; case 'v': verify(); break; case 'w': write_table(); /* does not return */ break; case 'y': if (LABEL_IS_SUN) sun_set_pcylcount(); break; default: xmenu(); } } } #endif /* ADVANCED mode */ static int is_ide_cdrom_or_tape(const char *device) { FILE *procf; char buf[100]; struct stat statbuf; int is_ide = 0; /* No device was given explicitly, and we are trying some likely things. But opening /dev/hdc may produce errors like "hdc: tray open or drive not ready" if it happens to be a CD-ROM drive. It even happens that the process hangs on the attempt to read a music CD. So try to be careful. This only works since 2.1.73. */ if (strncmp("/dev/hd", device, 7)) return 0; snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5); procf = fopen_for_read(buf); if (procf != NULL && fgets(buf, sizeof(buf), procf)) is_ide = (!strncmp(buf, "cdrom", 5) || !strncmp(buf, "tape", 4)); else /* Now when this proc file does not exist, skip the device when it is read-only. */ if (stat(device, &statbuf) == 0) is_ide = ((statbuf.st_mode & 0222) == 0); if (procf) fclose(procf); return is_ide; } static void open_list_and_close(const char *device, int user_specified) { int gb; disk_device = device; if (setjmp(listingbuf)) return; if (!user_specified) if (is_ide_cdrom_or_tape(device)) return; /* Open disk_device, save file descriptor to dev_fd */ errno = 0; gb = get_boot(TRY_ONLY); if (gb > 0) { /* I/O error */ /* Ignore other errors, since we try IDE and SCSI hard disks which may not be installed on the system. */ if (user_specified || errno == EACCES) bb_perror_msg("can't open '%s'", device); return; } if (gb < 0) { /* no DOS signature */ list_disk_geometry(); if (LABEL_IS_AIX) goto ret; #if ENABLE_FEATURE_OSF_LABEL if (bsd_trydev(device) < 0) #endif printf("Disk %s doesn't contain a valid " "partition table\n", device); } else { list_table(0); #if ENABLE_FEATURE_FDISK_WRITABLE if (!LABEL_IS_SUN && g_partitions > 4) { delete_partition(ext_index); } #endif } ret: close_dev_fd(); } /* Is it a whole disk? The digit check is still useful for Xen devices for example. */ static int is_whole_disk(const char *disk) { unsigned len; int fd = open(disk, O_RDONLY); if (fd != -1) { struct hd_geometry geometry; int err = ioctl(fd, HDIO_GETGEO, &geometry); close(fd); if (!err) return (geometry.start == 0); } /* Treat "nameN" as a partition name, not whole disk */ /* note: mmcblk0 should work from the geometry check above */ len = strlen(disk); if (len != 0 && isdigit(disk[len - 1])) return 0; return 1; } /* for fdisk -l: try all things in /proc/partitions that look like a partition name (do not end in a digit) */ static void list_devs_in_proc_partititons(void) { FILE *procpt; char line[100], ptname[100], devname[120]; int ma, mi, sz; procpt = fopen_or_warn("/proc/partitions", "r"); while (fgets(line, sizeof(line), procpt)) { if (sscanf(line, " %u %u %u %[^\n ]", &ma, &mi, &sz, ptname) != 4) continue; sprintf(devname, "/dev/%s", ptname); if (is_whole_disk(devname)) open_list_and_close(devname, 0); } #if ENABLE_FEATURE_CLEAN_UP fclose(procpt); #endif } #if ENABLE_FEATURE_FDISK_WRITABLE static void unknown_command(int c) { printf("%c: unknown command\n", c); } #endif int fdisk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fdisk_main(int argc UNUSED_PARAM, char **argv) { unsigned opt; /* * fdisk -v * fdisk -l [-b sectorsize] [-u] device ... * fdisk -s [partition] ... * fdisk [-b sectorsize] [-u] device * * Options -C, -H, -S set the geometry. */ INIT_G(); close_dev_fd(); /* needed: fd 3 must not stay closed */ opt_complementary = "b+:C+:H+:S+"; /* numeric params */ opt = getopt32(argv, "b:C:H:lS:u" IF_FEATURE_FDISK_BLKSIZE("s"), §or_size, &user_cylinders, &user_heads, &user_sectors); argv += optind; if (opt & OPT_b) { /* Ugly: this sector size is really per device, * so cannot be combined with multiple disks, * and the same goes for the C/H/S options. */ if (sector_size < 512 || sector_size > 0x10000 || (sector_size & (sector_size-1)) /* not power of 2 */ ) { bb_show_usage(); } sector_offset = 2; user_set_sector_size = 1; } if (user_heads <= 0 || user_heads >= 256) user_heads = 0; if (user_sectors <= 0 || user_sectors >= 64) user_sectors = 0; if (opt & OPT_u) display_in_cyl_units = 0; // -u #if ENABLE_FEATURE_FDISK_WRITABLE if (opt & OPT_l) { nowarn = 1; #endif if (*argv) { listing = 1; do { open_list_and_close(*argv, 1); } while (*++argv); } else { /* we don't have device names, */ /* use /proc/partitions instead */ list_devs_in_proc_partititons(); } return 0; #if ENABLE_FEATURE_FDISK_WRITABLE } #endif #if ENABLE_FEATURE_FDISK_BLKSIZE if (opt & OPT_s) { int j; nowarn = 1; if (!argv[0]) bb_show_usage(); for (j = 0; argv[j]; j++) { unsigned long long size; fd = xopen(argv[j], O_RDONLY); size = bb_BLKGETSIZE_sectors(fd) / 2; close(fd); if (argv[1]) printf("%llu\n", size); else printf("%s: %llu\n", argv[j], size); } return 0; } #endif #if ENABLE_FEATURE_FDISK_WRITABLE if (!argv[0] || argv[1]) bb_show_usage(); disk_device = argv[0]; get_boot(OPEN_MAIN); if (LABEL_IS_OSF) { /* OSF label, and no DOS label */ printf("Detected an OSF/1 disklabel on %s, entering " "disklabel mode\n", disk_device); bsd_select(); /*Why do we do this? It seems to be counter-intuitive*/ current_label_type = LABEL_DOS; /* If we return we may want to make an empty DOS label? */ } while (1) { int c; bb_putchar('\n'); c = 0x20 | read_nonempty("Command (m for help): "); switch (c) { case 'a': if (LABEL_IS_DOS) toggle_active(get_partition(1, g_partitions)); else if (LABEL_IS_SUN) toggle_sunflags(get_partition(1, g_partitions), 0x01); else if (LABEL_IS_SGI) sgi_set_bootpartition( get_partition(1, g_partitions)); else unknown_command(c); break; case 'b': if (LABEL_IS_SGI) { printf("\nThe current boot file is: %s\n", sgi_get_bootfile()); if (read_maybe_empty("Please enter the name of the " "new boot file: ") == '\n') printf("Boot file unchanged\n"); else sgi_set_bootfile(line_ptr); } #if ENABLE_FEATURE_OSF_LABEL else bsd_select(); #endif break; case 'c': if (LABEL_IS_DOS) toggle_dos_compatibility_flag(); else if (LABEL_IS_SUN) toggle_sunflags(get_partition(1, g_partitions), 0x10); else if (LABEL_IS_SGI) sgi_set_swappartition( get_partition(1, g_partitions)); else unknown_command(c); break; case 'd': { int j; /* If sgi_label then don't use get_existing_partition, let the user select a partition, since get_existing_partition() only works for Linux-like partition tables */ if (!LABEL_IS_SGI) { j = get_existing_partition(1, g_partitions); } else { j = get_partition(1, g_partitions); } if (j >= 0) delete_partition(j); } break; case 'i': if (LABEL_IS_SGI) create_sgiinfo(); else unknown_command(c); case 'l': list_types(get_sys_types()); break; case 'm': menu(); break; case 'n': new_partition(); break; case 'o': create_doslabel(); break; case 'p': list_table(0); break; case 'q': if (ENABLE_FEATURE_CLEAN_UP) close_dev_fd(); bb_putchar('\n'); return 0; case 's': #if ENABLE_FEATURE_SUN_LABEL create_sunlabel(); #endif break; case 't': change_sysid(); break; case 'u': change_units(); break; case 'v': verify(); break; case 'w': write_table(); /* does not return */ break; #if ENABLE_FEATURE_FDISK_ADVANCED case 'x': if (LABEL_IS_SGI) { printf("\n\tSorry, no experts menu for SGI " "partition tables available\n\n"); } else xselect(); break; #endif default: unknown_command(c); menu(); } } return 0; #endif /* FEATURE_FDISK_WRITABLE */ } ����������������������������������������������busybox-1.22.1/util-linux/rev.c���������������������������������������������������������������������0000644�0000000�0000000�00000004426�12263563520�015052� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * rev implementation for busybox * * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //applet:IF_REV(APPLET(rev, BB_DIR_BIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_REV) += rev.o //config:config REV //config: bool "rev" //config: default y //config: help //config: Reverse lines of a file or files. //usage:#define rev_trivial_usage //usage: "[FILE]..." //usage:#define rev_full_usage "\n\n" //usage: "Reverse lines of FILE" #include "libbb.h" #include "unicode.h" #undef CHAR_T #if ENABLE_UNICODE_SUPPORT # define CHAR_T wchar_t #else # define CHAR_T char #endif /* In-place invert */ static void strrev(CHAR_T *s, int len) { int i; if (len != 0) { len--; if (len != 0 && s[len] == '\n') len--; } for (i = 0; i < len; i++, len--) { CHAR_T c = s[i]; s[i] = s[len]; s[len] = c; } } int rev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int rev_main(int argc UNUSED_PARAM, char **argv) { int retval; size_t bufsize; char *buf; init_unicode(); getopt32(argv, ""); argv += optind; if (!argv[0]) argv = (char **)&bb_argv_dash; retval = EXIT_SUCCESS; bufsize = 256; buf = xmalloc(bufsize); do { size_t pos; FILE *fp; fp = fopen_or_warn_stdin(*argv++); if (!fp) { retval = EXIT_FAILURE; continue; } pos = 0; while (1) { /* Read one line */ buf[bufsize - 1] = 1; /* not 0 */ if (!fgets(buf + pos, bufsize - pos, fp)) break; /* EOF/error */ if (buf[bufsize - 1] == '\0' /* fgets filled entire buffer */ && buf[bufsize - 2] != '\n' /* and did not read '\n' */ && !feof(fp) ) { /* Line is too long, extend buffer */ pos = bufsize - 1; bufsize += 64 + bufsize / 8; buf = xrealloc(buf, bufsize); continue; } /* Process and print it */ #if ENABLE_UNICODE_SUPPORT { wchar_t *tmp = xmalloc(bufsize * sizeof(wchar_t)); /* Convert to wchar_t (might error out!) */ int len = mbstowcs(tmp, buf, bufsize); if (len >= 0) { strrev(tmp, len); /* Convert back to char */ wcstombs(buf, tmp, bufsize); } free(tmp); } #else strrev(buf, strlen(buf)); #endif fputs(buf, stdout); } fclose(fp); } while (*argv); if (ENABLE_FEATURE_CLEAN_UP) free(buf); fflush_stdout_and_exit(retval); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/acpid.c�������������������������������������������������������������������0000644�0000000�0000000�00000021605�12263563520�015334� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * simple ACPI events listener * * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define acpid_trivial_usage //usage: "[-df] [-c CONFDIR] [-l LOGFILE] [-a ACTIONFILE] [-M MAPFILE] [-e PROC_EVENT_FILE] [-p PIDFILE]" //usage:#define acpid_full_usage "\n\n" //usage: "Listen to ACPI events and spawn specific helpers on event arrival\n" //usage: "\n -d Log to stderr, not log file (implies -f)" //usage: "\n -f Run in foreground" //usage: "\n -c DIR Config directory [/etc/acpi]" //usage: "\n -e FILE /proc event file [/proc/acpi/event]" //usage: "\n -l FILE Log file [/var/log/acpid.log]" //usage: "\n -p FILE Pid file [/var/run/acpid.pid]" //usage: "\n -a FILE Action file [/etc/acpid.conf]" //usage: "\n -M FILE Map file [/etc/acpi.map]" //usage: IF_FEATURE_ACPID_COMPAT( //usage: "\n\nAccept and ignore compatibility options -g -m -s -S -v" //usage: ) //usage: //usage:#define acpid_example_usage //usage: "Without -e option, acpid uses all /dev/input/event* files\n" //usage: "# acpid\n" //usage: "# acpid -l /var/log/my-acpi-log\n" //usage: "# acpid -e /proc/acpi/event\n" #include "libbb.h" #include <syslog.h> #include <linux/input.h> #ifndef EV_SW # define EV_SW 0x05 #endif #ifndef EV_KEY # define EV_KEY 0x01 #endif #ifndef SW_LID # define SW_LID 0x00 #endif #ifndef SW_RFKILL_ALL # define SW_RFKILL_ALL 0x03 #endif #ifndef KEY_POWER # define KEY_POWER 116 /* SC System Power Down */ #endif #ifndef KEY_SLEEP # define KEY_SLEEP 142 /* SC System Sleep */ #endif enum { OPT_c = (1 << 0), OPT_d = (1 << 1), OPT_e = (1 << 2), OPT_f = (1 << 3), OPT_l = (1 << 4), OPT_a = (1 << 5), OPT_M = (1 << 6), OPT_p = (1 << 7) * ENABLE_FEATURE_PIDFILE, }; struct acpi_event { const char *s_type; uint16_t n_type; const char *s_code; uint16_t n_code; uint32_t value; const char *desc; }; static const struct acpi_event f_evt_tab[] = { { "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRF 00000080" }, { "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRB 00000080" }, { "EV_SW", 0x05, "SW_LID", 0x00, 1, "button/lid LID0 00000080" }, }; struct acpi_action { const char *key; const char *action; }; static const struct acpi_action f_act_tab[] = { { "PWRF", "PWRF/00000080" }, { "LID0", "LID/00000080" }, }; struct globals { struct acpi_action *act_tab; int n_act; struct acpi_event *evt_tab; int n_evt; } FIX_ALIASING; #define G (*ptr_to_globals) #define act_tab (G.act_tab) #define n_act (G.n_act ) #define evt_tab (G.evt_tab) #define n_evt (G.n_evt ) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ } while (0) /* * acpid listens to ACPI events coming either in textual form * from /proc/acpi/event (though it is marked deprecated, * it is still widely used and _is_ a standard) or in binary form * from specified evdevs (just use /dev/input/event*). * It parses the event to retrieve ACTION and a possible PARAMETER. * It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts * (if the resulting path is a directory) or directly. * If the resulting path does not exist it logs it via perror * and continues listening. */ static void process_event(const char *event) { struct stat st; char *handler = xasprintf("./%s", event); const char *args[] = { "run-parts", handler, NULL }; // debug info if (option_mask32 & OPT_d) { bb_error_msg("%s", event); } // spawn handler // N.B. run-parts would require scripts to have #!/bin/sh // handler is directory? -> use run-parts // handler is file? -> run it directly if (0 == stat(event, &st)) spawn((char **)args + (0==(st.st_mode & S_IFDIR))); else bb_simple_perror_msg(event); free(handler); } static const char *find_action(struct input_event *ev, const char *buf) { const char *action = NULL; int i; // map event for (i = 0; i < n_evt; i++) { if (ev) { if (ev->type == evt_tab[i].n_type && ev->code == evt_tab[i].n_code && ev->value == evt_tab[i].value) { action = evt_tab[i].desc; break; } } if (buf) { if (strncmp(buf, evt_tab[i].desc, strlen(buf)) == 0) { action = evt_tab[i].desc; break; } } } // get action if (action) { for (i = 0; i < n_act; i++) { if (strstr(action, act_tab[i].key)) { action = act_tab[i].action; break; } } } return action; } static void parse_conf_file(const char *filename) { parser_t *parser; char *tokens[2]; parser = config_open2(filename, fopen_for_read); if (parser) { while (config_read(parser, tokens, 2, 2, "# \t", PARSE_NORMAL)) { act_tab = xrealloc_vector(act_tab, 1, n_act); act_tab[n_act].key = xstrdup(tokens[0]); act_tab[n_act].action = xstrdup(tokens[1]); n_act++; } config_close(parser); } else { act_tab = (void*)f_act_tab; n_act = ARRAY_SIZE(f_act_tab); } } static void parse_map_file(const char *filename) { parser_t *parser; char *tokens[6]; parser = config_open2(filename, fopen_for_read); if (parser) { while (config_read(parser, tokens, 6, 6, "# \t", PARSE_NORMAL)) { evt_tab = xrealloc_vector(evt_tab, 1, n_evt); evt_tab[n_evt].s_type = xstrdup(tokens[0]); evt_tab[n_evt].n_type = xstrtou(tokens[1], 16); evt_tab[n_evt].s_code = xstrdup(tokens[2]); evt_tab[n_evt].n_code = xatou16(tokens[3]); evt_tab[n_evt].value = xatoi_positive(tokens[4]); evt_tab[n_evt].desc = xstrdup(tokens[5]); n_evt++; } config_close(parser); } else { evt_tab = (void*)f_evt_tab; n_evt = ARRAY_SIZE(f_evt_tab); } } /* * acpid [-c conf_dir] [-r conf_file ] [-a map_file ] [-l log_file] [-e proc_event_file] */ int acpid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int acpid_main(int argc UNUSED_PARAM, char **argv) { int nfd; int opts; struct pollfd *pfd; const char *opt_dir = "/etc/acpi"; const char *opt_input = "/dev/input/event"; const char *opt_logfile = "/var/log/acpid.log"; const char *opt_action = "/etc/acpid.conf"; const char *opt_map = "/etc/acpi.map"; #if ENABLE_FEATURE_PIDFILE const char *opt_pidfile = CONFIG_PID_FILE_PATH "/acpid.pid"; #endif INIT_G(); opt_complementary = "df:e--e"; opts = getopt32(argv, "c:de:fl:a:M:" IF_FEATURE_PIDFILE("p:") IF_FEATURE_ACPID_COMPAT("g:m:s:S:v"), &opt_dir, &opt_input, &opt_logfile, &opt_action, &opt_map IF_FEATURE_PIDFILE(, &opt_pidfile) IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL) ); if (!(opts & OPT_f)) { /* No -f "Foreground", we go to background */ bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); } if (!(opts & OPT_d)) { /* No -d "Debug", we log to log file. * This includes any output from children. */ xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO); xdup2(STDOUT_FILENO, STDERR_FILENO); /* Also, acpid's messages (but not children) will go to syslog too */ openlog(applet_name, LOG_PID, LOG_DAEMON); logmode = LOGMODE_SYSLOG | LOGMODE_STDIO; } /* else: -d "Debug", log is not redirected */ parse_conf_file(opt_action); parse_map_file(opt_map); xchdir(opt_dir); /* We spawn children but don't wait for them. Prevent zombies: */ bb_signals((1 << SIGCHLD), SIG_IGN); // If you enable this, (1) explain why, (2) // make sure while(poll) loop below is still interruptible // by SIGTERM et al: //bb_signals(BB_FATAL_SIGS, record_signo); pfd = NULL; nfd = 0; while (1) { int fd; char *dev_event; dev_event = xasprintf((opts & OPT_e) ? "%s" : "%s%u", opt_input, nfd); fd = open(dev_event, O_RDONLY | O_NONBLOCK); if (fd < 0) { if (nfd == 0) bb_simple_perror_msg_and_die(dev_event); break; } free(dev_event); pfd = xrealloc_vector(pfd, 1, nfd); pfd[nfd].fd = fd; pfd[nfd].events = POLLIN; nfd++; } write_pidfile(opt_pidfile); while (safe_poll(pfd, nfd, -1) > 0) { int i; for (i = 0; i < nfd; i++) { const char *event; if (!(pfd[i].revents & POLLIN)) { if (pfd[i].revents == 0) continue; /* this fd has nothing */ /* Likely POLLERR, POLLHUP, POLLNVAL. * Do not listen on this fd anymore. */ close(pfd[i].fd); nfd--; for (; i < nfd; i++) pfd[i].fd = pfd[i + 1].fd; break; /* do poll() again */ } event = NULL; if (option_mask32 & OPT_e) { char *buf; int len; buf = xmalloc_reads(pfd[i].fd, NULL); /* buf = "button/power PWRB 00000080 00000000" */ len = strlen(buf) - 9; if (len >= 0) buf[len] = '\0'; event = find_action(NULL, buf); free(buf); } else { struct input_event ev; if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev))) continue; if (ev.value != 1 && ev.value != 0) continue; event = find_action(&ev, NULL); } if (!event) continue; /* spawn event handler */ process_event(event); } } if (ENABLE_FEATURE_CLEAN_UP) { while (nfd--) close(pfd[nfd].fd); free(pfd); } remove_pidfile(opt_pidfile); return EXIT_SUCCESS; } ���������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/losetup.c�����������������������������������������������������������������0000644�0000000�0000000�00000006161�12263563520�015747� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Mini losetup implementation for busybox * * Copyright (C) 2002 Matt Kraai. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define losetup_trivial_usage //usage: "[-r] [-o OFS] {-f|LOOPDEV} FILE - associate loop devices\n" //usage: " losetup -d LOOPDEV - disassociate\n" //usage: " losetup -a - show status\n" //usage: " losetup -f - show next free loop device" //usage:#define losetup_full_usage "\n\n" //usage: " -o OFS Start OFS bytes into FILE" //usage: "\n -r Read-only" //usage: "\n -f Show/use next free loop device" //usage: //usage:#define losetup_notes_usage //usage: "One argument (losetup /dev/loop1) will display the current association\n" //usage: "(if any), or disassociate it (with -d). The display shows the offset\n" //usage: "and filename of the file the loop device is currently bound to.\n\n" //usage: "Two arguments (losetup /dev/loop1 file.img) create a new association,\n" //usage: "with an optional offset (-o 12345). Encryption is not yet supported.\n" //usage: "losetup -f will show the first loop free loop device\n\n" #include "libbb.h" /* 1048575 is a max possible minor number in Linux circa 2010 */ /* for now use something less extreme */ #define MAX_LOOP_NUM 1023 int losetup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int losetup_main(int argc UNUSED_PARAM, char **argv) { unsigned opt; char *opt_o; char dev[LOOP_NAMESIZE]; enum { OPT_d = (1 << 0), OPT_o = (1 << 1), OPT_f = (1 << 2), OPT_a = (1 << 3), OPT_r = (1 << 4), /* must be last */ }; opt_complementary = "?2:d--ofar:a--ofr"; opt = getopt32(argv, "do:far", &opt_o); argv += optind; /* LOOPDEV */ if (!opt && argv[0] && !argv[1]) { char *s; s = query_loop(argv[0]); if (!s) bb_simple_perror_msg_and_die(argv[0]); printf("%s: %s\n", argv[0], s); if (ENABLE_FEATURE_CLEAN_UP) free(s); return EXIT_SUCCESS; } /* -d LOOPDEV */ if (opt == OPT_d && argv[0]) { if (del_loop(argv[0])) bb_simple_perror_msg_and_die(argv[0]); return EXIT_SUCCESS; } /* -a */ if (opt == OPT_a) { int n; for (n = 0; n < MAX_LOOP_NUM; n++) { char *s; sprintf(dev, LOOP_FORMAT, n); s = query_loop(dev); if (s) { printf("%s: %s\n", dev, s); free(s); } } return EXIT_SUCCESS; } /* contains -f */ if (opt & OPT_f) { char *s; int n = 0; do { if (n > MAX_LOOP_NUM) bb_error_msg_and_die("no free loop devices"); sprintf(dev, LOOP_FORMAT, n++); s = query_loop(dev); free(s); } while (s); /* now: dev is next free "/dev/loopN" */ if ((opt == OPT_f) && !argv[0]) { puts(dev); return EXIT_SUCCESS; } } /* [-r] [-o OFS] {-f|LOOPDEV} FILE */ if (argv[0] && ((opt & OPT_f) || argv[1])) { unsigned long long offset = 0; char *d = dev; if (opt & OPT_o) offset = xatoull(opt_o); if (!(opt & OPT_f)) d = *argv++; if (argv[0]) { if (set_loop(&d, argv[0], offset, (opt & OPT_r)) < 0) bb_simple_perror_msg_and_die(argv[0]); return EXIT_SUCCESS; } } bb_show_usage(); /* does not return */ /*return EXIT_FAILURE;*/ } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/fbset.c�������������������������������������������������������������������0000644�0000000�0000000�00000036471�12263563520�015366� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Mini fbset implementation for busybox * * Copyright (C) 1999 by Randolph Chung <tausq@debian.org> * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * This is a from-scratch implementation of fbset; but the de facto fbset * implementation was a good reference. fbset (original) is released under * the GPL, and is (c) 1995-1999 by: * Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be) */ //usage:#define fbset_trivial_usage //usage: "[OPTIONS] [MODE]" //usage:#define fbset_full_usage "\n\n" //usage: "Show and modify frame buffer settings" //usage: //usage:#define fbset_example_usage //usage: "$ fbset\n" //usage: "mode \"1024x768-76\"\n" //usage: " # D: 78.653 MHz, H: 59.949 kHz, V: 75.694 Hz\n" //usage: " geometry 1024 768 1024 768 16\n" //usage: " timings 12714 128 32 16 4 128 4\n" //usage: " accel false\n" //usage: " rgba 5/11,6/5,5/0,0/0\n" //usage: "endmode\n" #include "libbb.h" #define DEFAULTFBDEV FB_0 #define DEFAULTFBMODE "/etc/fb.modes" /* Stuff stolen from the kernel's fb.h */ #define FB_ACTIVATE_ALL 64 enum { FBIOGET_VSCREENINFO = 0x4600, FBIOPUT_VSCREENINFO = 0x4601 }; struct fb_bitfield { uint32_t offset; /* beginning of bitfield */ uint32_t length; /* length of bitfield */ uint32_t msb_right; /* !=0: Most significant bit is right */ }; struct fb_var_screeninfo { uint32_t xres; /* visible resolution */ uint32_t yres; uint32_t xres_virtual; /* virtual resolution */ uint32_t yres_virtual; uint32_t xoffset; /* offset from virtual to visible */ uint32_t yoffset; /* resolution */ uint32_t bits_per_pixel; uint32_t grayscale; /* !=0 Graylevels instead of colors */ struct fb_bitfield red; /* bitfield in fb mem if true color, */ struct fb_bitfield green; /* else only length is significant */ struct fb_bitfield blue; struct fb_bitfield transp; /* transparency */ uint32_t nonstd; /* !=0 Non standard pixel format */ uint32_t activate; /* see FB_ACTIVATE_x */ uint32_t height; /* height of picture in mm */ uint32_t width; /* width of picture in mm */ uint32_t accel_flags; /* acceleration flags (hints) */ /* Timing: All values in pixclocks, except pixclock (of course) */ uint32_t pixclock; /* pixel clock in ps (pico seconds) */ uint32_t left_margin; /* time from sync to picture */ uint32_t right_margin; /* time from picture to sync */ uint32_t upper_margin; /* time from sync to picture */ uint32_t lower_margin; uint32_t hsync_len; /* length of horizontal sync */ uint32_t vsync_len; /* length of vertical sync */ uint32_t sync; /* see FB_SYNC_x */ uint32_t vmode; /* see FB_VMODE_x */ uint32_t reserved[6]; /* Reserved for future compatibility */ }; static void copy_if_gt0(uint32_t *src, uint32_t *dst, unsigned cnt) { do { if ((int32_t) *src > 0) *dst = *src; src++; dst++; } while (--cnt); } static NOINLINE void copy_changed_values( struct fb_var_screeninfo *base, struct fb_var_screeninfo *set) { //if ((int32_t) set->xres > 0) base->xres = set->xres; //if ((int32_t) set->yres > 0) base->yres = set->yres; //if ((int32_t) set->xres_virtual > 0) base->xres_virtual = set->xres_virtual; //if ((int32_t) set->yres_virtual > 0) base->yres_virtual = set->yres_virtual; copy_if_gt0(&set->xres, &base->xres, 4); if ((int32_t) set->bits_per_pixel > 0) base->bits_per_pixel = set->bits_per_pixel; //copy_if_gt0(&set->bits_per_pixel, &base->bits_per_pixel, 1); //if ((int32_t) set->pixclock > 0) base->pixclock = set->pixclock; //if ((int32_t) set->left_margin > 0) base->left_margin = set->left_margin; //if ((int32_t) set->right_margin > 0) base->right_margin = set->right_margin; //if ((int32_t) set->upper_margin > 0) base->upper_margin = set->upper_margin; //if ((int32_t) set->lower_margin > 0) base->lower_margin = set->lower_margin; //if ((int32_t) set->hsync_len > 0) base->hsync_len = set->hsync_len; //if ((int32_t) set->vsync_len > 0) base->vsync_len = set->vsync_len; //if ((int32_t) set->sync > 0) base->sync = set->sync; //if ((int32_t) set->vmode > 0) base->vmode = set->vmode; copy_if_gt0(&set->pixclock, &base->pixclock, 9); } enum { CMD_FB = 1, CMD_DB = 2, CMD_GEOMETRY = 3, CMD_TIMING = 4, CMD_ACCEL = 5, CMD_HSYNC = 6, CMD_VSYNC = 7, CMD_LACED = 8, CMD_DOUBLE = 9, /* CMD_XCOMPAT = 10, */ CMD_ALL = 11, CMD_INFO = 12, CMD_SHOW = 13, CMD_CHANGE = 14, #if ENABLE_FEATURE_FBSET_FANCY CMD_XRES = 100, CMD_YRES = 101, CMD_VXRES = 102, CMD_VYRES = 103, CMD_DEPTH = 104, CMD_MATCH = 105, CMD_PIXCLOCK = 106, CMD_LEFT = 107, CMD_RIGHT = 108, CMD_UPPER = 109, CMD_LOWER = 110, CMD_HSLEN = 111, CMD_VSLEN = 112, CMD_CSYNC = 113, CMD_GSYNC = 114, CMD_EXTSYNC = 115, CMD_BCAST = 116, CMD_RGBA = 117, CMD_STEP = 118, CMD_MOVE = 119, #endif }; static const struct cmdoptions_t { const char name[9]; const unsigned char param_count; const unsigned char code; } g_cmdoptions[] = { /*"12345678" + NUL */ { "fb" , 1, CMD_FB }, { "db" , 1, CMD_DB }, { "a" , 0, CMD_ALL }, { "i" , 0, CMD_INFO }, { "g" , 5, CMD_GEOMETRY }, { "t" , 7, CMD_TIMING }, { "accel" , 1, CMD_ACCEL }, { "hsync" , 1, CMD_HSYNC }, { "vsync" , 1, CMD_VSYNC }, { "laced" , 1, CMD_LACED }, { "double" , 1, CMD_DOUBLE }, { "show" , 0, CMD_SHOW }, { "s" , 0, CMD_SHOW }, #if ENABLE_FEATURE_FBSET_FANCY { "all" , 0, CMD_ALL }, { "xres" , 1, CMD_XRES }, { "yres" , 1, CMD_YRES }, { "vxres" , 1, CMD_VXRES }, { "vyres" , 1, CMD_VYRES }, { "depth" , 1, CMD_DEPTH }, { "match" , 0, CMD_MATCH }, { "geometry", 5, CMD_GEOMETRY }, { "pixclock", 1, CMD_PIXCLOCK }, { "left" , 1, CMD_LEFT }, { "right" , 1, CMD_RIGHT }, { "upper" , 1, CMD_UPPER }, { "lower" , 1, CMD_LOWER }, { "hslen" , 1, CMD_HSLEN }, { "vslen" , 1, CMD_VSLEN }, { "timings" , 7, CMD_TIMING }, { "csync" , 1, CMD_CSYNC }, { "gsync" , 1, CMD_GSYNC }, { "extsync" , 1, CMD_EXTSYNC }, { "bcast" , 1, CMD_BCAST }, { "rgba" , 1, CMD_RGBA }, { "step" , 1, CMD_STEP }, { "move" , 1, CMD_MOVE }, #endif }; /* taken from linux/fb.h */ enum { FB_SYNC_HOR_HIGH_ACT = 1, /* horizontal sync high active */ FB_SYNC_VERT_HIGH_ACT = 2, /* vertical sync high active */ #if ENABLE_FEATURE_FBSET_READMODE FB_VMODE_INTERLACED = 1, /* interlaced */ FB_VMODE_DOUBLE = 2, /* double scan */ FB_SYNC_EXT = 4, /* external sync */ FB_SYNC_COMP_HIGH_ACT = 8, /* composite sync high active */ #endif }; #if ENABLE_FEATURE_FBSET_READMODE static void ss(uint32_t *x, uint32_t flag, char *buf, const char *what) { if (strcmp(buf, what) == 0) *x &= ~flag; else *x |= flag; } /* Mode db file contains mode definitions like this: * mode "800x600-48-lace" * # D: 36.00 MHz, H: 33.835 kHz, V: 96.39 Hz * geometry 800 600 800 600 8 * timings 27778 56 80 79 11 128 12 * laced true * hsync high * vsync high * endmode */ static int read_mode_db(struct fb_var_screeninfo *base, const char *fn, const char *mode) { char *token[2], *p, *s; parser_t *parser = config_open(fn); while (config_read(parser, token, 2, 1, "# \t\r", PARSE_NORMAL)) { if (strcmp(token[0], "mode") != 0 || !token[1]) continue; p = strstr(token[1], mode); if (!p) continue; s = p + strlen(mode); //bb_info_msg("CHECK[%s][%s][%d]", mode, p-1, *s); /* exact match? */ if (((!*s || isspace(*s)) && '"' != s[-1]) /* end-of-token */ || ('"' == *s && '"' == p[-1]) /* ends with " but starts with " too! */ ) { //bb_info_msg("FOUND[%s][%s][%s][%d]", token[1], p, mode, isspace(*s)); break; } } if (!token[0]) return 0; while (config_read(parser, token, 2, 1, "# \t", PARSE_NORMAL)) { int i; //bb_info_msg("???[%s][%s]", token[0], token[1]); if (strcmp(token[0], "endmode") == 0) { //bb_info_msg("OK[%s]", mode); return 1; } p = token[1]; i = index_in_strings( "geometry\0timings\0interlaced\0double\0vsync\0hsync\0csync\0extsync\0rgba\0", token[0]); switch (i) { case 0: if (sizeof(int) == sizeof(base->xres)) { sscanf(p, "%d %d %d %d %d", &base->xres, &base->yres, &base->xres_virtual, &base->yres_virtual, &base->bits_per_pixel); } else { int base_xres, base_yres; int base_xres_virtual, base_yres_virtual; int base_bits_per_pixel; sscanf(p, "%d %d %d %d %d", &base_xres, &base_yres, &base_xres_virtual, &base_yres_virtual, &base_bits_per_pixel); base->xres = base_xres; base->yres = base_yres; base->xres_virtual = base_xres_virtual; base->yres_virtual = base_yres_virtual; base->bits_per_pixel = base_bits_per_pixel; } //bb_info_msg("GEO[%s]", p); break; case 1: if (sizeof(int) == sizeof(base->xres)) { sscanf(p, "%d %d %d %d %d %d %d", &base->pixclock, &base->left_margin, &base->right_margin, &base->upper_margin, &base->lower_margin, &base->hsync_len, &base->vsync_len); } else { int base_pixclock; int base_left_margin, base_right_margin; int base_upper_margin, base_lower_margin; int base_hsync_len, base_vsync_len; sscanf(p, "%d %d %d %d %d %d %d", &base_pixclock, &base_left_margin, &base_right_margin, &base_upper_margin, &base_lower_margin, &base_hsync_len, &base_vsync_len); base->pixclock = base_pixclock; base->left_margin = base_left_margin; base->right_margin = base_right_margin; base->upper_margin = base_upper_margin; base->lower_margin = base_lower_margin; base->hsync_len = base_hsync_len; base->vsync_len = base_vsync_len; } //bb_info_msg("TIM[%s]", p); break; case 2: case 3: { static const uint32_t syncs[] = {FB_VMODE_INTERLACED, FB_VMODE_DOUBLE}; ss(&base->vmode, syncs[i-2], p, "false"); //bb_info_msg("VMODE[%s]", p); break; } case 4: case 5: case 6: { static const uint32_t syncs[] = {FB_SYNC_VERT_HIGH_ACT, FB_SYNC_HOR_HIGH_ACT, FB_SYNC_COMP_HIGH_ACT}; ss(&base->sync, syncs[i-4], p, "low"); //bb_info_msg("SYNC[%s]", p); break; } case 7: ss(&base->sync, FB_SYNC_EXT, p, "false"); //bb_info_msg("EXTSYNC[%s]", p); break; case 8: { int red_offset, red_length; int green_offset, green_length; int blue_offset, blue_length; int transp_offset, transp_length; sscanf(p, "%d/%d,%d/%d,%d/%d,%d/%d", &red_offset, &red_length, &green_offset, &green_length, &blue_offset, &blue_length, &transp_offset, &transp_length); base->red.offset = red_offset; base->red.length = red_length; base->red.msb_right = 0; base->green.offset = green_offset; base->green.length = green_length; base->green.msb_right = 0; base->blue.offset = blue_offset; base->blue.length = blue_length; base->blue.msb_right = 0; base->transp.offset = transp_offset; base->transp.length = transp_length; base->transp.msb_right = 0; } } } return 0; } #endif static NOINLINE void showmode(struct fb_var_screeninfo *v) { double drate = 0, hrate = 0, vrate = 0; if (v->pixclock) { drate = 1e12 / v->pixclock; hrate = drate / (v->left_margin + v->xres + v->right_margin + v->hsync_len); vrate = hrate / (v->upper_margin + v->yres + v->lower_margin + v->vsync_len); } printf("\nmode \"%ux%u-%u\"\n" #if ENABLE_FEATURE_FBSET_FANCY "\t# D: %.3f MHz, H: %.3f kHz, V: %.3f Hz\n" #endif "\tgeometry %u %u %u %u %u\n" "\ttimings %u %u %u %u %u %u %u\n" "\taccel %s\n" "\trgba %u/%u,%u/%u,%u/%u,%u/%u\n" "endmode\n\n", v->xres, v->yres, (int) (vrate + 0.5), #if ENABLE_FEATURE_FBSET_FANCY drate / 1e6, hrate / 1e3, vrate, #endif v->xres, v->yres, v->xres_virtual, v->yres_virtual, v->bits_per_pixel, v->pixclock, v->left_margin, v->right_margin, v->upper_margin, v->lower_margin, v->hsync_len, v->vsync_len, (v->accel_flags > 0 ? "true" : "false"), v->red.length, v->red.offset, v->green.length, v->green.offset, v->blue.length, v->blue.offset, v->transp.length, v->transp.offset); } int fbset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fbset_main(int argc, char **argv) { enum { OPT_CHANGE = (1 << 0), OPT_SHOW = (1 << 1), OPT_READMODE = (1 << 2), OPT_ALL = (1 << 3), }; struct fb_var_screeninfo var_old, var_set; int fh, i; unsigned options = 0; const char *fbdev = DEFAULTFBDEV; const char *modefile = DEFAULTFBMODE; char *thisarg; char *mode = mode; /* for compiler */ memset(&var_set, 0xff, sizeof(var_set)); /* set all to -1 */ /* parse cmd args.... why do they have to make things so difficult? */ argv++; argc--; for (; argc > 0 && (thisarg = *argv) != NULL; argc--, argv++) { if (thisarg[0] != '-') { if (!ENABLE_FEATURE_FBSET_READMODE || argc != 1) bb_show_usage(); mode = thisarg; options |= OPT_READMODE; goto contin; } for (i = 0; i < ARRAY_SIZE(g_cmdoptions); i++) { if (strcmp(thisarg + 1, g_cmdoptions[i].name) != 0) continue; if (argc <= g_cmdoptions[i].param_count) bb_show_usage(); switch (g_cmdoptions[i].code) { case CMD_FB: fbdev = argv[1]; break; case CMD_DB: modefile = argv[1]; break; case CMD_ALL: options |= OPT_ALL; break; case CMD_SHOW: options |= OPT_SHOW; break; case CMD_GEOMETRY: var_set.xres = xatou32(argv[1]); var_set.yres = xatou32(argv[2]); var_set.xres_virtual = xatou32(argv[3]); var_set.yres_virtual = xatou32(argv[4]); var_set.bits_per_pixel = xatou32(argv[5]); break; case CMD_TIMING: var_set.pixclock = xatou32(argv[1]); var_set.left_margin = xatou32(argv[2]); var_set.right_margin = xatou32(argv[3]); var_set.upper_margin = xatou32(argv[4]); var_set.lower_margin = xatou32(argv[5]); var_set.hsync_len = xatou32(argv[6]); var_set.vsync_len = xatou32(argv[7]); break; case CMD_ACCEL: break; case CMD_HSYNC: var_set.sync |= FB_SYNC_HOR_HIGH_ACT; break; case CMD_VSYNC: var_set.sync |= FB_SYNC_VERT_HIGH_ACT; break; #if ENABLE_FEATURE_FBSET_FANCY case CMD_XRES: var_set.xres = xatou32(argv[1]); break; case CMD_YRES: var_set.yres = xatou32(argv[1]); break; case CMD_DEPTH: var_set.bits_per_pixel = xatou32(argv[1]); break; #endif } switch (g_cmdoptions[i].code) { case CMD_FB: case CMD_DB: case CMD_ALL: case CMD_SHOW: break; default: /* other commands imply changes */ options |= OPT_CHANGE; } argc -= g_cmdoptions[i].param_count; argv += g_cmdoptions[i].param_count; goto contin; } bb_show_usage(); contin: ; } fh = xopen(fbdev, O_RDONLY); xioctl(fh, FBIOGET_VSCREENINFO, &var_old); if (options & OPT_READMODE) { #if ENABLE_FEATURE_FBSET_READMODE if (!read_mode_db(&var_old, modefile, mode)) { bb_error_msg_and_die("unknown video mode '%s'", mode); } options |= OPT_CHANGE; #endif } if (options & OPT_CHANGE) { copy_changed_values(&var_old, &var_set); if (options & OPT_ALL) var_old.activate = FB_ACTIVATE_ALL; xioctl(fh, FBIOPUT_VSCREENINFO, &var_old); } if (options == 0 || (options & OPT_SHOW)) showmode(&var_old); if (ENABLE_FEATURE_CLEAN_UP) close(fh); return EXIT_SUCCESS; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/mkswap.c������������������������������������������������������������������0000644�0000000�0000000�00000007705�12263563520�015563� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* mkswap.c - format swap device (Linux v1 only) * * Copyright 2006 Rob Landley <rob@landley.net> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define mkswap_trivial_usage //usage: "[-L LBL] BLOCKDEV [KBYTES]" //usage:#define mkswap_full_usage "\n\n" //usage: "Prepare BLOCKDEV to be used as swap partition\n" //usage: "\n -L LBL Label" #include "libbb.h" #if ENABLE_SELINUX static void mkswap_selinux_setcontext(int fd, const char *path) { struct stat stbuf; if (!is_selinux_enabled()) return; xfstat(fd, &stbuf, path); if (S_ISREG(stbuf.st_mode)) { security_context_t newcon; security_context_t oldcon = NULL; context_t context; if (fgetfilecon(fd, &oldcon) < 0) { if (errno != ENODATA) goto error; if (matchpathcon(path, stbuf.st_mode, &oldcon) < 0) goto error; } context = context_new(oldcon); if (!context || context_type_set(context, "swapfile_t")) goto error; newcon = context_str(context); if (!newcon) goto error; /* fsetfilecon_raw is hidden */ if (strcmp(oldcon, newcon) != 0 && fsetfilecon(fd, newcon) < 0) goto error; if (ENABLE_FEATURE_CLEAN_UP) { context_free(context); freecon(oldcon); } } return; error: bb_perror_msg_and_die("SELinux relabeling failed"); } #else # define mkswap_selinux_setcontext(fd, path) ((void)0) #endif /* from Linux 2.6.23 */ /* * Magic header for a swap area. ... Note that the first * kilobyte is reserved for boot loader or disk label stuff. */ struct swap_header_v1 { /* char bootbits[1024]; Space for disklabel etc. */ uint32_t version; /* second kbyte, word 0 */ uint32_t last_page; /* 1 */ uint32_t nr_badpages; /* 2 */ char sws_uuid[16]; /* 3,4,5,6 */ char sws_volume[16]; /* 7,8,9,10 */ uint32_t padding[117]; /* 11..127 */ uint32_t badpages[1]; /* 128 */ /* total 129 32-bit words in 2nd kilobyte */ } FIX_ALIASING; #define NWORDS 129 #define hdr ((struct swap_header_v1*)bb_common_bufsiz1) struct BUG_sizes { char swap_header_v1_wrong[sizeof(*hdr) != (NWORDS * 4) ? -1 : 1]; char bufsiz1_is_too_small[COMMON_BUFSIZE < (NWORDS * 4) ? -1 : 1]; }; /* Stored without terminating NUL */ static const char SWAPSPACE2[sizeof("SWAPSPACE2")-1] ALIGN1 = "SWAPSPACE2"; int mkswap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mkswap_main(int argc UNUSED_PARAM, char **argv) { int fd; unsigned pagesize; off_t len; const char *label = ""; opt_complementary = "-1"; /* at least one param */ /* TODO: -p PAGESZ, -U UUID */ getopt32(argv, "L:", &label); argv += optind; fd = xopen(argv[0], O_WRONLY); /* Figure out how big the device is */ len = get_volume_size_in_bytes(fd, argv[1], 1024, /*extend:*/ 1); pagesize = getpagesize(); len -= pagesize; /* Announce our intentions */ printf("Setting up swapspace version 1, size = %"OFF_FMT"u bytes\n", len); mkswap_selinux_setcontext(fd, argv[0]); /* hdr is zero-filled so far. Clear the first kbyte, or else * mkswap-ing former FAT partition does NOT erase its signature. * * util-linux-ng 2.17.2 claims to erase it only if it does not see * a partition table and is not run on whole disk. -f forces it. */ xwrite(fd, hdr, 1024); /* Fill the header. */ hdr->version = 1; hdr->last_page = (uoff_t)len / pagesize; if (ENABLE_FEATURE_MKSWAP_UUID) { char uuid_string[32]; generate_uuid((void*)hdr->sws_uuid); bin2hex(uuid_string, hdr->sws_uuid, 16); /* f.e. UUID=dfd9c173-be52-4d27-99a5-c34c6c2ff55f */ printf("UUID=%.8s" "-%.4s-%.4s-%.4s-%.12s\n", uuid_string, uuid_string+8, uuid_string+8+4, uuid_string+8+4+4, uuid_string+8+4+4+4 ); } safe_strncpy(hdr->sws_volume, label, 16); /* Write the header. Sync to disk because some kernel versions check * signature on disk (not in cache) during swapon. */ xwrite(fd, hdr, NWORDS * 4); xlseek(fd, pagesize - 10, SEEK_SET); xwrite(fd, SWAPSPACE2, 10); fsync(fd); if (ENABLE_FEATURE_CLEAN_UP) close(fd); return 0; } �����������������������������������������������������������busybox-1.22.1/util-linux/lsusb.c�������������������������������������������������������������������0000644�0000000�0000000�00000003266�12263563520�015407� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * lsusb implementation for busybox * * Copyright (C) 2009 Malek Degachi <malek-degachi@laposte.net> * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define lsusb_trivial_usage NOUSAGE_STR //usage:#define lsusb_full_usage "" #include "libbb.h" static int FAST_FUNC fileAction( const char *fileName, struct stat *statbuf UNUSED_PARAM, void *userData UNUSED_PARAM, int depth UNUSED_PARAM) { parser_t *parser; char *tokens[4]; char *busnum = NULL, *devnum = NULL; int product_vid = 0, product_did = 0; char *uevent_filename = concat_path_file(fileName, "/uevent"); parser = config_open2(uevent_filename, fopen_for_read); free(uevent_filename); while (config_read(parser, tokens, 4, 2, "\\/=", PARSE_NORMAL)) { if ((parser->lineno == 1) && strcmp(tokens[0], "DEVTYPE") == 0) { break; } if (strcmp(tokens[0], "PRODUCT") == 0) { product_vid = xstrtou(tokens[1], 16); product_did = xstrtou(tokens[2], 16); continue; } if (strcmp(tokens[0], "BUSNUM") == 0) { busnum = xstrdup(tokens[1]); continue; } if (strcmp(tokens[0], "DEVNUM") == 0) { devnum = xstrdup(tokens[1]); continue; } } config_close(parser); if (busnum) { printf("Bus %s Device %s: ID %04x:%04x\n", busnum, devnum, product_vid, product_did); free(busnum); free(devnum); } return TRUE; } int lsusb_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int lsusb_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { /* no options, no getopt */ recursive_action("/sys/bus/usb/devices", ACTION_RECURSE, fileAction, NULL, /* dirAction */ NULL, /* userData */ 0 /* depth */); return EXIT_SUCCESS; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/mkfs_ext2.c���������������������������������������������������������������0000644�0000000�0000000�00000062105�12263563520�016156� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * mkfs_ext2: utility to create EXT2 filesystem * inspired by genext2fs * * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define mkfs_ext2_trivial_usage //usage: "[-Fn] " /* //usage: "[-c|-l filename] " */ //usage: "[-b BLK_SIZE] " /* //usage: "[-f fragment-size] [-g blocks-per-group] " */ //usage: "[-i INODE_RATIO] [-I INODE_SIZE] " /* //usage: "[-j] [-J journal-options] [-N number-of-inodes] " */ //usage: "[-m RESERVED_PERCENT] " /* //usage: "[-o creator-os] [-O feature[,...]] [-q] " */ /* //usage: "[r fs-revision-level] [-E extended-options] [-v] [-F] " */ //usage: "[-L LABEL] " /* //usage: "[-M last-mounted-directory] [-S] [-T filesystem-type] " */ //usage: "BLOCKDEV [KBYTES]" //usage:#define mkfs_ext2_full_usage "\n\n" //usage: " -b BLK_SIZE Block size, bytes" /* //usage: "\n -c Check device for bad blocks" */ /* //usage: "\n -E opts Set extended options" */ /* //usage: "\n -f size Fragment size in bytes" */ //usage: "\n -F Force" /* //usage: "\n -g N Number of blocks in a block group" */ //usage: "\n -i RATIO Max number of files is filesystem_size / RATIO" //usage: "\n -I BYTES Inode size (min 128)" /* //usage: "\n -j Create a journal (ext3)" */ /* //usage: "\n -J opts Set journal options (size/device)" */ /* //usage: "\n -l file Read bad blocks list from file" */ //usage: "\n -L LBL Volume label" //usage: "\n -m PERCENT Percent of blocks to reserve for admin" /* //usage: "\n -M dir Set last mounted directory" */ //usage: "\n -n Dry run" /* //usage: "\n -N N Number of inodes to create" */ /* //usage: "\n -o os Set the 'creator os' field" */ /* //usage: "\n -O features Dir_index/filetype/has_journal/journal_dev/sparse_super" */ /* //usage: "\n -q Quiet" */ /* //usage: "\n -r rev Set filesystem revision" */ /* //usage: "\n -S Write superblock and group descriptors only" */ /* //usage: "\n -T fs-type Set usage type (news/largefile/largefile4)" */ /* //usage: "\n -v Verbose" */ #include "libbb.h" #include <linux/fs.h> #include "bb_e2fs_defs.h" #define ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT 0 #define ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX 1 #define EXT2_HASH_HALF_MD4 1 #define EXT2_FLAGS_SIGNED_HASH 0x0001 #define EXT2_FLAGS_UNSIGNED_HASH 0x0002 // storage helpers char BUG_wrong_field_size(void); #define STORE_LE(field, value) \ do { \ if (sizeof(field) == 4) \ field = SWAP_LE32(value); \ else if (sizeof(field) == 2) \ field = SWAP_LE16(value); \ else if (sizeof(field) == 1) \ field = (value); \ else \ BUG_wrong_field_size(); \ } while (0) #define FETCH_LE32(field) \ (sizeof(field) == 4 ? SWAP_LE32(field) : BUG_wrong_field_size()) // All fields are little-endian struct ext2_dir { uint32_t inode1; uint16_t rec_len1; uint8_t name_len1; uint8_t file_type1; char name1[4]; uint32_t inode2; uint16_t rec_len2; uint8_t name_len2; uint8_t file_type2; char name2[4]; uint32_t inode3; uint16_t rec_len3; uint8_t name_len3; uint8_t file_type3; char name3[12]; }; static unsigned int_log2(unsigned arg) { unsigned r = 0; while ((arg >>= 1) != 0) r++; return r; } // taken from mkfs_minix.c. libbb candidate? // "uint32_t size", since we never use it for anything >32 bits static uint32_t div_roundup(uint32_t size, uint32_t n) { // Overflow-resistant uint32_t res = size / n; if (res * n != size) res++; return res; } static void allocate(uint8_t *bitmap, uint32_t blocksize, uint32_t start, uint32_t end) { uint32_t i; //bb_info_msg("ALLOC: [%u][%u][%u]: [%u-%u]:=[%x],[%x]", blocksize, start, end, start/8, blocksize - end/8 - 1, (1 << (start & 7)) - 1, (uint8_t)(0xFF00 >> (end & 7))); memset(bitmap, 0, blocksize); i = start / 8; memset(bitmap, 0xFF, i); bitmap[i] = (1 << (start & 7)) - 1; //0..7 => 00000000..01111111 i = end / 8; bitmap[blocksize - i - 1] |= 0x7F00 >> (end & 7); //0..7 => 00000000..11111110 memset(bitmap + blocksize - i, 0xFF, i); // N.B. no overflow here! } static uint32_t has_super(uint32_t x) { // 0, 1 and powers of 3, 5, 7 up to 2^32 limit static const uint32_t supers[] = { 0, 1, 3, 5, 7, 9, 25, 27, 49, 81, 125, 243, 343, 625, 729, 2187, 2401, 3125, 6561, 15625, 16807, 19683, 59049, 78125, 117649, 177147, 390625, 531441, 823543, 1594323, 1953125, 4782969, 5764801, 9765625, 14348907, 40353607, 43046721, 48828125, 129140163, 244140625, 282475249, 387420489, 1162261467, 1220703125, 1977326743, 3486784401/* >2^31 */, }; const uint32_t *sp = supers + ARRAY_SIZE(supers); while (1) { sp--; if (x == *sp) return 1; if (x > *sp) return 0; } } #define fd 3 /* predefined output descriptor */ static void PUT(uint64_t off, void *buf, uint32_t size) { // bb_info_msg("PUT[%llu]:[%u]", off, size); xlseek(fd, off, SEEK_SET); xwrite(fd, buf, size); } // 128 and 256-byte inodes: // 128-byte inode is described by struct ext2_inode. // 256-byte one just has these fields appended: // __u16 i_extra_isize; // __u16 i_pad1; // __u32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */ // __u32 i_mtime_extra; /* extra Modification time (nsec << 2 | epoch) */ // __u32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */ // __u32 i_crtime; /* File creation time */ // __u32 i_crtime_extra; /* extra File creation time (nsec << 2 | epoch)*/ // __u32 i_version_hi; /* high 32 bits for 64-bit version */ // the rest is padding. // // linux/ext2_fs.h has "#define i_size_high i_dir_acl" which suggests that even // 128-byte inode is capable of describing large files (i_dir_acl is meaningful // only for directories, which never need i_size_high). // // Standard mke2fs creates a filesystem with 256-byte inodes if it is // bigger than 0.5GB. // Standard mke2fs 1.41.9: // Usage: mke2fs [-c|-l filename] [-b block-size] [-f fragment-size] // [-i bytes-per-inode] [-I inode-size] [-J journal-options] // [-G meta group size] [-N number-of-inodes] // [-m reserved-blocks-percentage] [-o creator-os] // [-g blocks-per-group] [-L volume-label] [-M last-mounted-directory] // [-O feature[,...]] [-r fs-revision] [-E extended-option[,...]] // [-T fs-type] [-U UUID] [-jnqvFSV] device [blocks-count] // // Options not commented below are taken but silently ignored: enum { OPT_c = 1 << 0, OPT_l = 1 << 1, OPT_b = 1 << 2, // block size, in bytes OPT_f = 1 << 3, OPT_i = 1 << 4, // bytes per inode OPT_I = 1 << 5, // custom inode size, in bytes OPT_J = 1 << 6, OPT_G = 1 << 7, OPT_N = 1 << 8, OPT_m = 1 << 9, // percentage of blocks reserved for superuser OPT_o = 1 << 10, OPT_g = 1 << 11, OPT_L = 1 << 12, // label OPT_M = 1 << 13, OPT_O = 1 << 14, OPT_r = 1 << 15, OPT_E = 1 << 16, OPT_T = 1 << 17, OPT_U = 1 << 18, OPT_j = 1 << 19, OPT_n = 1 << 20, // dry run: do not write anything OPT_q = 1 << 21, OPT_v = 1 << 22, OPT_F = 1 << 23, OPT_S = 1 << 24, //OPT_V = 1 << 25, // -V version. bbox applets don't support that }; int mkfs_ext2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv) { unsigned i, pos, n; unsigned bs, bpi; unsigned blocksize, blocksize_log2; unsigned inodesize, user_inodesize; unsigned reserved_percent = 5; unsigned long long kilobytes; uint32_t nblocks, nblocks_full; uint32_t nreserved; uint32_t ngroups; uint32_t bytes_per_inode; uint32_t first_block; uint32_t inodes_per_group; uint32_t group_desc_blocks; uint32_t inode_table_blocks; uint32_t lost_and_found_blocks; time_t timestamp; const char *label = ""; struct stat st; struct ext2_super_block *sb; // superblock struct ext2_group_desc *gd; // group descriptors struct ext2_inode *inode; struct ext2_dir *dir; uint8_t *buf; // using global "option_mask32" instead of local "opts": // we are register starved here opt_complementary = "-1:b+:i+:I+:m+"; /*opts =*/ getopt32(argv, "cl:b:f:i:I:J:G:N:m:o:g:L:M:O:r:E:T:U:jnqvFS", /*lbfi:*/ NULL, &bs, NULL, &bpi, /*IJGN:*/ &user_inodesize, NULL, NULL, NULL, /*mogL:*/ &reserved_percent, NULL, NULL, &label, /*MOrE:*/ NULL, NULL, NULL, NULL, /*TU:*/ NULL, NULL); argv += optind; // argv[0] -- device // open the device, check the device is a block device xmove_fd(xopen(argv[0], O_WRONLY), fd); xfstat(fd, &st, argv[0]); if (!S_ISBLK(st.st_mode) && !(option_mask32 & OPT_F)) bb_error_msg_and_die("%s: not a block device", argv[0]); // check if it is mounted // N.B. what if we format a file? find_mount_point will return false negative since // it is loop block device which is mounted! if (find_mount_point(argv[0], 0)) bb_error_msg_and_die("can't format mounted filesystem"); // get size in kbytes kilobytes = get_volume_size_in_bytes(fd, argv[1], 1024, /*extend:*/ !(option_mask32 & OPT_n)) / 1024; bytes_per_inode = 16384; if (kilobytes < 512*1024) bytes_per_inode = 4096; if (kilobytes < 3*1024) bytes_per_inode = 8192; if (option_mask32 & OPT_i) bytes_per_inode = bpi; // Determine block size and inode size // block size is a multiple of 1024 // inode size is a multiple of 128 blocksize = 1024; inodesize = sizeof(struct ext2_inode); // 128 if (kilobytes >= 512*1024) { // mke2fs 1.41.9 compat blocksize = 4096; inodesize = 256; } if (EXT2_MAX_BLOCK_SIZE > 4096) { // kilobytes >> 22 == size in 4gigabyte chunks. // if size >= 16k gigs, blocksize must be increased. // Try "mke2fs -F image $((16 * 1024*1024*1024))" while ((kilobytes >> 22) >= blocksize) blocksize *= 2; } if (option_mask32 & OPT_b) blocksize = bs; if (blocksize < EXT2_MIN_BLOCK_SIZE || blocksize > EXT2_MAX_BLOCK_SIZE || (blocksize & (blocksize - 1)) // not power of 2 ) { bb_error_msg_and_die("blocksize %u is bad", blocksize); } // Do we have custom inode size? if (option_mask32 & OPT_I) { if (user_inodesize < sizeof(*inode) || user_inodesize > blocksize || (user_inodesize & (user_inodesize - 1)) // not power of 2 ) { bb_error_msg("-%c is bad", 'I'); } else { inodesize = user_inodesize; } } if ((int32_t)bytes_per_inode < blocksize) bb_error_msg_and_die("-%c is bad", 'i'); // number of bits in one block, i.e. 8*blocksize #define blocks_per_group (8 * blocksize) first_block = (EXT2_MIN_BLOCK_SIZE == blocksize); blocksize_log2 = int_log2(blocksize); // Determine number of blocks kilobytes >>= (blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE); nblocks = kilobytes; if (nblocks != kilobytes) bb_error_msg_and_die("block count doesn't fit in 32 bits"); #define kilobytes kilobytes_unused_after_this // Experimentally, standard mke2fs won't work on images smaller than 60k if (nblocks < 60) bb_error_msg_and_die("need >= 60 blocks"); // How many reserved blocks? if (reserved_percent > 50) bb_error_msg_and_die("-%c is bad", 'm'); nreserved = (uint64_t)nblocks * reserved_percent / 100; // N.B. killing e2fsprogs feature! Unused blocks don't account in calculations nblocks_full = nblocks; // If last block group is too small, nblocks may be decreased in order // to discard it, and control returns here to recalculate some // parameters. // Note: blocksize and bytes_per_inode are never recalculated. retry: // N.B. a block group can have no more than blocks_per_group blocks ngroups = div_roundup(nblocks - first_block, blocks_per_group); group_desc_blocks = div_roundup(ngroups, blocksize / sizeof(*gd)); // TODO: reserved blocks must be marked as such in the bitmaps, // or resulting filesystem is corrupt if (ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT) { /* * From e2fsprogs: Calculate the number of GDT blocks to reserve for online * filesystem growth. * The absolute maximum number of GDT blocks we can reserve is determined by * the number of block pointers that can fit into a single block. * We set it at 1024x the current filesystem size, or * the upper block count limit (2^32), whichever is lower. */ uint32_t reserved_group_desc_blocks = 0xFFFFFFFF; // maximum block number if (nblocks < reserved_group_desc_blocks / 1024) reserved_group_desc_blocks = nblocks * 1024; reserved_group_desc_blocks = div_roundup(reserved_group_desc_blocks - first_block, blocks_per_group); reserved_group_desc_blocks = div_roundup(reserved_group_desc_blocks, blocksize / sizeof(*gd)) - group_desc_blocks; if (reserved_group_desc_blocks > blocksize / sizeof(uint32_t)) reserved_group_desc_blocks = blocksize / sizeof(uint32_t); //TODO: STORE_LE(sb->s_reserved_gdt_blocks, reserved_group_desc_blocks); group_desc_blocks += reserved_group_desc_blocks; } { // N.B. e2fsprogs does as follows! uint32_t overhead, remainder; // ninodes is the max number of inodes in this filesystem uint32_t ninodes = ((uint64_t) nblocks_full * blocksize) / bytes_per_inode; if (ninodes < EXT2_GOOD_OLD_FIRST_INO+1) ninodes = EXT2_GOOD_OLD_FIRST_INO+1; inodes_per_group = div_roundup(ninodes, ngroups); // minimum number because the first EXT2_GOOD_OLD_FIRST_INO-1 are reserved if (inodes_per_group < 16) inodes_per_group = 16; // a block group can't have more inodes than blocks if (inodes_per_group > blocks_per_group) inodes_per_group = blocks_per_group; // adjust inodes per group so they completely fill the inode table blocks in the descriptor inodes_per_group = (div_roundup(inodes_per_group * inodesize, blocksize) * blocksize) / inodesize; // make sure the number of inodes per group is a multiple of 8 inodes_per_group &= ~7; inode_table_blocks = div_roundup(inodes_per_group * inodesize, blocksize); // to be useful, lost+found should occupy at least 2 blocks (but not exceeding 16*1024 bytes), // and at most EXT2_NDIR_BLOCKS. So reserve these blocks right now /* Or e2fsprogs comment verbatim (what does it mean?): * Ensure that lost+found is at least 2 blocks, so we always * test large empty blocks for big-block filesystems. */ lost_and_found_blocks = MIN(EXT2_NDIR_BLOCKS, 16 >> (blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE)); // the last group needs more attention: isn't it too small for possible overhead? overhead = (has_super(ngroups - 1) ? (1/*sb*/ + group_desc_blocks) : 0) + 1/*bbmp*/ + 1/*ibmp*/ + inode_table_blocks; remainder = (nblocks - first_block) % blocks_per_group; ////can't happen, nblocks >= 60 guarantees this ////if ((1 == ngroups) //// && remainder //// && (remainder < overhead + 1/* "/" */ + lost_and_found_blocks) ////) { //// bb_error_msg_and_die("way small device"); ////} // Standard mke2fs uses 50. Looks like a bug in our calculation // of "remainder" or "overhead" - we don't match standard mke2fs // when we transition from one group to two groups // (a bit after 8M image size), but it works for two->three groups // transition (at 16M). if (remainder && (remainder < overhead + 50)) { //bb_info_msg("CHOP[%u]", remainder); nblocks -= remainder; goto retry; } } if (nblocks_full - nblocks) printf("warning: %u blocks unused\n\n", nblocks_full - nblocks); printf( "Filesystem label=%s\n" "OS type: Linux\n" "Block size=%u (log=%u)\n" "Fragment size=%u (log=%u)\n" "%u inodes, %u blocks\n" "%u blocks (%u%%) reserved for the super user\n" "First data block=%u\n" "Maximum filesystem blocks=%u\n" "%u block groups\n" "%u blocks per group, %u fragments per group\n" "%u inodes per group" , label , blocksize, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE , blocksize, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE , inodes_per_group * ngroups, nblocks , nreserved, reserved_percent , first_block , group_desc_blocks * (blocksize / (unsigned)sizeof(*gd)) * blocks_per_group , ngroups , blocks_per_group, blocks_per_group , inodes_per_group ); { const char *fmt = "\nSuperblock backups stored on blocks:\n" "\t%u"; pos = first_block; for (i = 1; i < ngroups; i++) { pos += blocks_per_group; if (has_super(i)) { printf(fmt, (unsigned)pos); fmt = ", %u"; } } } bb_putchar('\n'); if (option_mask32 & OPT_n) { if (ENABLE_FEATURE_CLEAN_UP) close(fd); return EXIT_SUCCESS; } // TODO: 3/5 refuse if mounted // TODO: 4/5 compat options // TODO: 1/5 sanity checks // TODO: 0/5 more verbose error messages // TODO: 4/5 bigendianness: recheck, wait for ARM reporters // TODO: 2/5 reserved GDT: how to mark but not allocate? // TODO: 3/5 dir_index? // fill the superblock sb = xzalloc(1024); STORE_LE(sb->s_rev_level, EXT2_DYNAMIC_REV); // revision 1 filesystem STORE_LE(sb->s_magic, EXT2_SUPER_MAGIC); STORE_LE(sb->s_inode_size, inodesize); // set "Required extra isize" and "Desired extra isize" fields to 28 if (inodesize != sizeof(*inode)) { STORE_LE(sb->s_min_extra_isize, 0x001c); STORE_LE(sb->s_want_extra_isize, 0x001c); } STORE_LE(sb->s_first_ino, EXT2_GOOD_OLD_FIRST_INO); STORE_LE(sb->s_log_block_size, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE); STORE_LE(sb->s_log_frag_size, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE); // first 1024 bytes of the device are for boot record. If block size is 1024 bytes, then // the first block is 1, otherwise 0 STORE_LE(sb->s_first_data_block, first_block); // block and inode bitmaps occupy no more than one block, so maximum number of blocks is STORE_LE(sb->s_blocks_per_group, blocks_per_group); STORE_LE(sb->s_frags_per_group, blocks_per_group); // blocks STORE_LE(sb->s_blocks_count, nblocks); // reserve blocks for superuser STORE_LE(sb->s_r_blocks_count, nreserved); // ninodes STORE_LE(sb->s_inodes_per_group, inodes_per_group); STORE_LE(sb->s_inodes_count, inodes_per_group * ngroups); STORE_LE(sb->s_free_inodes_count, inodes_per_group * ngroups - EXT2_GOOD_OLD_FIRST_INO); // timestamps timestamp = time(NULL); STORE_LE(sb->s_mkfs_time, timestamp); STORE_LE(sb->s_wtime, timestamp); STORE_LE(sb->s_lastcheck, timestamp); // misc. Values are chosen to match mke2fs 1.41.9 STORE_LE(sb->s_state, 1); // TODO: what's 1? STORE_LE(sb->s_creator_os, EXT2_OS_LINUX); STORE_LE(sb->s_checkinterval, 24*60*60 * 180); // 180 days STORE_LE(sb->s_errors, EXT2_ERRORS_DEFAULT); // mke2fs 1.41.9 also sets EXT3_FEATURE_COMPAT_RESIZE_INODE // and if >= 0.5GB, EXT3_FEATURE_RO_COMPAT_LARGE_FILE. // we use values which match "mke2fs -O ^resize_inode": // in this case 1.41.9 never sets EXT3_FEATURE_RO_COMPAT_LARGE_FILE. STORE_LE(sb->s_feature_compat, EXT2_FEATURE_COMPAT_SUPP | (EXT2_FEATURE_COMPAT_RESIZE_INO * ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT) | (EXT2_FEATURE_COMPAT_DIR_INDEX * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX) ); STORE_LE(sb->s_feature_incompat, EXT2_FEATURE_INCOMPAT_FILETYPE); STORE_LE(sb->s_feature_ro_compat, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER); STORE_LE(sb->s_flags, EXT2_FLAGS_UNSIGNED_HASH * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX); generate_uuid(sb->s_uuid); if (ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX) { STORE_LE(sb->s_def_hash_version, EXT2_HASH_HALF_MD4); generate_uuid((uint8_t *)sb->s_hash_seed); } /* * From e2fsprogs: add "jitter" to the superblock's check interval so that we * don't check all the filesystems at the same time. We use a * kludgy hack of using the UUID to derive a random jitter value. */ STORE_LE(sb->s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT + (sb->s_uuid[ARRAY_SIZE(sb->s_uuid)-1] % EXT2_DFL_MAX_MNT_COUNT)); // write the label safe_strncpy((char *)sb->s_volume_name, label, sizeof(sb->s_volume_name)); // calculate filesystem skeleton structures gd = xzalloc(group_desc_blocks * blocksize); buf = xmalloc(blocksize); sb->s_free_blocks_count = 0; for (i = 0, pos = first_block, n = nblocks - first_block; i < ngroups; i++, pos += blocks_per_group, n -= blocks_per_group ) { uint32_t overhead = pos + (has_super(i) ? (1/*sb*/ + group_desc_blocks) : 0); uint32_t free_blocks; // fill group descriptors STORE_LE(gd[i].bg_block_bitmap, overhead + 0); STORE_LE(gd[i].bg_inode_bitmap, overhead + 1); STORE_LE(gd[i].bg_inode_table, overhead + 2); overhead = overhead - pos + 1/*bbmp*/ + 1/*ibmp*/ + inode_table_blocks; gd[i].bg_free_inodes_count = inodes_per_group; //STORE_LE(gd[i].bg_used_dirs_count, 0); // N.B. both "/" and "/lost+found" are within the first block group // "/" occupies 1 block, "/lost+found" occupies lost_and_found_blocks... if (0 == i) { // ... thus increased overhead for the first block group ... overhead += 1 + lost_and_found_blocks; // ... and 2 used directories STORE_LE(gd[i].bg_used_dirs_count, 2); // well known reserved inodes belong to the first block too gd[i].bg_free_inodes_count -= EXT2_GOOD_OLD_FIRST_INO; } // cache free block count of the group free_blocks = (n < blocks_per_group ? n : blocks_per_group) - overhead; // mark preallocated blocks as allocated //bb_info_msg("ALLOC: [%u][%u][%u]", blocksize, overhead, blocks_per_group - (free_blocks + overhead)); allocate(buf, blocksize, // reserve "overhead" blocks overhead, // mark unused trailing blocks blocks_per_group - (free_blocks + overhead) ); // dump block bitmap PUT((uint64_t)(FETCH_LE32(gd[i].bg_block_bitmap)) * blocksize, buf, blocksize); STORE_LE(gd[i].bg_free_blocks_count, free_blocks); // mark preallocated inodes as allocated allocate(buf, blocksize, // mark reserved inodes inodes_per_group - gd[i].bg_free_inodes_count, // mark unused trailing inodes blocks_per_group - inodes_per_group ); // dump inode bitmap //PUT((uint64_t)(FETCH_LE32(gd[i].bg_block_bitmap)) * blocksize, buf, blocksize); //but it's right after block bitmap, so we can just: xwrite(fd, buf, blocksize); STORE_LE(gd[i].bg_free_inodes_count, gd[i].bg_free_inodes_count); // count overall free blocks sb->s_free_blocks_count += free_blocks; } STORE_LE(sb->s_free_blocks_count, sb->s_free_blocks_count); // dump filesystem skeleton structures // printf("Writing superblocks and filesystem accounting information: "); for (i = 0, pos = first_block; i < ngroups; i++, pos += blocks_per_group) { // dump superblock and group descriptors and their backups if (has_super(i)) { // N.B. 1024 byte blocks are special PUT(((uint64_t)pos * blocksize) + ((0 == i && 1024 != blocksize) ? 1024 : 0), sb, 1024); PUT(((uint64_t)pos * blocksize) + blocksize, gd, group_desc_blocks * blocksize); } } // zero boot sectors memset(buf, 0, blocksize); // Disabled: standard mke2fs doesn't do this, and // on SPARC this destroys Sun disklabel. // Users who need/want zeroing can easily do it with dd. //PUT(0, buf, 1024); // N.B. 1024 <= blocksize, so buf[0..1023] contains zeros // zero inode tables for (i = 0; i < ngroups; ++i) for (n = 0; n < inode_table_blocks; ++n) PUT((uint64_t)(FETCH_LE32(gd[i].bg_inode_table) + n) * blocksize, buf, blocksize); // prepare directory inode inode = (struct ext2_inode *)buf; STORE_LE(inode->i_mode, S_IFDIR | S_IRWXU | S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH); STORE_LE(inode->i_mtime, timestamp); STORE_LE(inode->i_atime, timestamp); STORE_LE(inode->i_ctime, timestamp); STORE_LE(inode->i_size, blocksize); // inode->i_blocks stores the number of 512 byte data blocks // (512, because it goes directly to struct stat without scaling) STORE_LE(inode->i_blocks, blocksize / 512); // dump root dir inode STORE_LE(inode->i_links_count, 3); // "/.", "/..", "/lost+found/.." point to this inode STORE_LE(inode->i_block[0], FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks); PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) * blocksize) + (EXT2_ROOT_INO-1) * inodesize, buf, inodesize); // dump lost+found dir inode STORE_LE(inode->i_links_count, 2); // both "/lost+found" and "/lost+found/." point to this inode STORE_LE(inode->i_size, lost_and_found_blocks * blocksize); STORE_LE(inode->i_blocks, (lost_and_found_blocks * blocksize) / 512); n = FETCH_LE32(inode->i_block[0]) + 1; for (i = 0; i < lost_and_found_blocks; ++i) STORE_LE(inode->i_block[i], i + n); // use next block //bb_info_msg("LAST BLOCK USED[%u]", i + n); PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) * blocksize) + (EXT2_GOOD_OLD_FIRST_INO-1) * inodesize, buf, inodesize); // dump directories memset(buf, 0, blocksize); dir = (struct ext2_dir *)buf; // dump 2nd+ blocks of "/lost+found" STORE_LE(dir->rec_len1, blocksize); // e2fsck 1.41.4 compat (1.41.9 does not need this) for (i = 1; i < lost_and_found_blocks; ++i) PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks + 1+i) * blocksize, buf, blocksize); // dump 1st block of "/lost+found" STORE_LE(dir->inode1, EXT2_GOOD_OLD_FIRST_INO); STORE_LE(dir->rec_len1, 12); STORE_LE(dir->name_len1, 1); STORE_LE(dir->file_type1, EXT2_FT_DIR); dir->name1[0] = '.'; STORE_LE(dir->inode2, EXT2_ROOT_INO); STORE_LE(dir->rec_len2, blocksize - 12); STORE_LE(dir->name_len2, 2); STORE_LE(dir->file_type2, EXT2_FT_DIR); dir->name2[0] = '.'; dir->name2[1] = '.'; PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks + 1) * blocksize, buf, blocksize); // dump root dir block STORE_LE(dir->inode1, EXT2_ROOT_INO); STORE_LE(dir->rec_len2, 12); STORE_LE(dir->inode3, EXT2_GOOD_OLD_FIRST_INO); STORE_LE(dir->rec_len3, blocksize - 12 - 12); STORE_LE(dir->name_len3, 10); STORE_LE(dir->file_type3, EXT2_FT_DIR); strcpy(dir->name3, "lost+found"); PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks + 0) * blocksize, buf, blocksize); // cleanup if (ENABLE_FEATURE_CLEAN_UP) { free(buf); free(gd); free(sb); } xclose(fd); return EXIT_SUCCESS; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/hexdump.c�����������������������������������������������������������������0000644�0000000�0000000�00000010570�12263563520�015725� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * hexdump implementation for busybox * 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 source tree. */ //usage:#define hexdump_trivial_usage //usage: "[-bcCdefnosvx" IF_FEATURE_HEXDUMP_REVERSE("R") "] [FILE]..." //usage:#define hexdump_full_usage "\n\n" //usage: "Display FILEs (or stdin) in a user specified format\n" //usage: "\n -b One-byte octal display" //usage: "\n -c One-byte character display" //usage: "\n -C Canonical hex+ASCII, 16 bytes per line" //usage: "\n -d Two-byte decimal display" //usage: "\n -e FORMAT_STRING" //usage: "\n -f FORMAT_FILE" //usage: "\n -n LENGTH Interpret only LENGTH bytes of input" //usage: "\n -o Two-byte octal display" //usage: "\n -s OFFSET Skip OFFSET bytes" //usage: "\n -v Display all input data" //usage: "\n -x Two-byte hexadecimal display" //usage: IF_FEATURE_HEXDUMP_REVERSE( //usage: "\n -R Reverse of 'hexdump -Cv'") //usage: //usage:#define hd_trivial_usage //usage: "FILE..." //usage:#define hd_full_usage "\n\n" //usage: "hd is an alias for hexdump -C" #include "libbb.h" #include "dump.h" /* This is a NOEXEC applet. Be very careful! */ static void bb_dump_addfile(dumper_t *dumper, char *name) { char *p; FILE *fp; char *buf; fp = xfopen_for_read(name); while ((buf = xmalloc_fgetline(fp)) != NULL) { p = skip_whitespace(buf); if (*p && (*p != '#')) { bb_dump_add(dumper, p); } free(buf); } fclose(fp); } static const char *const add_strings[] = { "\"%07.7_ax \" 16/1 \"%03o \" \"\\n\"", /* b */ "\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\"", /* c */ "\"%07.7_ax \" 8/2 \" %05u \" \"\\n\"", /* d */ "\"%07.7_ax \" 8/2 \" %06o \" \"\\n\"", /* o */ "\"%07.7_ax \" 8/2 \" %04x \" \"\\n\"", /* x */ }; static const char add_first[] ALIGN1 = "\"%07.7_Ax\n\""; static const char hexdump_opts[] ALIGN1 = "bcdoxCe:f:n:s:v" IF_FEATURE_HEXDUMP_REVERSE("R"); int hexdump_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int hexdump_main(int argc, char **argv) { dumper_t *dumper = alloc_dumper(); const char *p; int ch; #if ENABLE_FEATURE_HEXDUMP_REVERSE FILE *fp; smallint rdump = 0; #endif if (ENABLE_HD && !applet_name[2]) { /* we are "hd" */ ch = 'C'; goto hd_applet; } /* We cannot use getopt32: in hexdump options are cumulative. * E.g. "hexdump -C -C file" should dump each line twice */ while ((ch = getopt(argc, argv, hexdump_opts)) > 0) { p = strchr(hexdump_opts, ch); if (!p) bb_show_usage(); if ((p - hexdump_opts) < 5) { bb_dump_add(dumper, add_first); bb_dump_add(dumper, add_strings[(int)(p - hexdump_opts)]); } /* Save a little bit of space below by omitting the 'else's. */ if (ch == 'C') { hd_applet: bb_dump_add(dumper, "\"%08.8_Ax\n\""); bb_dump_add(dumper, "\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \" "); bb_dump_add(dumper, "\" |\" 16/1 \"%_p\" \"|\\n\""); } if (ch == 'e') { bb_dump_add(dumper, optarg); } /* else */ if (ch == 'f') { bb_dump_addfile(dumper, optarg); } /* else */ if (ch == 'n') { dumper->dump_length = xatoi_positive(optarg); } /* else */ if (ch == 's') { /* compat: -s accepts hex numbers too */ dumper->dump_skip = xstrtoull_range_sfx( optarg, /*base:*/ 0, /*lo:*/ 0, /*hi:*/ OFF_T_MAX, bkm_suffixes ); } /* else */ if (ch == 'v') { dumper->dump_vflag = ALL; } #if ENABLE_FEATURE_HEXDUMP_REVERSE if (ch == 'R') { rdump = 1; } #endif } if (!dumper->fshead) { bb_dump_add(dumper, add_first); bb_dump_add(dumper, "\"%07.7_ax \" 8/2 \"%04x \" \"\\n\""); } argv += optind; #if !ENABLE_FEATURE_HEXDUMP_REVERSE return bb_dump_dump(dumper, argv); #else if (!rdump) { return bb_dump_dump(dumper, argv); } /* -R: reverse of 'hexdump -Cv' */ fp = stdin; if (!*argv) { argv--; goto jump_in; } do { char *buf; fp = xfopen_for_read(*argv); jump_in: while ((buf = xmalloc_fgetline(fp)) != NULL) { p = buf; while (1) { /* skip address or previous byte */ while (isxdigit(*p)) p++; while (*p == ' ') p++; /* '|' char will break the line */ if (!isxdigit(*p) || sscanf(p, "%x ", &ch) != 1) break; putchar(ch); } free(buf); } fclose(fp); } while (*++argv); fflush_stdout_and_exit(EXIT_SUCCESS); #endif } ����������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/flock.c�������������������������������������������������������������������0000644�0000000�0000000�00000004627�12263563520�015357� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 Timo Teras <timo.teras@iki.fi> * * This is free software, licensed under the GNU General Public License v2. */ //usage:#define flock_trivial_usage //usage: "[-sxun] FD|{FILE [-c] PROG ARGS}" //usage:#define flock_full_usage "\n\n" //usage: "[Un]lock file descriptor, or lock FILE, run PROG\n" //usage: "\n -s Shared lock" //usage: "\n -x Exclusive lock (default)" //usage: "\n -u Unlock FD" //usage: "\n -n Fail rather than wait" #include <sys/file.h> #include "libbb.h" int flock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int flock_main(int argc UNUSED_PARAM, char **argv) { int mode, opt, fd; enum { OPT_s = (1 << 0), OPT_x = (1 << 1), OPT_n = (1 << 2), OPT_u = (1 << 3), OPT_c = (1 << 4), }; #if ENABLE_LONG_OPTS static const char getopt_longopts[] ALIGN1 = "shared\0" No_argument "s" "exclusive\0" No_argument "x" "unlock\0" No_argument "u" "nonblock\0" No_argument "n" ; applet_long_options = getopt_longopts; #endif opt_complementary = "-1"; opt = getopt32(argv, "+sxnu"); argv += optind; if (argv[1]) { fd = open(argv[0], O_RDONLY|O_NOCTTY|O_CREAT, 0666); if (fd < 0 && errno == EISDIR) fd = open(argv[0], O_RDONLY|O_NOCTTY); if (fd < 0) bb_perror_msg_and_die("can't open '%s'", argv[0]); //TODO? close_on_exec_on(fd); } else { fd = xatoi_positive(argv[0]); } argv++; /* If it is "flock FILE -c PROG", then -c isn't caught by getopt32: * we use "+" in order to support "flock -opt FILE PROG -with-opts", * we need to remove -c by hand. * TODO: in upstream, -c 'PROG ARGS' means "run sh -c 'PROG ARGS'" */ if (argv[0] && argv[0][0] == '-' && ( (argv[0][1] == 'c' && !argv[0][2]) || (ENABLE_LONG_OPTS && strcmp(argv[0] + 1, "-command") == 0) ) ) { argv++; } if (OPT_s == LOCK_SH && OPT_x == LOCK_EX && OPT_n == LOCK_NB && OPT_u == LOCK_UN) { /* With suitably matched constants, mode setting is much simpler */ mode = opt & (LOCK_SH + LOCK_EX + LOCK_NB + LOCK_UN); if (!(mode & ~LOCK_NB)) mode |= LOCK_EX; } else { if (opt & OPT_u) mode = LOCK_UN; else if (opt & OPT_s) mode = LOCK_SH; else mode = LOCK_EX; if (opt & OPT_n) mode |= LOCK_NB; } if (flock(fd, mode) != 0) { if (errno == EWOULDBLOCK) return EXIT_FAILURE; bb_perror_nomsg_and_die(); } if (argv[0]) return spawn_and_wait(argv); return EXIT_SUCCESS; } ���������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/Config.src����������������������������������������������������������������0000644�0000000�0000000�00000052244�12263563520�016031� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Linux System Utilities" INSERT config ACPID bool "acpid" default y select PLATFORM_LINUX help acpid listens to ACPI events coming either in textual form from /proc/acpi/event (though it is marked deprecated it is still widely used and _is_ a standard) or in binary form from specified evdevs (just use /dev/input/event*). It parses the event to retrieve ACTION and a possible PARAMETER. It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts (if the resulting path is a directory) or directly as an executable. N.B. acpid relies on run-parts so have the latter installed. config FEATURE_ACPID_COMPAT bool "Accept and ignore redundant options" default y depends on ACPID help Accept and ignore compatibility options -g -m -s -S -v. config BLKID bool "blkid" default y select PLATFORM_LINUX select VOLUMEID help Lists labels and UUIDs of all filesystems. WARNING: With all submodules selected, it will add ~8k to busybox. config FEATURE_BLKID_TYPE bool "Print filesystem type" default n depends on BLKID help Show TYPE="filesystem type" config DMESG bool "dmesg" default y select PLATFORM_LINUX help dmesg is used to examine or control the kernel ring buffer. When the Linux kernel prints messages to the system log, they are stored in the kernel ring buffer. You can use dmesg to print the kernel's ring buffer, clear the kernel ring buffer, change the size of the kernel ring buffer, and change the priority level at which kernel messages are also logged to the system console. Enable this option if you wish to enable the 'dmesg' utility. config FEATURE_DMESG_PRETTY bool "Pretty dmesg output" default y depends on DMESG help If you wish to scrub the syslog level from the output, say 'Y' here. The syslog level is a string prefixed to every line with the form "<#>". With this option you will see: # dmesg Linux version 2.6.17.4 ..... BIOS-provided physical RAM map: BIOS-e820: 0000000000000000 - 000000000009f000 (usable) Without this option you will see: # dmesg <5>Linux version 2.6.17.4 ..... <6>BIOS-provided physical RAM map: <6> BIOS-e820: 0000000000000000 - 000000000009f000 (usable) config FBSET bool "fbset" default y select PLATFORM_LINUX help fbset is used to show or change the settings of a Linux frame buffer device. The frame buffer device provides a simple and unique interface to access a graphics display. Enable this option if you wish to enable the 'fbset' utility. config FEATURE_FBSET_FANCY bool "Turn on extra fbset options" default y depends on FBSET help This option enables extended fbset options, allowing one to set the framebuffer size, color depth, etc. interface to access a graphics display. Enable this option if you wish to enable extended fbset options. config FEATURE_FBSET_READMODE bool "Turn on fbset readmode support" default y depends on FBSET help This option allows fbset to read the video mode database stored by default as /etc/fb.modes, which can be used to set frame buffer device to pre-defined video modes. config FDFLUSH bool "fdflush" default y select PLATFORM_LINUX help fdflush is only needed when changing media on slightly-broken removable media drives. It is used to make Linux believe that a hardware disk-change switch has been actuated, which causes Linux to forget anything it has cached from the previous media. If you have such a slightly-broken drive, you will need to run fdflush every time you change a disk. Most people have working hardware and can safely leave this disabled. config FDFORMAT bool "fdformat" default y select PLATFORM_LINUX help fdformat is used to low-level format a floppy disk. config FDISK bool "fdisk" default y select PLATFORM_LINUX help The fdisk utility is used to divide hard disks into one or more logical disks, which are generally called partitions. This utility can be used to list and edit the set of partitions or BSD style 'disk slices' that are defined on a hard drive. config FDISK_SUPPORT_LARGE_DISKS bool "Support over 4GB disks" default y depends on FDISK depends on !LFS # with LFS no special code is needed help Enable this option to support large disks > 4GB. config FEATURE_FDISK_WRITABLE bool "Write support" default y depends on FDISK help Enabling this option allows you to create or change a partition table and write those changes out to disk. If you leave this option disabled, you will only be able to view the partition table. config FEATURE_AIX_LABEL bool "Support AIX disklabels" default n depends on FDISK && FEATURE_FDISK_WRITABLE help Enabling this option allows you to create or change AIX disklabels. Most people can safely leave this option disabled. config FEATURE_SGI_LABEL bool "Support SGI disklabels" default n depends on FDISK && FEATURE_FDISK_WRITABLE help Enabling this option allows you to create or change SGI disklabels. Most people can safely leave this option disabled. config FEATURE_SUN_LABEL bool "Support SUN disklabels" default n depends on FDISK && FEATURE_FDISK_WRITABLE help Enabling this option allows you to create or change SUN disklabels. Most people can safely leave this option disabled. config FEATURE_OSF_LABEL bool "Support BSD disklabels" default n depends on FDISK && FEATURE_FDISK_WRITABLE help Enabling this option allows you to create or change BSD disklabels and define and edit BSD disk slices. config FEATURE_GPT_LABEL bool "Support GPT disklabels" default n depends on FDISK && FEATURE_FDISK_WRITABLE help Enabling this option allows you to view GUID Partition Table disklabels. config FEATURE_FDISK_ADVANCED bool "Support expert mode" default y depends on FDISK && FEATURE_FDISK_WRITABLE help Enabling this option allows you to do terribly unsafe things like define arbitrary drive geometry, move the beginning of data in a partition, and similarly evil things. Unless you have a very good reason you would be wise to leave this disabled. config FINDFS bool "findfs" default y select PLATFORM_LINUX select VOLUMEID help Prints the name of a filesystem with given label or UUID. WARNING: With all submodules selected, it will add ~8k to busybox. config FLOCK bool "flock" default y help Manage locks from shell scripts config FREERAMDISK bool "freeramdisk" default y select PLATFORM_LINUX help Linux allows you to create ramdisks. This utility allows you to delete them and completely free all memory that was used for the ramdisk. For example, if you boot Linux into a ramdisk and later pivot_root, you may want to free the memory that is allocated to the ramdisk. If you have no use for freeing memory from a ramdisk, leave this disabled. config FSCK_MINIX bool "fsck_minix" default y help The minix filesystem is a nice, small, compact, read-write filesystem with little overhead. It is not a journaling filesystem however and can experience corruption if it is not properly unmounted or if the power goes off in the middle of a write. This utility allows you to check for and attempt to repair any corruption that occurs to a minix filesystem. config MKFS_EXT2 bool "mkfs_ext2" default y select PLATFORM_LINUX help Utility to create EXT2 filesystems. config MKFS_MINIX bool "mkfs_minix" default y select PLATFORM_LINUX help The minix filesystem is a nice, small, compact, read-write filesystem with little overhead. If you wish to be able to create minix filesystems this utility will do the job for you. config FEATURE_MINIX2 bool "Support Minix fs v2 (fsck_minix/mkfs_minix)" default y depends on FSCK_MINIX || MKFS_MINIX help If you wish to be able to create version 2 minix filesystems, enable this. If you enabled 'mkfs_minix' then you almost certainly want to be using the version 2 filesystem support. config MKFS_REISER bool "mkfs_reiser" default n select PLATFORM_LINUX help Utility to create ReiserFS filesystems. Note: this applet needs a lot of testing and polishing. config MKFS_VFAT bool "mkfs_vfat" default y select PLATFORM_LINUX help Utility to create FAT32 filesystems. config GETOPT bool "getopt" default y help The getopt utility is used to break up (parse) options in command lines to make it easy to write complex shell scripts that also check for legal (and illegal) options. If you want to write horribly complex shell scripts, or use some horribly complex shell script written by others, this utility may be for you. Most people will wisely leave this disabled. config FEATURE_GETOPT_LONG bool "Support option -l" default y if LONG_OPTS depends on GETOPT help Enable support for long options (option -l). config HEXDUMP bool "hexdump" default y help The hexdump utility is used to display binary data in a readable way that is comparable to the output from most hex editors. config FEATURE_HEXDUMP_REVERSE bool "Support -R, reverse of 'hexdump -Cv'" default y depends on HEXDUMP help The hexdump utility is used to display binary data in an ascii readable way. This option creates binary data from an ascii input. NB: this option is non-standard. It's unwise to use it in scripts aimed to be portable. config HD bool "hd" default y depends on HEXDUMP help hd is an alias to hexdump -C. config HWCLOCK bool "hwclock" default y select PLATFORM_LINUX help The hwclock utility is used to read and set the hardware clock on a system. This is primarily used to set the current time on shutdown in the hardware clock, so the hardware will keep the correct time when Linux is _not_ running. config FEATURE_HWCLOCK_LONG_OPTIONS bool "Support long options (--hctosys,...)" default y depends on HWCLOCK && LONG_OPTS help By default, the hwclock utility only uses short options. If you are overly fond of its long options, such as --hctosys, --utc, etc) then enable this option. config FEATURE_HWCLOCK_ADJTIME_FHS bool "Use FHS /var/lib/hwclock/adjtime" default n # util-linux-ng in Fedora 13 still uses /etc/adjtime depends on HWCLOCK help Starting with FHS 2.3, the adjtime state file is supposed to exist at /var/lib/hwclock/adjtime instead of /etc/adjtime. If you wish to use the FHS behavior, answer Y here, otherwise answer N for the classic /etc/adjtime path. pathname.com/fhs/pub/fhs-2.3.html#VARLIBHWCLOCKSTATEDIRECTORYFORHWCLO config IPCRM bool "ipcrm" default y help The ipcrm utility allows the removal of System V interprocess communication (IPC) objects and the associated data structures from the system. config IPCS bool "ipcs" default y select PLATFORM_LINUX help The ipcs utility is used to provide information on the currently allocated System V interprocess (IPC) objects in the system. config LOSETUP bool "losetup" default y select PLATFORM_LINUX help losetup is used to associate or detach a loop device with a regular file or block device, and to query the status of a loop device. This version does not currently support enabling data encryption. config LSPCI bool "lspci" default y #select PLATFORM_LINUX help lspci is a utility for displaying information about PCI buses in the system and devices connected to them. This version uses sysfs (/sys/bus/pci/devices) only. config LSUSB bool "lsusb" default y #select PLATFORM_LINUX help lsusb is a utility for displaying information about USB buses in the system and devices connected to them. This version uses sysfs (/sys/bus/usb/devices) only. config MKSWAP bool "mkswap" default y help The mkswap utility is used to configure a file or disk partition as Linux swap space. This allows Linux to use the entire file or partition as if it were additional RAM, which can greatly increase the capability of low-memory machines. This additional memory is much slower than real RAM, but can be very helpful at preventing your applications being killed by the Linux out of memory (OOM) killer. Once you have created swap space using 'mkswap' you need to enable the swap space using the 'swapon' utility. config FEATURE_MKSWAP_UUID bool "UUID support" default y depends on MKSWAP help Generate swap spaces with universally unique identifiers. config MORE bool "more" default y help more is a simple utility which allows you to read text one screen sized page at a time. If you want to read text that is larger than the screen, and you are using anything faster than a 300 baud modem, you will probably find this utility very helpful. If you don't have any need to reading text files, you can leave this disabled. config MOUNT bool "mount" default y select PLATFORM_LINUX help All files and filesystems in Unix are arranged into one big directory tree. The 'mount' utility is used to graft a filesystem onto a particular part of the tree. A filesystem can either live on a block device, or it can be accessible over the network, as is the case with NFS filesystems. Most people using BusyBox will also want to enable the 'mount' utility. config FEATURE_MOUNT_FAKE bool "Support option -f" default y depends on MOUNT help Enable support for faking a file system mount. config FEATURE_MOUNT_VERBOSE bool "Support option -v" default y depends on MOUNT help Enable multi-level -v[vv...] verbose messages. Useful if you debug mount problems and want to see what is exactly passed to the kernel. config FEATURE_MOUNT_HELPERS bool "Support mount helpers" default n depends on MOUNT help Enable mounting of virtual file systems via external helpers. E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call "obexfs -b00.11.22.33.44.55 /mnt" Also "mount -t sometype [-o opts] fs /mnt" will try "sometype [-o opts] fs /mnt" if simple mount syscall fails. The idea is to use such virtual filesystems in /etc/fstab. config FEATURE_MOUNT_LABEL bool "Support specifying devices by label or UUID" default y depends on MOUNT select VOLUMEID help This allows for specifying a device by label or uuid, rather than by name. This feature utilizes the same functionality as blkid/findfs. This also enables label or uuid support for swapon. config FEATURE_MOUNT_NFS bool "Support mounting NFS file systems on Linux < 2.6.23" default n depends on MOUNT select FEATURE_HAVE_RPC select FEATURE_SYSLOG help Enable mounting of NFS file systems on Linux kernels prior to version 2.6.23. Note that in this case mounting of NFS over IPv6 will not be possible. Note that this option links in RPC support from libc, which is rather large (~10 kbytes on uclibc). config FEATURE_MOUNT_CIFS bool "Support mounting CIFS/SMB file systems" default y depends on MOUNT help Enable support for samba mounts. config FEATURE_MOUNT_FLAGS depends on MOUNT bool "Support lots of -o flags in mount" default y help Without this, mount only supports ro/rw/remount. With this, it supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime, noatime, diratime, nodiratime, loud, bind, move, shared, slave, private, unbindable, rshared, rslave, rprivate, and runbindable. config FEATURE_MOUNT_FSTAB depends on MOUNT bool "Support /etc/fstab and -a" default y help Support mount all and looking for files in /etc/fstab. config PIVOT_ROOT bool "pivot_root" default y select PLATFORM_LINUX help The pivot_root utility swaps the mount points for the root filesystem with some other mounted filesystem. This allows you to do all sorts of wild and crazy things with your Linux system and is far more powerful than 'chroot'. Note: This is for initrd in linux 2.4. Under initramfs (introduced in linux 2.6) use switch_root instead. config RDATE bool "rdate" default y help The rdate utility allows you to synchronize the date and time of your system clock with the date and time of a remote networked system using the RFC868 protocol, which is built into the inetd daemon on most systems. config RDEV bool "rdev" default y help Print the device node associated with the filesystem mounted at '/'. config READPROFILE bool "readprofile" default y #select PLATFORM_LINUX help This allows you to parse /proc/profile for basic profiling. config RTCWAKE bool "rtcwake" default y select PLATFORM_LINUX help Enter a system sleep state until specified wakeup time. config SCRIPT bool "script" default y help The script makes typescript of terminal session. config SCRIPTREPLAY bool "scriptreplay" default y help This program replays a typescript, using timing information given by script -t. config SETARCH bool "setarch" default y select PLATFORM_LINUX help The linux32 utility is used to create a 32bit environment for the specified program (usually a shell). It only makes sense to have this util on a system that supports both 64bit and 32bit userland (like amd64/x86, ppc64/ppc, sparc64/sparc, etc...). config SWAPONOFF bool "swaponoff" default y select PLATFORM_LINUX help This option enables both the 'swapon' and the 'swapoff' utilities. Once you have created some swap space using 'mkswap', you also need to enable your swap space with the 'swapon' utility. The 'swapoff' utility is used, typically at system shutdown, to disable any swap space. If you are not using any swap space, you can leave this option disabled. config FEATURE_SWAPON_PRI bool "Support priority option -p" default y depends on SWAPONOFF help Enable support for setting swap device priority in swapon. config SWITCH_ROOT bool "switch_root" default y select PLATFORM_LINUX help The switch_root utility is used from initramfs to select a new root device. Under initramfs, you have to use this instead of pivot_root. (Stop reading here if you don't care why.) Booting with initramfs extracts a gzipped cpio archive into rootfs (which is a variant of ramfs/tmpfs). Because rootfs can't be moved or unmounted*, pivot_root will not work from initramfs. Instead, switch_root deletes everything out of rootfs (including itself), does a mount --move that overmounts rootfs with the new root, and then execs the specified init program. * Because the Linux kernel uses rootfs internally as the starting and ending point for searching through the kernel's doubly linked list of active mount points. That's why. config UMOUNT bool "umount" default y select PLATFORM_LINUX help When you want to remove a mounted filesystem from its current mount point, for example when you are shutting down the system, the 'umount' utility is the tool to use. If you enabled the 'mount' utility, you almost certainly also want to enable 'umount'. config FEATURE_UMOUNT_ALL bool "Support option -a" default y depends on UMOUNT help Support -a option to unmount all currently mounted filesystems. comment "Common options for mount/umount" depends on MOUNT || UMOUNT config FEATURE_MOUNT_LOOP bool "Support loopback mounts" default y depends on MOUNT || UMOUNT help Enabling this feature allows automatic mounting of files (containing filesystem images) via the linux kernel's loopback devices. The mount command will detect you are trying to mount a file instead of a block device, and transparently associate the file with a loopback device. The umount command will also free that loopback device. You can still use the 'losetup' utility (to manually associate files with loop devices) if you need to do something advanced, such as specify an offset or cryptographic options to the loopback device. (If you don't want umount to free the loop device, use "umount -D".) config FEATURE_MOUNT_LOOP_CREATE bool "Create new loopback devices if needed" default y depends on FEATURE_MOUNT_LOOP help Linux kernels >= 2.6.24 support unlimited loopback devices. They are allocated for use when trying to use a loop device. The loop device must however exist. This feature lets mount to try to create next /dev/loopN device if it does not find a free one. config FEATURE_MTAB_SUPPORT bool "Support for the old /etc/mtab file" default n depends on MOUNT || UMOUNT select FEATURE_MOUNT_FAKE help Historically, Unix systems kept track of the currently mounted partitions in the file "/etc/mtab". These days, the kernel exports the list of currently mounted partitions in "/proc/mounts", rendering the old mtab file obsolete. (In modern systems, /etc/mtab should be a symlink to /proc/mounts.) The only reason to have mount maintain an /etc/mtab file itself is if your stripped-down embedded system does not have a /proc directory. If you must use this, keep in mind it's inherently brittle (for example a mount under chroot won't update it), can't handle modern features like separate per-process filesystem namespaces, requires that your /etc directory be writable, tends to get easily confused by --bind or --move mounts, won't update if you rename a directory that contains a mount point, and so on. (In brief: avoid.) About the only reason to use this is if you've removed /proc from your kernel. source util-linux/volume_id/Config.in endmenu ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/script.c������������������������������������������������������������������0000644�0000000�0000000�00000013104�12263563520�015553� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * script implementation for busybox * * pascal.bellard@ads-lu.com * * Based on code from util-linux v 2.12r * Copyright (c) 1980 * The Regents of the University of California. All rights reserved. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define script_trivial_usage //usage: "[-afq" IF_SCRIPTREPLAY("t") "] [-c PROG] [OUTFILE]" //usage:#define script_full_usage "\n\n" //usage: " -a Append output" //usage: "\n -c PROG Run PROG, not shell" //usage: "\n -f Flush output after each write" //usage: "\n -q Quiet" //usage: IF_SCRIPTREPLAY( //usage: "\n -t Send timing to stderr" //usage: ) #include "libbb.h" int script_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int script_main(int argc UNUSED_PARAM, char **argv) { int opt; int mode; int child_pid; int attr_ok; /* NB: 0: ok */ int winsz_ok; int pty; char pty_line[GETPTY_BUFSIZE]; struct termios tt, rtt; struct winsize win; const char *fname = "typescript"; const char *shell; char shell_opt[] = "-i"; char *shell_arg = NULL; enum { OPT_a = (1 << 0), OPT_c = (1 << 1), OPT_f = (1 << 2), OPT_q = (1 << 3), OPT_t = (1 << 4), }; #if ENABLE_LONG_OPTS static const char getopt_longopts[] ALIGN1 = "append\0" No_argument "a" "command\0" Required_argument "c" "flush\0" No_argument "f" "quiet\0" No_argument "q" IF_SCRIPTREPLAY("timing\0" No_argument "t") ; applet_long_options = getopt_longopts; #endif opt_complementary = "?1"; /* max one arg */ opt = getopt32(argv, "ac:fq" IF_SCRIPTREPLAY("t") , &shell_arg); //argc -= optind; argv += optind; if (argv[0]) { fname = argv[0]; } mode = O_CREAT|O_TRUNC|O_WRONLY; if (opt & OPT_a) { mode = O_CREAT|O_APPEND|O_WRONLY; } if (opt & OPT_c) { shell_opt[1] = 'c'; } if (!(opt & OPT_q)) { printf("Script started, file is %s\n", fname); } shell = get_shell_name(); pty = xgetpty(pty_line); /* get current stdin's tty params */ attr_ok = tcgetattr(0, &tt); winsz_ok = ioctl(0, TIOCGWINSZ, (char *)&win); rtt = tt; cfmakeraw(&rtt); rtt.c_lflag &= ~ECHO; tcsetattr(0, TCSAFLUSH, &rtt); /* "script" from util-linux exits when child exits, * we wouldn't wait for EOF from slave pty * (output may be produced by grandchildren of child) */ signal(SIGCHLD, record_signo); /* TODO: SIGWINCH? pass window size changes down to slave? */ child_pid = xvfork(); if (child_pid) { /* parent */ #define buf bb_common_bufsiz1 struct pollfd pfd[2]; int outfd, count, loop; double oldtime = ENABLE_SCRIPTREPLAY ? time(NULL) : 0; smallint fd_count = 2; outfd = xopen(fname, mode); pfd[0].fd = pty; pfd[0].events = POLLIN; pfd[1].fd = STDIN_FILENO; pfd[1].events = POLLIN; ndelay_on(pty); /* this descriptor is not shared, can do this */ /* ndelay_on(STDIN_FILENO); - NO, stdin can be shared! Pity :( */ /* copy stdin to pty master input, * copy pty master output to stdout and file */ /* TODO: don't use full_write's, use proper write buffering */ while (fd_count && !bb_got_signal) { /* not safe_poll! we want SIGCHLD to EINTR poll */ if (poll(pfd, fd_count, -1) < 0 && errno != EINTR) { /* If child exits too quickly, we may get EIO: * for example, try "script -c true" */ break; } if (pfd[0].revents) { errno = 0; count = safe_read(pty, buf, sizeof(buf)); if (count <= 0 && errno != EAGAIN) { /* err/eof from pty: exit */ goto restore; } if (count > 0) { if (ENABLE_SCRIPTREPLAY && (opt & OPT_t)) { struct timeval tv; double newtime; gettimeofday(&tv, NULL); newtime = tv.tv_sec + (double) tv.tv_usec / 1000000; fprintf(stderr, "%f %u\n", newtime - oldtime, count); oldtime = newtime; } full_write(STDOUT_FILENO, buf, count); full_write(outfd, buf, count); if (opt & OPT_f) { fsync(outfd); } } } if (pfd[1].revents) { count = safe_read(STDIN_FILENO, buf, sizeof(buf)); if (count <= 0) { /* err/eof from stdin: don't read stdin anymore */ pfd[1].revents = 0; fd_count--; } else { full_write(pty, buf, count); } } } /* If loop was exited because SIGCHLD handler set bb_got_signal, * there still can be some buffered output. But dont loop forever: * we won't pump orphaned grandchildren's output indefinitely. * Testcase: running this in script: * exec dd if=/dev/zero bs=1M count=1 * must have "1+0 records in, 1+0 records out" captured too. * (util-linux's script doesn't do this. buggy :) */ loop = 999; /* pty is in O_NONBLOCK mode, we exit as soon as buffer is empty */ while (--loop && (count = safe_read(pty, buf, sizeof(buf))) > 0) { full_write(STDOUT_FILENO, buf, count); full_write(outfd, buf, count); } restore: if (attr_ok == 0) tcsetattr(0, TCSAFLUSH, &tt); if (!(opt & OPT_q)) printf("Script done, file is %s\n", fname); return EXIT_SUCCESS; } /* child: make pty slave to be input, output, error; run shell */ close(pty); /* close pty master */ /* open pty slave to fd 0,1,2 */ close(0); xopen(pty_line, O_RDWR); /* uses fd 0 */ xdup2(0, 1); xdup2(0, 2); /* copy our original stdin tty's parameters to pty */ if (attr_ok == 0) tcsetattr(0, TCSAFLUSH, &tt); if (winsz_ok == 0) ioctl(0, TIOCSWINSZ, (char *)&win); /* set pty as a controlling tty */ setsid(); ioctl(0, TIOCSCTTY, 0 /* 0: don't forcibly steal */); /* Non-ignored signals revert to SIG_DFL on exec anyway */ /*signal(SIGCHLD, SIG_DFL);*/ execl(shell, shell, shell_opt, shell_arg, (char *) NULL); bb_simple_perror_msg_and_die(shell); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/hwclock.c�����������������������������������������������������������������0000644�0000000�0000000�00000022166�12263563520�015711� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Mini hwclock implementation for busybox * * Copyright (C) 2002 Robert Griebl <griebl@gmx.de> * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" /* After libbb.h, since it needs sys/types.h on some systems */ #include <sys/utsname.h> #include "rtc_.h" /* diff code is disabled: it's not sys/hw clock diff, it's some useless * "time between hwclock was started and we saw CMOS tick" quantity. * It's useless since hwclock is started at a random moment, * thus the quantity is also random, useless. Showing 0.000000 does not * deprive us from any useful info. * * SHOW_HWCLOCK_DIFF code in this file shows the difference between system * and hw clock. It is useful, but not compatible with standard hwclock. * Thus disabled. */ #define SHOW_HWCLOCK_DIFF 0 #if !SHOW_HWCLOCK_DIFF # define read_rtc(pp_rtcname, sys_tv, utc) read_rtc(pp_rtcname, utc) #endif static time_t read_rtc(const char **pp_rtcname, struct timeval *sys_tv, int utc) { struct tm tm_time; int fd; fd = rtc_xopen(pp_rtcname, O_RDONLY); rtc_read_tm(&tm_time, fd); #if SHOW_HWCLOCK_DIFF { int before = tm_time.tm_sec; while (1) { rtc_read_tm(&tm_time, fd); gettimeofday(sys_tv, NULL); if (before != (int)tm_time.tm_sec) break; } } #endif if (ENABLE_FEATURE_CLEAN_UP) close(fd); return rtc_tm2time(&tm_time, utc); } static void show_clock(const char **pp_rtcname, int utc) { #if SHOW_HWCLOCK_DIFF struct timeval sys_tv; #endif time_t t = read_rtc(pp_rtcname, &sys_tv, utc); #if ENABLE_LOCALE_SUPPORT /* Standard hwclock uses locale-specific output format */ char cp[64]; struct tm *ptm = localtime(&t); strftime(cp, sizeof(cp), "%c", ptm); #else char *cp = ctime(&t); strchrnul(cp, '\n')[0] = '\0'; #endif #if !SHOW_HWCLOCK_DIFF printf("%s 0.000000 seconds\n", cp); #else { long diff = sys_tv.tv_sec - t; if (diff < 0 /*&& tv.tv_usec != 0*/) { /* Why we need diff++? */ /* diff >= 0 is ok: | diff < 0, can't just use tv.tv_usec: */ /* 45.520820 | 43.520820 */ /* - 44.000000 | - 45.000000 */ /* = 1.520820 | = -1.479180, not -2.520820! */ diff++; /* Should be 1000000 - tv.tv_usec, but then we must check tv.tv_usec != 0 */ sys_tv.tv_usec = 999999 - sys_tv.tv_usec; } printf("%s %ld.%06lu seconds\n", cp, diff, (unsigned long)sys_tv.tv_usec); } #endif } static void to_sys_clock(const char **pp_rtcname, int utc) { struct timeval tv; struct timezone tz; tz.tz_minuteswest = timezone/60 - 60*daylight; tz.tz_dsttime = 0; tv.tv_sec = read_rtc(pp_rtcname, NULL, utc); tv.tv_usec = 0; if (settimeofday(&tv, &tz)) bb_perror_msg_and_die("settimeofday"); } static void from_sys_clock(const char **pp_rtcname, int utc) { #if 1 struct timeval tv; struct tm tm_time; int rtc; rtc = rtc_xopen(pp_rtcname, O_WRONLY); gettimeofday(&tv, NULL); /* Prepare tm_time */ if (sizeof(time_t) == sizeof(tv.tv_sec)) { if (utc) gmtime_r((time_t*)&tv.tv_sec, &tm_time); else localtime_r((time_t*)&tv.tv_sec, &tm_time); } else { time_t t = tv.tv_sec; if (utc) gmtime_r(&t, &tm_time); else localtime_r(&t, &tm_time); } #else /* Bloated code which tries to set hw clock with better precision. * On x86, even though code does set hw clock within <1ms of exact * whole seconds, apparently hw clock (at least on some machines) * doesn't reset internal fractional seconds to 0, * making all this a pointless excercise. */ /* If we see that we are N usec away from whole second, * we'll sleep for N-ADJ usecs. ADJ corrects for the fact * that CPU is not infinitely fast. * On infinitely fast CPU, next wakeup would be * on (exactly_next_whole_second - ADJ). On real CPUs, * this difference between current time and whole second * is less than ADJ (assuming system isn't heavily loaded). */ /* Small value of 256us gives very precise sync for 2+ GHz CPUs. * Slower CPUs will fail to sync and will go to bigger * ADJ values. qemu-emulated armv4tl with ~100 MHz * performance ends up using ADJ ~= 4*1024 and it takes * 2+ secs (2 tries with successively larger ADJ) * to sync. Even straced one on the same qemu (very slow) * takes only 4 tries. */ #define TWEAK_USEC 256 unsigned adj = TWEAK_USEC; struct tm tm_time; struct timeval tv; int rtc = rtc_xopen(pp_rtcname, O_WRONLY); /* Try to catch the moment when whole second is close */ while (1) { unsigned rem_usec; time_t t; gettimeofday(&tv, NULL); t = tv.tv_sec; rem_usec = 1000000 - tv.tv_usec; if (rem_usec < adj) { /* Close enough */ small_rem: t++; } /* Prepare tm_time from t */ if (utc) gmtime_r(&t, &tm_time); /* may read /etc/xxx (it takes time) */ else localtime_r(&t, &tm_time); /* same */ if (adj >= 32*1024) { break; /* 32 ms diff and still no luck?? give up trying to sync */ } /* gmtime/localtime took some time, re-get cur time */ gettimeofday(&tv, NULL); if (tv.tv_sec < t /* we are still in old second */ || (tv.tv_sec == t && tv.tv_usec < adj) /* not too far into next second */ ) { break; /* good, we are in sync! */ } rem_usec = 1000000 - tv.tv_usec; if (rem_usec < adj) { t = tv.tv_sec; goto small_rem; /* already close to next sec, don't sleep */ } /* Try to sync up by sleeping */ usleep(rem_usec - adj); /* Jump to 1ms diff, then increase fast (x2): EVERY loop * takes ~1 sec, people won't like slowly converging code here! */ //bb_error_msg("adj:%d tv.tv_usec:%d", adj, (int)tv.tv_usec); if (adj < 512) adj = 512; /* ... and if last "overshoot" does not look insanely big, * just use it as adj increment. This makes convergence faster. */ if (tv.tv_usec < adj * 8) { adj += tv.tv_usec; continue; } adj *= 2; } /* Debug aid to find "optimal" TWEAK_USEC with nearly exact sync. * Look for a value which makes tv_usec close to 999999 or 0. * For 2.20GHz Intel Core 2: optimal TWEAK_USEC ~= 200 */ //bb_error_msg("tv.tv_usec:%d", (int)tv.tv_usec); #endif tm_time.tm_isdst = 0; xioctl(rtc, RTC_SET_TIME, &tm_time); if (ENABLE_FEATURE_CLEAN_UP) close(rtc); } /* * At system boot, kernel may set system time from RTC, * but it knows nothing about timezones. If RTC is in local time, * then system time is wrong - it is offset by timezone. * This option corrects system time if RTC is in local time, * and (always) sets in-kernel timezone. * * This is an alternate option to --hctosys that does not read the * hardware clock. */ static void set_system_clock_timezone(int utc) { struct timeval tv; struct tm *broken; struct timezone tz; gettimeofday(&tv, NULL); broken = localtime(&tv.tv_sec); tz.tz_minuteswest = timezone / 60; if (broken->tm_isdst) tz.tz_minuteswest -= 60; tz.tz_dsttime = 0; gettimeofday(&tv, NULL); if (!utc) tv.tv_sec += tz.tz_minuteswest * 60; if (settimeofday(&tv, &tz)) bb_perror_msg_and_die("settimeofday"); } //usage:#define hwclock_trivial_usage //usage: IF_FEATURE_HWCLOCK_LONG_OPTIONS( //usage: "[-r|--show] [-s|--hctosys] [-w|--systohc] [-t|--systz]" //usage: " [-l|--localtime] [-u|--utc]" //usage: " [-f|--rtc FILE]" //usage: ) //usage: IF_NOT_FEATURE_HWCLOCK_LONG_OPTIONS( //usage: "[-r] [-s] [-w] [-t] [-l] [-u] [-f FILE]" //usage: ) //usage:#define hwclock_full_usage "\n\n" //usage: "Query and set hardware clock (RTC)\n" //usage: "\n -r Show hardware clock time" //usage: "\n -s Set system time from hardware clock" //usage: "\n -w Set hardware clock from system time" //usage: "\n -t Set in-kernel timezone, correct system time" //usage: "\n if hardware clock is in local time" //usage: "\n -u Assume hardware clock is kept in UTC" //usage: "\n -l Assume hardware clock is kept in local time" //usage: "\n -f FILE Use specified device (e.g. /dev/rtc2)" #define HWCLOCK_OPT_LOCALTIME 0x01 #define HWCLOCK_OPT_UTC 0x02 #define HWCLOCK_OPT_SHOW 0x04 #define HWCLOCK_OPT_HCTOSYS 0x08 #define HWCLOCK_OPT_SYSTOHC 0x10 #define HWCLOCK_OPT_SYSTZ 0x20 #define HWCLOCK_OPT_RTCFILE 0x40 int hwclock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int hwclock_main(int argc UNUSED_PARAM, char **argv) { const char *rtcname = NULL; unsigned opt; int utc; #if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS static const char hwclock_longopts[] ALIGN1 = "localtime\0" No_argument "l" /* short opt is non-standard */ "utc\0" No_argument "u" "show\0" No_argument "r" "hctosys\0" No_argument "s" "systohc\0" No_argument "w" "systz\0" No_argument "t" /* short opt is non-standard */ "rtc\0" Required_argument "f" ; applet_long_options = hwclock_longopts; #endif opt_complementary = "r--wst:w--rst:s--wrt:t--rsw:l--u:u--l"; opt = getopt32(argv, "lurswtf:", &rtcname); /* If -u or -l wasn't given check if we are using utc */ if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME)) utc = (opt & HWCLOCK_OPT_UTC); else utc = rtc_adjtime_is_utc(); if (opt & HWCLOCK_OPT_HCTOSYS) to_sys_clock(&rtcname, utc); else if (opt & HWCLOCK_OPT_SYSTOHC) from_sys_clock(&rtcname, utc); else if (opt & HWCLOCK_OPT_SYSTZ) set_system_clock_timezone(utc); else /* default HWCLOCK_OPT_SHOW */ show_clock(&rtcname, utc); return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/mkfs_reiser.c�������������������������������������������������������������0000644�0000000�0000000�00000032541�12263563520�016566� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * mkfs_reiser: utility to create ReiserFS filesystem * * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define mkfs_reiser_trivial_usage //usage: "[-f] [-l LABEL] BLOCKDEV [4K-BLOCKS]" //usage:#define mkfs_reiser_full_usage "\n\n" //usage: "Make a ReiserFS V3 filesystem\n" //usage: "\n -f Force" //usage: "\n -l LBL Volume label" #include "libbb.h" #include <linux/fs.h> char BUG_wrong_field_size(void); #define STORE_LE(field, value) \ do { \ if (sizeof(field) == 4) \ field = SWAP_LE32(value); \ else if (sizeof(field) == 2) \ field = SWAP_LE16(value); \ else if (sizeof(field) == 1) \ field = (value); \ else \ BUG_wrong_field_size(); \ } while (0) #define FETCH_LE32(field) \ (sizeof(field) == 4 ? SWAP_LE32(field) : BUG_wrong_field_size()) struct journal_params { uint32_t jp_journal_1st_block; /* where does journal start from on its device */ uint32_t jp_journal_dev; /* journal device st_rdev */ uint32_t jp_journal_size; /* size of the journal on FS creation. used to make sure they don't overflow it */ uint32_t jp_journal_trans_max; /* max number of blocks in a transaction. */ uint32_t jp_journal_magic; /* random value made on fs creation (this was sb_journal_block_count) */ uint32_t jp_journal_max_batch; /* max number of blocks to batch into a trans */ uint32_t jp_journal_max_commit_age; /* in seconds, how old can an async commit be */ uint32_t jp_journal_max_trans_age; /* in seconds, how old can a transaction be */ }; struct reiserfs_journal_header { uint32_t jh_last_flush_trans_id; /* id of last fully flushed transaction */ uint32_t jh_first_unflushed_offset; /* offset in the log of where to start replay after a crash */ uint32_t jh_mount_id; struct journal_params jh_journal; uint32_t jh_last_check_mount_id; /* the mount id of the fs during the last reiserfsck --check. */ }; struct reiserfs_super_block { uint32_t sb_block_count; /* 0 number of block on data device */ uint32_t sb_free_blocks; /* 4 free blocks count */ uint32_t sb_root_block; /* 8 root of the tree */ struct journal_params sb_journal; /* 12 */ uint16_t sb_blocksize; /* 44 */ uint16_t sb_oid_maxsize; /* 46 max size of object id array, see get_objectid() commentary */ uint16_t sb_oid_cursize; /* 48 current size of object id array */ uint16_t sb_umount_state; /* 50 this is set to 1 when filesystem was umounted, to 2 - when not */ char s_magic[10]; /* 52 "ReIsErFs" or "ReIsEr2Fs" or "ReIsEr3Fs" */ uint16_t sb_fs_state; /* 62 it is set to used by fsck to mark which phase of rebuilding is done (used for fsck debugging) */ uint32_t sb_hash_function_code; /* 64 code of fuction which was/is/will be used to sort names in a directory. See codes in above */ uint16_t sb_tree_height; /* 68 height of filesytem tree. Tree consisting of only one root block has 2 here */ uint16_t sb_bmap_nr; /* 70 amount of bitmap blocks needed to address each block of file system */ uint16_t sb_version; /* 72 this field is only reliable on filesystem with non-standard journal */ uint16_t sb_reserved_for_journal; /* 74 size in blocks of journal area on main device, we need to keep after non-standard journal relocation */ uint32_t sb_inode_generation; /* 76 */ uint32_t sb_flags; /* 80 Right now used only by inode-attributes, if enabled */ unsigned char s_uuid[16]; /* 84 filesystem unique identifier */ unsigned char s_label[16]; /* 100 filesystem volume label */ uint16_t sb_mnt_count; /* 116 */ uint16_t sb_max_mnt_count; /* 118 */ uint32_t sb_lastcheck; /* 120 */ uint32_t sb_check_interval; /* 124 */ /* zero filled by mkreiserfs and reiserfs_convert_objectid_map_v1() so any additions must be updated there as well. */ char s_unused[76]; /* 128 */ /* 204 */ }; /* Header of a disk block. More precisely, header of a formatted leaf or internal node, and not the header of an unformatted node. */ struct block_head { uint16_t blk2_level; /* Level of a block in the tree. */ uint16_t blk2_nr_item; /* Number of keys/items in a block. */ uint16_t blk2_free_space; /* Block free space in bytes. */ uint16_t blk_reserved; uint32_t reserved[4]; }; #define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024) #define REISERFS_3_6_SUPER_MAGIC_STRING "ReIsEr2Fs" #define REISERFS_FORMAT_3_6 2 #define DEFAULT_MAX_MNT_COUNT 30 /* 30 mounts */ #define DEFAULT_CHECK_INTERVAL (180 * 60 * 60 * 24) /* 180 days */ #define FS_CLEANLY_UMOUNTED 1 /* this was REISERFS_VALID_FS */ #define JOURNAL_MIN_SIZE 512 /* biggest possible single transaction, don't change for now (8/3/99) */ #define JOURNAL_TRANS_MAX 1024 #define JOURNAL_TRANS_MIN 256 /* need to check whether it works */ #define JOURNAL_DEFAULT_RATIO 8 /* default journal size / max trans length */ #define JOURNAL_MIN_RATIO 2 /* max blocks to batch into one transaction, don't make this any bigger than 900 */ #define JOURNAL_MAX_BATCH 900 #define JOURNAL_MAX_COMMIT_AGE 30 // Standard mkreiserfs 3.6.21: // -b | --block-size N size of file-system block, in bytes // -j | --journal-device FILE path to separate device to hold journal // -s | --journal-size N size of the journal in blocks // -o | --journal-offset N offset of the journal from the start of // the separate device, in blocks // -t | --transaction-max-size N maximal size of transaction, in blocks // -B | --badblocks file store all bad blocks given in file on the fs // -h | --hash rupasov|tea|r5 hash function to use by default // -u | --uuid UUID store UUID in the superblock // -l | --label LABEL store LABEL in the superblock // --format 3.5|3.6 old 3.5 format or newer 3.6 // -f | --force specified once, make mkreiserfs the whole // disk, not block device or mounted partition; // specified twice, do not ask for confirmation // -q | --quiet quiet work without messages, progress and // questions. Useful if run in a script. For use // by end users only. // -d | --debug print debugging information during mkreiser // -V print version and exit // Options not commented below are taken but silently ignored: enum { OPT_b = 1 << 0, OPT_j = 1 << 1, OPT_s = 1 << 2, OPT_o = 1 << 3, OPT_t = 1 << 4, OPT_B = 1 << 5, OPT_h = 1 << 6, OPT_u = 1 << 7, OPT_l = 1 << 8, // label OPT_f = 1 << 9, // ask no questions OPT_q = 1 << 10, OPT_d = 1 << 11, //OPT_V = 1 << 12, // -V version. bbox applets don't support that }; int mkfs_reiser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mkfs_reiser_main(int argc UNUSED_PARAM, char **argv) { unsigned blocksize = 4096; unsigned journal_blocks = 8192; unsigned blocks, bitmap_blocks, i, block; time_t timestamp; const char *label = ""; struct stat st; int fd; uint8_t *buf; struct reiserfs_super_block *sb; struct journal_params *jp; struct block_head *root; // using global "option_mask32" instead of local "opts": // we are register starved here opt_complementary = "-1:b+"; /*opts =*/ getopt32(argv, "b:j:s:o:t:B:h:u:l:fqd", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &label); argv += optind; // argv[0] -- device // check the device is a block device fd = xopen(argv[0], O_WRONLY | O_EXCL); xfstat(fd, &st, argv[0]); if (!S_ISBLK(st.st_mode) && !(option_mask32 & OPT_f)) bb_error_msg_and_die("%s: not a block device", argv[0]); // check if it is mounted // N.B. what if we format a file? find_mount_point will return false negative since // it is loop block device which is mounted! if (find_mount_point(argv[0], 0)) bb_error_msg_and_die("can't format mounted filesystem"); // open the device, get size in blocks blocks = get_volume_size_in_bytes(fd, argv[1], blocksize, /*extend:*/ 1) / blocksize; // block number sanity check // we have a limit: skipped area, super block, journal and root block // all have to be addressed by one first bitmap block = REISERFS_DISK_OFFSET_IN_BYTES / blocksize // boot area + 1 // sb + 1 // bitmap#0 + journal_blocks+1 // journal ; // count overhead bitmap_blocks = (blocks - 1) / (blocksize * 8) + 1; i = block + bitmap_blocks; // check overhead if (MIN(blocksize * 8, blocks) < i) bb_error_msg_and_die("need >= %u blocks", i); // ask confirmation? // TODO: ??? // wipe out first REISERFS_DISK_OFFSET_IN_BYTES of device // TODO: do we really need to wipe?! xlseek(fd, REISERFS_DISK_OFFSET_IN_BYTES, SEEK_SET); // fill superblock sb = (struct reiserfs_super_block *)xzalloc(blocksize); // block count STORE_LE(sb->sb_block_count, blocks); STORE_LE(sb->sb_free_blocks, blocks - i); // TODO: decypher! STORE_LE(sb->sb_root_block, block); // fill journal related fields jp = &sb->sb_journal; STORE_LE(jp->jp_journal_1st_block, REISERFS_DISK_OFFSET_IN_BYTES / blocksize + 1/*sb*/ + 1/*bmp#0*/); timestamp = time(NULL); srandom(timestamp); STORE_LE(jp->jp_journal_magic, random()); STORE_LE(jp->jp_journal_size, journal_blocks); STORE_LE(jp->jp_journal_trans_max, JOURNAL_TRANS_MAX); STORE_LE(jp->jp_journal_max_batch, JOURNAL_MAX_BATCH); STORE_LE(jp->jp_journal_max_commit_age, JOURNAL_MAX_COMMIT_AGE); // sizes STORE_LE(sb->sb_blocksize, blocksize); STORE_LE(sb->sb_oid_maxsize, (blocksize - sizeof(*sb)) / sizeof(uint32_t) / 2 * 2); STORE_LE(sb->sb_oid_cursize, 2); // "." and ".." strcpy(sb->s_magic, REISERFS_3_6_SUPER_MAGIC_STRING); STORE_LE(sb->sb_bmap_nr, (bitmap_blocks > ((1LL << 16) - 1)) ? 0 : bitmap_blocks); // misc STORE_LE(sb->sb_version, REISERFS_FORMAT_3_6); STORE_LE(sb->sb_lastcheck, timestamp); STORE_LE(sb->sb_check_interval, DEFAULT_CHECK_INTERVAL); STORE_LE(sb->sb_mnt_count, 1); STORE_LE(sb->sb_max_mnt_count, DEFAULT_MAX_MNT_COUNT); STORE_LE(sb->sb_umount_state, FS_CLEANLY_UMOUNTED); STORE_LE(sb->sb_tree_height, 2); STORE_LE(sb->sb_hash_function_code, 3); // R5_HASH STORE_LE(sb->sb_flags, 1); //STORE_LE(sb->sb_reserved_for_journal, 0); // create UUID generate_uuid(sb->s_uuid); // write the label safe_strncpy((char *)sb->s_label, label, sizeof(sb->s_label)); // TODO: EMPIRIC! ENDIANNESS! // superblock has only 204 bytes. What are these? buf = (uint8_t *)sb; buf[205] = 1; buf[209] = 3; // put superblock xwrite(fd, sb, blocksize); // create bitmaps buf = xzalloc(blocksize); // bitmap #0 uses initial "block"+1 blocks i = block + 1; memset(buf, 0xFF, i / 8); buf[i / 8] = (1 << (i & 7)) - 1; //0..7 => 00000000..01111111 // mark trailing absent blocks, if any if (blocks < 8*blocksize) { unsigned n = 8*blocksize - blocks; i = n / 8; buf[blocksize - i - 1] |= 0x7F00 >> (n & 7); //0..7 => 00000000..11111110 memset(buf + blocksize - i, 0xFF, i); // N.B. no overflow here! } // put bitmap #0 xwrite(fd, buf, blocksize); // now go journal blocks memset(buf, 0, blocksize); for (i = 0; i < journal_blocks; i++) xwrite(fd, buf, blocksize); // dump journal control block memcpy(&((struct reiserfs_journal_header *)buf)->jh_journal, &sb->sb_journal, sizeof(sb->sb_journal)); xwrite(fd, buf, blocksize); // other bitmaps are in every (8*blocksize)-th block // N.B. they use the only block -- namely bitmap itself! buf[0] = 0x01; // put bitmaps for (i = 1; i < bitmap_blocks; i++) { xlseek(fd, i*8*blocksize * blocksize, SEEK_SET); // mark trailing absent blocks, if any if (i == bitmap_blocks - 1 && (blocks % (8*blocksize))) { unsigned n = 8*blocksize - blocks % (8*blocksize); unsigned j = n / 8; buf[blocksize - j - 1] |= 0x7F00 >> (n & 7); //0..7 => 00000000..11111110 memset(buf + blocksize - j, 0xFF, j); // N.B. no overflow here! } xwrite(fd, buf, blocksize); } // fill root block // block head memset(buf, 0, blocksize); root = (struct block_head *)buf; STORE_LE(root->blk2_level, 1); // leaf node STORE_LE(root->blk2_nr_item, 2); // "." and ".." STORE_LE(root->blk2_free_space, blocksize - sizeof(struct block_head)); // item head // root directory // TODO: EMPIRIC! ENDIANNESS! // TODO: indented assignments seem to be timestamps buf[4] = 0134; buf[24] = 01; buf[28] = 02; buf[42] = 054; buf[44] = 0324; buf[45] = 017; buf[46] = 01; buf[48] = 01; buf[52] = 02; buf[56] = 01; buf[60] = 0364; buf[61] = 01; buf[64] = 02; buf[66] = 060; buf[68] = 0244; buf[69] = 017; buf[4004] = 01; buf[4008] = 01; buf[4012] = 02; buf[4016] = 050; buf[4018] = 04; buf[4020] = 02; buf[4028] = 01; buf[4032] = 040; buf[4034] = 04; buf[4036] = 056; buf[4037] = 056; // ".." buf[4044] = 056; // "." buf[4052] = 0355; buf[4053] = 0101; buf[4056] = 03; buf[4060] = 060; buf[4076] = 0173; buf[4077] = 0240; buf[4078] = 0344; buf[4079] = 0112; buf[4080] = 0173; buf[4081] = 0240; buf[4082] = 0344; buf[4083] = 0112; buf[4084] = 0173; buf[4085] = 0240; buf[4086] = 0344; buf[4087] = 0112; buf[4088] = 01; // put root block xlseek(fd, block * blocksize, SEEK_SET); xwrite(fd, buf, blocksize); // cleanup if (ENABLE_FEATURE_CLEAN_UP) { free(buf); free(sb); } xclose(fd); return EXIT_SUCCESS; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/fstrim.c������������������������������������������������������������������0000644�0000000�0000000�00000005736�12263563520�015567� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * fstrim.c - discard the part (or whole) of mounted filesystem. * * 03 March 2012 - Malek Degachi <malek-degachi@laposte.net> * Adapted for busybox from util-linux-2.12a. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //config:config FSTRIM //config: bool "fstrim" //config: default y //config: select PLATFORM_LINUX //config: help //config: Discard unused blocks on a mounted filesystem. //applet:IF_FSTRIM(APPLET(fstrim, BB_DIR_SBIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_FSTRIM) += fstrim.o //usage:#define fstrim_trivial_usage //usage: "[OPTIONS] MOUNTPOINT" //usage:#define fstrim_full_usage "\n\n" //usage: IF_LONG_OPTS( //usage: " -o,--offset=OFFSET Offset in bytes to discard from" //usage: "\n -l,--length=LEN Bytes to discard" //usage: "\n -m,--minimum=MIN Minimum extent length" //usage: "\n -v,--verbose Print number of discarded bytes" //usage: ) //usage: IF_NOT_LONG_OPTS( //usage: " -o OFFSET Offset in bytes to discard from" //usage: "\n -l LEN Bytes to discard" //usage: "\n -m MIN Minimum extent length" //usage: "\n -v, Print number of discarded bytes" //usage: ) #include "libbb.h" #include <linux/fs.h> #ifndef FITRIM struct fstrim_range { uint64_t start; uint64_t len; uint64_t minlen; }; #define FITRIM _IOWR('X', 121, struct fstrim_range) #endif static const struct suffix_mult fstrim_sfx[] = { { "KiB", 1024 }, { "kiB", 1024 }, { "K", 1024 }, { "k", 1024 }, { "MiB", 1048576 }, { "miB", 1048576 }, { "M", 1048576 }, { "m", 1048576 }, { "GiB", 1073741824 }, { "giB", 1073741824 }, { "G", 1073741824 }, { "g", 1073741824 }, { "KB", 1000 }, { "MB", 1000000 }, { "GB", 1000000000 }, { "", 0 } }; int fstrim_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fstrim_main(int argc UNUSED_PARAM, char **argv) { struct fstrim_range range; char *arg_o, *arg_l, *arg_m, *mp; unsigned opts; int fd; enum { OPT_o = (1 << 0), OPT_l = (1 << 1), OPT_m = (1 << 2), OPT_v = (1 << 3), }; #if ENABLE_LONG_OPTS static const char getopt_longopts[] ALIGN1 = "offset\0" Required_argument "o" "length\0" Required_argument "l" "minimum\0" Required_argument "m" "verbose\0" No_argument "v" ; applet_long_options = getopt_longopts; #endif opt_complementary = "=1"; /* exactly one non-option arg: the mountpoint */ opts = getopt32(argv, "o:l:m:v", &arg_o, &arg_l, &arg_m); memset(&range, 0, sizeof(range)); range.len = ULLONG_MAX; if (opts & OPT_o) range.start = xatoull_sfx(arg_o, fstrim_sfx); if (opts & OPT_l) range.len = xatoull_sfx(arg_l, fstrim_sfx); if (opts & OPT_m) range.minlen = xatoull_sfx(arg_m, fstrim_sfx); mp = argv[optind]; if (find_block_device(mp)) { fd = xopen_nonblocking(mp); xioctl(fd, FITRIM, &range); if (ENABLE_FEATURE_CLEAN_UP) close(fd); if (opts & OPT_v) printf("%s: %llu bytes trimmed\n", mp, (unsigned long long)range.len); return EXIT_SUCCESS; } return EXIT_FAILURE; } ����������������������������������busybox-1.22.1/util-linux/Kbuild.src����������������������������������������������������������������0000644�0000000�0000000�00000003463�12263563520�016035� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org> # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT lib-$(CONFIG_ACPID) += acpid.o lib-$(CONFIG_BLKID) += blkid.o lib-$(CONFIG_DMESG) += dmesg.o lib-$(CONFIG_FBSET) += fbset.o lib-$(CONFIG_FDFLUSH) += freeramdisk.o lib-$(CONFIG_FDFORMAT) += fdformat.o lib-$(CONFIG_FDISK) += fdisk.o lib-$(CONFIG_FINDFS) += findfs.o lib-$(CONFIG_FLOCK) += flock.o lib-$(CONFIG_FREERAMDISK) += freeramdisk.o lib-$(CONFIG_FSCK_MINIX) += fsck_minix.o lib-$(CONFIG_GETOPT) += getopt.o lib-$(CONFIG_HEXDUMP) += hexdump.o lib-$(CONFIG_HWCLOCK) += hwclock.o lib-$(CONFIG_IPCRM) += ipcrm.o lib-$(CONFIG_IPCS) += ipcs.o lib-$(CONFIG_LOSETUP) += losetup.o lib-$(CONFIG_LSPCI) += lspci.o lib-$(CONFIG_LSUSB) += lsusb.o lib-$(CONFIG_MKFS_EXT2) += mkfs_ext2.o lib-$(CONFIG_MKFS_MINIX) += mkfs_minix.o lib-$(CONFIG_MKFS_REISER) += mkfs_reiser.o lib-$(CONFIG_MKFS_VFAT) += mkfs_vfat.o lib-$(CONFIG_MKSWAP) += mkswap.o lib-$(CONFIG_MORE) += more.o lib-$(CONFIG_MOUNT) += mount.o lib-$(CONFIG_PIVOT_ROOT) += pivot_root.o lib-$(CONFIG_RDATE) += rdate.o lib-$(CONFIG_RDEV) += rdev.o lib-$(CONFIG_READPROFILE) += readprofile.o lib-$(CONFIG_RTCWAKE) += rtcwake.o lib-$(CONFIG_SCRIPT) += script.o lib-$(CONFIG_SCRIPTREPLAY) += scriptreplay.o lib-$(CONFIG_SETARCH) += setarch.o lib-$(CONFIG_SWAPONOFF) += swaponoff.o lib-$(CONFIG_SWITCH_ROOT) += switch_root.o lib-$(CONFIG_UMOUNT) += umount.o �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/freeramdisk.c�������������������������������������������������������������0000644�0000000�0000000�00000002503�12263563520�016544� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * freeramdisk and fdflush implementations for busybox * * Copyright (C) 2000 and written by Emanuele Caratti <wiz@iol.it> * Adjusted a bit by Erik Andersen <andersen@codepoet.org> * Unified with fdflush by Tito Ragusa <farmatito@tiscali.it> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define freeramdisk_trivial_usage //usage: "DEVICE" //usage:#define freeramdisk_full_usage "\n\n" //usage: "Free all memory used by the specified ramdisk" //usage: //usage:#define freeramdisk_example_usage //usage: "$ freeramdisk /dev/ram2\n" //usage: //usage:#define fdflush_trivial_usage //usage: "DEVICE" //usage:#define fdflush_full_usage "\n\n" //usage: "Force floppy disk drive to detect disk change" #include <sys/mount.h> #include "libbb.h" /* From <linux/fd.h> */ #define FDFLUSH _IO(2,0x4b) int freeramdisk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int freeramdisk_main(int argc UNUSED_PARAM, char **argv) { int fd; fd = xopen(single_argv(argv), O_RDWR); // Act like freeramdisk, fdflush, or both depending on configuration. ioctl_or_perror_and_die(fd, (ENABLE_FREERAMDISK && applet_name[1] == 'r') || !ENABLE_FDFLUSH ? BLKFLSBUF : FDFLUSH, NULL, "%s", argv[1]); if (ENABLE_FEATURE_CLEAN_UP) close(fd); return EXIT_SUCCESS; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/blockdev.c����������������������������������������������������������������0000644�0000000�0000000�00000011035�12263563520�016041� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * blockdev implementation for busybox * * Copyright (C) 2010 Sergey Naumov <sknaumov@gmail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //applet:IF_BLOCKDEV(APPLET(blockdev, BB_DIR_SBIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_BLOCKDEV) += blockdev.o //config:config BLOCKDEV //config: bool "blockdev" //config: default y //config: help //config: Performs some ioctls with block devices. //usage:#define blockdev_trivial_usage //usage: "OPTION BLOCKDEV" //usage:#define blockdev_full_usage "\n\n" //usage: " --setro Set ro" //usage: "\n --setrw Set rw" //usage: "\n --getro Get ro" //usage: "\n --getss Get sector size" //usage: "\n --getbsz Get block size" //usage: "\n --setbsz BYTES Set block size" //usage: "\n --getsz Get device size in 512-byte sectors" /*//usage: "\n --getsize Get device size in sectors (deprecated)"*/ //usage: "\n --getsize64 Get device size in bytes" //usage: "\n --flushbufs Flush buffers" //usage: "\n --rereadpt Reread partition table" #include "libbb.h" #include <linux/fs.h> enum { ARG_NONE = 0, ARG_INT = 1, ARG_ULONG = 2, /* Yes, BLKGETSIZE64 takes pointer to uint64_t, not ullong! */ ARG_U64 = 3, ARG_MASK = 3, FL_USRARG = 4, /* argument is provided by user */ FL_NORESULT = 8, FL_SCALE512 = 16, }; struct bdc { uint32_t ioc; /* ioctl code */ const char name[sizeof("flushbufs")]; /* "--setfoo" wothout "--" */ uint8_t flags; int8_t argval; /* default argument value */ }; static const struct bdc bdcommands[] = { { .ioc = BLKROSET, .name = "setro", .flags = ARG_INT + FL_NORESULT, .argval = 1, },{ .ioc = BLKROSET, .name = "setrw", .flags = ARG_INT + FL_NORESULT, .argval = 0, },{ .ioc = BLKROGET, .name = "getro", .flags = ARG_INT, .argval = -1, },{ .ioc = BLKSSZGET, .name = "getss", .flags = ARG_INT, .argval = -1, },{ .ioc = BLKBSZGET, .name = "getbsz", .flags = ARG_INT, .argval = -1, },{ .ioc = BLKBSZSET, .name = "setbsz", .flags = ARG_INT + FL_NORESULT + FL_USRARG, .argval = 0, },{ .ioc = BLKGETSIZE64, .name = "getsz", .flags = ARG_U64 + FL_SCALE512, .argval = -1, },{ .ioc = BLKGETSIZE, .name = "getsize", .flags = ARG_ULONG, .argval = -1, },{ .ioc = BLKGETSIZE64, .name = "getsize64", .flags = ARG_U64, .argval = -1, },{ .ioc = BLKFLSBUF, .name = "flushbufs", .flags = ARG_NONE + FL_NORESULT, .argval = 0, },{ .ioc = BLKRRPART, .name = "rereadpt", .flags = ARG_NONE + FL_NORESULT, .argval = 0, } }; static const struct bdc *find_cmd(const char *s) { const struct bdc *bdcmd = bdcommands; if (s[0] == '-' && s[1] == '-') { s += 2; do { if (strcmp(s, bdcmd->name) == 0) return bdcmd; bdcmd++; } while (bdcmd != bdcommands + ARRAY_SIZE(bdcommands)); } bb_show_usage(); } int blockdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int blockdev_main(int argc UNUSED_PARAM, char **argv) { const struct bdc *bdcmd; int fd; uint64_t u64; union { int i; unsigned long lu; uint64_t u64; } ioctl_val_on_stack; argv++; if (!argv[0] || !argv[1]) /* must have at least 2 args */ bb_show_usage(); bdcmd = find_cmd(*argv); u64 = (int)bdcmd->argval; if (bdcmd->flags & FL_USRARG) u64 = xatoi_positive(*++argv); argv++; if (!argv[0] || argv[1]) bb_show_usage(); fd = xopen(argv[0], O_RDONLY); ioctl_val_on_stack.u64 = u64; #if BB_BIG_ENDIAN /* Store data properly wrt data size. * (1) It's no-op for little-endian. * (2) it's no-op for 0 and -1. Only --setro uses arg != 0 and != -1, * and it is ARG_INT. --setbsz USER_VAL is also ARG_INT. * Thus, we don't need to handle ARG_ULONG. */ switch (bdcmd->flags & ARG_MASK) { case ARG_INT: ioctl_val_on_stack.i = (int)u64; break; # if 0 /* unused */ case ARG_ULONG: ioctl_val_on_stack.lu = (unsigned long)u64; break; # endif } #endif if (ioctl(fd, bdcmd->ioc, &ioctl_val_on_stack.u64) == -1) bb_simple_perror_msg_and_die(*argv); /* Fetch it into register(s) */ u64 = ioctl_val_on_stack.u64; if (bdcmd->flags & FL_SCALE512) u64 >>= 9; /* Zero- or one-extend the value if needed, then print */ switch (bdcmd->flags & (ARG_MASK+FL_NORESULT)) { case ARG_INT: /* Smaller code when we use long long * (gcc tail-merges printf call) */ printf("%lld\n", (long long)(int)u64); break; case ARG_ULONG: u64 = (unsigned long)u64; /* FALLTHROUGH */ case ARG_U64: printf("%llu\n", (unsigned long long)u64); break; } if (ENABLE_FEATURE_CLEAN_UP) close(fd); return EXIT_SUCCESS; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/fdisk_osf.c���������������������������������������������������������������0000644�0000000�0000000�00000072237�12263563520�016232� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 1987, 1988 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. All advertising materials mentioning features or use of this software * must display the following acknowledgment: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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. */ #if ENABLE_FEATURE_OSF_LABEL #ifndef BSD_DISKMAGIC #define BSD_DISKMAGIC ((uint32_t) 0x82564557) #endif #ifndef BSD_MAXPARTITIONS #define BSD_MAXPARTITIONS 16 #endif #define BSD_LINUX_BOOTDIR "/usr/ucb/mdec" #if defined(__alpha__) \ || defined(__powerpc__) \ || defined(__ia64__) \ || defined(__hppa__) # define BSD_LABELSECTOR 0 # define BSD_LABELOFFSET 64 #else # define BSD_LABELSECTOR 1 # define BSD_LABELOFFSET 0 #endif #define BSD_BBSIZE 8192 /* size of boot area, with label */ #define BSD_SBSIZE 8192 /* max size of fs superblock */ struct xbsd_disklabel { uint32_t d_magic; /* the magic number */ int16_t d_type; /* drive type */ int16_t d_subtype; /* controller/d_type specific */ char d_typename[16]; /* type name, e.g. "eagle" */ char d_packname[16]; /* pack identifier */ /* disk geometry: */ uint32_t d_secsize; /* # of bytes per sector */ uint32_t d_nsectors; /* # of data sectors per track */ uint32_t d_ntracks; /* # of tracks per cylinder */ uint32_t d_ncylinders; /* # of data cylinders per unit */ uint32_t d_secpercyl; /* # of data sectors per cylinder */ uint32_t d_secperunit; /* # of data sectors per unit */ /* * Spares (bad sector replacements) below * are not counted in d_nsectors or d_secpercyl. * Spare sectors are assumed to be physical sectors * which occupy space at the end of each track and/or cylinder. */ uint16_t d_sparespertrack; /* # of spare sectors per track */ uint16_t d_sparespercyl; /* # of spare sectors per cylinder */ /* * Alternate cylinders include maintenance, replacement, * configuration description areas, etc. */ uint32_t d_acylinders; /* # of alt. cylinders per unit */ /* hardware characteristics: */ /* * d_interleave, d_trackskew and d_cylskew describe perturbations * in the media format used to compensate for a slow controller. * Interleave is physical sector interleave, set up by the formatter * or controller when formatting. When interleaving is in use, * logically adjacent sectors are not physically contiguous, * but instead are separated by some number of sectors. * It is specified as the ratio of physical sectors traversed * per logical sector. Thus an interleave of 1:1 implies contiguous * layout, while 2:1 implies that logical sector 0 is separated * by one sector from logical sector 1. * d_trackskew is the offset of sector 0 on track N * relative to sector 0 on track N-1 on the same cylinder. * Finally, d_cylskew is the offset of sector 0 on cylinder N * relative to sector 0 on cylinder N-1. */ uint16_t d_rpm; /* rotational speed */ uint16_t d_interleave; /* hardware sector interleave */ uint16_t d_trackskew; /* sector 0 skew, per track */ uint16_t d_cylskew; /* sector 0 skew, per cylinder */ uint32_t d_headswitch; /* head switch time, usec */ uint32_t d_trkseek; /* track-to-track seek, usec */ uint32_t d_flags; /* generic flags */ #define NDDATA 5 uint32_t d_drivedata[NDDATA]; /* drive-type specific information */ #define NSPARE 5 uint32_t d_spare[NSPARE]; /* reserved for future use */ uint32_t d_magic2; /* the magic number (again) */ uint16_t d_checksum; /* xor of data incl. partitions */ /* filesystem and partition information: */ uint16_t d_npartitions; /* number of partitions in following */ uint32_t d_bbsize; /* size of boot area at sn0, bytes */ uint32_t d_sbsize; /* max size of fs superblock, bytes */ struct xbsd_partition { /* the partition table */ uint32_t p_size; /* number of sectors in partition */ uint32_t p_offset; /* starting sector */ uint32_t p_fsize; /* filesystem basic fragment size */ uint8_t p_fstype; /* filesystem type, see below */ uint8_t p_frag; /* filesystem fragments per block */ uint16_t p_cpg; /* filesystem cylinders per group */ } d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */ }; /* d_type values: */ #define BSD_DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */ #define BSD_DTYPE_MSCP 2 /* MSCP */ #define BSD_DTYPE_DEC 3 /* other DEC (rk, rl) */ #define BSD_DTYPE_SCSI 4 /* SCSI */ #define BSD_DTYPE_ESDI 5 /* ESDI interface */ #define BSD_DTYPE_ST506 6 /* ST506 etc. */ #define BSD_DTYPE_HPIB 7 /* CS/80 on HP-IB */ #define BSD_DTYPE_HPFL 8 /* HP Fiber-link */ #define BSD_DTYPE_FLOPPY 10 /* floppy */ /* d_subtype values: */ #define BSD_DSTYPE_INDOSPART 0x8 /* is inside dos partition */ #define BSD_DSTYPE_DOSPART(s) ((s) & 3) /* dos partition number */ #define BSD_DSTYPE_GEOMETRY 0x10 /* drive params in label */ static const char *const xbsd_dktypenames[] = { "unknown", "SMD", "MSCP", "old DEC", "SCSI", "ESDI", "ST506", "HP-IB", "HP-FL", "type 9", "floppy", 0 }; /* * Filesystem type and version. * Used to interpret other filesystem-specific * per-partition information. */ #define BSD_FS_UNUSED 0 /* unused */ #define BSD_FS_SWAP 1 /* swap */ #define BSD_FS_V6 2 /* Sixth Edition */ #define BSD_FS_V7 3 /* Seventh Edition */ #define BSD_FS_SYSV 4 /* System V */ #define BSD_FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */ #define BSD_FS_V8 6 /* Eighth Edition, 4K blocks */ #define BSD_FS_BSDFFS 7 /* 4.2BSD fast file system */ #define BSD_FS_BSDLFS 9 /* 4.4BSD log-structured file system */ #define BSD_FS_OTHER 10 /* in use, but unknown/unsupported */ #define BSD_FS_HPFS 11 /* OS/2 high-performance file system */ #define BSD_FS_ISO9660 12 /* ISO-9660 filesystem (cdrom) */ #define BSD_FS_ISOFS BSD_FS_ISO9660 #define BSD_FS_BOOT 13 /* partition contains bootstrap */ #define BSD_FS_ADOS 14 /* AmigaDOS fast file system */ #define BSD_FS_HFS 15 /* Macintosh HFS */ #define BSD_FS_ADVFS 16 /* Digital Unix AdvFS */ /* this is annoying, but it's also the way it is :-( */ #ifdef __alpha__ #define BSD_FS_EXT2 8 /* ext2 file system */ #else #define BSD_FS_MSDOS 8 /* MS-DOS file system */ #endif static const char *const xbsd_fstypes[] = { "\x00" "unused", /* BSD_FS_UNUSED */ "\x01" "swap", /* BSD_FS_SWAP */ "\x02" "Version 6", /* BSD_FS_V6 */ "\x03" "Version 7", /* BSD_FS_V7 */ "\x04" "System V", /* BSD_FS_SYSV */ "\x05" "4.1BSD", /* BSD_FS_V71K */ "\x06" "Eighth Edition", /* BSD_FS_V8 */ "\x07" "4.2BSD", /* BSD_FS_BSDFFS */ #ifdef __alpha__ "\x08" "ext2", /* BSD_FS_EXT2 */ #else "\x08" "MS-DOS", /* BSD_FS_MSDOS */ #endif "\x09" "4.4LFS", /* BSD_FS_BSDLFS */ "\x0a" "unknown", /* BSD_FS_OTHER */ "\x0b" "HPFS", /* BSD_FS_HPFS */ "\x0c" "ISO-9660", /* BSD_FS_ISO9660 */ "\x0d" "boot", /* BSD_FS_BOOT */ "\x0e" "ADOS", /* BSD_FS_ADOS */ "\x0f" "HFS", /* BSD_FS_HFS */ "\x10" "AdvFS", /* BSD_FS_ADVFS */ NULL }; /* * flags shared by various drives: */ #define BSD_D_REMOVABLE 0x01 /* removable media */ #define BSD_D_ECC 0x02 /* supports ECC */ #define BSD_D_BADSECT 0x04 /* supports bad sector forw. */ #define BSD_D_RAMDISK 0x08 /* disk emulator */ #define BSD_D_CHAIN 0x10 /* can do back-back transfers */ #define BSD_D_DOSPART 0x20 /* within MSDOS partition */ /* Changes: 19990319 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> - i18n/nls 20000101 - David Huggins-Daines <dhuggins@linuxcare.com> - Better support for OSF/1 disklabels on Alpha. Also fixed unaligned accesses in alpha_bootblock_checksum() */ #define FREEBSD_PARTITION 0xa5 #define NETBSD_PARTITION 0xa9 static void xbsd_delete_part(void); static void xbsd_new_part(void); static void xbsd_write_disklabel(void); static int xbsd_create_disklabel(void); static void xbsd_edit_disklabel(void); static void xbsd_write_bootstrap(void); static void xbsd_change_fstype(void); static int xbsd_get_part_index(int max); static int xbsd_check_new_partition(int *i); static void xbsd_list_types(void); static uint16_t xbsd_dkcksum(struct xbsd_disklabel *lp); static int xbsd_initlabel(struct partition *p); static int xbsd_readlabel(struct partition *p); static int xbsd_writelabel(struct partition *p); #if defined(__alpha__) static void alpha_bootblock_checksum(char *boot); #endif #if !defined(__alpha__) static int xbsd_translate_fstype(int linux_type); static void xbsd_link_part(void); static struct partition *xbsd_part; static int xbsd_part_index; #endif /* Group big globals data and allocate it in one go */ struct bsd_globals { /* We access this through a uint64_t * when checksumming */ /* hopefully xmalloc gives us required alignment */ char disklabelbuffer[BSD_BBSIZE]; struct xbsd_disklabel xbsd_dlabel; }; static struct bsd_globals *bsd_globals_ptr; #define disklabelbuffer (bsd_globals_ptr->disklabelbuffer) #define xbsd_dlabel (bsd_globals_ptr->xbsd_dlabel) /* Code */ #define bsd_cround(n) \ (display_in_cyl_units ? ((n)/xbsd_dlabel.d_secpercyl) + 1 : (n)) /* * Test whether the whole disk has BSD disk label magic. * * Note: often reformatting with DOS-type label leaves the BSD magic, * so this does not mean that there is a BSD disk label. */ static int check_osf_label(void) { if (xbsd_readlabel(NULL) == 0) return 0; return 1; } static int bsd_trydev(const char * dev) { if (xbsd_readlabel(NULL) == 0) return -1; printf("\nBSD label for device: %s\n", dev); xbsd_print_disklabel(0); return 0; } static void bsd_menu(void) { puts("Command Action"); puts("d\tdelete a BSD partition"); puts("e\tedit drive data"); puts("i\tinstall bootstrap"); puts("l\tlist known filesystem types"); puts("n\tadd a new BSD partition"); puts("p\tprint BSD partition table"); puts("q\tquit without saving changes"); puts("r\treturn to main menu"); puts("s\tshow complete disklabel"); puts("t\tchange a partition's filesystem id"); puts("u\tchange units (cylinders/sectors)"); puts("w\twrite disklabel to disk"); #if !defined(__alpha__) puts("x\tlink BSD partition to non-BSD partition"); #endif } #if !defined(__alpha__) static int hidden(int type) { return type ^ 0x10; } static int is_bsd_partition_type(int type) { return (type == FREEBSD_PARTITION || type == hidden(FREEBSD_PARTITION) || type == NETBSD_PARTITION || type == hidden(NETBSD_PARTITION)); } #endif static void bsd_select(void) { #if !defined(__alpha__) int t, ss; struct partition *p; for (t = 0; t < 4; t++) { p = get_part_table(t); if (p && is_bsd_partition_type(p->sys_ind)) { xbsd_part = p; xbsd_part_index = t; ss = get_start_sect(xbsd_part); if (ss == 0) { printf("Partition %s has invalid starting sector 0\n", partname(disk_device, t+1, 0)); return; } printf("Reading disklabel of %s at sector %u\n", partname(disk_device, t+1, 0), ss + BSD_LABELSECTOR); if (xbsd_readlabel(xbsd_part) == 0) if (xbsd_create_disklabel() == 0) return; break; } } if (t == 4) { printf("There is no *BSD partition on %s\n", disk_device); return; } #elif defined(__alpha__) if (xbsd_readlabel(NULL) == 0) if (xbsd_create_disklabel() == 0) exit(EXIT_SUCCESS); #endif while (1) { bb_putchar('\n'); switch (tolower(read_nonempty("BSD disklabel command (m for help): "))) { case 'd': xbsd_delete_part(); break; case 'e': xbsd_edit_disklabel(); break; case 'i': xbsd_write_bootstrap(); break; case 'l': xbsd_list_types(); break; case 'n': xbsd_new_part(); break; case 'p': xbsd_print_disklabel(0); break; case 'q': if (ENABLE_FEATURE_CLEAN_UP) close_dev_fd(); exit(EXIT_SUCCESS); case 'r': return; case 's': xbsd_print_disklabel(1); break; case 't': xbsd_change_fstype(); break; case 'u': change_units(); break; case 'w': xbsd_write_disklabel(); break; #if !defined(__alpha__) case 'x': xbsd_link_part(); break; #endif default: bsd_menu(); break; } } } static void xbsd_delete_part(void) { int i; i = xbsd_get_part_index(xbsd_dlabel.d_npartitions); xbsd_dlabel.d_partitions[i].p_size = 0; xbsd_dlabel.d_partitions[i].p_offset = 0; xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED; if (xbsd_dlabel.d_npartitions == i + 1) while (xbsd_dlabel.d_partitions[xbsd_dlabel.d_npartitions-1].p_size == 0) xbsd_dlabel.d_npartitions--; } static void xbsd_new_part(void) { off_t begin, end; char mesg[256]; int i; if (!xbsd_check_new_partition(&i)) return; #if !defined(__alpha__) && !defined(__powerpc__) && !defined(__hppa__) begin = get_start_sect(xbsd_part); end = begin + get_nr_sects(xbsd_part) - 1; #else begin = 0; end = xbsd_dlabel.d_secperunit - 1; #endif snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR)); begin = read_int(bsd_cround(begin), bsd_cround(begin), bsd_cround(end), 0, mesg); if (display_in_cyl_units) begin = (begin - 1) * xbsd_dlabel.d_secpercyl; snprintf(mesg, sizeof(mesg), "Last %s or +size or +sizeM or +sizeK", str_units(SINGULAR)); end = read_int(bsd_cround(begin), bsd_cround(end), bsd_cround(end), bsd_cround(begin), mesg); if (display_in_cyl_units) end = end * xbsd_dlabel.d_secpercyl - 1; xbsd_dlabel.d_partitions[i].p_size = end - begin + 1; xbsd_dlabel.d_partitions[i].p_offset = begin; xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED; } static void xbsd_print_disklabel(int show_all) { struct xbsd_disklabel *lp = &xbsd_dlabel; struct xbsd_partition *pp; int i, j; if (show_all) { static const int d_masks[] = { BSD_D_REMOVABLE, BSD_D_ECC, BSD_D_BADSECT }; #if defined(__alpha__) printf("# %s:\n", disk_device); #else printf("# %s:\n", partname(disk_device, xbsd_part_index+1, 0)); #endif if ((unsigned) lp->d_type < ARRAY_SIZE(xbsd_dktypenames)-1) printf("type: %s\n", xbsd_dktypenames[lp->d_type]); else printf("type: %u\n", lp->d_type); printf("disk: %.*s\n", (int) sizeof(lp->d_typename), lp->d_typename); printf("label: %.*s\n", (int) sizeof(lp->d_packname), lp->d_packname); printf("flags: "); print_flags_separated(d_masks, "removable\0""ecc\0""badsect\0", lp->d_flags, " "); bb_putchar('\n'); /* On various machines the fields of *lp are short/int/long */ /* In order to avoid problems, we cast them all to long. */ printf("bytes/sector: %lu\n", (long) lp->d_secsize); printf("sectors/track: %lu\n", (long) lp->d_nsectors); printf("tracks/cylinder: %lu\n", (long) lp->d_ntracks); printf("sectors/cylinder: %lu\n", (long) lp->d_secpercyl); printf("cylinders: %lu\n", (long) lp->d_ncylinders); printf("rpm: %u\n", lp->d_rpm); printf("interleave: %u\n", lp->d_interleave); printf("trackskew: %u\n", lp->d_trackskew); printf("cylinderskew: %u\n", lp->d_cylskew); printf("headswitch: %lu\t\t# milliseconds\n", (long) lp->d_headswitch); printf("track-to-track seek: %lu\t# milliseconds\n", (long) lp->d_trkseek); printf("drivedata: "); for (i = NDDATA - 1; i >= 0; i--) if (lp->d_drivedata[i]) break; if (i < 0) i = 0; for (j = 0; j <= i; j++) printf("%lu ", (long) lp->d_drivedata[j]); } printf("\n%u partitions:\n", lp->d_npartitions); printf("# start end size fstype [fsize bsize cpg]\n"); pp = lp->d_partitions; for (i = 0; i < lp->d_npartitions; i++, pp++) { if (pp->p_size) { if (display_in_cyl_units && lp->d_secpercyl) { printf(" %c: %8lu%c %8lu%c %8lu%c ", 'a' + i, (unsigned long) pp->p_offset / lp->d_secpercyl + 1, (pp->p_offset % lp->d_secpercyl) ? '*' : ' ', (unsigned long) (pp->p_offset + pp->p_size + lp->d_secpercyl - 1) / lp->d_secpercyl, ((pp->p_offset + pp->p_size) % lp->d_secpercyl) ? '*' : ' ', (long) pp->p_size / lp->d_secpercyl, (pp->p_size % lp->d_secpercyl) ? '*' : ' ' ); } else { printf(" %c: %8lu %8lu %8lu ", 'a' + i, (long) pp->p_offset, (long) pp->p_offset + pp->p_size - 1, (long) pp->p_size ); } if ((unsigned) pp->p_fstype < ARRAY_SIZE(xbsd_fstypes)-1) printf("%8.8s", xbsd_fstypes[pp->p_fstype]); else printf("%8x", pp->p_fstype); switch (pp->p_fstype) { case BSD_FS_UNUSED: printf(" %5lu %5lu %5.5s ", (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, ""); break; case BSD_FS_BSDFFS: printf(" %5lu %5lu %5u ", (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, pp->p_cpg); break; default: printf("%22.22s", ""); break; } bb_putchar('\n'); } } } static void xbsd_write_disklabel(void) { #if defined(__alpha__) printf("Writing disklabel to %s\n", disk_device); xbsd_writelabel(NULL); #else printf("Writing disklabel to %s\n", partname(disk_device, xbsd_part_index + 1, 0)); xbsd_writelabel(xbsd_part); #endif reread_partition_table(0); /* no exit yet */ } static int xbsd_create_disklabel(void) { char c; #if defined(__alpha__) printf("%s contains no disklabel\n", disk_device); #else printf("%s contains no disklabel\n", partname(disk_device, xbsd_part_index + 1, 0)); #endif while (1) { c = read_nonempty("Do you want to create a disklabel? (y/n) "); if ((c|0x20) == 'y') { if (xbsd_initlabel( #if defined(__alpha__) || defined(__powerpc__) || defined(__hppa__) || \ defined(__s390__) || defined(__s390x__) NULL #else xbsd_part #endif ) == 1) { xbsd_print_disklabel(1); return 1; } return 0; } if ((c|0x20) == 'n') return 0; } } static int edit_int(int def, const char *mesg) { mesg = xasprintf("%s (%u): ", mesg, def); do { if (!read_line(mesg)) goto ret; } while (!isdigit(*line_ptr)); def = atoi(line_ptr); ret: free((char*)mesg); return def; } static void xbsd_edit_disklabel(void) { struct xbsd_disklabel *d; d = &xbsd_dlabel; #if defined(__alpha__) || defined(__ia64__) d->d_secsize = edit_int(d->d_secsize , "bytes/sector"); d->d_nsectors = edit_int(d->d_nsectors , "sectors/track"); d->d_ntracks = edit_int(d->d_ntracks , "tracks/cylinder"); d->d_ncylinders = edit_int(d->d_ncylinders , "cylinders"); #endif /* d->d_secpercyl can be != d->d_nsectors * d->d_ntracks */ while (1) { d->d_secpercyl = edit_int(d->d_nsectors * d->d_ntracks, "sectors/cylinder"); if (d->d_secpercyl <= d->d_nsectors * d->d_ntracks) break; printf("Must be <= sectors/track * tracks/cylinder (default)\n"); } d->d_rpm = edit_int(d->d_rpm , "rpm"); d->d_interleave = edit_int(d->d_interleave, "interleave"); d->d_trackskew = edit_int(d->d_trackskew , "trackskew"); d->d_cylskew = edit_int(d->d_cylskew , "cylinderskew"); d->d_headswitch = edit_int(d->d_headswitch, "headswitch"); d->d_trkseek = edit_int(d->d_trkseek , "track-to-track seek"); d->d_secperunit = d->d_secpercyl * d->d_ncylinders; } static int xbsd_get_bootstrap(char *path, void *ptr, int size) { int fdb; fdb = open_or_warn(path, O_RDONLY); if (fdb < 0) { return 0; } if (full_read(fdb, ptr, size) < 0) { bb_simple_perror_msg(path); close(fdb); return 0; } printf(" ... %s\n", path); close(fdb); return 1; } static void sync_disks(void) { printf("Syncing disks\n"); sync(); /* sleep(4); What? */ } static void xbsd_write_bootstrap(void) { char path[MAXPATHLEN]; const char *bootdir = BSD_LINUX_BOOTDIR; const char *dkbasename; struct xbsd_disklabel dl; char *d, *p, *e; int sector; if (xbsd_dlabel.d_type == BSD_DTYPE_SCSI) dkbasename = "sd"; else dkbasename = "wd"; snprintf(path, sizeof(path), "Bootstrap: %sboot -> boot%s (%s): ", dkbasename, dkbasename, dkbasename); if (read_line(path)) { dkbasename = line_ptr; } snprintf(path, sizeof(path), "%s/%sboot", bootdir, dkbasename); if (!xbsd_get_bootstrap(path, disklabelbuffer, (int) xbsd_dlabel.d_secsize)) return; /* We need a backup of the disklabel (xbsd_dlabel might have changed). */ d = &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE]; memmove(&dl, d, sizeof(struct xbsd_disklabel)); /* The disklabel will be overwritten by 0's from bootxx anyway */ memset(d, 0, sizeof(struct xbsd_disklabel)); snprintf(path, sizeof(path), "%s/boot%s", bootdir, dkbasename); if (!xbsd_get_bootstrap(path, &disklabelbuffer[xbsd_dlabel.d_secsize], (int) xbsd_dlabel.d_bbsize - xbsd_dlabel.d_secsize)) return; e = d + sizeof(struct xbsd_disklabel); for (p = d; p < e; p++) if (*p) { printf("Bootstrap overlaps with disk label!\n"); exit(EXIT_FAILURE); } memmove(d, &dl, sizeof(struct xbsd_disklabel)); #if defined(__powerpc__) || defined(__hppa__) sector = 0; #elif defined(__alpha__) sector = 0; alpha_bootblock_checksum(disklabelbuffer); #else sector = get_start_sect(xbsd_part); #endif seek_sector(sector); xwrite(dev_fd, disklabelbuffer, BSD_BBSIZE); #if defined(__alpha__) printf("Bootstrap installed on %s\n", disk_device); #else printf("Bootstrap installed on %s\n", partname(disk_device, xbsd_part_index+1, 0)); #endif sync_disks(); } static void xbsd_change_fstype(void) { int i; i = xbsd_get_part_index(xbsd_dlabel.d_npartitions); xbsd_dlabel.d_partitions[i].p_fstype = read_hex(xbsd_fstypes); } static int xbsd_get_part_index(int max) { char prompt[sizeof("Partition (a-%c): ") + 16]; char l; snprintf(prompt, sizeof(prompt), "Partition (a-%c): ", 'a' + max - 1); do l = tolower(read_nonempty(prompt)); while (l < 'a' || l > 'a' + max - 1); return l - 'a'; } static int xbsd_check_new_partition(int *i) { /* room for more? various BSD flavours have different maxima */ if (xbsd_dlabel.d_npartitions == BSD_MAXPARTITIONS) { int t; for (t = 0; t < BSD_MAXPARTITIONS; t++) if (xbsd_dlabel.d_partitions[t].p_size == 0) break; if (t == BSD_MAXPARTITIONS) { printf("The maximum number of partitions has been created\n"); return 0; } } *i = xbsd_get_part_index(BSD_MAXPARTITIONS); if (*i >= xbsd_dlabel.d_npartitions) xbsd_dlabel.d_npartitions = (*i) + 1; if (xbsd_dlabel.d_partitions[*i].p_size != 0) { printf("This partition already exists\n"); return 0; } return 1; } static void xbsd_list_types(void) { list_types(xbsd_fstypes); } static uint16_t xbsd_dkcksum(struct xbsd_disklabel *lp) { uint16_t *start, *end; uint16_t sum = 0; start = (uint16_t *) lp; end = (uint16_t *) &lp->d_partitions[lp->d_npartitions]; while (start < end) sum ^= *start++; return sum; } static int xbsd_initlabel(struct partition *p) { struct xbsd_disklabel *d = &xbsd_dlabel; struct xbsd_partition *pp; get_geometry(); memset(d, 0, sizeof(struct xbsd_disklabel)); d->d_magic = BSD_DISKMAGIC; if (strncmp(disk_device, "/dev/sd", 7) == 0) d->d_type = BSD_DTYPE_SCSI; else d->d_type = BSD_DTYPE_ST506; #if !defined(__alpha__) d->d_flags = BSD_D_DOSPART; #else d->d_flags = 0; #endif d->d_secsize = SECTOR_SIZE; /* bytes/sector */ d->d_nsectors = g_sectors; /* sectors/track */ d->d_ntracks = g_heads; /* tracks/cylinder (heads) */ d->d_ncylinders = g_cylinders; d->d_secpercyl = g_sectors * g_heads;/* sectors/cylinder */ if (d->d_secpercyl == 0) d->d_secpercyl = 1; /* avoid segfaults */ d->d_secperunit = d->d_secpercyl * d->d_ncylinders; d->d_rpm = 3600; d->d_interleave = 1; d->d_trackskew = 0; d->d_cylskew = 0; d->d_headswitch = 0; d->d_trkseek = 0; d->d_magic2 = BSD_DISKMAGIC; d->d_bbsize = BSD_BBSIZE; d->d_sbsize = BSD_SBSIZE; #if !defined(__alpha__) d->d_npartitions = 4; pp = &d->d_partitions[2]; /* Partition C should be NetBSD partition */ pp->p_offset = get_start_sect(p); pp->p_size = get_nr_sects(p); pp->p_fstype = BSD_FS_UNUSED; pp = &d->d_partitions[3]; /* Partition D should be whole disk */ pp->p_offset = 0; pp->p_size = d->d_secperunit; pp->p_fstype = BSD_FS_UNUSED; #else d->d_npartitions = 3; pp = &d->d_partitions[2]; /* Partition C should be the whole disk */ pp->p_offset = 0; pp->p_size = d->d_secperunit; pp->p_fstype = BSD_FS_UNUSED; #endif return 1; } /* * Read a xbsd_disklabel from sector 0 or from the starting sector of p. * If it has the right magic, return 1. */ static int xbsd_readlabel(struct partition *p) { struct xbsd_disklabel *d; int t, sector; if (!bsd_globals_ptr) bsd_globals_ptr = xzalloc(sizeof(*bsd_globals_ptr)); d = &xbsd_dlabel; /* p is used only to get the starting sector */ #if !defined(__alpha__) sector = (p ? get_start_sect(p) : 0); #else sector = 0; #endif seek_sector(sector); if (BSD_BBSIZE != full_read(dev_fd, disklabelbuffer, BSD_BBSIZE)) fdisk_fatal(unable_to_read); memmove(d, &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET], sizeof(struct xbsd_disklabel)); if (d->d_magic != BSD_DISKMAGIC || d->d_magic2 != BSD_DISKMAGIC) return 0; for (t = d->d_npartitions; t < BSD_MAXPARTITIONS; t++) { d->d_partitions[t].p_size = 0; d->d_partitions[t].p_offset = 0; d->d_partitions[t].p_fstype = BSD_FS_UNUSED; } if (d->d_npartitions > BSD_MAXPARTITIONS) printf("Warning: too many partitions (%u, maximum is %u)\n", d->d_npartitions, BSD_MAXPARTITIONS); return 1; } static int xbsd_writelabel(struct partition *p) { struct xbsd_disklabel *d = &xbsd_dlabel; unsigned int sector; #if !defined(__alpha__) && !defined(__powerpc__) && !defined(__hppa__) sector = get_start_sect(p) + BSD_LABELSECTOR; #else (void)p; /* silence warning */ sector = BSD_LABELSECTOR; #endif d->d_checksum = 0; d->d_checksum = xbsd_dkcksum(d); /* This is necessary if we want to write the bootstrap later, otherwise we'd write the old disklabel with the bootstrap. */ memmove(&disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET], d, sizeof(struct xbsd_disklabel)); #if defined(__alpha__) && BSD_LABELSECTOR == 0 alpha_bootblock_checksum(disklabelbuffer); seek_sector(0); xwrite(dev_fd, disklabelbuffer, BSD_BBSIZE); #else seek_sector(sector); lseek(dev_fd, BSD_LABELOFFSET, SEEK_CUR); xwrite(dev_fd, d, sizeof(*d)); #endif sync_disks(); return 1; } #if !defined(__alpha__) static int xbsd_translate_fstype(int linux_type) { switch (linux_type) { case 0x01: /* DOS 12-bit FAT */ case 0x04: /* DOS 16-bit <32M */ case 0x06: /* DOS 16-bit >=32M */ case 0xe1: /* DOS access */ case 0xe3: /* DOS R/O */ case 0xf2: /* DOS secondary */ return BSD_FS_MSDOS; case 0x07: /* OS/2 HPFS */ return BSD_FS_HPFS; default: return BSD_FS_OTHER; } } static void xbsd_link_part(void) { int k, i; struct partition *p; k = get_partition(1, g_partitions); if (!xbsd_check_new_partition(&i)) return; p = get_part_table(k); xbsd_dlabel.d_partitions[i].p_size = get_nr_sects(p); xbsd_dlabel.d_partitions[i].p_offset = get_start_sect(p); xbsd_dlabel.d_partitions[i].p_fstype = xbsd_translate_fstype(p->sys_ind); } #endif #if defined(__alpha__) static void alpha_bootblock_checksum(char *boot) { uint64_t *dp, sum; int i; dp = (uint64_t *)boot; sum = 0; for (i = 0; i < 63; i++) sum += dp[i]; dp[63] = sum; } #endif /* __alpha__ */ /* Undefine 'global' tricks */ #undef disklabelbuffer #undef xbsd_dlabel #endif /* OSF_LABEL */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/rdev.c��������������������������������������������������������������������0000644�0000000�0000000�00000001517�12263563520�015214� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * rdev - print device node associated with a filesystem * * Copyright (c) 2008 Nuovation System Designs, LLC * Grant Erickson <gerickson@nuovations.com> * * Licensed under GPLv2, see file LICENSE in this source tree. * */ //usage:#define rdev_trivial_usage //usage: "" //usage:#define rdev_full_usage "\n\n" //usage: "Print the device node associated with the filesystem mounted at '/'" //usage: //usage:#define rdev_example_usage //usage: "$ rdev\n" //usage: "/dev/mtdblock9 /\n" #include "libbb.h" int rdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int rdev_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { const char *root_device = find_block_device("/"); if (root_device) { printf("%s /\n", root_device); return EXIT_SUCCESS; } return EXIT_FAILURE; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/getopt.c������������������������������������������������������������������0000644�0000000�0000000�00000030502�12263563520�015552� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * getopt.c - Enhanced implementation of BSD getopt(1) * Copyright (c) 1997, 1998, 1999, 2000 Frodo Looijaard <frodol@dds.nl> * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* * Version 1.0-b4: Tue Sep 23 1997. First public release. * Version 1.0: Wed Nov 19 1997. * Bumped up the version number to 1.0 * Fixed minor typo (CSH instead of TCSH) * Version 1.0.1: Tue Jun 3 1998 * Fixed sizeof instead of strlen bug * Bumped up the version number to 1.0.1 * Version 1.0.2: Thu Jun 11 1998 (not present) * Fixed gcc-2.8.1 warnings * Fixed --version/-V option (not present) * Version 1.0.5: Tue Jun 22 1999 * Make -u option work (not present) * Version 1.0.6: Tue Jun 27 2000 * No important changes * Version 1.1.0: Tue Jun 30 2000 * Added NLS support (partly written by Arkadiusz Mickiewicz * <misiek@misiek.eu.org>) * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org> * Removed --version/-V and --help/-h * Removed parse_error(), using bb_error_msg() from Busybox instead * Replaced our_malloc with xmalloc and our_realloc with xrealloc * */ //usage:#define getopt_trivial_usage //usage: "[OPTIONS] [--] OPTSTRING PARAMS" //usage:#define getopt_full_usage "\n\n" //usage: IF_LONG_OPTS( //usage: IF_FEATURE_GETOPT_LONG( //usage: " -a,--alternative Allow long options starting with single -\n" //usage: " -l,--longoptions=LOPT[,...] Long options to recognize\n" //usage: ) //usage: " -n,--name=PROGNAME The name under which errors are reported" //usage: "\n -o,--options=OPTSTRING Short options to recognize" //usage: "\n -q,--quiet No error messages on unrecognized options" //usage: "\n -Q,--quiet-output No normal output" //usage: "\n -s,--shell=SHELL Set shell quoting conventions" //usage: "\n -T,--test Version test (exits with 4)" //usage: "\n -u,--unquoted Don't quote output" //usage: ) //usage: IF_NOT_LONG_OPTS( //usage: IF_FEATURE_GETOPT_LONG( //usage: " -a Allow long options starting with single -\n" //usage: " -l LOPT[,...] Long options to recognize\n" //usage: ) //usage: " -n PROGNAME The name under which errors are reported" //usage: "\n -o OPTSTRING Short options to recognize" //usage: "\n -q No error messages on unrecognized options" //usage: "\n -Q No normal output" //usage: "\n -s SHELL Set shell quoting conventions" //usage: "\n -T Version test (exits with 4)" //usage: "\n -u Don't quote output" //usage: ) //usage: IF_FEATURE_GETOPT_LONG( /* example uses -l, needs FEATURE_GETOPT_LONG */ //usage: "\n" //usage: "\nExample:" //usage: "\n" //usage: "\nO=`getopt -l bb: -- ab:c:: \"$@\"` || exit 1" //usage: "\neval set -- \"$O\"" //usage: "\nwhile true; do" //usage: "\n case \"$1\" in" //usage: "\n -a) echo A; shift;;" //usage: "\n -b|--bb) echo \"B:'$2'\"; shift 2;;" //usage: "\n -c) case \"$2\" in" //usage: "\n \"\") echo C; shift 2;;" //usage: "\n *) echo \"C:'$2'\"; shift 2;;" //usage: "\n esac;;" //usage: "\n --) shift; break;;" //usage: "\n *) echo Error; exit 1;;" //usage: "\n esac" //usage: "\ndone" //usage: ) //usage: //usage:#define getopt_example_usage //usage: "$ cat getopt.test\n" //usage: "#!/bin/sh\n" //usage: "GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \\\n" //usage: " -n 'example.busybox' -- \"$@\"`\n" //usage: "if [ $? != 0 ]; then exit 1; fi\n" //usage: "eval set -- \"$GETOPT\"\n" //usage: "while true; do\n" //usage: " case $1 in\n" //usage: " -a|--a-long) echo \"Option a\"; shift;;\n" //usage: " -b|--b-long) echo \"Option b, argument '$2'\"; shift 2;;\n" //usage: " -c|--c-long)\n" //usage: " case \"$2\" in\n" //usage: " \"\") echo \"Option c, no argument\"; shift 2;;\n" //usage: " *) echo \"Option c, argument '$2'\"; shift 2;;\n" //usage: " esac;;\n" //usage: " --) shift; break;;\n" //usage: " *) echo \"Internal error!\"; exit 1;;\n" //usage: " esac\n" //usage: "done\n" #if ENABLE_FEATURE_GETOPT_LONG # include <getopt.h> #endif #include "libbb.h" /* NON_OPT is the code that is returned when a non-option is found in '+' mode */ enum { NON_OPT = 1, #if ENABLE_FEATURE_GETOPT_LONG /* LONG_OPT is the code that is returned when a long option is found. */ LONG_OPT = 2 #endif }; /* For finding activated option flags. Must match getopt32 call! */ enum { OPT_o = 0x1, // -o OPT_n = 0x2, // -n OPT_q = 0x4, // -q OPT_Q = 0x8, // -Q OPT_s = 0x10, // -s OPT_T = 0x20, // -T OPT_u = 0x40, // -u #if ENABLE_FEATURE_GETOPT_LONG OPT_a = 0x80, // -a OPT_l = 0x100, // -l #endif SHELL_IS_TCSH = 0x8000, /* hijack this bit for other purposes */ }; /* 0 is getopt_long, 1 is getopt_long_only */ #define alternative (option_mask32 & OPT_a) #define quiet_errors (option_mask32 & OPT_q) #define quiet_output (option_mask32 & OPT_Q) #define quote (!(option_mask32 & OPT_u)) #define shell_TCSH (option_mask32 & SHELL_IS_TCSH) /* * This function 'normalizes' a single argument: it puts single quotes around * it and escapes other special characters. If quote is false, it just * returns its argument. * Bash only needs special treatment for single quotes; tcsh also recognizes * exclamation marks within single quotes, and nukes whitespace. * This function returns a pointer to a buffer that is overwritten by * each call. */ static const char *normalize(const char *arg) { char *bufptr; #if ENABLE_FEATURE_CLEAN_UP static char *BUFFER = NULL; free(BUFFER); #else char *BUFFER; #endif if (!quote) { /* Just copy arg */ BUFFER = xstrdup(arg); return BUFFER; } /* Each character in arg may take up to four characters in the result: For a quote we need a closing quote, a backslash, a quote and an opening quote! We need also the global opening and closing quote, and one extra character for '\0'. */ BUFFER = xmalloc(strlen(arg)*4 + 3); bufptr = BUFFER; *bufptr ++= '\''; while (*arg) { if (*arg == '\'') { /* Quote: replace it with: '\'' */ *bufptr ++= '\''; *bufptr ++= '\\'; *bufptr ++= '\''; *bufptr ++= '\''; } else if (shell_TCSH && *arg == '!') { /* Exclamation mark: replace it with: \! */ *bufptr ++= '\''; *bufptr ++= '\\'; *bufptr ++= '!'; *bufptr ++= '\''; } else if (shell_TCSH && *arg == '\n') { /* Newline: replace it with: \n */ *bufptr ++= '\\'; *bufptr ++= 'n'; } else if (shell_TCSH && isspace(*arg)) { /* Non-newline whitespace: replace it with \<ws> */ *bufptr ++= '\''; *bufptr ++= '\\'; *bufptr ++= *arg; *bufptr ++= '\''; } else /* Just copy */ *bufptr ++= *arg; arg++; } *bufptr ++= '\''; *bufptr ++= '\0'; return BUFFER; } /* * Generate the output. argv[0] is the program name (used for reporting errors). * argv[1..] contains the options to be parsed. argc must be the number of * elements in argv (ie. 1 if there are no options, only the program name), * optstr must contain the short options, and longopts the long options. * Other settings are found in global variables. */ #if !ENABLE_FEATURE_GETOPT_LONG #define generate_output(argv,argc,optstr,longopts) \ generate_output(argv,argc,optstr) #endif static int generate_output(char **argv, int argc, const char *optstr, const struct option *longopts) { int exit_code = 0; /* We assume everything will be OK */ if (quiet_errors) /* No error reporting from getopt(3) */ opterr = 0; /* We used it already in main() in getopt32(), * we *must* reset getopt(3): */ #ifdef __GLIBC__ optind = 0; #else /* BSD style */ optind = 1; /* optreset = 1; */ #endif while (1) { #if ENABLE_FEATURE_GETOPT_LONG int longindex; int opt = alternative ? getopt_long_only(argc, argv, optstr, longopts, &longindex) : getopt_long(argc, argv, optstr, longopts, &longindex) ; #else int opt = getopt(argc, argv, optstr); #endif if (opt == -1) break; if (opt == '?' || opt == ':' ) exit_code = 1; else if (!quiet_output) { #if ENABLE_FEATURE_GETOPT_LONG if (opt == LONG_OPT) { printf(" --%s", longopts[longindex].name); if (longopts[longindex].has_arg) printf(" %s", normalize(optarg ? optarg : "")); } else #endif if (opt == NON_OPT) printf(" %s", normalize(optarg)); else { const char *charptr; printf(" -%c", opt); charptr = strchr(optstr, opt); if (charptr && *++charptr == ':') printf(" %s", normalize(optarg ? optarg : "")); } } } if (!quiet_output) { unsigned idx; printf(" --"); idx = optind; while (argv[idx]) printf(" %s", normalize(argv[idx++])); bb_putchar('\n'); } return exit_code; } #if ENABLE_FEATURE_GETOPT_LONG /* * Register several long options. options is a string of long options, * separated by commas or whitespace. * This nukes options! */ static struct option *add_long_options(struct option *long_options, char *options) { int long_nr = 0; int arg_opt, tlen; char *tokptr = strtok(options, ", \t\n"); if (long_options) while (long_options[long_nr].name) long_nr++; while (tokptr) { arg_opt = no_argument; tlen = strlen(tokptr); if (tlen) { tlen--; if (tokptr[tlen] == ':') { arg_opt = required_argument; if (tlen && tokptr[tlen-1] == ':') { tlen--; arg_opt = optional_argument; } tokptr[tlen] = '\0'; if (tlen == 0) bb_error_msg_and_die("empty long option specified"); } long_options = xrealloc_vector(long_options, 4, long_nr); long_options[long_nr].has_arg = arg_opt; /*long_options[long_nr].flag = NULL; - xrealloc_vector did it */ long_options[long_nr].val = LONG_OPT; long_options[long_nr].name = xstrdup(tokptr); long_nr++; /*memset(&long_options[long_nr], 0, sizeof(long_options[0])); - xrealloc_vector did it */ } tokptr = strtok(NULL, ", \t\n"); } return long_options; } #endif static void set_shell(const char *new_shell) { if (!strcmp(new_shell, "bash") || !strcmp(new_shell, "sh")) return; if (!strcmp(new_shell, "tcsh") || !strcmp(new_shell, "csh")) option_mask32 |= SHELL_IS_TCSH; else bb_error_msg("unknown shell '%s', assuming bash", new_shell); } /* Exit codes: * 0) No errors, successful operation. * 1) getopt(3) returned an error. * 2) A problem with parameter parsing for getopt(1). * 3) Internal error, out of memory * 4) Returned for -T */ #if ENABLE_FEATURE_GETOPT_LONG static const char getopt_longopts[] ALIGN1 = "options\0" Required_argument "o" "longoptions\0" Required_argument "l" "quiet\0" No_argument "q" "quiet-output\0" No_argument "Q" "shell\0" Required_argument "s" "test\0" No_argument "T" "unquoted\0" No_argument "u" "alternative\0" No_argument "a" "name\0" Required_argument "n" ; #endif int getopt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int getopt_main(int argc, char **argv) { int n; char *optstr = NULL; char *name = NULL; unsigned opt; const char *compatible; char *s_arg; #if ENABLE_FEATURE_GETOPT_LONG struct option *long_options = NULL; llist_t *l_arg = NULL; #endif compatible = getenv("GETOPT_COMPATIBLE"); /* used as yes/no flag */ if (!argv[1]) { if (compatible) { /* For some reason, the original getopt gave no error * when there were no arguments. */ printf(" --\n"); return 0; } bb_error_msg_and_die("missing optstring argument"); } if (argv[1][0] != '-' || compatible) { char *s = argv[1]; option_mask32 |= OPT_u; /* quoting off */ s = xstrdup(s + strspn(s, "-+")); argv[1] = argv[0]; return generate_output(argv+1, argc-1, s, long_options); } #if !ENABLE_FEATURE_GETOPT_LONG opt = getopt32(argv, "+o:n:qQs:Tu", &optstr, &name, &s_arg); #else applet_long_options = getopt_longopts; opt_complementary = "l::"; opt = getopt32(argv, "+o:n:qQs:Tual:", &optstr, &name, &s_arg, &l_arg); /* Effectuate the read options for the applet itself */ while (l_arg) { long_options = add_long_options(long_options, llist_pop(&l_arg)); } #endif if (opt & OPT_s) { set_shell(s_arg); } if (opt & OPT_T) { return 4; } /* All options controlling the applet have now been parsed */ n = optind - 1; if (!optstr) { optstr = argv[++n]; if (!optstr) bb_error_msg_and_die("missing optstring argument"); } argv[n] = name ? name : argv[0]; return generate_output(argv + n, argc - n, optstr, long_options); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/swaponoff.c���������������������������������������������������������������0000644�0000000�0000000�00000006632�12263563520�016261� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Mini swapon/swapoff implementation for busybox * * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define swapon_trivial_usage //usage: "[-a]" IF_FEATURE_SWAPON_PRI(" [-p PRI]") " [DEVICE]" //usage:#define swapon_full_usage "\n\n" //usage: "Start swapping on DEVICE\n" //usage: "\n -a Start swapping on all swap devices" //usage: IF_FEATURE_SWAPON_PRI( //usage: "\n -p PRI Set swap device priority" //usage: ) //usage: //usage:#define swapoff_trivial_usage //usage: "[-a] [DEVICE]" //usage:#define swapoff_full_usage "\n\n" //usage: "Stop swapping on DEVICE\n" //usage: "\n -a Stop swapping on all swap devices" #include "libbb.h" #include <mntent.h> #ifndef __BIONIC__ # include <sys/swap.h> #endif #if ENABLE_FEATURE_MOUNT_LABEL # include "volume_id.h" #else # define resolve_mount_spec(fsname) ((void)0) #endif #ifndef MNTTYPE_SWAP # define MNTTYPE_SWAP "swap" #endif #if ENABLE_FEATURE_SWAPON_PRI struct globals { int flags; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define g_flags (G.flags) #else #define g_flags 0 #endif #define INIT_G() do { } while (0) static int swap_enable_disable(char *device) { int status; struct stat st; resolve_mount_spec(&device); xstat(device, &st); #if ENABLE_DESKTOP /* test for holes */ if (S_ISREG(st.st_mode)) if (st.st_blocks * (off_t)512 < st.st_size) bb_error_msg("warning: swap file has holes"); #endif if (applet_name[5] == 'n') status = swapon(device, g_flags); else status = swapoff(device); if (status != 0) { bb_simple_perror_msg(device); return 1; } return 0; } static int do_em_all(void) { struct mntent *m; FILE *f; int err; f = setmntent("/etc/fstab", "r"); if (f == NULL) bb_perror_msg_and_die("/etc/fstab"); err = 0; while ((m = getmntent(f)) != NULL) { if (strcmp(m->mnt_type, MNTTYPE_SWAP) == 0) { /* swapon -a should ignore entries with noauto, * but swapoff -a should process them */ if (applet_name[5] != 'n' || hasmntopt(m, MNTOPT_NOAUTO) == NULL ) { #if ENABLE_FEATURE_SWAPON_PRI char *p; g_flags = 0; /* each swap space might have different flags */ p = hasmntopt(m, "pri"); if (p) { /* Max allowed 32767 (==SWAP_FLAG_PRIO_MASK) */ unsigned int swap_prio = MIN(bb_strtou(p + 4 , NULL, 10), SWAP_FLAG_PRIO_MASK); /* We want to allow "NNNN,foo", thus errno == EINVAL is allowed too */ if (errno != ERANGE) { g_flags = SWAP_FLAG_PREFER | (swap_prio << SWAP_FLAG_PRIO_SHIFT); } } #endif err += swap_enable_disable(m->mnt_fsname); } } } if (ENABLE_FEATURE_CLEAN_UP) endmntent(f); return err; } int swap_on_off_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int swap_on_off_main(int argc UNUSED_PARAM, char **argv) { int ret; INIT_G(); #if !ENABLE_FEATURE_SWAPON_PRI ret = getopt32(argv, "a"); #else if (applet_name[5] == 'n') opt_complementary = "p+"; ret = getopt32(argv, (applet_name[5] == 'n') ? "ap:" : "a", &g_flags); if (ret & 2) { // -p g_flags = SWAP_FLAG_PREFER | ((g_flags & SWAP_FLAG_PRIO_MASK) << SWAP_FLAG_PRIO_SHIFT); ret &= 1; } #endif if (ret /* & 1: not needed */) // -a return do_em_all(); argv += optind; if (!*argv) bb_show_usage(); /* ret = 0; redundant */ do { ret += swap_enable_disable(*argv); } while (*++argv); return ret; } ������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/fdisk_sgi.c���������������������������������������������������������������0000644�0000000�0000000�00000060757�12263563520�016231� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) Andreas Neuper, Sep 1998. * * Licensed under GPLv2, see file LICENSE in this source tree. */ #if ENABLE_FEATURE_SGI_LABEL #define SGI_DEBUG 0 #define SGI_VOLHDR 0x00 /* 1 and 2 were used for drive types no longer supported by SGI */ #define SGI_SWAP 0x03 /* 4 and 5 were for filesystem types SGI haven't ever supported on MIPS CPUs */ #define SGI_VOLUME 0x06 #define SGI_EFS 0x07 #define SGI_LVOL 0x08 #define SGI_RLVOL 0x09 #define SGI_XFS 0x0a #define SGI_XFSLOG 0x0b #define SGI_XLV 0x0c #define SGI_XVM 0x0d #define SGI_ENTIRE_DISK SGI_VOLUME struct device_parameter { /* 48 bytes */ unsigned char skew; unsigned char gap1; unsigned char gap2; unsigned char sparecyl; unsigned short pcylcount; unsigned short head_vol0; unsigned short ntrks; /* tracks in cyl 0 or vol 0 */ unsigned char cmd_tag_queue_depth; unsigned char unused0; unsigned short unused1; unsigned short nsect; /* sectors/tracks in cyl 0 or vol 0 */ unsigned short bytes; unsigned short ilfact; unsigned int flags; /* controller flags */ unsigned int datarate; unsigned int retries_on_error; unsigned int ms_per_word; unsigned short xylogics_gap1; unsigned short xylogics_syncdelay; unsigned short xylogics_readdelay; unsigned short xylogics_gap2; unsigned short xylogics_readgate; unsigned short xylogics_writecont; }; /* * controller flags */ #define SECTOR_SLIP 0x01 #define SECTOR_FWD 0x02 #define TRACK_FWD 0x04 #define TRACK_MULTIVOL 0x08 #define IGNORE_ERRORS 0x10 #define RESEEK 0x20 #define ENABLE_CMDTAGQ 0x40 typedef struct { unsigned int magic; /* expect SGI_LABEL_MAGIC */ unsigned short boot_part; /* active boot partition */ unsigned short swap_part; /* active swap partition */ unsigned char boot_file[16]; /* name of the bootfile */ struct device_parameter devparam; /* 1 * 48 bytes */ struct volume_directory { /* 15 * 16 bytes */ unsigned char vol_file_name[8]; /* a character array */ unsigned int vol_file_start; /* number of logical block */ unsigned int vol_file_size; /* number of bytes */ } directory[15]; struct sgi_partinfo { /* 16 * 12 bytes */ unsigned int num_sectors; /* number of blocks */ unsigned int start_sector; /* must be cylinder aligned */ unsigned int id; } partitions[16]; unsigned int csum; unsigned int fillbytes; } sgi_partition; typedef struct { unsigned int magic; /* looks like a magic number */ unsigned int a2; unsigned int a3; unsigned int a4; unsigned int b1; unsigned short b2; unsigned short b3; unsigned int c[16]; unsigned short d[3]; unsigned char scsi_string[50]; unsigned char serial[137]; unsigned short check1816; unsigned char installer[225]; } sgiinfo; #define SGI_LABEL_MAGIC 0x0be5a941 #define SGI_LABEL_MAGIC_SWAPPED 0x41a9e50b #define SGI_INFO_MAGIC 0x00072959 #define SGI_INFO_MAGIC_SWAPPED 0x59290700 #define SGI_SSWAP16(x) (sgi_other_endian ? fdisk_swap16(x) : (uint16_t)(x)) #define SGI_SSWAP32(x) (sgi_other_endian ? fdisk_swap32(x) : (uint32_t)(x)) #define sgilabel ((sgi_partition *)MBRbuffer) #define sgiparam (sgilabel->devparam) /* * * fdisksgilabel.c * * Copyright (C) Andreas Neuper, Sep 1998. * This file may be modified and redistributed under * the terms of the GNU Public License. * * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br> * Internationalization */ static smallint sgi_other_endian; /* bool */ static smallint sgi_volumes = 1; /* max 15 */ /* * only dealing with free blocks here */ typedef struct { unsigned int first; unsigned int last; } freeblocks; static freeblocks freelist[17]; /* 16 partitions can produce 17 vacant slots */ static void setfreelist(int i, unsigned int f, unsigned int l) { freelist[i].first = f; freelist[i].last = l; } static void add2freelist(unsigned int f, unsigned int l) { int i; for (i = 0; i < 17; i++) if (freelist[i].last == 0) break; setfreelist(i, f, l); } static void clearfreelist(void) { int i; for (i = 0; i < 17; i++) setfreelist(i, 0, 0); } static unsigned int isinfreelist(unsigned int b) { int i; for (i = 0; i < 17; i++) if (freelist[i].first <= b && freelist[i].last >= b) return freelist[i].last; return 0; } /* return last vacant block of this stride (never 0). */ /* the '>=' is not quite correct, but simplifies the code */ /* * end of free blocks section */ static const char *const sgi_sys_types[] = { /* SGI_VOLHDR */ "\x00" "SGI volhdr" , /* 0x01 */ "\x01" "SGI trkrepl" , /* 0x02 */ "\x02" "SGI secrepl" , /* SGI_SWAP */ "\x03" "SGI raw" , /* 0x04 */ "\x04" "SGI bsd" , /* 0x05 */ "\x05" "SGI sysv" , /* SGI_ENTIRE_DISK */ "\x06" "SGI volume" , /* SGI_EFS */ "\x07" "SGI efs" , /* 0x08 */ "\x08" "SGI lvol" , /* 0x09 */ "\x09" "SGI rlvol" , /* SGI_XFS */ "\x0a" "SGI xfs" , /* SGI_XFSLOG */ "\x0b" "SGI xfslog" , /* SGI_XLV */ "\x0c" "SGI xlv" , /* SGI_XVM */ "\x0d" "SGI xvm" , /* LINUX_SWAP */ "\x82" "Linux swap" , /* LINUX_NATIVE */ "\x83" "Linux native", /* LINUX_LVM */ "\x8d" "Linux LVM" , /* LINUX_RAID */ "\xfd" "Linux RAID" , NULL }; static int sgi_get_nsect(void) { return SGI_SSWAP16(sgilabel->devparam.nsect); } static int sgi_get_ntrks(void) { return SGI_SSWAP16(sgilabel->devparam.ntrks); } static unsigned int two_s_complement_32bit_sum(unsigned int* base, int size /* in bytes */) { int i = 0; unsigned int sum = 0; size /= sizeof(unsigned int); for (i = 0; i < size; i++) sum -= SGI_SSWAP32(base[i]); return sum; } void BUG_bad_sgi_partition_size(void); static int check_sgi_label(void) { if (sizeof(sgi_partition) > 512) { /* According to MIPS Computer Systems, Inc the label * must not contain more than 512 bytes */ BUG_bad_sgi_partition_size(); } if (sgilabel->magic != SGI_LABEL_MAGIC && sgilabel->magic != SGI_LABEL_MAGIC_SWAPPED ) { current_label_type = LABEL_DOS; return 0; } sgi_other_endian = (sgilabel->magic == SGI_LABEL_MAGIC_SWAPPED); /* * test for correct checksum */ if (two_s_complement_32bit_sum((unsigned int*)sgilabel, sizeof(*sgilabel))) { printf("Detected sgi disklabel with wrong checksum\n"); } update_units(); current_label_type = LABEL_SGI; g_partitions = 16; sgi_volumes = 15; return 1; } static unsigned int sgi_get_start_sector(int i) { return SGI_SSWAP32(sgilabel->partitions[i].start_sector); } static unsigned int sgi_get_num_sectors(int i) { return SGI_SSWAP32(sgilabel->partitions[i].num_sectors); } static int sgi_get_sysid(int i) { return SGI_SSWAP32(sgilabel->partitions[i].id); } static int sgi_get_bootpartition(void) { return SGI_SSWAP16(sgilabel->boot_part); } static int sgi_get_swappartition(void) { return SGI_SSWAP16(sgilabel->swap_part); } static void sgi_list_table(int xtra) { int i, w, wd; int kpi = 0; /* kernel partition ID */ if (xtra) { printf("\nDisk %s (SGI disk label): %u heads, %u sectors\n" "%u cylinders, %u physical cylinders\n" "%u extra sects/cyl, interleave %u:1\n" "%s\n" "Units = %s of %u * 512 bytes\n\n", disk_device, g_heads, g_sectors, g_cylinders, SGI_SSWAP16(sgiparam.pcylcount), SGI_SSWAP16(sgiparam.sparecyl), SGI_SSWAP16(sgiparam.ilfact), (char *)sgilabel, str_units(PLURAL), units_per_sector); } else { printf("\nDisk %s (SGI disk label): " "%u heads, %u sectors, %u cylinders\n" "Units = %s of %u * 512 bytes\n\n", disk_device, g_heads, g_sectors, g_cylinders, str_units(PLURAL), units_per_sector ); } w = strlen(disk_device); wd = sizeof("Device") - 1; if (w < wd) w = wd; printf("----- partitions -----\n" "Pt# %*s Info Start End Sectors Id System\n", w + 2, "Device"); for (i = 0; i < g_partitions; i++) { if (sgi_get_num_sectors(i) || SGI_DEBUG) { uint32_t start = sgi_get_start_sector(i); uint32_t len = sgi_get_num_sectors(i); kpi++; /* only count nonempty partitions */ printf( "%2u: %s %4s %9lu %9lu %9lu %2x %s\n", /* fdisk part number */ i+1, /* device */ partname(disk_device, kpi, w+3), /* flags */ (sgi_get_swappartition() == i) ? "swap" : /* flags */ (sgi_get_bootpartition() == i) ? "boot" : " ", /* start */ (long) scround(start), /* end */ (long) scround(start+len)-1, /* no odd flag on end */(long) len, /* type id */ sgi_get_sysid(i), /* type name */ partition_type(sgi_get_sysid(i))); } } printf("----- Bootinfo -----\nBootfile: %s\n" "----- Directory Entries -----\n", sgilabel->boot_file); for (i = 0; i < sgi_volumes; i++) { if (sgilabel->directory[i].vol_file_size) { uint32_t start = SGI_SSWAP32(sgilabel->directory[i].vol_file_start); uint32_t len = SGI_SSWAP32(sgilabel->directory[i].vol_file_size); unsigned char *name = sgilabel->directory[i].vol_file_name; printf("%2u: %-10s sector%5u size%8u\n", i, (char*)name, (unsigned int) start, (unsigned int) len); } } } static void sgi_set_bootpartition(int i) { sgilabel->boot_part = SGI_SSWAP16(((short)i)); } static unsigned int sgi_get_lastblock(void) { return g_heads * g_sectors * g_cylinders; } static void sgi_set_swappartition(int i) { sgilabel->swap_part = SGI_SSWAP16(((short)i)); } static int sgi_check_bootfile(const char* aFile) { if (strlen(aFile) < 3) /* "/a\n" is minimum */ { printf("\nInvalid Bootfile!\n" "\tThe bootfile must be an absolute non-zero pathname,\n" "\te.g. \"/unix\" or \"/unix.save\".\n"); return 0; } if (strlen(aFile) > 16) { printf("\nName of Bootfile too long (>16 bytes)\n"); return 0; } if (aFile[0] != '/') { printf("\nBootfile must have a fully qualified pathname\n"); return 0; } if (strncmp(aFile, (char*)sgilabel->boot_file, 16)) { printf("\nBe aware, that the bootfile is not checked for existence.\n" "\tSGI's default is \"/unix\" and for backup \"/unix.save\".\n"); /* filename is correct and did change */ return 1; } return 0; /* filename did not change */ } static const char * sgi_get_bootfile(void) { return (char*)sgilabel->boot_file; } static void sgi_set_bootfile(const char* aFile) { int i = 0; if (sgi_check_bootfile(aFile)) { while (i < 16) { if ((aFile[i] != '\n') /* in principle caught again by next line */ && (strlen(aFile) > i)) sgilabel->boot_file[i] = aFile[i]; else sgilabel->boot_file[i] = 0; i++; } printf("\n\tBootfile is changed to \"%s\"\n", sgilabel->boot_file); } } static void create_sgiinfo(void) { /* I keep SGI's habit to write the sgilabel to the second block */ sgilabel->directory[0].vol_file_start = SGI_SSWAP32(2); sgilabel->directory[0].vol_file_size = SGI_SSWAP32(sizeof(sgiinfo)); strncpy((char*)sgilabel->directory[0].vol_file_name, "sgilabel", 8); } static sgiinfo *fill_sgiinfo(void); static void sgi_write_table(void) { sgilabel->csum = 0; sgilabel->csum = SGI_SSWAP32(two_s_complement_32bit_sum( (unsigned int*)sgilabel, sizeof(*sgilabel))); assert(two_s_complement_32bit_sum( (unsigned int*)sgilabel, sizeof(*sgilabel)) == 0); write_sector(0, sgilabel); if (!strncmp((char*)sgilabel->directory[0].vol_file_name, "sgilabel", 8)) { /* * keep this habit of first writing the "sgilabel". * I never tested whether it works without (AN 981002). */ sgiinfo *info = fill_sgiinfo(); int infostartblock = SGI_SSWAP32(sgilabel->directory[0].vol_file_start); write_sector(infostartblock, info); free(info); } } static int compare_start(int *x, int *y) { /* * sort according to start sectors * and prefers largest partition: * entry zero is entire disk entry */ unsigned int i = *x; unsigned int j = *y; unsigned int a = sgi_get_start_sector(i); unsigned int b = sgi_get_start_sector(j); unsigned int c = sgi_get_num_sectors(i); unsigned int d = sgi_get_num_sectors(j); if (a == b) return (d > c) ? 1 : (d == c) ? 0 : -1; return (a > b) ? 1 : -1; } static int verify_sgi(int verbose) { int Index[16]; /* list of valid partitions */ int sortcount = 0; /* number of used partitions, i.e. non-zero lengths */ int entire = 0, i = 0; unsigned int start = 0; long long gap = 0; /* count unused blocks */ unsigned int lastblock = sgi_get_lastblock(); clearfreelist(); for (i = 0; i < 16; i++) { if (sgi_get_num_sectors(i) != 0) { Index[sortcount++] = i; if (sgi_get_sysid(i) == SGI_ENTIRE_DISK) { if (entire++ == 1) { if (verbose) printf("More than one entire disk entry present\n"); } } } } if (sortcount == 0) { if (verbose) printf("No partitions defined\n"); return (lastblock > 0) ? 1 : (lastblock == 0) ? 0 : -1; } qsort(Index, sortcount, sizeof(Index[0]), (void*)compare_start); if (sgi_get_sysid(Index[0]) == SGI_ENTIRE_DISK) { if ((Index[0] != 10) && verbose) printf("IRIX likes when Partition 11 covers the entire disk\n"); if ((sgi_get_start_sector(Index[0]) != 0) && verbose) printf("The entire disk partition should start " "at block 0,\n" "not at diskblock %u\n", sgi_get_start_sector(Index[0])); if (SGI_DEBUG) /* I do not understand how some disks fulfil it */ if ((sgi_get_num_sectors(Index[0]) != lastblock) && verbose) printf("The entire disk partition is only %u diskblock large,\n" "but the disk is %u diskblocks long\n", sgi_get_num_sectors(Index[0]), lastblock); lastblock = sgi_get_num_sectors(Index[0]); } else { if (verbose) printf("One Partition (#11) should cover the entire disk\n"); if (SGI_DEBUG > 2) printf("sysid=%u\tpartition=%u\n", sgi_get_sysid(Index[0]), Index[0]+1); } for (i = 1, start = 0; i < sortcount; i++) { int cylsize = sgi_get_nsect() * sgi_get_ntrks(); if ((sgi_get_start_sector(Index[i]) % cylsize) != 0) { if (SGI_DEBUG) /* I do not understand how some disks fulfil it */ if (verbose) printf("Partition %u does not start on cylinder boundary\n", Index[i]+1); } if (sgi_get_num_sectors(Index[i]) % cylsize != 0) { if (SGI_DEBUG) /* I do not understand how some disks fulfil it */ if (verbose) printf("Partition %u does not end on cylinder boundary\n", Index[i]+1); } /* We cannot handle several "entire disk" entries. */ if (sgi_get_sysid(Index[i]) == SGI_ENTIRE_DISK) continue; if (start > sgi_get_start_sector(Index[i])) { if (verbose) printf("Partitions %u and %u overlap by %u sectors\n", Index[i-1]+1, Index[i]+1, start - sgi_get_start_sector(Index[i])); if (gap > 0) gap = -gap; if (gap == 0) gap = -1; } if (start < sgi_get_start_sector(Index[i])) { if (verbose) printf("Unused gap of %u sectors - sectors %u-%u\n", sgi_get_start_sector(Index[i]) - start, start, sgi_get_start_sector(Index[i])-1); gap += sgi_get_start_sector(Index[i]) - start; add2freelist(start, sgi_get_start_sector(Index[i])); } start = sgi_get_start_sector(Index[i]) + sgi_get_num_sectors(Index[i]); if (SGI_DEBUG > 1) { if (verbose) printf("%2u:%12u\t%12u\t%12u\n", Index[i], sgi_get_start_sector(Index[i]), sgi_get_num_sectors(Index[i]), sgi_get_sysid(Index[i])); } } if (start < lastblock) { if (verbose) printf("Unused gap of %u sectors - sectors %u-%u\n", lastblock - start, start, lastblock-1); gap += lastblock - start; add2freelist(start, lastblock); } /* * Done with arithmetics * Go for details now */ if (verbose) { if (!sgi_get_num_sectors(sgi_get_bootpartition())) { printf("\nThe boot partition does not exist\n"); } if (!sgi_get_num_sectors(sgi_get_swappartition())) { printf("\nThe swap partition does not exist\n"); } else { if ((sgi_get_sysid(sgi_get_swappartition()) != SGI_SWAP) && (sgi_get_sysid(sgi_get_swappartition()) != LINUX_SWAP)) printf("\nThe swap partition has no swap type\n"); } if (sgi_check_bootfile("/unix")) printf("\tYou have chosen an unusual boot file name\n"); } return (gap > 0) ? 1 : (gap == 0) ? 0 : -1; } static int sgi_gaps(void) { /* * returned value is: * = 0 : disk is properly filled to the rim * < 0 : there is an overlap * > 0 : there is still some vacant space */ return verify_sgi(0); } static void sgi_change_sysid(int i, int sys) { if (sgi_get_num_sectors(i) == 0) { /* caught already before, ... */ printf("Sorry you may change the Tag of non-empty partitions\n"); return; } if ((sys != SGI_ENTIRE_DISK) && (sys != SGI_VOLHDR) && (sgi_get_start_sector(i) < 1) ) { read_maybe_empty( "It is highly recommended that the partition at offset 0\n" "is of type \"SGI volhdr\", the IRIX system will rely on it to\n" "retrieve from its directory standalone tools like sash and fx.\n" "Only the \"SGI volume\" entire disk section may violate this.\n" "Type YES if you are sure about tagging this partition differently.\n"); if (strcmp(line_ptr, "YES\n") != 0) return; } sgilabel->partitions[i].id = SGI_SSWAP32(sys); } /* returns partition index of first entry marked as entire disk */ static int sgi_entire(void) { int i; for (i = 0; i < 16; i++) if (sgi_get_sysid(i) == SGI_VOLUME) return i; return -1; } static void sgi_set_partition(int i, unsigned int start, unsigned int length, int sys) { sgilabel->partitions[i].id = SGI_SSWAP32(sys); sgilabel->partitions[i].num_sectors = SGI_SSWAP32(length); sgilabel->partitions[i].start_sector = SGI_SSWAP32(start); set_changed(i); if (sgi_gaps() < 0) /* rebuild freelist */ printf("Partition overlap detected\n"); } static void sgi_set_entire(void) { int n; for (n = 10; n < g_partitions; n++) { if (!sgi_get_num_sectors(n) ) { sgi_set_partition(n, 0, sgi_get_lastblock(), SGI_VOLUME); break; } } } static void sgi_set_volhdr(void) { int n; for (n = 8; n < g_partitions; n++) { if (!sgi_get_num_sectors(n)) { /* * 5 cylinders is an arbitrary value I like * IRIX 5.3 stored files in the volume header * (like sash, symmon, fx, ide) with ca. 3200 * sectors. */ if (g_heads * g_sectors * 5 < sgi_get_lastblock()) sgi_set_partition(n, 0, g_heads * g_sectors * 5, SGI_VOLHDR); break; } } } static void sgi_delete_partition(int i) { sgi_set_partition(i, 0, 0, 0); } static void sgi_add_partition(int n, int sys) { char mesg[256]; unsigned int first = 0, last = 0; if (n == 10) { sys = SGI_VOLUME; } else if (n == 8) { sys = 0; } if (sgi_get_num_sectors(n)) { printf(msg_part_already_defined, n + 1); return; } if ((sgi_entire() == -1) && (sys != SGI_VOLUME)) { printf("Attempting to generate entire disk entry automatically\n"); sgi_set_entire(); sgi_set_volhdr(); } if ((sgi_gaps() == 0) && (sys != SGI_VOLUME)) { printf("The entire disk is already covered with partitions\n"); return; } if (sgi_gaps() < 0) { printf("You got a partition overlap on the disk. Fix it first!\n"); return; } snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR)); while (1) { if (sys == SGI_VOLUME) { last = sgi_get_lastblock(); first = read_int(0, 0, last-1, 0, mesg); if (first != 0) { printf("It is highly recommended that eleventh partition\n" "covers the entire disk and is of type 'SGI volume'\n"); } } else { first = freelist[0].first; last = freelist[0].last; first = read_int(scround(first), scround(first), scround(last)-1, 0, mesg); } if (display_in_cyl_units) first *= units_per_sector; else first = first; /* align to cylinder if you know how ... */ if (!last ) last = isinfreelist(first); if (last != 0) break; printf("You will get a partition overlap on the disk. " "Fix it first!\n"); } snprintf(mesg, sizeof(mesg), " Last %s", str_units(SINGULAR)); last = read_int(scround(first), scround(last)-1, scround(last)-1, scround(first), mesg)+1; if (display_in_cyl_units) last *= units_per_sector; else last = last; /* align to cylinder if You know how ... */ if ( (sys == SGI_VOLUME) && (first != 0 || last != sgi_get_lastblock() ) ) printf("It is highly recommended that eleventh partition\n" "covers the entire disk and is of type 'SGI volume'\n"); sgi_set_partition(n, first, last-first, sys); } #if ENABLE_FEATURE_FDISK_ADVANCED static void create_sgilabel(void) { struct hd_geometry geometry; struct { unsigned int start; unsigned int nsect; int sysid; } old[4]; int i = 0; long longsectors; /* the number of sectors on the device */ int res; /* the result from the ioctl */ int sec_fac; /* the sector factor */ sec_fac = sector_size / 512; /* determine the sector factor */ printf(msg_building_new_label, "SGI disklabel"); sgi_other_endian = BB_LITTLE_ENDIAN; res = ioctl(dev_fd, BLKGETSIZE, &longsectors); if (!ioctl(dev_fd, HDIO_GETGEO, &geometry)) { g_heads = geometry.heads; g_sectors = geometry.sectors; if (res == 0) { /* the get device size ioctl was successful */ g_cylinders = longsectors / (g_heads * g_sectors); g_cylinders /= sec_fac; } else { /* otherwise print error and use truncated version */ g_cylinders = geometry.cylinders; printf( "Warning: BLKGETSIZE ioctl failed on %s. Using geometry cylinder value of %u.\n" "This value may be truncated for devices > 33.8 GB.\n", disk_device, g_cylinders); } } for (i = 0; i < 4; i++) { old[i].sysid = 0; if (valid_part_table_flag(MBRbuffer)) { if (get_part_table(i)->sys_ind) { old[i].sysid = get_part_table(i)->sys_ind; old[i].start = get_start_sect(get_part_table(i)); old[i].nsect = get_nr_sects(get_part_table(i)); printf("Trying to keep parameters of partition %u\n", i); if (SGI_DEBUG) printf("ID=%02x\tSTART=%u\tLENGTH=%u\n", old[i].sysid, old[i].start, old[i].nsect); } } } memset(MBRbuffer, 0, sizeof(MBRbuffer)); /* fields with '//' are already zeroed out by memset above */ sgilabel->magic = SGI_SSWAP32(SGI_LABEL_MAGIC); //sgilabel->boot_part = SGI_SSWAP16(0); sgilabel->swap_part = SGI_SSWAP16(1); //memset(sgilabel->boot_file, 0, 16); strcpy((char*)sgilabel->boot_file, "/unix"); /* sizeof(sgilabel->boot_file) == 16 > 6 */ //sgilabel->devparam.skew = (0); //sgilabel->devparam.gap1 = (0); //sgilabel->devparam.gap2 = (0); //sgilabel->devparam.sparecyl = (0); sgilabel->devparam.pcylcount = SGI_SSWAP16(geometry.cylinders); //sgilabel->devparam.head_vol0 = SGI_SSWAP16(0); /* tracks/cylinder (heads) */ sgilabel->devparam.ntrks = SGI_SSWAP16(geometry.heads); //sgilabel->devparam.cmd_tag_queue_depth = (0); //sgilabel->devparam.unused0 = (0); //sgilabel->devparam.unused1 = SGI_SSWAP16(0); /* sectors/track */ sgilabel->devparam.nsect = SGI_SSWAP16(geometry.sectors); sgilabel->devparam.bytes = SGI_SSWAP16(512); sgilabel->devparam.ilfact = SGI_SSWAP16(1); sgilabel->devparam.flags = SGI_SSWAP32(TRACK_FWD| IGNORE_ERRORS|RESEEK); //sgilabel->devparam.datarate = SGI_SSWAP32(0); sgilabel->devparam.retries_on_error = SGI_SSWAP32(1); //sgilabel->devparam.ms_per_word = SGI_SSWAP32(0); //sgilabel->devparam.xylogics_gap1 = SGI_SSWAP16(0); //sgilabel->devparam.xylogics_syncdelay = SGI_SSWAP16(0); //sgilabel->devparam.xylogics_readdelay = SGI_SSWAP16(0); //sgilabel->devparam.xylogics_gap2 = SGI_SSWAP16(0); //sgilabel->devparam.xylogics_readgate = SGI_SSWAP16(0); //sgilabel->devparam.xylogics_writecont = SGI_SSWAP16(0); //memset( &(sgilabel->directory), 0, sizeof(struct volume_directory)*15 ); //memset( &(sgilabel->partitions), 0, sizeof(struct sgi_partinfo)*16 ); current_label_type = LABEL_SGI; g_partitions = 16; sgi_volumes = 15; sgi_set_entire(); sgi_set_volhdr(); for (i = 0; i < 4; i++) { if (old[i].sysid) { sgi_set_partition(i, old[i].start, old[i].nsect, old[i].sysid); } } } static void sgi_set_xcyl(void) { /* do nothing in the beginning */ } #endif /* FEATURE_FDISK_ADVANCED */ /* _____________________________________________________________ */ static sgiinfo * fill_sgiinfo(void) { sgiinfo *info = xzalloc(sizeof(sgiinfo)); info->magic = SGI_SSWAP32(SGI_INFO_MAGIC); info->b1 = SGI_SSWAP32(-1); info->b2 = SGI_SSWAP16(-1); info->b3 = SGI_SSWAP16(1); /* You may want to replace this string !!!!!!! */ strcpy( (char*)info->scsi_string, "IBM OEM 0662S12 3 30" ); strcpy( (char*)info->serial, "0000" ); info->check1816 = SGI_SSWAP16(18*256 +16 ); strcpy( (char*)info->installer, "Sfx version 5.3, Oct 18, 1994" ); return info; } #endif /* SGI_LABEL */ �����������������busybox-1.22.1/util-linux/minix.h�������������������������������������������������������������������0000644�0000000�0000000�00000004551�12263563520�015406� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * This is the original minix inode layout on disk. * Note the 8-bit gid and atime and ctime. */ struct minix1_inode { uint16_t i_mode; uint16_t i_uid; uint32_t i_size; uint32_t i_time; uint8_t i_gid; uint8_t i_nlinks; uint16_t i_zone[9]; }; /* * The new minix inode has all the time entries, as well as * long block numbers and a third indirect block (7+1+1+1 * instead of 7+1+1). Also, some previously 8-bit values are * now 16-bit. The inode is now 64 bytes instead of 32. */ struct minix2_inode { uint16_t i_mode; uint16_t i_nlinks; uint16_t i_uid; uint16_t i_gid; uint32_t i_size; uint32_t i_atime; uint32_t i_mtime; uint32_t i_ctime; uint32_t i_zone[10]; }; /* * minix superblock data on disk */ struct minix_superblock { uint16_t s_ninodes; uint16_t s_nzones; uint16_t s_imap_blocks; uint16_t s_zmap_blocks; uint16_t s_firstdatazone; uint16_t s_log_zone_size; uint32_t s_max_size; uint16_t s_magic; uint16_t s_state; uint32_t s_zones; }; struct minix_dir_entry { uint16_t inode; char name[]; }; /* Believe it or not, but mount.h has this one #defined */ #undef BLOCK_SIZE enum { BLOCK_SIZE = 1024, BITS_PER_BLOCK = BLOCK_SIZE << 3, MINIX_ROOT_INO = 1, MINIX_BAD_INO = 2, MINIX1_SUPER_MAGIC = 0x137F, /* original minix fs */ MINIX1_SUPER_MAGIC2 = 0x138F, /* minix fs, 30 char names */ MINIX2_SUPER_MAGIC = 0x2468, /* minix V2 fs */ MINIX2_SUPER_MAGIC2 = 0x2478, /* minix V2 fs, 30 char names */ MINIX_VALID_FS = 0x0001, /* clean fs */ MINIX_ERROR_FS = 0x0002, /* fs has errors */ INODE_SIZE1 = sizeof(struct minix1_inode), INODE_SIZE2 = sizeof(struct minix2_inode), MINIX1_INODES_PER_BLOCK = BLOCK_SIZE / sizeof(struct minix1_inode), MINIX2_INODES_PER_BLOCK = BLOCK_SIZE / sizeof(struct minix2_inode), }; /* Basic test script for regressions in mkfs/fsck. Copies current dir into image (typically bbox build tree). #!/bin/sh tmpdir=/tmp/minixtest-$$ tmpimg=/tmp/minix-img-$$ mkdir $tmpdir dd if=/dev/zero of=$tmpimg bs=1M count=20 || exit ./busybox mkfs.minix $tmpimg || exit mount -o loop $tmpimg $tmpdir || exit cp -a "$PWD" $tmpdir umount $tmpdir || exit ./busybox fsck.minix -vfm $tmpimg || exit echo "Continue?" read junk ./busybox fsck.minix -vfml $tmpimg || exit rmdir $tmpdir rm $tmpimg */ �������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/mount.c�������������������������������������������������������������������0000644�0000000�0000000�00000165471�12263563520�015430� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Mini mount implementation for busybox * * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>. * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net> * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ // Design notes: There is no spec for mount. Remind me to write one. // // mount_main() calls singlemount() which calls mount_it_now(). // // mount_main() can loop through /etc/fstab for mount -a // singlemount() can loop through /etc/filesystems for fstype detection. // mount_it_now() does the actual mount. // //usage:#define mount_trivial_usage //usage: "[OPTIONS] [-o OPTS] DEVICE NODE" //usage:#define mount_full_usage "\n\n" //usage: "Mount a filesystem. Filesystem autodetection requires /proc.\n" //usage: "\n -a Mount all filesystems in fstab" //usage: IF_FEATURE_MOUNT_FAKE( //usage: IF_FEATURE_MTAB_SUPPORT( //usage: "\n -f Update /etc/mtab, but don't mount" //usage: ) //usage: IF_NOT_FEATURE_MTAB_SUPPORT( //usage: "\n -f Dry run" //usage: ) //usage: ) //usage: IF_FEATURE_MOUNT_HELPERS( //usage: "\n -i Don't run mount helper" //usage: ) //usage: IF_FEATURE_MTAB_SUPPORT( //usage: "\n -n Don't update /etc/mtab" //usage: ) //usage: IF_FEATURE_MOUNT_VERBOSE( //usage: "\n -v Verbose" //usage: ) ////usage: "\n -s Sloppy (ignored)" //usage: "\n -r Read-only mount" //usage: "\n -w Read-write mount (default)" //usage: "\n -t FSTYPE[,...] Filesystem type(s)" //usage: "\n -O OPT Mount only filesystems with option OPT (-a only)" //usage: "\n-o OPT:" //usage: IF_FEATURE_MOUNT_LOOP( //usage: "\n loop Ignored (loop devices are autodetected)" //usage: ) //usage: IF_FEATURE_MOUNT_FLAGS( //usage: "\n [a]sync Writes are [a]synchronous" //usage: "\n [no]atime Disable/enable updates to inode access times" //usage: "\n [no]diratime Disable/enable atime updates to directories" //usage: "\n [no]relatime Disable/enable atime updates relative to modification time" //usage: "\n [no]dev (Dis)allow use of special device files" //usage: "\n [no]exec (Dis)allow use of executable files" //usage: "\n [no]suid (Dis)allow set-user-id-root programs" //usage: "\n [r]shared Convert [recursively] to a shared subtree" //usage: "\n [r]slave Convert [recursively] to a slave subtree" //usage: "\n [r]private Convert [recursively] to a private subtree" //usage: "\n [un]bindable Make mount point [un]able to be bind mounted" //usage: "\n [r]bind Bind a file or directory [recursively] to another location" //usage: "\n move Relocate an existing mount point" //usage: ) //usage: "\n remount Remount a mounted filesystem, changing flags" //usage: "\n ro/rw Same as -r/-w" //usage: "\n" //usage: "\nThere are filesystem-specific -o flags." //usage: //usage:#define mount_example_usage //usage: "$ mount\n" //usage: "/dev/hda3 on / type minix (rw)\n" //usage: "proc on /proc type proc (rw)\n" //usage: "devpts on /dev/pts type devpts (rw)\n" //usage: "$ mount /dev/fd0 /mnt -t msdos -o ro\n" //usage: "$ mount /tmp/diskimage /opt -t ext2 -o loop\n" //usage: "$ mount cd_image.iso mydir\n" //usage:#define mount_notes_usage //usage: "Returns 0 for success, number of failed mounts for -a, or errno for one mount." #include <mntent.h> #include <syslog.h> #include <sys/mount.h> // Grab more as needed from util-linux's mount/mount_constants.h #ifndef MS_DIRSYNC # define MS_DIRSYNC (1 << 7) // Directory modifications are synchronous #endif #ifndef MS_UNION # define MS_UNION (1 << 8) #endif #ifndef MS_BIND # define MS_BIND (1 << 12) #endif #ifndef MS_MOVE # define MS_MOVE (1 << 13) #endif #ifndef MS_RECURSIVE # define MS_RECURSIVE (1 << 14) #endif #ifndef MS_SILENT # define MS_SILENT (1 << 15) #endif // The shared subtree stuff, which went in around 2.6.15 #ifndef MS_UNBINDABLE # define MS_UNBINDABLE (1 << 17) #endif #ifndef MS_PRIVATE # define MS_PRIVATE (1 << 18) #endif #ifndef MS_SLAVE # define MS_SLAVE (1 << 19) #endif #ifndef MS_SHARED # define MS_SHARED (1 << 20) #endif #ifndef MS_RELATIME # define MS_RELATIME (1 << 21) #endif #ifndef MS_STRICTATIME # define MS_STRICTATIME (1 << 24) #endif /* Any ~MS_FOO value has this bit set: */ #define BB_MS_INVERTED_VALUE (1u << 31) #include "libbb.h" #if ENABLE_FEATURE_MOUNT_LABEL # include "volume_id.h" #else # define resolve_mount_spec(fsname) ((void)0) #endif // Needed for nfs support only #include <sys/utsname.h> #undef TRUE #undef FALSE #if ENABLE_FEATURE_MOUNT_NFS /* This is just a warning of a common mistake. Possibly this should be a * uclibc faq entry rather than in busybox... */ # if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) # error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support" # endif # include <rpc/rpc.h> # include <rpc/pmap_prot.h> # include <rpc/pmap_clnt.h> #endif #if defined(__dietlibc__) // 16.12.2006, Sampo Kellomaki (sampo@iki.fi) // dietlibc-0.30 does not have implementation of getmntent_r() static struct mntent *getmntent_r(FILE* stream, struct mntent* result, char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM) { struct mntent* ment = getmntent(stream); return memcpy(result, ment, sizeof(*ment)); } #endif // Not real flags, but we want to be able to check for this. enum { MOUNT_USERS = (1 << 28) * ENABLE_DESKTOP, MOUNT_NOAUTO = (1 << 29), MOUNT_SWAP = (1 << 30), }; #define OPTION_STR "o:t:rwanfvsiO:" enum { OPT_o = (1 << 0), OPT_t = (1 << 1), OPT_r = (1 << 2), OPT_w = (1 << 3), OPT_a = (1 << 4), OPT_n = (1 << 5), OPT_f = (1 << 6), OPT_v = (1 << 7), OPT_s = (1 << 8), OPT_i = (1 << 9), OPT_O = (1 << 10), }; #if ENABLE_FEATURE_MTAB_SUPPORT #define USE_MTAB (!(option_mask32 & OPT_n)) #else #define USE_MTAB 0 #endif #if ENABLE_FEATURE_MOUNT_FAKE #define FAKE_IT (option_mask32 & OPT_f) #else #define FAKE_IT 0 #endif #if ENABLE_FEATURE_MOUNT_HELPERS #define HELPERS_ALLOWED (!(option_mask32 & OPT_i)) #else #define HELPERS_ALLOWED 0 #endif // TODO: more "user" flag compatibility. // "user" option (from mount manpage): // Only the user that mounted a filesystem can unmount it again. // If any user should be able to unmount, then use users instead of user // in the fstab line. The owner option is similar to the user option, // with the restriction that the user must be the owner of the special file. // This may be useful e.g. for /dev/fd if a login script makes // the console user owner of this device. // Standard mount options (from -o options or --options), // with corresponding flags static const int32_t mount_options[] = { // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs. IF_FEATURE_MOUNT_LOOP( /* "loop" */ 0, ) IF_FEATURE_MOUNT_FSTAB( /* "defaults" */ 0, /* "quiet" 0 - do not filter out, vfat wants to see it */ /* "noauto" */ MOUNT_NOAUTO, /* "sw" */ MOUNT_SWAP, /* "swap" */ MOUNT_SWAP, IF_DESKTOP(/* "user" */ MOUNT_USERS,) IF_DESKTOP(/* "users" */ MOUNT_USERS,) /* "_netdev" */ 0, IF_DESKTOP(/* "comment=" */ 0,) /* systemd uses this in fstab */ ) IF_FEATURE_MOUNT_FLAGS( // vfs flags /* "nosuid" */ MS_NOSUID, /* "suid" */ ~MS_NOSUID, /* "dev" */ ~MS_NODEV, /* "nodev" */ MS_NODEV, /* "exec" */ ~MS_NOEXEC, /* "noexec" */ MS_NOEXEC, /* "sync" */ MS_SYNCHRONOUS, /* "dirsync" */ MS_DIRSYNC, /* "async" */ ~MS_SYNCHRONOUS, /* "atime" */ ~MS_NOATIME, /* "noatime" */ MS_NOATIME, /* "diratime" */ ~MS_NODIRATIME, /* "nodiratime" */ MS_NODIRATIME, /* "mand" */ MS_MANDLOCK, /* "nomand" */ ~MS_MANDLOCK, /* "relatime" */ MS_RELATIME, /* "norelatime" */ ~MS_RELATIME, /* "strictatime" */ MS_STRICTATIME, /* "loud" */ ~MS_SILENT, /* "rbind" */ MS_BIND|MS_RECURSIVE, // action flags /* "union" */ MS_UNION, /* "bind" */ MS_BIND, /* "move" */ MS_MOVE, /* "shared" */ MS_SHARED, /* "slave" */ MS_SLAVE, /* "private" */ MS_PRIVATE, /* "unbindable" */ MS_UNBINDABLE, /* "rshared" */ MS_SHARED|MS_RECURSIVE, /* "rslave" */ MS_SLAVE|MS_RECURSIVE, /* "rprivate" */ MS_PRIVATE|MS_RECURSIVE, /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE, ) // Always understood. /* "ro" */ MS_RDONLY, // vfs flag /* "rw" */ ~MS_RDONLY, // vfs flag /* "remount" */ MS_REMOUNT // action flag }; static const char mount_option_str[] = IF_FEATURE_MOUNT_LOOP( "loop\0" ) IF_FEATURE_MOUNT_FSTAB( "defaults\0" // "quiet\0" - do not filter out, vfat wants to see it "noauto\0" "sw\0" "swap\0" IF_DESKTOP("user\0") IF_DESKTOP("users\0") "_netdev\0" IF_DESKTOP("comment=\0") /* systemd uses this in fstab */ ) IF_FEATURE_MOUNT_FLAGS( // vfs flags "nosuid\0" "suid\0" "dev\0" "nodev\0" "exec\0" "noexec\0" "sync\0" "dirsync\0" "async\0" "atime\0" "noatime\0" "diratime\0" "nodiratime\0" "mand\0" "nomand\0" "relatime\0" "norelatime\0" "strictatime\0" "loud\0" "rbind\0" // action flags "union\0" "bind\0" "move\0" "make-shared\0" "make-slave\0" "make-private\0" "make-unbindable\0" "make-rshared\0" "make-rslave\0" "make-rprivate\0" "make-runbindable\0" ) // Always understood. "ro\0" // vfs flag "rw\0" // vfs flag "remount\0" // action flag ; struct globals { #if ENABLE_FEATURE_MOUNT_NFS smalluint nfs_mount_version; #endif #if ENABLE_FEATURE_MOUNT_VERBOSE unsigned verbose; #endif llist_t *fslist; char getmntent_buf[1]; } FIX_ALIASING; enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) }; #define G (*(struct globals*)&bb_common_bufsiz1) #define nfs_mount_version (G.nfs_mount_version) #if ENABLE_FEATURE_MOUNT_VERBOSE #define verbose (G.verbose ) #else #define verbose 0 #endif #define fslist (G.fslist ) #define getmntent_buf (G.getmntent_buf ) #define INIT_G() do { } while (0) #if ENABLE_FEATURE_MTAB_SUPPORT /* * update_mtab_entry_on_move() is used to update entry in case of mount --move. * we are looking for existing entries mnt_dir which is equal to mnt_fsname of * input mntent and replace it by new one. */ static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp) { struct mntent *entries, *m; int i, count; FILE *mountTable; mountTable = setmntent(bb_path_mtab_file, "r"); if (!mountTable) { bb_perror_msg(bb_path_mtab_file); return; } entries = NULL; count = 0; while ((m = getmntent(mountTable)) != NULL) { entries = xrealloc_vector(entries, 3, count); entries[count].mnt_fsname = xstrdup(m->mnt_fsname); entries[count].mnt_dir = xstrdup(m->mnt_dir); entries[count].mnt_type = xstrdup(m->mnt_type); entries[count].mnt_opts = xstrdup(m->mnt_opts); entries[count].mnt_freq = m->mnt_freq; entries[count].mnt_passno = m->mnt_passno; count++; } endmntent(mountTable); mountTable = setmntent(bb_path_mtab_file, "w"); if (mountTable) { for (i = 0; i < count; i++) { if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0) addmntent(mountTable, &entries[i]); else addmntent(mountTable, mp); } endmntent(mountTable); } else if (errno != EROFS) bb_perror_msg(bb_path_mtab_file); if (ENABLE_FEATURE_CLEAN_UP) { for (i = 0; i < count; i++) { free(entries[i].mnt_fsname); free(entries[i].mnt_dir); free(entries[i].mnt_type); free(entries[i].mnt_opts); } free(entries); } } #endif #if ENABLE_FEATURE_MOUNT_VERBOSE static int verbose_mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) { int rc; errno = 0; rc = mount(source, target, filesystemtype, mountflags, data); if (verbose >= 2) bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d", source, target, filesystemtype, mountflags, (char*)data, rc); return rc; } #else #define verbose_mount(...) mount(__VA_ARGS__) #endif // Append mount options to string static void append_mount_options(char **oldopts, const char *newopts) { if (*oldopts && **oldopts) { // Do not insert options which are already there while (newopts[0]) { char *p; int len = strlen(newopts); p = strchr(newopts, ','); if (p) len = p - newopts; p = *oldopts; while (1) { if (!strncmp(p, newopts, len) && (p[len] == ',' || p[len] == '\0')) goto skip; p = strchr(p,','); if (!p) break; p++; } p = xasprintf("%s,%.*s", *oldopts, len, newopts); free(*oldopts); *oldopts = p; skip: newopts += len; while (newopts[0] == ',') newopts++; } } else { if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts); *oldopts = xstrdup(newopts); } } // Use the mount_options list to parse options into flags. // Also update list of unrecognized options if unrecognized != NULL static unsigned long parse_mount_options(char *options, char **unrecognized) { unsigned long flags = MS_SILENT; // Loop through options for (;;) { unsigned i; char *comma = strchr(options, ','); const char *option_str = mount_option_str; if (comma) *comma = '\0'; // FIXME: use hasmntopt() // Find this option in mount_options for (i = 0; i < ARRAY_SIZE(mount_options); i++) { unsigned opt_len = strlen(option_str); if (strncasecmp(option_str, options, opt_len) == 0 && (options[opt_len] == '\0' /* or is it "comment=" thingy in fstab? */ IF_FEATURE_MOUNT_FSTAB(IF_DESKTOP( || option_str[opt_len-1] == '=' )) ) ) { unsigned long fl = mount_options[i]; if (fl & BB_MS_INVERTED_VALUE) flags &= fl; else flags |= fl; goto found; } option_str += opt_len + 1; } // We did not recognize this option. // If "unrecognized" is not NULL, append option there. // Note that we should not append *empty* option - // in this case we want to pass NULL, not "", to "data" // parameter of mount(2) syscall. // This is crucial for filesystems that don't accept // any arbitrary mount options, like cgroup fs: // "mount -t cgroup none /mnt" if (options[0] && unrecognized) { // Add it to strflags, to pass on to kernel char *p = *unrecognized; unsigned len = p ? strlen(p) : 0; *unrecognized = p = xrealloc(p, len + strlen(options) + 2); // Comma separated if it's not the first one if (len) p[len++] = ','; strcpy(p + len, options); } found: if (!comma) break; // Advance to next option *comma = ','; options = ++comma; } return flags; } // Return a list of all block device backed filesystems static llist_t *get_block_backed_filesystems(void) { static const char filesystems[2][sizeof("/proc/filesystems")] = { "/etc/filesystems", "/proc/filesystems", }; char *fs, *buf; llist_t *list = NULL; int i; FILE *f; for (i = 0; i < 2; i++) { f = fopen_for_read(filesystems[i]); if (!f) continue; while ((buf = xmalloc_fgetline(f)) != NULL) { if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5])) goto next; fs = skip_whitespace(buf); if (*fs == '#' || *fs == '*' || !*fs) goto next; llist_add_to_end(&list, xstrdup(fs)); next: free(buf); } if (ENABLE_FEATURE_CLEAN_UP) fclose(f); } return list; } #if ENABLE_FEATURE_CLEAN_UP static void delete_block_backed_filesystems(void) { llist_free(fslist, free); } #else void delete_block_backed_filesystems(void); #endif // Perform actual mount of specific filesystem at specific location. // NB: mp->xxx fields may be trashed on exit static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts) { int rc = 0; if (FAKE_IT) { if (verbose >= 2) bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')", mp->mnt_fsname, mp->mnt_dir, mp->mnt_type, vfsflags, filteropts); goto mtab; } // Mount, with fallback to read-only if necessary. for (;;) { errno = 0; rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type, vfsflags, filteropts); // If mount failed, try // helper program mount.<mnt_type> if (HELPERS_ALLOWED && rc && mp->mnt_type) { char *args[8]; int errno_save = errno; args[0] = xasprintf("mount.%s", mp->mnt_type); rc = 1; if (FAKE_IT) args[rc++] = (char *)"-f"; if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB) args[rc++] = (char *)"-n"; args[rc++] = mp->mnt_fsname; args[rc++] = mp->mnt_dir; if (filteropts) { args[rc++] = (char *)"-o"; args[rc++] = filteropts; } args[rc] = NULL; rc = spawn_and_wait(args); free(args[0]); if (!rc) break; errno = errno_save; } if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS)) break; if (!(vfsflags & MS_SILENT)) bb_error_msg("%s is write-protected, mounting read-only", mp->mnt_fsname); vfsflags |= MS_RDONLY; } // Abort entirely if permission denied. if (rc && errno == EPERM) bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); // If the mount was successful, and we're maintaining an old-style // mtab file by hand, add the new entry to it now. mtab: if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) { char *fsname; FILE *mountTable = setmntent(bb_path_mtab_file, "a+"); const char *option_str = mount_option_str; int i; if (!mountTable) { bb_perror_msg(bb_path_mtab_file); goto ret; } // Add vfs string flags for (i = 0; mount_options[i] != MS_REMOUNT; i++) { if (mount_options[i] > 0 && (mount_options[i] & vfsflags)) append_mount_options(&(mp->mnt_opts), option_str); option_str += strlen(option_str) + 1; } // Remove trailing / (if any) from directory we mounted on i = strlen(mp->mnt_dir) - 1; while (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i--] = '\0'; // Convert to canonical pathnames as needed mp->mnt_dir = bb_simplify_path(mp->mnt_dir); fsname = NULL; if (!mp->mnt_type || !*mp->mnt_type) { // bind mount mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname); mp->mnt_type = (char*)"bind"; } mp->mnt_freq = mp->mnt_passno = 0; // Write and close #if ENABLE_FEATURE_MTAB_SUPPORT if (vfsflags & MS_MOVE) update_mtab_entry_on_move(mp); else #endif addmntent(mountTable, mp); endmntent(mountTable); if (ENABLE_FEATURE_CLEAN_UP) { free(mp->mnt_dir); free(fsname); } } ret: return rc; } #if ENABLE_FEATURE_MOUNT_NFS /* * Linux NFS mount * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com> * * Licensed under GPLv2, see file LICENSE in this source tree. * * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port * numbers to be specified on the command line. * * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>: * Omit the call to connect() for Linux version 1.3.11 or later. * * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com> * Implemented the "bg", "fg" and "retry" mount options for NFS. * * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org> * - added Native Language Support * * Modified by Olaf Kirch and Trond Myklebust for new NFS code, * plus NFSv3 stuff. */ #define MOUNTPORT 635 #define MNTPATHLEN 1024 #define MNTNAMLEN 255 #define FHSIZE 32 #define FHSIZE3 64 typedef char fhandle[FHSIZE]; typedef struct { unsigned int fhandle3_len; char *fhandle3_val; } fhandle3; enum mountstat3 { MNT_OK = 0, MNT3ERR_PERM = 1, MNT3ERR_NOENT = 2, MNT3ERR_IO = 5, MNT3ERR_ACCES = 13, MNT3ERR_NOTDIR = 20, MNT3ERR_INVAL = 22, MNT3ERR_NAMETOOLONG = 63, MNT3ERR_NOTSUPP = 10004, MNT3ERR_SERVERFAULT = 10006, }; typedef enum mountstat3 mountstat3; struct fhstatus { unsigned int fhs_status; union { fhandle fhs_fhandle; } fhstatus_u; }; typedef struct fhstatus fhstatus; struct mountres3_ok { fhandle3 fhandle; struct { unsigned int auth_flavours_len; char *auth_flavours_val; } auth_flavours; }; typedef struct mountres3_ok mountres3_ok; struct mountres3 { mountstat3 fhs_status; union { mountres3_ok mountinfo; } mountres3_u; }; typedef struct mountres3 mountres3; typedef char *dirpath; typedef char *name; typedef struct mountbody *mountlist; struct mountbody { name ml_hostname; dirpath ml_directory; mountlist ml_next; }; typedef struct mountbody mountbody; typedef struct groupnode *groups; struct groupnode { name gr_name; groups gr_next; }; typedef struct groupnode groupnode; typedef struct exportnode *exports; struct exportnode { dirpath ex_dir; groups ex_groups; exports ex_next; }; typedef struct exportnode exportnode; struct ppathcnf { int pc_link_max; short pc_max_canon; short pc_max_input; short pc_name_max; short pc_path_max; short pc_pipe_buf; uint8_t pc_vdisable; char pc_xxx; short pc_mask[2]; }; typedef struct ppathcnf ppathcnf; #define MOUNTPROG 100005 #define MOUNTVERS 1 #define MOUNTPROC_NULL 0 #define MOUNTPROC_MNT 1 #define MOUNTPROC_DUMP 2 #define MOUNTPROC_UMNT 3 #define MOUNTPROC_UMNTALL 4 #define MOUNTPROC_EXPORT 5 #define MOUNTPROC_EXPORTALL 6 #define MOUNTVERS_POSIX 2 #define MOUNTPROC_PATHCONF 7 #define MOUNT_V3 3 #define MOUNTPROC3_NULL 0 #define MOUNTPROC3_MNT 1 #define MOUNTPROC3_DUMP 2 #define MOUNTPROC3_UMNT 3 #define MOUNTPROC3_UMNTALL 4 #define MOUNTPROC3_EXPORT 5 enum { #ifndef NFS_FHSIZE NFS_FHSIZE = 32, #endif #ifndef NFS_PORT NFS_PORT = 2049 #endif }; /* * We want to be able to compile mount on old kernels in such a way * that the binary will work well on more recent kernels. * Thus, if necessary we teach nfsmount.c the structure of new fields * that will come later. * * Moreover, the new kernel includes conflict with glibc includes * so it is easiest to ignore the kernel altogether (at compile time). */ struct nfs2_fh { char data[32]; }; struct nfs3_fh { unsigned short size; unsigned char data[64]; }; struct nfs_mount_data { int version; /* 1 */ int fd; /* 1 */ struct nfs2_fh old_root; /* 1 */ int flags; /* 1 */ int rsize; /* 1 */ int wsize; /* 1 */ int timeo; /* 1 */ int retrans; /* 1 */ int acregmin; /* 1 */ int acregmax; /* 1 */ int acdirmin; /* 1 */ int acdirmax; /* 1 */ struct sockaddr_in addr; /* 1 */ char hostname[256]; /* 1 */ int namlen; /* 2 */ unsigned int bsize; /* 3 */ struct nfs3_fh root; /* 4 */ }; /* bits in the flags field */ enum { NFS_MOUNT_SOFT = 0x0001, /* 1 */ NFS_MOUNT_INTR = 0x0002, /* 1 */ NFS_MOUNT_SECURE = 0x0004, /* 1 */ NFS_MOUNT_POSIX = 0x0008, /* 1 */ NFS_MOUNT_NOCTO = 0x0010, /* 1 */ NFS_MOUNT_NOAC = 0x0020, /* 1 */ NFS_MOUNT_TCP = 0x0040, /* 2 */ NFS_MOUNT_VER3 = 0x0080, /* 3 */ NFS_MOUNT_KERBEROS = 0x0100, /* 3 */ NFS_MOUNT_NONLM = 0x0200, /* 3 */ NFS_MOUNT_NOACL = 0x0800, /* 4 */ NFS_MOUNT_NORDIRPLUS = 0x4000 }; /* * We need to translate between nfs status return values and * the local errno values which may not be the same. * * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno: * "after #include <errno.h> the symbol errno is reserved for any use, * it cannot even be used as a struct tag or field name". */ #ifndef EDQUOT # define EDQUOT ENOSPC #endif /* Convert each NFSERR_BLAH into EBLAH */ static const uint8_t nfs_err_stat[] = { 1, 2, 5, 6, 13, 17, 19, 20, 21, 22, 27, 28, 30, 63, 66, 69, 70, 71 }; #if ( \ EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \ ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \ EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256 typedef uint8_t nfs_err_type; #else typedef uint16_t nfs_err_type; #endif static const nfs_err_type nfs_err_errnum[] = { EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST, ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC, EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE }; static char *nfs_strerror(int status) { int i; for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) { if (nfs_err_stat[i] == status) return strerror(nfs_err_errnum[i]); } return xasprintf("unknown nfs status return value: %d", status); } static bool_t xdr_fhandle(XDR *xdrs, fhandle objp) { return xdr_opaque(xdrs, objp, FHSIZE); } static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp) { if (!xdr_u_int(xdrs, &objp->fhs_status)) return FALSE; if (objp->fhs_status == 0) return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle); return TRUE; } static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp) { return xdr_string(xdrs, objp, MNTPATHLEN); } static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp) { return xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3); } static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp) { if (!xdr_fhandle3(xdrs, &objp->fhandle)) return FALSE; return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0, sizeof(int), (xdrproc_t) xdr_int); } static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp) { return xdr_enum(xdrs, (enum_t *) objp); } static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp) { if (!xdr_mountstat3(xdrs, &objp->fhs_status)) return FALSE; if (objp->fhs_status == MNT_OK) return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo); return TRUE; } #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2) /* * Unfortunately, the kernel prints annoying console messages * in case of an unexpected nfs mount version (instead of * just returning some error). Therefore we'll have to try * and figure out what version the kernel expects. * * Variables: * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time * NFS_MOUNT_VERSION: these nfsmount sources at compile time * nfs_mount_version: version this source and running kernel can handle */ static void find_kernel_nfs_mount_version(void) { int kernel_version; if (nfs_mount_version) return; nfs_mount_version = 4; /* default */ kernel_version = get_linux_version_code(); if (kernel_version) { if (kernel_version < KERNEL_VERSION(2,2,18)) nfs_mount_version = 3; /* else v4 since 2.3.99pre4 */ } } static void get_mountport(struct pmap *pm_mnt, struct sockaddr_in *server_addr, long unsigned prog, long unsigned version, long unsigned proto, long unsigned port) { struct pmaplist *pmap; server_addr->sin_port = PMAPPORT; /* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *). * I understand it like "IPv6 for this is not 100% ready" */ pmap = pmap_getmaps(server_addr); if (version > MAX_NFSPROT) version = MAX_NFSPROT; if (!prog) prog = MOUNTPROG; pm_mnt->pm_prog = prog; pm_mnt->pm_vers = version; pm_mnt->pm_prot = proto; pm_mnt->pm_port = port; while (pmap) { if (pmap->pml_map.pm_prog != prog) goto next; if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers) goto next; if (version > 2 && pmap->pml_map.pm_vers != version) goto next; if (version && version <= 2 && pmap->pml_map.pm_vers > 2) goto next; if (pmap->pml_map.pm_vers > MAX_NFSPROT || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) || (port && pmap->pml_map.pm_port != port) ) { goto next; } memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt)); next: pmap = pmap->pml_next; } if (!pm_mnt->pm_vers) pm_mnt->pm_vers = MOUNTVERS; if (!pm_mnt->pm_port) pm_mnt->pm_port = MOUNTPORT; if (!pm_mnt->pm_prot) pm_mnt->pm_prot = IPPROTO_TCP; } #if BB_MMU static int daemonize(void) { int pid = fork(); if (pid < 0) /* error */ return -errno; if (pid > 0) /* parent */ return 0; /* child */ close(0); xopen(bb_dev_null, O_RDWR); xdup2(0, 1); xdup2(0, 2); setsid(); openlog(applet_name, LOG_PID, LOG_DAEMON); logmode = LOGMODE_SYSLOG; return 1; } #else static inline int daemonize(void) { return -ENOSYS; } #endif /* TODO */ static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM) { return 0; } /* RPC strerror analogs are terminally idiotic: * *mandatory* prefix and \n at end. * This hopefully helps. Usage: * error_msg_rpc(clnt_*error*(" ")) */ static void error_msg_rpc(const char *msg) { int len; while (msg[0] == ' ' || msg[0] == ':') msg++; len = strlen(msg); while (len && msg[len-1] == '\n') len--; bb_error_msg("%.*s", len, msg); } /* NB: mp->xxx fields may be trashed on exit */ static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts) { CLIENT *mclient; char *hostname; char *pathname; char *mounthost; /* prior to 2.6.23, kernel took NFS options in a form of this struct * only. 2.6.23+ looks at data->version, and if it's not 1..6, * then data pointer is interpreted as a string. */ struct nfs_mount_data data; char *opt; struct hostent *hp; struct sockaddr_in server_addr; struct sockaddr_in mount_server_addr; int msock, fsock; union { struct fhstatus nfsv2; struct mountres3 nfsv3; } status; int daemonized; char *s; int port; int mountport; int proto; #if BB_MMU smallint bg = 0; #else enum { bg = 0 }; #endif int retry; int mountprog; int mountvers; int nfsprog; int nfsvers; int retval; /* these all are one-bit really. gcc 4.3.1 likes this combination: */ smallint tcp; smallint soft; int intr; int posix; int nocto; int noac; int nordirplus; int nolock; int noacl; find_kernel_nfs_mount_version(); daemonized = 0; mounthost = NULL; retval = ETIMEDOUT; msock = fsock = -1; mclient = NULL; /* NB: hostname, mounthost, filteropts must be free()d prior to return */ filteropts = xstrdup(filteropts); /* going to trash it later... */ hostname = xstrdup(mp->mnt_fsname); /* mount_main() guarantees that ':' is there */ s = strchr(hostname, ':'); pathname = s + 1; *s = '\0'; /* Ignore all but first hostname in replicated mounts * until they can be fully supported. (mack@sgi.com) */ s = strchr(hostname, ','); if (s) { *s = '\0'; bb_error_msg("warning: multiple hostnames not supported"); } server_addr.sin_family = AF_INET; if (!inet_aton(hostname, &server_addr.sin_addr)) { hp = gethostbyname(hostname); if (hp == NULL) { bb_herror_msg("%s", hostname); goto fail; } if (hp->h_length != (int)sizeof(struct in_addr)) { bb_error_msg_and_die("only IPv4 is supported"); } memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr)); } memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr)); /* add IP address to mtab options for use when unmounting */ if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */ mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr)); } else { char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts, mp->mnt_opts[0] ? "," : "", inet_ntoa(server_addr.sin_addr)); free(mp->mnt_opts); mp->mnt_opts = tmp; } /* Set default options. * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to * let the kernel decide. * timeo is filled in after we know whether it'll be TCP or UDP. */ memset(&data, 0, sizeof(data)); data.retrans = 3; data.acregmin = 3; data.acregmax = 60; data.acdirmin = 30; data.acdirmax = 60; data.namlen = NAME_MAX; soft = 0; intr = 0; posix = 0; nocto = 0; nolock = 0; noac = 0; nordirplus = 0; noacl = 0; retry = 10000; /* 10000 minutes ~ 1 week */ tcp = 1; /* nfs-utils uses tcp per default */ mountprog = MOUNTPROG; mountvers = 0; port = 0; mountport = 0; nfsprog = 100003; nfsvers = 0; /* parse options */ if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) { char *opteq = strchr(opt, '='); if (opteq) { int val, idx; static const char options[] ALIGN1 = /* 0 */ "rsize\0" /* 1 */ "wsize\0" /* 2 */ "timeo\0" /* 3 */ "retrans\0" /* 4 */ "acregmin\0" /* 5 */ "acregmax\0" /* 6 */ "acdirmin\0" /* 7 */ "acdirmax\0" /* 8 */ "actimeo\0" /* 9 */ "retry\0" /* 10 */ "port\0" /* 11 */ "mountport\0" /* 12 */ "mounthost\0" /* 13 */ "mountprog\0" /* 14 */ "mountvers\0" /* 15 */ "nfsprog\0" /* 16 */ "nfsvers\0" /* 17 */ "vers\0" /* 18 */ "proto\0" /* 19 */ "namlen\0" /* 20 */ "addr\0"; *opteq++ = '\0'; idx = index_in_strings(options, opt); switch (idx) { case 12: // "mounthost" mounthost = xstrndup(opteq, strcspn(opteq, " \t\n\r,")); continue; case 18: // "proto" if (!strncmp(opteq, "tcp", 3)) tcp = 1; else if (!strncmp(opteq, "udp", 3)) tcp = 0; else bb_error_msg("warning: unrecognized proto= option"); continue; case 20: // "addr" - ignore continue; case -1: // unknown if (vfsflags & MS_REMOUNT) continue; } val = xatoi_positive(opteq); switch (idx) { case 0: // "rsize" data.rsize = val; continue; case 1: // "wsize" data.wsize = val; continue; case 2: // "timeo" data.timeo = val; continue; case 3: // "retrans" data.retrans = val; continue; case 4: // "acregmin" data.acregmin = val; continue; case 5: // "acregmax" data.acregmax = val; continue; case 6: // "acdirmin" data.acdirmin = val; continue; case 7: // "acdirmax" data.acdirmax = val; continue; case 8: // "actimeo" data.acregmin = val; data.acregmax = val; data.acdirmin = val; data.acdirmax = val; continue; case 9: // "retry" retry = val; continue; case 10: // "port" port = val; continue; case 11: // "mountport" mountport = val; continue; case 13: // "mountprog" mountprog = val; continue; case 14: // "mountvers" mountvers = val; continue; case 15: // "nfsprog" nfsprog = val; continue; case 16: // "nfsvers" case 17: // "vers" nfsvers = val; continue; case 19: // "namlen" //if (nfs_mount_version >= 2) data.namlen = val; //else // bb_error_msg("warning: option namlen is not supported\n"); continue; default: bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val); goto fail; } } else { /* not of the form opt=val */ static const char options[] ALIGN1 = "bg\0" "fg\0" "soft\0" "hard\0" "intr\0" "posix\0" "cto\0" "ac\0" "tcp\0" "udp\0" "lock\0" "rdirplus\0" "acl\0"; int val = 1; if (!strncmp(opt, "no", 2)) { val = 0; opt += 2; } switch (index_in_strings(options, opt)) { case 0: // "bg" #if BB_MMU bg = val; #endif break; case 1: // "fg" #if BB_MMU bg = !val; #endif break; case 2: // "soft" soft = val; break; case 3: // "hard" soft = !val; break; case 4: // "intr" intr = val; break; case 5: // "posix" posix = val; break; case 6: // "cto" nocto = !val; break; case 7: // "ac" noac = !val; break; case 8: // "tcp" tcp = val; break; case 9: // "udp" tcp = !val; break; case 10: // "lock" if (nfs_mount_version >= 3) nolock = !val; else bb_error_msg("warning: option nolock is not supported"); break; case 11: //rdirplus nordirplus = !val; break; case 12: // acl noacl = !val; break; default: bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt); goto fail; } } } proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP; data.flags = (soft ? NFS_MOUNT_SOFT : 0) | (intr ? NFS_MOUNT_INTR : 0) | (posix ? NFS_MOUNT_POSIX : 0) | (nocto ? NFS_MOUNT_NOCTO : 0) | (noac ? NFS_MOUNT_NOAC : 0) | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0) | (noacl ? NFS_MOUNT_NOACL : 0); if (nfs_mount_version >= 2) data.flags |= (tcp ? NFS_MOUNT_TCP : 0); if (nfs_mount_version >= 3) data.flags |= (nolock ? NFS_MOUNT_NONLM : 0); if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) { bb_error_msg("NFSv%d not supported", nfsvers); goto fail; } if (nfsvers && !mountvers) mountvers = (nfsvers < 3) ? 1 : nfsvers; if (nfsvers && nfsvers < mountvers) { mountvers = nfsvers; } /* Adjust options if none specified */ if (!data.timeo) data.timeo = tcp ? 70 : 7; data.version = nfs_mount_version; if (vfsflags & MS_REMOUNT) goto do_mount; /* * If the previous mount operation on the same host was * backgrounded, and the "bg" for this mount is also set, * give up immediately, to avoid the initial timeout. */ if (bg && we_saw_this_host_before(hostname)) { daemonized = daemonize(); if (daemonized <= 0) { /* parent or error */ retval = -daemonized; goto ret; } } /* Create mount daemon client */ /* See if the nfs host = mount host. */ if (mounthost) { if (mounthost[0] >= '0' && mounthost[0] <= '9') { mount_server_addr.sin_family = AF_INET; mount_server_addr.sin_addr.s_addr = inet_addr(hostname); } else { hp = gethostbyname(mounthost); if (hp == NULL) { bb_herror_msg("%s", mounthost); goto fail; } if (hp->h_length != (int)sizeof(struct in_addr)) { bb_error_msg_and_die("only IPv4 is supported"); } mount_server_addr.sin_family = AF_INET; memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr)); } } /* * The following loop implements the mount retries. When the mount * times out, and the "bg" option is set, we background ourself * and continue trying. * * The case where the mount point is not present and the "bg" * option is set, is treated as a timeout. This is done to * support nested mounts. * * The "retry" count specified by the user is the number of * minutes to retry before giving up. */ { struct timeval total_timeout; struct timeval retry_timeout; struct pmap pm_mnt; time_t t; time_t prevt; time_t timeout; retry_timeout.tv_sec = 3; retry_timeout.tv_usec = 0; total_timeout.tv_sec = 20; total_timeout.tv_usec = 0; /* FIXME: use monotonic()? */ timeout = time(NULL) + 60 * retry; prevt = 0; t = 30; retry: /* Be careful not to use too many CPU cycles */ if (t - prevt < 30) sleep(30); get_mountport(&pm_mnt, &mount_server_addr, mountprog, mountvers, proto, mountport); nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers; /* contact the mount daemon via TCP */ mount_server_addr.sin_port = htons(pm_mnt.pm_port); msock = RPC_ANYSOCK; switch (pm_mnt.pm_prot) { case IPPROTO_UDP: mclient = clntudp_create(&mount_server_addr, pm_mnt.pm_prog, pm_mnt.pm_vers, retry_timeout, &msock); if (mclient) break; mount_server_addr.sin_port = htons(pm_mnt.pm_port); msock = RPC_ANYSOCK; case IPPROTO_TCP: mclient = clnttcp_create(&mount_server_addr, pm_mnt.pm_prog, pm_mnt.pm_vers, &msock, 0, 0); break; default: mclient = NULL; } if (!mclient) { if (!daemonized && prevt == 0) error_msg_rpc(clnt_spcreateerror(" ")); } else { enum clnt_stat clnt_stat; /* Try to mount hostname:pathname */ mclient->cl_auth = authunix_create_default(); /* Make pointers in xdr_mountres3 NULL so * that xdr_array allocates memory for us */ memset(&status, 0, sizeof(status)); if (pm_mnt.pm_vers == 3) clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT, (xdrproc_t) xdr_dirpath, (caddr_t) &pathname, (xdrproc_t) xdr_mountres3, (caddr_t) &status, total_timeout); else clnt_stat = clnt_call(mclient, MOUNTPROC_MNT, (xdrproc_t) xdr_dirpath, (caddr_t) &pathname, (xdrproc_t) xdr_fhstatus, (caddr_t) &status, total_timeout); if (clnt_stat == RPC_SUCCESS) goto prepare_kernel_data; /* we're done */ if (errno != ECONNREFUSED) { error_msg_rpc(clnt_sperror(mclient, " ")); goto fail; /* don't retry */ } /* Connection refused */ if (!daemonized && prevt == 0) /* print just once */ error_msg_rpc(clnt_sperror(mclient, " ")); auth_destroy(mclient->cl_auth); clnt_destroy(mclient); mclient = NULL; close(msock); msock = -1; } /* Timeout. We are going to retry... maybe */ if (!bg) goto fail; if (!daemonized) { daemonized = daemonize(); if (daemonized <= 0) { /* parent or error */ retval = -daemonized; goto ret; } } prevt = t; t = time(NULL); if (t >= timeout) /* TODO error message */ goto fail; goto retry; } prepare_kernel_data: if (nfsvers == 2) { if (status.nfsv2.fhs_status != 0) { bb_error_msg("%s:%s failed, reason given by server: %s", hostname, pathname, nfs_strerror(status.nfsv2.fhs_status)); goto fail; } memcpy(data.root.data, (char *) status.nfsv2.fhstatus_u.fhs_fhandle, NFS_FHSIZE); data.root.size = NFS_FHSIZE; memcpy(data.old_root.data, (char *) status.nfsv2.fhstatus_u.fhs_fhandle, NFS_FHSIZE); } else { fhandle3 *my_fhandle; if (status.nfsv3.fhs_status != 0) { bb_error_msg("%s:%s failed, reason given by server: %s", hostname, pathname, nfs_strerror(status.nfsv3.fhs_status)); goto fail; } my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle; memset(data.old_root.data, 0, NFS_FHSIZE); memset(&data.root, 0, sizeof(data.root)); data.root.size = my_fhandle->fhandle3_len; memcpy(data.root.data, (char *) my_fhandle->fhandle3_val, my_fhandle->fhandle3_len); data.flags |= NFS_MOUNT_VER3; } /* Create nfs socket for kernel */ if (tcp) { if (nfs_mount_version < 3) { bb_error_msg("NFS over TCP is not supported"); goto fail; } fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); } else fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fsock < 0) { bb_perror_msg("nfs socket"); goto fail; } if (bindresvport(fsock, 0) < 0) { bb_perror_msg("nfs bindresvport"); goto fail; } if (port == 0) { server_addr.sin_port = PMAPPORT; port = pmap_getport(&server_addr, nfsprog, nfsvers, tcp ? IPPROTO_TCP : IPPROTO_UDP); if (port == 0) port = NFS_PORT; } server_addr.sin_port = htons(port); /* Prepare data structure for kernel */ data.fd = fsock; memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr)); strncpy(data.hostname, hostname, sizeof(data.hostname)); /* Clean up */ auth_destroy(mclient->cl_auth); clnt_destroy(mclient); close(msock); msock = -1; if (bg) { /* We must wait until mount directory is available */ struct stat statbuf; int delay = 1; while (stat(mp->mnt_dir, &statbuf) == -1) { if (!daemonized) { daemonized = daemonize(); if (daemonized <= 0) { /* parent or error */ /* FIXME: parent doesn't close fsock - ??! */ retval = -daemonized; goto ret; } } sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */ delay *= 2; if (delay > 30) delay = 30; } } /* Perform actual mount */ do_mount: retval = mount_it_now(mp, vfsflags, (char*)&data); goto ret; /* Abort */ fail: if (msock >= 0) { if (mclient) { auth_destroy(mclient->cl_auth); clnt_destroy(mclient); } close(msock); } if (fsock >= 0) close(fsock); ret: free(hostname); free(mounthost); free(filteropts); return retval; } #else // !ENABLE_FEATURE_MOUNT_NFS /* Linux 2.6.23+ supports nfs mounts with options passed as a string. * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS. * (However, note that then you lose any chances that NFS over IPv6 would work). */ static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts) { len_and_sockaddr *lsa; char *opts; char *end; char *dotted; int ret; # if ENABLE_FEATURE_IPV6 end = strchr(mp->mnt_fsname, ']'); if (end && end[1] == ':') end++; else # endif /* mount_main() guarantees that ':' is there */ end = strchr(mp->mnt_fsname, ':'); *end = '\0'; lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0); *end = ':'; dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa); if (ENABLE_FEATURE_CLEAN_UP) free(lsa); opts = xasprintf("%s%saddr=%s", filteropts ? filteropts : "", filteropts ? "," : "", dotted ); if (ENABLE_FEATURE_CLEAN_UP) free(dotted); ret = mount_it_now(mp, vfsflags, opts); if (ENABLE_FEATURE_CLEAN_UP) free(opts); return ret; } #endif // !ENABLE_FEATURE_MOUNT_NFS // Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem // type detection. Returns 0 for success, nonzero for failure. // NB: mp->xxx fields may be trashed on exit static int singlemount(struct mntent *mp, int ignore_busy) { int rc = -1; unsigned long vfsflags; char *loopFile = NULL, *filteropts = NULL; llist_t *fl = NULL; struct stat st; errno = 0; vfsflags = parse_mount_options(mp->mnt_opts, &filteropts); // Treat fstype "auto" as unspecified if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0) mp->mnt_type = NULL; // Might this be a virtual filesystem? if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) { char *args[35]; char *s; int n; // fsname: "cmd#arg1#arg2..." // WARNING: allows execution of arbitrary commands! // Try "mount 'sh#-c#sh' bogus_dir". // It is safe ONLY because non-root // cannot use two-argument mount command // and using one-argument "mount 'sh#-c#sh'" doesn't work: // "mount: can't find sh#-c#sh in /etc/fstab" // (if /etc/fstab has it, it's ok: root sets up /etc/fstab). s = mp->mnt_fsname; n = 0; args[n++] = s; while (*s && n < 35 - 2) { if (*s++ == '#' && *s != '#') { s[-1] = '\0'; args[n++] = s; } } args[n++] = mp->mnt_dir; args[n] = NULL; rc = spawn_and_wait(args); goto report_error; } // Might this be an CIFS filesystem? if (ENABLE_FEATURE_MOUNT_CIFS && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0) && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\') && mp->mnt_fsname[0] == mp->mnt_fsname[1] ) { int len; char c; char *hostname, *share; char *dotted, *ip; len_and_sockaddr *lsa; // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]" hostname = mp->mnt_fsname + 2; len = strcspn(hostname, "/\\"); share = hostname + len + 1; if (len == 0 // 3rd char is a [back]slash (IOW: empty hostname) || share[-1] == '\0' // no [back]slash after hostname || share[0] == '\0' // empty share name ) { goto report_error; } c = share[-1]; share[-1] = '\0'; len = strcspn(share, "/\\"); // "unc=\\hostname\share" option is mandatory // after CIFS option parsing was rewritten in Linux 3.4. // Must use backslashes. // If /dir1/dir2 is present, also add "prefixpath=dir1/dir2" { char *unc = xasprintf( share[len] != '\0' /* "/dir1/dir2" exists? */ ? "unc=\\\\%s\\%.*s,prefixpath=%s" : "unc=\\\\%s\\%.*s", hostname, len, share, share + len + 1 /* "dir1/dir2" */ ); parse_mount_options(unc, &filteropts); if (ENABLE_FEATURE_CLEAN_UP) free(unc); } lsa = host2sockaddr(hostname, 0); share[-1] = c; if (!lsa) goto report_error; // Insert "ip=..." option into options dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa); if (ENABLE_FEATURE_CLEAN_UP) free(lsa); ip = xasprintf("ip=%s", dotted); if (ENABLE_FEATURE_CLEAN_UP) free(dotted); parse_mount_options(ip, &filteropts); if (ENABLE_FEATURE_CLEAN_UP) free(ip); mp->mnt_type = (char*)"cifs"; rc = mount_it_now(mp, vfsflags, filteropts); goto report_error; } // Might this be an NFS filesystem? if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0) && strchr(mp->mnt_fsname, ':') != NULL ) { if (!mp->mnt_type) mp->mnt_type = (char*)"nfs"; rc = nfsmount(mp, vfsflags, filteropts); goto report_error; } // Look at the file. (Not found isn't a failure for remount, or for // a synthetic filesystem like proc or sysfs.) // (We use stat, not lstat, in order to allow // mount symlink_to_file_or_blkdev dir) if (!stat(mp->mnt_fsname, &st) && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)) ) { // Do we need to allocate a loopback device for it? if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) { loopFile = bb_simplify_path(mp->mnt_fsname); mp->mnt_fsname = NULL; // will receive malloced loop dev name if (set_loop(&mp->mnt_fsname, loopFile, 0, /*ro:*/ (vfsflags & MS_RDONLY)) < 0) { if (errno == EPERM || errno == EACCES) bb_error_msg(bb_msg_perm_denied_are_you_root); else bb_perror_msg("can't setup loop device"); return errno; } // Autodetect bind mounts } else if (S_ISDIR(st.st_mode) && !mp->mnt_type) vfsflags |= MS_BIND; } // If we know the fstype (or don't need to), jump straight // to the actual mount. if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) { char *next; for (;;) { next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL; if (next) *next = '\0'; rc = mount_it_now(mp, vfsflags, filteropts); if (rc == 0 || !next) break; mp->mnt_type = next + 1; } } else { // Loop through filesystem types until mount succeeds // or we run out // Initialize list of block backed filesystems. // This has to be done here so that during "mount -a", // mounts after /proc shows up can autodetect. if (!fslist) { fslist = get_block_backed_filesystems(); if (ENABLE_FEATURE_CLEAN_UP && fslist) atexit(delete_block_backed_filesystems); } for (fl = fslist; fl; fl = fl->link) { mp->mnt_type = fl->data; rc = mount_it_now(mp, vfsflags, filteropts); if (rc == 0) break; } } // If mount failed, clean up loop file (if any). if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) { del_loop(mp->mnt_fsname); if (ENABLE_FEATURE_CLEAN_UP) { free(loopFile); free(mp->mnt_fsname); } } report_error: if (ENABLE_FEATURE_CLEAN_UP) free(filteropts); if (errno == EBUSY && ignore_busy) return 0; if (rc != 0) bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir); return rc; } // -O support // -O interprets a list of filter options which select whether a mount // point will be mounted: only mounts with options matching *all* filtering // options will be selected. // By default each -O filter option must be present in the list of mount // options, but if it is prefixed by "no" then it must be absent. // For example, // -O a,nob,c matches -o a,c but fails to match -o a,b,c // (and also fails to match -o a because -o c is absent). // // It is different from -t in that each option is matched exactly; a leading // "no" at the beginning of one option does not negate the rest. static int match_opt(const char *fs_opt_in, const char *O_opt) { if (!O_opt) return 1; while (*O_opt) { const char *fs_opt = fs_opt_in; int O_len; int match; // If option begins with "no" then treat as an inverted match: // matching is a failure match = 0; if (O_opt[0] == 'n' && O_opt[1] == 'o') { match = 1; O_opt += 2; } // Isolate the current O option O_len = strchrnul(O_opt, ',') - O_opt; // Check for a match against existing options while (1) { if (strncmp(fs_opt, O_opt, O_len) == 0 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',') ) { if (match) return 0; // "no" prefix, but option found match = 1; // current O option found, go check next one break; } fs_opt = strchr(fs_opt, ','); if (!fs_opt) break; fs_opt++; } if (match == 0) return 0; // match wanted but not found if (O_opt[O_len] == '\0') // end? break; // Step to the next O option O_opt += O_len + 1; } // If we get here then everything matched return 1; } // Parse options, if necessary parse fstab/mtab, and call singlemount for // each directory to be mounted. int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mount_main(int argc UNUSED_PARAM, char **argv) { char *cmdopts = xzalloc(1); char *fstype = NULL; char *O_optmatch = NULL; char *storage_path; llist_t *lst_o = NULL; const char *fstabname; FILE *fstab; int i, j; int rc = EXIT_SUCCESS; unsigned long cmdopt_flags; unsigned opt; struct mntent mtpair[2], *mtcur = mtpair; IF_NOT_DESKTOP(const int nonroot = 0;) IF_DESKTOP(int nonroot = ) sanitize_env_if_suid(); INIT_G(); // Parse long options, like --bind and --move. Note that -o option // and --option are synonymous. Yes, this means --remount,rw works. for (i = j = 1; argv[i]; i++) { if (argv[i][0] == '-' && argv[i][1] == '-') append_mount_options(&cmdopts, argv[i] + 2); else argv[j++] = argv[i]; } argv[j] = NULL; // Parse remaining options // Max 2 params; -o is a list, -v is a counter opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv"); opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch IF_FEATURE_MOUNT_VERBOSE(, &verbose)); while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w argv += optind; // If we have no arguments, show currently mounted filesystems if (!argv[0]) { if (!(opt & OPT_a)) { FILE *mountTable = setmntent(bb_path_mtab_file, "r"); if (!mountTable) bb_error_msg_and_die("no %s", bb_path_mtab_file); while (getmntent_r(mountTable, &mtpair[0], getmntent_buf, GETMNTENT_BUFSIZE)) { // Don't show rootfs. FIXME: why?? // util-linux 2.12a happily shows rootfs... //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue; if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0) printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname, mtpair->mnt_dir, mtpair->mnt_type, mtpair->mnt_opts); } if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable); return EXIT_SUCCESS; } storage_path = NULL; } else { // When we have two arguments, the second is the directory and we can // skip looking at fstab entirely. We can always abspath() the directory // argument when we get it. if (argv[1]) { if (nonroot) bb_error_msg_and_die(bb_msg_you_must_be_root); mtpair->mnt_fsname = argv[0]; mtpair->mnt_dir = argv[1]; mtpair->mnt_type = fstype; mtpair->mnt_opts = cmdopts; resolve_mount_spec(&mtpair->mnt_fsname); rc = singlemount(mtpair, /*ignore_busy:*/ 0); return rc; } storage_path = bb_simplify_path(argv[0]); // malloced } // Past this point, we are handling either "mount -a [opts]" // or "mount [opts] single_param" cmdopt_flags = parse_mount_options(cmdopts, NULL); if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags bb_error_msg_and_die(bb_msg_you_must_be_root); // If we have a shared subtree flag, don't worry about fstab or mtab. if (ENABLE_FEATURE_MOUNT_FLAGS && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) ) { // verbose_mount(source, target, type, flags, data) rc = verbose_mount("", argv[0], "", cmdopt_flags, ""); if (rc) bb_simple_perror_msg_and_die(argv[0]); return rc; } // Open either fstab or mtab fstabname = "/etc/fstab"; if (cmdopt_flags & MS_REMOUNT) { // WARNING. I am not sure this matches util-linux's // behavior. It's possible util-linux does not // take -o opts from mtab (takes only mount source). fstabname = bb_path_mtab_file; } fstab = setmntent(fstabname, "r"); if (!fstab) bb_perror_msg_and_die("can't read '%s'", fstabname); // Loop through entries until we find what we're looking for memset(mtpair, 0, sizeof(mtpair)); for (;;) { struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair); // Get next fstab entry if (!getmntent_r(fstab, mtcur, getmntent_buf + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0), GETMNTENT_BUFSIZE/2) ) { // End of fstab/mtab is reached mtcur = mtother; // the thing we found last time break; } // If we're trying to mount something specific and this isn't it, // skip it. Note we must match the exact text in fstab (ala // "proc") or a full path from root if (argv[0]) { // Is this what we're looking for? if (strcmp(argv[0], mtcur->mnt_fsname) != 0 && strcmp(storage_path, mtcur->mnt_fsname) != 0 && strcmp(argv[0], mtcur->mnt_dir) != 0 && strcmp(storage_path, mtcur->mnt_dir) != 0 ) { continue; // no } // Remember this entry. Something later may have // overmounted it, and we want the _last_ match. mtcur = mtother; // If we're mounting all } else { struct mntent *mp; // No, mount -a won't mount anything, // even user mounts, for mere humans if (nonroot) bb_error_msg_and_die(bb_msg_you_must_be_root); // Does type match? (NULL matches always) if (!match_fstype(mtcur, fstype)) continue; // Skip noauto and swap anyway if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP)) // swap is bogus "fstype", parse_mount_options can't check fstypes || strcasecmp(mtcur->mnt_type, "swap") == 0 ) { continue; } // Does (at least one) option match? // (NULL matches always) if (!match_opt(mtcur->mnt_opts, O_optmatch)) continue; resolve_mount_spec(&mtcur->mnt_fsname); // NFS mounts want this to be xrealloc-able mtcur->mnt_opts = xstrdup(mtcur->mnt_opts); // If nothing is mounted on this directory... // (otherwise repeated "mount -a" mounts everything again) mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0); // We do not check fsname match of found mount point - // "/" may have fsname of "/dev/root" while fstab // says "/dev/something_else". if (mp) { if (verbose) { bb_error_msg("according to %s, " "%s is already mounted on %s", bb_path_mtab_file, mp->mnt_fsname, mp->mnt_dir); } } else { // ...mount this thing if (singlemount(mtcur, /*ignore_busy:*/ 1)) { // Count number of failed mounts rc++; } } free(mtcur->mnt_opts); } } // End of fstab/mtab is reached. // Were we looking for something specific? if (argv[0]) { // yes unsigned long l; // If we didn't find anything, complain if (!mtcur->mnt_fsname) bb_error_msg_and_die("can't find %s in %s", argv[0], fstabname); // What happens when we try to "mount swap_partition"? // (fstab containts "swap_partition swap swap defaults 0 0") // util-linux-ng 2.13.1 does this: // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory) // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory) // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory) // write(2, "mount: mount point swap does not exist\n", 39) = 39 // exit_group(32) = ? #if 0 // In case we want to simply skip swap partitions: l = parse_mount_options(mtcur->mnt_opts, NULL); if ((l & MOUNT_SWAP) // swap is bogus "fstype", parse_mount_options can't check fstypes || strcasecmp(mtcur->mnt_type, "swap") == 0 ) { goto ret; } #endif if (nonroot) { // fstab must have "users" or "user" l = parse_mount_options(mtcur->mnt_opts, NULL); if (!(l & MOUNT_USERS)) bb_error_msg_and_die(bb_msg_you_must_be_root); } //util-linux-2.12 does not do this check. //// If nothing is mounted on this directory... //// (otherwise repeated "mount FOO" mounts FOO again) //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0); //if (mp) { // bb_error_msg("according to %s, " // "%s is already mounted on %s", // bb_path_mtab_file, // mp->mnt_fsname, mp->mnt_dir); //} else { // ...mount the last thing we found mtcur->mnt_opts = xstrdup(mtcur->mnt_opts); append_mount_options(&(mtcur->mnt_opts), cmdopts); resolve_mount_spec(&mtpair->mnt_fsname); rc = singlemount(mtcur, /*ignore_busy:*/ 0); if (ENABLE_FEATURE_CLEAN_UP) free(mtcur->mnt_opts); //} } //ret: if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab); if (ENABLE_FEATURE_CLEAN_UP) { free(storage_path); free(cmdopts); } //TODO: exitcode should be ORed mask of (from "man mount"): // 0 success // 1 incorrect invocation or permissions // 2 system error (out of memory, cannot fork, no more loop devices) // 4 internal mount bug or missing nfs support in mount // 8 user interrupt //16 problems writing or locking /etc/mtab //32 mount failure //64 some mount succeeded return rc; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/ipcrm.c�������������������������������������������������������������������0000644�0000000�0000000�00000011552�12263563520�015366� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * ipcrm.c - utility to allow removal of IPC objects and data structures. * * 01 Sept 2004 - Rodney Radford <rradford@mindspring.com> * Adapted for busybox from util-linux-2.12a. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define ipcrm_trivial_usage //usage: "[-MQS key] [-mqs id]" //usage:#define ipcrm_full_usage "\n\n" //usage: "Upper-case options MQS remove an object by shmkey value.\n" //usage: "Lower-case options remove an object by shmid value.\n" //usage: "\n -mM Remove memory segment after last detach" //usage: "\n -qQ Remove message queue" //usage: "\n -sS Remove semaphore" #include "libbb.h" /* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */ /* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */ #include <sys/ipc.h> #include <sys/shm.h> #include <sys/msg.h> #include <sys/sem.h> #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) /* union semun is defined by including <sys/sem.h> */ #else /* according to X/OPEN we have to define it ourselves */ union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; }; #endif #define IPCRM_LEGACY 1 #if IPCRM_LEGACY typedef enum type_id { SHM, SEM, MSG } type_id; static int remove_ids(type_id type, char **argv) { unsigned long id; int nb_errors = 0; union semun arg; arg.val = 0; while (argv[0]) { id = bb_strtoul(argv[0], NULL, 10); if (errno || id > INT_MAX) { bb_error_msg("invalid id: %s", argv[0]); nb_errors++; } else { int ret = 0; if (type == SEM) ret = semctl(id, 0, IPC_RMID, arg); else if (type == MSG) ret = msgctl(id, IPC_RMID, NULL); else if (type == SHM) ret = shmctl(id, IPC_RMID, NULL); if (ret) { bb_perror_msg("can't remove id %s", argv[0]); nb_errors++; } } argv++; } return nb_errors; } #endif /* IPCRM_LEGACY */ int ipcrm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ipcrm_main(int argc, char **argv) { int c; int error = 0; /* if the command is executed without parameters, do nothing */ if (argc == 1) return 0; #if IPCRM_LEGACY /* check to see if the command is being invoked in the old way if so then run the old code. Valid commands are msg, shm, sem. */ { type_id what = 0; /* silence gcc */ char w; w = argv[1][0]; if ( ((w == 'm' && argv[1][1] == 's' && argv[1][2] == 'g') || (argv[1][0] == 's' && ((w = argv[1][1]) == 'h' || w == 'e') && argv[1][2] == 'm') ) && argv[1][3] == '\0' ) { if (argc < 3) bb_show_usage(); if (w == 'h') what = SHM; else if (w == 'm') what = MSG; else if (w == 'e') what = SEM; if (remove_ids(what, &argv[2])) fflush_stdout_and_exit(EXIT_FAILURE); printf("resource(s) deleted\n"); return 0; } } #endif /* IPCRM_LEGACY */ /* process new syntax to conform with SYSV ipcrm */ while ((c = getopt(argc, argv, "q:m:s:Q:M:S:h?")) != -1) { int result; int id = 0; int iskey = isupper(c); /* needed to delete semaphores */ union semun arg; arg.val = 0; if ((c == '?') || (c == 'h')) { bb_show_usage(); } /* we don't need case information any more */ c = tolower(c); /* make sure the option is in range: allowed are q, m, s */ if (c != 'q' && c != 'm' && c != 's') { bb_show_usage(); } if (iskey) { /* keys are in hex or decimal */ key_t key = xstrtoul(optarg, 0); if (key == IPC_PRIVATE) { error++; bb_error_msg("illegal key (%s)", optarg); continue; } /* convert key to id */ id = ((c == 'q') ? msgget(key, 0) : (c == 'm') ? shmget(key, 0, 0) : semget(key, 0, 0)); if (id < 0) { const char *errmsg; error++; switch (errno) { case EACCES: errmsg = "permission denied for"; break; case EIDRM: errmsg = "already removed"; break; case ENOENT: errmsg = "invalid"; break; default: errmsg = "unknown error in"; break; } bb_error_msg("%s %s (%s)", errmsg, "key", optarg); continue; } } else { /* ids are in decimal */ id = xatoul(optarg); } result = ((c == 'q') ? msgctl(id, IPC_RMID, NULL) : (c == 'm') ? shmctl(id, IPC_RMID, NULL) : semctl(id, 0, IPC_RMID, arg)); if (result) { const char *errmsg; const char *const what = iskey ? "key" : "id"; error++; switch (errno) { case EACCES: case EPERM: errmsg = "permission denied for"; break; case EINVAL: errmsg = "invalid"; break; case EIDRM: errmsg = "already removed"; break; default: errmsg = "unknown error in"; break; } bb_error_msg("%s %s (%s)", errmsg, what, optarg); continue; } } /* print usage if we still have some arguments left over */ if (optind != argc) { bb_show_usage(); } /* exit value reflects the number of errors encountered */ return error; } ������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/findfs.c������������������������������������������������������������������0000644�0000000�0000000�00000002225�12263563520�015522� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Support functions for mounting devices by label/uuid * * Copyright (C) 2006 by Jason Schoon <floydpink@gmail.com> * Some portions cribbed from e2fsprogs, util-linux, dosfstools * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define findfs_trivial_usage //usage: "LABEL=label or UUID=uuid" //usage:#define findfs_full_usage "\n\n" //usage: "Find a filesystem device based on a label or UUID" //usage: //usage:#define findfs_example_usage //usage: "$ findfs LABEL=MyDevice" #include "libbb.h" #include "volume_id.h" int findfs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int findfs_main(int argc UNUSED_PARAM, char **argv) { char *dev = *++argv; if (!dev) bb_show_usage(); if (strncmp(dev, "/dev/", 5) == 0) { /* Just pass any /dev/xxx name right through. * This might aid in some scripts being able * to call this unconditionally */ dev = NULL; } else { /* Otherwise, handle LABEL=xxx and UUID=xxx, * fail on anything else */ if (!resolve_mount_spec(argv)) bb_show_usage(); } if (*argv != dev) { puts(*argv); return 0; } return 1; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/mkfs_vfat.c���������������������������������������������������������������0000644�0000000�0000000�00000053410�12263563520�016233� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * mkfs_vfat: utility to create FAT32 filesystem * inspired by dosfstools * * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define mkfs_vfat_trivial_usage //usage: "[-v] [-n LABEL] BLOCKDEV [KBYTES]" /* Accepted but ignored: "[-c] [-C] [-I] [-l bad-block-file] [-b backup-boot-sector] " "[-m boot-msg-file] [-i volume-id] " "[-s sectors-per-cluster] [-S logical-sector-size] [-f number-of-FATs] " "[-h hidden-sectors] [-F fat-size] [-r root-dir-entries] [-R reserved-sectors] " */ //usage:#define mkfs_vfat_full_usage "\n\n" //usage: "Make a FAT32 filesystem\n" /* //usage: "\n -c Check device for bad blocks" */ //usage: "\n -v Verbose" /* //usage: "\n -I Allow to use entire disk device (e.g. /dev/hda)" */ //usage: "\n -n LBL Volume label" #include "libbb.h" #include <linux/hdreg.h> /* HDIO_GETGEO */ #include <linux/fd.h> /* FDGETPRM */ #include <sys/mount.h> /* BLKSSZGET */ #if !defined(BLKSSZGET) # define BLKSSZGET _IO(0x12, 104) #endif //#include <linux/msdos_fs.h> #define SECTOR_SIZE 512 #define SECTORS_PER_BLOCK (BLOCK_SIZE / SECTOR_SIZE) // M$ says the high 4 bits of a FAT32 FAT entry are reserved #define EOF_FAT32 0x0FFFFFF8 #define BAD_FAT32 0x0FFFFFF7 #define MAX_CLUST_32 0x0FFFFFF0 #define ATTR_VOLUME 8 #define NUM_FATS 2 /* FAT32 filesystem looks like this: * sector -nn...-1: "hidden" sectors, all sectors before this partition * (-h hidden-sectors sets it. Useful only for boot loaders, * they need to know _disk_ offset in order to be able to correctly * address sectors relative to start of disk) * sector 0: boot sector * sector 1: info sector * sector 2: set aside for boot code which didn't fit into sector 0 * ...(zero-filled sectors)... * sector B: backup copy of sector 0 [B set by -b backup-boot-sector] * sector B+1: backup copy of sector 1 * sector B+2: backup copy of sector 2 * ...(zero-filled sectors)... * sector R: FAT#1 [R set by -R reserved-sectors] * ...(FAT#1)... * sector R+fat_size: FAT#2 * ...(FAT#2)... * sector R+fat_size*2: cluster #2 * ...(cluster #2)... * sector R+fat_size*2+clust_size: cluster #3 * ...(the rest is filled by clusters till the end)... */ enum { // Perhaps this should remain constant info_sector_number = 1, // TODO: make these cmdline options // dont forget sanity check: backup_boot_sector + 3 <= reserved_sect backup_boot_sector = 3, reserved_sect = 6, }; // how many blocks we try to read while testing #define TEST_BUFFER_BLOCKS 16 struct msdos_dir_entry { char name[11]; /* 000 name and extension */ uint8_t attr; /* 00b attribute bits */ uint8_t lcase; /* 00c case for base and extension */ uint8_t ctime_cs; /* 00d creation time, centiseconds (0-199) */ uint16_t ctime; /* 00e creation time */ uint16_t cdate; /* 010 creation date */ uint16_t adate; /* 012 last access date */ uint16_t starthi; /* 014 high 16 bits of cluster in FAT32 */ uint16_t time; /* 016 time */ uint16_t date; /* 018 date */ uint16_t start; /* 01a first cluster */ uint32_t size; /* 01c file size in bytes */ } PACKED; /* Example of boot sector's beginning: 0000 eb 58 90 4d 53 57 49 4e 34 2e 31 00 02 08 26 00 |...MSWIN4.1...&.| 0010 02 00 00 00 00 f8 00 00 3f 00 ff 00 3f 00 00 00 |........?...?...| 0020 54 9b d0 00 0d 34 00 00 00 00 00 00 02 00 00 00 |T....4..........| 0030 01 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 0040 80 00 29 71 df 51 e0 4e 4f 20 4e 41 4d 45 20 20 |..)q.Q.NO NAME | 0050 20 20 46 41 54 33 32 20 20 20 33 c9 8e d1 bc f4 | FAT32 3.....| */ struct msdos_volume_info { /* (offsets are relative to start of boot sector) */ uint8_t drive_number; /* 040 BIOS drive number */ uint8_t reserved; /* 041 unused */ uint8_t ext_boot_sign; /* 042 0x29 if fields below exist (DOS 3.3+) */ uint32_t volume_id32; /* 043 volume ID number */ char volume_label[11];/* 047 volume label */ char fs_type[8]; /* 052 typically "FATnn" */ } PACKED; /* 05a end. Total size 26 (0x1a) bytes */ struct msdos_boot_sector { /* We use strcpy to fill both, and gcc-4.4.x complains if they are separate */ char boot_jump_and_sys_id[3+8]; /* 000 short or near jump instruction */ /*char system_id[8];*/ /* 003 name - can be used to special case partition manager volumes */ uint16_t bytes_per_sect; /* 00b bytes per logical sector */ uint8_t sect_per_clust; /* 00d sectors/cluster */ uint16_t reserved_sect; /* 00e reserved sectors (sector offset of 1st FAT relative to volume start) */ uint8_t fats; /* 010 number of FATs */ uint16_t dir_entries; /* 011 root directory entries */ uint16_t volume_size_sect; /* 013 volume size in sectors */ uint8_t media_byte; /* 015 media code */ uint16_t sect_per_fat; /* 016 sectors/FAT */ uint16_t sect_per_track; /* 018 sectors per track */ uint16_t heads; /* 01a number of heads */ uint32_t hidden; /* 01c hidden sectors (sector offset of volume within physical disk) */ uint32_t fat32_volume_size_sect; /* 020 volume size in sectors (if volume_size_sect == 0) */ uint32_t fat32_sect_per_fat; /* 024 sectors/FAT */ uint16_t fat32_flags; /* 028 bit 8: fat mirroring, low 4: active fat */ uint8_t fat32_version[2]; /* 02a major, minor filesystem version (I see 0,0) */ uint32_t fat32_root_cluster; /* 02c first cluster in root directory */ uint16_t fat32_info_sector; /* 030 filesystem info sector (usually 1) */ uint16_t fat32_backup_boot; /* 032 backup boot sector (usually 6) */ uint32_t reserved2[3]; /* 034 unused */ struct msdos_volume_info vi; /* 040 */ char boot_code[0x200 - 0x5a - 2]; /* 05a */ #define BOOT_SIGN 0xAA55 uint16_t boot_sign; /* 1fe */ } PACKED; #define FAT_FSINFO_SIG1 0x41615252 #define FAT_FSINFO_SIG2 0x61417272 struct fat32_fsinfo { uint32_t signature1; /* 0x52,0x52,0x41,0x61, "RRaA" */ uint32_t reserved1[128 - 8]; uint32_t signature2; /* 0x72,0x72,0x61,0x41, "rrAa" */ uint32_t free_clusters; /* free cluster count. -1 if unknown */ uint32_t next_cluster; /* most recently allocated cluster */ uint32_t reserved2[3]; uint16_t reserved3; /* 1fc */ uint16_t boot_sign; /* 1fe */ } PACKED; struct bug_check { char BUG1[sizeof(struct msdos_dir_entry ) == 0x20 ? 1 : -1]; char BUG2[sizeof(struct msdos_volume_info) == 0x1a ? 1 : -1]; char BUG3[sizeof(struct msdos_boot_sector) == 0x200 ? 1 : -1]; char BUG4[sizeof(struct fat32_fsinfo ) == 0x200 ? 1 : -1]; }; static const char boot_code[] ALIGN1 = "\x0e" /* 05a: push cs */ "\x1f" /* 05b: pop ds */ "\xbe\x77\x7c" /* write_msg: mov si, offset message_txt */ "\xac" /* 05f: lodsb */ "\x22\xc0" /* 060: and al, al */ "\x74\x0b" /* 062: jz key_press */ "\x56" /* 064: push si */ "\xb4\x0e" /* 065: mov ah, 0eh */ "\xbb\x07\x00" /* 067: mov bx, 0007h */ "\xcd\x10" /* 06a: int 10h */ "\x5e" /* 06c: pop si */ "\xeb\xf0" /* 06d: jmp write_msg */ "\x32\xe4" /* key_press: xor ah, ah */ "\xcd\x16" /* 071: int 16h */ "\xcd\x19" /* 073: int 19h */ "\xeb\xfe" /* foo: jmp foo */ /* 077: message_txt: */ "This is not a bootable disk\r\n"; #define MARK_CLUSTER(cluster, value) \ ((uint32_t *)fat)[cluster] = SWAP_LE32(value) void BUG_unsupported_field_size(void); #define STORE_LE(field, value) \ do { \ if (sizeof(field) == 4) \ field = SWAP_LE32(value); \ else if (sizeof(field) == 2) \ field = SWAP_LE16(value); \ else if (sizeof(field) == 1) \ field = (value); \ else \ BUG_unsupported_field_size(); \ } while (0) /* compat: * mkdosfs 2.11 (12 Mar 2005) * Usage: mkdosfs [-A] [-c] [-C] [-v] [-I] [-l bad-block-file] * [-b backup-boot-sector] * [-m boot-msg-file] [-n volume-name] [-i volume-id] * [-s sectors-per-cluster] [-S logical-sector-size] * [-f number-of-FATs] * [-h hidden-sectors] [-F fat-size] [-r root-dir-entries] * [-R reserved-sectors] * /dev/name [blocks] */ int mkfs_vfat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv) { struct stat st; const char *volume_label = ""; char *buf; char *device_name; uoff_t volume_size_bytes; uoff_t volume_size_sect; uint32_t total_clust; uint32_t volume_id; int dev; unsigned bytes_per_sect; unsigned sect_per_fat; unsigned opts; uint16_t sect_per_track; uint8_t media_byte; uint8_t sect_per_clust; uint8_t heads; enum { OPT_A = 1 << 0, // [IGNORED] atari format OPT_b = 1 << 1, // [IGNORED] location of backup boot sector OPT_c = 1 << 2, // [IGNORED] check filesystem OPT_C = 1 << 3, // [IGNORED] create a new file OPT_f = 1 << 4, // [IGNORED] number of FATs OPT_F = 1 << 5, // [IGNORED, implied 32] choose FAT size OPT_h = 1 << 6, // [IGNORED] number of hidden sectors OPT_I = 1 << 7, // [IGNORED] don't bark at entire disk devices OPT_i = 1 << 8, // [IGNORED] volume ID OPT_l = 1 << 9, // [IGNORED] bad block filename OPT_m = 1 << 10, // [IGNORED] message file OPT_n = 1 << 11, // volume label OPT_r = 1 << 12, // [IGNORED] root directory entries OPT_R = 1 << 13, // [IGNORED] number of reserved sectors OPT_s = 1 << 14, // [IGNORED] sectors per cluster OPT_S = 1 << 15, // [IGNORED] sector size OPT_v = 1 << 16, // verbose }; opt_complementary = "-1";//:b+:f+:F+:h+:r+:R+:s+:S+:vv:c--l:l--c"; opts = getopt32(argv, "Ab:cCf:F:h:Ii:l:m:n:r:R:s:S:v", NULL, NULL, NULL, NULL, NULL, NULL, NULL, &volume_label, NULL, NULL, NULL, NULL); argv += optind; // cache device name device_name = argv[0]; // default volume ID = creation time volume_id = time(NULL); dev = xopen(device_name, O_RDWR); xfstat(dev, &st, device_name); // // Get image size and sector size // bytes_per_sect = SECTOR_SIZE; if (!S_ISBLK(st.st_mode)) { if (!S_ISREG(st.st_mode)) { if (!argv[1]) bb_error_msg_and_die("image size must be specified"); } // not a block device, skip bad sectors check opts &= ~OPT_c; } else { int min_bytes_per_sect; #if 0 unsigned device_num; // for true block devices we do check sanity device_num = st.st_rdev & 0xff3f; // do we allow to format the whole disk device? if (!(opts & OPT_I) && ( device_num == 0x0300 || // hda, hdb (device_num & 0xff0f) == 0x0800 || // sd device_num == 0x0d00 || // xd device_num == 0x1600 ) // hdc, hdd ) bb_error_msg_and_die("will not try to make filesystem on full-disk device (use -I if wanted)"); // can't work on mounted filesystems if (find_mount_point(device_name, 0)) bb_error_msg_and_die("can't format mounted filesystem"); #endif // get true sector size // (parameter must be int*, not long* or size_t*) xioctl(dev, BLKSSZGET, &min_bytes_per_sect); if (min_bytes_per_sect > SECTOR_SIZE) { bytes_per_sect = min_bytes_per_sect; bb_error_msg("for this device sector size is %u", min_bytes_per_sect); } } volume_size_bytes = get_volume_size_in_bytes(dev, argv[1], 1024, /*extend:*/ 1); volume_size_sect = volume_size_bytes / bytes_per_sect; // // Find out or guess media parameters // media_byte = 0xf8; heads = 255; sect_per_track = 63; sect_per_clust = 1; { struct hd_geometry geometry; // size (in sectors), sect (per track), head struct floppy_struct param; // N.B. whether to use HDIO_GETGEO or HDIO_REQ? if (ioctl(dev, HDIO_GETGEO, &geometry) == 0 && geometry.sectors && geometry.heads ) { // hard drive sect_per_track = geometry.sectors; heads = geometry.heads; set_cluster_size: /* For FAT32, try to do the same as M$'s format command * (see http://www.win.tue.nl/~aeb/linux/fs/fat/fatgen103.pdf p. 20): * fs size <= 260M: 0.5k clusters * fs size <= 8G: 4k clusters * fs size <= 16G: 8k clusters * fs size > 16G: 16k clusters */ sect_per_clust = 1; if (volume_size_bytes >= 260*1024*1024) { sect_per_clust = 8; /* fight gcc: */ /* "error: integer overflow in expression" */ /* "error: right shift count >= width of type" */ if (sizeof(off_t) > 4) { unsigned t = (volume_size_bytes >> 31 >> 1); if (t >= 8/4) sect_per_clust = 16; if (t >= 16/4) sect_per_clust = 32; } } } else { // floppy, loop, or regular file int not_floppy = ioctl(dev, FDGETPRM, ¶m); if (not_floppy == 0) { // floppy disk sect_per_track = param.sect; heads = param.head; volume_size_sect = param.size; volume_size_bytes = param.size * SECTOR_SIZE; } // setup the media descriptor byte switch (volume_size_sect) { case 2*360: // 5.25", 2, 9, 40 - 360K media_byte = 0xfd; break; case 2*720: // 3.5", 2, 9, 80 - 720K case 2*1200: // 5.25", 2, 15, 80 - 1200K media_byte = 0xf9; break; default: // anything else if (not_floppy) goto set_cluster_size; case 2*1440: // 3.5", 2, 18, 80 - 1440K case 2*2880: // 3.5", 2, 36, 80 - 2880K media_byte = 0xf0; break; } // not floppy, but size matches floppy exactly. // perhaps it is a floppy image. // we already set media_byte as if it is a floppy, // now set sect_per_track and heads. heads = 2; sect_per_track = (unsigned)volume_size_sect / 160; if (sect_per_track < 9) sect_per_track = 9; } } // // Calculate number of clusters, sectors/cluster, sectors/FAT // (an initial guess for sect_per_clust should already be set) // // "mkdosfs -v -F 32 image5k 5" is the minimum: // 2 sectors for FATs and 2 data sectors if ((off_t)(volume_size_sect - reserved_sect) < 4) bb_error_msg_and_die("the image is too small for FAT32"); sect_per_fat = 1; while (1) { while (1) { int spf_adj; uoff_t tcl = (volume_size_sect - reserved_sect - NUM_FATS * sect_per_fat) / sect_per_clust; // tcl may be > MAX_CLUST_32 here, but it may be // because sect_per_fat is underestimated, // and with increased sect_per_fat it still may become // <= MAX_CLUST_32. Therefore, we do not check // against MAX_CLUST_32, but against a bigger const: if (tcl > 0x80ffffff) goto next; total_clust = tcl; // fits in uint32_t // Every cluster needs 4 bytes in FAT. +2 entries since // FAT has space for non-existent clusters 0 and 1. // Let's see how many sectors that needs. //May overflow at "*4": //spf_adj = ((total_clust+2) * 4 + bytes_per_sect-1) / bytes_per_sect - sect_per_fat; //Same in the more obscure, non-overflowing form: spf_adj = ((total_clust+2) + (bytes_per_sect/4)-1) / (bytes_per_sect/4) - sect_per_fat; #if 0 bb_error_msg("sect_per_clust:%u sect_per_fat:%u total_clust:%u", sect_per_clust, sect_per_fat, (int)tcl); bb_error_msg("adjust to sect_per_fat:%d", spf_adj); #endif if (spf_adj <= 0) { // do not need to adjust sect_per_fat. // so, was total_clust too big after all? if (total_clust <= MAX_CLUST_32) goto found_total_clust; // no // yes, total_clust is _a bit_ too big goto next; } // adjust sect_per_fat, go back and recalc total_clust // (note: just "sect_per_fat += spf_adj" isn't ok) sect_per_fat += ((unsigned)spf_adj / 2) | 1; } next: if (sect_per_clust == 128) bb_error_msg_and_die("can't make FAT32 with >128 sectors/cluster"); sect_per_clust *= 2; sect_per_fat = (sect_per_fat / 2) | 1; } found_total_clust: // // Print info // if (opts & OPT_v) { fprintf(stderr, "Device '%s':\n" "heads:%u, sectors/track:%u, bytes/sector:%u\n" "media descriptor:%02x\n" "total sectors:%"OFF_FMT"u, clusters:%u, sectors/cluster:%u\n" "FATs:2, sectors/FAT:%u\n" "volumeID:%08x, label:'%s'\n", device_name, heads, sect_per_track, bytes_per_sect, (int)media_byte, volume_size_sect, (int)total_clust, (int)sect_per_clust, sect_per_fat, (int)volume_id, volume_label ); } // // Write filesystem image sequentially (no seeking) // { // (a | b) is poor man's max(a, b) unsigned bufsize = reserved_sect; //bufsize |= sect_per_fat; // can be quite large bufsize |= 2; // use this instead bufsize |= sect_per_clust; buf = xzalloc(bufsize * bytes_per_sect); } { // boot and fsinfo sectors, and their copies struct msdos_boot_sector *boot_blk = (void*)buf; struct fat32_fsinfo *info = (void*)(buf + bytes_per_sect); strcpy(boot_blk->boot_jump_and_sys_id, "\xeb\x58\x90" "mkdosfs"); STORE_LE(boot_blk->bytes_per_sect, bytes_per_sect); STORE_LE(boot_blk->sect_per_clust, sect_per_clust); // cast in needed on big endian to suppress a warning STORE_LE(boot_blk->reserved_sect, (uint16_t)reserved_sect); STORE_LE(boot_blk->fats, 2); //STORE_LE(boot_blk->dir_entries, 0); // for FAT32, stays 0 if (volume_size_sect <= 0xffff) STORE_LE(boot_blk->volume_size_sect, volume_size_sect); STORE_LE(boot_blk->media_byte, media_byte); // wrong: this would make Linux think that it's fat12/16: //if (sect_per_fat <= 0xffff) // STORE_LE(boot_blk->sect_per_fat, sect_per_fat); // works: //STORE_LE(boot_blk->sect_per_fat, 0); STORE_LE(boot_blk->sect_per_track, sect_per_track); STORE_LE(boot_blk->heads, heads); //STORE_LE(boot_blk->hidden, 0); STORE_LE(boot_blk->fat32_volume_size_sect, volume_size_sect); STORE_LE(boot_blk->fat32_sect_per_fat, sect_per_fat); //STORE_LE(boot_blk->fat32_flags, 0); //STORE_LE(boot_blk->fat32_version[2], 0,0); STORE_LE(boot_blk->fat32_root_cluster, 2); STORE_LE(boot_blk->fat32_info_sector, info_sector_number); STORE_LE(boot_blk->fat32_backup_boot, backup_boot_sector); //STORE_LE(boot_blk->reserved2[3], 0,0,0); STORE_LE(boot_blk->vi.ext_boot_sign, 0x29); STORE_LE(boot_blk->vi.volume_id32, volume_id); strncpy(boot_blk->vi.fs_type, "FAT32 ", sizeof(boot_blk->vi.fs_type)); strncpy(boot_blk->vi.volume_label, volume_label, sizeof(boot_blk->vi.volume_label)); memcpy(boot_blk->boot_code, boot_code, sizeof(boot_code)); STORE_LE(boot_blk->boot_sign, BOOT_SIGN); STORE_LE(info->signature1, FAT_FSINFO_SIG1); STORE_LE(info->signature2, FAT_FSINFO_SIG2); // we've allocated cluster 2 for the root dir STORE_LE(info->free_clusters, (total_clust - 1)); STORE_LE(info->next_cluster, 2); STORE_LE(info->boot_sign, BOOT_SIGN); // 1st copy xwrite(dev, buf, bytes_per_sect * backup_boot_sector); // 2nd copy and possibly zero sectors xwrite(dev, buf, bytes_per_sect * (reserved_sect - backup_boot_sector)); } { // file allocation tables unsigned i,j; unsigned char *fat = (void*)buf; memset(buf, 0, bytes_per_sect * 2); // initial FAT entries MARK_CLUSTER(0, 0x0fffff00 | media_byte); MARK_CLUSTER(1, 0xffffffff); // mark cluster 2 as EOF (used for root dir) MARK_CLUSTER(2, EOF_FAT32); for (i = 0; i < NUM_FATS; i++) { xwrite(dev, buf, bytes_per_sect); for (j = 1; j < sect_per_fat; j++) xwrite(dev, buf + bytes_per_sect, bytes_per_sect); } } // root directory // empty directory is just a set of zero bytes memset(buf, 0, sect_per_clust * bytes_per_sect); if (volume_label[0]) { // create dir entry for volume_label struct msdos_dir_entry *de; #if 0 struct tm tm_time; uint16_t t, d; #endif de = (void*)buf; strncpy(de->name, volume_label, sizeof(de->name)); STORE_LE(de->attr, ATTR_VOLUME); #if 0 localtime_r(&create_time, &tm_time); t = (tm_time.tm_sec >> 1) + (tm_time.tm_min << 5) + (tm_time.tm_hour << 11); d = tm_time.tm_mday + ((tm_time.tm_mon+1) << 5) + ((tm_time.tm_year-80) << 9); STORE_LE(de->time, t); STORE_LE(de->date, d); //STORE_LE(de->ctime_cs, 0); de->ctime = de->time; de->cdate = de->date; de->adate = de->date; #endif } xwrite(dev, buf, sect_per_clust * bytes_per_sect); #if 0 if (opts & OPT_c) { uoff_t volume_size_blocks; unsigned start_data_sector; unsigned start_data_block; unsigned badblocks = 0; int try, got; off_t currently_testing; char *blkbuf = xmalloc(BLOCK_SIZE * TEST_BUFFER_BLOCKS); volume_size_blocks = (volume_size_bytes >> BLOCK_SIZE_BITS); // N.B. the two following vars are in hard sectors, i.e. SECTOR_SIZE byte sectors! start_data_sector = (reserved_sect + NUM_FATS * sect_per_fat) * (bytes_per_sect / SECTOR_SIZE); start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) / SECTORS_PER_BLOCK; bb_info_msg("searching for bad blocks "); currently_testing = 0; try = TEST_BUFFER_BLOCKS; while (currently_testing < volume_size_blocks) { if (currently_testing + try > volume_size_blocks) try = volume_size_blocks - currently_testing; // perform a test on a block. return the number of blocks // that could be read successfully. // seek to the correct location xlseek(dev, currently_testing * BLOCK_SIZE, SEEK_SET); // try reading got = read(dev, blkbuf, try * BLOCK_SIZE); if (got < 0) got = 0; if (got & (BLOCK_SIZE - 1)) bb_error_msg("unexpected values in do_check: probably bugs"); got /= BLOCK_SIZE; currently_testing += got; if (got == try) { try = TEST_BUFFER_BLOCKS; continue; } try = 1; if (currently_testing < start_data_block) bb_error_msg_and_die("bad blocks before data-area: cannot make fs"); // mark all of the sectors in the block as bad for (i = 0; i < SECTORS_PER_BLOCK; i++) { int cluster = (currently_testing * SECTORS_PER_BLOCK + i - start_data_sector) / (int) (sect_per_clust) / (bytes_per_sect / SECTOR_SIZE); if (cluster < 0) bb_error_msg_and_die("invalid cluster number in mark_sector: probably bug!"); MARK_CLUSTER(cluster, BAD_FAT32); } badblocks++; currently_testing++; } free(blkbuf); if (badblocks) bb_info_msg("%d bad block(s)", badblocks); } #endif // cleanup if (ENABLE_FEATURE_CLEAN_UP) { free(buf); close(dev); } return 0; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/dmesg.c�������������������������������������������������������������������0000644�0000000�0000000�00000003661�12263563520�015355� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * * dmesg - display/control kernel ring buffer. * * Copyright 2006 Rob Landley <rob@landley.net> * Copyright 2006 Bernhard Reutner-Fischer <rep.nop@aon.at> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define dmesg_trivial_usage //usage: "[-c] [-n LEVEL] [-s SIZE]" //usage:#define dmesg_full_usage "\n\n" //usage: "Print or control the kernel ring buffer\n" //usage: "\n -c Clear ring buffer after printing" //usage: "\n -n LEVEL Set console logging level" //usage: "\n -s SIZE Buffer size" #include <sys/klog.h> #include "libbb.h" int dmesg_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int dmesg_main(int argc UNUSED_PARAM, char **argv) { int len, level; char *buf; unsigned opts; enum { OPT_c = 1 << 0, OPT_s = 1 << 1, OPT_n = 1 << 2 }; opt_complementary = "s+:n+"; /* numeric */ opts = getopt32(argv, "cs:n:", &len, &level); if (opts & OPT_n) { if (klogctl(8, NULL, (long) level)) bb_perror_msg_and_die("klogctl"); return EXIT_SUCCESS; } if (!(opts & OPT_s)) len = klogctl(10, NULL, 0); /* read ring buffer size */ if (len < 16*1024) len = 16*1024; if (len > 16*1024*1024) len = 16*1024*1024; buf = xmalloc(len); len = klogctl(3 + (opts & OPT_c), buf, len); /* read ring buffer */ if (len < 0) bb_perror_msg_and_die("klogctl"); if (len == 0) return EXIT_SUCCESS; if (ENABLE_FEATURE_DMESG_PRETTY) { int last = '\n'; int in = 0; /* Skip <[0-9]+> at the start of lines */ while (1) { if (last == '\n' && buf[in] == '<') { while (buf[in++] != '>' && in < len) ; } else { last = buf[in++]; putchar(last); } if (in >= len) break; } /* Make sure we end with a newline */ if (last != '\n') bb_putchar('\n'); } else { full_write(STDOUT_FILENO, buf, len); if (buf[len-1] != '\n') bb_putchar('\n'); } if (ENABLE_FEATURE_CLEAN_UP) free(buf); return EXIT_SUCCESS; } �������������������������������������������������������������������������������busybox-1.22.1/util-linux/fdisk_sun.c���������������������������������������������������������������0000644�0000000�0000000�00000050223�12263563520�016237� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * fdisk_sun.c * * I think this is mostly, or entirely, due to * Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996 * * Merged with fdisk for other architectures, aeb, June 1998. * * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br> * Internationalization * * Licensed under GPLv2, see file LICENSE in this source tree. */ #if ENABLE_FEATURE_SUN_LABEL #define SUNOS_SWAP 3 #define SUN_WHOLE_DISK 5 #define SUN_LABEL_MAGIC 0xDABE #define SUN_LABEL_MAGIC_SWAPPED 0xBEDA #define SUN_SSWAP16(x) (sun_other_endian ? fdisk_swap16(x) : (uint16_t)(x)) #define SUN_SSWAP32(x) (sun_other_endian ? fdisk_swap32(x) : (uint32_t)(x)) /* Copied from linux/major.h */ #define FLOPPY_MAJOR 2 #define SCSI_IOCTL_GET_IDLUN 0x5382 static smallint sun_other_endian; static smallint scsi_disk; static smallint floppy; #ifndef IDE0_MAJOR #define IDE0_MAJOR 3 #endif #ifndef IDE1_MAJOR #define IDE1_MAJOR 22 #endif static void guess_device_type(void) { struct stat bootstat; if (fstat(dev_fd, &bootstat) < 0) { scsi_disk = 0; floppy = 0; } else if (S_ISBLK(bootstat.st_mode) && (major(bootstat.st_rdev) == IDE0_MAJOR || major(bootstat.st_rdev) == IDE1_MAJOR)) { scsi_disk = 0; floppy = 0; } else if (S_ISBLK(bootstat.st_mode) && major(bootstat.st_rdev) == FLOPPY_MAJOR) { scsi_disk = 0; floppy = 1; } else { scsi_disk = 1; floppy = 0; } } static const char *const sun_sys_types[] = { "\x00" "Empty" , /* 0 */ "\x01" "Boot" , /* 1 */ "\x02" "SunOS root" , /* 2 */ "\x03" "SunOS swap" , /* SUNOS_SWAP */ "\x04" "SunOS usr" , /* 4 */ "\x05" "Whole disk" , /* SUN_WHOLE_DISK */ "\x06" "SunOS stand" , /* 6 */ "\x07" "SunOS var" , /* 7 */ "\x08" "SunOS home" , /* 8 */ "\x82" "Linux swap" , /* LINUX_SWAP */ "\x83" "Linux native", /* LINUX_NATIVE */ "\x8e" "Linux LVM" , /* 0x8e */ /* New (2.2.x) raid partition with autodetect using persistent superblock */ "\xfd" "Linux raid autodetect", /* 0xfd */ NULL }; static void set_sun_partition(int i, unsigned start, unsigned stop, int sysid) { sunlabel->infos[i].id = sysid; sunlabel->partitions[i].start_cylinder = SUN_SSWAP32(start / (g_heads * g_sectors)); sunlabel->partitions[i].num_sectors = SUN_SSWAP32(stop - start); set_changed(i); } static int check_sun_label(void) { unsigned short *ush; int csum; if (sunlabel->magic != SUN_LABEL_MAGIC && sunlabel->magic != SUN_LABEL_MAGIC_SWAPPED ) { current_label_type = LABEL_DOS; sun_other_endian = 0; return 0; } sun_other_endian = (sunlabel->magic == SUN_LABEL_MAGIC_SWAPPED); ush = ((unsigned short *) (sunlabel + 1)) - 1; for (csum = 0; ush >= (unsigned short *)sunlabel;) csum ^= *ush--; if (csum) { printf("Detected sun disklabel with wrong checksum.\n" "Probably you'll have to set all the values,\n" "e.g. heads, sectors, cylinders and partitions\n" "or force a fresh label (s command in main menu)\n"); } else { g_heads = SUN_SSWAP16(sunlabel->ntrks); g_cylinders = SUN_SSWAP16(sunlabel->ncyl); g_sectors = SUN_SSWAP16(sunlabel->nsect); } update_units(); current_label_type = LABEL_SUN; g_partitions = 8; return 1; } static const struct sun_predefined_drives { const char *vendor; const char *model; unsigned short sparecyl; unsigned short ncyl; unsigned short nacyl; unsigned short pcylcount; unsigned short ntrks; unsigned short nsect; unsigned short rspeed; } sun_drives[] = { { "Quantum","ProDrive 80S",1,832,2,834,6,34,3662}, { "Quantum","ProDrive 105S",1,974,2,1019,6,35,3662}, { "CDC","Wren IV 94171-344",3,1545,2,1549,9,46,3600}, { "IBM","DPES-31080",0,4901,2,4903,4,108,5400}, { "IBM","DORS-32160",0,1015,2,1017,67,62,5400}, { "IBM","DNES-318350",0,11199,2,11474,10,320,7200}, { "SEAGATE","ST34371",0,3880,2,3882,16,135,7228}, { "","SUN0104",1,974,2,1019,6,35,3662}, { "","SUN0207",4,1254,2,1272,9,36,3600}, { "","SUN0327",3,1545,2,1549,9,46,3600}, { "","SUN0340",0,1538,2,1544,6,72,4200}, { "","SUN0424",2,1151,2,2500,9,80,4400}, { "","SUN0535",0,1866,2,2500,7,80,5400}, { "","SUN0669",5,1614,2,1632,15,54,3600}, { "","SUN1.0G",5,1703,2,1931,15,80,3597}, { "","SUN1.05",0,2036,2,2038,14,72,5400}, { "","SUN1.3G",6,1965,2,3500,17,80,5400}, { "","SUN2.1G",0,2733,2,3500,19,80,5400}, { "IOMEGA","Jaz",0,1019,2,1021,64,32,5394}, }; static const struct sun_predefined_drives * sun_autoconfigure_scsi(void) { const struct sun_predefined_drives *p = NULL; #ifdef SCSI_IOCTL_GET_IDLUN unsigned int id[2]; char buffer[2048]; char buffer2[2048]; FILE *pfd; char *vendor; char *model; char *q; int i; if (ioctl(dev_fd, SCSI_IOCTL_GET_IDLUN, &id)) return NULL; sprintf(buffer, "Host: scsi%u Channel: %02u Id: %02u Lun: %02u\n", /* This is very wrong (works only if you have one HBA), but I haven't found a way how to get hostno from the current kernel */ 0, (id[0]>>16) & 0xff, id[0] & 0xff, (id[0]>>8) & 0xff ); pfd = fopen_for_read("/proc/scsi/scsi"); if (!pfd) { return NULL; } while (fgets(buffer2, 2048, pfd)) { if (strcmp(buffer, buffer2)) continue; if (!fgets(buffer2, 2048, pfd)) break; q = strstr(buffer2, "Vendor: "); if (!q) break; q += 8; vendor = q; q = strstr(q, " "); *q++ = '\0'; /* truncate vendor name */ q = strstr(q, "Model: "); if (!q) break; *q = '\0'; q += 7; model = q; q = strstr(q, " Rev: "); if (!q) break; *q = '\0'; for (i = 0; i < ARRAY_SIZE(sun_drives); i++) { if (*sun_drives[i].vendor && strcasecmp(sun_drives[i].vendor, vendor)) continue; if (!strstr(model, sun_drives[i].model)) continue; printf("Autoconfigure found a %s%s%s\n", sun_drives[i].vendor, (*sun_drives[i].vendor) ? " " : "", sun_drives[i].model); p = sun_drives + i; break; } break; } fclose(pfd); #endif return p; } static void create_sunlabel(void) { struct hd_geometry geometry; unsigned ndiv; unsigned char c; const struct sun_predefined_drives *p = NULL; printf(msg_building_new_label, "sun disklabel"); sun_other_endian = BB_LITTLE_ENDIAN; memset(MBRbuffer, 0, sizeof(MBRbuffer)); sunlabel->magic = SUN_SSWAP16(SUN_LABEL_MAGIC); if (!floppy) { unsigned i; puts("Drive type\n" " ? auto configure\n" " 0 custom (with hardware detected defaults)"); for (i = 0; i < ARRAY_SIZE(sun_drives); i++) { printf(" %c %s%s%s\n", i + 'a', sun_drives[i].vendor, (*sun_drives[i].vendor) ? " " : "", sun_drives[i].model); } while (1) { c = read_nonempty("Select type (? for auto, 0 for custom): "); if (c == '0') { break; } if (c >= 'a' && c < 'a' + ARRAY_SIZE(sun_drives)) { p = sun_drives + c - 'a'; break; } if (c >= 'A' && c < 'A' + ARRAY_SIZE(sun_drives)) { p = sun_drives + c - 'A'; break; } if (c == '?' && scsi_disk) { p = sun_autoconfigure_scsi(); if (p) break; printf("Autoconfigure failed\n"); } } } if (!p || floppy) { if (!ioctl(dev_fd, HDIO_GETGEO, &geometry)) { g_heads = geometry.heads; g_sectors = geometry.sectors; g_cylinders = geometry.cylinders; } else { g_heads = 0; g_sectors = 0; g_cylinders = 0; } if (floppy) { sunlabel->nacyl = 0; sunlabel->pcylcount = SUN_SSWAP16(g_cylinders); sunlabel->rspeed = SUN_SSWAP16(300); sunlabel->ilfact = SUN_SSWAP16(1); sunlabel->sparecyl = 0; } else { g_heads = read_int(1, g_heads, 1024, 0, "Heads"); g_sectors = read_int(1, g_sectors, 1024, 0, "Sectors/track"); if (g_cylinders) g_cylinders = read_int(1, g_cylinders - 2, 65535, 0, "Cylinders"); else g_cylinders = read_int(1, 0, 65535, 0, "Cylinders"); sunlabel->nacyl = SUN_SSWAP16(read_int(0, 2, 65535, 0, "Alternate cylinders")); sunlabel->pcylcount = SUN_SSWAP16(read_int(0, g_cylinders + SUN_SSWAP16(sunlabel->nacyl), 65535, 0, "Physical cylinders")); sunlabel->rspeed = SUN_SSWAP16(read_int(1, 5400, 100000, 0, "Rotation speed (rpm)")); sunlabel->ilfact = SUN_SSWAP16(read_int(1, 1, 32, 0, "Interleave factor")); sunlabel->sparecyl = SUN_SSWAP16(read_int(0, 0, g_sectors, 0, "Extra sectors per cylinder")); } } else { sunlabel->sparecyl = SUN_SSWAP16(p->sparecyl); sunlabel->ncyl = SUN_SSWAP16(p->ncyl); sunlabel->nacyl = SUN_SSWAP16(p->nacyl); sunlabel->pcylcount = SUN_SSWAP16(p->pcylcount); sunlabel->ntrks = SUN_SSWAP16(p->ntrks); sunlabel->nsect = SUN_SSWAP16(p->nsect); sunlabel->rspeed = SUN_SSWAP16(p->rspeed); sunlabel->ilfact = SUN_SSWAP16(1); g_cylinders = p->ncyl; g_heads = p->ntrks; g_sectors = p->nsect; puts("You may change all the disk params from the x menu"); } snprintf((char *)(sunlabel->info), sizeof(sunlabel->info), "%s%s%s cyl %u alt %u hd %u sec %u", p ? p->vendor : "", (p && *p->vendor) ? " " : "", p ? p->model : (floppy ? "3,5\" floppy" : "Linux custom"), g_cylinders, SUN_SSWAP16(sunlabel->nacyl), g_heads, g_sectors); sunlabel->ntrks = SUN_SSWAP16(g_heads); sunlabel->nsect = SUN_SSWAP16(g_sectors); sunlabel->ncyl = SUN_SSWAP16(g_cylinders); if (floppy) set_sun_partition(0, 0, g_cylinders * g_heads * g_sectors, LINUX_NATIVE); else { if (g_cylinders * g_heads * g_sectors >= 150 * 2048) { ndiv = g_cylinders - (50 * 2048 / (g_heads * g_sectors)); /* 50M swap */ } else ndiv = g_cylinders * 2 / 3; set_sun_partition(0, 0, ndiv * g_heads * g_sectors, LINUX_NATIVE); set_sun_partition(1, ndiv * g_heads * g_sectors, g_cylinders * g_heads * g_sectors, LINUX_SWAP); sunlabel->infos[1].flags |= 0x01; /* Not mountable */ } set_sun_partition(2, 0, g_cylinders * g_heads * g_sectors, SUN_WHOLE_DISK); { unsigned short *ush = (unsigned short *)sunlabel; unsigned short csum = 0; while (ush < (unsigned short *)(&sunlabel->csum)) csum ^= *ush++; sunlabel->csum = csum; } set_all_unchanged(); set_changed(0); check_sun_label(); get_boot(CREATE_EMPTY_SUN); } static void toggle_sunflags(int i, unsigned char mask) { if (sunlabel->infos[i].flags & mask) sunlabel->infos[i].flags &= ~mask; else sunlabel->infos[i].flags |= mask; set_changed(i); } static void fetch_sun(unsigned *starts, unsigned *lens, unsigned *start, unsigned *stop) { int i, continuous = 1; *start = 0; *stop = g_cylinders * g_heads * g_sectors; for (i = 0; i < g_partitions; i++) { if (sunlabel->partitions[i].num_sectors && sunlabel->infos[i].id && sunlabel->infos[i].id != SUN_WHOLE_DISK) { starts[i] = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * g_heads * g_sectors; lens[i] = SUN_SSWAP32(sunlabel->partitions[i].num_sectors); if (continuous) { if (starts[i] == *start) *start += lens[i]; else if (starts[i] + lens[i] >= *stop) *stop = starts[i]; else continuous = 0; /* There will be probably more gaps than one, so lets check afterwards */ } } else { starts[i] = 0; lens[i] = 0; } } } static unsigned *verify_sun_starts; static int verify_sun_cmp(int *a, int *b) { if (*a == -1) return 1; if (*b == -1) return -1; if (verify_sun_starts[*a] > verify_sun_starts[*b]) return 1; return -1; } static void verify_sun(void) { unsigned starts[8], lens[8], start, stop; int i,j,k,starto,endo; int array[8]; verify_sun_starts = starts; fetch_sun(starts, lens, &start, &stop); for (k = 0; k < 7; k++) { for (i = 0; i < 8; i++) { if (k && (lens[i] % (g_heads * g_sectors))) { printf("Partition %u doesn't end on cylinder boundary\n", i+1); } if (lens[i]) { for (j = 0; j < i; j++) if (lens[j]) { if (starts[j] == starts[i]+lens[i]) { starts[j] = starts[i]; lens[j] += lens[i]; lens[i] = 0; } else if (starts[i] == starts[j]+lens[j]){ lens[j] += lens[i]; lens[i] = 0; } else if (!k) { if (starts[i] < starts[j]+lens[j] && starts[j] < starts[i]+lens[i]) { starto = starts[i]; if (starts[j] > starto) starto = starts[j]; endo = starts[i]+lens[i]; if (starts[j]+lens[j] < endo) endo = starts[j]+lens[j]; printf("Partition %u overlaps with others in " "sectors %u-%u\n", i+1, starto, endo); } } } } } } for (i = 0; i < 8; i++) { if (lens[i]) array[i] = i; else array[i] = -1; } qsort(array, ARRAY_SIZE(array), sizeof(array[0]), (int (*)(const void *,const void *)) verify_sun_cmp); if (array[0] == -1) { printf("No partitions defined\n"); return; } stop = g_cylinders * g_heads * g_sectors; if (starts[array[0]]) printf("Unused gap - sectors %u-%u\n", 0, starts[array[0]]); for (i = 0; i < 7 && array[i+1] != -1; i++) { printf("Unused gap - sectors %u-%u\n", starts[array[i]]+lens[array[i]], starts[array[i+1]]); } start = starts[array[i]] + lens[array[i]]; if (start < stop) printf("Unused gap - sectors %u-%u\n", start, stop); } static void add_sun_partition(int n, int sys) { unsigned start, stop, stop2; unsigned starts[8], lens[8]; int whole_disk = 0; char mesg[256]; int i, first, last; if (sunlabel->partitions[n].num_sectors && sunlabel->infos[n].id) { printf(msg_part_already_defined, n + 1); return; } fetch_sun(starts, lens, &start, &stop); if (stop <= start) { if (n == 2) whole_disk = 1; else { printf("Other partitions already cover the whole disk.\n" "Delete/shrink them before retry.\n"); return; } } snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR)); while (1) { if (whole_disk) first = read_int(0, 0, 0, 0, mesg); else first = read_int(scround(start), scround(stop)+1, scround(stop), 0, mesg); if (display_in_cyl_units) { first *= units_per_sector; } else { /* Starting sector has to be properly aligned */ first = (first + g_heads * g_sectors - 1) / (g_heads * g_sectors); first *= g_heads * g_sectors; } if (n == 2 && first != 0) printf("\ It is highly recommended that the third partition covers the whole disk\n\ and is of type 'Whole disk'\n"); /* ewt asks to add: "don't start a partition at cyl 0" However, edmundo@rano.demon.co.uk writes: "In addition to having a Sun partition table, to be able to boot from the disc, the first partition, /dev/sdX1, must start at cylinder 0. This means that /dev/sdX1 contains the partition table and the boot block, as these are the first two sectors of the disc. Therefore you must be careful what you use /dev/sdX1 for. In particular, you must not use a partition starting at cylinder 0 for Linux swap, as that would overwrite the partition table and the boot block. You may, however, use such a partition for a UFS or EXT2 file system, as these file systems leave the first 1024 bytes undisturbed. */ /* On the other hand, one should not use partitions starting at block 0 in an md, or the label will be trashed. */ for (i = 0; i < g_partitions; i++) if (lens[i] && starts[i] <= first && starts[i] + lens[i] > first) break; if (i < g_partitions && !whole_disk) { if (n == 2 && !first) { whole_disk = 1; break; } printf("Sector %u is already allocated\n", first); } else break; } stop = g_cylinders * g_heads * g_sectors; stop2 = stop; for (i = 0; i < g_partitions; i++) { if (starts[i] > first && starts[i] < stop) stop = starts[i]; } snprintf(mesg, sizeof(mesg), "Last %s or +size or +sizeM or +sizeK", str_units(SINGULAR)); if (whole_disk) last = read_int(scround(stop2), scround(stop2), scround(stop2), 0, mesg); else if (n == 2 && !first) last = read_int(scround(first), scround(stop2), scround(stop2), scround(first), mesg); else last = read_int(scround(first), scround(stop), scround(stop), scround(first), mesg); if (display_in_cyl_units) last *= units_per_sector; if (n == 2 && !first) { if (last >= stop2) { whole_disk = 1; last = stop2; } else if (last > stop) { printf( "You haven't covered the whole disk with the 3rd partition,\n" "but your value %u %s covers some other partition.\n" "Your entry has been changed to %u %s\n", scround(last), str_units(SINGULAR), scround(stop), str_units(SINGULAR)); last = stop; } } else if (!whole_disk && last > stop) last = stop; if (whole_disk) sys = SUN_WHOLE_DISK; set_sun_partition(n, first, last, sys); } static void sun_delete_partition(int i) { unsigned int nsec; if (i == 2 && sunlabel->infos[i].id == SUN_WHOLE_DISK && !sunlabel->partitions[i].start_cylinder && (nsec = SUN_SSWAP32(sunlabel->partitions[i].num_sectors)) == g_heads * g_sectors * g_cylinders) printf("If you want to maintain SunOS/Solaris compatibility, " "consider leaving this\n" "partition as Whole disk (5), starting at 0, with %u " "sectors\n", nsec); sunlabel->infos[i].id = 0; sunlabel->partitions[i].num_sectors = 0; } static void sun_change_sysid(int i, int sys) { if (sys == LINUX_SWAP && !sunlabel->partitions[i].start_cylinder) { read_maybe_empty( "It is highly recommended that the partition at offset 0\n" "is UFS, EXT2FS filesystem or SunOS swap. Putting Linux swap\n" "there may destroy your partition table and bootblock.\n" "Type YES if you're very sure you would like that partition\n" "tagged with 82 (Linux swap): "); if (strcmp (line_ptr, "YES\n")) return; } switch (sys) { case SUNOS_SWAP: case LINUX_SWAP: /* swaps are not mountable by default */ sunlabel->infos[i].flags |= 0x01; break; default: /* assume other types are mountable; user can change it anyway */ sunlabel->infos[i].flags &= ~0x01; break; } sunlabel->infos[i].id = sys; } static void sun_list_table(int xtra) { int i, w; w = strlen(disk_device); if (xtra) printf( "\nDisk %s (Sun disk label): %u heads, %u sectors, %u rpm\n" "%u cylinders, %u alternate cylinders, %u physical cylinders\n" "%u extra sects/cyl, interleave %u:1\n" "%s\n" "Units = %s of %u * 512 bytes\n\n", disk_device, g_heads, g_sectors, SUN_SSWAP16(sunlabel->rspeed), g_cylinders, SUN_SSWAP16(sunlabel->nacyl), SUN_SSWAP16(sunlabel->pcylcount), SUN_SSWAP16(sunlabel->sparecyl), SUN_SSWAP16(sunlabel->ilfact), (char *)sunlabel, str_units(PLURAL), units_per_sector); else printf( "\nDisk %s (Sun disk label): %u heads, %u sectors, %u cylinders\n" "Units = %s of %u * 512 bytes\n\n", disk_device, g_heads, g_sectors, g_cylinders, str_units(PLURAL), units_per_sector); printf("%*s Flag Start End Blocks Id System\n", w + 1, "Device"); for (i = 0; i < g_partitions; i++) { if (sunlabel->partitions[i].num_sectors) { uint32_t start = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * g_heads * g_sectors; uint32_t len = SUN_SSWAP32(sunlabel->partitions[i].num_sectors); printf("%s %c%c %9lu %9lu %9lu%c %2x %s\n", partname(disk_device, i+1, w), /* device */ (sunlabel->infos[i].flags & 0x01) ? 'u' : ' ', /* flags */ (sunlabel->infos[i].flags & 0x10) ? 'r' : ' ', (long) scround(start), /* start */ (long) scround(start+len), /* end */ (long) len / 2, len & 1 ? '+' : ' ', /* odd flag on end */ sunlabel->infos[i].id, /* type id */ partition_type(sunlabel->infos[i].id)); /* type name */ } } } #if ENABLE_FEATURE_FDISK_ADVANCED static void sun_set_alt_cyl(void) { sunlabel->nacyl = SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->nacyl), 65535, 0, "Number of alternate cylinders")); } static void sun_set_ncyl(int cyl) { sunlabel->ncyl = SUN_SSWAP16(cyl); } static void sun_set_xcyl(void) { sunlabel->sparecyl = SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->sparecyl), g_sectors, 0, "Extra sectors per cylinder")); } static void sun_set_ilfact(void) { sunlabel->ilfact = SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->ilfact), 32, 0, "Interleave factor")); } static void sun_set_rspeed(void) { sunlabel->rspeed = SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->rspeed), 100000, 0, "Rotation speed (rpm)")); } static void sun_set_pcylcount(void) { sunlabel->pcylcount = SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->pcylcount), 65535, 0, "Number of physical cylinders")); } #endif /* FEATURE_FDISK_ADVANCED */ static void sun_write_table(void) { unsigned short *ush = (unsigned short *)sunlabel; unsigned short csum = 0; while (ush < (unsigned short *)(&sunlabel->csum)) csum ^= *ush++; sunlabel->csum = csum; write_sector(0, sunlabel); } #endif /* SUN_LABEL */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/mdev.c��������������������������������������������������������������������0000644�0000000�0000000�00000075224�12263563520�015215� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * mdev - Mini udev for busybox * * Copyright 2005 Rob Landley <rob@landley.net> * Copyright 2005 Frank Sorenson <frank@tuxrocks.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //config:config MDEV //config: bool "mdev" //config: default y //config: select PLATFORM_LINUX //config: help //config: mdev is a mini-udev implementation for dynamically creating device //config: nodes in the /dev directory. //config: //config: For more information, please see docs/mdev.txt //config: //config:config FEATURE_MDEV_CONF //config: bool "Support /etc/mdev.conf" //config: default y //config: depends on MDEV //config: help //config: Add support for the mdev config file to control ownership and //config: permissions of the device nodes. //config: //config: For more information, please see docs/mdev.txt //config: //config:config FEATURE_MDEV_RENAME //config: bool "Support subdirs/symlinks" //config: default y //config: depends on FEATURE_MDEV_CONF //config: help //config: Add support for renaming devices and creating symlinks. //config: //config: For more information, please see docs/mdev.txt //config: //config:config FEATURE_MDEV_RENAME_REGEXP //config: bool "Support regular expressions substitutions when renaming device" //config: default y //config: depends on FEATURE_MDEV_RENAME //config: help //config: Add support for regular expressions substitutions when renaming //config: device. //config: //config:config FEATURE_MDEV_EXEC //config: bool "Support command execution at device addition/removal" //config: default y //config: depends on FEATURE_MDEV_CONF //config: help //config: This adds support for an optional field to /etc/mdev.conf for //config: executing commands when devices are created/removed. //config: //config: For more information, please see docs/mdev.txt //config: //config:config FEATURE_MDEV_LOAD_FIRMWARE //config: bool "Support loading of firmwares" //config: default y //config: depends on MDEV //config: help //config: Some devices need to load firmware before they can be usable. //config: //config: These devices will request userspace look up the files in //config: /lib/firmware/ and if it exists, send it to the kernel for //config: loading into the hardware. //applet:IF_MDEV(APPLET(mdev, BB_DIR_SBIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_MDEV) += mdev.o //usage:#define mdev_trivial_usage //usage: "[-s]" //usage:#define mdev_full_usage "\n\n" //usage: "mdev -s is to be run during boot to scan /sys and populate /dev.\n" //usage: "\n" //usage: "Bare mdev is a kernel hotplug helper. To activate it:\n" //usage: " echo /sbin/mdev >/proc/sys/kernel/hotplug\n" //usage: IF_FEATURE_MDEV_CONF( //usage: "\n" //usage: "It uses /etc/mdev.conf with lines\n" //usage: " [-][ENV=regex;]...DEVNAME UID:GID PERM" //usage: IF_FEATURE_MDEV_RENAME(" [>|=PATH]|[!]") //usage: IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]") //usage: "\n" //usage: "where DEVNAME is device name regex, @major,minor[-minor2], or\n" //usage: "environment variable regex. A common use of the latter is\n" //usage: "to load modules for hotplugged devices:\n" //usage: " $MODALIAS=.* 0:0 660 @modprobe \"$MODALIAS\"\n" //usage: ) //usage: "\n" //usage: "If /dev/mdev.seq file exists, mdev will wait for its value\n" //usage: "to match $SEQNUM variable. This prevents plug/unplug races.\n" //usage: "To activate this feature, create empty /dev/mdev.seq at boot.\n" //usage: "\n" //usage: "If /dev/mdev.log file exists, debug log will be appended to it." #include "libbb.h" #include "xregex.h" /* "mdev -s" scans /sys/class/xxx, looking for directories which have dev * file (it is of the form "M:m\n"). Example: /sys/class/tty/tty0/dev * contains "4:0\n". Directory name is taken as device name, path component * directly after /sys/class/ as subsystem. In this example, "tty0" and "tty". * Then mdev creates the /dev/device_name node. * If /sys/class/.../dev file does not exist, mdev still may act * on this device: see "@|$|*command args..." parameter in config file. * * mdev w/o parameters is called as hotplug helper. It takes device * and subsystem names from $DEVPATH and $SUBSYSTEM, extracts * maj,min from "/sys/$DEVPATH/dev" and also examines * $ACTION ("add"/"delete") and $FIRMWARE. * * If action is "add", mdev creates /dev/device_name similarly to mdev -s. * (todo: explain "delete" and $FIRMWARE) * * If /etc/mdev.conf exists, it may modify /dev/device_name's properties. * * Leading minus in 1st field means "don't stop on this line", otherwise * search is stopped after the matching line is encountered. * * $envvar=regex format is useful for loading modules for hot-plugged devices * which do not have driver loaded yet. In this case /sys/class/.../dev * does not exist, but $MODALIAS is set to needed module's name * (actually, an alias to it) by kernel. This rule instructs mdev * to load the module and exit: * $MODALIAS=.* 0:0 660 @modprobe "$MODALIAS" * The kernel will generate another hotplug event when /sys/class/.../dev * file appears. * * When line matches, the device node is created, chmod'ed and chown'ed, * moved to path, and if >path, a symlink to moved node is created, * all this if /sys/class/.../dev exists. * Examples: * =loop/ - moves to /dev/loop * >disk/sda%1 - moves to /dev/disk/sdaN, makes /dev/sdaN a symlink * * Then "command args..." is executed (via sh -c 'command args...'). * @:execute on creation, $:on deletion, *:on both. * This happens regardless of /sys/class/.../dev existence. */ /* Kernel's hotplug environment constantly changes. * Here are new cases I observed on 3.1.0: * * Case with $DEVNAME and $DEVICE, not just $DEVPATH: * ACTION=add * BUSNUM=001 * DEVICE=/proc/bus/usb/001/003 * DEVNAME=bus/usb/001/003 * DEVNUM=003 * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5 * DEVTYPE=usb_device * MAJOR=189 * MINOR=2 * PRODUCT=18d1/4e12/227 * SUBSYSTEM=usb * TYPE=0/0/0 * * Case with $DEVICE, but no $DEVNAME - apparenty, usb iface notification? * "Please load me a module" thing? * ACTION=add * DEVICE=/proc/bus/usb/001/003 * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0 * DEVTYPE=usb_interface * INTERFACE=8/6/80 * MODALIAS=usb:v18D1p4E12d0227dc00dsc00dp00ic08isc06ip50 * PRODUCT=18d1/4e12/227 * SUBSYSTEM=usb * TYPE=0/0/0 * * ACTION=add * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5 * DEVTYPE=scsi_host * SUBSYSTEM=scsi * * ACTION=add * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/scsi_host/host5 * SUBSYSTEM=scsi_host * * ACTION=add * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0 * DEVTYPE=scsi_target * SUBSYSTEM=scsi * * Case with strange $MODALIAS: * ACTION=add * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0 * DEVTYPE=scsi_device * MODALIAS=scsi:t-0x00 * SUBSYSTEM=scsi * * ACTION=add * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/scsi_disk/5:0:0:0 * SUBSYSTEM=scsi_disk * * ACTION=add * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/scsi_device/5:0:0:0 * SUBSYSTEM=scsi_device * * Case with explicit $MAJOR/$MINOR (no need to read /sys/$DEVPATH/dev?): * ACTION=add * DEVNAME=bsg/5:0:0:0 * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/bsg/5:0:0:0 * MAJOR=253 * MINOR=1 * SUBSYSTEM=bsg * * ACTION=add * DEVPATH=/devices/virtual/bdi/8:16 * SUBSYSTEM=bdi * * ACTION=add * DEVNAME=sdb * DEVPATH=/block/sdb * DEVTYPE=disk * MAJOR=8 * MINOR=16 * SUBSYSTEM=block * * Case with ACTION=change: * ACTION=change * DEVNAME=sdb * DEVPATH=/block/sdb * DEVTYPE=disk * DISK_MEDIA_CHANGE=1 * MAJOR=8 * MINOR=16 * SUBSYSTEM=block */ #define DEBUG_LVL 2 #if DEBUG_LVL >= 1 # define dbg1(...) do { if (G.verbose) bb_error_msg(__VA_ARGS__); } while(0) #else # define dbg1(...) ((void)0) #endif #if DEBUG_LVL >= 2 # define dbg2(...) do { if (G.verbose >= 2) bb_error_msg(__VA_ARGS__); } while(0) #else # define dbg2(...) ((void)0) #endif #if DEBUG_LVL >= 3 # define dbg3(...) do { if (G.verbose >= 3) bb_error_msg(__VA_ARGS__); } while(0) #else # define dbg3(...) ((void)0) #endif static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0" enum { OP_add, OP_remove }; struct envmatch { struct envmatch *next; char *envname; regex_t match; }; struct rule { bool keep_matching; bool regex_compiled; mode_t mode; int maj, min0, min1; struct bb_uidgid_t ugid; char *envvar; char *ren_mov; IF_FEATURE_MDEV_EXEC(char *r_cmd;) regex_t match; struct envmatch *envmatch; }; struct globals { int root_major, root_minor; smallint verbose; char *subsystem; char *subsys_env; /* for putenv("SUBSYSTEM=subsystem") */ #if ENABLE_FEATURE_MDEV_CONF const char *filename; parser_t *parser; struct rule **rule_vec; unsigned rule_idx; #endif struct rule cur_rule; char timestr[sizeof("60.123456")]; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { \ IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.maj = -1;) \ IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.mode = 0660;) \ } while (0) /* Prevent infinite loops in /sys symlinks */ #define MAX_SYSFS_DEPTH 3 /* We use additional bytes in make_device() */ #define SCRATCH_SIZE 128 #if ENABLE_FEATURE_MDEV_CONF static void make_default_cur_rule(void) { memset(&G.cur_rule, 0, sizeof(G.cur_rule)); G.cur_rule.maj = -1; /* "not a @major,minor rule" */ G.cur_rule.mode = 0660; } static void clean_up_cur_rule(void) { struct envmatch *e; free(G.cur_rule.envvar); free(G.cur_rule.ren_mov); if (G.cur_rule.regex_compiled) regfree(&G.cur_rule.match); IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);) e = G.cur_rule.envmatch; while (e) { free(e->envname); regfree(&e->match); e = e->next; } make_default_cur_rule(); } static char *parse_envmatch_pfx(char *val) { struct envmatch **nextp = &G.cur_rule.envmatch; for (;;) { struct envmatch *e; char *semicolon; char *eq = strchr(val, '='); if (!eq /* || eq == val? */) return val; if (endofname(val) != eq) return val; semicolon = strchr(eq, ';'); if (!semicolon) return val; /* ENVVAR=regex;... */ *nextp = e = xzalloc(sizeof(*e)); nextp = &e->next; e->envname = xstrndup(val, eq - val); *semicolon = '\0'; xregcomp(&e->match, eq + 1, REG_EXTENDED); *semicolon = ';'; val = semicolon + 1; } } static void parse_next_rule(void) { /* Note: on entry, G.cur_rule is set to default */ while (1) { char *tokens[4]; char *val; /* No PARSE_EOL_COMMENTS, because command may contain '#' chars */ if (!config_read(G.parser, tokens, 4, 3, "# \t", PARSE_NORMAL & ~PARSE_EOL_COMMENTS)) break; /* Fields: [-]regex uid:gid mode [alias] [cmd] */ dbg3("token1:'%s'", tokens[1]); /* 1st field */ val = tokens[0]; G.cur_rule.keep_matching = ('-' == val[0]); val += G.cur_rule.keep_matching; /* swallow leading dash */ val = parse_envmatch_pfx(val); if (val[0] == '@') { /* @major,minor[-minor2] */ /* (useful when name is ambiguous: * "/sys/class/usb/lp0" and * "/sys/class/printer/lp0") */ int sc = sscanf(val, "@%u,%u-%u", &G.cur_rule.maj, &G.cur_rule.min0, &G.cur_rule.min1); if (sc < 2 || G.cur_rule.maj < 0) { bb_error_msg("bad @maj,min on line %d", G.parser->lineno); goto next_rule; } if (sc == 2) G.cur_rule.min1 = G.cur_rule.min0; } else { char *eq = strchr(val, '='); if (val[0] == '$') { /* $ENVVAR=regex ... */ val++; if (!eq) { bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno); goto next_rule; } G.cur_rule.envvar = xstrndup(val, eq - val); val = eq + 1; } xregcomp(&G.cur_rule.match, val, REG_EXTENDED); G.cur_rule.regex_compiled = 1; } /* 2nd field: uid:gid - device ownership */ if (get_uidgid(&G.cur_rule.ugid, tokens[1], /*allow_numeric:*/ 1) == 0) { bb_error_msg("unknown user/group '%s' on line %d", tokens[1], G.parser->lineno); goto next_rule; } /* 3rd field: mode - device permissions */ bb_parse_mode(tokens[2], &G.cur_rule.mode); /* 4th field (opt): ">|=alias" or "!" to not create the node */ val = tokens[3]; if (ENABLE_FEATURE_MDEV_RENAME && val && strchr(">=!", val[0])) { char *s = skip_non_whitespace(val); G.cur_rule.ren_mov = xstrndup(val, s - val); val = skip_whitespace(s); } if (ENABLE_FEATURE_MDEV_EXEC && val && val[0]) { const char *s = "$@*"; const char *s2 = strchr(s, val[0]); if (!s2) { bb_error_msg("bad line %u", G.parser->lineno); goto next_rule; } IF_FEATURE_MDEV_EXEC(G.cur_rule.r_cmd = xstrdup(val);) } return; next_rule: clean_up_cur_rule(); } /* while (config_read) */ dbg3("config_close(G.parser)"); config_close(G.parser); G.parser = NULL; return; } /* If mdev -s, we remember rules in G.rule_vec[]. * Otherwise, there is no point in doing it, and we just * save only one parsed rule in G.cur_rule. */ static const struct rule *next_rule(void) { struct rule *rule; /* Open conf file if we didn't do it yet */ if (!G.parser && G.filename) { dbg3("config_open('%s')", G.filename); G.parser = config_open2(G.filename, fopen_for_read); G.filename = NULL; } if (G.rule_vec) { /* mdev -s */ /* Do we have rule parsed already? */ if (G.rule_vec[G.rule_idx]) { dbg3("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]); return G.rule_vec[G.rule_idx++]; } make_default_cur_rule(); } else { /* not mdev -s */ clean_up_cur_rule(); } /* Parse one more rule if file isn't fully read */ rule = &G.cur_rule; if (G.parser) { parse_next_rule(); if (G.rule_vec) { /* mdev -s */ rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule)); G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx); G.rule_vec[G.rule_idx++] = rule; dbg3("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]); } } return rule; } static int env_matches(struct envmatch *e) { while (e) { int r; char *val = getenv(e->envname); if (!val) return 0; r = regexec(&e->match, val, /*size*/ 0, /*range[]*/ NULL, /*eflags*/ 0); if (r != 0) /* no match */ return 0; e = e->next; } return 1; } #else # define next_rule() (&G.cur_rule) #endif static void mkdir_recursive(char *name) { /* if name has many levels ("dir1/dir2"), * bb_make_directory() will create dir1 according to umask, * not according to its "mode" parameter. * Since we run with umask=0, need to temporarily switch it. */ umask(022); /* "dir1" (if any) will be 0755 too */ bb_make_directory(name, 0755, FILEUTILS_RECUR); umask(0); } /* Builds an alias path. * This function potentionally reallocates the alias parameter. * Only used for ENABLE_FEATURE_MDEV_RENAME */ static char *build_alias(char *alias, const char *device_name) { char *dest; /* ">bar/": rename to bar/device_name */ /* ">bar[/]baz": rename to bar[/]baz */ dest = strrchr(alias, '/'); if (dest) { /* ">bar/[baz]" ? */ *dest = '\0'; /* mkdir bar */ mkdir_recursive(alias); *dest = '/'; if (dest[1] == '\0') { /* ">bar/" => ">bar/device_name" */ dest = alias; alias = concat_path_file(alias, device_name); free(dest); } } return alias; } /* mknod in /dev based on a path like "/sys/block/hda/hda1" * NB1: path parameter needs to have SCRATCH_SIZE scratch bytes * after NUL, but we promise to not mangle (IOW: to restore NUL if needed) * path string. * NB2: "mdev -s" may call us many times, do not leak memory/fds! * * device_name = $DEVNAME (may be NULL) * path = /sys/$DEVPATH */ static void make_device(char *device_name, char *path, int operation) { int major, minor, type, len; char *path_end = path + strlen(path); /* Try to read major/minor string. Note that the kernel puts \n after * the data, so we don't need to worry about null terminating the string * because sscanf() will stop at the first nondigit, which \n is. * We also depend on path having writeable space after it. */ major = -1; if (operation == OP_add) { strcpy(path_end, "/dev"); len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1); *path_end = '\0'; if (len < 1) { if (!ENABLE_FEATURE_MDEV_EXEC) return; /* no "dev" file, but we can still run scripts * based on device name */ } else if (sscanf(path_end + 1, "%u:%u", &major, &minor) == 2) { dbg1("dev %u,%u", major, minor); } else { major = -1; } } /* else: for delete, -1 still deletes the node, but < -1 suppresses that */ /* Determine device name */ if (!device_name) { /* * There was no $DEVNAME envvar (for example, mdev -s never has). * But it is very useful: it contains the *path*, not only basename, * Thankfully, uevent file has it. * Example of .../sound/card0/controlC0/uevent file on Linux-3.7.7: * MAJOR=116 * MINOR=7 * DEVNAME=snd/controlC0 */ strcpy(path_end, "/uevent"); len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1); if (len < 0) len = 0; *path_end = '\0'; path_end[1 + len] = '\0'; device_name = strstr(path_end + 1, "\nDEVNAME="); if (device_name) { device_name += sizeof("\nDEVNAME=")-1; strchrnul(device_name, '\n')[0] = '\0'; } else { /* Fall back to just basename */ device_name = (char*) bb_basename(path); } } /* Determine device type */ /* * http://kernel.org/doc/pending/hotplug.txt says that only * "/sys/block/..." is for block devices. "/sys/bus" etc is not. * But since 2.6.25 block devices are also in /sys/class/block. * We use strstr("/block/") to forestall future surprises. */ type = S_IFCHR; if (strstr(path, "/block/") || (G.subsystem && strncmp(G.subsystem, "block", 5) == 0)) type = S_IFBLK; #if ENABLE_FEATURE_MDEV_CONF G.rule_idx = 0; /* restart from the beginning (think mdev -s) */ #endif for (;;) { const char *str_to_match; regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP]; char *command; char *alias; char aliaslink = aliaslink; /* for compiler */ char *node_name; const struct rule *rule; str_to_match = device_name; rule = next_rule(); #if ENABLE_FEATURE_MDEV_CONF if (!env_matches(rule->envmatch)) continue; if (rule->maj >= 0) { /* @maj,min rule */ if (major != rule->maj) continue; if (minor < rule->min0 || minor > rule->min1) continue; memset(off, 0, sizeof(off)); goto rule_matches; } if (rule->envvar) { /* $envvar=regex rule */ str_to_match = getenv(rule->envvar); dbg3("getenv('%s'):'%s'", rule->envvar, str_to_match); if (!str_to_match) continue; } /* else: str_to_match = device_name */ if (rule->regex_compiled) { int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0); dbg3("regex_match for '%s':%d", str_to_match, regex_match); //bb_error_msg("matches:"); //for (int i = 0; i < ARRAY_SIZE(off); i++) { // if (off[i].rm_so < 0) continue; // bb_error_msg("match %d: '%.*s'\n", i, // (int)(off[i].rm_eo - off[i].rm_so), // device_name + off[i].rm_so); //} if (regex_match != 0 /* regexec returns whole pattern as "range" 0 */ || off[0].rm_so != 0 || (int)off[0].rm_eo != (int)strlen(str_to_match) ) { continue; /* this rule doesn't match */ } } /* else: it's final implicit "match-all" rule */ rule_matches: dbg2("rule matched, line %d", G.parser ? G.parser->lineno : -1); #endif /* Build alias name */ alias = NULL; if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) { aliaslink = rule->ren_mov[0]; if (aliaslink == '!') { /* "!": suppress node creation/deletion */ major = -2; } else if (aliaslink == '>' || aliaslink == '=') { if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) { char *s; char *p; unsigned n; /* substitute %1..9 with off[1..9], if any */ n = 0; s = rule->ren_mov; while (*s) if (*s++ == '%') n++; p = alias = xzalloc(strlen(rule->ren_mov) + n * strlen(str_to_match)); s = rule->ren_mov + 1; while (*s) { *p = *s; if ('%' == *s) { unsigned i = (s[1] - '0'); if (i <= 9 && off[i].rm_so >= 0) { n = off[i].rm_eo - off[i].rm_so; strncpy(p, str_to_match + off[i].rm_so, n); p += n - 1; s++; } } p++; s++; } } else { alias = xstrdup(rule->ren_mov + 1); } } } dbg3("alias:'%s'", alias); command = NULL; IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;) if (command) { /* Are we running this command now? * Run @cmd on create, $cmd on delete, *cmd on any */ if ((command[0] == '@' && operation == OP_add) || (command[0] == '$' && operation == OP_remove) || (command[0] == '*') ) { command++; } else { command = NULL; } } dbg3("command:'%s'", command); /* "Execute" the line we found */ node_name = device_name; if (ENABLE_FEATURE_MDEV_RENAME && alias) { node_name = alias = build_alias(alias, device_name); dbg3("alias2:'%s'", alias); } if (operation == OP_add && major >= 0) { char *slash = strrchr(node_name, '/'); if (slash) { *slash = '\0'; mkdir_recursive(node_name); *slash = '/'; } if (ENABLE_FEATURE_MDEV_CONF) { dbg1("mknod %s (%d,%d) %o" " %u:%u", node_name, major, minor, rule->mode | type, rule->ugid.uid, rule->ugid.gid ); } else { dbg1("mknod %s (%d,%d) %o", node_name, major, minor, rule->mode | type ); } if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST) bb_perror_msg("can't create '%s'", node_name); if (ENABLE_FEATURE_MDEV_CONF) { chmod(node_name, rule->mode); chown(node_name, rule->ugid.uid, rule->ugid.gid); } if (major == G.root_major && minor == G.root_minor) symlink(node_name, "root"); if (ENABLE_FEATURE_MDEV_RENAME && alias) { if (aliaslink == '>') { //TODO: on devtmpfs, device_name already exists and symlink() fails. //End result is that instead of symlink, we have two nodes. //What should be done? dbg1("symlink: %s", device_name); symlink(node_name, device_name); } } } if (ENABLE_FEATURE_MDEV_EXEC && command) { /* setenv will leak memory, use putenv/unsetenv/free */ char *s = xasprintf("%s=%s", "MDEV", node_name); putenv(s); dbg1("running: %s", command); if (system(command) == -1) bb_perror_msg("can't run '%s'", command); bb_unsetenv_and_free(s); } if (operation == OP_remove && major >= -1) { if (ENABLE_FEATURE_MDEV_RENAME && alias) { if (aliaslink == '>') { dbg1("unlink: %s", device_name); unlink(device_name); } } dbg1("unlink: %s", node_name); unlink(node_name); } if (ENABLE_FEATURE_MDEV_RENAME) free(alias); /* We found matching line. * Stop unless it was prefixed with '-' */ if (!ENABLE_FEATURE_MDEV_CONF || !rule->keep_matching) break; } /* for (;;) */ } /* File callback for /sys/ traversal */ static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf UNUSED_PARAM, void *userData, int depth UNUSED_PARAM) { size_t len = strlen(fileName) - 4; /* can't underflow */ char *scratch = userData; /* len check is for paranoid reasons */ if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX) return FALSE; strcpy(scratch, fileName); scratch[len] = '\0'; make_device(/*DEVNAME:*/ NULL, scratch, OP_add); return TRUE; } /* Directory callback for /sys/ traversal */ static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM, struct stat *statbuf UNUSED_PARAM, void *userData UNUSED_PARAM, int depth) { /* Extract device subsystem -- the name of the directory * under /sys/class/ */ if (1 == depth) { free(G.subsystem); if (G.subsys_env) { bb_unsetenv_and_free(G.subsys_env); G.subsys_env = NULL; } G.subsystem = strrchr(fileName, '/'); if (G.subsystem) { G.subsystem = xstrdup(G.subsystem + 1); G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem); putenv(G.subsys_env); } } return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE); } /* For the full gory details, see linux/Documentation/firmware_class/README * * Firmware loading works like this: * - kernel sets FIRMWARE env var * - userspace checks /lib/firmware/$FIRMWARE * - userspace waits for /sys/$DEVPATH/loading to appear * - userspace writes "1" to /sys/$DEVPATH/loading * - userspace copies /lib/firmware/$FIRMWARE into /sys/$DEVPATH/data * - userspace writes "0" (worked) or "-1" (failed) to /sys/$DEVPATH/loading * - kernel loads firmware into device */ static void load_firmware(const char *firmware, const char *sysfs_path) { int cnt; int firmware_fd, loading_fd; /* check for /lib/firmware/$FIRMWARE */ xchdir("/lib/firmware"); firmware_fd = open(firmware, O_RDONLY); /* can fail */ /* check for /sys/$DEVPATH/loading ... give 30 seconds to appear */ xchdir(sysfs_path); for (cnt = 0; cnt < 30; ++cnt) { loading_fd = open("loading", O_WRONLY); if (loading_fd >= 0) goto loading; sleep(1); } goto out; loading: cnt = 0; if (firmware_fd >= 0) { int data_fd; /* tell kernel we're loading by "echo 1 > /sys/$DEVPATH/loading" */ if (full_write(loading_fd, "1", 1) != 1) goto out; /* load firmware into /sys/$DEVPATH/data */ data_fd = open("data", O_WRONLY); if (data_fd < 0) goto out; cnt = bb_copyfd_eof(firmware_fd, data_fd); if (ENABLE_FEATURE_CLEAN_UP) close(data_fd); } /* Tell kernel result by "echo [0|-1] > /sys/$DEVPATH/loading" * Note: we emit -1 also if firmware file wasn't found. * There are cases when otherwise kernel would wait for minutes * before timing out. */ if (cnt > 0) full_write(loading_fd, "0", 1); else full_write(loading_fd, "-1", 2); out: xchdir("/dev"); if (ENABLE_FEATURE_CLEAN_UP) { close(firmware_fd); close(loading_fd); } } static char *curtime(void) { struct timeval tv; gettimeofday(&tv, NULL); sprintf(G.timestr, "%u.%06u", (unsigned)tv.tv_sec % 60, (unsigned)tv.tv_usec); return G.timestr; } static void open_mdev_log(const char *seq, unsigned my_pid) { int logfd = open("mdev.log", O_WRONLY | O_APPEND); if (logfd >= 0) { xmove_fd(logfd, STDERR_FILENO); G.verbose = 2; applet_name = xasprintf("%s[%s]", applet_name, seq ? seq : utoa(my_pid)); } } /* If it exists, does /dev/mdev.seq match $SEQNUM? * If it does not match, earlier mdev is running * in parallel, and we need to wait. * Active mdev pokes us with SIGCHLD to check the new file. */ static int wait_for_seqfile(const char *seq) { /* We time out after 2 sec */ static const struct timespec ts = { 0, 32*1000*1000 }; int timeout = 2000 / 32; int seq_fd = -1; int do_once = 1; sigset_t set_CHLD; sigemptyset(&set_CHLD); sigaddset(&set_CHLD, SIGCHLD); sigprocmask(SIG_BLOCK, &set_CHLD, NULL); for (;;) { int seqlen; char seqbuf[sizeof(int)*3 + 2]; if (seq_fd < 0) { seq_fd = open("mdev.seq", O_RDWR); if (seq_fd < 0) break; } seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0); if (seqlen < 0) { close(seq_fd); seq_fd = -1; break; } seqbuf[seqlen] = '\0'; if (seqbuf[0] == '\n') { /* seed file: write out seq ASAP */ xwrite_str(seq_fd, seq); xlseek(seq_fd, 0, SEEK_SET); dbg2("first seq written"); break; } if (strcmp(seq, seqbuf) == 0) { /* correct idx */ break; } if (do_once) { dbg2("%s waiting for '%s'", curtime(), seqbuf); do_once = 0; } if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) { dbg3("woken up"); continue; /* don't decrement timeout! */ } if (--timeout == 0) { dbg1("%s waiting for '%s'", "timed out", seqbuf); break; } } sigprocmask(SIG_UNBLOCK, &set_CHLD, NULL); return seq_fd; } static void signal_mdevs(unsigned my_pid) { procps_status_t* p = NULL; while ((p = procps_scan(p, PSSCAN_ARGV0)) != NULL) { if (p->pid != my_pid && p->argv0 && strcmp(bb_basename(p->argv0), "mdev") == 0 ) { kill(p->pid, SIGCHLD); } } } int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mdev_main(int argc UNUSED_PARAM, char **argv) { RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE); INIT_G(); #if ENABLE_FEATURE_MDEV_CONF G.filename = "/etc/mdev.conf"; #endif /* We can be called as hotplug helper */ /* Kernel cannot provide suitable stdio fds for us, do it ourself */ bb_sanitize_stdio(); /* Force the configuration file settings exactly */ umask(0); xchdir("/dev"); if (argv[1] && strcmp(argv[1], "-s") == 0) { /* * Scan: mdev -s */ struct stat st; #if ENABLE_FEATURE_MDEV_CONF /* Same as xrealloc_vector(NULL, 4, 0): */ G.rule_vec = xzalloc((1 << 4) * sizeof(*G.rule_vec)); #endif xstat("/", &st); G.root_major = major(st.st_dev); G.root_minor = minor(st.st_dev); putenv((char*)"ACTION=add"); /* ACTION_FOLLOWLINKS is needed since in newer kernels * /sys/block/loop* (for example) are symlinks to dirs, * not real directories. * (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs, * but we can't enforce that on users) */ if (access("/sys/class/block", F_OK) != 0) { /* Scan obsolete /sys/block only if /sys/class/block * doesn't exist. Otherwise we'll have dupes. * Also, do not complain if it doesn't exist. * Some people configure kernel to have no blockdevs. */ recursive_action("/sys/block", ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET, fileAction, dirAction, temp, 0); } recursive_action("/sys/class", ACTION_RECURSE | ACTION_FOLLOWLINKS, fileAction, dirAction, temp, 0); } else { char *fw; char *seq; char *action; char *env_devname; char *env_devpath; unsigned my_pid; int seq_fd; smalluint op; /* Hotplug: * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev * ACTION can be "add", "remove", "change" * DEVPATH is like "/block/sda" or "/class/input/mice" */ env_devname = getenv("DEVNAME"); /* can be NULL */ G.subsystem = getenv("SUBSYSTEM"); action = getenv("ACTION"); env_devpath = getenv("DEVPATH"); if (!action || !env_devpath /*|| !G.subsystem*/) bb_show_usage(); fw = getenv("FIRMWARE"); seq = getenv("SEQNUM"); op = index_in_strings(keywords, action); my_pid = getpid(); open_mdev_log(seq, my_pid); seq_fd = seq ? wait_for_seqfile(seq) : -1; dbg1("%s " "ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s" "%s%s", curtime(), action, G.subsystem, env_devname, env_devpath, fw ? " FW:" : "", fw ? fw : "" ); snprintf(temp, PATH_MAX, "/sys%s", env_devpath); if (op == OP_remove) { /* Ignoring "remove firmware". It was reported * to happen and to cause erroneous deletion * of device nodes. */ if (!fw) make_device(env_devname, temp, op); } else { make_device(env_devname, temp, op); if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) { if (op == OP_add && fw) load_firmware(fw, temp); } } dbg1("%s exiting", curtime()); if (seq_fd >= 0) { xwrite_str(seq_fd, utoa(xatou(seq) + 1)); signal_mdevs(my_pid); } } if (ENABLE_FEATURE_CLEAN_UP) RELEASE_CONFIG_BUFFER(temp); return EXIT_SUCCESS; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/fdformat.c����������������������������������������������������������������0000644�0000000�0000000�00000007270�12263563520�016060� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* fdformat.c - Low-level formats a floppy disk - Werner Almesberger * 5 July 2003 -- modified for Busybox by Erik Andersen * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define fdformat_trivial_usage //usage: "[-n] DEVICE" //usage:#define fdformat_full_usage "\n\n" //usage: "Format floppy disk\n" //usage: "\n -n Don't verify after format" #include "libbb.h" /* Stuff extracted from linux/fd.h */ struct floppy_struct { unsigned int size, /* nr of sectors total */ sect, /* sectors per track */ head, /* nr of heads */ track, /* nr of tracks */ stretch; /* !=0 means double track steps */ #define FD_STRETCH 1 #define FD_SWAPSIDES 2 unsigned char gap, /* gap1 size */ rate, /* data rate. |= 0x40 for perpendicular */ #define FD_2M 0x4 #define FD_SIZECODEMASK 0x38 #define FD_SIZECODE(floppy) (((((floppy)->rate&FD_SIZECODEMASK)>> 3)+ 2) %8) #define FD_SECTSIZE(floppy) ( (floppy)->rate & FD_2M ? \ 512 : 128 << FD_SIZECODE(floppy) ) #define FD_PERP 0x40 spec1, /* stepping rate, head unload time */ fmt_gap; /* gap2 size */ const char * name; /* used only for predefined formats */ }; struct format_descr { unsigned int device,head,track; }; #define FDFMTBEG _IO(2,0x47) #define FDFMTTRK _IOW(2,0x48, struct format_descr) #define FDFMTEND _IO(2,0x49) #define FDGETPRM _IOR(2, 0x04, struct floppy_struct) #define FD_FILL_BYTE 0xF6 /* format fill byte. */ int fdformat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fdformat_main(int argc UNUSED_PARAM, char **argv) { int fd, n, cyl, read_bytes, verify; unsigned char *data; struct stat st; struct floppy_struct param; struct format_descr descr; opt_complementary = "=1"; /* must have 1 param */ verify = !getopt32(argv, "n"); argv += optind; xstat(*argv, &st); if (!S_ISBLK(st.st_mode)) { bb_error_msg_and_die("%s: not a block device", *argv); /* do not test major - perhaps this was an USB floppy */ } /* O_RDWR for formatting and verifying */ fd = xopen(*argv, O_RDWR); /* original message was: "Could not determine current format type" */ xioctl(fd, FDGETPRM, ¶m); printf("%s-sided, %u tracks, %u sec/track. Total capacity %d kB\n", (param.head == 2) ? "Double" : "Single", param.track, param.sect, param.size >> 1); /* FORMAT */ printf("Formatting... "); xioctl(fd, FDFMTBEG, NULL); /* n == track */ for (n = 0; n < param.track; n++) { descr.head = 0; descr.track = n; xioctl(fd, FDFMTTRK, &descr); printf("%3d\b\b\b", n); if (param.head == 2) { descr.head = 1; xioctl(fd, FDFMTTRK, &descr); } } xioctl(fd, FDFMTEND, NULL); printf("done\n"); /* VERIFY */ if (verify) { /* n == cyl_size */ n = param.sect*param.head*512; data = xmalloc(n); printf("Verifying... "); for (cyl = 0; cyl < param.track; cyl++) { printf("%3d\b\b\b", cyl); read_bytes = safe_read(fd, data, n); if (read_bytes != n) { if (read_bytes < 0) { bb_perror_msg(bb_msg_read_error); } bb_error_msg_and_die("problem reading cylinder %d, " "expected %d, read %d", cyl, n, read_bytes); // FIXME: maybe better seek & continue?? } /* Check backwards so we don't need a counter */ while (--read_bytes >= 0) { if (data[read_bytes] != FD_FILL_BYTE) { printf("bad data in cyl %d\nContinuing... ", cyl); } } } /* There is no point in freeing blocks at the end of a program, because all of the program's space is given back to the system when the process terminates.*/ if (ENABLE_FEATURE_CLEAN_UP) free(data); printf("done\n"); } if (ENABLE_FEATURE_CLEAN_UP) close(fd); /* Don't bother closing. Exit does * that, so we can save a few bytes */ return EXIT_SUCCESS; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/ipcs.c��������������������������������������������������������������������0000644�0000000�0000000�00000041741�12263563520�015215� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * ipcs.c -- provides information on allocated ipc resources. * * 01 Sept 2004 - Rodney Radford <rradford@mindspring.com> * Adapted for busybox from util-linux-2.12a. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define ipcs_trivial_usage //usage: "[[-smq] -i shmid] | [[-asmq] [-tcplu]]" //usage:#define ipcs_full_usage "\n\n" //usage: " -i Show specific resource" //usage: "\nResource specification:" //usage: "\n -m Shared memory segments" //usage: "\n -q Message queues" //usage: "\n -s Semaphore arrays" //usage: "\n -a All (default)" //usage: "\nOutput format:" //usage: "\n -t Time" //usage: "\n -c Creator" //usage: "\n -p Pid" //usage: "\n -l Limits" //usage: "\n -u Summary" /* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */ /* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */ /* X/OPEN tells us to use <sys/{types,ipc,shm}.h> for shmctl() */ #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/msg.h> #include <sys/shm.h> #include "libbb.h" /*-------------------------------------------------------------------*/ /* SHM_DEST and SHM_LOCKED are defined in kernel headers, but inside #ifdef __KERNEL__ ... #endif */ #ifndef SHM_DEST /* shm_mode upper byte flags */ #define SHM_DEST 01000 /* segment will be destroyed on last detach */ #define SHM_LOCKED 02000 /* segment will not be swapped */ #endif /* For older kernels the same holds for the defines below */ #ifndef MSG_STAT #define MSG_STAT 11 #define MSG_INFO 12 #endif #ifndef SHM_STAT #define SHM_STAT 13 #define SHM_INFO 14 struct shm_info { int used_ids; unsigned long shm_tot; /* total allocated shm */ unsigned long shm_rss; /* total resident shm */ unsigned long shm_swp; /* total swapped shm */ unsigned long swap_attempts; unsigned long swap_successes; }; #endif #ifndef SEM_STAT #define SEM_STAT 18 #define SEM_INFO 19 #endif /* Some versions of libc only define IPC_INFO when __USE_GNU is defined. */ #ifndef IPC_INFO #define IPC_INFO 3 #endif /*-------------------------------------------------------------------*/ /* The last arg of semctl is a union semun, but where is it defined? X/OPEN tells us to define it ourselves, but until recently Linux include files would also define it. */ #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) /* union semun is defined by including <sys/sem.h> */ #else /* according to X/OPEN we have to define it ourselves */ union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; }; #endif /* X/OPEN (Jan 1987) does not define fields key, seq in struct ipc_perm; libc 4/5 does not mention struct ipc_term at all, but includes <linux/ipc.h>, which defines a struct ipc_perm with such fields. glibc-1.09 has no support for sysv ipc. glibc 2 uses __key, __seq */ #if defined(__GNU_LIBRARY__) && __GNU_LIBRARY__ > 1 #define KEY __key #else #define KEY key #endif #define LIMITS 1 #define STATUS 2 #define CREATOR 3 #define TIME 4 #define PID 5 static char format; static void print_perms(int id, struct ipc_perm *ipcp) { struct passwd *pw; struct group *gr; printf("%-10d %-10o", id, ipcp->mode & 0777); pw = getpwuid(ipcp->cuid); if (pw) printf(" %-10s", pw->pw_name); else printf(" %-10d", ipcp->cuid); gr = getgrgid(ipcp->cgid); if (gr) printf(" %-10s", gr->gr_name); else printf(" %-10d", ipcp->cgid); pw = getpwuid(ipcp->uid); if (pw) printf(" %-10s", pw->pw_name); else printf(" %-10d", ipcp->uid); gr = getgrgid(ipcp->gid); if (gr) printf(" %-10s\n", gr->gr_name); else printf(" %-10d\n", ipcp->gid); } static NOINLINE void do_shm(void) { int maxid, shmid, id; struct shmid_ds shmseg; struct shm_info shm_info; struct shminfo shminfo; struct ipc_perm *ipcp = &shmseg.shm_perm; struct passwd *pw; maxid = shmctl(0, SHM_INFO, (struct shmid_ds *) (void *) &shm_info); if (maxid < 0) { printf("kernel not configured for %s\n", "shared memory"); return; } switch (format) { case LIMITS: printf("------ Shared Memory %s --------\n", "Limits"); if ((shmctl(0, IPC_INFO, (struct shmid_ds *) (void *) &shminfo)) < 0) return; /* glibc 2.1.3 and all earlier libc's have ints as fields * of struct shminfo; glibc 2.1.91 has unsigned long; ach */ printf("max number of segments = %lu\n" "max seg size (kbytes) = %lu\n" "max total shared memory (pages) = %lu\n" "min seg size (bytes) = %lu\n", (unsigned long) shminfo.shmmni, (unsigned long) (shminfo.shmmax >> 10), (unsigned long) shminfo.shmall, (unsigned long) shminfo.shmmin); return; case STATUS: printf("------ Shared Memory %s --------\n", "Status"); printf("segments allocated %d\n" "pages allocated %lu\n" "pages resident %lu\n" "pages swapped %lu\n" "Swap performance: %lu attempts\t%lu successes\n", shm_info.used_ids, shm_info.shm_tot, shm_info.shm_rss, shm_info.shm_swp, shm_info.swap_attempts, shm_info.swap_successes); return; case CREATOR: printf("------ Shared Memory %s --------\n", "Segment Creators/Owners"); printf("%-10s %-10s %-10s %-10s %-10s %-10s\n", "shmid", "perms", "cuid", "cgid", "uid", "gid"); break; case TIME: printf("------ Shared Memory %s --------\n", "Attach/Detach/Change Times"); printf("%-10s %-10s %-20s %-20s %-20s\n", "shmid", "owner", "attached", "detached", "changed"); break; case PID: printf("------ Shared Memory %s --------\n", "Creator/Last-op"); printf("%-10s %-10s %-10s %-10s\n", "shmid", "owner", "cpid", "lpid"); break; default: printf("------ Shared Memory %s --------\n", "Segments"); printf("%-10s %-10s %-10s %-10s %-10s %-10s %-12s\n", "key", "shmid", "owner", "perms", "bytes", "nattch", "status"); break; } for (id = 0; id <= maxid; id++) { shmid = shmctl(id, SHM_STAT, &shmseg); if (shmid < 0) continue; if (format == CREATOR) { print_perms(shmid, ipcp); continue; } pw = getpwuid(ipcp->uid); switch (format) { case TIME: if (pw) printf("%-10d %-10.10s", shmid, pw->pw_name); else printf("%-10d %-10d", shmid, ipcp->uid); /* ctime uses static buffer: use separate calls */ printf(" %-20.16s", shmseg.shm_atime ? ctime(&shmseg.shm_atime) + 4 : "Not set"); printf(" %-20.16s", shmseg.shm_dtime ? ctime(&shmseg.shm_dtime) + 4 : "Not set"); printf(" %-20.16s\n", shmseg.shm_ctime ? ctime(&shmseg.shm_ctime) + 4 : "Not set"); break; case PID: if (pw) printf("%-10d %-10.10s", shmid, pw->pw_name); else printf("%-10d %-10d", shmid, ipcp->uid); printf(" %-10d %-10d\n", shmseg.shm_cpid, shmseg.shm_lpid); break; default: printf("0x%08x ", ipcp->KEY); if (pw) printf("%-10d %-10.10s", shmid, pw->pw_name); else printf("%-10d %-10d", shmid, ipcp->uid); printf(" %-10o %-10lu %-10ld %-6s %-6s\n", ipcp->mode & 0777, /* * earlier: int, Austin has size_t */ (unsigned long) shmseg.shm_segsz, /* * glibc-2.1.3 and earlier has unsigned short; * Austin has shmatt_t */ (long) shmseg.shm_nattch, ipcp->mode & SHM_DEST ? "dest" : " ", ipcp->mode & SHM_LOCKED ? "locked" : " "); break; } } } static NOINLINE void do_sem(void) { int maxid, semid, id; struct semid_ds semary; struct seminfo seminfo; struct ipc_perm *ipcp = &semary.sem_perm; struct passwd *pw; union semun arg; arg.array = (unsigned short *) (void *) &seminfo; maxid = semctl(0, 0, SEM_INFO, arg); if (maxid < 0) { printf("kernel not configured for %s\n", "semaphores"); return; } switch (format) { case LIMITS: printf("------ Semaphore %s --------\n", "Limits"); arg.array = (unsigned short *) (void *) &seminfo; /* damn union */ if ((semctl(0, 0, IPC_INFO, arg)) < 0) return; printf("max number of arrays = %d\n" "max semaphores per array = %d\n" "max semaphores system wide = %d\n" "max ops per semop call = %d\n" "semaphore max value = %d\n", seminfo.semmni, seminfo.semmsl, seminfo.semmns, seminfo.semopm, seminfo.semvmx); return; case STATUS: printf("------ Semaphore %s --------\n", "Status"); printf("used arrays = %d\n" "allocated semaphores = %d\n", seminfo.semusz, seminfo.semaem); return; case CREATOR: printf("------ Semaphore %s --------\n", "Arrays Creators/Owners"); printf("%-10s %-10s %-10s %-10s %-10s %-10s\n", "semid", "perms", "cuid", "cgid", "uid", "gid"); break; case TIME: printf("------ Shared Memory %s --------\n", "Operation/Change Times"); printf("%-8s %-10s %-26.24s %-26.24s\n", "shmid", "owner", "last-op", "last-changed"); break; case PID: break; default: printf("------ Semaphore %s --------\n", "Arrays"); printf("%-10s %-10s %-10s %-10s %-10s\n", "key", "semid", "owner", "perms", "nsems"); break; } for (id = 0; id <= maxid; id++) { arg.buf = (struct semid_ds *) &semary; semid = semctl(id, 0, SEM_STAT, arg); if (semid < 0) continue; if (format == CREATOR) { print_perms(semid, ipcp); continue; } pw = getpwuid(ipcp->uid); switch (format) { case TIME: if (pw) printf("%-8d %-10.10s", semid, pw->pw_name); else printf("%-8d %-10d", semid, ipcp->uid); /* ctime uses static buffer: use separate calls */ printf(" %-26.24s", semary.sem_otime ? ctime(&semary.sem_otime) : "Not set"); printf(" %-26.24s\n", semary.sem_ctime ? ctime(&semary.sem_ctime) : "Not set"); break; case PID: break; default: printf("0x%08x ", ipcp->KEY); if (pw) printf("%-10d %-10.9s", semid, pw->pw_name); else printf("%-10d %-9d", semid, ipcp->uid); printf(" %-10o %-10ld\n", ipcp->mode & 0777, /* * glibc-2.1.3 and earlier has unsigned short; * glibc-2.1.91 has variation between * unsigned short and unsigned long * Austin prescribes unsigned short. */ (long) semary.sem_nsems); break; } } } static NOINLINE void do_msg(void) { int maxid, msqid, id; struct msqid_ds msgque; struct msginfo msginfo; struct ipc_perm *ipcp = &msgque.msg_perm; struct passwd *pw; maxid = msgctl(0, MSG_INFO, (struct msqid_ds *) (void *) &msginfo); if (maxid < 0) { printf("kernel not configured for %s\n", "message queues"); return; } switch (format) { case LIMITS: if ((msgctl(0, IPC_INFO, (struct msqid_ds *) (void *) &msginfo)) < 0) return; printf("------ Message%s --------\n", "s: Limits"); printf("max queues system wide = %d\n" "max size of message (bytes) = %d\n" "default max size of queue (bytes) = %d\n", msginfo.msgmni, msginfo.msgmax, msginfo.msgmnb); return; case STATUS: printf("------ Message%s --------\n", "s: Status"); printf("allocated queues = %d\n" "used headers = %d\n" "used space = %d bytes\n", msginfo.msgpool, msginfo.msgmap, msginfo.msgtql); return; case CREATOR: printf("------ Message%s --------\n", " Queues: Creators/Owners"); printf("%-10s %-10s %-10s %-10s %-10s %-10s\n", "msqid", "perms", "cuid", "cgid", "uid", "gid"); break; case TIME: printf("------ Message%s --------\n", " Queues Send/Recv/Change Times"); printf("%-8s %-10s %-20s %-20s %-20s\n", "msqid", "owner", "send", "recv", "change"); break; case PID: printf("------ Message%s --------\n", " Queues PIDs"); printf("%-10s %-10s %-10s %-10s\n", "msqid", "owner", "lspid", "lrpid"); break; default: printf("------ Message%s --------\n", " Queues"); printf("%-10s %-10s %-10s %-10s %-12s %-12s\n", "key", "msqid", "owner", "perms", "used-bytes", "messages"); break; } for (id = 0; id <= maxid; id++) { msqid = msgctl(id, MSG_STAT, &msgque); if (msqid < 0) continue; if (format == CREATOR) { print_perms(msqid, ipcp); continue; } pw = getpwuid(ipcp->uid); switch (format) { case TIME: if (pw) printf("%-8d %-10.10s", msqid, pw->pw_name); else printf("%-8d %-10d", msqid, ipcp->uid); printf(" %-20.16s", msgque.msg_stime ? ctime(&msgque.msg_stime) + 4 : "Not set"); printf(" %-20.16s", msgque.msg_rtime ? ctime(&msgque.msg_rtime) + 4 : "Not set"); printf(" %-20.16s\n", msgque.msg_ctime ? ctime(&msgque.msg_ctime) + 4 : "Not set"); break; case PID: if (pw) printf("%-8d %-10.10s", msqid, pw->pw_name); else printf("%-8d %-10d", msqid, ipcp->uid); printf(" %5d %5d\n", msgque.msg_lspid, msgque.msg_lrpid); break; default: printf("0x%08x ", ipcp->KEY); if (pw) printf("%-10d %-10.10s", msqid, pw->pw_name); else printf("%-10d %-10d", msqid, ipcp->uid); printf(" %-10o %-12ld %-12ld\n", ipcp->mode & 0777, /* * glibc-2.1.3 and earlier has unsigned short; * glibc-2.1.91 has variation between * unsigned short, unsigned long * Austin has msgqnum_t */ (long) msgque.msg_cbytes, (long) msgque.msg_qnum); break; } } } static void print_shm(int shmid) { struct shmid_ds shmds; struct ipc_perm *ipcp = &shmds.shm_perm; if (shmctl(shmid, IPC_STAT, &shmds) == -1) { bb_perror_msg("shmctl"); return; } printf("\nShared memory Segment shmid=%d\n" "uid=%d\tgid=%d\tcuid=%d\tcgid=%d\n" "mode=%#o\taccess_perms=%#o\n" "bytes=%ld\tlpid=%d\tcpid=%d\tnattch=%ld\n", shmid, ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid, ipcp->mode, ipcp->mode & 0777, (long) shmds.shm_segsz, shmds.shm_lpid, shmds.shm_cpid, (long) shmds.shm_nattch); printf("att_time=%-26.24s\n", shmds.shm_atime ? ctime(&shmds.shm_atime) : "Not set"); printf("det_time=%-26.24s\n", shmds.shm_dtime ? ctime(&shmds.shm_dtime) : "Not set"); printf("change_time=%-26.24s\n\n", ctime(&shmds.shm_ctime)); } static void print_msg(int msqid) { struct msqid_ds buf; struct ipc_perm *ipcp = &buf.msg_perm; if (msgctl(msqid, IPC_STAT, &buf) == -1) { bb_perror_msg("msgctl"); return; } printf("\nMessage Queue msqid=%d\n" "uid=%d\tgid=%d\tcuid=%d\tcgid=%d\tmode=%#o\n" "cbytes=%ld\tqbytes=%ld\tqnum=%ld\tlspid=%d\tlrpid=%d\n", msqid, ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid, ipcp->mode, /* * glibc-2.1.3 and earlier has unsigned short; * glibc-2.1.91 has variation between * unsigned short, unsigned long * Austin has msgqnum_t (for msg_qbytes) */ (long) buf.msg_cbytes, (long) buf.msg_qbytes, (long) buf.msg_qnum, buf.msg_lspid, buf.msg_lrpid); printf("send_time=%-26.24s\n", buf.msg_stime ? ctime(&buf.msg_stime) : "Not set"); printf("rcv_time=%-26.24s\n", buf.msg_rtime ? ctime(&buf.msg_rtime) : "Not set"); printf("change_time=%-26.24s\n\n", buf.msg_ctime ? ctime(&buf.msg_ctime) : "Not set"); } static void print_sem(int semid) { struct semid_ds semds; struct ipc_perm *ipcp = &semds.sem_perm; union semun arg; unsigned int i; arg.buf = &semds; if (semctl(semid, 0, IPC_STAT, arg)) { bb_perror_msg("semctl"); return; } printf("\nSemaphore Array semid=%d\n" "uid=%d\t gid=%d\t cuid=%d\t cgid=%d\n" "mode=%#o, access_perms=%#o\n" "nsems = %ld\n" "otime = %-26.24s\n", semid, ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid, ipcp->mode, ipcp->mode & 0777, (long) semds.sem_nsems, semds.sem_otime ? ctime(&semds.sem_otime) : "Not set"); printf("ctime = %-26.24s\n" "%-10s %-10s %-10s %-10s %-10s\n", ctime(&semds.sem_ctime), "semnum", "value", "ncount", "zcount", "pid"); arg.val = 0; for (i = 0; i < semds.sem_nsems; i++) { int val, ncnt, zcnt, pid; val = semctl(semid, i, GETVAL, arg); ncnt = semctl(semid, i, GETNCNT, arg); zcnt = semctl(semid, i, GETZCNT, arg); pid = semctl(semid, i, GETPID, arg); if (val < 0 || ncnt < 0 || zcnt < 0 || pid < 0) { bb_perror_msg_and_die("semctl"); } printf("%-10u %-10d %-10d %-10d %-10d\n", i, val, ncnt, zcnt, pid); } bb_putchar('\n'); } int ipcs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ipcs_main(int argc UNUSED_PARAM, char **argv) { int id = 0; unsigned flags = 0; unsigned opt; char *opt_i; #define flag_print (1<<0) #define flag_msg (1<<1) #define flag_sem (1<<2) #define flag_shm (1<<3) opt = getopt32(argv, "i:aqsmtcplu", &opt_i); if (opt & 0x1) { // -i id = xatoi(opt_i); flags |= flag_print; } if (opt & 0x2) flags |= flag_msg | flag_sem | flag_shm; // -a if (opt & 0x4) flags |= flag_msg; // -q if (opt & 0x8) flags |= flag_sem; // -s if (opt & 0x10) flags |= flag_shm; // -m if (opt & 0x20) format = TIME; // -t if (opt & 0x40) format = CREATOR; // -c if (opt & 0x80) format = PID; // -p if (opt & 0x100) format = LIMITS; // -l if (opt & 0x200) format = STATUS; // -u if (flags & flag_print) { if (flags & flag_shm) { print_shm(id); fflush_stdout_and_exit(EXIT_SUCCESS); } if (flags & flag_sem) { print_sem(id); fflush_stdout_and_exit(EXIT_SUCCESS); } if (flags & flag_msg) { print_msg(id); fflush_stdout_and_exit(EXIT_SUCCESS); } bb_show_usage(); } if (!(flags & (flag_shm | flag_msg | flag_sem))) flags |= flag_msg | flag_shm | flag_sem; bb_putchar('\n'); if (flags & flag_shm) { do_shm(); bb_putchar('\n'); } if (flags & flag_sem) { do_sem(); bb_putchar('\n'); } if (flags & flag_msg) { do_msg(); bb_putchar('\n'); } fflush_stdout_and_exit(EXIT_SUCCESS); } �������������������������������busybox-1.22.1/util-linux/volume_id/����������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365360�016063� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/squashfs.c������������������������������������������������������0000644�0000000�0000000�00000003223�12263563520�020070� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2012 S-G Bergh <sgb@systemasis.org> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_SQUASHFS) += squashfs.o //config: //config:config FEATURE_VOLUMEID_SQUASHFS //config: bool "SquashFS filesystem" //config: default y //config: depends on VOLUMEID && FEATURE_BLKID_TYPE //config: help //config: Squashfs is a compressed read-only filesystem for Linux. Squashfs is //config: intended for general read-only filesystem use and in constrained block //config: device/memory systems (e.g. embedded systems) where low overhead is //config: needed. //config: #include "volume_id_internal.h" struct squashfs_superblock { uint32_t magic; /* uint32_t dummy[6]; uint16_t major; uint16_t minor; */ } PACKED; int FAST_FUNC volume_id_probe_squashfs(struct volume_id *id /*,uint64_t off*/) { #define off ((uint64_t)0) struct squashfs_superblock *sb; dbg("SquashFS: probing at offset 0x%llx", (unsigned long long) off); sb = volume_id_get_buffer(id, off, 0x200); if (!sb) return -1; // Old SquashFS (pre 4.0) can be both big and little endian, so test for both. // Likewise, it is commonly used in firwmare with some non-standard signatures. #define pack(a,b,c,d) ( (uint32_t)((a * 256 + b) * 256 + c) * 256 + d ) #define SIG1 pack('s','q','s','h') #define SIG2 pack('h','s','q','s') #define SIG3 pack('s','h','s','q') #define SIG4 pack('q','s','h','s') if (sb->magic == SIG1 || sb->magic == SIG2 || sb->magic == SIG3 || sb->magic == SIG4 ) { IF_FEATURE_BLKID_TYPE(id->type = "squashfs";) return 0; } return -1; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/volume_id.c�����������������������������������������������������0000644�0000000�0000000�00000014762�12263563520�020230� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_VOLUMEID) += volume_id.o util.o #include "volume_id_internal.h" /* Some detection routines do not set label or uuid anyway, * so they are disabled. */ /* Looks for partitions, we don't use it: */ #define ENABLE_FEATURE_VOLUMEID_MAC 0 /* #define ENABLE_FEATURE_VOLUMEID_MSDOS 0 - NB: this one * was not properly added to probe table anyway - ??! */ /* None of RAIDs have label or uuid, except LinuxRAID: */ #define ENABLE_FEATURE_VOLUMEID_HIGHPOINTRAID 0 #define ENABLE_FEATURE_VOLUMEID_ISWRAID 0 #define ENABLE_FEATURE_VOLUMEID_LSIRAID 0 #define ENABLE_FEATURE_VOLUMEID_LVM 0 #define ENABLE_FEATURE_VOLUMEID_NVIDIARAID 0 #define ENABLE_FEATURE_VOLUMEID_PROMISERAID 0 #define ENABLE_FEATURE_VOLUMEID_SILICONRAID 0 #define ENABLE_FEATURE_VOLUMEID_VIARAID 0 /* These filesystems also have no label or uuid: */ #define ENABLE_FEATURE_VOLUMEID_MINIX 0 #define ENABLE_FEATURE_VOLUMEID_HPFS 0 #define ENABLE_FEATURE_VOLUMEID_UFS 0 typedef int FAST_FUNC (*raid_probe_fptr)(struct volume_id *id, /*uint64_t off,*/ uint64_t size); typedef int FAST_FUNC (*probe_fptr)(struct volume_id *id /*, uint64_t off*/); static const raid_probe_fptr raid1[] = { #if ENABLE_FEATURE_VOLUMEID_LINUXRAID volume_id_probe_linux_raid, #endif #if ENABLE_FEATURE_VOLUMEID_ISWRAID volume_id_probe_intel_software_raid, #endif #if ENABLE_FEATURE_VOLUMEID_LSIRAID volume_id_probe_lsi_mega_raid, #endif #if ENABLE_FEATURE_VOLUMEID_VIARAID volume_id_probe_via_raid, #endif #if ENABLE_FEATURE_VOLUMEID_SILICONRAID volume_id_probe_silicon_medley_raid, #endif #if ENABLE_FEATURE_VOLUMEID_NVIDIARAID volume_id_probe_nvidia_raid, #endif #if ENABLE_FEATURE_VOLUMEID_PROMISERAID volume_id_probe_promise_fasttrack_raid, #endif #if ENABLE_FEATURE_VOLUMEID_HIGHPOINTRAID volume_id_probe_highpoint_45x_raid, #endif }; static const probe_fptr raid2[] = { #if ENABLE_FEATURE_VOLUMEID_LVM volume_id_probe_lvm1, volume_id_probe_lvm2, #endif #if ENABLE_FEATURE_VOLUMEID_HIGHPOINTRAID volume_id_probe_highpoint_37x_raid, #endif #if ENABLE_FEATURE_VOLUMEID_LUKS volume_id_probe_luks, #endif }; /* signature in the first block, only small buffer needed */ static const probe_fptr fs1[] = { #if ENABLE_FEATURE_VOLUMEID_FAT volume_id_probe_vfat, #endif #if ENABLE_FEATURE_VOLUMEID_EXFAT volume_id_probe_exfat, #endif #if ENABLE_FEATURE_VOLUMEID_MAC volume_id_probe_mac_partition_map, #endif #if ENABLE_FEATURE_VOLUMEID_SQUASHFS volume_id_probe_squashfs, #endif #if ENABLE_FEATURE_VOLUMEID_XFS volume_id_probe_xfs, #endif }; /* fill buffer with maximum */ static const probe_fptr fs2[] = { #if ENABLE_FEATURE_VOLUMEID_LINUXSWAP volume_id_probe_linux_swap, #endif #if ENABLE_FEATURE_VOLUMEID_EXT volume_id_probe_ext, #endif #if ENABLE_FEATURE_VOLUMEID_BTRFS volume_id_probe_btrfs, #endif #if ENABLE_FEATURE_VOLUMEID_REISERFS volume_id_probe_reiserfs, #endif #if ENABLE_FEATURE_VOLUMEID_JFS volume_id_probe_jfs, #endif #if ENABLE_FEATURE_VOLUMEID_UDF volume_id_probe_udf, #endif #if ENABLE_FEATURE_VOLUMEID_ISO9660 volume_id_probe_iso9660, #endif #if ENABLE_FEATURE_VOLUMEID_HFS volume_id_probe_hfs_hfsplus, #endif #if ENABLE_FEATURE_VOLUMEID_UFS volume_id_probe_ufs, #endif #if ENABLE_FEATURE_VOLUMEID_F2FS volume_id_probe_f2fs, #endif #if ENABLE_FEATURE_VOLUMEID_NILFS volume_id_probe_nilfs, #endif #if ENABLE_FEATURE_VOLUMEID_NTFS volume_id_probe_ntfs, #endif #if ENABLE_FEATURE_VOLUMEID_CRAMFS volume_id_probe_cramfs, #endif #if ENABLE_FEATURE_VOLUMEID_ROMFS volume_id_probe_romfs, #endif #if ENABLE_FEATURE_VOLUMEID_HPFS volume_id_probe_hpfs, #endif #if ENABLE_FEATURE_VOLUMEID_SYSV volume_id_probe_sysv, #endif #if ENABLE_FEATURE_VOLUMEID_MINIX volume_id_probe_minix, #endif #if ENABLE_FEATURE_VOLUMEID_OCFS2 volume_id_probe_ocfs2, #endif }; int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64_t size) { unsigned i; /* probe for raid first, cause fs probes may be successful on raid members */ if (size) { for (i = 0; i < ARRAY_SIZE(raid1); i++) { if (raid1[i](id, /*off,*/ size) == 0) goto ret; if (id->error) goto ret; } } for (i = 0; i < ARRAY_SIZE(raid2); i++) { if (raid2[i](id /*,off*/) == 0) goto ret; if (id->error) goto ret; } /* signature in the first block, only small buffer needed */ for (i = 0; i < ARRAY_SIZE(fs1); i++) { if (fs1[i](id /*,off*/) == 0) goto ret; if (id->error) goto ret; } /* fill buffer with maximum */ volume_id_get_buffer(id, 0, SB_BUFFER_SIZE); for (i = 0; i < ARRAY_SIZE(fs2); i++) { if (fs2[i](id /*,off*/) == 0) goto ret; if (id->error) goto ret; } ret: volume_id_free_buffer(id); return (- id->error); /* 0 or -1 */ } /* open volume by device node */ struct volume_id* FAST_FUNC volume_id_open_node(int fd) { struct volume_id *id; id = xzalloc(sizeof(struct volume_id)); id->fd = fd; ///* close fd on device close */ //id->fd_close = 1; return id; } #ifdef UNUSED /* open volume by major/minor */ struct volume_id* FAST_FUNC volume_id_open_dev_t(dev_t devt) { struct volume_id *id; char *tmp_node[VOLUME_ID_PATH_MAX]; tmp_node = xasprintf("/dev/.volume_id-%u-%u-%u", (unsigned)getpid(), (unsigned)major(devt), (unsigned)minor(devt)); /* create temporary node to open block device */ unlink(tmp_node); if (mknod(tmp_node, (S_IFBLK | 0600), devt) != 0) bb_perror_msg_and_die("can't mknod(%s)", tmp_node); id = volume_id_open_node(tmp_node); unlink(tmp_node); free(tmp_node); return id; } #endif void FAST_FUNC free_volume_id(struct volume_id *id) { if (id == NULL) return; //if (id->fd_close != 0) - always true close(id->fd); volume_id_free_buffer(id); #ifdef UNUSED_PARTITION_CODE free(id->partitions); #endif free(id); } ��������������busybox-1.22.1/util-linux/volume_id/hfs.c�����������������������������������������������������������0000644�0000000�0000000�00000020503�12263563520�017013� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_HFS) += hfs.o //config: //config:config FEATURE_VOLUMEID_HFS //config: bool "hfs filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" struct hfs_finder_info{ uint32_t boot_folder; uint32_t start_app; uint32_t open_folder; uint32_t os9_folder; uint32_t reserved; uint32_t osx_folder; uint8_t id[8]; } PACKED; struct hfs_mdb { uint8_t signature[2]; uint32_t cr_date; uint32_t ls_Mod; uint16_t atrb; uint16_t nm_fls; uint16_t vbm_st; uint16_t alloc_ptr; uint16_t nm_al_blks; uint32_t al_blk_size; uint32_t clp_size; uint16_t al_bl_st; uint32_t nxt_cnid; uint16_t free_bks; uint8_t label_len; uint8_t label[27]; uint32_t vol_bkup; uint16_t vol_seq_num; uint32_t wr_cnt; uint32_t xt_clump_size; uint32_t ct_clump_size; uint16_t num_root_dirs; uint32_t file_count; uint32_t dir_count; struct hfs_finder_info finder_info; uint8_t embed_sig[2]; uint16_t embed_startblock; uint16_t embed_blockcount; } PACKED; struct hfsplus_bnode_descriptor { uint32_t next; uint32_t prev; uint8_t type; uint8_t height; uint16_t num_recs; uint16_t reserved; } PACKED; struct hfsplus_bheader_record { uint16_t depth; uint32_t root; uint32_t leaf_count; uint32_t leaf_head; uint32_t leaf_tail; uint16_t node_size; } PACKED; struct hfsplus_catalog_key { uint16_t key_len; uint32_t parent_id; uint16_t unicode_len; uint8_t unicode[255 * 2]; } PACKED; struct hfsplus_extent { uint32_t start_block; uint32_t block_count; } PACKED; #define HFSPLUS_EXTENT_COUNT 8 struct hfsplus_fork { uint64_t total_size; uint32_t clump_size; uint32_t total_blocks; struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT]; } PACKED; struct hfsplus_vol_header { uint8_t signature[2]; uint16_t version; uint32_t attributes; uint32_t last_mount_vers; uint32_t reserved; uint32_t create_date; uint32_t modify_date; uint32_t backup_date; uint32_t checked_date; uint32_t file_count; uint32_t folder_count; uint32_t blocksize; uint32_t total_blocks; uint32_t free_blocks; uint32_t next_alloc; uint32_t rsrc_clump_sz; uint32_t data_clump_sz; uint32_t next_cnid; uint32_t write_count; uint64_t encodings_bmp; struct hfs_finder_info finder_info; struct hfsplus_fork alloc_file; struct hfsplus_fork ext_file; struct hfsplus_fork cat_file; struct hfsplus_fork attr_file; struct hfsplus_fork start_file; } PACKED; #define HFS_SUPERBLOCK_OFFSET 0x400 #define HFS_NODE_LEAF 0xff #define HFSPLUS_POR_CNID 1 static void FAST_FUNC hfs_set_uuid(struct volume_id *id, const uint8_t *hfs_id) { #define hfs_id_len 8 md5_ctx_t md5c; uint8_t uuid[16]; unsigned i; for (i = 0; i < hfs_id_len; i++) if (hfs_id[i] != 0) goto do_md5; return; do_md5: md5_begin(&md5c); md5_hash(&md5c, "\263\342\17\71\362\222\21\326\227\244\0\60\145\103\354\254", 16); md5_hash(&md5c, hfs_id, hfs_id_len); md5_end(&md5c, uuid); uuid[6] = 0x30 | (uuid[6] & 0x0f); uuid[8] = 0x80 | (uuid[8] & 0x3f); volume_id_set_uuid(id, uuid, UUID_DCE); } int FAST_FUNC volume_id_probe_hfs_hfsplus(struct volume_id *id /*,uint64_t off*/) { uint64_t off = 0; unsigned blocksize; unsigned cat_block; unsigned ext_block_start; unsigned ext_block_count; int ext; unsigned leaf_node_head; unsigned leaf_node_count; unsigned leaf_node_size; unsigned leaf_block; uint64_t leaf_off; unsigned alloc_block_size; unsigned alloc_first_block; unsigned embed_first_block; unsigned record_count; struct hfsplus_vol_header *hfsplus; struct hfsplus_bnode_descriptor *descr; struct hfsplus_bheader_record *bnode; struct hfsplus_catalog_key *key; unsigned label_len; struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT]; struct hfs_mdb *hfs; const uint8_t *buf; dbg("probing at offset 0x%llx", (unsigned long long) off); buf = volume_id_get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200); if (buf == NULL) return -1; hfs = (struct hfs_mdb *) buf; if (hfs->signature[0] != 'B' || hfs->signature[1] != 'D') goto checkplus; /* it may be just a hfs wrapper for hfs+ */ if (hfs->embed_sig[0] == 'H' && hfs->embed_sig[1] == '+') { alloc_block_size = be32_to_cpu(hfs->al_blk_size); dbg("alloc_block_size 0x%x", alloc_block_size); alloc_first_block = be16_to_cpu(hfs->al_bl_st); dbg("alloc_first_block 0x%x", alloc_first_block); embed_first_block = be16_to_cpu(hfs->embed_startblock); dbg("embed_first_block 0x%x", embed_first_block); off += (alloc_first_block * 512) + (embed_first_block * alloc_block_size); dbg("hfs wrapped hfs+ found at offset 0x%llx", (unsigned long long) off); buf = volume_id_get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200); if (buf == NULL) return -1; goto checkplus; } if (hfs->label_len > 0 && hfs->label_len < 28) { // volume_id_set_label_raw(id, hfs->label, hfs->label_len); volume_id_set_label_string(id, hfs->label, hfs->label_len) ; } hfs_set_uuid(id, hfs->finder_info.id); // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); IF_FEATURE_BLKID_TYPE(id->type = "hfs";) return 0; checkplus: hfsplus = (struct hfsplus_vol_header *) buf; if (hfs->signature[0] == 'H') if (hfs->signature[1] == '+' || hfs->signature[1] == 'X') goto hfsplus; return -1; hfsplus: hfs_set_uuid(id, hfsplus->finder_info.id); blocksize = be32_to_cpu(hfsplus->blocksize); dbg("blocksize %u", blocksize); memcpy(extents, hfsplus->cat_file.extents, sizeof(extents)); cat_block = be32_to_cpu(extents[0].start_block); dbg("catalog start block 0x%x", cat_block); buf = volume_id_get_buffer(id, off + (cat_block * blocksize), 0x2000); if (buf == NULL) goto found; bnode = (struct hfsplus_bheader_record *) &buf[sizeof(struct hfsplus_bnode_descriptor)]; leaf_node_head = be32_to_cpu(bnode->leaf_head); dbg("catalog leaf node 0x%x", leaf_node_head); leaf_node_size = be16_to_cpu(bnode->node_size); dbg("leaf node size 0x%x", leaf_node_size); leaf_node_count = be32_to_cpu(bnode->leaf_count); dbg("leaf node count 0x%x", leaf_node_count); if (leaf_node_count == 0) goto found; leaf_block = (leaf_node_head * leaf_node_size) / blocksize; /* get physical location */ for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) { ext_block_start = be32_to_cpu(extents[ext].start_block); ext_block_count = be32_to_cpu(extents[ext].block_count); dbg("extent start block 0x%x, count 0x%x", ext_block_start, ext_block_count); if (ext_block_count == 0) goto found; /* this is our extent */ if (leaf_block < ext_block_count) break; leaf_block -= ext_block_count; } if (ext == HFSPLUS_EXTENT_COUNT) goto found; dbg("found block in extent %i", ext); leaf_off = (ext_block_start + leaf_block) * blocksize; buf = volume_id_get_buffer(id, off + leaf_off, leaf_node_size); if (buf == NULL) goto found; descr = (struct hfsplus_bnode_descriptor *) buf; dbg("descriptor type 0x%x", descr->type); record_count = be16_to_cpu(descr->num_recs); dbg("number of records %u", record_count); if (record_count == 0) goto found; if (descr->type != HFS_NODE_LEAF) goto found; key = (struct hfsplus_catalog_key *) &buf[sizeof(struct hfsplus_bnode_descriptor)]; dbg("parent id 0x%x", be32_to_cpu(key->parent_id)); if (key->parent_id != cpu_to_be32(HFSPLUS_POR_CNID)) goto found; label_len = be16_to_cpu(key->unicode_len) * 2; dbg("label unicode16 len %i", label_len); // volume_id_set_label_raw(id, key->unicode, label_len); volume_id_set_label_unicode16(id, key->unicode, BE, label_len); found: // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); IF_FEATURE_BLKID_TYPE(id->type = "hfsplus";) return 0; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/ntfs.c����������������������������������������������������������0000644�0000000�0000000�00000013003�12263563520�017202� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_NTFS) += ntfs.o //config: //config:config FEATURE_VOLUMEID_NTFS //config: bool "ntfs filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" struct ntfs_super_block { uint8_t jump[3]; uint8_t oem_id[8]; uint16_t bytes_per_sector; uint8_t sectors_per_cluster; uint16_t reserved_sectors; uint8_t fats; uint16_t root_entries; uint16_t sectors; uint8_t media_type; uint16_t sectors_per_fat; uint16_t sectors_per_track; uint16_t heads; uint32_t hidden_sectors; uint32_t large_sectors; uint16_t unused[2]; uint64_t number_of_sectors; uint64_t mft_cluster_location; uint64_t mft_mirror_cluster_location; int8_t cluster_per_mft_record; uint8_t reserved1[3]; int8_t cluster_per_index_record; uint8_t reserved2[3]; uint8_t volume_serial[8]; uint16_t checksum; } PACKED; struct master_file_table_record { uint8_t magic[4]; uint16_t usa_ofs; uint16_t usa_count; uint64_t lsn; uint16_t sequence_number; uint16_t link_count; uint16_t attrs_offset; uint16_t flags; uint32_t bytes_in_use; uint32_t bytes_allocated; } PACKED; struct file_attribute { uint32_t type; uint32_t len; uint8_t non_resident; uint8_t name_len; uint16_t name_offset; uint16_t flags; uint16_t instance; uint32_t value_len; uint16_t value_offset; } PACKED; struct volume_info { uint64_t reserved; uint8_t major_ver; uint8_t minor_ver; } PACKED; #define MFT_RECORD_VOLUME 3 #define MFT_RECORD_ATTR_VOLUME_NAME 0x60 #define MFT_RECORD_ATTR_VOLUME_INFO 0x70 #define MFT_RECORD_ATTR_OBJECT_ID 0x40 #define MFT_RECORD_ATTR_END 0xffffffffu int FAST_FUNC volume_id_probe_ntfs(struct volume_id *id /*,uint64_t off*/) { #define off ((uint64_t)0) unsigned sector_size; unsigned cluster_size; uint64_t mft_cluster; uint64_t mft_off; unsigned mft_record_size; unsigned attr_type; unsigned attr_off; unsigned attr_len; unsigned val_off; unsigned val_len; struct master_file_table_record *mftr; struct ntfs_super_block *ns; const uint8_t *buf; const uint8_t *val; dbg("probing at offset 0x%llx", (unsigned long long) off); ns = volume_id_get_buffer(id, off, 0x200); if (ns == NULL) return -1; if (memcmp(ns->oem_id, "NTFS", 4) != 0) return -1; volume_id_set_uuid(id, ns->volume_serial, UUID_NTFS); sector_size = le16_to_cpu(ns->bytes_per_sector); cluster_size = ns->sectors_per_cluster * sector_size; mft_cluster = le64_to_cpu(ns->mft_cluster_location); mft_off = mft_cluster * cluster_size; if (ns->cluster_per_mft_record < 0) /* size = -log2(mft_record_size); normally 1024 Bytes */ mft_record_size = 1 << -ns->cluster_per_mft_record; else mft_record_size = ns->cluster_per_mft_record * cluster_size; dbg("sectorsize 0x%x", sector_size); dbg("clustersize 0x%x", cluster_size); dbg("mftcluster %llu", (unsigned long long) mft_cluster); dbg("mftoffset 0x%llx", (unsigned long long) mft_off); dbg("cluster per mft_record %i", ns->cluster_per_mft_record); dbg("mft record size %i", mft_record_size); buf = volume_id_get_buffer(id, off + mft_off + (MFT_RECORD_VOLUME * mft_record_size), mft_record_size); if (buf == NULL) goto found; mftr = (struct master_file_table_record*) buf; dbg("mftr->magic '%c%c%c%c'", mftr->magic[0], mftr->magic[1], mftr->magic[2], mftr->magic[3]); if (memcmp(mftr->magic, "FILE", 4) != 0) goto found; attr_off = le16_to_cpu(mftr->attrs_offset); dbg("file $Volume's attributes are at offset %i", attr_off); while (1) { struct file_attribute *attr; attr = (struct file_attribute*) &buf[attr_off]; attr_type = le32_to_cpu(attr->type); attr_len = le32_to_cpu(attr->len); val_off = le16_to_cpu(attr->value_offset); val_len = le32_to_cpu(attr->value_len); attr_off += attr_len; if (attr_len == 0) break; if (attr_off >= mft_record_size) break; if (attr_type == MFT_RECORD_ATTR_END) break; dbg("found attribute type 0x%x, len %i, at offset %i", attr_type, attr_len, attr_off); // if (attr_type == MFT_RECORD_ATTR_VOLUME_INFO) { // struct volume_info *info; // dbg("found info, len %i", val_len); // info = (struct volume_info*) (((uint8_t *) attr) + val_off); // snprintf(id->type_version, sizeof(id->type_version)-1, // "%u.%u", info->major_ver, info->minor_ver); // } if (attr_type == MFT_RECORD_ATTR_VOLUME_NAME) { dbg("found label, len %i", val_len); if (val_len > VOLUME_ID_LABEL_SIZE) val_len = VOLUME_ID_LABEL_SIZE; val = ((uint8_t *) attr) + val_off; // volume_id_set_label_raw(id, val, val_len); volume_id_set_label_unicode16(id, val, LE, val_len); } } found: // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); IF_FEATURE_BLKID_TYPE(id->type = "ntfs";) return 0; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/get_devname.c���������������������������������������������������0000644�0000000�0000000�00000016236�12263563520�020521� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Support functions for mounting devices by label/uuid * * Copyright (C) 2006 by Jason Schoon <floydpink@gmail.com> * Some portions cribbed from e2fsprogs, util-linux, dosfstools * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //kbuild:lib-$(CONFIG_BLKID) += get_devname.o //kbuild:lib-$(CONFIG_FINDFS) += get_devname.o //kbuild:lib-$(CONFIG_FEATURE_MOUNT_LABEL) += get_devname.o #include <sys/mount.h> /* BLKGETSIZE64 */ #if !defined(BLKGETSIZE64) # define BLKGETSIZE64 _IOR(0x12,114,size_t) #endif #include "volume_id_internal.h" static struct uuidCache_s { struct uuidCache_s *next; // int major, minor; char *device; char *label; char *uc_uuid; /* prefix makes it easier to grep for */ IF_FEATURE_BLKID_TYPE(const char *type;) } *uuidCache; #if !ENABLE_FEATURE_BLKID_TYPE #define get_label_uuid(fd, label, uuid, type) \ get_label_uuid(fd, label, uuid) #define uuidcache_addentry(device, label, uuid, type) \ uuidcache_addentry(device, label, uuid) #endif /* Returns !0 on error. * Otherwise, returns malloc'ed strings for label and uuid * (and they can't be NULL, although they can be ""). * NB: closes fd. */ static int get_label_uuid(int fd, char **label, char **uuid, const char **type) { int rv = 1; uint64_t size; struct volume_id *vid; /* fd is owned by vid now */ vid = volume_id_open_node(fd); if (ioctl(/*vid->*/fd, BLKGETSIZE64, &size) != 0) size = 0; if (volume_id_probe_all(vid, /*0,*/ size) != 0) goto ret; if (vid->label[0] != '\0' || vid->uuid[0] != '\0' #if ENABLE_FEATURE_BLKID_TYPE || vid->type != NULL #endif ) { *label = xstrndup(vid->label, sizeof(vid->label)); *uuid = xstrndup(vid->uuid, sizeof(vid->uuid)); #if ENABLE_FEATURE_BLKID_TYPE *type = vid->type; dbg("found label '%s', uuid '%s', type '%s'", *label, *uuid, *type); #else dbg("found label '%s', uuid '%s'", *label, *uuid); #endif rv = 0; } ret: free_volume_id(vid); /* also closes fd */ return rv; } /* NB: we take ownership of (malloc'ed) label and uuid */ static void uuidcache_addentry(char *device, /*int major, int minor,*/ char *label, char *uuid, const char *type) { struct uuidCache_s *last; if (!uuidCache) { last = uuidCache = xzalloc(sizeof(*uuidCache)); } else { for (last = uuidCache; last->next; last = last->next) continue; last->next = xzalloc(sizeof(*uuidCache)); last = last->next; } /*last->next = NULL; - xzalloc did it*/ // last->major = major; // last->minor = minor; last->device = device; last->label = label; last->uc_uuid = uuid; IF_FEATURE_BLKID_TYPE(last->type = type;) } /* If get_label_uuid() on device_name returns success, * add a cache entry for this device. * If device node does not exist, it will be temporarily created. */ static int FAST_FUNC uuidcache_check_device(const char *device, struct stat *statbuf, void *userData UNUSED_PARAM, int depth UNUSED_PARAM) { /* note: this check rejects links to devices, among other nodes */ if (!S_ISBLK(statbuf->st_mode)) return TRUE; /* Users report that mucking with floppies (especially non-present * ones) is significant PITA. This is a horribly dirty hack, * but it is very useful in real world. * If this will ever need to be enabled, consider using O_NONBLOCK. */ if (major(statbuf->st_rdev) == 2) return TRUE; add_to_uuid_cache(device); return TRUE; } static struct uuidCache_s* uuidcache_init(int scan_devices) { dbg("DBG: uuidCache=%x, uuidCache"); if (uuidCache) return uuidCache; /* We were scanning /proc/partitions * and /proc/sys/dev/cdrom/info here. * Missed volume managers. I see that "standard" blkid uses these: * /dev/mapper/control * /proc/devices * /proc/evms/volumes * /proc/lvm/VGs * This is unacceptably complex. Let's just scan /dev. * (Maybe add scanning of /sys/block/XXX/dev for devices * somehow not having their /dev/XXX entries created?) */ if (scan_devices) recursive_action("/dev", ACTION_RECURSE, uuidcache_check_device, /* file_action */ NULL, /* dir_action */ NULL, /* userData */ 0 /* depth */); return uuidCache; } #define UUID 1 #define VOL 2 #ifdef UNUSED static char * get_spec_by_x(int n, const char *t, int *majorPtr, int *minorPtr) { struct uuidCache_s *uc; uc = uuidcache_init(/*scan_devices:*/ 1); while (uc) { switch (n) { case UUID: if (strcmp(t, uc->uc_uuid) == 0) { *majorPtr = uc->major; *minorPtr = uc->minor; return uc->device; } break; case VOL: if (strcmp(t, uc->label) == 0) { *majorPtr = uc->major; *minorPtr = uc->minor; return uc->device; } break; } uc = uc->next; } return NULL; } static unsigned char fromhex(char c) { if (isdigit(c)) return (c - '0'); return ((c|0x20) - 'a' + 10); } static char * get_spec_by_uuid(const char *s, int *major, int *minor) { unsigned char uuid[16]; int i; if (strlen(s) != 36 || s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' ) { goto bad_uuid; } for (i = 0; i < 16; i++) { if (*s == '-') s++; if (!isxdigit(s[0]) || !isxdigit(s[1])) goto bad_uuid; uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1])); s += 2; } return get_spec_by_x(UUID, (char *)uuid, major, minor); bad_uuid: fprintf(stderr, _("mount: bad UUID")); return 0; } static char * get_spec_by_volume_label(const char *s, int *major, int *minor) { return get_spec_by_x(VOL, s, major, minor); } #endif // UNUSED /* Used by blkid */ void display_uuid_cache(int scan_devices) { struct uuidCache_s *uc; uc = uuidcache_init(scan_devices); while (uc) { printf("%s:", uc->device); if (uc->label[0]) printf(" LABEL=\"%s\"", uc->label); if (uc->uc_uuid[0]) printf(" UUID=\"%s\"", uc->uc_uuid); #if ENABLE_FEATURE_BLKID_TYPE if (uc->type) printf(" TYPE=\"%s\"", uc->type); #endif bb_putchar('\n'); uc = uc->next; } } int add_to_uuid_cache(const char *device) { char *uuid = uuid; /* for compiler */ char *label = label; #if ENABLE_FEATURE_BLKID_TYPE const char *type = type; #endif int fd; fd = open(device, O_RDONLY); if (fd < 0) return 0; /* get_label_uuid() closes fd in all cases (success & failure) */ if (get_label_uuid(fd, &label, &uuid, &type) == 0) { /* uuidcache_addentry() takes ownership of all four params */ uuidcache_addentry(xstrdup(device), /*ma, mi,*/ label, uuid, type); return 1; } return 0; } /* Used by mount and findfs */ char *get_devname_from_label(const char *spec) { struct uuidCache_s *uc; uc = uuidcache_init(/*scan_devices:*/ 1); while (uc) { if (uc->label[0] && strcmp(spec, uc->label) == 0) { return xstrdup(uc->device); } uc = uc->next; } return NULL; } char *get_devname_from_uuid(const char *spec) { struct uuidCache_s *uc; uc = uuidcache_init(/*scan_devices:*/ 1); while (uc) { /* case of hex numbers doesn't matter */ if (strcasecmp(spec, uc->uc_uuid) == 0) { return xstrdup(uc->device); } uc = uc->next; } return NULL; } int resolve_mount_spec(char **fsname) { char *tmp = *fsname; if (strncmp(*fsname, "UUID=", 5) == 0) tmp = get_devname_from_uuid(*fsname + 5); else if (strncmp(*fsname, "LABEL=", 6) == 0) tmp = get_devname_from_label(*fsname + 6); if (tmp == *fsname) return 0; /* no UUID= or LABEL= prefix found */ if (tmp) *fsname = tmp; return 1; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/fat.c�����������������������������������������������������������0000644�0000000�0000000�00000022412�12263563520�017006� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_FAT) += fat.o //config: //config:config FEATURE_VOLUMEID_FAT //config: bool "fat filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" /* linux/msdos_fs.h says: */ #define FAT12_MAX 0xff4 #define FAT16_MAX 0xfff4 #define FAT32_MAX 0x0ffffff6 #define FAT_ATTR_VOLUME_ID 0x08 #define FAT_ATTR_DIR 0x10 #define FAT_ATTR_LONG_NAME 0x0f #define FAT_ATTR_MASK 0x3f #define FAT_ENTRY_FREE 0xe5 struct vfat_super_block { uint8_t boot_jump[3]; uint8_t sysid[8]; uint16_t sector_size_bytes; uint8_t sectors_per_cluster; uint16_t reserved_sct; uint8_t fats; uint16_t dir_entries; uint16_t sectors; uint8_t media; uint16_t fat_length; uint16_t secs_track; uint16_t heads; uint32_t hidden; uint32_t total_sect; union { struct fat_super_block { uint8_t unknown[3]; uint8_t serno[4]; uint8_t label[11]; uint8_t magic[8]; uint8_t dummy2[192]; uint8_t pmagic[2]; } PACKED fat; struct fat32_super_block { uint32_t fat32_length; uint16_t flags; uint8_t version[2]; uint32_t root_cluster; uint16_t insfo_sector; uint16_t backup_boot; uint16_t reserved2[6]; uint8_t unknown[3]; uint8_t serno[4]; uint8_t label[11]; uint8_t magic[8]; uint8_t dummy2[164]; uint8_t pmagic[2]; } PACKED fat32; } PACKED type; } PACKED; struct vfat_dir_entry { uint8_t name[11]; uint8_t attr; uint16_t time_creat; uint16_t date_creat; uint16_t time_acc; uint16_t date_acc; uint16_t cluster_high; uint16_t time_write; uint16_t date_write; uint16_t cluster_low; uint32_t size; } PACKED; static uint8_t *get_attr_volume_id(struct vfat_dir_entry *dir, int count) { for (;--count >= 0; dir++) { /* end marker */ if (dir->name[0] == 0x00) { dbg("end of dir"); break; } /* empty entry */ if (dir->name[0] == FAT_ENTRY_FREE) continue; /* long name */ if ((dir->attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME) continue; if ((dir->attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) == FAT_ATTR_VOLUME_ID) { /* labels do not have file data */ if (dir->cluster_high != 0 || dir->cluster_low != 0) continue; dbg("found ATTR_VOLUME_ID id in root dir"); return dir->name; } dbg("skip dir entry"); } return NULL; } int FAST_FUNC volume_id_probe_vfat(struct volume_id *id /*,uint64_t fat_partition_off*/) { #define fat_partition_off ((uint64_t)0) struct vfat_super_block *vs; struct vfat_dir_entry *dir; uint16_t sector_size_bytes; uint16_t dir_entries; uint32_t sect_count; uint16_t reserved_sct; uint32_t fat_size_sct; uint32_t root_cluster; uint32_t dir_size_sct; uint32_t cluster_count; uint64_t root_start_off; uint32_t start_data_sct; uint8_t *buf; uint32_t buf_size; uint8_t *label = NULL; uint32_t next_cluster; int maxloop; dbg("probing at offset 0x%llx", (unsigned long long) fat_partition_off); vs = volume_id_get_buffer(id, fat_partition_off, 0x200); if (vs == NULL) return -1; /* believe only that's fat, don't trust the version * the cluster_count will tell us */ if (memcmp(vs->sysid, "NTFS", 4) == 0) return -1; if (memcmp(vs->type.fat32.magic, "MSWIN", 5) == 0) goto valid; if (memcmp(vs->type.fat32.magic, "FAT32 ", 8) == 0) goto valid; if (memcmp(vs->type.fat.magic, "FAT16 ", 8) == 0) goto valid; if (memcmp(vs->type.fat.magic, "MSDOS", 5) == 0) goto valid; if (memcmp(vs->type.fat.magic, "FAT12 ", 8) == 0) goto valid; /* * There are old floppies out there without a magic, so we check * for well known values and guess if it's a fat volume */ /* boot jump address check */ if ((vs->boot_jump[0] != 0xeb || vs->boot_jump[2] != 0x90) && vs->boot_jump[0] != 0xe9 ) { return -1; } /* heads check */ if (vs->heads == 0) return -1; /* cluster size check */ if (vs->sectors_per_cluster == 0 || (vs->sectors_per_cluster & (vs->sectors_per_cluster-1)) ) { return -1; } /* media check */ if (vs->media < 0xf8 && vs->media != 0xf0) return -1; /* fat count*/ if (vs->fats != 2) return -1; valid: /* sector size check */ sector_size_bytes = le16_to_cpu(vs->sector_size_bytes); if (sector_size_bytes != 0x200 && sector_size_bytes != 0x400 && sector_size_bytes != 0x800 && sector_size_bytes != 0x1000 ) { return -1; } dbg("sector_size_bytes 0x%x", sector_size_bytes); dbg("sectors_per_cluster 0x%x", vs->sectors_per_cluster); reserved_sct = le16_to_cpu(vs->reserved_sct); dbg("reserved_sct 0x%x", reserved_sct); sect_count = le16_to_cpu(vs->sectors); if (sect_count == 0) sect_count = le32_to_cpu(vs->total_sect); dbg("sect_count 0x%x", sect_count); fat_size_sct = le16_to_cpu(vs->fat_length); if (fat_size_sct == 0) fat_size_sct = le32_to_cpu(vs->type.fat32.fat32_length); fat_size_sct *= vs->fats; dbg("fat_size_sct 0x%x", fat_size_sct); dir_entries = le16_to_cpu(vs->dir_entries); dir_size_sct = ((dir_entries * sizeof(struct vfat_dir_entry)) + (sector_size_bytes-1)) / sector_size_bytes; dbg("dir_size_sct 0x%x", dir_size_sct); cluster_count = sect_count - (reserved_sct + fat_size_sct + dir_size_sct); cluster_count /= vs->sectors_per_cluster; dbg("cluster_count 0x%x", cluster_count); // if (cluster_count < FAT12_MAX) { // strcpy(id->type_version, "FAT12"); // } else if (cluster_count < FAT16_MAX) { // strcpy(id->type_version, "FAT16"); // } else { // strcpy(id->type_version, "FAT32"); // goto fat32; // } if (cluster_count >= FAT16_MAX) goto fat32; /* the label may be an attribute in the root directory */ root_start_off = (reserved_sct + fat_size_sct) * sector_size_bytes; dbg("root dir start 0x%llx", (unsigned long long) root_start_off); dbg("expected entries 0x%x", dir_entries); buf_size = dir_entries * sizeof(struct vfat_dir_entry); buf = volume_id_get_buffer(id, fat_partition_off + root_start_off, buf_size); if (buf == NULL) goto ret; label = get_attr_volume_id((struct vfat_dir_entry*) buf, dir_entries); vs = volume_id_get_buffer(id, fat_partition_off, 0x200); if (vs == NULL) return -1; if (label != NULL && memcmp(label, "NO NAME ", 11) != 0) { // volume_id_set_label_raw(id, label, 11); volume_id_set_label_string(id, label, 11); } else if (memcmp(vs->type.fat.label, "NO NAME ", 11) != 0) { // volume_id_set_label_raw(id, vs->type.fat.label, 11); volume_id_set_label_string(id, vs->type.fat.label, 11); } volume_id_set_uuid(id, vs->type.fat.serno, UUID_DOS); goto ret; fat32: /* FAT32 root dir is a cluster chain like any other directory */ buf_size = vs->sectors_per_cluster * sector_size_bytes; root_cluster = le32_to_cpu(vs->type.fat32.root_cluster); start_data_sct = reserved_sct + fat_size_sct; next_cluster = root_cluster; maxloop = 100; while (--maxloop) { uint64_t next_off_sct; uint64_t next_off; uint64_t fat_entry_off; int count; dbg("next_cluster 0x%x", (unsigned)next_cluster); next_off_sct = (uint64_t)(next_cluster - 2) * vs->sectors_per_cluster; next_off = (start_data_sct + next_off_sct) * sector_size_bytes; dbg("cluster offset 0x%llx", (unsigned long long) next_off); /* get cluster */ buf = volume_id_get_buffer(id, fat_partition_off + next_off, buf_size); if (buf == NULL) goto ret; dir = (struct vfat_dir_entry*) buf; count = buf_size / sizeof(struct vfat_dir_entry); dbg("expected entries 0x%x", count); label = get_attr_volume_id(dir, count); if (label) break; /* get FAT entry */ fat_entry_off = (reserved_sct * sector_size_bytes) + (next_cluster * sizeof(uint32_t)); dbg("fat_entry_off 0x%llx", (unsigned long long)fat_entry_off); buf = volume_id_get_buffer(id, fat_partition_off + fat_entry_off, buf_size); if (buf == NULL) goto ret; /* set next cluster */ next_cluster = le32_to_cpu(*(uint32_t*)buf) & 0x0fffffff; if (next_cluster < 2 || next_cluster > FAT32_MAX) break; } if (maxloop == 0) dbg("reached maximum follow count of root cluster chain, give up"); vs = volume_id_get_buffer(id, fat_partition_off, 0x200); if (vs == NULL) return -1; if (label != NULL && memcmp(label, "NO NAME ", 11) != 0) { // volume_id_set_label_raw(id, label, 11); volume_id_set_label_string(id, label, 11); } else if (memcmp(vs->type.fat32.label, "NO NAME ", 11) != 0) { // volume_id_set_label_raw(id, vs->type.fat32.label, 11); volume_id_set_label_string(id, vs->type.fat32.label, 11); } volume_id_set_uuid(id, vs->type.fat32.serno, UUID_DOS); ret: // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); IF_FEATURE_BLKID_TYPE(id->type = "vfat";) return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/reiserfs.c������������������������������������������������������0000644�0000000�0000000�00000006663�12263563520�020070� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * Copyright (C) 2005 Tobias Klauser <tklauser@access.unizh.ch> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_REISERFS) += reiserfs.o //config: //config:config FEATURE_VOLUMEID_REISERFS //config: bool "Reiser filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" struct reiserfs_super_block { uint32_t blocks_count; uint32_t free_blocks; uint32_t root_block; uint32_t journal_block; uint32_t journal_dev; uint32_t orig_journal_size; uint32_t dummy2[5]; uint16_t blocksize; uint16_t dummy3[3]; uint8_t magic[12]; uint32_t dummy4[5]; uint8_t uuid[16]; uint8_t label[16]; } PACKED; struct reiser4_super_block { uint8_t magic[16]; uint16_t dummy[2]; uint8_t uuid[16]; uint8_t label[16]; uint64_t dummy2; } PACKED; #define REISERFS1_SUPERBLOCK_OFFSET 0x2000 #define REISERFS_SUPERBLOCK_OFFSET 0x10000 int FAST_FUNC volume_id_probe_reiserfs(struct volume_id *id /*,uint64_t off*/) { #define off ((uint64_t)0) struct reiserfs_super_block *rs; struct reiser4_super_block *rs4; dbg("reiserfs: probing at offset 0x%llx", (unsigned long long) off); rs = volume_id_get_buffer(id, off + REISERFS_SUPERBLOCK_OFFSET, 0x200); if (rs == NULL) return -1; if (memcmp(rs->magic, "ReIsErFs", 8) == 0) { dbg("reiserfs: ReIsErFs, no label"); // strcpy(id->type_version, "3.5"); goto found; } if (memcmp(rs->magic, "ReIsEr2Fs", 9) == 0) { dbg("reiserfs: ReIsEr2Fs"); // strcpy(id->type_version, "3.6"); goto found_label; } if (memcmp(rs->magic, "ReIsEr3Fs", 9) == 0) { dbg("reiserfs: ReIsEr3Fs"); // strcpy(id->type_version, "JR"); goto found_label; } rs4 = (struct reiser4_super_block *) rs; if (memcmp(rs4->magic, "ReIsEr4", 7) == 0) { // strcpy(id->type_version, "4"); // volume_id_set_label_raw(id, rs4->label, 16); volume_id_set_label_string(id, rs4->label, 16); volume_id_set_uuid(id, rs4->uuid, UUID_DCE); dbg("reiserfs: ReIsEr4, label '%s' uuid '%s'", id->label, id->uuid); goto found; } rs = volume_id_get_buffer(id, off + REISERFS1_SUPERBLOCK_OFFSET, 0x200); if (rs == NULL) return -1; if (memcmp(rs->magic, "ReIsErFs", 8) == 0) { dbg("reiserfs: ReIsErFs, no label"); // strcpy(id->type_version, "3.5"); goto found; } dbg("reiserfs: no signature found"); return -1; found_label: // volume_id_set_label_raw(id, rs->label, 16); volume_id_set_label_string(id, rs->label, 16); volume_id_set_uuid(id, rs->uuid, UUID_DCE); dbg("reiserfs: label '%s' uuid '%s'", id->label, id->uuid); found: // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); IF_FEATURE_BLKID_TYPE(id->type = "reiserfs";) return 0; } �����������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/unused_mac.c����������������������������������������������������0000644�0000000�0000000�00000007732�12263563520�020367� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_MAC) += mac.o //config: //config:### config FEATURE_VOLUMEID_MAC //config:### bool "mac filesystem" //config:### default y //config:### depends on VOLUMEID //config:### help //config:### TODO //config: #include "volume_id_internal.h" struct mac_driver_desc { uint8_t signature[2]; uint16_t block_size; uint32_t block_count; } PACKED; struct mac_partition { uint8_t signature[2]; uint16_t res1; uint32_t map_count; uint32_t start_block; uint32_t block_count; uint8_t name[32]; uint8_t type[32]; } PACKED; int FAST_FUNC volume_id_probe_mac_partition_map(struct volume_id *id, uint64_t off) { const uint8_t *buf; struct mac_driver_desc *driver; struct mac_partition *part; dbg("probing at offset 0x%llx", (unsigned long long) off); buf = volume_id_get_buffer(id, off, 0x200); if (buf == NULL) return -1; part = (struct mac_partition *) buf; if (part->signature[0] == 'P' && part->signature[1] == 'M' /* "PM" */ && (memcmp(part->type, "Apple_partition_map", 19) == 0) ) { /* linux creates an own subdevice for the map * just return the type if the drive header is missing */ // volume_id_set_usage(id, VOLUME_ID_PARTITIONTABLE); // id->type = "mac_partition_map"; return 0; } driver = (struct mac_driver_desc *) buf; if (driver->signature[0] == 'E' && driver->signature[1] == 'R') { /* "ER" */ /* we are on a main device, like a CD * just try to probe the first partition from the map */ unsigned bsize = be16_to_cpu(driver->block_size); int part_count; int i; /* get first entry of partition table */ buf = volume_id_get_buffer(id, off + bsize, 0x200); if (buf == NULL) return -1; part = (struct mac_partition *) buf; if (part->signature[0] != 'P' || part->signature[1] != 'M') /* not "PM" */ return -1; part_count = be32_to_cpu(part->map_count); dbg("expecting %d partition entries", part_count); if (id->partitions != NULL) free(id->partitions); id->partitions = xzalloc(part_count * sizeof(struct volume_id_partition)); id->partition_count = part_count; for (i = 0; i < part_count; i++) { uint64_t poff; uint64_t plen; buf = volume_id_get_buffer(id, off + ((i+1) * bsize), 0x200); if (buf == NULL) return -1; part = (struct mac_partition *) buf; if (part->signature[0] != 'P' || part->signature[1] != 'M') /* not "PM" */ return -1; poff = be32_to_cpu(part->start_block) * bsize; plen = be32_to_cpu(part->block_count) * bsize; dbg("found '%s' partition entry at 0x%llx, len 0x%llx", part->type, (unsigned long long) poff, (unsigned long long) plen); // id->partitions[i].pt_off = poff; // id->partitions[i].pt_len = plen; // if (memcmp(part->type, "Apple_Free", 10) == 0) { // volume_id_set_usage_part(&id->partitions[i], VOLUME_ID_UNUSED); // } else if (memcmp(part->type, "Apple_partition_map", 19) == 0) { // volume_id_set_usage_part(&id->partitions[i], VOLUME_ID_PARTITIONTABLE); // } else { // volume_id_set_usage_part(&id->partitions[i], VOLUME_ID_UNPROBED); // } } // volume_id_set_usage(id, VOLUME_ID_PARTITIONTABLE); // id->type = "mac_partition_map"; return 0; } return -1; } ��������������������������������������busybox-1.22.1/util-linux/volume_id/unused_via_raid.c�����������������������������������������������0000644�0000000�0000000�00000004146�12263563520�021401� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_VIARAID) += via_raid.o //config: //config:### config FEATURE_VOLUMEID_VIARAID //config:### bool "via raid" //config:### default y //config:### depends on VOLUMEID //config:### help //config:### TODO //config: #include "volume_id_internal.h" struct via_meta { uint16_t signature; uint8_t version_number; struct via_array { uint16_t disk_bits; uint8_t disk_array_ex; uint32_t capacity_low; uint32_t capacity_high; uint32_t serial_checksum; } PACKED array; uint32_t serial_checksum[8]; uint8_t checksum; } PACKED; #define VIA_SIGNATURE 0xAA55 int FAST_FUNC volume_id_probe_via_raid(struct volume_id *id, uint64_t off, uint64_t size) { uint64_t meta_off; struct via_meta *via; dbg("probing at offset 0x%llx, size 0x%llx", (unsigned long long) off, (unsigned long long) size); if (size < 0x10000) return -1; meta_off = ((size / 0x200)-1) * 0x200; via = volume_id_get_buffer(id, off + meta_off, 0x200); if (via == NULL) return -1; if (via->signature != cpu_to_le16(VIA_SIGNATURE)) return -1; if (via->version_number > 1) return -1; // volume_id_set_usage(id, VOLUME_ID_RAID); // id->type_version[0] = '0' + via->version_number; // id->type_version[1] = '\0'; // id->type = "via_raid_member"; return 0; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/Config.src������������������������������������������������������0000644�0000000�0000000�00000000406�12263563520�020005� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # config VOLUMEID bool #No description makes it a hidden option default n menu "Filesystem/Volume identification" depends on VOLUMEID INSERT endmenu ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/exfat.c���������������������������������������������������������0000644�0000000�0000000�00000011542�12263563520�017345� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2012 S-G Bergh <sgb@systemasis.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_EXFAT) += exfat.o //config: //config:config FEATURE_VOLUMEID_EXFAT //config: bool "exFAT filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: exFAT (extended FAT) is a proprietary file system designed especially //config: for flash drives. It has many features from NTFS, but with less //config: overhead. exFAT is used on most SDXC cards for consumer electronics. //config: #include "volume_id_internal.h" #define EXFAT_SB_OFFSET 0 #define EXFAT_DIR_ENTRY_SZ 32 #define EXFAT_MAX_DIR_ENTRIES 100 struct exfat_super_block { /* 0x00 */ uint8_t boot_jump[3]; /* 0x03 */ uint8_t fs_name[8]; /* 0x0B */ uint8_t must_be_zero[53]; /* 0x40 */ uint64_t partition_offset; /* 0x48 */ uint64_t volume_length; /* 0x50 */ uint32_t fat_offset; // Sector address of 1st FAT /* 0x54 */ uint32_t fat_size; // In sectors /* 0x58 */ uint32_t cluster_heap_offset; // Sector address of Data Region /* 0x5C */ uint32_t cluster_count; /* 0x60 */ uint32_t root_dir; // Cluster address of Root Directory /* 0x64 */ uint8_t vol_serial_nr[4]; // Volume ID /* 0x68 */ uint16_t fs_revision; // VV.MM /* 0x6A */ uint16_t vol_flags; /* 0x6C */ uint8_t bytes_per_sector; // Power of 2: 9 => 512, 12 => 4096 /* 0x6D */ uint8_t sectors_per_cluster; // Power of 2 /* 0x6E */ uint8_t nr_of_fats; // 2 for TexFAT /* 0x6F */ // ... } PACKED; struct exfat_dir_entry { /* 0x00 */ uint8_t entry_type; union { struct volume_label { /* 0x01 */ uint8_t char_count; // Length of label /* 0x02 */ uint16_t vol_label[11]; // UTF16 string without null termination /* 0x18 */ uint8_t reserved[8]; /* 0x20 */ } PACKED label; struct volume_guid { /* 0x01 */ uint8_t sec_count; /* 0x02 */ uint16_t set_checksum; /* 0x04 */ uint16_t flags; /* 0x06 */ uint8_t vol_guid[16]; /* 0x16 */ uint8_t reserved[10]; /* 0x20 */ } PACKED guid; } PACKED type; } PACKED; int FAST_FUNC volume_id_probe_exfat(struct volume_id *id /*,uint64_t off*/) { struct exfat_super_block *sb; struct exfat_dir_entry *de; unsigned sector_sz; unsigned cluster_sz; uint64_t root_dir_off; unsigned count; unsigned need_lbl_guid; // Primary super block dbg("exFAT: probing at offset 0x%x", EXFAT_SB_OFFSET); sb = volume_id_get_buffer(id, EXFAT_SB_OFFSET, sizeof(*sb)); if (!sb) return -1; if (memcmp(sb->fs_name, "EXFAT ", 8) != 0) return -1; sector_sz = 1 << sb->bytes_per_sector; cluster_sz = sector_sz << sb->sectors_per_cluster; // There are no clusters 0 and 1, so the first cluster is 2. root_dir_off = (uint64_t)EXFAT_SB_OFFSET + // Hmm... should we cast sector_sz/cluster_sz to uint64_t? (le32_to_cpu(sb->cluster_heap_offset)) * sector_sz + (le32_to_cpu(sb->root_dir) - 2) * cluster_sz; dbg("exFAT: sector size 0x%x bytes", sector_sz); dbg("exFAT: cluster size 0x%x bytes", cluster_sz); dbg("exFAT: root dir is at 0x%llx", (long long)root_dir_off); // Use DOS uuid as fallback, if no GUID set volume_id_set_uuid(id, sb->vol_serial_nr, UUID_DOS); // EXFAT_MAX_DIR_ENTRIES is used as a safety belt. // The Root Directory may hold an unlimited number of entries, // so we do not want to check all. Usually label and GUID // are in the beginning, but there are no guarantees. need_lbl_guid = (1 << 0) | (1 << 1); for (count = 0; count < EXFAT_MAX_DIR_ENTRIES; count++) { de = volume_id_get_buffer(id, root_dir_off + (count * EXFAT_DIR_ENTRY_SZ), EXFAT_DIR_ENTRY_SZ); if (de == NULL) break; if (de->entry_type == 0x00) { // End of Directory Marker dbg("exFAT: End of root directory reached after %u entries", count); break; } if (de->entry_type == 0x83) { // Volume Label Directory Entry volume_id_set_label_unicode16(id, (uint8_t *)de->type.label.vol_label, LE, 2 * de->type.label.char_count); need_lbl_guid &= ~(1 << 0); } if (de->entry_type == 0xA0) { // Volume GUID Directory Entry volume_id_set_uuid(id, de->type.guid.vol_guid, UUID_DCE); need_lbl_guid &= ~(1 << 1); } if (!need_lbl_guid) break; } IF_FEATURE_BLKID_TYPE(id->type = "exfat";) return 0; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/Kbuild.src������������������������������������������������������0000644�0000000�0000000�00000000255�12263563520�020014� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org> # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/nilfs.c���������������������������������������������������������0000644�0000000�0000000�00000010163�12263563520�017347� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * Copyright (C) 2012 S-G Bergh <sgb@systemasis.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_NILFS) += nilfs.o //config: //config:config FEATURE_VOLUMEID_NILFS //config: bool "nilfs filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: NILFS is a New Implementation of a Log-Structured File System (LFS) //config: that supports continuous snapshots. This provides features like //config: versioning of the entire filesystem, restoration of files that //config: were deleted a few minutes ago. NILFS keeps consistency like //config: conventional LFS, so it provides quick recovery after system crashes. //config: //config: The possible use of NILFS includes versioning, tamper detection, //config: SOX compliance logging, and so forth. It can serve as an alternative //config: filesystem for Linux desktop environment, or as a basis of advanced //config: storage appliances. //config: #include "volume_id_internal.h" #define NILFS_UUID_SIZE 16 #define NILFS_LABEL_SIZE 80 #define NILFS_SB1_OFFSET 0x400 #define NILFS_SB2_OFFSET 0x1000 #define NILFS_MAGIC 0x3434 struct nilfs2_super_block { /* 0x00 */ uint32_t s_rev_level; // Major revision level. /* 0x04 */ uint16_t s_minor_rev_level; // Minor revision level. /* 0x06 */ uint16_t s_magic; // Magic signature. /* 0x08 */ uint16_t s_bytes; /* 0x0A */ uint16_t s_flags; /* 0x0C */ uint32_t s_crc_seed; /* 0x10 */ uint32_t s_sum; /* 0x14 */ uint32_t s_log_block_size; /* 0x18 */ uint64_t s_nsegments; /* 0x20 */ uint64_t s_dev_size; // Block device size in bytes. /* 0x28 */ uint64_t s_first_data_block; /* 0x30 */ uint32_t s_blocks_per_segment; /* 0x34 */ uint32_t s_r_segments_percentage; /* 0x38 */ uint64_t s_last_cno; /* 0x40 */ uint64_t s_last_pseg; /* 0x48 */ uint64_t s_last_seq; /* 0x50 */ uint64_t s_free_blocks_count; /* 0x58 */ uint64_t s_ctime; /* 0x60 */ uint64_t s_mtime; /* 0x68 */ uint64_t s_wtime; /* 0x70 */ uint16_t s_mnt_count; /* 0x72 */ uint16_t s_max_mnt_count; /* 0x74 */ uint16_t s_state; /* 0x76 */ uint16_t s_errors; /* 0x78 */ uint64_t s_lastcheck; /* 0x80 */ uint32_t s_checkinterval; /* 0x84 */ uint32_t s_creator_os; /* 0x88 */ uint16_t s_def_resuid; /* 0x8A */ uint16_t s_def_resgid; /* 0x8C */ uint32_t s_first_ino; /* 0x90 */ uint16_t s_inode_size; /* 0x92 */ uint16_t s_dat_entry_size; /* 0x94 */ uint16_t s_checkpoint_size; /* 0x96 */ uint16_t s_segment_usage_size; /* 0x98 */ uint8_t s_uuid[NILFS_UUID_SIZE]; // 128-bit UUID for volume. /* 0xA8 */ uint8_t s_volume_name[NILFS_LABEL_SIZE]; // Volume label. /* 0xF8 */ // ... } PACKED; int FAST_FUNC volume_id_probe_nilfs(struct volume_id *id /*,uint64_t off*/) { struct nilfs2_super_block *sb; // Primary super block dbg("nilfs: probing at offset 0x%x", NILFS_SB1_OFFSET); sb = volume_id_get_buffer(id, NILFS_SB1_OFFSET, sizeof(*sb)); if (sb == NULL) return -1; if (sb->s_magic != NILFS_MAGIC) return -1; // The secondary superblock is not always used, so ignore it for now. // When used it is at 4K from the end of the partition (sb->s_dev_size - NILFS_SB2_OFFSET). volume_id_set_label_string(id, sb->s_volume_name, NILFS_LABEL_SIZE < VOLUME_ID_LABEL_SIZE ? NILFS_LABEL_SIZE : VOLUME_ID_LABEL_SIZE); volume_id_set_uuid(id, sb->s_uuid, UUID_DCE); if (sb->s_rev_level == 2) IF_FEATURE_BLKID_TYPE(id->type = "nilfs2"); return 0; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/ext.c�����������������������������������������������������������0000644�0000000�0000000�00000004334�12263563520�017037� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_EXT) += ext.o //config: //config:config FEATURE_VOLUMEID_EXT //config: bool "Ext filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" #include "bb_e2fs_defs.h" #define EXT_SUPERBLOCK_OFFSET 0x400 int FAST_FUNC volume_id_probe_ext(struct volume_id *id /*,uint64_t off*/) { #define off ((uint64_t)0) struct ext2_super_block *es; dbg("ext: probing at offset 0x%llx", (unsigned long long) off); es = volume_id_get_buffer(id, off + EXT_SUPERBLOCK_OFFSET, 0x200); if (es == NULL) return -1; if (es->s_magic != cpu_to_le16(EXT2_SUPER_MAGIC)) { dbg("ext: no magic found"); return -1; } // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); // volume_id_set_label_raw(id, es->volume_name, 16); volume_id_set_label_string(id, (void*)es->s_volume_name, 16); volume_id_set_uuid(id, es->s_uuid, UUID_DCE); dbg("ext: label '%s' uuid '%s'", id->label, id->uuid); #if ENABLE_FEATURE_BLKID_TYPE if ((es->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_HUGE_FILE | EXT4_FEATURE_RO_COMPAT_DIR_NLINK)) || (es->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_EXTENTS | EXT4_FEATURE_INCOMPAT_64BIT)) ) { id->type = "ext4"; } else if (es->s_feature_compat & cpu_to_le32(EXT3_FEATURE_COMPAT_HAS_JOURNAL)) id->type = "ext3"; else id->type = "ext2"; #endif return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/luks.c����������������������������������������������������������0000644�0000000�0000000�00000005550�12263563520�017216� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2005 W. Michael Petullo <mike@flyn.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_LUKS) += luks.o //config: //config:config FEATURE_VOLUMEID_LUKS //config: bool "luks filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" #define LUKS_MAGIC_L 6 #define UUID_STRING_L 40 #define LUKS_CIPHERNAME_L 32 #define LUKS_CIPHERMODE_L 32 #define LUKS_HASHSPEC_L 32 #define LUKS_DIGESTSIZE 20 #define LUKS_SALTSIZE 32 #define LUKS_NUMKEYS 8 static const uint8_t LUKS_MAGIC[] = { 'L','U','K','S', 0xba, 0xbe }; struct luks_phdr { uint8_t magic[LUKS_MAGIC_L]; uint16_t version; uint8_t cipherName[LUKS_CIPHERNAME_L]; uint8_t cipherMode[LUKS_CIPHERMODE_L]; uint8_t hashSpec[LUKS_HASHSPEC_L]; uint32_t payloadOffset; uint32_t keyBytes; uint8_t mkDigest[LUKS_DIGESTSIZE]; uint8_t mkDigestSalt[LUKS_SALTSIZE]; uint32_t mkDigestIterations; uint8_t uuid[UUID_STRING_L]; struct { uint32_t active; uint32_t passwordIterations; uint8_t passwordSalt[LUKS_SALTSIZE]; uint32_t keyMaterialOffset; uint32_t stripes; } keyblock[LUKS_NUMKEYS]; }; enum { EXPECTED_SIZE_luks_phdr = 0 + 1 * LUKS_MAGIC_L + 2 + 1 * LUKS_CIPHERNAME_L + 1 * LUKS_CIPHERMODE_L + 1 * LUKS_HASHSPEC_L + 4 + 4 + 1 * LUKS_DIGESTSIZE + 1 * LUKS_SALTSIZE + 4 + 1 * UUID_STRING_L + LUKS_NUMKEYS * (0 + 4 + 4 + 1 * LUKS_SALTSIZE + 4 + 4 ) }; struct BUG_bad_size_luks_phdr { char BUG_bad_size_luks_phdr[ sizeof(struct luks_phdr) == EXPECTED_SIZE_luks_phdr ? 1 : -1]; }; int FAST_FUNC volume_id_probe_luks(struct volume_id *id /*,uint64_t off*/) { #define off ((uint64_t)0) struct luks_phdr *header; header = volume_id_get_buffer(id, off, sizeof(*header)); if (header == NULL) return -1; if (memcmp(header->magic, LUKS_MAGIC, LUKS_MAGIC_L)) return -1; // volume_id_set_usage(id, VOLUME_ID_CRYPTO); volume_id_set_uuid(id, header->uuid, UUID_DCE_STRING); IF_FEATURE_BLKID_TYPE(id->type = "crypto_LUKS";) return 0; } ��������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/unused_isw_raid.c�����������������������������������������������0000644�0000000�0000000�00000003704�12263563520�021423� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_ISWRAID) += isw_raid.o //config: //config:### config FEATURE_VOLUMEID_ISWRAID //config:### bool "intel raid" //config:### default y //config:### depends on VOLUMEID //config:### help //config:### TODO //config: #include "volume_id_internal.h" struct isw_meta { uint8_t sig[32]; uint32_t check_sum; uint32_t mpb_size; uint32_t family_num; uint32_t generation_num; } PACKED; #define ISW_SIGNATURE "Intel Raid ISM Cfg Sig. " int FAST_FUNC volume_id_probe_intel_software_raid(struct volume_id *id, uint64_t off, uint64_t size) { uint64_t meta_off; struct isw_meta *isw; dbg("probing at offset 0x%llx, size 0x%llx", (unsigned long long) off, (unsigned long long) size); if (size < 0x10000) return -1; meta_off = ((size / 0x200)-2) * 0x200; isw = volume_id_get_buffer(id, off + meta_off, 0x200); if (isw == NULL) return -1; if (memcmp(isw->sig, ISW_SIGNATURE, sizeof(ISW_SIGNATURE)-1) != 0) return -1; // volume_id_set_usage(id, VOLUME_ID_RAID); // memcpy(id->type_version, &isw->sig[sizeof(ISW_SIGNATURE)-1], 6); // id->type = "isw_raid_member"; return 0; } ������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/jfs.c�����������������������������������������������������������0000644�0000000�0000000�00000003665�12263563520�017027� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_JFS) += jfs.o //config: //config:config FEATURE_VOLUMEID_JFS //config: bool "jfs filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" struct jfs_super_block { uint8_t magic[4]; uint32_t version; uint64_t size; uint32_t bsize; uint32_t dummy1; uint32_t pbsize; uint32_t dummy2[27]; uint8_t uuid[16]; uint8_t label[16]; uint8_t loguuid[16]; } PACKED; #define JFS_SUPERBLOCK_OFFSET 0x8000 int FAST_FUNC volume_id_probe_jfs(struct volume_id *id /*,uint64_t off*/) { #define off ((uint64_t)0) struct jfs_super_block *js; dbg("probing at offset 0x%llx", (unsigned long long) off); js = volume_id_get_buffer(id, off + JFS_SUPERBLOCK_OFFSET, 0x200); if (js == NULL) return -1; if (memcmp(js->magic, "JFS1", 4) != 0) return -1; // volume_id_set_label_raw(id, js->label, 16); volume_id_set_label_string(id, js->label, 16); volume_id_set_uuid(id, js->uuid, UUID_DCE); // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); IF_FEATURE_BLKID_TYPE(id->type = "jfs";) return 0; } ���������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/unused_promise_raid.c�������������������������������������������0000644�0000000�0000000�00000004030�12263563520�022270� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_PROMISERAID) += promise_raid.o //config: //config:### config FEATURE_VOLUMEID_PROMISERAID //config:### bool "promise raid" //config:### default y //config:### depends on VOLUMEID //config:### help //config:### TODO //config: #include "volume_id_internal.h" struct promise_meta { uint8_t sig[24]; } PACKED; #define PDC_CONFIG_OFF 0x1200 #define PDC_SIGNATURE "Promise Technology, Inc." int FAST_FUNC volume_id_probe_promise_fasttrack_raid(struct volume_id *id, uint64_t off, uint64_t size) { static const unsigned short sectors[] = { 63, 255, 256, 16, 399 }; struct promise_meta *pdc; unsigned i; dbg("probing at offset 0x%llx, size 0x%llx", (unsigned long long) off, (unsigned long long) size); if (size < 0x40000) return -1; for (i = 0; i < ARRAY_SIZE(sectors); i++) { uint64_t meta_off; meta_off = ((size / 0x200) - sectors[i]) * 0x200; pdc = volume_id_get_buffer(id, off + meta_off, 0x200); if (pdc == NULL) return -1; if (memcmp(pdc->sig, PDC_SIGNATURE, sizeof(PDC_SIGNATURE)-1) == 0) goto found; } return -1; found: // volume_id_set_usage(id, VOLUME_ID_RAID); // id->type = "promise_fasttrack_raid_member"; return 0; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/linux_raid.c����������������������������������������������������0000644�0000000�0000000�00000005061�12263563520�020373� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_LINUXRAID) += linux_raid.o //config: //config:config FEATURE_VOLUMEID_LINUXRAID //config: bool "linuxraid" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" struct mdp_super_block { uint32_t md_magic; uint32_t major_version; uint32_t minor_version; uint32_t patch_version; uint32_t gvalid_words; uint32_t set_uuid0; uint32_t ctime; uint32_t level; uint32_t size; uint32_t nr_disks; uint32_t raid_disks; uint32_t md_minor; uint32_t not_persistent; uint32_t set_uuid1; uint32_t set_uuid2; uint32_t set_uuid3; } PACKED; #define MD_RESERVED_BYTES 0x10000 #define MD_MAGIC 0xa92b4efc int FAST_FUNC volume_id_probe_linux_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size) { typedef uint32_t aliased_uint32_t FIX_ALIASING; #define off ((uint64_t)0) uint64_t sboff; uint8_t uuid[16]; struct mdp_super_block *mdp; dbg("probing at offset 0x%llx, size 0x%llx", (unsigned long long) off, (unsigned long long) size); if (size < 0x10000) return -1; sboff = (size & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES; mdp = volume_id_get_buffer(id, off + sboff, 0x800); if (mdp == NULL) return -1; if (mdp->md_magic != cpu_to_le32(MD_MAGIC)) return -1; *(aliased_uint32_t*)uuid = mdp->set_uuid0; memcpy(&uuid[4], &mdp->set_uuid1, 12); volume_id_set_uuid(id, uuid, UUID_DCE); // snprintf(id->type_version, sizeof(id->type_version)-1, "%u.%u.%u", // le32_to_cpu(mdp->major_version), // le32_to_cpu(mdp->minor_version), // le32_to_cpu(mdp->patch_version)); dbg("found raid signature"); // volume_id_set_usage(id, VOLUME_ID_RAID); IF_FEATURE_BLKID_TYPE(id->type = "linux_raid_member";) return 0; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/f2fs.c����������������������������������������������������������0000644�0000000�0000000�00000007233�12263563520�017100� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2012 S-G Bergh <sgb@systemasis.org> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_F2FS) += f2fs.o //config: //config:config FEATURE_VOLUMEID_F2FS //config: bool "f2fs filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: F2FS (aka Flash-Friendly File System) is a log-structured file system, //config: which is adapted to newer forms of storage. F2FS also remedies some //config: known issues of the older log structured file systems, such as high //config: cleaning overhead. //config: #include "volume_id_internal.h" #define F2FS_MAGIC 0xF2F52010 // F2FS Magic Number #define F2FS_UUID_SIZE 16 #define F2FS_LABEL_SIZE 512 #define F2FS_LABEL_BYTES 1024 #define F2FS_SB1_OFFSET 0x400 // offset for 1:st super block /* #define F2FS_SB2_OFFSET 0x1400 // offset for 2:nd super block */ struct f2fs_super_block { // According to version 1.1 /* 0x00 */ uint32_t magic; // Magic Number /* 0x04 */ uint16_t major_ver; // Major Version /* 0x06 */ uint16_t minor_ver; // Minor Version /* 0x08 */ uint32_t log_sectorsize; // log2 sector size in bytes /* 0x0C */ uint32_t log_sectors_per_block; // log2 # of sectors per block /* 0x10 */ uint32_t log_blocksize; // log2 block size in bytes /* 0x14 */ uint32_t log_blocks_per_seg; // log2 # of blocks per segment /* 0x18 */ uint32_t segs_per_sec; // # of segments per section /* 0x1C */ uint32_t secs_per_zone; // # of sections per zone /* 0x20 */ uint32_t checksum_offset; // checksum offset inside super block /* 0x24 */ uint64_t block_count; // total # of user blocks /* 0x2C */ uint32_t section_count; // total # of sections /* 0x30 */ uint32_t segment_count; // total # of segments /* 0x34 */ uint32_t segment_count_ckpt; // # of segments for checkpoint /* 0x38 */ uint32_t segment_count_sit; // # of segments for SIT /* 0x3C */ uint32_t segment_count_nat; // # of segments for NAT /* 0x40 */ uint32_t segment_count_ssa; // # of segments for SSA /* 0x44 */ uint32_t segment_count_main; // # of segments for main area /* 0x48 */ uint32_t segment0_blkaddr; // start block address of segment 0 /* 0x4C */ uint32_t cp_blkaddr; // start block address of checkpoint /* 0x50 */ uint32_t sit_blkaddr; // start block address of SIT /* 0x54 */ uint32_t nat_blkaddr; // start block address of NAT /* 0x58 */ uint32_t ssa_blkaddr; // start block address of SSA /* 0x5C */ uint32_t main_blkaddr; // start block address of main area /* 0x60 */ uint32_t root_ino; // root inode number /* 0x64 */ uint32_t node_ino; // node inode number /* 0x68 */ uint32_t meta_ino; // meta inode number /* 0x6C */ uint8_t uuid[F2FS_UUID_SIZE]; // 128-bit uuid for volume /* 0x7C */ uint16_t volume_name[F2FS_LABEL_SIZE]; // volume name // /* 0x47C */ uint32_t extension_count; // # of extensions below // /* 0x480 */ uint8_t extension_list[64][8]; // extension array } PACKED; int FAST_FUNC volume_id_probe_f2fs(struct volume_id *id /*,uint64_t off*/) { struct f2fs_super_block *sb; // Go for primary super block (ignore second sb) dbg("f2fs: probing at offset 0x%x", F2FS_SB1_OFFSET); sb = volume_id_get_buffer(id, F2FS_SB1_OFFSET, sizeof(*sb)); if (!sb) return -1; if (sb->magic != cpu_to_le32(F2FS_MAGIC)) return -1; IF_FEATURE_BLKID_TYPE(id->type = "f2fs"); // For version 1.0 we don't know sb structure and can't set label/uuid if (sb->major_ver == cpu_to_le16(1) && sb->minor_ver == cpu_to_le16(0)) return 0; volume_id_set_label_unicode16(id, (uint8_t *)sb->volume_name, LE, MIN(F2FS_LABEL_BYTES, VOLUME_ID_LABEL_SIZE)); volume_id_set_uuid(id, sb->uuid, UUID_DCE); return 0; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/ocfs2.c���������������������������������������������������������0000644�0000000�0000000�00000011051�12263563520�017245� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) Andre Masella <andre@masella.no-ip.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_OCFS2) += ocfs2.o //config: //config:config FEATURE_VOLUMEID_OCFS2 //config: bool "ocfs2 filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" /* All these values are taken from ocfs2-tools's ocfs2_fs.h */ #define OCFS2_VOL_UUID_LEN 16 #define OCFS2_MAX_VOL_LABEL_LEN 64 #define OCFS2_SUPERBLOCK_OFFSET 0x2000 /* This is the superblock. The OCFS2 header files have structs in structs. This is one has been simplified since we only care about the superblock. */ struct ocfs2_super_block { uint8_t i_signature[8]; /* Signature for validation */ uint32_t i_generation; /* Generation number */ int16_t i_suballoc_slot; /* Slot suballocator this inode belongs to */ uint16_t i_suballoc_bit; /* Bit offset in suballocator block group */ uint32_t i_reserved0; uint32_t i_clusters; /* Cluster count */ uint32_t i_uid; /* Owner UID */ uint32_t i_gid; /* Owning GID */ uint64_t i_size; /* Size in bytes */ uint16_t i_mode; /* File mode */ uint16_t i_links_count; /* Links count */ uint32_t i_flags; /* File flags */ uint64_t i_atime; /* Access time */ uint64_t i_ctime; /* Creation time */ uint64_t i_mtime; /* Modification time */ uint64_t i_dtime; /* Deletion time */ uint64_t i_blkno; /* Offset on disk, in blocks */ uint64_t i_last_eb_blk; /* Pointer to last extent block */ uint32_t i_fs_generation; /* Generation per fs-instance */ uint32_t i_atime_nsec; uint32_t i_ctime_nsec; uint32_t i_mtime_nsec; uint64_t i_reserved1[9]; uint64_t i_pad1; /* Generic way to refer to this 64bit union */ /* Normally there is a union of the different block types, but we only care about the superblock. */ uint16_t s_major_rev_level; uint16_t s_minor_rev_level; uint16_t s_mnt_count; int16_t s_max_mnt_count; uint16_t s_state; /* File system state */ uint16_t s_errors; /* Behaviour when detecting errors */ uint32_t s_checkinterval; /* Max time between checks */ uint64_t s_lastcheck; /* Time of last check */ uint32_t s_creator_os; /* OS */ uint32_t s_feature_compat; /* Compatible feature set */ uint32_t s_feature_incompat; /* Incompatible feature set */ uint32_t s_feature_ro_compat; /* Readonly-compatible feature set */ uint64_t s_root_blkno; /* Offset, in blocks, of root directory dinode */ uint64_t s_system_dir_blkno; /* Offset, in blocks, of system directory dinode */ uint32_t s_blocksize_bits; /* Blocksize for this fs */ uint32_t s_clustersize_bits; /* Clustersize for this fs */ uint16_t s_max_slots; /* Max number of simultaneous mounts before tunefs required */ uint16_t s_reserved1; uint32_t s_reserved2; uint64_t s_first_cluster_group; /* Block offset of 1st cluster group header */ uint8_t s_label[OCFS2_MAX_VOL_LABEL_LEN]; /* Label for mounting, etc. */ uint8_t s_uuid[OCFS2_VOL_UUID_LEN]; /* 128-bit uuid */ } PACKED; int FAST_FUNC volume_id_probe_ocfs2(struct volume_id *id /*,uint64_t off*/) { #define off ((uint64_t)0) struct ocfs2_super_block *os; dbg("probing at offset 0x%llx", (unsigned long long) off); os = volume_id_get_buffer(id, off + OCFS2_SUPERBLOCK_OFFSET, 0x200); if (os == NULL) return -1; if (memcmp(os->i_signature, "OCFSV2", 6) != 0) { return -1; } // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); // volume_id_set_label_raw(id, os->s_label, OCFS2_MAX_VOL_LABEL_LEN < VOLUME_ID_LABEL_SIZE ? // OCFS2_MAX_VOL_LABEL_LEN : VOLUME_ID_LABEL_SIZE); volume_id_set_label_string(id, os->s_label, OCFS2_MAX_VOL_LABEL_LEN < VOLUME_ID_LABEL_SIZE ? OCFS2_MAX_VOL_LABEL_LEN : VOLUME_ID_LABEL_SIZE); volume_id_set_uuid(id, os->s_uuid, UUID_DCE); IF_FEATURE_BLKID_TYPE(id->type = "ocfs2";) return 0; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/udf.c�����������������������������������������������������������0000644�0000000�0000000�00000011077�12263563520�017017� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_UDF) += udf.o //config: //config:config FEATURE_VOLUMEID_UDF //config: bool "udf filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" struct volume_descriptor { struct descriptor_tag { uint16_t id; uint16_t version; uint8_t checksum; uint8_t reserved; uint16_t serial; uint16_t crc; uint16_t crc_len; uint32_t location; } PACKED tag; union { struct anchor_descriptor { uint32_t length; uint32_t location; } PACKED anchor; struct primary_descriptor { uint32_t seq_num; uint32_t desc_num; struct dstring { uint8_t clen; uint8_t c[31]; } PACKED ident; } PACKED primary; } PACKED type; } PACKED; struct volume_structure_descriptor { uint8_t type; uint8_t id[5]; uint8_t version; } PACKED; #define UDF_VSD_OFFSET 0x8000 int FAST_FUNC volume_id_probe_udf(struct volume_id *id /*,uint64_t off*/) { #define off ((uint64_t)0) struct volume_descriptor *vd; struct volume_structure_descriptor *vsd; unsigned bs; unsigned b; unsigned type; unsigned count; unsigned loc; unsigned clen; dbg("probing at offset 0x%llx", (unsigned long long) off); vsd = volume_id_get_buffer(id, off + UDF_VSD_OFFSET, 0x200); if (vsd == NULL) return -1; if (memcmp(vsd->id, "NSR02", 5) == 0) goto blocksize; if (memcmp(vsd->id, "NSR03", 5) == 0) goto blocksize; if (memcmp(vsd->id, "BEA01", 5) == 0) goto blocksize; if (memcmp(vsd->id, "BOOT2", 5) == 0) goto blocksize; if (memcmp(vsd->id, "CD001", 5) == 0) goto blocksize; if (memcmp(vsd->id, "CDW02", 5) == 0) goto blocksize; if (memcmp(vsd->id, "TEA03", 5) == 0) goto blocksize; return -1; blocksize: /* search the next VSD to get the logical block size of the volume */ for (bs = 0x800; bs < 0x8000; bs += 0x800) { vsd = volume_id_get_buffer(id, off + UDF_VSD_OFFSET + bs, 0x800); if (vsd == NULL) return -1; dbg("test for blocksize: 0x%x", bs); if (vsd->id[0] != '\0') goto nsr; } return -1; nsr: /* search the list of VSDs for a NSR descriptor */ for (b = 0; b < 64; b++) { vsd = volume_id_get_buffer(id, off + UDF_VSD_OFFSET + (b * bs), 0x800); if (vsd == NULL) return -1; dbg("vsd: %c%c%c%c%c", vsd->id[0], vsd->id[1], vsd->id[2], vsd->id[3], vsd->id[4]); if (vsd->id[0] == '\0') return -1; if (memcmp(vsd->id, "NSR02", 5) == 0) goto anchor; if (memcmp(vsd->id, "NSR03", 5) == 0) goto anchor; } return -1; anchor: /* read anchor volume descriptor */ vd = volume_id_get_buffer(id, off + (256 * bs), 0x200); if (vd == NULL) return -1; type = le16_to_cpu(vd->tag.id); if (type != 2) /* TAG_ID_AVDP */ goto found; /* get desriptor list address and block count */ count = le32_to_cpu(vd->type.anchor.length) / bs; loc = le32_to_cpu(vd->type.anchor.location); dbg("0x%x descriptors starting at logical secor 0x%x", count, loc); /* pick the primary descriptor from the list */ for (b = 0; b < count; b++) { vd = volume_id_get_buffer(id, off + ((loc + b) * bs), 0x200); if (vd == NULL) return -1; type = le16_to_cpu(vd->tag.id); dbg("descriptor type %i", type); /* check validity */ if (type == 0) goto found; if (le32_to_cpu(vd->tag.location) != loc + b) goto found; if (type == 1) /* TAG_ID_PVD */ goto pvd; } goto found; pvd: // volume_id_set_label_raw(id, &(vd->type.primary.ident.clen), 32); clen = vd->type.primary.ident.clen; dbg("label string charsize=%i bit", clen); if (clen == 8) volume_id_set_label_string(id, vd->type.primary.ident.c, 31); else if (clen == 16) volume_id_set_label_unicode16(id, vd->type.primary.ident.c, BE, 31); found: // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); IF_FEATURE_BLKID_TYPE(id->type = "udf";) return 0; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/btrfs.c���������������������������������������������������������0000644�0000000�0000000�00000006232�12263563520�017356� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * Copyright (C) 2009 Vladimir Dronnikov <dronnikov@gmail.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_BTRFS) += btrfs.o //config: //config:config FEATURE_VOLUMEID_BTRFS //config: bool "btrfs filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" #define BTRFS_UUID_SIZE 16 #define BTRFS_LABEL_SIZE 256 #define BTRFS_CSUM_SIZE 32 #define BTRFS_FSID_SIZE 16 #define BTRFS_MAGIC "_BHRfS_M" struct btrfs_dev_item { uint64_t devid; uint64_t total_bytes; uint64_t bytes_used; uint32_t io_align; uint32_t io_width; uint32_t sector_size; uint64_t type; uint64_t generation; uint64_t start_offset; uint32_t dev_group; uint8_t seek_speed; uint8_t bandwidth; uint8_t uuid[BTRFS_UUID_SIZE]; uint8_t fsid[BTRFS_UUID_SIZE]; } PACKED; struct btrfs_super_block { uint8_t csum[BTRFS_CSUM_SIZE]; uint8_t fsid[BTRFS_FSID_SIZE]; // UUID uint64_t bytenr; uint64_t flags; uint8_t magic[8]; uint64_t generation; uint64_t root; uint64_t chunk_root; uint64_t log_root; uint64_t log_root_transid; uint64_t total_bytes; uint64_t bytes_used; uint64_t root_dir_objectid; uint64_t num_devices; uint32_t sectorsize; uint32_t nodesize; uint32_t leafsize; uint32_t stripesize; uint32_t sys_chunk_array_size; uint64_t chunk_root_generation; uint64_t compat_flags; uint64_t compat_ro_flags; uint64_t incompat_flags; uint16_t csum_type; uint8_t root_level; uint8_t chunk_root_level; uint8_t log_root_level; struct btrfs_dev_item dev_item; uint8_t label[BTRFS_LABEL_SIZE]; // LABEL // ... } PACKED; int FAST_FUNC volume_id_probe_btrfs(struct volume_id *id /*,uint64_t off*/) { // btrfs has superblocks at 64K, 64M and 256G // minimum btrfs size is 256M // so we never step out the device if we analyze // the first and the second superblocks struct btrfs_super_block *sb; unsigned off = 64; while (off < 64*1024*1024) { off *= 1024; dbg("btrfs: probing at offset 0x%x", off); sb = volume_id_get_buffer(id, off, sizeof(*sb)); if (sb == NULL) return -1; if (memcmp(sb->magic, BTRFS_MAGIC, 8) != 0) return -1; } // N.B.: btrfs natively supports 256 (>VOLUME_ID_LABEL_SIZE) size labels volume_id_set_label_string(id, sb->label, VOLUME_ID_LABEL_SIZE); volume_id_set_uuid(id, sb->fsid, UUID_DCE); IF_FEATURE_BLKID_TYPE(id->type = "btrfs";) return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/sysv.c����������������������������������������������������������0000644�0000000�0000000�00000006747�12263563520�017255� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_SYSV) += sysv.o //config: //config:config FEATURE_VOLUMEID_SYSV //config: bool "sysv filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" #define SYSV_NICINOD 100 #define SYSV_NICFREE 50 struct sysv_super { uint16_t s_isize; uint16_t s_pad0; uint32_t s_fsize; uint16_t s_nfree; uint16_t s_pad1; uint32_t s_free[SYSV_NICFREE]; uint16_t s_ninode; uint16_t s_pad2; uint16_t s_inode[SYSV_NICINOD]; uint8_t s_flock; uint8_t s_ilock; uint8_t s_fmod; uint8_t s_ronly; uint32_t s_time; uint16_t s_dinfo[4]; uint32_t s_tfree; uint16_t s_tinode; uint16_t s_pad3; uint8_t s_fname[6]; uint8_t s_fpack[6]; uint32_t s_fill[12]; uint32_t s_state; uint32_t s_magic; uint32_t s_type; } PACKED; #define XENIX_NICINOD 100 #define XENIX_NICFREE 100 struct xenix_super { uint16_t s_isize; uint32_t s_fsize; uint16_t s_nfree; uint32_t s_free[XENIX_NICFREE]; uint16_t s_ninode; uint16_t s_inode[XENIX_NICINOD]; uint8_t s_flock; uint8_t s_ilock; uint8_t s_fmod; uint8_t s_ronly; uint32_t s_time; uint32_t s_tfree; uint16_t s_tinode; uint16_t s_dinfo[4]; uint8_t s_fname[6]; uint8_t s_fpack[6]; uint8_t s_clean; uint8_t s_fill[371]; uint32_t s_magic; uint32_t s_type; } PACKED; #define SYSV_SUPERBLOCK_BLOCK 0x01 #define SYSV_MAGIC 0xfd187e20 #define XENIX_SUPERBLOCK_BLOCK 0x18 #define XENIX_MAGIC 0x2b5544 #define SYSV_MAX_BLOCKSIZE 0x800 int FAST_FUNC volume_id_probe_sysv(struct volume_id *id /*,uint64_t off*/) { #define off ((uint64_t)0) struct sysv_super *vs; struct xenix_super *xs; unsigned boff; dbg("probing at offset 0x%llx", (unsigned long long) off); for (boff = 0x200; boff <= SYSV_MAX_BLOCKSIZE; boff <<= 1) { vs = volume_id_get_buffer(id, off + (boff * SYSV_SUPERBLOCK_BLOCK), 0x200); if (vs == NULL) return -1; if (vs->s_magic == cpu_to_le32(SYSV_MAGIC) || vs->s_magic == cpu_to_be32(SYSV_MAGIC)) { // volume_id_set_label_raw(id, vs->s_fname, 6); volume_id_set_label_string(id, vs->s_fname, 6); IF_FEATURE_BLKID_TYPE(id->type = "sysv"); goto found; } } for (boff = 0x200; boff <= SYSV_MAX_BLOCKSIZE; boff <<= 1) { xs = volume_id_get_buffer(id, off + (boff + XENIX_SUPERBLOCK_BLOCK), 0x200); if (xs == NULL) return -1; if (xs->s_magic == cpu_to_le32(XENIX_MAGIC) || xs->s_magic == cpu_to_be32(XENIX_MAGIC)) { // volume_id_set_label_raw(id, xs->s_fname, 6); volume_id_set_label_string(id, xs->s_fname, 6); IF_FEATURE_BLKID_TYPE(id->type = "xenix";) goto found; } } return -1; found: // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); return 0; } �������������������������busybox-1.22.1/util-linux/volume_id/linux_swap.c����������������������������������������������������0000644�0000000�0000000�00000005020�12263563520�020421� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_LINUXSWAP) += linux_swap.o //config: //config:config FEATURE_VOLUMEID_LINUXSWAP //config: bool "linux swap filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" struct swap_header_v1_2 { uint8_t bootbits[1024]; uint32_t version; uint32_t last_page; uint32_t nr_badpages; uint8_t uuid[16]; uint8_t volume_name[16]; } PACKED; #define LARGEST_PAGESIZE 0x4000 int FAST_FUNC volume_id_probe_linux_swap(struct volume_id *id /*,uint64_t off*/) { #define off ((uint64_t)0) struct swap_header_v1_2 *sw; const uint8_t *buf; unsigned page; dbg("probing at offset 0x%llx", (unsigned long long) off); /* the swap signature is at the end of the PAGE_SIZE */ for (page = 0x1000; page <= LARGEST_PAGESIZE; page <<= 1) { buf = volume_id_get_buffer(id, off + page-10, 10); if (buf == NULL) return -1; if (memcmp(buf, "SWAP-SPACE", 10) == 0) { // id->type_version[0] = '1'; // id->type_version[1] = '\0'; goto found; } if (memcmp(buf, "SWAPSPACE2", 10) == 0 || memcmp(buf, "S1SUSPEND", 9) == 0 || memcmp(buf, "S2SUSPEND", 9) == 0 || memcmp(buf, "ULSUSPEND", 9) == 0 ) { sw = volume_id_get_buffer(id, off, sizeof(struct swap_header_v1_2)); if (sw == NULL) return -1; // id->type_version[0] = '2'; // id->type_version[1] = '\0'; // volume_id_set_label_raw(id, sw->volume_name, 16); volume_id_set_label_string(id, sw->volume_name, 16); volume_id_set_uuid(id, sw->uuid, UUID_DCE); goto found; } } return -1; found: // volume_id_set_usage(id, VOLUME_ID_OTHER); IF_FEATURE_BLKID_TYPE(id->type = "swap";) return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/unused_minix.c��������������������������������������������������0000644�0000000�0000000�00000004252�12263563520�020745� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_MINIX) += minix.o //config: //config:### config FEATURE_VOLUMEID_MINIX //config:### bool "minix filesystem" //config:### default y //config:### depends on VOLUMEID //config:### help //config:### TODO //config: #include "volume_id_internal.h" struct minix_super_block { uint16_t s_ninodes; uint16_t s_nzones; uint16_t s_imap_blocks; uint16_t s_zmap_blocks; uint16_t s_firstdatazone; uint16_t s_log_zone_size; uint32_t s_max_size; uint16_t s_magic; uint16_t s_state; uint32_t s_zones; } PACKED; #define MINIX_SUPERBLOCK_OFFSET 0x400 int FAST_FUNC volume_id_probe_minix(struct volume_id *id, uint64_t off) { struct minix_super_block *ms; dbg("probing at offset 0x%llx", (unsigned long long) off); ms = volume_id_get_buffer(id, off + MINIX_SUPERBLOCK_OFFSET, 0x200); if (ms == NULL) return -1; if (ms->s_magic == cpu_to_le16(0x137f)) { // id->type_version[0] = '1'; goto found; } if (ms->s_magic == cpu_to_le16(0x1387)) { // id->type_version[0] = '1'; goto found; } if (ms->s_magic == cpu_to_le16(0x2468)) { // id->type_version[0] = '2'; goto found; } if (ms->s_magic == cpu_to_le16(0x2478)) { // id->type_version[0] = '2'; goto found; } return -1; found: // id->type_version[1] = '\0'; // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); // id->type = "minix"; return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/unused_highpoint.c����������������������������������������������0000644�0000000�0000000�00000005103�12263563520�021606� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_HIGHPOINTRAID) += highpoint.o //config: //config:### config FEATURE_VOLUMEID_HIGHPOINTRAID //config:### bool "highpoint raid" //config:### default y //config:### depends on VOLUMEID //config:### help //config:### TODO //config: #include "volume_id_internal.h" struct hpt37x_meta { uint8_t filler1[32]; uint32_t magic; } PACKED; struct hpt45x_meta { uint32_t magic; } PACKED; #define HPT37X_CONFIG_OFF 0x1200 #define HPT37X_MAGIC_OK 0x5a7816f0 #define HPT37X_MAGIC_BAD 0x5a7816fd #define HPT45X_MAGIC_OK 0x5a7816f3 #define HPT45X_MAGIC_BAD 0x5a7816fd int FAST_FUNC volume_id_probe_highpoint_37x_raid(struct volume_id *id, uint64_t off) { struct hpt37x_meta *hpt; uint32_t magic; dbg("probing at offset 0x%llx", (unsigned long long) off); hpt = volume_id_get_buffer(id, off + HPT37X_CONFIG_OFF, 0x200); if (hpt == NULL) return -1; magic = hpt->magic; if (magic != cpu_to_le32(HPT37X_MAGIC_OK) && magic != cpu_to_le32(HPT37X_MAGIC_BAD)) return -1; // volume_id_set_usage(id, VOLUME_ID_RAID); // id->type = "highpoint_raid_member"; return 0; } int FAST_FUNC volume_id_probe_highpoint_45x_raid(struct volume_id *id, uint64_t off, uint64_t size) { struct hpt45x_meta *hpt; uint64_t meta_off; uint32_t magic; dbg("probing at offset 0x%llx, size 0x%llx", (unsigned long long) off, (unsigned long long) size); if (size < 0x10000) return -1; meta_off = ((size / 0x200)-11) * 0x200; hpt = volume_id_get_buffer(id, off + meta_off, 0x200); if (hpt == NULL) return -1; magic = hpt->magic; if (magic != cpu_to_le32(HPT45X_MAGIC_OK) && magic != cpu_to_le32(HPT45X_MAGIC_BAD)) return -1; // volume_id_set_usage(id, VOLUME_ID_RAID); // id->type = "highpoint_raid_member"; return 0; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/cramfs.c��������������������������������������������������������0000644�0000000�0000000�00000003573�12263563520�017516� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_CRAMFS) += cramfs.o //config: //config:config FEATURE_VOLUMEID_CRAMFS //config: bool "cramfs filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" struct cramfs_super { uint32_t magic; uint32_t size; uint32_t flags; uint32_t future; uint8_t signature[16]; struct cramfs_info { uint32_t crc; uint32_t edition; uint32_t blocks; uint32_t files; } PACKED info; uint8_t name[16]; } PACKED; int FAST_FUNC volume_id_probe_cramfs(struct volume_id *id /*,uint64_t off*/) { #define off ((uint64_t)0) struct cramfs_super *cs; dbg("probing at offset 0x%llx", (unsigned long long) off); cs = volume_id_get_buffer(id, off, 0x200); if (cs == NULL) return -1; if (cs->magic == cpu_to_be32(0x453dcd28)) { // volume_id_set_label_raw(id, cs->name, 16); volume_id_set_label_string(id, cs->name, 16); // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); IF_FEATURE_BLKID_TYPE(id->type = "cramfs";) return 0; } return -1; } �������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/util.c����������������������������������������������������������0000644�0000000�0000000�00000015205�12263563520�017213� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "volume_id_internal.h" void volume_id_set_unicode16(char *str, size_t len, const uint8_t *buf, enum endian endianess, size_t count) { unsigned i, j; unsigned c; j = 0; for (i = 0; i + 2 <= count; i += 2) { if (endianess == LE) c = (buf[i+1] << 8) | buf[i]; else c = (buf[i] << 8) | buf[i+1]; if (c == 0) break; if (j+1 >= len) break; if (c < 0x80) { /* 0xxxxxxx */ } else { uint8_t topbits = 0xc0; if (j+2 >= len) break; if (c < 0x800) { /* 110yyyxx 10xxxxxx */ } else { if (j+3 >= len) break; /* 1110yyyy 10yyyyxx 10xxxxxx */ str[j++] = (uint8_t) (0xe0 | (c >> 12)); topbits = 0x80; } str[j++] = (uint8_t) (topbits | ((c >> 6) & 0x3f)); c = 0x80 | (c & 0x3f); } str[j++] = (uint8_t) c; } str[j] = '\0'; } #ifdef UNUSED static const char *usage_to_string(enum volume_id_usage usage_id) { switch (usage_id) { case VOLUME_ID_FILESYSTEM: return "filesystem"; case VOLUME_ID_PARTITIONTABLE: return "partitiontable"; case VOLUME_ID_OTHER: return "other"; case VOLUME_ID_RAID: return "raid"; case VOLUME_ID_DISKLABEL: return "disklabel"; case VOLUME_ID_CRYPTO: return "crypto"; case VOLUME_ID_UNPROBED: return "unprobed"; case VOLUME_ID_UNUSED: return "unused"; } return NULL; } void volume_id_set_usage_part(struct volume_id_partition *part, enum volume_id_usage usage_id) { part->usage_id = usage_id; part->usage = usage_to_string(usage_id); } void volume_id_set_usage(struct volume_id *id, enum volume_id_usage usage_id) { id->usage_id = usage_id; id->usage = usage_to_string(usage_id); } void volume_id_set_label_raw(struct volume_id *id, const uint8_t *buf, size_t count) { memcpy(id->label_raw, buf, count); id->label_raw_len = count; } #endif #ifdef NOT_NEEDED static size_t strnlen(const char *s, size_t maxlen) { size_t i; if (!maxlen) return 0; if (!s) return 0; for (i = 0; *s && i < maxlen; ++s) ++i; return i; } #endif void volume_id_set_label_string(struct volume_id *id, const uint8_t *buf, size_t count) { unsigned i; memcpy(id->label, buf, count); /* remove trailing whitespace */ i = strnlen(id->label, count); while (i--) { if (!isspace(id->label[i])) break; } id->label[i+1] = '\0'; } void volume_id_set_label_unicode16(struct volume_id *id, const uint8_t *buf, enum endian endianess, size_t count) { volume_id_set_unicode16(id->label, sizeof(id->label), buf, endianess, count); } void volume_id_set_uuid(struct volume_id *id, const uint8_t *buf, enum uuid_format format) { unsigned i; unsigned count = (format == UUID_DCE_STRING ? VOLUME_ID_UUID_SIZE : 4 << format); // memcpy(id->uuid_raw, buf, count); // id->uuid_raw_len = count; /* if set, create string in the same format, the native platform uses */ for (i = 0; i < count; i++) if (buf[i] != 0) goto set; return; /* all bytes are zero, leave it empty ("") */ set: switch (format) { case UUID_DOS: sprintf(id->uuid, "%02X%02X-%02X%02X", buf[3], buf[2], buf[1], buf[0]); break; case UUID_NTFS: sprintf(id->uuid, "%02X%02X%02X%02X%02X%02X%02X%02X", buf[7], buf[6], buf[5], buf[4], buf[3], buf[2], buf[1], buf[0]); break; case UUID_DCE: sprintf(id->uuid, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); break; case UUID_DCE_STRING: memcpy(id->uuid, buf, count); id->uuid[count] = '\0'; break; } } /* Do not use xlseek here. With it, single corrupted filesystem * may result in attempt to seek past device -> exit. * It's better to ignore such fs and continue. */ void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len) { uint8_t *dst; unsigned small_off; ssize_t read_len; dbg("get buffer off 0x%llx(%llu), len 0x%zx", (unsigned long long) off, (unsigned long long) off, len); /* check if requested area fits in superblock buffer */ if (off + len <= SB_BUFFER_SIZE /* && off <= SB_BUFFER_SIZE - want this paranoid overflow check? */ ) { if (id->sbbuf == NULL) { id->sbbuf = xmalloc(SB_BUFFER_SIZE); } small_off = off; dst = id->sbbuf; /* check if we need to read */ len += off; if (len <= id->sbbuf_len) goto ret; /* we already have it */ dbg("read sbbuf len:0x%x", (unsigned) len); id->sbbuf_len = len; off = 0; goto do_read; } if (len > SEEK_BUFFER_SIZE) { dbg("seek buffer too small %d", SEEK_BUFFER_SIZE); return NULL; } dst = id->seekbuf; /* check if we need to read */ if ((off >= id->seekbuf_off) && ((off + len) <= (id->seekbuf_off + id->seekbuf_len)) ) { small_off = off - id->seekbuf_off; /* can't overflow */ goto ret; /* we already have it */ } id->seekbuf_off = off; id->seekbuf_len = len; id->seekbuf = xrealloc(id->seekbuf, len); small_off = 0; dst = id->seekbuf; dbg("read seekbuf off:0x%llx len:0x%zx", (unsigned long long) off, len); do_read: if (lseek(id->fd, off, SEEK_SET) != off) { dbg("seek(0x%llx) failed", (unsigned long long) off); goto err; } read_len = full_read(id->fd, dst, len); if (read_len != len) { dbg("requested 0x%x bytes, got 0x%x bytes", (unsigned) len, (unsigned) read_len); err: /* No filesystem can be this tiny. It's most likely * non-associated loop device, empty drive and so on. * Flag it, making it possible to short circuit future * accesses. Rationale: * users complained of slow blkid due to empty floppy drives. */ if (off < 64*1024) id->error = 1; /* id->seekbuf_len or id->sbbuf_len is wrong now! Fixing. */ volume_id_free_buffer(id); return NULL; } ret: return dst + small_off; } void volume_id_free_buffer(struct volume_id *id) { free(id->sbbuf); id->sbbuf = NULL; id->sbbuf_len = 0; free(id->seekbuf); id->seekbuf = NULL; id->seekbuf_len = 0; id->seekbuf_off = 0; /* paranoia */ } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/unused_lvm.c����������������������������������������������������0000644�0000000�0000000�00000004626�12263563520�020424� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_LVM) += lvm.o //config: //config:### config FEATURE_VOLUMEID_LVM //config:### bool "lvm" //config:### default y //config:### depends on VOLUMEID //config:### help //config:### TODO //config: #include "volume_id_internal.h" struct lvm1_super_block { uint8_t id[2]; } PACKED; struct lvm2_super_block { uint8_t id[8]; uint64_t sector_xl; uint32_t crc_xl; uint32_t offset_xl; uint8_t type[8]; } PACKED; #define LVM1_SB_OFF 0x400 int FAST_FUNC volume_id_probe_lvm1(struct volume_id *id, uint64_t off) { struct lvm1_super_block *lvm; dbg("probing at offset 0x%llx", (unsigned long long) off); lvm = volume_id_get_buffer(id, off + LVM1_SB_OFF, 0x800); if (lvm == NULL) return -1; if (lvm->id[0] != 'H' || lvm->id[1] != 'M') return -1; // volume_id_set_usage(id, VOLUME_ID_RAID); // id->type = "LVM1_member"; return 0; } #define LVM2_LABEL_ID "LABELONE" #define LVM2LABEL_SCAN_SECTORS 4 int FAST_FUNC volume_id_probe_lvm2(struct volume_id *id, uint64_t off) { const uint8_t *buf; unsigned soff; struct lvm2_super_block *lvm; dbg("probing at offset 0x%llx", (unsigned long long) off); buf = volume_id_get_buffer(id, off, LVM2LABEL_SCAN_SECTORS * 0x200); if (buf == NULL) return -1; for (soff = 0; soff < LVM2LABEL_SCAN_SECTORS * 0x200; soff += 0x200) { lvm = (struct lvm2_super_block *) &buf[soff]; if (memcmp(lvm->id, LVM2_LABEL_ID, 8) == 0) goto found; } return -1; found: // memcpy(id->type_version, lvm->type, 8); // volume_id_set_usage(id, VOLUME_ID_RAID); // id->type = "LVM2_member"; return 0; } ����������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/unused_ufs.c����������������������������������������������������0000644�0000000�0000000�00000012236�12263563520�020417� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_UFS) += ufs.o //config: //config:### config FEATURE_VOLUMEID_UFS //config:### bool "ufs filesystem" //config:### default y //config:### depends on VOLUMEID //config:### help //config:### TODO //config: #include "volume_id_internal.h" struct ufs_super_block { uint32_t fs_link; uint32_t fs_rlink; uint32_t fs_sblkno; uint32_t fs_cblkno; uint32_t fs_iblkno; uint32_t fs_dblkno; uint32_t fs_cgoffset; uint32_t fs_cgmask; uint32_t fs_time; uint32_t fs_size; uint32_t fs_dsize; uint32_t fs_ncg; uint32_t fs_bsize; uint32_t fs_fsize; uint32_t fs_frag; uint32_t fs_minfree; uint32_t fs_rotdelay; uint32_t fs_rps; uint32_t fs_bmask; uint32_t fs_fmask; uint32_t fs_bshift; uint32_t fs_fshift; uint32_t fs_maxcontig; uint32_t fs_maxbpg; uint32_t fs_fragshift; uint32_t fs_fsbtodb; uint32_t fs_sbsize; uint32_t fs_csmask; uint32_t fs_csshift; uint32_t fs_nindir; uint32_t fs_inopb; uint32_t fs_nspf; uint32_t fs_optim; uint32_t fs_npsect_state; uint32_t fs_interleave; uint32_t fs_trackskew; uint32_t fs_id[2]; uint32_t fs_csaddr; uint32_t fs_cssize; uint32_t fs_cgsize; uint32_t fs_ntrak; uint32_t fs_nsect; uint32_t fs_spc; uint32_t fs_ncyl; uint32_t fs_cpg; uint32_t fs_ipg; uint32_t fs_fpg; struct ufs_csum { uint32_t cs_ndir; uint32_t cs_nbfree; uint32_t cs_nifree; uint32_t cs_nffree; } PACKED fs_cstotal; int8_t fs_fmod; int8_t fs_clean; int8_t fs_ronly; int8_t fs_flags; union { struct { int8_t fs_fsmnt[512]; uint32_t fs_cgrotor; uint32_t fs_csp[31]; uint32_t fs_maxcluster; uint32_t fs_cpc; uint16_t fs_opostbl[16][8]; } PACKED fs_u1; struct { int8_t fs_fsmnt[468]; uint8_t fs_volname[32]; uint64_t fs_swuid; int32_t fs_pad; uint32_t fs_cgrotor; uint32_t fs_ocsp[28]; uint32_t fs_contigdirs; uint32_t fs_csp; uint32_t fs_maxcluster; uint32_t fs_active; int32_t fs_old_cpc; int32_t fs_maxbsize; int64_t fs_sparecon64[17]; int64_t fs_sblockloc; struct ufs2_csum_total { uint64_t cs_ndir; uint64_t cs_nbfree; uint64_t cs_nifree; uint64_t cs_nffree; uint64_t cs_numclusters; uint64_t cs_spare[3]; } PACKED fs_cstotal; struct ufs_timeval { int32_t tv_sec; int32_t tv_usec; } PACKED fs_time; int64_t fs_size; int64_t fs_dsize; uint64_t fs_csaddr; int64_t fs_pendingblocks; int32_t fs_pendinginodes; } PACKED fs_u2; } fs_u11; union { struct { int32_t fs_sparecon[53]; int32_t fs_reclaim; int32_t fs_sparecon2[1]; int32_t fs_state; uint32_t fs_qbmask[2]; uint32_t fs_qfmask[2]; } PACKED fs_sun; struct { int32_t fs_sparecon[53]; int32_t fs_reclaim; int32_t fs_sparecon2[1]; uint32_t fs_npsect; uint32_t fs_qbmask[2]; uint32_t fs_qfmask[2]; } PACKED fs_sunx86; struct { int32_t fs_sparecon[50]; int32_t fs_contigsumsize; int32_t fs_maxsymlinklen; int32_t fs_inodefmt; uint32_t fs_maxfilesize[2]; uint32_t fs_qbmask[2]; uint32_t fs_qfmask[2]; int32_t fs_state; } PACKED fs_44; } fs_u2; int32_t fs_postblformat; int32_t fs_nrpos; int32_t fs_postbloff; int32_t fs_rotbloff; uint32_t fs_magic; uint8_t fs_space[1]; } PACKED; #define UFS_MAGIC 0x00011954 #define UFS2_MAGIC 0x19540119 #define UFS_MAGIC_FEA 0x00195612 #define UFS_MAGIC_LFN 0x00095014 int FAST_FUNC volume_id_probe_ufs(struct volume_id *id, uint64_t off) { static const short offsets[] = { 0, 8, 64, 256 }; uint32_t magic; unsigned i; struct ufs_super_block *ufs; dbg("probing at offset 0x%llx", (unsigned long long) off); for (i = 0; i < ARRAY_SIZE(offsets); i++) { ufs = volume_id_get_buffer(id, off + (offsets[i] * 0x400), 0x800); if (ufs == NULL) return -1; dbg("offset 0x%x", offsets[i] * 0x400); magic = ufs->fs_magic; if ((magic == cpu_to_be32(UFS_MAGIC)) || (magic == cpu_to_be32(UFS2_MAGIC)) || (magic == cpu_to_be32(UFS_MAGIC_FEA)) || (magic == cpu_to_be32(UFS_MAGIC_LFN)) ) { dbg("magic 0x%08x(be)", magic); goto found; } if ((magic == cpu_to_le32(UFS_MAGIC)) || (magic == cpu_to_le32(UFS2_MAGIC)) || (magic == cpu_to_le32(UFS_MAGIC_FEA)) || (magic == cpu_to_le32(UFS_MAGIC_LFN)) ) { dbg("magic 0x%08x(le)", magic); goto found; } } return -1; found: // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); // id->type = "ufs"; return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/unused_msdos.c��������������������������������������������������0000644�0000000�0000000�00000012533�12263563520�020747� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_MSDOS) += msdos.o //config: //config:### config FEATURE_VOLUMEID_MSDOS //config:### bool "msdos filesystem" //config:### default y //config:### depends on VOLUMEID //config:### help //config:### TODO //config: #include "volume_id_internal.h" struct msdos_partition_entry { uint8_t boot_ind; uint8_t head; uint8_t sector; uint8_t cyl; uint8_t sys_ind; uint8_t end_head; uint8_t end_sector; uint8_t end_cyl; uint32_t start_sect; uint32_t nr_sects; } PACKED; #define MSDOS_PARTTABLE_OFFSET 0x1be #define MSDOS_SIG_OFF 0x1fe #define BSIZE 0x200 #define DOS_EXTENDED_PARTITION 0x05 #define LINUX_EXTENDED_PARTITION 0x85 #define WIN98_EXTENDED_PARTITION 0x0f #define LINUX_RAID_PARTITION 0xfd #define is_extended(type) \ (type == DOS_EXTENDED_PARTITION || \ type == WIN98_EXTENDED_PARTITION || \ type == LINUX_EXTENDED_PARTITION) #define is_raid(type) \ (type == LINUX_RAID_PARTITION) int FAST_FUNC volume_id_probe_msdos_part_table(struct volume_id *id, uint64_t off) { const uint8_t *buf; int i; uint64_t poff; uint64_t plen; uint64_t extended = 0; uint64_t current; uint64_t next; int limit; int empty = 1; struct msdos_partition_entry *part; struct volume_id_partition *p; dbg("probing at offset 0x%llx", (unsigned long long) off); buf = volume_id_get_buffer(id, off, 0x200); if (buf == NULL) return -1; if (buf[MSDOS_SIG_OFF] != 0x55 || buf[MSDOS_SIG_OFF + 1] != 0xaa) return -1; /* check flags on all entries for a valid partition table */ part = (struct msdos_partition_entry*) &buf[MSDOS_PARTTABLE_OFFSET]; for (i = 0; i < 4; i++) { if (part[i].boot_ind != 0 && part[i].boot_ind != 0x80 ) { return -1; } if (part[i].nr_sects != 0) empty = 0; } if (empty == 1) return -1; if (id->partitions != NULL) free(id->partitions); id->partitions = xzalloc(VOLUME_ID_PARTITIONS_MAX * sizeof(struct volume_id_partition)); for (i = 0; i < 4; i++) { poff = (uint64_t) le32_to_cpu(part[i].start_sect) * BSIZE; plen = (uint64_t) le32_to_cpu(part[i].nr_sects) * BSIZE; if (plen == 0) continue; p = &id->partitions[i]; // p->pt_type_raw = part[i].sys_ind; if (is_extended(part[i].sys_ind)) { dbg("found extended partition at 0x%llx", (unsigned long long) poff); // volume_id_set_usage_part(p, VOLUME_ID_PARTITIONTABLE); // p->type = "msdos_extended_partition"; if (extended == 0) extended = off + poff; } else { dbg("found 0x%x data partition at 0x%llx, len 0x%llx", part[i].sys_ind, (unsigned long long) poff, (unsigned long long) plen); // if (is_raid(part[i].sys_ind)) // volume_id_set_usage_part(p, VOLUME_ID_RAID); // else // volume_id_set_usage_part(p, VOLUME_ID_UNPROBED); } // p->pt_off = off + poff; // p->pt_len = plen; id->partition_count = i+1; } next = extended; current = extended; limit = 50; /* follow extended partition chain and add data partitions */ while (next != 0) { if (limit-- == 0) { dbg("extended chain limit reached"); break; } buf = volume_id_get_buffer(id, current, 0x200); if (buf == NULL) break; part = (struct msdos_partition_entry*) &buf[MSDOS_PARTTABLE_OFFSET]; if (buf[MSDOS_SIG_OFF] != 0x55 || buf[MSDOS_SIG_OFF + 1] != 0xaa) break; next = 0; for (i = 0; i < 4; i++) { poff = (uint64_t) le32_to_cpu(part[i].start_sect) * BSIZE; plen = (uint64_t) le32_to_cpu(part[i].nr_sects) * BSIZE; if (plen == 0) continue; if (is_extended(part[i].sys_ind)) { dbg("found extended partition at 0x%llx", (unsigned long long) poff); if (next == 0) next = extended + poff; } else { dbg("found 0x%x data partition at 0x%llx, len 0x%llx", part[i].sys_ind, (unsigned long long) poff, (unsigned long long) plen); /* we always start at the 5th entry */ // while (id->partition_count < 4) // volume_id_set_usage_part(&id->partitions[id->partition_count++], VOLUME_ID_UNUSED); if (id->partition_count < 4) id->partition_count = 4; // p = &id->partitions[id->partition_count]; // if (is_raid(part[i].sys_ind)) // volume_id_set_usage_part(p, VOLUME_ID_RAID); // else // volume_id_set_usage_part(p, VOLUME_ID_UNPROBED); // p->pt_off = current + poff; // p->pt_len = plen; id->partition_count++; // p->pt_type_raw = part[i].sys_ind; if (id->partition_count >= VOLUME_ID_PARTITIONS_MAX) { dbg("too many partitions"); next = 0; } } } current = next; } // volume_id_set_usage(id, VOLUME_ID_PARTITIONTABLE); // id->type = "msdos_partition_table"; return 0; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/romfs.c���������������������������������������������������������0000644�0000000�0000000�00000003442�12263563520�017364� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_ROMFS) += romfs.o //config: //config:config FEATURE_VOLUMEID_ROMFS //config: bool "romfs filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" struct romfs_super { uint8_t magic[8]; uint32_t size; uint32_t checksum; uint8_t name[]; } PACKED; int FAST_FUNC volume_id_probe_romfs(struct volume_id *id /*,uint64_t off*/) { #define off ((uint64_t)0) struct romfs_super *rfs; dbg("probing at offset 0x%llx", (unsigned long long) off); rfs = volume_id_get_buffer(id, off, 0x200); if (rfs == NULL) return -1; if (memcmp(rfs->magic, "-rom1fs-", 4) == 0) { size_t len = strlen((char *)rfs->name); if (len) { // volume_id_set_label_raw(id, rfs->name, len); volume_id_set_label_string(id, rfs->name, len); } // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); IF_FEATURE_BLKID_TYPE(id->type = "romfs";) return 0; } return -1; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/iso9660.c�������������������������������������������������������0000644�0000000�0000000�00000007130�12263563520�017353� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_ISO9660) += iso9660.o //config: //config:config FEATURE_VOLUMEID_ISO9660 //config: bool "iso9660 filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" #define ISO_SUPERBLOCK_OFFSET 0x8000 #define ISO_SECTOR_SIZE 0x800 #define ISO_VD_OFFSET (ISO_SUPERBLOCK_OFFSET + ISO_SECTOR_SIZE) #define ISO_VD_PRIMARY 0x1 #define ISO_VD_SUPPLEMENTARY 0x2 #define ISO_VD_END 0xff #define ISO_VD_MAX 16 struct iso_volume_descriptor { uint8_t vd_type; uint8_t vd_id[5]; uint8_t vd_version; uint8_t flags; uint8_t system_id[32]; uint8_t volume_id[32]; uint8_t unused[8]; uint8_t space_size[8]; uint8_t escape_sequences[8]; } PACKED; struct high_sierra_volume_descriptor { uint8_t foo[8]; uint8_t type; uint8_t id[4]; uint8_t version; } PACKED; int FAST_FUNC volume_id_probe_iso9660(struct volume_id *id /*,uint64_t off*/) { #define off ((uint64_t)0) uint8_t *buf; struct iso_volume_descriptor *is; struct high_sierra_volume_descriptor *hs; dbg("probing at offset 0x%llx", (unsigned long long) off); buf = volume_id_get_buffer(id, off + ISO_SUPERBLOCK_OFFSET, 0x200); if (buf == NULL) return -1; is = (struct iso_volume_descriptor *) buf; if (memcmp(is->vd_id, "CD001", 5) == 0) { int vd_offset; int i; dbg("read label from PVD"); // volume_id_set_label_raw(id, is->volume_id, 32); volume_id_set_label_string(id, is->volume_id, 32); dbg("looking for SVDs"); vd_offset = ISO_VD_OFFSET; for (i = 0; i < ISO_VD_MAX; i++) { uint8_t svd_label[64]; is = volume_id_get_buffer(id, off + vd_offset, 0x200); if (is == NULL || is->vd_type == ISO_VD_END) break; if (is->vd_type != ISO_VD_SUPPLEMENTARY) continue; dbg("found SVD at offset 0x%llx", (unsigned long long) (off + vd_offset)); if (memcmp(is->escape_sequences, "%/@", 3) == 0 || memcmp(is->escape_sequences, "%/C", 3) == 0 || memcmp(is->escape_sequences, "%/E", 3) == 0 ) { dbg("Joliet extension found"); volume_id_set_unicode16((char *)svd_label, sizeof(svd_label), is->volume_id, BE, 32); if (memcmp(id->label, svd_label, 16) == 0) { dbg("SVD label is identical, use the possibly longer PVD one"); break; } // volume_id_set_label_raw(id, is->volume_id, 32); volume_id_set_label_string(id, svd_label, 32); // strcpy(id->type_version, "Joliet Extension"); goto found; } vd_offset += ISO_SECTOR_SIZE; } goto found; } hs = (struct high_sierra_volume_descriptor *) buf; if (memcmp(hs->id, "CDROM", 5) == 0) { // strcpy(id->type_version, "High Sierra"); goto found; } return -1; found: // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); IF_FEATURE_BLKID_TYPE(id->type = "iso9660";) return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/unused_silicon_raid.c�������������������������������������������0000644�0000000�0000000�00000004530�12263563520�022257� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_SILICONRAID) += silicon_raid.o //config: //config:### config FEATURE_VOLUMEID_SILICONRAID //config:### bool "silicon raid" //config:### default y //config:### depends on VOLUMEID //config:### help //config:### TODO //config: #include "volume_id_internal.h" struct silicon_meta { uint8_t unknown0[0x2E]; uint8_t ascii_version[0x36 - 0x2E]; uint8_t diskname[0x56 - 0x36]; uint8_t unknown1[0x60 - 0x56]; uint32_t magic; uint32_t unknown1a[0x6C - 0x64]; uint32_t array_sectors_low; uint32_t array_sectors_high; uint8_t unknown2[0x78 - 0x74]; uint32_t thisdisk_sectors; uint8_t unknown3[0x100 - 0x7C]; uint8_t unknown4[0x104 - 0x100]; uint16_t product_id; uint16_t vendor_id; uint16_t minor_ver; uint16_t major_ver; } PACKED; #define SILICON_MAGIC 0x2F000000 int FAST_FUNC volume_id_probe_silicon_medley_raid(struct volume_id *id, uint64_t off, uint64_t size) { uint64_t meta_off; struct silicon_meta *sil; dbg("probing at offset 0x%llx, size 0x%llx", (unsigned long long) off, (unsigned long long) size); if (size < 0x10000) return -1; meta_off = ((size / 0x200)-1) * 0x200; sil = volume_id_get_buffer(id, off + meta_off, 0x200); if (sil == NULL) return -1; if (sil->magic != cpu_to_le32(SILICON_MAGIC)) return -1; // volume_id_set_usage(id, VOLUME_ID_RAID); // snprintf(id->type_version, sizeof(id->type_version)-1, "%u.%u", // le16_to_cpu(sil->major_ver), le16_to_cpu(sil->minor_ver)); // id->type = "silicon_medley_raid_member"; return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/unused_hpfs.c���������������������������������������������������0000644�0000000�0000000�00000003263�12263563520�020562� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_HPFS) += hpfs.o //config: //config:### config FEATURE_VOLUMEID_HPFS //config:### bool "hpfs filesystem" //config:### default y //config:### depends on VOLUMEID //config:### help //config:### TODO //config: #include "volume_id_internal.h" struct hpfs_super { uint8_t magic[4]; uint8_t version; } PACKED; #define HPFS_SUPERBLOCK_OFFSET 0x2000 int FAST_FUNC volume_id_probe_hpfs(struct volume_id *id, uint64_t off) { struct hpfs_super *hs; dbg("probing at offset 0x%llx", (unsigned long long) off); hs = volume_id_get_buffer(id, off + HPFS_SUPERBLOCK_OFFSET, 0x200); if (hs == NULL) return -1; if (memcmp(hs->magic, "\x49\xe8\x95\xf9", 4) == 0) { // sprintf(id->type_version, "%u", hs->version); // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); // id->type = "hpfs"; return 0; } return -1; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/unused_lsi_raid.c�����������������������������������������������0000644�0000000�0000000�00000003420�12263563520�021403� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_LSIRAID) += lsi_raid.o //config: //config:### config FEATURE_VOLUMEID_LSIRAID //config:### bool "lsi raid" //config:### default y //config:### depends on VOLUMEID //config:### help //config:### TODO //config: #include "volume_id_internal.h" struct lsi_meta { uint8_t sig[6]; } PACKED; #define LSI_SIGNATURE "$XIDE$" int FAST_FUNC volume_id_probe_lsi_mega_raid(struct volume_id *id, uint64_t off, uint64_t size) { uint64_t meta_off; struct lsi_meta *lsi; dbg("probing at offset 0x%llx, size 0x%llx", (unsigned long long) off, (unsigned long long) size); if (size < 0x10000) return -1; meta_off = ((size / 0x200)-1) * 0x200; lsi = volume_id_get_buffer(id, off + meta_off, 0x200); if (lsi == NULL) return -1; if (memcmp(lsi->sig, LSI_SIGNATURE, sizeof(LSI_SIGNATURE)-1) != 0) return -1; // volume_id_set_usage(id, VOLUME_ID_RAID); // id->type = "lsi_mega_raid_member"; return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/xfs.c�����������������������������������������������������������0000644�0000000�0000000�00000003637�12263563520�017044� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_XFS) += xfs.o //config: //config:config FEATURE_VOLUMEID_XFS //config: bool "xfs filesystem" //config: default y //config: depends on VOLUMEID //config: help //config: TODO //config: #include "volume_id_internal.h" struct xfs_super_block { uint8_t magic[4]; uint32_t blocksize; uint64_t dblocks; uint64_t rblocks; uint32_t dummy1[2]; uint8_t uuid[16]; uint32_t dummy2[15]; uint8_t fname[12]; uint32_t dummy3[2]; uint64_t icount; uint64_t ifree; uint64_t fdblocks; } PACKED; int FAST_FUNC volume_id_probe_xfs(struct volume_id *id /*,uint64_t off*/) { #define off ((uint64_t)0) struct xfs_super_block *xs; dbg("probing at offset 0x%llx", (unsigned long long) off); xs = volume_id_get_buffer(id, off, 0x200); if (xs == NULL) return -1; if (memcmp(xs->magic, "XFSB", 4) != 0) return -1; // volume_id_set_label_raw(id, xs->fname, 12); volume_id_set_label_string(id, xs->fname, 12); volume_id_set_uuid(id, xs->uuid, UUID_DCE); // volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); IF_FEATURE_BLKID_TYPE(id->type = "xfs";) return 0; } �������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/unused_nvidia_raid.c��������������������������������������������0000644�0000000�0000000�00000003671�12263563520�022076� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_NVIDIARAID) += nvidia_raid.o //config: //config:### config FEATURE_VOLUMEID_NVIDIARAID //config:### bool "nvidia raid" //config:### default y //config:### depends on VOLUMEID //config:### help //config:### TODO //config: #include "volume_id_internal.h" struct nvidia_meta { uint8_t vendor[8]; uint32_t size; uint32_t chksum; uint16_t version; } PACKED; #define NVIDIA_SIGNATURE "NVIDIA" int FAST_FUNC volume_id_probe_nvidia_raid(struct volume_id *id, uint64_t off, uint64_t size) { uint64_t meta_off; struct nvidia_meta *nv; dbg("probing at offset 0x%llx, size 0x%llx", (unsigned long long) off, (unsigned long long) size); if (size < 0x10000) return -1; meta_off = ((size / 0x200)-2) * 0x200; nv = volume_id_get_buffer(id, off + meta_off, 0x200); if (nv == NULL) return -1; if (memcmp(nv->vendor, NVIDIA_SIGNATURE, sizeof(NVIDIA_SIGNATURE)-1) != 0) return -1; // volume_id_set_usage(id, VOLUME_ID_RAID); // snprintf(id->type_version, sizeof(id->type_version)-1, "%u", le16_to_cpu(nv->version)); // id->type = "nvidia_raid_member"; return 0; } �����������������������������������������������������������������������busybox-1.22.1/util-linux/volume_id/volume_id_internal.h��������������������������������������������0000644�0000000�0000000�00000017054�12263563520�022126� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * volume_id - reads filesystem label and uuid * * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "libbb.h" #include "volume_id.h" PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN #define dbg(...) ((void)0) /* #define dbg(...) bb_error_msg(__VA_ARGS__) */ /* volume_id.h */ #define VOLUME_ID_VERSION 48 #define VOLUME_ID_LABEL_SIZE 64 #define VOLUME_ID_UUID_SIZE 36 #define VOLUME_ID_FORMAT_SIZE 32 #define VOLUME_ID_PARTITIONS_MAX 256 enum volume_id_usage { VOLUME_ID_UNUSED, VOLUME_ID_UNPROBED, VOLUME_ID_OTHER, VOLUME_ID_FILESYSTEM, VOLUME_ID_PARTITIONTABLE, VOLUME_ID_RAID, VOLUME_ID_DISKLABEL, VOLUME_ID_CRYPTO, }; #ifdef UNUSED_PARTITION_CODE struct volume_id_partition { // const char *type; // const char *usage; // smallint usage_id; // uint8_t pt_type_raw; // uint64_t pt_off; // uint64_t pt_len; }; #endif struct volume_id { int fd; // int fd_close:1; int error; size_t sbbuf_len; size_t seekbuf_len; uint8_t *sbbuf; uint8_t *seekbuf; uint64_t seekbuf_off; #ifdef UNUSED_PARTITION_CODE struct volume_id_partition *partitions; size_t partition_count; #endif // uint8_t label_raw[VOLUME_ID_LABEL_SIZE]; // size_t label_raw_len; char label[VOLUME_ID_LABEL_SIZE+1]; // uint8_t uuid_raw[VOLUME_ID_UUID_SIZE]; // size_t uuid_raw_len; /* uuid is stored in ASCII (not binary) form here: */ char uuid[VOLUME_ID_UUID_SIZE+1]; // char type_version[VOLUME_ID_FORMAT_SIZE]; // smallint usage_id; // const char *usage; #if ENABLE_FEATURE_BLKID_TYPE const char *type; #endif }; struct volume_id* FAST_FUNC volume_id_open_node(int fd); int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64_t size); void FAST_FUNC free_volume_id(struct volume_id *id); /* util.h */ /* size of superblock buffer, reiserfs block is at 64k */ #define SB_BUFFER_SIZE 0x11000 /* size of seek buffer, FAT cluster is 32k max */ #define SEEK_BUFFER_SIZE 0x10000 #if BB_LITTLE_ENDIAN # define le16_to_cpu(x) (uint16_t)(x) # define le32_to_cpu(x) (uint32_t)(x) # define le64_to_cpu(x) (uint64_t)(x) # define be16_to_cpu(x) (uint16_t)(bswap_16(x)) # define be32_to_cpu(x) (uint32_t)(bswap_32(x)) # define cpu_to_le16(x) (uint16_t)(x) # define cpu_to_le32(x) (uint32_t)(x) # define cpu_to_be32(x) (uint32_t)(bswap_32(x)) #else # define le16_to_cpu(x) (uint16_t)(bswap_16(x)) # define le32_to_cpu(x) (uint32_t)(bswap_32(x)) # define le64_to_cpu(x) (uint64_t)(bb_bswap_64(x)) # define be16_to_cpu(x) (uint16_t)(x) # define be32_to_cpu(x) (uint32_t)(x) # define cpu_to_le16(x) (uint16_t)(bswap_16(x)) # define cpu_to_le32(x) (uint32_t)(bswap_32(x)) # define cpu_to_be32(x) (uint32_t)(x) #endif /* volume_id_set_uuid(id,buf,fmt) assumes size of uuid buf * by shifting: 4 << fmt, except for fmt == UUID_DCE_STRING. * The constants below should match sizes. */ enum uuid_format { UUID_DOS = 0, /* 4 bytes */ UUID_NTFS = 1, /* 8 bytes */ UUID_DCE = 2, /* 16 bytes */ UUID_DCE_STRING = 3, /* 36 bytes (VOLUME_ID_UUID_SIZE) */ }; enum endian { LE = 0, BE = 1 }; void volume_id_set_unicode16(char *str, size_t len, const uint8_t *buf, enum endian endianess, size_t count); //void volume_id_set_usage(struct volume_id *id, enum volume_id_usage usage_id); //void volume_id_set_usage_part(struct volume_id_partition *part, enum volume_id_usage usage_id); //void volume_id_set_label_raw(struct volume_id *id, const uint8_t *buf, size_t count); void volume_id_set_label_string(struct volume_id *id, const uint8_t *buf, size_t count); void volume_id_set_label_unicode16(struct volume_id *id, const uint8_t *buf, enum endian endianess, size_t count); void volume_id_set_uuid(struct volume_id *id, const uint8_t *buf, enum uuid_format format); void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len); void volume_id_free_buffer(struct volume_id *id); /* Probe routines */ /* RAID */ //int FAST_FUNC volume_id_probe_highpoint_37x_raid(struct volume_id *id /*,uint64_t off*/); //int FAST_FUNC volume_id_probe_highpoint_45x_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size); //int FAST_FUNC volume_id_probe_intel_software_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size); int FAST_FUNC volume_id_probe_linux_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size); //int FAST_FUNC volume_id_probe_lsi_mega_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size); //int FAST_FUNC volume_id_probe_nvidia_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size); //int FAST_FUNC volume_id_probe_promise_fasttrack_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size); //int FAST_FUNC volume_id_probe_silicon_medley_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size); //int FAST_FUNC volume_id_probe_via_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size); //int FAST_FUNC volume_id_probe_lvm1(struct volume_id *id /*,uint64_t off*/); //int FAST_FUNC volume_id_probe_lvm2(struct volume_id *id /*,uint64_t off*/); /* FS */ int FAST_FUNC volume_id_probe_btrfs(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_cramfs(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_ext(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_vfat(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_hfs_hfsplus(struct volume_id *id /*,uint64_t off*/); //int FAST_FUNC volume_id_probe_hpfs(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_iso9660(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_jfs(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_linux_swap(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_luks(struct volume_id *id /*,uint64_t off*/); //int FAST_FUNC volume_id_probe_mac_partition_map(struct volume_id *id /*,uint64_t off*/); //int FAST_FUNC volume_id_probe_minix(struct volume_id *id /*,uint64_t off*/); //int FAST_FUNC volume_id_probe_msdos_part_table(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_f2fs(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_nilfs(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_ntfs(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_exfat(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_ocfs2(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_reiserfs(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_romfs(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_squashfs(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_sysv(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_udf(struct volume_id *id /*,uint64_t off*/); //int FAST_FUNC volume_id_probe_ufs(struct volume_id *id /*,uint64_t off*/); int FAST_FUNC volume_id_probe_xfs(struct volume_id *id /*,uint64_t off*/); POP_SAVED_FUNCTION_VISIBILITY ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/readprofile.c�������������������������������������������������������������0000644�0000000�0000000�00000015632�12263563520�016553� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * readprofile.c - used to read /proc/profile * * Copyright (C) 1994,1996 Alessandro Rubini (rubini@ipvvis.unipv.it) * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* * 1999-02-22 Arkadiusz Mickiewicz <misiek@pld.ORG.PL> * - added Native Language Support * 1999-09-01 Stephane Eranian <eranian@cello.hpl.hp.com> * - 64bit clean patch * 3Feb2001 Andrew Morton <andrewm@uow.edu.au> * - -M option to write profile multiplier. * 2001-11-07 Werner Almesberger <wa@almesberger.net> * - byte order auto-detection and -n option * 2001-11-09 Werner Almesberger <wa@almesberger.net> * - skip step size (index 0) * 2002-03-09 John Levon <moz@compsoc.man.ac.uk> * - make maplineno do something * 2002-11-28 Mads Martin Joergensen + * - also try /boot/System.map-`uname -r` * 2003-04-09 Werner Almesberger <wa@almesberger.net> * - fixed off-by eight error and improved heuristics in byte order detection * 2003-08-12 Nikita Danilov <Nikita@Namesys.COM> * - added -s option; example of use: * "readprofile -s -m /boot/System.map-test | grep __d_lookup | sort -n -k3" * * Taken from util-linux and adapted for busybox by * Paul Mundt <lethal@linux-sh.org>. */ //usage:#define readprofile_trivial_usage //usage: "[OPTIONS]" //usage:#define readprofile_full_usage "\n\n" //usage: " -m mapfile (Default: /boot/System.map)" //usage: "\n -p profile (Default: /proc/profile)" //usage: "\n -M NUM Set the profiling multiplier to NUM" //usage: "\n -i Print only info about the sampling step" //usage: "\n -v Verbose" //usage: "\n -a Print all symbols, even if count is 0" //usage: "\n -b Print individual histogram-bin counts" //usage: "\n -s Print individual counters within functions" //usage: "\n -r Reset all the counters (root only)" //usage: "\n -n Disable byte order auto-detection" #include "libbb.h" #include <sys/utsname.h> #define S_LEN 128 /* These are the defaults */ static const char defaultmap[] ALIGN1 = "/boot/System.map"; static const char defaultpro[] ALIGN1 = "/proc/profile"; int readprofile_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int readprofile_main(int argc UNUSED_PARAM, char **argv) { FILE *map; const char *mapFile, *proFile; unsigned long indx = 1; size_t len; uint64_t add0 = 0; unsigned int step; unsigned int *buf, total, fn_len; unsigned long long fn_add, next_add; /* current and next address */ char fn_name[S_LEN], next_name[S_LEN]; /* current and next name */ char mapline[S_LEN]; char mode[8]; int maplineno = 1; int header_printed; int multiplier = 0; unsigned opt; enum { OPT_M = (1 << 0), OPT_m = (1 << 1), OPT_p = (1 << 2), OPT_n = (1 << 3), OPT_a = (1 << 4), OPT_b = (1 << 5), OPT_s = (1 << 6), OPT_i = (1 << 7), OPT_r = (1 << 8), OPT_v = (1 << 9), }; #define optMult (opt & OPT_M) #define optNative (opt & OPT_n) #define optAll (opt & OPT_a) #define optBins (opt & OPT_b) #define optSub (opt & OPT_s) #define optInfo (opt & OPT_i) #define optReset (opt & OPT_r) #define optVerbose (opt & OPT_v) #define next (current^1) proFile = defaultpro; mapFile = defaultmap; opt_complementary = "M+"; /* -M N */ opt = getopt32(argv, "M:m:p:nabsirv", &multiplier, &mapFile, &proFile); if (opt & (OPT_M|OPT_r)) { /* mult or reset, or both */ int fd, to_write; /* * When writing the multiplier, if the length of the write is * not sizeof(int), the multiplier is not changed */ to_write = sizeof(int); if (!optMult) to_write = 1; /* sth different from sizeof(int) */ fd = xopen(defaultpro, O_WRONLY); xwrite(fd, &multiplier, to_write); close(fd); return EXIT_SUCCESS; } /* * Use an fd for the profiling buffer, to skip stdio overhead */ len = MAXINT(ssize_t); buf = xmalloc_xopen_read_close(proFile, &len); if (!optNative) { int entries = len / sizeof(*buf); int big = 0, small = 0, i; unsigned *p; for (p = buf+1; p < buf+entries; p++) { if (*p & ~0U << (sizeof(*buf)*4)) big++; if (*p & ((1 << (sizeof(*buf)*4))-1)) small++; } if (big > small) { bb_error_msg("assuming reversed byte order, " "use -n to force native byte order"); for (p = buf; p < buf+entries; p++) for (i = 0; i < sizeof(*buf)/2; i++) { unsigned char *b = (unsigned char *) p; unsigned char tmp; tmp = b[i]; b[i] = b[sizeof(*buf)-i-1]; b[sizeof(*buf)-i-1] = tmp; } } } step = buf[0]; if (optInfo) { printf("Sampling_step: %u\n", step); return EXIT_SUCCESS; } total = 0; map = xfopen_for_read(mapFile); while (fgets(mapline, S_LEN, map)) { if (sscanf(mapline, "%llx %s %s", &fn_add, mode, fn_name) != 3) bb_error_msg_and_die("%s(%i): wrong map line", mapFile, maplineno); if (!strcmp(fn_name, "_stext")) /* only elf works like this */ { add0 = fn_add; break; } maplineno++; } if (!add0) bb_error_msg_and_die("can't find \"_stext\" in %s", mapFile); /* * Main loop. */ while (fgets(mapline, S_LEN, map)) { unsigned int this = 0; if (sscanf(mapline, "%llx %s %s", &next_add, mode, next_name) != 3) bb_error_msg_and_die("%s(%i): wrong map line", mapFile, maplineno); header_printed = 0; /* ignore any LEADING (before a '[tT]' symbol is found) Absolute symbols */ if ((*mode == 'A' || *mode == '?') && total == 0) continue; if (*mode != 'T' && *mode != 't' && *mode != 'W' && *mode != 'w' ) { break; /* only text is profiled */ } if (indx >= len / sizeof(*buf)) bb_error_msg_and_die("profile address out of range. " "Wrong map file?"); while (indx < (next_add-add0)/step) { if (optBins && (buf[indx] || optAll)) { if (!header_printed) { printf("%s:\n", fn_name); header_printed = 1; } printf("\t%"PRIx64"\t%u\n", (indx - 1)*step + add0, buf[indx]); } this += buf[indx++]; } total += this; if (optBins) { if (optVerbose || this > 0) printf(" total\t\t\t\t%u\n", this); } else if ((this || optAll) && (fn_len = next_add-fn_add) != 0 ) { if (optVerbose) printf("%016llx %-40s %6u %8.4f\n", fn_add, fn_name, this, this/(double)fn_len); else printf("%6u %-40s %8.4f\n", this, fn_name, this/(double)fn_len); if (optSub) { unsigned long long scan; for (scan = (fn_add-add0)/step + 1; scan < (next_add-add0)/step; scan++) { unsigned long long addr; addr = (scan - 1)*step + add0; printf("\t%#llx\t%s+%#llx\t%u\n", addr, fn_name, addr - fn_add, buf[scan]); } } } fn_add = next_add; strcpy(fn_name, next_name); maplineno++; } /* clock ticks, out of kernel text - probably modules */ printf("%6u %s\n", buf[len/sizeof(*buf)-1], "*unknown*"); /* trailer */ if (optVerbose) printf("%016x %-40s %6u %8.4f\n", 0, "total", total, total/(double)(fn_add-add0)); else printf("%6u %-40s %8.4f\n", total, "total", total/(double)(fn_add-add0)); fclose(map); free(buf); return EXIT_SUCCESS; } ������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/fdisk_gpt.c���������������������������������������������������������������0000644�0000000�0000000�00000010735�12263563520�016230� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#if ENABLE_FEATURE_GPT_LABEL /* * Copyright (C) 2010 Kevin Cernekee <cernekee@gmail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ #define GPT_MAGIC 0x5452415020494645ULL enum { LEGACY_GPT_TYPE = 0xee, GPT_MAX_PARTS = 256, GPT_MAX_PART_ENTRY_LEN = 4096, GUID_LEN = 16, }; typedef struct { uint64_t magic; uint32_t revision; uint32_t hdr_size; uint32_t hdr_crc32; uint32_t reserved; uint64_t current_lba; uint64_t backup_lba; uint64_t first_usable_lba; uint64_t last_usable_lba; uint8_t disk_guid[GUID_LEN]; uint64_t first_part_lba; uint32_t n_parts; uint32_t part_entry_len; uint32_t part_array_crc32; } gpt_header; typedef struct { uint8_t type_guid[GUID_LEN]; uint8_t part_guid[GUID_LEN]; uint64_t lba_start; uint64_t lba_end; uint64_t flags; uint16_t name[36]; } gpt_partition; static gpt_header *gpt_hdr; static char *part_array; static unsigned int n_parts; static unsigned int part_array_len; static unsigned int part_entry_len; static inline gpt_partition * gpt_part(int i) { if (i >= n_parts) { return NULL; } return (gpt_partition *)&part_array[i * part_entry_len]; } static uint32_t gpt_crc32(void *buf, int len) { return ~crc32_block_endian0(0xffffffff, buf, len, global_crc32_table); } static void gpt_print_guid(uint8_t *buf) { printf( "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", buf[3], buf[2], buf[1], buf[0], buf[5], buf[4], buf[7], buf[6], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); } /* TODO: real unicode support */ static void gpt_print_wide(uint16_t *s, int max_len) { int i = 0; while (i < max_len) { if (*s == 0) return; fputc(*s, stdout); s++; } } static void gpt_list_table(int xtra UNUSED_PARAM) { int i; char numstr6[6]; smart_ulltoa5(total_number_of_sectors * sector_size, numstr6, " KMGTPEZY")[0] = '\0'; printf("Disk %s: %llu sectors, %s\n", disk_device, (unsigned long long)total_number_of_sectors, numstr6); printf("Logical sector size: %u\n", sector_size); printf("Disk identifier (GUID): "); gpt_print_guid(gpt_hdr->disk_guid); printf("\nPartition table holds up to %u entries\n", (int)SWAP_LE32(gpt_hdr->n_parts)); printf("First usable sector is %llu, last usable sector is %llu\n\n", (unsigned long long)SWAP_LE64(gpt_hdr->first_usable_lba), (unsigned long long)SWAP_LE64(gpt_hdr->last_usable_lba)); printf("Number Start (sector) End (sector) Size Code Name\n"); for (i = 0; i < n_parts; i++) { gpt_partition *p = gpt_part(i); if (p->lba_start) { smart_ulltoa5((1 + SWAP_LE64(p->lba_end) - SWAP_LE64(p->lba_start)) * sector_size, numstr6, " KMGTPEZY")[0] = '\0'; printf("%4u %15llu %15llu %11s %04x ", i + 1, (unsigned long long)SWAP_LE64(p->lba_start), (unsigned long long)SWAP_LE64(p->lba_end), numstr6, 0x0700 /* FIXME */); gpt_print_wide(p->name, 18); printf("\n"); } } } static int check_gpt_label(void) { struct partition *first = pt_offset(MBRbuffer, 0); struct pte pe; uint32_t crc; /* LBA 0 contains the legacy MBR */ if (!valid_part_table_flag(MBRbuffer) || first->sys_ind != LEGACY_GPT_TYPE ) { current_label_type = 0; return 0; } /* LBA 1 contains the GPT header */ read_pte(&pe, 1); gpt_hdr = (void *)pe.sectorbuffer; if (gpt_hdr->magic != SWAP_LE64(GPT_MAGIC)) { current_label_type = 0; return 0; } if (!global_crc32_table) { global_crc32_table = crc32_filltable(NULL, 0); } crc = SWAP_LE32(gpt_hdr->hdr_crc32); gpt_hdr->hdr_crc32 = 0; if (gpt_crc32(gpt_hdr, SWAP_LE32(gpt_hdr->hdr_size)) != crc) { /* FIXME: read the backup table */ puts("\nwarning: GPT header CRC is invalid\n"); } n_parts = SWAP_LE32(gpt_hdr->n_parts); part_entry_len = SWAP_LE32(gpt_hdr->part_entry_len); if (n_parts > GPT_MAX_PARTS || part_entry_len > GPT_MAX_PART_ENTRY_LEN || SWAP_LE32(gpt_hdr->hdr_size) > sector_size ) { puts("\nwarning: unable to parse GPT disklabel\n"); current_label_type = 0; return 0; } part_array_len = n_parts * part_entry_len; part_array = xmalloc(part_array_len); seek_sector(SWAP_LE64(gpt_hdr->first_part_lba)); if (full_read(dev_fd, part_array, part_array_len) != part_array_len) { fdisk_fatal(unable_to_read); } if (gpt_crc32(part_array, part_array_len) != gpt_hdr->part_array_crc32) { /* FIXME: read the backup table */ puts("\nwarning: GPT array CRC is invalid\n"); } puts("Found valid GPT with protective MBR; using GPT\n"); current_label_type = LABEL_GPT; return 1; } #endif /* GPT_LABEL */ �����������������������������������busybox-1.22.1/util-linux/switch_root.c�������������������������������������������������������������0000644�0000000�0000000�00000017774�12263563520�016634� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* Copyright 2005 Rob Landley <rob@landley.net> * * Switch from rootfs to another filesystem as the root of the mount tree. * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define switch_root_trivial_usage //usage: "[-c /dev/console] NEW_ROOT NEW_INIT [ARGS]" //usage:#define switch_root_full_usage "\n\n" //usage: "Free initramfs and switch to another root fs:\n" //usage: "chroot to NEW_ROOT, delete all in /, move NEW_ROOT to /,\n" //usage: "execute NEW_INIT. PID must be 1. NEW_ROOT must be a mountpoint.\n" //usage: "\n -c DEV Reopen stdio to DEV after switch" #include <sys/vfs.h> #include <sys/mount.h> #include "libbb.h" // Make up for header deficiencies #ifndef RAMFS_MAGIC # define RAMFS_MAGIC ((unsigned)0x858458f6) #endif #ifndef TMPFS_MAGIC # define TMPFS_MAGIC ((unsigned)0x01021994) #endif #ifndef MS_MOVE # define MS_MOVE 8192 #endif // Recursively delete contents of rootfs static void delete_contents(const char *directory, dev_t rootdev) { DIR *dir; struct dirent *d; struct stat st; // Don't descend into other filesystems if (lstat(directory, &st) || st.st_dev != rootdev) return; // Recursively delete the contents of directories if (S_ISDIR(st.st_mode)) { dir = opendir(directory); if (dir) { while ((d = readdir(dir))) { char *newdir = d->d_name; // Skip . and .. if (DOT_OR_DOTDOT(newdir)) continue; // Recurse to delete contents newdir = concat_path_file(directory, newdir); delete_contents(newdir, rootdev); free(newdir); } closedir(dir); // Directory should now be empty, zap it rmdir(directory); } } else { // It wasn't a directory, zap it unlink(directory); } } int switch_root_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int switch_root_main(int argc UNUSED_PARAM, char **argv) { char *newroot, *console = NULL; struct stat st; struct statfs stfs; dev_t rootdev; // Parse args (-c console) opt_complementary = "-2"; // minimum 2 params getopt32(argv, "+c:", &console); // '+': stop at first non-option argv += optind; newroot = *argv++; // Change to new root directory and verify it's a different fs xchdir(newroot); xstat("/", &st); rootdev = st.st_dev; xstat(".", &st); if (st.st_dev == rootdev || getpid() != 1) { // Show usage, it says new root must be a mountpoint // and we must be PID 1 bb_show_usage(); } // Additional sanity checks: we're about to rm -rf /, so be REALLY SURE // we mean it. I could make this a CONFIG option, but I would get email // from all the people who WILL destroy their filesystems. if (stat("/init", &st) != 0 || !S_ISREG(st.st_mode)) { bb_error_msg_and_die("/init is not a regular file"); } statfs("/", &stfs); // this never fails if ((unsigned)stfs.f_type != RAMFS_MAGIC && (unsigned)stfs.f_type != TMPFS_MAGIC ) { bb_error_msg_and_die("root filesystem is not ramfs/tmpfs"); } // Zap everything out of rootdev delete_contents("/", rootdev); // Overmount / with newdir and chroot into it if (mount(".", "/", NULL, MS_MOVE, NULL)) { // For example, fails when newroot is not a mountpoint bb_perror_msg_and_die("error moving root"); } xchroot("."); // The chdir is needed to recalculate "." and ".." links /*xchdir("/"); - done in xchroot */ // If a new console specified, redirect stdin/stdout/stderr to it if (console) { close(0); xopen(console, O_RDWR); xdup2(0, 1); xdup2(0, 2); } // Exec real init execv(argv[0], argv); bb_perror_msg_and_die("can't execute '%s'", argv[0]); } /* From: Rob Landley <rob@landley.net> Date: Tue, Jun 16, 2009 at 7:47 PM Subject: Re: switch_root... ... ... ... If you're _not_ running out of init_ramfs (if for example you're using initrd instead), you probably shouldn't use switch_root because it's the wrong tool. Basically what the sucker does is something like the following shell script: find / -xdev | xargs rm -rf cd "$1" shift mount --move . / exec chroot . "$@" There are a couple reasons that won't work as a shell script: 1) If you delete the commands out of your $PATH, your shell scripts can't run more commands, but you can't start using dynamically linked _new_ commands until after you do the chroot because the path to the dynamic linker is wrong. So there's a step that needs to be sort of atomic but can't be as a shell script. (You can work around this with static linking or very carefully laid out paths and sequencing, but it's brittle, ugly, and non-obvious.) 2) The "find | rm" bit will acually delete everything because the mount points still show up (even if their contents don't), and rm -rf will then happily zap that. So the first line is an oversimplification of what you need to do _not_ to descend into other filesystems and delete their contents. The reason we do this is to free up memory, by the way. Since initramfs is a ramfs, deleting its contents frees up the memory it uses. (We leave it with one remaining dentry for the new mount point, but that's ok.) Note that you cannot ever umount rootfs, for approximately the same reason you can't kill PID 1. The kernel tracks mount points as a doubly linked list, and the pointer to the start/end of that list always points to an entry that's known to be there (rootfs), so it never has to worry about moving that pointer and it never has to worry about the list being empty. (Back around 2.6.13 there _was_ a bug that let you umount rootfs, and the system locked hard the instant you did so endlessly looping to find the end of the mount list and never stopping. They fixed it.) Oh, and the reason we mount --move _and_ do the chroot is due to the way "/" works. Each process has two special symlinks, ".", and "/". Each of them points to the dentry of a directory, and give you a location paths can start from. (Historically ".." was also special, because you could enter a directory via a symlink so backing out to the directory you came from doesn't necessarily mean the one physically above where "." points to. These days I think it's just handed off to the filesystem.) Anyway, path resolution starts with "." or "/" (although the "./" at the start of the path may be implicit), meaning it's relative to one of those two directories. Your current directory, and your current root directory. The chdir() syscall changes where "." points to, and the chroot() syscall changes where "/" points to. (Again, both are per-process which is why chroot only affects your current process and its child processes.) Note that chroot() does _not_ change where "." points to, and back before they put crazy security checks into the kernel your current directory could be somewhere you could no longer access after the chroot. (The command line chroot does a cd as well, the chroot _syscall_ is what I'm talking about.) The reason mounting something new over / has no obvious effect is the same reason mounting something over your current directory has no obvious effect: the . and / links aren't recalculated after a mount, so they still point to the same dentry they did before, even if that dentry is no longer accessible by other means. Note that "cd ." is a NOP, and "chroot /" is a nop; both look up the cached dentry and set it right back. They don't re-parse any paths, because they're what all paths your process uses would be relative to. That's why the careful sequencing above: we cd into the new mount point before we do the mount --move. Moving the mount point would otherwise make it totally inaccessible to is because cd-ing to the old path wouldn't give it to us anymore, and cd "/" just gives us the cached dentry from when the process was created (in this case the old initramfs one). But the "." symlink gives us the dentry of the filesystem we just moved, so we can then "chroot ." to copy that dentry to "/" and get the new filesystem. If we _didn't_ save that dentry in "." we couldn't get it back after the mount --move. (Yes, this is all screwy and I had to email questions to Linus Torvalds to get it straight myself. I keep meaning to write up a "how mount actually works" document someday...) */ ����busybox-1.22.1/util-linux/setarch.c�����������������������������������������������������������������0000644�0000000�0000000�00000003015�12263563520�015700� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * linux32/linux64 allows for changing uname emulation. * * Copyright 2002 Andi Kleen, SuSE Labs. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define setarch_trivial_usage //usage: "personality PROG ARGS" //usage:#define setarch_full_usage "\n\n" //usage: "Personality may be:\n" //usage: " linux32 Set 32bit uname emulation\n" //usage: " linux64 Set 64bit uname emulation" //usage: //usage:#define linux32_trivial_usage NOUSAGE_STR //usage:#define linux32_full_usage "" //usage: //usage:#define linux64_trivial_usage NOUSAGE_STR //usage:#define linux64_full_usage "" #include <sys/personality.h> #include "libbb.h" int setarch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int setarch_main(int argc UNUSED_PARAM, char **argv) { int pers; /* Figure out what personality we are supposed to switch to ... * we can be invoked as either: * argv[0],argv[1] == "setarch","personality" * argv[0] == "personality" */ if (ENABLE_SETARCH && applet_name[0] == 's' && argv[1] && strncpy(argv[1], "linux", 5) ) { applet_name = argv[1]; argv++; } if (applet_name[5] == '6') /* linux64 */ pers = PER_LINUX; else if (applet_name[5] == '3') /* linux32 */ pers = PER_LINUX32; else bb_show_usage(); argv++; if (argv[0] == NULL) bb_show_usage(); /* Try to set personality */ if (personality(pers) >= 0) { /* Try to execute the program */ BB_EXECVP(argv[0], argv); } bb_simple_perror_msg_and_die(argv[0]); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/blkid.c�������������������������������������������������������������������0000644�0000000�0000000�00000001270�12263563520�015335� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Print UUIDs on all filesystems * * Copyright (C) 2008 Denys Vlasenko. * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define blkid_trivial_usage //usage: "[BLOCKDEV]..." //usage:#define blkid_full_usage "\n\n" //usage: "Print UUIDs of all filesystems" #include "libbb.h" #include "volume_id.h" int blkid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int blkid_main(int argc UNUSED_PARAM, char **argv) { int scan_devices = 1; while (*++argv) { /* Note: bogus device names don't cause any error messages */ add_to_uuid_cache(*argv); scan_devices = 0; } display_uuid_cache(scan_devices); return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/rtcwake.c�����������������������������������������������������������������0000644�0000000�0000000�00000015437�12263563520�015722� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * rtcwake -- enter a system sleep state until specified wakeup time. * * This version was taken from util-linux and scrubbed down for busybox. * * Licensed under GPLv2, see file LICENSE in this source tree. * * This uses cross-platform Linux interfaces to enter a system sleep state, * and leave it no later than a specified time. It uses any RTC framework * driver that supports standard driver model wakeup flags. * * This is normally used like the old "apmsleep" utility, to wake from a * suspend state like ACPI S1 (standby) or S3 (suspend-to-RAM). Most * platforms can implement those without analogues of BIOS, APM, or ACPI. * * On some systems, this can also be used like "nvram-wakeup", waking * from states like ACPI S4 (suspend to disk). Not all systems have * persistent media that are appropriate for such suspend modes. * * The best way to set the system's RTC is so that it holds the current * time in UTC. Use the "-l" flag to tell this program that the system * RTC uses a local timezone instead (maybe you dual-boot MS-Windows). * That flag should not be needed on systems with adjtime support. */ //usage:#define rtcwake_trivial_usage //usage: "[-a | -l | -u] [-d DEV] [-m MODE] [-s SEC | -t TIME]" //usage:#define rtcwake_full_usage "\n\n" //usage: "Enter a system sleep state until specified wakeup time\n" //usage: IF_LONG_OPTS( //usage: "\n -a,--auto Read clock mode from adjtime" //usage: "\n -l,--local Clock is set to local time" //usage: "\n -u,--utc Clock is set to UTC time" //usage: "\n -d,--device=DEV Specify the RTC device" //usage: "\n -m,--mode=MODE Set the sleep state (default: standby)" //usage: "\n -s,--seconds=SEC Set the timeout in SEC seconds from now" //usage: "\n -t,--time=TIME Set the timeout to TIME seconds from epoch" //usage: ) //usage: IF_NOT_LONG_OPTS( //usage: "\n -a Read clock mode from adjtime" //usage: "\n -l Clock is set to local time" //usage: "\n -u Clock is set to UTC time" //usage: "\n -d DEV Specify the RTC device" //usage: "\n -m MODE Set the sleep state (default: standby)" //usage: "\n -s SEC Set the timeout in SEC seconds from now" //usage: "\n -t TIME Set the timeout to TIME seconds from epoch" //usage: ) #include "libbb.h" #include "rtc_.h" #define SYS_RTC_PATH "/sys/class/rtc/%s/device/power/wakeup" #define SYS_POWER_PATH "/sys/power/state" #define DEFAULT_MODE "standby" static NOINLINE bool may_wakeup(const char *rtcname) { ssize_t ret; char buf[128]; /* strip "/dev/" from the rtcname here */ rtcname = skip_dev_pfx(rtcname); snprintf(buf, sizeof(buf), SYS_RTC_PATH, rtcname); ret = open_read_close(buf, buf, sizeof(buf)); if (ret < 0) return false; /* wakeup events could be disabled or not supported */ return strncmp(buf, "enabled\n", 8) == 0; } static NOINLINE void setup_alarm(int fd, time_t *wakeup, time_t rtc_time) { struct tm *ptm; struct linux_rtc_wkalrm wake; /* The wakeup time is in POSIX time (more or less UTC). * Ideally RTCs use that same time; but PCs can't do that * if they need to boot MS-Windows. Messy... * * When running in utc mode this process's timezone is UTC, * so we'll pass a UTC date to the RTC. * * Else mode is local so the time given to the RTC * will instead use the local time zone. */ ptm = localtime(wakeup); wake.time.tm_sec = ptm->tm_sec; wake.time.tm_min = ptm->tm_min; wake.time.tm_hour = ptm->tm_hour; wake.time.tm_mday = ptm->tm_mday; wake.time.tm_mon = ptm->tm_mon; wake.time.tm_year = ptm->tm_year; /* wday, yday, and isdst fields are unused by Linux */ wake.time.tm_wday = -1; wake.time.tm_yday = -1; wake.time.tm_isdst = -1; /* many rtc alarms only support up to 24 hours from 'now', * so use the "more than 24 hours" request only if we must */ if ((rtc_time + (24 * 60 * 60)) > *wakeup) { xioctl(fd, RTC_ALM_SET, &wake.time); xioctl(fd, RTC_AIE_ON, 0); } else { /* avoid an extra AIE_ON call */ wake.enabled = 1; xioctl(fd, RTC_WKALM_SET, &wake); } } #define RTCWAKE_OPT_AUTO 0x01 #define RTCWAKE_OPT_LOCAL 0x02 #define RTCWAKE_OPT_UTC 0x04 #define RTCWAKE_OPT_DEVICE 0x08 #define RTCWAKE_OPT_SUSPEND_MODE 0x10 #define RTCWAKE_OPT_SECONDS 0x20 #define RTCWAKE_OPT_TIME 0x40 int rtcwake_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int rtcwake_main(int argc UNUSED_PARAM, char **argv) { time_t rtc_time; unsigned opt; const char *rtcname = NULL; const char *suspend; const char *opt_seconds; const char *opt_time; time_t sys_time; time_t alarm_time = 0; unsigned seconds = 0; int utc = -1; int fd; #if ENABLE_LONG_OPTS static const char rtcwake_longopts[] ALIGN1 = "auto\0" No_argument "a" "local\0" No_argument "l" "utc\0" No_argument "u" "device\0" Required_argument "d" "mode\0" Required_argument "m" "seconds\0" Required_argument "s" "time\0" Required_argument "t" ; applet_long_options = rtcwake_longopts; #endif opt = getopt32(argv, "alud:m:s:t:", &rtcname, &suspend, &opt_seconds, &opt_time); /* this is the default if (opt & RTCWAKE_OPT_AUTO) utc = -1; */ if (opt & (RTCWAKE_OPT_UTC | RTCWAKE_OPT_LOCAL)) utc = opt & RTCWAKE_OPT_UTC; if (!(opt & RTCWAKE_OPT_SUSPEND_MODE)) suspend = DEFAULT_MODE; if (opt & RTCWAKE_OPT_SECONDS) /* alarm time, seconds-to-sleep (relative) */ seconds = xatoi(opt_seconds); if (opt & RTCWAKE_OPT_TIME) /* alarm time, time_t (absolute, seconds since 1/1 1970 UTC) */ alarm_time = xatol(opt_time); if (!alarm_time && !seconds) bb_error_msg_and_die("must provide wake time"); if (utc == -1) utc = rtc_adjtime_is_utc(); /* the rtcname is relative to /dev */ xchdir("/dev"); /* this RTC must exist and (if we'll sleep) be wakeup-enabled */ fd = rtc_xopen(&rtcname, O_RDONLY); if (strcmp(suspend, "on") && !may_wakeup(rtcname)) bb_error_msg_and_die("%s not enabled for wakeup events", rtcname); /* relative or absolute alarm time, normalized to time_t */ sys_time = time(NULL); { struct tm tm_time; rtc_read_tm(&tm_time, fd); rtc_time = rtc_tm2time(&tm_time, utc); } if (alarm_time) { if (alarm_time < sys_time) bb_error_msg_and_die("time doesn't go backward to %s", ctime(&alarm_time)); alarm_time += sys_time - rtc_time; } else alarm_time = rtc_time + seconds + 1; setup_alarm(fd, &alarm_time, rtc_time); sync(); printf("wakeup from \"%s\" at %s", suspend, ctime(&alarm_time)); fflush_all(); usleep(10 * 1000); if (strcmp(suspend, "on")) xopen_xwrite_close(SYS_POWER_PATH, suspend); else { /* "fake" suspend ... we'll do the delay ourselves */ unsigned long data; do { ssize_t ret = safe_read(fd, &data, sizeof(data)); if (ret < 0) { bb_perror_msg("rtc read"); break; } } while (!(data & RTC_AF)); } xioctl(fd, RTC_AIE_OFF, 0); if (ENABLE_FEATURE_CLEAN_UP) close(fd); return EXIT_SUCCESS; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/pivot_root.c��������������������������������������������������������������0000644�0000000�0000000�00000001653�12263563520�016461� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * pivot_root.c - Change root file system. Based on util-linux 2.10s * * busyboxed by Evin Robertson * pivot_root syscall stubbed by Erik Andersen, so it will compile * regardless of the kernel being used. * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define pivot_root_trivial_usage //usage: "NEW_ROOT PUT_OLD" //usage:#define pivot_root_full_usage "\n\n" //usage: "Move the current root file system to PUT_OLD and make NEW_ROOT\n" //usage: "the new root file system" #include "libbb.h" extern int pivot_root(const char * new_root,const char * put_old); int pivot_root_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int pivot_root_main(int argc, char **argv) { if (argc != 3) bb_show_usage(); if (pivot_root(argv[1], argv[2]) < 0) { /* prints "pivot_root: <strerror text>" */ bb_perror_nomsg_and_die(); } return EXIT_SUCCESS; } �������������������������������������������������������������������������������������busybox-1.22.1/util-linux/mkfs_ext2.txt�������������������������������������������������������������0000644�0000000�0000000�00000016064�12263563520�016556� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Difference between bbox mke2fs and standard one (dumpe2fs comparison): [upd: inode size has been fixed since then] Two significant differences: - standard mke2fs has resize_inode feature and thus has reserved GDT blocks, which decreases free blocks; - inode size: 128/256 (since 679807k is >0.5G, standard mke2fs uses "big" inodes) this affects inode table in block groups Filesystem volume name: <none> Filesystem volume name: <none> Last mounted on: <not available> Last mounted on: <not available> Filesystem UUID: f4839760-c89b-44b7-ac52-08b4e326c0b4 Filesystem UUID: 71179558-8c8e-4a56-ad54-018008f5c358 Filesystem magic number: 0xEF53 Filesystem magic number: 0xEF53 Filesystem revision #: 1 (dynamic) Filesystem revision #: 1 (dynamic) Filesystem features: ext_attr dir_index filetype sparse_super Filesystem features: ext_attr resize_inode dir_index filetype sparse_super large_file Filesystem flags: signed_directory_hash Filesystem flags: signed_directory_hash Default mount options: (none) Default mount options: (none) Filesystem state: clean Filesystem state: clean Errors behavior: Continue Errors behavior: Continue Filesystem OS type: Linux Filesystem OS type: Linux Inode count: 42624 Inode count: 42528 Block count: 169951 Block count: 169951 Reserved block count: 8497 Reserved block count: 8497 Free blocks: 168594 Free blocks: 167103 Free inodes: 42613 Free inodes: 42517 First block: 0 First block: 0 Block size: 4096 Block size: 4096 Fragment size: 4096 Fragment size: 4096 Reserved GDT blocks: 41 Blocks per group: 32768 Blocks per group: 32768 Fragments per group: 32768 Fragments per group: 32768 Inodes per group: 7104 Inodes per group: 7088 Inode blocks per group: 222 Inode blocks per group: 443 Filesystem created: Wed Oct 21 13:40:55 2009 Filesystem created: Wed Oct 21 13:40:55 2009 Last mount time: n/a Last mount time: n/a Last write time: Wed Oct 21 13:40:55 2009 Last write time: Wed Oct 21 13:40:55 2009 Mount count: 0 Mount count: 0 Maximum mount count: 20 Maximum mount count: 37 Last checked: Wed Oct 21 13:40:55 2009 Last checked: Wed Oct 21 13:40:55 2009 Check interval: 15552000 (6 months) Check interval: 15552000 (6 months) Next check after: Mon Apr 19 13:40:55 2010 Next check after: Mon Apr 19 13:40:55 2010 Reserved blocks uid: 0 (user root) Reserved blocks uid: 0 (user root) Reserved blocks gid: 0 (group root) Reserved blocks gid: 0 (group root) First inode: 11 First inode: 11 Inode size: 128 Inode size: 256 Required extra isize: 28 Desired extra isize: 28 Default directory hash: half_md4 Default directory hash: half_md4 Directory Hash Seed: ff94d047-c9ca-4fe5-b553-937a76101a89 Directory Hash Seed: c270fd43-9868-4a92-ae99-050098e12835 Group 0: (Blocks 0-32767) Group 0: (Blocks 0-32767) Primary superblock at 0, Group descriptors at 1-1 Primary superblock at 0, Group descriptors at 1-1 Reserved GDT blocks at 2-42 Block bitmap at 2 (+2), Inode bitmap at 3 (+3) Block bitmap at 43 (+43), Inode bitmap at 44 (+44) Inode table at 4-225 (+4) Inode table at 45-487 (+45) 32537 free blocks, 7093 free inodes, 2 directories 32274 free blocks, 7077 free inodes, 2 directories Free blocks: 231-32767 Free blocks: 494-32767 Free inodes: 12-7104 Free inodes: 12-7088 Group 1: (Blocks 32768-65535) Group 1: (Blocks 32768-65535) Backup superblock at 32768, Group descriptors at 32769-32769 Backup superblock at 32768, Group descriptors at 32769-32769 Reserved GDT blocks at 32770-32810 Block bitmap at 32770 (+2), Inode bitmap at 32771 (+3) Block bitmap at 32811 (+43), Inode bitmap at 32812 (+44) Inode table at 32772-32993 (+4) Inode table at 32813-33255 (+45) 32542 free blocks, 7104 free inodes, 0 directories 32280 free blocks, 7088 free inodes, 0 directories Free blocks: 32994-65535 Free blocks: 33256-65535 Free inodes: 7105-14208 Free inodes: 7089-14176 Group 2: (Blocks 65536-98303) Group 2: (Blocks 65536-98303) Block bitmap at 65536 (+0), Inode bitmap at 65537 (+1) Block bitmap at 65536 (+0), Inode bitmap at 65537 (+1) Inode table at 65538-65759 (+2) Inode table at 65538-65980 (+2) 32544 free blocks, 7104 free inodes, 0 directories 32323 free blocks, 7088 free inodes, 0 directories Free blocks: 65760-98303 Free blocks: 65981-98303 Free inodes: 14209-21312 Free inodes: 14177-21264 ... ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/lspci.c�������������������������������������������������������������������0000644�0000000�0000000�00000005054�12263563520�015366� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * lspci implementation for busybox * * Copyright (C) 2009 Malek Degachi <malek-degachi@laposte.net> * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define lspci_trivial_usage //usage: "[-mk]" //usage:#define lspci_full_usage "\n\n" //usage: "List all PCI devices" //usage: "\n" //usage: "\n -m Parsable output" //usage: "\n -k Show driver" #include "libbb.h" enum { OPT_m = (1 << 0), OPT_k = (1 << 1), }; /* * PCI_SLOT_NAME PCI_CLASS: PCI_VID:PCI_DID [PCI_SUBSYS_VID:PCI_SUBSYS_DID] [DRIVER] */ static int FAST_FUNC fileAction( const char *fileName, struct stat *statbuf UNUSED_PARAM, void *userData UNUSED_PARAM, int depth UNUSED_PARAM) { parser_t *parser; char *tokens[3]; char *pci_slot_name = NULL, *driver = NULL; int pci_class = 0, pci_vid = 0, pci_did = 0; int pci_subsys_vid = 0, pci_subsys_did = 0; char *uevent_filename = concat_path_file(fileName, "/uevent"); parser = config_open2(uevent_filename, fopen_for_read); free(uevent_filename); while (config_read(parser, tokens, 3, 2, "\0:=", PARSE_NORMAL)) { if (strcmp(tokens[0], "DRIVER") == 0) { driver = xstrdup(tokens[1]); continue; } if (strcmp(tokens[0], "PCI_CLASS") == 0) { pci_class = xstrtou(tokens[1], 16)>>8; continue; } if (strcmp(tokens[0], "PCI_ID") == 0) { pci_vid = xstrtou(tokens[1], 16); pci_did = xstrtou(tokens[2], 16); continue; } if (strcmp(tokens[0], "PCI_SUBSYS_ID") == 0) { pci_subsys_vid = xstrtou(tokens[1], 16); pci_subsys_did = xstrtou(tokens[2], 16); continue; } if (strcmp(tokens[0], "PCI_SLOT_NAME") == 0) { pci_slot_name = xstrdup(tokens[2]); continue; } } config_close(parser); if (option_mask32 & OPT_m) { printf("%s \"Class %04x\" \"%04x\" \"%04x\" \"%04x\" \"%04x\"", pci_slot_name, pci_class, pci_vid, pci_did, pci_subsys_vid, pci_subsys_did); } else { printf("%s Class %04x: %04x:%04x", pci_slot_name, pci_class, pci_vid, pci_did); } if ((option_mask32 & OPT_k) && driver) { if (option_mask32 & OPT_m) { printf(" \"%s\"", driver); } else { printf(" %s", driver); } } bb_putchar('\n'); free(driver); free(pci_slot_name); return TRUE; } int lspci_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int lspci_main(int argc UNUSED_PARAM, char **argv) { getopt32(argv, "m" /*non-compat:*/ "k" /*ignored:*/ "nv"); recursive_action("/sys/bus/pci/devices", ACTION_RECURSE, fileAction, NULL, /* dirAction */ NULL, /* userData */ 0 /* depth */); return EXIT_SUCCESS; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/fdisk_aix.c���������������������������������������������������������������0000644�0000000�0000000�00000003554�12263563520�016220� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#if ENABLE_FEATURE_AIX_LABEL /* * Copyright (C) Andreas Neuper, Sep 1998. * * Licensed under GPLv2, see file LICENSE in this source tree. */ typedef struct { unsigned int magic; /* expect AIX_LABEL_MAGIC */ unsigned int fillbytes1[124]; unsigned int physical_volume_id; unsigned int fillbytes2[124]; } aix_partition; #define AIX_LABEL_MAGIC 0xc9c2d4c1 #define AIX_LABEL_MAGIC_SWAPPED 0xc1d4c2c9 #define AIX_INFO_MAGIC 0x00072959 #define AIX_INFO_MAGIC_SWAPPED 0x59290700 #define aixlabel ((aix_partition *)MBRbuffer) /* Changes: * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br> * Internationalization * * 2003-03-20 Phillip Kesling <pkesling@sgi.com> * Some fixes */ static smallint aix_other_endian; /* bool */ static smallint aix_volumes = 1; /* max 15 */ /* * only dealing with free blocks here */ static void aix_info(void) { puts("\n" "There is a valid AIX label on this disk.\n" "Unfortunately Linux cannot handle these disks at the moment.\n" "Nevertheless some advice:\n" "1. fdisk will destroy its contents on write.\n" "2. Be sure that this disk is NOT a still vital part of a volume group.\n" " (Otherwise you may erase the other disks as well, if unmirrored.)\n" "3. Before deleting this physical volume be sure to remove the disk\n" " logically from your AIX machine. (Otherwise you become an AIXpert).\n" ); } static int check_aix_label(void) { if (aixlabel->magic != AIX_LABEL_MAGIC && aixlabel->magic != AIX_LABEL_MAGIC_SWAPPED ) { current_label_type = 0; aix_other_endian = 0; return 0; } aix_other_endian = (aixlabel->magic == AIX_LABEL_MAGIC_SWAPPED); update_units(); current_label_type = LABEL_AIX; g_partitions = 1016; aix_volumes = 15; aix_info(); /*aix_nolabel();*/ /* %% */ /*aix_label = 1;*/ /* %% */ return 1; } #endif /* AIX_LABEL */ ����������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/more.c��������������������������������������������������������������������0000644�0000000�0000000�00000013277�12263563520�015224� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Mini more implementation for busybox * * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>. * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> * * Latest version blended together by Erik Andersen <andersen@codepoet.org>, * based on the original more implementation by Bruce, and code from the * Debian boot-floppies team. * * Termios corrects by Vladimir Oleynik <dzo@simtreas.ru> * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define more_trivial_usage //usage: "[FILE]..." //usage:#define more_full_usage "\n\n" //usage: "View FILE (or stdin) one screenful at a time" //usage: //usage:#define more_example_usage //usage: "$ dmesg | more\n" #include "libbb.h" /* Support for FEATURE_USE_TERMIOS */ struct globals { int cin_fileno; struct termios initial_settings; struct termios new_settings; } FIX_ALIASING; #define G (*(struct globals*)bb_common_bufsiz1) #define INIT_G() ((void)0) #define initial_settings (G.initial_settings) #define new_settings (G.new_settings ) #define cin_fileno (G.cin_fileno ) #define setTermSettings(fd, argp) \ do { \ if (ENABLE_FEATURE_USE_TERMIOS) \ tcsetattr(fd, TCSANOW, argp); \ } while (0) #define getTermSettings(fd, argp) tcgetattr(fd, argp) static void gotsig(int sig UNUSED_PARAM) { /* bb_putchar_stderr doesn't use stdio buffering, * therefore it is safe in signal handler */ bb_putchar_stderr('\n'); setTermSettings(cin_fileno, &initial_settings); _exit(EXIT_FAILURE); } #define CONVERTED_TAB_SIZE 8 int more_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int more_main(int argc UNUSED_PARAM, char **argv) { int c = c; /* for compiler */ int lines; int input = 0; int spaces = 0; int please_display_more_prompt; struct stat st; FILE *file; FILE *cin; int len; unsigned terminal_width; unsigned terminal_height; INIT_G(); argv++; /* Another popular pager, most, detects when stdout * is not a tty and turns into cat. This makes sense. */ if (!isatty(STDOUT_FILENO)) return bb_cat(argv); cin = fopen_for_read(CURRENT_TTY); if (!cin) return bb_cat(argv); if (ENABLE_FEATURE_USE_TERMIOS) { cin_fileno = fileno(cin); getTermSettings(cin_fileno, &initial_settings); new_settings = initial_settings; new_settings.c_lflag &= ~(ICANON | ECHO); new_settings.c_cc[VMIN] = 1; new_settings.c_cc[VTIME] = 0; setTermSettings(cin_fileno, &new_settings); bb_signals(0 + (1 << SIGINT) + (1 << SIGQUIT) + (1 << SIGTERM) , gotsig); } do { file = stdin; if (*argv) { file = fopen_or_warn(*argv, "r"); if (!file) continue; } st.st_size = 0; fstat(fileno(file), &st); please_display_more_prompt = 0; /* never returns w, h <= 1 */ get_terminal_width_height(fileno(cin), &terminal_width, &terminal_height); terminal_height -= 1; len = 0; lines = 0; while (spaces || (c = getc(file)) != EOF) { int wrap; if (spaces) spaces--; loop_top: if (input != 'r' && please_display_more_prompt) { len = printf("--More-- "); if (st.st_size != 0) { uoff_t d = (uoff_t)st.st_size / 100; if (d == 0) d = 1; len += printf("(%u%% of %"OFF_FMT"u bytes)", (int) ((uoff_t)ftello(file) / d), st.st_size); } fflush_all(); /* * We've just displayed the "--More--" prompt, so now we need * to get input from the user. */ for (;;) { input = getc(cin); input = tolower(input); if (!ENABLE_FEATURE_USE_TERMIOS) printf("\033[A"); /* cursor up */ /* Erase the last message */ printf("\r%*s\r", len, ""); /* Due to various multibyte escape * sequences, it's not ok to accept * any input as a command to scroll * the screen. We only allow known * commands, else we show help msg. */ if (input == ' ' || input == '\n' || input == 'q' || input == 'r') break; len = printf("(Enter:next line Space:next page Q:quit R:show the rest)"); } len = 0; lines = 0; please_display_more_prompt = 0; if (input == 'q') goto end; /* The user may have resized the terminal. * Re-read the dimensions. */ if (ENABLE_FEATURE_USE_TERMIOS) { get_terminal_width_height(cin_fileno, &terminal_width, &terminal_height); terminal_height -= 1; } } /* Crudely convert tabs into spaces, which are * a bajillion times easier to deal with. */ if (c == '\t') { spaces = ((unsigned)~len) % CONVERTED_TAB_SIZE; c = ' '; } /* * There are two input streams to worry about here: * * c : the character we are reading from the file being "mored" * input: a character received from the keyboard * * If we hit a newline in the _file_ stream, we want to test and * see if any characters have been hit in the _input_ stream. This * allows the user to quit while in the middle of a file. */ wrap = (++len > terminal_width); if (c == '\n' || wrap) { /* Then outputting this character * will move us to a new line. */ if (++lines >= terminal_height || input == '\n') please_display_more_prompt = 1; len = 0; } if (c != '\n' && wrap) { /* Then outputting this will also put a character on * the beginning of that new line. Thus we first want to * display the prompt (if any), so we skip the putchar() * and go back to the top of the loop, without reading * a new character. */ goto loop_top; } /* My small mind cannot fathom backspaces and UTF-8 */ putchar(c); die_if_ferror_stdout(); /* if tty was destroyed (closed xterm, etc) */ } fclose(file); fflush_all(); } while (*argv && *++argv); end: setTermSettings(cin_fileno, &initial_settings); return 0; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/rdate.c�������������������������������������������������������������������0000644�0000000�0000000�00000004062�12263563520�015351� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * The Rdate command will ask a time server for the RFC 868 time * and optionally set the system time. * * by Sterling Huxley <sterling@europa.com> * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //usage:#define rdate_trivial_usage //usage: "[-sp] HOST" //usage:#define rdate_full_usage "\n\n" //usage: "Get and possibly set the system date/time from a remote HOST\n" //usage: "\n -s Set the system date/time (default)" //usage: "\n -p Print the date/time" #include "libbb.h" enum { RFC_868_BIAS = 2208988800UL }; static void socket_timeout(int sig UNUSED_PARAM) { bb_error_msg_and_die("timeout connecting to time server"); } static time_t askremotedate(const char *host) { uint32_t nett; int fd; /* Add a timeout for dead or inaccessible servers */ alarm(10); signal(SIGALRM, socket_timeout); fd = create_and_connect_stream_or_die(host, bb_lookup_port("time", "tcp", 37)); if (safe_read(fd, &nett, 4) != 4) /* read time from server */ bb_error_msg_and_die("%s did not send the complete time", host); if (ENABLE_FEATURE_CLEAN_UP) close(fd); /* Convert from network byte order to local byte order. * RFC 868 time is the number of seconds * since 00:00 (midnight) 1 January 1900 GMT * the RFC 868 time 2,208,988,800 corresponds to 00:00 1 Jan 1970 GMT * Subtract the RFC 868 time to get Linux epoch. */ return ntohl(nett) - RFC_868_BIAS; } int rdate_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int rdate_main(int argc UNUSED_PARAM, char **argv) { time_t remote_time; unsigned flags; opt_complementary = "-1"; flags = getopt32(argv, "sp"); remote_time = askremotedate(argv[optind]); if (!(flags & 2)) { /* no -p (-s may be present) */ time_t current_time; time(¤t_time); if (current_time == remote_time) bb_error_msg("current time matches remote time"); else if (stime(&remote_time) < 0) bb_perror_msg_and_die("can't set time of day"); } if (flags != 1) /* not lone -s */ printf("%s", ctime(&remote_time)); return EXIT_SUCCESS; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/util-linux/mkfs_minix.c��������������������������������������������������������������0000644�0000000�0000000�00000044406�12263563520�016424� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * mkfs.c - make a linux (minix) file-system. * * (C) 1991 Linus Torvalds. * * Licensed under GPLv2, see file LICENSE in this source tree. */ /* * DD.MM.YY * * 24.11.91 - Time began. Used the fsck sources to get started. * * 25.11.91 - Corrected some bugs. Added support for ".badblocks" * The algorithm for ".badblocks" is a bit weird, but * it should work. Oh, well. * * 25.01.92 - Added the -l option for getting the list of bad blocks * out of a named file. (Dave Rivers, rivers@ponds.uucp) * * 28.02.92 - Added %-information when using -c. * * 28.02.93 - Added support for other namelengths than the original * 14 characters so that I can test the new kernel routines.. * * 09.10.93 - Make exit status conform to that required by fsutil * (Rik Faith, faith@cs.unc.edu) * * 31.10.93 - Added inode request feature, for backup floppies: use * 32 inodes, for a news partition use more. * (Scott Heavner, sdh@po.cwru.edu) * * 03.01.94 - Added support for file system valid flag. * (Dr. Wettstein, greg%wind.uucp@plains.nodak.edu) * * 30.10.94 - added support for v2 filesystem * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de) * * 09.11.94 - Added test to prevent overwrite of mounted fs adapted * from Theodore Ts'o's (tytso@athena.mit.edu) mke2fs * program. (Daniel Quinlan, quinlan@yggdrasil.com) * * 03.20.95 - Clear first 512 bytes of filesystem to make certain that * the filesystem is not misidentified as a MS-DOS FAT filesystem. * (Daniel Quinlan, quinlan@yggdrasil.com) * * 02.07.96 - Added small patch from Russell King to make the program a * good deal more portable (janl@math.uio.no) * * Usage: mkfs [-c | -l filename ] [-v] [-nXX] [-iXX] device [size-in-blocks] * * -c for readability checking (SLOW!) * -l for getting a list of bad blocks from a file. * -n for namelength (currently the kernel only uses 14 or 30) * -i for number of inodes * -v for v2 filesystem * * The device may be a block device or a image of one, but this isn't * enforced (but it's not much fun on a character device :-). * * Modified for BusyBox by Erik Andersen <andersen@debian.org> -- * removed getopt based parser and added a hand rolled one. */ //usage:#define mkfs_minix_trivial_usage //usage: "[-c | -l FILE] [-nXX] [-iXX] BLOCKDEV [KBYTES]" //usage:#define mkfs_minix_full_usage "\n\n" //usage: "Make a MINIX filesystem\n" //usage: "\n -c Check device for bad blocks" //usage: "\n -n [14|30] Maximum length of filenames" //usage: "\n -i INODES Number of inodes for the filesystem" //usage: "\n -l FILE Read bad blocks list from FILE" //usage: "\n -v Make version 2 filesystem" #include "libbb.h" #include <mntent.h> #include "minix.h" /* Store the very same times/uids/gids for image consistency */ #if 1 # define CUR_TIME 0 # define GETUID 0 # define GETGID 0 #else /* Was using this. Is it useful? NB: this will break testsuite */ # define CUR_TIME time(NULL) # define GETUID getuid() # define GETGID getgid() #endif enum { MAX_GOOD_BLOCKS = 512, TEST_BUFFER_BLOCKS = 16, }; #if !ENABLE_FEATURE_MINIX2 enum { version2 = 0 }; #endif enum { dev_fd = 3 }; struct globals { #if ENABLE_FEATURE_MINIX2 smallint version2; #define version2 G.version2 #endif char *device_name; uint32_t total_blocks; int badblocks; int namelen; int dirsize; int magic; char *inode_buffer; char *inode_map; char *zone_map; int used_good_blocks; unsigned long req_nr_inodes; unsigned currently_testing; char root_block[BLOCK_SIZE]; char superblock_buffer[BLOCK_SIZE]; char boot_block_buffer[512]; unsigned short good_blocks_table[MAX_GOOD_BLOCKS]; /* check_blocks(): buffer[] was the biggest static in entire bbox */ char check_blocks_buffer[BLOCK_SIZE * TEST_BUFFER_BLOCKS]; unsigned short ind_block1[BLOCK_SIZE >> 1]; unsigned short dind_block1[BLOCK_SIZE >> 1]; unsigned long ind_block2[BLOCK_SIZE >> 2]; unsigned long dind_block2[BLOCK_SIZE >> 2]; }; #define G (*ptr_to_globals) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ } while (0) static ALWAYS_INLINE unsigned div_roundup(unsigned size, unsigned n) { return (size + n-1) / n; } #define INODE_BUF1 (((struct minix1_inode*)G.inode_buffer) - 1) #define INODE_BUF2 (((struct minix2_inode*)G.inode_buffer) - 1) #define SB (*(struct minix_superblock*)G.superblock_buffer) #define SB_INODES (SB.s_ninodes) #define SB_IMAPS (SB.s_imap_blocks) #define SB_ZMAPS (SB.s_zmap_blocks) #define SB_FIRSTZONE (SB.s_firstdatazone) #define SB_ZONE_SIZE (SB.s_log_zone_size) #define SB_MAXSIZE (SB.s_max_size) #define SB_MAGIC (SB.s_magic) #if !ENABLE_FEATURE_MINIX2 # define SB_ZONES (SB.s_nzones) # define INODE_BLOCKS div_roundup(SB_INODES, MINIX1_INODES_PER_BLOCK) #else # define SB_ZONES (version2 ? SB.s_zones : SB.s_nzones) # define INODE_BLOCKS div_roundup(SB_INODES, \ (version2 ? MINIX2_INODES_PER_BLOCK : MINIX1_INODES_PER_BLOCK)) #endif #define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE) #define NORM_FIRSTZONE (2 + SB_IMAPS + SB_ZMAPS + INODE_BLOCKS) /* Before you ask "where they come from?": */ /* setbit/clrbit are supplied by sys/param.h */ static int minix_bit(const char* a, unsigned i) { return a[i >> 3] & (1<<(i & 7)); } static void minix_setbit(char *a, unsigned i) { setbit(a, i); } static void minix_clrbit(char *a, unsigned i) { clrbit(a, i); } /* Note: do not assume 0/1, it is 0/nonzero */ #define zone_in_use(x) minix_bit(G.zone_map,(x)-SB_FIRSTZONE+1) /*#define inode_in_use(x) minix_bit(G.inode_map,(x))*/ #define mark_inode(x) minix_setbit(G.inode_map,(x)) #define unmark_inode(x) minix_clrbit(G.inode_map,(x)) #define mark_zone(x) minix_setbit(G.zone_map,(x)-SB_FIRSTZONE+1) #define unmark_zone(x) minix_clrbit(G.zone_map,(x)-SB_FIRSTZONE+1) #ifndef BLKGETSIZE # define BLKGETSIZE _IO(0x12,96) /* return device size */ #endif static void write_tables(void) { /* Mark the superblock valid. */ SB.s_state |= MINIX_VALID_FS; SB.s_state &= ~MINIX_ERROR_FS; msg_eol = "seek to 0 failed"; xlseek(dev_fd, 0, SEEK_SET); msg_eol = "can't clear boot sector"; xwrite(dev_fd, G.boot_block_buffer, 512); msg_eol = "seek to BLOCK_SIZE failed"; xlseek(dev_fd, BLOCK_SIZE, SEEK_SET); msg_eol = "can't write superblock"; xwrite(dev_fd, G.superblock_buffer, BLOCK_SIZE); msg_eol = "can't write inode map"; xwrite(dev_fd, G.inode_map, SB_IMAPS * BLOCK_SIZE); msg_eol = "can't write zone map"; xwrite(dev_fd, G.zone_map, SB_ZMAPS * BLOCK_SIZE); msg_eol = "can't write inodes"; xwrite(dev_fd, G.inode_buffer, INODE_BUFFER_SIZE); msg_eol = "\n"; } static void write_block(int blk, char *buffer) { xlseek(dev_fd, blk * BLOCK_SIZE, SEEK_SET); xwrite(dev_fd, buffer, BLOCK_SIZE); } static int get_free_block(void) { int blk; if (G.used_good_blocks + 1 >= MAX_GOOD_BLOCKS) bb_error_msg_and_die("too many bad blocks"); if (G.used_good_blocks) blk = G.good_blocks_table[G.used_good_blocks - 1] + 1; else blk = SB_FIRSTZONE; while (blk < SB_ZONES && zone_in_use(blk)) blk++; if (blk >= SB_ZONES) bb_error_msg_and_die("not enough good blocks"); G.good_blocks_table[G.used_good_blocks] = blk; G.used_good_blocks++; return blk; } static void mark_good_blocks(void) { int blk; for (blk = 0; blk < G.used_good_blocks; blk++) mark_zone(G.good_blocks_table[blk]); } static int next(int zone) { if (!zone) zone = SB_FIRSTZONE - 1; while (++zone < SB_ZONES) if (zone_in_use(zone)) return zone; return 0; } static void make_bad_inode(void) { struct minix1_inode *inode = &INODE_BUF1[MINIX_BAD_INO]; int i, j, zone; int ind = 0, dind = 0; /* moved to globals to reduce stack usage unsigned short ind_block[BLOCK_SIZE >> 1]; unsigned short dind_block[BLOCK_SIZE >> 1]; */ #define ind_block (G.ind_block1) #define dind_block (G.dind_block1) #define NEXT_BAD (zone = next(zone)) if (!G.badblocks) return; mark_inode(MINIX_BAD_INO); inode->i_nlinks = 1; /* BTW, setting this makes all images different */ /* it's harder to check for bugs then - diff isn't helpful :(... */ inode->i_time = CUR_TIME; inode->i_mode = S_IFREG + 0000; inode->i_size = G.badblocks * BLOCK_SIZE; zone = next(0); for (i = 0; i < 7; i++) { inode->i_zone[i] = zone; if (!NEXT_BAD) goto end_bad; } inode->i_zone[7] = ind = get_free_block(); memset(ind_block, 0, BLOCK_SIZE); for (i = 0; i < 512; i++) { ind_block[i] = zone; if (!NEXT_BAD) goto end_bad; } inode->i_zone[8] = dind = get_free_block(); memset(dind_block, 0, BLOCK_SIZE); for (i = 0; i < 512; i++) { write_block(ind, (char *) ind_block); dind_block[i] = ind = get_free_block(); memset(ind_block, 0, BLOCK_SIZE); for (j = 0; j < 512; j++) { ind_block[j] = zone; if (!NEXT_BAD) goto end_bad; } } bb_error_msg_and_die("too many bad blocks"); end_bad: if (ind) write_block(ind, (char *) ind_block); if (dind) write_block(dind, (char *) dind_block); #undef ind_block #undef dind_block } #if ENABLE_FEATURE_MINIX2 static void make_bad_inode2(void) { struct minix2_inode *inode = &INODE_BUF2[MINIX_BAD_INO]; int i, j, zone; int ind = 0, dind = 0; /* moved to globals to reduce stack usage unsigned long ind_block[BLOCK_SIZE >> 2]; unsigned long dind_block[BLOCK_SIZE >> 2]; */ #define ind_block (G.ind_block2) #define dind_block (G.dind_block2) if (!G.badblocks) return; mark_inode(MINIX_BAD_INO); inode->i_nlinks = 1; inode->i_atime = inode->i_mtime = inode->i_ctime = CUR_TIME; inode->i_mode = S_IFREG + 0000; inode->i_size = G.badblocks * BLOCK_SIZE; zone = next(0); for (i = 0; i < 7; i++) { inode->i_zone[i] = zone; if (!NEXT_BAD) goto end_bad; } inode->i_zone[7] = ind = get_free_block(); memset(ind_block, 0, BLOCK_SIZE); for (i = 0; i < 256; i++) { ind_block[i] = zone; if (!NEXT_BAD) goto end_bad; } inode->i_zone[8] = dind = get_free_block(); memset(dind_block, 0, BLOCK_SIZE); for (i = 0; i < 256; i++) { write_block(ind, (char *) ind_block); dind_block[i] = ind = get_free_block(); memset(ind_block, 0, BLOCK_SIZE); for (j = 0; j < 256; j++) { ind_block[j] = zone; if (!NEXT_BAD) goto end_bad; } } /* Could make triple indirect block here */ bb_error_msg_and_die("too many bad blocks"); end_bad: if (ind) write_block(ind, (char *) ind_block); if (dind) write_block(dind, (char *) dind_block); #undef ind_block #undef dind_block } #else void make_bad_inode2(void); #endif static void make_root_inode(void) { struct minix1_inode *inode = &INODE_BUF1[MINIX_ROOT_INO]; mark_inode(MINIX_ROOT_INO); inode->i_zone[0] = get_free_block(); inode->i_nlinks = 2; inode->i_time = CUR_TIME; if (G.badblocks) inode->i_size = 3 * G.dirsize; else { G.root_block[2 * G.dirsize] = '\0'; G.root_block[2 * G.dirsize + 1] = '\0'; inode->i_size = 2 * G.dirsize; } inode->i_mode = S_IFDIR + 0755; inode->i_uid = GETUID; if (inode->i_uid) inode->i_gid = GETGID; write_block(inode->i_zone[0], G.root_block); } #if ENABLE_FEATURE_MINIX2 static void make_root_inode2(void) { struct minix2_inode *inode = &INODE_BUF2[MINIX_ROOT_INO]; mark_inode(MINIX_ROOT_INO); inode->i_zone[0] = get_free_block(); inode->i_nlinks = 2; inode->i_atime = inode->i_mtime = inode->i_ctime = CUR_TIME; if (G.badblocks) inode->i_size = 3 * G.dirsize; else { G.root_block[2 * G.dirsize] = '\0'; G.root_block[2 * G.dirsize + 1] = '\0'; inode->i_size = 2 * G.dirsize; } inode->i_mode = S_IFDIR + 0755; inode->i_uid = GETUID; if (inode->i_uid) inode->i_gid = GETGID; write_block(inode->i_zone[0], G.root_block); } #else void make_root_inode2(void); #endif /* * Perform a test of a block; return the number of * blocks readable. */ static size_t do_check(char *buffer, size_t try, unsigned current_block) { ssize_t got; /* Seek to the correct loc. */ msg_eol = "seek failed during testing of blocks"; xlseek(dev_fd, current_block * BLOCK_SIZE, SEEK_SET); msg_eol = "\n"; /* Try the read */ got = read(dev_fd, buffer, try * BLOCK_SIZE); if (got < 0) got = 0; try = ((size_t)got) / BLOCK_SIZE; if (got & (BLOCK_SIZE - 1)) fprintf(stderr, "Short read at block %u\n", (unsigned)(current_block + try)); return try; } static void alarm_intr(int alnum UNUSED_PARAM) { if (G.currently_testing >= SB_ZONES) return; signal(SIGALRM, alarm_intr); alarm(5); if (!G.currently_testing) return; printf("%d ...", G.currently_testing); fflush_all(); } static void check_blocks(void) { size_t try, got; G.currently_testing = 0; signal(SIGALRM, alarm_intr); alarm(5); while (G.currently_testing < SB_ZONES) { msg_eol = "seek failed in check_blocks"; xlseek(dev_fd, G.currently_testing * BLOCK_SIZE, SEEK_SET); msg_eol = "\n"; try = TEST_BUFFER_BLOCKS; if (G.currently_testing + try > SB_ZONES) try = SB_ZONES - G.currently_testing; got = do_check(G.check_blocks_buffer, try, G.currently_testing); G.currently_testing += got; if (got == try) continue; if (G.currently_testing < SB_FIRSTZONE) bb_error_msg_and_die("bad blocks before data-area: cannot make fs"); mark_zone(G.currently_testing); G.badblocks++; G.currently_testing++; } alarm(0); printf("%d bad block(s)\n", G.badblocks); } static void get_list_blocks(char *filename) { FILE *listfile; unsigned long blockno; listfile = xfopen_for_read(filename); while (!feof(listfile)) { fscanf(listfile, "%lu\n", &blockno); mark_zone(blockno); G.badblocks++; } printf("%d bad block(s)\n", G.badblocks); } static void setup_tables(void) { unsigned long inodes; unsigned norm_firstzone; unsigned sb_zmaps; unsigned i; /* memset(G.superblock_buffer, 0, BLOCK_SIZE); */ /* memset(G.boot_block_buffer, 0, 512); */ SB_MAGIC = G.magic; SB_ZONE_SIZE = 0; SB_MAXSIZE = version2 ? 0x7fffffff : (7 + 512 + 512 * 512) * 1024; if (version2) SB.s_zones = G.total_blocks; else SB.s_nzones = G.total_blocks; /* some magic nrs: 1 inode / 3 blocks */ if (G.req_nr_inodes == 0) inodes = G.total_blocks / 3; else inodes = G.req_nr_inodes; /* Round up inode count to fill block size */ if (version2) inodes = (inodes + MINIX2_INODES_PER_BLOCK - 1) & ~(MINIX2_INODES_PER_BLOCK - 1); else inodes = (inodes + MINIX1_INODES_PER_BLOCK - 1) & ~(MINIX1_INODES_PER_BLOCK - 1); if (inodes > 65535) inodes = 65535; SB_INODES = inodes; SB_IMAPS = div_roundup(SB_INODES + 1, BITS_PER_BLOCK); /* Real bad hack but overwise mkfs.minix can be thrown * in infinite loop... * try: * dd if=/dev/zero of=test.fs count=10 bs=1024 * mkfs.minix -i 200 test.fs */ /* This code is not insane: NORM_FIRSTZONE is not a constant, * it is calculated from SB_INODES, SB_IMAPS and SB_ZMAPS */ i = 999; SB_ZMAPS = 0; do { norm_firstzone = NORM_FIRSTZONE; sb_zmaps = div_roundup(G.total_blocks - norm_firstzone + 1, BITS_PER_BLOCK); if (SB_ZMAPS == sb_zmaps) goto got_it; SB_ZMAPS = sb_zmaps; /* new SB_ZMAPS, need to recalc NORM_FIRSTZONE */ } while (--i); bb_error_msg_and_die("incompatible size/inode count, try different -i N"); got_it: SB_FIRSTZONE = norm_firstzone; G.inode_map = xmalloc(SB_IMAPS * BLOCK_SIZE); G.zone_map = xmalloc(SB_ZMAPS * BLOCK_SIZE); memset(G.inode_map, 0xff, SB_IMAPS * BLOCK_SIZE); memset(G.zone_map, 0xff, SB_ZMAPS * BLOCK_SIZE); for (i = SB_FIRSTZONE; i < SB_ZONES; i++) unmark_zone(i); for (i = MINIX_ROOT_INO; i <= SB_INODES; i++) unmark_inode(i); G.inode_buffer = xzalloc(INODE_BUFFER_SIZE); printf("%ld inodes\n", (long)SB_INODES); printf("%ld blocks\n", (long)SB_ZONES); printf("Firstdatazone=%ld (%ld)\n", (long)SB_FIRSTZONE, (long)norm_firstzone); printf("Zonesize=%d\n", BLOCK_SIZE << SB_ZONE_SIZE); printf("Maxsize=%ld\n", (long)SB_MAXSIZE); } int mkfs_minix_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mkfs_minix_main(int argc UNUSED_PARAM, char **argv) { unsigned opt; char *tmp; char *str_i; char *listfile = NULL; INIT_G(); /* default (changed to 30, per Linus's suggestion, Sun Nov 21 08:05:07 1993) */ G.namelen = 30; G.dirsize = 32; G.magic = MINIX1_SUPER_MAGIC2; if (INODE_SIZE1 * MINIX1_INODES_PER_BLOCK != BLOCK_SIZE) bb_error_msg_and_die("bad inode size"); #if ENABLE_FEATURE_MINIX2 if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE) bb_error_msg_and_die("bad inode size"); #endif opt_complementary = "n+"; /* -n N */ opt = getopt32(argv, "ci:l:n:v", &str_i, &listfile, &G.namelen); argv += optind; //if (opt & 1) -c if (opt & 2) G.req_nr_inodes = xatoul(str_i); // -i //if (opt & 4) -l if (opt & 8) { // -n if (G.namelen == 14) G.magic = MINIX1_SUPER_MAGIC; else if (G.namelen == 30) G.magic = MINIX1_SUPER_MAGIC2; else bb_show_usage(); G.dirsize = G.namelen + 2; } if (opt & 0x10) { // -v #if ENABLE_FEATURE_MINIX2 version2 = 1; #else bb_error_msg_and_die("not compiled with minix v2 support"); #endif } G.device_name = argv[0]; if (!G.device_name) bb_show_usage(); /* Check if it is mounted */ if (find_mount_point(G.device_name, 0)) bb_error_msg_and_die("can't format mounted filesystem"); xmove_fd(xopen(G.device_name, O_RDWR), dev_fd); G.total_blocks = get_volume_size_in_bytes(dev_fd, argv[1], 1024, /*extend:*/ 1) / 1024; if (G.total_blocks < 10) bb_error_msg_and_die("must have at least 10 blocks"); if (version2) { G.magic = MINIX2_SUPER_MAGIC2; if (G.namelen == 14) G.magic = MINIX2_SUPER_MAGIC; } else if (G.total_blocks > 65535) G.total_blocks = 65535; #if 0 struct stat statbuf; xfstat(dev_fd, &statbuf, G.device_name); /* why? */ if (!S_ISBLK(statbuf.st_mode)) opt &= ~1; // clear -c (check) #if 0 /* I don't know why someone has special code to prevent mkfs.minix * on IDE devices. Why IDE but not SCSI, etc?... */ else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340) /* what is this? */ bb_error_msg_and_die("will not try " "to make filesystem on '%s'", G.device_name); #endif #endif tmp = G.root_block; *(short *) tmp = 1; strcpy(tmp + 2, "."); tmp += G.dirsize; *(short *) tmp = 1; strcpy(tmp + 2, ".."); tmp += G.dirsize; *(short *) tmp = 2; strcpy(tmp + 2, ".badblocks"); setup_tables(); if (opt & 1) // -c ? check_blocks(); else if (listfile) get_list_blocks(listfile); if (version2) { make_root_inode2(); make_bad_inode2(); } else { make_root_inode(); make_bad_inode(); } mark_good_blocks(); write_tables(); return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/mailutils/���������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�013777� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/mailutils/mail.h���������������������������������������������������������������������0000644�0000000�0000000�00000001655�12263563520�015077� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * helper routines * * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ struct globals { pid_t helper_pid; unsigned timeout; unsigned verbose; unsigned opts; char *user; char *pass; FILE *fp0; // initial stdin char *opt_charset; }; #define G (*ptr_to_globals) #define timeout (G.timeout ) #define verbose (G.verbose ) #define opts (G.opts ) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ G.opt_charset = (char *)CONFIG_FEATURE_MIME_CHARSET; \ } while (0) //char FAST_FUNC *parse_url(char *url, char **user, char **pass); void launch_helper(const char **argv) FAST_FUNC; void get_cred_or_die(int fd) FAST_FUNC; char *send_mail_command(const char *fmt, const char *param) FAST_FUNC; void encode_base64(char *fname, const char *text, const char *eol) FAST_FUNC; �����������������������������������������������������������������������������������busybox-1.22.1/mailutils/Config.src�����������������������������������������������������������������0000644�0000000�0000000�00000002174�12263563520�015717� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������menu "Mail Utilities" INSERT config MAKEMIME bool "makemime" default y help Create MIME-formatted messages. config FEATURE_MIME_CHARSET string "Default charset" default "us-ascii" depends on MAKEMIME || REFORMIME || SENDMAIL help Default charset of the message. config POPMAILDIR bool "popmaildir" default y help Simple yet powerful POP3 mail popper. Delivers content of remote mailboxes to local Maildir. config FEATURE_POPMAILDIR_DELIVERY bool "Allow message filters and custom delivery program" default y depends on POPMAILDIR help Allow to use a custom program to filter the content of the message before actual delivery (-F "prog [args...]"). Allow to use a custom program for message actual delivery (-M "prog [args...]"). config REFORMIME bool "reformime" default y help Parse MIME-formatted messages. config FEATURE_REFORMIME_COMPAT bool "Accept and ignore options other than -x and -X" default y depends on REFORMIME help Accept (for compatibility only) and ignore options other than -x and -X. config SENDMAIL bool "sendmail" default y help Barebones sendmail. endmenu ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/mailutils/Kbuild.src�����������������������������������������������������������������0000644�0000000�0000000�00000000255�12263563520�015722� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org> # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/mailutils/makemime.c�����������������������������������������������������������������0000644�0000000�0000000�00000020241�12263563520�015725� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * makemime: create MIME-encoded message * * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //kbuild:lib-$(CONFIG_MAKEMIME) += makemime.o mail.o #include "libbb.h" #include "mail.h" #if 0 # define dbg_error_msg(...) bb_error_msg(__VA_ARGS__) #else # define dbg_error_msg(...) ((void)0) #endif /* makemime -c type [-o file] [-e encoding] [-C charset] [-N name] \ [-a "Header: Contents"] file -m [ type ] [-o file] [-e encoding] [-a "Header: Contents"] file -j [-o file] file1 file2 @file file: filename - read or write from filename - - read or write from stdin or stdout &n - read or write from file descriptor n \( opts \) - read from child process, that generates [ opts ] Options: -c type - create a new MIME section from "file" with this Content-Type: (default is application/octet-stream). -C charset - MIME charset of a new text/plain section. -N name - MIME content name of the new mime section. -m [ type ] - create a multipart mime section from "file" of this Content-Type: (default is multipart/mixed). -e encoding - use the given encoding (7bit, 8bit, quoted-printable, or base64), instead of guessing. Omit "-e" and use -c auto to set Content-Type: to text/plain or application/octet-stream based on picked encoding. -j file1 file2 - join mime section file2 to multipart section file1. -o file - write the result to file, instead of stdout (not allowed in child processes). -a header - prepend an additional header to the output. @file - read all of the above options from file, one option or value on each line. {which version of makemime is this? What do we support?} */ /* man makemime: * -c TYPE: create a (non-multipart) MIME section with Content-Type: TYPE * makemime -c TYPE [-e ENCODING] [-o OUTFILE] [-C CHARSET] [-N NAME] [-a HEADER...] FILE * The -C option sets the MIME charset attribute for text/plain content. * The -N option sets the name attribute for Content-Type: * Encoding must be one of the following: 7bit, 8bit, quoted-printable, or base64. * -m multipart/TYPE: create a multipart MIME collection with Content-Type: multipart/TYPE * makemime -m multipart/TYPE [-e ENCODING] [-o OUTFILE] [-a HEADER...] FILE * Type must be either "multipart/mixed", "multipart/alternative", or some other MIME multipart content type. * Additionally, encoding can only be "7bit" or "8bit", and will default to "8bit" if not specified. * Finally, filename must be a MIME-formatted section, NOT a regular file. * The -m option creates an initial multipart MIME collection, that contains only one MIME section, taken from filename. * The collection is written to standard output, or the pipe or to outputfile. * -j FILE1: add a section to a multipart MIME collection * makemime -j FILE1 [-o OUTFILE] FILE2 * FILE1 must be a MIME collection that was previously created by the -m option. * FILE2 must be a MIME section that was previously created by the -c option. * The -j options adds the MIME section in FILE2 to the MIME collection in FILE1. */ /* In busybox 1.15.0.svn, makemime generates output like this * (empty lines are shown exactly!): {headers added with -a HDR} Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="24269534-2145583448-1655890676" --24269534-2145583448-1655890676 Content-Type: {set by -c, e.g. text/plain}; charset={set by -C, e.g. us-ascii} Content-Disposition: inline; filename="A" Content-Transfer-Encoding: base64 ...file A contents... --24269534-2145583448-1655890676 Content-Type: {set by -c, e.g. text/plain}; charset={set by -C, e.g. us-ascii} Content-Disposition: inline; filename="B" Content-Transfer-Encoding: base64 ...file B contents... --24269534-2145583448-1655890676-- * * For reference: here is an example email to LKML which has * 1st unnamed part (so it serves as an email body) * and one attached file: ...other headers... Content-Type: multipart/mixed; boundary="=-tOfTf3byOS0vZgxEWcX+" ...other headers... Mime-Version: 1.0 ...other headers... --=-tOfTf3byOS0vZgxEWcX+ Content-Type: text/plain Content-Transfer-Encoding: 7bit ...email text... ...email text... --=-tOfTf3byOS0vZgxEWcX+ Content-Disposition: attachment; filename="xyz" Content-Type: text/plain; name="xyz"; charset="UTF-8" Content-Transfer-Encoding: 7bit ...file contents... ...file contents... --=-tOfTf3byOS0vZgxEWcX+-- ...random junk added by mailing list robots and such... */ //usage:#define makemime_trivial_usage //usage: "[OPTIONS] [FILE]..." //usage:#define makemime_full_usage "\n\n" //usage: "Create multipart MIME-encoded message from FILEs\n" /* //usage: "Transfer encoding is base64, disposition is inline (not attachment)\n" */ //usage: "\n -o FILE Output. Default: stdout" //usage: "\n -a HDR Add header(s). Examples:" //usage: "\n \"From: user@host.org\", \"Date: `date -R`\"" //usage: "\n -c CT Content type. Default: application/octet-stream" //usage: "\n -C CS Charset. Default: " CONFIG_FEATURE_MIME_CHARSET /* //usage: "\n -e ENC Transfer encoding. Ignored. base64 is assumed" */ //usage: "\n" //usage: "\nOther options are silently ignored" /* * -c [Content-Type] should create just one MIME section * with "Content-Type:", "Content-Transfer-Encoding:", and HDRs from "-a HDR". * NB: without "Content-Disposition:" auto-added, unlike we do now * NB2: -c has *optional* param which nevertheless _can_ be specified after a space :( * * -m [multipart/mixed] should create multipart MIME section * with "Content-Type:", "Content-Transfer-Encoding:", and HDRs from "-a HDR", * and add FILE to it _verbatim_: * HEADERS * * --=_1_1321709112_1605 * FILE_CONTENTS * --=_1_1321709112_1605 * without any encoding of FILE_CONTENTS. (Basically, it expects that FILE * is the result of "makemime -c"). * * -j MULTIPART_FILE1 SINGLE_FILE2 should output MULTIPART_FILE1 + SINGLE_FILE2 * * Our current behavior is a mutant "-m + -c + -j" one: we create multipart MIME * and we put "-c" encoded FILEs into many multipart sections. */ int makemime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int makemime_main(int argc UNUSED_PARAM, char **argv) { llist_t *opt_headers = NULL, *l; const char *opt_output; const char *content_type = "application/octet-stream"; #define boundary opt_output enum { OPT_c = 1 << 0, // create (non-multipart) section OPT_e = 1 << 1, // Content-Transfer-Encoding. Ignored. Assumed base64 OPT_o = 1 << 2, // output to OPT_C = 1 << 3, // charset OPT_N = 1 << 4, // COMPAT OPT_a = 1 << 5, // additional headers //OPT_m = 1 << 6, // create mutipart section //OPT_j = 1 << 7, // join section to multipart section }; INIT_G(); // parse options opt_complementary = "a::"; opts = getopt32(argv, "c:e:o:C:N:a:", // "m:j:", &content_type, NULL, &opt_output, &G.opt_charset, NULL, &opt_headers //, NULL, NULL ); //argc -= optind; argv += optind; // respect -o output if (opts & OPT_o) freopen(opt_output, "w", stdout); // no files given on command line? -> use stdin if (!*argv) *--argv = (char *)"-"; // put additional headers for (l = opt_headers; l; l = l->link) puts(l->data); // make a random string -- it will delimit message parts srand(monotonic_us()); boundary = xasprintf("%u-%u-%u", (unsigned)rand(), (unsigned)rand(), (unsigned)rand()); // put multipart header printf( "Mime-Version: 1.0\n" "Content-Type: multipart/mixed; boundary=\"%s\"\n" , boundary ); // put attachments while (*argv) { printf( "\n--%s\n" "Content-Type: %s; charset=%s\n" "Content-Disposition: inline; filename=\"%s\"\n" "Content-Transfer-Encoding: base64\n" , boundary , content_type , G.opt_charset , bb_get_last_path_component_strip(*argv) ); encode_base64(*argv++, (const char *)stdin, ""); } // put multipart footer printf("\n--%s--\n" "\n", boundary); return EXIT_SUCCESS; #undef boundary } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/mailutils/reformime.c����������������������������������������������������������������0000644�0000000�0000000�00000016177�12263563520�016142� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * reformime: parse MIME-encoded message * * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //kbuild:lib-$(CONFIG_REFORMIME) += reformime.o mail.o #include "libbb.h" #include "mail.h" #if 0 # define dbg_error_msg(...) bb_error_msg(__VA_ARGS__) #else # define dbg_error_msg(...) ((void)0) #endif static const char *find_token(const char *const string_array[], const char *key, const char *defvalue) { const char *r = NULL; int i; for (i = 0; string_array[i] != NULL; i++) { if (strcasecmp(string_array[i], key) == 0) { r = (char *)string_array[i+1]; break; } } return (r) ? r : defvalue; } static const char *xfind_token(const char *const string_array[], const char *key) { const char *r = find_token(string_array, key, NULL); if (r) return r; bb_error_msg_and_die("not found: '%s'", key); } enum { OPT_x = 1 << 0, OPT_X = 1 << 1, #if ENABLE_FEATURE_REFORMIME_COMPAT OPT_d = 1 << 2, OPT_e = 1 << 3, OPT_i = 1 << 4, OPT_s = 1 << 5, OPT_r = 1 << 6, OPT_c = 1 << 7, OPT_m = 1 << 8, OPT_h = 1 << 9, OPT_o = 1 << 10, OPT_O = 1 << 11, #endif }; static int parse(const char *boundary, char **argv) { int boundary_len = strlen(boundary); char uniq[sizeof("%%llu.%u") + sizeof(int)*3]; dbg_error_msg("BOUNDARY[%s]", boundary); // prepare unique string pattern sprintf(uniq, "%%llu.%u", (unsigned)getpid()); dbg_error_msg("UNIQ[%s]", uniq); while (1) { char *header; const char *tokens[32]; /* 32 is enough */ const char *type; /* Read the header (everything up to two \n) */ { unsigned header_idx = 0; int last_ch = 0; header = NULL; while (1) { int ch = fgetc(stdin); if (ch == '\r') /* Support both line endings */ continue; if (ch == EOF) break; if (ch == '\n' && last_ch == ch) break; if (!(header_idx & 0xff)) header = xrealloc(header, header_idx + 0x101); header[header_idx++] = last_ch = ch; } if (!header) { dbg_error_msg("EOF"); break; } header[header_idx] = '\0'; dbg_error_msg("H:'%s'", p); } /* Split to tokens */ { char *s, *p; unsigned ntokens; const char *delims = ";=\" \t\n"; /* Skip to last Content-Type: */ s = p = header; while ((p = strchr(p, '\n')) != NULL) { p++; if (strncasecmp(p, "Content-Type:", sizeof("Content-Type:")-1) == 0) s = p; } dbg_error_msg("L:'%s'", p); ntokens = 0; s = strtok(s, delims); while (s) { tokens[ntokens] = s; if (ntokens < ARRAY_SIZE(tokens) - 1) ntokens++; dbg_error_msg("L[%d]='%s'", ntokens, s); s = strtok(NULL, delims); } tokens[ntokens] = NULL; dbg_error_msg("EMPTYLINE, ntokens:%d", ntokens); if (ntokens == 0) break; } /* Is it multipart? */ type = find_token(tokens, "Content-Type:", "text/plain"); dbg_error_msg("TYPE:'%s'", type); if (0 == strncasecmp(type, "multipart/", 10)) { /* Yes, recurse */ if (strcasecmp(type + 10, "mixed") != 0) bb_error_msg_and_die("no support of content type '%s'", type); parse(xfind_token(tokens, "boundary"), argv); } else { /* No, process one non-multipart section */ char *end; pid_t pid = pid; FILE *fp; const char *charset = find_token(tokens, "charset", CONFIG_FEATURE_MIME_CHARSET); const char *encoding = find_token(tokens, "Content-Transfer-Encoding:", "7bit"); /* Compose target filename */ char *filename = (char *)find_token(tokens, "filename", NULL); if (!filename) filename = xasprintf(uniq, monotonic_us()); else filename = bb_get_last_path_component_strip(xstrdup(filename)); if (opts & OPT_X) { int fd[2]; /* start external helper */ xpipe(fd); pid = vfork(); if (0 == pid) { /* child reads from fd[0] */ close(fd[1]); xmove_fd(fd[0], STDIN_FILENO); xsetenv("CONTENT_TYPE", type); xsetenv("CHARSET", charset); xsetenv("ENCODING", encoding); xsetenv("FILENAME", filename); BB_EXECVP_or_die(argv); } /* parent will write to fd[1] */ close(fd[0]); fp = xfdopen_for_write(fd[1]); signal(SIGPIPE, SIG_IGN); } else { /* write to file */ char *fname = xasprintf("%s%s", *argv, filename); fp = xfopen_for_write(fname); free(fname); } free(filename); /* write to fp */ end = NULL; if (0 == strcasecmp(encoding, "base64")) { read_base64(stdin, fp, '-'); } else if (0 != strcasecmp(encoding, "7bit") && 0 != strcasecmp(encoding, "8bit") ) { /* quoted-printable, binary, user-defined are unsupported so far */ bb_error_msg_and_die("encoding '%s' not supported", encoding); } else { /* plain 7bit or 8bit */ while ((end = xmalloc_fgets(stdin)) != NULL) { if ('-' == end[0] && '-' == end[1] && strncmp(end + 2, boundary, boundary_len) == 0 ) { break; } fputs(end, fp); } } fclose(fp); /* Wait for child */ if (opts & OPT_X) { int rc; signal(SIGPIPE, SIG_DFL); rc = (wait4pid(pid) & 0xff); if (rc != 0) return rc + 20; } /* Multipart ended? */ if (end && '-' == end[2 + boundary_len] && '-' == end[2 + boundary_len + 1]) { dbg_error_msg("FINISHED MPART:'%s'", end); break; } dbg_error_msg("FINISHED:'%s'", end); free(end); } /* end of "handle one non-multipart block" */ free(header); } /* while (1) */ dbg_error_msg("ENDPARSE[%s]", boundary); return EXIT_SUCCESS; } //usage:#define reformime_trivial_usage //usage: "[OPTIONS]" //usage:#define reformime_full_usage "\n\n" //usage: "Parse MIME-encoded message on stdin\n" //usage: "\n -x PREFIX Extract content of MIME sections to files" //usage: "\n -X PROG ARGS Filter content of MIME sections through PROG" //usage: "\n Must be the last option" //usage: "\n" //usage: "\nOther options are silently ignored" /* Usage: reformime [options] -d - parse a delivery status notification. -e - extract contents of MIME section. -x - extract MIME section to a file. -X - pipe MIME section to a program. -i - show MIME info. -s n.n.n.n - specify MIME section. -r - rewrite message, filling in missing MIME headers. -r7 - also convert 8bit/raw encoding to quoted-printable, if possible. -r8 - also convert quoted-printable encoding to 8bit, if possible. -c charset - default charset for rewriting, -o, and -O. -m [file] [file]... - create a MIME message digest. -h "header" - decode RFC 2047-encoded header. -o "header" - encode unstructured header using RFC 2047. -O "header" - encode address list header using RFC 2047. */ int reformime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int reformime_main(int argc UNUSED_PARAM, char **argv) { const char *opt_prefix = ""; INIT_G(); // parse options // N.B. only -x and -X are supported so far opt_complementary = "x--X:X--x" IF_FEATURE_REFORMIME_COMPAT(":m::"); opts = getopt32(argv, "x:X" IF_FEATURE_REFORMIME_COMPAT("deis:r:c:m:h:o:O:"), &opt_prefix IF_FEATURE_REFORMIME_COMPAT(, NULL, NULL, &G.opt_charset, NULL, NULL, NULL, NULL) ); argv += optind; return parse("", (opts & OPT_X) ? argv : (char **)&opt_prefix); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/mailutils/popmaildir.c���������������������������������������������������������������0000644�0000000�0000000�00000017551�12263563520�016312� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * popmaildir: a simple yet powerful POP3 client * Delivers contents of remote mailboxes to local Maildir * * Inspired by original utility by Nikola Vladov * * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //kbuild:lib-$(CONFIG_POPMAILDIR) += popmaildir.o mail.o //usage:#define popmaildir_trivial_usage //usage: "[OPTIONS] MAILDIR [CONN_HELPER ARGS]" //usage:#define popmaildir_full_usage "\n\n" //usage: "Fetch content of remote mailbox to local maildir\n" /* //usage: "\n -b Binary mode. Ignored" */ /* //usage: "\n -d Debug. Ignored" */ /* //usage: "\n -m Show used memory. Ignored" */ /* //usage: "\n -V Show version. Ignored" */ /* //usage: "\n -c Use tcpclient. Ignored" */ /* //usage: "\n -a Use APOP protocol. Implied. If server supports APOP -> use it" */ //usage: "\n -s Skip authorization" //usage: "\n -T Get messages with TOP instead of RETR" //usage: "\n -k Keep retrieved messages on the server" //usage: "\n -t SEC Network timeout" //usage: IF_FEATURE_POPMAILDIR_DELIVERY( //usage: "\n -F \"PROG ARGS\" Filter program (may be repeated)" //usage: "\n -M \"PROG ARGS\" Delivery program" //usage: ) //usage: "\n" //usage: "\nFetch from plain POP3 server:" //usage: "\npopmaildir -k DIR nc pop3.server.com 110 <user_and_pass.txt" //usage: "\nFetch from SSLed POP3 server and delete fetched emails:" //usage: "\npopmaildir DIR -- openssl s_client -quiet -connect pop3.server.com:995 <user_and_pass.txt" /* //usage: "\n -R BYTES Remove old messages on the server >= BYTES. Ignored" */ /* //usage: "\n -Z N1-N2 Remove messages from N1 to N2 (dangerous). Ignored" */ /* //usage: "\n -L BYTES Don't retrieve new messages >= BYTES. Ignored" */ /* //usage: "\n -H LINES Type first LINES of a message. Ignored" */ //usage: //usage:#define popmaildir_example_usage //usage: "$ popmaildir -k ~/Maildir -- nc pop.drvv.ru 110 [<password_file]\n" //usage: "$ popmaildir ~/Maildir -- openssl s_client -quiet -connect pop.gmail.com:995 [<password_file]\n" #include "libbb.h" #include "mail.h" static void pop3_checkr(const char *fmt, const char *param, char **ret) { char *msg = send_mail_command(fmt, param); char *answer = xmalloc_fgetline(stdin); if (answer && '+' == answer[0]) { free(msg); if (timeout) alarm(0); if (ret) { // skip "+OK " memmove(answer, answer + 4, strlen(answer) - 4); *ret = answer; } else free(answer); return; } bb_error_msg_and_die("%s failed, reply was: %s", msg, answer); } static void pop3_check(const char *fmt, const char *param) { pop3_checkr(fmt, param, NULL); } int popmaildir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int popmaildir_main(int argc UNUSED_PARAM, char **argv) { char *buf; unsigned nmsg; char *hostname; pid_t pid; const char *retr; #if ENABLE_FEATURE_POPMAILDIR_DELIVERY const char *delivery; #endif unsigned opt_nlines = 0; enum { OPT_b = 1 << 0, // -b binary mode. Ignored OPT_d = 1 << 1, // -d,-dd,-ddd debug. Ignored OPT_m = 1 << 2, // -m show used memory. Ignored OPT_V = 1 << 3, // -V version. Ignored OPT_c = 1 << 4, // -c use tcpclient. Ignored OPT_a = 1 << 5, // -a use APOP protocol OPT_s = 1 << 6, // -s skip authorization OPT_T = 1 << 7, // -T get messages with TOP instead with RETR OPT_k = 1 << 8, // -k keep retrieved messages on the server OPT_t = 1 << 9, // -t90 set timeout to 90 sec OPT_R = 1 << 10, // -R20000 remove old messages on the server >= 20000 bytes (requires -k). Ignored OPT_Z = 1 << 11, // -Z11-23 remove messages from 11 to 23 (dangerous). Ignored OPT_L = 1 << 12, // -L50000 not retrieve new messages >= 50000 bytes. Ignored OPT_H = 1 << 13, // -H30 type first 30 lines of a message; (-L12000 -H30). Ignored OPT_M = 1 << 14, // -M\"program arg1 arg2 ...\"; deliver by program. Treated like -F OPT_F = 1 << 15, // -F\"program arg1 arg2 ...\"; filter by program. Treated like -M }; // init global variables INIT_G(); // parse options opt_complementary = "-1:dd:t+:R+:L+:H+"; opts = getopt32(argv, "bdmVcasTkt:" "R:Z:L:H:" IF_FEATURE_POPMAILDIR_DELIVERY("M:F:"), &timeout, NULL, NULL, NULL, &opt_nlines IF_FEATURE_POPMAILDIR_DELIVERY(, &delivery, &delivery) // we treat -M and -F the same ); //argc -= optind; argv += optind; // get auth info if (!(opts & OPT_s)) get_cred_or_die(STDIN_FILENO); // goto maildir xchdir(*argv++); // launch connect helper, if any if (*argv) launch_helper((const char **)argv); // get server greeting pop3_checkr(NULL, NULL, &buf); // authenticate (if no -s given) if (!(opts & OPT_s)) { // server supports APOP and we want it? if ('<' == buf[0] && (opts & OPT_a)) { union { // save a bit of stack md5_ctx_t ctx; char hex[16 * 2 + 1]; } md5; uint32_t res[16 / 4]; char *s = strchr(buf, '>'); if (s) s[1] = '\0'; // get md5 sum of "<stamp>password" string md5_begin(&md5.ctx); md5_hash(&md5.ctx, buf, strlen(buf)); md5_hash(&md5.ctx, G.pass, strlen(G.pass)); md5_end(&md5.ctx, res); *bin2hex(md5.hex, (char*)res, 16) = '\0'; // APOP s = xasprintf("%s %s", G.user, md5.hex); pop3_check("APOP %s", s); free(s); free(buf); // server ignores APOP -> use simple text authentication } else { // USER pop3_check("USER %s", G.user); // PASS pop3_check("PASS %s", G.pass); } } // get mailbox statistics pop3_checkr("STAT", NULL, &buf); // prepare message filename suffix hostname = safe_gethostname(); pid = getpid(); // get messages counter // NOTE: we don't use xatou(buf) since buf is "nmsg nbytes" // we only need nmsg and atoi is just exactly what we need // if atoi fails to convert buf into number it returns 0 // in this case the following loop simply will not be executed nmsg = atoi(buf); free(buf); // loop through messages retr = (opts & OPT_T) ? xasprintf("TOP %%u %u", opt_nlines) : "RETR %u"; for (; nmsg; nmsg--) { char *filename; char *target; char *answer; FILE *fp; #if ENABLE_FEATURE_POPMAILDIR_DELIVERY int rc; #endif // generate unique filename filename = xasprintf("tmp/%llu.%u.%s", monotonic_us(), (unsigned)pid, hostname); // retrieve message in ./tmp/ unless filter is specified pop3_check(retr, (const char *)(ptrdiff_t)nmsg); #if ENABLE_FEATURE_POPMAILDIR_DELIVERY // delivery helper ordered? -> setup pipe if (opts & (OPT_F|OPT_M)) { // helper will have $FILENAME set to filename xsetenv("FILENAME", filename); fp = popen(delivery, "w"); unsetenv("FILENAME"); if (!fp) { bb_perror_msg("delivery helper"); break; } } else #endif // create and open file filename fp = xfopen_for_write(filename); // copy stdin to fp (either filename or delivery helper) while ((answer = xmalloc_fgets_str(stdin, "\r\n")) != NULL) { char *s = answer; if ('.' == answer[0]) { if ('.' == answer[1]) s++; else if ('\r' == answer[1] && '\n' == answer[2] && '\0' == answer[3]) break; } //*strchrnul(s, '\r') = '\n'; fputs(s, fp); free(answer); } #if ENABLE_FEATURE_POPMAILDIR_DELIVERY // analyse delivery status if (opts & (OPT_F|OPT_M)) { rc = pclose(fp); if (99 == rc) // 99 means bail out break; // if (rc) // !0 means skip to the next message goto skip; // // 0 means continue } else { // close filename fclose(fp); } #endif // delete message from server if (!(opts & OPT_k)) pop3_check("DELE %u", (const char*)(ptrdiff_t)nmsg); // atomically move message to ./new/ target = xstrdup(filename); strncpy(target, "new", 3); // ... or just stop receiving on failure if (rename_or_warn(filename, target)) break; free(target); #if ENABLE_FEATURE_POPMAILDIR_DELIVERY skip: #endif free(filename); } // Bye pop3_check("QUIT", NULL); if (ENABLE_FEATURE_CLEAN_UP) { free(G.user); free(G.pass); } return EXIT_SUCCESS; } �������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/mailutils/mail.c���������������������������������������������������������������������0000644�0000000�0000000�00000010106�12263563520�015061� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * helper routines * * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" #include "mail.h" static void kill_helper(void) { if (G.helper_pid > 0) { kill(G.helper_pid, SIGTERM); G.helper_pid = 0; } } // generic signal handler static void signal_handler(int signo) { #define err signo if (SIGALRM == signo) { kill_helper(); bb_error_msg_and_die("timed out"); } // SIGCHLD. reap zombies if (safe_waitpid(G.helper_pid, &err, WNOHANG) > 0) { if (WIFSIGNALED(err)) bb_error_msg_and_die("helper killed by signal %u", WTERMSIG(err)); if (WIFEXITED(err)) { G.helper_pid = 0; if (WEXITSTATUS(err)) bb_error_msg_and_die("helper exited (%u)", WEXITSTATUS(err)); } } #undef err } void FAST_FUNC launch_helper(const char **argv) { // setup vanilla unidirectional pipes interchange int i; int pipes[4]; xpipe(pipes); xpipe(pipes + 2); // NB: handler must be installed before vfork bb_signals(0 + (1 << SIGCHLD) + (1 << SIGALRM) , signal_handler); G.helper_pid = xvfork(); i = (!G.helper_pid) * 2; // for parent:0, for child:2 close(pipes[i + 1]); // 1 or 3 - closing one write end close(pipes[2 - i]); // 2 or 0 - closing one read end xmove_fd(pipes[i], STDIN_FILENO); // 0 or 2 - using other read end xmove_fd(pipes[3 - i], STDOUT_FILENO); // 3 or 1 - using other write end // End result: // parent stdout [3] -> child stdin [2] // child stdout [1] -> parent stdin [0] if (!G.helper_pid) { // child: try to execute connection helper // NB: SIGCHLD & SIGALRM revert to SIG_DFL on exec BB_EXECVP_or_die((char**)argv); } // parent // check whether child is alive //redundant:signal_handler(SIGCHLD); // child seems OK -> parent goes on atexit(kill_helper); } char* FAST_FUNC send_mail_command(const char *fmt, const char *param) { char *msg; if (timeout) alarm(timeout); msg = (char*)fmt; if (fmt) { msg = xasprintf(fmt, param); if (verbose) bb_error_msg("send:'%s'", msg); printf("%s\r\n", msg); } fflush_all(); return msg; } // NB: parse_url can modify url[] (despite const), but only if '@' is there /* static char* FAST_FUNC parse_url(char *url, char **user, char **pass) { // parse [user[:pass]@]host // return host char *s = strchr(url, '@'); *user = *pass = NULL; if (s) { *s++ = '\0'; *user = url; url = s; s = strchr(*user, ':'); if (s) { *s++ = '\0'; *pass = s; } } return url; } */ void FAST_FUNC encode_base64(char *fname, const char *text, const char *eol) { enum { SRC_BUF_SIZE = 57, /* This *MUST* be a multiple of 3 */ DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3), }; #define src_buf text char src[SRC_BUF_SIZE]; FILE *fp = fp; ssize_t len = len; char dst_buf[DST_BUF_SIZE + 1]; if (fname) { fp = (NOT_LONE_DASH(fname)) ? xfopen_for_read(fname) : (FILE *)text; src_buf = src; } else if (text) { // though we do not call uuencode(NULL, NULL) explicitly // still we do not want to break things suddenly len = strlen(text); } else return; while (1) { size_t size; if (fname) { size = fread((char *)src_buf, 1, SRC_BUF_SIZE, fp); if ((ssize_t)size < 0) bb_perror_msg_and_die(bb_msg_read_error); } else { size = len; if (len > SRC_BUF_SIZE) size = SRC_BUF_SIZE; } if (!size) break; // encode the buffer we just read in bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64); if (fname) { printf("%s\n", eol); } else { src_buf += size; len -= size; } fwrite(dst_buf, 1, 4 * ((size + 2) / 3), stdout); } if (fname && NOT_LONE_DASH(fname)) fclose(fp); #undef src_buf } /* * get username and password from a file descriptor */ void FAST_FUNC get_cred_or_die(int fd) { if (isatty(fd)) { G.user = xstrdup(bb_ask(fd, /* timeout: */ 0, "User: ")); G.pass = xstrdup(bb_ask(fd, /* timeout: */ 0, "Password: ")); } else { G.user = xmalloc_reads(fd, /* maxsize: */ NULL); G.pass = xmalloc_reads(fd, /* maxsize: */ NULL); } if (!G.user || !*G.user || !G.pass) bb_error_msg_and_die("no username or password"); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/mailutils/sendmail.c�����������������������������������������������������������������0000644�0000000�0000000�00000031234�12263563520�015740� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * bare bones sendmail * * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ //kbuild:lib-$(CONFIG_SENDMAIL) += sendmail.o mail.o //usage:#define sendmail_trivial_usage //usage: "[OPTIONS] [RECIPIENT_EMAIL]..." //usage:#define sendmail_full_usage "\n\n" //usage: "Read email from stdin and send it\n" //usage: "\nStandard options:" //usage: "\n -t Read additional recipients from message body" //usage: "\n -f SENDER Sender (required)" //usage: "\n -o OPTIONS Various options. -oi implied, others are ignored" //usage: "\n -i -oi synonym. implied and ignored" //usage: "\n" //usage: "\nBusybox specific options:" //usage: "\n -v Verbose" //usage: "\n -w SECS Network timeout" //usage: "\n -H 'PROG ARGS' Run connection helper" //usage: "\n Examples:" //usage: "\n -H 'exec openssl s_client -quiet -tls1 -starttls smtp" //usage: "\n -connect smtp.gmail.com:25' <email.txt" //usage: "\n [4<username_and_passwd.txt | -auUSER -apPASS]" //usage: "\n -H 'exec openssl s_client -quiet -tls1" //usage: "\n -connect smtp.gmail.com:465' <email.txt" //usage: "\n [4<username_and_passwd.txt | -auUSER -apPASS]" //usage: "\n -S HOST[:PORT] Server" //usage: "\n -auUSER Username for AUTH LOGIN" //usage: "\n -apPASS Password for AUTH LOGIN" ////usage: "\n -amMETHOD Authentication method. Ignored. LOGIN is implied" //usage: "\n" //usage: "\nOther options are silently ignored; -oi -t is implied" //usage: IF_MAKEMIME( //usage: "\nUse makemime to create emails with attachments" //usage: ) #include "libbb.h" #include "mail.h" // limit maximum allowed number of headers to prevent overflows. // set to 0 to not limit #define MAX_HEADERS 256 static void send_r_n(const char *s) { if (verbose) bb_error_msg("send:'%s'", s); printf("%s\r\n", s); } static int smtp_checkp(const char *fmt, const char *param, int code) { char *answer; char *msg = send_mail_command(fmt, param); // read stdin // if the string has a form NNN- -- read next string. E.g. EHLO response // parse first bytes to a number // if code = -1 then just return this number // if code != -1 then checks whether the number equals the code // if not equal -> die saying msg while ((answer = xmalloc_fgetline(stdin)) != NULL) { if (verbose) bb_error_msg("recv:'%.*s'", (int)(strchrnul(answer, '\r') - answer), answer); if (strlen(answer) <= 3 || '-' != answer[3]) break; free(answer); } if (answer) { int n = atoi(answer); if (timeout) alarm(0); free(answer); if (-1 == code || n == code) { free(msg); return n; } } bb_error_msg_and_die("%s failed", msg); } static int smtp_check(const char *fmt, int code) { return smtp_checkp(fmt, NULL, code); } // strip argument of bad chars static char *sane_address(char *str) { char *s; trim(str); s = str; while (*s) { if (!isalnum(*s) && !strchr("_-.@", *s)) { bb_error_msg("bad address '%s'", str); /* returning "": */ str[0] = '\0'; return str; } s++; } return str; } // check for an address inside angle brackets, if not found fall back to normal static char *angle_address(char *str) { char *s, *e; trim(str); e = last_char_is(str, '>'); if (e) { s = strrchr(str, '<'); if (s) { *e = '\0'; str = s + 1; } } return sane_address(str); } static void rcptto(const char *s) { if (!*s) return; // N.B. we don't die if recipient is rejected, for the other recipients may be accepted if (250 != smtp_checkp("RCPT TO:<%s>", s, -1)) bb_error_msg("Bad recipient: <%s>", s); } // send to a list of comma separated addresses static void rcptto_list(const char *list) { char *str = xstrdup(list); char *s = str; char prev = 0; int in_quote = 0; while (*s) { char ch = *s++; if (ch == '"' && prev != '\\') { in_quote = !in_quote; } else if (!in_quote && ch == ',') { s[-1] = '\0'; rcptto(angle_address(str)); str = s; } prev = ch; } if (prev != ',') rcptto(angle_address(str)); free(str); } int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int sendmail_main(int argc UNUSED_PARAM, char **argv) { char *opt_connect = opt_connect; char *opt_from; char *s; llist_t *list = NULL; char *host = sane_address(safe_gethostname()); unsigned nheaders = 0; int code; enum { HDR_OTHER = 0, HDR_TOCC, HDR_BCC, } last_hdr = 0; int check_hdr; int has_to = 0; enum { //--- standard options OPT_t = 1 << 0, // read message for recipients, append them to those on cmdline OPT_f = 1 << 1, // sender address OPT_o = 1 << 2, // various options. -oi IMPLIED! others are IGNORED! OPT_i = 1 << 3, // IMPLIED! //--- BB specific options OPT_w = 1 << 4, // network timeout OPT_H = 1 << 5, // use external connection helper OPT_S = 1 << 6, // specify connection string OPT_a = 1 << 7, // authentication tokens OPT_v = 1 << 8, // verbosity }; // init global variables INIT_G(); // save initial stdin since body is piped! xdup2(STDIN_FILENO, 3); G.fp0 = xfdopen_for_read(3); // parse options // -v is a counter, -f is required. -H and -S are mutually exclusive, -a is a list opt_complementary = "vv:f:w+:H--S:S--H:a::"; // N.B. since -H and -S are mutually exclusive they do not interfere in opt_connect // -a is for ssmtp (http://downloads.openwrt.org/people/nico/man/man8/ssmtp.8.html) compatibility, // it is still under development. opts = getopt32(argv, "tf:o:iw:H:S:a::v", &opt_from, NULL, &timeout, &opt_connect, &opt_connect, &list, &verbose); //argc -= optind; argv += optind; // process -a[upm]<token> options if ((opts & OPT_a) && !list) bb_show_usage(); while (list) { char *a = (char *) llist_pop(&list); if ('u' == a[0]) G.user = xstrdup(a+1); if ('p' == a[0]) G.pass = xstrdup(a+1); // N.B. we support only AUTH LOGIN so far //if ('m' == a[0]) // G.method = xstrdup(a+1); } // N.B. list == NULL here //bb_info_msg("OPT[%x] AU[%s], AP[%s], AM[%s], ARGV[%s]", opts, au, ap, am, *argv); // connect to server // connection helper ordered? -> if (opts & OPT_H) { const char *args[] = { "sh", "-c", opt_connect, NULL }; // plug it in launch_helper(args); // Now: // our stdout will go to helper's stdin, // helper's stdout will be available on our stdin. // Wait for initial server message. // If helper (such as openssl) invokes STARTTLS, the initial 220 // is swallowed by helper (and not repeated after TLS is initiated). // We will send NOOP cmd to server and check the response. // We should get 220+250 on plain connection, 250 on STARTTLSed session. // // The problem here is some servers delay initial 220 message, // and consider client to be a spammer if it starts sending cmds // before 220 reached it. The code below is unsafe in this regard: // in non-STARTTLSed case, we potentially send NOOP before 220 // is sent by server. // Ideas? (--delay SECS opt? --assume-starttls-helper opt?) code = smtp_check("NOOP", -1); if (code == 220) // we got 220 - this is not STARTTLSed connection, // eat 250 response to our NOOP smtp_check(NULL, 250); else if (code != 250) bb_error_msg_and_die("SMTP init failed"); } else { // vanilla connection int fd; // host[:port] not explicitly specified? -> use $SMTPHOST // no $SMTPHOST? -> use localhost if (!(opts & OPT_S)) { opt_connect = getenv("SMTPHOST"); if (!opt_connect) opt_connect = (char *)"127.0.0.1"; } // do connect fd = create_and_connect_stream_or_die(opt_connect, 25); // and make ourselves a simple IO filter xmove_fd(fd, STDIN_FILENO); xdup2(STDIN_FILENO, STDOUT_FILENO); // Wait for initial server 220 message smtp_check(NULL, 220); } // we should start with modern EHLO if (250 != smtp_checkp("EHLO %s", host, -1)) smtp_checkp("HELO %s", host, 250); free(host); // perform authentication if (opts & OPT_a) { smtp_check("AUTH LOGIN", 334); // we must read credentials unless they are given via -a[up] options if (!G.user || !G.pass) get_cred_or_die(4); encode_base64(NULL, G.user, NULL); smtp_check("", 334); encode_base64(NULL, G.pass, NULL); smtp_check("", 235); } // set sender // N.B. we have here a very loosely defined algorythm // since sendmail historically offers no means to specify secrets on cmdline. // 1) server can require no authentication -> // we must just provide a (possibly fake) reply address. // 2) server can require AUTH -> // we must provide valid username and password along with a (possibly fake) reply address. // For the sake of security username and password are to be read either from console or from a secured file. // Since reading from console may defeat usability, the solution is either to read from a predefined // file descriptor (e.g. 4), or again from a secured file. // got no sender address? -> use system username as a resort // N.B. we marked -f as required option! //if (!G.user) { // // N.B. IMHO getenv("USER") can be way easily spoofed! // G.user = xuid2uname(getuid()); // opt_from = xasprintf("%s@%s", G.user, domain); //} smtp_checkp("MAIL FROM:<%s>", opt_from, 250); // process message // read recipients from message and add them to those given on cmdline. // this means we scan stdin for To:, Cc:, Bcc: lines until an empty line // and then use the rest of stdin as message body code = 0; // set "analyze headers" mode while ((s = xmalloc_fgetline(G.fp0)) != NULL) { dump: // put message lines doubling leading dots if (code) { // escape leading dots // N.B. this feature is implied even if no -i (-oi) switch given // N.B. we need to escape the leading dot regardless of // whether it is single or not character on the line if ('.' == s[0] /*&& '\0' == s[1] */) printf("."); // dump read line send_r_n(s); free(s); continue; } // analyze headers // To: or Cc: headers add recipients check_hdr = (0 == strncasecmp("To:", s, 3)); has_to |= check_hdr; if (opts & OPT_t) { if (check_hdr || 0 == strncasecmp("Bcc:" + 1, s, 3)) { rcptto_list(s+3); last_hdr = HDR_TOCC; goto addheader; } // Bcc: header adds blind copy (hidden) recipient if (0 == strncasecmp("Bcc:", s, 4)) { rcptto_list(s+4); free(s); last_hdr = HDR_BCC; continue; // N.B. Bcc: vanishes from headers! } } check_hdr = (list && isspace(s[0])); if (strchr(s, ':') || check_hdr) { // other headers go verbatim // N.B. RFC2822 2.2.3 "Long Header Fields" allows for headers to occupy several lines. // Continuation is denoted by prefixing additional lines with whitespace(s). // Thanks (stefan.seyfried at googlemail.com) for pointing this out. if (check_hdr && last_hdr != HDR_OTHER) { rcptto_list(s+1); if (last_hdr == HDR_BCC) continue; // N.B. Bcc: vanishes from headers! } else { last_hdr = HDR_OTHER; } addheader: // N.B. we allow MAX_HEADERS generic headers at most to prevent attacks if (MAX_HEADERS && ++nheaders >= MAX_HEADERS) goto bail; llist_add_to_end(&list, s); } else { // a line without ":" (an empty line too, by definition) doesn't look like a valid header // so stop "analyze headers" mode reenter: // put recipients specified on cmdline check_hdr = 1; while (*argv) { char *t = sane_address(*argv); rcptto(t); //if (MAX_HEADERS && ++nheaders >= MAX_HEADERS) // goto bail; if (!has_to) { const char *hdr; if (check_hdr && argv[1]) hdr = "To: %s,"; else if (check_hdr) hdr = "To: %s"; else if (argv[1]) hdr = "To: %s," + 3; else hdr = "To: %s" + 3; llist_add_to_end(&list, xasprintf(hdr, t)); check_hdr = 0; } argv++; } // enter "put message" mode // N.B. DATA fails iff no recipients were accepted (or even provided) // in this case just bail out gracefully if (354 != smtp_check("DATA", -1)) goto bail; // dump the headers while (list) { send_r_n((char *) llist_pop(&list)); } // stop analyzing headers code++; // N.B. !s means: we read nothing, and nothing to be read in the future. // just dump empty line and break the loop if (!s) { send_r_n(""); break; } // go dump message body // N.B. "s" already contains the first non-header line, so pretend we read it from input goto dump; } } // odd case: we didn't stop "analyze headers" mode -> message body is empty. Reenter the loop // N.B. after reenter code will be > 0 if (!code) goto reenter; // finalize the message smtp_check(".", 250); bail: // ... and say goodbye smtp_check("QUIT", 221); // cleanup if (ENABLE_FEATURE_CLEAN_UP) fclose(G.fp0); return EXIT_SUCCESS; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/�������������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365445�013101� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash.c��������������������������������������������������������������������������0000644�0000000�0000000�00001112071�12320365445�014023� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * ash shell port for busybox * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * Original BSD copyright notice is retained at the end of this file. * * Copyright (c) 1989, 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au> * was re-ported from NetBSD and debianized. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* * The following should be set to reflect the type of system you have: * JOBS -> 1 if you have Berkeley job control, 0 otherwise. * define SYSV if you are running under System V. * define DEBUG=1 to compile in debugging ('set -o debug' to turn on) * define DEBUG=2 to compile in and turn on debugging. * * When debugging is on (DEBUG is 1 and "set -o debug" was executed), * debugging info will be written to ./trace and a quit signal * will generate a core dump. */ #define DEBUG 0 /* Tweak debug output verbosity here */ #define DEBUG_TIME 0 #define DEBUG_PID 1 #define DEBUG_SIG 1 #define PROFILE 0 #define JOBS ENABLE_ASH_JOB_CONTROL #include <paths.h> #include <setjmp.h> #include <fnmatch.h> #include <sys/times.h> #include "busybox.h" /* for applet_names */ #include "unicode.h" #include "shell_common.h" #if ENABLE_SH_MATH_SUPPORT # include "math.h" #endif #if ENABLE_ASH_RANDOM_SUPPORT # include "random.h" #else # define CLEAR_RANDOM_T(rnd) ((void)0) #endif #include "NUM_APPLETS.h" #if NUM_APPLETS == 1 /* STANDALONE does not make sense, and won't compile */ # undef CONFIG_FEATURE_SH_STANDALONE # undef ENABLE_FEATURE_SH_STANDALONE # undef IF_FEATURE_SH_STANDALONE # undef IF_NOT_FEATURE_SH_STANDALONE # define ENABLE_FEATURE_SH_STANDALONE 0 # define IF_FEATURE_SH_STANDALONE(...) # define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__ #endif #ifndef PIPE_BUF # define PIPE_BUF 4096 /* amount of buffering in a pipe */ #endif #if !BB_MMU # error "Do not even bother, ash will not run on NOMMU machine" #endif //config:config ASH //config: bool "ash" //config: default y //config: depends on !NOMMU //config: help //config: Tha 'ash' shell adds about 60k in the default configuration and is //config: the most complete and most pedantically correct shell included with //config: busybox. This shell is actually a derivative of the Debian 'dash' //config: shell (by Herbert Xu), which was created by porting the 'ash' shell //config: (written by Kenneth Almquist) from NetBSD. //config: //config:config ASH_BASH_COMPAT //config: bool "bash-compatible extensions" //config: default y //config: depends on ASH //config: help //config: Enable bash-compatible extensions. //config: //config:config ASH_IDLE_TIMEOUT //config: bool "Idle timeout variable" //config: default n //config: depends on ASH //config: help //config: Enables bash-like auto-logout after $TMOUT seconds of idle time. //config: //config:config ASH_JOB_CONTROL //config: bool "Job control" //config: default y //config: depends on ASH //config: help //config: Enable job control in the ash shell. //config: //config:config ASH_ALIAS //config: bool "Alias support" //config: default y //config: depends on ASH //config: help //config: Enable alias support in the ash shell. //config: //config:config ASH_GETOPTS //config: bool "Builtin getopt to parse positional parameters" //config: default y //config: depends on ASH //config: help //config: Enable support for getopts builtin in ash. //config: //config:config ASH_BUILTIN_ECHO //config: bool "Builtin version of 'echo'" //config: default y //config: depends on ASH //config: help //config: Enable support for echo builtin in ash. //config: //config:config ASH_BUILTIN_PRINTF //config: bool "Builtin version of 'printf'" //config: default y //config: depends on ASH //config: help //config: Enable support for printf builtin in ash. //config: //config:config ASH_BUILTIN_TEST //config: bool "Builtin version of 'test'" //config: default y //config: depends on ASH //config: help //config: Enable support for test builtin in ash. //config: //config:config ASH_CMDCMD //config: bool "'command' command to override shell builtins" //config: default y //config: depends on ASH //config: help //config: Enable support for the ash 'command' builtin, which allows //config: you to run the specified command with the specified arguments, //config: even when there is an ash builtin command with the same name. //config: //config:config ASH_MAIL //config: bool "Check for new mail on interactive shells" //config: default n //config: depends on ASH //config: help //config: Enable "check for new mail" function in the ash shell. //config: //config:config ASH_OPTIMIZE_FOR_SIZE //config: bool "Optimize for size instead of speed" //config: default y //config: depends on ASH //config: help //config: Compile ash for reduced size at the price of speed. //config: //config:config ASH_RANDOM_SUPPORT //config: bool "Pseudorandom generator and $RANDOM variable" //config: default y //config: depends on ASH //config: help //config: Enable pseudorandom generator and dynamic variable "$RANDOM". //config: Each read of "$RANDOM" will generate a new pseudorandom value. //config: You can reset the generator by using a specified start value. //config: After "unset RANDOM" the generator will switch off and this //config: variable will no longer have special treatment. //config: //config:config ASH_EXPAND_PRMT //config: bool "Expand prompt string" //config: default y //config: depends on ASH //config: help //config: "PS#" may contain volatile content, such as backquote commands. //config: This option recreates the prompt string from the environment //config: variable each time it is displayed. //config: //applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) //applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh)) //applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash)) //kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o //kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o /* ============ Hash table sizes. Configurable. */ #define VTABSIZE 39 #define ATABSIZE 39 #define CMDTABLESIZE 31 /* should be prime */ /* ============ Shell options */ static const char *const optletters_optnames[] = { "e" "errexit", "f" "noglob", "I" "ignoreeof", "i" "interactive", "m" "monitor", "n" "noexec", "s" "stdin", "x" "xtrace", "v" "verbose", "C" "noclobber", "a" "allexport", "b" "notify", "u" "nounset", "\0" "vi" #if ENABLE_ASH_BASH_COMPAT ,"\0" "pipefail" #endif #if DEBUG ,"\0" "nolog" ,"\0" "debug" #endif }; #define optletters(n) optletters_optnames[n][0] #define optnames(n) (optletters_optnames[n] + 1) enum { NOPTS = ARRAY_SIZE(optletters_optnames) }; /* ============ Misc data */ #define msg_illnum "Illegal number: %s" /* * We enclose jmp_buf in a structure so that we can declare pointers to * jump locations. The global variable handler contains the location to * jump to when an exception occurs, and the global variable exception_type * contains a code identifying the exception. To implement nested * exception handlers, the user should save the value of handler on entry * to an inner scope, set handler to point to a jmploc structure for the * inner scope, and restore handler on exit from the scope. */ struct jmploc { jmp_buf loc; }; struct globals_misc { /* pid of main shell */ int rootpid; /* shell level: 0 for the main shell, 1 for its children, and so on */ int shlvl; #define rootshell (!shlvl) char *minusc; /* argument to -c option */ char *curdir; // = nullstr; /* current working directory */ char *physdir; // = nullstr; /* physical working directory */ char *arg0; /* value of $0 */ struct jmploc *exception_handler; volatile int suppress_int; /* counter */ volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ /* last pending signal */ volatile /*sig_atomic_t*/ smallint pending_sig; smallint exception_type; /* kind of exception (0..5) */ /* exceptions */ #define EXINT 0 /* SIGINT received */ #define EXERROR 1 /* a generic error */ #define EXSHELLPROC 2 /* execute a shell procedure */ #define EXEXEC 3 /* command execution failed */ #define EXEXIT 4 /* exit the shell */ #define EXSIG 5 /* trapped signal in wait(1) */ smallint isloginsh; char nullstr[1]; /* zero length string */ char optlist[NOPTS]; #define eflag optlist[0] #define fflag optlist[1] #define Iflag optlist[2] #define iflag optlist[3] #define mflag optlist[4] #define nflag optlist[5] #define sflag optlist[6] #define xflag optlist[7] #define vflag optlist[8] #define Cflag optlist[9] #define aflag optlist[10] #define bflag optlist[11] #define uflag optlist[12] #define viflag optlist[13] #if ENABLE_ASH_BASH_COMPAT # define pipefail optlist[14] #else # define pipefail 0 #endif #if DEBUG # define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT] # define debug optlist[15 + ENABLE_ASH_BASH_COMPAT] #endif /* trap handler commands */ /* * Sigmode records the current value of the signal handlers for the various * modes. A value of zero means that the current handler is not known. * S_HARD_IGN indicates that the signal was ignored on entry to the shell. */ char sigmode[NSIG - 1]; #define S_DFL 1 /* default signal handling (SIG_DFL) */ #define S_CATCH 2 /* signal is caught */ #define S_IGN 3 /* signal is ignored (SIG_IGN) */ #define S_HARD_IGN 4 /* signal is ignored permenantly */ /* indicates specified signal received */ uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */ char *trap[NSIG]; char **trap_ptr; /* used only by "trap hack" */ /* Rarely referenced stuff */ #if ENABLE_ASH_RANDOM_SUPPORT random_t random_gen; #endif pid_t backgndpid; /* pid of last background process */ smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ }; extern struct globals_misc *const ash_ptr_to_globals_misc; #define G_misc (*ash_ptr_to_globals_misc) #define rootpid (G_misc.rootpid ) #define shlvl (G_misc.shlvl ) #define minusc (G_misc.minusc ) #define curdir (G_misc.curdir ) #define physdir (G_misc.physdir ) #define arg0 (G_misc.arg0 ) #define exception_handler (G_misc.exception_handler) #define exception_type (G_misc.exception_type ) #define suppress_int (G_misc.suppress_int ) #define pending_int (G_misc.pending_int ) #define pending_sig (G_misc.pending_sig ) #define isloginsh (G_misc.isloginsh ) #define nullstr (G_misc.nullstr ) #define optlist (G_misc.optlist ) #define sigmode (G_misc.sigmode ) #define gotsig (G_misc.gotsig ) #define may_have_traps (G_misc.may_have_traps ) #define trap (G_misc.trap ) #define trap_ptr (G_misc.trap_ptr ) #define random_gen (G_misc.random_gen ) #define backgndpid (G_misc.backgndpid ) #define job_warning (G_misc.job_warning) #define INIT_G_misc() do { \ (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \ barrier(); \ curdir = nullstr; \ physdir = nullstr; \ trap_ptr = trap; \ } while (0) /* ============ DEBUG */ #if DEBUG static void trace_printf(const char *fmt, ...); static void trace_vprintf(const char *fmt, va_list va); # define TRACE(param) trace_printf param # define TRACEV(param) trace_vprintf param # define close(fd) do { \ int dfd = (fd); \ if (close(dfd) < 0) \ bb_error_msg("bug on %d: closing %d(0x%x)", \ __LINE__, dfd, dfd); \ } while (0) #else # define TRACE(param) # define TRACEV(param) #endif /* ============ Utility functions */ #define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0) #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) static int isdigit_str9(const char *str) { int maxlen = 9 + 1; /* max 9 digits: 999999999 */ while (--maxlen && isdigit(*str)) str++; return (*str == '\0'); } static const char *var_end(const char *var) { while (*var) if (*var++ == '=') break; return var; } /* ============ Interrupts / exceptions */ static void exitshell(void) NORETURN; /* * These macros allow the user to suspend the handling of interrupt signals * over a period of time. This is similar to SIGHOLD or to sigblock, but * much more efficient and portable. (But hacking the kernel is so much * more fun than worrying about efficiency and portability. :-)) */ #define INT_OFF do { \ suppress_int++; \ xbarrier(); \ } while (0) /* * Called to raise an exception. Since C doesn't include exceptions, we * just do a longjmp to the exception handler. The type of exception is * stored in the global variable "exception_type". */ static void raise_exception(int) NORETURN; static void raise_exception(int e) { #if DEBUG if (exception_handler == NULL) abort(); #endif INT_OFF; exception_type = e; longjmp(exception_handler->loc, 1); } #if DEBUG #define raise_exception(e) do { \ TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \ raise_exception(e); \ } while (0) #endif /* * Called from trap.c when a SIGINT is received. (If the user specifies * that SIGINT is to be trapped or ignored using the trap builtin, then * this routine is not called.) Suppressint is nonzero when interrupts * are held using the INT_OFF macro. (The test for iflag is just * defensive programming.) */ static void raise_interrupt(void) NORETURN; static void raise_interrupt(void) { int ex_type; pending_int = 0; /* Signal is not automatically unmasked after it is raised, * do it ourself - unmask all signals */ sigprocmask_allsigs(SIG_UNBLOCK); /* pending_sig = 0; - now done in signal_handler() */ ex_type = EXSIG; if (gotsig[SIGINT - 1] && !trap[SIGINT]) { if (!(rootshell && iflag)) { /* Kill ourself with SIGINT */ signal(SIGINT, SIG_DFL); raise(SIGINT); } ex_type = EXINT; } raise_exception(ex_type); /* NOTREACHED */ } #if DEBUG #define raise_interrupt() do { \ TRACE(("raising interrupt on line %d\n", __LINE__)); \ raise_interrupt(); \ } while (0) #endif static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void int_on(void) { xbarrier(); if (--suppress_int == 0 && pending_int) { raise_interrupt(); } } #define INT_ON int_on() static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void force_int_on(void) { xbarrier(); suppress_int = 0; if (pending_int) raise_interrupt(); } #define FORCE_INT_ON force_int_on() #define SAVE_INT(v) ((v) = suppress_int) #define RESTORE_INT(v) do { \ xbarrier(); \ suppress_int = (v); \ if (suppress_int == 0 && pending_int) \ raise_interrupt(); \ } while (0) /* ============ Stdout/stderr output */ static void outstr(const char *p, FILE *file) { INT_OFF; fputs(p, file); INT_ON; } static void flush_stdout_stderr(void) { INT_OFF; fflush_all(); INT_ON; } static void outcslow(int c, FILE *dest) { INT_OFF; putc(c, dest); fflush(dest); INT_ON; } static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2))); static int out1fmt(const char *fmt, ...) { va_list ap; int r; INT_OFF; va_start(ap, fmt); r = vprintf(fmt, ap); va_end(ap); INT_ON; return r; } static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4))); static int fmtstr(char *outbuf, size_t length, const char *fmt, ...) { va_list ap; int ret; va_start(ap, fmt); INT_OFF; ret = vsnprintf(outbuf, length, fmt, ap); va_end(ap); INT_ON; return ret; } static void out1str(const char *p) { outstr(p, stdout); } static void out2str(const char *p) { outstr(p, stderr); flush_stdout_stderr(); } /* ============ Parser structures */ /* control characters in argument strings */ #define CTL_FIRST CTLESC #define CTLESC ((unsigned char)'\201') /* escape next character */ #define CTLVAR ((unsigned char)'\202') /* variable defn */ #define CTLENDVAR ((unsigned char)'\203') #define CTLBACKQ ((unsigned char)'\204') #define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ /* CTLBACKQ | CTLQUOTE == '\205' */ #define CTLARI ((unsigned char)'\206') /* arithmetic expression */ #define CTLENDARI ((unsigned char)'\207') #define CTLQUOTEMARK ((unsigned char)'\210') #define CTL_LAST CTLQUOTEMARK /* variable substitution byte (follows CTLVAR) */ #define VSTYPE 0x0f /* type of variable substitution */ #define VSNUL 0x10 /* colon--treat the empty string as unset */ #define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ /* values of VSTYPE field */ #define VSNORMAL 0x1 /* normal variable: $var or ${var} */ #define VSMINUS 0x2 /* ${var-text} */ #define VSPLUS 0x3 /* ${var+text} */ #define VSQUESTION 0x4 /* ${var?message} */ #define VSASSIGN 0x5 /* ${var=text} */ #define VSTRIMRIGHT 0x6 /* ${var%pattern} */ #define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ #define VSTRIMLEFT 0x8 /* ${var#pattern} */ #define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ #define VSLENGTH 0xa /* ${#var} */ #if ENABLE_ASH_BASH_COMPAT #define VSSUBSTR 0xc /* ${var:position:length} */ #define VSREPLACE 0xd /* ${var/pattern/replacement} */ #define VSREPLACEALL 0xe /* ${var//pattern/replacement} */ #endif static const char dolatstr[] ALIGN1 = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' }; #define NCMD 0 #define NPIPE 1 #define NREDIR 2 #define NBACKGND 3 #define NSUBSHELL 4 #define NAND 5 #define NOR 6 #define NSEMI 7 #define NIF 8 #define NWHILE 9 #define NUNTIL 10 #define NFOR 11 #define NCASE 12 #define NCLIST 13 #define NDEFUN 14 #define NARG 15 #define NTO 16 #if ENABLE_ASH_BASH_COMPAT #define NTO2 17 #endif #define NCLOBBER 18 #define NFROM 19 #define NFROMTO 20 #define NAPPEND 21 #define NTOFD 22 #define NFROMFD 23 #define NHERE 24 #define NXHERE 25 #define NNOT 26 #define N_NUMBER 27 union node; struct ncmd { smallint type; /* Nxxxx */ union node *assign; union node *args; union node *redirect; }; struct npipe { smallint type; smallint pipe_backgnd; struct nodelist *cmdlist; }; struct nredir { smallint type; union node *n; union node *redirect; }; struct nbinary { smallint type; union node *ch1; union node *ch2; }; struct nif { smallint type; union node *test; union node *ifpart; union node *elsepart; }; struct nfor { smallint type; union node *args; union node *body; char *var; }; struct ncase { smallint type; union node *expr; union node *cases; }; struct nclist { smallint type; union node *next; union node *pattern; union node *body; }; struct narg { smallint type; union node *next; char *text; struct nodelist *backquote; }; /* nfile and ndup layout must match! * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight * that it is actually NTO2 (>&file), and change its type. */ struct nfile { smallint type; union node *next; int fd; int _unused_dupfd; union node *fname; char *expfname; }; struct ndup { smallint type; union node *next; int fd; int dupfd; union node *vname; char *_unused_expfname; }; struct nhere { smallint type; union node *next; int fd; union node *doc; }; struct nnot { smallint type; union node *com; }; union node { smallint type; struct ncmd ncmd; struct npipe npipe; struct nredir nredir; struct nbinary nbinary; struct nif nif; struct nfor nfor; struct ncase ncase; struct nclist nclist; struct narg narg; struct nfile nfile; struct ndup ndup; struct nhere nhere; struct nnot nnot; }; /* * NODE_EOF is returned by parsecmd when it encounters an end of file. * It must be distinct from NULL. */ #define NODE_EOF ((union node *) -1L) struct nodelist { struct nodelist *next; union node *n; }; struct funcnode { int count; union node n; }; /* * Free a parse tree. */ static void freefunc(struct funcnode *f) { if (f && --f->count < 0) free(f); } /* ============ Debugging output */ #if DEBUG static FILE *tracefile; static void trace_printf(const char *fmt, ...) { va_list va; if (debug != 1) return; if (DEBUG_TIME) fprintf(tracefile, "%u ", (int) time(NULL)); if (DEBUG_PID) fprintf(tracefile, "[%u] ", (int) getpid()); if (DEBUG_SIG) fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int); va_start(va, fmt); vfprintf(tracefile, fmt, va); va_end(va); } static void trace_vprintf(const char *fmt, va_list va) { if (debug != 1) return; if (DEBUG_TIME) fprintf(tracefile, "%u ", (int) time(NULL)); if (DEBUG_PID) fprintf(tracefile, "[%u] ", (int) getpid()); if (DEBUG_SIG) fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int); vfprintf(tracefile, fmt, va); } static void trace_puts(const char *s) { if (debug != 1) return; fputs(s, tracefile); } static void trace_puts_quoted(char *s) { char *p; char c; if (debug != 1) return; putc('"', tracefile); for (p = s; *p; p++) { switch ((unsigned char)*p) { case '\n': c = 'n'; goto backslash; case '\t': c = 't'; goto backslash; case '\r': c = 'r'; goto backslash; case '\"': c = '\"'; goto backslash; case '\\': c = '\\'; goto backslash; case CTLESC: c = 'e'; goto backslash; case CTLVAR: c = 'v'; goto backslash; case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; case CTLBACKQ: c = 'q'; goto backslash; case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; backslash: putc('\\', tracefile); putc(c, tracefile); break; default: if (*p >= ' ' && *p <= '~') putc(*p, tracefile); else { putc('\\', tracefile); putc((*p >> 6) & 03, tracefile); putc((*p >> 3) & 07, tracefile); putc(*p & 07, tracefile); } break; } } putc('"', tracefile); } static void trace_puts_args(char **ap) { if (debug != 1) return; if (!*ap) return; while (1) { trace_puts_quoted(*ap); if (!*++ap) { putc('\n', tracefile); break; } putc(' ', tracefile); } } static void opentrace(void) { char s[100]; #ifdef O_APPEND int flags; #endif if (debug != 1) { if (tracefile) fflush(tracefile); /* leave open because libedit might be using it */ return; } strcpy(s, "./trace"); if (tracefile) { if (!freopen(s, "a", tracefile)) { fprintf(stderr, "Can't re-open %s\n", s); debug = 0; return; } } else { tracefile = fopen(s, "a"); if (tracefile == NULL) { fprintf(stderr, "Can't open %s\n", s); debug = 0; return; } } #ifdef O_APPEND flags = fcntl(fileno(tracefile), F_GETFL); if (flags >= 0) fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); #endif setlinebuf(tracefile); fputs("\nTracing started.\n", tracefile); } static void indent(int amount, char *pfx, FILE *fp) { int i; for (i = 0; i < amount; i++) { if (pfx && i == amount - 1) fputs(pfx, fp); putc('\t', fp); } } /* little circular references here... */ static void shtree(union node *n, int ind, char *pfx, FILE *fp); static void sharg(union node *arg, FILE *fp) { char *p; struct nodelist *bqlist; unsigned char subtype; if (arg->type != NARG) { out1fmt("<node type %d>\n", arg->type); abort(); } bqlist = arg->narg.backquote; for (p = arg->narg.text; *p; p++) { switch ((unsigned char)*p) { case CTLESC: p++; putc(*p, fp); break; case CTLVAR: putc('$', fp); putc('{', fp); subtype = *++p; if (subtype == VSLENGTH) putc('#', fp); while (*p != '=') { putc(*p, fp); p++; } if (subtype & VSNUL) putc(':', fp); switch (subtype & VSTYPE) { case VSNORMAL: putc('}', fp); break; case VSMINUS: putc('-', fp); break; case VSPLUS: putc('+', fp); break; case VSQUESTION: putc('?', fp); break; case VSASSIGN: putc('=', fp); break; case VSTRIMLEFT: putc('#', fp); break; case VSTRIMLEFTMAX: putc('#', fp); putc('#', fp); break; case VSTRIMRIGHT: putc('%', fp); break; case VSTRIMRIGHTMAX: putc('%', fp); putc('%', fp); break; case VSLENGTH: break; default: out1fmt("<subtype %d>", subtype); } break; case CTLENDVAR: putc('}', fp); break; case CTLBACKQ: case CTLBACKQ|CTLQUOTE: putc('$', fp); putc('(', fp); shtree(bqlist->n, -1, NULL, fp); putc(')', fp); break; default: putc(*p, fp); break; } } } static void shcmd(union node *cmd, FILE *fp) { union node *np; int first; const char *s; int dftfd; first = 1; for (np = cmd->ncmd.args; np; np = np->narg.next) { if (!first) putc(' ', fp); sharg(np, fp); first = 0; } for (np = cmd->ncmd.redirect; np; np = np->nfile.next) { if (!first) putc(' ', fp); dftfd = 0; switch (np->nfile.type) { case NTO: s = ">>"+1; dftfd = 1; break; case NCLOBBER: s = ">|"; dftfd = 1; break; case NAPPEND: s = ">>"; dftfd = 1; break; #if ENABLE_ASH_BASH_COMPAT case NTO2: #endif case NTOFD: s = ">&"; dftfd = 1; break; case NFROM: s = "<"; break; case NFROMFD: s = "<&"; break; case NFROMTO: s = "<>"; break; default: s = "*error*"; break; } if (np->nfile.fd != dftfd) fprintf(fp, "%d", np->nfile.fd); fputs(s, fp); if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { fprintf(fp, "%d", np->ndup.dupfd); } else { sharg(np->nfile.fname, fp); } first = 0; } } static void shtree(union node *n, int ind, char *pfx, FILE *fp) { struct nodelist *lp; const char *s; if (n == NULL) return; indent(ind, pfx, fp); if (n == NODE_EOF) { fputs("<EOF>", fp); return; } switch (n->type) { case NSEMI: s = "; "; goto binop; case NAND: s = " && "; goto binop; case NOR: s = " || "; binop: shtree(n->nbinary.ch1, ind, NULL, fp); /* if (ind < 0) */ fputs(s, fp); shtree(n->nbinary.ch2, ind, NULL, fp); break; case NCMD: shcmd(n, fp); if (ind >= 0) putc('\n', fp); break; case NPIPE: for (lp = n->npipe.cmdlist; lp; lp = lp->next) { shtree(lp->n, 0, NULL, fp); if (lp->next) fputs(" | ", fp); } if (n->npipe.pipe_backgnd) fputs(" &", fp); if (ind >= 0) putc('\n', fp); break; default: fprintf(fp, "<node type %d>", n->type); if (ind >= 0) putc('\n', fp); break; } } static void showtree(union node *n) { trace_puts("showtree called\n"); shtree(n, 1, NULL, stderr); } #endif /* DEBUG */ /* ============ Parser data */ /* * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up. */ struct strlist { struct strlist *next; char *text; }; struct alias; struct strpush { struct strpush *prev; /* preceding string on stack */ char *prev_string; int prev_left_in_line; #if ENABLE_ASH_ALIAS struct alias *ap; /* if push was associated with an alias */ #endif char *string; /* remember the string since it may change */ }; struct parsefile { struct parsefile *prev; /* preceding file on stack */ int linno; /* current line */ int pf_fd; /* file descriptor (or -1 if string) */ int left_in_line; /* number of chars left in this line */ int left_in_buffer; /* number of chars left in this buffer past the line */ char *next_to_pgetc; /* next char in buffer */ char *buf; /* input buffer */ struct strpush *strpush; /* for pushing strings at this level */ struct strpush basestrpush; /* so pushing one is fast */ }; static struct parsefile basepf; /* top level input file */ static struct parsefile *g_parsefile = &basepf; /* current input file */ static int startlinno; /* line # where last token started */ static char *commandname; /* currently executing command */ static struct strlist *cmdenviron; /* environment for builtin command */ static uint8_t exitstatus; /* exit status of last command */ /* ============ Message printing */ static void ash_vmsg(const char *msg, va_list ap) { fprintf(stderr, "%s: ", arg0); if (commandname) { if (strcmp(arg0, commandname)) fprintf(stderr, "%s: ", commandname); if (!iflag || g_parsefile->pf_fd > 0) fprintf(stderr, "line %d: ", startlinno); } vfprintf(stderr, msg, ap); outcslow('\n', stderr); } /* * Exverror is called to raise the error exception. If the second argument * is not NULL then error prints an error message using printf style * formatting. It then raises the error exception. */ static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN; static void ash_vmsg_and_raise(int cond, const char *msg, va_list ap) { #if DEBUG if (msg) { TRACE(("ash_vmsg_and_raise(%d, \"", cond)); TRACEV((msg, ap)); TRACE(("\") pid=%d\n", getpid())); } else TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid())); if (msg) #endif ash_vmsg(msg, ap); flush_stdout_stderr(); raise_exception(cond); /* NOTREACHED */ } static void ash_msg_and_raise_error(const char *, ...) NORETURN; static void ash_msg_and_raise_error(const char *msg, ...) { va_list ap; va_start(ap, msg); ash_vmsg_and_raise(EXERROR, msg, ap); /* NOTREACHED */ va_end(ap); } static void raise_error_syntax(const char *) NORETURN; static void raise_error_syntax(const char *msg) { ash_msg_and_raise_error("syntax error: %s", msg); /* NOTREACHED */ } static void ash_msg_and_raise(int, const char *, ...) NORETURN; static void ash_msg_and_raise(int cond, const char *msg, ...) { va_list ap; va_start(ap, msg); ash_vmsg_and_raise(cond, msg, ap); /* NOTREACHED */ va_end(ap); } /* * error/warning routines for external builtins */ static void ash_msg(const char *fmt, ...) { va_list ap; va_start(ap, fmt); ash_vmsg(fmt, ap); va_end(ap); } /* * Return a string describing an error. The returned string may be a * pointer to a static buffer that will be overwritten on the next call. * Action describes the operation that got the error. */ static const char * errmsg(int e, const char *em) { if (e == ENOENT || e == ENOTDIR) { return em; } return strerror(e); } /* ============ Memory allocation */ #if 0 /* I consider these wrappers nearly useless: * ok, they return you to nearest exception handler, but * how much memory do you leak in the process, making * memory starvation worse? */ static void * ckrealloc(void * p, size_t nbytes) { p = realloc(p, nbytes); if (!p) ash_msg_and_raise_error(bb_msg_memory_exhausted); return p; } static void * ckmalloc(size_t nbytes) { return ckrealloc(NULL, nbytes); } static void * ckzalloc(size_t nbytes) { return memset(ckmalloc(nbytes), 0, nbytes); } static char * ckstrdup(const char *s) { char *p = strdup(s); if (!p) ash_msg_and_raise_error(bb_msg_memory_exhausted); return p; } #else /* Using bbox equivalents. They exit if out of memory */ # define ckrealloc xrealloc # define ckmalloc xmalloc # define ckzalloc xzalloc # define ckstrdup xstrdup #endif /* * It appears that grabstackstr() will barf with such alignments * because stalloc() will return a string allocated in a new stackblock. */ #define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE) enum { /* Most machines require the value returned from malloc to be aligned * in some way. The following macro will get this right * on many machines. */ SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1, /* Minimum size of a block */ MINSIZE = SHELL_ALIGN(504), }; struct stack_block { struct stack_block *prev; char space[MINSIZE]; }; struct stackmark { struct stack_block *stackp; char *stacknxt; size_t stacknleft; struct stackmark *marknext; }; struct globals_memstack { struct stack_block *g_stackp; // = &stackbase; struct stackmark *markp; char *g_stacknxt; // = stackbase.space; char *sstrend; // = stackbase.space + MINSIZE; size_t g_stacknleft; // = MINSIZE; int herefd; // = -1; struct stack_block stackbase; }; extern struct globals_memstack *const ash_ptr_to_globals_memstack; #define G_memstack (*ash_ptr_to_globals_memstack) #define g_stackp (G_memstack.g_stackp ) #define markp (G_memstack.markp ) #define g_stacknxt (G_memstack.g_stacknxt ) #define sstrend (G_memstack.sstrend ) #define g_stacknleft (G_memstack.g_stacknleft) #define herefd (G_memstack.herefd ) #define stackbase (G_memstack.stackbase ) #define INIT_G_memstack() do { \ (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \ barrier(); \ g_stackp = &stackbase; \ g_stacknxt = stackbase.space; \ g_stacknleft = MINSIZE; \ sstrend = stackbase.space + MINSIZE; \ herefd = -1; \ } while (0) #define stackblock() ((void *)g_stacknxt) #define stackblocksize() g_stacknleft /* * Parse trees for commands are allocated in lifo order, so we use a stack * to make this more efficient, and also to avoid all sorts of exception * handling code to handle interrupts in the middle of a parse. * * The size 504 was chosen because the Ultrix malloc handles that size * well. */ static void * stalloc(size_t nbytes) { char *p; size_t aligned; aligned = SHELL_ALIGN(nbytes); if (aligned > g_stacknleft) { size_t len; size_t blocksize; struct stack_block *sp; blocksize = aligned; if (blocksize < MINSIZE) blocksize = MINSIZE; len = sizeof(struct stack_block) - MINSIZE + blocksize; if (len < blocksize) ash_msg_and_raise_error(bb_msg_memory_exhausted); INT_OFF; sp = ckmalloc(len); sp->prev = g_stackp; g_stacknxt = sp->space; g_stacknleft = blocksize; sstrend = g_stacknxt + blocksize; g_stackp = sp; INT_ON; } p = g_stacknxt; g_stacknxt += aligned; g_stacknleft -= aligned; return p; } static void * stzalloc(size_t nbytes) { return memset(stalloc(nbytes), 0, nbytes); } static void stunalloc(void *p) { #if DEBUG if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) { write(STDERR_FILENO, "stunalloc\n", 10); abort(); } #endif g_stacknleft += g_stacknxt - (char *)p; g_stacknxt = p; } /* * Like strdup but works with the ash stack. */ static char * ststrdup(const char *p) { size_t len = strlen(p) + 1; return memcpy(stalloc(len), p, len); } static void setstackmark(struct stackmark *mark) { mark->stackp = g_stackp; mark->stacknxt = g_stacknxt; mark->stacknleft = g_stacknleft; mark->marknext = markp; markp = mark; } static void popstackmark(struct stackmark *mark) { struct stack_block *sp; if (!mark->stackp) return; INT_OFF; markp = mark->marknext; while (g_stackp != mark->stackp) { sp = g_stackp; g_stackp = sp->prev; free(sp); } g_stacknxt = mark->stacknxt; g_stacknleft = mark->stacknleft; sstrend = mark->stacknxt + mark->stacknleft; INT_ON; } /* * When the parser reads in a string, it wants to stick the string on the * stack and only adjust the stack pointer when it knows how big the * string is. Stackblock (defined in stack.h) returns a pointer to a block * of space on top of the stack and stackblocklen returns the length of * this block. Growstackblock will grow this space by at least one byte, * possibly moving it (like realloc). Grabstackblock actually allocates the * part of the block that has been used. */ static void growstackblock(void) { size_t newlen; newlen = g_stacknleft * 2; if (newlen < g_stacknleft) ash_msg_and_raise_error(bb_msg_memory_exhausted); if (newlen < 128) newlen += 128; if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) { struct stack_block *oldstackp; struct stackmark *xmark; struct stack_block *sp; struct stack_block *prevstackp; size_t grosslen; INT_OFF; oldstackp = g_stackp; sp = g_stackp; prevstackp = sp->prev; grosslen = newlen + sizeof(struct stack_block) - MINSIZE; sp = ckrealloc(sp, grosslen); sp->prev = prevstackp; g_stackp = sp; g_stacknxt = sp->space; g_stacknleft = newlen; sstrend = sp->space + newlen; /* * Stack marks pointing to the start of the old block * must be relocated to point to the new block */ xmark = markp; while (xmark != NULL && xmark->stackp == oldstackp) { xmark->stackp = g_stackp; xmark->stacknxt = g_stacknxt; xmark->stacknleft = g_stacknleft; xmark = xmark->marknext; } INT_ON; } else { char *oldspace = g_stacknxt; size_t oldlen = g_stacknleft; char *p = stalloc(newlen); /* free the space we just allocated */ g_stacknxt = memcpy(p, oldspace, oldlen); g_stacknleft += newlen; } } static void grabstackblock(size_t len) { len = SHELL_ALIGN(len); g_stacknxt += len; g_stacknleft -= len; } /* * The following routines are somewhat easier to use than the above. * The user declares a variable of type STACKSTR, which may be declared * to be a register. The macro STARTSTACKSTR initializes things. Then * the user uses the macro STPUTC to add characters to the string. In * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is * grown as necessary. When the user is done, she can just leave the * string there and refer to it using stackblock(). Or she can allocate * the space for it using grabstackstr(). If it is necessary to allow * someone else to use the stack temporarily and then continue to grow * the string, the user should use grabstack to allocate the space, and * then call ungrabstr(p) to return to the previous mode of operation. * * USTPUTC is like STPUTC except that it doesn't check for overflow. * CHECKSTACKSPACE can be called before USTPUTC to ensure that there * is space for at least one character. */ static void * growstackstr(void) { size_t len = stackblocksize(); if (herefd >= 0 && len >= 1024) { full_write(herefd, stackblock(), len); return stackblock(); } growstackblock(); return (char *)stackblock() + len; } /* * Called from CHECKSTRSPACE. */ static char * makestrspace(size_t newlen, char *p) { size_t len = p - g_stacknxt; size_t size = stackblocksize(); for (;;) { size_t nleft; size = stackblocksize(); nleft = size - len; if (nleft >= newlen) break; growstackblock(); } return (char *)stackblock() + len; } static char * stack_nputstr(const char *s, size_t n, char *p) { p = makestrspace(n, p); p = (char *)memcpy(p, s, n) + n; return p; } static char * stack_putstr(const char *s, char *p) { return stack_nputstr(s, strlen(s), p); } static char * _STPUTC(int c, char *p) { if (p == sstrend) p = growstackstr(); *p++ = c; return p; } #define STARTSTACKSTR(p) ((p) = stackblock()) #define STPUTC(c, p) ((p) = _STPUTC((c), (p))) #define CHECKSTRSPACE(n, p) do { \ char *q = (p); \ size_t l = (n); \ size_t m = sstrend - q; \ if (l > m) \ (p) = makestrspace(l, q); \ } while (0) #define USTPUTC(c, p) (*(p)++ = (c)) #define STACKSTRNUL(p) do { \ if ((p) == sstrend) \ (p) = growstackstr(); \ *(p) = '\0'; \ } while (0) #define STUNPUTC(p) (--(p)) #define STTOPC(p) ((p)[-1]) #define STADJUST(amount, p) ((p) += (amount)) #define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock()) #define ungrabstackstr(s, p) stunalloc(s) #define stackstrend() ((void *)sstrend) /* ============ String helpers */ /* * prefix -- see if pfx is a prefix of string. */ static char * prefix(const char *string, const char *pfx) { while (*pfx) { if (*pfx++ != *string++) return NULL; } return (char *) string; } /* * Check for a valid number. This should be elsewhere. */ static int is_number(const char *p) { do { if (!isdigit(*p)) return 0; } while (*++p != '\0'); return 1; } /* * Convert a string of digits to an integer, printing an error message on * failure. */ static int number(const char *s) { if (!is_number(s)) ash_msg_and_raise_error(msg_illnum, s); return atoi(s); } /* * Produce a possibly single quoted string suitable as input to the shell. * The return string is allocated on the stack. */ static char * single_quote(const char *s) { char *p; STARTSTACKSTR(p); do { char *q; size_t len; len = strchrnul(s, '\'') - s; q = p = makestrspace(len + 3, p); *q++ = '\''; q = (char *)memcpy(q, s, len) + len; *q++ = '\''; s += len; STADJUST(q - p, p); if (*s != '\'') break; len = 0; do len++; while (*++s == '\''); q = p = makestrspace(len + 3, p); *q++ = '"'; q = (char *)memcpy(q, s - len, len) + len; *q++ = '"'; STADJUST(q - p, p); } while (*s); USTPUTC('\0', p); return stackblock(); } /* ============ nextopt */ static char **argptr; /* argument list for builtin commands */ static char *optionarg; /* set by nextopt (like getopt) */ static char *optptr; /* used by nextopt */ /* * XXX - should get rid of. Have all builtins use getopt(3). * The library getopt must have the BSD extension static variable * "optreset", otherwise it can't be used within the shell safely. * * Standard option processing (a la getopt) for builtin routines. * The only argument that is passed to nextopt is the option string; * the other arguments are unnecessary. It returns the character, * or '\0' on end of input. */ static int nextopt(const char *optstring) { char *p; const char *q; char c; p = optptr; if (p == NULL || *p == '\0') { /* We ate entire "-param", take next one */ p = *argptr; if (p == NULL) return '\0'; if (*p != '-') return '\0'; if (*++p == '\0') /* just "-" ? */ return '\0'; argptr++; if (LONE_DASH(p)) /* "--" ? */ return '\0'; /* p => next "-param" */ } /* p => some option char in the middle of a "-param" */ c = *p++; for (q = optstring; *q != c;) { if (*q == '\0') ash_msg_and_raise_error("illegal option -%c", c); if (*++q == ':') q++; } if (*++q == ':') { if (*p == '\0') { p = *argptr++; if (p == NULL) ash_msg_and_raise_error("no arg for -%c option", c); } optionarg = p; p = NULL; } optptr = p; return c; } /* ============ Shell variables */ /* * The parsefile structure pointed to by the global variable parsefile * contains information about the current file being read. */ struct shparam { int nparam; /* # of positional parameters (without $0) */ #if ENABLE_ASH_GETOPTS int optind; /* next parameter to be processed by getopts */ int optoff; /* used by getopts */ #endif unsigned char malloced; /* if parameter list dynamically allocated */ char **p; /* parameter list */ }; /* * Free the list of positional parameters. */ static void freeparam(volatile struct shparam *param) { if (param->malloced) { char **ap, **ap1; ap = ap1 = param->p; while (*ap) free(*ap++); free(ap1); } } #if ENABLE_ASH_GETOPTS static void FAST_FUNC getoptsreset(const char *value); #endif struct var { struct var *next; /* next entry in hash list */ int flags; /* flags are defined above */ const char *var_text; /* name=value */ void (*var_func)(const char *) FAST_FUNC; /* function to be called when */ /* the variable gets set/unset */ }; struct localvar { struct localvar *next; /* next local variable in list */ struct var *vp; /* the variable that was made local */ int flags; /* saved flags */ const char *text; /* saved text */ }; /* flags */ #define VEXPORT 0x01 /* variable is exported */ #define VREADONLY 0x02 /* variable cannot be modified */ #define VSTRFIXED 0x04 /* variable struct is statically allocated */ #define VTEXTFIXED 0x08 /* text is statically allocated */ #define VSTACK 0x10 /* text is allocated on the stack */ #define VUNSET 0x20 /* the variable is not set */ #define VNOFUNC 0x40 /* don't call the callback function */ #define VNOSET 0x80 /* do not set variable - just readonly test */ #define VNOSAVE 0x100 /* when text is on the heap before setvareq */ #if ENABLE_ASH_RANDOM_SUPPORT # define VDYNAMIC 0x200 /* dynamic variable */ #else # define VDYNAMIC 0 #endif /* Need to be before varinit_data[] */ #if ENABLE_LOCALE_SUPPORT static void FAST_FUNC change_lc_all(const char *value) { if (value && *value != '\0') setlocale(LC_ALL, value); } static void FAST_FUNC change_lc_ctype(const char *value) { if (value && *value != '\0') setlocale(LC_CTYPE, value); } #endif #if ENABLE_ASH_MAIL static void chkmail(void); static void changemail(const char *var_value) FAST_FUNC; #else # define chkmail() ((void)0) #endif static void changepath(const char *) FAST_FUNC; #if ENABLE_ASH_RANDOM_SUPPORT static void change_random(const char *) FAST_FUNC; #endif static const struct { int flags; const char *var_text; void (*var_func)(const char *) FAST_FUNC; } varinit_data[] = { /* * Note: VEXPORT would not work correctly here for NOFORK applets: * some environment strings may be constant. */ { VSTRFIXED|VTEXTFIXED , defifsvar , NULL }, #if ENABLE_ASH_MAIL { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail }, { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail }, #endif { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath }, { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL }, { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL }, { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL }, #if ENABLE_ASH_GETOPTS { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset }, #endif #if ENABLE_ASH_RANDOM_SUPPORT { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random }, #endif #if ENABLE_LOCALE_SUPPORT { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all }, { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype }, #endif #if ENABLE_FEATURE_EDITING_SAVEHISTORY { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL }, #endif }; struct redirtab; struct globals_var { struct shparam shellparam; /* $@ current positional parameters */ struct redirtab *redirlist; int g_nullredirs; int preverrout_fd; /* save fd2 before print debug if xflag is set. */ struct var *vartab[VTABSIZE]; struct var varinit[ARRAY_SIZE(varinit_data)]; }; extern struct globals_var *const ash_ptr_to_globals_var; #define G_var (*ash_ptr_to_globals_var) #define shellparam (G_var.shellparam ) //#define redirlist (G_var.redirlist ) #define g_nullredirs (G_var.g_nullredirs ) #define preverrout_fd (G_var.preverrout_fd) #define vartab (G_var.vartab ) #define varinit (G_var.varinit ) #define INIT_G_var() do { \ unsigned i; \ (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \ barrier(); \ for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \ varinit[i].flags = varinit_data[i].flags; \ varinit[i].var_text = varinit_data[i].var_text; \ varinit[i].var_func = varinit_data[i].var_func; \ } \ } while (0) #define vifs varinit[0] #if ENABLE_ASH_MAIL # define vmail (&vifs)[1] # define vmpath (&vmail)[1] # define vpath (&vmpath)[1] #else # define vpath (&vifs)[1] #endif #define vps1 (&vpath)[1] #define vps2 (&vps1)[1] #define vps4 (&vps2)[1] #if ENABLE_ASH_GETOPTS # define voptind (&vps4)[1] # if ENABLE_ASH_RANDOM_SUPPORT # define vrandom (&voptind)[1] # endif #else # if ENABLE_ASH_RANDOM_SUPPORT # define vrandom (&vps4)[1] # endif #endif /* * The following macros access the values of the above variables. * They have to skip over the name. They return the null string * for unset variables. */ #define ifsval() (vifs.var_text + 4) #define ifsset() ((vifs.flags & VUNSET) == 0) #if ENABLE_ASH_MAIL # define mailval() (vmail.var_text + 5) # define mpathval() (vmpath.var_text + 9) # define mpathset() ((vmpath.flags & VUNSET) == 0) #endif #define pathval() (vpath.var_text + 5) #define ps1val() (vps1.var_text + 4) #define ps2val() (vps2.var_text + 4) #define ps4val() (vps4.var_text + 4) #if ENABLE_ASH_GETOPTS # define optindval() (voptind.var_text + 7) #endif #if ENABLE_ASH_GETOPTS static void FAST_FUNC getoptsreset(const char *value) { shellparam.optind = number(value); shellparam.optoff = -1; } #endif /* * Compares two strings up to the first = or '\0'. The first * string must be terminated by '='; the second may be terminated by * either '=' or '\0'. */ static int varcmp(const char *p, const char *q) { int c, d; while ((c = *p) == (d = *q)) { if (!c || c == '=') goto out; p++; q++; } if (c == '=') c = '\0'; if (d == '=') d = '\0'; out: return c - d; } /* * Find the appropriate entry in the hash table from the name. */ static struct var ** hashvar(const char *p) { unsigned hashval; hashval = ((unsigned char) *p) << 4; while (*p && *p != '=') hashval += (unsigned char) *p++; return &vartab[hashval % VTABSIZE]; } static int vpcmp(const void *a, const void *b) { return varcmp(*(const char **)a, *(const char **)b); } /* * This routine initializes the builtin variables. */ static void initvar(void) { struct var *vp; struct var *end; struct var **vpp; /* * PS1 depends on uid */ #if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT vps1.var_text = "PS1=\\w \\$ "; #else if (!geteuid()) vps1.var_text = "PS1=# "; #endif vp = varinit; end = vp + ARRAY_SIZE(varinit); do { vpp = hashvar(vp->var_text); vp->next = *vpp; *vpp = vp; } while (++vp < end); } static struct var ** findvar(struct var **vpp, const char *name) { for (; *vpp; vpp = &(*vpp)->next) { if (varcmp((*vpp)->var_text, name) == 0) { break; } } return vpp; } /* * Find the value of a variable. Returns NULL if not set. */ static const char* FAST_FUNC lookupvar(const char *name) { struct var *v; v = *findvar(hashvar(name), name); if (v) { #if ENABLE_ASH_RANDOM_SUPPORT /* * Dynamic variables are implemented roughly the same way they are * in bash. Namely, they're "special" so long as they aren't unset. * As soon as they're unset, they're no longer dynamic, and dynamic * lookup will no longer happen at that point. -- PFM. */ if (v->flags & VDYNAMIC) v->var_func(NULL); #endif if (!(v->flags & VUNSET)) return var_end(v->var_text); } return NULL; } /* * Search the environment of a builtin command. */ static const char * bltinlookup(const char *name) { struct strlist *sp; for (sp = cmdenviron; sp; sp = sp->next) { if (varcmp(sp->text, name) == 0) return var_end(sp->text); } return lookupvar(name); } /* * Same as setvar except that the variable and value are passed in * the first argument as name=value. Since the first argument will * be actually stored in the table, it should not be a string that * will go away. * Called with interrupts off. */ static void setvareq(char *s, int flags) { struct var *vp, **vpp; vpp = hashvar(s); flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); vp = *findvar(vpp, s); if (vp) { if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) { const char *n; if (flags & VNOSAVE) free(s); n = vp->var_text; ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n); } if (flags & VNOSET) return; if (vp->var_func && !(flags & VNOFUNC)) vp->var_func(var_end(s)); if (!(vp->flags & (VTEXTFIXED|VSTACK))) free((char*)vp->var_text); flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); } else { /* variable s is not found */ if (flags & VNOSET) return; vp = ckzalloc(sizeof(*vp)); vp->next = *vpp; /*vp->func = NULL; - ckzalloc did it */ *vpp = vp; } if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE))) s = ckstrdup(s); vp->var_text = s; vp->flags = flags; } /* * Set the value of a variable. The flags argument is ored with the * flags of the variable. If val is NULL, the variable is unset. */ static void setvar(const char *name, const char *val, int flags) { const char *q; char *p; char *nameeq; size_t namelen; size_t vallen; q = endofname(name); p = strchrnul(q, '='); namelen = p - name; if (!namelen || p != q) ash_msg_and_raise_error("%.*s: bad variable name", namelen, name); vallen = 0; if (val == NULL) { flags |= VUNSET; } else { vallen = strlen(val); } INT_OFF; nameeq = ckmalloc(namelen + vallen + 2); p = memcpy(nameeq, name, namelen) + namelen; if (val) { *p++ = '='; p = memcpy(p, val, vallen) + vallen; } *p = '\0'; setvareq(nameeq, flags | VNOSAVE); INT_ON; } static void FAST_FUNC setvar2(const char *name, const char *val) { setvar(name, val, 0); } #if ENABLE_ASH_GETOPTS /* * Safe version of setvar, returns 1 on success 0 on failure. */ static int setvarsafe(const char *name, const char *val, int flags) { int err; volatile int saveint; struct jmploc *volatile savehandler = exception_handler; struct jmploc jmploc; SAVE_INT(saveint); if (setjmp(jmploc.loc)) err = 1; else { exception_handler = &jmploc; setvar(name, val, flags); err = 0; } exception_handler = savehandler; RESTORE_INT(saveint); return err; } #endif /* * Unset the specified variable. */ static int unsetvar(const char *s) { struct var **vpp; struct var *vp; int retval; vpp = findvar(hashvar(s), s); vp = *vpp; retval = 2; if (vp) { int flags = vp->flags; retval = 1; if (flags & VREADONLY) goto out; #if ENABLE_ASH_RANDOM_SUPPORT vp->flags &= ~VDYNAMIC; #endif if (flags & VUNSET) goto ok; if ((flags & VSTRFIXED) == 0) { INT_OFF; if ((flags & (VTEXTFIXED|VSTACK)) == 0) free((char*)vp->var_text); *vpp = vp->next; free(vp); INT_ON; } else { setvar2(s, 0); vp->flags &= ~VEXPORT; } ok: retval = 0; } out: return retval; } /* * Process a linked list of variable assignments. */ static void listsetvar(struct strlist *list_set_var, int flags) { struct strlist *lp = list_set_var; if (!lp) return; INT_OFF; do { setvareq(lp->text, flags); lp = lp->next; } while (lp); INT_ON; } /* * Generate a list of variables satisfying the given conditions. */ static char ** listvars(int on, int off, char ***end) { struct var **vpp; struct var *vp; char **ep; int mask; STARTSTACKSTR(ep); vpp = vartab; mask = on | off; do { for (vp = *vpp; vp; vp = vp->next) { if ((vp->flags & mask) == on) { if (ep == stackstrend()) ep = growstackstr(); *ep++ = (char*)vp->var_text; } } } while (++vpp < vartab + VTABSIZE); if (ep == stackstrend()) ep = growstackstr(); if (end) *end = ep; *ep++ = NULL; return grabstackstr(ep); } /* ============ Path search helper * * The variable path (passed by reference) should be set to the start * of the path before the first call; path_advance will update * this value as it proceeds. Successive calls to path_advance will return * the possible path expansions in sequence. If an option (indicated by * a percent sign) appears in the path entry then the global variable * pathopt will be set to point to it; otherwise pathopt will be set to * NULL. */ static const char *pathopt; /* set by path_advance */ static char * path_advance(const char **path, const char *name) { const char *p; char *q; const char *start; size_t len; if (*path == NULL) return NULL; start = *path; for (p = start; *p && *p != ':' && *p != '%'; p++) continue; len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ while (stackblocksize() < len) growstackblock(); q = stackblock(); if (p != start) { memcpy(q, start, p - start); q += p - start; *q++ = '/'; } strcpy(q, name); pathopt = NULL; if (*p == '%') { pathopt = ++p; while (*p && *p != ':') p++; } if (*p == ':') *path = p + 1; else *path = NULL; return stalloc(len); } /* ============ Prompt */ static smallint doprompt; /* if set, prompt the user */ static smallint needprompt; /* true if interactive and at start of line */ #if ENABLE_FEATURE_EDITING static line_input_t *line_input_state; static const char *cmdedit_prompt; static void putprompt(const char *s) { if (ENABLE_ASH_EXPAND_PRMT) { free((char*)cmdedit_prompt); cmdedit_prompt = ckstrdup(s); return; } cmdedit_prompt = s; } #else static void putprompt(const char *s) { out2str(s); } #endif #if ENABLE_ASH_EXPAND_PRMT /* expandstr() needs parsing machinery, so it is far away ahead... */ static const char *expandstr(const char *ps); #else #define expandstr(s) s #endif static void setprompt_if(smallint do_set, int whichprompt) { const char *prompt; IF_ASH_EXPAND_PRMT(struct stackmark smark;) if (!do_set) return; needprompt = 0; switch (whichprompt) { case 1: prompt = ps1val(); break; case 2: prompt = ps2val(); break; default: /* 0 */ prompt = nullstr; } #if ENABLE_ASH_EXPAND_PRMT setstackmark(&smark); stalloc(stackblocksize()); #endif putprompt(expandstr(prompt)); #if ENABLE_ASH_EXPAND_PRMT popstackmark(&smark); #endif } /* ============ The cd and pwd commands */ #define CD_PHYSICAL 1 #define CD_PRINT 2 static int cdopt(void) { int flags = 0; int i, j; j = 'L'; while ((i = nextopt("LP")) != '\0') { if (i != j) { flags ^= CD_PHYSICAL; j = i; } } return flags; } /* * Update curdir (the name of the current directory) in response to a * cd command. */ static const char * updatepwd(const char *dir) { char *new; char *p; char *cdcomppath; const char *lim; cdcomppath = ststrdup(dir); STARTSTACKSTR(new); if (*dir != '/') { if (curdir == nullstr) return 0; new = stack_putstr(curdir, new); } new = makestrspace(strlen(dir) + 2, new); lim = (char *)stackblock() + 1; if (*dir != '/') { if (new[-1] != '/') USTPUTC('/', new); if (new > lim && *lim == '/') lim++; } else { USTPUTC('/', new); cdcomppath++; if (dir[1] == '/' && dir[2] != '/') { USTPUTC('/', new); cdcomppath++; lim++; } } p = strtok(cdcomppath, "/"); while (p) { switch (*p) { case '.': if (p[1] == '.' && p[2] == '\0') { while (new > lim) { STUNPUTC(new); if (new[-1] == '/') break; } break; } if (p[1] == '\0') break; /* fall through */ default: new = stack_putstr(p, new); USTPUTC('/', new); } p = strtok(0, "/"); } if (new > lim) STUNPUTC(new); *new = 0; return stackblock(); } /* * Find out what the current directory is. If we already know the current * directory, this routine returns immediately. */ static char * getpwd(void) { char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */ return dir ? dir : nullstr; } static void setpwd(const char *val, int setold) { char *oldcur, *dir; oldcur = dir = curdir; if (setold) { setvar("OLDPWD", oldcur, VEXPORT); } INT_OFF; if (physdir != nullstr) { if (physdir != oldcur) free(physdir); physdir = nullstr; } if (oldcur == val || !val) { char *s = getpwd(); physdir = s; if (!val) dir = s; } else dir = ckstrdup(val); if (oldcur != dir && oldcur != nullstr) { free(oldcur); } curdir = dir; INT_ON; setvar("PWD", dir, VEXPORT); } static void hashcd(void); /* * Actually do the chdir. We also call hashcd to let the routines in exec.c * know that the current directory has changed. */ static int docd(const char *dest, int flags) { const char *dir = NULL; int err; TRACE(("docd(\"%s\", %d) called\n", dest, flags)); INT_OFF; if (!(flags & CD_PHYSICAL)) { dir = updatepwd(dest); if (dir) dest = dir; } err = chdir(dest); if (err) goto out; setpwd(dir, 1); hashcd(); out: INT_ON; return err; } static int FAST_FUNC cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { const char *dest; const char *path; const char *p; char c; struct stat statb; int flags; flags = cdopt(); dest = *argptr; if (!dest) dest = bltinlookup("HOME"); else if (LONE_DASH(dest)) { dest = bltinlookup("OLDPWD"); flags |= CD_PRINT; } if (!dest) dest = nullstr; if (*dest == '/') goto step7; if (*dest == '.') { c = dest[1]; dotdot: switch (c) { case '\0': case '/': goto step6; case '.': c = dest[2]; if (c != '.') goto dotdot; } } if (!*dest) dest = "."; path = bltinlookup("CDPATH"); if (!path) { step6: step7: p = dest; goto docd; } do { c = *path; p = path_advance(&path, dest); if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { if (c && c != ':') flags |= CD_PRINT; docd: if (!docd(p, flags)) goto out; break; } } while (path); ash_msg_and_raise_error("can't cd to %s", dest); /* NOTREACHED */ out: if (flags & CD_PRINT) out1fmt("%s\n", curdir); return 0; } static int FAST_FUNC pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int flags; const char *dir = curdir; flags = cdopt(); if (flags) { if (physdir == nullstr) setpwd(dir, 0); dir = physdir; } out1fmt("%s\n", dir); return 0; } /* ============ ... */ #define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024) /* Syntax classes */ #define CWORD 0 /* character is nothing special */ #define CNL 1 /* newline character */ #define CBACK 2 /* a backslash character */ #define CSQUOTE 3 /* single quote */ #define CDQUOTE 4 /* double quote */ #define CENDQUOTE 5 /* a terminating quote */ #define CBQUOTE 6 /* backwards single quote */ #define CVAR 7 /* a dollar sign */ #define CENDVAR 8 /* a '}' character */ #define CLP 9 /* a left paren in arithmetic */ #define CRP 10 /* a right paren in arithmetic */ #define CENDFILE 11 /* end of file */ #define CCTL 12 /* like CWORD, except it must be escaped */ #define CSPCL 13 /* these terminate a word */ #define CIGN 14 /* character should be ignored */ #define PEOF 256 #if ENABLE_ASH_ALIAS # define PEOA 257 #endif #define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE #if ENABLE_SH_MATH_SUPPORT # define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12)) #else # define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8)) #endif static const uint16_t S_I_T[] = { #if ENABLE_ASH_ALIAS SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */ #endif SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */ SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */ SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */ SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */ SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */ SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */ SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */ SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */ SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */ SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */ SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */ #if !USE_SIT_FUNCTION SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */ SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */ SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */ #endif #undef SIT_ITEM }; /* Constants below must match table above */ enum { #if ENABLE_ASH_ALIAS CSPCL_CIGN_CIGN_CIGN , /* 0 */ #endif CSPCL_CWORD_CWORD_CWORD , /* 1 */ CNL_CNL_CNL_CNL , /* 2 */ CWORD_CCTL_CCTL_CWORD , /* 3 */ CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */ CVAR_CVAR_CWORD_CVAR , /* 5 */ CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */ CSPCL_CWORD_CWORD_CLP , /* 7 */ CSPCL_CWORD_CWORD_CRP , /* 8 */ CBACK_CBACK_CCTL_CBACK , /* 9 */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */ CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */ CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */ CWORD_CWORD_CWORD_CWORD , /* 13 */ CCTL_CCTL_CCTL_CCTL , /* 14 */ }; /* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF, * caller must ensure proper cast on it if c is *char_ptr! */ /* Values for syntax param */ #define BASESYNTAX 0 /* not in quotes */ #define DQSYNTAX 1 /* in double quotes */ #define SQSYNTAX 2 /* in single quotes */ #define ARISYNTAX 3 /* in arithmetic */ #define PSSYNTAX 4 /* prompt. never passed to SIT() */ #if USE_SIT_FUNCTION static int SIT(int c, int syntax) { static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~"; # if ENABLE_ASH_ALIAS static const uint8_t syntax_index_table[] ALIGN1 = { 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */ 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */ 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */ 11, 3 /* "}~" */ }; # else static const uint8_t syntax_index_table[] ALIGN1 = { 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */ 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */ 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */ 10, 2 /* "}~" */ }; # endif const char *s; int indx; if (c == PEOF) return CENDFILE; # if ENABLE_ASH_ALIAS if (c == PEOA) indx = 0; else # endif { /* Cast is purely for paranoia here, * just in case someone passed signed char to us */ if ((unsigned char)c >= CTL_FIRST && (unsigned char)c <= CTL_LAST ) { return CCTL; } s = strchrnul(spec_symbls, c); if (*s == '\0') return CWORD; indx = syntax_index_table[s - spec_symbls]; } return (S_I_T[indx] >> (syntax*4)) & 0xf; } #else /* !USE_SIT_FUNCTION */ static const uint8_t syntax_index_table[] = { /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */ /* 0 */ CWORD_CWORD_CWORD_CWORD, /* 1 */ CWORD_CWORD_CWORD_CWORD, /* 2 */ CWORD_CWORD_CWORD_CWORD, /* 3 */ CWORD_CWORD_CWORD_CWORD, /* 4 */ CWORD_CWORD_CWORD_CWORD, /* 5 */ CWORD_CWORD_CWORD_CWORD, /* 6 */ CWORD_CWORD_CWORD_CWORD, /* 7 */ CWORD_CWORD_CWORD_CWORD, /* 8 */ CWORD_CWORD_CWORD_CWORD, /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD, /* 10 "\n" */ CNL_CNL_CNL_CNL, /* 11 */ CWORD_CWORD_CWORD_CWORD, /* 12 */ CWORD_CWORD_CWORD_CWORD, /* 13 */ CWORD_CWORD_CWORD_CWORD, /* 14 */ CWORD_CWORD_CWORD_CWORD, /* 15 */ CWORD_CWORD_CWORD_CWORD, /* 16 */ CWORD_CWORD_CWORD_CWORD, /* 17 */ CWORD_CWORD_CWORD_CWORD, /* 18 */ CWORD_CWORD_CWORD_CWORD, /* 19 */ CWORD_CWORD_CWORD_CWORD, /* 20 */ CWORD_CWORD_CWORD_CWORD, /* 21 */ CWORD_CWORD_CWORD_CWORD, /* 22 */ CWORD_CWORD_CWORD_CWORD, /* 23 */ CWORD_CWORD_CWORD_CWORD, /* 24 */ CWORD_CWORD_CWORD_CWORD, /* 25 */ CWORD_CWORD_CWORD_CWORD, /* 26 */ CWORD_CWORD_CWORD_CWORD, /* 27 */ CWORD_CWORD_CWORD_CWORD, /* 28 */ CWORD_CWORD_CWORD_CWORD, /* 29 */ CWORD_CWORD_CWORD_CWORD, /* 30 */ CWORD_CWORD_CWORD_CWORD, /* 31 */ CWORD_CWORD_CWORD_CWORD, /* 32 " " */ CSPCL_CWORD_CWORD_CWORD, /* 33 "!" */ CWORD_CCTL_CCTL_CWORD, /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD, /* 35 "#" */ CWORD_CWORD_CWORD_CWORD, /* 36 "$" */ CVAR_CVAR_CWORD_CVAR, /* 37 "%" */ CWORD_CWORD_CWORD_CWORD, /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD, /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD, /* 40 "(" */ CSPCL_CWORD_CWORD_CLP, /* 41 ")" */ CSPCL_CWORD_CWORD_CRP, /* 42 "*" */ CWORD_CCTL_CCTL_CWORD, /* 43 "+" */ CWORD_CWORD_CWORD_CWORD, /* 44 "," */ CWORD_CWORD_CWORD_CWORD, /* 45 "-" */ CWORD_CCTL_CCTL_CWORD, /* 46 "." */ CWORD_CWORD_CWORD_CWORD, /* 47 "/" */ CWORD_CCTL_CCTL_CWORD, /* 48 "0" */ CWORD_CWORD_CWORD_CWORD, /* 49 "1" */ CWORD_CWORD_CWORD_CWORD, /* 50 "2" */ CWORD_CWORD_CWORD_CWORD, /* 51 "3" */ CWORD_CWORD_CWORD_CWORD, /* 52 "4" */ CWORD_CWORD_CWORD_CWORD, /* 53 "5" */ CWORD_CWORD_CWORD_CWORD, /* 54 "6" */ CWORD_CWORD_CWORD_CWORD, /* 55 "7" */ CWORD_CWORD_CWORD_CWORD, /* 56 "8" */ CWORD_CWORD_CWORD_CWORD, /* 57 "9" */ CWORD_CWORD_CWORD_CWORD, /* 58 ":" */ CWORD_CCTL_CCTL_CWORD, /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD, /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD, /* 61 "=" */ CWORD_CCTL_CCTL_CWORD, /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD, /* 63 "?" */ CWORD_CCTL_CCTL_CWORD, /* 64 "@" */ CWORD_CWORD_CWORD_CWORD, /* 65 "A" */ CWORD_CWORD_CWORD_CWORD, /* 66 "B" */ CWORD_CWORD_CWORD_CWORD, /* 67 "C" */ CWORD_CWORD_CWORD_CWORD, /* 68 "D" */ CWORD_CWORD_CWORD_CWORD, /* 69 "E" */ CWORD_CWORD_CWORD_CWORD, /* 70 "F" */ CWORD_CWORD_CWORD_CWORD, /* 71 "G" */ CWORD_CWORD_CWORD_CWORD, /* 72 "H" */ CWORD_CWORD_CWORD_CWORD, /* 73 "I" */ CWORD_CWORD_CWORD_CWORD, /* 74 "J" */ CWORD_CWORD_CWORD_CWORD, /* 75 "K" */ CWORD_CWORD_CWORD_CWORD, /* 76 "L" */ CWORD_CWORD_CWORD_CWORD, /* 77 "M" */ CWORD_CWORD_CWORD_CWORD, /* 78 "N" */ CWORD_CWORD_CWORD_CWORD, /* 79 "O" */ CWORD_CWORD_CWORD_CWORD, /* 80 "P" */ CWORD_CWORD_CWORD_CWORD, /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD, /* 82 "R" */ CWORD_CWORD_CWORD_CWORD, /* 83 "S" */ CWORD_CWORD_CWORD_CWORD, /* 84 "T" */ CWORD_CWORD_CWORD_CWORD, /* 85 "U" */ CWORD_CWORD_CWORD_CWORD, /* 86 "V" */ CWORD_CWORD_CWORD_CWORD, /* 87 "W" */ CWORD_CWORD_CWORD_CWORD, /* 88 "X" */ CWORD_CWORD_CWORD_CWORD, /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD, /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD, /* 91 "[" */ CWORD_CCTL_CCTL_CWORD, /* 92 "\" */ CBACK_CBACK_CCTL_CBACK, /* 93 "]" */ CWORD_CCTL_CCTL_CWORD, /* 94 "^" */ CWORD_CWORD_CWORD_CWORD, /* 95 "_" */ CWORD_CWORD_CWORD_CWORD, /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE, /* 97 "a" */ CWORD_CWORD_CWORD_CWORD, /* 98 "b" */ CWORD_CWORD_CWORD_CWORD, /* 99 "c" */ CWORD_CWORD_CWORD_CWORD, /* 100 "d" */ CWORD_CWORD_CWORD_CWORD, /* 101 "e" */ CWORD_CWORD_CWORD_CWORD, /* 102 "f" */ CWORD_CWORD_CWORD_CWORD, /* 103 "g" */ CWORD_CWORD_CWORD_CWORD, /* 104 "h" */ CWORD_CWORD_CWORD_CWORD, /* 105 "i" */ CWORD_CWORD_CWORD_CWORD, /* 106 "j" */ CWORD_CWORD_CWORD_CWORD, /* 107 "k" */ CWORD_CWORD_CWORD_CWORD, /* 108 "l" */ CWORD_CWORD_CWORD_CWORD, /* 109 "m" */ CWORD_CWORD_CWORD_CWORD, /* 110 "n" */ CWORD_CWORD_CWORD_CWORD, /* 111 "o" */ CWORD_CWORD_CWORD_CWORD, /* 112 "p" */ CWORD_CWORD_CWORD_CWORD, /* 113 "q" */ CWORD_CWORD_CWORD_CWORD, /* 114 "r" */ CWORD_CWORD_CWORD_CWORD, /* 115 "s" */ CWORD_CWORD_CWORD_CWORD, /* 116 "t" */ CWORD_CWORD_CWORD_CWORD, /* 117 "u" */ CWORD_CWORD_CWORD_CWORD, /* 118 "v" */ CWORD_CWORD_CWORD_CWORD, /* 119 "w" */ CWORD_CWORD_CWORD_CWORD, /* 120 "x" */ CWORD_CWORD_CWORD_CWORD, /* 121 "y" */ CWORD_CWORD_CWORD_CWORD, /* 122 "z" */ CWORD_CWORD_CWORD_CWORD, /* 123 "{" */ CWORD_CWORD_CWORD_CWORD, /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD, /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR, /* 126 "~" */ CWORD_CCTL_CCTL_CWORD, /* 127 del */ CWORD_CWORD_CWORD_CWORD, /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD, /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL, /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL, /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL, /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL, /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL, /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL, /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL, /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL, /* 137 */ CWORD_CWORD_CWORD_CWORD, /* 138 */ CWORD_CWORD_CWORD_CWORD, /* 139 */ CWORD_CWORD_CWORD_CWORD, /* 140 */ CWORD_CWORD_CWORD_CWORD, /* 141 */ CWORD_CWORD_CWORD_CWORD, /* 142 */ CWORD_CWORD_CWORD_CWORD, /* 143 */ CWORD_CWORD_CWORD_CWORD, /* 144 */ CWORD_CWORD_CWORD_CWORD, /* 145 */ CWORD_CWORD_CWORD_CWORD, /* 146 */ CWORD_CWORD_CWORD_CWORD, /* 147 */ CWORD_CWORD_CWORD_CWORD, /* 148 */ CWORD_CWORD_CWORD_CWORD, /* 149 */ CWORD_CWORD_CWORD_CWORD, /* 150 */ CWORD_CWORD_CWORD_CWORD, /* 151 */ CWORD_CWORD_CWORD_CWORD, /* 152 */ CWORD_CWORD_CWORD_CWORD, /* 153 */ CWORD_CWORD_CWORD_CWORD, /* 154 */ CWORD_CWORD_CWORD_CWORD, /* 155 */ CWORD_CWORD_CWORD_CWORD, /* 156 */ CWORD_CWORD_CWORD_CWORD, /* 157 */ CWORD_CWORD_CWORD_CWORD, /* 158 */ CWORD_CWORD_CWORD_CWORD, /* 159 */ CWORD_CWORD_CWORD_CWORD, /* 160 */ CWORD_CWORD_CWORD_CWORD, /* 161 */ CWORD_CWORD_CWORD_CWORD, /* 162 */ CWORD_CWORD_CWORD_CWORD, /* 163 */ CWORD_CWORD_CWORD_CWORD, /* 164 */ CWORD_CWORD_CWORD_CWORD, /* 165 */ CWORD_CWORD_CWORD_CWORD, /* 166 */ CWORD_CWORD_CWORD_CWORD, /* 167 */ CWORD_CWORD_CWORD_CWORD, /* 168 */ CWORD_CWORD_CWORD_CWORD, /* 169 */ CWORD_CWORD_CWORD_CWORD, /* 170 */ CWORD_CWORD_CWORD_CWORD, /* 171 */ CWORD_CWORD_CWORD_CWORD, /* 172 */ CWORD_CWORD_CWORD_CWORD, /* 173 */ CWORD_CWORD_CWORD_CWORD, /* 174 */ CWORD_CWORD_CWORD_CWORD, /* 175 */ CWORD_CWORD_CWORD_CWORD, /* 176 */ CWORD_CWORD_CWORD_CWORD, /* 177 */ CWORD_CWORD_CWORD_CWORD, /* 178 */ CWORD_CWORD_CWORD_CWORD, /* 179 */ CWORD_CWORD_CWORD_CWORD, /* 180 */ CWORD_CWORD_CWORD_CWORD, /* 181 */ CWORD_CWORD_CWORD_CWORD, /* 182 */ CWORD_CWORD_CWORD_CWORD, /* 183 */ CWORD_CWORD_CWORD_CWORD, /* 184 */ CWORD_CWORD_CWORD_CWORD, /* 185 */ CWORD_CWORD_CWORD_CWORD, /* 186 */ CWORD_CWORD_CWORD_CWORD, /* 187 */ CWORD_CWORD_CWORD_CWORD, /* 188 */ CWORD_CWORD_CWORD_CWORD, /* 189 */ CWORD_CWORD_CWORD_CWORD, /* 190 */ CWORD_CWORD_CWORD_CWORD, /* 191 */ CWORD_CWORD_CWORD_CWORD, /* 192 */ CWORD_CWORD_CWORD_CWORD, /* 193 */ CWORD_CWORD_CWORD_CWORD, /* 194 */ CWORD_CWORD_CWORD_CWORD, /* 195 */ CWORD_CWORD_CWORD_CWORD, /* 196 */ CWORD_CWORD_CWORD_CWORD, /* 197 */ CWORD_CWORD_CWORD_CWORD, /* 198 */ CWORD_CWORD_CWORD_CWORD, /* 199 */ CWORD_CWORD_CWORD_CWORD, /* 200 */ CWORD_CWORD_CWORD_CWORD, /* 201 */ CWORD_CWORD_CWORD_CWORD, /* 202 */ CWORD_CWORD_CWORD_CWORD, /* 203 */ CWORD_CWORD_CWORD_CWORD, /* 204 */ CWORD_CWORD_CWORD_CWORD, /* 205 */ CWORD_CWORD_CWORD_CWORD, /* 206 */ CWORD_CWORD_CWORD_CWORD, /* 207 */ CWORD_CWORD_CWORD_CWORD, /* 208 */ CWORD_CWORD_CWORD_CWORD, /* 209 */ CWORD_CWORD_CWORD_CWORD, /* 210 */ CWORD_CWORD_CWORD_CWORD, /* 211 */ CWORD_CWORD_CWORD_CWORD, /* 212 */ CWORD_CWORD_CWORD_CWORD, /* 213 */ CWORD_CWORD_CWORD_CWORD, /* 214 */ CWORD_CWORD_CWORD_CWORD, /* 215 */ CWORD_CWORD_CWORD_CWORD, /* 216 */ CWORD_CWORD_CWORD_CWORD, /* 217 */ CWORD_CWORD_CWORD_CWORD, /* 218 */ CWORD_CWORD_CWORD_CWORD, /* 219 */ CWORD_CWORD_CWORD_CWORD, /* 220 */ CWORD_CWORD_CWORD_CWORD, /* 221 */ CWORD_CWORD_CWORD_CWORD, /* 222 */ CWORD_CWORD_CWORD_CWORD, /* 223 */ CWORD_CWORD_CWORD_CWORD, /* 224 */ CWORD_CWORD_CWORD_CWORD, /* 225 */ CWORD_CWORD_CWORD_CWORD, /* 226 */ CWORD_CWORD_CWORD_CWORD, /* 227 */ CWORD_CWORD_CWORD_CWORD, /* 228 */ CWORD_CWORD_CWORD_CWORD, /* 229 */ CWORD_CWORD_CWORD_CWORD, /* 230 */ CWORD_CWORD_CWORD_CWORD, /* 231 */ CWORD_CWORD_CWORD_CWORD, /* 232 */ CWORD_CWORD_CWORD_CWORD, /* 233 */ CWORD_CWORD_CWORD_CWORD, /* 234 */ CWORD_CWORD_CWORD_CWORD, /* 235 */ CWORD_CWORD_CWORD_CWORD, /* 236 */ CWORD_CWORD_CWORD_CWORD, /* 237 */ CWORD_CWORD_CWORD_CWORD, /* 238 */ CWORD_CWORD_CWORD_CWORD, /* 239 */ CWORD_CWORD_CWORD_CWORD, /* 230 */ CWORD_CWORD_CWORD_CWORD, /* 241 */ CWORD_CWORD_CWORD_CWORD, /* 242 */ CWORD_CWORD_CWORD_CWORD, /* 243 */ CWORD_CWORD_CWORD_CWORD, /* 244 */ CWORD_CWORD_CWORD_CWORD, /* 245 */ CWORD_CWORD_CWORD_CWORD, /* 246 */ CWORD_CWORD_CWORD_CWORD, /* 247 */ CWORD_CWORD_CWORD_CWORD, /* 248 */ CWORD_CWORD_CWORD_CWORD, /* 249 */ CWORD_CWORD_CWORD_CWORD, /* 250 */ CWORD_CWORD_CWORD_CWORD, /* 251 */ CWORD_CWORD_CWORD_CWORD, /* 252 */ CWORD_CWORD_CWORD_CWORD, /* 253 */ CWORD_CWORD_CWORD_CWORD, /* 254 */ CWORD_CWORD_CWORD_CWORD, /* 255 */ CWORD_CWORD_CWORD_CWORD, /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE, # if ENABLE_ASH_ALIAS /* PEOA */ CSPCL_CIGN_CIGN_CIGN, # endif }; # define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf) #endif /* !USE_SIT_FUNCTION */ /* ============ Alias handling */ #if ENABLE_ASH_ALIAS #define ALIASINUSE 1 #define ALIASDEAD 2 struct alias { struct alias *next; char *name; char *val; int flag; }; static struct alias **atab; // [ATABSIZE]; #define INIT_G_alias() do { \ atab = xzalloc(ATABSIZE * sizeof(atab[0])); \ } while (0) static struct alias ** __lookupalias(const char *name) { unsigned int hashval; struct alias **app; const char *p; unsigned int ch; p = name; ch = (unsigned char)*p; hashval = ch << 4; while (ch) { hashval += ch; ch = (unsigned char)*++p; } app = &atab[hashval % ATABSIZE]; for (; *app; app = &(*app)->next) { if (strcmp(name, (*app)->name) == 0) { break; } } return app; } static struct alias * lookupalias(const char *name, int check) { struct alias *ap = *__lookupalias(name); if (check && ap && (ap->flag & ALIASINUSE)) return NULL; return ap; } static struct alias * freealias(struct alias *ap) { struct alias *next; if (ap->flag & ALIASINUSE) { ap->flag |= ALIASDEAD; return ap; } next = ap->next; free(ap->name); free(ap->val); free(ap); return next; } static void setalias(const char *name, const char *val) { struct alias *ap, **app; app = __lookupalias(name); ap = *app; INT_OFF; if (ap) { if (!(ap->flag & ALIASINUSE)) { free(ap->val); } ap->val = ckstrdup(val); ap->flag &= ~ALIASDEAD; } else { /* not found */ ap = ckzalloc(sizeof(struct alias)); ap->name = ckstrdup(name); ap->val = ckstrdup(val); /*ap->flag = 0; - ckzalloc did it */ /*ap->next = NULL;*/ *app = ap; } INT_ON; } static int unalias(const char *name) { struct alias **app; app = __lookupalias(name); if (*app) { INT_OFF; *app = freealias(*app); INT_ON; return 0; } return 1; } static void rmaliases(void) { struct alias *ap, **app; int i; INT_OFF; for (i = 0; i < ATABSIZE; i++) { app = &atab[i]; for (ap = *app; ap; ap = *app) { *app = freealias(*app); if (ap == *app) { app = &ap->next; } } } INT_ON; } static void printalias(const struct alias *ap) { out1fmt("%s=%s\n", ap->name, single_quote(ap->val)); } /* * TODO - sort output */ static int FAST_FUNC aliascmd(int argc UNUSED_PARAM, char **argv) { char *n, *v; int ret = 0; struct alias *ap; if (!argv[1]) { int i; for (i = 0; i < ATABSIZE; i++) { for (ap = atab[i]; ap; ap = ap->next) { printalias(ap); } } return 0; } while ((n = *++argv) != NULL) { v = strchr(n+1, '='); if (v == NULL) { /* n+1: funny ksh stuff */ ap = *__lookupalias(n); if (ap == NULL) { fprintf(stderr, "%s: %s not found\n", "alias", n); ret = 1; } else printalias(ap); } else { *v++ = '\0'; setalias(n, v); } } return ret; } static int FAST_FUNC unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int i; while ((i = nextopt("a")) != '\0') { if (i == 'a') { rmaliases(); return 0; } } for (i = 0; *argptr; argptr++) { if (unalias(*argptr)) { fprintf(stderr, "%s: %s not found\n", "unalias", *argptr); i = 1; } } return i; } #endif /* ASH_ALIAS */ /* ============ jobs.c */ /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ #define FORK_FG 0 #define FORK_BG 1 #define FORK_NOJOB 2 /* mode flags for showjob(s) */ #define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */ #define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */ #define SHOW_CHANGED 0x04 /* only jobs whose state has changed */ /* * A job structure contains information about a job. A job is either a * single process or a set of processes contained in a pipeline. In the * latter case, pidlist will be non-NULL, and will point to a -1 terminated * array of pids. */ struct procstat { pid_t ps_pid; /* process id */ int ps_status; /* last process status from wait() */ char *ps_cmd; /* text of command being run */ }; struct job { struct procstat ps0; /* status of process */ struct procstat *ps; /* status or processes when more than one */ #if JOBS int stopstatus; /* status of a stopped job */ #endif uint32_t nprocs: 16, /* number of processes */ state: 8, #define JOBRUNNING 0 /* at least one proc running */ #define JOBSTOPPED 1 /* all procs are stopped */ #define JOBDONE 2 /* all procs are completed */ #if JOBS sigint: 1, /* job was killed by SIGINT */ jobctl: 1, /* job running under job control */ #endif waited: 1, /* true if this entry has been waited for */ used: 1, /* true if this entry is in used */ changed: 1; /* true if status has changed */ struct job *prev_job; /* previous job */ }; static struct job *makejob(/*union node *,*/ int); static int forkshell(struct job *, union node *, int); static int waitforjob(struct job *); #if !JOBS enum { doing_jobctl = 0 }; #define setjobctl(on) do {} while (0) #else static smallint doing_jobctl; //references:8 static void setjobctl(int); #endif /* * Ignore a signal. */ static void ignoresig(int signo) { /* Avoid unnecessary system calls. Is it already SIG_IGNed? */ if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { /* No, need to do it */ signal(signo, SIG_IGN); } sigmode[signo - 1] = S_HARD_IGN; } /* * Only one usage site - in setsignal() */ static void signal_handler(int signo) { gotsig[signo - 1] = 1; if (signo == SIGINT && !trap[SIGINT]) { if (!suppress_int) { pending_sig = 0; raise_interrupt(); /* does not return */ } pending_int = 1; } else { pending_sig = signo; } } /* * Set the signal handler for the specified signal. The routine figures * out what it should be set to. */ static void setsignal(int signo) { char *t; char cur_act, new_act; struct sigaction act; t = trap[signo]; new_act = S_DFL; if (t != NULL) { /* trap for this sig is set */ new_act = S_CATCH; if (t[0] == '\0') /* trap is "": ignore this sig */ new_act = S_IGN; } if (rootshell && new_act == S_DFL) { switch (signo) { case SIGINT: if (iflag || minusc || sflag == 0) new_act = S_CATCH; break; case SIGQUIT: #if DEBUG if (debug) break; #endif /* man bash: * "In all cases, bash ignores SIGQUIT. Non-builtin * commands run by bash have signal handlers * set to the values inherited by the shell * from its parent". */ new_act = S_IGN; break; case SIGTERM: if (iflag) new_act = S_IGN; break; #if JOBS case SIGTSTP: case SIGTTOU: if (mflag) new_act = S_IGN; break; #endif } } //TODO: if !rootshell, we reset SIGQUIT to DFL, //whereas we have to restore it to what shell got on entry //from the parent. See comment above t = &sigmode[signo - 1]; cur_act = *t; if (cur_act == 0) { /* current setting is not yet known */ if (sigaction(signo, NULL, &act)) { /* pretend it worked; maybe we should give a warning, * but other shells don't. We don't alter sigmode, * so we retry every time. * btw, in Linux it never fails. --vda */ return; } if (act.sa_handler == SIG_IGN) { cur_act = S_HARD_IGN; if (mflag && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU) ) { cur_act = S_IGN; /* don't hard ignore these */ } } } if (cur_act == S_HARD_IGN || cur_act == new_act) return; act.sa_handler = SIG_DFL; switch (new_act) { case S_CATCH: act.sa_handler = signal_handler; break; case S_IGN: act.sa_handler = SIG_IGN; break; } /* flags and mask matter only if !DFL and !IGN, but we do it * for all cases for more deterministic behavior: */ act.sa_flags = 0; sigfillset(&act.sa_mask); sigaction_set(signo, &act); *t = new_act; } /* mode flags for set_curjob */ #define CUR_DELETE 2 #define CUR_RUNNING 1 #define CUR_STOPPED 0 /* mode flags for dowait */ #define DOWAIT_NONBLOCK WNOHANG #define DOWAIT_BLOCK 0 #if JOBS /* pgrp of shell on invocation */ static int initialpgrp; //references:2 static int ttyfd = -1; //5 #endif /* array of jobs */ static struct job *jobtab; //5 /* size of array */ static unsigned njobs; //4 /* current job */ static struct job *curjob; //lots /* number of presumed living untracked jobs */ static int jobless; //4 static void set_curjob(struct job *jp, unsigned mode) { struct job *jp1; struct job **jpp, **curp; /* first remove from list */ jpp = curp = &curjob; while (1) { jp1 = *jpp; if (jp1 == jp) break; jpp = &jp1->prev_job; } *jpp = jp1->prev_job; /* Then re-insert in correct position */ jpp = curp; switch (mode) { default: #if DEBUG abort(); #endif case CUR_DELETE: /* job being deleted */ break; case CUR_RUNNING: /* newly created job or backgrounded job, * put after all stopped jobs. */ while (1) { jp1 = *jpp; #if JOBS if (!jp1 || jp1->state != JOBSTOPPED) #endif break; jpp = &jp1->prev_job; } /* FALLTHROUGH */ #if JOBS case CUR_STOPPED: #endif /* newly stopped job - becomes curjob */ jp->prev_job = *jpp; *jpp = jp; break; } } #if JOBS || DEBUG static int jobno(const struct job *jp) { return jp - jobtab + 1; } #endif /* * Convert a job name to a job structure. */ #if !JOBS #define getjob(name, getctl) getjob(name) #endif static struct job * getjob(const char *name, int getctl) { struct job *jp; struct job *found; const char *err_msg = "%s: no such job"; unsigned num; int c; const char *p; char *(*match)(const char *, const char *); jp = curjob; p = name; if (!p) goto currentjob; if (*p != '%') goto err; c = *++p; if (!c) goto currentjob; if (!p[1]) { if (c == '+' || c == '%') { currentjob: err_msg = "No current job"; goto check; } if (c == '-') { if (jp) jp = jp->prev_job; err_msg = "No previous job"; check: if (!jp) goto err; goto gotit; } } if (is_number(p)) { num = atoi(p); if (num < njobs) { jp = jobtab + num - 1; if (jp->used) goto gotit; goto err; } } match = prefix; if (*p == '?') { match = strstr; p++; } found = NULL; while (jp) { if (match(jp->ps[0].ps_cmd, p)) { if (found) goto err; found = jp; err_msg = "%s: ambiguous"; } jp = jp->prev_job; } if (!found) goto err; jp = found; gotit: #if JOBS err_msg = "job %s not created under job control"; if (getctl && jp->jobctl == 0) goto err; #endif return jp; err: ash_msg_and_raise_error(err_msg, name); } /* * Mark a job structure as unused. */ static void freejob(struct job *jp) { struct procstat *ps; int i; INT_OFF; for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) { if (ps->ps_cmd != nullstr) free(ps->ps_cmd); } if (jp->ps != &jp->ps0) free(jp->ps); jp->used = 0; set_curjob(jp, CUR_DELETE); INT_ON; } #if JOBS static void xtcsetpgrp(int fd, pid_t pgrp) { if (tcsetpgrp(fd, pgrp)) ash_msg_and_raise_error("can't set tty process group (%m)"); } /* * Turn job control on and off. * * Note: This code assumes that the third arg to ioctl is a character * pointer, which is true on Berkeley systems but not System V. Since * System V doesn't have job control yet, this isn't a problem now. * * Called with interrupts off. */ static void setjobctl(int on) { int fd; int pgrp; if (on == doing_jobctl || rootshell == 0) return; if (on) { int ofd; ofd = fd = open(_PATH_TTY, O_RDWR); if (fd < 0) { /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails. * That sometimes helps to acquire controlling tty. * Obviously, a workaround for bugs when someone * failed to provide a controlling tty to bash! :) */ fd = 2; while (!isatty(fd)) if (--fd < 0) goto out; } fd = fcntl(fd, F_DUPFD, 10); if (ofd >= 0) close(ofd); if (fd < 0) goto out; /* fd is a tty at this point */ close_on_exec_on(fd); while (1) { /* while we are in the background */ pgrp = tcgetpgrp(fd); if (pgrp < 0) { out: ash_msg("can't access tty; job control turned off"); mflag = on = 0; goto close; } if (pgrp == getpgrp()) break; killpg(0, SIGTTIN); } initialpgrp = pgrp; setsignal(SIGTSTP); setsignal(SIGTTOU); setsignal(SIGTTIN); pgrp = rootpid; setpgid(0, pgrp); xtcsetpgrp(fd, pgrp); } else { /* turning job control off */ fd = ttyfd; pgrp = initialpgrp; /* was xtcsetpgrp, but this can make exiting ash * loop forever if pty is already deleted */ tcsetpgrp(fd, pgrp); setpgid(0, pgrp); setsignal(SIGTSTP); setsignal(SIGTTOU); setsignal(SIGTTIN); close: if (fd >= 0) close(fd); fd = -1; } ttyfd = fd; doing_jobctl = on; } static int FAST_FUNC killcmd(int argc, char **argv) { if (argv[1] && strcmp(argv[1], "-l") != 0) { int i = 1; do { if (argv[i][0] == '%') { /* * "kill %N" - job kill * Converting to pgrp / pid kill */ struct job *jp; char *dst; int j, n; jp = getjob(argv[i], 0); /* * In jobs started under job control, we signal * entire process group by kill -PGRP_ID. * This happens, f.e., in interactive shell. * * Otherwise, we signal each child via * kill PID1 PID2 PID3. * Testcases: * sh -c 'sleep 1|sleep 1 & kill %1' * sh -c 'true|sleep 2 & sleep 1; kill %1' * sh -c 'true|sleep 1 & sleep 2; kill %1' */ n = jp->nprocs; /* can't be 0 (I hope) */ if (jp->jobctl) n = 1; dst = alloca(n * sizeof(int)*4); argv[i] = dst; for (j = 0; j < n; j++) { struct procstat *ps = &jp->ps[j]; /* Skip non-running and not-stopped members * (i.e. dead members) of the job */ if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status)) continue; /* * kill_main has matching code to expect * leading space. Needed to not confuse * negative pids with "kill -SIGNAL_NO" syntax */ dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid); } *dst = '\0'; } } while (argv[++i]); } return kill_main(argc, argv); } static void showpipe(struct job *jp /*, FILE *out*/) { struct procstat *ps; struct procstat *psend; psend = jp->ps + jp->nprocs; for (ps = jp->ps + 1; ps < psend; ps++) printf(" | %s", ps->ps_cmd); outcslow('\n', stdout); flush_stdout_stderr(); } static int restartjob(struct job *jp, int mode) { struct procstat *ps; int i; int status; pid_t pgid; INT_OFF; if (jp->state == JOBDONE) goto out; jp->state = JOBRUNNING; pgid = jp->ps[0].ps_pid; if (mode == FORK_FG) xtcsetpgrp(ttyfd, pgid); killpg(pgid, SIGCONT); ps = jp->ps; i = jp->nprocs; do { if (WIFSTOPPED(ps->ps_status)) { ps->ps_status = -1; } ps++; } while (--i); out: status = (mode == FORK_FG) ? waitforjob(jp) : 0; INT_ON; return status; } static int FAST_FUNC fg_bgcmd(int argc UNUSED_PARAM, char **argv) { struct job *jp; int mode; int retval; mode = (**argv == 'f') ? FORK_FG : FORK_BG; nextopt(nullstr); argv = argptr; do { jp = getjob(*argv, 1); if (mode == FORK_BG) { set_curjob(jp, CUR_RUNNING); printf("[%d] ", jobno(jp)); } out1str(jp->ps[0].ps_cmd); showpipe(jp /*, stdout*/); retval = restartjob(jp, mode); } while (*argv && *++argv); return retval; } #endif static int sprint_status(char *s, int status, int sigonly) { int col; int st; col = 0; if (!WIFEXITED(status)) { #if JOBS if (WIFSTOPPED(status)) st = WSTOPSIG(status); else #endif st = WTERMSIG(status); if (sigonly) { if (st == SIGINT || st == SIGPIPE) goto out; #if JOBS if (WIFSTOPPED(status)) goto out; #endif } st &= 0x7f; //TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata col = fmtstr(s, 32, strsignal(st)); if (WCOREDUMP(status)) { col += fmtstr(s + col, 16, " (core dumped)"); } } else if (!sigonly) { st = WEXITSTATUS(status); if (st) col = fmtstr(s, 16, "Done(%d)", st); else col = fmtstr(s, 16, "Done"); } out: return col; } static int dowait(int wait_flags, struct job *job) { int pid; int status; struct job *jp; struct job *thisjob; int state; TRACE(("dowait(0x%x) called\n", wait_flags)); /* Do a wait system call. If job control is compiled in, we accept * stopped processes. wait_flags may have WNOHANG, preventing blocking. * NB: _not_ safe_waitpid, we need to detect EINTR */ if (doing_jobctl) wait_flags |= WUNTRACED; pid = waitpid(-1, &status, wait_flags); TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", pid, status, errno, strerror(errno))); if (pid <= 0) return pid; INT_OFF; thisjob = NULL; for (jp = curjob; jp; jp = jp->prev_job) { struct procstat *ps; struct procstat *psend; if (jp->state == JOBDONE) continue; state = JOBDONE; ps = jp->ps; psend = ps + jp->nprocs; do { if (ps->ps_pid == pid) { TRACE(("Job %d: changing status of proc %d " "from 0x%x to 0x%x\n", jobno(jp), pid, ps->ps_status, status)); ps->ps_status = status; thisjob = jp; } if (ps->ps_status == -1) state = JOBRUNNING; #if JOBS if (state == JOBRUNNING) continue; if (WIFSTOPPED(ps->ps_status)) { jp->stopstatus = ps->ps_status; state = JOBSTOPPED; } #endif } while (++ps < psend); if (thisjob) goto gotjob; } #if JOBS if (!WIFSTOPPED(status)) #endif jobless--; goto out; gotjob: if (state != JOBRUNNING) { thisjob->changed = 1; if (thisjob->state != state) { TRACE(("Job %d: changing state from %d to %d\n", jobno(thisjob), thisjob->state, state)); thisjob->state = state; #if JOBS if (state == JOBSTOPPED) { set_curjob(thisjob, CUR_STOPPED); } #endif } } out: INT_ON; if (thisjob && thisjob == job) { char s[48 + 1]; int len; len = sprint_status(s, status, 1); if (len) { s[len] = '\n'; s[len + 1] = '\0'; out2str(s); } } return pid; } static int blocking_wait_with_raise_on_sig(void) { pid_t pid = dowait(DOWAIT_BLOCK, NULL); if (pid <= 0 && pending_sig) raise_exception(EXSIG); return pid; } #if JOBS static void showjob(FILE *out, struct job *jp, int mode) { struct procstat *ps; struct procstat *psend; int col; int indent_col; char s[80]; ps = jp->ps; if (mode & SHOW_ONLY_PGID) { /* jobs -p */ /* just output process (group) id of pipeline */ fprintf(out, "%d\n", ps->ps_pid); return; } col = fmtstr(s, 16, "[%d] ", jobno(jp)); indent_col = col; if (jp == curjob) s[col - 3] = '+'; else if (curjob && jp == curjob->prev_job) s[col - 3] = '-'; if (mode & SHOW_PIDS) col += fmtstr(s + col, 16, "%d ", ps->ps_pid); psend = ps + jp->nprocs; if (jp->state == JOBRUNNING) { strcpy(s + col, "Running"); col += sizeof("Running") - 1; } else { int status = psend[-1].ps_status; if (jp->state == JOBSTOPPED) status = jp->stopstatus; col += sprint_status(s + col, status, 0); } /* By now, "[JOBID]* [maybe PID] STATUS" is printed */ /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line * or prints several "PID | <cmdN>" lines, * depending on SHOW_PIDS bit. * We do not print status of individual processes * between PID and <cmdN>. bash does it, but not very well: * first line shows overall job status, not process status, * making it impossible to know 1st process status. */ goto start; do { /* for each process */ s[0] = '\0'; col = 33; if (mode & SHOW_PIDS) col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1; start: fprintf(out, "%s%*c%s%s", s, 33 - col >= 0 ? 33 - col : 0, ' ', ps == jp->ps ? "" : "| ", ps->ps_cmd ); } while (++ps != psend); outcslow('\n', out); jp->changed = 0; if (jp->state == JOBDONE) { TRACE(("showjob: freeing job %d\n", jobno(jp))); freejob(jp); } } /* * Print a list of jobs. If "change" is nonzero, only print jobs whose * statuses have changed since the last call to showjobs. */ static void showjobs(FILE *out, int mode) { struct job *jp; TRACE(("showjobs(0x%x) called\n", mode)); /* Handle all finished jobs */ while (dowait(DOWAIT_NONBLOCK, NULL) > 0) continue; for (jp = curjob; jp; jp = jp->prev_job) { if (!(mode & SHOW_CHANGED) || jp->changed) { showjob(out, jp, mode); } } } static int FAST_FUNC jobscmd(int argc UNUSED_PARAM, char **argv) { int mode, m; mode = 0; while ((m = nextopt("lp")) != '\0') { if (m == 'l') mode |= SHOW_PIDS; else mode |= SHOW_ONLY_PGID; } argv = argptr; if (*argv) { do showjob(stdout, getjob(*argv, 0), mode); while (*++argv); } else { showjobs(stdout, mode); } return 0; } #endif /* JOBS */ /* Called only on finished or stopped jobs (no members are running) */ static int getstatus(struct job *job) { int status; int retval; struct procstat *ps; /* Fetch last member's status */ ps = job->ps + job->nprocs - 1; status = ps->ps_status; if (pipefail) { /* "set -o pipefail" mode: use last _nonzero_ status */ while (status == 0 && --ps >= job->ps) status = ps->ps_status; } retval = WEXITSTATUS(status); if (!WIFEXITED(status)) { #if JOBS retval = WSTOPSIG(status); if (!WIFSTOPPED(status)) #endif { /* XXX: limits number of signals */ retval = WTERMSIG(status); #if JOBS if (retval == SIGINT) job->sigint = 1; #endif } retval += 128; } TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n", jobno(job), job->nprocs, status, retval)); return retval; } static int FAST_FUNC waitcmd(int argc UNUSED_PARAM, char **argv) { struct job *job; int retval; struct job *jp; if (pending_sig) raise_exception(EXSIG); nextopt(nullstr); retval = 0; argv = argptr; if (!*argv) { /* wait for all jobs */ for (;;) { jp = curjob; while (1) { if (!jp) /* no running procs */ goto ret; if (jp->state == JOBRUNNING) break; jp->waited = 1; jp = jp->prev_job; } blocking_wait_with_raise_on_sig(); /* man bash: * "When bash is waiting for an asynchronous command via * the wait builtin, the reception of a signal for which a trap * has been set will cause the wait builtin to return immediately * with an exit status greater than 128, immediately after which * the trap is executed." * * blocking_wait_with_raise_on_sig raises signal handlers * if it gets no pid (pid < 0). However, * if child sends us a signal *and immediately exits*, * blocking_wait_with_raise_on_sig gets pid > 0 * and does not handle pending_sig. Check this case: */ if (pending_sig) raise_exception(EXSIG); } } retval = 127; do { if (**argv != '%') { pid_t pid = number(*argv); job = curjob; while (1) { if (!job) goto repeat; if (job->ps[job->nprocs - 1].ps_pid == pid) break; job = job->prev_job; } } else { job = getjob(*argv, 0); } /* loop until process terminated or stopped */ while (job->state == JOBRUNNING) blocking_wait_with_raise_on_sig(); job->waited = 1; retval = getstatus(job); repeat: ; } while (*++argv); ret: return retval; } static struct job * growjobtab(void) { size_t len; ptrdiff_t offset; struct job *jp, *jq; len = njobs * sizeof(*jp); jq = jobtab; jp = ckrealloc(jq, len + 4 * sizeof(*jp)); offset = (char *)jp - (char *)jq; if (offset) { /* Relocate pointers */ size_t l = len; jq = (struct job *)((char *)jq + l); while (l) { l -= sizeof(*jp); jq--; #define joff(p) ((struct job *)((char *)(p) + l)) #define jmove(p) (p) = (void *)((char *)(p) + offset) if (joff(jp)->ps == &jq->ps0) jmove(joff(jp)->ps); if (joff(jp)->prev_job) jmove(joff(jp)->prev_job); } if (curjob) jmove(curjob); #undef joff #undef jmove } njobs += 4; jobtab = jp; jp = (struct job *)((char *)jp + len); jq = jp + 3; do { jq->used = 0; } while (--jq >= jp); return jp; } /* * Return a new job structure. * Called with interrupts off. */ static struct job * makejob(/*union node *node,*/ int nprocs) { int i; struct job *jp; for (i = njobs, jp = jobtab; ; jp++) { if (--i < 0) { jp = growjobtab(); break; } if (jp->used == 0) break; if (jp->state != JOBDONE || !jp->waited) continue; #if JOBS if (doing_jobctl) continue; #endif freejob(jp); break; } memset(jp, 0, sizeof(*jp)); #if JOBS /* jp->jobctl is a bitfield. * "jp->jobctl |= jobctl" likely to give awful code */ if (doing_jobctl) jp->jobctl = 1; #endif jp->prev_job = curjob; curjob = jp; jp->used = 1; jp->ps = &jp->ps0; if (nprocs > 1) { jp->ps = ckmalloc(nprocs * sizeof(struct procstat)); } TRACE(("makejob(%d) returns %%%d\n", nprocs, jobno(jp))); return jp; } #if JOBS /* * Return a string identifying a command (to be printed by the * jobs command). */ static char *cmdnextc; static void cmdputs(const char *s) { static const char vstype[VSTYPE + 1][3] = { "", "}", "-", "+", "?", "=", "%", "%%", "#", "##" IF_ASH_BASH_COMPAT(, ":", "/", "//") }; const char *p, *str; char cc[2]; char *nextc; unsigned char c; unsigned char subtype = 0; int quoted = 0; cc[1] = '\0'; nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc); p = s; while ((c = *p++) != '\0') { str = NULL; switch (c) { case CTLESC: c = *p++; break; case CTLVAR: subtype = *p++; if ((subtype & VSTYPE) == VSLENGTH) str = "${#"; else str = "${"; if (!(subtype & VSQUOTE) == !(quoted & 1)) goto dostr; quoted ^= 1; c = '"'; break; case CTLENDVAR: str = "\"}" + !(quoted & 1); quoted >>= 1; subtype = 0; goto dostr; case CTLBACKQ: str = "$(...)"; goto dostr; case CTLBACKQ+CTLQUOTE: str = "\"$(...)\""; goto dostr; #if ENABLE_SH_MATH_SUPPORT case CTLARI: str = "$(("; goto dostr; case CTLENDARI: str = "))"; goto dostr; #endif case CTLQUOTEMARK: quoted ^= 1; c = '"'; break; case '=': if (subtype == 0) break; if ((subtype & VSTYPE) != VSNORMAL) quoted <<= 1; str = vstype[subtype & VSTYPE]; if (subtype & VSNUL) c = ':'; else goto checkstr; break; case '\'': case '\\': case '"': case '$': /* These can only happen inside quotes */ cc[0] = c; str = cc; c = '\\'; break; default: break; } USTPUTC(c, nextc); checkstr: if (!str) continue; dostr: while ((c = *str++) != '\0') { USTPUTC(c, nextc); } } /* while *p++ not NUL */ if (quoted & 1) { USTPUTC('"', nextc); } *nextc = 0; cmdnextc = nextc; } /* cmdtxt() and cmdlist() call each other */ static void cmdtxt(union node *n); static void cmdlist(union node *np, int sep) { for (; np; np = np->narg.next) { if (!sep) cmdputs(" "); cmdtxt(np); if (sep && np->narg.next) cmdputs(" "); } } static void cmdtxt(union node *n) { union node *np; struct nodelist *lp; const char *p; if (!n) return; switch (n->type) { default: #if DEBUG abort(); #endif case NPIPE: lp = n->npipe.cmdlist; for (;;) { cmdtxt(lp->n); lp = lp->next; if (!lp) break; cmdputs(" | "); } break; case NSEMI: p = "; "; goto binop; case NAND: p = " && "; goto binop; case NOR: p = " || "; binop: cmdtxt(n->nbinary.ch1); cmdputs(p); n = n->nbinary.ch2; goto donode; case NREDIR: case NBACKGND: n = n->nredir.n; goto donode; case NNOT: cmdputs("!"); n = n->nnot.com; donode: cmdtxt(n); break; case NIF: cmdputs("if "); cmdtxt(n->nif.test); cmdputs("; then "); if (n->nif.elsepart) { cmdtxt(n->nif.ifpart); cmdputs("; else "); n = n->nif.elsepart; } else { n = n->nif.ifpart; } p = "; fi"; goto dotail; case NSUBSHELL: cmdputs("("); n = n->nredir.n; p = ")"; goto dotail; case NWHILE: p = "while "; goto until; case NUNTIL: p = "until "; until: cmdputs(p); cmdtxt(n->nbinary.ch1); n = n->nbinary.ch2; p = "; done"; dodo: cmdputs("; do "); dotail: cmdtxt(n); goto dotail2; case NFOR: cmdputs("for "); cmdputs(n->nfor.var); cmdputs(" in "); cmdlist(n->nfor.args, 1); n = n->nfor.body; p = "; done"; goto dodo; case NDEFUN: cmdputs(n->narg.text); p = "() { ... }"; goto dotail2; case NCMD: cmdlist(n->ncmd.args, 1); cmdlist(n->ncmd.redirect, 0); break; case NARG: p = n->narg.text; dotail2: cmdputs(p); break; case NHERE: case NXHERE: p = "<<..."; goto dotail2; case NCASE: cmdputs("case "); cmdputs(n->ncase.expr->narg.text); cmdputs(" in "); for (np = n->ncase.cases; np; np = np->nclist.next) { cmdtxt(np->nclist.pattern); cmdputs(") "); cmdtxt(np->nclist.body); cmdputs(";; "); } p = "esac"; goto dotail2; case NTO: p = ">"; goto redir; case NCLOBBER: p = ">|"; goto redir; case NAPPEND: p = ">>"; goto redir; #if ENABLE_ASH_BASH_COMPAT case NTO2: #endif case NTOFD: p = ">&"; goto redir; case NFROM: p = "<"; goto redir; case NFROMFD: p = "<&"; goto redir; case NFROMTO: p = "<>"; redir: cmdputs(utoa(n->nfile.fd)); cmdputs(p); if (n->type == NTOFD || n->type == NFROMFD) { cmdputs(utoa(n->ndup.dupfd)); break; } n = n->nfile.fname; goto donode; } } static char * commandtext(union node *n) { char *name; STARTSTACKSTR(cmdnextc); cmdtxt(n); name = stackblock(); TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n", name, cmdnextc, cmdnextc)); return ckstrdup(name); } #endif /* JOBS */ /* * Fork off a subshell. If we are doing job control, give the subshell its * own process group. Jp is a job structure that the job is to be added to. * N is the command that will be evaluated by the child. Both jp and n may * be NULL. The mode parameter can be one of the following: * FORK_FG - Fork off a foreground process. * FORK_BG - Fork off a background process. * FORK_NOJOB - Like FORK_FG, but don't give the process its own * process group even if job control is on. * * When job control is turned off, background processes have their standard * input redirected to /dev/null (except for the second and later processes * in a pipeline). * * Called with interrupts off. */ /* * Clear traps on a fork. */ static void clear_traps(void) { char **tp; for (tp = trap; tp < &trap[NSIG]; tp++) { if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */ INT_OFF; if (trap_ptr == trap) free(*tp); /* else: it "belongs" to trap_ptr vector, don't free */ *tp = NULL; if ((tp - trap) != 0) setsignal(tp - trap); INT_ON; } } may_have_traps = 0; } /* Lives far away from here, needed for forkchild */ static void closescript(void); /* Called after fork(), in child */ static NOINLINE void forkchild(struct job *jp, union node *n, int mode) { int oldlvl; TRACE(("Child shell %d\n", getpid())); oldlvl = shlvl; shlvl++; /* man bash: "Non-builtin commands run by bash have signal handlers * set to the values inherited by the shell from its parent". * Do we do it correctly? */ closescript(); if (mode == FORK_NOJOB /* is it `xxx` ? */ && n && n->type == NCMD /* is it single cmd? */ /* && n->ncmd.args->type == NARG - always true? */ && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */ /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */ ) { TRACE(("Trap hack\n")); /* Awful hack for `trap` or $(trap). * * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html * contains an example where "trap" is executed in a subshell: * * save_traps=$(trap) * ... * eval "$save_traps" * * Standard does not say that "trap" in subshell shall print * parent shell's traps. It only says that its output * must have suitable form, but then, in the above example * (which is not supposed to be normative), it implies that. * * bash (and probably other shell) does implement it * (traps are reset to defaults, but "trap" still shows them), * but as a result, "trap" logic is hopelessly messed up: * * # trap * trap -- 'echo Ho' SIGWINCH <--- we have a handler * # (trap) <--- trap is in subshell - no output (correct, traps are reset) * # true | trap <--- trap is in subshell - no output (ditto) * # echo `true | trap` <--- in subshell - output (but traps are reset!) * trap -- 'echo Ho' SIGWINCH * # echo `(trap)` <--- in subshell in subshell - output * trap -- 'echo Ho' SIGWINCH * # echo `true | (trap)` <--- in subshell in subshell in subshell - output! * trap -- 'echo Ho' SIGWINCH * * The rules when to forget and when to not forget traps * get really complex and nonsensical. * * Our solution: ONLY bare $(trap) or `trap` is special. */ /* Save trap handler strings for trap builtin to print */ trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap)); /* Fall through into clearing traps */ } clear_traps(); #if JOBS /* do job control only in root shell */ doing_jobctl = 0; if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) { pid_t pgrp; if (jp->nprocs == 0) pgrp = getpid(); else pgrp = jp->ps[0].ps_pid; /* this can fail because we are doing it in the parent also */ setpgid(0, pgrp); if (mode == FORK_FG) xtcsetpgrp(ttyfd, pgrp); setsignal(SIGTSTP); setsignal(SIGTTOU); } else #endif if (mode == FORK_BG) { /* man bash: "When job control is not in effect, * asynchronous commands ignore SIGINT and SIGQUIT" */ ignoresig(SIGINT); ignoresig(SIGQUIT); if (jp->nprocs == 0) { close(0); if (open(bb_dev_null, O_RDONLY) != 0) ash_msg_and_raise_error("can't open '%s'", bb_dev_null); } } if (oldlvl == 0) { if (iflag) { /* why if iflag only? */ setsignal(SIGINT); setsignal(SIGTERM); } /* man bash: * "In all cases, bash ignores SIGQUIT. Non-builtin * commands run by bash have signal handlers * set to the values inherited by the shell * from its parent". * Take care of the second rule: */ setsignal(SIGQUIT); } #if JOBS if (n && n->type == NCMD && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0 ) { TRACE(("Job hack\n")); /* "jobs": we do not want to clear job list for it, * instead we remove only _its_ own_ job from job list. * This makes "jobs .... | cat" more useful. */ freejob(curjob); return; } #endif for (jp = curjob; jp; jp = jp->prev_job) freejob(jp); jobless = 0; } /* Called after fork(), in parent */ #if !JOBS #define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid) #endif static void forkparent(struct job *jp, union node *n, int mode, pid_t pid) { TRACE(("In parent shell: child = %d\n", pid)); if (!jp) { while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0) continue; jobless++; return; } #if JOBS if (mode != FORK_NOJOB && jp->jobctl) { int pgrp; if (jp->nprocs == 0) pgrp = pid; else pgrp = jp->ps[0].ps_pid; /* This can fail because we are doing it in the child also */ setpgid(pid, pgrp); } #endif if (mode == FORK_BG) { backgndpid = pid; /* set $! */ set_curjob(jp, CUR_RUNNING); } if (jp) { struct procstat *ps = &jp->ps[jp->nprocs++]; ps->ps_pid = pid; ps->ps_status = -1; ps->ps_cmd = nullstr; #if JOBS if (doing_jobctl && n) ps->ps_cmd = commandtext(n); #endif } } static int forkshell(struct job *jp, union node *n, int mode) { int pid; TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode)); pid = fork(); if (pid < 0) { TRACE(("Fork failed, errno=%d", errno)); if (jp) freejob(jp); ash_msg_and_raise_error("can't fork"); } if (pid == 0) { CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */ forkchild(jp, n, mode); } else { forkparent(jp, n, mode, pid); } return pid; } /* * Wait for job to finish. * * Under job control we have the problem that while a child process * is running interrupts generated by the user are sent to the child * but not to the shell. This means that an infinite loop started by * an interactive user may be hard to kill. With job control turned off, * an interactive user may place an interactive program inside a loop. * If the interactive program catches interrupts, the user doesn't want * these interrupts to also abort the loop. The approach we take here * is to have the shell ignore interrupt signals while waiting for a * foreground process to terminate, and then send itself an interrupt * signal if the child process was terminated by an interrupt signal. * Unfortunately, some programs want to do a bit of cleanup and then * exit on interrupt; unless these processes terminate themselves by * sending a signal to themselves (instead of calling exit) they will * confuse this approach. * * Called with interrupts off. */ static int waitforjob(struct job *jp) { int st; TRACE(("waitforjob(%%%d) called\n", jobno(jp))); INT_OFF; while (jp->state == JOBRUNNING) { /* In non-interactive shells, we _can_ get * a keyboard signal here and be EINTRed, * but we just loop back, waiting for command to complete. * * man bash: * "If bash is waiting for a command to complete and receives * a signal for which a trap has been set, the trap * will not be executed until the command completes." * * Reality is that even if trap is not set, bash * will not act on the signal until command completes. * Try this. sleep5intoff.c: * #include <signal.h> * #include <unistd.h> * int main() { * sigset_t set; * sigemptyset(&set); * sigaddset(&set, SIGINT); * sigaddset(&set, SIGQUIT); * sigprocmask(SIG_BLOCK, &set, NULL); * sleep(5); * return 0; * } * $ bash -c './sleep5intoff; echo hi' * ^C^C^C^C <--- pressing ^C once a second * $ _ * $ bash -c './sleep5intoff; echo hi' * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT) * $ _ */ dowait(DOWAIT_BLOCK, jp); } INT_ON; st = getstatus(jp); #if JOBS if (jp->jobctl) { xtcsetpgrp(ttyfd, rootpid); /* * This is truly gross. * If we're doing job control, then we did a TIOCSPGRP which * caused us (the shell) to no longer be in the controlling * session -- so we wouldn't have seen any ^C/SIGINT. So, we * intuit from the subprocess exit status whether a SIGINT * occurred, and if so interrupt ourselves. Yuck. - mycroft */ if (jp->sigint) /* TODO: do the same with all signals */ raise(SIGINT); /* ... by raise(jp->sig) instead? */ } if (jp->state == JOBDONE) #endif freejob(jp); return st; } /* * return 1 if there are stopped jobs, otherwise 0 */ static int stoppedjobs(void) { struct job *jp; int retval; retval = 0; if (job_warning) goto out; jp = curjob; if (jp && jp->state == JOBSTOPPED) { out2str("You have stopped jobs.\n"); job_warning = 2; retval++; } out: return retval; } /* ============ redir.c * * Code for dealing with input/output redirection. */ #undef EMPTY #undef CLOSED #define EMPTY -2 /* marks an unused slot in redirtab */ #define CLOSED -3 /* marks a slot of previously-closed fd */ /* * Open a file in noclobber mode. * The code was copied from bash. */ static int noclobberopen(const char *fname) { int r, fd; struct stat finfo, finfo2; /* * If the file exists and is a regular file, return an error * immediately. */ r = stat(fname, &finfo); if (r == 0 && S_ISREG(finfo.st_mode)) { errno = EEXIST; return -1; } /* * If the file was not present (r != 0), make sure we open it * exclusively so that if it is created before we open it, our open * will fail. Make sure that we do not truncate an existing file. * Note that we don't turn on O_EXCL unless the stat failed -- if the * file was not a regular file, we leave O_EXCL off. */ if (r != 0) return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666); fd = open(fname, O_WRONLY|O_CREAT, 0666); /* If the open failed, return the file descriptor right away. */ if (fd < 0) return fd; /* * OK, the open succeeded, but the file may have been changed from a * non-regular file to a regular file between the stat and the open. * We are assuming that the O_EXCL open handles the case where FILENAME * did not exist and is symlinked to an existing file between the stat * and open. */ /* * If we can open it and fstat the file descriptor, and neither check * revealed that it was a regular file, and the file has not been * replaced, return the file descriptor. */ if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino ) { return fd; } /* The file has been replaced. badness. */ close(fd); errno = EEXIST; return -1; } /* * Handle here documents. Normally we fork off a process to write the * data to a pipe. If the document is short, we can stuff the data in * the pipe without forking. */ /* openhere needs this forward reference */ static void expandhere(union node *arg, int fd); static int openhere(union node *redir) { int pip[2]; size_t len = 0; if (pipe(pip) < 0) ash_msg_and_raise_error("pipe call failed"); if (redir->type == NHERE) { len = strlen(redir->nhere.doc->narg.text); if (len <= PIPE_BUF) { full_write(pip[1], redir->nhere.doc->narg.text, len); goto out; } } if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { /* child */ close(pip[0]); ignoresig(SIGINT); //signal(SIGINT, SIG_IGN); ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN); ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN); ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN); signal(SIGPIPE, SIG_DFL); if (redir->type == NHERE) full_write(pip[1], redir->nhere.doc->narg.text, len); else /* NXHERE */ expandhere(redir->nhere.doc, pip[1]); _exit(EXIT_SUCCESS); } out: close(pip[1]); return pip[0]; } static int openredirect(union node *redir) { char *fname; int f; fname = redir->nfile.expfname; switch (redir->nfile.type) { case NFROM: f = open(fname, O_RDONLY); if (f < 0) goto eopen; break; case NFROMTO: f = open(fname, O_RDWR|O_CREAT, 0666); if (f < 0) goto ecreate; break; case NTO: #if ENABLE_ASH_BASH_COMPAT case NTO2: #endif /* Take care of noclobber mode. */ if (Cflag) { f = noclobberopen(fname); if (f < 0) goto ecreate; break; } /* FALLTHROUGH */ case NCLOBBER: f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666); if (f < 0) goto ecreate; break; case NAPPEND: f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666); if (f < 0) goto ecreate; break; default: #if DEBUG abort(); #endif /* Fall through to eliminate warning. */ /* Our single caller does this itself */ // case NTOFD: // case NFROMFD: // f = -1; // break; case NHERE: case NXHERE: f = openhere(redir); break; } return f; ecreate: ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory")); eopen: ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file")); } /* * Copy a file descriptor to be >= to. Returns -1 * if the source file descriptor is closed, EMPTY if there are no unused * file descriptors left. */ /* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD). * old code was doing close(to) prior to copyfd() to achieve the same */ enum { COPYFD_EXACT = (int)~(INT_MAX), COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1), }; static int copyfd(int from, int to) { int newfd; if (to & COPYFD_EXACT) { to &= ~COPYFD_EXACT; /*if (from != to)*/ newfd = dup2(from, to); } else { newfd = fcntl(from, F_DUPFD, to); } if (newfd < 0) { if (errno == EMFILE) return EMPTY; /* Happens when source fd is not open: try "echo >&99" */ ash_msg_and_raise_error("%d: %m", from); } return newfd; } /* Struct def and variable are moved down to the first usage site */ struct two_fd_t { int orig, copy; }; struct redirtab { struct redirtab *next; int nullredirs; int pair_count; struct two_fd_t two_fd[]; }; #define redirlist (G_var.redirlist) static int need_to_remember(struct redirtab *rp, int fd) { int i; if (!rp) /* remembering was not requested */ return 0; for (i = 0; i < rp->pair_count; i++) { if (rp->two_fd[i].orig == fd) { /* already remembered */ return 0; } } return 1; } /* "hidden" fd is a fd used to read scripts, or a copy of such */ static int is_hidden_fd(struct redirtab *rp, int fd) { int i; struct parsefile *pf; if (fd == -1) return 0; /* Check open scripts' fds */ pf = g_parsefile; while (pf) { /* We skip pf_fd == 0 case because of the following case: * $ ash # running ash interactively * $ . ./script.sh * and in script.sh: "exec 9>&0". * Even though top-level pf_fd _is_ 0, * it's still ok to use it: "read" builtin uses it, * why should we cripple "exec" builtin? */ if (pf->pf_fd > 0 && fd == pf->pf_fd) { return 1; } pf = pf->prev; } if (!rp) return 0; /* Check saved fds of redirects */ fd |= COPYFD_RESTORE; for (i = 0; i < rp->pair_count; i++) { if (rp->two_fd[i].copy == fd) { return 1; } } return 0; } /* * Process a list of redirection commands. If the REDIR_PUSH flag is set, * old file descriptors are stashed away so that the redirection can be * undone by calling popredir. */ /* flags passed to redirect */ #define REDIR_PUSH 01 /* save previous values of file descriptors */ #define REDIR_SAVEFD2 03 /* set preverrout */ static void redirect(union node *redir, int flags) { struct redirtab *sv; int sv_pos; int i; int fd; int newfd; int copied_fd2 = -1; g_nullredirs++; if (!redir) { return; } sv = NULL; sv_pos = 0; INT_OFF; if (flags & REDIR_PUSH) { union node *tmp = redir; do { sv_pos++; #if ENABLE_ASH_BASH_COMPAT if (tmp->nfile.type == NTO2) sv_pos++; #endif tmp = tmp->nfile.next; } while (tmp); sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0])); sv->next = redirlist; sv->pair_count = sv_pos; redirlist = sv; sv->nullredirs = g_nullredirs - 1; g_nullredirs = 0; while (sv_pos > 0) { sv_pos--; sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY; } } do { int right_fd = -1; fd = redir->nfile.fd; if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { right_fd = redir->ndup.dupfd; //bb_error_msg("doing %d > %d", fd, right_fd); /* redirect from/to same file descriptor? */ if (right_fd == fd) continue; /* "echo >&10" and 10 is a fd opened to a sh script? */ if (is_hidden_fd(sv, right_fd)) { errno = EBADF; /* as if it is closed */ ash_msg_and_raise_error("%d: %m", right_fd); } newfd = -1; } else { newfd = openredirect(redir); /* always >= 0 */ if (fd == newfd) { /* Descriptor wasn't open before redirect. * Mark it for close in the future */ if (need_to_remember(sv, fd)) { goto remember_to_close; } continue; } } #if ENABLE_ASH_BASH_COMPAT redirect_more: #endif if (need_to_remember(sv, fd)) { /* Copy old descriptor */ /* Careful to not accidentally "save" * to the same fd as right side fd in N>&M */ int minfd = right_fd < 10 ? 10 : right_fd + 1; i = fcntl(fd, F_DUPFD, minfd); /* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds * are closed in popredir() in the child, preventing them from leaking * into child. (popredir() also cleans up the mess in case of failures) */ if (i == -1) { i = errno; if (i != EBADF) { /* Strange error (e.g. "too many files" EMFILE?) */ if (newfd >= 0) close(newfd); errno = i; ash_msg_and_raise_error("%d: %m", fd); /* NOTREACHED */ } /* EBADF: it is not open - good, remember to close it */ remember_to_close: i = CLOSED; } else { /* fd is open, save its copy */ /* "exec fd>&-" should not close fds * which point to script file(s). * Force them to be restored afterwards */ if (is_hidden_fd(sv, fd)) i |= COPYFD_RESTORE; } if (fd == 2) copied_fd2 = i; sv->two_fd[sv_pos].orig = fd; sv->two_fd[sv_pos].copy = i; sv_pos++; } if (newfd < 0) { /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */ if (redir->ndup.dupfd < 0) { /* "fd>&-" */ /* Don't want to trigger debugging */ if (fd != -1) close(fd); } else { copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT); } } else if (fd != newfd) { /* move newfd to fd */ copyfd(newfd, fd | COPYFD_EXACT); #if ENABLE_ASH_BASH_COMPAT if (!(redir->nfile.type == NTO2 && fd == 2)) #endif close(newfd); } #if ENABLE_ASH_BASH_COMPAT if (redir->nfile.type == NTO2 && fd == 1) { /* We already redirected it to fd 1, now copy it to 2 */ newfd = 1; fd = 2; goto redirect_more; } #endif } while ((redir = redir->nfile.next) != NULL); INT_ON; if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0) preverrout_fd = copied_fd2; } /* * Undo the effects of the last redirection. */ static void popredir(int drop, int restore) { struct redirtab *rp; int i; if (--g_nullredirs >= 0) return; INT_OFF; rp = redirlist; for (i = 0; i < rp->pair_count; i++) { int fd = rp->two_fd[i].orig; int copy = rp->two_fd[i].copy; if (copy == CLOSED) { if (!drop) close(fd); continue; } if (copy != EMPTY) { if (!drop || (restore && (copy & COPYFD_RESTORE))) { copy &= ~COPYFD_RESTORE; /*close(fd);*/ copyfd(copy, fd | COPYFD_EXACT); } close(copy & ~COPYFD_RESTORE); } } redirlist = rp->next; g_nullredirs = rp->nullredirs; free(rp); INT_ON; } /* * Undo all redirections. Called on error or interrupt. */ /* * Discard all saved file descriptors. */ static void clearredir(int drop) { for (;;) { g_nullredirs = 0; if (!redirlist) break; popredir(drop, /*restore:*/ 0); } } static int redirectsafe(union node *redir, int flags) { int err; volatile int saveint; struct jmploc *volatile savehandler = exception_handler; struct jmploc jmploc; SAVE_INT(saveint); /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */ err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2; if (!err) { exception_handler = &jmploc; redirect(redir, flags); } exception_handler = savehandler; if (err && exception_type != EXERROR) longjmp(exception_handler->loc, 1); RESTORE_INT(saveint); return err; } /* ============ Routines to expand arguments to commands * * We have to deal with backquotes, shell variables, and file metacharacters. */ #if ENABLE_SH_MATH_SUPPORT static arith_t ash_arith(const char *s) { arith_state_t math_state; arith_t result; math_state.lookupvar = lookupvar; math_state.setvar = setvar2; //math_state.endofname = endofname; INT_OFF; result = arith(&math_state, s); if (math_state.errmsg) ash_msg_and_raise_error(math_state.errmsg); INT_ON; return result; } #endif /* * expandarg flags */ #define EXP_FULL 0x1 /* perform word splitting & file globbing */ #define EXP_TILDE 0x2 /* do normal tilde expansion */ #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ #define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ #define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */ #define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ #define EXP_WORD 0x80 /* expand word in parameter expansion */ #define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ /* * rmescape() flags */ #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ #define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */ #define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ /* * Structure specifying which parts of the string should be searched * for IFS characters. */ struct ifsregion { struct ifsregion *next; /* next region in list */ int begoff; /* offset of start of region */ int endoff; /* offset of end of region */ int nulonly; /* search for nul bytes only */ }; struct arglist { struct strlist *list; struct strlist **lastp; }; /* output of current string */ static char *expdest; /* list of back quote expressions */ static struct nodelist *argbackq; /* first struct in list of ifs regions */ static struct ifsregion ifsfirst; /* last struct in list */ static struct ifsregion *ifslastp; /* holds expanded arg list */ static struct arglist exparg; /* * Our own itoa(). */ #if !ENABLE_SH_MATH_SUPPORT /* cvtnum() is used even if math support is off (to prepare $? values and such) */ typedef long arith_t; # define ARITH_FMT "%ld" #endif static int cvtnum(arith_t num) { int len; expdest = makestrspace(32, expdest); len = fmtstr(expdest, 32, ARITH_FMT, num); STADJUST(len, expdest); return len; } static size_t esclen(const char *start, const char *p) { size_t esc = 0; while (p > start && (unsigned char)*--p == CTLESC) { esc++; } return esc; } /* * Remove any CTLESC characters from a string. */ static char * rmescapes(char *str, int flag) { static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' }; char *p, *q, *r; unsigned inquotes; unsigned protect_against_glob; unsigned globbing; p = strpbrk(str, qchars); if (!p) return str; q = p; r = str; if (flag & RMESCAPE_ALLOC) { size_t len = p - str; size_t fulllen = len + strlen(p) + 1; if (flag & RMESCAPE_GROW) { int strloc = str - (char *)stackblock(); r = makestrspace(fulllen, expdest); /* p and str may be invalidated by makestrspace */ str = (char *)stackblock() + strloc; p = str + len; } else if (flag & RMESCAPE_HEAP) { r = ckmalloc(fulllen); } else { r = stalloc(fulllen); } q = r; if (len > 0) { q = (char *)memcpy(q, str, len) + len; } } inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; globbing = flag & RMESCAPE_GLOB; protect_against_glob = globbing; while (*p) { if ((unsigned char)*p == CTLQUOTEMARK) { // TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0 // (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok? // Note: both inquotes and protect_against_glob only affect whether // CTLESC,<ch> gets converted to <ch> or to \<ch> inquotes = ~inquotes; p++; protect_against_glob = globbing; continue; } if (*p == '\\') { /* naked back slash */ protect_against_glob = 0; goto copy; } if ((unsigned char)*p == CTLESC) { p++; if (protect_against_glob && inquotes && *p != '/') { *q++ = '\\'; } } protect_against_glob = globbing; copy: *q++ = *p++; } *q = '\0'; if (flag & RMESCAPE_GROW) { expdest = r; STADJUST(q - r + 1, expdest); } return r; } #define pmatch(a, b) !fnmatch((a), (b), 0) /* * Prepare a pattern for a expmeta (internal glob(3)) call. * * Returns an stalloced string. */ static char * preglob(const char *pattern, int quoted, int flag) { flag |= RMESCAPE_GLOB; if (quoted) { flag |= RMESCAPE_QUOTED; } return rmescapes((char *)pattern, flag); } /* * Put a string on the stack. */ static void memtodest(const char *p, size_t len, int syntax, int quotes) { char *q = expdest; q = makestrspace(quotes ? len * 2 : len, q); while (len--) { unsigned char c = *p++; if (c == '\0') continue; if (quotes) { int n = SIT(c, syntax); if (n == CCTL || n == CBACK) USTPUTC(CTLESC, q); } USTPUTC(c, q); } expdest = q; } static void strtodest(const char *p, int syntax, int quotes) { memtodest(p, strlen(p), syntax, quotes); } /* * Record the fact that we have to scan this region of the * string for IFS characters. */ static void recordregion(int start, int end, int nulonly) { struct ifsregion *ifsp; if (ifslastp == NULL) { ifsp = &ifsfirst; } else { INT_OFF; ifsp = ckzalloc(sizeof(*ifsp)); /*ifsp->next = NULL; - ckzalloc did it */ ifslastp->next = ifsp; INT_ON; } ifslastp = ifsp; ifslastp->begoff = start; ifslastp->endoff = end; ifslastp->nulonly = nulonly; } static void removerecordregions(int endoff) { if (ifslastp == NULL) return; if (ifsfirst.endoff > endoff) { while (ifsfirst.next) { struct ifsregion *ifsp; INT_OFF; ifsp = ifsfirst.next->next; free(ifsfirst.next); ifsfirst.next = ifsp; INT_ON; } if (ifsfirst.begoff > endoff) { ifslastp = NULL; } else { ifslastp = &ifsfirst; ifsfirst.endoff = endoff; } return; } ifslastp = &ifsfirst; while (ifslastp->next && ifslastp->next->begoff < endoff) ifslastp = ifslastp->next; while (ifslastp->next) { struct ifsregion *ifsp; INT_OFF; ifsp = ifslastp->next->next; free(ifslastp->next); ifslastp->next = ifsp; INT_ON; } if (ifslastp->endoff > endoff) ifslastp->endoff = endoff; } static char * exptilde(char *startp, char *p, int flags) { unsigned char c; char *name; struct passwd *pw; const char *home; int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); int startloc; name = p + 1; while ((c = *++p) != '\0') { switch (c) { case CTLESC: return startp; case CTLQUOTEMARK: return startp; case ':': if (flags & EXP_VARTILDE) goto done; break; case '/': case CTLENDVAR: goto done; } } done: *p = '\0'; if (*name == '\0') { home = lookupvar("HOME"); } else { pw = getpwnam(name); if (pw == NULL) goto lose; home = pw->pw_dir; } if (!home || !*home) goto lose; *p = c; startloc = expdest - (char *)stackblock(); strtodest(home, SQSYNTAX, quotes); recordregion(startloc, expdest - (char *)stackblock(), 0); return p; lose: *p = c; return startp; } /* * Execute a command inside back quotes. If it's a builtin command, we * want to save its output in a block obtained from malloc. Otherwise * we fork off a subprocess and get the output of the command via a pipe. * Should be called with interrupts off. */ struct backcmd { /* result of evalbackcmd */ int fd; /* file descriptor to read from */ int nleft; /* number of chars in buffer */ char *buf; /* buffer */ struct job *jp; /* job structure for command */ }; /* These forward decls are needed to use "eval" code for backticks handling: */ static uint8_t back_exitstatus; /* exit status of backquoted command */ #define EV_EXIT 01 /* exit after evaluating tree */ static void evaltree(union node *, int); static void FAST_FUNC evalbackcmd(union node *n, struct backcmd *result) { int saveherefd; result->fd = -1; result->buf = NULL; result->nleft = 0; result->jp = NULL; if (n == NULL) goto out; saveherefd = herefd; herefd = -1; { int pip[2]; struct job *jp; if (pipe(pip) < 0) ash_msg_and_raise_error("pipe call failed"); jp = makejob(/*n,*/ 1); if (forkshell(jp, n, FORK_NOJOB) == 0) { FORCE_INT_ON; close(pip[0]); if (pip[1] != 1) { /*close(1);*/ copyfd(pip[1], 1 | COPYFD_EXACT); close(pip[1]); } eflag = 0; evaltree(n, EV_EXIT); /* actually evaltreenr... */ /* NOTREACHED */ } close(pip[1]); result->fd = pip[0]; result->jp = jp; } herefd = saveherefd; out: TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", result->fd, result->buf, result->nleft, result->jp)); } /* * Expand stuff in backwards quotes. */ static void expbackq(union node *cmd, int quoted, int quotes) { struct backcmd in; int i; char buf[128]; char *p; char *dest; int startloc; int syntax = quoted ? DQSYNTAX : BASESYNTAX; struct stackmark smark; INT_OFF; setstackmark(&smark); dest = expdest; startloc = dest - (char *)stackblock(); grabstackstr(dest); evalbackcmd(cmd, &in); popstackmark(&smark); p = in.buf; i = in.nleft; if (i == 0) goto read; for (;;) { memtodest(p, i, syntax, quotes); read: if (in.fd < 0) break; i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1); TRACE(("expbackq: read returns %d\n", i)); if (i <= 0) break; p = buf; } free(in.buf); if (in.fd >= 0) { close(in.fd); back_exitstatus = waitforjob(in.jp); } INT_ON; /* Eat all trailing newlines */ dest = expdest; for (; dest > (char *)stackblock() && dest[-1] == '\n';) STUNPUTC(dest); expdest = dest; if (quoted == 0) recordregion(startloc, dest - (char *)stackblock(), 0); TRACE(("evalbackq: size:%d:'%.*s'\n", (int)((dest - (char *)stackblock()) - startloc), (int)((dest - (char *)stackblock()) - startloc), stackblock() + startloc)); } #if ENABLE_SH_MATH_SUPPORT /* * Expand arithmetic expression. Backup to start of expression, * evaluate, place result in (backed up) result, adjust string position. */ static void expari(int quotes) { char *p, *start; int begoff; int flag; int len; /* ifsfree(); */ /* * This routine is slightly over-complicated for * efficiency. Next we scan backwards looking for the * start of arithmetic. */ start = stackblock(); p = expdest - 1; *p = '\0'; p--; while (1) { int esc; while ((unsigned char)*p != CTLARI) { p--; #if DEBUG if (p < start) { ash_msg_and_raise_error("missing CTLARI (shouldn't happen)"); } #endif } esc = esclen(start, p); if (!(esc % 2)) { break; } p -= esc + 1; } begoff = p - start; removerecordregions(begoff); flag = p[1]; expdest = p; if (quotes) rmescapes(p + 2, 0); len = cvtnum(ash_arith(p + 2)); if (flag != '"') recordregion(begoff, begoff + len, 0); } #endif /* argstr needs it */ static char *evalvar(char *p, int flags, struct strlist *var_str_list); /* * Perform variable and command substitution. If EXP_FULL is set, output CTLESC * characters to allow for further processing. Otherwise treat * $@ like $* since no splitting will be performed. * * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it * for correct expansion of "B=$A" word. */ static void argstr(char *p, int flags, struct strlist *var_str_list) { static const char spclchars[] ALIGN1 = { '=', ':', CTLQUOTEMARK, CTLENDVAR, CTLESC, CTLVAR, CTLBACKQ, CTLBACKQ | CTLQUOTE, #if ENABLE_SH_MATH_SUPPORT CTLENDARI, #endif '\0' }; const char *reject = spclchars; int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ int breakall = flags & EXP_WORD; int inquotes; size_t length; int startloc; if (!(flags & EXP_VARTILDE)) { reject += 2; } else if (flags & EXP_VARTILDE2) { reject++; } inquotes = 0; length = 0; if (flags & EXP_TILDE) { char *q; flags &= ~EXP_TILDE; tilde: q = p; if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD)) q++; if (*q == '~') p = exptilde(p, q, flags); } start: startloc = expdest - (char *)stackblock(); for (;;) { unsigned char c; length += strcspn(p + length, reject); c = p[length]; if (c) { if (!(c & 0x80) IF_SH_MATH_SUPPORT(|| c == CTLENDARI) ) { /* c == '=' || c == ':' || c == CTLENDARI */ length++; } } if (length > 0) { int newloc; expdest = stack_nputstr(p, length, expdest); newloc = expdest - (char *)stackblock(); if (breakall && !inquotes && newloc > startloc) { recordregion(startloc, newloc, 0); } startloc = newloc; } p += length + 1; length = 0; switch (c) { case '\0': goto breakloop; case '=': if (flags & EXP_VARTILDE2) { p--; continue; } flags |= EXP_VARTILDE2; reject++; /* fall through */ case ':': /* * sort of a hack - expand tildes in variable * assignments (after the first '=' and after ':'s). */ if (*--p == '~') { goto tilde; } continue; } switch (c) { case CTLENDVAR: /* ??? */ goto breakloop; case CTLQUOTEMARK: /* "$@" syntax adherence hack */ if (!inquotes && memcmp(p, dolatstr, 4) == 0 && ( p[4] == (char)CTLQUOTEMARK || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK) ) ) { p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1; goto start; } inquotes = !inquotes; addquote: if (quotes) { p--; length++; startloc++; } break; case CTLESC: startloc++; length++; goto addquote; case CTLVAR: TRACE(("argstr: evalvar('%s')\n", p)); p = evalvar(p, flags, var_str_list); TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); goto start; case CTLBACKQ: c = '\0'; case CTLBACKQ|CTLQUOTE: expbackq(argbackq->n, c, quotes); argbackq = argbackq->next; goto start; #if ENABLE_SH_MATH_SUPPORT case CTLENDARI: p--; expari(quotes); goto start; #endif } } breakloop: ; } static char * scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *pattern, int quotes, int zero) { char *loc, *loc2; char c; loc = startp; loc2 = rmesc; do { int match; const char *s = loc2; c = *loc2; if (zero) { *loc2 = '\0'; s = rmesc; } match = pmatch(pattern, s); *loc2 = c; if (match) return loc; if (quotes && (unsigned char)*loc == CTLESC) loc++; loc++; loc2++; } while (c); return NULL; } static char * scanright(char *startp, char *rmesc, char *rmescend, char *pattern, int quotes, int match_at_start) { #if !ENABLE_ASH_OPTIMIZE_FOR_SIZE int try2optimize = match_at_start; #endif int esc = 0; char *loc; char *loc2; /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}": * startp="escaped_value_of_v" rmesc="raw_value_of_v" * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1 * Logic: * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc, * and on each iteration they go back two/one char until they reach the beginning. * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc. */ /* TODO: document in what other circumstances we are called. */ for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) { int match; char c = *loc2; const char *s = loc2; if (match_at_start) { *loc2 = '\0'; s = rmesc; } match = pmatch(pattern, s); //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match); *loc2 = c; if (match) return loc; #if !ENABLE_ASH_OPTIMIZE_FOR_SIZE if (try2optimize) { /* Maybe we can optimize this: * if pattern ends with unescaped *, we can avoid checking * shorter strings: if "foo*" doesnt match "raw_value_of_v", * it wont match truncated "raw_value_of_" strings too. */ unsigned plen = strlen(pattern); /* Does it end with "*"? */ if (plen != 0 && pattern[--plen] == '*') { /* "xxxx*" is not escaped */ /* "xxx\*" is escaped */ /* "xx\\*" is not escaped */ /* "x\\\*" is escaped */ int slashes = 0; while (plen != 0 && pattern[--plen] == '\\') slashes++; if (!(slashes & 1)) break; /* ends with unescaped "*" */ } try2optimize = 0; } #endif loc--; if (quotes) { if (--esc < 0) { esc = esclen(startp, loc); } if (esc % 2) { esc--; loc--; } } } return NULL; } static void varunset(const char *, const char *, const char *, int) NORETURN; static void varunset(const char *end, const char *var, const char *umsg, int varflags) { const char *msg; const char *tail; tail = nullstr; msg = "parameter not set"; if (umsg) { if ((unsigned char)*end == CTLENDVAR) { if (varflags & VSNUL) tail = " or null"; } else { msg = umsg; } } ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail); } #if ENABLE_ASH_BASH_COMPAT static char * parse_sub_pattern(char *arg, int varflags) { char *idx, *repl = NULL; unsigned char c; //char *org_arg = arg; //bb_error_msg("arg:'%s' varflags:%x", arg, varflags); idx = arg; while (1) { c = *arg; if (!c) break; if (c == '/') { /* Only the first '/' seen is our separator */ if (!repl) { repl = idx + 1; c = '\0'; } } *idx++ = c; arg++; /* * Example: v='ab\c'; echo ${v/\\b/_\\_\z_} * The result is a_\_z_c (not a\_\_z_c)! * * Enable debug prints in this function and you'll see: * ash: arg:'\\b/_\\_z_' varflags:d * ash: pattern:'\\b' repl:'_\_z_' * That is, \\b is interpreted as \\b, but \\_ as \_! * IOW: search pattern and replace string treat backslashes * differently! That is the reason why we check repl below: */ if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE)) arg++; /* skip both '\', not just first one */ } *idx = c; /* NUL */ //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl); return repl; } #endif /* ENABLE_ASH_BASH_COMPAT */ static const char * subevalvar(char *p, char *varname, int strloc, int subtype, int startloc, int varflags, int quotes, struct strlist *var_str_list) { struct nodelist *saveargbackq = argbackq; char *startp; char *loc; char *rmesc, *rmescend; char *str; IF_ASH_BASH_COMPAT(const char *repl = NULL;) IF_ASH_BASH_COMPAT(int pos, len, orig_len;) int saveherefd = herefd; int amount, resetloc; IF_ASH_BASH_COMPAT(int workloc;) int zero; char *(*scan)(char*, char*, char*, char*, int, int); //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)", // p, varname, strloc, subtype, startloc, varflags, quotes); herefd = -1; argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0, var_str_list); STPUTC('\0', expdest); herefd = saveherefd; argbackq = saveargbackq; startp = (char *)stackblock() + startloc; switch (subtype) { case VSASSIGN: setvar2(varname, startp); amount = startp - expdest; STADJUST(amount, expdest); return startp; case VSQUESTION: varunset(p, varname, startp, varflags); /* NOTREACHED */ #if ENABLE_ASH_BASH_COMPAT case VSSUBSTR: loc = str = stackblock() + strloc; /* Read POS in ${var:POS:LEN} */ pos = atoi(loc); /* number(loc) errors out on "1:4" */ len = str - startp - 1; /* *loc != '\0', guaranteed by parser */ if (quotes) { char *ptr; /* Adjust the length by the number of escapes */ for (ptr = startp; ptr < (str - 1); ptr++) { if ((unsigned char)*ptr == CTLESC) { len--; ptr++; } } } orig_len = len; if (*loc++ == ':') { /* ${var::LEN} */ len = number(loc); } else { /* Skip POS in ${var:POS:LEN} */ len = orig_len; while (*loc && *loc != ':') { /* TODO? * bash complains on: var=qwe; echo ${var:1a:123} if (!isdigit(*loc)) ash_msg_and_raise_error(msg_illnum, str); */ loc++; } if (*loc++ == ':') { len = number(loc); } } if (pos >= orig_len) { pos = 0; len = 0; } if (len > (orig_len - pos)) len = orig_len - pos; for (str = startp; pos; str++, pos--) { if (quotes && (unsigned char)*str == CTLESC) str++; } for (loc = startp; len; len--) { if (quotes && (unsigned char)*str == CTLESC) *loc++ = *str++; *loc++ = *str++; } *loc = '\0'; amount = loc - expdest; STADJUST(amount, expdest); return loc; #endif } resetloc = expdest - (char *)stackblock(); /* We'll comeback here if we grow the stack while handling * a VSREPLACE or VSREPLACEALL, since our pointers into the * stack will need rebasing, and we'll need to remove our work * areas each time */ IF_ASH_BASH_COMPAT(restart:) amount = expdest - ((char *)stackblock() + resetloc); STADJUST(-amount, expdest); startp = (char *)stackblock() + startloc; rmesc = startp; rmescend = (char *)stackblock() + strloc; if (quotes) { rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); if (rmesc != startp) { rmescend = expdest; startp = (char *)stackblock() + startloc; } } rmescend--; str = (char *)stackblock() + strloc; preglob(str, varflags & VSQUOTE, 0); #if ENABLE_ASH_BASH_COMPAT workloc = expdest - (char *)stackblock(); if (subtype == VSREPLACE || subtype == VSREPLACEALL) { char *idx, *end; if (!repl) { repl = parse_sub_pattern(str, varflags); //bb_error_msg("repl:'%s'", repl); if (!repl) repl = nullstr; } /* If there's no pattern to match, return the expansion unmolested */ if (str[0] == '\0') return NULL; len = 0; idx = startp; end = str - 1; while (idx < end) { try_to_match: loc = scanright(idx, rmesc, rmescend, str, quotes, 1); //bb_error_msg("scanright('%s'):'%s'", str, loc); if (!loc) { /* No match, advance */ char *restart_detect = stackblock(); skip_matching: STPUTC(*idx, expdest); if (quotes && (unsigned char)*idx == CTLESC) { idx++; len++; STPUTC(*idx, expdest); } if (stackblock() != restart_detect) goto restart; idx++; len++; rmesc++; /* continue; - prone to quadratic behavior, smarter code: */ if (idx >= end) break; if (str[0] == '*') { /* Pattern is "*foo". If "*foo" does not match "long_string", * it would never match "ong_string" etc, no point in trying. */ goto skip_matching; } goto try_to_match; } if (subtype == VSREPLACEALL) { while (idx < loc) { if (quotes && (unsigned char)*idx == CTLESC) idx++; idx++; rmesc++; } } else { idx = loc; } //bb_error_msg("repl:'%s'", repl); for (loc = (char*)repl; *loc; loc++) { char *restart_detect = stackblock(); if (quotes && *loc == '\\') { STPUTC(CTLESC, expdest); len++; } STPUTC(*loc, expdest); if (stackblock() != restart_detect) goto restart; len++; } if (subtype == VSREPLACE) { //bb_error_msg("tail:'%s', quotes:%x", idx, quotes); while (*idx) { char *restart_detect = stackblock(); STPUTC(*idx, expdest); if (stackblock() != restart_detect) goto restart; len++; idx++; } break; } } /* We've put the replaced text into a buffer at workloc, now * move it to the right place and adjust the stack. */ STPUTC('\0', expdest); startp = (char *)stackblock() + startloc; memmove(startp, (char *)stackblock() + workloc, len + 1); //bb_error_msg("startp:'%s'", startp); amount = expdest - (startp + len); STADJUST(-amount, expdest); return startp; } #endif /* ENABLE_ASH_BASH_COMPAT */ subtype -= VSTRIMRIGHT; #if DEBUG if (subtype < 0 || subtype > 7) abort(); #endif /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */ zero = subtype >> 1; /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */ scan = (subtype & 1) ^ zero ? scanleft : scanright; loc = scan(startp, rmesc, rmescend, str, quotes, zero); if (loc) { if (zero) { memmove(startp, loc, str - loc); loc = startp + (str - loc) - 1; } *loc = '\0'; amount = loc - expdest; STADJUST(amount, expdest); } return loc; } /* * Add the value of a specialized variable to the stack string. * name parameter (examples): * ash -c 'echo $1' name:'1=' * ash -c 'echo $qwe' name:'qwe=' * ash -c 'echo $$' name:'$=' * ash -c 'echo ${$}' name:'$=' * ash -c 'echo ${$##q}' name:'$=q' * ash -c 'echo ${#$}' name:'$=' * note: examples with bad shell syntax: * ash -c 'echo ${#$1}' name:'$=1' * ash -c 'echo ${#1#}' name:'1=#' */ static NOINLINE ssize_t varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) { const char *p; int num; int i; int sepq = 0; ssize_t len = 0; int subtype = varflags & VSTYPE; int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); int quoted = varflags & VSQUOTE; int syntax = quoted ? DQSYNTAX : BASESYNTAX; switch (*name) { case '$': num = rootpid; goto numvar; case '?': num = exitstatus; goto numvar; case '#': num = shellparam.nparam; goto numvar; case '!': num = backgndpid; if (num == 0) return -1; numvar: len = cvtnum(num); goto check_1char_name; case '-': expdest = makestrspace(NOPTS, expdest); for (i = NOPTS - 1; i >= 0; i--) { if (optlist[i]) { USTPUTC(optletters(i), expdest); len++; } } check_1char_name: #if 0 /* handles cases similar to ${#$1} */ if (name[2] != '\0') raise_error_syntax("bad substitution"); #endif break; case '@': { char **ap; int sep; if (quoted && (flags & EXP_FULL)) { /* note: this is not meant as PEOF value */ sep = 1 << CHAR_BIT; goto param; } /* fall through */ case '*': sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' '; i = SIT(sep, syntax); if (quotes && (i == CCTL || i == CBACK)) sepq = 1; param: ap = shellparam.p; if (!ap) return -1; while ((p = *ap++) != NULL) { size_t partlen; partlen = strlen(p); len += partlen; if (!(subtype == VSPLUS || subtype == VSLENGTH)) memtodest(p, partlen, syntax, quotes); if (*ap && sep) { char *q; len++; if (subtype == VSPLUS || subtype == VSLENGTH) { continue; } q = expdest; if (sepq) STPUTC(CTLESC, q); /* note: may put NUL despite sep != 0 * (see sep = 1 << CHAR_BIT above) */ STPUTC(sep, q); expdest = q; } } return len; } /* case '@' and '*' */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': num = atoi(name); /* number(name) fails on ${N#str} etc */ if (num < 0 || num > shellparam.nparam) return -1; p = num ? shellparam.p[num - 1] : arg0; goto value; default: /* NB: name has form "VAR=..." */ /* "A=a B=$A" case: var_str_list is a list of "A=a" strings * which should be considered before we check variables. */ if (var_str_list) { unsigned name_len = (strchrnul(name, '=') - name) + 1; p = NULL; do { char *str, *eq; str = var_str_list->text; eq = strchr(str, '='); if (!eq) /* stop at first non-assignment */ break; eq++; if (name_len == (unsigned)(eq - str) && strncmp(str, name, name_len) == 0 ) { p = eq; /* goto value; - WRONG! */ /* think "A=1 A=2 B=$A" */ } var_str_list = var_str_list->next; } while (var_str_list); if (p) goto value; } p = lookupvar(name); value: if (!p) return -1; len = strlen(p); if (!(subtype == VSPLUS || subtype == VSLENGTH)) memtodest(p, len, syntax, quotes); return len; } if (subtype == VSPLUS || subtype == VSLENGTH) STADJUST(-len, expdest); return len; } /* * Expand a variable, and return a pointer to the next character in the * input string. */ static char * evalvar(char *p, int flags, struct strlist *var_str_list) { char varflags; char subtype; char quoted; char easy; char *var; int patloc; int startloc; ssize_t varlen; varflags = (unsigned char) *p++; subtype = varflags & VSTYPE; quoted = varflags & VSQUOTE; var = p; easy = (!quoted || (*var == '@' && shellparam.nparam)); startloc = expdest - (char *)stackblock(); p = strchr(p, '=') + 1; //TODO: use var_end(p)? again: varlen = varvalue(var, varflags, flags, var_str_list); if (varflags & VSNUL) varlen--; if (subtype == VSPLUS) { varlen = -1 - varlen; goto vsplus; } if (subtype == VSMINUS) { vsplus: if (varlen < 0) { argstr( p, flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD), var_str_list ); goto end; } if (easy) goto record; goto end; } if (subtype == VSASSIGN || subtype == VSQUESTION) { if (varlen < 0) { if (subevalvar(p, var, /* strloc: */ 0, subtype, startloc, varflags, /* quotes: */ 0, var_str_list) ) { varflags &= ~VSNUL; /* * Remove any recorded regions beyond * start of variable */ removerecordregions(startloc); goto again; } goto end; } if (easy) goto record; goto end; } if (varlen < 0 && uflag) varunset(p, var, 0, 0); if (subtype == VSLENGTH) { cvtnum(varlen > 0 ? varlen : 0); goto record; } if (subtype == VSNORMAL) { if (easy) goto record; goto end; } #if DEBUG switch (subtype) { case VSTRIMLEFT: case VSTRIMLEFTMAX: case VSTRIMRIGHT: case VSTRIMRIGHTMAX: #if ENABLE_ASH_BASH_COMPAT case VSSUBSTR: case VSREPLACE: case VSREPLACEALL: #endif break; default: abort(); } #endif if (varlen >= 0) { /* * Terminate the string and start recording the pattern * right after it */ STPUTC('\0', expdest); patloc = expdest - (char *)stackblock(); if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, startloc, varflags, /* quotes: */ flags & (EXP_FULL | EXP_CASE | EXP_REDIR), var_str_list) ) { int amount = expdest - ( (char *)stackblock() + patloc - 1 ); STADJUST(-amount, expdest); } /* Remove any recorded regions beyond start of variable */ removerecordregions(startloc); record: recordregion(startloc, expdest - (char *)stackblock(), quoted); } end: if (subtype != VSNORMAL) { /* skip to end of alternative */ int nesting = 1; for (;;) { unsigned char c = *p++; if (c == CTLESC) p++; else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { if (varlen >= 0) argbackq = argbackq->next; } else if (c == CTLVAR) { if ((*p++ & VSTYPE) != VSNORMAL) nesting++; } else if (c == CTLENDVAR) { if (--nesting == 0) break; } } } return p; } /* * Break the argument string into pieces based upon IFS and add the * strings to the argument list. The regions of the string to be * searched for IFS characters have been stored by recordregion. */ static void ifsbreakup(char *string, struct arglist *arglist) { struct ifsregion *ifsp; struct strlist *sp; char *start; char *p; char *q; const char *ifs, *realifs; int ifsspc; int nulonly; start = string; if (ifslastp != NULL) { ifsspc = 0; nulonly = 0; realifs = ifsset() ? ifsval() : defifs; ifsp = &ifsfirst; do { p = string + ifsp->begoff; nulonly = ifsp->nulonly; ifs = nulonly ? nullstr : realifs; ifsspc = 0; while (p < string + ifsp->endoff) { q = p; if ((unsigned char)*p == CTLESC) p++; if (!strchr(ifs, *p)) { p++; continue; } if (!nulonly) ifsspc = (strchr(defifs, *p) != NULL); /* Ignore IFS whitespace at start */ if (q == start && ifsspc) { p++; start = p; continue; } *q = '\0'; sp = stzalloc(sizeof(*sp)); sp->text = start; *arglist->lastp = sp; arglist->lastp = &sp->next; p++; if (!nulonly) { for (;;) { if (p >= string + ifsp->endoff) { break; } q = p; if ((unsigned char)*p == CTLESC) p++; if (strchr(ifs, *p) == NULL) { p = q; break; } if (strchr(defifs, *p) == NULL) { if (ifsspc) { p++; ifsspc = 0; } else { p = q; break; } } else p++; } } start = p; } /* while */ ifsp = ifsp->next; } while (ifsp != NULL); if (nulonly) goto add; } if (!*start) return; add: sp = stzalloc(sizeof(*sp)); sp->text = start; *arglist->lastp = sp; arglist->lastp = &sp->next; } static void ifsfree(void) { struct ifsregion *p; INT_OFF; p = ifsfirst.next; do { struct ifsregion *ifsp; ifsp = p->next; free(p); p = ifsp; } while (p); ifslastp = NULL; ifsfirst.next = NULL; INT_ON; } /* * Add a file name to the list. */ static void addfname(const char *name) { struct strlist *sp; sp = stzalloc(sizeof(*sp)); sp->text = ststrdup(name); *exparg.lastp = sp; exparg.lastp = &sp->next; } /* * Do metacharacter (i.e. *, ?, [...]) expansion. */ static void expmeta(char *expdir, char *enddir, char *name) { char *p; const char *cp; char *start; char *endname; int metaflag; struct stat statb; DIR *dirp; struct dirent *dp; int atend; int matchdot; metaflag = 0; start = name; for (p = name; *p; p++) { if (*p == '*' || *p == '?') metaflag = 1; else if (*p == '[') { char *q = p + 1; if (*q == '!') q++; for (;;) { if (*q == '\\') q++; if (*q == '/' || *q == '\0') break; if (*++q == ']') { metaflag = 1; break; } } } else if (*p == '\\') p++; else if (*p == '/') { if (metaflag) goto out; start = p + 1; } } out: if (metaflag == 0) { /* we've reached the end of the file name */ if (enddir != expdir) metaflag++; p = name; do { if (*p == '\\') p++; *enddir++ = *p; } while (*p++); if (metaflag == 0 || lstat(expdir, &statb) >= 0) addfname(expdir); return; } endname = p; if (name < start) { p = name; do { if (*p == '\\') p++; *enddir++ = *p++; } while (p < start); } if (enddir == expdir) { cp = "."; } else if (enddir == expdir + 1 && *expdir == '/') { cp = "/"; } else { cp = expdir; enddir[-1] = '\0'; } dirp = opendir(cp); if (dirp == NULL) return; if (enddir != expdir) enddir[-1] = '/'; if (*endname == 0) { atend = 1; } else { atend = 0; *endname++ = '\0'; } matchdot = 0; p = start; if (*p == '\\') p++; if (*p == '.') matchdot++; while (!pending_int && (dp = readdir(dirp)) != NULL) { if (dp->d_name[0] == '.' && !matchdot) continue; if (pmatch(start, dp->d_name)) { if (atend) { strcpy(enddir, dp->d_name); addfname(expdir); } else { for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';) continue; p[-1] = '/'; expmeta(expdir, p, endname); } } } closedir(dirp); if (!atend) endname[-1] = '/'; } static struct strlist * msort(struct strlist *list, int len) { struct strlist *p, *q = NULL; struct strlist **lpp; int half; int n; if (len <= 1) return list; half = len >> 1; p = list; for (n = half; --n >= 0;) { q = p; p = p->next; } q->next = NULL; /* terminate first half of list */ q = msort(list, half); /* sort first half of list */ p = msort(p, len - half); /* sort second half */ lpp = &list; for (;;) { #if ENABLE_LOCALE_SUPPORT if (strcoll(p->text, q->text) < 0) #else if (strcmp(p->text, q->text) < 0) #endif { *lpp = p; lpp = &p->next; p = *lpp; if (p == NULL) { *lpp = q; break; } } else { *lpp = q; lpp = &q->next; q = *lpp; if (q == NULL) { *lpp = p; break; } } } return list; } /* * Sort the results of file name expansion. It calculates the number of * strings to sort and then calls msort (short for merge sort) to do the * work. */ static struct strlist * expsort(struct strlist *str) { int len; struct strlist *sp; len = 0; for (sp = str; sp; sp = sp->next) len++; return msort(str, len); } static void expandmeta(struct strlist *str /*, int flag*/) { static const char metachars[] ALIGN1 = { '*', '?', '[', 0 }; /* TODO - EXP_REDIR */ while (str) { char *expdir; struct strlist **savelastp; struct strlist *sp; char *p; if (fflag) goto nometa; if (!strpbrk(str->text, metachars)) goto nometa; savelastp = exparg.lastp; INT_OFF; p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP); { int i = strlen(str->text); expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ } expmeta(expdir, expdir, p); free(expdir); if (p != str->text) free(p); INT_ON; if (exparg.lastp == savelastp) { /* * no matches */ nometa: *exparg.lastp = str; rmescapes(str->text, 0); exparg.lastp = &str->next; } else { *exparg.lastp = NULL; *savelastp = sp = expsort(*savelastp); while (sp->next != NULL) sp = sp->next; exparg.lastp = &sp->next; } str = str->next; } } /* * Perform variable substitution and command substitution on an argument, * placing the resulting list of arguments in arglist. If EXP_FULL is true, * perform splitting and file name expansion. When arglist is NULL, perform * here document expansion. */ static void expandarg(union node *arg, struct arglist *arglist, int flag) { struct strlist *sp; char *p; argbackq = arg->narg.backquote; STARTSTACKSTR(expdest); ifsfirst.next = NULL; ifslastp = NULL; TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag)); argstr(arg->narg.text, flag, /* var_str_list: */ arglist ? arglist->list : NULL); p = _STPUTC('\0', expdest); expdest = p - 1; if (arglist == NULL) { return; /* here document expanded */ } p = grabstackstr(p); TRACE(("expandarg: p:'%s'\n", p)); exparg.lastp = &exparg.list; /* * TODO - EXP_REDIR */ if (flag & EXP_FULL) { ifsbreakup(p, &exparg); *exparg.lastp = NULL; exparg.lastp = &exparg.list; expandmeta(exparg.list /*, flag*/); } else { if (flag & EXP_REDIR) { /*XXX - for now, just remove escapes */ rmescapes(p, 0); TRACE(("expandarg: rmescapes:'%s'\n", p)); } sp = stzalloc(sizeof(*sp)); sp->text = p; *exparg.lastp = sp; exparg.lastp = &sp->next; } if (ifsfirst.next) ifsfree(); *exparg.lastp = NULL; if (exparg.list) { *arglist->lastp = exparg.list; arglist->lastp = exparg.lastp; } } /* * Expand shell variables and backquotes inside a here document. */ static void expandhere(union node *arg, int fd) { herefd = fd; expandarg(arg, (struct arglist *)NULL, 0); full_write(fd, stackblock(), expdest - (char *)stackblock()); } /* * Returns true if the pattern matches the string. */ static int patmatch(char *pattern, const char *string) { return pmatch(preglob(pattern, 0, 0), string); } /* * See if a pattern matches in a case statement. */ static int casematch(union node *pattern, char *val) { struct stackmark smark; int result; setstackmark(&smark); argbackq = pattern->narg.backquote; STARTSTACKSTR(expdest); ifslastp = NULL; argstr(pattern->narg.text, EXP_TILDE | EXP_CASE, /* var_str_list: */ NULL); STACKSTRNUL(expdest); result = patmatch(stackblock(), val); popstackmark(&smark); return result; } /* ============ find_command */ struct builtincmd { const char *name; int (*builtin)(int, char **) FAST_FUNC; /* unsigned flags; */ }; #define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1) /* "regular" builtins always take precedence over commands, * regardless of PATH=....%builtin... position */ #define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2) #define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4) struct cmdentry { smallint cmdtype; /* CMDxxx */ union param { int index; /* index >= 0 for commands without path (slashes) */ /* (TODO: what exactly does the value mean? PATH position?) */ /* index == -1 for commands with slashes */ /* index == (-2 - applet_no) for NOFORK applets */ const struct builtincmd *cmd; struct funcnode *func; } u; }; /* values of cmdtype */ #define CMDUNKNOWN -1 /* no entry in table for command */ #define CMDNORMAL 0 /* command is an executable program */ #define CMDFUNCTION 1 /* command is a shell function */ #define CMDBUILTIN 2 /* command is a shell builtin */ /* action to find_command() */ #define DO_ERR 0x01 /* prints errors */ #define DO_ABS 0x02 /* checks absolute paths */ #define DO_NOFUNC 0x04 /* don't return shell functions, for command */ #define DO_ALTPATH 0x08 /* using alternate path */ #define DO_ALTBLTIN 0x20 /* %builtin in alt. path */ static void find_command(char *, struct cmdentry *, int, const char *); /* ============ Hashing commands */ /* * When commands are first encountered, they are entered in a hash table. * This ensures that a full path search will not have to be done for them * on each invocation. * * We should investigate converting to a linear search, even though that * would make the command name "hash" a misnomer. */ struct tblentry { struct tblentry *next; /* next entry in hash chain */ union param param; /* definition of builtin function */ smallint cmdtype; /* CMDxxx */ char rehash; /* if set, cd done since entry created */ char cmdname[1]; /* name of command */ }; static struct tblentry **cmdtable; #define INIT_G_cmdtable() do { \ cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \ } while (0) static int builtinloc = -1; /* index in path of %builtin, or -1 */ static void tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp) { #if ENABLE_FEATURE_SH_STANDALONE if (applet_no >= 0) { if (APPLET_IS_NOEXEC(applet_no)) { clearenv(); while (*envp) putenv(*envp++); run_applet_no_and_exit(applet_no, argv); } /* re-exec ourselves with the new arguments */ execve(bb_busybox_exec_path, argv, envp); /* If they called chroot or otherwise made the binary no longer * executable, fall through */ } #endif repeat: #ifdef SYSV do { execve(cmd, argv, envp); } while (errno == EINTR); #else execve(cmd, argv, envp); #endif if (cmd == (char*) bb_busybox_exec_path) { /* We already visited ENOEXEC branch below, don't do it again */ //TODO: try execve(initial_argv0_of_shell, argv, envp) before giving up? free(argv); return; } if (errno == ENOEXEC) { /* Run "cmd" as a shell script: * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html * "If the execve() function fails with ENOEXEC, the shell * shall execute a command equivalent to having a shell invoked * with the command name as its first operand, * with any remaining arguments passed to the new shell" * * That is, do not use $SHELL, user's shell, or /bin/sh; * just call ourselves. * * Note that bash reads ~80 chars of the file, and if it sees * a zero byte before it sees newline, it doesn't try to * interpret it, but fails with "cannot execute binary file" * message and exit code 126. For one, this prevents attempts * to interpret foreign ELF binaries as shell scripts. */ char **ap; char **new; for (ap = argv; *ap; ap++) continue; new = ckmalloc((ap - argv + 2) * sizeof(new[0])); new[0] = (char*) "ash"; new[1] = cmd; ap = new + 2; while ((*ap++ = *++argv) != NULL) continue; cmd = (char*) bb_busybox_exec_path; argv = new; goto repeat; } } /* * Exec a program. Never returns. If you change this routine, you may * have to change the find_command routine as well. */ static void shellexec(char **, const char *, int) NORETURN; static void shellexec(char **argv, const char *path, int idx) { char *cmdname; int e; char **envp; int exerrno; int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ clearredir(/*drop:*/ 1); envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL); if (strchr(argv[0], '/') != NULL #if ENABLE_FEATURE_SH_STANDALONE || (applet_no = find_applet_by_name(argv[0])) >= 0 #endif ) { tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp); if (applet_no >= 0) { /* We tried execing ourself, but it didn't work. * Maybe /proc/self/exe doesn't exist? * Try $PATH search. */ goto try_PATH; } e = errno; } else { try_PATH: e = ENOENT; while ((cmdname = path_advance(&path, argv[0])) != NULL) { if (--idx < 0 && pathopt == NULL) { tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp); if (errno != ENOENT && errno != ENOTDIR) e = errno; } stunalloc(cmdname); } } /* Map to POSIX errors */ switch (e) { case EACCES: exerrno = 126; break; case ENOENT: exerrno = 127; break; default: exerrno = 2; break; } exitstatus = exerrno; TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n", argv[0], e, suppress_int)); ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found")); /* NOTREACHED */ } static void printentry(struct tblentry *cmdp) { int idx; const char *path; char *name; idx = cmdp->param.index; path = pathval(); do { name = path_advance(&path, cmdp->cmdname); stunalloc(name); } while (--idx >= 0); out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); } /* * Clear out command entries. The argument specifies the first entry in * PATH which has changed. */ static void clearcmdentry(int firstchange) { struct tblentry **tblp; struct tblentry **pp; struct tblentry *cmdp; INT_OFF; for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) { pp = tblp; while ((cmdp = *pp) != NULL) { if ((cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange) || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) ) { *pp = cmdp->next; free(cmdp); } else { pp = &cmdp->next; } } } INT_ON; } /* * Locate a command in the command hash table. If "add" is nonzero, * add the command to the table if it is not already present. The * variable "lastcmdentry" is set to point to the address of the link * pointing to the entry, so that delete_cmd_entry can delete the * entry. * * Interrupts must be off if called with add != 0. */ static struct tblentry **lastcmdentry; static struct tblentry * cmdlookup(const char *name, int add) { unsigned int hashval; const char *p; struct tblentry *cmdp; struct tblentry **pp; p = name; hashval = (unsigned char)*p << 4; while (*p) hashval += (unsigned char)*p++; hashval &= 0x7FFF; pp = &cmdtable[hashval % CMDTABLESIZE]; for (cmdp = *pp; cmdp; cmdp = cmdp->next) { if (strcmp(cmdp->cmdname, name) == 0) break; pp = &cmdp->next; } if (add && cmdp == NULL) { cmdp = *pp = ckzalloc(sizeof(struct tblentry) + strlen(name) /* + 1 - already done because * tblentry::cmdname is char[1] */); /*cmdp->next = NULL; - ckzalloc did it */ cmdp->cmdtype = CMDUNKNOWN; strcpy(cmdp->cmdname, name); } lastcmdentry = pp; return cmdp; } /* * Delete the command entry returned on the last lookup. */ static void delete_cmd_entry(void) { struct tblentry *cmdp; INT_OFF; cmdp = *lastcmdentry; *lastcmdentry = cmdp->next; if (cmdp->cmdtype == CMDFUNCTION) freefunc(cmdp->param.func); free(cmdp); INT_ON; } /* * Add a new command entry, replacing any existing command entry for * the same name - except special builtins. */ static void addcmdentry(char *name, struct cmdentry *entry) { struct tblentry *cmdp; cmdp = cmdlookup(name, 1); if (cmdp->cmdtype == CMDFUNCTION) { freefunc(cmdp->param.func); } cmdp->cmdtype = entry->cmdtype; cmdp->param = entry->u; cmdp->rehash = 0; } static int FAST_FUNC hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { struct tblentry **pp; struct tblentry *cmdp; int c; struct cmdentry entry; char *name; if (nextopt("r") != '\0') { clearcmdentry(0); return 0; } if (*argptr == NULL) { for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) { for (cmdp = *pp; cmdp; cmdp = cmdp->next) { if (cmdp->cmdtype == CMDNORMAL) printentry(cmdp); } } return 0; } c = 0; while ((name = *argptr) != NULL) { cmdp = cmdlookup(name, 0); if (cmdp != NULL && (cmdp->cmdtype == CMDNORMAL || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) ) { delete_cmd_entry(); } find_command(name, &entry, DO_ERR, pathval()); if (entry.cmdtype == CMDUNKNOWN) c = 1; argptr++; } return c; } /* * Called when a cd is done. Marks all commands so the next time they * are executed they will be rehashed. */ static void hashcd(void) { struct tblentry **pp; struct tblentry *cmdp; for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) { for (cmdp = *pp; cmdp; cmdp = cmdp->next) { if (cmdp->cmdtype == CMDNORMAL || (cmdp->cmdtype == CMDBUILTIN && !IS_BUILTIN_REGULAR(cmdp->param.cmd) && builtinloc > 0) ) { cmdp->rehash = 1; } } } } /* * Fix command hash table when PATH changed. * Called before PATH is changed. The argument is the new value of PATH; * pathval() still returns the old value at this point. * Called with interrupts off. */ static void FAST_FUNC changepath(const char *new) { const char *old; int firstchange; int idx; int idx_bltin; old = pathval(); firstchange = 9999; /* assume no change */ idx = 0; idx_bltin = -1; for (;;) { if (*old != *new) { firstchange = idx; if ((*old == '\0' && *new == ':') || (*old == ':' && *new == '\0') ) { firstchange++; } old = new; /* ignore subsequent differences */ } if (*new == '\0') break; if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin")) idx_bltin = idx; if (*new == ':') idx++; new++; old++; } if (builtinloc < 0 && idx_bltin >= 0) builtinloc = idx_bltin; /* zap builtins */ if (builtinloc >= 0 && idx_bltin < 0) firstchange = 0; clearcmdentry(firstchange); builtinloc = idx_bltin; } #define TEOF 0 #define TNL 1 #define TREDIR 2 #define TWORD 3 #define TSEMI 4 #define TBACKGND 5 #define TAND 6 #define TOR 7 #define TPIPE 8 #define TLP 9 #define TRP 10 #define TENDCASE 11 #define TENDBQUOTE 12 #define TNOT 13 #define TCASE 14 #define TDO 15 #define TDONE 16 #define TELIF 17 #define TELSE 18 #define TESAC 19 #define TFI 20 #define TFOR 21 #define TIF 22 #define TIN 23 #define TTHEN 24 #define TUNTIL 25 #define TWHILE 26 #define TBEGIN 27 #define TEND 28 typedef smallint token_id_t; /* first char is indicating which tokens mark the end of a list */ static const char *const tokname_array[] = { "\1end of file", "\0newline", "\0redirection", "\0word", "\0;", "\0&", "\0&&", "\0||", "\0|", "\0(", "\1)", "\1;;", "\1`", #define KWDOFFSET 13 /* the following are keywords */ "\0!", "\0case", "\1do", "\1done", "\1elif", "\1else", "\1esac", "\1fi", "\0for", "\0if", "\0in", "\1then", "\0until", "\0while", "\0{", "\1}", }; /* Wrapper around strcmp for qsort/bsearch/... */ static int pstrcmp(const void *a, const void *b) { return strcmp((char*) a, (*(char**) b) + 1); } static const char *const * findkwd(const char *s) { return bsearch(s, tokname_array + KWDOFFSET, ARRAY_SIZE(tokname_array) - KWDOFFSET, sizeof(tokname_array[0]), pstrcmp); } /* * Locate and print what a word is... */ static int describe_command(char *command, int describe_command_verbose) { struct cmdentry entry; struct tblentry *cmdp; #if ENABLE_ASH_ALIAS const struct alias *ap; #endif const char *path = pathval(); if (describe_command_verbose) { out1str(command); } /* First look at the keywords */ if (findkwd(command)) { out1str(describe_command_verbose ? " is a shell keyword" : command); goto out; } #if ENABLE_ASH_ALIAS /* Then look at the aliases */ ap = lookupalias(command, 0); if (ap != NULL) { if (!describe_command_verbose) { out1str("alias "); printalias(ap); return 0; } out1fmt(" is an alias for %s", ap->val); goto out; } #endif /* Then check if it is a tracked alias */ cmdp = cmdlookup(command, 0); if (cmdp != NULL) { entry.cmdtype = cmdp->cmdtype; entry.u = cmdp->param; } else { /* Finally use brute force */ find_command(command, &entry, DO_ABS, path); } switch (entry.cmdtype) { case CMDNORMAL: { int j = entry.u.index; char *p; if (j < 0) { p = command; } else { do { p = path_advance(&path, command); stunalloc(p); } while (--j >= 0); } if (describe_command_verbose) { out1fmt(" is%s %s", (cmdp ? " a tracked alias for" : nullstr), p ); } else { out1str(p); } break; } case CMDFUNCTION: if (describe_command_verbose) { out1str(" is a shell function"); } else { out1str(command); } break; case CMDBUILTIN: if (describe_command_verbose) { out1fmt(" is a %sshell builtin", IS_BUILTIN_SPECIAL(entry.u.cmd) ? "special " : nullstr ); } else { out1str(command); } break; default: if (describe_command_verbose) { out1str(": not found\n"); } return 127; } out: out1str("\n"); return 0; } static int FAST_FUNC typecmd(int argc UNUSED_PARAM, char **argv) { int i = 1; int err = 0; int verbose = 1; /* type -p ... ? (we don't bother checking for 'p') */ if (argv[1] && argv[1][0] == '-') { i++; verbose = 0; } while (argv[i]) { err |= describe_command(argv[i++], verbose); } return err; } #if ENABLE_ASH_CMDCMD static int FAST_FUNC commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int c; enum { VERIFY_BRIEF = 1, VERIFY_VERBOSE = 2, } verify = 0; while ((c = nextopt("pvV")) != '\0') if (c == 'V') verify |= VERIFY_VERBOSE; else if (c == 'v') verify |= VERIFY_BRIEF; #if DEBUG else if (c != 'p') abort(); #endif /* Mimic bash: just "command -v" doesn't complain, it's a nop */ if (verify && (*argptr != NULL)) { return describe_command(*argptr, verify - VERIFY_BRIEF); } return 0; } #endif /* ============ eval.c */ static int funcblocksize; /* size of structures in function */ static int funcstringsize; /* size of strings in node */ static void *funcblock; /* block to allocate function from */ static char *funcstring; /* block to allocate strings from */ /* flags in argument to evaltree */ #define EV_EXIT 01 /* exit after evaluating tree */ #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ #define EV_BACKCMD 04 /* command executing within back quotes */ static const uint8_t nodesize[N_NUMBER] = { [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)), [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)), [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)), [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)), [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)), [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)), [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)), [NIF ] = SHELL_ALIGN(sizeof(struct nif)), [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)), [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)), [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)), [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)), [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)), [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)), [NARG ] = SHELL_ALIGN(sizeof(struct narg)), [NTO ] = SHELL_ALIGN(sizeof(struct nfile)), #if ENABLE_ASH_BASH_COMPAT [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)), #endif [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)), [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)), [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)), [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)), [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)), [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)), [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)), [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)), [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)), }; static void calcsize(union node *n); static void sizenodelist(struct nodelist *lp) { while (lp) { funcblocksize += SHELL_ALIGN(sizeof(struct nodelist)); calcsize(lp->n); lp = lp->next; } } static void calcsize(union node *n) { if (n == NULL) return; funcblocksize += nodesize[n->type]; switch (n->type) { case NCMD: calcsize(n->ncmd.redirect); calcsize(n->ncmd.args); calcsize(n->ncmd.assign); break; case NPIPE: sizenodelist(n->npipe.cmdlist); break; case NREDIR: case NBACKGND: case NSUBSHELL: calcsize(n->nredir.redirect); calcsize(n->nredir.n); break; case NAND: case NOR: case NSEMI: case NWHILE: case NUNTIL: calcsize(n->nbinary.ch2); calcsize(n->nbinary.ch1); break; case NIF: calcsize(n->nif.elsepart); calcsize(n->nif.ifpart); calcsize(n->nif.test); break; case NFOR: funcstringsize += strlen(n->nfor.var) + 1; calcsize(n->nfor.body); calcsize(n->nfor.args); break; case NCASE: calcsize(n->ncase.cases); calcsize(n->ncase.expr); break; case NCLIST: calcsize(n->nclist.body); calcsize(n->nclist.pattern); calcsize(n->nclist.next); break; case NDEFUN: case NARG: sizenodelist(n->narg.backquote); funcstringsize += strlen(n->narg.text) + 1; calcsize(n->narg.next); break; case NTO: #if ENABLE_ASH_BASH_COMPAT case NTO2: #endif case NCLOBBER: case NFROM: case NFROMTO: case NAPPEND: calcsize(n->nfile.fname); calcsize(n->nfile.next); break; case NTOFD: case NFROMFD: calcsize(n->ndup.vname); calcsize(n->ndup.next); break; case NHERE: case NXHERE: calcsize(n->nhere.doc); calcsize(n->nhere.next); break; case NNOT: calcsize(n->nnot.com); break; }; } static char * nodeckstrdup(char *s) { char *rtn = funcstring; strcpy(funcstring, s); funcstring += strlen(s) + 1; return rtn; } static union node *copynode(union node *); static struct nodelist * copynodelist(struct nodelist *lp) { struct nodelist *start; struct nodelist **lpp; lpp = &start; while (lp) { *lpp = funcblock; funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist)); (*lpp)->n = copynode(lp->n); lp = lp->next; lpp = &(*lpp)->next; } *lpp = NULL; return start; } static union node * copynode(union node *n) { union node *new; if (n == NULL) return NULL; new = funcblock; funcblock = (char *) funcblock + nodesize[n->type]; switch (n->type) { case NCMD: new->ncmd.redirect = copynode(n->ncmd.redirect); new->ncmd.args = copynode(n->ncmd.args); new->ncmd.assign = copynode(n->ncmd.assign); break; case NPIPE: new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); new->npipe.pipe_backgnd = n->npipe.pipe_backgnd; break; case NREDIR: case NBACKGND: case NSUBSHELL: new->nredir.redirect = copynode(n->nredir.redirect); new->nredir.n = copynode(n->nredir.n); break; case NAND: case NOR: case NSEMI: case NWHILE: case NUNTIL: new->nbinary.ch2 = copynode(n->nbinary.ch2); new->nbinary.ch1 = copynode(n->nbinary.ch1); break; case NIF: new->nif.elsepart = copynode(n->nif.elsepart); new->nif.ifpart = copynode(n->nif.ifpart); new->nif.test = copynode(n->nif.test); break; case NFOR: new->nfor.var = nodeckstrdup(n->nfor.var); new->nfor.body = copynode(n->nfor.body); new->nfor.args = copynode(n->nfor.args); break; case NCASE: new->ncase.cases = copynode(n->ncase.cases); new->ncase.expr = copynode(n->ncase.expr); break; case NCLIST: new->nclist.body = copynode(n->nclist.body); new->nclist.pattern = copynode(n->nclist.pattern); new->nclist.next = copynode(n->nclist.next); break; case NDEFUN: case NARG: new->narg.backquote = copynodelist(n->narg.backquote); new->narg.text = nodeckstrdup(n->narg.text); new->narg.next = copynode(n->narg.next); break; case NTO: #if ENABLE_ASH_BASH_COMPAT case NTO2: #endif case NCLOBBER: case NFROM: case NFROMTO: case NAPPEND: new->nfile.fname = copynode(n->nfile.fname); new->nfile.fd = n->nfile.fd; new->nfile.next = copynode(n->nfile.next); break; case NTOFD: case NFROMFD: new->ndup.vname = copynode(n->ndup.vname); new->ndup.dupfd = n->ndup.dupfd; new->ndup.fd = n->ndup.fd; new->ndup.next = copynode(n->ndup.next); break; case NHERE: case NXHERE: new->nhere.doc = copynode(n->nhere.doc); new->nhere.fd = n->nhere.fd; new->nhere.next = copynode(n->nhere.next); break; case NNOT: new->nnot.com = copynode(n->nnot.com); break; }; new->type = n->type; return new; } /* * Make a copy of a parse tree. */ static struct funcnode * copyfunc(union node *n) { struct funcnode *f; size_t blocksize; funcblocksize = offsetof(struct funcnode, n); funcstringsize = 0; calcsize(n); blocksize = funcblocksize; f = ckmalloc(blocksize + funcstringsize); funcblock = (char *) f + offsetof(struct funcnode, n); funcstring = (char *) f + blocksize; copynode(n); f->count = 0; return f; } /* * Define a shell function. */ static void defun(char *name, union node *func) { struct cmdentry entry; INT_OFF; entry.cmdtype = CMDFUNCTION; entry.u.func = copyfunc(func); addcmdentry(name, &entry); INT_ON; } /* Reasons for skipping commands (see comment on breakcmd routine) */ #define SKIPBREAK (1 << 0) #define SKIPCONT (1 << 1) #define SKIPFUNC (1 << 2) #define SKIPFILE (1 << 3) #define SKIPEVAL (1 << 4) static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ static int skipcount; /* number of levels to skip */ static int funcnest; /* depth of function calls */ static int loopnest; /* current loop nesting level */ /* Forward decl way out to parsing code - dotrap needs it */ static int evalstring(char *s, int mask); /* Called to execute a trap. * Single callsite - at the end of evaltree(). * If we return non-zero, evaltree raises EXEXIT exception. * * Perhaps we should avoid entering new trap handlers * while we are executing a trap handler. [is it a TODO?] */ static int dotrap(void) { uint8_t *g; int sig; uint8_t savestatus; savestatus = exitstatus; pending_sig = 0; xbarrier(); TRACE(("dotrap entered\n")); for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) { int want_exexit; char *t; if (*g == 0) continue; t = trap[sig]; /* non-trapped SIGINT is handled separately by raise_interrupt, * don't upset it by resetting gotsig[SIGINT-1] */ if (sig == SIGINT && !t) continue; TRACE(("sig %d is active, will run handler '%s'\n", sig, t)); *g = 0; if (!t) continue; want_exexit = evalstring(t, SKIPEVAL); exitstatus = savestatus; if (want_exexit) { TRACE(("dotrap returns %d\n", want_exexit)); return want_exexit; } } TRACE(("dotrap returns 0\n")); return 0; } /* forward declarations - evaluation is fairly recursive business... */ static void evalloop(union node *, int); static void evalfor(union node *, int); static void evalcase(union node *, int); static void evalsubshell(union node *, int); static void expredir(union node *); static void evalpipe(union node *, int); static void evalcommand(union node *, int); static int evalbltin(const struct builtincmd *, int, char **); static void prehash(union node *); /* * Evaluate a parse tree. The value is left in the global variable * exitstatus. */ static void evaltree(union node *n, int flags) { struct jmploc *volatile savehandler = exception_handler; struct jmploc jmploc; int checkexit = 0; void (*evalfn)(union node *, int); int status; int int_level; SAVE_INT(int_level); if (n == NULL) { TRACE(("evaltree(NULL) called\n")); goto out1; } TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags)); exception_handler = &jmploc; { int err = setjmp(jmploc.loc); if (err) { /* if it was a signal, check for trap handlers */ if (exception_type == EXSIG) { TRACE(("exception %d (EXSIG) in evaltree, err=%d\n", exception_type, err)); goto out; } /* continue on the way out */ TRACE(("exception %d in evaltree, propagating err=%d\n", exception_type, err)); exception_handler = savehandler; longjmp(exception_handler->loc, err); } } switch (n->type) { default: #if DEBUG out1fmt("Node type = %d\n", n->type); fflush_all(); break; #endif case NNOT: evaltree(n->nnot.com, EV_TESTED); status = !exitstatus; goto setstatus; case NREDIR: expredir(n->nredir.redirect); status = redirectsafe(n->nredir.redirect, REDIR_PUSH); if (!status) { evaltree(n->nredir.n, flags & EV_TESTED); status = exitstatus; } popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */); goto setstatus; case NCMD: evalfn = evalcommand; checkexit: if (eflag && !(flags & EV_TESTED)) checkexit = ~0; goto calleval; case NFOR: evalfn = evalfor; goto calleval; case NWHILE: case NUNTIL: evalfn = evalloop; goto calleval; case NSUBSHELL: case NBACKGND: evalfn = evalsubshell; goto calleval; case NPIPE: evalfn = evalpipe; goto checkexit; case NCASE: evalfn = evalcase; goto calleval; case NAND: case NOR: case NSEMI: { #if NAND + 1 != NOR #error NAND + 1 != NOR #endif #if NOR + 1 != NSEMI #error NOR + 1 != NSEMI #endif unsigned is_or = n->type - NAND; evaltree( n->nbinary.ch1, (flags | ((is_or >> 1) - 1)) & EV_TESTED ); if (!exitstatus == is_or) break; if (!evalskip) { n = n->nbinary.ch2; evaln: evalfn = evaltree; calleval: evalfn(n, flags); break; } break; } case NIF: evaltree(n->nif.test, EV_TESTED); if (evalskip) break; if (exitstatus == 0) { n = n->nif.ifpart; goto evaln; } if (n->nif.elsepart) { n = n->nif.elsepart; goto evaln; } goto success; case NDEFUN: defun(n->narg.text, n->narg.next); success: status = 0; setstatus: exitstatus = status; break; } out: exception_handler = savehandler; out1: /* Order of checks below is important: * signal handlers trigger before exit caused by "set -e". */ if (pending_sig && dotrap()) goto exexit; if (checkexit & exitstatus) evalskip |= SKIPEVAL; if (flags & EV_EXIT) { exexit: raise_exception(EXEXIT); } RESTORE_INT(int_level); TRACE(("leaving evaltree (no interrupts)\n")); } #if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) static #endif void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__)); static void evalloop(union node *n, int flags) { int status; loopnest++; status = 0; flags &= EV_TESTED; for (;;) { int i; evaltree(n->nbinary.ch1, EV_TESTED); if (evalskip) { skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { evalskip = 0; continue; } if (evalskip == SKIPBREAK && --skipcount <= 0) evalskip = 0; break; } i = exitstatus; if (n->type != NWHILE) i = !i; if (i != 0) break; evaltree(n->nbinary.ch2, flags); status = exitstatus; if (evalskip) goto skipping; } loopnest--; exitstatus = status; } static void evalfor(union node *n, int flags) { struct arglist arglist; union node *argp; struct strlist *sp; struct stackmark smark; setstackmark(&smark); arglist.list = NULL; arglist.lastp = &arglist.list; for (argp = n->nfor.args; argp; argp = argp->narg.next) { expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD); /* XXX */ if (evalskip) goto out; } *arglist.lastp = NULL; exitstatus = 0; loopnest++; flags &= EV_TESTED; for (sp = arglist.list; sp; sp = sp->next) { setvar2(n->nfor.var, sp->text); evaltree(n->nfor.body, flags); if (evalskip) { if (evalskip == SKIPCONT && --skipcount <= 0) { evalskip = 0; continue; } if (evalskip == SKIPBREAK && --skipcount <= 0) evalskip = 0; break; } } loopnest--; out: popstackmark(&smark); } static void evalcase(union node *n, int flags) { union node *cp; union node *patp; struct arglist arglist; struct stackmark smark; setstackmark(&smark); arglist.list = NULL; arglist.lastp = &arglist.list; expandarg(n->ncase.expr, &arglist, EXP_TILDE); exitstatus = 0; for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) { for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) { if (casematch(patp, arglist.list->text)) { if (evalskip == 0) { evaltree(cp->nclist.body, flags); } goto out; } } } out: popstackmark(&smark); } /* * Kick off a subshell to evaluate a tree. */ static void evalsubshell(union node *n, int flags) { struct job *jp; int backgnd = (n->type == NBACKGND); int status; expredir(n->nredir.redirect); if (!backgnd && (flags & EV_EXIT) && !may_have_traps) goto nofork; INT_OFF; jp = makejob(/*n,*/ 1); if (forkshell(jp, n, backgnd) == 0) { /* child */ INT_ON; flags |= EV_EXIT; if (backgnd) flags &= ~EV_TESTED; nofork: redirect(n->nredir.redirect, 0); evaltreenr(n->nredir.n, flags); /* never returns */ } status = 0; if (!backgnd) status = waitforjob(jp); exitstatus = status; INT_ON; } /* * Compute the names of the files in a redirection list. */ static void fixredir(union node *, const char *, int); static void expredir(union node *n) { union node *redir; for (redir = n; redir; redir = redir->nfile.next) { struct arglist fn; fn.list = NULL; fn.lastp = &fn.list; switch (redir->type) { case NFROMTO: case NFROM: case NTO: #if ENABLE_ASH_BASH_COMPAT case NTO2: #endif case NCLOBBER: case NAPPEND: expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); TRACE(("expredir expanded to '%s'\n", fn.list->text)); #if ENABLE_ASH_BASH_COMPAT store_expfname: #endif #if 0 // By the design of stack allocator, the loop of this kind: // while true; do while true; do break; done </dev/null; done // will look like a memory leak: ash plans to free expfname's // of "/dev/null" as soon as it finishes running the loop // (in this case, never). // This "fix" is wrong: if (redir->nfile.expfname) stunalloc(redir->nfile.expfname); // It results in corrupted state of stacked allocations. #endif redir->nfile.expfname = fn.list->text; break; case NFROMFD: case NTOFD: /* >& */ if (redir->ndup.vname) { expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE); if (fn.list == NULL) ash_msg_and_raise_error("redir error"); #if ENABLE_ASH_BASH_COMPAT //FIXME: we used expandarg with different args! if (!isdigit_str9(fn.list->text)) { /* >&file, not >&fd */ if (redir->nfile.fd != 1) /* 123>&file - BAD */ ash_msg_and_raise_error("redir error"); redir->type = NTO2; goto store_expfname; } #endif fixredir(redir, fn.list->text, 1); } break; } } } /* * Evaluate a pipeline. All the processes in the pipeline are children * of the process creating the pipeline. (This differs from some versions * of the shell, which make the last process in a pipeline the parent * of all the rest.) */ static void evalpipe(union node *n, int flags) { struct job *jp; struct nodelist *lp; int pipelen; int prevfd; int pip[2]; TRACE(("evalpipe(0x%lx) called\n", (long)n)); pipelen = 0; for (lp = n->npipe.cmdlist; lp; lp = lp->next) pipelen++; flags |= EV_EXIT; INT_OFF; jp = makejob(/*n,*/ pipelen); prevfd = -1; for (lp = n->npipe.cmdlist; lp; lp = lp->next) { prehash(lp->n); pip[1] = -1; if (lp->next) { if (pipe(pip) < 0) { close(prevfd); ash_msg_and_raise_error("pipe call failed"); } } if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { INT_ON; if (pip[1] >= 0) { close(pip[0]); } if (prevfd > 0) { dup2(prevfd, 0); close(prevfd); } if (pip[1] > 1) { dup2(pip[1], 1); close(pip[1]); } evaltreenr(lp->n, flags); /* never returns */ } if (prevfd >= 0) close(prevfd); prevfd = pip[0]; /* Don't want to trigger debugging */ if (pip[1] != -1) close(pip[1]); } if (n->npipe.pipe_backgnd == 0) { exitstatus = waitforjob(jp); TRACE(("evalpipe: job done exit status %d\n", exitstatus)); } INT_ON; } /* * Controls whether the shell is interactive or not. */ static void setinteractive(int on) { static smallint is_interactive; if (++on == is_interactive) return; is_interactive = on; setsignal(SIGINT); setsignal(SIGQUIT); setsignal(SIGTERM); #if !ENABLE_FEATURE_SH_EXTRA_QUIET if (is_interactive > 1) { /* Looks like they want an interactive shell */ static smallint did_banner; if (!did_banner) { /* note: ash and hush share this string */ out1fmt("\n\n%s %s\n" "Enter 'help' for a list of built-in commands." "\n\n", bb_banner, "built-in shell (ash)" ); did_banner = 1; } } #endif } static void optschanged(void) { #if DEBUG opentrace(); #endif setinteractive(iflag); setjobctl(mflag); #if ENABLE_FEATURE_EDITING_VI if (viflag) line_input_state->flags |= VI_MODE; else line_input_state->flags &= ~VI_MODE; #else viflag = 0; /* forcibly keep the option off */ #endif } static struct localvar *localvars; /* * Called after a function returns. * Interrupts must be off. */ static void poplocalvars(void) { struct localvar *lvp; struct var *vp; while ((lvp = localvars) != NULL) { localvars = lvp->next; vp = lvp->vp; TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-")); if (vp == NULL) { /* $- saved */ memcpy(optlist, lvp->text, sizeof(optlist)); free((char*)lvp->text); optschanged(); } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { unsetvar(vp->var_text); } else { if (vp->var_func) vp->var_func(var_end(lvp->text)); if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) free((char*)vp->var_text); vp->flags = lvp->flags; vp->var_text = lvp->text; } free(lvp); } } static int evalfun(struct funcnode *func, int argc, char **argv, int flags) { volatile struct shparam saveparam; struct localvar *volatile savelocalvars; struct jmploc *volatile savehandler; struct jmploc jmploc; int e; saveparam = shellparam; savelocalvars = localvars; e = setjmp(jmploc.loc); if (e) { goto funcdone; } INT_OFF; savehandler = exception_handler; exception_handler = &jmploc; localvars = NULL; shellparam.malloced = 0; func->count++; funcnest++; INT_ON; shellparam.nparam = argc - 1; shellparam.p = argv + 1; #if ENABLE_ASH_GETOPTS shellparam.optind = 1; shellparam.optoff = -1; #endif evaltree(&func->n, flags & EV_TESTED); funcdone: INT_OFF; funcnest--; freefunc(func); poplocalvars(); localvars = savelocalvars; freeparam(&shellparam); shellparam = saveparam; exception_handler = savehandler; INT_ON; evalskip &= ~SKIPFUNC; return e; } #if ENABLE_ASH_CMDCMD static char ** parse_command_args(char **argv, const char **path) { char *cp, c; for (;;) { cp = *++argv; if (!cp) return 0; if (*cp++ != '-') break; c = *cp++; if (!c) break; if (c == '-' && !*cp) { argv++; break; } do { switch (c) { case 'p': *path = bb_default_path; break; default: /* run 'typecmd' for other options */ return 0; } c = *cp++; } while (c); } return argv; } #endif /* * Make a variable a local variable. When a variable is made local, it's * value and flags are saved in a localvar structure. The saved values * will be restored when the shell function returns. We handle the name * "-" as a special case. */ static void mklocal(char *name) { struct localvar *lvp; struct var **vpp; struct var *vp; INT_OFF; lvp = ckzalloc(sizeof(struct localvar)); if (LONE_DASH(name)) { char *p; p = ckmalloc(sizeof(optlist)); lvp->text = memcpy(p, optlist, sizeof(optlist)); vp = NULL; } else { char *eq; vpp = hashvar(name); vp = *findvar(vpp, name); eq = strchr(name, '='); if (vp == NULL) { if (eq) setvareq(name, VSTRFIXED); else setvar(name, NULL, VSTRFIXED); vp = *vpp; /* the new variable */ lvp->flags = VUNSET; } else { lvp->text = vp->var_text; lvp->flags = vp->flags; vp->flags |= VSTRFIXED|VTEXTFIXED; if (eq) setvareq(name, 0); } } lvp->vp = vp; lvp->next = localvars; localvars = lvp; INT_ON; } /* * The "local" command. */ static int FAST_FUNC localcmd(int argc UNUSED_PARAM, char **argv) { char *name; argv = argptr; while ((name = *argv++) != NULL) { mklocal(name); } return 0; } static int FAST_FUNC falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { return 1; } static int FAST_FUNC truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { return 0; } static int FAST_FUNC execcmd(int argc UNUSED_PARAM, char **argv) { if (argv[1]) { iflag = 0; /* exit on error */ mflag = 0; optschanged(); shellexec(argv + 1, pathval(), 0); } return 0; } /* * The return command. */ static int FAST_FUNC returncmd(int argc UNUSED_PARAM, char **argv) { /* * If called outside a function, do what ksh does; * skip the rest of the file. */ evalskip = funcnest ? SKIPFUNC : SKIPFILE; return argv[1] ? number(argv[1]) : exitstatus; } /* Forward declarations for builtintab[] */ static int breakcmd(int, char **) FAST_FUNC; static int dotcmd(int, char **) FAST_FUNC; static int evalcmd(int, char **) FAST_FUNC; static int exitcmd(int, char **) FAST_FUNC; static int exportcmd(int, char **) FAST_FUNC; #if ENABLE_ASH_GETOPTS static int getoptscmd(int, char **) FAST_FUNC; #endif #if !ENABLE_FEATURE_SH_EXTRA_QUIET static int helpcmd(int, char **) FAST_FUNC; #endif #if MAX_HISTORY static int historycmd(int, char **) FAST_FUNC; #endif #if ENABLE_SH_MATH_SUPPORT static int letcmd(int, char **) FAST_FUNC; #endif static int readcmd(int, char **) FAST_FUNC; static int setcmd(int, char **) FAST_FUNC; static int shiftcmd(int, char **) FAST_FUNC; static int timescmd(int, char **) FAST_FUNC; static int trapcmd(int, char **) FAST_FUNC; static int umaskcmd(int, char **) FAST_FUNC; static int unsetcmd(int, char **) FAST_FUNC; static int ulimitcmd(int, char **) FAST_FUNC; #define BUILTIN_NOSPEC "0" #define BUILTIN_SPECIAL "1" #define BUILTIN_REGULAR "2" #define BUILTIN_SPEC_REG "3" #define BUILTIN_ASSIGN "4" #define BUILTIN_SPEC_ASSG "5" #define BUILTIN_REG_ASSG "6" #define BUILTIN_SPEC_REG_ASSG "7" /* Stubs for calling non-FAST_FUNC's */ #if ENABLE_ASH_BUILTIN_ECHO static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); } #endif #if ENABLE_ASH_BUILTIN_PRINTF static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); } #endif #if ENABLE_ASH_BUILTIN_TEST static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); } #endif /* Keep these in proper order since it is searched via bsearch() */ static const struct builtincmd builtintab[] = { { BUILTIN_SPEC_REG "." , dotcmd }, { BUILTIN_SPEC_REG ":" , truecmd }, #if ENABLE_ASH_BUILTIN_TEST { BUILTIN_REGULAR "[" , testcmd }, #if ENABLE_ASH_BASH_COMPAT { BUILTIN_REGULAR "[[" , testcmd }, #endif #endif #if ENABLE_ASH_ALIAS { BUILTIN_REG_ASSG "alias" , aliascmd }, #endif #if JOBS { BUILTIN_REGULAR "bg" , fg_bgcmd }, #endif { BUILTIN_SPEC_REG "break" , breakcmd }, { BUILTIN_REGULAR "cd" , cdcmd }, { BUILTIN_NOSPEC "chdir" , cdcmd }, #if ENABLE_ASH_CMDCMD { BUILTIN_REGULAR "command" , commandcmd }, #endif { BUILTIN_SPEC_REG "continue", breakcmd }, #if ENABLE_ASH_BUILTIN_ECHO { BUILTIN_REGULAR "echo" , echocmd }, #endif { BUILTIN_SPEC_REG "eval" , evalcmd }, { BUILTIN_SPEC_REG "exec" , execcmd }, { BUILTIN_SPEC_REG "exit" , exitcmd }, { BUILTIN_SPEC_REG_ASSG "export" , exportcmd }, { BUILTIN_REGULAR "false" , falsecmd }, #if JOBS { BUILTIN_REGULAR "fg" , fg_bgcmd }, #endif #if ENABLE_ASH_GETOPTS { BUILTIN_REGULAR "getopts" , getoptscmd }, #endif { BUILTIN_NOSPEC "hash" , hashcmd }, #if !ENABLE_FEATURE_SH_EXTRA_QUIET { BUILTIN_NOSPEC "help" , helpcmd }, #endif #if MAX_HISTORY { BUILTIN_NOSPEC "history" , historycmd }, #endif #if JOBS { BUILTIN_REGULAR "jobs" , jobscmd }, { BUILTIN_REGULAR "kill" , killcmd }, #endif #if ENABLE_SH_MATH_SUPPORT { BUILTIN_NOSPEC "let" , letcmd }, #endif { BUILTIN_ASSIGN "local" , localcmd }, #if ENABLE_ASH_BUILTIN_PRINTF { BUILTIN_REGULAR "printf" , printfcmd }, #endif { BUILTIN_NOSPEC "pwd" , pwdcmd }, { BUILTIN_REGULAR "read" , readcmd }, { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd }, { BUILTIN_SPEC_REG "return" , returncmd }, { BUILTIN_SPEC_REG "set" , setcmd }, { BUILTIN_SPEC_REG "shift" , shiftcmd }, #if ENABLE_ASH_BASH_COMPAT { BUILTIN_SPEC_REG "source" , dotcmd }, #endif #if ENABLE_ASH_BUILTIN_TEST { BUILTIN_REGULAR "test" , testcmd }, #endif { BUILTIN_SPEC_REG "times" , timescmd }, { BUILTIN_SPEC_REG "trap" , trapcmd }, { BUILTIN_REGULAR "true" , truecmd }, { BUILTIN_NOSPEC "type" , typecmd }, { BUILTIN_NOSPEC "ulimit" , ulimitcmd }, { BUILTIN_REGULAR "umask" , umaskcmd }, #if ENABLE_ASH_ALIAS { BUILTIN_REGULAR "unalias" , unaliascmd }, #endif { BUILTIN_SPEC_REG "unset" , unsetcmd }, { BUILTIN_REGULAR "wait" , waitcmd }, }; /* Should match the above table! */ #define COMMANDCMD (builtintab + \ 2 + \ 1 * ENABLE_ASH_BUILTIN_TEST + \ 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \ 1 * ENABLE_ASH_ALIAS + \ 1 * ENABLE_ASH_JOB_CONTROL + \ 3) #define EXECCMD (builtintab + \ 2 + \ 1 * ENABLE_ASH_BUILTIN_TEST + \ 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \ 1 * ENABLE_ASH_ALIAS + \ 1 * ENABLE_ASH_JOB_CONTROL + \ 3 + \ 1 * ENABLE_ASH_CMDCMD + \ 1 + \ ENABLE_ASH_BUILTIN_ECHO + \ 1) /* * Search the table of builtin commands. */ static struct builtincmd * find_builtin(const char *name) { struct builtincmd *bp; bp = bsearch( name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]), pstrcmp ); return bp; } /* * Execute a simple command. */ static int isassignment(const char *p) { const char *q = endofname(p); if (p == q) return 0; return *q == '='; } static int FAST_FUNC bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { /* Preserve exitstatus of a previous possible redirection * as POSIX mandates */ return back_exitstatus; } static void evalcommand(union node *cmd, int flags) { static const struct builtincmd null_bltin = { "\0\0", bltincmd /* why three NULs? */ }; struct stackmark smark; union node *argp; struct arglist arglist; struct arglist varlist; char **argv; int argc; const struct strlist *sp; struct cmdentry cmdentry; struct job *jp; char *lastarg; const char *path; int spclbltin; int status; char **nargv; struct builtincmd *bcmd; smallint cmd_is_exec; smallint pseudovarflag = 0; /* First expand the arguments. */ TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); setstackmark(&smark); back_exitstatus = 0; cmdentry.cmdtype = CMDBUILTIN; cmdentry.u.cmd = &null_bltin; varlist.lastp = &varlist.list; *varlist.lastp = NULL; arglist.lastp = &arglist.list; *arglist.lastp = NULL; argc = 0; if (cmd->ncmd.args) { bcmd = find_builtin(cmd->ncmd.args->narg.text); pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd); } for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) { struct strlist **spp; spp = arglist.lastp; if (pseudovarflag && isassignment(argp->narg.text)) expandarg(argp, &arglist, EXP_VARTILDE); else expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); for (sp = *spp; sp; sp = sp->next) argc++; } argv = nargv = stalloc(sizeof(char *) * (argc + 1)); for (sp = arglist.list; sp; sp = sp->next) { TRACE(("evalcommand arg: %s\n", sp->text)); *nargv++ = sp->text; } *nargv = NULL; lastarg = NULL; if (iflag && funcnest == 0 && argc > 0) lastarg = nargv[-1]; preverrout_fd = 2; expredir(cmd->ncmd.redirect); status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2); path = vpath.var_text; for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { struct strlist **spp; char *p; spp = varlist.lastp; expandarg(argp, &varlist, EXP_VARTILDE); /* * Modify the command lookup path, if a PATH= assignment * is present */ p = (*spp)->text; if (varcmp(p, path) == 0) path = p; } /* Print the command if xflag is set. */ if (xflag) { int n; const char *p = " %s" + 1; fdprintf(preverrout_fd, p, expandstr(ps4val())); sp = varlist.list; for (n = 0; n < 2; n++) { while (sp) { fdprintf(preverrout_fd, p, sp->text); sp = sp->next; p = " %s"; } sp = arglist.list; } safe_write(preverrout_fd, "\n", 1); } cmd_is_exec = 0; spclbltin = -1; /* Now locate the command. */ if (argc) { int cmd_flag = DO_ERR; #if ENABLE_ASH_CMDCMD const char *oldpath = path + 5; #endif path += 5; for (;;) { find_command(argv[0], &cmdentry, cmd_flag, path); if (cmdentry.cmdtype == CMDUNKNOWN) { flush_stdout_stderr(); status = 127; goto bail; } /* implement bltin and command here */ if (cmdentry.cmdtype != CMDBUILTIN) break; if (spclbltin < 0) spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd); if (cmdentry.u.cmd == EXECCMD) cmd_is_exec = 1; #if ENABLE_ASH_CMDCMD if (cmdentry.u.cmd == COMMANDCMD) { path = oldpath; nargv = parse_command_args(argv, &path); if (!nargv) break; argc -= nargv - argv; argv = nargv; cmd_flag |= DO_NOFUNC; } else #endif break; } } if (status) { /* We have a redirection error. */ if (spclbltin > 0) raise_exception(EXERROR); bail: exitstatus = status; goto out; } /* Execute the command. */ switch (cmdentry.cmdtype) { default: { #if ENABLE_FEATURE_SH_NOFORK /* (1) BUG: if variables are set, we need to fork, or save/restore them * around run_nofork_applet() call. * (2) Should this check also be done in forkshell()? * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...) */ /* find_command() encodes applet_no as (-2 - applet_no) */ int applet_no = (- cmdentry.u.index - 2); if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) { listsetvar(varlist.list, VEXPORT|VSTACK); /* run <applet>_main() */ exitstatus = run_nofork_applet(applet_no, argv); break; } #endif /* Can we avoid forking off? For example, very last command * in a script or a subshell does not need forking, * we can just exec it. */ if (!(flags & EV_EXIT) || may_have_traps) { /* No, forking off a child is necessary */ INT_OFF; jp = makejob(/*cmd,*/ 1); if (forkshell(jp, cmd, FORK_FG) != 0) { /* parent */ exitstatus = waitforjob(jp); INT_ON; TRACE(("forked child exited with %d\n", exitstatus)); break; } /* child */ FORCE_INT_ON; /* fall through to exec'ing external program */ } listsetvar(varlist.list, VEXPORT|VSTACK); shellexec(argv, path, cmdentry.u.index); /* NOTREACHED */ } /* default */ case CMDBUILTIN: cmdenviron = varlist.list; if (cmdenviron) { struct strlist *list = cmdenviron; int i = VNOSET; if (spclbltin > 0 || argc == 0) { i = 0; if (cmd_is_exec && argc > 1) i = VEXPORT; } listsetvar(list, i); } /* Tight loop with builtins only: * "while kill -0 $child; do true; done" * will never exit even if $child died, unless we do this * to reap the zombie and make kill detect that it's gone: */ dowait(DOWAIT_NONBLOCK, NULL); if (evalbltin(cmdentry.u.cmd, argc, argv)) { int exit_status; int i = exception_type; if (i == EXEXIT) goto raise; exit_status = 2; if (i == EXINT) exit_status = 128 + SIGINT; if (i == EXSIG) exit_status = 128 + pending_sig; exitstatus = exit_status; if (i == EXINT || spclbltin > 0) { raise: longjmp(exception_handler->loc, 1); } FORCE_INT_ON; } break; case CMDFUNCTION: listsetvar(varlist.list, 0); /* See above for the rationale */ dowait(DOWAIT_NONBLOCK, NULL); if (evalfun(cmdentry.u.func, argc, argv, flags)) goto raise; break; } /* switch */ out: popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec); if (lastarg) { /* dsl: I think this is intended to be used to support * '_' in 'vi' command mode during line editing... * However I implemented that within libedit itself. */ setvar2("_", lastarg); } popstackmark(&smark); } static int evalbltin(const struct builtincmd *cmd, int argc, char **argv) { char *volatile savecmdname; struct jmploc *volatile savehandler; struct jmploc jmploc; int i; savecmdname = commandname; i = setjmp(jmploc.loc); if (i) goto cmddone; savehandler = exception_handler; exception_handler = &jmploc; commandname = argv[0]; argptr = argv + 1; optptr = NULL; /* initialize nextopt */ exitstatus = (*cmd->builtin)(argc, argv); flush_stdout_stderr(); cmddone: exitstatus |= ferror(stdout); clearerr(stdout); commandname = savecmdname; exception_handler = savehandler; return i; } static int goodname(const char *p) { return endofname(p)[0] == '\0'; } /* * Search for a command. This is called before we fork so that the * location of the command will be available in the parent as well as * the child. The check for "goodname" is an overly conservative * check that the name will not be subject to expansion. */ static void prehash(union node *n) { struct cmdentry entry; if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text)) find_command(n->ncmd.args->narg.text, &entry, 0, pathval()); } /* ============ Builtin commands * * Builtin commands whose functions are closely tied to evaluation * are implemented here. */ /* * Handle break and continue commands. Break, continue, and return are * all handled by setting the evalskip flag. The evaluation routines * above all check this flag, and if it is set they start skipping * commands rather than executing them. The variable skipcount is * the number of loops to break/continue, or the number of function * levels to return. (The latter is always 1.) It should probably * be an error to break out of more loops than exist, but it isn't * in the standard shell so we don't make it one here. */ static int FAST_FUNC breakcmd(int argc UNUSED_PARAM, char **argv) { int n = argv[1] ? number(argv[1]) : 1; if (n <= 0) ash_msg_and_raise_error(msg_illnum, argv[1]); if (n > loopnest) n = loopnest; if (n > 0) { evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK; skipcount = n; } return 0; } /* ============ input.c * * This implements the input routines used by the parser. */ enum { INPUT_PUSH_FILE = 1, INPUT_NOFILE_OK = 2, }; static smallint checkkwd; /* values of checkkwd variable */ #define CHKALIAS 0x1 #define CHKKWD 0x2 #define CHKNL 0x4 /* * Push a string back onto the input at this current parsefile level. * We handle aliases this way. */ #if !ENABLE_ASH_ALIAS #define pushstring(s, ap) pushstring(s) #endif static void pushstring(char *s, struct alias *ap) { struct strpush *sp; int len; len = strlen(s); INT_OFF; if (g_parsefile->strpush) { sp = ckzalloc(sizeof(*sp)); sp->prev = g_parsefile->strpush; } else { sp = &(g_parsefile->basestrpush); } g_parsefile->strpush = sp; sp->prev_string = g_parsefile->next_to_pgetc; sp->prev_left_in_line = g_parsefile->left_in_line; #if ENABLE_ASH_ALIAS sp->ap = ap; if (ap) { ap->flag |= ALIASINUSE; sp->string = s; } #endif g_parsefile->next_to_pgetc = s; g_parsefile->left_in_line = len; INT_ON; } static void popstring(void) { struct strpush *sp = g_parsefile->strpush; INT_OFF; #if ENABLE_ASH_ALIAS if (sp->ap) { if (g_parsefile->next_to_pgetc[-1] == ' ' || g_parsefile->next_to_pgetc[-1] == '\t' ) { checkkwd |= CHKALIAS; } if (sp->string != sp->ap->val) { free(sp->string); } sp->ap->flag &= ~ALIASINUSE; if (sp->ap->flag & ALIASDEAD) { unalias(sp->ap->name); } } #endif g_parsefile->next_to_pgetc = sp->prev_string; g_parsefile->left_in_line = sp->prev_left_in_line; g_parsefile->strpush = sp->prev; if (sp != &(g_parsefile->basestrpush)) free(sp); INT_ON; } //FIXME: BASH_COMPAT with "...&" does TWO pungetc(): //it peeks whether it is &>, and then pushes back both chars. //This function needs to save last *next_to_pgetc to buf[0] //to make two pungetc() reliable. Currently, // pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work... static int preadfd(void) { int nr; char *buf = g_parsefile->buf; g_parsefile->next_to_pgetc = buf; #if ENABLE_FEATURE_EDITING retry: if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1); else { int timeout = -1; # if ENABLE_ASH_IDLE_TIMEOUT if (iflag) { const char *tmout_var = lookupvar("TMOUT"); if (tmout_var) { timeout = atoi(tmout_var) * 1000; if (timeout <= 0) timeout = -1; } } # endif # if ENABLE_FEATURE_TAB_COMPLETION line_input_state->path_lookup = pathval(); # endif /* Unicode support should be activated even if LANG is set * _during_ shell execution, not only if it was set when * shell was started. Therefore, re-check LANG every time: */ { const char *s = lookupvar("LC_ALL"); if (!s) s = lookupvar("LC_CTYPE"); if (!s) s = lookupvar("LANG"); reinit_unicode(s); } nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout); if (nr == 0) { /* Ctrl+C pressed */ if (trap[SIGINT]) { buf[0] = '\n'; buf[1] = '\0'; raise(SIGINT); return 1; } goto retry; } if (nr < 0) { if (errno == 0) { /* Ctrl+D pressed */ nr = 0; } # if ENABLE_ASH_IDLE_TIMEOUT else if (errno == EAGAIN && timeout > 0) { printf("\007timed out waiting for input: auto-logout\n"); exitshell(); } # endif } } #else nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1); #endif #if 0 /* disabled: nonblock_immune_read() handles this problem */ if (nr < 0) { if (parsefile->fd == 0 && errno == EWOULDBLOCK) { int flags = fcntl(0, F_GETFL); if (flags >= 0 && (flags & O_NONBLOCK)) { flags &= ~O_NONBLOCK; if (fcntl(0, F_SETFL, flags) >= 0) { out2str("sh: turning off NDELAY mode\n"); goto retry; } } } } #endif return nr; } /* * Refill the input buffer and return the next input character: * * 1) If a string was pushed back on the input, pop it; * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM) * or we are reading from a string so we can't refill the buffer, * return EOF. * 3) If there is more stuff in this buffer, use it else call read to fill it. * 4) Process input up to the next newline, deleting nul characters. */ //#define pgetc_debug(...) bb_error_msg(__VA_ARGS__) #define pgetc_debug(...) ((void)0) static int preadbuffer(void) { char *q; int more; while (g_parsefile->strpush) { #if ENABLE_ASH_ALIAS if (g_parsefile->left_in_line == -1 && g_parsefile->strpush->ap && g_parsefile->next_to_pgetc[-1] != ' ' && g_parsefile->next_to_pgetc[-1] != '\t' ) { pgetc_debug("preadbuffer PEOA"); return PEOA; } #endif popstring(); /* try "pgetc" now: */ pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'", g_parsefile->left_in_line, g_parsefile->next_to_pgetc, g_parsefile->next_to_pgetc); if (--g_parsefile->left_in_line >= 0) return (unsigned char)(*g_parsefile->next_to_pgetc++); } /* on both branches above g_parsefile->left_in_line < 0. * "pgetc" needs refilling. */ /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read", * pungetc() may increment it a few times. * Assuming it won't increment it to less than -90. */ if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) { pgetc_debug("preadbuffer PEOF1"); /* even in failure keep left_in_line and next_to_pgetc * in lock step, for correct multi-layer pungetc. * left_in_line was decremented before preadbuffer(), * must inc next_to_pgetc: */ g_parsefile->next_to_pgetc++; return PEOF; } more = g_parsefile->left_in_buffer; if (more <= 0) { flush_stdout_stderr(); again: more = preadfd(); if (more <= 0) { /* don't try reading again */ g_parsefile->left_in_line = -99; pgetc_debug("preadbuffer PEOF2"); g_parsefile->next_to_pgetc++; return PEOF; } } /* Find out where's the end of line. * Set g_parsefile->left_in_line * and g_parsefile->left_in_buffer acordingly. * NUL chars are deleted. */ q = g_parsefile->next_to_pgetc; for (;;) { char c; more--; c = *q; if (c == '\0') { memmove(q, q + 1, more); } else { q++; if (c == '\n') { g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1; break; } } if (more <= 0) { g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1; if (g_parsefile->left_in_line < 0) goto again; break; } } g_parsefile->left_in_buffer = more; if (vflag) { char save = *q; *q = '\0'; out2str(g_parsefile->next_to_pgetc); *q = save; } pgetc_debug("preadbuffer at %d:%p'%s'", g_parsefile->left_in_line, g_parsefile->next_to_pgetc, g_parsefile->next_to_pgetc); return (unsigned char)*g_parsefile->next_to_pgetc++; } #define pgetc_as_macro() \ (--g_parsefile->left_in_line >= 0 \ ? (unsigned char)*g_parsefile->next_to_pgetc++ \ : preadbuffer() \ ) static int pgetc(void) { pgetc_debug("pgetc_fast at %d:%p'%s'", g_parsefile->left_in_line, g_parsefile->next_to_pgetc, g_parsefile->next_to_pgetc); return pgetc_as_macro(); } #if ENABLE_ASH_OPTIMIZE_FOR_SIZE # define pgetc_fast() pgetc() #else # define pgetc_fast() pgetc_as_macro() #endif #if ENABLE_ASH_ALIAS static int pgetc_without_PEOA(void) { int c; do { pgetc_debug("pgetc_fast at %d:%p'%s'", g_parsefile->left_in_line, g_parsefile->next_to_pgetc, g_parsefile->next_to_pgetc); c = pgetc_fast(); } while (c == PEOA); return c; } #else # define pgetc_without_PEOA() pgetc() #endif /* * Read a line from the script. */ static char * pfgets(char *line, int len) { char *p = line; int nleft = len; int c; while (--nleft > 0) { c = pgetc_without_PEOA(); if (c == PEOF) { if (p == line) return NULL; break; } *p++ = c; if (c == '\n') break; } *p = '\0'; return line; } /* * Undo the last call to pgetc. Only one character may be pushed back. * PEOF may be pushed back. */ static void pungetc(void) { g_parsefile->left_in_line++; g_parsefile->next_to_pgetc--; pgetc_debug("pushed back to %d:%p'%s'", g_parsefile->left_in_line, g_parsefile->next_to_pgetc, g_parsefile->next_to_pgetc); } /* * To handle the "." command, a stack of input files is used. Pushfile * adds a new entry to the stack and popfile restores the previous level. */ static void pushfile(void) { struct parsefile *pf; pf = ckzalloc(sizeof(*pf)); pf->prev = g_parsefile; pf->pf_fd = -1; /*pf->strpush = NULL; - ckzalloc did it */ /*pf->basestrpush.prev = NULL;*/ g_parsefile = pf; } static void popfile(void) { struct parsefile *pf = g_parsefile; INT_OFF; if (pf->pf_fd >= 0) close(pf->pf_fd); free(pf->buf); while (pf->strpush) popstring(); g_parsefile = pf->prev; free(pf); INT_ON; } /* * Return to top level. */ static void popallfiles(void) { while (g_parsefile != &basepf) popfile(); } /* * Close the file(s) that the shell is reading commands from. Called * after a fork is done. */ static void closescript(void) { popallfiles(); if (g_parsefile->pf_fd > 0) { close(g_parsefile->pf_fd); g_parsefile->pf_fd = 0; } } /* * Like setinputfile, but takes an open file descriptor. Call this with * interrupts off. */ static void setinputfd(int fd, int push) { close_on_exec_on(fd); if (push) { pushfile(); g_parsefile->buf = NULL; } g_parsefile->pf_fd = fd; if (g_parsefile->buf == NULL) g_parsefile->buf = ckmalloc(IBUFSIZ); g_parsefile->left_in_buffer = 0; g_parsefile->left_in_line = 0; g_parsefile->linno = 1; } /* * Set the input to take input from a file. If push is set, push the * old input onto the stack first. */ static int setinputfile(const char *fname, int flags) { int fd; int fd2; INT_OFF; fd = open(fname, O_RDONLY); if (fd < 0) { if (flags & INPUT_NOFILE_OK) goto out; ash_msg_and_raise_error("can't open '%s'", fname); } if (fd < 10) { fd2 = copyfd(fd, 10); close(fd); if (fd2 < 0) ash_msg_and_raise_error("out of file descriptors"); fd = fd2; } setinputfd(fd, flags & INPUT_PUSH_FILE); out: INT_ON; return fd; } /* * Like setinputfile, but takes input from a string. */ static void setinputstring(char *string) { INT_OFF; pushfile(); g_parsefile->next_to_pgetc = string; g_parsefile->left_in_line = strlen(string); g_parsefile->buf = NULL; g_parsefile->linno = 1; INT_ON; } /* ============ mail.c * * Routines to check for mail. */ #if ENABLE_ASH_MAIL #define MAXMBOXES 10 /* times of mailboxes */ static time_t mailtime[MAXMBOXES]; /* Set if MAIL or MAILPATH is changed. */ static smallint mail_var_path_changed; /* * Print appropriate message(s) if mail has arrived. * If mail_var_path_changed is set, * then the value of MAIL has mail_var_path_changed, * so we just update the values. */ static void chkmail(void) { const char *mpath; char *p; char *q; time_t *mtp; struct stackmark smark; struct stat statb; setstackmark(&smark); mpath = mpathset() ? mpathval() : mailval(); for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) { p = path_advance(&mpath, nullstr); if (p == NULL) break; if (*p == '\0') continue; for (q = p; *q; q++) continue; #if DEBUG if (q[-1] != '/') abort(); #endif q[-1] = '\0'; /* delete trailing '/' */ if (stat(p, &statb) < 0) { *mtp = 0; continue; } if (!mail_var_path_changed && statb.st_mtime != *mtp) { fprintf( stderr, "%s\n", pathopt ? pathopt : "you have mail" ); } *mtp = statb.st_mtime; } mail_var_path_changed = 0; popstackmark(&smark); } static void FAST_FUNC changemail(const char *val UNUSED_PARAM) { mail_var_path_changed = 1; } #endif /* ASH_MAIL */ /* ============ ??? */ /* * Set the shell parameters. */ static void setparam(char **argv) { char **newparam; char **ap; int nparam; for (nparam = 0; argv[nparam]; nparam++) continue; ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap)); while (*argv) { *ap++ = ckstrdup(*argv++); } *ap = NULL; freeparam(&shellparam); shellparam.malloced = 1; shellparam.nparam = nparam; shellparam.p = newparam; #if ENABLE_ASH_GETOPTS shellparam.optind = 1; shellparam.optoff = -1; #endif } /* * Process shell options. The global variable argptr contains a pointer * to the argument list; we advance it past the options. * * SUSv3 section 2.8.1 "Consequences of Shell Errors" says: * For a non-interactive shell, an error condition encountered * by a special built-in ... shall cause the shell to write a diagnostic message * to standard error and exit as shown in the following table: * Error Special Built-In * ... * Utility syntax error (option or operand error) Shall exit * ... * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142) * we see that bash does not do that (set "finishes" with error code 1 instead, * and shell continues), and people rely on this behavior! * Testcase: * set -o barfoo 2>/dev/null * echo $? * * Oh well. Let's mimic that. */ static int plus_minus_o(char *name, int val) { int i; if (name) { for (i = 0; i < NOPTS; i++) { if (strcmp(name, optnames(i)) == 0) { optlist[i] = val; return 0; } } ash_msg("illegal option %co %s", val ? '-' : '+', name); return 1; } for (i = 0; i < NOPTS; i++) { if (val) { out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off"); } else { out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i)); } } return 0; } static void setoption(int flag, int val) { int i; for (i = 0; i < NOPTS; i++) { if (optletters(i) == flag) { optlist[i] = val; return; } } ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag); /* NOTREACHED */ } static int options(int cmdline) { char *p; int val; int c; if (cmdline) minusc = NULL; while ((p = *argptr) != NULL) { c = *p++; if (c != '-' && c != '+') break; argptr++; val = 0; /* val = 0 if c == '+' */ if (c == '-') { val = 1; if (p[0] == '\0' || LONE_DASH(p)) { if (!cmdline) { /* "-" means turn off -x and -v */ if (p[0] == '\0') xflag = vflag = 0; /* "--" means reset params */ else if (*argptr == NULL) setparam(argptr); } break; /* "-" or "--" terminates options */ } } /* first char was + or - */ while ((c = *p++) != '\0') { /* bash 3.2 indeed handles -c CMD and +c CMD the same */ if (c == 'c' && cmdline) { minusc = p; /* command is after shell args */ } else if (c == 'o') { if (plus_minus_o(*argptr, val)) { /* it already printed err message */ return 1; /* error */ } if (*argptr) argptr++; } else if (cmdline && (c == 'l')) { /* -l or +l == --login */ isloginsh = 1; /* bash does not accept +-login, we also won't */ } else if (cmdline && val && (c == '-')) { /* long options */ if (strcmp(p, "login") == 0) isloginsh = 1; break; } else { setoption(c, val); } } } return 0; } /* * The shift builtin command. */ static int FAST_FUNC shiftcmd(int argc UNUSED_PARAM, char **argv) { int n; char **ap1, **ap2; n = 1; if (argv[1]) n = number(argv[1]); if (n > shellparam.nparam) n = 0; /* bash compat, was = shellparam.nparam; */ INT_OFF; shellparam.nparam -= n; for (ap1 = shellparam.p; --n >= 0; ap1++) { if (shellparam.malloced) free(*ap1); } ap2 = shellparam.p; while ((*ap2++ = *ap1++) != NULL) continue; #if ENABLE_ASH_GETOPTS shellparam.optind = 1; shellparam.optoff = -1; #endif INT_ON; return 0; } /* * POSIX requires that 'set' (but not export or readonly) output the * variables in lexicographic order - by the locale's collating order (sigh). * Maybe we could keep them in an ordered balanced binary tree * instead of hashed lists. * For now just roll 'em through qsort for printing... */ static int showvars(const char *sep_prefix, int on, int off) { const char *sep; char **ep, **epend; ep = listvars(on, off, &epend); qsort(ep, epend - ep, sizeof(char *), vpcmp); sep = *sep_prefix ? " " : sep_prefix; for (; ep < epend; ep++) { const char *p; const char *q; p = strchrnul(*ep, '='); q = nullstr; if (*p) q = single_quote(++p); out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q); } return 0; } /* * The set command builtin. */ static int FAST_FUNC setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int retval; if (!argv[1]) return showvars(nullstr, 0, VUNSET); INT_OFF; retval = options(/*cmdline:*/ 0); if (retval == 0) { /* if no parse error... */ optschanged(); if (*argptr != NULL) { setparam(argptr); } } INT_ON; return retval; } #if ENABLE_ASH_RANDOM_SUPPORT static void FAST_FUNC change_random(const char *value) { uint32_t t; if (value == NULL) { /* "get", generate */ t = next_random(&random_gen); /* set without recursion */ setvar(vrandom.var_text, utoa(t), VNOFUNC); vrandom.flags &= ~VNOFUNC; } else { /* set/reset */ t = strtoul(value, NULL, 10); INIT_RANDOM_T(&random_gen, (t ? t : 1), t); } } #endif #if ENABLE_ASH_GETOPTS static int getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff) { char *p, *q; char c = '?'; int done = 0; int err = 0; char s[12]; char **optnext; if (*param_optind < 1) return 1; optnext = optfirst + *param_optind - 1; if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff) p = NULL; else p = optnext[-1] + *optoff; if (p == NULL || *p == '\0') { /* Current word is done, advance */ p = *optnext; if (p == NULL || *p != '-' || *++p == '\0') { atend: p = NULL; done = 1; goto out; } optnext++; if (LONE_DASH(p)) /* check for "--" */ goto atend; } c = *p++; for (q = optstr; *q != c;) { if (*q == '\0') { if (optstr[0] == ':') { s[0] = c; s[1] = '\0'; err |= setvarsafe("OPTARG", s, 0); } else { fprintf(stderr, "Illegal option -%c\n", c); unsetvar("OPTARG"); } c = '?'; goto out; } if (*++q == ':') q++; } if (*++q == ':') { if (*p == '\0' && (p = *optnext) == NULL) { if (optstr[0] == ':') { s[0] = c; s[1] = '\0'; err |= setvarsafe("OPTARG", s, 0); c = ':'; } else { fprintf(stderr, "No arg for -%c option\n", c); unsetvar("OPTARG"); c = '?'; } goto out; } if (p == *optnext) optnext++; err |= setvarsafe("OPTARG", p, 0); p = NULL; } else err |= setvarsafe("OPTARG", nullstr, 0); out: *optoff = p ? p - *(optnext - 1) : -1; *param_optind = optnext - optfirst + 1; fmtstr(s, sizeof(s), "%d", *param_optind); err |= setvarsafe("OPTIND", s, VNOFUNC); s[0] = c; s[1] = '\0'; err |= setvarsafe(optvar, s, 0); if (err) { *param_optind = 1; *optoff = -1; flush_stdout_stderr(); raise_exception(EXERROR); } return done; } /* * The getopts builtin. Shellparam.optnext points to the next argument * to be processed. Shellparam.optptr points to the next character to * be processed in the current argument. If shellparam.optnext is NULL, * then it's the first time getopts has been called. */ static int FAST_FUNC getoptscmd(int argc, char **argv) { char **optbase; if (argc < 3) ash_msg_and_raise_error("usage: getopts optstring var [arg]"); if (argc == 3) { optbase = shellparam.p; if (shellparam.optind > shellparam.nparam + 1) { shellparam.optind = 1; shellparam.optoff = -1; } } else { optbase = &argv[3]; if (shellparam.optind > argc - 2) { shellparam.optind = 1; shellparam.optoff = -1; } } return getopts(argv[1], argv[2], optbase, &shellparam.optind, &shellparam.optoff); } #endif /* ASH_GETOPTS */ /* ============ Shell parser */ struct heredoc { struct heredoc *next; /* next here document in list */ union node *here; /* redirection node */ char *eofmark; /* string indicating end of input */ smallint striptabs; /* if set, strip leading tabs */ }; static smallint tokpushback; /* last token pushed back */ static smallint parsebackquote; /* nonzero if we are inside backquotes */ static smallint quoteflag; /* set if (part of) last token was quoted */ static token_id_t lasttoken; /* last token read (integer id Txxx) */ static struct heredoc *heredoclist; /* list of here documents to read */ static char *wordtext; /* text of last word returned by readtoken */ static struct nodelist *backquotelist; static union node *redirnode; static struct heredoc *heredoc; static const char * tokname(char *buf, int tok) { if (tok < TSEMI) return tokname_array[tok] + 1; sprintf(buf, "\"%s\"", tokname_array[tok] + 1); return buf; } /* raise_error_unexpected_syntax: * Called when an unexpected token is read during the parse. The argument * is the token that is expected, or -1 if more than one type of token can * occur at this point. */ static void raise_error_unexpected_syntax(int) NORETURN; static void raise_error_unexpected_syntax(int token) { char msg[64]; char buf[16]; int l; l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken)); if (token >= 0) sprintf(msg + l, " (expecting %s)", tokname(buf, token)); raise_error_syntax(msg); /* NOTREACHED */ } #define EOFMARKLEN 79 /* parsing is heavily cross-recursive, need these forward decls */ static union node *andor(void); static union node *pipeline(void); static union node *parse_command(void); static void parseheredoc(void); static char peektoken(void); static int readtoken(void); static union node * list(int nlflag) { union node *n1, *n2, *n3; int tok; checkkwd = CHKNL | CHKKWD | CHKALIAS; if (nlflag == 2 && peektoken()) return NULL; n1 = NULL; for (;;) { n2 = andor(); tok = readtoken(); if (tok == TBACKGND) { if (n2->type == NPIPE) { n2->npipe.pipe_backgnd = 1; } else { if (n2->type != NREDIR) { n3 = stzalloc(sizeof(struct nredir)); n3->nredir.n = n2; /*n3->nredir.redirect = NULL; - stzalloc did it */ n2 = n3; } n2->type = NBACKGND; } } if (n1 == NULL) { n1 = n2; } else { n3 = stzalloc(sizeof(struct nbinary)); n3->type = NSEMI; n3->nbinary.ch1 = n1; n3->nbinary.ch2 = n2; n1 = n3; } switch (tok) { case TBACKGND: case TSEMI: tok = readtoken(); /* fall through */ case TNL: if (tok == TNL) { parseheredoc(); if (nlflag == 1) return n1; } else { tokpushback = 1; } checkkwd = CHKNL | CHKKWD | CHKALIAS; if (peektoken()) return n1; break; case TEOF: if (heredoclist) parseheredoc(); else pungetc(); /* push back EOF on input */ return n1; default: if (nlflag == 1) raise_error_unexpected_syntax(-1); tokpushback = 1; return n1; } } } static union node * andor(void) { union node *n1, *n2, *n3; int t; n1 = pipeline(); for (;;) { t = readtoken(); if (t == TAND) { t = NAND; } else if (t == TOR) { t = NOR; } else { tokpushback = 1; return n1; } checkkwd = CHKNL | CHKKWD | CHKALIAS; n2 = pipeline(); n3 = stzalloc(sizeof(struct nbinary)); n3->type = t; n3->nbinary.ch1 = n1; n3->nbinary.ch2 = n2; n1 = n3; } } static union node * pipeline(void) { union node *n1, *n2, *pipenode; struct nodelist *lp, *prev; int negate; negate = 0; TRACE(("pipeline: entered\n")); if (readtoken() == TNOT) { negate = !negate; checkkwd = CHKKWD | CHKALIAS; } else tokpushback = 1; n1 = parse_command(); if (readtoken() == TPIPE) { pipenode = stzalloc(sizeof(struct npipe)); pipenode->type = NPIPE; /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */ lp = stzalloc(sizeof(struct nodelist)); pipenode->npipe.cmdlist = lp; lp->n = n1; do { prev = lp; lp = stzalloc(sizeof(struct nodelist)); checkkwd = CHKNL | CHKKWD | CHKALIAS; lp->n = parse_command(); prev->next = lp; } while (readtoken() == TPIPE); lp->next = NULL; n1 = pipenode; } tokpushback = 1; if (negate) { n2 = stzalloc(sizeof(struct nnot)); n2->type = NNOT; n2->nnot.com = n1; return n2; } return n1; } static union node * makename(void) { union node *n; n = stzalloc(sizeof(struct narg)); n->type = NARG; /*n->narg.next = NULL; - stzalloc did it */ n->narg.text = wordtext; n->narg.backquote = backquotelist; return n; } static void fixredir(union node *n, const char *text, int err) { int fd; TRACE(("Fix redir %s %d\n", text, err)); if (!err) n->ndup.vname = NULL; fd = bb_strtou(text, NULL, 10); if (!errno && fd >= 0) n->ndup.dupfd = fd; else if (LONE_DASH(text)) n->ndup.dupfd = -1; else { if (err) raise_error_syntax("bad fd number"); n->ndup.vname = makename(); } } /* * Returns true if the text contains nothing to expand (no dollar signs * or backquotes). */ static int noexpand(const char *text) { unsigned char c; while ((c = *text++) != '\0') { if (c == CTLQUOTEMARK) continue; if (c == CTLESC) text++; else if (SIT(c, BASESYNTAX) == CCTL) return 0; } return 1; } static void parsefname(void) { union node *n = redirnode; if (readtoken() != TWORD) raise_error_unexpected_syntax(-1); if (n->type == NHERE) { struct heredoc *here = heredoc; struct heredoc *p; int i; if (quoteflag == 0) n->type = NXHERE; TRACE(("Here document %d\n", n->type)); if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) raise_error_syntax("illegal eof marker for << redirection"); rmescapes(wordtext, 0); here->eofmark = wordtext; here->next = NULL; if (heredoclist == NULL) heredoclist = here; else { for (p = heredoclist; p->next; p = p->next) continue; p->next = here; } } else if (n->type == NTOFD || n->type == NFROMFD) { fixredir(n, wordtext, 0); } else { n->nfile.fname = makename(); } } static union node * simplecmd(void) { union node *args, **app; union node *n = NULL; union node *vars, **vpp; union node **rpp, *redir; int savecheckkwd; #if ENABLE_ASH_BASH_COMPAT smallint double_brackets_flag = 0; #endif args = NULL; app = &args; vars = NULL; vpp = &vars; redir = NULL; rpp = &redir; savecheckkwd = CHKALIAS; for (;;) { int t; checkkwd = savecheckkwd; t = readtoken(); switch (t) { #if ENABLE_ASH_BASH_COMPAT case TAND: /* "&&" */ case TOR: /* "||" */ if (!double_brackets_flag) { tokpushback = 1; goto out; } wordtext = (char *) (t == TAND ? "-a" : "-o"); #endif case TWORD: n = stzalloc(sizeof(struct narg)); n->type = NARG; /*n->narg.next = NULL; - stzalloc did it */ n->narg.text = wordtext; #if ENABLE_ASH_BASH_COMPAT if (strcmp("[[", wordtext) == 0) double_brackets_flag = 1; else if (strcmp("]]", wordtext) == 0) double_brackets_flag = 0; #endif n->narg.backquote = backquotelist; if (savecheckkwd && isassignment(wordtext)) { *vpp = n; vpp = &n->narg.next; } else { *app = n; app = &n->narg.next; savecheckkwd = 0; } break; case TREDIR: *rpp = n = redirnode; rpp = &n->nfile.next; parsefname(); /* read name of redirection file */ break; case TLP: if (args && app == &args->narg.next && !vars && !redir ) { struct builtincmd *bcmd; const char *name; /* We have a function */ if (readtoken() != TRP) raise_error_unexpected_syntax(TRP); name = n->narg.text; if (!goodname(name) || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd)) ) { raise_error_syntax("bad function name"); } n->type = NDEFUN; checkkwd = CHKNL | CHKKWD | CHKALIAS; n->narg.next = parse_command(); return n; } /* fall through */ default: tokpushback = 1; goto out; } } out: *app = NULL; *vpp = NULL; *rpp = NULL; n = stzalloc(sizeof(struct ncmd)); n->type = NCMD; n->ncmd.args = args; n->ncmd.assign = vars; n->ncmd.redirect = redir; return n; } static union node * parse_command(void) { union node *n1, *n2; union node *ap, **app; union node *cp, **cpp; union node *redir, **rpp; union node **rpp2; int t; redir = NULL; rpp2 = &redir; switch (readtoken()) { default: raise_error_unexpected_syntax(-1); /* NOTREACHED */ case TIF: n1 = stzalloc(sizeof(struct nif)); n1->type = NIF; n1->nif.test = list(0); if (readtoken() != TTHEN) raise_error_unexpected_syntax(TTHEN); n1->nif.ifpart = list(0); n2 = n1; while (readtoken() == TELIF) { n2->nif.elsepart = stzalloc(sizeof(struct nif)); n2 = n2->nif.elsepart; n2->type = NIF; n2->nif.test = list(0); if (readtoken() != TTHEN) raise_error_unexpected_syntax(TTHEN); n2->nif.ifpart = list(0); } if (lasttoken == TELSE) n2->nif.elsepart = list(0); else { n2->nif.elsepart = NULL; tokpushback = 1; } t = TFI; break; case TWHILE: case TUNTIL: { int got; n1 = stzalloc(sizeof(struct nbinary)); n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL; n1->nbinary.ch1 = list(0); got = readtoken(); if (got != TDO) { TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1, got == TWORD ? wordtext : "")); raise_error_unexpected_syntax(TDO); } n1->nbinary.ch2 = list(0); t = TDONE; break; } case TFOR: if (readtoken() != TWORD || quoteflag || !goodname(wordtext)) raise_error_syntax("bad for loop variable"); n1 = stzalloc(sizeof(struct nfor)); n1->type = NFOR; n1->nfor.var = wordtext; checkkwd = CHKKWD | CHKALIAS; if (readtoken() == TIN) { app = ≈ while (readtoken() == TWORD) { n2 = stzalloc(sizeof(struct narg)); n2->type = NARG; /*n2->narg.next = NULL; - stzalloc did it */ n2->narg.text = wordtext; n2->narg.backquote = backquotelist; *app = n2; app = &n2->narg.next; } *app = NULL; n1->nfor.args = ap; if (lasttoken != TNL && lasttoken != TSEMI) raise_error_unexpected_syntax(-1); } else { n2 = stzalloc(sizeof(struct narg)); n2->type = NARG; /*n2->narg.next = NULL; - stzalloc did it */ n2->narg.text = (char *)dolatstr; /*n2->narg.backquote = NULL;*/ n1->nfor.args = n2; /* * Newline or semicolon here is optional (but note * that the original Bourne shell only allowed NL). */ if (lasttoken != TNL && lasttoken != TSEMI) tokpushback = 1; } checkkwd = CHKNL | CHKKWD | CHKALIAS; if (readtoken() != TDO) raise_error_unexpected_syntax(TDO); n1->nfor.body = list(0); t = TDONE; break; case TCASE: n1 = stzalloc(sizeof(struct ncase)); n1->type = NCASE; if (readtoken() != TWORD) raise_error_unexpected_syntax(TWORD); n1->ncase.expr = n2 = stzalloc(sizeof(struct narg)); n2->type = NARG; /*n2->narg.next = NULL; - stzalloc did it */ n2->narg.text = wordtext; n2->narg.backquote = backquotelist; do { checkkwd = CHKKWD | CHKALIAS; } while (readtoken() == TNL); if (lasttoken != TIN) raise_error_unexpected_syntax(TIN); cpp = &n1->ncase.cases; next_case: checkkwd = CHKNL | CHKKWD; t = readtoken(); while (t != TESAC) { if (lasttoken == TLP) readtoken(); *cpp = cp = stzalloc(sizeof(struct nclist)); cp->type = NCLIST; app = &cp->nclist.pattern; for (;;) { *app = ap = stzalloc(sizeof(struct narg)); ap->type = NARG; /*ap->narg.next = NULL; - stzalloc did it */ ap->narg.text = wordtext; ap->narg.backquote = backquotelist; if (readtoken() != TPIPE) break; app = &ap->narg.next; readtoken(); } //ap->narg.next = NULL; if (lasttoken != TRP) raise_error_unexpected_syntax(TRP); cp->nclist.body = list(2); cpp = &cp->nclist.next; checkkwd = CHKNL | CHKKWD; t = readtoken(); if (t != TESAC) { if (t != TENDCASE) raise_error_unexpected_syntax(TENDCASE); goto next_case; } } *cpp = NULL; goto redir; case TLP: n1 = stzalloc(sizeof(struct nredir)); n1->type = NSUBSHELL; n1->nredir.n = list(0); /*n1->nredir.redirect = NULL; - stzalloc did it */ t = TRP; break; case TBEGIN: n1 = list(0); t = TEND; break; case TWORD: case TREDIR: tokpushback = 1; return simplecmd(); } if (readtoken() != t) raise_error_unexpected_syntax(t); redir: /* Now check for redirection which may follow command */ checkkwd = CHKKWD | CHKALIAS; rpp = rpp2; while (readtoken() == TREDIR) { *rpp = n2 = redirnode; rpp = &n2->nfile.next; parsefname(); } tokpushback = 1; *rpp = NULL; if (redir) { if (n1->type != NSUBSHELL) { n2 = stzalloc(sizeof(struct nredir)); n2->type = NREDIR; n2->nredir.n = n1; n1 = n2; } n1->nredir.redirect = redir; } return n1; } #if ENABLE_ASH_BASH_COMPAT static int decode_dollar_squote(void) { static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567"; int c, cnt; char *p; char buf[4]; c = pgetc(); p = strchr(C_escapes, c); if (p) { buf[0] = c; p = buf; cnt = 3; if ((unsigned char)(c - '0') <= 7) { /* \ooo */ do { c = pgetc(); *++p = c; } while ((unsigned char)(c - '0') <= 7 && --cnt); pungetc(); } else if (c == 'x') { /* \xHH */ do { c = pgetc(); *++p = c; } while (isxdigit(c) && --cnt); pungetc(); if (cnt == 3) { /* \x but next char is "bad" */ c = 'x'; goto unrecognized; } } else { /* simple seq like \\ or \t */ p++; } *p = '\0'; p = buf; c = bb_process_escape_sequence((void*)&p); } else { /* unrecognized "\z": print both chars unless ' or " */ if (c != '\'' && c != '"') { unrecognized: c |= 0x100; /* "please encode \, then me" */ } } return c; } #endif /* * If eofmark is NULL, read a word or a redirection symbol. If eofmark * is not NULL, read a here document. In the latter case, eofmark is the * word which marks the end of the document and striptabs is true if * leading tabs should be stripped from the document. The argument c * is the first character of the input token or document. * * Because C does not have internal subroutines, I have simulated them * using goto's to implement the subroutine linkage. The following macros * will run code that appears at the end of readtoken1. */ #define CHECKEND() {goto checkend; checkend_return:;} #define PARSEREDIR() {goto parseredir; parseredir_return:;} #define PARSESUB() {goto parsesub; parsesub_return:;} #define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} #define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} #define PARSEARITH() {goto parsearith; parsearith_return:;} static int readtoken1(int c, int syntax, char *eofmark, int striptabs) { /* NB: syntax parameter fits into smallint */ /* c parameter is an unsigned char or PEOF or PEOA */ char *out; int len; char line[EOFMARKLEN + 1]; struct nodelist *bqlist; smallint quotef; smallint dblquote; smallint oldstyle; smallint prevsyntax; /* syntax before arithmetic */ #if ENABLE_ASH_EXPAND_PRMT smallint pssyntax; /* we are expanding a prompt string */ #endif int varnest; /* levels of variables expansion */ int arinest; /* levels of arithmetic expansion */ int parenlevel; /* levels of parens in arithmetic */ int dqvarnest; /* levels of variables expansion within double quotes */ IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;) #if __GNUC__ /* Avoid longjmp clobbering */ (void) &out; (void) "ef; (void) &dblquote; (void) &varnest; (void) &arinest; (void) &parenlevel; (void) &dqvarnest; (void) &oldstyle; (void) &prevsyntax; (void) &syntax; #endif startlinno = g_parsefile->linno; bqlist = NULL; quotef = 0; prevsyntax = 0; #if ENABLE_ASH_EXPAND_PRMT pssyntax = (syntax == PSSYNTAX); if (pssyntax) syntax = DQSYNTAX; #endif dblquote = (syntax == DQSYNTAX); varnest = 0; arinest = 0; parenlevel = 0; dqvarnest = 0; STARTSTACKSTR(out); loop: /* For each line, until end of word */ CHECKEND(); /* set c to PEOF if at end of here document */ for (;;) { /* until end of line or end of word */ CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ switch (SIT(c, syntax)) { case CNL: /* '\n' */ if (syntax == BASESYNTAX) goto endword; /* exit outer loop */ USTPUTC(c, out); g_parsefile->linno++; setprompt_if(doprompt, 2); c = pgetc(); goto loop; /* continue outer loop */ case CWORD: USTPUTC(c, out); break; case CCTL: if (eofmark == NULL || dblquote) USTPUTC(CTLESC, out); #if ENABLE_ASH_BASH_COMPAT if (c == '\\' && bash_dollar_squote) { c = decode_dollar_squote(); if (c & 0x100) { USTPUTC('\\', out); c = (unsigned char)c; } } #endif USTPUTC(c, out); break; case CBACK: /* backslash */ c = pgetc_without_PEOA(); if (c == PEOF) { USTPUTC(CTLESC, out); USTPUTC('\\', out); pungetc(); } else if (c == '\n') { setprompt_if(doprompt, 2); } else { #if ENABLE_ASH_EXPAND_PRMT if (c == '$' && pssyntax) { USTPUTC(CTLESC, out); USTPUTC('\\', out); } #endif /* Backslash is retained if we are in "str" and next char isn't special */ if (dblquote && c != '\\' && c != '`' && c != '$' && (c != '"' || eofmark != NULL) ) { USTPUTC(CTLESC, out); USTPUTC('\\', out); } if (SIT(c, SQSYNTAX) == CCTL) USTPUTC(CTLESC, out); USTPUTC(c, out); quotef = 1; } break; case CSQUOTE: syntax = SQSYNTAX; quotemark: if (eofmark == NULL) { USTPUTC(CTLQUOTEMARK, out); } break; case CDQUOTE: syntax = DQSYNTAX; dblquote = 1; goto quotemark; case CENDQUOTE: IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;) if (eofmark != NULL && arinest == 0 && varnest == 0 ) { USTPUTC(c, out); } else { if (dqvarnest == 0) { syntax = BASESYNTAX; dblquote = 0; } quotef = 1; goto quotemark; } break; case CVAR: /* '$' */ PARSESUB(); /* parse substitution */ break; case CENDVAR: /* '}' */ if (varnest > 0) { varnest--; if (dqvarnest > 0) { dqvarnest--; } c = CTLENDVAR; } USTPUTC(c, out); break; #if ENABLE_SH_MATH_SUPPORT case CLP: /* '(' in arithmetic */ parenlevel++; USTPUTC(c, out); break; case CRP: /* ')' in arithmetic */ if (parenlevel > 0) { parenlevel--; } else { if (pgetc() == ')') { if (--arinest == 0) { syntax = prevsyntax; dblquote = (syntax == DQSYNTAX); c = CTLENDARI; } } else { /* * unbalanced parens * (don't 2nd guess - no error) */ pungetc(); } } USTPUTC(c, out); break; #endif case CBQUOTE: /* '`' */ PARSEBACKQOLD(); break; case CENDFILE: goto endword; /* exit outer loop */ case CIGN: break; default: if (varnest == 0) { #if ENABLE_ASH_BASH_COMPAT if (c == '&') { if (pgetc() == '>') c = 0x100 + '>'; /* flag &> */ pungetc(); } #endif goto endword; /* exit outer loop */ } IF_ASH_ALIAS(if (c != PEOA)) USTPUTC(c, out); } c = pgetc_fast(); } /* for (;;) */ endword: #if ENABLE_SH_MATH_SUPPORT if (syntax == ARISYNTAX) raise_error_syntax("missing '))'"); #endif if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL) raise_error_syntax("unterminated quoted string"); if (varnest != 0) { startlinno = g_parsefile->linno; /* { */ raise_error_syntax("missing '}'"); } USTPUTC('\0', out); len = out - (char *)stackblock(); out = stackblock(); if (eofmark == NULL) { if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>')) && quotef == 0 ) { if (isdigit_str9(out)) { PARSEREDIR(); /* passed as params: out, c */ lasttoken = TREDIR; return lasttoken; } /* else: non-number X seen, interpret it * as "NNNX>file" = "NNNX >file" */ } pungetc(); } quoteflag = quotef; backquotelist = bqlist; grabstackblock(len); wordtext = out; lasttoken = TWORD; return lasttoken; /* end of readtoken routine */ /* * Check to see whether we are at the end of the here document. When this * is called, c is set to the first character of the next input line. If * we are at the end of the here document, this routine sets the c to PEOF. */ checkend: { if (eofmark) { #if ENABLE_ASH_ALIAS if (c == PEOA) c = pgetc_without_PEOA(); #endif if (striptabs) { while (c == '\t') { c = pgetc_without_PEOA(); } } if (c == *eofmark) { if (pfgets(line, sizeof(line)) != NULL) { char *p, *q; p = line; for (q = eofmark + 1; *q && *p == *q; p++, q++) continue; if (*p == '\n' && *q == '\0') { c = PEOF; g_parsefile->linno++; needprompt = doprompt; } else { pushstring(line, NULL); } } } } goto checkend_return; } /* * Parse a redirection operator. The variable "out" points to a string * specifying the fd to be redirected. The variable "c" contains the * first character of the redirection operator. */ parseredir: { /* out is already checked to be a valid number or "" */ int fd = (*out == '\0' ? -1 : atoi(out)); union node *np; np = stzalloc(sizeof(struct nfile)); if (c == '>') { np->nfile.fd = 1; c = pgetc(); if (c == '>') np->type = NAPPEND; else if (c == '|') np->type = NCLOBBER; else if (c == '&') np->type = NTOFD; /* it also can be NTO2 (>&file), but we can't figure it out yet */ else { np->type = NTO; pungetc(); } } #if ENABLE_ASH_BASH_COMPAT else if (c == 0x100 + '>') { /* this flags &> redirection */ np->nfile.fd = 1; pgetc(); /* this is '>', no need to check */ np->type = NTO2; } #endif else { /* c == '<' */ /*np->nfile.fd = 0; - stzalloc did it */ c = pgetc(); switch (c) { case '<': if (sizeof(struct nfile) != sizeof(struct nhere)) { np = stzalloc(sizeof(struct nhere)); /*np->nfile.fd = 0; - stzalloc did it */ } np->type = NHERE; heredoc = stzalloc(sizeof(struct heredoc)); heredoc->here = np; c = pgetc(); if (c == '-') { heredoc->striptabs = 1; } else { /*heredoc->striptabs = 0; - stzalloc did it */ pungetc(); } break; case '&': np->type = NFROMFD; break; case '>': np->type = NFROMTO; break; default: np->type = NFROM; pungetc(); break; } } if (fd >= 0) np->nfile.fd = fd; redirnode = np; goto parseredir_return; } /* * Parse a substitution. At this point, we have read the dollar sign * and nothing else. */ /* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise * (assuming ascii char codes, as the original implementation did) */ #define is_special(c) \ (((unsigned)(c) - 33 < 32) \ && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1)) parsesub: { unsigned char subtype; int typeloc; int flags; c = pgetc(); if (c > 255 /* PEOA or PEOF */ || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) ) { #if ENABLE_ASH_BASH_COMPAT if (c == '\'') bash_dollar_squote = 1; else #endif USTPUTC('$', out); pungetc(); } else if (c == '(') { /* $(command) or $((arith)) */ if (pgetc() == '(') { #if ENABLE_SH_MATH_SUPPORT PARSEARITH(); #else raise_error_syntax("you disabled math support for $((arith)) syntax"); #endif } else { pungetc(); PARSEBACKQNEW(); } } else { /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */ USTPUTC(CTLVAR, out); typeloc = out - (char *)stackblock(); USTPUTC(VSNORMAL, out); subtype = VSNORMAL; if (c == '{') { c = pgetc(); if (c == '#') { c = pgetc(); if (c == '}') c = '#'; /* ${#} - same as $# */ else subtype = VSLENGTH; /* ${#VAR} */ } else { subtype = 0; } } if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) { /* $[{[#]]NAME[}] */ do { STPUTC(c, out); c = pgetc(); } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c)); } else if (isdigit(c)) { /* $[{[#]]NUM[}] */ do { STPUTC(c, out); c = pgetc(); } while (isdigit(c)); } else if (is_special(c)) { /* $[{[#]]<specialchar>[}] */ USTPUTC(c, out); c = pgetc(); } else { badsub: raise_error_syntax("bad substitution"); } if (c != '}' && subtype == VSLENGTH) { /* ${#VAR didn't end with } */ goto badsub; } STPUTC('=', out); flags = 0; if (subtype == 0) { /* ${VAR...} but not $VAR or ${#VAR} */ /* c == first char after VAR */ switch (c) { case ':': c = pgetc(); #if ENABLE_ASH_BASH_COMPAT if (c == ':' || c == '$' || isdigit(c)) { //TODO: support more general format ${v:EXPR:EXPR}, // where EXPR follows $(()) rules subtype = VSSUBSTR; pungetc(); break; /* "goto do_pungetc" is bigger (!) */ } #endif flags = VSNUL; /*FALLTHROUGH*/ default: { static const char types[] ALIGN1 = "}-+?="; const char *p = strchr(types, c); if (p == NULL) goto badsub; subtype = p - types + VSNORMAL; break; } case '%': case '#': { int cc = c; subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT); c = pgetc(); if (c != cc) goto do_pungetc; subtype++; break; } #if ENABLE_ASH_BASH_COMPAT case '/': /* ${v/[/]pattern/repl} */ //TODO: encode pattern and repl separately. // Currently ${v/$var_with_slash/repl} is horribly broken subtype = VSREPLACE; c = pgetc(); if (c != '/') goto do_pungetc; subtype++; /* VSREPLACEALL */ break; #endif } } else { do_pungetc: pungetc(); } if (dblquote || arinest) flags |= VSQUOTE; ((unsigned char *)stackblock())[typeloc] = subtype | flags; if (subtype != VSNORMAL) { varnest++; if (dblquote || arinest) { dqvarnest++; } } } goto parsesub_return; } /* * Called to parse command substitutions. Newstyle is set if the command * is enclosed inside $(...); nlpp is a pointer to the head of the linked * list of commands (passed by reference), and savelen is the number of * characters on the top of the stack which must be preserved. */ parsebackq: { struct nodelist **nlpp; smallint savepbq; union node *n; char *volatile str; struct jmploc jmploc; struct jmploc *volatile savehandler; size_t savelen; smallint saveprompt = 0; #ifdef __GNUC__ (void) &saveprompt; #endif savepbq = parsebackquote; if (setjmp(jmploc.loc)) { free(str); parsebackquote = 0; exception_handler = savehandler; longjmp(exception_handler->loc, 1); } INT_OFF; str = NULL; savelen = out - (char *)stackblock(); if (savelen > 0) { str = ckmalloc(savelen); memcpy(str, stackblock(), savelen); } savehandler = exception_handler; exception_handler = &jmploc; INT_ON; if (oldstyle) { /* We must read until the closing backquote, giving special * treatment to some slashes, and then push the string and * reread it as input, interpreting it normally. */ char *pout; size_t psavelen; char *pstr; STARTSTACKSTR(pout); for (;;) { int pc; setprompt_if(needprompt, 2); pc = pgetc(); switch (pc) { case '`': goto done; case '\\': pc = pgetc(); if (pc == '\n') { g_parsefile->linno++; setprompt_if(doprompt, 2); /* * If eating a newline, avoid putting * the newline into the new character * stream (via the STPUTC after the * switch). */ continue; } if (pc != '\\' && pc != '`' && pc != '$' && (!dblquote || pc != '"') ) { STPUTC('\\', pout); } if (pc <= 255 /* not PEOA or PEOF */) { break; } /* fall through */ case PEOF: IF_ASH_ALIAS(case PEOA:) startlinno = g_parsefile->linno; raise_error_syntax("EOF in backquote substitution"); case '\n': g_parsefile->linno++; needprompt = doprompt; break; default: break; } STPUTC(pc, pout); } done: STPUTC('\0', pout); psavelen = pout - (char *)stackblock(); if (psavelen > 0) { pstr = grabstackstr(pout); setinputstring(pstr); } } nlpp = &bqlist; while (*nlpp) nlpp = &(*nlpp)->next; *nlpp = stzalloc(sizeof(**nlpp)); /* (*nlpp)->next = NULL; - stzalloc did it */ parsebackquote = oldstyle; if (oldstyle) { saveprompt = doprompt; doprompt = 0; } n = list(2); if (oldstyle) doprompt = saveprompt; else if (readtoken() != TRP) raise_error_unexpected_syntax(TRP); (*nlpp)->n = n; if (oldstyle) { /* * Start reading from old file again, ignoring any pushed back * tokens left from the backquote parsing */ popfile(); tokpushback = 0; } while (stackblocksize() <= savelen) growstackblock(); STARTSTACKSTR(out); if (str) { memcpy(out, str, savelen); STADJUST(savelen, out); INT_OFF; free(str); str = NULL; INT_ON; } parsebackquote = savepbq; exception_handler = savehandler; if (arinest || dblquote) USTPUTC(CTLBACKQ | CTLQUOTE, out); else USTPUTC(CTLBACKQ, out); if (oldstyle) goto parsebackq_oldreturn; goto parsebackq_newreturn; } #if ENABLE_SH_MATH_SUPPORT /* * Parse an arithmetic expansion (indicate start of one and set state) */ parsearith: { if (++arinest == 1) { prevsyntax = syntax; syntax = ARISYNTAX; USTPUTC(CTLARI, out); if (dblquote) USTPUTC('"', out); else USTPUTC(' ', out); } else { /* * we collapse embedded arithmetic expansion to * parenthesis, which should be equivalent */ USTPUTC('(', out); } goto parsearith_return; } #endif } /* end of readtoken */ /* * Read the next input token. * If the token is a word, we set backquotelist to the list of cmds in * backquotes. We set quoteflag to true if any part of the word was * quoted. * If the token is TREDIR, then we set redirnode to a structure containing * the redirection. * In all cases, the variable startlinno is set to the number of the line * on which the token starts. * * [Change comment: here documents and internal procedures] * [Readtoken shouldn't have any arguments. Perhaps we should make the * word parsing code into a separate routine. In this case, readtoken * doesn't need to have any internal procedures, but parseword does. * We could also make parseoperator in essence the main routine, and * have parseword (readtoken1?) handle both words and redirection.] */ #define NEW_xxreadtoken #ifdef NEW_xxreadtoken /* singles must be first! */ static const char xxreadtoken_chars[7] ALIGN1 = { '\n', '(', ')', /* singles */ '&', '|', ';', /* doubles */ 0 }; #define xxreadtoken_singles 3 #define xxreadtoken_doubles 3 static const char xxreadtoken_tokens[] ALIGN1 = { TNL, TLP, TRP, /* only single occurrence allowed */ TBACKGND, TPIPE, TSEMI, /* if single occurrence */ TEOF, /* corresponds to trailing nul */ TAND, TOR, TENDCASE /* if double occurrence */ }; static int xxreadtoken(void) { int c; if (tokpushback) { tokpushback = 0; return lasttoken; } setprompt_if(needprompt, 2); startlinno = g_parsefile->linno; for (;;) { /* until token or start of word found */ c = pgetc_fast(); if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) continue; if (c == '#') { while ((c = pgetc()) != '\n' && c != PEOF) continue; pungetc(); } else if (c == '\\') { if (pgetc() != '\n') { pungetc(); break; /* return readtoken1(...) */ } startlinno = ++g_parsefile->linno; setprompt_if(doprompt, 2); } else { const char *p; p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1; if (c != PEOF) { if (c == '\n') { g_parsefile->linno++; needprompt = doprompt; } p = strchr(xxreadtoken_chars, c); if (p == NULL) break; /* return readtoken1(...) */ if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) { int cc = pgetc(); if (cc == c) { /* double occurrence? */ p += xxreadtoken_doubles + 1; } else { pungetc(); #if ENABLE_ASH_BASH_COMPAT if (c == '&' && cc == '>') /* &> */ break; /* return readtoken1(...) */ #endif } } } lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars]; return lasttoken; } } /* for (;;) */ return readtoken1(c, BASESYNTAX, (char *) NULL, 0); } #else /* old xxreadtoken */ #define RETURN(token) return lasttoken = token static int xxreadtoken(void) { int c; if (tokpushback) { tokpushback = 0; return lasttoken; } setprompt_if(needprompt, 2); startlinno = g_parsefile->linno; for (;;) { /* until token or start of word found */ c = pgetc_fast(); switch (c) { case ' ': case '\t': IF_ASH_ALIAS(case PEOA:) continue; case '#': while ((c = pgetc()) != '\n' && c != PEOF) continue; pungetc(); continue; case '\\': if (pgetc() == '\n') { startlinno = ++g_parsefile->linno; setprompt_if(doprompt, 2); continue; } pungetc(); goto breakloop; case '\n': g_parsefile->linno++; needprompt = doprompt; RETURN(TNL); case PEOF: RETURN(TEOF); case '&': if (pgetc() == '&') RETURN(TAND); pungetc(); RETURN(TBACKGND); case '|': if (pgetc() == '|') RETURN(TOR); pungetc(); RETURN(TPIPE); case ';': if (pgetc() == ';') RETURN(TENDCASE); pungetc(); RETURN(TSEMI); case '(': RETURN(TLP); case ')': RETURN(TRP); default: goto breakloop; } } breakloop: return readtoken1(c, BASESYNTAX, (char *)NULL, 0); #undef RETURN } #endif /* old xxreadtoken */ static int readtoken(void) { int t; #if DEBUG smallint alreadyseen = tokpushback; #endif #if ENABLE_ASH_ALIAS top: #endif t = xxreadtoken(); /* * eat newlines */ if (checkkwd & CHKNL) { while (t == TNL) { parseheredoc(); t = xxreadtoken(); } } if (t != TWORD || quoteflag) { goto out; } /* * check for keywords */ if (checkkwd & CHKKWD) { const char *const *pp; pp = findkwd(wordtext); if (pp) { lasttoken = t = pp - tokname_array; TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1)); goto out; } } if (checkkwd & CHKALIAS) { #if ENABLE_ASH_ALIAS struct alias *ap; ap = lookupalias(wordtext, 1); if (ap != NULL) { if (*ap->val) { pushstring(ap->val, ap); } goto top; } #endif } out: checkkwd = 0; #if DEBUG if (!alreadyseen) TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : "")); else TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : "")); #endif return t; } static char peektoken(void) { int t; t = readtoken(); tokpushback = 1; return tokname_array[t][0]; } /* * Read and parse a command. Returns NODE_EOF on end of file. * (NULL is a valid parse tree indicating a blank line.) */ static union node * parsecmd(int interact) { int t; tokpushback = 0; doprompt = interact; setprompt_if(doprompt, doprompt); needprompt = 0; t = readtoken(); if (t == TEOF) return NODE_EOF; if (t == TNL) return NULL; tokpushback = 1; return list(1); } /* * Input any here documents. */ static void parseheredoc(void) { struct heredoc *here; union node *n; here = heredoclist; heredoclist = NULL; while (here) { setprompt_if(needprompt, 2); readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX, here->eofmark, here->striptabs); n = stzalloc(sizeof(struct narg)); n->narg.type = NARG; /*n->narg.next = NULL; - stzalloc did it */ n->narg.text = wordtext; n->narg.backquote = backquotelist; here->here->nhere.doc = n; here = here->next; } } /* * called by editline -- any expansions to the prompt should be added here. */ #if ENABLE_ASH_EXPAND_PRMT static const char * expandstr(const char *ps) { union node n; /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value, * and token processing _can_ alter it (delete NULs etc). */ setinputstring((char *)ps); readtoken1(pgetc(), PSSYNTAX, nullstr, 0); popfile(); n.narg.type = NARG; n.narg.next = NULL; n.narg.text = wordtext; n.narg.backquote = backquotelist; expandarg(&n, NULL, 0); return stackblock(); } #endif /* * Execute a command or commands contained in a string. */ static int evalstring(char *s, int mask) { union node *n; struct stackmark smark; int skip; setinputstring(s); setstackmark(&smark); skip = 0; while ((n = parsecmd(0)) != NODE_EOF) { evaltree(n, 0); popstackmark(&smark); skip = evalskip; if (skip) break; } popfile(); skip &= mask; evalskip = skip; return skip; } /* * The eval command. */ static int FAST_FUNC evalcmd(int argc UNUSED_PARAM, char **argv) { char *p; char *concat; if (argv[1]) { p = argv[1]; argv += 2; if (argv[0]) { STARTSTACKSTR(concat); for (;;) { concat = stack_putstr(p, concat); p = *argv++; if (p == NULL) break; STPUTC(' ', concat); } STPUTC('\0', concat); p = grabstackstr(concat); } evalstring(p, ~SKIPEVAL); } return exitstatus; } /* * Read and execute commands. * "Top" is nonzero for the top level command loop; * it turns on prompting if the shell is interactive. */ static int cmdloop(int top) { union node *n; struct stackmark smark; int inter; int numeof = 0; TRACE(("cmdloop(%d) called\n", top)); for (;;) { int skip; setstackmark(&smark); #if JOBS if (doing_jobctl) showjobs(stderr, SHOW_CHANGED); #endif inter = 0; if (iflag && top) { inter++; chkmail(); } n = parsecmd(inter); #if DEBUG if (DEBUG > 2 && debug && (n != NODE_EOF)) showtree(n); #endif if (n == NODE_EOF) { if (!top || numeof >= 50) break; if (!stoppedjobs()) { if (!Iflag) break; out2str("\nUse \"exit\" to leave shell.\n"); } numeof++; } else if (nflag == 0) { /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ job_warning >>= 1; numeof = 0; evaltree(n, 0); } popstackmark(&smark); skip = evalskip; if (skip) { evalskip = 0; return skip & SKIPEVAL; } } return 0; } /* * Take commands from a file. To be compatible we should do a path * search for the file, which is necessary to find sub-commands. */ static char * find_dot_file(char *name) { char *fullname; const char *path = pathval(); struct stat statb; /* don't try this for absolute or relative paths */ if (strchr(name, '/')) return name; /* IIRC standards do not say whether . is to be searched. * And it is even smaller this way, making it unconditional for now: */ if (1) { /* ENABLE_ASH_BASH_COMPAT */ fullname = name; goto try_cur_dir; } while ((fullname = path_advance(&path, name)) != NULL) { try_cur_dir: if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { /* * Don't bother freeing here, since it will * be freed by the caller. */ return fullname; } if (fullname != name) stunalloc(fullname); } /* not found in the PATH */ ash_msg_and_raise_error("%s: not found", name); /* NOTREACHED */ } static int FAST_FUNC dotcmd(int argc, char **argv) { char *fullname; struct strlist *sp; volatile struct shparam saveparam; for (sp = cmdenviron; sp; sp = sp->next) setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED); if (!argv[1]) { /* bash says: "bash: .: filename argument required" */ return 2; /* bash compat */ } /* "false; . empty_file; echo $?" should print 0, not 1: */ exitstatus = 0; /* This aborts if file isn't found, which is POSIXly correct. * bash returns exitcode 1 instead. */ fullname = find_dot_file(argv[1]); argv += 2; argc -= 2; if (argc) { /* argc > 0, argv[0] != NULL */ saveparam = shellparam; shellparam.malloced = 0; shellparam.nparam = argc; shellparam.p = argv; }; /* This aborts if file can't be opened, which is POSIXly correct. * bash returns exitcode 1 instead. */ setinputfile(fullname, INPUT_PUSH_FILE); commandname = fullname; cmdloop(0); popfile(); if (argc) { freeparam(&shellparam); shellparam = saveparam; }; return exitstatus; } static int FAST_FUNC exitcmd(int argc UNUSED_PARAM, char **argv) { if (stoppedjobs()) return 0; if (argv[1]) exitstatus = number(argv[1]); raise_exception(EXEXIT); /* NOTREACHED */ } /* * Read a file containing shell functions. */ static void readcmdfile(char *name) { setinputfile(name, INPUT_PUSH_FILE); cmdloop(0); popfile(); } /* ============ find_command inplementation */ /* * Resolve a command name. If you change this routine, you may have to * change the shellexec routine as well. */ static void find_command(char *name, struct cmdentry *entry, int act, const char *path) { struct tblentry *cmdp; int idx; int prev; char *fullname; struct stat statb; int e; int updatetbl; struct builtincmd *bcmd; /* If name contains a slash, don't use PATH or hash table */ if (strchr(name, '/') != NULL) { entry->u.index = -1; if (act & DO_ABS) { while (stat(name, &statb) < 0) { #ifdef SYSV if (errno == EINTR) continue; #endif entry->cmdtype = CMDUNKNOWN; return; } } entry->cmdtype = CMDNORMAL; return; } /* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */ updatetbl = (path == pathval()); if (!updatetbl) { act |= DO_ALTPATH; if (strstr(path, "%builtin") != NULL) act |= DO_ALTBLTIN; } /* If name is in the table, check answer will be ok */ cmdp = cmdlookup(name, 0); if (cmdp != NULL) { int bit; switch (cmdp->cmdtype) { default: #if DEBUG abort(); #endif case CMDNORMAL: bit = DO_ALTPATH; break; case CMDFUNCTION: bit = DO_NOFUNC; break; case CMDBUILTIN: bit = DO_ALTBLTIN; break; } if (act & bit) { updatetbl = 0; cmdp = NULL; } else if (cmdp->rehash == 0) /* if not invalidated by cd, we're done */ goto success; } /* If %builtin not in path, check for builtin next */ bcmd = find_builtin(name); if (bcmd) { if (IS_BUILTIN_REGULAR(bcmd)) goto builtin_success; if (act & DO_ALTPATH) { if (!(act & DO_ALTBLTIN)) goto builtin_success; } else if (builtinloc <= 0) { goto builtin_success; } } #if ENABLE_FEATURE_SH_STANDALONE { int applet_no = find_applet_by_name(name); if (applet_no >= 0) { entry->cmdtype = CMDNORMAL; entry->u.index = -2 - applet_no; return; } } #endif /* We have to search path. */ prev = -1; /* where to start */ if (cmdp && cmdp->rehash) { /* doing a rehash */ if (cmdp->cmdtype == CMDBUILTIN) prev = builtinloc; else prev = cmdp->param.index; } e = ENOENT; idx = -1; loop: while ((fullname = path_advance(&path, name)) != NULL) { stunalloc(fullname); /* NB: code below will still use fullname * despite it being "unallocated" */ idx++; if (pathopt) { if (prefix(pathopt, "builtin")) { if (bcmd) goto builtin_success; continue; } if ((act & DO_NOFUNC) || !prefix(pathopt, "func") ) { /* ignore unimplemented options */ continue; } } /* if rehash, don't redo absolute path names */ if (fullname[0] == '/' && idx <= prev) { if (idx < prev) continue; TRACE(("searchexec \"%s\": no change\n", name)); goto success; } while (stat(fullname, &statb) < 0) { #ifdef SYSV if (errno == EINTR) continue; #endif if (errno != ENOENT && errno != ENOTDIR) e = errno; goto loop; } e = EACCES; /* if we fail, this will be the error */ if (!S_ISREG(statb.st_mode)) continue; if (pathopt) { /* this is a %func directory */ stalloc(strlen(fullname) + 1); /* NB: stalloc will return space pointed by fullname * (because we don't have any intervening allocations * between stunalloc above and this stalloc) */ readcmdfile(fullname); cmdp = cmdlookup(name, 0); if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION) ash_msg_and_raise_error("%s not defined in %s", name, fullname); stunalloc(fullname); goto success; } TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); if (!updatetbl) { entry->cmdtype = CMDNORMAL; entry->u.index = idx; return; } INT_OFF; cmdp = cmdlookup(name, 1); cmdp->cmdtype = CMDNORMAL; cmdp->param.index = idx; INT_ON; goto success; } /* We failed. If there was an entry for this command, delete it */ if (cmdp && updatetbl) delete_cmd_entry(); if (act & DO_ERR) ash_msg("%s: %s", name, errmsg(e, "not found")); entry->cmdtype = CMDUNKNOWN; return; builtin_success: if (!updatetbl) { entry->cmdtype = CMDBUILTIN; entry->u.cmd = bcmd; return; } INT_OFF; cmdp = cmdlookup(name, 1); cmdp->cmdtype = CMDBUILTIN; cmdp->param.cmd = bcmd; INT_ON; success: cmdp->rehash = 0; entry->cmdtype = cmdp->cmdtype; entry->u = cmdp->param; } /* ============ trap.c */ /* * The trap builtin. */ static int FAST_FUNC trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { char *action; char **ap; int signo, exitcode; nextopt(nullstr); ap = argptr; if (!*ap) { for (signo = 0; signo < NSIG; signo++) { char *tr = trap_ptr[signo]; if (tr) { /* note: bash adds "SIG", but only if invoked * as "bash". If called as "sh", or if set -o posix, * then it prints short signal names. * We are printing short names: */ out1fmt("trap -- %s %s\n", single_quote(tr), get_signame(signo)); /* trap_ptr != trap only if we are in special-cased `trap` code. * In this case, we will exit very soon, no need to free(). */ /* if (trap_ptr != trap && tp[0]) */ /* free(tr); */ } } /* if (trap_ptr != trap) { free(trap_ptr); trap_ptr = trap; } */ return 0; } action = NULL; if (ap[1]) action = *ap++; exitcode = 0; while (*ap) { signo = get_signum(*ap); if (signo < 0) { /* Mimic bash message exactly */ ash_msg("%s: invalid signal specification", *ap); exitcode = 1; goto next; } INT_OFF; if (action) { if (LONE_DASH(action)) action = NULL; else action = ckstrdup(action); } free(trap[signo]); if (action) may_have_traps = 1; trap[signo] = action; if (signo != 0) setsignal(signo); INT_ON; next: ap++; } return exitcode; } /* ============ Builtins */ #if !ENABLE_FEATURE_SH_EXTRA_QUIET /* * Lists available builtins */ static int FAST_FUNC helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { unsigned col; unsigned i; out1fmt( "Built-in commands:\n" "------------------\n"); for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) { col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), builtintab[i].name + 1); if (col > 60) { out1fmt("\n"); col = 0; } } #if ENABLE_FEATURE_SH_STANDALONE { const char *a = applet_names; while (*a) { col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a); if (col > 60) { out1fmt("\n"); col = 0; } a += strlen(a) + 1; } } #endif out1fmt("\n\n"); return EXIT_SUCCESS; } #endif /* FEATURE_SH_EXTRA_QUIET */ #if MAX_HISTORY static int FAST_FUNC historycmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { show_history(line_input_state); return EXIT_SUCCESS; } #endif /* * The export and readonly commands. */ static int FAST_FUNC exportcmd(int argc UNUSED_PARAM, char **argv) { struct var *vp; char *name; const char *p; char **aptr; char opt; int flag; int flag_off; /* "readonly" in bash accepts, but ignores -n. * We do the same: it saves a conditional in nextopt's param. */ flag_off = 0; while ((opt = nextopt("np")) != '\0') { if (opt == 'n') flag_off = VEXPORT; } flag = VEXPORT; if (argv[0][0] == 'r') { flag = VREADONLY; flag_off = 0; /* readonly ignores -n */ } flag_off = ~flag_off; /*if (opt_p_not_specified) - bash doesnt check this. Try "export -p NAME" */ { aptr = argptr; name = *aptr; if (name) { do { p = strchr(name, '='); if (p != NULL) { p++; } else { vp = *findvar(hashvar(name), name); if (vp) { vp->flags = ((vp->flags | flag) & flag_off); continue; } } setvar(name, p, (flag & flag_off)); } while ((name = *++aptr) != NULL); return 0; } } /* No arguments. Show the list of exported or readonly vars. * -n is ignored. */ showvars(argv[0], flag, 0); return 0; } /* * Delete a function if it exists. */ static void unsetfunc(const char *name) { struct tblentry *cmdp; cmdp = cmdlookup(name, 0); if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION) delete_cmd_entry(); } /* * The unset builtin command. We unset the function before we unset the * variable to allow a function to be unset when there is a readonly variable * with the same name. */ static int FAST_FUNC unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { char **ap; int i; int flag = 0; int ret = 0; while ((i = nextopt("vf")) != 0) { flag = i; } for (ap = argptr; *ap; ap++) { if (flag != 'f') { i = unsetvar(*ap); ret |= i; if (!(i & 2)) continue; } if (flag != 'v') unsetfunc(*ap); } return ret & 1; } static const unsigned char timescmd_str[] ALIGN1 = { ' ', offsetof(struct tms, tms_utime), '\n', offsetof(struct tms, tms_stime), ' ', offsetof(struct tms, tms_cutime), '\n', offsetof(struct tms, tms_cstime), 0 }; static int FAST_FUNC timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { unsigned long clk_tck, s, t; const unsigned char *p; struct tms buf; clk_tck = sysconf(_SC_CLK_TCK); times(&buf); p = timescmd_str; do { t = *(clock_t *)(((char *) &buf) + p[1]); s = t / clk_tck; t = t % clk_tck; out1fmt("%lum%lu.%03lus%c", s / 60, s % 60, (t * 1000) / clk_tck, p[0]); p += 2; } while (*p); return 0; } #if ENABLE_SH_MATH_SUPPORT /* * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell. * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc. * * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru> */ static int FAST_FUNC letcmd(int argc UNUSED_PARAM, char **argv) { arith_t i; argv++; if (!*argv) ash_msg_and_raise_error("expression expected"); do { i = ash_arith(*argv); } while (*++argv); return !i; } #endif /* * The read builtin. Options: * -r Do not interpret '\' specially * -s Turn off echo (tty only) * -n NCHARS Read NCHARS max * -p PROMPT Display PROMPT on stderr (if input is from tty) * -t SECONDS Timeout after SECONDS (tty or pipe only) * -u FD Read from given FD instead of fd 0 * This uses unbuffered input, which may be avoidable in some cases. * TODO: bash also has: * -a ARRAY Read into array[0],[1],etc * -d DELIM End on DELIM char, not newline * -e Use line editing (tty only) */ static int FAST_FUNC readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { char *opt_n = NULL; char *opt_p = NULL; char *opt_t = NULL; char *opt_u = NULL; int read_flags = 0; const char *r; int i; while ((i = nextopt("p:u:rt:n:s")) != '\0') { switch (i) { case 'p': opt_p = optionarg; break; case 'n': opt_n = optionarg; break; case 's': read_flags |= BUILTIN_READ_SILENT; break; case 't': opt_t = optionarg; break; case 'r': read_flags |= BUILTIN_READ_RAW; break; case 'u': opt_u = optionarg; break; default: break; } } /* "read -s" needs to save/restore termios, can't allow ^C * to jump out of it. */ INT_OFF; r = shell_builtin_read(setvar2, argptr, bltinlookup("IFS"), /* can be NULL */ read_flags, opt_n, opt_p, opt_t, opt_u ); INT_ON; if ((uintptr_t)r > 1) ash_msg_and_raise_error(r); return (uintptr_t)r; } static int FAST_FUNC umaskcmd(int argc UNUSED_PARAM, char **argv) { static const char permuser[3] ALIGN1 = "ugo"; static const char permmode[3] ALIGN1 = "rwx"; static const short permmask[] ALIGN2 = { S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH }; /* TODO: use bb_parse_mode() instead */ char *ap; mode_t mask; int i; int symbolic_mode = 0; while (nextopt("S") != '\0') { symbolic_mode = 1; } INT_OFF; mask = umask(0); umask(mask); INT_ON; ap = *argptr; if (ap == NULL) { if (symbolic_mode) { char buf[18]; char *p = buf; for (i = 0; i < 3; i++) { int j; *p++ = permuser[i]; *p++ = '='; for (j = 0; j < 3; j++) { if ((mask & permmask[3 * i + j]) == 0) { *p++ = permmode[j]; } } *p++ = ','; } *--p = 0; puts(buf); } else { out1fmt("%.4o\n", mask); } } else { if (isdigit((unsigned char) *ap)) { mask = 0; do { if (*ap >= '8' || *ap < '0') ash_msg_and_raise_error(msg_illnum, argv[1]); mask = (mask << 3) + (*ap - '0'); } while (*++ap != '\0'); umask(mask); } else { mask = ~mask & 0777; if (!bb_parse_mode(ap, &mask)) { ash_msg_and_raise_error("illegal mode: %s", ap); } umask(~mask & 0777); } } return 0; } static int FAST_FUNC ulimitcmd(int argc UNUSED_PARAM, char **argv) { return shell_builtin_ulimit(argv); } /* ============ main() and helpers */ /* * Called to exit the shell. */ static void exitshell(void) { struct jmploc loc; char *p; int status; #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT save_history(line_input_state); #endif status = exitstatus; TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); if (setjmp(loc.loc)) { if (exception_type == EXEXIT) /* dash bug: it just does _exit(exitstatus) here * but we have to do setjobctl(0) first! * (bug is still not fixed in dash-0.5.3 - if you run dash * under Midnight Commander, on exit from dash MC is backgrounded) */ status = exitstatus; goto out; } exception_handler = &loc; p = trap[0]; if (p) { trap[0] = NULL; evalstring(p, 0); free(p); } flush_stdout_stderr(); out: setjobctl(0); _exit(status); /* NOTREACHED */ } static void init(void) { /* from input.c: */ /* we will never free this */ basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); /* from trap.c: */ signal(SIGCHLD, SIG_DFL); /* bash re-enables SIGHUP which is SIG_IGNed on entry. * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" */ signal(SIGHUP, SIG_DFL); /* from var.c: */ { char **envp; const char *p; struct stat st1, st2; initvar(); for (envp = environ; envp && *envp; envp++) { if (strchr(*envp, '=')) { setvareq(*envp, VEXPORT|VTEXTFIXED); } } setvar2("PPID", utoa(getppid())); #if ENABLE_ASH_BASH_COMPAT p = lookupvar("SHLVL"); setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT); #endif p = lookupvar("PWD"); if (p) { if (*p != '/' || stat(p, &st1) || stat(".", &st2) || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino ) { p = '\0'; } } setpwd(p, 0); } } //usage:#define ash_trivial_usage //usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" //usage:#define ash_full_usage "\n\n" //usage: "Unix shell interpreter" //usage:#if ENABLE_FEATURE_SH_IS_ASH //usage:# define sh_trivial_usage ash_trivial_usage //usage:# define sh_full_usage ash_full_usage //usage:#endif //usage:#if ENABLE_FEATURE_BASH_IS_ASH //usage:# define bash_trivial_usage ash_trivial_usage //usage:# define bash_full_usage ash_full_usage //usage:#endif /* * Process the shell command line arguments. */ static void procargs(char **argv) { int i; const char *xminusc; char **xargv; xargv = argv; arg0 = xargv[0]; /* if (xargv[0]) - mmm, this is always true! */ xargv++; for (i = 0; i < NOPTS; i++) optlist[i] = 2; argptr = xargv; if (options(/*cmdline:*/ 1)) { /* it already printed err message */ raise_exception(EXERROR); } xargv = argptr; xminusc = minusc; if (*xargv == NULL) { if (xminusc) ash_msg_and_raise_error(bb_msg_requires_arg, "-c"); sflag = 1; } if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) iflag = 1; if (mflag == 2) mflag = iflag; for (i = 0; i < NOPTS; i++) if (optlist[i] == 2) optlist[i] = 0; #if DEBUG == 2 debug = 1; #endif /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ if (xminusc) { minusc = *xargv++; if (*xargv) goto setarg0; } else if (!sflag) { setinputfile(*xargv, 0); setarg0: arg0 = *xargv++; commandname = arg0; } shellparam.p = xargv; #if ENABLE_ASH_GETOPTS shellparam.optind = 1; shellparam.optoff = -1; #endif /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */ while (*xargv) { shellparam.nparam++; xargv++; } optschanged(); } /* * Read /etc/profile or .profile. */ static void read_profile(const char *name) { int skip; if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0) return; skip = cmdloop(0); popfile(); if (skip) exitshell(); } /* * This routine is called when an error or an interrupt occurs in an * interactive shell and control is returned to the main command loop. */ static void reset(void) { /* from eval.c: */ evalskip = 0; loopnest = 0; /* from input.c: */ g_parsefile->left_in_buffer = 0; g_parsefile->left_in_line = 0; /* clear input buffer */ popallfiles(); /* from parser.c: */ tokpushback = 0; checkkwd = 0; /* from redir.c: */ clearredir(/*drop:*/ 0); } #if PROFILE static short profile_buf[16384]; extern int etext(); #endif /* * Main routine. We initialize things, parse the arguments, execute * profiles if we're a login shell, and then call cmdloop to execute * commands. The setjmp call sets up the location to jump to when an * exception occurs. When an exception occurs the variable "state" * is used to figure out how far we had gotten. */ int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ash_main(int argc UNUSED_PARAM, char **argv) { const char *shinit; volatile smallint state; struct jmploc jmploc; struct stackmark smark; /* Initialize global data */ INIT_G_misc(); INIT_G_memstack(); INIT_G_var(); #if ENABLE_ASH_ALIAS INIT_G_alias(); #endif INIT_G_cmdtable(); #if PROFILE monitor(4, etext, profile_buf, sizeof(profile_buf), 50); #endif #if ENABLE_FEATURE_EDITING line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP); #endif state = 0; if (setjmp(jmploc.loc)) { smallint e; smallint s; reset(); e = exception_type; if (e == EXERROR) exitstatus = 2; s = state; if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) { exitshell(); } if (e == EXINT) { outcslow('\n', stderr); } popstackmark(&smark); FORCE_INT_ON; /* enable interrupts */ if (s == 1) goto state1; if (s == 2) goto state2; if (s == 3) goto state3; goto state4; } exception_handler = &jmploc; #if DEBUG opentrace(); TRACE(("Shell args: ")); trace_puts_args(argv); #endif rootpid = getpid(); init(); setstackmark(&smark); procargs(argv); if (argv[0] && argv[0][0] == '-') isloginsh = 1; if (isloginsh) { const char *hp; state = 1; read_profile("/etc/profile"); state1: state = 2; hp = lookupvar("HOME"); if (hp) { hp = concat_path_file(hp, ".profile"); read_profile(hp); free((char*)hp); } } state2: state = 3; if ( #ifndef linux getuid() == geteuid() && getgid() == getegid() && #endif iflag ) { shinit = lookupvar("ENV"); if (shinit != NULL && *shinit != '\0') { read_profile(shinit); } } state3: state = 4; if (minusc) { /* evalstring pushes parsefile stack. * Ensure we don't falsely claim that 0 (stdin) * is one of stacked source fds. * Testcase: ash -c 'exec 1>&0' must not complain. */ // if (!sflag) g_parsefile->pf_fd = -1; // ^^ not necessary since now we special-case fd 0 // in is_hidden_fd() to not be considered "hidden fd" evalstring(minusc, 0); } if (sflag || minusc == NULL) { #if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY if (iflag) { const char *hp = lookupvar("HISTFILE"); if (!hp) { hp = lookupvar("HOME"); if (hp) { hp = concat_path_file(hp, ".ash_history"); setvar2("HISTFILE", hp); free((char*)hp); hp = lookupvar("HISTFILE"); } } if (hp) line_input_state->hist_file = hp; # if ENABLE_FEATURE_SH_HISTFILESIZE hp = lookupvar("HISTFILESIZE"); line_input_state->max_history = size_from_HISTFILESIZE(hp); # endif } #endif state4: /* XXX ??? - why isn't this before the "if" statement */ cmdloop(1); } #if PROFILE monitor(0); #endif #ifdef GPROF { extern void _mcleanup(void); _mcleanup(); } #endif TRACE(("End of main reached\n")); exitshell(); /* NOTREACHED */ } /*- * Copyright (c) 1989, 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * 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. */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/match.c������������������������������������������������������������������������0000644�0000000�0000000�00000006313�12263563520�014344� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * ##/%% variable matching code ripped out of ash shell for code sharing * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Copyright (c) 1989, 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au> * was re-ported from NetBSD and debianized. */ #ifdef STANDALONE # include <stdbool.h> # include <stdio.h> # include <stdlib.h> # include <string.h> # include <unistd.h> # define FAST_FUNC /* nothing */ # define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* nothing */ # define POP_SAVED_FUNCTION_VISIBILITY /* nothing */ #else # include "libbb.h" #endif #include <fnmatch.h> #include "match.h" char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags) { char *loc; char *end; unsigned len = strlen(string); int early_exit; /* We can stop the scan early only if the string part * we are matching against is shrinking, and the pattern has * an unquoted "star" at the corresponding end. There are two cases. * Case 1: * "qwerty" does not match against pattern "*zy", * no point in trying to match "werty", "erty" etc: */ early_exit = (flags == (SCAN_MOVE_FROM_LEFT + SCAN_MATCH_RIGHT_HALF) && pattern[0] == '*'); if (flags & SCAN_MOVE_FROM_LEFT) { loc = string; end = string + len + 1; } else { loc = string + len; end = string - 1; if (flags == (SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF)) { /* Case 2: * "qwerty" does not match against pattern "qz*", * no point in trying to match "qwert", "qwer" etc: */ const char *p = pattern + strlen(pattern); if (--p >= pattern && *p == '*') { early_exit = 1; while (--p >= pattern && *p == '\\') early_exit ^= 1; } } } while (loc != end) { char c; int r; c = *loc; if (flags & SCAN_MATCH_LEFT_HALF) { *loc = '\0'; r = fnmatch(pattern, string, 0); *loc = c; } else { r = fnmatch(pattern, loc, 0); } if (r == 0) /* match found */ return loc; if (early_exit) { #ifdef STANDALONE printf("(early exit) "); #endif break; } if (flags & SCAN_MOVE_FROM_LEFT) { loc++; } else { loc--; } } return NULL; } #ifdef STANDALONE int main(int argc, char *argv[]) { char *string; char *op; char *pattern; char *loc; setvbuf(stdout, NULL, _IONBF, 0); if (!argv[1]) { puts( "Usage: match <test> [test...]\n\n" "Where a <test> is the form: <string><op><match>\n" "This is to test the shell ${var#val} expression type.\n\n" "e.g. `match 'abc#a*'` -> bc" ); return 1; } while (*++argv) { size_t off; unsigned scan_flags; string = *argv; off = strcspn(string, "#%"); if (!off) { printf("invalid format\n"); continue; } op = string + off; scan_flags = pick_scan(op[0], op[1]); printf("'%s': flags:%x, ", string, scan_flags); pattern = op + 1; if (op[0] == op[1]) pattern++; op[0] = '\0'; loc = scan_and_match(string, pattern, scan_flags); if (scan_flags & SCAN_MATCH_LEFT_HALF) { printf("'%s'\n", loc); } else { if (loc) *loc = '\0'; printf("'%s'\n", string); } } return 0; } #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_doc.txt��������������������������������������������������������������������0000644�0000000�0000000�00000010340�12263563520�015240� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� Wait + signals We had some bugs here which are hard to test in testsuite. Bug 1280 (http://busybox.net/bugs/view.php?id=1280): was misbehaving in interactive ash. Correct behavior: $ sleep 20 & $ wait ^C $ wait ^C $ wait ^C ... Bug 1984 (http://busybox.net/bugs/view.php?id=1984): traps were not triggering: trap_handler_usr () { echo trap usr } trap_handler_int () { echo trap int } trap trap_handler_usr USR1 trap trap_handler_int INT sleep 3600 & echo "Please do: kill -USR1 $$" echo "or: kill -INT $$" while true; do wait; echo wait interrupted; done Bug 189 (https://bugs.busybox.net/show_bug.cgi?id=189) func() { sleep 1 } while (true); do func echo Looping done ^C was observed to make ash processes geometrically multiply (!) instead of exiting. (true) in subshell does not seem to matter, as another user reports the same with: trap "echo USR1" USR1 while true; do echo Sleeping sleep 5 done Compat note. Bash version 3.2.0(1) exits this script at the receipt of SIGINT _only_ if it had two last children die from it. The following trace was obtained while periodically running "killall -SIGINT sleep; sleep 0.1; kill -SIGINT <bash_PID>": 23:48:32.376707 clone(...) = 13528 23:48:32.388706 waitpid(-1, 0xffc832ec, 0) = ? ERESTARTSYS (To be restarted) 23:48:32.459761 --- SIGINT (Interrupt) @ 0 (0) --- kill -SIGINT <bash_PID> is ignored, back to waiting: 23:48:32.463706 waitpid(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0) = 13528 sleep exited with 0 23:48:37.377557 --- SIGCHLD (Child exited) @ 0 (0) --- 23:48:37.378451 clone(...) = 13538 23:48:37.390708 waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}], 0) = 13538 sleep was killed by "killall -SIGINT sleep" 23:48:38.523944 --- SIGCHLD (Child exited) @ 0 (0) --- 23:48:38.524861 clone(...) = 13542 23:48:38.538706 waitpid(-1, 0xffc832ec, 0) = ? ERESTARTSYS (To be restarted) 23:48:38.624761 --- SIGINT (Interrupt) @ 0 (0) --- kill -SIGINT <bash_PID> is ignored, back to waiting: 23:48:38.628706 waitpid(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0) = 13542 sleep exited with 0 23:48:43.525674 --- SIGCHLD (Child exited) @ 0 (0) --- 23:48:43.526563 clone(...) = 13545 23:48:43.538709 waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}], 0) = 13545 sleep was killed by "killall -SIGINT sleep" 23:48:44.466848 --- SIGCHLD (Child exited) @ 0 (0) --- 23:48:44.467735 clone(...) = 13549 23:48:44.481706 waitpid(-1, 0xffc832ec, 0) = ? ERESTARTSYS (To be restarted) 23:48:44.567757 --- SIGINT (Interrupt) @ 0 (0) --- kill -SIGINT <bash_PID> is ignored, back to waiting: 23:48:44.571706 waitpid(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0) = 13549 sleep exited with 0 23:48:49.468553 --- SIGCHLD (Child exited) @ 0 (0) --- 23:48:49.469445 clone(...) = 13551 23:48:49.481708 waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}], 0) = 13551 sleep was killed by "killall -SIGINT sleep" 23:48:50.515837 --- SIGCHLD (Child exited) @ 0 (0) --- 23:48:50.516718 clone(...) = 13555 23:48:50.530706 waitpid(-1, 0xffc832ec, 0) = ? ERESTARTSYS (To be restarted) 23:48:50.615761 --- SIGINT (Interrupt) @ 0 (0) --- kill -SIGINT <bash_PID> is ignored, back to waiting: 23:48:50.619705 waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}], 0) = 13555 sleep was killed by "killall -SIGINT sleep". This is the second one in a row. Kill ourself: 23:48:51.504604 kill(13515, SIGINT) = 0 23:48:51.504689 --- SIGINT (Interrupt) @ 0 (0) --- 23:48:51.504915 +++ killed by SIGINT +++ As long as there is at least one "sleep 5" which exited successfully (not killed by SIGINT), bash continues. This is not documented anywhere AFAIKS. Why keyboard ^C acts differently? 00:08:07.655985 clone(...) = 14270 00:08:07.669707 waitpid(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0) = 14270 00:08:12.656872 --- SIGCHLD (Child exited) @ 0 (0) --- 00:08:12.657743 clone(...) = 14273 00:08:12.671708 waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}], 0) = 14273 00:08:13.810778 --- SIGINT (Interrupt) @ 0 (0) --- 00:08:13.818705 kill(14269, SIGINT) = 0 00:08:13.820103 --- SIGINT (Interrupt) @ 0 (0) --- 00:08:13.820925 +++ killed by SIGINT +++ Perhaps because at the moment bash got SIGINT it had no children? (it did not manage to spawn new sleep yet, see the trace) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/���������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�015111� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-read/�����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�016771� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-read/read_n.right�����������������������������������������������0000644�0000000�0000000�00000000015�12263563520�021252� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test tes tes �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-read/read_ifs.right���������������������������������������������0000644�0000000�0000000�00000000341�12263563520�021600� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test 1: .a. .b. .c. test 2: .a. .b. .c. test 3: .a. .. .b,c. test 4: .a. .. .b,c. test 5: .a. .. .c. test 6: .a. .. .c. .d. test 7: .a. .. .b,c,d , ,. test 8: .. .a. .b. .c. test 9: .a. .b. .c. .. test A: .. .a. .. .b. .c. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-read/read_t.right�����������������������������������������������0000644�0000000�0000000�00000000024�12263563520�021260� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>< >< >test< >test< ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-read/read_r.right�����������������������������������������������0000644�0000000�0000000�00000000017�12263563520�021260� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������testbest test\ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-read/read_ifs.tests���������������������������������������������0000755�0000000�0000000�00000001611�12263563520�021631� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������printf 'a\t\tb\tc\n' | ( IFS=$(printf "\t") read a b c; echo "test 1: .$a. .$b. .$c." ) printf 'a\t\tb\tc\n' | ( IFS=$(printf " \t") read a b c; echo "test 2: .$a. .$b. .$c." ) printf 'a,,b,c\n' | ( IFS="," read a b c; echo "test 3: .$a. .$b. .$c." ) printf 'a,,b,c\n' | ( IFS=" ," read a b c; echo "test 4: .$a. .$b. .$c." ) printf 'a ,, c\n' | ( IFS=" ," read a b c; echo "test 5: .$a. .$b. .$c." ) printf 'a ,, c d\n' | ( IFS=" ," read a b c d; echo "test 6: .$a. .$b. .$c. .$d." ) printf ' a,,b,c,d , ,\n' | ( IFS=" ," read a b c; echo "test 7: .$a. .$b. .$c." ) printf '\t,\ta\t,\tb\tc' | ( IFS=$(printf " \t,") read a b c d; echo "test 8: .$a. .$b. .$c. .$d." ) printf '\t\ta\t,\tb\tc' | ( IFS=$(printf " \t,") read a b c d; echo "test 9: .$a. .$b. .$c. .$d." ) printf '\t,\ta\t,,\tb\tc' | ( IFS=$(printf " \t,") read a b c d e; echo "test A: .$a. .$b. .$c. .$d. .$e." ) �����������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-read/read_REPLY.tests�������������������������������������������0000755�0000000�0000000�00000000432�12263563520�021743� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo ' \abc1 d\ef ' | ( read ; echo "test 1: |$REPLY|" ) echo ' \abc2 d\ef ' | ( read -r ; echo "test 2: |$REPLY|" ) echo ' \abc3 d\ef ' | ( read REPLY; echo "test 3: |$REPLY|" ) echo ' \abc4 d\ef ' | ( read -r REPLY; echo "test 4: |$REPLY|" ) echo Done ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-read/read_REPLY.right�������������������������������������������0000644�0000000�0000000�00000000141�12263563520�021710� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test 1: | abc1 def | test 2: | \abc2 d\ef | test 3: |abc3 def| test 4: |\abc4 d\ef| Done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-read/read_n.tests�����������������������������������������������0000755�0000000�0000000�00000000207�12263563520�021305� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo 'test' | (read reply; echo "$reply") echo 'test' | (read -n 3 reply; echo "$reply") echo 'test' | (read -n3 reply; echo "$reply") �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-read/read_r.tests�����������������������������������������������0000755�0000000�0000000�00000000155�12263563520�021313� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo -e 'test\\\nbest' | (read reply; echo "$reply") echo -e 'test\\\nbest' | (read -r reply; echo "$reply") �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-read/read_t.tests�����������������������������������������������0000755�0000000�0000000�00000000545�12263563520�021320� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# bash 3.2 outputs: # >< { echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply<") # >< { sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<") # >test< { echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply<") # >test< { sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<") �����������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/��������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�017521� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/starquoted2.right���������������������������������������0000644�0000000�0000000�00000000220�12263563520�023025� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Should be printed Would not be printed by bash Would not be printed by bash Would not be printed by bash Should be printed Empty: Empty: Empty: ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/brace1.tests��������������������������������������������0000755�0000000�0000000�00000000064�12263563520�021743� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo {abc} echo { echo } {cmd ""{ {"" echo Done: $? ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/escape5.right�������������������������������������������0000644�0000000�0000000�00000000045�12263563520�022102� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a\nb\nc\n a b c a\nb\nc\n a b c Done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/escape3.right�������������������������������������������0000644�0000000�0000000�00000000241�12263563520�022076� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������v: a \ b \\ c \\\ d \\\\ e v: a \ b \\ c \\\ d \\\\ e Unquoted: .a. .\. .b. .\\. .c. .\\\. .d. .\\\\. .e. Quoted: .a. .\. .b. .\\. .c. .\\\. .d. .\\\\. .e. done ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/quote3.tests��������������������������������������������0000755�0000000�0000000�00000000636�12263563520�022033� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������empty='' echo 'Testing: in ""' for a in ""; do echo ".$a."; done echo 'Testing: in '"''" for a in ''; do echo ".$a."; done echo 'Testing: in $empty' for a in $empty; do echo ".$a."; done echo 'Testing: in $empty""' for a in $empty""; do echo ".$a."; done echo 'Testing: in $empty'"''" for a in $empty''; do echo ".$a."; done echo 'Testing: in "$empty"' for a in "$empty"; do echo ".$a."; done echo Finished ��������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/eol1.right����������������������������������������������0000644�0000000�0000000�00000000007�12263563520�021413� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Done:0 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/noeol.tests���������������������������������������������0000755�0000000�0000000�00000000042�12263563520�021716� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# next line has no EOL! echo HELLO����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/brace1.right��������������������������������������������0000644�0000000�0000000�00000000260�12263563520�021711� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{abc} { } hush: can't execute '{cmd': No such file or directory hush: can't execute '{': No such file or directory hush: can't execute '{': No such file or directory Done: 127 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/comment1.tests������������������������������������������0000755�0000000�0000000�00000000105�12263563520�022325� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo Nothing: #should-not-be-echoed echo String: ""#should-be-echoed �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/group2.tests��������������������������������������������0000755�0000000�0000000�00000000172�12263563520�022024� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Bug was in handling of "}&" without space { trap "echo got TERM" TERM; sleep 2; }& sleep 1; kill $!; wait echo Done: $? ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/escape4.right�������������������������������������������0000644�0000000�0000000�00000000007�12263563520�022077� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Ok End �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/noeol2.tests��������������������������������������������0000755�0000000�0000000�00000000076�12263563520�022007� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# last line has no EOL! if true then echo 1 else echo 2 fi������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/escape1.tests�������������������������������������������0000755�0000000�0000000�00000000140�12263563520�022122� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test "$CONFIG_FEATURE_FANCY_ECHO" = "y" || exit 77 echo "\\" echo a"\\"b echo '\\' echo c'\\'d ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/negate.tests��������������������������������������������0000755�0000000�0000000�00000001113�12263563520�022045� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo ! printing ! ! false echo $? ! true echo $? if ! false; then false; echo $?; fi echo $? if ! false; then ! false; echo $?; fi echo $? PRINTF=`which printf` for a in ! a b c; do echo $a; done for a in ! a b c; do ! printf "$a "; echo $?; done test x"$PRINTF" = x"" && exit 1 for a in ! a b c; do ! "$PRINTF" "$a "; echo $?; done for a in ! a b c; do ! printf "$a " | false; echo $?; done for a in ! a b c; do ! printf "$a " | true; echo $?; done for a in ! a b c; do ! { printf "$a " | false; }; echo $?; done for a in ! a b c; do ! { printf "$a " | true; }; echo $?; done echo Done �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/negate.right��������������������������������������������0000644�0000000�0000000�00000000146�12263563520�022022� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������! printing ! 0 1 1 0 0 0 ! a b c ! 1 a 1 b 1 c 1 ! 1 a 1 b 1 c 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 Done ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/noeol3.tests��������������������������������������������0000755�0000000�0000000�00000000052�12263563520�022002� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# last line has no EOL! echo "unterminated��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/redir_space.right���������������������������������������0000644�0000000�0000000�00000000054�12263563520�023035� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������z1.tmp: 1 z2.tmp: 1 "z1.tmp z2.tmp": TEST 0 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/escape4.tests�������������������������������������������0000755�0000000�0000000�00000000061�12263563520�022127� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������i\ f tr\ ue; th\ en echo "O\ k"; fi; echo "\ End"�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/argv0.tests���������������������������������������������0000755�0000000�0000000�00000000075�12263563520�021627� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" "$0" arg fi echo OK �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/escape5.tests�������������������������������������������0000755�0000000�0000000�00000000122�12263563520�022126� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������v="a\nb\nc\n" echo "$v" printf "$v" v='a\nb\nc\n' echo "$v" printf "$v" echo Done ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/group2.right��������������������������������������������0000644�0000000�0000000�00000000021�12263563520�021765� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������got TERM Done: 0 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/starquoted2.tests���������������������������������������0000755�0000000�0000000�00000001344�12263563520�023065� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# != 0; then exec "$THIS_SH" "$0" fi # No params! for a in "$*"; do echo Should be printed; done for a in "$@"; do echo Should not be printed; done # Yes, believe it or not, bash is mesmerized by "$@" and stops # treating "" as "this word cannot be expanded to nothing, # but must be at least null string". Now it can be expanded to nothing. for a in "$@"""; do echo Would not be printed by bash; done for a in """$@"; do echo Would not be printed by bash; done for a in """$@"''"$@"''; do echo Would not be printed by bash; done for a in ""; do echo Should be printed; done # Bug 207: "$@" expands to nothing, and we erroneously glob "%s\n" twice: printf 'Empty:%s\n' "$@" printf "Empty:%s\n" "$@" printf "Empty:%s\\n" "$@" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/brace2.right��������������������������������������������0000644�0000000�0000000�00000000021�12263563520�021705� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{q,w} {q,w} Done ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/process_subst.right�������������������������������������0000644�0000000�0000000�00000000050�12263563520�023447� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������TESTzzBEST TEST$(echo zz)BEST TEST'BEST ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/starquoted.tests����������������������������������������0000755�0000000�0000000�00000000324�12263563520�023000� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" "$0" 1 abc 'd e f' fi for a in "$*"; do echo ".$a."; done for a in "$@"; do echo ".$a."; done for a in "-$*-"; do echo ".$a."; done for a in "-$@-"; do echo ".$a."; done ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/group1.tests��������������������������������������������0000755�0000000�0000000�00000000022�12263563520�022015� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ echo word} }; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/quote2.tests��������������������������������������������0000755�0000000�0000000�00000000017�12263563520�022023� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a=1 echo ">$a" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/brace2.tests��������������������������������������������0000755�0000000�0000000�00000000110�12263563520�021734� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������v='{q,w}' # Should not brace-expand v value echo $v echo "$v" echo Done ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/noeol.right���������������������������������������������0000644�0000000�0000000�00000000006�12263563520�021666� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������HELLO ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/redir_space.tests���������������������������������������0000755�0000000�0000000�00000000323�12263563520�023064� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������v='z1.tmp z2.tmp' echo TEST >$v echo 'z1.tmp:' `cat 'z1.tmp' 2>/dev/null; echo $?` echo 'z2.tmp:' `cat 'z2.tmp' 2>/dev/null; echo $?` echo '"z1.tmp z2.tmp":' `cat 'z1.tmp z2.tmp' 2>/dev/null; echo $?` rm z*.tmp �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/escape1.right�������������������������������������������0000644�0000000�0000000�00000000016�12263563520�022074� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������\ a\b \\ c\\d ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/group1.right��������������������������������������������0000644�0000000�0000000�00000000010�12263563520�021762� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������word} } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/escape2.tests�������������������������������������������0000755�0000000�0000000�00000000074�12263563520�022131� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo "*?[a]*" echo a"*?[a]*"b echo '*?[a]*' echo c'*?[a]*'d ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/starquoted.right����������������������������������������0000644�0000000�0000000�00000000104�12263563520�022744� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.1 abc d e f. .1. .abc. .d e f. .-1 abc d e f-. .-1. .abc. .d e f-. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/argv0.right���������������������������������������������0000644�0000000�0000000�00000000003�12263563520�021566� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������OK �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/comment1.right������������������������������������������0000644�0000000�0000000�00000000043�12263563520�022276� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Nothing: String: #should-be-echoed ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/quote3.right��������������������������������������������0000644�0000000�0000000�00000000210�12263563520�021767� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Testing: in "" .. Testing: in '' .. Testing: in $empty Testing: in $empty"" .. Testing: in $empty'' .. Testing: in "$empty" .. Finished ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/escape2.right�������������������������������������������0000644�0000000�0000000�00000000040�12263563520�022072� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������*?[a]* a*?[a]*b *?[a]* c*?[a]*d ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/process_subst.tests�������������������������������������0000755�0000000�0000000�00000000140�12263563520�023477� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo "TEST`echo zz;echo;echo`BEST" echo "TEST`echo '$(echo zz)'`BEST" echo "TEST`echo "'"`BEST" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/groups_and_keywords1.tests������������������������������0000755�0000000�0000000�00000000451�12263563520�024757� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo "Semicolons after } can be omitted 1:" if { echo foo; } then { echo bar; } fi echo "Semicolons after } can be omitted 2:" while { echo foo; } do { echo bar; break; } done echo "Semicolons after fi can be omitted:" while if echo foo; then echo bar; fi do echo baz; break; done echo Done:$? �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/quote4.right��������������������������������������������0000644�0000000�0000000�00000000004�12263563520�021771� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a b ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/escape3.tests�������������������������������������������0000755�0000000�0000000�00000000320�12263563520�022124� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test "$CONFIG_FEATURE_FANCY_ECHO" = "y" || exit 77 v='a \ b \\ c \\\ d \\\\ e' echo v: $v echo v: "$v" echo Unquoted: for a in $v; do echo .$a.; done echo Quoted: for a in $v; do echo ".$a."; done echo done ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/eol1.tests����������������������������������������������0000755�0000000�0000000�00000000505�12263563520�021446� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# bug was that we treated <newline> as ';' in this line: true || echo foo | echo BAD1 | cat # variation on the same theme true || echo foo | # comment echo BAD2 | cat # variation on the same theme true || echo foo | echo BAD3 | cat # this should error out, but currently works in hush: #true || echo foo |; echo Done:$? �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/quote4.tests��������������������������������������������0000755�0000000�0000000�00000000026�12263563520�022025� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a_b='a b' echo "$a_b" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/quote1.right��������������������������������������������0000644�0000000�0000000�00000000004�12263563520�021766� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������'1' ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/noeol2.right��������������������������������������������0000644�0000000�0000000�00000000002�12263563520�021744� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/groups_and_keywords1.right������������������������������0000644�0000000�0000000�00000000221�12263563520�024722� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Semicolons after } can be omitted 1: foo bar Semicolons after } can be omitted 2: foo bar Semicolons after fi can be omitted: foo bar baz Done:0 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/noeol3.right��������������������������������������������0000644�0000000�0000000�00000000043�12263563520�021752� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������hush: syntax error: unterminated " ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/quote1.tests��������������������������������������������0000755�0000000�0000000�00000000020�12263563520�022014� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a=1 echo "'$a'" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-parsing/quote2.right��������������������������������������������0000644�0000000�0000000�00000000003�12263563520�021766� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>1 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/�����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�017024� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/subshell.tests���������������������������������������������0000755�0000000�0000000�00000001104�12263563520�021726� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Non-empty traps should be reset in subshell # HUP is special in interactive shells trap '' HUP # QUIT is always special trap '' QUIT # SYS is not special trap '' SYS # WINCH is harmless trap 'bad: caught WINCH' WINCH # With TERM we'll check whether it is reset trap 'bad: caught TERM' TERM (trap; "$THIS_SH" -c 'kill -HUP $PPID'; echo Ok) (trap; "$THIS_SH" -c 'kill -QUIT $PPID'; echo Ok) (trap; "$THIS_SH" -c 'kill -SYS $PPID'; echo Ok) (trap; "$THIS_SH" -c 'kill -WINCH $PPID'; echo Ok) (trap; "$THIS_SH" -c 'kill -TERM $PPID'; echo Bad: TERM is not reset) echo Done ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/save-ret.right���������������������������������������������0000644�0000000�0000000�00000000007�12263563520�021604� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������YEAH 0 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/signal_read2.right�����������������������������������������0000644�0000000�0000000�00000000015�12263563520�022407� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������HUP Done:129 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/exit.tests�������������������������������������������������0000755�0000000�0000000�00000001254�12263563520�021064� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������"$THIS_SH" -c 'trap "echo cow" 0' "$THIS_SH" -c 'trap "echo moo" EXIT' "$THIS_SH" -c 'trap "echo no" 0; trap 0' ( exitfunc() { echo "Traps1:" trap # EXIT trap is disabled after it is triggered, # it can not be "re-armed" like this: trap "echo Should not run" EXIT echo "Traps2:" trap } trap 'exitfunc' EXIT exit 42 ) echo Check1: $? ( exitfunc() { echo "Traps1:" trap # EXIT trap is disabled after it is triggered, # it can not be "re-armed" like this: trap "echo Should not run" EXIT echo "Traps2:" trap exit 42 } trap 'exitfunc' EXIT exit 66 ) echo Check2: $? ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/usage.right������������������������������������������������0000644�0000000�0000000�00000000222�12263563520�021161� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������___ ___ ___ trap -- 'a' EXIT trap -- 'a' INT trap -- 'a' USR1 trap -- 'a' USR2 ___ ___ trap -- 'a' USR1 trap -- 'a' USR2 ___ ___ trap -- 'a' USR2 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/signal_read1.tests�����������������������������������������0000755�0000000�0000000�00000000137�12263563520�022443� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������(sleep 1; kill -HUP $$) & trap 'echo "Got HUP:$?"; exit' HUP while true; do read ignored done ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/signal7.right����������������������������������������������0000644�0000000�0000000�00000000020�12263563520�021415� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Bug detected: 0 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/subshell.right���������������������������������������������0000644�0000000�0000000�00000000374�12263563520�021706� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������trap -- '' HUP trap -- '' QUIT trap -- '' SYS Ok trap -- '' HUP trap -- '' QUIT trap -- '' SYS Ok trap -- '' HUP trap -- '' QUIT trap -- '' SYS Ok trap -- '' HUP trap -- '' QUIT trap -- '' SYS Ok trap -- '' HUP trap -- '' QUIT trap -- '' SYS TERM Done ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/signal_read1.right�����������������������������������������0000644�0000000�0000000�00000000012�12263563520�022403� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Got HUP:0 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/signal_read2.tests�����������������������������������������0000755�0000000�0000000�00000000133�12263563520�022440� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$THIS_SH -c ' (sleep 1; kill -HUP $$) & while true; do read ignored done ' echo "Done:$?" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/save-ret.tests���������������������������������������������0000755�0000000�0000000�00000000141�12263563520�021633� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# make sure we do not corrupt $? across traps trap "echo YEAH; false" USR1 kill -USR1 $$ echo $? �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/catch.right������������������������������������������������0000644�0000000�0000000�00000000063�12263563520�021142� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sending USR2 caught sending USR2 sending USR2 USR2 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/exit.right�������������������������������������������������0000644�0000000�0000000�00000000264�12263563520�021034� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������cow moo Traps1: trap -- 'exitfunc' EXIT Traps2: trap -- 'echo Should not run' EXIT Check1: 42 Traps1: trap -- 'exitfunc' EXIT Traps2: trap -- 'echo Should not run' EXIT Check2: 42 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/catch.tests������������������������������������������������0000755�0000000�0000000�00000000436�12263563520�021176� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# avoid ugly warnings about signals not being caught trap ":" USR1 USR2 "$THIS_SH" -c ' trap "echo caught" USR2 echo "sending USR2" kill -USR2 $$ trap "" USR2 echo "sending USR2" kill -USR2 $$ trap "-" USR2 echo "sending USR2" kill -USR2 $$ echo "not reached" ' trap "-" USR1 USR2 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/signal7.tests����������������������������������������������0000755�0000000�0000000�00000000611�12263563520�021453� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������bug() { trap : exit # Bug was causing sh to be run in subshell, # as if this line is replaced with (sh -c ...; exit $?) & # here: sh -c 'echo REAL_CHILD=$$' & echo PARENTS_IDEA_OF_CHILD=$! wait # make sure bkgd shell completes } bug | { while read varval; do eval $varval done test x"$REAL_CHILD" != x"" \ && test x"$REAL_CHILD" = x"$PARENTS_IDEA_OF_CHILD" echo "Bug detected: $?" } �����������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/usage.tests������������������������������������������������0000755�0000000�0000000�00000000351�12263563520�021214� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# no output -- default state echo ___ trap # assign some traps echo ___ trap "a" EXIT INT USR1 USR2 # show them all echo ___ trap # clear one echo ___ trap 0 INT echo ___ trap # clear another echo ___ trap "-" USR1 echo ___ trap ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/savetrap.tests���������������������������������������������0000755�0000000�0000000�00000000177�12263563520�021743� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������trap 'echo Exiting' EXIT trap 'echo WINCH!' SIGWINCH v=` trap ` echo "$v" v=$( trap ) echo "$v" v=`trap` echo "$v" echo Done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-trap/savetrap.right���������������������������������������������0000644�0000000�0000000�00000000265�12263563520�021711� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������trap -- 'echo Exiting' EXIT trap -- 'echo WINCH!' WINCH trap -- 'echo Exiting' EXIT trap -- 'echo WINCH!' WINCH trap -- 'echo Exiting' EXIT trap -- 'echo WINCH!' WINCH Done Exiting �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-leak/�����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�016772� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-leak/leak_argv1.tests�������������������������������������������0000755�0000000�0000000�00000007043�12263563520�022077� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Warm up i=1 while test $i != X; do set -- 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 shift shift 2 shift 5 shift 11 set -- 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 shift 3 shift 7 i=1$i if test $i = 1111111111111111111111111111111111111111111111; then i=2; fi if test $i = 1111111111111111111111111111111111111111111112; then i=3; fi if test $i = 1111111111111111111111111111111111111111111113; then i=4; fi if test $i = 1111111111111111111111111111111111111111111114; then i=5; fi if test $i = 1111111111111111111111111111111111111111111115; then i=6; fi if test $i = 1111111111111111111111111111111111111111111116; then i=7; fi if test $i = 1111111111111111111111111111111111111111111117; then i=8; fi if test $i = 1111111111111111111111111111111111111111111118; then i=9; fi if test $i = 1111111111111111111111111111111111111111111119; then i=a; fi if test $i = 111111111111111111111111111111111111111111111a; then i=b; fi if test $i = 111111111111111111111111111111111111111111111b; then i=c; fi if test $i = 111111111111111111111111111111111111111111111c; then i=d; fi if test $i = 111111111111111111111111111111111111111111111d; then i=e; fi if test $i = 111111111111111111111111111111111111111111111e; then i=f; fi if test $i = 111111111111111111111111111111111111111111111f; then i=g; fi if test $i = 111111111111111111111111111111111111111111111g; then i=h; fi if test $i = 111111111111111111111111111111111111111111111h; then i=i; fi if test $i = 111111111111111111111111111111111111111111111i; then i=j; fi if test $i = 111111111111111111111111111111111111111111111j; then i=X; fi done unset i set -- memleak echo "Measuring memory leak..." i=1 while test $i != X; do set -- 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 shift shift 2 shift 5 shift 11 set -- 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 shift 3 shift 7 i=1$i if test $i = 1111111111111111111111111111111111111111111111; then i=2; fi if test $i = 1111111111111111111111111111111111111111111112; then i=3; fi if test $i = 1111111111111111111111111111111111111111111113; then i=4; fi if test $i = 1111111111111111111111111111111111111111111114; then i=5; fi if test $i = 1111111111111111111111111111111111111111111115; then i=6; fi if test $i = 1111111111111111111111111111111111111111111116; then i=7; fi if test $i = 1111111111111111111111111111111111111111111117; then i=8; fi if test $i = 1111111111111111111111111111111111111111111118; then i=9; fi if test $i = 1111111111111111111111111111111111111111111119; then i=a; fi if test $i = 111111111111111111111111111111111111111111111a; then i=b; fi if test $i = 111111111111111111111111111111111111111111111b; then i=c; fi if test $i = 111111111111111111111111111111111111111111111c; then i=d; fi if test $i = 111111111111111111111111111111111111111111111d; then i=e; fi if test $i = 111111111111111111111111111111111111111111111e; then i=f; fi if test $i = 111111111111111111111111111111111111111111111f; then i=g; fi if test $i = 111111111111111111111111111111111111111111111g; then i=h; fi if test $i = 111111111111111111111111111111111111111111111h; then i=i; fi if test $i = 111111111111111111111111111111111111111111111i; then i=j; fi if test $i = 111111111111111111111111111111111111111111111j; then i=X; fi done unset i set -- memleak kb=$? if test $kb -le 4; then echo Ok #$kb else echo "Bad: $kb kb leaked" fi ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-leak/leak_argv1.right�������������������������������������������0000644�0000000�0000000�00000000034�12263563520�022040� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Measuring memory leak... Ok ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-bugs/�����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�017016� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-bugs/and_or_and_backgrounding.tests�����������������������������0000755�0000000�0000000�00000002056�12263563520�025067� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# UNFIXED BUG: hush thinks that ; && || & have the same precedence. # According to this doc, && || have higher precedence than ; &. # See example below. # Precedence of ; is not a problem in practice. Precedence of & is. # #http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html # #2.9.3 Lists # #An AND-OR list is a sequence of one or more pipelines separated by #the operators "&&" and "||" . # #A list is a sequence of one or more AND-OR lists separated by the operators #';' and '&' and optionally terminated by ';', '&', or <newline>. # #The operators "&&" and "||" shall have equal precedence and shall be #evaluated with left associativity. For example, both of the following #commands write solely bar to standard output: # # false && echo foo || echo bar # true || echo foo && echo bar # #A ';' or <newline> terminator shall cause the preceding AND-OR list #to be executed sequentially; an '&' shall cause asynchronous execution #of the preceding AND-OR list. echo First && sleep 0.2 && echo Third & sleep 0.1 echo Second wait echo Done ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-bugs/and_or_and_backgrounding.right�����������������������������0000644�0000000�0000000�00000000030�12263563520�025025� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������First Second Third Done ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-bugs/export_exp.right�������������������������������������������0000644�0000000�0000000�00000000072�12263563520�022247� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������aa0 bb0 a=aa0 b=bb0 aa1 bb1 a=aa1 b=bb1 zzz=zzz zz=* Done ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-bugs/export_exp.tests.disabled����������������������������������0000644�0000000�0000000�00000000651�12263563520�024045� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This test shows a very special handling of export and local # builtins by bash. v="a=aa0 b=bb0" # only 1st arg should be expanded in multiple words export $v c=$v echo $a $b echo $c # only 1st arg should be expanded in multiple words export `echo a=aa1 b=bb1` c=`echo a=aa1 b=bb1` echo $a $b echo $c >zz=zz >zzz=zzz # only 1st arg should be globbed export zzz* zz=* env | grep ^zz | sort rm -rf zz=zz zzz=zzz echo Done ���������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-glob/�����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�017001� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-glob/glob_and_assign.tests��������������������������������������0000755�0000000�0000000�00000000432�12263563520�023176� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>ZVAR=z.tmp >z.tmp ZVAR=*.tmp echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp" ZVAR=*.tmp /bin/echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp" ZVAR=*.tmp echo "$ZVAR" echo $ZVAR echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp" /bin/echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp" rm ZVAR=z.tmp z.tmp ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-glob/glob1.tests������������������������������������������������0000755�0000000�0000000�00000000053�12263563520�021070� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo *glob1?t[e]sts* echo "glob1"?'t'[e]s* �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-glob/glob_redir.tests�������������������������������������������0000755�0000000�0000000�00000000325�12263563520�022176� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Redirections are not globbed. # bash: # if run as "sh", they are not globbed, but # if run as "bash", they are! >z.tmp echo TEST >?.tmp echo 'z.tmp:' `cat 'z.tmp'` echo '?.tmp:' `cat '?.tmp'` rm 'z.tmp' '?.tmp' �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-glob/glob2.tests������������������������������������������������0000755�0000000�0000000�00000001306�12263563520�021073� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This test demonstrates that in unquoted $v, backslashes expand by this rule: # \z -> \\\z; \<eol> -> \\<eol> (for any z, special or not), # and subsequently globbing converts \\ to \ and treats \z as literal z # even if it is a special char. >'Zf' >'Z\f' echo 'Expected' 'Actual' v='\*'; echo 'Z\* :' Z$v echo 'Z* :' Z\* echo 'Z\f :' Z\\* echo 'Z\* :' Z\\\* # NB! only this matches Z$v output echo v='\z'; echo 'Z\z :' Z$v echo 'Zz :' Z\z echo 'Z\z :' Z\\z echo 'Z\z :' Z\\\z echo v='\'; echo 'Z\ :' Z$v echo 'Z\ :' Z\\ echo v='*'; echo 'Z\f Zf :' Z$v echo 'Z\f Zf :' Z* echo rm 'Z\f' 'Zf' echo Done: $? ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-glob/glob_redir.right�������������������������������������������0000644�0000000�0000000�00000000023�12263563520�022141� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������z.tmp: ?.tmp: TEST �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-glob/bash_brace1.tests������������������������������������������0000755�0000000�0000000�00000000274�12263563520�022223� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# unquoted $v should be globbed: v='*brace1.t*'; echo $v # ...but not brace expanded: v='*{b,b}race1.t*'; echo $v # whereas direct brces are expanded: echo *{b,b}race1.t* echo Done: $? ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-glob/bash_brace1.right������������������������������������������0000644�0000000�0000000�00000000115�12263563520�022165� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������bash_brace1.tests *{b,b}race1.t* bash_brace1.tests bash_brace1.tests Done: 0 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-glob/glob1.right������������������������������������������������0000644�0000000�0000000�00000000030�12263563520�021033� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������glob1.tests glob1.tests ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-glob/glob2.right������������������������������������������������0000644�0000000�0000000�00000000272�12263563520�021044� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Expected Actual Z\* : Z\* Z* : Z* Z\f : Z\f Z\* : Z\* Z\z : Z\z Zz : Zz Z\z : Z\z Z\z : Z\z Z\ : Z\ Z\ : Z\ Z\f Zf : Z\f Zf Z\f Zf : Z\f Zf Done: 0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-glob/glob_and_assign.right��������������������������������������0000644�0000000�0000000�00000000243�12263563520�023146� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp *.tmp ZVAR=z.tmp z.tmp ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/run-all��������������������������������������������������������������0000755�0000000�0000000�00000003777�12263563520�016425� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh unset LANG LANGUAGE unset LC_COLLATE unset LC_CTYPE unset LC_MONETARY unset LC_MESSAGES unset LC_NUMERIC unset LC_TIME unset LC_ALL if test ! -x hush; then if test ! -x ../../busybox; then echo "Can't run tests. Put hush binary into this directory (`pwd`)" exit 1 fi echo "No ./hush - creating a link to ../../busybox" ln -s ../../busybox hush fi if test ! -f .config; then if test ! -f ../../.config; then echo "Missing .config file" exit 1 fi cp ../../.config . || exit 1 fi eval $(sed -e '/^#/d' -e '/^$/d' -e 's:^:export :' .config) PATH="`pwd`:$PATH" # for hush and recho/zecho/printenv export PATH THIS_SH="`pwd`/hush" export THIS_SH do_test() { test -d "$1" || return 0 d=${d%/} # echo Running tests in directory "$1" ( tret=0 cd "$1" || { echo "cannot cd $1!"; exit 1; } for x in run-*; do test -f "$x" || continue case "$x" in "$0"|run-minimal|run-gprof) ;; *.orig|*~) ;; #*) echo $x ; sh $x ;; *) echo -n "$1/$x:" sh "$x" >"../$1-$x.fail" 2>&1 && \ { { echo " ok"; rm "../$1-$x.fail"; } || echo " fail"; } ;; esac done # Many bash run-XXX scripts just do this, # no point in duplication it all over the place for x in *.tests; do test -x "$x" || continue name="${x%%.tests}" test -f "$name.right" || continue # echo Running test: "$x" echo -n "$1/$x:" ( "$THIS_SH" "./$x" >"$name.xx" 2>&1 # filter C library differences sed -i \ -e "/: invalid option /s:'::g" \ "$name.xx" test $? -eq 77 && rm -f "../$1-$x.fail" && exit 77 diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" ) case $? in 0) echo " ok";; 77) echo " skip (feature disabled)";; *) echo " fail"; tret=1;; esac done exit ${tret} ) } # Main part of this script # Usage: run-all [directories] ret=0 if [ $# -lt 1 ]; then # All sub directories modules=`ls -d hush-*` for module in $modules; do do_test $module || ret=1 done else while [ $# -ge 1 ]; do if [ -d $1 ]; then do_test $1 || ret=1 fi shift done fi exit ${ret} �busybox-1.22.1/shell/hush_test/hush-vars/�����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�017031� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_expand_assign.tests����������������������������������0000755�0000000�0000000�00000001552�12263563520�024124� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# first try some invalid patterns (do in subshell due to parsing error) "$THIS_SH" -c 'echo ${=}' "$THIS_SH" -c 'echo ${:=}' # now some funky ones "$THIS_SH" -c 'echo ${#=}' "$THIS_SH" -c 'echo ${#:=}' # should error out "$THIS_SH" -c 'set --; echo _${1=}' "$THIS_SH" -c 'set --; echo _${1:=}' "$THIS_SH" -c 'set --; echo _${1=word}' "$THIS_SH" -c 'set --; echo _${1:=word}' # should not error "$THIS_SH" -c 'set aa; echo _${1=}' "$THIS_SH" -c 'set aa; echo _${1:=}' "$THIS_SH" -c 'set aa; echo _${1=word}' "$THIS_SH" -c 'set aa; echo _${1:=word}' # should work fine unset f; echo _$f unset f; echo _${f=} unset f; echo _${f:=} unset f; echo _${f=word} unset f; echo _${f:=word} f=; echo _$f f=; echo _${f=} f=; echo _${f:=} f=; echo _${f=word} f=; echo _${f:=word} f=fff; echo _$f f=fff; echo _${f=} f=fff; echo _${f:=} f=fff; echo _${f=word} f=fff; echo _${f:=word} ������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_expand_default.tests���������������������������������0000755�0000000�0000000�00000000722�12263563520�024262� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# first try some invalid patterns (do in subshell due to parsing error) "$THIS_SH" -c 'echo ${-}' "$THIS_SH" -c 'echo ${:-}' # now some funky ones echo _${#-} _${#:-} # now some valid ones set -- echo _$1 _${1-} _${1:-} _${1-word} _${1:-word} set -- aaaa echo _$1 _${1-} _${1:-} _${1-word} _${1:-word} unset f echo _$f _${f-} _${f:-} _${f-word} _${f:-word} f= echo _$f _${f-} _${f:-} _${f-word} _${f:-word} f=fff echo _$f _${f-} _${f:-} _${f-word} _${f:-word} ����������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_subst_in_for.tests�������������������������������������0000755�0000000�0000000�00000001434�12263563520�023464� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" "$0" abc "d e" fi echo 'Testing: in x y z' for a in x y z; do echo ".$a."; done echo 'Testing: in u $empty v' empty='' for a in u $empty v; do echo ".$a."; done echo 'Testing: in u " $empty" v' empty='' for a in u " $empty" v; do echo ".$a."; done echo 'Testing: in u $empty $empty$a v' a='a' for a in u $empty $empty$a v; do echo ".$a."; done echo 'Testing: in $a_b' a_b='a b' for a in $a_b; do echo ".$a."; done echo 'Testing: in $*' for a in $*; do echo ".$a."; done echo 'Testing: in $@' for a in $@; do echo ".$a."; done echo 'Testing: in -$*-' for a in -$*-; do echo ".$a."; done echo 'Testing: in -$@-' for a in -$@-; do echo ".$a."; done echo 'Testing: in $a_b -$a_b-' a_b='a b' for a in $a_b -$a_b-; do echo ".$a."; done echo Finished ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var2.right�������������������������������������������������0000644�0000000�0000000�00000000052�12263563520�020735� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������http://busybox.net http://busybox.net_abc ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_bash3.right��������������������������������������������0000644�0000000�0000000�00000000254�12263563520�021737� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 a041#c 2 a041#c 3 a\041#c 4 a\041#c 5 a\041#c 6 a\041#c 7 a\041#c 8 a\041#c 9 a\041#c 10 a\c 11 a\c 12 a\c 13 a\\c 14 a\\c 15 a\\c 16 a\tc 17 a\tc 18 a\tc 19 atc 20 a\tc ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_bash5.right��������������������������������������������0000644�0000000�0000000�00000000112�12263563520�021732� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 a/ 2 a/d 3 a/e/f 4 a\ 5 a\d 6 a\e\f 7 a\\ 8 a\\d 9 a\\e\\f a ab Done: 0 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_serial.tests�������������������������������������������0000755�0000000�0000000�00000000463�12263563520�022250� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a=a b=b c=c # Second assignment depends on the first: b=$a c=$b echo Assignments only: c=$c b=b c=c b=$a c=$b "$THIS_SH" -c 'echo Assignments and a command: c=$c' b=b c=c b=$a c=$b eval 'echo Assignments and a builtin: c=$c' b=b c=c f() { echo Assignments and a function: c=$c; } b=$a c=$b f echo Done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_glob.tests�������������������������������������������0000755�0000000�0000000�00000000201�12263563520�022212� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" "$0" 'param_glob.t*' echo NOT SHOWN exit fi echo $* echo $@ echo "$*" echo "$@" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_expand_indicate_error.right��������������������������0000644�0000000�0000000�00000001020�12263563520�025567� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������hush: syntax error: unterminated ${name} 0 0 ==== _ hush: 1: parameter null or not set hush: 1: parameter null or not set hush: 1: message1 hush: 1: message1 hush: 1: unset! hush: 1: null or unset! ==== _aaaa _aaaa _aaaa _aaaa _aaaa _aaaa _aaaa ==== _ hush: f: parameter null or not set hush: f: parameter null or not set hush: f: message3 hush: f: message3 hush: f: unset! hush: f: null or unset! ==== _ _ hush: f: parameter null or not set _ hush: f: message4 _ hush: f: null or unset! ==== _fff _fff _fff _fff _fff _fff _fff ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_expand_len.right�������������������������������������0000644�0000000�0000000�00000000134�12263563520�023361� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0 0 1 Make sure len parsing doesnt break arg count 0 0 4 4 Testing len op 4 3 2 1 0 0 0 3 0 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_leaks.right��������������������������������������������0000644�0000000�0000000�00000000003�12263563520�022026� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������OK �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_subshell.right���������������������������������������0000644�0000000�0000000�00000000034�12263563520�023064� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1=1 2=2 3=3 4=4 5=5 6=6 7=7 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_posix1.right�������������������������������������������0000644�0000000�0000000�00000000464�12263563520�022165� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Empty: Empty: Empty: Empty: Empty: Empty: Empty: Empty: Empty: Empty: Empty: Empty: Empty: Empty: Empty: Empty: abcdcd abcdcd abcdcd cdcd babcdcd babcdcd ababcdcd Empty: ababcdcd}_tail ababcdcd_tail ababcd ababcd ababcd abab ababcdc ababcdc ababcdcd Empty: ababcdcd}_tail ababcdcd_tail ababcdcd ab ab ab End ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var1.tests�������������������������������������������������0000755�0000000�0000000�00000000152�12263563520�020765� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������URL=http://busybox.net echo $URL echo ${URL}_abc true false; echo $? ${?} true { false; echo $? ${?}; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var1.right�������������������������������������������������0000644�0000000�0000000�00000000062�12263563520�020735� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������http://busybox.net http://busybox.net_abc 1 1 1 1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_preserved.tests����������������������������������������0000755�0000000�0000000�00000000272�12263563520�022766� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������export a=b # external program a=c /bin/true env | grep ^a= # builtin a=d true env | grep ^a= # exec with redirection only # in bash, this leaks! a=e exec 1>&1 env | grep ^a= echo OK ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_bash5.tests��������������������������������������������0000755�0000000�0000000�00000000713�12263563520�021771� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This testcase checks whether slashes in ${v/a/b} are parsed before # or after expansions v='a/b/c' s='b/c' r='e/f' echo "1 ${v/$s}" echo "2 ${v/$s/d}" echo "3 ${v/$s/$r}" v='a\b\c' s='b\\c' r='e\f' echo "4 ${v/$s}" echo "5 ${v/$s/d}" echo "6 ${v/$s/$r}" v='a\\b\\c' s='b\\\\c' r='e\\f' echo "7 ${v/$s}" echo "8 ${v/$s/d}" echo "9 ${v/$s/$r}" v='a-$a-\t-\\-\"-\`-\--\z-\*-\?-b' s='-$a-\\t-\\\\-\\"-\\`-\\--\\z-\\\*-\\\?-' echo "a ${v/$s}" echo Done: $? �����������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_bash4.right��������������������������������������������0000644�0000000�0000000�00000002166�12263563520�021744� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Source: a*b\*c Replace str: _\\_\z_ Pattern: single backslash and star: "replace literal star" Unquoted: a_\_z_b\*c Unquoted =: a_\_z_b\*c Quoted: a_\_\z_b\*c Quoted =: a_\_\z_b\*c Pattern: double backslash and star: "replace backslash and everything after it" Unquoted: a*b_\_z_ Unquoted =: a*b_\_z_ Quoted: a*b_\_\z_ Quoted =: a*b_\_\z_ Source: a\bc Replace str: _\\_\z_ Pattern: single backslash and b: "replace b" Unquoted: a\_\_z_c Unquoted =: a\_\_z_c Quoted: a\_\_\z_c Quoted =: a\_\_\z_c Pattern: double backslash and b: "replace backslash and b" Unquoted: a_\_z_c Unquoted =: a_\_z_c Quoted: a_\_\z_c Quoted =: a_\_\z_c Source: a\bc Replace str: _\\_\z_ (as variable $s) Pattern: single backslash and b: "replace b" Unquoted: a\_\\_\z_c Unquoted =: a\_\\_\z_c Quoted: a\_\\_\z_c Quoted =: a\_\\_\z_c Pattern: double backslash and b: "replace backslash and b" Unquoted: a_\\_\z_c Unquoted =: a_\\_\z_c Quoted: a_\\_\z_c Quoted =: a_\\_\z_c Done: 0 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_glob.right�������������������������������������������0000644�0000000�0000000�00000000076�12263563520�022174� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������param_glob.tests param_glob.tests param_glob.t* param_glob.t* ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_bash2.tests��������������������������������������������0000755�0000000�0000000�00000000771�12263563520�021772� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������var=abc123dcba123 echo ${var/d/x} echo ${var/c/x} echo ${var//c/x} echo ${var/[123]/x} echo ${var//[123]/x} echo ${var/c*/x} echo ${var/*c/x} # must match longest match: result is "abx23" echo ${var/c*1/x} # empty replacement - 2nd slash can be omitted echo ${var/[123]} echo ${var//[123]} ### ash doesn't support ### # match only at the beginning: ### echo ${var/#a/x} ### echo ${var/#b/x} # should not match ### echo ${var//#b/x} # should not match ### # match only at the end: ### echo ${var/%3/x} �������busybox-1.22.1/shell/hush_test/hush-vars/unset.tests������������������������������������������������0000755�0000000�0000000�00000000650�12263563520�021255� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# check invalid options are rejected unset - echo $? unset -m a b c echo $? # check funky usage unset echo $? # check normal usage echo ___ f=f g=g echo $? $f $g unset f echo $? $f $g unset g echo $? $f $g echo ___ f=f g=g echo $? $f $g unset f g echo $? $f $g f=f g=g echo $? $f $g unset -v f g echo $? $f $g # check read only vars echo ___ f=f g=g unset HUSH_VERSION echo $? $f $g unset f HUSH_VERSION g echo $? $f $g ����������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/empty.tests������������������������������������������������0000755�0000000�0000000�00000000430�12263563520�021251� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������e= echo a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F echo a $e b $e c $e d $e e $e f $e 1 $e 2 $e 3 $e 4 $e 5 $e 6 $e 7 $e 8 $e 9 $e 0 $e A $e B $e C $e D $e E $e F echo $e a $e b $e c $e d $e e $e f $e 1 $e 2 $e 3 $e 4 $e 5 $e 6 $e 7 $e 8 $e 9 $e 0 $e A $e B $e C $e D $e E $e F ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_expand_on_ifs.right������������������������������������0000644�0000000�0000000�00000000120�12263563520�023543� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 a b c 2 a + b c 3 a b c 4 a b c 5 a b c 6 a b + c 7 a b c 8 a b c 9 a b c ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_preserved.right����������������������������������������0000644�0000000�0000000�00000000017�12263563520�022733� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a=b a=b a=b OK �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_expand_on_ifs.tests������������������������������������0000755�0000000�0000000�00000000235�12263563520�023602� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������b=' b ' e='' echo 1 a $b c echo 2 a +$b c echo 3 a $e$b c echo 4 a "$e"$b c echo 5 a ""$b c echo 6 a $b+ c echo 7 a $b$e c echo 8 a $b"$e" c echo 9 a $b"" c �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_expand_in_redir.tests����������������������������������0000755�0000000�0000000�00000000441�12263563520�024117� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" "$0" abc "d e" fi echo TEST1 >"$1.out" echo TEST2 >"$2.out" # bash says: "$@.out": ambiguous redirect # ash handles it as if it is '$*' - we do the same echo TEST3 >"$@.out" cat abc.out "d e.out" "abc d e.out" rm abc.out "d e.out" "abc d e.out" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/glob_and_vars.tests����������������������������������������0000755�0000000�0000000�00000000040�12263563520�022710� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������v=. echo $v/glob_and_vars.[tr]* ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_bash6.right��������������������������������������������0000644�0000000�0000000�00000000105�12263563520�021735� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Expected Actual a*z : a*z \z : \z a1z a2z: a1z a2z z : z �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/empty.right������������������������������������������������0000644�0000000�0000000�00000000204�12263563520�021220� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_bash3.tests��������������������������������������������0000755�0000000�0000000�00000001015�12263563520�021763� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a='abc' r=${a//b/\041#} echo 1 $r echo 2 ${a//b/\041#} echo 3 "${a//b/\041#}" a='abc' r=${a//b/\\041#} echo 4 $r echo 5 ${a//b/\\041#} echo 6 "${a//b/\\041#}" a='abc' b='\041#' r=${a//b/$b} echo 7 $r echo 8 ${a//b/$b} echo 9 "${a//b/$b}" a='abc' b='\' r="${a//b/$b}" echo 10 $r echo 11 ${a//b/$b} echo 12 "${a//b/$b}" a='abc' b='\\' r="${a//b/$b}" echo 13 $r echo 14 ${a//b/$b} echo 15 "${a//b/$b}" a='abc' b='\t' r="${a//b/$b}" echo 16 $r echo 17 ${a//b/$b} echo 18 "${a//b/$b}" echo 19 ${a//b/\t} echo 20 "${a//b/\t}" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var3.tests�������������������������������������������������0000755�0000000�0000000�00000000216�12263563520�020770� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# reject invalid vars "$THIS_SH" -c 'echo ${1q}' "$THIS_SH" -c 'echo ${&}' #"$THIS_SH" -c 'echo ${$}' -- this is valid as it's the same as $$ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_serial.right�������������������������������������������0000644�0000000�0000000�00000000171�12263563520�022214� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Assignments only: c=a Assignments and a command: c=a Assignments and a builtin: c=a Assignments and a function: c=a Done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_bash2.right��������������������������������������������0000644�0000000�0000000�00000000154�12263563520�021735� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abc123xcba123 abx123dcba123 abx123dxba123 abcx23dcba123 abcxxxdcbaxxx abx xba123 abx23 abc23dcba123 abcdcba ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_expand_in_assign.right���������������������������������0000644�0000000�0000000�00000000054�12263563520�024246� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������. . .abc d e. .abc d e. .abc d e. .abc d e. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_unbackslash.right��������������������������������������0000644�0000000�0000000�00000000476�12263563520�023243� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������b1=-qwerty-t-\-"-`---z-*-?- b1=-qwerty-t-\-"-`---z-*-?- b2=-qwerty-\t-\-"-`-\--\z-\*-\?- b2=-qwerty-\t-\-"-`-\--\z-\*-\?- b3=-$a-\t-\\-\"-\`-\--\z-\*-\?- b3=-$a-\t-\\-\"-\`-\--\z-\*-\?- c=-$a-\t-\\-\"-\`-\--\z-\*-\?- c=-$a-\t-\\-\"-\`-\--\z-\*-\?- c=-$a-\t-\\-\"-\`-\--\z-\*-\?- c=-$a-\t-\\-\"-\`-\--\z-\*-\?- Done: 0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_expand_len.tests�������������������������������������0000755�0000000�0000000�00000000447�12263563520�023420� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������"$THIS_SH" -c 'echo $#' "$THIS_SH" -c 'echo $#' arg0 "$THIS_SH" -c 'echo $#' arg0 arg1 echo Make sure len parsing doesnt break arg count set -- echo $# ${#} set -- aaaa bbb cc d echo $# ${#} echo Testing len op echo ${#1} ${#2} ${#3} ${#4} ${#5} ${#6} unset e f=abc g= echo ${#e} ${#f} ${#g} �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_subst_in_for.right�������������������������������������0000644�0000000�0000000�00000000527�12263563520�023436� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Testing: in x y z .x. .y. .z. Testing: in u $empty v .u. .v. Testing: in u " $empty" v .u. . . .v. Testing: in u $empty $empty$a v .u. .a. .v. Testing: in $a_b .a. .b. Testing: in $* .abc. .d. .e. Testing: in $@ .abc. .d. .e. Testing: in -$*- .-abc. .d. .e-. Testing: in -$@- .-abc. .d. .e-. Testing: in $a_b -$a_b- .a. .b. .-a. .b-. Finished �������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_leaks.tests��������������������������������������������0000755�0000000�0000000�00000000256�12263563520�022070� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# external program a=b /bin/true env | grep ^a= # builtin a=b true env | grep ^a= # exec with redirection only # in bash, this leaks! a=b exec 1>&1 env | grep ^a= echo OK ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_posix1.tests�������������������������������������������0000755�0000000�0000000�00000001431�12263563520�022210� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������unset var echo Empty:${var#} echo Empty:${var##} echo Empty:${var#*} echo Empty:${var##*} echo Empty:${var%} echo Empty:${var%%} echo Empty:${var%*} echo Empty:${var%%*} var= echo Empty:${var#} echo Empty:${var##} echo Empty:${var#*} echo Empty:${var##*} echo Empty:${var%} echo Empty:${var%%} echo Empty:${var%*} echo Empty:${var%%*} var=ababcdcd echo ${var#ab} echo ${var##ab} echo ${var#a*b} echo ${var##a*b} echo ${var#?} echo ${var##?} echo ${var#*} echo Empty:${var##*} echo ${var#}}_tail echo ${var#\}}_tail echo ${var%cd} echo ${var%%cd} echo ${var%c*d} echo ${var%%c*d} echo ${var%?} echo ${var%%?} echo ${var%*} echo Empty:${var%%*} echo ${var#}}_tail echo ${var#\}}_tail echo ${var%\\*} a=ab}; echo ${a%\}}; a=abc; c=c; echo ${a%${c}} a=ab{{c; echo ${a%`echo {{c`} echo End ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/glob_and_vars.right����������������������������������������0000644�0000000�0000000�00000000054�12263563520�022665� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������./glob_and_vars.right ./glob_and_vars.tests ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var2.tests�������������������������������������������������0000755�0000000�0000000�00000000057�12263563520�020772� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������_1=http://busybox.net echo $_1 echo ${_1}_abc ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_expand_alt.tests�������������������������������������0000755�0000000�0000000�00000001010�12263563520�023405� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# first try some invalid patterns (do in subshell due to parsing error) "$THIS_SH" -c 'echo ${+} ; echo moo' "$THIS_SH" -c 'echo ${:+} ; echo moo' # now some funky ones. (bash doesn't accept ${#+}) echo _${#+}_ _${#:+}_ # now some valid ones set -- echo _$1 _${1+} _${1:+} _${1+word} _${1:+word} set -- aaaa echo _$1 _${1+} _${1:+} _${1+word} _${1:+word} unset f echo _$f _${f+} _${f:+} _${f+word} _${f:+word} f= echo _$f _${f+} _${f:+} _${f+word} _${f:+word} f=fff echo _$f _${f+} _${f:+} _${f+word} _${f:+word} ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_unbackslash.tests��������������������������������������0000755�0000000�0000000�00000000474�12263563520�023271� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Test for correct handling of backslashes a=qwerty b=-$a-\t-\\-\"-\`-\--\z-\*-\?- echo b1=$b echo "b1=$b" b="-$a-\t-\\-\"-\`-\--\z-\*-\?-" echo b2=$b echo "b2=$b" b='-$a-\t-\\-\"-\`-\--\z-\*-\?-' echo b3=$b echo "b3=$b" c=$b echo "c=$c" c=${b} echo "c=$c" c="$b" echo "c=$c" c="${b}" echo "c=$c" echo "Done: $?" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_expand_bash_substring.right��������������������������0000644�0000000�0000000�00000001576�12263563520�025633� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������hush: syntax error: unterminated ${name} hush: syntax error: unterminated ${name} hush: syntax error: unterminated ${name} hush: syntax error: unterminated ${name} 0123456789 1 =|| 1:1 =|| 1:1:2=|| 1::2 =|| 1:1: =|| 1:: =|| 1 =|0123| 1:1 =|123| 1:1:2=|12| 1::2 =|01| 1:1: =|| 1:: =|| f =|| f:1 =|| f:1:2=|| f::2 =|| f:1: =|| f:: =|| f =|| f:1 =|| f:1:2=|| f::2 =|| f:1: =|| f:: =|| f =|a| f:1 =|| f:1:2=|| f::2 =|a| f:1: =|| f:: =|| f =|0123456789| f:1 =|123456789| f:1:2=|12| f::2 =|01| f:1: =|| f:: =|| Substrings from special vars ? =|0| ?:1 =|| ?:1:2=|| ?::2 =|0| ?:1: =|| ?:: =|| # =|11| #:1 =|1| #:1:2=|1| #::2 =|11| #:1: =|| #:: =|| Substrings with expressions f =|01234567| f:1+1:2+2 =|2345| f:-1:2+2 =|01234567| f:1:f =|1234567| f:1:$f =|1234567| f:1:${f} =|1234567| f:1:${f:3:1} =|123| f:1:1`echo 1`=|1| Done ����������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_bash1.right��������������������������������������������0000644�0000000�0000000�00000000111�12263563520�021725� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� f bcdef abcdef abcdef bcde abcd abcd abcdef bcdef abcdef abcdef abcdef �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/star.tests�������������������������������������������������0000755�0000000�0000000�00000000331�12263563520�021064� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" star.tests 1 abc 'd e f' fi # 'd e f' should be split into 3 separate args: for a in $*; do echo ".$a."; done # must produce .1 abc d e f. for a in "$*"; do echo ".$a."; done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_bash1.tests��������������������������������������������0000755�0000000�0000000�00000000343�12263563520�021764� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������var=abcdef echo ${var:7} echo ${var:6} echo ${var:5} echo ${var:1} echo ${var:0} echo ${var:-1} echo ${var:1:4} echo ${var:0:4} echo ${var::4} echo ${var:-1:4} echo ${var:1:7} echo ${var:0:7} echo ${var::7} echo ${var:-1:7} ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_bash4.tests��������������������������������������������0000755�0000000�0000000�00000004433�12263563520�021773� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This testcase demonstrates that backslashes are treated differently # in 1st and 2nd parts of ${var/search/repl}: # if quoted ("${var/search/repl}"), and repl contains \a (a non-special char), # the backslash in repl stays; if unquoted, backslash is removed. # But search part does not act like that: \a is always converted to just a, # even in quotes. # # bash4 (and probably bash3 too): "Quoted:" results are different from # unquoted expansions - they have a backslash before z. # # The difference only exists if repl is a literal. If it is a variable: # ${v/.../$s}, then all backslashes are preserved in both cases. v='a*b\*c' echo 'Source: ' "$v" echo 'Replace str: ' '_\\_\z_' echo 'Pattern: ' 'single backslash and star: "replace literal star"' echo 'Unquoted: ' ${v/\*/_\\_\z_} r=${v/\*/_\\_\z_} echo 'Unquoted =: ' "$r" echo 'Quoted: ' "${v/\*/_\\_\z_}" r="${v/\*/_\\_\z_}" echo 'Quoted =: ' "$r" echo 'Pattern: ' 'double backslash and star: "replace backslash and everything after it"' echo 'Unquoted: ' ${v/\\*/_\\_\z_} r=${v/\\*/_\\_\z_} echo 'Unquoted =: ' "$r" echo 'Quoted: ' "${v/\\*/_\\_\z_}" r="${v/\\*/_\\_\z_}" echo 'Quoted =: ' "$r" echo v='a\bc' echo 'Source: ' "$v" echo 'Replace str: ' '_\\_\z_' echo 'Pattern: ' 'single backslash and b: "replace b"' echo 'Unquoted: ' ${v/\b/_\\_\z_} r=${v/\b/_\\_\z_} echo 'Unquoted =: ' "$r" echo 'Quoted: ' "${v/\b/_\\_\z_}" r="${v/\b/_\\_\z_}" echo 'Quoted =: ' "$r" echo 'Pattern: ' 'double backslash and b: "replace backslash and b"' echo 'Unquoted: ' ${v/\\b/_\\_\z_} r=${v/\\b/_\\_\z_} echo 'Unquoted =: ' "$r" echo 'Quoted: ' "${v/\\b/_\\_\z_}" r="${v/\\b/_\\_\z_}" echo 'Quoted =: ' "$r" echo v='a\bc' s='_\\_\z_' echo 'Source: ' "$v" echo 'Replace str: ' "$s" '(as variable $s)' echo 'Pattern: ' 'single backslash and b: "replace b"' echo 'Unquoted: ' ${v/\b/$s} r=${v/\b/$s} echo 'Unquoted =: ' "$r" echo 'Quoted: ' "${v/\b/$s}" r="${v/\b/$s}" echo 'Quoted =: ' "$r" echo 'Pattern: ' 'double backslash and b: "replace backslash and b"' echo 'Unquoted: ' ${v/\\b/$s} r=${v/\\b/$s} echo 'Unquoted =: ' "$r" echo 'Quoted: ' "${v/\\b/$s}" r="${v/\\b/$s}" echo 'Quoted =: ' "$r" echo echo Done: $? �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_expand_in_assign.tests���������������������������������0000755�0000000�0000000�00000000225�12263563520�024276� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" "$0" abc "d e" fi space=' ' echo .$space. a=$* echo .$a. a=$@ echo .$a. a="$*" echo .$a. a="$@" echo .$a. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/star.right�������������������������������������������������0000644�0000000�0000000�00000000044�12263563520�021035� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.1. .abc. .d. .e. .f. .1 abc d e f. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_expand_assign.right����������������������������������0000644�0000000�0000000�00000000457�12263563520�024077� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������hush: syntax error: unterminated ${name} hush: syntax error: unterminated ${name} 0 0 hush: $1: cannot assign in this way hush: $1: cannot assign in this way hush: $1: cannot assign in this way hush: $1: cannot assign in this way _aa _aa _aa _aa _ _ _ _word _word _ _ _ _ _word _fff _fff _fff _fff _fff �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/unset.right������������������������������������������������0000644�0000000�0000000�00000000235�12263563520�021224� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0 unset: invalid option -- m 1 0 ___ 0 f g 0 g 0 ___ 0 f g 0 0 f g 0 ___ hush: HUSH_VERSION: readonly variable 1 f g hush: HUSH_VERSION: readonly variable 1 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_expand_indicate_error.tests��������������������������0000755�0000000�0000000�00000003655�12263563520�025637� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# do all of these in subshells since it's supposed to error out # first try some invalid patterns #"$THIS_SH" -c 'echo ${?}' -- this is valid as it's the same as $? "$THIS_SH" -c 'echo ${:?}' # then some funky ones # note: bash prints 1 - treats it as "length of $#"? We print 0 "$THIS_SH" -c 'echo ${#?}' # bash prints 0 "$THIS_SH" -c 'echo ${#:?}' # now some valid ones export msg_unset="unset!" export msg_null_or_unset="null or unset!" echo ==== "$THIS_SH" -c 'set --; echo _$1' "$THIS_SH" -c 'set --; echo _${1?}' "$THIS_SH" -c 'set --; echo _${1:?}' "$THIS_SH" -c 'set --; echo _${1?message1}' "$THIS_SH" -c 'set --; echo _${1:?message1}' "$THIS_SH" -c 'set --; echo _${1?$msg_unset}' "$THIS_SH" -c 'set --; echo _${1:?$msg_null_or_unset}' echo ==== "$THIS_SH" -c 'set -- aaaa; echo _$1' "$THIS_SH" -c 'set -- aaaa; echo _${1?}' "$THIS_SH" -c 'set -- aaaa; echo _${1:?}' "$THIS_SH" -c 'set -- aaaa; echo _${1?word}' "$THIS_SH" -c 'set -- aaaa; echo _${1:?word}' "$THIS_SH" -c 'set -- aaaa; echo _${1?$msg_unset}' "$THIS_SH" -c 'set -- aaaa; echo _${1:?$msg_null_or_unset}' echo ==== "$THIS_SH" -c 'unset f; echo _$f' "$THIS_SH" -c 'unset f; echo _${f?}' "$THIS_SH" -c 'unset f; echo _${f:?}' "$THIS_SH" -c 'unset f; echo _${f?message3}' "$THIS_SH" -c 'unset f; echo _${f:?message3}' "$THIS_SH" -c 'unset f; echo _${f?$msg_unset}' "$THIS_SH" -c 'unset f; echo _${f:?$msg_null_or_unset}' echo ==== "$THIS_SH" -c 'f=; echo _$f' "$THIS_SH" -c 'f=; echo _${f?}' "$THIS_SH" -c 'f=; echo _${f:?}' "$THIS_SH" -c 'f=; echo _${f?word}' "$THIS_SH" -c 'f=; echo _${f:?message4}' "$THIS_SH" -c 'f=; echo _${f?$msg_unset}' "$THIS_SH" -c 'f=; echo _${f:?$msg_null_or_unset}' echo ==== "$THIS_SH" -c 'f=fff; echo _$f' "$THIS_SH" -c 'f=fff; echo _${f?}' "$THIS_SH" -c 'f=fff; echo _${f:?}' "$THIS_SH" -c 'f=fff; echo _${f?word}' "$THIS_SH" -c 'f=fff; echo _${f:?word}' "$THIS_SH" -c 'f=fff; echo _${f?$msg_unset}' "$THIS_SH" -c 'f=fff; echo _${f:?$msg_null_or_unset}' �����������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_in_pipes.right�����������������������������������������0000644�0000000�0000000�00000000030�12263563520�022535� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������b=1 b=2 b=3 b=4 b=5 b=6 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_bash6.tests��������������������������������������������0000755�0000000�0000000�00000000421�12263563520�021766� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This testcase checks globbing correctness in ${v/a/b} >a1z; >a2z; echo 'Expected' 'Actual' v='a bz'; echo 'a*z :' "${v/a*z/a*z}" v='a bz'; echo '\z :' "${v/a*z/\z}" v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z} v='a bz'; echo 'z :' ${v/a*z/\z} rm a1z a2z �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_subshell.tests���������������������������������������0000755�0000000�0000000�00000000467�12263563520�023126� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then "$THIS_SH" "$0" 1 2 3 4 5 6 7 8 9 exit fi echo 1=$1 { echo 2=$2; } { echo 3=$3; } & # cant use usleep as it isnt standard in $PATH -- # we fail when testing busybox compiled solely as "hush" wait ( echo 4=$4 ) ( echo 5=$5 ) & wait true | echo 6=$6 | cat true | { echo 7=$7; } | cat ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var3.right�������������������������������������������������0000644�0000000�0000000�00000000103�12263563520�020733� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������hush: invalid number '1q' hush: syntax error: unterminated ${name} �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_expand_alt.right�������������������������������������0000644�0000000�0000000�00000000245�12263563520�023366� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������hush: syntax error: unterminated ${name} hush: syntax error: unterminated ${name} __ __ _ _ _ _ _ _aaaa _ _ _word _word _ _ _ _ _ _ _ _ _word _ _fff _ _ _word _word �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_expand_in_redir.right����������������������������������0000644�0000000�0000000�00000000022�12263563520�024062� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������TEST1 TEST2 TEST3 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_expand_default.right���������������������������������0000644�0000000�0000000�00000000301�12263563520�024223� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������hush: syntax error: unterminated ${name} hush: syntax error: unterminated ${name} _0 _0 _ _ _ _word _word _aaaa _aaaa _aaaa _aaaa _aaaa _ _ _ _word _word _ _ _ _ _word _fff _fff _fff _fff _fff �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/param_expand_bash_substring.tests��������������������������0000755�0000000�0000000�00000004426�12263563520�025660� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# first try some invalid patterns # do all of these in subshells since it's supposed to error out export var=0123456789 "$THIS_SH" -c 'echo ${:}' "$THIS_SH" -c 'echo ${::}' "$THIS_SH" -c 'echo ${:1}' "$THIS_SH" -c 'echo ${::1}' #this also is not valid in bash, but we accept it: "$THIS_SH" -c 'echo ${var:}' # then some funky ones # UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}' # now some valid ones set --; echo "1 =|${1}|" set --; echo "1:1 =|${1:1}|" set --; echo "1:1:2=|${1:1:2}|" set --; echo "1::2 =|${1::2}|" set --; echo "1:1: =|${1:1:}|" set --; echo "1:: =|${1::}|" set -- 0123; echo "1 =|${1}|" set -- 0123; echo "1:1 =|${1:1}|" set -- 0123; echo "1:1:2=|${1:1:2}|" set -- 0123; echo "1::2 =|${1::2}|" set -- 0123; echo "1:1: =|${1:1:}|" set -- 0123; echo "1:: =|${1::}|" unset f; echo "f =|$f|" unset f; echo "f:1 =|${f:1}|" unset f; echo "f:1:2=|${f:1:2}|" unset f; echo "f::2 =|${f::2}|" unset f; echo "f:1: =|${f:1:}|" unset f; echo "f:: =|${f::}|" f=; echo "f =|$f|" f=; echo "f:1 =|${f:1}|" f=; echo "f:1:2=|${f:1:2}|" f=; echo "f::2 =|${f::2}|" f=; echo "f:1: =|${f:1:}|" f=; echo "f:: =|${f::}|" f=a; echo "f =|$f|" f=a; echo "f:1 =|${f:1}|" f=a; echo "f:1:2=|${f:1:2}|" f=a; echo "f::2 =|${f::2}|" f=a; echo "f:1: =|${f:1:}|" f=a; echo "f:: =|${f::}|" f=0123456789; echo "f =|$f|" f=0123456789; echo "f:1 =|${f:1}|" f=0123456789; echo "f:1:2=|${f:1:2}|" f=0123456789; echo "f::2 =|${f::2}|" f=0123456789; echo "f:1: =|${f:1:}|" f=0123456789; echo "f:: =|${f::}|" echo "Substrings from special vars" echo '? '"=|$?|" echo '?:1 '"=|${?:1}|" echo '?:1:2'"=|${?:1:2}|" echo '?::2 '"=|${?::2}|" echo '?:1: '"=|${?:1:}|" echo '?:: '"=|${?::}|" set -- 1 2 3 4 5 6 7 8 9 10 11 echo '# '"=|$#|" echo '#:1 '"=|${#:1}|" echo '#:1:2'"=|${#:1:2}|" echo '#::2 '"=|${#::2}|" echo '#:1: '"=|${#:1:}|" echo '#:: '"=|${#::}|" echo "Substrings with expressions" f=01234567; echo 'f '"=|$f|" f=01234567; echo 'f:1+1:2+2 '"=|${f:1+1:2+2}|" f=01234567; echo 'f:-1:2+2 '"=|${f:-1:2+2}|" f=01234567; echo 'f:1:f '"=|${f:1:f}|" f=01234567; echo 'f:1:$f '"=|${f:1:$f}|" f=01234567; echo 'f:1:${f} '"=|${f:1:${f}}|" f=01234567; echo 'f:1:${f:3:1} '"=|${f:1:${f:3:1}}|" f=01234567; echo 'f:1:1`echo 1`'"=|${f:1:`echo 1`}|" echo Done ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-vars/var_in_pipes.tests�����������������������������������������0000755�0000000�0000000�00000000235�12263563520�022574� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������b=1 env | grep ^b= true | b=2 env | grep ^b= a=1 true | b=3 env | grep ^b= (b=4 env) | grep ^b= (true | b=5 env) | grep ^b= (a=1 true | b=6 env) | grep ^b= �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-z_slow/���������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�017373� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-z_slow/leak_all2.right������������������������������������������0000644�0000000�0000000�00000000044�12263563520�022254� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Warm up Measuring memory leak... Ok ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-z_slow/leak_var.tests�������������������������������������������0000755�0000000�0000000�00000004212�12263563520�022243� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo "Measuring memory leak..." i=1 while test $i != X; do unset t t=111111111111111111111111111111111111111111111111111111111111111111111111 export t unset t t=111111111111111111111111111111111111111111111111111111111111111111111111 export t unset t t=111111111111111111111111111111111111111111111111111111111111111111111111 export t unset t t=111111111111111111111111111111111111111111111111111111111111111111111111 export t unset t t=111111111111111111111111111111111111111111111111111111111111111111111111 export t i=1$i if test $i = 1111111111111111111111111111111111111111111111; then i=2; fi if test $i = 1111111111111111111111111111111111111111111112; then i=3; fi if test $i = 1111111111111111111111111111111111111111111113; then i=4; fi if test $i = 1111111111111111111111111111111111111111111114; then i=5; fi if test $i = 1111111111111111111111111111111111111111111115; then i=6; fi if test $i = 1111111111111111111111111111111111111111111116; then i=7; fi if test $i = 1111111111111111111111111111111111111111111117; then i=8; fi if test $i = 1111111111111111111111111111111111111111111118; then i=9; fi if test $i = 1111111111111111111111111111111111111111111119; then i=a; fi if test $i = 111111111111111111111111111111111111111111111a; then i=b; fi if test $i = 111111111111111111111111111111111111111111111b; then i=c; fi if test $i = 111111111111111111111111111111111111111111111c; then i=d; fi if test $i = 111111111111111111111111111111111111111111111d; then i=e; fi if test $i = 111111111111111111111111111111111111111111111e; then i=f; fi if test $i = 111111111111111111111111111111111111111111111f; then i=g; fi if test $i = 111111111111111111111111111111111111111111111g; then i=h; fi if test $i = 111111111111111111111111111111111111111111111h; then i=i; fi if test $i = 111111111111111111111111111111111111111111111i; then i=j; fi if test $i = 111111111111111111111111111111111111111111111j; then i=X; fi memleak done memleak kb=$? if test $kb -le 4; then echo Ok else echo "Bad: $kb kb (or more) leaked" fi ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-z_slow/leak_all2.tests������������������������������������������0000755�0000000�0000000�00000005015�12263563520�022307� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# "Check many leaks" test #2 # Cramming all kinds of weird commands in here. # As you find leaks, please create separate, small test # for each leak. # Narrowing down the leak using this large test may be difficult. # It is intended to be a blanket "is everything ok?" test echo "Warm up" local_var="local val" export dev_null="/dev/null" >$dev_null echo hi1 $local_var `echo ho` >>/dev/null echo hi2 $local_var </dev/null | echo 2>&- | cat 1<>/dev/null { echo hi4 $local_var `echo ho` 1<>/dev/null; } ( echo hi4 $local_var `echo ho` 1<>/dev/null ) if echo $local_var; false then echo not run elif false <$dev_null then none else cat 0<>$dev_null 1<>"$dev_null" fi >>/dev/null { if echo $local_var; then cat <<HERE Hi cat HERE fi >>/dev/null } 1<>/dev/null while { echo $dev_null >>$dev_null; }; do cat <"$dev_null"; break; done ( until { echo $dev_null >>$dev_null | false; }; do cat <"$dev_null"; break; done ) <$dev_null f() { echo $1; } f >/dev/null memleak echo "Measuring memory leak..." # Please copy the entire block from above verbatim local_var="local val" export dev_null="/dev/null" >$dev_null echo hi1 $local_var `echo ho` >>/dev/null echo hi2 $local_var </dev/null | echo 2>&- | cat 1<>/dev/null { echo hi4 $local_var `echo ho` 1<>/dev/null; } ( echo hi4 $local_var `echo ho` 1<>/dev/null ) if echo $local_var; false then echo not run elif false <$dev_null then none else cat 0<>$dev_null 1<>"$dev_null" fi >>/dev/null { if echo $local_var; then cat <<HERE Hi cat HERE fi >>/dev/null } 1<>/dev/null while { echo $dev_null >>$dev_null; }; do cat <"$dev_null"; break; done ( until { echo $dev_null >>$dev_null | false; }; do cat <"$dev_null"; break; done ) <$dev_null f() { echo $1; } f >/dev/null # And same again local_var="local val" export dev_null="/dev/null" >$dev_null echo hi1 $local_var `echo ho` >>/dev/null echo hi2 $local_var </dev/null | echo 2>&- | cat 1<>/dev/null { echo hi4 $local_var `echo ho` 1<>/dev/null; } ( echo hi4 $local_var `echo ho` 1<>/dev/null ) if echo $local_var; false then echo not run elif false <$dev_null then none else cat 0<>$dev_null 1<>"$dev_null" fi >>/dev/null { if echo $local_var; then cat <<HERE Hi cat HERE fi >>/dev/null } 1<>/dev/null while { echo $dev_null >>$dev_null; }; do cat <"$dev_null"; break; done ( until { echo $dev_null >>$dev_null | false; }; do cat <"$dev_null"; break; done ) <$dev_null f() { echo $1; } f >/dev/null memleak kb=$? if test $kb -le 4; then echo Ok #$kb else echo "Bad: $kb kb (or more) leaked" fi �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-z_slow/leak_all1.right������������������������������������������0000644�0000000�0000000�00000000044�12263563520�022253� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Warm up Measuring memory leak... Ok ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-z_slow/leak_heredoc1.tests��������������������������������������0000755�0000000�0000000�00000000670�12263563520�023151� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo "Warm up" i=1 while test $i != 99; do : <<HERE Hello $i `echo builtin_$i` HERE : $((i++)) done memleak echo "Measuring memory leak..." i=1 while test $i != 99; do : <<HERE Hello $i `echo builtin_$i` HERE : $((i++)) done i=1 while test $i != 99; do : <<HERE Hello $i `echo builtin_$i` HERE : $((i++)) done memleak kb=$? if test $kb -le 4; then echo Ok #$kb else echo "Bad: $kb kb (or more) leaked" fi ������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-z_slow/leak_empty_tick.right������������������������������������0000644�0000000�0000000�00000000044�12263563520�023572� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Warm up Measuring memory leak... Ok ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-z_slow/leak_empty_tick.tests������������������������������������0000755�0000000�0000000�00000000523�12263563520�023624� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo "Warm up" i=1 while test $i != 9; do `true` : $((i++)) done memleak echo "Measuring memory leak..." i=1 while test $i != 199; do `true` : $((i++)) done i=1 while test $i != 199; do `true` : $((i++)) done memleak kb=$? if test $kb -le 4; then echo Ok #$kb else echo "Bad: $kb kb (or more) leaked" fi �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-z_slow/leak_var2.tests������������������������������������������0000755�0000000�0000000�00000003110�12263563520�022321� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo "Warm up" t=1 export t i=1 while test $i != X; do t=111111111111111111111111111111111111111111111111111111111111111111111110$i t=111111111111111111111111111111111111111111111111111111111111111111111111$i true t=111111111111111111111111111111111111111111111111111111111111111111111112$i /bin/true t=111111111111111111111111111111111111111111111111111111111111111111111113$i exec 1>&1 i=1$i if test $i = 1111111111111111111111111111111111111111111111; then i=2; fi if test $i = 1111111111111111111111111111111111111111111112; then i=3; fi if test $i = 1111111111111111111111111111111111111111111113; then i=4; fi if test $i = 1111111111111111111111111111111111111111111114; then i=X; fi done unset t i memleak echo "Measuring memory leak..." t=1 export t i=1 while test $i != X; do t=111111111111111111111111111111111111111111111111111111111111111111111110$i t=111111111111111111111111111111111111111111111111111111111111111111111111$i true t=111111111111111111111111111111111111111111111111111111111111111111111112$i /bin/true t=111111111111111111111111111111111111111111111111111111111111111111111113$i exec 1>&1 i=1$i if test $i = 1111111111111111111111111111111111111111111111; then i=2; fi if test $i = 1111111111111111111111111111111111111111111112; then i=3; fi if test $i = 1111111111111111111111111111111111111111111113; then i=4; fi if test $i = 1111111111111111111111111111111111111111111114; then i=X; fi done unset t i memleak kb=$? if test $kb -le 4; then echo Ok else echo "Bad: $kb kb (or more) leaked" fi ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-z_slow/leak_var2.right������������������������������������������0000644�0000000�0000000�00000000044�12263563520�022274� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Warm up Measuring memory leak... Ok ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-z_slow/leak_all1.tests������������������������������������������0000755�0000000�0000000�00000007424�12263563520�022314� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# "Check many leaks" test #1 # Cramming all kinds of weird commands in here. # As you find leaks, please create separate, small test # for each leak. # Narrowing down the leak using this large test may be difficult. # It is intended to be a blanket "is everything ok?" test echo "Warm up" i=1 l=1 t=1 export t while test $i != 99; do t=value1_$i; t=value2_$i true; t=value3_$i /bin/true; t=value4_$i exec 1>&1 { t=value3_$i /bin/true; } </dev/null if true; t=valueA_$i false >>/dev/null; true; then : << HERE >/dev/null; true <<HERE Hello builtin : HERE Hello $i true HERE elif false; then true; echo Doesnt run else { true; }; echo Doesnt run too >>/foo/bar fi { : /bin/*; } unset var echo >/dev/null ${var#} echo >/dev/null ${var##} echo >/dev/null ${var#*} echo >/dev/null ${var##*} echo >/dev/null ${var%} echo >/dev/null ${var%%} echo >/dev/null ${var%*} echo >/dev/null ${var%%*} var= echo >/dev/null ${var#} echo >/dev/null ${var##} echo >/dev/null ${var#*} echo >/dev/null ${var##*} echo >/dev/null ${var%} echo >/dev/null ${var%%} echo >/dev/null ${var%*} echo >/dev/null ${var%%*} var=ababcdcd echo >/dev/null ${var#ab} echo >/dev/null ${var##ab} echo >/dev/null ${var#a*b} echo >/dev/null ${var##a*b} echo >/dev/null ${var#?} echo >/dev/null ${var##?} echo >/dev/null ${var#*} echo >/dev/null ${var##*} echo >/dev/null ${var%cd} echo >/dev/null ${var%%cd} echo >/dev/null ${var%c*d} echo >/dev/null ${var%%c*d} echo >/dev/null ${var%?} echo >/dev/null ${var%%?} echo >/dev/null ${var%*} echo >/dev/null ${var%%*} set -- par1_$i par2_$i par3_$i par4_$i trap "echo trap$i" WINCH f() { true; true; true; true; true; true; true; true; } f() { true; true; true; true; true; true; true; true; echo $1; } i=iii$i t=ttt$i z=zzz$i f >/dev/null : $((i++)) done unset i l t unset -f f memleak echo "Measuring memory leak..." # Please copy the entire block from above verbatim i=1 l=1 t=1 export t while test $i != 99; do t=value1_$i; t=value2_$i true; t=value3_$i /bin/true; t=value4_$i exec 1>&1 { t=value3_$i /bin/true; } </dev/null if true; t=valueA_$i false >>/dev/null; true; then : << HERE >/dev/null; true <<HERE Hello builtin : HERE Hello $i true HERE elif false; then true; echo Doesnt run else { true; }; echo Doesnt run too >>/foo/bar fi { : /bin/*; } unset var echo >/dev/null ${var#} echo >/dev/null ${var##} echo >/dev/null ${var#*} echo >/dev/null ${var##*} echo >/dev/null ${var%} echo >/dev/null ${var%%} echo >/dev/null ${var%*} echo >/dev/null ${var%%*} var= echo >/dev/null ${var#} echo >/dev/null ${var##} echo >/dev/null ${var#*} echo >/dev/null ${var##*} echo >/dev/null ${var%} echo >/dev/null ${var%%} echo >/dev/null ${var%*} echo >/dev/null ${var%%*} var=ababcdcd echo >/dev/null ${var#ab} echo >/dev/null ${var##ab} echo >/dev/null ${var#a*b} echo >/dev/null ${var##a*b} echo >/dev/null ${var#?} echo >/dev/null ${var##?} echo >/dev/null ${var#*} echo >/dev/null ${var##*} echo >/dev/null ${var%cd} echo >/dev/null ${var%%cd} echo >/dev/null ${var%c*d} echo >/dev/null ${var%%c*d} echo >/dev/null ${var%?} echo >/dev/null ${var%%?} echo >/dev/null ${var%*} echo >/dev/null ${var%%*} set -- par1_$i par2_$i par3_$i par4_$i trap "echo trap$i" WINCH f() { true; true; true; true; true; true; true; true; } f() { true; true; true; true; true; true; true; true; echo $1; } i=iii$i t=ttt$i z=zzz$i f >/dev/null : $((i++)) done unset i l t unset -f f memleak kb=$? if test $kb -le 4; then echo Ok #$kb else echo "Bad: $kb kb leaked" fi ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-z_slow/leak_var3.tests������������������������������������������0000755�0000000�0000000�00000001240�12263563520�022324� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Was seen leaking on NOMMU build echo "Warm up" i=1; t=1; export t while test $i != 400; do t=valueA_$i true : $((i++)) done memleak echo "Measuring memory leak..." # Please copy the entire block from above verbatim i=1; t=1; export t while test $i != 400; do t=valueA_$i true : $((i++)) done i=1; t=1; export t while test $i != 400; do t=valueA_$i true : $((i++)) done i=1; t=1; export t while test $i != 400; do t=valueA_$i true : $((i++)) done i=1; t=1; export t while test $i != 400; do t=valueA_$i true : $((i++)) done memleak kb=$? if test $kb -le 4; then echo Ok #$kb else echo "Bad: $kb kb (or more) leaked" fi ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-z_slow/leak_heredoc1.right��������������������������������������0000644�0000000�0000000�00000000044�12263563520�023114� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Warm up Measuring memory leak... Ok ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-z_slow/leak_var3.right������������������������������������������0000644�0000000�0000000�00000000044�12263563520�022275� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Warm up Measuring memory leak... Ok ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-z_slow/leak_var.right�������������������������������������������0000644�0000000�0000000�00000000034�12263563520�022211� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Measuring memory leak... Ok ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/�����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�017011� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/break4.right�����������������������������������������������0000644�0000000�0000000�00000000024�12263563520�021212� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������A AA TRUE A AA OK:0 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/exec.tests�������������������������������������������������0000755�0000000�0000000�00000000773�12263563520�021031� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# make sure we have a way of checking these things cd /proc/$$/fd || cd /dev/fd || exit 1 [ -e 44 ] && exit 1 exec 44>/dev/null [ -e 44 ] || exit 1 echo pass fd out open [ -e 55 ] && exit 1 exec 55>&44 [ -e 55 ] || exit 1 echo pass fd out dup exec 44>&- [ -e 44 ] && exit 1 echo pass fd out close [ -e 66 ] && exit 1 exec 66</dev/null [ -e 66 ] || exit 1 echo pass fd in open [ -e 77 ] && exit 1 exec 77<&66 [ -e 77 ] || exit 1 echo pass fd in dup exec 66<&- [ -e 66 ] && exit 1 echo pass fd in close �����busybox-1.22.1/shell/hush_test/hush-misc/if_false_exitcode.tests������������������������������������0000755�0000000�0000000�00000000047�12263563520�023533� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if false; then echo Bad; fi echo Ok:$? �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/shift.tests������������������������������������������������0000755�0000000�0000000�00000000267�12263563520�021220� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" $0 abc "d e" 123 fi echo $0 $1 $2 shift echo $0 $1 $2 shift 999 echo $0 $1 $2 shift 2 echo $0 $1 $2 shift 2 echo $0 $1 $2 shift echo $0 $1 $2 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/while_in_subshell.right������������������������������������0000644�0000000�0000000�00000000006�12263563520�023541� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������OK: 0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/while4.tests�����������������������������������������������0000755�0000000�0000000�00000000121�12263563520�021264� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������false while false && echo Not reached; do echo BUG break done echo Ok:$? �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/for_with_keywords.right������������������������������������0000644�0000000�0000000�00000000023�12263563520�023611� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������do done then OK: 0 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/builtin1.right���������������������������������������������0000644�0000000�0000000�00000000025�12263563520�021572� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������VARIABLE=export OK:0 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/assignment2.rigth������������������������������������������0000644�0000000�0000000�00000000064�12263563520�022300� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������hush: can't exec 'a=b': No such file or directory 1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/assignment1.tests������������������������������������������0000755�0000000�0000000�00000001233�12263563520�022326� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Assignments after some keywords should still work if a=1 true; then a=1 true; elif a=1 true; then a=1 true; else a=1 true; fi echo if1:$? while a=1 true; do a=1 true; break; done echo while1:$? until a=1 false; do a=1 true; break; done echo until1:$? if a=1 true then a=1 true elif a=1 true then a=1 true else a=1 true fi echo if2:$? while a=1 true do a=1 true break done echo while2:$? until a=1 false do a=1 true break done echo until2:$? if a=1 true; then a=1 true; elif a=1 true; then a=1 true; else a=1 true; fi echo if3:$? while a=1 true; do a=1 true; break; done echo while3:$? until a=1 false; do a=1 true; break; done echo until3:$? ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/until1.tests�����������������������������������������������0000755�0000000�0000000�00000000233�12263563520�021310� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x=1 until test "$x" = 4; do echo $x; x=4; done # We had a bug in multi-line form x=1 until test "$x" = 4; do echo $x x=4 done echo Ok:$? ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/heredoc_huge.right�����������������������������������������0000644�0000000�0000000�00000000114�12263563520�022463� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������546ed3f5c81c780d3ab86ada14824237 - 546ed3f5c81c780d3ab86ada14824237 - End ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/break3.tests�����������������������������������������������0000755�0000000�0000000�00000000113�12263563520�021240� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������v=break; while true; do echo A; $v; echo B; break; echo C; done echo OK:$? �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/and-or.right�����������������������������������������������0000644�0000000�0000000�00000000076�12263563520�021231� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a1 a4 b1 b3 b4 b6 c4 c5 c7 c8 ff1 ff3 ft2 ft3 tf2 tf3 tt2 tt4 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/while3.tests�����������������������������������������������0000755�0000000�0000000�00000000115�12263563520�021266� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������while false; do # bash will require at least ":" here... done echo OK:$? ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/redir2.tests�����������������������������������������������0000755�0000000�0000000�00000000044�12263563520�021263� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo NOT SHOWN \2>/dev/null echo Ok ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/heredoc1.right���������������������������������������������0000644�0000000�0000000�00000000023�12263563520�021533� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qwe asd 123 456 Ok �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/heredoc_huge.tests�����������������������������������������0000755�0000000�0000000�00000000403�12263563520�022514� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This creates 120k heredoc echo 'cat <<HERE | md5sum' >"$0.tmp" yes "123456789 123456789 123456789 123456789" | head -3000 >>"$0.tmp" echo 'HERE' >>"$0.tmp" yes "123456789 123456789 123456789 123456789" | head -3000 | md5sum . "$0.tmp" rm "$0.tmp" echo End �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/redir3.right�����������������������������������������������0000644�0000000�0000000�00000000647�12263563520�021245� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������hush: can't open '/does/not/exist': No such file or directory One:1 hush: can't open '/cant/be/created': No such file or directory One:1 Ok hush: can't open '/cant/be/created': No such file or directory Zero:0 hush: can't open '/cant/be/created': No such file or directory One:1 hush: can't open '/cant/be/created': No such file or directory One:1 hush: can't open '/cant/be/created': No such file or directory Zero:0 Done �����������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/source2.tests����������������������������������������������0000755�0000000�0000000�00000000267�12263563520�021465� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo 'echo "0:$0 1:$1 2:$2"' >sourced1 set -- 1 2 3 "$THIS_SH" -c '. ./sourced1' arg0 arg1 arg2 echo Ok1:$? "$THIS_SH" -c '. ./sourced1 q w e' arg0 arg1 arg2 echo Ok2:$? rm sourced1 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/return1.tests����������������������������������������������0000755�0000000�0000000�00000000162�12263563520�021475� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo "true && return; echo Should not be printed" >return_sourced . ./return_sourced rm return_sourced echo Ok:$? ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/func3.tests������������������������������������������������0000755�0000000�0000000�00000000220�12263563520�021106� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������f() { false; return; echo BAD; }; { f; echo One:$?; }; echo Zero:$? f() { false; return; }; f; echo One:$? f() { return 5; }; f; echo Five:$? ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/env_and_func.right�����������������������������������������0000644�0000000�0000000�00000000020�12263563520�022463� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������var=val var=old ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/for_with_bslashes.right������������������������������������0000644�0000000�0000000�00000000047�12263563520�023554� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a b\c b\\c b"c b'c b$c b`true`c Zero:0 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/opts1.tests������������������������������������������������0000755�0000000�0000000�00000000133�12263563520�021141� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" $0 -10qwertyuiop fi echo "Param1: >$1<" echo Done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/pipefail.right���������������������������������������������0000644�0000000�0000000�00000000442�12263563520�021637� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Default: true | true: 0 1 true | false: 1 0 false | true: 0 1 exit 2 | exit 3 | exit 4: 4 0 Pipefail on: true | true: 0 1 true | false: 1 0 false | true: 1 0 exit 2 | exit 3 | exit 4: 4 0 Pipefail off: true | true: 0 1 true | false: 1 0 false | true: 0 1 exit 2 | exit 3 | exit 4: 4 0 Done ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/exit1.tests������������������������������������������������0000755�0000000�0000000�00000000132�12263563520�021124� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������trap "echo Not shown" EXIT (exit) # must be silent trap "echo Once; exit" EXIT { exit; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/heredoc3.tests���������������������������������������������0000755�0000000�0000000�00000000163�12263563520�021572� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������f=1 cat <<- EOF-f"" exit EOF-f " echo $f echo `echo Hello World` moo EOF-f EOF-f f EOF-f EOF-f echo Ok �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/while_in_subshell.tests������������������������������������0000755�0000000�0000000�00000000050�12263563520�023570� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������(while true; do exit; done) echo OK: $? ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/assignment2.tests������������������������������������������0000755�0000000�0000000�00000000163�12263563520�022330� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This must not be interpreted as an assignment a''=b true echo $? # (buglet: $? should be 127. it is currently 1) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/case1.tests������������������������������������������������0000755�0000000�0000000�00000002303�12263563520�021070� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������case w in a) echo SKIP;; w) echo OK_1;; w) echo WRONG;; esac case w in a) echo SKIP;; w)echo OK_1 ;; w) echo WRONG ;; esac t=w case $t in a) echo SKIP;; w) echo OK_21;; w) echo WRONG;; esac; case "$t" in a) echo SKIP;; w) echo OK_22;; w) echo WRONG;; esac; case w in a) echo SKIP;; $t) echo OK_23;; "$t") echo WRONG;; esac; case '' in a) echo SKIP;; w) echo WRONG;; *) echo OK_31;; esac; case '' in a) echo SKIP;; '') echo OK_32;; *) echo WRONG;; esac; case `echo w` in a) echo SKIP;; w) echo OK_41;; w) echo WRONG;; esac; case "`echo w`" in a) echo SKIP;; w) echo OK_42;; w) echo WRONG;; esac; case `echo w w` in a) echo SKIP;; w) echo WRONG;; 'w w') echo OK_43;; esac; case `echo w w` in a) echo SKIP;; w) echo WRONG;; w*) echo OK_44;; esac; case w in `echo w`) echo OK_51;; `echo WRONG >&2`w) echo WRONG;; esac; case w in `echo OK_52 >&2`) echo SKIP;; `echo`w) echo OK_53;; esac; # parsing cases in subshells can easily get messy case m in m) echo OK_sub1;; esac case m in (m) echo OK_sub2;; esac (case m in m) echo OK_sub3;; esac) (case m in (m) echo OK_sub4;; esac) ( case m in m) echo OK_sub5;; esac ) ( case m in (m) echo OK_sub6;; esac ) (case esac in "esac") echo OK_esac1;; esac) echo Done �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/func5.right������������������������������������������������0000644�0000000�0000000�00000000006�12263563520�021062� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 2 3 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/export.tests�����������������������������������������������0000755�0000000�0000000�00000000207�12263563520�021416� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������export aaa1="'''" export aaa2="" export aaa3="'''abc" export aaa4="def'''" export aaa5="'''abc'''def'''" export | grep aaa.= echo Done �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/redir4.right�����������������������������������������������0000644�0000000�0000000�00000000252�12263563520�021236� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������shell_test \shell_test \shell_test \shell_test Here1 Ok1 Here2 Ok2 Here3 Ok3 Here4 Ok4 Now with variable refs shell_test_1 \shell_test_1 \shell_test_1 \shell_test_1 Done ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/for_with_keywords.tests������������������������������������0000755�0000000�0000000�00000000066�12263563520�023650� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������for if in do done then; do echo $if; done echo OK: $? ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/pid.right��������������������������������������������������0000644�0000000�0000000�00000000002�12263563520�020612� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/redir6.tests�����������������������������������������������0000755�0000000�0000000�00000000220�12263563520�021263� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo "Testing multiple redirections to same fd" # bug was making us lose fd #1 after this: echo Hello >/dev/null 1>&2 echo Done1 echo Done2 >&2 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/break2.tests�����������������������������������������������0000755�0000000�0000000�00000000150�12263563520�021240� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������while true; do echo A while true; do echo AA; break 2; echo BB; done echo B done echo OK:$? ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/source1.tests����������������������������������������������0000755�0000000�0000000�00000000176�12263563520�021463� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo 'echo ${^} echo line2' >sourced1 . ./sourced1 echo Ok1:$? echo "echo '" >sourced1 . ./sourced1 echo Ok2:$? rm sourced1 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/source1.right����������������������������������������������0000644�0000000�0000000�00000000136�12263563520�021427� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������hush: syntax error: unterminated ${name} line2 Ok1:0 hush: syntax error: unterminated ' Ok2:1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/while2.right�����������������������������������������������0000644�0000000�0000000�00000000013�12263563520�021232� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Hello OK:0 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/break4.tests�����������������������������������������������0000755�0000000�0000000�00000000305�12263563520�021244� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������cond=true while $cond; do echo A if test "$cond" = true; then cond='echo TRUE' else cond=false fi while true; do echo AA; continue 2; echo BB; done echo B done echo OK:$? ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/func_local2.tests������������������������������������������0000755�0000000�0000000�00000000176�12263563520�022271� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x=1 f() { echo $x; local x=$((x+1)); echo $x; } g() { f; echo $x; f; local x=$((x+1)); f; echo $x; f; } f g echo $x echo Done ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/source2.right����������������������������������������������0000644�0000000�0000000�00000000060�12263563520�021424� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0:arg0 1:arg1 2:arg2 Ok1:0 0:arg0 1:q 2:w Ok2:0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/export-n.right���������������������������������������������0000644�0000000�0000000�00000000170�12263563520�021620� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������export aaa1="'''" export aaa2='' export aaa3="'''"'abc' export aaa8='8' aaa9=9 aaa10=10 Nothing: Nothing: Nothing: Done ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/return1.right����������������������������������������������0000644�0000000�0000000�00000000005�12263563520�021441� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Ok:0 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/empty_for.right��������������������������������������������0000644�0000000�0000000�00000000006�12263563520�022046� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������OK: 0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/while3.right�����������������������������������������������0000644�0000000�0000000�00000000005�12263563520�021234� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������OK:0 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/and-or.tests�����������������������������������������������0000755�0000000�0000000�00000001304�12263563520�021254� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������false || echo a1 false && echo a2 true || echo a3 true && echo a4 false || echo b1 || echo b2 false || echo b3 && echo b4 false && echo b5 || echo b6 false && echo b7 && echo b8 true || echo c1 || echo c2 true || echo c3 && echo c4 true && echo c5 || echo c6 true && echo c7 && echo c8 false || false || echo ff1 false || false && echo ff2 false && false || echo ff3 false && false && echo ff4 false || true || echo ft1 false || true && echo ft2 false && true || echo ft3 false && true && echo ft4 true || false || echo tf1 true || false && echo tf2 true && false || echo tf3 true && false && echo tf4 true || true || echo tt1 true || true && echo tt2 true && true || echo tt3 true && true && echo tt4 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/sigint1.tests����������������������������������������������0000755�0000000�0000000�00000003176�12263563520�021463� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# What should happen if non-interactive shell gets SIGINT? (sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) & # We create a child which exits with 0 even on SIGINT # (The complex command is necessary only if SIGINT is generated by ^C, # in this testcase even bare "sleep 2" would do because # in the testcase we don't send SIGINT *to the child*...) $THIS_SH -c 'trap "exit 0" SIGINT; sleep 2' # In one second, we (main shell) get SIGINT here. # The question is whether we should, or should not, exit. # bash will not stop here. It will execute next command(s). # The rationale for this is described here: # http://www.cons.org/cracauer/sigint.html # # Basically, bash will not exit on SIGINT immediately if it waits # for a child. It will wait for the child to exit. # If child exits NOT by dying on SIGINT, then bash will not exit. # # The idea is that the following script: # | emacs file.txt # | more cmds # User may use ^C to interrupt editor's ops like search. But then # emacs exits normally. User expects that script doesn't stop. # # This is a nice idea, but detecting "did process really exit # with SIGINT?" is racy. Consider: # | bash -c 'while true; do /bin/true; done' # When ^C is pressed while bash waits for /bin/true to exit, # it may happen that /bin/true exits with exitcode 0 before # ^C is delivered to it as SIGINT. bash will see SIGINT, then # it will see that child exited with 0, and bash will NOT EXIT. # Therefore we do not implement bash behavior. # I'd say that emacs need to put itself into a separate pgrp # to isolate shell from getting stray SIGINTs from ^C. echo Next command after SIGINT was executed ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/empty_args.right�������������������������������������������0000644�0000000�0000000�00000000146�12263563520�022221� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Null 0th arg: hush: can't execute '': No such file or directory 127 Null 1st arg: 0 Null arg in exec: ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/compound.right���������������������������������������������0000644�0000000�0000000�00000000064�12263563520�021672� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������new group 0 1 2 3 4 5 6 new group new group 0 1 2 3 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/colon.right������������������������������������������������0000644�0000000�0000000�00000000010�12263563520�021147� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0 OK: 0 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/compound.tests���������������������������������������������0000755�0000000�0000000�00000000536�12263563520�021726� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo new group echo 0; { :; } echo 1; { : ;} echo 2; ({ :; }) echo 3; ({ : ;}) echo 4; ( : ) echo 5; ( :; ) echo 6; ( : ;) # not sure if POSIX requires these, but bash accepts them ... #echo 7; {( : )} #echo 8; {( :; )} #echo 9; {( : ;)} echo new group #echo 0; {(:);} echo new group echo 0; (:) echo 1; (:;) echo 2; (:); echo 3; (:;); ������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/syntax_err_negate.tests������������������������������������0000755�0000000�0000000�00000000042�12263563520�023613� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo bash 3.2 fails this ! ! true ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/empty_for2.right�������������������������������������������0000644�0000000�0000000�00000000044�12263563520�022132� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PARAM:abc PARAM:d e PARAM:123 OK: 0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/continue3.tests��������������������������������������������0000755�0000000�0000000�00000000201�12263563520�021776� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Test that "continue" does affect exitcode (sets to 0) e='' while echo $?; test $e && exit; true; do e=1; false; continue; done �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/heredoc_backslash1.tests�����������������������������������0000755�0000000�0000000�00000002550�12263563520�023605� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Test for correct handling of backslashes. # Note that some lines in each heredoc start with a tab. a=qwerty echo Quoted heredoc: cat <<"EOF1" a\ b a\\ b 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?- -$a-\t-\\-\"-\'-\`-\--\z-\*-\?- 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'` 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-') c\ EOF1 echo echo Unquoted heredoc: cat <<EOF2 a\ b a\\ b 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?- -$a-\t-\\-\"-\'-\`-\--\z-\*-\?- 123456 `echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'` 123456 $(echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-') c\ EOF2 EOF2 echo echo Quoted -heredoc: cat <<-"EOF3" a\ b a\\ b 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?- -$a-\t-\\-\"-\'-\`-\--\z-\*-\?- 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'` 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-') c\ EOF3 # In -heredoc case the marker is detected even if it is indented. echo echo Unquoted -heredoc: cat <<-EOF4 a\ b a\\ b 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?- -$a-\t-\\-\"-\'-\`-\--\z-\*-\?- 123456 `echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'` 123456 $(echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-') c\ EOF4 EOF4 # The marker is not detected if preceding line ends in backslash. # TODO: marker should be detected even if it is split by line continuation: # EOF\ # 4 # but currently hush doesn't do it. (Tab before "4" is not allowed, though.) echo echo "Done: $?" ��������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/heredoc1.tests���������������������������������������������0000755�0000000�0000000�00000000104�12263563520�021563� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������cat <<000; cat <<www; cat <<eee 000 qwe asd www 123 456 eee echo Ok ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/break3.right�����������������������������������������������0000644�0000000�0000000�00000000007�12263563520�021212� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������A OK:0 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/func_args1.tests�������������������������������������������0000755�0000000�0000000�00000000240�12263563520�022122� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# UNFIXED BUG f() { echo "'f $1 $2 $3' called"; } set -- a b c echo "params: $1 $2 $3" f 1 2 3 echo "params: $1 $2 $3" true | f 1 2 3 echo "params: $1 $2 $3" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/export-n.tests���������������������������������������������0000755�0000000�0000000�00000001227�12263563520�021654� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������export aaa1="'''" export aaa2="" export aaa3="'''abc" export | grep aaa.= export -n aaa1 unset aaa2; export -n aaa2="ghi" export -n aaa3="klm" export | grep aaa.= export aaa4=4 aaa5=5 export -n aaa4=4n export -n aaa5 export | grep aaa.= export aaa5=5 aaa6=6 aaa7=7 aaa8=8 export -n aaa5 aaa6=6n aaa7 export | grep aaa.= aaa9=9 export -n aaa9 set | grep ^aaa9= export aaa10=10 export -n aaa10 set | grep ^aaa10= export EXPORTED=qwe export -nnnnnn nnnnnn; echo "Nothing:"; env | grep nnnnnn export -n EXPORTED=123; echo "Nothing:"; env | grep ^EXPORTED export EXPORTED=qwe export -n EXPORTED; EXPORTED=123; echo "Nothing:"; env | grep ^EXPORTED echo Done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/func2.tests������������������������������������������������0000755�0000000�0000000�00000000174�12263563520�021115� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������i=0 while test $i != 2; do f() { echo First $i; } f f() { echo Second $i; } f : $((i++)) done echo Done ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/exit1.right������������������������������������������������0000644�0000000�0000000�00000000005�12263563520�021073� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Once ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/assignment3.tests������������������������������������������0000755�0000000�0000000�00000000125�12263563520�022327� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This must be interpreted as assignments a=1 b\ =2 c=3 echo Done:$? echo abc=$a$b$c �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/func_local1.tests������������������������������������������0000755�0000000�0000000�00000000107�12263563520�022262� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������export z=z f() { local z=a; env | grep ^z; } f env | grep ^z echo Done ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/colon.tests������������������������������������������������0000755�0000000�0000000�00000000065�12263563520�021211� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������false : echo $? (while :; do exit; done) echo OK: $? ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/empty_for2.tests�������������������������������������������0000755�0000000�0000000�00000000157�12263563520�022167� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" $0 abc "d e" 123 fi false for v; do echo "PARAM:$v"; done echo OK: $? �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/heredoc3.right���������������������������������������������0000644�0000000�0000000�00000000116�12263563520�021540� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������exit EOF-f " echo $f echo `echo Hello World` moo EOF-f EOF-f f EOF-f Ok ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/func4.tests������������������������������������������������0000755�0000000�0000000�00000000106�12263563520�021112� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������func() { eval "echo \"\${val_${1}}\"" } val_x=24 (func x) echo Done ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/continue1.right��������������������������������������������0000644�0000000�0000000�00000000040�12263563520�021745� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������A:a A:b A:c OK1 A:a A:b A:c OK2 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/redir3.tests�����������������������������������������������0000755�0000000�0000000�00000000413�12263563520�021264� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo Error >/does/not/exist; echo One:$? t=BAD t=Ok >>/cant/be/created; echo One:$? echo $t ! >/cant/be/created; echo Zero:$? exec >/cant/be/created; echo One:$? exec /bin/true >/cant/be/created; echo One:$? ! exec /bin/true >/cant/be/created; echo Zero:$? echo Done �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/redir6.right�����������������������������������������������0000644�0000000�0000000�00000000073�12263563520�021241� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Testing multiple redirections to same fd Hello Done1 Done2 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/assignment4.right������������������������������������������0000644�0000000�0000000�00000000007�12263563520�022277� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Done:0 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/break1.tests�����������������������������������������������0000755�0000000�0000000�00000000066�12263563520�021245� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������while true; do echo A; break; echo B; done echo OK:$? ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/while1.tests�����������������������������������������������0000755�0000000�0000000�00000000060�12263563520�021263� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������while false; do echo NOT SHOWN; done echo OK:$? ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/break1.right�����������������������������������������������0000644�0000000�0000000�00000000007�12263563520�021210� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������A OK:0 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/for_with_bslashes.tests������������������������������������0000755�0000000�0000000�00000000404�12263563520�023601� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# UNFIXED BUG. # commented-out words contain ^C character. # It's a SPECIAL_VAR_SYMBOL, for now hush does not escape it. # When it is fixed, update this test. for a in 'a' 'b\c' 'b\\c' 'b"c' "b'c" 'b$c' 'b`true`c' ### 'b#c' do echo $a done echo Zero:$? ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/case1.right������������������������������������������������0000644�0000000�0000000�00000000220�12263563520�021034� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������OK_1 OK_1 OK_21 OK_22 OK_23 OK_31 OK_32 OK_41 OK_42 OK_43 OK_44 OK_51 OK_52 OK_53 OK_sub1 OK_sub2 OK_sub3 OK_sub4 OK_sub5 OK_sub6 OK_esac1 Done ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/empty_args.tests�������������������������������������������0000755�0000000�0000000�00000000226�12263563520�022250� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo Null 0th arg: "" echo $? echo Null 1st arg: # printf without args would print usage info printf "" echo $? echo Null arg in exec: exec printf "" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/while1.right�����������������������������������������������0000644�0000000�0000000�00000000005�12263563520�021232� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������OK:0 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/continue3.right��������������������������������������������0000644�0000000�0000000�00000000004�12263563520�021747� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0 0 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/func3.right������������������������������������������������0000644�0000000�0000000�00000000032�12263563520�021057� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������One:1 Zero:0 One:1 Five:5 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/redir2.right�����������������������������������������������0000644�0000000�0000000�00000000003�12263563520�021226� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Ok �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/shift.right������������������������������������������������0000644�0000000�0000000�00000000154�12263563520�021163� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������./shift.tests abc d e ./shift.tests d e 123 ./shift.tests d e 123 ./shift.tests ./shift.tests ./shift.tests ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/until1.right�����������������������������������������������0000644�0000000�0000000�00000000011�12263563520�021252� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 1 Ok:0 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/echo_write_error.tests�������������������������������������0000755�0000000�0000000�00000000127�12263563520�023437� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������trap "" PIPE { sleep 1 echo Cant write this - get EPIPE echo Ok: $? >&2 } | { true; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/func1.right������������������������������������������������0000644�0000000�0000000�00000000127�12263563520�021062� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Hello Zero: 0 One: 1 Param1: World Zero: 0 Param1: Restored Multi line function One: 1 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/continue1.tests��������������������������������������������0000755�0000000�0000000�00000000164�12263563520�022004� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������for v in a b c; do echo A:$v; continue 666; done echo OK1 for v in a b c; do echo A:$v; continue 666; done echo OK2 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/func_args1.right�������������������������������������������0000644�0000000�0000000�00000000114�12263563520�022072� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������params: a b c 'f 1 2 3' called params: a b c 'f 1 2 3' called params: a b c ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/nommu1.tests�����������������������������������������������0000755�0000000�0000000�00000000151�12263563520�021307� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������(echo \ Ok) ( (echo \ Ok) ) ( ( (echo \ Ok) ) ) (echo \Ok) ( (echo \Ok) ) ( ( (echo \Ok) ) ) echo Done �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/continue2.right��������������������������������������������0000644�0000000�0000000�00000000005�12263563520�021747� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Ok:1 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/assignment3.right������������������������������������������0000644�0000000�0000000�00000000017�12263563520�022277� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Done:0 abc=123 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/env_and_func.tests�����������������������������������������0000755�0000000�0000000�00000000073�12263563520�022523� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������var=old f() { echo "var=$var"; } var=val f echo "var=$var" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/read.right�������������������������������������������������0000644�0000000�0000000�00000000064�12263563520�020761� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������read cat echo "REPLY=$REPLY" REPLY=exec <read.tests ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/read.tests�������������������������������������������������0000755�0000000�0000000�00000000056�12263563520�021012� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������exec <read.tests read cat echo "REPLY=$REPLY" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/func2.right������������������������������������������������0000644�0000000�0000000�00000000047�12263563520�021064� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������First 0 Second 0 First 1 Second 1 Done �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/nommu1.right�����������������������������������������������0000644�0000000�0000000�00000000027�12263563520�021261� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Ok Ok Ok Ok Ok Ok Done ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/syntax_err.tests�������������������������������������������0000755�0000000�0000000�00000000057�12263563520�022276� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo shown echo test `echo 'aa` echo not shown ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/func1.tests������������������������������������������������0000755�0000000�0000000�00000000336�12263563520�021114� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������f() { echo Hello; } g () { echo One: $# Param1: $1; } h ( ) { echo -n 'Multi ' && echo -n 'line ' echo function false } f echo Zero: $? set -- Restored { g World; } echo Zero: $? Param1: $1 ( h ) echo One: $? ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/func5.tests������������������������������������������������0000755�0000000�0000000�00000000205�12263563520�021113� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������f() { echo $1; } f 1 # hush fails on this syntax, but i've never seen anyone use it ... #f() ( echo $1; ) f 2 #f() ( echo $1 ) f 3 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/syntax_err_negate.right������������������������������������0000644�0000000�0000000�00000000064�12263563520�023567� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������bash 3.2 fails this hush: syntax error: ! ! command ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/func4.right������������������������������������������������0000644�0000000�0000000�00000000010�12263563520�021054� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������24 Done ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/sig_exitcode.tests�����������������������������������������0000755�0000000�0000000�00000000141�12263563520�022540� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������exec 2>&1 $THIS_SH -c 'kill -9 $$' echo 137:$? ! $THIS_SH -c 'kill -9 $$' echo 0:$? echo Done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/empty_for.tests��������������������������������������������0000755�0000000�0000000�00000000062�12263563520�022100� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������false for a in; do echo "HELLO"; done echo OK: $? ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/heredoc2.tests���������������������������������������������0000755�0000000�0000000�00000000161�12263563520�021567� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������f=1 cat <<- EOF-f exit EOF-f " echo $f echo `echo Hello World` moo EOF-f EOF-f f EOF-f EOF-f echo Ok ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/nommu2.tests�����������������������������������������������0000755�0000000�0000000�00000000334�12263563520�021313� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo Not shown | if true; then echo $(echo Ok); fi echo Not shown | if true; then echo `echo Ok`; fi echo Not shown | ( if true; then echo $(echo Ok); fi ) echo Not shown | ( if true; then echo `echo Ok`; fi ) echo Done ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/echo_write_error.right�������������������������������������0000644�0000000�0000000�00000000045�12263563520�023406� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������hush: write error: Broken pipe Ok: 1 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/opts1.right������������������������������������������������0000644�0000000�0000000�00000000035�12263563520�021112� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Param1: >-10qwertyuiop< Done ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/redir5.right�����������������������������������������������0000644�0000000�0000000�00000000125�12263563520�021236� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Backgrounded pipes shall have their stdin redirected to /dev/null Zero:0 Zero:0 Done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/pipefail.tests���������������������������������������������0000755�0000000�0000000�00000001777�12263563520�021703� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo Default: echo "true | true:" true | true; echo $? ! true | true; echo $? echo "true | false:" true | false; echo $? ! true | false; echo $? echo "false | true:" false | true; echo $? ! false | true; echo $? echo "exit 2 | exit 3 | exit 4:" exit 2 | exit 3 | exit 4; echo $? ! exit 2 | exit 3 | exit 4; echo $? echo Pipefail on: set -o pipefail echo "true | true:" true | true; echo $? ! true | true; echo $? echo "true | false:" true | false; echo $? ! true | false; echo $? echo "false | true:" false | true; echo $? ! false | true; echo $? echo "exit 2 | exit 3 | exit 4:" exit 2 | exit 3 | exit 4; echo $? ! exit 2 | exit 3 | exit 4; echo $? echo Pipefail off: set +o pipefail echo "true | true:" true | true; echo $? ! true | true; echo $? echo "true | false:" true | false; echo $? ! true | false; echo $? echo "false | true:" false | true; echo $? ! false | true; echo $? echo "exit 2 | exit 3 | exit 4:" exit 2 | exit 3 | exit 4; echo $? ! exit 2 | exit 3 | exit 4; echo $? echo Done �busybox-1.22.1/shell/hush_test/hush-misc/func_local1.right������������������������������������������0000644�0000000�0000000�00000000015�12263563520�022230� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������z=a z=z Done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/continue2.tests��������������������������������������������0000755�0000000�0000000�00000000110�12263563520�021774� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������e='' (while test $e && exit 1; true; do e=1; continue; done) echo Ok:$? ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/redir5.tests�����������������������������������������������0000755�0000000�0000000�00000000544�12263563520�021273� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo "Backgrounded pipes shall have their stdin redirected to /dev/null" # 1. bash does not redirect stdin to /dev/null if it is interactive. # hush does it always (this is allowed by standards). # 2. Failure will result in this script hanging cat & wait; echo Zero:$? # This does not work for bash! bash bug? cat | cat & wait; echo Zero:$? echo Done ������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/builtin1.tests���������������������������������������������0000755�0000000�0000000�00000000225�12263563520�021624� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# builtins, unlike keywords like "while", can be constructed # with substitutions VARIABLE=export $VARIABLE VARIABLE env | grep ^VARIABLE echo OK:$? ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/redir1.right�����������������������������������������������0000644�0000000�0000000�00000000300�12263563520�021225� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Test 0: var:ok File created:ok Test 1: var:ok File created:ok Test 2: var:ok File created:ok Test 3: var:ok File created:ok Test 4: var:ok File created:ok Test 5: var:ok File created:ok ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/nommu2.right�����������������������������������������������0000644�0000000�0000000�00000000021�12263563520�021254� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Ok Ok Ok Ok Done ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/break5.tests�����������������������������������������������0000755�0000000�0000000�00000000224�12263563520�021245� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������while true; do echo A; { echo B; break; echo C; }; echo D; done echo $? for v in a b c; do echo A:$v; (echo B; break; echo C); echo D; done echo $? ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/syntax_err.right�������������������������������������������0000644�0000000�0000000�00000000070�12263563520�022241� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������shown hush: syntax error: unterminated ' test not shown ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/if_false_exitcode.right������������������������������������0000644�0000000�0000000�00000000005�12263563520�023475� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Ok:0 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/break5.right�����������������������������������������������0000644�0000000�0000000�00000000040�12263563520�021211� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������A B 0 A:a B D A:b B D A:c B D 0 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/while4.right�����������������������������������������������0000644�0000000�0000000�00000000005�12263563520�021235� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Ok:0 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/break2.right�����������������������������������������������0000644�0000000�0000000�00000000012�12263563520�021205� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������A AA OK:0 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/heredoc_backslash1.right�����������������������������������0000644�0000000�0000000�00000001445�12263563520�023557� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Quoted heredoc: a\ b a\\ b 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?- -$a-\t-\\-\"-\'-\`-\--\z-\*-\?- 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'` 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-') c\ Unquoted heredoc: a b a\ b 123456 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?- -qwerty-\t-\-\"-\'-`-\--\z-\*-\?- 123456 v-$a-\t-\-\"-\x-`-\--\z-\*-\?- 123456 v-$a-\t-\\-\"-\x-\`-\--\z-\*-\?- cEOF2 Quoted -heredoc: a\ b a\\ b 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?- -$a-\t-\\-\"-\'-\`-\--\z-\*-\?- 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'` 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-') c\ Unquoted -heredoc: a b a\ b 123456 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?- -qwerty-\t-\-\"-\'-`-\--\z-\*-\?- 123456 v-$a-\t-\-\"-\x-`-\--\z-\*-\?- 123456 v-$a-\t-\\-\"-\x-\`-\--\z-\*-\?- cEOF4 Done: 0 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/heredoc2.right���������������������������������������������0000644�0000000�0000000�00000000106�12263563520�021536� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������exit EOF-f " echo 1 echo Hello World moo EOF-f EOF-f f EOF-f Ok ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/exec.right�������������������������������������������������0000644�0000000�0000000�00000000143�12263563520�020770� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pass fd out open pass fd out dup pass fd out close pass fd in open pass fd in dup pass fd in close �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/while2.tests�����������������������������������������������0000755�0000000�0000000�00000000074�12263563520�021271� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������while echo Hello; false; do echo NOT SHOWN; done echo OK:$? ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/sigint1.right����������������������������������������������0000644�0000000�0000000�00000000041�12263563520�021417� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Sending SIGINT to main shell PID �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/redir4.tests�����������������������������������������������0000755�0000000�0000000�00000002147�12263563520�021273� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rm *shell_test* 2>/dev/null >\shell_test echo *shell_test* rm *shell_test* >\\shell_test echo *shell_test* rm *shell_test* >"\shell_test" echo *shell_test* rm *shell_test* >"\\shell_test" echo *shell_test* rm *shell_test* cat <<\shell_test Here1 shell_test echo Ok1 cat <<\\shell_test Here2 \shell_test echo Ok2 cat <<"\shell_test" Here3 \shell_test echo Ok3 cat <<"\\shell_test" Here4 \shell_test echo Ok4 echo Now with variable refs i=1 >\shell_test_$i echo *shell_test* rm *shell_test* >\\shell_test_$i echo *shell_test* rm *shell_test* >"\shell_test_$i" echo *shell_test* rm *shell_test* >"\\shell_test_$i" echo *shell_test* rm *shell_test* echo Done;exit # UNFIXED BUG. bash apparently will expand $i even in terminating delimiter. # http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html # does not mandate this behavior. # This is not likely to be used much in real-world. cat <<\shell_test_$i Here1 shell_test_$i echo Ok1 cat <<\\shell_test_$i Here2 \shell_test_$i echo Ok2 cat <<"\shell_test_$i" Here3 \shell_test_$i echo Ok3 cat <<"\\shell_test_$i" Here4 \shell_test_$i echo Ok4 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/func_local2.right������������������������������������������0000644�0000000�0000000�00000000037�12263563520�022235� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 2 1 2 1 1 2 2 3 2 2 3 1 Done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/redir1.tests�����������������������������������������������0000755�0000000�0000000�00000001702�12263563520�021264� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rm shell_test_$$ 2>/dev/null var=bad >shell_test_$$ var=ok echo "Test 0: var:$var" test -f shell_test_$$ && echo "File created:ok" rm shell_test_$$ 2>/dev/null var=bad var=ok >shell_test_$$ echo "Test 1: var:$var" test -f shell_test_$$ && echo "File created:ok" rm shell_test_$$ 2>/dev/null var=ok true | var=bad >shell_test_$$ echo "Test 2: var:$var" test -f shell_test_$$ && echo "File created:ok" rm shell_test_$$ 2>/dev/null var=bad { var=ok >shell_test_$$; } echo "Test 3: var:$var" test -f shell_test_$$ && echo "File created:ok" rm shell_test_$$ 2>/dev/null var=ok { var=bad >shell_test_$$; } & # cant use usleep as it isnt standard in $PATH -- # we fail when testing busybox compiled solely as "hush" wait echo "Test 4: var:$var" test -f shell_test_$$ && echo "File created:ok" rm shell_test_$$ 2>/dev/null var=ok ( var=bad >shell_test_$$ ) echo "Test 5: var:$var" test -f shell_test_$$ && echo "File created:ok" rm shell_test_$$ 2>/dev/null ��������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/export.right�����������������������������������������������0000644�0000000�0000000�00000000172�12263563520�021367� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������export aaa1="'''" export aaa2='' export aaa3="'''"'abc' export aaa4='def'"'''" export aaa5="'''"'abc'"'''"'def'"'''" Done ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/sig_exitcode.right�����������������������������������������0000644�0000000�0000000�00000000033�12263563520�022510� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������KILL 137:137 KILL 0:0 Done �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/assignment4.tests������������������������������������������0000755�0000000�0000000�00000000154�12263563520�022332� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# There was a bug where we misinterpreted assignments after 'do': for i in 1; do eval b=; done echo Done:$? ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/pid.tests��������������������������������������������������0000755�0000000�0000000�00000000046�12263563520�020652� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test `(echo $$)` = `echo $$`; echo $? ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-misc/assignment1.right������������������������������������������0000644�0000000�0000000�00000000110�12263563520�022267� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if1:0 while1:0 until1:0 if2:0 while2:0 until2:0 if3:0 while3:0 until3:0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/���������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�017376� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/tick3.tests����������������������������������������������0000755�0000000�0000000�00000000614�12263563520�021501� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test "$CONFIG_FEATURE_FANCY_ECHO" = "y" || exit 77 TEST=Q # \` is special echo `echo '\'TEST\`echo ZZ\`BEST` # \$ and \\ are special echo `echo \\$TEST` echo `echo \$TEST` echo a`echo \\\\b`c # \" is not special if in unquoted `cmd` (passed verbatim WITH \), # but is special in quoted one echo `echo 11'-$a-\t-\\-\"-\`-\--\z-\*-\?-'22` "`echo 33'-$a-\t-\\-\"-\`-\--\z-\*-\?-'44`" echo done:$? ��������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/tick3.right����������������������������������������������0000644�0000000�0000000�00000000135�12263563520�021447� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������\TESTZZBEST $TEST Q a\bc 11-$a-\t-\-\"-`-\--\z-\*-\?-22 33-$a-\t-\-"-`-\--\z-\*-\?-44 done:0 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/tick4.tests����������������������������������������������0000755�0000000�0000000�00000000250�12263563520�021476� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo $(echo '(TEST)' BEST) echo $(echo 'TEST)' BEST) echo $(echo \(\(TEST\) BEST) echo $(echo \)) echo $(echo a"`echo "b"`"c ) echo $(echo a"`echo ")"`"c ) echo OK: $? ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/tick2.right����������������������������������������������0000644�0000000�0000000�00000000004�12263563520�021441� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������BAZ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/tick5.right����������������������������������������������0000644�0000000�0000000�00000000002�12263563520�021442� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/tick_huge.right������������������������������������������0000644�0000000�0000000�00000000114�12263563520�022371� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������546ed3f5c81c780d3ab86ada14824237 - 546ed3f5c81c780d3ab86ada14824237 - End ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/tick2.tests����������������������������������������������0000755�0000000�0000000�00000000101�12263563520�021467� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if false; then echo "FOO" tmp=`echo BAR >&2` fi echo BAZ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/tick.right�����������������������������������������������0000644�0000000�0000000�00000000004�12263563520�021357� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/tick5.tests����������������������������������������������0000755�0000000�0000000�00000000026�12263563520�021500� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������true; echo `false` $? ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/emptytick.right������������������������������������������0000644�0000000�0000000�00000000432�12263563520�022443� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0 0 hush: can't execute '': No such file or directory 127 hush: can't execute '': No such file or directory 127 0 0 0 0 hush: can't execute '': No such file or directory 127 hush: can't execute '': No such file or directory 127 0 0 hush: can't execute '': No such file or directory ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/falsetick.tests������������������������������������������0000755�0000000�0000000�00000001513�12263563520�022430� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Exitcode 0 (`` has no exitcode, but assignment has): true; a=``; echo $? false; a=``; echo $? true; a=$(); echo $? false; a=$(); echo $? # Exitcode 2 (`cmd` expansion sets exitcode after assignment set it to 0): true; a=`exit 2`; echo $? false; a=`exit 2`; echo $? true; a=$(exit 2); echo $? false; a=$(exit 2); echo $? # Exitcode 1 (redirect sets exitcode to 1 on error after them): true; a=`` >/does/not/exist; echo $? false; a=`` >/does/not/exist; echo $? true; a=$() >/does/not/exist; echo $? false; a=$() >/does/not/exist; echo $? true; a=`exit 2` >/does/not/exist; echo $? false; a=`exit 2` >/does/not/exist; echo $? true; a=$(exit 2) >/does/not/exist; echo $? false; a=$(exit 2) >/does/not/exist; echo $? # ...and assignment still happens despite redirect error: true; a=$(echo b) >/does/not/exist; echo $? echo "Done: a=$a" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/tick.tests�����������������������������������������������0000755�0000000�0000000�00000000073�12263563520�021415� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������true false; echo `echo $?` true { false; echo `echo $?`; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/tick4.right����������������������������������������������0000644�0000000�0000000�00000000064�12263563520�021451� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������(TEST) BEST TEST) BEST ((TEST) BEST ) abc a)c OK: 0 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/tick_huge.tests������������������������������������������0000755�0000000�0000000�00000000326�12263563520�022426� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This creates 120k file yes "123456789 123456789 123456789 123456789" | head -3000 >>"$0.tmp" echo "`cat $0.tmp`" | md5sum rm "$0.tmp" yes "123456789 123456789 123456789 123456789" | head -3000 | md5sum echo End ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/falsetick.right������������������������������������������0000644�0000000�0000000�00000001132�12263563520�022375� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0 0 0 0 2 2 2 2 hush: can't open '/does/not/exist': No such file or directory 1 hush: can't open '/does/not/exist': No such file or directory 1 hush: can't open '/does/not/exist': No such file or directory 1 hush: can't open '/does/not/exist': No such file or directory 1 hush: can't open '/does/not/exist': No such file or directory 1 hush: can't open '/does/not/exist': No such file or directory 1 hush: can't open '/does/not/exist': No such file or directory 1 hush: can't open '/does/not/exist': No such file or directory 1 hush: can't open '/does/not/exist': No such file or directory 1 Done: a=b ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-psubst/emptytick.tests������������������������������������������0000755�0000000�0000000�00000000452�12263563520�022475� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������true; ``; echo $? false; ``; echo $? true; `""`; echo $? false; `""`; echo $? true; ` `; echo $? false; ` `; echo $? true; $(); echo $? false; $(); echo $? true; $(""); echo $? false; $(""); echo $? true; $( ); echo $? false; $( ); echo $? exec ''; echo $? echo Not reached ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-arith/����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�017165� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-arith/arith.right�����������������������������������������������0000644�0000000�0000000�00000003561�12263563520�021336� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Format: 'expected actual' 163 163 4 4 16 16 8 8 2 2 4 4 2 2 2 2 1 1 0 0 0 0 0 0 1 1 1 1 2 2 -3 -3 -2 -2 1 1 0 0 2 2 131072 131072 29 29 33 33 49 49 1 1 1 1 0 0 0 0 1 1 1 1 1 1 2 2 3 3 1 1 58 58 2 2 60 60 1 1 256 256 16 16 62 62 4 4 29 29 5 5 unary plus, minus -4 -4 4 4 conditional expressions 1 1 32 32 32 32 1 1 1 1 32 32 check that parentheses in `cmd` are interpreted correctly 3 3 check that the unevaluated part of the ternary operator does not do evaluation or assignment 20 20 30 30 20 20 30 30 check precedence of assignment vs. conditional operator hush: arithmetic syntax error check precedence of assignment vs. conditional operator associativity of assignment-operator operator 6 6 6,5,3 6,5,3 octal, hex 263 263 255 255 40 40 hush: arithmetic syntax error hush: divide by zero hush: can't execute 'let': No such file or directory hush: arithmetic syntax error hush: can't execute 'let': No such file or directory abc def ghi hush: arithmetic syntax error 16 16 hush: arithmetic syntax error hush: malformed ?: operator hush: arithmetic syntax error 9 9 hush: arithmetic syntax error hush: arithmetic syntax error 9 9 9 9 9 9 7 7 7 4 4 32767 32767 32768 32768 131072 131072 2147483647 2147483647 1 1 4 4 4 4 5 5 5 5 4 4 3 3 3 3 4 4 4 4 hush: arithmetic syntax error hush: arithmetic syntax error hush: arithmetic syntax error hush: arithmetic syntax error hush: arithmetic syntax error 4 4 7 7 -7 -7 hush: arithmetic syntax error hush: arithmetic syntax error hush: arithmetic syntax error hush: arithmetic syntax error 6 6 3 3 7 7 4 4 0 0 3 3 7 7 2 2 -2 -2 1 1 hush: arithmetic syntax error hush: arithmetic syntax error hush: arithmetic syntax error hush: arithmetic syntax error hush: arithmetic syntax error 5 5 1 1 4 4 0 0 hush: arithmetic syntax error hush: arithmetic syntax error 8 12 hush: arithmetic syntax error 42 42 42 hush: can't execute 'a[b[c]d]=e': No such file or directory �����������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-arith/arith1.sub������������������������������������������������0000755�0000000�0000000�00000001152�12263563520�021070� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# test of redone post-increment and post-decrement code ( echo $(( 4-- )) ) ( echo $(( 4++ )) ) ( echo $(( 4 -- )) ) ( echo $(( 4 ++ )) ) #ash# (( array[0]++ )) #ash# echo ${array} #ash# (( array[0] ++ )) #ash# echo ${array} #ash# (( a++ )) #ash# echo $a #ash# (( a ++ )) #ash# echo $a a=2 echo 6 $(( a ++ + 4 )) echo 3 $a echo 7 $(( a+++4 )) echo 4 $a echo 0 $(( a---4 )) echo 3 $a echo 7 $(( a -- + 4 )) echo 2 $a echo -2 $(( a -- - 4 )) echo 1 $a #ash# (( ++ + 7 )) #ash# (( ++ )) ( echo $(( +++7 )) ) # bash 3.2 apparently thinks that ++ +7 is 7 #ash# echo $(( ++ + 7 )) #ash# (( -- )) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-arith/arith2.sub������������������������������������������������0000755�0000000�0000000�00000001701�12263563520�021071� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# bash 3.2 apparently thinks that ++7 is 7 etc ( echo $(( --7 )) ) ( echo $(( ++7 )) ) ( echo $(( -- 7 )) ) ( echo $(( ++ 7 )) ) #ash# ((++array[0] )) #ash# echo 1 $array #ash# (( ++ array[0] )) #ash# echo 2 $array #ash# (( ++a )) #ash# echo 1 $a #ash# (( ++ a )) #ash# echo 2 $a #ash# (( --a )) #ash# echo 1 $a #ash# (( -- a )) #ash# echo 0 $a a=0 echo 5 $(( 4 + ++a )) echo 1 $a # ash doesn't handle it right... #ash# echo 6 $(( 4+++a )) #ash# echo 2 $a a=2 # ash doesn't handle it right... #ash# echo 3 $(( 4---a )) #ash# echo 1 $a a=1 echo 4 $(( 4 - -- a )) echo 0 $a #ash# (( -- )) # bash 3.2 apparently thinks that ---7 is -7 #ash# echo $(( ---7 )) ( echo $(( -- - 7 )) ) #ash# (( ++ )) # bash 3.2: 7 #ash# echo 7 $(( ++7 )) ( echo $(( ++ + 7 )) ) # bash 3.2: -7 #ash# echo -7 $(( ++-7 )) # bash 3.2: -7 #ash# echo -7 $(( ++ - 7 )) # bash 3.2: 7 #ash# echo 7 $(( +--7 )) # bash 3.2: 7 #ash# echo 7 $(( -- + 7 )) ���������������������������������������������������������������busybox-1.22.1/shell/hush_test/hush-arith/arith.tests�����������������������������������������������0000755�0000000�0000000�00000013212�12263563520�021360� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ash# set +o posix #ash# declare -i iv jv echo "Format: 'expected actual'" iv=$(( 3 + 5 * 32 )) echo 163 $iv #ash# iv=iv+3 #ash# echo 166 $iv iv=2 jv=iv : $((jv *= 2)) ##hush## let "jv *= 2" echo 4 $jv jv=$(( $jv << 2 )) echo 16 $jv : $((jv=$jv / 2)) ##hush## let jv="$jv / 2" echo 8 $jv #ash# jv="jv >> 2" : $((jv=jv >> 2)) ##hush## let jv="jv >> 2" echo 2 $jv iv=$((iv+ $jv)) echo 4 $iv echo 2 $((iv -= jv)) echo 2 $iv echo 1 $(( iv == jv )) echo 0 $(( iv != $jv )) echo 0 $(( iv < jv )) echo 0 $(( $iv > $jv )) echo 1 $(( iv <= $jv )) echo 1 $(( $iv >= jv )) echo 2 $jv echo -3 $(( ~$jv )) echo -2 $(( ~1 )) echo 1 $(( ! 0 )) echo 0 $(( jv % 2 )) echo 2 $(( $iv % 4 )) echo 131072 $(( iv <<= 16 )) echo 29 $(( iv %= 33 )) echo 33 $(( 33 & 55 )) echo 49 $(( 33 | 17 )) echo 1 $(( iv && $jv )) echo 1 $(( $iv || jv )) echo 0 $(( iv && 0 )) echo 0 $(( iv & 0 )) echo 1 $(( iv && 1 )) echo 1 $(( iv & 1 )) echo 1 $(( $jv || 0 )) echo 2 $(( jv | 0 )) echo 3 $(( jv | 1 )) echo 1 $(( $jv || 1 )) : $((iv *= jv)) ##hush## let 'iv *= jv' echo 58 $iv echo 2 $jv : $((jv += $iv)) ##hush## let "jv += $iv" echo 60 $jv echo 1 $(( jv /= iv )) echo 256 $(( jv <<= 8 )) echo 16 $(( jv >>= 4 )) echo 62 $(( iv |= 4 )) echo 4 $(( iv &= 4 )) echo 29 $(( iv += (jv + 9))) echo 5 $(( (iv + 4) % 7 )) echo unary plus, minus echo -4 $(( +4 - 8 )) echo 4 $(( -4 + 8 )) echo conditional expressions echo 1 $(( 4<5 ? 1 : 32)) echo 32 $(( 4>5 ? 1 : 32)) echo 32 $(( 4>(2+3) ? 1 : 32)) echo 1 $(( 4<(2+3) ? 1 : 32)) echo 1 $(( (2+2)<(2+3) ? 1 : 32)) echo 32 $(( (2+2)>(2+3) ? 1 : 32)) echo 'check that parentheses in `cmd` are interpreted correctly' # \x28 is '(' echo 3 $(( ( `printf '(\x28 1'` + `echo 2\)\)` ) )) echo check that the unevaluated part of the ternary operator does not do evaluation or assignment x=i+=2 y=j+=2 #ash# declare -i i=1 j=1 i=1 j=1 echo 20 $((1 ? 20 : (x+=2))) #ash# echo $i,$x # ash mishandles this echo 30 $((0 ? (y+=2) : 30)) #ash# echo $j,$y # ash mishandles this x=i+=2 y=j+=2 #ash# declare -i i=1 j=1 i=1 j=1 echo 20 $((1 ? 20 : (x+=2))) #ash# echo $i,$x # ash mishandles this echo 30 $((0 ? (y+=2) : 30)) #ash# echo $i,$y # ash mishandles this echo check precedence of assignment vs. conditional operator # should be an error #ash# declare -i x=2 x=2 #ashnote# bash reports error but continues, ash aborts - using subshell to 'emulate' bash: ( y=$((1 ? 20 : x+=2)) ) echo check precedence of assignment vs. conditional operator #ash# declare -i x=2 x=2 # ash says "line NNN: syntax error: 0 ? x+=2 : 20" #ash# echo 20 $((0 ? x+=2 : 20)) echo associativity of assignment-operator operator #ash# declare -i i=1 j=2 k=3 i=1 j=2 k=3 echo 6 $((i += j += k)) echo 6,5,3 $i,$j,$k echo octal, hex echo 263 $(( 0x100 | 007 )) echo 255 $(( 0xff )) #ash# echo 255 $(( 16#ff )) #ash# echo 127 $(( 16#FF/2 )) #ash# echo 36 $(( 8#44 )) echo 40 $(( 8 ^ 32 )) #ash# # other bases #ash# echo 10 $(( 16#a )) #ash# echo 10 $(( 32#a )) #ash# echo 10 $(( 56#a )) #ash# echo 10 $(( 64#a )) #ash# #ash# echo 10 $(( 16#A )) #ash# echo 10 $(( 32#A )) #ash# echo 36 $(( 56#A )) #ash# echo 36 $(( 64#A )) #ash# #ash# echo 62 $(( 64#@ )) #ash# echo 63 $(( 64#_ )) #ash# # weird bases (error) #ash# echo $(( 3425#56 )) #ash# # missing number after base #ash# echo 0 $(( 2# )) # these should generate errors ( echo $(( 7 = 43 )) ) #ash# echo $(( 2#44 )) ( echo $(( 44 / 0 )) ) ( let 'jv += $iv' ) ( echo $(( jv += \$iv )) ) ( let 'rv = 7 + (43 * 6' ) #ash# # more errors #ash# declare -i i #ash# i=0#4 #ash# i=2#110#11 ((echo abc; echo def;); echo ghi) #ash# if (((4+4) + (4 + 7))); then #ash# echo ok #ash# fi #ash# (()) # make sure the null expression works OK #ash# a=(0 2 4 6) #ash# echo 6 $(( a[1] + a[2] )) #ash# echo 1 $(( (a[1] + a[2]) == a[3] )) #ash# (( (a[1] + a[2]) == a[3] )) ; echo 0 $? # test pushing and popping the expression stack unset A A="4 + " ( echo A $(( ( 4 + A ) + 4 )) ) A="3 + 5" echo 16 $(( ( 4 + A ) + 4 )) # badly-formed conditional expressions ( echo $(( 4 ? : $A )) ) ( echo $(( 1 ? 20 )) ) ( echo $(( 4 ? 20 : )) ) # precedence and short-circuit evaluation B=9 echo 9 $B # error ( echo $(( 0 && B=42 )); echo 9 $B ) # error ( echo $(( 1 || B=88 )); echo 9 $B ) # ash mistakenly evaluates B=... below #ash# echo 0 $(( 0 && (B=42) )) echo 9 $B #ash# echo 0 $(( (${$} - $$) && (B=42) )) echo 9 $B #ash# echo 1 $(( 1 || (B=88) )) echo 9 $B # until command with (( )) command x=7 echo 7 $x #ash# until (( x == 4 )) until test "$x" = 4 do echo $x x=4 done echo 4 $x # exponentiation echo 32767 $(( 2**15 - 1)) echo 32768 $(( 2**(16-1))) echo 131072 $(( 2**16*2 )) echo 2147483647 $(( 2**31-1)) echo 1 $(( 2**0 )) # {pre,post}-{inc,dec}rement and associated errors x=4 echo 4 $x echo 4 $(( x++ )) echo 5 $x echo 5 $(( x-- )) echo 4 $x echo 3 $(( --x )) echo 3 $x echo 4 $(( ++x )) echo 4 $x # bash 3.2 apparently thinks that ++7 is 7 #ash# echo 7 $(( ++7 )) ( echo $(( 7-- )) ) ( echo $(( --x=7 )) ) ( echo $(( ++x=7 )) ) ( echo $(( x++=7 )) ) ( echo $(( x--=7 )) ) echo 4 $x echo 7 $(( +7 )) echo -7 $(( -7 )) # bash 3.2 apparently thinks that ++7 is 7 #ash# echo $(( ++7 )) #ash# echo $(( --7 )) ${THIS_SH} ./arith1.sub ${THIS_SH} ./arith2.sub x=4 y=7 #ash# (( x=8 , y=12 )) x=8 y=12 echo $x $y #ash# # should be an error #ash# (( x=9 y=41 )) # These are errors unset b ( echo $((a b)) ) #ash# ((a b)) n=42 printf "%d\n" $n printf "%i\n" $n #ash# echo $(( 8#$(printf "%o\n" $n) )) printf "%u\n" $n #ash# echo $(( 16#$(printf "%x\n" $n) )) #ash# echo $(( 16#$(printf "%X\n" $n) )) # causes longjmp botches through bash-2.05b a[b[c]d]=e ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/random.c�����������������������������������������������������������������������0000644�0000000�0000000�00000002256�12263563520�014532� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * $RANDOM support. * * Copyright (C) 2009 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" #include "random.h" uint32_t FAST_FUNC next_random(random_t *rnd) { /* Galois LFSR parameter */ /* Taps at 32 31 29 1: */ enum { MASK = 0x8000000b }; /* Another example - taps at 32 31 30 10: */ /* MASK = 0x00400007 */ uint32_t t; if (UNINITED_RANDOM_T(rnd)) { /* Can use monotonic_ns() for better randomness but for now * it is not used anywhere else in busybox... so avoid bloat */ INIT_RANDOM_T(rnd, getpid(), monotonic_us()); } /* LCG has period of 2^32 and alternating lowest bit */ rnd->LCG = 1664525 * rnd->LCG + 1013904223; /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */ t = (rnd->galois_LFSR << 1); if (rnd->galois_LFSR < 0) /* if we just shifted 1 out of msb... */ t ^= MASK; rnd->galois_LFSR = t; /* Both are weak, combining them gives better randomness * and ~2^64 period. & 0x7fff is probably bash compat * for $RANDOM range. Combining with subtraction is * just for fun. + and ^ would work equally well. */ t = (t - rnd->LCG) & 0x7fff; return t; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/README�������������������������������������������������������������������������0000644�0000000�0000000�00000003764�12263563520�013773� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������http://www.opengroup.org/onlinepubs/9699919799/ Open Group Base Specifications Issue 7 http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap01.html Shell & Utilities It says that any of the standard utilities may be implemented as a regular shell built-in. It gives a list of utilities which are usually implemented that way (and some of them can only be implemented as built-ins, like "alias"): alias bg cd command false fc fg getopts jobs kill newgrp pwd read true umask unalias wait http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html Shell Command Language It says that shell must implement special built-ins. Special built-ins differ from regular ones by the fact that variable assignments done on special builtin are *PRESERVED*. That is, VAR=VAL special_builtin; echo $VAR should print VAL. (Another distinction is that an error in special built-in should abort the shell, but this is not such a critical difference, and moreover, at least bash's "set" does not follow this rule, which is even codified in autoconf configure logic now...) List of special builtins: . file : [argument...] break [n] continue [n] eval [argument...] exec [command [argument...]] exit [n] export name[=word]... export -p readonly name[=word]... readonly -p return [n] set [-abCefhmnuvx] [-o option] [argument...] set [+abCefhmnuvx] [+o option] [argument...] set -- [argument...] set -o set +o shift [n] times trap n [condition...] trap [action condition...] unset [-fv] name... In practice, no one uses this obscure feature - none of these builtins gives any special reasons to play such dirty tricks. However. This section also says that *function invocation* should act similar to special built-in. That is, variable assignments done on function invocation should be preserved after function invocation. This is significant: it is not unthinkable to want to run a function with some variables set to special values. But because of the above, it does not work: variable will "leak" out of the function. ������������busybox-1.22.1/shell/hush_leaktool.sh���������������������������������������������������������������0000755�0000000�0000000�00000001066�12263563520�016304� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # hush's stderr with leak debug enabled output=output freelist=`grep 'free 0x' "$output" | cut -d' ' -f2 | sort | uniq | xargs` grep -v free "$output" >"$output.leaked" i=8 list= for freed in $freelist; do list="$list -e $freed" test $((--i)) != 0 && continue echo Dropping $list grep -F -v $list <"$output.leaked" >"$output.temp" mv "$output.temp" "$output.leaked" i=8 list= done if test "$list"; then echo Dropping $list grep -F -v $list <"$output.leaked" >"$output.temp" mv "$output.temp" "$output.leaked" fi ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/Config.src���������������������������������������������������������������������0000644�0000000�0000000�00000010723�12263563520�015022� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # For a description of the syntax of this configuration file, # see scripts/kbuild/config-language.txt. # menu "Shells" INSERT choice prompt "Choose which shell is aliased to 'sh' name" default FEATURE_SH_IS_ASH help Choose which shell you want to be executed by 'sh' alias. The ash shell is the most bash compatible and full featured one. # note: cannot use "select ASH" here, it breaks "make allnoconfig" config FEATURE_SH_IS_ASH depends on ASH bool "ash" depends on !NOMMU config FEATURE_SH_IS_HUSH depends on HUSH bool "hush" config FEATURE_SH_IS_NONE bool "none" endchoice choice prompt "Choose which shell is aliased to 'bash' name" default FEATURE_BASH_IS_NONE help Choose which shell you want to be executed by 'bash' alias. The ash shell is the most bash compatible and full featured one. Note that selecting this option does not switch on any bash compatibility code. It merely makes it possible to install /bin/bash (sym)link and run scripts which start with #!/bin/bash line. Many systems use it in scripts which use bash-specific features, even simple ones like $RANDOM. Without this option, busybox can't be used for running them because it won't recongnize "bash" as a supported applet name. config FEATURE_BASH_IS_ASH depends on ASH bool "ash" depends on !NOMMU config FEATURE_BASH_IS_HUSH depends on HUSH bool "hush" config FEATURE_BASH_IS_NONE bool "none" endchoice config SH_MATH_SUPPORT bool "POSIX math support" default y depends on ASH || HUSH help Enable math support in the shell via $((...)) syntax. config SH_MATH_SUPPORT_64 bool "Extend POSIX math support to 64 bit" default y depends on SH_MATH_SUPPORT help Enable 64-bit math support in the shell. This will make the shell slightly larger, but will allow computation with very large numbers. This is not in POSIX, so do not rely on this in portable code. config FEATURE_SH_EXTRA_QUIET bool "Hide message on interactive shell startup" default y depends on HUSH || ASH help Remove the busybox introduction when starting a shell. config FEATURE_SH_STANDALONE bool "Standalone shell" default n depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS help This option causes busybox shells to use busybox applets in preference to executables in the PATH whenever possible. For example, entering the command 'ifconfig' into the shell would cause busybox to use the ifconfig busybox applet. Specifying the fully qualified executable name, such as '/sbin/ifconfig' will still execute the /sbin/ifconfig executable on the filesystem. This option is generally used when creating a statically linked version of busybox for use as a rescue shell, in the event that you screw up your system. This is implemented by re-execing /proc/self/exe (typically) with right parameters. Some selected applets ("NOFORK" applets) can even be executed without creating new process. Instead, busybox will call <applet>_main() internally. However, this causes problems in chroot jails without mounted /proc and with ps/top (command name can be shown as 'exe' for applets started this way). # untrue? # Note that this will *also* cause applets to take precedence # over shell builtins of the same name. So turning this on will # eliminate any performance gained by turning on the builtin "echo" # and "test" commands in ash. # untrue? # Note that when using this option, the shell will attempt to directly # run '/bin/busybox'. If you do not have the busybox binary sitting in # that exact location with that exact name, this option will not work at # all. config FEATURE_SH_NOFORK bool "Run 'nofork' applets directly" default n depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS help This option causes busybox shells to not execute typical fork/exec/wait sequence, but call <applet>_main directly, if possible. (Sometimes it is not possible: for example, this is not possible in pipes). This will be done only for some applets (those which are marked NOFORK in include/applets.h). This may significantly speed up some shell scripts. This feature is relatively new. Use with care. Report bugs to project mailing list. config FEATURE_SH_HISTFILESIZE bool "Use $HISTFILESIZE" default y depends on HUSH || ASH help This option makes busybox shells to use $HISTFILESIZE variable to set shell history size. Note that its max value is capped by "History size" setting in library tuning section. endmenu ���������������������������������������������busybox-1.22.1/shell/shell_common.h�����������������������������������������������������������������0000644�0000000�0000000�00000002256�12263563520�015736� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Adapted from ash applet code * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * Copyright (c) 1989, 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au> * was re-ported from NetBSD and debianized. * * Copyright (c) 2010 Denys Vlasenko * Split from ash.c * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #ifndef SHELL_COMMON_H #define SHELL_COMMON_H 1 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN extern const char defifsvar[] ALIGN1; /* "IFS= \t\n" */ #define defifs (defifsvar + 4) int FAST_FUNC is_well_formed_var_name(const char *s, char terminator); /* Builtins */ enum { BUILTIN_READ_SILENT = 1 << 0, BUILTIN_READ_RAW = 1 << 1, }; const char* FAST_FUNC shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), char **argv, const char *ifs, int read_flags, const char *opt_n, const char *opt_p, const char *opt_t, const char *opt_u ); int FAST_FUNC shell_builtin_ulimit(char **argv); POP_SAVED_FUNCTION_VISIBILITY #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/Kbuild.src���������������������������������������������������������������������0000644�0000000�0000000�00000000326�12263563520�015025� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Makefile for busybox # # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org> # # Licensed under GPLv2, see file LICENSE in this source tree. lib-y:= INSERT lib-$(CONFIG_SH_MATH_SUPPORT) += math.o ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_ptr_hack.c�����������������������������������������������������������������0000644�0000000�0000000�00000001716�12263563520�015700� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Copyright (C) 2008 by Denys Vlasenko <vda.linux@googlemail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ struct globals_misc; struct globals_memstack; struct globals_var; #ifndef GCC_COMBINE /* We cheat here. They are declared as const ptr in ash.c, * but here we make them live in R/W memory */ struct globals_misc *ash_ptr_to_globals_misc; struct globals_memstack *ash_ptr_to_globals_memstack; struct globals_var *ash_ptr_to_globals_var; #else /* gcc -combine will see through and complain */ /* Using alternative method which is more likely to break * on weird architectures, compilers, linkers and so on */ struct globals_misc *const ash_ptr_to_globals_misc __attribute__ ((section (".data"))); struct globals_memstack *const ash_ptr_to_globals_memstack __attribute__ ((section (".data"))); struct globals_var *const ash_ptr_to_globals_var __attribute__ ((section (".data"))); #endif ��������������������������������������������������busybox-1.22.1/shell/brace.txt����������������������������������������������������������������������0000644�0000000�0000000�00000005165�12263563520�014725� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Brace Expansion Brace expansion is a mechanism by which arbitrary strings may be gener- ated. This mechanism is similar to pathname expansion, but the file- names generated need not exist. Patterns to be brace expanded take the form of an optional preamble, followed by either a series of comma-sep- arated strings or a sequence expression between a pair of braces, fol- lowed by an optional postscript. The preamble is prefixed to each string contained within the braces, and the postscript is then appended to each resulting string, expanding left to right. Brace expansions may be nested. The results of each expanded string are not sorted; left to right order is preserved. For example, a{d,c,b}e expands into `ade ace abe'. A sequence expression takes the form {x..y}, where x and y are either integers or single characters. When integers are supplied, the expres- sion expands to each number between x and y, inclusive. When charac- ters are supplied, the expression expands to each character lexico- graphically between x and y, inclusive. Note that both x and y must be of the same type. Brace expansion is performed before any other expansions, and any char- acters special to other expansions are preserved in the result. It is strictly textual. Bash does not apply any syntactic interpretation to the context of the expansion or the text between the braces. A correctly-formed brace expansion must contain unquoted opening and closing braces, and at least one unquoted comma or a valid sequence expression. Any incorrectly formed brace expansion is left unchanged. A { or , may be quoted with a backslash to prevent its being considered part of a brace expression. To avoid conflicts with parameter expan- sion, the string ${ is not considered eligible for brace expansion. This construct is typically used as shorthand when the common prefix of the strings to be generated is longer than in the above example: mkdir /usr/local/src/bash/{old,new,dist,bugs} or chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}} Brace expansion introduces a slight incompatibility with historical versions of sh. sh does not treat opening or closing braces specially when they appear as part of a word, and preserves them in the output. Bash removes braces from words as a consequence of brace expansion. For example, a word entered to sh as file{1,2} appears identically in the output. The same word is output as file1 file2 after expansion by bash. If strict compatibility with sh is desired, start bash with the +B option or disable brace expansion with the +B option to the set com- mand �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�014731� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/�������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�016456� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/shift.tests��������������������������������������������������0000755�0000000�0000000�00000000267�12263563520�020665� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" $0 abc "d e" 123 fi echo $0 $1 $2 shift echo $0 $1 $2 shift 999 echo $0 $1 $2 shift 2 echo $0 $1 $2 shift 2 echo $0 $1 $2 shift echo $0 $1 $2 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/noeol3.tests�������������������������������������������������0000755�0000000�0000000�00000000052�12263563520�020737� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# last line has no EOL! echo "unterminated��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/var_expand_in_redir.tests������������������������������������0000755�0000000�0000000�00000000441�12263563520�023544� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" "$0" abc "d e" fi echo TEST1 >"$1.out" echo TEST2 >"$2.out" # bash says: "$@.out": ambiguous redirect # ash handles it as if it is '$*' - we do the same echo TEST3 >"$@.out" cat abc.out "d e.out" "abc d e.out" rm abc.out "d e.out" "abc d e.out" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/process_subst.right������������������������������������������0000644�0000000�0000000�00000000050�12263563520�022404� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������TESTzzBEST TEST$(echo zz)BEST TEST'BEST ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/starquoted.tests���������������������������������������������0000755�0000000�0000000�00000000324�12263563520�021735� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" "$0" 1 abc 'd e f' fi for a in "$*"; do echo ".$a."; done for a in "$@"; do echo ".$a."; done for a in "-$*-"; do echo ".$a."; done for a in "-$@-"; do echo ".$a."; done ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/shift.right��������������������������������������������������0000644�0000000�0000000�00000000154�12263563520�020630� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������./shift.tests abc d e ./shift.tests d e 123 ./shift.tests d e 123 ./shift.tests ./shift.tests ./shift.tests ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/read.right���������������������������������������������������0000644�0000000�0000000�00000000064�12263563520�020426� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������read cat echo "REPLY=$REPLY" REPLY=exec <read.tests ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/read.tests���������������������������������������������������0000755�0000000�0000000�00000000056�12263563520�020457� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������exec <read.tests read cat echo "REPLY=$REPLY" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/var_expand_in_assign.right�����������������������������������0000644�0000000�0000000�00000000054�12263563520�023673� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������. . .abc d e. .abc d e. .abc d e. .abc d e. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/starquoted.right���������������������������������������������0000644�0000000�0000000�00000000104�12263563520�021701� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.1 abc d e f. .1. .abc. .d e f. .-1 abc d e f-. .-1. .abc. .d e f-. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/syntax_err.tests���������������������������������������������0000755�0000000�0000000�00000000057�12263563520�021743� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo shown echo test `echo 'aa` echo not shown ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/process_subst.tests������������������������������������������0000755�0000000�0000000�00000000140�12263563520�022434� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo "TEST`echo zz;echo;echo`BEST" echo "TEST`echo '$(echo zz)'`BEST" echo "TEST`echo "'"`BEST" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/syntax_err.right���������������������������������������������0000644�0000000�0000000�00000000051�12263563520�021705� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������shown hush: syntax error: unterminated ' ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/var_expand_in_assign.tests�����������������������������������0000755�0000000�0000000�00000000225�12263563520�023723� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" "$0" abc "d e" fi space=' ' echo .$space. a=$* echo .$a. a=$@ echo .$a. a="$*" echo .$a. a="$@" echo .$a. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/noeol3.right�������������������������������������������������0000644�0000000�0000000�00000000043�12263563520�020707� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������hush: syntax error: unterminated " ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-bugs/var_expand_in_redir.right������������������������������������0000644�0000000�0000000�00000000022�12263563520�023507� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������TEST1 TEST2 TEST3 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/run-all���������������������������������������������������������������0000755�0000000�0000000�00000002465�12263563520�016236� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh test -x msh || { echo "No ./msh - creating a link to ../../busybox" ln -s ../../busybox msh } PATH="$PWD:$PATH" # for msh export PATH THIS_SH="$PWD/msh" export THIS_SH do_test() { test -d "$1" || return 0 # echo Running tests in directory "$1" ( cd "$1" || { echo "cannot cd $1!"; exit 1; } for x in run-*; do test -f "$x" || continue case "$x" in "$0"|run-minimal|run-gprof) ;; *.orig|*~) ;; #*) echo $x ; sh $x ;; *) sh "$x" >"../$1-$x.fail" 2>&1 && \ { echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail"; ;; esac done # Many bash run-XXX scripts just do this, # no point in duplication it all over the place for x in *.tests; do test -x "$x" || continue name="${x%%.tests}" test -f "$name.right" || continue # echo Running test: "$name.right" { "$THIS_SH" "./$x" >"$name.xx" 2>&1 diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" } && echo "$1/$x: ok" || echo "$1/$x: fail" done ) } # Main part of this script # Usage: run-all [directories] if [ $# -lt 1 ]; then # All sub directories modules=`ls -d msh-*` for module in $modules; do do_test $module done else while [ $# -ge 1 ]; do if [ -d $1 ]; then do_test $1 fi shift done fi �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-execution/��������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�017521� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-execution/exitcode_ENOENT.right�����������������������������������0000644�0000000�0000000�00000000051�12263563520�023426� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������./does_not_exist_for_sure: not found 127 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-execution/exitcode_EACCES.right�����������������������������������0000644�0000000�0000000�00000000026�12263563520�023363� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������./: can't execute 126 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-execution/many_continues.right������������������������������������0000644�0000000�0000000�00000000003�12263563520�023602� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������OK �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-execution/nested_break.tests��������������������������������������0000755�0000000�0000000�00000000275�12263563520�023240� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Testcase for http://bugs.busybox.net/view.php?id=846 n=0 while : do echo A while : do echo B break done echo iteration [ $n = 1 ] && break echo C n=`expr $n + 1` done echo D �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-execution/exitcode_ENOENT.tests�����������������������������������0000755�0000000�0000000�00000000042�12263563520�023456� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������./does_not_exist_for_sure echo $? ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-execution/exitcode_EACCES.tests�����������������������������������0000755�0000000�0000000�00000000013�12263563520�023407� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������./ echo $? ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-execution/nested_break.right��������������������������������������0000644�0000000�0000000�00000000040�12263563520�023176� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������A B iteration C A B iteration D ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-execution/many_continues.tests������������������������������������0000755�0000000�0000000�00000000340�12263563520�023636� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then # Child will kill us in 1 second "$THIS_SH" "$0" $$ & # Loop many, many times trap 'echo OK; exit 0' 15 while true; do continue done echo BAD exit 1 fi sleep 1 kill $1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-misc/�������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�016451� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-misc/tick.right���������������������������������������������������0000644�0000000�0000000�00000000004�12263563520�020432� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-misc/tick.tests���������������������������������������������������0000755�0000000�0000000�00000000073�12263563520�020470� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������true false; echo `echo $?` true { false; echo `echo $?`; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-parsing/����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�017161� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-parsing/quote3.tests����������������������������������������������0000755�0000000�0000000�00000000237�12263563520�021470� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" quote3.tests abc "d e" fi echo 'Testing: in $empty""' empty='' for a in $empty""; do echo ".$a."; done echo Finished �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-parsing/noeol.tests�����������������������������������������������0000755�0000000�0000000�00000000042�12263563520�021356� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# next line has no EOL! echo HELLO����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-parsing/noeol2.tests����������������������������������������������0000755�0000000�0000000�00000000076�12263563520�021447� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# last line has no EOL! if true then echo 1 else echo 2 fi������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-parsing/argv0.tests�����������������������������������������������0000755�0000000�0000000�00000000075�12263563520�021267� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" "$0" arg fi echo OK �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-parsing/quote2.tests����������������������������������������������0000755�0000000�0000000�00000000017�12263563520�021463� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a=1 echo ">$a" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-parsing/noeol.right�����������������������������������������������0000644�0000000�0000000�00000000006�12263563520�021326� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������HELLO ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-parsing/argv0.right�����������������������������������������������0000644�0000000�0000000�00000000003�12263563520�021226� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������OK �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-parsing/quote3.right����������������������������������������������0000644�0000000�0000000�00000000041�12263563520�021431� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Testing: in $empty"" .. Finished �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-parsing/quote4.right����������������������������������������������0000644�0000000�0000000�00000000004�12263563520�021431� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a b ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-parsing/quote4.tests����������������������������������������������0000755�0000000�0000000�00000000026�12263563520�021465� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a_b='a b' echo "$a_b" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-parsing/quote1.right����������������������������������������������0000644�0000000�0000000�00000000004�12263563520�021426� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������'1' ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-parsing/noeol2.right����������������������������������������������0000644�0000000�0000000�00000000002�12263563520�021404� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-parsing/quote1.tests����������������������������������������������0000755�0000000�0000000�00000000020�12263563520�021454� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a=1 echo "'$a'" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-parsing/quote2.right����������������������������������������������0000644�0000000�0000000�00000000003�12263563520�021426� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>1 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-vars/�������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�016471� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-vars/var_subst_in_for.tests���������������������������������������0000755�0000000�0000000�00000001456�12263563520�023130� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" var_subst_in_for.tests abc "d e" fi echo 'Testing: in x y z' for a in x y z; do echo ".$a."; done echo 'Testing: in u $empty v' empty='' for a in u $empty v; do echo ".$a."; done echo 'Testing: in u " $empty" v' empty='' for a in u " $empty" v; do echo ".$a."; done echo 'Testing: in u $empty $empty$a v' a='a' for a in u $empty $empty$a v; do echo ".$a."; done echo 'Testing: in $a_b' a_b='a b' for a in $a_b; do echo ".$a."; done echo 'Testing: in $*' for a in $*; do echo ".$a."; done echo 'Testing: in $@' for a in $@; do echo ".$a."; done echo 'Testing: in -$*-' for a in -$*-; do echo ".$a."; done echo 'Testing: in -$@-' for a in -$@-; do echo ".$a."; done echo 'Testing: in $a_b -$a_b-' a_b='a b' for a in $a_b -$a_b-; do echo ".$a."; done echo Finished ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-vars/var.right����������������������������������������������������0000644�0000000�0000000�00000000056�12263563520�020317� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������http://busybox.net http://busybox.net_abc 1 1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-vars/var_subst_in_for.right���������������������������������������0000644�0000000�0000000�00000000527�12263563520�023076� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Testing: in x y z .x. .y. .z. Testing: in u $empty v .u. .v. Testing: in u " $empty" v .u. . . .v. Testing: in u $empty $empty$a v .u. .a. .v. Testing: in $a_b .a. .b. Testing: in $* .abc. .d. .e. Testing: in $@ .abc. .d. .e. Testing: in -$*- .-abc. .d. .e-. Testing: in -$@- .-abc. .d. .e-. Testing: in $a_b -$a_b- .a. .b. .-a. .b-. Finished �������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-vars/var.tests����������������������������������������������������0000755�0000000�0000000�00000000140�12263563520�020341� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������URL=http://busybox.net echo $URL echo ${URL}_abc true false; echo $? true { false; echo $?; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-vars/star.tests���������������������������������������������������0000755�0000000�0000000�00000000331�12263563520�020524� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if test $# = 0; then exec "$THIS_SH" star.tests 1 abc 'd e f' fi # 'd e f' should be split into 3 separate args: for a in $*; do echo ".$a."; done # must produce .1 abc d e f. for a in "$*"; do echo ".$a."; done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/msh_test/msh-vars/star.right���������������������������������������������������0000644�0000000�0000000�00000000044�12263563520�020475� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.1. .abc. .d. .e. .f. .1 abc d e f. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush.c�������������������������������������������������������������������������0000644�0000000�0000000�00000761122�12263563520�014225� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * A prototype Bourne shell grammar parser. * Intended to follow the original Thompson and Ritchie * "small and simple is beautiful" philosophy, which * incidentally is a good match to today's BusyBox. * * Copyright (C) 2000,2001 Larry Doolittle <larry@doolittle.boa.org> * Copyright (C) 2008,2009 Denys Vlasenko <vda.linux@googlemail.com> * * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Credits: * The parser routines proper are all original material, first * written Dec 2000 and Jan 2001 by Larry Doolittle. The * execution engine, the builtins, and much of the underlying * support has been adapted from busybox-0.49pre's lash, which is * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> * written by Erik Andersen <andersen@codepoet.org>. That, in turn, * is based in part on ladsh.c, by Michael K. Johnson and Erik W. * Troan, which they placed in the public domain. I don't know * how much of the Johnson/Troan code has survived the repeated * rewrites. * * Other credits: * o_addchr derived from similar w_addchar function in glibc-2.2. * parse_redirect, redirect_opt_num, and big chunks of main * and many builtins derived from contributions by Erik Andersen. * Miscellaneous bugfixes from Matt Kraai. * * There are two big (and related) architecture differences between * this parser and the lash parser. One is that this version is * actually designed from the ground up to understand nearly all * of the Bourne grammar. The second, consequential change is that * the parser and input reader have been turned inside out. Now, * the parser is in control, and asks for input as needed. The old * way had the input reader in control, and it asked for parsing to * take place as needed. The new way makes it much easier to properly * handle the recursion implicit in the various substitutions, especially * across continuation lines. * * TODOs: * grep for "TODO" and fix (some of them are easy) * special variables (done: PWD, PPID, RANDOM) * tilde expansion * aliases * follow IFS rules more precisely, including update semantics * builtins mandated by standards we don't support: * [un]alias, command, fc, getopts, newgrp, readonly, times * make complex ${var%...} constructs support optional * make here documents optional * * Bash compat TODO: * redirection of stdout+stderr: &> and >& * reserved words: function select * advanced test: [[ ]] * process substitution: <(list) and >(list) * =~: regex operator * let EXPR [EXPR...] * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION) * If the last arg evaluates to 0, let returns 1; 0 otherwise. * NB: let `echo 'a=a + 1'` - error (IOW: multi-word expansion is used) * ((EXPR)) * The EXPR is evaluated according to ARITHMETIC EVALUATION. * This is exactly equivalent to let "EXPR". * $[EXPR]: synonym for $((EXPR)) * * Won't do: * In bash, export builtin is special, its arguments are assignments * and therefore expansion of them should be "one-word" expansion: * $ export i=`echo 'a b'` # export has one arg: "i=a b" * compare with: * $ ls i=`echo 'a b'` # ls has two args: "i=a" and "b" * ls: cannot access i=a: No such file or directory * ls: cannot access b: No such file or directory * Note1: same applies to local builtin. * Note2: bash 3.2.33(1) does this only if export word itself * is not quoted: * $ export i=`echo 'aaa bbb'`; echo "$i" * aaa bbb * $ "export" i=`echo 'aaa bbb'`; echo "$i" * aaa */ #if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ || defined(__APPLE__) \ ) # include <malloc.h> /* for malloc_trim */ #endif #include <glob.h> /* #include <dmalloc.h> */ #if ENABLE_HUSH_CASE # include <fnmatch.h> #endif #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ #include "unicode.h" #include "shell_common.h" #include "math.h" #include "match.h" #if ENABLE_HUSH_RANDOM_SUPPORT # include "random.h" #else # define CLEAR_RANDOM_T(rnd) ((void)0) #endif #ifndef PIPE_BUF # define PIPE_BUF 4096 /* amount of buffering in a pipe */ #endif //config:config HUSH //config: bool "hush" //config: default y //config: help //config: hush is a small shell (25k). It handles the normal flow control //config: constructs such as if/then/elif/else/fi, for/in/do/done, while loops, //config: case/esac. Redirections, here documents, $((arithmetic)) //config: and functions are supported. //config: //config: It will compile and work on no-mmu systems. //config: //config: It does not handle select, aliases, tilde expansion, //config: &>file and >&file redirection of stdout+stderr. //config: //config:config HUSH_BASH_COMPAT //config: bool "bash-compatible extensions" //config: default y //config: depends on HUSH //config: help //config: Enable bash-compatible extensions. //config: //config:config HUSH_BRACE_EXPANSION //config: bool "Brace expansion" //config: default y //config: depends on HUSH_BASH_COMPAT //config: help //config: Enable {abc,def} extension. //config: //config:config HUSH_HELP //config: bool "help builtin" //config: default y //config: depends on HUSH //config: help //config: Enable help builtin in hush. Code size + ~1 kbyte. //config: //config:config HUSH_INTERACTIVE //config: bool "Interactive mode" //config: default y //config: depends on HUSH //config: help //config: Enable interactive mode (prompt and command editing). //config: Without this, hush simply reads and executes commands //config: from stdin just like a shell script from a file. //config: No prompt, no PS1/PS2 magic shell variables. //config: //config:config HUSH_SAVEHISTORY //config: bool "Save command history to .hush_history" //config: default y //config: depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY //config: help //config: Enable history saving in hush. //config: //config:config HUSH_JOB //config: bool "Job control" //config: default y //config: depends on HUSH_INTERACTIVE //config: help //config: Enable job control: Ctrl-Z backgrounds, Ctrl-C interrupts current //config: command (not entire shell), fg/bg builtins work. Without this option, //config: "cmd &" still works by simply spawning a process and immediately //config: prompting for next command (or executing next command in a script), //config: but no separate process group is formed. //config: //config:config HUSH_TICK //config: bool "Process substitution" //config: default y //config: depends on HUSH //config: help //config: Enable process substitution `command` and $(command) in hush. //config: //config:config HUSH_IF //config: bool "Support if/then/elif/else/fi" //config: default y //config: depends on HUSH //config: help //config: Enable if/then/elif/else/fi in hush. //config: //config:config HUSH_LOOPS //config: bool "Support for, while and until loops" //config: default y //config: depends on HUSH //config: help //config: Enable for, while and until loops in hush. //config: //config:config HUSH_CASE //config: bool "Support case ... esac statement" //config: default y //config: depends on HUSH //config: help //config: Enable case ... esac statement in hush. +400 bytes. //config: //config:config HUSH_FUNCTIONS //config: bool "Support funcname() { commands; } syntax" //config: default y //config: depends on HUSH //config: help //config: Enable support for shell functions in hush. +800 bytes. //config: //config:config HUSH_LOCAL //config: bool "Support local builtin" //config: default y //config: depends on HUSH_FUNCTIONS //config: help //config: Enable support for local variables in functions. //config: //config:config HUSH_RANDOM_SUPPORT //config: bool "Pseudorandom generator and $RANDOM variable" //config: default y //config: depends on HUSH //config: help //config: Enable pseudorandom generator and dynamic variable "$RANDOM". //config: Each read of "$RANDOM" will generate a new pseudorandom value. //config: //config:config HUSH_EXPORT_N //config: bool "Support 'export -n' option" //config: default y //config: depends on HUSH //config: help //config: export -n unexports variables. It is a bash extension. //config: //config:config HUSH_MODE_X //config: bool "Support 'hush -x' option and 'set -x' command" //config: default y //config: depends on HUSH //config: help //config: This instructs hush to print commands before execution. //config: Adds ~300 bytes. //config: //config:config MSH //config: bool "msh (deprecated: aliased to hush)" //config: default n //config: select HUSH //config: help //config: msh is deprecated and will be removed, please migrate to hush. //config: //applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP)) //applet:IF_MSH(APPLET(msh, BB_DIR_BIN, BB_SUID_DROP)) //applet:IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, sh)) //applet:IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, bash)) //kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o //kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o /* -i (interactive) and -s (read stdin) are also accepted, * but currently do nothing, therefore aren't shown in help. * NOMMU-specific options are not meant to be used by users, * therefore we don't show them either. */ //usage:#define hush_trivial_usage //usage: "[-nxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" //usage:#define hush_full_usage "\n\n" //usage: "Unix shell interpreter" //usage:#define msh_trivial_usage hush_trivial_usage //usage:#define msh_full_usage hush_full_usage //usage:#if ENABLE_FEATURE_SH_IS_HUSH //usage:# define sh_trivial_usage hush_trivial_usage //usage:# define sh_full_usage hush_full_usage //usage:#endif //usage:#if ENABLE_FEATURE_BASH_IS_HUSH //usage:# define bash_trivial_usage hush_trivial_usage //usage:# define bash_full_usage hush_full_usage //usage:#endif /* Build knobs */ #define LEAK_HUNTING 0 #define BUILD_AS_NOMMU 0 /* Enable/disable sanity checks. Ok to enable in production, * only adds a bit of bloat. Set to >1 to get non-production level verbosity. * Keeping 1 for now even in released versions. */ #define HUSH_DEBUG 1 /* Slightly bigger (+200 bytes), but faster hush. * So far it only enables a trick with counting SIGCHLDs and forks, * which allows us to do fewer waitpid's. * (we can detect a case where neither forks were done nor SIGCHLDs happened * and therefore waitpid will return the same result as last time) */ #define ENABLE_HUSH_FAST 0 /* TODO: implement simplified code for users which do not need ${var%...} ops * So far ${var%...} ops are always enabled: */ #define ENABLE_HUSH_DOLLAR_OPS 1 #if BUILD_AS_NOMMU # undef BB_MMU # undef USE_FOR_NOMMU # undef USE_FOR_MMU # define BB_MMU 0 # define USE_FOR_NOMMU(...) __VA_ARGS__ # define USE_FOR_MMU(...) #endif #include "NUM_APPLETS.h" #if NUM_APPLETS == 1 /* STANDALONE does not make sense, and won't compile */ # undef CONFIG_FEATURE_SH_STANDALONE # undef ENABLE_FEATURE_SH_STANDALONE # undef IF_FEATURE_SH_STANDALONE # undef IF_NOT_FEATURE_SH_STANDALONE # define ENABLE_FEATURE_SH_STANDALONE 0 # define IF_FEATURE_SH_STANDALONE(...) # define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__ #endif #if !ENABLE_HUSH_INTERACTIVE # undef ENABLE_FEATURE_EDITING # define ENABLE_FEATURE_EDITING 0 # undef ENABLE_FEATURE_EDITING_FANCY_PROMPT # define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0 # undef ENABLE_FEATURE_EDITING_SAVE_ON_EXIT # define ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 0 #endif /* Do we support ANY keywords? */ #if ENABLE_HUSH_IF || ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE # define HAS_KEYWORDS 1 # define IF_HAS_KEYWORDS(...) __VA_ARGS__ # define IF_HAS_NO_KEYWORDS(...) #else # define HAS_KEYWORDS 0 # define IF_HAS_KEYWORDS(...) # define IF_HAS_NO_KEYWORDS(...) __VA_ARGS__ #endif /* If you comment out one of these below, it will be #defined later * to perform debug printfs to stderr: */ #define debug_printf(...) do {} while (0) /* Finer-grained debug switches */ #define debug_printf_parse(...) do {} while (0) #define debug_print_tree(a, b) do {} while (0) #define debug_printf_exec(...) do {} while (0) #define debug_printf_env(...) do {} while (0) #define debug_printf_jobs(...) do {} while (0) #define debug_printf_expand(...) do {} while (0) #define debug_printf_varexp(...) do {} while (0) #define debug_printf_glob(...) do {} while (0) #define debug_printf_list(...) do {} while (0) #define debug_printf_subst(...) do {} while (0) #define debug_printf_clean(...) do {} while (0) #define ERR_PTR ((void*)(long)1) #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" #define _SPECIAL_VARS_STR "_*@$!?#" #define SPECIAL_VARS_STR ("_*@$!?#" + 1) #define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3) #if ENABLE_HUSH_BASH_COMPAT /* Support / and // replace ops */ /* Note that // is stored as \ in "encoded" string representation */ # define VAR_ENCODED_SUBST_OPS "\\/%#:-=+?" # define VAR_SUBST_OPS ("\\/%#:-=+?" + 1) # define MINUS_PLUS_EQUAL_QUESTION ("\\/%#:-=+?" + 5) #else # define VAR_ENCODED_SUBST_OPS "%#:-=+?" # define VAR_SUBST_OPS "%#:-=+?" # define MINUS_PLUS_EQUAL_QUESTION ("%#:-=+?" + 3) #endif #define SPECIAL_VAR_SYMBOL 3 struct variable; static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="BB_VER; /* This supports saving pointers malloced in vfork child, * to be freed in the parent. */ #if !BB_MMU typedef struct nommu_save_t { char **new_env; struct variable *old_vars; char **argv; char **argv_from_re_execing; } nommu_save_t; #endif enum { RES_NONE = 0, #if ENABLE_HUSH_IF RES_IF , RES_THEN , RES_ELIF , RES_ELSE , RES_FI , #endif #if ENABLE_HUSH_LOOPS RES_FOR , RES_WHILE , RES_UNTIL , RES_DO , RES_DONE , #endif #if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE RES_IN , #endif #if ENABLE_HUSH_CASE RES_CASE , /* three pseudo-keywords support contrived "case" syntax: */ RES_CASE_IN, /* "case ... IN", turns into RES_MATCH when IN is observed */ RES_MATCH , /* "word)" */ RES_CASE_BODY, /* "this command is inside CASE" */ RES_ESAC , #endif RES_XXXX , RES_SNTX }; typedef struct o_string { char *data; int length; /* position where data is appended */ int maxlen; int o_expflags; /* At least some part of the string was inside '' or "", * possibly empty one: word"", wo''rd etc. */ smallint has_quoted_part; smallint has_empty_slot; smallint o_assignment; /* 0:maybe, 1:yes, 2:no */ } o_string; enum { EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */ EXP_FLAG_GLOB = 0x2, /* Protect newly added chars against globbing * by prepending \ to *, ?, [, \ */ EXP_FLAG_ESC_GLOB_CHARS = 0x1, }; enum { MAYBE_ASSIGNMENT = 0, DEFINITELY_ASSIGNMENT = 1, NOT_ASSIGNMENT = 2, /* Not an assigment, but next word may be: "if v=xyz cmd;" */ WORD_IS_KEYWORD = 3, }; /* Used for initialization: o_string foo = NULL_O_STRING; */ #define NULL_O_STRING { NULL } #ifndef debug_printf_parse static const char *const assignment_flag[] = { "MAYBE_ASSIGNMENT", "DEFINITELY_ASSIGNMENT", "NOT_ASSIGNMENT", "WORD_IS_KEYWORD", }; #endif typedef struct in_str { const char *p; /* eof_flag=1: last char in ->p is really an EOF */ char eof_flag; /* meaningless if ->p == NULL */ char peek_buf[2]; #if ENABLE_HUSH_INTERACTIVE smallint promptmode; /* 0: PS1, 1: PS2 */ #endif int last_char; FILE *file; int (*get) (struct in_str *) FAST_FUNC; int (*peek) (struct in_str *) FAST_FUNC; } in_str; #define i_getch(input) ((input)->get(input)) #define i_peek(input) ((input)->peek(input)) /* The descrip member of this structure is only used to make * debugging output pretty */ static const struct { int mode; signed char default_fd; char descrip[3]; } redir_table[] = { { O_RDONLY, 0, "<" }, { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" }, { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" }, { O_CREAT|O_RDWR, 1, "<>" }, { O_RDONLY, 0, "<<" }, /* Should not be needed. Bogus default_fd helps in debugging */ /* { O_RDONLY, 77, "<<" }, */ }; struct redir_struct { struct redir_struct *next; char *rd_filename; /* filename */ int rd_fd; /* fd to redirect */ /* fd to redirect to, or -3 if rd_fd is to be closed (n>&-) */ int rd_dup; smallint rd_type; /* (enum redir_type) */ /* note: for heredocs, rd_filename contains heredoc delimiter, * and subsequently heredoc itself; and rd_dup is a bitmask: * bit 0: do we need to trim leading tabs? * bit 1: is heredoc quoted (<<'delim' syntax) ? */ }; typedef enum redir_type { REDIRECT_INPUT = 0, REDIRECT_OVERWRITE = 1, REDIRECT_APPEND = 2, REDIRECT_IO = 3, REDIRECT_HEREDOC = 4, REDIRECT_HEREDOC2 = 5, /* REDIRECT_HEREDOC after heredoc is loaded */ REDIRFD_CLOSE = -3, REDIRFD_SYNTAX_ERR = -2, REDIRFD_TO_FILE = -1, /* otherwise, rd_fd is redirected to rd_dup */ HEREDOC_SKIPTABS = 1, HEREDOC_QUOTED = 2, } redir_type; struct command { pid_t pid; /* 0 if exited */ int assignment_cnt; /* how many argv[i] are assignments? */ smallint cmd_type; /* CMD_xxx */ #define CMD_NORMAL 0 #define CMD_SUBSHELL 1 #if ENABLE_HUSH_BASH_COMPAT /* used for "[[ EXPR ]]" */ # define CMD_SINGLEWORD_NOGLOB 2 #endif #if ENABLE_HUSH_FUNCTIONS # define CMD_FUNCDEF 3 #endif smalluint cmd_exitcode; /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */ struct pipe *group; #if !BB_MMU char *group_as_string; #endif #if ENABLE_HUSH_FUNCTIONS struct function *child_func; /* This field is used to prevent a bug here: * while...do f1() {a;}; f1; f1() {b;}; f1; done * When we execute "f1() {a;}" cmd, we create new function and clear * cmd->group, cmd->group_as_string, cmd->argv[0]. * When we execute "f1() {b;}", we notice that f1 exists, * and that its "parent cmd" struct is still "alive", * we put those fields back into cmd->xxx * (struct function has ->parent_cmd ptr to facilitate that). * When we loop back, we can execute "f1() {a;}" again and set f1 correctly. * Without this trick, loop would execute a;b;b;b;... * instead of correct sequence a;b;a;b;... * When command is freed, it severs the link * (sets ->child_func->parent_cmd to NULL). */ #endif char **argv; /* command name and arguments */ /* argv vector may contain variable references (^Cvar^C, ^C0^C etc) * and on execution these are substituted with their values. * Substitution can make _several_ words out of one argv[n]! * Example: argv[0]=='.^C*^C.' here: echo .$*. * References of the form ^C`cmd arg^C are `cmd arg` substitutions. */ struct redir_struct *redirects; /* I/O redirections */ }; /* Is there anything in this command at all? */ #define IS_NULL_CMD(cmd) \ (!(cmd)->group && !(cmd)->argv && !(cmd)->redirects) struct pipe { struct pipe *next; int num_cmds; /* total number of commands in pipe */ int alive_cmds; /* number of commands running (not exited) */ int stopped_cmds; /* number of commands alive, but stopped */ #if ENABLE_HUSH_JOB int jobid; /* job number */ pid_t pgrp; /* process group ID for the job */ char *cmdtext; /* name of job */ #endif struct command *cmds; /* array of commands in pipe */ smallint followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */ IF_HAS_KEYWORDS(smallint pi_inverted;) /* "! cmd | cmd" */ IF_HAS_KEYWORDS(smallint res_word;) /* needed for if, for, while, until... */ }; typedef enum pipe_style { PIPE_SEQ = 1, PIPE_AND = 2, PIPE_OR = 3, PIPE_BG = 4, } pipe_style; /* Is there anything in this pipe at all? */ #define IS_NULL_PIPE(pi) \ ((pi)->num_cmds == 0 IF_HAS_KEYWORDS( && (pi)->res_word == RES_NONE)) /* This holds pointers to the various results of parsing */ struct parse_context { /* linked list of pipes */ struct pipe *list_head; /* last pipe (being constructed right now) */ struct pipe *pipe; /* last command in pipe (being constructed right now) */ struct command *command; /* last redirect in command->redirects list */ struct redir_struct *pending_redirect; #if !BB_MMU o_string as_string; #endif #if HAS_KEYWORDS smallint ctx_res_w; smallint ctx_inverted; /* "! cmd | cmd" */ #if ENABLE_HUSH_CASE smallint ctx_dsemicolon; /* ";;" seen */ #endif /* bitmask of FLAG_xxx, for figuring out valid reserved words */ int old_flag; /* group we are enclosed in: * example: "if pipe1; pipe2; then pipe3; fi" * when we see "if" or "then", we malloc and copy current context, * and make ->stack point to it. then we parse pipeN. * when closing "then" / fi" / whatever is found, * we move list_head into ->stack->command->group, * copy ->stack into current context, and delete ->stack. * (parsing of { list } and ( list ) doesn't use this method) */ struct parse_context *stack; #endif }; /* On program start, environ points to initial environment. * putenv adds new pointers into it, unsetenv removes them. * Neither of these (de)allocates the strings. * setenv allocates new strings in malloc space and does putenv, * and thus setenv is unusable (leaky) for shell's purposes */ #define setenv(...) setenv_is_leaky_dont_use() struct variable { struct variable *next; char *varstr; /* points to "name=" portion */ #if ENABLE_HUSH_LOCAL unsigned func_nest_level; #endif int max_len; /* if > 0, name is part of initial env; else name is malloced */ smallint flg_export; /* putenv should be done on this var */ smallint flg_read_only; }; enum { BC_BREAK = 1, BC_CONTINUE = 2, }; #if ENABLE_HUSH_FUNCTIONS struct function { struct function *next; char *name; struct command *parent_cmd; struct pipe *body; # if !BB_MMU char *body_as_string; # endif }; #endif /* set -/+o OPT support. (TODO: make it optional) * bash supports the following opts: * allexport off * braceexpand on * emacs on * errexit off * errtrace off * functrace off * hashall on * histexpand off * history on * ignoreeof off * interactive-comments on * keyword off * monitor on * noclobber off * noexec off * noglob off * nolog off * notify off * nounset off * onecmd off * physical off * pipefail off * posix off * privileged off * verbose off * vi off * xtrace off */ static const char o_opt_strings[] ALIGN1 = "pipefail\0" "noexec\0" #if ENABLE_HUSH_MODE_X "xtrace\0" #endif ; enum { OPT_O_PIPEFAIL, OPT_O_NOEXEC, #if ENABLE_HUSH_MODE_X OPT_O_XTRACE, #endif NUM_OPT_O }; /* "Globals" within this file */ /* Sorted roughly by size (smaller offsets == smaller code) */ struct globals { /* interactive_fd != 0 means we are an interactive shell. * If we are, then saved_tty_pgrp can also be != 0, meaning * that controlling tty is available. With saved_tty_pgrp == 0, * job control still works, but terminal signals * (^C, ^Z, ^Y, ^\) won't work at all, and background * process groups can only be created with "cmd &". * With saved_tty_pgrp != 0, hush will use tcsetpgrp() * to give tty to the foreground process group, * and will take it back when the group is stopped (^Z) * or killed (^C). */ #if ENABLE_HUSH_INTERACTIVE /* 'interactive_fd' is a fd# open to ctty, if we have one * _AND_ if we decided to act interactively */ int interactive_fd; const char *PS1; const char *PS2; # define G_interactive_fd (G.interactive_fd) #else # define G_interactive_fd 0 #endif #if ENABLE_FEATURE_EDITING line_input_t *line_input_state; #endif pid_t root_pid; pid_t root_ppid; pid_t last_bg_pid; #if ENABLE_HUSH_RANDOM_SUPPORT random_t random_gen; #endif #if ENABLE_HUSH_JOB int run_list_level; int last_jobid; pid_t saved_tty_pgrp; struct pipe *job_list; # define G_saved_tty_pgrp (G.saved_tty_pgrp) #else # define G_saved_tty_pgrp 0 #endif char o_opt[NUM_OPT_O]; #if ENABLE_HUSH_MODE_X # define G_x_mode (G.o_opt[OPT_O_XTRACE]) #else # define G_x_mode 0 #endif smallint flag_SIGINT; #if ENABLE_HUSH_LOOPS smallint flag_break_continue; #endif #if ENABLE_HUSH_FUNCTIONS /* 0: outside of a function (or sourced file) * -1: inside of a function, ok to use return builtin * 1: return is invoked, skip all till end of func */ smallint flag_return_in_progress; #endif smallint exiting; /* used to prevent EXIT trap recursion */ /* These four support $?, $#, and $1 */ smalluint last_exitcode; /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ smalluint global_args_malloced; /* how many non-NULL argv's we have. NB: $# + 1 */ int global_argc; char **global_argv; #if !BB_MMU char *argv0_for_re_execing; #endif #if ENABLE_HUSH_LOOPS unsigned depth_break_continue; unsigned depth_of_loop; #endif const char *ifs; const char *cwd; struct variable *top_var; char **expanded_assignments; #if ENABLE_HUSH_FUNCTIONS struct function *top_func; # if ENABLE_HUSH_LOCAL struct variable **shadowed_vars_pp; unsigned func_nest_level; # endif #endif /* Signal and trap handling */ #if ENABLE_HUSH_FAST unsigned count_SIGCHLD; unsigned handled_SIGCHLD; smallint we_have_children; #endif /* Which signals have non-DFL handler (even with no traps set)? * Set at the start to: * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) * SPECIAL_INTERACTIVE_SIGS are cleared after fork. * The rest is cleared right before execv syscalls. * Other than these two times, never modified. */ unsigned special_sig_mask; #if ENABLE_HUSH_JOB unsigned fatal_sig_mask; # define G_fatal_sig_mask G.fatal_sig_mask #else # define G_fatal_sig_mask 0 #endif char **traps; /* char *traps[NSIG] */ sigset_t pending_set; #if HUSH_DEBUG unsigned long memleak_value; int debug_indent; #endif struct sigaction sa; char user_input_buf[ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 2]; }; #define G (*ptr_to_globals) /* Not #defining name to G.name - this quickly gets unwieldy * (too many defines). Also, I actually prefer to see when a variable * is global, thus "G." prefix is a useful hint */ #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ /* memset(&G.sa, 0, sizeof(G.sa)); */ \ sigfillset(&G.sa.sa_mask); \ G.sa.sa_flags = SA_RESTART; \ } while (0) /* Function prototypes for builtins */ static int builtin_cd(char **argv) FAST_FUNC; static int builtin_echo(char **argv) FAST_FUNC; static int builtin_eval(char **argv) FAST_FUNC; static int builtin_exec(char **argv) FAST_FUNC; static int builtin_exit(char **argv) FAST_FUNC; static int builtin_export(char **argv) FAST_FUNC; #if ENABLE_HUSH_JOB static int builtin_fg_bg(char **argv) FAST_FUNC; static int builtin_jobs(char **argv) FAST_FUNC; #endif #if ENABLE_HUSH_HELP static int builtin_help(char **argv) FAST_FUNC; #endif #if MAX_HISTORY && ENABLE_FEATURE_EDITING static int builtin_history(char **argv) FAST_FUNC; #endif #if ENABLE_HUSH_LOCAL static int builtin_local(char **argv) FAST_FUNC; #endif #if HUSH_DEBUG static int builtin_memleak(char **argv) FAST_FUNC; #endif #if ENABLE_PRINTF static int builtin_printf(char **argv) FAST_FUNC; #endif static int builtin_pwd(char **argv) FAST_FUNC; static int builtin_read(char **argv) FAST_FUNC; static int builtin_set(char **argv) FAST_FUNC; static int builtin_shift(char **argv) FAST_FUNC; static int builtin_source(char **argv) FAST_FUNC; static int builtin_test(char **argv) FAST_FUNC; static int builtin_trap(char **argv) FAST_FUNC; static int builtin_type(char **argv) FAST_FUNC; static int builtin_true(char **argv) FAST_FUNC; static int builtin_umask(char **argv) FAST_FUNC; static int builtin_unset(char **argv) FAST_FUNC; static int builtin_wait(char **argv) FAST_FUNC; #if ENABLE_HUSH_LOOPS static int builtin_break(char **argv) FAST_FUNC; static int builtin_continue(char **argv) FAST_FUNC; #endif #if ENABLE_HUSH_FUNCTIONS static int builtin_return(char **argv) FAST_FUNC; #endif /* Table of built-in functions. They can be forked or not, depending on * context: within pipes, they fork. As simple commands, they do not. * When used in non-forking context, they can change global variables * in the parent shell process. If forked, of course they cannot. * For example, 'unset foo | whatever' will parse and run, but foo will * still be set at the end. */ struct built_in_command { const char *b_cmd; int (*b_function)(char **argv) FAST_FUNC; #if ENABLE_HUSH_HELP const char *b_descr; # define BLTIN(cmd, func, help) { cmd, func, help } #else # define BLTIN(cmd, func, help) { cmd, func } #endif }; static const struct built_in_command bltins1[] = { BLTIN("." , builtin_source , "Run commands in a file"), BLTIN(":" , builtin_true , NULL), #if ENABLE_HUSH_JOB BLTIN("bg" , builtin_fg_bg , "Resume a job in the background"), #endif #if ENABLE_HUSH_LOOPS BLTIN("break" , builtin_break , "Exit from a loop"), #endif BLTIN("cd" , builtin_cd , "Change directory"), #if ENABLE_HUSH_LOOPS BLTIN("continue" , builtin_continue, "Start new loop iteration"), #endif BLTIN("eval" , builtin_eval , "Construct and run shell command"), BLTIN("exec" , builtin_exec , "Execute command, don't return to shell"), BLTIN("exit" , builtin_exit , "Exit"), BLTIN("export" , builtin_export , "Set environment variables"), #if ENABLE_HUSH_JOB BLTIN("fg" , builtin_fg_bg , "Bring job into the foreground"), #endif #if ENABLE_HUSH_HELP BLTIN("help" , builtin_help , NULL), #endif #if MAX_HISTORY && ENABLE_FEATURE_EDITING BLTIN("history" , builtin_history , "Show command history"), #endif #if ENABLE_HUSH_JOB BLTIN("jobs" , builtin_jobs , "List jobs"), #endif #if ENABLE_HUSH_LOCAL BLTIN("local" , builtin_local , "Set local variables"), #endif #if HUSH_DEBUG BLTIN("memleak" , builtin_memleak , NULL), #endif BLTIN("read" , builtin_read , "Input into variable"), #if ENABLE_HUSH_FUNCTIONS BLTIN("return" , builtin_return , "Return from a function"), #endif BLTIN("set" , builtin_set , "Set/unset positional parameters"), BLTIN("shift" , builtin_shift , "Shift positional parameters"), #if ENABLE_HUSH_BASH_COMPAT BLTIN("source" , builtin_source , "Run commands in a file"), #endif BLTIN("trap" , builtin_trap , "Trap signals"), BLTIN("type" , builtin_type , "Show command type"), BLTIN("ulimit" , shell_builtin_ulimit , "Control resource limits"), BLTIN("umask" , builtin_umask , "Set file creation mask"), BLTIN("unset" , builtin_unset , "Unset variables"), BLTIN("wait" , builtin_wait , "Wait for process"), }; /* For now, echo and test are unconditionally enabled. * Maybe make it configurable? */ static const struct built_in_command bltins2[] = { BLTIN("[" , builtin_test , NULL), BLTIN("echo" , builtin_echo , NULL), #if ENABLE_PRINTF BLTIN("printf" , builtin_printf , NULL), #endif BLTIN("pwd" , builtin_pwd , NULL), BLTIN("test" , builtin_test , NULL), }; /* Debug printouts. */ #if HUSH_DEBUG /* prevent disasters with G.debug_indent < 0 */ # define indent() fdprintf(2, "%*s", (G.debug_indent * 2) & 0xff, "") # define debug_enter() (G.debug_indent++) # define debug_leave() (G.debug_indent--) #else # define indent() ((void)0) # define debug_enter() ((void)0) # define debug_leave() ((void)0) #endif #ifndef debug_printf # define debug_printf(...) (indent(), fdprintf(2, __VA_ARGS__)) #endif #ifndef debug_printf_parse # define debug_printf_parse(...) (indent(), fdprintf(2, __VA_ARGS__)) #endif #ifndef debug_printf_exec #define debug_printf_exec(...) (indent(), fdprintf(2, __VA_ARGS__)) #endif #ifndef debug_printf_env # define debug_printf_env(...) (indent(), fdprintf(2, __VA_ARGS__)) #endif #ifndef debug_printf_jobs # define debug_printf_jobs(...) (indent(), fdprintf(2, __VA_ARGS__)) # define DEBUG_JOBS 1 #else # define DEBUG_JOBS 0 #endif #ifndef debug_printf_expand # define debug_printf_expand(...) (indent(), fdprintf(2, __VA_ARGS__)) # define DEBUG_EXPAND 1 #else # define DEBUG_EXPAND 0 #endif #ifndef debug_printf_varexp # define debug_printf_varexp(...) (indent(), fdprintf(2, __VA_ARGS__)) #endif #ifndef debug_printf_glob # define debug_printf_glob(...) (indent(), fdprintf(2, __VA_ARGS__)) # define DEBUG_GLOB 1 #else # define DEBUG_GLOB 0 #endif #ifndef debug_printf_list # define debug_printf_list(...) (indent(), fdprintf(2, __VA_ARGS__)) #endif #ifndef debug_printf_subst # define debug_printf_subst(...) (indent(), fdprintf(2, __VA_ARGS__)) #endif #ifndef debug_printf_clean # define debug_printf_clean(...) (indent(), fdprintf(2, __VA_ARGS__)) # define DEBUG_CLEAN 1 #else # define DEBUG_CLEAN 0 #endif #if DEBUG_EXPAND static void debug_print_strings(const char *prefix, char **vv) { indent(); fdprintf(2, "%s:\n", prefix); while (*vv) fdprintf(2, " '%s'\n", *vv++); } #else # define debug_print_strings(prefix, vv) ((void)0) #endif /* Leak hunting. Use hush_leaktool.sh for post-processing. */ #if LEAK_HUNTING static void *xxmalloc(int lineno, size_t size) { void *ptr = xmalloc((size + 0xff) & ~0xff); fdprintf(2, "line %d: malloc %p\n", lineno, ptr); return ptr; } static void *xxrealloc(int lineno, void *ptr, size_t size) { ptr = xrealloc(ptr, (size + 0xff) & ~0xff); fdprintf(2, "line %d: realloc %p\n", lineno, ptr); return ptr; } static char *xxstrdup(int lineno, const char *str) { char *ptr = xstrdup(str); fdprintf(2, "line %d: strdup %p\n", lineno, ptr); return ptr; } static void xxfree(void *ptr) { fdprintf(2, "free %p\n", ptr); free(ptr); } # define xmalloc(s) xxmalloc(__LINE__, s) # define xrealloc(p, s) xxrealloc(__LINE__, p, s) # define xstrdup(s) xxstrdup(__LINE__, s) # define free(p) xxfree(p) #endif /* Syntax and runtime errors. They always abort scripts. * In interactive use they usually discard unparsed and/or unexecuted commands * and return to the prompt. * HUSH_DEBUG >= 2 prints line number in this file where it was detected. */ #if HUSH_DEBUG < 2 # define die_if_script(lineno, ...) die_if_script(__VA_ARGS__) # define syntax_error(lineno, msg) syntax_error(msg) # define syntax_error_at(lineno, msg) syntax_error_at(msg) # define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch) # define syntax_error_unterm_str(lineno, s) syntax_error_unterm_str(s) # define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch) #endif static void die_if_script(unsigned lineno, const char *fmt, ...) { va_list p; #if HUSH_DEBUG >= 2 bb_error_msg("hush.c:%u", lineno); #endif va_start(p, fmt); bb_verror_msg(fmt, p, NULL); va_end(p); if (!G_interactive_fd) xfunc_die(); } static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg) { if (msg) bb_error_msg("syntax error: %s", msg); else bb_error_msg("syntax error"); } static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg) { bb_error_msg("syntax error at '%s'", msg); } static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s) { bb_error_msg("syntax error: unterminated %s", s); } static void syntax_error_unterm_ch(unsigned lineno, char ch) { char msg[2] = { ch, '\0' }; syntax_error_unterm_str(lineno, msg); } static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch) { char msg[2]; msg[0] = ch; msg[1] = '\0'; bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg); } #if HUSH_DEBUG < 2 # undef die_if_script # undef syntax_error # undef syntax_error_at # undef syntax_error_unterm_ch # undef syntax_error_unterm_str # undef syntax_error_unexpected_ch #else # define die_if_script(...) die_if_script(__LINE__, __VA_ARGS__) # define syntax_error(msg) syntax_error(__LINE__, msg) # define syntax_error_at(msg) syntax_error_at(__LINE__, msg) # define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch) # define syntax_error_unterm_str(s) syntax_error_unterm_str(__LINE__, s) # define syntax_error_unexpected_ch(ch) syntax_error_unexpected_ch(__LINE__, ch) #endif #if ENABLE_HUSH_INTERACTIVE static void cmdedit_update_prompt(void); #else # define cmdedit_update_prompt() ((void)0) #endif /* Utility functions */ /* Replace each \x with x in place, return ptr past NUL. */ static char *unbackslash(char *src) { char *dst = src = strchrnul(src, '\\'); while (1) { if (*src == '\\') src++; if ((*dst++ = *src++) == '\0') break; } return dst; } static char **add_strings_to_strings(char **strings, char **add, int need_to_dup) { int i; unsigned count1; unsigned count2; char **v; v = strings; count1 = 0; if (v) { while (*v) { count1++; v++; } } count2 = 0; v = add; while (*v) { count2++; v++; } v = xrealloc(strings, (count1 + count2 + 1) * sizeof(char*)); v[count1 + count2] = NULL; i = count2; while (--i >= 0) v[count1 + i] = (need_to_dup ? xstrdup(add[i]) : add[i]); return v; } #if LEAK_HUNTING static char **xx_add_strings_to_strings(int lineno, char **strings, char **add, int need_to_dup) { char **ptr = add_strings_to_strings(strings, add, need_to_dup); fdprintf(2, "line %d: add_strings_to_strings %p\n", lineno, ptr); return ptr; } #define add_strings_to_strings(strings, add, need_to_dup) \ xx_add_strings_to_strings(__LINE__, strings, add, need_to_dup) #endif /* Note: takes ownership of "add" ptr (it is not strdup'ed) */ static char **add_string_to_strings(char **strings, char *add) { char *v[2]; v[0] = add; v[1] = NULL; return add_strings_to_strings(strings, v, /*dup:*/ 0); } #if LEAK_HUNTING static char **xx_add_string_to_strings(int lineno, char **strings, char *add) { char **ptr = add_string_to_strings(strings, add); fdprintf(2, "line %d: add_string_to_strings %p\n", lineno, ptr); return ptr; } #define add_string_to_strings(strings, add) \ xx_add_string_to_strings(__LINE__, strings, add) #endif static void free_strings(char **strings) { char **v; if (!strings) return; v = strings; while (*v) { free(*v); v++; } free(strings); } /* Helpers for setting new $n and restoring them back */ typedef struct save_arg_t { char *sv_argv0; char **sv_g_argv; int sv_g_argc; smallint sv_g_malloced; } save_arg_t; static void save_and_replace_G_args(save_arg_t *sv, char **argv) { int n; sv->sv_argv0 = argv[0]; sv->sv_g_argv = G.global_argv; sv->sv_g_argc = G.global_argc; sv->sv_g_malloced = G.global_args_malloced; argv[0] = G.global_argv[0]; /* retain $0 */ G.global_argv = argv; G.global_args_malloced = 0; n = 1; while (*++argv) n++; G.global_argc = n; } static void restore_G_args(save_arg_t *sv, char **argv) { char **pp; if (G.global_args_malloced) { /* someone ran "set -- arg1 arg2 ...", undo */ pp = G.global_argv; while (*++pp) /* note: does not free $0 */ free(*pp); free(G.global_argv); } argv[0] = sv->sv_argv0; G.global_argv = sv->sv_g_argv; G.global_argc = sv->sv_g_argc; G.global_args_malloced = sv->sv_g_malloced; } /* Basic theory of signal handling in shell * ======================================== * This does not describe what hush does, rather, it is current understanding * what it _should_ do. If it doesn't, it's a bug. * http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#trap * * Signals are handled only after each pipe ("cmd | cmd | cmd" thing) * is finished or backgrounded. It is the same in interactive and * non-interactive shells, and is the same regardless of whether * a user trap handler is installed or a shell special one is in effect. * ^C or ^Z from keyboard seems to execute "at once" because it usually * backgrounds (i.e. stops) or kills all members of currently running * pipe. * * Wait builtin is interruptible by signals for which user trap is set * or by SIGINT in interactive shell. * * Trap handlers will execute even within trap handlers. (right?) * * User trap handlers are forgotten when subshell ("(cmd)") is entered, * except for handlers set to '' (empty string). * * If job control is off, backgrounded commands ("cmd &") * have SIGINT, SIGQUIT set to SIG_IGN. * * Commands which are run in command substitution ("`cmd`") * have SIGTTIN, SIGTTOU, SIGTSTP set to SIG_IGN. * * Ordinary commands have signals set to SIG_IGN/DFL as inherited * by the shell from its parent. * * Signals which differ from SIG_DFL action * (note: child (i.e., [v]forked) shell is not an interactive shell): * * SIGQUIT: ignore * SIGTERM (interactive): ignore * SIGHUP (interactive): * send SIGCONT to stopped jobs, send SIGHUP to all jobs and exit * SIGTTIN, SIGTTOU, SIGTSTP (if job control is on): ignore * Note that ^Z is handled not by trapping SIGTSTP, but by seeing * that all pipe members are stopped. Try this in bash: * while :; do :; done - ^Z does not background it * (while :; do :; done) - ^Z backgrounds it * SIGINT (interactive): wait for last pipe, ignore the rest * of the command line, show prompt. NB: ^C does not send SIGINT * to interactive shell while shell is waiting for a pipe, * since shell is bg'ed (is not in foreground process group). * Example 1: this waits 5 sec, but does not execute ls: * "echo $$; sleep 5; ls -l" + "kill -INT <pid>" * Example 2: this does not wait and does not execute ls: * "echo $$; sleep 5 & wait; ls -l" + "kill -INT <pid>" * Example 3: this does not wait 5 sec, but executes ls: * "sleep 5; ls -l" + press ^C * Example 4: this does not wait and does not execute ls: * "sleep 5 & wait; ls -l" + press ^C * * (What happens to signals which are IGN on shell start?) * (What happens with signal mask on shell start?) * * Old implementation * ================== * We use in-kernel pending signal mask to determine which signals were sent. * We block all signals which we don't want to take action immediately, * i.e. we block all signals which need to have special handling as described * above, and all signals which have traps set. * After each pipe execution, we extract any pending signals via sigtimedwait() * and act on them. * * unsigned special_sig_mask: a mask of such "special" signals * sigset_t blocked_set: current blocked signal set * * "trap - SIGxxx": * clear bit in blocked_set unless it is also in special_sig_mask * "trap 'cmd' SIGxxx": * set bit in blocked_set (even if 'cmd' is '') * after [v]fork, if we plan to be a shell: * unblock signals with special interactive handling * (child shell is not interactive), * unset all traps except '' (note: regardless of child shell's type - {}, (), etc) * after [v]fork, if we plan to exec: * POSIX says fork clears pending signal mask in child - no need to clear it. * Restore blocked signal set to one inherited by shell just prior to exec. * * Note: as a result, we do not use signal handlers much. The only uses * are to count SIGCHLDs * and to restore tty pgrp on signal-induced exit. * * Note 2 (compat): * Standard says "When a subshell is entered, traps that are not being ignored * are set to the default actions". bash interprets it so that traps which * are set to '' (ignore) are NOT reset to defaults. We do the same. * * Problem: the above approach makes it unwieldy to catch signals while * we are in read builtin, or while we read commands from stdin: * masked signals are not visible! * * New implementation * ================== * We record each signal we are interested in by installing signal handler * for them - a bit like emulating kernel pending signal mask in userspace. * We are interested in: signals which need to have special handling * as described above, and all signals which have traps set. * Signals are recorded in pending_set. * After each pipe execution, we extract any pending signals * and act on them. * * unsigned special_sig_mask: a mask of shell-special signals. * unsigned fatal_sig_mask: a mask of signals on which we restore tty pgrp. * char *traps[sig] if trap for sig is set (even if it's ''). * sigset_t pending_set: set of sigs we received. * * "trap - SIGxxx": * if sig is in special_sig_mask, set handler back to: * record_pending_signo, or to IGN if it's a tty stop signal * if sig is in fatal_sig_mask, set handler back to sigexit. * else: set handler back to SIG_DFL * "trap 'cmd' SIGxxx": * set handler to record_pending_signo. * "trap '' SIGxxx": * set handler to SIG_IGN. * after [v]fork, if we plan to be a shell: * set signals with special interactive handling to SIG_DFL * (because child shell is not interactive), * unset all traps except '' (note: regardless of child shell's type - {}, (), etc) * after [v]fork, if we plan to exec: * POSIX says fork clears pending signal mask in child - no need to clear it. * * To make wait builtin interruptible, we handle SIGCHLD as special signal, * otherwise (if we leave it SIG_DFL) sigsuspend in wait builtin will not wake up on it. * * Note (compat): * Standard says "When a subshell is entered, traps that are not being ignored * are set to the default actions". bash interprets it so that traps which * are set to '' (ignore) are NOT reset to defaults. We do the same. */ enum { SPECIAL_INTERACTIVE_SIGS = 0 | (1 << SIGTERM) | (1 << SIGINT) | (1 << SIGHUP) , SPECIAL_JOBSTOP_SIGS = 0 #if ENABLE_HUSH_JOB | (1 << SIGTTIN) | (1 << SIGTTOU) | (1 << SIGTSTP) #endif , }; static void record_pending_signo(int sig) { sigaddset(&G.pending_set, sig); #if ENABLE_HUSH_FAST if (sig == SIGCHLD) { G.count_SIGCHLD++; //bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); } #endif } static sighandler_t install_sighandler(int sig, sighandler_t handler) { struct sigaction old_sa; /* We could use signal() to install handlers... almost: * except that we need to mask ALL signals while handlers run. * I saw signal nesting in strace, race window isn't small. * SA_RESTART is also needed, but in Linux, signal() * sets SA_RESTART too. */ /* memset(&G.sa, 0, sizeof(G.sa)); - already done */ /* sigfillset(&G.sa.sa_mask); - already done */ /* G.sa.sa_flags = SA_RESTART; - already done */ G.sa.sa_handler = handler; sigaction(sig, &G.sa, &old_sa); return old_sa.sa_handler; } #if ENABLE_HUSH_JOB /* After [v]fork, in child: do not restore tty pgrp on xfunc death */ # define disable_restore_tty_pgrp_on_exit() (die_sleep = 0) /* After [v]fork, in parent: restore tty pgrp on xfunc death */ # define enable_restore_tty_pgrp_on_exit() (die_sleep = -1) /* Restores tty foreground process group, and exits. * May be called as signal handler for fatal signal * (will resend signal to itself, producing correct exit state) * or called directly with -EXITCODE. * We also call it if xfunc is exiting. */ static void sigexit(int sig) NORETURN; static void sigexit(int sig) { /* Careful: we can end up here after [v]fork. Do not restore * tty pgrp then, only top-level shell process does that */ if (G_saved_tty_pgrp && getpid() == G.root_pid) { /* Disable all signals: job control, SIGPIPE, etc. * Mostly paranoid measure, to prevent infinite SIGTTOU. */ sigprocmask_allsigs(SIG_BLOCK); tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); } /* Not a signal, just exit */ if (sig <= 0) _exit(- sig); kill_myself_with_sig(sig); /* does not return */ } #else # define disable_restore_tty_pgrp_on_exit() ((void)0) # define enable_restore_tty_pgrp_on_exit() ((void)0) #endif static sighandler_t pick_sighandler(unsigned sig) { sighandler_t handler = SIG_DFL; if (sig < sizeof(unsigned)*8) { unsigned sigmask = (1 << sig); #if ENABLE_HUSH_JOB /* is sig fatal? */ if (G_fatal_sig_mask & sigmask) handler = sigexit; else #endif /* sig has special handling? */ if (G.special_sig_mask & sigmask) { handler = record_pending_signo; /* TTIN/TTOU/TSTP can't be set to record_pending_signo * in order to ignore them: they will be raised * in an endless loop when we try to do some * terminal ioctls! We do have to _ignore_ these. */ if (SPECIAL_JOBSTOP_SIGS & sigmask) handler = SIG_IGN; } } return handler; } /* Restores tty foreground process group, and exits. */ static void hush_exit(int exitcode) NORETURN; static void hush_exit(int exitcode) { #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT save_history(G.line_input_state); #endif fflush_all(); if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) { char *argv[3]; /* argv[0] is unused */ argv[1] = G.traps[0]; argv[2] = NULL; G.exiting = 1; /* prevent EXIT trap recursion */ /* Note: G.traps[0] is not cleared! * "trap" will still show it, if executed * in the handler */ builtin_eval(argv); } #if ENABLE_FEATURE_CLEAN_UP { struct variable *cur_var; if (G.cwd != bb_msg_unknown) free((char*)G.cwd); cur_var = G.top_var; while (cur_var) { struct variable *tmp = cur_var; if (!cur_var->max_len) free(cur_var->varstr); cur_var = cur_var->next; free(tmp); } } #endif #if ENABLE_HUSH_JOB fflush_all(); sigexit(- (exitcode & 0xff)); #else exit(exitcode); #endif } //TODO: return a mask of ALL handled sigs? static int check_and_run_traps(void) { int last_sig = 0; while (1) { int sig; if (sigisemptyset(&G.pending_set)) break; sig = 0; do { sig++; if (sigismember(&G.pending_set, sig)) { sigdelset(&G.pending_set, sig); goto got_sig; } } while (sig < NSIG); break; got_sig: if (G.traps && G.traps[sig]) { if (G.traps[sig][0]) { /* We have user-defined handler */ smalluint save_rcode; char *argv[3]; /* argv[0] is unused */ argv[1] = G.traps[sig]; argv[2] = NULL; save_rcode = G.last_exitcode; builtin_eval(argv); G.last_exitcode = save_rcode; last_sig = sig; } /* else: "" trap, ignoring signal */ continue; } /* not a trap: special action */ switch (sig) { case SIGINT: /* Builtin was ^C'ed, make it look prettier: */ bb_putchar('\n'); G.flag_SIGINT = 1; last_sig = sig; break; #if ENABLE_HUSH_JOB case SIGHUP: { struct pipe *job; /* bash is observed to signal whole process groups, * not individual processes */ for (job = G.job_list; job; job = job->next) { if (job->pgrp <= 0) continue; debug_printf_exec("HUPing pgrp %d\n", job->pgrp); if (kill(- job->pgrp, SIGHUP) == 0) kill(- job->pgrp, SIGCONT); } sigexit(SIGHUP); } #endif #if ENABLE_HUSH_FAST case SIGCHLD: G.count_SIGCHLD++; //bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); /* Note: * We dont do 'last_sig = sig' here -> NOT returning this sig. * This simplifies wait builtin a bit. */ break; #endif default: /* ignored: */ /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ /* Note: * We dont do 'last_sig = sig' here -> NOT returning this sig. * Example: wait is not interrupted by TERM * in interactive shell, because TERM is ignored. */ break; } } return last_sig; } static const char *get_cwd(int force) { if (force || G.cwd == NULL) { /* xrealloc_getcwd_or_warn(arg) calls free(arg), * we must not try to free(bb_msg_unknown) */ if (G.cwd == bb_msg_unknown) G.cwd = NULL; G.cwd = xrealloc_getcwd_or_warn((char *)G.cwd); if (!G.cwd) G.cwd = bb_msg_unknown; } return G.cwd; } /* * Shell and environment variable support */ static struct variable **get_ptr_to_local_var(const char *name, unsigned len) { struct variable **pp; struct variable *cur; pp = &G.top_var; while ((cur = *pp) != NULL) { if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=') return pp; pp = &cur->next; } return NULL; } static const char* FAST_FUNC get_local_var_value(const char *name) { struct variable **vpp; unsigned len = strlen(name); if (G.expanded_assignments) { char **cpp = G.expanded_assignments; while (*cpp) { char *cp = *cpp; if (strncmp(cp, name, len) == 0 && cp[len] == '=') return cp + len + 1; cpp++; } } vpp = get_ptr_to_local_var(name, len); if (vpp) return (*vpp)->varstr + len + 1; if (strcmp(name, "PPID") == 0) return utoa(G.root_ppid); // bash compat: UID? EUID? #if ENABLE_HUSH_RANDOM_SUPPORT if (strcmp(name, "RANDOM") == 0) return utoa(next_random(&G.random_gen)); #endif return NULL; } /* str holds "NAME=VAL" and is expected to be malloced. * We take ownership of it. * flg_export: * 0: do not change export flag * (if creating new variable, flag will be 0) * 1: set export flag and putenv the variable * -1: clear export flag and unsetenv the variable * flg_read_only is set only when we handle -R var=val */ #if !BB_MMU && ENABLE_HUSH_LOCAL /* all params are used */ #elif BB_MMU && ENABLE_HUSH_LOCAL #define set_local_var(str, flg_export, local_lvl, flg_read_only) \ set_local_var(str, flg_export, local_lvl) #elif BB_MMU && !ENABLE_HUSH_LOCAL #define set_local_var(str, flg_export, local_lvl, flg_read_only) \ set_local_var(str, flg_export) #elif !BB_MMU && !ENABLE_HUSH_LOCAL #define set_local_var(str, flg_export, local_lvl, flg_read_only) \ set_local_var(str, flg_export, flg_read_only) #endif static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_only) { struct variable **var_pp; struct variable *cur; char *eq_sign; int name_len; eq_sign = strchr(str, '='); if (!eq_sign) { /* not expected to ever happen? */ free(str); return -1; } name_len = eq_sign - str + 1; /* including '=' */ var_pp = &G.top_var; while ((cur = *var_pp) != NULL) { if (strncmp(cur->varstr, str, name_len) != 0) { var_pp = &cur->next; continue; } /* We found an existing var with this name */ if (cur->flg_read_only) { #if !BB_MMU if (!flg_read_only) #endif bb_error_msg("%s: readonly variable", str); free(str); return -1; } if (flg_export == -1) { // "&& cur->flg_export" ? debug_printf_env("%s: unsetenv '%s'\n", __func__, str); *eq_sign = '\0'; unsetenv(str); *eq_sign = '='; } #if ENABLE_HUSH_LOCAL if (cur->func_nest_level < local_lvl) { /* New variable is declared as local, * and existing one is global, or local * from enclosing function. * Remove and save old one: */ *var_pp = cur->next; cur->next = *G.shadowed_vars_pp; *G.shadowed_vars_pp = cur; /* bash 3.2.33(1) and exported vars: * # export z=z * # f() { local z=a; env | grep ^z; } * # f * z=a * # env | grep ^z * z=z */ if (cur->flg_export) flg_export = 1; break; } #endif if (strcmp(cur->varstr + name_len, eq_sign + 1) == 0) { free_and_exp: free(str); goto exp; } if (cur->max_len != 0) { if (cur->max_len >= strlen(str)) { /* This one is from startup env, reuse space */ strcpy(cur->varstr, str); goto free_and_exp; } } else { /* max_len == 0 signifies "malloced" var, which we can * (and has to) free */ free(cur->varstr); } cur->max_len = 0; goto set_str_and_exp; } /* Not found - create new variable struct */ cur = xzalloc(sizeof(*cur)); #if ENABLE_HUSH_LOCAL cur->func_nest_level = local_lvl; #endif cur->next = *var_pp; *var_pp = cur; set_str_and_exp: cur->varstr = str; #if !BB_MMU cur->flg_read_only = flg_read_only; #endif exp: if (flg_export == 1) cur->flg_export = 1; if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S') cmdedit_update_prompt(); if (cur->flg_export) { if (flg_export == -1) { cur->flg_export = 0; /* unsetenv was already done */ } else { debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr); return putenv(cur->varstr); } } return 0; } /* Used at startup and after each cd */ static void set_pwd_var(int exp) { set_local_var(xasprintf("PWD=%s", get_cwd(/*force:*/ 1)), /*exp:*/ exp, /*lvl:*/ 0, /*ro:*/ 0); } static int unset_local_var_len(const char *name, int name_len) { struct variable *cur; struct variable **var_pp; if (!name) return EXIT_SUCCESS; var_pp = &G.top_var; while ((cur = *var_pp) != NULL) { if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') { if (cur->flg_read_only) { bb_error_msg("%s: readonly variable", name); return EXIT_FAILURE; } *var_pp = cur->next; debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr); bb_unsetenv(cur->varstr); if (name_len == 3 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S') cmdedit_update_prompt(); if (!cur->max_len) free(cur->varstr); free(cur); return EXIT_SUCCESS; } var_pp = &cur->next; } return EXIT_SUCCESS; } static int unset_local_var(const char *name) { return unset_local_var_len(name, strlen(name)); } static void unset_vars(char **strings) { char **v; if (!strings) return; v = strings; while (*v) { const char *eq = strchrnul(*v, '='); unset_local_var_len(*v, (int)(eq - *v)); v++; } free(strings); } static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) { char *var = xasprintf("%s=%s", name, val); set_local_var(var, /*flags:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); } /* * Helpers for "var1=val1 var2=val2 cmd" feature */ static void add_vars(struct variable *var) { struct variable *next; while (var) { next = var->next; var->next = G.top_var; G.top_var = var; if (var->flg_export) { debug_printf_env("%s: restoring exported '%s'\n", __func__, var->varstr); putenv(var->varstr); } else { debug_printf_env("%s: restoring variable '%s'\n", __func__, var->varstr); } var = next; } } static struct variable *set_vars_and_save_old(char **strings) { char **s; struct variable *old = NULL; if (!strings) return old; s = strings; while (*s) { struct variable *var_p; struct variable **var_pp; char *eq; eq = strchr(*s, '='); if (eq) { var_pp = get_ptr_to_local_var(*s, eq - *s); if (var_pp) { /* Remove variable from global linked list */ var_p = *var_pp; debug_printf_env("%s: removing '%s'\n", __func__, var_p->varstr); *var_pp = var_p->next; /* Add it to returned list */ var_p->next = old; old = var_p; } set_local_var(*s, /*exp:*/ 1, /*lvl:*/ 0, /*ro:*/ 0); } s++; } return old; } /* * in_str support */ static int FAST_FUNC static_get(struct in_str *i) { int ch = *i->p; if (ch != '\0') { i->p++; i->last_char = ch; return ch; } return EOF; } static int FAST_FUNC static_peek(struct in_str *i) { return *i->p; } #if ENABLE_HUSH_INTERACTIVE static void cmdedit_update_prompt(void) { if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) { G.PS1 = get_local_var_value("PS1"); if (G.PS1 == NULL) G.PS1 = "\\w \\$ "; G.PS2 = get_local_var_value("PS2"); } else { G.PS1 = NULL; } if (G.PS2 == NULL) G.PS2 = "> "; } static const char *setup_prompt_string(int promptmode) { const char *prompt_str; debug_printf("setup_prompt_string %d ", promptmode); if (!ENABLE_FEATURE_EDITING_FANCY_PROMPT) { /* Set up the prompt */ if (promptmode == 0) { /* PS1 */ free((char*)G.PS1); /* bash uses $PWD value, even if it is set by user. * It uses current dir only if PWD is unset. * We always use current dir. */ G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#'); prompt_str = G.PS1; } else prompt_str = G.PS2; } else prompt_str = (promptmode == 0) ? G.PS1 : G.PS2; debug_printf("result '%s'\n", prompt_str); return prompt_str; } static void get_user_input(struct in_str *i) { int r; const char *prompt_str; prompt_str = setup_prompt_string(i->promptmode); # if ENABLE_FEATURE_EDITING /* Enable command line editing only while a command line * is actually being read */ do { /* Unicode support should be activated even if LANG is set * _during_ shell execution, not only if it was set when * shell was started. Therefore, re-check LANG every time: */ const char *s = get_local_var_value("LC_ALL"); if (!s) s = get_local_var_value("LC_CTYPE"); if (!s) s = get_local_var_value("LANG"); reinit_unicode(s); G.flag_SIGINT = 0; /* buglet: SIGINT will not make new prompt to appear _at once_, * only after <Enter>. (^C will work) */ r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1); /* catch *SIGINT* etc (^C is handled by read_line_input) */ check_and_run_traps(); } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ i->eof_flag = (r < 0); if (i->eof_flag) { /* EOF/error detected */ G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */ G.user_input_buf[1] = '\0'; } # else do { G.flag_SIGINT = 0; if (i->last_char == '\0' || i->last_char == '\n') { /* Why check_and_run_traps here? Try this interactively: * $ trap 'echo INT' INT; (sleep 2; kill -INT $$) & * $ <[enter], repeatedly...> * Without check_and_run_traps, handler never runs. */ check_and_run_traps(); fputs(prompt_str, stdout); } fflush_all(); G.user_input_buf[0] = r = fgetc(i->file); /*G.user_input_buf[1] = '\0'; - already is and never changed */ } while (G.flag_SIGINT); i->eof_flag = (r == EOF); # endif i->p = G.user_input_buf; } #endif /* INTERACTIVE */ /* This is the magic location that prints prompts * and gets data back from the user */ static int FAST_FUNC file_get(struct in_str *i) { int ch; /* If there is data waiting, eat it up */ if (i->p && *i->p) { #if ENABLE_HUSH_INTERACTIVE take_cached: #endif ch = *i->p++; if (i->eof_flag && !*i->p) ch = EOF; /* note: ch is never NUL */ } else { /* need to double check i->file because we might be doing something * more complicated by now, like sourcing or substituting. */ #if ENABLE_HUSH_INTERACTIVE if (G_interactive_fd && i->file == stdin) { do { get_user_input(i); } while (!*i->p); /* need non-empty line */ i->promptmode = 1; /* PS2 */ goto take_cached; } #endif do ch = fgetc(i->file); while (ch == '\0'); } debug_printf("file_get: got '%c' %d\n", ch, ch); i->last_char = ch; return ch; } /* All callers guarantee this routine will never * be used right after a newline, so prompting is not needed. */ static int FAST_FUNC file_peek(struct in_str *i) { int ch; if (i->p && *i->p) { if (i->eof_flag && !i->p[1]) return EOF; return *i->p; /* note: ch is never NUL */ } do ch = fgetc(i->file); while (ch == '\0'); i->eof_flag = (ch == EOF); i->peek_buf[0] = ch; i->peek_buf[1] = '\0'; i->p = i->peek_buf; debug_printf("file_peek: got '%c' %d\n", ch, ch); return ch; } static void setup_file_in_str(struct in_str *i, FILE *f) { memset(i, 0, sizeof(*i)); i->peek = file_peek; i->get = file_get; /* i->promptmode = 0; - PS1 (memset did it) */ i->file = f; /* i->p = NULL; */ } static void setup_string_in_str(struct in_str *i, const char *s) { memset(i, 0, sizeof(*i)); i->peek = static_peek; i->get = static_get; /* i->promptmode = 0; - PS1 (memset did it) */ i->p = s; /* i->eof_flag = 0; */ } /* * o_string support */ #define B_CHUNK (32 * sizeof(char*)) static void o_reset_to_empty_unquoted(o_string *o) { o->length = 0; o->has_quoted_part = 0; if (o->data) o->data[0] = '\0'; } static void o_free(o_string *o) { free(o->data); memset(o, 0, sizeof(*o)); } static ALWAYS_INLINE void o_free_unsafe(o_string *o) { free(o->data); } static void o_grow_by(o_string *o, int len) { if (o->length + len > o->maxlen) { o->maxlen += (2*len > B_CHUNK ? 2*len : B_CHUNK); o->data = xrealloc(o->data, 1 + o->maxlen); } } static void o_addchr(o_string *o, int ch) { debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o); o_grow_by(o, 1); o->data[o->length] = ch; o->length++; o->data[o->length] = '\0'; } static void o_addblock(o_string *o, const char *str, int len) { o_grow_by(o, len); memcpy(&o->data[o->length], str, len); o->length += len; o->data[o->length] = '\0'; } static void o_addstr(o_string *o, const char *str) { o_addblock(o, str, strlen(str)); } #if !BB_MMU static void nommu_addchr(o_string *o, int ch) { if (o) o_addchr(o, ch); } #else # define nommu_addchr(o, str) ((void)0) #endif static void o_addstr_with_NUL(o_string *o, const char *str) { o_addblock(o, str, strlen(str) + 1); } /* * HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side. * Currently, "v='{q,w}'; echo $v" erroneously expands braces in $v. * Apparently, on unquoted $v bash still does globbing * ("v='*.txt'; echo $v" prints all .txt files), * but NOT brace expansion! Thus, there should be TWO independent * quoting mechanisms on $v expansion side: one protects * $v from brace expansion, and other additionally protects "$v" against globbing. * We have only second one. */ #if ENABLE_HUSH_BRACE_EXPANSION # define MAYBE_BRACES "{}" #else # define MAYBE_BRACES "" #endif /* My analysis of quoting semantics tells me that state information * is associated with a destination, not a source. */ static void o_addqchr(o_string *o, int ch) { int sz = 1; char *found = strchr("*?[\\" MAYBE_BRACES, ch); if (found) sz++; o_grow_by(o, sz); if (found) { o->data[o->length] = '\\'; o->length++; } o->data[o->length] = ch; o->length++; o->data[o->length] = '\0'; } static void o_addQchr(o_string *o, int ch) { int sz = 1; if ((o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS) && strchr("*?[\\" MAYBE_BRACES, ch) ) { sz++; o->data[o->length] = '\\'; o->length++; } o_grow_by(o, sz); o->data[o->length] = ch; o->length++; o->data[o->length] = '\0'; } static void o_addqblock(o_string *o, const char *str, int len) { while (len) { char ch; int sz; int ordinary_cnt = strcspn(str, "*?[\\" MAYBE_BRACES); if (ordinary_cnt > len) /* paranoia */ ordinary_cnt = len; o_addblock(o, str, ordinary_cnt); if (ordinary_cnt == len) return; /* NUL is already added by o_addblock */ str += ordinary_cnt; len -= ordinary_cnt + 1; /* we are processing + 1 char below */ ch = *str++; sz = 1; if (ch) { /* it is necessarily one of "*?[\\" MAYBE_BRACES */ sz++; o->data[o->length] = '\\'; o->length++; } o_grow_by(o, sz); o->data[o->length] = ch; o->length++; } o->data[o->length] = '\0'; } static void o_addQblock(o_string *o, const char *str, int len) { if (!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)) { o_addblock(o, str, len); return; } o_addqblock(o, str, len); } static void o_addQstr(o_string *o, const char *str) { o_addQblock(o, str, strlen(str)); } /* A special kind of o_string for $VAR and `cmd` expansion. * It contains char* list[] at the beginning, which is grown in 16 element * increments. Actual string data starts at the next multiple of 16 * (char*). * list[i] contains an INDEX (int!) into this string data. * It means that if list[] needs to grow, data needs to be moved higher up * but list[i]'s need not be modified. * NB: remembering how many list[i]'s you have there is crucial. * o_finalize_list() operation post-processes this structure - calculates * and stores actual char* ptrs in list[]. Oh, it NULL terminates it as well. */ #if DEBUG_EXPAND || DEBUG_GLOB static void debug_print_list(const char *prefix, o_string *o, int n) { char **list = (char**)o->data; int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); int i = 0; indent(); fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n", prefix, list, n, string_start, o->length, o->maxlen, !!(o->o_expflags & EXP_FLAG_GLOB), o->has_quoted_part, !!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); while (i < n) { indent(); fdprintf(2, " list[%d]=%d '%s' %p\n", i, (int)(uintptr_t)list[i], o->data + (int)(uintptr_t)list[i] + string_start, o->data + (int)(uintptr_t)list[i] + string_start); i++; } if (n) { const char *p = o->data + (int)(uintptr_t)list[n - 1] + string_start; indent(); fdprintf(2, " total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data)); } } #else # define debug_print_list(prefix, o, n) ((void)0) #endif /* n = o_save_ptr_helper(str, n) "starts new string" by storing an index value * in list[n] so that it points past last stored byte so far. * It returns n+1. */ static int o_save_ptr_helper(o_string *o, int n) { char **list = (char**)o->data; int string_start; int string_len; if (!o->has_empty_slot) { string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); string_len = o->length - string_start; if (!(n & 0xf)) { /* 0, 0x10, 0x20...? */ debug_printf_list("list[%d]=%d string_start=%d (growing)\n", n, string_len, string_start); /* list[n] points to string_start, make space for 16 more pointers */ o->maxlen += 0x10 * sizeof(list[0]); o->data = xrealloc(o->data, o->maxlen + 1); list = (char**)o->data; memmove(list + n + 0x10, list + n, string_len); o->length += 0x10 * sizeof(list[0]); } else { debug_printf_list("list[%d]=%d string_start=%d\n", n, string_len, string_start); } } else { /* We have empty slot at list[n], reuse without growth */ string_start = ((n+1 + 0xf) & ~0xf) * sizeof(list[0]); /* NB: n+1! */ string_len = o->length - string_start; debug_printf_list("list[%d]=%d string_start=%d (empty slot)\n", n, string_len, string_start); o->has_empty_slot = 0; } o->has_quoted_part = 0; list[n] = (char*)(uintptr_t)string_len; return n + 1; } /* "What was our last o_save_ptr'ed position (byte offset relative o->data)?" */ static int o_get_last_ptr(o_string *o, int n) { char **list = (char**)o->data; int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); return ((int)(uintptr_t)list[n-1]) + string_start; } #if ENABLE_HUSH_BRACE_EXPANSION /* There in a GNU extension, GLOB_BRACE, but it is not usable: * first, it processes even {a} (no commas), second, * I didn't manage to make it return strings when they don't match * existing files. Need to re-implement it. */ /* Helper */ static int glob_needed(const char *s) { while (*s) { if (*s == '\\') { if (!s[1]) return 0; s += 2; continue; } if (*s == '*' || *s == '[' || *s == '?' || *s == '{') return 1; s++; } return 0; } /* Return pointer to next closing brace or to comma */ static const char *next_brace_sub(const char *cp) { unsigned depth = 0; cp++; while (*cp != '\0') { if (*cp == '\\') { if (*++cp == '\0') break; cp++; continue; } if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0)) break; if (*cp++ == '{') depth++; } return *cp != '\0' ? cp : NULL; } /* Recursive brace globber. Note: may garble pattern[]. */ static int glob_brace(char *pattern, o_string *o, int n) { char *new_pattern_buf; const char *begin; const char *next; const char *rest; const char *p; size_t rest_len; debug_printf_glob("glob_brace('%s')\n", pattern); begin = pattern; while (1) { if (*begin == '\0') goto simple_glob; if (*begin == '{') { /* Find the first sub-pattern and at the same time * find the rest after the closing brace */ next = next_brace_sub(begin); if (next == NULL) { /* An illegal expression */ goto simple_glob; } if (*next == '}') { /* "{abc}" with no commas - illegal * brace expr, disregard and skip it */ begin = next + 1; continue; } break; } if (*begin == '\\' && begin[1] != '\0') begin++; begin++; } debug_printf_glob("begin:%s\n", begin); debug_printf_glob("next:%s\n", next); /* Now find the end of the whole brace expression */ rest = next; while (*rest != '}') { rest = next_brace_sub(rest); if (rest == NULL) { /* An illegal expression */ goto simple_glob; } debug_printf_glob("rest:%s\n", rest); } rest_len = strlen(++rest) + 1; /* We are sure the brace expression is well-formed */ /* Allocate working buffer large enough for our work */ new_pattern_buf = xmalloc(strlen(pattern)); /* We have a brace expression. BEGIN points to the opening {, * NEXT points past the terminator of the first element, and REST * points past the final }. We will accumulate result names from * recursive runs for each brace alternative in the buffer using * GLOB_APPEND. */ p = begin + 1; while (1) { /* Construct the new glob expression */ memcpy( mempcpy( mempcpy(new_pattern_buf, /* We know the prefix for all sub-patterns */ pattern, begin - pattern), p, next - p), rest, rest_len); /* Note: glob_brace() may garble new_pattern_buf[]. * That's why we re-copy prefix every time (1st memcpy above). */ n = glob_brace(new_pattern_buf, o, n); if (*next == '}') { /* We saw the last entry */ break; } p = next + 1; next = next_brace_sub(next); } free(new_pattern_buf); return n; simple_glob: { int gr; glob_t globdata; memset(&globdata, 0, sizeof(globdata)); gr = glob(pattern, 0, NULL, &globdata); debug_printf_glob("glob('%s'):%d\n", pattern, gr); if (gr != 0) { if (gr == GLOB_NOMATCH) { globfree(&globdata); /* NB: garbles parameter */ unbackslash(pattern); o_addstr_with_NUL(o, pattern); debug_printf_glob("glob pattern '%s' is literal\n", pattern); return o_save_ptr_helper(o, n); } if (gr == GLOB_NOSPACE) bb_error_msg_and_die(bb_msg_memory_exhausted); /* GLOB_ABORTED? Only happens with GLOB_ERR flag, * but we didn't specify it. Paranoia again. */ bb_error_msg_and_die("glob error %d on '%s'", gr, pattern); } if (globdata.gl_pathv && globdata.gl_pathv[0]) { char **argv = globdata.gl_pathv; while (1) { o_addstr_with_NUL(o, *argv); n = o_save_ptr_helper(o, n); argv++; if (!*argv) break; } } globfree(&globdata); } return n; } /* Performs globbing on last list[], * saving each result as a new list[]. */ static int perform_glob(o_string *o, int n) { char *pattern, *copy; debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data); if (!o->data) return o_save_ptr_helper(o, n); pattern = o->data + o_get_last_ptr(o, n); debug_printf_glob("glob pattern '%s'\n", pattern); if (!glob_needed(pattern)) { /* unbackslash last string in o in place, fix length */ o->length = unbackslash(pattern) - o->data; debug_printf_glob("glob pattern '%s' is literal\n", pattern); return o_save_ptr_helper(o, n); } copy = xstrdup(pattern); /* "forget" pattern in o */ o->length = pattern - o->data; n = glob_brace(copy, o, n); free(copy); if (DEBUG_GLOB) debug_print_list("perform_glob returning", o, n); return n; } #else /* !HUSH_BRACE_EXPANSION */ /* Helper */ static int glob_needed(const char *s) { while (*s) { if (*s == '\\') { if (!s[1]) return 0; s += 2; continue; } if (*s == '*' || *s == '[' || *s == '?') return 1; s++; } return 0; } /* Performs globbing on last list[], * saving each result as a new list[]. */ static int perform_glob(o_string *o, int n) { glob_t globdata; int gr; char *pattern; debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data); if (!o->data) return o_save_ptr_helper(o, n); pattern = o->data + o_get_last_ptr(o, n); debug_printf_glob("glob pattern '%s'\n", pattern); if (!glob_needed(pattern)) { literal: /* unbackslash last string in o in place, fix length */ o->length = unbackslash(pattern) - o->data; debug_printf_glob("glob pattern '%s' is literal\n", pattern); return o_save_ptr_helper(o, n); } memset(&globdata, 0, sizeof(globdata)); /* Can't use GLOB_NOCHECK: it does not unescape the string. * If we glob "*.\*" and don't find anything, we need * to fall back to using literal "*.*", but GLOB_NOCHECK * will return "*.\*"! */ gr = glob(pattern, 0, NULL, &globdata); debug_printf_glob("glob('%s'):%d\n", pattern, gr); if (gr != 0) { if (gr == GLOB_NOMATCH) { globfree(&globdata); goto literal; } if (gr == GLOB_NOSPACE) bb_error_msg_and_die(bb_msg_memory_exhausted); /* GLOB_ABORTED? Only happens with GLOB_ERR flag, * but we didn't specify it. Paranoia again. */ bb_error_msg_and_die("glob error %d on '%s'", gr, pattern); } if (globdata.gl_pathv && globdata.gl_pathv[0]) { char **argv = globdata.gl_pathv; /* "forget" pattern in o */ o->length = pattern - o->data; while (1) { o_addstr_with_NUL(o, *argv); n = o_save_ptr_helper(o, n); argv++; if (!*argv) break; } } globfree(&globdata); if (DEBUG_GLOB) debug_print_list("perform_glob returning", o, n); return n; } #endif /* !HUSH_BRACE_EXPANSION */ /* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered. * Otherwise, just finish current list[] and start new */ static int o_save_ptr(o_string *o, int n) { if (o->o_expflags & EXP_FLAG_GLOB) { /* If o->has_empty_slot, list[n] was already globbed * (if it was requested back then when it was filled) * so don't do that again! */ if (!o->has_empty_slot) return perform_glob(o, n); /* o_save_ptr_helper is inside */ } return o_save_ptr_helper(o, n); } /* "Please convert list[n] to real char* ptrs, and NULL terminate it." */ static char **o_finalize_list(o_string *o, int n) { char **list; int string_start; n = o_save_ptr(o, n); /* force growth for list[n] if necessary */ if (DEBUG_EXPAND) debug_print_list("finalized", o, n); debug_printf_expand("finalized n:%d\n", n); list = (char**)o->data; string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); list[--n] = NULL; while (n) { n--; list[n] = o->data + (int)(uintptr_t)list[n] + string_start; } return list; } static void free_pipe_list(struct pipe *pi); /* Returns pi->next - next pipe in the list */ static struct pipe *free_pipe(struct pipe *pi) { struct pipe *next; int i; debug_printf_clean("free_pipe (pid %d)\n", getpid()); for (i = 0; i < pi->num_cmds; i++) { struct command *command; struct redir_struct *r, *rnext; command = &pi->cmds[i]; debug_printf_clean(" command %d:\n", i); if (command->argv) { if (DEBUG_CLEAN) { int a; char **p; for (a = 0, p = command->argv; *p; a++, p++) { debug_printf_clean(" argv[%d] = %s\n", a, *p); } } free_strings(command->argv); //command->argv = NULL; } /* not "else if": on syntax error, we may have both! */ if (command->group) { debug_printf_clean(" begin group (cmd_type:%d)\n", command->cmd_type); free_pipe_list(command->group); debug_printf_clean(" end group\n"); //command->group = NULL; } /* else is crucial here. * If group != NULL, child_func is meaningless */ #if ENABLE_HUSH_FUNCTIONS else if (command->child_func) { debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func); command->child_func->parent_cmd = NULL; } #endif #if !BB_MMU free(command->group_as_string); //command->group_as_string = NULL; #endif for (r = command->redirects; r; r = rnext) { debug_printf_clean(" redirect %d%s", r->rd_fd, redir_table[r->rd_type].descrip); /* guard against the case >$FOO, where foo is unset or blank */ if (r->rd_filename) { debug_printf_clean(" fname:'%s'\n", r->rd_filename); free(r->rd_filename); //r->rd_filename = NULL; } debug_printf_clean(" rd_dup:%d\n", r->rd_dup); rnext = r->next; free(r); } //command->redirects = NULL; } free(pi->cmds); /* children are an array, they get freed all at once */ //pi->cmds = NULL; #if ENABLE_HUSH_JOB free(pi->cmdtext); //pi->cmdtext = NULL; #endif next = pi->next; free(pi); return next; } static void free_pipe_list(struct pipe *pi) { while (pi) { #if HAS_KEYWORDS debug_printf_clean("pipe reserved word %d\n", pi->res_word); #endif debug_printf_clean("pipe followup code %d\n", pi->followup); pi = free_pipe(pi); } } /*** Parsing routines ***/ #ifndef debug_print_tree static void debug_print_tree(struct pipe *pi, int lvl) { static const char *const PIPE[] = { [PIPE_SEQ] = "SEQ", [PIPE_AND] = "AND", [PIPE_OR ] = "OR" , [PIPE_BG ] = "BG" , }; static const char *RES[] = { [RES_NONE ] = "NONE" , # if ENABLE_HUSH_IF [RES_IF ] = "IF" , [RES_THEN ] = "THEN" , [RES_ELIF ] = "ELIF" , [RES_ELSE ] = "ELSE" , [RES_FI ] = "FI" , # endif # if ENABLE_HUSH_LOOPS [RES_FOR ] = "FOR" , [RES_WHILE] = "WHILE", [RES_UNTIL] = "UNTIL", [RES_DO ] = "DO" , [RES_DONE ] = "DONE" , # endif # if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE [RES_IN ] = "IN" , # endif # if ENABLE_HUSH_CASE [RES_CASE ] = "CASE" , [RES_CASE_IN ] = "CASE_IN" , [RES_MATCH] = "MATCH", [RES_CASE_BODY] = "CASE_BODY", [RES_ESAC ] = "ESAC" , # endif [RES_XXXX ] = "XXXX" , [RES_SNTX ] = "SNTX" , }; static const char *const CMDTYPE[] = { "{}", "()", "[noglob]", # if ENABLE_HUSH_FUNCTIONS "func()", # endif }; int pin, prn; pin = 0; while (pi) { fdprintf(2, "%*spipe %d res_word=%s followup=%d %s\n", lvl*2, "", pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]); prn = 0; while (prn < pi->num_cmds) { struct command *command = &pi->cmds[prn]; char **argv = command->argv; fdprintf(2, "%*s cmd %d assignment_cnt:%d", lvl*2, "", prn, command->assignment_cnt); if (command->group) { fdprintf(2, " group %s: (argv=%p)%s%s\n", CMDTYPE[command->cmd_type], argv # if !BB_MMU , " group_as_string:", command->group_as_string # else , "", "" # endif ); debug_print_tree(command->group, lvl+1); prn++; continue; } if (argv) while (*argv) { fdprintf(2, " '%s'", *argv); argv++; } fdprintf(2, "\n"); prn++; } pi = pi->next; pin++; } } #endif /* debug_print_tree */ static struct pipe *new_pipe(void) { struct pipe *pi; pi = xzalloc(sizeof(struct pipe)); /*pi->followup = 0; - deliberately invalid value */ /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */ return pi; } /* Command (member of a pipe) is complete, or we start a new pipe * if ctx->command is NULL. * No errors possible here. */ static int done_command(struct parse_context *ctx) { /* The command is really already in the pipe structure, so * advance the pipe counter and make a new, null command. */ struct pipe *pi = ctx->pipe; struct command *command = ctx->command; if (command) { if (IS_NULL_CMD(command)) { debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds); goto clear_and_ret; } pi->num_cmds++; debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds); //debug_print_tree(ctx->list_head, 20); } else { debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds); } /* Only real trickiness here is that the uncommitted * command structure is not counted in pi->num_cmds. */ pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1)); ctx->command = command = &pi->cmds[pi->num_cmds]; clear_and_ret: memset(command, 0, sizeof(*command)); return pi->num_cmds; /* used only for 0/nonzero check */ } static void done_pipe(struct parse_context *ctx, pipe_style type) { int not_null; debug_printf_parse("done_pipe entered, followup %d\n", type); /* Close previous command */ not_null = done_command(ctx); ctx->pipe->followup = type; #if HAS_KEYWORDS ctx->pipe->pi_inverted = ctx->ctx_inverted; ctx->ctx_inverted = 0; ctx->pipe->res_word = ctx->ctx_res_w; #endif /* Without this check, even just <enter> on command line generates * tree of three NOPs (!). Which is harmless but annoying. * IOW: it is safe to do it unconditionally. */ if (not_null #if ENABLE_HUSH_IF || ctx->ctx_res_w == RES_FI #endif #if ENABLE_HUSH_LOOPS || ctx->ctx_res_w == RES_DONE || ctx->ctx_res_w == RES_FOR || ctx->ctx_res_w == RES_IN #endif #if ENABLE_HUSH_CASE || ctx->ctx_res_w == RES_ESAC #endif ) { struct pipe *new_p; debug_printf_parse("done_pipe: adding new pipe: " "not_null:%d ctx->ctx_res_w:%d\n", not_null, ctx->ctx_res_w); new_p = new_pipe(); ctx->pipe->next = new_p; ctx->pipe = new_p; /* RES_THEN, RES_DO etc are "sticky" - * they remain set for pipes inside if/while. * This is used to control execution. * RES_FOR and RES_IN are NOT sticky (needed to support * cases where variable or value happens to match a keyword): */ #if ENABLE_HUSH_LOOPS if (ctx->ctx_res_w == RES_FOR || ctx->ctx_res_w == RES_IN) ctx->ctx_res_w = RES_NONE; #endif #if ENABLE_HUSH_CASE if (ctx->ctx_res_w == RES_MATCH) ctx->ctx_res_w = RES_CASE_BODY; if (ctx->ctx_res_w == RES_CASE) ctx->ctx_res_w = RES_CASE_IN; #endif ctx->command = NULL; /* trick done_command below */ /* Create the memory for command, roughly: * ctx->pipe->cmds = new struct command; * ctx->command = &ctx->pipe->cmds[0]; */ done_command(ctx); //debug_print_tree(ctx->list_head, 10); } debug_printf_parse("done_pipe return\n"); } static void initialize_context(struct parse_context *ctx) { memset(ctx, 0, sizeof(*ctx)); ctx->pipe = ctx->list_head = new_pipe(); /* Create the memory for command, roughly: * ctx->pipe->cmds = new struct command; * ctx->command = &ctx->pipe->cmds[0]; */ done_command(ctx); } /* If a reserved word is found and processed, parse context is modified * and 1 is returned. */ #if HAS_KEYWORDS struct reserved_combo { char literal[6]; unsigned char res; unsigned char assignment_flag; int flag; }; enum { FLAG_END = (1 << RES_NONE ), # if ENABLE_HUSH_IF FLAG_IF = (1 << RES_IF ), FLAG_THEN = (1 << RES_THEN ), FLAG_ELIF = (1 << RES_ELIF ), FLAG_ELSE = (1 << RES_ELSE ), FLAG_FI = (1 << RES_FI ), # endif # if ENABLE_HUSH_LOOPS FLAG_FOR = (1 << RES_FOR ), FLAG_WHILE = (1 << RES_WHILE), FLAG_UNTIL = (1 << RES_UNTIL), FLAG_DO = (1 << RES_DO ), FLAG_DONE = (1 << RES_DONE ), FLAG_IN = (1 << RES_IN ), # endif # if ENABLE_HUSH_CASE FLAG_MATCH = (1 << RES_MATCH), FLAG_ESAC = (1 << RES_ESAC ), # endif FLAG_START = (1 << RES_XXXX ), }; static const struct reserved_combo* match_reserved_word(o_string *word) { /* Mostly a list of accepted follow-up reserved words. * FLAG_END means we are done with the sequence, and are ready * to turn the compound list into a command. * FLAG_START means the word must start a new compound list. */ static const struct reserved_combo reserved_list[] = { # if ENABLE_HUSH_IF { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START }, { "then", RES_THEN, MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, { "elif", RES_ELIF, MAYBE_ASSIGNMENT, FLAG_THEN }, { "else", RES_ELSE, MAYBE_ASSIGNMENT, FLAG_FI }, { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, # endif # if ENABLE_HUSH_LOOPS { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, { "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START }, { "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START }, { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, { "do", RES_DO, MAYBE_ASSIGNMENT, FLAG_DONE }, { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, # endif # if ENABLE_HUSH_CASE { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, # endif }; const struct reserved_combo *r; for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) { if (strcmp(word->data, r->literal) == 0) return r; } return NULL; } /* Return 0: not a keyword, 1: keyword */ static int reserved_word(o_string *word, struct parse_context *ctx) { # if ENABLE_HUSH_CASE static const struct reserved_combo reserved_match = { "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC }; # endif const struct reserved_combo *r; if (word->has_quoted_part) return 0; r = match_reserved_word(word); if (!r) return 0; debug_printf("found reserved word %s, res %d\n", r->literal, r->res); # if ENABLE_HUSH_CASE if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) { /* "case word IN ..." - IN part starts first MATCH part */ r = &reserved_match; } else # endif if (r->flag == 0) { /* '!' */ if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */ syntax_error("! ! command"); ctx->ctx_res_w = RES_SNTX; } ctx->ctx_inverted = 1; return 1; } if (r->flag & FLAG_START) { struct parse_context *old; old = xmalloc(sizeof(*old)); debug_printf_parse("push stack %p\n", old); *old = *ctx; /* physical copy */ initialize_context(ctx); ctx->stack = old; } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { syntax_error_at(word->data); ctx->ctx_res_w = RES_SNTX; return 1; } else { /* "{...} fi" is ok. "{...} if" is not * Example: * if { echo foo; } then { echo bar; } fi */ if (ctx->command->group) done_pipe(ctx, PIPE_SEQ); } ctx->ctx_res_w = r->res; ctx->old_flag = r->flag; word->o_assignment = r->assignment_flag; debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]); if (ctx->old_flag & FLAG_END) { struct parse_context *old; done_pipe(ctx, PIPE_SEQ); debug_printf_parse("pop stack %p\n", ctx->stack); old = ctx->stack; old->command->group = ctx->list_head; old->command->cmd_type = CMD_NORMAL; # if !BB_MMU o_addstr(&old->as_string, ctx->as_string.data); o_free_unsafe(&ctx->as_string); old->command->group_as_string = xstrdup(old->as_string.data); debug_printf_parse("pop, remembering as:'%s'\n", old->command->group_as_string); # endif *ctx = *old; /* physical copy */ free(old); } return 1; } #endif /* HAS_KEYWORDS */ /* Word is complete, look at it and update parsing context. * Normal return is 0. Syntax errors return 1. * Note: on return, word is reset, but not o_free'd! */ static int done_word(o_string *word, struct parse_context *ctx) { struct command *command = ctx->command; debug_printf_parse("done_word entered: '%s' %p\n", word->data, command); if (word->length == 0 && !word->has_quoted_part) { debug_printf_parse("done_word return 0: true null, ignored\n"); return 0; } if (ctx->pending_redirect) { /* We do not glob in e.g. >*.tmp case. bash seems to glob here * only if run as "bash", not "sh" */ /* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html * "2.7 Redirection * ...the word that follows the redirection operator * shall be subjected to tilde expansion, parameter expansion, * command substitution, arithmetic expansion, and quote * removal. Pathname expansion shall not be performed * on the word by a non-interactive shell; an interactive * shell may perform it, but shall do so only when * the expansion would result in one word." */ ctx->pending_redirect->rd_filename = xstrdup(word->data); /* Cater for >\file case: * >\a creates file a; >\\a, >"\a", >"\\a" create file \a * Same with heredocs: * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H */ if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) { unbackslash(ctx->pending_redirect->rd_filename); /* Is it <<"HEREDOC"? */ if (word->has_quoted_part) { ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED; } } debug_printf_parse("word stored in rd_filename: '%s'\n", word->data); ctx->pending_redirect = NULL; } else { #if HAS_KEYWORDS # if ENABLE_HUSH_CASE if (ctx->ctx_dsemicolon && strcmp(word->data, "esac") != 0 /* not "... pattern) cmd;; esac" */ ) { /* already done when ctx_dsemicolon was set to 1: */ /* ctx->ctx_res_w = RES_MATCH; */ ctx->ctx_dsemicolon = 0; } else # endif if (!command->argv /* if it's the first word... */ # if ENABLE_HUSH_LOOPS && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ && ctx->ctx_res_w != RES_IN # endif # if ENABLE_HUSH_CASE && ctx->ctx_res_w != RES_CASE # endif ) { int reserved = reserved_word(word, ctx); debug_printf_parse("checking for reserved-ness: %d\n", reserved); if (reserved) { o_reset_to_empty_unquoted(word); debug_printf_parse("done_word return %d\n", (ctx->ctx_res_w == RES_SNTX)); return (ctx->ctx_res_w == RES_SNTX); } # if ENABLE_HUSH_BASH_COMPAT if (strcmp(word->data, "[[") == 0) { command->cmd_type = CMD_SINGLEWORD_NOGLOB; } /* fall through */ # endif } #endif if (command->group) { /* "{ echo foo; } echo bar" - bad */ syntax_error_at(word->data); debug_printf_parse("done_word return 1: syntax error, " "groups and arglists don't mix\n"); return 1; } /* If this word wasn't an assignment, next ones definitely * can't be assignments. Even if they look like ones. */ if (word->o_assignment != DEFINITELY_ASSIGNMENT && word->o_assignment != WORD_IS_KEYWORD ) { word->o_assignment = NOT_ASSIGNMENT; } else { if (word->o_assignment == DEFINITELY_ASSIGNMENT) { command->assignment_cnt++; debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt); } debug_printf_parse("word->o_assignment was:'%s'\n", assignment_flag[word->o_assignment]); word->o_assignment = MAYBE_ASSIGNMENT; } debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]); if (word->has_quoted_part /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) /* (otherwise it's known to be not empty and is already safe) */ ) { /* exclude "$@" - it can expand to no word despite "" */ char *p = word->data; while (p[0] == SPECIAL_VAR_SYMBOL && (p[1] & 0x7f) == '@' && p[2] == SPECIAL_VAR_SYMBOL ) { p += 3; } } command->argv = add_string_to_strings(command->argv, xstrdup(word->data)); debug_print_strings("word appended to argv", command->argv); } #if ENABLE_HUSH_LOOPS if (ctx->ctx_res_w == RES_FOR) { if (word->has_quoted_part || !is_well_formed_var_name(command->argv[0], '\0') ) { /* bash says just "not a valid identifier" */ syntax_error("not a valid identifier in for"); return 1; } /* Force FOR to have just one word (variable name) */ /* NB: basically, this makes hush see "for v in ..." * syntax as if it is "for v; in ...". FOR and IN become * two pipe structs in parse tree. */ done_pipe(ctx, PIPE_SEQ); } #endif #if ENABLE_HUSH_CASE /* Force CASE to have just one word */ if (ctx->ctx_res_w == RES_CASE) { done_pipe(ctx, PIPE_SEQ); } #endif o_reset_to_empty_unquoted(word); debug_printf_parse("done_word return 0\n"); return 0; } /* Peek ahead in the input to find out if we have a "&n" construct, * as in "2>&1", that represents duplicating a file descriptor. * Return: * REDIRFD_CLOSE if >&- "close fd" construct is seen, * REDIRFD_SYNTAX_ERR if syntax error, * REDIRFD_TO_FILE if no & was seen, * or the number found. */ #if BB_MMU #define parse_redir_right_fd(as_string, input) \ parse_redir_right_fd(input) #endif static int parse_redir_right_fd(o_string *as_string, struct in_str *input) { int ch, d, ok; ch = i_peek(input); if (ch != '&') return REDIRFD_TO_FILE; ch = i_getch(input); /* get the & */ nommu_addchr(as_string, ch); ch = i_peek(input); if (ch == '-') { ch = i_getch(input); nommu_addchr(as_string, ch); return REDIRFD_CLOSE; } d = 0; ok = 0; while (ch != EOF && isdigit(ch)) { d = d*10 + (ch-'0'); ok = 1; ch = i_getch(input); nommu_addchr(as_string, ch); ch = i_peek(input); } if (ok) return d; //TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2) bb_error_msg("ambiguous redirect"); return REDIRFD_SYNTAX_ERR; } /* Return code is 0 normal, 1 if a syntax error is detected */ static int parse_redirect(struct parse_context *ctx, int fd, redir_type style, struct in_str *input) { struct command *command = ctx->command; struct redir_struct *redir; struct redir_struct **redirp; int dup_num; dup_num = REDIRFD_TO_FILE; if (style != REDIRECT_HEREDOC) { /* Check for a '>&1' type redirect */ dup_num = parse_redir_right_fd(&ctx->as_string, input); if (dup_num == REDIRFD_SYNTAX_ERR) return 1; } else { int ch = i_peek(input); dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */ if (dup_num) { /* <<-... */ ch = i_getch(input); nommu_addchr(&ctx->as_string, ch); ch = i_peek(input); } } if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) { int ch = i_peek(input); if (ch == '|') { /* >|FILE redirect ("clobbering" >). * Since we do not support "set -o noclobber" yet, * >| and > are the same for now. Just eat |. */ ch = i_getch(input); nommu_addchr(&ctx->as_string, ch); } } /* Create a new redir_struct and append it to the linked list */ redirp = &command->redirects; while ((redir = *redirp) != NULL) { redirp = &(redir->next); } *redirp = redir = xzalloc(sizeof(*redir)); /* redir->next = NULL; */ /* redir->rd_filename = NULL; */ redir->rd_type = style; redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd; debug_printf_parse("redirect type %d %s\n", redir->rd_fd, redir_table[style].descrip); redir->rd_dup = dup_num; if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) { /* Erik had a check here that the file descriptor in question * is legit; I postpone that to "run time" * A "-" representation of "close me" shows up as a -3 here */ debug_printf_parse("duplicating redirect '%d>&%d'\n", redir->rd_fd, redir->rd_dup); } else { /* Set ctx->pending_redirect, so we know what to do at the * end of the next parsed word. */ ctx->pending_redirect = redir; } return 0; } /* If a redirect is immediately preceded by a number, that number is * supposed to tell which file descriptor to redirect. This routine * looks for such preceding numbers. In an ideal world this routine * needs to handle all the following classes of redirects... * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo * * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html * "2.7 Redirection * ... If n is quoted, the number shall not be recognized as part of * the redirection expression. For example: * echo \2>a * writes the character 2 into file a" * We are getting it right by setting ->has_quoted_part on any \<char> * * A -1 return means no valid number was found, * the caller should use the appropriate default for this redirection. */ static int redirect_opt_num(o_string *o) { int num; if (o->data == NULL) return -1; num = bb_strtou(o->data, NULL, 10); if (errno || num < 0) return -1; o_reset_to_empty_unquoted(o); return num; } #if BB_MMU #define fetch_till_str(as_string, input, word, skip_tabs) \ fetch_till_str(input, word, skip_tabs) #endif static char *fetch_till_str(o_string *as_string, struct in_str *input, const char *word, int heredoc_flags) { o_string heredoc = NULL_O_STRING; unsigned past_EOL; int prev = 0; /* not \ */ int ch; goto jump_in; while (1) { ch = i_getch(input); if (ch != EOF) nommu_addchr(as_string, ch); if ((ch == '\n' || ch == EOF) && ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\') ) { if (strcmp(heredoc.data + past_EOL, word) == 0) { heredoc.data[past_EOL] = '\0'; debug_printf_parse("parsed heredoc '%s'\n", heredoc.data); return heredoc.data; } while (ch == '\n') { o_addchr(&heredoc, ch); prev = ch; jump_in: past_EOL = heredoc.length; do { ch = i_getch(input); if (ch != EOF) nommu_addchr(as_string, ch); } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t'); } } if (ch == EOF) { o_free_unsafe(&heredoc); return NULL; } o_addchr(&heredoc, ch); nommu_addchr(as_string, ch); if (prev == '\\' && ch == '\\') /* Correctly handle foo\\<eol> (not a line cont.) */ prev = 0; /* not \ */ else prev = ch; } } /* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs * and load them all. There should be exactly heredoc_cnt of them. */ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_str *input) { struct pipe *pi = ctx->list_head; while (pi && heredoc_cnt) { int i; struct command *cmd = pi->cmds; debug_printf_parse("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n", pi->num_cmds, cmd->argv ? cmd->argv[0] : "NONE"); for (i = 0; i < pi->num_cmds; i++) { struct redir_struct *redir = cmd->redirects; debug_printf_parse("fetch_heredocs: %d cmd argv0:'%s'\n", i, cmd->argv ? cmd->argv[0] : "NONE"); while (redir) { if (redir->rd_type == REDIRECT_HEREDOC) { char *p; redir->rd_type = REDIRECT_HEREDOC2; /* redir->rd_dup is (ab)used to indicate <<- */ p = fetch_till_str(&ctx->as_string, input, redir->rd_filename, redir->rd_dup); if (!p) { syntax_error("unexpected EOF in here document"); return 1; } free(redir->rd_filename); redir->rd_filename = p; heredoc_cnt--; } redir = redir->next; } cmd++; } pi = pi->next; } #if 0 /* Should be 0. If it isn't, it's a parse error */ if (heredoc_cnt) bb_error_msg_and_die("heredoc BUG 2"); #endif return 0; } static int run_list(struct pipe *pi); #if BB_MMU #define parse_stream(pstring, input, end_trigger) \ parse_stream(input, end_trigger) #endif static struct pipe *parse_stream(char **pstring, struct in_str *input, int end_trigger); #if !ENABLE_HUSH_FUNCTIONS #define parse_group(dest, ctx, input, ch) \ parse_group(ctx, input, ch) #endif static int parse_group(o_string *dest, struct parse_context *ctx, struct in_str *input, int ch) { /* dest contains characters seen prior to ( or {. * Typically it's empty, but for function defs, * it contains function name (without '()'). */ struct pipe *pipe_list; int endch; struct command *command = ctx->command; debug_printf_parse("parse_group entered\n"); #if ENABLE_HUSH_FUNCTIONS if (ch == '(' && !dest->has_quoted_part) { if (dest->length) if (done_word(dest, ctx)) return 1; if (!command->argv) goto skip; /* (... */ if (command->argv[1]) { /* word word ... (... */ syntax_error_unexpected_ch('('); return 1; } /* it is "word(..." or "word (..." */ do ch = i_getch(input); while (ch == ' ' || ch == '\t'); if (ch != ')') { syntax_error_unexpected_ch(ch); return 1; } nommu_addchr(&ctx->as_string, ch); do ch = i_getch(input); while (ch == ' ' || ch == '\t' || ch == '\n'); if (ch != '{') { syntax_error_unexpected_ch(ch); return 1; } nommu_addchr(&ctx->as_string, ch); command->cmd_type = CMD_FUNCDEF; goto skip; } #endif #if 0 /* Prevented by caller */ if (command->argv /* word [word]{... */ || dest->length /* word{... */ || dest->has_quoted_part /* ""{... */ ) { syntax_error(NULL); debug_printf_parse("parse_group return 1: " "syntax error, groups and arglists don't mix\n"); return 1; } #endif #if ENABLE_HUSH_FUNCTIONS skip: #endif endch = '}'; if (ch == '(') { endch = ')'; command->cmd_type = CMD_SUBSHELL; } else { /* bash does not allow "{echo...", requires whitespace */ ch = i_getch(input); if (ch != ' ' && ch != '\t' && ch != '\n') { syntax_error_unexpected_ch(ch); return 1; } nommu_addchr(&ctx->as_string, ch); } { #if BB_MMU # define as_string NULL #else char *as_string = NULL; #endif pipe_list = parse_stream(&as_string, input, endch); #if !BB_MMU if (as_string) o_addstr(&ctx->as_string, as_string); #endif /* empty ()/{} or parse error? */ if (!pipe_list || pipe_list == ERR_PTR) { /* parse_stream already emitted error msg */ if (!BB_MMU) free(as_string); debug_printf_parse("parse_group return 1: " "parse_stream returned %p\n", pipe_list); return 1; } command->group = pipe_list; #if !BB_MMU as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */ command->group_as_string = as_string; debug_printf_parse("end of group, remembering as:'%s'\n", command->group_as_string); #endif #undef as_string } debug_printf_parse("parse_group return 0\n"); return 0; /* command remains "open", available for possible redirects */ } #if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS /* Subroutines for copying $(...) and `...` things */ static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote); /* '...' */ static int add_till_single_quote(o_string *dest, struct in_str *input) { while (1) { int ch = i_getch(input); if (ch == EOF) { syntax_error_unterm_ch('\''); return 0; } if (ch == '\'') return 1; o_addchr(dest, ch); } } /* "...\"...`..`...." - do we need to handle "...$(..)..." too? */ static int add_till_double_quote(o_string *dest, struct in_str *input) { while (1) { int ch = i_getch(input); if (ch == EOF) { syntax_error_unterm_ch('"'); return 0; } if (ch == '"') return 1; if (ch == '\\') { /* \x. Copy both chars. */ o_addchr(dest, ch); ch = i_getch(input); } o_addchr(dest, ch); if (ch == '`') { if (!add_till_backquote(dest, input, /*in_dquote:*/ 1)) return 0; o_addchr(dest, ch); continue; } //if (ch == '$') ... } } /* Process `cmd` - copy contents until "`" is seen. Complicated by * \` quoting. * "Within the backquoted style of command substitution, backslash * shall retain its literal meaning, except when followed by: '$', '`', or '\'. * The search for the matching backquote shall be satisfied by the first * backquote found without a preceding backslash; during this search, * if a non-escaped backquote is encountered within a shell comment, * a here-document, an embedded command substitution of the $(command) * form, or a quoted string, undefined results occur. A single-quoted * or double-quoted string that begins, but does not end, within the * "`...`" sequence produces undefined results." * Example Output * echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST */ static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote) { while (1) { int ch = i_getch(input); if (ch == '`') return 1; if (ch == '\\') { /* \x. Copy both unless it is \`, \$, \\ and maybe \" */ ch = i_getch(input); if (ch != '`' && ch != '$' && ch != '\\' && (!in_dquote || ch != '"') ) { o_addchr(dest, '\\'); } } if (ch == EOF) { syntax_error_unterm_ch('`'); return 0; } o_addchr(dest, ch); } } /* Process $(cmd) - copy contents until ")" is seen. Complicated by * quoting and nested ()s. * "With the $(command) style of command substitution, all characters * following the open parenthesis to the matching closing parenthesis * constitute the command. Any valid shell script can be used for command, * except a script consisting solely of redirections which produces * unspecified results." * Example Output * echo $(echo '(TEST)' BEST) (TEST) BEST * echo $(echo 'TEST)' BEST) TEST) BEST * echo $(echo \(\(TEST\) BEST) ((TEST) BEST * * Also adapted to eat ${var%...} and $((...)) constructs, since ... part * can contain arbitrary constructs, just like $(cmd). * In bash compat mode, it needs to also be able to stop on ':' or '/' * for ${var:N[:M]} and ${var/P[/R]} parsing. */ #define DOUBLE_CLOSE_CHAR_FLAG 0x80 static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch) { int ch; char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; # if ENABLE_HUSH_BASH_COMPAT char end_char2 = end_ch >> 8; # endif end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); while (1) { ch = i_getch(input); if (ch == EOF) { syntax_error_unterm_ch(end_ch); return 0; } if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) { if (!dbl) break; /* we look for closing )) of $((EXPR)) */ if (i_peek(input) == end_ch) { i_getch(input); /* eat second ')' */ break; } } o_addchr(dest, ch); if (ch == '(' || ch == '{') { ch = (ch == '(' ? ')' : '}'); if (!add_till_closing_bracket(dest, input, ch)) return 0; o_addchr(dest, ch); continue; } if (ch == '\'') { if (!add_till_single_quote(dest, input)) return 0; o_addchr(dest, ch); continue; } if (ch == '"') { if (!add_till_double_quote(dest, input)) return 0; o_addchr(dest, ch); continue; } if (ch == '`') { if (!add_till_backquote(dest, input, /*in_dquote:*/ 0)) return 0; o_addchr(dest, ch); continue; } if (ch == '\\') { /* \x. Copy verbatim. Important for \(, \) */ ch = i_getch(input); if (ch == EOF) { syntax_error_unterm_ch(')'); return 0; } o_addchr(dest, ch); continue; } } return ch; } #endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS */ /* Return code: 0 for OK, 1 for syntax error */ #if BB_MMU #define parse_dollar(as_string, dest, input, quote_mask) \ parse_dollar(dest, input, quote_mask) #define as_string NULL #endif static int parse_dollar(o_string *as_string, o_string *dest, struct in_str *input, unsigned char quote_mask) { int ch = i_peek(input); /* first character after the $ */ debug_printf_parse("parse_dollar entered: ch='%c'\n", ch); if (isalpha(ch)) { ch = i_getch(input); nommu_addchr(as_string, ch); make_var: o_addchr(dest, SPECIAL_VAR_SYMBOL); while (1) { debug_printf_parse(": '%c'\n", ch); o_addchr(dest, ch | quote_mask); quote_mask = 0; ch = i_peek(input); if (!isalnum(ch) && ch != '_') break; ch = i_getch(input); nommu_addchr(as_string, ch); } o_addchr(dest, SPECIAL_VAR_SYMBOL); } else if (isdigit(ch)) { make_one_char_var: ch = i_getch(input); nommu_addchr(as_string, ch); o_addchr(dest, SPECIAL_VAR_SYMBOL); debug_printf_parse(": '%c'\n", ch); o_addchr(dest, ch | quote_mask); o_addchr(dest, SPECIAL_VAR_SYMBOL); } else switch (ch) { case '$': /* pid */ case '!': /* last bg pid */ case '?': /* last exit code */ case '#': /* number of args */ case '*': /* args */ case '@': /* args */ goto make_one_char_var; case '{': { o_addchr(dest, SPECIAL_VAR_SYMBOL); ch = i_getch(input); /* eat '{' */ nommu_addchr(as_string, ch); ch = i_getch(input); /* first char after '{' */ /* It should be ${?}, or ${#var}, * or even ${?+subst} - operator acting on a special variable, * or the beginning of variable name. */ if (ch == EOF || (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) /* not one of those */ ) { bad_dollar_syntax: syntax_error_unterm_str("${name}"); debug_printf_parse("parse_dollar return 0: unterminated ${name}\n"); return 0; } nommu_addchr(as_string, ch); ch |= quote_mask; /* It's possible to just call add_till_closing_bracket() at this point. * However, this regresses some of our testsuite cases * which check invalid constructs like ${%}. * Oh well... let's check that the var name part is fine... */ while (1) { unsigned pos; o_addchr(dest, ch); debug_printf_parse(": '%c'\n", ch); ch = i_getch(input); nommu_addchr(as_string, ch); if (ch == '}') break; if (!isalnum(ch) && ch != '_') { unsigned end_ch; unsigned char last_ch; /* handle parameter expansions * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 */ if (!strchr(VAR_SUBST_OPS, ch)) /* ${var<bad_char>... */ goto bad_dollar_syntax; /* Eat everything until closing '}' (or ':') */ end_ch = '}'; if (ENABLE_HUSH_BASH_COMPAT && ch == ':' && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input)) ) { /* It's ${var:N[:M]} thing */ end_ch = '}' * 0x100 + ':'; } if (ENABLE_HUSH_BASH_COMPAT && ch == '/' ) { /* It's ${var/[/]pattern[/repl]} thing */ if (i_peek(input) == '/') { /* ${var//pattern[/repl]}? */ i_getch(input); nommu_addchr(as_string, '/'); ch = '\\'; } end_ch = '}' * 0x100 + '/'; } o_addchr(dest, ch); again: if (!BB_MMU) pos = dest->length; #if ENABLE_HUSH_DOLLAR_OPS last_ch = add_till_closing_bracket(dest, input, end_ch); if (last_ch == 0) /* error? */ return 0; #else #error Simple code to only allow ${var} is not implemented #endif if (as_string) { o_addstr(as_string, dest->data + pos); o_addchr(as_string, last_ch); } if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) { /* close the first block: */ o_addchr(dest, SPECIAL_VAR_SYMBOL); /* while parsing N from ${var:N[:M]} * or pattern from ${var/[/]pattern[/repl]} */ if ((end_ch & 0xff) == last_ch) { /* got ':' or '/'- parse the rest */ end_ch = '}'; goto again; } /* got '}' */ if (end_ch == '}' * 0x100 + ':') { /* it's ${var:N} - emulate :999999999 */ o_addstr(dest, "999999999"); } /* else: it's ${var/[/]pattern} */ } break; } } o_addchr(dest, SPECIAL_VAR_SYMBOL); break; } #if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK case '(': { unsigned pos; ch = i_getch(input); nommu_addchr(as_string, ch); # if ENABLE_SH_MATH_SUPPORT if (i_peek(input) == '(') { ch = i_getch(input); nommu_addchr(as_string, ch); o_addchr(dest, SPECIAL_VAR_SYMBOL); o_addchr(dest, /*quote_mask |*/ '+'); if (!BB_MMU) pos = dest->length; if (!add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG)) return 0; /* error */ if (as_string) { o_addstr(as_string, dest->data + pos); o_addchr(as_string, ')'); o_addchr(as_string, ')'); } o_addchr(dest, SPECIAL_VAR_SYMBOL); break; } # endif # if ENABLE_HUSH_TICK o_addchr(dest, SPECIAL_VAR_SYMBOL); o_addchr(dest, quote_mask | '`'); if (!BB_MMU) pos = dest->length; if (!add_till_closing_bracket(dest, input, ')')) return 0; /* error */ if (as_string) { o_addstr(as_string, dest->data + pos); o_addchr(as_string, ')'); } o_addchr(dest, SPECIAL_VAR_SYMBOL); # endif break; } #endif case '_': ch = i_getch(input); nommu_addchr(as_string, ch); ch = i_peek(input); if (isalnum(ch)) { /* it's $_name or $_123 */ ch = '_'; goto make_var; } /* else: it's $_ */ /* TODO: $_ and $-: */ /* $_ Shell or shell script name; or last argument of last command * (if last command wasn't a pipe; if it was, bash sets $_ to ""); * but in command's env, set to full pathname used to invoke it */ /* $- Option flags set by set builtin or shell options (-i etc) */ default: o_addQchr(dest, '$'); } debug_printf_parse("parse_dollar return 1 (ok)\n"); return 1; #undef as_string } #if BB_MMU # if ENABLE_HUSH_BASH_COMPAT #define encode_string(as_string, dest, input, dquote_end, process_bkslash) \ encode_string(dest, input, dquote_end, process_bkslash) # else /* only ${var/pattern/repl} (its pattern part) needs additional mode */ #define encode_string(as_string, dest, input, dquote_end, process_bkslash) \ encode_string(dest, input, dquote_end) # endif #define as_string NULL #else /* !MMU */ # if ENABLE_HUSH_BASH_COMPAT /* all parameters are needed, no macro tricks */ # else #define encode_string(as_string, dest, input, dquote_end, process_bkslash) \ encode_string(as_string, dest, input, dquote_end) # endif #endif static int encode_string(o_string *as_string, o_string *dest, struct in_str *input, int dquote_end, int process_bkslash) { #if !ENABLE_HUSH_BASH_COMPAT const int process_bkslash = 1; #endif int ch; int next; again: ch = i_getch(input); if (ch != EOF) nommu_addchr(as_string, ch); if (ch == dquote_end) { /* may be only '"' or EOF */ debug_printf_parse("encode_string return 1 (ok)\n"); return 1; } /* note: can't move it above ch == dquote_end check! */ if (ch == EOF) { syntax_error_unterm_ch('"'); return 0; /* error */ } next = '\0'; if (ch != '\n') { next = i_peek(input); } debug_printf_parse("\" ch=%c (%d) escape=%d\n", ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); if (process_bkslash && ch == '\\') { if (next == EOF) { syntax_error("\\<eof>"); xfunc_die(); } /* bash: * "The backslash retains its special meaning [in "..."] * only when followed by one of the following characters: * $, `, ", \, or <newline>. A double quote may be quoted * within double quotes by preceding it with a backslash." * NB: in (unquoted) heredoc, above does not apply to ", * therefore we check for it by "next == dquote_end" cond. */ if (next == dquote_end || strchr("$`\\\n", next)) { ch = i_getch(input); /* eat next */ if (ch == '\n') goto again; /* skip \<newline> */ } /* else: ch remains == '\\', and we double it below: */ o_addqchr(dest, ch); /* \c if c is a glob char, else just c */ nommu_addchr(as_string, ch); goto again; } if (ch == '$') { if (!parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80)) { debug_printf_parse("encode_string return 0: " "parse_dollar returned 0 (error)\n"); return 0; } goto again; } #if ENABLE_HUSH_TICK if (ch == '`') { //unsigned pos = dest->length; o_addchr(dest, SPECIAL_VAR_SYMBOL); o_addchr(dest, 0x80 | '`'); if (!add_till_backquote(dest, input, /*in_dquote:*/ dquote_end == '"')) return 0; /* error */ o_addchr(dest, SPECIAL_VAR_SYMBOL); //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos); goto again; } #endif o_addQchr(dest, ch); goto again; #undef as_string } /* * Scan input until EOF or end_trigger char. * Return a list of pipes to execute, or NULL on EOF * or if end_trigger character is met. * On syntax error, exit if shell is not interactive, * reset parsing machinery and start parsing anew, * or return ERR_PTR. */ static struct pipe *parse_stream(char **pstring, struct in_str *input, int end_trigger) { struct parse_context ctx; o_string dest = NULL_O_STRING; int heredoc_cnt; /* Single-quote triggers a bypass of the main loop until its mate is * found. When recursing, quote state is passed in via dest->o_expflags. */ debug_printf_parse("parse_stream entered, end_trigger='%c'\n", end_trigger ? end_trigger : 'X'); debug_enter(); /* If very first arg is "" or '', dest.data may end up NULL. * Preventing this: */ o_addchr(&dest, '\0'); dest.length = 0; /* We used to separate words on $IFS here. This was wrong. * $IFS is used only for word splitting when $var is expanded, * here we should use blank chars as separators, not $IFS */ if (MAYBE_ASSIGNMENT != 0) dest.o_assignment = MAYBE_ASSIGNMENT; initialize_context(&ctx); heredoc_cnt = 0; while (1) { const char *is_blank; const char *is_special; int ch; int next; int redir_fd; redir_type redir_style; ch = i_getch(input); debug_printf_parse(": ch=%c (%d) escape=%d\n", ch, ch, !!(dest.o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); if (ch == EOF) { struct pipe *pi; if (heredoc_cnt) { syntax_error_unterm_str("here document"); goto parse_error; } /* end_trigger == '}' case errors out earlier, * checking only ')' */ if (end_trigger == ')') { syntax_error_unterm_ch('('); goto parse_error; } if (done_word(&dest, &ctx)) { goto parse_error; } o_free(&dest); done_pipe(&ctx, PIPE_SEQ); pi = ctx.list_head; /* If we got nothing... */ /* (this makes bare "&" cmd a no-op. * bash says: "syntax error near unexpected token '&'") */ if (pi->num_cmds == 0 IF_HAS_KEYWORDS(&& pi->res_word == RES_NONE) ) { free_pipe_list(pi); pi = NULL; } #if !BB_MMU debug_printf_parse("as_string '%s'\n", ctx.as_string.data); if (pstring) *pstring = ctx.as_string.data; else o_free_unsafe(&ctx.as_string); #endif debug_leave(); debug_printf_parse("parse_stream return %p\n", pi); return pi; } nommu_addchr(&ctx.as_string, ch); next = '\0'; if (ch != '\n') next = i_peek(input); is_special = "{}<>;&|()#'" /* special outside of "str" */ "\\$\"" IF_HUSH_TICK("`"); /* always special */ /* Are { and } special here? */ if (ctx.command->argv /* word [word]{... - non-special */ || dest.length /* word{... - non-special */ || dest.has_quoted_part /* ""{... - non-special */ || (next != ';' /* }; - special */ && next != ')' /* }) - special */ && next != '&' /* }& and }&& ... - special */ && next != '|' /* }|| ... - special */ && !strchr(defifs, next) /* {word - non-special */ ) ) { /* They are not special, skip "{}" */ is_special += 2; } is_special = strchr(is_special, ch); is_blank = strchr(defifs, ch); if (!is_special && !is_blank) { /* ordinary char */ ordinary_char: o_addQchr(&dest, ch); if ((dest.o_assignment == MAYBE_ASSIGNMENT || dest.o_assignment == WORD_IS_KEYWORD) && ch == '=' && is_well_formed_var_name(dest.data, '=') ) { dest.o_assignment = DEFINITELY_ASSIGNMENT; debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); } continue; } if (is_blank) { if (done_word(&dest, &ctx)) { goto parse_error; } if (ch == '\n') { /* Is this a case when newline is simply ignored? * Some examples: * "cmd | <newline> cmd ..." * "case ... in <newline> word) ..." */ if (IS_NULL_CMD(ctx.command) && dest.length == 0 && !dest.has_quoted_part ) { /* This newline can be ignored. But... * Without check #1, interactive shell * ignores even bare <newline>, * and shows the continuation prompt: * ps1_prompt$ <enter> * ps2> _ <=== wrong, should be ps1 * Without check #2, "cmd & <newline>" * is similarly mistreated. * (BTW, this makes "cmd & cmd" * and "cmd && cmd" non-orthogonal. * Really, ask yourself, why * "cmd && <newline>" doesn't start * cmd but waits for more input? * No reason...) */ struct pipe *pi = ctx.list_head; if (pi->num_cmds != 0 /* check #1 */ && pi->followup != PIPE_BG /* check #2 */ ) { continue; } } /* Treat newline as a command separator. */ done_pipe(&ctx, PIPE_SEQ); debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt); if (heredoc_cnt) { if (fetch_heredocs(heredoc_cnt, &ctx, input)) { goto parse_error; } heredoc_cnt = 0; } dest.o_assignment = MAYBE_ASSIGNMENT; debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); ch = ';'; /* note: if (is_blank) continue; * will still trigger for us */ } } /* "cmd}" or "cmd }..." without semicolon or &: * } is an ordinary char in this case, even inside { cmd; } * Pathological example: { ""}; } should exec "}" cmd */ if (ch == '}') { if (!IS_NULL_CMD(ctx.command) /* cmd } */ || dest.length != 0 /* word} */ || dest.has_quoted_part /* ""} */ ) { goto ordinary_char; } if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */ goto skip_end_trigger; /* else: } does terminate a group */ } if (end_trigger && end_trigger == ch && (ch != ';' || heredoc_cnt == 0) #if ENABLE_HUSH_CASE && (ch != ')' || ctx.ctx_res_w != RES_MATCH || (!dest.has_quoted_part && strcmp(dest.data, "esac") == 0) ) #endif ) { if (heredoc_cnt) { /* This is technically valid: * { cat <<HERE; }; echo Ok * heredoc * heredoc * HERE * but we don't support this. * We require heredoc to be in enclosing {}/(), * if any. */ syntax_error_unterm_str("here document"); goto parse_error; } if (done_word(&dest, &ctx)) { goto parse_error; } done_pipe(&ctx, PIPE_SEQ); dest.o_assignment = MAYBE_ASSIGNMENT; debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); /* Do we sit outside of any if's, loops or case's? */ if (!HAS_KEYWORDS IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0)) ) { o_free(&dest); #if !BB_MMU debug_printf_parse("as_string '%s'\n", ctx.as_string.data); if (pstring) *pstring = ctx.as_string.data; else o_free_unsafe(&ctx.as_string); #endif debug_leave(); debug_printf_parse("parse_stream return %p: " "end_trigger char found\n", ctx.list_head); return ctx.list_head; } } skip_end_trigger: if (is_blank) continue; /* Catch <, > before deciding whether this word is * an assignment. a=1 2>z b=2: b=2 is still assignment */ switch (ch) { case '>': redir_fd = redirect_opt_num(&dest); if (done_word(&dest, &ctx)) { goto parse_error; } redir_style = REDIRECT_OVERWRITE; if (next == '>') { redir_style = REDIRECT_APPEND; ch = i_getch(input); nommu_addchr(&ctx.as_string, ch); } #if 0 else if (next == '(') { syntax_error(">(process) not supported"); goto parse_error; } #endif if (parse_redirect(&ctx, redir_fd, redir_style, input)) goto parse_error; continue; /* back to top of while (1) */ case '<': redir_fd = redirect_opt_num(&dest); if (done_word(&dest, &ctx)) { goto parse_error; } redir_style = REDIRECT_INPUT; if (next == '<') { redir_style = REDIRECT_HEREDOC; heredoc_cnt++; debug_printf_parse("++heredoc_cnt=%d\n", heredoc_cnt); ch = i_getch(input); nommu_addchr(&ctx.as_string, ch); } else if (next == '>') { redir_style = REDIRECT_IO; ch = i_getch(input); nommu_addchr(&ctx.as_string, ch); } #if 0 else if (next == '(') { syntax_error("<(process) not supported"); goto parse_error; } #endif if (parse_redirect(&ctx, redir_fd, redir_style, input)) goto parse_error; continue; /* back to top of while (1) */ case '#': if (dest.length == 0 && !dest.has_quoted_part) { /* skip "#comment" */ while (1) { ch = i_peek(input); if (ch == EOF || ch == '\n') break; i_getch(input); /* note: we do not add it to &ctx.as_string */ } nommu_addchr(&ctx.as_string, '\n'); continue; /* back to top of while (1) */ } break; case '\\': if (next == '\n') { /* It's "\<newline>" */ #if !BB_MMU /* Remove trailing '\' from ctx.as_string */ ctx.as_string.data[--ctx.as_string.length] = '\0'; #endif ch = i_getch(input); /* eat it */ continue; /* back to top of while (1) */ } break; } if (dest.o_assignment == MAYBE_ASSIGNMENT /* check that we are not in word in "a=1 2>word b=1": */ && !ctx.pending_redirect ) { /* ch is a special char and thus this word * cannot be an assignment */ dest.o_assignment = NOT_ASSIGNMENT; debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); } /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ switch (ch) { case '#': /* non-comment #: "echo a#b" etc */ o_addQchr(&dest, ch); break; case '\\': if (next == EOF) { syntax_error("\\<eof>"); xfunc_die(); } ch = i_getch(input); /* note: ch != '\n' (that case does not reach this place) */ o_addchr(&dest, '\\'); /*nommu_addchr(&ctx.as_string, '\\'); - already done */ o_addchr(&dest, ch); nommu_addchr(&ctx.as_string, ch); /* Example: echo Hello \2>file * we need to know that word 2 is quoted */ dest.has_quoted_part = 1; break; case '$': if (!parse_dollar(&ctx.as_string, &dest, input, /*quote_mask:*/ 0)) { debug_printf_parse("parse_stream parse error: " "parse_dollar returned 0 (error)\n"); goto parse_error; } break; case '\'': dest.has_quoted_part = 1; if (next == '\'' && !ctx.pending_redirect) { insert_empty_quoted_str_marker: nommu_addchr(&ctx.as_string, next); i_getch(input); /* eat second ' */ o_addchr(&dest, SPECIAL_VAR_SYMBOL); o_addchr(&dest, SPECIAL_VAR_SYMBOL); } else { while (1) { ch = i_getch(input); if (ch == EOF) { syntax_error_unterm_ch('\''); goto parse_error; } nommu_addchr(&ctx.as_string, ch); if (ch == '\'') break; o_addqchr(&dest, ch); } } break; case '"': dest.has_quoted_part = 1; if (next == '"' && !ctx.pending_redirect) goto insert_empty_quoted_str_marker; if (dest.o_assignment == NOT_ASSIGNMENT) dest.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS; if (!encode_string(&ctx.as_string, &dest, input, '"', /*process_bkslash:*/ 1)) goto parse_error; dest.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; break; #if ENABLE_HUSH_TICK case '`': { USE_FOR_NOMMU(unsigned pos;) o_addchr(&dest, SPECIAL_VAR_SYMBOL); o_addchr(&dest, '`'); USE_FOR_NOMMU(pos = dest.length;) if (!add_till_backquote(&dest, input, /*in_dquote:*/ 0)) goto parse_error; # if !BB_MMU o_addstr(&ctx.as_string, dest.data + pos); o_addchr(&ctx.as_string, '`'); # endif o_addchr(&dest, SPECIAL_VAR_SYMBOL); //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos); break; } #endif case ';': #if ENABLE_HUSH_CASE case_semi: #endif if (done_word(&dest, &ctx)) { goto parse_error; } done_pipe(&ctx, PIPE_SEQ); #if ENABLE_HUSH_CASE /* Eat multiple semicolons, detect * whether it means something special */ while (1) { ch = i_peek(input); if (ch != ';') break; ch = i_getch(input); nommu_addchr(&ctx.as_string, ch); if (ctx.ctx_res_w == RES_CASE_BODY) { ctx.ctx_dsemicolon = 1; ctx.ctx_res_w = RES_MATCH; break; } } #endif new_cmd: /* We just finished a cmd. New one may start * with an assignment */ dest.o_assignment = MAYBE_ASSIGNMENT; debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); break; case '&': if (done_word(&dest, &ctx)) { goto parse_error; } if (next == '&') { ch = i_getch(input); nommu_addchr(&ctx.as_string, ch); done_pipe(&ctx, PIPE_AND); } else { done_pipe(&ctx, PIPE_BG); } goto new_cmd; case '|': if (done_word(&dest, &ctx)) { goto parse_error; } #if ENABLE_HUSH_CASE if (ctx.ctx_res_w == RES_MATCH) break; /* we are in case's "word | word)" */ #endif if (next == '|') { /* || */ ch = i_getch(input); nommu_addchr(&ctx.as_string, ch); done_pipe(&ctx, PIPE_OR); } else { /* we could pick up a file descriptor choice here * with redirect_opt_num(), but bash doesn't do it. * "echo foo 2| cat" yields "foo 2". */ done_command(&ctx); #if !BB_MMU o_reset_to_empty_unquoted(&ctx.as_string); #endif } goto new_cmd; case '(': #if ENABLE_HUSH_CASE /* "case... in [(]word)..." - skip '(' */ if (ctx.ctx_res_w == RES_MATCH && ctx.command->argv == NULL /* not (word|(... */ && dest.length == 0 /* not word(... */ && dest.has_quoted_part == 0 /* not ""(... */ ) { continue; } #endif case '{': if (parse_group(&dest, &ctx, input, ch) != 0) { goto parse_error; } goto new_cmd; case ')': #if ENABLE_HUSH_CASE if (ctx.ctx_res_w == RES_MATCH) goto case_semi; #endif case '}': /* proper use of this character is caught by end_trigger: * if we see {, we call parse_group(..., end_trigger='}') * and it will match } earlier (not here). */ syntax_error_unexpected_ch(ch); goto parse_error; default: if (HUSH_DEBUG) bb_error_msg_and_die("BUG: unexpected %c\n", ch); } } /* while (1) */ parse_error: { struct parse_context *pctx; IF_HAS_KEYWORDS(struct parse_context *p2;) /* Clean up allocated tree. * Sample for finding leaks on syntax error recovery path. * Run it from interactive shell, watch pmap `pidof hush`. * while if false; then false; fi; do break; fi * Samples to catch leaks at execution: * while if (true | {true;}); then echo ok; fi; do break; done * while if (true | {true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done */ pctx = &ctx; do { /* Update pipe/command counts, * otherwise freeing may miss some */ done_pipe(pctx, PIPE_SEQ); debug_printf_clean("freeing list %p from ctx %p\n", pctx->list_head, pctx); debug_print_tree(pctx->list_head, 0); free_pipe_list(pctx->list_head); debug_printf_clean("freed list %p\n", pctx->list_head); #if !BB_MMU o_free_unsafe(&pctx->as_string); #endif IF_HAS_KEYWORDS(p2 = pctx->stack;) if (pctx != &ctx) { free(pctx); } IF_HAS_KEYWORDS(pctx = p2;) } while (HAS_KEYWORDS && pctx); o_free(&dest); G.last_exitcode = 1; #if !BB_MMU if (pstring) *pstring = NULL; #endif debug_leave(); return ERR_PTR; } } /*** Execution routines ***/ /* Expansion can recurse, need forward decls: */ #if !ENABLE_HUSH_BASH_COMPAT /* only ${var/pattern/repl} (its pattern part) needs additional mode */ #define expand_string_to_string(str, do_unbackslash) \ expand_string_to_string(str) #endif static char *expand_string_to_string(const char *str, int do_unbackslash); #if ENABLE_HUSH_TICK static int process_command_subs(o_string *dest, const char *s); #endif /* expand_strvec_to_strvec() takes a list of strings, expands * all variable references within and returns a pointer to * a list of expanded strings, possibly with larger number * of strings. (Think VAR="a b"; echo $VAR). * This new list is allocated as a single malloc block. * NULL-terminated list of char* pointers is at the beginning of it, * followed by strings themselves. * Caller can deallocate entire list by single free(list). */ /* A horde of its helpers come first: */ static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len) { while (--len >= 0) { char c = *str++; #if ENABLE_HUSH_BRACE_EXPANSION if (c == '{' || c == '}') { /* { -> \{, } -> \} */ o_addchr(o, '\\'); /* And now we want to add { or } and continue: * o_addchr(o, c); * continue; * luckily, just falling throught achieves this. */ } #endif o_addchr(o, c); if (c == '\\') { /* \z -> \\\z; \<eol> -> \\<eol> */ o_addchr(o, '\\'); if (len) { len--; o_addchr(o, '\\'); o_addchr(o, *str++); } } } } /* Store given string, finalizing the word and starting new one whenever * we encounter IFS char(s). This is used for expanding variable values. * End-of-string does NOT finalize word: think about 'echo -$VAR-'. * Return in *ended_with_ifs: * 1 - ended with IFS char, else 0 (this includes case of empty str). */ static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const char *str) { int last_is_ifs = 0; while (1) { int word_len; if (!*str) /* EOL - do not finalize word */ break; word_len = strcspn(str, G.ifs); if (word_len) { /* We have WORD_LEN leading non-IFS chars */ if (!(output->o_expflags & EXP_FLAG_GLOB)) { o_addblock(output, str, word_len); } else { /* Protect backslashes against globbing up :) * Example: "v='\*'; echo b$v" prints "b\*" * (and does not try to glob on "*") */ o_addblock_duplicate_backslash(output, str, word_len); /*/ Why can't we do it easier? */ /*o_addblock(output, str, word_len); - WRONG: "v='\*'; echo Z$v" prints "Z*" instead of "Z\*" */ /*o_addqblock(output, str, word_len); - WRONG: "v='*'; echo Z$v" prints "Z*" instead of Z* files */ } last_is_ifs = 0; str += word_len; if (!*str) /* EOL - do not finalize word */ break; } /* We know str here points to at least one IFS char */ last_is_ifs = 1; str += strspn(str, G.ifs); /* skip IFS chars */ if (!*str) /* EOL - do not finalize word */ break; /* Start new word... but not always! */ /* Case "v=' a'; echo ''$v": we do need to finalize empty word: */ if (output->has_quoted_part /* Case "v=' a'; echo $v": * here nothing precedes the space in $v expansion, * therefore we should not finish the word * (IOW: if there *is* word to finalize, only then do it): */ || (n > 0 && output->data[output->length - 1]) ) { o_addchr(output, '\0'); debug_print_list("expand_on_ifs", output, n); n = o_save_ptr(output, n); } } if (ended_with_ifs) *ended_with_ifs = last_is_ifs; debug_print_list("expand_on_ifs[1]", output, n); return n; } /* Helper to expand $((...)) and heredoc body. These act as if * they are in double quotes, with the exception that they are not :). * Just the rules are similar: "expand only $var and `cmd`" * * Returns malloced string. * As an optimization, we return NULL if expansion is not needed. */ #if !ENABLE_HUSH_BASH_COMPAT /* only ${var/pattern/repl} (its pattern part) needs additional mode */ #define encode_then_expand_string(str, process_bkslash, do_unbackslash) \ encode_then_expand_string(str) #endif static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash) { char *exp_str; struct in_str input; o_string dest = NULL_O_STRING; if (!strchr(str, '$') && !strchr(str, '\\') #if ENABLE_HUSH_TICK && !strchr(str, '`') #endif ) { return NULL; } /* We need to expand. Example: * echo $(($a + `echo 1`)) $((1 + $((2)) )) */ setup_string_in_str(&input, str); encode_string(NULL, &dest, &input, EOF, process_bkslash); //TODO: error check (encode_string returns 0 on error)? //bb_error_msg("'%s' -> '%s'", str, dest.data); exp_str = expand_string_to_string(dest.data, /*unbackslash:*/ do_unbackslash); //bb_error_msg("'%s' -> '%s'", dest.data, exp_str); o_free_unsafe(&dest); return exp_str; } #if ENABLE_SH_MATH_SUPPORT static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) { arith_state_t math_state; arith_t res; char *exp_str; math_state.lookupvar = get_local_var_value; math_state.setvar = set_local_var_from_halves; //math_state.endofname = endofname; exp_str = encode_then_expand_string(arg, /*process_bkslash:*/ 1, /*unbackslash:*/ 1); res = arith(&math_state, exp_str ? exp_str : arg); free(exp_str); if (errmsg_p) *errmsg_p = math_state.errmsg; if (math_state.errmsg) die_if_script(math_state.errmsg); return res; } #endif #if ENABLE_HUSH_BASH_COMPAT /* ${var/[/]pattern[/repl]} helpers */ static char *strstr_pattern(char *val, const char *pattern, int *size) { while (1) { char *end = scan_and_match(val, pattern, SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF); debug_printf_varexp("val:'%s' pattern:'%s' end:'%s'\n", val, pattern, end); if (end) { *size = end - val; return val; } if (*val == '\0') return NULL; /* Optimization: if "*pat" did not match the start of "string", * we know that "tring", "ring" etc will not match too: */ if (pattern[0] == '*') return NULL; val++; } } static char *replace_pattern(char *val, const char *pattern, const char *repl, char exp_op) { char *result = NULL; unsigned res_len = 0; unsigned repl_len = strlen(repl); while (1) { int size; char *s = strstr_pattern(val, pattern, &size); if (!s) break; result = xrealloc(result, res_len + (s - val) + repl_len + 1); memcpy(result + res_len, val, s - val); res_len += s - val; strcpy(result + res_len, repl); res_len += repl_len; debug_printf_varexp("val:'%s' s:'%s' result:'%s'\n", val, s, result); val = s + size; if (exp_op == '/') break; } if (val[0] && result) { result = xrealloc(result, res_len + strlen(val) + 1); strcpy(result + res_len, val); debug_printf_varexp("val:'%s' result:'%s'\n", val, result); } debug_printf_varexp("result:'%s'\n", result); return result; } #endif /* Helper: * Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct. */ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp) { const char *val = NULL; char *to_be_freed = NULL; char *p = *pp; char *var; char first_char; char exp_op; char exp_save = exp_save; /* for compiler */ char *exp_saveptr; /* points to expansion operator */ char *exp_word = exp_word; /* for compiler */ char arg0; *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */ var = arg; exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL; arg0 = arg[0]; first_char = arg[0] = arg0 & 0x7f; exp_op = 0; if (first_char == '#' /* ${#... */ && arg[1] && !exp_saveptr /* not ${#} and not ${#<op_char>...} */ ) { /* It must be length operator: ${#var} */ var++; exp_op = 'L'; } else { /* Maybe handle parameter expansion */ if (exp_saveptr /* if 2nd char is one of expansion operators */ && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */ ) { /* ${?:0}, ${#[:]%0} etc */ exp_saveptr = var + 1; } else { /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */ exp_saveptr = var+1 + strcspn(var+1, VAR_ENCODED_SUBST_OPS); } exp_op = exp_save = *exp_saveptr; if (exp_op) { exp_word = exp_saveptr + 1; if (exp_op == ':') { exp_op = *exp_word++; //TODO: try ${var:} and ${var:bogus} in non-bash config if (ENABLE_HUSH_BASH_COMPAT && (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op)) ) { /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */ exp_op = ':'; exp_word--; } } *exp_saveptr = '\0'; } /* else: it's not an expansion op, but bare ${var} */ } /* Look up the variable in question */ if (isdigit(var[0])) { /* parse_dollar should have vetted var for us */ int n = xatoi_positive(var); if (n < G.global_argc) val = G.global_argv[n]; /* else val remains NULL: $N with too big N */ } else { switch (var[0]) { case '$': /* pid */ val = utoa(G.root_pid); break; case '!': /* bg pid */ val = G.last_bg_pid ? utoa(G.last_bg_pid) : ""; break; case '?': /* exitcode */ val = utoa(G.last_exitcode); break; case '#': /* argc */ val = utoa(G.global_argc ? G.global_argc-1 : 0); break; default: val = get_local_var_value(var); } } /* Handle any expansions */ if (exp_op == 'L') { debug_printf_expand("expand: length(%s)=", val); val = utoa(val ? strlen(val) : 0); debug_printf_expand("%s\n", val); } else if (exp_op) { if (exp_op == '%' || exp_op == '#') { /* Standard-mandated substring removal ops: * ${parameter%word} - remove smallest suffix pattern * ${parameter%%word} - remove largest suffix pattern * ${parameter#word} - remove smallest prefix pattern * ${parameter##word} - remove largest prefix pattern * * Word is expanded to produce a glob pattern. * Then var's value is matched to it and matching part removed. */ if (val && val[0]) { char *t; char *exp_exp_word; char *loc; unsigned scan_flags = pick_scan(exp_op, *exp_word); if (exp_op == *exp_word) /* ## or %% */ exp_word++; exp_exp_word = encode_then_expand_string(exp_word, /*process_bkslash:*/ 1, /*unbackslash:*/ 1); if (exp_exp_word) exp_word = exp_exp_word; /* HACK ALERT. We depend here on the fact that * G.global_argv and results of utoa and get_local_var_value * are actually in writable memory: * scan_and_match momentarily stores NULs there. */ t = (char*)val; loc = scan_and_match(t, exp_word, scan_flags); //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'", // exp_op, t, exp_word, loc); free(exp_exp_word); if (loc) { /* match was found */ if (scan_flags & SCAN_MATCH_LEFT_HALF) /* #[#] */ val = loc; /* take right part */ else /* %[%] */ val = to_be_freed = xstrndup(val, loc - val); /* left */ } } } #if ENABLE_HUSH_BASH_COMPAT else if (exp_op == '/' || exp_op == '\\') { /* It's ${var/[/]pattern[/repl]} thing. * Note that in encoded form it has TWO parts: * var/pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL> * and if // is used, it is encoded as \: * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL> */ /* Empty variable always gives nothing: */ // "v=''; echo ${v/*/w}" prints "", not "w" if (val && val[0]) { /* pattern uses non-standard expansion. * repl should be unbackslashed and globbed * by the usual expansion rules: * >az; >bz; * v='a bz'; echo "${v/a*z/a*z}" prints "a*z" * v='a bz'; echo "${v/a*z/\z}" prints "\z" * v='a bz'; echo ${v/a*z/a*z} prints "az" * v='a bz'; echo ${v/a*z/\z} prints "z" * (note that a*z _pattern_ is never globbed!) */ char *pattern, *repl, *t; pattern = encode_then_expand_string(exp_word, /*process_bkslash:*/ 0, /*unbackslash:*/ 0); if (!pattern) pattern = xstrdup(exp_word); debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern); *p++ = SPECIAL_VAR_SYMBOL; exp_word = p; p = strchr(p, SPECIAL_VAR_SYMBOL); *p = '\0'; repl = encode_then_expand_string(exp_word, /*process_bkslash:*/ arg0 & 0x80, /*unbackslash:*/ 1); debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl); /* HACK ALERT. We depend here on the fact that * G.global_argv and results of utoa and get_local_var_value * are actually in writable memory: * replace_pattern momentarily stores NULs there. */ t = (char*)val; to_be_freed = replace_pattern(t, pattern, (repl ? repl : exp_word), exp_op); if (to_be_freed) /* at least one replace happened */ val = to_be_freed; free(pattern); free(repl); } } #endif else if (exp_op == ':') { #if ENABLE_HUSH_BASH_COMPAT && ENABLE_SH_MATH_SUPPORT /* It's ${var:N[:M]} bashism. * Note that in encoded form it has TWO parts: * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> */ arith_t beg, len; const char *errmsg; beg = expand_and_evaluate_arith(exp_word, &errmsg); if (errmsg) goto arith_err; debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); *p++ = SPECIAL_VAR_SYMBOL; exp_word = p; p = strchr(p, SPECIAL_VAR_SYMBOL); *p = '\0'; len = expand_and_evaluate_arith(exp_word, &errmsg); if (errmsg) goto arith_err; debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); if (len >= 0) { /* bash compat: len < 0 is illegal */ if (beg < 0) /* bash compat */ beg = 0; debug_printf_varexp("from val:'%s'\n", val); if (len == 0 || !val || beg >= strlen(val)) { arith_err: val = NULL; } else { /* Paranoia. What if user entered 9999999999999 * which fits in arith_t but not int? */ if (len >= INT_MAX) len = INT_MAX; val = to_be_freed = xstrndup(val + beg, len); } debug_printf_varexp("val:'%s'\n", val); } else #endif { die_if_script("malformed ${%s:...}", var); val = NULL; } } else { /* one of "-=+?" */ /* Standard-mandated substitution ops: * ${var?word} - indicate error if unset * If var is unset, word (or a message indicating it is unset * if word is null) is written to standard error * and the shell exits with a non-zero exit status. * Otherwise, the value of var is substituted. * ${var-word} - use default value * If var is unset, word is substituted. * ${var=word} - assign and use default value * If var is unset, word is assigned to var. * In all cases, final value of var is substituted. * ${var+word} - use alternative value * If var is unset, null is substituted. * Otherwise, word is substituted. * * Word is subjected to tilde expansion, parameter expansion, * command substitution, and arithmetic expansion. * If word is not needed, it is not expanded. * * Colon forms (${var:-word}, ${var:=word} etc) do the same, * but also treat null var as if it is unset. */ int use_word = (!val || ((exp_save == ':') && !val[0])); if (exp_op == '+') use_word = !use_word; debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, (exp_save == ':') ? "true" : "false", use_word); if (use_word) { to_be_freed = encode_then_expand_string(exp_word, /*process_bkslash:*/ 1, /*unbackslash:*/ 1); if (to_be_freed) exp_word = to_be_freed; if (exp_op == '?') { /* mimic bash message */ die_if_script("%s: %s", var, exp_word[0] ? exp_word : "parameter null or not set" ); //TODO: how interactive bash aborts expansion mid-command? } else { val = exp_word; } if (exp_op == '=') { /* ${var=[word]} or ${var:=[word]} */ if (isdigit(var[0]) || var[0] == '#') { /* mimic bash message */ die_if_script("$%s: cannot assign in this way", var); val = NULL; } else { char *new_var = xasprintf("%s=%s", var, val); set_local_var(new_var, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); } } } } /* one of "-=+?" */ *exp_saveptr = exp_save; } /* if (exp_op) */ arg[0] = arg0; *pp = p; *to_be_freed_pp = to_be_freed; return val; } /* Expand all variable references in given string, adding words to list[] * at n, n+1,... positions. Return updated n (so that list[n] is next one * to be filled). This routine is extremely tricky: has to deal with * variables/parameters with whitespace, $* and $@, and constructs like * 'echo -$*-'. If you play here, you must run testsuite afterwards! */ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) { /* output->o_expflags & EXP_FLAG_SINGLEWORD (0x80) if we are in * expansion of right-hand side of assignment == 1-element expand. */ char cant_be_null = 0; /* only bit 0x80 matters */ int ended_in_ifs = 0; /* did last unquoted expansion end with IFS chars? */ char *p; debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg, !!(output->o_expflags & EXP_FLAG_SINGLEWORD)); debug_print_list("expand_vars_to_list", output, n); n = o_save_ptr(output, n); debug_print_list("expand_vars_to_list[0]", output, n); while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { char first_ch; char *to_be_freed = NULL; const char *val = NULL; #if ENABLE_HUSH_TICK o_string subst_result = NULL_O_STRING; #endif #if ENABLE_SH_MATH_SUPPORT char arith_buf[sizeof(arith_t)*3 + 2]; #endif if (ended_in_ifs) { o_addchr(output, '\0'); n = o_save_ptr(output, n); ended_in_ifs = 0; } o_addblock(output, arg, p - arg); debug_print_list("expand_vars_to_list[1]", output, n); arg = ++p; p = strchr(p, SPECIAL_VAR_SYMBOL); /* Fetch special var name (if it is indeed one of them) * and quote bit, force the bit on if singleword expansion - * important for not getting v=$@ expand to many words. */ first_ch = arg[0] | (output->o_expflags & EXP_FLAG_SINGLEWORD); /* Is this variable quoted and thus expansion can't be null? * "$@" is special. Even if quoted, it can still * expand to nothing (not even an empty string), * thus it is excluded. */ if ((first_ch & 0x7f) != '@') cant_be_null |= first_ch; switch (first_ch & 0x7f) { /* Highest bit in first_ch indicates that var is double-quoted */ case '*': case '@': { int i; if (!G.global_argv[1]) break; i = 1; cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */ if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ while (G.global_argv[i]) { n = expand_on_ifs(NULL, output, n, G.global_argv[i]); debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1); if (G.global_argv[i++][0] && G.global_argv[i]) { /* this argv[] is not empty and not last: * put terminating NUL, start new word */ o_addchr(output, '\0'); debug_print_list("expand_vars_to_list[2]", output, n); n = o_save_ptr(output, n); debug_print_list("expand_vars_to_list[3]", output, n); } } } else /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....' * and in this case should treat it like '$*' - see 'else...' below */ if (first_ch == ('@'|0x80) /* quoted $@ */ && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */ ) { while (1) { o_addQstr(output, G.global_argv[i]); if (++i >= G.global_argc) break; o_addchr(output, '\0'); debug_print_list("expand_vars_to_list[4]", output, n); n = o_save_ptr(output, n); } } else { /* quoted $* (or v="$@" case): add as one word */ while (1) { o_addQstr(output, G.global_argv[i]); if (!G.global_argv[++i]) break; if (G.ifs[0]) o_addchr(output, G.ifs[0]); } output->has_quoted_part = 1; } break; } case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */ /* "Empty variable", used to make "" etc to not disappear */ output->has_quoted_part = 1; arg++; cant_be_null = 0x80; break; #if ENABLE_HUSH_TICK case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ arg++; /* Can't just stuff it into output o_string, * expanded result may need to be globbed * and $IFS-splitted */ debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch); G.last_exitcode = process_command_subs(&subst_result, arg); debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data); val = subst_result.data; goto store_val; #endif #if ENABLE_SH_MATH_SUPPORT case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */ arith_t res; arg++; /* skip '+' */ *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch); res = expand_and_evaluate_arith(arg, NULL); debug_printf_subst("ARITH RES '"ARITH_FMT"'\n", res); sprintf(arith_buf, ARITH_FMT, res); val = arith_buf; break; } #endif default: val = expand_one_var(&to_be_freed, arg, &p); IF_HUSH_TICK(store_val:) if (!(first_ch & 0x80)) { /* unquoted $VAR */ debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); if (val && val[0]) { n = expand_on_ifs(&ended_in_ifs, output, n, val); val = NULL; } } else { /* quoted $VAR, val will be appended below */ output->has_quoted_part = 1; debug_printf_expand("quoted '%s', output->o_escape:%d\n", val, !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); } break; } /* switch (char after <SPECIAL_VAR_SYMBOL>) */ if (val && val[0]) { o_addQstr(output, val); } free(to_be_freed); /* Restore NULL'ed SPECIAL_VAR_SYMBOL. * Do the check to avoid writing to a const string. */ if (*p != SPECIAL_VAR_SYMBOL) *p = SPECIAL_VAR_SYMBOL; #if ENABLE_HUSH_TICK o_free(&subst_result); #endif arg = ++p; } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */ if (arg[0]) { if (ended_in_ifs) { o_addchr(output, '\0'); n = o_save_ptr(output, n); } debug_print_list("expand_vars_to_list[a]", output, n); /* this part is literal, and it was already pre-quoted * if needed (much earlier), do not use o_addQstr here! */ o_addstr_with_NUL(output, arg); debug_print_list("expand_vars_to_list[b]", output, n); } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ && !(cant_be_null & 0x80) /* and all vars were not quoted. */ ) { n--; /* allow to reuse list[n] later without re-growth */ output->has_empty_slot = 1; } else { o_addchr(output, '\0'); } return n; } static char **expand_variables(char **argv, unsigned expflags) { int n; char **list; o_string output = NULL_O_STRING; output.o_expflags = expflags; n = 0; while (*argv) { n = expand_vars_to_list(&output, n, *argv); argv++; } debug_print_list("expand_variables", &output, n); /* output.data (malloced in one block) gets returned in "list" */ list = o_finalize_list(&output, n); debug_print_strings("expand_variables[1]", list); return list; } static char **expand_strvec_to_strvec(char **argv) { return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); } #if ENABLE_HUSH_BASH_COMPAT static char **expand_strvec_to_strvec_singleword_noglob(char **argv) { return expand_variables(argv, EXP_FLAG_SINGLEWORD); } #endif /* Used for expansion of right hand of assignments, * $((...)), heredocs, variable espansion parts. * * NB: should NOT do globbing! * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*" */ static char *expand_string_to_string(const char *str, int do_unbackslash) { #if !ENABLE_HUSH_BASH_COMPAT const int do_unbackslash = 1; #endif char *argv[2], **list; debug_printf_expand("string_to_string<='%s'\n", str); /* This is generally an optimization, but it also * handles "", which otherwise trips over !list[0] check below. * (is this ever happens that we actually get str="" here?) */ if (!strchr(str, SPECIAL_VAR_SYMBOL) && !strchr(str, '\\')) { //TODO: Can use on strings with \ too, just unbackslash() them? debug_printf_expand("string_to_string(fast)=>'%s'\n", str); return xstrdup(str); } argv[0] = (char*)str; argv[1] = NULL; list = expand_variables(argv, do_unbackslash ? EXP_FLAG_ESC_GLOB_CHARS | EXP_FLAG_SINGLEWORD : EXP_FLAG_SINGLEWORD ); if (HUSH_DEBUG) if (!list[0] || list[1]) bb_error_msg_and_die("BUG in varexp2"); /* actually, just move string 2*sizeof(char*) bytes back */ overlapping_strcpy((char*)list, list[0]); if (do_unbackslash) unbackslash((char*)list); debug_printf_expand("string_to_string=>'%s'\n", (char*)list); return (char*)list; } /* Used for "eval" builtin */ static char* expand_strvec_to_string(char **argv) { char **list; list = expand_variables(argv, EXP_FLAG_SINGLEWORD); /* Convert all NULs to spaces */ if (list[0]) { int n = 1; while (list[n]) { if (HUSH_DEBUG) if (list[n-1] + strlen(list[n-1]) + 1 != list[n]) bb_error_msg_and_die("BUG in varexp3"); /* bash uses ' ' regardless of $IFS contents */ list[n][-1] = ' '; n++; } } overlapping_strcpy((char*)list, list[0]); debug_printf_expand("strvec_to_string='%s'\n", (char*)list); return (char*)list; } static char **expand_assignments(char **argv, int count) { int i; char **p; G.expanded_assignments = p = NULL; /* Expand assignments into one string each */ for (i = 0; i < count; i++) { G.expanded_assignments = p = add_string_to_strings(p, expand_string_to_string(argv[i], /*unbackslash:*/ 1)); } G.expanded_assignments = NULL; return p; } static void switch_off_special_sigs(unsigned mask) { unsigned sig = 0; while ((mask >>= 1) != 0) { sig++; if (!(mask & 1)) continue; if (G.traps) { if (G.traps[sig] && !G.traps[sig][0]) /* trap is '', has to remain SIG_IGN */ continue; free(G.traps[sig]); G.traps[sig] = NULL; } /* We are here only if no trap or trap was not '' */ install_sighandler(sig, SIG_DFL); } } #if BB_MMU /* never called */ void re_execute_shell(char ***to_free, const char *s, char *g_argv0, char **g_argv, char **builtin_argv) NORETURN; static void reset_traps_to_defaults(void) { /* This function is always called in a child shell * after fork (not vfork, NOMMU doesn't use this function). */ unsigned sig; unsigned mask; /* Child shells are not interactive. * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling. * Testcase: (while :; do :; done) + ^Z should background. * Same goes for SIGTERM, SIGHUP, SIGINT. */ mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask; if (!G.traps && !mask) return; /* already no traps and no special sigs */ /* Switch off special sigs */ switch_off_special_sigs(mask); #if ENABLE_HUSH_JOB G_fatal_sig_mask = 0; #endif G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS; /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS * remain set in G.special_sig_mask */ if (!G.traps) return; /* Reset all sigs to default except ones with empty traps */ for (sig = 0; sig < NSIG; sig++) { if (!G.traps[sig]) continue; /* no trap: nothing to do */ if (!G.traps[sig][0]) continue; /* empty trap: has to remain SIG_IGN */ /* sig has non-empty trap, reset it: */ free(G.traps[sig]); G.traps[sig] = NULL; /* There is no signal for trap 0 (EXIT) */ if (sig == 0) continue; install_sighandler(sig, pick_sighandler(sig)); } } #else /* !BB_MMU */ static void re_execute_shell(char ***to_free, const char *s, char *g_argv0, char **g_argv, char **builtin_argv) NORETURN; static void re_execute_shell(char ***to_free, const char *s, char *g_argv0, char **g_argv, char **builtin_argv) { # define NOMMU_HACK_FMT ("-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x")) /* delims + 2 * (number of bytes in printed hex numbers) */ char param_buf[sizeof(NOMMU_HACK_FMT) + 2 * (sizeof(int)*6 + sizeof(long long)*1)]; char *heredoc_argv[4]; struct variable *cur; # if ENABLE_HUSH_FUNCTIONS struct function *funcp; # endif char **argv, **pp; unsigned cnt; unsigned long long empty_trap_mask; if (!g_argv0) { /* heredoc */ argv = heredoc_argv; argv[0] = (char *) G.argv0_for_re_execing; argv[1] = (char *) "-<"; argv[2] = (char *) s; argv[3] = NULL; pp = &argv[3]; /* used as pointer to empty environment */ goto do_exec; } cnt = 0; pp = builtin_argv; if (pp) while (*pp++) cnt++; empty_trap_mask = 0; if (G.traps) { int sig; for (sig = 1; sig < NSIG; sig++) { if (G.traps[sig] && !G.traps[sig][0]) empty_trap_mask |= 1LL << sig; } } sprintf(param_buf, NOMMU_HACK_FMT , (unsigned) G.root_pid , (unsigned) G.root_ppid , (unsigned) G.last_bg_pid , (unsigned) G.last_exitcode , cnt , empty_trap_mask IF_HUSH_LOOPS(, G.depth_of_loop) ); # undef NOMMU_HACK_FMT /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<etc...> <vars...> <funcs...> * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL */ cnt += 6; for (cur = G.top_var; cur; cur = cur->next) { if (!cur->flg_export || cur->flg_read_only) cnt += 2; } # if ENABLE_HUSH_FUNCTIONS for (funcp = G.top_func; funcp; funcp = funcp->next) cnt += 3; # endif pp = g_argv; while (*pp++) cnt++; *to_free = argv = pp = xzalloc(sizeof(argv[0]) * cnt); *pp++ = (char *) G.argv0_for_re_execing; *pp++ = param_buf; for (cur = G.top_var; cur; cur = cur->next) { if (strcmp(cur->varstr, hush_version_str) == 0) continue; if (cur->flg_read_only) { *pp++ = (char *) "-R"; *pp++ = cur->varstr; } else if (!cur->flg_export) { *pp++ = (char *) "-V"; *pp++ = cur->varstr; } } # if ENABLE_HUSH_FUNCTIONS for (funcp = G.top_func; funcp; funcp = funcp->next) { *pp++ = (char *) "-F"; *pp++ = funcp->name; *pp++ = funcp->body_as_string; } # endif /* We can pass activated traps here. Say, -Tnn:trap_string * * However, POSIX says that subshells reset signals with traps * to SIG_DFL. * I tested bash-3.2 and it not only does that with true subshells * of the form ( list ), but with any forked children shells. * I set trap "echo W" WINCH; and then tried: * * { echo 1; sleep 20; echo 2; } & * while true; do echo 1; sleep 20; echo 2; break; done & * true | { echo 1; sleep 20; echo 2; } | cat * * In all these cases sending SIGWINCH to the child shell * did not run the trap. If I add trap "echo V" WINCH; * _inside_ group (just before echo 1), it works. * * I conclude it means we don't need to pass active traps here. */ *pp++ = (char *) "-c"; *pp++ = (char *) s; if (builtin_argv) { while (*++builtin_argv) *pp++ = *builtin_argv; *pp++ = (char *) ""; } *pp++ = g_argv0; while (*g_argv) *pp++ = *g_argv++; /* *pp = NULL; - is already there */ pp = environ; do_exec: debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s); /* Don't propagate SIG_IGN to the child */ if (SPECIAL_JOBSTOP_SIGS != 0) switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); execve(bb_busybox_exec_path, argv, pp); /* Fallback. Useful for init=/bin/hush usage etc */ if (argv[0][0] == '/') execve(argv[0], argv, pp); xfunc_error_retval = 127; bb_error_msg_and_die("can't re-execute the shell"); } #endif /* !BB_MMU */ static int run_and_free_list(struct pipe *pi); /* Executing from string: eval, sh -c '...' * or from file: /etc/profile, . file, sh <script>, sh (intereactive) * end_trigger controls how often we stop parsing * NUL: parse all, execute, return * ';': parse till ';' or newline, execute, repeat till EOF */ static void parse_and_run_stream(struct in_str *inp, int end_trigger) { /* Why we need empty flag? * An obscure corner case "false; ``; echo $?": * empty command in `` should still set $? to 0. * But we can't just set $? to 0 at the start, * this breaks "false; echo `echo $?`" case. */ bool empty = 1; while (1) { struct pipe *pipe_list; #if ENABLE_HUSH_INTERACTIVE if (end_trigger == ';') inp->promptmode = 0; /* PS1 */ #endif pipe_list = parse_stream(NULL, inp, end_trigger); if (!pipe_list || pipe_list == ERR_PTR) { /* EOF/error */ /* If we are in "big" script * (not in `cmd` or something similar)... */ if (pipe_list == ERR_PTR && end_trigger == ';') { /* Discard cached input (rest of line) */ int ch = inp->last_char; while (ch != EOF && ch != '\n') { //bb_error_msg("Discarded:'%c'", ch); ch = i_getch(inp); } /* Force prompt */ inp->p = NULL; /* This stream isn't empty */ empty = 0; continue; } if (!pipe_list && empty) G.last_exitcode = 0; break; } debug_print_tree(pipe_list, 0); debug_printf_exec("parse_and_run_stream: run_and_free_list\n"); run_and_free_list(pipe_list); empty = 0; #if ENABLE_HUSH_FUNCTIONS if (G.flag_return_in_progress == 1) break; #endif } } static void parse_and_run_string(const char *s) { struct in_str input; setup_string_in_str(&input, s); parse_and_run_stream(&input, '\0'); } static void parse_and_run_file(FILE *f) { struct in_str input; setup_file_in_str(&input, f); parse_and_run_stream(&input, ';'); } #if ENABLE_HUSH_TICK static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) { pid_t pid; int channel[2]; # if !BB_MMU char **to_free = NULL; # endif xpipe(channel); pid = BB_MMU ? xfork() : xvfork(); if (pid == 0) { /* child */ disable_restore_tty_pgrp_on_exit(); /* Process substitution is not considered to be usual * 'command execution'. * SUSv3 says ctrl-Z should be ignored, ctrl-C should not. */ bb_signals(0 + (1 << SIGTSTP) + (1 << SIGTTIN) + (1 << SIGTTOU) , SIG_IGN); CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ close(channel[0]); /* NB: close _first_, then move fd! */ xmove_fd(channel[1], 1); /* Prevent it from trying to handle ctrl-z etc */ IF_HUSH_JOB(G.run_list_level = 1;) /* Awful hack for `trap` or $(trap). * * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html * contains an example where "trap" is executed in a subshell: * * save_traps=$(trap) * ... * eval "$save_traps" * * Standard does not say that "trap" in subshell shall print * parent shell's traps. It only says that its output * must have suitable form, but then, in the above example * (which is not supposed to be normative), it implies that. * * bash (and probably other shell) does implement it * (traps are reset to defaults, but "trap" still shows them), * but as a result, "trap" logic is hopelessly messed up: * * # trap * trap -- 'echo Ho' SIGWINCH <--- we have a handler * # (trap) <--- trap is in subshell - no output (correct, traps are reset) * # true | trap <--- trap is in subshell - no output (ditto) * # echo `true | trap` <--- in subshell - output (but traps are reset!) * trap -- 'echo Ho' SIGWINCH * # echo `(trap)` <--- in subshell in subshell - output * trap -- 'echo Ho' SIGWINCH * # echo `true | (trap)` <--- in subshell in subshell in subshell - output! * trap -- 'echo Ho' SIGWINCH * * The rules when to forget and when to not forget traps * get really complex and nonsensical. * * Our solution: ONLY bare $(trap) or `trap` is special. */ s = skip_whitespace(s); if (strncmp(s, "trap", 4) == 0 && skip_whitespace(s + 4)[0] == '\0' ) { static const char *const argv[] = { NULL, NULL }; builtin_trap((char**)argv); exit(0); /* not _exit() - we need to fflush */ } # if BB_MMU reset_traps_to_defaults(); parse_and_run_string(s); _exit(G.last_exitcode); # else /* We re-execute after vfork on NOMMU. This makes this script safe: * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG * huge=`cat BIG` # was blocking here forever * echo OK */ re_execute_shell(&to_free, s, G.global_argv[0], G.global_argv + 1, NULL); # endif } /* parent */ *pid_p = pid; # if ENABLE_HUSH_FAST G.count_SIGCHLD++; //bb_error_msg("[%d] fork in generate_stream_from_string:" // " G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", // getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); # endif enable_restore_tty_pgrp_on_exit(); # if !BB_MMU free(to_free); # endif close(channel[1]); close_on_exec_on(channel[0]); return xfdopen_for_read(channel[0]); } /* Return code is exit status of the process that is run. */ static int process_command_subs(o_string *dest, const char *s) { FILE *fp; struct in_str pipe_str; pid_t pid; int status, ch, eol_cnt; fp = generate_stream_from_string(s, &pid); /* Now send results of command back into original context */ setup_file_in_str(&pipe_str, fp); eol_cnt = 0; while ((ch = i_getch(&pipe_str)) != EOF) { if (ch == '\n') { eol_cnt++; continue; } while (eol_cnt) { o_addchr(dest, '\n'); eol_cnt--; } o_addQchr(dest, ch); } debug_printf("done reading from `cmd` pipe, closing it\n"); fclose(fp); /* We need to extract exitcode. Test case * "true; echo `sleep 1; false` $?" * should print 1 */ safe_waitpid(pid, &status, 0); debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status)); return WEXITSTATUS(status); } #endif /* ENABLE_HUSH_TICK */ static void setup_heredoc(struct redir_struct *redir) { struct fd_pair pair; pid_t pid; int len, written; /* the _body_ of heredoc (misleading field name) */ const char *heredoc = redir->rd_filename; char *expanded; #if !BB_MMU char **to_free; #endif expanded = NULL; if (!(redir->rd_dup & HEREDOC_QUOTED)) { expanded = encode_then_expand_string(heredoc, /*process_bkslash:*/ 1, /*unbackslash:*/ 1); if (expanded) heredoc = expanded; } len = strlen(heredoc); close(redir->rd_fd); /* often saves dup2+close in xmove_fd */ xpiped_pair(pair); xmove_fd(pair.rd, redir->rd_fd); /* Try writing without forking. Newer kernels have * dynamically growing pipes. Must use non-blocking write! */ ndelay_on(pair.wr); while (1) { written = write(pair.wr, heredoc, len); if (written <= 0) break; len -= written; if (len == 0) { close(pair.wr); free(expanded); return; } heredoc += written; } ndelay_off(pair.wr); /* Okay, pipe buffer was not big enough */ /* Note: we must not create a stray child (bastard? :) * for the unsuspecting parent process. Child creates a grandchild * and exits before parent execs the process which consumes heredoc * (that exec happens after we return from this function) */ #if !BB_MMU to_free = NULL; #endif pid = xvfork(); if (pid == 0) { /* child */ disable_restore_tty_pgrp_on_exit(); pid = BB_MMU ? xfork() : xvfork(); if (pid != 0) _exit(0); /* grandchild */ close(redir->rd_fd); /* read side of the pipe */ #if BB_MMU full_write(pair.wr, heredoc, len); /* may loop or block */ _exit(0); #else /* Delegate blocking writes to another process */ xmove_fd(pair.wr, STDOUT_FILENO); re_execute_shell(&to_free, heredoc, NULL, NULL, NULL); #endif } /* parent */ #if ENABLE_HUSH_FAST G.count_SIGCHLD++; //bb_error_msg("[%d] fork in setup_heredoc: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); #endif enable_restore_tty_pgrp_on_exit(); #if !BB_MMU free(to_free); #endif close(pair.wr); free(expanded); wait(NULL); /* wait till child has died */ } /* squirrel != NULL means we squirrel away copies of stdin, stdout, * and stderr if they are redirected. */ static int setup_redirects(struct command *prog, int squirrel[]) { int openfd, mode; struct redir_struct *redir; for (redir = prog->redirects; redir; redir = redir->next) { if (redir->rd_type == REDIRECT_HEREDOC2) { /* rd_fd<<HERE case */ if (squirrel && redir->rd_fd < 3 && squirrel[redir->rd_fd] < 0 ) { squirrel[redir->rd_fd] = dup(redir->rd_fd); } /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ * of the heredoc */ debug_printf_parse("set heredoc '%s'\n", redir->rd_filename); setup_heredoc(redir); continue; } if (redir->rd_dup == REDIRFD_TO_FILE) { /* rd_fd<*>file case (<*> is <,>,>>,<>) */ char *p; if (redir->rd_filename == NULL) { /* Something went wrong in the parse. * Pretend it didn't happen */ bb_error_msg("bug in redirect parse"); continue; } mode = redir_table[redir->rd_type].mode; p = expand_string_to_string(redir->rd_filename, /*unbackslash:*/ 1); openfd = open_or_warn(p, mode); free(p); if (openfd < 0) { /* this could get lost if stderr has been redirected, but * bash and ash both lose it as well (though zsh doesn't!) */ //what the above comment tries to say? return 1; } } else { /* rd_fd<*>rd_dup or rd_fd<*>- cases */ openfd = redir->rd_dup; } if (openfd != redir->rd_fd) { if (squirrel && redir->rd_fd < 3 && squirrel[redir->rd_fd] < 0 ) { squirrel[redir->rd_fd] = dup(redir->rd_fd); } if (openfd == REDIRFD_CLOSE) { /* "n>-" means "close me" */ close(redir->rd_fd); } else { xdup2(openfd, redir->rd_fd); if (redir->rd_dup == REDIRFD_TO_FILE) close(openfd); } } } return 0; } static void restore_redirects(int squirrel[]) { int i, fd; for (i = 0; i < 3; i++) { fd = squirrel[i]; if (fd != -1) { /* We simply die on error */ xmove_fd(fd, i); } } } static char *find_in_path(const char *arg) { char *ret = NULL; const char *PATH = get_local_var_value("PATH"); if (!PATH) return NULL; while (1) { const char *end = strchrnul(PATH, ':'); int sz = end - PATH; /* must be int! */ free(ret); if (sz != 0) { ret = xasprintf("%.*s/%s", sz, PATH, arg); } else { /* We have xxx::yyyy in $PATH, * it means "use current dir" */ ret = xstrdup(arg); } if (access(ret, F_OK) == 0) break; if (*end == '\0') { free(ret); return NULL; } PATH = end + 1; } return ret; } static const struct built_in_command *find_builtin_helper(const char *name, const struct built_in_command *x, const struct built_in_command *end) { while (x != end) { if (strcmp(name, x->b_cmd) != 0) { x++; continue; } debug_printf_exec("found builtin '%s'\n", name); return x; } return NULL; } static const struct built_in_command *find_builtin1(const char *name) { return find_builtin_helper(name, bltins1, &bltins1[ARRAY_SIZE(bltins1)]); } static const struct built_in_command *find_builtin(const char *name) { const struct built_in_command *x = find_builtin1(name); if (x) return x; return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]); } #if ENABLE_HUSH_FUNCTIONS static struct function **find_function_slot(const char *name) { struct function **funcpp = &G.top_func; while (*funcpp) { if (strcmp(name, (*funcpp)->name) == 0) { break; } funcpp = &(*funcpp)->next; } return funcpp; } static const struct function *find_function(const char *name) { const struct function *funcp = *find_function_slot(name); if (funcp) debug_printf_exec("found function '%s'\n", name); return funcp; } /* Note: takes ownership on name ptr */ static struct function *new_function(char *name) { struct function **funcpp = find_function_slot(name); struct function *funcp = *funcpp; if (funcp != NULL) { struct command *cmd = funcp->parent_cmd; debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd); if (!cmd) { debug_printf_exec("freeing & replacing function '%s'\n", funcp->name); free(funcp->name); /* Note: if !funcp->body, do not free body_as_string! * This is a special case of "-F name body" function: * body_as_string was not malloced! */ if (funcp->body) { free_pipe_list(funcp->body); # if !BB_MMU free(funcp->body_as_string); # endif } } else { debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name); cmd->argv[0] = funcp->name; cmd->group = funcp->body; # if !BB_MMU cmd->group_as_string = funcp->body_as_string; # endif } } else { debug_printf_exec("remembering new function '%s'\n", name); funcp = *funcpp = xzalloc(sizeof(*funcp)); /*funcp->next = NULL;*/ } funcp->name = name; return funcp; } static void unset_func(const char *name) { struct function **funcpp = find_function_slot(name); struct function *funcp = *funcpp; if (funcp != NULL) { debug_printf_exec("freeing function '%s'\n", funcp->name); *funcpp = funcp->next; /* funcp is unlinked now, deleting it. * Note: if !funcp->body, the function was created by * "-F name body", do not free ->body_as_string * and ->name as they were not malloced. */ if (funcp->body) { free_pipe_list(funcp->body); free(funcp->name); # if !BB_MMU free(funcp->body_as_string); # endif } free(funcp); } } # if BB_MMU #define exec_function(to_free, funcp, argv) \ exec_function(funcp, argv) # endif static void exec_function(char ***to_free, const struct function *funcp, char **argv) NORETURN; static void exec_function(char ***to_free, const struct function *funcp, char **argv) { # if BB_MMU int n = 1; argv[0] = G.global_argv[0]; G.global_argv = argv; while (*++argv) n++; G.global_argc = n; /* On MMU, funcp->body is always non-NULL */ n = run_list(funcp->body); fflush_all(); _exit(n); # else re_execute_shell(to_free, funcp->body_as_string, G.global_argv[0], argv + 1, NULL); # endif } static int run_function(const struct function *funcp, char **argv) { int rc; save_arg_t sv; smallint sv_flg; save_and_replace_G_args(&sv, argv); /* "we are in function, ok to use return" */ sv_flg = G.flag_return_in_progress; G.flag_return_in_progress = -1; # if ENABLE_HUSH_LOCAL G.func_nest_level++; # endif /* On MMU, funcp->body is always non-NULL */ # if !BB_MMU if (!funcp->body) { /* Function defined by -F */ parse_and_run_string(funcp->body_as_string); rc = G.last_exitcode; } else # endif { rc = run_list(funcp->body); } # if ENABLE_HUSH_LOCAL { struct variable *var; struct variable **var_pp; var_pp = &G.top_var; while ((var = *var_pp) != NULL) { if (var->func_nest_level < G.func_nest_level) { var_pp = &var->next; continue; } /* Unexport */ if (var->flg_export) bb_unsetenv(var->varstr); /* Remove from global list */ *var_pp = var->next; /* Free */ if (!var->max_len) free(var->varstr); free(var); } G.func_nest_level--; } # endif G.flag_return_in_progress = sv_flg; restore_G_args(&sv, argv); return rc; } #endif /* ENABLE_HUSH_FUNCTIONS */ #if BB_MMU #define exec_builtin(to_free, x, argv) \ exec_builtin(x, argv) #else #define exec_builtin(to_free, x, argv) \ exec_builtin(to_free, argv) #endif static void exec_builtin(char ***to_free, const struct built_in_command *x, char **argv) NORETURN; static void exec_builtin(char ***to_free, const struct built_in_command *x, char **argv) { #if BB_MMU int rcode; fflush_all(); rcode = x->b_function(argv); fflush_all(); _exit(rcode); #else fflush_all(); /* On NOMMU, we must never block! * Example: { sleep 99 | read line; } & echo Ok */ re_execute_shell(to_free, argv[0], G.global_argv[0], G.global_argv + 1, argv); #endif } static void execvp_or_die(char **argv) NORETURN; static void execvp_or_die(char **argv) { debug_printf_exec("execing '%s'\n", argv[0]); /* Don't propagate SIG_IGN to the child */ if (SPECIAL_JOBSTOP_SIGS != 0) switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); execvp(argv[0], argv); bb_perror_msg("can't execute '%s'", argv[0]); _exit(127); /* bash compat */ } #if ENABLE_HUSH_MODE_X static void dump_cmd_in_x_mode(char **argv) { if (G_x_mode && argv) { /* We want to output the line in one write op */ char *buf, *p; int len; int n; len = 3; n = 0; while (argv[n]) len += strlen(argv[n++]) + 1; buf = xmalloc(len); buf[0] = '+'; p = buf + 1; n = 0; while (argv[n]) p += sprintf(p, " %s", argv[n++]); *p++ = '\n'; *p = '\0'; fputs(buf, stderr); free(buf); } } #else # define dump_cmd_in_x_mode(argv) ((void)0) #endif #if BB_MMU #define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ pseudo_exec_argv(argv, assignment_cnt, argv_expanded) #define pseudo_exec(nommu_save, command, argv_expanded) \ pseudo_exec(command, argv_expanded) #endif /* Called after [v]fork() in run_pipe, or from builtin_exec. * Never returns. * Don't exit() here. If you don't exec, use _exit instead. * The at_exit handlers apparently confuse the calling process, * in particular stdin handling. Not sure why? -- because of vfork! (vda) */ static void pseudo_exec_argv(nommu_save_t *nommu_save, char **argv, int assignment_cnt, char **argv_expanded) NORETURN; static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, char **argv, int assignment_cnt, char **argv_expanded) { char **new_env; new_env = expand_assignments(argv, assignment_cnt); dump_cmd_in_x_mode(new_env); if (!argv[assignment_cnt]) { /* Case when we are here: ... | var=val | ... * (note that we do not exit early, i.e., do not optimize out * expand_assignments(): think about ... | var=`sleep 1` | ... */ free_strings(new_env); _exit(EXIT_SUCCESS); } #if BB_MMU set_vars_and_save_old(new_env); free(new_env); /* optional */ /* we can also destroy set_vars_and_save_old's return value, * to save memory */ #else nommu_save->new_env = new_env; nommu_save->old_vars = set_vars_and_save_old(new_env); #endif if (argv_expanded) { argv = argv_expanded; } else { argv = expand_strvec_to_strvec(argv + assignment_cnt); #if !BB_MMU nommu_save->argv = argv; #endif } dump_cmd_in_x_mode(argv); #if ENABLE_FEATURE_SH_STANDALONE || BB_MMU if (strchr(argv[0], '/') != NULL) goto skip; #endif /* Check if the command matches any of the builtins. * Depending on context, this might be redundant. But it's * easier to waste a few CPU cycles than it is to figure out * if this is one of those cases. */ { /* On NOMMU, it is more expensive to re-execute shell * just in order to run echo or test builtin. * It's better to skip it here and run corresponding * non-builtin later. */ const struct built_in_command *x; x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]); if (x) { exec_builtin(&nommu_save->argv_from_re_execing, x, argv); } } #if ENABLE_HUSH_FUNCTIONS /* Check if the command matches any functions */ { const struct function *funcp = find_function(argv[0]); if (funcp) { exec_function(&nommu_save->argv_from_re_execing, funcp, argv); } } #endif #if ENABLE_FEATURE_SH_STANDALONE /* Check if the command matches any busybox applets */ { int a = find_applet_by_name(argv[0]); if (a >= 0) { # if BB_MMU /* see above why on NOMMU it is not allowed */ if (APPLET_IS_NOEXEC(a)) { debug_printf_exec("running applet '%s'\n", argv[0]); run_applet_no_and_exit(a, argv); } # endif /* Re-exec ourselves */ debug_printf_exec("re-execing applet '%s'\n", argv[0]); /* Don't propagate SIG_IGN to the child */ if (SPECIAL_JOBSTOP_SIGS != 0) switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); execv(bb_busybox_exec_path, argv); /* If they called chroot or otherwise made the binary no longer * executable, fall through */ } } #endif #if ENABLE_FEATURE_SH_STANDALONE || BB_MMU skip: #endif execvp_or_die(argv); } /* Called after [v]fork() in run_pipe */ static void pseudo_exec(nommu_save_t *nommu_save, struct command *command, char **argv_expanded) NORETURN; static void pseudo_exec(nommu_save_t *nommu_save, struct command *command, char **argv_expanded) { if (command->argv) { pseudo_exec_argv(nommu_save, command->argv, command->assignment_cnt, argv_expanded); } if (command->group) { /* Cases when we are here: * ( list ) * { list } & * ... | ( list ) | ... * ... | { list } | ... */ #if BB_MMU int rcode; debug_printf_exec("pseudo_exec: run_list\n"); reset_traps_to_defaults(); rcode = run_list(command->group); /* OK to leak memory by not calling free_pipe_list, * since this process is about to exit */ _exit(rcode); #else re_execute_shell(&nommu_save->argv_from_re_execing, command->group_as_string, G.global_argv[0], G.global_argv + 1, NULL); #endif } /* Case when we are here: ... | >file */ debug_printf_exec("pseudo_exec'ed null command\n"); _exit(EXIT_SUCCESS); } #if ENABLE_HUSH_JOB static const char *get_cmdtext(struct pipe *pi) { char **argv; char *p; int len; /* This is subtle. ->cmdtext is created only on first backgrounding. * (Think "cat, <ctrl-z>, fg, <ctrl-z>, fg, <ctrl-z>...." here...) * On subsequent bg argv is trashed, but we won't use it */ if (pi->cmdtext) return pi->cmdtext; argv = pi->cmds[0].argv; if (!argv || !argv[0]) { pi->cmdtext = xzalloc(1); return pi->cmdtext; } len = 0; do { len += strlen(*argv) + 1; } while (*++argv); p = xmalloc(len); pi->cmdtext = p; argv = pi->cmds[0].argv; do { len = strlen(*argv); memcpy(p, *argv, len); p += len; *p++ = ' '; } while (*++argv); p[-1] = '\0'; return pi->cmdtext; } static void insert_bg_job(struct pipe *pi) { struct pipe *job, **jobp; int i; /* Linear search for the ID of the job to use */ pi->jobid = 1; for (job = G.job_list; job; job = job->next) if (job->jobid >= pi->jobid) pi->jobid = job->jobid + 1; /* Add job to the list of running jobs */ jobp = &G.job_list; while ((job = *jobp) != NULL) jobp = &job->next; job = *jobp = xmalloc(sizeof(*job)); *job = *pi; /* physical copy */ job->next = NULL; job->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds); /* Cannot copy entire pi->cmds[] vector! This causes double frees */ for (i = 0; i < pi->num_cmds; i++) { job->cmds[i].pid = pi->cmds[i].pid; /* all other fields are not used and stay zero */ } job->cmdtext = xstrdup(get_cmdtext(pi)); if (G_interactive_fd) printf("[%d] %d %s\n", job->jobid, job->cmds[0].pid, job->cmdtext); G.last_jobid = job->jobid; } static void remove_bg_job(struct pipe *pi) { struct pipe *prev_pipe; if (pi == G.job_list) { G.job_list = pi->next; } else { prev_pipe = G.job_list; while (prev_pipe->next != pi) prev_pipe = prev_pipe->next; prev_pipe->next = pi->next; } if (G.job_list) G.last_jobid = G.job_list->jobid; else G.last_jobid = 0; } /* Remove a backgrounded job */ static void delete_finished_bg_job(struct pipe *pi) { remove_bg_job(pi); free_pipe(pi); } #endif /* JOB */ /* Check to see if any processes have exited -- if they * have, figure out why and see if a job has completed */ static int checkjobs(struct pipe *fg_pipe) { int attributes; int status; #if ENABLE_HUSH_JOB struct pipe *pi; #endif pid_t childpid; int rcode = 0; debug_printf_jobs("checkjobs %p\n", fg_pipe); attributes = WUNTRACED; if (fg_pipe == NULL) attributes |= WNOHANG; errno = 0; #if ENABLE_HUSH_FAST if (G.handled_SIGCHLD == G.count_SIGCHLD) { //bb_error_msg("[%d] checkjobs: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d children?:%d fg_pipe:%p", //getpid(), G.count_SIGCHLD, G.handled_SIGCHLD, G.we_have_children, fg_pipe); /* There was neither fork nor SIGCHLD since last waitpid */ /* Avoid doing waitpid syscall if possible */ if (!G.we_have_children) { errno = ECHILD; return -1; } if (fg_pipe == NULL) { /* is WNOHANG set? */ /* We have children, but they did not exit * or stop yet (we saw no SIGCHLD) */ return 0; } /* else: !WNOHANG, waitpid will block, can't short-circuit */ } #endif /* Do we do this right? * bash-3.00# sleep 20 | false * <ctrl-Z pressed> * [3]+ Stopped sleep 20 | false * bash-3.00# echo $? * 1 <========== bg pipe is not fully done, but exitcode is already known! * [hush 1.14.0: yes we do it right] */ wait_more: while (1) { int i; int dead; #if ENABLE_HUSH_FAST i = G.count_SIGCHLD; #endif childpid = waitpid(-1, &status, attributes); if (childpid <= 0) { if (childpid && errno != ECHILD) bb_perror_msg("waitpid"); #if ENABLE_HUSH_FAST else { /* Until next SIGCHLD, waitpid's are useless */ G.we_have_children = (childpid == 0); G.handled_SIGCHLD = i; //bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); } #endif break; } dead = WIFEXITED(status) || WIFSIGNALED(status); #if DEBUG_JOBS if (WIFSTOPPED(status)) debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n", childpid, WSTOPSIG(status), WEXITSTATUS(status)); if (WIFSIGNALED(status)) debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n", childpid, WTERMSIG(status), WEXITSTATUS(status)); if (WIFEXITED(status)) debug_printf_jobs("pid %d exited, exitcode %d\n", childpid, WEXITSTATUS(status)); #endif /* Were we asked to wait for fg pipe? */ if (fg_pipe) { i = fg_pipe->num_cmds; while (--i >= 0) { debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid); if (fg_pipe->cmds[i].pid != childpid) continue; if (dead) { int ex; fg_pipe->cmds[i].pid = 0; fg_pipe->alive_cmds--; ex = WEXITSTATUS(status); /* bash prints killer signal's name for *last* * process in pipe (prints just newline for SIGINT/SIGPIPE). * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT) */ if (WIFSIGNALED(status)) { int sig = WTERMSIG(status); if (i == fg_pipe->num_cmds-1) /* TODO: use strsignal() instead for bash compat? but that's bloat... */ printf("%s\n", sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig)); /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */ /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? * Maybe we need to use sig | 128? */ ex = sig + 128; } fg_pipe->cmds[i].cmd_exitcode = ex; } else { fg_pipe->stopped_cmds++; } debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n", fg_pipe->alive_cmds, fg_pipe->stopped_cmds); if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) { /* All processes in fg pipe have exited or stopped */ i = fg_pipe->num_cmds; while (--i >= 0) { rcode = fg_pipe->cmds[i].cmd_exitcode; /* usually last process gives overall exitstatus, * but with "set -o pipefail", last *failed* process does */ if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0) break; } IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;) /* Note: *non-interactive* bash does not continue if all processes in fg pipe * are stopped. Testcase: "cat | cat" in a script (not on command line!) * and "killall -STOP cat" */ if (G_interactive_fd) { #if ENABLE_HUSH_JOB if (fg_pipe->alive_cmds != 0) insert_bg_job(fg_pipe); #endif return rcode; } if (fg_pipe->alive_cmds == 0) return rcode; } /* There are still running processes in the fg pipe */ goto wait_more; /* do waitpid again */ } /* it wasnt fg_pipe, look for process in bg pipes */ } #if ENABLE_HUSH_JOB /* We asked to wait for bg or orphaned children */ /* No need to remember exitcode in this case */ for (pi = G.job_list; pi; pi = pi->next) { for (i = 0; i < pi->num_cmds; i++) { if (pi->cmds[i].pid == childpid) goto found_pi_and_prognum; } } /* Happens when shell is used as init process (init=/bin/sh) */ debug_printf("checkjobs: pid %d was not in our list!\n", childpid); continue; /* do waitpid again */ found_pi_and_prognum: if (dead) { /* child exited */ pi->cmds[i].pid = 0; pi->alive_cmds--; if (!pi->alive_cmds) { if (G_interactive_fd) printf(JOB_STATUS_FORMAT, pi->jobid, "Done", pi->cmdtext); delete_finished_bg_job(pi); } } else { /* child stopped */ pi->stopped_cmds++; } #endif } /* while (waitpid succeeds)... */ return rcode; } #if ENABLE_HUSH_JOB static int checkjobs_and_fg_shell(struct pipe *fg_pipe) { pid_t p; int rcode = checkjobs(fg_pipe); if (G_saved_tty_pgrp) { /* Job finished, move the shell to the foreground */ p = getpgrp(); /* our process group id */ debug_printf_jobs("fg'ing ourself: getpgrp()=%d\n", (int)p); tcsetpgrp(G_interactive_fd, p); } return rcode; } #endif /* Start all the jobs, but don't wait for anything to finish. * See checkjobs(). * * Return code is normally -1, when the caller has to wait for children * to finish to determine the exit status of the pipe. If the pipe * is a simple builtin command, however, the action is done by the * time run_pipe returns, and the exit code is provided as the * return value. * * Returns -1 only if started some children. IOW: we have to * mask out retvals of builtins etc with 0xff! * * The only case when we do not need to [v]fork is when the pipe * is single, non-backgrounded, non-subshell command. Examples: * cmd ; ... { list } ; ... * cmd && ... { list } && ... * cmd || ... { list } || ... * If it is, then we can run cmd as a builtin, NOFORK, * or (if SH_STANDALONE) an applet, and we can run the { list } * with run_list. If it isn't one of these, we fork and exec cmd. * * Cases when we must fork: * non-single: cmd | cmd * backgrounded: cmd & { list } & * subshell: ( list ) [&] */ #if !ENABLE_HUSH_MODE_X #define redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel, argv_expanded) \ redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel) #endif static int redirect_and_varexp_helper(char ***new_env_p, struct variable **old_vars_p, struct command *command, int squirrel[3], char **argv_expanded) { /* setup_redirects acts on file descriptors, not FILEs. * This is perfect for work that comes after exec(). * Is it really safe for inline use? Experimentally, * things seem to work. */ int rcode = setup_redirects(command, squirrel); if (rcode == 0) { char **new_env = expand_assignments(command->argv, command->assignment_cnt); *new_env_p = new_env; dump_cmd_in_x_mode(new_env); dump_cmd_in_x_mode(argv_expanded); if (old_vars_p) *old_vars_p = set_vars_and_save_old(new_env); } return rcode; } static NOINLINE int run_pipe(struct pipe *pi) { static const char *const null_ptr = NULL; int cmd_no; int next_infd; struct command *command; char **argv_expanded; char **argv; /* it is not always needed, but we aim to smaller code */ int squirrel[] = { -1, -1, -1 }; int rcode; debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds); debug_enter(); /* Testcase: set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*" * Result should be 3 lines: q w e, qwe, q w e */ G.ifs = get_local_var_value("IFS"); if (!G.ifs) G.ifs = defifs; IF_HUSH_JOB(pi->pgrp = -1;) pi->stopped_cmds = 0; command = &pi->cmds[0]; argv_expanded = NULL; if (pi->num_cmds != 1 || pi->followup == PIPE_BG || command->cmd_type == CMD_SUBSHELL ) { goto must_fork; } pi->alive_cmds = 1; debug_printf_exec(": group:%p argv:'%s'\n", command->group, command->argv ? command->argv[0] : "NONE"); if (command->group) { #if ENABLE_HUSH_FUNCTIONS if (command->cmd_type == CMD_FUNCDEF) { /* "executing" func () { list } */ struct function *funcp; funcp = new_function(command->argv[0]); /* funcp->name is already set to argv[0] */ funcp->body = command->group; # if !BB_MMU funcp->body_as_string = command->group_as_string; command->group_as_string = NULL; # endif command->group = NULL; command->argv[0] = NULL; debug_printf_exec("cmd %p has child func at %p\n", command, funcp); funcp->parent_cmd = command; command->child_func = funcp; debug_printf_exec("run_pipe: return EXIT_SUCCESS\n"); debug_leave(); return EXIT_SUCCESS; } #endif /* { list } */ debug_printf("non-subshell group\n"); rcode = 1; /* exitcode if redir failed */ if (setup_redirects(command, squirrel) == 0) { debug_printf_exec(": run_list\n"); rcode = run_list(command->group) & 0xff; } restore_redirects(squirrel); IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) debug_leave(); debug_printf_exec("run_pipe: return %d\n", rcode); return rcode; } argv = command->argv ? command->argv : (char **) &null_ptr; { const struct built_in_command *x; #if ENABLE_HUSH_FUNCTIONS const struct function *funcp; #else enum { funcp = 0 }; #endif char **new_env = NULL; struct variable *old_vars = NULL; if (argv[command->assignment_cnt] == NULL) { /* Assignments, but no command */ /* Ensure redirects take effect (that is, create files). * Try "a=t >file" */ #if 0 /* A few cases in testsuite fail with this code. FIXME */ rcode = redirect_and_varexp_helper(&new_env, /*old_vars:*/ NULL, command, squirrel, /*argv_expanded:*/ NULL); /* Set shell variables */ if (new_env) { argv = new_env; while (*argv) { set_local_var(*argv, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); /* Do we need to flag set_local_var() errors? * "assignment to readonly var" and "putenv error" */ argv++; } } /* Redirect error sets $? to 1. Otherwise, * if evaluating assignment value set $?, retain it. * Try "false; q=`exit 2`; echo $?" - should print 2: */ if (rcode == 0) rcode = G.last_exitcode; /* Exit, _skipping_ variable restoring code: */ goto clean_up_and_ret0; #else /* Older, bigger, but more correct code */ rcode = setup_redirects(command, squirrel); restore_redirects(squirrel); /* Set shell variables */ if (G_x_mode) bb_putchar_stderr('+'); while (*argv) { char *p = expand_string_to_string(*argv, /*unbackslash:*/ 1); if (G_x_mode) fprintf(stderr, " %s", p); debug_printf_exec("set shell var:'%s'->'%s'\n", *argv, p); set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); /* Do we need to flag set_local_var() errors? * "assignment to readonly var" and "putenv error" */ argv++; } if (G_x_mode) bb_putchar_stderr('\n'); /* Redirect error sets $? to 1. Otherwise, * if evaluating assignment value set $?, retain it. * Try "false; q=`exit 2`; echo $?" - should print 2: */ if (rcode == 0) rcode = G.last_exitcode; IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) debug_leave(); debug_printf_exec("run_pipe: return %d\n", rcode); return rcode; #endif } /* Expand the rest into (possibly) many strings each */ #if ENABLE_HUSH_BASH_COMPAT if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) { argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); } else #endif { argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); } /* if someone gives us an empty string: `cmd with empty output` */ if (!argv_expanded[0]) { free(argv_expanded); debug_leave(); return G.last_exitcode; } x = find_builtin(argv_expanded[0]); #if ENABLE_HUSH_FUNCTIONS funcp = NULL; if (!x) funcp = find_function(argv_expanded[0]); #endif if (x || funcp) { if (!funcp) { if (x->b_function == builtin_exec && argv_expanded[1] == NULL) { debug_printf("exec with redirects only\n"); rcode = setup_redirects(command, NULL); goto clean_up_and_ret1; } } rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded); if (rcode == 0) { if (!funcp) { debug_printf_exec(": builtin '%s' '%s'...\n", x->b_cmd, argv_expanded[1]); fflush_all(); rcode = x->b_function(argv_expanded) & 0xff; fflush_all(); } #if ENABLE_HUSH_FUNCTIONS else { # if ENABLE_HUSH_LOCAL struct variable **sv; sv = G.shadowed_vars_pp; G.shadowed_vars_pp = &old_vars; # endif debug_printf_exec(": function '%s' '%s'...\n", funcp->name, argv_expanded[1]); rcode = run_function(funcp, argv_expanded) & 0xff; # if ENABLE_HUSH_LOCAL G.shadowed_vars_pp = sv; # endif } #endif } clean_up_and_ret: unset_vars(new_env); add_vars(old_vars); /* clean_up_and_ret0: */ restore_redirects(squirrel); clean_up_and_ret1: free(argv_expanded); IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) debug_leave(); debug_printf_exec("run_pipe return %d\n", rcode); return rcode; } if (ENABLE_FEATURE_SH_NOFORK) { int n = find_applet_by_name(argv_expanded[0]); if (n >= 0 && APPLET_IS_NOFORK(n)) { rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded); if (rcode == 0) { debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]); rcode = run_nofork_applet(n, argv_expanded); } goto clean_up_and_ret; } } /* It is neither builtin nor applet. We must fork. */ } must_fork: /* NB: argv_expanded may already be created, and that * might include `cmd` runs! Do not rerun it! We *must* * use argv_expanded if it's non-NULL */ /* Going to fork a child per each pipe member */ pi->alive_cmds = 0; next_infd = 0; cmd_no = 0; while (cmd_no < pi->num_cmds) { struct fd_pair pipefds; #if !BB_MMU volatile nommu_save_t nommu_save; nommu_save.new_env = NULL; nommu_save.old_vars = NULL; nommu_save.argv = NULL; nommu_save.argv_from_re_execing = NULL; #endif command = &pi->cmds[cmd_no]; cmd_no++; if (command->argv) { debug_printf_exec(": pipe member '%s' '%s'...\n", command->argv[0], command->argv[1]); } else { debug_printf_exec(": pipe member with no argv\n"); } /* pipes are inserted between pairs of commands */ pipefds.rd = 0; pipefds.wr = 1; if (cmd_no < pi->num_cmds) xpiped_pair(pipefds); command->pid = BB_MMU ? fork() : vfork(); if (!command->pid) { /* child */ #if ENABLE_HUSH_JOB disable_restore_tty_pgrp_on_exit(); CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ /* Every child adds itself to new process group * with pgid == pid_of_first_child_in_pipe */ if (G.run_list_level == 1 && G_interactive_fd) { pid_t pgrp; pgrp = pi->pgrp; if (pgrp < 0) /* true for 1st process only */ pgrp = getpid(); if (setpgid(0, pgrp) == 0 && pi->followup != PIPE_BG && G_saved_tty_pgrp /* we have ctty */ ) { /* We do it in *every* child, not just first, * to avoid races */ tcsetpgrp(G_interactive_fd, pgrp); } } #endif if (pi->alive_cmds == 0 && pi->followup == PIPE_BG) { /* 1st cmd in backgrounded pipe * should have its stdin /dev/null'ed */ close(0); if (open(bb_dev_null, O_RDONLY)) xopen("/", O_RDONLY); } else { xmove_fd(next_infd, 0); } xmove_fd(pipefds.wr, 1); if (pipefds.rd > 1) close(pipefds.rd); /* Like bash, explicit redirects override pipes, * and the pipe fd is available for dup'ing. */ if (setup_redirects(command, NULL)) _exit(1); /* Stores to nommu_save list of env vars putenv'ed * (NOMMU, on MMU we don't need that) */ /* cast away volatility... */ pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded); /* pseudo_exec() does not return */ } /* parent or error */ #if ENABLE_HUSH_FAST G.count_SIGCHLD++; //bb_error_msg("[%d] fork in run_pipe: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); #endif enable_restore_tty_pgrp_on_exit(); #if !BB_MMU /* Clean up after vforked child */ free(nommu_save.argv); free(nommu_save.argv_from_re_execing); unset_vars(nommu_save.new_env); add_vars(nommu_save.old_vars); #endif free(argv_expanded); argv_expanded = NULL; if (command->pid < 0) { /* [v]fork failed */ /* Clearly indicate, was it fork or vfork */ bb_perror_msg(BB_MMU ? "vfork"+1 : "vfork"); } else { pi->alive_cmds++; #if ENABLE_HUSH_JOB /* Second and next children need to know pid of first one */ if (pi->pgrp < 0) pi->pgrp = command->pid; #endif } if (cmd_no > 1) close(next_infd); if (cmd_no < pi->num_cmds) close(pipefds.wr); /* Pass read (output) pipe end to next iteration */ next_infd = pipefds.rd; } if (!pi->alive_cmds) { debug_leave(); debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n"); return 1; } debug_leave(); debug_printf_exec("run_pipe return -1 (%u children started)\n", pi->alive_cmds); return -1; } /* NB: called by pseudo_exec, and therefore must not modify any * global data until exec/_exit (we can be a child after vfork!) */ static int run_list(struct pipe *pi) { #if ENABLE_HUSH_CASE char *case_word = NULL; #endif #if ENABLE_HUSH_LOOPS struct pipe *loop_top = NULL; char **for_lcur = NULL; char **for_list = NULL; #endif smallint last_followup; smalluint rcode; #if ENABLE_HUSH_IF || ENABLE_HUSH_CASE smalluint cond_code = 0; #else enum { cond_code = 0 }; #endif #if HAS_KEYWORDS smallint rword; /* RES_foo */ smallint last_rword; /* ditto */ #endif debug_printf_exec("run_list start lvl %d\n", G.run_list_level); debug_enter(); #if ENABLE_HUSH_LOOPS /* Check syntax for "for" */ { struct pipe *cpipe; for (cpipe = pi; cpipe; cpipe = cpipe->next) { if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN) continue; /* current word is FOR or IN (BOLD in comments below) */ if (cpipe->next == NULL) { syntax_error("malformed for"); debug_leave(); debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level); return 1; } /* "FOR v; do ..." and "for v IN a b; do..." are ok */ if (cpipe->next->res_word == RES_DO) continue; /* next word is not "do". It must be "in" then ("FOR v in ...") */ if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */ || cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */ ) { syntax_error("malformed for"); debug_leave(); debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level); return 1; } } } #endif /* Past this point, all code paths should jump to ret: label * in order to return, no direct "return" statements please. * This helps to ensure that no memory is leaked. */ #if ENABLE_HUSH_JOB G.run_list_level++; #endif #if HAS_KEYWORDS rword = RES_NONE; last_rword = RES_XXXX; #endif last_followup = PIPE_SEQ; rcode = G.last_exitcode; /* Go through list of pipes, (maybe) executing them. */ for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) { if (G.flag_SIGINT) break; IF_HAS_KEYWORDS(rword = pi->res_word;) debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n", rword, cond_code, last_rword); #if ENABLE_HUSH_LOOPS if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) && loop_top == NULL /* avoid bumping G.depth_of_loop twice */ ) { /* start of a loop: remember where loop starts */ loop_top = pi; G.depth_of_loop++; } #endif /* Still in the same "if...", "then..." or "do..." branch? */ if (IF_HAS_KEYWORDS(rword == last_rword &&) 1) { if ((rcode == 0 && last_followup == PIPE_OR) || (rcode != 0 && last_followup == PIPE_AND) ) { /* It is "<true> || CMD" or "<false> && CMD" * and we should not execute CMD */ debug_printf_exec("skipped cmd because of || or &&\n"); last_followup = pi->followup; goto dont_check_jobs_but_continue; } } last_followup = pi->followup; IF_HAS_KEYWORDS(last_rword = rword;) #if ENABLE_HUSH_IF if (cond_code) { if (rword == RES_THEN) { /* if false; then ... fi has exitcode 0! */ G.last_exitcode = rcode = EXIT_SUCCESS; /* "if <false> THEN cmd": skip cmd */ continue; } } else { if (rword == RES_ELSE || rword == RES_ELIF) { /* "if <true> then ... ELSE/ELIF cmd": * skip cmd and all following ones */ break; } } #endif #if ENABLE_HUSH_LOOPS if (rword == RES_FOR) { /* && pi->num_cmds - always == 1 */ if (!for_lcur) { /* first loop through for */ static const char encoded_dollar_at[] ALIGN1 = { SPECIAL_VAR_SYMBOL, '@' | 0x80, SPECIAL_VAR_SYMBOL, '\0' }; /* encoded representation of "$@" */ static const char *const encoded_dollar_at_argv[] = { encoded_dollar_at, NULL }; /* argv list with one element: "$@" */ char **vals; vals = (char**)encoded_dollar_at_argv; if (pi->next->res_word == RES_IN) { /* if no variable values after "in" we skip "for" */ if (!pi->next->cmds[0].argv) { G.last_exitcode = rcode = EXIT_SUCCESS; debug_printf_exec(": null FOR: exitcode EXIT_SUCCESS\n"); break; } vals = pi->next->cmds[0].argv; } /* else: "for var; do..." -> assume "$@" list */ /* create list of variable values */ debug_print_strings("for_list made from", vals); for_list = expand_strvec_to_strvec(vals); for_lcur = for_list; debug_print_strings("for_list", for_list); } if (!*for_lcur) { /* "for" loop is over, clean up */ free(for_list); for_list = NULL; for_lcur = NULL; break; } /* Insert next value from for_lcur */ /* note: *for_lcur already has quotes removed, $var expanded, etc */ set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], *for_lcur++), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); continue; } if (rword == RES_IN) { continue; /* "for v IN list;..." - "in" has no cmds anyway */ } if (rword == RES_DONE) { continue; /* "done" has no cmds too */ } #endif #if ENABLE_HUSH_CASE if (rword == RES_CASE) { case_word = expand_strvec_to_string(pi->cmds->argv); continue; } if (rword == RES_MATCH) { char **argv; if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */ break; /* all prev words didn't match, does this one match? */ argv = pi->cmds->argv; while (*argv) { char *pattern = expand_string_to_string(*argv, /*unbackslash:*/ 1); /* TODO: which FNM_xxx flags to use? */ cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0); free(pattern); if (cond_code == 0) { /* match! we will execute this branch */ free(case_word); /* make future "word)" stop */ case_word = NULL; break; } argv++; } continue; } if (rword == RES_CASE_BODY) { /* inside of a case branch */ if (cond_code != 0) continue; /* not matched yet, skip this pipe */ } #endif /* Just pressing <enter> in shell should check for jobs. * OTOH, in non-interactive shell this is useless * and only leads to extra job checks */ if (pi->num_cmds == 0) { if (G_interactive_fd) goto check_jobs_and_continue; continue; } /* After analyzing all keywords and conditions, we decided * to execute this pipe. NB: have to do checkjobs(NULL) * after run_pipe to collect any background children, * even if list execution is to be stopped. */ debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds); { int r; #if ENABLE_HUSH_LOOPS G.flag_break_continue = 0; #endif rcode = r = run_pipe(pi); /* NB: rcode is a smallint */ if (r != -1) { /* We ran a builtin, function, or group. * rcode is already known * and we don't need to wait for anything. */ G.last_exitcode = rcode; debug_printf_exec(": builtin/func exitcode %d\n", rcode); check_and_run_traps(); #if ENABLE_HUSH_LOOPS /* Was it "break" or "continue"? */ if (G.flag_break_continue) { smallint fbc = G.flag_break_continue; /* We might fall into outer *loop*, * don't want to break it too */ if (loop_top) { G.depth_break_continue--; if (G.depth_break_continue == 0) G.flag_break_continue = 0; /* else: e.g. "continue 2" should *break* once, *then* continue */ } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */ if (G.depth_break_continue != 0 || fbc == BC_BREAK) { checkjobs(NULL); break; } /* "continue": simulate end of loop */ rword = RES_DONE; continue; } #endif #if ENABLE_HUSH_FUNCTIONS if (G.flag_return_in_progress == 1) { checkjobs(NULL); break; } #endif } else if (pi->followup == PIPE_BG) { /* What does bash do with attempts to background builtins? */ /* even bash 3.2 doesn't do that well with nested bg: * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". * I'm NOT treating inner &'s as jobs */ check_and_run_traps(); #if ENABLE_HUSH_JOB if (G.run_list_level == 1) insert_bg_job(pi); #endif /* Last command's pid goes to $! */ G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid; G.last_exitcode = rcode = EXIT_SUCCESS; debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); } else { #if ENABLE_HUSH_JOB if (G.run_list_level == 1 && G_interactive_fd) { /* Waits for completion, then fg's main shell */ rcode = checkjobs_and_fg_shell(pi); debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode); check_and_run_traps(); } else #endif { /* This one just waits for completion */ rcode = checkjobs(pi); debug_printf_exec(": checkjobs exitcode %d\n", rcode); check_and_run_traps(); } G.last_exitcode = rcode; } } /* Analyze how result affects subsequent commands */ #if ENABLE_HUSH_IF if (rword == RES_IF || rword == RES_ELIF) cond_code = rcode; #endif check_jobs_and_continue: checkjobs(NULL); dont_check_jobs_but_continue: ; #if ENABLE_HUSH_LOOPS /* Beware of "while false; true; do ..."! */ if (pi->next && (pi->next->res_word == RES_DO || pi->next->res_word == RES_DONE) /* check for RES_DONE is needed for "while ...; do \n done" case */ ) { if (rword == RES_WHILE) { if (rcode) { /* "while false; do...done" - exitcode 0 */ G.last_exitcode = rcode = EXIT_SUCCESS; debug_printf_exec(": while expr is false: breaking (exitcode:EXIT_SUCCESS)\n"); break; } } if (rword == RES_UNTIL) { if (!rcode) { debug_printf_exec(": until expr is true: breaking\n"); break; } } } #endif } /* for (pi) */ #if ENABLE_HUSH_JOB G.run_list_level--; #endif #if ENABLE_HUSH_LOOPS if (loop_top) G.depth_of_loop--; free(for_list); #endif #if ENABLE_HUSH_CASE free(case_word); #endif debug_leave(); debug_printf_exec("run_list lvl %d return %d\n", G.run_list_level + 1, rcode); return rcode; } /* Select which version we will use */ static int run_and_free_list(struct pipe *pi) { int rcode = 0; debug_printf_exec("run_and_free_list entered\n"); if (!G.o_opt[OPT_O_NOEXEC]) { debug_printf_exec(": run_list: 1st pipe with %d cmds\n", pi->num_cmds); rcode = run_list(pi); } /* free_pipe_list has the side effect of clearing memory. * In the long run that function can be merged with run_list, * but doing that now would hobble the debugging effort. */ free_pipe_list(pi); debug_printf_exec("run_and_free_list return %d\n", rcode); return rcode; } static void install_sighandlers(unsigned mask) { sighandler_t old_handler; unsigned sig = 0; while ((mask >>= 1) != 0) { sig++; if (!(mask & 1)) continue; old_handler = install_sighandler(sig, pick_sighandler(sig)); /* POSIX allows shell to re-enable SIGCHLD * even if it was SIG_IGN on entry. * Therefore we skip IGN check for it: */ if (sig == SIGCHLD) continue; if (old_handler == SIG_IGN) { /* oops... restore back to IGN, and record this fact */ install_sighandler(sig, old_handler); if (!G.traps) G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); free(G.traps[sig]); G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ } } } /* Called a few times only (or even once if "sh -c") */ static void install_special_sighandlers(void) { unsigned mask; /* Which signals are shell-special? */ mask = (1 << SIGQUIT) | (1 << SIGCHLD); if (G_interactive_fd) { mask |= SPECIAL_INTERACTIVE_SIGS; if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */ mask |= SPECIAL_JOBSTOP_SIGS; } /* Careful, do not re-install handlers we already installed */ if (G.special_sig_mask != mask) { unsigned diff = mask & ~G.special_sig_mask; G.special_sig_mask = mask; install_sighandlers(diff); } } #if ENABLE_HUSH_JOB /* helper */ /* Set handlers to restore tty pgrp and exit */ static void install_fatal_sighandlers(void) { unsigned mask; /* We will restore tty pgrp on these signals */ mask = 0 + (1 << SIGILL ) * HUSH_DEBUG + (1 << SIGFPE ) * HUSH_DEBUG + (1 << SIGBUS ) * HUSH_DEBUG + (1 << SIGSEGV) * HUSH_DEBUG + (1 << SIGTRAP) * HUSH_DEBUG + (1 << SIGABRT) /* bash 3.2 seems to handle these just like 'fatal' ones */ + (1 << SIGPIPE) + (1 << SIGALRM) /* if we are interactive, SIGHUP, SIGTERM and SIGINT are special sigs. * if we aren't interactive... but in this case * we never want to restore pgrp on exit, and this fn is not called */ /*+ (1 << SIGHUP )*/ /*+ (1 << SIGTERM)*/ /*+ (1 << SIGINT )*/ ; G_fatal_sig_mask = mask; install_sighandlers(mask); } #endif static int set_mode(int state, char mode, const char *o_opt) { int idx; switch (mode) { case 'n': G.o_opt[OPT_O_NOEXEC] = state; break; case 'x': IF_HUSH_MODE_X(G_x_mode = state;) break; case 'o': if (!o_opt) { /* "set -+o" without parameter. * in bash, set -o produces this output: * pipefail off * and set +o: * set +o pipefail * We always use the second form. */ const char *p = o_opt_strings; idx = 0; while (*p) { printf("set %co %s\n", (G.o_opt[idx] ? '-' : '+'), p); idx++; p += strlen(p) + 1; } break; } idx = index_in_strings(o_opt_strings, o_opt); if (idx >= 0) { G.o_opt[idx] = state; break; } default: return EXIT_FAILURE; } return EXIT_SUCCESS; } int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int hush_main(int argc, char **argv) { enum { OPT_login = (1 << 0), }; unsigned flags; int opt; unsigned builtin_argc; char **e; struct variable *cur_var; struct variable *shell_ver; INIT_G(); if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ G.last_exitcode = EXIT_SUCCESS; #if ENABLE_HUSH_FAST G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ #endif #if !BB_MMU G.argv0_for_re_execing = argv[0]; #endif /* Deal with HUSH_VERSION */ shell_ver = xzalloc(sizeof(*shell_ver)); shell_ver->flg_export = 1; shell_ver->flg_read_only = 1; /* Code which handles ${var<op>...} needs writable values for all variables, * therefore we xstrdup: */ shell_ver->varstr = xstrdup(hush_version_str); /* Create shell local variables from the values * currently living in the environment */ debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); unsetenv("HUSH_VERSION"); /* in case it exists in initial env */ G.top_var = shell_ver; cur_var = G.top_var; e = environ; if (e) while (*e) { char *value = strchr(*e, '='); if (value) { /* paranoia */ cur_var->next = xzalloc(sizeof(*cur_var)); cur_var = cur_var->next; cur_var->varstr = *e; cur_var->max_len = strlen(*e); cur_var->flg_export = 1; } e++; } /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */ debug_printf_env("putenv '%s'\n", shell_ver->varstr); putenv(shell_ver->varstr); /* Export PWD */ set_pwd_var(/*exp:*/ 1); /* bash also exports SHLVL and _, * and sets (but doesn't export) the following variables: * BASH=/bin/bash * BASH_VERSINFO=([0]="3" [1]="2" [2]="0" [3]="1" [4]="release" [5]="i386-pc-linux-gnu") * BASH_VERSION='3.2.0(1)-release' * HOSTTYPE=i386 * MACHTYPE=i386-pc-linux-gnu * OSTYPE=linux-gnu * HOSTNAME=<xxxxxxxxxx> * PPID=<NNNNN> - we also do it elsewhere * EUID=<NNNNN> * UID=<NNNNN> * GROUPS=() * LINES=<NNN> * COLUMNS=<NNN> * BASH_ARGC=() * BASH_ARGV=() * BASH_LINENO=() * BASH_SOURCE=() * DIRSTACK=() * PIPESTATUS=([0]="0") * HISTFILE=/<xxx>/.bash_history * HISTFILESIZE=500 * HISTSIZE=500 * MAILCHECK=60 * PATH=/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:. * SHELL=/bin/bash * SHELLOPTS=braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor * TERM=dumb * OPTERR=1 * OPTIND=1 * IFS=$' \t\n' * PS1='\s-\v\$ ' * PS2='> ' * PS4='+ ' */ #if ENABLE_FEATURE_EDITING G.line_input_state = new_line_input_t(FOR_SHELL); #endif /* Initialize some more globals to non-zero values */ cmdedit_update_prompt(); if (setjmp(die_jmp)) { /* xfunc has failed! die die die */ /* no EXIT traps, this is an escape hatch! */ G.exiting = 1; hush_exit(xfunc_error_retval); } /* Shell is non-interactive at first. We need to call * install_special_sighandlers() if we are going to execute "sh <script>", * "sh -c <cmds>" or login shell's /etc/profile and friends. * If we later decide that we are interactive, we run install_special_sighandlers() * in order to intercept (more) signals. */ /* Parse options */ /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; builtin_argc = 0; while (1) { opt = getopt(argc, argv, "+c:xinsl" #if !BB_MMU "<:$:R:V:" # if ENABLE_HUSH_FUNCTIONS "F:" # endif #endif ); if (opt <= 0) break; switch (opt) { case 'c': /* Possibilities: * sh ... -c 'script' * sh ... -c 'script' ARG0 [ARG1...] * On NOMMU, if builtin_argc != 0, * sh ... -c 'builtin' BARGV... "" ARG0 [ARG1...] * "" needs to be replaced with NULL * and BARGV vector fed to builtin function. * Note: the form without ARG0 never happens: * sh ... -c 'builtin' BARGV... "" */ if (!G.root_pid) { G.root_pid = getpid(); G.root_ppid = getppid(); } G.global_argv = argv + optind; G.global_argc = argc - optind; if (builtin_argc) { /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */ const struct built_in_command *x; install_special_sighandlers(); x = find_builtin(optarg); if (x) { /* paranoia */ G.global_argc -= builtin_argc; /* skip [BARGV...] "" */ G.global_argv += builtin_argc; G.global_argv[-1] = NULL; /* replace "" */ fflush_all(); G.last_exitcode = x->b_function(argv + optind - 1); } goto final_return; } if (!G.global_argv[0]) { /* -c 'script' (no params): prevent empty $0 */ G.global_argv--; /* points to argv[i] of 'script' */ G.global_argv[0] = argv[0]; G.global_argc++; } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */ install_special_sighandlers(); parse_and_run_string(optarg); goto final_return; case 'i': /* Well, we cannot just declare interactiveness, * we have to have some stuff (ctty, etc) */ /* G_interactive_fd++; */ break; case 's': /* "-s" means "read from stdin", but this is how we always * operate, so simply do nothing here. */ break; case 'l': flags |= OPT_login; break; #if !BB_MMU case '<': /* "big heredoc" support */ full_write1_str(optarg); _exit(0); case '$': { unsigned long long empty_trap_mask; G.root_pid = bb_strtou(optarg, &optarg, 16); optarg++; G.root_ppid = bb_strtou(optarg, &optarg, 16); optarg++; G.last_bg_pid = bb_strtou(optarg, &optarg, 16); optarg++; G.last_exitcode = bb_strtou(optarg, &optarg, 16); optarg++; builtin_argc = bb_strtou(optarg, &optarg, 16); optarg++; empty_trap_mask = bb_strtoull(optarg, &optarg, 16); if (empty_trap_mask != 0) { int sig; install_special_sighandlers(); G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); for (sig = 1; sig < NSIG; sig++) { if (empty_trap_mask & (1LL << sig)) { G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ install_sighandler(sig, SIG_IGN); } } } # if ENABLE_HUSH_LOOPS optarg++; G.depth_of_loop = bb_strtou(optarg, &optarg, 16); # endif break; } case 'R': case 'V': set_local_var(xstrdup(optarg), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ opt == 'R'); break; # if ENABLE_HUSH_FUNCTIONS case 'F': { struct function *funcp = new_function(optarg); /* funcp->name is already set to optarg */ /* funcp->body is set to NULL. It's a special case. */ funcp->body_as_string = argv[optind]; optind++; break; } # endif #endif case 'n': case 'x': if (set_mode(1, opt, NULL) == 0) /* no error */ break; default: #ifndef BB_VER fprintf(stderr, "Usage: sh [FILE]...\n" " or: sh -c command [args]...\n\n"); exit(EXIT_FAILURE); #else bb_show_usage(); #endif } } /* option parsing loop */ /* Skip options. Try "hush -l": $1 should not be "-l"! */ G.global_argc = argc - (optind - 1); G.global_argv = argv + (optind - 1); G.global_argv[0] = argv[0]; if (!G.root_pid) { G.root_pid = getpid(); G.root_ppid = getppid(); } /* If we are login shell... */ if (flags & OPT_login) { FILE *input; debug_printf("sourcing /etc/profile\n"); input = fopen_for_read("/etc/profile"); if (input != NULL) { close_on_exec_on(fileno(input)); install_special_sighandlers(); parse_and_run_file(input); fclose(input); } /* bash: after sourcing /etc/profile, * tries to source (in the given order): * ~/.bash_profile, ~/.bash_login, ~/.profile, * stopping on first found. --noprofile turns this off. * bash also sources ~/.bash_logout on exit. * If called as sh, skips .bash_XXX files. */ } if (G.global_argv[1]) { FILE *input; /* * "bash <script>" (which is never interactive (unless -i?)) * sources $BASH_ENV here (without scanning $PATH). * If called as sh, does the same but with $ENV. */ G.global_argc--; G.global_argv++; debug_printf("running script '%s'\n", G.global_argv[0]); input = xfopen_for_read(G.global_argv[0]); close_on_exec_on(fileno(input)); install_special_sighandlers(); parse_and_run_file(input); #if ENABLE_FEATURE_CLEAN_UP fclose(input); #endif goto final_return; } /* Up to here, shell was non-interactive. Now it may become one. * NB: don't forget to (re)run install_special_sighandlers() as needed. */ /* A shell is interactive if the '-i' flag was given, * or if all of the following conditions are met: * no -c command * no arguments remaining or the -s flag given * standard input is a terminal * standard output is a terminal * Refer to Posix.2, the description of the 'sh' utility. */ #if ENABLE_HUSH_JOB if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp); if (G_saved_tty_pgrp < 0) G_saved_tty_pgrp = 0; /* try to dup stdin to high fd#, >= 255 */ G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); if (G_interactive_fd < 0) { /* try to dup to any fd */ G_interactive_fd = dup(STDIN_FILENO); if (G_interactive_fd < 0) { /* give up */ G_interactive_fd = 0; G_saved_tty_pgrp = 0; } } // TODO: track & disallow any attempts of user // to (inadvertently) close/redirect G_interactive_fd } debug_printf("interactive_fd:%d\n", G_interactive_fd); if (G_interactive_fd) { close_on_exec_on(G_interactive_fd); if (G_saved_tty_pgrp) { /* If we were run as 'hush &', sleep until we are * in the foreground (tty pgrp == our pgrp). * If we get started under a job aware app (like bash), * make sure we are now in charge so we don't fight over * who gets the foreground */ while (1) { pid_t shell_pgrp = getpgrp(); G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd); if (G_saved_tty_pgrp == shell_pgrp) break; /* send TTIN to ourself (should stop us) */ kill(- shell_pgrp, SIGTTIN); } } /* Install more signal handlers */ install_special_sighandlers(); if (G_saved_tty_pgrp) { /* Set other signals to restore saved_tty_pgrp */ install_fatal_sighandlers(); /* Put ourselves in our own process group * (bash, too, does this only if ctty is available) */ bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ /* Grab control of the terminal */ tcsetpgrp(G_interactive_fd, getpid()); } /* -1 is special - makes xfuncs longjmp, not exit * (we reset die_sleep = 0 whereever we [v]fork) */ enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */ # if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0 { const char *hp = get_local_var_value("HISTFILE"); if (!hp) { hp = get_local_var_value("HOME"); if (hp) hp = concat_path_file(hp, ".hush_history"); } else { hp = xstrdup(hp); } if (hp) { G.line_input_state->hist_file = hp; //set_local_var(xasprintf("HISTFILE=%s", ...)); } # if ENABLE_FEATURE_SH_HISTFILESIZE hp = get_local_var_value("HISTFILESIZE"); G.line_input_state->max_history = size_from_HISTFILESIZE(hp); # endif } # endif } else { install_special_sighandlers(); } #elif ENABLE_HUSH_INTERACTIVE /* No job control compiled in, only prompt/line editing */ if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); if (G_interactive_fd < 0) { /* try to dup to any fd */ G_interactive_fd = dup(STDIN_FILENO); if (G_interactive_fd < 0) /* give up */ G_interactive_fd = 0; } } if (G_interactive_fd) { close_on_exec_on(G_interactive_fd); } install_special_sighandlers(); #else /* We have interactiveness code disabled */ install_special_sighandlers(); #endif /* bash: * if interactive but not a login shell, sources ~/.bashrc * (--norc turns this off, --rcfile <file> overrides) */ if (!ENABLE_FEATURE_SH_EXTRA_QUIET && G_interactive_fd) { /* note: ash and hush share this string */ printf("\n\n%s %s\n" IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n") "\n", bb_banner, "hush - the humble shell" ); } parse_and_run_file(stdin); final_return: hush_exit(G.last_exitcode); } #if ENABLE_MSH int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int msh_main(int argc, char **argv) { //bb_error_msg("msh is deprecated, please use hush instead"); return hush_main(argc, argv); } #endif /* * Built-ins */ static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM) { return 0; } static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv)) { int argc = 0; while (*argv) { argc++; argv++; } return applet_main_func(argc, argv - argc); } static int FAST_FUNC builtin_test(char **argv) { return run_applet_main(argv, test_main); } static int FAST_FUNC builtin_echo(char **argv) { return run_applet_main(argv, echo_main); } #if ENABLE_PRINTF static int FAST_FUNC builtin_printf(char **argv) { return run_applet_main(argv, printf_main); } #endif static char **skip_dash_dash(char **argv) { argv++; if (argv[0] && argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == '\0') argv++; return argv; } static int FAST_FUNC builtin_eval(char **argv) { int rcode = EXIT_SUCCESS; argv = skip_dash_dash(argv); if (*argv) { char *str = expand_strvec_to_string(argv); /* bash: * eval "echo Hi; done" ("done" is syntax error): * "echo Hi" will not execute too. */ parse_and_run_string(str); free(str); rcode = G.last_exitcode; } return rcode; } static int FAST_FUNC builtin_cd(char **argv) { const char *newdir; argv = skip_dash_dash(argv); newdir = argv[0]; if (newdir == NULL) { /* bash does nothing (exitcode 0) if HOME is ""; if it's unset, * bash says "bash: cd: HOME not set" and does nothing * (exitcode 1) */ const char *home = get_local_var_value("HOME"); newdir = home ? home : "/"; } if (chdir(newdir)) { /* Mimic bash message exactly */ bb_perror_msg("cd: %s", newdir); return EXIT_FAILURE; } /* Read current dir (get_cwd(1) is inside) and set PWD. * Note: do not enforce exporting. If PWD was unset or unexported, * set it again, but do not export. bash does the same. */ set_pwd_var(/*exp:*/ 0); return EXIT_SUCCESS; } static int FAST_FUNC builtin_exec(char **argv) { argv = skip_dash_dash(argv); if (argv[0] == NULL) return EXIT_SUCCESS; /* bash does this */ /* Careful: we can end up here after [v]fork. Do not restore * tty pgrp then, only top-level shell process does that */ if (G_saved_tty_pgrp && getpid() == G.root_pid) tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); /* TODO: if exec fails, bash does NOT exit! We do. * We'll need to undo trap cleanup (it's inside execvp_or_die) * and tcsetpgrp, and this is inherently racy. */ execvp_or_die(argv); } static int FAST_FUNC builtin_exit(char **argv) { debug_printf_exec("%s()\n", __func__); /* interactive bash: * # trap "echo EEE" EXIT * # exit * exit * There are stopped jobs. * (if there are _stopped_ jobs, running ones don't count) * # exit * exit * EEE (then bash exits) * * TODO: we can use G.exiting = -1 as indicator "last cmd was exit" */ /* note: EXIT trap is run by hush_exit */ argv = skip_dash_dash(argv); if (argv[0] == NULL) hush_exit(G.last_exitcode); /* mimic bash: exit 123abc == exit 255 + error msg */ xfunc_error_retval = 255; /* bash: exit -2 == exit 254, no error msg */ hush_exit(xatoi(argv[0]) & 0xff); } static void print_escaped(const char *s) { if (*s == '\'') goto squote; do { const char *p = strchrnul(s, '\''); /* print 'xxxx', possibly just '' */ printf("'%.*s'", (int)(p - s), s); if (*p == '\0') break; s = p; squote: /* s points to '; print "'''...'''" */ putchar('"'); do putchar('\''); while (*++s == '\''); putchar('"'); } while (*s); } #if !ENABLE_HUSH_LOCAL #define helper_export_local(argv, exp, lvl) \ helper_export_local(argv, exp) #endif static void helper_export_local(char **argv, int exp, int lvl) { do { char *name = *argv; char *name_end = strchrnul(name, '='); /* So far we do not check that name is valid (TODO?) */ if (*name_end == '\0') { struct variable *var, **vpp; vpp = get_ptr_to_local_var(name, name_end - name); var = vpp ? *vpp : NULL; if (exp == -1) { /* unexporting? */ /* export -n NAME (without =VALUE) */ if (var) { var->flg_export = 0; debug_printf_env("%s: unsetenv '%s'\n", __func__, name); unsetenv(name); } /* else: export -n NOT_EXISTING_VAR: no-op */ continue; } if (exp == 1) { /* exporting? */ /* export NAME (without =VALUE) */ if (var) { var->flg_export = 1; debug_printf_env("%s: putenv '%s'\n", __func__, var->varstr); putenv(var->varstr); continue; } } /* Exporting non-existing variable. * bash does not put it in environment, * but remembers that it is exported, * and does put it in env when it is set later. * We just set it to "" and export. */ /* Or, it's "local NAME" (without =VALUE). * bash sets the value to "". */ name = xasprintf("%s=", name); } else { /* (Un)exporting/making local NAME=VALUE */ name = xstrdup(name); } set_local_var(name, /*exp:*/ exp, /*lvl:*/ lvl, /*ro:*/ 0); } while (*++argv); } static int FAST_FUNC builtin_export(char **argv) { unsigned opt_unexport; #if ENABLE_HUSH_EXPORT_N /* "!": do not abort on errors */ opt_unexport = getopt32(argv, "!n"); if (opt_unexport == (uint32_t)-1) return EXIT_FAILURE; argv += optind; #else opt_unexport = 0; argv++; #endif if (argv[0] == NULL) { char **e = environ; if (e) { while (*e) { #if 0 puts(*e++); #else /* ash emits: export VAR='VAL' * bash: declare -x VAR="VAL" * we follow ash example */ const char *s = *e++; const char *p = strchr(s, '='); if (!p) /* wtf? take next variable */ continue; /* export var= */ printf("export %.*s", (int)(p - s) + 1, s); print_escaped(p + 1); putchar('\n'); #endif } /*fflush_all(); - done after each builtin anyway */ } return EXIT_SUCCESS; } helper_export_local(argv, (opt_unexport ? -1 : 1), 0); return EXIT_SUCCESS; } #if ENABLE_HUSH_LOCAL static int FAST_FUNC builtin_local(char **argv) { if (G.func_nest_level == 0) { bb_error_msg("%s: not in a function", argv[0]); return EXIT_FAILURE; /* bash compat */ } helper_export_local(argv, 0, G.func_nest_level); return EXIT_SUCCESS; } #endif static int FAST_FUNC builtin_trap(char **argv) { int sig; char *new_cmd; if (!G.traps) G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); argv++; if (!*argv) { int i; /* No args: print all trapped */ for (i = 0; i < NSIG; ++i) { if (G.traps[i]) { printf("trap -- "); print_escaped(G.traps[i]); /* note: bash adds "SIG", but only if invoked * as "bash". If called as "sh", or if set -o posix, * then it prints short signal names. * We are printing short names: */ printf(" %s\n", get_signame(i)); } } /*fflush_all(); - done after each builtin anyway */ return EXIT_SUCCESS; } new_cmd = NULL; /* If first arg is a number: reset all specified signals */ sig = bb_strtou(*argv, NULL, 10); if (errno == 0) { int ret; process_sig_list: ret = EXIT_SUCCESS; while (*argv) { sighandler_t handler; sig = get_signum(*argv++); if (sig < 0 || sig >= NSIG) { ret = EXIT_FAILURE; /* Mimic bash message exactly */ bb_perror_msg("trap: %s: invalid signal specification", argv[-1]); continue; } free(G.traps[sig]); G.traps[sig] = xstrdup(new_cmd); debug_printf("trap: setting SIG%s (%i) to '%s'\n", get_signame(sig), sig, G.traps[sig]); /* There is no signal for 0 (EXIT) */ if (sig == 0) continue; if (new_cmd) handler = (new_cmd[0] ? record_pending_signo : SIG_IGN); else /* We are removing trap handler */ handler = pick_sighandler(sig); install_sighandler(sig, handler); } return ret; } if (!argv[1]) { /* no second arg */ bb_error_msg("trap: invalid arguments"); return EXIT_FAILURE; } /* First arg is "-": reset all specified to default */ /* First arg is "--": skip it, the rest is "handler SIGs..." */ /* Everything else: set arg as signal handler * (includes "" case, which ignores signal) */ if (argv[0][0] == '-') { if (argv[0][1] == '\0') { /* "-" */ /* new_cmd remains NULL: "reset these sigs" */ goto reset_traps; } if (argv[0][1] == '-' && argv[0][2] == '\0') { /* "--" */ argv++; } /* else: "-something", no special meaning */ } new_cmd = *argv; reset_traps: argv++; goto process_sig_list; } /* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */ static int FAST_FUNC builtin_type(char **argv) { int ret = EXIT_SUCCESS; while (*++argv) { const char *type; char *path = NULL; if (0) {} /* make conditional compile easier below */ /*else if (find_alias(*argv)) type = "an alias";*/ #if ENABLE_HUSH_FUNCTIONS else if (find_function(*argv)) type = "a function"; #endif else if (find_builtin(*argv)) type = "a shell builtin"; else if ((path = find_in_path(*argv)) != NULL) type = path; else { bb_error_msg("type: %s: not found", *argv); ret = EXIT_FAILURE; continue; } printf("%s is %s\n", *argv, type); free(path); } return ret; } #if ENABLE_HUSH_JOB /* built-in 'fg' and 'bg' handler */ static int FAST_FUNC builtin_fg_bg(char **argv) { int i, jobnum; struct pipe *pi; if (!G_interactive_fd) return EXIT_FAILURE; /* If they gave us no args, assume they want the last backgrounded task */ if (!argv[1]) { for (pi = G.job_list; pi; pi = pi->next) { if (pi->jobid == G.last_jobid) { goto found; } } bb_error_msg("%s: no current job", argv[0]); return EXIT_FAILURE; } if (sscanf(argv[1], "%%%d", &jobnum) != 1) { bb_error_msg("%s: bad argument '%s'", argv[0], argv[1]); return EXIT_FAILURE; } for (pi = G.job_list; pi; pi = pi->next) { if (pi->jobid == jobnum) { goto found; } } bb_error_msg("%s: %d: no such job", argv[0], jobnum); return EXIT_FAILURE; found: /* TODO: bash prints a string representation * of job being foregrounded (like "sleep 1 | cat") */ if (argv[0][0] == 'f' && G_saved_tty_pgrp) { /* Put the job into the foreground. */ tcsetpgrp(G_interactive_fd, pi->pgrp); } /* Restart the processes in the job */ debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_cmds, pi->pgrp); for (i = 0; i < pi->num_cmds; i++) { debug_printf_jobs("reviving pid %d\n", pi->cmds[i].pid); } pi->stopped_cmds = 0; i = kill(- pi->pgrp, SIGCONT); if (i < 0) { if (errno == ESRCH) { delete_finished_bg_job(pi); return EXIT_SUCCESS; } bb_perror_msg("kill (SIGCONT)"); } if (argv[0][0] == 'f') { remove_bg_job(pi); return checkjobs_and_fg_shell(pi); } return EXIT_SUCCESS; } #endif #if ENABLE_HUSH_HELP static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM) { const struct built_in_command *x; printf( "Built-in commands:\n" "------------------\n"); for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) { if (x->b_descr) printf("%-10s%s\n", x->b_cmd, x->b_descr); } bb_putchar('\n'); return EXIT_SUCCESS; } #endif #if MAX_HISTORY && ENABLE_FEATURE_EDITING static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM) { show_history(G.line_input_state); return EXIT_SUCCESS; } #endif #if ENABLE_HUSH_JOB static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM) { struct pipe *job; const char *status_string; for (job = G.job_list; job; job = job->next) { if (job->alive_cmds == job->stopped_cmds) status_string = "Stopped"; else status_string = "Running"; printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext); } return EXIT_SUCCESS; } #endif #if HUSH_DEBUG static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM) { void *p; unsigned long l; # ifdef M_TRIM_THRESHOLD /* Optional. Reduces probability of false positives */ malloc_trim(0); # endif /* Crude attempt to find where "free memory" starts, * sans fragmentation. */ p = malloc(240); l = (unsigned long)p; free(p); p = malloc(3400); if (l < (unsigned long)p) l = (unsigned long)p; free(p); if (!G.memleak_value) G.memleak_value = l; l -= G.memleak_value; if ((long)l < 0) l = 0; l /= 1024; if (l > 127) l = 127; /* Exitcode is "how many kilobytes we leaked since 1st call" */ return l; } #endif static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM) { puts(get_cwd(0)); return EXIT_SUCCESS; } /* Interruptibility of read builtin in bash * (tested on bash-4.2.8 by sending signals (not by ^C)): * * Empty trap makes read ignore corresponding signal, for any signal. * * SIGINT: * - terminates non-interactive shell; * - interrupts read in interactive shell; * if it has non-empty trap: * - executes trap and returns to command prompt in interactive shell; * - executes trap and returns to read in non-interactive shell; * SIGTERM: * - is ignored (does not interrupt) read in interactive shell; * - terminates non-interactive shell; * if it has non-empty trap: * - executes trap and returns to read; * SIGHUP: * - terminates shell (regardless of interactivity); * if it has non-empty trap: * - executes trap and returns to read; */ static int FAST_FUNC builtin_read(char **argv) { const char *r; char *opt_n = NULL; char *opt_p = NULL; char *opt_t = NULL; char *opt_u = NULL; const char *ifs; int read_flags; /* "!": do not abort on errors. * Option string must start with "sr" to match BUILTIN_READ_xxx */ read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u); if (read_flags == (uint32_t)-1) return EXIT_FAILURE; argv += optind; ifs = get_local_var_value("IFS"); /* can be NULL */ again: r = shell_builtin_read(set_local_var_from_halves, argv, ifs, read_flags, opt_n, opt_p, opt_t, opt_u ); if ((uintptr_t)r == 1 && errno == EINTR) { unsigned sig = check_and_run_traps(); if (sig && sig != SIGINT) goto again; } if ((uintptr_t)r > 1) { bb_error_msg("%s", r); r = (char*)(uintptr_t)1; } return (uintptr_t)r; } /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set * built-in 'set' handler * SUSv3 says: * set [-abCefhmnuvx] [-o option] [argument...] * set [+abCefhmnuvx] [+o option] [argument...] * set -- [argument...] * set -o * set +o * Implementations shall support the options in both their hyphen and * plus-sign forms. These options can also be specified as options to sh. * Examples: * Write out all variables and their values: set * Set $1, $2, and $3 and set "$#" to 3: set c a b * Turn on the -x and -v options: set -xv * Unset all positional parameters: set -- * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x" * Set the positional parameters to the expansion of x, even if x expands * with a leading '-' or '+': set -- $x * * So far, we only support "set -- [argument...]" and some of the short names. */ static int FAST_FUNC builtin_set(char **argv) { int n; char **pp, **g_argv; char *arg = *++argv; if (arg == NULL) { struct variable *e; for (e = G.top_var; e; e = e->next) puts(e->varstr); return EXIT_SUCCESS; } do { if (strcmp(arg, "--") == 0) { ++argv; goto set_argv; } if (arg[0] != '+' && arg[0] != '-') break; for (n = 1; arg[n]; ++n) { if (set_mode((arg[0] == '-'), arg[n], argv[1])) goto error; if (arg[n] == 'o' && argv[1]) argv++; } } while ((arg = *++argv) != NULL); /* Now argv[0] is 1st argument */ if (arg == NULL) return EXIT_SUCCESS; set_argv: /* NB: G.global_argv[0] ($0) is never freed/changed */ g_argv = G.global_argv; if (G.global_args_malloced) { pp = g_argv; while (*++pp) free(*pp); g_argv[1] = NULL; } else { G.global_args_malloced = 1; pp = xzalloc(sizeof(pp[0]) * 2); pp[0] = g_argv[0]; /* retain $0 */ g_argv = pp; } /* This realloc's G.global_argv */ G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1); n = 1; while (*++pp) n++; G.global_argc = n; return EXIT_SUCCESS; /* Nothing known, so abort */ error: bb_error_msg("set: %s: invalid option", arg); return EXIT_FAILURE; } static int FAST_FUNC builtin_shift(char **argv) { int n = 1; argv = skip_dash_dash(argv); if (argv[0]) { n = atoi(argv[0]); } if (n >= 0 && n < G.global_argc) { if (G.global_args_malloced) { int m = 1; while (m <= n) free(G.global_argv[m++]); } G.global_argc -= n; memmove(&G.global_argv[1], &G.global_argv[n+1], G.global_argc * sizeof(G.global_argv[0])); return EXIT_SUCCESS; } return EXIT_FAILURE; } static int FAST_FUNC builtin_source(char **argv) { char *arg_path, *filename; FILE *input; save_arg_t sv; #if ENABLE_HUSH_FUNCTIONS smallint sv_flg; #endif argv = skip_dash_dash(argv); filename = argv[0]; if (!filename) { /* bash says: "bash: .: filename argument required" */ return 2; /* bash compat */ } arg_path = NULL; if (!strchr(filename, '/')) { arg_path = find_in_path(filename); if (arg_path) filename = arg_path; } input = fopen_or_warn(filename, "r"); free(arg_path); if (!input) { /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */ /* POSIX: non-interactive shell should abort here, * not merely fail. So far no one complained :) */ return EXIT_FAILURE; } close_on_exec_on(fileno(input)); #if ENABLE_HUSH_FUNCTIONS sv_flg = G.flag_return_in_progress; /* "we are inside sourced file, ok to use return" */ G.flag_return_in_progress = -1; #endif if (argv[1]) save_and_replace_G_args(&sv, argv); parse_and_run_file(input); fclose(input); if (argv[1]) restore_G_args(&sv, argv); #if ENABLE_HUSH_FUNCTIONS G.flag_return_in_progress = sv_flg; #endif return G.last_exitcode; } static int FAST_FUNC builtin_umask(char **argv) { int rc; mode_t mask; mask = umask(0); argv = skip_dash_dash(argv); if (argv[0]) { mode_t old_mask = mask; mask ^= 0777; rc = bb_parse_mode(argv[0], &mask); mask ^= 0777; if (rc == 0) { mask = old_mask; /* bash messages: * bash: umask: 'q': invalid symbolic mode operator * bash: umask: 999: octal number out of range */ bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]); } } else { rc = 1; /* Mimic bash */ printf("%04o\n", (unsigned) mask); /* fall through and restore mask which we set to 0 */ } umask(mask); return !rc; /* rc != 0 - success */ } /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */ static int FAST_FUNC builtin_unset(char **argv) { int ret; unsigned opts; /* "!": do not abort on errors */ /* "+": stop at 1st non-option */ opts = getopt32(argv, "!+vf"); if (opts == (unsigned)-1) return EXIT_FAILURE; if (opts == 3) { bb_error_msg("unset: -v and -f are exclusive"); return EXIT_FAILURE; } argv += optind; ret = EXIT_SUCCESS; while (*argv) { if (!(opts & 2)) { /* not -f */ if (unset_local_var(*argv)) { /* unset <nonexistent_var> doesn't fail. * Error is when one tries to unset RO var. * Message was printed by unset_local_var. */ ret = EXIT_FAILURE; } } #if ENABLE_HUSH_FUNCTIONS else { unset_func(*argv); } #endif argv++; } return ret; } /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ static int FAST_FUNC builtin_wait(char **argv) { int ret = EXIT_SUCCESS; int status; argv = skip_dash_dash(argv); if (argv[0] == NULL) { /* Don't care about wait results */ /* Note 1: must wait until there are no more children */ /* Note 2: must be interruptible */ /* Examples: * $ sleep 3 & sleep 6 & wait * [1] 30934 sleep 3 * [2] 30935 sleep 6 * [1] Done sleep 3 * [2] Done sleep 6 * $ sleep 3 & sleep 6 & wait * [1] 30936 sleep 3 * [2] 30937 sleep 6 * [1] Done sleep 3 * ^C <-- after ~4 sec from keyboard * $ */ while (1) { int sig; sigset_t oldset, allsigs; /* waitpid is not interruptible by SA_RESTARTed * signals which we use. Thus, this ugly dance: */ /* Make sure possible SIGCHLD is stored in kernel's * pending signal mask before we call waitpid. * Or else we may race with SIGCHLD, lose it, * and get stuck in sigwaitinfo... */ sigfillset(&allsigs); sigprocmask(SIG_SETMASK, &allsigs, &oldset); if (!sigisemptyset(&G.pending_set)) { /* Crap! we raced with some signal! */ // sig = 0; goto restore; } checkjobs(NULL); /* waitpid(WNOHANG) inside */ if (errno == ECHILD) { sigprocmask(SIG_SETMASK, &oldset, NULL); break; } /* Wait for SIGCHLD or any other signal */ //sig = sigwaitinfo(&allsigs, NULL); /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */ /* Note: sigsuspend invokes signal handler */ sigsuspend(&oldset); restore: sigprocmask(SIG_SETMASK, &oldset, NULL); /* So, did we get a signal? */ //if (sig > 0) // raise(sig); /* run handler */ sig = check_and_run_traps(); if (sig /*&& sig != SIGCHLD - always true */) { /* see note 2 */ ret = 128 + sig; break; } /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */ } return ret; } /* This is probably buggy wrt interruptible-ness */ while (*argv) { pid_t pid = bb_strtou(*argv, NULL, 10); if (errno) { /* mimic bash message */ bb_error_msg("wait: '%s': not a pid or valid job spec", *argv); return EXIT_FAILURE; } if (waitpid(pid, &status, 0) == pid) { if (WIFSIGNALED(status)) ret = 128 + WTERMSIG(status); else if (WIFEXITED(status)) ret = WEXITSTATUS(status); else /* wtf? */ ret = EXIT_FAILURE; } else { bb_perror_msg("wait %s", *argv); ret = 127; } argv++; } return ret; } #if ENABLE_HUSH_LOOPS || ENABLE_HUSH_FUNCTIONS static unsigned parse_numeric_argv1(char **argv, unsigned def, unsigned def_min) { if (argv[1]) { def = bb_strtou(argv[1], NULL, 10); if (errno || def < def_min || argv[2]) { bb_error_msg("%s: bad arguments", argv[0]); def = UINT_MAX; } } return def; } #endif #if ENABLE_HUSH_LOOPS static int FAST_FUNC builtin_break(char **argv) { unsigned depth; if (G.depth_of_loop == 0) { bb_error_msg("%s: only meaningful in a loop", argv[0]); return EXIT_SUCCESS; /* bash compat */ } G.flag_break_continue++; /* BC_BREAK = 1 */ G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1); if (depth == UINT_MAX) G.flag_break_continue = BC_BREAK; if (G.depth_of_loop < depth) G.depth_break_continue = G.depth_of_loop; return EXIT_SUCCESS; } static int FAST_FUNC builtin_continue(char **argv) { G.flag_break_continue = 1; /* BC_CONTINUE = 2 = 1+1 */ return builtin_break(argv); } #endif #if ENABLE_HUSH_FUNCTIONS static int FAST_FUNC builtin_return(char **argv) { int rc; if (G.flag_return_in_progress != -1) { bb_error_msg("%s: not in a function or sourced script", argv[0]); return EXIT_FAILURE; /* bash compat */ } G.flag_return_in_progress = 1; /* bash: * out of range: wraps around at 256, does not error out * non-numeric param: * f() { false; return qwe; }; f; echo $? * bash: return: qwe: numeric argument required <== we do this * 255 <== we also do this */ rc = parse_numeric_argv1(argv, G.last_exitcode, 0); return rc; } #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/math.h�������������������������������������������������������������������������0000644�0000000�0000000�00000006060�12263563520�014205� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* math.h - interface to shell math "library" -- this allows shells to share * the implementation of arithmetic $((...)) expansions. * * This aims to be a POSIX shell math library as documented here: * http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_04 * * See math.c for internal documentation. */ /* The math library has just one function: * * arith_t arith(arith_state_t *state, const char *expr); * * The expr argument is the math string to parse. All normal expansions must * be done already. i.e. no dollar symbols should be present. * * The state argument is a pointer to a struct of hooks for your shell (see below), * and an error message string (NULL if no error). * * The function returns the answer to the expression. So if you called it * with the expression: * "1 + 2 + 3" * you would obviously get back 6. */ /* To add support to a shell, you need to implement three functions: * * lookupvar() - look up and return the value of a variable * * If the shell does: * foo=123 * Then the code: * const char *val = lookupvar("foo"); * will result in val pointing to "123" * * setvar() - set a variable to some value * * If the arithmetic expansion does something like: * $(( i = 1)) * then the math code will make a call like so: * setvar("i", "1", 0); * The storage for the first two parameters are not allocated, so your * shell implementation will most likely need to strdup() them to save. * * endofname() - return the end of a variable name from input * * The arithmetic code does not know about variable naming conventions. * So when it is given an experession, it knows something is not numeric, * but it is up to the shell to dictate what is a valid identifiers. * So when it encounters something like: * $(( some_var + 123 )) * It will make a call like so: * end = endofname("some_var + 123"); * So the shell needs to scan the input string and return a pointer to the * first non-identifier string. In this case, it should return the input * pointer with an offset pointing to the first space. The typical * implementation will return the offset of first char that does not match * the regex (in C locale): ^[a-zA-Z_][a-zA-Z_0-9]* */ #ifndef SHELL_MATH_H #define SHELL_MATH_H 1 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN #if ENABLE_SH_MATH_SUPPORT_64 typedef long long arith_t; #define ARITH_FMT "%lld" #define strto_arith_t strtoull #else typedef long arith_t; #define ARITH_FMT "%ld" #define strto_arith_t strtoul #endif typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name); typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *val); //typedef const char* FAST_FUNC (*arith_var_endofname_t)(const char *name); typedef struct arith_state_t { const char *errmsg; arith_var_lookup_t lookupvar; arith_var_set_t setvar; // arith_var_endofname_t endofname; void *list_of_recursed_names; } arith_state_t; arith_t FAST_FUNC arith(arith_state_t *state, const char *expr); POP_SAVED_FUNCTION_VISIBILITY #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/cttyhack.c���������������������������������������������������������������������0000644�0000000�0000000�00000013104�12263563520�015056� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Copyright (c) 2007 Denys Vlasenko <vda.linux@googlemail.com> * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" //applet:IF_CTTYHACK(APPLET(cttyhack, BB_DIR_BIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_CTTYHACK) += cttyhack.o //config:config CTTYHACK //config: bool "cttyhack" //config: default y //config: help //config: One common problem reported on the mailing list is the "can't //config: access tty; job control turned off" error message, which typically //config: appears when one tries to use a shell with stdin/stdout on //config: /dev/console. //config: This device is special - it cannot be a controlling tty. //config: //config: The proper solution is to use the correct device instead of //config: /dev/console. //config: //config: cttyhack provides a "quick and dirty" solution to this problem. //config: It analyzes stdin with various ioctls, trying to determine whether //config: it is a /dev/ttyN or /dev/ttySN (virtual terminal or serial line). //config: On Linux it also checks sysfs for a pointer to the active console. //config: If cttyhack is able to find the real console device, it closes //config: stdin/out/err and reopens that device. //config: Then it executes the given program. Opening the device will make //config: that device a controlling tty. This may require cttyhack //config: to be a session leader. //config: //config: Example for /etc/inittab (for busybox init): //config: //config: ::respawn:/bin/cttyhack /bin/sh //config: //config: Starting an interactive shell from boot shell script: //config: //config: setsid cttyhack sh //config: //config: Giving controlling tty to shell running with PID 1: //config: //config: # exec cttyhack sh //config: //config: Without cttyhack, you need to know exact tty name, //config: and do something like this: //config: //config: # exec setsid sh -c 'exec sh </dev/tty1 >/dev/tty1 2>&1' //config: //config: Starting getty on a controlling tty from a shell script: //config: //config: # getty 115200 $(cttyhack) //usage:#define cttyhack_trivial_usage //usage: "[PROG ARGS]" //usage:#define cttyhack_full_usage "\n\n" //usage: "Give PROG a controlling tty if possible." //usage: "\nExample for /etc/inittab (for busybox init):" //usage: "\n ::respawn:/bin/cttyhack /bin/sh" //usage: "\nGiving controlling tty to shell running with PID 1:" //usage: "\n $ exec cttyhack sh" //usage: "\nStarting interactive shell from boot shell script:" //usage: "\n setsid cttyhack sh" #if !defined(__linux__) && !defined(TIOCGSERIAL) && !ENABLE_WERROR # warning cttyhack will not be able to detect a controlling tty on this system #endif /* From <linux/vt.h> */ struct vt_stat { unsigned short v_active; /* active vt */ unsigned short v_signal; /* signal to send */ unsigned short v_state; /* vt bitmask */ }; enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */ /* From <linux/serial.h> */ struct serial_struct { int type; int line; unsigned int port; int irq; int flags; int xmit_fifo_size; int custom_divisor; int baud_base; unsigned short close_delay; char io_type; char reserved_char[1]; int hub6; unsigned short closing_wait; /* time to wait before closing */ unsigned short closing_wait2; /* no longer used... */ unsigned char *iomem_base; unsigned short iomem_reg_shift; unsigned int port_high; unsigned long iomap_base; /* cookie passed into ioremap */ int reserved[1]; }; int cttyhack_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int cttyhack_main(int argc UNUSED_PARAM, char **argv) { int fd; char console[sizeof(int)*3 + 16]; union { struct vt_stat vt; struct serial_struct sr; char paranoia[sizeof(struct serial_struct) * 3]; } u; strcpy(console, "/dev/tty"); fd = open(console, O_RDWR); if (fd < 0) { /* We don't have ctty (or don't have "/dev/tty" node...) */ do { #ifdef __linux__ /* Note that this method does not use _stdin_. * Thus, "cttyhack </dev/something" can't be used. * However, this method is more reliable than * TIOCGSERIAL check, which assumes that all * serial lines follow /dev/ttySn convention - * which is not always the case. * Therefore, we use this method first: */ int s = open_read_close("/sys/class/tty/console/active", console + 5, sizeof(console) - 5); if (s > 0) { char *last; /* Found active console via sysfs (Linux 2.6.38+). * It looks like "[tty0 ]ttyS0\n" so zap the newline: */ console[4 + s] = '\0'; /* If there are multiple consoles, * take the last one: */ last = strrchr(console + 5, ' '); if (last) overlapping_strcpy(console + 5, last + 1); break; } if (ioctl(0, VT_GETSTATE, &u.vt) == 0) { /* this is linux virtual tty */ sprintf(console + 8, "S%u" + 1, (int)u.vt.v_active); break; } #endif #ifdef TIOCGSERIAL if (ioctl(0, TIOCGSERIAL, &u.sr) == 0) { /* this is a serial console; assuming it is named /dev/ttySn */ sprintf(console + 8, "S%u", (int)u.sr.line); break; } #endif /* nope, could not find it */ console[0] = '\0'; } while (0); } argv++; if (!argv[0]) { if (!console[0]) return EXIT_FAILURE; puts(console); return EXIT_SUCCESS; } if (fd < 0) { fd = open_or_warn(console, O_RDWR); if (fd < 0) goto ret; } //bb_error_msg("switching to '%s'", console); dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); while (fd > 2) close(fd--); /* Some other session may have it as ctty, * try to steal it from them: */ ioctl(0, TIOCSCTTY, 1); ret: BB_EXECVP_or_die(argv); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/match.h������������������������������������������������������������������������0000644�0000000�0000000�00000001446�12263563520�014353� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* match.h - interface to shell ##/%% matching code */ #ifndef SHELL_MATCH_H #define SHELL_MATCH_H 1 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN //TODO! Why ash.c still uses internal version?! enum { SCAN_MOVE_FROM_LEFT = (1 << 0), SCAN_MOVE_FROM_RIGHT = (1 << 1), SCAN_MATCH_LEFT_HALF = (1 << 2), SCAN_MATCH_RIGHT_HALF = (1 << 3), }; char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags); static inline unsigned pick_scan(char op1, char op2) { unsigned scan_flags; if (op1 == '#') { scan_flags = SCAN_MATCH_LEFT_HALF + (op1 == op2 ? SCAN_MOVE_FROM_RIGHT : SCAN_MOVE_FROM_LEFT); } else { /* % */ scan_flags = SCAN_MATCH_RIGHT_HALF + (op1 == op2 ? SCAN_MOVE_FROM_LEFT : SCAN_MOVE_FROM_RIGHT); } return scan_flags; } POP_SAVED_FUNCTION_VISIBILITY #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/random.h�����������������������������������������������������������������������0000644�0000000�0000000�00000001335�12263563520�014534� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * $RANDOM support. * * Copyright (C) 2009 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #ifndef SHELL_RANDOM_H #define SHELL_RANDOM_H 1 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN typedef struct random_t { /* Random number generators */ int32_t galois_LFSR; /* Galois LFSR (fast but weak). signed! */ uint32_t LCG; /* LCG (fast but weak) */ } random_t; #define UNINITED_RANDOM_T(rnd) \ ((rnd)->galois_LFSR == 0) #define INIT_RANDOM_T(rnd, nonzero, v) \ ((rnd)->galois_LFSR = (nonzero), (rnd)->LCG = (v)) #define CLEAR_RANDOM_T(rnd) \ ((rnd)->galois_LFSR = 0) uint32_t next_random(random_t *rnd) FAST_FUNC; POP_SAVED_FUNCTION_VISIBILITY #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/����������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�014715� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-misc/�������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�016421� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-misc/source2.tests������������������������������������������������0000755�0000000�0000000�00000000040�12263563520�021062� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������false . /dev/null echo Done: $? ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-misc/source1.tests������������������������������������������������0000755�0000000�0000000�00000000136�12263563520�021067� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo "echo Sourced ok" >../sourced.sh PATH="..:$PATH" . sourced.sh rm ../sourced.sh echo Done ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-misc/source1.right������������������������������������������������0000644�0000000�0000000�00000000020�12263563520�021027� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Sourced ok Done ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-misc/shift1.tests�������������������������������������������������0000755�0000000�0000000�00000000707�12263563520�020710� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$THIS_SH -c 'shift; echo "$@"' 0 1 2 3 4 #We do abort on -1, but then we abort. bash executes echo. $THIS_SH -c 'shift -1; echo "$@"' 0 1 2 3 4 $THIS_SH -c 'shift 0; echo "$@"' 0 1 2 3 4 $THIS_SH -c 'shift 1; echo "$@"' 0 1 2 3 4 $THIS_SH -c 'shift 2; echo "$@"' 0 1 2 3 4 $THIS_SH -c 'shift 3; echo "$@"' 0 1 2 3 4 $THIS_SH -c 'shift 4; echo "$@"' 0 1 2 3 4 $THIS_SH -c 'shift 5; echo "$@"' 0 1 2 3 4 $THIS_SH -c 'shift 6; echo "$@"' 0 1 2 3 4 ���������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-misc/source2.right������������������������������������������������0000644�0000000�0000000�00000000010�12263563520�021027� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Done: 0 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-misc/nulltick1.right����������������������������������������������0000644�0000000�0000000�00000000023�12263563520�021357� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Test 1 Test 2 Done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-misc/shift1.right�������������������������������������������������0000644�0000000�0000000�00000000120�12263563520�020645� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������2 3 4 0: shift: line 1: Illegal number: -1 1 2 3 4 2 3 4 3 4 4 1 2 3 4 1 2 3 4 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-misc/last_amp.right�����������������������������������������������0000644�0000000�0000000�00000000006�12263563520�021252� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������3 End ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-misc/nulltick1.tests����������������������������������������������0000755�0000000�0000000�00000000063�12263563520�021413� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo Test ` ` 1 echo Test `</dev/null` 2 echo Done �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-misc/echo_write_error.tests���������������������������������������0000644�0000000�0000000�00000000127�12263563520�023044� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������trap "" PIPE { sleep 1 echo Cant write this - get EPIPE echo Ok: $? >&2 } | { true; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-misc/last_amp.tests�����������������������������������������������0000755�0000000�0000000�00000000234�12263563520�021305� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$THIS_SH -c 'echo 3&' d=`date` while test "`date`" = "$d"; do true; done d1=`date` $THIS_SH -c 'sleep 1&' d2=`date` test "$d1" = "$d2" || echo BAD echo End ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-misc/echo_write_error.right���������������������������������������0000644�0000000�0000000�00000000044�12263563520�023015� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ash: write error: Broken pipe Ok: 1 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-quoting/����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�017154� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests�����������������������������0000755�0000000�0000000�00000001027�12263563520�025010� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# The bug here was triggered by: # * performin pathname expansion because we see [ # * replace operator did not escape \ in replace string IP=192.168.0.1 rm -f '192.168.0.1[' echo "${IP//./\\.}" echo "${IP//./\\.}"'[' # bug was here echo "${IP//./\\.}[" # bug was here echo "${IP//./\\\\.}[" # bug was here echo "192\.168\.0\.1[" echo >'192.168.0.1[' echo "${IP//./\\.}" echo "${IP//./\\.}"'[' # bug was here echo "${IP//./\\.}[" # bug was here echo "${IP//./\\\\.}[" # bug was here echo "192\.168\.0\.1[" rm -f '192.168.0.1[' ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.right�����������������������������0000644�0000000�0000000�00000000244�12263563520�024760� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������192\.168\.0\.1 192\.168\.0\.1[ 192\.168\.0\.1[ 192\\.168\\.0\\.1[ 192\.168\.0\.1[ 192\.168\.0\.1 192\.168\.0\.1[ 192\.168\.0\.1[ 192\\.168\\.0\\.1[ 192\.168\.0\.1[ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-quoting/dollar_squote_bash1.tests���������������������������������0000755�0000000�0000000�00000000246�12263563520�024176� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo $'a\tb' echo $'a\nb' $'c\nd''ef' echo $'a\'b' $'c\"d' $'e\\f' echo $'a\63b' $'c\063b' $'e\0633f' echo $'a\80b' $'c\608b' echo $'a\x33b' $'c\x330b' echo $'x\x9y' ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-quoting/dollar_squote_bash1.right���������������������������������0000644�0000000�0000000�00000000077�12263563520�024150� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a b a b c def a'b c"d e\f a3b c3b e33f a\80b c08b a3b c30b x y �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-standalone/�������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�017616� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-standalone/var_standalone1.tests����������������������������������0000755�0000000�0000000�00000000076�12263563520�023767� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������VAR=42 $THIS_SH -c 'unset VAR; env | grep ^VAR' echo Done: $? ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-standalone/nofork_trashes_getopt.tests����������������������������0000755�0000000�0000000�00000000330�12263563520�025310� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# In this test, rm is NOFORK and it modifies getopt internal state rm -f non_existent_file # Subsequent hexdump is run as NOEXEC, and thus still uses this state hexdump </dev/null # Did hexdump segfault etc? echo $? ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-standalone/nofork_trashes_getopt.right����������������������������0000644�0000000�0000000�00000000002�12263563520�025254� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-standalone/noexec_gets_no_env.right�������������������������������0000644�0000000�0000000�00000000026�12263563520�024520� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������VAR7=VAL 0 VAR8=VAL 0 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-standalone/noexec_gets_no_env.tests�������������������������������0000755�0000000�0000000�00000000115�12263563520�024547� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������export VAR7=VAL env | grep ^VAR7= echo $? VAR8=VAL env | grep ^VAR8= echo $? ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-standalone/var_standalone1.right����������������������������������0000644�0000000�0000000�00000000010�12263563520�023723� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Done: 1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-read/�������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�016401� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-read/read_n.right�������������������������������������������������0000644�0000000�0000000�00000000015�12263563520�020662� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test tes tes �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-read/read_ifs.right�����������������������������������������������0000644�0000000�0000000�00000000341�12263563520�021210� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test 1: .a. .b. .c. test 2: .a. .b. .c. test 3: .a. .. .b,c. test 4: .a. .. .b,c. test 5: .a. .. .c. test 6: .a. .. .c. .d. test 7: .a. .. .b,c,d , ,. test 8: .. .a. .b. .c. test 9: .a. .b. .c. .. test A: .. .a. .. .b. .c. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-read/read_t.right�������������������������������������������������0000644�0000000�0000000�00000000024�12263563520�020670� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>< >< >test< >test< ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-read/read_r.right�������������������������������������������������0000644�0000000�0000000�00000000017�12263563520�020670� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������testbest test\ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-read/read_ifs.tests�����������������������������������������������0000755�0000000�0000000�00000001611�12263563520�021241� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������printf 'a\t\tb\tc\n' | ( IFS=$(printf "\t") read a b c; echo "test 1: .$a. .$b. .$c." ) printf 'a\t\tb\tc\n' | ( IFS=$(printf " \t") read a b c; echo "test 2: .$a. .$b. .$c." ) printf 'a,,b,c\n' | ( IFS="," read a b c; echo "test 3: .$a. .$b. .$c." ) printf 'a,,b,c\n' | ( IFS=" ," read a b c; echo "test 4: .$a. .$b. .$c." ) printf 'a ,, c\n' | ( IFS=" ," read a b c; echo "test 5: .$a. .$b. .$c." ) printf 'a ,, c d\n' | ( IFS=" ," read a b c d; echo "test 6: .$a. .$b. .$c. .$d." ) printf ' a,,b,c,d , ,\n' | ( IFS=" ," read a b c; echo "test 7: .$a. .$b. .$c." ) printf '\t,\ta\t,\tb\tc' | ( IFS=$(printf " \t,") read a b c d; echo "test 8: .$a. .$b. .$c. .$d." ) printf '\t\ta\t,\tb\tc' | ( IFS=$(printf " \t,") read a b c d; echo "test 9: .$a. .$b. .$c. .$d." ) printf '\t,\ta\t,,\tb\tc' | ( IFS=$(printf " \t,") read a b c d e; echo "test A: .$a. .$b. .$c. .$d. .$e." ) �����������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-read/read_REPLY.tests���������������������������������������������0000755�0000000�0000000�00000000432�12263563520�021353� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo ' \abc1 d\ef ' | ( read ; echo "test 1: |$REPLY|" ) echo ' \abc2 d\ef ' | ( read -r ; echo "test 2: |$REPLY|" ) echo ' \abc3 d\ef ' | ( read REPLY; echo "test 3: |$REPLY|" ) echo ' \abc4 d\ef ' | ( read -r REPLY; echo "test 4: |$REPLY|" ) echo Done ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-read/read_REPLY.right���������������������������������������������0000644�0000000�0000000�00000000141�12263563520�021320� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test 1: | abc1 def | test 2: | \abc2 d\ef | test 3: |abc3 def| test 4: |\abc4 d\ef| Done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-read/read_n.tests�������������������������������������������������0000755�0000000�0000000�00000000207�12263563520�020715� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo 'test' | (read reply; echo "$reply") echo 'test' | (read -n 3 reply; echo "$reply") echo 'test' | (read -n3 reply; echo "$reply") �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-read/read_r.tests�������������������������������������������������0000755�0000000�0000000�00000000155�12263563520�020723� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo -e 'test\\\nbest' | (read reply; echo "$reply") echo -e 'test\\\nbest' | (read -r reply; echo "$reply") �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-read/read_t.tests�������������������������������������������������0000755�0000000�0000000�00000000545�12263563520�020730� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# bash 3.2 outputs: # >< { echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply<") # >< { sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<") # >test< { echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply<") # >test< { sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<") �����������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/printenv.c������������������������������������������������������������0000644�0000000�0000000�00000003110�12263563520�016717� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* printenv -- minimal clone of BSD printenv(1). usage: printenv [varname] Chet Ramey chet@po.cwru.edu */ /* Copyright (C) 1997-2002 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. Bash is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. Bash is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Bash; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #include <stdlib.h> #include <string.h> extern char **environ; int main (argc, argv) int argc; char **argv; { register char **envp, *eval; int len; argv++; argc--; /* printenv */ if (argc == 0) { for (envp = environ; *envp; envp++) puts (*envp); exit(EXIT_SUCCESS); } /* printenv varname */ len = strlen (*argv); for (envp = environ; *envp; envp++) { if (**argv == **envp && strncmp (*envp, *argv, len) == 0) { eval = *envp + len; /* If the environment variable doesn't have an `=', ignore it. */ if (*eval == '=') { puts (eval + 1); exit(EXIT_SUCCESS); } } } exit(EXIT_FAILURE); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-alias/������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�016557� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-alias/alias.tests�������������������������������������������������0000755�0000000�0000000�00000001105�12263563520�020732� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# place holder for future alias testing #ash# shopt -s expand_aliases # alias/unalias tests originally in builtins.tests unalias -a # this should return success, according to POSIX.2 alias echo alias: $? alias foo=bar unalias foo # this had better return success, according to POSIX.2 alias echo alias: $? # bug in all versions through bash-2.05b unalias qfoo qbar qbaz quux 2>/dev/null alias qfoo=qbar alias qbar=qbaz alias qbaz=quux alias quux=qfoo qfoo unalias qfoo qbar qbaz quux unalias -a alias foo='echo ' alias bar=baz alias baz=quux foo bar unalias foo bar baz �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-alias/alias.right�������������������������������������������������0000644�0000000�0000000�00000000077�12263563520�020711� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������alias: 0 alias: 0 ./alias.tests: line 25: qfoo: not found quux �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/run-all���������������������������������������������������������������0000755�0000000�0000000�00000003504�12263563520�016215� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh TOPDIR=$PWD test -x ash || { echo "No ./ash - creating a link to ../../busybox" ln -s ../../busybox ash } test -x printenv || gcc -O2 -o printenv printenv.c || exit $? test -x recho || gcc -O2 -o recho recho.c || exit $? test -x zecho || gcc -O2 -o zecho zecho.c || exit $? PATH="$PWD:$PATH" # for ash and recho/zecho/printenv export PATH THIS_SH="$PWD/ash" export THIS_SH do_test() { test -d "$1" || return 0 # echo do_test "$1" # $1 but with / replaced by # so that it can be used as filename part noslash=`echo "$1" | sed 's:/:#:g'` ( cd "$1" || { echo "cannot cd $1!"; exit 1; } for x in run-*; do test -f "$x" || continue case "$x" in "$0"|run-minimal|run-gprof) ;; *.orig|*~) ;; #*) echo $x ; sh $x ;; *) sh "$x" >"$TOPDIR/$noslash-$x.fail" 2>&1 && \ { echo "$1/$x: ok"; rm "$TOPDIR/$noslash-$x.fail"; } || echo "$1/$x: fail"; ;; esac done # Many bash run-XXX scripts just do this, # no point in duplication it all over the place for x in *.tests; do test -x "$x" || continue name="${x%%.tests}" test -f "$name.right" || continue { "$THIS_SH" "./$x" >"$name.xx" 2>&1 diff -u "$name.xx" "$name.right" >"$TOPDIR/$noslash-$x.fail" \ && rm -f "$name.xx" "$TOPDIR/$noslash-$x.fail" } && echo "$1/$x: ok" || echo "$1/$x: fail" done ) } # main part of this script # Usage: run-all [directories] if [ $# -lt 1 ]; then # All sub directories modules=`ls -d ash-*` # If you want to test ash against hush and msh testsuites # (have to copy hush_test and msh_test dirs to current dir first): #modules=`ls -d ash-* hush_test/hush-* msh_test/msh-*` for module in $modules; do do_test $module done else while [ $# -ge 1 ]; do if [ -d $1 ]; then do_test $1 fi shift done fi ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/zecho.c���������������������������������������������������������������0000644�0000000�0000000�00000002017�12263563520�016167� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* zecho - bare-bones echo */ /* Copyright (C) 1996-2002 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. Bash is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. Bash is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Bash; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { argv++; while (*argv) { (void)printf("%s", *argv); if (*++argv) putchar(' '); } putchar('\n'); exit(EXIT_SUCCESS); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�016573� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir2.tests������������������������������������������������0000755�0000000�0000000�00000000176�12263563520�021053� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# ash once couldn't redirect above fd#9 exec 1>/dev/null (echo LOST1 >&22) 22>&1 (echo LOST2 >&22) 22>&1 (echo OK >&22) 22>&2 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir.tests�������������������������������������������������0000755�0000000�0000000�00000000170�12263563520�020763� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# test: closed fds should stay closed exec 1>&- echo TEST >TEST echo JUNK # lost: stdout is closed cat TEST >&2 rm TEST ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir3.right������������������������������������������������0000644�0000000�0000000�00000000106�12263563520�021015� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������TEST ./redir3.tests: line 4: 9: Bad file descriptor Output to fd#9: 1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir4.right������������������������������������������������0000644�0000000�0000000�00000000003�12263563520�021012� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������OK �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir6.tests������������������������������������������������0000755�0000000�0000000�00000000107�12263563520�021051� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# we had a bug where this would hang (head -n 1 <redir6.right) echo OK ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir8.tests������������������������������������������������0000755�0000000�0000000�00000000655�12263563520�021063� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Chars above 0x7f are used as special codes. # 0x81 is CTLESC (see ash.c). # The bug was that quoting and unquoting of them # was out of sync for redirect filenames. # Subcase when redirect filename is specified in a variable. >unicode.sh echo -e 'v=uni\x81code' >>unicode.sh echo -e 'echo Ok >"$v"' >>unicode.sh echo -e 'cat uni\x81code' >>unicode.sh echo -e 'cat uni?code' >>unicode.sh . ./unicode.sh rm uni*code* echo Done �����������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir9.right������������������������������������������������0000644�0000000�0000000�00000000012�12263563520�021017� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Ok Done:0 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redirA.tests������������������������������������������������0000755�0000000�0000000�00000000264�12263563520�021070� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x="tmp11:tmp22" # Bug was incorrectly expanding variables in >redir echo "${x%:*}" >"${x%:*}" echo tmp1* rm tmp1* # Also try unquoted echo "${x%:*}" >${x%:*} echo tmp1* rm tmp1* ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redirA.right������������������������������������������������0000644�0000000�0000000�00000000014�12263563520�021031� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tmp11 tmp11 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir7.tests������������������������������������������������0000755�0000000�0000000�00000000521�12263563520�021052� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Chars above 0x7f are used as special codes. # 0x81 is CTLESC (see ash.c). # The bug was that quoting and unquoting of them # was out of sync for redirect filenames. >unicode.sh echo -e 'echo Ok >uni\x81code' >>unicode.sh echo -e 'cat uni\x81code' >>unicode.sh echo -e 'cat uni?code' >>unicode.sh . ./unicode.sh rm uni*code* echo Done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir7.right������������������������������������������������0000644�0000000�0000000�00000000013�12263563520�021016� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Ok Ok Done ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir3.tests������������������������������������������������0000755�0000000�0000000�00000000237�12263563520�021052� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# redirects to closed descriptors should not leave these descriptors # open afterwards echo TEST 9>/dev/null echo MUST ERROR OUT >&9 echo "Output to fd#9: $?" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir6.right������������������������������������������������0000644�0000000�0000000�00000000011�12263563520�021013� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Hello OK �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir9.tests������������������������������������������������0000755�0000000�0000000�00000000073�12263563520�021056� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������echo Ok >file.tmp cat 0<>file.tmp echo Done:$? rm file.tmp ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir.right�������������������������������������������������0000644�0000000�0000000�00000000053�12263563520�020733� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ash: write error: Bad file descriptor TEST �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir2.right������������������������������������������������0000644�0000000�0000000�00000000003�12263563520�021010� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������OK �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir5.right������������������������������������������������0000644�0000000�0000000�00000000063�12263563520�021021� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������./redir5.tests: line 2: 10: Bad file descriptor OK �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir5.tests������������������������������������������������0000755�0000000�0000000�00000000111�12263563520�021043� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# ash uses fd 10 (usually) for reading the script echo LOST >&10 echo OK �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir4.tests������������������������������������������������0000755�0000000�0000000�00000010311�12263563520�021045� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# ash uses fd 10 (usually) for reading the script exec 13>&- exec 12>&- exec 11>&- exec 10>&- # some amount of input is prefetched. # make sure final echo is far enough to not be prefetched. ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### ############################################################### echo "OK" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-redir/redir8.right������������������������������������������������0000644�0000000�0000000�00000000013�12263563520�021017� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Ok Ok Done ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365357�017126� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal1.tests���������������������������������������������0000755�0000000�0000000�00000001101�12263563520�021542� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������trap "echo got signal" USR1 for try in 1 2 3 4 5; do kill -USR1 $$ sleep 0.2 echo "sent $try signal" done & # Ensure "wait" has something to wait for sleep 2 & # Ensure we do not execute "trap" below before "kill -USR1" above # (was getting failure on loaded machine without this) sleep 0.1 sleeping=true while $sleeping; do trap if wait %%; then echo "sleep completed" sleeping=false elif [ $? == 127 ]; then echo "BUG: no processes to wait for?!" sleeping=false else echo "wait interrupted" fi done ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal7.right���������������������������������������������0000644�0000000�0000000�00000000020�12263563520�021517� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Bug detected: 0 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/sigint1.tests���������������������������������������������0000755�0000000�0000000�00000003176�12263563520�021600� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# What should happen if non-interactive shell gets SIGINT? (sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) & # We create a child which exits with 0 even on SIGINT # (The complex command is necessary only if SIGINT is generated by ^C, # in this testcase even bare "sleep 2" would do because # in the testcase we don't send SIGINT *to the child*...) $THIS_SH -c 'trap "exit 0" SIGINT; sleep 2' # In one second, we (main shell) get SIGINT here. # The question is whether we should, or should not, exit. # bash will not stop here. It will execute next command(s). # The rationale for this is described here: # http://www.cons.org/cracauer/sigint.html # # Basically, bash will not exit on SIGINT immediately if it waits # for a child. It will wait for the child to exit. # If child exits NOT by dying on SIGINT, then bash will not exit. # # The idea is that the following script: # | emacs file.txt # | more cmds # User may use ^C to interrupt editor's ops like search. But then # emacs exits normally. User expects that script doesn't stop. # # This is a nice idea, but detecting "did process really exit # with SIGINT?" is racy. Consider: # | bash -c 'while true; do /bin/true; done' # When ^C is pressed while bash waits for /bin/true to exit, # it may happen that /bin/true exits with exitcode 0 before # ^C is delivered to it as SIGINT. bash will see SIGINT, then # it will see that child exited with 0, and bash will NOT EXIT. # Therefore we do not implement bash behavior. # I'd say that emacs need to put itself into a separate pgrp # to isolate shell from getting stray SIGINTs from ^C. echo Next command after SIGINT was executed ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal4.right���������������������������������������������0000644�0000000�0000000�00000000122�12263563520�021517� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������./signal4.tests: trap: line 3: BADNAME: invalid signal specification 1 Trapped Ok ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal9.right���������������������������������������������0000644�0000000�0000000�00000000050�12263563520�021524� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Removing traps End of exit_func Done: 0 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal6.tests���������������������������������������������0000755�0000000�0000000�00000000116�12263563520�021554� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ trap "echo got TERM" TERM; sleep 3; }& sleep 1; kill $!; wait echo Done: $? ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal2.right���������������������������������������������0000644�0000000�0000000�00000000062�12263563520�021520� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������child sleeps child exits as expected parent exits ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal5.tests���������������������������������������������0000755�0000000�0000000�00000000405�12263563520�021554� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������trap "echo USR1 received" USR1 stub() { echo "Sleeping" sleep $1 echo "$1 sec passed, sending USR1 to parent" kill -USR1 $$ } stub 3 & stub 2 & sleep 1 until { echo "Waiting"; wait; } do echo "Wait exit code: $?" done echo "Wait returned 0" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal6.right���������������������������������������������0000644�0000000�0000000�00000000021�12263563520�021517� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������got TERM Done: 0 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal3.tests���������������������������������������������0000755�0000000�0000000�00000000334�12263563520�021553� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh $THIS_SH -c ' hup() { echo "child got HUP" } trap hup HUP echo "child sleeps" sleep 1 echo "child exits" ' & child=$! sleep 0.1 # let child install handler first kill -HUP $child wait echo "parent exits" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal7.tests���������������������������������������������0000755�0000000�0000000�00000000611�12263563520�021555� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������bug() { trap : exit # Bug was causing sh to be run in subshell, # as if this line is replaced with (sh -c ...; exit $?) & # here: sh -c 'echo REAL_CHILD=$$' & echo PARENTS_IDEA_OF_CHILD=$! wait # make sure bkgd shell completes } bug | { while read varval; do eval $varval done test x"$REAL_CHILD" != x"" \ && test x"$REAL_CHILD" = x"$PARENTS_IDEA_OF_CHILD" echo "Bug detected: $?" } �����������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal9.tests���������������������������������������������0000755�0000000�0000000�00000000642�12263563520�021563� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Note: the inner script is a test which checks for a different bug # (ordering between INT handler and exit on "set -e"), # but so far I did not figure out how to simulate it non-interactively. "$THIS_SH" -c ' exit_func() { echo "Removing traps" trap - EXIT TERM INT echo "End of exit_func" } set -e trap exit_func EXIT TERM INT sleep 2 exit 77 ' & child=$! sleep 1 kill -TERM $child wait echo Done: $? ����������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal4.tests���������������������������������������������0000755�0000000�0000000�00000000105�12263563520�021550� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh trap "echo Trapped" BADNAME TERM; echo $? kill $$ echo Ok �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/reap1.tests�����������������������������������������������0000755�0000000�0000000�00000000377�12263563520�021232� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Must not find us alive { sleep 2; kill -9 $$; } 2>/dev/null & sleep 1 & PID=$! # We must exit the loop in one second. # We had bug 5304: builtins never waited for exited children while kill -0 $PID >/dev/null 2>&1; do true done echo Ok �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal3.right���������������������������������������������0000644�0000000�0000000�00000000064�12263563520�021523� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������child sleeps child got HUP child exits parent exits ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal5.right���������������������������������������������0000644�0000000�0000000�00000000310�12263563520�021517� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Sleeping Sleeping Waiting 2 sec passed, sending USR1 to parent USR1 received Wait exit code: 138 Waiting 3 sec passed, sending USR1 to parent USR1 received Wait exit code: 138 Waiting Wait returned 0 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/savetrap.tests��������������������������������������������0000755�0000000�0000000�00000000177�12263563520�022045� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������trap 'echo Exiting' EXIT trap 'echo WINCH!' SIGWINCH v=` trap ` echo "$v" v=$( trap ) echo "$v" v=`trap` echo "$v" echo Done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal1.right���������������������������������������������0000644�0000000�0000000�00000000554�12263563520�021525� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������got signal trap -- 'echo got signal' USR1 sent 1 signal got signal wait interrupted trap -- 'echo got signal' USR1 sent 2 signal got signal wait interrupted trap -- 'echo got signal' USR1 sent 3 signal got signal wait interrupted trap -- 'echo got signal' USR1 sent 4 signal got signal wait interrupted trap -- 'echo got signal' USR1 sent 5 signal sleep completed ����������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal8.tests���������������������������������������������0000755�0000000�0000000�00000000513�12263563520�021557� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������"$THIS_SH" -c ' exit_func() { echo "Removing traps" trap - EXIT TERM INT echo "End of exit_func" } set -e trap exit_func EXIT TERM INT sleep 2 exit 77 ' & sleep 1 # BUG: ash kills -PGRP, but in non-interactive shell we do not create pgrps! # In this case, bash kills by PID, not PGRP. kill -TERM %1 wait echo Done: $? �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal2.tests���������������������������������������������0000755�0000000�0000000�00000000400�12263563520�021544� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh $THIS_SH -c ' cleanup() { echo "child exits as expected" exit } trap cleanup HUP echo "child sleeps" sleep 1 echo "BAD exit from child!" ' & child=$! sleep 0.1 # let child install handler first kill -HUP $child wait echo "parent exits" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/reap1.right�����������������������������������������������0000644�0000000�0000000�00000000003�12263563520�021164� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Ok �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/savetrap.right��������������������������������������������0000644�0000000�0000000�00000000265�12263563520�022013� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������trap -- 'echo Exiting' EXIT trap -- 'echo WINCH!' WINCH trap -- 'echo Exiting' EXIT trap -- 'echo WINCH!' WINCH trap -- 'echo Exiting' EXIT trap -- 'echo WINCH!' WINCH Done Exiting �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/sigint1.right���������������������������������������������0000644�0000000�0000000�00000000041�12263563520�021534� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Sending SIGINT to main shell PID �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-signals/signal8.right���������������������������������������������0000644�0000000�0000000�00000000050�12263563520�021523� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Removing traps End of exit_func Done: 0 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/recho.c���������������������������������������������������������������0000644�0000000�0000000�00000002571�12263563520�016164� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* recho -- really echo args, bracketed with <> and with invisible chars made visible. Chet Ramey chet@po.cwru.edu */ /* Copyright (C) 2002-2005 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. Bash is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. Bash is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Bash; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #include <stdio.h> #include <stdlib.h> void strprint(); int main(int argc, char **argv) { int i; for (i = 1; i < argc; i++) { printf("argv[%d] = <", i); strprint(argv[i]); printf(">\n"); } exit(EXIT_SUCCESS); } void strprint(char *str) { unsigned char *s; for (s = (unsigned char *)str; s && *s; s++) { if (*s < ' ') { putchar('^'); putchar(*s+64); } else if (*s == 127) { putchar('^'); putchar('?'); } else putchar(*s); } } ���������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/�������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365356�016440� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var_leak.tests�����������������������������������������������0000755�0000000�0000000�00000001171�12263563520�021312� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# cat is an external program, variable should not leak out of it. # this currently fails with CONFIG_FEATURE_SH_NOFORK=y VAR='' VAR=val0 cat /dev/null echo "should be empty: '$VAR'" # true is a regular builtin, variable should not leak out of it. VAR='' VAR=val1 true echo "should be empty: '$VAR'" # ash follows the "special builtin leaks variables" rule here: # exec is a special builtin. (bash does not do it) VAR='' VAR=val2 exec 2>&1 echo "should be not empty: '$VAR'" # ash follows the "function call is a special builtin" rule here # (bash does not do it) f() { true; } VAR='' VAR=val3 f echo "should be not empty: '$VAR'" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var2.right���������������������������������������������������0000644�0000000�0000000�00000000014�12263563520�020343� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������bus/usb/1/2 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var_bash3.right����������������������������������������������0000644�0000000�0000000�00000000254�12263563520�021347� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 a041#c 2 a041#c 3 a\041#c 4 a\041#c 5 a\041#c 6 a\041#c 7 a\041#c 8 a\041#c 9 a\041#c 10 a\c 11 a\c 12 a\c 13 a\\c 14 a\\c 15 a\\c 16 a\tc 17 a\tc 18 a\tc 19 atc 20 a\tc ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var_bash5.right����������������������������������������������0000644�0000000�0000000�00000000025�12263563520�021345� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a/ a/d a/e/f Done: 0 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var_posix1.right���������������������������������������������0000644�0000000�0000000�00000000154�12263563520�021571� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcdcd abcdcd abcdcd cdcd babcdcd babcdcd ababcdcd ababcd ababcd ababcd abab ababcdc ababcdc ababcdcd end ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var1.tests���������������������������������������������������0000755�0000000�0000000�00000000344�12263563520�020400� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# check that first assignment has proper effect on second one ( a=a A=$a echo a=$a A=$A ) (a=a A=$a; echo a=$a A=$A) (a=a A=$a echo a=$a A=$A) (a=a A=$a /bin/echo a=$a A=$A) f() { echo a=$a A=$A; } (a=a A=$a f) (a=a A=$a; f) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var1.right���������������������������������������������������0000644�0000000�0000000�00000000054�12263563520�020346� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a=a A=a a=a A=a a= A= a= A= a=a A=a a=a A=a ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var_bash5.tests����������������������������������������������0000755�0000000�0000000�00000000267�12263563520�021405� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This testcase checks whether slashes in ${v/a/b} are parsed before # or after expansions v='a/b/c' s='b/c' r='e/f' echo "${v/$s}" echo "${v/$s/d}" echo "${v/$s/$r}" echo Done: $? �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var_bash4.right����������������������������������������������0000644�0000000�0000000�00000001224�12263563520�021346� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Source: a*b\*c Replace str: _\\_\z_ Pattern: single backslash and star: "replace literal star" In assignment: a_\_z_b\*c Unquoted: a_\_z_b\*c Quoted: a_\_\z_b\*c Pattern: double backslash and star: "replace backslash and everything after it" In assignment: a*b_\_z_ Unquoted: a*b_\_z_ Quoted: a*b_\_\z_ Source: a\bc Replace str: _\\_\z_ Pattern: single backslash and b: "replace b" In assignment: a\_\_z_c Unquoted: a\_\_z_c Quoted: a\_\_\z_c Pattern: double backslash and b: "replace backslash and b" In assignment: a_\_z_c Unquoted: a_\_z_c Quoted: a_\_\z_c Done: 0 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var_bash2.tests����������������������������������������������0000755�0000000�0000000�00000000771�12263563520�021402� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������var=abc123dcba123 echo ${var/d/x} echo ${var/c/x} echo ${var//c/x} echo ${var/[123]/x} echo ${var//[123]/x} echo ${var/c*/x} echo ${var/*c/x} # must match longest match: result is "abx23" echo ${var/c*1/x} # empty replacement - 2nd slash can be omitted echo ${var/[123]} echo ${var//[123]} ### ash doesn't support ### # match only at the beginning: ### echo ${var/#a/x} ### echo ${var/#b/x} # should not match ### echo ${var//#b/x} # should not match ### # match only at the end: ### echo ${var/%3/x} �������busybox-1.22.1/shell/ash_test/ash-vars/var_bash3.tests����������������������������������������������0000755�0000000�0000000�00000001015�12263563520�021373� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a='abc' r=${a//b/\041#} echo 1 $r echo 2 ${a//b/\041#} echo 3 "${a//b/\041#}" a='abc' r=${a//b/\\041#} echo 4 $r echo 5 ${a//b/\\041#} echo 6 "${a//b/\\041#}" a='abc' b='\041#' r=${a//b/$b} echo 7 $r echo 8 ${a//b/$b} echo 9 "${a//b/$b}" a='abc' b='\' r="${a//b/$b}" echo 10 $r echo 11 ${a//b/$b} echo 12 "${a//b/$b}" a='abc' b='\\' r="${a//b/$b}" echo 13 $r echo 14 ${a//b/$b} echo 15 "${a//b/$b}" a='abc' b='\t' r="${a//b/$b}" echo 16 $r echo 17 ${a//b/$b} echo 18 "${a//b/$b}" echo 19 ${a//b/\t} echo 20 "${a//b/\t}" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var_bash2.right����������������������������������������������0000644�0000000�0000000�00000000154�12263563520�021345� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abc123xcba123 abx123dcba123 abx123dxba123 abcx23dcba123 abcxxxdcbaxxx abx xba123 abx23 abc23dcba123 abcdcba ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var_posix1.tests���������������������������������������������0000755�0000000�0000000�00000000415�12263563520�021621� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������var=ababcdcd echo ${var#ab} echo ${var##ab} echo ${var#a*b} echo ${var##a*b} echo ${var#?} echo ${var##?} echo ${var#*} echo ${var##*} echo ${var%cd} echo ${var%%cd} echo ${var%c*d} echo ${var%%c*d} echo ${var%?} echo ${var%%?} echo ${var%*} echo ${var%%*} echo end ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var2.tests���������������������������������������������������0000755�0000000�0000000�00000000103�12263563520�020372� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������X=usbdev1.2 X=${X#usbdev} B=${X%%.*} D=${X#*.}; echo bus/usb/$B/$D �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var_leak.right�����������������������������������������������0000644�0000000�0000000�00000000140�12263563520�021255� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������should be empty: '' should be empty: '' should be not empty: 'val2' should be not empty: 'val3' ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var_bash1.right����������������������������������������������0000644�0000000�0000000�00000000111�12263563520�021335� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� f bcdef abcdef abcdef bcde abcd abcd abcdef bcdef abcdef abcdef abcdef �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var_bash1.tests����������������������������������������������0000755�0000000�0000000�00000000343�12263563520�021374� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������var=abcdef echo ${var:7} echo ${var:6} echo ${var:5} echo ${var:1} echo ${var:0} echo ${var:-1} echo ${var:1:4} echo ${var:0:4} echo ${var::4} echo ${var:-1:4} echo ${var:1:7} echo ${var:0:7} echo ${var::7} echo ${var:-1:7} ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-vars/var_bash4.tests����������������������������������������������0000755�0000000�0000000�00000002702�12263563520�021400� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This testcase demonstrates that backslashes are treated differently # in 1st and 2nd parts of ${var/search/repl}: # if quoted ("${var/search/repl}"), and repl contains \a (a non-special char), # the backslash in repl stays; if unquoted, backslash is removed. # But search part does not act like that: \a is always converted to just a, # even in quotes. # # bash4 (and probably bash3 too): "Quoted:" results are different from # unquoted and assignment expansions - they have a backslash before z. v='a*b\*c' echo 'Source: ' "$v" echo 'Replace str: ' '_\\_\z_' echo 'Pattern: ' 'single backslash and star: "replace literal star"' r=${v/\*/_\\_\z_} echo 'In assignment:' "$r" echo 'Unquoted: ' ${v/\*/_\\_\z_} echo 'Quoted: ' "${v/\*/_\\_\z_}" echo 'Pattern: ' 'double backslash and star: "replace backslash and everything after it"' r=${v/\\*/_\\_\z_} echo 'In assignment:' "$r" echo 'Unquoted: ' ${v/\\*/_\\_\z_} echo 'Quoted: ' "${v/\\*/_\\_\z_}" echo v='a\bc' echo 'Source: ' "$v" echo 'Replace str: ' '_\\_\z_' echo 'Pattern: ' 'single backslash and b: "replace b"' r=${v/\b/_\\_\z_} echo 'In assignment:' "$r" echo 'Unquoted: ' ${v/\b/_\\_\z_} echo 'Quoted: ' "${v/\b/_\\_\z_}" echo 'Pattern: ' 'double backslash and b: "replace backslash and b"' r=${v/\\b/_\\_\z_} echo 'In assignment:' "$r" echo 'Unquoted: ' ${v/\\b/_\\_\z_} echo 'Quoted: ' "${v/\\b/_\\_\z_}" echo echo Done: $? ��������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-heredoc/����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365356�017076� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-heredoc/heredoc.right���������������������������������������������0000644�0000000�0000000�00000000227�12263563520�021546� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������there one - alpha two - beta three - gamma hi\ there$a stuff hi\ there EO\ F hi tab 1 tab 2 tab 3 abc def ghi jkl mno fff is a shell function hi there �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-heredoc/heredoc.tests���������������������������������������������0000755�0000000�0000000�00000002347�12263563520�021603� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# check order and content of multiple here docs cat << EOF1 << EOF2 hi EOF1 there EOF2 while read line1; do read line2 <&3 echo $line1 - $line2 done <<EOF1 3<<EOF2 one two three EOF1 alpha beta gamma EOF2 # check quoted here-doc is protected a=foo cat << 'EOF' hi\ there$a stuff EOF # check that quoted here-documents don't have \newline processing done cat << 'EOF' hi\ there EO\ F EOF true # check that \newline is removed at start of here-doc cat << EO\ F hi EOF #ash# # check that \newline removal works for here-doc delimiter #ash# cat << EOF #ash# hi #ash# EO\ #ash# F # check operation of tab removal in here documents cat <<- EOF tab 1 tab 2 tab 3 EOF # check appending of text to file from here document rm -f /tmp/bash-zzz cat > /tmp/bash-zzz << EOF abc EOF cat >> /tmp/bash-zzz << EOF def ghi jkl mno EOF cat /tmp/bash-zzz rm -f /tmp/bash-zzz # make sure command printing puts the here-document as the last redirection # on the line, and the function export code preserves syntactic correctness fff() { ed /tmp/foo <<ENDOFINPUT >/dev/null /^name/d w q ENDOFINPUT aa=1 } type fff #ash# export -f fff #ash# ${THIS_SH} -c 'type fff' # check that end of file delimits a here-document # THIS MUST BE LAST! cat << EOF hi there �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-invert/�����������������������������������������������������������0000755�0000000�0000000�00000000000�12320365356�016774� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-invert/invert.right�����������������������������������������������0000644�0000000�0000000�00000000024�12263563520�021335� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 1 1 0 0 1 0 1 0 1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-invert/invert.tests�����������������������������������������������0000755�0000000�0000000�00000000641�12263563520�021372� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# tests of return value inversion # placeholder for future expansion # user subshells (...) did this wrong in bash versions before 2.04 ! ( echo hello | grep h >/dev/null 2>&1 ); echo $? ! echo hello | grep h >/dev/null 2>&1 ; echo $? ! true ; echo $? ! false; echo $? ! (false) ; echo $? ! (true); echo $? ! true | false ; echo $? ! false | true ; echo $? ! (true | false) ; echo $? ! (false | true) ; echo $? �����������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-arith/������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365356�016574� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-arith/arith-for.right���������������������������������������������0000644�0000000�0000000�00000001607�12263563520�021531� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0 1 2 0 1 2 0 1 2 0 2 4 fx is a function fx () { i=0; for ((1; i < 3; i++ )) do echo $i; done; for ((i=0; 1; i++ )) do if (( i >= 3 )); then break; fi; echo $i; done; for ((i=0; i<3; 1)) do echo $i; (( i++ )); done; i=0; for ((1; 1; 1)) do if (( i > 2 )); then break; fi; echo $i; (( i++ )); done; i=0; for ((1; 1; 1)) do if (( i > 2 )); then break; fi; echo $i; (( i++ )); done } 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 ./arith-for.tests: line 77: syntax error: arithmetic expression required ./arith-for.tests: line 77: syntax error: `(( i=0; "i < 3" ))' 2 ./arith-for.tests: line 83: syntax error: `;' unexpected ./arith-for.tests: line 83: syntax error: `(( i=0; i < 3; i++; 7 ))' 2 20 20 �������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-arith/arith-bash1.tests�������������������������������������������0000755�0000000�0000000�00000000116�12263563520�021763� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# checks for [[ ]] # && and || [[ a && "" ]]; echo $? [[ a || "" ]]; echo $? ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-arith/arith.right�������������������������������������������������0000644�0000000�0000000�00000003733�12263563520�020747� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Format: 'expected actual' 163 163 4 4 16 16 8 8 2 2 4 4 2 2 2 2 1 1 0 0 0 0 0 0 1 1 1 1 2 2 -3 -3 -2 -2 1 1 0 0 2 2 131072 131072 29 29 33 33 49 49 1 1 1 1 0 0 0 0 1 1 1 1 1 1 2 2 3 3 1 1 58 58 2 2 60 60 1 1 256 256 16 16 62 62 4 4 29 29 5 5 -4 -4 4 4 1 1 32 32 32 32 1 1 1 1 32 32 20 20 30 30 20 20 30 30 ./arith.tests: line 117: arithmetic syntax error 6 6 6,5,3 6,5,3 263 263 255 255 40 40 ./arith.tests: line 163: arithmetic syntax error ./arith.tests: line 165: divide by zero ./arith.tests: let: line 166: arithmetic syntax error ./arith.tests: line 167: arithmetic syntax error ./arith.tests: let: line 168: arithmetic syntax error abc def ghi ./arith.tests: line 191: arithmetic syntax error 16 16 ./arith.tests: line 196: arithmetic syntax error ./arith.tests: line 197: malformed ?: operator ./arith.tests: line 198: arithmetic syntax error 9 9 ./arith.tests: line 205: arithmetic syntax error ./arith.tests: line 208: arithmetic syntax error 9 9 9 9 9 9 7 7 7 4 4 32767 32767 32768 32768 131072 131072 2147483647 2147483647 1 1 4 4 4 4 5 5 5 5 4 4 3 3 3 3 4 4 4 4 ./arith.tests: line 257: arithmetic syntax error ./arith.tests: line 259: arithmetic syntax error ./arith.tests: line 260: arithmetic syntax error ./arith.tests: line 262: arithmetic syntax error ./arith.tests: line 263: arithmetic syntax error 4 4 7 7 -7 -7 ./arith1.sub: line 2: arithmetic syntax error ./arith1.sub: line 3: arithmetic syntax error ./arith1.sub: line 4: arithmetic syntax error ./arith1.sub: line 5: arithmetic syntax error 6 6 3 3 7 7 4 4 0 0 3 3 7 7 2 2 -2 -2 1 1 ./arith1.sub: line 37: arithmetic syntax error ./arith2.sub: line 2: arithmetic syntax error ./arith2.sub: line 3: arithmetic syntax error ./arith2.sub: line 4: arithmetic syntax error ./arith2.sub: line 5: arithmetic syntax error 5 5 1 1 4 4 0 0 ./arith2.sub: line 42: arithmetic syntax error ./arith2.sub: line 47: arithmetic syntax error 8 12 ./arith.tests: line 290: arithmetic syntax error 42 42 42 ./arith.tests: line 302: a[b[c]d]=e: not found �������������������������������������busybox-1.22.1/shell/ash_test/ash-arith/arith-for.testsx��������������������������������������������0000755�0000000�0000000�00000001537�12263563520�021753� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fx() { i=0 for (( ; i < 3; i++ )) do echo $i done for (( i=0; ; i++ )) do if (( i >= 3 )); then break; fi echo $i done for (( i=0; i<3; )) do echo $i (( i++ )) done i=0 for (( ; ; )) do if (( i > 2 )); then break; fi echo $i; (( i++ )) done i=0 for ((;;)) do if (( i > 2 )); then break; fi echo $i; (( i++ )) done } for (( i=0; "i < 3" ; i++ )) do echo $i done i=0 for (( ; "i < 3"; i++ )) do echo $i done for (( i=0; ; i++ )) do if (( i >= 3 )); then break; fi echo $i done for ((i = 0; ;i++ )) do echo $i if (( i < 3 )); then (( i++ )) continue; fi break done type fx fx # errors for (( i=0; "i < 3" )) do echo $i done echo $? for (( i=0; i < 3; i++; 7 )) do echo $i done echo $? # one-liners added in post-bash-2.04 for ((i=0; i < 20; i++)) do : ; done echo $i for ((i=0; i < 20; i++)) { : ; } echo $i �����������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-arith/arith1.sub��������������������������������������������������0000755�0000000�0000000�00000001152�12263563520�020500� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# test of redone post-increment and post-decrement code ( echo $(( 4-- )) ) ( echo $(( 4++ )) ) ( echo $(( 4 -- )) ) ( echo $(( 4 ++ )) ) #ash# (( array[0]++ )) #ash# echo ${array} #ash# (( array[0] ++ )) #ash# echo ${array} #ash# (( a++ )) #ash# echo $a #ash# (( a ++ )) #ash# echo $a a=2 echo 6 $(( a ++ + 4 )) echo 3 $a echo 7 $(( a+++4 )) echo 4 $a echo 0 $(( a---4 )) echo 3 $a echo 7 $(( a -- + 4 )) echo 2 $a echo -2 $(( a -- - 4 )) echo 1 $a #ash# (( ++ + 7 )) #ash# (( ++ )) ( echo $(( +++7 )) ) # bash 3.2 apparently thinks that ++ +7 is 7 #ash# echo $(( ++ + 7 )) #ash# (( -- )) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-arith/README.ash��������������������������������������������������0000644�0000000�0000000�00000000060�12263563520�020221� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������there is no support for (( )) constructs in ash ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-arith/arith2.sub��������������������������������������������������0000755�0000000�0000000�00000001701�12263563520�020501� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# bash 3.2 apparently thinks that ++7 is 7 etc ( echo $(( --7 )) ) ( echo $(( ++7 )) ) ( echo $(( -- 7 )) ) ( echo $(( ++ 7 )) ) #ash# ((++array[0] )) #ash# echo 1 $array #ash# (( ++ array[0] )) #ash# echo 2 $array #ash# (( ++a )) #ash# echo 1 $a #ash# (( ++ a )) #ash# echo 2 $a #ash# (( --a )) #ash# echo 1 $a #ash# (( -- a )) #ash# echo 0 $a a=0 echo 5 $(( 4 + ++a )) echo 1 $a # ash doesn't handle it right... #ash# echo 6 $(( 4+++a )) #ash# echo 2 $a a=2 # ash doesn't handle it right... #ash# echo 3 $(( 4---a )) #ash# echo 1 $a a=1 echo 4 $(( 4 - -- a )) echo 0 $a #ash# (( -- )) # bash 3.2 apparently thinks that ---7 is -7 #ash# echo $(( ---7 )) ( echo $(( -- - 7 )) ) #ash# (( ++ )) # bash 3.2: 7 #ash# echo 7 $(( ++7 )) ( echo $(( ++ + 7 )) ) # bash 3.2: -7 #ash# echo -7 $(( ++-7 )) # bash 3.2: -7 #ash# echo -7 $(( ++ - 7 )) # bash 3.2: 7 #ash# echo 7 $(( +--7 )) # bash 3.2: 7 #ash# echo 7 $(( -- + 7 )) ���������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-arith/arith.tests�������������������������������������������������0000755�0000000�0000000�00000012570�12263563520�020776� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ash# set +o posix #ash# declare -i iv jv echo "Format: 'expected actual'" iv=$(( 3 + 5 * 32 )) echo 163 $iv #ash# iv=iv+3 #ash# echo 166 $iv iv=2 jv=iv let "jv *= 2" echo 4 $jv jv=$(( $jv << 2 )) echo 16 $jv let jv="$jv / 2" echo 8 $jv #ash# jv="jv >> 2" let jv="jv >> 2" echo 2 $jv iv=$((iv+ $jv)) echo 4 $iv echo 2 $((iv -= jv)) echo 2 $iv echo 1 $(( iv == jv )) echo 0 $(( iv != $jv )) echo 0 $(( iv < jv )) echo 0 $(( $iv > $jv )) echo 1 $(( iv <= $jv )) echo 1 $(( $iv >= jv )) echo 2 $jv echo -3 $(( ~$jv )) echo -2 $(( ~1 )) echo 1 $(( ! 0 )) echo 0 $(( jv % 2 )) echo 2 $(( $iv % 4 )) echo 131072 $(( iv <<= 16 )) echo 29 $(( iv %= 33 )) echo 33 $(( 33 & 55 )) echo 49 $(( 33 | 17 )) echo 1 $(( iv && $jv )) echo 1 $(( $iv || jv )) echo 0 $(( iv && 0 )) echo 0 $(( iv & 0 )) echo 1 $(( iv && 1 )) echo 1 $(( iv & 1 )) echo 1 $(( $jv || 0 )) echo 2 $(( jv | 0 )) echo 3 $(( jv | 1 )) echo 1 $(( $jv || 1 )) let 'iv *= jv' echo 58 $iv echo 2 $jv let "jv += $iv" echo 60 $jv echo 1 $(( jv /= iv )) echo 256 $(( jv <<= 8 )) echo 16 $(( jv >>= 4 )) echo 62 $(( iv |= 4 )) echo 4 $(( iv &= 4 )) echo 29 $(( iv += (jv + 9))) echo 5 $(( (iv + 4) % 7 )) # unary plus, minus echo -4 $(( +4 - 8 )) echo 4 $(( -4 + 8 )) # conditional expressions echo 1 $(( 4<5 ? 1 : 32)) echo 32 $(( 4>5 ? 1 : 32)) echo 32 $(( 4>(2+3) ? 1 : 32)) echo 1 $(( 4<(2+3) ? 1 : 32)) echo 1 $(( (2+2)<(2+3) ? 1 : 32)) echo 32 $(( (2+2)>(2+3) ? 1 : 32)) # check that the unevaluated part of the ternary operator does not do # evaluation or assignment x=i+=2 y=j+=2 #ash# declare -i i=1 j=1 i=1 j=1 echo 20 $((1 ? 20 : (x+=2))) #ash# echo $i,$x # ash mishandles this echo 30 $((0 ? (y+=2) : 30)) #ash# echo $j,$y # ash mishandles this x=i+=2 y=j+=2 #ash# declare -i i=1 j=1 i=1 j=1 echo 20 $((1 ? 20 : (x+=2))) #ash# echo $i,$x # ash mishandles this echo 30 $((0 ? (y+=2) : 30)) #ash# echo $i,$y # ash mishandles this # check precedence of assignment vs. conditional operator # should be an error #ash# declare -i x=2 x=2 #ashnote# bash reports error but continues, ash aborts - using subshell to 'emulate' bash: ( y=$((1 ? 20 : x+=2)) ) # check precedence of assignment vs. conditional operator #ash# declare -i x=2 x=2 # ash says "line NNN: syntax error: 0 ? x+=2 : 20" #ash# echo 20 $((0 ? x+=2 : 20)) # associativity of assignment-operator operator #ash# declare -i i=1 j=2 k=3 i=1 j=2 k=3 echo 6 $((i += j += k)) echo 6,5,3 $i,$j,$k # octal, hex echo 263 $(( 0x100 | 007 )) echo 255 $(( 0xff )) #ash# echo 255 $(( 16#ff )) #ash# echo 127 $(( 16#FF/2 )) #ash# echo 36 $(( 8#44 )) echo 40 $(( 8 ^ 32 )) #ash# # other bases #ash# echo 10 $(( 16#a )) #ash# echo 10 $(( 32#a )) #ash# echo 10 $(( 56#a )) #ash# echo 10 $(( 64#a )) #ash# #ash# echo 10 $(( 16#A )) #ash# echo 10 $(( 32#A )) #ash# echo 36 $(( 56#A )) #ash# echo 36 $(( 64#A )) #ash# #ash# echo 62 $(( 64#@ )) #ash# echo 63 $(( 64#_ )) #ash# # weird bases (error) #ash# echo $(( 3425#56 )) #ash# # missing number after base #ash# echo 0 $(( 2# )) # these should generate errors ( echo $(( 7 = 43 )) ) #ash# echo $(( 2#44 )) ( echo $(( 44 / 0 )) ) ( let 'jv += $iv' ) ( echo $(( jv += \$iv )) ) ( let 'rv = 7 + (43 * 6' ) #ash# # more errors #ash# declare -i i #ash# i=0#4 #ash# i=2#110#11 ((echo abc; echo def;); echo ghi) #ash# if (((4+4) + (4 + 7))); then #ash# echo ok #ash# fi #ash# (()) # make sure the null expression works OK #ash# a=(0 2 4 6) #ash# echo 6 $(( a[1] + a[2] )) #ash# echo 1 $(( (a[1] + a[2]) == a[3] )) #ash# (( (a[1] + a[2]) == a[3] )) ; echo 0 $? # test pushing and popping the expression stack unset A A="4 + " ( echo A $(( ( 4 + A ) + 4 )) ) A="3 + 5" echo 16 $(( ( 4 + A ) + 4 )) # badly-formed conditional expressions ( echo $(( 4 ? : $A )) ) ( echo $(( 1 ? 20 )) ) ( echo $(( 4 ? 20 : )) ) # precedence and short-circuit evaluation B=9 echo 9 $B # error ( echo $(( 0 && B=42 )); echo 9 $B ) # error ( echo $(( 1 || B=88 )); echo 9 $B ) # ash mistakenly evaluates B=... below #ash# echo 0 $(( 0 && (B=42) )) echo 9 $B #ash# echo 0 $(( (${$} - $$) && (B=42) )) echo 9 $B #ash# echo 1 $(( 1 || (B=88) )) echo 9 $B # until command with (( )) command x=7 echo 7 $x #ash# until (( x == 4 )) until test "$x" = 4 do echo $x x=4 done echo 4 $x # exponentiation echo 32767 $(( 2**15 - 1)) echo 32768 $(( 2**(16-1))) echo 131072 $(( 2**16*2 )) echo 2147483647 $(( 2**31-1)) echo 1 $(( 2**0 )) # {pre,post}-{inc,dec}rement and associated errors x=4 echo 4 $x echo 4 $(( x++ )) echo 5 $x echo 5 $(( x-- )) echo 4 $x echo 3 $(( --x )) echo 3 $x echo 4 $(( ++x )) echo 4 $x # bash 3.2 apparently thinks that ++7 is 7 #ash# echo 7 $(( ++7 )) ( echo $(( 7-- )) ) ( echo $(( --x=7 )) ) ( echo $(( ++x=7 )) ) ( echo $(( x++=7 )) ) ( echo $(( x--=7 )) ) echo 4 $x echo 7 $(( +7 )) echo -7 $(( -7 )) # bash 3.2 apparently thinks that ++7 is 7 #ash# echo $(( ++7 )) #ash# echo $(( --7 )) ${THIS_SH} ./arith1.sub ${THIS_SH} ./arith2.sub x=4 y=7 #ash# (( x=8 , y=12 )) x=8 y=12 echo $x $y #ash# # should be an error #ash# (( x=9 y=41 )) # These are errors unset b ( echo $((a b)) ) #ash# ((a b)) n=42 printf "%d\n" $n printf "%i\n" $n #ash# echo $(( 8#$(printf "%o\n" $n) )) printf "%u\n" $n #ash# echo $(( 16#$(printf "%x\n" $n) )) #ash# echo $(( 16#$(printf "%X\n" $n) )) # causes longjmp botches through bash-2.05b a[b[c]d]=e ����������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/ash_test/ash-arith/arith-bash1.right�������������������������������������������0000644�0000000�0000000�00000000004�12263563520�021727� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 0 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/math.c�������������������������������������������������������������������������0000644�0000000�0000000�00000055252�12263563520�014207� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Arithmetic code ripped out of ash shell for code sharing. * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * Original BSD copyright notice is retained at the end of this file. * * Copyright (c) 1989, 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au> * was re-ported from NetBSD and debianized. * * rewrite arith.y to micro stack based cryptic algorithm by * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com> * * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support * dynamic variables. * * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be * used in busybox and size optimizations, * rewrote arith (see notes to this), added locale support, * rewrote dynamic variables. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com> * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* This is my infix parser/evaluator. It is optimized for size, intended * as a replacement for yacc-based parsers. However, it may well be faster * than a comparable parser written in yacc. The supported operators are * listed in #defines below. Parens, order of operations, and error handling * are supported. This code is thread safe. The exact expression format should * be that which POSIX specifies for shells. * * The code uses a simple two-stack algorithm. See * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html * for a detailed explanation of the infix-to-postfix algorithm on which * this is based (this code differs in that it applies operators immediately * to the stack instead of adding them to a queue to end up with an * expression). */ /* * Aug 24, 2001 Manuel Novoa III * * Reduced the generated code size by about 30% (i386) and fixed several bugs. * * 1) In arith_apply(): * a) Cached values of *numptr and &(numptr[-1]). * b) Removed redundant test for zero denominator. * * 2) In arith(): * a) Eliminated redundant code for processing operator tokens by moving * to a table-based implementation. Also folded handling of parens * into the table. * b) Combined all 3 loops which called arith_apply to reduce generated * code size at the cost of speed. * * 3) The following expressions were treated as valid by the original code: * 1() , 0! , 1 ( *3 ) . * These bugs have been fixed by internally enclosing the expression in * parens and then checking that all binary ops and right parens are * preceded by a valid expression (NUM_TOKEN). * * Note: It may be desirable to replace Aaron's test for whitespace with * ctype's isspace() if it is used by another busybox applet or if additional * whitespace chars should be considered. Look below the "#include"s for a * precompiler test. */ /* * Aug 26, 2001 Manuel Novoa III * * Return 0 for null expressions. Pointed out by Vladimir Oleynik. * * Merge in Aaron's comments previously posted to the busybox list, * modified slightly to take account of my changes to the code. * */ /* * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru> * * - allow access to variable, * use recursive value indirection: c="2*2"; a="c"; echo $((a+=2)) produce 6 * - implement assign syntax (VAR=expr, +=, *= etc) * - implement exponentiation (** operator) * - implement comma separated - expr, expr * - implement ++expr --expr expr++ expr-- * - implement expr ? expr : expr (but second expr is always calculated) * - allow hexadecimal and octal numbers * - restore lost XOR operator * - protect $((num num)) as true zero expr (Manuel's error) * - always use special isspace(), see comment from bash ;-) */ #include "libbb.h" #include "math.h" #define lookupvar (math_state->lookupvar) #define setvar (math_state->setvar ) //#define endofname (math_state->endofname) typedef unsigned char operator; /* An operator's token id is a bit of a bitfield. The lower 5 bits are the * precedence, and 3 high bits are an ID unique across operators of that * precedence. The ID portion is so that multiple operators can have the * same precedence, ensuring that the leftmost one is evaluated first. * Consider * and / */ #define tok_decl(prec,id) (((id)<<5) | (prec)) #define PREC(op) ((op) & 0x1F) #define TOK_LPAREN tok_decl(0,0) #define TOK_COMMA tok_decl(1,0) /* All assignments are right associative and have the same precedence, * but there are 11 of them, which doesn't fit into 3 bits for unique id. * Abusing another precedence level: */ #define TOK_ASSIGN tok_decl(2,0) #define TOK_AND_ASSIGN tok_decl(2,1) #define TOK_OR_ASSIGN tok_decl(2,2) #define TOK_XOR_ASSIGN tok_decl(2,3) #define TOK_PLUS_ASSIGN tok_decl(2,4) #define TOK_MINUS_ASSIGN tok_decl(2,5) #define TOK_LSHIFT_ASSIGN tok_decl(2,6) #define TOK_RSHIFT_ASSIGN tok_decl(2,7) #define TOK_MUL_ASSIGN tok_decl(3,0) #define TOK_DIV_ASSIGN tok_decl(3,1) #define TOK_REM_ASSIGN tok_decl(3,2) #define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0) /* Ternary conditional operator is right associative too */ #define TOK_CONDITIONAL tok_decl(4,0) #define TOK_CONDITIONAL_SEP tok_decl(4,1) #define TOK_OR tok_decl(5,0) #define TOK_AND tok_decl(6,0) #define TOK_BOR tok_decl(7,0) #define TOK_BXOR tok_decl(8,0) #define TOK_BAND tok_decl(9,0) #define TOK_EQ tok_decl(10,0) #define TOK_NE tok_decl(10,1) #define TOK_LT tok_decl(11,0) #define TOK_GT tok_decl(11,1) #define TOK_GE tok_decl(11,2) #define TOK_LE tok_decl(11,3) #define TOK_LSHIFT tok_decl(12,0) #define TOK_RSHIFT tok_decl(12,1) #define TOK_ADD tok_decl(13,0) #define TOK_SUB tok_decl(13,1) #define TOK_MUL tok_decl(14,0) #define TOK_DIV tok_decl(14,1) #define TOK_REM tok_decl(14,2) /* Exponent is right associative */ #define TOK_EXPONENT tok_decl(15,1) /* Unary operators */ #define UNARYPREC 16 #define TOK_BNOT tok_decl(UNARYPREC,0) #define TOK_NOT tok_decl(UNARYPREC,1) #define TOK_UMINUS tok_decl(UNARYPREC+1,0) #define TOK_UPLUS tok_decl(UNARYPREC+1,1) #define PREC_PRE (UNARYPREC+2) #define TOK_PRE_INC tok_decl(PREC_PRE, 0) #define TOK_PRE_DEC tok_decl(PREC_PRE, 1) #define PREC_POST (UNARYPREC+3) #define TOK_POST_INC tok_decl(PREC_POST, 0) #define TOK_POST_DEC tok_decl(PREC_POST, 1) #define SPEC_PREC (UNARYPREC+4) #define TOK_NUM tok_decl(SPEC_PREC, 0) #define TOK_RPAREN tok_decl(SPEC_PREC, 1) static int is_assign_op(operator op) { operator prec = PREC(op); fix_assignment_prec(prec); return prec == PREC(TOK_ASSIGN) || prec == PREC_PRE || prec == PREC_POST; } static int is_right_associative(operator prec) { return prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) || prec == PREC(TOK_CONDITIONAL); } typedef struct { arith_t val; /* We acquire second_val only when "expr1 : expr2" part * of ternary ?: op is evaluated. * We treat ?: as two binary ops: (expr ? (expr1 : expr2)). * ':' produces a new value which has two parts, val and second_val; * then '?' selects one of them based on its left side. */ arith_t second_val; char second_val_present; /* If NULL then it's just a number, else it's a named variable */ char *var; } var_or_num_t; typedef struct remembered_name { struct remembered_name *next; const char *var; } remembered_name; static arith_t FAST_FUNC evaluate_string(arith_state_t *math_state, const char *expr); static const char* arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) { if (t->var) { const char *p = lookupvar(t->var); if (p) { remembered_name *cur; remembered_name cur_save; /* did we already see this name? * testcase: a=b; b=a; echo $((a)) */ for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { if (strcmp(cur->var, t->var) == 0) { /* Yes */ return "expression recursion loop detected"; } } /* push current var name */ cur = math_state->list_of_recursed_names; cur_save.var = t->var; cur_save.next = cur; math_state->list_of_recursed_names = &cur_save; /* recursively evaluate p as expression */ t->val = evaluate_string(math_state, p); /* pop current var name */ math_state->list_of_recursed_names = cur; return math_state->errmsg; } /* treat undefined var as 0 */ t->val = 0; } return 0; } /* "Applying" a token means performing it on the top elements on the integer * stack. For an unary operator it will only change the top element, but a * binary operator will pop two arguments and push the result */ static NOINLINE const char* arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_or_num_t **numstackptr) { #define NUMPTR (*numstackptr) var_or_num_t *top_of_stack; arith_t rez; const char *err; /* There is no operator that can work without arguments */ if (NUMPTR == numstack) goto err; top_of_stack = NUMPTR - 1; /* Resolve name to value, if needed */ err = arith_lookup_val(math_state, top_of_stack); if (err) return err; rez = top_of_stack->val; if (op == TOK_UMINUS) rez = -rez; else if (op == TOK_NOT) rez = !rez; else if (op == TOK_BNOT) rez = ~rez; else if (op == TOK_POST_INC || op == TOK_PRE_INC) rez++; else if (op == TOK_POST_DEC || op == TOK_PRE_DEC) rez--; else if (op != TOK_UPLUS) { /* Binary operators */ arith_t right_side_val; char bad_second_val; /* Binary operators need two arguments */ if (top_of_stack == numstack) goto err; /* ...and they pop one */ NUMPTR = top_of_stack; /* this decrements NUMPTR */ bad_second_val = top_of_stack->second_val_present; if (op == TOK_CONDITIONAL) { /* ? operation */ /* Make next if (...) protect against * $((expr1 ? expr2)) - that is, missing ": expr" */ bad_second_val = !bad_second_val; } if (bad_second_val) { /* Protect against $((expr <not_?_op> expr1 : expr2)) */ return "malformed ?: operator"; } top_of_stack--; /* now points to left side */ if (op != TOK_ASSIGN) { /* Resolve left side value (unless the op is '=') */ err = arith_lookup_val(math_state, top_of_stack); if (err) return err; } right_side_val = rez; rez = top_of_stack->val; if (op == TOK_CONDITIONAL) /* ? operation */ rez = (rez ? right_side_val : top_of_stack[1].second_val); else if (op == TOK_CONDITIONAL_SEP) { /* : operation */ if (top_of_stack == numstack) { /* Protect against $((expr : expr)) */ return "malformed ?: operator"; } top_of_stack->second_val_present = op; top_of_stack->second_val = right_side_val; } else if (op == TOK_BOR || op == TOK_OR_ASSIGN) rez |= right_side_val; else if (op == TOK_OR) rez = right_side_val || rez; else if (op == TOK_BAND || op == TOK_AND_ASSIGN) rez &= right_side_val; else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) rez ^= right_side_val; else if (op == TOK_AND) rez = rez && right_side_val; else if (op == TOK_EQ) rez = (rez == right_side_val); else if (op == TOK_NE) rez = (rez != right_side_val); else if (op == TOK_GE) rez = (rez >= right_side_val); else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) rez >>= right_side_val; else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) rez <<= right_side_val; else if (op == TOK_GT) rez = (rez > right_side_val); else if (op == TOK_LT) rez = (rez < right_side_val); else if (op == TOK_LE) rez = (rez <= right_side_val); else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) rez *= right_side_val; else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) rez += right_side_val; else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) rez -= right_side_val; else if (op == TOK_ASSIGN || op == TOK_COMMA) rez = right_side_val; else if (op == TOK_EXPONENT) { arith_t c; if (right_side_val < 0) return "exponent less than 0"; c = 1; while (--right_side_val >= 0) c *= rez; rez = c; } else if (right_side_val == 0) return "divide by zero"; else if (op == TOK_DIV || op == TOK_DIV_ASSIGN) rez /= right_side_val; else if (op == TOK_REM || op == TOK_REM_ASSIGN) rez %= right_side_val; } if (is_assign_op(op)) { char buf[sizeof(arith_t)*3 + 2]; if (top_of_stack->var == NULL) { /* Hmm, 1=2 ? */ //TODO: actually, bash allows ++7 but for some reason it evals to 7, not 8 goto err; } /* Save to shell variable */ sprintf(buf, ARITH_FMT, rez); setvar(top_of_stack->var, buf); /* After saving, make previous value for v++ or v-- */ if (op == TOK_POST_INC) rez--; else if (op == TOK_POST_DEC) rez++; } top_of_stack->val = rez; /* Erase var name, it is just a number now */ top_of_stack->var = NULL; return NULL; err: return "arithmetic syntax error"; #undef NUMPTR } /* longest must be first */ static const char op_tokens[] ALIGN1 = { '<','<','=',0, TOK_LSHIFT_ASSIGN, '>','>','=',0, TOK_RSHIFT_ASSIGN, '<','<', 0, TOK_LSHIFT, '>','>', 0, TOK_RSHIFT, '|','|', 0, TOK_OR, '&','&', 0, TOK_AND, '!','=', 0, TOK_NE, '<','=', 0, TOK_LE, '>','=', 0, TOK_GE, '=','=', 0, TOK_EQ, '|','=', 0, TOK_OR_ASSIGN, '&','=', 0, TOK_AND_ASSIGN, '*','=', 0, TOK_MUL_ASSIGN, '/','=', 0, TOK_DIV_ASSIGN, '%','=', 0, TOK_REM_ASSIGN, '+','=', 0, TOK_PLUS_ASSIGN, '-','=', 0, TOK_MINUS_ASSIGN, '-','-', 0, TOK_POST_DEC, '^','=', 0, TOK_XOR_ASSIGN, '+','+', 0, TOK_POST_INC, '*','*', 0, TOK_EXPONENT, '!', 0, TOK_NOT, '<', 0, TOK_LT, '>', 0, TOK_GT, '=', 0, TOK_ASSIGN, '|', 0, TOK_BOR, '&', 0, TOK_BAND, '*', 0, TOK_MUL, '/', 0, TOK_DIV, '%', 0, TOK_REM, '+', 0, TOK_ADD, '-', 0, TOK_SUB, '^', 0, TOK_BXOR, /* uniq */ '~', 0, TOK_BNOT, ',', 0, TOK_COMMA, '?', 0, TOK_CONDITIONAL, ':', 0, TOK_CONDITIONAL_SEP, ')', 0, TOK_RPAREN, '(', 0, TOK_LPAREN, 0 }; #define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7]) static arith_t FAST_FUNC evaluate_string(arith_state_t *math_state, const char *expr) { operator lasttok; const char *errmsg; const char *start_expr = expr = skip_whitespace(expr); unsigned expr_len = strlen(expr) + 2; /* Stack of integers */ /* The proof that there can be no more than strlen(startbuf)/2+1 * integers in any given correct or incorrect expression * is left as an exercise to the reader. */ var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0])); var_or_num_t *numstackptr = numstack; /* Stack of operator tokens */ operator *const stack = alloca(expr_len * sizeof(stack[0])); operator *stackptr = stack; /* Start with a left paren */ *stackptr++ = lasttok = TOK_LPAREN; errmsg = NULL; while (1) { const char *p; operator op; operator prec; char arithval; expr = skip_whitespace(expr); arithval = *expr; if (arithval == '\0') { if (expr == start_expr) { /* Null expression */ numstack->val = 0; goto ret; } /* This is only reached after all tokens have been extracted from the * input stream. If there are still tokens on the operator stack, they * are to be applied in order. At the end, there should be a final * result on the integer stack */ if (expr != ptr_to_rparen + 1) { /* If we haven't done so already, * append a closing right paren * and let the loop process it */ expr = ptr_to_rparen; continue; } /* At this point, we're done with the expression */ if (numstackptr != numstack + 1) { /* ...but if there isn't, it's bad */ goto err; } if (numstack->var) { /* expression is $((var)) only, lookup now */ errmsg = arith_lookup_val(math_state, numstack); } goto ret; } p = endofname(expr); if (p != expr) { /* Name */ size_t var_name_size = (p-expr) + 1; /* +1 for NUL */ numstackptr->var = alloca(var_name_size); safe_strncpy(numstackptr->var, expr, var_name_size); expr = p; num: numstackptr->second_val_present = 0; numstackptr++; lasttok = TOK_NUM; continue; } if (isdigit(arithval)) { /* Number */ numstackptr->var = NULL; errno = 0; numstackptr->val = strto_arith_t(expr, (char**) &expr, 0); if (errno) numstackptr->val = 0; /* bash compat */ goto num; } /* Should be an operator */ p = op_tokens; while (1) { // TODO: bash allows 7+++v, treats it as 7 + ++v // we treat it as 7++ + v and reject /* Compare expr to current op_tokens[] element */ const char *e = expr; while (1) { if (*p == '\0') { /* Match: operator is found */ expr = e; goto tok_found; } if (*p != *e) break; p++; e++; } /* No match, go to next element of op_tokens[] */ while (*p) p++; p += 2; /* skip NUL and TOK_foo bytes */ if (*p == '\0') { /* No next element, operator not found */ //math_state->syntax_error_at = expr; goto err; } } tok_found: op = p[1]; /* fetch TOK_foo value */ /* NB: expr now points past the operator */ /* post grammar: a++ reduce to num */ if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) lasttok = TOK_NUM; /* Plus and minus are binary (not unary) _only_ if the last * token was a number, or a right paren (which pretends to be * a number, since it evaluates to one). Think about it. * It makes sense. */ if (lasttok != TOK_NUM) { switch (op) { case TOK_ADD: op = TOK_UPLUS; break; case TOK_SUB: op = TOK_UMINUS; break; case TOK_POST_INC: op = TOK_PRE_INC; break; case TOK_POST_DEC: op = TOK_PRE_DEC; break; } } /* We don't want an unary operator to cause recursive descent on the * stack, because there can be many in a row and it could cause an * operator to be evaluated before its argument is pushed onto the * integer stack. * But for binary operators, "apply" everything on the operator * stack until we find an operator with a lesser priority than the * one we have just extracted. If op is right-associative, * then stop "applying" on the equal priority too. * Left paren is given the lowest priority so it will never be * "applied" in this way. */ prec = PREC(op); if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { /* not left paren or unary */ if (lasttok != TOK_NUM) { /* binary op must be preceded by a num */ goto err; } while (stackptr != stack) { operator prev_op = *--stackptr; if (op == TOK_RPAREN) { /* The algorithm employed here is simple: while we don't * hit an open paren nor the bottom of the stack, pop * tokens and apply them */ if (prev_op == TOK_LPAREN) { /* Any operator directly after a * close paren should consider itself binary */ lasttok = TOK_NUM; goto next; } } else { operator prev_prec = PREC(prev_op); fix_assignment_prec(prec); fix_assignment_prec(prev_prec); if (prev_prec < prec || (prev_prec == prec && is_right_associative(prec)) ) { stackptr++; break; } } errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); if (errmsg) goto err_with_custom_msg; } if (op == TOK_RPAREN) goto err; } /* Push this operator to the stack and remember it */ *stackptr++ = lasttok = op; next: ; } /* while (1) */ err: errmsg = "arithmetic syntax error"; err_with_custom_msg: numstack->val = -1; ret: math_state->errmsg = errmsg; return numstack->val; } arith_t FAST_FUNC arith(arith_state_t *math_state, const char *expr) { math_state->errmsg = NULL; math_state->list_of_recursed_names = NULL; return evaluate_string(math_state, expr); } /* * Copyright (c) 1989, 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * 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. */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/hush_doc.txt�������������������������������������������������������������������0000644�0000000�0000000�00000012064�12263563520�015441� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008-07-14 Command parsing Command parsing results in a list of "pipe" structures. This list correspond not only to usual "pipe1 || pipe2 && pipe3" lists, but it also controls execution of if, while, etc statements. Every such statement is a list for hush. List consists of pipes. struct pipe fields: smallint res_word - "none" for normal commands, "if" for if condition etc struct child_prog progs[] - array of commands in pipe smallint followup - how this pipe is related to next: is it "pipe; pipe", "pipe & pipe" "pipe && pipe", "pipe || pipe"? Blocks of commands { pipe; pipe; } and (pipe; pipe) are represented as one pipe struct with one progs[0] element which is a "group" - struct child_prog can contain a list of pipes. Sometimes these "groups" are created implicitly, e.g. every control statement (if, while, etc) sits inside its own group. res_word controls statement execution. Examples: "echo Hello" - pipe 0 res_word=NONE followup=SEQ prog[0] 'echo' 'Hello' pipe 1 res_word=NONE followup=SEQ "echo foo || echo bar" - pipe 0 res_word=NONE followup=OR prog[0] 'echo' 'foo' pipe 1 res_word=NONE followup=SEQ prog[0] 'echo' 'bar' pipe 2 res_word=NONE followup=SEQ "if true; then echo Hello; true; fi" - res_word=NONE followup=SEQ prog 0 group {}: pipe 0 res_word=IF followup=SEQ prog[0] 'true' pipe 1 res_word=THEN followup=SEQ prog[0] 'echo' 'Hello' pipe 2 res_word=THEN followup=SEQ prog[0] 'true' pipe 3 res_word=FI followup=SEQ pipe 4 res_word=NONE followup=(null) pipe 1 res_word=NONE followup=SEQ Above you see that if is a list, and it sits in a {} group implicitly created by hush. Also note two THEN res_word's - it is explained below. "if true; then { echo Hello; true; }; fi" - pipe 0 res_word=NONE followup=SEQ prog 0 group {}: pipe 0 res_word=IF followup=SEQ prog[0] 'true' pipe 1 res_word=THEN followup=SEQ prog 0 group {}: pipe 0 res_word=NONE followup=SEQ prog[0] 'echo' 'Hello' pipe 1 res_word=NONE followup=SEQ prog[0] 'true' pipe 2 res_word=NONE followup=SEQ pipe 2 res_word=NONE followup=(null) pipe 1 res_word=NONE followup=SEQ "for v in a b; do echo $v; true; done" - pipe 0 res_word=NONE followup=SEQ prog 0 group {}: pipe 0 res_word=FOR followup=SEQ prog[0] 'v' pipe 1 res_word=IN followup=SEQ prog[0] 'a' 'b' pipe 2 res_word=DO followup=SEQ prog[0] 'echo' '$v' pipe 3 res_word=DO followup=SEQ prog[0] 'true' pipe 4 res_word=DONE followup=SEQ pipe 5 res_word=NONE followup=(null) pipe 1 res_word=NONE followup=SEQ Note how "THEN" and "DO" does not just mark the first pipe, it "sticks" to all pipes in the body. This is used when hush executes parsed pipes. Dummy trailing pipes with no commands are artifacts of imperfect parsing algorithm - done_pipe() appends new pipe struct beforehand and last one ends up empty and unused. "for" and "case" statements (ab)use progs[] to keep their data instead of argv vector progs[] usually do. "for" keyword is forcing pipe termination after first word, which makes hush see "for v in..." as "for v; in...". "case" keyword does the same. Other judiciuosly placed hacks make hush see "case word in a) cmd1;; b) cmd2;; esac" as if it was "case word; match a; cmd; match b; cmd2; esac" ("match" is a fictitious keyword here): "case word in a) cmd1;; b) cmd2; esac" - pipe 0 res_word=NONE followup=1 SEQ prog 0 group {}: pipe 0 res_word=CASE followup=SEQ prog[0] 'word' pipe 1 res_word=MATCH followup=SEQ prog[0] 'a' pipe 2 res_word=CASEI followup=SEQ prog[0] 'cmd1' pipe 3 res_word=MATCH followup=SEQ prog[0] 'b' pipe 4 res_word=CASEI followup=SEQ prog[0] 'cmd2' pipe 5 res_word=CASEI followup=SEQ prog[0] 'cmd3' pipe 6 res_word=ESAC followup=SEQ pipe 7 res_word=NONE followup=(null) pipe 1 res_word=NONE followup=SEQ 2008-01 Command execution /* callsite: process_command_subs */ generate_stream_from_list(struct pipe *head) - handles `cmds` create UNIX pipe [v]fork child: redirect pipe output to stdout _exit(run_list(head)); /* leaks memory */ parent: return UNIX pipe's output fd /* head is freed by the caller */ /* callsite: parse_and_run_stream */ run_and_free_list(struct pipe *) run_list(struct pipe *) free_pipe_list(struct pipe *) /* callsites: generate_stream_from_list, run_and_free_list, pseudo_exec, run_pipe */ run_list(struct pipe *) - handles "cmd; cmd2 && cmd3", while/for/do loops run_pipe - for every pipe in list /* callsite: run_list */ run_pipe - runs "cmd1 | cmd2 | cmd3 [&]" run_list - used if only one cmd and it is of the form "{cmds;}" forks for every cmd if more than one cmd or if & is there pseudo_exec - runs each "cmdN" (handles builtins etc) /* callsite: run_pipe */ pseudo_exec - runs "cmd" (handles builtins etc) exec - execs external programs run_list - used if cmdN is "(cmds)" or "{cmds;}" /* problem: putenv's malloced strings into environ - ** with vfork they will leak into parent process */ /* problem with ENABLE_FEATURE_SH_STANDALONE: ** run_applet_no_and_exit(a, argv) uses exit - this can interfere ** with vfork - switch to _exit there? */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/shell_common.c�����������������������������������������������������������������0000644�0000000�0000000�00000026402�12263563520�015730� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vi: set sw=4 ts=4: */ /* * Adapted from ash applet code * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * Copyright (c) 1989, 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au> * was re-ported from NetBSD and debianized. * * Copyright (c) 2010 Denys Vlasenko * Split from ash.c * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" #include "shell_common.h" #include <sys/resource.h> /* getrlimit */ const char defifsvar[] ALIGN1 = "IFS= \t\n"; int FAST_FUNC is_well_formed_var_name(const char *s, char terminator) { if (!s || !(isalpha(*s) || *s == '_')) return 0; do s++; while (isalnum(*s) || *s == '_'); return *s == terminator; } /* read builtin */ /* Needs to be interruptible: shell must handle traps and shell-special signals * while inside read. To implement this, be sure to not loop on EINTR * and return errno == EINTR reliably. */ //TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL" //string. hush naturally has it, and ash has setvareq(). //Here we can simply store "VAR=" at buffer start and store read data directly //after "=", then pass buffer to setvar() to consume. const char* FAST_FUNC shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), char **argv, const char *ifs, int read_flags, const char *opt_n, const char *opt_p, const char *opt_t, const char *opt_u ) { unsigned err; unsigned end_ms; /* -t TIMEOUT */ int fd; /* -u FD */ int nchars; /* -n NUM */ char **pp; char *buffer; struct termios tty, old_tty; const char *retval; int bufpos; /* need to be able to hold -1 */ int startword; smallint backslash; errno = err = 0; pp = argv; while (*pp) { if (!is_well_formed_var_name(*pp, '\0')) { /* Mimic bash message */ bb_error_msg("read: '%s': not a valid identifier", *pp); return (const char *)(uintptr_t)1; } pp++; } nchars = 0; /* if != 0, -n is in effect */ if (opt_n) { nchars = bb_strtou(opt_n, NULL, 10); if (nchars < 0 || errno) return "invalid count"; /* note: "-n 0": off (bash 3.2 does this too) */ } end_ms = 0; if (opt_t) { end_ms = bb_strtou(opt_t, NULL, 10); if (errno || end_ms > UINT_MAX / 2048) return "invalid timeout"; end_ms *= 1000; #if 0 /* even bash has no -t N.NNN support */ ts.tv_sec = bb_strtou(opt_t, &p, 10); ts.tv_usec = 0; /* EINVAL means number is ok, but not terminated by NUL */ if (*p == '.' && errno == EINVAL) { char *p2; if (*++p) { int scale; ts.tv_usec = bb_strtou(p, &p2, 10); if (errno) return "invalid timeout"; scale = p2 - p; /* normalize to usec */ if (scale > 6) return "invalid timeout"; while (scale++ < 6) ts.tv_usec *= 10; } } else if (ts.tv_sec < 0 || errno) { return "invalid timeout"; } if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */ return "invalid timeout"; } #endif /* if 0 */ } fd = STDIN_FILENO; if (opt_u) { fd = bb_strtou(opt_u, NULL, 10); if (fd < 0 || errno) return "invalid file descriptor"; } if (opt_p && isatty(fd)) { fputs(opt_p, stderr); fflush_all(); } if (ifs == NULL) ifs = defifs; if (nchars || (read_flags & BUILTIN_READ_SILENT)) { tcgetattr(fd, &tty); old_tty = tty; if (nchars) { tty.c_lflag &= ~ICANON; // Setting it to more than 1 breaks poll(): // it blocks even if there's data. !?? //tty.c_cc[VMIN] = nchars < 256 ? nchars : 255; /* reads would block only if < 1 char is available */ tty.c_cc[VMIN] = 1; /* no timeout (reads block forever) */ tty.c_cc[VTIME] = 0; } if (read_flags & BUILTIN_READ_SILENT) { tty.c_lflag &= ~(ECHO | ECHOK | ECHONL); } /* This forces execution of "restoring" tcgetattr later */ read_flags |= BUILTIN_READ_SILENT; /* if tcgetattr failed, tcsetattr will fail too. * Ignoring, it's harmless. */ tcsetattr(fd, TCSANOW, &tty); } retval = (const char *)(uintptr_t)0; startword = 1; backslash = 0; if (end_ms) /* NB: end_ms stays nonzero: */ end_ms = ((unsigned)monotonic_ms() + end_ms) | 1; buffer = NULL; bufpos = 0; do { char c; struct pollfd pfd[1]; int timeout; if ((bufpos & 0xff) == 0) buffer = xrealloc(buffer, bufpos + 0x101); timeout = -1; if (end_ms) { timeout = end_ms - (unsigned)monotonic_ms(); if (timeout <= 0) { /* already late? */ retval = (const char *)(uintptr_t)1; goto ret; } } /* We must poll even if timeout is -1: * we want to be interrupted if signal arrives, * regardless of SA_RESTART-ness of that signal! */ errno = 0; pfd[0].fd = fd; pfd[0].events = POLLIN; if (poll(pfd, 1, timeout) != 1) { /* timed out, or EINTR */ err = errno; retval = (const char *)(uintptr_t)1; goto ret; } if (read(fd, &buffer[bufpos], 1) != 1) { err = errno; retval = (const char *)(uintptr_t)1; break; } c = buffer[bufpos]; if (c == '\0') continue; if (backslash) { backslash = 0; if (c != '\n') goto put; continue; } if (!(read_flags & BUILTIN_READ_RAW) && c == '\\') { backslash = 1; continue; } if (c == '\n') break; /* $IFS splitting. NOT done if we run "read" * without variable names (bash compat). * Thus, "read" and "read REPLY" are not the same. */ if (argv[0]) { /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */ const char *is_ifs = strchr(ifs, c); if (startword && is_ifs) { if (isspace(c)) continue; /* it is a non-space ifs char */ startword--; if (startword == 1) /* first one? */ continue; /* yes, it is not next word yet */ } startword = 0; if (argv[1] != NULL && is_ifs) { buffer[bufpos] = '\0'; bufpos = 0; setvar(*argv, buffer); argv++; /* can we skip one non-space ifs char? (2: yes) */ startword = isspace(c) ? 2 : 1; continue; } } put: bufpos++; } while (--nchars); if (argv[0]) { /* Remove trailing space $IFS chars */ while (--bufpos >= 0 && isspace(buffer[bufpos]) && strchr(ifs, buffer[bufpos]) != NULL) continue; buffer[bufpos + 1] = '\0'; /* Use the remainder as a value for the next variable */ setvar(*argv, buffer); /* Set the rest to "" */ while (*++argv) setvar(*argv, ""); } else { /* Note: no $IFS removal */ buffer[bufpos] = '\0'; setvar("REPLY", buffer); } ret: free(buffer); if (read_flags & BUILTIN_READ_SILENT) tcsetattr(fd, TCSANOW, &old_tty); errno = err; return retval; } /* ulimit builtin */ struct limits { uint8_t cmd; /* RLIMIT_xxx fit into it */ uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ char option; const char *name; }; static const struct limits limits_tbl[] = { #ifdef RLIMIT_FSIZE { RLIMIT_FSIZE, 9, 'f', "file size (blocks)" }, #endif #ifdef RLIMIT_CPU { RLIMIT_CPU, 0, 't', "cpu time (seconds)" }, #endif #ifdef RLIMIT_DATA { RLIMIT_DATA, 10, 'd', "data seg size (kb)" }, #endif #ifdef RLIMIT_STACK { RLIMIT_STACK, 10, 's', "stack size (kb)" }, #endif #ifdef RLIMIT_CORE { RLIMIT_CORE, 9, 'c', "core file size (blocks)" }, #endif #ifdef RLIMIT_RSS { RLIMIT_RSS, 10, 'm', "resident set size (kb)" }, #endif #ifdef RLIMIT_MEMLOCK { RLIMIT_MEMLOCK, 10, 'l', "locked memory (kb)" }, #endif #ifdef RLIMIT_NPROC { RLIMIT_NPROC, 0, 'p', "processes" }, #endif #ifdef RLIMIT_NOFILE { RLIMIT_NOFILE, 0, 'n', "file descriptors" }, #endif #ifdef RLIMIT_AS { RLIMIT_AS, 10, 'v', "address space (kb)" }, #endif #ifdef RLIMIT_LOCKS { RLIMIT_LOCKS, 0, 'w', "locks" }, #endif #ifdef RLIMIT_NICE { RLIMIT_NICE, 0, 'e', "scheduling priority" }, #endif #ifdef RLIMIT_RTPRIO { RLIMIT_RTPRIO, 0, 'r', "real-time priority" }, #endif }; enum { OPT_hard = (1 << 0), OPT_soft = (1 << 1), }; /* "-": treat args as parameters of option with ASCII code 1 */ static const char ulimit_opt_string[] = "-HSa" #ifdef RLIMIT_FSIZE "f::" #endif #ifdef RLIMIT_CPU "t::" #endif #ifdef RLIMIT_DATA "d::" #endif #ifdef RLIMIT_STACK "s::" #endif #ifdef RLIMIT_CORE "c::" #endif #ifdef RLIMIT_RSS "m::" #endif #ifdef RLIMIT_MEMLOCK "l::" #endif #ifdef RLIMIT_NPROC "p::" #endif #ifdef RLIMIT_NOFILE "n::" #endif #ifdef RLIMIT_AS "v::" #endif #ifdef RLIMIT_LOCKS "w::" #endif #ifdef RLIMIT_NICE "e::" #endif #ifdef RLIMIT_RTPRIO "r::" #endif ; static void printlim(unsigned opts, const struct rlimit *limit, const struct limits *l) { rlim_t val; val = limit->rlim_max; if (!(opts & OPT_hard)) val = limit->rlim_cur; if (val == RLIM_INFINITY) printf("unlimited\n"); else { val >>= l->factor_shift; printf("%llu\n", (long long) val); } } int FAST_FUNC shell_builtin_ulimit(char **argv) { unsigned opts; unsigned argc; /* We can't use getopt32: need to handle commands like * ulimit 123 -c2 -l 456 */ /* In case getopt was already called: * reset the libc getopt() function, which keeps internal state. */ #ifdef __GLIBC__ optind = 0; #else /* BSD style */ optind = 1; /* optreset = 1; */ #endif /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */ argc = 1; while (argv[argc]) argc++; opts = 0; while (1) { struct rlimit limit; const struct limits *l; int opt_char = getopt(argc, argv, ulimit_opt_string); if (opt_char == -1) break; if (opt_char == 'H') { opts |= OPT_hard; continue; } if (opt_char == 'S') { opts |= OPT_soft; continue; } if (opt_char == 'a') { for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { getrlimit(l->cmd, &limit); printf("-%c: %-30s ", l->option, l->name); printlim(opts, &limit, l); } continue; } if (opt_char == 1) opt_char = 'f'; for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { if (opt_char == l->option) { char *val_str; getrlimit(l->cmd, &limit); val_str = optarg; if (!val_str && argv[optind] && argv[optind][0] != '-') val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */ if (val_str) { rlim_t val; if (strcmp(val_str, "unlimited") == 0) val = RLIM_INFINITY; else { if (sizeof(val) == sizeof(int)) val = bb_strtou(val_str, NULL, 10); else if (sizeof(val) == sizeof(long)) val = bb_strtoul(val_str, NULL, 10); else val = bb_strtoull(val_str, NULL, 10); if (errno) { bb_error_msg("invalid number '%s'", val_str); return EXIT_FAILURE; } val <<= l->factor_shift; } //bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val); /* from man bash: "If neither -H nor -S * is specified, both the soft and hard * limits are set. */ if (!opts) opts = OPT_hard + OPT_soft; if (opts & OPT_hard) limit.rlim_max = val; if (opts & OPT_soft) limit.rlim_cur = val; //bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max); if (setrlimit(l->cmd, &limit) < 0) { bb_perror_msg("error setting limit"); return EXIT_FAILURE; } } else { printlim(opts, &limit, l); } break; } } /* for (every possible opt) */ if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) { /* bad option. getopt already complained. */ break; } } /* while (there are options) */ return 0; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/shell/README.job���������������������������������������������������������������������0000644�0000000�0000000�00000040243�12263563520�014535� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������strace of "sleep 1 | sleep 2" being run from interactive bash 3.0 Synopsis: open /dev/tty [, if fails, open ttyname(0)]; close /* helps re-establish ctty */ get current signal mask TCGETS on fd# 0 TCGETS on fd# 2 /* NB: if returns ENOTTY (2>/dev/null), sh seems to disable job control, does not show prompt, but still executes cmds from fd# 0 */ install default handlers for CHLD QUIT TERM install common handler for HUP INT ILL TRAP ABRT FPE BUS SEGV SYS PIPE ALRM TERM XCPU XFSZ VTALRM USR1 USR2 ignore QUIT install handler for INT ignore TERM install handler for INT ignore TSTP TTOU TTIN install handler for WINCH get pid, ppid block all signals unblock all signals get our pprocess group minidoc: Each process group is a member of a session and each process is a member of the session of which its process group is a member. Process groups are used for distribution of signals, and by terminals to arbitrate requests for their input: processes that have the same process group as the terminal are foreground and may read, while others will block with a signal if they attempt to read. These calls are thus used by programs (shells) to create process groups in implementing job control. The TIOCGPGRP and TIOCSPGRP calls described in termios(3) are used to get/set the process group of the control terminal. If a session has a controlling terminal, CLOCAL is not set and a hangup occurs, then the session leader is sent a SIGHUP. If the session leader exits, the SIGHUP signal will be sent to each process in the foreground process group of the controlling terminal. If the exit of the process causes a process group to become orphaned, and if any member of the newly-orphaned process group is stopped, then a SIGHUP signal followed by a SIGCONT signal will be sent to each process in the newly-orphaned process group. ... dup stderr to fd# 255 move ourself to our own process group block CHLD TSTP TTIN TTOU set tty's (255, stderr's) foreground process group to our group allow all signals mark 255 CLOEXEC set CHLD handler get signal mask get fd#0 flags get signal mask set INT handler block CHLD TSTP TTIN TTOU set fd #255 foreground process group to our group allow all signals set INT handler block all signals allow all signals block INT allow all signals lotsa sigactions: set INT,ALRM,WINCH handlers, ignore TERM,QUIT,TSTP,TTOU,TTIN block all signals allow all signals block all signals allow all signals block all signals allow all signals read "sleep 1 | sleep 2\n" block INT TCSETSW on fd# 0 allow all signals lotsa sigactions: set INT,ALRM,WINCH handlers, ignore TERM,QUIT,TSTP,TTOU,TTIN block CHLD pipe([4, 5]) /* oops seems I lost another pipe() in editing... */ fork child #1 put child in it's own process group block only CHLD close(5) block only INT CHLD fork child #2 put child in the same process group as first one block only CHLD close(4) block only CHLD block only CHLD TSTP TTIN TTOU set fd# 255 foreground process group to first child's one block only CHLD block only CHLD block only CHLD /* note: because shell is not in foreground now, e.g. Ctrl-C will send INT to children only! */ wait4 for children to die or stop - first child exits wait4 for children to die or stop - second child exits block CHLD TSTP TTIN TTOU set fd# 255 foreground process group to our own one block only CHLD block only CHLD block nothing --- SIGCHLD (Child exited) @ 0 (0) --- wait for it - no child (already waited for) sigreturn() read signal mask lotsa sigactions... read next command execve("/bin/sh", ["sh"], [/* 34 vars */]) = 0 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0 ioctl(2, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0 rt_sigaction(SIGCHLD, {SIG_DFL}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGQUIT, {SIG_DFL}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGTERM, {SIG_DFL}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGHUP, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGINT, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGILL, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGTRAP, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGABRT, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGFPE, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGBUS, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGSEGV, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGSYS, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGPIPE, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGALRM, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGTERM, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGXCPU, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGXFSZ, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGVTALRM, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGUSR1, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGUSR2, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 rt_sigaction(SIGQUIT, {SIG_IGN}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGTERM, {SIG_IGN}, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGTSTP, {SIG_IGN}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGTTOU, {SIG_IGN}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGTTIN, {SIG_IGN}, {SIG_DFL}, 8) = 0 rt_sigaction(SIGWINCH, {0x807dc33, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 getpid() = 19473 getppid() = 19472 rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 getpgrp() = 1865 dup(2) = 4 fcntl64(255, F_GETFD) = -1 EBADF (Bad file descriptor) dup2(4, 255) = 255 close(4) = 0 ioctl(255, TIOCGPGRP, [1865]) = 0 getpid() = 19473 setpgid(0, 19473) = 0 rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [], 8) = 0 ioctl(255, TIOCSPGRP, [19473]) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 fcntl64(255, F_SETFD, FD_CLOEXEC) = 0 rt_sigaction(SIGCHLD, {0x807c922, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 fcntl64(0, F_GETFL) = 0x2 (flags O_RDWR) ... rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [], 8) = 0 ioctl(255, TIOCSPGRP, [19473]) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 rt_sigaction(SIGINT, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGTERM, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTERM, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGQUIT, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGQUIT, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGALRM, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGTSTP, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTSTP, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGTTOU, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTTOU, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGTTIN, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTTIN, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGWINCH, {0x80ca5cd, [], SA_RESTORER|SA_RESTART, 0x6ff7a4f8}, {0x807dc33, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 write(2, "sh-3.00# ", 9) = 9 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 read(0, "s", 1) = 1 write(2, "s", 1) = 1 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 read(0, "l", 1) = 1 write(2, "l", 1) = 1 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 ... rest of "sleep 1 | sleep 2" entered... rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 read(0, "2", 1) = 1 write(2, "2", 1) = 1 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 read(0, "\r", 1) = 1 write(2, "\n", 1) = 1 rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0 ioctl(0, SNDCTL_TMR_STOP or TCSETSW, {B38400 opost isig icanon echo ...}) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGTERM, {SIG_IGN}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGQUIT, {SIG_IGN}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGALRM, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGTSTP, {SIG_IGN}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTTOU, {SIG_IGN}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTTIN, {SIG_IGN}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGWINCH, {0x807dc33, [], SA_RESTORER, 0x6ff7a4f8}, {0x80ca5cd, [], SA_RESTORER|SA_RESTART, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0 pipe([4, 5]) = 0 rt_sigprocmask(SIG_BLOCK, [INT CHLD], [CHLD], 8) = 0 fork() = 19755 setpgid(19755, 19755) = 0 rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0 close(5) = 0 rt_sigprocmask(SIG_BLOCK, [INT CHLD], [CHLD], 8) = 0 fork() = 19756 setpgid(19756, 19755) = 0 rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0 close(4) = 0 rt_sigprocmask(SIG_BLOCK, [CHLD], [CHLD], 8) = 0 rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [CHLD], 8) = 0 ioctl(255, TIOCSPGRP, [19755]) = 0 rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0 rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0 rt_sigprocmask(SIG_BLOCK, [CHLD], [CHLD], 8) = 0 wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WUNTRACED, NULL) = 19755 wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WUNTRACED, NULL) = 19756 rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [CHLD], 8) = 0 ioctl(255, TIOCSPGRP, [19473]) = 0 rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0 rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 --- SIGCHLD (Child exited) @ 0 (0) --- wait4(-1, 0x77fc9c54, WNOHANG|WUNTRACED, NULL) = -1 ECHILD (No child processes) sigreturn() = ? (mask now []) rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [], 8) = 0 ioctl(255, TIOCSPGRP, [19473]) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 rt_sigaction(SIGINT, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGTERM, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTERM, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGQUIT, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGQUIT, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGALRM, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGTSTP, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTSTP, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGTTOU, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTTOU, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGTTIN, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTTIN, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGWINCH, {0x80ca5cd, [], SA_RESTORER|SA_RESTART, 0x6ff7a4f8}, {0x807dc33, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 write(2, "sh-3.00# ", 9) = 9 getpid() = 19755 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 rt_sigaction(SIGTSTP, {SIG_DFL}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTTIN, {SIG_DFL}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTTOU, {SIG_DFL}, {SIG_IGN}, 8) = 0 setpgid(19755, 19755) = 0 rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [], 8) = 0 ioctl(255, TIOCSPGRP, [19755]) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 close(4) = 0 dup2(5, 1) = 1 close(5) = 0 rt_sigaction(SIGINT, {SIG_DFL}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGQUIT, {SIG_DFL}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTERM, {SIG_DFL}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGCHLD, {SIG_DFL}, {0x807c922, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 execve("/bin/sleep", ["sleep", "1"], [/* 34 vars */]) = 0 ... _exit(0) = ? getpid() = 19756 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 rt_sigaction(SIGTSTP, {SIG_DFL}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTTIN, {SIG_DFL}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTTOU, {SIG_DFL}, {SIG_IGN}, 8) = 0 setpgid(19756, 19755) = 0 rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [], 8) = 0 ioctl(255, TIOCSPGRP, [19755]) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 dup2(4, 0) = 0 close(4) = 0 rt_sigaction(SIGINT, {SIG_DFL}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 rt_sigaction(SIGQUIT, {SIG_DFL}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGTERM, {SIG_DFL}, {SIG_IGN}, 8) = 0 rt_sigaction(SIGCHLD, {SIG_DFL}, {0x807c922, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0 execve("/bin/sleep", ["sleep", "2"], [/* 34 vars */]) = 0 ... _exit(0) = ? �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/applets_sh/��������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365356�014135� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/applets_sh/nologin�������������������������������������������������������������������0000755�0000000�0000000�00000000142�12263563520�015524� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cat /etc/nologin.txt 2>/dev/null || echo "This account is not available" sleep 5 exit 1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/applets_sh/README��������������������������������������������������������������������0000644�0000000�0000000�00000000347�12263563520�015020� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This directory contains examples of applets implemented as shell scripts. So far these scripts are not hooked to the build system and are not installed by "make install". If you want to use them, you need to install them by hand. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/applets_sh/tac�����������������������������������������������������������������������0000755�0000000�0000000�00000000221�12263563520�014624� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # TODO: use getopt to avoid parsing options as filenames, # and to support -- and --help for i in "$@" do sed -e '1!G;h;$!d' "$i" done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/applets_sh/unix2dos������������������������������������������������������������������0000755�0000000�0000000�00000000233�12263563520�015633� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # TODO: use getopt to avoid parsing options as filenames, # and to support -- and --help [ $# -ne 0 ] && DASH_I=-i sed $DASH_I -e 's/$/\r/' "$@" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/applets_sh/dos2unix������������������������������������������������������������������0000755�0000000�0000000�00000000233�12263563520�015633� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # TODO: use getopt to avoid parsing options as filenames, # and to support -- and --help [ $# -ne 0 ] && DASH_I=-i sed $DASH_I -e 's/\r$//' "$@" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/editors/�����������������������������������������������������������������������������0000755�0000000�0000000�00000000000�12320365356�013444� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������busybox-1.22.1/editors/sed_summary.htm��������������������������������������������������������������0000644�0000000�0000000�00000020130�12263563520�016501� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <head><title>Command Summary for sed (sed & awk, Second Edition)

Command Summary for sed

: :label
Label a line in the script for the transfer of control by b or t. label may contain up to seven characters. (The POSIX standard says that an implementation can allow longer labels if it wishes to. GNU sed allows labels to be of any length.)

= [address]=
Write to standard output the line number of addressed line.

a [address]a\
text

Append text following each line matched by address. If text goes over more than one line, newlines must be "hidden" by preceding them with a backslash. The text will be terminated by the first newline that is not hidden in this way. The text is not available in the pattern space and subsequent commands cannot be applied to it. The results of this command are sent to standard output when the list of editing commands is finished, regardless of what happens to the current line in the pattern space.

b [address1[,address2]]b[label]
Transfer control unconditionally (branch) to :label elsewhere in script. That is, the command following the label is the next command applied to the current line. If no label is specified, control falls through to the end of the script, so no more commands are applied to the current line.

c [address1[,address2]]c\
text

Replace (change) the lines selected by the address with text. When a range of lines is specified, all lines as a group are replaced by a single copy of text. The newline following each line of text must be escaped by a backslash, except the last line. The contents of the pattern space are, in effect, deleted and no subsequent editing commands can be applied to it (or to text).

d [address1[,address2]]d
Delete line(s) from pattern space. Thus, the line is not passed to standard output. A new line of input is read and editing resumes with first command in script.

D [address1[,address2]]D
Delete first part (up to embedded newline) of multiline pattern space created by N command and resume editing with first command in script. If this command empties the pattern space, then a new line of input is read, as if the d command had been executed.

g [address1[,address2]]g
Copy (get) contents of hold space (see h or H command) into the pattern space, wiping out previous contents.

G [address1[,address2]]G
Append newline followed by contents of hold space (see h or H command) to contents of the pattern space. If hold space is empty, a newline is still appended to the pattern space.

h [address1[,address2]]h
Copy pattern space into hold space, a special temporary buffer. Previous contents of hold space are wiped out.

H [address1[,address2]]H
Append newline and contents of pattern space to contents of the hold space. Even if hold space is empty, this command still appends the newline first.

i [address1]i\
text

Insert text before each line matched by address. (See a for details on text.)

l [address1[,address2]]l
List the contents of the pattern space, showing nonprinting characters as ASCII codes. Long lines are wrapped.

n [address1[,address2]]n
Read next line of input into pattern space. Current line is sent to standard output. New line becomes current line and increments line counter. Control passes to command following n instead of resuming at the top of the script.

N [address1[,address2]]N
Append next input line to contents of pattern space; the new line is separated from the previous contents of the pattern space by a newline. (This command is designed to allow pattern matches across two lines. Using \n to match the embedded newline, you can match patterns across multiple lines.)

p [address1[,address2]]p
Print the addressed line(s). Note that this can result in duplicate output unless default output is suppressed by using "#n" or the -n command-line option. Typically used before commands that change flow control (d, n, b) and might prevent the current line from being output.

P [address1[,address2]]P
Print first part (up to embedded newline) of multiline pattern space created by N command. Same as p if N has not been applied to a line.

q [address]q
Quit when address is encountered. The addressed line is first written to output (if default output is not suppressed), along with any text appended to it by previous a or r commands.

r [address]r file
Read contents of file and append after the contents of the pattern space. Exactly one space must be put between r and the filename.

s [address1[,address2]]s/pattern/replacement/[flags]
Substitute replacement for pattern on each addressed line. If pattern addresses are used, the pattern // represents the last pattern address specified. The following flags can be specified:

n
Replace nth instance of /pattern/ on each addressed line. n is any number in the range 1 to 512, and the default is 1.

g
Replace all instances of /pattern/ on each addressed line, not just the first instance.

I
Matching is case-insensitive.

p
Print the line if a successful substitution is done. If several successful substitutions are done, multiple copies of the line will be printed.

w file
Write the line to file if a replacement was done. A maximum of 10 different files can be opened.

t [address1[,address2]]t [label]
Test if successful substitutions have been made on addressed lines, and if so, branch to line marked by :label. (See b and :.) If label is not specified, control falls through to bottom of script.

w [address1[,address2]]w file
Append contents of pattern space to file. This action occurs when the command is encountered rather than when the pattern space is output. Exactly one space must separate the w and the filename. A maximum of 10 different files can be opened in a script. This command will create the file if it does not exist; if the file exists, its contents will be overwritten each time the script is executed. Multiple write commands that direct output to the same file append to the end of the file.

x [address1[,address2]]x
Exchange contents of the pattern space with the contents of the hold space.

y [address1[,address2]]y/abc/xyz/
Transform each character by position in string abc to its equivalent in string xyz.

busybox-1.22.1/editors/sed1line.txt0000644000000000000000000004256512263563520015724 0ustar rootroothttp://www.student.northpark.edu/pemente/sed/sed1line.txt ------------------------------------------------------------------------- HANDY ONE-LINERS FOR SED (Unix stream editor) Apr. 26, 2004 compiled by Eric Pement - pemente[at]northpark[dot]edu version 5.4 Latest version of this file is usually at: http://sed.sourceforge.net/sed1line.txt http://www.student.northpark.edu/pemente/sed/sed1line.txt This file is also available in Portuguese at: http://www.lrv.ufsc.br/wmaker/sed_ptBR.html FILE SPACING: # double space a file sed G # double space a file which already has blank lines in it. Output file # should contain no more than one blank line between lines of text. sed '/^$/d;G' # triple space a file sed 'G;G' # undo double-spacing (assumes even-numbered lines are always blank) sed 'n;d' # insert a blank line above every line which matches "regex" sed '/regex/{x;p;x;}' # insert a blank line below every line which matches "regex" sed '/regex/G' # insert a blank line above and below every line which matches "regex" sed '/regex/{x;p;x;G;}' NUMBERING: # number each line of a file (simple left alignment). Using a tab (see # note on '\t' at end of file) instead of space will preserve margins. sed = filename | sed 'N;s/\n/\t/' # number each line of a file (number on left, right-aligned) sed = filename | sed 'N; s/^/ /; s/ *\(.\{6,\}\)\n/\1 /' # number each line of file, but only print numbers if line is not blank sed '/./=' filename | sed '/./N; s/\n/ /' # count lines (emulates "wc -l") sed -n '$=' TEXT CONVERSION AND SUBSTITUTION: # IN UNIX ENVIRONMENT: convert DOS newlines (CR/LF) to Unix format sed 's/.$//' # assumes that all lines end with CR/LF sed 's/^M$//' # in bash/tcsh, press Ctrl-V then Ctrl-M sed 's/\x0D$//' # gsed 3.02.80, but top script is easier # IN UNIX ENVIRONMENT: convert Unix newlines (LF) to DOS format sed "s/$/`echo -e \\\r`/" # command line under ksh sed 's/$'"/`echo \\\r`/" # command line under bash sed "s/$/`echo \\\r`/" # command line under zsh sed 's/$/\r/' # gsed 3.02.80 # IN DOS ENVIRONMENT: convert Unix newlines (LF) to DOS format sed "s/$//" # method 1 sed -n p # method 2 # IN DOS ENVIRONMENT: convert DOS newlines (CR/LF) to Unix format # Can only be done with UnxUtils sed, version 4.0.7 or higher. # Cannot be done with other DOS versions of sed. Use "tr" instead. sed "s/\r//" infile >outfile # UnxUtils sed v4.0.7 or higher tr -d \r outfile # GNU tr version 1.22 or higher # delete leading whitespace (spaces, tabs) from front of each line # aligns all text flush left sed 's/^[ \t]*//' # see note on '\t' at end of file # delete trailing whitespace (spaces, tabs) from end of each line sed 's/[ \t]*$//' # see note on '\t' at end of file # delete BOTH leading and trailing whitespace from each line sed 's/^[ \t]*//;s/[ \t]*$//' # insert 5 blank spaces at beginning of each line (make page offset) sed 's/^/ /' # align all text flush right on a 79-column width sed -e :a -e 's/^.\{1,78\}$/ &/;ta' # set at 78 plus 1 space # center all text in the middle of 79-column width. In method 1, # spaces at the beginning of the line are significant, and trailing # spaces are appended at the end of the line. In method 2, spaces at # the beginning of the line are discarded in centering the line, and # no trailing spaces appear at the end of lines. sed -e :a -e 's/^.\{1,77\}$/ & /;ta' # method 1 sed -e :a -e 's/^.\{1,77\}$/ &/;ta' -e 's/\( *\)\1/\1/' # method 2 # substitute (find and replace) "foo" with "bar" on each line sed 's/foo/bar/' # replaces only 1st instance in a line sed 's/foo/bar/4' # replaces only 4th instance in a line sed 's/foo/bar/g' # replaces ALL instances in a line sed 's/\(.*\)foo\(.*foo\)/\1bar\2/' # replace the next-to-last case sed 's/\(.*\)foo/\1bar/' # replace only the last case # substitute "foo" with "bar" ONLY for lines which contain "baz" sed '/baz/s/foo/bar/g' # substitute "foo" with "bar" EXCEPT for lines which contain "baz" sed '/baz/!s/foo/bar/g' # change "scarlet" or "ruby" or "puce" to "red" sed 's/scarlet/red/g;s/ruby/red/g;s/puce/red/g' # most seds gsed 's/scarlet\|ruby\|puce/red/g' # GNU sed only # reverse order of lines (emulates "tac") # bug/feature in HHsed v1.5 causes blank lines to be deleted sed '1!G;h;$!d' # method 1 sed -n '1!G;h;$p' # method 2 # reverse each character on the line (emulates "rev") sed '/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D;s/.//' # join pairs of lines side-by-side (like "paste") sed '$!N;s/\n/ /' # if a line ends with a backslash, append the next line to it sed -e :a -e '/\\$/N; s/\\\n//; ta' # if a line begins with an equal sign, append it to the previous line # and replace the "=" with a single space sed -e :a -e '$!N;s/\n=/ /;ta' -e 'P;D' # add commas to numeric strings, changing "1234567" to "1,234,567" gsed ':a;s/\B[0-9]\{3\}\>/,&/;ta' # GNU sed sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/;ta' # other seds # add commas to numbers with decimal points and minus signs (GNU sed) gsed ':a;s/\(^\|[^0-9.]\)\([0-9]\+\)\([0-9]\{3\}\)/\1\2,\3/g;ta' # add a blank line every 5 lines (after lines 5, 10, 15, 20, etc.) gsed '0~5G' # GNU sed only sed 'n;n;n;n;G;' # other seds SELECTIVE PRINTING OF CERTAIN LINES: # print first 10 lines of file (emulates behavior of "head") sed 10q # print first line of file (emulates "head -1") sed q # print the last 10 lines of a file (emulates "tail") sed -e :a -e '$q;N;11,$D;ba' # print the last 2 lines of a file (emulates "tail -2") sed '$!N;$!D' # print the last line of a file (emulates "tail -1") sed '$!d' # method 1 sed -n '$p' # method 2 # print only lines which match regular expression (emulates "grep") sed -n '/regexp/p' # method 1 sed '/regexp/!d' # method 2 # print only lines which do NOT match regexp (emulates "grep -v") sed -n '/regexp/!p' # method 1, corresponds to above sed '/regexp/d' # method 2, simpler syntax # print the line immediately before a regexp, but not the line # containing the regexp sed -n '/regexp/{g;1!p;};h' # print the line immediately after a regexp, but not the line # containing the regexp sed -n '/regexp/{n;p;}' # print 1 line of context before and after regexp, with line number # indicating where the regexp occurred (similar to "grep -A1 -B1") sed -n -e '/regexp/{=;x;1!p;g;$!N;p;D;}' -e h # grep for AAA and BBB and CCC (in any order) sed '/AAA/!d; /BBB/!d; /CCC/!d' # grep for AAA and BBB and CCC (in that order) sed '/AAA.*BBB.*CCC/!d' # grep for AAA or BBB or CCC (emulates "egrep") sed -e '/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d # most seds gsed '/AAA\|BBB\|CCC/!d' # GNU sed only # print paragraph if it contains AAA (blank lines separate paragraphs) # HHsed v1.5 must insert a 'G;' after 'x;' in the next 3 scripts below sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;' # print paragraph if it contains AAA and BBB and CCC (in any order) sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;/BBB/!d;/CCC/!d' # print paragraph if it contains AAA or BBB or CCC sed -e '/./{H;$!d;}' -e 'x;/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d gsed '/./{H;$!d;};x;/AAA\|BBB\|CCC/b;d' # GNU sed only # print only lines of 65 characters or longer sed -n '/^.\{65\}/p' # print only lines of less than 65 characters sed -n '/^.\{65\}/!p' # method 1, corresponds to above sed '/^.\{65\}/d' # method 2, simpler syntax # print section of file from regular expression to end of file sed -n '/regexp/,$p' # print section of file based on line numbers (lines 8-12, inclusive) sed -n '8,12p' # method 1 sed '8,12!d' # method 2 # print line number 52 sed -n '52p' # method 1 sed '52!d' # method 2 sed '52q;d' # method 3, efficient on large files # beginning at line 3, print every 7th line gsed -n '3~7p' # GNU sed only sed -n '3,${p;n;n;n;n;n;n;}' # other seds # print section of file between two regular expressions (inclusive) sed -n '/Iowa/,/Montana/p' # case sensitive SELECTIVE DELETION OF CERTAIN LINES: # print all of file EXCEPT section between 2 regular expressions sed '/Iowa/,/Montana/d' # delete duplicate, consecutive lines from a file (emulates "uniq"). # First line in a set of duplicate lines is kept, rest are deleted. sed '$!N; /^\(.*\)\n\1$/!P; D' # delete duplicate, nonconsecutive lines from a file. Beware not to # overflow the buffer size of the hold space, or else use GNU sed. sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P' # delete all lines except duplicate lines (emulates "uniq -d"). sed '$!N; s/^\(.*\)\n\1$/\1/; t; D' # delete the first 10 lines of a file sed '1,10d' # delete the last line of a file sed '$d' # delete the last 2 lines of a file sed 'N;$!P;$!D;$d' # delete the last 10 lines of a file sed -e :a -e '$d;N;2,10ba' -e 'P;D' # method 1 sed -n -e :a -e '1,10!{P;N;D;};N;ba' # method 2 # delete every 8th line gsed '0~8d' # GNU sed only sed 'n;n;n;n;n;n;n;d;' # other seds # delete ALL blank lines from a file (same as "grep '.' ") sed '/^$/d' # method 1 sed '/./!d' # method 2 # delete all CONSECUTIVE blank lines from file except the first; also # deletes all blank lines from top and end of file (emulates "cat -s") sed '/./,/^$/!d' # method 1, allows 0 blanks at top, 1 at EOF sed '/^$/N;/\n$/D' # method 2, allows 1 blank at top, 0 at EOF # delete all CONSECUTIVE blank lines from file except the first 2: sed '/^$/N;/\n$/N;//D' # delete all leading blank lines at top of file sed '/./,$!d' # delete all trailing blank lines at end of file sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' # works on all seds sed -e :a -e '/^\n*$/N;/\n$/ba' # ditto, except for gsed 3.02* # delete the last line of each paragraph sed -n '/^$/{p;h;};/./{x;/./p;}' SPECIAL APPLICATIONS: # remove nroff overstrikes (char, backspace) from man pages. The 'echo' # command may need an -e switch if you use Unix System V or bash shell. sed "s/.`echo \\\b`//g" # double quotes required for Unix environment sed 's/.^H//g' # in bash/tcsh, press Ctrl-V and then Ctrl-H sed 's/.\x08//g' # hex expression for sed v1.5 # get Usenet/e-mail message header sed '/^$/q' # deletes everything after first blank line # get Usenet/e-mail message body sed '1,/^$/d' # deletes everything up to first blank line # get Subject header, but remove initial "Subject: " portion sed '/^Subject: */!d; s///;q' # get return address header sed '/^Reply-To:/q; /^From:/h; /./d;g;q' # parse out the address proper. Pulls out the e-mail address by itself # from the 1-line return address header (see preceding script) sed 's/ *(.*)//; s/>.*//; s/.*[:<] *//' # add a leading angle bracket and space to each line (quote a message) sed 's/^/> /' # delete leading angle bracket & space from each line (unquote a message) sed 's/^> //' # remove most HTML tags (accommodates multiple-line tags) sed -e :a -e 's/<[^>]*>//g;/zipup.bat dir /b *.txt | sed "s/^\(.*\)\.TXT/pkzip -mo \1 \1.TXT/" >>zipup.bat TYPICAL USE: Sed takes one or more editing commands and applies all of them, in sequence, to each line of input. After all the commands have been applied to the first input line, that line is output and a second input line is taken for processing, and the cycle repeats. The preceding examples assume that input comes from the standard input device (i.e, the console, normally this will be piped input). One or more filenames can be appended to the command line if the input does not come from stdin. Output is sent to stdout (the screen). Thus: cat filename | sed '10q' # uses piped input sed '10q' filename # same effect, avoids a useless "cat" sed '10q' filename > newfile # redirects output to disk For additional syntax instructions, including the way to apply editing commands from a disk file instead of the command line, consult "sed & awk, 2nd Edition," by Dale Dougherty and Arnold Robbins (O'Reilly, 1997; http://www.ora.com), "UNIX Text Processing," by Dale Dougherty and Tim O'Reilly (Hayden Books, 1987) or the tutorials by Mike Arst distributed in U-SEDIT2.ZIP (many sites). To fully exploit the power of sed, one must understand "regular expressions." For this, see "Mastering Regular Expressions" by Jeffrey Friedl (O'Reilly, 1997). The manual ("man") pages on Unix systems may be helpful (try "man sed", "man regexp", or the subsection on regular expressions in "man ed"), but man pages are notoriously difficult. They are not written to teach sed use or regexps to first-time users, but as a reference text for those already acquainted with these tools. QUOTING SYNTAX: The preceding examples use single quotes ('...') instead of double quotes ("...") to enclose editing commands, since sed is typically used on a Unix platform. Single quotes prevent the Unix shell from intrepreting the dollar sign ($) and backquotes (`...`), which are expanded by the shell if they are enclosed in double quotes. Users of the "csh" shell and derivatives will also need to quote the exclamation mark (!) with the backslash (i.e., \!) to properly run the examples listed above, even within single quotes. Versions of sed written for DOS invariably require double quotes ("...") instead of single quotes to enclose editing commands. USE OF '\t' IN SED SCRIPTS: For clarity in documentation, we have used the expression '\t' to indicate a tab character (0x09) in the scripts. However, most versions of sed do not recognize the '\t' abbreviation, so when typing these scripts from the command line, you should press the TAB key instead. '\t' is supported as a regular expression metacharacter in awk, perl, and HHsed, sedmod, and GNU sed v3.02.80. VERSIONS OF SED: Versions of sed do differ, and some slight syntax variation is to be expected. In particular, most do not support the use of labels (:name) or branch instructions (b,t) within editing commands, except at the end of those commands. We have used the syntax which will be portable to most users of sed, even though the popular GNU versions of sed allow a more succinct syntax. When the reader sees a fairly long command such as this: sed -e '/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d it is heartening to know that GNU sed will let you reduce it to: sed '/AAA/b;/BBB/b;/CCC/b;d' # or even sed '/AAA\|BBB\|CCC/b;d' In addition, remember that while many versions of sed accept a command like "/one/ s/RE1/RE2/", some do NOT allow "/one/! s/RE1/RE2/", which contains space before the 's'. Omit the space when typing the command. OPTIMIZING FOR SPEED: If execution speed needs to be increased (due to large input files or slow processors or hard disks), substitution will be executed more quickly if the "find" expression is specified before giving the "s/.../.../" instruction. Thus: sed 's/foo/bar/g' filename # standard replace command sed '/foo/ s/foo/bar/g' filename # executes more quickly sed '/foo/ s//bar/g' filename # shorthand sed syntax On line selection or deletion in which you only need to output lines from the first part of the file, a "quit" command (q) in the script will drastically reduce processing time for large files. Thus: sed -n '45,50p' filename # print line nos. 45-50 of a file sed -n '51q;45,50p' filename # same, but executes much faster If you have any additional scripts to contribute or if you find errors in this document, please send e-mail to the compiler. Indicate the version of sed you used, the operating system it was compiled for, and the nature of the problem. Various scripts in this file were written or contributed by: Al Aab # "seders" list moderator Edgar Allen # various Yiorgos Adamopoulos Dale Dougherty # author of "sed & awk" Carlos Duarte # author of "do it with sed" Eric Pement # author of this document Ken Pizzini # author of GNU sed v3.02 S.G. Ravenhall # great de-html script Greg Ubben # many contributions & much help ------------------------------------------------------------------------- busybox-1.22.1/editors/diff.c0000644000000000000000000007404212263563520014526 0ustar rootroot/* vi: set sw=4 ts=4: */ /* * Mini diff implementation for busybox, adapted from OpenBSD diff. * * Copyright (C) 2010 by Matheus Izvekov * Copyright (C) 2006 by Robert Sullivan * Copyright (c) 2003 Todd C. Miller * * Sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F39502-99-1-0512. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* * The following code uses an algorithm due to Harold Stone, * which finds a pair of longest identical subsequences in * the two files. * * The major goal is to generate the match vector J. * J[i] is the index of the line in file1 corresponding * to line i in file0. J[i] = 0 if there is no * such line in file1. * * Lines are hashed so as to work in core. All potential * matches are located by sorting the lines of each file * on the hash (called "value"). In particular, this * collects the equivalence classes in file1 together. * Subroutine equiv replaces the value of each line in * file0 by the index of the first element of its * matching equivalence in (the reordered) file1. * To save space equiv squeezes file1 into a single * array member in which the equivalence classes * are simply concatenated, except that their first * members are flagged by changing sign. * * Next the indices that point into member are unsorted into * array class according to the original order of file0. * * The cleverness lies in routine stone. This marches * through the lines of file0, developing a vector klist * of "k-candidates". At step i a k-candidate is a matched * pair of lines x,y (x in file0, y in file1) such that * there is a common subsequence of length k * between the first i lines of file0 and the first y * lines of file1, but there is no such subsequence for * any smaller y. x is the earliest possible mate to y * that occurs in such a subsequence. * * Whenever any of the members of the equivalence class of * lines in file1 matable to a line in file0 has serial number * less than the y of some k-candidate, that k-candidate * with the smallest such y is replaced. The new * k-candidate is chained (via pred) to the current * k-1 candidate so that the actual subsequence can * be recovered. When a member has serial number greater * that the y of all k-candidates, the klist is extended. * At the end, the longest subsequence is pulled out * and placed in the array J by unravel * * With J in hand, the matches there recorded are * checked against reality to assure that no spurious * matches have crept in due to hashing. If they have, * they are broken, and "jackpot" is recorded--a harmless * matter except that a true match for a spuriously * mated line may now be unnecessarily reported as a change. * * Much of the complexity of the program comes simply * from trying to minimize core utilization and * maximize the range of doable problems by dynamically * allocating what is needed and reusing what is not. * The core requirements for problems larger than somewhat * are (in words) 2*length(file0) + length(file1) + * 3*(number of k-candidates installed), typically about * 6n words for files of length n. */ //config:config DIFF //config: bool "diff" //config: default y //config: help //config: diff compares two files or directories and outputs the //config: differences between them in a form that can be given to //config: the patch command. //config: //config:config FEATURE_DIFF_LONG_OPTIONS //config: bool "Enable long options" //config: default y //config: depends on DIFF && LONG_OPTS //config: help //config: Enable use of long options. //config: //config:config FEATURE_DIFF_DIR //config: bool "Enable directory support" //config: default y //config: depends on DIFF //config: help //config: This option enables support for directory and subdirectory //config: comparison. //kbuild:lib-$(CONFIG_DIFF) += diff.o //applet:IF_DIFF(APPLET(diff, BB_DIR_USR_BIN, BB_SUID_DROP)) //usage:#define diff_trivial_usage //usage: "[-abBdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] FILE1 FILE2" //usage:#define diff_full_usage "\n\n" //usage: "Compare files line by line and output the differences between them.\n" //usage: "This implementation supports unified diffs only.\n" //usage: "\n -a Treat all files as text" //usage: "\n -b Ignore changes in the amount of whitespace" //usage: "\n -B Ignore changes whose lines are all blank" //usage: "\n -d Try hard to find a smaller set of changes" //usage: "\n -i Ignore case differences" //usage: "\n -L Use LABEL instead of the filename in the unified header" //usage: "\n -N Treat absent files as empty" //usage: "\n -q Output only whether files differ" //usage: "\n -r Recurse" //usage: "\n -S Start with FILE when comparing directories" //usage: "\n -T Make tabs line up by prefixing a tab when necessary" //usage: "\n -s Report when two files are the same" //usage: "\n -t Expand tabs to spaces in output" //usage: "\n -U Output LINES lines of context" //usage: "\n -w Ignore all whitespace" #include "libbb.h" #if 0 # define dbg_error_msg(...) bb_error_msg(__VA_ARGS__) #else # define dbg_error_msg(...) ((void)0) #endif enum { /* print_status() and diffreg() return values */ STATUS_SAME, /* files are the same */ STATUS_DIFFER, /* files differ */ STATUS_BINARY, /* binary files differ */ }; enum { /* Commandline flags */ FLAG_a, FLAG_b, FLAG_d, FLAG_i, FLAG_L, /* never used, handled by getopt32 */ FLAG_N, FLAG_q, FLAG_r, FLAG_s, FLAG_S, /* never used, handled by getopt32 */ FLAG_t, FLAG_T, FLAG_U, /* never used, handled by getopt32 */ FLAG_w, FLAG_u, /* ignored, this is the default */ FLAG_p, /* not implemented */ FLAG_B, FLAG_E, /* not implemented */ }; #define FLAG(x) (1 << FLAG_##x) /* We cache file position to avoid excessive seeking */ typedef struct FILE_and_pos_t { FILE *ft_fp; off_t ft_pos; } FILE_and_pos_t; struct globals { smallint exit_status; int opt_U_context; const char *other_dir; char *label[2]; struct stat stb[2]; }; #define G (*ptr_to_globals) #define exit_status (G.exit_status ) #define opt_U_context (G.opt_U_context ) #define label (G.label ) #define stb (G.stb ) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ opt_U_context = 3; \ } while (0) typedef int token_t; enum { /* Public */ TOK_EMPTY = 1 << 9, /* Line fully processed, you can proceed to the next */ TOK_EOF = 1 << 10, /* File ended */ /* Private (Only to be used by read_token() */ TOK_EOL = 1 << 11, /* we saw EOL (sticky) */ TOK_SPACE = 1 << 12, /* used -b code, means we are skipping spaces */ SHIFT_EOF = (sizeof(token_t)*8 - 8) - 1, CHAR_MASK = 0x1ff, /* 8th bit is used to distinguish EOF from 0xff */ }; /* Restores full EOF from one 8th bit: */ //#define TOK2CHAR(t) (((t) << SHIFT_EOF) >> SHIFT_EOF) /* We don't really need the above, we only need to have EOF != any_real_char: */ #define TOK2CHAR(t) ((t) & CHAR_MASK) static void seek_ft(FILE_and_pos_t *ft, off_t pos) { if (ft->ft_pos != pos) { ft->ft_pos = pos; fseeko(ft->ft_fp, pos, SEEK_SET); } } /* Reads tokens from given fp, handling -b and -w flags * The user must reset tok every line start */ static int read_token(FILE_and_pos_t *ft, token_t tok) { tok |= TOK_EMPTY; while (!(tok & TOK_EOL)) { bool is_space; int t; t = fgetc(ft->ft_fp); if (t != EOF) ft->ft_pos++; is_space = (t == EOF || isspace(t)); /* If t == EOF (-1), set both TOK_EOF and TOK_EOL */ tok |= (t & (TOK_EOF + TOK_EOL)); /* Only EOL? */ if (t == '\n') tok |= TOK_EOL; if (option_mask32 & FLAG(i)) /* Handcoded tolower() */ t = (t >= 'A' && t <= 'Z') ? t - ('A' - 'a') : t; if ((option_mask32 & FLAG(w)) && is_space) continue; /* Trim char value to low 9 bits */ t &= CHAR_MASK; if (option_mask32 & FLAG(b)) { /* Was prev char whitespace? */ if (tok & TOK_SPACE) { /* yes */ if (is_space) /* this one too, ignore it */ continue; tok &= ~TOK_SPACE; } else if (is_space) { /* 1st whitespace char. * Set TOK_SPACE and replace char by ' ' */ t = TOK_SPACE + ' '; } } /* Clear EMPTY */ tok &= ~(TOK_EMPTY + CHAR_MASK); /* Assign char value (low 9 bits) and maybe set TOK_SPACE */ tok |= t; break; } #if 0 bb_error_msg("fp:%p tok:%x '%c'%s%s%s%s", fp, tok, tok & 0xff , tok & TOK_EOF ? " EOF" : "" , tok & TOK_EOL ? " EOL" : "" , tok & TOK_EMPTY ? " EMPTY" : "" , tok & TOK_SPACE ? " SPACE" : "" ); #endif return tok; } struct cand { int x; int y; int pred; }; static int search(const int *c, int k, int y, const struct cand *list) { int i, j; if (list[c[k]].y < y) /* quick look for typical case */ return k + 1; for (i = 0, j = k + 1;;) { const int l = (i + j) >> 1; if (l > i) { const int t = list[c[l]].y; if (t > y) j = l; else if (t < y) i = l; else return l; } else return l + 1; } } static unsigned isqrt(unsigned n) { unsigned x = 1; while (1) { const unsigned y = x; x = ((n / x) + x) >> 1; if (x <= (y + 1) && x >= (y - 1)) return x; } } static void stone(const int *a, int n, const int *b, int *J, int pref) { const unsigned isq = isqrt(n); const unsigned bound = (option_mask32 & FLAG(d)) ? UINT_MAX : MAX(256, isq); int clen = 1; int clistlen = 100; int k = 0; struct cand *clist = xzalloc(clistlen * sizeof(clist[0])); struct cand cand; struct cand *q; int *klist = xzalloc((n + 2) * sizeof(klist[0])); /*clist[0] = (struct cand){0}; - xzalloc did it */ /*klist[0] = 0; */ for (cand.x = 1; cand.x <= n; cand.x++) { int j = a[cand.x], oldl = 0; unsigned numtries = 0; if (j == 0) continue; cand.y = -b[j]; cand.pred = klist[0]; do { int l, tc; if (cand.y <= clist[cand.pred].y) continue; l = search(klist, k, cand.y, clist); if (l != oldl + 1) cand.pred = klist[l - 1]; if (l <= k && clist[klist[l]].y <= cand.y) continue; if (clen == clistlen) { clistlen = clistlen * 11 / 10; clist = xrealloc(clist, clistlen * sizeof(clist[0])); } clist[clen] = cand; tc = klist[l]; klist[l] = clen++; if (l <= k) { cand.pred = tc; oldl = l; numtries++; } else { k++; break; } } while ((cand.y = b[++j]) > 0 && numtries < bound); } /* Unravel */ for (q = clist + klist[k]; q->y; q = clist + q->pred) J[q->x + pref] = q->y + pref; free(klist); free(clist); } struct line { /* 'serial' is not used in the begining, so we reuse it * to store line offsets, thus reducing memory pressure */ union { unsigned serial; off_t offset; }; unsigned value; }; static void equiv(struct line *a, int n, struct line *b, int m, int *c) { int i = 1, j = 1; while (i <= n && j <= m) { if (a[i].value < b[j].value) a[i++].value = 0; else if (a[i].value == b[j].value) a[i++].value = j; else j++; } while (i <= n) a[i++].value = 0; b[m + 1].value = 0; j = 0; while (++j <= m) { c[j] = -b[j].serial; while (b[j + 1].value == b[j].value) { j++; c[j] = b[j].serial; } } c[j] = -1; } static void unsort(const struct line *f, int l, int *b) { int i; int *a = xmalloc((l + 1) * sizeof(a[0])); for (i = 1; i <= l; i++) a[f[i].serial] = f[i].value; for (i = 1; i <= l; i++) b[i] = a[i]; free(a); } static int line_compar(const void *a, const void *b) { #define l0 ((const struct line*)a) #define l1 ((const struct line*)b) int r = l0->value - l1->value; if (r) return r; return l0->serial - l1->serial; #undef l0 #undef l1 } static void fetch(FILE_and_pos_t *ft, const off_t *ix, int a, int b, int ch) { int i, j, col; for (i = a; i <= b; i++) { seek_ft(ft, ix[i - 1]); putchar(ch); if (option_mask32 & FLAG(T)) putchar('\t'); for (j = 0, col = 0; j < ix[i] - ix[i - 1]; j++) { int c = fgetc(ft->ft_fp); if (c == EOF) { printf("\n\\ No newline at end of file\n"); return; } ft->ft_pos++; if (c == '\t' && (option_mask32 & FLAG(t))) do putchar(' '); while (++col & 7); else { putchar(c); col++; } } } } /* Creates the match vector J, where J[i] is the index * of the line in the new file corresponding to the line i * in the old file. Lines start at 1 instead of 0, that value * being used instead to denote no corresponding line. * This vector is dynamically allocated and must be freed by the caller. * * * fp is an input parameter, where fp[0] and fp[1] are the open * old file and new file respectively. * * nlen is an output variable, where nlen[0] and nlen[1] * gets the number of lines in the old and new file respectively. * * ix is an output variable, where ix[0] and ix[1] gets * assigned dynamically allocated vectors of the offsets of the lines * of the old and new file respectively. These must be freed by the caller. */ static NOINLINE int *create_J(FILE_and_pos_t ft[2], int nlen[2], off_t *ix[2]) { int *J, slen[2], *class, *member; struct line *nfile[2], *sfile[2]; int pref = 0, suff = 0, i, j, delta; /* Lines of both files are hashed, and in the process * their offsets are stored in the array ix[fileno] * where fileno == 0 points to the old file, and * fileno == 1 points to the new one. */ for (i = 0; i < 2; i++) { unsigned hash; token_t tok; size_t sz = 100; nfile[i] = xmalloc((sz + 3) * sizeof(nfile[i][0])); /* ft gets here without the correct position, cant use seek_ft */ ft[i].ft_pos = 0; fseeko(ft[i].ft_fp, 0, SEEK_SET); nlen[i] = 0; /* We could zalloc nfile, but then zalloc starts showing in gprof at ~1% */ nfile[i][0].offset = 0; goto start; /* saves code */ while (1) { tok = read_token(&ft[i], tok); if (!(tok & TOK_EMPTY)) { /* Hash algorithm taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578. */ /*hash = hash * 128 - hash + TOK2CHAR(tok); * gcc insists on optimizing above to "hash * 127 + ...", thus... */ unsigned o = hash - TOK2CHAR(tok); hash = hash * 128 - o; /* we want SPEED here */ continue; } if (nlen[i]++ == sz) { sz = sz * 3 / 2; nfile[i] = xrealloc(nfile[i], (sz + 3) * sizeof(nfile[i][0])); } /* line_compar needs hashes fit into positive int */ nfile[i][nlen[i]].value = hash & INT_MAX; /* like ftello(ft[i].ft_fp) but faster (avoids lseek syscall) */ nfile[i][nlen[i]].offset = ft[i].ft_pos; if (tok & TOK_EOF) { /* EOF counts as a token, so we have to adjust it here */ nfile[i][nlen[i]].offset++; break; } start: hash = tok = 0; } /* Exclude lone EOF line from the end of the file, to make fetch()'s job easier */ if (nfile[i][nlen[i]].offset - nfile[i][nlen[i] - 1].offset == 1) nlen[i]--; /* Now we copy the line offsets into ix */ ix[i] = xmalloc((nlen[i] + 2) * sizeof(ix[i][0])); for (j = 0; j < nlen[i] + 1; j++) ix[i][j] = nfile[i][j].offset; } /* length of prefix and suffix is calculated */ for (; pref < nlen[0] && pref < nlen[1] && nfile[0][pref + 1].value == nfile[1][pref + 1].value; pref++); for (; suff < nlen[0] - pref && suff < nlen[1] - pref && nfile[0][nlen[0] - suff].value == nfile[1][nlen[1] - suff].value; suff++); /* Arrays are pruned by the suffix and prefix length, * the result being sorted and stored in sfile[fileno], * and their sizes are stored in slen[fileno] */ for (j = 0; j < 2; j++) { sfile[j] = nfile[j] + pref; slen[j] = nlen[j] - pref - suff; for (i = 0; i <= slen[j]; i++) sfile[j][i].serial = i; qsort(sfile[j] + 1, slen[j], sizeof(*sfile[j]), line_compar); } /* nfile arrays are reused to reduce memory pressure * The #if zeroed out section performs the same task as the * one in the #else section. * Peak memory usage is higher, but one array copy is avoided * by not using unsort() */ #if 0 member = xmalloc((slen[1] + 2) * sizeof(member[0])); equiv(sfile[0], slen[0], sfile[1], slen[1], member); free(nfile[1]); class = xmalloc((slen[0] + 1) * sizeof(class[0])); for (i = 1; i <= slen[0]; i++) /* Unsorting */ class[sfile[0][i].serial] = sfile[0][i].value; free(nfile[0]); #else member = (int *)nfile[1]; equiv(sfile[0], slen[0], sfile[1], slen[1], member); member = xrealloc(member, (slen[1] + 2) * sizeof(member[0])); class = (int *)nfile[0]; unsort(sfile[0], slen[0], (int *)nfile[0]); class = xrealloc(class, (slen[0] + 2) * sizeof(class[0])); #endif J = xmalloc((nlen[0] + 2) * sizeof(J[0])); /* The elements of J which fall inside the prefix and suffix regions * are marked as unchanged, while the ones which fall outside * are initialized with 0 (no matches), so that function stone can * then assign them their right values */ for (i = 0, delta = nlen[1] - nlen[0]; i <= nlen[0]; i++) J[i] = i <= pref ? i : i > (nlen[0] - suff) ? (i + delta) : 0; /* Here the magic is performed */ stone(class, slen[0], member, J, pref); J[nlen[0] + 1] = nlen[1] + 1; free(class); free(member); /* Both files are rescanned, in an effort to find any lines * which, due to limitations intrinsic to any hashing algorithm, * are different but ended up confounded as the same */ for (i = 1; i <= nlen[0]; i++) { if (!J[i]) continue; seek_ft(&ft[0], ix[0][i - 1]); seek_ft(&ft[1], ix[1][J[i] - 1]); for (j = J[i]; i <= nlen[0] && J[i] == j; i++, j++) { token_t tok0 = 0, tok1 = 0; do { tok0 = read_token(&ft[0], tok0); tok1 = read_token(&ft[1], tok1); if (((tok0 ^ tok1) & TOK_EMPTY) != 0 /* one is empty (not both) */ || (!(tok0 & TOK_EMPTY) && TOK2CHAR(tok0) != TOK2CHAR(tok1)) ) { J[i] = 0; /* Break the correspondence */ } } while (!(tok0 & tok1 & TOK_EMPTY)); } } return J; } static bool diff(FILE* fp[2], char *file[2]) { int nlen[2]; off_t *ix[2]; FILE_and_pos_t ft[2]; typedef struct { int a, b; } vec_t[2]; vec_t *vec = NULL; int i = 1, j, k, idx = -1; bool anychange = false; int *J; ft[0].ft_fp = fp[0]; ft[1].ft_fp = fp[1]; /* note that ft[i].ft_pos is unintitalized, create_J() * must not assume otherwise */ J = create_J(ft, nlen, ix); do { bool nonempty = false; while (1) { vec_t v; for (v[0].a = i; v[0].a <= nlen[0] && J[v[0].a] == J[v[0].a - 1] + 1; v[0].a++) continue; v[1].a = J[v[0].a - 1] + 1; for (v[0].b = v[0].a - 1; v[0].b < nlen[0] && !J[v[0].b + 1]; v[0].b++) continue; v[1].b = J[v[0].b + 1] - 1; /* * Indicate that there is a difference between lines a and b of the 'from' file * to get to lines c to d of the 'to' file. If a is greater than b then there * are no lines in the 'from' file involved and this means that there were * lines appended (beginning at b). If c is greater than d then there are * lines missing from the 'to' file. */ if (v[0].a <= v[0].b || v[1].a <= v[1].b) { /* * If this change is more than 'context' lines from the * previous change, dump the record and reset it. */ int ct = (2 * opt_U_context) + 1; if (idx >= 0 && v[0].a > vec[idx][0].b + ct && v[1].a > vec[idx][1].b + ct ) { break; } for (j = 0; j < 2; j++) for (k = v[j].a; k < v[j].b; k++) nonempty |= (ix[j][k+1] - ix[j][k] != 1); vec = xrealloc_vector(vec, 6, ++idx); memcpy(vec[idx], v, sizeof(v)); } i = v[0].b + 1; if (i > nlen[0]) break; J[v[0].b] = v[1].b; } if (idx < 0 || ((option_mask32 & FLAG(B)) && !nonempty)) goto cont; if (!(option_mask32 & FLAG(q))) { int lowa; vec_t span, *cvp = vec; if (!anychange) { /* Print the context/unidiff header first time through */ printf("--- %s\n", label[0] ? label[0] : file[0]); printf("+++ %s\n", label[1] ? label[1] : file[1]); } printf("@@"); for (j = 0; j < 2; j++) { int a = span[j].a = MAX(1, (*cvp)[j].a - opt_U_context); int b = span[j].b = MIN(nlen[j], vec[idx][j].b + opt_U_context); printf(" %c%d", j ? '+' : '-', MIN(a, b)); if (a == b) continue; printf(",%d", (a < b) ? b - a + 1 : 0); } printf(" @@\n"); /* * Output changes in "unified" diff format--the old and new lines * are printed together. */ for (lowa = span[0].a; ; lowa = (*cvp++)[0].b + 1) { bool end = cvp > &vec[idx]; fetch(&ft[0], ix[0], lowa, end ? span[0].b : (*cvp)[0].a - 1, ' '); if (end) break; for (j = 0; j < 2; j++) fetch(&ft[j], ix[j], (*cvp)[j].a, (*cvp)[j].b, j ? '+' : '-'); } } anychange = true; cont: idx = -1; } while (i <= nlen[0]); free(vec); free(ix[0]); free(ix[1]); free(J); return anychange; } static int diffreg(char *file[2]) { FILE *fp[2]; bool binary = false, differ = false; int status = STATUS_SAME, i; fp[0] = stdin; fp[1] = stdin; for (i = 0; i < 2; i++) { int fd = open_or_warn_stdin(file[i]); if (fd == -1) goto out; /* Our diff implementation is using seek. * When we meet non-seekable file, we must make a temp copy. */ if (lseek(fd, 0, SEEK_SET) == -1 && errno == ESPIPE) { char name[] = "/tmp/difXXXXXX"; int fd_tmp = xmkstemp(name); unlink(name); if (bb_copyfd_eof(fd, fd_tmp) < 0) xfunc_die(); if (fd) /* Prevents closing of stdin */ close(fd); fd = fd_tmp; } fp[i] = fdopen(fd, "r"); } while (1) { const size_t sz = COMMON_BUFSIZE / 2; char *const buf0 = bb_common_bufsiz1; char *const buf1 = buf0 + sz; int j, k; i = fread(buf0, 1, sz, fp[0]); j = fread(buf1, 1, sz, fp[1]); if (i != j) { differ = true; i = MIN(i, j); } if (i == 0) break; for (k = 0; k < i; k++) { if (!buf0[k] || !buf1[k]) binary = true; if (buf0[k] != buf1[k]) differ = true; } } if (differ) { if (binary && !(option_mask32 & FLAG(a))) status = STATUS_BINARY; else if (diff(fp, file)) status = STATUS_DIFFER; } if (status != STATUS_SAME) exit_status |= 1; out: fclose_if_not_stdin(fp[0]); fclose_if_not_stdin(fp[1]); return status; } static void print_status(int status, char *path[2]) { switch (status) { case STATUS_BINARY: case STATUS_DIFFER: if ((option_mask32 & FLAG(q)) || status == STATUS_BINARY) printf("Files %s and %s differ\n", path[0], path[1]); break; case STATUS_SAME: if (option_mask32 & FLAG(s)) printf("Files %s and %s are identical\n", path[0], path[1]); break; } } #if ENABLE_FEATURE_DIFF_DIR struct dlist { size_t len; int s, e; char **dl; }; /* This function adds a filename to dl, the directory listing. */ static int FAST_FUNC add_to_dirlist(const char *filename, struct stat *sb UNUSED_PARAM, void *userdata, int depth UNUSED_PARAM) { struct dlist *const l = userdata; const char *file = filename + l->len; while (*file == '/') file++; l->dl = xrealloc_vector(l->dl, 6, l->e); l->dl[l->e] = xstrdup(file); l->e++; return TRUE; } /* If recursion is not set, this function adds the directory * to the list and prevents recursive_action from recursing into it. */ static int FAST_FUNC skip_dir(const char *filename, struct stat *sb, void *userdata, int depth) { if (!(option_mask32 & FLAG(r)) && depth) { add_to_dirlist(filename, sb, userdata, depth); return SKIP; } if (!(option_mask32 & FLAG(N))) { /* -r without -N: no need to recurse into dirs * which do not exist on the "other side". * Testcase: diff -r /tmp / * (it would recurse deep into /proc without this code) */ struct dlist *const l = userdata; filename += l->len; if (filename[0]) { struct stat osb; char *othername = concat_path_file(G.other_dir, filename); int r = stat(othername, &osb); free(othername); if (r != 0 || !S_ISDIR(osb.st_mode)) { /* other dir doesn't have similarly named * directory, don't recurse; return 1 upon * exit, just like diffutils' diff */ exit_status |= 1; return SKIP; } } } return TRUE; } static void diffdir(char *p[2], const char *s_start) { struct dlist list[2]; int i; memset(&list, 0, sizeof(list)); for (i = 0; i < 2; i++) { /*list[i].s = list[i].e = 0; - memset did it */ /*list[i].dl = NULL; */ G.other_dir = p[1 - i]; /* We need to trim root directory prefix. * Using list.len to specify its length, * add_to_dirlist will remove it. */ list[i].len = strlen(p[i]); recursive_action(p[i], ACTION_RECURSE | ACTION_FOLLOWLINKS, add_to_dirlist, skip_dir, &list[i], 0); /* Sort dl alphabetically. * GNU diff does this ignoring any number of trailing dots. * We don't, so for us dotted files almost always are * first on the list. */ qsort_string_vector(list[i].dl, list[i].e); /* If -S was set, find the starting point. */ if (!s_start) continue; while (list[i].s < list[i].e && strcmp(list[i].dl[list[i].s], s_start) < 0) list[i].s++; } /* Now that both dirlist1 and dirlist2 contain sorted directory * listings, we can start to go through dirlist1. If both listings * contain the same file, then do a normal diff. Otherwise, behaviour * is determined by whether the -N flag is set. */ while (1) { char *dp[2]; int pos; int k; dp[0] = list[0].s < list[0].e ? list[0].dl[list[0].s] : NULL; dp[1] = list[1].s < list[1].e ? list[1].dl[list[1].s] : NULL; if (!dp[0] && !dp[1]) break; pos = !dp[0] ? 1 : (!dp[1] ? -1 : strcmp(dp[0], dp[1])); k = pos > 0; if (pos && !(option_mask32 & FLAG(N))) { printf("Only in %s: %s\n", p[k], dp[k]); exit_status |= 1; } else { char *fullpath[2], *path[2]; /* if -N */ for (i = 0; i < 2; i++) { if (pos == 0 || i == k) { path[i] = fullpath[i] = concat_path_file(p[i], dp[i]); stat(fullpath[i], &stb[i]); } else { fullpath[i] = concat_path_file(p[i], dp[1 - i]); path[i] = (char *)bb_dev_null; } } if (pos) stat(fullpath[k], &stb[1 - k]); if (S_ISDIR(stb[0].st_mode) && S_ISDIR(stb[1].st_mode)) printf("Common subdirectories: %s and %s\n", fullpath[0], fullpath[1]); else if (!S_ISREG(stb[0].st_mode) && !S_ISDIR(stb[0].st_mode)) printf("File %s is not a regular file or directory and was skipped\n", fullpath[0]); else if (!S_ISREG(stb[1].st_mode) && !S_ISDIR(stb[1].st_mode)) printf("File %s is not a regular file or directory and was skipped\n", fullpath[1]); else if (S_ISDIR(stb[0].st_mode) != S_ISDIR(stb[1].st_mode)) { if (S_ISDIR(stb[0].st_mode)) printf("File %s is a %s while file %s is a %s\n", fullpath[0], "directory", fullpath[1], "regular file"); else printf("File %s is a %s while file %s is a %s\n", fullpath[0], "regular file", fullpath[1], "directory"); } else print_status(diffreg(path), fullpath); free(fullpath[0]); free(fullpath[1]); } free(dp[k]); list[k].s++; if (pos == 0) { free(dp[1 - k]); list[1 - k].s++; } } if (ENABLE_FEATURE_CLEAN_UP) { free(list[0].dl); free(list[1].dl); } } #endif #if ENABLE_FEATURE_DIFF_LONG_OPTIONS static const char diff_longopts[] ALIGN1 = "ignore-case\0" No_argument "i" "ignore-tab-expansion\0" No_argument "E" "ignore-space-change\0" No_argument "b" "ignore-all-space\0" No_argument "w" "ignore-blank-lines\0" No_argument "B" "text\0" No_argument "a" "unified\0" Required_argument "U" "label\0" Required_argument "L" "show-c-function\0" No_argument "p" "brief\0" No_argument "q" "expand-tabs\0" No_argument "t" "initial-tab\0" No_argument "T" "recursive\0" No_argument "r" "new-file\0" No_argument "N" "report-identical-files\0" No_argument "s" "starting-file\0" Required_argument "S" "minimal\0" No_argument "d" ; #endif int diff_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int diff_main(int argc UNUSED_PARAM, char **argv) { int gotstdin = 0, i; char *file[2], *s_start = NULL; llist_t *L_arg = NULL; INIT_G(); /* exactly 2 params; collect multiple -L