daily コマンド - 1日に1度のコマンド実行

定期あるいは定時に何かをしなければならないという状況は至るところにあります が、Unix の場合は、次の二つのアプローチが普通です。

1) cron による起動
2) /erc/rc からの起動

cron は常駐プログラムが crontab という予定表に基づいて、分単位で指定された コマンドを実行する仕掛ですから、かなり細かい制御ができますが、これはシステ ムが稼働していなければだめ。一方、/etc/rc からの起動は、システムの起動時に 確実に一度だけ実行することができるという特徴があって、連続運転しないマシン の calendar (予定チェック) の起動などには最適です。

しかし、一日に何度も起動される可能性のあるシステムで、一日に一度だけ実行し たいという場合はどうしたら良いでしょうか。cron でやろうとしても、その時間 に動いていなければうまくゆきません。

つまり、/etc/rc から実行して、しかも1日に2度以上は動かない仕組みが必要で すから、少なくとも、どこかに実行した日付と時刻を記憶しなければなりません。 すぐに思いつくのはファイルとして記憶することですが、実は、もっと面白い方法 があります。

コマンドそのもに記憶させてしまうのです!

これにもいくつかの方法がありますが、コマンドの変更日付を実行した日付に変え てしまうのがスマートで、昔、Minix の calandar(1) コマンドとして公開し、世 界の人々に使っていただけるようになりました。

ここでは、より汎用性をもたせた「daily」というコマンドをご紹介しょう。この コマンドは、例えば、

  daily command
を実行すると、指定された command の変更日付とシステム日付を比較して、同じ 日ならなにもせず、別の日なら command を実行してから、command の日付を実行 したときの日付に変更してしまいます!

こうして、実行した日付情報を保存するための特別な資源をなんら消費すること なく、特定のコマンドを1日に1度だけ実行することを保証することができます。

/*
 *  daily - execute a command only once a day.
 *
 *  (C) 1990 Saeko & Kouichi Hirabayashi
 */

#include 
#include 
#include 
#include 
#include 
#include 

void usage(), error(), onint();

int vflg;
char *cmd = "daily";
char buf[BUFSIZ];

main(argc, argv) char **argv;
{
  char *s;

  signal(SIGINT, onint);
  while (--argc > 0 && (*++argv)[0] == '-')
	for (s = argv[0]+1; *s != NULL; s++)
		switch (*s) {
		case 'v': vflg++; break;
		default: usage();
		}
  if (argc < 1)
	usage();
  daily(argv[0], argc, argv);
  exit(0);
}

void
usage()
{
  fprintf(stderr, "Usage: %s command [ arg .. ]\n", cmd);
  exit(1);
}

daily(file, argc, argv) char *file, **argv;
{
  int i;
  long clock, time();
  struct tm *tm, *localtime();
  struct stat stbuf;
  char newdate[8], olddate[8];

  time(&clock);
  tm = localtime(&clock);
  sprintf(newdate, "%02d%02d%02d", tm->tm_year, tm->tm_mon+1, tm->tm_mday);

  if (stat(file, &stbuf) == -1)
	error("can't open %s", file);
  tm = localtime(&stbuf.st_mtime);
  clock = stbuf.st_mtime;
  tm = localtime(&clock);
  sprintf(olddate, "%02d%02d%02d", tm->tm_year, tm->tm_mon+1, tm->tm_mday);

  if (strcmp(newdate, olddate) != 0) {
	if (systemv(argv) == -1)
		error("execvp() failed", NULL);
	if (vflg)
		fprintf(stderr, "\"%s\" was executed.\n", buf);
	utime(file, NULL);
  }
}

void
onint(i)
{
  exit(0);
}

void
error(s, t) char *s, *t;
{
  fprintf(stderr, "%s: ", cmd);
  fprintf(stderr, s, t);
  fprintf(stderr, "\n");
  exit(1);
}

systemv(argv) char **argv;
{
  int status, pid, w;
  register void (*istat)(), (*qstat)();

  if ((pid = fork()) == 0) {
	execvp(argv[0], argv);
	_exit(127);
  }
  istat = signal(SIGINT, SIG_IGN);
  qstat = signal(SIGQUIT, SIG_IGN);
  while ((w = wait(&status)) != pid && w != -1)
	;
  if (w == -1)
	status = -1;
  signal(SIGINT, istat);
  signal(SIGQUIT, qstat);
  return status;
}

注: /etc/rc の「rc」は、システムが立ち上がったときに1度だけ sh によって 実行されるスクリプトで、「Run Command」からの命名だそうです。

平林浩一