4e9bf976ba9502d30f88a75478d5b395bb8556d2
[platform/core/system/dlog.git] / src / libdlog / dynamic_config.c
1 /*  MIT License
2  *
3  * Copyright (c) 2015-2020 Samsung Electronics Co., Ltd
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to deal
7  * in the Software without restriction, including without limitation the rights
8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9  * copies of the Software, and to permit persons to whom the Software is furnished
10  * to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21  * THE SOFTWARE. */
22
23 // C
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 // POSIX
29 #include <limits.h>
30 #include <pthread.h>
31 #include <unistd.h>
32 #include <sys/inotify.h>
33
34 // DLog
35 #include <dynamic_config.h>
36 #include <libdlog.h>
37 #include <logcommon.h>
38 #include <logconfig.h>
39 #include <loglimiter.h>
40
41 static int inotify_fd = -1;
42 static typeof(((struct inotify_event *)0)->wd) inotify_wd = -1;
43 static char *inotify_path;
44
45 static pthread_mutex_t log_dynamic_config_lock = PTHREAD_MUTEX_INITIALIZER;
46
47 static bool __setup_runtime_watch(char const *path)
48 {
49         assert(path);
50         assert(inotify_fd == -1);
51         assert(inotify_wd == -1);
52
53         inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
54         if (inotify_fd == -1) {
55                 syslog_critical_failure("DLog: inotify_init failed (%d)", errno);
56                 return false;
57         }
58
59         inotify_wd = inotify_add_watch(inotify_fd, path, IN_CREATE | IN_CLOSE_WRITE | IN_MOVE | IN_DELETE);
60         if (inotify_wd == -1) {
61                 syslog_critical_failure("DLog: inotify_add_watch failed (%d)", errno);
62                 close(inotify_fd);
63                 inotify_fd = -1;
64                 return false;
65         }
66
67         return true;
68 }
69
70 static void __apply_update()
71 {
72         assert(inotify_path);
73
74         __attribute__((cleanup(log_config_free))) struct log_config config = {NULL, NULL};
75         log_config_read_dir(&config, inotify_path);
76
77         /* This could be concurrently read by other threads, but it's just a bunch
78          * of bools whose values carry no implications regarding others so it is
79          * fine not to lock. */
80         __update_plog(&config);
81
82         if (limiter) {
83                 const int l = pthread_rwlock_wrlock(&log_limiter_lock);
84                 assert(!l); // we should never have a read lock at this point so wrlock() cannot fail
85
86                 __log_limiter_update(&config);
87                 pthread_rwlock_unlock(&log_limiter_lock);
88         }
89 }
90
91 /// caller has to guarantee exclusive access
92 bool __dynamic_config_create(struct log_config *config)
93 {
94         assert(config);
95         assert(!inotify_path);
96
97         const char *const extra_config_path = log_config_get(config, DYNAMIC_CONFIG_CONF_KEY);
98         if (!extra_config_path || extra_config_path[0] != '/')
99                 return false;
100
101         inotify_path = strdup(extra_config_path);
102         if (inotify_path == NULL) {
103                 syslog_critical_failure("DLog: out of memory");
104                 return false;
105         }
106
107         if (!__setup_runtime_watch(extra_config_path)) {
108                 free(inotify_path);
109                 inotify_path = NULL;
110                 return false;
111         }
112
113         log_config_read_dir(config, inotify_path);
114
115         return true;
116 }
117
118 /// caller has to guarantee exclusive access
119 void __dynamic_config_destroy()
120 {
121         assert(inotify_fd <  0 ||  inotify_path);
122         assert(inotify_fd >= 0 || !inotify_path);
123         assert(inotify_wd < 0 || inotify_fd >= 0);
124         assert(inotify_fd < 0 || inotify_wd >= 0);
125
126         if (!inotify_path)
127                 return;
128
129         free(inotify_path);
130         inotify_path = NULL;
131
132         /* Important: We can not call inotify_rm_watch() here - the
133          * process might have forked, sharing inotify_fd. Removing the
134          * watch would break monitoring capability in all processes
135          * sharing the inotify_fd.
136          */
137         inotify_wd = -1;
138
139         assert(inotify_fd >= 0);
140         close(inotify_fd);
141         inotify_fd = -1;
142 }
143
144 void __dynamic_config_update()
145 {
146         if (inotify_fd < 0)
147                 return;
148
149         struct ievent_with_filename {
150                 struct inotify_event ie;
151                 char filename[NAME_MAX + 1];
152         } ievent;
153         const int r = read(inotify_fd, &ievent, sizeof ievent);
154         if (r < 0)
155                 return;
156
157         /* read() cannot really return 0 here (technically it can do
158          * that in kernels before 2.6.21 if the buffer is too small,
159          * but that is not really applicable here). The reason for
160          * this check is that some daemons close all file descriptors
161          * including this one (they don't have much choice since dlog
162          * does not expose any way to get its FD numbers), open some
163          * other files (which potentially reuses the same FD number
164          * as inotify_fd) and then try to log something using dlog.
165          * The result is that read(inofify_fd, ...) instead reads from
166          * that other descriptor, which is not necessarily (and in fact,
167          * quite unlikely to be) an inotify descriptor, meaning it can
168          * return 0. Sometimes we're lucky and it happens - the client
169          * shouldn't see any difference afterwards so this situation
170          * can be ignored without much fuss. Other times we "steal"
171          * some data which does not belong to us and is not a correctly
172          * formatted inotify event, in which case WD will contain some
173          * garbage value, triggering the assert. This is not actually
174          * a bad thing; it's a fairly decent way to let the client know
175          * that something is amiss. */
176         if (r == 0)
177                 return;
178
179         /* -1 means an overflow of the the event queue. This is fine
180          * (if a bit worrisome) since it still tells us that an event
181          * has arrived, which is all we care about at this point. */
182         assert(ievent.ie.wd == inotify_wd || ievent.ie.wd == -1);
183
184         /* Somebody else might already be updating, in which case we
185          * needn't bother since we would be reading the same files. */
186         int lock_r = pthread_mutex_trylock(&log_dynamic_config_lock);
187         if (lock_r < 0)
188                 return;
189
190         __apply_update();
191
192         pthread_mutex_unlock(&log_dynamic_config_lock);
193 }