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