diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-02-21 00:15:20 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-02-21 00:15:20 +0000 |
| commit | 7a2ca5e111a21cca703e111ee448317bfaf96ed9 (patch) | |
| tree | be7c65eeb924440e094ba4bdc46b60317eefe6f1 | |
| parent | a624c11d5e9ac32f6e7b22b406e5a0eec44e2d3b (diff) | |
| download | busybox-w32-7a2ca5e111a21cca703e111ee448317bfaf96ed9.tar.gz busybox-w32-7a2ca5e111a21cca703e111ee448317bfaf96ed9.tar.bz2 busybox-w32-7a2ca5e111a21cca703e111ee448317bfaf96ed9.zip | |
init: stop doing silly things with the console (-400 bytes)
init_shared.[ch]: unused, thus deleted
| -rw-r--r-- | init/Kbuild | 2 | ||||
| -rw-r--r-- | init/init.c | 348 | ||||
| -rw-r--r-- | init/init_shared.c | 63 | ||||
| -rw-r--r-- | init/init_shared.h | 10 |
4 files changed, 131 insertions, 292 deletions
diff --git a/init/Kbuild b/init/Kbuild index e99360241..c060f3af4 100644 --- a/init/Kbuild +++ b/init/Kbuild | |||
| @@ -8,5 +8,3 @@ lib-y:= | |||
| 8 | lib-$(CONFIG_HALT) += halt.o | 8 | lib-$(CONFIG_HALT) += halt.o |
| 9 | lib-$(CONFIG_INIT) += init.o | 9 | lib-$(CONFIG_INIT) += init.o |
| 10 | lib-$(CONFIG_MESG) += mesg.o | 10 | lib-$(CONFIG_MESG) += mesg.o |
| 11 | lib-$(CONFIG_INIT) += init_shared.o | ||
| 12 | lib-$(CONFIG_HALT) += init_shared.o | ||
diff --git a/init/init.c b/init/init.c index b488f649f..ec18332fb 100644 --- a/init/init.c +++ b/init/init.c | |||
| @@ -16,44 +16,13 @@ | |||
| 16 | #include <sys/wait.h> | 16 | #include <sys/wait.h> |
| 17 | #include <sys/reboot.h> | 17 | #include <sys/reboot.h> |
| 18 | 18 | ||
| 19 | #include "init_shared.h" | ||
| 20 | |||
| 21 | #if ENABLE_SYSLOGD | 19 | #if ENABLE_SYSLOGD |
| 22 | # include <sys/syslog.h> | 20 | # include <sys/syslog.h> |
| 23 | #endif | 21 | #endif |
| 24 | 22 | ||
| 25 | #define INIT_BUFFS_SIZE 256 | 23 | #define INIT_BUFFS_SIZE 256 |
| 26 | 24 | #define CONSOLE_NAME_SIZE 32 | |
| 27 | /* From <linux/vt.h> */ | 25 | #define MAXENV 16 /* Number of env. vars */ |
| 28 | struct vt_stat { | ||
| 29 | unsigned short v_active; /* active vt */ | ||
| 30 | unsigned short v_signal; /* signal to send */ | ||
| 31 | unsigned short v_state; /* vt bitmask */ | ||
| 32 | }; | ||
| 33 | enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */ | ||
| 34 | |||
| 35 | /* From <linux/serial.h> */ | ||
| 36 | struct serial_struct { | ||
| 37 | int type; | ||
| 38 | int line; | ||
| 39 | unsigned int port; | ||
| 40 | int irq; | ||
| 41 | int flags; | ||
| 42 | int xmit_fifo_size; | ||
| 43 | int custom_divisor; | ||
| 44 | int baud_base; | ||
| 45 | unsigned short close_delay; | ||
| 46 | char io_type; | ||
| 47 | char reserved_char[1]; | ||
| 48 | int hub6; | ||
| 49 | unsigned short closing_wait; /* time to wait before closing */ | ||
| 50 | unsigned short closing_wait2; /* no longer used... */ | ||
| 51 | unsigned char *iomem_base; | ||
| 52 | unsigned short iomem_reg_shift; | ||
| 53 | unsigned int port_high; | ||
| 54 | unsigned long iomap_base; /* cookie passed into ioremap */ | ||
| 55 | int reserved[1]; | ||
| 56 | }; | ||
| 57 | 26 | ||
| 58 | #ifndef _PATH_STDPATH | 27 | #ifndef _PATH_STDPATH |
| 59 | #define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" | 28 | #define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" |
| @@ -75,10 +44,6 @@ struct serial_struct { | |||
| 75 | #define INIT_SCRIPT "/etc/init.d/rcS" /* Default sysinit script. */ | 44 | #define INIT_SCRIPT "/etc/init.d/rcS" /* Default sysinit script. */ |
| 76 | #endif | 45 | #endif |
| 77 | 46 | ||
| 78 | #define MAXENV 16 /* Number of env. vars */ | ||
| 79 | |||
| 80 | #define CONSOLE_BUFF_SIZE 32 | ||
| 81 | |||
| 82 | /* Allowed init action types */ | 47 | /* Allowed init action types */ |
| 83 | #define SYSINIT 0x001 | 48 | #define SYSINIT 0x001 |
| 84 | #define RESPAWN 0x002 | 49 | #define RESPAWN 0x002 |
| @@ -109,16 +74,15 @@ static const struct init_action_type actions[] = { | |||
| 109 | 74 | ||
| 110 | /* Set up a linked list of init_actions, to be read from inittab */ | 75 | /* Set up a linked list of init_actions, to be read from inittab */ |
| 111 | struct init_action { | 76 | struct init_action { |
| 112 | pid_t pid; | ||
| 113 | char command[INIT_BUFFS_SIZE]; | ||
| 114 | char terminal[CONSOLE_BUFF_SIZE]; | ||
| 115 | struct init_action *next; | 77 | struct init_action *next; |
| 116 | int action; | 78 | int action; |
| 79 | pid_t pid; | ||
| 80 | char command[INIT_BUFFS_SIZE]; | ||
| 81 | char terminal[CONSOLE_NAME_SIZE]; | ||
| 117 | }; | 82 | }; |
| 118 | 83 | ||
| 119 | /* Static variables */ | 84 | /* Static variables */ |
| 120 | static struct init_action *init_action_list = NULL; | 85 | static struct init_action *init_action_list = NULL; |
| 121 | static char console_name[CONSOLE_BUFF_SIZE] = DEV_CONSOLE; | ||
| 122 | 86 | ||
| 123 | #if !ENABLE_SYSLOGD | 87 | #if !ENABLE_SYSLOGD |
| 124 | static const char *log_console = VC_5; | 88 | static const char *log_console = VC_5; |
| @@ -180,68 +144,60 @@ static void message(int device, const char *fmt, ...) | |||
| 180 | __attribute__ ((format(printf, 2, 3))); | 144 | __attribute__ ((format(printf, 2, 3))); |
| 181 | static void message(int device, const char *fmt, ...) | 145 | static void message(int device, const char *fmt, ...) |
| 182 | { | 146 | { |
| 183 | va_list arguments; | ||
| 184 | int l; | ||
| 185 | RESERVE_CONFIG_BUFFER(msg, 1024); | ||
| 186 | #if !ENABLE_SYSLOGD | 147 | #if !ENABLE_SYSLOGD |
| 187 | static int log_fd = -1; | 148 | static int log_fd = -1; |
| 188 | #endif | 149 | #endif |
| 189 | 150 | ||
| 151 | va_list arguments; | ||
| 152 | int l; | ||
| 153 | char msg[128]; | ||
| 154 | |||
| 190 | msg[0] = '\r'; | 155 | msg[0] = '\r'; |
| 191 | va_start(arguments, fmt); | 156 | va_start(arguments, fmt); |
| 192 | l = vsnprintf(msg + 1, 1024 - 2, fmt, arguments) + 1; | 157 | vsnprintf(msg + 1, sizeof(msg) - 2, fmt, arguments); |
| 193 | va_end(arguments); | 158 | va_end(arguments); |
| 159 | msg[sizeof(msg) - 2] = '\0'; | ||
| 160 | l = strlen(msg); | ||
| 194 | 161 | ||
| 195 | #if ENABLE_SYSLOGD | 162 | #if ENABLE_SYSLOGD |
| 196 | /* Log the message to syslogd */ | 163 | /* Log the message to syslogd */ |
| 197 | if (device & L_LOG) { | 164 | if (device & L_LOG) { |
| 198 | /* don`t out "\r\n" */ | 165 | /* don't out "\r" */ |
| 199 | openlog(applet_name, 0, LOG_DAEMON); | 166 | openlog(applet_name, 0, LOG_DAEMON); |
| 200 | syslog(LOG_INFO, "%s", msg + 1); | 167 | syslog(LOG_INFO, "init: %s", msg + 1); |
| 201 | closelog(); | 168 | closelog(); |
| 202 | } | 169 | } |
| 203 | |||
| 204 | msg[l++] = '\n'; | 170 | msg[l++] = '\n'; |
| 205 | msg[l] = 0; | 171 | msg[l] = '\0'; |
| 206 | #else | 172 | #else |
| 207 | |||
| 208 | msg[l++] = '\n'; | 173 | msg[l++] = '\n'; |
| 209 | msg[l] = 0; | 174 | msg[l] = '\0'; |
| 210 | /* Take full control of the log tty, and never close it. | 175 | /* Take full control of the log tty, and never close it. |
| 211 | * It's mine, all mine! Muhahahaha! */ | 176 | * It's mine, all mine! Muhahahaha! */ |
| 212 | if (log_fd < 0) { | 177 | if (log_fd < 0) { |
| 213 | log_fd = device_open(log_console, O_WRONLY | O_NONBLOCK | O_NOCTTY); | 178 | if (!log_console) { |
| 214 | if (log_fd < 0) { | 179 | log_fd = 2; |
| 215 | log_fd = -2; | ||
| 216 | bb_error_msg("bummer, can't log to %s!", log_console); | ||
| 217 | device = L_CONSOLE; | ||
| 218 | } else { | 180 | } else { |
| 219 | fcntl(log_fd, F_SETFD, FD_CLOEXEC); | 181 | log_fd = device_open(log_console, O_WRONLY | O_NONBLOCK | O_NOCTTY); |
| 182 | if (log_fd < 0) { | ||
| 183 | bb_error_msg("can't log to %s", log_console); | ||
| 184 | device = L_CONSOLE; | ||
| 185 | } else { | ||
| 186 | fcntl(log_fd, F_SETFD, FD_CLOEXEC); | ||
| 187 | } | ||
| 220 | } | 188 | } |
| 221 | } | 189 | } |
| 222 | if (device & L_LOG) { | 190 | if (device & L_LOG) { |
| 223 | full_write(log_fd, msg, l); | 191 | full_write(log_fd, msg, l); |
| 192 | if (log_fd == 2) | ||
| 193 | return; /* don't print dup messages */ | ||
| 224 | } | 194 | } |
| 225 | #endif | 195 | #endif |
| 226 | 196 | ||
| 227 | if (device & L_CONSOLE) { | 197 | if (device & L_CONSOLE) { |
| 228 | int fd = device_open(DEV_CONSOLE, | 198 | /* Send console messages to console so people will see them. */ |
| 229 | O_WRONLY | O_NOCTTY | O_NONBLOCK); | 199 | full_write(2, msg, l); |
| 230 | /* Always send console messages to /dev/console so people will see them. */ | ||
| 231 | if (fd >= 0) { | ||
| 232 | full_write(fd, msg, l); | ||
| 233 | close(fd); | ||
| 234 | #if ENABLE_DEBUG_INIT | ||
| 235 | /* all descriptors may be closed */ | ||
| 236 | } else { | ||
| 237 | bb_error_msg("bummer, can't print: "); | ||
| 238 | va_start(arguments, fmt); | ||
| 239 | vfprintf(stderr, fmt, arguments); | ||
| 240 | va_end(arguments); | ||
| 241 | #endif | ||
| 242 | } | ||
| 243 | } | 200 | } |
| 244 | RELEASE_CONFIG_BUFFER(msg); | ||
| 245 | } | 201 | } |
| 246 | 202 | ||
| 247 | /* Set terminal settings to reasonable defaults */ | 203 | /* Set terminal settings to reasonable defaults */ |
| @@ -268,7 +224,6 @@ static void set_term(void) | |||
| 268 | tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD; | 224 | tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD; |
| 269 | tty.c_cflag |= CREAD | HUPCL | CLOCAL; | 225 | tty.c_cflag |= CREAD | HUPCL | CLOCAL; |
| 270 | 226 | ||
| 271 | |||
| 272 | /* input modes */ | 227 | /* input modes */ |
| 273 | tty.c_iflag = ICRNL | IXON | IXOFF; | 228 | tty.c_iflag = ICRNL | IXON | IXOFF; |
| 274 | 229 | ||
| @@ -282,60 +237,59 @@ static void set_term(void) | |||
| 282 | tcsetattr(STDIN_FILENO, TCSANOW, &tty); | 237 | tcsetattr(STDIN_FILENO, TCSANOW, &tty); |
| 283 | } | 238 | } |
| 284 | 239 | ||
| 240 | /* From <linux/serial.h> */ | ||
| 241 | struct serial_struct { | ||
| 242 | int type; | ||
| 243 | int line; | ||
| 244 | unsigned int port; | ||
| 245 | int irq; | ||
| 246 | int flags; | ||
| 247 | int xmit_fifo_size; | ||
| 248 | int custom_divisor; | ||
| 249 | int baud_base; | ||
| 250 | unsigned short close_delay; | ||
| 251 | char io_type; | ||
| 252 | char reserved_char[1]; | ||
| 253 | int hub6; | ||
| 254 | unsigned short closing_wait; /* time to wait before closing */ | ||
| 255 | unsigned short closing_wait2; /* no longer used... */ | ||
| 256 | unsigned char *iomem_base; | ||
| 257 | unsigned short iomem_reg_shift; | ||
| 258 | unsigned int port_high; | ||
| 259 | unsigned long iomap_base; /* cookie passed into ioremap */ | ||
| 260 | int reserved[1]; | ||
| 261 | /* Paranoia (imagine 64bit kernel overwriting 32bit userspace stack) */ | ||
| 262 | uint32_t bbox_reserved[16]; | ||
| 263 | }; | ||
| 285 | static void console_init(void) | 264 | static void console_init(void) |
| 286 | { | 265 | { |
| 287 | int fd; | ||
| 288 | int tried = 0; | ||
| 289 | struct vt_stat vt; | ||
| 290 | struct serial_struct sr; | 266 | struct serial_struct sr; |
| 291 | char *s; | 267 | char *s; |
| 292 | 268 | ||
| 293 | if ((s = getenv("CONSOLE")) != NULL || (s = getenv("console")) != NULL) { | 269 | s = getenv("CONSOLE"); |
| 294 | safe_strncpy(console_name, s, sizeof(console_name)); | 270 | if (!s) s = getenv("console"); |
| 295 | } else { | 271 | if (s) { |
| 296 | /* 2.2 kernels: identify the real console backend and try to use it */ | 272 | int fd = open(s, O_RDWR | O_NONBLOCK | O_NOCTTY); |
| 297 | if (ioctl(0, TIOCGSERIAL, &sr) == 0) { | 273 | if (fd >= 0) { |
| 298 | /* this is a serial console */ | 274 | dup2(fd, 0); |
| 299 | snprintf(console_name, sizeof(console_name) - 1, SC_FORMAT, sr.line); | 275 | dup2(fd, 1); |
| 300 | } else if (ioctl(0, VT_GETSTATE, &vt) == 0) { | 276 | dup2(fd, 2); |
| 301 | /* this is linux virtual tty */ | 277 | while (fd > 2) close(fd--); |
| 302 | snprintf(console_name, sizeof(console_name) - 1, VC_FORMAT, vt.v_active); | ||
| 303 | } else { | ||
| 304 | strcpy(console_name, DEV_CONSOLE); | ||
| 305 | tried++; | ||
| 306 | } | 278 | } |
| 279 | messageD(L_LOG, "console='%s'", s); | ||
| 307 | } | 280 | } |
| 308 | 281 | ||
| 309 | while ((fd = open(console_name, O_RDONLY | O_NONBLOCK)) < 0 && tried < 2) { | 282 | s = getenv("TERM"); |
| 310 | /* Can't open selected console -- try | 283 | if (ioctl(0, TIOCGSERIAL, &sr) == 0) { |
| 311 | logical system console and VT_MASTER */ | 284 | /* Force the TERM setting to vt102 for serial console -- |
| 312 | strcpy(console_name, (tried == 0 ? DEV_CONSOLE : CURRENT_VC)); | 285 | * if TERM is set to linux (the default) */ |
| 313 | tried++; | 286 | if (!s || strcmp(s, "linux") == 0) |
| 314 | } | 287 | putenv((char*)"TERM=vt102"); |
| 315 | if (fd < 0) { | ||
| 316 | /* Perhaps we should panic here? */ | ||
| 317 | #if !ENABLE_SYSLOGD | ||
| 318 | log_console = | ||
| 319 | #endif | ||
| 320 | strcpy(console_name, bb_dev_null); | ||
| 321 | } else { | ||
| 322 | s = getenv("TERM"); | ||
| 323 | /* check for serial console */ | ||
| 324 | if (ioctl(fd, TIOCGSERIAL, &sr) == 0) { | ||
| 325 | /* Force the TERM setting to vt102 for serial console -- | ||
| 326 | * if TERM is set to linux (the default) */ | ||
| 327 | if (s == NULL || strcmp(s, "linux") == 0) | ||
| 328 | putenv((char*)"TERM=vt102"); | ||
| 329 | #if !ENABLE_SYSLOGD | 288 | #if !ENABLE_SYSLOGD |
| 330 | log_console = console_name; | 289 | log_console = NULL; |
| 331 | #endif | 290 | #endif |
| 332 | } else { | 291 | } else if (!s) |
| 333 | if (s == NULL) | 292 | putenv((char*)"TERM=linux"); |
| 334 | putenv((char*)"TERM=linux"); | ||
| 335 | } | ||
| 336 | close(fd); | ||
| 337 | } | ||
| 338 | messageD(L_LOG, "console=%s", console_name); | ||
| 339 | } | 293 | } |
| 340 | 294 | ||
| 341 | static void fixup_argv(int argc, char **argv, const char *new_argv0) | 295 | static void fixup_argv(int argc, char **argv, const char *new_argv0) |
| @@ -355,24 +309,29 @@ static void fixup_argv(int argc, char **argv, const char *new_argv0) | |||
| 355 | } | 309 | } |
| 356 | 310 | ||
| 357 | /* Open the new terminal device */ | 311 | /* Open the new terminal device */ |
| 358 | static void open_new_terminal(const char* device, int fail) { | 312 | static void open_stdio_to_tty(const char* tty_name, int fail) |
| 359 | struct stat sb; | 313 | { |
| 360 | 314 | /* empty tty_name means "use init's tty", else... */ | |
| 361 | if ((device_open(device, O_RDWR)) < 0) { | 315 | if (tty_name[0]) { |
| 362 | if (stat(device, &sb) != 0) { | 316 | close(0); |
| 363 | message(L_LOG | L_CONSOLE, "device '%s' does not exist", device); | 317 | if ((device_open(tty_name, O_RDWR)) < 0) { |
| 364 | } else { | 318 | dup2(1, 0); /* restore fd #0 - avoid nasty surprises */ |
| 365 | message(L_LOG | L_CONSOLE, "Bummer, can't open %s", device); | 319 | message(L_LOG | L_CONSOLE, "can't open %s: %s", |
| 366 | } | 320 | tty_name, strerror(errno)); |
| 367 | if (fail) | 321 | if (fail) |
| 368 | _exit(1); | 322 | _exit(1); |
| 369 | /* else */ | ||
| 370 | #if !ENABLE_DEBUG_INIT | 323 | #if !ENABLE_DEBUG_INIT |
| 371 | shutdown_signal(SIGUSR1); | 324 | shutdown_signal(SIGUSR1); |
| 372 | #else | 325 | #else |
| 373 | _exit(2); | 326 | _exit(2); |
| 374 | #endif | 327 | #endif |
| 328 | } | ||
| 375 | } | 329 | } |
| 330 | close(1); | ||
| 331 | close(2); | ||
| 332 | set_term(); | ||
| 333 | dup(0); | ||
| 334 | dup(0); | ||
| 376 | } | 335 | } |
| 377 | 336 | ||
| 378 | static pid_t run(const struct init_action *a) | 337 | static pid_t run(const struct init_action *a) |
| @@ -395,11 +354,7 @@ static pid_t run(const struct init_action *a) | |||
| 395 | sigprocmask(SIG_BLOCK, &nmask, &omask); | 354 | sigprocmask(SIG_BLOCK, &nmask, &omask); |
| 396 | 355 | ||
| 397 | if ((pid = fork()) == 0) { | 356 | if ((pid = fork()) == 0) { |
| 398 | |||
| 399 | /* Clean up */ | 357 | /* Clean up */ |
| 400 | close(0); | ||
| 401 | close(1); | ||
| 402 | close(2); | ||
| 403 | sigprocmask(SIG_SETMASK, &omask, NULL); | 358 | sigprocmask(SIG_SETMASK, &omask, NULL); |
| 404 | 359 | ||
| 405 | /* Reset signal handlers that were set by the parent process */ | 360 | /* Reset signal handlers that were set by the parent process */ |
| @@ -418,14 +373,7 @@ static pid_t run(const struct init_action *a) | |||
| 418 | setsid(); | 373 | setsid(); |
| 419 | 374 | ||
| 420 | /* Open the new terminal device */ | 375 | /* Open the new terminal device */ |
| 421 | open_new_terminal(a->terminal, 1); | 376 | open_stdio_to_tty(a->terminal, 1); |
| 422 | |||
| 423 | /* Make sure the terminal will act fairly normal for us */ | ||
| 424 | set_term(); | ||
| 425 | /* Setup stdout, stderr for the new process so | ||
| 426 | * they point to the supplied terminal */ | ||
| 427 | dup(0); | ||
| 428 | dup(0); | ||
| 429 | 377 | ||
| 430 | /* If the init Action requires us to wait, then force the | 378 | /* If the init Action requires us to wait, then force the |
| 431 | * supplied terminal to be the controlling tty. */ | 379 | * supplied terminal to be the controlling tty. */ |
| @@ -433,7 +381,7 @@ static pid_t run(const struct init_action *a) | |||
| 433 | 381 | ||
| 434 | /* Now fork off another process to just hang around */ | 382 | /* Now fork off another process to just hang around */ |
| 435 | if ((pid = fork()) < 0) { | 383 | if ((pid = fork()) < 0) { |
| 436 | message(L_LOG | L_CONSOLE, "Can't fork!"); | 384 | message(L_LOG | L_CONSOLE, "can't fork"); |
| 437 | _exit(1); | 385 | _exit(1); |
| 438 | } | 386 | } |
| 439 | 387 | ||
| @@ -452,7 +400,7 @@ static pid_t run(const struct init_action *a) | |||
| 452 | 400 | ||
| 453 | /* Use a temporary process to steal the controlling tty. */ | 401 | /* Use a temporary process to steal the controlling tty. */ |
| 454 | if ((pid = fork()) < 0) { | 402 | if ((pid = fork()) < 0) { |
| 455 | message(L_LOG | L_CONSOLE, "Can't fork!"); | 403 | message(L_LOG | L_CONSOLE, "can't fork"); |
| 456 | _exit(1); | 404 | _exit(1); |
| 457 | } | 405 | } |
| 458 | if (pid == 0) { | 406 | if (pid == 0) { |
| @@ -489,14 +437,12 @@ static pid_t run(const struct init_action *a) | |||
| 489 | cmdpath = cmd[0]; | 437 | cmdpath = cmd[0]; |
| 490 | 438 | ||
| 491 | /* | 439 | /* |
| 492 | Interactive shells want to see a dash in argv[0]. This | 440 | * Interactive shells want to see a dash in argv[0]. This |
| 493 | typically is handled by login, argv will be setup this | 441 | * typically is handled by login, argv will be setup this |
| 494 | way if a dash appears at the front of the command path | 442 | * way if a dash appears at the front of the command path |
| 495 | (like "-/bin/sh"). | 443 | * (like "-/bin/sh"). |
| 496 | */ | 444 | */ |
| 497 | |||
| 498 | if (*cmdpath == '-') { | 445 | if (*cmdpath == '-') { |
| 499 | |||
| 500 | /* skip over the dash */ | 446 | /* skip over the dash */ |
| 501 | ++cmdpath; | 447 | ++cmdpath; |
| 502 | 448 | ||
| @@ -515,8 +461,8 @@ static pid_t run(const struct init_action *a) | |||
| 515 | /* Establish this process as session leader and | 461 | /* Establish this process as session leader and |
| 516 | * (attempt) to make the tty (if any) a controlling tty. | 462 | * (attempt) to make the tty (if any) a controlling tty. |
| 517 | */ | 463 | */ |
| 518 | (void) setsid(); | 464 | setsid(); |
| 519 | (void) ioctl(0, TIOCSCTTY, 0/*don't steal it*/); | 465 | ioctl(0, TIOCSCTTY, 0 /*don't steal it*/); |
| 520 | #endif | 466 | #endif |
| 521 | } | 467 | } |
| 522 | 468 | ||
| @@ -532,7 +478,7 @@ static pid_t run(const struct init_action *a) | |||
| 532 | * specifies. | 478 | * specifies. |
| 533 | */ | 479 | */ |
| 534 | messageD(L_LOG, "Waiting for enter to start '%s'" | 480 | messageD(L_LOG, "Waiting for enter to start '%s'" |
| 535 | "(pid %d, terminal %s)\n", | 481 | "(pid %d, tty '%s')\n", |
| 536 | cmdpath, getpid(), a->terminal); | 482 | cmdpath, getpid(), a->terminal); |
| 537 | full_write(1, press_enter, sizeof(press_enter) - 1); | 483 | full_write(1, press_enter, sizeof(press_enter) - 1); |
| 538 | while (read(0, &c, 1) == 1 && c != '\n') | 484 | while (read(0, &c, 1) == 1 && c != '\n') |
| @@ -541,7 +487,7 @@ static pid_t run(const struct init_action *a) | |||
| 541 | #endif | 487 | #endif |
| 542 | 488 | ||
| 543 | /* Log the process name and args */ | 489 | /* Log the process name and args */ |
| 544 | message(L_LOG, "Starting pid %d, console %s: '%s'", | 490 | message(L_LOG, "starting pid %d, tty '%s': '%s'", |
| 545 | getpid(), a->terminal, cmdpath); | 491 | getpid(), a->terminal, cmdpath); |
| 546 | 492 | ||
| 547 | #if ENABLE_FEATURE_INIT_COREDUMPS | 493 | #if ENABLE_FEATURE_INIT_COREDUMPS |
| @@ -562,7 +508,8 @@ static pid_t run(const struct init_action *a) | |||
| 562 | BB_EXECVP(cmdpath, cmd); | 508 | BB_EXECVP(cmdpath, cmd); |
| 563 | 509 | ||
| 564 | /* We're still here? Some error happened. */ | 510 | /* We're still here? Some error happened. */ |
| 565 | message(L_LOG | L_CONSOLE, "Bummer, cannot run '%s': %m", cmdpath); | 511 | message(L_LOG | L_CONSOLE, "Cannot run '%s': %s", |
| 512 | cmdpath, strerror(errno)); | ||
| 566 | _exit(-1); | 513 | _exit(-1); |
| 567 | } | 514 | } |
| 568 | sigprocmask(SIG_SETMASK, &omask, NULL); | 515 | sigprocmask(SIG_SETMASK, &omask, NULL); |
| @@ -597,7 +544,8 @@ static void run_actions(int action) | |||
| 597 | for (a = init_action_list; a; a = tmp) { | 544 | for (a = init_action_list; a; a = tmp) { |
| 598 | tmp = a->next; | 545 | tmp = a->next; |
| 599 | if (a->action == action) { | 546 | if (a->action == action) { |
| 600 | if (access(a->terminal, R_OK | W_OK)) { | 547 | /* a->terminal of "" means "init's console" */ |
| 548 | if (a->terminal[0] && access(a->terminal, R_OK | W_OK)) { | ||
| 601 | delete_init_action(a); | 549 | delete_init_action(a); |
| 602 | } else if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { | 550 | } else if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { |
| 603 | waitfor(a, 0); | 551 | waitfor(a, 0); |
| @@ -654,23 +602,21 @@ static void shutdown_system(void) | |||
| 654 | sigaddset(&block_signals, SIGTSTP); | 602 | sigaddset(&block_signals, SIGTSTP); |
| 655 | sigprocmask(SIG_BLOCK, &block_signals, NULL); | 603 | sigprocmask(SIG_BLOCK, &block_signals, NULL); |
| 656 | 604 | ||
| 605 | message(L_CONSOLE | L_LOG, "The system is going down NOW!"); | ||
| 606 | |||
| 657 | /* Allow Ctrl-Alt-Del to reboot system. */ | 607 | /* Allow Ctrl-Alt-Del to reboot system. */ |
| 658 | init_reboot(RB_ENABLE_CAD); | 608 | init_reboot(RB_ENABLE_CAD); |
| 659 | 609 | ||
| 660 | message(L_CONSOLE | L_LOG, "The system is going down NOW !!"); | ||
| 661 | sync(); | ||
| 662 | |||
| 663 | /* Send signals to every process _except_ pid 1 */ | 610 | /* Send signals to every process _except_ pid 1 */ |
| 664 | message(L_CONSOLE | L_LOG, init_sending_format, "TERM"); | 611 | message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "TERM"); |
| 665 | kill(-1, SIGTERM); | 612 | kill(-1, SIGTERM); |
| 666 | sleep(1); | ||
| 667 | sync(); | 613 | sync(); |
| 668 | |||
| 669 | message(L_CONSOLE | L_LOG, init_sending_format, "KILL"); | ||
| 670 | kill(-1, SIGKILL); | ||
| 671 | sleep(1); | 614 | sleep(1); |
| 672 | 615 | ||
| 616 | message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "KILL"); | ||
| 617 | kill(-1, SIGKILL); | ||
| 673 | sync(); | 618 | sync(); |
| 619 | sleep(1); | ||
| 674 | } | 620 | } |
| 675 | 621 | ||
| 676 | static void exec_signal(int sig ATTRIBUTE_UNUSED) | 622 | static void exec_signal(int sig ATTRIBUTE_UNUSED) |
| @@ -697,26 +643,14 @@ static void exec_signal(int sig ATTRIBUTE_UNUSED) | |||
| 697 | sigaddset(&unblock_signals, SIGTSTP); | 643 | sigaddset(&unblock_signals, SIGTSTP); |
| 698 | sigprocmask(SIG_UNBLOCK, &unblock_signals, NULL); | 644 | sigprocmask(SIG_UNBLOCK, &unblock_signals, NULL); |
| 699 | 645 | ||
| 700 | /* Close whatever files are open. */ | ||
| 701 | close(0); | ||
| 702 | close(1); | ||
| 703 | close(2); | ||
| 704 | |||
| 705 | /* Open the new terminal device */ | 646 | /* Open the new terminal device */ |
| 706 | open_new_terminal(a->terminal, 0); | 647 | open_stdio_to_tty(a->terminal, 0); |
| 707 | |||
| 708 | /* Make sure the terminal will act fairly normal for us */ | ||
| 709 | set_term(); | ||
| 710 | /* Setup stdout, stderr on the supplied terminal */ | ||
| 711 | dup(0); | ||
| 712 | dup(0); | ||
| 713 | 648 | ||
| 714 | messageD(L_CONSOLE | L_LOG, "Trying to re-exec %s", a->command); | 649 | messageD(L_CONSOLE | L_LOG, "Trying to re-exec %s", a->command); |
| 715 | BB_EXECLP(a->command, a->command, NULL); | 650 | BB_EXECLP(a->command, a->command, NULL); |
| 716 | 651 | ||
| 717 | message(L_CONSOLE | L_LOG, "exec of '%s' failed: %m", | 652 | message(L_CONSOLE | L_LOG, "Cannot run '%s': %s", |
| 718 | a->command); | 653 | a->command, strerror(errno)); |
| 719 | sync(); | ||
| 720 | sleep(2); | 654 | sleep(2); |
| 721 | init_reboot(RB_HALT_SYSTEM); | 655 | init_reboot(RB_HALT_SYSTEM); |
| 722 | loop_forever(); | 656 | loop_forever(); |
| @@ -741,11 +675,8 @@ static void shutdown_signal(int sig) | |||
| 741 | rb = RB_POWER_OFF; | 675 | rb = RB_POWER_OFF; |
| 742 | } | 676 | } |
| 743 | message(L_CONSOLE | L_LOG, "Requesting system %s", m); | 677 | message(L_CONSOLE | L_LOG, "Requesting system %s", m); |
| 744 | sync(); | ||
| 745 | |||
| 746 | /* allow time for last message to reach serial console */ | 678 | /* allow time for last message to reach serial console */ |
| 747 | sleep(2); | 679 | sleep(2); |
| 748 | |||
| 749 | init_reboot(rb); | 680 | init_reboot(rb); |
| 750 | loop_forever(); | 681 | loop_forever(); |
| 751 | } | 682 | } |
| @@ -779,14 +710,9 @@ static void new_init_action(int action, const char *command, const char *cons) | |||
| 779 | { | 710 | { |
| 780 | struct init_action *new_action, *a, *last; | 711 | struct init_action *new_action, *a, *last; |
| 781 | 712 | ||
| 782 | if (*cons == '\0') | ||
| 783 | cons = console_name; | ||
| 784 | |||
| 785 | if (strcmp(cons, bb_dev_null) == 0 && (action & ASKFIRST)) | 713 | if (strcmp(cons, bb_dev_null) == 0 && (action & ASKFIRST)) |
| 786 | return; | 714 | return; |
| 787 | 715 | ||
| 788 | new_action = xzalloc(sizeof(struct init_action)); | ||
| 789 | |||
| 790 | /* Append to the end of the list */ | 716 | /* Append to the end of the list */ |
| 791 | for (a = last = init_action_list; a; a = a->next) { | 717 | for (a = last = init_action_list; a; a = a->next) { |
| 792 | /* don't enter action if it's already in the list, | 718 | /* don't enter action if it's already in the list, |
| @@ -795,11 +721,12 @@ static void new_init_action(int action, const char *command, const char *cons) | |||
| 795 | && (strcmp(a->terminal, cons) == 0) | 721 | && (strcmp(a->terminal, cons) == 0) |
| 796 | ) { | 722 | ) { |
| 797 | a->action = action; | 723 | a->action = action; |
| 798 | free(new_action); | ||
| 799 | return; | 724 | return; |
| 800 | } | 725 | } |
| 801 | last = a; | 726 | last = a; |
| 802 | } | 727 | } |
| 728 | |||
| 729 | new_action = xzalloc(sizeof(struct init_action)); | ||
| 803 | if (last) { | 730 | if (last) { |
| 804 | last->next = new_action; | 731 | last->next = new_action; |
| 805 | } else { | 732 | } else { |
| @@ -808,7 +735,7 @@ static void new_init_action(int action, const char *command, const char *cons) | |||
| 808 | strcpy(new_action->command, command); | 735 | strcpy(new_action->command, command); |
| 809 | new_action->action = action; | 736 | new_action->action = action; |
| 810 | strcpy(new_action->terminal, cons); | 737 | strcpy(new_action->terminal, cons); |
| 811 | messageD(L_LOG | L_CONSOLE, "command='%s' action='%d' terminal='%s'\n", | 738 | messageD(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n", |
| 812 | new_action->command, new_action->action, new_action->terminal); | 739 | new_action->command, new_action->action, new_action->terminal); |
| 813 | } | 740 | } |
| 814 | 741 | ||
| @@ -841,11 +768,10 @@ static void parse_inittab(void) | |||
| 841 | #if ENABLE_FEATURE_USE_INITTAB | 768 | #if ENABLE_FEATURE_USE_INITTAB |
| 842 | FILE *file; | 769 | FILE *file; |
| 843 | char buf[INIT_BUFFS_SIZE], lineAsRead[INIT_BUFFS_SIZE]; | 770 | char buf[INIT_BUFFS_SIZE], lineAsRead[INIT_BUFFS_SIZE]; |
| 844 | char tmpConsole[CONSOLE_BUFF_SIZE]; | 771 | char tmpConsole[CONSOLE_NAME_SIZE]; |
| 845 | char *id, *runlev, *action, *command, *eol; | 772 | char *id, *runlev, *action, *command, *eol; |
| 846 | const struct init_action_type *a = actions; | 773 | const struct init_action_type *a = actions; |
| 847 | 774 | ||
| 848 | |||
| 849 | file = fopen(INITTAB, "r"); | 775 | file = fopen(INITTAB, "r"); |
| 850 | if (file == NULL) { | 776 | if (file == NULL) { |
| 851 | /* No inittab file -- set up some default behavior */ | 777 | /* No inittab file -- set up some default behavior */ |
| @@ -925,7 +851,7 @@ static void parse_inittab(void) | |||
| 925 | id += 5; | 851 | id += 5; |
| 926 | strcpy(tmpConsole, "/dev/"); | 852 | strcpy(tmpConsole, "/dev/"); |
| 927 | safe_strncpy(tmpConsole + 5, id, | 853 | safe_strncpy(tmpConsole + 5, id, |
| 928 | CONSOLE_BUFF_SIZE - 5); | 854 | sizeof(tmpConsole) - 5); |
| 929 | id = tmpConsole; | 855 | id = tmpConsole; |
| 930 | } | 856 | } |
| 931 | new_init_action(a->action, command, id); | 857 | new_init_action(a->action, command, id); |
| @@ -938,7 +864,6 @@ static void parse_inittab(void) | |||
| 938 | } | 864 | } |
| 939 | } | 865 | } |
| 940 | fclose(file); | 866 | fclose(file); |
| 941 | return; | ||
| 942 | #endif /* FEATURE_USE_INITTAB */ | 867 | #endif /* FEATURE_USE_INITTAB */ |
| 943 | } | 868 | } |
| 944 | 869 | ||
| @@ -947,7 +872,7 @@ static void reload_signal(int sig ATTRIBUTE_UNUSED) | |||
| 947 | { | 872 | { |
| 948 | struct init_action *a, *tmp; | 873 | struct init_action *a, *tmp; |
| 949 | 874 | ||
| 950 | message(L_LOG, "Reloading /etc/inittab"); | 875 | message(L_LOG, "reloading /etc/inittab"); |
| 951 | 876 | ||
| 952 | /* disable old entrys */ | 877 | /* disable old entrys */ |
| 953 | for (a = init_action_list; a; a = a->next ) { | 878 | for (a = init_action_list; a; a = a->next ) { |
| @@ -965,7 +890,6 @@ static void reload_signal(int sig ATTRIBUTE_UNUSED) | |||
| 965 | } | 890 | } |
| 966 | } | 891 | } |
| 967 | run_actions(RESPAWN); | 892 | run_actions(RESPAWN); |
| 968 | return; | ||
| 969 | } | 893 | } |
| 970 | #endif /* FEATURE_USE_INITTAB */ | 894 | #endif /* FEATURE_USE_INITTAB */ |
| 971 | 895 | ||
| @@ -978,13 +902,13 @@ int init_main(int argc, char **argv) | |||
| 978 | die_sleep = 30 * 24*60*60; /* if xmalloc will ever die... */ | 902 | die_sleep = 30 * 24*60*60; /* if xmalloc will ever die... */ |
| 979 | 903 | ||
| 980 | if (argc > 1 && !strcmp(argv[1], "-q")) { | 904 | if (argc > 1 && !strcmp(argv[1], "-q")) { |
| 981 | return kill(1,SIGHUP); | 905 | return kill(1, SIGHUP); |
| 982 | } | 906 | } |
| 983 | #if !ENABLE_DEBUG_INIT | 907 | #if !ENABLE_DEBUG_INIT |
| 984 | /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ | 908 | /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ |
| 985 | if (getpid() != 1 && | 909 | if (getpid() != 1 |
| 986 | (!ENABLE_FEATURE_INITRD || !strstr(applet_name, "linuxrc"))) | 910 | && (!ENABLE_FEATURE_INITRD || !strstr(applet_name, "linuxrc")) |
| 987 | { | 911 | ) { |
| 988 | bb_show_usage(); | 912 | bb_show_usage(); |
| 989 | } | 913 | } |
| 990 | /* Set up sig handlers -- be sure to | 914 | /* Set up sig handlers -- be sure to |
| @@ -1006,17 +930,7 @@ int init_main(int argc, char **argv) | |||
| 1006 | 930 | ||
| 1007 | /* Figure out where the default console should be */ | 931 | /* Figure out where the default console should be */ |
| 1008 | console_init(); | 932 | console_init(); |
| 1009 | 933 | set_term(); | |
| 1010 | /* Close whatever files are open, and reset the console. */ | ||
| 1011 | close(0); | ||
| 1012 | close(1); | ||
| 1013 | close(2); | ||
| 1014 | |||
| 1015 | if (device_open(console_name, O_RDWR | O_NOCTTY) == 0) { | ||
| 1016 | set_term(); | ||
| 1017 | close(0); | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | chdir("/"); | 934 | chdir("/"); |
| 1021 | setsid(); | 935 | setsid(); |
| 1022 | { | 936 | { |
| @@ -1029,7 +943,7 @@ int init_main(int argc, char **argv) | |||
| 1029 | if (argc > 1) setenv("RUNLEVEL", argv[1], 1); | 943 | if (argc > 1) setenv("RUNLEVEL", argv[1], 1); |
| 1030 | 944 | ||
| 1031 | /* Hello world */ | 945 | /* Hello world */ |
| 1032 | message(MAYBE_CONSOLE | L_LOG, "init started: %s", bb_msg_full_version); | 946 | message(MAYBE_CONSOLE | L_LOG, "init started: %s", bb_msg_full_version); |
| 1033 | 947 | ||
| 1034 | /* Make sure there is enough memory to do something useful. */ | 948 | /* Make sure there is enough memory to do something useful. */ |
| 1035 | if (ENABLE_SWAPONOFF) { | 949 | if (ENABLE_SWAPONOFF) { |
| @@ -1038,7 +952,7 @@ int init_main(int argc, char **argv) | |||
| 1038 | if (!sysinfo(&info) && | 952 | if (!sysinfo(&info) && |
| 1039 | (info.mem_unit ? : 1) * (long long)info.totalram < 1024*1024) | 953 | (info.mem_unit ? : 1) * (long long)info.totalram < 1024*1024) |
| 1040 | { | 954 | { |
| 1041 | message(L_CONSOLE, "Low memory: forcing swapon."); | 955 | message(L_CONSOLE, "Low memory, forcing swapon"); |
| 1042 | /* swapon -a requires /proc typically */ | 956 | /* swapon -a requires /proc typically */ |
| 1043 | new_init_action(SYSINIT, "mount -t proc proc /proc", ""); | 957 | new_init_action(SYSINIT, "mount -t proc proc /proc", ""); |
| 1044 | /* Try to turn on swap */ | 958 | /* Try to turn on swap */ |
| @@ -1073,7 +987,7 @@ int init_main(int argc, char **argv) | |||
| 1073 | } else if (enforce > 0) { | 987 | } else if (enforce > 0) { |
| 1074 | /* SELinux in enforcing mode but load_policy failed */ | 988 | /* SELinux in enforcing mode but load_policy failed */ |
| 1075 | /* At this point, we probably can't open /dev/console, so log() won't work */ | 989 | /* At this point, we probably can't open /dev/console, so log() won't work */ |
| 1076 | message(CONSOLE,"Unable to load SELinux Policy. Machine is in enforcing mode. Halting now."); | 990 | message(CONSOLE, "Cannot load SELinux Policy. Machine is in enforcing mode. Halting now."); |
| 1077 | exit(1); | 991 | exit(1); |
| 1078 | } | 992 | } |
| 1079 | } | 993 | } |
| @@ -1121,7 +1035,7 @@ int init_main(int argc, char **argv) | |||
| 1121 | /* Set the pid to 0 so that the process gets | 1035 | /* Set the pid to 0 so that the process gets |
| 1122 | * restarted by run_actions() */ | 1036 | * restarted by run_actions() */ |
| 1123 | a->pid = 0; | 1037 | a->pid = 0; |
| 1124 | message(L_LOG, "Process '%s' (pid %d) exited. " | 1038 | message(L_LOG, "process '%s' (pid %d) exited. " |
| 1125 | "Scheduling it for restart.", | 1039 | "Scheduling it for restart.", |
| 1126 | a->command, wpid); | 1040 | a->command, wpid); |
| 1127 | } | 1041 | } |
diff --git a/init/init_shared.c b/init/init_shared.c deleted file mode 100644 index 47480fc21..000000000 --- a/init/init_shared.c +++ /dev/null | |||
| @@ -1,63 +0,0 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * Stuff shared between init, reboot, halt, and poweroff | ||
| 4 | * | ||
| 5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
| 6 | * | ||
| 7 | * Licensed under GPL version 2, see file LICENSE in this tarball for details. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include "busybox.h" | ||
| 11 | #include <sys/reboot.h> | ||
| 12 | #include <sys/syslog.h> | ||
| 13 | #include "init_shared.h" | ||
| 14 | |||
| 15 | const char * const init_sending_format = "Sending SIG%s to all processes."; | ||
| 16 | #ifndef CONFIG_INIT | ||
| 17 | const char * const bb_shutdown_format = "\r%s\n"; | ||
| 18 | int bb_shutdown_system(unsigned long magic) | ||
| 19 | { | ||
| 20 | int pri = LOG_KERN|LOG_NOTICE|LOG_FACMASK; | ||
| 21 | const char *message; | ||
| 22 | |||
| 23 | /* Don't kill ourself */ | ||
| 24 | signal(SIGTERM,SIG_IGN); | ||
| 25 | signal(SIGHUP,SIG_IGN); | ||
| 26 | bb_setpgrp; | ||
| 27 | |||
| 28 | /* Allow Ctrl-Alt-Del to reboot system. */ | ||
| 29 | #ifndef RB_ENABLE_CAD | ||
| 30 | #define RB_ENABLE_CAD 0x89abcdef | ||
| 31 | #endif | ||
| 32 | reboot(RB_ENABLE_CAD); | ||
| 33 | |||
| 34 | openlog(applet_name, 0, pri); | ||
| 35 | |||
| 36 | message = "\nThe system is going down NOW !!"; | ||
| 37 | syslog(pri, "%s", message); | ||
| 38 | printf(bb_shutdown_format, message); | ||
| 39 | |||
| 40 | sync(); | ||
| 41 | |||
| 42 | /* Send signals to every process _except_ pid 1 */ | ||
| 43 | message = "TERM"; | ||
| 44 | syslog(pri, init_sending_format, message); | ||
| 45 | printf(bb_shutdown_format, message); | ||
| 46 | |||
| 47 | kill(-1, SIGTERM); | ||
| 48 | sleep(1); | ||
| 49 | sync(); | ||
| 50 | |||
| 51 | message = "KILL"; | ||
| 52 | syslog(pri, init_sending_format, message); | ||
| 53 | printf(bb_shutdown_format, message); | ||
| 54 | |||
| 55 | kill(-1, SIGKILL); | ||
| 56 | sleep(1); | ||
| 57 | |||
| 58 | sync(); | ||
| 59 | |||
| 60 | reboot(magic); | ||
| 61 | return 0; /* Shrug */ | ||
| 62 | } | ||
| 63 | #endif | ||
diff --git a/init/init_shared.h b/init/init_shared.h deleted file mode 100644 index 6df8de484..000000000 --- a/init/init_shared.h +++ /dev/null | |||
| @@ -1,10 +0,0 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * Helper functions shared by init et al. | ||
| 4 | * | ||
| 5 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
| 6 | */ | ||
| 7 | extern int kill_init(int sig); | ||
| 8 | extern int bb_shutdown_system(unsigned long magic); | ||
| 9 | extern const char * const init_sending_format; | ||
| 10 | |||
