经过陆陆续续的更正,现在的automount已经基本适应大多数情况。
当用户插入u盘时,调用mdev - (设置/proc/sys/kernel/hotplug),mdev根据/etc/mdev.conf调用相应的过程。
里面还存在一个防止一个程序多次被启动的方法,open(pathname, O_CREAT|O_EXCL)来实现。
另外注意fork()的子进程会随主进程退出,所以可以放到后台去执行。
相关行如下:
sd[a-z][0-9]* 0:0 0660 */etc/disk_links
disk_links写如下 - 其中check-idong是为了挂载idong disk:
# add for debug
if [ -e "/tmp/automount" ]; then
echo "disk_links: $ACTION $DEVPATH $MDEV" >> /tmp/automount
fi
if [ "$ACTION" = "add" ]; then
if echo $DEVPATH | grep "usb"; then
/opt/bin/automount check-idong &
fi
elif [ "$ACTION" = "remove" ]; then
if echo $DEVPATH | grep "usb"; then
/opt/bin/automount check-idong &
fi
fi
automount.c的实现如下:
#include <stdio.h>
#include <mntent.h>
#include <stdbool.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <sys/vfs.h>
#include <fcntl.h>
#include <stdlib.h>
//#define USE_IN_PC
#ifdef USE_IN_PC
#define MOUNT_POINT "/home/qianjiang/tmp/media"
#else
#define MOUNT_POINT "/tmp/media"
#endif
#define START_DISK_CHAR 'E'
#define END_DISK_CHAR 'Z'
#define MAX_PARTITION_ITEMS (END_DISK_CHAR - START_DISK_CHAR + 1) //from E: to Z:
typedef struct{
char devname[120];
bool bNeedMount;
char disk_char;
}stPartitionItem;
static int partition_num;
static stPartitionItem partitions_list[MAX_PARTITION_ITEMS];
typedef enum{
EVENT_MEDIA_REMOVE = 0,
EVENT_MEDIA_ADD = 1,
EVENT_IDONG_REMOVE = 2,
EVENT_IDONG_ADD = 3,
EVENT_UNKNOWN = 4
}tEvent;
static tEvent iEvent = EVENT_UNKNOWN;
static FILE * fpDbg = NULL;
#define DebugPrint(x) if(fpDbg) fprintf x;
static void get_block_backed_filesystems(void);
#define MAX_FS_TYPES 10
#define FS_TYPES_NAME_SIZE 30
static char _astFsTypes[MAX_FS_TYPES][FS_TYPES_NAME_SIZE];
static int _iFsTypesNum;
static bool CHECK_IDONG_DISK = false;
static int pidof(char * task_name);
static void build_partitions_list(void)
{
FILE *procpt;
char line[100], ptname[100], *s;
int ma, mi, sz;
procpt = fopen("/proc/partitions", "r");
partition_num = 0;
while (fgets(line, sizeof(line), procpt)) {
if (sscanf(line, " %u %u %u %[^\n ]",
&ma, &mi, &sz, ptname) != 4)
continue;
if(ptname[0] != 's' || ptname[1] != 'd') continue; //we only care about sd*
#ifdef USE_IN_PC
if(ptname[2] == 'a') continue;
#endif
#if 0 //qianjiang: do not mount sda or sdb, only mount sda1 or sdb2 etc...
for (s = ptname; *s; s++)
continue;
if (!isdigit(s[-1]))
continue;
#endif
if(partition_num < MAX_PARTITION_ITEMS)
{
strcpy(partitions_list[partition_num].devname, ptname);
partitions_list[partition_num].bNeedMount = true;
partition_num ++;
}
}
fclose(procpt);
}
//check whether dir is matched $MONT_POINT/[E-Z]
static bool reserved_dir(char * dir)
{
char * s;
if(strstr(dir, MOUNT_POINT) != dir) return false;
s = dir + strlen(MOUNT_POINT);
while(*s == '/' || *s == '\\') s ++;
if(*s < START_DISK_CHAR || *s > END_DISK_CHAR) return false;
s ++;
if(*s != '\0' && *s != '/' && *s != '\\') return false;
return true;
}
static char get_disk_char(char * dir)
{
char * s;
s = dir + strlen(MOUNT_POINT);
while(*s == '/' || *s == '\\') s ++;
return *s;
}
static stPartitionItem * find_device(char * device)
{
int i;
for(i = 0; i < partition_num; i ++)
{
if(strcmp(partitions_list[i].devname, device) == 0) return &partitions_list[i];
}
return NULL;
}
static void scan_mount_points(void)
{
struct mntent * mnt;
FILE * mntfd;
struct stat buf;
pid_t child;
int status;
int count;
mntfd= setmntent("/etc/mtab", "r");
if(mntfd == NULL) return;
while(( mnt= getmntent(mntfd)) != NULL)
{
#ifdef USE_IN_PC
if(strstr(mnt->mnt_fsname, "/dev/sd") && mnt->mnt_fsname[7] != 'a') //we only care about sd[^a]
#else
if(strstr(mnt->mnt_fsname, "/dev/sd")) //we only care about sd*
#endif
{
if(stat(mnt->mnt_fsname, &buf)) //not exist, we should umount this point
{
DebugPrint((fpDbg, "%s is not exist, unmount %s\n", mnt->mnt_fsname, mnt->mnt_dir));
count = 0;
while(1) //sometimes, we may umount fail
{
#if 0
if(!(child = fork())){
execlp("umount", "umount", mnt->mnt_dir, NULL);
}
waitpid(child, &status, WUNTRACED);
if(WEXITSTATUS(status) == 0)
{
break;
}
#endif
if(umount(mnt->mnt_dir) == 0)
{
break;
}
count ++;
sleep(1);
if(count >= 3){
umount2(mnt->mnt_dir, MNT_DETACH);
break;
}
}
//check whether we should remove this dir
if(reserved_dir(mnt->mnt_dir))
{
rmdir(mnt->mnt_dir);
iEvent = EVENT_MEDIA_REMOVE;
}
else if(strstr(mnt->mnt_dir, "idong"))
{
iEvent = EVENT_IDONG_REMOVE;
}
}
else
{
//check whether this device is already mount to $MOUNT_POINT
if(reserved_dir(mnt->mnt_dir))
{
stPartitionItem * item;
//set flag in partitions_list[] we don't need do mount again
item = find_device(mnt->mnt_fsname + 5); //strlen("/dev/") is 5
if(item)
{
item->bNeedMount = false;
item->disk_char = get_disk_char(mnt->mnt_dir);
}
}
}
}
}
endmntent(mntfd);
}
static void assign_disk_char(void)
{
int i, j;
stPartitionItem * item;
char disk_char = START_DISK_CHAR;
char path[120];
//we don't mount sda or sdb if sda1 or sdb4 exist
for(i = 0; i < partition_num; i ++)
{
item = &partitions_list[i];
if(item->bNeedMount && strlen(item->devname) == 3)
{
int j;
stPartitionItem * new;
for(j = 0; j < partition_num; j ++)
{
new = &partitions_list[j];
if(strstr(new->devname, item->devname) && strcmp(new->devname, item->devname) > 0)
break;
}
if(j < partition_num){
item->bNeedMount = false;
DebugPrint((fpDbg, "We don't mount the main partition %s\n", item->devname));
}
}
}
for(i = 0; i < partition_num; i ++)
{
item = &partitions_list[i];
if(item->bNeedMount)
{
for(j = 0; j < partition_num; j ++)
{
if(item->bNeedMount == false && disk_char == item->disk_char)
{
disk_char ++;
}
}
//remove this dir before we use this disk_char
sprintf(path, "%s/%c", MOUNT_POINT, disk_char);
rmdir(path);
item->disk_char = disk_char;
}
disk_char ++;
}
}
static void do_mount(stPartitionItem * item)
{
pid_t child;
int status;
char path[120];
char devicename[100];
int i, ret;
bool try_ntfs_3g = true;
sprintf(path, "%s/%c", MOUNT_POINT, item->disk_char);
mkdir( path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH );
sprintf(devicename, "/dev/%s", item->devname);
for(i = 0; i < _iFsTypesNum; i ++)
{
if(strcmp(_astFsTypes[i], "xfs") == 0) continue;
if(strcmp(_astFsTypes[i], "squashfs") == 0) continue;
if(strcmp(_astFsTypes[i], "ntfs") == 0)
{
try_ntfs_3g = false;
ret = mount(devicename, path, _astFsTypes[i], 0, "nls=utf8");
}
else if(strncmp(_astFsTypes[i], "ext", 3) == 0) //ext2, ext3 or ext4
{
ret = mount(devicename, path, _astFsTypes[i], 0, NULL);
}
else //vfat, msdos etc
{
ret = mount(devicename, path, _astFsTypes[i], 0, "iocharset=utf8");
}
if(ret == 0) //successfully
{
iEvent = EVENT_MEDIA_ADD;
//check whether idong disk
if(CHECK_IDONG_DISK && strncmp(_astFsTypes[i], "ext", 3) == 0) //idong only exist on ext2, ext3, ext4
{
struct stat statbuf;
char checking_file[100];
sprintf(checking_file, "%s/iDong-TolTech-Game.bin", path);
if(stat(checking_file, &statbuf) == 0) //it's idong udisk
{
mount(devicename, "/opt/idong", _astFsTypes[i], 0, "iocharset=utf8");
iEvent = EVENT_IDONG_ADD;
}
}
return;
}
}
if(try_ntfs_3g)
{
//try userspace filesystem: ntfs-3g
if(!(child = fork())){
execlp("ntfs-3g", "ntfs-3g", "-o", "force", devicename, path, NULL);
}
waitpid(child, &status, WUNTRACED);
if(WEXITSTATUS(status) == 0) //mount successfully
{
iEvent = EVENT_MEDIA_ADD;
return;
}
}
rmdir(path);
}
static void mount_all(void)
{
int i;
stPartitionItem * item;
for(i = 0; i < partition_num; i ++)
{
item = &partitions_list[i];
if(item->bNeedMount)
{
DebugPrint((fpDbg, "do_mount /dev/%s %s/%c\n", item->devname, MOUNT_POINT, item->disk_char));
do_mount(item);
}
}
}
int main_proc(int argc, char * argv[])
{
char * action, * mdev;
char pathname[100];
int count;
action = getenv("ACTION");
mdev = getenv("MDEV");
if(access("/tmp/automount", W_OK) == 0) fpDbg = fopen("/tmp/automount", "w");
// fpDbg = stdout;
DebugPrint((fpDbg, "Checking USB DISK ...%s %s\n", action, mdev));
if(action != NULL && mdev != NULL)
{
sprintf(pathname, "/dev/%s", mdev);
count = 0;
if(strcmp(action, "add") == 0)
{
if(strlen(mdev) == 3) sleep(1); //sda or sdb, we sleep a while to wait for sub partition exist
while(access(pathname, F_OK) != 0)
{
DebugPrint((fpDbg, "device is not added to /dev yet!!!\n"));
sleep(1);
if(++ count > 2) break;
}
}
else if(strcmp(action, "remove") == 0)
{
while(access(pathname, F_OK) == 0)
{
DebugPrint((fpDbg, "device is not removed from /dev yet\n"));
sleep(1);
if(++ count > 2) break;
}
}
}
mkdir( MOUNT_POINT, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ); //first create root directory if not exit
if(argc > 1 && strcmp(argv[1], "check-idong") == 0)
{
CHECK_IDONG_DISK = true;
}
get_block_backed_filesystems();
build_partitions_list();
scan_mount_points();
assign_disk_char();
mount_all();
if(iEvent != EVENT_UNKNOWN)
{
int pid;
union sigval val;
pid = pidof("SystemMonitor");
if(pid > 0)
{
val.sival_int = iEvent;
sigqueue(pid, SIGRTMIN, val);
DebugPrint((fpDbg, "Send message to SystemMonitor\n"));
}
}
DebugPrint((fpDbg, "Done\n"));
if(fpDbg) fclose(fpDbg);
return -1;
}
int main(int argc, char * argv[])
{
// pid_t child;
// if(!(child = fork())) {
//we need avoid multi-call of automount
int fd;
int count = 0;
while((fd = open("/tmp/automount.lock", O_CREAT | O_EXCL)) < 0){
//file exist, wait for its removal
sleep(1);
if(count ++ > 20) break;
}
if(fd >= 0) close(fd);
main_proc(argc, argv);
unlink("/tmp/automount.lock");
// }
return 0;
}
//============================== Get from busybox mount.c =============================================
//
char* skip_whitespace(const char *s)
{
/* In POSIX/C locale (the only locale we care about: do we REALLY want
* to allow Unicode whitespace in, say, .conf files? nuts!)
* isspace is only these chars: "\t\n\v\f\r" and space.
* "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13.
* Use that.
*/
while (*s == ' ' || (unsigned char)(*s - 9) <= (13 - 9))
s++;
return (char *) s;
}
// Return a list of all block device backed filesystems
static void get_block_backed_filesystems(void)
{
static const char filesystems[2][sizeof("/proc/filesystems")] = {
"/etc/filesystems",
"/proc/filesystems",
};
char *fs, buf[100], *tmp;
int i;
FILE *f;
_iFsTypesNum = 0;
for (i = 0; i < 2; i++) {
f = fopen(filesystems[i], "r");
if (!f) continue;
while ((fgets(buf, 100, f)) != NULL) {
if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
continue;
fs = skip_whitespace(buf);
if (*fs == '#' || *fs == '*' || !*fs)
continue;
//skip newline or space
tmp = fs;
while(*tmp)
{
if(*tmp == ' ' || (unsigned char)(*tmp - 9) <= (13 - 9))
{
*tmp = 0;
break;
}
tmp ++;
}
//keep filesystem
if(*fs)
{
strncpy(_astFsTypes[_iFsTypesNum], fs, FS_TYPES_NAME_SIZE);
_astFsTypes[_iFsTypesNum ++ ][FS_TYPES_NAME_SIZE - 1] = 0;
if(_iFsTypesNum >= MAX_FS_TYPES) break;
}
}
fclose(f);
}
return ;
}
//
/* > 0 means successfully find the pid, else fail*/
static int pidof(char * task_name)
{
DIR * dir;
struct dirent * ent;
int pid;
char filename[100];
struct statfs buf;
int fd;
char cmdline[256];
if(!(dir = opendir("/proc"))) {
return -1;
}
while((ent = readdir(dir))){
pid = atoi(ent->d_name);
sprintf(filename, "/proc/%d", pid);
if((pid > 0) && (statfs(filename, &buf) == 0))
{
strcat(filename, "/cmdline");
fd = open(filename, O_RDONLY);
if(fd < 0) continue;
memset(cmdline, 0, 256);
read(fd, cmdline, 256);
close(fd);
if(strstr(cmdline, task_name))
return pid;
}
}
closedir(dir);
return -1;
}