Subversion Repositories public

Rev

Rev 55 | Rev 63 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
45 luk 1
 
2
/// inotify cron table manipulator main file
3
/**
4
 * \file ict-main.cpp
5
 *
6
 * inotify cron system
7
 *
8
 * Copyright (C) 2006 Lukas Jelinek, <lukas@aiken.cz>
9
 *
10
 * This program is free software; you can use it, redistribute
11
 * it and/or modify it under the terms of the GNU General Public
12
 * License, version 2 (see LICENSE-GPL).
13
 *  
14
 */
15
 
16
 
17
#include <argp.h>
18
#include <pwd.h>
19
#include <string>
20
#include <stdio.h>
21
#include <unistd.h>
22
#include <sys/stat.h>
23
#include <sys/wait.h>
24
 
55 luk 25
#include "incron.h"
45 luk 26
#include "incrontab.h"
27
 
28
 
29
// #define INCRON_DEFAULT_EDITOR "nano" // for vim haters like me ;-)
30
#define INCRON_DEFAULT_EDITOR "vim"
31
 
32
 
55 luk 33
const char* argp_program_version = INCRON_TAB_NAME " " INCRON_VERSION;
34
const char* argp_program_bug_address = INCRON_BUG_ADDRESS;
45 luk 35
 
55 luk 36
static char doc[] = "incrontab - incron table manipulator";
45 luk 37
 
38
static char args_doc[] = "FILE";
39
 
40
static struct argp_option options[] = {
55 luk 41
  {"list",    'l', 0,      0,  "List the current table" },
42
  {"remove",  'r', 0,      0,  "Remove the table completely" },
43
  {"edit",    'e', 0,      0,  "Edit the table" },
44
  {"user",    'u', "USER", 0,  "Override the current user" },
45 luk 45
  { 0 }
46
};
47
 
49 luk 48
/// incrontab operations
49
typedef enum
45 luk 50
{
49 luk 51
  OPER_NONE,    /// nothing
52
  OPER_LIST,    /// list table
53
  OPER_REMOVE,  /// remove table
54
  OPER_EDIT     /// edit table
55
} InCronTab_Operation_t;
45 luk 56
 
49 luk 57
/// incrontab arguments
45 luk 58
struct arguments
59
{
49 luk 60
  char *user;     /// user name
61
  int oper;       /// operation code
62
  char *file;     /// file to import
45 luk 63
};
64
 
49 luk 65
/// Parses the program options (arguments).
66
/**
67
 * \param[in] key argument key (name)
68
 * \param[in] arg argument value
69
 * \param[out] state options setting
70
 * \return 0 on success, ARGP_ERR_UNKNOWN on unknown argument(s)
71
 */
72
static error_t parse_opt(int key, char *arg, struct argp_state *state)
45 luk 73
{
74
  struct arguments* arguments = (struct arguments*) state->input;
75
 
76
  switch (key) {
77
    case 'l':
78
      arguments->oper = OPER_LIST;
79
      break;
80
    case 'r':
81
      arguments->oper = OPER_REMOVE;
82
      break;
83
    case 'e':
84
      arguments->oper = OPER_EDIT;
85
      break;
86
    case 'u':
87
      arguments->user = arg;
88
      break;
89
    case ARGP_KEY_ARG:
90
      if (state->arg_num >= 1)
91
        argp_usage(state);
92
      arguments->file = arg;
93
      break;
94
    case ARGP_KEY_END:
95
      break;
96
    default:
97
      return ARGP_ERR_UNKNOWN;
98
  }
99
 
100
  return 0;
101
}
102
 
49 luk 103
/// Program arguments
45 luk 104
static struct argp argp = { options, parse_opt, args_doc, doc };
105
 
106
 
61 luk 107
 
49 luk 108
/// Copies a file to an user table.
109
/**
110
 * \param[in] path path to file
111
 * \param[in] user user name
112
 * \return true = success, false = failure
113
 */
45 luk 114
bool copy_from_file(const char* path, const char* user)
115
{
116
  InCronTab tab;
117
  std::string s(path);
118
  if (s == "-")
119
    s = "/dev/stdin";
120
  if (!tab.Load(s)) {
121
    fprintf(stderr, "cannot load table from file: %s\n", path);
122
    return false;
123
  }
124
 
125
  std::string out(InCronTab::GetUserTablePath(user));
126
  if (!tab.Save(out)) {
127
    fprintf(stderr, "cannot create table for user: %s\n", user);
128
    return false;
129
  }
130
 
131
  return true;
132
}
133
 
49 luk 134
/// Removes an user table.
135
/**
136
 * \param[in] user user name
137
 * \return true = success, false = failure
138
 */
45 luk 139
bool remove_table(const char* user)
140
{
141
  std::string tp(InCronTab::GetUserTablePath(user));
142
 
143
  if (unlink(tp.c_str()) != 0 && errno != ENOENT) {
144
    fprintf(stderr, "cannot remove table for user: %s\n", user);
145
    return false;
146
  }
147
 
148
  return true;
149
}
150
 
49 luk 151
/// Lists an user table.
152
/**
153
 * \param[in] user user name
154
 * \return true = success, false = failure
155
 *
156
 * \attention Listing is currently done through 'cat'.
157
 */
45 luk 158
bool list_table(const char* user)
159
{
160
  std::string tp(InCronTab::GetUserTablePath(user));
161
 
162
  if (access(tp.c_str(), R_OK) != 0) {
163
    if (errno == ENOENT) {
164
      fprintf(stderr, "no table for %s\n", user);
165
      return true;
166
    }
167
    else {
168
      fprintf(stderr, "cannot read table for %s: %s\n", user, strerror(errno));
169
      return false;
170
    }
171
  }
172
 
173
  std::string cmd("cat ");
174
  cmd.append(tp);
175
  return system(cmd.c_str()) == 0;
176
}
177
 
49 luk 178
/// Allows to edit an user table.
179
/**
180
 * \param[in] user user name
181
 * \return true = success, false = failure
182
 *
183
 * \attention This function is very complex and may contain
184
 *            various bugs including security ones. Please keep
185
 *            it in mind..
186
 */
45 luk 187
bool edit_table(const char* user)
188
{
189
  std::string tp(InCronTab::GetUserTablePath(user));
190
 
191
  struct passwd* ppwd = getpwnam(user);
192
  if (ppwd == NULL) {
193
    fprintf(stderr, "cannot find user %s: %s\n", user, strerror(errno));
194
    return false;
195
  }
61 luk 196
 
45 luk 197
  uid_t uid = ppwd->pw_uid;
61 luk 198
  uid_t gid = ppwd->pw_gid;
45 luk 199
 
200
  char s[NAME_MAX];
201
  strcpy(s, "/tmp/incron.table-XXXXXX");
202
 
203
  uid_t iu = geteuid();
61 luk 204
  uid_t ig = getegid();
55 luk 205
 
61 luk 206
  if (seteuid(uid) != 0 || setegid(gid) != 0) {
207
    fprintf(stderr, "cannot change effective UID/GID for user %s: %s\n", user, strerror(errno));
45 luk 208
    return false;
209
  }
210
 
211
  int fd = mkstemp(s);
212
  if (fd == -1) {
213
    fprintf(stderr, "cannot create temporary file: %s\n", strerror(errno));
214
    return false;
215
  }
216
 
61 luk 217
  bool ok = false;
218
  FILE* out = NULL;
219
  FILE* in = NULL;
220
  time_t mt = (time_t) 0;
221
  const char* e = NULL;
222
 
45 luk 223
  if (fchmod(fd, 0644) != 0) {
224
    fprintf(stderr, "cannot change mode of temporary file: %s\n", strerror(errno));
225
    close(fd);
61 luk 226
    goto end;
45 luk 227
  }
228
 
61 luk 229
  if (seteuid(iu) != 0 || setegid(ig) != 0) {
230
    fprintf(stderr, "cannot change effective UID/GID: %s\n", strerror(errno));
45 luk 231
    close(fd);
61 luk 232
    goto end;
45 luk 233
  }
234
 
61 luk 235
  out = fdopen(fd, "w");
45 luk 236
  if (out == NULL) {
237
    fprintf(stderr, "cannot write to temporary file: %s\n", strerror(errno));
238
    close(fd);
61 luk 239
    goto end;
45 luk 240
  }
241
 
61 luk 242
  in = fopen(tp.c_str(), "r");
45 luk 243
  if (in == NULL) {
244
    if (errno == ENOENT) {
245
      in = fopen("/dev/null", "r");
246
      if (in == NULL) {
247
        fprintf(stderr, "cannot get empty table for %s: %s\n", user, strerror(errno));
248
        fclose(out);
61 luk 249
        goto end;
45 luk 250
      }
251
    }
252
    else {
253
      fprintf(stderr, "cannot read old table for %s: %s\n", user, strerror(errno));
254
      fclose(out);
61 luk 255
      goto end;
45 luk 256
    }
257
  }
258
 
259
  char buf[1024];
260
  while (fgets(buf, 1024, in) != NULL) {
261
    fputs(buf, out);
262
  }
263
  fclose(in);
264
  fclose(out);
265
 
266
  struct stat st;
267
  if (stat(s, &st) != 0) {
268
    fprintf(stderr, "cannot stat temporary file: %s\n", strerror(errno));
61 luk 269
    goto end;
45 luk 270
  }
271
 
61 luk 272
  mt = st.st_mtime;
45 luk 273
 
61 luk 274
  e = getenv("EDITOR");
45 luk 275
  if (e == NULL)
276
    e = INCRON_DEFAULT_EDITOR;
277
 
61 luk 278
  {
279
    pid_t pid = fork();
280
    if (pid == 0) {
281
      if (setuid(uid) != 0 || setgid(gid) != 0) {
282
        fprintf(stderr, "cannot set user %s: %s\n", user, strerror(errno));
283
        goto end;
284
      }    
285
 
286
      execlp(e, e, s, NULL);
287
      _exit(1);
45 luk 288
    }
61 luk 289
    else if (pid > 0) {
290
      int status;
291
      if (wait(&status) != pid) {
292
        perror("error while waiting for editor");
293
        goto end;
294
      }
295
      if (!(WIFEXITED(status)) || WEXITSTATUS(status) != 0) {
296
        perror("editor finished with error");
297
        goto end;
298
      }
45 luk 299
    }
61 luk 300
    else {
301
      perror("cannot start editor");
302
      goto end;
303
    }
45 luk 304
  }
305
 
306
  if (stat(s, &st) != 0) {
307
    fprintf(stderr, "cannot stat temporary file: %s\n", strerror(errno));
61 luk 308
    goto end;
45 luk 309
  }
310
 
311
  if (st.st_mtime == mt) {
312
    fprintf(stderr, "table unchanged\n");
61 luk 313
    ok = true;
314
    goto end;
45 luk 315
  }
316
 
61 luk 317
  {
318
    InCronTab ict;
319
    if (!ict.Load(s) || !ict.Save(tp)) {
320
      fprintf(stderr, "cannot move temporary table: %s\n", strerror(errno));
321
      goto end;
322
    }
45 luk 323
  }
324
 
61 luk 325
  ok = true;
45 luk 326
  fprintf(stderr, "table updated\n");
327
 
61 luk 328
end:  
329
 
330
  unlink(s);
331
  return ok;
45 luk 332
}
333
 
334
 
335
int main(int argc, char** argv)
336
{
337
  struct arguments arguments;
338
 
339
  arguments.user = NULL;
340
  arguments.oper = OPER_NONE;
341
  arguments.file = NULL;
342
 
343
  argp_parse (&argp, argc, argv, 0, 0, &arguments);
344
 
345
  if (arguments.file != NULL && arguments.oper != OPER_NONE) {
346
    fprintf(stderr, "invalid arguments - specify source file or operation\n");
347
    return 1;
348
  }
349
  if (arguments.file == NULL && arguments.oper == OPER_NONE) {
350
    fprintf(stderr, "invalid arguments - specify source file or operation\n");
351
    return 1;
352
  }
353
 
354
  uid_t uid = getuid();
355
 
356
  if (uid != 0 && arguments.user != NULL) {
357
    fprintf(stderr, "cannot access table for user %s: permission denied\n", arguments.user);
358
    return 1;
359
  }
360
 
361
  struct passwd pwd;
362
 
363
  if (arguments.user == NULL) {
364
    struct passwd* ppwd = getpwuid(uid);
365
    if (ppwd == NULL) {
366
      fprintf(stderr, "cannot determine current user\n");
367
      return 1;
368
    }
369
    memcpy(&pwd, ppwd, sizeof(pwd));
370
    arguments.user = pwd.pw_name;
371
  }
372
  else if (getpwnam(arguments.user) == NULL) {
373
    fprintf(stderr, "user %s not found\n", arguments.user);
374
    return 1;
375
  }
376
 
377
  if (!InCronTab::CheckUser(arguments.user)) {
378
    fprintf(stderr, "user %s is not allowed to use incron\n", arguments.user);
379
    return 1;
380
  }
381
 
382
  switch (arguments.oper) {
383
    case OPER_NONE:
384
      fprintf(stderr, "copying table from file: %s\n", arguments.file);
385
      if (!copy_from_file(arguments.file, arguments.user))
386
        return 1;
387
      break;
388
    case OPER_LIST:
389
      if (!list_table(arguments.user))
390
        return 1;
391
      break;
392
    case OPER_REMOVE:
393
      fprintf(stderr, "removing table for user %s\n", arguments.user);
394
      if (!remove_table(arguments.user))
395
        return 1;
396
      break;
397
    case OPER_EDIT:
398
      if (!edit_table(arguments.user))
399
        return 1;
400
      break;
401
    default:
402
      fprintf(stderr, "invalid usage\n");
403
      return 1;
404
  }
405
 
406
  return 0;
407
}