udhcpc: fix a problem with binary-encoded options #2
[oweals/busybox.git] / loginutils / adduser.c
index da41fd7ac766e8cc1c9167c8a777050d89253ae8..dc02444766378cd11ec395d1bc55aec3a96080d6 100644 (file)
@@ -5,8 +5,22 @@
  * Copyright (C) 1999 by Lineo, inc. and John Beppu
  * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * 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
@@ -52,6 +66,7 @@ static void passwd_study(struct passwd *p)
                }
                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++;
        }
@@ -65,24 +80,44 @@ static void passwd_study(struct passwd *p)
        }
 }
 
-static void addgroup_wrapper(struct passwd *p, const char *group_name)
+static int addgroup_wrapper(struct passwd *p, const char *group_name)
 {
-       char *cmd;
-
-       if (group_name) /* Add user to existing group */
-               cmd = xasprintf("addgroup '%s' '%s'", p->pw_name, group_name);
-       else    /* Add user to his own group with the first free gid found in passwd_study */
-               cmd = xasprintf("addgroup -g %u '%s'", (unsigned)p->pw_gid, p->pw_name);
-       /* Warning: to be compatible with external addgroup programs we should use --gid instead */
-       system(cmd);
-       free(cmd);
+       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) NORETURN;
+static void passwd_wrapper(const char *login_name) NORETURN;
 
-static void passwd_wrapper(const char *login)
+static void passwd_wrapper(const char *login_name)
 {
-       BB_EXECLP("passwd", "passwd", login, NULL);
+       BB_EXECLP("passwd", "passwd", "--", login_name, NULL);
        bb_error_msg_and_die("can't execute passwd, you must set password manually");
 }
 
@@ -123,12 +158,13 @@ int adduser_main(int argc UNUSED_PARAM, char **argv)
        }
 
        pw.pw_gecos = (char *)"Linux User,,,";
-       pw.pw_shell = (char *)DEFAULT_SHELL;
+       /* We assume that newly created users "inherit" root's shell setting */
+       pw.pw_shell = (char *)get_shell_name();
        pw.pw_dir = NULL;
 
-       /* exactly one non-option arg */
+       /* at most two non-option args */
        /* disable interactive passwd for system accounts */
-       opt_complementary = "=1:SD:u+";
+       opt_complementary = "?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 {
@@ -139,9 +175,16 @@ int adduser_main(int argc UNUSED_PARAM, char **argv)
                }
        }
        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 */
-       pw.pw_name = argv[0];
        die_if_bad_username(pw.pw_name);
        if (!pw.pw_dir) {
                /* create string for $HOME if not specified already */
@@ -169,7 +212,6 @@ int adduser_main(int argc UNUSED_PARAM, char **argv)
        }
        if (ENABLE_FEATURE_CLEAN_UP)
                free(p);
-
 #if ENABLE_FEATURE_SHADOWPASSWDS
        /* /etc/shadow fields:
         * 1. username
@@ -199,7 +241,23 @@ int adduser_main(int argc UNUSED_PARAM, char **argv)
                /* 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 */
-               if ((mkdir(pw.pw_dir, 0755) != 0 && errno != EEXIST)
+               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 */
                ) {
@@ -212,5 +270,5 @@ int adduser_main(int argc UNUSED_PARAM, char **argv)
                passwd_wrapper(pw.pw_name);
        }
 
-       return 0;
+       return EXIT_SUCCESS;
 }