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