]> mj.ucw.cz Git - libucw.git/blob - ucw/log-stream.c
Logging: Fix log_close_all().
[libucw.git] / ucw / log-stream.c
1 /*
2  *      UCW Library -- Logging: Management of Log Streams
3  *
4  *      (c) 2008 Tomas Gavenciak <gavento@ucw.cz>
5  *      (c) 2009 Martin Mares <mj@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 #include "ucw/lib.h"
12 #include "ucw/log.h"
13 #include "ucw/log-internal.h"
14 #include "ucw/simple-lists.h"
15
16 #include <string.h>
17
18 /* Initial number of streams to allocate (must be >=2) */
19 #define LS_INIT_STREAMS 8
20
21 /* Flag indicating initialization of the module */
22 static int log_initialized = 0;
23
24 /* The head of the list of freed log_streams indexes in log_streams.ptr (~0U if none free).
25  * Freed positions in log_streams.ptr are connected into a linked list in the following way:
26  * log_streams.ptr[log_streams_free].levels is the index of next freed position (or ~0U) */
27 static uns log_streams_free = ~0U;
28
29 /* Initialize the logstream module.
30  * It is not neccessary to call this explicitely as it is called by
31  * the first log_new_stream()  (for backward compatibility and ease of use). */
32 static void
33 log_init_module(void)
34 {
35   if (log_initialized)
36     return;
37
38   /* Create the growing array */
39   lsbuf_init(&log_streams);
40   lsbuf_set_size(&log_streams, LS_INIT_STREAMS);
41
42   bzero(log_streams.ptr, sizeof(struct log_stream*) * (log_streams.len));
43   log_streams_free = ~0U;
44
45   log_initialized = 1;
46
47   /* init the default stream (0) as forwarder to fd2 */
48   struct log_stream *ls = log_new_stream(sizeof(*ls));
49   ASSERT(ls == log_streams.ptr[0]);
50   ASSERT(ls->regnum == 0);
51   ls->name = "default";
52   log_add_substream(ls, &log_stream_default);
53 }
54
55 /* Close all open streams, un-initialize the module, free all memory,
56  * and fall back to using only log_stream_default. */
57 void
58 log_close_all(void)
59 {
60   if (!log_initialized)
61     return;
62
63   // Close all open streams
64   for (int i=0; i < log_streams_after; i++)
65     if (log_streams.ptr[i]->regnum >= 0)
66       log_close_stream(log_streams.ptr[i]);
67
68   // Free all cached structures
69   for (int i=0; i < log_streams_after; i++)
70     xfree(log_streams.ptr[i]);
71
72   /* Back to the default state */
73   lsbuf_done(&log_streams);
74   log_streams_after = 0;
75   log_streams_free = ~0U;
76   log_initialized = 0;
77 }
78
79 /* Add a new substream. The parent stream takes a reference on the substream,
80  * preventing it from being closed as long as it is linked. */
81 void
82 log_add_substream(struct log_stream *where, struct log_stream *what)
83 {
84   ASSERT(where);
85   ASSERT(what);
86
87   simp_node *n = xmalloc(sizeof(simp_node));
88   n->p = log_ref_stream(what);
89   clist_add_tail(&where->substreams, &n->n);
90 }
91
92 /* Remove all occurrences of a substream together with the references they
93  * keep. If a substream becomes unreferenced, it is closed. If what is NULL,
94  * all substreams are removed. Returns the number of deleted entries. */
95 int
96 log_rm_substream(struct log_stream *where, struct log_stream *what)
97 {
98   void *tmp;
99   int cnt = 0;
100   ASSERT(where);
101
102   CLIST_FOR_EACH_DELSAFE(simp_node *, i, where->substreams, tmp)
103     if (i->p == what || !what)
104       {
105         clist_remove(&i->n);
106         log_close_stream(i->p);
107         xfree(i);
108         cnt++;
109       }
110   return cnt;
111 }
112
113 /* Return a pointer to a new stream with no handler and an empty substream list. */
114 struct log_stream *
115 log_new_stream(size_t size)
116 {
117   struct log_stream *l;
118   int index;
119
120   /* Initialize the data structures if needed */
121   log_init_module();
122
123   /* Get a free stream, possibly recycling a closed one */
124   if (log_streams_free == ~0U)
125     {
126       lsbuf_grow(&log_streams, log_streams_after+1);
127       index = log_streams_after++;
128       l = log_streams.ptr[index] = xmalloc(size);
129     }
130   else
131     {
132       index = log_streams_free;
133       l = xrealloc(log_streams.ptr[index], size);
134       log_streams.ptr[index] = l;
135       log_streams_free = l->levels;
136     }
137
138   /* Initialize the stream */
139   bzero(l, sizeof(*l));
140   l->levels = ~0U;
141   l->regnum = LS_SET_STRNUM(index);
142   clist_init(&l->substreams);
143   return log_ref_stream(l);
144 }
145
146 /* Remove a reference on a stream and close it if it was the last reference.
147  * Closing automatically unlinks all substreams and closes them if they are
148  * no longer referenced. Returns 1 if the stream has been really closed. */
149 int
150 log_close_stream(struct log_stream *ls)
151 {
152   ASSERT(ls);
153   ASSERT(ls->use_count);
154   if (--ls->use_count)
155     return 0;
156
157   /* Unlink all subtreams */
158   log_rm_substream(ls, NULL);
159
160   /* Close the stream and add it to the free-list */
161   if (ls->close)
162     ls->close(ls);
163   ls->levels = log_streams_free;
164   log_streams_free = LS_GET_STRNUM(ls->regnum);
165   ls->regnum = -1;
166   return 1;
167 }
168
169 void
170 log_set_format(struct log_stream *ls, uns mask, uns data)
171 {
172   ls->msgfmt = (ls->msgfmt & mask) | data;
173   CLIST_FOR_EACH(simp_node *, i, ls->substreams)
174     log_set_format(i->p, mask, data);
175 }