]> sourceware.org Git - systemtap.git/blob - util.cxx
Merge branch 'master' of ssh://sources.redhat.com/git/systemtap
[systemtap.git] / util.cxx
1 // Copyright (C) Andrew Tridgell 2002 (original file)
2 // Copyright (C) 2006, 2009 Red Hat Inc. (systemtap changes)
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License as
6 // published by the Free Software Foundation; either version 2 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 #include "util.h"
19 #include "sys/sdt.h"
20 #include <stdexcept>
21 #include <cerrno>
22
23 extern "C" {
24 #include <fcntl.h>
25 #include <pwd.h>
26 #include <spawn.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <unistd.h>
33 }
34
35 using namespace std;
36
37
38 // Return current users home directory or die.
39 const char *
40 get_home_directory(void)
41 {
42 const char *p = getenv("HOME");
43 if (p)
44 return p;
45
46 struct passwd *pwd = getpwuid(getuid());
47 if (pwd)
48 return pwd->pw_dir;
49
50 throw runtime_error("Unable to determine home directory");
51 return NULL;
52 }
53
54
55 // Get the size of a file in bytes
56 size_t
57 get_file_size(const string &path)
58 {
59 struct stat file_info;
60
61 if (stat(path.c_str(), &file_info) == 0)
62 return file_info.st_size;
63 else
64 return 0;
65 }
66
67 // Get the size of a file in bytes
68 bool
69 file_exists (const string &path)
70 {
71 struct stat file_info;
72
73 if (stat(path.c_str(), &file_info) == 0)
74 return true;
75
76 return false;
77 }
78
79 // Copy a file. The copy is done via a temporary file and atomic
80 // rename.
81 bool
82 copy_file(const string& src, const string& dest, bool verbose)
83 {
84 int fd1, fd2;
85 char buf[10240];
86 int n;
87 string tmp;
88 char *tmp_name;
89 mode_t mask;
90
91 if (verbose)
92 clog << "Copying " << src << " to " << dest << endl;
93
94 // Open the src file.
95 fd1 = open(src.c_str(), O_RDONLY);
96 if (fd1 == -1)
97 goto error;
98
99 // Open the temporary output file.
100 tmp = dest + string(".XXXXXX");
101 tmp_name = (char *)tmp.c_str();
102 fd2 = mkstemp(tmp_name);
103 if (fd2 == -1)
104 {
105 close(fd1);
106 goto error;
107 }
108
109 // Copy the src file to the temporary output file.
110 while ((n = read(fd1, buf, sizeof(buf))) > 0)
111 {
112 if (write(fd2, buf, n) != n)
113 {
114 close(fd2);
115 close(fd1);
116 unlink(tmp_name);
117 goto error;
118 }
119 }
120 close(fd1);
121
122 // Set the permissions on the temporary output file.
123 mask = umask(0);
124 fchmod(fd2, 0666 & ~mask);
125 umask(mask);
126
127 // Close the temporary output file. The close can fail on NFS if
128 // out of space.
129 if (close(fd2) == -1)
130 {
131 unlink(tmp_name);
132 goto error;
133 }
134
135 // Rename the temporary output file to the destination file.
136 unlink(dest.c_str());
137 if (rename(tmp_name, dest.c_str()) == -1)
138 {
139 unlink(tmp_name);
140 goto error;
141 }
142
143 return true;
144
145 error:
146 cerr << "Copy failed (\"" << src << "\" to \"" << dest << "\"): "
147 << strerror(errno) << endl;
148 return false;
149 }
150
151
152 // Make sure a directory exists.
153 int
154 create_dir(const char *dir)
155 {
156 struct stat st;
157 if (stat(dir, &st) == 0)
158 {
159 if (S_ISDIR(st.st_mode))
160 return 0;
161 errno = ENOTDIR;
162 return 1;
163 }
164
165 if (mkdir(dir, 0777) != 0 && errno != EEXIST)
166 return 1;
167
168 return 0;
169 }
170
171 // Remove a file or directory
172 int
173 remove_file_or_dir (const char *name)
174 {
175 int rc;
176 struct stat st;
177
178 if ((rc = stat(name, &st)) != 0)
179 {
180 if (errno == ENOENT)
181 return 0;
182 return 1;
183 }
184
185 if (remove (name) != 0)
186 return 1;
187 cerr << "remove returned 0" << endl;
188 return 0;
189 }
190
191 void
192 tokenize(const string& str, vector<string>& tokens,
193 const string& delimiters = " ")
194 {
195 // Skip delimiters at beginning.
196 string::size_type lastPos = str.find_first_not_of(delimiters, 0);
197 // Find first "non-delimiter".
198 string::size_type pos = str.find_first_of(delimiters, lastPos);
199
200 while (pos != string::npos || lastPos != string::npos)
201 {
202 // Found a token, add it to the vector.
203 tokens.push_back(str.substr(lastPos, pos - lastPos));
204 // Skip delimiters. Note the "not_of"
205 lastPos = str.find_first_not_of(delimiters, pos);
206 // Find next "non-delimiter"
207 pos = str.find_first_of(delimiters, lastPos);
208 }
209 }
210
211
212 // Resolve an executable name to a canonical full path name, with the
213 // same policy as execvp(). A program name not containing a slash
214 // will be searched along the $PATH.
215
216 string find_executable(const string& name)
217 {
218 string retpath;
219
220 if (name.size() == 0)
221 return name;
222
223 struct stat st;
224
225 if (name.find('/') != string::npos) // slash in the path already?
226 {
227 retpath = name;
228 }
229 else // Nope, search $PATH.
230 {
231 char *path = getenv("PATH");
232 if (path)
233 {
234 // Split PATH up.
235 vector<string> dirs;
236 tokenize(string(path), dirs, string(":"));
237
238 // Search the path looking for the first executable of the right name.
239 for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); i++)
240 {
241 string fname = *i + "/" + name;
242 const char *f = fname.c_str();
243
244 // Look for a normal executable file.
245 if (access(f, X_OK) == 0
246 && stat(f, &st) == 0
247 && S_ISREG(st.st_mode))
248 {
249 retpath = fname;
250 break;
251 }
252 }
253 }
254 }
255
256
257 // Could not find the program on the $PATH. We'll just fall back to
258 // the unqualified name, which our caller will probably fail with.
259 if (retpath == "")
260 retpath = name;
261
262 // Canonicalize the path name.
263 char *cf = canonicalize_file_name (retpath.c_str());
264 if (cf)
265 {
266 retpath = string(cf);
267 free (cf);
268 }
269
270 return retpath;
271 }
272
273
274
275 const string cmdstr_quoted(const string& cmd)
276 {
277 // original cmd : substr1
278 // or : substr1'substr2
279 // or : substr1'substr2'substr3......
280 // after quoted :
281 // every substr(even it's empty) is quoted by ''
282 // every single-quote(') is quoted by ""
283 // examples: substr1 --> 'substr1'
284 // substr1'substr2 --> 'substr1'"'"'substr2'
285
286 string quoted_cmd;
287 string quote("'");
288 string replace("'\"'\"'");
289 string::size_type pos = 0;
290
291 quoted_cmd += quote;
292 for (string::size_type quote_pos = cmd.find(quote, pos);
293 quote_pos != string::npos;
294 quote_pos = cmd.find(quote, pos)) {
295 quoted_cmd += cmd.substr(pos, quote_pos - pos);
296 quoted_cmd += replace;
297 pos = quote_pos + 1;
298 }
299 quoted_cmd += cmd.substr(pos, cmd.length() - pos);
300 quoted_cmd += quote;
301
302 return quoted_cmd;
303 }
304
305
306 string
307 git_revision(const string& path)
308 {
309 string revision = "(not-a-git-repository)";
310 string git_dir = path + "/.git/";
311
312 struct stat st;
313 if (stat(git_dir.c_str(), &st) == 0)
314 {
315 string command = "git --git-dir=\"" + git_dir
316 + "\" rev-parse HEAD 2>/dev/null";
317
318 char buf[50];
319 FILE *fp = popen(command.c_str(), "r");
320 if (fp != NULL)
321 {
322 char *bufp = fgets(buf, sizeof(buf), fp);
323 int rc = pclose(fp);
324 if (bufp != NULL && rc == 0)
325 revision = buf;
326 }
327 }
328
329 return revision;
330 }
331
332
333 static pid_t spawned_pid = 0;
334
335 // Runs a command with a saved PID, so we can kill it from the signal handler
336 int
337 stap_system(int verbose, const std::string& command)
338 {
339 const char *cmd = command.c_str();
340 STAP_PROBE1(stap, stap_system__start, cmd);
341 char const * const argv[] = { "sh", "-c", cmd, NULL };
342 int ret, status;
343
344 spawned_pid = 0;
345
346 if (verbose > 1)
347 clog << "Running " << command << endl;
348
349 ret = posix_spawn(&spawned_pid, "/bin/sh", NULL, NULL, const_cast<char * const *>(argv), environ);
350 STAP_PROBE2(stap, stap_system__spawn, ret, spawned_pid);
351 if (ret == 0)
352 {
353 ret = waitpid(spawned_pid, &status, 0);
354 if (ret == spawned_pid)
355 {
356 ret = WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
357 if (verbose > 2)
358 clog << "Spawn waitpid result (0x" << ios::hex << status << ios::dec << "): " << ret << endl;
359 }
360 else
361 {
362 if (verbose > 1)
363 clog << "Spawn waitpid error (" << ret << "): " << strerror(errno) << endl;
364 ret = -1;
365 }
366 }
367 else
368 {
369 if (verbose > 1)
370 clog << "Spawn error (" << ret << "): " << strerror(ret) << endl;
371 ret = -1;
372 }
373 STAP_PROBE1(stap, stap_system__complete, ret);
374 spawned_pid = 0;
375 return ret;
376 }
377
378 // Send a signal to our spawned command
379 int
380 kill_stap_spawn(int sig)
381 {
382 return spawned_pid ? kill(spawned_pid, sig) : 0;
383 }
384
385 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.053278 seconds and 5 git commands to generate.