summaryrefslogtreecommitdiff
path: root/src/main.c
blob: 8736a3ccccae527fa27b8380b15356293b01428e (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 #define SECONDS_IN_DAY 86400
   23 
   24 
   25 /**
   26  * Prints the helptext
   27  */
   28 void usage() {
   29   printf(
   30 "Terminus is a program to trigger another program or script if the\n"
   31 "configured passive activity threshold is exceeded. For example, if a user\n"
   32 "does not log in to their system more recently than the configured duration,\n"
   33 "then the configured program or script is executed by terminus on the next\n"
   34 "login.\n"
   35 "\n"
   36 "Terminus is intended for use within the shell's profile/rc file so it is\n"
   37 "called every time a new shell is loaded, which will update the lastlogin\n"
   38 "timestamp as the user uses the system.\n"
   39 "\n"
   40 "Usage:\n"
   41 "  terminus <days> <command to execute>\n"
   42 "\n"
   43 "Options:\n"
   44 "  -h,--help    Print this help text\n"
   45 "  -r,--reset   Reset last login timestamp to current time\n"
   46 );
   47 }
   48 
   49 
   50 /**
   51  * Writes the current timestamp to the specified lastlogin file
   52  * 
   53  * @param path Path to the lastlogin file
   54  * 
   55  * @return int Status of the write operation
   56  */
   57 int write_lastlogin(char* path) {
   58   FILE* f = fopen(path, "w+");
   59 
   60   // Exit if file cannot be opened for writing
   61   if(f == NULL) { return 1; }
   62 
   63   // Write the current time
   64   fprintf(f, "%d", (int) time(NULL));
   65 
   66   fclose(f);
   67   return 0;
   68 }
   69 
   70 
   71 /**
   72  * Reads a long from a file. Only reads the first line of the file and only
   73  * numbers are accounted for (strtol handles the parsing).
   74  *
   75  * @path   Path to the file to read
   76  *
   77  * @return Long read from the specified file
   78  */
   79 long fread_long(char* path) {
   80   FILE* fd;
   81   char buf[32];
   82   fd = fopen(path, "r");
   83   if(!fd)
   84     return -1;
   85   fgets(buf, 32, fd);
   86   fclose(fd);
   87   return strtol(buf, NULL, 10);
   88 }
   89 
   90 
   91 /**
   92  * Executes the specified command, printing the output.
   93  * Note that this does read the calling shell's environmental variables, and so
   94  * absolute paths are not necessary if the command targets an executable in
   95  * PATH.
   96  * 
   97  * @param cmd Command to be executed and output to be printed
   98  * 
   99  * @return int Execution status of the command
  100  *             Note that if the command fails execution at start, -1 is
  101  *             returned.
  102  */
  103 int exec_cmd(char* cmd) {
  104   FILE* p = popen(cmd, "r");
  105   if(p == NULL) { return -1; }
  106 
  107   // Buffer for each line
  108   char line[256];
  109   // For each line, print the output
  110   while (fgets(line, sizeof(line)-1, p) != NULL) {
  111     printf("%s", line);
  112   }
  113 
  114   return pclose(p);
  115 }
  116 
  117 
  118 /**
  119  * Ye olde main function
  120  */
  121 int main(int argc, char* argv[]) {
  122   int days;
  123   char* cmd;
  124   // Expected path to lastlogin file
  125   char lastlogin_path[256];
  126   // Time-related variables
  127   time_t now;
  128   time_t threshold;
  129   time_t lastlogin;
  130 
  131   if(argc == 1) {
  132     usage();
  133     return 1;
  134   }
  135 
  136   // Compose path to lastlogin file
  137   if(!getenv("HOME")) {
  138     printf("ERROR: HOME is unset\n");
  139     return 1;
  140   }
  141 
  142   sprintf(lastlogin_path, "%s/.lastlogin", getenv("HOME"));
  143 
  144   // Parse program arguments
  145   for(int i=1; i<argc; i++) {
  146     if(strcmp("-r", argv[i]) == 0 || strcmp("--reset", argv[i]) == 0) {
  147       // Reset flag set. Override default behavior and write current timestamp
  148       // to lastlogin file
  149       if(write_lastlogin(lastlogin_path) == 1) {
  150         printf("Lastlogin reset failure.\n"
  151                "Unable to write to file %s.\n", lastlogin_path);
  152         return 1;
  153       }
  154       // Exit early since we're force resetting lastlogin.
  155       return 0;
  156     } else if(strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
  157       // The user requested the help text
  158       usage();
  159       return 0;
  160     }
  161   }
  162 
  163   // Get program arguments
  164   days = atoi(argv[1]);
  165   cmd = argv[2];
  166 
  167   // Set time-related values
  168   now = time(NULL);
  169   threshold = days * SECONDS_IN_DAY;
  170   lastlogin = fread_long(lastlogin_path);
  171 
  172   // Set lastlogin to "now" if no lastlogin file is found
  173   if(lastlogin == -1) {
  174     fprintf(stderr, "Lastlogin file could not be read. Initializing.\n");
  175     lastlogin = time(NULL);
  176   }
  177 
  178   // Determine if I'm dead
  179   if((now - lastlogin) > threshold) {
  180     printf("You're dead!\n");
  181     return exec_cmd(cmd);
  182   } else if((lastlogin - now) > SECONDS_IN_DAY) {
  183     // Abort if last login is in the future.
  184     fprintf(stderr,
  185       "ERROR: Last login appears to be in the future. This likely means\n"
  186       "something is wrong with the system clock.\n"
  187     );
  188     return 1;
  189   }
  190 
  191   // All other checks passed, update the lastlogin file
  192   if(write_lastlogin(lastlogin_path) == 1) {
  193     fprintf(stderr, "Failure writing lastlogin\n");
  194     return 1;
  195   }
  196   printf("You're not dead!\n");
  197   return 0;
  198 }

Generated by cgit