blob: 32f7d405106dcf99de2ffebf51575fbf11ebfd75 [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// A simple daemon to detect and access PTP/MTP devices.
#include <glib.h>
#include <glib-object.h>
#include <signal.h>
#include <sys/signalfd.h>
#include <dbus-c++/glib-integration.h>
#include <base/at_exit.h>
#include <base/command_line.h>
#include <base/logging.h>
#include <base/macros.h>
#include <base/strings/string_number_conversions.h>
#include "build_config.h"
#include "daemon.h"
#include "service_constants.h"
#if defined(CROS_BUILD)
#include <brillo/syslog_logging.h>
#endif
using base::CommandLine;
using mtpd::Daemon;
// Messages logged at a level lower than this don't get logged anywhere.
static const char kMinLogLevelSwitch[] = "minloglevel";
void SetupLogging() {
#if defined(CROS_BUILD)
brillo::InitLog(brillo::kLogToSyslog);
#endif
std::string log_level_str =
CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kMinLogLevelSwitch);
int log_level = 0;
if (base::StringToInt(log_level_str, &log_level) && log_level >= 0)
logging::SetMinLogLevel(log_level);
}
// This callback will be invoked once there is a new device event that
// should be processed by the Daemon::ProcessDeviceEvents().
gboolean DeviceEventCallback(GIOChannel* /* source */,
GIOCondition /* condition */,
gpointer data) {
Daemon* daemon = reinterpret_cast<Daemon*>(data);
daemon->ProcessDeviceEvents();
// This function should always return true so that the main loop
// continues to select on device event file descriptor.
return true;
}
// This callback will be inovked when this process receives SIGINT or SIGTERM.
gboolean TerminationSignalCallback(GIOChannel* /* source */,
GIOCondition /* condition */,
gpointer data) {
LOG(INFO) << "Received a signal to terminate the daemon";
GMainLoop* loop = reinterpret_cast<GMainLoop*>(data);
g_main_loop_quit(loop);
// This function can return false to remove this signal handler as we are
// quitting the main loop anyway.
return false;
}
int main(int argc, char** argv) {
// g_type_init() is deprecated since glib 2.36.
#if !(GLIB_CHECK_VERSION(2, 36, 0))
::g_type_init();
#endif
// g_thread_init() is deprecated since glib 2.32 and the symbol is no longer
// exported since glib 2.34:
//
// https://developer.gnome.org/glib/2.32/glib-Deprecated-Thread-APIs.html
//
// To be compatible with various versions of glib, only call g_thread_init()
// when using glib older than 2.32.0.
#if !(GLIB_CHECK_VERSION(2, 32, 0))
g_thread_init(NULL);
#endif
// Needed by base::RandBytes() and other Chromium bits that expects
// an AtExitManager to exist.
base::AtExitManager exit_manager;
CommandLine::Init(argc, argv);
SetupLogging();
LOG(INFO) << "Creating a GMainLoop";
GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
CHECK(loop) << "Failed to create a GMainLoop";
LOG(INFO) << "Creating the D-Bus dispatcher";
DBus::Glib::BusDispatcher dispatcher;
DBus::default_dispatcher = &dispatcher;
dispatcher.attach(NULL);
LOG(INFO) << "Creating the mtpd server";
DBus::Connection server_conn = DBus::Connection::SystemBus();
CHECK(server_conn.acquire_name(mtpd::kMtpdServiceName));
Daemon daemon(&server_conn);
// Set up a monitor for handling device events.
g_io_add_watch_full(g_io_channel_unix_new(daemon.GetDeviceEventDescriptor()),
G_PRIORITY_HIGH_IDLE,
GIOCondition(G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_NVAL),
DeviceEventCallback,
&daemon,
NULL);
// TODO(thestig) Switch back to g_unix_signal_add() once Chromium no longer
// supports a Linux system with glib older than 2.30.
//
// Set up a signal socket and monitor it.
sigset_t signal_set;
CHECK_EQ(0, sigemptyset(&signal_set));
CHECK_EQ(0, sigaddset(&signal_set, SIGINT));
CHECK_EQ(0, sigaddset(&signal_set, SIGTERM));
int signal_fd = signalfd(-1, &signal_set, 0);
PCHECK(signal_fd >= 0);
// Set up a monitor for |signal_fd|.
g_io_add_watch_full(g_io_channel_unix_new(signal_fd),
G_PRIORITY_HIGH_IDLE,
GIOCondition(G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_NVAL),
TerminationSignalCallback,
loop,
NULL);
g_main_loop_run(loop);
LOG(INFO) << "Cleaning up and exiting";
g_main_loop_unref(loop);
return 0;
}