Fix a serious bug in deamon mode.
[dss.git] / fd.c
1 #include <unistd.h>
2 #include <assert.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <sys/types.h>
6 #include <dirent.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9
10
11 #include "gcc-compat.h"
12 #include "error.h"
13 #include "string.h"
14
15 /**
16  * Call a function for each subdirectory of the current working directory.
17  *
18  * \param dirname The directory to traverse.
19  * \param func The function to call for each subdirecrtory.
20  * \param private_data Pointer to an arbitrary data structure.
21  *
22  * For each top-level directory under \a dirname, the supplied function \a func is
23  * called.  The full path of the subdirectory and the \a private_data pointer
24  * are passed to \a func.
25  *
26  * \return This function returns immediately if \a func returned a negative
27  * value. In this case \a func must set error_txt and this negative value is
28  * returned to the caller. Otherwise the function returns when all
29  * subdirectories have been passed to \a func.
30  */
31
32 int for_each_subdir(int (*func)(const char *, void *), void *private_data)
33 {
34         struct dirent *entry;
35         int ret;
36         DIR *dir = opendir(".");
37
38         if (!dir)
39                 return -ERRNO_TO_DSS_ERROR(errno);
40         while ((entry = readdir(dir))) {
41                 mode_t m;
42                 struct stat s;
43
44                 if (!strcmp(entry->d_name, "."))
45                         continue;
46                 if (!strcmp(entry->d_name, ".."))
47                         continue;
48                 ret = lstat(entry->d_name, &s) == -1;
49                 if (ret == -1) {
50                         ret = -ERRNO_TO_DSS_ERROR(errno);
51                         goto out;
52                 }
53                 m = s.st_mode;
54                 if (!S_ISDIR(m))
55                         continue;
56                 ret = func(entry->d_name, private_data);
57                 if (ret < 0)
58                         goto out;
59         }
60         ret = 1;
61 out:
62         closedir(dir);
63         return ret;
64 }
65 /**
66  * Wrapper for chdir(2).
67  *
68  * \param path The specified directory.
69  *
70  * \return Standard.
71  */
72 int dss_chdir(const char *path)
73 {
74         if (chdir(path) >= 0)
75                 return 1;
76         return -ERRNO_TO_DSS_ERROR(errno);
77 }
78
79 /**
80  * Set a file descriptor to non-blocking mode.
81  *
82  * \param fd The file descriptor.
83  *
84  * \return Standard.
85  */
86 __must_check int mark_fd_nonblocking(int fd)
87 {
88         int flags = fcntl(fd, F_GETFL);
89         if (flags < 0)
90                 return -ERRNO_TO_DSS_ERROR(errno);
91         flags = fcntl(fd, F_SETFL, ((long)flags) | O_NONBLOCK);
92         if (flags < 0)
93                 return -ERRNO_TO_DSS_ERROR(errno);
94         return 1;
95 }
96
97 /**
98  * dss' wrapper for select(2).
99  *
100  * It calls select(2) (with no exceptfds) and starts over if select() was
101  * interrupted by a signal.
102  *
103  * \param n The highest-numbered descriptor in any of the two sets, plus 1.
104  * \param readfds fds that should be checked for readability.
105  * \param writefds fds that should be checked for writablility.
106  * \param timeout_tv upper bound on the amount of time elapsed before select()
107  * returns.
108  *
109  * \return The return value of the underlying select() call on success, the
110  * negative system error code on errors.
111  *
112  * All arguments are passed verbatim to select(2).
113  * \sa select(2) select_tut(2).
114  */
115 int dss_select(int n, fd_set *readfds, fd_set *writefds,
116                 struct timeval *timeout_tv)
117 {
118         int ret, err;
119         do {
120                 ret = select(n, readfds, writefds, NULL, timeout_tv);
121                 err = errno;
122         } while (ret < 0 && err == EINTR);
123         if (ret < 0)
124                 return -ERRNO_TO_DSS_ERROR(errno);
125         return ret;
126 }