OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Crashpad Authors. All rights reserved. | |
2 // | |
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 "util/mac/service_management.h" | |
16 | |
17 #import <Foundation/Foundation.h> | |
18 #include <launch.h> | |
19 #include <time.h> | |
20 | |
21 #include <string> | |
22 #include <vector> | |
23 | |
24 #include "base/mac/foundation_util.h" | |
25 #include "base/mac/scoped_cftyperef.h" | |
26 #include "base/strings/stringprintf.h" | |
27 #include "base/strings/sys_string_conversions.h" | |
28 #include "base/rand_util.h" | |
29 #include "gtest/gtest.h" | |
30 #include "util/posix/process_util.h" | |
31 #include "util/stdlib/objc.h" | |
32 | |
33 namespace { | |
34 | |
35 using namespace crashpad; | |
36 | |
37 // Ensures that the process with the specified PID is running, identifying it by | |
38 // requiring that its argv[argc - 1] compare equal to last_arg. | |
39 void ExpectProcessIsRunning(pid_t pid, std::string& last_arg) { | |
40 // The process may not have called exec yet, so loop with a small delay while | |
41 // looking for the cookie. | |
42 int outer_tries = 10; | |
43 std::vector<std::string> job_argv; | |
44 while (outer_tries--) { | |
45 // If the process is in the middle of calling exec, ProcessArgumentsForPID | |
46 // may fail. Loop with a small retry delay while waiting for the expected | |
47 // successful call. | |
48 int inner_tries = 10; | |
49 bool success; | |
50 do { | |
51 success = ProcessArgumentsForPID(pid, &job_argv); | |
52 if (success) { | |
53 break; | |
54 } | |
55 if (inner_tries > 0) { | |
56 timespec sleep_time; | |
57 sleep_time.tv_sec = 0; | |
58 sleep_time.tv_nsec = 1E5; // 100 microseconds | |
59 nanosleep(&sleep_time, NULL); | |
60 } | |
61 } while (inner_tries--); | |
62 ASSERT_TRUE(success); | |
63 | |
64 ASSERT_TRUE(ProcessArgumentsForPID(pid, &job_argv)); | |
65 ASSERT_FALSE(job_argv.empty()); | |
66 if (job_argv.back() == last_arg) { | |
67 break; | |
68 } | |
69 | |
70 if (outer_tries > 0) { | |
71 timespec sleep_time; | |
72 sleep_time.tv_sec = 0; | |
73 sleep_time.tv_nsec = 1E6; // 1 millisecond | |
74 nanosleep(&sleep_time, NULL); | |
75 } | |
76 } | |
77 | |
78 ASSERT_FALSE(job_argv.empty()); | |
79 EXPECT_EQ(last_arg, job_argv.back()); | |
80 } | |
81 | |
82 // Ensures that the process with the specified PID is not running. Because the | |
83 // PID may be reused for another process, a process is only treated as running | |
84 // if its argv[argc - 1] compares equal to last_arg. | |
85 void ExpectProcessIsNotRunning(pid_t pid, std::string& last_arg) { | |
86 // The process may not have exited yet, so loop with a small delay while | |
87 // checking that it has exited. | |
88 int tries = 10; | |
89 std::vector<std::string> job_argv; | |
90 while (tries--) { | |
91 if (!ProcessArgumentsForPID(pid, &job_argv)) { | |
92 // The PID was not found. | |
93 return; | |
94 } | |
95 | |
96 // The PID was found. It may have been recycled for another process. Make | |
97 // sure that the cookie isn’t found. | |
98 ASSERT_FALSE(job_argv.empty()); | |
99 if (job_argv.back() != last_arg) { | |
100 break; | |
101 } | |
102 | |
103 if (tries > 0) { | |
104 timespec sleep_time; | |
105 sleep_time.tv_sec = 0; | |
106 sleep_time.tv_nsec = 1E6; // 1 millisecond | |
107 nanosleep(&sleep_time, NULL); | |
108 } | |
109 } | |
110 | |
111 ASSERT_FALSE(job_argv.empty()); | |
112 EXPECT_NE(last_arg, job_argv.back()); | |
113 } | |
114 | |
115 TEST(ServiceManagement, SubmitRemoveJob) { | |
116 @autoreleasepool { | |
117 std::string cookie; | |
118 for (int index = 0; index < 16; ++index) { | |
119 cookie.append(1, base::RandInt('A', 'Z')); | |
120 } | |
121 | |
122 std::string shell_script = | |
123 base::StringPrintf("sleep 10; echo %s", cookie.c_str()); | |
124 NSString* shell_script_ns = base::SysUTF8ToNSString(shell_script); | |
125 | |
126 const char kJobLabel[] = "com.googlecode.crashpad.test.service_management"; | |
127 NSDictionary* job_dictionary_ns = @{ | |
128 @LAUNCH_JOBKEY_LABEL : @"com.googlecode.crashpad.test.service_management", | |
129 @LAUNCH_JOBKEY_RUNATLOAD : @YES, | |
130 @LAUNCH_JOBKEY_PROGRAMARGUMENTS : | |
131 @[ @"/bin/sh", @"-c", shell_script_ns, ], | |
132 }; | |
133 CFDictionaryRef job_dictionary_cf = | |
134 base::mac::NSToCFCast(job_dictionary_ns); | |
135 | |
136 // The job may be left over from a failed previous run. | |
137 if (ServiceManagementIsJobLoaded(kJobLabel)) { | |
138 EXPECT_TRUE(ServiceManagementRemoveJob(kJobLabel, true)); | |
139 } | |
140 | |
141 EXPECT_FALSE(ServiceManagementIsJobLoaded(kJobLabel)); | |
Robert Sesek
2014/08/14 20:27:22
Maybe ASSERT this instead.
| |
142 EXPECT_FALSE(ServiceManagementIsJobRunning(kJobLabel)); | |
143 | |
144 // Submit the job. | |
145 ASSERT_TRUE(ServiceManagementSubmitJob(job_dictionary_cf)); | |
146 EXPECT_TRUE(ServiceManagementIsJobLoaded(kJobLabel)); | |
147 | |
148 // launchd started the job because RunAtLoad is true. | |
149 pid_t job_pid = ServiceManagementIsJobRunning(kJobLabel); | |
150 ASSERT_GT(job_pid, 0); | |
151 | |
152 ExpectProcessIsRunning(job_pid, shell_script); | |
153 | |
154 // Remove the job. | |
155 ASSERT_TRUE(ServiceManagementRemoveJob(kJobLabel, true)); | |
156 EXPECT_FALSE(ServiceManagementIsJobLoaded(kJobLabel)); | |
157 EXPECT_EQ(0, ServiceManagementIsJobRunning(kJobLabel)); | |
158 | |
159 // Now that the job is unloaded, a subsequent attempt to unload it should be | |
160 // an error. | |
161 EXPECT_FALSE(ServiceManagementRemoveJob(kJobLabel, false)); | |
162 | |
163 ExpectProcessIsNotRunning(job_pid, shell_script); | |
164 } | |
165 } | |
166 | |
167 } // namespace | |
OLD | NEW |