blob: 5e183e61f103d82cdb255d3ec79f62ce66a7d99d [file] [log] [blame]
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001/*
2 devfsd implementation for busybox
3
4 Copyright (C) 2003 by Tito Ragusa <farmatito@tiscali.it>
5
6 Busybox version is based on some previous work and ideas
7 Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it>
8
9 devfsd.c
10
11 Main file for devfsd (devfs daemon for Linux).
12
13 Copyright (C) 1998-2002 Richard Gooch
14
15 devfsd.h
16
17 Header file for devfsd (devfs daemon for Linux).
18
19 Copyright (C) 1998-2000 Richard Gooch
20
21 compat_name.c
22
23 Compatibility name file for devfsd (build compatibility names).
24
25 Copyright (C) 1998-2002 Richard Gooch
26
27 expression.c
28
29 This code provides Borne Shell-like expression expansion.
30
31 Copyright (C) 1997-1999 Richard Gooch
32
33 This program is free software; you can redistribute it and/or modify
34 it under the terms of the GNU General Public License as published by
35 the Free Software Foundation; either version 2 of the License, or
36 (at your option) any later version.
37
38 This program is distributed in the hope that it will be useful,
39 but WITHOUT ANY WARRANTY; without even the implied warranty of
40 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
41 GNU General Public License for more details.
42
43 You should have received a copy of the GNU General Public License
44 along with this program; if not, write to the Free Software
45 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
46
47 Richard Gooch may be reached by email at rgooch@atnf.csiro.au
48 The postal address is:
49 Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
50*/
51
52#include "libbb.h"
53#include "busybox.h"
54#include <unistd.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <stdarg.h>
58#include <string.h>
59#include <ctype.h>
Glenn L McGrath17d21fa2003-10-09 11:46:23 +000060#include <sys/time.h>
61#include <sys/stat.h>
62#include <sys/types.h>
63#include <sys/wait.h>
64#include <sys/ioctl.h>
65#include <sys/socket.h>
66#include <sys/un.h>
67#include <dirent.h>
68#include <fcntl.h>
Glenn L McGrath17d21fa2003-10-09 11:46:23 +000069#include <syslog.h>
70#include <signal.h>
71#include <regex.h>
72#include <errno.h>
Eric Andersen2af70002003-10-09 21:02:23 +000073#include <sys/sysmacros.h>
Glenn L McGrath17d21fa2003-10-09 11:46:23 +000074
Eric Andersen2af70002003-10-09 21:02:23 +000075
76/* Various defines taken from linux/major.h */
77#define IDE0_MAJOR 3
78#define IDE1_MAJOR 22
79#define IDE2_MAJOR 33
80#define IDE3_MAJOR 34
81#define IDE4_MAJOR 56
82#define IDE5_MAJOR 57
83#define IDE6_MAJOR 88
84#define IDE7_MAJOR 89
85#define IDE8_MAJOR 90
86#define IDE9_MAJOR 91
87
88
89/* Various defines taken from linux/devfs_fs.h */
90#define DEVFSD_PROTOCOL_REVISION_KERNEL 5
91#define DEVFSD_IOCTL_BASE 'd'
92/* These are the various ioctls */
93#define DEVFSDIOC_GET_PROTO_REV _IOR(DEVFSD_IOCTL_BASE, 0, int)
94#define DEVFSDIOC_SET_EVENT_MASK _IOW(DEVFSD_IOCTL_BASE, 2, int)
95#define DEVFSDIOC_RELEASE_EVENT_QUEUE _IOW(DEVFSD_IOCTL_BASE, 3, int)
Eric Andersenf18bd892003-12-19 11:07:59 +000096#define DEVFSDIOC_SET_CONFIG_DEBUG_MASK _IOW(DEVFSD_IOCTL_BASE, 4, int)
Eric Andersen2af70002003-10-09 21:02:23 +000097#define DEVFSD_NOTIFY_REGISTERED 0
98#define DEVFSD_NOTIFY_UNREGISTERED 1
99#define DEVFSD_NOTIFY_ASYNC_OPEN 2
100#define DEVFSD_NOTIFY_CLOSE 3
101#define DEVFSD_NOTIFY_LOOKUP 4
102#define DEVFSD_NOTIFY_CHANGE 5
103#define DEVFSD_NOTIFY_CREATE 6
104#define DEVFSD_NOTIFY_DELETE 7
Eric Andersenf18bd892003-12-19 11:07:59 +0000105#define DEVFS_PATHLEN 1024
106/* Never change this otherwise the binary interface will change */
107
Eric Andersen2af70002003-10-09 21:02:23 +0000108struct devfsd_notify_struct
109{ /* Use native C types to ensure same types in kernel and user space */
110 unsigned int type; /* DEVFSD_NOTIFY_* value */
111 unsigned int mode; /* Mode of the inode or device entry */
112 unsigned int major; /* Major number of device entry */
113 unsigned int minor; /* Minor number of device entry */
114 unsigned int uid; /* Uid of process, inode or device entry */
115 unsigned int gid; /* Gid of process, inode or device entry */
116 unsigned int overrun_count; /* Number of lost events */
117 unsigned int namelen; /* Number of characters not including '\0' */
118 /* The device name MUST come last */
119 char devname[DEVFS_PATHLEN]; /* This will be '\0' terminated */
120};
121
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000122#define BUFFER_SIZE 16384
123#define DEVFSD_VERSION "1.3.25"
124#define CONFIG_FILE "/etc/devfsd.conf"
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000125#define MODPROBE "/sbin/modprobe"
Eric Andersenf18bd892003-12-19 11:07:59 +0000126#define MODPROBE_SWITCH_1 "-k"
127#define MODPROBE_SWITCH_2 "-C"
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000128#define CONFIG_MODULES_DEVFS "/etc/modules.devfs"
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000129#define MAX_ARGS (6 + 1)
130#define MAX_SUBEXPR 10
131#define STRING_LENGTH 255
132
133/* for get_uid_gid() */
134#define UID 0
135#define GID 1
136
137/* for msg_logger(), do_ioctl(),
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000138 fork_and_execute() */
139# define DIE 1
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000140# define NO_DIE 0
141
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000142/* for dir_operation() */
143#define RESTORE 0
144#define SERVICE 1
145#define READ_CONFIG 2
146
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000147/* Update only after changing code to reflect new protocol */
148#define DEVFSD_PROTOCOL_REVISION_DAEMON 5
149
150/* Compile-time check */
151#if DEVFSD_PROTOCOL_REVISION_KERNEL != DEVFSD_PROTOCOL_REVISION_DAEMON
152#error protocol version mismatch. Update your kernel headers
153#endif
154
155#define AC_PERMISSIONS 0
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000156#define AC_MODLOAD 1
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000157#define AC_EXECUTE 2
158#define AC_MFUNCTION 3 /* not supported by busybox */
159#define AC_CFUNCTION 4 /* not supported by busybox */
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000160#define AC_COPY 5
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000161#define AC_IGNORE 6
162#define AC_MKOLDCOMPAT 7
163#define AC_MKNEWCOMPAT 8
164#define AC_RMOLDCOMPAT 9
165#define AC_RMNEWCOMPAT 10
166#define AC_RESTORE 11
167
168
169struct permissions_type
170{
171 mode_t mode;
172 uid_t uid;
173 gid_t gid;
174};
175
176struct execute_type
177{
178 char *argv[MAX_ARGS + 1]; /* argv[0] must always be the programme */
179};
180
181struct copy_type
182{
183 const char *source;
184 const char *destination;
185};
186
187struct action_type
188{
189 unsigned int what;
190 unsigned int when;
191};
192
193struct config_entry_struct
194{
195 struct action_type action;
196 regex_t preg;
197 union
198 {
199 struct permissions_type permissions;
200 struct execute_type execute;
201 struct copy_type copy;
202 }
203 u;
204 struct config_entry_struct *next;
205};
206
207struct get_variable_info
208{
209 const struct devfsd_notify_struct *info;
210 const char *devname;
211 char devpath[STRING_LENGTH];
212};
213
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000214static void dir_operation(int , const char * , int, unsigned long* );
215static void service(struct stat statbuf, char *path);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000216static int st_expr_expand(char *, unsigned, const char *, const char *(*) (const char *, void *), void *);
217static const char *get_old_name(const char *, unsigned, char *, unsigned, unsigned);
218static int mksymlink (const char *oldpath, const char *newpath);
Eric Andersenf18bd892003-12-19 11:07:59 +0000219static void read_config_file (char *path, int optional, unsigned long *event_mask);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000220static void process_config_line (const char *, unsigned long *);
221static int do_servicing (int, unsigned long);
222static void service_name (const struct devfsd_notify_struct *);
223static void action_permissions (const struct devfsd_notify_struct *, const struct config_entry_struct *);
224static void action_execute (const struct devfsd_notify_struct *, const struct config_entry_struct *,
225 const regmatch_t *, unsigned);
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000226#ifdef CONFIG_DEVFSD_MODLOAD
227static void action_modload (const struct devfsd_notify_struct *info, const struct config_entry_struct *entry);
228#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000229static void action_copy (const struct devfsd_notify_struct *, const struct config_entry_struct *,
230 const regmatch_t *, unsigned);
231static void action_compat (const struct devfsd_notify_struct *, unsigned);
232static void free_config (void);
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000233static void restore(char *spath, struct stat source_stat, int rootlen);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000234static int copy_inode (const char *, const struct stat *, mode_t, const char *, const struct stat *);
235static mode_t get_mode (const char *);
236static void signal_handler (int);
237static const char *get_variable (const char *, void *);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000238static int make_dir_tree (const char *);
239static int expand_expression(char *, unsigned, const char *, const char *(*)(const char *, void *), void *,
240 const char *, const regmatch_t *, unsigned );
241static void expand_regexp (char *, size_t, const char *, const char *, const regmatch_t *, unsigned );
242static const char *expand_variable( char *, unsigned, unsigned *, const char *,
243 const char *(*) (const char *, void *), void * );
244static const char *get_variable_v2(const char *, const char *(*) (const char *, void *), void *);
245static char get_old_ide_name (unsigned , unsigned);
246static char *write_old_sd_name (char *, unsigned, unsigned, char *);
247
248/* busybox functions */
Glenn L McGrathfa134cd2004-02-22 07:38:36 +0000249#if defined(CONFIG_DEVFSD_VERBOSE) || defined(CONFIG_DEBUG)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000250static void msg_logger(int die, int pri, const char * fmt, ... );
Eric Andersenf18bd892003-12-19 11:07:59 +0000251#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000252static void do_ioctl(int die, int fd, int request, unsigned long event_mask_flag);
253static void fork_and_execute(int die, char *arg0, char **arg );
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000254static int get_uid_gid ( int, const char *);
255static void safe_memcpy( char * dest, const char * src, int len);
Eric Andersenf18bd892003-12-19 11:07:59 +0000256static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, char *ptr);
257static unsigned int scan_dev_name(const char *d, unsigned int n, char *ptr);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000258
259/* Structs and vars */
260static struct config_entry_struct *first_config = NULL;
261static struct config_entry_struct *last_config = NULL;
262static const char *mount_point = NULL;
263static volatile int caught_signal = FALSE;
264static volatile int caught_sighup = FALSE;
265static struct initial_symlink_struct
266{
267 char *dest;
268 char *name;
269} initial_symlinks[] =
270{
271 {"/proc/self/fd", "fd"},
272 {"fd/0", "stdin"},
273 {"fd/1", "stdout"},
274 {"fd/2", "stderr"},
275 {NULL, NULL},
276};
277
278static struct event_type
279{
280 unsigned int type; /* The DEVFSD_NOTIFY_* value */
281 const char *config_name; /* The name used in the config file */
282} event_types[] =
283{
284 {DEVFSD_NOTIFY_REGISTERED, "REGISTER"},
285 {DEVFSD_NOTIFY_UNREGISTERED, "UNREGISTER"},
286 {DEVFSD_NOTIFY_ASYNC_OPEN, "ASYNC_OPEN"},
287 {DEVFSD_NOTIFY_CLOSE, "CLOSE"},
288 {DEVFSD_NOTIFY_LOOKUP, "LOOKUP"},
289 {DEVFSD_NOTIFY_CHANGE, "CHANGE"},
290 {DEVFSD_NOTIFY_CREATE, "CREATE"},
291 {DEVFSD_NOTIFY_DELETE, "DELETE"},
292 {0xffffffff, NULL}
293};
294
295/* busybox functions and messages */
296
297extern void xregcomp(regex_t * preg, const char *regex, int cflags);
298
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000299const char * const bb_msg_proto_rev = "protocol revision";
300#ifdef CONFIG_DEVFSD_VERBOSE
Eric Andersenf18bd892003-12-19 11:07:59 +0000301const char * const bb_msg_bad_config = "bad %s config file: %s\n";
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000302const char * const bb_msg_small_buffer = "buffer too small\n";
Eric Andersenf18bd892003-12-19 11:07:59 +0000303const char * const bb_msg_variable_not_found = "variable: %s not found\n";
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000304#endif
305
Glenn L McGrathfa134cd2004-02-22 07:38:36 +0000306#if defined(CONFIG_DEVFSD_VERBOSE) || defined(CONFIG_DEBUG)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000307static void msg_logger(int die, int pri, const char * fmt, ... )
308{
309 va_list ap;
310
311 va_start(ap, fmt);
312 if (access ("/dev/log", F_OK) == 0)
313 {
314 openlog(bb_applet_name, 0, LOG_DAEMON);
315 vsyslog( pri , fmt , ap);
316 closelog();
317 }
Eric Andersenf18bd892003-12-19 11:07:59 +0000318#ifndef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000319 else
320#endif
321 bb_verror_msg(fmt, ap);
322 va_end(ap);
323 if(die==DIE)
324 exit(EXIT_FAILURE);
325}
Eric Andersenf18bd892003-12-19 11:07:59 +0000326#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000327
328static void do_ioctl(int die, int fd, int request, unsigned long event_mask_flag)
329{
Eric Andersenf18bd892003-12-19 11:07:59 +0000330#ifdef CONFIG_DEVFSD_VERBOSE
331 if (ioctl (fd, request, event_mask_flag) == -1)
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000332 msg_logger(die, LOG_ERR, "ioctl(): %m\n");
Eric Andersenf18bd892003-12-19 11:07:59 +0000333#else
334 if (ioctl (fd, request, event_mask_flag) == -1)
335 exit(EXIT_FAILURE);
336#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000337}
338
339static void fork_and_execute(int die, char *arg0, char **arg )
340{
341 switch ( fork () )
342 {
343 case 0:
344 /* Child */
345 break;
346 case -1:
347 /* Parent: Error : die or return */
Eric Andersenf18bd892003-12-19 11:07:59 +0000348#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000349 msg_logger(die, LOG_ERR,(char *) bb_msg_memory_exhausted);
Eric Andersenf18bd892003-12-19 11:07:59 +0000350#else
351 if(die == DIE)
352 exit(EXIT_FAILURE);
353#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000354 return;
355 default:
356 /* Parent : ok : return or exit */
357 if(arg0 != NULL)
358 {
359 wait (NULL);
360 return;
361 }
362 exit (EXIT_SUCCESS);
363 }
364 /* Child : if arg0 != NULL do execvp */
365 if(arg0 != NULL )
366 {
367 execvp (arg0, arg);
Eric Andersenf18bd892003-12-19 11:07:59 +0000368#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000369 msg_logger(DIE, LOG_ERR, "execvp(): %s: %m\n", arg0);
Eric Andersenf18bd892003-12-19 11:07:59 +0000370#else
371 exit(EXIT_FAILURE);
372#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000373 }
374}
375
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000376static void safe_memcpy( char *dest, const char *src, int len)
377{
378 memcpy (dest , src , len );
379 dest[len] = '\0';
380}
381
Eric Andersenf18bd892003-12-19 11:07:59 +0000382static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, char *ptr)
383{
384 if( d[n - 4]=='d' && d[n - 3]=='i' && d[n - 2]=='s' && d[n - 1]=='c')
385 return (2 + addendum);
386 else if( d[n - 2]=='c' && d[n - 1]=='d')
387 return (3 + addendum);
388 else if(ptr[0]=='p' && ptr[1]=='a' && ptr[2]=='r' && ptr[3]=='t')
389 return (4 + addendum);
390 else if( ptr[n - 2]=='m' && ptr[n - 1]=='t')
391 return (5 + addendum);
392 else
393 return 0;
394}
395
396static unsigned int scan_dev_name(const char *d, unsigned int n, char *ptr)
397{
398 if(d[0]=='s' && d[1]=='c' && d[2]=='s' && d[3]=='i' && d[4]=='/')
399 {
400 if( d[n - 7]=='g' && d[n - 6]=='e' && d[n - 5]=='n' &&
401 d[n - 4]=='e' && d[n - 3]=='r' && d[n - 2]=='i' &&
402 d[n - 1]=='c' )
403 return 1;
404 return scan_dev_name_common(d, n, 0, ptr);
405 }
406 else if(d[0]=='i' && d[1]=='d' && d[2]=='e' && d[3]=='/' &&
407 d[4]=='h' && d[5]=='o' && d[6]=='s' && d[7]=='t')
408 {
409 return scan_dev_name_common(d, n, 4, ptr);
410 }
411 else if(d[0]=='s' && d[1]=='b' && d[2]=='p' && d[3]=='/')
412 {
413 return 10;
414 }
415 else if(d[0]=='v' && d[1]=='c' && d[2]=='c' && d[3]=='/')
416 {
417 return 11;
418 }
419 else if(d[0]=='p' && d[1]=='t' && d[2]=='y' && d[3]=='/')
420 {
421 return 12;
422 }
423 return 0;
424}
425
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000426/* Public functions follow */
427
428int devfsd_main (int argc, char **argv)
429{
430 int print_version = FALSE;
Eric Andersenf18bd892003-12-19 11:07:59 +0000431#ifdef CONFIG_DEVFSD_FG_NP
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000432 int do_daemon = TRUE;
433 int no_polling = FALSE;
Eric Andersenf18bd892003-12-19 11:07:59 +0000434#endif
435 int do_scan;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000436 int fd, proto_rev, count;
437 unsigned long event_mask = 0;
438 struct sigaction new_action;
439 struct initial_symlink_struct *curr;
440
441 if (argc < 2)
442 bb_show_usage();
443
444 for (count = 2; count < argc; ++count)
445 {
Eric Andersenf18bd892003-12-19 11:07:59 +0000446 if(argv[count][0] == '-')
447 {
448 if(argv[count][1]=='v' && !argv[count][2]) /* -v */
449 print_version = TRUE;
450#ifdef CONFIG_DEVFSD_FG_NP
451 else if(argv[count][1]=='f' && argv[count][2]=='g' && !argv[count][3]) /* -fg */
452 do_daemon = FALSE;
453 else if(argv[count][1]=='n' && argv[count][2]=='p' && !argv[count][3]) /* -np */
454 no_polling = TRUE;
455#endif
456 else
457 bb_show_usage();
458 }
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000459 }
460
Eric Andersenf18bd892003-12-19 11:07:59 +0000461 /* strip last / from mount point, so we don't need to check for it later */
462 while( argv[1][1]!='\0' && argv[1][strlen(argv[1])-1] == '/' )
463 argv[1][strlen(argv[1]) -1] = '\0';
464
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000465 mount_point = argv[1];
466
467 if (chdir (mount_point) != 0)
Eric Andersenf18bd892003-12-19 11:07:59 +0000468#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000469 bb_error_msg_and_die( " %s: %m", mount_point);
Eric Andersenf18bd892003-12-19 11:07:59 +0000470#else
471 exit(EXIT_FAILURE);
472#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000473
474 fd = bb_xopen (".devfsd", O_RDONLY);
475
476 if (fcntl (fd, F_SETFD, FD_CLOEXEC) != 0)
Eric Andersenf18bd892003-12-19 11:07:59 +0000477#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000478 bb_error_msg( "FD_CLOEXEC");
Eric Andersenf18bd892003-12-19 11:07:59 +0000479#else
480 exit(EXIT_FAILURE);
481#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000482
483 do_ioctl(DIE, fd, DEVFSDIOC_GET_PROTO_REV,(int )&proto_rev);
484
485 /*setup initial entries */
486 for (curr = initial_symlinks; curr->dest != NULL; ++curr)
487 symlink (curr->dest, curr->name);
488
489 /* NB: The check for CONFIG_FILE is done in read_config_file() */
490
491 if ( print_version || (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev) )
492 {
493 bb_printf( "%s v%s\nDaemon %s:\t%d\nKernel-side %s:\t%d\n",
494 bb_applet_name,DEVFSD_VERSION,bb_msg_proto_rev,
495 DEVFSD_PROTOCOL_REVISION_DAEMON,bb_msg_proto_rev, proto_rev);
496 if (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev)
497 bb_error_msg_and_die( "%s mismatch!",bb_msg_proto_rev);
498 exit(EXIT_SUCCESS); /* -v */
499 }
500 /* Tell kernel we are special (i.e. we get to see hidden entries) */
501 do_ioctl(DIE, fd, DEVFSDIOC_SET_EVENT_MASK, 0);
502
503 sigemptyset (&new_action.sa_mask);
504 new_action.sa_flags = 0;
505
506 /* Set up SIGHUP and SIGUSR1 handlers */
507 new_action.sa_handler = signal_handler;
508 if (sigaction (SIGHUP, &new_action, NULL) != 0 || sigaction (SIGUSR1, &new_action, NULL) != 0 )
Eric Andersenf18bd892003-12-19 11:07:59 +0000509#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000510 bb_error_msg_and_die( "sigaction()");
Eric Andersenf18bd892003-12-19 11:07:59 +0000511#else
512 exit(EXIT_FAILURE);
513#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000514
515 bb_printf("%s v%s started for %s\n",bb_applet_name, DEVFSD_VERSION, mount_point);
516
517 /* Set umask so that mknod(2), open(2) and mkdir(2) have complete control over permissions */
518 umask (0);
519 read_config_file (CONFIG_FILE, FALSE, &event_mask);
520 /* Do the scan before forking, so that boot scripts see the finished product */
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000521 dir_operation(SERVICE,mount_point,0,NULL);
Eric Andersenf18bd892003-12-19 11:07:59 +0000522#ifdef CONFIG_DEVFSD_FG_NP
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000523 if (no_polling)
524 exit (0);
525 if (do_daemon)
526 {
Eric Andersenf18bd892003-12-19 11:07:59 +0000527#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000528 /* Release so that the child can grab it */
529 do_ioctl(DIE, fd, DEVFSDIOC_RELEASE_EVENT_QUEUE, 0);
530 fork_and_execute(DIE, NULL, NULL);
531 setsid (); /* Prevent hangups and become pgrp leader */
Eric Andersenf18bd892003-12-19 11:07:59 +0000532#ifdef CONFIG_DEVFSD_FG_NP
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000533 }
534 else
535 setpgid (0, 0); /* Become process group leader */
Eric Andersenf18bd892003-12-19 11:07:59 +0000536#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000537
538 while (TRUE)
539 {
Eric Andersenf18bd892003-12-19 11:07:59 +0000540 do_scan = do_servicing (fd, event_mask);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000541
542 free_config ();
543 read_config_file (CONFIG_FILE, FALSE, &event_mask);
544 if (do_scan)
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000545 dir_operation(SERVICE,mount_point,0,NULL);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000546 }
547} /* End Function main */
548
549
550/* Private functions follow */
551
Eric Andersenf18bd892003-12-19 11:07:59 +0000552static void read_config_file (char *path, int optional, unsigned long *event_mask)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000553/* [SUMMARY] Read a configuration database.
554 <path> The path to read the database from. If this is a directory, all
555 entries in that directory will be read (except hidden entries).
556 <optional> If TRUE, the routine will silently ignore a missing config file.
557 <event_mask> The event mask is written here. This is not initialised.
558 [RETURNS] Nothing.
559*/
560{
561 struct stat statbuf;
562 FILE *fp;
563 char buf[STRING_LENGTH];
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000564 char *line=NULL;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000565
Eric Andersenf18bd892003-12-19 11:07:59 +0000566#ifdef CONFIG_DEBUG
567 msg_logger( NO_DIE, LOG_INFO, "read_config_file(): %s\n", path);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000568#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000569 if (stat (path, &statbuf) != 0 || statbuf.st_size == 0 )
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000570 goto read_config_file_err;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000571
572 if ( S_ISDIR (statbuf.st_mode) )
573 {
Eric Andersenf18bd892003-12-19 11:07:59 +0000574 /* strip last / from dirname so we don't need to check for it later */
575 while( path && path[1]!='\0' && path[strlen(path)-1] == '/')
576 path[strlen(path) -1] = '\0';
577
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000578 dir_operation(READ_CONFIG, path, 0, event_mask);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000579 return;
580 }
581
582 if ( ( fp = fopen (path, "r") ) != NULL )
583 {
584 while (fgets (buf, STRING_LENGTH, fp) != NULL)
585 {
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000586 /* GETS(3) Linux Programmer's Manual
587 fgets() reads in at most one less than size characters from stream and
588 stores them into the buffer pointed to by s. Reading stops after an
589 EOF or a newline. If a newline is read, it is stored into the buffer.
590 A '\0' is stored after the last character in the buffer.
591 */
592 /*buf[strlen (buf) - 1] = '\0';*/
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000593 /* Skip whitespace */
594 for (line = buf; isspace (*line); ++line)
595 /*VOID*/;
596 if (line[0] == '\0' || line[0] == '#' )
597 continue;
598 process_config_line (line, event_mask);
599 }
600 fclose (fp);
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000601 errno=0;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000602 }
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000603read_config_file_err:
604#ifdef CONFIG_DEVFSD_VERBOSE
605 msg_logger(((optional == 0 ) && (errno == ENOENT))? DIE : NO_DIE, LOG_ERR, "read config file: %s: %m\n", path);
Eric Andersenf18bd892003-12-19 11:07:59 +0000606#else
607 if(optional == 0 && errno == ENOENT)
608 exit(EXIT_FAILURE);
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000609#endif
610 return;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000611} /* End Function read_config_file */
612
613static void process_config_line (const char *line, unsigned long *event_mask)
614/* [SUMMARY] Process a line from a configuration file.
615 <line> The configuration line.
616 <event_mask> The event mask is written here. This is not initialised.
617 [RETURNS] Nothing.
618*/
619{
620 int num_args, count;
621 struct config_entry_struct *new;
622 char p[MAX_ARGS][STRING_LENGTH];
623 char when[STRING_LENGTH], what[STRING_LENGTH];
624 char name[STRING_LENGTH];
625 char * msg="";
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000626 char *ptr;
627
Eric Andersenf18bd892003-12-19 11:07:59 +0000628 /* !!!! Only Uppercase Keywords in devsfd.conf */
629 const char *options[] = { "CLEAR_CONFIG", "INCLUDE", "OPTIONAL_INCLUDE", "RESTORE",
630 "PERMISSIONS", "MODLOAD", "EXECUTE", "COPY", "IGNORE",
631 "MKOLDCOMPAT", "MKNEWCOMPAT","RMOLDCOMPAT", "RMNEWCOMPAT", 0 };
632
633 short int i;
634
635#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000636 msg_logger( NO_DIE, LOG_INFO, "process_config_line()\n");
637#endif
638
639 for (count = 0; count < MAX_ARGS; ++count) p[count][0] = '\0';
640 num_args = sscanf (line, "%s %s %s %s %s %s %s %s %s %s",
641 when, name, what,
642 p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
Eric Andersenf18bd892003-12-19 11:07:59 +0000643
644 i = compare_string_array(options, when );
645
646 /*"CLEAR_CONFIG"*/
647 if( i == 0)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000648 {
649 free_config ();
650 *event_mask = 0;
651 return;
652 }
Eric Andersenf18bd892003-12-19 11:07:59 +0000653
654 if ( num_args < 2)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000655 goto process_config_line_err;
656
Eric Andersenf18bd892003-12-19 11:07:59 +0000657 /* "INCLUDE" & "OPTIONAL_INCLUDE" */
658 if( i == 1 || i == 2 )
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000659 {
660 st_expr_expand (name, STRING_LENGTH, name, get_variable, NULL );
Eric Andersenf18bd892003-12-19 11:07:59 +0000661#ifdef CONFIG_DEBUG
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000662 msg_logger( NO_DIE, LOG_INFO, "%sinclude: %s\n",(toupper (when[0]) == 'I') ? "": "optional_", name);
663#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000664 read_config_file (name, (toupper (when[0]) == 'I') ? FALSE : TRUE, event_mask);
665 return;
666 }
Eric Andersenf18bd892003-12-19 11:07:59 +0000667 /* "RESTORE" */
668 if( i == 3)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000669 {
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000670 dir_operation(RESTORE,name, strlen (name),NULL);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000671 return;
672 }
673 if (num_args < 3)
674 goto process_config_line_err;
675
676 new = xmalloc (sizeof *new);
677 memset (new, 0, sizeof *new);
678
679 for (count = 0; event_types[count].config_name != NULL; ++count)
680 {
681 if (strcasecmp (when, event_types[count].config_name) != 0)
682 continue;
683 new->action.when = event_types[count].type;
684 break;
685 }
686 if (event_types[count].config_name == NULL)
687 {
688 msg="WHEN in";
689 goto process_config_line_err;
690 }
691
Eric Andersenf18bd892003-12-19 11:07:59 +0000692 i = compare_string_array(options, what );
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000693
Eric Andersenf18bd892003-12-19 11:07:59 +0000694 switch(i)
695 {
696 case 4: /* "PERMISSIONS" */
697 new->action.what = AC_PERMISSIONS;
698 /* Get user and group */
699 if ( ( ptr = strchr (p[0], '.') ) == NULL )
700 {
701 msg="UID.GID";
702 goto process_config_line_err; /*"missing '.' in UID.GID */
703 }
704
705 *ptr++ = '\0';
706 new->u.permissions.uid = get_uid_gid (UID, p[0]);
707 new->u.permissions.gid = get_uid_gid (GID, ptr);
708 /* Get mode */
709 new->u.permissions.mode = get_mode (p[1]);
710 break;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000711#ifdef CONFIG_DEVFSD_MODLOAD
Eric Andersenf18bd892003-12-19 11:07:59 +0000712 case 5: /* MODLOAD */
713 /*This action will pass "/dev/$devname" (i.e. "/dev/" prefixed to
714 the device name) to the module loading facility. In addition,
715 the /etc/modules.devfs configuration file is used.*/
716 new->action.what = AC_MODLOAD;
717 break;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000718#endif
Eric Andersenf18bd892003-12-19 11:07:59 +0000719 case 6: /* EXECUTE */
720 new->action.what = AC_EXECUTE;
721 num_args -= 3;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000722
Eric Andersenf18bd892003-12-19 11:07:59 +0000723 for (count = 0; count < num_args; ++count)
724 new->u.execute.argv[count] = bb_xstrdup (p[count]);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000725
Eric Andersenf18bd892003-12-19 11:07:59 +0000726 new->u.execute.argv[num_args] = NULL;
727 break;
728 case 7: /* COPY */
729 new->action.what = AC_COPY;
730 num_args -= 3;
731 if (num_args != 2)
732 goto process_config_line_err; /* missing path and function in line */
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000733
Eric Andersenf18bd892003-12-19 11:07:59 +0000734 new->u.copy.source = bb_xstrdup (p[0]);
735 new->u.copy.destination = bb_xstrdup (p[1]);
736 break;
737 case 8: /* IGNORE */
738 /* FALLTROUGH */
739 case 9: /* MKOLDCOMPAT */
740 /* FALLTROUGH */
741 case 10: /* MKNEWCOMPAT */
742 /* FALLTROUGH */
743 case 11:/* RMOLDCOMPAT */
744 /* FALLTROUGH */
745 case 12: /* RMNEWCOMPAT */
746 /* AC_IGNORE 6
747 AC_MKOLDCOMPAT 7
748 AC_MKNEWCOMPAT 8
749 AC_RMOLDCOMPAT 9
750 AC_RMNEWCOMPAT 10*/
751 new->action.what = i - 2;
752 break;
753 default:
754 msg ="WHAT in";
755 goto process_config_line_err;
756 /*esac*/
757 } /* switch (i) */
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000758
759 xregcomp( &new->preg, name, REG_EXTENDED);
760
761 *event_mask |= 1 << new->action.when;
762 new->next = NULL;
763 if (first_config == NULL)
764 first_config = new;
765 else
766 last_config->next = new;
767 last_config = new;
768 return;
769process_config_line_err:
Eric Andersenf18bd892003-12-19 11:07:59 +0000770#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000771 msg_logger( DIE, LOG_ERR, bb_msg_bad_config, msg , line);
Eric Andersenf18bd892003-12-19 11:07:59 +0000772#else
773 exit(EXIT_FAILURE);
774#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000775} /* End Function process_config_line */
776
777static int do_servicing (int fd, unsigned long event_mask)
778/* [SUMMARY] Service devfs changes until a signal is received.
779 <fd> The open control file.
780 <event_mask> The event mask.
781 [RETURNS] TRUE if SIGHUP was caught, else FALSE.
782*/
783{
784 ssize_t bytes;
785 struct devfsd_notify_struct info;
786 unsigned long tmp_event_mask;
787
Eric Andersenf18bd892003-12-19 11:07:59 +0000788#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000789 msg_logger( NO_DIE, LOG_INFO, "do_servicing()\n");
790#endif
791 /* Tell devfs what events we care about */
792 tmp_event_mask = event_mask;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000793 do_ioctl(DIE, fd, DEVFSDIOC_SET_EVENT_MASK, tmp_event_mask);
794 while (!caught_signal)
795 {
796 errno = 0;
797 bytes = read (fd, (char *) &info, sizeof info);
798 if (caught_signal)
799 break; /* Must test for this first */
800 if (errno == EINTR)
801 continue; /* Yes, the order is important */
802 if (bytes < 1)
803 break;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000804 service_name (&info);
805 }
806 if (caught_signal)
807 {
808 int c_sighup = caught_sighup;
809
810 caught_signal = FALSE;
811 caught_sighup = FALSE;
812 return (c_sighup);
813 }
814#ifdef CONFIG_DEVFSD_VERBOSE
815 msg_logger( NO_DIE, LOG_ERR, "read error on control file: %m\n");
816#endif
817 /* This is to shut up a compiler warning */
Eric Andersenf18bd892003-12-19 11:07:59 +0000818 exit(EXIT_FAILURE);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000819} /* End Function do_servicing */
820
821static void service_name (const struct devfsd_notify_struct *info)
822/* [SUMMARY] Service a single devfs change.
823 <info> The devfs change.
824 [RETURNS] Nothing.
825*/
826{
827 unsigned int n;
828 regmatch_t mbuf[MAX_SUBEXPR];
829 struct config_entry_struct *entry;
830
Eric Andersenf18bd892003-12-19 11:07:59 +0000831#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000832 msg_logger( NO_DIE, LOG_INFO, "service_name()\n");
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000833 if (info->overrun_count > 0)
834 msg_logger( NO_DIE, LOG_ERR, "lost %u events\n", info->overrun_count);
835#endif
836
837 /* Discard lookups on "/dev/log" and "/dev/initctl" */
Eric Andersenf18bd892003-12-19 11:07:59 +0000838 if( info->type == DEVFSD_NOTIFY_LOOKUP &&
839 ((info->devname[0]=='l' && info->devname[1]=='o' &&
Glenn L McGrath5b0d7de2004-02-04 08:27:57 +0000840 info->devname[2]=='g' && !info->devname[3]) ||
Eric Andersenf18bd892003-12-19 11:07:59 +0000841 ( info->devname[0]=='i' && info->devname[1]=='n' &&
Glenn L McGrath5b0d7de2004-02-04 08:27:57 +0000842 info->devname[2]=='i' && info->devname[3]=='t' &&
843 info->devname[4]=='c' && info->devname[5]=='t' &&
844 info->devname[6]=='l' && !info->devname[7])))
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000845 return;
846 for (entry = first_config; entry != NULL; entry = entry->next)
847 {
848 /* First check if action matches the type, then check if name matches */
849 if (info->type != entry->action.when || regexec (&entry->preg, info->devname, MAX_SUBEXPR, mbuf, 0) != 0 )
850 continue;
851 for (n = 0; (n < MAX_SUBEXPR) && (mbuf[n].rm_so != -1); ++n)
852 /* VOID */;
Eric Andersenf18bd892003-12-19 11:07:59 +0000853#ifdef CONFIG_DEBUG
854 msg_logger( NO_DIE, LOG_INFO, "service_name(): action.what %d\n", entry->action.what);
855#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000856 switch (entry->action.what)
857 {
858 case AC_PERMISSIONS:
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000859 action_permissions (info, entry);
860 break;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000861#ifdef CONFIG_DEVFSD_MODLOAD
862 case AC_MODLOAD:
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000863 action_modload (info, entry);
864 break;
865#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000866 case AC_EXECUTE:
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000867 action_execute (info, entry, mbuf, n);
868 break;
869 case AC_COPY:
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000870 action_copy (info, entry, mbuf, n);
871 break;
872 case AC_IGNORE:
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000873 return;
874 /*break;*/
875 case AC_MKOLDCOMPAT:
876 case AC_MKNEWCOMPAT:
877 case AC_RMOLDCOMPAT:
878 case AC_RMNEWCOMPAT:
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000879 action_compat (info, entry->action.what);
880 break;
881 default:
Eric Andersenf18bd892003-12-19 11:07:59 +0000882#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000883 msg_logger( DIE, LOG_ERR, "Unknown action\n");
Eric Andersenf18bd892003-12-19 11:07:59 +0000884#else
885 exit(EXIT_FAILURE);
886#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000887 /*break;*/
888 }
889 }
890} /* End Function service_name */
891
892static void action_permissions (const struct devfsd_notify_struct *info,
893 const struct config_entry_struct *entry)
894/* [SUMMARY] Update permissions for a device entry.
895 <info> The devfs change.
896 <entry> The config file entry.
897 [RETURNS] Nothing.
898*/
899{
900 struct stat statbuf;
901
Eric Andersenf18bd892003-12-19 11:07:59 +0000902#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000903 msg_logger( NO_DIE, LOG_INFO, "action_permission()\n");
904#endif
905
906 if ( stat (info->devname, &statbuf) != 0 ||
907 chmod (info->devname,(statbuf.st_mode & S_IFMT) | (entry->u.permissions.mode & ~S_IFMT)) != 0 ||
908 chown (info->devname, entry->u.permissions.uid, entry->u.permissions.gid) != 0)
909 {
910#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000911 msg_logger( NO_DIE, LOG_ERR, "chmod() or chown(): %s: %m\n",info->devname);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000912#endif
913 return;
914 }
915
916} /* End Function action_permissions */
917
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000918#ifdef CONFIG_DEVFSD_MODLOAD
919static void action_modload (const struct devfsd_notify_struct *info,
920 const struct config_entry_struct *entry)
921/* [SUMMARY] Load a module.
922 <info> The devfs change.
923 <entry> The config file entry.
924 [RETURNS] Nothing.
925*/
926{
927 char *argv[6];
928 char device[STRING_LENGTH];
929
930 argv[0] = MODPROBE;
Eric Andersenf18bd892003-12-19 11:07:59 +0000931 argv[1] = MODPROBE_SWITCH_1; /* "-k" */
932 argv[2] = MODPROBE_SWITCH_2; /* "-C" */
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000933 argv[3] = CONFIG_MODULES_DEVFS;
934 argv[4] = device;
935 argv[5] = NULL;
Eric Andersenf18bd892003-12-19 11:07:59 +0000936
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000937 snprintf (device, sizeof (device), "/dev/%s", info->devname);
Eric Andersenf18bd892003-12-19 11:07:59 +0000938 #ifdef CONFIG_DEBUG
939 msg_logger( NO_DIE, LOG_INFO, "action_modload():%s %s %s %s %s\n",argv[0],argv[1],argv[2],argv[3],argv[4]);
940 #endif
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000941 fork_and_execute(DIE, argv[0], argv);
942} /* End Function action_modload */
943#endif
944
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000945static void action_execute (const struct devfsd_notify_struct *info,
946 const struct config_entry_struct *entry,
947 const regmatch_t *regexpr, unsigned int numexpr)
948/* [SUMMARY] Execute a programme.
949 <info> The devfs change.
950 <entry> The config file entry.
951 <regexpr> The number of subexpression (start, end) offsets within the
952 device name.
953 <numexpr> The number of elements within <<regexpr>>.
954 [RETURNS] Nothing.
955*/
956{
957 unsigned int count;
958 struct get_variable_info gv_info;
959 char *argv[MAX_ARGS + 1];
960 char largv[MAX_ARGS + 1][STRING_LENGTH];
961
Eric Andersenf18bd892003-12-19 11:07:59 +0000962#ifdef CONFIG_DEBUG
Tim Riker6fe55ae2003-10-17 18:54:55 +0000963 int i;
Eric Andersenf18bd892003-12-19 11:07:59 +0000964 char buff[512];
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000965#endif
966
967 gv_info.info = info;
968 gv_info.devname = info->devname;
969 snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname);
970 for (count = 0; entry->u.execute.argv[count] != NULL; ++count)
971 {
972 expand_expression (largv[count], STRING_LENGTH,
973 entry->u.execute.argv[count],
974 get_variable, &gv_info,
975 gv_info.devname, regexpr, numexpr );
976 argv[count] = largv[count];
977 }
978 argv[count] = NULL;
979
Eric Andersenf18bd892003-12-19 11:07:59 +0000980#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000981 buff[0]='\0';
982 for(i=0;argv[i]!=NULL;i++) /* argv[i] < MAX_ARGS + 1 */
983 {
984 strcat(buff," ");
Eric Andersenf18bd892003-12-19 11:07:59 +0000985 if( (strlen(buff)+ 1 + strlen(argv[i])) >= 512)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000986 break;
987 strcat(buff,argv[i]);
988 }
989 strcat(buff,"\n");
990 msg_logger( NO_DIE, LOG_INFO, "action_execute(): %s",buff);
991#endif
992
993 fork_and_execute(NO_DIE, argv[0], argv);
994} /* End Function action_execute */
995
996
997static void action_copy (const struct devfsd_notify_struct *info,
998 const struct config_entry_struct *entry,
999 const regmatch_t *regexpr, unsigned int numexpr)
1000/* [SUMMARY] Copy permissions.
1001 <info> The devfs change.
1002 <entry> The config file entry.
1003 <regexpr> This list of subexpression (start, end) offsets within the
1004 device name.
1005 <numexpr> The number of elements in <<regexpr>>.
1006 [RETURNS] Nothing.
1007*/
1008{
1009 mode_t new_mode;
1010 struct get_variable_info gv_info;
1011 struct stat source_stat, dest_stat;
1012 char source[STRING_LENGTH], destination[STRING_LENGTH];
1013 dest_stat.st_mode = 0;
1014
Eric Andersenf18bd892003-12-19 11:07:59 +00001015#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001016 msg_logger( NO_DIE, LOG_INFO, "action_copy()\n");
1017#endif
1018
1019 if ( (info->type == DEVFSD_NOTIFY_CHANGE) && S_ISLNK (info->mode) )
1020 return;
1021 gv_info.info = info;
1022 gv_info.devname = info->devname;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001023
Eric Andersenf18bd892003-12-19 11:07:59 +00001024 snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001025 expand_expression (source, STRING_LENGTH, entry->u.copy.source,
1026 get_variable, &gv_info, gv_info.devname,
1027 regexpr, numexpr);
1028
1029 expand_expression (destination, STRING_LENGTH, entry->u.copy.destination,
1030 get_variable, &gv_info, gv_info.devname,
1031 regexpr, numexpr);
1032
1033 if ( !make_dir_tree (destination) || lstat (source, &source_stat) != 0)
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001034 return;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001035 lstat (destination, &dest_stat);
1036 new_mode = source_stat.st_mode & ~S_ISVTX;
1037 if (info->type == DEVFSD_NOTIFY_CREATE)
1038 new_mode |= S_ISVTX;
1039 else if ( (info->type == DEVFSD_NOTIFY_CHANGE) && (dest_stat.st_mode & S_ISVTX) )
1040 new_mode |= S_ISVTX;
Eric Andersenf18bd892003-12-19 11:07:59 +00001041#ifdef CONFIG_DEBUG
1042 if ( !copy_inode (destination, &dest_stat, new_mode, source, &source_stat) && (errno != EEXIST))
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001043 msg_logger( NO_DIE, LOG_ERR, "copy_inode(): %s to %s: %m\n", source, destination);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001044#else
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001045 copy_inode (destination, &dest_stat, new_mode, source, &source_stat);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001046#endif
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001047 return;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001048} /* End Function action_copy */
1049
Eric Andersenf18bd892003-12-19 11:07:59 +00001050static void action_compat (const struct devfsd_notify_struct *info, unsigned int action)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001051/* [SUMMARY] Process a compatibility request.
1052 <info> The devfs change.
1053 <action> The action to take.
1054 [RETURNS] Nothing.
1055*/
1056{
1057 const char *compat_name = NULL;
1058 const char *dest_name = info->devname;
Eric Andersenf18bd892003-12-19 11:07:59 +00001059 char *ptr=NULL;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001060 char compat_buf[STRING_LENGTH], dest_buf[STRING_LENGTH];
Eric Andersenf18bd892003-12-19 11:07:59 +00001061 int mode, host, bus, target, lun;
1062 unsigned int i;
1063 char rewind_;
1064 /* 1 to 5 "scsi/" , 6 to 9 "ide/host" */
1065 const char *fmt[] = { NULL ,
1066 "sg/c%db%dt%du%d", /* scsi/generic */
1067 "sd/c%db%dt%du%d", /* scsi/disc */
1068 "sr/c%db%dt%du%d", /* scsi/cd */
1069 "sd/c%db%dt%du%dp%d", /* scsi/part */
1070 "st/c%db%dt%du%dm%d%c", /* scsi/mt */
1071 "ide/hd/c%db%dt%du%d", /* ide/host/disc */
1072 "ide/cd/c%db%dt%du%d", /* ide/host/cd */
1073 "ide/hd/c%db%dt%du%dp%d", /* ide/host/part */
1074 "ide/mt/c%db%dt%du%d%s", /* ide/host/mt */
1075 NULL };
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001076
1077 /* First construct compatibility name */
1078 switch (action)
1079 {
1080 case AC_MKOLDCOMPAT:
1081 case AC_RMOLDCOMPAT:
1082 compat_name = get_old_name (info->devname, info->namelen, compat_buf, info->major, info->minor);
1083 break;
1084 case AC_MKNEWCOMPAT:
1085 case AC_RMNEWCOMPAT:
Eric Andersenf18bd892003-12-19 11:07:59 +00001086 ptr = strrchr (info->devname, '/') + 1;
1087 i=scan_dev_name(info->devname, info->namelen, ptr);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001088
Eric Andersenf18bd892003-12-19 11:07:59 +00001089#ifdef CONFIG_DEBUG
1090 msg_logger( NO_DIE, LOG_INFO, "action_compat(): scan_dev_name() returned %d\n", i);
1091#endif
1092
1093 /* nothing found */
1094 if(i==0 || i > 9)
1095 return;
1096
1097 sscanf (info->devname +((i<6)?5:4), "host%d/bus%d/target%d/lun%d/", &host, &bus, &target, &lun);
1098 snprintf (dest_buf, sizeof (dest_buf), "../%s", info->devname + ((i>5)?4:0));
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001099 dest_name = dest_buf;
Eric Andersenf18bd892003-12-19 11:07:59 +00001100 compat_name = compat_buf;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001101
Eric Andersenf18bd892003-12-19 11:07:59 +00001102
1103 /* 1 == scsi/generic 2 == scsi/disc 3 == scsi/cd 6 == ide/host/disc 7 == ide/host/cd */
1104 if( i == 1 || i == 2 || i == 3 || i == 6 || i ==7 )
1105 sprintf ( compat_buf, fmt[i], host, bus, target, lun);
1106
1107 /* 4 == scsi/part 8 == ide/host/part */
1108 if( i == 4 || i == 8)
1109 sprintf ( compat_buf, fmt[i], host, bus, target, lun, atoi (ptr + 4) );
1110
1111 /* 5 == scsi/mt */
1112 if( i == 5)
1113 {
1114 rewind_ = info->devname[info->namelen - 1];
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001115 if (rewind_ != 'n')
1116 rewind_ = '\0';
Eric Andersenf18bd892003-12-19 11:07:59 +00001117 mode=0;
1118 if(ptr[2] == 'l' /*108*/ || ptr[2] == 'm'/*109*/)
1119 mode = ptr[2] - 107; /* 1 or 2 */
1120 if(ptr[2] == 'a')
1121 mode = 3;
1122 sprintf (compat_buf, fmt [i], host, bus, target, lun, mode, rewind_);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001123 }
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001124
Eric Andersenf18bd892003-12-19 11:07:59 +00001125 /* 9 == ide/host/mt */
1126 if( i == 9 )
1127 snprintf (compat_buf, sizeof (compat_buf), fmt[i], host, bus, target, lun, ptr + 2);
1128 /* esac */
1129 } /* switch(action) */
1130
1131 if(compat_name == NULL )
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001132 return;
Eric Andersenf18bd892003-12-19 11:07:59 +00001133
1134#ifdef CONFIG_DEBUG
1135 msg_logger( NO_DIE, LOG_INFO, "action_compat(): %s\n", compat_name);
1136#endif
1137
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001138 /* Now decide what to do with it */
1139 switch (action)
1140 {
1141 case AC_MKOLDCOMPAT:
1142 case AC_MKNEWCOMPAT:
1143 mksymlink (dest_name, compat_name);
1144 break;
1145 case AC_RMOLDCOMPAT:
1146 case AC_RMNEWCOMPAT:
Eric Andersenf18bd892003-12-19 11:07:59 +00001147#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001148 if (unlink (compat_name) != 0)
1149 msg_logger( NO_DIE, LOG_ERR, "unlink(): %s: %m\n", compat_name);
1150#else
1151 unlink (compat_name);
1152#endif
1153 break;
Eric Andersenf18bd892003-12-19 11:07:59 +00001154 /*esac*/
1155 } /* switch(action) */
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001156} /* End Function action_compat */
1157
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001158static void restore(char *spath, struct stat source_stat, int rootlen)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001159{
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001160 char dpath[STRING_LENGTH];
1161 struct stat dest_stat;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001162
Eric Andersenf18bd892003-12-19 11:07:59 +00001163#ifdef CONFIG_DEBUG
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001164 msg_logger( NO_DIE, LOG_INFO, "restore()\n");
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001165#endif
1166
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001167 dest_stat.st_mode = 0;
1168 snprintf (dpath, sizeof dpath, "%s%s", mount_point, spath + rootlen);
1169 lstat (dpath, &dest_stat);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001170
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001171 if ( S_ISLNK (source_stat.st_mode) || (source_stat.st_mode & S_ISVTX) )
1172 copy_inode (dpath, &dest_stat, (source_stat.st_mode & ~S_ISVTX) , spath, &source_stat);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001173
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001174 if ( S_ISDIR (source_stat.st_mode) )
1175 dir_operation(RESTORE, spath, rootlen,NULL);
1176}
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001177
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001178
1179static int copy_inode (const char *destpath, const struct stat *dest_stat,
1180 mode_t new_mode,
1181 const char *sourcepath, const struct stat *source_stat)
1182/* [SUMMARY] Copy an inode.
1183 <destpath> The destination path. An existing inode may be deleted.
1184 <dest_stat> The destination stat(2) information.
1185 <new_mode> The desired new mode for the destination.
1186 <sourcepath> The source path.
1187 <source_stat> The source stat(2) information.
1188 [RETURNS] TRUE on success, else FALSE.
1189*/
1190{
Eric Andersenf18bd892003-12-19 11:07:59 +00001191 int source_len, dest_len;
1192 char source_link[STRING_LENGTH], dest_link[STRING_LENGTH];
1193 int fd, val;
1194 struct sockaddr_un un_addr;
1195 char symlink_val[STRING_LENGTH];
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001196
Eric Andersenf18bd892003-12-19 11:07:59 +00001197#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001198 msg_logger( NO_DIE, LOG_INFO, "copy_inode()\n");
1199#endif
1200
1201 if ( (source_stat->st_mode & S_IFMT) == (dest_stat->st_mode & S_IFMT) )
1202 {
1203 /* Same type */
1204 if ( S_ISLNK (source_stat->st_mode) )
1205 {
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001206 if (( source_len = readlink (sourcepath, source_link, STRING_LENGTH - 1) ) < 0 ||
1207 ( dest_len = readlink (destpath , dest_link , STRING_LENGTH - 1) ) < 0 )
1208 return (FALSE);
1209 source_link[source_len] = '\0';
1210 dest_link[dest_len] = '\0';
1211 if ( (source_len != dest_len) || (strcmp (source_link, dest_link) != 0) )
1212 {
1213 unlink (destpath);
1214 symlink (source_link, destpath);
1215 }
1216 return (TRUE);
1217 } /* Else not a symlink */
1218 chmod (destpath, new_mode & ~S_IFMT);
1219 chown (destpath, source_stat->st_uid, source_stat->st_gid);
1220 return (TRUE);
1221 }
1222 /* Different types: unlink and create */
1223 unlink (destpath);
1224 switch (source_stat->st_mode & S_IFMT)
1225 {
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001226 case S_IFSOCK:
1227 if ( ( fd = socket (AF_UNIX, SOCK_STREAM, 0) ) < 0 )
1228 break;
1229 un_addr.sun_family = AF_UNIX;
1230 snprintf (un_addr.sun_path, sizeof (un_addr.sun_path), "%s", destpath);
1231 val = bind (fd, (struct sockaddr *) &un_addr, (int) sizeof un_addr);
1232 close (fd);
1233 if (val != 0 || chmod (destpath, new_mode & ~S_IFMT) != 0)
1234 break;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001235 goto do_chown;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001236 case S_IFLNK:
1237 if ( ( val = readlink (sourcepath, symlink_val, STRING_LENGTH - 1) ) < 0 )
1238 break;
1239 symlink_val[val] = '\0';
1240 if (symlink (symlink_val, destpath) == 0)
1241 return (TRUE);
1242 break;
1243 case S_IFREG:
1244 if ( ( fd = open (destpath, O_RDONLY | O_CREAT, new_mode & ~S_IFMT) ) < 0 )
1245 break;
1246 close (fd);
1247 if (chmod (destpath, new_mode & ~S_IFMT) != 0)
1248 break;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001249 goto do_chown;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001250 case S_IFBLK:
1251 case S_IFCHR:
1252 case S_IFIFO:
1253 if (mknod (destpath, new_mode, source_stat->st_rdev) != 0)
1254 break;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001255 goto do_chown;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001256 case S_IFDIR:
1257 if (mkdir (destpath, new_mode & ~S_IFMT) != 0)
1258 break;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001259do_chown:
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001260 if (chown (destpath, source_stat->st_uid, source_stat->st_gid) == 0)
1261 return (TRUE);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001262 /*break;*/
1263 }
1264 return (FALSE);
1265} /* End Function copy_inode */
1266
1267static void free_config ()
1268/* [SUMMARY] Free the configuration information.
1269 [RETURNS] Nothing.
1270*/
1271{
1272 struct config_entry_struct *c_entry;
1273 void *next;
1274
Eric Andersenf18bd892003-12-19 11:07:59 +00001275#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001276 msg_logger( NO_DIE, LOG_INFO, "free_config()\n");
1277#endif
1278
1279 for (c_entry = first_config; c_entry != NULL; c_entry = next)
1280 {
1281 unsigned int count;
1282
1283 next = c_entry->next;
1284 regfree (&c_entry->preg);
1285 if (c_entry->action.what == AC_EXECUTE)
1286 {
1287 for (count = 0; count < MAX_ARGS; ++count)
1288 {
1289 if (c_entry->u.execute.argv[count] == NULL)
1290 break;
1291 free (c_entry->u.execute.argv[count]);
1292 }
1293 }
1294 free (c_entry);
1295 }
1296 first_config = NULL;
1297 last_config = NULL;
1298} /* End Function free_config */
1299
1300static int get_uid_gid (int flag, const char *string)
1301/* [SUMMARY] Convert a string to a UID or GID value.
1302 <flag> "UID" or "GID".
1303 <string> The string.
1304 [RETURNS] The UID or GID value.
1305*/
1306{
1307 struct passwd *pw_ent;
1308 struct group *grp_ent;
Eric Andersenf18bd892003-12-19 11:07:59 +00001309#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001310 char * msg="user";
Eric Andersenf18bd892003-12-19 11:07:59 +00001311#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001312
Eric Andersenf18bd892003-12-19 11:07:59 +00001313#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001314 msg_logger( NO_DIE, LOG_INFO, "get_uid_gid()\n");
1315
1316
1317 if(flag != UID && flag != GID )
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001318 msg_logger( DIE, LOG_ERR,"get_uid_gid(): flag != UID && flag != GID\n");
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001319#endif
1320
1321 if ( isdigit (string[0]) || ( (string[0] == '-') && isdigit (string[1]) ) )
1322 return atoi (string);
1323
1324 if ( flag == UID && ( pw_ent = getpwnam (string) ) != NULL )
1325 return (pw_ent->pw_uid);
1326
1327 if ( flag == GID && ( grp_ent = getgrnam (string) ) != NULL )
1328 return (grp_ent->gr_gid);
Eric Andersenf18bd892003-12-19 11:07:59 +00001329#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001330 else
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001331 msg="group";
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001332
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001333 msg_logger( NO_DIE, LOG_ERR,"unknown %s: %s, defaulting to %cID=0\n", msg, string, msg[0] - 32);
Eric Andersenf18bd892003-12-19 11:07:59 +00001334#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001335 return (0);
1336}/* End Function get_uid_gid */
1337
1338static mode_t get_mode (const char *string)
1339/* [SUMMARY] Convert a string to a mode value.
1340 <string> The string.
1341 [RETURNS] The mode value.
1342*/
1343{
1344 mode_t mode;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001345 int i;
Eric Andersenf18bd892003-12-19 11:07:59 +00001346#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001347 msg_logger( NO_DIE, LOG_INFO, "get_mode()\n");
1348#endif
1349
1350 if ( isdigit (string[0]) )
1351 return strtoul (string, NULL, 8);
1352 if (strlen (string) != 9)
Eric Andersenf18bd892003-12-19 11:07:59 +00001353#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001354 msg_logger( DIE, LOG_ERR, "bad mode: %s\n", string);
Eric Andersenf18bd892003-12-19 11:07:59 +00001355#else
1356 exit(EXIT_FAILURE);
1357#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001358 mode = 0;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001359 i= S_IRUSR;
1360 while(i>0)
1361 {
1362 if(string[0]=='r'||string[0]=='w'||string[0]=='x')
1363 mode+=i;
1364 i=i/2;
1365 string++;
1366 }
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001367 return (mode);
1368} /* End Function get_mode */
1369
1370static void signal_handler (int sig)
1371{
Eric Andersenf18bd892003-12-19 11:07:59 +00001372#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001373 msg_logger( NO_DIE, LOG_INFO, "signal_handler()\n");
1374#endif
1375
1376 caught_signal = TRUE;
1377 if (sig == SIGHUP)
1378 caught_sighup = TRUE;
1379#ifdef CONFIG_DEVFSD_VERBOSE
1380 msg_logger( NO_DIE, LOG_INFO, "Caught %s\n",(sig == SIGHUP)?"SIGHUP" : "SIGUSR1");
1381#endif
1382} /* End Function signal_handler */
1383
1384static const char *get_variable (const char *variable, void *info)
1385{
1386 struct get_variable_info *gv_info = info;
1387 static char hostname[STRING_LENGTH], sbuf[STRING_LENGTH];
Eric Andersenf18bd892003-12-19 11:07:59 +00001388 const char *field_names[] = { "hostname", "mntpt", "devpath", "devname",
1389 "uid", "gid", "mode", hostname, mount_point,
1390 gv_info->devpath, gv_info->devname, 0 };
1391 short int i;
1392#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001393 msg_logger( NO_DIE, LOG_INFO, "get_variable()\n");
1394#endif
1395
1396 if (gethostname (hostname, STRING_LENGTH - 1) != 0)
Eric Andersenf18bd892003-12-19 11:07:59 +00001397#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001398 msg_logger( DIE, LOG_ERR, "gethostname(): %m\n");
Eric Andersenf18bd892003-12-19 11:07:59 +00001399#else
1400 exit(EXIT_FAILURE);
1401#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001402 /* Here on error we should do exit(RV_SYS_ERROR), instead we do exit(EXIT_FAILURE) */
Eric Andersenf18bd892003-12-19 11:07:59 +00001403 hostname[STRING_LENGTH - 1] = '\0';
1404
1405 /* compare_string_array returns i>=0 */
1406 i=compare_string_array(field_names, variable);
1407
1408 if ( i > 6 && (i > 1 && gv_info == NULL))
1409 return (NULL);
1410 if( i >= 0 || i <= 3)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001411 {
Eric Andersenf18bd892003-12-19 11:07:59 +00001412#ifdef CONFIG_DEBUG
1413 msg_logger( NO_DIE, LOG_INFO, "get_variable(): i=%d %s\n",i ,field_names[i+7]);
1414#endif
1415 return(field_names[i+7]);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001416 }
Eric Andersenf18bd892003-12-19 11:07:59 +00001417
1418 if(i == 4 )
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001419 sprintf (sbuf, "%u", gv_info->info->uid);
Eric Andersenf18bd892003-12-19 11:07:59 +00001420 else if(i == 5)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001421 sprintf (sbuf, "%u", gv_info->info->gid);
Eric Andersenf18bd892003-12-19 11:07:59 +00001422 else if(i == 6)
1423 sprintf (sbuf, "%o", gv_info->info->mode);
1424#ifdef CONFIG_DEBUG
1425 msg_logger( NO_DIE, LOG_INFO, "get_variable(): %s\n", sbuf);
1426#endif
1427 return (sbuf);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001428} /* End Function get_variable */
1429
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001430static void service(struct stat statbuf, char *path)
1431{
1432 struct devfsd_notify_struct info;
1433
Eric Andersenf18bd892003-12-19 11:07:59 +00001434#ifdef CONFIG_DEBUG
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001435 msg_logger( NO_DIE, LOG_INFO, "service()\n");
1436#endif
1437
1438 memset (&info, 0, sizeof info);
1439 info.type = DEVFSD_NOTIFY_REGISTERED;
1440 info.mode = statbuf.st_mode;
1441 info.major = major (statbuf.st_rdev);
1442 info.minor = minor (statbuf.st_rdev);
1443 info.uid = statbuf.st_uid;
1444 info.gid = statbuf.st_gid;
1445 snprintf (info.devname, sizeof (info.devname), "%s", path + strlen (mount_point) + 1);
1446 info.namelen = strlen (info.devname);
1447 service_name (&info);
1448 if ( S_ISDIR (statbuf.st_mode) )
1449 dir_operation(SERVICE,path,0,NULL);
1450}
1451
1452static void dir_operation(int type, const char * dir_name, int var, unsigned long *event_mask)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001453/* [SUMMARY] Scan a directory tree and generate register events on leaf nodes.
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001454 <flag> To choose which function to perform
1455 <dp> The directory pointer. This is closed upon completion.
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001456 <dir_name> The name of the directory.
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001457 <rootlen> string length parameter.
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001458 [RETURNS] Nothing.
1459*/
1460{
1461 struct stat statbuf;
1462 DIR *dp;
1463 struct dirent *de;
1464 char path[STRING_LENGTH];
1465
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001466
Eric Andersenf18bd892003-12-19 11:07:59 +00001467#ifdef CONFIG_DEBUG
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001468 msg_logger( NO_DIE, LOG_INFO, "dir_operation()\n");
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001469#endif
1470
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001471 if((dp = opendir( dir_name))==NULL)
1472 {
Eric Andersenf18bd892003-12-19 11:07:59 +00001473#ifdef CONFIG_DEBUG
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001474 msg_logger( NO_DIE, LOG_ERR, "opendir(): %s: %m\n", dir_name);
Eric Andersenf18bd892003-12-19 11:07:59 +00001475#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001476 return;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001477 }
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001478
1479 while ( (de = readdir (dp) ) != NULL )
1480 {
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001481
Eric Andersenf18bd892003-12-19 11:07:59 +00001482 if(de->d_name && *de->d_name == '.' && (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])))
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001483 continue;
Eric Andersenf18bd892003-12-19 11:07:59 +00001484 snprintf (path, sizeof (path), "%s/%s", dir_name, de->d_name);
1485#ifdef CONFIG_DEBUG
1486 msg_logger( NO_DIE, LOG_ERR, "dir_operation(): %s\n", path);
1487#endif
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001488
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001489 if (lstat (path, &statbuf) != 0)
1490 {
Eric Andersenf18bd892003-12-19 11:07:59 +00001491#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001492 msg_logger( NO_DIE, LOG_ERR, "%s: %m\n", path);
1493#endif
1494 continue;
1495 }
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001496 switch(type)
1497 {
1498 case SERVICE:
1499 service(statbuf,path);
1500 break;
1501 case RESTORE:
1502 restore(path, statbuf, var);
1503 break;
1504 case READ_CONFIG:
1505 read_config_file (path, var, event_mask);
1506 break;
1507 }
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001508 }
1509 closedir (dp);
1510} /* End Function do_scan_and_service */
1511
1512static int mksymlink (const char *oldpath, const char *newpath)
1513/* [SUMMARY] Create a symlink, creating intervening directories as required.
1514 <oldpath> The string contained in the symlink.
1515 <newpath> The name of the new symlink.
1516 [RETURNS] 0 on success, else -1.
1517*/
1518{
Eric Andersenf18bd892003-12-19 11:07:59 +00001519#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001520 msg_logger( NO_DIE, LOG_INFO, "mksymlink()\n", newpath);
1521#endif
1522
1523
1524 if ( !make_dir_tree (newpath) )
1525 return (-1);
1526
1527 if (symlink (oldpath, newpath) != 0)
1528 {
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001529 if (errno != EEXIST)
Eric Andersenf18bd892003-12-19 11:07:59 +00001530 {
1531#ifdef CONFIG_DEBUG
1532 msg_logger( NO_DIE, LOG_ERR, "mksymlink(): %s to %s: %m\n", oldpath, newpath);
1533#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001534 return (-1);
Eric Andersenf18bd892003-12-19 11:07:59 +00001535 }
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001536 }
1537 return (0);
1538} /* End Function mksymlink */
1539
1540
1541static int make_dir_tree (const char *path)
1542/* [SUMMARY] Creating intervening directories for a path as required.
Eric Andersen52899692003-10-22 10:10:50 +00001543 <path> The full pathname (including the leaf node).
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001544 [RETURNS] TRUE on success, else FALSE.
1545*/
1546{
Eric Andersenf18bd892003-12-19 11:07:59 +00001547#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001548 msg_logger( NO_DIE, LOG_INFO, "make_dir_tree()\n");
1549#endif
Eric Andersen0e020d12004-10-13 06:25:52 +00001550 if (bb_make_directory( dirname((char *)path), -1, FILEUTILS_RECUR )==-1)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001551 {
Eric Andersenf18bd892003-12-19 11:07:59 +00001552#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001553 msg_logger( NO_DIE, LOG_ERR, "make_dir_tree(): %s: %m\n", path);
1554#endif
1555 return (FALSE);
1556 }
1557 return(TRUE);
1558} /* End Function make_dir_tree */
1559
1560static int expand_expression(char *output, unsigned int outsize,
1561 const char *input,
1562 const char *(*get_variable_func)(const char *variable, void *info),
1563 void *info,
1564 const char *devname,
1565 const regmatch_t *ex, unsigned int numexp)
Eric Andersenaff114c2004-04-14 17:51:38 +00001566/* [SUMMARY] Expand environment variables and regular subexpressions in string.
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001567 <output> The output expanded expression is written here.
1568 <length> The size of the output buffer.
1569 <input> The input expression. This may equal <<output>>.
1570 <get_variable> A function which will be used to get variable values. If
1571 this returns NULL, the environment is searched instead. If this is NULL,
1572 only the environment is searched.
1573 <info> An arbitrary pointer passed to <<get_variable>>.
1574 <devname> Device name; specifically, this is the string that contains all
1575 of the regular subexpressions.
1576 <ex> Array of start / end offsets into info->devname for each subexpression
1577 <numexp> Number of regular subexpressions found in <<devname>>.
1578 [RETURNS] TRUE on success, else FALSE.
1579*/
1580{
1581 char temp[STRING_LENGTH];
1582
Eric Andersenf18bd892003-12-19 11:07:59 +00001583#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001584 msg_logger( NO_DIE, LOG_INFO, "expand_expression()\n");
1585#endif
1586
1587 if ( !st_expr_expand (temp, STRING_LENGTH, input, get_variable_func, info) )
1588 return (FALSE);
1589 expand_regexp (output, outsize, temp, devname, ex, numexp);
1590 return (TRUE);
1591} /* End Function expand_expression */
1592
1593static void expand_regexp (char *output, size_t outsize, const char *input,
1594 const char *devname,
1595 const regmatch_t *ex, unsigned int numex )
1596/* [SUMMARY] Expand all occurrences of the regular subexpressions \0 to \9.
1597 <output> The output expanded expression is written here.
1598 <outsize> The size of the output buffer.
1599 <input> The input expression. This may NOT equal <<output>>, because
1600 supporting that would require yet another string-copy. However, it's not
1601 hard to write a simple wrapper function to add this functionality for those
1602 few cases that need it.
1603 <devname> Device name; specifically, this is the string that contains all
1604 of the regular subexpressions.
1605 <ex> An array of start and end offsets into <<devname>>, one for each
1606 subexpression
1607 <numex> Number of subexpressions in the offset-array <<ex>>.
1608 [RETURNS] Nothing.
1609*/
1610{
1611 const char last_exp = '0' - 1 + numex;
1612 int c = -1;
1613
Eric Andersenf18bd892003-12-19 11:07:59 +00001614#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001615 msg_logger( NO_DIE, LOG_INFO, "expand_regexp()\n");
1616#endif
1617
1618 /* Guarantee NULL termination by writing an explicit '\0' character into
1619 the very last byte */
1620 if (outsize)
1621 output[--outsize] = '\0';
1622 /* Copy the input string into the output buffer, replacing '\\' with '\'
1623 and '\0' .. '\9' with subexpressions 0 .. 9, if they exist. Other \x
1624 codes are deleted */
1625 while ( (c != '\0') && (outsize != 0) )
1626 {
1627 c = *input;
1628 ++input;
1629 if (c == '\\')
1630 {
1631 c = *input;
1632 ++input;
1633 if (c != '\\')
1634 {
1635 if ((c >= '0') && (c <= last_exp))
1636 {
1637 const regmatch_t *subexp = ex + (c - '0');
1638 unsigned int sublen = subexp->rm_eo - subexp->rm_so;
1639
1640 /* Range checking */
1641 if (sublen > outsize)
1642 sublen = outsize;
1643 strncpy (output, devname + subexp->rm_so, sublen);
1644 output += sublen;
1645 outsize -= sublen;
1646 }
1647 continue;
1648 }
1649 }
1650 *output = c;
1651 ++output;
1652 --outsize;
1653 } /* while */
1654} /* End Function expand_regexp */
1655
1656
1657/* from compat_name.c */
1658
1659struct translate_struct
1660{
1661 char *match; /* The string to match to (up to length) */
1662 char *format; /* Format of output, "%s" takes data past match string,
1663 NULL is effectively "%s" (just more efficient) */
1664};
1665
1666static struct translate_struct translate_table[] =
1667{
1668 {"sound/", NULL},
1669 {"printers/", "lp%s"},
1670 {"v4l/", NULL},
1671 {"parports/", "parport%s"},
1672 {"fb/", "fb%s"},
1673 {"netlink/", NULL},
1674 {"loop/", "loop%s"},
1675 {"floppy/", "fd%s"},
1676 {"rd/", "ram%s"},
1677 {"md/", "md%s"}, /* Meta-devices */
1678 {"vc/", "tty%s"},
1679 {"misc/", NULL},
1680 {"isdn/", NULL},
1681 {"pg/", "pg%s"}, /* Parallel port generic ATAPI interface*/
1682 {"i2c/", "i2c-%s"},
1683 {"staliomem/", "staliomem%s"}, /* Stallion serial driver control */
1684 {"tts/E", "ttyE%s"}, /* Stallion serial driver */
1685 {"cua/E", "cue%s"}, /* Stallion serial driver callout */
1686 {"tts/R", "ttyR%s"}, /* Rocketport serial driver */
1687 {"cua/R", "cur%s"}, /* Rocketport serial driver callout */
1688 {"ip2/", "ip2%s"}, /* Computone serial driver control */
1689 {"tts/F", "ttyF%s"}, /* Computone serial driver */
1690 {"cua/F", "cuf%s"}, /* Computone serial driver callout */
1691 {"tts/C", "ttyC%s"}, /* Cyclades serial driver */
1692 {"cua/C", "cub%s"}, /* Cyclades serial driver callout */
1693 {"tts/", "ttyS%s"}, /* Generic serial: must be after others */
1694 {"cua/", "cua%s"}, /* Generic serial: must be after others */
1695 {"input/js", "js%s"}, /* Joystick driver */
1696 {NULL, NULL}
1697};
1698
1699const char *get_old_name (const char *devname, unsigned int namelen,
1700 char *buffer, unsigned int major, unsigned int minor)
1701/* [SUMMARY] Translate a kernel-supplied name into an old name.
1702 <devname> The device name provided by the kernel.
1703 <namelen> The length of the name.
1704 <buffer> A buffer that may be used. This should be at least 128 bytes long.
1705 <major> The major number for the device.
1706 <minor> The minor number for the device.
1707 [RETURNS] A pointer to the old name if known, else NULL.
1708*/
1709{
1710 const char *compat_name = NULL;
1711 char *ptr;
1712 struct translate_struct *trans;
Eric Andersenf18bd892003-12-19 11:07:59 +00001713 unsigned int i;
1714 char mode;
1715 int indexx;
1716 const char *pty1;
1717 const char *pty2;
1718 size_t len;
1719 /* 1 to 5 "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */
1720 const char *fmt[] = { NULL ,
1721 "sg%u", /* scsi/generic */
1722 NULL, /* scsi/disc */
1723 "sr%u", /* scsi/cd */
1724 NULL, /* scsi/part */
1725 "nst%u%c", /* scsi/mt */
1726 "hd%c" , /* ide/host/disc */
1727 "hd%c" , /* ide/host/cd */
1728 "hd%c%s", /* ide/host/part */
1729 "%sht%d", /* ide/host/mt */
1730 "sbpcd%u", /* sbp/ */
1731 "vcs%s", /* vcc/ */
1732 "%cty%c%c", /* pty/ */
1733 NULL };
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001734
Eric Andersenf18bd892003-12-19 11:07:59 +00001735#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001736 msg_logger( NO_DIE, LOG_INFO, "get_old_name()\n");
1737#endif
1738
1739 for (trans = translate_table; trans->match != NULL; ++trans)
1740 {
Eric Andersenf18bd892003-12-19 11:07:59 +00001741 len = strlen (trans->match);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001742
1743 if (strncmp (devname, trans->match, len) == 0)
1744 {
1745 if (trans->format == NULL)
1746 return (devname + len);
1747 sprintf (buffer, trans->format, devname + len);
1748 return (buffer);
1749 }
1750 }
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001751
Eric Andersenf18bd892003-12-19 11:07:59 +00001752 ptr = (strrchr (devname, '/') + 1);
1753 i = scan_dev_name(devname, namelen, ptr);
1754
1755 if( i > 0 && i < 13)
1756 compat_name = buffer;
1757 else
1758 return NULL;
1759
1760#ifdef CONFIG_DEBUG
1761 msg_logger( NO_DIE, LOG_INFO, "get_old_name(): scan_dev_name() returned %d\n", i);
1762#endif
1763
1764 /* 1 == scsi/generic, 3 == scsi/cd, 10 == sbp/ */
1765 if( i == 1 || i == 3 || i == 10 )
1766 sprintf (buffer, fmt[i], minor);
1767
1768 /* 2 ==scsi/disc, 4 == scsi/part */
1769 if( i == 2 || i == 4)
1770 compat_name = write_old_sd_name (buffer, major, minor,((i == 2)?"":(ptr + 4)));
1771
1772 /* 5 == scsi/mt */
1773 if( i == 5)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001774 {
Eric Andersenf18bd892003-12-19 11:07:59 +00001775 mode = ptr[2];
1776 if (mode == 'n')
1777 mode = '\0';
1778 sprintf (buffer, fmt[i], minor & 0x1f, mode);
1779 if (devname[namelen - 1] != 'n')
1780 ++compat_name;
1781 }
1782 /* 6 == ide/host/disc, 7 == ide/host/cd, 8 == ide/host/part */
1783 if( i == 6 || i == 7 || i == 8 )
1784 sprintf (buffer, fmt[i] , get_old_ide_name (major, minor), ptr + 4); /* last arg should be ignored for i == 6 or i== 7 */
1785
1786 /* 9 == ide/host/mt */
1787 if( i == 9 )
1788 sprintf (buffer, fmt[i], ptr + 2, minor & 0x7f);
1789
1790 /* 11 == vcc/ */
1791 if( i == 11 )
1792 {
1793 sprintf (buffer, fmt[i], devname + 4);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001794 if (buffer[3] == '0')
1795 buffer[3] = '\0';
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001796 }
Eric Andersenf18bd892003-12-19 11:07:59 +00001797 /* 12 == pty/ */
1798 if( i == 12 )
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001799 {
Eric Andersenf18bd892003-12-19 11:07:59 +00001800 pty1 = "pqrstuvwxyzabcde";
1801 pty2 = "0123456789abcdef";
1802 indexx = atoi (devname + 5);
1803 sprintf (buffer, fmt[i], (devname[4] == 'm') ? 'p' : 't', pty1[indexx >> 4], pty2[indexx & 0x0f]);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001804 }
Eric Andersenf18bd892003-12-19 11:07:59 +00001805#ifdef CONFIG_DEBUG
1806 if(compat_name!=NULL)
1807 msg_logger( NO_DIE, LOG_INFO, "get_old_name(): compat_name %s\n", compat_name);
1808#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001809 return (compat_name);
1810} /* End Function get_old_name */
1811
1812static char get_old_ide_name (unsigned int major, unsigned int minor)
1813/* [SUMMARY] Get the old IDE name for a device.
1814 <major> The major number for the device.
1815 <minor> The minor number for the device.
1816 [RETURNS] The drive letter.
1817*/
1818{
1819 char letter='y'; /* 121 */
1820 char c='a'; /* 97 */
1821 int i=IDE0_MAJOR;
1822
Eric Andersenf18bd892003-12-19 11:07:59 +00001823#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001824 msg_logger( NO_DIE, LOG_INFO, "get_old_ide_name()\n");
1825#endif
1826
1827 /* I hope it works like the previous code as it saves a few bytes. Tito ;P */
1828 do {
1829 if( i==IDE0_MAJOR || i==IDE1_MAJOR || i==IDE2_MAJOR ||
1830 i==IDE3_MAJOR || i==IDE4_MAJOR || i==IDE5_MAJOR ||
1831 i==IDE6_MAJOR || i==IDE7_MAJOR || i==IDE8_MAJOR ||
1832 i==IDE9_MAJOR )
1833 {
1834 if(i==major)
1835 {
1836 letter=c;
1837 break;
1838 }
1839 c+=2;
1840 }
1841 i++;
1842 } while(i<=IDE9_MAJOR);
1843
1844 if (minor > 63)
1845 ++letter;
1846 return (letter);
1847} /* End Function get_old_ide_name */
1848
1849static char *write_old_sd_name (char *buffer,
1850 unsigned int major, unsigned int minor,
1851 char *part)
1852/* [SUMMARY] Write the old SCSI disc name to a buffer.
1853 <buffer> The buffer to write to.
1854 <major> The major number for the device.
1855 <minor> The minor number for the device.
1856 <part> The partition string. Must be "" for a whole-disc entry.
1857 [RETURNS] A pointer to the buffer on success, else NULL.
1858*/
1859{
1860 unsigned int disc_index;
1861
Eric Andersenf18bd892003-12-19 11:07:59 +00001862#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001863 msg_logger( NO_DIE, LOG_INFO, "write_old_sd_name()\n");
1864#endif
1865
1866 if (major == 8)
1867 {
1868 sprintf (buffer, "sd%c%s", 'a' + (minor >> 4), part);
1869 return (buffer);
1870 }
1871 if ( (major > 64) && (major < 72) )
1872 {
1873 disc_index = ( (major - 64) << 4 ) + (minor >> 4);
1874 if (disc_index < 26)
1875 sprintf (buffer, "sd%c%s", 'a' + disc_index, part);
1876 else
1877 sprintf (buffer, "sd%c%c%s", 'a' + (disc_index / 26) - 1, 'a' + disc_index % 26,part);
1878 return (buffer);
1879 }
1880 return (NULL);
1881} /* End Function write_old_sd_name */
1882
1883
1884/* expression.c */
1885
1886/*EXPERIMENTAL_FUNCTION*/
1887
1888int st_expr_expand (char *output, unsigned int length, const char *input,
1889 const char *(*get_variable_func) (const char *variable,
1890 void *info),
1891 void *info)
1892/* [SUMMARY] Expand an expression using Borne Shell-like unquoted rules.
1893 <output> The output expanded expression is written here.
1894 <length> The size of the output buffer.
1895 <input> The input expression. This may equal <<output>>.
1896 <get_variable> A function which will be used to get variable values. If
1897 this returns NULL, the environment is searched instead. If this is NULL,
1898 only the environment is searched.
1899 <info> An arbitrary pointer passed to <<get_variable>>.
1900 [RETURNS] TRUE on success, else FALSE.
1901*/
1902{
1903 char ch;
1904 unsigned int len;
1905 unsigned int out_pos = 0;
1906 const char *env;
1907 const char *ptr;
1908 struct passwd *pwent;
1909 char buffer[BUFFER_SIZE], tmp[STRING_LENGTH];
1910
Eric Andersenf18bd892003-12-19 11:07:59 +00001911#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001912 msg_logger( NO_DIE, LOG_INFO, "st_expr_expand()\n");
1913#endif
1914
1915 if (length > BUFFER_SIZE)
1916 length = BUFFER_SIZE;
1917 for (; TRUE; ++input)
1918 {
1919 switch (ch = *input)
1920 {
1921 case '$':
1922 /* Variable expansion */
1923 input = expand_variable (buffer, length, &out_pos, ++input, get_variable_func, info);
1924 if (input == NULL)
1925 return (FALSE);
1926 break;
1927 case '~':
1928 /* Home directory expansion */
1929 ch = input[1];
1930 if ( isspace (ch) || (ch == '/') || (ch == '\0') )
1931 {
1932 /* User's own home directory: leave separator for next time */
1933 if ( ( env = getenv ("HOME") ) == NULL )
1934 {
1935#ifdef CONFIG_DEVFSD_VERBOSE
1936 msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, "HOME");
1937#endif
1938 return (FALSE);
1939 }
1940 len = strlen (env);
1941 if (len + out_pos >= length)
1942 goto st_expr_expand_out;
1943 memcpy (buffer + out_pos, env, len + 1);
1944 out_pos += len;
1945 continue;
1946 }
1947 /* Someone else's home directory */
1948 for (ptr = ++input; !isspace (ch) && (ch != '/') && (ch != '\0'); ch = *++ptr)
1949 /* VOID */ ;
1950 len = ptr - input;
1951 if (len >= sizeof tmp)
1952 goto st_expr_expand_out;
1953 safe_memcpy (tmp, input, len);
1954 input = ptr - 1;
1955 if ( ( pwent = getpwnam (tmp) ) == NULL )
1956 {
1957#ifdef CONFIG_DEVFSD_VERBOSE
Eric Andersenf18bd892003-12-19 11:07:59 +00001958 msg_logger( NO_DIE, LOG_INFO, "no pwent for: %s\n", tmp);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001959#endif
1960 return (FALSE);
1961 }
1962 len = strlen (pwent->pw_dir);
1963 if (len + out_pos >= length)
1964 goto st_expr_expand_out;
1965 memcpy (buffer + out_pos, pwent->pw_dir, len + 1);
1966 out_pos += len;
1967 break;
1968 case '\0':
1969 /* Falltrough */
1970 default:
1971 if (out_pos >= length)
1972 goto st_expr_expand_out;
1973 buffer[out_pos++] = ch;
1974 if (ch == '\0')
1975 {
1976 memcpy (output, buffer, out_pos);
1977 return (TRUE);
1978 }
1979 break;
1980 /* esac */
1981 }
1982 }
1983 return (FALSE);
1984st_expr_expand_out:
1985#ifdef CONFIG_DEVFSD_VERBOSE
1986 msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer);
1987#endif
1988 return (FALSE);
1989} /* End Function st_expr_expand */
1990
1991
1992/* Private functions follow */
1993
1994static const char *expand_variable (char *buffer, unsigned int length,
1995 unsigned int *out_pos, const char *input,
1996 const char *(*func) (const char *variable,
1997 void *info),
1998 void *info)
1999/* [SUMMARY] Expand a variable.
2000 <buffer> The buffer to write to.
2001 <length> The length of the output buffer.
2002 <out_pos> The current output position. This is updated.
2003 <input> A pointer to the input character pointer.
2004 <func> A function which will be used to get variable values. If this
2005 returns NULL, the environment is searched instead. If this is NULL, only
2006 the environment is searched.
2007 <info> An arbitrary pointer passed to <<func>>.
2008 <errfp> Diagnostic messages are written here.
2009 [RETURNS] A pointer to the end of this subexpression on success, else NULL.
2010*/
2011{
2012 char ch;
2013 int len;
2014 unsigned int open_braces;
2015 const char *env, *ptr;
2016 char tmp[STRING_LENGTH];
2017
Eric Andersenf18bd892003-12-19 11:07:59 +00002018#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00002019 msg_logger( NO_DIE, LOG_INFO, "expand_variable()\n");
2020#endif
2021
2022 ch = input[0];
2023 if (ch == '$')
2024 {
2025 /* Special case for "$$": PID */
2026 sprintf ( tmp, "%d", (int) getpid () );
2027 len = strlen (tmp);
2028 if (len + *out_pos >= length)
2029 goto expand_variable_out;
2030
2031 memcpy (buffer + *out_pos, tmp, len + 1);
2032 out_pos += len;
2033 return (input);
2034 }
2035 /* Ordinary variable expansion, possibly in braces */
2036 if (ch != '{')
2037 {
2038 /* Simple variable expansion */
2039 for (ptr = input; isalnum (ch) || (ch == '_') || (ch == ':');ch = *++ptr)
2040 /* VOID */ ;
2041 len = ptr - input;
2042 if (len >= sizeof tmp)
2043 goto expand_variable_out;
2044
2045 safe_memcpy (tmp, input, len);
2046 input = ptr - 1;
2047 if ( ( env = get_variable_v2 (tmp, func, info) ) == NULL )
2048 {
2049#ifdef CONFIG_DEVFSD_VERBOSE
2050 msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, tmp);
2051#endif
2052 return (NULL);
2053 }
2054 len = strlen (env);
2055 if (len + *out_pos >= length)
2056 goto expand_variable_out;
2057
2058 memcpy (buffer + *out_pos, env, len + 1);
2059 *out_pos += len;
2060 return (input);
2061 }
2062 /* Variable in braces: check for ':' tricks */
2063 ch = *++input;
2064 for (ptr = input; isalnum (ch) || (ch == '_'); ch = *++ptr)
2065 /* VOID */;
2066 if (ch == '}')
2067 {
2068 /* Must be simple variable expansion with "${var}" */
2069 len = ptr - input;
2070 if (len >= sizeof tmp)
2071 goto expand_variable_out;
2072
2073 safe_memcpy (tmp, input, len);
2074 ptr = expand_variable (buffer, length, out_pos, tmp, func, info );
2075 if (ptr == NULL)
2076 return (NULL);
2077 return (input + len);
2078 }
2079 if (ch != ':' || ptr[1] != '-' )
2080 {
2081#ifdef CONFIG_DEVFSD_VERBOSE
2082 msg_logger( NO_DIE, LOG_INFO,"illegal char in var name\n");
2083#endif
2084 return (NULL);
2085 }
2086 /* It's that handy "${var:-word}" expression. Check if var is defined */
2087 len = ptr - input;
2088 if (len >= sizeof tmp)
2089 goto expand_variable_out;
2090
2091 safe_memcpy (tmp, input, len);
2092 /* Move input pointer to ':' */
2093 input = ptr;
2094 /* First skip to closing brace, taking note of nested expressions */
2095 ptr += 2;
2096 ch = ptr[0];
2097 for (open_braces = 1; open_braces > 0; ch = *++ptr)
2098 {
2099 switch (ch)
2100 {
2101 case '{':
2102 ++open_braces;
2103 break;
2104 case '}':
2105 --open_braces;
2106 break;
2107 case '\0':
2108#ifdef CONFIG_DEVFSD_VERBOSE
2109 msg_logger( NO_DIE, LOG_INFO,"\"}\" not found in: %s\n", input);
2110#endif
2111 return (NULL);
2112 default:
2113 break;
2114 }
2115 }
2116 --ptr;
2117 /* At this point ptr should point to closing brace of "${var:-word}" */
2118 if ( ( env = get_variable_v2 (tmp, func, info) ) != NULL )
2119 {
2120 /* Found environment variable, so skip the input to the closing brace
2121 and return the variable */
2122 input = ptr;
2123 len = strlen (env);
2124 if (len + *out_pos >= length)
2125 goto expand_variable_out;
2126
2127 memcpy (buffer + *out_pos, env, len + 1);
2128 *out_pos += len;
2129 return (input);
2130 }
2131 /* Environment variable was not found, so process word. Advance input
2132 pointer to start of word in "${var:-word}" */
2133 input += 2;
2134 len = ptr - input;
2135 if (len >= sizeof tmp)
2136 goto expand_variable_out;
2137
2138 safe_memcpy (tmp, input, len);
2139 input = ptr;
2140 if ( !st_expr_expand (tmp, STRING_LENGTH, tmp, func, info ) )
2141 return (NULL);
2142 len = strlen (tmp);
2143 if (len + *out_pos >= length)
2144 goto expand_variable_out;
2145
2146 memcpy (buffer + *out_pos, tmp, len + 1);
2147 *out_pos += len;
2148 return (input);
2149expand_variable_out:
2150#ifdef CONFIG_DEVFSD_VERBOSE
2151 msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer);
2152#endif
2153 return (NULL);
2154} /* End Function expand_variable */
2155
2156
2157static const char *get_variable_v2 (const char *variable,
2158 const char *(*func) (const char *variable, void *info),
2159 void *info)
2160/* [SUMMARY] Get a variable from the environment or .
2161 <variable> The variable name.
2162 <func> A function which will be used to get the variable. If this returns
2163 NULL, the environment is searched instead. If this is NULL, only the
2164 environment is searched.
2165 [RETURNS] The value of the variable on success, else NULL.
2166*/
2167{
2168 const char *value;
2169
Eric Andersenf18bd892003-12-19 11:07:59 +00002170#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00002171 msg_logger( NO_DIE, LOG_INFO, "get_variable_v2()\n");
2172#endif
2173
2174 if (func != NULL)
2175 {
2176 value = (*func) (variable, info);
2177 if (value != NULL)
2178 return (value);
2179 }
2180 return getenv (variable);
2181} /* End Function get_variable */
2182
2183/* END OF CODE */