/[vps]/baselayout-vserver/trunk/src/core/misc.c
Gentoo

Diff of /baselayout-vserver/trunk/src/core/misc.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 126 Revision 127
1/*
2 * misc.c
3 *
4 * Miscellaneous macro's and functions.
5 *
6 * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation version 2 of the License.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 * $Header$
23 */
24
25#include <errno.h>
26#include <string.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <sys/mman.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <sys/param.h>
33#include <dirent.h>
34#include <unistd.h>
35#include <fcntl.h>
36
37#include "debug.h"
38#include "misc.h"
39
40char *memrepchr(char **str, char old, char new, size_t size)
41{
42 char *str_p;
43
44 if ((NULL == str)
45 || (NULL == *str)
46 || (0 == strlen(*str))) {
47 DBG_MSG("Invalid argument passed!\n");
48 errno = EINVAL;
49 return NULL;
50 }
51
52 str_p = memchr(*str, old, size);
53
54 while (NULL != str_p) {
55 str_p[0] = new;
56 str_p = memchr(&str_p[1], old, size - (str_p - *str) - 1);
57 }
58
59 return *str;
60}
61
62char *strcatpaths(const char *pathname1, const char *pathname2)
63{
64 char *new_path = NULL;
65 int lenght;
66
67 if ((NULL == pathname1)
68 || (0 == strlen(pathname1))
69 || (NULL == pathname2)
70 || (0 == strlen(pathname2))) {
71 DBG_MSG("Invalid argument passed!\n");
72 errno = EINVAL;
73 return NULL;
74 }
75
76 /* Lenght of pathname1 + lenght of pathname2 + '/' if needed */
77 lenght = strlen(pathname1) + strlen(pathname2) + 1;
78 /* lenght + '\0' */
79 new_path = malloc(lenght + 1);
80 if (NULL == new_path) {
81 DBG_MSG("Failed to allocate buffer!\n");
82 return NULL;
83 }
84
85 strncpy(new_path, pathname1, lenght);
86 /* Should we add a '/' ? */
87 if (new_path[strlen(new_path)-1] != '/')
88 strncat(new_path, "/", lenght - strlen(new_path));
89 strncat(new_path, pathname2, lenght - strlen(new_path));
90
91 return new_path;
92}
93
94char *strndup(const char *str, size_t size)
95{
96 char *new_str = NULL;
97 size_t len;
98
99 if (NULL == str) {
100 DBG_MSG("Invalid argument passed!\n");
101 errno = EINVAL;
102 return NULL;
103 }
104
105 /* Check lenght of str without breaching the size limit */
106 for (len = 0; (len < size) && ('\0' != str[len]); len++);
107
108 new_str = malloc(len + 1);
109 if (NULL == new_str) {
110 DBG_MSG("Failed to allocate buffer!\n");
111 return NULL;
112 }
113
114 /* Make sure our string is NULL terminated */
115 new_str[len] = '\0';
116
117 return (char *)memcpy(new_str, str, len);
118}
119
120char *gbasename(const char *path)
121{
122 char *new_path = NULL;
123
124 if ((NULL == path) || (0 == strlen(path))) {
125 DBG_MSG("Invalid argument passed!\n");
126 errno = EINVAL;
127 return NULL;
128 }
129
130 /* Copied from glibc */
131 new_path = strrchr (path, '/');
132 return new_path ? new_path + 1 : (char *)path;
133}
134
135
136int exists(const char *pathname)
137{
138 struct stat buf;
139 int retval;
140
141 if ((NULL == pathname) || (0 == strlen(pathname))) {
142 DBG_MSG("Invalid argument passed!\n");
143 return 0;
144 }
145
146 retval = lstat(pathname, &buf);
147 if (-1 != retval)
148 return 1;
149
150 /* Clear errno, as we do not want debugging to trigger */
151 errno = 0;
152
153 return 0;
154}
155
156int is_file(const char *pathname, int follow_link)
157{
158 struct stat buf;
159 int retval;
160
161 if ((NULL == pathname) || (0 == strlen(pathname))) {
162 DBG_MSG("Invalid argument passed!\n");
163 return 0;
164 }
165
166 retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf);
167 if ((-1 != retval) && (S_ISREG(buf.st_mode)))
168 return 1;
169
170 /* Clear errno, as we do not want debugging to trigger */
171 errno = 0;
172
173 return 0;
174}
175
176int is_link(const char *pathname)
177{
178 struct stat buf;
179 int retval;
180
181 if ((NULL == pathname) || (0 == strlen(pathname))) {
182 DBG_MSG("Invalid argument passed!\n");
183 return 0;
184 }
185
186 retval = lstat(pathname, &buf);
187 if ((-1 != retval) && (S_ISLNK(buf.st_mode)))
188 return 1;
189
190 /* Clear errno, as we do not want debugging to trigger */
191 errno = 0;
192
193 return 0;
194}
195
196int is_dir(const char *pathname, int follow_link)
197{
198 struct stat buf;
199 int retval;
200
201 if ((NULL == pathname) || (0 == strlen(pathname))) {
202 DBG_MSG("Invalid argument passed!\n");
203 return 0;
204 }
205
206 retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf);
207 if ((-1 != retval) && (S_ISDIR(buf.st_mode)))
208 return 1;
209
210 /* Clear errno, as we do not want debugging to trigger */
211 errno = 0;
212
213 return 0;
214}
215
216time_t get_mtime(const char *pathname, int follow_link)
217{
218 struct stat buf;
219 int retval;
220
221 if ((NULL == pathname) || (0 == strlen(pathname))) {
222 DBG_MSG("Invalid argument passed!\n");
223 return 0;
224 }
225
226 retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf);
227 if (-1 != retval)
228 return buf.st_mtime;
229
230 /* Clear errno, as we do not want debugging to trigger */
231 errno = 0;
232
233 return 0;
234}
235
236#ifdef __KLIBC__
237int remove(const char *pathname)
238{
239 int retval;
240
241 if ((NULL == pathname) || (0 == strlen(pathname))) {
242 DBG_MSG("Invalid argument passed!\n");
243 errno = EINVAL;
244 return -1;
245 }
246
247 if (1 == is_dir(pathname, 0))
248 retval = rmdir(pathname);
249 else
250 retval = unlink(pathname);
251
252 return retval;
253}
254#endif
255
256int mktree(const char *pathname, mode_t mode)
257{
258 char *temp_name = NULL;
259 char *temp_token = NULL;
260 char *token_p;
261 char *token;
262 int retval;
263 int lenght;
264
265 if ((NULL == pathname) || (0 == strlen(pathname))) {
266 DBG_MSG("Invalid argument passed!\n");
267 errno = EINVAL;
268 return -1;
269 }
270
271 /* Lenght of 'pathname' + extra for "./" if needed */
272 lenght = strlen(pathname) + 2;
273 /* lenght + '\0' */
274 temp_name = malloc(lenght + 1);
275 if (NULL == temp_name) {
276 DBG_MSG("Failed to allocate temporary buffer!\n");
277 return -1;
278 }
279
280 temp_token = strndup(pathname, strlen(pathname));
281 if (NULL == temp_token) {
282 DBG_MSG("Failed to allocate temporary buffer!\n");
283 goto error;
284 }
285 token_p = temp_token;
286
287 if (pathname[0] == '/')
288 temp_name[0] = '\0';
289 else
290 /* If not an absolute path, make it local */
291 strncpy(temp_name, ".", lenght);
292
293 token = strsep(&token_p, "/");
294 /* First token might be "", but that is OK as it will be when the
295 * pathname starts with '/' */
296 while (NULL != token) {
297 strncat(temp_name, "/", lenght - strlen(temp_name));
298 strncat(temp_name, token, lenght - strlen(temp_name));
299
300 /* If it does not exist, create the dir. If it does exit,
301 * but is not a directory, we will catch it below. */
302 if (1 != exists(temp_name)) {
303 retval = mkdir(temp_name, mode);
304 if (-1 == retval) {
305 DBG_MSG("Failed to create directory!\n");
306 goto error;
307 }
308 /* Not a directory or symlink pointing to a directory */
309 } else if (1 != is_dir(temp_name, 1)) {
310 DBG_MSG("Component in pathname is not a directory!\n");
311 errno = ENOTDIR;
312 goto error;
313 }
314
315 do {
316 token = strsep(&token_p, "/");
317 /* The first "" was Ok, but rather skip double '/' after that */
318 } while ((NULL != token) && (0 == strlen(token)));
319 }
320
321 free(temp_name);
322 free(temp_token);
323
324 return 0;
325
326error:
327 free(temp_name);
328 free(temp_token);
329
330 return -1;
331}
332
333int rmtree(const char *pathname)
334{
335 char **dirlist = NULL;
336 int i = 0;
337
338 if ((NULL == pathname) || (0 == strlen(pathname))) {
339 DBG_MSG("Invalid argument passed!\n");
340 errno = EINVAL;
341 return -1;
342 }
343
344 if (1 != exists(pathname)) {
345 DBG_MSG("'%s' does not exists!\n", pathname);
346 errno = ENOENT;
347 return -1;
348 }
349
350 dirlist = ls_dir(pathname, 1);
351 if ((NULL == dirlist) && (0 != errno)) {
352 DBG_MSG("Could not get listing for '%s'!\n", pathname);
353 return -1;
354 }
355
356 while ((NULL != dirlist) && (NULL != dirlist[i])) {
357 /* If it is a directory, call rmtree() again with
358 * it as argument */
359 if (1 == is_dir(dirlist[i], 0)) {
360 if (-1 == rmtree(dirlist[i])) {
361 DBG_MSG("Failed to delete sub directory!\n");
362 goto error;
363 }
364 }
365
366 /* Now actually remove it. Note that if it was a directory,
367 * it should already be removed by above rmtree() call */
368 if ((1 == exists(dirlist[i]) && (-1 == remove(dirlist[i])))) {
369 DBG_MSG("Failed to remove '%s'!\n", dirlist[i]);
370 goto error;
371 }
372 i++;
373 }
374
375 STRING_LIST_FREE(dirlist);
376
377 /* Now remove the parent */
378 if (-1 == remove(pathname)) {
379 DBG_MSG("Failed to remove '%s'!\n", pathname);
380 goto error;
381 }
382
383 return 0;
384error:
385 STRING_LIST_FREE(dirlist);
386
387 return -1;
388}
389
390char **ls_dir(const char *pathname, int hidden)
391{
392 DIR *dirfd;
393 struct dirent *dir_entry;
394 char **dirlist = NULL;
395
396 if ((NULL == pathname) || (0 == strlen(pathname))) {
397 DBG_MSG("Invalid argument passed!\n");
398 errno = EINVAL;
399 return NULL;
400 }
401
402 dirfd = opendir(pathname);
403 if (NULL == dirfd) {
404 DBG_MSG("Failed to call opendir()!\n");
405 /* errno will be set by opendir() */
406 goto error;
407 }
408
409 do {
410 /* Clear errno to distinguish between EOF and error */
411 errno = 0;
412 dir_entry = readdir(dirfd);
413 /* Only an error if 'errno' != 0, else EOF */
414 if ((NULL == dir_entry) && (0 != errno)) {
415 DBG_MSG("Failed to call readdir()!\n");
416 goto error;
417 }
418 if ((NULL != dir_entry) &&
419 /* Should we display hidden files? */
420 (hidden ? 1 : dir_entry->d_name[0] != '.'))
421 {
422 char *d_name = dir_entry->d_name;
423 char *tmp_p;
424
425 /* Do not list current or parent entries */
426 if ((0 == strcmp(d_name, ".")) ||
427 (0 == strcmp(d_name, "..")))
428 continue;
429
430 tmp_p = strcatpaths(pathname, d_name);
431 if (NULL == tmp_p) {
432 DBG_MSG("Failed to allocate buffer!\n");
433 goto error;
434 }
435
436 STRING_LIST_ADD(dirlist, tmp_p, error);
437 }
438 } while (NULL != dir_entry);
439
440 if ((NULL == dirlist) || (NULL == dirlist[0]))
441 DBG_MSG("Directory is empty.\n");
442
443 closedir(dirfd);
444
445 return dirlist;
446
447error:
448 /* Free dirlist on error */
449 STRING_LIST_FREE(dirlist);
450
451 if (NULL != dirfd) {
452 int old_errno = errno;
453 closedir(dirfd);
454 /* closedir() might have changed it */
455 errno = old_errno;
456 }
457
458 return NULL;
459}
460
461/* This handles simple 'entry="bar"' type variables. If it is more complex
462 * ('entry="$(pwd)"' or such), it will obviously not work, but current behaviour
463 * should be fine for the type of variables we want. */
464char *get_cnf_entry(const char *pathname, const char *entry)
465{
466 char *buf = NULL;
467 char *tmp_buf = NULL;
468 char *tmp_p;
469 char *value = NULL;
470 char *token;
471 size_t lenght;
472 int count;
473 int current = 0;
474
475
476 if ((NULL == pathname)
477 || (0 == strlen(pathname))
478 || (NULL == entry)
479 || (0 == strlen(entry))) {
480 DBG_MSG("Invalid argument passed!\n");
481 errno = EINVAL;
482 return NULL;
483 }
484
485 /* If it is not a file or symlink pointing to a file, bail */
486 if (1 != is_file(pathname, 1)) {
487 DBG_MSG("Given pathname is not a file or do not exist!\n");
488 /* FIXME: Might need to set this to something better? */
489 errno = ENOENT;
490 return NULL;
491 }
492
493 if (-1 == file_map(pathname, &buf, &lenght)) {
494 DBG_MSG("Could not open config file for reading!\n");
495 return NULL;
496 }
497
498 while (current < lenght) {
499 count = buf_get_line(buf, lenght, current);
500
501 tmp_buf = strndup(&buf[current], count);
502 if (NULL == tmp_buf) {
503 DBG_MSG("Failed to allocate temporary buffer!\n");
504 goto error;
505 }
506 tmp_p = tmp_buf;
507
508 /* Strip leading spaces/tabs */
509 while ((tmp_p[0] == ' ') || (tmp_p[0] == '\t'))
510 tmp_p++;
511
512 /* Get entry and value */
513 token = strsep(&tmp_p, "=");
514 /* Bogus entry or value */
515 if (NULL == token)
516 goto _continue;
517
518 /* Make sure we have a string that is larger than 'entry', and
519 * the first part equals 'entry' */
520 if ((strlen(token) > 0) && (0 == strcmp(entry, token)))
521 {
522 do {
523 /* Bash variables are usually quoted */
524 token = strsep(&tmp_p, "\"\'");
525 /* If quoted, the first match will be "" */
526 } while ((NULL != token) && (0 == strlen(token)));
527
528 /* We have a 'entry='. We respect bash rules, so NULL
529 * value for now (if not already) */
530 if (NULL == token) {
531 /* We might have 'entry=' and later 'entry="bar"',
532 * so just continue for now ... we will handle
533 * it below when 'value == NULL' */
534 if (NULL != value) {
535 free(value);
536 value = NULL;
537 }
538 goto _continue;
539 }
540
541 /* If we have already allocated 'value', free it */
542 if (NULL != value)
543 free(value);
544
545 value = strndup(token, strlen(token));
546 if (NULL == value) {
547 DBG_MSG("Failed to allocate temporary buffer!\n");
548 goto error;
549 }
550
551 /* We do not break, as there might be more than one entry
552 * defined, and as bash uses the last, so should we */
553 /* break; */
554 }
555
556_continue:
557 current += count + 1;
558 free(tmp_buf);
559 /* Set to NULL in case we error out above and have
560 * to free below */
561 tmp_buf = NULL;
562 }
563
564
565 if (NULL == value)
566 DBG_MSG("Failed to get value for config entry '%s'!\n", entry);
567
568 file_unmap(buf, lenght);
569
570 return value;
571
572error:
573 free(tmp_buf);
574 free(value);
575
576 if (NULL != buf) {
577 int old_errno = errno;
578 file_unmap(buf, lenght);
579 /* unmmap() might have changed it */
580 errno = old_errno;
581 }
582
583 return NULL;
584}
585
586
587/*
588 * Below three functions (file_map, file_unmap and buf_get_line) are
589 * from udev-050 (udev_utils.c).
590 * (Some are slightly modified, please check udev for originals.)
591 *
592 * Copyright (C) 2004 Kay Sievers <kay@vrfy.org>
593 *
594 * This program is free software; you can redistribute it and/or modify it
595 * under the terms of the GNU General Public License as published by the
596 * Free Software Foundation version 2 of the License.
597 *
598 * This program is distributed in the hope that it will be useful, but
599 * WITHOUT ANY WARRANTY; without even the implied warranty of
600 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
601 * General Public License for more details.
602 *
603 * You should have received a copy of the GNU General Public License along
604 * with this program; if not, write to the Free Software Foundation, Inc.,
605 * 675 Mass Ave, Cambridge, MA 02139, USA.
606 *
607 */
608
609int file_map(const char *filename, char **buf, size_t *bufsize)
610{
611 struct stat stats;
612 int fd;
613 int old_errno;
614
615 fd = open(filename, O_RDONLY);
616 if (fd < 0) {
617 DBG_MSG("Failed to open file!\n");
618 return -1;
619 }
620
621 if (fstat(fd, &stats) < 0) {
622 DBG_MSG("Failed to stat file!\n");
623 old_errno = errno;
624 close(fd);
625 /* close() might have changed it */
626 errno = old_errno;
627 return -1;
628 }
629
630 *buf = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0);
631 if (*buf == MAP_FAILED) {
632 DBG_MSG("Failed to mmap file!\n");
633 old_errno = errno;
634 close(fd);
635 /* close() might have changed it */
636 errno = old_errno;
637 return -1;
638 }
639 *bufsize = stats.st_size;
640
641 close(fd);
642
643 return 0;
644}
645
646void file_unmap(char *buf, size_t bufsize)
647{
648 munmap(buf, bufsize);
649}
650
651size_t buf_get_line(char *buf, size_t buflen, size_t cur)
652{
653 size_t count = 0;
654
655 for (count = cur; count < buflen && buf[count] != '\n'; count++);
656
657 return count - cur;
658}
659

Legend:
Removed from v.126  
changed lines
  Added in v.127

  ViewVC Help
Powered by ViewVC 1.1.20