1 /*
2  * Copyright (C) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <cstdio>
16 #include <cstring>
17 #include <random>
18 #include <sstream>
19 #include <thread>
20 #include <fcntl.h>
21 #ifdef _WIN32
22 #include <synchapi.h>
23 #include <errhandlingapi.h>
24 #include <winerror.h>
25 #include <handleapi.h>
26 #include <windows.h>
27 #else
28 #include <pthread.h>
29 #endif
30 #include <unistd.h>
31 #include <securec.h>
32 #include "log.h"
33 
34 constexpr uint16_t BUF_SIZE_TINY = 64;
35 constexpr uint16_t BUF_SIZE_DEFAULT = 1024;
36 constexpr int ERR_API_FAIL = -13000;
37 constexpr int ERR_BUF_OVERFLOW = -9998;
38 constexpr int ERR_FILE_OPEN = -11000;
39 constexpr int ERR_FILE_WRITE = -10996;
40 constexpr int ERR_FILE_STAT = -10997;
41 
42 #ifdef _WIN32
43 constexpr uint16_t BUF_SIZE_SMALL = 256;
44 #endif
45 
46 extern "C" {
GetPathSep()47     static char GetPathSep()
48     {
49 #ifdef _WIN32
50         const char sep = '\\';
51 #else
52         const char sep = '/';
53 #endif
54         return sep;
55     }
56 
57 #ifdef _WIN32
58     // return value: <0 error; 0 can start new server instance; >0 server already exists
ProgramMutex(const char* procname, bool checkOrNew, const char* tmpDir)59     __declspec(dllexport) int ProgramMutex(const char* procname, bool checkOrNew, const char* tmpDir)
60     {
61         char bufPath[BUF_SIZE_DEFAULT] = "";
62         if (tmpDir == nullptr) {
63             return ERR_API_FAIL;
64         }
65 
66         if (snprintf_s(bufPath, sizeof(bufPath), sizeof(bufPath) - 1, "%s%c.%s.pid",
67                        tmpDir, GetPathSep(), procname) < 0) {
68             return ERR_BUF_OVERFLOW;
69         }
70 
71         char pidBuf[BUF_SIZE_TINY] = "";
72         int pid = static_cast<int>(getpid());
73         if (snprintf_s(pidBuf, sizeof(pidBuf), sizeof(pidBuf) - 1, "%d", pid) < 0) {
74             return ERR_BUF_OVERFLOW;
75         }
76 
77         FILE *fp = fopen(bufPath, "a+");
78         if (fp == nullptr) {
79             return ERR_FILE_OPEN;
80         }
81 
82         char buf[BUF_SIZE_DEFAULT] = "";
83         if (snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "Global\\%s", procname) < 0) {
84             fclose(fp);
85             return ERR_BUF_OVERFLOW;
86         }
87         HANDLE hMutex = CreateMutex(nullptr, FALSE, buf);
88         DWORD dwError = GetLastError();
89         if (ERROR_ALREADY_EXISTS == dwError || ERROR_ACCESS_DENIED == dwError) {
90             fclose(fp);
91             return 1;
92         }
93         if (checkOrNew) {
94             CloseHandle(hMutex);
95         }
96 
97         int fd = fileno(fp);
98         int rc = ftruncate(fd, 0);
99         if (rc == -1) {
100             fclose(fp);
101             return ERR_FILE_STAT;
102         }
103         rc = fwrite(&pidBuf, sizeof(char), strlen(pidBuf), fp);
104         if (rc == -1) {
105             fclose(fp);
106             return ERR_FILE_WRITE;
107         }
108 
109         if (checkOrNew) {
110             fclose(fp);
111         }
112 
113         return 0;
114     }
115 
116 #else
117     // return value: <0 error; 0 can start new server instance; >0 server already exists
ProgramMutex(const char* procname, bool checkOrNew, const char* tmpDir)118     int ProgramMutex(const char* procname, bool checkOrNew, const char* tmpDir)
119     {
120         char bufPath[BUF_SIZE_DEFAULT] = "";
121         if (tmpDir == nullptr) {
122             return ERR_API_FAIL;
123         }
124 
125         if (snprintf_s(bufPath, sizeof(bufPath), sizeof(bufPath) - 1, "%s%c.%s.pid",
126                        tmpDir, GetPathSep(), procname) < 0) {
127             return ERR_BUF_OVERFLOW;
128         }
129 
130         char pidBuf[BUF_SIZE_TINY] = "";
131         int pid = static_cast<int>(getpid());
132         if (snprintf_s(pidBuf, sizeof(pidBuf), sizeof(pidBuf) - 1, "%d", pid) < 0) {
133             return ERR_BUF_OVERFLOW;
134         }
135 
136         int fd = open(bufPath, O_RDWR | O_CREAT, 0644);  // 0644:-rw-r--r--
137         if (fd < 0) {
138             return ERR_FILE_OPEN;
139         }
140 
141         struct flock fl;
142         fl.l_type = F_WRLCK;
143         fl.l_start = 0;
144         fl.l_whence = SEEK_SET;
145         fl.l_len = 0;
146         int retChild = fcntl(fd, F_SETLK, &fl);
147         if (retChild == -1) {
148             close(fd);
149             return 1;
150         }
151 
152         int rc = ftruncate(fd, 0);
153         if (rc == -1) {
154             close(fd);
155             return ERR_FILE_STAT;
156         }
157         rc = write(fd, &pidBuf, strlen(pidBuf));
158         if (rc == -1) {
159             close(fd);
160             return ERR_FILE_WRITE;
161         }
162 
163         if (checkOrNew) {
164             close(fd);
165         }
166 
167         return 0;
168     }
169 #endif
170 
171 #ifdef _WIN32
LaunchServerWin32(const char *runPath, const char *listenString, int logLevel)172 __declspec(dllexport) bool LaunchServerWin32(const char *runPath, const char *listenString, int logLevel)
173 {
174     bool retVal = false;
175     STARTUPINFO si = {};
176     PROCESS_INFORMATION pi = {};
177     ZeroMemory(&si, sizeof(si));
178     ZeroMemory(&pi, sizeof(pi));
179 
180     char buf[BUF_SIZE_SMALL] = "";
181     // here we give a dummy option first, because getopt will assume the first option is command. it
182     // begin from 2nd args.
183     if (sprintf_s(buf, sizeof(buf), "dummy -b -l %d -s %s -m", logLevel, listenString) < 0) {
184         return retVal;
185     }
186 
187     si.cb = sizeof(STARTUPINFO);
188     if (!CreateProcess(runPath, buf, nullptr, nullptr, false, DETACHED_PROCESS, nullptr, nullptr, &si, &pi)) {
189         retVal = false;
190     } else {
191         retVal = true;
192     }
193     CloseHandle(pi.hThread);
194     CloseHandle(pi.hProcess);
195     return retVal;
196 }
197 #endif
198 }
199 
200