Code

utils os: Add sdb_realpath().
[sysdb.git] / src / utils / os.c
1 /*
2  * SysDB - src/utils/os.c
3  * Copyright (C) 2014 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 #include "utils/os.h"
33 #include "utils/error.h"
35 #include <errno.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <sys/stat.h>
41 #include <dirent.h>
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <unistd.h>
48 #include <libgen.h>
49 #include <netdb.h>
50 #include <pwd.h>
52 /*
53  * public API
54  */
56 char *
57 sdb_get_homedir(void)
58 {
59         char *username = sdb_get_current_user();
61         struct passwd pw_entry;
62         struct passwd *result = NULL;
64         char buf[4096];
66         int status;
68         if (username) {
69                 memset(&pw_entry, 0, sizeof(pw_entry));
70                 status = getpwnam_r(username, &pw_entry, buf, sizeof(buf), &result);
71         }
72         else
73                 status = -1;
75         if (status || (! result)) {
76                 char errbuf[1024];
77                 sdb_log(SDB_LOG_WARNING, "os: Failed to determine home directory "
78                                 "for user %s: %s", username,
79                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
80                 free(username);
81                 return NULL;
82         }
83         free(username);
84         return strdup(result->pw_dir);
85 } /* sdb_get_homedir */
87 char *
88 sdb_realpath(const char *path)
89 {
90         if (! path)
91                 return NULL;
93         if ((strlen(path) >= 2) && (path[0] == '~') && (path[1] == '/')) {
94                 char *homedir = sdb_get_homedir();
95                 char tmp[(homedir ? strlen(homedir) : 0) + strlen(path)];
96                 char *ret;
98                 if (! homedir)
99                         return NULL;
101                 snprintf(tmp, sizeof(tmp), "%s/%s", homedir, path + 2);
102                 ret = realpath(tmp, NULL);
103                 free(homedir);
104                 return ret;
105         }
107         return realpath(path, NULL);
108 } /* sdb_realpath */
110 int
111 sdb_mkdir_all(const char *pathname, mode_t mode)
113         struct stat st;
114         char *pathname_copy;
115         char *base_dir;
117         int status = 0;
119         if ((! pathname) || (! *pathname)) {
120                 errno = EINVAL;
121                 return -1;
122         }
124         memset(&st, 0, sizeof(st));
125         if (! stat(pathname, &st)) {
126                 if (! S_ISDIR(st.st_mode)) {
127                         errno = ENOTDIR;
128                         return -1;
129                 }
130                 return 0;
131         }
133         if (errno != ENOENT)
134                 /* pathname exists but we cannot access it */
135                 return -1;
137         pathname_copy = strdup(pathname);
138         if (! pathname_copy)
139                 return -1;
140         base_dir = dirname(pathname_copy);
142         status = sdb_mkdir_all(base_dir, mode);
143         if (! status)
144                 status = mkdir(pathname, mode);
146         free(pathname_copy);
147         return status;
148 } /* sdb_mkdir_all */
150 int
151 sdb_remove_all(const char *pathname)
153         struct stat st;
155         if ((! pathname) || (! *pathname)) {
156                 errno = EINVAL;
157                 return -1;
158         }
160         memset(&st, 0, sizeof(st));
161         if (stat(pathname, &st))
162                 return -1;
164         if (S_ISDIR(st.st_mode)) {
165                 DIR *d = opendir(pathname);
167                 if (! d)
168                         return -1;
170                 while (42) {
171                         struct dirent de;
172                         struct dirent *res = NULL;
174                         char filename[strlen(pathname) + sizeof(de.d_name) + 2];
176                         memset(&de, 0, sizeof(de));
177                         if (readdir_r(d, &de, &res)) {
178                                 closedir(d);
179                                 return -1;
180                         }
182                         if (! res)
183                                 break;
185                         if ((de.d_name[0] == '.') && ((de.d_name[1] == '\0')
186                                                 || ((de.d_name[1] == '.')&& (de.d_name[2] == '\0'))))
187                                 continue;
189                         snprintf(filename, sizeof(filename),
190                                         "%s/%s", pathname, de.d_name);
191                         if (sdb_remove_all(filename)) {
192                                 closedir(d);
193                                 return -1;
194                         }
195                 }
196                 closedir(d);
197         }
198         return remove(pathname);
199 } /* sdb_remove_all */
201 char *
202 sdb_get_current_user(void)
204         struct passwd pw_entry;
205         struct passwd *result = NULL;
207         uid_t uid;
209         char buf[4096];
210         int status;
212         uid = geteuid();
213         memset(&pw_entry, 0, sizeof(pw_entry));
214         status = getpwuid_r(uid, &pw_entry, buf, sizeof(buf), &result);
216         if (status || (! result)) {
217                 char errbuf[1024];
218                 sdb_log(SDB_LOG_ERR, "Failed to determine current username: %s",
219                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
220                 return NULL;
221         }
222         return strdup(result->pw_name);
223 } /* sdb_get_current_user */
225 int
226 sdb_select(int fd, int type)
228         fd_set fds;
229         fd_set *readfds = NULL;
230         fd_set *writefds = NULL;
231         fd_set *exceptfds = NULL;
233         if (fd < 0) {
234                 errno = EBADF;
235                 return -1;
236         }
238         FD_ZERO(&fds);
240         switch (type) {
241                 case SDB_SELECTIN:
242                         readfds = &fds;
243                         break;
244                 case SDB_SELECTOUT:
245                         writefds = &fds;
246                         break;
247                 case SDB_SELECTERR:
248                         exceptfds = &fds;
249                         break;
250                 default:
251                         errno = EINVAL;
252                         return -1;
253         }
255         FD_SET(fd, &fds);
257         while (42) {
258                 int n;
259                 errno = 0;
260                 n = select(fd + 1, readfds, writefds, exceptfds, NULL);
262                 if ((n < 0) && (errno != EINTR))
263                         return n;
264                 if (n > 0)
265                         break;
266         }
267         return 0;
268 } /* sdb_select */
270 ssize_t
271 sdb_write(int fd, size_t msg_len, const void *msg)
273         const char *buf;
274         size_t len;
276         if ((fd < 0) || (msg_len && (! msg)))
277                 return -1;
278         if (! msg_len)
279                 return 0;
281         buf = msg;
282         len = msg_len;
283         while (len > 0) {
284                 ssize_t status;
286                 if (sdb_select(fd, SDB_SELECTOUT))
287                         return -1;
289                 errno = 0;
290                 status = write(fd, buf, len);
291                 if (status < 0) {
292                         if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
293                                 continue;
294                         if (errno == EINTR)
295                                 continue;
297                         return status;
298                 }
300                 len -= (size_t)status;
301                 buf += status;
302         }
304         return (ssize_t)msg_len;
305 } /* sdb_write */
307 int
308 sdb_resolve(int network, const char *address, struct addrinfo **res)
310         struct addrinfo ai_hints;
311         const char *host;
312         char *port;
313         int status;
315         if (! res) {
316                 errno = EINVAL;
317                 return EAI_SYSTEM;
318         }
320         if (address) {
321                 host = address;
322                 port = strchr(host, ':');
323                 if (port) {
324                         *port = '\0';
325                         ++port;
326                 }
327                 if (! *host)
328                         host = NULL;
329         }
330         else {
331                 host = NULL;
332                 port = NULL;
333         }
335         memset(&ai_hints, 0, sizeof(ai_hints));
336         ai_hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
337         if (network & SDB_NET_V4)
338                 ai_hints.ai_family = AF_INET;
339         else if (network & SDB_NET_V6)
340                 ai_hints.ai_family = AF_INET6;
341         else
342                 ai_hints.ai_family = AF_UNSPEC;
344         if ((network & SDB_NET_IP) == SDB_NET_IP) {
345                 ai_hints.ai_socktype = 0;
346                 ai_hints.ai_protocol = 0;
347         }
348         else if (network & SDB_NET_TCP) {
349                 ai_hints.ai_socktype = SOCK_STREAM;
350                 ai_hints.ai_protocol = IPPROTO_TCP;
351         }
352         else if (network & SDB_NET_UDP) {
353                 ai_hints.ai_socktype = SOCK_DGRAM;
354                 ai_hints.ai_protocol = IPPROTO_UDP;
355         }
357         status = getaddrinfo(host, port, &ai_hints, res);
358         if (port) {
359                 --port;
360                 *port = ':';
361         }
362         return status;
363 } /* sdb_resolve */
365 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */