blob: 9f1dc996600de636815c1899a6a54b4530c6bd67 [file] [log] [blame]
Rob Landleyd3904932013-07-16 00:04:56 -05001/* xwrap.c - wrappers around existing library functions.
2 *
3 * Functions with the x prefix are wrappers that either succeed or kill the
4 * program with an error message, but never return failure. They usually have
5 * the same arguments and return value as the function they wrap.
6 *
7 * Copyright 2006 Rob Landley <rob@landley.net>
8 */
9
10#include "toys.h"
11
12// Strcpy with size checking: exit if there's not enough space for the string.
13void xstrncpy(char *dest, char *src, size_t size)
14{
15 if (strlen(src)+1 > size) error_exit("xstrcpy");
16 strcpy(dest, src);
17}
18
19void xexit(void)
20{
21 if (toys.rebound) longjmp(*toys.rebound, 1);
22 else exit(toys.exitval);
23}
24
25// Die unless we can allocate memory.
26void *xmalloc(size_t size)
27{
28 void *ret = malloc(size);
29 if (!ret) error_exit("xmalloc");
30
31 return ret;
32}
33
34// Die unless we can allocate prezeroed memory.
35void *xzalloc(size_t size)
36{
37 void *ret = xmalloc(size);
38 memset(ret, 0, size);
39 return ret;
40}
41
42// Die unless we can change the size of an existing allocation, possibly
43// moving it. (Notice different arguments from libc function.)
44void *xrealloc(void *ptr, size_t size)
45{
46 ptr = realloc(ptr, size);
47 if (!ptr) error_exit("xrealloc");
48
49 return ptr;
50}
51
52// Die unless we can allocate a copy of this many bytes of string.
53char *xstrndup(char *s, size_t n)
54{
55 char *ret = xmalloc(++n);
56 strncpy(ret, s, n);
57 ret[--n]=0;
58
59 return ret;
60}
61
62// Die unless we can allocate a copy of this string.
63char *xstrdup(char *s)
64{
65 return xstrndup(s, strlen(s));
66}
67
68// Die unless we can allocate enough space to sprintf() into.
69char *xmsprintf(char *format, ...)
70{
71 va_list va, va2;
72 int len;
73 char *ret;
74
75 va_start(va, format);
76 va_copy(va2, va);
77
78 // How long is it?
79 len = vsnprintf(0, 0, format, va);
80 len++;
81 va_end(va);
82
83 // Allocate and do the sprintf()
84 ret = xmalloc(len);
85 vsnprintf(ret, len, format, va2);
86 va_end(va2);
87
88 return ret;
89}
90
91void xprintf(char *format, ...)
92{
93 va_list va;
94 va_start(va, format);
95
96 vprintf(format, va);
97 if (ferror(stdout)) perror_exit("write");
98}
99
100void xputs(char *s)
101{
102 if (EOF == puts(s) || fflush(stdout)) perror_exit("write");
103}
104
105void xputc(char c)
106{
107 if (EOF == fputc(c, stdout) || fflush(stdout)) perror_exit("write");
108}
109
110void xflush(void)
111{
112 if (fflush(stdout)) perror_exit("write");;
113}
114
Rob Landley72756672013-07-17 17:22:46 -0500115// Call xexec with a chunk of optargs, starting at skip. (You can't just
116// call xexec() directly because toy_init() frees optargs.)
117void xexec_optargs(int skip)
118{
119 char **s = toys.optargs;
120
121 toys.optargs = 0;
122 xexec(s+skip);
123}
124
125
Rob Landleyd3904932013-07-16 00:04:56 -0500126// Die unless we can exec argv[] (or run builtin command). Note that anything
127// with a path isn't a builtin, so /bin/sh won't match the builtin sh.
128void xexec(char **argv)
129{
Rob Landleybb504f32013-07-19 02:03:02 -0500130 if (!CFG_TOYBOX_SINGLE) toy_exec(argv);
Rob Landleyd3904932013-07-16 00:04:56 -0500131 execvp(argv[0], argv);
132
133 perror_exit("exec %s", argv[0]);
134}
135
136void xaccess(char *path, int flags)
137{
138 if (access(path, flags)) perror_exit("Can't access '%s'", path);
139}
140
141// Die unless we can delete a file. (File must exist to be deleted.)
142void xunlink(char *path)
143{
144 if (unlink(path)) perror_exit("unlink '%s'", path);
145}
146
147// Die unless we can open/create a file, returning file descriptor.
148int xcreate(char *path, int flags, int mode)
149{
150 int fd = open(path, flags, mode);
151 if (fd == -1) perror_exit("%s", path);
152 return fd;
153}
154
155// Die unless we can open a file, returning file descriptor.
156int xopen(char *path, int flags)
157{
158 return xcreate(path, flags, 0);
159}
160
161void xclose(int fd)
162{
163 if (close(fd)) perror_exit("xclose");
164}
165
166int xdup(int fd)
167{
168 if (fd != -1) {
169 fd = dup(fd);
170 if (fd == -1) perror_exit("xdup");
171 }
172 return fd;
173}
174
Rob Landley1aa75112013-08-07 12:19:51 -0500175FILE *xfdopen(int fd, char *mode)
176{
177 FILE *f = fdopen(fd, mode);
178
179 if (!f) perror_exit("xfdopen");
180
181 return f;
182}
183
Rob Landleyd3904932013-07-16 00:04:56 -0500184// Die unless we can open/create a file, returning FILE *.
185FILE *xfopen(char *path, char *mode)
186{
187 FILE *f = fopen(path, mode);
188 if (!f) perror_exit("No file %s", path);
189 return f;
190}
191
192// Die if there's an error other than EOF.
193size_t xread(int fd, void *buf, size_t len)
194{
195 ssize_t ret = read(fd, buf, len);
196 if (ret < 0) perror_exit("xread");
197
198 return ret;
199}
200
201void xreadall(int fd, void *buf, size_t len)
202{
203 if (len != readall(fd, buf, len)) perror_exit("xreadall");
204}
205
206// There's no xwriteall(), just xwrite(). When we read, there may or may not
207// be more data waiting. When we write, there is data and it had better go
208// somewhere.
209
210void xwrite(int fd, void *buf, size_t len)
211{
212 if (len != writeall(fd, buf, len)) perror_exit("xwrite");
213}
214
215// Die if lseek fails, probably due to being called on a pipe.
216
217off_t xlseek(int fd, off_t offset, int whence)
218{
219 offset = lseek(fd, offset, whence);
220 if (offset<0) perror_exit("lseek");
221
222 return offset;
223}
224
225char *xgetcwd(void)
226{
227 char *buf = getcwd(NULL, 0);
228 if (!buf) perror_exit("xgetcwd");
229
230 return buf;
231}
232
233void xstat(char *path, struct stat *st)
234{
235 if(stat(path, st)) perror_exit("Can't stat %s", path);
236}
237
238// Cannonicalize path, even to file with one or more missing components at end.
239// if exact, require last path component to exist
240char *xabspath(char *path, int exact)
241{
242 struct string_list *todo, *done = 0;
243 int try = 9999, dirfd = open("/", 0);;
244 char buf[4096], *ret;
245
246 // If this isn't an absolute path, start with cwd.
247 if (*path != '/') {
248 char *temp = xgetcwd();
249
250 splitpath(path, splitpath(temp, &todo));
251 free(temp);
252 } else splitpath(path, &todo);
253
254 // Iterate through path components
255 while (todo) {
256 struct string_list *new = llist_pop(&todo), **tail;
257 ssize_t len;
258
259 if (!try--) {
260 errno = ELOOP;
261 goto error;
262 }
263
264 // Removable path componenents.
265 if (!strcmp(new->str, ".") || !strcmp(new->str, "..")) {
266 int x = new->str[1];
267
268 free(new);
269 if (x) {
270 if (done) free(llist_pop(&done));
271 len = 0;
272 } else continue;
273
274 // Is this a symlink?
275 } else len=readlinkat(dirfd, new->str, buf, 4096);
276
277 if (len>4095) goto error;
278 if (len<1) {
279 int fd;
280 char *s = "..";
281
282 // For .. just move dirfd
283 if (len) {
284 // Not a symlink: add to linked list, move dirfd, fail if error
285 if ((exact || todo) && errno != EINVAL) goto error;
286 new->next = done;
287 done = new;
288 if (errno == EINVAL && !todo) break;
289 s = new->str;
290 }
291 fd = openat(dirfd, s, 0);
292 if (fd == -1 && (exact || todo || errno != ENOENT)) goto error;
293 close(dirfd);
294 dirfd = fd;
295 continue;
296 }
297
298 // If this symlink is to an absolute path, discard existing resolved path
299 buf[len] = 0;
300 if (*buf == '/') {
301 llist_traverse(done, free);
302 done=0;
303 close(dirfd);
304 dirfd = open("/", 0);
305 }
306 free(new);
307
308 // prepend components of new path. Note symlink to "/" will leave new NULL
309 tail = splitpath(buf, &new);
310
311 // symlink to "/" will return null and leave tail alone
312 if (new) {
313 *tail = todo;
314 todo = new;
315 }
316 }
317 close(dirfd);
318
319 // At this point done has the path, in reverse order. Reverse list while
320 // calculating buffer length.
321
322 try = 2;
323 while (done) {
324 struct string_list *temp = llist_pop(&done);;
325
326 if (todo) try++;
327 try += strlen(temp->str);
328 temp->next = todo;
329 todo = temp;
330 }
331
332 // Assemble return buffer
333
334 ret = xmalloc(try);
335 *ret = '/';
336 ret [try = 1] = 0;
337 while (todo) {
338 if (try>1) ret[try++] = '/';
339 try = stpcpy(ret+try, todo->str) - ret;
340 free(llist_pop(&todo));
341 }
342
343 return ret;
344
345error:
346 close(dirfd);
347 llist_traverse(todo, free);
348 llist_traverse(done, free);
349
350 return NULL;
351}
352
353// Resolve all symlinks, returning malloc() memory.
354char *xrealpath(char *path)
355{
356 char *new = realpath(path, NULL);
357 if (!new) perror_exit("realpath '%s'", path);
358 return new;
359}
360
361void xchdir(char *path)
362{
363 if (chdir(path)) error_exit("chdir '%s'", path);
364}
365
366// Ensure entire path exists.
367// If mode != -1 set permissions on newly created dirs.
368// Requires that path string be writable (for temporary null terminators).
369void xmkpath(char *path, int mode)
370{
371 char *p, old;
372 mode_t mask;
373 int rc;
374 struct stat st;
375
376 for (p = path; ; p++) {
377 if (!*p || *p == '/') {
378 old = *p;
379 *p = rc = 0;
380 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
381 if (mode != -1) {
382 mask=umask(0);
383 rc = mkdir(path, mode);
384 umask(mask);
385 } else rc = mkdir(path, 0777);
386 }
387 *p = old;
388 if(rc) perror_exit("mkpath '%s'", path);
389 }
390 if (!*p) break;
391 }
392}
393
394// setuid() can fail (for example, too many processes belonging to that user),
395// which opens a security hole if the process continues as the original user.
396
397void xsetuid(uid_t uid)
398{
399 if (setuid(uid)) perror_exit("xsetuid");
400}
401
402// This can return null (meaning file not found). It just won't return null
403// for memory allocation reasons.
404char *xreadlink(char *name)
405{
406 int len, size = 0;
407 char *buf = 0;
408
409 // Grow by 64 byte chunks until it's big enough.
410 for(;;) {
411 size +=64;
412 buf = xrealloc(buf, size);
413 len = readlink(name, buf, size);
414
415 if (len<0) {
416 free(buf);
417 return 0;
418 }
419 if (len<size) {
420 buf[len]=0;
421 return buf;
422 }
423 }
424}
425
426char *xreadfile(char *name)
427{
428 char *buf = readfile(name);
429 if (!buf) perror_exit("xreadfile %s", name);
430 return buf;
431}
432
433int xioctl(int fd, int request, void *data)
434{
435 int rc;
436
437 errno = 0;
438 rc = ioctl(fd, request, data);
439 if (rc == -1 && errno) perror_exit("ioctl %x", request);
440
441 return rc;
442}
443
444// Open a /var/run/NAME.pid file, dying if we can't write it or if it currently
445// exists and is this executable.
446void xpidfile(char *name)
447{
448 char pidfile[256], spid[32];
449 int i, fd;
450 pid_t pid;
451
452 sprintf(pidfile, "/var/run/%s.pid", name);
453 // Try three times to open the sucker.
454 for (i=0; i<3; i++) {
455 fd = open(pidfile, O_CREAT|O_EXCL, 0644);
456 if (fd != -1) break;
457
458 // If it already existed, read it. Loop for race condition.
459 fd = open(pidfile, O_RDONLY);
460 if (fd == -1) continue;
461
462 // Is the old program still there?
463 spid[xread(fd, spid, sizeof(spid)-1)] = 0;
464 close(fd);
465 pid = atoi(spid);
466 if (pid < 1 || kill(pid, 0) == ESRCH) unlink(pidfile);
467
468 // An else with more sanity checking might be nice here.
469 }
470
471 if (i == 3) error_exit("xpidfile %s", name);
472
473 xwrite(fd, spid, sprintf(spid, "%ld\n", (long)getpid()));
474 close(fd);
475}
476
477// Copy the rest of in to out and close both files.
478
479void xsendfile(int in, int out)
480{
481 long len;
482 char buf[4096];
483
484 if (in<0) return;
485 for (;;) {
486 len = xread(in, buf, 4096);
487 if (len<1) break;
488 xwrite(out, buf, len);
489 }
490}
Rob Landley72756672013-07-17 17:22:46 -0500491
492// parse fractional seconds with optional s/m/h/d suffix
493long xparsetime(char *arg, long units, long *fraction)
494{
495 double d;
496 long l;
497
498 if (CFG_TOYBOX_FLOAT) d = strtod(arg, &arg);
499 else l = strtoul(arg, &arg, 10);
500
501 // Parse suffix
502 if (*arg) {
503 int ismhd[]={1,60,3600,86400}, i = stridx("smhd", *arg);
504
505 if (i == -1) error_exit("Unknown suffix '%c'", *arg);
506 if (CFG_TOYBOX_FLOAT) d *= ismhd[i];
507 else l *= ismhd[i];
508 }
509
510 if (CFG_TOYBOX_FLOAT) {
511 l = (long)d;
512 if (fraction) *fraction = units*(d-l);
513 } else if (fraction) *fraction = 0;
514
515 return l;
516}