blob: 0bffb99b2f7d9bc8dffce4fffe8ef715d1bf7243 [file] [log] [blame]
robert.swiecki3bb518c2010-10-14 00:48:24 +00001/*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +00002 *
robert.swiecki@gmail.com90e99112015-02-15 02:05:14 +00003 * honggfuzz - the main file
4 * -----------------------------------------
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +00005 *
Robert Swiecki46288f72018-02-27 17:28:47 +01006 * Authors: Robert Swiecki <swiecki@google.com>
7 * Felix Gröbert <groebert@google.com>
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +00008 *
Robert Swieckidacf91a2019-02-11 15:09:16 +01009 * Copyright 2010-2019 by Google Inc. All Rights Reserved.
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000010 *
11 * Licensed under the Apache License, Version 2.0 (the "License"); you may
12 * not use this file except in compliance with the License. You may obtain
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000013 * a copy of the License at
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000014 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000015 * http://www.apache.org/licenses/LICENSE-2.0
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000016 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000017 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
20 * implied. See the License for the specific language governing
21 * permissions and limitations under the License.
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000022 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000023 */
robert.swiecki3bb518c2010-10-14 00:48:24 +000024
Robert Swiecki64d52432019-02-14 23:02:13 +010025#include <errno.h>
robert.swiecki@gmail.comba85c3e2015-02-02 14:55:16 +000026#include <getopt.h>
Robert Swieckid0fa62c2017-09-28 18:11:05 +020027#include <inttypes.h>
Jagger08816fd2016-03-11 01:32:38 +010028#include <signal.h>
robert.swiecki3bb518c2010-10-14 00:48:24 +000029#include <stdio.h>
30#include <stdlib.h>
robert.swiecki3bb518c2010-10-14 00:48:24 +000031#include <string.h>
Robert Swiecki10eeb0a2017-09-28 15:42:52 +020032#include <sys/mman.h>
Robert Swiecki087ff532018-01-16 22:28:07 +010033#include <sys/resource.h>
34#include <sys/time.h>
robert.swiecki@gmail.comba85c3e2015-02-02 14:55:16 +000035#include <time.h>
36#include <unistd.h>
robert.swiecki3bb518c2010-10-14 00:48:24 +000037
Robert Swieckic8c32db2015-10-09 18:06:22 +020038#include "cmdline.h"
Robert Swieckia375f4b2017-06-01 13:20:54 +020039#include "display.h"
robert.swiecki3bb518c2010-10-14 00:48:24 +000040#include "fuzz.h"
Robert Swiecki10eeb0a2017-09-28 15:42:52 +020041#include "input.h"
Robert Swiecki246af3e2018-01-05 14:56:32 +010042#include "libhfcommon/common.h"
43#include "libhfcommon/files.h"
44#include "libhfcommon/log.h"
45#include "libhfcommon/util.h"
Robert Swiecki56276192018-01-21 15:43:02 +010046#include "socketfuzzer.h"
Robert Swiecki64d52432019-02-14 23:02:13 +010047#include "subproc.h"
robert.swiecki3bb518c2010-10-14 00:48:24 +000048
Jagger08816fd2016-03-11 01:32:38 +010049static int sigReceived = 0;
50
Robert Swieckic6a48b12016-03-11 17:41:57 +010051/*
52 * CygWin/MinGW incorrectly copies stack during fork(), so we need to keep some
53 * structures in the data section
54 */
55honggfuzz_t hfuzz;
Robert Swieckic6a48b12016-03-11 17:41:57 +010056
Robert Swiecki35978ac2017-11-16 18:00:53 +010057static void exitWithMsg(const char* msg, int exit_code) {
Robert Swieckif90fb152018-01-08 15:30:03 +010058 HF_ATTR_UNUSED ssize_t sz = write(STDERR_FILENO, msg, strlen(msg));
Robert Swiecki8b4cc332018-10-12 17:10:00 +020059 for (;;) {
60 exit(exit_code);
Robert Swieckid7f594e2019-01-26 18:28:57 +010061 _exit(exit_code);
Robert Swiecki8b4cc332018-10-12 17:10:00 +020062 abort();
Robert Swieckid7f594e2019-01-26 18:28:57 +010063 __builtin_trap();
Robert Swiecki8b4cc332018-10-12 17:10:00 +020064 }
Robert Swiecki35978ac2017-11-16 18:00:53 +010065}
66
Robert Swiecki21eec512018-02-21 18:44:47 +010067static bool showDisplay = true;
Robert Swiecki98e23372019-01-30 11:50:18 +010068static void sigHandler(int sig) {
Robert Swiecki1c9d8092017-06-01 02:39:09 +020069 /* We should not terminate upon SIGALRM delivery */
70 if (sig == SIGALRM) {
Robert Swiecki0dde76d2017-11-16 19:25:44 +010071 if (fuzz_shouldTerminate()) {
72 exitWithMsg("Terminating forcefully\n", EXIT_FAILURE);
Robert Swiecki35978ac2017-11-16 18:00:53 +010073 }
Robert Swiecki21eec512018-02-21 18:44:47 +010074 showDisplay = true;
Jagger08816fd2016-03-11 01:32:38 +010075 return;
76 }
Robert Swiecki91a61ec2019-02-10 11:52:28 +010077 /* Do nothing with pings from the main thread */
78 if (sig == SIGUSR1) {
79 return;
80 }
Robert Swiecki64d52432019-02-14 23:02:13 +010081 /* It's handled in the signal thread */
82 if (sig == SIGCHLD) {
83 return;
84 }
Jagger08816fd2016-03-11 01:32:38 +010085
Robert Swiecki8d01b012017-02-19 15:48:11 +010086 if (ATOMIC_GET(sigReceived) != 0) {
Robert Swiecki35978ac2017-11-16 18:00:53 +010087 exitWithMsg("Repeated termination signal caugth\n", EXIT_FAILURE);
Robert Swiecki8d01b012017-02-19 15:48:11 +010088 }
89
90 ATOMIC_SET(sigReceived, sig);
Jagger08816fd2016-03-11 01:32:38 +010091}
92
Robert Swiecki087ff532018-01-16 22:28:07 +010093static void setupRLimits(void) {
94 struct rlimit rlim;
95 if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) {
96 PLOG_W("getrlimit(RLIMIT_NOFILE)");
97 return;
98 }
99 if (rlim.rlim_cur >= 1024) {
100 return;
101 }
102 if (rlim.rlim_max < 1024) {
Robert Swiecki14960692018-02-22 03:15:41 +0100103 LOG_E("RLIMIT_NOFILE max limit < 1024 (%zu). Expect troubles!", (size_t)rlim.rlim_max);
Robert Swiecki087ff532018-01-16 22:28:07 +0100104 return;
105 }
Robert Swieckidf02b9d2018-02-21 21:20:13 +0100106 rlim.rlim_cur = MIN(1024, rlim.rlim_max); // we don't need more
Robert Swiecki087ff532018-01-16 22:28:07 +0100107 if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {
Robert Swiecki6a2fa642018-02-22 17:28:34 +0100108 PLOG_E("Couldn't setrlimit(RLIMIT_NOFILE, cur=%zu/max=%zu)", (size_t)rlim.rlim_cur,
109 (size_t)rlim.rlim_max);
Robert Swiecki087ff532018-01-16 22:28:07 +0100110 }
111}
112
Robert Swieckidc8047c2018-01-07 21:18:26 +0100113static void setupMainThreadTimer(void) {
114 const struct itimerval it = {
Robert Swiecki7cd3f1e2019-02-12 08:25:22 +0100115 .it_value =
116 {
117 .tv_sec = 1,
118 .tv_usec = 0,
119 },
120 .it_interval =
121 {
122 .tv_sec = 0,
123 .tv_usec = 1000ULL * 200ULL,
124 },
Jagger08816fd2016-03-11 01:32:38 +0100125 };
126 if (setitimer(ITIMER_REAL, &it, NULL) == -1) {
127 PLOG_F("setitimer(ITIMER_REAL)");
128 }
129}
130
Robert Swieckidc8047c2018-01-07 21:18:26 +0100131static void setupSignalsPreThreads(void) {
Jaggerfbd4ae12016-09-02 02:26:02 +0200132 /* Block signals which should be handled or blocked in the main thread */
Jagger33cce5d2016-04-16 20:10:36 +0200133 sigset_t ss;
Jagger090de222016-04-16 20:05:13 +0200134 sigemptyset(&ss);
Jagger08816fd2016-03-11 01:32:38 +0100135 sigaddset(&ss, SIGTERM);
136 sigaddset(&ss, SIGINT);
137 sigaddset(&ss, SIGQUIT);
Jagger33cce5d2016-04-16 20:10:36 +0200138 sigaddset(&ss, SIGALRM);
Jaggerac75f262016-08-20 12:38:20 +0200139 sigaddset(&ss, SIGPIPE);
Robert Swiecki7cd3f1e2019-02-12 08:25:22 +0100140 /* Linux/arch uses it to discover events from persistent fuzzing processes */
Jaggerfbd4ae12016-09-02 02:26:02 +0200141 sigaddset(&ss, SIGIO);
Robert Swiecki64d52432019-02-14 23:02:13 +0100142 /* Let the signal thread catch SIGCHLD */
Robert Swieckidacf91a2019-02-11 15:09:16 +0100143 sigaddset(&ss, SIGCHLD);
Robert Swiecki64d52432019-02-14 23:02:13 +0100144 /* This is checked for via sigwaitinfo/sigtimedwait */
145 sigaddset(&ss, SIGUSR1);
Robert Swiecki198ea702019-02-11 22:10:00 +0100146 if (sigprocmask(SIG_SETMASK, &ss, NULL) != 0) {
147 PLOG_F("pthread_sigmask(SIG_SETMASK)");
Jagger08816fd2016-03-11 01:32:38 +0100148 }
149
150 struct sigaction sa = {
151 .sa_handler = sigHandler,
152 .sa_flags = 0,
153 };
154 sigemptyset(&sa.sa_mask);
155 if (sigaction(SIGTERM, &sa, NULL) == -1) {
156 PLOG_F("sigaction(SIGTERM) failed");
157 }
158 if (sigaction(SIGINT, &sa, NULL) == -1) {
159 PLOG_F("sigaction(SIGINT) failed");
160 }
161 if (sigaction(SIGQUIT, &sa, NULL) == -1) {
162 PLOG_F("sigaction(SIGQUIT) failed");
163 }
Jagger33cce5d2016-04-16 20:10:36 +0200164 if (sigaction(SIGALRM, &sa, NULL) == -1) {
165 PLOG_F("sigaction(SIGQUIT) failed");
166 }
Robert Swiecki91a61ec2019-02-10 11:52:28 +0100167 if (sigaction(SIGUSR1, &sa, NULL) == -1) {
168 PLOG_F("sigaction(SIGUSR1) failed");
169 }
Robert Swiecki64d52432019-02-14 23:02:13 +0100170 if (sigaction(SIGCHLD, &sa, NULL) == -1) {
171 PLOG_F("sigaction(SIGCHLD) failed");
172 }
173}
174
175static void setupSignalsMainThread(void) {
Jagger08816fd2016-03-11 01:32:38 +0100176 /* Unblock signals which should be handled by the main thread */
177 sigset_t ss;
178 sigemptyset(&ss);
179 sigaddset(&ss, SIGTERM);
180 sigaddset(&ss, SIGINT);
181 sigaddset(&ss, SIGQUIT);
Jagger3846dfc2016-04-16 20:06:16 +0200182 sigaddset(&ss, SIGALRM);
Jagger08816fd2016-03-11 01:32:38 +0100183 if (sigprocmask(SIG_UNBLOCK, &ss, NULL) != 0) {
Jagger33cce5d2016-04-16 20:10:36 +0200184 PLOG_F("pthread_sigmask(SIG_UNBLOCK)");
Jagger08816fd2016-03-11 01:32:38 +0100185 }
186}
187
Robert Swiecki42b5d0c2018-07-26 15:12:29 +0200188static void printSummary(honggfuzz_t* hfuzz) {
189 uint64_t exec_per_sec = 0;
190 uint64_t elapsed_sec = time(NULL) - hfuzz->timing.timeStart;
191 if (elapsed_sec) {
192 exec_per_sec = hfuzz->cnts.mutationsCnt / elapsed_sec;
193 }
194 LOG_I("Summary iterations:%zu time:%" PRIu64 " speed:%" PRIu64, hfuzz->cnts.mutationsCnt,
195 elapsed_sec, exec_per_sec);
196}
197
Robert Swiecki64d52432019-02-14 23:02:13 +0100198static void pingThreads(honggfuzz_t* hfuzz) {
Robert Swieckidacf91a2019-02-11 15:09:16 +0100199 for (size_t i = 0; i < hfuzz->threads.threadsMax; i++) {
Robert Swiecki66fc5732019-02-15 01:56:55 +0100200 if (pthread_kill(hfuzz->threads.threads[i], SIGUSR1) != 0 && errno != EINTR) {
Robert Swieckidacf91a2019-02-11 15:09:16 +0100201 PLOG_W("pthread_kill(thread=%zu, SIGUSR1)", i);
202 }
203 }
204}
205
Robert Swiecki64d52432019-02-14 23:02:13 +0100206static void* signalThread(void* arg) {
207 honggfuzz_t* hfuzz = (honggfuzz_t*)arg;
208
209 sigset_t ss;
210 sigemptyset(&ss);
211 sigaddset(&ss, SIGCHLD);
212 if (pthread_sigmask(SIG_UNBLOCK, &ss, NULL) != 0) {
213 PLOG_F("Couldn't unblock SIGCHLD in the signal thread");
214 }
215
216 for (;;) {
Robert Swiecki4a8dbbe2019-02-14 23:16:32 +0100217 int sig;
Robert Swiecki66fc5732019-02-15 01:56:55 +0100218 if (sigwait(&ss, &sig) != 0 && errno != EINTR) {
Robert Swiecki4a8dbbe2019-02-14 23:16:32 +0100219 PLOG_F("sigwait(SIGCHLD)");
Robert Swiecki64d52432019-02-14 23:02:13 +0100220 }
221 if (fuzz_isTerminating()) {
222 break;
223 }
Robert Swiecki66fc5732019-02-15 01:56:55 +0100224 if (sig == SIGCHLD) {
225 pingThreads(hfuzz);
226 }
Robert Swiecki64d52432019-02-14 23:02:13 +0100227 }
228
229 return NULL;
230}
231
Robert Swieckid50ed422017-11-13 23:32:26 +0100232int main(int argc, char** argv) {
Robert Swieckic6a48b12016-03-11 17:41:57 +0100233 /*
234 * Work around CygWin/MinGW
235 */
Robert Swiecki4e595fb2017-10-11 17:26:51 +0200236 char** myargs = (char**)util_Malloc(sizeof(char*) * (argc + 1));
Robert Swiecki3ab16642018-01-12 18:08:37 +0100237 defer {
238 free(myargs);
239 };
Jagger1ebc6dc2016-03-12 01:39:09 +0100240
241 int i;
242 for (i = 0U; i < argc; i++) {
Robert Swieckic6a48b12016-03-11 17:41:57 +0100243 myargs[i] = argv[i];
244 }
245 myargs[i] = NULL;
246
247 if (cmdlineParse(argc, myargs, &hfuzz) == false) {
Robert Swieckic8c32db2015-10-09 18:06:22 +0200248 LOG_F("Parsing of the cmd-line arguments failed");
robert.swiecki3bb518c2010-10-14 00:48:24 +0000249 }
250
Robert Swiecki5e26bd92018-03-02 12:09:34 +0100251 if (hfuzz.display.useScreen) {
Robert Swiecki1f1ce172017-02-10 03:43:00 +0100252 display_init();
253 }
254
Robert Swiecki5e26bd92018-03-02 12:09:34 +0100255 if (hfuzz.socketFuzzer.enabled) {
Robert Swiecki98e23372019-01-30 11:50:18 +0100256 LOG_I("No input file corpus loaded, the external socket_fuzzer is responsible for "
257 "creating the fuzz data");
dobinedf9f8d2018-01-21 13:57:02 +0100258 setupSocketFuzzer(&hfuzz);
259 } else if (!input_init(&hfuzz)) {
Robert Swieckiced3eba2017-12-15 15:33:03 +0100260 LOG_F("Couldn't load input corpus");
261 exit(EXIT_FAILURE);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000262 }
263
Robert Swiecki04dcac32018-03-02 03:05:26 +0100264 if (hfuzz.mutate.dictionaryFile && (input_parseDictionary(&hfuzz) == false)) {
265 LOG_F("Couldn't parse dictionary file ('%s')", hfuzz.mutate.dictionaryFile);
robert.swiecki@gmail.com4f1124f2015-04-21 17:12:22 +0000266 }
267
Robert Swieckia5b918a2018-03-07 23:59:53 +0100268 if (hfuzz.feedback.blacklistFile && (input_parseBlacklist(&hfuzz) == false)) {
269 LOG_F("Couldn't parse stackhash blacklist file ('%s')", hfuzz.feedback.blacklistFile);
Anestis Bechtsoudisd59af692015-09-21 15:15:05 +0300270 }
Anestis Bechtsoudis00be1892016-10-31 09:29:44 +0200271#define hfuzzl hfuzz.linux
Robert Swieckid50ed422017-11-13 23:32:26 +0100272 if (hfuzzl.symsBlFile &&
273 ((hfuzzl.symsBlCnt = files_parseSymbolFilter(hfuzzl.symsBlFile, &hfuzzl.symsBl)) == 0)) {
Anestis Bechtsoudis3dce6ac2016-10-31 08:51:14 +0200274 LOG_F("Couldn't parse symbols blacklist file ('%s')", hfuzzl.symsBlFile);
Anestis Bechtsoudisba68b382016-10-29 20:44:15 +0300275 }
276
Robert Swieckid50ed422017-11-13 23:32:26 +0100277 if (hfuzzl.symsWlFile &&
278 ((hfuzzl.symsWlCnt = files_parseSymbolFilter(hfuzzl.symsWlFile, &hfuzzl.symsWl)) == 0)) {
Anestis Bechtsoudis3dce6ac2016-10-31 08:51:14 +0200279 LOG_F("Couldn't parse symbols whitelist file ('%s')", hfuzzl.symsWlFile);
Anestis Bechtsoudisba68b382016-10-29 20:44:15 +0300280 }
Anestis Bechtsoudis00be1892016-10-31 09:29:44 +0200281
Robert Swieckia5b918a2018-03-07 23:59:53 +0100282 if (hfuzz.feedback.dynFileMethod != _HF_DYNFILE_NONE) {
283 if (!(hfuzz.feedback.feedbackMap = files_mapSharedMem(
284 sizeof(feedback_t), &hfuzz.feedback.bbFd, "hfuzz-feedback", hfuzz.io.workDir))) {
Robert Swiecki82c707c2017-11-14 16:36:23 +0100285 LOG_F("files_mapSharedMem(sz=%zu, dir='%s') failed", sizeof(feedback_t),
286 hfuzz.io.workDir);
Robert Swieckie4d63d42016-11-01 00:40:51 +0100287 }
288 }
289
Robert Swiecki087ff532018-01-16 22:28:07 +0100290 setupRLimits();
Robert Swieckidc8047c2018-01-07 21:18:26 +0100291 setupSignalsPreThreads();
Robert Swiecki64d52432019-02-14 23:02:13 +0100292 fuzz_threadsStart(&hfuzz);
293
294 pthread_t sigthread;
295 if (!subproc_runThread(&hfuzz, &sigthread, signalThread)) {
296 LOG_F("Couldn't start the signal thread");
297 }
298
Robert Swieckidc8047c2018-01-07 21:18:26 +0100299 setupSignalsMainThread();
Robert Swieckidc8047c2018-01-07 21:18:26 +0100300 setupMainThreadTimer();
301
Jagger08816fd2016-03-11 01:32:38 +0100302 for (;;) {
Robert Swiecki5e26bd92018-03-02 12:09:34 +0100303 if (hfuzz.display.useScreen && showDisplay) {
Jagger08816fd2016-03-11 01:32:38 +0100304 display_display(&hfuzz);
Robert Swiecki21eec512018-02-21 18:44:47 +0100305 showDisplay = false;
Jagger08816fd2016-03-11 01:32:38 +0100306 }
Robert Swiecki8d01b012017-02-19 15:48:11 +0100307 if (ATOMIC_GET(sigReceived) > 0) {
Robert Swieckic95cf2a2017-06-23 15:31:08 +0200308 LOG_I("Signal %d (%s) received, terminating", ATOMIC_GET(sigReceived),
Robert Swiecki4e595fb2017-10-11 17:26:51 +0200309 strsignal(ATOMIC_GET(sigReceived)));
Jagger08816fd2016-03-11 01:32:38 +0100310 break;
311 }
Robert Swiecki66b65122017-11-11 02:55:55 +0100312 if (ATOMIC_GET(hfuzz.threads.threadsFinished) >= hfuzz.threads.threadsMax) {
Jagger08816fd2016-03-11 01:32:38 +0100313 break;
314 }
Robert Swiecki371e1292017-12-18 01:10:33 +0100315 if (hfuzz.timing.runEndTime > 0 && (time(NULL) > hfuzz.timing.runEndTime)) {
Robert Swieckic95cf2a2017-06-23 15:31:08 +0200316 LOG_I("Maximum run time reached, terminating");
Robert Swieckic95cf2a2017-06-23 15:31:08 +0200317 break;
318 }
Robert Swiecki387b79a2019-02-14 23:20:52 +0100319 pingThreads(&hfuzz);
Jagger08816fd2016-03-11 01:32:38 +0100320 pause();
321 }
322
Robert Swiecki0dde76d2017-11-16 19:25:44 +0100323 fuzz_setTerminating();
Robert Swieckidacf91a2019-02-11 15:09:16 +0100324
Robert Swieckiacdf0bd2019-02-17 02:42:04 +0100325 for (;;) {
326 if (ATOMIC_GET(hfuzz.threads.threadsFinished) >= hfuzz.threads.threadsMax) {
327 break;
328 }
329 pingThreads(&hfuzz);
330 usleep(50000); /* 50ms */
331 }
Robert Swiecki33fb2842017-02-19 05:39:50 +0100332
Jagger08816fd2016-03-11 01:32:38 +0100333 /* Clean-up global buffers */
Robert Swieckia5b918a2018-03-07 23:59:53 +0100334 if (hfuzz.feedback.blacklist) {
335 free(hfuzz.feedback.blacklist);
Jagger08816fd2016-03-11 01:32:38 +0100336 }
Kamil Rytarowskibdfe2a72018-08-18 12:28:29 +0200337#if defined(_HF_ARCH_LINUX)
Anestis Bechtsoudisba68b382016-10-29 20:44:15 +0300338 if (hfuzz.linux.symsBl) {
339 free(hfuzz.linux.symsBl);
340 }
341 if (hfuzz.linux.symsWl) {
342 free(hfuzz.linux.symsWl);
343 }
Kamil Rytarowskibdfe2a72018-08-18 12:28:29 +0200344#elif defined(_HF_ARCH_NETBSD)
345 if (hfuzz.netbsd.symsBl) {
346 free(hfuzz.netbsd.symsBl);
347 }
348 if (hfuzz.netbsd.symsWl) {
349 free(hfuzz.netbsd.symsWl);
350 }
351#endif
Robert Swiecki5e26bd92018-03-02 12:09:34 +0100352 if (hfuzz.socketFuzzer.enabled) {
dobinedf9f8d2018-01-21 13:57:02 +0100353 cleanupSocketFuzzer();
354 }
Jagger08816fd2016-03-11 01:32:38 +0100355
Robert Swiecki42b5d0c2018-07-26 15:12:29 +0200356 printSummary(&hfuzz);
Robert Swiecki19a08e32018-07-26 02:07:52 +0200357
Robert Swiecki37778e02016-03-15 15:45:28 +0100358 return EXIT_SUCCESS;
robert.swiecki3bb518c2010-10-14 00:48:24 +0000359}