dss.c: Add some more documentation.
[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 ret = -ERRNO_TO_DSS_ERROR(errno);
40 make_err_msg("opendir(\".\") failed");
41 return ret;
42 }
43 while ((entry = readdir(dir))) {
44 mode_t m;
45 struct stat s;
46
47 if (!strcmp(entry->d_name, "."))
48 continue;
49 if (!strcmp(entry->d_name, ".."))
50 continue;
51 ret = lstat(entry->d_name, &s) == -1;
52 if (ret == -1) {
53 ret = -ERRNO_TO_DSS_ERROR(errno);
54 make_err_msg("lstat(\"%s\") failed", entry->d_name);
55 goto out;
56 }
57 m = s.st_mode;
58 if (!S_ISDIR(m))
59 continue;
60 ret = func(entry->d_name, private_data);
61 if (ret < 0)
62 goto out;
63 }
64 ret = 1;
65 out:
66 closedir(dir);
67 return ret;
68 }
69 /**
70 * Wrapper for chdir(2).
71 *
72 * \param path The specified directory.
73 *
74 * \return Standard.
75 */
76 int dss_chdir(const char *path)
77 {
78 int ret = chdir(path);
79
80 if (ret >= 0)
81 return 1;
82 ret = -ERRNO_TO_DSS_ERROR(errno);
83 make_err_msg("chdir to %s failed", path);
84 return ret;
85 }
86
87 /**
88 * Set a file descriptor to non-blocking mode.
89 *
90 * \param fd The file descriptor.
91 *
92 * \return Standard.
93 */
94 __must_check int mark_fd_nonblocking(int fd)
95 {
96 int flags = fcntl(fd, F_GETFL);
97 if (flags < 0)
98 return -ERRNO_TO_DSS_ERROR(errno);
99 flags = fcntl(fd, F_SETFL, ((long)flags) | O_NONBLOCK);
100 if (flags < 0)
101 return -ERRNO_TO_DSS_ERROR(errno);
102 return 1;
103 }
104
105 /**
106 * dss' wrapper for select(2).
107 *
108 * It calls select(2) (with no exceptfds) and starts over if select() was
109 * interrupted by a signal.
110 *
111 * \param n The highest-numbered descriptor in any of the two sets, plus 1.
112 * \param readfds fds that should be checked for readability.
113 * \param writefds fds that should be checked for writablility.
114 * \param timeout_tv upper bound on the amount of time elapsed before select()
115 * returns.
116 *
117 * \return The return value of the underlying select() call on success, the
118 * negative system error code on errors.
119 *
120 * All arguments are passed verbatim to select(2).
121 * \sa select(2) select_tut(2).
122 */
123 int dss_select(int n, fd_set *readfds, fd_set *writefds,
124 struct timeval *timeout_tv)
125 {
126 int ret, err;
127 do {
128 ret = select(n, readfds, writefds, NULL, timeout_tv);
129 err = errno;
130 } while (ret < 0 && err == EINTR);
131 if (ret < 0)
132 return -ERRNO_TO_DSS_ERROR(errno);
133 return ret;
134 }