1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 #include <functional> 19 #include <sys/wait.h> 20 #include <unistd.h> 21 22 namespace android::audio_utils { 23 24 /** 25 * RunRemote run a method in a remote process. 26 * 27 * This can be used for lightweight remote process testing. 28 * This can be used for implementing microservices. 29 */ 30 class RunRemote { 31 public: 32 /** Runs the method without a communication pipe. */ 33 explicit RunRemote(std::function<void()>&& runnable, bool detached = false) 34 : mRunnable{std::move(runnable)} 35 , mDetached(detached) {} 36 37 /** Runs the method with a reference back to the RunRemote for communication */ 38 explicit RunRemote( 39 std::function<void(RunRemote& runRemote)>&& runnable, bool detached = false) 40 : mRunnableExt{std::move(runnable)} 41 , mDetached(detached) {} 42 ~RunRemote()43 ~RunRemote() { 44 if (!mDetached) stop(); 45 } 46 run()47 bool run() { 48 int fd1[2] = {-1, -1}; 49 int fd2[2] = {-1, -1}; 50 if (mRunnableExt) { 51 if (pipe(fd1) != 0) return false; 52 if (pipe(fd2) != 0) { 53 close(fd1[0]); 54 close(fd1[1]); 55 return false; 56 } 57 } 58 pid_t ret = fork(); 59 if (ret < 0) { 60 close(fd1[0]); 61 close(fd1[1]); 62 close(fd2[0]); 63 close(fd2[1]); 64 return false; 65 } else if (ret == 0) { 66 // child 67 if (mRunnableExt) { 68 mInFd = fd2[0]; 69 close(fd2[1]); 70 mOutFd = fd1[1]; 71 close(fd1[0]); 72 mRunnableExt(*this); 73 } else { 74 mRunnable(); 75 } 76 // let the system reclaim handles. 77 exit(EXIT_SUCCESS); 78 } else { 79 // parent 80 if (mRunnableExt) { 81 mInFd = fd1[0]; 82 close(fd1[1]); 83 mOutFd = fd2[1]; 84 close(fd2[0]); 85 } 86 mChildPid = ret; 87 return true; 88 } 89 } 90 stop()91 bool stop() { 92 if (mInFd != -1) { 93 close(mInFd); 94 mInFd = -1; 95 } 96 if (mOutFd != -1) { 97 close(mOutFd); 98 mOutFd = -1; 99 } 100 if (!mDetached && mChildPid > 0) { 101 if (kill(mChildPid, SIGTERM)) { 102 return false; 103 } 104 int status = 0; 105 if (TEMP_FAILURE_RETRY(waitpid(mChildPid, &status, 0)) != mChildPid) { 106 return false; 107 } 108 mChildPid = 0; 109 return WIFEXITED(status) && WEXITSTATUS(status) == 0; 110 } 111 return true; 112 } 113 114 /** waits for a char from the remote process. */ getc()115 int getc() { 116 unsigned char c; 117 // EOF returns 0 (this is a blocking read), -1 on error. 118 if (read(mInFd, &c, 1) != 1) return -1; 119 return c; 120 } 121 122 /** sends a char to the remote process. */ putc(int c)123 int putc(int c) { 124 while (true) { 125 int ret = write(mOutFd, &c, 1); // LE. 126 if (ret == 1) return 1; 127 if (ret < 0) return -1; 128 // on 0, retry. 129 } 130 } 131 132 private: 133 const std::function<void()> mRunnable; 134 const std::function<void(RunRemote& runRemote)> mRunnableExt; 135 const bool mDetached; 136 137 // These values are effectively const after calling run(), which does the fork, 138 // until stop() is called, which terminates the remote process. run() is assumed 139 // called shortly after construction, and not asynchronously with a reader or writer. 140 141 pid_t mChildPid = 0; 142 int mOutFd = -1; 143 int mInFd =1; 144 }; 145 146 } // namespace android::audio_utils 147