1 /* Lepton EDA library
2 * Copyright (C) 1998-2010 Ales Hvezda
3 * Copyright (C) 1998-2016 gEDA Contributors
4 * Copyright (C) 2017-2021 Lepton EDA Contributors
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <sys/stat.h>
24 #ifdef HAVE_STDLIB_H
25 #include <stdlib.h>
26 #endif
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30 #ifdef HAVE_FCNTL_H
31 #include <fcntl.h>
32 #endif
33 #ifdef HAVE_ERRNO_H
34 #include <errno.h>
35 #endif
36 #ifdef HAVE_STRING_H
37 #include <string.h>
38 #endif
39
40 #include "liblepton_priv.h"
41
42 #include <time.h>
43
44 /*! Default setting for log update callback function. */
45 void (*x_log_update_func)(const gchar*, GLogLevelFlags, const gchar*) = NULL;
46
47 /*! Default setting for log enable. */
48 int do_logging = TRUE;
49
50 #define CATCH_LOG_LEVELS (G_LOG_LEVEL_MASK ^ \
51 (G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_INFO))
52 #define PRINT_LOG_LEVELS (CATCH_LOG_LEVELS ^ \
53 (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE))
54
55 #define LOG_OPEN_ATTEMPTS 5
56
57 static void s_log_handler (const gchar *log_domain,
58 GLogLevelFlags log_level,
59 const gchar *message,
60 gpointer user_data);
61
62 static int logfile_fd = -1;
63
64 static guint log_handler_id;
65
66 /*! \brief Initialize libgeda logging feature.
67 * \par Function Description
68 * This function opens the file <B>filename</B> to log to and registers the
69 * handler to redirect log message to this file.
70 *
71 * \param [in] prefix Character string with file name prefix to log to.
72 */
s_log_init(const gchar * prefix)73 void s_log_init (const gchar *prefix)
74 {
75 /* FIXME we assume that the prefix is in the filesystem encoding. */
76
77 time_t nowt;
78 struct tm *nowtm;
79 gchar *full_prefix = NULL;
80 size_t full_prefix_len = 0;
81 gchar *dir_path = NULL;
82 gchar *filename = NULL;
83 int s, i;
84 int last_exist_logn = 0;
85 GDir *logdir = NULL;
86
87 if (logfile_fd != -1) {
88 g_critical ("s_log_init: Log already initialised.\n");
89 return;
90 }
91 if (do_logging == FALSE) {
92 return;
93 }
94
95 time (&nowt);
96 nowtm = gmtime (&nowt);
97
98 /* create "real" prefix -- this has the form "<prefix>-<date>-" */
99 full_prefix = g_strdup_printf ("%s-%04i%02i%02i-", prefix,
100 nowtm->tm_year + 1900, nowtm->tm_mon + 1,
101 nowtm->tm_mday);
102 full_prefix_len = strlen (full_prefix);
103
104 /* Find/create the directory where we're going to put the logs.
105 * FIXME should this be configured somehow?
106 *
107 * Then run through it finding the "biggest" existing filename with
108 * a matching prefix & date. */
109 dir_path = g_build_filename (eda_get_user_cache_dir(), "logs", NULL);
110 /* Try to create the directory. */
111 s = g_mkdir_with_parents (dir_path, 0777/*octal*/);
112 if (s != 0) {
113 /* It's okay to use the logging functions from here, because
114 * there's already a default handler. */
115 g_warning ("Could not create log directory %1$s: %2$s\n",
116 dir_path, strerror (errno));
117 g_free (dir_path);
118 g_free (full_prefix);
119 return;
120 }
121
122 logdir = g_dir_open (dir_path, 0, NULL);
123 while (TRUE) {
124 const gchar *file = g_dir_read_name (logdir);
125 int n;
126 if (file == NULL) break;
127 if (strncmp (full_prefix, file, full_prefix_len)) continue;
128
129 s = sscanf (file + full_prefix_len, "%i", &n);
130 if (s != 1) continue;
131
132 if (n > last_exist_logn) last_exist_logn = n;
133 }
134 g_dir_close (logdir);
135
136 /* Now try and create a new file. When we fail, increment the number. */
137 i = 0;
138 while (logfile_fd == -1 && (LOG_OPEN_ATTEMPTS > i++)) {
139 filename = g_strdup_printf ("%s%s%s%i.log", dir_path,
140 G_DIR_SEPARATOR_S, full_prefix,
141 ++last_exist_logn);
142 logfile_fd = open (filename, O_RDWR|O_CREAT|O_EXCL, 0600);
143
144 if (logfile_fd == -1 && (errno != EEXIST)) break;
145 }
146
147 if (logfile_fd != -1) {
148
149 /* install the log handler */
150 log_handler_id = g_log_set_handler (NULL,
151 (GLogLevelFlags) CATCH_LOG_LEVELS,
152 s_log_handler,
153 NULL);
154
155 } else {
156 /* It's okay to use the logging functions from here, because
157 * there's already a default handler. */
158 if (errno == EEXIST) {
159 g_warning ("Could not create unique log filename in %1$s\n",
160 dir_path);
161 } else {
162 g_warning ("Could not create log file in %1$s: %2$s\n",
163 dir_path, strerror (errno));
164 }
165 }
166
167 g_free (filename);
168 g_free (dir_path);
169 g_free (full_prefix);
170 }
171
172 /*! \brief Terminates the logging of messages.
173 * \par Function Description
174 * This function deregisters the handler for redirection to the log file
175 * and closes it.
176 */
s_log_close(void)177 void s_log_close (void)
178 {
179 do_logging = FALSE; /* subsequent messages are lost after the close */
180
181 if (logfile_fd == -1)
182 {
183 return;
184 }
185
186 /* remove the handler */
187 g_log_remove_handler (NULL, log_handler_id);
188
189 /* close the file */
190 if (logfile_fd != -1) {
191 close (logfile_fd);
192 logfile_fd = -1;
193 }
194
195 }
196
197 /*! \brief Reads the current log file and returns its contents.
198 * \par Function Description
199 * This function reads the current log file and returns its contents.
200 *
201 * \return Character string with current log's contents.
202 *
203 */
s_log_read(void)204 gchar *s_log_read (void)
205 {
206 gboolean tmp;
207 #define BUFSIZE 200
208 gchar buf[BUFSIZE];
209 GString *contents;
210 gint len;
211
212 if (logfile_fd == -1) {
213 return NULL;
214 }
215
216 tmp = do_logging;
217 do_logging = FALSE;
218
219 /* rewind the file */
220 lseek(logfile_fd, 0, SEEK_SET);
221
222 /* read its contents and build a string */
223 contents = g_string_new ("");
224 while ((len = read (logfile_fd, &buf, BUFSIZE)) != 0) {
225 contents = g_string_append_len (contents, buf, len);
226 }
227
228 do_logging = tmp;
229
230 return g_string_free (contents, FALSE);
231 }
232
233 /*! \brief Write a message to the current log file.
234 * \par Function Description
235 * Writes <B>message</B> to the current log file whose file descriptor
236 * is <B>logfile_fd</B>.
237 *
238 * It also sends <B>message</B> to the optional function <B>x_log_update</B>
239 * for further use.
240 *
241 * \param [in] log_domain (unused).
242 * \param [in] log_level (unused).
243 * \param [in] message Character string containing message to
244 * write to log.
245 * \param [in] user_data (unused).
246 *
247 */
s_log_handler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)248 static void s_log_handler (const gchar *log_domain,
249 GLogLevelFlags log_level,
250 const gchar *message,
251 gpointer user_data)
252 {
253 if (do_logging == FALSE) {
254 return;
255 }
256 g_return_if_fail (logfile_fd != -1);
257
258 size_t len = strlen (message);
259 int status = 0;
260 if (status >= 0)
261 status = write (logfile_fd, message, len);
262 if (status >= 0)
263 status = write (logfile_fd, "\n", 1);
264 if (status == -1) {
265 fprintf(stderr, "Could not write message to log file\n");
266 }
267 if ((status == -1) || (log_level & PRINT_LOG_LEVELS)) {
268 /* If messages are serious or writing to file failed, call the
269 * default handler to write to the console. */
270 g_log_default_handler (log_domain, log_level, message, NULL);
271 }
272
273 if (x_log_update_func) {
274 (*x_log_update_func) (log_domain, log_level, message);
275 }
276
277 }
278
279
280 /* Helper functions to construct GLogLevelFlags values in
281 Scheme. We don't just list their current values in Scheme code
282 since GLogLevelFlag is an opaque Glib enum and the flag values
283 may change in future. */
284
285 GLogLevelFlags
lepton_log_flag_fatal()286 lepton_log_flag_fatal ()
287 {
288 return G_LOG_FLAG_FATAL;
289 }
290
291 GLogLevelFlags
lepton_log_level_error()292 lepton_log_level_error ()
293 {
294 return G_LOG_LEVEL_ERROR;
295 }
296
297 GLogLevelFlags
lepton_log_level_critical()298 lepton_log_level_critical ()
299 {
300 return G_LOG_LEVEL_CRITICAL;
301 }
302
303 GLogLevelFlags
lepton_log_level_warning()304 lepton_log_level_warning ()
305 {
306 return G_LOG_LEVEL_WARNING;
307 }
308
309 GLogLevelFlags
lepton_log_level_message()310 lepton_log_level_message ()
311 {
312 return G_LOG_LEVEL_MESSAGE;
313 }
314
315 GLogLevelFlags
lepton_log_level_info()316 lepton_log_level_info ()
317 {
318 return G_LOG_LEVEL_INFO;
319 }
320
321 GLogLevelFlags
lepton_log_level_debug()322 lepton_log_level_debug ()
323 {
324 return G_LOG_LEVEL_DEBUG;
325 }
326