1 /*
2  * Copyright (c) 2011-2014, Intel Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this
9  * list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation and/or
13  * other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  * may be used to endorse or promote products derived from this software without
17  * specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "TestPlatform.h"
32 #include "FullIo.hpp"
33 
34 #include <iostream>
35 #include <cstdlib>
36 #include <semaphore.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <cerrno>
40 #include <cassert>
41 
42 using namespace std;
43 
44 const int iDefaultPortNumber = 5001;
45 
46 // Starts test-platform in blocking mode
startBlockingTestPlatform(const char * filePath,int portNumber,string & strError)47 static bool startBlockingTestPlatform(const char *filePath, int portNumber, string &strError)
48 {
49 
50     // Init semaphore
51     sem_t sem;
52 
53     sem_init(&sem, false, 0);
54 
55     // Create param mgr
56     CTestPlatform testPlatform(filePath, portNumber, sem);
57 
58     // Start platformmgr
59     if (!testPlatform.load(strError)) {
60 
61         sem_destroy(&sem);
62 
63         return false;
64     }
65 
66     // Block here
67     sem_wait(&sem);
68 
69     sem_destroy(&sem);
70 
71     return true;
72 }
73 
notifyParent(int parentFd,bool success)74 static void notifyParent(int parentFd, bool success)
75 {
76     if (not utility::fullWrite(parentFd, &success, sizeof(success))) {
77         cerr << "Unable to warn parent process of load "
78              << (success ? "success" : "failure") << ": "
79              << strerror(errno) << endl;
80         assert(false);
81     }
82 }
83 
84 // Starts test-platform in daemon mode
startDaemonTestPlatform(const char * filePath,int portNumber,string & strError)85 static bool startDaemonTestPlatform(const char *filePath, int portNumber, string &strError)
86 {
87     // Pipe used for communication between the child and the parent processes
88     int pipefd[2];
89 
90     if (pipe(pipefd) == -1) {
91 
92         strError = "pipe failed";
93         return false;
94     }
95 
96     // Fork the current process:
97     // - Child process is used to start test-platform
98     // - Parent process is killed in order to turn its child into a daemon
99     pid_t pid = fork();
100 
101     if (pid < 0) {
102 
103         strError = "fork failed!";
104         return false;
105 
106     } else if (pid == 0) {
107 
108         // Child process : starts test-platform and notify the parent if it succeeds.
109 
110         // Close read side of the pipe
111         close(pipefd[0]);
112 
113         // Init semaphore
114         sem_t sem;
115 
116         sem_init(&sem, false, 0);
117 
118         // Create param mgr
119         CTestPlatform testPlatform(filePath, portNumber, sem);
120 
121         // Message to send to parent process
122         bool loadSuccess = testPlatform.load(strError);
123 
124         if (!loadSuccess) {
125 
126             cerr << strError << endl;
127 
128             // Notify parent of failure;
129             notifyParent(pipefd[1], false);
130 
131         } else {
132 
133             // Notify parent of success
134             notifyParent(pipefd[1], true);
135 
136             // Block here
137             sem_wait(&sem);
138         }
139         sem_destroy(&sem);
140 
141         return loadSuccess;
142 
143     } else {
144 
145         // Parent process : need to kill it once the child notifies the successs/failure to start
146         // test-platform (this status is used as exit value of the program).
147 
148         // Close write side of the pipe
149         close(pipefd[1]);
150 
151         // Message received from the child process
152         bool msgFromChild = false;
153 
154         if (not utility::fullRead(pipefd[0], &msgFromChild, sizeof(msgFromChild))) {
155             strError = "Read pipe failed";
156             return false;
157         }
158 
159         // return success/failure in exit status
160         return msgFromChild;
161     }
162 }
163 
showUsage()164 static void showUsage()
165 {
166     cerr << "test-platform [-dh] <file path> [port number, default "
167          << iDefaultPortNumber << "]" << endl;
168 }
169 
showInvalidUsage()170 static void showInvalidUsage()
171 {
172     cerr << "Invalid arguments: ";
173     showUsage();
174 }
175 
showHelp()176 static void showHelp()
177 {
178     showUsage();
179     cerr << "<file path> must be a valid .xml file, oftenly ParameterFrameworkConfiguration.xml" << endl;
180     cerr << "Arguments:" << endl
181         << "    -d  starts as a deamon" << endl
182         << "    -h  display this help and exit" << endl;
183 }
184 
main(int argc,char * argv[])185 int main(int argc, char *argv[])
186 {
187     // Option found by call to getopt()
188     int opt;
189 
190     // Port number to be used by test-platform
191     int portNumber;
192 
193     // Daemon flag
194     bool isDaemon = false;
195 
196     // Index of the <file path> argument in the arguments list provided
197     int indexFilePath = 1;
198 
199     // Handle the -d option
200     while ((opt = getopt(argc, argv, "dh")) != -1) {
201         switch (opt) {
202         case 'd':
203             isDaemon = true;
204             indexFilePath = 2;
205             break;
206         case 'h':
207             showHelp();
208             return 0;
209         default:
210             showInvalidUsage();
211             return -1;
212         }
213     }
214 
215     // Check the number of arguments
216     if ((argc < indexFilePath + 1) || (argc > indexFilePath + 2)) {
217 
218         showInvalidUsage();
219         return -1;
220     }
221 
222     char *filePath = argv[indexFilePath];
223     portNumber = argc > indexFilePath + 1 ? atoi(argv[indexFilePath + 1]) : iDefaultPortNumber;
224 
225     // Choose either blocking or daemon test-platform
226     bool startError;
227     string strError;
228 
229     if (isDaemon) {
230 
231         startError = startDaemonTestPlatform(filePath, portNumber, strError);
232     } else {
233 
234         startError = startBlockingTestPlatform(filePath, portNumber, strError);
235     }
236 
237     if (!startError) {
238 
239         cerr << "Test-platform error:" << strError.c_str() << endl;
240         return -1;
241     }
242     return 0;
243 }
244