3 * Copyright (c) 2015-2020 Samsung Electronics Co., Ltd
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:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
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
32 #include <sys/inotify.h>
35 #include <dynamic_config.h>
37 #include <logcommon.h>
38 #include <logconfig.h>
39 #include <loglimiter.h>
41 static int inotify_fd = -1;
42 static typeof(((struct inotify_event *)0)->wd) inotify_wd = -1;
43 static char *inotify_path;
45 static pthread_mutex_t log_dynamic_config_lock = PTHREAD_MUTEX_INITIALIZER;
47 static bool __setup_runtime_watch(char const *path)
50 assert(inotify_fd == -1);
51 assert(inotify_wd == -1);
53 inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
54 if (inotify_fd == -1) {
55 syslog_critical_failure("DLog: inotify_init failed (%d)", errno);
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);
70 static void __apply_update()
74 __attribute__((cleanup(log_config_free))) struct log_config config = {NULL, NULL};
75 log_config_read_dir(&config, inotify_path);
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);
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
86 __log_limiter_update(&config);
87 pthread_rwlock_unlock(&log_limiter_lock);
91 /// caller has to guarantee exclusive access
92 bool __dynamic_config_create(struct log_config *config)
95 assert(!inotify_path);
97 const char *const extra_config_path = log_config_get(config, DYNAMIC_CONFIG_CONF_KEY);
98 if (!extra_config_path || extra_config_path[0] != '/')
101 inotify_path = strdup(extra_config_path);
102 if (inotify_path == NULL) {
103 syslog_critical_failure("DLog: out of memory");
107 if (!__setup_runtime_watch(extra_config_path)) {
113 log_config_read_dir(config, inotify_path);
118 /// caller has to guarantee exclusive access
119 void __dynamic_config_destroy()
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);
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.
139 assert(inotify_fd >= 0);
144 void __dynamic_config_update()
149 struct ievent_with_filename {
150 struct inotify_event ie;
151 char filename[NAME_MAX + 1];
153 const int r = read(inotify_fd, &ievent, sizeof ievent);
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. */
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);
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);
192 pthread_mutex_unlock(&log_dynamic_config_lock);