1 /*
2 * Functions for storing program settings
3 * Copyright (C) 2008 Andreas Öman
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <dirent.h>
29
30 #include "htsmsg.h"
31 #include "htsmsg_binary.h"
32 #include "htsmsg_json.h"
33 #include "settings.h"
34 #include "tvheadend.h"
35 #include "filebundle.h"
36
37 static char *settingspath = NULL;
38
39 /**
40 *
41 */
42 const char *
hts_settings_get_root(void)43 hts_settings_get_root(void)
44 {
45 return settingspath;
46 }
47
48 /**
49 *
50 */
51 void
hts_settings_init(const char * confpath)52 hts_settings_init(const char *confpath)
53 {
54 if (confpath)
55 settingspath = realpath(confpath, NULL);
56 }
57
58 /**
59 *
60 */
61 void
hts_settings_done(void)62 hts_settings_done(void)
63 {
64 free(settingspath);
65 }
66
67 /**
68 *
69 */
70 int
hts_settings_makedirs(const char * inpath)71 hts_settings_makedirs ( const char *inpath )
72 {
73 size_t x = strlen(inpath) - 1;
74 char *path = alloca(x + 2);
75
76 if (path == NULL) return -1;
77 strcpy(path, inpath);
78
79 while (x) {
80 if (path[x] == '/') {
81 path[x] = 0;
82 break;
83 }
84 x--;
85 }
86 return makedirs(LS_SETTINGS, path, 0700, 1, -1, -1);
87 }
88
89 /**
90 *
91 */
92 static void
_hts_settings_buildpath(char * dst,size_t dstsize,const char * fmt,va_list ap,const char * prefix)93 _hts_settings_buildpath
94 (char *dst, size_t dstsize, const char *fmt, va_list ap, const char *prefix)
95 {
96 char tmp[PATH_MAX];
97 char *n = dst;
98
99 vsnprintf(tmp, sizeof(tmp), fmt, ap);
100 if (*tmp != '/' && prefix)
101 snprintf(dst, dstsize, "%s/%s", prefix, tmp);
102 else
103 strlcpy(dst, tmp, dstsize);
104
105 while(*n) {
106 if(*n == ':' || *n == '?' || *n == '*' || *n > 127 || *n < 32)
107 *n = '_';
108 n++;
109 }
110 }
111
112 int
hts_settings_buildpath(char * dst,size_t dstsize,const char * fmt,...)113 hts_settings_buildpath
114 (char *dst, size_t dstsize, const char *fmt, ...)
115 {
116 va_list va;
117 if (!settingspath)
118 return 1;
119 va_start(va, fmt);
120 _hts_settings_buildpath(dst, dstsize, fmt, va, settingspath);
121 va_end(va);
122 return 0;
123 }
124
125 /**
126 *
127 */
128 void
hts_settings_save(htsmsg_t * record,const char * pathfmt,...)129 hts_settings_save(htsmsg_t *record, const char *pathfmt, ...)
130 {
131 char path[PATH_MAX];
132 char tmppath[PATH_MAX + 4];
133 int fd;
134 va_list ap;
135 htsbuf_queue_t hq;
136 htsbuf_data_t *hd;
137 int ok, r, pack;
138
139 if(settingspath == NULL)
140 return;
141
142 /* Clean the path */
143 va_start(ap, pathfmt);
144 _hts_settings_buildpath(path, sizeof(path), pathfmt, ap, settingspath);
145 va_end(ap);
146
147 /* Create directories */
148 if (hts_settings_makedirs(path)) return;
149
150 tvhdebug(LS_SETTINGS, "saving to %s", path);
151
152 /* Create tmp file */
153 snprintf(tmppath, sizeof(tmppath), "%s.tmp", path);
154 if((fd = tvh_open(tmppath, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR)) < 0) {
155 tvhalert(LS_SETTINGS, "Unable to create \"%s\" - %s",
156 tmppath, strerror(errno));
157 return;
158 }
159
160 /* Store data */
161 #if ENABLE_ZLIB
162 pack = strstr(path, "/muxes/") != NULL && /* ugly, redesign API */
163 strstr(path, "/networks/") != NULL &&
164 strstr(path, "/input/") != NULL;
165 #else
166 pack = 0;
167 #endif
168 ok = 1;
169
170 if (!pack) {
171 htsbuf_queue_init(&hq, 0);
172 htsmsg_json_serialize(record, &hq, 1);
173 TAILQ_FOREACH(hd, &hq.hq_q, hd_link)
174 if(tvh_write(fd, hd->hd_data + hd->hd_data_off, hd->hd_data_len)) {
175 tvhalert(LS_SETTINGS, "Failed to write file \"%s\" - %s",
176 tmppath, strerror(errno));
177 ok = 0;
178 break;
179 }
180 htsbuf_queue_flush(&hq);
181 } else {
182 #if ENABLE_ZLIB
183 void *msgdata = NULL;
184 size_t msglen;
185 r = htsmsg_binary_serialize(record, &msgdata, &msglen, 2*1024*1024);
186 if (!r && msglen >= 4) {
187 r = tvh_gzip_deflate_fd_header(fd, msgdata + 4, msglen - 4, NULL, 3);
188 if (r)
189 ok = 0;
190 } else {
191 tvhalert(LS_SETTINGS, "Unable to pack the configuration data \"%s\"", path);
192 }
193 free(msgdata);
194 #endif
195 }
196 close(fd);
197
198 /* Move */
199 if(ok) {
200 r = rename(tmppath, path);
201 if (r && errno == EISDIR) {
202 rmtree(path);
203 r = rename(tmppath, path);
204 }
205 if (r)
206 tvhalert(LS_SETTINGS, "Unable to rename file \"%s\" to \"%s\" - %s",
207 tmppath, path, strerror(errno));
208
209 /* Delete tmp */
210 } else
211 unlink(tmppath);
212 }
213
214 /**
215 *
216 */
217 static htsmsg_t *
hts_settings_load_one(const char * filename)218 hts_settings_load_one(const char *filename)
219 {
220 ssize_t n, size;
221 char *mem;
222 fb_file *fp;
223 htsmsg_t *r = NULL;
224
225 /* Open */
226 if (!(fp = fb_open(filename, 1, 0))) return NULL;
227 size = fb_size(fp);
228
229 /* Load data */
230 mem = malloc(size+1);
231 n = fb_read(fp, mem, size);
232 if (n >= 0) mem[n] = 0;
233
234 /* Decode */
235 if(n == size) {
236 if (size > 12 && memcmp(mem, "\xff\xffGZIP00", 8) == 0) {
237 #if ENABLE_ZLIB
238 uint32_t orig = (mem[8] << 24) | (mem[9] << 16) | (mem[10] << 8) | mem[11];
239 if (orig > 10*1024*1024U) {
240 tvhalert(LS_SETTINGS, "too big gzip for %s", filename);
241 r = NULL;
242 } else if (orig > 0) {
243 uint8_t *unpacked = tvh_gzip_inflate((uint8_t *)mem + 12, size - 12, orig);
244 if (unpacked) {
245 r = htsmsg_binary_deserialize(unpacked, orig, NULL);
246 free(unpacked);
247 }
248 }
249 #endif
250 } else {
251 r = htsmsg_json_deserialize(mem);
252 }
253 }
254
255 /* Close */
256 fb_close(fp);
257 free(mem);
258
259 return r;
260 }
261
262 /**
263 *
264 */
265 static htsmsg_t *
hts_settings_load_path(const char * fullpath,int depth)266 hts_settings_load_path(const char *fullpath, int depth)
267 {
268 char child[PATH_MAX];
269 struct filebundle_stat st;
270 fb_dirent **namelist, *d;
271 htsmsg_t *r, *c;
272 int n, i;
273
274 /* Invalid */
275 if (fb_stat(fullpath, &st)) return NULL;
276
277 /* Directory */
278 if (st.is_dir) {
279
280 /* Get file list */
281 if((n = fb_scandir(fullpath, &namelist)) < 0)
282 return NULL;
283
284 /* Read files */
285 r = htsmsg_create_map();
286 for(i = 0; i < n; i++) {
287 d = namelist[i];
288 if(d->name[0] != '.') {
289
290 snprintf(child, sizeof(child), "%s/%s", fullpath, d->name);
291 if(d->type == FB_DIR && depth > 0) {
292 c = hts_settings_load_path(child, depth - 1);
293 } else {
294 c = hts_settings_load_one(child);
295 }
296 if(c != NULL)
297 htsmsg_add_msg(r, d->name, c);
298
299 }
300 free(d);
301 }
302 free(namelist);
303
304 /* File */
305 } else {
306 r = hts_settings_load_one(fullpath);
307 }
308
309 return r;
310 }
311
312 /**
313 *
314 */
315 static htsmsg_t *
hts_settings_vload(const char * pathfmt,va_list ap,int depth)316 hts_settings_vload(const char *pathfmt, va_list ap, int depth)
317 {
318 htsmsg_t *ret = NULL;
319 char fullpath[PATH_MAX];
320 va_list ap2;
321 va_copy(ap2, ap);
322
323 /* Try normal path */
324 _hts_settings_buildpath(fullpath, sizeof(fullpath),
325 pathfmt, ap, settingspath);
326 ret = hts_settings_load_path(fullpath, depth);
327
328 /* Try bundle path */
329 if (!ret && *pathfmt != '/') {
330 _hts_settings_buildpath(fullpath, sizeof(fullpath),
331 pathfmt, ap2, "data/conf");
332 ret = hts_settings_load_path(fullpath, depth);
333 }
334
335 va_end(ap2);
336
337 return ret;
338 }
339
340
341 /**
342 *
343 */
344 htsmsg_t *
hts_settings_load(const char * pathfmt,...)345 hts_settings_load(const char *pathfmt, ...)
346 {
347 va_list ap;
348 va_start(ap, pathfmt);
349 htsmsg_t *r = hts_settings_vload(pathfmt, ap, 0);
350 va_end(ap);
351 return r;
352 }
353
354
355 /**
356 *
357 */
358 htsmsg_t *
hts_settings_load_r(int depth,const char * pathfmt,...)359 hts_settings_load_r(int depth, const char *pathfmt, ...)
360 {
361 va_list ap;
362 va_start(ap, pathfmt);
363 htsmsg_t *r = hts_settings_vload(pathfmt, ap, depth);
364 va_end(ap);
365 return r;
366 }
367
368 /**
369 *
370 */
371 void
hts_settings_remove(const char * pathfmt,...)372 hts_settings_remove(const char *pathfmt, ...)
373 {
374 char fullpath[PATH_MAX];
375 va_list ap;
376 struct stat st;
377
378 va_start(ap, pathfmt);
379 _hts_settings_buildpath(fullpath, sizeof(fullpath),
380 pathfmt, ap, settingspath);
381 va_end(ap);
382 if (stat(fullpath, &st) == 0) {
383 if (S_ISDIR(st.st_mode))
384 rmtree(fullpath);
385 else {
386 unlink(fullpath);
387 while (rmdir(dirname(fullpath)) == 0);
388 }
389 }
390 }
391
392 /**
393 *
394 */
395 int
hts_settings_open_file(int for_write,const char * pathfmt,...)396 hts_settings_open_file(int for_write, const char *pathfmt, ...)
397 {
398 char path[PATH_MAX];
399 int flags;
400 va_list ap;
401
402 /* Build path */
403 va_start(ap, pathfmt);
404 _hts_settings_buildpath(path, sizeof(path), pathfmt, ap, settingspath);
405 va_end(ap);
406
407 /* Create directories */
408 if (for_write)
409 if (hts_settings_makedirs(path)) return -1;
410
411 /* Open file */
412 flags = for_write ? O_CREAT | O_TRUNC | O_WRONLY : O_RDONLY;
413
414 return tvh_open(path, flags, S_IRUSR | S_IWUSR);
415 }
416
417 /*
418 * Check if a path exists
419 */
420 int
hts_settings_exists(const char * pathfmt,...)421 hts_settings_exists ( const char *pathfmt, ... )
422 {
423 va_list ap;
424 char path[PATH_MAX];
425 struct stat st;
426
427 /* Build path */
428 va_start(ap, pathfmt);
429 _hts_settings_buildpath(path, sizeof(path), pathfmt, ap, settingspath);
430 va_end(ap);
431
432 return (stat(path, &st) == 0);
433 }
434