Use CRITICAL job priority class for long running dispatcher jobs
[strongswan.git] / src / libcharon / plugins / led / led_listener.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "led_listener.h"
17
18 #include <errno.h>
19
20 #include <daemon.h>
21 #include <threading/mutex.h>
22 #include <processing/jobs/callback_job.h>
23
24 typedef struct private_led_listener_t private_led_listener_t;
25
26 /**
27 * Private data of an led_listener_t object.
28 */
29 struct private_led_listener_t {
30
31 /**
32 * Public led_listener_t interface.
33 */
34 led_listener_t public;
35
36 /**
37 * Mutex
38 */
39 mutex_t *mutex;
40
41 /**
42 * Number of established IKE_SAs
43 */
44 int count;
45
46 /**
47 * LED blink on/off time, in ms
48 */
49 int blink_time;
50
51 /**
52 * Activity LED fd, if any
53 */
54 FILE *activity;
55
56 /**
57 * Activity LED maximum brightness
58 */
59 int activity_max;
60 };
61
62 /**
63 * Open a LED brightness control file, get max brightness
64 */
65 static FILE *open_led(char *name, int *max_brightness)
66 {
67 char path[PATH_MAX];
68 FILE *f;
69
70 if (!name)
71 {
72 return NULL;
73 }
74
75 *max_brightness = 1;
76 snprintf(path, sizeof(path), "/sys/class/leds/%s/max_brightness", name);
77 f = fopen(path, "r");
78 if (f)
79 {
80 if (fscanf(f, "%d\n", max_brightness) != 1)
81 {
82 DBG1(DBG_CFG, "reading max brightness for '%s' failed: %s, using 1",
83 name, strerror(errno));
84 }
85 fclose(f);
86 }
87 else
88 {
89 DBG1(DBG_CFG, "reading max_brightness for '%s' failed: %s, using 1",
90 name, strerror(errno));
91 }
92
93 snprintf(path, sizeof(path), "/sys/class/leds/%s/brightness", name);
94 f = fopen(path, "w");
95 if (!f)
96 {
97 DBG1(DBG_CFG, "opening LED file '%s' failed: %s", path, strerror(errno));
98 }
99 return f;
100 }
101
102 /**
103 * Set a LED to a given brightness
104 */
105 static void set_led(FILE *led, int brightness)
106 {
107 if (led)
108 {
109 if (fprintf(led, "%d\n", brightness) <= 0 ||
110 fflush(led) != 0)
111 {
112 DBG1(DBG_CFG, "setting LED brightness failed: %s", strerror(errno));
113 }
114 }
115 }
116
117 /**
118 * Plugin unloaded?
119 */
120 static bool plugin_gone = FALSE;
121
122 /**
123 * Reset activity LED after timeout
124 */
125 static job_requeue_t reset_activity_led(private_led_listener_t *this)
126 {
127 if (!plugin_gone)
128 { /* TODO: fix race */
129 this->mutex->lock(this->mutex);
130 if (this->count)
131 {
132 set_led(this->activity, this->activity_max);
133 }
134 else
135 {
136 set_led(this->activity, 0);
137 }
138 this->mutex->unlock(this->mutex);
139 }
140 return JOB_REQUEUE_NONE;
141 }
142
143 /**
144 * Blink the activity LED
145 */
146 static void blink_activity(private_led_listener_t *this)
147 {
148 if (this->activity)
149 {
150 this->mutex->lock(this->mutex);
151 if (this->count)
152 {
153 set_led(this->activity, 0);
154 }
155 else
156 {
157 set_led(this->activity, this->activity_max);
158 }
159 lib->scheduler->schedule_job_ms(lib->scheduler, (job_t*)
160 callback_job_create_with_prio((callback_job_cb_t)reset_activity_led,
161 this, NULL, NULL, JOB_PRIO_CRITICAL), this->blink_time);
162 this->mutex->unlock(this->mutex);
163 }
164 }
165
166 METHOD(listener_t, ike_state_change, bool,
167 private_led_listener_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
168 {
169 this->mutex->lock(this->mutex);
170 if (state == IKE_ESTABLISHED && ike_sa->get_state(ike_sa) != IKE_ESTABLISHED)
171 {
172 this->count++;
173 if (this->count == 1)
174 {
175 set_led(this->activity, this->activity_max);
176 }
177 }
178 if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED && state != IKE_ESTABLISHED)
179 {
180 this->count--;
181 if (this->count == 0)
182 {
183 set_led(this->activity, 0);
184 }
185 }
186 this->mutex->unlock(this->mutex);
187 return TRUE;
188 }
189
190 METHOD(listener_t, message_hook, bool,
191 private_led_listener_t *this, ike_sa_t *ike_sa,
192 message_t *message, bool incoming)
193 {
194 if (incoming || message->get_request(message))
195 {
196 blink_activity(this);
197 }
198 return TRUE;
199 }
200
201 METHOD(led_listener_t, destroy, void,
202 private_led_listener_t *this)
203 {
204 this->mutex->lock(this->mutex);
205 set_led(this->activity, 0);
206 plugin_gone = TRUE;
207 this->mutex->unlock(this->mutex);
208 if (this->activity)
209 {
210 fclose(this->activity);
211 }
212 this->mutex->destroy(this->mutex);
213 free(this);
214 }
215
216 /**
217 * See header
218 */
219 led_listener_t *led_listener_create()
220 {
221 private_led_listener_t *this;
222
223 INIT(this,
224 .public = {
225 .listener = {
226 .ike_state_change = _ike_state_change,
227 .message = _message_hook,
228 },
229 .destroy = _destroy,
230 },
231 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
232 .blink_time = lib->settings->get_int(lib->settings,
233 "charon.plugins.led.blink_time", 50),
234 );
235
236 this->activity = open_led(lib->settings->get_str(lib->settings,
237 "charon.plugins.led.activity_led", NULL), &this->activity_max);
238 set_led(this->activity, 0);
239
240 return &this->public;
241 }