static void afinter_source_mark(gpointer s) { AFInterSource *self = (AFInterSource *) s; LogMessage *msg; LogPathOptions path_options = LOG_PATH_OPTIONS_INIT; struct timespec nmt; main_loop_assert_main_thread(); g_static_mutex_lock(&internal_mark_target_lock); nmt = next_mark_target; g_static_mutex_unlock(&internal_mark_target_lock); if (log_source_free_to_send(&self->super) && nmt.tv_sec <= self->mark_timer.expires.tv_sec) { /* the internal_mark_target has not been overwritten by an incoming message in afinter_postpone_mark (there was no msg in the meantime) -> the mark msg can be sent */ msg = log_msg_new_mark(); path_options.ack_needed = FALSE; log_pipe_queue(&self->super.super, msg, &path_options); /* the next_mark_target will be increased in afinter_postpone_mark */ } afinter_source_update_watches(self); }
static void log_writer_io_flush_output(gpointer s) { LogWriter *self = (LogWriter *) s; main_loop_assert_main_thread(); log_writer_stop_watches(self); log_pipe_ref(&self->super); if ((self->options->options & LWO_THREADED)) { main_loop_io_worker_job_submit(&self->io_job); } else { /* Checking main_loop_io_worker_job_quit() helps to speed up the * reload process. If reload/shutdown is requested we shouldn't do * anything here, a final flush will be attempted in * log_writer_deinit(). * * Our current understanding is that it doesn't prevent race * conditions of any kind. */ if (!main_loop_io_worker_job_quit()) { log_writer_work_perform(s); log_writer_work_finished(s); } } }
static void log_reader_update_watches(LogReader *self) { GIOCondition cond; gboolean free_to_send; gboolean line_is_ready_in_buffer; main_loop_assert_main_thread(); if (!log_reader_is_opened(self)) return; log_reader_start_watches_if_stopped(self); free_to_send = log_source_free_to_send(&self->super); if (!free_to_send) { log_reader_suspend_until_awoken(self); return; } line_is_ready_in_buffer = log_proto_server_prepare(self->proto, &cond); if (self->immediate_check || line_is_ready_in_buffer) { log_reader_force_check_in_next_poll(self); return; } poll_events_update_watches(self->poll_events, cond); }
static void log_writer_queue_filled(gpointer s) { LogWriter *self = (LogWriter *) s; main_loop_assert_main_thread(); /* * NOTE: This theory is somewhat questionable, e.g. I'm not 100% sure it * is the right scenario, but the race was closed. So take this with a * grain of salt. * * The queue_filled callback is running in the main thread. Because of the * possible delay caused by iv_event_post() the callback might be * delivered event after stop_watches() has been called. * * - log_writer_schedule_update_watches() is called by the reader * thread, which calls iv_event_post() * - the main thread calls stop_watches() in work_perform * - the event is delivered in the main thread * * But since stop/start watches always run in the main thread and we do * too, we can check if this is the case. A LogWriter without watches * running is busy writing out data to the destination, e.g. a * start_watches is to be expected once log_writer_work_finished() is run * at the end of the deferred work, executed by the I/O threads. */ if (self->watches_running) log_writer_update_watches((LogWriter *) s); }
static void afsocket_dd_start_watches(AFSocketDestDriver *self) { main_loop_assert_main_thread(); self->connect_fd.fd = self->fd; iv_fd_register(&self->connect_fd); }
static gboolean afsocket_dd_connected(AFSocketDestDriver *self) { gchar buf1[256], buf2[256]; int error = 0; socklen_t errorlen = sizeof(error); LogTransport *transport; LogProtoClient *proto; main_loop_assert_main_thread(); if (iv_fd_registered(&self->connect_fd)) iv_fd_unregister(&self->connect_fd); if (self->transport_mapper->sock_type == SOCK_STREAM) { if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &error, &errorlen) == -1) { msg_error("getsockopt(SOL_SOCKET, SO_ERROR) failed for connecting socket", evt_tag_int("fd", self->fd), evt_tag_str("server", g_sockaddr_format(self->dest_addr, buf2, sizeof(buf2), GSA_FULL)), evt_tag_errno(EVT_TAG_OSERROR, errno), evt_tag_int("time_reopen", self->time_reopen), NULL); goto error_reconnect; } if (error) { msg_error("Syslog connection failed", evt_tag_int("fd", self->fd), evt_tag_str("server", g_sockaddr_format(self->dest_addr, buf2, sizeof(buf2), GSA_FULL)), evt_tag_errno(EVT_TAG_OSERROR, error), evt_tag_int("time_reopen", self->time_reopen), NULL); goto error_reconnect; } } msg_notice("Syslog connection established", evt_tag_int("fd", self->fd), evt_tag_str("server", g_sockaddr_format(self->dest_addr, buf2, sizeof(buf2), GSA_FULL)), evt_tag_str("local", g_sockaddr_format(self->bind_addr, buf1, sizeof(buf1), GSA_FULL)), NULL); transport = afsocket_dd_construct_transport(self, self->fd); if (!transport) goto error_reconnect; proto = log_proto_client_factory_construct(self->proto_factory, transport, &self->writer_options.proto_options.super); log_writer_reopen(self->writer, proto); return TRUE; error_reconnect: close(self->fd); self->fd = -1; afsocket_dd_start_reconnect_timer(self); return FALSE; }
static void log_reader_update_watches(LogReader *self) { GIOCondition cond; gint idle_timeout = -1; main_loop_assert_main_thread(); log_reader_stop_idle_timer(self); if (!log_reader_is_opened(self)) return; log_reader_start_watches_if_stopped(self); gboolean free_to_send = log_source_free_to_send(&self->super); if (!free_to_send) { log_reader_suspend_until_awoken(self); return; } LogProtoPrepareAction prepare_action = log_proto_server_prepare(self->proto, &cond, &idle_timeout); if (idle_timeout > 0) { iv_validate_now(); self->idle_timer.expires = iv_now; self->idle_timer.expires.tv_sec += idle_timeout; iv_timer_register(&self->idle_timer); } if (self->immediate_check) { log_reader_force_check_in_next_poll(self); return; } switch (prepare_action) { case LPPA_POLL_IO: poll_events_update_watches(self->poll_events, cond); break; case LPPA_FORCE_SCHEDULE_FETCH: log_reader_force_check_in_next_poll(self); break; case LPPA_SUSPEND: log_reader_suspend_until_awoken(self); break; default: g_assert_not_reached(); break; } }
static void log_writer_arm_suspend_timer(LogWriter *self, void (*handler)(void *), gint timeout_msec) { main_loop_assert_main_thread(); if (iv_timer_registered(&self->suspend_timer)) iv_timer_unregister(&self->suspend_timer); iv_validate_now(); self->suspend_timer.handler = handler; self->suspend_timer.expires = iv_now; timespec_add_msec(&self->suspend_timer.expires, timeout_msec); iv_timer_register(&self->suspend_timer); }
static void afsocket_dd_start_reconnect_timer(AFSocketDestDriver *self) { main_loop_assert_main_thread(); if (iv_timer_registered(&self->reconnect_timer)) iv_timer_unregister(&self->reconnect_timer); iv_validate_now(); self->reconnect_timer.expires = iv_now; timespec_add_msec(&self->reconnect_timer.expires, self->time_reopen * 1000); iv_timer_register(&self->reconnect_timer); }
static gboolean log_writer_suppress_timeout(gpointer pt) { LogWriter *self = (LogWriter *) pt; main_loop_assert_main_thread(); g_static_mutex_lock(&self->suppress_lock); log_writer_emit_suppress_summary(self); g_static_mutex_unlock(&self->suppress_lock); return FALSE; }
static gboolean log_writer_last_msg_timer(gpointer pt) { LogWriter *self = (LogWriter *) pt; main_loop_assert_main_thread(); g_static_mutex_lock(&self->suppress_lock); log_writer_last_msg_flush(self); g_static_mutex_unlock(&self->suppress_lock); return FALSE; }
static void log_writer_work_finished(gpointer s) { LogWriter *self = (LogWriter *) s; main_loop_assert_main_thread(); self->flush_waiting_for_timeout = FALSE; if (self->pending_proto_present) { /* pending proto is only set in the main thread, so no need to * lock it before coming here. After we're syncing with the * log_writer_reopen() call, quite possibly coming from a * non-main thread. */ g_static_mutex_lock(&self->pending_proto_lock); if (self->proto) log_proto_free(self->proto); self->proto = self->pending_proto; self->pending_proto = NULL; self->pending_proto_present = FALSE; g_cond_signal(self->pending_proto_cond); g_static_mutex_unlock(&self->pending_proto_lock); } if (!self->work_result) { log_writer_broken(self, NC_WRITE_ERROR); if (self->proto) { log_writer_suspend(self); msg_notice("Suspending write operation because of an I/O error", evt_tag_int("fd", log_proto_get_fd(self->proto)), evt_tag_int("time_reopen", self->options->time_reopen), NULL); } goto exit; } if ((self->super.flags & PIF_INITIALIZED) && self->proto) { /* reenable polling the source, but only if we're still initialized */ log_writer_start_watches(self); } exit: log_pipe_unref(&self->super); }
/* function called using main_loop_call() in case the suppress timer needs * to be updated. It is running in the main thread, thus is able to * reregister our ivykis timer */ static void ml_batched_timer_perform_update(MlBatchedTimer *self) { main_loop_assert_main_thread(); if (iv_timer_registered(&self->timer)) iv_timer_unregister(&self->timer); self->timer.expires = self->expires; if (self->timer.expires.tv_sec > 0) iv_timer_register(&self->timer); self->unref_cookie(self->cookie); }
static gboolean log_reader_deinit(LogPipe *s) { LogReader *self = (LogReader *) s; main_loop_assert_main_thread(); iv_event_unregister(&self->schedule_wakeup); log_reader_stop_watches(self); if (!log_source_deinit(s)) return FALSE; return TRUE; }
/* function called using main_loop_call() in case the suppress timer needs * to be updated */ static void log_writer_perform_suppress_timer_update(LogWriter *self) { main_loop_assert_main_thread(); if (iv_timer_registered(&self->suppress_timer)) iv_timer_unregister(&self->suppress_timer); g_static_mutex_lock(&self->suppress_lock); self->suppress_timer.expires = self->suppress_timer_expires; self->suppress_timer_updated = TRUE; g_static_mutex_unlock(&self->suppress_lock); if (self->suppress_timer.expires.tv_sec > 0) iv_timer_register(&self->suppress_timer); log_pipe_unref(&self->super); }
static void affile_dw_reap(gpointer s) { AFFileDestWriter *self = (AFFileDestWriter *) s; main_loop_assert_main_thread(); g_static_mutex_lock(&self->lock); if (!log_writer_has_pending_writes(self->writer) && !self->queue_pending && (cached_g_current_time_sec() - self->last_msg_stamp) >= self->owner->time_reap) { g_static_mutex_unlock(&self->lock); msg_verbose("Destination timed out, reaping", evt_tag_str("template", self->owner->filename_template->template),
static gboolean afsocket_dd_start_connect(AFSocketDestDriver *self) { int sock, rc; gchar buf1[MAX_SOCKADDR_STRING], buf2[MAX_SOCKADDR_STRING]; main_loop_assert_main_thread(); if (!afsocket_open_socket(self->bind_addr, !!(self->flags & AFSOCKET_STREAM), &sock)) { return FALSE; } if (self->setup_socket && !self->setup_socket(self, sock)) { close(sock); return FALSE; } g_assert(self->dest_addr); rc = g_connect(sock, self->dest_addr); if (rc == G_IO_STATUS_NORMAL) { self->fd = sock; afsocket_dd_connected(self); } else if (rc == G_IO_STATUS_ERROR && errno == EINPROGRESS) { /* we must wait until connect succeeds */ self->fd = sock; afsocket_dd_start_watches(self); } else { /* error establishing connection */ msg_error("Connection failed", evt_tag_int("fd", sock), evt_tag_str("server", g_sockaddr_format(self->dest_addr, buf2, sizeof(buf2), GSA_FULL)), evt_tag_str("local", g_sockaddr_format(self->bind_addr, buf1, sizeof(buf1), GSA_FULL)), evt_tag_errno(EVT_TAG_OSERROR, errno), NULL); close(sock); return FALSE; } return TRUE; }
static void log_writer_update_fd_callbacks(LogWriter *self, GIOCondition cond) { main_loop_assert_main_thread(); if (self->pollable_state > 0) { if (self->flags & LW_DETECT_EOF && (cond & G_IO_IN) == 0 && (cond & G_IO_OUT)) { /* if output is enabled, and we're in DETECT_EOF mode, and input is * not needed by the log protocol, install the eof check callback to * destroy the connection if an EOF is received. */ iv_fd_set_handler_in(&self->fd_watch, log_writer_io_check_eof); } else if (cond & G_IO_IN) { /* in case the protocol requested G_IO_IN, it means that it needs to * invoke read in the flush code, so just install the flush_output * handler for input */ iv_fd_set_handler_in(&self->fd_watch, log_writer_io_flush_output); } else { /* otherwise we're not interested in input */ iv_fd_set_handler_in(&self->fd_watch, NULL); } if (cond & G_IO_OUT) iv_fd_set_handler_out(&self->fd_watch, log_writer_io_flush_output); else iv_fd_set_handler_out(&self->fd_watch, NULL); iv_fd_set_handler_err(&self->fd_watch, log_writer_io_error); } else { /* fd is not pollable, assume it is always writable */ if (cond & G_IO_OUT) { if (!iv_task_registered(&self->immed_io_task)) iv_task_register(&self->immed_io_task); } else if (iv_task_registered(&self->immed_io_task)) { iv_task_unregister(&self->immed_io_task); } } }
static void afsocket_dd_stop_watches(AFSocketDestDriver *self) { main_loop_assert_main_thread(); if (iv_fd_registered(&self->connect_fd)) { iv_fd_unregister(&self->connect_fd); /* need to close the fd in this case as it wasn't established yet */ msg_verbose("Closing connecting fd", evt_tag_int("fd", self->fd)); close(self->fd); } if (iv_timer_registered(&self->reconnect_timer)) iv_timer_unregister(&self->reconnect_timer); }
static void trigger_source_triggered (gpointer s) { TriggerSource *self = (TriggerSource *) s; LogMessage *msg; LogPathOptions path_options = LOG_PATH_OPTIONS_INIT; main_loop_assert_main_thread (); if (!log_source_free_to_send (&self->super)) goto end; msg = log_msg_new_internal (LOG_INFO, self->options->message); path_options.ack_needed = FALSE; log_pipe_queue (&self->super.super, msg, &path_options); end: trigger_source_update_watches (self); }
static gboolean log_writer_suppress_timeout(gpointer pt) { LogWriter *self = (LogWriter *) pt; main_loop_assert_main_thread(); /* NOTE: this will probably do nothing as we are the timer callback, but * we may not do it with the suppress_lock held */ ml_batched_timer_cancel(&self->suppress_timer); g_static_mutex_lock(&self->suppress_lock); /* NOTE: we may be waken up an extra time if the suppress_timer setup race * is lost, see the comment at log_writer_is_msg_suppressed() for an * explanation */ if (self->last_msg_count > 0) log_writer_emit_suppress_summary(self); g_static_mutex_unlock(&self->suppress_lock); return FALSE; }
/* this is the callback function that gets called when the MARK timeout * elapsed. It runs in the main thread. */ static void log_writer_mark_timeout(void *cookie) { LogWriter *self = (LogWriter *)cookie; LogPathOptions path_options = LOG_PATH_OPTIONS_INIT; const gchar *hostname; gsize hostname_len; LogMessage *msg; main_loop_assert_main_thread(); msg = log_msg_new_mark(); /* timeout: there was no new message on the writer or it is in periodical mode */ hostname = resolve_sockaddr_to_hostname(&hostname_len, msg->saddr, &self->options->host_resolve_options); log_msg_set_value(msg, LM_V_HOST, hostname, hostname_len); /* set the current time in the message stamp */ msg->timestamps[LM_TS_STAMP] = msg->timestamps[LM_TS_RECVD]; if (!log_writer_is_msg_suppressed(self, msg)) { log_queue_push_tail(self->queue, msg, &path_options); stats_counter_inc(self->processed_messages); } else { log_msg_drop(msg, &path_options); } /* we need to issue another MARK in all mark-mode cases that already * triggered this callback (dst-idle, host-idle, periodical). The * original setup of the timer is at a different location: * - log_writer_queue() for "*-idle" modes * - log_writer_init() for periodical mode */ log_writer_postpone_mark_timer(self); }
static void _update_watches(JournalReader *self) { gboolean free_to_send; main_loop_assert_main_thread(); _start_watches_if_stopped(self); free_to_send = log_source_free_to_send(&self->super); if (!free_to_send) { _suspend_until_awoken(self); return; } if (self->immediate_check) { _force_check_in_next_poll(self); return; } poll_events_update_watches(self->poll_events, G_IO_IN); }
static void log_writer_update_watches(LogWriter *self) { gint fd; GIOCondition cond = 0; gboolean partial_batch; gint timeout_msec = 0; main_loop_assert_main_thread(); /* NOTE: we either start the suspend_timer or enable the fd_watch. The two MUST not happen at the same time. */ if (log_proto_prepare(self->proto, &fd, &cond) || self->flush_waiting_for_timeout || log_queue_check_items(self->queue, self->options->flush_lines, &partial_batch, &timeout_msec, (LogQueuePushNotifyFunc) log_writer_schedule_update_watches, self, NULL)) { /* flush_lines number of element is already available and throttle would permit us to send. */ log_writer_update_fd_callbacks(self, cond); } else if (partial_batch || timeout_msec) { /* few elements are available, but less than flush_lines, we need to start a timer to initiate a flush */ log_writer_update_fd_callbacks(self, 0); self->flush_waiting_for_timeout = TRUE; log_writer_arm_suspend_timer(self, (void (*)(void *)) log_writer_update_watches, timeout_msec ? timeout_msec : self->options->flush_timeout); } else { /* no elements or no throttle space, wait for a wakeup by the queue * when the required number of items are added. see the * log_queue_check_items and its parallel_push argument above */ log_writer_update_fd_callbacks(self, 0); } }
static void _on_directory_monitor_changed(const DirectoryMonitorEvent *event, gpointer user_data) { main_loop_assert_main_thread(); WildcardSourceDriver *self = (WildcardSourceDriver *)user_data; if ((event->event_type == FILE_CREATED)) { _handle_file_created(self, event); } else if (event->event_type == DIRECTORY_CREATED) { _handle_directory_created(self, event); } else if (event->event_type == FILE_DELETED) { _handle_file_deleted(self, event); } else if (event->event_type == DIRECTORY_DELETED) { _handler_directory_deleted(self, event); } }
static gboolean afsocket_dd_connected(AFSocketDestDriver *self) { gchar buf1[256], buf2[256]; int error = 0; socklen_t errorlen = sizeof(error); LogTransport *transport; LogProto *proto; guint32 transport_flags = 0; main_loop_assert_main_thread(); if (iv_fd_registered(&self->connect_fd)) iv_fd_unregister(&self->connect_fd); if (self->flags & AFSOCKET_STREAM) { transport_flags |= LTF_SHUTDOWN; if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &error, &errorlen) == -1) { msg_error("getsockopt(SOL_SOCKET, SO_ERROR) failed for connecting socket", evt_tag_int("fd", self->fd), evt_tag_str("server", g_sockaddr_format(self->dest_addr, buf2, sizeof(buf2), GSA_FULL)), evt_tag_errno(EVT_TAG_OSERROR, errno), evt_tag_int("time_reopen", self->time_reopen), NULL); goto error_reconnect; } if (error) { msg_error("Syslog connection failed", evt_tag_int("fd", self->fd), evt_tag_str("server", g_sockaddr_format(self->dest_addr, buf2, sizeof(buf2), GSA_FULL)), evt_tag_errno(EVT_TAG_OSERROR, error), evt_tag_int("time_reopen", self->time_reopen), NULL); goto error_reconnect; } } msg_notice("Syslog connection established", evt_tag_int("fd", self->fd), evt_tag_str("server", g_sockaddr_format(self->dest_addr, buf2, sizeof(buf2), GSA_FULL)), evt_tag_str("local", g_sockaddr_format(self->bind_addr, buf1, sizeof(buf1), GSA_FULL)), NULL); #if ENABLE_SSL if (self->tls_context) { TLSSession *tls_session; tls_session = tls_context_setup_session(self->tls_context); if (!tls_session) { goto error_reconnect; } tls_session_set_verify(tls_session, afsocket_dd_tls_verify_callback, self, NULL); transport = log_transport_tls_new(tls_session, self->fd, transport_flags); } else #endif transport = log_transport_plain_new(self->fd, transport_flags); if (self->flags & AFSOCKET_SYSLOG_PROTOCOL) { if (self->flags & AFSOCKET_STREAM) proto = log_proto_framed_client_new(transport); else proto = log_proto_text_client_new(transport); } else { proto = log_proto_text_client_new(transport); } log_writer_reopen(self->writer, proto); return TRUE; error_reconnect: close(self->fd); self->fd = -1; afsocket_dd_start_reconnect_timer(self); return FALSE; }
static void log_reader_update_watches(LogReader *self) { gint fd; GIOCondition cond; gboolean free_to_send; main_loop_assert_main_thread(); self->suspended = FALSE; free_to_send = log_source_free_to_send(&self->super); if (!free_to_send || self->immediate_check || log_proto_prepare(self->proto, &fd, &cond)) { /* we disable all I/O related callbacks here because we either know * that we can continue (e.g. immediate_check == TRUE) or we know * that we can't continue even if data would be available (e.g. * free_to_send == FALSE) */ self->immediate_check = FALSE; if (iv_fd_registered(&self->fd_watch)) { iv_fd_set_handler_in(&self->fd_watch, NULL); iv_fd_set_handler_out(&self->fd_watch, NULL); /* we disable the error handler too, as it might be * triggered even when we don't want to read data * (e.g. log_source_free_to_send() is FALSE). * * And at least on Linux, it may happen that EPOLLERR is * set, while there's still data in the socket buffer. Thus * in reaction to an EPOLLERR, we could possibly send * further messages without validating the * log_source_free_to_send() would allow us to, potentially * overflowing our window (and causing a failed assertion in * log_source_queue(). */ iv_fd_set_handler_err(&self->fd_watch, NULL); } if (iv_timer_registered(&self->follow_timer)) iv_timer_unregister(&self->follow_timer); if (free_to_send) { /* we have data in our input buffer, we need to start working * on it immediately, without waiting for I/O events */ if (!iv_task_registered(&self->restart_task)) { iv_task_register(&self->restart_task); } } else { self->suspended = TRUE; } return; } if (iv_fd_registered(&self->fd_watch)) { /* this branch is executed when our fd is connected to a non-file * source (e.g. TCP, UDP socket). We set up I/O callbacks here. * files cannot be polled using epoll, as it causes an I/O error * (thus abort in ivykis). */ if (cond & G_IO_IN) iv_fd_set_handler_in(&self->fd_watch, log_reader_io_process_input); else iv_fd_set_handler_in(&self->fd_watch, NULL); if (cond & G_IO_OUT) iv_fd_set_handler_out(&self->fd_watch, log_reader_io_process_input); else iv_fd_set_handler_out(&self->fd_watch, NULL); if (cond & (G_IO_IN + G_IO_OUT)) iv_fd_set_handler_err(&self->fd_watch, log_reader_io_process_input); else iv_fd_set_handler_err(&self->fd_watch, NULL); } else { if (self->options->follow_freq > 0) { if (iv_timer_registered(&self->follow_timer)) iv_timer_unregister(&self->follow_timer); iv_validate_now(); self->follow_timer.expires = iv_now; timespec_add_msec(&self->follow_timer.expires, self->options->follow_freq); iv_timer_register(&self->follow_timer); } else { /* NOTE: we don't need to unregister the timer here as follow_freq * never changes during runtime, thus if ever it was registered that * also means that we go into the if branch above. */ } } }