Close all fds for the daemon for compatibility with usermount(8).
[lufs.git] / lufsd / daemon.c
1 /*
2  * daemon.c
3  * Copyright (C) 2002 Florin Malita <mali@go.ro>
4  *
5  * This file is part of LUFS, a free userspace filesystem implementation.
6  * See http://lufs.sourceforge.net/ for updates.
7  *
8  * LUFS is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * LUFS is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <syslog.h>
26 #include <unistd.h>
27 #include <signal.h>
28 #include <fcntl.h>
29 #include <pwd.h>
30 #include <errno.h>
31 #include <time.h>
32 #include <dirent.h>
33
34 #include <sys/types.h>
35 #include <sys/wait.h>
36 #include <sys/stat.h>
37 #include <sys/socket.h>
38 #include <sys/un.h>
39 #include <sys/ioctl.h>
40 #include <sys/time.h>
41 #include <sys/mount.h>
42
43
44 #include <lufs/proto.h>
45 #include <lufs/fs.h>
46
47 #include "list.h"
48 #include "message.h"
49 #include "filesystem.h"
50 #include "fsctl.h"
51
52 #define CONFIG_FILE1    "/etc/lufsd.conf"
53 #define CONFIG_FILE2    "~/.lufs/lufsd.conf"
54
55 const char *exec_paths[]={
56     "/usr/local/bin/lufsmnt",
57     "/usr/bin/lufsmnt",
58     "/sbin/lufsmnt",
59     NULL
60 };
61
62 int
63 tempsock(char *base, char *name){
64     struct sockaddr_un addr;
65     unsigned long rnd;
66     int sock, res;
67     
68     if((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
69         ERROR("socket error: %s", strerror(errno));
70         return sock;
71     }
72
73     addr.sun_family = AF_UNIX;
74
75     do{
76         rnd = random();
77         sprintf(addr.sun_path, "%s%lu", base, rnd);
78
79         TRACE("trying address %s", addr.sun_path);
80
81         res = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
82     }while((res < 0) && (errno == EADDRINUSE));
83
84     if(res < 0){
85         ERROR("bind error: %s", strerror(errno));
86         return res;
87     }
88     
89     if((res = listen(sock, 10)) < 0){
90         ERROR("listen error: %s", strerror(errno));
91         return res;
92     }
93     
94     if(name)
95         strcpy(name, addr.sun_path);
96
97     return sock;
98 }
99
100 /* From captive/src/libcaptive/sandbox/split.c/captive_sandbox_fd_closeup(): */
101 #define g_return_if_fail(cond) do { if (!(cond)) { ERROR("FAIL: " #cond ); return; } } while (0)
102 #define g_assert(cond) do { if (!(cond)) ERROR("FAIL: " #cond ); } while (0)
103 static void fd_closeup(int fd_first_to_delete,int exception)
104 {
105 DIR *dir;
106 int errint;
107 int dir_fd;
108 struct dirent *dirent;
109
110         dir=opendir("/proc/self/fd/");
111         g_return_if_fail(dir!=NULL);
112         dir_fd=dirfd(dir);
113         g_return_if_fail(dir_fd!=-1);
114
115         while (errno=0,(dirent=readdir(dir))) {
116 long dirent_fd;
117 char *endptr;
118
119                 if (0
120                                 || !strcmp(dirent->d_name,".")
121                                 || !strcmp(dirent->d_name,".."))
122                         continue;
123                 dirent_fd=strtol(dirent->d_name,&endptr,10 /* base */);
124                 g_assert(dirent_fd>=0 && (!endptr || !*endptr));
125                 if (dirent_fd<fd_first_to_delete || dirent_fd==dir_fd || dirent_fd==exception)
126                         continue;
127
128                 errint=close(dirent_fd);
129                 g_assert(errint==0);
130                 errno=0;
131                 errint=close(dirent_fd);
132                 g_assert(errint==-1); g_assert(errno==EBADF);
133                 }
134         g_return_if_fail(errno==0);     /* check for EOF */
135
136         errint=closedir(dir);
137         g_return_if_fail(errint==0);
138         errno=0;
139         close(dir_fd); g_assert(errno==EBADF);  /* just a bit of paranoia; it should be already closed by closedir() */
140 }
141 #undef g_return_if_fail
142 #undef g_assert
143
144 int
145 main(int argc, char **argv){
146     char *service, *mountpoint, *odata;
147     struct list_head cfg;
148     struct fs_ctl *ctl;
149     char tmp[256], *nopts;
150     int ssock, pid, mpid, res;
151
152     INIT_LIST_HEAD(&cfg);
153
154     srandom(time(NULL));
155     setlinebuf(stdout);
156     setlinebuf(stderr);
157
158     /* Close all fds as we will later fork(2) and at least usermount(8) waits
159      * for child finish by select(2) on fd left open for mount(8). We can be
160      * called as external program from mount(8). Close even STD* fds as
161      * usermount(8) uses STDERR for its pipe watching.
162      */
163     /* FIXME: 'quiet' option has no meaning now. */
164     fd_closeup(3,-1);
165
166     if((argc < 5) || (strcmp(argv[3], "-o")) ){
167         ERROR("Usage: %s none <mount-point> -o [options, ...]", argv[0]);
168         exit(1);
169     }
170
171     if(argc > 5){
172         TRACE("more options than expected...");
173     }
174     
175     service = argv[1];
176     mountpoint = argv[2];
177     odata = argv[4];
178     
179
180     nopts = malloc(strlen(odata) + 100);
181     if(!nopts){
182         ERROR("out of memory!");
183         exit(1);
184     }
185
186     strcpy(nopts, odata);
187
188     if(lu_opt_parse(&cfg, "MOUNT", odata) < 0){
189         ERROR("could not parse options!");
190         exit(1);
191     }
192
193     if((lu_opt_loadcfg(&cfg, CONFIG_FILE1) < 0))
194         lu_opt_loadcfg(&cfg, CONFIG_FILE2);
195
196     if(!(ctl = lu_fsctl_create(&cfg))){
197         WARN("could not create fs_ctl!");
198         exit(1);
199     }
200
201     if(!lu_fsctl_mount(ctl)){
202         ERROR("could not mount filesystem!");
203         lu_fsctl_destroy(ctl);
204         exit(1);
205     }
206
207     if((ssock = tempsock("/tmp/lufsd", tmp)) < 0)
208         exit(1);
209
210     TRACE("starting filesystem master at %s", tmp);
211
212     chmod(tmp, S_IRWXU | S_IRWXG | S_IRWXO);
213
214     /* detach & launch FSCtl */
215
216     if((pid = fork()) < 0){
217         ERROR("fork failed!");
218         exit(1);
219     }else
220         if(pid == 0){
221             int fd;
222             const char *quiet;
223
224             quiet = lu_opt_getchar(&cfg, "MOUNT", "quiet");
225             
226             if((fd = open("/dev/tty", O_RDWR, 0)) < 0){
227                 WARN("couldn't open tty, assuming still ok...");
228                 fflush(stdout);
229             }else{
230                 ioctl(fd, TIOCNOTTY, 0);
231                 close(fd);
232                 setsid();
233             }
234
235             free(nopts);
236
237             /* FIXME: 'quiet' option has no meaning now; check fd_closeup(). */
238             if(1 || quiet){
239                 int stdfd;
240
241                 TRACE("going dumb...");
242                 if((stdfd = open("/dev/null", O_RDWR, 0)) < 0){
243                     WARN("couldn't open /dev/null!");
244                 }else{
245                     dup2(stdfd, 0);
246                     dup2(stdfd, 1);
247                     dup2(stdfd, 2);
248                     close(stdfd);
249                 }
250             }
251
252             /* launching FSCtl... */
253             lu_fsctl_run(ctl, ssock, tmp);
254             exit(0);
255         }
256
257     
258     sprintf(nopts, "%s,server_socket=%s,server_pid=%d", nopts, tmp, pid);
259
260     /* execute lufsmnt and wait for it. */
261     
262     if((mpid = fork()) < 0){
263         ERROR("fork failed!");
264         kill(pid, SIGUSR1);
265         exit(1);
266     }else 
267         if(mpid == 0){
268             char *args[4];
269             const char *p;
270
271             args[0] = "lufsmnt";
272             args[1] = mountpoint;
273             args[2] = nopts;
274             args[3] = NULL;
275
276             TRACE("executing %s %s %s", args[0], args[1], args[2]);
277             execvp("lufsmnt", args);
278             WARN("execvp of lufsmnt failed: %s", strerror(errno));
279             WARN("you don't seem to have lufsmnt in your path. trying regular locations...");
280             
281             for(p = exec_paths[0]; p; p++){
282                 TRACE("trying %s %s %s", p, args[1], args[2]);
283                 execv(p, args);         
284             }
285
286             ERROR("could not launch lufsmnt!\n");
287             exit(1);
288         }
289
290     if(waitpid(mpid, &res, 0) < 0){
291         ERROR("waitpid failed!");
292         kill(pid, SIGUSR1);
293         exit(1);
294     }
295
296     if(WIFEXITED(res) && WEXITSTATUS(res) != 0){
297         kill(pid, SIGUSR1);
298         exit(1);
299     }
300
301     TRACE("mount succeded");
302
303     return 0;
304 }
305
306
307
308
309
310
311