Subversion Repositories public

Rev

Rev 51 | Rev 61 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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