summaryrefslogtreecommitdiff
path: root/src/main.c
blob: 42015921a4050beec0fbb8ed4abdd16ce9ca468b (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 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 }

Generated by cgit