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