···11+## Name
22+33+userdel - delete a user account
44+55+## Synopsis
66+77+```**sh
88+# userdel [-r] <login>
99+```
1010+1111+## Description
1212+1313+This program deletes a user account in the system.
1414+1515+This program must be run as root.
1616+1717+## Options
1818+1919+* `-r`, `--remove`: Remove the home directory for this user if the directory exists.
2020+2121+## Exit Values
2222+2323+* 0 - Success
2424+* 1 - Couldn't update the password file
2525+* 6 - Specified user doesn't exist
2626+* 12 - Couldn't remove home directory
2727+2828+## Files
2929+3030+* `/etc/passwd` - user information (such as UID and GID) in this file is deleted.
3131+* `/home/` - user home directroy is deleted if the `-r` flag is specified.
3232+3333+## Examples
3434+3535+```sh
3636+# userdel alice
3737+# userdel -r alice
3838+# userdel --remove alice
3939+```
4040+4141+## See Also
4242+4343+* [`useradd`(8)](useradd.md)
+151
Userland/userdel.cpp
···11+/*
22+ * Copyright (c) 2019-2020, Fei Wu <f.eiwu@yahoo.com>
33+ * All rights reserved.
44+ *
55+ * Redistribution and use in source and binary forms, with or without
66+ * modification, are permitted provided that the following conditions are met:
77+ *
88+ * 1. Redistributions of source code must retain the above copyright notice, this
99+ * list of conditions and the following disclaimer.
1010+ *
1111+ * 2. Redistributions in binary form must reproduce the above copyright notice,
1212+ * this list of conditions and the following disclaimer in the documentation
1313+ * and/or other materials provided with the distribution.
1414+ *
1515+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1616+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1717+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1818+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
1919+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2020+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2121+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
2222+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2323+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2424+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2525+ */
2626+2727+#include <AK/String.h>
2828+#include <AK/StringBuilder.h>
2929+#include <LibCore/ArgsParser.h>
3030+#include <ctype.h>
3131+#include <dirent.h>
3232+#include <errno.h>
3333+#include <pwd.h>
3434+#include <stdio.h>
3535+#include <stdlib.h>
3636+#include <string.h>
3737+#include <sys/stat.h>
3838+#include <sys/types.h>
3939+#include <sys/wait.h>
4040+#include <unistd.h>
4141+4242+int main(int argc, char** argv)
4343+{
4444+ const char* username = nullptr;
4545+ bool remove_home = false;
4646+4747+ Core::ArgsParser args_parser;
4848+ args_parser.add_option(remove_home, "Remove home directory", "remove", 'r');
4949+ args_parser.add_positional_argument(username, "Login user identity (username)", "login");
5050+ args_parser.parse(argc, argv);
5151+5252+ char temp_filename[] = "/etc/passwd.XXXXXX";
5353+ auto fd = mkstemp(temp_filename);
5454+ if (fd == -1) {
5555+ perror("failed to create temporary file");
5656+ return 1;
5757+ }
5858+5959+ FILE* temp_file = fdopen(fd, "w");
6060+ if (!temp_file) {
6161+ perror("fdopen");
6262+ if (unlink(temp_filename) < 0) {
6363+ perror("unlink");
6464+ }
6565+6666+ return 1;
6767+ }
6868+6969+ bool user_exists = false;
7070+ String home_directory;
7171+7272+ int rc = 0;
7373+ setpwent();
7474+ for (auto* pw = getpwent(); pw; pw = getpwent()) {
7575+ if (strcmp(pw->pw_name, username)) {
7676+ if (putpwent(pw, temp_file) != 0) {
7777+ perror("failed to put an entry in the temporary passwd file");
7878+ rc = 1;
7979+ break;
8080+ }
8181+ } else {
8282+ user_exists = true;
8383+ if (remove_home)
8484+ home_directory = pw->pw_dir;
8585+ }
8686+ }
8787+ endpwent();
8888+8989+ if (fclose(temp_file)) {
9090+ perror("fclose");
9191+ if (!rc)
9292+ rc = 1;
9393+ }
9494+9595+ if (rc == 0 && !user_exists) {
9696+ fprintf(stderr, "specified user doesn't exist\n");
9797+ rc = 6;
9898+ }
9999+100100+ if (rc == 0 && chmod(temp_filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) {
101101+ perror("chmod");
102102+ rc = 1;
103103+ }
104104+105105+ if (rc == 0 && rename(temp_filename, "/etc/passwd") < 0) {
106106+ perror("failed to rename the temporary passwd file");
107107+ rc = 1;
108108+ }
109109+110110+ if (rc) {
111111+ if (unlink(temp_filename) < 0) {
112112+ perror("unlink");
113113+ }
114114+ return rc;
115115+ }
116116+117117+ if (remove_home) {
118118+ if (home_directory == "/") {
119119+ fprintf(stderr, "home directory is /, not deleted!\n");
120120+ return 12;
121121+ }
122122+123123+ if (access(home_directory.characters(), F_OK) != -1) {
124124+ auto child = fork();
125125+126126+ if (child < 0) {
127127+ perror("fork");
128128+ return 12;
129129+ }
130130+131131+ if (!child) {
132132+ int rc = execl("/bin/rm", "rm", "-r", home_directory.characters(), nullptr);
133133+ ASSERT(rc < 0);
134134+ perror("execl");
135135+ exit(127);
136136+ }
137137+138138+ int wstatus;
139139+ if (waitpid(child, &wstatus, 0) < 0) {
140140+ perror("waitpid");
141141+ return 12;
142142+ }
143143+ if (WEXITSTATUS(wstatus)) {
144144+ fprintf(stderr, "failed to remove the home directory\n");
145145+ return 12;
146146+ }
147147+ }
148148+ }
149149+150150+ return 0;
151151+}