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