From 49876695d2d6dc1dff5355d9f958898737e1d6da Mon Sep 17 00:00:00 2001 From: Bradford Hovinen Date: Sun, 22 Apr 2001 01:05:26 +0000 Subject: [PATCH] Added InputBuffer, removed get_line (struct _Slave): (struct 2001-04-20 Bradford Hovinen * config-log.c: Added InputBuffer, removed get_line (struct _Slave): (struct _ConfigLogPrivate ): Replaced GIOChannel * and FILE * with InputBuffer *'s (input_buffer_new): Implement. Constructs a new input buffer (input_buffer_destroy): Implement. Destroys an input buffer and closes the file descriptor (input_buffer_cycle): Implement (borrowed from GDict). Reads additional data from the input file (input_buffer_read_line): Implement (borrowed from GDict). Reads a line from the input file and returns a pointer to it (input_buffer_write): Implement. Write the indicated string out to the channel 2001-04-15 Bradford Hovinen * config-log.c (config_log_set_arg): Create semaphore for auto-reloading (do_load): Removed locking code (load_log_entry): Support prepending log entries to the beginning of the log; free entry->date if parsing was unsuccessful (config_log_reset_filenames): Unlink socket filename if not owner (config_log_reset_filenames): Rebind socket when filename is reset (connect_socket): Implement. Creates or connects to the socket associated with the config log (check_socket_filename): Implement. Checks to see if the filename associated with the socket is in use. (bind_socket): Implement. Binds the socket to the filename. (socket_connect_cb): Implement. Callback issued when a master gets a new connection. (socket_data_cb): Implement. Callback issued when a slave gets data from the master. (slave_new): Implement. Creates a new slave structure (slave_destroy): Implement. Destroys a slave structure (slave_data_cb): Implement. Callback issued when data comes in from a slave (slave_broadcast_data): Implement. Broadcast the first log entry to all the slaves except the given one (disconnect_socket): Implement. Disconnects the socket (config_log_open): Call connect_socket (do_unload): Don't dump the log unless currently the socket owner (config_log_write_entry): Broadcast data to slaves or write data to socket, depending on whether currently the socket owner 2001-04-14 Bradford Hovinen * config-log.c (do_unload): Add a parameter write_log to tell whether to write out the log when unloading (config_log_finalize): Implement * config-log.[ch]: Put all data fields in ConfigLogPrivate structure * gui-backend-list.c (gui_backend_list_launch_current): Rewrite to use popen --- archiver/ChangeLog | 55 +++ archiver/config-log.c | 990 ++++++++++++++++++++++++++++++-------- archiver/config-log.h | 49 +- archiver/default-user.xml | 6 +- archiver/main.c | 3 + 5 files changed, 872 insertions(+), 231 deletions(-) diff --git a/archiver/ChangeLog b/archiver/ChangeLog index effff0617..9ff6b22fa 100644 --- a/archiver/ChangeLog +++ b/archiver/ChangeLog @@ -1,3 +1,58 @@ +2001-04-20 Bradford Hovinen + + * config-log.c: Added InputBuffer, removed get_line + (struct _Slave): + (struct _ConfigLogPrivate ): Replaced GIOChannel * and FILE * with + InputBuffer *'s + (input_buffer_new): Implement. Constructs a new input buffer + (input_buffer_destroy): Implement. Destroys an input buffer and + closes the file descriptor + (input_buffer_cycle): Implement (borrowed from GDict). Reads + additional data from the input file + (input_buffer_read_line): Implement (borrowed from GDict). Reads a + line from the input file and returns a pointer to it + (input_buffer_write): Implement. Write the indicated string out to + the channel + +2001-04-15 Bradford Hovinen + + * config-log.c (config_log_set_arg): Create semaphore for + auto-reloading + (do_load): Removed locking code + (load_log_entry): Support prepending log entries to the beginning + of the log; free entry->date if parsing was unsuccessful + (config_log_reset_filenames): Unlink socket filename if not owner + (config_log_reset_filenames): Rebind socket when filename is reset + (connect_socket): Implement. Creates or connects to the socket + associated with the config log + (check_socket_filename): Implement. Checks to see if the filename + associated with the socket is in use. + (bind_socket): Implement. Binds the socket to the filename. + (socket_connect_cb): Implement. Callback issued when a master gets + a new connection. + (socket_data_cb): Implement. Callback issued when a slave gets + data from the master. + (slave_new): Implement. Creates a new slave structure + (slave_destroy): Implement. Destroys a slave structure + (slave_data_cb): Implement. Callback issued when data comes in + from a slave + (slave_broadcast_data): Implement. Broadcast the first log entry + to all the slaves except the given one + (disconnect_socket): Implement. Disconnects the socket + (config_log_open): Call connect_socket + (do_unload): Don't dump the log unless currently the socket owner + (config_log_write_entry): Broadcast data to slaves or write data + to socket, depending on whether currently the socket owner + +2001-04-14 Bradford Hovinen + + * config-log.c (do_unload): Add a parameter write_log to tell + whether to write out the log when unloading + (config_log_finalize): Implement + + * config-log.[ch]: Put all data fields in ConfigLogPrivate + structure + 2001-04-11 Bradford Hovinen * location.c (run_backend_proc): Make sure to search location of diff --git a/archiver/config-log.c b/archiver/config-log.c index 2d99d9781..70aa08d0a 100644 --- a/archiver/config-log.c +++ b/archiver/config-log.c @@ -26,13 +26,21 @@ #endif #include +#include #include #include +#include +#include +#include #include "config-log.h" #include "location.h" #include "util.h" +/* Maximum amount to read from an I/O channel at one time */ + +#define READ_BUF_LEN 4096 + static GtkObjectClass *parent_class; enum { @@ -41,6 +49,8 @@ enum { }; typedef struct _ConfigLogEntry ConfigLogEntry; +typedef struct _Slave Slave; +typedef struct _IOBuffer IOBuffer; struct _ConfigLogEntry { @@ -49,50 +59,124 @@ struct _ConfigLogEntry gchar *backend_id; }; -static void config_log_init (ConfigLog *config_log); -static void config_log_class_init (ConfigLogClass *klass); +struct _IOBuffer +{ + GIOChannel *channel; + char buffer[READ_BUF_LEN + 1]; + gchar *read_ptr; + gchar *write_ptr; + gboolean from_socket; + gboolean read_cycle_done; + gboolean closed; +}; -static void config_log_set_arg (GtkObject *object, - GtkArg *arg, - guint arg_id); +struct _Slave +{ + ConfigLog *config_log; + IOBuffer *buffer; + guint source_id; +}; -static void config_log_get_arg (GtkObject *object, - GtkArg *arg, - guint arg_id); +struct _ConfigLogPrivate +{ + Location *location; -static void config_log_destroy (GtkObject *object); + IOBuffer *file_buffer; + char *filename; -static GList *find_config_log_entry_id (ConfigLog *config_log, - GList *start, - gint id); -static GList *find_config_log_entry_date (ConfigLog *config_log, - GList *start, - struct tm *date); -static GList *find_config_log_entry_backend (ConfigLog *config_log, - GList *start, - gchar *backend_id); + GList *log_data; + GList *first_old; -static GList *load_next_log_entry (ConfigLog *config_log, - GList *last); + char *socket_filename; + IOBuffer *socket_buffer; + gboolean socket_owner; + guint input_id; -static gchar *get_line (FILE *file); -static gboolean parse_line (char *buffer, - int *id, - struct tm *time, - char **backend_id); -static gboolean time_geq (struct tm *time1, - struct tm *time2); + GList *slaves; +}; -static gboolean do_load (ConfigLog *config_log); -static void do_unload (ConfigLog *config_log); +static void config_log_init (ConfigLog *config_log); +static void config_log_class_init (ConfigLogClass *klass); -static gint get_next_id (ConfigLog *config_log); -static struct tm *get_current_date (void); -static void write_log (FILE *output, - ConfigLogEntry *entry); -static void dump_log (ConfigLog *config_log); +static void config_log_set_arg (GtkObject *object, + GtkArg *arg, + guint arg_id); -static void config_log_entry_destroy (ConfigLogEntry *entry); +static void config_log_get_arg (GtkObject *object, + GtkArg *arg, + guint arg_id); + +static void config_log_destroy (GtkObject *object); +static void config_log_finalize (GtkObject *object); + +static GList *find_config_log_entry_id (ConfigLog *config_log, + GList *start, + gint id); +static GList *find_config_log_entry_date (ConfigLog *config_log, + GList *start, + struct tm *date); +static GList *find_config_log_entry_backend (ConfigLog *config_log, + GList *start, + gchar *backend_id); + +static GList *load_log_entry (ConfigLog *config_log, + gboolean from_socket, + IOBuffer *input, + GList *last); + +static gboolean parse_line (char *buffer, + int *id, + struct tm *time, + char **backend_id); +static gboolean time_geq (struct tm *time1, + struct tm *time2); + +static gboolean do_load (ConfigLog *config_log); +static void do_unload (ConfigLog *config_log, + gboolean write_log); + +static gint get_next_id (ConfigLog *config_log); +static struct tm *get_current_date (void); +static void write_log (IOBuffer *output, + ConfigLogEntry *entry); +static void dump_log (ConfigLog *config_log); + +static gboolean connect_socket (ConfigLog *config_log); +static gboolean check_socket_filename (ConfigLog *config_log); +static gboolean bind_socket (ConfigLog *config_log, + int fd, + gboolean do_connect); +static void disconnect_socket (ConfigLog *config_log); +static gboolean socket_connect_cb (GIOChannel *channel, + GIOCondition condition, + ConfigLog *config_log); +static gboolean socket_data_cb (GIOChannel *channel, + GIOCondition condition, + ConfigLog *config_log); + +static void config_log_entry_destroy (ConfigLogEntry *entry); + +static Slave *slave_new (ConfigLog *config_log, + int fd); +static void slave_destroy (Slave *slave); + +static gboolean slave_data_cb (GIOChannel *channel, + GIOCondition condition, + Slave *slave); +static void slave_apprise_data (Slave *slave); +static void slave_broadcast_data (Slave *slave, + ConfigLog *config_log); + +static IOBuffer *io_buffer_new (GIOChannel *channel, + gboolean from_socket); +static void io_buffer_destroy (IOBuffer *buffer); +static int io_buffer_cycle (IOBuffer *buffer); +static gchar *io_buffer_read_line (IOBuffer *buffer); +static void io_buffer_write (IOBuffer *buffer, + gchar *str); +static void io_buffer_rewind (IOBuffer *buffer); +static void io_buffer_dump (IOBuffer *source, + IOBuffer *dest); guint config_log_get_type (void) @@ -121,7 +205,7 @@ config_log_get_type (void) static void config_log_init (ConfigLog *config_log) { - config_log->location = NULL; + config_log->p = g_new0 (ConfigLogPrivate, 1); } static void @@ -132,6 +216,7 @@ config_log_class_init (ConfigLogClass *klass) object_class = GTK_OBJECT_CLASS (klass); object_class->destroy = config_log_destroy; + object_class->finalize = config_log_finalize; object_class->set_arg = config_log_set_arg; object_class->get_arg = config_log_get_arg; @@ -159,8 +244,9 @@ config_log_set_arg (GtkObject *object, GtkArg *arg, guint arg_id) g_return_if_fail (GTK_VALUE_POINTER (*arg) != NULL); g_return_if_fail (IS_LOCATION (GTK_VALUE_POINTER (*arg))); - config_log->location = GTK_VALUE_POINTER (*arg); + config_log->p->location = GTK_VALUE_POINTER (*arg); break; + default: break; } @@ -179,7 +265,7 @@ config_log_get_arg (GtkObject *object, GtkArg *arg, guint arg_id) switch (arg_id) { case ARG_LOCATION: - GTK_VALUE_POINTER (*arg) = config_log->location; + GTK_VALUE_POINTER (*arg) = config_log->p->location; break; default: arg->type = GTK_TYPE_INVALID; @@ -187,24 +273,6 @@ config_log_get_arg (GtkObject *object, GtkArg *arg, guint arg_id) } } -/* Loads a configuration log. Creates it if it has not been created - * already - */ - -GtkObject * -config_log_open (Location *location) -{ - GtkObject *object; - - object = gtk_object_new (config_log_get_type (), - "location", location, - NULL); - - do_load (CONFIG_LOG (object)); - - return object; -} - /* Destroys a configuration log data structure and frees all memory * associated with it, dumping the existing log out to disk */ @@ -219,11 +287,48 @@ config_log_destroy (GtkObject *object) config_log = CONFIG_LOG (object); - do_unload (config_log); + disconnect_socket (config_log); + do_unload (config_log, TRUE); GTK_OBJECT_CLASS (parent_class)->destroy (GTK_OBJECT (config_log)); } +/* Deallocates memory associated with a config log structure */ + +static void +config_log_finalize (GtkObject *object) +{ + ConfigLog *config_log; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_CONFIG_LOG (object)); + + config_log = CONFIG_LOG (object); + + g_free (config_log->p); + + GTK_OBJECT_CLASS (parent_class)->finalize (GTK_OBJECT (config_log)); +} + +/* Loads a configuration log. Creates it if it has not been created + * already + */ + +GtkObject * +config_log_open (Location *location) +{ + GtkObject *object; + + object = gtk_object_new (config_log_get_type (), + "location", location, + NULL); + + do_load (CONFIG_LOG (object)); + connect_socket (CONFIG_LOG (object)); + + return object; +} + /** * config_log_delete: * @config_log: @@ -238,13 +343,13 @@ config_log_delete (ConfigLog *config_log) g_return_if_fail (config_log != NULL); g_return_if_fail (IS_CONFIG_LOG (config_log)); - if (config_log->file != NULL) { - fclose (config_log->file); - config_log->file = NULL; + if (config_log->p->file_buffer != NULL) { + io_buffer_destroy (config_log->p->file_buffer); + config_log->p->file_buffer = NULL; } - if (config_log->filename != NULL) - unlink (config_log->filename); + if (config_log->p->filename != NULL) + unlink (config_log->p->filename); gtk_object_destroy (GTK_OBJECT (config_log)); } @@ -265,15 +370,16 @@ config_log_get_rollback_id_for_date (ConfigLog *config_log, g_return_val_if_fail (IS_CONFIG_LOG (config_log), -1); g_return_val_if_fail (backend_id != NULL, -1); - if (config_log->log_data == NULL) - config_log->log_data = - load_next_log_entry (config_log, NULL); + if (config_log->p->log_data == NULL) + config_log->p->log_data = + load_log_entry (config_log, FALSE, + config_log->p->file_buffer, NULL); if (date == NULL) - node = config_log->log_data; + node = config_log->p->log_data; else node = find_config_log_entry_date (config_log, - config_log->log_data, + config_log->p->log_data, date); node = find_config_log_entry_backend (config_log, node, backend_id); @@ -298,10 +404,11 @@ config_log_get_rollback_id_by_steps (ConfigLog *config_log, g_return_val_if_fail (IS_CONFIG_LOG (config_log), -1); g_return_val_if_fail (backend_id != NULL, -1); - node = config_log->log_data; + node = config_log->p->log_data; if (node == NULL) - node = load_next_log_entry (config_log, node); + node = load_log_entry (config_log, FALSE, + config_log->p->file_buffer, node); while (node != NULL && steps-- > 0) { node = find_config_log_entry_backend @@ -309,7 +416,9 @@ config_log_get_rollback_id_by_steps (ConfigLog *config_log, if (steps > 0) { if (node->next == NULL) - node = load_next_log_entry (config_log, node); + node = load_log_entry + (config_log, FALSE, + config_log->p->file_buffer, node); else node = node->next; } @@ -332,12 +441,13 @@ config_log_get_backend_id_for_id (ConfigLog *config_log, gint id) g_return_val_if_fail (IS_CONFIG_LOG (config_log), NULL); g_return_val_if_fail (id >= 0, NULL); - if (config_log->log_data == NULL) - config_log->log_data = - load_next_log_entry (config_log, NULL); + if (config_log->p->log_data == NULL) + config_log->p->log_data = + load_log_entry (config_log, FALSE, + config_log->p->file_buffer, NULL); node = find_config_log_entry_id (config_log, - config_log->log_data, id); + config_log->p->log_data, id); if (!node) return NULL; @@ -356,12 +466,13 @@ config_log_get_date_for_id (ConfigLog *config_log, gint id) g_return_val_if_fail (IS_CONFIG_LOG (config_log), NULL); g_return_val_if_fail (id >= 0, NULL); - if (config_log->log_data == NULL) - config_log->log_data = - load_next_log_entry (config_log, NULL); + if (config_log->p->log_data == NULL) + config_log->p->log_data = + load_log_entry (config_log, FALSE, + config_log->p->file_buffer, NULL); node = find_config_log_entry_id (config_log, - config_log->log_data, id); + config_log->p->log_data, id); if (!node) return NULL; @@ -383,7 +494,13 @@ config_log_write_entry (ConfigLog *config_log, gchar *backend_id) entry->date = get_current_date (); entry->backend_id = g_strdup (backend_id); - config_log->log_data = g_list_prepend (config_log->log_data, entry); + config_log->p->log_data = + g_list_prepend (config_log->p->log_data, entry); + + if (config_log->p->socket_owner) + slave_broadcast_data (NULL, config_log); + else + write_log (config_log->p->socket_buffer, entry); return entry->id; } @@ -409,14 +526,16 @@ config_log_iterate (ConfigLog *config_log, ConfigLogIteratorCB callback, g_return_if_fail (IS_CONFIG_LOG (config_log)); g_return_if_fail (callback != NULL); - node = config_log->log_data; + node = config_log->p->log_data; while (node != NULL) { entry = (ConfigLogEntry *) node->data; if (callback (config_log, entry->id, entry->backend_id, entry->date, node->data)) break; if (node->next == NULL) - node = load_next_log_entry (config_log, node); + node = load_log_entry (config_log, FALSE, + config_log->p->file_buffer, + node); else node = node->next; } @@ -435,20 +554,52 @@ config_log_reset_filenames (ConfigLog *config_log) g_return_if_fail (config_log != NULL); g_return_if_fail (IS_CONFIG_LOG (config_log)); - if (config_log->filename != NULL) - g_free (config_log->filename); + if (config_log->p->filename != NULL) + g_free (config_log->p->filename); - config_log->filename = + config_log->p->filename = g_concat_dir_and_file (location_get_path - (config_log->location), + (config_log->p->location), "config.log"); - if (config_log->lock_filename != NULL) - g_free (config_log->lock_filename); - config_log->lock_filename = + if (config_log->p->socket_filename != NULL) { + if (config_log->p->socket_owner) + unlink (config_log->p->socket_filename); + + g_free (config_log->p->socket_filename); + } + + config_log->p->socket_filename = g_concat_dir_and_file (location_get_path - (config_log->location), - "config.log.lock"); + (config_log->p->location), + "config.log.socket"); + + if (config_log->p->socket_owner) { + if (check_socket_filename (config_log)) + bind_socket (config_log, + g_io_channel_unix_get_fd + (config_log->p->socket_buffer->channel), + FALSE); + else + g_warning ("Could not rebind socket after " + "reseting filenames"); + } +} + +/** + * config_log_reload + * @config_log: + * + * Reloads the entire config log, throwing out any newly created entries + **/ + +void +config_log_reload (ConfigLog *config_log) +{ + g_return_if_fail (config_log != NULL); + g_return_if_fail (IS_CONFIG_LOG (config_log)); + + do_load (config_log); } /* Find the config log entry with the id given, starting at the given @@ -478,7 +629,8 @@ find_config_log_entry_id (ConfigLog *config_log, GList *start, gint id) } while (1) { - start = load_next_log_entry (config_log, last); + start = load_log_entry (config_log, FALSE, + config_log->p->file_buffer, last); if (start == NULL) return NULL; entry = (ConfigLogEntry *) start->data; if (entry->id == id) @@ -516,7 +668,8 @@ find_config_log_entry_date (ConfigLog *config_log, GList *start, } while (1) { - start = load_next_log_entry (config_log, last); + start = load_log_entry (config_log, FALSE, + config_log->p->file_buffer, last); if (start == NULL) return NULL; entry = (ConfigLogEntry *) start->data; if (time_geq (date, entry->date)) @@ -552,7 +705,8 @@ find_config_log_entry_backend (ConfigLog *config_log, GList *start, } while (1) { - start = load_next_log_entry (config_log, last); + start = load_log_entry (config_log, FALSE, + config_log->p->file_buffer, last); if (start == NULL) return NULL; entry = (ConfigLogEntry *) start->data; if (!strcmp (entry->backend_id, backend_id)) @@ -562,8 +716,12 @@ find_config_log_entry_backend (ConfigLog *config_log, GList *start, return NULL; } +/* Loads a log entry from the given file and attaches it to the beginning or + * the end of the config log */ + static GList * -load_next_log_entry (ConfigLog *config_log, GList *last) +load_log_entry (ConfigLog *config_log, gboolean from_socket, + IOBuffer *input, GList *last) { gchar *buffer, *backend_id; ConfigLogEntry *entry; @@ -572,9 +730,11 @@ load_next_log_entry (ConfigLog *config_log, GList *last) g_return_val_if_fail (config_log != NULL, NULL); g_return_val_if_fail (IS_CONFIG_LOG (config_log), NULL); - if (!config_log->file || feof (config_log->file)) return NULL; + if (input == NULL || input->closed) return NULL; - buffer = get_line (config_log->file); + buffer = io_buffer_read_line (input); + + if (buffer == NULL) return NULL; entry = g_new0 (ConfigLogEntry, 1); entry->date = g_new0 (struct tm, 1); @@ -583,58 +743,30 @@ load_next_log_entry (ConfigLog *config_log, GList *last) if (success) { entry->backend_id = g_strdup (backend_id); - last = g_list_append (last, entry); - if (!config_log->log_data) { - config_log->log_data = last; - config_log->first_old = last; + if (from_socket) { + config_log->p->log_data = + g_list_prepend (config_log->p->log_data, entry); + + return config_log->p->log_data; + } else { + last = g_list_append (last, entry); + + if (config_log->p->log_data == NULL) + config_log->p->log_data = last; + + if (config_log->p->first_old == NULL) + config_log->p->first_old = last; + + return g_list_find (last, entry); } - - return g_list_find (last, entry); } else { + g_free (entry->date); g_free (entry); return NULL; } } -/* Read an entire line from the given file, returning a pointer to an - * allocated string. Strip the trailing newline from the line. - */ - -static gchar * -get_line (FILE *file) -{ - int buf_size = 0; - char *buf = NULL, *tmp = NULL; - size_t distance = 0, amt_read = 0; - - g_return_val_if_fail (file != NULL, NULL); - - while (amt_read == buf_size - distance) { - distance = tmp - buf; - - if (distance >= buf_size) { - if (buf == NULL) { - buf_size = 1024; - buf = g_new (char, buf_size); - } else { - buf_size *= 2; - buf = g_renew (char, buf, buf_size); - } - - tmp = buf + distance; - } - - fgets (tmp, buf_size - distance, file); - amt_read = strlen (tmp); - tmp += amt_read; - } - - if (tmp) *(tmp - 1) = '\0'; - - return buf; -} - /* Parse a line from the log file. All pointers must be valid. * * Note: backend just points to somewhere in buffer, so it becomes @@ -710,34 +842,29 @@ time_geq (struct tm *time1, struct tm *time2) * already initialized. Creates the log if not already done. * * Returns TRUE on success and FALSE on failure (unable to open output - * file or log is locked) + * file) */ static gboolean do_load (ConfigLog *config_log) { - FILE *lock_file; + int fd; g_return_val_if_fail (config_log != NULL, FALSE); g_return_val_if_fail (IS_CONFIG_LOG (config_log), FALSE); - g_return_val_if_fail (config_log->location != NULL, FALSE); - g_return_val_if_fail (IS_LOCATION (config_log->location), FALSE); + g_return_val_if_fail (config_log->p->location != NULL, FALSE); + g_return_val_if_fail (IS_LOCATION (config_log->p->location), FALSE); - do_unload (config_log); + do_unload (config_log, FALSE); config_log_reset_filenames (config_log); -#if 0 /* Locking disabled for now */ - /* FIXME: Race condition here, plus lock handling should be - * better */ + fd = open (config_log->p->filename, O_RDONLY); - if (g_file_test (config_log->lock_filename, G_FILE_TEST_ISFILE)) - return FALSE; - - lock_file = fopen (config_log->lock_filename, "w"); - fclose (lock_file); -#endif - - config_log->file = fopen (config_log->filename, "r"); + if (fd != -1) + config_log->p->file_buffer = + io_buffer_new (g_io_channel_unix_new (fd), FALSE); + else + config_log->p->file_buffer = NULL; return TRUE; } @@ -747,37 +874,39 @@ do_load (ConfigLog *config_log) */ static void -do_unload (ConfigLog *config_log) +do_unload (ConfigLog *config_log, gboolean write_log) { GList *tmp; g_return_if_fail (config_log != NULL); g_return_if_fail (IS_CONFIG_LOG (config_log)); - dump_log (config_log); + if (write_log && config_log->p->socket_owner) dump_log (config_log); - if (config_log->file) { - fclose (config_log->file); - config_log->file = NULL; + if (config_log->p->file_buffer) { + io_buffer_destroy (config_log->p->file_buffer); + config_log->p->file_buffer = NULL; } - if (config_log->filename) { - g_free (config_log->filename); - config_log->filename = NULL; + if (config_log->p->filename) { + g_free (config_log->p->filename); + config_log->p->filename = NULL; } - if (config_log->lock_filename) { - unlink (config_log->lock_filename); - g_free (config_log->lock_filename); - config_log->lock_filename = NULL; + if (config_log->p->socket_filename) { + if (config_log->p->socket_owner) + unlink (config_log->p->socket_filename); + + g_free (config_log->p->socket_filename); + config_log->p->socket_filename = NULL; } - while (config_log->log_data) { - tmp = config_log->log_data->next; + while (config_log->p->log_data != NULL) { + tmp = config_log->p->log_data->next; config_log_entry_destroy - ((ConfigLogEntry *) config_log->log_data->data); - g_list_free_1 (config_log->log_data); - config_log->log_data = tmp; + ((ConfigLogEntry *) config_log->p->log_data->data); + g_list_free_1 (config_log->p->log_data); + config_log->p->log_data = tmp; } } @@ -786,12 +915,13 @@ do_unload (ConfigLog *config_log) static gint get_next_id (ConfigLog *config_log) { - if (config_log->log_data == NULL) { - if (load_next_log_entry (config_log, NULL) == NULL) + if (config_log->p->log_data == NULL) { + if (load_log_entry (config_log, FALSE, + config_log->p->file_buffer, NULL) == NULL) return 0; } - return ((ConfigLogEntry *) config_log->log_data->data)->id + 1; + return ((ConfigLogEntry *) config_log->p->log_data->data)->id + 1; } /* Return a newly allocated struct tm with the current time */ @@ -812,66 +942,285 @@ get_current_date (void) /* Write out a log entry */ static void -write_log (FILE *output, ConfigLogEntry *entry) +write_log (IOBuffer *output, ConfigLogEntry *entry) { + gchar *str; + g_return_if_fail (output != NULL); g_return_if_fail (entry != NULL); g_return_if_fail (entry->id >= 0); g_return_if_fail (entry->date != NULL); g_return_if_fail (entry->backend_id != NULL); - fprintf (output, "%08x %04d%02d%02d %02d:%02d:%02d %s\n", - entry->id, entry->date->tm_year + 1900, - entry->date->tm_mon + 1, entry->date->tm_mday, - entry->date->tm_hour, entry->date->tm_min, - entry->date->tm_sec, entry->backend_id); + str = g_strdup_printf ("%08x %04d%02d%02d %02d:%02d:%02d %s\n", + entry->id, entry->date->tm_year + 1900, + entry->date->tm_mon + 1, entry->date->tm_mday, + entry->date->tm_hour, entry->date->tm_min, + entry->date->tm_sec, entry->backend_id); + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, + "%s (pid %d): Writing %s", __FUNCTION__, getpid (), str); + io_buffer_write (output, str); + g_free (str); } static void dump_log (ConfigLog *config_log) { char *filename_out; - FILE *output; GList *first; - char buffer[16384]; - size_t size; + int out_fd; + IOBuffer *output; g_return_if_fail (config_log != NULL); g_return_if_fail (IS_CONFIG_LOG (config_log)); - g_return_if_fail (config_log->location != NULL); - g_return_if_fail (IS_LOCATION (config_log->location)); - g_return_if_fail (location_get_path (config_log->location) != NULL); + g_return_if_fail (config_log->p->location != NULL); + g_return_if_fail (IS_LOCATION (config_log->p->location)); + g_return_if_fail (location_get_path (config_log->p->location) != NULL); filename_out = g_concat_dir_and_file (location_get_path - (config_log->location), + (config_log->p->location), "config.log.out"); - output = fopen (filename_out, "w"); + out_fd = open (filename_out, O_CREAT | O_WRONLY | O_TRUNC, 0600); - if (!output) { + if (out_fd == -1) { g_warning ("Could not open output file: %s", g_strerror (errno)); return; } - for (first = config_log->log_data; first != config_log->first_old; + output = io_buffer_new (g_io_channel_unix_new (out_fd), FALSE); + + for (first = config_log->p->log_data; + first != config_log->p->first_old; first = first->next) write_log (output, first->data); - if (config_log->file) { - rewind (config_log->file); + if (config_log->p->file_buffer) { + io_buffer_rewind (config_log->p->file_buffer); + io_buffer_dump (config_log->p->file_buffer, output); + } - while (!feof (config_log->file)) { - size = fread (buffer, sizeof (char), - 16384, config_log->file); - fwrite (buffer, sizeof (char), size, output); + io_buffer_destroy (output); + + if (config_log->p->filename) + rename (filename_out, config_log->p->filename); +} + +static gboolean +connect_socket (ConfigLog *config_log) +{ + int fd, flags; + + config_log->p->socket_buffer = NULL; + config_log->p->socket_owner = check_socket_filename (config_log); + + fd = socket (PF_LOCAL, SOCK_STREAM, 0); + + if (fd < 0) { + g_warning ("Could not create socket: %s", g_strerror (errno)); + return FALSE; + } + + if (!bind_socket (config_log, fd, TRUE)) { + close (fd); + return FALSE; + } + + config_log->p->socket_buffer = + io_buffer_new (g_io_channel_unix_new (fd), TRUE); + + if (config_log->p->socket_owner) { + config_log->p->input_id = + g_io_add_watch (config_log->p->socket_buffer->channel, + G_IO_IN | G_IO_ERR, + (GIOFunc) socket_connect_cb, + config_log); + } else { + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, + "%s (pid %d): Adding watch to listen for data\n", + __FUNCTION__, getpid ()); + + config_log->p->input_id = + g_io_add_watch (config_log->p->socket_buffer->channel, + G_IO_IN | G_IO_ERR | G_IO_HUP, + (GIOFunc) socket_data_cb, + config_log); + + /* Read any data that might have come through before we added + * the watch */ + flags = fcntl (fd, F_GETFL); + + if (flags != -1) { + flags |= O_NONBLOCK; + fcntl (fd, F_SETFL, flags); + } + + socket_data_cb (config_log->p->socket_buffer->channel, + G_IO_IN, config_log); + } + + return config_log->p->socket_owner; +} + +/* Checks to see if the filename associated with the socket is free. Returns + * TRUE if it is and FALSE otherwise */ + +static gboolean +check_socket_filename (ConfigLog *config_log) +{ + struct stat buf; + gboolean is_free; + + if (config_log->p->socket_filename == NULL) return FALSE; + + if (stat (config_log->p->socket_filename, &buf) == -1) { + if (errno == ENOENT) { + is_free = TRUE; + } else { + g_warning ("Could not stat file: %s", + g_strerror (errno)); + return FALSE; + } + } else { + is_free = FALSE; + } + + if (!is_free && !S_ISSOCK (buf.st_mode)) { + g_warning ("There is another file in the way of the socket"); + return FALSE; + } + + return is_free; +} + +/* Binds (or rebinds) the given socket to the correct filename; optionally + * connects to the correct socket if the current object is not the config log + * owner + * + * If it cannot connect to the given socket, this means the process owner has + * died unexpectedly and left it lying around. It then tries to take ownership + * of it. + */ + +static gboolean +bind_socket (ConfigLog *config_log, int fd, gboolean do_connect) +{ + struct sockaddr_un name; + + /* FIXME: What if the socket filename is too long here? */ + if (config_log->p->socket_owner || do_connect) { + name.sun_family = AF_LOCAL; + strncpy (name.sun_path, config_log->p->socket_filename, + sizeof (name.sun_path)); + } + + if (do_connect && !config_log->p->socket_owner) { + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, + "Trying to connect to socket (pid %d)", getpid ()); + + if (!connect (fd, (struct sockaddr *) &name, SUN_LEN (&name))) + return TRUE; + + if (errno != ECONNREFUSED) { + g_warning ("Could not connect to socket: %s", + g_strerror (errno)); + return FALSE; + } else { + unlink (config_log->p->socket_filename); + config_log->p->socket_owner = TRUE; } } - fclose (output); + if (bind (fd, (struct sockaddr *) &name, SUN_LEN (&name)) < 0) { + g_warning ("Could not bind to socket filename: %s", + g_strerror (errno)); + return FALSE; + } - if (config_log->filename) - rename (filename_out, config_log->filename); + if (listen (fd, 5) < 0) { + g_warning ("Could not set up socket for listening: %s", + g_strerror (errno)); + return FALSE; + } + + return TRUE; +} + +static void +disconnect_socket (ConfigLog *config_log) +{ + if (config_log->p->socket_owner) + g_list_foreach (config_log->p->slaves, + (GFunc) slave_destroy, NULL); + + if (config_log->p->socket_buffer != NULL) + io_buffer_destroy (config_log->p->socket_buffer); +} + +static gboolean +socket_connect_cb (GIOChannel *channel, GIOCondition condition, + ConfigLog *config_log) +{ + int fd; + struct sockaddr_un addr; + socklen_t len; + + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Enter %s (pid %d)", + __FUNCTION__, getpid ()); + + g_return_val_if_fail (config_log != NULL, FALSE); + g_return_val_if_fail (IS_CONFIG_LOG (config_log), FALSE); + + if (condition == G_IO_IN) { + fd = accept (g_io_channel_unix_get_fd + (config_log->p->socket_buffer->channel), + &addr, &len); + + if (fd < 0) { + g_warning ("Could not accept connection: %s", + g_strerror (fd)); + return TRUE; + } + + config_log->p->slaves = + g_list_prepend (config_log->p->slaves, + slave_new (config_log, fd)); + } + + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Exit %s (pid %d)", + __FUNCTION__, getpid ()); + + return TRUE; +} + +/* Callback issued when data comes in over the socket; only used when this is + * *not* the master socket. */ + +static gboolean +socket_data_cb (GIOChannel *channel, GIOCondition condition, + ConfigLog *config_log) +{ + g_return_val_if_fail (config_log != NULL, FALSE); + g_return_val_if_fail (IS_CONFIG_LOG (config_log), FALSE); + + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Enter %s (pid %d)", + __FUNCTION__, getpid ()); + + if (condition == G_IO_IN) { + load_log_entry (config_log, TRUE, + config_log->p->socket_buffer, NULL); + } + else if (condition == G_IO_HUP) { + disconnect_socket (config_log); + connect_socket (config_log); + return FALSE; + } + + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Exit %s (pid %d)", + __FUNCTION__, getpid ()); + + return TRUE; } static void @@ -885,3 +1234,242 @@ config_log_entry_destroy (ConfigLogEntry *entry) g_free (entry->backend_id); g_free (entry); } + +static Slave * +slave_new (ConfigLog *config_log, int fd) +{ + Slave *slave; + + slave = g_new0 (Slave, 1); + slave->config_log = config_log; + slave->buffer = io_buffer_new (g_io_channel_unix_new (fd), TRUE); + slave->source_id = g_io_add_watch (slave->buffer->channel, + G_IO_IN | G_IO_ERR | G_IO_HUP, + (GIOFunc) slave_data_cb, + slave); + + slave_apprise_data (slave); + + return slave; +} + +static void +slave_destroy (Slave *slave) +{ + g_return_if_fail (slave != NULL); + g_return_if_fail (slave->config_log != NULL); + g_return_if_fail (IS_CONFIG_LOG (slave->config_log)); + + slave->config_log->p->slaves = + g_list_remove (slave->config_log->p->slaves, slave); + io_buffer_destroy (slave->buffer); + g_free (slave); +} + +static gboolean +slave_data_cb (GIOChannel *channel, GIOCondition condition, + Slave *slave) +{ + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Enter %s (pid %d)", + __FUNCTION__, getpid ()); + + g_return_val_if_fail (slave != NULL, FALSE); + g_return_val_if_fail (slave->config_log != NULL, FALSE); + g_return_val_if_fail (IS_CONFIG_LOG (slave->config_log), FALSE); + + if (condition == G_IO_HUP || slave->buffer->closed) { + slave_destroy (slave); + return FALSE; + } + else if (condition == G_IO_IN) { + if (load_log_entry (slave->config_log, TRUE, slave->buffer, + NULL) != NULL) + slave_broadcast_data (slave, slave->config_log); + } + + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Exit %s", __FUNCTION__); + + return TRUE; +} + +/* Broadcasts first log entry to all the slaves of the config log besides the + * one given */ + +static void +slave_broadcast_data (Slave *slave, ConfigLog *config_log) +{ + GList *node; + Slave *current; + ConfigLogEntry *first_entry; + + first_entry = config_log->p->log_data->data; + + for (node = config_log->p->slaves; node != NULL; node = node->next) { + current = node->data; + + if (current == slave) continue; + write_log (current->buffer, first_entry); + } +} + +/* Sends all the new log entries to the given slave */ + +static void +slave_apprise_data (Slave *slave) +{ + GList *node; + ConfigLogEntry *current; + + g_return_if_fail (slave != NULL); + g_return_if_fail (IS_CONFIG_LOG (slave->config_log)); + + for (node = slave->config_log->p->log_data; + node != slave->config_log->p->first_old; + node = node->next) + { + current = node->data; + write_log (slave->buffer, current); + } +} + +static IOBuffer * +io_buffer_new (GIOChannel *channel, gboolean from_socket) +{ + IOBuffer *buffer; + + buffer = g_new0 (IOBuffer, 1); + buffer->channel = channel; + buffer->read_ptr = buffer->write_ptr = buffer->buffer; + buffer->from_socket = from_socket; + + return buffer; +} + +static void +io_buffer_destroy (IOBuffer *buffer) +{ + int fd; + + fd = g_io_channel_unix_get_fd (buffer->channel); + g_io_channel_unref (buffer->channel); + close (fd); + g_free (buffer); +} + +/* Note: The two functions below are borrowed from GDict */ + +/* io_buffer_cycle () + * + * Reads additional data from the socket if it can + * + * Returns 0 on success, 1 if there is no more data to read, -1 on socket error + */ + +static int +io_buffer_cycle (IOBuffer *buffer) +{ + int amt_read, res; + + if (!buffer->read_cycle_done && !buffer->closed) { + if (buffer->write_ptr > buffer->read_ptr) + memmove (buffer->buffer, buffer->read_ptr, + buffer->write_ptr - buffer->read_ptr); + buffer->write_ptr -= buffer->read_ptr - buffer->buffer; + buffer->read_ptr = buffer->buffer; + + res = g_io_channel_read (buffer->channel, buffer->write_ptr, + buffer->buffer + READ_BUF_LEN + - buffer->write_ptr, + &amt_read); + + if (res == G_IO_ERROR_AGAIN) + buffer->read_cycle_done = TRUE; + else if (amt_read == 0) + buffer->closed = TRUE; + else if (res != G_IO_ERROR_NONE) + return -1; + + buffer->write_ptr += amt_read; + return 0; + } + else { + return 1; + } +} + +/* io_buffer_read_line () + * + * Reads a line of text from the socket and performs a CR-LF translation + * + * Returns a pointer to the string on success + */ + +static gchar * +io_buffer_read_line (IOBuffer *buffer) { + gchar *start_ptr, *end_ptr; + + if (!buffer->channel) return NULL; + + while (1) { + end_ptr = strchr (buffer->read_ptr, '\n'); + + if (end_ptr == NULL || end_ptr > buffer->write_ptr) { + if (io_buffer_cycle (buffer)) return NULL; + } + else { + break; + } + } + + end_ptr[0] = '\0'; + + start_ptr = buffer->read_ptr; + buffer->read_ptr = end_ptr + 1; + + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, + "%s (pid %d): Line read was %s; from_socket = %d", + __FUNCTION__, getpid (), start_ptr, buffer->from_socket); + + return start_ptr; +} + +static void +io_buffer_write (IOBuffer *buffer, gchar *str) +{ + gint bytes_written; + + g_return_if_fail (buffer != NULL); + + if (str == NULL) return; + + g_io_channel_write (buffer->channel, str, strlen (str), + &bytes_written); +} + +static void +io_buffer_rewind (IOBuffer *buffer) +{ + g_return_if_fail (buffer != NULL); + + g_io_channel_seek (buffer->channel, 0, G_SEEK_SET); +} + +/* Dumps the contents of the source into the destination. + * + * Note: The source will be completely screwed up and unusable after this + * point!!!! + */ + +static void +io_buffer_dump (IOBuffer *source, IOBuffer *dest) +{ + gchar buffer[4096]; + gint written, read; + + g_return_if_fail (buffer != NULL); + + do { + g_io_channel_read (source->channel, buffer, 4096, &read); + g_io_channel_write (dest->channel, buffer, read, &written); + } while (read == 4096); +} diff --git a/archiver/config-log.h b/archiver/config-log.h index eedbc6496..3fd19b6c1 100644 --- a/archiver/config-log.h +++ b/archiver/config-log.h @@ -34,6 +34,7 @@ typedef struct _ConfigLog ConfigLog; typedef struct _ConfigLogClass ConfigLogClass; +typedef struct _ConfigLogPrivate ConfigLogPrivate; typedef struct _Location Location; @@ -44,14 +45,7 @@ struct _ConfigLog { GtkObject object; - Location *location; - - FILE *file; - char *filename; - char *lock_filename; - - GList *log_data; - GList *first_old; + ConfigLogPrivate *p; }; struct _ConfigLogClass @@ -59,30 +53,31 @@ struct _ConfigLogClass GtkObjectClass parent; }; -guint config_log_get_type (void); +guint config_log_get_type (void); -GtkObject *config_log_open (Location *location); -void config_log_delete (ConfigLog *config_log); +GtkObject *config_log_open (Location *location); +void config_log_delete (ConfigLog *config_log); -gint config_log_get_rollback_id_for_date (ConfigLog *config_log, - struct tm *date, - gchar *backend_id); -gint config_log_get_rollback_id_by_steps (ConfigLog *config_log, - guint steps, - gchar *backend_id); +gint config_log_get_rollback_id_for_date (ConfigLog *config_log, + struct tm *date, + gchar *backend_id); +gint config_log_get_rollback_id_by_steps (ConfigLog *config_log, + guint steps, + gchar *backend_id); -gchar *config_log_get_backend_id_for_id (ConfigLog *config_log, - gint id); -struct tm *config_log_get_date_for_id (ConfigLog *config_log, - gint id); +gchar *config_log_get_backend_id_for_id (ConfigLog *config_log, + gint id); +struct tm *config_log_get_date_for_id (ConfigLog *config_log, + gint id); -gint config_log_write_entry (ConfigLog *config_log, - gchar *backend_id); +gint config_log_write_entry (ConfigLog *config_log, + gchar *backend_id); -void config_log_iterate (ConfigLog *config_log, - ConfigLogIteratorCB callback, - gpointer data); +void config_log_iterate (ConfigLog *config_log, + ConfigLogIteratorCB callback, + gpointer data); -void config_log_reset_filenames (ConfigLog *config_log); +void config_log_reset_filenames (ConfigLog *config_log); +void config_log_reload (ConfigLog *config_log); #endif /* __CONFIG_LOG */ diff --git a/archiver/default-user.xml b/archiver/default-user.xml index fb5e2159c..d7c887869 100644 --- a/archiver/default-user.xml +++ b/archiver/default-user.xml @@ -2,10 +2,10 @@ - + - - + + diff --git a/archiver/main.c b/archiver/main.c index df6b050d4..877311708 100644 --- a/archiver/main.c +++ b/archiver/main.c @@ -302,6 +302,9 @@ main (int argc, char **argv) Archive *archive; Location *location = NULL; + /* For Electric Fence */ + free (malloc (1)); + bindtextdomain (PACKAGE, GNOMELOCALEDIR); textdomain (PACKAGE);