1 mike 1.1 /*
2 **==============================================================================
3 **
4 ** Open Management Infrastructure (OMI)
5 **
6 ** Copyright (c) Microsoft Corporation
7 **
8 ** Licensed under the Apache License, Version 2.0 (the "License"); you may not
9 ** use this file except in compliance with the License. You may obtain a copy
10 ** of the License at
11 **
12 ** http://www.apache.org/licenses/LICENSE-2.0
13 **
14 ** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 ** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
16 ** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
17 ** MERCHANTABLITY OR NON-INFRINGEMENT.
18 **
19 ** See the Apache 2 License for the specific language governing permissions
20 ** and limitations under the License.
21 **
22 mike 1.1 **==============================================================================
23 */
24
25 #include <protocol/protocol.h>
|
26 krisbash 1.3 #include <pal/sleep.h>
|
27 mike 1.1 #include <provmgr/provmgr.h>
|
28 krisbash 1.3 #include <pal/strings.h>
|
29 mike 1.1 #include <base/log.h>
30 #include <base/env.h>
31 #include <base/paths.h>
|
32 krisbash 1.3 #include <base/omigetopt.h>
33 #include <base/multiplex.h>
34 #include <base/Strand.h>
35 #include <pal/format.h>
|
36 mike 1.1 #include <sys/types.h>
|
37 krisbash 1.3 #include <sys/resource.h>
|
38 mike 1.1 #include <pwd.h>
39 #include <grp.h>
40
|
41 krisbash 1.3 STRAND_DEBUGNAME( IdleNotification );
|
42 mike 1.1
43 typedef struct _AgentData AgentData;
44
45 struct _AgentData
46 {
|
47 krisbash 1.3 MuxIn mux;
48 ProtocolSocketAndBase* protocol;
49 ProvMgr provmgr;
50 Selector selector;
51 Strand idleNotificationStrand;
|
52 mike 1.1 } ;
53
54 typedef struct _Options
55 {
56 const char* provDir;
57 MI_Boolean help;
58 MI_Uint32 idletimeout;
59 }
60 Options;
61
62 static AgentData s_data;
63
64 static Options s_opts;
65 static const char* arg0 = 0;
|
66 krisbash 1.3 static const ZChar HELP[] = ZT("\
|
67 mike 1.1 Usage: %s [OPTIONS]\n\
68 \n\
69 This program starts the OMI agent.\n\
70 \n\
71 OPTIONS:\n\
72 --version Print version information.\n\
73 --providerdir Find providers in this directory.\n\
|
74 krisbash 1.3 --loglevel LEVEL Set the log level (0-5).\n\
75 \n");
|
76 mike 1.1
77 PRINTF_FORMAT(1, 2)
|
78 krisbash 1.3 static void FUNCTION_NEVER_RETURNS err(const ZChar* fmt, ...)
|
79 mike 1.1 {
|
80 krisbash 1.3 ZChar* buf = NULL;
81 ZChar* buf2 = NULL;
|
82 mike 1.1 va_list ap;
|
83 krisbash 1.3 size_t buflen = PAL_MAX_PATH_SIZE;
84 buf = (ZChar*) PAL_Malloc( sizeof(ZChar) * buflen * 2 );
85 if (buf)
86 {
87 buf2 = buf + buflen;
88
89 memset(&ap, 0, sizeof(ap));
90 va_start(ap, fmt);
91 Vstprintf(buf, buflen, fmt, ap);
92 va_end(ap);
93 Stprintf(buf2, buflen, ZT("%s: %T"), scs(arg0), buf);
|
94 mike 1.1
|
95 krisbash 1.3 Ftprintf(stderr, ZT("%T\n"), buf2);
96 trace_CriticalError(buf2);
|
97 mike 1.1
|
98 krisbash 1.3 PAL_Free(buf);
99 }
100 exit(1);
101 }
|
102 mike 1.1
|
103 krisbash 1.3 /* enable core dump for current process */
104 static void _EnableCoreDump()
105 {
106 struct rlimit core_limits;
107 core_limits.rlim_cur = RLIM_INFINITY;
108 core_limits.rlim_max = RLIM_INFINITY;
109 setrlimit(RLIMIT_CORE, &core_limits);
|
110 mike 1.1 }
111
|
112 krisbash 1.3 void _IdleNotification_Post( _In_ Strand* self_, _In_ Message* msg)
|
113 mike 1.1 {
|
114 krisbash 1.3 DEBUG_ASSERT( MI_FALSE ); // not used
115 }
|
116 mike 1.1
|
117 krisbash 1.3 void _IdleNotification_PostControl( _In_ Strand* self_, _In_ Message* msg)
118 {
119 DEBUG_ASSERT( MI_FALSE ); // not used
120 }
|
121 mike 1.1
|
122 krisbash 1.3 void _IdleNotification_Ack( _In_ Strand* self_ )
123 {
124 // Nothing to do
|
125 mike 1.1 }
126
|
127 krisbash 1.3 void _IdleNotification_Finish( _In_ Strand* self_ )
|
128 mike 1.1 {
|
129 krisbash 1.3 // Nothing to do
130 }
|
131 mike 1.1
|
132 krisbash 1.3 /*
133 Object that is just used to send to agent manager BinProtocolNotification
|
134 mike 1.1
|
135 krisbash 1.3 Behavior:
136 - It is created when the initial BinProtocolNotification is sent by the server
137 then kept alive as long as the connection is alive.
138 - Post, PostControl and Ack are not actually used
139 - Shutdown:
140 The objects are deleted thru the normal Strand logic once the connection is closed.
141
142 Unique features and special Behavior:
143 - If the agent is idle it will be use to post a BinProtocolNotification
144 thru the many-to-one interface so there are no problem or races with
145 a request being received or a response to a normal request going out
146 at the same time
147 */
148 static StrandFT _IdleNotification_FT = {
149 _IdleNotification_Post,
150 _IdleNotification_PostControl,
151 _IdleNotification_Ack,
152 NULL,
153 NULL,
154 _IdleNotification_Finish,
155 NULL,
156 krisbash 1.3 NULL,
157 NULL,
158 NULL,
159 NULL,
160 NULL };
161
162 /* Called by mux to dispatch an incoming request message */
163 static void _RequestCallback(
164 _Inout_ InteractionOpenParams* interactionParams )
165 {
166 DEBUG_ASSERT( NULL != interactionParams );
167 DEBUG_ASSERT( NULL != interactionParams->msg );
168 DEBUG_ASSERT( NULL == interactionParams->callbackData );
|
169 mike 1.1
|
170 krisbash 1.3 if (BinProtocolNotificationTag == interactionParams->msg->tag)
|
171 mike 1.1 {
|
172 krisbash 1.3 BinProtocolNotification* notification = (BinProtocolNotification*)interactionParams->msg;
173 (void) notification; /* In case DEBUG_ASSERT is compiled out, avoid compiler warnings */
|
174 mike 1.1
|
175 krisbash 1.3 DEBUG_ASSERT(BinNotificationAgentIdle == notification->type);
176
177 Strand_Init( STRAND_DEBUG(IdleNotification) &s_data.idleNotificationStrand, &_IdleNotification_FT, 0, interactionParams );
178 }
179 else
180 {
181 MI_Result result;
182 ProvRegEntry regentry;
183 RequestMsg* request = (RequestMsg*)interactionParams->msg;
|
184 mike 1.1
|
185 krisbash 1.3 DEBUG_ASSERT( Message_IsRequest(interactionParams->msg) );
|
186 mike 1.1
|
187 krisbash 1.3 memset(®entry, 0, sizeof(regentry));
188 regentry.libraryName = request->libraryName;
189 regentry.instanceLifetimeContext = request->instanceLifetimeContext;
|
190 mike 1.1
|
191 krisbash 1.3 result = ProvMgr_NewRequest(&s_data.provmgr, ®entry, interactionParams );
192
193 if (MI_RESULT_OK != result)
194 {
195 trace_Agent_ProvMgrNewRequest_Failed( result );
196 Strand_FailOpenWithResult(interactionParams, result, PostResultMsg_NewAndSerialize);
197 }
|
198 mike 1.1 }
199 }
200
201 static void GetCommandLineDestDirOption(
202 int* argc_,
203 const char* argv[])
204 {
205 int argc = *argc_;
206 int i;
207 const char* destdir = NULL;
208
209 for (i = 1; i < argc; )
210 {
211 if (strcmp(argv[i], "--destdir") == 0)
212 {
213 if (i + 1 == argc)
|
214 krisbash 1.3 err(ZT("missing argument for --destdir option"));
|
215 mike 1.1
216 destdir = argv[i+1];
217 memmove((char*)&argv[i], (char*)&argv[i+2],
218 sizeof(char*) * (argc-i-1));
219 argc -= 2;
220 }
221 else if (strncmp(argv[i], "--destdir=", 10) == 0)
222 {
223 destdir = argv[i] + 10;
224 memmove((char*)&argv[i], (char*)&argv[i+1],
225 sizeof(char*) * (argc-i));
226
227 argc -= 1;
228 }
229 else
230 i++;
231 }
232
233 if (destdir)
234 {
235 if (SetPath(ID_DESTDIR, destdir) != 0)
|
236 krisbash 1.3 err(ZT("failed to set destdir"));
|
237 mike 1.1 }
238
239 *argc_ = argc;
240 }
241
242 static void GetCommandLineOptions(int* argc, const char* argv[])
243 {
244 GetOptState state = GETOPTSTATE_INITIALIZER;
245 const char* opts[] =
246 {
247 "-h",
248 "--help",
249 "-v",
250 "--version",
251 "--providerdir:",
252 "--idletimeout:",
253 "--loglevel:",
254 NULL,
255 };
256
257 for (;;)
258 mike 1.1 {
259 int r = GetOpt(argc, argv, opts, &state);
260
261 if (r == 1)
262 break;
263
264 if (r == -1)
|
265 krisbash 1.3 err(ZT("%s"), scs(state.err));
|
266 mike 1.1
267 /* Check for -h option */
268 if (strcmp(state.opt, "-h") == 0 || strcmp(state.opt, "--help") == 0)
269 {
270 s_opts.help = MI_TRUE;
271 }
272 else if (strcmp(state.opt, "--version") == 0)
273 {
|
274 krisbash 1.3 Tprintf(ZT("%s: %s\n"), scs(arg0),
275 scs(CONFIG_PRODUCT "-" CONFIG_VERSION " - " CONFIG_DATE));
|
276 mike 1.1 exit(0);
277 }
278 else if (strcmp(state.opt, "--providerdir") == 0)
279 {
280 s_opts.provDir = state.arg;
281 }
282 else if (strcmp(state.opt, "--idletimeout") == 0)
283 {
284 char* end;
285 MI_Uint64 x = Strtoull(state.arg, &end, 10);
286
287 if (*end != '\0')
|
288 krisbash 1.3 {
289 err(ZT("bad option argument for --idletimeout: %s"),
290 scs(state.arg));
291 }
|
292 mike 1.1
293 s_opts.idletimeout = x;
294 }
295 else if (strcmp(state.opt, "--loglevel") == 0)
296 {
297 if (Log_SetLevelFromString(state.arg) != 0)
298 {
|
299 krisbash 1.3 err(ZT("bad option argument for %s: %s"),
300 scs(state.opt), scs(state.arg));
|
301 mike 1.1 }
302 }
303 }
304 }
305
|
306 krisbash 1.3 static void _OnCloseCallback(
307 _In_ void* object)
308 {
309 MI_UNUSED(object);
|
310 mike 1.1
|
311 krisbash 1.3 trace_Agent_DisconnectedFromServer();
312 Selector_StopRunning(&s_data.selector);
|
313 mike 1.1 }
314
315 static void _ProvMgrCallbackOnIdle(
316 ProvMgr* mgr,
317 void* callbackData)
318 {
319 BinProtocolNotification* notification;
320
321 MI_UNUSED(mgr);
322 MI_UNUSED(callbackData);
323
|
324 krisbash 1.3 DEBUG_ASSERT( s_data.idleNotificationStrand.info.opened );
325
326 trace_Agent_SendingIdleNotification();
|
327 mike 1.1
328 notification = BinProtocolNotification_New( BinNotificationAgentIdle );
329
330 if (!notification)
|
331 krisbash 1.3 {
332 // Nothing we can do here
333 DEBUG_ASSERT( MI_FALSE );
|
334 mike 1.1 return;
|
335 krisbash 1.3 }
336
337 // Call on the always opened idle notification strand
338 Strand_SchedulePost( &s_data.idleNotificationStrand, ¬ification->base);
|
339 mike 1.1
340 BinProtocolNotification_Release(notification);
341 }
342
343 int agent_main(int argc, const char* argv[])
344 {
345 MI_Result r;
346 Sock fd;
347 int logfd;
348
349 arg0 = argv[0];
350
351 memset(&s_data, 0, sizeof(s_data));
352
|
353 krisbash 1.3 /* Enable core dump */
354 _EnableCoreDump();
355
|
356 mike 1.1 /* Get --destdir option first (other options may depend on it) */
357 GetCommandLineDestDirOption(&argc, argv);
358
359 /* Extract options */
360 GetCommandLineOptions(&argc, argv);
361
362 /* Print help */
363 if (s_opts.help)
364 {
|
365 krisbash 1.3 Ftprintf(stderr, HELP, scs(arg0));
|
366 mike 1.1 exit(1);
367 }
368
369 /* extract socket number */
370 if (argc < 3)
371 {
|
372 krisbash 1.3 trace_Agent_FDParameterIsMissing();
|
373 mike 1.1 exit(1);
374 }
375
376 fd = Strtol(argv[1], 0, 10);
377 logfd = Strtol(argv[2], 0, 10);
378
379 /* Attach log file */
380 {
381 /* Open the log file */
382 if (Log_OpenFD(logfd) != MI_RESULT_OK)
383 {
|
384 krisbash 1.3 err(ZT("failed to attach log file to fd: %d; errno %d"), logfd,
|
385 mike 1.1 (int)errno);
386 }
387 }
388
389 /* selector */
390 {
391 /* Initialize the network */
392 Sock_Start();
393
394 if(Selector_Init(&s_data.selector) != MI_RESULT_OK)
|
395 krisbash 1.3 err(ZT("Selector_Init() failed"));
396
397 Timer_SetSelector(&s_data.selector);
|
398 mike 1.1 }
399
|
400 krisbash 1.3 /* mux */
401 {
402 if(MuxIn_Init(&s_data.mux, _RequestCallback, NULL, _OnCloseCallback, PostResultMsg_NewAndSerialize) != MI_RESULT_OK)
403 err(ZT("MuxIn_Init() failed"));
404 }
405
|
406 mike 1.1 /* Create new protocol object */
407 {
|
408 krisbash 1.3 r = ProtocolSocketAndBase_New_Agent(
|
409 mike 1.1 &s_data.protocol,
410 &s_data.selector,
411 fd,
|
412 krisbash 1.3 MuxIn_Open,
413 &s_data.mux);
|
414 mike 1.1
415 if (r != MI_RESULT_OK)
|
416 krisbash 1.3 err(ZT("ProtocolSocketAndBase_New_Agent() failed"));
|
417 mike 1.1 }
418
419 /* Provider manager */
420 {
421 r = ProvMgr_Init(&s_data.provmgr, &s_data.selector, _ProvMgrCallbackOnIdle, &s_data, s_opts.provDir);
422
423 if (r != MI_RESULT_OK)
|
424 krisbash 1.3 err(ZT("ProvMgr_Init() failed"));
|
425 mike 1.1 }
426
427 /* idle timeout */
428 if (s_opts.idletimeout)
429 {
430 /* convert it to usec */
431 s_data.provmgr.idleTimeoutUsec = s_opts.idletimeout * 1000000;
|
432 krisbash 1.3 trace_Agent_ChangingIdleTimeout(s_opts.idletimeout);
|
433 mike 1.1
434 }
435
436 /* Log start up message */
|
437 krisbash 1.3 trace_Agent_Started((int)fd);
|
438 mike 1.1
439 /* Run the protocol object (waiting for new messages) */
|
440 krisbash 1.3 r = Protocol_Run( &s_data.protocol->internalProtocolBase, TIME_NEVER);
|
441 mike 1.1
442 if (r != MI_RESULT_OK)
|
443 krisbash 1.3 err(ZT("Protocol_Run() failed (%d)"), (int)r);
|
444 mike 1.1
445 // Destroy all global objects
446 Selector_RemoveAllHandlers(&s_data.selector);
447 Selector_Destroy(&s_data.selector);
|
448 krisbash 1.3 ProvMgr_Destroy(&s_data.provmgr);
449 ProtocolSocketAndBase_ReadyToFinish(s_data.protocol);
|
450 mike 1.1
451 return 0;
452 }
|