Fawkes API  Fawkes Development Version
main.cpp
1 
2 /***************************************************************************
3  * main.cpp - Fawkes main application
4  *
5  * Created: Sun Apr 11 19:34:09 2010
6  * Copyright 2006-2010 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include "beep.h"
25 
26 #include <blackboard/remote.h>
27 #include <core/threading/thread.h>
28 #include <interfaces/SwitchInterface.h>
29 #include <utils/system/argparser.h>
30 #include <utils/system/signal.h>
31 #include <utils/time/time.h>
32 
33 #include <cmath>
34 #include <cstdio>
35 #include <unistd.h>
36 #ifdef HAVE_LIBDAEMON
37 # include <libdaemon/dfork.h>
38 # include <libdaemon/dlog.h>
39 # include <libdaemon/dpid.h>
40 # include <sys/stat.h>
41 # include <sys/wait.h>
42 
43 # include <cerrno>
44 # include <cstring>
45 #endif
46 
47 using namespace std;
48 using namespace fawkes;
49 
50 /** Fawkes beep daemon.
51  *
52  * @author Tim Niemueller
53  */
54 class FawkesBeepDaemon : public Thread, public SignalHandler
55 {
56 public:
57  /** Constructor. */
58  FawkesBeepDaemon() : Thread("FawkesBeepDaemon", Thread::OPMODE_CONTINUOUS)
59  {
60  until_ = NULL;
61  bb_ = NULL;
62  switch_if_ = NULL;
63  }
64 
65  virtual void
66  loop()
67  {
68  while (!(bb_ && bb_->is_alive() && switch_if_->is_valid())) {
69  if (bb_) {
70  printf("Lost connection to blackboard\n");
71  bb_->close(switch_if_);
72  delete bb_;
73  bb_ = NULL;
74  }
75  try {
76  printf("Trying to connect to remote BB...");
77  bb_ = new RemoteBlackBoard("localhost", 1910);
78  switch_if_ = bb_->open_for_writing<SwitchInterface>("Beep");
79  printf("succeeded\n");
80  } catch (Exception &e) {
81  printf("failed\n");
82  delete bb_;
83  bb_ = NULL;
84  sleep(5);
85  }
86  }
87 
88  if (until_) {
89  Time now;
90  if ((now - until_) >= 0) {
91  beep_.beep_off();
92  delete until_;
93  until_ = NULL;
94  }
95  }
96 
97  while (!switch_if_->msgq_empty()) {
98  if (switch_if_->msgq_first_is<SwitchInterface::SetMessage>()) {
99  SwitchInterface::SetMessage *msg = switch_if_->msgq_first<SwitchInterface::SetMessage>();
100  if (msg->value() > 0.0) {
101  beep_.beep_on(msg->value());
102  } else if (msg->is_enabled()) {
103  beep_.beep_on();
104  } else {
105  beep_.beep_off();
106  }
107 
108  } else if (switch_if_->msgq_first_is<SwitchInterface::EnableDurationMessage>()) {
110  switch_if_->msgq_first<SwitchInterface::EnableDurationMessage>();
111  float duration = fabs(msg->duration());
112  float value = fabs(msg->value());
113 
114  delete until_;
115  until_ = new Time();
116  *until_ += duration;
117  beep_.beep_on(value);
118  } else if (switch_if_->msgq_first_is<SwitchInterface::EnableSwitchMessage>()) {
119  beep_.beep_on();
120  } else if (switch_if_->msgq_first_is<SwitchInterface::DisableSwitchMessage>()) {
121  beep_.beep_off();
122  }
123 
124  switch_if_->msgq_pop();
125  }
126 
127  usleep(10000);
128  }
129 
130  /** Handle signals.
131  * @param signum signal number
132  */
133  void
134  handle_signal(int signum)
135  {
136  this->cancel();
137  }
138 
139 private:
140  BeepController beep_;
141  BlackBoard * bb_;
142  SwitchInterface *switch_if_;
143 
144  Time *until_;
145 };
146 
147 void
148 usage(const char *progname)
149 {
150 }
151 
152 #ifdef HAVE_LIBDAEMON
153 void
154 daemonize_cleanup()
155 {
156  daemon_retval_send(-1);
157  daemon_retval_done();
158  daemon_pid_file_remove();
159 }
160 
161 pid_t
162 daemonize(int argc, char **argv)
163 {
164  pid_t pid;
165  mode_t old_umask = umask(0);
166 
167  // Prepare for return value passing
168  daemon_retval_init();
169 
170  // Do the fork
171  if ((pid = daemon_fork()) < 0) {
172  return -1;
173 
174  } else if (pid) { // the parent
175  int ret;
176 
177  // Wait for 20 seconds for the return value passed from the daemon process
178  if ((ret = daemon_retval_wait(20)) < 0) {
179  daemon_log(LOG_ERR, "Could not recieve return value from daemon process.");
180  return -1;
181  }
182 
183  if (ret != 0) {
184  daemon_log(LOG_ERR, "*** Daemon startup failed, see syslog for details. ***");
185  switch (ret) {
186  case 1: daemon_log(LOG_ERR, "Daemon failed to close file descriptors"); break;
187  case 2: daemon_log(LOG_ERR, "Daemon failed to create PID file"); break;
188  }
189  return -1;
190  } else {
191  return pid;
192  }
193 
194  } else { // the daemon
195 # ifdef DAEMON_CLOSE_ALL_AVAILABLE
196  if (daemon_close_all(-1) < 0) {
197  daemon_log(LOG_ERR, "Failed to close all file descriptors: %s", strerror(errno));
198  // Send the error condition to the parent process
199  daemon_retval_send(1);
200  return -1;
201  }
202 # endif
203 
204  // Create the PID file
205  if (daemon_pid_file_create() < 0) {
206  printf("Could not create PID file (%s).", strerror(errno));
207  daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno));
208 
209  // Send the error condition to the parent process
210  daemon_retval_send(2);
211  return -1;
212  }
213 
214  // Send OK to parent process
215  daemon_retval_send(0);
216 
217  daemon_log(LOG_INFO, "Sucessfully started");
218 
219  umask(old_umask);
220  return 0;
221  }
222 }
223 
224 /** Global variable containing the path to the PID file.
225  * unfortunately needed for libdaemon */
226 const char *fawkes_pid_file;
227 
228 /** Function that returns the PID file name.
229  * @return PID file name
230  */
231 const char *
232 fawkes_daemon_pid_file_proc()
233 {
234  return fawkes_pid_file;
235 }
236 #endif // HAVE_LIBDAEMON
237 
238 /** Fawkes application.
239  * @param argc argument count
240  * @param argv array of arguments
241  */
242 int
243 main(int argc, char **argv)
244 {
245  ArgumentParser *argp = new ArgumentParser(argc, argv, "hD::ks");
246 
247 #ifdef HAVE_LIBDAEMON
248  pid_t pid;
249 
250  if (argp->has_arg("D")) {
251  // Set identification string for the daemon for both syslog and PID file
252  daemon_pid_file_ident = daemon_log_ident = daemon_ident_from_argv0(argv[0]);
253  if (argp->arg("D") != NULL) {
254  fawkes_pid_file = argp->arg("D");
255  daemon_pid_file_proc = fawkes_daemon_pid_file_proc;
256  }
257 
258  // We should daemonize, check if we were called to kill a daemonized copy
259  if (argp->has_arg("k")) {
260  // Check that the daemon is not run twice a the same time
261  if ((pid = daemon_pid_file_is_running()) < 0) {
262  daemon_log(LOG_ERR, "Fawkes daemon not running.");
263  return 1;
264  }
265 
266  // Kill daemon with SIGINT
267  int ret;
268  if ((ret = daemon_pid_file_kill_wait(SIGINT, 5)) < 0) {
269  daemon_log(LOG_WARNING, "Failed to kill daemon");
270  }
271  return (ret < 0) ? 1 : 0;
272  }
273 
274  if (argp->has_arg("s")) {
275  // Check daemon status
276  return (daemon_pid_file_is_running() < 0);
277  }
278 
279  // Check that the daemon is not run twice a the same time
280  if ((pid = daemon_pid_file_is_running()) >= 0) {
281  daemon_log(LOG_ERR, "Daemon already running on (PID %u)", pid);
282  return 201;
283  }
284 
285  pid = daemonize(argc, argv);
286  if (pid < 0) {
287  daemonize_cleanup();
288  return 201;
289  } else if (pid) {
290  // parent
291  return 0;
292  } // else child, continue as usual
293  }
294 #else
295  if (argp->has_arg("D")) {
296  printf("Daemonizing support is not available.\n"
297  "(libdaemon[-devel] was not available at compile time)\n");
298  return 202;
299  }
300 #endif
301 
302  Thread::init_main();
303 
304  if (argp->has_arg("h")) {
305  usage(argv[0]);
306  delete argp;
307  return 0;
308  }
309 
310  FawkesBeepDaemon beepd;
311  SignalManager::register_handler(SIGINT, &beepd);
312  SignalManager::register_handler(SIGTERM, &beepd);
313 
314  beepd.start();
315  beepd.join();
316 
317  Thread::destroy_main();
318 
319 #ifdef HAVE_LIBDAEMON
320  if (argp->has_arg("D")) {
321  daemonize_cleanup();
322  }
323 #endif
324 
325  delete argp;
326  return 0;
327 }
Simple speaker beep controller.
Definition: beep.h:25
Fawkes beep daemon.
Definition: main.cpp:55
virtual void loop()
Code to execute in the thread.
Definition: main.cpp:66
void handle_signal(int signum)
Handle signals.
Definition: main.cpp:134
FawkesBeepDaemon()
Constructor.
Definition: main.cpp:58
Parse command line arguments.
Definition: argparser.h:64
const char * arg(const char *argn)
Get argument value.
Definition: argparser.cpp:177
bool has_arg(const char *argn)
Check if argument has been supplied.
Definition: argparser.cpp:165
The BlackBoard abstract class.
Definition: blackboard.h:46
Base class for exceptions in Fawkes.
Definition: exception.h:36
Remote BlackBoard.
Definition: remote.h:49
Interface for signal handling.
Definition: signal.h:36
DisableSwitchMessage Fawkes BlackBoard Interface Message.
EnableDurationMessage Fawkes BlackBoard Interface Message.
float duration() const
Get duration value.
EnableSwitchMessage Fawkes BlackBoard Interface Message.
SetMessage Fawkes BlackBoard Interface Message.
float value() const
Get value value.
bool is_enabled() const
Get enabled value.
SwitchInterface Fawkes BlackBoard Interface.
Thread class encapsulation of pthreads.
Definition: thread.h:46
void start(bool wait=true)
Call this method to start the thread.
Definition: thread.cpp:499
void join()
Join the thread.
Definition: thread.cpp:597
A class for handling time.
Definition: time.h:93
Fawkes library namespace.