]> mj.ucw.cz Git - ursary.git/blob - ursaryd.c
Lights: Better handling of long-press-to-max
[ursary.git] / ursaryd.c
1 /*
2  *      The Ursary Audio Controls
3  *
4  *      (c) 2014--2020 Martin Mares <mj@ucw.cz>
5  */
6
7 #undef LOCAL_DEBUG
8
9 #include <ucw/lib.h>
10 #include <ucw/clists.h>
11 #include <ucw/daemon.h>
12 #include <ucw/log.h>
13 #include <ucw/mainloop.h>
14 #include <ucw/opt.h>
15 #include <ucw/stkstring.h>
16
17 #include <math.h>
18 #include <signal.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <syslog.h>
23
24 #include "ursaryd.h"
25 #include "usb.h"
26
27 /*
28  *      Map of all controls
29  *
30  *              rotary          red button      green button
31  *              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32  *      0       sink PCH        mute            switch to PCH
33  *      1       sink BT         mute            switch to BT
34  *      2       ceil brightness mic non-mute    ceiling lights on
35  *      3       desk brightness -               desk lights on
36  *      4       Albireo MPD     mute            MPD play/pause
37  *      5       Albireo MPV     mute            MPD stop
38  *      6       Albireo Zoom    mute            MPD prev
39  *      7       eveyrhing else  mute            MPD next
40  *
41  *      center  -
42  *      slider  light color temperature
43  */
44
45 #define PCH_SINK "alsa_output.pci-0000_00_1f.3.analog-stereo"
46 #define BT_SINK "bluez_sink.CC_98_8B_D0_8C_06.a2dp_sink"
47 #define LOGI_SOURCE "alsa_input.usb-046d_Logitech_Webcam_C925e_EF163C5F-02.analog-stereo"
48
49 /*** Sink controls ***/
50
51 static double volume_from_pa(pa_volume_t vol)
52 {
53   return (double) vol / PA_VOLUME_NORM;
54 }
55
56 static pa_volume_t volume_to_pa(double vol)
57 {
58   return vol * PA_VOLUME_NORM + 0.0001;
59 }
60
61 static void update_ring_from_sink(int ring, const char *sink_name)
62 {
63   struct pulse_sink *s = pulse_sink_by_name(sink_name);
64   if (!s)
65     {
66       noct_set_ring(ring, RING_MODE_SINGLE_ON, 0x7f);
67       noct_set_button(ring, 0);
68       return;
69     }
70
71   if (s->mute)
72     {
73       noct_set_ring(ring, RING_MODE_SINGLE_ON, 0x7f);
74       noct_set_button(ring, 1);
75       return;
76     }
77
78   double vol = CLAMP(volume_from_pa(s->volume), 0, 1);
79   noct_set_ring(ring, RING_MODE_LEFT, CLAMP((int)(0x7f * vol), 12, 0x7f));
80   noct_set_button(ring, 0);
81 }
82
83 static void update_sink_from_rotary(int delta, const char *sink_name)
84 {
85   struct pulse_sink *s = pulse_sink_by_name(sink_name);
86   if (!s)
87     return;
88
89   double vol = volume_from_pa(s->volume) + delta * 0.02;
90   pa_volume_t pavol = volume_to_pa(CLAMP(vol, 0, 1));
91   if (pavol == s->volume)
92     return;
93   pa_cvolume cvol;
94   pa_cvolume_set(&cvol, s->channels, pavol);
95
96   DBG("## Setting volume of sink %s to %d", s->name, cvol.values[0]);
97   pulse_sink_set_volume(s->idx, &cvol);
98 }
99
100 static void update_sink_mute_from_button(int on, const char *sink_name)
101 {
102   if (!on)
103     return;
104
105   struct pulse_sink *s = pulse_sink_by_name(sink_name);
106   if (!s)
107     return;
108
109   DBG("## Setting mute of sink %s to %d", s->name, !s->mute);
110   pulse_sink_set_mute(s->idx, !s->mute);
111 }
112
113 /*** Client controls ***/
114
115 struct client_map {
116   int group;
117   const char *client;
118   const char *host;
119 };
120
121 static struct client_map client_map[] = {
122   { 4, "Music Player Daemon",   "albireo",      },
123   { 5, "mpv",                   "albireo",      },
124   { 6, "ZOOM VoiceEngine",      "albireo",      },
125   { 7, NULL,                    NULL,           },
126 };
127
128 #define CLIENT_MAP_SIZE ARRAY_SIZE(client_map)
129
130 struct group_config {
131   bool enabled;
132   double range;
133 };
134
135 struct group_state {
136   double volume;
137   bool have_muted[2];
138 };
139
140 #define NUM_GROUPS 9
141
142 static struct group_config group_config[NUM_GROUPS] = {
143   [4] = { .enabled = 1, .range = 1.5 },
144   [5] = { .enabled = 1, .range = 1.5 },
145   [6] = { .enabled = 1, .range = 1.5 },
146   [7] = { .enabled = 1, .range = 1.5 },
147 };
148
149 static struct group_state group_state[NUM_GROUPS];
150
151 static void calc_groups(void)
152 {
153   bzero(group_state, sizeof(group_state));
154
155   CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list)
156     {
157       s->noct_group_idx = -1;
158
159       if (s->client_idx < 0 || s->sink_idx < 0)
160         continue;
161
162       struct pulse_client *c = pulse_client_by_idx(s->client_idx);
163       if (!c)
164         continue;
165
166       for (uns i=0; i < CLIENT_MAP_SIZE; i++)
167         {
168           struct client_map *cm = &client_map[i];
169           if ((!cm->client || !strcmp(cm->client, c->name)) &&
170               (!cm->host || !strcmp(cm->host, c->host)))
171             {
172               int g = cm->group;
173               struct group_state *gs = &group_state[g];
174               DBG("@@ Client #%d, sink input #%d -> group %d", s->client_idx, s->idx, g);
175               s->noct_group_idx = g;
176               gs->volume = MAX(gs->volume, s->volume);
177               gs->have_muted[!!s->mute] = 1;
178               break;
179             }
180         }
181     }
182 }
183
184 static void update_groups(void)
185 {
186   calc_groups();
187
188   for (uns i=0; i < NUM_GROUPS; i++)
189     {
190       struct group_config *gc = &group_config[i];
191       struct group_state *gs = &group_state[i];
192       if (!gc->enabled)
193         continue;
194
195       DBG("@@ Group #%d: mute=%d/%d volume=%.3f/%.3f", i, gs->have_muted[0], gs->have_muted[1], volume_from_pa(gs->volume), gc->range);
196
197       if (!gs->have_muted[0] && !gs->have_muted[1])
198         {
199           noct_set_ring(i, RING_MODE_LEFT, 0);
200           noct_set_button(i, 0);
201         }
202       else if (!gs->have_muted[0])
203         {
204           noct_set_ring(i, RING_MODE_SINGLE_ON, 0x7f);
205           noct_set_button(i, 1);
206         }
207       else
208         {
209           double vol = CLAMP(volume_from_pa(gs->volume), 0, gc->range);
210           int val = 0x7f * vol / gc->range;
211           val = CLAMP(val, 12, 0x7f);
212           noct_set_ring(i, RING_MODE_LEFT, val);
213           noct_set_button(i, 0);
214         }
215     }
216 }
217
218 static void update_group_from_rotary(int i, int delta)
219 {
220   if (i >= NUM_GROUPS)
221     return;
222   struct group_config *gc = &group_config[i];
223   struct group_state *gs = &group_state[i];
224   if (!gc->enabled)
225     return;
226
227   calc_groups();
228   double vol = volume_from_pa(gs->volume) + delta*0.02;
229   pa_volume_t pavol = volume_to_pa(CLAMP(vol, 0, gc->range));
230
231   CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list)
232     {
233       if (s->noct_group_idx == i && s->volume != pavol)
234         {
235           DBG("@@ Client #%d, sink input #%d: setting volume=%u", s->client_idx, s->idx, pavol);
236           pa_cvolume cvol;
237           pa_cvolume_set(&cvol, s->channels, pavol);
238           pulse_sink_input_set_volume(s->idx, &cvol);
239         }
240     }
241 }
242
243 static void update_group_from_button(int i, int on)
244 {
245   if (!on)
246     return;
247   if (i >= NUM_GROUPS)
248     return;
249   struct group_config *gc = &group_config[i];
250   struct group_state *gs = &group_state[i];
251   if (!gc->enabled)
252     return;
253
254   calc_groups();
255   if (!gs->have_muted[0] && !gs->have_muted[1])
256     return;
257   uns mute = !gs->have_muted[1];
258
259   CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list)
260     {
261       if (s->noct_group_idx == i)
262         {
263           DBG("@@ Client #%d, sink input #%d: setting mute=%u", s->client_idx, s->idx, mute);
264           pulse_sink_input_set_mute(s->idx, mute);
265         }
266     }
267 }
268
269 static int find_touched_client(void)
270 {
271   int touched = -1;
272
273   for (uns i=0; i < NUM_GROUPS; i++)
274     if (group_config[i].enabled && noct_rotary_touched[i])
275       {
276         if (touched >= 0)
277           return -1;
278         touched = i;
279       }
280   return touched;
281 }
282
283 /*** Default sink controls ***/
284
285 static const char *get_client_sink(int i)
286 {
287   const char *sink = NULL;
288
289   CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list)
290     if (s->noct_group_idx == i)
291       {
292         struct pulse_sink *sk = (s->sink_idx >= 0) ? pulse_sink_by_idx(s->sink_idx) : NULL;
293         const char *ss = sk ? sk->name : NULL;
294         if (!sink)
295           sink = ss;
296         else if (strcmp(sink, ss))
297           sink = "?";
298       }
299   return sink ? : "?";
300 }
301
302 static void update_default_sink(void)
303 {
304   int i = find_touched_client();
305   const char *sink;
306   if (i >= 0)
307     sink = get_client_sink(i);
308   else
309     sink = pulse_default_sink_name ? : "?";
310
311   if (!strcmp(sink, PCH_SINK))
312     {
313       noct_set_button(8, 1);
314       noct_set_button(9, 0);
315     }
316   else if (!strcmp(sink, BT_SINK))
317     {
318       noct_set_button(8, 0);
319       noct_set_button(9, 1);
320     }
321   else
322     {
323       noct_set_button(8, 0);
324       noct_set_button(9, 0);
325     }
326 }
327
328 static void update_default_sink_from_button(int button, int on)
329 {
330   if (!on)
331     return;
332
333   int i = find_touched_client();
334 #if 0
335   const char *sink;
336   if (i >= 0)
337     sink = get_client_sink(i);
338   else
339     sink = pulse_default_sink_name ? : "?";
340 #endif
341
342   const char *switch_to = NULL;
343   if (button == 8)
344     switch_to = PCH_SINK;
345   else if (button == 9)
346     switch_to = BT_SINK;
347
348   if (!switch_to)
349     return;
350
351   if (i >= 0)
352     {
353       struct pulse_sink *sk = pulse_sink_by_name(switch_to);
354       if (!sk)
355         return;
356
357       CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list)
358         if (s->noct_group_idx == i)
359           {
360             DBG("Moving input #%d to sink #%d", s->idx, sk->idx);
361             pulse_sink_input_move(s->idx, sk->idx);
362           }
363     }
364   else
365     {
366       DBG("Switching default sink to %s", switch_to);
367       pulse_server_set_default_sink(switch_to);
368     }
369 }
370
371 /*** Source mute controls ***/
372
373 static void update_source_buttons(void)
374 {
375   struct pulse_source *source = pulse_source_by_name(LOGI_SOURCE);
376   if (!source)
377     return;
378
379 #if 0   // Disabled for now
380   // if (source->suspended || source->mute)
381   if (source->mute)
382     noct_set_button(2, 0);
383   else
384     noct_set_button(2, 1);
385 #endif
386 }
387
388 static void update_source_mute_from_button(int on, const char *source_name)
389 {
390   if (!on)
391     return;
392
393   struct pulse_source *s = pulse_source_by_name(source_name);
394   if (!s)
395     return;
396
397 #if 0
398   DBG("## Setting mute of source %s to %d", s->name, !s->mute);
399   pulse_source_set_mute(s->idx, !s->mute);
400 #endif
401 }
402
403 /*** MPD controls ***/
404
405 static bool mpd_flash_state;
406
407 static void mpd_flash_timeout(struct main_timer *t)
408 {
409   mpd_flash_state ^= 1;
410   noct_set_button(12, mpd_flash_state);
411   timer_add_rel(t, 500);
412 }
413
414 static struct main_timer mpd_flash_timer = {
415   .handler = mpd_flash_timeout,
416 };
417
418 static void update_mpd(void)
419 {
420   const char *state = mpd_get_player_state();
421   if (!strcmp(state, "play"))
422     {
423       noct_set_button(12, 1);
424       timer_del(&mpd_flash_timer);
425     }
426   else if (!strcmp(state, "pause"))
427     {
428       if (!timer_is_active(&mpd_flash_timer))
429         {
430           mpd_flash_state = 1;
431           mpd_flash_timeout(&mpd_flash_timer);
432         }
433     }
434   else
435     {
436       noct_set_button(12, 0);
437       timer_del(&mpd_flash_timer);
438     }
439 }
440
441 static void mpd_button_timeout(struct main_timer *t)
442 {
443   DBG("MPD stop");
444   timer_del(t);
445   mpd_stop();
446 }
447
448 static void update_mpd_from_button(int button UNUSED, int on)
449 {
450   static struct main_timer mpd_button_timer = {
451     .handler = mpd_button_timeout,
452   };
453
454   const char *state = mpd_get_player_state();
455
456   if (!on)
457     {
458       if (timer_is_active(&mpd_button_timer))
459         {
460           timer_del(&mpd_button_timer);
461           if (!strcmp(state, "play"))
462             {
463               DBG("MPD pause");
464               mpd_pause(1);
465             }
466           else if (!strcmp(state, "pause"))
467             {
468               DBG("MPD resume");
469               mpd_pause(0);
470             }
471         }
472       return;
473     }
474
475   if (!strcmp(state, "stop"))
476     {
477       DBG("MPD play");
478       mpd_play();
479     }
480   else
481     {
482       DBG("MPD starting button timer");
483       timer_add_rel(&mpd_button_timer, 1000);
484     }
485 }
486
487 /*** Lights ***/
488
489 static bool lights_on[2];
490 static double lights_brightness[2];
491 static double lights_temperature[2];
492
493 static void update_lights(void)
494 {
495   if (!dmx_is_ready())
496     {
497       noct_set_ring(3, RING_MODE_SINGLE_ON, 0x7f);
498       noct_set_button(10, 0);
499       noct_set_button(11, 0);
500       return;
501     }
502
503   for (uint i=0; i<2; i++)
504     {
505       uint warm, cold;
506       if (lights_on[i])
507         {
508           noct_set_ring(3-i, RING_MODE_LEFT, lights_brightness[i] * 127);
509           noct_set_button(11-i, 1);
510           double r = 2;
511           double x = (exp(r*lights_brightness[i]) - 1) / (exp(r) - 1);
512           double t = lights_temperature[i];
513           double w = 2*x*(1-t);
514           double c = 2*x*t;
515           warm = CLAMP((int)(255*w), 0, 255);
516           cold = CLAMP((int)(255*c), 0, 255);
517         }
518       else
519         {
520           noct_set_ring(3-i, RING_MODE_LEFT, 0);
521           noct_set_button(11-i, 0);
522           warm = cold = 0;
523         }
524       DBG("Lights[%d]: on=%d bri=%.3f temp=%.3f -> warm=%d cold=%d", i, lights_on[i], lights_brightness[i], lights_temperature[i], warm, cold);
525       dmx_set_pwm(2*i, warm);
526       dmx_set_pwm(2*i+1, cold);
527     }
528 }
529
530 static void update_lights_from_rotary(int ch, int delta)
531 {
532   if (lights_on[ch])
533     lights_brightness[ch] = CLAMP(lights_brightness[ch] + 0.015*delta*abs(delta), 0., 1.);
534   update_lights();
535 }
536
537 static void update_lights_from_slider(int value)
538 {
539   lights_temperature[0] = value / 127.;
540   lights_temperature[1] = value / 127.;
541   update_lights();
542 }
543
544 static void lights_button_timeout(struct main_timer *t)
545 {
546   int ch = (uintptr_t) t->data;
547   DBG("Lights[%d]: Full throttle!", ch);
548   timer_del(t);
549   lights_on[ch] = 1;
550   lights_brightness[ch] = 1;
551   update_lights();
552 }
553
554 static void update_lights_from_button(int ch, int on)
555 {
556   static struct main_timer lights_button_timer[2] = {{
557     .handler = lights_button_timeout,
558     .data = (void *)(uintptr_t) 0,
559   },{
560     .handler = lights_button_timeout,
561     .data = (void *)(uintptr_t) 1,
562   }};
563
564   if (on)
565     timer_add_rel(&lights_button_timer[ch], 500);
566   else if (timer_is_active(&lights_button_timer[ch]))
567     {
568       timer_del(&lights_button_timer[ch]);
569       lights_on[ch] = !lights_on[ch];
570       update_lights();
571     }
572 }
573
574 /*** Main update routines ***/
575
576 static struct main_timer update_timer;
577 static timestamp_t last_touch_time;
578
579 enum update_state {
580   US_OFFLINE,
581   US_ONLINE,
582   US_SLEEPING,
583   US_PULSE_DEAD,
584 };
585
586 static enum update_state update_state;
587
588 static bool want_sleep_p(void)
589 {
590   CLIST_FOR_EACH(struct pulse_sink *, s, pulse_sink_list)
591     if (!s->suspended)
592       return 0;
593   return 1;
594 }
595
596 static void do_update(struct main_timer *t)
597 {
598   DBG("## UPDATE in state %u", update_state);
599   timer_del(t);
600
601   // Nocturn dead?
602   if (!noct_is_ready())
603     {
604       DBG("## UPDATE: Nocturn is not ready");
605       update_state = US_OFFLINE;
606       return;
607     }
608   else if (update_state == US_OFFLINE)
609     {
610       DBG("## UPDATE: Going online");
611       update_state = US_ONLINE;
612     }
613
614   // Pulse dead?
615   if (!pulse_is_ready())
616     {
617       DBG("## UPDATE: Pulse is not online");
618       if (update_state != US_PULSE_DEAD)
619         {
620           update_state = US_PULSE_DEAD;
621           noct_clear();
622           for (int i=0; i<8; i++)
623             noct_set_button(i, 1);
624         }
625       return;
626     }
627   else if (update_state == US_PULSE_DEAD)
628     {
629       DBG("## UPDATE: Waking up from the dead");
630       update_state = US_ONLINE;
631       noct_clear();
632     }
633
634 #ifdef LOCAL_DEBUG
635   pulse_dump();
636 #endif
637
638   // Sleeping?
639   bool want_sleep = want_sleep_p();
640   if (!want_sleep)
641     last_touch_time = main_get_now();
642   timestamp_t since_touch = last_touch_time ? main_get_now() - last_touch_time : 0;
643   timestamp_t sleep_in = 30000;
644   if (since_touch >= sleep_in)
645     {
646       DBG("UPDATE: Sleeping");
647       if (update_state == US_ONLINE)
648         {
649           update_state = US_SLEEPING;
650           noct_clear();
651           noct_set_ring(8, RING_MODE_LEFT, 127);
652         }
653       return;
654     }
655   else
656     {
657       if (update_state == US_SLEEPING)
658         {
659           DBG("UPDATE: Waking up");
660           update_state = US_ONLINE;
661           noct_clear();
662         }
663       if (want_sleep)
664         {
665           timestamp_t t = sleep_in - since_touch + 10;
666           DBG("UPDATE: Scheduling sleep in %d ms", (int) t);
667           timer_add_rel(&update_timer, t);
668         }
669     }
670
671   // Everything normal
672   update_ring_from_sink(0, PCH_SINK);
673   update_ring_from_sink(1, BT_SINK);
674   update_groups();
675   update_default_sink();
676   update_source_buttons();
677   update_mpd();
678   update_lights();
679 }
680
681 void schedule_update(void)
682 {
683   timer_add_rel(&update_timer, 10);
684 }
685
686 static bool prepare_notify(void)
687 {
688   if (!pulse_is_ready())
689     {
690       DBG("## NOTIFY: Pulse is not online");
691       return 0;
692     }
693
694   last_touch_time = main_get_now();
695   if (update_state == US_SLEEPING)
696     {
697       DBG("## NOTIFY: Scheduling wakeup");
698       schedule_update();
699     }
700
701   return 1;
702 }
703
704 void notify_rotary(int rotary, int delta)
705 {
706   if (!prepare_notify())
707     return;
708
709   switch (rotary)
710     {
711     case 0:
712       update_sink_from_rotary(delta, PCH_SINK);
713       break;
714     case 1:
715       update_sink_from_rotary(delta, BT_SINK);
716       break;
717     case 2:
718       update_lights_from_rotary(1, delta);
719       break;
720     case 3:
721       update_lights_from_rotary(0, delta);
722       break;
723     case 9:
724       update_lights_from_slider(delta);
725       break;
726     default:
727       update_group_from_rotary(rotary, delta);
728     }
729 }
730
731 void notify_button(int button, int on)
732 {
733   if (!prepare_notify())
734     return;
735
736   switch (button)
737     {
738     case 0:
739       update_sink_mute_from_button(on, PCH_SINK);
740       break;
741     case 1:
742       update_sink_mute_from_button(on, BT_SINK);
743       break;
744     case 2:
745       update_source_mute_from_button(on, LOGI_SOURCE);
746       break;
747     case 8:
748     case 9:
749       update_default_sink_from_button(button, on);
750       break;
751     case 10:
752       update_lights_from_button(1, on);
753       break;
754     case 11:
755       update_lights_from_button(0, on);
756       break;
757     case 12:
758       update_mpd_from_button(button, on);
759       break;
760     case 13:
761       if (on)
762         mpd_stop();
763       break;
764     case 14:
765       if (on)
766         mpd_prev();
767       break;
768     case 15:
769       if (on)
770         mpd_next();
771       break;
772     default:
773       update_group_from_button(button, on);
774     }
775 }
776
777 void notify_touch(int rotary UNUSED, int on UNUSED)
778 {
779   if (!prepare_notify())
780     return;
781
782   // Rotary touches switch meaning of LEDs, this is handled inside display updates
783   if (rotary >= 4 && rotary < 8)
784     schedule_update();
785 }
786
787 /*** Main entry point ***/
788
789 static int debug;
790 static int no_fork;
791
792 static struct opt_section options = {
793   OPT_ITEMS {
794     OPT_HELP("Control console for the Ursary"),
795     OPT_HELP(""),
796     OPT_HELP("Options:"),
797     OPT_HELP_OPTION,
798     OPT_BOOL('d', "debug", debug, 0, "\tEnable debugging mode (no fork etc.)"),
799     OPT_BOOL(0, "no-fork", no_fork, 0, "\tDo not fork\n"),
800     OPT_END
801   }
802 };
803
804 static void sigterm_handler(struct main_signal *ms UNUSED)
805 {
806   main_shut_down();
807 }
808
809 static void daemon_body(struct daemon_params *dp)
810 {
811   main_init();
812   update_timer.handler = do_update;
813
814   usb_init();
815   noct_init();
816   dmx_init();
817   pulse_init();
818   mpd_init();
819
820   static struct main_signal term_sig = {
821     .signum = SIGTERM,
822     .handler = sigterm_handler,
823   };
824   signal_add(&term_sig);
825
826   msg(L_INFO, "Ursary daemon starting");
827   main_loop();
828   msg(L_INFO, "Ursary daemon shut down");
829   daemon_exit(dp);
830 }
831
832 int main(int argc UNUSED, char **argv)
833 {
834   opt_parse(&options, argv+1);
835   unsetenv("DISPLAY");
836   unsetenv("HOME");
837
838   struct daemon_params dp = {
839     .flags = ((debug || no_fork) ? DAEMON_FLAG_SIMULATE : 0),
840     .pid_file = "/run/ursaryd.pid",
841     .run_as_user = "ursary",
842   };
843   daemon_init(&dp);
844
845   log_init(argv[0]);
846   if (!debug)
847     {
848       struct log_stream *ls = log_new_syslog("daemon", LOG_PID);
849       ls->levels = ~(1U << L_DEBUG);
850       log_set_default_stream(ls);
851     }
852
853   daemon_run(&dp, daemon_body);
854   return 0;
855 }