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