summaryrefslogtreecommitdiff
path: root/src/main.c
blob: 8ab9f488840e8d02f75f4b0a4538d4798eec3f81 (plain)
    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 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   "Example:\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 -r or the\n"
   37   "--reset 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 }

Generated by cgit