It seems to me this is straightforward to do using a POSIX shared memory object:
POSIX shared memory objects have kernel persistence: a shared memory object will exist until the system is shut down, or until all processes have unmapped the object and it has been deleted with shm_unlink
Whenever your program launches it can shm_open
a new object with some consistent name and set the owner to root
. The object needn't contain any particular value. POSIX requires that all shared memory objects persist until reboot unless manually destroyed (which only its owner or creator can do...which in this case is the root user).
Whenever your program launches it first checks if such a shared memory object already exists having root as the owner. Since only root could create such an object, and only root or a reboot could destroy it, you can thus know for certain whether your program has been launched since the last reboot, save the only possible circumvention being the root user invoking shm_unlink
on the object manually.
I wrote a test-and-set function below that should do exactly what you need. And it works except for the ownership setting/detection: for some unknown reason both calls to shmctl
are failing on my system, saying "invalid argument". The man
page for shmctl
says the EINVAL
error indicates either an invalid memory object identifier or an invalid command. But the IPC_SET
and IPC_STAT
commands are certainly valid, and you can watch the program's output to see the valid object identifier that is being created and/or opened each time.
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
int rebooted_test_and_set() {
int err;
int rebooted;
struct shmid_ds shmst;
// create object if nonexistent, returning failure if already exists
int shmid = shm_open("/bootcheck", O_CREAT | O_EXCL);
if (shmid != -1) {
fprintf(stderr, "bootcheck object did not exist, so created: %d\n", shmid);
// object did not exist, so system has been rebooted
rebooted = 1;
// set owner to root, and no permissions for anyone
shmst.shm_perm.uid = 0;
shmst.shm_perm.gid = 0;
shmst.shm_perm.mode = 0;
if ((err = shmctl(shmid, IPC_SET, &shmst)) == -1) {
perror("shmctl: shmctl failed to set owner and permissions for bootcheck object");
exit(1);
}
} else {
// object already exists, so reopen with read access and verify that the owner is root
shmid = shm_open("/bootcheck", O_RDONLY);
if (shmid == -1) {
perror("shm_open: failed, perhaps due to insufficient privileges");
exit(1);
}
fprintf(stderr, "bootcheck object (%d) exists, so checking ownership\n", shmid);
if ((err = shmctl(shmid, IPC_STAT, &shmst)) == -1) {
perror("shmctl: shmctl failed");
exit(1);
}
if (shmst.shm_perm.uid == 0) {
// yes, the bootcheck owner is root,
// so we are confident the system has NOT been rebooted since last launch
rebooted = 0;
} else {
// uh oh, looks like someone created the object illegitimately.
// since that is only possible if the root-owned object did not exist,
// therefore we know that it never did exist since the last reboot
rebooted = 1;
}
}
return rebooted;
}
// for debugging purposes ONLY, so I don't have to keep rebooting to clear the object:
void rebooted_clear() {
if (shm_unlink("/bootcheck") == -1) {
perror("shm_unlink: failed, probably due to insufficient privileges or object nonexistent");
exit(1);
}
}
int main() {
int rebooted = rebooted_test_and_set();
printf("rebooted since last launch: %d\n", rebooted);
return 0;
}
If anyone has any clues, I'm stumped. Some information and examples for POSIX shared memory here.