1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1996-2016. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21-module(snmp_config). 22 23-include_lib("kernel/include/file.hrl"). 24-include("snmp_types.hrl"). 25 26%% Avoid warning for local function error/1 clashing with autoimported BIF. 27-compile({no_auto_import,[error/1]}). 28-export([config/0]). 29 30%%-export([write_config_file/4, append_config_file/4, read_config_file/4]). 31 32-export([write_config_file/6, append_config_file/6, read_config_file/4]). 33 34-export([write_agent_snmp_files/7, write_agent_snmp_files/12, 35 write_agent_snmp_files/6, write_agent_snmp_files/11, 36 37 write_agent_snmp_conf/4, write_agent_snmp_conf/5, 38 write_agent_snmp_context_conf/1, 39 write_agent_snmp_community_conf/1, 40 write_agent_snmp_standard_conf/2, 41 write_agent_snmp_target_addr_conf/3, 42 write_agent_snmp_target_addr_conf/4, 43 write_agent_snmp_target_addr_conf/5, 44 write_agent_snmp_target_addr_conf/6, 45 write_agent_snmp_target_params_conf/2, 46 write_agent_snmp_notify_conf/2, 47 write_agent_snmp_usm_conf/5, 48 write_agent_snmp_vacm_conf/3, 49 50 write_manager_snmp_files/8, 51 write_manager_snmp_conf/4, write_manager_snmp_conf/5, 52 write_manager_snmp_users_conf/2, 53 write_manager_snmp_agents_conf/2, 54 write_manager_snmp_usm_conf/2 55 56 ]). 57 58-export([write_agent_config/3, 59 update_agent_config/2, 60 61 write_agent_context_config/3, 62 update_agent_context_config/2, 63 64 write_agent_community_config/3, 65 update_agent_community_config/2, 66 67 write_agent_standard_config/3, 68 update_agent_standard_config/2, 69 70 write_agent_target_addr_config/3, 71 update_agent_target_addr_config/2, 72 73 write_agent_target_params_config/3, 74 update_agent_target_params_config/2, 75 76 write_agent_notify_config/3, 77 update_agent_notify_config/2, 78 79 write_agent_vacm_config/3, 80 update_agent_vacm_config/2, 81 82 write_agent_usm_config/3, 83 update_agent_usm_config/2, 84 85 write_manager_config/3, 86 update_manager_config/2, 87 88 write_manager_users_config/3, 89 update_manager_users_config/2, 90 91 write_manager_agents_config/3, 92 update_manager_agents_config/2, 93 94 write_manager_usm_config/3, 95 update_manager_usm_config/2 96 ]). 97 98 99-export_type([void/0, 100 order_config_entry_function/0, 101 check_config_entry_function/0, 102 write_config_function/0]). 103 104 105%%---------------------------------------------------------------------- 106 107-type void() :: term(). % Any value - ignored 108 109 110%%---------------------------------------------------------------------- 111%% Handy SNMP configuration 112%%---------------------------------------------------------------------- 113 114config() -> 115 case (catch config2()) of 116 ok -> 117 ok; 118 {error, Reason} -> 119 {error, Reason}; 120 E -> 121 {error, {failed, E}} 122 end. 123 124 125config2() -> 126 intro(), 127 SysAgentConfig = 128 case config_agent() of 129 [] -> 130 []; 131 SAC -> 132 [{agent, SAC}] 133 end, 134 SysMgrConfig = 135 case config_manager() of 136 [] -> 137 []; 138 SMC -> 139 [{manager, SMC}] 140 end, 141 config_sys(SysAgentConfig ++ SysMgrConfig), 142 ok. 143 144 145intro() -> 146 i("~nSimple SNMP configuration tool (version ~s)", [?version]), 147 i("------------------------------------------------"), 148 i("Note: Non-trivial configurations still has to be"), 149 i(" done manually. IP addresses may be entered "), 150 i(" as dront.ericsson.se (UNIX only) or"), 151 i(" 123.12.13.23"), 152 i("------------------------------------------------"), 153 ok. 154 155 156config_agent() -> 157 case (catch snmp_agent2()) of 158 ok -> 159 []; 160 {ok, SysConf} -> 161 SysConf; 162 {error, Reason} -> 163 error(Reason); 164 {'EXIT', Reason} -> 165 error(Reason); 166 E -> 167 error({agent_config_failed, E}) 168 end. 169 170snmp_agent2() -> 171 case ask("~nConfigure an agent (y/n)?", "y", fun verify_yes_or_no/1) of 172 yes -> 173 {Vsns, ConfigDir, SysConf} = config_agent_sys(), 174 config_agent_snmp(ConfigDir, Vsns), 175 {ok, SysConf}; 176 no -> 177 ok 178 end. 179 180 181config_manager() -> 182 case (catch config_manager2()) of 183 ok -> 184 []; 185 {ok, SysConf} -> 186 SysConf; 187 {error, Reason} -> 188 error(Reason); 189 {'EXIT', Reason} -> 190 error(Reason); 191 E -> 192 error({manager_config_failed, E}) 193 end. 194 195config_manager2() -> 196 case ask("~nConfigure a manager (y/n)?", "y", fun verify_yes_or_no/1) of 197 yes -> 198 {Vsns, ConfigDir, SysConf} = config_manager_sys(), 199 config_manager_snmp(ConfigDir, Vsns), 200 {ok, SysConf}; 201 no -> 202 ok 203 end. 204 205 206config_sys(SysConfig) -> 207 i("~n--------------------"), 208 {ok, DefDir} = file:get_cwd(), 209 ConfigDir = ask("Configuration directory for system file (absolute path)?", 210 DefDir, fun verify_dir/1), 211 write_sys_config_file(ConfigDir, SysConfig). 212 213 214%% ------------------- 215 216config_agent_sys() -> 217 i("~nAgent system config: " 218 "~n--------------------"), 219 Prio = ask("1. Agent process priority (low/normal/high)", 220 "normal", fun verify_prio/1), 221 Vsns = ask("2. What SNMP version(s) should be used " 222 "(1,2,3,1&2,1&2&3,2&3)?", "3", fun verify_versions/1), 223 %% d("Vsns: ~p", [Vsns]), 224 {ok, DefDir} = file:get_cwd(), 225 {DefConfDir, Warning} = default_agent_dir(DefDir), 226 ConfQ = 227 if 228 Warning == "" -> 229 "3. Configuration directory (absolute path)?"; 230 true -> 231 lists:flatten( 232 io_lib:format("3. Configuration directory (absolute path)?" 233 "~n ~s", [Warning])) 234 end, 235 ConfigDir = ask(ConfQ, DefConfDir, fun verify_dir/1), 236 ConfigVerb = ask("4. Config verbosity " 237 "(silence/info/log/debug/trace)?", 238 "silence", 239 fun verify_verbosity/1), 240 DbDir = ask("5. Database directory (absolute path)?", DefDir, 241 fun verify_dir/1), 242 DbInitError = ask("6. How to handle DB init error?", 243 "terminate", fun verify_db_init_error/1), 244 MibStorageType = ask("7. Mib storage type (ets/dets/mnesia)?", "ets", 245 fun verify_mib_storage_type/1), 246 MibStorage = 247 case MibStorageType of 248 ets -> 249 [{module, snmpa_mib_storage_ets}]; 250 dets -> 251 DetsDir = ask("7b. Mib storage directory (absolute path)?", 252 DbDir, fun verify_dir/1), 253 DetsAction = ask("7c. Mib storage [dets] database start " 254 "action " 255 "(default/clear/keep)?", 256 "default", fun verify_mib_storage_action/1), 257 case DetsAction of 258 default -> 259 [{module, snmpa_mib_storage_dets}, 260 {options, [{dir, DetsDir}]}]; 261 _ -> 262 [{module, snmpa_mib_storage_dets}, 263 {options, [{dir, DetsDir}, 264 {action, DetsAction}]}] 265 end; 266 mnesia -> 267 Nodes = [], 268 MnesiaAction = ask("7b. Mib storage [mnesia] database start " 269 "action " 270 "(default/clear/keep)?", 271 "default", fun verify_mib_storage_action/1), 272 case MnesiaAction of 273 default -> 274 [{module, snmpa_mib_storage_mnesia}, 275 {options, [{nodes, Nodes}]}]; 276 _ -> 277 [{module, snmpa_mib_storage_mnesia}, 278 {options, [{nodes, Nodes}, 279 {action, MnesiaAction}]}] 280 end 281 end, 282 283 %% Here we should ask about mib-server data module, 284 %% but as we only have one at the moment... 285 286 TargetCacheVerb = ask("8. Target cache verbosity " 287 "(silence/info/log/debug/trace)?", "silence", 288 fun verify_verbosity/1), 289 SymStoreVerb = ask("9. Symbolic store verbosity " 290 "(silence/info/log/debug/trace)?", "silence", 291 fun verify_verbosity/1), 292 LocalDbVerb = ask("10. Local DB verbosity " 293 "(silence/info/log/debug/trace)?", "silence", 294 fun verify_verbosity/1), 295 LocalDbRepair = ask("11. Local DB repair (true/false/force)?", "true", 296 fun verify_dets_repair/1), 297 LocalDbAutoSave = ask("12. Local DB auto save (infinity/milli seconds)?", 298 "5000", fun verify_dets_auto_save/1), 299 ErrorMod = ask("13. Error report module?", "snmpa_error_logger", fun verify_module/1), 300 Type = ask("14. Agent type (master/sub)?", "master", 301 fun verify_agent_type/1), 302 AgentConfig = 303 case Type of 304 master -> 305 MasterAgentVerb = ask("15. Master-agent verbosity " 306 "(silence/info/log/debug/trace)?", 307 "silence", 308 fun verify_verbosity/1), 309 ForceLoad = ask("16. Shall the agent re-read the " 310 "configuration files during startup ~n" 311 " (and ignore the configuration " 312 "database) (true/false)?", "true", 313 fun verify_bool/1), 314 MultiThreaded = ask("17. Multi threaded agent (true/false)?", 315 "false", 316 fun verify_bool/1), 317 MeOverride = ask("18. Check for duplicate mib entries when " 318 "installing a mib (true/false)?", "false", 319 fun verify_bool/1), 320 TrapOverride = ask("19. Check for duplicate trap names when " 321 "installing a mib (true/false)?", "false", 322 fun verify_bool/1), 323 MibServerVerb = ask("20. Mib server verbosity " 324 "(silence/info/log/debug/trace)?", 325 "silence", 326 fun verify_verbosity/1), 327 MibServerCache = ask("21. Mib server cache " 328 "(true/false)?", 329 "true", 330 fun verify_bool/1), 331 NoteStoreVerb = ask("22. Note store verbosity " 332 "(silence/info/log/debug/trace)?", 333 "silence", 334 fun verify_verbosity/1), 335 NoteStoreTimeout = ask("23. Note store GC timeout?", "30000", 336 fun verify_timeout/1), 337 ATL = 338 case ask("24. Shall the agent use an audit trail log " 339 "(y/n)?", 340 "n", fun verify_yes_or_no/1) of 341 yes -> 342 ATLType = ask("24b. Audit trail log type " 343 "(write/read_write)?", 344 "read_write", fun verify_atl_type/1), 345 ATLDir = ask("24c. Where to store the " 346 "audit trail log?", 347 DefDir, fun verify_dir/1), 348 ATLMaxFiles = ask("24d. Max number of files?", 349 "10", 350 fun verify_pos_integer/1), 351 ATLMaxBytes = ask("24e. Max size (in bytes) " 352 "of each file?", 353 "10240", 354 fun verify_pos_integer/1), 355 ATLSize = {ATLMaxBytes, ATLMaxFiles}, 356 ATLRepair = ask("24f. Audit trail log repair " 357 "(true/false/truncate/snmp_repair)?", "true", 358 fun verify_atl_repair/1), 359 ATLSeqNo = ask("24g. Audit trail log " 360 "sequence-numbering (true/false)?", 361 "false", 362 fun verify_atl_seqno/1), 363 [{audit_trail_log, [{type, ATLType}, 364 {dir, ATLDir}, 365 {size, ATLSize}, 366 {repair, ATLRepair}, 367 {seqno, ATLSeqNo}]}]; 368 no -> 369 [] 370 end, 371 NetIfVerb = ask("25. Network interface verbosity " 372 "(silence/info/log/debug/trace)?", 373 "silence", 374 fun verify_verbosity/1), 375 NetIfMod = ask("26. Which network interface module shall be used?", 376 "snmpa_net_if", fun verify_module/1), 377 NetIfOpts = 378 case NetIfMod of 379 snmpa_net_if -> 380 NetIfBindTo = 381 ask("26a. Bind the agent IP address " 382 "(true/false)?", 383 "false", fun verify_bool/1), 384 NetIfNoReuse = 385 ask("26b. Shall the agents " 386 "IP address " 387 "and port be not reusable " 388 "(true/false)?", 389 "false", fun verify_bool/1), 390 NetIfReqLimit = 391 ask("26c. Agent request limit " 392 "(used for flow control) " 393 "(infinity/pos integer)?", 394 "infinity", 395 fun verify_netif_req_limit/1), 396 NetIfRecbuf = 397 case ask("26d. Receive buffer size of the " 398 "agent (in bytes) " 399 "(default/pos integer)?", 400 "default", 401 fun verify_netif_recbuf/1) of 402 default -> 403 []; 404 RecBufSz -> 405 [{recbuf, RecBufSz}] 406 end, 407 NetIfSndbuf = 408 case ask("26e. Send buffer size of the agent " 409 "(in bytes) (default/pos integer)?", 410 "default", 411 fun verify_netif_sndbuf/1) of 412 default -> 413 []; 414 SndBufSz -> 415 [{sndbuf, SndBufSz}] 416 end, 417 NetIfFilter = 418 case ask("26f. Do you wish to specify a " 419 "network interface filter module " 420 "(or use default)", 421 "default", fun verify_module/1) of 422 default -> 423 []; 424 NetIfFilterMod -> 425 [{filter, [{module, NetIfFilterMod}]}] 426 end, 427 [{bind_to, NetIfBindTo}, 428 {no_reuse, NetIfNoReuse}, 429 {req_limit, NetIfReqLimit}] ++ 430 NetIfRecbuf ++ NetIfSndbuf ++ NetIfFilter; 431 _ -> 432 [] 433 end, 434 NetIf = [{module, NetIfMod}, 435 {verbosity, NetIfVerb}, 436 {options, NetIfOpts}], 437 TermDiscoEnable = ask("27. Allow terminating discovery " 438 "(true/false)?", "true", 439 fun verify_bool/1), 440 TermDiscoConf = 441 case TermDiscoEnable of 442 true -> 443 TermDiscoStage2 = 444 ask("27a. Second stage behaviour " 445 "(discovery/plain)?", "discovery", 446 fun verify_term_disco_behaviour/1), 447 TermDiscoTrigger = 448 ask("27b. Trigger username " 449 "(default/a string)?", "default", 450 fun verify_term_disco_trigger_username/1), 451 [{enable, TermDiscoEnable}, 452 {stage2, TermDiscoStage2}, 453 {trigger_username, TermDiscoTrigger}]; 454 false -> 455 [{enable, TermDiscoEnable}, 456 {stage2, discovery}, 457 {trigger_username, ""}] 458 end, 459 OrigDiscoEnable = ask("28. Allow originating discovery " 460 "(true/false)?", "true", 461 fun verify_bool/1), 462 OrigDiscoConf = 463 [{enable, OrigDiscoEnable}], 464 DiscoveryConfig = 465 [{terminating, TermDiscoConf}, 466 {originating, OrigDiscoConf}], 467 [{agent_type, master}, 468 {agent_verbosity, MasterAgentVerb}, 469 {discovery, DiscoveryConfig}, 470 {config, [{dir, ConfigDir}, 471 {force_load, ForceLoad}, 472 {verbosity, ConfigVerb}]}, 473 {multi_threaded, MultiThreaded}, 474 {mib_server, [{mibentry_override, MeOverride}, 475 {trapentry_override, TrapOverride}, 476 {verbosity, MibServerVerb}, 477 {cache, MibServerCache}]}, 478 {note_store, [{timeout, NoteStoreTimeout}, 479 {verbosity, NoteStoreVerb}]}, 480 {net_if, NetIf}] ++ ATL; 481 sub -> 482 SubAgentVerb = ask("15. Sub-agent verbosity " 483 "(silence/info/log/debug/trace)?", 484 "silence", 485 fun verify_verbosity/1), 486 [{agent_type, sub}, 487 {agent_verbosity, SubAgentVerb}, 488 {config, [{dir, ConfigDir}]}] 489 end, 490 SysConfig = 491 [{priority, Prio}, 492 {versions, Vsns}, 493 {db_dir, DbDir}, 494 {db_init_error, DbInitError}, 495 {mib_storage, MibStorage}, 496 {target_cache, [{verbosity, TargetCacheVerb}]}, 497 {symbolic_store, [{verbosity, SymStoreVerb}]}, 498 {local_db, [{repair, LocalDbRepair}, 499 {auto_save, LocalDbAutoSave}, 500 {verbosity, LocalDbVerb}]}, 501 {error_report_module, ErrorMod}] ++ AgentConfig, 502 {Vsns, ConfigDir, SysConfig}. 503 504 505config_agent_snmp(Dir, Vsns) -> 506 i("~nAgent snmp config: " 507 "~n------------------"), 508 AgentName = guess_agent_name(), 509 EngineName = guess_engine_name(), 510 SysName = ask("1. System name (sysName standard variable)", 511 AgentName, fun verify_system_name/1), 512 EngineID = ask("2. Engine ID (snmpEngineID standard variable)", 513 EngineName, fun verify_engine_id/1), 514 MMS = ask("3. Max message size?", "484", 515 fun verify_max_message_size/1), 516 AgentUDP = ask("4. The UDP port the agent listens to. " 517 "(standard 161)", 518 "4000", fun verify_port_number/1), 519 Host = host(), 520 AgentIP = ask("5. IP address for the agent (only used as id ~n" 521 " when sending traps)", Host, fun verify_address/1), 522 %% We intentionally skip TDomain... 523 %% If the user wish to use IPv6, the user must create an dummy entry here 524 %% and then manually edit these entries later. 525 ManagerIP = ask("6. IP address for the manager (only this manager ~n" 526 " will have access to the agent, traps are sent ~n" 527 " to this one)", Host, fun verify_address/1), 528 TrapUdp = ask("7. To what UDP port at the manager should traps ~n" 529 " be sent (standard 162)?", "5000", 530 fun verify_port_number/1), 531 SecType = ask("8. Do you want a none- minimum- or semi-secure" 532 " configuration? ~n" 533 " Note that if you chose v1 or v2, you won't get any" 534 " security for these~n" 535 " requests (none, minimum, semi_des, semi_aes)", 536 "minimum", 537 fun verify_sec_type/1), 538 Passwd = 539 case lists:member(v3, Vsns) and (SecType /= none) of 540 true -> 541 ensure_crypto_started(), 542 ask("8b. Give a password of at least length 8. It is used to " 543 "generate ~n" 544 " private keys for the configuration: ", 545 mandatory, fun verify_passwd/1); 546 false -> 547 "" 548 end, 549 NotifType = 550 case lists:member(v1, Vsns) of 551 true -> 552 Overwrite = ask("9. Current configuration files will " 553 "now be overwritten. " 554 "Ok (y/n)?", "y", fun verify_yes_or_no/1), 555 case Overwrite of 556 no -> 557 error(overwrite_not_allowed); 558 yes -> 559 ok 560 end, 561 trap; 562 false -> 563 NT = ask("9. Should notifications be sent as traps or informs " 564 "(trap/inform)?", "trap", fun verify_notif_type/1), 565 Overwrite = ask("10. Current configuration files will " 566 "now be overwritten. " 567 "Ok (y/n)?", "y", fun verify_yes_or_no/1), 568 case Overwrite of 569 no -> 570 error(overwrite_not_allowed); 571 yes -> 572 ok 573 end, 574 NT 575 end, 576 case (catch write_agent_snmp_files( 577 Dir, Vsns, ManagerIP, TrapUdp, AgentIP, AgentUDP, SysName, 578 NotifType, SecType, Passwd, EngineID, MMS)) of 579 ok -> 580 i("~n- - - - - - - - - - - - -"), 581 i("Info: 1. SecurityName \"initial\" has noAuthNoPriv read access~n" 582 " and authenticated write access to the \"restricted\"~n" 583 " subtree."), 584 i(" 2. SecurityName \"all-rights\" has noAuthNoPriv " 585 "read/write~n" 586 " access to the \"internet\" subtree."), 587 i(" 3. Standard traps are sent to the manager."), 588 case lists:member(v1, Vsns) or lists:member(v2, Vsns) of 589 true -> 590 i(" 4. Community \"public\" is mapped to security name" 591 " \"initial\"."), 592 i(" 5. Community \"all-rights\" is mapped to security" 593 " name \"all-rights\"."); 594 false -> 595 ok 596 end, 597 i("The following agent files where written: agent.conf, " 598 "community.conf,~n" 599 "standard.conf, target_addr.conf, " 600 "target_params.conf, ~n" 601 "notify.conf" ++ 602 case lists:member(v3, Vsns) of 603 true -> ", vacm.conf and usm.conf"; 604 false -> " and vacm.conf" 605 end), 606 i("- - - - - - - - - - - - -"), 607 ok; 608 E -> 609 error({failed_writing_files, E}) 610 end. 611 612 613%% ------------------- 614 615config_manager_sys() -> 616 i("~nManager system config: " 617 "~n----------------------"), 618 Prio = ask("1. Manager process priority (low/normal/high)", 619 "normal", fun verify_prio/1), 620 Vsns = ask("2. What SNMP version(s) should be used " 621 "(1,2,3,1&2,1&2&3,2&3)?", "3", fun verify_versions/1), 622 {ok, DefDir} = file:get_cwd(), 623 {DefConfDir, Warning} = default_manager_dir(DefDir), 624 ConfQ = 625 if 626 Warning == "" -> 627 "3. Configuration directory (absolute path)?"; 628 true -> 629 lists:flatten( 630 io_lib:format("3. Configuration directory (absolute path)?" 631 "~n ~s", [Warning])) 632 end, 633 ConfigDir = ask(ConfQ, DefConfDir, fun verify_dir/1), 634 ConfigVerb = ask("4. Config verbosity " 635 "(silence/info/log/debug/trace)?", 636 "silence", 637 fun verify_verbosity/1), 638 ConfigDbDir = ask("5. Database directory (absolute path)?", 639 DefDir, fun verify_dir/1), 640 ConfigDbInitError = ask("6. How to handle DB init error?", 641 "terminate", fun verify_db_init_error/1), 642 ConfigDbRepair = ask("7. Database repair " 643 "(true/false/force)?", "true", 644 fun verify_dets_repair/1), 645 ConfigDbAutoSave = ask("8. Database auto save " 646 "(infinity/milli seconds)?", 647 "5000", fun verify_dets_auto_save/1), 648 IRB = 649 case ask("9. Inform request behaviour (auto/user)?", 650 "auto", fun verify_irb/1) of 651 auto -> 652 auto; 653 user -> 654 case ask("9b. Use default GC timeout" 655 "(default/seconds)?", 656 "default", fun verify_irb_user/1) of 657 default -> 658 user; 659 IrbGcTo -> 660 {user, IrbGcTo} 661 end 662 end, 663 ServerVerb = ask("10. Server verbosity " 664 "(silence/info/log/debug/trace)?", 665 "silence", 666 fun verify_verbosity/1), 667 ServerTimeout = ask("11. Server GC timeout?", "30000", 668 fun verify_timeout/1), 669 NoteStoreVerb = ask("12. Note store verbosity " 670 "(silence/info/log/debug/trace)?", 671 "silence", 672 fun verify_verbosity/1), 673 NoteStoreTimeout = ask("13. Note store GC timeout?", "30000", 674 fun verify_timeout/1), 675 NetIfMod = ask("14. Which network interface module shall be used?", 676 "snmpm_net_if", fun verify_module/1), 677 NetIfVerb = ask("15. Network interface verbosity " 678 "(silence/info/log/debug/trace)?", "silence", 679 fun verify_verbosity/1), 680 NetIfBindTo = ask("16. Bind the manager IP address " 681 "(true/false)?", 682 "false", fun verify_bool/1), 683 NetIfNoReuse = ask("17. Shall the manager IP address and port " 684 "be not reusable (true/false)?", 685 "false", fun verify_bool/1), 686 NetIfRecbuf = 687 case ask("18. Receive buffer size of the manager (in bytes) " 688 "(default/pos integer)?", "default", 689 fun verify_netif_recbuf/1) of 690 default -> 691 []; 692 RecBufSz -> 693 [{recbuf, RecBufSz}] 694 end, 695 NetIfSndbuf = 696 case ask("19. Send buffer size of the manager (in bytes) " 697 "(default/pos integer)?", "default", 698 fun verify_netif_sndbuf/1) of 699 default -> 700 []; 701 SndBufSz -> 702 [{sndbuf, SndBufSz}] 703 end, 704 NetIfOpts = 705 [{bind_to, NetIfBindTo}, 706 {no_reuse, NetIfNoReuse}] ++ NetIfRecbuf ++ NetIfSndbuf, 707 NetIf = 708 [{module, NetIfMod}, 709 {verbosity, NetIfVerb}, 710 {options, NetIfOpts}], 711 ATL = 712 case ask("20. Shall the manager use an audit trail log " 713 "(y/n)?", 714 "n", fun verify_yes_or_no/1) of 715 yes -> 716 ATLType = ask("20b. Audit trail log type " 717 "(write/read_write)?", 718 "read_write", fun verify_atl_type/1), 719 ATLDir = ask("20c. Where to store the " 720 "audit trail log?", 721 DefDir, fun verify_dir/1), 722 ATLMaxFiles = ask("20d. Max number of files?", 723 "10", 724 fun verify_pos_integer/1), 725 ATLMaxBytes = ask("20e. Max size (in bytes) " 726 "of each file?", 727 "10240", 728 fun verify_pos_integer/1), 729 ATLSize = {ATLMaxBytes, ATLMaxFiles}, 730 ATLRepair = ask("20f. Audit trail log repair " 731 "(true/false/truncate/snmp_repair)?", "true", 732 fun verify_atl_repair/1), 733 ATLSeqNo = ask("20g. Audit trail log sequence-numbering " 734 "(true/false)?", "false", 735 fun verify_atl_seqno/1), 736 [{audit_trail_log, [{type, ATLType}, 737 {dir, ATLDir}, 738 {size, ATLSize}, 739 {repair, ATLRepair}, 740 {seqno, ATLSeqNo}]}]; 741 no -> 742 [] 743 end, 744 DefUser = 745 case ask("21. Do you wish to assign a default user [yes] or use~n" 746 " the default settings [no] (y/n)?", "n", 747 fun verify_yes_or_no/1) of 748 yes -> 749 DefUserMod = ask("21b. Default user module?", 750 "snmpm_user_default", 751 fun verify_module/1), 752 DefUserData = ask("21c. Default user data?", "undefined", 753 fun verify_user_data/1), 754 [{def_user_mod, DefUserMod}, 755 {def_user_data, DefUserData}]; 756 no -> 757 [] 758 end, 759 SysConfig = 760 [{priority, Prio}, 761 {versions, Vsns}, 762 {config, [{dir, ConfigDir}, 763 {db_dir, ConfigDbDir}, 764 {db_init_error, ConfigDbInitError}, 765 {repair, ConfigDbRepair}, 766 {auto_save, ConfigDbAutoSave}, 767 {verbosity, ConfigVerb}]}, 768 {inform_request_behaviour, IRB}, 769 {mibs, []}, 770 {server, [{timeout, ServerTimeout}, 771 {verbosity, ServerVerb}]}, 772 {note_store, [{timeout, NoteStoreTimeout}, 773 {verbosity, NoteStoreVerb}]}, 774 {net_if, NetIf}] ++ ATL ++ DefUser, 775 {Vsns, ConfigDir, SysConfig}. 776 777 778config_manager_snmp(Dir, Vsns) -> 779 i("~nManager snmp config: " 780 "~n--------------------"), 781 EngineName = guess_engine_name(), 782 EngineID = ask("1. Engine ID (snmpEngineID standard variable)", 783 EngineName, fun verify_engine_id/1), 784 MMS = ask("2. Max message size?", "484", 785 fun verify_max_message_size/1), 786 Host = host(), 787 IP = ask("3. IP address for the manager (only used as id ~n" 788 " when sending requests)", 789 Host, fun verify_address/1), 790 Port = ask("4. Port number (standard 162)?", "5000", 791 fun verify_port_number/1), 792 Users = config_manager_snmp_users([]), 793 Agents = config_manager_snmp_agents([]), 794 Usms = config_manager_snmp_usms([]), 795 Overwrite = ask("8. Current configuration files will now be overwritten. " 796 "Ok (y/n)?", "y", fun verify_yes_or_no/1), 797 case Overwrite of 798 no -> 799 error(overwrite_not_allowed); 800 yes -> 801 ok 802 end, 803 case (catch write_manager_snmp_files(Dir, 804 IP, Port, MMS, EngineID, 805 Users, Agents, Usms)) of 806 ok -> 807 i("~n- - - - - - - - - - - - -"), 808 i("The following manager files where written: " 809 "manager.conf, agents.conf " ++ 810 case lists:member(v3, Vsns) of 811 true -> 812 ", users.conf and usm.conf"; 813 false -> 814 " and users.conf" 815 end), 816 i("- - - - - - - - - - - - -"), 817 ok; 818 E -> 819 error({failed_writing_files, E}) 820 end. 821 822 823config_manager_snmp_users(Users) -> 824 case ask("5. Configure a user of this manager (y/n)?", 825 "y", fun verify_yes_or_no/1) of 826 yes -> 827 User = config_manager_snmp_user(), 828 config_manager_snmp_users([User|Users]); 829 no -> 830 lists:reverse(Users) 831 end. 832 833config_manager_snmp_user() -> 834 UserId = ask("5b. User id?", mandatory, 835 fun verify_user_id/1), 836 UserMod = ask("5c. User callback module?", mandatory, 837 fun verify_module/1), 838 UserData = ask("5d. User (callback) data?", "undefined", 839 fun verify_user_data/1), 840 {UserId, UserMod, UserData}. 841 842 843config_manager_snmp_agents(Agents) -> 844 case ask("6. Configure an agent handled by this manager (y/n)?", 845 "y", fun verify_yes_or_no/1) of 846 yes -> 847 Agent = config_manager_snmp_agent(), 848 config_manager_snmp_agents([Agent|Agents]); 849 no -> 850 lists:reverse(Agents) 851 end. 852 853config_manager_snmp_agent() -> 854 UserId = ask("6b. User id?", mandatory, 855 fun verify_user_id/1), 856 TargetName = ask("6c. Target name?", guess_agent_name(), 857 fun verify_system_name/1), 858 Version = ask("6d. Version (1/2/3)?", "1", 859 fun verify_version/1), 860 Comm = ask("6e. Community string ?", "public", 861 fun verify_community/1), 862 EngineID = ask("6f. Engine ID (snmpEngineID standard variable)", 863 guess_engine_name(), fun verify_engine_id/1), 864 IP = ask("6g. IP address for the agent", host(), 865 fun verify_address/1), 866 Port = ask("6h. The UDP port the agent listens to. " 867 "(standard 161)", "4000", fun verify_port_number/1), 868 Timeout = ask("6i. Retransmission timeout (infinity/pos integer)?", 869 "infinity", fun verify_retransmission_timeout/1), 870 MMS = ask("6j. Max message size?", "484", 871 fun verify_max_message_size/1), 872 SecModel = ask("6k. Security model (any/v1/v2c/usm)?", "any", 873 fun verify_sec_model/1), 874 SecName = ask("6l. Security name?", "\"initial\"", 875 fun verify_sec_name/1), 876 SecLevel = ask("6m. Security level (noAuthNoPriv/authNoPriv/authPriv)?", 877 "noAuthNoPriv", fun verify_sec_level/1), 878 {UserId, 879 TargetName, Comm, IP, Port, EngineID, Timeout, MMS, 880 Version, SecModel, SecName, SecLevel}. 881 882 883config_manager_snmp_usms(Usms) -> 884 case ask("7. Configure an usm user handled by this manager (y/n)?", 885 "y", fun verify_yes_or_no/1) of 886 yes -> 887 Usm = config_manager_snmp_usm(), 888 config_manager_snmp_usms([Usm|Usms]); 889 no -> 890 lists:reverse(Usms) 891 end. 892 893config_manager_snmp_usm() -> 894 EngineID = ask("7a. Engine ID", guess_engine_name(), 895 fun verify_engine_id/1), 896 UserName = ask("7b. User name?", mandatory, fun verify_usm_name/1), 897 SecName = ask("7c. Security name?", UserName, 898 fun verify_usm_sec_name/1), 899 AuthP = ask("7d. Authentication protocol (no/sha/md5)?", "no", 900 fun verify_usm_auth_protocol/1), 901 AuthKey = ask_auth_key("7e", AuthP), 902 PrivP = ask("7e. Priv protocol (no/des/aes)?", "no", 903 fun verify_usm_priv_protocol/1), 904 PrivKey = ask_priv_key("7f", PrivP), 905 {EngineID, UserName, 906 SecName, AuthP, AuthKey, PrivP, PrivKey}. 907 908 909%% ------------------------------------------------------------------ 910 911is_members([], _Files) -> 912 true; 913is_members([H|T], Files) -> 914 lists:member(H, Files) andalso is_members(T, Files). 915 916default_agent_dir(DefDir) -> 917 default_dir("agent", DefDir). 918 919default_manager_dir(DefDir) -> 920 default_dir("manager", DefDir). 921 922default_dir(Component, DefDir) -> 923 %% Look for the component dir, if found use that as default 924 {ok, Files} = file:list_dir(DefDir), 925 case lists:member(Component, Files) of 926 true -> 927 {filename:join(DefDir, Component), ""}; 928 false -> 929 %% No luck, 930 %% so check if cwd contains either agent and/or 931 %% manager config files. If it does, issue a warning 932 933 %% Check for presence of agent config files 934 %% If all the agent config files are present, 935 %% issue a warning 936 AgentConfs = 937 [ 938 "agent.conf", 939 "context.conf", 940 "community.conf", 941 "notify.conf", 942 "standard.conf", 943 "target_params.conf", 944 "target_addr.conf", 945 "usm.conf", 946 "vacm.conf" 947 ], 948 IsAgentDir = is_members(AgentConfs, Files), 949 950 %% Check for presence of manager config files 951 %% If all the manager config files are present, 952 %% issue a warning 953 ManagerConfs = 954 [ 955 "agents.conf", 956 "manager.conf", 957 "users.conf", 958 "usm.conf" 959 ], 960 IsManagerDir = is_members(ManagerConfs, Files), 961 Warning = 962 if 963 IsAgentDir and IsManagerDir -> 964 "Note that the default directory contains both agent and manager config files"; 965 IsAgentDir -> 966 "Note that the default directory contains agent config files"; 967 IsManagerDir -> 968 "Note that the default directory contains manager config files"; 969 true -> 970 "" 971 end, 972 {DefDir, Warning} 973 end. 974 975 976%% ------------------------------------------------------------------ 977 978ask_auth_key(_Prefix, usmNoAuthProtocol) -> 979 ""; 980ask_auth_key(Prefix, usmHMACSHAAuthProtocol) -> 981 ask(Prefix ++ " Authentication [sha] key (length 0 or 20)?", "\"\"", 982 fun verify_usm_auth_sha_key/1); 983ask_auth_key(Prefix, usmHMACMD5AuthProtocol) -> 984 ask(Prefix ++ " Authentication [md5] key (length 0 or 16)?", "\"\"", 985 fun verify_usm_auth_md5_key/1). 986 987ask_priv_key(_Prefix, usmNoPrivProtocol) -> 988 ""; 989ask_priv_key(Prefix, usmDESPrivProtocol) -> 990 ask(Prefix ++ " Priv [des] key (length 0 or 16)?", "\"\"", 991 fun verify_usm_priv_des_key/1); 992ask_priv_key(Prefix, usmAesCfb128Protocol) -> 993 ask(Prefix ++ " Priv [aes] key (length 0 or 16)?", "\"\"", 994 fun verify_usm_priv_aes_key/1). 995 996 997%% ------------------------------------------------------------------ 998 999verify_yes_or_no("y") -> 1000 {ok, yes}; 1001verify_yes_or_no("yes") -> 1002 {ok, yes}; 1003verify_yes_or_no("n") -> 1004 {ok, no}; 1005verify_yes_or_no("no") -> 1006 {ok, no}; 1007verify_yes_or_no(YON) -> 1008 {error, "invalid yes or no: " ++ YON}. 1009 1010 1011verify_prio("low") -> 1012 {ok, low}; 1013verify_prio("normal") -> 1014 {ok, normal}; 1015verify_prio("high") -> 1016 {ok, high}; 1017verify_prio(Prio) -> 1018 {error, "invalid process priority: " ++ Prio}. 1019 1020 1021verify_system_name(Name) -> {ok, Name}. 1022 1023 1024verify_engine_id(Name) -> {ok, Name}. 1025 1026 1027verify_max_message_size(MMS) -> 1028 case (catch list_to_integer(MMS)) of 1029 I when is_integer(I) andalso (I >= 484) -> 1030 {ok, I}; 1031 I when is_integer(I) -> 1032 {error, "invalid max message size (must be atleast 484): " ++ MMS}; 1033 _ -> 1034 {error, "invalid max message size: " ++ MMS} 1035 end. 1036 1037 1038verify_port_number(P) -> 1039 case (catch list_to_integer(P)) of 1040 N when is_integer(N) andalso (N > 0) -> 1041 {ok, N}; 1042 _ -> 1043 {error, "invalid port number: " ++ P} 1044 end. 1045 1046 1047verify_versions("1") -> {ok, [v1]}; 1048verify_versions("2") -> {ok, [v2]}; 1049verify_versions("3") -> {ok, [v3]}; 1050verify_versions("1&2") -> {ok, [v1,v2]}; 1051verify_versions("1&3") -> {ok, [v1,v3]}; 1052verify_versions("2&3") -> {ok, [v2,v3]}; 1053verify_versions("1&2&3") -> {ok, [v1,v2,v3]}; 1054verify_versions(V) -> {error, "incorrect version(s): " ++ V}. 1055 1056verify_version("1") -> {ok, v1}; 1057verify_version("2") -> {ok, v2}; 1058verify_version("3") -> {ok, v3}; 1059verify_version(V) -> {error, "incorrect version: " ++ V}. 1060 1061 1062verify_passwd(Passwd) when length(Passwd) >= 8 -> 1063 {ok, Passwd}; 1064verify_passwd(_P) -> 1065 {error, "invalid password"}. 1066 1067 1068verify_dir(Dir) -> 1069 case filename:pathtype(Dir) of 1070 absolute -> 1071 case file:read_file_info(Dir) of 1072 {ok, #file_info{type = directory}} -> 1073 {ok, snmp_misc:ensure_trailing_dir_delimiter(Dir)}; 1074 {ok, _FileInfo} -> 1075 {error, Dir ++ " is not a directory"}; 1076 _ -> 1077 {error, "invalid directory: " ++ Dir} 1078 end; 1079 _E -> 1080 {error, "invalid directory (not absolute): " ++ Dir} 1081 end. 1082 1083 1084verify_db_init_error("terminate") -> 1085 {ok, true}; 1086verify_db_init_error("create") -> 1087 {ok, create}; 1088verify_db_init_error("create_db_and_dir") -> 1089 {ok, create_db_and_dir}; 1090verify_db_init_error(R) -> 1091 {error, "invalid DB init error: " ++ R}. 1092 1093 1094verify_notif_type("trap") -> {ok, trap}; 1095verify_notif_type("inform") -> {ok, inform}; 1096verify_notif_type(NT) -> {error, "invalid notifcation type: " ++ NT}. 1097 1098 1099verify_sec_type("none") -> {ok, none}; 1100verify_sec_type("minimum") -> {ok, minimum}; 1101verify_sec_type("semi_des") -> {ok, {semi, des}}; 1102verify_sec_type("semi_aes") -> {ok, {semi, aes}}; 1103verify_sec_type(ST) -> {error, "invalid security type: " ++ ST}. 1104 1105 1106verify_address(A) -> 1107 verify_address(A, snmpUDPDomain). 1108 1109verify_address(A, snmpUDPDomain = _Domain) -> 1110 do_verify_address(A, inet); 1111verify_address(A, transportDomainUdpIpv4 = _Domain) -> 1112 do_verify_address(A, inet); 1113verify_address(A, transportDomainUdpIpv6 = _Domain) -> 1114 do_verify_address(A, inet6). 1115 1116do_verify_address(A, Family) -> 1117 case (catch snmp_misc:ip(A, Family)) of 1118 {ok, IP} -> 1119 {ok, tuple_to_list(IP)}; 1120 {error, _} -> 1121 {error, "invalid address: " ++ A}; 1122 _E -> 1123 {error, "invalid address: " ++ A} 1124 end. 1125 1126 1127verify_mib_storage_type("m") -> 1128 {ok, mnesia}; 1129verify_mib_storage_type("mnesia") -> 1130 {ok, mnesia}; 1131verify_mib_storage_type("d") -> 1132 {ok, dets}; 1133verify_mib_storage_type("dets") -> 1134 {ok, dets}; 1135verify_mib_storage_type("e") -> 1136 {ok, ets}; 1137verify_mib_storage_type("ets") -> 1138 {ok, ets}; 1139verify_mib_storage_type(T) -> 1140 {error, "invalid mib storage type: " ++ T}. 1141 1142verify_mib_storage_action("default") -> 1143 {ok, default}; 1144verify_mib_storage_action("clear") -> 1145 {ok, clear}; 1146verify_mib_storage_action("keep") -> 1147 {ok, keep}; 1148verify_mib_storage_action(A) -> 1149 {error, "invalid mib storage action: " ++ A}. 1150 1151 1152verify_verbosity("silence") -> 1153 {ok, silence}; 1154verify_verbosity("info") -> 1155 {ok, info}; 1156verify_verbosity("log") -> 1157 {ok, log}; 1158verify_verbosity("debug") -> 1159 {ok, debug}; 1160verify_verbosity("trace") -> 1161 {ok, trace}; 1162verify_verbosity(V) -> 1163 {error, "invalid verbosity: " ++ V}. 1164 1165 1166verify_dets_repair("true") -> 1167 {ok, true}; 1168verify_dets_repair("false") -> 1169 {ok, false}; 1170verify_dets_repair("force") -> 1171 {ok, force}; 1172verify_dets_repair(R) -> 1173 {error, "invalid repair: " ++ R}. 1174 1175verify_dets_auto_save("infinity") -> 1176 {ok, infinity}; 1177verify_dets_auto_save(I0) -> 1178 case (catch list_to_integer(I0)) of 1179 I when is_integer(I) andalso (I > 0) -> 1180 {ok, I}; 1181 _ -> 1182 {error, "invalid auto save timeout time: " ++ I0} 1183 end. 1184 1185 1186%% I know that this is a little of the edge, but... 1187verify_module(M) when is_atom(M) -> 1188 {ok, M}; 1189verify_module(M0) when is_list(M0) -> 1190 {ok, list_to_atom(M0)}; 1191verify_module(M0) -> 1192 {error, lists:flatten(io_lib:format("invalid module: ~p", [M0]))}. 1193 1194%% verify_module(M0) -> 1195%% case (catch list_to_atom(M0)) of 1196%% M when is_atom(M) -> 1197%% {ok, M}; 1198%% _ -> 1199%% {error, "invalid module: " ++ M0} 1200%% end. 1201 1202 1203verify_agent_type("master") -> 1204 {ok, master}; 1205verify_agent_type("sub") -> 1206 {ok, sub}; 1207verify_agent_type(AT) -> 1208 {error, "invalid agent type: " ++ AT}. 1209 1210 1211verify_bool("true") -> 1212 {ok, true}; 1213verify_bool("false") -> 1214 {ok, false}; 1215verify_bool(B) -> 1216 {error, "invalid boolean: " ++ B}. 1217 1218 1219verify_timeout(T0) -> 1220 case (catch list_to_integer(T0)) of 1221 T when is_integer(T) andalso (T > 0) -> 1222 {ok, T}; 1223 _ -> 1224 {error, "invalid timeout time: '" ++ T0 ++ "'"} 1225 end. 1226 1227 1228verify_retransmission_timeout("infinity") -> 1229 {ok, infinity}; 1230verify_retransmission_timeout([${|R] = Timer) -> 1231 case lists:reverse(R) of 1232 [$}|R2] -> 1233 case string:tokens(lists:reverse(R2), ", ") of 1234 [WaitForStr, FactorStr, IncrStr, RetryStr] -> 1235 WaitFor = incr_timer_value(WaitForStr, 1), 1236 Factor = incr_timer_value(FactorStr, 1), 1237 Incr = incr_timer_value(IncrStr, 0), 1238 Retry = incr_timer_value(RetryStr, 0), 1239 {ok, {WaitFor, Factor, Incr, Retry}}; 1240 _ -> 1241 {error, "invalid retransmission timer: '" ++ Timer ++ "'"} 1242 end; 1243 _ -> 1244 {error, "invalid retransmission timer: '" ++ Timer ++ "'"} 1245 end; 1246verify_retransmission_timeout(T0) -> 1247 case (catch list_to_integer(T0)) of 1248 T when is_integer(T) andalso (T > 0) -> 1249 {ok, T}; 1250 _ -> 1251 {error, "invalid timeout time: '" ++ T0 ++ "'"} 1252 end. 1253 1254incr_timer_value(Str, Min) -> 1255 case (catch list_to_integer(Str)) of 1256 I when is_integer(I) andalso (I >= Min) -> 1257 I; 1258 I when is_integer(I) -> 1259 E = lists:flatten(io_lib:format("invalid incremental timer value " 1260 "(min value is ~w): " ++ Str, 1261 [Min])), 1262 error(E); 1263 _ -> 1264 error("invalid incremental timer value: " ++ Str) 1265 end. 1266 1267 1268%% verify_atl_type("read") -> 1269%% {ok, read}; 1270verify_atl_type("write") -> 1271 {ok, write}; 1272verify_atl_type("read_write") -> 1273 {ok, read_write}; 1274verify_atl_type(T) -> 1275 {error, "invalid log type: " ++ T}. 1276 1277verify_atl_repair("true") -> 1278 {ok, true}; 1279verify_atl_repair("false") -> 1280 {ok, false}; 1281verify_atl_repair("truncate") -> 1282 {ok, truncate}; 1283verify_atl_repair("snmp_repair") -> 1284 {ok, snmp_repair}; 1285verify_atl_repair(R) -> 1286 {error, "invalid audit trail log repair: " ++ R}. 1287 1288verify_atl_seqno("true") -> 1289 {ok, true}; 1290verify_atl_seqno("false") -> 1291 {ok, false}; 1292verify_atl_seqno(SN) -> 1293 {error, "invalid audit trail log seqno: " ++ SN}. 1294 1295 1296verify_pos_integer(I0) -> 1297 case (catch list_to_integer(I0)) of 1298 I when is_integer(I) andalso (I > 0) -> 1299 {ok, I}; 1300 _ -> 1301 {error, "invalid integer value: " ++ I0} 1302 end. 1303 1304 1305verify_netif_req_limit("infinity") -> 1306 {ok, infinity}; 1307verify_netif_req_limit(I0) -> 1308 case (catch list_to_integer(I0)) of 1309 I when is_integer(I) andalso (I > 0) -> 1310 {ok, I}; 1311 _ -> 1312 {error, "invalid network interface request limit: " ++ I0} 1313 end. 1314 1315verify_netif_recbuf(Val) -> 1316 verify_netif_recbuf_or_sndbuf(Val, "recbuf"). 1317 1318verify_netif_sndbuf(Val) -> 1319 verify_netif_recbuf_or_sndbuf(Val, "sndbuf"). 1320 1321verify_netif_recbuf_or_sndbuf("default", _) -> 1322 {ok, default}; 1323verify_netif_recbuf_or_sndbuf(I0, Buf) -> 1324 case (catch list_to_integer(I0)) of 1325 I when is_integer(I) andalso (I > 0) -> 1326 {ok, I}; 1327 _ -> 1328 {error, "invalid network interface " ++ Buf ++ " size: " ++ I0} 1329 end. 1330 1331 1332verify_irb("auto") -> 1333 {ok, auto}; 1334verify_irb("user") -> 1335 {ok, user}; 1336verify_irb(IRB) -> 1337 E = lists:flatten(io_lib:format("invalid irb: ~p", [IRB])), 1338 {error, E}. 1339 1340 1341verify_irb_user("default") -> 1342 {ok, default}; 1343verify_irb_user(TO) -> 1344 case (catch list_to_integer(TO)) of 1345 I when is_integer(I) andalso (I > 0) -> 1346 {ok, I*1000}; % Time is given in seconds 1347 _ -> 1348 {error, "invalid IRB GC time: " ++ TO} 1349 end. 1350 1351 1352verify_term_disco_behaviour("discovery") -> 1353 {ok, discovery}; 1354verify_term_disco_behaviour("plain") -> 1355 {ok, plain}; 1356verify_term_disco_behaviour(B) -> 1357 {error, "invalid terminating discovery behaviour: " ++ B}. 1358 1359verify_term_disco_trigger_username("default") -> 1360 {ok, ""}; 1361verify_term_disco_trigger_username(Trigger) -> 1362 {ok, Trigger}. 1363 1364 1365verify_user_id(UserId) when is_list(UserId) -> 1366 case (catch list_to_atom(UserId)) of 1367 A when is_atom(A) -> 1368 {ok, A}; 1369 _ -> 1370 {error, "invalid user id: " ++ UserId} 1371 end; 1372verify_user_id(UserId) when is_atom(UserId) -> 1373 {ok, UserId}; 1374verify_user_id(UserId) -> 1375 E = lists:flatten(io_lib:format("invalid user id: ~p", [UserId])), 1376 {error, E}. 1377 1378verify_user_data("undefined") -> 1379 {ok, undefined}; 1380verify_user_data(UserData) -> 1381 {ok, UserData}. 1382 1383 1384verify_community("\"\"") -> 1385 {ok, ""}; 1386verify_community(Comm) -> 1387 {ok, Comm}. 1388 1389 1390% verify_context_name("\"\"") -> 1391% {ok, ""}; 1392% verify_context_name(Ctx) -> 1393% {ok, Ctx}. 1394 1395 1396% verify_mp_model("v1") -> 1397% {ok, v1}; 1398% verify_mp_model("v2c") -> 1399% {ok, v2c}; 1400% verify_mp_model("v3") -> 1401% {ok, v3}; 1402% verify_mp_model(M) -> 1403% {error, "invalid mp model: " ++ M}. 1404 1405 1406verify_sec_model("any") -> 1407 {ok, any}; 1408verify_sec_model("v1") -> 1409 {ok, v1}; 1410verify_sec_model("v2c") -> 1411 {ok, v2c}; 1412verify_sec_model("usm") -> 1413 {ok, usm}; 1414verify_sec_model(M) -> 1415 {error, "invalid sec model: " ++ M}. 1416 1417verify_sec_name("\"initial\"") -> 1418 {ok, "initial"}; 1419verify_sec_name(N) -> 1420 {ok, N}. 1421 1422 1423verify_sec_level("noAuthNoPriv") -> 1424 {ok, noAuthNoPriv}; 1425verify_sec_level("authNoPriv") -> 1426 {ok, authNoPriv}; 1427verify_sec_level("authPriv") -> 1428 {ok, authPriv}; 1429verify_sec_level(L) -> 1430 {error, "invalid sec level: " ++ L}. 1431 1432 1433verify_usm_name(Name) -> 1434 {ok, Name}. 1435 1436verify_usm_sec_name(Name) -> 1437 {ok, Name}. 1438 1439 1440verify_usm_auth_protocol("no") -> 1441 {ok, usmNoAuthProtocol}; 1442verify_usm_auth_protocol("sha") -> 1443 {ok, usmHMACSHAAuthProtocol}; 1444verify_usm_auth_protocol("md5") -> 1445 {ok, usmHMACMD5AuthProtocol}; 1446verify_usm_auth_protocol(AuthP) -> 1447 {error, "invalid auth protocol: " ++ AuthP}. 1448 1449verify_usm_auth_sha_key(Key) -> 1450 verify_usm_key("auth sha", Key, 20). 1451 1452verify_usm_auth_md5_key(Key) -> 1453 verify_usm_key("auth md5", Key, 16). 1454 1455verify_usm_priv_protocol("no") -> 1456 {ok, usmNoPrivProtocol}; 1457verify_usm_priv_protocol("des") -> 1458 {ok, usmDESPrivProtocol}; 1459verify_usm_priv_protocol("aes") -> 1460 {ok, usmAesCfb128Protocol}; 1461verify_usm_priv_protocol(AuthP) -> 1462 {error, "invalid priv protocol: " ++ AuthP}. 1463 1464verify_usm_priv_des_key(Key) -> 1465 verify_usm_key("priv des", Key, 16). 1466 1467verify_usm_priv_aes_key(Key) -> 1468 verify_usm_key("priv aes", Key, 16). 1469 1470verify_usm_key(_What, "\"\"", _ExpectLength) -> 1471 {ok, ""}; 1472verify_usm_key(_What, Key, ExpectLength) when length(Key) =:= ExpectLength -> 1473 {ok, Key}; 1474verify_usm_key(What, [$[|RestKey] = Key0, ExpectLength) -> 1475 case lists:reverse(RestKey) of 1476 [$]|RevRestKey] -> 1477 Key1 = lists:reverse(RevRestKey), 1478 verify_usm_key2(What, Key1, ExpectLength); 1479 _ -> 1480 %% Its not a list ([...]) and its not the correct length, ... 1481 {error, "invalid " ++ What ++ " key length: " ++ Key0} 1482 end; 1483verify_usm_key(What, Key, ExpectLength) -> 1484 verify_usm_key2(What, Key, ExpectLength). 1485 1486verify_usm_key2(What, Key0, ExpectLength) -> 1487 case string:tokens(Key0, [$,]) of 1488 Key when length(Key) =:= ExpectLength -> 1489 convert_usm_key(Key, []); 1490 _ -> 1491 {error, "invalid " ++ What ++ " key length: " ++ Key0} 1492 end. 1493 1494convert_usm_key([], Acc) -> 1495 {ok, lists:reverse(Acc)}; 1496convert_usm_key([I|Is], Acc) -> 1497 case (catch list_to_integer(I)) of 1498 Int when is_integer(Int) -> 1499 convert_usm_key(Is, [Int|Acc]); 1500 _Err -> 1501 {error, "invalid key number: " ++ I} 1502 end. 1503 1504 1505% ip(Host) -> 1506% case catch snmp_misc:ip(Host) of 1507% {ok, IPtuple} -> tuple_to_list(IPtuple); 1508% {error, Reason} -> throw({error, Reason}); 1509% _Q -> throw({error, {"ip conversion failed", Host}}) 1510% end. 1511 1512% make_ip(Str) -> 1513% case catch snmp_misc:ip(Str) of 1514% {ok, IPtuple} -> tuple_to_list(IPtuple); 1515% _Q -> ip(Str) 1516% end. 1517 1518 1519print_q(Q, mandatory) -> 1520 io:format(Q ++ " ",[]); 1521print_q(Q, Default) when is_list(Default) -> 1522 io:format(Q ++ " [~s] ",[Default]). 1523 1524%% Defval = string() | mandatory 1525ask(Q, Default, Verify) when is_list(Q) andalso is_function(Verify) -> 1526 print_q(Q, Default), 1527 PrelAnsw = io:get_line(''), 1528 Answer = 1529 case remove_newline(PrelAnsw) of 1530 "" when Default =/= mandatory -> Default; 1531 "" -> ask(Q, Default, Verify); 1532 A -> A 1533 end, 1534 case (catch Verify(Answer)) of 1535 {ok, Answer2} -> 1536 Answer2; 1537 {error, ReasonStr} -> 1538 i("ERROR: " ++ ReasonStr), 1539 ask(Q, Default, Verify) 1540 end. 1541 1542 1543host() -> 1544 case (catch inet:gethostname()) of 1545 {ok, Name} -> 1546 case (catch inet:getaddr(Name, inet)) of 1547 {ok, Addr} when is_tuple(Addr) -> 1548 lists:flatten( 1549 io_lib:format("~w.~w.~w.~w", tuple_to_list(Addr))); 1550 _ -> 1551 "127.0.0.1" 1552 end; 1553 _ -> 1554 "127.0.0.1" 1555 end. 1556 1557guess_agent_name() -> 1558 case os:type() of 1559 {unix, _} -> 1560 lists:append(remove_newline(os:cmd("echo $USER")), "'s agent"); 1561 {_,_} -> "my agent" 1562 end. 1563 1564guess_engine_name() -> 1565 case os:type() of 1566 {unix, _} -> 1567 lists:append(remove_newline(os:cmd("echo $USER")), "'s engine"); 1568 {_,_} -> "my engine" 1569 end. 1570 1571% guess_user_id() -> 1572% case os:type() of 1573% {unix, _} -> 1574% lists:append(remove_newline(os:cmd("echo $USER")), "'s user"); 1575% {_,_} -> "user_id" 1576% end. 1577 1578 1579remove_newline(Str) -> 1580 lists:delete($\n, Str). 1581 1582 1583%%====================================================================== 1584%% File generation 1585%%====================================================================== 1586 1587write_agent_snmp_files( 1588 Dir, Vsns, Domain, ManagerAddr, AgentAddr, SysName) 1589 when is_list(Dir), 1590 is_list(Vsns), 1591 is_atom(Domain), 1592 is_list(SysName) -> 1593 write_agent_snmp_files( 1594 Dir, Vsns, Domain, ManagerAddr, AgentAddr, SysName, 1595 trap, none, "", "agentEngine", 484). 1596 1597%%---------------------------------------------------------------------- 1598%% Dir: string() (ex: "../conf/") 1599%% ManagerIP, AgentIP: [int(),int(),int(),int()] 1600%% TrapUdp, AgentUDP: integer() 1601%% SysName: string() 1602%%---------------------------------------------------------------------- 1603write_agent_snmp_files( 1604 Dir, Vsns, ManagerIP, TrapUDP, AgentIP, AgentUDP, SysName) 1605 when is_list(Dir) andalso 1606 is_list(Vsns) andalso 1607 is_list(ManagerIP) andalso 1608 is_integer(TrapUDP) andalso 1609 is_list(AgentIP) andalso 1610 is_integer(AgentUDP) andalso 1611 is_list(SysName) -> 1612 write_agent_snmp_files( 1613 Dir, Vsns, ManagerIP, TrapUDP, AgentIP, AgentUDP, SysName, 1614 trap, none, "", "agentEngine", 484). 1615 1616%% 1617%% ----- Agent config files generator functions ----- 1618%% 1619 1620write_agent_snmp_files( 1621 Dir, Vsns, Domain, ManagerAddr, AgentAddr, SysName, 1622 NotifType, SecType, Passwd, EngineID, MMS) -> 1623 write_agent_snmp_conf(Dir, Domain, AgentAddr, EngineID, MMS), 1624 write_agent_snmp_context_conf(Dir), 1625 write_agent_snmp_community_conf(Dir), 1626 write_agent_snmp_standard_conf(Dir, SysName), 1627 write_agent_snmp_target_addr_conf(Dir, Domain, ManagerAddr, Vsns), 1628 write_agent_snmp_target_params_conf(Dir, Vsns), 1629 write_agent_snmp_notify_conf(Dir, NotifType), 1630 write_agent_snmp_usm_conf(Dir, Vsns, EngineID, SecType, Passwd), 1631 write_agent_snmp_vacm_conf(Dir, Vsns, SecType), 1632 ok. 1633 1634write_agent_snmp_files( 1635 Dir, Vsns, ManagerIP, TrapUDP, AgentIP, AgentUDP, SysName, 1636 NotifType, SecType, Passwd, EngineID, MMS) -> 1637 Domain = snmp_target_mib:default_domain(), 1638 ManagerAddr = {ManagerIP, TrapUDP}, 1639 write_agent_snmp_conf(Dir, AgentIP, AgentUDP, EngineID, MMS), 1640 write_agent_snmp_context_conf(Dir), 1641 write_agent_snmp_community_conf(Dir), 1642 write_agent_snmp_standard_conf(Dir, SysName), 1643 write_agent_snmp_target_addr_conf(Dir, Domain, ManagerAddr, Vsns), 1644 write_agent_snmp_target_params_conf(Dir, Vsns), 1645 write_agent_snmp_notify_conf(Dir, NotifType), 1646 write_agent_snmp_usm_conf(Dir, Vsns, EngineID, SecType, Passwd), 1647 write_agent_snmp_vacm_conf(Dir, Vsns, SecType), 1648 ok. 1649 1650 1651 1652%% 1653%% ------ [agent] agent.conf ------ 1654%% 1655 1656write_agent_snmp_conf(Dir, Transports, EngineID, MMS) -> 1657 Conf = 1658 [{intAgentTransports, Transports}, 1659 {snmpEngineID, EngineID}, 1660 {snmpEngineMaxMessageSize, MMS}], 1661 do_write_agent_snmp_conf(Dir, Conf). 1662 1663write_agent_snmp_conf(Dir, Domain, AgentAddr, EngineID, MMS) 1664 when is_atom(Domain) -> 1665 {AgentIP, AgentUDP} = AgentAddr, 1666 Conf = 1667 [{intAgentTransportDomain, Domain}, 1668 {intAgentUDPPort, AgentUDP}, 1669 {intAgentIpAddress, AgentIP}, 1670 {snmpEngineID, EngineID}, 1671 {snmpEngineMaxMessageSize, MMS}], 1672 do_write_agent_snmp_conf(Dir, Conf); 1673write_agent_snmp_conf(Dir, AgentIP, AgentUDP, EngineID, MMS) 1674 when is_integer(AgentUDP) -> 1675 Conf = 1676 [{intAgentUDPPort, AgentUDP}, 1677 {intAgentIpAddress, AgentIP}, 1678 {snmpEngineID, EngineID}, 1679 {snmpEngineMaxMessageSize, MMS}], 1680 do_write_agent_snmp_conf(Dir, Conf); 1681write_agent_snmp_conf(_Dir, Domain, AgentAddr, _EngineID, _MMS) -> 1682 error({bad_address, {Domain, AgentAddr}}). 1683 1684do_write_agent_snmp_conf(Dir, Conf) -> 1685 Comment = 1686"%% This file defines the Agent local configuration info\n" 1687"%% The data is inserted into the snmpEngine* variables defined\n" 1688"%% in SNMP-FRAMEWORK-MIB, and the intAgent* variables defined\n" 1689"%% in OTP-SNMPEA-MIB.\n" 1690"%% Each row is a 2-tuple:\n" 1691"%% {AgentVariable, Value}.\n" 1692"%% For example\n" 1693"%% {intAgentUDPPort, 4000}.\n" 1694"%% The ip address for the agent is sent as id in traps.\n" 1695"%% {intAgentIpAddress, [127,42,17,5]}.\n" 1696"%% {snmpEngineID, \"agentEngine\"}.\n" 1697"%% {snmpEngineMaxMessageSize, 484}.\n" 1698"%%\n\n", 1699 Hdr = header() ++ Comment, 1700 write_agent_config(Dir, Hdr, Conf). 1701 1702write_agent_config(Dir, Hdr, Conf) -> 1703 snmpa_conf:write_agent_config(Dir, Hdr, Conf). 1704 1705update_agent_config(Dir, Conf) -> 1706 snmpa_conf:append_agent_config(Dir, Conf). 1707 1708 1709%% 1710%% ------ [agent] context.conf ------ 1711%% 1712 1713write_agent_snmp_context_conf(Dir) -> 1714 Comment = 1715"%% This file defines the contexts known to the agent.\n" 1716"%% The data is inserted into the vacmContextTable defined\n" 1717"%% in SNMP-VIEW-BASED-ACM-MIB.\n" 1718"%% Each row is a string:\n" 1719"%% ContextName.\n" 1720"%%\n" 1721"%% The empty string is the default context.\n" 1722"%% For example\n" 1723"%% \"bridge1\".\n" 1724"%% \"bridge2\".\n" 1725"%%\n\n", 1726 Hdr = header() ++ Comment, 1727 Conf = [""], 1728 write_agent_context_config(Dir, Hdr, Conf). 1729 1730write_agent_context_config(Dir, Hdr, Conf) -> 1731 snmpa_conf:write_context_config(Dir, Hdr, Conf). 1732 1733update_agent_context_config(Dir, Conf) -> 1734 snmpa_conf:append_context_config(Dir, Conf). 1735 1736 1737%% 1738%% ------ community.conf ------ 1739%% 1740 1741write_agent_snmp_community_conf(Dir) -> 1742 Comment = 1743"%% This file defines the community info which maps to VACM parameters.\n" 1744"%% The data is inserted into the snmpCommunityTable defined\n" 1745"%% in SNMP-COMMUNITY-MIB.\n" 1746"%% Each row is a 5-tuple:\n" 1747"%% {CommunityIndex, CommunityName, SecurityName, ContextName, TransportTag}.\n" 1748"%% For example\n" 1749"%% {\"1\", \"public\", \"initial\", \"\", \"\"}.\n" 1750"%% {\"2\", \"secret\", \"secret_name\", \"\", \"tag\"}.\n" 1751"%% {\"3\", \"bridge1\", \"initial\", \"bridge1\", \"\"}.\n" 1752"%%\n\n", 1753 Hdr = header() ++ Comment, 1754 Conf = [{"public", "public", "initial", "", ""}, 1755 {"all-rights", "all-rights", "all-rights", "", ""}, 1756 {"standard trap", "standard trap", "initial", "", ""}], 1757 write_agent_community_config(Dir, Hdr, Conf). 1758 1759write_agent_community_config(Dir, Hdr, Conf) -> 1760 snmpa_conf:write_community_config(Dir, Hdr, Conf). 1761 1762update_agent_community_config(Dir, Conf) -> 1763 snmpa_conf:append_community_config(Dir, Conf). 1764 1765 1766%% 1767%% ------ standard.conf ------ 1768%% 1769 1770write_agent_snmp_standard_conf(Dir, SysName) -> 1771 Comment = 1772"%% This file defines the STANDARD-MIB info.\n" 1773"%% Each row is a 2-tuple:\n" 1774"%% {StandardVariable, Value}.\n" 1775"%% For example\n" 1776"%% {sysDescr, \"Erlang SNMP agent\"}.\n" 1777"%% {sysObjectID, [1,2,3]}.\n" 1778"%% {sysContact, \"{mbj,eklas}@erlang.ericsson.se\"}.\n" 1779"%% {sysName, \"test\"}.\n" 1780"%% {sysLocation, \"erlang\"}.\n" 1781"%% {sysServices, 72}.\n" 1782"%% {snmpEnableAuthenTraps, enabled}.\n" 1783"%%\n\n", 1784 Hdr = header() ++ Comment, 1785 Conf = [{sysDescr, "Erlang SNMP agent"}, 1786 {sysObjectID, [1,2,3]}, 1787 {sysContact, "{mbj,eklas}@erlang.ericsson.se"}, 1788 {sysLocation, "erlang"}, 1789 {sysServices, 72}, 1790 {snmpEnableAuthenTraps, enabled}, 1791 {sysName, SysName}], 1792 write_agent_standard_config(Dir, Hdr, Conf). 1793 1794write_agent_standard_config(Dir, Hdr, Conf) -> 1795 snmpa_conf:write_standard_config(Dir, Hdr, Conf). 1796 1797update_agent_standard_config(Dir, Conf) -> 1798 snmpa_conf:append_standard_config(Dir, Conf). 1799 1800 1801%% 1802%% ------ target_addr.conf ------ 1803%% 1804 1805write_agent_snmp_target_addr_conf(Dir, Addresses, Vsns) -> 1806 Timeout = 1500, 1807 RetryCount = 3, 1808 write_agent_snmp_target_addr_conf( 1809 Dir, Addresses, Timeout, RetryCount, Vsns). 1810 1811write_agent_snmp_target_addr_conf(Dir, Domain_or_Ip, Addr_or_Port, Vsns) -> 1812 Addresses = [{Domain_or_Ip, Addr_or_Port}], 1813 write_agent_snmp_target_addr_conf(Dir, Addresses, Vsns). 1814 1815write_agent_snmp_target_addr_conf( 1816 Dir, Addresses, Timeout, RetryCount, Vsns) -> 1817 Comment = 1818"%% This file defines the target address parameters.\n" 1819"%% The data is inserted into the snmpTargetAddrTable defined\n" 1820"%% in SNMP-TARGET-MIB, and in the snmpTargetAddrExtTable defined\n" 1821"%% in SNMP-COMMUNITY-MIB.\n" 1822"%% Each row is a 10-tuple:\n" 1823"%% {Name, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId,\n" 1824"%% TMask, MaxMessageSize}.\n" 1825"%% The EngineId value is only used if Inform-Requests are sent to this\n" 1826"%% target. If Informs are not sent, this value is ignored, and can be\n" 1827"%% e.g. an empty string. However, if Informs are sent, it is essential\n" 1828"%% that the value of EngineId matches the value of the target's\n" 1829"%% actual snmpEngineID.\n" 1830"%% For example\n" 1831"%% {\"1.2.3.4 v1\", [1,2,3,4], 162, \n" 1832"%% 1500, 3, \"std_inform\", \"otp_v2\", \"\",\n" 1833"%% [127,0,0,0], 2048}.\n" 1834"%%\n\n", 1835 Hdr = header() ++ Comment, 1836 Conf = 1837 lists:foldl( 1838 fun ({Domain_or_Ip, Addr_or_Port} = Address, OuterAcc) -> 1839 lists:foldl( 1840 fun(v1 = Vsn, Acc) -> 1841 [{mk_name(Address, Vsn), 1842 Domain_or_Ip, Addr_or_Port, 1843 Timeout, RetryCount, 1844 "std_trap", mk_param(Vsn), "", 1845 [], 2048}| Acc]; 1846 (v2 = Vsn, Acc) -> 1847 [{mk_name(Address, Vsn), 1848 Domain_or_Ip, Addr_or_Port, 1849 Timeout, RetryCount, 1850 "std_trap", mk_param(Vsn), "", 1851 [], 2048}, 1852 {lists:flatten( 1853 io_lib:format( 1854 "~s.2", [mk_name(Address, Vsn)])), 1855 Domain_or_Ip, Addr_or_Port, 1856 Timeout, RetryCount, 1857 "std_inform", mk_param(Vsn), "", 1858 [], 2048}| Acc]; 1859 (v3 = Vsn, Acc) -> 1860 [{mk_name(Address, Vsn), 1861 Domain_or_Ip, Addr_or_Port, 1862 Timeout, RetryCount, 1863 "std_trap", mk_param(Vsn), "", 1864 [], 2048}, 1865 {lists:flatten( 1866 io_lib:format( 1867 "~s.3", [mk_name(Address, Vsn)])), 1868 Domain_or_Ip, Addr_or_Port, 1869 Timeout, RetryCount, 1870 "std_inform", mk_param(Vsn), "mgrEngine", 1871 [], 2048} | Acc] 1872 end, OuterAcc, Vsns) 1873 end, [], Addresses), 1874 write_agent_target_addr_config(Dir, Hdr, Conf). 1875 1876write_agent_snmp_target_addr_conf( 1877 Dir, Domain_or_Ip, Addr_or_Port, Timeout, RetryCount, Vsns) -> 1878 Addresses = [{Domain_or_Ip, Addr_or_Port}], 1879 write_agent_snmp_target_addr_conf( 1880 Dir, Addresses, Timeout, RetryCount, Vsns). 1881 1882mk_param(Vsn) -> 1883 lists:flatten(io_lib:format("target_~w", [Vsn])). 1884 1885mk_name(Address, Vsn) -> 1886 lists:flatten( 1887 io_lib:format( 1888 "~s ~w", [snmp_conf:mk_addr_string(Address), Vsn])). 1889 1890write_agent_target_addr_config(Dir, Hdr, Conf) -> 1891 snmpa_conf:write_target_addr_config(Dir, Hdr, Conf). 1892 1893update_agent_target_addr_config(Dir, Conf) -> 1894 snmpa_conf:append_target_addr_config(Dir, Conf). 1895 1896 1897%% 1898%% ------ target_params.conf ------ 1899%% 1900 1901write_agent_snmp_target_params_conf(Dir, Vsns) -> 1902 Comment = 1903"%% This file defines the target parameters.\n" 1904"%% The data is inserted into the snmpTargetParamsTable defined\n" 1905"%% in SNMP-TARGET-MIB.\n" 1906"%% Each row is a 5-tuple:\n" 1907"%% {Name, MPModel, SecurityModel, SecurityName, SecurityLevel}.\n" 1908"%% For example\n" 1909"%% {\"target_v3\", v3, usm, \"\", noAuthNoPriv}.\n" 1910"%%\n\n", 1911 Hdr = header() ++ Comment, 1912 Conf = [fun(V) -> 1913 MP = if V == v1 -> v1; 1914 V == v2 -> v2c; 1915 V == v3 -> v3 1916 end, 1917 SM = if V == v1 -> v1; 1918 V == v2 -> v2c; 1919 V == v3 -> usm 1920 end, 1921 Name = lists:flatten( 1922 io_lib:format("target_~w", [V])), 1923 {Name, MP, SM, "initial", noAuthNoPriv} 1924 end(Vsn) || Vsn <- Vsns], 1925 write_agent_target_params_config(Dir, Hdr, Conf). 1926 1927write_agent_target_params_config(Dir, Hdr, Conf) -> 1928 snmpa_conf:write_target_params_config(Dir, Hdr, Conf). 1929 1930update_agent_target_params_config(Dir, Conf) -> 1931 snmpa_conf:append_target_params_config(Dir, Conf). 1932 1933 1934%% 1935%% ------ notify.conf ------ 1936%% 1937 1938write_agent_snmp_notify_conf(Dir, NotifyType) -> 1939 Comment = 1940"%% This file defines the notification parameters.\n" 1941"%% The data is inserted into the snmpNotifyTable defined\n" 1942"%% in SNMP-NOTIFICATION-MIB.\n" 1943"%% The Name is used as CommunityString for v1 and v2c.\n" 1944"%% Each row is a 3-tuple:\n" 1945"%% {Name, Tag, Type}.\n" 1946"%% For example\n" 1947"%% {\"standard trap\", \"std_trap\", trap}.\n" 1948"%% {\"standard inform\", \"std_inform\", inform}.\n" 1949"%%\n\n", 1950 Hdr = header() ++ Comment, 1951 Conf = [{"standard trap", "std_trap", NotifyType}], 1952 write_agent_notify_config(Dir, Hdr, Conf). 1953 1954write_agent_notify_config(Dir, Hdr, Conf) -> 1955 snmpa_conf:write_notify_config(Dir, Hdr, Conf). 1956 1957update_agent_notify_config(Dir, Conf) -> 1958 snmpa_conf:append_notify_config(Dir, Conf). 1959 1960 1961%% 1962%% ------ usm.conf ------ 1963%% 1964 1965write_agent_snmp_usm_conf(Dir, Vsns, EngineID, SecType, Passwd) -> 1966 case lists:member(v3, Vsns) of 1967 false -> ok; 1968 true -> write_agent_snmp_usm_conf(Dir, EngineID, SecType, Passwd) 1969 end. 1970 1971write_agent_snmp_usm_conf(Dir, EngineID, SecType, Passwd) -> 1972 Comment = 1973"%% This file defines the security parameters for the user-based\n" 1974"%% security model.\n" 1975"%% The data is inserted into the usmUserTable defined\n" 1976"%% in SNMP-USER-BASED-SM-MIB.\n" 1977"%% Each row is a 14-tuple:\n" 1978"%% {EngineID, UserName, SecName, Clone, AuthP, AuthKeyC, OwnAuthKeyC,\n" 1979"%% PrivP, PrivKeyC, OwnPrivKeyC, Public, AuthKey, PrivKey}.\n" 1980"%% For example\n" 1981"%% {\"agentEngine\", \"initial\", \"initial\", zeroDotZero,\n" 1982"%% usmNoAuthProtocol, \"\", \"\", usmNoPrivProtocol, \"\", \"\", \"\",\n" 1983"%% \"\", \"\"}.\n" 1984"%%\n\n", 1985 Hdr = header() ++ Comment, 1986 Conf = write_agent_snmp_usm_conf2(EngineID, SecType, Passwd), 1987 write_agent_usm_config(Dir, Hdr, Conf). 1988 1989write_agent_snmp_usm_conf2(EngineID, none, _Passwd) -> 1990 [{EngineID, "initial", "initial", zeroDotZero, 1991 usmNoAuthProtocol, "", "", 1992 usmNoPrivProtocol, "", "", 1993 "", "", ""}]; 1994write_agent_snmp_usm_conf2(EngineID, SecType, Passwd) -> 1995 Secret16 = agent_snmp_mk_secret(md5, Passwd, EngineID), 1996 Secret20 = agent_snmp_mk_secret(sha, Passwd, EngineID), 1997 {PrivProt, PrivSecret} = 1998 case SecType of 1999 minimum -> 2000 {usmNoPrivProtocol, ""}; 2001 {semi, des} -> 2002 {usmDESPrivProtocol, Secret16}; 2003 {semi, aes} -> 2004 {usmAesCfb128Protocol, Secret16} 2005 end, 2006 [{EngineID, "initial", "initial", zeroDotZero, 2007 usmHMACMD5AuthProtocol, "", "", 2008 PrivProt, "", "", 2009 "", Secret16, PrivSecret}, 2010 2011 {EngineID, "templateMD5", "templateMD5", zeroDotZero, 2012 usmHMACMD5AuthProtocol, "", "", 2013 PrivProt, "", "", 2014 "", Secret16, PrivSecret}, 2015 2016 {EngineID, "templateSHA", "templateSHA", zeroDotZero, 2017 usmHMACSHAAuthProtocol, "", "", 2018 PrivProt, "", "", 2019 "", Secret20, PrivSecret}]. 2020 2021write_agent_usm_config(Dir, Hdr, Conf) -> 2022 snmpa_conf:write_usm_config(Dir, Hdr, Conf). 2023 2024update_agent_usm_config(Dir, Conf) -> 2025 snmpa_conf:append_usm_config(Dir, Conf). 2026 2027 2028%% 2029%% ------ vacm.conf ------ 2030%% 2031 2032write_agent_snmp_vacm_conf(Dir, Vsns, SecType) -> 2033 Comment = 2034"%% This file defines the Mib Views.\n" 2035"%% The data is inserted into the vacm* tables defined\n" 2036"%% in SNMP-VIEW-BASED-ACM-MIB.\n" 2037"%% Each row is one of 3 tuples; one for each table in the MIB:\n" 2038"%% {vacmSecurityToGroup, SecModel, SecName, GroupName}.\n" 2039"%% {vacmAccess, GroupName, Prefix, SecModel, SecLevel, Match, RV, WV, NV}.\n" 2040"%% {vacmViewTreeFamily, ViewIndex, ViewSubtree, ViewStatus, ViewMask}.\n" 2041"%% For example\n" 2042"%% {vacmSecurityToGroup, v2c, \"initial\", \"initial\"}.\n" 2043"%% {vacmSecurityToGroup, usm, \"initial\", \"initial\"}.\n" 2044"%% read/notify access to system\n" 2045"%% {vacmAccess, \"initial\", \"\", any, noAuthNoPriv, exact,\n" 2046"%% \"system\", \"\", \"system\"}.\n" 2047"%% {vacmViewTreeFamily, \"system\", [1,3,6,1,2,1,1], included, null}.\n" 2048"%% {vacmViewTreeFamily, \"exmib\", [1,3,6,1,3], included, null}." 2049" % for EX1-MIB\n" 2050"%% {vacmViewTreeFamily, \"internet\", [1,3,6,1], included, null}.\n" 2051"%%\n\n", 2052 Hdr = lists:flatten(header()) ++ Comment, 2053 Groups = 2054 lists:foldl( 2055 fun(V, Acc) -> 2056 [{vacmSecurityToGroup, vacm_ver(V), 2057 "initial", "initial"}, 2058 {vacmSecurityToGroup, vacm_ver(V), 2059 "all-rights", "all-rights"}| 2060 Acc] 2061 end, [], Vsns), 2062 Acc = 2063 [{vacmAccess, "initial", "", any, noAuthNoPriv, exact, 2064 "restricted", "", "restricted"}, 2065 {vacmAccess, "initial", "", usm, authNoPriv, exact, 2066 "internet", "internet", "internet"}, 2067 {vacmAccess, "initial", "", usm, authPriv, exact, 2068 "internet", "internet", "internet"}, 2069 {vacmAccess, "all-rights", "", any, noAuthNoPriv, exact, 2070 "internet", "internet", "internet"}], 2071 VTF0 = 2072 case SecType of 2073 none -> 2074 [{vacmViewTreeFamily, 2075 "restricted", [1,3,6,1], included, null}]; 2076 minimum -> 2077 [{vacmViewTreeFamily, 2078 "restricted", [1,3,6,1], included, null}]; 2079 {semi, _} -> 2080 [{vacmViewTreeFamily, 2081 "restricted", [1,3,6,1,2,1,1], included, null}, 2082 {vacmViewTreeFamily, 2083 "restricted", [1,3,6,1,2,1,11], included, null}, 2084 {vacmViewTreeFamily, 2085 "restricted", [1,3,6,1,6,3,10,2,1], included, null}, 2086 {vacmViewTreeFamily, 2087 "restricted", [1,3,6,1,6,3,11,2,1], included, null}, 2088 {vacmViewTreeFamily, 2089 "restricted", [1,3,6,1,6,3,15,1,1], included, null}] 2090 end, 2091 VTF = VTF0 ++ [{vacmViewTreeFamily,"internet",[1,3,6,1],included,null}], 2092 write_agent_vacm_config(Dir, Hdr, Groups ++ Acc ++ VTF). 2093 2094vacm_ver(v1) -> v1; 2095vacm_ver(v2) -> v2c; 2096vacm_ver(v3) -> usm. 2097 2098write_agent_vacm_config(Dir, Hdr, Conf) -> 2099 snmpa_conf:write_vacm_config(Dir, Hdr, Conf). 2100 2101update_agent_vacm_config(Dir, Conf) -> 2102 snmpa_conf:append_vacm_config(Dir, Conf). 2103 2104 2105%% 2106%% ----- Manager config files generator functions ----- 2107%% 2108 2109write_manager_snmp_files(Dir, IP, Port, MMS, EngineID, 2110 Users, Agents, Usms) -> 2111 write_manager_snmp_conf(Dir, IP, Port, MMS, EngineID), 2112 write_manager_snmp_users_conf(Dir, Users), 2113 write_manager_snmp_agents_conf(Dir, Agents), 2114 write_manager_snmp_usm_conf(Dir, Usms), 2115 ok. 2116 2117 2118%% 2119%% ------ manager.conf ------ 2120%% 2121 2122write_manager_snmp_conf(Dir, Transports, MMS, EngineID) -> 2123 Comment = 2124"%% This file defines the Manager local configuration info\n" 2125"%% Each row is a 2-tuple:\n" 2126"%% {Variable, Value}.\n" 2127"%% For example\n" 2128"%% {transports, [{transportDomainUdpIpv4, {{127,42,17,5}, 5000}}]}.\n" 2129"%% {engine_id, \"managerEngine\"}.\n" 2130"%% {max_message_size, 484}.\n" 2131"%%\n\n", 2132 Hdr = header() ++ Comment, 2133 Conf = 2134 [{transports, Transports}, 2135 {engine_id, EngineID}, 2136 {max_message_size, MMS}], 2137 write_manager_config(Dir, Hdr, Conf). 2138 2139write_manager_snmp_conf(Dir, Domain_or_IP, Addr_or_Port, MMS, EngineID) -> 2140 Comment = 2141"%% This file defines the Manager local configuration info\n" 2142"%% Each row is a 2-tuple:\n" 2143"%% {Variable, Value}.\n" 2144"%% For example\n" 2145"%% {port, 5000}.\n" 2146"%% {address, [127,42,17,5]}.\n" 2147"%% {engine_id, \"managerEngine\"}.\n" 2148"%% {max_message_size, 484}.\n" 2149"%%\n\n", 2150 Hdr = header() ++ Comment, 2151 Conf = 2152 case Addr_or_Port of 2153 {IP, Port} when is_integer(Port), is_atom(Domain_or_IP) -> 2154 [{domain, Domain_or_IP}, 2155 {port, Port}, 2156 {address, IP}]; 2157 _ when is_integer(Addr_or_Port) -> 2158 [{port, Addr_or_Port}, 2159 {address, Domain_or_IP}]; 2160 _ -> 2161 error({bad_address, {Domain_or_IP, Addr_or_Port}}) 2162 end ++ 2163 [{engine_id, EngineID}, 2164 {max_message_size, MMS}], 2165 write_manager_config(Dir, Hdr, Conf). 2166 2167write_manager_config(Dir, Hdr, Conf) -> 2168 snmpm_conf:write_manager_config(Dir, Hdr, Conf). 2169 2170update_manager_config(Dir, Conf) -> 2171 snmpm_conf:append_manager_config(Dir, Conf). 2172 2173 2174%% 2175%% ------ users.conf ------ 2176%% 2177 2178write_manager_snmp_users_conf(Dir, Users) -> 2179 Comment = 2180"%% This file defines the users the manager handles\n" 2181"%% Each row is a 3-tuple:\n" 2182"%% {UserId, UserMod, UserData}.\n" 2183"%% For example\n" 2184"%% {kalle, kalle_callback_user_mod, \"dummy\"}.\n" 2185"%%\n\n", 2186 Hdr = header() ++ Comment, 2187 write_manager_users_config(Dir, Hdr, Users). 2188 2189write_manager_users_config(Dir, Hdr, Users) -> 2190 snmpm_conf:write_users_config(Dir, Hdr, Users). 2191 2192update_manager_users_config(Dir, Users) -> 2193 snmpm_conf:append_users_config(Dir, Users). 2194 2195 2196%% 2197%% ------ agents.conf ------ 2198%% 2199 2200write_manager_snmp_agents_conf(Dir, Agents) -> 2201 Comment = 2202"%% This file defines the agents the manager handles\n" 2203"%% Each row is a 12-tuple:\n" 2204"%% {UserId, \n" 2205"%% TargetName, Comm, Ip, Port, EngineID, Timeout, \n" 2206"%% MaxMessageSize, Version, SecModel, SecName, SecLevel}\n" 2207"%%\n\n", 2208 Hdr = header() ++ Comment, 2209 write_manager_agents_config(Dir, Hdr, Agents). 2210 2211write_manager_agents_config(Dir, Hdr, Agents) -> 2212 snmpm_conf:write_agents_config(Dir, Hdr, Agents). 2213 2214update_manager_agents_config(Dir, Agents) -> 2215 snmpm_conf:append_agents_config(Dir, Agents). 2216 2217 2218%% 2219%% ------ usm.conf ----- 2220%% 2221 2222write_manager_snmp_usm_conf(Dir, Usms) -> 2223 Comment = 2224"%% This file defines the usm users the manager handles\n" 2225"%% Each row is a 6 or 7-tuple:\n" 2226"%% {EngineID, UserName, AuthP, AuthKey, PrivP, PrivKey}\n" 2227"%% {EngineID, UserName, SecName, AuthP, AuthKey, PrivP, PrivKey}\n" 2228"%%\n\n", 2229 Hdr = header() ++ Comment, 2230 write_manager_usm_config(Dir, Hdr, Usms). 2231 2232write_manager_usm_config(Dir, Hdr, Usms) -> 2233 snmpm_conf:write_usm_config(Dir, Hdr, Usms). 2234 2235update_manager_usm_config(Dir, Usms) -> 2236 snmpm_conf:append_usm_config(Dir, Usms). 2237 2238 2239%% 2240%% ------------------------------------------------------------------------- 2241%% 2242 2243write_sys_config_file(Dir, Services) -> 2244 {ok, Fid} = file:open(filename:join(Dir,"sys.config"), [write]), 2245 ok = io:format(Fid, "~s", [header()]), 2246 ok = io:format(Fid, "[{snmp, ~n", []), 2247 ok = io:format(Fid, " [~n", []), 2248 write_sys_config_file_services(Fid, Services), 2249 ok = io:format(Fid, " ]~n", []), 2250 ok = io:format(Fid, " }~n", []), 2251 ok = io:format(Fid, "].~n", []), 2252 ok. 2253 2254write_sys_config_file_services(Fid, [Service]) -> 2255 write_sys_config_file_service(Fid, Service), 2256 ok = io:format(Fid, "~n", []), 2257 ok; 2258write_sys_config_file_services(Fid, [Service|Services]) -> 2259 write_sys_config_file_service(Fid, Service), 2260 ok = io:format(Fid, ", ~n", []), 2261 write_sys_config_file_services(Fid, Services). 2262 2263write_sys_config_file_service(Fid, {Service, Opts}) -> 2264 ok = io:format(Fid, " {~w,~n", [Service]), 2265 ok = io:format(Fid, " [~n", []), 2266 write_sys_config_file_service_opts(Fid, Service, Opts), 2267 ok = io:format(Fid, " ]~n", []), 2268 ok = io:format(Fid, " }", []), 2269 true. 2270 2271write_sys_config_file_service_opts(Fid, agent, Opts) -> 2272 write_sys_config_file_agent_opts(Fid, Opts); 2273write_sys_config_file_service_opts(Fid, manager, Opts) -> 2274 write_sys_config_file_manager_opts(Fid, Opts). 2275 2276 2277write_sys_config_file_agent_opts(Fid, [Opt]) -> 2278 write_sys_config_file_agent_opt(Fid, Opt), 2279 ok = io:format(Fid, "~n", []), 2280 ok; 2281write_sys_config_file_agent_opts(Fid, [Opt|Opts]) -> 2282 write_sys_config_file_agent_opt(Fid, Opt), 2283 ok = io:format(Fid, ", ~n", []), 2284 write_sys_config_file_agent_opts(Fid, Opts). 2285 2286 2287write_sys_config_file_agent_opt(Fid, {mibs, []}) -> 2288 ok = io:format(Fid, " {mibs, []}", []); 2289write_sys_config_file_agent_opt(Fid, {priority, Prio}) -> 2290 ok = io:format(Fid, " {priority, ~w}", [Prio]); 2291write_sys_config_file_agent_opt(Fid, {error_report_mod, Mod}) -> 2292 ok = io:format(Fid, " {error_report_mod, ~w}", [Mod]); 2293write_sys_config_file_agent_opt(Fid, {versions, Vsns}) -> 2294 ok = io:format(Fid, " {versions, ~w}", [Vsns]); 2295write_sys_config_file_agent_opt(Fid, {multi_threaded, B}) -> 2296 ok = io:format(Fid, " {multi_threaded, ~w}", [B]); 2297write_sys_config_file_agent_opt(Fid, {config, Opts}) -> 2298 ok = io:format(Fid, " {config, [", []), 2299 write_sys_config_file_agent_config_opts(Fid, Opts), 2300 ok = io:format(Fid, "}", []); 2301write_sys_config_file_agent_opt(Fid, {db_dir, Dir}) -> 2302 ok = io:format(Fid, " {db_dir, \"~s\"}", [Dir]); 2303write_sys_config_file_agent_opt(Fid, {db_init_error, Action}) -> 2304 ok = io:format(Fid, " {db_init_error, ~w}", [Action]); 2305write_sys_config_file_agent_opt(Fid, {mib_storage, ets}) -> 2306 ok = io:format(Fid, " {mib_storage, ets}", []); 2307write_sys_config_file_agent_opt(Fid, {mib_storage, {dets, Dir}}) -> 2308 ok = io:format(Fid, " {mib_storage, {dets, \"~s\"}}", [Dir]); 2309write_sys_config_file_agent_opt(Fid, {mib_storage, {dets, Dir, Act}}) -> 2310 ok = io:format(Fid, " {mib_storage, {dets, \"~s\", ~w}}", 2311 [Dir, Act]); 2312write_sys_config_file_agent_opt(Fid, {mib_storage, {mnesia, Nodes}}) -> 2313 ok = io:format(Fid, " {mib_storage, {mnesia, ~w}}", [Nodes]); 2314write_sys_config_file_agent_opt(Fid, {mib_storage, {mnesia, Nodes, Act}}) -> 2315 ok = io:format(Fid, " {mib_storage, {mnesia, ~w, ~w}}", 2316 [Nodes, Act]); 2317write_sys_config_file_agent_opt(Fid, {target_cache, Opts}) -> 2318 ok = io:format(Fid, " {target_cache, ~w}", [Opts]); 2319write_sys_config_file_agent_opt(Fid, {local_db, Opts}) -> 2320 ok = io:format(Fid, " {local_db, ~w}", [Opts]); 2321write_sys_config_file_agent_opt(Fid, {note_store, Opts}) -> 2322 ok = io:format(Fid, " {note_store, ~w}", [Opts]); 2323write_sys_config_file_agent_opt(Fid, {symbolic_store, Opts}) -> 2324 ok = io:format(Fid, " {symbolic_store, ~w}", [Opts]); 2325write_sys_config_file_agent_opt(Fid, {agent_type, Type}) -> 2326 ok = io:format(Fid, " {agent_type, ~w}", [Type]); 2327write_sys_config_file_agent_opt(Fid, {agent_verbosity, Verb}) -> 2328 ok = io:format(Fid, " {agent_verbosity, ~w}", [Verb]); 2329write_sys_config_file_agent_opt(Fid, {audit_trail_log, Opts}) -> 2330 ok = io:format(Fid, " {audit_trail_log, [", []), 2331 write_sys_config_file_agent_atl_opts(Fid, Opts), 2332 ok = io:format(Fid, "}", []); 2333write_sys_config_file_agent_opt(Fid, {discovery, Opts}) -> 2334 ok = io:format(Fid, " {discovery, [", []), 2335 write_sys_config_file_agent_disco_opts(Fid, Opts), 2336 ok = io:format(Fid, "}", []); 2337write_sys_config_file_agent_opt(Fid, {net_if, Opts}) -> 2338 ok = io:format(Fid, " {net_if, ~w}", [Opts]); 2339write_sys_config_file_agent_opt(Fid, {mib_server, Opts}) -> 2340 ok = io:format(Fid, " {mib_server, ~w}", [Opts]); 2341write_sys_config_file_agent_opt(Fid, {Key, Val}) -> 2342 ok = io:format(Fid, " {~w, ~w}", [Key, Val]). 2343 2344 2345%% Mandatory option dir, means that this is never empty: 2346write_sys_config_file_agent_config_opts(Fid, [Opt]) -> 2347 write_sys_config_file_agent_config_opt(Fid, Opt), 2348 ok = io:format(Fid, "]", []), 2349 ok; 2350write_sys_config_file_agent_config_opts(Fid, [Opt|Opts]) -> 2351 write_sys_config_file_agent_config_opt(Fid, Opt), 2352 ok = io:format(Fid, ", ", []), 2353 write_sys_config_file_agent_config_opts(Fid, Opts). 2354 2355write_sys_config_file_agent_config_opt(Fid, {dir, Dir}) -> 2356 ok = io:format(Fid, "{dir, \"~s\"}", [Dir]); 2357write_sys_config_file_agent_config_opt(Fid, {force_load, Bool}) -> 2358 ok = io:format(Fid, "{force_load, ~w}", [Bool]); 2359write_sys_config_file_agent_config_opt(Fid, {verbosity, Verb}) -> 2360 ok = io:format(Fid, "{verbosity, ~w}", [Verb]). 2361 2362 2363%% This is only present if there is atleast one option 2364write_sys_config_file_agent_atl_opts(Fid, [Opt]) -> 2365 write_sys_config_file_agent_atl_opt(Fid, Opt), 2366 ok = io:format(Fid, "]", []), 2367 ok; 2368write_sys_config_file_agent_atl_opts(Fid, [Opt|Opts]) -> 2369 write_sys_config_file_agent_atl_opt(Fid, Opt), 2370 ok = io:format(Fid, ", ", []), 2371 write_sys_config_file_agent_atl_opts(Fid, Opts). 2372 2373write_sys_config_file_agent_atl_opt(Fid, {dir, Dir}) -> 2374 ok = io:format(Fid, "{dir, \"~s\"}", [Dir]); 2375write_sys_config_file_agent_atl_opt(Fid, {type, Type}) -> 2376 ok = io:format(Fid, "{type, ~w}", [Type]); 2377write_sys_config_file_agent_atl_opt(Fid, {size, Size}) -> 2378 ok = io:format(Fid, "{size, ~w}", [Size]); 2379write_sys_config_file_agent_atl_opt(Fid, {repair, Rep}) -> 2380 ok = io:format(Fid, "{repair, ~w}", [Rep]); 2381write_sys_config_file_agent_atl_opt(Fid, {seqno, SeqNo}) -> 2382 ok = io:format(Fid, "{seqno, ~w}", [SeqNo]). 2383 2384 2385%% These options are allways there 2386write_sys_config_file_agent_disco_opts(Fid, [Opt]) -> 2387 write_sys_config_file_agent_disco_opt(Fid, Opt), 2388 ok = io:format(Fid, "]", []), 2389 ok; 2390write_sys_config_file_agent_disco_opts(Fid, [Opt|Opts]) -> 2391 write_sys_config_file_agent_disco_opt(Fid, Opt), 2392 ok = io:format(Fid, ", ", []), 2393 write_sys_config_file_agent_disco_opts(Fid, Opts). 2394 2395write_sys_config_file_agent_disco_opt(Fid, {terminating, Opts}) -> 2396 ok = io:format(Fid, "{terminating, [", []), 2397 write_sys_config_file_agent_term_disco_opts(Fid, Opts), 2398 ok = io:format(Fid, "}", []); 2399write_sys_config_file_agent_disco_opt(Fid, {originating, Opts}) -> 2400 ok = io:format(Fid, "{originating, [", []), 2401 write_sys_config_file_agent_orig_disco_opts(Fid, Opts), 2402 ok = io:format(Fid, "}", []). 2403 2404write_sys_config_file_agent_term_disco_opts(Fid, [Opt]) -> 2405 write_sys_config_file_agent_term_disco_opt(Fid, Opt), 2406 ok = io:format(Fid, "]", []), 2407 ok; 2408write_sys_config_file_agent_term_disco_opts(Fid, [Opt|Opts]) -> 2409 write_sys_config_file_agent_term_disco_opt(Fid, Opt), 2410 ok = io:format(Fid, ", ", []), 2411 write_sys_config_file_agent_term_disco_opts(Fid, Opts). 2412 2413write_sys_config_file_agent_term_disco_opt(Fid, {enable, Enable}) -> 2414 ok = io:format(Fid, "{enable, ~w}", [Enable]); 2415write_sys_config_file_agent_term_disco_opt(Fid, {stage2, Stage2}) -> 2416 ok = io:format(Fid, "{stage2, ~w}", [Stage2]); 2417write_sys_config_file_agent_term_disco_opt(Fid, {trigger_username, Trigger}) -> 2418 ok = io:format(Fid, "{trigger_username, \"~s\"}", [Trigger]). 2419 2420write_sys_config_file_agent_orig_disco_opts(Fid, [Opt]) -> 2421 write_sys_config_file_agent_orig_disco_opt(Fid, Opt), 2422 ok = io:format(Fid, "]", []), 2423 ok; 2424write_sys_config_file_agent_orig_disco_opts(Fid, [Opt|Opts]) -> 2425 write_sys_config_file_agent_orig_disco_opt(Fid, Opt), 2426 ok = io:format(Fid, ", ", []), 2427 write_sys_config_file_agent_orig_disco_opts(Fid, Opts). 2428 2429write_sys_config_file_agent_orig_disco_opt(Fid, {enable, Enable}) -> 2430 ok = io:format(Fid, "{enable, ~w}", [Enable]). 2431 2432 2433 2434write_sys_config_file_manager_opts(Fid, [Opt]) -> 2435 write_sys_config_file_manager_opt(Fid, Opt), 2436 ok = io:format(Fid, "~n", []), 2437 ok; 2438write_sys_config_file_manager_opts(Fid, [Opt|Opts]) -> 2439 write_sys_config_file_manager_opt(Fid, Opt), 2440 ok = io:format(Fid, ", ~n", []), 2441 write_sys_config_file_manager_opts(Fid, Opts). 2442 2443 2444write_sys_config_file_manager_opt(Fid, {mibs, []}) -> 2445 ok = io:format(Fid, " {mibs, []}", []); 2446write_sys_config_file_manager_opt(Fid, {priority, Prio}) -> 2447 ok = io:format(Fid, " {priority, ~w}", [Prio]); 2448write_sys_config_file_manager_opt(Fid, {versions, Vsns}) -> 2449 ok = io:format(Fid, " {versions, ~w}", [Vsns]); 2450write_sys_config_file_manager_opt(Fid, {config, Opts}) -> 2451 ok = io:format(Fid, " {config, [", []), 2452 write_sys_config_file_manager_config_opts(Fid, Opts), 2453 ok = io:format(Fid, "}", []); 2454write_sys_config_file_manager_opt(Fid, {server, Opts}) -> 2455 ok = io:format(Fid, " {server, ~w}", [Opts]); 2456write_sys_config_file_manager_opt(Fid, {note_store, Opts}) -> 2457 ok = io:format(Fid, " {note_store, ~w}", [Opts]); 2458write_sys_config_file_manager_opt(Fid, {audit_trail_log, Opts}) -> 2459 ok = io:format(Fid, " {audit_trail_log, [", []), 2460 write_sys_config_file_manager_atl_opts(Fid, Opts), 2461 ok = io:format(Fid, "}", []); 2462write_sys_config_file_manager_opt(Fid, {net_if, Opts}) -> 2463 ok = io:format(Fid, " {net_if, ~w}", [Opts]); 2464write_sys_config_file_manager_opt(Fid, {Key, Val}) -> 2465 ok = io:format(Fid, " {~w, ~w}", [Key, Val]). 2466 2467%% Mandatory option dir, means that this is never empty: 2468write_sys_config_file_manager_config_opts(Fid, [Opt]) -> 2469 write_sys_config_file_manager_config_opt(Fid, Opt), 2470 ok = io:format(Fid, "]", []), 2471 ok; 2472write_sys_config_file_manager_config_opts(Fid, [Opt|Opts]) -> 2473 write_sys_config_file_manager_config_opt(Fid, Opt), 2474 ok = io:format(Fid, ", ", []), 2475 write_sys_config_file_manager_config_opts(Fid, Opts). 2476 2477write_sys_config_file_manager_config_opt(Fid, {dir, Dir}) -> 2478 ok = io:format(Fid, "{dir, \"~s\"}", [Dir]); 2479write_sys_config_file_manager_config_opt(Fid, {db_dir, Dir}) -> 2480 ok = io:format(Fid, "{db_dir, \"~s\"}", [Dir]); 2481write_sys_config_file_manager_config_opt(Fid, {db_init_error, Action}) -> 2482 ok = io:format(Fid, "{db_init_error, ~w}", [Action]); 2483write_sys_config_file_manager_config_opt(Fid, {repair, Rep}) -> 2484 ok = io:format(Fid, "{repair, ~w}", [Rep]); 2485write_sys_config_file_manager_config_opt(Fid, {auto_save, As}) -> 2486 ok = io:format(Fid, "{auto_save, ~w}", [As]); 2487write_sys_config_file_manager_config_opt(Fid, {verbosity, Verb}) -> 2488 ok = io:format(Fid, "{verbosity, ~w}", [Verb]). 2489 2490 2491%% This is only present if there is atleast one option 2492write_sys_config_file_manager_atl_opts(Fid, [Opt]) -> 2493 write_sys_config_file_manager_atl_opt(Fid, Opt), 2494 ok = io:format(Fid, "]", []), 2495 ok; 2496write_sys_config_file_manager_atl_opts(Fid, [Opt|Opts]) -> 2497 write_sys_config_file_manager_atl_opt(Fid, Opt), 2498 ok = io:format(Fid, ", ", []), 2499 write_sys_config_file_manager_atl_opts(Fid, Opts). 2500 2501write_sys_config_file_manager_atl_opt(Fid, {dir, Dir}) -> 2502 ok = io:format(Fid, "{dir, \"~s\"}", [Dir]); 2503write_sys_config_file_manager_atl_opt(Fid, {type, Type}) -> 2504 ok = io:format(Fid, "{type, ~w}", [Type]); 2505write_sys_config_file_manager_atl_opt(Fid, {size, Size}) -> 2506 ok = io:format(Fid, "{size, ~w}", [Size]); 2507write_sys_config_file_manager_atl_opt(Fid, {repair, Rep}) -> 2508 ok = io:format(Fid, "{repair, ~w}", [Rep]); 2509write_sys_config_file_manager_atl_opt(Fid, {seqno, SeqNo}) -> 2510 ok = io:format(Fid, "{seqno, ~w}", [SeqNo]). 2511 2512 2513header() -> 2514 {Y, Mo, D} = date(), 2515 {H, Mi, S} = time(), 2516 io_lib:format("%% This file was generated by " 2517 "~w (version-~s) ~w-~2.2.0w-~2.2.0w " 2518 "~2.2.0w:~2.2.0w:~2.2.0w\n", 2519 [?MODULE, ?version, Y, Mo, D, H, Mi, S]). 2520 2521 2522%% *If* these functions are successfull, they successfully return 2523%% (value is ignored), but they fail preferably with 2524%% throw({error, Reason}). Other exceptions are also handled. 2525 2526%% Sorting order for config entries (see lists:sort/2) 2527-type(order_config_entry_function() :: 2528 fun((term(), term()) -> boolean())). 2529 2530%% Check of config entries. Initial State is 'undefined' 2531-type(check_config_entry_function() :: 2532 fun((Entry :: term(), State :: undefined | term()) -> 2533 {ok | {ok, NewEntry :: term()}, NewState :: term()})). 2534 2535%% Write configuration entries to file descriptor Fd 2536-type(write_config_function() :: 2537 fun((Fd :: file:io_device(), [Entry :: term()]) -> ok)). 2538 2539-spec write_config_file( 2540 Dir :: string(), 2541 FileName :: string(), 2542 Order :: order_config_entry_function(), 2543 Check :: check_config_entry_function(), 2544 Write :: write_config_function(), 2545 Entries :: [term()]) -> 2546 ok | {error, term()}. 2547 2548write_config_file(Dir, FileName, Order, Check, Write, Entries) 2549 when is_list(Dir), is_list(FileName), 2550 is_function(Order), is_function(Check), is_function(Write), 2551 is_list(Entries) -> 2552 try 2553 SortedEntries = lists:sort(Order, Entries), 2554 _ = 2555 lists:foldl( 2556 fun (Entry, State) -> 2557 case Check(Entry, State) of 2558 {Ok, NewState} when is_list(Ok) -> 2559 NewState; 2560 {ok, NewState} -> 2561 NewState; 2562 {{ok, _}, NewState} -> 2563 NewState 2564 end 2565 end, undefined, SortedEntries), 2566 ok 2567 of 2568 _ -> 2569 case file:open(filename:join(Dir, FileName), [write]) of 2570 {ok, Fd} -> 2571 write_config_file(Dir, FileName, Write, Entries, Fd); 2572 Error -> 2573 Error 2574 end 2575 catch 2576 Error -> 2577 S = erlang:get_stacktrace(), 2578 d("File write of ~s throwed: ~p~n ~p~n", 2579 [FileName, Error, S]), 2580 Error; 2581 C:E -> 2582 S = erlang:get_stacktrace(), 2583 d("File write of ~s exception: ~p:~p~n ~p~n", 2584 [FileName,C,E,S]), 2585 {error, {failed_write, Dir, FileName, {C, E, S}}} 2586 end. 2587 2588write_config_file(Dir, FileName, Write, Entries, Fd) -> 2589 try Write(Fd, Entries) of 2590 ok -> 2591 close_config_file(Dir, FileName, Fd) 2592 catch 2593 Error -> 2594 S = erlang:get_stacktrace(), 2595 d("File write of ~s throwed: ~p~n ~p~n", 2596 [FileName, Error, S]), 2597 close_config_file(Dir, FileName, Fd), 2598 Error; 2599 C:E -> 2600 S = erlang:get_stacktrace(), 2601 d("File write of ~s exception: ~p:~p~n ~p~n", 2602 [FileName,C,E,S]), 2603 close_config_file(Dir, FileName, Fd), 2604 {error, {failed_write, Dir, FileName, {C, E, S}}} 2605 end. 2606 2607close_config_file(Dir, FileName, Fd) -> 2608 case file:sync(Fd) of 2609 ok -> 2610 case file:close(Fd) of 2611 ok -> 2612 ok; 2613 {error, Reason} -> 2614 {error, {failed_closing, Dir, FileName, Reason}} 2615 end; 2616 {error, Reason} -> 2617 _ = file:close(Fd), 2618 {error, {failed_syncing, Dir, FileName, Reason}} 2619 end. 2620 2621 2622 2623-spec append_config_file( 2624 Dir :: string(), 2625 FileName :: string(), 2626 Order :: order_config_entry_function(), 2627 Check :: check_config_entry_function(), 2628 Write :: write_config_function(), 2629 Entries :: [term()]) -> 2630 ok | {error, term()}. 2631 2632append_config_file(Dir, FileName, Order, Check, Write, Entries) 2633 when is_list(Dir), is_list(FileName), 2634 is_function(Order), is_function(Check), is_function(Write), 2635 is_list(Entries) -> 2636 case file:open(filename:join(Dir, FileName), [read, write]) of 2637 {ok, Fd} -> 2638 append_config_file( 2639 Dir, FileName, Order, Check, Write, Entries, Fd); 2640 Error -> 2641 Error 2642 end. 2643 2644append_config_file(Dir, FileName, Order, Check, Write, Entries, Fd) -> 2645 try 2646 %% Verify the entries together with the file content 2647 LinesInFileR = read_lines(Fd, [], 1), 2648 StartLine = 2649 case LinesInFileR of 2650 [] -> 2651 1; 2652 [{_, _, EndLine} | _] -> 2653 EndLine 2654 end, 2655 LinesR = prepend_lines(LinesInFileR, Entries, StartLine), 2656 SortedLines = sort_lines(lists:reverse(LinesR), Order), 2657 _ = verify_lines(SortedLines, Check, undefined, []), 2658 %% Append to the file 2659 Write(Fd, Entries) 2660 of 2661 ok -> 2662 close_config_file(Dir, FileName, Fd) 2663 catch 2664 Error -> 2665 S = erlang:get_stacktrace(), 2666 d("File append of ~s throwed: ~p~n ~p~n", 2667 [FileName, Error, S]), 2668 close_config_file(Dir, FileName, Fd), 2669 Error; 2670 C:E -> 2671 S = erlang:get_stacktrace(), 2672 d("File append of ~s exception: ~p:~p~n ~p~n", 2673 [FileName,C,E,S]), 2674 close_config_file(Dir, FileName, Fd), 2675 {error, {failed_append, Dir, FileName, {C, E, S}}} 2676 end. 2677 2678%% Fake line numbers, one per entry 2679prepend_lines(Lines, [], _) -> 2680 Lines; 2681prepend_lines(Lines, [Entry | Entries], StartLine) -> 2682 EndLine = StartLine + 1, 2683 prepend_lines([{StartLine, Entry, EndLine} | Lines], Entries, EndLine). 2684 2685 2686 2687-spec read_config_file( 2688 Dir :: string(), 2689 FileName :: string(), 2690 Order :: order_config_entry_function(), 2691 Check :: check_config_entry_function()) -> 2692 {ok, Config :: [Entry :: term()]} | 2693 {error, Reason :: term()}. 2694 2695read_config_file(Dir, FileName, Order, Check) 2696 when is_list(Dir), is_list(FileName), 2697 is_function(Order), is_function(Check) -> 2698 case file:open(filename:join(Dir, FileName), [read]) of 2699 {ok, Fd} -> 2700 try 2701 Lines = lists:reverse(read_lines(Fd, [], 1)), 2702 SortedLines = sort_lines(Lines, Order), 2703 {ok, verify_lines(SortedLines, Check, undefined, [])} 2704 catch 2705 Error -> 2706 S = erlang:get_stacktrace(), 2707 d("File read of ~s throwed: ~p~n ~p~n", 2708 [FileName, Error, S]), 2709 {error, Error}; 2710 T:E -> 2711 S = erlang:get_stacktrace(), 2712 d("File read of ~s exception: ~p:~p~n ~p~n", 2713 [FileName,T,E,S]), 2714 {error, {failed_read, Dir, FileName, {T, E, S}}} 2715 after 2716 file:close(Fd) 2717 end; 2718 {error, Reason} -> 2719 {error, {Reason, FileName}} 2720 end. 2721 2722read_lines(Fd, Acc, StartLine) -> 2723 case read_and_parse_term(Fd, StartLine) of 2724 {ok, Term, EndLine} -> 2725 read_lines(Fd, [{StartLine, Term, EndLine}|Acc], EndLine); 2726 {error, Error, EndLine} -> 2727 throw({failed_reading, StartLine, EndLine, Error}); 2728 {eof, _EndLine} -> 2729 Acc 2730 end. 2731 2732read_and_parse_term(Fd, StartLine) -> 2733 case io:request(Fd, {get_until, "", erl_scan, tokens, [StartLine]}) of 2734 {ok, Tokens, EndLine} -> 2735 case erl_parse:parse_term(Tokens) of 2736 {ok, Term} -> 2737 {ok, Term, EndLine}; 2738 {error, {Line, erl_parse, Error}} -> 2739 {error, {parse_error, Error}, Line} 2740 end; 2741 Other -> 2742 Other 2743 end. 2744 2745sort_lines(Lines, Order) -> 2746 lists:sort( 2747 fun ({_, T1, _}, {_, T2, _}) -> 2748 Order(T1, T2) 2749 end, Lines). 2750 2751verify_lines([], _, _, Acc) -> 2752 lists:reverse(Acc); 2753verify_lines( 2754 [{StartLine, Term, EndLine}|Lines], Check, State, Acc) -> 2755 try Check(Term, State) of 2756 {Terms, NewState} when is_list(Terms) -> 2757 verify_lines(Lines, Check, NewState, Terms ++ Acc); 2758 {ok, NewState} -> 2759 verify_lines(Lines, Check, NewState, [Term|Acc]); 2760 {{ok, NewTerm}, NewState} -> 2761 verify_lines(Lines, Check, NewState, [NewTerm|Acc]) 2762 catch 2763 {error, Reason} -> 2764 throw({failed_check, StartLine, EndLine, Reason}); 2765 C:R -> 2766 S = erlang:get_stacktrace(), 2767 throw({failed_check, StartLine, EndLine, {C, R, S}}) 2768 end. 2769 2770 2771agent_snmp_mk_secret(Alg, Passwd, EngineID) -> 2772 snmp_usm:passwd2localized_key(Alg, Passwd, EngineID). 2773 2774 2775ensure_crypto_started() -> 2776 i("making sure crypto server is started..."), 2777 ensure_started(crypto). 2778 2779ensure_started(App) -> 2780 case (catch App:start()) of 2781 ok -> 2782 ok; 2783 {error, {already_started, App}} -> 2784 ok; 2785 E -> 2786 error({failed_starting, App, E}) 2787 end. 2788 2789 2790%% ------------------------------------------------------------------------- 2791 2792d(F, A) -> 2793 i("DBG: " ++ F, A). 2794 2795i(F) -> 2796 i(F, []). 2797 2798i(F, A) -> 2799 io:format(F ++ "~n", A). 2800 2801error(R) -> 2802 throw({error, R}). 2803