Don't touch my screen!
[dtms] / src / main.c
1 /*
2  * DTMS Copyright (C) 2016 Eleni Maria Stea <elene.mst@gmail.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  * */
17
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <pwd.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/select.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <time.h>
28 #include <unistd.h>
29
30 static int parse_args(int argc, char **argv);
31 static void print_help();
32 static int check_device(char *device_path);
33
34 static int uid = -1;
35 static char *player_path;
36 static char *dev_path;
37
38 #ifdef PREFIX
39 static const char* player_arg = PREFIX "/share/dtms/dtms.mp3";
40 #else
41 static const char* player_arg = "data/dtms.mp3";
42 #endif
43
44 int main(int argc, char **argv)
45 {
46     time_t last_touch_time = 0;
47     int fd;
48
49     if(parse_args(argc, argv) == -1)
50         return 1;
51
52     if(getuid() == 0 && uid == -1) {
53         fprintf(stderr, "It's a bad idea to run this program as root, either make it setuid-root, or use the -u option to specify an unprivileged user.\n");
54         return 1;
55     }
56
57     if(check_device(dev_path) == -1)
58         return 1;
59
60     if((fd = open(dev_path, O_RDONLY | O_NONBLOCK)) == -1) {
61         fprintf(stderr, "Failed to open device: %s, error: %s\n", dev_path, strerror(errno));
62         return 1;
63     }
64
65     if(uid == -1) {
66         uid = getuid();
67     }
68
69     if(seteuid(uid) == -1) {
70         perror("Set uid failed");
71         return 1;
72     }
73
74     if(!uid) {
75         if(player_path[0] != '/') {
76             fprintf(stderr, "If you run this program as root you should pass the absolute path to a valid mp3 player.\n");
77             return 1;
78         }
79     }
80
81     char *cmd = malloc(strlen(player_path) + 1 + strlen(player_arg) + 1);
82     sprintf(cmd, "%s %s", player_path, player_arg);
83
84     while(1) {
85         fd_set read_set;
86         FD_ZERO(&read_set);
87         FD_SET(fd, &read_set);
88
89         int res;
90         while((res = select(fd + 1, &read_set, 0, 0, 0)) == -1 && errno == EINTR);
91         if(res < 0) {
92             perror("Select failed");
93             break;
94         }
95         if(res == 0) //nothing to read
96             continue;
97
98         if(FD_ISSET(fd, &read_set)) {
99             char buf[1024];
100             time_t now = time(0);
101             while(read(fd, buf, sizeof buf) > 0);
102             if (now - last_touch_time > 2) {
103                 system(cmd);
104                 last_touch_time = now;
105             }
106         }
107     }
108
109     free(cmd);
110     close(fd);
111
112     return 0;
113 }
114
115 static int parse_args(int argc, char **argv)
116 {
117     for(int i=1; i<argc; i++) {
118         if((strcmp(argv[i], "-h") == 0)) {
119             print_help();
120             exit(0);
121         }
122
123         else if((strcmp(argv[i], "-p") == 0)) {
124             if(argv[++i]) {
125                 player_path = argv[i];
126             }
127             else {
128                 fprintf(stderr, "Invalid path. Please give the absolute path to an mp3 player.\n");
129                 return -1;
130             }
131         }
132
133         else if((strcmp(argv[i], "-u") == 0)) {
134             if(argv[++i]) {
135                 struct passwd *passwd = getpwnam(argv[i]);
136                 if(!passwd) {
137                     fprintf(stderr, "Failed to get uid for: %s : %s.\n", argv[i], strerror(errno));
138                     return -1;
139                 }
140                 uid = passwd->pw_uid;
141                 if(uid == 0) {
142                     fprintf(stderr, "You should pass an unprivileged username.\n");
143                     return -1;
144                 }
145             }
146             else {
147                 fprintf(stderr, "Missing username.\n");
148                 return -1;
149             }
150         }
151         else if((strcmp(argv[i], "-d") == 0)) {
152             if(argv[++i]) {
153                 dev_path = argv[i];
154             }
155             else {
156                 fprintf(stderr, "Invalid device file.\n");
157                 return -1;
158             }
159         }
160         else {
161             fprintf(stderr, "Unknown argument: %s\n", argv[i]);
162             return -1;
163         }
164     }
165     return 0;
166 }
167
168 static void print_help()
169 {
170     printf("Options:\n");
171     printf("-h, prints this help\n");
172     printf("-d, path to the device\n");
173     printf("-p, path to the mp3 player\n");
174     printf("-u. username of the user that runs the program\n");
175     printf("--------\n");
176     printf("Examples:\n");
177     printf("--------\n");
178     printf("./dtms -d /dev/usb/hiddev0 -u eleni -p /usr/bin/mpv\n");
179 }
180
181 static int check_device(char *device_path)
182 {
183     struct stat sb;
184     if(stat(dev_path, &sb) == -1) {
185         perror("stat");
186         return -1;
187     }
188     if(((sb.st_mode & S_IFMT) != S_IFBLK) && ((sb.st_mode & S_IFMT) != S_IFCHR)) {
189         fprintf(stderr, "%s is not a device file.\n", device_path);
190         return -1;
191     }
192     return 0;
193 }