1
/*
2
    Copyright (c) 2009 digitalSTROM.org, Zurich, Switzerland
3
4
    Author: Patrick Staehlin, futureLAB AG <pstaehlin@futurelab.ch>
5
6
    This file is part of digitalSTROM Server.
7
8
    digitalSTROM Server is free software: you can redistribute it and/or modify
9
    it under the terms of the GNU General Public License as published by
10
    the Free Software Foundation, either version 3 of the License, or
11
    (at your option) any later version.
12
13
    digitalSTROM Server is distributed in the hope that it will be useful,
14
    but WITHOUT ANY WARRANTY; without even the implied warranty of
15
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
    GNU General Public License for more details.
17
18
    You should have received a copy of the GNU General Public License
19
    along with digitalSTROM Server. If not, see <http://www.gnu.org/licenses/>.
20
21
    Last change $Date$
22
    by $Author$
23
*/
24
25
26
#ifdef HAVE_CONFIG_H
27
  #include "config.h"
28
#endif
29
30
#include "core/base.h"
31
#include "core/dss.h"
32
#include "core/logger.h"
33
#include "core/ds485client.h"
34
#include "core/ds485/ds485.h"
35
#ifdef WITH_TESTS
36
#include "tests/tests.h"
37
#endif
38
39
#include <ctime>
40
#include <csignal>
41
#include <getopt.h>
42
43
#include <boost/program_options.hpp>
44
45
#ifdef USE_LIBXML
46
  #include <libxml/tree.h>
47
  #include <libxml/encoding.h>
48
#endif
49
50
#include <iostream>
51
52
using namespace std;
53
namespace po = boost::program_options;
54
55
pair<string, string> parse_prop(const string& s) {
56
    if ((s.find("--prop") == 0) && (s.length() >=7)) {
57
      return make_pair("prop", s.substr(7));
58
    } else {
59
      return make_pair(string(), string());
60
    }
61
} // parse_prop
62
63
int main (int argc, char* argv[]) {
64
65
  if (!setlocale(LC_CTYPE, "")) {
66
    dss::Logger::getInstance()->log("Can't set the specified locale! Check LANG, LC_CTYPE, LC_ALL.", lsError);
67
    return 1;
68
  }
69
70
  // make sure timezone gets set
71
  tzset();
72
73
  char* tzNameCopy = strdup("GMT");
74
  tzname[0] = tzname[1] = tzNameCopy;
75
  timezone = 0;
76
  daylight = 0;
77
78
  setenv("TZ", "UTC", 1);
79
80
81
#ifndef WIN32
82
  srand((getpid() << 16) ^ getuid() ^ time(0));
83
  // disable broken pipe signal
84
  signal(SIGPIPE, SIG_IGN);
85
  signal(SIGUSR1, dss::DSS::handleSignal);
86
  signal(SIGTERM, dss::DSS::handleSignal);
87
  signal(SIGINT, dss::DSS::handleSignal);
88
#else
89
  srand( (int)time( (time_t)NULL ) );
90
  WSAData dat;
91
  WSAStartup( 0x1010, &dat );
92
#endif
93
94
95
  vector<string> properties;
96
#ifdef USE_LIBXML
97
  // let libXML initialize its parser
98
  xmlInitParser();
99
#endif
100
101
102
  po::options_description desc("Allowed options");
103
  desc.add_options()
104
      ("help", "produce help message")
105
#ifdef WITH_TESTS
106
      ("dont-runtests", po::value<bool>(), "if set, no tests will be run")
107
      ("quit-after-tests", po::value<bool>(), "if set, the application will terminate after running its tests")
108
#endif
109
#ifndef __APPLE__
110
      ("sniff,s", po::value<string>(), "start the ds485 sniffer")
111
#endif
112
      ("prop", po::value<vector<string> >(), "sets a property")
113
      ("version,v", "print version information and exit")
114
      ("datadir,D", po::value<string>(), "set data directory")
115
      ("webrootdir,w", po::value<string>(), "set webroot directory")
116
      ("configdir,c", po::value<string>(), "set config directory")
117
      ("config,C", po::value<string>(), "set configuration file to use")
118
#ifndef __APPLE__ // daemon() is marked as deprecated on OS X
119
      ("daemonize,d", "start as a daemon")
120
#endif
121
  ;
122
123
  po::variables_map vm;
124
  try {
125
    po::store(po::command_line_parser(argc, argv).options(desc).extra_parser(parse_prop)
126
          .run(), vm);
127
  } catch (const po::error& e) {
128
    cout << "Error parsing command line: " << e.what() << endl;
129
    return 1;
130
  }
131
  po::notify(vm);
132
133
  if (vm.count("help")) {
134
      cout << desc << "\n";
135
      return 1;
136
  }
137
138
  if (vm.count("version")) {
139
    std::cout << dss::DSS::versionString() << std::endl;
140
    return 1;
141
  }
142
143
  bool runTests = true;
144
  if(vm.count("dont-runtests")) {
145
    runTests = !vm["dont-runtests"].as<bool>();
146
  }
147
148
  bool quitAfterTests = false;
149
  if(runTests && vm.count("quit-after-tests")) {
150
    quitAfterTests = vm["quit-after-tests"].as<bool>();
151
  }
152
153
  if(vm.count("prop")) {
154
      properties = vm["prop"].as< vector<string> >();
155
  }
156
157
  if(vm.count("datadir")) {
158
    properties.push_back("/config/datadirectory=" + 
159
                         vm["datadir"].as<string>());
160
  }
161
162
  if(vm.count("webrootdir")) {
163
    properties.push_back("/config/webrootdirectory=" + 
164
                          vm["webrootdir"].as<string>());
165
  }
166
167
  if(vm.count("configdir")) {
168
    properties.push_back("/config/configdirectory=" + 
169
                         vm["configdir"].as<string>());
170
  }
171
172
  string config_file;
173
  if(vm.count("config")) {
174
    config_file = vm["config"].as<string>();
175
  }
176
177
  string snifferDev;
178
  bool startSniffer = false;
179
180
  if(vm.count("sniff")) {
181
    startSniffer = true;
182
    snifferDev = vm["sniff"].as<string>();
183
  }
184
185
#ifndef __APPLE__
186
  bool daemonize = vm.count("daemonize") != 0;
187
#endif
188
189
#ifdef WITH_TESTS
190
  cout << "compiled WITH_TESTS" << endl;
191
  if(runTests) {
192
    cout << "running tests" << endl;
193
    dss::Tests::run();
194
    cout << "done running tests" << endl;
195
  }
196
#endif
197
198
  if(!quitAfterTests && startSniffer) {
199
#ifndef __APPLE__
200
    dss::DS485FrameSniffer sniffer(snifferDev);
201
    sniffer.run();
202
    while(true) {
203
      dss::sleepSeconds(10);
204
    }
205
#endif
206
  } else {
207
    if(!quitAfterTests) {
208
      // start DSS
209
      if (dss::DSS::getInstance()->initialize(properties, config_file)) {
210
#ifndef __APPLE__
211
        if(daemonize) {
212
          int result = daemon(1,0);
213
          if(result != 0) {
214
            perror("daemon()");
215
            return 0;
216
          }
217
        }
218
#endif
219
        dss::DSS::getInstance()->run();
220
      } else {
221
        dss::Logger::getInstance()->log("Failed to initialize dss. Exiting", lsFatal);
222
      }
223
    }
224
    if(dss::DSS::hasInstance()) {
225
      dss::DSS::shutdown();
226
    }
227
    dss::Logger::shutdown();
228
  }
229
230
#ifdef USE_LIBXML
231
  // free some internal memory of libxml to make valgrind happy
232
  // NOTE: this needs to be the last call in a process.
233
  //       Trust me....
234
  xmlCleanupParser();
235
#endif
236
  free(tzNameCopy);
237
238
  
239
  // force the DS485Client symbols to be contained in the dss binary
240
  // Also see redmine ticket #54
241
  dss::DS485Client* __attribute__ ((unused)) pClient = new dss::DS485Client();
242
243
  return 0;
244
}