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