New log level in use: GW_LOG_ERRNO.
[egate.git] / res+log / gw-log.c
1 /*
2  * Implementation of logging facilities.
3  *
4  * Europagate, 1994-1995.
5  *
6  * $Log: gw-log.c,v $
7  * Revision 1.5  1995/03/27 12:51:10  adam
8  * New log level in use: GW_LOG_ERRNO.
9  *
10  * Revision 1.4  1995/02/23  08:32:22  adam
11  * Changed header.
12  *
13  * Revision 1.2  1995/02/17  17:06:56  adam
14  * Remove everything before '/' in app_name. Use compact date format.
15  *
16  * Revision 1.1.1.1  1995/02/09  17:27:12  adam
17  * Initial version of email gateway under CVS control.
18  *
19  * Initial:       Dec  7, 94 (Adam Dickmeiss)
20  */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <time.h>
29 #include <errno.h>
30
31 #include <gw-log.h>
32
33 static char *app_name = NULL;
34 static unsigned level = GW_LOG_DEFAULT;
35 static int session = 0;
36
37 struct file_mask {
38     unsigned mask;           /* level mask for this file entry */
39     int fd;                  /* file descriptor for this file */
40     char *fname;             /* name of file ("" if stdout) */
41     struct file_mask *next;  /* next file in chain */
42 };
43
44 struct file_mask *file_mask_list = NULL;
45
46 char *gw_strdup (const char *s)
47 {
48     char *n = malloc (strlen(s)+1);
49     if (n)
50         strcpy (n, s);
51     return n;
52 }
53
54 void gw_log_init (const char *app_name_a)
55 {
56     struct file_mask *list, *list1;
57     const char *cp;
58
59     if ((cp = strrchr (app_name_a, '/')))
60         app_name = gw_strdup (cp+1);
61     else
62         app_name = gw_strdup (app_name_a);
63     level = GW_LOG_DEFAULT;
64     session = 0;
65
66     /* clean up all output file masks... */
67     for (list = file_mask_list; list; list = list1)
68     {
69         if (list->fd > 2)                  /* avoid closing stdout/stderr */
70             close (list->fd);              
71         free (list->fname);
72         list1 = list->next;
73         free (list);
74     }
75     file_mask_list = NULL;
76 }
77
78 void gw_log_level (unsigned level_a)
79 {
80     level = level_a;
81 }
82
83 void gw_log_session (int session_a)
84 {
85     session = session_a;
86 }
87
88 int gw_log_file (unsigned level_a, const char *fname_a)
89 {
90     struct file_mask **listp, *new_file_mask;
91     listp = &file_mask_list;
92
93     /* go through file mask list and close files already associated */
94     /* on new level */
95     while (*listp)
96     {
97         if (!((*listp)->mask &= ~level_a)) /* any levels left on this one? */
98         {
99             if ((*listp)->fd > 2)          /* close if not stdout/stderr */
100                 close ((*listp)->fd);      
101             free ((*listp)->fname);
102             *listp = (*listp)->next;
103         }
104         listp = &(*listp)->next;
105     }
106     if (!fname_a)                          /* stderr? */
107         return 0;                          /* stderr doesn't appear on list */
108     new_file_mask = malloc (sizeof(*new_file_mask));
109     if (!new_file_mask)
110         return -1;
111     new_file_mask->mask = level_a;
112     new_file_mask->next = file_mask_list;
113     file_mask_list = new_file_mask;
114     if (!(file_mask_list->fname = gw_strdup (fname_a)))
115         return -1;
116     if (*fname_a == '\0')                  /* stdout? */
117         new_file_mask->fd = 1;            
118     else                                   /* no, open as usual */
119     {
120         new_file_mask->fd = open (fname_a, O_WRONLY|O_CREAT|O_APPEND, 0666);
121         if (new_file_mask->fd == -1)
122             return -1;
123     }
124     return 0;
125 }
126
127 int gw_log (unsigned level_a, const char *event_type, const char *format, ...)
128 {
129     static char emit_str[2048];
130     struct file_mask *list;
131     va_list ap;
132     unsigned e_level = level_a & level;
133     int count;
134     int err = 0;
135     time_t time_now;
136     struct tm tms;
137     char *cp;
138
139     if (!e_level)                          /* any effective level(s)? */      
140         return 0;
141
142     va_start (ap, format);
143     time (&time_now);
144     memcpy (&tms, localtime (&time_now), sizeof(tms));
145     sprintf (emit_str, "%s %d %02d%02d%02d%02d%02d%02d %d %s ",
146              app_name, session,
147              tms.tm_year, tms.tm_mon, tms.tm_mday,
148              tms.tm_hour, tms.tm_min, tms.tm_sec,
149              e_level, event_type);
150     if ((cp = strchr (emit_str, '\n')))    /* remove \n from ctime-str */
151         *cp = ' ';
152     count = strlen (emit_str);
153     vsprintf (emit_str+count, format, ap);
154     if (e_level & GW_LOG_ERRNO)
155     {
156         strcat (emit_str, ": ");
157         strcat (emit_str, strerror (errno));
158     }
159     strcat (emit_str, "\n");
160     count = strlen (emit_str);
161
162     /* go through file mask list... */
163     for (list = file_mask_list; list; list = list->next)
164         if (list->mask & e_level)          /* output this one? */
165         {
166             e_level &= ~list->mask;        /* remove from effective level */
167             if (write (list->fd, emit_str, count) != count)
168                 err = 1;
169         }
170     if (e_level)                           /* bits left on effective level? */
171         write (2, emit_str, count);
172     va_end (ap);
173     return err;
174 }