1 /*
2  * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  *
23  */
24 
25 #include "precompiled.hpp"
26 #include "jvm.h"
27 #include "memory/oopFactory.hpp"
28 #include "memory/resourceArea.hpp"
29 #include "oops/oop.inline.hpp"
30 #include "runtime/handles.inline.hpp"
31 #include "runtime/javaCalls.hpp"
32 #include "runtime/mutexLocker.hpp"
33 #include "services/diagnosticArgument.hpp"
34 #include "services/diagnosticFramework.hpp"
35 #include "services/management.hpp"
36 
CmdLine(const char * line,size_t len,bool no_command_name)37 CmdLine::CmdLine(const char* line, size_t len, bool no_command_name)
38   : _cmd(line), _cmd_len(0), _args(NULL), _args_len(0)
39 {
40   assert(line != NULL, "Command line string should not be NULL");
41   const char* line_end;
42   const char* cmd_end;
43 
44   _cmd = line;
45   line_end = &line[len];
46 
47   // Skip whitespace in the beginning of the line.
48   while (_cmd < line_end && isspace((int) _cmd[0])) {
49     _cmd++;
50   }
51   cmd_end = _cmd;
52 
53   if (no_command_name) {
54     _cmd = NULL;
55     _cmd_len = 0;
56   } else {
57     // Look for end of the command name
58     while (cmd_end < line_end && !isspace((int) cmd_end[0])) {
59       cmd_end++;
60     }
61     _cmd_len = cmd_end - _cmd;
62   }
63   _args = cmd_end;
64   _args_len = line_end - _args;
65 }
66 
next(TRAPS)67 bool DCmdArgIter::next(TRAPS) {
68   if (_len == 0) return false;
69   // skipping delimiters
70   while (_cursor < _len - 1 && _buffer[_cursor] == _delim) {
71     _cursor++;
72   }
73   // handling end of command line
74   if (_cursor == _len - 1 && _buffer[_cursor] == _delim) {
75     _key_addr = &_buffer[_cursor];
76     _key_len = 0;
77     _value_addr = &_buffer[_cursor];
78     _value_len = 0;
79     return false;
80   }
81   // extracting first item, argument or option name
82   _key_addr = &_buffer[_cursor];
83   bool arg_had_quotes = false;
84   while (_cursor <= _len - 1 && _buffer[_cursor] != '=' && _buffer[_cursor] != _delim) {
85     // argument can be surrounded by single or double quotes
86     if (_buffer[_cursor] == '\"' || _buffer[_cursor] == '\'') {
87       _key_addr++;
88       char quote = _buffer[_cursor];
89       arg_had_quotes = true;
90       while (_cursor < _len - 1) {
91         _cursor++;
92         if (_buffer[_cursor] == quote && _buffer[_cursor - 1] != '\\') {
93           break;
94         }
95       }
96       if (_buffer[_cursor] != quote) {
97         THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(),
98                 "Format error in diagnostic command arguments", false);
99       }
100       break;
101     }
102     _cursor++;
103   }
104   _key_len = &_buffer[_cursor] - _key_addr;
105   if (arg_had_quotes) {
106     // if the argument was quoted, we need to step past the last quote here
107     _cursor++;
108   }
109   // check if the argument has the <key>=<value> format
110   if (_cursor <= _len -1 && _buffer[_cursor] == '=') {
111     _cursor++;
112     _value_addr = &_buffer[_cursor];
113     bool value_had_quotes = false;
114     // extract the value
115     while (_cursor <= _len - 1 && _buffer[_cursor] != _delim) {
116       // value can be surrounded by simple or double quotes
117       if (_buffer[_cursor] == '\"' || _buffer[_cursor] == '\'') {
118         _value_addr++;
119         char quote = _buffer[_cursor];
120         value_had_quotes = true;
121         while (_cursor < _len - 1) {
122           _cursor++;
123           if (_buffer[_cursor] == quote && _buffer[_cursor - 1] != '\\') {
124             break;
125           }
126         }
127         if (_buffer[_cursor] != quote) {
128           THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(),
129                   "Format error in diagnostic command arguments", false);
130         }
131         break;
132       }
133       _cursor++;
134     }
135     _value_len = &_buffer[_cursor] - _value_addr;
136     if (value_had_quotes) {
137       // if the value was quoted, we need to step past the last quote here
138       _cursor++;
139     }
140   } else {
141     _value_addr = NULL;
142     _value_len = 0;
143   }
144   return _key_len != 0;
145 }
146 
by_name(void * cmd_name,DCmdInfo * info)147 bool DCmdInfo::by_name(void* cmd_name, DCmdInfo* info) {
148   if (info == NULL) return false;
149   return strcmp((const char*)cmd_name, info->name()) == 0;
150 }
151 
add_dcmd_option(GenDCmdArgument * arg)152 void DCmdParser::add_dcmd_option(GenDCmdArgument* arg) {
153   assert(arg != NULL, "Sanity");
154   if (_options == NULL) {
155     _options = arg;
156   } else {
157     GenDCmdArgument* o = _options;
158     while (o->next() != NULL) {
159       o = o->next();
160     }
161     o->set_next(arg);
162   }
163   arg->set_next(NULL);
164   Thread* THREAD = Thread::current();
165   arg->init_value(THREAD);
166   if (HAS_PENDING_EXCEPTION) {
167     fatal("Initialization must be successful");
168   }
169 }
170 
add_dcmd_argument(GenDCmdArgument * arg)171 void DCmdParser::add_dcmd_argument(GenDCmdArgument* arg) {
172   assert(arg != NULL, "Sanity");
173   if (_arguments_list == NULL) {
174     _arguments_list = arg;
175   } else {
176     GenDCmdArgument* a = _arguments_list;
177     while (a->next() != NULL) {
178       a = a->next();
179     }
180     a->set_next(arg);
181   }
182   arg->set_next(NULL);
183   Thread* THREAD = Thread::current();
184   arg->init_value(THREAD);
185   if (HAS_PENDING_EXCEPTION) {
186     fatal("Initialization must be successful");
187   }
188 }
189 
parse(CmdLine * line,char delim,TRAPS)190 void DCmdParser::parse(CmdLine* line, char delim, TRAPS) {
191   GenDCmdArgument* next_argument = _arguments_list;
192   DCmdArgIter iter(line->args_addr(), line->args_len(), delim);
193   bool cont = iter.next(CHECK);
194   while (cont) {
195     GenDCmdArgument* arg = lookup_dcmd_option(iter.key_addr(),
196             iter.key_length());
197     if (arg != NULL) {
198       arg->read_value(iter.value_addr(), iter.value_length(), CHECK);
199     } else {
200       if (next_argument != NULL) {
201         arg = next_argument;
202         arg->read_value(iter.key_addr(), iter.key_length(), CHECK);
203         next_argument = next_argument->next();
204       } else {
205         const size_t buflen    = 120;
206         const size_t argbuflen = 30;
207         char buf[buflen];
208         char argbuf[argbuflen];
209         size_t len = MIN2<size_t>(iter.key_length(), argbuflen - 1);
210 
211         strncpy(argbuf, iter.key_addr(), len);
212         argbuf[len] = '\0';
213         jio_snprintf(buf, buflen - 1, "Unknown argument '%s' in diagnostic command.", argbuf);
214 
215         THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), buf);
216       }
217     }
218     cont = iter.next(CHECK);
219   }
220   check(CHECK);
221 }
222 
lookup_dcmd_option(const char * name,size_t len)223 GenDCmdArgument* DCmdParser::lookup_dcmd_option(const char* name, size_t len) {
224   GenDCmdArgument* arg = _options;
225   while (arg != NULL) {
226     if (strlen(arg->name()) == len &&
227       strncmp(name, arg->name(), len) == 0) {
228       return arg;
229     }
230     arg = arg->next();
231   }
232   return NULL;
233 }
234 
check(TRAPS)235 void DCmdParser::check(TRAPS) {
236   const size_t buflen = 256;
237   char buf[buflen];
238   GenDCmdArgument* arg = _arguments_list;
239   while (arg != NULL) {
240     if (arg->is_mandatory() && !arg->has_value()) {
241       jio_snprintf(buf, buflen - 1, "The argument '%s' is mandatory.", arg->name());
242       THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), buf);
243     }
244     arg = arg->next();
245   }
246   arg = _options;
247   while (arg != NULL) {
248     if (arg->is_mandatory() && !arg->has_value()) {
249       jio_snprintf(buf, buflen - 1, "The option '%s' is mandatory.", arg->name());
250       THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), buf);
251     }
252     arg = arg->next();
253   }
254 }
255 
print_help(outputStream * out,const char * cmd_name) const256 void DCmdParser::print_help(outputStream* out, const char* cmd_name) const {
257   out->print("Syntax : %s %s", cmd_name, _options == NULL ? "" : "[options]");
258   GenDCmdArgument* arg = _arguments_list;
259   while (arg != NULL) {
260     if (arg->is_mandatory()) {
261       out->print(" <%s>", arg->name());
262     } else {
263       out->print(" [<%s>]", arg->name());
264     }
265     arg = arg->next();
266   }
267   out->cr();
268   if (_arguments_list != NULL) {
269     out->print_cr("\nArguments:");
270     arg = _arguments_list;
271     while (arg != NULL) {
272       out->print("\t%s : %s %s (%s, ", arg->name(),
273                  arg->is_mandatory() ? "" : "[optional]",
274                  arg->description(), arg->type());
275       if (arg->has_default()) {
276         out->print("%s", arg->default_string());
277       } else {
278         out->print("no default value");
279       }
280       out->print_cr(")");
281       arg = arg->next();
282     }
283   }
284   if (_options != NULL) {
285     out->print_cr("\nOptions: (options must be specified using the <key> or <key>=<value> syntax)");
286     arg = _options;
287     while (arg != NULL) {
288       out->print("\t%s : %s %s (%s, ", arg->name(),
289                  arg->is_mandatory() ? "" : "[optional]",
290                  arg->description(), arg->type());
291       if (arg->has_default()) {
292         out->print("%s", arg->default_string());
293       } else {
294         out->print("no default value");
295       }
296       out->print_cr(")");
297       arg = arg->next();
298     }
299   }
300 }
301 
reset(TRAPS)302 void DCmdParser::reset(TRAPS) {
303   GenDCmdArgument* arg = _arguments_list;
304   while (arg != NULL) {
305     arg->reset(CHECK);
306     arg = arg->next();
307   }
308   arg = _options;
309   while (arg != NULL) {
310     arg->reset(CHECK);
311     arg = arg->next();
312   }
313 }
314 
cleanup()315 void DCmdParser::cleanup() {
316   GenDCmdArgument* arg = _arguments_list;
317   while (arg != NULL) {
318     arg->cleanup();
319     arg = arg->next();
320   }
321   arg = _options;
322   while (arg != NULL) {
323     arg->cleanup();
324     arg = arg->next();
325   }
326 }
327 
num_arguments() const328 int DCmdParser::num_arguments() const {
329   GenDCmdArgument* arg = _arguments_list;
330   int count = 0;
331   while (arg != NULL) {
332     count++;
333     arg = arg->next();
334   }
335   arg = _options;
336   while (arg != NULL) {
337     count++;
338     arg = arg->next();
339   }
340   return count;
341 }
342 
argument_name_array() const343 GrowableArray<const char *>* DCmdParser::argument_name_array() const {
344   int count = num_arguments();
345   GrowableArray<const char *>* array = new GrowableArray<const char *>(count);
346   GenDCmdArgument* arg = _arguments_list;
347   while (arg != NULL) {
348     array->append(arg->name());
349     arg = arg->next();
350   }
351   arg = _options;
352   while (arg != NULL) {
353     array->append(arg->name());
354     arg = arg->next();
355   }
356   return array;
357 }
358 
argument_info_array() const359 GrowableArray<DCmdArgumentInfo*>* DCmdParser::argument_info_array() const {
360   int count = num_arguments();
361   GrowableArray<DCmdArgumentInfo*>* array = new GrowableArray<DCmdArgumentInfo *>(count);
362   int idx = 0;
363   GenDCmdArgument* arg = _arguments_list;
364   while (arg != NULL) {
365     array->append(new DCmdArgumentInfo(arg->name(), arg->description(),
366                   arg->type(), arg->default_string(), arg->is_mandatory(),
367                   false, arg->allow_multiple(), idx));
368     idx++;
369     arg = arg->next();
370   }
371   arg = _options;
372   while (arg != NULL) {
373     array->append(new DCmdArgumentInfo(arg->name(), arg->description(),
374                   arg->type(), arg->default_string(), arg->is_mandatory(),
375                   true, arg->allow_multiple()));
376     arg = arg->next();
377   }
378   return array;
379 }
380 
381 DCmdFactory* DCmdFactory::_DCmdFactoryList = NULL;
382 bool DCmdFactory::_has_pending_jmx_notification = false;
383 
parse_and_execute(DCmdSource source,outputStream * out,const char * cmdline,char delim,TRAPS)384 void DCmd::parse_and_execute(DCmdSource source, outputStream* out,
385                              const char* cmdline, char delim, TRAPS) {
386 
387   if (cmdline == NULL) return; // Nothing to do!
388   DCmdIter iter(cmdline, '\n');
389 
390   int count = 0;
391   while (iter.has_next()) {
392     if(source == DCmd_Source_MBean && count > 0) {
393       // When diagnostic commands are invoked via JMX, each command line
394       // must contains one and only one command because of the Permission
395       // checks performed by the DiagnosticCommandMBean
396       THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
397               "Invalid syntax");
398     }
399     CmdLine line = iter.next();
400     if (line.is_stop()) {
401       break;
402     }
403     if (line.is_executable()) {
404       ResourceMark rm;
405       DCmd* command = DCmdFactory::create_local_DCmd(source, line, out, CHECK);
406       assert(command != NULL, "command error must be handled before this line");
407       DCmdMark mark(command);
408       command->parse(&line, delim, CHECK);
409       command->execute(source, CHECK);
410     }
411     count++;
412   }
413 }
414 
parse(CmdLine * line,char delim,TRAPS)415 void DCmdWithParser::parse(CmdLine* line, char delim, TRAPS) {
416   _dcmdparser.parse(line, delim, CHECK);
417 }
418 
print_help(const char * name) const419 void DCmdWithParser::print_help(const char* name) const {
420   _dcmdparser.print_help(output(), name);
421 }
422 
reset(TRAPS)423 void DCmdWithParser::reset(TRAPS) {
424   _dcmdparser.reset(CHECK);
425 }
426 
cleanup()427 void DCmdWithParser::cleanup() {
428   _dcmdparser.cleanup();
429 }
430 
argument_name_array() const431 GrowableArray<const char*>* DCmdWithParser::argument_name_array() const {
432   return _dcmdparser.argument_name_array();
433 }
434 
argument_info_array() const435 GrowableArray<DCmdArgumentInfo*>* DCmdWithParser::argument_info_array() const {
436   return _dcmdparser.argument_info_array();
437 }
438 
push_jmx_notification_request()439 void DCmdFactory::push_jmx_notification_request() {
440   MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag);
441   _has_pending_jmx_notification = true;
442   Notification_lock->notify_all();
443 }
444 
send_notification(TRAPS)445 void DCmdFactory::send_notification(TRAPS) {
446   DCmdFactory::send_notification_internal(THREAD);
447   // Clearing pending exception to avoid premature termination of
448   // the service thread
449   if (HAS_PENDING_EXCEPTION) {
450     CLEAR_PENDING_EXCEPTION;
451   }
452 }
send_notification_internal(TRAPS)453 void DCmdFactory::send_notification_internal(TRAPS) {
454   ResourceMark rm(THREAD);
455   HandleMark hm(THREAD);
456   bool notif = false;
457   {
458     MutexLocker ml(THREAD, Notification_lock, Mutex::_no_safepoint_check_flag);
459     notif = _has_pending_jmx_notification;
460     _has_pending_jmx_notification = false;
461   }
462   if (notif) {
463 
464     Klass* k = Management::com_sun_management_internal_DiagnosticCommandImpl_klass(CHECK);
465     InstanceKlass* dcmd_mbean_klass = InstanceKlass::cast(k);
466 
467     JavaValue result(T_OBJECT);
468     JavaCalls::call_static(&result,
469             dcmd_mbean_klass,
470             vmSymbols::getDiagnosticCommandMBean_name(),
471             vmSymbols::getDiagnosticCommandMBean_signature(),
472             CHECK);
473 
474     instanceOop m = (instanceOop) result.get_jobject();
475     instanceHandle dcmd_mbean_h(THREAD, m);
476 
477     if (!dcmd_mbean_h->is_a(k)) {
478       THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
479               "DiagnosticCommandImpl.getDiagnosticCommandMBean didn't return a DiagnosticCommandMBean instance");
480     }
481 
482     JavaValue result2(T_VOID);
483     JavaCallArguments args2(dcmd_mbean_h);
484 
485     JavaCalls::call_virtual(&result2,
486             dcmd_mbean_klass,
487             vmSymbols::createDiagnosticFrameworkNotification_name(),
488             vmSymbols::void_method_signature(),
489             &args2,
490             CHECK);
491   }
492 }
493 
494 bool DCmdFactory::_send_jmx_notification = false;
495 
factory(DCmdSource source,const char * name,size_t len)496 DCmdFactory* DCmdFactory::factory(DCmdSource source, const char* name, size_t len) {
497   MutexLocker ml(DCmdFactory_lock, Mutex::_no_safepoint_check_flag);
498   DCmdFactory* factory = _DCmdFactoryList;
499   while (factory != NULL) {
500     if (strlen(factory->name()) == len &&
501         strncmp(name, factory->name(), len) == 0) {
502       if(factory->export_flags() & source) {
503         return factory;
504       } else {
505         return NULL;
506       }
507     }
508     factory = factory->_next;
509   }
510   return NULL;
511 }
512 
register_DCmdFactory(DCmdFactory * factory)513 int DCmdFactory::register_DCmdFactory(DCmdFactory* factory) {
514   MutexLocker ml(DCmdFactory_lock, Mutex::_no_safepoint_check_flag);
515   factory->_next = _DCmdFactoryList;
516   _DCmdFactoryList = factory;
517   if (_send_jmx_notification && !factory->_hidden
518       && (factory->_export_flags & DCmd_Source_MBean)) {
519     DCmdFactory::push_jmx_notification_request();
520   }
521   return 0; // Actually, there's no checks for duplicates
522 }
523 
create_local_DCmd(DCmdSource source,CmdLine & line,outputStream * out,TRAPS)524 DCmd* DCmdFactory::create_local_DCmd(DCmdSource source, CmdLine &line,
525                                      outputStream* out, TRAPS) {
526   DCmdFactory* f = factory(source, line.cmd_addr(), line.cmd_len());
527   if (f != NULL) {
528     if (!f->is_enabled()) {
529       THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(),
530                      f->disabled_message());
531     }
532     return f->create_resource_instance(out);
533   }
534   THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(),
535              "Unknown diagnostic command");
536 }
537 
DCmd_list(DCmdSource source)538 GrowableArray<const char*>* DCmdFactory::DCmd_list(DCmdSource source) {
539   MutexLocker ml(DCmdFactory_lock, Mutex::_no_safepoint_check_flag);
540   GrowableArray<const char*>* array = new GrowableArray<const char*>();
541   DCmdFactory* factory = _DCmdFactoryList;
542   while (factory != NULL) {
543     if (!factory->is_hidden() && (factory->export_flags() & source)) {
544       array->append(factory->name());
545     }
546     factory = factory->next();
547   }
548   return array;
549 }
550 
DCmdInfo_list(DCmdSource source)551 GrowableArray<DCmdInfo*>* DCmdFactory::DCmdInfo_list(DCmdSource source ) {
552   MutexLocker ml(DCmdFactory_lock, Mutex::_no_safepoint_check_flag);
553   GrowableArray<DCmdInfo*>* array = new GrowableArray<DCmdInfo*>();
554   DCmdFactory* factory = _DCmdFactoryList;
555   while (factory != NULL) {
556     if (!factory->is_hidden() && (factory->export_flags() & source)) {
557       array->append(new DCmdInfo(factory->name(),
558                     factory->description(), factory->impact(),
559                     factory->permission(), factory->num_arguments(),
560                     factory->is_enabled()));
561     }
562     factory = factory->next();
563   }
564   return array;
565 }
566