16 #include <sys/ioctl.h>
19 #include <sys/socket.h>
20 #include <arpa/inet.h>
21 #include <sys/sysinfo.h>
22 #include <sys/types.h>
32 /* Serialize using fcntl() calls
35 int file_lock(char *tag
)
42 sprintf(fn
, "/var/lock/%s.lock", tag
);
43 if ((lockfd
= open(fn
, O_CREAT
| O_RDWR
, 0666)) < 0)
47 if (read(lockfd
, &lockpid
, sizeof(pid_t
))) {
48 // check if we already hold a lock
50 // don't close the file here as that will release all locks
55 memset(&lock
, 0, sizeof(lock
));
56 lock
.l_type
= F_WRLCK
;
59 if (fcntl(lockfd
, F_SETLKW
, &lock
) < 0) {
64 lseek(lockfd
, 0, SEEK_SET
);
65 write(lockfd
, &pid
, sizeof(pid_t
));
68 // No proper error processing
69 syslog(LOG_DEBUG
, "Error %d locking %s, proceeding anyway", errno
, fn
);
73 void file_unlock(int lockfd
)
82 /* Execute a function for each disc partition on the specified controller.
84 * Directory /dev/discs/ looks like this:
85 * disc0 -> ../scsi/host0/bus0/target0/lun0/
86 * disc1 -> ../scsi/host1/bus0/target0/lun0/
87 * disc2 -> ../scsi/host2/bus0/target0/lun0/
88 * disc3 -> ../scsi/host2/bus0/target0/lun1/
90 * Scsi host 2 supports multiple drives.
91 * Scsi host 0 & 1 support one drive.
93 * For attached drives, like this. If not attached, there is no "part#" item.
94 * Here, only one drive, with 2 partitions, is plugged in.
95 * /dev/discs/disc0/disc
96 * /dev/discs/disc0/part1
97 * /dev/discs/disc0/part2
98 * /dev/discs/disc1/disc
99 * /dev/discs/disc2/disc
101 * Which is the same as:
102 * /dev/scsi/host0/bus0/target0/lun0/disc
103 * /dev/scsi/host0/bus0/target0/lun0/part1
104 * /dev/scsi/host0/bus0/target0/lun0/part2
105 * /dev/scsi/host1/bus0/target0/lun0/disc
106 * /dev/scsi/host2/bus0/target0/lun0/disc
107 * /dev/scsi/host2/bus0/target0/lun1/disc
109 * Implementation notes:
110 * Various mucking about with a disc that just got plugged in or unplugged
111 * will make the scsi subsystem try a re-validate, and read the partition table of the disc.
112 * This will make sure the partitions show up.
114 * It appears to try to do the revalidate and re-read & update the partition
115 * information when this code does the "readdir of /dev/discs/disc0/?". If the
116 * disc has any mounted partitions the revalidate will be rejected. So the
117 * current partition info will remain. On an unplug event, when it is doing the
118 * readdir's, it will try to do the revalidate as we are doing the readdir's.
119 * But luckily they'll be rejected, otherwise the later partitions will disappear as
120 * soon as we get the first one.
121 * But be very careful! If something goes not exactly right, the partition entries
122 * will disappear before we've had a chance to unmount from them.
124 * To avoid this automatic revalidation, we go through /proc/partitions looking for the partitions
125 * that /dev/discs point to. That will avoid the implicit revalidate attempt.
127 * If host < 0, do all hosts. If >= 0, it is the host number to do.
131 /* check if the block device has no partition */
132 int is_no_partition(const char *discname
)
135 char line
[128], ptname
[32];
139 if ((procpt
= fopen("/proc/partitions", "r"))) {
140 while (fgets(line
, sizeof(line
), procpt
)) {
141 if (sscanf(line
, " %d %d %d %[^\n ]", &ma
, &mi
, &sz
, ptname
) != 4)
143 if (strstr(ptname
, discname
))
151 int exec_for_host(int host
, int obsolete
, uint flags
, host_exec func
)
154 char bfr
[256]; /* Will be: /dev/discs/disc# */
155 char ptname
[32];/* Will be: discDN_PN */
156 char dsname
[16];/* Will be: discDN */
157 int host_no
; /* SCSI controller/host # */
164 flags
|= EFH_1ST_HOST
;
171 * Scsi block devices in kernel 2.6 (for attached devices) can be found as
172 * /sys/bus/scsi/devices/<host_no>:x:x:x/block:[sda|sdb|...]
174 if ((usb_dev_disc
= opendir("/sys/bus/scsi/devices"))) {
175 sprintf(hostbuf
, "%d:", host
);
177 while ((dp
= readdir(usb_dev_disc
))) {
178 if (host
>= 0 && strncmp(dp
->d_name
, hostbuf
, strlen(hostbuf
)) != 0)
180 if (sscanf(dp
->d_name
, "%d:%*s:%*s:%*s", &host_no
) != 1)
182 sprintf(bfr
, "/sys/bus/scsi/devices/%s", dp
->d_name
);
183 if ((dir_host
= opendir(bfr
))) {
184 while ((dp
= readdir(dir_host
))) {
185 if (strncmp(dp
->d_name
, "block:", 6) != 0)
187 strncpy(dsname
, dp
->d_name
+ 6, sizeof(dsname
));
188 siz
= strlen(dsname
);
190 flags
|= EFH_1ST_DISC
;
191 if (func
&& (prt_fp
= fopen("/proc/partitions", "r"))) {
192 while (fgets(line
, sizeof(line
) - 2, prt_fp
)) {
193 if (sscanf(line
, " %*s %*s %*s %s", ptname
) == 1) {
194 if (strncmp(ptname
, dsname
, siz
) == 0) {
195 if ((strcmp(ptname
, dsname
) == 0) && !is_no_partition(dsname
))
197 sprintf(line
, "/dev/%s", ptname
);
198 result
= (*func
)(line
, host_no
, dsname
, ptname
, flags
) || result
;
199 flags
&= ~(EFH_1ST_HOST
| EFH_1ST_DISC
);
209 closedir(usb_dev_disc
);
213 char link
[256]; /* Will be: ../scsi/host#/bus0/target0/lun# that bfr links to. */
214 /* When calling the func, will be: /dev/discs/disc#/part# */
215 char bfr2
[128]; /* Will be: /dev/discs/disc#/disc for the BLKRRPART. */
218 int disc_num
; /* Disc # */
219 int part_num
; /* Parition # */
220 char *mp
; /* Ptr to after any leading ../ path */
222 if ((usb_dev_disc
= opendir(DEV_DISCS_ROOT
))) {
223 while ((dp
= readdir(usb_dev_disc
))) {
224 sprintf(bfr
, "%s/%s", DEV_DISCS_ROOT
, dp
->d_name
);
225 if (strncmp(dp
->d_name
, "disc", 4) != 0)
228 disc_num
= atoi(dp
->d_name
+ 4);
229 len
= readlink(bfr
, link
, sizeof(link
) - 1);
234 cp
= strstr(link
, "/scsi/host");
238 host_no
= atoi(cp
+ 10);
239 if (host
>= 0 && host_no
!= host
)
242 /* We have found a disc that is on this controller.
243 * Loop thru all the partitions on this disc.
244 * The new way, reading thru /proc/partitions.
247 if ((cp
= strstr(link
, "../")) != NULL
)
251 flags
|= EFH_1ST_DISC
;
252 if (func
&& (prt_fp
= fopen("/proc/partitions", "r"))) {
253 while (fgets(line
, sizeof(line
) - 2, prt_fp
)) {
254 if (sscanf(line
, " %*s %*s %*s %s", bfr2
) == 1 &&
255 strncmp(bfr2
, mp
, siz
) == 0)
257 if ((cp
= strstr(bfr2
, "/part"))) {
258 part_num
= atoi(cp
+ 5);
259 sprintf(line
, "%s/part%d", bfr
, part_num
);
260 sprintf(dsname
, "disc%d", disc_num
);
261 sprintf(ptname
, "disc%d_%d", disc_num
, part_num
);
263 else if ((cp
= strstr(bfr2
, "/disc"))) {
265 if (!is_no_partition(bfr2
))
267 sprintf(line
, "%s/disc", bfr
);
268 sprintf(dsname
, "disc%d", disc_num
);
269 strcpy(ptname
, dsname
);
274 result
= (*func
)(line
, host_no
, dsname
, ptname
, flags
) || result
;
275 flags
&= ~(EFH_1ST_HOST
| EFH_1ST_DISC
);
281 closedir(usb_dev_disc
);
289 /* Concept taken from the e2fsprogs/ismounted.c.
290 * Find wherever 'file' (actually: device) is mounted.
291 * Either the exact same device-name, or another device-name.
292 * The latter is detected by comparing the rdev or dev&inode.
293 * So aliasing won't fool us---we'll still find if it's mounted.
294 * Return its mnt entry.
295 * In particular, the caller would look at the mnt->mountpoint.
297 * Find the matching devname(s) in mounts or swaps.
298 * If func is supplied, call it for each match. If not, return mnt on the first match.
301 static inline int is_same_device(char *fsname
, dev_t file_rdev
, dev_t file_dev
, ino_t file_ino
)
305 if (stat(fsname
, &st_buf
) == 0) {
306 if (S_ISBLK(st_buf
.st_mode
)) {
307 if (file_rdev
&& (file_rdev
== st_buf
.st_rdev
))
311 if (file_dev
&& ((file_dev
== st_buf
.st_dev
) &&
312 (file_ino
== st_buf
.st_ino
)))
314 /* Check for [swap]file being on the device. */
315 if (file_dev
== 0 && file_ino
== 0 && file_rdev
== st_buf
.st_dev
)
323 struct mntent
*findmntents(char *file
, int swp
, int (*func
)(struct mntent
*mnt
, uint flags
), uint flags
)
327 dev_t file_dev
=0, file_rdev
=0;
331 if ((f
= setmntent(swp
? "/proc/swaps": "/proc/mounts", "r")) == NULL
)
334 if (stat(file
, &st_buf
) == 0) {
335 if (S_ISBLK(st_buf
.st_mode
)) {
336 file_rdev
= st_buf
.st_rdev
;
339 file_dev
= st_buf
.st_dev
;
340 file_ino
= st_buf
.st_ino
;
343 while ((mnt
= getmntent(f
)) != NULL
) {
344 /* Always ignore rootfs mount */
345 if (strcmp(mnt
->mnt_fsname
, "rootfs") == 0)
348 if (strcmp(file
, mnt
->mnt_fsname
) == 0 ||
349 strcmp(file
, mnt
->mnt_dir
) == 0 ||
350 is_same_device(mnt
->mnt_fsname
, file_rdev
, file_dev
, file_ino
)) {
362 //#define SAME_AS_KERNEL
363 /* Simulate a hotplug event, as if a USB storage device
364 * got plugged or unplugged.
365 * Either use a hardcoded program name, or the same
366 * hotplug program that the kernel uses for a real event.
368 void add_remove_usbhost(char *host
, int add
)
370 setenv("ACTION", add
? "add" : "remove", 1);
371 setenv("SCSI_HOST", host
, 1);
372 setenv("PRODUCT", host
, 1);
373 setenv("INTERFACE", "TOMATO/0", 1);
374 #ifdef SAME_AS_KERNEL
375 char pgm
[256] = "/sbin/hotplug usb";
377 int fd
= open("/proc/sys/kernel/hotplug", O_RDONLY
);
379 if (read(fd
, pgm
, sizeof(pgm
) - 5) >= 0) {
380 if ((p
= strchr(pgm
, '\n')) != NULL
)
388 // don't use value from /proc/sys/kernel/hotplug
389 // since it may be overriden by a user.
390 system("/sbin/hotplug usb");
392 unsetenv("INTERFACE");
394 unsetenv("SCSI_HOST");
399 /****************************************************/
400 /* Use busybox routines to get labels for fat & ext */
401 /* Probe for label the same way that mount does. */
402 /****************************************************/
404 #define VOLUME_ID_LABEL_SIZE 64
405 #define VOLUME_ID_UUID_SIZE 36
406 #define SB_BUFFER_SIZE 0x11000
415 uint64_t seekbuf_off
;
416 char label
[VOLUME_ID_LABEL_SIZE
+1];
417 char uuid
[VOLUME_ID_UUID_SIZE
+1];
420 extern void volume_id_set_uuid();
421 extern void *volume_id_get_buffer();
422 extern void volume_id_free_buffer();
423 extern int volume_id_probe_ext();
424 extern int volume_id_probe_vfat();
425 extern int volume_id_probe_ntfs();
426 extern int volume_id_probe_linux_swap();
428 /* Put the label in *label and uuid in *uuid.
429 * Return fstype if determined.
431 char *find_label_or_uuid(char *dev_name
, char *label
, char *uuid
)
436 memset(&id
, 0x00, sizeof(id
));
437 if (label
) *label
= 0;
439 if ((id
.fd
= open(dev_name
, O_RDONLY
)) < 0)
442 volume_id_get_buffer(&id
, 0, SB_BUFFER_SIZE
);
444 if (!id
.error
&& volume_id_probe_linux_swap(&id
) == 0)
446 else if (!id
.error
&& volume_id_probe_vfat(&id
) == 0)
448 else if (!id
.error
&& volume_id_probe_ext(&id
) == 0)
449 fstype
= ((id
.sbbuf
[0x460] & 0x0008 /* JOURNAL_DEV */) != 0 ||
450 (id
.sbbuf
[0x45c] & 0x0004 /* HAS_JOURNAL */) != 0) ? "ext3" : "ext2";
451 else if (!id
.error
&& volume_id_probe_ntfs(&id
) == 0)
456 volume_id_free_buffer(&id
);
457 if (label
&& (*id
.label
!= 0))
458 strcpy(label
, id
.label
);
459 if (uuid
&& (*id
.uuid
!= 0))
460 strcpy(uuid
, id
.uuid
);
465 void *xmalloc(size_t siz
)
467 return (malloc(siz
));
470 void *xrealloc(void *old
, size_t size
)
472 return realloc(old
, size
);
475 ssize_t
full_read(int fd
, void *buf
, size_t len
)
477 return read(fd
, buf
, len
);