This file is indexed.

/usr/share/check_mk/modules/check_mk.py is in check-mk-server 1.1.12-1ubuntu1.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2010             mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software;  you can redistribute it and/or modify it
# under the  terms of the  GNU General Public License  as published by
# the Free Software Foundation in version 2.  check_mk is  distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY;  with-
# out even the implied warranty of  MERCHANTABILITY  or  FITNESS FOR A
# PARTICULAR PURPOSE. See the  GNU General Public License for more de-
# ails.  You should have  received  a copy of the  GNU  General Public
# License along with GNU Make; see the file  COPYING.  If  not,  write
# to the Free Software Foundation, Inc., 51 Franklin St,  Fifth Floor,
# Boston, MA 02110-1301 USA.

# This file is also read in by check_mk's web pages. In that case,
# the variable check_mk_web is set to True

import os, sys, socket, time, getopt, glob, re, stat, py_compile, urllib

# These variable will be substituted at 'make dist' time
check_mk_version  = '(inofficial)'

# Some things have to be done before option parsing and might
# want to output some verbose messages.
g_profile      = None
g_profile_path = 'profile.out'

if __name__ == "__main__":
    opt_debug        = '--debug' in sys.argv[1:]
    opt_verbose      = opt_debug or '-v' in sys.argv[1:] or '--verbose' in sys.argv[1:]
    if '--profile' in sys.argv[1:]:
        import cProfile
        g_profile = cProfile.Profile()
        g_profile.enable()
        if opt_verbose:
            sys.stderr.write("Enabled profiling.\n")
        
else:
    opt_verbose = False
    opt_debug = False

# are we running OMD? If yes, honor local/ hierarchy
omd_root = os.getenv("OMD_ROOT", None)
if omd_root:
    local_share              = omd_root + "/local/share/check_mk"
    local_checks_dir         = local_share + "/checks"
    local_check_manpages_dir = local_share + "/checkman"
    local_agents_dir         = local_share + "/agents"
    local_web_dir            = local_share + "/web"
    local_pnp_templates_dir  = local_share + "/pnp-templates"
    local_pnp_rraconf_dir    = local_share + "/pnp-rraconf"
    local_doc_dir            = omd_root + "/local/share/doc/check_mk"
    local_locale_dir         = local_share + "/locale"
else:
    local_checks_dir         = None
    local_check_manpages_dir = None
    local_agents_dir         = None
    local_web_dir            = None
    local_pnp_templates_dir  = None
    local_pnp_rraconf_dir    = None
    local_doc_dir            = None
    local_locale_dir         = None



#   +----------------------------------------------------------------------+
#   |        ____       _   _                                              |
#   |       |  _ \ __ _| |_| |__  _ __   __ _ _ __ ___   ___  ___          |
#   |       | |_) / _` | __| '_ \| '_ \ / _` | '_ ` _ \ / _ \/ __|         |
#   |       |  __/ (_| | |_| | | | | | | (_| | | | | | |  __/\__ \         |
#   |       |_|   \__,_|\__|_| |_|_| |_|\__,_|_| |_| |_|\___||___/         |
#   |                                                                      |
#   +----------------------------------------------------------------------+

# Pathnames, directories   and  other  settings.  All  these  settings
# should be  overriden by  /usr/share/check_mk/modules/defaults, which
# is created by setup.sh. The user might override those values again
# in main.mk

default_config_dir                 = '/etc/check_mk'
check_mk_configdir                 = default_config_dir + "/conf.d"
checks_dir                         = '/usr/share/check_mk/checks'
agents_dir                         = '/usr/share/check_mk/agents'
check_manpages_dir                 = '/usr/share/doc/check_mk/checks'
modules_dir                        = '/usr/share/check_mk/modules'
var_dir                            = '/var/lib/check_mk'
autochecksdir                      = var_dir + '/autochecks'
snmpwalks_dir                      = var_dir + '/snmpwalks'
precompiled_hostchecks_dir         = var_dir + '/precompiled'
counters_directory                 = var_dir + '/counters'
tcp_cache_dir                      = var_dir + '/cache'
logwatch_dir                       = var_dir + '/logwatch'
nagios_objects_file                = var_dir + '/check_mk_objects.cfg'
nagios_command_pipe_path           = '/usr/local/nagios/var/rw/nagios.cmd'
check_result_path                  = '/usr/local/nagios/var/spool/checkresults'
www_group                          = None # unset
nagios_startscript                 = '/etc/init.d/nagios'
nagios_binary                      = '/usr/sbin/nagios'
nagios_config_file                 = '/etc/nagios/nagios.cfg'
logwatch_notes_url                 = "/nagios/logwatch.php?host=%s&file=%s"

def verbose(t):
    if opt_verbose:
        sys.stderr.write(t)
        sys.stderr.flush()


# During setup a file called defaults is created in the modules
# directory.  In this file all directories are configured.  We need to
# read in this file first. It tells us where to look for our
# configuration file. In python argv[0] always contains the directory,
# even if the binary lies in the PATH and is called without
# '/'. This allows us to find our directory by taking everying up to
# the first '/'

# Allow to specify defaults file on command line (needed for OMD)
if len(sys.argv) >= 2 and sys.argv[1] == '--defaults':
    defaults_path = sys.argv[2]
    del sys.argv[1:3]
elif __name__ == "__main__":
    defaults_path = os.path.dirname(sys.argv[0]) + "/defaults"

if opt_debug:
    sys.stderr.write("Reading default settings from %s\n" % defaults_path)
try:
    execfile(defaults_path)
except Exception, e:
    sys.stderr.write(("ERROR: Cannot read installation settings of check_mk.\n%s\n\n"+
                      "During setup the file '%s'\n"+
                      "should have been created. Please make sure that that file\n"+
                      "exists, is readable and contains valid Python code.\n") %
                     (e, defaults_path))
    sys.exit(3)

# Now determine the location of the directory containing main.mk. It
# is searched for at several places:
#
# 1. if present - the option '-c' specifies the path to main.mk
# 2. in the default_config_dir (that path should be present in modules/defaults)


if __name__ == "__main__":
    try:
        i = sys.argv.index('-c')
        if i > 0 and i < len(sys.argv)-1:
            check_mk_configfile = sys.argv[i+1]
            parts = check_mk_configfile.split('/')
            if len(parts) > 1:
                check_mk_basedir = check_mk_configfile.rsplit('/',1)[0]
            else:
                check_mk_basedir = "." # no / contained in filename

            if not os.path.exists(check_mk_basedir):
                sys.stderr.write("Directory %s does not exist.\n" % check_mk_basedir)
                sys.exit(1)

            if not os.path.exists(check_mk_configfile):
                sys.stderr.write("Missing configuration file %s.\n" % check_mk_configfile)
                sys.exit(1)
        else:
            sys.stderr.write("Missing argument to option -c.\n")
            sys.exit(1)

    except ValueError:
        if not os.path.exists(default_config_dir + "/main.mk"):
            sys.stderr.write("Missing main configuration file %s/main.mk\n" % default_config_dir)
            sys.exit(4)
        check_mk_basedir = default_config_dir
        check_mk_configfile = check_mk_basedir + "/main.mk"

    except SystemExit, exitcode:
        sys.exit(exitcode)

else:
    check_mk_basedir = default_config_dir
    check_mk_configfile = default_config_dir + "/main.mk"


#   +----------------------------------------------------------------------+
#   |        ____       _     ____        __             _ _               |
#   |       / ___|  ___| |_  |  _ \  ___ / _| __ _ _   _| | |_ ___         |
#   |       \___ \ / _ \ __| | | | |/ _ \ |_ / _` | | | | | __/ __|        |
#   |        ___) |  __/ |_  | |_| |  __/  _| (_| | |_| | | |_\__ \        |
#   |       |____/ \___|\__| |____/ \___|_|  \__,_|\__,_|_|\__|___/        |
#   |                                                                      |
#   +----------------------------------------------------------------------+

# Before we read the configuration files we create default settings
# for all variables. The user can easily override them.

# define magic keys for use in host extraconf lists
PHYSICAL_HOSTS = [ '@physical' ] # all hosts but not clusters
CLUSTER_HOSTS  = [ '@cluster' ]  # all cluster hosts
ALL_HOSTS      = [ '@all' ]      # physical and cluster hosts
ALL_SERVICES   = [ "" ]          # optical replacement"
NEGATE         = '@negate'       # negation in boolean lists

# Basic Settings
agent_port                         = 6556
agent_ports                        = []
snmp_ports                         = [] # UDP ports used for SNMP
tcp_connect_timeout                = 5.0
delay_precompile                   = False  # delay Python compilation to Nagios execution
check_submission                   = "pipe" # alternative: "file"
aggr_summary_hostname              = "%s-s"
agent_min_version                  = 0 # warn, if plugin has not at least version
check_max_cachefile_age            = 0 # per default do not use cache files when checking
cluster_max_cachefile_age          = 90   # secs.
simulation_mode                    = False
agent_simulator                    = False
perfdata_format                    = "pnp" # also possible: "standard"
debug_log                          = None
monitoring_host                    = "localhost" # your Nagios host
max_num_processes                  = 50

# SNMP communities and encoding
snmp_default_community             = 'public'
snmp_communities                   = []
snmp_character_encodings           = []

# Inventory and inventory checks
inventory_check_interval           = None # Nagios intervals (4h = 240)
inventory_check_severity           = 1    # warning
inventory_max_cachefile_age        = 120  # secs.
always_cleanup_autochecks          = False

# Nagios templates and other settings concerning generation
# of Nagios configuration files. No need to change these values.
# Better adopt the content of the templates
host_template                      = 'check_mk_host'
cluster_template                   = 'check_mk_cluster'
pingonly_template                  = 'check_mk_pingonly'
active_service_template            = 'check_mk_active'
inventory_check_template           = 'check_mk_inventory'
passive_service_template           = 'check_mk_passive'
passive_service_template_perf      = 'check_mk_passive_perf'
summary_service_template           = 'check_mk_summarized'
service_dependency_template        = 'check_mk'
default_host_group                 = 'check_mk'
generate_hostconf                  = True
generate_dummy_commands            = True
dummy_check_commandline            = 'echo "ERROR - you did an active check on this service - please disable active checks" && exit 1'
nagios_illegal_chars               = '`~!$%^&*|\'"<>?,()='

# Data to be defined in main.mk
checks                               = []
check_parameters                     = []
legacy_checks                        = []
all_hosts                            = []
host_paths                           = {}
snmp_hosts                           = [ (['snmp'], ALL_HOSTS) ]
tcp_hosts                            = [ (['tcp'], ALL_HOSTS), (NEGATE, ['snmp'], ALL_HOSTS), (['!ping'], ALL_HOSTS) ]
bulkwalk_hosts                       = []
usewalk_hosts                        = []
dyndns_hosts                         = [] # use host name as ip address for these hosts
ignored_checktypes                   = [] # exclude from inventory
ignored_services                     = [] # exclude from inventory
ignored_checks                       = [] # exclude from inventory
host_groups                          = []
service_groups                       = []
service_contactgroups                = []
service_notification_periods         = []
host_notification_periods            = []
host_contactgroups                   = []
parents                              = []
define_hostgroups                    = None
define_servicegroups                 = None
define_contactgroups                 = None
clusters                             = {}
clustered_services                   = []
clustered_services_of                = {} # new in 1.1.4
datasource_programs                  = []
service_aggregations                 = []
service_dependencies                 = []
non_aggregated_hosts                 = []
aggregate_check_mk                   = False
aggregation_output_format            = "multiline" # new in 1.1.6. Possible also: "multiline"
summary_host_groups                  = []
summary_service_groups               = [] # service groups for aggregated services
summary_service_contactgroups        = [] # service contact groups for aggregated services
summary_host_notification_periods    = []
summary_service_notification_periods = []
ipaddresses                          = {} # mapping from hostname to ipadress
only_hosts                           = None
extra_host_conf                      = {}
extra_summary_host_conf              = {}
extra_service_conf                   = {}
extra_summary_service_conf           = {}
extra_nagios_conf                    = ""
service_descriptions                 = {}
donation_hosts                       = []
donation_command                     = 'mail -r checkmk@yoursite.de  -s "Host donation %s" donatehosts@mathias-kettner.de' % check_mk_version
scanparent_hosts                     = [ ( ALL_HOSTS ) ]
host_attributes                      = {} # needed by WATO, ignored by Check_MK

# global variables used to cache temporary values (not needed in check_mk_base)
ip_to_hostname_cache = None

# The following data structures will be filled by the various checks
# found in the checks/ directory.
check_info                         = {} # all known checks
check_includes                     = {} # library files needed by checks
precompile_params                  = {} # optional functions for parameter precompilation, look at df for an example
check_default_levels               = {} # dictionary-configured checks declare their default level variables here
factory_settings                   = {} # factory settings for dictionary-configured checks
check_config_variables             = [] # variables (names) in checks/* needed for check itself
snmp_info                          = {} # whichs OIDs to fetch for which check (for tabular information)
snmp_scan_functions                = {} # SNMP autodetection


# Now include the other modules. They contain everything that is needed
# at check time (and many of that is also needed at administration time).
try:
    modules =  [ 'check_mk_base', 'snmp' ]
    for module in modules:
        filename = modules_dir + "/" + module + ".py"
        execfile(filename)

except Exception, e:
    sys.stderr.write("Cannot read file %s: %s\n" % (filename, e))
    sys.exit(5)



#   +----------------------------------------------------------------------+
#   |     ____ _               _      _          _                         |
#   |    / ___| |__   ___  ___| | __ | |__   ___| |_ __   ___ _ __ ___     |
#   |   | |   | '_ \ / _ \/ __| |/ / | '_ \ / _ \ | '_ \ / _ \ '__/ __|    |
#   |   | |___| | | |  __/ (__|   <  | | | |  __/ | |_) |  __/ |  \__ \    |
#   |    \____|_| |_|\___|\___|_|\_\ |_| |_|\___|_| .__/ \___|_|  |___/    |
#   |                                             |_|                      |
#   |                                                                      |
#   | These functions are used by some checks at administration time.      |
#   +----------------------------------------------------------------------+

# The function no_inventory_possible is as stub function used for
# those checks that do not support inventory. It must be known before
# we read in all the checks
def no_inventory_possible(checkname, info):
    sys.stderr.write("Sorry. No inventory possible for check type %s.\n" % checkname)
    sys.exit(3)


#   +----------------------------------------------------------------------+
#   |       _                    _        _               _                |
#   |      | |    ___   __ _  __| |   ___| |__   ___  ___| | _____         |
#   |      | |   / _ \ / _` |/ _` |  / __| '_ \ / _ \/ __| |/ / __|        |
#   |      | |__| (_) | (_| | (_| | | (__| | | |  __/ (__|   <\__ \        |
#   |      |_____\___/ \__,_|\__,_|  \___|_| |_|\___|\___|_|\_\___/        |
#   |                                                                      |
#   +----------------------------------------------------------------------+

# Now read in all checks. Note: this is done *before* reading the
# configuration, because checks define variables with default
# values. The user can override those variables in his configuration.
# Do not read in the checks if check_mk is called as module

if __name__ == "__main__":
    filelist = glob.glob(checks_dir + "/*")
    filelist.sort()

    # read local checks *after* shipped ones!
    if local_checks_dir:
        local_files = glob.glob(local_checks_dir + "/*")
        local_files.sort()
        filelist += local_files

    # read include files always first, but still in the sorted
    # order with local ones last (possibly overriding variables)
    filelist = [ f for f in filelist if f.endswith(".include") ] + \
               [ f for f in filelist if not f.endswith(".include") ]

    for f in filelist: 
        if not f.endswith("~"): # ignore emacs-like backup files
            try:
                execfile(f)
            except Exception, e:
                sys.stderr.write("Error in plugin file %s: %s\n" % (f, e))
                if opt_debug:
                    raise
                sys.exit(5)


#   +----------------------------------------------------------------------+
#   |                    ____ _               _                            |
#   |                   / ___| |__   ___  ___| | _____                     |
#   |                  | |   | '_ \ / _ \/ __| |/ / __|                    |
#   |                  | |___| | | |  __/ (__|   <\__ \                    |
#   |                   \____|_| |_|\___|\___|_|\_\___/                    |
#   |                                                                      |
#   +----------------------------------------------------------------------+

def have_perfdata(checkname):
    try:
        return check_info[checkname][2]
    except:
        return False

def output_check_info():
    print "Available check types:"
    print
    print "                      plugin   perf-  in- "
    print "Name                  type     data   vent.  service description"
    print "-------------------------------------------------------------------------"

    checks_sorted = check_info.items()
    checks_sorted.sort()
    for check_type, info in checks_sorted:
        try:
            func, itemstring, have_perfdata, invfunc = info
            if have_perfdata == 1:
                p = tty_green + tty_bold + "yes" + tty_normal
            else:
                p = "no"
            if invfunc == no_inventory_possible:
                i = "no"
            else:
                i = tty_blue + tty_bold + "yes" + tty_normal

            if check_uses_snmp(check_type):
                typename = tty_magenta + "snmp" + tty_normal
            else:
                typename = tty_yellow + "tcp " + tty_normal

            print (tty_bold + "%-19s" + tty_normal + "   %s     %-3s    %-3s    %s") % \
                  (check_type, typename, p, i, itemstring)
        except Exception, e:
            sys.stderr.write("ERROR in check_type %s: %s\n" % (check_type, e))



#   +----------------------------------------------------------------------+
#   |              _   _           _     _                                 |
#   |             | | | | ___  ___| |_  | |_ __ _  __ _ ___                |
#   |             | |_| |/ _ \/ __| __| | __/ _` |/ _` / __|               |
#   |             |  _  | (_) \__ \ |_  | || (_| | (_| \__ \               |
#   |             |_| |_|\___/|___/\__|  \__\__,_|\__, |___/               |
#   |                                             |___/                    |
#   +----------------------------------------------------------------------+

def strip_tags(host_or_list):
    if type(host_or_list) == list:
        return [ strip_tags(h) for h in host_or_list ]
    else:
        return host_or_list.split("|")[0]

def tags_of_host(hostname):
    return hosttags.get(hostname, [])

# Check if a host fullfills the requirements of a tags
# list. The host must have all tags in the list, except
# for those negated with '!'. Those the host must *not* have!
def hosttags_match_taglist(hosttags, required_tags):
    for tag in required_tags:
        if len(tag) > 0 and tag[0] == '!':
            negate = True
            tag = tag[1:]
        else:
            negate = False
        if (tag in hosttags) == negate:
            return False
    return True

#   +----------------------------------------------------------------------+
#   |         _                                    _   _                   |
#   |        / \   __ _  __ _ _ __ ___  __ _  __ _| |_(_) ___  _ __        |
#   |       / _ \ / _` |/ _` | '__/ _ \/ _` |/ _` | __| |/ _ \| '_ \       |
#   |      / ___ \ (_| | (_| | | |  __/ (_| | (_| | |_| | (_) | | | |      |
#   |     /_/   \_\__, |\__, |_|  \___|\__, |\__,_|\__|_|\___/|_| |_|      |
#   |             |___/ |___/          |___/                               |
#   +----------------------------------------------------------------------+

# Checks if a host has service aggregations
def host_is_aggregated(hostname):
    if not service_aggregations:
        return False

    # host might by explicitely configured as not aggregated
    if in_binary_hostlist(hostname, non_aggregated_hosts):
        return False

    # convert into host_conf_list suitable for host_extra_conf()
    host_conf_list = [ entry[:-1] for entry in service_aggregations ]
    is_aggr = len(host_extra_conf(hostname, host_conf_list)) > 0
    return is_aggr

# Determines the aggretated service name for a given
# host and service description. Returns "" if the service
# is not aggregated
def aggregated_service_name(hostname, servicedesc):
    if not service_aggregations:
        return ""

    for entry in service_aggregations:
        if len(entry) == 3:
            aggrname, hostlist, pattern = entry
            tags = []
        elif len(entry) == 4:
            aggrname, tags, hostlist, pattern = entry
        else:
            raise MKGeneralException("Invalid entry '%r' in service_aggregations: must have 3 or 4 entries" % entry)

        if len(hostlist) == 1 and hostlist[0] == "":
            sys.stderr.write('WARNING: deprecated hostlist [ "" ] in service_aggregations. Please use all_hosts instead\n')

        if hosttags_match_taglist(tags_of_host(hostname), tags) and \
           in_extraconf_hostlist(hostlist, hostname):
            if type(pattern) != str:
                raise MKGeneralException("Invalid entry '%r' in service_aggregations:\n "
                                         "service specification must be a string, not %s.\n" %
                                         (entry, pattern))
            matchobject = re.search(pattern, servicedesc)
            if matchobject:
                try:
                    item = matchobject.groups()[-1]
                    return aggrname % item
                except:
                    return aggrname
    return ""


#   +----------------------------------------------------------------------+
#   |                      ____  _   _ __  __ ____                         |
#   |                     / ___|| \ | |  \/  |  _ \                        |
#   |                     \___ \|  \| | |\/| | |_) |                       |
#   |                      ___) | |\  | |  | |  __/                        |
#   |                     |____/|_| \_|_|  |_|_|                           |
#   |                                                                      |
#   +----------------------------------------------------------------------+

# Returns command lines for snmpwalk and snmpget including
# options for authentication. This handles communities and
# authentication for SNMP V3. Also bulkwalk hosts
def snmp_walk_command(hostname):
    return snmp_base_command('walk', hostname) + " -Cc"

# Constructs the basic snmp commands for a host with all important information
# like the commandname, SNMP version and credentials.
# This function also changes snmpbulkwalk to snmpwalk for snmpv1.
def snmp_base_command(what, hostname):
    # if the credentials are a string, we use that as community,
    # if it is a four-tuple, we use it as V3 auth parameters:
    # (1) security level (-l)
    # (2) auth protocol (-a, e.g. 'md5')
    # (3) security name (-u)
    # (4) auth password (-A)
    # And if it is a six-tuple, it has the following additional arguments:
    # (5) privacy protocol (DES|AES) (-x)  
    # (6) privacy protocol pass phrase (-X) 

    credentials = snmp_credentials_of(hostname)
    if what == 'get':
        command = 'snmpget'
    elif what == 'getnext':
        command = 'snmpgetnext -Cf'
    else:
        command = 'snmpbulkwalk'

    # Handle V1 and V2C
    if type(credentials) == str:
        if is_bulkwalk_host(hostname):
            options = '-v2c'
        else:
            options = '-v1'
            if what == 'walk':
                command = 'snmpwalk'
        options += " -c '%s'" % credentials

        # Handle V3
    else:
        if len(credentials) == 6:
           options = "-v3 -l '%s' -a '%s' -u '%s' -A '%s' -x '%s' -X '%s'" % tuple(credentials)
        elif len(credentials) == 4:
           options = "-v3 -l '%s' -a '%s' -u '%s' -A '%s'" % tuple(credentials)
        else:
            raise MKGeneralException("Invalid SNMP credentials '%r' for host %s: must be string, 4-tuple or 6-tuple" % (credentials, hostname))

    # Do not load *any* MIB files. This save lot's of CPU.
    options += " -m '' -M ''"
    return command + ' ' + options


# Determine SNMP community for a specific host.  It the host is found
# int the map snmp_communities, that community is returned. Otherwise
# the snmp_default_community is returned (wich is preset with
# "public", but can be overridden in main.mk
def snmp_credentials_of(hostname):
    communities = host_extra_conf(hostname, snmp_communities)
    if len(communities) > 0:
        return communities[0]

    # nothing configured for this host -> use default
    return snmp_default_community

def get_snmp_character_encoding(hostname):
    entries = host_extra_conf(hostname, snmp_character_encodings)
    if len(entries) > 0:
        return entries[0]

def check_uses_snmp(check_type):
    check_name = check_type.split(".")[0]
    return snmp_info.has_key(check_name)

def is_snmp_host(hostname):
    return in_binary_hostlist(hostname, snmp_hosts)

def is_tcp_host(hostname):
    return in_binary_hostlist(hostname, tcp_hosts)

def is_ping_host(hostname):
    return not is_snmp_host(hostname) and not is_tcp_host(hostname)

def is_bulkwalk_host(hostname):
    if bulkwalk_hosts:
        return in_binary_hostlist(hostname, bulkwalk_hosts)
    else:
        return False

def is_usewalk_host(hostname):
    return in_binary_hostlist(hostname, usewalk_hosts)

def get_single_oid(hostname, ipaddress, oid):
    # New in Check_MK 1.1.11: oid can end with ".*". In that case
    # we do a snmpgetnext and try to find an OID with the prefix
    # in question. The *cache* is working including the X, however.

    global g_single_oid_hostname
    global g_single_oid_cache

    if g_single_oid_hostname != hostname:
        g_single_oid_hostname = hostname
        g_single_oid_cache = {}

    if oid in g_single_oid_cache:
        return g_single_oid_cache[oid]

    if opt_use_snmp_walk or is_usewalk_host(hostname):
        walk = get_stored_snmpwalk(hostname, oid)
        if len(walk) == 1:
            return walk[0][1]
        else:
            return None

    if oid.endswith(".*"):
        oid_prefix = oid[:-2]
        commandtype = "getnext"
    else:
        oid_prefix = oid
        commandtype = "get"

    portspec = snmp_port_spec(hostname)
    command = snmp_base_command(commandtype, hostname) + \
         " -On -OQ -Oe -Ot %s%s %s 2>/dev/null" % (ipaddress, portspec, oid_prefix)
    try:
        if opt_debug:
            sys.stdout.write("Running '%s'\n" % command)

        snmp_process = os.popen(command, "r")
        line = snmp_process.readline().strip()
        item, value = line.split("=")
        value = value.strip()
        if opt_debug:
            sys.stdout.write("SNMP answer: ==> [%s]\n" % value)
        if value.startswith('No more variables') or value.startswith('End of MIB') \
           or value.startswith('No Such Object available') or value.startswith('No Such Instance currently exists'):
            value = None

        # In case of .*, check if prefix is the one we are looking for
        if commandtype == "getnext" and not item.startswith(oid_prefix + "."):
            value = None

        # Strip quotes
        if value.startswith('"') and value.endswith('"'):
            value = value[1:-1]
        # try to remove text, only keep number
        # value_num = value_text.split(" ")[0]
        # value_num = value_num.lstrip("+")
        # value_num = value_num.rstrip("%")
        # value = value_num
    except:
        value = None

    g_single_oid_cache[oid] = value
    return value

def snmp_scan(hostname, ipaddress):
    if opt_verbose:
        sys.stdout.write("Scanning host %s(%s) for SNMP checks..." % (hostname, ipaddress))
    sys_descr = get_single_oid(hostname, ipaddress, ".1.3.6.1.2.1.1.1.0")
    if sys_descr == None:
        if opt_debug:
            sys.stderr.write("no SNMP answer\n")
        return []

    found = []
    items = snmp_scan_functions.items()
    items.sort()
    for checktype, detect_function in items:
        if checktype in ignored_checktypes:
            continue
        try:
            if detect_function(lambda oid: get_single_oid(hostname, ipaddress, oid)):
                found.append(checktype)
                if opt_verbose:
                    sys.stdout.write(tty_green + tty_bold + checktype + " " + tty_normal)
                    sys.stdout.flush()
        except:
            pass

    # Now try all checks not having a scan function
    items = check_info.keys()
    items.sort()
    for checktype in items:
        if checktype in ignored_checktypes:
            continue

        datatype = checktype.split('.')[0]
        if not check_uses_snmp(datatype):
            continue # no snmp check
        if checktype not in snmp_scan_functions:
            if opt_verbose:
                sys.stdout.write(tty_blue + tty_bold + checktype + tty_normal + " ")
                sys.stdout.flush()
            found.append(checktype)

    if opt_verbose:
        if found == []:
            sys.stdout.write("nothing detected.\n")
        else:
            sys.stdout.write("\n")
    return found


#   +----------------------------------------------------------------------+
#   |                    ____ _           _                                |
#   |                   / ___| |_   _ ___| |_ ___ _ __                     |
#   |                  | |   | | | | / __| __/ _ \ '__|                    |
#   |                  | |___| | |_| \__ \ ||  __/ |                       |
#   |                   \____|_|\__,_|___/\__\___|_|                       |
#   |                                                                      |
#   +----------------------------------------------------------------------+

# clusternames (keys into dictionary) might be tagged :-(
# names of nodes not!
def is_cluster(hostname):
    for tagged_hostname, nodes in clusters.items():
        if hostname == strip_tags(tagged_hostname):
            return True
    return False

# If host is node of a cluster, return name of that cluster
# (untagged). If not, return None. If a host belongs to
# more than one cluster, then a random cluster is choosen.
def cluster_of(hostname):
    for clustername, nodes in clusters.items():
        if hostname in nodes:
            return strip_tags(clustername)
    return None

# Determine wether a service (found on a physical host) is a clustered
# service and - if yes - return the cluster host of the service. If
# no, returns the hostname of the physical host.
def host_of_clustered_service(hostname, servicedesc):
    my_cluster = cluster_of(hostname)
    if not my_cluster: # we are not a cluster node
        return hostname

    # 1. New style: explicitlely assigned services
    for cluster, conf in clustered_services_of.items():
        nodes = nodes_of(cluster)
        if not nodes:
            raise MKGeneralException("Invalid entry clustered_services_of['%s']: %s is not a cluster." % 
                   (cluster, cluster))
        if hostname in nodes and \
            in_boolean_serviceconf_list(hostname, servicedesc, conf):
            return cluster

    # 1. Old style: clustered_services assumes that each host belong to
    #    exactly on cluster
    if in_boolean_serviceconf_list(hostname, servicedesc, clustered_services):
        cluster = cluster_of(hostname)
        if cluster:
            return cluster

    return hostname


#   +----------------------------------------------------------------------+
#   |          _   _           _       _               _                   |
#   |         | | | | ___  ___| |_ ___| |__   ___  ___| | _____            |
#   |         | |_| |/ _ \/ __| __/ __| '_ \ / _ \/ __| |/ / __|           |
#   |         |  _  | (_) \__ \ || (__| | | |  __/ (__|   <\__ \           |
#   |         |_| |_|\___/|___/\__\___|_| |_|\___|\___|_|\_\___/           |
#   |                                                                      |
#   +----------------------------------------------------------------------+


# Returns check table for a specific host
# Format: ( checkname, item ) -> (params, description )

# Keep a global cache of per-host-checktables, since this
# operation is quiet lengty.
g_check_table_cache = {}
# A further cache splits up all checks into single-host-entries
# and those possibly matching multiple hosts. The single host entries
# are used in the autochecks and assumed be make up the vast majority.
g_singlehost_checks = None
g_multihost_checks = None
def get_check_table(hostname):
    global g_singlehost_checks
    global g_multihost_checks

    # speed up multiple lookup of same host
    if hostname in g_check_table_cache:
        return g_check_table_cache[hostname]

    check_table = {}

    # First time? Split up all checks in single and
    # multi-host-checks
    if g_singlehost_checks == None:
        g_singlehost_checks = {}
        g_multihost_checks = []
        for entry in checks:
            if len(entry) == 4 and type(entry[0]) == str:
                g_singlehost_checks.setdefault(entry[0], []).append(entry)
            else:
                g_multihost_checks.append(entry)
        
    def handle_entry(entry):
        if len(entry) == 4:
            hostlist, checkname, item, params = entry
            tags = []
        elif len(entry) == 5:
            tags, hostlist, checkname, item, params = entry
            if type(tags) != list:
                raise MKGeneralException("Invalid entry '%r' in check table. First entry must be list of host tags." %
                                         (entry, ))

        else:
            raise MKGeneralException("Invalid entry '%r' in check table. It has %d entries, but must have 4 or 5." %
                                     (entry, len(entry)))

        # hostinfo list might be:
        # 1. a plain hostname (string)
        # 2. a list of hostnames (list of strings)
        # Hostnames may be tagged. Tags are removed.
        # In autochecks there are always single untagged hostnames.
        # We optimize for that. But: hostlist might be tagged hostname!
        if type(hostlist) == str:
            if hostlist != hostname:
                return # optimize most common case: hostname mismatch
            hostlist = [ strip_tags(hostlist) ]
        elif type(hostlist[0]) == str:
            hostlist = strip_tags(hostlist)
        elif hostlist != []:
            raise MKGeneralException("Invalid entry '%r' in check table. Must be single hostname or list of hostnames" % hostinfolist)

        if hosttags_match_taglist(tags_of_host(hostname), tags) and \
               in_extraconf_hostlist(hostlist, hostname):
            descr = service_description(checkname, item)
            deps  = service_deps(hostname, descr)
            check_table[(checkname, item)] = (params, descr, deps)

    # Now process all entries that are specific to the host
    # in search (single host) or that might match the host.
    for entry in g_singlehost_checks.get(hostname, []):
        handle_entry(entry)

    for entry in g_multihost_checks:
        handle_entry(entry)

    # Remove dependencies to non-existing services
    all_descr = set([ descr for ((checkname, item), (params, descr, deps)) in check_table.items() ])
    for (checkname, item), (params, descr, deps) in check_table.items():
        deeps = deps[:]
        del deps[:]
        for d in deeps:
            if d in all_descr:
                deps.append(d)

    g_check_table_cache[hostname] = check_table
    return check_table


def get_sorted_check_table(hostname):
    # Convert from dictionary into simple tuple list. Then sort
    # it according to the service dependencies.
    unsorted = [ (checkname, item, params, descr, deps)
                 for ((checkname, item), (params, descr, deps))
                 in get_check_table(hostname).items() ]
    def cmp(a, b):
        if a[3] < b[3]:
            return -1
        else:
            return 1
    unsorted.sort(cmp)


    sorted = []
    while len(unsorted) > 0:
        unsorted_descrs = set([ entry[3] for entry in unsorted ])
        left = []
        at_least_one_hit = False
        for check in unsorted:
            deps_fullfilled = True
            for dep in check[4]: # deps
                if dep in unsorted_descrs:
                    deps_fullfilled = False
                    break
            if deps_fullfilled:
                sorted.append(check)
                at_least_one_hit = True
            else:
                left.append(check)
        if len(left) == 0:
            break
        if not at_least_one_hit:
            raise MKGeneralException("Cyclic service dependency of host %s. Problematic are: %s" %
                                     (hostname, ",".join(unsorted_descrs)))
        unsorted = left
    return sorted



# Determine, which program to call to get data. Should
# be None in most cases -> to TCP connect on port 6556
def get_datasource_program(hostname, ipaddress):
    programs = host_extra_conf(hostname, datasource_programs)
    if len(programs) == 0:
        return None
    else:
        return programs[0].replace("<IP>", ipaddress).replace("<HOST>", hostname)

# Determine the IP address of a host
def lookup_ipaddress(hostname):
    # Quick hack, where all IP addresses are faked (--fake-dns)
    if fake_dns:
        return fake_dns

    # Honor simulation mode und usewalk hosts. Never contact the network.
    elif simulation_mode or opt_use_snmp_walk or \
         (is_usewalk_host(hostname) and is_snmp_host(hostname)):
        return "127.0.0.1"

    # Now check, if IP address is hard coded by the user
    ipa = ipaddresses.get(hostname)
    if ipa:
        return ipa

    # Hosts listed in dyndns hosts always use dynamic DNS lookup.
    # The use their hostname as IP address at all places
    if in_binary_hostlist(hostname, dyndns_hosts):
        return hostname
    
    # Address has already been resolved in prior call to this function?
    if hostname in g_dns_cache:
        return g_dns_cache[hostname]

    # No do the actual DNS lookup
    try:
        ipa = socket.gethostbyname(hostname)
    except:
        g_dns_cache[hostname] = None
        raise
    g_dns_cache[hostname] = ipa
    return ipa

def agent_port_of(hostname):
    ports = host_extra_conf(hostname, agent_ports)
    if len(ports) == 0:
        return agent_port
    else:
        return ports[0]

def snmp_port_of(hostname):
    ports = host_extra_conf(hostname, snmp_ports)
    if len(ports) == 0:
        return None # do not specify a port, use default
    else:
        return ports[0]

def snmp_port_spec(hostname):
    port = snmp_port_of(hostname)
    if port == None:
        return ""
    else:
        return ":%d" % port


def service_description(checkname, item):
    if checkname not in check_info:
        if item:
            return "Unimplmented check %s / %s" % (checkname, item)
        else:
            return "Unimplemented check %s" % checkname

        # raise MKGeneralException("Unknown check type '%s'.\n"
        #                         "Please use check_mk -L for a list of all check types.\n" % checkname)

    # use user-supplied service description, of available
    descr_format = service_descriptions.get(checkname)
    if not descr_format:
        descr_format = check_info[checkname][1]

    # Note: we strip the service description (remove spaces).
    # One check defines "Pages %s" as a desription, but the item
    # can by empty in some cases. Nagios silently drops leading
    # and trailing spaces in the configuration file.

    if type(item) == str:
        # Remove characters from item name that are banned by Nagios
        item_safe = "".join([ c for c in item if c not in nagios_illegal_chars ])
        if "%s" not in descr_format:
            descr_format += " %s"
        return (descr_format % (item_safe,)).strip()
    if type(item) == int or type(item) == long:
        if "%s" not in descr_format:
            descr_format += " %s"
        return (descr_format % (item,)).strip()
    else:
        return descr_format.strip()

#   +----------------------------------------------------------------------+
#   |    ____             __ _                     _               _       |
#   |   / ___|___  _ __  / _(_) __ _    ___  _   _| |_ _ __  _   _| |_     |
#   |  | |   / _ \| '_ \| |_| |/ _` |  / _ \| | | | __| '_ \| | | | __|    |
#   |  | |__| (_) | | | |  _| | (_| | | (_) | |_| | |_| |_) | |_| | |_     |
#   |   \____\___/|_| |_|_| |_|\__, |  \___/ \__,_|\__| .__/ \__,_|\__|    |
#   |                          |___/                  |_|                  |
#   +----------------------------------------------------------------------+

def output_conf_header(outfile):
    outfile.write("""#
# Created by Check_MK. Do not edit.
#

""")

def all_active_hosts():
    if only_hosts == None:
        return strip_tags(all_hosts)
    else:
        return [ hostname for hostname in strip_tags(all_hosts) \
                 if in_binary_hostlist(hostname, only_hosts) ]

def all_active_clusters():
    if only_hosts == None:
        return strip_tags(clusters.keys())
    else:
        return [ hostname for hostname in strip_tags(clusters.keys()) \
                 if in_binary_hostlist(hostname, only_hosts) ]

def hostgroups_of(hostname):
    return host_extra_conf(hostname, host_groups)

def summary_hostgroups_of(hostname):
    return host_extra_conf(hostname, summary_host_groups)

def host_contactgroups_of(hostlist):
    cgrs = []
    for host in hostlist:
        cgrs += host_extra_conf(host, host_contactgroups)
    return list(set(cgrs))

def host_contactgroups_nag(hostlist):
    cgrs = host_contactgroups_of(hostlist)
    if len(cgrs) > 0:
        return "    contact_groups +" + ",".join(cgrs) + "\n"
    else:
        return ""

def parents_of(hostname):
    par = host_extra_conf(hostname, parents)
    # Use only those parents which are defined and active in
    # all_hosts.
    used_parents = []
    for p in par:
        ps = p.split(",")
        for pss in ps:
            if pss in all_hosts_untagged:
                used_parents.append(pss)
    return used_parents

def extra_host_conf_of(hostname):
    return extra_conf_of(extra_host_conf, hostname, None)

def extra_summary_host_conf_of(hostname):
    return extra_conf_of(extra_summary_host_conf, hostname, None)

# Collect all extra configuration data for a service
def extra_service_conf_of(hostname, description):
    global contactgroups_to_define
    global servicegroups_to_define
    conf = ""

    # Contact groups
    sercgr = service_extra_conf(hostname, description, service_contactgroups)
    contactgroups_to_define.update(sercgr)
    if len(sercgr) > 0:
        conf += "  contact_groups\t\t+" + ",".join(sercgr) + "\n"

    sergr = service_extra_conf(hostname, description, service_groups)
    if len(sergr) > 0:
        conf += "  service_groups\t\t+" + ",".join(sergr) + "\n"
        if define_servicegroups:
            servicegroups_to_define.update(sergr)
    conf += extra_conf_of(extra_service_conf, hostname, description)
    return conf

def extra_summary_service_conf_of(hostname, description):
    return extra_conf_of(extra_summary_service_conf, hostname, description)

def extra_conf_of(confdict, hostname, service):
    result = ""
    for key, conflist in confdict.items():
        if service != None:
            values = service_extra_conf(hostname, service, conflist)
        else:
            values = host_extra_conf(hostname, conflist)
        if len(values) > 0:
            format = "  %-29s %s\n"
            result += format % (key, values[0])
    return result


# Return a list of services this services depends upon
def service_deps(hostname, servicedesc):
    deps = []
    for entry in service_dependencies:
        if len(entry) == 3:
            depname, hostlist, patternlist = entry
            tags = []
        elif len(entry) == 4:
            depname, tags, hostlist, patternlist = entry
        else:
            raise MKGeneralException("Invalid entry '%r' in service dependencies: must have 3 or 4 entries" % entry)

        if hosttags_match_taglist(tags_of_host(hostname), tags) and \
           in_extraconf_hostlist(hostlist, hostname):
            for pattern in patternlist:
                reg = compiled_regexes.get(pattern)
                if not reg:
                    reg = re.compile(pattern)
                    compiled_regexes[pattern] = reg
                matchobject = reg.search(servicedesc)
                if matchobject:
                    try:
                        item = matchobject.groups()[-1]
                        deps.append(depname % item)
                    except:
                        deps.append(depname)
    return deps


def host_extra_conf(hostname, conf):
    items = []
    if len(conf) == 1 and conf[0] == "":
        sys.stderr.write('WARNING: deprecated entry [ "" ] in host configuration list\n')

    for entry in conf:
        if len(entry) == 2:
            item, hostlist = entry
            tags = []
        elif len(entry) == 3:
            item, tags, hostlist = entry
        else:
            raise MKGeneralException("Invalid entry '%r' in host configuration list: must have 2 or 3 entries" % (entry,))

        if hosttags_match_taglist(tags_of_host(hostname), tags) and \
           in_extraconf_hostlist(hostlist, hostname):
            items.append(item)
    return items

def in_binary_hostlist(hostname, conf):
    # if we have just a list of strings just take it as list of (may be tagged) hostnames
    if len(conf) > 0 and type(conf[0]) == str:
        return hostname in strip_tags(conf)

    for entry in conf:
        try:
            # Negation via 'NEGATE'
            if entry[0] == NEGATE:
                entry = entry[1:]
                negate = True
            else:
                negate = False
            # entry should be one-tuple or two-tuple. Tuple's elements are
            # lists of strings. User might forget comma in one tuple. Then the
            # entry is the list itself.
            if type(entry) == list:
                hostlist = entry
                tags = []
            else:
                if len(entry) == 1: # 1-Tuple with list of hosts
                    hostlist = entry[0]
                    tags = []
                else:
                    tags, hostlist = entry

            if hosttags_match_taglist(tags_of_host(hostname), tags) and \
                   in_extraconf_hostlist(hostlist, hostname):
                return not negate

        except:
            MKGeneralException("Invalid entry '%r' in host configuration list: must be tupel with 1 or 2 entries" % (entry,))

    return False


# Compute list of service_groups or contact_groups of service
# conf is either service_groups or service_contactgroups
def service_extra_conf(hostname, service, conf):
    entries = []
    for entry in conf:
        if len(entry) == 3:
            item, hostlist, servlist = entry
            tags = []
        elif len(entry) == 4:
            item, tags, hostlist, servlist = entry
        else:
            raise MKGeneralException("Invalid entry '%r' in service configuration list: must have 3 or 4 elements" % (entry,))

        if hosttags_match_taglist(tags_of_host(hostname), tags) and \
           in_extraconf_hostlist(hostlist, hostname) and \
           in_extraconf_servicelist(servlist, service):
            entries.append(item)
    return entries



# Entries in list are (tagged) hostnames that must equal the
# (untagged) hostname. Expressions beginning with ! are negated: if
# they match, the item is excluded from the list. Also the three
# special tags '@all', '@clusters', '@physical' are allowed.
def in_extraconf_hostlist(hostlist, hostname):

    # Migration help: print error if old format appears in config file
    if len(hostlist) == 1 and hostlist[0] == "":
        raise MKGeneralException('Invalid emtpy entry [ "" ] in configuration')

    for hostentry in hostlist:
        if len(hostentry) == 0:
            raise MKGeneralException('Empty hostname in host list %r' % hostlist)
        if hostentry[0] == '@':
            if hostentry == '@all':
                return True
            ic = is_cluster(hostname)
            if hostentry == '@cluster' and ic:
                return True
            elif hostentry == '@physical' and not ic:
                return True

        # Allow negation of hostentry with prefix '!'
        elif hostentry[0] == '!':
            hostentry = hostentry[1:]
            negate = True
        else:
            negate = False

        if hostname == strip_tags(hostentry):
            return not negate

    return False

def in_extraconf_servicelist(list, item):
    for pattern in list:
        # Allow negation of pattern with prefix '!'
        if len(pattern) > 0 and pattern[0] == '!':
            pattern = pattern[1:]
            negate = True
        else:
            negate = False

        reg = compiled_regexes.get(pattern)
        if not reg:
            reg = re.compile(pattern)
            compiled_regexes[pattern] = reg
        if reg.match(item):
            return not negate

    # no match in list -> negative answer
    return False


# NEW IMPLEMENTATION
def create_nagios_config(outfile = sys.stdout, hostnames = None):
    global hostgroups_to_define
    hostgroups_to_define = set([])
    global servicegroups_to_define
    servicegroups_to_define = set([])
    global contactgroups_to_define
    contactgroups_to_define = set([])
    global checknames_to_define
    checknames_to_define = set([])

    if host_notification_periods != []:
        raise MKGeneralException("host_notification_periods is not longer supported. Please use extra_host_conf['notification_period'] instead.")

    if summary_host_notification_periods != []:
        raise MKGeneralException("summary_host_notification_periods is not longer supported. Please use extra_summary_host_conf['notification_period'] instead.")

    if service_notification_periods != []:
        raise MKGeneralException("service_notification_periods is not longer supported. Please use extra_service_conf['notification_period'] instead.")

    if summary_service_notification_periods != []:
        raise MKGeneralException("summary_service_notification_periods is not longer supported. Please use extra_summary_service_conf['notification_period'] instead.")

    if filesystem_levels != []:
        raise MKGeneralException("filesystem_levels is not longer supported.\n"
                "Please use check_parameters instead.\n"
                "Please refer to documentation:\n"
                " --> http://mathias-kettner.de/checkmk_check_parameters.html\n")

    output_conf_header(outfile)
    if hostnames == None:
        hostnames = all_hosts_untagged + all_active_clusters()

    for hostname in hostnames:
        create_nagios_config_host(outfile, hostname)

    create_nagios_config_hostgroups(outfile)
    create_nagios_config_servicegroups(outfile)
    create_nagios_config_contactgroups(outfile)
    create_nagios_config_commands(outfile)

    if extra_nagios_conf:
        outfile.write("\n# extra_nagios_conf\n\n")
        outfile.write(extra_nagios_conf)



def create_nagios_config_host(outfile, hostname):
    outfile.write("\n# ----------------------------------------------------\n")
    outfile.write("# %s\n" % hostname)
    outfile.write("# ----------------------------------------------------\n")
    if generate_hostconf:
        create_nagios_hostdefs(outfile, hostname)
    create_nagios_servicedefs(outfile, hostname)

def create_nagios_hostdefs(outfile, hostname):
    is_clust = is_cluster(hostname)

    # Determine IP address. For cluster hosts this is optional.
    # A cluster might have or not have a service ip address.
    try:
        ip = lookup_ipaddress(hostname)
    except:
        if not is_cluster(hostname):
            raise MKGeneralException("Cannot determine ip address of %s. Please add to ipaddresses." % hostname)
        ip = None

    #   _
    #  / |
    #  | |
    #  | |
    #  |_|    1. normal, physical hosts

    alias = hostname
    outfile.write("\ndefine host {\n")
    outfile.write("  host_name\t\t\t%s\n" % hostname)
    outfile.write("  use\t\t\t\t%s\n" % (is_clust and cluster_template or host_template))
    outfile.write("  address\t\t\t%s\n" % (ip and ip or "0.0.0.0"))
    outfile.write("  _TAGS\t\t\t\t%s\n" % " ".join(tags_of_host(hostname)))

    # WATO folder path
    path = host_paths.get(hostname)
    if path:
        outfile.write("  _FILENAME\t\t\t%s\n" % path)

    # Host groups: If the host has no hostgroups it gets the default
    # hostgroup (Nagios requires each host to be member of at least on
    # group.
    hgs = hostgroups_of(hostname)
    hostgroups = ",".join(hgs)
    if len(hgs) == 0:
        hostgroups = default_host_group
        hostgroups_to_define.add(default_host_group)
    elif define_hostgroups:
        hostgroups_to_define.update(hgs)
    outfile.write("  hostgroups\t\t\t+%s\n" % hostgroups)

    # Contact groups
    cgrs = host_contactgroups_of([hostname])
    if len(cgrs) > 0:
        outfile.write("  contact_groups\t\t+%s\n" % ",".join(cgrs))
        contactgroups_to_define.update(cgrs)

    # Parents for non-clusters
    if not is_clust:
        parents_list = parents_of(hostname)
        if len(parents_list) > 0:
            outfile.write("  parents\t\t\t%s\n" % (",".join(parents_list)))

    # Special handling of clusters
    if is_clust:
        nodes = nodes_of(hostname)
        for node in nodes:
            if node not in all_hosts_untagged:
                raise MKGeneralException("Node %s of cluster %s not in all_hosts." % (node, hostname))
        node_ips = [ lookup_ipaddress(h) for h in nodes ]
        alias = "cluster of %s" % ", ".join(nodes)
        outfile.write("  _NODEIPS\t\t\t%s\n" % " ".join(node_ips))
        outfile.write("  parents\t\t\t%s\n" % ",".join(nodes))

        # Host check uses (service-) IP address if available
        if ip:
            outfile.write("  check_command\t\t\tcheck-mk-ping\n")

    # Output alias, but only if it's not define in extra_host_conf
    aliases = host_extra_conf(hostname, extra_host_conf.get("alias", []))
    if len(aliases) == 0:
        outfile.write("  alias\t\t\t\t%s\n" % alias)
    else:
        alias = aliases[0].encode("utf-8")

    # Custom configuration last -> user may override all other values
    outfile.write(extra_host_conf_of(hostname).encode("utf-8"))

    outfile.write("}\n")

    #   ____
    #  |___ \
    #   __) |
    #  / __/
    #  |_____|  2. summary hosts

    if host_is_aggregated(hostname):
        outfile.write("\ndefine host {\n")
        outfile.write("  host_name\t\t\t%s\n" % summary_hostname(hostname))
        outfile.write("  use\t\t\t\t%s-summary\n" % (is_clust and cluster_template or host_template))
        outfile.write("  alias\t\t\t\tSummary of %s\n" % alias)
        outfile.write("  address\t\t\t%s\n" % (ip and ip or "0.0.0.0"))
        outfile.write("  _TAGS\t\t\t\t%s\n" % " ".join(tags_of_host(hostname)))
        outfile.write("  __REALNAME\t\t\t%s\n" % hostname)
        outfile.write("  parents\t\t\t%s\n" % hostname)

        if path:
            outfile.write("  _FILENAME\t\t\t%s\n" % path)

        hgs = summary_hostgroups_of(hostname)
        hostgroups = ",".join(hgs)
        if len(hgs) == 0:
            hostgroups = default_host_group
            hostgroups_to_define.add(default_host_group)
        elif define_hostgroups:
            hostgroups_to_define.update(hgs)
        outfile.write("  hostgroups\t\t\t+%s\n" % hostgroups)

        # host gets same contactgroups as real host
        if len(cgrs) > 0:
            outfile.write("  contact_groups\t\t+%s\n" % ",".join(cgrs))

        if is_clust:
            outfile.write("  _NODEIPS\t\t\t%s\n" % " ".join(node_ips))
        outfile.write("}\n")
    outfile.write("\n")

def create_nagios_servicedefs(outfile, hostname):
    #   _____
    #  |___ /
    #    |_ \
    #   ___) |
    #  |____/   3. Services

    host_checks = get_check_table(hostname).items()
    host_checks.sort() # Create deterministic order
    aggregated_services_conf = set([])
    do_aggregation = host_is_aggregated(hostname)
    have_at_least_one_service = False
    used_descriptions = {}
    for ((checkname, item), (params, description, deps)) in host_checks:
        # Make sure, the service description is unique on this host
        if description in used_descriptions:
            cn, it = used_descriptions[description]
            raise MKGeneralException(
                    "ERROR: Duplicate service description '%s' for host '%s'!\n"
                    " - 1st occurrance: checktype = %s, item = %r\n"
                    " - 2nd occurrance: checktype = %s, item = %r\n" % 
                    (description, hostname, cn, it, checkname, item))

        else:
            used_descriptions[description] = ( checkname, item )
        if have_perfdata(checkname):
            template = passive_service_template_perf
        else:
            template = passive_service_template

        # Hardcoded for logwatch check: Link to logwatch.php
        if checkname == "logwatch":
            logwatch = "  notes_url\t\t\t" + (logwatch_notes_url % (urllib.quote(hostname), urllib.quote(item))) + "\n"
        else:
            logwatch = "";

        # Services Dependencies
        for dep in deps:
            outfile.write("define servicedependency {\n"
                         "    use\t\t\t\t%s\n"
                         "    host_name\t\t\t%s\n"
                         "    service_description\t%s\n"
                         "    dependent_host_name\t%s\n"
                         "    dependent_service_description %s\n"
                         "}\n\n" % (service_dependency_template, hostname, dep, hostname, description))


        # Handle aggregated services. If this service belongs to an aggregation,
        # remember, that the aggregated service must be configured. We cannot
        # do this here, because each aggregated service must occur only once
        # in the configuration.
        if do_aggregation:
            asn = aggregated_service_name(hostname, description)
            if asn != "":
                aggregated_services_conf.add(asn)

        outfile.write("""define service {
  use\t\t\t\t%s
  host_name\t\t\t%s
  service_description\t\t%s
%s%s  check_command\t\t\tcheck_mk-%s
}

""" % ( template, hostname, description, logwatch,
        extra_service_conf_of(hostname, description), checkname ))

        checknames_to_define.add(checkname)
        have_at_least_one_service = True


    # Now create definitions of the aggregated services for this host
    if do_aggregation and service_aggregations:
        outfile.write("\n# Aggregated services\n\n")

    aggr_descripts = aggregated_services_conf
    if aggregate_check_mk and host_is_aggregated(hostname) and have_at_least_one_service:
        aggr_descripts.add("Check_MK")

    # If a ping-only-host is aggregated, the summary host gets it's own
    # copy of the ping - as active check. We cannot aggregate the result
    # from the ping of the real host since no Check_MK is running during
    # the check.
    elif host_is_aggregated(hostname) and not have_at_least_one_service:
        outfile.write("""
define service {
  use\t\t\t\t%s
%s  host_name\t\t\t%s
}

""" % (pingonly_template, extra_service_conf_of(hostname, "PING"), summary_hostname(hostname)))

    for description in aggr_descripts:
        sergr = service_extra_conf(hostname, description, summary_service_groups)
        if len(sergr) > 0:
            sg = "  service_groups\t\t\t+" + ",".join(sergr) + "\n"
            if define_servicegroups:
                servicegroups_to_define.update(sergr)
        else:
            sg = ""

        sercgr = service_extra_conf(hostname, description, summary_service_contactgroups)
        contactgroups_to_define.update(sercgr)
        if len(sercgr) > 0:
            scg = "  contact_groups\t\t\t+" + ",".join(sercgr) + "\n"
        else:
            scg = ""

        outfile.write("""define service {
  use\t\t\t\t%s
  host_name\t\t\t%s
%s%s%s  service_description\t\t%s
}

""" % ( summary_service_template, summary_hostname(hostname), sg, scg,
extra_summary_service_conf_of(hostname, description), description  ))

    # Active check for check_mk
    if have_at_least_one_service:
        outfile.write("""
# Active checks

define service {
  use\t\t\t\t%s
  host_name\t\t\t%s
%s  service_description\t\tCheck_MK
}
""" % (active_service_template, hostname, extra_service_conf_of(hostname, "Check_MK")))
        # Inventory checks - if user has configured them. Not for clusters.
        if inventory_check_interval and not is_cluster(hostname):
            outfile.write("""
define service {
  use\t\t\t\t%s
  host_name\t\t\t%s
  normal_check_interval\t\t%d
%s  service_description\t\tCheck_MK inventory
}

define servicedependency {
  use\t\t\t\t%s
  host_name\t\t\t%s
  service_description\t\tCheck_MK
  dependent_host_name\t\t%s
  dependent_service_description\tCheck_MK inventory
}
""" % (inventory_check_template, hostname, inventory_check_interval,
       extra_service_conf_of(hostname, "Check_MK inventory"),
       service_dependency_template, hostname, hostname))

    legchecks = host_extra_conf(hostname, legacy_checks)
    if len(legchecks) > 0:
        outfile.write("\n\n# Legacy checks\n")
    for command, description, has_perfdata in legchecks:
        if description in used_descriptions:
            cn, it = used_descriptions[description]
            raise MKGeneralException(
                    "ERROR: Duplicate service description (legacy check) '%s' for host '%s'!\n"
                    " - 1st occurrance: checktype = %s, item = %r\n"
                    " - 2nd occurrance: checktype = legacy(%s), item = None\n" % 
                    (description, hostname, cn, it, command))

        else:
            used_descriptions[description] = ( "legacy(" + command + ")", description )
        
        extraconf = extra_service_conf_of(hostname, description)
        if has_perfdata:
            template = "check_mk_perf,"
        else:
            template = ""
        outfile.write("""
define service {
  use\t\t\t\t%scheck_mk_default
  host_name\t\t\t%s
  service_description\t\t%s
  check_command\t\t\t%s
  active_checks_enabled\t\t1
%s}
""" % (template, hostname, description, command, extraconf))

    # No check_mk service, no legacy service -> create PING service
    if not have_at_least_one_service and len(legchecks) == 0:
        outfile.write("""
define service {
  use\t\t\t\t%s
%s  host_name\t\t\t%s
}

""" % (pingonly_template, extra_service_conf_of(hostname, "PING"), hostname))


def create_nagios_config_hostgroups(outfile):
    if define_hostgroups:
        outfile.write("\n# ------------------------------------------------------------\n")
        outfile.write("# Host groups (controlled by define_hostgroups)\n")
        outfile.write("# ------------------------------------------------------------\n")
        hgs = list(hostgroups_to_define)
        hgs.sort()
        for hg in hgs:
            try:
                alias = define_hostgroups[hg]
            except:
                alias = hg
            outfile.write("""
define hostgroup {
  hostgroup_name\t\t%s
  alias\t\t\t\t%s
}
""" % (hg, alias))

    # No creation of host groups but we need to define
    # default host group
    elif default_host_group in hostgroups_to_define:
	outfile.write("""
define hostgroup {
  hostgroup_name\t\t%s
  alias\t\t\t\tCheck_MK default hostgroup
}
""" % default_host_group)
	

def create_nagios_config_servicegroups(outfile):
    if define_servicegroups:
        outfile.write("\n# ------------------------------------------------------------\n")
        outfile.write("# Service groups (controlled by define_servicegroups)\n")
        outfile.write("# ------------------------------------------------------------\n")
        sgs = list(servicegroups_to_define)
        sgs.sort()
        for sg in sgs:
            try:
                alias = define_servicegroups[sg]
            except:
                alias = sg
            outfile.write("""
define servicegroup {
  servicegroup_name\t\t%s
  alias\t\t\t\t%s
}
""" % (sg, alias))

def create_nagios_config_contactgroups(outfile):
    if define_contactgroups:
        cgs = list(contactgroups_to_define)
        cgs.sort()
        outfile.write("\n# ------------------------------------------------------------\n")
        outfile.write("# Contact groups (controlled by define_contactgroups)\n")
        outfile.write("# ------------------------------------------------------------\n\n")
        for name in cgs:
            if type(define_contactgroups) == dict:
                alias = define_contactgroups.get(name, name)
            else:
                alias = name
            outfile.write("\ndefine contactgroup {\n"
                    "  contactgroup_name\t\t%s\n"
                    "  alias\t\t\t\t%s\n"
                    "}\n" % (name, alias))


def create_nagios_config_commands(outfile):
    if generate_dummy_commands:
        outfile.write("\n# ------------------------------------------------------------\n")
        outfile.write("# Dummy check commands (controlled by generate_dummy_commands)\n")
        outfile.write("# ------------------------------------------------------------\n\n")
        for checkname in checknames_to_define:
            outfile.write("""define command {
  command_name\t\t\tcheck_mk-%s
  command_line\t\t\t%s
}

""" % ( checkname, dummy_check_commandline ))

#   +----------------------------------------------------------------------+
#   |            ___                      _                                |
#   |           |_ _|_ ____   _____ _ __ | |_ ___  _ __ _   _              |
#   |            | || '_ \ \ / / _ \ '_ \| __/ _ \| '__| | | |             |
#   |            | || | | \ V /  __/ | | | || (_) | |  | |_| |             |
#   |           |___|_| |_|\_/ \___|_| |_|\__\___/|_|   \__, |             |
#   |                                                   |___/              |
#   +----------------------------------------------------------------------+


def inventorable_checktypes(what): # snmp, tcp, all
    checknames = [ k for k in check_info.keys()
                   if check_info[k][3] != no_inventory_possible
#                   and (k not in ignored_checktypes)
                   and (what == "all" or ((k.split('.')[0] in snmp_info) == (what == "snmp")))
                 ]
    checknames.sort()
    return checknames

def checktype_ignored_for_host(host, checktype):
    if checktype in ignored_checktypes: 
        return True
    ignored = host_extra_conf(host, ignored_checks)
    for e in ignored:
        if checktype == e or (type(e) == list and checktype in e):
            return True
    return False

def do_snmp_scan(hostnamelist, check_only=False, include_state=False):
    if hostnamelist == []:
        hostnamelist = all_hosts_untagged

    result = []
    for hostname in hostnamelist:
        if not is_snmp_host(hostname):
            if opt_verbose:
                sys.stdout.write("Skipping %s, not an snmp host\n" % hostname)
            continue
        try:
            ipaddress = lookup_ipaddress(hostname)
        except:
            sys.stdout.write("Cannot resolve %s into IP address. Skipping.\n" % hostname)
            continue
        checknames = snmp_scan(hostname, ipaddress)
        for checkname in checknames:
            if opt_debug:
                sys.stdout.write("Trying inventory for %s on %s\n" % (checkname, hostname))
            result += make_inventory(checkname, [hostname], check_only, include_state)
    return result



def make_inventory(checkname, hostnamelist, check_only=False, include_state=False):
    try:
        inventory_function = check_info[checkname][3]
    except KeyError:
        sys.stderr.write("No such check type '%s'. Try check_mk -L.\n" % checkname)
        sys.exit(1)

    is_snmp_check = check_uses_snmp(checkname)

    newchecks = []
    newitems = []   # used by inventory check to display unchecked items
    count_new = 0
    checked_hosts = []

    # if no hostnamelist is specified, we use all hosts
    if not hostnamelist or len(hostnamelist) == 0:
        global opt_use_cachefile
        opt_use_cachefile = True
        hostnamelist = all_hosts_untagged

    try:
        for host in hostnamelist:

            # Skip SNMP checks on non-SNMP hosts
            if is_snmp_check and not is_snmp_host(host): 
                continue

            # Skip TCP checks on non-TCP hosts
            if not is_snmp_check and not is_tcp_host(host):
                continue

            # Skip checktypes which are generally ignored for this host
            if checktype_ignored_for_host(host, checkname):
                continue

            if is_cluster(host):
                sys.stderr.write("%s is a cluster host and cannot be inventorized.\n" % host)
                continue

            # host is either hostname or "hostname/ipaddress"
            s = host.split("/")
            hostname = s[0]
            if len(s) == 2:
                ipaddress = s[1]
            else:
                # try to resolve name into ip address
                if not opt_no_tcp:
                    try:
                        ipaddress = lookup_ipaddress(hostname)
                    except:
                        sys.stderr.write("Cannot resolve %s into IP address.\n" % hostname)
                        continue
                else:
                    ipaddress = None # not needed, not TCP used

            # Make hostname available as global variable in inventory functions
            # (used e.g. by ps-inventory)
            global g_hostname
            g_hostname = hostname

            # On --no-tcp option skip hosts without cache file
            if opt_no_tcp:
                if opt_no_cache:
                    sys.stderr.write("You allowed me neither TCP nor cache. Bailing out.\n")
                    sys.exit(4)

                cachefile = tcp_cache_dir + "/" + hostname
                if not os.path.exists(cachefile):
                    if opt_verbose:
                        sys.stderr.write("No cachefile %s. Skipping this host.\n" % cachefile)
                    continue

            checked_hosts.append(hostname)

            checkname_base = checkname.split('.')[0]    # make e.g. 'lsi' from 'lsi.arrays'
            try:
                info = get_realhost_info(hostname, ipaddress, checkname_base, inventory_max_cachefile_age)
            except MKAgentError, e:
                # This special handling is needed for the inventory check. It needs special
                # handling for WATO.
                if check_only and not include_state and str(e):
                    raise
		elif not include_state and str(e):
		    sys.stderr.write("Host '%s': %s\n" % (hostname, str(e)))
                elif include_state and str(e): # WATO automation. Abort
                    raise
                continue
            except MKSNMPError, e:
                # This special handling is needed for the inventory check. It needs special
                # handling for WATO.
                if check_only and not include_state and str(e):
                    raise
		elif not include_state and str(e):
                    sys.stderr.write("Host '%s': %s\n" % (hostname, str(e)))
                continue
            except Exception, e:
                if check_only or opt_debug:
                    raise
                sys.stderr.write("Cannot get information from host '%s': %s\n" % (hostname, e))
                continue

            if info == None: # No data for this check type
                continue
            try:
                # New preferred style since 1.1.11i3: only one argument: info
                try:
                    inventory = inventory_function(info)
                except Exception, e:
                    # failed? Lets try pre-1.1.11i3 style inventory function
                    try:
                        inventory = inventory_function(checkname, info) # inventory is a list of pairs (item, current_value)
                    except Exception, ee:
                        raise e # raise original exception

                if inventory == None: # tolerate if function does no explicit return
                    inventory = []
            except Exception, e:
                if opt_debug:
                    sys.stderr.write("Exception in inventory function of check type %s\n" % checkname)
                    raise
                if opt_verbose:
		    sys.stderr.write("%s: Invalid output from agent or invalid configuration: %s\n" % (hostname, e))
                continue

            if not isinstance(inventory, list):
                sys.stderr.write("%s: Check %s returned invalid inventory data: %s\n" %
                                                    (hostname, checkname, repr(inventory)))
                continue

            for entry in inventory:
                state_type = "new" # assume new, change later if wrong
                if len(entry) == 2: # comment is now obsolete
                    item, paramstring = entry
                else:
                    item, comment, paramstring = entry

                description = service_description(checkname, item)
                # make sanity check
                if len(description) == 0:
                    sys.stderr.write("%s: Check %s returned empty service description - ignoring it.\n" %
                                                    (hostname, checkname))
                    continue


                # Find logical host this check belongs to. The service might belong to a cluster.
                hn = host_of_clustered_service(hostname, description)

                # Now compare with already known checks for this host (from
                # previous inventory or explicit checks). Also drop services
                # the user wants to ignore via 'ignored_services'.
                checktable = get_check_table(hn)
                checked_items = [ i for ( (cn, i), (par, descr, deps) ) \
                                  in checktable.items() if cn == checkname ]
                if item in checked_items:
                    if include_state:
                        state_type = "old"
                    else:
                        continue # we have that already

                if service_ignored(hn, description):
                    if include_state:
                        if state_type == "old":
                            state_type = "obsolete"
                        else:
                            state_type = "ignored"
                    else:
                        continue # user does not want this item to be checked

                newcheck = '  ("%s", "%s", %r, %s),' % (hn, checkname, item, paramstring)
                newcheck += "\n"
                if newcheck not in newchecks: # avoid duplicates if inventory outputs item twice
                    newchecks.append(newcheck)
                    if include_state:
                        newitems.append( (hn, checkname, item, paramstring, state_type) )
                    else:
                        newitems.append( (hn, checkname, item) )
                    count_new += 1


    except KeyboardInterrupt:
        sys.stderr.write('<Interrupted>\n')


    if not check_only:
        if newchecks != []:
            filename = autochecksdir + "/" + checkname + "-" + time.strftime("%Y-%m-%d_%H.%M.%S")
            while os.path.exists(filename + ".mk"): # in case of more than one file per second and checktype...
                filename += ".x"
            filename += ".mk"
            if not os.path.exists(autochecksdir):
                os.makedirs(autochecksdir)
            file(filename, "w").write('# %s\n[\n%s]\n' % (filename, ''.join(newchecks)))
            sys.stdout.write('%-30s ' % (tty_cyan + tty_bold + checkname + tty_normal))
            sys.stdout.write('%s%d new checks%s\n' % (tty_bold + tty_green, count_new, tty_normal))

    return newitems


def check_inventory(hostname):
    newchecks = []
    newitems = []
    total_count = 0
    is_snmp = is_snmp_host(hostname)
    is_tcp  = is_tcp_host(hostname)
    check_table = get_check_table(hostname)
    hosts_checktypes = set([ ct for (ct, item), params in check_table.items() ])
    try:
        for ct in inventorable_checktypes("all"):
            if check_uses_snmp(ct) and not is_snmp:
                continue # Skip SNMP checks on non-SNMP hosts
            elif check_uses_snmp(ct) and ct not in hosts_checktypes:
 		continue # Do not look for new SNMP services (maybe change in future)
            elif not check_uses_snmp(ct) and not is_tcp:
                continue # Skip TCP checks on non-TCP hosts

            new = make_inventory(ct, [hostname], True)
            newitems += new
            count = len(new)
            if count > 0:
                newchecks.append((ct, count))
                total_count += count
        if total_count > 0:
            info = ", ".join([ "%s:%d" % (ct, count) for ct,count in newchecks ])
            statustext = { 0 : "OK", 1: "WARNING", 2:"CRITICAL" }.get(inventory_check_severity, "UNKNOWN")
            sys.stdout.write("%s - %d unchecked services (%s)\n" % (statustext, total_count, info))
            # Put detailed list into long pluging output
            for hostname, checkname, item in newitems:
                sys.stdout.write("%s: %s\n" % (checkname, service_description(checkname, item)))
            sys.exit(inventory_check_severity)
        else:
            sys.stdout.write("OK - no unchecked services found\n")
            sys.exit(0)
    except SystemExit, e:
        raise e
    except Exception, e:
        if opt_debug:
            raise
        sys.stdout.write("UNKNOWN - %s\n" % (e,))
        sys.exit(3)


def service_ignored(hostname, service_description):
    return in_boolean_serviceconf_list(hostname, service_description, ignored_services)

def in_boolean_serviceconf_list(hostname, service_description, conflist):
    for entry in conflist:
        if entry[0] == NEGATE: # this entry is logically negated
            negate = True
            entry = entry[1:]
        else:
            negate = False

        if len(entry) == 2:
            hostlist, servlist = entry
            tags = []
        elif len(entry) == 3:
            tags, hostlist, servlist = entry
        else:
            raise MKGeneralException("Invalid entry '%r' in configuration: must have 2 or 3 elements" % (entry,))

        if hosttags_match_taglist(tags_of_host(hostname), tags) and \
           in_extraconf_hostlist(hostlist, hostname) and \
           in_extraconf_servicelist(servlist, service_description):
            if opt_verbose:
                print "Ignoring service '%s' on host %s." % (service_description, hostname)
            return not negate
    return False # no match. Do not ignore


# Remove all autochecks of certain types of a certain host
def remove_autochecks_of(hostname, checktypes = None): # None = all
    removed = 0
    for fn in glob.glob(autochecksdir + "/*.mk"):
        if opt_debug:
            sys.stdout.write("Scanning %s...\n" % fn)
        lines = []
        count = 0
        for line in file(fn):
            # hostname and check type can be quoted with ' or with "
            double_quoted = line.replace("'", '"').lstrip()
            if double_quoted.startswith('("'):
                count += 1
                splitted = double_quoted.split('"')
                if splitted[1] != hostname or (checktypes != None and splitted[3] not in checktypes):
                    if splitted[3] not in check_info:
                        sys.stderr.write('Removing unimplemented check %s\n' % splitted[3])
                        continue
                    lines.append(line)
                else:
                    removed += 1
        if len(lines) == 0:
            if opt_verbose:
                sys.stdout.write("Deleting %s.\n" % fn)
            os.remove(fn)
        elif count > len(lines):
            if opt_verbose:
                sys.stdout.write("Removing %d checks from %s.\n" % (count - len(lines), fn))
            f = file(fn, "w+")
            f.write("[\n")
            for line in lines:
                f.write(line)
            f.write("]\n")

    return removed

def remove_all_autochecks():
    for f in glob.glob(autochecksdir + '/*.mk'):
        if opt_verbose:
            sys.stdout.write("Deleting %s.\n" % f)
        os.remove(f)

def reread_autochecks():
    global checks
    checks = checks[len(autochecks):]
    read_all_autochecks()
    checks = autochecks + checks

#   +----------------------------------------------------------------------+
#   |          ____                                     _ _                |
#   |         |  _ \ _ __ ___  ___ ___  _ __ ___  _ __ (_) | ___           |
#   |         | |_) | '__/ _ \/ __/ _ \| '_ ` _ \| '_ \| | |/ _ \          |
#   |         |  __/| | |  __/ (_| (_) | | | | | | |_) | | |  __/          |
#   |         |_|   |_|  \___|\___\___/|_| |_| |_| .__/|_|_|\___|          |
#   |                                            |_|                       |
#   +----------------------------------------------------------------------+

def find_check_plugin(checktype):
    if local_checks_dir and os.path.exists(local_checks_dir + "/" + checktype):
        return local_checks_dir + "/" + checktype
    filename = checks_dir + "/" + checktype
    if os.path.exists(filename):
        return filename

def get_precompiled_check_table(hostname):
    host_checks = get_sorted_check_table(hostname)
    precomp_table = []
    for checktype, item, params, description, deps in host_checks:
        aggr_name = aggregated_service_name(hostname, description)
        # some checks need precompilation of parameters
        precomp_func = precompile_params.get(checktype)
        if precomp_func:
            params = precomp_func(hostname, item, params)
        precomp_table.append((checktype, item, params, description, aggr_name)) # deps not needed while checking
    return precomp_table

def precompile_hostchecks():
    if not os.path.exists(precompiled_hostchecks_dir):
        os.makedirs(precompiled_hostchecks_dir)
    for host in all_active_hosts() + all_active_clusters():
        try:
            precompile_hostcheck(host)
        except Exception, e:
            if opt_debug:
                raise
            sys.stderr.write("Error precompiling checks for host %s: %s\n" % (host, e))
            sys.exit(5)

# read python file and strip comments
g_stripped_file_cache = {}
def stripped_python_file(filename):
    if filename in g_stripped_file_cache:
        return g_stripped_file_cache[filename]
    a = ""
    for line in file(filename):
        l = line.strip()
        if l == "" or l[0] != '#':
            a += line # not stripped line because of indentation!
    g_stripped_file_cache[filename] = a
    return a

def precompile_hostcheck(hostname):
    if opt_verbose:
        sys.stderr.write("%s%s%-16s%s:" % (tty_bold, tty_blue, hostname, tty_normal))

    try:
        os.remove(compiled_filename)
        os.remove(source_filename)
    except:
        pass

    compiled_filename = precompiled_hostchecks_dir + "/" + hostname
    source_filename = compiled_filename + ".py"

    output = file(source_filename + ".new", "w")
    output.write("#!/usr/bin/python\n")
    output.write("# encoding: utf-8\n")

    # Self-compile: replace symlink with precompiled python-code, if
    # we are run for the first time
    if delay_precompile:
        output.write("""
import os
if os.path.islink(%(dst)r):
    import py_compile
    os.remove(%(dst)r)
    py_compile.compile(%(src)r, %(dst)r, %(dst)r, True)
    os.chmod(%(dst)r, 0755)

""" % { "src" : source_filename, "dst" : compiled_filename })

    output.write(stripped_python_file(modules_dir + "/check_mk_base.py"))

    # initialize global variables
    output.write("""
# very simple commandline parsing: only -v is supported
opt_verbose = '-v' in sys.argv
opt_debug   = False

# make sure these names are defined (even if never needed)
no_inventory_possible = None
""")

    # Compile in all neccessary global variables
    output.write("\n# Global variables\n")
    for var in [ 'check_mk_version', 'tcp_connect_timeout', 'agent_min_version',
                 'perfdata_format', 'aggregation_output_format',
                 'aggr_summary_hostname', 'nagios_command_pipe_path',
                 'check_result_path', 'check_submission',
                 'var_dir', 'counters_directory', 'tcp_cache_dir',
                 'snmpwalks_dir', 'check_mk_basedir', 'nagios_user',
                 'www_group', 'cluster_max_cachefile_age', 'check_max_cachefile_age',
                 'simulation_mode', 'agent_simulator', 'aggregate_check_mk', 'debug_log',
                 ]:
        output.write("%s = %r\n" % (var, globals()[var]))

    # check table, enriched with addition precompiled information.
    check_table = get_precompiled_check_table(hostname)
    output.write("\n# Checks for %s\n\n" % hostname)
    output.write("def get_sorted_check_table(hostname):\n    return %r\n\n" % check_table)

    # Do we need to load the SNMP module? This is the case, if the host
    # has at least one SNMP based check. Also collect the needed check
    # types.
    need_snmp_module = False
    needed_types = set([])
    for checktype, item, param, descr, aggr in check_table:
        if checktype not in check_info:
            sys.stderr.write('Warning: Ignoring missing check %s.\n' % checktype)
            continue
        needed_types.add(checktype.split(".")[0])
        if check_uses_snmp(checktype):
            need_snmp_module = True

    if need_snmp_module:
        output.write(stripped_python_file(modules_dir + "/snmp.py"))

    if agent_simulator:
        output.write(stripped_python_file(modules_dir + "/agent_simulator.py"))

    # check info table
    # We need to include all those plugins that are referenced in the host's
    # check table
    filenames = []
    for checktype in needed_types:
        # Add library files needed by check (also look in local)
        for lib in check_includes.get(checktype, []):
            if local_checks_dir and os.path.exists(local_checks_dir + "/" + lib):
                to_add = local_checks_dir + "/" + lib
            else:
                to_add = checks_dir + "/" + lib
            if to_add not in filenames:
                filenames.append(to_add)

        # Now add check file itself
        path = find_check_plugin(checktype)
        if not path:
            raise MKGeneralException("Cannot find plugin for check type %s (missing file %s/%s)\n" % \
                                     (checktype, checks_dir, checktype))

        if path not in filenames:
            filenames.append(path)


    output.write("check_info = {}\n" +
                 "check_includes = {}\n" +
                 "precompile_params = {}\n" +
                 "factory_settings = {}\n" + 
                 "check_config_variables = []\n" +
                 "check_default_levels = {}\n" +
                 "snmp_info = {}\n" +
                 "snmp_scan_functions = {}\n")

    for filename in filenames:
        output.write("# %s\n" % filename)
        output.write(stripped_python_file(filename))
        output.write("\n\n")
        if opt_verbose:
            sys.stderr.write(" %s%s%s" % (tty_green, filename.split('/')[-1], tty_normal))

    # handling of clusters
    if is_cluster(hostname):
        output.write("clusters = { %r : %r }\n" %
                     (hostname, nodes_of(hostname)))
        output.write("def is_cluster(hostname):\n    return True\n\n")
    else:
        output.write("clusters = {}\ndef is_cluster(hostname):\n    return False\n\n")

    # snmp hosts
    output.write("def is_snmp_host(hostname):\n   return %r\n\n" % is_snmp_host(hostname))
    output.write("def is_tcp_host(hostname):\n   return %r\n\n" % is_tcp_host(hostname))
    output.write("def snmp_walk_command(hostname):\n   return %r\n\n" % snmp_walk_command(hostname))
    output.write("def is_usewalk_host(hostname):\n   return %r\n\n" % is_usewalk_host(hostname))

    # IP addresses
    needed_ipaddresses = {}
    nodes = []
    if is_cluster(hostname):
        for node in nodes_of(hostname):
            ipa = lookup_ipaddress(node)
            needed_ipaddresses[node] = ipa
            nodes.append( (node, ipa) )
        try:
            ipaddress = lookup_ipaddress(hostname) # might throw exception
            needed_ipaddresses[hostname] = ipaddress
        except:
            ipaddress = None
    else:
        ipaddress = lookup_ipaddress(hostname) # might throw exception
        needed_ipaddresses[hostname] = ipaddress
        nodes = [ (hostname, ipaddress) ]

    output.write("ipaddresses = %r\n\n" % needed_ipaddresses)
    output.write("def lookup_ipaddress(hostname):\n   return ipaddresses.get(hostname)\n\n");

    # datasource programs. Is this host relevant?
    # ACHTUNG: HIER GIBT ES BEI CLUSTERN EIN PROBLEM!! WIR MUESSEN DIE NODES
    # NEHMEN!!!!!

    dsprogs = {}
    for node, ipa in nodes:
        program = get_datasource_program(node, ipa)
        dsprogs[node] = program
    output.write("def get_datasource_program(hostname, ipaddress):\n" +
                 "    return %r[hostname]\n\n" % dsprogs)

    # aggregation
    output.write("def host_is_aggregated(hostname):\n    return %r\n\n" % host_is_aggregated(hostname))

    # TCP and SNMP port of agent
    output.write("def agent_port_of(hostname):\n    return %d\n\n" % agent_port_of(hostname))
    output.write("def snmp_port_spec(hostname):\n    return %r\n\n" % snmp_port_spec(hostname))

    # SNMP character encoding
    output.write("def get_snmp_character_encoding(hostname):\n    return %r\n\n" 
      % get_snmp_character_encoding(hostname))

    # Parameters for checks: Default values are defined in checks/*. The
    # variables might be overridden by the user in main.mk. We need
    # to set the actual values of those variables here. Otherwise the users'
    # settings would get lost. But we only need to set those variables that
    # influence the check itself - not those needed during inventory.
    for var in check_config_variables:
        output.write("%s = %r\n" % (var, eval(var)))

    # perform actual check
    output.write("do_check(%r, %r)\n" % (hostname, ipaddress))
    output.close()

    # compile python (either now or delayed), but only if the source
    # code has not changed. The Python compilation is the most costly
    # operation here.
    if os.path.exists(source_filename):
        if file(source_filename).read() == file(source_filename + ".new").read():
            if opt_verbose:
                sys.stderr.write(" (%s is unchanged)\n" % source_filename)
            os.remove(source_filename + ".new")
            return
        elif opt_verbose:
            sys.stderr.write(" (new content)")
    
    os.rename(source_filename + ".new", source_filename)
    if not delay_precompile:
        py_compile.compile(source_filename, compiled_filename, compiled_filename, True)
        os.chmod(compiled_filename, 0755)
    else:
        if os.path.exists(compiled_filename) or os.path.islink(compiled_filename):
            os.remove(compiled_filename)
        os.symlink(hostname + ".py", compiled_filename)

    if opt_verbose:
        sys.stderr.write(" ==> %s.\n" % compiled_filename)


#   +----------------------------------------------------------------------+
#   |                  __  __                         _                    |
#   |                 |  \/  | __ _ _ __  _   _  __ _| |                   |
#   |                 | |\/| |/ _` | '_ \| | | |/ _` | |                   |
#   |                 | |  | | (_| | | | | |_| | (_| | |                   |
#   |                 |_|  |_|\__,_|_| |_|\__,_|\__,_|_|                   |
#   |                                                                      |
#   +----------------------------------------------------------------------+

opt_nowiki = False

def get_tty_size():
    import termios,struct,fcntl
    try:
        ws = struct.pack("HHHH", 0, 0, 0, 0)
        ws = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, ws)
        lines, columns, x, y = struct.unpack("HHHH", ws)
        if lines > 0 and columns > 0:
            return lines, columns
    except:
        pass
    return (24, 80)


def all_manuals():
    entries = dict([(fn, check_manpages_dir + "/" + fn) for fn in os.listdir(check_manpages_dir)])
    if local_check_manpages_dir and os.path.exists(local_check_manpages_dir):
        entries.update(dict([(fn, local_check_manpages_dir + "/" + fn) 
                for fn in os.listdir(local_check_manpages_dir)]))
    return entries

def list_all_manuals():
    table = []
    for filename, path in all_manuals().items():
        if filename.endswith("~"):
            continue
        
        try:
            for line in file(path):
                if line.startswith("title:"):
                    table.append((filename, line.split(":", 1)[1].strip()))
        except:
            pass

    table.sort()
    print_table(['Check type', 'Title'], [tty_bold, tty_normal], table)

def show_check_manual(checkname):
    bg_color = 4
    fg_color = 7
    bold_color = tty_white + tty_bold
    normal_color = tty_normal + tty(fg_color, bg_color)
    title_color_left = tty(0,7,1)
    title_color_right = tty(0,7)
    subheader_color = tty(fg_color, bg_color, 1)
    header_color_left = tty(0,2)
    header_color_right = tty(7,2,1)
    parameters_color = tty(6,4,1)
    examples_color = tty(6,4,1)

    filename = all_manuals().get(checkname)
    if not filename:
        sys.stdout.write("No manpage for %s. Sorry.\n" % checkname)
        return

    sections = {}
    current_section = []
    current_variable = None
    sections['header'] = current_section
    lineno = 0
    empty_line_count = 0

    try:
        for line in file(filename):
            lineno += 1
            if line.startswith(' ') and line.strip() != "": # continuation line
                empty_line_count = 0
                if current_variable:
                    name, curval = current_section[-1]
                    if curval.strip() == "":
                        current_section[-1] = (name, line.rstrip()[1:])
                    else:
                        current_section[-1] = (name, curval + "\n" + line.rstrip()[1:])
                else:
                    raise Exception
                continue

            line = line.strip()
            if line == "":
                empty_line_count += 1
                if empty_line_count == 1 and current_variable:
                    name, curval = current_section[-1]
                    current_section[-1] = (name, curval + "\n<br>\n")
                continue
            empty_line_count = 0

            if line[0] == '[' and line[-1] == ']':
                section_header = line[1:-1]
                current_section = []
                sections[section_header] = current_section
            else:
                current_variable, restofline = line.split(':', 1)
                current_section.append((current_variable, restofline.lstrip()))
    except Exception, e:
        sys.stderr.write("Syntax error in %s line %d (%s).\n" % (filename, lineno, e))
        sys.exit(1)

    # Output
    height, width = get_tty_size()
    if os.path.exists("/usr/bin/less") and not opt_nowiki:
        output = os.popen("/usr/bin/less -S -R -Q -u -L", "w")
    else:
        output = sys.stdout

    if opt_nowiki:
        print "TI:Check manual page of %s" % checkname
        print "DT:%s" % (time.strftime("%Y-%m-%d"))
        print "SA:checks"

        def markup(line, ignored=None):
            # preserve the inner { and } in double braces and then replace the braces left
            return line.replace('{{', '{&#123;').replace('}}', '&#125;}').replace("{", "<b>").replace("}", "</b>")

        def print_sectionheader(line, ignored):
            print "H1:" + line

        def print_subheader(line):
            print "H2:" + line

        def print_line(line, attr=None, no_markup = False):
            if no_markup:
                print line
            else:
                print markup(line)

        def print_splitline(attr1, left, attr2, right):
            print "<b style=\"width: 300px;\">%s</b> %s\n" % (left, right)

        def empty_line():
            print

        def print_textbody(text):
            print markup(text)

        def print_splitwrap(attr1, left, attr2, text):
            if '(' in left:
                name, typ = left.split('(', 1)
                name = name.strip()
                typ = typ.strip()[:-2]
            else:
                name = left
                typ = ""
            print "<tr><td class=tt>%s</td><td>%s</td><td>%s</td></tr>" % (name, typ, markup(text))

    else:
        def markup(line, attr):
            # Replaces braces in the line but preserves the inner braces
            return re.sub('(?<!{){', bold_color, re.sub('(?<!})}', tty_normal + attr, line))

        def print_sectionheader(left, right):
            print_splitline(title_color_left, "%-19s" % left, title_color_right, right)

        def print_subheader(line):
            empty_line()
            output.write(subheader_color + " " + tty_underline +
                         line.upper() +
                         normal_color +
                         (" " * (width - 1 - len(line))) +
                         tty_normal + "\n")

        def print_line(line, attr=normal_color, no_markup = False):
            if no_markup:
                text = line
                l = len(line)
            else:
                text = markup(line, attr)
                l = print_len(line)
            output.write(attr + " ")
            output.write(text)
            output.write(" " * (width - 2 - l))
            output.write(" " + tty_normal + "\n")

        def print_splitline(attr1, left, attr2, right):
            output.write(attr1 + " " + left)
            output.write(attr2)
            output.write(markup(right, attr2))
            output.write(" " * (width - 1 - len(left) - print_len(right)))
            output.write(tty_normal + "\n")

        def empty_line():
            print_line("", tty(7,4))

        def print_len(word):
            # In case of double braces remove only one brace for counting the length
            netto = word.replace('{{', 'x').replace('}}', 'x').replace("{", "").replace("}", "")
            netto = re.sub("\033[^m]+m", "", netto)
            return len(netto)

        # only used for debugging
        def remove_ansi(line):
            newline = ""
            ci = 0
            while ci < len(line):
                c = line[ci]
                if c == '\033':
                    while line[ci] != 'm' and ci < len(line):
                        ci += 1
                else:
                    newline += c
                ci += 1

            return newline

        def justify(line, width):
            need_spaces = float(width - print_len(line))
            spaces = float(line.count(' '))
            newline = ""
            x = 0.0
            s = 0.0
            words = line.split()
            newline = words[0]
            for word in words[1:]:
                newline += ' '
                x += 1.0
                while s/x < need_spaces / spaces:
                    newline += ' '
                    s += 1
                newline += word
            return newline

        def fillup(line, width):
            printlen = print_len(line)
            if printlen < width:
                line += " " * (width - printlen)
            return line

        def wrap_text(text, width, attr=tty(7,4)):
            wrapped = []
            line = ""
            col = 0
            for word in text.split():
                if word == '<br>':
                    if line != "":
                        wrapped.append(fillup(line, width))
                        wrapped.append(fillup("", width))
                        line = ""
                        col = 0
                else:
                    netto = print_len(word)
                    if line != "" and netto + col + 1 > width:
                        wrapped.append(justify(line, width))
                        col = 0
                        line = ""
                    if line != "":
                        line += ' '
                        col += 1
                    line += markup(word, attr)
                    col += netto
            if line != "":
                wrapped.append(fillup(line, width))

            # remove trailing empty lines
            while wrapped[-1].strip() == "":
                wrapped = wrapped[:-1]
            return wrapped

        def print_textbody(text, attr=tty(7,4)):
            wrapped = wrap_text(text, width - 2)
            for line in wrapped:
                print_line(line, attr)

        def print_splitwrap(attr1, left, attr2, text):
            wrapped = wrap_text(left + attr2 + text, width - 2)
            output.write(attr1 + " " + wrapped[0] + " " + tty_normal + "\n")
            for line in wrapped[1:]:
                output.write(attr2 + " " + line + " " + tty_normal + "\n")

    try:
        header = {}
        for key, value in sections['header']:
            header[key] = value.strip()

        print_sectionheader(checkname, header['title'])
        if opt_nowiki:
            sys.stderr.write("<tr><td class=tt>%s</td><td>[check_%s|%s]</td></tr>\n" % (checkname, checkname, header['title']))
        print_splitline(header_color_left, "Author:            ", header_color_right, header['author'])
        print_splitline(header_color_left, "License:           ", header_color_right, header['license'])
        distro = header['distribution']
        if distro == 'check_mk':
            distro = "official part of Check_MK"
        print_splitline(header_color_left, "Distribution:      ", header_color_right, distro)
        ags = []
        for agent in header['agents'].split(","):
            agent = agent.strip()
            ags.append({ "vms" : "VMS", "linux":"Linux", "aix": "AIX", 
                         "solaris":"Solaris", "windows":"Windows", "snmp":"SNMP"}
                         .get(agent, agent.upper()))
        print_splitline(header_color_left, "Supported Agents:  ", header_color_right, ", ".join(ags))

        empty_line()
        print_textbody(header['description'])
        if 'item' in header:
            print_subheader("Item")
            print_textbody(header['item'])

        print_subheader("Check parameters")
        if sections.has_key('parameters'):
            if opt_nowiki:
                print "<table><th>Parameter</th><th>Type</th><th>Description</th></tr>"
            first = True
            for name, text in sections['parameters']:
                if not first:
                    empty_line()
                first = False
                print_splitwrap(parameters_color, name + ": ", normal_color, text)
            if opt_nowiki:
                print "</table>"
        else:
            print_line("None.")

        print_subheader("Performance data")
        if header.has_key('perfdata'):
            print_textbody(header['perfdata'])
        else:
            print_textbody("None.")

        print_subheader("Inventory")
        if header.has_key('inventory'):
            print_textbody(header['inventory'])
        else:
            print_textbody("No inventory supported.")

        print_subheader("Configuration variables")
        if sections.has_key('configuration'):
            if opt_nowiki:
                print "<table><th>Variable</th><th>Type</th><th>Description</th></tr>"
            first = True
            for name, text in sections['configuration']:
                if not first:
                    empty_line()
                first = False
                print_splitwrap(tty(2,4,1), name + ": ", tty_normal + tty(7,4), text)
            if opt_nowiki:
                print "</table>"
        else:
            print_line("None.")

        if header.has_key("examples"):
            print_subheader("Examples")
            lines = header['examples'].split('\n')
            if opt_nowiki:
                print "F+:main.mk"
            for line in lines:
                if line.lstrip().startswith('#'):
                    print_line(line)
                elif line != "<br>":
                    print_line(line, examples_color, True) # nomarkup
            if opt_nowiki:
                print "F-:"

        empty_line()
        output.flush()
        output.close()
    except Exception, e:
        print "Invalid check manpage %s: missing %s" % (filename, e)

#   +----------------------------------------------------------------------+
#   |                  ____             _                                  |
#   |                 | __ )  __ _  ___| | ___   _ _ __                    |
#   |                 |  _ \ / _` |/ __| |/ / | | | '_ \                   |
#   |                 | |_) | (_| | (__|   <| |_| | |_) |                  |
#   |                 |____/ \__,_|\___|_|\_\\__,_| .__/                   |
#   |                                             |_|                      |
#   +----------------------------------------------------------------------+

class fake_file:
    def __init__(self, content):
        self.content = content
        self.pointer = 0

    def size(self):
        return len(self.content)

    def read(self, size):
        new_end = self.pointer + size
        data = self.content[self.pointer:new_end]
        self.pointer = new_end
        return data

def do_backup(tarname):
    import tarfile
    if opt_verbose:
        sys.stderr.write("Creating backup file '%s'...\n" % tarname)
    tar = tarfile.open(tarname, "w:gz")


    for name, path, canonical_name, descr, is_dir, owned_by_nagios, group_www in backup_paths:
        absdir = os.path.abspath(path)
        if os.path.exists(path):
            if opt_verbose:
                sys.stderr.write("  Adding %s (%s) " %  (descr, absdir))
            if is_dir:
                basedir = absdir
                filename = "."
                subtarname = name + ".tar"
                subdata = os.popen("tar cf - --dereference --force-local -C '%s' '%s'" % \
                                   (basedir, filename)).read()
            else:
                basedir = os.path.dirname(absdir)
                filename = os.path.basename(absdir)
                subtarname = canonical_name
                subdata = file(absdir).read()

            info = tarfile.TarInfo(subtarname)
            info.mtime = time.time()
            info.uid = 0
            info.gid = 0
            info.size = len(subdata)
            info.mode = 0644
            info.type = tarfile.REGTYPE
            info.name = subtarname
            if opt_verbose:
                sys.stderr.write("(%d bytes)...\n" % info.size)
            tar.addfile(info, fake_file(subdata))

    tar.close()
    if opt_verbose:
        sys.stderr.write("Successfully created backup.\n")


def do_restore(tarname):
    import tarfile, shutil

    if opt_verbose:
        sys.stderr.write("Restoring from '%s'...\n" % tarname)

    for name, path, canonical_name, descr, is_dir, owned_by_nagios, group_www in backup_paths:
        absdir = os.path.abspath(path)
        if is_dir:
            basedir = absdir
            filename = "."
            if os.path.exists(absdir):
                if opt_verbose:
                    sys.stderr.write("  Deleting old contents of '%s'\n" % absdir)
                # The path might point to a symbalic link. So it is no option
                # to call shutil.rmtree(). We must delete just the contents
                for f in os.listdir(absdir):
                    if f not in [ '.', '..' ]:
                        try:
                            p = absdir + "/" + f
                            if os.path.isdir(p):
                                shutil.rmtree(p)
                            else:
                                os.remove(p)
                        except Exception, e:
                            sys.stderr.write("  Warning: cannot delete %s: %s\n" % (p, e))
        else:
            basedir = os.path.dirname(absdir)
            filename = os.path.basename(absdir)
            canonical_path = basedir + "/" + canonical_name
            if os.path.exists(canonical_path):
                if opt_verbose:
                    sys.stderr.write("  Deleting old version of '%s'\n" % canonical_path)
                os.remove(canonical_path)

        if not os.path.exists(basedir):
            if opt_verbose:
                sys.stderr.write("  Creating directory %s\n" %  basedir)
            os.makedirs(basedir)

        if opt_verbose:
            sys.stderr.write("  Extracting %s (%s)\n" % (descr, absdir))
        if is_dir:
            os.system("tar xzf '%s' --force-local --to-stdout '%s' 2>/dev/null "
                      "| tar xf - -C '%s' '%s' 2>/dev/null" % \
                      (tarname, name + ".tar", basedir, filename))
        else:
            os.system("tar xzf '%s' --force-local --to-stdout '%s' 2>/dev/null > '%s' 2>/dev/null" %
                      (tarname, filename, canonical_path))

        if i_am_root():
            if owned_by_nagios:
                to_user = str(nagios_user)
            else:
                to_user = "root"
            if group_www and www_group != None:
                to_group = ":" + str(www_group)
                if opt_verbose:
                    sys.stderr.write("  Adding group write permissions\n")
                    os.system("chmod -R g+w '%s'" % absdir)
            else:
                to_group = ":root"
            if opt_verbose:
                sys.stderr.write("  Changing ownership to %s%s\n" % (to_user, to_group))
            os.system("chown -R '%s%s' '%s' 2>/dev/null" % (to_user, to_group, absdir))

    if opt_verbose:
        sys.stderr.write("Successfully restored backup.\n")


def do_flush(hosts):
    if len(hosts) == 0:
        hosts = all_active_hosts() + all_active_clusters()
    for host in hosts:
        sys.stdout.write("%-20s: " % host)
        sys.stdout.flush()
        flushed = False

        # counters
        try:
            os.remove(counters_directory + "/" + host)
            sys.stdout.write(tty_bold + tty_blue + " counters")
            sys.stdout.flush()
            flushed = True
        except:
            pass

        # cache files
        d = 0
        dir = tcp_cache_dir
        if os.path.exists(tcp_cache_dir):
            for f in os.listdir(dir):
                if f == host or f.startswith(host + "."):
                    try:
                        os.remove(dir + "/" + f)
                        d += 1
                        flushed = True
                    except:
                        pass
            if d == 1:
                sys.stdout.write(tty_bold + tty_green + " cache")
            elif d > 1:
                sys.stdout.write(tty_bold + tty_green + " cache(%d)" % d)
            sys.stdout.flush()

        # logfiles
        dir = logwatch_dir + "/" + host
        if os.path.exists(dir):
            d = 0
            for f in os.listdir(dir):
                if f not in [".", ".."]:
                    try:
                        os.remove(dir + "/" + f)
                        d += 1
                        flushed = True
                    except:
                        pass
            if d > 0:
                sys.stdout.write(tty_bold + tty_magenta + " logfiles(%d)" % d)

        # autochecks
        d = remove_autochecks_of(host)
        if d > 0:
            flushed = True
            sys.stdout.write(tty_bold + tty_cyan + " autochecks(%d)" % d)

        if not flushed:
            sys.stdout.write("(nothing)")


        sys.stdout.write(tty_normal + "\n")


#   +----------------------------------------------------------------------+
#   |   __  __       _        __                  _   _                    |
#   |  |  \/  | __ _(_)_ __  / _|_   _ _ __   ___| |_(_) ___  _ __  ___    |
#   |  | |\/| |/ _` | | '_ \| |_| | | | '_ \ / __| __| |/ _ \| '_ \/ __|   |
#   |  | |  | | (_| | | | | |  _| |_| | | | | (__| |_| | (_) | | | \__ \   |
#   |  |_|  |_|\__,_|_|_| |_|_|  \__,_|_| |_|\___|\__|_|\___/|_| |_|___/   |
#   |                                                                      |
#   +----------------------------------------------------------------------+

# Create a list of all hosts of a certain hostgroup. Needed only for
# option --list-hosts
def list_all_hosts(hostgroups):
    hostlist = []
    for hn in all_active_hosts() + all_active_clusters():
        if len(hostgroups) == 0:
            hostlist.append(hn)
        else:
            for hg in hostgroups_of(hn):
                if hg in hostgroups:
                    hostlist.append(hn)
                    break
    hostlist.sort()
    return hostlist

# Same for host tags, needed for --list-tag
def list_all_hosts_with_tags(tags):
    hosts = []
    for h in all_active_hosts() + all_active_clusters():
        if hosttags_match_taglist(tags_of_host(h), tags):
            hosts.append(h)
    return hosts


# Implementation of option -d
def output_plain_hostinfo(hostname):
    try:
        ipaddress = lookup_ipaddress(hostname)
        sys.stdout.write(get_agent_info(hostname, ipaddress, 0))
    except MKAgentError, e:
        sys.stderr.write("Problem contacting agent: %s\n" % (e,))
        sys.exit(3)
    except MKGeneralException, e:
        sys.stderr.write("General problem: %s\n" % (e,))
        sys.exit(3)
    except socket.gaierror, e:
        sys.stderr.write("Network error: %s\n" % e)
    except Exception, e:
        sys.stderr.write("Unexpected exception: %s\n" % (e,))
        sys.exit(3)

def do_snmpwalk(hostnames):
    if len(hostnames) == 0:
        sys.stderr.write("Please specify host names to walk on.\n")
        return
    if not os.path.exists(snmpwalks_dir):
        os.makedirs(snmpwalks_dir)
    for host in hostnames:
        try:
            do_snmpwalk_on(host, snmpwalks_dir + "/" + host)
        except Exception, e:
            sys.stderr.write("Error walking %s: %s\n" % (host, e))
            if opt_debug:
                raise

def do_snmpwalk_on(hostname, filename):
    if opt_verbose:
        sys.stdout.write("%s:\n" % hostname)
    ip = lookup_ipaddress(hostname)
    portspec = snmp_port_spec(hostname)
    cmd = snmp_walk_command(hostname) + " -On -Ob -OQ -Ot %s%s " % (ip, portspec)
    if opt_debug:
        print 'Executing: %s' % cmd
    out = file(filename, "w")
    for oid in [ "", "1.3.6.1.4.1" ]: # SNMPv2-SMI::enterprises
        oids = []
        values = []
        if opt_verbose:
            sys.stdout.write("%s..." % (cmd + oid))
            sys.stdout.flush()
        count = 0
        f = os.popen(cmd + oid)
        while True:
            line = f.readline()
            if not line:
                break
            parts = line.split("=", 1)
            if len(parts) != 2:
                continue
            oid, value = parts
            value = value.rstrip("\n")
            if value.lstrip().startswith('"'):
                while value[-1] != '"':
                    value += f.readline().rstrip("\n")

            if not oid.startswith("."):
                oid = "." + oid
            oids.append(oid)
            values.append(value)
        for oid, value in zip(oids, values):
            out.write("%s %s\n" % (oid, value.strip()))
            count += 1
        if opt_verbose:
            sys.stdout.write("%d variables.\n" % count)

    out.close()
    if opt_verbose:
        sys.stdout.write("Successfully Wrote %s%s%s.\n" % (tty_bold, filename, tty_normal))

def do_snmpget(oid, hostnames):
    if len(hostnames) == 0:
        for host in all_active_hosts():
            if is_snmp_host(host):
                hostnames.append(host)

    for host in hostnames:
        ip = lookup_ipaddress(host)
        value = get_single_oid(host, ip, oid)
        sys.stdout.write("%s (%s): %r\n" % (host, ip, value))


def show_paths():
    inst = 1
    conf = 2
    data = 3
    pipe = 4
    local = 5
    dir = 1
    fil = 2

    paths = [
        ( modules_dir,                 dir, inst, "Main components of check_mk"),
        ( checks_dir,                  dir, inst, "Checks"),
        ( agents_dir,                  dir, inst, "Agents for operating systems"),
        ( doc_dir,                     dir, inst, "Documentatoin files"),
        ( web_dir,                     dir, inst, "Check_MK's web pages"),
        ( check_manpages_dir,          dir, inst, "Check manpages (for check_mk -M)"),
        ( lib_dir,                     dir, inst, "Binary plugins (architecture specific)"),
        ( pnp_templates_dir,           dir, inst, "Templates for PNP4Nagios"),
        ( pnp_rraconf_dir,             dir, inst, "RRA configuration for PNP4Nagios"),
        ( nagios_startscript,          fil, inst, "Startscript for Nagios daemon"),
        ( nagios_binary,               fil, inst, "Path to Nagios executable"),

        ( default_config_dir,          dir, conf, "Directory that contains main.mk"),
        ( check_mk_configdir,          dir, conf, "Directory containing further *.mk files"),
        ( nagios_config_file,          fil, conf, "Main configuration file of Nagios"),
        ( nagios_conf_dir,             dir, conf, "Directory where Nagios reads all *.cfg files"),
        ( apache_config_dir,           dir, conf, "Directory where Apache reads all config files"),
        ( htpasswd_file,               fil, conf, "Users/Passwords for HTTP basic authentication"),

        ( var_dir,                     dir, data, "Base working directory for variable data"),
        ( autochecksdir,               dir, data, "Checks found by inventory"),
        ( precompiled_hostchecks_dir,  dir, data, "Precompiled host checks"),
        ( snmpwalks_dir,               dir, data, "Stored snmpwalks (output of --snmpwalk)"),
        ( counters_directory,          dir, data, "Current state of performance counters"),
        ( tcp_cache_dir,               dir, data, "Cached output from agents"),
        ( logwatch_dir,                dir, data, "Unacknowledged logfiles of logwatch extension"),
        ( nagios_objects_file,         fil, data, "File into which Nagios configuration is written"),
        ( nagios_status_file,          fil, data, "Path to Nagios status.dat"),

        ( nagios_command_pipe_path,    fil, pipe, "Nagios' command pipe"),
        ( check_result_path,           fil, pipe, "Nagios' check results directory"),
        ( livestatus_unix_socket,      fil, pipe, "Socket of Check_MK's livestatus module"),
        ]

    if omd_root:
        paths += [
         ( local_checks_dir,           dir, local, "Locally installed checks"),
         ( local_check_manpages_dir,   dir, local, "Locally installed check man pages"),
         ( local_agents_dir,           dir, local, "Locally installed agents and plugins"),
         ( local_web_dir,              dir, local, "Locally installed Multisite addons"),
         ( local_pnp_templates_dir,    dir, local, "Locally installed PNP templates"),
         ( local_pnp_rraconf_dir,      dir, local, "Locally installed PNP RRA configuration"),
         ( local_doc_dir,              dir, local, "Locally installed documentation"),
         ( local_locale_dir,           dir, local, "Locally installed localizations"),
        ]

    def show_paths(title, t):
        if t != inst:
            print
        print(tty_bold + title + tty_normal)
        for path, filedir, typp, descr in paths:
            if typp == t:
                if filedir == dir:
                    path += "/"
                print("  %-47s: %s%s%s" % (descr, tty_bold + tty_blue, path, tty_normal))

    for title, t in [
        ( "Files copied or created during installation", inst ),
        ( "Configuration files edited by you", conf ),
        ( "Data created by Nagios/Check_MK at runtime", data ),
        ( "Sockets and pipes", pipe ),
        ( "Locally installed addons", local ),
        ]:
        show_paths(title, t)

def dump_all_hosts(hostlist):
    if hostlist == []:
        hostlist = all_hosts_untagged + all_active_clusters()
    hostlist.sort()
    for hostname in hostlist:
        dump_host(hostname)

def dump_host(hostname):
    print
    if is_cluster(hostname):
        color = tty_bgmagenta
        add_txt = " (cluster of " + (",".join(nodes_of(hostname))) + ")"
        try:
            ipaddress = lookup_ipaddress(hostname)
	except:
	    ipaddress = "0.0.0.0"
    else:
        color = tty_bgblue
        try:
            ipaddress = lookup_ipaddress(hostname)
            add_txt = " (%s)" % ipaddress
        except:
            add_txt = " (no DNS, no entry in ipaddresses)"
            ipaddress = "X.X.X.X"
    print "%s%s%s%-78s %s" % (color, tty_bold, tty_white, hostname + add_txt, tty_normal)

    tags = tags_of_host(hostname)
    print tty_yellow + "Tags:                   " + tty_normal + ", ".join(tags)
    if is_cluster(hostname):
        parents_list = nodes_of(hostname)
    else:
        parents_list = parents_of(hostname)
    if len(parents_list) > 0:
        print tty_yellow + "Parents:                " + tty_normal + ", ".join(parents_list)
    print tty_yellow + "Host groups:            " + tty_normal + ", ".join(hostgroups_of(hostname))
    print tty_yellow + "Contact groups:         " + tty_normal + ", ".join(host_contactgroups_of([hostname]))
    agenttype = "TCP (port: %d)" % agent_port_of(hostname)
    dapg = get_datasource_program(hostname, ipaddress)
    if dapg:
        agenttype = "Datasource program: %s" % dapg

    if is_snmp_host(hostname):
        if is_usewalk_host(hostname):
            agenttype = "SNMP (use stored walk)"
        else:
            credentials = snmp_credentials_of(hostname)
            if is_bulkwalk_host(hostname):
                bulk = "yes"
            else:
                bulk = "no"
            portinfo = snmp_port_of(hostname)
            if portinfo == None:
                portinfo = 'default'
            agenttype = "SNMP (community: '%s', bulk walk: %s, port: %s)" % (credentials, bulk, portinfo)
    print tty_yellow + "Type of agent:          " + tty_normal + agenttype
    is_aggregated = host_is_aggregated(hostname)
    if is_aggregated:
        print tty_yellow + "Is aggregated:          " + tty_normal + "yes"
        shn = summary_hostname(hostname)
        print tty_yellow + "Summary host:           " + tty_normal + shn
        print tty_yellow + "Summary host groups:    " + tty_normal + ", ".join(summary_hostgroups_of(hostname))
        print tty_yellow + "Summary contact groups: " + tty_normal + ", ".join(host_contactgroups_of([shn]))
        notperiod = (host_extra_conf(hostname, summary_host_notification_periods) + [""])[0]
        print tty_yellow + "Summary notification:   " + tty_normal + notperiod
    else:
        print tty_yellow + "Is aggregated:          " + tty_normal + "no"


    format_string = " %-15s %s%-10s %s%-17s %s%-14s%s %s%-16s%s"
    print tty_yellow + "Services:" + tty_normal
    check_items = get_sorted_check_table(hostname)

    headers = ["checktype", "item",    "params", "description", "groups", "summarized to", "groups"]
    colors =  [ tty_normal,  tty_blue, tty_normal, tty_green,     tty_normal, tty_red, tty_white ]
    if service_dependencies != []:
        headers.append("depends on")
        colors.append(tty_magenta)

    def if_aggr(a):
        if is_aggregated:
            return a
        else:
            return ""

    print_table(headers, colors, [ [
        checktype,
        item,
        params,
        description,
        ",".join(service_extra_conf(hostname, description, service_groups)),
        if_aggr(aggregated_service_name(hostname, description)),
        if_aggr(",".join(service_extra_conf(hostname, aggregated_service_name(hostname, description), summary_service_groups))),
        ",".join(deps)
        ]
                  for checktype, item, params, description, deps in check_items ], "  ")

def print_table(headers, colors, rows, indent = ""):
    lengths = [ len(h) for h in headers ]
    for row in rows:
        lengths = [ max(len(str(c)), l) for c, l in zip(row, lengths) ]
    sumlen = sum(lengths) + len(headers)
    format = indent
    sep = ""
    for l,c in zip(lengths, colors):
        format += c + sep + "%-" + str(l) + "s" + tty_normal
        sep = " "

    first = True
    for row in [ headers ] + rows:
        print format % tuple(row[:len(headers)])
        if first:
            first = False
            print format % tuple([ "-" * l for l in lengths ])

def print_version():
    print """This is check_mk version %s
Copyright (C) 2009 Mathias Kettner

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; see the file COPYING.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
""" % check_mk_version

def usage():
    print """WAYS TO CALL:
 check_mk [-n] [-v] [-p] HOST [IPADDRESS]  check all services on HOST
 check_mk [-u] -I [HOST ..]                inventory - find new services
 check_mk [-u] -II ...                     renew inventory, drop old services
 check_mk -u, --cleanup-autochecks         reorder autochecks files
 check_mk -N [HOSTS...]                    output Nagios configuration
 check_mk -C, --compile                    precompile host checks
 check_mk -U, --update                     precompile + create Nagios config
 check_mk -O, --reload                     precompile + config + Nagios reload
 check_mk -R, --restart                    precompile + config + Nagios restart
 check_mk -D, --dump [H1 H2 ..]            dump all or some hosts
 check_mk -d HOSTNAME|IPADDRESS            show raw information from agent
 check_mk --check-inventory HOSTNAME       check for items not yet checked
 check_mk --list-hosts [G1 G2 ...]         print list of hosts
 check_mk --list-tag TAG1 TAG2 ...         list hosts having certain tags
 check_mk -L, --list-checks                list all available check types
 check_mk -M, --man [CHECKTYPE]            show manpage for check CHECKTYPE
 check_mk --paths                          list all pathnames and directories
 check_mk -X, --check-config               check configuration for invalid vars
 check_mk --backup BACKUPFILE.tar.gz       make backup of configuration and data
 check_mk --restore BACKUPFILE.tar.gz      restore configuration and data
 check_mk --flush [HOST1 HOST2...]         flush all data of some or all hosts
 check_mk --donate                         Email data of configured hosts to MK
 check_mk --snmpwalk HOST1 HOST2 ...       Do snmpwalk on host
 check_mk --snmpget OID HOST1 HOST2 ...    Fetch single OIDs and output them
 check_mk --scan-parents [HOST1 HOST2...]  autoscan parents, create conf.d/parents.mk
 check_mk -P, --package COMMAND            do package operations
 check_mk --localize COMMAND               do localization operations
 check_mk -V, --version                    print version
 check_mk -h, --help                       print this help

OPTIONS:
  -v             show what's going on
  -p             also show performance data (use with -v)
  -n             do not submit results to Nagios, do not save counters
  -c FILE        read config file FILE instead of %s
  --cache        read info from cache file is present and fresh, use TCP
                 only, if cache file is absent or too old
  --no-cache     never use cached information
  --no-tcp       for -I: only use cache files. Skip hosts without
                 cache files.
  --fake-dns IP  fake IP addresses of all hosts to be IP. This
                 prevents DNS lookups.
  --usewalk      use snmpwalk stored with --snmpwalk
  --debug        never catch Python exceptions
  --procs N      start up to N processes in parallel during --scan-parents
  --checks A,..  restrict inventory to specified checks (tcp/snmp/check type)

NOTES:
  -I can be restricted to certain check types. Write '--checks df -I' if you
  just want to look for new filesystems. Use 'check_mk -L' for a list
  of all check types. Use 'tcp' for all TCP based checks and 'snmp' for
  all SNMP based checks.

  -II does the same as -I but deletes all existing checks of the
  specified types and hosts.

  -u, --cleanup-autochecks resorts all checks found by inventory
  into per-host files. It can be used as an options to -I or as
  a standalone operation.

  -N outputs the Nagios configuration. You may optionally add a list
  of hosts. In that case the configuration is generated only for
  that hosts (useful for debugging).

  -U redirects both the output of -S and -H to the file %s
  and also calls check_mk -C.

  -D, --dump dumps out the complete configuration and information
  about one, several or all hosts. It shows all services, hostgroups,
  contacts and other information about that host.

  -d does not work on clusters (such defined in main.mk) but only on
  real hosts.

  --check-inventory make check_mk behave as Nagios plugins that
  checks if an inventory would find new services for the host.

  --list-hosts called without argument lists all hosts. You may
  specify one or more host groups to restrict the output to hosts
  that are in at least one of those groups.

  --list-tag prints all hosts that have all of the specified tags
  at once.

  -M, --man shows documentation about a check type. If
  /usr/bin/less is available it is used as pager. Exit by pressing
  Q. Use -M without an argument to show a list of all manual pages.

  --backup saves all configuration and runtime data to a gzip
  compressed tar file. --restore *erases* the current configuration
  and data and replaces it with that from the backup file.

  --flush deletes all runtime data belonging to a host (not
  inventory data). This includes the state of performance counters,
  cached agent output,  and logfiles. Precompiled host checks
  are not deleted.

  -P, --package brings you into packager mode. Packages are
  used to ship inofficial extensions of Check_MK. Call without
  arguments for a help on packaging.

  --localize brings you into localization mode. You can create
  and/or improve the localization of Check_MKs Multisite.  Call without
  arguments for a help on localization.

  --donate is for those who decided to help the Check_MK project
  by donating live host data. It tars the cached agent data of
  those host which are configured in main.mk:donation_hosts and sends
  them via email to donatehosts@mathias-kettner.de. The host data
  is then publicly available for others and can be used for setting
  up demo sites, implementing checks and so on.
  Do this only with test data from test hosts - not with productive
  data! By donating real-live host data you help others trying out
  Check_MK and developing checks by donating hosts. This is completely
  voluntary and turned off by default.

  --snmpwalk does a complete snmpwalk for the specifies hosts both
  on the standard MIB and the enterprises MIB and stores the
  result in the directory %s.

  --scan-parents uses traceroute in order to automatically detect
  hosts's parents. It creates the file conf.d/parents.mk which
  defines gateway hosts and parent declarations.

  Nagios can call check_mk without options and the hostname and its IP
  address as arguments. Much faster is using precompiled host checks,
  though.


""" % (check_mk_configfile,
       precompiled_hostchecks_dir,
       snmpwalks_dir,
       )


def do_create_config():
    out = file(nagios_objects_file, "w")
    sys.stdout.write("Generating Nagios configuration...")
    sys.stdout.flush()
    create_nagios_config(out)
    sys.stdout.write(tty_ok + "\n")

def do_output_nagios_conf(args):
    if len(args) == 0:
        args = None
    create_nagios_config(sys.stdout, args)

def do_precompile_hostchecks():
    sys.stdout.write("Precompiling host checks...")
    sys.stdout.flush()
    precompile_hostchecks()
    sys.stdout.write(tty_ok + "\n")


def do_update():
    try:
        do_create_config()
        do_precompile_hostchecks()
        sys.stdout.write(("Successfully created Nagios configuration file %s%s%s.\n\n" +
                         "Please make sure that file will be read by Nagios.\n" +
                         "You need to restart Nagios in order to activate " +
                         "the changes.\n") % (tty_green + tty_bold, nagios_objects_file, tty_normal))

    except Exception, e:
        sys.stderr.write("Configuration Error: %s\n" % e)
        if opt_debug:
            raise
        sys.exit(1)


def do_check_nagiosconfig():
    command = nagios_binary + " -v "  + nagios_config_file + " 2>&1"
    sys.stdout.write("Validating Nagios configuration...")
    if opt_verbose:
        sys.stderr.write("Running '%s'" % command)
    sys.stderr.flush()

    process = os.popen(command, "r")
    output = process.read()
    exit_status = process.close()
    if not exit_status:
        sys.stdout.write(tty_ok + "\n")
        return True
    else:
        sys.stdout.write("ERROR:\n")
        sys.stderr.write(output)
        return False


def do_restart_nagios(only_reload):
    action = only_reload and "load" or "start"
    sys.stdout.write("Re%sing Nagios..." % action)
    sys.stdout.flush()
    command = nagios_startscript + " re%s 2>&1" % action
    process = os.popen(command, "r")
    output = process.read()
    if process.close():
        sys.stdout.write("ERROR: %s\n" % output)
        raise MKGeneralException("Cannot re%s Nagios: %s" % (action, output))
    else:
        sys.stdout.write(tty_ok + "\n")

def do_reload():
    do_restart(True)

def do_restart(only_reload = False):
    try:
        # Save current configuration
        if os.path.exists(nagios_objects_file):
            backup_path = nagios_objects_file + ".save"
            if opt_verbose:
                sys.stderr.write("Renaming %s to %s\n" % (nagios_objects_file, backup_path))
            os.rename(nagios_objects_file, backup_path)
        else:
            backup_path = None

        try:
            do_create_config()
        except Exception, e:
            sys.stderr.write("Error creating configuration: %s\n" % e)
            os.rename(backup_path, nagios_objects_file)
            if opt_debug:
                raise
            sys.exit(1)

        if do_check_nagiosconfig():
            if backup_path:
                os.remove(backup_path)
            do_precompile_hostchecks()
            do_restart_nagios(only_reload)
        else:
            sys.stderr.write("Nagios configuration is invalid. Rolling back.\n")
            if backup_path:
                os.rename(backup_path, nagios_objects_file)
            else:
                os.remove(nagios_objects_file)
            sys.exit(1)

    except Exception, e:
        try:
            if backup_path and os.path.exists(backup_path):
                os.remove(backup_path)
        except:
            pass
        if opt_debug:
            raise
        sys.stderr.write("An error occurred: %s\n" % e)
        sys.exit(1)

def do_donation():
    donate = []
    cache_files = os.listdir(tcp_cache_dir)
    for host in all_hosts_untagged:
        if in_binary_hostlist(host, donation_hosts):
            for f in cache_files:
                if f == host or f.startswith("%s." % host):
                    donate.append(f)
    if opt_verbose:
        print "Donating files %s" % " ".join(cache_files)
    import base64
    indata = base64.b64encode(os.popen("tar czf - -C %s %s" % (tcp_cache_dir, " ".join(donate))).read())
    output = os.popen(donation_command, "w")
    output.write("\n\n@STARTDATA\n")
    while len(indata) > 0:
        line = indata[:64]
        output.write(line)
        output.write('\n')
        indata = indata[64:]

def do_cleanup_autochecks():
    # 1. Read in existing autochecks
    hostdata = {}
    os.chdir(autochecksdir)
    checks = 0
    for fn in glob.glob("*.mk"):
        if opt_debug:
            sys.stdout.write("Scanning %s...\n" % fn)
        for line in file(fn):
            testline = line.lstrip().replace("'", '"')
            if testline.startswith('("'):
                splitted = testline.split('"')
                hostname = splitted[1]
                hostchecks = hostdata.get(hostname, [])
                hostchecks.append(line)
                checks += 1
                hostdata[hostname] = hostchecks
    if opt_verbose:
        sys.stdout.write("Found %d checks from %d hosts.\n" % (checks, len(hostdata)))

    # 2. Write out new autochecks.
    newfiles = set([])
    for host, lines in hostdata.items():
        lines.sort()
        fn = host.replace(":","_") + ".mk"
        if opt_verbose:
            sys.stdout.write("Writing %s: %d checks\n" % (fn, len(lines)))
        newfiles.add(fn)
        f = file(fn, "w+")
        f.write("[\n")
        for line in lines:
            f.write(line)
        f.write("]\n")

    # 3. Remove obsolete files
    for f in glob.glob("*.mk"):
        if f not in newfiles:
            if opt_verbose:
                sys.stdout.write("Deleting %s\n" % f)
            os.remove(f)

def check_bin_in_path(prog):
    for path in os.environ['PATH'].split(os.pathsep):
        f = path + '/' + prog
        if os.path.exists(f) and os.access(f, os.X_OK):
            return True

def do_scan_parents(hosts):
    global max_num_processes
    if len(hosts) == 0:
        hosts = filter(lambda h: in_binary_hostlist(h, scanparent_hosts), all_hosts_untagged)

    found = []
    parent_hosts = []
    parent_ips   = {}
    parent_rules = []
    gateway_hosts = set([])

    if max_num_processes < 1:
        max_num_processes = 1

    outfilename = check_mk_configdir + "/parents.mk"

    if not check_bin_in_path('traceroute'):
        raise MKGeneralException('The traceroute command can not be found in PATH?')

    if os.path.exists(outfilename):
        first_line = file(outfilename, "r").readline()
        if not first_line.startswith('# Automatically created by --scan-parents at'):
            raise MKGeneralException("conf.d/parents.mk seems to be created manually.\n\n"
                                     "The --scan-parents function would overwrite this file.\n"
                                     "Please rename it to keep the configuration or delete "
                                     "the file and try again.")

    sys.stdout.write("Scanning for parents (%d processes)..." % max_num_processes)
    sys.stdout.flush()
    while len(hosts) > 0:
        chunk = []
        while len(chunk) < max_num_processes and len(hosts) > 0:
            host = hosts[0]
            del hosts[0]
            # skip hosts that already have a parent
            if len(parents_of(host)) > 0:
                if opt_verbose:
                    sys.stdout.write("(manual parent) ")
                    sys.stdout.flush()
                continue
            chunk.append(host)

        gws = scan_parents_of(chunk)

        for host, gw in zip(chunk, gws):
            if gw:
                gateway, gateway_ip = gw
                if not gateway: # create artificial host
                    gateway = "gw-%s" % (gateway_ip.replace(".", "-"))
                    if gateway not in gateway_hosts:
                        gateway_hosts.add(gateway)
                        parent_hosts.append("%s|parent|ping" % gateway)
                        parent_ips[gateway] = gateway_ip
                        parent_rules.append( (monitoring_host, [gateway]) ) # make Nagios a parent of gw
                parent_rules.append( (gateway, [host]) )
            elif host != monitoring_host:
                # make monitoring host the parent of all hosts without real parent
                parent_rules.append( (monitoring_host, [host]) )

    import pprint
    out = file(outfilename, "w")
    out.write("# Automatically created by --scan-parents at %s\n\n" % time.asctime())
    out.write("# Do not edit this file. If you want to convert an\n")
    out.write("# artificial gateway host into a permanent one, then\n")
    out.write("# move its definition into another *.mk file\n")

    out.write("# Parents which are not listed in your all_hosts:\n")
    out.write("all_hosts += %s\n\n" % pprint.pformat(parent_hosts))

    out.write("# IP addresses of parents not listed in all_hosts:\n")
    out.write("ipaddresses.update(%s)\n\n" % pprint.pformat(parent_ips))

    out.write("# Parent definitions\n")
    out.write("parents += %s\n\n" % pprint.pformat(parent_rules))
    sys.stdout.write("\nWrote %s\n" % outfilename)

def scan_parents_of(hosts):
    nagios_ip = lookup_ipaddress(monitoring_host)
    os.putenv("LANG", "")
    os.putenv("LC_ALL", "")

    # Start processes in parallel
    procs = []
    for host in hosts:
        if opt_verbose:
            sys.stdout.write("%s " % host)
            sys.stdout.flush()
        try:
            ip = lookup_ipaddress(host)
        except:
            sys.stderr.write("%s: cannot resolve host name\n" % host)
            ip = None
        command = "traceroute -m 15 -n -w 3 %s" % ip
        if opt_debug:
            sys.stderr.write("Running '%s'\n" % command)
        procs.append( (host, ip, os.popen(command) ) )

    # Now all run and we begin to read the answers
    def dot(color, dot='o'):
        sys.stdout.write(tty_bold + color + dot + tty_normal)
        sys.stdout.flush()

    gateways = []
    for host, ip, proc in procs:
        lines = [l.strip() for l in proc.readlines()]
        exitstatus = proc.close()
        if exitstatus:
            dot(tty_red, '*')
            gateways.append(None)
            continue

        if len(lines) == 0:
            if opt_debug:
                raise MKGeneralException("Cannot execute %s. Is traceroute installed? Are you root?" % command)
            else:
                dot(tty_red, '!')
        elif len(lines) < 2:
            sys.stderr.write("%s: %s\n" % (host, ' '.join(lines)))
            gateways.append(None)
            dot(tty_blue)
            continue

        # Parse output of traceroute:
        # traceroute to 8.8.8.8 (8.8.8.8), 30 hops max, 40 byte packets
        #  1  * * *
        #  2  10.0.0.254  0.417 ms  0.459 ms  0.670 ms
        #  3  172.16.0.254  0.967 ms  1.031 ms  1.544 ms
        #  4  217.0.116.201  23.118 ms  25.153 ms  26.959 ms
        #  5  217.0.76.134  32.103 ms  32.491 ms  32.337 ms
        #  6  217.239.41.106  32.856 ms  35.279 ms  36.170 ms
        #  7  74.125.50.149  45.068 ms  44.991 ms *
        #  8  * 66.249.94.86  41.052 ms 66.249.94.88  40.795 ms
        #  9  209.85.248.59  43.739 ms  41.106 ms 216.239.46.240  43.208 ms
        # 10  216.239.48.53  45.608 ms  47.121 ms 64.233.174.29  43.126 ms
        # 11  209.85.255.245  49.265 ms  40.470 ms  39.870 ms
        # 12  8.8.8.8  28.339 ms  28.566 ms  28.791 ms
        routes = []
        for line in lines[1:]:
            parts = line.split()
            route = parts[1]
            if route.count('.') == 3:
                routes.append(route)
            elif route == '*':
                routes.append(None) # No answer from this router
            else:
                sys.stderr.write("%s: invalid output line from traceroute: '%s'\n" % (host, line))

        if len(routes) == 0:
            sys.stderr.write("%s: incomplete output from traceroute. No routes found.\n" % host)
            gateways.append(None)
            dot(tty_red)
            continue

        # Only one entry -> host is directly reachable and gets nagios as parent -
        # if nagios is not the parent itself. Problem here: How can we determine
        # if the host in question is the monitoring host? The user must configure
        # this in monitoring_host.
        elif len(routes) == 1:
            if ip == nagios_ip:
                gateways.append(None) # We are the monitoring host
                dot(tty_white, 'N')
            else:
                gateways.append( (monitoring_host, nagios_ip) )
                dot(tty_cyan, 'L')
            continue

        # Try far most route which is not identical with host itself
        route = None
        for r in routes[::-1]:
            if not r or (r == ip):
                continue
            route = r
            break
        if not route:
            sys.stderr.write("%s: No usable routing information\n" % host)
            gateways.append(None)
            dot(tty_blue)
            continue

        # TTLs already have been filtered out)
        gateway_ip = route
        gateway = ip_to_hostname(route)
        if opt_verbose:
            sys.stdout.write("%s(%s) " % (gateway, gateway_ip))
        gateways.append( (gateway, gateway_ip) )
        dot(tty_green, 'G')
    return gateways

# find hostname belonging to an ip address. We must not use
# reverse DNS but the Check_MK mechanisms
def ip_to_hostname(ip):
    global ip_to_hostname_cache
    if ip_to_hostname_cache == None:
        ip_to_hostname_cache = {}
        for host in all_hosts_untagged:
            try:
                ip_to_hostname_cache[lookup_ipaddress(host)] = host
            except:
                pass
    return ip_to_hostname_cache.get(ip)


#   +----------------------------------------------------------------------+
#   |         ____                _                    __ _                |
#   |        |  _ \ ___  __ _  __| |   ___ ___  _ __  / _(_) __ _          |
#   |        | |_) / _ \/ _` |/ _` |  / __/ _ \| '_ \| |_| |/ _` |         |
#   |        |  _ <  __/ (_| | (_| | | (_| (_) | | | |  _| | (_| |         |
#   |        |_| \_\___|\__,_|\__,_|  \___\___/|_| |_|_| |_|\__, |         |
#   |                                                       |___/          |
#   +----------------------------------------------------------------------+

# Now - at last - we can read in the user's configuration files
def all_nonfunction_vars():
    return set([ name for name,value in globals().items() if name[0] != '_' and type(value) != type(lambda:0) ])

def marks_hosts_with_path(old, all, filename):
    if not filename.startswith(check_mk_configdir):
        return
    path = filename[len(check_mk_configdir):]
    old = set([ o.split("|", 1)[0] for o in old ])
    all = set([ a.split("|", 1)[0] for a in all ])
    for host in all:
        if host not in old:
            host_paths[host] = path

def read_config_files():
    global vars_before_config, final_mk, local_mk, checks

    # Initialize dictionary-type default levels variables
    for varname in check_default_levels.values():
        globals()[varname] = {}

    # Create list of all files to be included
    list_of_files = reduce(lambda a,b: a+b, 
       [ [ "%s/%s" % (d, f) for f in fs if f.endswith(".mk")] 
         for d, sb, fs in os.walk(check_mk_configdir) ], [])
    list_of_files.sort()
    list_of_files = [ check_mk_configfile ] + list_of_files
    final_mk = check_mk_basedir + "/final.mk"
    if os.path.exists(final_mk):
        list_of_files.append(final_mk)
    local_mk = check_mk_basedir + "/local.mk"
    if os.path.exists(local_mk):
        list_of_files.append(local_mk)

    vars_before_config = all_nonfunction_vars()
    for _f in list_of_files:
        # Hack: during parent scan mode we must not read in old version of parents.mk!
        if '--scan-parents' in sys.argv and _f.endswith("/parents.mk"):
            continue
        try:
            if opt_debug:
                sys.stderr.write("Reading config file %s...\n" % _f)
            _old_all_hosts = all_hosts[:]
            execfile(_f, globals(), globals())
            marks_hosts_with_path(_old_all_hosts, all_hosts, _f)
        except Exception, e:
            sys.stderr.write("Cannot read in configuration file %s:\n%s\n" % (_f, e))
            if __name__ == "__main__":
                sys.exit(3)
            else:
                raise

    # Strip off host tags from the list of all_hosts.  Host tags can be
    # appended to the hostnames in all_hosts, separated by pipe symbols,
    # e.g. "zbghlnx04|bgh|linux|test" and are stored in a separate
    # dictionary called 'hosttags'
    global hosttags, all_hosts_untagged
    hosttags = {}
    for taggedhost in all_hosts + clusters.keys():
        parts = taggedhost.split("|")
        hosttags[parts[0]] = parts[1:]
    all_hosts_untagged = all_active_hosts()
    
    # Sanity check for duplicate hostnames
    seen_hostnames = set([])
    for hostname in strip_tags(all_hosts + clusters.keys()):
        if hostname in seen_hostnames:
            sys.stderr.write("Error in configuration: duplicate host '%s'\n" % hostname)
            sys.exit(4)
        seen_hostnames.add(hostname)

    # Read autochecks and append them to explicit checks
    read_all_autochecks()
    checks = autochecks + checks

    # Check for invalid configuration variables
    vars_after_config = all_nonfunction_vars()
    ignored_variables = set(['vars_before_config', 'autochecks', 'parts',
                             'hosttags' ,'seen_hostnames',
                             'all_hosts_untagged' ,'taggedhost' ,'hostname'])
    errors = 0
    for name in vars_after_config:
        if name not in ignored_variables and name not in vars_before_config:
            sys.stderr.write("Invalid configuration variable '%s'\n" % name)
            errors += 1

    # Special handling for certain deprecated variables
    if type(snmp_communities) == dict:
        sys.stderr.write("ERROR: snmp_communities cannot be a dict any more.\n")
        errors += 1

    if errors > 0:
        sys.stderr.write("--> Found %d invalid variables\n" % errors)
        sys.stderr.write("If you use own helper variables, please prefix them with _.\n")
        sys.exit(1)

    # Convert www_group into numeric id
    global www_group
    if type(www_group) == str:
        try:
            import grp
            www_group = grp.getgrnam(www_group)[2]
        except Exception, e:
            sys.stderr.write("Cannot convert group '%s' into group id: %s\n" % (www_group, e))
            sys.stderr.write("Please set www_group to an existing group in main.mk.\n")
            sys.exit(3)

    # Prepare information for --backup and --restore
    global backup_paths
    backup_paths = [
        # tarname               path                 canonical name   description                is_dir owned_by_nagios www_group
        ('check_mk_configfile', check_mk_configfile, "main.mk",       "Main configuration file",           False, False, False ),
        ('final_mk',            final_mk,            "final.mk",      "Final configuration file final.mk", False, False, False ),
        ('check_mk_configdir',  check_mk_configdir,  "",              "Configuration sub files",           True,  False, False ),
        ('autochecksdir',       autochecksdir,       "",              "Automatically inventorized checks", True,  False, False ),
        ('counters_directory',  counters_directory,  "",              "Performance counters",              True,  True,  False ),
        ('tcp_cache_dir',       tcp_cache_dir,       "",              "Agent cache",                       True,  True,  False ),
        ('logwatch_dir',        logwatch_dir,        "",              "Logwatch",                          True,  True,  True  ),
        ]

    # Load agent simulator if enabled in configuration
    if agent_simulator:
        execfile(modules_dir + "/agent_simulator.py", globals(), globals())


# Compute parameters for a check honoring factory settings,
# default settings of user in main.mk, check_parameters[] and
# the values code in autochecks (given as parameter params)
def compute_check_parameters(host, checktype, item, params):
    # Handle dictionary based checks
    def_levels_varname = check_default_levels.get(checktype)
    if def_levels_varname:
        vars_before_config.add(def_levels_varname)
    if def_levels_varname and type(params) == dict:

        # Start with factory settings
        new_params = factory_settings.get(def_levels_varname, {}).copy()

        # Merge user's default settings onto it
        if def_levels_varname in globals():
            def_levels = eval(def_levels_varname)
            if type(def_levels) == dict:
                new_params.update(eval(def_levels_varname))

        # Merge params from inventory onto it
        new_params.update(params)
        params = new_params

    descr = service_description(checktype, item)
    entries = service_extra_conf(host, descr, check_parameters)
    if len(entries) > 0:
        # loop from last to first (first must have precedence)
        for entry in entries[::-1]:
            if def_levels_varname and type(entry) == dict:
                params.update(entry)
            else:
                params = entry
    return params

# read automatically generated checks. They are prepended to the check
# table: explicit user defined checks override automatically generated
# ones. Do not read in autochecks, if check_mk is called as module.
def read_all_autochecks():
    global autochecks
    autochecks = []
    for f in glob.glob(autochecksdir + '/*.mk'):
        try:
            autochecks += eval(file(f).read())
        except SyntaxError,e:
            if opt_verbose:
                sys.stderr.write("Syntax error in file %s: %s\n" % (f, e))
            if opt_debug: 
                sys.exit(3)
        except Exception, e:
            if opt_verbose:
                sys.stderr.write("Error in file %s:\n%s\n" % (f, e))
            if opt_debug: 
                sys.exit(3)

    # Exchange inventorized check parameters with those configured by
    # the user. Also merge with default levels for modern dictionary based checks.
    autochecks = [ (host, ct, it, compute_check_parameters(host, ct, it, par)) 
                   for (host, ct, it, par) in autochecks ]


def output_profile():
    if g_profile:
        g_profile.dump_stats(g_profile_path)
        show_profile = os.path.join(os.path.dirname(g_profile_path), 'show_profile.py')
        file(show_profile, "w")\
            .write("#!/usr/bin/python\n"
                   "import pstats\n"
                   "stats = pstats.Stats('%s')\n"
                   "stats.sort_stats('time').print_stats()\n" % g_profile_path)
        os.chmod(show_profile, 0755)

        sys.stderr.write("Profile '%s' written. Please run %s.\n" % (g_profile_path, show_profile))

#   +----------------------------------------------------------------------+
#   |                        __  __       _                                |
#   |                       |  \/  | __ _(_)_ __                           |
#   |                       | |\/| |/ _` | | '_ \                          |
#   |                       | |  | | (_| | | | | |                         |
#   |                       |_|  |_|\__,_|_|_| |_|                         |
#   |                                                                      |
#   +----------------------------------------------------------------------+



# Do option parsing and execute main function -
# if check_mk is not called as module
if __name__ == "__main__":
    short_options = 'SHVLCURODMd:Ic:nhvpXPuN'
    long_options = [ "help", "version", "verbose", "compile", "debug",
                     "list-checks", "list-hosts", "list-tag", "no-tcp", "cache",
                     "flush", "package", "localize", "donate", "snmpwalk", "usewalk",
                     "scan-parents", "procs=", "automation=", 
                     "snmpget=", "profile",
                     "no-cache", "update", "restart", "reload", "dump", "fake-dns=",
                     "man", "nowiki", "config-check", "backup=", "restore=",
                     "check-inventory=", "paths", "cleanup-autochecks", "checks=" ]

    non_config_options = ['-L', '--list-checks', '-P', '--package', '-M', 
                          '--man', '-V', '--version' ,'-h', '--help']

    try:
        opts, args = getopt.getopt(sys.argv[1:], short_options, long_options)
    except getopt.GetoptError, err:
        print str(err)
        sys.exit(1)

    # Read the configuration files (main.mk, autochecks, etc.), but not for
    # certain operation modes that does not need them and should not be harmed
    # by a broken configuration
    if len(set.intersection(set(non_config_options), [o[0] for o in opts])) == 0:
        read_config_files()

    done = False
    seen_I = 0
    inventory_checks = None
    # Scan modifying options first (makes use independent of option order)
    for o,a in opts:
        if o in [ '-v', '--verbose' ]:
            opt_verbose = True
        elif o == '-c':
            check_mk_configfile = a
        elif o == '--cache':
            opt_use_cachefile = True
            check_max_cachefile_age     = 1000000000
            inventory_max_cachefile_age = 1000000000
        elif o == '--no-tcp':
            opt_no_tcp = True
        elif o == '--no-cache':
            opt_no_cache = True
        elif o == '-p':
            opt_showperfdata = True
        elif o == '-n':
            opt_dont_submit = True
        elif o == '-u':
            opt_cleanup_autochecks = True
        elif o == '--fake-dns':
            fake_dns = a
        elif o == '--usewalk':
            opt_use_snmp_walk = True
        elif o == '--procs':
            max_num_processes = int(a)
        elif o == '--nowiki':
            opt_nowiki = True
        elif o == '--debug':
            opt_debug = True
        elif o == '-I':
            seen_I += 1
        elif o == "--checks":
            inventory_checks = a

    # Perform actions (major modes)
    try:
        for o, a in opts:
            if o in [ '-h', '--help' ]:
                usage()
                done = True
            elif o in [ '-V', '--version' ]:
                print_version()
                done = True
            elif o in [ '-X', '--config-check' ]:
                done = True
            elif o in [ '-S', '-H' ]:
                sys.stderr.write(tty_bold + tty_red + "ERROR" + tty_normal + "\n")
                sys.stderr.write("The options -S and -H have been replaced with the option -N. If you \n")
                sys.stderr.write("want to generate only the service definitions, please set \n")
                sys.stderr.write("'generate_hostconf = False' in main.mk.\n")
                done = True
            elif o == '-N':
                do_output_nagios_conf(args)
                done = True
            elif o in [ '-C', '--compile' ]:
                precompile_hostchecks()
                done = True
            elif o in [ '-U', '--update' ] :
                do_update()
                done = True
            elif o in [ '-R', '--restart' ] :
                do_restart()
                done = True
            elif o in [ '-O', '--reload' ] :
                do_reload()
                done = True
            elif o in [ '-D', '--dump' ]:
                dump_all_hosts(args)
                done = True
            elif o == '--backup':
                do_backup(a)
                done = True
            elif o ==  '--restore':
                do_restore(a)
                done = True
            elif o == '--flush':
                do_flush(args)
                done = True
            elif o == '--paths':
                show_paths()
                done = True
            elif o in ['-P', '--package']:
                execfile(modules_dir + "/packaging.py")
                do_packaging(args)
                done = True
            elif o in ['--localize']:
                execfile(modules_dir + "/localize.py")
                do_localize(args)
                done = True
            elif o == '--donate':
                do_donation()
                done = True
            elif o == '--snmpwalk':
                do_snmpwalk(args)
                done = True
            elif o == '--snmpget':
                do_snmpget(a, args)
                done = True
            elif o in [ '-M', '--man' ]:
                if len(args) > 0:
                    show_check_manual(args[0])
                else:
                    list_all_manuals()
                done = True
            elif o == '--list-hosts':
                l = list_all_hosts(args)
                sys.stdout.write("\n".join(l))
                if l != []:
                    sys.stdout.write("\n")
                done = True
            elif o == '--list-tag':
                l = list_all_hosts_with_tags(args)
                sys.stdout.write("\n".join(l))
                if l != []:
                    sys.stdout.write("\n")
                done = True
            elif o in [ '-L', '--list-checks' ]:
                output_check_info()
                done = True
            elif o == '-d':
                output_plain_hostinfo(a)
                done = True
            elif o == '--check-inventory':
                check_inventory(a)
                done = True
            elif o == '--scan-parents':
                do_scan_parents(args)
                done = True
            elif o == '--automation':
                execfile(modules_dir + "/automation.py")
                do_automation(a, args)
                done = True


    except MKGeneralException, e:
        sys.stderr.write("%s\n" % e)
        if opt_debug:
            raise
        sys.exit(3)

    if not done and seen_I > 0:

        hostnames = args
        if inventory_checks:
            checknames = inventory_checks.split(",")

        # remove existing checks, if option -I is used twice
        if seen_I > 1:
            if inventory_checks == None:
                checknames = inventorable_checktypes("all")
            if len(hostnames) > 0:
                for host in hostnames:
                    remove_autochecks_of(host, checknames)
                    clust = cluster_of(host)
                    if clust:
                        missing = []
                        for node in nodes_of(clust):
                            if node not in hostnames:
                                missing.append(node)
                        if len(missing) == 0:
                            if opt_verbose:
                                sys.stdout.write("All nodes of %s specified, dropping checks of %s, too.\n" % (clust, clust))
                            remove_autochecks_of(clust, checknames)
                        else:
                            sys.stdout.write("Warning: %s is part of cluster %s, but you didn't specify %s as well.\nChecks on %s will be kept.\n" % 
                            (host, clust, ",".join(missing), clust))
                    # if the host is part of a cluster and
                    # the other nodes of that cluster are
                    # also present in the list, then we also # drop the checks of the clusters

            else:
                for host in all_active_hosts() + all_active_clusters():
                    remove_autochecks_of(host, checknames)
            reread_autochecks()

        if inventory_checks == None:
            do_snmp_scan(hostnames)
            checknames = inventorable_checktypes("tcp")
        
        for checkname in checknames:
            make_inventory(checkname, hostnames, False)

        # -u, --cleanup-autochecks called in stand alone mode
        if opt_cleanup_autochecks or always_cleanup_autochecks:
            do_cleanup_autochecks()
        done = True

    if not done and opt_cleanup_autochecks: # -u as standalone option
        do_cleanup_autochecks()
        done = True


    if done:
        output_profile()
        sys.exit(0)
    elif len(args) == 0 or len(args) > 2:
        usage()
        sys.exit(1)
    else:

        hostname = args[0]
        if len(args) == 2:
            ipaddress = args[1]
        else:
            if is_cluster(hostname):
                ipaddress = None
            else:
                try:
                    ipaddress = lookup_ipaddress(hostname)
                except:
                    print "Cannot resolve hostname '%s'." % hostname
                    sys.exit(2)

        do_check(hostname, ipaddress)