~ubuntu-branches/ubuntu/quantal/logrotate/quantal

« back to all changes in this revision

Viewing changes to .pc/nofollow.patch/logrotate.c

  • Committer: Bazaar Package Importer
  • Author(s): Lorenzo De Liso
  • Date: 2010-07-14 19:34:54 UTC
  • mfrom: (4.1.6 squeeze)
  • Revision ID: james.westby@ubuntu.com-20100714193454-ihz13w539ca2jz18
Tags: 3.7.8-6ubuntu1
* Merge from debian unstable (LP: #554823), remaining changes:
  - debian/control: Drop mailx to Suggests for Ubuntu; it's only used
    on request, and we don'c configure an MTA by default.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <sys/queue.h>
 
2
#include <alloca.h>
 
3
#include <ctype.h>
 
4
#include <dirent.h>
 
5
#include <errno.h>
 
6
#include <fcntl.h>
 
7
#include <popt.h>
 
8
#include <stdio.h>
 
9
#include <stdlib.h>
 
10
#include <string.h>
 
11
#include <sys/stat.h>
 
12
#include <sys/wait.h>
 
13
#include <time.h>
 
14
#include <unistd.h>
 
15
#include <glob.h>
 
16
#include <locale.h>
 
17
#include <sys/types.h>
 
18
#include <utime.h>
 
19
 
 
20
#ifdef WITH_SELINUX
 
21
#include <selinux/selinux.h>
 
22
static security_context_t prev_context = NULL;
 
23
int selinux_enabled = 0;
 
24
int selinux_enforce = 0;
 
25
#endif
 
26
 
 
27
#include "basenames.h"
 
28
#include "log.h"
 
29
#include "logrotate.h"
 
30
 
 
31
#if !defined(GLOB_ABORTED) && defined(GLOB_ABEND)
 
32
#define GLOB_ABORTED GLOB_ABEND
 
33
#endif
 
34
 
 
35
struct logState {
 
36
    char *fn;
 
37
    struct tm lastRotated;      /* only tm.mon, tm_mday, tm_year are good! */
 
38
    struct stat sb;
 
39
    int doRotate;
 
40
    LIST_ENTRY(logState) list;
 
41
};
 
42
 
 
43
struct logNames {
 
44
    char *firstRotated;
 
45
    char *disposeName;
 
46
    char *finalName;
 
47
    char *dirName;
 
48
    char *baseName;
 
49
};
 
50
 
 
51
struct logStates {
 
52
        LIST_HEAD(stateSet, logState) head;
 
53
} **states;
 
54
 
 
55
unsigned int hashSize;
 
56
int numLogs = 0;
 
57
int debug = 0;
 
58
char *mailCommand = DEFAULT_MAIL_COMMAND;
 
59
time_t nowSecs = 0;
 
60
 
 
61
static int shred_file(char *filename, struct logInfo *log);
 
62
 
 
63
static int globerr(const char *pathname, int theerr)
 
64
{
 
65
    message(MESS_ERROR, "error accessing %s: %s\n", pathname,
 
66
            strerror(theerr));
 
67
 
 
68
    /* We want the glob operation to continue, so return 0 */
 
69
    return 1;
 
70
}
 
71
 
 
72
#define HASH_SIZE_MIN 64
 
73
static int allocateHash(void)
 
74
{
 
75
        struct logInfo *log;
 
76
        unsigned int hs;
 
77
        int i;
 
78
 
 
79
        hs = 0;
 
80
 
 
81
        for (log = logs.tqh_first; log != NULL; log = log->list.tqe_next)
 
82
                hs += log->numFiles;
 
83
 
 
84
        hs *= 2;
 
85
 
 
86
        /* Enforce some reasonable minimum hash size */
 
87
        if (hs < HASH_SIZE_MIN)
 
88
                hs = HASH_SIZE_MIN;
 
89
 
 
90
        states = calloc(hs, sizeof(struct logStates *));
 
91
        if (states == NULL) {
 
92
                message(MESS_ERROR, "could not allocate memory for "
 
93
                                "hash table\n");
 
94
                return 1;
 
95
        }
 
96
 
 
97
        for (i = 0; i < hs; i++) {
 
98
                states[i] = malloc(sizeof(struct logState));
 
99
                if (states[i] == NULL) {
 
100
                        message(MESS_ERROR, "could not allocate memory for "
 
101
                                "hash element\n");
 
102
                        return 1;
 
103
                }
 
104
                LIST_INIT(&(states[i]->head));
 
105
        }
 
106
 
 
107
        hashSize = hs;
 
108
 
 
109
        return 0;
 
110
}
 
111
 
 
112
#define HASH_CONST 13
 
113
static unsigned hashIndex(const char *fn)
 
114
{
 
115
        unsigned hash = 0;
 
116
 
 
117
        while (*fn) {
 
118
                hash *= HASH_CONST;
 
119
                hash += *fn++;
 
120
        }
 
121
 
 
122
        return hash % hashSize;
 
123
}
 
124
 
 
125
static struct logState *newState(const char *fn)
 
126
{
 
127
        struct tm now = *localtime(&nowSecs);
 
128
        struct logState *new;
 
129
        time_t lr_time;
 
130
 
 
131
        if ((new = malloc(sizeof(*new))) == NULL)
 
132
                return NULL;
 
133
 
 
134
        if ((new->fn = strdup(fn)) == NULL)
 
135
                return NULL;
 
136
 
 
137
        new->doRotate = 0;
 
138
 
 
139
        memset(&new->lastRotated, 0, sizeof(new->lastRotated));
 
140
        new->lastRotated.tm_mon = now.tm_mon;
 
141
        new->lastRotated.tm_mday = now.tm_mday;
 
142
        new->lastRotated.tm_year = now.tm_year;
 
143
        new->lastRotated.tm_hour = now.tm_hour;
 
144
        new->lastRotated.tm_isdst = now.tm_isdst;
 
145
 
 
146
        /* fill in the rest of the new->lastRotated fields */
 
147
        lr_time = mktime(&new->lastRotated);
 
148
        new->lastRotated = *localtime(&lr_time);
 
149
 
 
150
        return new;
 
151
}
 
152
 
 
153
static struct logState *findState(const char *fn)
 
154
{
 
155
        unsigned int i = hashIndex(fn);
 
156
        struct logState *p;
 
157
 
 
158
        for (p = states[i]->head.lh_first; p != NULL; p = p->list.le_next)
 
159
                if (!strcmp(fn, p->fn))
 
160
                        break;
 
161
 
 
162
        /* new state */
 
163
        if (p == NULL) {
 
164
                if ((p = newState(fn)) == NULL)
 
165
                        return NULL;
 
166
 
 
167
                LIST_INSERT_HEAD(&(states[i]->head), p, list);
 
168
        }
 
169
 
 
170
        return p;
 
171
}
 
172
 
 
173
static int runScript(char *logfn, char *script)
 
174
{
 
175
    int rc;
 
176
 
 
177
    if (debug) {
 
178
        message(MESS_DEBUG, "running script with arg %s: \"%s\"\n",
 
179
                logfn, script);
 
180
        return 0;
 
181
    }
 
182
 
 
183
        if (!fork()) {
 
184
                execl("/bin/sh", "sh", "-c", script, "logrotate_script", logfn, NULL);
 
185
                exit(1);
 
186
        }
 
187
 
 
188
    wait(&rc);
 
189
    return rc;
 
190
}
 
191
 
 
192
static int runScriptMultiple(char *logfn, char *script, struct logNames **rotNames, int numFiles)
 
193
{
 
194
    int rc,i;
 
195
    char **argv;
 
196
 
 
197
    if (debug) {
 
198
        message(MESS_DEBUG, "running script (multiple) with arg %s: \"%s\"\n",
 
199
                logfn, script);
 
200
        return 0;
 
201
    }
 
202
 
 
203
    argv = calloc(numFiles+5,sizeof(char*));
 
204
    if (NULL == argv) {
 
205
        message(MESS_ERROR,"error allocating memory to runScriptMultiple\n");
 
206
        return -1;
 
207
    }
 
208
 
 
209
    argv[0] = "sh";
 
210
    argv[1] = "-c";
 
211
    argv[2] = script;
 
212
    argv[3] = "logrotate_script";
 
213
 
 
214
    for (i = 0; i<numFiles; i++) {
 
215
        argv[i+4] = rotNames[i]->finalName;
 
216
    }
 
217
    argv[i+4] = NULL;
 
218
 
 
219
    if (!fork()) {
 
220
        execv("/bin/sh",argv);
 
221
        exit(1);
 
222
    }
 
223
 
 
224
    wait(&rc);
 
225
    return rc;
 
226
}
 
227
 
 
228
int createOutputFile(char *fileName, int flags, struct stat *sb)
 
229
{
 
230
    int fd;
 
231
 
 
232
    fd = open(fileName, flags, (S_IRUSR | S_IWUSR) & sb->st_mode);
 
233
    if (fd < 0) {
 
234
        message(MESS_ERROR, "error creating output file %s: %s\n",
 
235
                fileName, strerror(errno));
 
236
        return -1;
 
237
    }
 
238
    if (fchmod(fd, (S_IRUSR | S_IWUSR) & sb->st_mode)) {
 
239
        message(MESS_ERROR, "error setting mode of %s: %s\n",
 
240
                fileName, strerror(errno));
 
241
        close(fd);
 
242
        return -1;
 
243
    }
 
244
    if (fchown(fd, sb->st_uid, sb->st_gid)) {
 
245
        message(MESS_ERROR, "error setting owner of %s: %s\n",
 
246
                fileName, strerror(errno));
 
247
        if (geteuid() == 0) {
 
248
                /* If run as non-root, we may not be able to duplicate
 
249
                   the ownership of the file. Warn but don't fail. */
 
250
                close(fd);
 
251
                return -1;
 
252
        }
 
253
    }
 
254
    if (fchmod(fd, sb->st_mode)) {
 
255
        message(MESS_ERROR, "error setting mode of %s: %s\n",
 
256
                fileName, strerror(errno));
 
257
        close(fd);
 
258
        return -1;
 
259
    }
 
260
    return fd;
 
261
}
 
262
 
 
263
#define SHRED_CALL "shred -u "
 
264
#define SHRED_COUNT_FLAG "-n "
 
265
#define DIGITS 10
 
266
/* unlink, but try to call shred from GNU fileutils */
 
267
static int shred_file(char *filename, struct logInfo *log)
 
268
{
 
269
        int len, ret;
 
270
        char *cmd;
 
271
        char count[DIGITS];    /*  that's a lot of shredding :)  */
 
272
 
 
273
        if (!(log->flags & LOG_FLAG_SHRED)) {
 
274
                return unlink(filename);
 
275
        }
 
276
 
 
277
        len = strlen(filename) + strlen(SHRED_CALL);
 
278
        len += strlen(SHRED_COUNT_FLAG) + DIGITS;
 
279
        cmd = malloc(len);
 
280
 
 
281
        if (!cmd) {
 
282
                message(MESS_ERROR, "malloc error while shredding");
 
283
                return unlink(filename);
 
284
        }
 
285
        strcpy(cmd, SHRED_CALL);
 
286
        if (log->shred_cycles != 0) {
 
287
                strcat(cmd, SHRED_COUNT_FLAG);
 
288
                snprintf(count, DIGITS - 1, "%d", log->shred_cycles);
 
289
                strcat(count, " ");
 
290
                strcat(cmd, count);
 
291
        }
 
292
        strcat(cmd, filename);
 
293
        ret = system(cmd);
 
294
        free(cmd);
 
295
        if (ret != 0) {
 
296
                message(MESS_ERROR, "Failed to shred %s\n, trying unlink", filename);
 
297
                if (ret != -1) {
 
298
                        message(MESS_NORMAL, "Shred returned %d\n", ret);
 
299
                }
 
300
                return unlink(filename);
 
301
        } else {
 
302
                return ret;
 
303
        }
 
304
}
 
305
 
 
306
static int removeLogFile(char *name, struct logInfo *log)
 
307
{
 
308
    message(MESS_DEBUG, "removing old log %s\n", name);
 
309
 
 
310
    if (!debug && shred_file(name, log)) {
 
311
        message(MESS_ERROR, "Failed to remove old log %s: %s\n",
 
312
                name, strerror(errno));
 
313
        return 1;
 
314
    }
 
315
    return 0;
 
316
}
 
317
 
 
318
static int compressLogFile(char *name, struct logInfo *log, struct stat *sb)
 
319
{
 
320
    char *compressedName;
 
321
    const char **fullCommand;
 
322
    struct utimbuf utim;
 
323
    int inFile;
 
324
    int outFile;
 
325
    int i;
 
326
    int status;
 
327
 
 
328
    message(MESS_DEBUG, "compressing log with: %s\n", log->compress_prog);
 
329
    if (debug)
 
330
        return 0;
 
331
 
 
332
    fullCommand = alloca(sizeof(*fullCommand) *
 
333
                         (log->compress_options_count + 2));
 
334
    fullCommand[0] = log->compress_prog;
 
335
    for (i = 0; i < log->compress_options_count; i++)
 
336
        fullCommand[i + 1] = log->compress_options_list[i];
 
337
    fullCommand[log->compress_options_count + 1] = NULL;
 
338
 
 
339
    compressedName = alloca(strlen(name) + strlen(log->compress_ext) + 2);
 
340
    sprintf(compressedName, "%s%s", name, log->compress_ext);
 
341
 
 
342
    if ((inFile = open(name, O_RDONLY)) < 0) {
 
343
        message(MESS_ERROR, "unable to open %s for compression\n", name);
 
344
        return 1;
 
345
    }
 
346
 
 
347
    outFile =
 
348
        createOutputFile(compressedName, O_RDWR | O_CREAT | O_TRUNC, sb);
 
349
    if (outFile < 0) {
 
350
        close(inFile);
 
351
        return 1;
 
352
    }
 
353
 
 
354
    if (!fork()) {
 
355
        dup2(inFile, 0);
 
356
        close(inFile);
 
357
        dup2(outFile, 1);
 
358
        close(outFile);
 
359
 
 
360
        execvp(fullCommand[0], (void *) fullCommand);
 
361
        exit(1);
 
362
    }
 
363
 
 
364
    close(inFile);
 
365
    close(outFile);
 
366
 
 
367
    wait(&status);
 
368
 
 
369
    if (!WIFEXITED(status) || WEXITSTATUS(status)) {
 
370
        message(MESS_ERROR, "failed to compress log %s\n", name);
 
371
        return 1;
 
372
    }
 
373
 
 
374
    utim.actime = sb->st_atime;
 
375
    utim.modtime = sb->st_mtime;
 
376
    utime(compressedName,&utim);
 
377
    /* If we can't change atime/mtime, it's not a disaster.
 
378
       It might possibly fail under SELinux. */
 
379
 
 
380
    shred_file(name, log);
 
381
 
 
382
    return 0;
 
383
}
 
384
 
 
385
static int mailLog(char *logFile, char *mailCommand,
 
386
                   char *uncompressCommand, char *address, char *subject)
 
387
{
 
388
    int mailInput;
 
389
    pid_t mailChild, uncompressChild = 0;
 
390
    int mailStatus, uncompressStatus;
 
391
    int uncompressPipe[2];
 
392
    char *mailArgv[] = { mailCommand, "-s", subject, address, NULL };
 
393
    int rc = 0;
 
394
 
 
395
    if ((mailInput = open(logFile, O_RDONLY)) < 0) {
 
396
        message(MESS_ERROR, "failed to open %s for mailing: %s\n", logFile,
 
397
                strerror(errno));
 
398
        return 1;
 
399
    }
 
400
 
 
401
    if (uncompressCommand) {
 
402
                if (pipe(uncompressPipe) < 0) {
 
403
                        message(MESS_ERROR, "error opening pipe for uncompress: %s",
 
404
                                        strerror(errno));
 
405
                        return 1;
 
406
                }
 
407
                if (!(uncompressChild = fork())) {
 
408
                        /* uncompress child */
 
409
                        dup2(mailInput, 0);
 
410
                        close(mailInput);
 
411
                        dup2(uncompressPipe[1], 1);
 
412
                        close(uncompressPipe[0]);
 
413
                        close(uncompressPipe[1]);
 
414
 
 
415
                        execlp(uncompressCommand, uncompressCommand, NULL);
 
416
                        exit(1);
 
417
                }
 
418
 
 
419
                close(mailInput);
 
420
                mailInput = uncompressPipe[0];
 
421
                close(uncompressPipe[1]);
 
422
    }
 
423
 
 
424
    if (!(mailChild = fork())) {
 
425
        dup2(mailInput, 0);
 
426
        close(mailInput);
 
427
        close(1);
 
428
 
 
429
        execvp(mailArgv[0], mailArgv);
 
430
        exit(1);
 
431
    }
 
432
 
 
433
    close(mailInput);
 
434
 
 
435
    waitpid(mailChild, &mailStatus, 0);
 
436
 
 
437
    if (!WIFEXITED(mailStatus) || WEXITSTATUS(mailStatus)) {
 
438
        message(MESS_ERROR, "mail command failed for %s\n", logFile);
 
439
        rc = 1;
 
440
    }
 
441
 
 
442
    if (uncompressCommand) {
 
443
        waitpid(uncompressChild, &uncompressStatus, 0);
 
444
 
 
445
        if (!WIFEXITED(uncompressStatus) || WEXITSTATUS(uncompressStatus)) {
 
446
            message(MESS_ERROR, "uncompress command failed mailing %s\n",
 
447
                    logFile);
 
448
            rc = 1;
 
449
        }
 
450
    }
 
451
 
 
452
    return rc;
 
453
}
 
454
 
 
455
static int mailLogWrapper(char *mailFilename, char *mailCommand,
 
456
                          int logNum, struct logInfo *log)
 
457
{
 
458
    /* if the log is compressed (and we're not mailing a
 
459
     * file whose compression has been delayed), we need
 
460
     * to uncompress it */
 
461
    if ((log->flags & LOG_FLAG_COMPRESS) &&
 
462
        !((log->flags & LOG_FLAG_DELAYCOMPRESS) &&
 
463
          (log->flags & LOG_FLAG_MAILFIRST))) {
 
464
        if (mailLog(mailFilename, mailCommand,
 
465
                    log->uncompress_prog, log->logAddress,
 
466
                    log->files[logNum]))
 
467
            return 1;
 
468
    } else {
 
469
        if (mailLog(mailFilename, mailCommand, NULL,
 
470
                    log->logAddress, mailFilename))
 
471
            return 1;
 
472
    }
 
473
    return 0;
 
474
}
 
475
 
 
476
static int copyTruncate(char *currLog, char *saveLog, struct stat *sb,
 
477
                        int flags)
 
478
{
 
479
    char buf[BUFSIZ];
 
480
    int fdcurr = -1, fdsave = -1;
 
481
    ssize_t cnt;
 
482
 
 
483
    message(MESS_DEBUG, "copying %s to %s\n", currLog, saveLog);
 
484
 
 
485
    if (!debug) {
 
486
        if ((fdcurr = open(currLog, O_RDWR)) < 0) {
 
487
            message(MESS_ERROR, "error opening %s: %s\n", currLog,
 
488
                    strerror(errno));
 
489
            return 1;
 
490
        }
 
491
#ifdef WITH_SELINUX
 
492
        if (selinux_enabled) {
 
493
            security_context_t oldContext;
 
494
            if (fgetfilecon_raw(fdcurr, &oldContext) >= 0) {
 
495
                if (getfscreatecon_raw(&prev_context) < 0) {
 
496
                    message(MESS_ERROR,
 
497
                            "getting default context: %s\n",
 
498
                            strerror(errno));
 
499
                    if (selinux_enforce) {
 
500
                                freecon(oldContext);
 
501
                                return 1;
 
502
                    }
 
503
                }
 
504
                if (setfscreatecon_raw(oldContext) < 0) {
 
505
                    message(MESS_ERROR,
 
506
                            "setting file context %s to %s: %s\n",
 
507
                            saveLog, oldContext, strerror(errno));
 
508
                        if (selinux_enforce) {
 
509
                                freecon(oldContext);
 
510
                                return 1;
 
511
                    }
 
512
                }
 
513
                message(MESS_DEBUG, "set default create context\n");
 
514
                freecon(oldContext);
 
515
            } else {
 
516
                    if (errno != ENOTSUP) {
 
517
                            message(MESS_ERROR, "getting file context %s: %s\n",
 
518
                                    currLog, strerror(errno));
 
519
                            if (selinux_enforce) {
 
520
                                    return 1;
 
521
                            }
 
522
                    }
 
523
            }
 
524
        }
 
525
#endif
 
526
        fdsave =
 
527
            createOutputFile(saveLog, O_WRONLY | O_CREAT | O_TRUNC, sb);
 
528
#ifdef WITH_SELINUX
 
529
        if (selinux_enabled) {
 
530
            setfscreatecon_raw(prev_context);
 
531
                freecon(prev_context);
 
532
                prev_context = NULL;
 
533
        }
 
534
#endif
 
535
        if (fdsave < 0) {
 
536
            close(fdcurr);
 
537
            return 1;
 
538
        }
 
539
        while ((cnt = read(fdcurr, buf, sizeof(buf))) > 0) {
 
540
            if (write(fdsave, buf, cnt) != cnt) {
 
541
                message(MESS_ERROR, "error writing to %s: %s\n",
 
542
                        saveLog, strerror(errno));
 
543
                close(fdcurr);
 
544
                close(fdsave);
 
545
                return 1;
 
546
            }
 
547
        }
 
548
        if (cnt != 0) {
 
549
            message(MESS_ERROR, "error reading %s: %s\n",
 
550
                    currLog, strerror(errno));
 
551
            close(fdcurr);
 
552
            close(fdsave);
 
553
            return 1;
 
554
        }
 
555
    }
 
556
 
 
557
    if (flags & LOG_FLAG_COPYTRUNCATE) {
 
558
        message(MESS_DEBUG, "truncating %s\n", currLog);
 
559
 
 
560
        if (!debug)
 
561
            if (ftruncate(fdcurr, 0)) {
 
562
                message(MESS_ERROR, "error truncating %s: %s\n", currLog,
 
563
                        strerror(errno));
 
564
                close(fdcurr);
 
565
                close(fdsave);
 
566
                return 1;
 
567
            }
 
568
    } else
 
569
        message(MESS_DEBUG, "Not truncating %s\n", currLog);
 
570
 
 
571
    close(fdcurr);
 
572
    close(fdsave);
 
573
    return 0;
 
574
}
 
575
 
 
576
int findNeedRotating(struct logInfo *log, int logNum)
 
577
{
 
578
    struct stat sb;
 
579
    struct logState *state = NULL;
 
580
    struct tm now = *localtime(&nowSecs);
 
581
 
 
582
    message(MESS_DEBUG, "considering log %s\n", log->files[logNum]);
 
583
 
 
584
    if (stat(log->files[logNum], &sb)) {
 
585
        if ((log->flags & LOG_FLAG_MISSINGOK) && (errno == ENOENT)) {
 
586
            message(MESS_DEBUG, "  log %s does not exist -- skipping\n",
 
587
                    log->files[logNum]);
 
588
            return 0;
 
589
        }
 
590
        message(MESS_ERROR, "stat of %s failed: %s\n", log->files[logNum],
 
591
                strerror(errno));
 
592
        return 1;
 
593
    }
 
594
 
 
595
    state = findState(log->files[logNum]);
 
596
    state->doRotate = 0;
 
597
    state->sb = sb;
 
598
 
 
599
    if (log->criterium == ROT_SIZE) {
 
600
        state->doRotate = (sb.st_size >= log->threshhold);
 
601
    } else if (log->criterium == ROT_FORCE) {
 
602
        /* user forced rotation of logs from command line */
 
603
        state->doRotate = 1;
 
604
    } else if (state->lastRotated.tm_year > now.tm_year ||
 
605
               (state->lastRotated.tm_year == now.tm_year &&
 
606
                (state->lastRotated.tm_mon > now.tm_mon ||
 
607
                 (state->lastRotated.tm_mon == now.tm_mon &&
 
608
                  state->lastRotated.tm_mday > now.tm_mday)))) {
 
609
        message(MESS_ERROR,
 
610
                "log %s last rotated in the future -- rotation forced\n",
 
611
                log->files[logNum]);
 
612
        state->doRotate = 1;
 
613
    } else if (state->lastRotated.tm_year != now.tm_year ||
 
614
               state->lastRotated.tm_mon != now.tm_mon ||
 
615
               state->lastRotated.tm_mday != now.tm_mday) {
 
616
        switch (log->criterium) {
 
617
        case ROT_WEEKLY:
 
618
            /* rotate if:
 
619
               1) the current weekday is before the weekday of the
 
620
               last rotation
 
621
               2) more then a week has passed since the last
 
622
               rotation */
 
623
            state->doRotate = ((now.tm_wday < state->lastRotated.tm_wday)
 
624
                               ||
 
625
                               ((mktime(&now) -
 
626
                                 mktime(&state->lastRotated)) >
 
627
                                (7 * 24 * 3600)));
 
628
            break;
 
629
        case ROT_MONTHLY:
 
630
            /* rotate if the logs haven't been rotated this month or
 
631
               this year */
 
632
            state->doRotate = ((now.tm_mon != state->lastRotated.tm_mon) ||
 
633
                               (now.tm_year !=
 
634
                                state->lastRotated.tm_year));
 
635
            break;
 
636
        case ROT_DAYS:
 
637
            /* FIXME: only days=1 is implemented!! */
 
638
            state->doRotate = 1;
 
639
            break;
 
640
        case ROT_YEARLY:
 
641
            /* rotate if the logs haven't been rotated this year */
 
642
            state->doRotate = (now.tm_year != state->lastRotated.tm_year);
 
643
            break;
 
644
        default:
 
645
            /* ack! */
 
646
            state->doRotate = 0;
 
647
            break;
 
648
        }
 
649
        if (log->minsize && sb.st_size < log->minsize)
 
650
            state->doRotate = 0;
 
651
    }
 
652
 
 
653
    /* The notifempty flag overrides the normal criteria */
 
654
    if (!(log->flags & LOG_FLAG_IFEMPTY) && !sb.st_size)
 
655
        state->doRotate = 0;
 
656
 
 
657
    if (state->doRotate) {
 
658
        message(MESS_DEBUG, "  log needs rotating\n");
 
659
    } else {
 
660
        message(MESS_DEBUG, "  log does not need rotating\n");
 
661
    }
 
662
 
 
663
    return 0;
 
664
}
 
665
 
 
666
int prerotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
 
667
                       struct logNames *rotNames)
 
668
{
 
669
    struct tm now = *localtime(&nowSecs);
 
670
    char *oldName, *newName = NULL;
 
671
    char *tmp;
 
672
    char *compext = "";
 
673
    char *fileext = "";
 
674
    int hasErrors = 0;
 
675
    int i, j;
 
676
    char *glob_pattern;
 
677
    glob_t globResult;
 
678
    int rc;
 
679
    int rotateCount = log->rotateCount ? log->rotateCount : 1;
 
680
    int logStart = (log->logStart == -1) ? 1 : log->logStart;
 
681
#define DATEEXT_LEN 64
 
682
#define PATTERN_LEN (DATEEXT_LEN * 2)
 
683
        char dext_str[DATEEXT_LEN];
 
684
        char dformat[DATEEXT_LEN];
 
685
        char dext_pattern[PATTERN_LEN];
 
686
        char *dext;
 
687
 
 
688
    if (!state->doRotate)
 
689
        return 0;
 
690
 
 
691
    /* Logs with rotateCounts of 0 are rotated once, then removed. This
 
692
       lets scripts run properly, and everything gets mailed properly. */
 
693
 
 
694
    message(MESS_DEBUG, "rotating log %s, log->rotateCount is %d\n",
 
695
            log->files[logNum], log->rotateCount);
 
696
 
 
697
    if (log->flags & LOG_FLAG_COMPRESS)
 
698
        compext = log->compress_ext;
 
699
 
 
700
    state->lastRotated = now;
 
701
 
 
702
    if (log->oldDir) {
 
703
        if (log->oldDir[0] != '/') {
 
704
            char *ld = ourDirName(log->files[logNum]);
 
705
            rotNames->dirName =
 
706
                malloc(strlen(ld) + strlen(log->oldDir) + 2);
 
707
            sprintf(rotNames->dirName, "%s/%s", ld, log->oldDir);
 
708
            free(ld);
 
709
        } else
 
710
            rotNames->dirName = strdup(log->oldDir);
 
711
    } else
 
712
        rotNames->dirName = ourDirName(log->files[logNum]);
 
713
 
 
714
    rotNames->baseName = strdup(ourBaseName(log->files[logNum]));
 
715
 
 
716
    oldName = alloca(PATH_MAX);
 
717
    newName = alloca(PATH_MAX);
 
718
    rotNames->disposeName = malloc(PATH_MAX);
 
719
 
 
720
    if (log->extension &&
 
721
        strncmp(&
 
722
                (rotNames->
 
723
                 baseName[strlen(rotNames->baseName) -
 
724
                          strlen(log->extension)]), log->extension,
 
725
                strlen(log->extension)) == 0) {
 
726
        char *tempstr;
 
727
 
 
728
        fileext = log->extension;
 
729
        tempstr =
 
730
            calloc(strlen(rotNames->baseName) - strlen(log->extension) + 1,
 
731
                   sizeof(char));
 
732
        strncat(tempstr, rotNames->baseName,
 
733
                strlen(rotNames->baseName) - strlen(log->extension));
 
734
        free(rotNames->baseName);
 
735
        rotNames->baseName = tempstr;
 
736
    }
 
737
        
 
738
        /* Allow only %Y %d %m and create valid strftime format string
 
739
         * Construct the glob pattern corresponding to the date format */
 
740
        dext_str[0] = '\0';
 
741
        if (log->dateformat) {
 
742
                i = j = 0;
 
743
                memset(dext_pattern, 0, sizeof(dext_pattern));
 
744
                dext = log->dateformat;
 
745
                while (*dext == ' ')
 
746
                        dext++;
 
747
                while ((*dext != '\0') && (!hasErrors)) {
 
748
                        /* Will there be a space for a char and '\0'? */
 
749
                        if (j >= (sizeof(dext_pattern) - 1)) {
 
750
                                message(MESS_ERROR, "Date format %s is too long\n",
 
751
                                                log->dateformat);
 
752
                                hasErrors = 1;
 
753
                                break;
 
754
                        }
 
755
                        if (*dext == '%') {
 
756
                                switch (*(dext + 1)) {
 
757
                                        case 'Y':
 
758
                                                strncat(dext_pattern, "[0-9][0-9]",
 
759
                                                                sizeof(dext_pattern) - strlen(dext_pattern));
 
760
                                                j += 10; /* strlen("[0-9][0-9]") */
 
761
                                        case 'm':
 
762
                                        case 'd':
 
763
                                                strncat(dext_pattern, "[0-9][0-9]",
 
764
                                                                sizeof(dext_pattern) - strlen(dext_pattern));
 
765
                                                j += 10;
 
766
                                                if (j >= (sizeof(dext_pattern) - 1)) {
 
767
                                                        message(MESS_ERROR, "Date format %s is too long\n",
 
768
                                                                        log->dateformat);
 
769
                                                        hasErrors = 1;
 
770
                                                        break;
 
771
                                                }
 
772
                                                dformat[i++] = *(dext++);
 
773
                                                dformat[i] = *dext;
 
774
                                                break;
 
775
                                        case 's':
 
776
                                                /* End of year 2293 this pattern does not work. */
 
777
                                                strncat(dext_pattern,
 
778
                                                                "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
 
779
                                                                sizeof(dext_pattern) - strlen(dext_pattern));
 
780
                                                j += 50;
 
781
                                                if (j >= (sizeof(dext_pattern) - 1)) {
 
782
                                                        message(MESS_ERROR, "Date format %s is too long\n",
 
783
                                                                        log->dateformat);
 
784
                                                        hasErrors = 1;
 
785
                                                        break;
 
786
                                                }
 
787
                                                dformat[i++] = *(dext++);
 
788
                                                dformat[i] = *dext;
 
789
                                                break;
 
790
                                        default:
 
791
                                                dformat[i++] = *dext;
 
792
                                                dformat[i] = '%';
 
793
                                                dext_pattern[j++] = *dext;
 
794
                                                break;
 
795
                                }
 
796
                        } else {
 
797
                                dformat[i] = *dext;
 
798
                                dext_pattern[j++] = *dext;
 
799
                        }
 
800
                        ++i;
 
801
                        ++dext;
 
802
                }
 
803
                dformat[i] = '\0';
 
804
                message(MESS_DEBUG, "Converted '%s' -> '%s'\n", log->dateformat, dformat);
 
805
                strftime(dext_str, sizeof(dext_str), dformat, &now);
 
806
        } else {
 
807
                /* The default dateformat and glob pattern */
 
808
                strftime(dext_str, sizeof(dext_str), "-%Y%m%d", &now);
 
809
                strncpy(dext_pattern, "-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
 
810
                                sizeof(dext_pattern));
 
811
                dext_pattern[PATTERN_LEN - 1] = '\0';
 
812
        }
 
813
        message(MESS_DEBUG, "dateext suffix '%s'\n", dext_str);
 
814
        message(MESS_DEBUG, "glob pattern '%s'\n", dext_pattern);
 
815
 
 
816
    /* First compress the previous log when necessary */
 
817
    if (log->flags & LOG_FLAG_COMPRESS &&
 
818
        log->flags & LOG_FLAG_DELAYCOMPRESS) {
 
819
        if (log->flags & LOG_FLAG_DATEEXT) {
 
820
                /* glob for uncompressed files with our pattern */
 
821
                if (asprintf(&glob_pattern, "%s/%s%s%s", rotNames->dirName,
 
822
                                        rotNames->baseName, dext_pattern, fileext) < 0) {
 
823
                        message(MESS_ERROR, "could not allocate glob pattern memory\n");
 
824
                }
 
825
            rc = glob(glob_pattern, 0, globerr, &globResult);
 
826
            if (!rc && globResult.gl_pathc > 0) {
 
827
                for (i = 0; i < globResult.gl_pathc && !hasErrors; i++) {
 
828
                    struct stat sbprev;
 
829
 
 
830
                        snprintf(oldName, PATH_MAX, "%s", (globResult.gl_pathv)[i]);
 
831
                        if (stat(oldName, &sbprev)) {
 
832
                        message(MESS_DEBUG,
 
833
                                "previous log %s does not exist\n",
 
834
                                oldName);
 
835
                    } else {
 
836
                        hasErrors = compressLogFile(oldName, log, &sbprev);
 
837
                    }
 
838
                }
 
839
            } else {
 
840
                message(MESS_DEBUG,
 
841
                        "glob finding logs to compress failed\n");
 
842
                /* fallback to old behaviour */
 
843
                snprintf(oldName, PATH_MAX, "%s/%s.%d%s", rotNames->dirName,
 
844
                        rotNames->baseName, logStart, fileext);
 
845
            }
 
846
            globfree(&globResult);
 
847
            free(glob_pattern);
 
848
        } else {
 
849
            struct stat sbprev;
 
850
 
 
851
            snprintf(oldName, PATH_MAX, "%s/%s.%d%s", rotNames->dirName,
 
852
                    rotNames->baseName, logStart, fileext);
 
853
            if (stat(oldName, &sbprev)) {
 
854
                message(MESS_DEBUG, "previous log %s does not exist\n",
 
855
                        oldName);
 
856
            } else {
 
857
                hasErrors = compressLogFile(oldName, log, &sbprev);
 
858
            }
 
859
        }
 
860
    }
 
861
 
 
862
    rotNames->firstRotated =
 
863
        malloc(strlen(rotNames->dirName) + strlen(rotNames->baseName) +
 
864
               strlen(fileext) + strlen(compext) + 30);
 
865
 
 
866
    if (log->flags & LOG_FLAG_DATEEXT) {
 
867
        /* glob for compressed files with our pattern
 
868
         * and compress ext */
 
869
        if (asprintf(&glob_pattern, "%s/%s%s%s%s", rotNames->dirName,
 
870
                                rotNames->baseName, dext_pattern, fileext, compext) < 0) {
 
871
                message(MESS_ERROR, "could not allocate glob pattern memory\n");
 
872
        }
 
873
        rc = glob(glob_pattern, 0, globerr, &globResult);
 
874
        if (!rc) {
 
875
            /* search for files to drop, if we find one remember it,
 
876
             * if we find another one mail and remove the first and
 
877
             * remember the second and so on */
 
878
            struct stat fst_buf;
 
879
            int mail_out = -1;
 
880
            /* remove the first (n - rotateCount) matches
 
881
             * no real rotation needed, since the files have
 
882
             * the date in their name */
 
883
            for (i = 0; i < globResult.gl_pathc; i++) {
 
884
                if (!stat((globResult.gl_pathv)[i], &fst_buf)) {
 
885
                    if ((i <= ((int) globResult.gl_pathc - rotateCount))
 
886
                        || ((log->rotateAge > 0)
 
887
                            &&
 
888
                            (((nowSecs - fst_buf.st_mtime) / 60 / 60 / 24)
 
889
                             > log->rotateAge))) {
 
890
                        if (mail_out != -1) {
 
891
                            char *mailFilename =
 
892
                                (globResult.gl_pathv)[mail_out];
 
893
                            if (!hasErrors && log->logAddress)
 
894
                                hasErrors =
 
895
                                    mailLogWrapper(mailFilename,
 
896
                                                   mailCommand, logNum,
 
897
                                                   log);
 
898
                            if (!hasErrors)
 
899
                                message(MESS_DEBUG, "removing %s\n", mailFilename);
 
900
                                hasErrors = removeLogFile(mailFilename, log);
 
901
                        }
 
902
                        mail_out = i;
 
903
                    }
 
904
                }
 
905
            }
 
906
            if (mail_out != -1) {
 
907
                /* oldName is oldest Backup found (for unlink later) */
 
908
                snprintf(oldName, PATH_MAX, "%s", (globResult.gl_pathv)[mail_out]);
 
909
                strcpy(rotNames->disposeName, oldName);
 
910
            } else {
 
911
                free(rotNames->disposeName);
 
912
                rotNames->disposeName = NULL;
 
913
            }
 
914
        } else {
 
915
            message(MESS_DEBUG, "glob finding old rotated logs failed\n");
 
916
            free(rotNames->disposeName);
 
917
            rotNames->disposeName = NULL;
 
918
        }
 
919
        /* firstRotated is most recently created/compressed rotated log */
 
920
        sprintf(rotNames->firstRotated, "%s/%s%s%s%s",
 
921
                rotNames->dirName, rotNames->baseName, dext_str, fileext,
 
922
                (log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext);
 
923
        globfree(&globResult);
 
924
        free(glob_pattern);
 
925
    } else {
 
926
        if (log->rotateAge) {
 
927
            struct stat fst_buf;
 
928
            for (i = 1; i <= rotateCount + 1; i++) {
 
929
                snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
 
930
                        rotNames->baseName, i, fileext, compext);
 
931
                if (!stat(oldName, &fst_buf)
 
932
                    && (((nowSecs - fst_buf.st_mtime) / 60 / 60 / 24)
 
933
                        > log->rotateAge)) {
 
934
                    char *mailFilename = oldName;
 
935
                    if (!hasErrors && log->logAddress)
 
936
                        hasErrors =
 
937
                            mailLogWrapper(mailFilename, mailCommand,
 
938
                                           logNum, log);
 
939
                    if (!hasErrors)
 
940
                        hasErrors = removeLogFile(mailFilename, log);
 
941
                }
 
942
            }
 
943
        }
 
944
 
 
945
        snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
 
946
                rotNames->baseName, logStart + rotateCount, fileext,
 
947
                compext);
 
948
        strcpy(newName, oldName);
 
949
 
 
950
        strcpy(rotNames->disposeName, oldName);
 
951
 
 
952
        sprintf(rotNames->firstRotated, "%s/%s.%d%s%s", rotNames->dirName,
 
953
                rotNames->baseName, logStart, fileext,
 
954
                (log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext);
 
955
 
 
956
#ifdef WITH_SELINUX
 
957
        if (selinux_enabled) {
 
958
            security_context_t oldContext = NULL;
 
959
            if (getfilecon_raw(log->files[logNum], &oldContext) > 0) {
 
960
                        if (getfscreatecon_raw(&prev_context) < 0) {
 
961
                                message(MESS_ERROR,
 
962
                                        "getting default context: %s\n",
 
963
                                        strerror(errno));
 
964
                                if (selinux_enforce) {
 
965
                                        freecon(oldContext);
 
966
                                        return 1;
 
967
                                }
 
968
                        }
 
969
                        if (setfscreatecon_raw(oldContext) < 0) {
 
970
                                message(MESS_ERROR,
 
971
                                        "setting file context %s to %s: %s\n",
 
972
                                        log->files[logNum], oldContext,
 
973
                                        strerror(errno));
 
974
                                if (selinux_enforce) {
 
975
                                        freecon(oldContext);
 
976
                                        return 1;
 
977
                                }
 
978
                        }
 
979
                        freecon(oldContext);
 
980
            } else {
 
981
                if (errno != ENOENT && errno != ENOTSUP) {
 
982
                        message(MESS_ERROR, "getting file context %s: %s\n",
 
983
                                log->files[logNum], strerror(errno));
 
984
                        if (selinux_enforce) {
 
985
                                return 1;
 
986
                        }
 
987
                }
 
988
            }
 
989
        }
 
990
#endif
 
991
        for (i = rotateCount + logStart - 1; (i >= 0) && !hasErrors; i--) {
 
992
            tmp = newName;
 
993
            newName = oldName;
 
994
            oldName = tmp;
 
995
                snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
 
996
                    rotNames->baseName, i, fileext, compext);
 
997
 
 
998
            message(MESS_DEBUG,
 
999
                    "renaming %s to %s (rotatecount %d, logstart %d, i %d), \n",
 
1000
                    oldName, newName, rotateCount, logStart, i);
 
1001
 
 
1002
            if (!debug && rename(oldName, newName)) {
 
1003
                if (errno == ENOENT) {
 
1004
                    message(MESS_DEBUG, "old log %s does not exist\n",
 
1005
                            oldName);
 
1006
                } else {
 
1007
                    message(MESS_ERROR, "error renaming %s to %s: %s\n",
 
1008
                            oldName, newName, strerror(errno));
 
1009
                    hasErrors = 1;
 
1010
                }
 
1011
            }
 
1012
        }
 
1013
    }                           /* !LOG_FLAG_DATEEXT */
 
1014
 
 
1015
        if (log->flags & LOG_FLAG_DATEEXT) {
 
1016
                char *destFile = alloca(PATH_MAX);
 
1017
                struct stat fst_buf;
 
1018
 
 
1019
                if (asprintf(&(rotNames->finalName), "%s/%s%s%s", rotNames->dirName,
 
1020
                                        rotNames->baseName, dext_str, fileext) < 0) {
 
1021
                        message(MESS_ERROR, "could not allocate finalName memory\n");
 
1022
                }
 
1023
                snprintf(destFile, PATH_MAX, "%s%s", rotNames->finalName, compext);
 
1024
                if (!stat(destFile, &fst_buf)) {
 
1025
                        message(MESS_DEBUG,
 
1026
                                        "destination %s already exists, skipping rotation\n",
 
1027
                                        rotNames->firstRotated);
 
1028
                        hasErrors = 1;
 
1029
                }
 
1030
        } else {
 
1031
                /* note: the gzip extension is *not* used here! */
 
1032
                if (asprintf(&(rotNames->finalName), "%s/%s.%d%s", rotNames->dirName,
 
1033
                                        rotNames->baseName, logStart, fileext) < 0) {
 
1034
                        message(MESS_ERROR, "could not allocate finalName memory\n");
 
1035
                }
 
1036
        }
 
1037
 
 
1038
    /* if the last rotation doesn't exist, that's okay */
 
1039
    if (!debug && rotNames->disposeName
 
1040
        && access(rotNames->disposeName, F_OK)) {
 
1041
        message(MESS_DEBUG,
 
1042
                "log %s doesn't exist -- won't try to " "dispose of it\n",
 
1043
                rotNames->disposeName);
 
1044
        free(rotNames->disposeName);
 
1045
        rotNames->disposeName = NULL;
 
1046
    }
 
1047
 
 
1048
    return hasErrors;
 
1049
}
 
1050
 
 
1051
int rotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
 
1052
                    struct logNames *rotNames)
 
1053
{
 
1054
    int hasErrors = 0;
 
1055
    struct stat sb;
 
1056
    int fd;
 
1057
#ifdef WITH_SELINUX
 
1058
        security_context_t savedContext = NULL;
 
1059
#endif
 
1060
 
 
1061
    if (!state->doRotate)
 
1062
        return 0;
 
1063
 
 
1064
    if (!hasErrors) {
 
1065
 
 
1066
        if (!(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
 
1067
#ifdef WITH_SELINUX
 
1068
                if (selinux_enabled) {
 
1069
                        security_context_t oldContext = NULL;
 
1070
                        int fdcurr = -1;
 
1071
 
 
1072
                        if ((fdcurr = open(log->files[logNum], O_RDWR)) < 0) {
 
1073
                                message(MESS_ERROR, "error opening %s: %s\n",
 
1074
                                                log->files[logNum],
 
1075
                                        strerror(errno));
 
1076
                                return 1;
 
1077
                        }
 
1078
                        if (fgetfilecon_raw(fdcurr, &oldContext) >= 0) {
 
1079
                                if (getfscreatecon_raw(&savedContext) < 0) {
 
1080
                                        message(MESS_ERROR,
 
1081
                                                "getting default context: %s\n",
 
1082
                                                strerror(errno));
 
1083
                                        if (selinux_enforce) {
 
1084
                                                freecon(oldContext);
 
1085
                                                if (close(fdcurr) < 0)
 
1086
                                                        message(MESS_ERROR, "error closing file %s",
 
1087
                                                                        log->files[logNum]);
 
1088
                                                return 1;
 
1089
                                        }
 
1090
                                }
 
1091
                                if (setfscreatecon_raw(oldContext) < 0) {
 
1092
                                        message(MESS_ERROR,
 
1093
                                                "setting file context %s to %s: %s\n",
 
1094
                                                log->files[logNum], oldContext, strerror(errno));
 
1095
                                        if (selinux_enforce) {
 
1096
                                                freecon(oldContext);
 
1097
                                                if (close(fdcurr) < 0)
 
1098
                                                        message(MESS_ERROR, "error closing file %s",
 
1099
                                                                        log->files[logNum]);
 
1100
                                                return 1;
 
1101
                                        }
 
1102
                                }
 
1103
                                message(MESS_DEBUG, "fscreate context set to %s\n",
 
1104
                                                oldContext);
 
1105
                                freecon(oldContext);
 
1106
                        } else {
 
1107
                                if (errno != ENOTSUP) {
 
1108
                                        message(MESS_ERROR, "getting file context %s: %s\n",
 
1109
                                                log->files[logNum], strerror(errno));
 
1110
                                        if (selinux_enforce) {
 
1111
                                                if (close(fdcurr) < 0)
 
1112
                                                        message(MESS_ERROR, "error closing file %s",
 
1113
                                                                        log->files[logNum]);
 
1114
                                                return 1;
 
1115
                                        }
 
1116
                                }
 
1117
                        }
 
1118
                        if (close(fdcurr) < 0)
 
1119
                                message(MESS_ERROR, "error closing file %s",
 
1120
                                                log->files[logNum]);
 
1121
                }
 
1122
#endif
 
1123
                message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum],
 
1124
                    rotNames->finalName);
 
1125
            if (!debug && !hasErrors &&
 
1126
                rename(log->files[logNum], rotNames->finalName)) {
 
1127
                message(MESS_ERROR, "failed to rename %s to %s: %s\n",
 
1128
                        log->files[logNum], rotNames->finalName,
 
1129
                        strerror(errno));
 
1130
            }
 
1131
 
 
1132
            if (!log->rotateCount) {
 
1133
                rotNames->disposeName =
 
1134
                    realloc(rotNames->disposeName,
 
1135
                            strlen(rotNames->dirName) +
 
1136
                            strlen(rotNames->baseName) +
 
1137
                            strlen(log->files[logNum]) + 10);
 
1138
                sprintf(rotNames->disposeName, "%s%s", rotNames->finalName,
 
1139
                        (log->compress_ext
 
1140
                         && (log->flags & LOG_FLAG_COMPRESS)) ? log->
 
1141
                        compress_ext : "");
 
1142
                message(MESS_DEBUG, "disposeName will be %s\n",
 
1143
                        rotNames->disposeName);
 
1144
            }
 
1145
        }
 
1146
 
 
1147
        if (!hasErrors && log->flags & LOG_FLAG_CREATE &&
 
1148
            !(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
 
1149
            if (log->createUid == NO_UID)
 
1150
                sb.st_uid = state->sb.st_uid;
 
1151
            else
 
1152
                sb.st_uid = log->createUid;
 
1153
 
 
1154
            if (log->createGid == NO_GID)
 
1155
                sb.st_gid = state->sb.st_gid;
 
1156
            else
 
1157
                sb.st_gid = log->createGid;
 
1158
 
 
1159
            if (log->createMode == NO_MODE)
 
1160
                sb.st_mode = state->sb.st_mode & 0777;
 
1161
            else
 
1162
                sb.st_mode = log->createMode;
 
1163
 
 
1164
            message(MESS_DEBUG, "creating new %s mode = 0%o uid = %d "
 
1165
                    "gid = %d\n", log->files[logNum], (unsigned int) sb.st_mode,
 
1166
                    (int) sb.st_uid, (int) sb.st_gid);
 
1167
 
 
1168
            if (!debug) {
 
1169
                        fd = createOutputFile(log->files[logNum], O_CREAT | O_RDWR,
 
1170
                                                  &sb);
 
1171
                        if (fd < 0)
 
1172
                                hasErrors = 1;
 
1173
                        else
 
1174
                                close(fd);
 
1175
            }
 
1176
        }
 
1177
#ifdef WITH_SELINUX
 
1178
        if (selinux_enabled) {
 
1179
            setfscreatecon_raw(savedContext);
 
1180
                freecon(savedContext);
 
1181
                savedContext = NULL;
 
1182
        }
 
1183
#endif
 
1184
 
 
1185
        if (!hasErrors
 
1186
            && log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))
 
1187
            hasErrors =
 
1188
                copyTruncate(log->files[logNum], rotNames->finalName,
 
1189
                             &state->sb, log->flags);
 
1190
 
 
1191
    }
 
1192
    return hasErrors;
 
1193
}
 
1194
 
 
1195
int postrotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
 
1196
                        struct logNames *rotNames)
 
1197
{
 
1198
    int hasErrors = 0;
 
1199
 
 
1200
    if (!state->doRotate)
 
1201
        return 0;
 
1202
 
 
1203
    if ((log->flags & LOG_FLAG_COMPRESS) &&
 
1204
        !(log->flags & LOG_FLAG_DELAYCOMPRESS)) {
 
1205
        hasErrors = compressLogFile(rotNames->finalName, log, &state->sb);
 
1206
    }
 
1207
 
 
1208
    if (!hasErrors && log->logAddress) {
 
1209
        char *mailFilename;
 
1210
 
 
1211
        if (log->flags & LOG_FLAG_MAILFIRST)
 
1212
            mailFilename = rotNames->firstRotated;
 
1213
        else
 
1214
            mailFilename = rotNames->disposeName;
 
1215
 
 
1216
        if (mailFilename)
 
1217
            hasErrors =
 
1218
                mailLogWrapper(mailFilename, mailCommand, logNum, log);
 
1219
    }
 
1220
 
 
1221
    if (!hasErrors && rotNames->disposeName)
 
1222
        hasErrors = removeLogFile(rotNames->disposeName, log);
 
1223
 
 
1224
#ifdef WITH_SELINUX
 
1225
        if (selinux_enabled) {
 
1226
                setfscreatecon_raw(prev_context);
 
1227
                freecon(prev_context);
 
1228
                prev_context = NULL;
 
1229
        }
 
1230
#endif
 
1231
    return hasErrors;
 
1232
}
 
1233
 
 
1234
int rotateLogSet(struct logInfo *log, int force)
 
1235
{
 
1236
    int i, j;
 
1237
    int hasErrors = 0;
 
1238
    int logHasErrors[log->numFiles];
 
1239
    int numRotated = 0;
 
1240
    struct logState **state;
 
1241
    struct logNames **rotNames;
 
1242
 
 
1243
    if (force)
 
1244
        log->criterium = ROT_FORCE;
 
1245
 
 
1246
    message(MESS_DEBUG, "\nrotating pattern: %s ", log->pattern);
 
1247
    switch (log->criterium) {
 
1248
    case ROT_DAYS:
 
1249
        message(MESS_DEBUG, "after %d days ", log->threshhold);
 
1250
        break;
 
1251
    case ROT_WEEKLY:
 
1252
        message(MESS_DEBUG, "weekly ");
 
1253
        break;
 
1254
    case ROT_MONTHLY:
 
1255
        message(MESS_DEBUG, "monthly ");
 
1256
        break;
 
1257
    case ROT_YEARLY:
 
1258
        message(MESS_DEBUG, "yearly ");
 
1259
        break;
 
1260
    case ROT_SIZE:
 
1261
        message(MESS_DEBUG, "%d bytes ", log->threshhold);
 
1262
        break;
 
1263
    case ROT_FORCE:
 
1264
        message(MESS_DEBUG, "forced from command line ");
 
1265
        break;
 
1266
    }
 
1267
 
 
1268
    if (log->rotateCount)
 
1269
        message(MESS_DEBUG, "(%d rotations)\n", log->rotateCount);
 
1270
    else
 
1271
        message(MESS_DEBUG, "(no old logs will be kept)\n");
 
1272
 
 
1273
    if (log->oldDir)
 
1274
        message(MESS_DEBUG, "olddir is %s, ", log->oldDir);
 
1275
 
 
1276
    if (log->flags & LOG_FLAG_IFEMPTY)
 
1277
        message(MESS_DEBUG, "empty log files are rotated, ");
 
1278
    else
 
1279
        message(MESS_DEBUG, "empty log files are not rotated, ");
 
1280
 
 
1281
    if (log->minsize) 
 
1282
        message(MESS_DEBUG, "only log files >= %d bytes are rotated, ", log->minsize);
 
1283
 
 
1284
    if (log->logAddress) {
 
1285
        message(MESS_DEBUG, "old logs mailed to %s\n", log->logAddress);
 
1286
    } else {
 
1287
        message(MESS_DEBUG, "old logs are removed\n");
 
1288
    }
 
1289
 
 
1290
    for (i = 0; i < log->numFiles; i++) {
 
1291
        logHasErrors[i] = findNeedRotating(log, i);
 
1292
        hasErrors |= logHasErrors[i];
 
1293
 
 
1294
        /* sure is a lot of findStating going on .. */
 
1295
        if ((findState(log->files[i]))->doRotate)
 
1296
            numRotated++;
 
1297
    }
 
1298
 
 
1299
    if (log->first) {
 
1300
        if (!numRotated) {
 
1301
            message(MESS_DEBUG, "not running first action script, "
 
1302
                    "since no logs will be rotated\n");
 
1303
        } else {
 
1304
            message(MESS_DEBUG, "running first action script\n");
 
1305
            if (runScript(log->pattern, log->first)) {
 
1306
                message(MESS_ERROR, "error running first action script "
 
1307
                        "for %s\n", log->pattern);
 
1308
                hasErrors = 1;
 
1309
                /* finish early, firstaction failed, affects all logs in set */
 
1310
                return hasErrors;
 
1311
            }
 
1312
        }
 
1313
    }
 
1314
 
 
1315
    state = malloc(log->numFiles * sizeof(struct logState *));
 
1316
    rotNames = malloc(log->numFiles * sizeof(struct logNames *));
 
1317
 
 
1318
    for (j = 0;
 
1319
         (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < log->numFiles)
 
1320
         || ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < 1); j++) {
 
1321
 
 
1322
        for (i = j;
 
1323
             ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
 
1324
             || (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
 
1325
            state[i] = findState(log->files[i]);
 
1326
 
 
1327
            rotNames[i] = malloc(sizeof(struct logNames));
 
1328
            memset(rotNames[i], 0, sizeof(struct logNames));
 
1329
 
 
1330
            logHasErrors[i] |=
 
1331
                prerotateSingleLog(log, i, state[i], rotNames[i]);
 
1332
            hasErrors |= logHasErrors[i];
 
1333
        }
 
1334
 
 
1335
        if (log->pre
 
1336
            && (! ( (logHasErrors[j] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
 
1337
                   || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) )) {
 
1338
            if (!numRotated) {
 
1339
                message(MESS_DEBUG, "not running prerotate script, "
 
1340
                        "since no logs will be rotated\n");
 
1341
            } else {
 
1342
                message(MESS_DEBUG, "running prerotate script\n");
 
1343
                if (log->flags & LOG_FLAG_SHAREDSCRIPTS) {
 
1344
                    if (runScriptMultiple(log->pattern, log->pre, rotNames, log->numFiles)) {
 
1345
                        message(MESS_ERROR,
 
1346
                                "error running shared prerotate script "
 
1347
                                "for '%s'\n", log->pattern);
 
1348
                        logHasErrors[j] = 1;
 
1349
                        hasErrors = 1;
 
1350
                    }
 
1351
                } else {
 
1352
                    if (runScriptMultiple(log->pattern, log->pre, &rotNames[j], 1)) {
 
1353
                        message(MESS_ERROR,
 
1354
                                "error running non-shared prerotate script "
 
1355
                                "for %s of '%s'\n", log->files[j], log->pattern);
 
1356
                        logHasErrors[j] = 1;
 
1357
                        hasErrors = 1;
 
1358
                    }
 
1359
                }
 
1360
            }
 
1361
        }
 
1362
 
 
1363
        for (i = j;
 
1364
             ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
 
1365
             || (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
 
1366
            if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
 
1367
                   || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
 
1368
                logHasErrors[i] |=
 
1369
                    rotateSingleLog(log, i, state[i], rotNames[i]);
 
1370
                hasErrors |= logHasErrors[i];
 
1371
            }
 
1372
        }
 
1373
 
 
1374
        if (log->post
 
1375
            && (! ( (logHasErrors[j] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
 
1376
                   || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) )) {
 
1377
            if (!numRotated) {
 
1378
                message(MESS_DEBUG, "not running postrotate script, "
 
1379
                        "since no logs were rotated\n");
 
1380
            } else {
 
1381
                message(MESS_DEBUG, "running postrotate script\n");
 
1382
                if (log->flags & LOG_FLAG_SHAREDSCRIPTS) {
 
1383
                    if (runScriptMultiple(log->pattern, log->post, rotNames, log->numFiles)) {
 
1384
                        message(MESS_ERROR,
 
1385
                                "error running shared postrotate script "
 
1386
                                "for '%s'\n", log->pattern);
 
1387
                        logHasErrors[j] = 1;
 
1388
                        hasErrors = 1;
 
1389
                    }
 
1390
                } else {
 
1391
                    if (runScriptMultiple(log->pattern, log->post, &rotNames[j], 1)) {
 
1392
                        message(MESS_ERROR,
 
1393
                                "error running non-shared postrotate script "
 
1394
                                "for %s of '%s'\n", log->files[j], log->pattern);
 
1395
                        logHasErrors[j] = 1;
 
1396
                        hasErrors = 1;
 
1397
                    }
 
1398
                }
 
1399
            }
 
1400
        }
 
1401
 
 
1402
        for (i = j;
 
1403
             ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
 
1404
             || (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
 
1405
            if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
 
1406
                   || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
 
1407
                logHasErrors[i] |=
 
1408
                    postrotateSingleLog(log, i, state[i], rotNames[i]);
 
1409
                hasErrors |= logHasErrors[i];
 
1410
            }
 
1411
        }
 
1412
 
 
1413
    }
 
1414
 
 
1415
    for (i = 0; i < log->numFiles; i++) {
 
1416
        free(rotNames[i]->firstRotated);
 
1417
        free(rotNames[i]->disposeName);
 
1418
        free(rotNames[i]->finalName);
 
1419
        free(rotNames[i]->dirName);
 
1420
        free(rotNames[i]->baseName);
 
1421
        free(rotNames[i]);
 
1422
    }
 
1423
    free(rotNames);
 
1424
    free(state);
 
1425
 
 
1426
    if (log->last) {
 
1427
        if (!numRotated) {
 
1428
            message(MESS_DEBUG, "not running last action script, "
 
1429
                    "since no logs will be rotated\n");
 
1430
        } else {
 
1431
            message(MESS_DEBUG, "running last action script\n");
 
1432
            if (runScript(log->pattern, log->last)) {
 
1433
                message(MESS_ERROR, "error running last action script "
 
1434
                        "for %s\n", log->pattern);
 
1435
                hasErrors = 1;
 
1436
            }
 
1437
        }
 
1438
    }
 
1439
 
 
1440
    return hasErrors;
 
1441
}
 
1442
 
 
1443
static int writeState(char *stateFilename)
 
1444
{
 
1445
    struct logState *p;
 
1446
    FILE *f;
 
1447
    char *chptr;
 
1448
    int i;
 
1449
 
 
1450
    f = fopen(stateFilename, "w");
 
1451
    if (!f) {
 
1452
        message(MESS_ERROR, "error creating state file %s: %s\n",
 
1453
                stateFilename, strerror(errno));
 
1454
        return 1;
 
1455
    }
 
1456
 
 
1457
    fprintf(f, "logrotate state -- version 2\n");
 
1458
 
 
1459
        for (i = 0; i < hashSize; i++) {
 
1460
                for (p = states[i]->head.lh_first; p != NULL;
 
1461
                                p = p->list.le_next) {
 
1462
                        fputc('"', f);
 
1463
                        for (chptr = p->fn; *chptr; chptr++) {
 
1464
                                switch (*chptr) {
 
1465
                                case '"':
 
1466
                                        fputc('\\', f);
 
1467
                                }
 
1468
 
 
1469
                                fputc(*chptr, f);
 
1470
                        }
 
1471
 
 
1472
                        fputc('"', f);
 
1473
                        fprintf(f, " %d-%d-%d\n",
 
1474
                        p->lastRotated.tm_year + 1900,
 
1475
                        p->lastRotated.tm_mon + 1,
 
1476
                        p->lastRotated.tm_mday);
 
1477
                }
 
1478
        }
 
1479
 
 
1480
        fclose(f);
 
1481
        return 0;
 
1482
}
 
1483
 
 
1484
static int readState(char *stateFilename)
 
1485
{
 
1486
    FILE *f;
 
1487
    char buf[1024];
 
1488
    const char **argv;
 
1489
    int argc;
 
1490
    int year, month, day;
 
1491
    int i;
 
1492
    int line = 0;
 
1493
    int error;
 
1494
    struct logState *st;
 
1495
    time_t lr_time;
 
1496
    struct stat f_stat;
 
1497
 
 
1498
    error = stat(stateFilename, &f_stat);
 
1499
 
 
1500
    if ((error && errno == ENOENT) || (!error && f_stat.st_size == 0)) {
 
1501
        /* create the file before continuing to ensure we have write
 
1502
           access to the file */
 
1503
        f = fopen(stateFilename, "w");
 
1504
        if (!f) {
 
1505
            message(MESS_ERROR, "error creating state file %s: %s\n",
 
1506
                    stateFilename, strerror(errno));
 
1507
            return 1;
 
1508
        }
 
1509
        fprintf(f, "logrotate state -- version 2\n");
 
1510
        fclose(f);
 
1511
        return 0;
 
1512
    } else if (error) {
 
1513
        message(MESS_ERROR, "error stat()ing state file %s: %s\n",
 
1514
                stateFilename, strerror(errno));
 
1515
        return 1;
 
1516
    }
 
1517
 
 
1518
    f = fopen(stateFilename, "r");
 
1519
    if (!f) {
 
1520
        message(MESS_ERROR, "error opening state file %s: %s\n",
 
1521
                stateFilename, strerror(errno));
 
1522
        return 1;
 
1523
    }
 
1524
 
 
1525
    if (!fgets(buf, sizeof(buf) - 1, f)) {
 
1526
        message(MESS_ERROR, "error reading top line of %s\n",
 
1527
                stateFilename);
 
1528
        fclose(f);
 
1529
        return 1;
 
1530
    }
 
1531
 
 
1532
    if (strcmp(buf, "logrotate state -- version 1\n") &&
 
1533
        strcmp(buf, "logrotate state -- version 2\n")) {
 
1534
        fclose(f);
 
1535
        message(MESS_ERROR, "bad top line in state file %s\n",
 
1536
                stateFilename);
 
1537
        return 1;
 
1538
    }
 
1539
 
 
1540
    line++;
 
1541
 
 
1542
    while (fgets(buf, sizeof(buf) - 1, f)) {
 
1543
        argv = NULL;
 
1544
        line++;
 
1545
        i = strlen(buf);
 
1546
        if (buf[i - 1] != '\n') {
 
1547
            message(MESS_ERROR, "line %d too long in state file %s\n",
 
1548
                    line, stateFilename);
 
1549
            fclose(f);
 
1550
            return 1;
 
1551
        }
 
1552
 
 
1553
        buf[i - 1] = '\0';
 
1554
 
 
1555
        if (i == 1)
 
1556
            continue;
 
1557
 
 
1558
        if (poptParseArgvString(buf, &argc, &argv) || (argc != 2) ||
 
1559
            (sscanf(argv[1], "%d-%d-%d", &year, &month, &day) != 3)) {
 
1560
            message(MESS_ERROR, "bad line %d in state file %s\n",
 
1561
                    line, stateFilename);
 
1562
                free(argv);
 
1563
            fclose(f);
 
1564
            return 1;
 
1565
        }
 
1566
 
 
1567
        /* Hack to hide earlier bug */
 
1568
        if ((year != 1900) && (year < 1970 || year > 2100)) {
 
1569
            message(MESS_ERROR,
 
1570
                    "bad year %d for file %s in state file %s\n", year,
 
1571
                    argv[0], stateFilename);
 
1572
            free(argv);
 
1573
            fclose(f);
 
1574
            return 1;
 
1575
        }
 
1576
 
 
1577
        if (month < 1 || month > 12) {
 
1578
            message(MESS_ERROR,
 
1579
                    "bad month %d for file %s in state file %s\n", month,
 
1580
                    argv[0], stateFilename);
 
1581
            free(argv);
 
1582
            fclose(f);
 
1583
            return 1;
 
1584
        }
 
1585
 
 
1586
        /* 0 to hide earlier bug */
 
1587
        if (day < 0 || day > 31) {
 
1588
            message(MESS_ERROR,
 
1589
                    "bad day %d for file %s in state file %s\n", day,
 
1590
                    argv[0], stateFilename);
 
1591
            free(argv);
 
1592
            fclose(f);
 
1593
            return 1;
 
1594
        }
 
1595
 
 
1596
        year -= 1900, month -= 1;
 
1597
 
 
1598
        if ((st = findState(argv[0])) == NULL)
 
1599
                return 1;
 
1600
 
 
1601
        st->lastRotated.tm_mon = month;
 
1602
        st->lastRotated.tm_mday = day;
 
1603
        st->lastRotated.tm_year = year;
 
1604
 
 
1605
        /* fill in the rest of the st->lastRotated fields */
 
1606
        lr_time = mktime(&st->lastRotated);
 
1607
        st->lastRotated = *localtime(&lr_time);
 
1608
 
 
1609
        free(argv);
 
1610
    }
 
1611
 
 
1612
    fclose(f);
 
1613
    return 0;
 
1614
}
 
1615
 
 
1616
int main(int argc, const char **argv)
 
1617
{
 
1618
    int force = 0;
 
1619
    char *stateFile = STATEFILE;
 
1620
    int rc = 0;
 
1621
    int arg;
 
1622
    const char **files;
 
1623
    poptContext optCon;
 
1624
        struct logInfo *log;
 
1625
        int state_file_ok = 1;
 
1626
 
 
1627
    struct poptOption options[] = {
 
1628
        {"debug", 'd', 0, 0, 'd',
 
1629
         "Don't do anything, just test (implies -v)"},
 
1630
        {"force", 'f', 0, &force, 0, "Force file rotation"},
 
1631
        {"mail", 'm', POPT_ARG_STRING, &mailCommand, 0,
 
1632
         "Command to send mail (instead of `" DEFAULT_MAIL_COMMAND "')",
 
1633
         "command"},
 
1634
        {"state", 's', POPT_ARG_STRING, &stateFile, 0,
 
1635
         "Path of state file",
 
1636
         "statefile"},
 
1637
        {"verbose", 'v', 0, 0, 'v', "Display messages during rotation"},
 
1638
        POPT_AUTOHELP {0, 0, 0, 0, 0}
 
1639
    };
 
1640
 
 
1641
    logSetLevel(MESS_NORMAL);
 
1642
    setlocale (LC_ALL, "");
 
1643
 
 
1644
    optCon = poptGetContext("logrotate", argc, argv, options, 0);
 
1645
    poptReadDefaultConfig(optCon, 1);
 
1646
    poptSetOtherOptionHelp(optCon, "[OPTION...] <configfile>");
 
1647
 
 
1648
    while ((arg = poptGetNextOpt(optCon)) >= 0) {
 
1649
        switch (arg) {
 
1650
        case 'd':
 
1651
            debug = 1;
 
1652
            /* fallthrough */
 
1653
        case 'v':
 
1654
            logSetLevel(MESS_DEBUG);
 
1655
            break;
 
1656
        }
 
1657
    }
 
1658
 
 
1659
    if (arg < -1) {
 
1660
        fprintf(stderr, "logrotate: bad argument %s: %s\n",
 
1661
                poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
 
1662
                poptStrerror(rc));
 
1663
        poptFreeContext(optCon);
 
1664
        return 2;
 
1665
    }
 
1666
 
 
1667
    files = poptGetArgs((poptContext) optCon);
 
1668
    if (!files) {
 
1669
        fprintf(stderr, "logrotate " VERSION
 
1670
                " - Copyright (C) 1995-2001 Red Hat, Inc.\n");
 
1671
        fprintf(stderr,
 
1672
                "This may be freely redistributed under the terms of "
 
1673
                "the GNU Public License\n\n");
 
1674
        poptPrintUsage(optCon, stderr, 0);
 
1675
        poptFreeContext(optCon);
 
1676
        exit(1);
 
1677
    }
 
1678
#ifdef WITH_SELINUX
 
1679
    selinux_enabled = (is_selinux_enabled() > 0);
 
1680
    selinux_enforce = security_getenforce();
 
1681
#endif
 
1682
 
 
1683
        TAILQ_INIT(&logs);
 
1684
 
 
1685
        if (readAllConfigPaths(files)) {
 
1686
        poptFreeContext(optCon);
 
1687
        exit(1);
 
1688
    }
 
1689
 
 
1690
    poptFreeContext(optCon);
 
1691
    nowSecs = time(NULL);
 
1692
 
 
1693
        if (allocateHash() != 0)
 
1694
                return 1;
 
1695
 
 
1696
        if (readState(stateFile))
 
1697
        {
 
1698
                state_file_ok = 0;
 
1699
                /* exit(1); */
 
1700
        }
 
1701
 
 
1702
        message(MESS_DEBUG, "\nHandling %d logs\n", numLogs);
 
1703
 
 
1704
        for (log = logs.tqh_first; log != NULL; log = log->list.tqe_next)
 
1705
                rc |= rotateLogSet(log, force);
 
1706
 
 
1707
        if (!debug && state_file_ok)
 
1708
                rc |= writeState(stateFile);
 
1709
 
 
1710
        if (!state_file_ok)
 
1711
        {
 
1712
                message(MESS_ERROR, "could not read state file, "
 
1713
                                "will not attempt to write into it\n");
 
1714
                rc = 1;
 
1715
        }
 
1716
        
 
1717
        return (rc != 0);
 
1718
}