add exfat support
[oweals/mountd.git] / autofs.c
1 #include <sys/types.h>
2 #include <linux/types.h>
3 #include <paths.h>
4 #include <limits.h>
5 #include <time.h>
6
7 #include <stdio.h>
8 #include <signal.h>
9 #include <stdlib.h>
10 #include <fcntl.h>
11
12 #include <errno.h>
13
14 #include <string.h>
15 #include <syslog.h>
16 #include <unistd.h>
17 #include <sys/ioctl.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
20 #include <sys/stat.h>
21 #include <sys/time.h>
22 #include <poll.h>
23 #include <linux/auto_fs4.h>
24
25 #include "include/log.h"
26 #include "include/sys.h"
27 #include "include/timer.h"
28 #include "include/mount.h"
29 #include "include/signal.h"
30 #include "include/ucix.h"
31 #include "include/autofs.h"
32
33 int fdin = 0; /* data coming out of the kernel */
34 int fdout = 0;/* data going into the kernel */
35 dev_t dev;
36
37 time_t uci_timeout;
38 char uci_path[32];
39
40 static void umount_autofs(void)
41 {
42         system_printf("umount %s 2> /dev/null", "/tmp/run/mountd/");
43 }
44
45 static int mount_autofs(void)
46 {
47         int pipefd[2];
48         struct stat st;
49         log_printf("trying to mount %s as the autofs root\n", "/tmp/run/mountd/");
50         if(is_mounted(0, "/tmp/run/mountd/"))
51         {
52                 log_printf("%s is already mounted\n", "/tmp/run/mountd/");
53                 return -1;
54         }
55         fdout = fdin = -1;
56         mkdir("/tmp/run/mountd/", 0555);
57         if(pipe(pipefd) < 0)
58         {
59                 log_printf("failed to get kernel pipe\n");
60                 return -1;
61         }
62         if(system_printf("/bin/mount -t autofs -o fd=%d,pgrp=%u,minproto=5,maxproto=5 \"mountd(pid%u)\" %s",
63                 pipefd[1], (unsigned) getpgrp(), getpid(), "/tmp/run/mountd/") != 0)
64         {
65                 log_printf("unable to mount autofs on %s\n", "/tmp/run/mountd/");
66                 close(pipefd[0]);
67                 close(pipefd[1]);
68                 return -1;
69         }
70
71         close(pipefd[1]);
72         fdout = pipefd[0];
73
74         fdin = open("/tmp/run/mountd/", O_RDONLY);
75         if(fdin < 0)
76         {
77                 umount_autofs();
78                 return -1;
79         }
80         stat("/tmp/run/mountd/", &st);
81         return 0;
82 }
83
84 static void send_ready(unsigned int wait_queue_token)
85 {
86         if(ioctl(fdin, AUTOFS_IOC_READY, wait_queue_token) < 0)
87                 log_printf("failed to report ready to kernel\n");
88 }
89
90 static void send_fail(unsigned int wait_queue_token)
91 {
92         if(ioctl(fdin, AUTOFS_IOC_FAIL, wait_queue_token) < 0)
93                 log_printf("failed to report fail to kernel\n");
94 }
95
96 static int autofs_process_request(const struct autofs_v5_packet *pkt)
97 {
98         struct stat st;
99         log_printf("kernel is requesting a mount -> %s\n", pkt->name);
100         chdir("/tmp/run/mountd/");
101         if (lstat(pkt->name, &st) == -1 || (S_ISDIR(st.st_mode) && st.st_dev == dev)) {
102                 if(!mount_new("/tmp/run/mountd/", (char*)pkt->name))
103                 {
104                         send_ready(pkt->wait_queue_token);
105                 } else {
106                         send_fail(pkt->wait_queue_token);
107                         log_printf("failed to mount %s\n", pkt->name);
108                 }
109         } else {
110                 send_ready(pkt->wait_queue_token);
111         }
112         chdir("/");
113
114         return 0;
115 }
116
117 static void expire_proc(void)
118 {
119         struct autofs_packet_expire pkt;
120         while(ioctl(fdin, AUTOFS_IOC_EXPIRE, &pkt) == 0)
121                 mount_remove("/tmp/run/mountd/", pkt.name);
122 }
123
124 static int fullread(void *ptr, size_t len)
125 {
126         char *buf = (char *) ptr;
127         while(len > 0)
128         {
129                 ssize_t r = read(fdout, buf, len);
130                 if(r == -1)
131                 {
132                         if (errno == EINTR)
133                                 continue;
134                         break;
135                 }
136                 buf += r;
137                 len -= r;
138         }
139         return len;
140 }
141
142 static int autofs_in(union autofs_v5_packet_union *pkt)
143 {
144         int res;
145         struct pollfd fds[1];
146
147         fds[0].fd = fdout;
148         fds[0].events = POLLIN;
149
150         while(1)
151         {
152                 res = poll(fds, 1, -1);
153
154                 if (res == -1)
155                 {
156                         if (errno == EINTR)
157                                 continue;
158                         log_printf("failed while trying to read packet from kernel\n");
159                         return -1;
160                 }
161                 else if ((res > 0) && (fds[0].revents & POLLIN))
162                 {
163                         return fullread(pkt, sizeof(*pkt));
164                 }
165         }
166 }
167
168 pid_t autofs_safe_fork(void)
169 {
170         pid_t pid = fork();
171         if(!pid)
172         {
173                 close(fdin);
174             close(fdout);
175         }
176         return pid;
177 }
178
179 static void autofs_cleanup_handler(void)
180 {
181         close(fdin);
182         close(fdout);
183         umount_autofs();
184 }
185
186 static void autofs_init(void)
187 {
188         int kproto_version;
189         char *p;
190         struct uci_context *ctx;
191         signal_init(autofs_cleanup_handler);
192         ctx = ucix_init("mountd");
193         uci_timeout = ucix_get_option_int(ctx, "mountd", "mountd", "timeout", 60);
194         p = ucix_get_option(ctx, "mountd", "mountd", "path");
195         ucix_cleanup(ctx);
196         if(p)
197                 snprintf(uci_path, 31, p);
198         else
199                 snprintf(uci_path, 31, "/tmp/mounts/");
200         uci_path[31] = '\0';
201         mkdir("/tmp/run/", 0555);
202         mkdir("/tmp/mounts", 0555);
203         system_printf("rm -rf %s*", uci_path);
204         if(uci_timeout < 16)
205                 uci_timeout = 16;
206         umount_autofs();
207         mount_init();
208         if(mount_autofs() < 0)
209         {
210                 closelog();
211                 exit(1);
212         }
213         ioctl(fdin, AUTOFS_IOC_PROTOVER, &kproto_version);
214         if(kproto_version != 5)
215         {
216                 log_printf("only kernel protocol version 5 is tested. You have %d.\n",
217                         kproto_version);
218                 closelog();
219                 exit(1);
220         }
221         ioctl(fdin, AUTOFS_IOC_SETTIMEOUT, &uci_timeout);
222         timer_add(expire_proc, 15);
223 }
224
225 int autofs_loop(void)
226 {
227         chdir("/");
228         autofs_init();
229         while(1)
230         {
231                 union autofs_v5_packet_union pkt;
232                 if(autofs_in(&pkt))
233                 {
234                         continue;
235                 }
236                 log_printf("Got a autofs packet\n");
237                 if(pkt.hdr.type == autofs_ptype_missing_indirect)
238                         autofs_process_request(&pkt.missing_indirect);
239                 else
240                         log_printf("unknown packet type %d\n", pkt.hdr.type);
241                 poll(0, 0, 200);
242         }
243         umount_autofs();
244         log_printf("... quitting\n");
245         closelog();
246         return 0;
247 }