diff options
Diffstat (limited to '')
-rw-r--r-- | src/mds.c | 786 |
1 files changed, 383 insertions, 403 deletions
@@ -46,12 +46,12 @@ static int argc; /** * Command line arguments */ -static char** argv; +static char **argv; /** * The master server */ -static const char* master_server = LIBEXECDIR "/mds-server"; +static const char *master_server = LIBEXECDIR "/mds-server"; /** * The umask the server start with @@ -66,167 +66,157 @@ static mode_t saved_umask; * @param argv_ Command line arguments * @return Non-zero on error */ -int main(int argc_, char** argv_) +int main(int argc_, char **argv_) { - int fd = -1; - int got_master_server = 0; - struct sockaddr_un address; - char pathname[PATH_MAX]; - char piddata[64]; - unsigned int display = DISPLAY_MAX; - FILE* f; - int rc; - int j, r; - - - argc = argc_; - argv = argv_; - - - /* Sanity check the number of command line arguments. */ - exit_if (argc > ARGC_LIMIT, - eprint("that number of arguments is ridiculous, I will not allow it.");); - - /* Parse command line arguments. */ - for (j = 1; j < argc; j++) - { - char* arg = argv[j]; - if (startswith(arg, "--master-server=")) /* Master server. */ - { - exit_if (got_master_server, - eprintf("duplicate declaration of %s.", "--master-server");); - got_master_server = 1; - master_server = arg + strlen("--master-server="); + int fd = -1; + int got_master_server = 0; + struct sockaddr_un address; + char pathname[PATH_MAX]; + char piddata[64]; + unsigned int display = DISPLAY_MAX; + FILE *f; + int rc; + int j, r; + char *arg; + + argc = argc_; + argv = argv_; + + /* Sanity check the number of command line arguments. */ + exit_if (argc > ARGC_LIMIT, + eprint("that number of arguments is ridiculous, I will not allow it.");); + + /* Parse command line arguments. */ + for (j = 1; j < argc; j++) { + arg = argv[j]; + if (startswith(arg, "--master-server=")) { /* Master server. */ + exit_if (got_master_server, + eprintf("duplicate declaration of %s.", "--master-server");); + got_master_server = 1; + master_server = arg + strlen("--master-server="); + } } - } - - /* Stymied if the effective user is not root. */ - exit_if (geteuid() != ROOT_USER_UID, - eprint("the effective user is not root, cannot continue.");); - - /* Set up to ignore SIGUPDATE, used in mds for re-exec, but we cannot re-exec. */ - if (xsigaction(SIGUPDATE, SIG_IGN) < 0) - xperror(*argv); - - /* Set up to ignore SIGDANGER. */ - if (xsigaction(SIGDANGER, SIG_IGN) < 0) - xperror(*argv); - - /* Set up to ignore SIGINFO. */ - if (xsigaction(SIGINFO, SIG_IGN) < 0) - xperror(*argv); - - /* Remove umask. */ - saved_umask = umask(0); - - /* Create directory for socket files, PID files and such. */ - fail_if (create_directory_root(MDS_RUNTIME_ROOT_DIRECTORY)); - - - /* Determine display index. */ - for (display = 0; display < DISPLAY_MAX; display++) - { - xsnprintf(pathname, "%s/%u.pid", MDS_RUNTIME_ROOT_DIRECTORY, display); - - fd = open(pathname, O_CREAT | O_EXCL, 0644); - if (fd == -1) - { - /* Reuse display index if no longer used. */ - f = fopen(pathname, "r"); - if (f == NULL) /* Race, or error? */ - { - xperror(*argv); - continue; - } - r = is_pid_file_reusable(f); - xfclose(f); - if (r == 0) - continue; + + /* Stymied if the effective user is not root. */ + exit_if (geteuid() != ROOT_USER_UID, + eprint("the effective user is not root, cannot continue.");); + + /* Set up to ignore SIGUPDATE, used in mds for re-exec, but we cannot re-exec. */ + if (xsigaction(SIGUPDATE, SIG_IGN) < 0) + xperror(*argv); + + /* Set up to ignore SIGDANGER. */ + if (xsigaction(SIGDANGER, SIG_IGN) < 0) + xperror(*argv); + + /* Set up to ignore SIGINFO. */ + if (xsigaction(SIGINFO, SIG_IGN) < 0) + xperror(*argv); + + /* Remove umask. */ + saved_umask = umask(0); + + /* Create directory for socket files, PID files and such. */ + fail_if (create_directory_root(MDS_RUNTIME_ROOT_DIRECTORY)); + + /* Determine display index. */ + for (display = 0; display < DISPLAY_MAX; display++) { + xsnprintf(pathname, "%s/%u.pid", MDS_RUNTIME_ROOT_DIRECTORY, display); + + fd = open(pathname, O_CREAT | O_EXCL, 0644); + if (fd < 0) { + /* Reuse display index if no longer used. */ + f = fopen(pathname, "r"); + if (!f) { /* Race, or error? */ + xperror(*argv); + continue; + } + r = is_pid_file_reusable(f); + xfclose(f); + if (!r) + continue; + } + xclose(fd); + break; + } + exit_if (display == DISPLAY_MAX, + eprint("sorry, too many displays on the system.");); + /* Yes, the directory could have been removed, but it probably was not. */ + + /* Create PID file. */ + fail_if (!(f = fopen(pathname, "w"))); + xsnprintf(piddata, "%u\n", getpid()); + if (fwrite(piddata, 1, strlen(piddata), f) < strlen(piddata)) { + xfclose(f); + fail_if (1); + } + fflush(f); + xfclose(f); + if (chmod(pathname, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) + xperror(*argv); + + /* Create data storage directory. */ + xsnprintf(pathname, "%s/%u.data", MDS_STORAGE_ROOT_DIRECTORY, display); + fail_if (create_directory_root(MDS_STORAGE_ROOT_DIRECTORY)); + fail_if (unlink_recursive(pathname)); + fail_if (create_directory_user(pathname)); + + /* Save MDS_DISPLAY environment variable. */ + xsnprintf(pathname, /* Excuse the reuse without renaming. */ + ":%u", display); + fail_if (setenv(DISPLAY_ENV, pathname, 1) < 0); + + /* Create a new process group and export MDS_PGROUP */ + fail_if (setpgid(0, 0) < 0); + xsnprintf(pathname, /* Excuse the reuse without renaming. */ + "%ji", (intmax_t)getpgrp()); + fail_if (setenv(PGROUP_ENV, pathname, 1) < 0); + + /* Create display socket. */ + xsnprintf(pathname, "%s/%u.socket", MDS_RUNTIME_ROOT_DIRECTORY, display); + address.sun_family = AF_UNIX; + if (strlen(pathname) >= sizeof(address.sun_path)) + fail_if ((errno = ENAMETOOLONG)); + strcpy(address.sun_path, pathname); + unlink(pathname); + fail_if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0); + fail_if (fchmod(fd, S_IRWXU) < 0); + fail_if (fchown(fd, getuid(), NOBODY_GROUP_GID) < 0); + fail_if (bind(fd, (struct sockaddr*)(&address), sizeof(address)) < 0); + + /* Start listening on socket. */ + fail_if (listen(fd, SOMAXCONN) < 0); + + /* Start master server and respawn it if it crashes. */ + rc = spawn_and_respawn_server(fd); + +done: + /* Shutdown, close and remove the socket. */ + if (fd >= 0) { + shutdown(fd, SHUT_RDWR); + xclose(fd); + unlink(pathname); } - xclose(fd); - break; - } - exit_if (display == DISPLAY_MAX, - eprint("sorry, too many displays on the system.");); - /* Yes, the directory could have been removed, but it probably was not. */ - - /* Create PID file. */ - fail_if (f = fopen(pathname, "w"), f == NULL); - xsnprintf(piddata, "%u\n", getpid()); - if (fwrite(piddata, 1, strlen(piddata), f) < strlen(piddata)) - { - xfclose(f); - fail_if (1); - } - fflush(f); - xfclose(f); - if (chmod(pathname, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) - xperror(*argv); - - /* Create data storage directory. */ - xsnprintf(pathname, "%s/%u.data", MDS_STORAGE_ROOT_DIRECTORY, display); - fail_if (create_directory_root(MDS_STORAGE_ROOT_DIRECTORY)); - fail_if (unlink_recursive(pathname)); - fail_if (create_directory_user(pathname)); - - - /* Save MDS_DISPLAY environment variable. */ - xsnprintf(pathname, /* Excuse the reuse without renaming. */ - ":%u", display); - fail_if (setenv(DISPLAY_ENV, pathname, 1) < 0); - - /* Create a new process group and export MDS_PGROUP */ - fail_if (setpgid(0, 0) < 0); - xsnprintf(pathname, /* Excuse the reuse without renaming. */ - "%ji", (intmax_t)getpgrp()); - fail_if (setenv(PGROUP_ENV, pathname, 1) < 0); - - /* Create display socket. */ - xsnprintf(pathname, "%s/%u.socket", MDS_RUNTIME_ROOT_DIRECTORY, display); - address.sun_family = AF_UNIX; - if (strlen(pathname) >= sizeof(address.sun_path)) - fail_if ((errno = ENAMETOOLONG)); - strcpy(address.sun_path, pathname); - unlink(pathname); - fail_if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0); - fail_if (fchmod(fd, S_IRWXU) < 0); - fail_if (fchown(fd, getuid(), NOBODY_GROUP_GID) < 0); - fail_if (bind(fd, (struct sockaddr*)(&address), sizeof(address)) < 0); - - /* Start listening on socket. */ - fail_if (listen(fd, SOMAXCONN) < 0); - - /* Start master server and respawn it if it crashes. */ - rc = spawn_and_respawn_server(fd); - - done: - /* Shutdown, close and remove the socket. */ - if (fd != -1) - { - shutdown(fd, SHUT_RDWR); - xclose(fd); - unlink(pathname); - } - - if (display == DISPLAY_MAX) - return rc; - - /* Remove PID file. */ - xsnprintf(pathname, "%s/%u.pid", MDS_RUNTIME_ROOT_DIRECTORY, display); - unlink(pathname); - - /* Remove directories. */ - rmdir(MDS_RUNTIME_ROOT_DIRECTORY); /* Do not care if it fails, it is probably used by another display. */ - rmdir(MDS_STORAGE_ROOT_DIRECTORY); /* Do not care if it fails, it is probably used by another display. */ - xsnprintf(pathname, "%s/%u.data", MDS_STORAGE_ROOT_DIRECTORY, display); - unlink_recursive(pathname); /* An error will be printed on error, do nothing more. */ - - return rc; - - fail: - xperror(*argv); - rc = 1; - goto done; + + if (display == DISPLAY_MAX) + return rc; + + /* Remove PID file. */ + xsnprintf(pathname, "%s/%u.pid", MDS_RUNTIME_ROOT_DIRECTORY, display); + unlink(pathname); + + /* Remove directories. */ + rmdir(MDS_RUNTIME_ROOT_DIRECTORY); /* Do not care if it fails, it is probably used by another display. */ + rmdir(MDS_STORAGE_ROOT_DIRECTORY); /* Do not care if it fails, it is probably used by another display. */ + xsnprintf(pathname, "%s/%u.data", MDS_STORAGE_ROOT_DIRECTORY, display); + unlink_recursive(pathname); /* An error will be printed on error, do nothing more. */ + + return rc; + +fail: + xperror(*argv); + rc = 1; + goto done; } @@ -236,29 +226,29 @@ int main(int argc_, char** argv_) * @param f The PID-file * @return Whether the PID-file is not longer used */ -int is_pid_file_reusable(FILE* f) +int +is_pid_file_reusable(FILE *f) { - char piddata[64]; - size_t read_len; - - read_len = fread(piddata, 1, sizeof(piddata) / sizeof(char), f); - fail_if (ferror(f)); /* Failed to read. */ - if (feof(f) == 0) /* Did not read everything. */ - eprint("the content of a PID file is larger than expected."); - else - { - pid_t pid = parse_pid_t(piddata, read_len - 1); - if (pid == (pid_t)-1) - eprint("the content of a PID file is invalid."); - else - if (kill(pid, 0) < 0) /* Check if the PID is still allocated to any process. */ - return errno == ESRCH; /* PID is not used. */ - } - - return 0; - fail: - xperror(*argv); - return 0; + char piddata[64]; + size_t read_len; + pid_t pid; + + read_len = fread(piddata, 1, sizeof(piddata) / sizeof(char), f); + fail_if (ferror(f)); /* Failed to read. */ + if (!feof(f)) { /* Did not read everything. */ + eprint("the content of a PID file is larger than expected."); + } else { + pid = parse_pid_t(piddata, read_len - 1); + if (pid == (pid_t)-1) + eprint("the content of a PID file is invalid."); + else if (kill(pid, 0) < 0) /* Check if the PID is still allocated to any process. */ + return errno == ESRCH; /* PID is not used. */ + } + + return 0; +fail: + xperror(*argv); + return 0; } @@ -269,24 +259,25 @@ int is_pid_file_reusable(FILE* f) * @param n The length of the string, excluding LF-termination * @return The pid, `(pid_t)-1` if malformated */ -pid_t parse_pid_t(const char* str, size_t n) +pid_t +parse_pid_t(const char *str, size_t n) { - pid_t pid = 0; - size_t i; - - for (i = 0; i < n; i++) - { - char c = str[i]; - if (('0' <= c) && (c <= '9')) - pid = pid * 10 + (c & 15); - else - return (pid_t)-1; - } - - if (str[n] != '\n') - return (pid_t)-1; - - return pid; + pid_t pid = 0; + size_t i; + char c; + + for (i = 0; i < n; i++) { + c = str[i]; + if ('0' <= c && c <= '9') + pid = pid * 10 + (c & 15); + else + return (pid_t)-1; + } + + if (str[n] != '\n') + return (pid_t)-1; + + return pid; } @@ -297,18 +288,18 @@ pid_t parse_pid_t(const char* str, size_t n) * * @param child_args Command line arguments for the new image */ -__attribute__((nonnull)) -static void exec_master_server(char** child_args) +static void __attribute__((nonnull)) +exec_master_server(char **child_args) { - /* Drop privileges. They most not be propagated non-authorised components. */ - /* setgid should not be set, but just to be safe we are restoring both user and group. */ - fail_if (drop_privileges()); - - /* Start master server. */ - execv(master_server, child_args); - fail_if (1); - fail: - return; + /* Drop privileges. They most not be propagated non-authorised components. */ + /* setgid should not be set, but just to be safe we are restoring both user and group. */ + fail_if (drop_privileges()); + + /* Start master server. */ + execv(master_server, child_args); + fail_if (1); +fail: + return; } @@ -318,113 +309,105 @@ static void exec_master_server(char** child_args) * @param fd The file descriptor of the socket * @return Non-zero on error */ -int spawn_and_respawn_server(int fd) +int +spawn_and_respawn_server(int fd) { - int time_error = 0; - int first_spawn = 1; - int rc = 0; - struct timespec time_start; - struct timespec time_end; - char* child_args[ARGC_LIMIT + LIBEXEC_ARGC_EXTRA_LIMIT + 1]; - char fdstr[(sizeof("--socket-fd=") / sizeof(char) - 1) + 64]; - int i; - pid_t pid; - int status; - - fail_if (xstrdup(child_args[0], master_server)); - for (i = 1; i < argc; i++) - child_args[i] = argv[i]; - fail_if (xstrdup(child_args[argc + 0], "--initial-spawn")); - xsnprintf(fdstr, "--socket-fd=%i", fd); - child_args[argc + 1] = fdstr; - child_args[argc + 2] = NULL; - + int time_error = 0; + int first_spawn = 1; + int rc = 0; + struct timespec time_start; + struct timespec time_end; + char *child_args[ARGC_LIMIT + LIBEXEC_ARGC_EXTRA_LIMIT + 1]; + char fdstr[(sizeof("--socket-fd=") / sizeof(char) - 1) + 64]; + int i; + pid_t pid; + int status; + + fail_if (xstrdup(child_args[0], master_server)); + for (i = 1; i < argc; i++) + child_args[i] = argv[i]; + fail_if (xstrdup(child_args[argc + 0], "--initial-spawn")); + xsnprintf(fdstr, "--socket-fd=%i", fd); + child_args[argc + 1] = fdstr; + child_args[argc + 2] = NULL; + #if (LIBEXEC_ARGC_EXTRA_LIMIT < 2) # error LIBEXEC_ARGC_EXTRA_LIMIT is too small, need at least 2. #endif - - - respawn: - - pid = fork(); - fail_if (pid == (pid_t)-1); - - if (pid == 0) /* Child. */ - { - /* If this image exits, it should do so with failure status. */ - rc++; - /* Reinstate original umask. */ - umask(saved_umask); - /* Change image into the master server. */ - exec_master_server(child_args); - fail_if (1); - } - - - /* Parent. */ - - /* Get the current time. (Start of child process.) */ - if ((time_error = (monotone(&time_start) < 0))) - xperror(*argv); - - /* Wait for master server to die. */ - fail_if (uninterruptable_waitpid(pid, &status, 0) == (pid_t)-1); - - /* If the server exited normally or SIGTERM, do not respawn. */ - if (WIFEXITED(status) ? (WEXITSTATUS(status) == 0) : - ((WTERMSIG(status) == SIGTERM) || (WTERMSIG(status) == SIGINT))) - { - /* Child exited normally, stop. */ - rc--; - goto done; - } - - /* Get the current time. (End of child process.) */ - time_error |= (monotone(&time_end) < 0); - - if (WIFEXITED(status)) - eprintf("`%s' exited with code %i.", master_server, WEXITSTATUS(status)); - else - eprintf("`%s' died by signal %i.", master_server, WTERMSIG(status)); - - /* Do not respawn if we could not read the time. */ - if (time_error) - { - xperror(*argv); - eprintf("`%s' died abnormally, not respawning because we could not read the time.", master_server); - goto done; - } - - /* Respawn if the server did not die too fast. */ - if (time_end.tv_sec - time_start.tv_sec >= RESPAWN_TIME_LIMIT_SECONDS) - eprintf("`%s' died abnormally, respawning.", master_server); - else - { - eprintf("`%s' died abnormally, died too fast, not respawning.", master_server); - goto done; - } - - if (first_spawn) - { - first_spawn = 0; - free(child_args[argc + 0]); - fail_if (xstrdup(child_args[argc + 0], "--respawn")); - } - - goto respawn; - - - done: - rc++; - free(child_args[0]); - free(child_args[argc + 0]); - if (rc == 2) - _exit(1); - return rc; - - fail: - xperror(*argv); - goto done; + +respawn: + pid = fork(); + fail_if (pid == (pid_t)-1); + + if (!pid) { /* Child. */ + /* If this image exits, it should do so with failure status. */ + rc++; + /* Reinstate original umask. */ + umask(saved_umask); + /* Change image into the master server. */ + exec_master_server(child_args); + fail_if (1); + } + + /* Parent. */ + + /* Get the current time. (Start of child process.) */ + if ((time_error = (monotone(&time_start) < 0))) + xperror(*argv); + + /* Wait for master server to die. */ + fail_if (uninterruptable_waitpid(pid, &status, 0) == (pid_t)-1); + + /* If the server exited normally or SIGTERM, do not respawn. */ + if (WIFEXITED(status) ? !WEXITSTATUS(status) : + (WTERMSIG(status) == SIGTERM || WTERMSIG(status) == SIGINT)) { + /* Child exited normally, stop. */ + rc--; + goto done; + } + + /* Get the current time. (End of child process.) */ + time_error |= (monotone(&time_end) < 0); + + if (WIFEXITED(status)) + eprintf("`%s' exited with code %i.", master_server, WEXITSTATUS(status)); + else + eprintf("`%s' died by signal %i.", master_server, WTERMSIG(status)); + + /* Do not respawn if we could not read the time. */ + if (time_error) { + xperror(*argv); + eprintf("`%s' died abnormally, not respawning because we could not read the time.", master_server); + goto done; + } + + /* Respawn if the server did not die too fast. */ + if (time_end.tv_sec - time_start.tv_sec >= RESPAWN_TIME_LIMIT_SECONDS) { + eprintf("`%s' died abnormally, respawning.", master_server); + } else { + eprintf("`%s' died abnormally, died too fast, not respawning.", master_server); + goto done; + } + + if (first_spawn) { + first_spawn = 0; + free(child_args[argc + 0]); + fail_if (xstrdup(child_args[argc + 0], "--respawn")); + } + + goto respawn; + +done: + rc++; + free(child_args[0]); + free(child_args[argc + 0]); + if (rc == 2) + _exit(1); + return rc; + +fail: + xperror(*argv); + goto done; } @@ -434,33 +417,32 @@ int spawn_and_respawn_server(int fd) * @param pathname The pathname of the directory to create * @return Non-zero on error */ -int create_directory_root(const char* pathname) +int +create_directory_root(const char *pathname) { - struct stat attr; - - if (stat(pathname, &attr) == 0) - { - /* Cannot create the directory, its pathname refers to an existing. */ - if (S_ISDIR(attr.st_mode) == 0) - { - /* But it is not a directory so we cannot continue. */ - eprintf("%s already exists but is not a directory.", pathname); - return 1; + struct stat attr; + + if (!stat(pathname, &attr)) { + /* Cannot create the directory, its pathname refers to an existing. */ + if (!S_ISDIR(attr.st_mode)) { + /* But it is not a directory so we cannot continue. */ + eprintf("%s already exists but is not a directory.", pathname); + return 1; + } + } else { + /* Directory is missing, create it. */ + if (mkdir(pathname, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) + /* Unlikely race condition. */ + fail_if (errno != EEXIST); + else + /* Set ownership. */ + fail_if (chown(pathname, ROOT_USER_UID, ROOT_GROUP_GID) < 0); } - } - else - /* Directory is missing, create it. */ - if (mkdir(pathname, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) - /* Unlikely race condition. */ - fail_if (errno != EEXIST); - else - /* Set ownership. */ - fail_if (chown(pathname, ROOT_USER_UID, ROOT_GROUP_GID) < 0); - - return 0; - fail: - xperror(*argv); - return 1; + + return 0; +fail: + xperror(*argv); + return 1; } @@ -470,33 +452,32 @@ int create_directory_root(const char* pathname) * @param pathname The pathname of the directory to create * @return Non-zero on error */ -int create_directory_user(const char* pathname) +int +create_directory_user(const char *pathname) { - struct stat attr; - - if (stat(pathname, &attr) == 0) - { - /* Cannot create the directory, its pathname refers to an existing. */ - if (S_ISDIR(attr.st_mode) == 0) - { - /* But it is not a directory so we cannot continue. */ - eprintf("%s already exists but is not a directory.", pathname); - return 1; + struct stat attr; + + if (!stat(pathname, &attr)) { + /* Cannot create the directory, its pathname refers to an existing. */ + if (!S_ISDIR(attr.st_mode)) { + /* But it is not a directory so we cannot continue. */ + eprintf("%s already exists but is not a directory.", pathname); + return 1; + } + } else { + /* Directory is missing, create it. */ + if (mkdir(pathname, S_IRWXU) < 0) + /* Unlikely race condition. */ + fail_if (errno != EEXIST); + else + /* Set ownership. */ + fail_if (chown(pathname, getuid(), NOBODY_GROUP_GID) < 0); } - } - else - /* Directory is missing, create it. */ - if (mkdir(pathname, S_IRWXU) < 0) - /* Unlikely race condition. */ - fail_if (errno != EEXIST); - else - /* Set ownership. */ - fail_if (chown(pathname, getuid(), NOBODY_GROUP_GID) < 0); - - return 0; - fail: - xperror(*argv); - return 1; + + return 0; +fail: + xperror(*argv); + return 1; } @@ -506,45 +487,44 @@ int create_directory_user(const char* pathname) * @param pathname The pathname of the directory to remove * @return Non-zero on error, but zero if the directory does not exist */ -int unlink_recursive(const char* pathname) +int +unlink_recursive(const char *pathname) { - DIR* dir = opendir(pathname); - int rc = 0; - struct dirent* file; - - /* Check that we could examine the directory. */ - if (dir == NULL) - { - int saved_errno = errno; - struct stat _attr; - if (stat(pathname, &_attr) < 0) - return 0; /* Directory does not exist. */ - errno = saved_errno; - fail_if (1); - } - - /* Remove the content of the directory. */ - while ((file = readdir(dir)) != NULL) - if (strcmp(file->d_name, ".") && - strcmp(file->d_name, "..") && - (unlink(file->d_name) < 0)) - { - fail_if (errno != EISDIR); - unlink_recursive(file->d_name); - } - - /* Remove the drectory. */ - fail_if (rmdir(pathname) < 0); - - done: - if (dir != NULL) - closedir(dir); - return rc; - - - fail: - xperror(*argv); - rc = -1; - goto done; -} + DIR *dir = opendir(pathname); + int rc = 0; + struct dirent *file; + int saved_errno; + struct stat _attr; + + /* Check that we could examine the directory. */ + if (!dir) { + saved_errno = errno; + if (stat(pathname, &_attr) < 0) + return 0; /* Directory does not exist. */ + errno = saved_errno; + fail_if (1); + } + + /* Remove the content of the directory. */ + while ((file = readdir(dir))) { + if (strcmp(file->d_name, ".") && + strcmp(file->d_name, "..") && + (unlink(file->d_name) < 0)) { + fail_if (errno != EISDIR); + unlink_recursive(file->d_name); + } + } + + /* Remove the drectory. */ + fail_if (rmdir(pathname) < 0); +done: + if (dir) + closedir(dir); + return rc; + +fail: + xperror(*argv); + rc = -1; + goto done; +} |