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