実行されないcron設定ファイルを検証してみた

systemdにてtimerユニットが使えるようになりましたがcronもまだまだ現役ではないでしょうか。
実は`/etc/cron.d`以下などで実行されないcron設定ファイルがあり
こちらを検証してみます。

##該当コード

以下が本家のソースコードです。

https://github.com/cronie-crond/cronie/blob/master/src/database.c

```c:src/database.c
static int not_a_crontab(DIR_T * dp) {
        size_t len;

        /* avoid file names beginning with ".".  this is good
         * because we would otherwise waste two guaranteed calls
         * to getpwnam() for . and .., and there shouldn't be
         * hidden files in here anyway
         */
        if (dp->d_name[0] == '.')
                return (1);

        /* ignore files starting with # and ending with ~ */
        if (dp->d_name[0] == '#')
                return (1);

        /* ignore CRON_HOSTNAME file (in case doesn't start with ".")  */
        if (0 == strcmp(dp->d_name, CRON_HOSTNAME))
                return(1);

        len = strlen(dp->d_name);

        if (len >= NAME_MAX || len == 0)
                return (1);

        if (dp->d_name[len - 1] == '~')
                return (1);

        if ((len > 8) && (strncmp(dp->d_name + len - 8, ".rpmsave", 8) == 0))
                return (1);
        if ((len > 8) && (strncmp(dp->d_name + len - 8, ".rpmorig", 8) == 0))
                return (1);
        if ((len > 7) && (strncmp(dp->d_name + len - 7, ".rpmnew", 7) == 0))
                return (1);

        return (0);
}
```

##解説1

```c
        /* avoid file names beginning with ".".  this is good
         * because we would otherwise waste two guaranteed calls
         * to getpwnam() for . and .., and there shouldn't be
         * hidden files in here anyway
         */
        if (dp->d_name[0] == '.')
                return (1);
```

DeepLで翻訳すると以下の通りです。
>.で始まるファイル名は避けてください。そうしないと、.と...のためにgetpwnam()を2回も保証付きで呼び出すことになるので、これは良いことです。また、ここには隠しファイルはないはずです。

`.`には現在のディレクトリの位置を示す意味があり、`..`は上位のディレクトリを示します。また`.`で始まるファイルはLinux上で隠しファイルを意味します。
viなどでファイルの編集中は`.<filename>.swp`が作成されますが、ここで無視されています。

##解説2

```c
        /* ignore files starting with # and ending with ~ */
        if (dp->d_name[0] == '#')
                return (1);
```

> \#で始まり、~で終わるファイルを無視する

`#`はファイルの中身ではなくファイル名のことです。
末尾`~`は後で出てきます。


##解説4

```c
        /* ignore CRON_HOSTNAME file (in case doesn't start with ".")  */
        if (0 == strcmp(dp->d_name, CRON_HOSTNAME))
                return(1);
```

>CRON_HOSTNAMEファイルを無視する（". "で始まっていない場合）。

`CRON_HOSTNAME`についてはMakefileとdatabase.cに説明があります。


```c:src/Makefile.in
                        /* CRON_HOSTNAME is file in SPOOL_DIR which, if it \
                         * exists, and does not just contain a line matching \
                         * the name returned by gethostname(), causes all \
                         * crontabs in SPOOL_DIR to be ignored.  This is \
                         * intended to be used when clustering hosts sharing \
                         * one NFS-mounted SPOOL_DIR, and where only one host \
                         * should use the crontab files here at any one time. \
                         */ \
        #define CRON_HOSTNAME   ".cron.hostname" \
```

>CRON_HOSTNAME は、SPOOL_DIR にあるファイルで、もしそれが存在し、かつ gethostname() が返す名前と一致する行がない場合は、SPOOL_DIR にあるすべての crontab を無視させます。 これは、1つのNFSマウントされたSPOOL_DIRを共有しているホストをクラスタリングする際に、常に1つのホストだけがここにあるcrontabファイルを使用する場合に使用することを目的としています。

```c:src/database.c
	/* to allow option of NFS-mounting SPOOL_DIR on a cluster of
	 * hosts and to only use crontabs here on any one host at a
	 * time, allow for existence of a CRON_HOSTNAME file, and if
	 * it doesn't exist, or exists but does not contain this
	 * host's hostname, then skip the crontabs.
	 *
	 * Note: for safety's sake, no CRON_HOSTNAME file means skip,
	 * otherwise its accidental deletion could result in multiple
	 * cluster hosts running the same cron jobs, which is
	 * potentially worse.
	 */
```

>SPOOL_DIRをクラスター上でNFSマウントし、同時に1つのホストでしかcrontabsを使用しないというオプションを可能にしました。
また、CRON_HOSTNAMEファイルの存在を確認し、存在しないか、存在してもこのホストのホスト名が含まれていない場合、crontabsをスキップします。
ホストのホスト名が含まれていない場合は、crontabsをスキップします。
注意：安全のため、CRON_HOSTNAMEファイルがない場合はスキップします。そうしないと、誤って削除された場合、複数のクラスタホストが同じcronジョブを実行することになり、最悪の事態になる可能性があります。


NFSなどで各サーバーのディレクトリを共有している場合にホスト名指定でそのホストだけ実行する仕組みだそうです。

##解説5

```c
        if (len >= NAME_MAX || len == 0)
                return (1);
```

自環境では`/usr/include/linux/limits.h`で255でした。255以上のファイル名は無視されます。


##解説6

```c
        if (dp->d_name[len - 1] == '~')
                return (1);
```

末尾が`~`のファイルは無視されます。
vimのバックアップファイルで出てきます。

##解説7


```c
        if ((len > 8) && (strncmp(dp->d_name + len - 8, ".rpmsave", 8) == 0))
                return (1);
        if ((len > 8) && (strncmp(dp->d_name + len - 8, ".rpmorig", 8) == 0))
                return (1);
        if ((len > 7) && (strncmp(dp->d_name + len - 7, ".rpmnew", 7) == 0))
                return (1);
```

cronを利用するソフトウェアなどで`/etc/cron.d`にファイルを置く場合があります。
redhat系OSのパッケージ管理rpmの設定で`%config`を指定している場合、ソフトウェアの削除などでこの拡張子で残される場合があります。この残骸を無視するという仕組みです。

##検証

`/etc/cron.d`以下に以下のファイルを置いてみました。

```
# ls -1aN
01_nomal
.02_startswitch_dot
#03_startswtch_sharp
04_childa~
05_.rpmsave
06_.rpmorig
07_.rpmnew
```

該当のファイル名は無視されました。

```
Dec 13 00:30:01 crontest CROND[465301]: (root) CMD (logger 01_nomal )
Dec 13 00:30:01 crontest root[465301]: 01_nomal
```