1 /***************************************************************************
2                           lineak_core_functions.cpp  -  description
3                              -------------------
4     begin                : Mon Feb 10 2003
5     copyright            : (C) 2003 by Sheldon Lee Wen
6     email                : leewsb@hotmail.com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 #include <lineak/lineak_core_functions.h>
18 
19 #include <fstream>
20 #include <iostream>
21 #include <sstream>
22 #include <cstdlib>
23 #include <stdlib.h>
24 #include <unistd.h>
25 
26 
27 
28 extern "C" {
29 #ifdef HAVE_GETOPT_H
30 #  define HAVE_GETOPT_LONG
31 #  include <getopt.h>
32 #endif
33 #ifndef X_NOT_STDC_ENV
34 #	include <stdlib.h>
35 #else
36 	extern char *getenv();
37 #endif
38 
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <dirent.h>
42 #include <stdio.h>
43 #include <errno.h>
44 #include <unistd.h>
45 #include <signal.h>
46 #include <pthread.h>
47 #include <semaphore.h>
48 
49 #include <X11/Xlib.h>
50 }
51 
52 #include <lineak/definitions.h>
53 #include <lineak/saver.h>
54 #include <lineak/configloader.h>
55 #include <lineak/lineak_util_functions.h>
56 #include <lineak/defloader.h>
57 #include <lineak/ldef.h>
58 #include <lineak/lkbd.h>
59 #include <lineak/lkey.h>
60 #include <lineak/lockctrl.h>
61 #include <lineak/lcommand.h>
62 #include <lineak/msgpasser.h>
63 #include <lineak/lobject.h>
64 
65 //#include "commandexec.h"
66 
67 extern bool verbose;
68 extern bool very_verbose;
69 
70 using namespace lineak_util_functions;
71 using namespace std;
72 
73 
msg(const string message)74 void lineak_core_functions::msg(const string message) {
75    lineak_core_functions::msg(message.c_str());
76 }
error(const string message)77 void lineak_core_functions::error(const string message) {
78    lineak_core_functions::error(message.c_str());
79 }
fatal(const string message)80 void lineak_core_functions::fatal(const string message) {
81    lineak_core_functions::fatal(message.c_str());
82 }
83 
vmsg(const string message)84 void lineak_core_functions::vmsg(const string message) {
85 	   lineak_core_functions::vmsg(message.c_str());
86 }
verror(const string message)87 void lineak_core_functions::verror(const string message) {
88 	   lineak_core_functions::verror(message.c_str());
89 }
vfatal(const string message)90 void lineak_core_functions::vfatal(const string message) {
91 	   lineak_core_functions::vfatal(message.c_str());
92 }
93 
msg(const char * message)94 void lineak_core_functions::msg(const char* message) {
95     if (verbose) {
96        cout << message << endl;
97     }
98 }
error(const char * message)99 void lineak_core_functions::error(const char* message) {
100     if (verbose) {
101        cerr << message << endl;
102     }
103 }
fatal(const char * message)104 void lineak_core_functions::fatal(const char* message) {
105     if (verbose) {
106        cerr << "Fatal Error: " << message << endl;
107        msgPasser message;
108        message.start();
109        message.sendMessage(msgPasser::EXIT,"exit");
110     }
111 }
112 
vmsg(const char * message)113 void lineak_core_functions::vmsg(const char* message) {
114     if (very_verbose) {
115         cout << message << endl;
116     }
117 }
verror(const char * message)118 void lineak_core_functions::verror(const char* message) {
119     if (very_verbose) {
120         cerr << message << endl;
121     }
122 }
vfatal(const char * message)123 void lineak_core_functions::vfatal(const char* message) {
124     if (very_verbose) {
125         cerr << "Fatal Error: " << message << endl;
126         msgPasser message;
127         message.start();
128         message.sendMessage(msgPasser::EXIT,"exit");
129     }
130 }
131 
132 /* Parse the configuration file.
133 		prefs: is a ConfigDirectives object. It is loaded with values obtained from the command line, and
134 			other system directives and defaults.
135 		       In this function, it can contain an alternative location for the users config file.
136 		myConfig: is a configuration object. It will be loaded with values from the config loader.
137 
138     Return false if we should exist b/c no config file exists, or we could not load
139 		any configuration information.
140 		Return true if we were successful in loading configuration information.
141 		*/
142 
parseconffile(ConfigDirectives & prefs,LConfig & myConfig)143 bool lineak_core_functions::parseconffile(ConfigDirectives & prefs, LConfig & myConfig) {
144   string conffilename;
145   string homedir = getenv("HOME");
146   /* set (globally) full path of config file ($HOME/.lineak/CONFIGFILE) */
147   if (prefs.getValue(_CD_USERCONFFILE) == snull) {
148   	if (!dir_exists(homedir + LINEAKDIR))
149    		create_homedir();
150    	conffilename = homedir + CONFFILE;
151   } else
152   	conffilename = prefs.getValue(_CD_USERCONFFILE);
153 
154 	/* does the file exist? */
155   if (conffilename == snull || !file_exists(conffilename)) {
156 		conffilename = SYS_CONFFILE;
157 		if (!file_exists(conffilename)) {
158 	   	/* file does not exist */
159 			cerr << "*** A configuration file was not found! ***" << endl;
160 			cerr << "       Please run lineakd -l to list the supported keyboards." << endl;
161 			cerr << "       Then run \"lineakd -c TYPE\" to create a configuration file for your keyboard." << endl;
162 			cerr << "       Once that is done, edit " << (homedir + CONFFILE) << " or " <<  SYS_CONFFILE << " to map your keys to commands, then run lineakd again." << endl;
163 	   	return(false);
164 		}
165   }
166 //FIXME: reconcile command line directives, with directives and defaults from the plugins, as well as our program
167 //	       directives and defaults. Then pass them here to the configldr object.
168 // prefs at this point is made up of first: directives and defaults from all of the plugins.
169 // second: any values specified on the command line are added to or overwrite the values in the directives and defaults.
170 // Now, we will pass this to the configloader, who will likewise overwrite and
171    vmsg("Instantiating ConfigLoader");
172    msg("Loading a config file");
173    //prefs.print(cout);
174    msg("conffilename = :" + conffilename);
175    ConfigLoader configldr(conffilename, prefs);
176    //cout << "Loading a config file. Got a configldr\n";
177    //(configldr.loadConfig()).print(cout);
178    //cout << "Doing it again.\n" << endl;
179    //configldr >> myConfig;
180    myConfig = configldr.loadConfig();
181    //sleep(2);
182    msg("Displaying config.");
183    if (verbose)
184 	   myConfig.print(cout);
185 
186    vmsg("Checking to see if it is empty");
187    if (myConfig.isEmpty()) {
188 		cerr << "*** Configuration file " << conffilename << " could not be loaded" << endl;
189 		cerr << "		Have you defined actions for your keys?" << endl;
190 		return(false);
191    }
192    vmsg("Returning from parseconffile");
193    return (true);
194 }
195 
196 /* Parse the keyboard definition file.
197 		prefs: is a ConfigDirectives object. It can be loaded with values obtained from the command line
198 		       or loaded with other relavant values from another program. In this function, it can contain
199 		       an alternative locations for the user and the system definitions files.
200 
201 		Return false if we should exit b/c of no definitions loaded.
202 		Return true if we are all good. */
parsedeffile(ConfigDirectives & prefs,LDef & def)203 bool lineak_core_functions::parsedeffile(ConfigDirectives & prefs, LDef & def) {
204   string usrdeffilename = prefs.getValue(_CD_USERDEFFILE); //userdefconf;
205   string deffilename = prefs.getValue(_CD_SYSDEFFILE); //defconf;
206   string homedir = getenv("HOME");
207   LDef udef;
208 
209    if (!dir_exists(homedir + LINEAKDIR))
210       create_homedir();
211 
212    if (usrdeffilename == snull) {
213 		usrdeffilename = homedir + LINEAKDIR;
214 		usrdeffilename += DEFFILE;
215    }
216 
217    if (file_exists(usrdeffilename)) {
218       msg("Parsing: " + usrdeffilename);
219       /* it exists, parse the contents */
220       DefLoader defldr(usrdeffilename);
221       udef = defldr.loadDef();
222       if ( udef.isEmpty() )
223       /* error parsing file */
224       cerr << "*** Error occurred while reading definition data from " << usrdeffilename << endl;
225    }
226 
227    if (deffilename == snull ) {
228       /* SET full path of definition files ($sysconfdir/lineakkb.def) */
229       deffilename = CONFDIR;
230       deffilename += "/";
231       deffilename += DEFFILE;
232    }
233 
234    if ( deffilename != usrdeffilename ) {
235       msg("Parsing: " + deffilename);
236       /* does the file exist? */
237       if (file_exists(deffilename)) {
238 	/* it exists, parse the contents */
239 	DefLoader sysdefldr(deffilename);
240 	def = sysdefldr.loadDef();
241         if ( def.isEmpty() ) {
242 	   /* error parsing file */
243 	   cerr << "*** Error occurred while reading definition data from " << deffilename << endl;
244 	}
245       }
246    }
247 
248   /* If we cannot load any keyboard definitions */
249   if ( def.isEmpty()  && udef.isEmpty() ) {
250      /* file does not exist */
251      cerr << "*** FATAL ERROR: No keyboard defintions could be loaded!" << endl;
252      return(false);
253   }
254   if (!udef.isEmpty())   {
255      /** Append the user definition data to the system data */
256      def.addKeyboards(udef);
257   }
258   vmsg("Outputting the definition file!");
259   if (very_verbose) cout << def;
260    return (true);
261 }
262 /*
263 void lineak_core_functions::showplugins(PluginManager & plugins) {
264         cout << "\nLinEAK v" << VERSION << " -- Loaded Plugins:\n" << endl;
265 	vector<string>::iterator it = plugins.begin();
266 	while (it != plugins.end()) {
267 		cout << "  " << *it << endl;
268 		it++;
269 	}
270 	cout << endl;
271 	cout << "\nLinEAK v" << VERSION << " -- Loaded Macros:\n" << endl;
272 	it = macros.begin();
273 	while (it != macros.end()) {
274                 cout << "  " << *it << endl;
275                 it++;
276         }
277 }
278 */
showkeyboards(LDef & def)279 void lineak_core_functions::showkeyboards(LDef & def) {
280 	map<string,LKbd*> &tmp = def.getTable();
281 
282 	cout << "\nLinEAK v" << VERSION << " -- supported keyboards:\n" << endl;
283 	cout << " [TYPE]\t\t[Full name]\n" << endl;
284     for (map<string,LKbd*>::const_iterator m = tmp.begin(); m != tmp.end(); m++) {
285         if (m->first != snull)
286     	   printf(" %s%s%s %s\n",(m->first).c_str(),strlen((m->first).c_str())>=7?"\t":"\t\t",strcasecmp(strip((m->second)->brand, "\"").c_str(),"other")==0?"":strip((m->second)->brand, "\"").c_str(),strip((m->second)->model, "\"").c_str());
287     }
288 }
create_homedir(void)289 void lineak_core_functions::create_homedir(void) {
290 	string picsdir;
291 	string homedir = getenv("HOME");
292 	/** Make sure that the ~/.lineak directory exists */
293 	string lineakdir = homedir + LINEAKDIR;
294 	if (!dir_exists(lineakdir)) {
295 		if (mkdir(lineakdir.c_str(), 0755) == -1) {
296         	cout << "*** FATAL ERROR: unable to create directory" << lineakdir << endl;
297         	exit(1);
298     	}
299   	}
300   picsdir = homedir + PICSDIR;
301 	if (!dir_exists(picsdir)) {
302 		if (mkdir(picsdir.c_str(), 0755) == -1) {
303         	cout << "*** ERROR: unable to create directory" << picsdir << endl;
304    	}
305  	}
306 }
307 /** We assume at this point that the global definitions objects are created */
create_new_conf(ConfigDirectives & prefs,LDef & def)308 void lineak_core_functions::create_new_conf(ConfigDirectives & prefs, LDef & def) {
309 	//string conffilename = prefs.getValue("conffilename");
310 	//string cdromdev = prefs.getValue("CdromDevice");
311 	//string mixdev = prefs.getValue("MixerDevice");
312 	//LDef def = idef;
313 	string homedir = getenv("HOME");
314 	string kbtype = prefs.getValue(_CD_KEYBOARD_TYPE);
315 
316   	LCommand blank;
317 	LConfig config;
318 
319 	// Make sure we have definitions loaded and the kbtype is not empty.
320 	if (def.isEmpty() || kbtype == snull) {
321 		cerr << "Attempting to create a config file with no keyboard definitions loaded" << endl;
322 		exit(false);
323 	}
324 	// Attempt to get the definition for the keyboard.
325 	if (!def.hasKeyboard(kbtype)) {
326 		cerr << "*** ERROR: Invalid keyboard type: " << kbtype<< " \nTo find out the supported keyboard types, use: 'lineakd -l'\n" << endl;
327 		exit(false);
328 	}
329   	create_homedir();
330 	/* set (globally) full path of config file ($HOME/.lineak/CONFIGFILE) */
331   	if (prefs.getValue(_CD_USERCONFFILE) == snull)
332    		prefs.addValue(_CD_USERCONFFILE, string(homedir + CONFFILE));
333 
334   /* no CD-ROM device specified? */
335 //   if (cdromdev == snull) {
336 // 		cout << "*** No CD-ROM device specified, using the default /dev/cdrom" << endl;
337 // 		cdromdev = DEFAULT_CDROM_DEVICE;
338 // 	}
339   /* no mixer device specified? */
340 //   if (mixdev == snull) {
341 // 		cout << "*** No mixer device specified, using the default /dev/mixer" << endl;
342 // 		mixdev = DEFAULT_MIXER_DEVICE;
343 // 	}
344 
345 	//cout << "create_new_conf: kbtype = " << kbtype << endl;
346         //cout << "printing prefs from create_new_conf" << endl;
347 	//cout << prefs << endl;
348 	//cout << "printing defs from create_new_conf" << endl;
349 	//cout << def << endl;
350 
351   	config = LConfig (prefs);
352 
353   	LKbd & kbd = def.getKeyboard(kbtype);
354 	keycommand_info info;
355 /**  	const map< string, LObject*> & keytable = kbd.getObjects(); */
356         /** Setup the keyboard objects in the kbd object */
357 	/**
358   	for (map< string, LObject*>::const_iterator m = keytable.begin(); m != keytable.end(); m++) {
359 	        info.config_name = m->first;
360 		info.parsed_name = m->first;
361 		info.modifiers = 0;
362 		info.command = blank;
363 		config.addKeycomm(m->first,info);
364         }*/
365 	const vector<string> keylist = kbd.getNames();
366 	for (vector<string>::const_iterator m = keylist.begin(); m != keylist.end(); m++) {
367                 info.config_name = *m;
368                 info.parsed_name = *m;
369                 info.modifiers = 0;
370                 info.command = blank;
371                 config.addKeycomm(*m,info);
372         }
373 	Saver tmp(config.getFilename());
374 
375 	if (!tmp.saveFile(config))
376 		exit(false);
377 	else {
378 		cout << "\n*** Creating fresh configuration in " << config.getFilename() << "\n      for keyboard type: " << kbtype << endl;
379 		cout << "NOTE: Now please edit the file and bind commands to the keys,\n      or use klineakconfig :)\n" << endl;
380         }
381 	//cout << "create_new_conf: kbtype = " << kbtype << endl;
382         //cout << "printing prefs from create_new_conf second time" << endl;
383         //cout << prefs << endl;
384         //cout << "printing defs from create_new_conf second time" << endl;
385         //cout << def << endl;
386 	//cout << "printing config from create_new_conf second time" << endl;
387 	//cout << config << endl;
388 
389 }
390 
391 /* check if there's already a running lineakd for this $USER */
is_running(string process)392 bool lineak_core_functions::is_running(string process) {
393     uid_t myuid;
394     pid_t mypid, pid;
395     DIR *dir;
396     struct dirent *d;
397     struct stat s;
398     string str, path;
399 
400     mypid = getpid();
401     myuid = getuid();
402 
403    if ((dir = opendir("/proc")) == NULL)
404    {
405       cout << "Cannot open /proc" << endl;
406       return -1;
407    }
408 
409    if (verbose)
410 	   cout << "Looking for " << process << endl;
411 
412    while ((d = readdir(dir)) != NULL)
413    {
414       // See if this is a process
415       if ((pid = atoi(d->d_name)) == 0)
416          continue;
417      //if (verbose) cout << "pid = " << pid << " mypid = " << mypid << endl;
418       // Ignore the pid of the current running process
419       if (pid == mypid) {
420          //if (verbose) cout << "oops it's me, moving on." << endl;
421          continue;
422       }
423 
424       path = "/proc/" + string(d->d_name) + "/stat";
425 
426       // Only processes with the same UID as the current running process
427       if (stat(path.c_str(), &s) != 0 && s.st_uid != myuid)
428          continue;
429 
430       // Open the status file and verify the program name
431       ifstream in(path.c_str());
432       if (in.is_open()) {
433          in >> str;
434          in >> str;
435          in.close();
436 //	 if (verbose)
437 //		 cout << "Stat: " << str << endl;
438 
439 	 // Only look for the first 15 characters as linux cuts off the stat entries after 15 characters.
440          if (str.find("(" + process.substr(0,15)) != string::npos && (mypid != pid)) {
441     		if (verbose) cout << "*** " << process << " is running (pid " << pid << ")" << endl;
442                 if (verbose) cout << "*** mypid: " << mypid << endl;
443                 if (verbose) cout << str << endl;
444                 // turn this on to see what the situation is like when we are starting.
445                 /* for (int i=0; i != 50; i++) {
446                         sleep (5);
447                 } */
448                 return true;
449 	 }
450       }
451    }
452    return false;
453 }
getModifierString(unsigned int imod)454 string lineak_core_functions::getModifierString(unsigned int imod) {
455 	// Print out the modifiers
456 	string modifier_string[] = { "control", "shift", "alt", "mod2", "mod3", "mod4", "mod5"};
457 	string str = "";
458 
459 	if (imod & ControlMask)
460 	{
461 		if (str[0])
462 		str+="+";
463 		str+=modifier_string[0];
464 	}
465 	if (imod & ShiftMask)
466 	{
467 		if (str[0])
468 		str+="+";
469 		str+=modifier_string[1];
470 	}
471 	if (imod & Mod1Mask)
472 	{
473 		if (str[0])
474 		str+="+";
475 		str+=modifier_string[2];
476 	}
477 	if (imod & Mod2Mask)
478 	{
479 		if (str[0])
480 		str+="+";
481 		str+=modifier_string[3];
482 	}
483 	if (imod & Mod3Mask)
484 	{
485 		if (str[0])
486 		str+="+";
487 		str+=modifier_string[4];
488 	}
489 	if (imod & Mod4Mask)
490 	{
491 		if (str[0])
492 		str+="+";
493 		str+=modifier_string[5];
494 	}
495 	if (imod & Mod5Mask)
496 	{
497 		if (str[0])
498 		str+="+";
499 		str+=modifier_string[6];
500 	}
501 	return str;
502 }
getEventTypeString(EventType_t event_type)503 string lineak_core_functions::getEventTypeString(EventType_t event_type) {
504         switch (event_type) {
505 		case PRESS:
506 			return "PRESS";
507 			break;
508 		case RELEASE:
509 			return "RELEASE";
510 			break;
511 		default:
512 			return "UNKNOWN";
513 	}
514 	return "UNKNOWN";
515 }
getEventType(string event_type)516 EventType_t lineak_core_functions::getEventType(string event_type) {
517 	if (event_type == "PRESS")
518 		return PRESS;
519 	if (event_type == "RELEASE")
520 		return RELEASE;
521 	//Return an unknown
522 	return UNKNOWN_EVENT;
523 }
getTypeString(KeyType_t type)524 string lineak_core_functions::getTypeString(KeyType_t type) {
525 	switch (type) {
526 		case SYM:
527 			return "SYM";
528 			break;
529 		case CODE:
530 			return "CODE";
531 			break;
532 		case BUTTON:
533 			return "BUTTON";
534 			break;
535 		default:
536 			return "UNKNOWN";
537 	}
538 	return "UNKNOWN";
539 }
getType(string type)540 KeyType_t lineak_core_functions::getType(string type) {
541         if (type == "SYM")
542 		return SYM;
543         if (type == "CODE")
544 		return CODE;
545         if (type == "BUTTON")
546 		return BUTTON;
547 	return UNKNOWN_KEY;
548 }
getModifierNumericValue(const string modstr)549 unsigned int lineak_core_functions::getModifierNumericValue(const string modstr) {
550 	string modifiers = modstr;
551 	unsigned int modifier = 0;
552         string::size_type j = 0;
553 	vector<string> mods;
554 
555         // If there is no modifier, return 0
556 	if ( modstr == "" || modstr == "default" )
557 	   return 0;
558 
559 	// Add a '+' to the end so we have something to parse on in the
560 	// following while loop in the event that there is only one modifier.
561 	if (modifiers[modifiers.size()-1] != '+') {
562 	   modifiers += '+';
563 	}
564 	// Break the string up and put the individual modifiers into a vector
565 	// of strings.
566 	while (modifiers.find('+') != string::npos) {
567 		j = modifiers.find('+');
568 		//cout << "adding modifier: " << modifiers.substr(0,j) << " to key:" << keyname << endl;
569 		mods.push_back(modifiers.substr(0,j));
570 		modifiers.erase(0,j+1);
571 	}
572 	for (vector<string>::iterator it = mods.begin(); it != mods.end(); it++) {
573 
574 	      /* build the modifier */
575 	      if (*it == "control" || *it == "Control_L" || *it == "Control_R" )
576 	         modifier |= ControlMask;
577 	      else if (*it == "shift" || *it == "Shift_L" || *it == "Shift_R" )
578 	         modifier |= ShiftMask;
579 	      else if (*it == "mod1" || *it == "alt" || *it == "Alt_L" || *it == "Alt_R" ) {
580 	         modifier |= Mod1Mask;
581 		 //cout << "adding Mod1Mask modifier to " << keyname << endl;
582 	      }
583 	      else if (*it == "mod2")
584 	         modifier |= Mod2Mask;
585 	      else if (*it == "mod3")
586 	         modifier |= Mod3Mask;
587 	      else if (*it == "mod4")
588 	         modifier |= Mod4Mask;
589 	      else if (*it == "mod5")
590 	         modifier |= Mod5Mask;
591 	      //else if (*it == "release")
592 	      //   event_type = RELEASE;
593 	      //else
594 	      //{
595 		//type = SYM;
596 		//keysym = XStringToKeysym (line2);
597 		//if (keysym == 0)
598 		//break;
599 	      //}
600              /* build the modifier */ /*
601               if (strcmp_nocase(*it,"control"))
602                  modifier |= ControlMask;
603               else if (strcmp_nocase(*it,"shift"))
604                  modifier |= ShiftMask;
605               else if (strcmp_nocase(*it,"mod1") || strcmp_nocase(*it, "alt")) {
606                  modifier |= Mod1Mask;
607                  //cout << "adding Mod1Mask modifier to " << keyname << endl;
608               }
609               else if (strcmp_nocase(*it,"mod2"))
610                  modifier |= Mod2Mask;
611               else if (strcmp_nocase(*it,"mod3"))
612                  modifier |= Mod3Mask;
613               else if (strcmp_nocase(*it,"mod4"))
614                  modifier |= Mod4Mask;
615               else if (strcmp_nocase(*it,"mod5"))
616                  modifier |= Mod5Mask;
617 */
618 	}
619 	return modifier;
620 }
621 
enable_IBMRA7993(void)622 void lineak_core_functions::enable_IBMRA7993(void) {
623    lineak_core_functions::send_commands("send_to_keyboard", "ea 71");
624 }
625 
send_commands(string command,string args)626 void lineak_core_functions::send_commands(string command, string args) {
627   string comm;
628   //comm += SBINDIR;
629   //comm += "/";
630   comm += command;
631   comm += " ";
632   comm += args;
633   comm += " &";
634 
635   if (!fork()) {
636 
637         /* child process that tries to run the command, and then exits */
638         /* all specials done, let's go for it... ;) */
639         if (verbose) cout << "... running " << comm << endl;
640         //system("which setkeycodes");
641         system(comm.c_str());
642         exit(true);
643   }
644 
645 }
646 
647