ifupdown: fixes for custom MAC address, by Wade Berrier <wberrier AT gmail.com>
[oweals/busybox.git] / procps / sysctl.c
index b5a01894fbd08f13ff1fdb86f987ba924a8422cc..79f0074c99370b4e3113d56ed9ae8a29d846e3ab 100644 (file)
  *
  */
 
-#include "busybox.h"
+#include "libbb.h"
 
-/*
- *    Function Prototypes
- */
-static int sysctl_read_setting(const char *setting, int output);
-static int sysctl_write_setting(const char *setting, int output);
-static int sysctl_preload_file(const char *filename, int output);
-static int sysctl_display_all(const char *path, int output, int show_table);
+static int sysctl_read_setting(const char *setting);
+static int sysctl_write_setting(const char *setting);
+static int sysctl_display_all(const char *path);
+static int sysctl_preload_file_and_exit(const char *filename);
 
-/*
- *    Globals...
- */
-static const char PROC_PATH[] = "/proc/sys/";
-static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf";
+static const char ETC_SYSCTL_CONF[] ALIGN1 = "/etc/sysctl.conf";
+static const char PROC_SYS[] ALIGN1 = "/proc/sys/";
+enum { strlen_PROC_SYS = sizeof(PROC_SYS) - 1 };
 
 /* error messages */
-static const char ERR_UNKNOWN_PARAMETER[] = "error: Unknown parameter '%s'\n";
-static const char ERR_MALFORMED_SETTING[] = "error: Malformed setting '%s'\n";
-static const char ERR_NO_EQUALS[] =
-       "error: '%s' must be of the form name=value\n";
-static const char ERR_INVALID_KEY[] = "error: '%s' is an unknown key\n";
-static const char ERR_UNKNOWN_WRITING[] =
-       "error: unknown error %d setting key '%s'\n";
-static const char ERR_UNKNOWN_READING[] =
-       "error: unknown error %d reading key '%s'\n";
-static const char ERR_PERMISSION_DENIED[] =
-       "error: permission denied on key '%s'\n";
-static const char ERR_PRELOAD_FILE[] =
-       "error: cannot open preload file '%s'\n";
-static const char WARN_BAD_LINE[] =
-       "warning: %s(%d): invalid syntax, continuing...\n";
+static const char ERR_MALFORMED_SETTING[] ALIGN1 =
+       "error: malformed setting '%s'";
+static const char ERR_NO_EQUALS[] ALIGN1 =
+       "error: '%s' must be of the form name=value";
+static const char ERR_INVALID_KEY[] ALIGN1 =
+       "error: '%s' is an unknown key";
+static const char ERR_UNKNOWN_WRITING[] ALIGN1 =
+       "error setting key '%s'";
+static const char ERR_UNKNOWN_READING[] ALIGN1 =
+       "error reading key '%s'";
+static const char ERR_PERMISSION_DENIED[] ALIGN1 =
+       "error: permission denied on key '%s'";
+static const char WARN_BAD_LINE[] ALIGN1 =
+       "warning: %s(%d): invalid syntax, continuing";
 
 
 static void dwrite_str(int fd, const char *buf)
@@ -53,148 +47,101 @@ static void dwrite_str(int fd, const char *buf)
        write(fd, buf, strlen(buf));
 }
 
-/*
- *    sysctl_main()...
- */
-int sysctl_main(int argc, char **argv);
-int sysctl_main(int argc, char **argv)
+enum {
+       FLAG_SHOW_KEYS       = 1 << 0,
+       FLAG_SHOW_KEY_ERRORS = 1 << 1,
+       FLAG_TABLE_FORMAT    = 1 << 2, /* not implemented */
+       FLAG_SHOW_ALL        = 1 << 3,
+       FLAG_PRELOAD_FILE    = 1 << 4,
+       FLAG_WRITE           = 1 << 5,
+};
+
+int sysctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int sysctl_main(int argc UNUSED_PARAM, char **argv)
 {
-       int retval = 0;
-       int output = 1;
-       int write_mode = 0;
-       int switches_allowed = 1;
-
-       if (argc < 2)
-               bb_show_usage();
-
-       argv++;
-
-       for (; argv && *argv && **argv; argv++) {
-               if (switches_allowed && **argv == '-') {        /* we have a switch */
-                       switch ((*argv)[1]) {
-                       case 'n':
-                               output = 0;
-                               break;
-                       case 'w':
-                               write_mode = 1;
-                               switches_allowed = 0;
-                               break;
-                       case 'p':
-                               argv++;
-                               return
-                                       sysctl_preload_file(((argv && *argv
-                                                                                 && **argv) ? *argv :
-                                                                                DEFAULT_PRELOAD), output);
-                       case 'a':
-                       case 'A':
-                               switches_allowed = 0;
-                               return sysctl_display_all(PROC_PATH, output,
-                                                                                 ((*argv)[1] == 'a') ? 0 : 1);
-                       case 'h':
-                       case '?':
-                               bb_show_usage();
-                       default:
-                               bb_error_msg(ERR_UNKNOWN_PARAMETER, *argv);
-                               bb_show_usage();
-                       }
-               } else {
-                       switches_allowed = 0;
-                       if (write_mode)
-                               retval = sysctl_write_setting(*argv, output);
-                       else
-                               sysctl_read_setting(*argv, output);
-               }
+       int retval;
+       int opt;
+
+       opt = getopt32(argv, "+neAapw"); /* '+' - stop on first non-option */
+       argv += optind;
+       opt ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS);
+       option_mask32 ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS);
+
+       if (opt & (FLAG_TABLE_FORMAT | FLAG_SHOW_ALL))
+               return sysctl_display_all(PROC_SYS);
+       if (opt & FLAG_PRELOAD_FILE)
+               return sysctl_preload_file_and_exit(*argv ? *argv : ETC_SYSCTL_CONF);
+
+       retval = 0;
+       while (*argv) {
+               if (opt & FLAG_WRITE)
+                       retval |= sysctl_write_setting(*argv);
+               else
+                       retval |= sysctl_read_setting(*argv);
+               argv++;
        }
-       return retval;
-}                                              /* end sysctl_main() */
-
 
+       return retval;
+} /* end sysctl_main() */
 
 /*
- *     sysctl_preload_file
- *     preload the sysctl's from a conf file
- *           - we parse the file and then reform it (strip out whitespace)
+ * preload the sysctl's from a conf file
+ * - we parse the file and then reform it (strip out whitespace)
  */
-#define PRELOAD_BUF 256
 
-int sysctl_preload_file(const char *filename, int output)
+static int sysctl_preload_file_and_exit(const char *filename)
 {
-       int lineno = 0;
-       char oneline[PRELOAD_BUF];
-       char buffer[PRELOAD_BUF];
-       char *name, *value, *ptr;
-       FILE *fp = NULL;
-
-       if (!filename || ((fp = fopen(filename, "r")) == NULL)) {
-               bb_error_msg_and_die(ERR_PRELOAD_FILE, filename);
-       }
-
-       while (fgets(oneline, sizeof(oneline) - 1, fp)) {
-               oneline[sizeof(oneline) - 1] = '\0';
-               lineno++;
-               trim(oneline);
-               ptr = (char *) oneline;
-
-               if (*ptr == '#' || *ptr == ';')
-                       continue;
-
-               if (strlen(ptr) < 2)
-                       continue;
-
-               name = strtok(ptr, "=");
-               if (!name || !*name) {
-                       bb_error_msg(WARN_BAD_LINE, filename, lineno);
-                       continue;
-               }
-
-               trim(name);
-
-               value = strtok(NULL, "\n\r");
-               if (!value || !*value) {
-                       bb_error_msg(WARN_BAD_LINE, filename, lineno);
-                       continue;
+       char *token[2];
+       parser_t *parser;
+
+       parser = config_open(filename);
+       while (config_read(parser, token, 2, 2, "# \t=", PARSE_NORMAL)) { // TODO: ';' is comment char too
+//             if (!token[1]) {
+//                     bb_error_msg(WARN_BAD_LINE, filename, parser->lineno);
+//             } else {
+               {
+#if 0
+                       char *s = xasprintf("%s=%s", token[0], token[1]);
+                       sysctl_write_setting(s);
+                       free(s);
+#else // PLAY_WITH_FIRE for -4 bytes?
+                       sprintf(parser->line, "%s=%s", token[0], token[1]); // must have room by definition
+                       sysctl_write_setting(parser->line);
+#endif
                }
-
-               while ((*value == ' ' || *value == '\t') && *value != 0)
-                       value++;
-               /* safe because sizeof(oneline) == sizeof(buffer) */
-               sprintf(buffer, "%s=%s", name, value);
-               sysctl_write_setting(buffer, output);
        }
-       fclose(fp);
+       if (ENABLE_FEATURE_CLEAN_UP)
+               config_close(parser);
        return 0;
-}                                              /* end sysctl_preload_file() */
-
+} /* end sysctl_preload_file_and_exit() */
 
 /*
  *     Write a single sysctl setting
  */
-int sysctl_write_setting(const char *setting, int output)
+static int sysctl_write_setting(const char *setting)
 {
-       int retval = 0;
-       const char *name = setting;
+       int retval;
+       const char *name;
        const char *value;
        const char *equals;
        char *tmpname, *outname, *cptr;
-       int fd = -1;
-
-       if (!name)                      /* probably dont' want to display this  err */
-               return 0;
+       int fd;
 
-       if (!(equals = strchr(setting, '='))) {
+       name = setting;
+       equals = strchr(setting, '=');
+       if (!equals) {
                bb_error_msg(ERR_NO_EQUALS, setting);
-               return -1;
+               return EXIT_FAILURE;
        }
 
-       value = equals + sizeof(char);  /* point to the value in name=value */
-
-       if (!*name || !*value || name == equals) {
+       value = equals + 1;     /* point to the value in name=value */
+       if (name == equals || !*value) {
                bb_error_msg(ERR_MALFORMED_SETTING, setting);
-               return -2;
+               return EXIT_FAILURE;
        }
 
-       tmpname = xasprintf("%s%.*s", PROC_PATH, (int)(equals - name), name);
-       outname = xstrdup(tmpname + strlen(PROC_PATH));
+       tmpname = xasprintf("%s%.*s", PROC_SYS, (int)(equals - name), name);
+       outname = xstrdup(tmpname + strlen_PROC_SYS);
 
        while ((cptr = strchr(tmpname, '.')) != NULL)
                *cptr = '/';
@@ -202,126 +149,120 @@ int sysctl_write_setting(const char *setting, int output)
        while ((cptr = strchr(outname, '/')) != NULL)
                *cptr = '.';
 
-       if ((fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
+       fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+       if (fd < 0) {
                switch (errno) {
                case ENOENT:
-                       bb_error_msg(ERR_INVALID_KEY, outname);
+                       if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
+                               bb_error_msg(ERR_INVALID_KEY, outname);
                        break;
                case EACCES:
                        bb_perror_msg(ERR_PERMISSION_DENIED, outname);
                        break;
                default:
-                       bb_error_msg(ERR_UNKNOWN_WRITING, errno, outname);
+                       bb_perror_msg(ERR_UNKNOWN_WRITING, outname);
                        break;
                }
-               retval = -1;
+               retval = EXIT_FAILURE;
        } else {
                dwrite_str(fd, value);
                close(fd);
-               if (output) {
-                       dwrite_str(STDOUT_FILENO, outname);
-                       dwrite_str(STDOUT_FILENO, " = ");
+               if (option_mask32 & FLAG_SHOW_KEYS) {
+                       printf("%s = ", outname);
                }
-               dwrite_str(STDOUT_FILENO, value);
-               dwrite_str(STDOUT_FILENO, "\n");
+               puts(value);
+               retval = EXIT_SUCCESS;
        }
 
-       /* cleanup */
        free(tmpname);
        free(outname);
        return retval;
-}                                              /* end sysctl_write_setting() */
-
+} /* end sysctl_write_setting() */
 
 /*
  *     Read a sysctl setting
- *
  */
-int sysctl_read_setting(const char *setting, int output)
+static int sysctl_read_setting(const char *name)
 {
-       int retval = 0;
+       int retval;
        char *tmpname, *outname, *cptr;
        char inbuf[1025];
-       const char *name = setting;
        FILE *fp;
 
-       if (!setting || !*setting)
-               bb_error_msg(ERR_INVALID_KEY, setting);
+       if (!*name) {
+               if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
+                       bb_error_msg(ERR_INVALID_KEY, name);
+               return -1;
+       }
 
-       tmpname = concat_path_file(PROC_PATH, name);
-       outname = xstrdup(tmpname + strlen(PROC_PATH));
+       tmpname = concat_path_file(PROC_SYS, name);
+       outname = xstrdup(tmpname + strlen_PROC_SYS);
 
        while ((cptr = strchr(tmpname, '.')) != NULL)
                *cptr = '/';
        while ((cptr = strchr(outname, '/')) != NULL)
                *cptr = '.';
 
-       if ((fp = fopen(tmpname, "r")) == NULL) {
+       fp = fopen_for_read(tmpname);
+       if (fp == NULL) {
                switch (errno) {
                case ENOENT:
-                       bb_error_msg(ERR_INVALID_KEY, outname);
+                       if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
+                               bb_error_msg(ERR_INVALID_KEY, outname);
                        break;
                case EACCES:
                        bb_error_msg(ERR_PERMISSION_DENIED, outname);
                        break;
                default:
-                       bb_error_msg(ERR_UNKNOWN_READING, errno, outname);
+                       bb_perror_msg(ERR_UNKNOWN_READING, outname);
                        break;
                }
-               retval = -1;
+               retval = EXIT_FAILURE;
        } else {
                while (fgets(inbuf, sizeof(inbuf) - 1, fp)) {
-                       if (output) {
-                               dwrite_str(STDOUT_FILENO, outname);
-                               dwrite_str(STDOUT_FILENO, " = ");
+                       if (option_mask32 & FLAG_SHOW_KEYS) {
+                               printf("%s = ", outname);
                        }
-                       dwrite_str(STDOUT_FILENO, inbuf);
+                       fputs(inbuf, stdout);
                }
                fclose(fp);
+               retval = EXIT_SUCCESS;
        }
 
        free(tmpname);
        free(outname);
        return retval;
-}                                              /* end sysctl_read_setting() */
-
-
+} /* end sysctl_read_setting() */
 
 /*
  *     Display all the sysctl settings
- *
  */
-int sysctl_display_all(const char *path, int output, int show_table)
+static int sysctl_display_all(const char *path)
 {
        int retval = 0;
-       int retval2;
        DIR *dp;
        struct dirent *de;
        char *tmpdir;
        struct stat ts;
 
-       if (!(dp = opendir(path))) {
-               retval = -1;
-       } else {
-               while ((de = readdir(dp)) != NULL) {
-                       tmpdir = concat_subpath_file(path, de->d_name);
-                       if(tmpdir == NULL)
-                               continue;
-                       if ((retval2 = stat(tmpdir, &ts)) != 0)
-                               bb_perror_msg(tmpdir);
-                       else {
-                               if (S_ISDIR(ts.st_mode)) {
-                                       sysctl_display_all(tmpdir, output, show_table);
-                               } else
-                                       retval |=
-                                               sysctl_read_setting(tmpdir + strlen(PROC_PATH),
-                                                                                       output);
-
-                       }
-                       free(tmpdir);
-               }                               /* end while */
-               closedir(dp);
+       dp = opendir(path);
+       if (!dp) {
+               return EXIT_FAILURE;
        }
+       while ((de = readdir(dp)) != NULL) {
+               tmpdir = concat_subpath_file(path, de->d_name);
+               if (tmpdir == NULL)
+                       continue; /* . or .. */
+               if (stat(tmpdir, &ts) != 0) {
+                       bb_perror_msg(tmpdir);
+               } else if (S_ISDIR(ts.st_mode)) {
+                       retval |= sysctl_display_all(tmpdir);
+               } else {
+                       retval |= sysctl_read_setting(tmpdir + strlen_PROC_SYS);
+               }
+               free(tmpdir);
+       } /* end while */
+       closedir(dp);
 
        return retval;
-}                                              /* end sysctl_display_all() */
+} /* end sysctl_display_all() */