Update the LED pattern helper to use a more robust testing method
[led-pattern-ed] / src / led-pattern-helper.vala
1 /* This file is part of LED Pattern Editor.
2  *
3  * Copyright (C) 2010 Philipp Zabel
4  *
5  * LED Pattern Editor is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * LED Pattern Editor is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with LED Pattern Editor. If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 bool sysfs_write (string filename, string contents) {
20         var f = FileStream.open (filename, "w");
21         if (f == null) {
22                 stderr.printf ("failed to open '%s' for writing\n", filename);
23                 return false;
24         }
25         f.printf ("%s", contents);
26         return true;
27 }
28
29 public bool test_pattern (string pattern, bool apply) {
30         string[] key_value = pattern.split ("=");
31
32         if (key_value.length != 2) {
33                 stderr.printf ("pattern does not contain '=': %s\n", pattern);
34                 return false;
35         }
36
37         string[] p = key_value[1].split (";");
38         if (p.length != 6) {
39                 stderr.printf ("pattern does not contain 6 components: %d\n", p.length);
40                 return false;
41         }
42
43         if (p[0].has_prefix ("Pattern")) {
44                 stderr.printf ("pattern name doesn't start with 'Pattern': '%s'\n", p[0]);
45                 return false;
46         }
47
48         // priority = p[0].to_int ();
49         // screen_on = p[1].to_int ();
50         // timeout = p[2].to_int ();
51
52         int led_map1 = 0x000;
53         int led_map2 = 0x000;
54         if ("r" in p[3])
55                 led_map1 |= 0x001;
56         if ("R" in p[3])
57                 led_map2 |= 0x001;
58         if ("g" in p[3])
59                 led_map1 |= 0x010;
60         if ("G" in p[3])
61                 led_map2 |= 0x010;
62         if ("b" in p[3])
63                 led_map1 |= 0x100;
64         if ("B" in p[3])
65                 led_map2 |= 0x100;
66
67         if ((led_map1 & led_map2) != 0) {
68                 stderr.printf ("pattern assigns a led to both channels: '%s'\n", p[3]);
69                 return false;
70         }
71
72         string led_map = "";
73         if (0x001 in led_map1)
74                 led_map += "r";
75         if (0x001 in led_map2)
76                 led_map += "R";
77         if (0x010 in led_map1)
78                 led_map += "g";
79         if (0x010 in led_map2)
80                 led_map += "G";
81         if (0x100 in led_map1)
82                 led_map += "b";
83         if (0x100 in led_map2)
84                 led_map += "B";
85
86         if (led_map != p[3]) {
87                 stderr.printf ("pattern contains invalid led map: '%s\n", p[3]);
88                 return false;
89         }
90
91         string[] led_currents = { "2", "2", "2" };
92         switch (led_map1 | led_map2) {
93         case 0x001:
94                 led_currents = { "8", "0", "2" };
95                 break;
96         case 0x010:
97                 led_currents = { "2", "2", "2" };
98                 break;
99         case 0x100:
100                 led_currents = { "2", "2", "2" };
101                 break;
102         case 0x011:
103                 led_currents = { "20", "2", "0" };
104                 break;
105         case 0x101:
106                 // TODO: led_currents?
107                 break;
108         case 0x110:
109                 // TODO: led_currents?
110                 break;
111         case 0x111:
112                 led_currents = { "8", "2", "2" };
113                 break;
114         }
115
116         if (p[4].length > 16*4 || p[5].length > 16*4) {
117                 stderr.printf ("engine1 pattern too long!\n");
118                 return false;
119         }
120
121         if (!(p[4].has_prefix ("9d80"))) {
122                 stderr.printf ("engine1 pattern doesn't start with reset mux command\n");
123                 return false;
124         }
125
126         if (!(p[4].has_suffix ("0000")) && !(p[4].has_suffix ("c000"))) {
127                 stderr.printf ("engine1 pattern doesn't end with repeat or stop command\n");
128                 return false;
129         }
130
131         if (p[5].length > 16*4 || p[5].length > 16*4) {
132                 stderr.printf ("engine2 pattern too long!\n");
133                 return false;
134         }
135
136         if (!(p[5].has_prefix ("9d80"))) {
137                 stderr.printf ("engine2 pattern doesn't start with reset mux command\n");
138                 return false;
139         }
140
141         if (!(p[5].has_suffix ("0000")) && !(p[5].has_suffix ("c000"))) {
142                 stderr.printf ("engine2 pattern doesn't end with repeat or stop command\n");
143                 return false;
144         }
145
146         if (apply == false)
147                 return true;
148
149         string mux1 = "0000%03x00\n".printf (led_map1);
150         string mux2 = "0000%03x00\n".printf (led_map2);
151         string pattern1 = p[4];
152         string pattern2 = p[5];
153
154         sysfs_write ("/sys/class/i2c-adapter/i2c-2/2-0032/engine1_mode", "disabled");
155         sysfs_write ("/sys/class/i2c-adapter/i2c-2/2-0032/engine2_mode", "disabled");
156         sysfs_write ("/sys/class/leds/lp5523:r/brightness", "0");
157         sysfs_write ("/sys/class/leds/lp5523:g/brightness", "0");
158         sysfs_write ("/sys/class/leds/lp5523:b/brightness", "0");
159
160         sysfs_write ("/sys/class/i2c-adapter/i2c-2/2-0032/engine1_mode", "load");
161         sysfs_write ("/sys/class/i2c-adapter/i2c-2/2-0032/engine1_leds", mux1);
162         sysfs_write ("/sys/class/i2c-adapter/i2c-2/2-0032/engine1_load", pattern1);
163         sysfs_write ("/sys/class/i2c-adapter/i2c-2/2-0032/engine2_mode", "load");
164         sysfs_write ("/sys/class/i2c-adapter/i2c-2/2-0032/engine2_leds", mux2);
165         sysfs_write ("/sys/class/i2c-adapter/i2c-2/2-0032/engine2_load", pattern2);
166         sysfs_write ("/sys/class/i2c-adapter/i2c-2/2-0032/engine2_mode", "run");
167         sysfs_write ("/sys/class/i2c-adapter/i2c-2/2-0032/engine1_mode", "run");
168
169         sysfs_write ("/sys/class/leds/lp5523:r/led_current", led_currents[0]);
170         sysfs_write ("/sys/class/leds/lp5523:g/led_current", led_currents[1]);
171         sysfs_write ("/sys/class/leds/lp5523:b/led_current", led_currents[2]);
172
173         return true;
174 }
175
176 bool mce_ini_check (string new_mce_ini) {
177         var f = FileStream.open ("/etc/mce/mce.ini", "r");
178         var g = FileStream.open (new_mce_ini, "r");
179
180         if (f == null || g == null) {
181                 stderr.printf ("failed to open /etc/mce/mce.ini and %s\n", new_mce_ini);
182                 return false;
183         }
184
185         var line1 = f.read_line ();
186         var line2 = g.read_line ();
187         while (line1 != null && line2 != null) {
188                 if (line1 == line2) {
189                         line1 = f.read_line ();
190                         line2 = g.read_line ();
191                         continue;
192                 }
193
194                 string[] key_value1 = line1.split ("=");
195                 string[] key_value2 = line2.split ("=");
196
197                 if (key_value1.length != 2 || key_value2.length != 2 || !line1.has_prefix ("Pattern")) {
198                         stderr.printf ("not allowed to change anything but led patterns:\n-%s\n+%s\n",
199                                        line1, line2);
200                         return false;
201                 }
202
203                 if (key_value1[0] != key_value2[0]) {
204                         stderr.printf ("not allowed to change pattern names\n-%s\n+%s\n",
205                                        line1, line2);
206                         return false;
207                 }
208
209                 if (!test_pattern (line2, false))
210                         return false;
211
212                 line1 = f.read_line ();
213                 line2 = g.read_line ();
214         }
215
216         if (line1 != line2) {
217                 stderr.printf ("different number of lines\n");
218                 return false;
219         }
220
221         return true;
222 }
223
224 int mce_copy (string source, string destination) {
225         var f = FileStream.open (source, "r");
226         var g = FileStream.open (destination, "w");
227
228         if (f == null || g == null) {
229                 return 1;
230         }
231
232         var line = f.read_line ();
233         while (line != null) {
234                 g.printf ("%s\n", line);
235                 line = f.read_line ();
236         }
237
238         return 0;
239 }
240
241 public static int main (string[] args) {
242         string usage = "usage: led-pattern-helper test <Pattern>\n";
243
244         if (args.length != 3) {
245                 stderr.printf (usage);
246                 return -1;
247         }
248
249         if (args[1] == "test") {
250                 if (!test_pattern (args[2], true))
251                         return -1;
252
253                 return 0;
254         }
255
256         if (args[1] == "save") {
257                 if (!FileUtils.test (args[2], FileTest.IS_REGULAR)) {
258                         stderr.printf ("not a regular file: %s\n", args[2]);
259                         return -1;
260                 }
261
262                 if (!mce_ini_check (args[2]))
263                         return -1;
264
265                 // Ok, we're good to go
266                 int result = mce_copy ("/etc/mce/mce.ini", "/etc/mce/mce.ini.led-pattern-old");
267                 if (result != 0) {
268                         stderr.printf ("failed to copy old mce.ini to mce.ini.led-pattern-old\n");
269                         return -1;
270                 }
271                 result = mce_copy (args[2], "/etc/mce/mce.ini.led-pattern-new");
272                 if (result != 0) {
273                         stderr.printf ("failed to copy new mce.ini to mce.ini.led-pattern-new\n");
274                         return -1;
275                 }
276                 result = FileUtils.rename ("/etc/mce/mce.ini.led-pattern-new", "/etc/mce/mce.ini");
277                 if (result != 0) {
278                         stderr.printf ("failed to replace MCE configuration: %d\n", result);
279                         return -1;
280                 }
281
282                 // Moment of truth, restart MCE
283                 try {
284                         int exit_status;
285                         string error;
286                         var command = "initctl stop mce";
287                         Process.spawn_command_line_sync (command, null, out error, out exit_status);
288                         if (exit_status != 0) {
289                                 stderr.printf ("stopping mce failed: %d\n%s", exit_status, error);
290                                 return -1;
291                         }
292                         command = "sleep 1";
293                         Process.spawn_command_line_sync (command, null, out error, out exit_status);
294                         command = "initctl start mce";
295                         Process.spawn_command_line_sync (command, null, out error, out exit_status);
296                         if (exit_status != 0) {
297                                 stderr.printf ("starting mce failed: %d\n%s", exit_status, error);
298                                 return -1;
299                         }
300                 } catch (SpawnError e) {
301                         stderr.printf ("restarting mce failed: %s", e.message);
302                         return -1;
303                 }
304
305                 return 0;
306         }
307
308         stderr.printf (usage);
309         return -1;
310 }