1/*** 2 This file is part of eudev, forked from systemd. 3 4 Copyright 2010 Lennart Poettering 5 6 systemd is free software; you can redistribute it and/or modify it 7 under the terms of the GNU Lesser General Public License as published by 8 the Free Software Foundation; either version 2.1 of the License, or 9 (at your option) any later version. 10 11 systemd is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with systemd; If not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#include <assert.h> 21#include <string.h> 22#include <unistd.h> 23#include <errno.h> 24#include <stdlib.h> 25#include <stdio.h> 26#include <sys/stat.h> 27#include <dirent.h> 28 29#include "macro.h" 30#include "util.h" 31#include "missing.h" 32#include "log.h" 33#include "strv.h" 34#include "path-util.h" 35#include "hashmap.h" 36#include "conf-files.h" 37 38static const char *basename_internal(const char *filename) { 39 const char *ret = filename, *p = filename; 40 41 p = strchr(filename, '/'); 42 while (p) { 43 ret = &p[1]; 44 p = strchr(ret, '/'); 45 } 46 return ret; 47} 48 49static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) { 50 _cleanup_closedir_ DIR *dir = NULL; 51 char *dirpath; 52 53 assert(path); 54 assert(suffix); 55 56 dirpath = strjoina(root ? root : "", path); 57 58 dir = opendir(dirpath); 59 if (!dir) { 60 if (errno == ENOENT) 61 return 0; 62 return -errno; 63 } 64 65 for (;;) { 66 struct dirent *de; 67 char *p; 68 int r; 69 70 errno = 0; 71 de = readdir(dir); 72 if (!de && errno != 0) 73 return -errno; 74 75 if (!de) 76 break; 77 78 if (!dirent_is_file_with_suffix(de, suffix)) 79 continue; 80 81 p = strjoin(dirpath, "/", de->d_name, NULL); 82 if (!p) 83 return -ENOMEM; 84 85 r = hashmap_put(h, basename_internal(p), p); 86 if (r == -EEXIST) { 87 log_debug("Skipping overridden file: %s.", p); 88 free(p); 89 } else if (r < 0) { 90 free(p); 91 return r; 92 } else if (r == 0) { 93 log_debug("Duplicate file %s", p); 94 free(p); 95 } 96 } 97 98 return 0; 99} 100 101static int base_cmp(const void *a, const void *b) { 102 const char *s1, *s2; 103 104 s1 = *(char * const *)a; 105 s2 = *(char * const *)b; 106 return strcmp(basename_internal(s1), basename_internal(s2)); 107} 108 109static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) { 110 _cleanup_hashmap_free_ Hashmap *fh = NULL; 111 char **files, **p; 112 int r; 113 114 assert(strv); 115 assert(suffix); 116 117 /* This alters the dirs string array */ 118 if (!path_strv_resolve_uniq(dirs, root)) 119 return -ENOMEM; 120 121 fh = hashmap_new(&string_hash_ops); 122 if (!fh) 123 return -ENOMEM; 124 125 STRV_FOREACH(p, dirs) { 126 r = files_add(fh, root, *p, suffix); 127 if (r == -ENOMEM) { 128 return r; 129 } else if (r < 0) 130 log_debug("Failed to search for files in %s: %s", 131 *p, strerror(-r)); 132 } 133 134 files = hashmap_get_strv(fh); 135 if (files == NULL) { 136 return -ENOMEM; 137 } 138 139 qsort_safe(files, hashmap_size(fh), sizeof(char *), base_cmp); 140 *strv = files; 141 142 return 0; 143} 144 145int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs) { 146 _cleanup_strv_free_ char **copy = NULL; 147 148 assert(strv); 149 assert(suffix); 150 151 copy = strv_copy((char**) dirs); 152 if (!copy) 153 return -ENOMEM; 154 155 return conf_files_list_strv_internal(strv, suffix, root, copy); 156} 157