/** * Generic send function for dsmesock messages * XXX: How should we handle sending failures? * * @param msg A pointer to the message to send */ static void mce_dsme_send(gpointer msg) { if (dsme_disabled == TRUE) goto EXIT; if (dsme_conn == NULL) { mce_log(LL_CRIT, "Attempt to use dsme_conn uninitialised; aborting!"); // FIXME: this is not how one should exit from mainloop mce_quit_mainloop(); exit(EXIT_FAILURE); } if ((dsmesock_send(dsme_conn, msg)) == -1) { mce_log(LL_CRIT, "dsmesock_send error: %s", g_strerror(errno)); #ifdef MCE_DSME_ERROR_POLICY // FIXME: this is not how one should exit from mainloop mce_quit_mainloop(); exit(EXIT_FAILURE); #endif /* MCE_DSME_ERROR_POLICY */ } EXIT: return; }
/** * Signal handler * * @param signr Signal type */ static void signal_handler(const gint signr) { switch (signr) { case SIGUSR1: /* We'll probably want some way to communicate with MCE */ break; case SIGHUP: /* Possibly for re-reading configuration? */ break; case SIGINT: case SIGQUIT: case SIGTERM: /* Just die if we somehow get here without having a mainloop */ if( !mainloop ) { mce_exit_via_signal(signr); } /* Terminate mainloop */ mce_quit_mainloop(); break; case SIGPIPE: break; default: /* Should never happen */ break; } }
/** * Create a new D-Bus method call reply, with proper error checking * will exit the mainloop if an error occurs * * @param message The DBusMessage to reply to * @return A new DBusMessage */ DBusMessage *dbus_new_method_reply(DBusMessage *const message) { DBusMessage *msg; if ((msg = dbus_message_new_method_return(message)) == NULL) { mce_log(LL_CRIT, "No memory for new reply!"); // FIXME: this is not how one should exit from mainloop mce_quit_mainloop(); exit(EXIT_FAILURE); } return msg; }
static gboolean mce_auto_exit_cb(gpointer aptr) { (void)aptr; if( mce_args.auto_exit <= 0 ) { mce_log(LL_WARN, "exit"); mce_quit_mainloop(); } else { mce_log(LL_WARN, "idle"); g_timeout_add_seconds(mce_args.auto_exit, mce_auto_exit_cb, 0); mce_args.auto_exit = 0; } return FALSE; }
/** * Create a new D-Bus signal, with proper error checking * will exit the mainloop if an error occurs * * @param path The signal path * @param interface The signal interface * @param name The name of the signal to send * @return A new DBusMessage */ DBusMessage *dbus_new_signal(const gchar *const path, const gchar *const interface, const gchar *const name) { DBusMessage *msg; if ((msg = dbus_message_new_signal(path, interface, name)) == NULL) { mce_log(LL_CRIT, "No memory for new signal!"); // FIXME: this is not how one should exit from mainloop mce_quit_mainloop(); exit(EXIT_FAILURE); } return msg; }
/** * Create a new D-Bus error message, with proper error checking * will exit the mainloop if an error occurs * * @param message The DBusMessage that caused the error message to be sent * @param error The message to send * @return A new DBusMessage */ static DBusMessage *dbus_new_error(DBusMessage *const message, const gchar *const error) { DBusMessage *error_msg; if ((error_msg = dbus_message_new_error(message, error, NULL)) == NULL) { mce_log(LL_CRIT, "No memory for new D-Bus error message!"); // FIXME: this is not how one should exit from mainloop mce_quit_mainloop(); exit(EXIT_FAILURE); } return error_msg; }
static gboolean io_error_cb(GIOChannel *source, GIOCondition condition, gpointer data) { /* Silence warnings */ (void)source; (void)condition; (void)data; /* DSME socket closed/error */ mce_log(LL_CRIT, "DSME socket closed/error, exiting..."); // FIXME: this is not how one should exit from mainloop mce_quit_mainloop(); exit(EXIT_FAILURE); }
/** * Create a new D-Bus method call, with proper error checking * will exit the mainloop if an error occurs * * @param service The method call service * @param path The method call path * @param interface The method call interface * @param name The name of the method to call * @return A new DBusMessage */ DBusMessage *dbus_new_method_call(const gchar *const service, const gchar *const path, const gchar *const interface, const gchar *const name) { DBusMessage *msg; if ((msg = dbus_message_new_method_call(service, path, interface, name)) == NULL) { mce_log(LL_CRIT, "Cannot allocate memory for D-Bus method call!"); // FIXME: this is not how one should exit from mainloop mce_quit_mainloop(); exit(EXIT_FAILURE); } return msg; }
/** * Signal handler * * @param signr Signal type */ static void signal_handler(const gint signr) { switch (signr) { case SIGUSR1: /* switch to debug verbosity */ mce_log_set_verbosity(LL_DEBUG); mce_log(LL_DEBUG, "switching to DEBUG verbosity level"); break; case SIGUSR2: /* switch to normal verbosity */ mce_log_set_verbosity(LL_DEBUG); mce_log(LL_DEBUG, "switching to WARNING verbosity level"); mce_log_set_verbosity(LL_WARN); break; case SIGHUP: /* Possibly for re-reading configuration? */ break; case SIGINT: case SIGQUIT: case SIGTERM: /* Just die if we somehow get here without having a mainloop */ if( !mainloop ) { mce_exit_via_signal(signr); } /* Terminate mainloop */ mce_quit_mainloop(); break; case SIGPIPE: break; default: /* Should never happen */ break; } }
/** * Callback for I/O errors * * @param source Unused * @param condition The GIOCondition for the error * @param data The iomon structure * @return Depending on error policy this function either exits * or returns TRUE */ static gboolean io_error_cb(GIOChannel *source, GIOCondition condition, gpointer data) { iomon_struct *iomon = data; gboolean exit_on_error = FALSE; loglevel_t loglevel; /* Silence warnings */ (void)source; if (iomon == NULL) { mce_log(LL_CRIT, "iomon == NULL!"); goto EXIT; } switch (iomon->error_policy) { case MCE_IO_ERROR_POLICY_EXIT: exit_on_error = TRUE; loglevel = LL_CRIT; break; case MCE_IO_ERROR_POLICY_WARN: loglevel = LL_WARN; break; case MCE_IO_ERROR_POLICY_IGNORE: default: /* No log message when ignoring errors */ loglevel = LL_NONE; break; } /* We just got an I/O condition we've already reported * since the last successful read; don't log */ if ((exit_on_error == FALSE) && ((iomon->latest_io_condition & condition) == condition)) { loglevel = LL_NONE; } else { iomon->latest_io_condition |= condition; } if (loglevel != LL_NONE) { mce_log(loglevel, "Error accessing %s (condition: %d). %s", iomon->file, condition, (exit_on_error == TRUE) ? "Exiting" : "Ignoring"); } EXIT: if ((iomon != NULL) && (exit_on_error == TRUE)) { // FIXME: this is not how one should exit from mainloop mce_quit_mainloop(); exit(EXIT_FAILURE); } /* Call error callback if set */ if (iomon->err_callback) { iomon->err_callback(iomon, condition); } return TRUE; }
/** * Callback for successful chunk I/O * * @param source The source of the activity * @param condition The I/O condition * @param data The iomon structure * @return Depending on error policy this function either exits * or returns TRUE */ static gboolean io_chunk_cb(GIOChannel *source, GIOCondition condition, gpointer data) { iomon_struct *iomon = data; gchar *buffer = NULL; gsize bytes_want = 4096; gsize bytes_read = 0; gsize chunks_read = 0; gsize chunks_done = 0; GIOStatus io_status; GError *error = NULL; gboolean status = TRUE; /* Silence warnings */ (void)condition; if (iomon == NULL) { mce_log(LL_CRIT, "iomon == NULL!"); status = FALSE; goto EXIT; } iomon->latest_io_condition = 0; /* Seek to the beginning of the file before reading if needed */ if (iomon->rewind == TRUE) { g_io_channel_seek_position(source, 0, G_SEEK_SET, &error); if( error ) { mce_log(LL_ERR, "%s: seek error: %s", iomon->file, error->message); } /* Reset errno, * to avoid false positives down the line */ errno = 0; g_clear_error(&error); } if( iomon->chunk_size < bytes_want ) { bytes_want -= bytes_want % iomon->chunk_size; } else { bytes_want = iomon->chunk_size; } buffer = g_malloc(bytes_want); #ifdef ENABLE_WAKELOCKS /* Since the locks on kernel side are released once all * events are read, we must obtain the userspace lock * before reading the available data */ wakelock_lock("mce_input_handler", -1); #endif io_status = g_io_channel_read_chars(source, buffer, bytes_want, &bytes_read, &error); /* If the read was interrupted, ignore */ if (io_status == G_IO_STATUS_AGAIN) { g_clear_error(&error); } if( bytes_read % iomon->chunk_size ) { mce_log(LL_WARN, "Incomplete chunks read from: %s", iomon->file); } /* Process the data, and optionally ignore some of it */ if( (chunks_read = bytes_read / iomon->chunk_size) ) { gchar *chunk = buffer; for( ; chunks_done < chunks_read ; chunk += iomon->chunk_size ) { ++chunks_done; if (iomon->callback(chunk, iomon->chunk_size) != TRUE) { continue; } /* if possible, seek to the end of file */ if (iomon->seekable) { g_io_channel_seek_position(iomon->iochan, 0, G_SEEK_END, &error); } /* in any case ignore rest of the data already read */ break; } } mce_log(LL_INFO, "%s: status=%s, data=%d/%d=%d+%d, skipped=%d", iomon->file, io_status_name(io_status), bytes_read, (int)iomon->chunk_size, chunks_read, bytes_read % (int)iomon->chunk_size, chunks_read - chunks_done); #ifdef ENABLE_WAKELOCKS /* Release the lock after we're done with processing it */ wakelock_unlock("mce_input_handler"); #endif g_free(buffer); /* Were there any errors? */ if (error != NULL) { mce_log(LL_ERR, "Error when reading from %s: %s", iomon->file, error->message); if ((error->code == G_IO_CHANNEL_ERROR_FAILED) && (errno == ENODEV) && (iomon->seekable)) { errno = 0; g_clear_error(&error); g_io_channel_seek_position(iomon->iochan, 0, G_SEEK_END, &error); if( error ) { mce_log(LL_ERR, "%s: seek error: %s", iomon->file, error->message); } } else { status = FALSE; } /* Reset errno, * to avoid false positives down the line */ errno = 0; g_clear_error(&error); } else if ((bytes_read == 0) && (io_status != G_IO_STATUS_EOF) && (io_status != G_IO_STATUS_AGAIN)) { mce_log(LL_ERR, "Empty read from %s", iomon->file); } EXIT: if ((status == FALSE) && (iomon != NULL) && (iomon->error_policy == MCE_IO_ERROR_POLICY_EXIT)) { // FIXME: this is not how one should exit from mainloop mce_quit_mainloop(); exit(EXIT_FAILURE); } return TRUE; }
/** * Callback for successful string I/O * * @param source The source of the activity * @param condition The I/O condition * @param data The iomon structure * @return Depending on error policy this function either exits * or returns TRUE */ static gboolean io_string_cb(GIOChannel *source, GIOCondition condition, gpointer data) { iomon_struct *iomon = data; gchar *str = NULL; gsize bytes_read; GError *error = NULL; gboolean status = TRUE; /* Silence warnings */ (void)condition; if (iomon == NULL) { mce_log(LL_CRIT, "iomon == NULL!"); status = FALSE; goto EXIT; } iomon->latest_io_condition = 0; /* Seek to the beginning of the file before reading if needed */ if (iomon->rewind == TRUE) { g_io_channel_seek_position(source, 0, G_SEEK_SET, &error); if( error ) { mce_log(LL_ERR, "%s: seek error: %s", iomon->file, error->message); } /* Reset errno, * to avoid false positives down the line */ errno = 0; g_clear_error(&error); } g_io_channel_read_line(source, &str, &bytes_read, NULL, &error); /* Errors and empty reads are nasty */ if (error != NULL) { mce_log(LL_ERR, "Error when reading from %s: %s", iomon->file, error->message); status = FALSE; } else if ((bytes_read == 0) || (str == NULL) || (strlen(str) == 0)) { mce_log(LL_ERR, "Empty read from %s", iomon->file); } else { (void)iomon->callback(str, bytes_read); } g_free(str); /* Reset errno, * to avoid false positives down the line */ errno = 0; g_clear_error(&error); EXIT: if ((status == FALSE) && (iomon != NULL) && (iomon->error_policy == MCE_IO_ERROR_POLICY_EXIT)) { // FIXME: this is not how one should exit from mainloop mce_quit_mainloop(); exit(EXIT_FAILURE); } return TRUE; }
/** * Callback for pending I/O from dsmesock * * XXX: is the error policy reasonable? * * @param source Unused * @param condition Unused * @param data Unused * @return TRUE on success, FALSE on failure */ static gboolean io_data_ready_cb(GIOChannel *source, GIOCondition condition, gpointer data) { dsmemsg_generic_t *msg; DSM_MSGTYPE_STATE_CHANGE_IND *msg2; system_state_t oldstate = datapipe_get_gint(system_state_pipe); system_state_t newstate = MCE_STATE_UNDEF; (void)source; (void)condition; (void)data; if (dsme_disabled == TRUE) goto EXIT; if ((msg = (dsmemsg_generic_t *)dsmesock_receive(dsme_conn)) == NULL) goto EXIT; if (DSMEMSG_CAST(DSM_MSGTYPE_CLOSE, msg)) { /* DSME socket closed: try once to reopen; * if that fails, exit */ mce_log(LL_ERR, "DSME socket closed; trying to reopen"); if ((init_dsmesock()) == FALSE) { // FIXME: this is not how one should exit from mainloop mce_quit_mainloop(); exit(EXIT_FAILURE); } } else if (DSMEMSG_CAST(DSM_MSGTYPE_PROCESSWD_PING, msg)) { dsme_send_pong(); } else if ((msg2 = DSMEMSG_CAST(DSM_MSGTYPE_STATE_CHANGE_IND, msg))) { newstate = normalise_dsme_state(msg2->state); mce_log(LL_DEBUG, "DSME device state change: %d", newstate); /* If we're changing to a different state, * add the transition flag, UNLESS the old state * was MCE_STATE_UNDEF */ if ((oldstate != newstate) && (oldstate != MCE_STATE_UNDEF)) mce_add_submode_int32(MCE_TRANSITION_SUBMODE); switch (newstate) { case MCE_STATE_USER: execute_datapipe_output_triggers(&led_pattern_activate_pipe, MCE_LED_PATTERN_DEVICE_ON, USE_INDATA); break; case MCE_STATE_ACTDEAD: case MCE_STATE_BOOT: case MCE_STATE_UNDEF: break; case MCE_STATE_SHUTDOWN: case MCE_STATE_REBOOT: execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, MCE_LED_PATTERN_DEVICE_ON, USE_INDATA); break; default: break; } execute_datapipe(&system_state_pipe, GINT_TO_POINTER(newstate), USE_INDATA, CACHE_INDATA); } else { mce_log(LL_DEBUG, "Unknown message type (%x) received from DSME!", msg->type_); /* <- unholy access of a private member */ } free(msg); EXIT: return TRUE; }