busybox/loginutils/add-remove-shell.c
<<
>>
Prefs
   1/*
   2 * add-shell and remove-shell implementation for busybox
   3 *
   4 * Copyright (C) 2010 Nokia Corporation. All rights reserved.
   5 * Written by Alexander Shishkin <virtuoso@slind.org>
   6 *
   7 * Licensed under GPLv2 or later, see the LICENSE file in this source tree
   8 * for details.
   9 */
  10//config:config ADD_SHELL
  11//config:       bool "add-shell (3.1 kb)"
  12//config:       default y if DESKTOP
  13//config:       help
  14//config:       Add shells to /etc/shells.
  15//config:
  16//config:config REMOVE_SHELL
  17//config:       bool "remove-shell (3 kb)"
  18//config:       default y if DESKTOP
  19//config:       help
  20//config:       Remove shells from /etc/shells.
  21
  22//                       APPLET_NOEXEC:name          main              location         suid_type     help
  23//applet:IF_ADD_SHELL(   APPLET_NOEXEC(add-shell   , add_remove_shell, BB_DIR_USR_SBIN, BB_SUID_DROP, add_shell   ))
  24//applet:IF_REMOVE_SHELL(APPLET_NOEXEC(remove-shell, add_remove_shell, BB_DIR_USR_SBIN, BB_SUID_DROP, remove_shell))
  25
  26//kbuild:lib-$(CONFIG_ADD_SHELL)    += add-remove-shell.o
  27//kbuild:lib-$(CONFIG_REMOVE_SHELL) += add-remove-shell.o
  28
  29//usage:#define add_shell_trivial_usage
  30//usage:       "SHELL..."
  31//usage:#define add_shell_full_usage "\n\n"
  32//usage:       "Add SHELLs to /etc/shells"
  33
  34//usage:#define remove_shell_trivial_usage
  35//usage:       "SHELL..."
  36//usage:#define remove_shell_full_usage "\n\n"
  37//usage:       "Remove SHELLs from /etc/shells"
  38
  39#include "libbb.h"
  40
  41#define SHELLS_FILE "/etc/shells"
  42
  43#define REMOVE_SHELL (ENABLE_REMOVE_SHELL && (!ENABLE_ADD_SHELL || applet_name[0] == 'r'))
  44#define ADD_SHELL    (ENABLE_ADD_SHELL && (!ENABLE_REMOVE_SHELL || applet_name[0] == 'a'))
  45
  46#define dont_add ((char*)(uintptr_t)1)
  47
  48int add_remove_shell_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  49int add_remove_shell_main(int argc UNUSED_PARAM, char **argv)
  50{
  51        FILE *orig_fp;
  52        char *orig_fn;
  53        char *new_fn;
  54        struct stat sb;
  55
  56        sb.st_mode = 0666;
  57
  58        argv++;
  59
  60        orig_fn = xmalloc_follow_symlinks(SHELLS_FILE);
  61        if (!orig_fn)
  62                return EXIT_FAILURE;
  63        orig_fp = fopen_for_read(orig_fn);
  64        if (orig_fp)
  65                xfstat(fileno(orig_fp), &sb, orig_fn);
  66
  67
  68        new_fn = xasprintf("%s.tmp", orig_fn);
  69        /*
  70         * O_TRUNC or O_EXCL? At the first glance, O_EXCL looks better,
  71         * since it prevents races. But: (1) it requires a retry loop,
  72         * (2) if /etc/shells.tmp is *stale*, then retry loop
  73         * with O_EXCL will never succeed - it should have a timeout,
  74         * after which it should revert to O_TRUNC.
  75         * For now, I settle for O_TRUNC instead.
  76         */
  77        xmove_fd(xopen3(new_fn, O_WRONLY | O_CREAT | O_TRUNC, sb.st_mode), STDOUT_FILENO);
  78        /* TODO?
  79        xfchown(STDOUT_FILENO, sb.st_uid, sb.st_gid);
  80        */
  81
  82        if (orig_fp) {
  83                /* Copy old file, possibly skipping removed shell names */
  84                char *line;
  85                while ((line = xmalloc_fgetline(orig_fp)) != NULL) {
  86                        char **cpp = argv;
  87                        while (*cpp) {
  88                                if (*cpp != dont_add && strcmp(*cpp, line) == 0) {
  89                                        /* Old file has this shell name */
  90                                        if (REMOVE_SHELL) {
  91                                                /* we are remove-shell */
  92                                                /* delete this name by not copying it */
  93                                                goto next_line;
  94                                        }
  95                                        /* we are add-shell */
  96                                        /* mark this name as "do not add" */
  97                                        *cpp = dont_add;
  98                                }
  99                                cpp++;
 100                        }
 101                        /* copy shell name from old to new file */
 102                        puts(line);
 103 next_line:
 104                        free(line);
 105                }
 106                if (ENABLE_FEATURE_CLEAN_UP)
 107                        fclose(orig_fp);
 108        }
 109
 110        if (ADD_SHELL) {
 111                char **cpp = argv;
 112                while (*cpp) {
 113                        if (*cpp != dont_add)
 114                                puts(*cpp);
 115                        cpp++;
 116                }
 117        }
 118
 119        /* Ensure we wrote out everything */
 120        if (fclose(stdout) != 0) {
 121                xunlink(new_fn);
 122                bb_perror_msg_and_die("%s: write error", new_fn);
 123        }
 124
 125        /* Small hole: if rename fails, /etc/shells.tmp is not removed */
 126        xrename(new_fn, orig_fn);
 127
 128        if (ENABLE_FEATURE_CLEAN_UP) {
 129                free(orig_fn);
 130                free(new_fn);
 131        }
 132
 133        return EXIT_SUCCESS;
 134}
 135