/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// This is a helper for the parentDeathSignal test in SubprocessTest.cpp.
//
// Basically, we create two processes, a parent and a child, and set the
// child to receive SIGUSR1 when the parent exits. We set the child to
// create a file when that happens. The child then kills the parent; the test
// will verify that the file actually gets created, which means that everything
// worked as intended.
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <glog/logging.h>
#include <folly/Conv.h>
#include <folly/Subprocess.h>
#include <folly/portability/GFlags.h>
#include <folly/portability/Unistd.h>
using folly::Subprocess;
DEFINE_bool(child, false, "");
namespace {
constexpr int kSignal = SIGUSR1;
} // namespace
void runChild(const char* file) {
// Block SIGUSR1 so it's queued
sigset_t sigs;
CHECK_ERR(sigemptyset(&sigs));
CHECK_ERR(sigaddset(&sigs, kSignal));
CHECK_ERR(sigprocmask(SIG_BLOCK, &sigs, nullptr));
// Kill the parent, wait for our signal.
CHECK_ERR(kill(getppid(), SIGKILL));
int sig = 0;
CHECK_ERR(sigwait(&sigs, &sig));
CHECK_EQ(sig, kSignal);
// Signal completion by creating the file
CHECK_ERR(creat(file, 0600));
}
[[noreturn]] void runParent(const char* file) {
std::vector<std::string> args{"/proc/self/exe", "--child", file};
Subprocess proc(args, Subprocess::Options().parentDeathSignal(kSignal));
CHECK(proc.poll().running());
// The child will kill us.
for (;;) {
pause();
}
}
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
CHECK_EQ(argc, 2);
if (FLAGS_child) {
runChild(argv[1]);
} else {
runParent(argv[1]);
}
return 0;
}