1 /**
2 * Copyright (C) 2015 Aaron Ball <nullspoon@iohq.net>
3 *
4 * Terminus 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 * Terminus 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 terminus. If not, see <http://www.gnu.org/licenses/>.
16 */
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <time.h>
21
22
23 /**
24 * Prints the helptext
25 */
26 void get_help() {
27 printf("Please specify a timeout threshold (in days) as well as a command\n"
28 "or commands to be run if the threshold is exceeded.\n\n"
29 "[1mExample:[0m\n"
30 " terminus 5 '<command to execute>'\n\n"
31 "This example will execute the given command if the user has not\n"
32 "logged in in the last 5 days."
33 "\n\n"
34 "If terminus detects that the login threshold has been exceeded, it will\n"
35 "not reset the last login. If however the threshold was exceeded but the\n"
36 "user is fine, the lastlogin file can be reset using the [1m-r[0m or the\n"
37 "[1m--reset[0m flags\n\n"
38 );
39 }
40
41
42 /**
43 * Converts the given day count to seconds (multiplies by the number of seconds
44 * in a day, 86400).
45 *
46 * @param days Day count to convert to seconds
47 *
48 * @return long Number of total seconds in the given day duration
49 */
50 long days_to_seconds(int days) {
51 return days * 86400;
52 }
53
54
55 /**
56 * TODO
57 */
58 int get_user_home(char* out) {
59 if(getenv("HOME") != NULL) {
60 strcpy(out, getenv("HOME"));
61 return 0;
62 } else {
63 return 1;
64 }
65 }
66
67
68 /**
69 * Writes the current timestamp to the specified lastlogin file
70 *
71 * @param path Path to the lastlogin file
72 *
73 * @return int Status of the write operation
74 */
75 int write_lastlogin(char* path) {
76 FILE* f = fopen(path, "w+");
77
78 // Exit if file cannot be opened for writing
79 if(f == NULL) { return 1; }
80
81 // Write the current time
82 fprintf(f, "%d", time(NULL));
83
84 fclose(f);
85 return 0;
86 }
87
88
89 /**
90 * Gets the time_t value contained in the specified lastlogin file.
91 *
92 * @param lastlogin_path Path to the lastlogin file to be read
93 * @param lastlogin Output time_t variable
94 *
95 * @return int Status of reading the lastlogin file
96 */
97 int get_lastlogin(char* lastlogin_path, time_t* lastlogin) {
98 // File pointer to lastlogin file (used for reading and writing)
99 FILE* lastlogin_file;
100 // String contents of the lastlogin file
101 char lastlogin_str[32];
102
103 // Attempt read lastlogin file contents
104 lastlogin_file = fopen(lastlogin_path, "r");
105
106 if(lastlogin_file == NULL) {
107 return 1;
108 } else {
109 // Read the lastlogin file and set its value to lastlogin
110 fgets(lastlogin_str, 32, lastlogin_file);
111 *lastlogin = atoi(lastlogin_str);
112 fclose(lastlogin_file);
113 }
114 return 0;
115 }
116
117
118 /**
119 * Executes the specified command, printing the output.
120 * Note that this does read the calling shell's environmental variables, and so
121 * absolute paths are not necessary if the command targets an executable in
122 * PATH.
123 *
124 * @param cmd Command to be executed and output to be printed
125 *
126 * @return int Execution status of the command
127 * Note that if the command fails execution at start, -1 is
128 * returned.
129 */
130 int exec_cmd(char* cmd) {
131 FILE* p = popen(cmd, "r");
132 if(p == NULL) { return -1; }
133
134 // Buffer for each line
135 char line[256];
136 // For each line, print the output
137 while (fgets(line, sizeof(line)-1, p) != NULL) {
138 printf("%s", line);
139 }
140
141 return pclose(p);
142 }
143
144
145 /**
146 * Ye olde main function
147 */
148 int main(int argc, char* argv[]) {
149 char home[128];
150 int days;
151 char* cmd;
152 // Expected path to lastlogin file
153 char lastlogin_path[256];
154 // Time-related variables
155 time_t now;
156 time_t threshold;
157 time_t lastlogin;
158
159 if(argc == 1) {
160 get_help();
161 return 1;
162 }
163
164
165 // Compose path to lastlogin file
166 get_user_home(home);
167 strcpy(lastlogin_path, home);
168 strcat(lastlogin_path, "/.lastlogin");
169
170 // Parse program arguments
171 for(int i=1; i<argc; i++) {
172 if(strcmp("-r", argv[i]) == 0 || strcmp("--reset", argv[i]) == 0) {
173 // Reset flag set. Override default behavior and write current timestamp
174 // to lastlogin file
175 if(write_lastlogin(lastlogin_path) == 1) {
176 printf("Lastlogin reset failure.\n"
177 "Unable to write to file %s.\n", lastlogin_path);
178 return 1;
179 }
180 // Exit early since we're force resetting lastlogin.
181 return 0;
182 } else if(strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
183 // The user requested the help text
184 get_help();
185 return 0;
186 }
187 }
188
189 // Get program arguments
190 days = atoi(argv[1]);
191 cmd = argv[2];
192
193 // Set time-related values
194 now = time(NULL);
195 threshold = days_to_seconds(days);
196 if(get_lastlogin(lastlogin_path, &lastlogin) == 1) {
197 // Create lastlogin file since it doesn't exist
198 printf("Lastlogin file could not be read. Writing\n");
199 if(write_lastlogin(lastlogin_path) == 1) {
200 printf("Failure writing lastlogin\n");
201 return 1;
202 }
203 // Go ahead and set lastlogin time, since writing it means we're using
204 // "now".
205 lastlogin = time(NULL);
206 }
207
208 // Determine if I'm dead
209 if((now - lastlogin) > threshold) {
210 printf("You're dead!\n");
211 exec_cmd(cmd);
212 } else if((lastlogin - now) > 86400) {
213 // Abort if last login is too far in the future.
214 // This should account for when
215 printf(
216 "Error: Last login appears to be over one day in the future.\n"
217 "This likely means something has gone wrong with the system clock.\n"
218 "Unable to determine current time. Aborting terminus check.\n"
219 );
220 return 2;
221 } else {
222 if(write_lastlogin(lastlogin_path) == 1) {
223 printf("Failure writing lastlogin\n");
224 return 1;
225 }
226 printf("You're not dead!\n");
227 }
228
229 return 0;
230 }
|