LICENSE.
[egate.git] / res+log / gw-log.c
1 /*
2  * Copyright (c) 1995, the EUROPAGATE consortium (see below).
3  *
4  * The EUROPAGATE consortium members are:
5  *
6  *    University College Dublin
7  *    Danmarks Teknologiske Videnscenter
8  *    An Chomhairle Leabharlanna
9  *    Consejo Superior de Investigaciones Cientificas
10  *
11  * Permission to use, copy, modify, distribute, and sell this software and
12  * its documentation, in whole or in part, for any purpose, is hereby granted,
13  * provided that:
14  *
15  * 1. This copyright and permission notice appear in all copies of the
16  * software and its documentation. Notices of copyright or attribution
17  * which appear at the beginning of any file must remain unchanged.
18  *
19  * 2. The names of EUROPAGATE or the project partners may not be used to
20  * endorse or promote products derived from this software without specific
21  * prior written permission.
22  *
23  * 3. Users of this software (implementors and gateway operators) agree to
24  * inform the EUROPAGATE consortium of their use of the software. This
25  * information will be used to evaluate the EUROPAGATE project and the
26  * software, and to plan further developments. The consortium may use
27  * the information in later publications.
28  * 
29  * 4. Users of this software agree to make their best efforts, when
30  * documenting their use of the software, to acknowledge the EUROPAGATE
31  * consortium, and the role played by the software in their work.
32  *
33  * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND,
34  * EXPRESS, IMPLIED, OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
35  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
36  * IN NO EVENT SHALL THE EUROPAGATE CONSORTIUM OR ITS MEMBERS BE LIABLE
37  * FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF
38  * ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
39  * OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND
40  * ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
41  * USE OR PERFORMANCE OF THIS SOFTWARE.
42  *
43  */
44 /*
45  * Implementation of logging facilities.
46  *
47  * Europagate, 1994-1995.
48  *
49  * $Log: gw-log.c,v $
50  * Revision 1.10  1995/05/16 09:40:48  adam
51  * LICENSE.
52  *
53  * Revision 1.9  1995/04/19  12:12:06  adam
54  * Resource system uses only one log debug level.
55  *
56  * Revision 1.8  1995/04/17  09:36:16  adam
57  * Minor changes.
58  *
59  * Revision 1.7  1995/04/10  13:20:25  adam
60  * Use gettimeofday(2) instead of time(2) to get log time in milliseconds.
61  *
62  * Revision 1.6  1995/03/28  08:01:51  adam
63  * Bug fix: GW_LOG_ERRNO.
64  *
65  * Revision 1.5  1995/03/27  12:51:10  adam
66  * New log level in use: GW_LOG_ERRNO.
67  *
68  * Revision 1.4  1995/02/23  08:32:22  adam
69  * Changed header.
70  *
71  * Revision 1.2  1995/02/17  17:06:56  adam
72  * Remove everything before '/' in app_name. Use compact date format.
73  *
74  * Revision 1.1.1.1  1995/02/09  17:27:12  adam
75  * Initial version of email gateway under CVS control.
76  *
77  * Initial:       Dec  7, 94 (Adam Dickmeiss)
78  */
79
80 #include <stdio.h>
81 #include <stdlib.h>
82 #include <string.h>
83 #include <stdarg.h>
84 #include <fcntl.h>
85 #include <sys/time.h>
86 #include <unistd.h>
87 #include <time.h>
88 #include <errno.h>
89
90 #include <gw-log.h>
91
92 static char *app_name = NULL;
93 static unsigned level = GW_LOG_DEFAULT;
94 static int session = 0;
95
96 struct file_mask {
97     unsigned mask;           /* level mask for this file entry */
98     int fd;                  /* file descriptor for this file */
99     char *fname;             /* name of file ("" if stdout) */
100     struct file_mask *next;  /* next file in chain */
101 };
102
103 struct file_mask *file_mask_list = NULL;
104
105 char *gw_strdup (const char *s)
106 {
107     char *n = malloc (strlen(s)+1);
108     if (n)
109         strcpy (n, s);
110     return n;
111 }
112
113 void gw_log_init (const char *app_name_a)
114 {
115     struct file_mask *list, *list1;
116     const char *cp;
117
118     if ((cp = strrchr (app_name_a, '/')))
119         app_name = gw_strdup (cp+1);
120     else
121         app_name = gw_strdup (app_name_a);
122     level = GW_LOG_DEFAULT;
123     session = 0;
124
125     /* clean up all output file masks... */
126     for (list = file_mask_list; list; list = list1)
127     {
128         if (list->fd > 2)                  /* avoid closing stdout/stderr */
129             close (list->fd);              
130         free (list->fname);
131         list1 = list->next;
132         free (list);
133     }
134     file_mask_list = NULL;
135 }
136
137 void gw_log_level (unsigned level_a)
138 {
139     level = level_a;
140 }
141
142 void gw_log_session (int session_a)
143 {
144     session = session_a;
145 }
146
147 int gw_log_file (unsigned level_a, const char *fname_a)
148 {
149     struct file_mask **listp, *new_file_mask;
150     listp = &file_mask_list;
151
152     /* go through file mask list and close files already associated */
153     /* on new level */
154     while (*listp)
155     {
156         if (!((*listp)->mask &= ~level_a)) /* any levels left on this one? */
157         {
158             if ((*listp)->fd > 2)          /* close if not stdout/stderr */
159                 close ((*listp)->fd);      
160             free ((*listp)->fname);
161             *listp = (*listp)->next;
162         }
163         listp = &(*listp)->next;
164     }
165     if (!fname_a)                          /* stderr? */
166         return 0;                          /* stderr doesn't appear on list */
167     new_file_mask = malloc (sizeof(*new_file_mask));
168     if (!new_file_mask)
169         return -1;
170     new_file_mask->mask = level_a;
171     new_file_mask->next = file_mask_list;
172     file_mask_list = new_file_mask;
173     if (!(file_mask_list->fname = gw_strdup (fname_a)))
174         return -1;
175     if (*fname_a == '\0')                  /* stdout? */
176         new_file_mask->fd = 1;            
177     else                                   /* no, open as usual */
178     {
179         new_file_mask->fd = open (fname_a, O_WRONLY|O_CREAT|O_APPEND, 0666);
180         if (new_file_mask->fd == -1)
181             return -1;
182     }
183     return 0;
184 }
185
186 int gw_log (unsigned level_a, const char *event_type, const char *format, ...)
187 {
188     static char emit_str[2048];
189     struct file_mask *list;
190     struct timeval tv;
191     struct timezone tz;
192     va_list ap;
193     unsigned e_level = level_a & level;
194     int count;
195     int err = 0;
196     struct tm tms;
197     char *cp;
198
199     if (!e_level)                          /* any effective level(s)? */      
200         return 0;
201
202     va_start (ap, format);
203     gettimeofday (&tv, &tz);
204
205     memcpy (&tms, localtime (&tv.tv_sec), sizeof(tms));
206     sprintf (emit_str, "%s %d %02d%02d%02d%02d%02d%02d%03d %d %s ",
207              app_name, session,
208              tms.tm_year, 1+tms.tm_mon, tms.tm_mday,
209              tms.tm_hour, tms.tm_min, tms.tm_sec,
210              (int) (tv.tv_usec/1000),
211              e_level, event_type);
212     if ((cp = strchr (emit_str, '\n')))    /* remove \n from ctime-str */
213         *cp = ' ';
214     count = strlen (emit_str);
215     vsprintf (emit_str+count, format, ap);
216     if (level_a & GW_LOG_ERRNO)
217     {
218         strcat (emit_str, ": ");
219         strcat (emit_str, strerror (errno));
220     }
221     strcat (emit_str, "\n");
222     count = strlen (emit_str);
223
224     /* go through file mask list... */
225     for (list = file_mask_list; list; list = list->next)
226         if (list->mask & e_level)          /* output this one? */
227         {
228             e_level &= ~list->mask;        /* remove from effective level */
229             if (write (list->fd, emit_str, count) != count)
230                 err = 1;
231         }
232     if (e_level)                           /* bits left on effective level? */
233         write (2, emit_str, count);
234     va_end (ap);
235     return err;
236 }