/* * Copyright (c) 2021 Huawei Device Co., Ltd. * 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. */ #include #include #include #include #include #include "log.h" #include "utils.h" #include "SignalTest.h" using namespace testing::ext; // static member must init before use. int IpcSignalTest::mReceivedSignal = 0; int IpcSignalTest::mShmid = 0; siginfo_t IpcSignalTest::mSiginfo; // special signal hander for function 'abort' void IpcSignalTest::SigAbortHandler(int signum) { LOG("hander: recv a signal: %d", signum); int *shared = static_cast(shmat(mShmid, nullptr, 0)); if (shared == reinterpret_cast(-1)) { LOG("SigAbortHandler: shmat fail, errno = %d", errno); } else { LOG("shared: %p", shared); *shared = signum; if (shmdt(shared) == -1) { LOG("SigAbortHandler: shmdt errno = %d", errno); } } } // special signal handler for function 'sigaction' void IpcSignalTest::SigactionHandler(int signum, siginfo_t* si, void* ucontext) { LOG("hander recv a signal: %s(%d)", ALL_SIGNALS[signum].signame, signum); mReceivedSignal = signum; // siginfo_t para is not supported yet } // general signal hander. note: not thread-safe void IpcSignalTest::SignalHandler(int signum) { LOG("hander recv a signal: %s(%d)", ALL_SIGNALS[signum].signame, signum); mReceivedSignal = signum; } SignalNameAction const ALL_SIGNALS[MAX_SIGNAL] = { {"NA", "Unknown signal", TERMINATE}, {"SIGHUP", "Hangup", TERMINATE}, // 1 {"SIGINT", "Interrupt", TERMINATE}, {"SIGQUIT", "Quit", COREDUMP}, {"SIGILL", "Illegal instruction", COREDUMP}, {"SIGTRAP", "Trace/breakpoint trap", COREDUMP}, {"SIGABRT", "Aborted", COREDUMP}, // alias: SIGIOT {"SIGBUS", "Bus error", COREDUMP}, {"SIGFPE", "Arithmetic exception", COREDUMP}, {"SIGKILL", "Killed", TERMINATE}, {"SIGUSR1", "User defined signal 1", TERMINATE}, // 10 {"SIGSEGV", "Segmentation fault", COREDUMP}, {"SIGUSR2", "User defined signal 2", TERMINATE}, {"SIGPIPE", "Broken pipe", TERMINATE}, {"SIGALRM", "Alarm clock", TERMINATE}, {"SIGTERM", "Terminated", TERMINATE}, {"SIGSTKFLT", "Stack fault", TERMINATE}, {"SIGCHLD", "Child process status", IGNORE}, // alias: SIGCLD {"SIGCONT", "Continued", CONTINUE}, {"SIGSTOP", "Stopped (signal)", STOP}, {"SIGTSTP", "Stopped", STOP}, // 20 {"SIGTTIN", "Stopped (tty input)", STOP}, {"SIGTTOU", "Stopped (tty output)", STOP}, {"SIGURG", "Urgent I/O condition", IGNORE}, {"SIGXCPU", "CPU time limit exceeded", COREDUMP}, {"SIGXFSZ", "File size limit exceeded", COREDUMP}, {"SIGVTALRM", "Virtual timer expired", TERMINATE}, {"SIGPROF", "Profiling timer expired", TERMINATE}, {"SIGWINCH", "Window changed", IGNORE}, {"SIGIO", "I/O possible", TERMINATE}, // alias: SIGPOLL {"SIGPWR", "Power failure", TERMINATE}, // alias: SIGINFO, 30 }; void IpcSignalTest::DefaultActionTest(const int signum, const bool expectStop, const bool coredump) { pid_t pid = fork(); ASSERT_TRUE(pid >= 0) << "======== Fork Error! ========="; if (pid > 0) { // parent Msleep(20); LOGD("befor kill"); kill(pid, signum); if (!expectStop) { Msleep(20); AssertProcAlive(pid); Msleep(20); WaitProcExitedOK(pid); } else { WaitProcKilled(pid, signum); if (coredump) { // check codedump, liteos not support yet } } } else { // child LOGD("child start"); KeepRun(100); if (expectStop) { LOG("Child should has been Terminated, but still alive."); exit(1); } exit(0); } } void IpcSignalTest::SendAndRecvTest(const int signum) { pid_t pid1, pid2; bool useBrother = GetRandom(100) % 2; // if use brother to send the signal pid1 = fork(); ASSERT_TRUE(pid1 >= 0) << "======== Fork1 Error! ========="; if (pid1 > 0) { // parent LOGD("in parent..."); if (useBrother) { pid2 = fork(); ASSERT_TRUE(pid2 >= 0) << "======== Fork2 Error! ========="; if (pid2 == 0) { // child 2 Msleep(50); kill(pid1, signum); exit(0); } // parent Msleep(20); LOG("check if child2 exited OK"); WaitProcExitedOK(pid2); } else { Msleep(100); kill(pid1, signum); } AssertProcAlive(pid1); Msleep(20); // child should exited now LOG("check if child exited OK"); WaitProcExitedOK(pid1); } else { // child 1, the receiver LOGD("in child, pid=%d", getpid()); handler_type rt = signal(signum, SignalHandler); if (rt == SIG_ERR) { LOG("set %d signal handler failed, errno=%d", signum, errno); exit(1); } Msleep(150); if (mReceivedSignal != signum) { LOG("SignalHandler check fail, expected=%d, received=%d", signum, mReceivedSignal); exit(1); } LOGD("child sleeping...."); Msleep(20); LOGD("child exit 0...."); exit(0); } } int IpcSignalTest::CheckSigString(const char* outfile, const char* expectStr) { const int bufLen = 64; char buffer[bufLen]; FILE *fp = fopen(outfile, "rb"); size_t bytes = fread(buffer, 1, bufLen, fp); buffer[bytes] = 0; LOGD("%d bytes read from logfile:%s", bytes, buffer); fclose(fp); size_t expectLen = strlen(expectStr); LOGD("expect string len = %d", expectLen); if (bytes != (expectLen + 1)) { LOG("bytes number read from stderr file error, expect:%d, actual:%d", expectLen + 1, bytes); return 1; } return strncmp(expectStr, buffer, expectLen); } void IpcSignalTest::SignalFailTest(int signum, handler_type h, int expectErrno) { errno = 0; handler_type rt = signal(signum, h); ASSERT_EQ(rt, SIG_ERR) << "signal error for " << signum; ASSERT_EQ(errno, expectErrno) << "signal error for " << signum; } void IpcSignalTest::SigpendingFailTest(sigset_t* pset) { errno = 0; int rt = sigpending(pset); ASSERT_EQ(rt, -1); ASSERT_EQ(errno, EFAULT); } void IpcSignalTest::SigtimedwaitFailTest(const sigset_t *set, siginfo_t* info, const timespec* timeout, int expectErrno) { errno = 0; int rt = sigtimedwait(set, info, timeout); ASSERT_EQ(rt, -1); ASSERT_EQ(errno, expectErrno); } // thread function for testcase: 'testPthreadkill' void* IpcSignalTest::ThreadFunc1(void* arg) { int sigNo = (int)((uintptr_t)arg); signal(sigNo, SignalHandler); Msleep(60); return nullptr; } // thread function for testcase: 'testPthreadkillMain' void* IpcSignalTest::ThreadFunc2(void* arg) { pthread_t* tid = (pthread_t*)arg; int ret = pthread_kill(*tid, SIGINT); if (ret != 0) { LOG("pthread_kill failed, errno=%d", ret); return (void*)1; } LOG("pthread_kill send signal(%d) ok", SIGINT); return nullptr; } // thread function for testcase: 'testPthreadSigmask' void* IpcSignalTest::ThreadFuncForSigmask1(void* arg) { int rt; int type = (int)((uintptr_t)arg); if (type == 1) { signal(SIGINT, SignalHandler); } LOG("block SIGINT"); sigset_t sigmask, oldmask; sigemptyset(&sigmask); sigemptyset(&oldmask); sigaddset(&sigmask, SIGINT); sigaddset(&sigmask, SIGUSR1); rt = pthread_sigmask(SIG_BLOCK, &sigmask, &oldmask); EXPECT_EQ(rt, 0); raise(SIGINT); EXPECT_EQ(mReceivedSignal, 0) << "SignalHandler check fail, SIGINT should has blocked!"; LOG("unblock SIGINT"); sigemptyset(&sigmask); sigaddset(&sigmask, SIGINT); rt = pthread_sigmask(SIG_UNBLOCK, &sigmask, nullptr); EXPECT_EQ(rt, 0); LOG("check the new block set"); sigemptyset(&oldmask); pthread_sigmask(SIG_UNBLOCK, nullptr, &oldmask); EXPECT_EQ(sigismember(&oldmask, SIGINT), 0) << "SIGINT should has deleted from block set!"; EXPECT_EQ(sigismember(&oldmask, SIGUSR1), 1) << "SIGUSR1 should still in block set!"; EXPECT_EQ(mReceivedSignal, SIGINT) << "SignalHandler check fail, SIGINT should deliver after unblock!"; return nullptr; } // thread function for testcase: 'testPthreadSigmaskInherit' void* IpcSignalTest::ThreadFuncForSigmask2(void* arg) { sigset_t sigmask, oldmask; sigemptyset(&sigmask); sigemptyset(&oldmask); LOG("SubThread: check the block set is inherited"); pthread_sigmask(SIG_BLOCK, nullptr, &oldmask); if (!sigismember(&oldmask, SIGUSR1)) { LOG("initial block set should include SIGUSR1!"); return (void*)1; } LOG("SubThread: add SIGINT to block set"); sigaddset(&sigmask, SIGINT); int rt = pthread_sigmask(SIG_BLOCK, &sigmask, &oldmask); EXPECT_EQ(rt, 0); LOG("SubThread: check the new block set"); sigemptyset(&oldmask); pthread_sigmask(SIG_UNBLOCK, nullptr, &oldmask); if (!sigismember(&oldmask, SIGINT)) { LOG("failed to add SIGINT to block set!"); return (void*)1; } if (!sigismember(&oldmask, SIGUSR1)) { LOG("SIGUSR1 should still in block set!"); return (void*)1; } return nullptr; }