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