blob: aa7a826b78d3bfd64ebf5329f23dcd7140c75c87 [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 */
Eric Andersenf18bd892003-12-19 11:07:59 +0000249#ifdef CONFIG_DEVFSD_VERBOSE
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
Eric Andersenf18bd892003-12-19 11:07:59 +0000306#ifdef CONFIG_DEVFSD_VERBOSE
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' &&
840 info->devname[2]=='g' && !info->devname[3]) &&
841 ( info->devname[0]=='i' && info->devname[1]=='n' &&
842 info->devname[2]=='i' && info->devname[3]=='c' &&
843 info->devname[4]=='t' && info->devname[5]=='l' && !info->devname[6])))
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000844 return;
845 for (entry = first_config; entry != NULL; entry = entry->next)
846 {
847 /* First check if action matches the type, then check if name matches */
848 if (info->type != entry->action.when || regexec (&entry->preg, info->devname, MAX_SUBEXPR, mbuf, 0) != 0 )
849 continue;
850 for (n = 0; (n < MAX_SUBEXPR) && (mbuf[n].rm_so != -1); ++n)
851 /* VOID */;
Eric Andersenf18bd892003-12-19 11:07:59 +0000852#ifdef CONFIG_DEBUG
853 msg_logger( NO_DIE, LOG_INFO, "service_name(): action.what %d\n", entry->action.what);
854#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000855 switch (entry->action.what)
856 {
857 case AC_PERMISSIONS:
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000858 action_permissions (info, entry);
859 break;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000860#ifdef CONFIG_DEVFSD_MODLOAD
861 case AC_MODLOAD:
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000862 action_modload (info, entry);
863 break;
864#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000865 case AC_EXECUTE:
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000866 action_execute (info, entry, mbuf, n);
867 break;
868 case AC_COPY:
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000869 action_copy (info, entry, mbuf, n);
870 break;
871 case AC_IGNORE:
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000872 return;
873 /*break;*/
874 case AC_MKOLDCOMPAT:
875 case AC_MKNEWCOMPAT:
876 case AC_RMOLDCOMPAT:
877 case AC_RMNEWCOMPAT:
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000878 action_compat (info, entry->action.what);
879 break;
880 default:
Eric Andersenf18bd892003-12-19 11:07:59 +0000881#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000882 msg_logger( DIE, LOG_ERR, "Unknown action\n");
Eric Andersenf18bd892003-12-19 11:07:59 +0000883#else
884 exit(EXIT_FAILURE);
885#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000886 /*break;*/
887 }
888 }
889} /* End Function service_name */
890
891static void action_permissions (const struct devfsd_notify_struct *info,
892 const struct config_entry_struct *entry)
893/* [SUMMARY] Update permissions for a device entry.
894 <info> The devfs change.
895 <entry> The config file entry.
896 [RETURNS] Nothing.
897*/
898{
899 struct stat statbuf;
900
Eric Andersenf18bd892003-12-19 11:07:59 +0000901#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000902 msg_logger( NO_DIE, LOG_INFO, "action_permission()\n");
903#endif
904
905 if ( stat (info->devname, &statbuf) != 0 ||
906 chmod (info->devname,(statbuf.st_mode & S_IFMT) | (entry->u.permissions.mode & ~S_IFMT)) != 0 ||
907 chown (info->devname, entry->u.permissions.uid, entry->u.permissions.gid) != 0)
908 {
909#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000910 msg_logger( NO_DIE, LOG_ERR, "chmod() or chown(): %s: %m\n",info->devname);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000911#endif
912 return;
913 }
914
915} /* End Function action_permissions */
916
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000917#ifdef CONFIG_DEVFSD_MODLOAD
918static void action_modload (const struct devfsd_notify_struct *info,
919 const struct config_entry_struct *entry)
920/* [SUMMARY] Load a module.
921 <info> The devfs change.
922 <entry> The config file entry.
923 [RETURNS] Nothing.
924*/
925{
926 char *argv[6];
927 char device[STRING_LENGTH];
928
929 argv[0] = MODPROBE;
Eric Andersenf18bd892003-12-19 11:07:59 +0000930 argv[1] = MODPROBE_SWITCH_1; /* "-k" */
931 argv[2] = MODPROBE_SWITCH_2; /* "-C" */
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000932 argv[3] = CONFIG_MODULES_DEVFS;
933 argv[4] = device;
934 argv[5] = NULL;
Eric Andersenf18bd892003-12-19 11:07:59 +0000935
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000936 snprintf (device, sizeof (device), "/dev/%s", info->devname);
Eric Andersenf18bd892003-12-19 11:07:59 +0000937 #ifdef CONFIG_DEBUG
938 msg_logger( NO_DIE, LOG_INFO, "action_modload():%s %s %s %s %s\n",argv[0],argv[1],argv[2],argv[3],argv[4]);
939 #endif
Glenn L McGrath3860b2e2003-11-30 23:46:06 +0000940 fork_and_execute(DIE, argv[0], argv);
941} /* End Function action_modload */
942#endif
943
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000944static void action_execute (const struct devfsd_notify_struct *info,
945 const struct config_entry_struct *entry,
946 const regmatch_t *regexpr, unsigned int numexpr)
947/* [SUMMARY] Execute a programme.
948 <info> The devfs change.
949 <entry> The config file entry.
950 <regexpr> The number of subexpression (start, end) offsets within the
951 device name.
952 <numexpr> The number of elements within <<regexpr>>.
953 [RETURNS] Nothing.
954*/
955{
956 unsigned int count;
957 struct get_variable_info gv_info;
958 char *argv[MAX_ARGS + 1];
959 char largv[MAX_ARGS + 1][STRING_LENGTH];
960
Eric Andersenf18bd892003-12-19 11:07:59 +0000961#ifdef CONFIG_DEBUG
Tim Riker6fe55ae2003-10-17 18:54:55 +0000962 int i;
Eric Andersenf18bd892003-12-19 11:07:59 +0000963 char buff[512];
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000964#endif
965
966 gv_info.info = info;
967 gv_info.devname = info->devname;
968 snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname);
969 for (count = 0; entry->u.execute.argv[count] != NULL; ++count)
970 {
971 expand_expression (largv[count], STRING_LENGTH,
972 entry->u.execute.argv[count],
973 get_variable, &gv_info,
974 gv_info.devname, regexpr, numexpr );
975 argv[count] = largv[count];
976 }
977 argv[count] = NULL;
978
Eric Andersenf18bd892003-12-19 11:07:59 +0000979#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000980 buff[0]='\0';
981 for(i=0;argv[i]!=NULL;i++) /* argv[i] < MAX_ARGS + 1 */
982 {
983 strcat(buff," ");
Eric Andersenf18bd892003-12-19 11:07:59 +0000984 if( (strlen(buff)+ 1 + strlen(argv[i])) >= 512)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +0000985 break;
986 strcat(buff,argv[i]);
987 }
988 strcat(buff,"\n");
989 msg_logger( NO_DIE, LOG_INFO, "action_execute(): %s",buff);
990#endif
991
992 fork_and_execute(NO_DIE, argv[0], argv);
993} /* End Function action_execute */
994
995
996static void action_copy (const struct devfsd_notify_struct *info,
997 const struct config_entry_struct *entry,
998 const regmatch_t *regexpr, unsigned int numexpr)
999/* [SUMMARY] Copy permissions.
1000 <info> The devfs change.
1001 <entry> The config file entry.
1002 <regexpr> This list of subexpression (start, end) offsets within the
1003 device name.
1004 <numexpr> The number of elements in <<regexpr>>.
1005 [RETURNS] Nothing.
1006*/
1007{
1008 mode_t new_mode;
1009 struct get_variable_info gv_info;
1010 struct stat source_stat, dest_stat;
1011 char source[STRING_LENGTH], destination[STRING_LENGTH];
1012 dest_stat.st_mode = 0;
1013
Eric Andersenf18bd892003-12-19 11:07:59 +00001014#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001015 msg_logger( NO_DIE, LOG_INFO, "action_copy()\n");
1016#endif
1017
1018 if ( (info->type == DEVFSD_NOTIFY_CHANGE) && S_ISLNK (info->mode) )
1019 return;
1020 gv_info.info = info;
1021 gv_info.devname = info->devname;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001022
Eric Andersenf18bd892003-12-19 11:07:59 +00001023 snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001024 expand_expression (source, STRING_LENGTH, entry->u.copy.source,
1025 get_variable, &gv_info, gv_info.devname,
1026 regexpr, numexpr);
1027
1028 expand_expression (destination, STRING_LENGTH, entry->u.copy.destination,
1029 get_variable, &gv_info, gv_info.devname,
1030 regexpr, numexpr);
1031
1032 if ( !make_dir_tree (destination) || lstat (source, &source_stat) != 0)
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001033 return;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001034 lstat (destination, &dest_stat);
1035 new_mode = source_stat.st_mode & ~S_ISVTX;
1036 if (info->type == DEVFSD_NOTIFY_CREATE)
1037 new_mode |= S_ISVTX;
1038 else if ( (info->type == DEVFSD_NOTIFY_CHANGE) && (dest_stat.st_mode & S_ISVTX) )
1039 new_mode |= S_ISVTX;
Eric Andersenf18bd892003-12-19 11:07:59 +00001040#ifdef CONFIG_DEBUG
1041 if ( !copy_inode (destination, &dest_stat, new_mode, source, &source_stat) && (errno != EEXIST))
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001042 msg_logger( NO_DIE, LOG_ERR, "copy_inode(): %s to %s: %m\n", source, destination);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001043#else
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001044 copy_inode (destination, &dest_stat, new_mode, source, &source_stat);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001045#endif
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001046 return;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001047} /* End Function action_copy */
1048
Eric Andersenf18bd892003-12-19 11:07:59 +00001049static void action_compat (const struct devfsd_notify_struct *info, unsigned int action)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001050/* [SUMMARY] Process a compatibility request.
1051 <info> The devfs change.
1052 <action> The action to take.
1053 [RETURNS] Nothing.
1054*/
1055{
1056 const char *compat_name = NULL;
1057 const char *dest_name = info->devname;
Eric Andersenf18bd892003-12-19 11:07:59 +00001058 char *ptr=NULL;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001059 char compat_buf[STRING_LENGTH], dest_buf[STRING_LENGTH];
Eric Andersenf18bd892003-12-19 11:07:59 +00001060 int mode, host, bus, target, lun;
1061 unsigned int i;
1062 char rewind_;
1063 /* 1 to 5 "scsi/" , 6 to 9 "ide/host" */
1064 const char *fmt[] = { NULL ,
1065 "sg/c%db%dt%du%d", /* scsi/generic */
1066 "sd/c%db%dt%du%d", /* scsi/disc */
1067 "sr/c%db%dt%du%d", /* scsi/cd */
1068 "sd/c%db%dt%du%dp%d", /* scsi/part */
1069 "st/c%db%dt%du%dm%d%c", /* scsi/mt */
1070 "ide/hd/c%db%dt%du%d", /* ide/host/disc */
1071 "ide/cd/c%db%dt%du%d", /* ide/host/cd */
1072 "ide/hd/c%db%dt%du%dp%d", /* ide/host/part */
1073 "ide/mt/c%db%dt%du%d%s", /* ide/host/mt */
1074 NULL };
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001075
1076 /* First construct compatibility name */
1077 switch (action)
1078 {
1079 case AC_MKOLDCOMPAT:
1080 case AC_RMOLDCOMPAT:
1081 compat_name = get_old_name (info->devname, info->namelen, compat_buf, info->major, info->minor);
1082 break;
1083 case AC_MKNEWCOMPAT:
1084 case AC_RMNEWCOMPAT:
Eric Andersenf18bd892003-12-19 11:07:59 +00001085 ptr = strrchr (info->devname, '/') + 1;
1086 i=scan_dev_name(info->devname, info->namelen, ptr);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001087
Eric Andersenf18bd892003-12-19 11:07:59 +00001088#ifdef CONFIG_DEBUG
1089 msg_logger( NO_DIE, LOG_INFO, "action_compat(): scan_dev_name() returned %d\n", i);
1090#endif
1091
1092 /* nothing found */
1093 if(i==0 || i > 9)
1094 return;
1095
1096 sscanf (info->devname +((i<6)?5:4), "host%d/bus%d/target%d/lun%d/", &host, &bus, &target, &lun);
1097 snprintf (dest_buf, sizeof (dest_buf), "../%s", info->devname + ((i>5)?4:0));
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001098 dest_name = dest_buf;
Eric Andersenf18bd892003-12-19 11:07:59 +00001099 compat_name = compat_buf;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001100
Eric Andersenf18bd892003-12-19 11:07:59 +00001101
1102 /* 1 == scsi/generic 2 == scsi/disc 3 == scsi/cd 6 == ide/host/disc 7 == ide/host/cd */
1103 if( i == 1 || i == 2 || i == 3 || i == 6 || i ==7 )
1104 sprintf ( compat_buf, fmt[i], host, bus, target, lun);
1105
1106 /* 4 == scsi/part 8 == ide/host/part */
1107 if( i == 4 || i == 8)
1108 sprintf ( compat_buf, fmt[i], host, bus, target, lun, atoi (ptr + 4) );
1109
1110 /* 5 == scsi/mt */
1111 if( i == 5)
1112 {
1113 rewind_ = info->devname[info->namelen - 1];
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001114 if (rewind_ != 'n')
1115 rewind_ = '\0';
Eric Andersenf18bd892003-12-19 11:07:59 +00001116 mode=0;
1117 if(ptr[2] == 'l' /*108*/ || ptr[2] == 'm'/*109*/)
1118 mode = ptr[2] - 107; /* 1 or 2 */
1119 if(ptr[2] == 'a')
1120 mode = 3;
1121 sprintf (compat_buf, fmt [i], host, bus, target, lun, mode, rewind_);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001122 }
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001123
Eric Andersenf18bd892003-12-19 11:07:59 +00001124 /* 9 == ide/host/mt */
1125 if( i == 9 )
1126 snprintf (compat_buf, sizeof (compat_buf), fmt[i], host, bus, target, lun, ptr + 2);
1127 /* esac */
1128 } /* switch(action) */
1129
1130 if(compat_name == NULL )
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001131 return;
Eric Andersenf18bd892003-12-19 11:07:59 +00001132
1133#ifdef CONFIG_DEBUG
1134 msg_logger( NO_DIE, LOG_INFO, "action_compat(): %s\n", compat_name);
1135#endif
1136
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001137 /* Now decide what to do with it */
1138 switch (action)
1139 {
1140 case AC_MKOLDCOMPAT:
1141 case AC_MKNEWCOMPAT:
1142 mksymlink (dest_name, compat_name);
1143 break;
1144 case AC_RMOLDCOMPAT:
1145 case AC_RMNEWCOMPAT:
Eric Andersenf18bd892003-12-19 11:07:59 +00001146#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001147 if (unlink (compat_name) != 0)
1148 msg_logger( NO_DIE, LOG_ERR, "unlink(): %s: %m\n", compat_name);
1149#else
1150 unlink (compat_name);
1151#endif
1152 break;
Eric Andersenf18bd892003-12-19 11:07:59 +00001153 /*esac*/
1154 } /* switch(action) */
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001155} /* End Function action_compat */
1156
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001157static void restore(char *spath, struct stat source_stat, int rootlen)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001158{
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001159 char dpath[STRING_LENGTH];
1160 struct stat dest_stat;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001161
Eric Andersenf18bd892003-12-19 11:07:59 +00001162#ifdef CONFIG_DEBUG
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001163 msg_logger( NO_DIE, LOG_INFO, "restore()\n");
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001164#endif
1165
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001166 dest_stat.st_mode = 0;
1167 snprintf (dpath, sizeof dpath, "%s%s", mount_point, spath + rootlen);
1168 lstat (dpath, &dest_stat);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001169
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001170 if ( S_ISLNK (source_stat.st_mode) || (source_stat.st_mode & S_ISVTX) )
1171 copy_inode (dpath, &dest_stat, (source_stat.st_mode & ~S_ISVTX) , spath, &source_stat);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001172
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001173 if ( S_ISDIR (source_stat.st_mode) )
1174 dir_operation(RESTORE, spath, rootlen,NULL);
1175}
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001176
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001177
1178static int copy_inode (const char *destpath, const struct stat *dest_stat,
1179 mode_t new_mode,
1180 const char *sourcepath, const struct stat *source_stat)
1181/* [SUMMARY] Copy an inode.
1182 <destpath> The destination path. An existing inode may be deleted.
1183 <dest_stat> The destination stat(2) information.
1184 <new_mode> The desired new mode for the destination.
1185 <sourcepath> The source path.
1186 <source_stat> The source stat(2) information.
1187 [RETURNS] TRUE on success, else FALSE.
1188*/
1189{
Eric Andersenf18bd892003-12-19 11:07:59 +00001190 int source_len, dest_len;
1191 char source_link[STRING_LENGTH], dest_link[STRING_LENGTH];
1192 int fd, val;
1193 struct sockaddr_un un_addr;
1194 char symlink_val[STRING_LENGTH];
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001195
Eric Andersenf18bd892003-12-19 11:07:59 +00001196#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001197 msg_logger( NO_DIE, LOG_INFO, "copy_inode()\n");
1198#endif
1199
1200 if ( (source_stat->st_mode & S_IFMT) == (dest_stat->st_mode & S_IFMT) )
1201 {
1202 /* Same type */
1203 if ( S_ISLNK (source_stat->st_mode) )
1204 {
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001205 if (( source_len = readlink (sourcepath, source_link, STRING_LENGTH - 1) ) < 0 ||
1206 ( dest_len = readlink (destpath , dest_link , STRING_LENGTH - 1) ) < 0 )
1207 return (FALSE);
1208 source_link[source_len] = '\0';
1209 dest_link[dest_len] = '\0';
1210 if ( (source_len != dest_len) || (strcmp (source_link, dest_link) != 0) )
1211 {
1212 unlink (destpath);
1213 symlink (source_link, destpath);
1214 }
1215 return (TRUE);
1216 } /* Else not a symlink */
1217 chmod (destpath, new_mode & ~S_IFMT);
1218 chown (destpath, source_stat->st_uid, source_stat->st_gid);
1219 return (TRUE);
1220 }
1221 /* Different types: unlink and create */
1222 unlink (destpath);
1223 switch (source_stat->st_mode & S_IFMT)
1224 {
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001225 case S_IFSOCK:
1226 if ( ( fd = socket (AF_UNIX, SOCK_STREAM, 0) ) < 0 )
1227 break;
1228 un_addr.sun_family = AF_UNIX;
1229 snprintf (un_addr.sun_path, sizeof (un_addr.sun_path), "%s", destpath);
1230 val = bind (fd, (struct sockaddr *) &un_addr, (int) sizeof un_addr);
1231 close (fd);
1232 if (val != 0 || chmod (destpath, new_mode & ~S_IFMT) != 0)
1233 break;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001234 goto do_chown;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001235 case S_IFLNK:
1236 if ( ( val = readlink (sourcepath, symlink_val, STRING_LENGTH - 1) ) < 0 )
1237 break;
1238 symlink_val[val] = '\0';
1239 if (symlink (symlink_val, destpath) == 0)
1240 return (TRUE);
1241 break;
1242 case S_IFREG:
1243 if ( ( fd = open (destpath, O_RDONLY | O_CREAT, new_mode & ~S_IFMT) ) < 0 )
1244 break;
1245 close (fd);
1246 if (chmod (destpath, new_mode & ~S_IFMT) != 0)
1247 break;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001248 goto do_chown;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001249 case S_IFBLK:
1250 case S_IFCHR:
1251 case S_IFIFO:
1252 if (mknod (destpath, new_mode, source_stat->st_rdev) != 0)
1253 break;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001254 goto do_chown;
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001255 case S_IFDIR:
1256 if (mkdir (destpath, new_mode & ~S_IFMT) != 0)
1257 break;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001258do_chown:
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001259 if (chown (destpath, source_stat->st_uid, source_stat->st_gid) == 0)
1260 return (TRUE);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001261 /*break;*/
1262 }
1263 return (FALSE);
1264} /* End Function copy_inode */
1265
1266static void free_config ()
1267/* [SUMMARY] Free the configuration information.
1268 [RETURNS] Nothing.
1269*/
1270{
1271 struct config_entry_struct *c_entry;
1272 void *next;
1273
Eric Andersenf18bd892003-12-19 11:07:59 +00001274#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001275 msg_logger( NO_DIE, LOG_INFO, "free_config()\n");
1276#endif
1277
1278 for (c_entry = first_config; c_entry != NULL; c_entry = next)
1279 {
1280 unsigned int count;
1281
1282 next = c_entry->next;
1283 regfree (&c_entry->preg);
1284 if (c_entry->action.what == AC_EXECUTE)
1285 {
1286 for (count = 0; count < MAX_ARGS; ++count)
1287 {
1288 if (c_entry->u.execute.argv[count] == NULL)
1289 break;
1290 free (c_entry->u.execute.argv[count]);
1291 }
1292 }
1293 free (c_entry);
1294 }
1295 first_config = NULL;
1296 last_config = NULL;
1297} /* End Function free_config */
1298
1299static int get_uid_gid (int flag, const char *string)
1300/* [SUMMARY] Convert a string to a UID or GID value.
1301 <flag> "UID" or "GID".
1302 <string> The string.
1303 [RETURNS] The UID or GID value.
1304*/
1305{
1306 struct passwd *pw_ent;
1307 struct group *grp_ent;
Eric Andersenf18bd892003-12-19 11:07:59 +00001308#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001309 char * msg="user";
Eric Andersenf18bd892003-12-19 11:07:59 +00001310#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001311
Eric Andersenf18bd892003-12-19 11:07:59 +00001312#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001313 msg_logger( NO_DIE, LOG_INFO, "get_uid_gid()\n");
1314
1315
1316 if(flag != UID && flag != GID )
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001317 msg_logger( DIE, LOG_ERR,"get_uid_gid(): flag != UID && flag != GID\n");
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001318#endif
1319
1320 if ( isdigit (string[0]) || ( (string[0] == '-') && isdigit (string[1]) ) )
1321 return atoi (string);
1322
1323 if ( flag == UID && ( pw_ent = getpwnam (string) ) != NULL )
1324 return (pw_ent->pw_uid);
1325
1326 if ( flag == GID && ( grp_ent = getgrnam (string) ) != NULL )
1327 return (grp_ent->gr_gid);
Eric Andersenf18bd892003-12-19 11:07:59 +00001328#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001329 else
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001330 msg="group";
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001331
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001332 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 +00001333#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001334 return (0);
1335}/* End Function get_uid_gid */
1336
1337static mode_t get_mode (const char *string)
1338/* [SUMMARY] Convert a string to a mode value.
1339 <string> The string.
1340 [RETURNS] The mode value.
1341*/
1342{
1343 mode_t mode;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001344 int i;
Eric Andersenf18bd892003-12-19 11:07:59 +00001345#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001346 msg_logger( NO_DIE, LOG_INFO, "get_mode()\n");
1347#endif
1348
1349 if ( isdigit (string[0]) )
1350 return strtoul (string, NULL, 8);
1351 if (strlen (string) != 9)
Eric Andersenf18bd892003-12-19 11:07:59 +00001352#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001353 msg_logger( DIE, LOG_ERR, "bad mode: %s\n", string);
Eric Andersenf18bd892003-12-19 11:07:59 +00001354#else
1355 exit(EXIT_FAILURE);
1356#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001357 mode = 0;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001358 i= S_IRUSR;
1359 while(i>0)
1360 {
1361 if(string[0]=='r'||string[0]=='w'||string[0]=='x')
1362 mode+=i;
1363 i=i/2;
1364 string++;
1365 }
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001366 return (mode);
1367} /* End Function get_mode */
1368
1369static void signal_handler (int sig)
1370{
Eric Andersenf18bd892003-12-19 11:07:59 +00001371#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001372 msg_logger( NO_DIE, LOG_INFO, "signal_handler()\n");
1373#endif
1374
1375 caught_signal = TRUE;
1376 if (sig == SIGHUP)
1377 caught_sighup = TRUE;
1378#ifdef CONFIG_DEVFSD_VERBOSE
1379 msg_logger( NO_DIE, LOG_INFO, "Caught %s\n",(sig == SIGHUP)?"SIGHUP" : "SIGUSR1");
1380#endif
1381} /* End Function signal_handler */
1382
1383static const char *get_variable (const char *variable, void *info)
1384{
1385 struct get_variable_info *gv_info = info;
1386 static char hostname[STRING_LENGTH], sbuf[STRING_LENGTH];
Eric Andersenf18bd892003-12-19 11:07:59 +00001387 const char *field_names[] = { "hostname", "mntpt", "devpath", "devname",
1388 "uid", "gid", "mode", hostname, mount_point,
1389 gv_info->devpath, gv_info->devname, 0 };
1390 short int i;
1391#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001392 msg_logger( NO_DIE, LOG_INFO, "get_variable()\n");
1393#endif
1394
1395 if (gethostname (hostname, STRING_LENGTH - 1) != 0)
Eric Andersenf18bd892003-12-19 11:07:59 +00001396#ifdef CONFIG_DEVFSD_VERBOSE
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001397 msg_logger( DIE, LOG_ERR, "gethostname(): %m\n");
Eric Andersenf18bd892003-12-19 11:07:59 +00001398#else
1399 exit(EXIT_FAILURE);
1400#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001401 /* Here on error we should do exit(RV_SYS_ERROR), instead we do exit(EXIT_FAILURE) */
Eric Andersenf18bd892003-12-19 11:07:59 +00001402 hostname[STRING_LENGTH - 1] = '\0';
1403
1404 /* compare_string_array returns i>=0 */
1405 i=compare_string_array(field_names, variable);
1406
1407 if ( i > 6 && (i > 1 && gv_info == NULL))
1408 return (NULL);
1409 if( i >= 0 || i <= 3)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001410 {
Eric Andersenf18bd892003-12-19 11:07:59 +00001411#ifdef CONFIG_DEBUG
1412 msg_logger( NO_DIE, LOG_INFO, "get_variable(): i=%d %s\n",i ,field_names[i+7]);
1413#endif
1414 return(field_names[i+7]);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001415 }
Eric Andersenf18bd892003-12-19 11:07:59 +00001416
1417 if(i == 4 )
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001418 sprintf (sbuf, "%u", gv_info->info->uid);
Eric Andersenf18bd892003-12-19 11:07:59 +00001419 else if(i == 5)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001420 sprintf (sbuf, "%u", gv_info->info->gid);
Eric Andersenf18bd892003-12-19 11:07:59 +00001421 else if(i == 6)
1422 sprintf (sbuf, "%o", gv_info->info->mode);
1423#ifdef CONFIG_DEBUG
1424 msg_logger( NO_DIE, LOG_INFO, "get_variable(): %s\n", sbuf);
1425#endif
1426 return (sbuf);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001427} /* End Function get_variable */
1428
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001429static void service(struct stat statbuf, char *path)
1430{
1431 struct devfsd_notify_struct info;
1432
Eric Andersenf18bd892003-12-19 11:07:59 +00001433#ifdef CONFIG_DEBUG
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001434 msg_logger( NO_DIE, LOG_INFO, "service()\n");
1435#endif
1436
1437 memset (&info, 0, sizeof info);
1438 info.type = DEVFSD_NOTIFY_REGISTERED;
1439 info.mode = statbuf.st_mode;
1440 info.major = major (statbuf.st_rdev);
1441 info.minor = minor (statbuf.st_rdev);
1442 info.uid = statbuf.st_uid;
1443 info.gid = statbuf.st_gid;
1444 snprintf (info.devname, sizeof (info.devname), "%s", path + strlen (mount_point) + 1);
1445 info.namelen = strlen (info.devname);
1446 service_name (&info);
1447 if ( S_ISDIR (statbuf.st_mode) )
1448 dir_operation(SERVICE,path,0,NULL);
1449}
1450
1451static void dir_operation(int type, const char * dir_name, int var, unsigned long *event_mask)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001452/* [SUMMARY] Scan a directory tree and generate register events on leaf nodes.
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001453 <flag> To choose which function to perform
1454 <dp> The directory pointer. This is closed upon completion.
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001455 <dir_name> The name of the directory.
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001456 <rootlen> string length parameter.
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001457 [RETURNS] Nothing.
1458*/
1459{
1460 struct stat statbuf;
1461 DIR *dp;
1462 struct dirent *de;
1463 char path[STRING_LENGTH];
1464
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001465
Eric Andersenf18bd892003-12-19 11:07:59 +00001466#ifdef CONFIG_DEBUG
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001467 msg_logger( NO_DIE, LOG_INFO, "dir_operation()\n");
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001468#endif
1469
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001470 if((dp = opendir( dir_name))==NULL)
1471 {
Eric Andersenf18bd892003-12-19 11:07:59 +00001472#ifdef CONFIG_DEBUG
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001473 msg_logger( NO_DIE, LOG_ERR, "opendir(): %s: %m\n", dir_name);
Eric Andersenf18bd892003-12-19 11:07:59 +00001474#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001475 return;
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001476 }
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001477
1478 while ( (de = readdir (dp) ) != NULL )
1479 {
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001480
Eric Andersenf18bd892003-12-19 11:07:59 +00001481 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 +00001482 continue;
Eric Andersenf18bd892003-12-19 11:07:59 +00001483 snprintf (path, sizeof (path), "%s/%s", dir_name, de->d_name);
1484#ifdef CONFIG_DEBUG
1485 msg_logger( NO_DIE, LOG_ERR, "dir_operation(): %s\n", path);
1486#endif
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001487
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001488 if (lstat (path, &statbuf) != 0)
1489 {
Eric Andersenf18bd892003-12-19 11:07:59 +00001490#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001491 msg_logger( NO_DIE, LOG_ERR, "%s: %m\n", path);
1492#endif
1493 continue;
1494 }
Glenn L McGrath3860b2e2003-11-30 23:46:06 +00001495 switch(type)
1496 {
1497 case SERVICE:
1498 service(statbuf,path);
1499 break;
1500 case RESTORE:
1501 restore(path, statbuf, var);
1502 break;
1503 case READ_CONFIG:
1504 read_config_file (path, var, event_mask);
1505 break;
1506 }
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001507 }
1508 closedir (dp);
1509} /* End Function do_scan_and_service */
1510
1511static int mksymlink (const char *oldpath, const char *newpath)
1512/* [SUMMARY] Create a symlink, creating intervening directories as required.
1513 <oldpath> The string contained in the symlink.
1514 <newpath> The name of the new symlink.
1515 [RETURNS] 0 on success, else -1.
1516*/
1517{
Eric Andersenf18bd892003-12-19 11:07:59 +00001518#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001519 msg_logger( NO_DIE, LOG_INFO, "mksymlink()\n", newpath);
1520#endif
1521
1522
1523 if ( !make_dir_tree (newpath) )
1524 return (-1);
1525
1526 if (symlink (oldpath, newpath) != 0)
1527 {
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001528 if (errno != EEXIST)
Eric Andersenf18bd892003-12-19 11:07:59 +00001529 {
1530#ifdef CONFIG_DEBUG
1531 msg_logger( NO_DIE, LOG_ERR, "mksymlink(): %s to %s: %m\n", oldpath, newpath);
1532#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001533 return (-1);
Eric Andersenf18bd892003-12-19 11:07:59 +00001534 }
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001535 }
1536 return (0);
1537} /* End Function mksymlink */
1538
1539
1540static int make_dir_tree (const char *path)
1541/* [SUMMARY] Creating intervening directories for a path as required.
Eric Andersen52899692003-10-22 10:10:50 +00001542 <path> The full pathname (including the leaf node).
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001543 [RETURNS] TRUE on success, else FALSE.
1544*/
1545{
Eric Andersenf18bd892003-12-19 11:07:59 +00001546#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001547 msg_logger( NO_DIE, LOG_INFO, "make_dir_tree()\n");
1548#endif
Eric Andersen52899692003-10-22 10:10:50 +00001549 if (bb_make_directory( dirname((char *)path), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ,FILEUTILS_RECUR )==-1)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001550 {
Eric Andersenf18bd892003-12-19 11:07:59 +00001551#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001552 msg_logger( NO_DIE, LOG_ERR, "make_dir_tree(): %s: %m\n", path);
1553#endif
1554 return (FALSE);
1555 }
1556 return(TRUE);
1557} /* End Function make_dir_tree */
1558
1559static int expand_expression(char *output, unsigned int outsize,
1560 const char *input,
1561 const char *(*get_variable_func)(const char *variable, void *info),
1562 void *info,
1563 const char *devname,
1564 const regmatch_t *ex, unsigned int numexp)
1565/* [SUMMARY] Expand enviroment variables and regular subexpressions in string.
1566 <output> The output expanded expression is written here.
1567 <length> The size of the output buffer.
1568 <input> The input expression. This may equal <<output>>.
1569 <get_variable> A function which will be used to get variable values. If
1570 this returns NULL, the environment is searched instead. If this is NULL,
1571 only the environment is searched.
1572 <info> An arbitrary pointer passed to <<get_variable>>.
1573 <devname> Device name; specifically, this is the string that contains all
1574 of the regular subexpressions.
1575 <ex> Array of start / end offsets into info->devname for each subexpression
1576 <numexp> Number of regular subexpressions found in <<devname>>.
1577 [RETURNS] TRUE on success, else FALSE.
1578*/
1579{
1580 char temp[STRING_LENGTH];
1581
Eric Andersenf18bd892003-12-19 11:07:59 +00001582#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001583 msg_logger( NO_DIE, LOG_INFO, "expand_expression()\n");
1584#endif
1585
1586 if ( !st_expr_expand (temp, STRING_LENGTH, input, get_variable_func, info) )
1587 return (FALSE);
1588 expand_regexp (output, outsize, temp, devname, ex, numexp);
1589 return (TRUE);
1590} /* End Function expand_expression */
1591
1592static void expand_regexp (char *output, size_t outsize, const char *input,
1593 const char *devname,
1594 const regmatch_t *ex, unsigned int numex )
1595/* [SUMMARY] Expand all occurrences of the regular subexpressions \0 to \9.
1596 <output> The output expanded expression is written here.
1597 <outsize> The size of the output buffer.
1598 <input> The input expression. This may NOT equal <<output>>, because
1599 supporting that would require yet another string-copy. However, it's not
1600 hard to write a simple wrapper function to add this functionality for those
1601 few cases that need it.
1602 <devname> Device name; specifically, this is the string that contains all
1603 of the regular subexpressions.
1604 <ex> An array of start and end offsets into <<devname>>, one for each
1605 subexpression
1606 <numex> Number of subexpressions in the offset-array <<ex>>.
1607 [RETURNS] Nothing.
1608*/
1609{
1610 const char last_exp = '0' - 1 + numex;
1611 int c = -1;
1612
Eric Andersenf18bd892003-12-19 11:07:59 +00001613#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001614 msg_logger( NO_DIE, LOG_INFO, "expand_regexp()\n");
1615#endif
1616
1617 /* Guarantee NULL termination by writing an explicit '\0' character into
1618 the very last byte */
1619 if (outsize)
1620 output[--outsize] = '\0';
1621 /* Copy the input string into the output buffer, replacing '\\' with '\'
1622 and '\0' .. '\9' with subexpressions 0 .. 9, if they exist. Other \x
1623 codes are deleted */
1624 while ( (c != '\0') && (outsize != 0) )
1625 {
1626 c = *input;
1627 ++input;
1628 if (c == '\\')
1629 {
1630 c = *input;
1631 ++input;
1632 if (c != '\\')
1633 {
1634 if ((c >= '0') && (c <= last_exp))
1635 {
1636 const regmatch_t *subexp = ex + (c - '0');
1637 unsigned int sublen = subexp->rm_eo - subexp->rm_so;
1638
1639 /* Range checking */
1640 if (sublen > outsize)
1641 sublen = outsize;
1642 strncpy (output, devname + subexp->rm_so, sublen);
1643 output += sublen;
1644 outsize -= sublen;
1645 }
1646 continue;
1647 }
1648 }
1649 *output = c;
1650 ++output;
1651 --outsize;
1652 } /* while */
1653} /* End Function expand_regexp */
1654
1655
1656/* from compat_name.c */
1657
1658struct translate_struct
1659{
1660 char *match; /* The string to match to (up to length) */
1661 char *format; /* Format of output, "%s" takes data past match string,
1662 NULL is effectively "%s" (just more efficient) */
1663};
1664
1665static struct translate_struct translate_table[] =
1666{
1667 {"sound/", NULL},
1668 {"printers/", "lp%s"},
1669 {"v4l/", NULL},
1670 {"parports/", "parport%s"},
1671 {"fb/", "fb%s"},
1672 {"netlink/", NULL},
1673 {"loop/", "loop%s"},
1674 {"floppy/", "fd%s"},
1675 {"rd/", "ram%s"},
1676 {"md/", "md%s"}, /* Meta-devices */
1677 {"vc/", "tty%s"},
1678 {"misc/", NULL},
1679 {"isdn/", NULL},
1680 {"pg/", "pg%s"}, /* Parallel port generic ATAPI interface*/
1681 {"i2c/", "i2c-%s"},
1682 {"staliomem/", "staliomem%s"}, /* Stallion serial driver control */
1683 {"tts/E", "ttyE%s"}, /* Stallion serial driver */
1684 {"cua/E", "cue%s"}, /* Stallion serial driver callout */
1685 {"tts/R", "ttyR%s"}, /* Rocketport serial driver */
1686 {"cua/R", "cur%s"}, /* Rocketport serial driver callout */
1687 {"ip2/", "ip2%s"}, /* Computone serial driver control */
1688 {"tts/F", "ttyF%s"}, /* Computone serial driver */
1689 {"cua/F", "cuf%s"}, /* Computone serial driver callout */
1690 {"tts/C", "ttyC%s"}, /* Cyclades serial driver */
1691 {"cua/C", "cub%s"}, /* Cyclades serial driver callout */
1692 {"tts/", "ttyS%s"}, /* Generic serial: must be after others */
1693 {"cua/", "cua%s"}, /* Generic serial: must be after others */
1694 {"input/js", "js%s"}, /* Joystick driver */
1695 {NULL, NULL}
1696};
1697
1698const char *get_old_name (const char *devname, unsigned int namelen,
1699 char *buffer, unsigned int major, unsigned int minor)
1700/* [SUMMARY] Translate a kernel-supplied name into an old name.
1701 <devname> The device name provided by the kernel.
1702 <namelen> The length of the name.
1703 <buffer> A buffer that may be used. This should be at least 128 bytes long.
1704 <major> The major number for the device.
1705 <minor> The minor number for the device.
1706 [RETURNS] A pointer to the old name if known, else NULL.
1707*/
1708{
1709 const char *compat_name = NULL;
1710 char *ptr;
1711 struct translate_struct *trans;
Eric Andersenf18bd892003-12-19 11:07:59 +00001712 unsigned int i;
1713 char mode;
1714 int indexx;
1715 const char *pty1;
1716 const char *pty2;
1717 size_t len;
1718 /* 1 to 5 "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */
1719 const char *fmt[] = { NULL ,
1720 "sg%u", /* scsi/generic */
1721 NULL, /* scsi/disc */
1722 "sr%u", /* scsi/cd */
1723 NULL, /* scsi/part */
1724 "nst%u%c", /* scsi/mt */
1725 "hd%c" , /* ide/host/disc */
1726 "hd%c" , /* ide/host/cd */
1727 "hd%c%s", /* ide/host/part */
1728 "%sht%d", /* ide/host/mt */
1729 "sbpcd%u", /* sbp/ */
1730 "vcs%s", /* vcc/ */
1731 "%cty%c%c", /* pty/ */
1732 NULL };
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001733
Eric Andersenf18bd892003-12-19 11:07:59 +00001734#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001735 msg_logger( NO_DIE, LOG_INFO, "get_old_name()\n");
1736#endif
1737
1738 for (trans = translate_table; trans->match != NULL; ++trans)
1739 {
Eric Andersenf18bd892003-12-19 11:07:59 +00001740 len = strlen (trans->match);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001741
1742 if (strncmp (devname, trans->match, len) == 0)
1743 {
1744 if (trans->format == NULL)
1745 return (devname + len);
1746 sprintf (buffer, trans->format, devname + len);
1747 return (buffer);
1748 }
1749 }
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001750
Eric Andersenf18bd892003-12-19 11:07:59 +00001751 ptr = (strrchr (devname, '/') + 1);
1752 i = scan_dev_name(devname, namelen, ptr);
1753
1754 if( i > 0 && i < 13)
1755 compat_name = buffer;
1756 else
1757 return NULL;
1758
1759#ifdef CONFIG_DEBUG
1760 msg_logger( NO_DIE, LOG_INFO, "get_old_name(): scan_dev_name() returned %d\n", i);
1761#endif
1762
1763 /* 1 == scsi/generic, 3 == scsi/cd, 10 == sbp/ */
1764 if( i == 1 || i == 3 || i == 10 )
1765 sprintf (buffer, fmt[i], minor);
1766
1767 /* 2 ==scsi/disc, 4 == scsi/part */
1768 if( i == 2 || i == 4)
1769 compat_name = write_old_sd_name (buffer, major, minor,((i == 2)?"":(ptr + 4)));
1770
1771 /* 5 == scsi/mt */
1772 if( i == 5)
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001773 {
Eric Andersenf18bd892003-12-19 11:07:59 +00001774 mode = ptr[2];
1775 if (mode == 'n')
1776 mode = '\0';
1777 sprintf (buffer, fmt[i], minor & 0x1f, mode);
1778 if (devname[namelen - 1] != 'n')
1779 ++compat_name;
1780 }
1781 /* 6 == ide/host/disc, 7 == ide/host/cd, 8 == ide/host/part */
1782 if( i == 6 || i == 7 || i == 8 )
1783 sprintf (buffer, fmt[i] , get_old_ide_name (major, minor), ptr + 4); /* last arg should be ignored for i == 6 or i== 7 */
1784
1785 /* 9 == ide/host/mt */
1786 if( i == 9 )
1787 sprintf (buffer, fmt[i], ptr + 2, minor & 0x7f);
1788
1789 /* 11 == vcc/ */
1790 if( i == 11 )
1791 {
1792 sprintf (buffer, fmt[i], devname + 4);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001793 if (buffer[3] == '0')
1794 buffer[3] = '\0';
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001795 }
Eric Andersenf18bd892003-12-19 11:07:59 +00001796 /* 12 == pty/ */
1797 if( i == 12 )
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001798 {
Eric Andersenf18bd892003-12-19 11:07:59 +00001799 pty1 = "pqrstuvwxyzabcde";
1800 pty2 = "0123456789abcdef";
1801 indexx = atoi (devname + 5);
1802 sprintf (buffer, fmt[i], (devname[4] == 'm') ? 'p' : 't', pty1[indexx >> 4], pty2[indexx & 0x0f]);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001803 }
Eric Andersenf18bd892003-12-19 11:07:59 +00001804#ifdef CONFIG_DEBUG
1805 if(compat_name!=NULL)
1806 msg_logger( NO_DIE, LOG_INFO, "get_old_name(): compat_name %s\n", compat_name);
1807#endif
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001808 return (compat_name);
1809} /* End Function get_old_name */
1810
1811static char get_old_ide_name (unsigned int major, unsigned int minor)
1812/* [SUMMARY] Get the old IDE name for a device.
1813 <major> The major number for the device.
1814 <minor> The minor number for the device.
1815 [RETURNS] The drive letter.
1816*/
1817{
1818 char letter='y'; /* 121 */
1819 char c='a'; /* 97 */
1820 int i=IDE0_MAJOR;
1821
Eric Andersenf18bd892003-12-19 11:07:59 +00001822#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001823 msg_logger( NO_DIE, LOG_INFO, "get_old_ide_name()\n");
1824#endif
1825
1826 /* I hope it works like the previous code as it saves a few bytes. Tito ;P */
1827 do {
1828 if( i==IDE0_MAJOR || i==IDE1_MAJOR || i==IDE2_MAJOR ||
1829 i==IDE3_MAJOR || i==IDE4_MAJOR || i==IDE5_MAJOR ||
1830 i==IDE6_MAJOR || i==IDE7_MAJOR || i==IDE8_MAJOR ||
1831 i==IDE9_MAJOR )
1832 {
1833 if(i==major)
1834 {
1835 letter=c;
1836 break;
1837 }
1838 c+=2;
1839 }
1840 i++;
1841 } while(i<=IDE9_MAJOR);
1842
1843 if (minor > 63)
1844 ++letter;
1845 return (letter);
1846} /* End Function get_old_ide_name */
1847
1848static char *write_old_sd_name (char *buffer,
1849 unsigned int major, unsigned int minor,
1850 char *part)
1851/* [SUMMARY] Write the old SCSI disc name to a buffer.
1852 <buffer> The buffer to write to.
1853 <major> The major number for the device.
1854 <minor> The minor number for the device.
1855 <part> The partition string. Must be "" for a whole-disc entry.
1856 [RETURNS] A pointer to the buffer on success, else NULL.
1857*/
1858{
1859 unsigned int disc_index;
1860
Eric Andersenf18bd892003-12-19 11:07:59 +00001861#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001862 msg_logger( NO_DIE, LOG_INFO, "write_old_sd_name()\n");
1863#endif
1864
1865 if (major == 8)
1866 {
1867 sprintf (buffer, "sd%c%s", 'a' + (minor >> 4), part);
1868 return (buffer);
1869 }
1870 if ( (major > 64) && (major < 72) )
1871 {
1872 disc_index = ( (major - 64) << 4 ) + (minor >> 4);
1873 if (disc_index < 26)
1874 sprintf (buffer, "sd%c%s", 'a' + disc_index, part);
1875 else
1876 sprintf (buffer, "sd%c%c%s", 'a' + (disc_index / 26) - 1, 'a' + disc_index % 26,part);
1877 return (buffer);
1878 }
1879 return (NULL);
1880} /* End Function write_old_sd_name */
1881
1882
1883/* expression.c */
1884
1885/*EXPERIMENTAL_FUNCTION*/
1886
1887int st_expr_expand (char *output, unsigned int length, const char *input,
1888 const char *(*get_variable_func) (const char *variable,
1889 void *info),
1890 void *info)
1891/* [SUMMARY] Expand an expression using Borne Shell-like unquoted rules.
1892 <output> The output expanded expression is written here.
1893 <length> The size of the output buffer.
1894 <input> The input expression. This may equal <<output>>.
1895 <get_variable> A function which will be used to get variable values. If
1896 this returns NULL, the environment is searched instead. If this is NULL,
1897 only the environment is searched.
1898 <info> An arbitrary pointer passed to <<get_variable>>.
1899 [RETURNS] TRUE on success, else FALSE.
1900*/
1901{
1902 char ch;
1903 unsigned int len;
1904 unsigned int out_pos = 0;
1905 const char *env;
1906 const char *ptr;
1907 struct passwd *pwent;
1908 char buffer[BUFFER_SIZE], tmp[STRING_LENGTH];
1909
Eric Andersenf18bd892003-12-19 11:07:59 +00001910#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001911 msg_logger( NO_DIE, LOG_INFO, "st_expr_expand()\n");
1912#endif
1913
1914 if (length > BUFFER_SIZE)
1915 length = BUFFER_SIZE;
1916 for (; TRUE; ++input)
1917 {
1918 switch (ch = *input)
1919 {
1920 case '$':
1921 /* Variable expansion */
1922 input = expand_variable (buffer, length, &out_pos, ++input, get_variable_func, info);
1923 if (input == NULL)
1924 return (FALSE);
1925 break;
1926 case '~':
1927 /* Home directory expansion */
1928 ch = input[1];
1929 if ( isspace (ch) || (ch == '/') || (ch == '\0') )
1930 {
1931 /* User's own home directory: leave separator for next time */
1932 if ( ( env = getenv ("HOME") ) == NULL )
1933 {
1934#ifdef CONFIG_DEVFSD_VERBOSE
1935 msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, "HOME");
1936#endif
1937 return (FALSE);
1938 }
1939 len = strlen (env);
1940 if (len + out_pos >= length)
1941 goto st_expr_expand_out;
1942 memcpy (buffer + out_pos, env, len + 1);
1943 out_pos += len;
1944 continue;
1945 }
1946 /* Someone else's home directory */
1947 for (ptr = ++input; !isspace (ch) && (ch != '/') && (ch != '\0'); ch = *++ptr)
1948 /* VOID */ ;
1949 len = ptr - input;
1950 if (len >= sizeof tmp)
1951 goto st_expr_expand_out;
1952 safe_memcpy (tmp, input, len);
1953 input = ptr - 1;
1954 if ( ( pwent = getpwnam (tmp) ) == NULL )
1955 {
1956#ifdef CONFIG_DEVFSD_VERBOSE
Eric Andersenf18bd892003-12-19 11:07:59 +00001957 msg_logger( NO_DIE, LOG_INFO, "no pwent for: %s\n", tmp);
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00001958#endif
1959 return (FALSE);
1960 }
1961 len = strlen (pwent->pw_dir);
1962 if (len + out_pos >= length)
1963 goto st_expr_expand_out;
1964 memcpy (buffer + out_pos, pwent->pw_dir, len + 1);
1965 out_pos += len;
1966 break;
1967 case '\0':
1968 /* Falltrough */
1969 default:
1970 if (out_pos >= length)
1971 goto st_expr_expand_out;
1972 buffer[out_pos++] = ch;
1973 if (ch == '\0')
1974 {
1975 memcpy (output, buffer, out_pos);
1976 return (TRUE);
1977 }
1978 break;
1979 /* esac */
1980 }
1981 }
1982 return (FALSE);
1983st_expr_expand_out:
1984#ifdef CONFIG_DEVFSD_VERBOSE
1985 msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer);
1986#endif
1987 return (FALSE);
1988} /* End Function st_expr_expand */
1989
1990
1991/* Private functions follow */
1992
1993static const char *expand_variable (char *buffer, unsigned int length,
1994 unsigned int *out_pos, const char *input,
1995 const char *(*func) (const char *variable,
1996 void *info),
1997 void *info)
1998/* [SUMMARY] Expand a variable.
1999 <buffer> The buffer to write to.
2000 <length> The length of the output buffer.
2001 <out_pos> The current output position. This is updated.
2002 <input> A pointer to the input character pointer.
2003 <func> A function which will be used to get variable values. If this
2004 returns NULL, the environment is searched instead. If this is NULL, only
2005 the environment is searched.
2006 <info> An arbitrary pointer passed to <<func>>.
2007 <errfp> Diagnostic messages are written here.
2008 [RETURNS] A pointer to the end of this subexpression on success, else NULL.
2009*/
2010{
2011 char ch;
2012 int len;
2013 unsigned int open_braces;
2014 const char *env, *ptr;
2015 char tmp[STRING_LENGTH];
2016
Eric Andersenf18bd892003-12-19 11:07:59 +00002017#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00002018 msg_logger( NO_DIE, LOG_INFO, "expand_variable()\n");
2019#endif
2020
2021 ch = input[0];
2022 if (ch == '$')
2023 {
2024 /* Special case for "$$": PID */
2025 sprintf ( tmp, "%d", (int) getpid () );
2026 len = strlen (tmp);
2027 if (len + *out_pos >= length)
2028 goto expand_variable_out;
2029
2030 memcpy (buffer + *out_pos, tmp, len + 1);
2031 out_pos += len;
2032 return (input);
2033 }
2034 /* Ordinary variable expansion, possibly in braces */
2035 if (ch != '{')
2036 {
2037 /* Simple variable expansion */
2038 for (ptr = input; isalnum (ch) || (ch == '_') || (ch == ':');ch = *++ptr)
2039 /* VOID */ ;
2040 len = ptr - input;
2041 if (len >= sizeof tmp)
2042 goto expand_variable_out;
2043
2044 safe_memcpy (tmp, input, len);
2045 input = ptr - 1;
2046 if ( ( env = get_variable_v2 (tmp, func, info) ) == NULL )
2047 {
2048#ifdef CONFIG_DEVFSD_VERBOSE
2049 msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, tmp);
2050#endif
2051 return (NULL);
2052 }
2053 len = strlen (env);
2054 if (len + *out_pos >= length)
2055 goto expand_variable_out;
2056
2057 memcpy (buffer + *out_pos, env, len + 1);
2058 *out_pos += len;
2059 return (input);
2060 }
2061 /* Variable in braces: check for ':' tricks */
2062 ch = *++input;
2063 for (ptr = input; isalnum (ch) || (ch == '_'); ch = *++ptr)
2064 /* VOID */;
2065 if (ch == '}')
2066 {
2067 /* Must be simple variable expansion with "${var}" */
2068 len = ptr - input;
2069 if (len >= sizeof tmp)
2070 goto expand_variable_out;
2071
2072 safe_memcpy (tmp, input, len);
2073 ptr = expand_variable (buffer, length, out_pos, tmp, func, info );
2074 if (ptr == NULL)
2075 return (NULL);
2076 return (input + len);
2077 }
2078 if (ch != ':' || ptr[1] != '-' )
2079 {
2080#ifdef CONFIG_DEVFSD_VERBOSE
2081 msg_logger( NO_DIE, LOG_INFO,"illegal char in var name\n");
2082#endif
2083 return (NULL);
2084 }
2085 /* It's that handy "${var:-word}" expression. Check if var is defined */
2086 len = ptr - input;
2087 if (len >= sizeof tmp)
2088 goto expand_variable_out;
2089
2090 safe_memcpy (tmp, input, len);
2091 /* Move input pointer to ':' */
2092 input = ptr;
2093 /* First skip to closing brace, taking note of nested expressions */
2094 ptr += 2;
2095 ch = ptr[0];
2096 for (open_braces = 1; open_braces > 0; ch = *++ptr)
2097 {
2098 switch (ch)
2099 {
2100 case '{':
2101 ++open_braces;
2102 break;
2103 case '}':
2104 --open_braces;
2105 break;
2106 case '\0':
2107#ifdef CONFIG_DEVFSD_VERBOSE
2108 msg_logger( NO_DIE, LOG_INFO,"\"}\" not found in: %s\n", input);
2109#endif
2110 return (NULL);
2111 default:
2112 break;
2113 }
2114 }
2115 --ptr;
2116 /* At this point ptr should point to closing brace of "${var:-word}" */
2117 if ( ( env = get_variable_v2 (tmp, func, info) ) != NULL )
2118 {
2119 /* Found environment variable, so skip the input to the closing brace
2120 and return the variable */
2121 input = ptr;
2122 len = strlen (env);
2123 if (len + *out_pos >= length)
2124 goto expand_variable_out;
2125
2126 memcpy (buffer + *out_pos, env, len + 1);
2127 *out_pos += len;
2128 return (input);
2129 }
2130 /* Environment variable was not found, so process word. Advance input
2131 pointer to start of word in "${var:-word}" */
2132 input += 2;
2133 len = ptr - input;
2134 if (len >= sizeof tmp)
2135 goto expand_variable_out;
2136
2137 safe_memcpy (tmp, input, len);
2138 input = ptr;
2139 if ( !st_expr_expand (tmp, STRING_LENGTH, tmp, func, info ) )
2140 return (NULL);
2141 len = strlen (tmp);
2142 if (len + *out_pos >= length)
2143 goto expand_variable_out;
2144
2145 memcpy (buffer + *out_pos, tmp, len + 1);
2146 *out_pos += len;
2147 return (input);
2148expand_variable_out:
2149#ifdef CONFIG_DEVFSD_VERBOSE
2150 msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer);
2151#endif
2152 return (NULL);
2153} /* End Function expand_variable */
2154
2155
2156static const char *get_variable_v2 (const char *variable,
2157 const char *(*func) (const char *variable, void *info),
2158 void *info)
2159/* [SUMMARY] Get a variable from the environment or .
2160 <variable> The variable name.
2161 <func> A function which will be used to get the variable. If this returns
2162 NULL, the environment is searched instead. If this is NULL, only the
2163 environment is searched.
2164 [RETURNS] The value of the variable on success, else NULL.
2165*/
2166{
2167 const char *value;
2168
Eric Andersenf18bd892003-12-19 11:07:59 +00002169#ifdef CONFIG_DEBUG
Glenn L McGrath17d21fa2003-10-09 11:46:23 +00002170 msg_logger( NO_DIE, LOG_INFO, "get_variable_v2()\n");
2171#endif
2172
2173 if (func != NULL)
2174 {
2175 value = (*func) (variable, info);
2176 if (value != NULL)
2177 return (value);
2178 }
2179 return getenv (variable);
2180} /* End Function get_variable */
2181
2182/* END OF CODE */