openssl: Use EVP API for AES.
[paraslash.git] / fade.c
1 /*
2 * Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>
3 *
4 * Licensed under the GPL v2. For licencing details see COPYING.
5 */
6
7 /** \file fade.c A volume fader and alarm clock for OSS. */
8
9 #include <regex.h>
10
11 #include "fade.cmdline.h"
12 #include "para.h"
13 #include "fd.h"
14 #include "string.h"
15 #include "mix.h"
16 #include "error.h"
17 #include "ggo.h"
18 #include "version.h"
19
20 INIT_FADE_ERRLISTS;
21 static struct fade_args_info conf;
22
23 enum mixer_id {MIXER_ENUM};
24 static char *mixer_name[] = {MIXER_NAMES};
25 DECLARE_MIXER_INITS;
26 static struct mixer supported_mixer[] = {MIXER_ARRAY};
27 #define FOR_EACH_MIXER(i) for ((i) = 0; (i) < NUM_SUPPORTED_MIXERS; (i)++)
28
29 static int loglevel;
30 static __printf_2_3 void date_log(int ll, const char *fmt, ...)
31 {
32 va_list argp;
33 time_t t1;
34 struct tm *tm;
35
36 if (ll < loglevel)
37 return;
38 time(&t1);
39 tm = localtime(&t1);
40 fprintf(stderr, "%d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec);
41 va_start(argp, fmt);
42 vprintf(fmt, argp);
43 va_end(argp);
44 }
45 __printf_2_3 void (*para_log)(int, const char*, ...) = date_log;
46
47 static int set_channel(struct mixer *m, struct mixer_handle *h, const char *channel)
48 {
49
50 PARA_NOTICE_LOG("using %s mixer channel\n", channel?
51 channel : "default");
52 return m->set_channel(h, channel);
53 }
54
55 /* Fade to new volume in fade_time seconds. */
56 static int fade(struct mixer *m, struct mixer_handle *h, int new_vol, int fade_time)
57 {
58 int vol, diff, incr, ret;
59 unsigned secs;
60 struct timespec ts;
61 unsigned long long tmp, tmp2; /* Careful with that axe, Eugene! */
62
63 if (fade_time <= 0)
64 return m->set(h, new_vol);
65 secs = fade_time;
66 ret = m->get(h);
67 if (ret < 0)
68 goto out;
69 vol = ret;
70 PARA_NOTICE_LOG("fading %s from %d to %d in %d seconds\n",
71 conf.mixer_channel_arg, vol, new_vol, secs);
72 diff = new_vol - vol;
73 if (!diff) {
74 sleep(secs);
75 goto out;
76 }
77 incr = diff > 0? 1: -1;
78 diff = diff > 0? diff: -diff;
79 tmp = secs * 1000 / diff;
80 tmp2 = tmp % 1000;
81 while ((new_vol - vol) * incr > 0) {
82 ts.tv_nsec = tmp2 * 1000000; /* really nec ?*/
83 ts.tv_sec = tmp / 1000; /* really nec ?*/
84 //printf("ts.tv_sec: %i\n", ts.tv_nsec);
85 vol += incr;
86 ret = m->set(h, vol);
87 if (ret < 0)
88 goto out;
89 //printf("vol = %i\n", vol);
90 nanosleep(&ts, NULL);
91 }
92 out:
93 return ret;
94 }
95
96 static void client_cmd(const char *cmd)
97 {
98 int ret, status, fds[3] = {0, 0, 0};
99 pid_t pid;
100 char *cmdline = make_message(BINDIR "/para_client %s", cmd);
101
102 PARA_NOTICE_LOG("%s\n", cmdline);
103 ret = para_exec_cmdline_pid(&pid, cmdline, fds);
104 free(cmdline);
105 if (ret < 0) {
106 PARA_ERROR_LOG("%s\n", para_strerror(-ret));
107 goto fail;
108 }
109 do
110 pid = waitpid(pid, &status, 0);
111 while (pid == -1 && errno == EINTR);
112 if (pid < 0) {
113 PARA_ERROR_LOG("%s\n", strerror(errno));
114 goto fail;
115 }
116 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
117 goto fail;
118 return;
119 fail:
120 PARA_EMERG_LOG("command \"%s\" failed\n", cmd);
121 exit(EXIT_FAILURE);
122 }
123
124 static void change_afs_mode(char *afs_mode)
125 {
126 char *cmd;
127
128 client_cmd("stop");
129 if (!afs_mode)
130 return;
131 cmd = make_message("select %s", afs_mode);
132 client_cmd(cmd);
133 free(cmd);
134 }
135
136 static int set_initial_volume(struct mixer *m, struct mixer_handle *h)
137 {
138 int i, ret;
139
140 for (i = 0; i < conf.ivol_given; i++) {
141 char *p, *ch, *arg = para_strdup(conf.ivol_arg[i]);
142 int32_t iv;
143 p = strchr(arg, ':');
144 if (p) {
145 *p = '\0';
146 p++;
147 ch = arg;
148 } else {
149 p = arg;
150 ch = NULL;
151 }
152 ret = para_atoi32(p, &iv);
153 if (ret < 0) {
154 free(arg);
155 return ret;
156 }
157 ret = set_channel(m, h, ch);
158 if (!ch)
159 ch = "default";
160 if (ret < 0) {
161 PARA_WARNING_LOG("ignoring channel %s\n", ch);
162 ret = 0;
163 } else {
164 PARA_INFO_LOG("initial volume %s: %d\n", ch, iv);
165 ret = m->set(h, iv);
166 }
167 free(arg);
168 if (ret < 0)
169 return ret;
170 }
171 return 1;
172 }
173
174 static int sweet_dreams(struct mixer *m, struct mixer_handle *h)
175 {
176 time_t t1, wake_time_epoch;
177 unsigned int delay;
178 struct tm *tm;
179 int ret, min = conf.wake_min_arg;
180 char *fo_mood = conf.fo_mood_arg;
181 char *fi_mood = conf.fi_mood_arg;
182 char *sleep_mood = conf.sleep_mood_arg;
183 int fit = conf.fi_time_arg;
184 int fot = conf.fo_time_arg;
185 int fiv = conf.fi_vol_arg;
186 int fov = conf.fo_vol_arg;
187
188 /* calculate wake time */
189 time(&t1);
190 if (conf.wake_hour_given) {
191 int hour = conf.wake_hour_arg;
192 tm = localtime(&t1);
193 if (tm->tm_hour > hour || (tm->tm_hour == hour && tm->tm_min> min)) {
194 t1 += 86400; /* wake time is tomorrow */
195 tm = localtime(&t1);
196 }
197 tm->tm_hour = hour;
198 tm->tm_min = min;
199 tm->tm_sec = 0;
200 } else {
201 t1 += 9 * 60 * 60; /* nine hours from now */
202 PARA_INFO_LOG("default wake time: %lu\n", (long unsigned)t1);
203 tm = localtime(&t1);
204 }
205 wake_time_epoch = mktime(tm);
206 PARA_INFO_LOG("waketime: %u:%02u\n", tm->tm_hour, tm->tm_min);
207 client_cmd("stop");
208 sleep(1);
209 if (fot) {
210 ret = set_initial_volume(m, h);
211 if (ret < 0)
212 return ret;
213 change_afs_mode(fo_mood);
214 client_cmd("play");
215 ret = set_channel(m, h, conf.mixer_channel_arg);
216 if (ret < 0)
217 return ret;
218 ret = fade(m, h, fov, fot);
219 if (ret < 0)
220 return ret;
221 } else {
222 ret = m->set(h, fov);
223 if (ret < 0)
224 return ret;
225 }
226 if (conf.sleep_mood_given) {
227 change_afs_mode(sleep_mood);
228 client_cmd("play");
229 } else
230 client_cmd("stop");
231 if (!fit)
232 return 1;
233 change_afs_mode(fi_mood);
234 for (;;) {
235 time(&t1);
236 if (wake_time_epoch <= t1 + fit)
237 break;
238 delay = wake_time_epoch - t1 - fit;
239 PARA_INFO_LOG("sleeping %u seconds (%u:%02u)\n",
240 delay, delay / 3600,
241 (delay % 3600) / 60);
242 sleep(delay);
243 }
244 client_cmd("play");
245 ret = fade(m, h, fiv, fit);
246 PARA_INFO_LOG("fade complete, returning\n");
247 return ret;
248 }
249
250 static int snooze(struct mixer *m, struct mixer_handle *h)
251 {
252 int ret, val;
253
254 if (conf.so_time_arg <= 0)
255 return 1;
256 ret = m->get(h);
257 if (ret < 0)
258 return ret;
259 val = ret;
260 if (val < conf.so_vol_arg)
261 ret = m->set(h, conf.so_vol_arg);
262 else
263 ret = fade(m, h, conf.so_vol_arg, conf.so_time_arg);
264 if (ret < 0)
265 return ret;
266 client_cmd("pause");
267 PARA_NOTICE_LOG("%d seconds snooze time...\n", conf.snooze_time_arg);
268 sleep(conf.snooze_time_arg);
269 client_cmd("play");
270 return fade(m, h, conf.si_vol_arg, conf.si_time_arg);
271 }
272
273 static int configfile_exists(void)
274 {
275 static char *config_file;
276
277 if (!conf.config_file_given) {
278 char *home = para_homedir();
279 free(config_file);
280 config_file = make_message("%s/.paraslash/fade.conf", home);
281 free(home);
282 conf.config_file_arg = config_file;
283 }
284 return file_exists(conf.config_file_arg);
285 }
286
287 static void init_mixers(void)
288 {
289 int i;
290
291 FOR_EACH_MIXER(i) {
292 struct mixer *m = &supported_mixer[i];
293 PARA_DEBUG_LOG("initializing mixer API #%d (%s)\n",
294 i, mixer_name[i]);
295 m->init(m);
296 }
297 }
298
299 static int set_val(struct mixer *m, struct mixer_handle *h)
300 {
301 return m->set(h, conf.val_arg);
302 }
303
304 static struct mixer *get_mixer_or_die(void)
305 {
306 int i;
307
308 if (!conf.mixer_api_given)
309 i = DEFAULT_MIXER;
310 else
311 FOR_EACH_MIXER(i)
312 if (!strcmp(mixer_name[i], conf.mixer_api_arg))
313 break;
314 if (i < NUM_SUPPORTED_MIXERS) {
315 PARA_NOTICE_LOG("using %s mixer API\n", mixer_name[i]);
316 return supported_mixer + i;
317 }
318 printf("available mixer APIs: ");
319 FOR_EACH_MIXER(i) {
320 int d = (i == DEFAULT_MIXER);
321 printf("%s%s%s ", d? "[" : "", mixer_name[i], d? "]" : "");
322 }
323 printf("\n");
324 exit(EXIT_FAILURE);
325 }
326
327 __noreturn static void print_help_and_die(void)
328 {
329 struct ggo_help h = DEFINE_GGO_HELP(fade);
330 bool d = conf.detailed_help_given;
331
332 ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
333 exit(0);
334 }
335
336 int main(int argc, char *argv[])
337 {
338 int ret;
339 struct mixer *m;
340 struct mixer_handle *h = NULL;
341
342 fade_cmdline_parser(argc, argv, &conf);
343 loglevel = get_loglevel_by_name(conf.loglevel_arg);
344 version_handle_flag("fade", conf.version_given);
345 if (conf.help_given || conf.detailed_help_given)
346 print_help_and_die();
347 ret = configfile_exists();
348 if (!ret && conf.config_file_given) {
349 PARA_EMERG_LOG("can not read config file %s\n",
350 conf.config_file_arg);
351 exit(EXIT_FAILURE);
352 }
353 if (ret) {
354 struct fade_cmdline_parser_params params = {
355 .override = 0,
356 .initialize = 0,
357 .check_required = 0,
358 .check_ambiguity = 0,
359 .print_errors = 1
360 };
361 fade_cmdline_parser_config_file(conf.config_file_arg,
362 &conf, &params);
363 loglevel = get_loglevel_by_name(conf.loglevel_arg);
364 }
365 init_mixers();
366 m = get_mixer_or_die();
367 ret = m->open(conf.mixer_device_arg, &h);
368 if (ret < 0)
369 goto out;
370 ret = set_channel(m, h, conf.mixer_channel_arg);
371 if (ret == -E_BAD_CHANNEL) {
372 char *channels = m->get_channels(h);
373 printf("Available channels: %s\n", channels);
374 free(channels);
375 }
376 if (ret < 0)
377 goto out;
378 switch (conf.mode_arg) {
379 case mode_arg_fade:
380 ret = fade(m, h, conf.fade_vol_arg, conf.fade_time_arg);
381 break;
382 case mode_arg_snooze:
383 ret = snooze(m, h);
384 break;
385 case mode_arg_set:
386 ret = set_val(m, h);
387 break;
388 default: /* sleep mode */
389 ret = sweet_dreams(m, h);
390 break;
391 }
392 out:
393 m->close(&h);
394 if (ret < 0)
395 PARA_EMERG_LOG("%s\n", para_strerror(-ret));
396 return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
397 }