home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: SysTools / SysTools.zip / pmcron03.zip / crontab.c < prev    next >
C/C++ Source or Header  |  1996-05-09  |  40KB  |  806 lines

  1. /* Copyright (c) 1995 Florian Große-Coosmann, RCS section at the eof         */
  2. /* crontab, the communication tool for the crond daemon.                     */
  3. /* We want to allow the compilation not only under OS/2. Thus we don't use   */
  4. /* the .RC-files to hold strings!                                            */
  5. /* There is another unsolved and probably unsolvable problem: The language   */
  6. /* depending output strings depend on one or more preselected codepages.     */
  7. /* I hope the user has selected the right while setting LANG to his/her      */
  8. /* preferred value.                                                          */
  9. /* I have a font display tool using VioTBL.DCP. Ask for it in case of        */
  10. /* problems. Florian.                                                        */
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <signal.h>
  15. #include <ctype.h>
  16. #include <errno.h>
  17. #include <locale.h>
  18. #include <fcntl.h>
  19. #include <io.h>
  20. #ifdef TCPIP_SUPPORT
  21. #  include <sys/types.h>
  22. #  include <sys/socket.h>
  23. #  include <sys/ioctl.h>
  24. #  include <netinet/in.h>
  25. #  include <arpa/inet.h>
  26. #  include <netdb.h>
  27. #endif
  28. #include "client.h"
  29.  
  30. char *Buf;
  31. size_t BufSize = 0x1000;
  32. int hf = -1;
  33. char *Destination = NULL;
  34. char DefPipename[] = DEF_PIPENAME;
  35.  
  36. #define ENGLISH            0
  37. #define GERMAN             1
  38. #define FRENCH             2
  39.  
  40. typedef struct {
  41.    unsigned    code;                    /* ENGLISH, GERMAN, etc              */
  42.    char       *LANG_descr;              /* values codes from env-var LANG    */
  43.    char       *description;             /* full description                  */
  44. } LANGUAGE;
  45.  
  46. const LANGUAGE Languages[] = {
  47.    {ENGLISH,   LC_C_UK,       "English"},
  48.    {ENGLISH,   LC_C_USA,      "English"},
  49.    {GERMAN,    LC_C_GERMANY,  "Deutsch"},
  50.    {FRENCH,    LC_C_FRANCE,   "Français"}
  51. };
  52. const unsigned LanguagesCount = sizeof(Languages) / sizeof(Languages[0]);
  53. unsigned Language = 0;
  54.  
  55. #define S_NOMEM            0
  56. #define S_COMMERR          1
  57. #define S_CONNCLOSED       2
  58. #define S_LARGEARG         3
  59. #define S_CRONDNOTSTARTED  4
  60. #define S_CRONDBUSY        5
  61. #define S_TRANSERR         6
  62. #define S_WRONGANSWER      7
  63. #define S_READERR          8
  64.  
  65. /*****************************************************************************/
  66. /*  function name : GetString                                                */
  67. /*                                                                           */
  68. /*  arguments     : number of the requested string. Use the S_... values.    */
  69. /*                                                                           */
  70. /*  return value  : the requested string or "???" in case on an error        */
  71. /*                                                                           */
  72. /*  description   : returns the string. The language identifier is used to   */
  73. /*                  return the right string for the locale.                  */
  74. /*****************************************************************************/
  75. const char *GetString(unsigned Id)
  76. {
  77.    switch (Id) {
  78.       case S_NOMEM: switch (Language) {
  79.             case GERMAN:   return("Zuwenig freier Speicher\r\n");
  80.             case FRENCH:   return("Mémoire insuffisance\r\n");
  81.             case ENGLISH:
  82.             default:       return("Not enough free memory\r\n");
  83.          }
  84.       case S_COMMERR: switch (Language) {
  85.             case GERMAN:   return("Der Kommunikationszugriff zu crond liefert "
  86.                                                           "den Fehler %s\r\n");
  87.             case FRENCH:   return("La communication avec cron retourne"
  88.                                   " l'erreur %s\r\n");
  89.             case ENGLISH:
  90.             default:       return("The communication access to cron returns "
  91.                                                            "the error %s\r\n");
  92.          }
  93.       case S_CONNCLOSED: switch (Language) {
  94.             case GERMAN:   return("Crond hat die Verbindung abgebrochen\r\n");
  95.             case FRENCH:   return("Crond a fermé la connexion\r\n");
  96.             case ENGLISH:
  97.             default:       return("Crond has closed the connection\r\n");
  98.          }
  99.       case S_LARGEARG: switch (Language) {
  100.             case GERMAN:   return("Die Summe der Zeichen in den Argumenten ist"
  101.                                                           " größer als %lu\r\n"
  102.                                   "Bitte reduzieren!\r\n");
  103.             case FRENCH:   return("La somme des caractères dans les arguments"
  104.                                   " dépasse %lu\r\n"
  105.                                   "Veuillez diminuer !\r\n");
  106.             case ENGLISH:
  107.             default:       return("The sum of the characters in the arguments "
  108.                                                               "exceeds %lu\r\n"
  109.                                   "please reduce!\r\n");
  110.          }
  111.       case S_CRONDNOTSTARTED: switch (Language) {
  112.             case GERMAN:   return("Crond wurde (auf dem angegebenen Rechner) "
  113.                                                          "nicht gestartet,\r\n"
  114.                                   "oder der Rechner kann im Netz nicht "
  115.                                                        "erreicht werden.\r\n");
  116.             case FRENCH:   return("Crond n'a pas été lancé (sur l'ordinateur"
  117.                                   " spécifié),\r\n"
  118.                                   "ou bien l'ordinateur ne peut être joint"
  119.                                   " sur le réseau.\r\n");
  120.             case ENGLISH:
  121.             default:       return("Crond has not been started (on the given "
  122.                                                                "computer),\r\n"
  123.                                   "or the computer can't be reached in the "
  124.                                                                "network.\r\n");
  125.          }
  126.       case S_CRONDBUSY: switch (Language) {
  127.             case GERMAN:   return("Probieren Sie es erneut, crond ist gerade "
  128.                                                            "beschäftigt.\r\n");
  129.             case FRENCH:   return("Veuillez ré-essayer. Crond est occupé"
  130.                                   " en ce moment.\r\n");
  131.             case ENGLISH:
  132.             default:       return("Please retry. Currently, Crond is "
  133.                                                                   "busy.\r\n");
  134.          }
  135.       case S_TRANSERR: switch (Language) {
  136.             case GERMAN:   return("Es konnten nur %d von %lu Byte zu crond "
  137.                                                      "übertragen werden.\r\n");
  138.             case FRENCH:   return("Seulement %d octets sur %lu on pu être"
  139.                                   " transmis à crond.\r\n");
  140.             case ENGLISH:
  141.             default:       return("Only %d of %lu bytes could be transferred "
  142.                                                               "to crond.\r\n");
  143.          }
  144.       case S_WRONGANSWER: switch (Language) {
  145.             case GERMAN:   return("Crond liefert falsche Informationen, "
  146.                                               "verweigert den Zugriff oder\r\n"
  147.                                   "die Kommunikation wurde gestört.\r\n");
  148.             case FRENCH:   return("Crond renvoie des informations incorrectes,"
  149.                                   " refuse l'accès ou bien la communication"
  150.                                   " est brouillée.\r\n");
  151.             case ENGLISH:
  152.             default:       return("Crond returns the wrong informations, "
  153.                                                  "denies the access or the\r\n"
  154.                                   "communication has been jammed.\r\n");
  155.          }
  156.       case S_READERR: switch (Language) {
  157.             case GERMAN:   return("(Nur %d von %lu Zeichen wurden "
  158.                                                             "empfangen.)\r\n");
  159.             case FRENCH:   return("Seulement %d octets sur %lu ont été"
  160.                                   " reçus.\r\n");
  161.             case ENGLISH:
  162.             default:       return("Only %d of %lu bytes have been "
  163.                                                              "received.)\r\n");
  164.          }
  165.    }
  166.    return("???");                       /* default value                     */
  167. }
  168.  
  169. /*****************************************************************************/
  170. /*  function name : ReadBlockedMsgPipe                                       */
  171. /*                                                                           */
  172. /*  arguments     : pointer to the handle of the pipe, buffer, size of the   */
  173. /*                  buffer, minimum to read before returning                 */
  174. /*                                                                           */
  175. /*  return value  : bytes that have been read or -errno in case of an        */
  176. /*                  error                                                    */
  177. /*                                                                           */
  178. /*  description   : reads bytes out of the pipe. Some possible errors were   */
  179. /*                  checked to correct reading problems.                     */
  180. /*                  Normally, the function returns after reading a minimum   */
  181. /*                  of bytes.                                                */
  182. /*                                                                           */
  183. /*  note          : the file may have been closed externally while reading   */
  184. /*                  in this function. The pipes work in the correct manner   */
  185. /*                  in this case but sockets return an errno of EINTR.       */
  186. /*                  This is an acceptable error in most cases of retry       */
  187. /*                  but we have to abort! (There is no way for both          */
  188. /*                  handles to figure out the close operation, really.)      */
  189. /*                  Therefore, we use the pointer to the handle to see       */
  190. /*                  wether the pipe has been closed.                         */
  191. /*****************************************************************************/
  192. int ReadBlockedMsgPipe(volatile int *hf,char *buffer,size_t maxbuffer,
  193.                                                               size_t minbuffer)
  194. {
  195.    int i;
  196.    int done = 0;
  197.    errno = 0;
  198.    i = read(*hf,buffer,maxbuffer);
  199.    if (i > 0) {                         /* looks fine..                      */
  200.       done = i;
  201.       if ((size_t) i >= minbuffer)      /* is it enough?                     */
  202.          return(i);                     /* yes, all done                     */
  203.    }
  204.                                         /* i <= 0:                           */
  205.    else if ((errno != EAGAIN) && (errno != EINTR)) /* accept interrupts and  */
  206.                                         /* empty read buffers only           */
  207.       return((errno) ? -errno : -EINVAL); /* unexpected error                */
  208.    while (((errno == EAGAIN) ||         /* these errnos are "good" errnos    */
  209.            (errno == EINTR) ||
  210.            (errno == 0)) &&
  211.           (done < minbuffer)) {
  212.       if (*hf == -1)                    /* has the file been closed from     */
  213.          break;                         /* the cron thread?                  */
  214.       errno = 0;                        /* reset errno and try to read       */
  215.       i = read(*hf,buffer + done,maxbuffer - (size_t) done); /* another chunk*/
  216.       if (i > 0)                        /* read OK?                          */
  217.          done += i;
  218.                                         /* maybe, we run into an error if we */
  219.                                         /* read 0 bytes again and again but  */
  220.                                         /* we'll be killed by the cron thread*/
  221.                                         /* after a timeout, he's a good      */
  222.                                         /* fellow :-)                        */
  223.    }
  224.    if (done > 0)                        /* anything done?                    */
  225.       return(done);                     /* ignore any errors                 */
  226.    return((errno) ? -errno : -EINVAL);  /* errno not set? choose EINVAL      */
  227. }
  228.  
  229. /*****************************************************************************/
  230. /*  function name : Handler                                                  */
  231. /*                                                                           */
  232. /*  arguments     : signal number generated by the runtime system            */
  233. /*                                                                           */
  234. /*  description   : This is a signal routine. This routine is called by a    */
  235. /*                  ^C or Break. If the pipe or socket is opened, it is      */
  236. /*                  will be closed. Otherwise or on the next call we die     */
  237. /*                  normally.                                                */
  238. /*                                                                           */
  239. /*  note          : don't call directly                                      */
  240. /*****************************************************************************/
  241. void Handler(int sig)
  242. {
  243.    if (hf != -1) {
  244.       close(hf);
  245.       hf = -1;
  246.    } else
  247.       exit(1);
  248.    signal(sig,SIG_ACK);
  249. }
  250.  
  251. /*****************************************************************************/
  252. /*  function name : NoMem                                                    */
  253. /*                                                                           */
  254. /*  description   : Prints an error message (not enough memory) and dies.    */
  255. /*****************************************************************************/
  256. void NoMem(void)
  257. {
  258.    printf(GetString(S_NOMEM));
  259.    exit(-1);
  260. }
  261.  
  262. /*****************************************************************************/
  263. /*  function name : CommErr                                                  */
  264. /*                                                                           */
  265. /*  arguments     : errno code                                               */
  266. /*                                                                           */
  267. /*  description   : displays the translated errno code as a result of a      */
  268. /*                  communication error with the daemon and dies. If the     */
  269. /*                  pipe or socket is already closed we assume an            */
  270. /*                  interrupt by the user. Thus, we don't display the        */
  271. /*                  string (but die).                                        */
  272. /*****************************************************************************/
  273. void CommErr(int errnocode)
  274. {
  275.    char strerrorbuf[256],*ptr;
  276.    if (hf == -1)
  277.       exit(3);
  278.    strcpy(strerrorbuf,strerror(errnocode));
  279.    if ((ptr = strpbrk(strerrorbuf,"\r\n")) != NULL)
  280.       *ptr = 0;
  281.    printf(GetString(S_COMMERR),strerrorbuf);
  282.    exit(3);
  283. }
  284.  
  285. /*****************************************************************************/
  286. /*  function name : ConnectionClosed                                         */
  287. /*                                                                           */
  288. /*  description   : displays the the string "crond has closed the            */
  289. /*                  connection" and dies. If the pipe or socket is           */
  290. /*                  already closed we assume an interrupt by the user.       */
  291. /*                  Thus, we don't display the string (but die).             */
  292. /*****************************************************************************/
  293. void ConnectionClosed(void)
  294. {
  295.    if (hf == -1)
  296.       exit(3);
  297.    printf(GetString(S_CONNCLOSED));
  298.    exit(3);
  299. }
  300.  
  301. /*****************************************************************************/
  302. /*  function name : PipeHandler                                              */
  303. /*                                                                           */
  304. /*  arguments     : signal number generated by the runtime system            */
  305. /*                                                                           */
  306. /*  description   : This is a signal routine. This routine is called by a    */
  307. /*                  SIGPIPE. We call ConnectionClosed, see above.            */
  308. /*                                                                           */
  309. /*  note          : don't call directly                                      */
  310. /*****************************************************************************/
  311. void PipeHandler(int sig)
  312. {
  313.    signal(sig,SIG_ACK);
  314.    ConnectionClosed();
  315. }
  316.  
  317. /*****************************************************************************/
  318. /*  function name : GermanHelp                                               */
  319. /*                                                                           */
  320. /*  description   : displays help in German and dies.                        */
  321. /*                                                                           */
  322. /*  note          : see Help                                                 */
  323. /*****************************************************************************/
  324. void GermanHelp(void)
  325. {
  326.    printf("Gebrauch:\r\n"
  327.           "\tcrontab [\"@\"Rechner] [\"Once\"] Zeiten Kommando\r\n"
  328.           "oder\tcrontab [\"@\"Rechner] \"Delete\" Nummer\r\n"
  329.           "oder\tcrontab [\"@\"Rechner] \"Show\"\r\n"
  330.           "Mit @Rechner kann man einen anderen cron-Dämon-Rechner angeben.\r\n"
  331. #ifdef TCPIP_SUPPORT
  332.           "Dabei wird zuerst der LAN-Weg und dann der TCP/IP-Weg probiert.\r\n"
  333. #endif
  334.           "Bei \"Show\" werden einen die aktuellen cron-jobs mit Nummern "
  335.                                                                "angezeigt.\r\n"
  336.           "Mit \"Delete\" Nummer läßt sich ein bestimmter Job löschen.\r\n"
  337.           "Die Nummer kann mit \"Show\" ermittelt werden.\r\n"
  338.           "\r\n"
  339.           "Mit der ersten Darstellung wird ein Job an den cron-Dämon "
  340.                                                           "weitergeleitet,\r\n"
  341.           "der zu dem gewissen Zeitpunkt oder Zeitpunkten abgearbeitet wird."
  342.                                                                          "\r\n"
  343.           "Als Kommando darf dabei jedes beliebige Kommando dienen.\r\n"
  344.           "Die Zeiten setzen sich aus 5 Feldern zusammen:\r\n"
  345.           "  a) Minute (Werte zwischen 0 und 59)    b) Stunde  (Werte zwischen"
  346.                                                                " 0 und 23)\r\n"
  347.           "  c) Tag    (Werte zwischen 1 und 31)    d) Monat   (Werte zwischen"
  348.                                                                " 1 und 12)\r\n"
  349.           "  e) Wochentag  (Werte zwischen 0 und 6, 0 = Sonntag)\r\n"
  350.           "Jede der einzelnen Angaben darf eine durch Kommata getrennte "
  351.                                                             "Intervalliste\r\n"
  352.           "sein, jede Angabe darf auch durch einen Stern (*) für \"jedesmal\" "
  353.                                                                   "ersetzt\r\n"
  354.           "werden.\r\n"
  355.           "\r\n"
  356.           "Ist \"Once\" angegeben, wird der Auftrag einmal ausgeführt und "
  357.                                                          "danach gelöscht.\r\n"
  358.           "Die Spezialzeiten \"CronStart\", \"CronStop\" und \"Daily\" "
  359.                                                                  "bedeuten\r\n"
  360.           "\"beim Start des cron-Dämon\", \"beim Ende des cron-Dämon\" und\r\n"
  361.           "\"einmal täglich zum frühestmöglichen Zeitpunkt\".\r\n"
  362.           "\r\n"
  363.           "Beispiel: Mit \"crontab 0,20,40 * * * 6 Kommando\" wird jeden "
  364.                                                              "Samstag alle\r\n"
  365.          "\t  20 Minuten Kommando ausgeführt.\r\n"
  366.           "\r\n"
  367.          "Bei Programmen mit Benutzerinteraktion sollte \"start\" benutzt "
  368.                                                                   "werden!\r\n"
  369.          "\r\n"
  370.          "Die Sprache dieses Programms kann über die Umgebungsvariable "
  371.                                                       "\"LANG\" ausgewählt\r\n"
  372.          "werden:\r\n"
  373.          "      Wert | Sprache\r\n"
  374.          );
  375. }
  376.  
  377. /*****************************************************************************/
  378. /*  function name : EnglishHelp                                              */
  379. /*                                                                           */
  380. /*  description   : displays help in English and dies.                       */
  381. /*                                                                           */
  382. /*  note          : see Help                                                 */
  383. /*****************************************************************************/
  384. void EnglishHelp(void)
  385. {
  386.    printf("usage:\r\n"
  387.           "\tcrontab [\"@\"Computer] [\"Once\"] Times Command\r\n"
  388.           "or\tcrontab [\"@\"Computer] \"Delete\" Number\r\n"
  389.           "or\tcrontab [\"@\"Computer] \"Show\"\r\n"
  390.           "With @Computer another machine could be selected.\r\n"
  391. #ifdef TCPIP_SUPPORT
  392.           "First, the LAN then the TCP/IP connection will be tried.\r\n"
  393. #endif
  394.           "With \"Show\" the current cron-jobs will be displayed.\r\n"
  395.           "With \"Delete\" Number a given job will be deleted.\r\n"
  396.           "The number can be determined with a \"Show\" command.\r\n"
  397.           "\r\n"
  398.           "With the first usage a job will be send to the cron daemon. It will"
  399.                                                              " be executed\r\n"
  400.           "at a given time or given times.\r\n"
  401.           "Every possible command may be used as Command.\r\n"
  402.           "The Times field consists of 5 entries:\r\n"
  403.           "  a) minute (values between 0 and 59)    b) hour  (values between 0"
  404.                                                                  " and 23)\r\n"
  405.           "  c) day    (values between 1 and 31)    d) month (values between 1"
  406.                                                                  " and 12)\r\n"
  407.           "  e) day of the week (values between 0 and 6, 0 = sunday)\r\n"
  408.           "Every entry may be a comma delimited interval list. Every entry "
  409.                                                                  "may be a\r\n"
  410.           "star (*), its meaning is \"every possible value\".\r\n"
  411.           "\r\n"
  412.           "If \"Once\" is supplied, the job will be executed only once, then "
  413.                                                                "it will be\r\n"
  414.           "deleted.\r\n"
  415.           "The meaning of the special times \"CronStart\", \"CronStop\" and "
  416.                                                             "\"Daily\" are\r\n"
  417.           "\"at start of the cron daemon\", \"at stop of the cron daemon\" "
  418.                                                                       "and\r\n"
  419.           "\"daily once at the earliest possible time\".\r\n"
  420.           "\r\n"
  421.           "example: With \"crontab 0,20,40 * * * 6 command\" on every "
  422.                                                     "saturday command will\r\n"
  423.           "\t be executed every 20 minutes.\r\n"
  424.           "\r\n"
  425.           "\"start\" should be used to executed programs with user "
  426.                                                              "interaction.\r\n"
  427.           "\r\n"
  428.           "The language of this program may be selected by the environment "
  429.                                                                  "variable\r\n"
  430.           "\"LANG\":\r\n"
  431.           "     value | language\r\n"
  432.           );
  433. }
  434.  
  435. /*****************************************************************************/
  436. /*  function name : FrenchHelp                                               */
  437. /*                                                                           */
  438. /*  description   : displays help in French and dies.                        */
  439. /*                                                                           */
  440. /*  note          : see Help                                                 */
  441. /*****************************************************************************/
  442. void FrenchHelp(void)
  443. {
  444.    printf("Utilisation :\r\n"
  445.           "\tcrontab [«@»Ordinateur] [«Once»] Heures Commande\r\n"
  446.           "ou\tcrontab [«@»Ordinateur] «Delete» Numéro\r\n"
  447.           "ou\tcrontab [«@»Ordinateur] «Show»\r\n"
  448.           "@Ordinateur permet de s'adresser à une machine distante.\r\n"
  449. #ifdef TCPIP_SUPPORT
  450.           "Les connexions réseau (LAN) puis TCP/IP seront recherchées.\r\n"
  451. #endif
  452.           "«Show» affiche les événements cron définis.\r\n"
  453.           "«Delete» supprime un événement.\r\n"
  454.           "Le numéro peut être déterminé par une commande «Show».\r\n"
  455.           "N'importe quelle commande peut être utilisée comme Commande.\r\n"
  456.           "Le paramètre Heures se compose de 5 entrées :\r\n"
  457.           "  a) minute (de 0 à 59)    b) heure  (de 0 à 23)\r\n"
  458.           "  c) jour   (de 1 à 31)    d) mois   (de 1 à 12)\r\n"
  459.           "  e) jour de la semaine (de 0 à 6, 0 = dimanche)\r\n"
  460.           "Chaque entrée peut être une liste d'éléments séparés par des "
  461.                                                                 "virgules.\r\n"
  462.           "Une valeur * signifie «toutes les valeurs».\r\n"
  463.           "\r\n"
  464.           "Si «Once» est précisé, l'événement ne sera exécuté qu'une fois, "
  465.                                                            "puis supprimé.\r\n"
  466.           "Les heures spéciales «CronStart», «CronStop» et «Daily» "
  467.                                                                "signifient\r\n"
  468.           "«au lancement de cron», «à l'arrêt de cron» et «une fois par "
  469.                                                                      "jour\r\n"
  470.           "aussi tôt que possible».\r\n"
  471.           "\r\n"
  472.           "exemple : avec «crontab 0,20,40 * * * 6 commande», commande "
  473.           "sera\r\n"
  474.           "exécutée toutes les 20 minutes chaque samedi.\r\n"
  475.           "\r\n"
  476.           "«start» doit être utilisé pour les programmes nécessitant une\r\n"
  477.           "intervention de l'utilisateur.\r\n"
  478.           "\r\n"
  479.           "La langue que pratique ce programme peut être choisie par la "
  480.           "variable «LANG» :\r\n"
  481.           "    valeur | langue\r\n"
  482.           );
  483. }
  484.  
  485. /*****************************************************************************/
  486. /*  function name : Help                                                     */
  487. /*                                                                           */
  488. /*  description   : displays help and dies.                                  */
  489. /*                  The language specific help is displayed.                 */
  490. /*****************************************************************************/
  491. void Help(void)
  492. {
  493.    unsigned i;
  494.    switch (Language) {
  495.       case GERMAN:
  496.          GermanHelp();
  497.          break;
  498.  
  499.       case FRENCH:
  500.          FrenchHelp();
  501.          break;
  502.  
  503.       case ENGLISH:
  504.       default:
  505.          EnglishHelp();
  506.          break;
  507.    }
  508.    printf("      -----+--------\r\n");
  509.    for (i = 0;i < LanguagesCount;i++)
  510.       printf("%10s | %s\r\n",Languages[i].LANG_descr,Languages[i].description);
  511.    exit(3);
  512. }
  513.  
  514. /*****************************************************************************/
  515. /*  function name : BuildRequest                                             */
  516. /*                                                                           */
  517. /*  arguments     : arg-arguments from main                                  */
  518. /*                                                                           */
  519. /*  return value  : 1 on success, 0 in case of invalid arguments             */
  520. /*                                                                           */
  521. /*  description   : checks the arguments and builds an request that would    */
  522. /*                  probably accepted by the daemon. The final checking      */
  523. /*                  for the validity of the arguments is done by the         */
  524. /*                  daemon.                                                  */
  525. /*****************************************************************************/
  526. int BuildRequest(int argc,char **argv)
  527. {
  528.    int i;
  529.    char *s;
  530.    if ((argc > 1) && (argv[1][0] == '@')) {  /* destination computer given?  */
  531.       s = argv[1] + 1;                  /* jump over @                       */
  532.       if (*s == 0)                      /* nothing more?                     */
  533.          return(0);
  534.       if ((Destination = malloc(strlen(DefPipename) + strlen(s) + 3)) == NULL)
  535.          NoMem();
  536.       strcpy(Destination,"\\\\");       /* build a new pipe name             */
  537.       strcat(Destination,s);
  538.       strcat(Destination,DefPipename);
  539.       argc--;
  540.       argv++;
  541.    }
  542.    if (argc < 2)                        /* empty command?                    */
  543.       return(0);
  544.    if (stricmp(argv[1],"Delete") == 0) {  /* delete a job?                   */
  545.       if (argc != 3)                    /* only expect one more argument     */
  546.          return(0);
  547.       strcpy(Buf,"D ");                 /* code of "Delete"                  */
  548.       strcat(Buf,argv[2]);
  549.       return(1);
  550.    } else if (stricmp(argv[1],"Show") == 0) {   /* display jobs?             */
  551.       if (argc != 2)                    /* don't expect any argument         */
  552.          return(0);
  553.       strcpy(Buf,"R");                  /* code of "Read list"               */
  554.       return(1);
  555.    }
  556.    if (argc < 2)                        /* new job, expect at least on more  */
  557.       return(0);                        /* argument (they may be quoted      */
  558.                                         /* together)                         */
  559.    strcpy(Buf,"N");                     /* code of "New job"                 */
  560.    for (i = 1;i < argc;i++) {
  561.       strcat(Buf," ");                  /* add them                          */
  562.       if (strlen(argv[i]) + strlen(Buf) + 1 > BufSize) { /* string too long? */
  563.          printf(GetString(S_LARGEARG),(unsigned long) BufSize);
  564.          exit(3);
  565.       }
  566.       strcat(Buf,argv[i]);
  567.    }
  568.    return(1);
  569. }
  570.  
  571. /*****************************************************************************/
  572. /*  function name : IsRequestPreAnswer                                       */
  573. /*                                                                           */
  574. /*  arguments     : string to check, buffer to hold the new answer size on   */
  575. /*                  success.                                                 */
  576. /*                                                                           */
  577. /*  return value  : 1 on success, 0 otherwise                                */
  578. /*                                                                           */
  579. /*  description   : tries to interpret the string as a length description    */
  580. /*                  from the daemon. Its form has to be                      */
  581. /*                  "crond: xxx Data OK<WhiteSpace>" with xxx as a           */
  582. /*                  positive number. In this case the supplied buffer is     */
  583. /*                  filled ond 1 returned.                                   */
  584. /*****************************************************************************/
  585. int IsRequestPreAnswer(char *s,size_t *bytes)
  586. {
  587.    char buf[101];
  588.    static char DataOK[] = "Data OK";
  589.    char *ptr,*ptr2;
  590.    unsigned long val;
  591.    if (strlen(s) > sizeof(buf) - 1)     /* too many chars?                   */
  592.       return(0);
  593.    strcpy(buf,s);                       /* work on a local buffer.           */
  594.    if (strnicmp(buf,"crond:",6) != 0)   /* check the beginning               */
  595.       return(0);
  596.    ptr = buf + 6;
  597.    errno = 0;
  598.    val = strtoul(ptr,&ptr2,10);         /* try to interpret a number         */
  599.    if ((errno) || (val == 0) || (ptr == ptr2))  /* not a number?             */
  600.       return(0);
  601.    while (isspace(*ptr2))               /* jump over whitespaces             */
  602.       ptr2++;
  603.  
  604.    if (strnicmp(ptr2,DataOK,strlen(DataOK)) != 0)  /* check the ending       */
  605.       return(0);
  606.    ptr2 += strlen(DataOK);
  607.    while (isspace(*ptr2))               /* ,jump over whitespaces            */
  608.       ptr2++;
  609.    if (*ptr2 != '\0')                   /* more characters in the string?    */
  610.       return(0);
  611.    *bytes = (size_t) val;               /* OK, the string is accepted as a   */
  612.    return(1);                           /* size string                       */
  613. }
  614.  
  615. #ifdef TCPIP_SUPPORT
  616. /*****************************************************************************/
  617. /*  function name : open_socket                                              */
  618. /*                                                                           */
  619. /*  arguments     : string describing the destination in pipe notation       */
  620. /*                                                                           */
  621. /*  return value  : handle to a newly opened socket or -1 in case of an      */
  622. /*                  error                                                    */
  623. /*                                                                           */
  624. /*  description   : tries to connect to the crond port number of the given   */
  625. /*                  computer. The argument is a string of the form           */
  626. /*                  `\\computername\pipe\pipename'. We extract the           */
  627. /*                  computername, connect to it and return a handle of the   */
  628. /*                  socket.                                                  */
  629. /*****************************************************************************/
  630. int open_socket(const char *s)
  631. {
  632.    int sock,off = 0,on = 1;
  633.    char *name;
  634.    struct hostent *host;
  635.    unsigned long ip_no;
  636.    struct sockaddr_in server;
  637.  
  638. #ifdef __EMX__
  639.    if ((_emx_env & 0x200) == 0)         /* running under DOS?                */
  640.       return(-1);                       /* socket are not supported          */
  641.    else {                               /* running under OS/2?               */
  642.       if (_emx_vcmp < 0x302e3961)       /* < 0.9a                            */
  643.          return(-1);
  644.    }
  645. #endif
  646.    if ((s[0] != '\\') || (s[1] != '\\'))  /* no computername given           */
  647.       return(-1);
  648.    if ((name = strdup(s + 2)) == NULL)  /* jump over the leading backslashes */
  649.       NoMem();
  650.    if (strchr(name,'\\') != NULL)       /* extract the computername          */
  651.       *strchr(name,'\\') = '\0';
  652.  
  653.    memset(&server,0,sizeof(server));
  654.    server.sin_family = AF_INET;
  655.    server.sin_port = htons(CRON_TCPIP_PORT);
  656.    ip_no = inet_addr(name);
  657.    if (ip_no != INADDR_NONE)
  658.       server.sin_addr.s_addr = ip_no;
  659.    else {
  660.       if ((host = gethostbyname(name)) == NULL) {
  661.           free(name);
  662.           return(-1);
  663.       }
  664.       memcpy(&server.sin_addr,host->h_addr,host->h_length);
  665.    }
  666.    free(name);
  667.  
  668.    if ((sock = socket(AF_INET,SOCK_STREAM,PF_UNSPEC)) < 0)
  669.       return(-1);
  670.  
  671.    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
  672.  
  673.    if (connect(sock,(struct sockaddr *) &server,sizeof(server)) < 0)
  674.       return(-1);
  675.  
  676.    ioctl(sock,FIONBIO,&off);            /* don't use "non blocking IO"       */
  677.    return(sock);
  678. }
  679. #endif
  680.  
  681. int main(int argc,char **argv)
  682. {
  683.    size_t w,wanted;
  684.    int done = 0;
  685.    unsigned i;
  686.    char *ptr;
  687.    setmode(1,O_BINARY);
  688.    setmode(2,O_BINARY);
  689.    signal(SIGINT,Handler);
  690.    signal(SIGBREAK,Handler);
  691.    signal(SIGPIPE,PipeHandler);
  692.  
  693.    Language = ENGLISH;                  /* Default in case of an error       */
  694.    ptr = getenv("LANG");
  695.    if (ptr != NULL) {
  696.       for (i = 0;i < LanguagesCount;i++)
  697.          if (strcmp(Languages[i].LANG_descr,ptr) == 0)
  698.             Language = Languages[i].code;
  699.    }
  700.  
  701.    if ((Buf = malloc(BufSize)) == NULL)
  702.       NoMem();
  703.    if (!BuildRequest(argc,argv))        /* invalid arguments?                */
  704.       Help();
  705.  
  706.    if (Destination == NULL)
  707.       Destination = DefPipename;
  708.    if ((hf = open(Destination,O_BINARY | O_RDWR)) == -1)
  709.       {
  710.          switch (errno) {
  711.             case ENOENT:                /* daemon not started                */
  712. #ifdef TCPIP_SUPPORT
  713.                if ((hf = open_socket(Destination)) != -1)   /* can open via  */
  714.                   break;                /* TCP/IP?                           */
  715. #endif
  716.                printf(GetString(S_CRONDNOTSTARTED));
  717.                return(2);
  718.             case EPIPE:
  719.             case EAGAIN:
  720.             case EBUSY:                 /* daemon doesn't accept a communic. */
  721.                printf(GetString(S_CRONDBUSY));
  722.                return(2);
  723.             default:                    /* unknown error                     */
  724.                hf = 0;                  /* don't die without a message!      */
  725.                CommErr(errno);
  726.          }
  727.       }
  728.  
  729.    w = strlen(Buf);
  730.    if ((done = write(hf,Buf,w)) == -1) {  /* can't transmit the request?     */
  731.       if ((errno == EPIPE) || (errno == EINVAL))
  732.          ConnectionClosed();
  733.         else
  734.          CommErr(errno);
  735.    } else if (done != w) {
  736.       printf(GetString(S_TRANSERR),done,(unsigned long) w);
  737.       return(3);
  738.    }
  739.  
  740.    done = 0;                            /* await the answer                  */
  741.    if ((done = ReadBlockedMsgPipe(&hf,Buf,BufSize - 1,1)) < 0) {
  742.       if ((done == -EPIPE) || (done == -EINVAL))
  743.          ConnectionClosed();
  744.         else
  745.          CommErr(-done);
  746.       return(3);
  747.    } else if (done < 6) {               /* crond sends "crond:" everytime    */
  748.       printf(GetString(S_WRONGANSWER));
  749.       return(3);
  750.    }
  751.    Buf[done] = 0;
  752.    if (!IsRequestPreAnswer(Buf,&wanted)) {   /* simple answer?               */
  753.       printf("%.*s\r\n",done,Buf);
  754.       return(0);
  755.    }
  756.                                         /* wanted contens the number of bytes*/
  757.                                         /* the daemon wants to send. ensure  */
  758.                                         /* the buffer is large enough...     */
  759.  
  760.    if (wanted + 3 >= BufSize) {         /* some byte more than needed...     */
  761.       BufSize = wanted + 4;
  762.       if ((Buf = realloc(Buf,BufSize)) == NULL)
  763.          NoMem();
  764.    }
  765.                                         /* read the extended answer          */
  766.    if ((done = ReadBlockedMsgPipe(&hf,Buf,BufSize - 1,wanted)) < 0) {
  767.       if ((done == -EPIPE) || (done == -EINVAL))
  768.          ConnectionClosed();
  769.         else
  770.          CommErr(-done);
  771.       return(3);
  772.    } else if (done < wanted)
  773.       printf(GetString(S_READERR),done,(unsigned long) wanted);
  774.    close(hf);                           /* close the pipe before any try to  */
  775.                                         /* display the data. Maybe, the user */
  776.                                         /* uses "more" or other slow closing */
  777.                                         /* programs. Allow crond to go to    */
  778.                                         /* sleep.                            */
  779.    hf = -1;
  780.    printf("%.*s\r\n",done,Buf);
  781.    return(0);
  782. }
  783.  
  784. /* RCS depending informations
  785.  *
  786.  * $Name: Version121 $
  787.  *
  788.  * $Log: crontab.c $
  789.  * Revision 1.4  1995/10/18 09:46:08  Florian
  790.  * Some cosmetic changes.
  791.  * Added support of the French language. Thanks to Raphaël Vanney.
  792.  *
  793.  * Revision 1.3  1995/08/04 09:02:19  Florian
  794.  * NLS supported (English and German)
  795.  *
  796.  * Revision 1.2  1995/03/15 09:07:34  Florian
  797.  * Some minor bugs fixed.
  798.  * TCP/IP support added.
  799.  *
  800.  * Revision 1.1  1995/02/03 10:42:35  Florian
  801.  * Initial revision
  802.  *
  803.  *
  804.  */
  805. static char rcsid[] = "@(#)$Id: crontab.c 1.4 1995/10/18 09:46:08 Florian Rel $";
  806.