source: mainline/uspace/srv/logger/logs.c@ 054476d

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 054476d was feeac0d, checked in by Jiri Svoboda <jiri@…>, 12 years ago

Simplify use of list_foreach.

  • Property mode set to 100644
File size: 8.4 KB
Line 
1/*
2 * Copyright (c) 2012 Vojtech Horky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup logger
30 * @{
31 */
32#include <assert.h>
33#include <malloc.h>
34#include <str.h>
35#include <stdio.h>
36#include <errno.h>
37#include "logger.h"
38
39
40static FIBRIL_MUTEX_INITIALIZE(log_list_guard);
41static LIST_INITIALIZE(log_list);
42
43
44static logger_log_t *find_log_by_name_and_parent_no_list_lock(const char *name, logger_log_t *parent)
45{
46 list_foreach(log_list, link, logger_log_t, log) {
47 if ((parent == log->parent) && (str_cmp(log->name, name) == 0))
48 return log;
49 }
50
51 return NULL;
52}
53
54static int create_dest(const char *name, logger_dest_t **dest)
55{
56 logger_dest_t *result = malloc(sizeof(logger_dest_t));
57 if (result == NULL)
58 return ENOMEM;
59 int rc = asprintf(&result->filename, "/log/%s", name);
60 if (rc < 0) {
61 free(result);
62 return ENOMEM;
63 }
64 result->logfile = NULL;
65 fibril_mutex_initialize(&result->guard);
66 *dest = result;
67 return EOK;
68}
69
70static logger_log_t *create_log_no_locking(const char *name, logger_log_t *parent)
71{
72 logger_log_t *result = calloc(1, sizeof(logger_log_t));
73 if (result == NULL)
74 return NULL;
75
76 result->name = str_dup(name);
77 if (result->name == NULL)
78 goto error;
79
80 /*
81 * Notice that we create new dest as the last
82 * operation that can fail and thus there is no code
83 * to deallocate dest.
84 */
85 if (parent == NULL) {
86 result->full_name = str_dup(name);
87 if (result->full_name == NULL)
88 goto error;
89 int rc = create_dest(name, &result->dest);
90 if (rc != EOK)
91 goto error;
92 } else {
93 int rc = asprintf(&result->full_name, "%s/%s",
94 parent->full_name, name);
95 if (rc < 0)
96 goto error;
97 result->dest = parent->dest;
98 }
99
100 /* Following initializations cannot fail. */
101 result->logged_level = LOG_LEVEL_USE_DEFAULT;
102 fibril_mutex_initialize(&result->guard);
103 link_initialize(&result->link);
104 result->parent = parent;
105
106 return result;
107
108error:
109 free(result->name);
110 free(result->full_name);
111 free(result);
112 return NULL;
113
114}
115
116logger_log_t *find_or_create_log_and_lock(const char *name, sysarg_t parent_id)
117{
118 logger_log_t *result = NULL;
119 logger_log_t *parent = (logger_log_t *) parent_id;
120
121 fibril_mutex_lock(&log_list_guard);
122
123 result = find_log_by_name_and_parent_no_list_lock(name, parent);
124 if (result == NULL) {
125 result = create_log_no_locking(name, parent);
126 if (result == NULL)
127 goto leave;
128 list_append(&result->link, &log_list);
129 if (result->parent != NULL) {
130 fibril_mutex_lock(&result->parent->guard);
131 result->parent->ref_counter++;
132 fibril_mutex_unlock(&result->parent->guard);
133 }
134 }
135
136 fibril_mutex_lock(&result->guard);
137
138leave:
139 fibril_mutex_unlock(&log_list_guard);
140
141 return result;
142}
143
144logger_log_t *find_log_by_name_and_lock(const char *name)
145{
146 logger_log_t *result = NULL;
147
148 fibril_mutex_lock(&log_list_guard);
149 list_foreach(log_list, link, logger_log_t, log) {
150 if (str_cmp(log->full_name, name) == 0) {
151 fibril_mutex_lock(&log->guard);
152 result = log;
153 break;
154 }
155 }
156 fibril_mutex_unlock(&log_list_guard);
157
158 return result;
159}
160
161logger_log_t *find_log_by_id_and_lock(sysarg_t id)
162{
163 logger_log_t *result = NULL;
164
165 fibril_mutex_lock(&log_list_guard);
166 list_foreach(log_list, link, logger_log_t, log) {
167 if ((sysarg_t) log == id) {
168 fibril_mutex_lock(&log->guard);
169 result = log;
170 break;
171 }
172 }
173 fibril_mutex_unlock(&log_list_guard);
174
175 return result;
176}
177
178static log_level_t get_actual_log_level(logger_log_t *log)
179{
180 /* Find recursively proper log level. */
181 if (log->logged_level == LOG_LEVEL_USE_DEFAULT) {
182 if (log->parent == NULL)
183 return get_default_logging_level();
184 else
185 return get_actual_log_level(log->parent);
186 }
187 return log->logged_level;
188}
189
190bool shall_log_message(logger_log_t *log, log_level_t level)
191{
192 fibril_mutex_lock(&log_list_guard);
193 bool result = level <= get_actual_log_level(log);
194 fibril_mutex_unlock(&log_list_guard);
195 return result;
196}
197
198void log_unlock(logger_log_t *log)
199{
200 assert(fibril_mutex_is_locked(&log->guard));
201 fibril_mutex_unlock(&log->guard);
202}
203
204/** Decreases reference counter on the log and destory the log if
205 * necessary.
206 *
207 * Precondition: log is locked.
208 *
209 * @param log Log to release from using by the caller.
210 */
211void log_release(logger_log_t *log)
212{
213 assert(fibril_mutex_is_locked(&log->guard));
214 assert(log->ref_counter > 0);
215
216 /* We are definitely not the last ones. */
217 if (log->ref_counter > 1) {
218 log->ref_counter--;
219 fibril_mutex_unlock(&log->guard);
220 return;
221 }
222
223 /*
224 * To prevent deadlock, we need to get the list lock first.
225 * Deadlock scenario:
226 * Us: LOCKED(log), want to LOCK(list)
227 * Someone else calls find_log_by_name_and_lock(log->fullname) ->
228 * LOCKED(list), wants to LOCK(log)
229 */
230 fibril_mutex_unlock(&log->guard);
231
232 /* Ensuring correct locking order. */
233 fibril_mutex_lock(&log_list_guard);
234 /*
235 * The reference must be still valid because we have not decreased
236 * the reference counter.
237 */
238 fibril_mutex_lock(&log->guard);
239 assert(log->ref_counter > 0);
240 log->ref_counter--;
241
242 if (log->ref_counter > 0) {
243 /*
244 * Meanwhile, someone else increased the ref counter.
245 * No big deal, we just return immediatelly.
246 */
247 fibril_mutex_unlock(&log->guard);
248 fibril_mutex_unlock(&log_list_guard);
249 return;
250 }
251
252 /*
253 * Here we are on a destroy path. We need to
254 * - remove ourselves from the list
255 * - decrease reference of the parent (if not top-level log)
256 * - we must do that after we relaase list lock to prevent
257 * deadlock with ourselves
258 * - destroy dest (if top-level log)
259 */
260 assert(log->ref_counter == 0);
261
262 list_remove(&log->link);
263 fibril_mutex_unlock(&log_list_guard);
264 fibril_mutex_unlock(&log->guard);
265
266 if (log->parent == NULL) {
267 /*
268 * Due to lazy file opening in write_to_log(),
269 * it is possible that no file was actually opened.
270 */
271 if (log->dest->logfile != NULL) {
272 fclose(log->dest->logfile);
273 }
274 free(log->dest->filename);
275 free(log->dest);
276 } else {
277 fibril_mutex_lock(&log->parent->guard);
278 log_release(log->parent);
279 }
280
281 logger_log("Destroyed log %s.\n", log->full_name);
282
283 free(log->name);
284 free(log->full_name);
285
286 free(log);
287}
288
289
290void write_to_log(logger_log_t *log, log_level_t level, const char *message)
291{
292 assert(fibril_mutex_is_locked(&log->guard));
293 assert(log->dest != NULL);
294 fibril_mutex_lock(&log->dest->guard);
295 if (log->dest->logfile == NULL)
296 log->dest->logfile = fopen(log->dest->filename, "a");
297
298 if (log->dest->logfile != NULL) {
299 fprintf(log->dest->logfile, "[%s] %s: %s\n",
300 log->full_name, log_level_str(level),
301 (const char *) message);
302 fflush(log->dest->logfile);
303 }
304
305 fibril_mutex_unlock(&log->dest->guard);
306}
307
308void registered_logs_init(logger_registered_logs_t *logs)
309{
310 logs->logs_count = 0;
311}
312
313bool register_log(logger_registered_logs_t *logs, logger_log_t *new_log)
314{
315 if (logs->logs_count >= MAX_REFERENCED_LOGS_PER_CLIENT) {
316 return false;
317 }
318
319 assert(fibril_mutex_is_locked(&new_log->guard));
320 new_log->ref_counter++;
321
322 logs->logs[logs->logs_count] = new_log;
323 logs->logs_count++;
324
325 return true;
326}
327
328void unregister_logs(logger_registered_logs_t *logs)
329{
330 for (size_t i = 0; i < logs->logs_count; i++) {
331 logger_log_t *log = logs->logs[i];
332 fibril_mutex_lock(&log->guard);
333 log_release(log);
334 }
335}
336
337/**
338 * @}
339 */
Note: See TracBrowser for help on using the repository browser.