I have ran the following code in Ubuntu. DBUS code crashes when placed inside daemon while process, but runs fine in an independent standalone main() function without daemon broiler plate code structure. Note: I will later re-factor the code in class structure once I solve the problem.
The following code crashes.
//file3=giommdaemontest
//g++ -g -std=c++0x $(file3).cc -o $(file3) `pkg-config --libs --cflags giomm-2.4`
/* Copyright (C) 2011 The giomm Development Team
* Copyright (C) 2013 enthusiasticgeek (Demo for StackOverflow)
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* This is a basic server providing a clock like functionality. Clients can
* get the current time, set the alarm and get notified when the alarm time is
* reached. It is basic because there is only one global alarm which any
* client can set. Clients listening for the alarm signal will be notified by
* use of the global alarm signal. The server should be easily modifiable to
* allow per-client alarms, but that is left as an exercise.
*
* Along with the above it provides a method to get its stdout's file
* descriptor to test the Gio::DBus::Message API.
*/
#include <giomm.h>
#include <glibmm.h>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
namespace
{
static Glib::RefPtr<Gio::DBus::NodeInfo> introspection_data;
static Glib::ustring introspection_xml =
"<node>"
" <interface name='enthusiasticgeek.test.DBus'>"
" <method name='GetTime'>"
" <arg type='s' name='iso8601' direction='out'/>"
" </method>"
" <method name='ParamFunction'>"
" <arg type='s' name='param1' direction='in'/>"
" <arg type='s' name='param2' direction='in'/>"
" <arg type='s' name='param3' direction='out'/>"
" <arg type='s' name='param4' direction='out'/>"
" </method>"
// The time of the alarm as an iso8601 string.
// " <property type='s' name='myproperty' access='readwrite'/>"
" <signal name='EventOccurred'>"
" <arg type='s' name='some_string'/>"
" </signal> "
" </interface>"
"</node>";
guint registered_id = 0;
// Stores the current alarm.
static Glib::TimeVal curr_alarm;
Glib::RefPtr<Gio::DBus::Connection> m_connection;
const char *m_path;
const char *m_interface_name;
} // anonymous namespace
void emit_signal(const Glib::ustring & name, const Glib::VariantContainerBase & parameters)
{
GError *error;
error = NULL;
/* g_dbus_connection_emit_signal (connection,
NULL,
"/org/gtk/GDBus/PeerTestObject",
"org.gtk.GDBus.PeerTestInterface",
"PeerSignal",
NULL,
&error); */
//work-around glibmm bug 645072
if( (m_connection) && (m_path) && (m_interface_name)){
std::cout << "info is " << m_connection << " and " << m_path << " and " << m_interface_name << std::endl;
g_dbus_connection_emit_signal(m_connection->gobj(), NULL, m_path, m_interface_name, name.c_str(),
const_cast<GVariant*>(parameters.gobj()), &error);
}
}
void EventOccurred(const std::string & some_string)
{
emit_signal("EventOccurred", Glib::VariantContainerBase::create_tuple(Glib::Variant<Glib::ustring>::create(some_string)));
}
static void on_method_call(const Glib::RefPtr<Gio::DBus::Connection>& conn /* connection */,
const Glib::ustring& sender /* sender */, const Glib::ustring& path/* object_path */,
const Glib::ustring& interface/* interface_name */, const Glib::ustring& method_name,
const Glib::VariantContainerBase& parameters,
const Glib::RefPtr<Gio::DBus::MethodInvocation>& invocation)
{
m_connection = conn;
m_path = path.c_str();
m_interface_name = interface.c_str();
if(method_name == "GetTime")
{
std::cout << "GetTime function" << std::endl;
std::cout << "test this function" << std::endl;
Glib::TimeVal curr_time;
curr_time.assign_current_time();
const Glib::ustring time_str = curr_time.as_iso8601();
const Glib::Variant<Glib::ustring> time_var =
Glib::Variant<Glib::ustring>::create(time_str);
// Create the tuple.
Glib::VariantContainerBase response =
Glib::VariantContainerBase::create_tuple(time_var);
// Return the tuple with the included time.
invocation->return_value(response);
}
else if(method_name == "ParamFunction")
{
// Get the parameter tuple.
Glib::Variant<Glib::ustring> param1;
Glib::Variant<Glib::ustring> param2;
if(parameters.get_n_children() == 2) {
parameters.get_child(param1, 0);
parameters.get_child(param2, 1);
}
// Get the strings.
const Glib::ustring param1_str = param1.get();
std::cout << "received data param[0]" << param1_str << std::endl;
const Glib::ustring param2_str = param2.get();
std::cout << "received data param[1]" << param2_str << std::endl;
std::string string1 = " Ok I received the name.";
std::string string2 = " Ok I received the profession.";
std::vector<Glib::VariantBase> parameters_response;
parameters_response.reserve(2); ;
parameters_response.push_back(Glib::Variant<Glib::ustring>::create(string1));
parameters_response.push_back(Glib::Variant<Glib::ustring>::create(string2));
Glib::VariantContainerBase params_response = Glib::VariantContainerBase::create_tuple(parameters_response);
invocation->return_value(params_response);
/*
if(!curr_alarm.assign_from_iso8601(time_str))
{
// If setting alarm was not successful, return an error.
Gio::DBus::Error error(Gio::DBus::Error::INVALID_ARGS,
"Alarm string is not in ISO8601 format.");
invocation->return_error(error);
}
*/
}
else
{
// Non-existent method on the interface.
Gio::DBus::Error error(Gio::DBus::Error::UNKNOWN_METHOD,
"Method does not exist.");
invocation->return_error(error);
}
}
//This must be a global instance. See the InterfaceVTable documentation.
//TODO: Make that unnecessary.
const Gio::DBus::InterfaceVTable interface_vtable(sigc::ptr_fun(&on_method_call));
void on_bus_acquired(const Glib::RefPtr<Gio::DBus::Connection>& connection, const Glib::ustring& /* name */)
{
// Export an object to the bus:
// See https://bugzilla.gnome.org/show_bug.cgi?id=646417 about avoiding
// the repetition of the interface name:
try
{
registered_id = connection->register_object("/enthusiasticgeek/test/DBus",
introspection_data->lookup_interface(),
interface_vtable);
}
catch(const Glib::Error& ex)
{
std::cerr << "Registration of object failed." << std::endl;
}
return;
}
void on_name_acquired(const Glib::RefPtr<Gio::DBus::Connection>& /* connection */, const Glib::ustring& /* name */)
{
//TODO: What is this good for? See https://bugzilla.gnome.org/show_bug.cgi?id=646427
}
void on_name_lost(const Glib::RefPtr<Gio::DBus::Connection>& connection, const Glib::ustring& /* name */)
{
connection->unregister_object(registered_id);
}
void on_get_property(Glib::VariantBase& property,
const Glib::RefPtr<Gio::DBus::Connection>& /* connection */,
const Glib::ustring& /* sender */, const Glib::ustring& /* object_path */,
const Glib::ustring& /* interface_name */, const Glib::ustring& property_name)
{
if(property_name == "myproperty")
{
if(curr_alarm.valid())
{
std::cout << "property is called " << std::endl;
//Glib::ustring alarm_str = curr_alarm.as_iso8601();
//Glib::Variant<Glib::ustring> alarm_var =
// Glib::Variant<Glib::ustring>::create(alarm_str);
//property = alarm_var;
}
else
{
throw Gio::Error(Gio::Error::FAILED, "Property has not been set.");
}
}
else
{
throw Gio::DBus::Error(Gio::DBus::Error::FAILED, "Unknown property name.");
}
}
/** TODO: This code does not seem to be used. murrayc.
bool on_set_property(const Glib::RefPtr<Gio::DBus::Connection>& connection,
const Glib::ustring& sender, const Glib::ustring& object_path,
const Glib::ustring& interface_name, const Glib::ustring& property_name,
const Glib::VariantBase& value)
{
if(property_name == "myproperty")
{
}
else
{
}
}
*/
bool TimeoutCheck()
{
std::cout << "Checking for timeout ..." << std::endl;
EventOccurred(" <<< Timeout Event has occurred >>>");
return true;
}
int main(int, char**)
{
/* Our process ID and Session ID */
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
/* Daemon-specific initialization goes here */
while(1){
std::locale::global(std::locale(""));
Gio::init();
try
{
introspection_data = Gio::DBus::NodeInfo::create_for_xml(introspection_xml);
}
catch(const Glib::Error& ex)
{
std::cerr << "Unable to create introspection data: " << ex.what() <<
"." << std::endl;
return 1;
}
const guint id = Gio::DBus::own_name(Gio::DBus::BUS_TYPE_SESSION,
"enthusiasticgeek.test.DBus",
sigc::ptr_fun(&on_bus_acquired),
sigc::ptr_fun(&on_name_acquired),
sigc::ptr_fun(&on_name_lost));
//create timeout signal
static const unsigned delayInMillis = 5000;
sigc::slot<bool> tslot = sigc::ptr_fun(&TimeoutCheck);
Glib::signal_timeout().connect(tslot, delayInMillis);
//Keep the service running until the process is killed:
Glib::RefPtr<Glib::MainLoop> loop = Glib::MainLoop::create();
loop->run();
Gio::DBus::unown_name(id);
}
return EXIT_SUCCESS;
}
However the following works fine
int main(int, char**)
{
//while(1){
std::locale::global(std::locale(""));
Gio::init();
try
{
introspection_data = Gio::DBus::NodeInfo::create_for_xml(introspection_xml);
}
catch(const Glib::Error& ex)
{
std::cerr << "Unable to create introspection data: " << ex.what() <<
"." << std::endl;
return 1;
}
const guint id = Gio::DBus::own_name(Gio::DBus::BUS_TYPE_SESSION,
"enthusiasticgeek.test.DBus",
sigc::ptr_fun(&on_bus_acquired),
sigc::ptr_fun(&on_name_acquired),
sigc::ptr_fun(&on_name_lost));
//create timeout signal
static const unsigned delayInMillis = 5000;
sigc::slot<bool> tslot = sigc::ptr_fun(&TimeoutCheck);
Glib::signal_timeout().connect(tslot, delayInMillis);
//Keep the service running until the process is killed:
Glib::RefPtr<Glib::MainLoop> loop = Glib::MainLoop::create();
loop->run();
Gio::DBus::unown_name(id);
//}
return EXIT_SUCCESS;
}
Ok some tweaks in this piece of code help run the code correctly
...
pid_t pid, sid;
static int pipefd[2];
pid = fork();
if (pid < 0) { //error
close(pipefd[0]);
close(pipefd[1]);
exit(EXIT_FAILURE);
}
if (pid > 0) { //parent process
close(pipefd[1]); //close write/ output
printf("%d\n", pid);
char buf[32];
while (read(pipefd[0], buf, sizeof(buf)) > 0) {
printf("%s", buf);
}
printf("\n");
close(pipefd[0]); //close read/ input
exit(EXIT_SUCCESS);
}
// running as the child, continue daemonizing
close(pipefd[0]);
umask(0);
...